Files

3.6 KiB

CLAUDE.md

Stack

Cloudflare Worker (TypeScript + Hono) + React 18 + Vite + Tailwind + shadcn/ui + Cloudflare D1 + remote MCP server (@hono/mcp) + OAuth for claude.ai connectors (@cloudflare/workers-oauth-provider, KV-backed).

Package manager: pnpm. Never use npm commands.

Key Commands

pnpm run dev               # Worker backend :8787
pnpm run dev:web           # React frontend :5173 (proxy /api → :8787)
pnpm run db:migrate:local  # apply D1 migrations locally
pnpm run deploy            # build:web + db:migrate:remote + wrangler deploy
pnpm run test              # unit tests (workers pool)
pnpm run fixtures:pull     # pull real mails from remote D1 → test/fixtures/*.eml (gitignored)
pnpm run test:mail         # deterministic regression on real-mail fixtures (free, no LLM)
pnpm run test:eval         # live LLM classification eval on fixtures (costs API calls)

Architecture Boundaries — Never Violate

  1. isPromotionalEmail() must run before any LLM call in the email handler (src/email/handler.ts).
  2. Every incoming email → raw_mails. Only AI-extracted results → code_mails. Never skip raw_mails.
  3. ASSETS is required for the frontend. There is no legacy HTML fallback.
  4. DOMPurify + sandboxed iframe on all email HTML rendering. Do not relax.
  5. Auth boundaries: middleware in src/app.ts (sessionAuth on /api/*, requireAdmin on /api/admin/*, apiKeyAuth on /mcp for aik_ keys) plus the OAuthProvider wrapper in src/index.ts (validates OAuth tokens on /mcp, then oauthPropsAuth re-fetches the user from D1 — never trust props beyond userId). Do not move or bypass either layer; the only allowed provider bypasses in index.ts are Bearer aik_ pre-routing and the whole-provider skip when OAUTH_KV is unbound (OAuth is an optional feature).
  6. visibleMails() (src/services/mail.ts) is the only mail permission layer — REST and MCP both query through it. Never add a parallel filter path.
  7. Sensitive categories (password_reset, account_security) are default-deny for users; legacy and raw email bodies are admin-only on every interface.
  8. All AI calls go through callProvider() (src/services/classify.ts). Do not add per-provider methods alongside it.
  9. Schema changes = new numbered file in migrations/. Never edit applied migrations.

Env Vars

Required: AI_BASE_URL, AI_API_KEY, AI_API_FORMAT (openai|responses|anthropic), AI_MODEL, JWT_SECRET (Cloudflare secret, signs session JWTs; local dev reads it from gitignored .dev.vars). Optional binding: OAUTH_KV KV namespace (name mandated by workers-oauth-provider; stores OAuth tokens/grants/clients). Present → OAuth enabled on /mcp; absent → OAuth fully bypassed, /mcp accepts aik_ keys only and /oauth/* returns 404. Optional fallback: AI_FALLBACK_BASE_URL, AI_FALLBACK_API_KEY, AI_FALLBACK_API_FORMAT, AI_FALLBACK_MODEL (all four or none). Optional Bark push: UseBark, barkUrl, barkTokens.

Frontend Component Rules

  • UI components: shadcn/ui only. No new UI libraries without discussion.
  • Colors: CSS variables in web/src/index.css. No hardcoded hex except table hover states.
  • Use text-muted-foreground for secondary text. text-muted is a background color — never use as text.
  • All grid children need min-w-0 to prevent overflow.

Compact Instructions

When compressing, preserve in priority order:

  1. Architecture decisions (NEVER summarize)
  2. Modified files and their key changes
  3. Current verification status (pass/fail)
  4. Open TODOs and rollback notes
  5. Tool outputs (can delete, keep pass/fail only)