diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..62fc190 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE '\\.(ts|tsx)\"'; then pnpm tsc --noEmit 2>&1 | head -30; fi" + } + ] + } + ] + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37423d7..887a258 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,11 +9,11 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 20 cache: pnpm @@ -21,6 +21,12 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Lint + run: pnpm lint + + - name: Type check + run: pnpm tsc --noEmit + - name: Run tests run: pnpm test diff --git a/.github/workflows/ingest.yml b/.github/workflows/ingest.yml index 251465c..68d406c 100644 --- a/.github/workflows/ingest.yml +++ b/.github/workflows/ingest.yml @@ -7,11 +7,11 @@ jobs: ingest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 20 cache: pnpm diff --git a/CLAUDE.md b/CLAUDE.md index 16c7c86..06584db 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,11 +6,49 @@ This is a RAG chatbot that answers TTRPG rules questions using Old School Essentials markdown files stored in `vault/`. -`DEBUG=true` enables server-side logging of query, retrieved doc count, and chunk previews. `CHATBOT_ENABLED=false` disables the LLM entirely. +**Data flow:** User message → `app/api/chat/route.ts` (Next.js API route) → `lib/retrieval.ts` (Cohere embedding + Supabase vector search) → Anthropic Claude LLM (via AI SDK) → streamed response. + +**Key files:** +- `app/api/chat/route.ts` — streaming chat API route +- `lib/retrieval.ts` — RAG retrieval logic (embedding + vector search) +- `lib/supabase-client.ts` — Supabase client (anon key, browser/server) +- `scripts/ingest.ts` — ingestion pipeline: reads vault MD → chunks → embeds → upserts to Supabase +- `scripts/supabase-admin.ts` — Supabase admin client (service role key, scripts only) +- `components/` — React chat UI components +- `vault/` — source OSE markdown files (read-only reference material) + +**Environment flags:** +- `DEBUG=true` — server-side logging of query, retrieved doc count, and chunk previews +- `CHATBOT_ENABLED=false` — disables the LLM entirely (retrieval still runs) Plans are found in `docs/plans` +## Path-scoped rules + +- `lib/` — pure functions; no direct Supabase admin client usage here (use `scripts/supabase-admin.ts` for admin ops) +- `scripts/` — CLI entry points; may use admin client; never imported by app code +- `components/` — React only; no direct API calls (use `useChat` hook from AI SDK) +- `app/api/` — Next.js route handlers; keep thin — delegate logic to `lib/` + +## Setup + +Pre-commit hooks require gitleaks for secret scanning: +```bash +brew install gitleaks +``` + +## Commands + +```bash +pnpm test # run vitest unit tests +pnpm lint # run ESLint +pnpm tsc --noEmit # type check (no output files) +pnpm dev # start Next.js dev server +pnpm ingest # run ingestion pipeline (requires .env.local) +``` + ## Remember - Write tests FIRST before writing production code - Check for TS errors before considering a task 'done' +- `pnpm tsc --noEmit` must pass before any PR is merged diff --git a/lefthook.yml b/lefthook.yml index e453694..aa3d8f9 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -3,4 +3,8 @@ pre-commit: check: glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc,md}" run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files} - stage_fixed: true \ No newline at end of file + stage_fixed: true + typecheck: + run: pnpm tsc --noEmit + secrets: + run: gitleaks protect --staged --redact \ No newline at end of file diff --git a/package.json b/package.json index d33dda1..ff42efd 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { + "postinstall": "lefthook install", "dev": "next dev", "build": "next build", "start": "next start",