
这是什么
一个以命令行工具形态发布的 agent 运行时。你部署一个团队——一个 CTO、若干后端 agent、一个 QA、一个复核者,各自绑定到你指定的那个 CLI——给团队一个目标,然后一个编排循环按 tick 做三件事:核对进程、把空闲 agent 派出去、收回落地的运行结果。它通过同一套适配器接口驱动 Claude、Codex、Cursor、Grok、Antigravity、Pi、OpenCode 或任意 shell 命令,既提供全屏终端面板,也提供给 CI 用的无头模式,并把每个任务、agent、运行与消息都存成一个隐藏目录下的文件。引擎本身也作为库发布,所以同一套运行时可以被另一个 Node 应用嵌进去用。
谁做的一个 2024 年 9 月注册的账号,50 个公开仓库、11 个关注者,简介写着「AI agents creator」。585 次提交里 581 次是他写的,用的是一个 iCloud 邮箱;其余来自另外两位贡献者,另有一次提交记在 Claude 名下。
它是怎么搭起来的
组成 · 6一台手接线的引擎,外面套一层薄壳。核心是一个小的分层应用——领域模型与状态机、应用服务与编排器、以及负责存储、进程、模板与适配器的基础设施——而命令行与终端面板是它的客户端,而不是它本身;这个包把引擎重新导出,所以另一个程序可以不经终端直接使用同一套运行时。编排器是一个三阶段的 tick 循环:核对、派发、收集。这正是让这个工具能活下来的地方——死掉的进程会被发现,闲着的 agent 会被派活,跑完的运行会被读回来并折算成一次任务状态迁移。一个 agent CLI 能是什么样,被压缩成一种适配器形状,于是新增一个适配器是一个文件,而不是循环里的一个分支。状态选文件,是为了让人能用文本编辑器读它、修它,写进去走原子写、读出来只读尾部。而那些会无限增长的东西——事件流、面板、日志——都有明确的上限。
- src/ 与它的四层
domain/放模型、迁移表与错误类型;application/放编排器、它的 tick 循环与事件总线;infrastructure/放适配器、文件存储、进程管理器、模板引擎、工作区与技能加载器;cli/放命令与无头 serve 模式,tui/是一个 26 文件的 Ink 面板。container.ts是那份两档的依赖图,bin/cli.ts决定一个子命令需要哪一档。- .orchestry/
- 全部运行时状态,就放在用户项目旁边:任务、agent、目标、团队各是 YAML 文件,每次运行一条 JSON 记录加一条 JSONL 事件流,每个任务一个工作区,再加一个全局状态文件。它就是整个数据库,而它是纯文本。
- skills/library/
- 二十六个 Markdown 技能,从一份 32 KB 的设计复核流程和一份 24 KB 的上线与部署手册,到只有三行的护栏,其中还包括一个教 agent 怎么驱动 Codex 的技能。它们是派活时按 agent 加载的提示词材料,而不是编排器里的代码路径。
- docs/
- 四份规格,每份开头都有一行状态说明它是什么:终端界面设计、一份 28 KB 的 MCP 规格、一份 19 KB 的无头 serve 模式规格,以及一份 27 KB、明确标为研究而非路线图的记忆规格。
CLAUDE.md压在它们之上,是这个代码库的工作指南。 - landing/ 与 assets/
- 产品站点,手写并提交进仓库:106 KB 的首页、38 KB 的对比页、21 KB 的理由页、演示页、一段 624 KB 的视频、一张 Open Graph 图,以及 robots、sitemap 与一份 Vercel 配置。发布脚本会改这里显示的那个版本号,这也是它留在仓库里的原因。
- test/ 与 .github/workflows/
- 125 个单元测试文件与
src/镜像对应,另有给编排器测试用的 mock 工厂与依赖构造器,以及集成测试、端到端测试和一个单独的「battle」测试。三个工作流:一个 338 字节的持续集成文件(检出、安装、类型检查、测试)、一个在打标签时重跑检查、跳过已发布版本并从 CHANGELOG 抽正文的发布工作流,以及一个欢迎新贡献者的工作流。
取舍,以及它替代了什么
状态全放文件,而不是数据库 替代 嵌入式数据库或一个服务端
这一点被写成了一条设计原则——一切在
.orchestry/下、没有数据库——并由两个工具函数兜住:写入走临时文件加改名,所以崩溃不会毁掉一条记录;事件流从尾部读,所以要看一眼不必先把一次长运行整个加载进来。给只读命令用一个轻量依赖容器 替代 所有命令共用一个容器
两档的内容是写明了的:只读命令只要 store 与服务,而真正干活的三个命令才加上编排器、进程管理器、适配器与模板引擎。入口按子命令决定建哪一档并惰性 import 命令模块,于是「列个任务」不必为启动进程付钱。
技能是注入提示词的 Markdown,而不是编排器里的代码 替代 把每种能力硬编码成一个分支
加载器读普通文件、在派活时注入内容、把带冒号命名空间的技能原样交给外部 CLI 自己的系统,并对每个名字做模式校验,好让构造出来的文件名爬不出技能目录。一个 agent 会什么,写在它自己的定义里。
所有 agent CLI 共用一套适配器接口 替代 在循环里给每家单独开分支
一个适配器负责 spawn 进程并返回进程号加一条异步事件流,这是编排器唯一认识的形状;注册了八个适配器,按 agent 自己的定义解析,其中包含一个普通 shell,于是任何能从命令行跑起来的东西都可以是一个 agent。
发布脚本掌管三处版本号 替代 只升包清单、其余留给以后
脚本同时更新包清单、命令行打印的版本字符串,以及手写落地页上的那个数字,然后打标签。文档另外列出了它刻意不覆盖的部分,于是那些手工步骤是被点名的,而不是事后才发现的。
规格文档都带一行状态 替代 让文档读起来像已经建成了
docs/下每份文档都开宗明义地说自己是什么:设计参考、规格,或者「探索性、未列入路线图」的研究。那份记忆规格尤其如此——它在提出任何方案之前,先把问题和当前限制讲清楚。
依据CLAUDE.md(6,937 字符)、docs/CLI_UI_DESIGN.md、docs/AGENT_MEMORY_SPEC.md、README.md(25,731)、两个工作流,以及完整的 346 个文件树及其体积。
制作过程
6 个阶段- 01
一个目标、五个 agent,以及 85% 讲明作者是谁的提交
仓库建于 2026-03-10,到 2026-08-01 有 585 次提交,其中三月一个月就落地 497 次,之后几个月的节奏掉到 32、20、4、27、5。除四次以外全部出自同一个作者。构建签名异常干净:498 次提交带共同作者尾注,占 85%,而且几乎只写 Claude 模型——Opus 4.6 二百八十三次、百万上下文的 Opus 4.6 一百五十五次、Sonnet 4.6 四十五次、Opus 4.7 十四次。五个月里它发了 34 个 release、45 个 tag,从 2026-03-12 的
v0.1.0到 2026-08-01 的v1.0.34。仓库有 167 个星、17 个 fork、7 个未关闭 issue,README 的徽章上写着 1,954 个通过的测试——这是作者自己的数字,也是那种仓库可以选择保持诚实、也可以不保持的数字。2026-08-01 之后没有新的推送,另有五个 pull request 还开着。 - 02
一个终端界面,配一份用俄语写的设计规格
docs/CLI_UI_DESIGN.md是 35 KB 的命令行界面设计,而且是用俄语写的。它开篇定下基调——仪表级终端:每一个字符都要算数,不做任何为了好看的装饰,因为信息应当像飞行员的面板一样被读取:看一眼、明白了、动手。接着它把三种运行模式当作三种沉浸深度来分别设计——打印完就退出的一次性命令、全屏面板、以及带实时日志和紧凑状态行的 watch 守护模式。真正少见的是后面那部分:状态区块被规定为不超过 80 列(于是在任何终端里都成立)、颜色用 ANSI 256 并回退到 16 色、任务行有一套固定的字形词汇(●运行中、○等待、◈待复核、✓完成、✕失败、↻重试中)、优先级有固定配色(P1 红到 P4 灰)、行的排序有唯一确定的顺序、页脚永远只有一行汇总,而每条命令都带--json,好让脚本不必去解析那张图。 - 03
没有数据库:状态全是文件,而每一条会增长的路径都有上限
存储这个决定只用一句话讲完——一切都放在
.orchestry/下,没有数据库——而它的后果被逐条写明。任务、agent、目标与团队各自是一个 YAML 文件;一次运行是一条 JSON 记录加一条只追加的 JSONL 事件流;全局状态是一个 JSON 文件。扛住这件事的是两个工具函数:atomicWrite()先写临时文件再改名,所以写到一半崩掉也不会留下半条记录;readJsonlTail()只读事件流的最后 N 行而不加载整个文件,这就是一块面板与一次长时间运行之后被内核杀掉之间的差别。同一种反射也出现在界面与守护进程里:终端面板把进来的消息按 80 毫秒批量刷新,任何单条详情字符串截到 2 KB,运行标识的映射表有 500 条的 LRU 上限;无头模式每第六个空闲 tick 才记一次日志,免得一个安静的夜晚把磁盘写满。这是一个作者预期自己的程序要在无人看管下、贴着若干高频吐事件的进程跑上几个小时时,会写出来的东西。 - 04
只加载这条命令需要的那几层
架构分成领域、应用、基础设施与界面四层,依赖注入是手写的——不用框架,也不用装饰器。有意思的是那个容器分成两档:轻档只装 store 与服务,给每一条只读命令用,比如列任务、看日志、看状态;完整档在此之上加编排器、进程管理器、模板引擎与适配器,给真正会干活的三个命令用。命令行入口先判断该建哪一档,然后只惰性 import 这个子命令对应的模块。对一个职责就是去启动别的进程的工具来说,启动延迟是用户最先感受到的东西——这个设计把它当成特性而不是副作用。再往下,一套适配器接口把每个外部 agent CLI 压成同一种形状:一个被 spawn 的进程、一个进程号、一条异步事件流;注册进来的有 Claude、Codex、Cursor、Grok、Antigravity、Pi、OpenCode,以及一个普通 shell。任务之上是一台小状态机,每一次迁移都由同一个函数校验,状态修改用一条 promise 链互斥量串行化,而 tick 循环负责核对进程存活、向空闲 agent 派活、收结果。
- 05
技能就是 Markdown,在派活时注入
仓库里带二十六个技能,就是普通的 Markdown 文件,而加载器把它们当成两个物种。名字由字母和连字符构成的那些会在派活时被读出来、内容注入 agent 的提示词;名字里带冒号的那些原样放行,因为它们属于外部 CLI 自己的扩展系统。加载器在进程生命周期内缓存它们、并行读取,并对每个名字做模式校验——一个可能爬出技能目录的文件名会在被打开之前就被拒掉。每个 agent 在自己定义里写明它要哪些技能,于是「这个 agent 会做什么」是文件里的数据,而不是编排器里的行为。同一种直觉还产出了那份记忆规格:一份 22 KB 的俄语技术文档,标注为「研究——探索性设计,未列入路线图」,它在提出任何方案之前先把问题说清楚——底层 CLI 的每一次调用都是一次全新的子进程、没有任何历史,而一个 agent 今天拥有的全部记忆,只是编排器手工拼进提示词的那点东西:一个没有结构也没有检索的键值存储、一次失败重试的上下文,以及一份被截到 2 KB 的最终总结。
- 06
一个把三处版本号同步好的发布脚本,和一份写明它管不到哪里的清单
版本号住在三个地方,而发布脚本把三处都管住:它升
package.json、改写命令行入口里打印的版本字符串,还更新落地页上的那一处——那个落地页是手写的静态站点,就放在仓库里,而不是生成的。发布工作流在打标签时重跑类型检查与测试,先查这个版本是否已经发上 npm、是就跳过发布,并从 CHANGELOG 里抽出对应段落当作 release 正文。文档对缺口也很坦白:一节标题就叫「发布脚本不覆盖的手工更新」,列着 CHANGELOG、readme 里的测试数徽章,以及落地页上的统计与功能文案。对一个已经发过三十四个版本的仓库来说,那张清单就是「一套发布流程」与「一种指望」之间的差别。pull request 的历史是同一件事更安静的那一半:一共十一个,合并了五个——包括一位贡献者送来的 Pi RPC 适配器和另一位送来的高对比度浅色主题——五个还开着,而其中一位贡献者的三个从四月一直放到现在。
相关档案
全部档案 →第 039 号
Vibe Projects
一个人的 monorepo:六个由 AI 生成的项目——一个 Android agent 运行框架、一个按 hunk 审阅改动的 VS Code 扩展,以及四个小型 Android 应用——被刻意当作「模型现在能造出什么」的持续标尺。
第 027 号
CCManager
一个终端菜单,同时看着若干个编码 agent——每个占一个 git worktree:它显示谁在忙、谁在等你、谁空着,负责创建与合并这些 worktree,并且能在崩溃之后把整批会话重新拉起来。
第 061 号
DeepSeek Harness
DeepSeek 的 agent 运行框架:模型适配器、工具注册表、会话日志、乃至 agent loop 本身,全都是插件——换掉它们靠的是改配置文件,而不是分叉源码。