# Project export: ProfAI

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: CruzHacks 2026
- Tagline: ProfAI: an AI TA-in-a-box that answers student questions 24/7, triages escalations, and automates course announcements—saving professors hours every week.
- Devpost: https://devpost.com/software/profai-um84ax
- GitHub: https://github.com/A5bhinav/SyllabusOS
- Video: https://www.youtube.com/embed/wDNcXSWudTA?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — gautamkathir123 (59 commits), A5bhinav (59 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

# ProfAI - AI Professor-in-a-Box

An intelligent course management system that automates syllabus operations, weekly pacing, and student triage using AI-powered multi-agent routing. Think of it as a "TA in a box" that handles routine course questions while intelligently escalating complex issues to the professor.

## 🎯 Project Overview

ProfAI is designed to manage course operations rather than just teaching content. It acts as a smart assistant that:

- **Answers student questions** about course policies, concepts, and schedules
- **Automatically triages** queries to determine if they need professor attention
- **Generates weekly announcements** based on course schedules
- **Provides insights** to professors about student confusions and concerns

The system uses a **human-in-the-loop** approach where AI handles routine tasks and escalates complex or sensitive issues to the professor.

## 🏗️ Architecture

### Tech Stack

- **Framework**: Next.js 14+ (App Router) with TypeScript
- **Styling**: Tailwind CSS + Shadcn/UI components
- **Backend**: Supabase (PostgreSQL + Auth + Edge Functions)
- **AI**: Google Gemini via LangChain
- **Vector DB**: Supabase Vector (pgvector) for RAG
- **State Management**: React Server Components + useOptimistic for chat

### Key Components

1. **Multi-Agent Router**: Classifies student queries into three categories:
   - **POLICY** (Syllabus questions) → Routes to Syllabus Agent
   - **CONCEPT** (Learning questions) → Routes to Concept Agent
   - **ESCALATE** (Personal/Complex issues) → Creates escalation for professor

2. **RAG (Retrieval-Augmented Generation)**: 
   - Chunks syllabus and course content into vector embeddings
   - Retrieves relevant context for accurate responses
   - Provides source citations (e.g., "See Syllabus page 4")

3. **Sunday Night Conductor**: 
   - Automated weekly announcement generator
   - Reads course schedule and generates week-specific announcements
   - Creates drafts for professor review and approval

## 🚀 Core Features

### For Students

#### Chat Interface
- Ask questions about the course in natural language
- Receive instant responses with source citations
- Get answers about:
  - Course policies (deadlines, grading, attendance)
  - Course concepts and materials
  - Weekly schedules and upcoming assignments

#### Smart Escalation
- Personal issues (e.g., "I'm sick, can I have an extension?") automatically create escalation tickets
- Students receive confirmation when their query has been escalated
- All sensitive queries are flagged for professor review

### For Professors

#### Professor Dashboard

**1. Announcement Drafts Widget**
- View automatically generated weekly announcements
- Review and edit before publishing
- Approve announcements to publish them to students

**2. Escalation Queue**
- See all student queries that need personal attention
- View student name, email, query text, and timestamp
- Resolve escalations after addressing student concerns

**3. Pulse Report**
- 3-bullet summary of "Top Student Confusions"
- Based on analysis of chat logs
- Helps professors identify topics that need clarification
- Shows total queries and escalation counts

#### Manual Controls
- Trigger Sunday Night Conductor manually to generate announcements
- View and manage all course content
- Monitor system activity and student engagement

### System Features

#### Intelligent Query Classification
The Agent Router uses AI to classify each student query:
- **POLICY**: Questions about syllabus, deadlines, grading policies
  - Example: "When is the midterm?"
  - Routes to: Syllabus Agent (RAG over syllabus content)

- **CONCEPT**: Questions about course material, lectures, concepts
  - Example: "Can you explain recursion?"
  - Routes to: Concept Agent (RAG over lecture notes/content)

