5.1 KiB
5.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
pnpm check # Merge gate — must pass before every commit (knip + format + lint + typecheck + test)
pnpm knip # Unused code detection (Knip)
pnpm test # Run all tests (Vitest)
pnpm test:watch # Tests in watch mode
pnpm typecheck # tsc --build (project references)
pnpm lint # Biome lint
pnpm format # Biome format (writes)
pnpm --filter web dev # Vite dev server (localhost:5173)
pnpm --filter web build # Production build
Run a single test file: pnpm vitest run packages/domain/src/__tests__/advance-turn.test.ts
Architecture
Strict layered architecture with ports/adapters and enforced dependency direction:
apps/web (React 19 + Vite) → packages/application (use cases) → packages/domain (pure logic)
- Domain — Pure functions, no I/O, no framework imports. All state transitions are deterministic. Errors returned as values (
DomainError), never thrown. Adapters may throw only for programmer errors. - Application — Orchestrates domain calls via port interfaces (e.g.,
EncounterStore). No business logic here. - Web — React adapter. Implements ports using hooks/state.
Layer boundaries are enforced by scripts/check-layer-boundaries.mjs, which runs as a Vitest test. Domain and application must never import from React, Vite, or upper layers.
Conventions
- Biome 2.0 for formatting and linting (no Prettier, no ESLint). Tab indentation, 80-char lines. Imports are auto-organized alphabetically.
- TypeScript strict mode with
verbatimModuleSyntax. Use.jsextensions in relative imports when required by the repo's ESM settings (e.g.,./types.js). - Branded types for identity values (e.g.,
CombatantId). Prefer immutability/readonlywhere practical. - Domain events are plain data objects with a
typediscriminant — no classes. - Tests live in
packages/*/src/__tests__/*.test.ts. Test pure functions directly; map acceptance scenarios and invariants from specs to individualit()blocks. - Feature specs live in
specs/<feature>/with spec.md, plan.md, tasks.md. The project constitution is at.specify/memory/constitution.md.
Constitution (key principles)
The constitution (.specify/memory/constitution.md) governs all feature work:
- Deterministic Domain Core — Pure state transitions only; no I/O, randomness, or clocks in domain.
- Layered Architecture — Domain → Application → Adapters. Never skip layers or reverse dependencies.
- Clarification-First — Ask before making non-trivial assumptions.
- MVP Baseline — Say "MVP baseline does not include X", never permanent bans.
- Every feature begins with a spec — Spec → Plan → Tasks → Implementation.
Active Technologies
- TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite (003-remove-combatant)
- In-memory React state (local-first, single-user MVP) (003-remove-combatant)
- TypeScript 5.x (project), Go binary via npm (Lefthook) +
lefthook(npm devDependency) (006-pre-commit-gate) - TypeScript 5.x (strict mode, verbatimModuleSyntax) + Knip v5 (new), Biome 2.0, Vitest, Vite 6, React 19 (007-add-knip)
- TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Biome 2.0, existing domain/application packages (008-persist-encounter)
- Browser localStorage (adapter layer only) (008-persist-encounter)
- TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Biome 2.0, Vites (009-combatant-hp)
- TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, shadcn/ui, Lucide React (icons) (010-ui-baseline)
- N/A (no storage changes — localStorage persistence unchanged) (010-ui-baseline)
- TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, shadcn/ui-style components, Lucide React (icons) (011-quick-hp-input)
- N/A (no storage changes -- existing localStorage persistence handles HP via
adjustHpUseCase) (011-quick-hp-input) - N/A (no storage changes -- existing localStorage persistence unchanged) (012-turn-navigation)
- N/A (no storage changes -- purely derived state, existing localStorage persistence unchanged) (013-hp-status-indicators)
- TypeScript 5.8 (strict mode, verbatimModuleSyntax) + jscpd (new dev dependency), Lefthook (existing), Biome 2.0 (existing), Knip (existing) (015-add-jscpd-gate)
- N/A (no storage changes) (015-add-jscpd-gate)
- Browser localStorage (existing adapter, transparent JSON serialization) (016-combatant-ac)
- TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, Lucide React (icons) (017-combat-conditions)
- N/A (no storage changes — existing localStorage persistence unchanged) (019-combatant-row-declutter)
- TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React (icons) (020-fix-zero-hp-opacity)
Recent Changes
- 003-remove-combatant: Added TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite