# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commands ```bash npm run dev # start dev server at localhost:3000 npm run build # production build (uses standalone output for Docker) npm run lint # eslint npx tsc --noEmit # type-check without building npm run extract # run PDF → benchmarks.json extraction (requires Ollama or HF) ``` ## Environment Copy `.env.local.example` to `.env.local` before running locally. Ollama must be running (`ollama serve`) with `llama3.1:8b` pulled. The LLM provider is swapped entirely via env vars — no code changes needed: - **Local (Ollama):** `OLLAMA_BASE_URL=http://localhost:11434/v1`, `LLM_MODEL=llama3.1:8b` - **HF Spaces:** `OLLAMA_BASE_URL=https://router.huggingface.co/v1`, `LLM_MODEL=Qwen/Qwen2.5-72B-Instruct`, `OPENAI_API_KEY=hf_...` - **OpenAI:** set `OPENAI_API_KEY`, `LLM_MODEL=gpt-4o`, remove `OLLAMA_BASE_URL` ## Architecture The app has two distinct flows: **One-time setup:** `scripts/extract-knowledge.ts` reads PDFs from `data/pdfs/`, chunks text into ~8000-char pieces, sends each to the LLM, merges results into `data/benchmarks.json` (47 patterns, 124 insights from 3 DORA reports). This file is committed and bundled into the Docker image — the script does not run at runtime. **Request flow:** Browser form (`app/page.tsx`, two steps) → POST `/api/interpret` → `lib/benchmarks.ts` loads `benchmarks.json` (cached in memory) → `lib/prompts.ts` builds system prompt with benchmark data → `lib/llm.ts` calls LLM via OpenAI-compatible client → response validated with `InterpretationReportSchema` (Zod) → JSON returned → stored in `sessionStorage` → `app/report/page.tsx` reads and renders report. **LLM abstraction:** All LLM calls go through `lib/llm.ts`, which wraps the `openai` npm package. The provider is controlled entirely by `OLLAMA_BASE_URL`, `OPENAI_API_KEY`, and `LLM_MODEL` env vars. The OpenAI client's `baseURL` is set to `OLLAMA_BASE_URL`, making any OpenAI-compatible endpoint (Ollama, HF router, Groq, OpenAI) work without code changes. ## Key constraints - `lib/schema.ts` defines both input schemas (`MetricsInputSchema`, `TeamContextSchema`) and output schema (`InterpretationReportSchema`). The API route validates both directions — 400 for bad input, 422 if the LLM returns a malformed report. - `lib/benchmarks.ts` sanitizes `data/benchmarks.json` before Zod validation (Ollama sometimes returns arrays instead of strings in pattern fields). - `next.config.ts` sets `output: 'standalone'` (required for Docker) and `serverExternalPackages: ['pdf-parse']`. - `tsconfig.json` has a `"ts-node"` override block with `module: "CommonJS"` so scripts in `scripts/` can use `require`-style resolution while the Next.js app uses bundler resolution. - The `sessionStorage` key is defined in `lib/constants.ts` as `REPORT_SESSION_KEY` — use that constant, not the string literal. - Print/PDF export uses `window.print()` with `@media print` CSS in `globals.css`. Elements to hide during print get the `print-hide` class. ## Deployment The app is deployed on HuggingFace Spaces at `rdlf/devops-metrics-interpreter`. To update, push to the `hf-deploy` branch (orphan, no PDF history) and force-push to `hf:main`. Do not push `data/pdfs/` — those files exceed HF's 10MB limit and are gitignored.