- **ESCALATE**: Personal, complex, or sensitive issues
  - Example: "I'm having personal issues and need an extension"
  - Routes to: Escalation Handler (creates ticket for professor)

#### Source Citations
Every AI response includes citations:
- "See Syllabus page 4, section 2.3"
- "See Lecture Week 5, slide 12"
- Helps students verify information and find original sources

#### Strict "I Don't Know" Policy
- If the AI can't find relevant information in the course materials
- If retrieval confidence is low (< 0.7)
- Automatically escalates to professor rather than hallucinating answers

## 📋 User Flows

### Student Flow

1. **Login/Signup** → Student creates account or logs in
2. **Onboarding** (First-time only) → Professor uploads syllabus PDF and schedule CSV
3. **Chat Interface** → Student asks questions in natural language
4. **Get Response** → Receives answer with citations or escalation confirmation
5. **View Escalations** → Can see status of escalated queries

### Professor Flow

1. **Login/Signup** → Professor creates account with professor role
2. **Course Setup** → Upload syllabus PDF and schedule CSV
3. **Weekly Routine**:
   - Review and approve Sunday Night Conductor announcements
   - Check Escalation Queue for student issues
   - Review Pulse Report for insights
4. **Ongoing Management**:
   - Resolve escalations
   - Edit and publish announcements
   - Monitor student questions and confusion patterns

### Sunday Night Conductor Flow

1. **Automated Trigger** → Runs every Sunday night (or manually triggered)
2. **Schedule Analysis** → Reads course schedule to determine current week
3. **Announcement Generation** → AI generates week-specific announcement in professor's persona
4. **Draft Creation** → Saves announcement as draft in professor dashboard
5. **Professor Review** → Professor reviews, edits, and approves announcement
6. **Publication** → Approved announcement is published to students

## 🔧 Technical Implementation

### Multi-Agent System

```
User Query
    ↓
Agent Router (Classification)
    ↓
┌──────────┬──────────┬──────────┐
│ POLICY   │ CONCEPT  │ ESCALATE │
│ Agent    │ Agent    │ Handler  │
│          │          │          │
│ RAG over │ RAG over │ Creates  │
│ Syllabus │ Lectures │ Ticket   │
└──────────┴──────────┴──────────┘
    ↓
Response with Citations
```

### Database Schema

- **profiles**: User accounts (students/professors)
- **courses**: Course metadata
- **course_content**: Chunked syllabus/lecture content with vector embeddings
- **schedules**: Weekly schedule data (from CSV)
- **escalations**: Student escalation queue
- **announcements**: Weekly announcements (drafts + published)
- **chat_logs**: Chat history for analytics

### API Endpoints

#### For Students
- `POST /api/chat` - Send chat message, receive AI response
- `GET /api/announcements` - Get published announcements

#### For Professors
- `GET /api/escalations` - Get escalation queue
- `GET /api/announcements` - Get all announcements (drafts + published)
- `POST /api/announcements` - Create announcement
- `PUT /api/announcements/:id` - Update/publish announcement
- `POST /api/conductor` - Trigger Sunday Night Conductor
- `GET /api/pulse` - Get pulse report data

#### Shared
- `POST /api/upload` - Upload syllabus PDF and schedule CSV

## 🛠️ Development Setup

### Prerequisites

- Node.js 18+ and npm
- Supabase account
- Google Cloud account (for Gemini API)

### Installation

1. **Clone the repository**
   ```bash
   git clone https://github.com/A5bhinav/SyllabusOS.git
   cd SyllabusOS
   ```

2. **Install dependencies**
   ```bash
   npm install
   ```

