照搬现有 L1「上次失败原因」链路,新增平行的「人工驳回意见」注入链路: - store.lastRejectReasonOf(taskId, gate?):从 approvals 取最近一次 reject reason, 支持按闸过滤(exec/spec/plan),无 reject 返回 null - orchestrator.claimOne:按返工角色对应闸装配 task.lastRejectReason 下发 job (executor→exec / planner-spec→spec / planner-decompose→plan) - runner.lastRejectLines + buildPrompt/buildPlannerPrompt 拼接驳回段 (紧接失败段之后,2000 字截断);buildConflictPrompt 不注入 - types.Task 新增 lastRejectReason 临时装配字段(仅注入 prompt,不持久化) - test/memory-inject.test.ts:覆盖 store gate 过滤、三处注入、截断、边界、顺序 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
maestro
多项目 TODO 管理与 Agent 执行系统(本地优先 daemon · Node/TypeScript)。
按任务复杂度(Hard / Medium / Easy)分级驱动审批闸,用后台 agent(Claude Code)在隔离 git worktree 自动执行可执行任务,执行后自动跑 code review + 安全审计,accept 时 --no-ff 自动合并,改动不自动 push。
执行是多进程解耦的:daemon 是唯一 DB 写者兼监工,每个执行任务 spawn 一个独立 worker 进程(哑执行器,完全不碰 DB),二者只经文件 + 进程信号通讯(job.json / outbox.ndjson / heartbeat / SIGTERM),daemon 重启可按心跳判活 re-adopt 仍在跑的 worker。详见 DESIGN.md §7。
- 完整设计见 DESIGN.md。
- 当前状态:Phase 1 + Phase 2 + Phase 3 核心均已落地(编排器 + score 调度 + 双复审 + 自动合并 + 多进程执行解耦 + macOS 通知 + 额度透传 + 归档)。
结构
src/
model/ 复杂度、状态机、实体类型、score 调度算法
store/ SQLite schema + 访问层(状态机守卫 + 审批闸)
api/ REST + WebSocket API + logo 解析
mcp/ MCP server(给 Claude Code)
executor/ Agent SDK 封装(cc.ts)、worktree 管理、verify、runner、reviewer(双复审)、merge、模型分级
protocol.ts(daemon↔worker 文件协议:job/outbox/heartbeat/判活)
worker.ts(独立 worker 进程入口)、pipeline.ts(纯执行管线,不碰 DB)
daemon/ maestrod 入口、编排器(监工:spawn/ingest/回收)、ingest(outbox→DB)、
macOS 通知、额度透传、配置
sync/ 旧 todo.json → maestro 单向同步引擎
web/ 看板(多项目·实时 WS·内联审批·归档详情)
scripts/ poc-exec.mjs(执行闭环验证脚本)
开发
npm install
npm run typecheck # tsc --noEmit
npm run build # → dist/
npm run dev # 启动 daemon(默认 http://127.0.0.1:4517)
npm test # 运行全量测试(node --test)
接入 Claude Code(MCP)
先 npm run build,并保证 maestrod 在跑(npm run dev,默认 http://127.0.0.1:4517)。两种接法:
# 方式一:claude mcp add(在要接入的项目里执行)
claude mcp add maestro -e MAESTRO_URL=http://127.0.0.1:4517 -- node /绝对路径/maestro/dist/mcp/index.js
// 方式二:项目根放 .mcp.json(模板见本仓库 .mcp.json.example)
{ "mcpServers": { "maestro": {
"command": "node",
"args": ["/绝对路径/maestro/dist/mcp/index.js"],
"env": { "MAESTRO_URL": "http://127.0.0.1:4517" } } } }
MCP 工具:list_projects / create_project / list_tasks / get_task / create_task / decompose_task / write_plan / write_spec / write_operations / update_status / get_next_executable / get_pending_approvals(accept/reject 在 Web 看板操作)。
核心功能速览
多进程执行(监工 + worker)
- daemon = 唯一 DB 写者 + 监工:领任务 → 写
job.json→ spawn worker → ingest 其 outbox → 回收死 worker。每轮 tick 顺序ingest → reap → claim。 - worker = 哑执行器:
node dist/executor/worker.js <runId>,每任务一个独立进程,跑 worktree→执行→verify→双复审→产出,完全不碰 DB,进度/结果只追加进outbox.ndjson。 - 通讯:
runs/<runId>/job.json(daemon→worker 唯一输入)/outbox.ndjson(worker→daemon,带 seq,daemon 按last_seq幂等 ingest)/heartbeat(worker 每 10s 刷 mtime,daemon 判活)/SIGTERM(取消/超时)。 - 重启 re-adopt:daemon 启动
reconcileInterrupted()按worker_pid + 心跳判活——活则 re-adopt 续 ingest(不打断 agent),死则回收重试(持久化退避next_eligible_at = min(30s·2^(n-1), 10min),超maxRetries→needs_attention)。
Score 调度
编排器按 score 降序领任务(不抢占,领取时现算):
score = P(自身优先级) // P0=3 / P1=2 / P2=1
+ Σ P(已完成依赖) // 链条惯性:前置投入越重越优先出活
+ Σ P(等我解锁的 blocked 任务) // 解锁效应:解锁面越广越优先
双复审 PR 流程
执行成功后顺序起两个只读 headless Claude Code:
- Code Review(kind=reviewer):正确性/可维护性逐条问题 →
VERDICT: approve|reject - 安全审计(kind=security):凭证泄露/注入/权限/UI红线词/部署风险 →
VERDICT: approve|reject
任一复审失败不挡任务进 exec_review;看板展示两份 markdown 报告 + verdict 徽章。
模型分级
| 角色 | Easy | Medium | Hard |
|---|---|---|---|
| executor | claude-sonnet-4-6 | claude-opus-4-8 | claude-fable-5 |
| reviewer | claude-sonnet-4-6 | claude-opus-4-8 | claude-opus-4-8 |
project.model(看板设置)最高优先;环境变量次之(见下);内置默认最低。模型不可用时自动回退 opus-4-8 → sonnet-4-6。
归档与运行历史
GET /api/tasks/:id/events— 状态流转时间线GET /api/tasks/:id/runs— executor / reviewer / security run 历史(含转录引用)
macOS 通知
进入审核闸(plan/spec/exec_review)、执行失败、复审建议拒绝、合并完成时推送原生通知。MAESTRO_NOTIFY=0 关闭。
额度透传
GET /api/agents 附带 Claude 订阅额度(5h 窗口 + 7天窗口),读取 ~/.claude/.credentials.json 或 macOS keychain,60s 缓存。
自动合并
exec_review accept 时自动 --no-ff 合并:若 defaultBranch 在主检出且工作区干净 → 直接合并;否则临时 worktree 合并后销毁。冲突时 abort 返回冲突文件列表,任务保留在 exec_review。merge:false 逃生口可仅通过不合并。
环境变量
| 变量 | 说明 | 默认 |
|---|---|---|
MAESTRO_URL |
daemon 地址(MCP 客户端侧) | http://127.0.0.1:4517 |
MAESTRO_PORT |
daemon 监听端口 | 4517 |
MAESTRO_DATA_DIR |
SQLite + worktree 数据目录 | ~/.maestro |
MAESTRO_ORCH_INTERVAL |
编排器轮询间隔(秒),0=关闭 |
15 |
MAESTRO_WORKER_CMD |
覆盖 worker 进程启动命令(空格分隔),供 tsx 跑 .ts 入口/测试用 | node dist/executor/worker.js |
MAESTRO_NOTIFY |
0 关闭 macOS 通知 |
开启 |
MAESTRO_SDK_RESUME |
0 关闭 SDK 抖动时的 session resume 续跑(worker 内部健壮性优化) |
开启 |
MAESTRO_MODEL_EASY |
executor Easy 模型覆盖 | claude-sonnet-4-6 |
MAESTRO_MODEL_MEDIUM |
executor Medium 模型覆盖 | claude-opus-4-8 |
MAESTRO_MODEL_HARD |
executor Hard 模型覆盖 | claude-fable-5 |
MAESTRO_MODEL_REVIEW_EASY |
reviewer Easy 模型覆盖 | claude-sonnet-4-6 |
MAESTRO_MODEL_REVIEW_MEDIUM |
reviewer Medium 模型覆盖 | claude-opus-4-8 |
MAESTRO_MODEL_REVIEW_HARD |
reviewer Hard 模型覆盖 | claude-opus-4-8 |
MAESTRO_SANDBOX |
worker OS 级沙箱总开关(on/off) |
关闭 |
MAESTRO_SANDBOX_PROFILE |
是否套 sandbox-exec(仅 macOS;off 则仅留 ulimit) |
开启 |
MAESTRO_SANDBOX_NET |
是否放行网络(SDK 需连 Anthropic;off 隔离) |
放行 |
MAESTRO_SANDBOX_CPU |
CPU 时间上限(秒),0=不限 |
0 |
MAESTRO_SANDBOX_MEM |
虚拟内存上限(MB),0=不限(macOS 对 -v 支持不稳定) |
0 |
MAESTRO_SANDBOX_NOFILE |
打开文件描述符上限 | 4096 |
MAESTRO_SANDBOX_FSIZE |
单文件大小上限(MB),0=不限 |
0 |
MAESTRO_SANDBOX_WRITE_EXTRA |
额外放行写的绝对路径(冒号分隔) | 空 |
worker 沙箱(OS 级硬隔离,可选)
进程隔离(独立 worker 进程 + env 收敛,密钥/DB/API 与 daemon 隔开)之上,再加一层 OS 级硬隔离,默认关闭,开启后对每个 worker 生效:
- 文件写围栏(macOS
sandbox-exec):基线allow default(放行读 / exec / 网络 / mach——keychain 鉴权所需),仅对写收口,逐条放行 worker 必需的写目标:任务 worktree、主仓.git/node_modules、runs/<runId>、transcripts、$TMPDIR与常用工具缓存(~/.npm、~/Library/Caches、GOCACHE等)。越界写(改系统、污染其它仓库、动 maestro 自身 DB)被内核拒绝。为什么不deny default全围栏:合法的 git/npm/go/node 需读海量系统/缓存路径,全围栏极易误伤——真正的破坏向量是「写」。 - 资源上限(
ulimit):CPU 时间 / 虚拟内存 / 打开文件数 / 单文件大小,防失控 agent 拖垮机器;超限由内核杀进程,daemon 的 reaper 据此回收并重试/转needs_attention。 - 生效的策略会在 daemon 启动日志打一行;每个 run 的实际 profile 落在
runs/<runId>/sandbox.sb(清晰记录)。
先在一个项目灰度:开 MAESTRO_SANDBOX=on 后跑一轮含 npm/go/git 的真实任务,确认能跑通、越界写被拒、超限被杀且日志清晰,再逐步铺开。个别项目若有特殊写路径,用 MAESTRO_SANDBOX_WRITE_EXTRA 放行。