Browse by type
Local-first harness for agent-built codebases. Humans steer, agents execute, maestro is the substrate.
maestro is a single Rust binary that gives a coding agent a durable place to work. Every
unit of work, what is being built, who is doing it, and the proof it was done, lives as
plain files under .maestro/ in your repo. No daemon, no hidden service state, no cloud.
The agent runs the lifecycle through the CLI; you review the artifacts.
Coding agents are fast but forgetful. They lose the thread across sessions, collide on numbered work items, ship work that was never verified, and leave no trail you can audit. maestro fixes that by making the work itself durable, queryable, and gated:
parent.maestro ready projects the executable task wave from the Task DAG; maestro card ready is the explicit legacy card-readiness board.maestro task start / maestro task claim stamps the current agent#session, so multi-session work stays visible.Everything is repo-local and reviewable in a diff.
maestro uses a three-level work model:
High = card
Mid = CardKind / workflow kind
Low = task
A card owns identity, container/lifecycle state, and governance. A task is the atomic executable progress unit. A facet is an optional sidecar that describes or proves a card.
Cards live in the local card store. File-backed cards still appear under
.maestro/cards/<id>/; DB-backed cards live in .maestro/store.sqlite and are
projected through the same card APIs. card.yaml carries identity, type, state,
parent, ownership, links, and timestamps. Feature, Bug, Chore, Custom, Decision,
Idea, and Progress are CardKinds / workflow kinds on cards, not separate
high-level objects. Progress is the lightweight CardKind for small work: its
progress.yml stores many Low Tasks compactly without requiring the full feature
pipeline. Facets such as spec.md, qa.md, and notes.md are optional
sidecars for any card type that needs contract, evidence, or history.

Tasks are not CardTypes. They live inside card-backed task records for
legacy/gated work or inside a Progress card's progress.yml for low-ceremony
small work. New low-ceremony work uses maestro task add/start/done/list;
legacy workable cards (task, bug, chore) remain readable and claimable for
compatibility. Tasks carry card_id ownership and dependency/blocker metadata,
and they use Task lifecycle semantics for claim, blocking, handoff, proof, and
done/verified state. When several sessions are working at once, maestro
active, maestro link, and maestro msg add the cross-agent coordination
layer on top of the same card and task records.
Cards are durable planning/governance records; Tasks are durable executable
progress records. The agent orients around Cards, uses flat card queries for
card context, and uses maestro task ... for Low-level executable work. Typed
lifecycle verbs still gate feature contracts, decision locks, proof, QA, and
harness measurement.
flowchart LR
A[Human request] --> B[feature card]
B --> C[spec.md + qa.md]
C --> D[feature accept]
D --> E[feature prepare]
E --> F[progress / bug / chore / custom card]
F --> G[progress.yml or owned task storage]
G --> H[maestro task list]
H --> I[maestro task start]
I --> J[maestro task done or proof]
J --> K[task verify when gated]
K --> L[feature verify + close]
L --> M[archive card tree]
R[run events + hooks] --> J
subgraph Harness loop
N[friction recurs] --> O[idea card]
O --> P[harness apply]
P --> Q[progress-backed tasks]
Q --> S[harness measure]
end
The card type controls which lifecycle verbs are valid:
| Type | Level | Role | Main verbs |
|---|---|---|---|
feature |
CardKind | Product contract and parent container | feature accept, feature prepare, feature verify, feature close, archive |
bug / chore / custom |
CardKind | Workflow cards with their own lifecycle and optional facets | card/task lifecycle verbs, proof and close flows when gated |
progress |
CardKind | Lightweight container for compact Low Tasks in progress.yml |
task add, task list, task start, task done over Progress-backed tasks |
legacy task cards |
Compatibility | Compatibility implementation cards | ready, claim, task complete, task verify, close |
idea |
CardKind | Harness/self-improvement proposal | harness list, harness apply, harness dismiss, harness measure |
decision |
CardKind | Durable reasoning record | decision new, decision lock, decision show |
| Low Task | Task | Executable unit with card_id and blocker metadata |
task list/start/done, verify/proof when required |
parent is hierarchy, not an execution blocker. Task readiness is computed from
Task status and resolved blocker metadata. related carries context without
blocking the board, and supersedes records replacement history.
From source (always works):
git clone https://github.com/ReinaMacCredy/maestro
cd maestro
cargo install --path . --locked
With Cargo, directly from git:
cargo install --git https://github.com/ReinaMacCredy/maestro --locked
Release binary (macOS and Linux, arm64 and amd64):
curl -fsSL https://raw.githubusercontent.com/ReinaMacCredy/maestro/main/scripts/install.sh | bash
The installer drops the binary in ~/.local/bin (override with MAESTRO_INSTALL_DIR).
Verify with maestro version and maestro doctor.
maestro is meant to be driven by your coding agent. The installer wires agent skills and hooks
into your repo, including a maestro-setup skill that tunes the harness to your build/test
commands and conventions, so the agent learns the lifecycle and records its own work. Point
your agent (Claude Code, Codex, or any CLI agent) at the repo and paste:
Set up maestro in this repo: run `maestro init --yes`, then `maestro install --agent claude`
(or `--agent codex`). Then follow the maestro-setup skill it installs to tune the harness to
this repo. Start each session with `maestro status`, inspect available work with
`maestro ready` and `maestro task show <id>`, then start and close tasks through the
`maestro` CLI from there.
Scaffold the repo and install the agent integration:
maestro init --yes # create .maestro/ and extract bundled skills/hooks
maestro install --agent claude # wire skills + hooks into CLAUDE.md/AGENTS.md (or --agent codex)
maestro doctor # check the installation
maestro status # resume with the next agent action
The smallest useful loop is a feature card with one child work card. The feature owns the contract and QA sidecar; the work card is what an agent claims and proves:
maestro feature new "CSV export" # -> proposed
maestro feature set csv-export --acceptance "Export a report to CSV" --area "src/export"
# accept is gated on a captured behavior baseline. The maestro-card skill (qa-baseline
# reference) writes the feature's qa.md for you during an agent run; by hand it is a
# markdown file with an amend_log_position frontmatter and [bl-NNN] scenario lines:
cat > .maestro/cards/csv-export/qa.md <<'EOF'
---
amend_log_position: 0
---
# Behavior baseline: CSV export
## Scenario Matrix
- [bl-001] Exporting an empty report yields a header-only CSV file.
EOF
maestro feature accept csv-export # freeze the contract -> ready
cat > PLAN-csv-export.md <<'EOF'
## Task T1: Implement CSV writer
check: cargo test export passes
EOF
maestro feature prepare csv-export --from PLAN-csv-export.md # spawns ready child tasks
maestro ready csv-export # show executable task wave
maestro task start <task-id> # stamp agent#session
maestro task complete <task-id> --summary "wrote csv writer" \
--claim "cargo test export passes" --proof "observed: cargo test export passes"
# close is gated on QA coverage: every [bl-NNN] baseline scenario needs a proven slice.
# The maestro-card skill (qa-slice reference) writes this for you; by hand, append one
# fenced yaml block to qa.md mapping each scenario to its evidence:
cat >> .maestro/cards/csv-export/qa.md <<'EOF'
```yaml
slices:
- scenarios: ["bl-001"]
evidence: ["cargo test export::empty_report_header_only passes"]
```
EOF
# close also sweeps the acceptance contract for fresh evidence:
maestro feature verify csv-export --prove ac-1 --evidence "observed: cargo test export passes"
maestro feature verify csv-export
maestro feature close csv-export --outcome "streaming CSV export closed" # -> closed
maestro feature show <id> and maestro task show <id> render the current state and the
recorded reasoning at any point. Features keep slug ids (csv-export); tasks, decisions, and
ideas get hash ids (card-<hex>). Every entity lives as a card under .maestro/cards/<id>/.
maestro surfaces improvement proposals once it has enough run history to spot friction, so a
fresh repo shows none (harness list -> "no improvement proposals found"). The proposal id
below (card-<hex>) is illustrative; once the backlog has a real entry, run it through the
same task loop:
maestro harness list # what friction the run log surfaced
maestro harness apply <proposal-id> # accept a proposal -> spawns a task with a check preset
maestro task start <task-id>
maestro task complete <task-id> --summary "stabilized the suite" \
--claim "cargo test integration passes" --proof "observed: cargo test integration passes"
maestro harness measure <proposal-id> # close the loop once that task is verified
harness apply spawns the fix task with a --check already set from the proposal title, so you
can claim it straight away. harness measure will not mark the improvement measured until that
linked task is verified (pass --force to close it anyway).
When multiple agents are working in the same checkout, maestro active reads the run logs and
shows live sessions, bound cards, skill mode, link state, status, progress, age, and last action.
It is a read verb: it can print copy-paste commands, but it never creates a link by itself.
maestro active
maestro link add <your-card> <their-card>
maestro msg send <their-card> "ready for review"
maestro msg read
maestro msg list