3. **Set up environment variables**
   
   Copy `.env.example` to `.env` and fill in your credentials:
   ```env
   # Supabase Configuration
   NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
   NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
   SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key

   # Google Gemini AI Configuration
   GOOGLE_GENAI_API_KEY=your_google_gemini_api_key

   # Application Configuration
   MOCK_MODE=false  # Set to true for development (saves API costs)
   DEMO_MODE=false  # Set to true to mock system time to Week

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 157 recognized source files, 967 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- LangChain (technology) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 167)

```
.DS_Store
.env.example
.eslintrc.json
.gitignore
app/(auth)/layout.tsx
app/(auth)/login/page.tsx
app/(auth)/signup/page.tsx
app/(dashboard)/dashboard/dashboard-content.tsx
app/(dashboard)/dashboard/page.tsx
app/(dashboard)/layout.tsx
app/(dashboard)/onboarding/page.tsx
app/(dashboard)/student/announcements/page.tsx
app/(dashboard)/student/browse/[courseCode]/feedback/page.tsx
app/(dashboard)/student/browse/page.tsx
app/(dashboard)/student/chat/page.tsx
app/(dashboard)/student/courses/[courseId]/page.tsx
app/(dashboard)/student/dashboard/page.tsx
app/(dashboard)/student/enroll/[courseId]/page.tsx
app/(dashboard)/student/escalations/page.tsx
app/(dashboard)/student/page.tsx
app/(dashboard)/student/schedule/page.tsx
app/api/announcements/[id]/route.ts
app/api/announcements/route.ts
app/api/auth/auto-confirm/route.ts
app/api/auth/create-profile/route.ts
app/api/chat/history/route.ts
app/api/chat/route.ts
app/api/conductor/route.ts
app/api/courses/[courseId]/route.ts
app/api/courses/feedback/[courseCode]/route.ts
app/api/courses/route.ts
app/api/demo/load/route.ts
app/api/enrollments/[id]/route.ts
app/api/enrollments/join/route.ts
app/api/enrollments/route.ts
app/api/escalations/[id]/generate-video/route.ts
app/api/escalations/[id]/video-status/route.ts
app/api/escalations/route.ts
app/api/escalations/suggest/route.ts
app/api/pulse/route.ts
app/api/upload/route.ts
app/api/video/poll/route.ts
app/globals.css
app/layout.tsx
app/page.tsx
components.json
components/professor/AnnouncementDrafts.tsx
components/professor/CourseManagement.tsx
components/professor/EnrolledStudents.tsx
components/professor/EscalationQueue.tsx
components/professor/ProfessorNav.tsx
components/professor/PulseReport.tsx
components/README.md
components/shared/DemoModeToggle.tsx
components/shared/EmbeddedVideoPlayer.tsx
components/shared/ErrorBoundary.tsx
components/shared/FileUpload.tsx
components/shared/LoadDemoButton.tsx
components/shared/LoadingSpinner.tsx
components/student/Announcements.tsx
components/student/ChatInterface.tsx
components/student/CitationDisplay.tsx
components/student/MessageBubble.tsx
components/student/QuestionHistory.tsx
components/student/StudentEscalations.tsx
components/student/StudentNav.tsx
components/student/UpcomingDeadlines.tsx
components/student/WeekSchedule.tsx
components/ui/button.tsx
components/ui/card.tsx
components/ui/dialog.tsx
components/ui/form.tsx
components/ui/input.tsx
components/ui/label.tsx
components/ui/scroll-area.tsx
components/ui/skeleton.tsx
components/ui/switch.tsx
components/ui/textarea.tsx
components/ui/toast.tsx
components/ui/toaster.tsx
docs/GEMINI_VIDEO_MODELS.md
docs/HOWTO.md
docs/plan.plan.md
docs/QUICK_START.md
docs/REDDIT_OAUTH_SETUP.md
docs/syllabusos_backend_plan_(abhinav)_5f8dbbbe.plan.md
docs/syllabusos_frontend_plan_(gautam)_d3a4e35d.plan.md
docs/syllabusos_implementation_plan_f0e15b24.plan.md
docs/VIDEO_ASYNC_IMPLEMENTATION.md
docs/VIDEO_FLOW_VERIFICATION.md
docs/video_generation_for_escalation_responses_c95a26bd.plan.md
docs/VIDEO_GENERATION_TROUBLESHOOTING.md
docs/VIDEO_REDDIT_STATUS.md
docs/VIDEO_STORAGE_SETUP.md
hooks/use-toast.ts
lib/agents/concept-agent.ts
lib/agents/escalation-handler.ts
lib/agents/index.ts
lib/agents/router.ts
lib/agents/syllabus-agent.ts
lib/agents/types.ts
lib/ai/client.ts
lib/ai/index.ts
lib/ai/langchain-setup.ts
lib/api/announcements.ts
lib/api/chat.ts
lib/api/client.ts
lib/api/conductor.ts
lib/api/courses.ts
lib/api/demo.ts
lib/api/enrollments.ts
lib/api/escalations.ts
lib/api/pulse.ts
lib/api/README.md
lib/api/upload.ts
lib/conductor/sunday-conductor.ts
lib/rag/chunking.ts
lib/rag/retrieval.ts
lib/rag/vector-store.ts
lib/supabase/client.ts
[47 more files omitted for size]
```

### Dependencies

- package.json: @hookform/resolvers@^5.2.2, @langchain/google-genai@^2.1.8, @radix-ui/react-dialog@^1.1.15, @radix-ui/react-label@^2.1.8, @radix-ui/react-scroll-area@^1.2.10, @radix-ui/react-slot@^1.2.4, @radix-ui/react-switch@^1.2.6, @radix-ui/react-toast@^1.2.15, @supabase/ssr@^0.8.0, @supabase/supabase-js@^2.90.1, @types/cheerio@^0.22.35, @types/fluent-ffmpeg@^2.1.24, @types/node@^25.0.8, @types/react@^19.2.8, @types/react-dom@^19.2.3, autoprefixer@^10.4.23, axios@^1.13.2, cheerio@^1.1.2, class-variance-authority@^0.7.1, clsx@^2.1.1, csv-parse@^6.1.0, date-fns@^4.1.0, eslint@^9.39.2, eslint-config-next@^16.1.3, exceljs@^4.4.0, ffmpeg-static@^5.2.0, fluent-ffmpeg@^2.1.2, framer-motion@^12.26.2, langchain@^1.2.8, lucide-react@^0.562.0, next@^16.1.1, pdf-parse@^2.4.5, postcss@^8.5.6, react@^19.2.3, react-dom@^19.2.3, react-hook-form@^7.71.1, recharts@^3.6.0, tailwind-merge@^3.4.0, tailwindcss@^3.4.19, tailwindcss-animate@^1.0.7, typescript@^5.9.3

### Recent commits (newest first)

- final
- help
- pls
- add error handling
- flkjdsafl
- modify scraper
- fixed cheerio
- PLS WORK
- fix reddit
- forgot to push
- maybe reddit
- add error handling
- fix reddit pls
- fix reddit scraping
- fixes
- Merge pull request #35 from A5bhinav/frontend
- fix it
- Merge pull request #34 from A5bhinav/backend
- Merge pull request #33 from A5bhinav/frontend
- lastcall

## Key source files (fetched from GitHub, selected and truncated for size)

### docs/HOWTO.md

```markdown
1. Do not submit multiple requests to Cursor
2. Make sure to provide proper context and what you want it to solve.
3. Dont use phrases like
     - fix this problem
     - what is wrong in my code and etc.


