# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Quick Reference | Task | Command | |------|---------| | Dev server | `npm run dev` (or `npx tsx src/index.ts web`) | | Type check | `tsc --noEmit` | | Lint | `npm run lint` (fix: `npm run lint:fix`) | | Format | `npm run format` (check: `npm run format:check`) | | Single test | `npm test -- test/.test.ts` (or `npx vitest run --config config/vitest.config.ts test/.test.ts`) — ⚠ **never** run bare `npm test`, see Testing section | | Build | `npm run build` (esbuild via `scripts/build.mjs`, NOT tsc — `tsc --noEmit` is type-check only) | | Production | `npm run build && systemctl --user restart codeman-web` | ## CRITICAL: Session Safety **You may be running inside a Codeman-managed tmux session.** Before killing ANY tmux or Claude process: 1. Check: `echo $CODEMAN_MUX` - if `1`, you're in a managed session 2. **NEVER** run `tmux kill-session`, `pkill tmux`, or `pkill claude` without confirming 3. Use the web UI or `./scripts/tmux-manager.sh` instead of direct kill commands ## CRITICAL: Always Test Before Deploying **NEVER COM without verifying your changes actually work.** For every fix: 1. **Backend changes**: Hit the API endpoint with `curl` and verify the response 2. **Frontend changes**: Use Playwright to load the page and assert the UI renders correctly. Use `waitUntil: 'domcontentloaded'` (not `networkidle` — SSE keeps the connection open). Wait 3-4s for polling/async data to populate, then check element visibility, text content, and CSS values 3. **Only after verification passes**, proceed with COM The production server caches static files for 1 year, `immutable` (`maxAge: '1y'` in `server.ts`). To avoid stale frontend after a deploy, `renderIndexHtml` runs `cacheBustAssets(html)` — it appends `?v=` to **every same-origin `.js`/`.css`** reference (mtime memoized ~1s so a burst of renders is cheap; external/already-versioned/missing refs untouched). Because `index.html` is served `no-cache`, a **normal reload now picks up edited modules/styles — no hard refresh needed** (the gesture bundle is injected separately with its own `?v=`). If you add an asset referenced by an *absolute* URL or from JS rather than a `