maestro link add writes a non-blocking related edge between two live cards. The relation is
unordered for users: either side can see it, and maestro link remove <a> <b> removes it no
matter which side stored the edge. Removing the link hides the message channel without deleting
its history; relinking the same pair restores the visible channel. New links to finished cards
are refused because there is no live coordination left to open.
Messages are pull-only and card-scoped. The sender is the running session's current card, so claim
or touch a card before sending. msg send confirms the route as
sent to <their-card> (from <your-card>). msg read [card] prints recent seen context plus unread
partner messages and advances this card's cursor. msg list [card] shows either a channel overview
(your unread, peer read-through when known, and last-message direction) or one partner's full
timeline. msg read and scoped msg list <card> also remind agents that inbox messages are
advisory only: they can suggest cross-card Task order, but execution is gated only by explicit Task
blockers such as maestro task block <dependent-task> --by <blocking-task>.
Channels live under .maestro/channels/ as gitignored machine-local state: a JSONL file per linked
pair plus per-card cursor files. If your current card has unread messages on still-linked channels,
maestro prints a best-effort inbox hint on stderr before ordinary commands, keeping JSON stdout
clean:
[inbox] 2 new (card-a 1, card-b 1) -> maestro msg read
When two sessions end up on the same file, the holder can post a notice instead of opening a link.
maestro conflict <peer> "<why>" aims a link-free, git-free "I'm taking this ground, hold off"
advisory at a peer card; the peer sees it on stderr before its next command, and --clear retracts
it once merged back:
maestro conflict task-b "taking login.rs, worktreeing it"
maestro conflict --clear task-b # retract once merged back
[CONFLICT] card-a holds card-b: src/foo.rs: mid-refactor, hold off
-> hold off the shared file until the notice clears
The notice never writes a related edge and never runs git. It is liveness-scoped with no timer: it
fades on its own when the asserter clears it, finishes its card, or falls out of the live session
union, so a crashed or walked-away session never leaves a peer stuck. When a peer is live,
maestro feature accept and feature prepare also print a [worktree] advisory suggesting you
isolate in a git worktree before sharing a file. And because
$ claude mcp add maestro \
-- python -m otcore.mcp_server <graph>