Write a prompt with as much detail as you can design.

Project idea

Front end design

Backend design

and review thorougly before submitting to cursor. You will lose tokens if you keep submitting even with paid subscription.
```

### docs/GEMINI_VIDEO_MODELS.md

```markdown
# Gemini Video Generation Models

## Available Models

### Veo 2
- **Released**: April 2025
- **Duration**: 8 seconds
- **Resolution**: 720p
- **Aspect Ratio**: 16:9
- **Format**: MP4
- **Features**: Text-to-video generation
- **Availability**: Gemini Advanced / Google One AI Premium subscribers

### Veo 3
- **Released**: After Veo 2
- **Duration**: 8 seconds
- **Resolution**: 720p (sometimes higher in certain tiers)
- **Aspect Ratio**: 16:9
- **Format**: MP4
- **Features**: 
  - Text-to-video generation
  - **Synchronized audio** (ambient sounds, dialogue, effects)
  - **Photo-to-video** capability (upload image + describe motion → video)
- **Availability**: Pro or Ultra tier users

### Veo 3.1
- **Released**: Latest version
- **Duration**: 8 seconds
- **Resolution**: 720p (sometimes higher in certain tiers)
- **Aspect Ratio**: 16:9
- **Format**: MP4
- **Features**:
  - All Veo 3 features
  - **"Ingredients to Video"** - Better control over style, referencing objects/characters/textures
