130 lines
7.4 KiB
Markdown
130 lines
7.4 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
pnpm check # Merge gate — must pass before every commit (audit + knip + biome + typecheck + test/coverage + jscpd)
|
|
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 (`EncounterStore`, `BestiarySourceCache`). No business logic here.
|
|
- **Web** — React adapter. Implements ports using hooks/state. All UI components, routing, and user interaction live here.
|
|
|
|
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.
|
|
|
|
### Data & Storage
|
|
|
|
- **localStorage** — encounter persistence (adapter layer, JSON serialization)
|
|
- **IndexedDB** — bestiary source cache (`apps/web/src/adapters/bestiary-cache.ts`, via `idb` wrapper)
|
|
- **`data/bestiary/index.json`** — pre-built search index for creature lookup, generated by `scripts/generate-bestiary-index.mjs`
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
apps/web/ React app — components, hooks, adapters
|
|
packages/domain/src/ Pure state transitions, types, validation
|
|
packages/application/src/ Use cases, port interfaces
|
|
data/bestiary/ Bestiary search index
|
|
scripts/ Build tooling (layer checks, index generation)
|
|
specs/NNN-feature-name/ Feature specs (spec.md, plan.md, tasks.md)
|
|
.specify/ Speckit config (templates, scripts, constitution)
|
|
docs/agents/ RPI skill artifacts (research reports, plans)
|
|
.claude/skills/ Agent skills (rpi-research, rpi-plan, rpi-implement)
|
|
```
|
|
|
|
## Tech Stack
|
|
|
|
- TypeScript 5.8 (strict mode, `verbatimModuleSyntax`)
|
|
- React 19, Vite 6, Tailwind CSS v4
|
|
- Lucide React (icons)
|
|
- `idb` (IndexedDB wrapper for bestiary cache)
|
|
- Biome 2.0 (formatting + linting), Knip (unused code), jscpd (copy-paste detection)
|
|
- Vitest (testing, v8 coverage), Lefthook (pre-commit hooks)
|
|
|
|
## 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 `.js` extensions 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/`readonly` where practical.
|
|
- **Domain events** are plain data objects with a `type` discriminant — no classes.
|
|
- **Tests** live in `packages/*/src/__tests__/*.test.ts`. Test pure functions directly; map acceptance scenarios and invariants from specs to individual `it()` blocks.
|
|
- **Feature specs** live in `specs/NNN-feature-name/` with spec.md (and optionally plan.md, tasks.md for new work). Specs describe features, not individual changes. The project constitution is at `.specify/memory/constitution.md`.
|
|
- **Quality gates** are enforced at pre-commit via Lefthook's `pnpm check` — the project's single earliest enforcement point. No gate may exist only as a CI step or manual process.
|
|
|
|
## Self-Review Checklist
|
|
|
|
Before finishing a change, consider:
|
|
- Is this the simplest approach that solves the current problem?
|
|
- Is there duplication that hurts readability? (But don't abstract prematurely.)
|
|
- Are errors handled correctly and communicated sensibly to the user?
|
|
- Does the UI follow modern patterns and feel intuitive to interact with?
|
|
|
|
## Speckit Workflow
|
|
|
|
Speckit (`/speckit.*` skills) manages the spec-driven development pipeline. Specs are **living documents** that describe features, not individual changes.
|
|
|
|
### Issue-driven workflow
|
|
- `/write-issue` — create a well-structured Gitea issue via interactive interview
|
|
- `/integrate-issue <number>` — fetch an issue, route it to the right spec, and update the spec with the new/changed requirements. Then implement directly.
|
|
- `/sync-issue <number>` — push acceptance criteria from the spec back to the Gitea issue
|
|
|
|
### RPI skills (Research → Plan → Implement)
|
|
- `rpi-research` — deep codebase research producing a written report in `docs/agents/research/`
|
|
- `rpi-plan` — interactive phased implementation plan in `docs/agents/plans/`
|
|
- `rpi-implement` — execute a plan file phase by phase with automated + manual verification
|
|
|
|
### Choosing the right workflow by scope
|
|
|
|
| Scope | Workflow |
|
|
|---|---|
|
|
| Bug fix / CSS tweak | Just fix it, commit |
|
|
| Small change to existing feature | `/integrate-issue` → implement → commit |
|
|
| Larger addition to existing feature | `/integrate-issue` → `rpi-research` → `rpi-plan` → `rpi-implement` |
|
|
| New feature | `/speckit.specify` → `/speckit.clarify` → `/speckit.plan` → `/speckit.tasks` → `/speckit.implement` |
|
|
|
|
Speckit manages **what** to build (specs as living documents). RPI manages **how** to build it (research, planning, execution). The full speckit pipeline is for new features. For changes to existing features, update the spec via `/integrate-issue`, then use RPI skills if the change is non-trivial.
|
|
|
|
### Current feature specs
|
|
- `specs/001-combatant-management/` — CRUD, persistence, clear, batch add, confirm buttons
|
|
- `specs/002-turn-tracking/` — rounds, turn order, advance/retreat, top bar
|
|
- `specs/003-combatant-state/` — HP, AC, conditions, concentration, initiative
|
|
- `specs/004-bestiary/` — search index, stat blocks, source management, panel UX
|
|
- `specs/005-player-characters/` — persistent player character templates (CRUD), search & add to encounters, color/icon visual distinction, `PlayerCharacterStore` port
|
|
|
|
## Constitution (key principles)
|
|
|
|
The constitution (`.specify/memory/constitution.md`) governs all feature work:
|
|
|
|
1. **Deterministic Domain Core** — Pure state transitions only; no I/O, randomness, or clocks in domain.
|
|
2. **Layered Architecture** — Domain → Application → Adapters. Never skip layers or reverse dependencies.
|
|
3. **Clarification-First** — Ask before making non-trivial assumptions.
|
|
4. **MVP Baseline** — Say "MVP baseline does not include X", never permanent bans.
|
|
5. **Spec-driven features** — Features are described in living specs; evolve existing specs via `/integrate-issue`, create new ones via `/speckit.specify`. Bug fixes and tooling changes do not require specs.
|
|
|
|
## Active Technologies
|
|
- TypeScript 5.8 (strict mode, `verbatimModuleSyntax`) + React 19, Vite 6, Tailwind CSS v4, Lucide Reac (005-player-characters)
|
|
- localStorage (new key `"initiative:player-characters"`) (005-player-characters)
|
|
|
|
## Recent Changes
|
|
- 005-player-characters: Added TypeScript 5.8 (strict mode, `verbatimModuleSyntax`) + React 19, Vite 6, Tailwind CSS v4, Lucide Reac
|