Files
maestro/CLAUDE.md
T

6.4 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

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/<file>.test.ts  # run a single test file

Worker processes can be run against TypeScript source directly (for dev/testing) via:

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 <runId>. It reads only runs/<runId>/job.json and appends progress/results to runs/<runId>/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
app/ Active console (the dashboard users actually use) — React SPA on :4519 (vite dev; proxies /api+/ws to :4517). Data & wiring live here: src/api.js, src/app.jsx, src/adapt.js.
design/ui_kits/console/ Active console UI — React UI components, the single source of truth for the console; app/ loads them via vite. Frontend UI changes go here; data/actions go in app/src/. Strings go through i18n.js (5 langs).
web/ ⚠️ Legacy / deprecated — old vanilla-JS dashboard served directly by the daemon. Superseded by the app/ + design/ui_kits/console/ React console. Do NOT add new features, config entries, or UI here — build them in app/ + design/ui_kits/console/.

Frontend rule of thumb: the live console is design/ui_kits/console/ (UI) + app/src/ (data/wiring) on :4519. web/ is legacy — never target it for new work.

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 varsMAESTRO_MODEL_{EASY,MEDIUM,HARD} (executor), MAESTRO_MODEL_REVIEW_* (reviewer), MAESTRO_MODEL_PLAN_* (planner).
  4. DEFAULT_MODEL = claude-opus-4-8.

Example projects.models:

{"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/<runId>/ 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