Contracts the eight transcript row kinds, daemon-vs-client decisions, the transcript/reduced-state projections and their cursor/replay semantics, the ACP handshake and declined capabilities, the adapter install command, and the approval nonce rules (generation, live-only placement, secrecy from the agent and durable history, single-use/duplicate resolution) and bounded prompt/cancel liveness. Amends RESTART.md to state plainly that a structured session's agent is a daemon child, not tmux-backed: a restart ends the active turn, the transcript survives in the event log, and pending approvals/nonces do not. Refs #137
5.8 KiB
Maestro
A terminal-session orchestrator for one machine. One daemon per host owns every interactive shell or agent (Claude Code, opencode, plain bash) running on it; clients — CLI, TUI, browser — attach over a plain HTTP + WebSocket API. tmux for persistence, WebSocket for transport.
Architecture
maestro daemon tmux wrapper + session API + SPA on 127.0.0.1:7070
├── GET / browser SPA, embedded (feature `web`)
├── GET/POST /api/sessions list / spawn
├── DELETE /api/sessions/:id kill
├── POST /api/sessions/:id/input type text into a session
├── POST /api/sessions/:id/prompt an agent reports its first prompt (#131)
├── GET /api/info hostname, version, home dir, spawn presets
├── GET /api/dirs directory listing (spawn autocomplete)
├── GET /api/recent-dirs spawn history
├── GET /api/gitroot enclosing repo of a path
├── WS /api/events Snapshot + session deltas
└── WS /api/attach/:id raw PTY bytes
maestro list/spawn/attach/kill/send CLI client
maestro tui ratatui client
browser loads the SPA from the daemon, same port
No authentication layer, no registry, no peer-to-peer. Whoever can reach the
daemon's bind address can drive every session on the host — same trust model as
tmux itself. The operator picks the perimeter: 127.0.0.1 for local-only, a
WireGuard/LAN address to share with their own devices, never a public IP.
Every config field has a default, so ~/.config/maestro/config.toml is
optional.
Where the work is
Gitea issues on naps62/maestro, grouped
into milestones.
.claude/linear.json (legacy filename, tracker: gitea) wires /yolo and
/work to that tracker — they pick from open, unassigned issues whose
dependencies are all closed. /blitz <milestone> sweeps a whole milestone.
Labels carry one value per axis: area/*, type/*, difficulty/*.
difficulty drives model selection downstream, so it tracks the shape of the
work, not its size.
Repo layout
Cargo workspace. NAP-245 unified the per-binary crates behind a single
crates/maestro/ umbrella that ships the maestro binary with feature gates
(cli / daemon / tui / web, default = all four).
crates/
├── maestro/ # umbrella binary `maestro` (cli + daemon + tui, feature-gated) (NAP-245)
├── maestro-protocol/ # shared wire types (NAP-211)
├── maestro-config/ # shared on-disk config schema (NAP-233)
├── maestro-cli/ # `maestro list/spawn/attach/kill` dispatch (NAP-216)
├── maestro-daemon/ # `maestro daemon` — tmux wrapper + HTTP/WS API + SPA (NAP-212, NAP-214)
├── maestro-tui/ # `maestro tui` — ratatui session manager (NAP-275 epic)
├── maestro-installer/ # `maestro-install` wizard: cargo install + systemd (NAP-225)
└── maestro-e2e/ # cross-process integration tests (NAP-217)
web/ # browser SPA (NAP-218), embedded by the daemon's `web` feature
flake.nix # Nix flake: packages, checks, dev shell, `nix run .#dev`
nix/ # Home Manager module (`services.maestro`)
See docs/INSTALL.md for end-user install instructions.
docs/WORKTREES.md is the contract for session worktrees — placement,
collisions, what blocks a kill, and what --force leaves behind. Read it
before changing anything that creates or removes one.
docs/SIDEBAR.md is the contract for session cards in the web and TUI
sidebars — line layout, activity colours, duration format, state precedence.
Read it before changing sidebar rendering.
docs/PREVIEW.md is the contract for what the content pane shows while the
operator moves the sidebar highlight — what a preview may touch, what ends it,
what commits it. Read it before changing sidebar-driven navigation.
docs/RESTART.md is the contract for what clients do when the daemon restarts
or is upgraded under them — the build stamp on /api/info, the status
indicator's states, the blocking overlay, grace and poll timings. Read it
before changing reconnect behaviour on either client.
docs/COMMAND-BAR.md is the contract for the ctrl-k command bar in both
clients — how it opens, the sigils, the key model, the match-and-rank algorithm
the two matchers must agree on, and the fixture table they are tested against.
Read it before changing either bar or either matcher.
docs/STRUCTURED-VIEW.md is the contract for a structured session (an agent
run as a daemon child over ACP instead of in a tmux pane) — the eight
transcript row kinds, the two projections and their cursor/replay semantics,
the handshake and declined capabilities, and the approval nonce rules. Read
it before changing anything that renders a structured session.
nix run .#dev (or just dev) brings up the whole local stack under
process-compose: a debug daemon on 127.0.0.1:7071, then Vite on :5173 once the
daemon answers /api/info. The SPA proxies /api/* back to the daemon, so only
the UI is exposed on the network. The dev port is deliberately not the daemon's
own default 7070 (devDaemonPort in flake.nix), so the stack never fights a
real daemon on the same host.
Conventions
- TDD by default. Failing test first, then minimal impl. Commit test + impl together.
- Frequent commits. One logical unit per commit; don't pile work up.
- Conventional commits with
maestroscope. Example:feat(maestro): add Hello message round-trip test. cargo fmt+cargo clippy --all-targets -- -D warningsbefore every commit.- Integration tests use real
tmuxandgit. Don't mock them. Isolate via temp$HOMEandTMUX_TMPDIR.