Files
interviewer/AGENTS.md

4.7 KiB

AGENTS.md - Interview Bot

AI-powered interviewer bot with video call interface and calendar scheduling on Cloudflare Workers.

Quick Reference

Command Description
npm run dev Start local dev server (port 8787)
npm run deploy Deploy to Cloudflare Workers
npm run tail Stream production logs

Project Structure

src/index.ts    # Entire app: API routes, AI chat, HTML/CSS/JS frontend
wrangler.toml   # Cloudflare Workers config (KV bindings, AI binding)
package.json    # Dependencies and npm scripts

Single-file architecture: All backend routes + frontend UI lives in src/index.ts.

Build & Development

npm install && npm run dev   # Local dev at http://localhost:8787
npm run deploy               # Deploy to production

KV Setup (First-time only)

npx wrangler kv namespace create INTERVIEWS
npx wrangler kv namespace create INTERVIEWS --preview
# Update wrangler.toml with returned IDs

Testing

No test framework. Manual testing only:

  • GET /api/interviews - List interviews
  • POST /api/interviews - Create interview
  • PUT /api/interviews/:id - Update interview
  • DELETE /api/interviews/:id - Delete interview
  • POST /api/chat - Stream AI chat

Code Style

TypeScript Patterns

  • Runtime: Cloudflare Workers (no Node.js APIs)
  • No tsconfig.json: Wrangler handles TypeScript compilation
  • Explicit types: Always type function params and return values
  • Interfaces: Define at file top, before constants
// Interfaces at top
interface Interview {
  id: string;
  candidateName: string;
  status: 'scheduled' | 'in-progress' | 'completed' | 'cancelled';
}

// Explicit return types
async function getInterviews(env: Env, corsHeaders: Record<string, string>): Promise<Response> {
  // ...
}

// Type assertions for JSON
const body = await request.json() as Partial<Interview>;

Naming Conventions

Element Convention Example
Interfaces PascalCase Interview, ChatMessage, Env
Functions camelCase handleChat, getInterviews
Constants SCREAMING_SNAKE SYSTEM_PROMPT
Variables camelCase corsHeaders, currentView

Error Handling

try {
  // operation
} catch (error) {
  console.error('Error:', error);
  return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
    status: 500,
    headers: { 'Content-Type': 'application/json', ...corsHeaders },
  });
}

API Response Pattern

// Always include CORS headers
const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type',
};

// JSON response
return new Response(JSON.stringify(data), {
  status: 200,
  headers: { 'Content-Type': 'application/json', ...corsHeaders },
});

// SSE streaming response
return new Response(stream as ReadableStream, {
  headers: { 'Content-Type': 'text/event-stream', ...corsHeaders },
});

Routing (No framework)

if (path === '/api/interviews' && request.method === 'GET') {
  return await getInterviews(env, corsHeaders);
}
if (path.startsWith('/api/interviews/') && request.method === 'DELETE') {
  const id = path.split('/')[3];
  return await deleteInterview(id, env, corsHeaders);
}

Frontend (Inline in getHTML())

  • CSS: Custom properties in :root, inline <style> block
  • JS: Inline <script> at end of body, vanilla DOM manipulation
  • State: Module-level variables (let interviews = [])

Environment Bindings

export interface Env {
  AI: Ai;                    // Workers AI
  INTERVIEWS: KVNamespace;   // KV storage
}

Key Data Structures

interface Interview {
  id: string;
  candidateName: string;
  email: string;
  scheduledAt: string;      // ISO 8601
  duration: number;         // minutes
  status: 'scheduled' | 'in-progress' | 'completed' | 'cancelled';
  notes: string[];
  transcript: string[];
}

interface ChatMessage {
  role: 'system' | 'user' | 'assistant';
  content: string;
}

Common Tasks

Add API endpoint: Add handler function + path match in fetch handler + include corsHeaders

Modify AI behavior: Edit SYSTEM_PROMPT constant or adjust max_tokens in handleChat()

Update UI: Modify HTML/CSS/JS in getHTML() function

Gotchas

  1. No Node.js APIs - Edge runtime only
  2. Single file - Don't split into modules
  3. KV IDs - Must update wrangler.toml after namespace creation
  4. Template literals - Frontend uses escaped backticks (``) for nested templates
  5. AI model - Default llama-3.1-8b-instruct; use llama-3.3-70b-instruct-fp8-fast for quality