# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commands ```bash npm install # install dependencies npm run typecheck # tsc --noEmit (type check without build) npm run build # compile TypeScript → dist/ (also copies schema.sql) npm run dev # start daemon on http://127.0.0.1:4517 npm test # run all tests (node --test via tsx) tsx --test test/.test.ts # run a single test file ``` Worker processes can be run against TypeScript source directly (for dev/testing) via: ```bash MAESTRO_WORKER_CMD="tsx src/executor/worker.ts" npm run dev ``` ## Architecture Maestro is a local-first daemon that manages tasks across multiple git projects. It drives tasks through a complexity-based approval pipeline and auto-executes approved tasks via headless Claude Code agents. ### Process Model (critical to understand) The system is split into two strictly separated process roles: **daemon** (`src/daemon/`) — the only DB writer. Responsible for: - Serving REST + WebSocket API (`src/api/`) - Running the orchestrator loop (tick = `ingest → reap → claim`) - Spawning one worker OS process per task - Ingesting worker output from `outbox.ndjson` into SQLite - Reaping dead workers, managing retries/back-off, state transitions **worker** (`src/executor/worker.ts` + `src/executor/pipeline.ts`) — a dumb executor that never touches the DB. Each task gets its own `node dist/executor/worker.js `. It reads only `runs//job.json` and appends progress/results to `runs//outbox.ndjson`. The daemon ingests this file idempotently via a `lastSeq` cursor. Communication between daemon and worker is **file + signal only** (`job.json`, `outbox.ndjson`, `heartbeat`, `SIGTERM`). This makes execution survive daemon restarts. ### Key Source Modules | Path | Purpose | |---|---| | `src/model/` | Types (`types.ts`), state machine (`status.ts`), complexity levels (`complexity.ts`), score scheduling algorithm (`scoring.ts`) | | `src/store/` | SQLite access layer (`store.ts`), schema (`schema.sql`), state-machine guards, mappers | | `src/daemon/orchestrator.ts` | Score-based task scheduling loop: `ingest → reap → claim` | | `src/daemon/ingest.ts` | Reads worker outbox.ndjson, writes DB events, triggers WS push | | `src/executor/protocol.ts` | File protocol definitions + heartbeat/liveness helpers | | `src/executor/pipeline.ts` | Worker execution pipeline: worktree setup → agent run → verify → dual review → output | | `src/executor/cc.ts` | Claude Agent SDK wrapper for headless CC runs | | `src/executor/reviewer.ts` | Code review + security audit agent runs | | `src/executor/merge.ts` | `--no-ff` auto-merge logic on exec_review accept | | `src/api/server.ts` | Fastify REST + WebSocket server | | `src/mcp/` | MCP server tools exposed to Claude Code sessions | | `src/sync/` | One-way sync from legacy `todo.json` files | | `web/` | Dashboard SPA (multi-project, real-time WS, inline approve/reject) | ### Task Lifecycle ``` init → [Hard: analyzing → plan_review] → [Medium: speccing → spec_review] → ready → queued → executing → exec_review → done ``` - **Hard**: planner agent produces analysis + task decomposition; requires `plan_review` approval - **Medium**: agent writes a spec (what + why); requires `spec_review` approval - **Easy**: agent writes operations record; goes directly to `ready` - All tasks with code changes pass through `exec_review` (dual review: code-review + security audit) - `accept` at `exec_review` triggers automatic `--no-ff` merge; `merge:false` skips merge ### Score Scheduling ``` score = P(self) + Σ P(completed deps) + Σ P(tasks blocked by self) ``` `P0=3, P1=2, P2=1`. Higher score → picked first. No preemption. ### Model Tier Assignment Built-in default for **every role × every complexity** is `claude-opus-4-8` (`DEFAULT_MODEL` in `src/executor/models.ts`). Models are **configurable per project** via `projects.models` (JSON). **Resolution order** (`resolveModel`, highest → lowest): 1. **`project.models[role]`** — per-project per-scenario config. Value is either a model id string (applies to all complexities) or `{easy,medium,hard}` (per-complexity). Applies to **all** roles incl. reviewer/conflict. 2. **legacy `project.model`** — single override, **executor/planner only** (NOT reviewer/conflict, preserving self-review/conflict-resolution independence). 3. **env vars** — `MAESTRO_MODEL_{EASY,MEDIUM,HARD}` (executor), `MAESTRO_MODEL_REVIEW_*` (reviewer), `MAESTRO_MODEL_PLAN_*` (planner). 4. **`DEFAULT_MODEL`** = `claude-opus-4-8`. Example `projects.models`: ```json {"executor":{"easy":"claude-sonnet-4-6","hard":"claude-opus-4-8"},"reviewer":"claude-fable-5"} ``` Model fallback chain on unavailability: `opus-4-8 → sonnet-4-6 → fable-5` (`MODEL_FALLBACK_CHAIN`). Note: `claude-fable-5` is no longer a built-in default (it is unavailable in some environments); configure it explicitly per project if desired. ### Data Directory Default `~/.maestro/` — contains SQLite DB, `runs//` directories (job.json, outbox.ndjson, heartbeat, sandbox.sb, transcripts), and git worktrees. ### Key Env Vars | Var | Default | Note | |---|---|---| | `MAESTRO_PORT` | `4517` | daemon listen port | | `MAESTRO_DATA_DIR` | `~/.maestro` | SQLite + worktree root | | `MAESTRO_ORCH_INTERVAL` | `15` | orchestrator tick seconds; `0` disables | | `MAESTRO_WORKER_CMD` | `node dist/executor/worker.js` | override for tsx dev | | `MAESTRO_NOTIFY` | on | set `0` to disable macOS notifications | | `MAESTRO_SANDBOX` | off | set `on` for OS-level write fence + ulimits on workers |