- **Availability**: Pro or Ultra tier users

## Subscription Requirements

- **Free tier**: Very limited access (if any), watermarks required
- **Pro/Ultra tier**: Full access to video generation features
- **Watermarks**: Required for free users (if available at all)

## API Integration

### Current Implementation Status
- **File**: `lib/video/generator.ts`
- **Status**: **NOT IMPLEMENTED** (Veo API integration is placeholder)
- **Line**: `const VEO_API_AVAILABLE = false`

### To Implement Veo API

1. **Get API Access**:
   - Need Gemini Pro or Ultra subscription
   - Obtain API key from Google AI Studio or Vertex AI

2. **API Endpoint** (example):
   ```typescript
   const response = await fetch(
     'https://generativelanguage.googleapis.com/v1beta/models/veo-3.1:generateVideo',
     {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
         'Authorization': `Bearer ${VEO_API_KEY}`,
       },
       body: JSON.stringify({
         prompt: veoPrompt,
         aspectRatio: '16:9',
         duration: 5, // seconds
       }),
     }
   )
   ```

3. **Update Code**:
   - Uncomment `VEO_API_AVAILABLE` calculation in `lib/video/generator.ts`
   - Implement `generateVeoClip()` function (currently throws error)
   - Add `GOOGLE_VEO_API_KEY` to environment variables

## Limits & Constraints

- **Video Length**: 8 seconds maximum
- **Resolution**: 720p standard (may vary by tier)
- **Format**: MP4 only
- **Rate Limits**: Subject to subscription tier
- **Geographic Restrictions**: May not be available in all countries

## Resources

- Google AI Studio: https://aistudio.google.com/
- Gemini API Docs: https://ai.google.dev/
- Veo Overview: https://gemini.google/re/overview/video-generation/

## Current Workaround

Until Veo API is implemented, the system:
- Creates placeholder video URLs (`https://example.com/videos/${id}.mp4`)
- Works in MOCK_MODE
- Sets `video_generation_status = 'completed'` with placeholder

