Minimal Multi-Agent Workflow runner. It coordinates an executor and one or more reviewers through planning, implementation, testing, and human approval gates.
Default roles: executor = claude (Opus 4.8, effort xhigh) and a two-reviewer panel — codex (GPT-5.5) + claude (Sonnet) — that run in parallel each round. Each role keeps a persistent session across phases, token usage and cost are measured precisely, and an interactive single-process UI shows live agent output.
新手从 docs/TUTORIAL.md 上手 —— 含一个你可以亲自在 human gate 参与的交互式示例:
mma run examples/demo_requirement.md --config interactive
flowchart TD
Start(["mma run req.md"]) --> Init["初始化<br/>解析 config / 解析 workdir<br/>创建 flow/ 与 log/run-id/<br/>启动交互式 UI + 常驻输入框"]
Init --> P["Planning 阶段<br/>_planning()"]
P --> HGP{"Human Gate: planning"}
HGP -->|"go / implement"| I["Implementation 阶段<br/>_implementation()"]
HGP -->|"反馈文本"| PRB["写 plan_review_human{k}.md<br/>Executor rebuttal → reviewer panel 复审"]
PRB --> HGP
HGP -->|"exit / quit"| EX(["安全退出 (终止所有 agent)"])
I --> HGF{"Human Gate: final"}
HGF -->|"finish / done"| Done(["phase = finished"])
HGF -->|"反馈文本"| IRB["写 implementation_test_human{k}.md<br/>Executor rebuttal → reviewer panel 复审"]
IRB --> HGF
HGF -->|"exit / quit"| EX
评审按轮同步进行:每轮所有(尚未通过的)reviewer 并行评审,一次 executor rebuttal 统一回应所有未通过的意见;最多 max_rounds 轮,直到每个 reviewer 都通过(结构化 verdict pass,或 marker 模式下输出 ACCEPT! / TEST PASS!)或轮数耗尽。最后一轮不再 rebuttal(改了也没有 reviewer 复核);若 rebuttal 修改了 plan/代码,已通过的 reviewer 会重新评审新版本。
flowchart TD
EX["Executor 产出 (plan.md / 代码)"] --> RV["Reviewer 评审 / 实测<br/>→ plan_review{i}.md / implementation_test{i}.md"]
RV --> OK{"输出含 [REVIEWERx] ACCEPT! / TEST PASS! ?"}
OK -->|"是"| Pass(["该 reviewer 通过"])
OK -->|"否"| RB["Executor rebuttal<br/>→ plan_rebuttal{i}.md / implementation_rebuttal{i}.md"]
RB --> Cnt{"round < max_rounds ?"}
Cnt -->|"是, round+1"| RV
Cnt -->|"否"| Warn(["进入 human gate"])
flowchart TD
R["build_command (claude/codex/plain)<br/>首call 用 --session-id;后续 resume<br/>prompt 走 stdin, 子进程流式输出"] --> Parse["按 kind 解析事件流<br/>readable text + 精确 tokens + session id"]
Parse --> Watch{"子进程结束 / 超时 / exit"}
Watch -->|"超时·取消"| Kill["终止进程组 (SIGTERM→SIGKILL)"] --> Code
Watch -->|"正常"| Code{"exit_code == 0 ?"}
Code -->|"是"| OKr(["AgentResult(text, tokens, cost, session_id)"])
Code -->|"否, --yes"| Err(["抛错停止"])
Code -->|"否, 交互"| HP{"human: retry / continue / exit"}
HP -->|retry| R
HP -->|continue| OKr
HP -->|exit| Stop(["退出"])
pip install -e .This exposes a global mma command and pulls in prompt_toolkit and rich.
On older Apple system Python/pip, editable install may need the legacy path:
python3 setup.py develop --userIf mma is installed but not on PATH, add the user Python bin dir to PATH or symlink it, e.g.:
ln -s ~/Library/Python/3.9/bin/mma /opt/homebrew/bin/mmaRequires the claude and codex CLIs on PATH for the default config.
Point at a requirement markdown file from any directory. The default config is claude_codex:
mma run /path/to/requirement.mdEquivalent explicit form:
mma run --requirement /path/to/requirement.md --config claude_codexConfig aliases:
claude_codex(default):examples/config.claude_codex.json— claude executor (Opus, ultracode) + a parallel reviewer panel (codex GPT-5.5, claude Opus +ultracode, claude Sonnet).interactive:examples/config.interactive.json— snappy sonnet executor + codex reviewer.codex:examples/config.codex.json— codex for both roles.codex_cursor:examples/config.codex_cursor.json.fake/fake2:examples/config.fake.json(1 reviewer) /config.fake2.json(2 reviewers) — offline smoke tests.
Useful flags:
--ui {auto,tui,plain}— force the interactive TUI or plain text output (defaultauto: TUI when attached to a terminal).--yes— auto-approve every human gate (headless / CI).--project <dir>— isolated project folder (created if needed).
Project layout: the project dir defaults to --project → workdir in config → the requirement file's directory. Each project is self-contained and runs never clobber each other:
workspace/— generated code (the executor's cwd; a clean tree you cangit init)flow/<run-id>/—plan.md, reviews, rebuttals, and the implementation report (isolated per run)log/<run-id>/— prompts, raw agent output,status.json,sessions.jsonrequirement.md— a snapshot of the requirement for that run
Resume an interrupted run (skips completed phases, re-attaches agent sessions):
mma resume /path/to/projectBatch a whole directory of tasks — each *.md runs headless as its own isolated project, N at a time; finished tasks are skipped and interrupted ones auto-resumed:
mma queue ./tasks --config claude_codex --concurrency 3When run in a terminal, MMA opens a single-process full-screen UI (no tmux required):
- Dashboard (top): current phase/round and a per-agent table — status, live token counts (in/out), cost, and elapsed time.
- Output pane (middle): the active agent's readable output, streamed live (assistant text, reasoning, tool/command calls, and
-/+edit diffs), auto-following the tail. Code and tool output are shown dimmed; additions are green, removals red. - Input box (bottom): always-on.
Entersends,Ctrl-Jinserts a newline, multi-line paste is supported.
Navigating the output pane (Tab focuses it; Tab again returns to the input box):
↑/↓(ork/j) scroll one line;Enter/Spacepage forward,b/Backspacepage back (PageUp/PageDowntoo); the mouse wheel scrolls as well.g/Homejump to the top of the current call and freeze; while you are scrolled up the view freezes for reading, and scrolling back to the bottom (orG/End) resumes following the live tail (likeless +F).Eschands the keyboard back to the input box. The pane's border brightens while it has focus, and the status bar switches to the pager key hints.←/→page through each agent call's full transcript (the header shows which call is on screen via the●/⏸ N/Mtag).Homejumps to the top of the current call and freezes it for reading;Endreturns to the live tail and resumes auto-following.- The pane's title bar shows a scroll readout —
L<first>–<last>/<total>with↑N/↓Nfor how many lines are above/below, or⤓ tailwhen you are at the bottom — so a short-looking pane is obviously just the tail, not the whole turn. The complete, untruncated transcript of every call is also on disk atlog/<run-id>/<NNN>_<agent>_<task>.trajectory.md(and the whole run intrajectory.md).
Gate commands typed into the input box:
- planning gate:
go/implementto proceed, or any other text as feedback (written toflow/plan_review_human<k>.md, one file per feedback round) — the executor rebuts it and the reviewer panel then re-reviews the update before returning to the gate. - final gate:
finish/doneto end, or feedback (written toflow/implementation_test_human<k>.md), with the same rebuttal + re-review loop. reviewat either gate sends the current work back through the reviewer panel.exit/quit(orCtrl-C) anywhere terminates all agent processes and exits safely.
Interaction modes (cycle with Ctrl-T, or type full / autopilot / lite):
- full (default) — reviewers + human gates.
- autopilot — reviewers run, human gates auto-approve. Toggling it while sitting at a gate applies from the NEXT gate (type
go/finishto pass the current one); before auto-finishing, reviewers re-review once if their last round didn't pass unanimously. - lite — no reviewers; the executor talks straight to you.
reviewers N typed at any time resizes the reviewer panel for the next review phase (extra reviewers clone the last configured one with fresh sessions).
Reviewer and testing stages always leave a readable artifact in flow/, even when the agent accepts or passes directly.
Token usage is parsed from each CLI's structured output (claude stream-json result.usage + total_cost_usd; codex --json turn.completed.usage), not estimated from characters. Totals accumulate per agent and are shown in the UI and in:
mma metrics --log-dir /path/to/log/<run-id>Codex's CLI does not report a dollar cost, so MMA shows an estimate computed
from its token counts and the hard-coded price table in backends.py
(CODEX_PRICES) — always labelled ~$ in the UI. Verify/adjust the table if
prices change. Claude's cost is exact (total_cost_usd); if a claude call is
killed by timeout/cancel, its tokens are still counted from the streamed events
but the dollar cost for that call is unknown.
Each role keeps one continuous session across phases (claude via --session-id/--resume, codex via exec resume <thread_id>), so later phases retain earlier reasoning. Session ids are recorded in log/<run-id>/sessions.json. Set "persistent": false on an agent to fall back to stateless one-shot calls.
{
"max_rounds": 3,
"agent_timeout_seconds": 1800,
"ui": "auto",
"agents": {
"executor": {
"name": "executor",
"kind": "claude",
"command": "claude",
"model": "claude-opus-4-8",
"effort": "xhigh",
"persistent": true,
"args": ["--dangerously-skip-permissions"]
},
"reviewers": [
{
"name": "reviewer1",
"kind": "codex",
"command": "codex",
"model": "gpt-5.5",
"effort": "xhigh",
"persistent": true,
"args": ["--skip-git-repo-check", "--sandbox", "workspace-write", "-C", "{workdir}"]
}
]
}
}Flow artifacts are archived by MMA itself from each agent's captured stdout (reviews via the typed verdict, rebuttals via the fallback writer) — agents are told to OUTPUT content, not to write flow/ files, so sandboxed backends work out of the box and the audit trail can't be rewritten by agents. Sandboxes: claude agents ship with --dangerously-skip-permissions; the codex counterpart is --yolo (bypasses approvals and the file sandbox — use it for a codex executor in a trusted personal setup; keep reviewers on --sandbox workspace-write, which is also the safer default).
"rereview_policy": "pending" (default) re-runs only not-yet-accepted reviewers each round — accepted votes stand unless a rebuttal changes the plan/workspace fingerprint; "all" re-runs the WHOLE panel every round with the full dispute history in context (a reviewer may withdraw its accept) — fuller cross-examination at roughly roster× the review cost, sensible with cheap panel models.
Per-agent fields: kind (claude | codex | plain) selects command building and output parsing; model, effort, persistent are optional; "ultracode": true (claude kind) lets the role fan out internal multi-agent workflows — supported and encouraged, with one rule: each MMA call is one CLI process, so the workflow must run synchronously within the turn (run_in_background: false, or block on TaskOutput); background-and-notify and scheduled wakeups die at the turn boundary (the executor prompts carry this usage note); args are extra CLI flags ({workspace}, {project}, {flow_dir}, {mma_root} are substituted; {workdir} is an alias for {workspace}). Add more reviewers by appending to reviewers; they run concurrently each round and a single executor rebuttal addresses all of them. Set "parallel_reviewers": false (or --no-parallel) to run them sequentially. Measured: 2 reviewers run ~1.5× faster in parallel; the gap widens with more reviewers.
Structured verdicts & lessons (default in claude_codex): with "verdict": "structured" (or --verdict structured) reviewers emit a schema-validated verdict ({decision, blocking_issues, tests_run, notes}) via claude --json-schema / codex --output-schema instead of a text marker — the pass/changes decision is a typed enum, not a substring grep (text markers remain the fallback). With "record_lessons": true (or --record-lessons) each run appends its reviewers' blocking issues to <project>/LESSONS.md, which — together with an optional global "lessons_file" — is injected into future agent prompts as reference: a self-improving loop. When an agent hits a transient failure (e.g. a dropped API connection), MMA first retries by resuming the same session with a short continue-nudge — the context is already loaded there, so it costs a fraction of a fresh session — and only falls back to a fresh session if that fails too. When retries are exhausted (or on a timeout), MMA stops at the current stage and (in interactive runs) lets you fix things and type retry, continue, or exit.
During a run, status is written to log/<run-id>/status.json. Render it from another shell:
mma status --log-dir /path/to/log/<run-id>Unit tests:
python3 -m pytestOffline fake workflow smoke test:
tmpdir=$(mktemp -d)
printf '# Requirement\n\nCreate a fake plan.\n' > "$tmpdir/req.md"
mma run --requirement "$tmpdir/req.md" --config fake --yesSee docs/BENCHMARKS.md for controlled single-agent vs MMA A/B tests. Headline: on precise tasks a strong model already scores 100% (MMA = verification), but with a weak executor or an ambiguous/hard spec the reviewer panel lifts the score (e.g. haiku 85→91 on terse semver ranges, 72→74 on the spreadsheet task), and a cross-vendor panel catches different bugs.
docs/references/ contains navigation maps of the two upstream projects used to design this tool:
claude-code-best.md— Claude Code's single-process TUI, session storage, and token accounting.openai-codex.md— Codex'sexec --jsonevent protocol, session rollouts/resume, and token usage.
See ROADMAP.md for planned work (live interruption, reviewer parallelism, configurable workflow graph).