This allows the UI/UX t
[truncated — 65 more characters]
```

### package.json

```
{
  "name": "syllabusos",
  "version": "1.0.0",
  "description": "AI Professor-in-a-Box - Course management system with multi-agent routing",
  "private": true,
  "scripts": {
    "dev": "next dev --webpack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/A5bhinav/SyllabusOS.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/A5bhinav/SyllabusOS/issues"
  },
  "homepage": "https://github.com/A5bhinav/SyllabusOS#readme",
  "dependencies": {
    "@hookform/resolvers": "^5.2.2",
    "@langchain/google-genai": "^2.1.8",
    "@radix-ui/react-dialog": "^1.1.15",
    "@radix-ui/react-label": "^2.1.8",
    "@radix-ui/react-scroll-area": "^1.2.10",
    "@radix-ui/react-slot": "^1.2.4",
    "@radix-ui/react-switch": "^1.2.6",
    "@radix-ui/react-toast": "^1.2.15",
    "@supabase/ssr": "^0.8.0",
    "@supabase/supabase-js": "^2.90.1",
    "@types/cheerio": "^0.22.35",
    "@types/fluent-ffmpeg": "^2.1.24",
    "@types/node": "^25.0.8",
    "@types/react": "^19.2.8",
    "@types/react-dom": "^19.2.3",
    "axios": "^1.13.2",
    "cheerio": "^1.1.2",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "csv-parse": "^6.1.0",
    "date-fns": "^4.1.0",
    "exceljs": "^4.4.0",
    "ffmpeg-static": "^5.2.0",
    "fluent-ffmpeg": "^2.1.2",
    "framer-motion": "^12.26.2",
    "langchain": "^1.2.8",
    "lucide-react": "^0.562.0",
    "next": "^16.1.1",
    "pdf-parse": "^2.4.5",
    "react": "^19.2.3",
    "react-dom": "^19.2.3",
    "react-hook-form": "^7.71.1",
    "recharts": "^3.6.0",
    "tailwind-merge": "^3.4.0",
    "tailwindcss-animate": "^1.0.7",
    "typescript": "^5.9.3"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.23",
    "eslint": "^9.39.2",
    "eslint-config-next": "^16.1.3",
    "postcss": "^8.5.6",
    "tailwindcss": "^3.4.19"
  }
}

```

### app/layout.tsx

```typescript
import type { Metadata } from 'next'
import './globals.css'
import { Toaster } from '@/components/ui/toaster'
import { ErrorBoundary } from '@/components/shared/ErrorBoundary'

export const metadata: Metadata = {
  title: 'ProfAI',
  description: 'AI Professor-in-a-Box - Course management system',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" data-scroll-behavior="smooth">
      <body suppressHydrationWarning>
        <ErrorBoundary>
          {children}
          <Toaster />
        </ErrorBoundary>
      </body>
    </html>
  )
}


```

### app/page.tsx

```typescript
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'

export default async function Home() {
  const supabase = await createClient()
  const {
    data: { user },
  } = await supabase.auth.getUser()

  // If user is authenticated, redirect them to the appropriate page based on their role
  if (user) {
    // Get user profile to determine redirect
    const { data: profile } = await supabase
      .from('profiles')
      .select('role')
      .eq('id', user.id)
      .maybeSingle()

    if (profile?.role === 'professor') {
      // Check if professor has courses - if not, go to onboarding
      const { data: courses } = await supabase
        .from('courses')
        .select('id')
        .eq('professor_id', user.id)
        .limit(1);

      if (!courses || courses.length === 0) {
        redirect('/onboarding')
      } else {
        redirect('/dashboard')
      }
    } else if (profile?.role === 'student') {
      redirect('/student')
    }
    // If profile doesn't exist or role is invalid, show home page
    // User can sign up or log in
  }

  return (
    <main className="min-h-screen bg-gradient-to-b from-background to-muted/20">
      {/* Hero Section */}
      <div className="container mx-auto px-4 py-16 md:py-24">
        <div className="max-w-4xl mx-auto text-center space-y-8 mb-16">
          {/* Logo/Brand */}
          <div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary/10 mb-6">
            <svg
              className="w-10 h-10 text-primary"
              fill="none"
              stroke="currentColor"
              viewBox="0 0 24 24"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth={2}
                d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
              />
            </svg>
          </div>

          <h1 className="text-5xl md:text-6xl font-bold tracking-tight">
            ProfAI
          </h1>
          <p className="text-xl md:text-2xl text-muted-foreground max-w-2xl mx-auto leading-relaxed">
            Your AI professor-in-a-box. Intelligent course management that answers questions, 
            automates announcements, and scales your teaching.
          </p>

          {/* CTA Buttons */}
          <div className="flex flex-col sm:flex-row gap-4 justify-center pt-4">
            <Link href="/signup">
              <Button size="lg" className="text-base px-8 py-6 h-auto shadow-lg hover:shadow-xl transition-shadow">
                Get Started Free
              </Button>
            </Link>
            <Link href="/login">
              <Button size="lg" variant="outline" className="text-base px-8 py-6 h-auto">
                Sign In
              </Button>
            </Link>
          </div>
        </div>

        {/* Feature Cards */}
        <div className="grid gap-6 md:grid-cols-3 max-w-5xl mx-auto mt-20">
          <Card className="border-2 hover:border-primary/50 transition-colors hover:shadow-lg">
            <CardHeader className="pb-4">
              <div className="w-12 h-12 rounded-lg bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center mb-4">
                <svg
                  className="w-6 h-6 text-blue-600 dark:text-blue-400"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth={2}
                    d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
                  />
                </svg>
              </div>
              <CardTitle className="text-xl">For Students</CardTitle>
            </CardHeader>
            <CardContent>
              <p className="text-muted-foreground leading-relaxed">
                Ask questions about course policies, concepts, and schedules. Get instant, 
                accurate AI-powered answers with source citations.
              </p>
            </CardContent>
          </Card>

          <Card className="border-2 hover:border-primary/50 transition-colors hover:shadow-lg">
            <CardHeader className="pb-4">
              <div className="w-12 h-12 rounded-lg bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center mb-4">
                <svg
                  className="w-6 h-6 text-purple-600 dark:text-purple-400"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth={2}
                    d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
                  />
                </svg>
              </div>
              <CardTitle className="text-xl">For Professors</CardTitle>
            </CardHeader>
            <CardContent>
              <p className="text-muted-foreground leading-relaxed">
                Automate syllabus operations, manage escalations, and get insights into 
                student confusions. Focus on teaching, not admin.
              </p>
            </CardContent>
          </Card>

          <Card className="border-2 hover:border-primary/50 transition-colors hover:shadow-lg
[truncated — 1513 more characters]
```

### app/(auth)/layout.tsx

```typescript
export default function AuthLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return <>{children}</>;
}

```

### lib/ai/index.ts

```typescript
/**
 * AI module exports
 * Centralized exports for AI-related functionality
 */

export { getGeminiClient, generateChatCompletion, isMockMode } from './client'
export type { AgentRoute, LLMResponse } from './client'

export { 
  createClassificationPrompt, 
  createResponsePrompt, 
  createClassificationChain, 
  createResponseChain 
} from './langchain-setup'


```

### app/(dashboard)/layout.tsx

```typescript
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'

export default async function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const supabase = await createClient()
  const {
    data: { user },
  } = await supabase.auth.getUser()

  if (!user) {
    redirect('/login')
  }

  return <>{children}</>
}


```

### lib/agents/index.ts

```typescript
/**
 * Agent module exports
 * Centralized exports for all agent-related functionality
 */

export { AgentRouter, getAgentRouter } from './router'
export type { RoutingDecision } from './router'

export { SyllabusAgent, getSyllabusAgent } from './syllabus-agent'
export { ConceptAgent, getConceptAgent } from './concept-agent'
export type { AgentResponse } from './types'

export { EscalationHandler, getEscalationHandler } from './escalation-handler'
export type { EscalationResult } from './escalation-handler'


```

### lib/supabase/server.ts

```typescript
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
import { createClient as createSupabaseClient } from '@supabase/supabase-js'

export async function createClient() {
  const cookieStore = await cookies()

  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
  const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY

  if (!supabaseUrl || !supabaseAnonKey) {
    throw new Error('Missing Supabase environment variables')
  }

  return createServerClient(
    supabaseUrl,
    supabaseAnonKey,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  )
}

/**
 * Create a server-side Supabase client with service role key for admin operations
 * Use this for operations that bypass RLS (e.g., system operations, migrations)
 */
export function createServiceClient() {
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
  const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY

  if (!supabaseUrl || !serviceRoleKey) {
    throw new Error('Missing Supabase service role key')
  }

  return createSupabaseClient(supabaseUrl, serviceRoleKey, {
    auth: {
      autoRefreshToken: false,
      persistSession: false,
    },
  })
}


```

[142 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]