CLI-Anything 的 `<APP>.md` 里到底写什么:一层没有强模板的项目分析与 SOP
翻 CLI-Anything 这个仓库,最先撞上的往往不是代码,而是每个软件目录下 agent-harness/ 里那份全大写命名的 markdown:BLENDER.md、AUDACITY.md、ADGUARDHOME.md。方法论文档 cli-anything-plugin/HARNESS.md 的 ## Directory Structure 一节(第 672 行起)把它排在目录树第一位,括号里的定位是「项目分析与 SOP」。
一个很自然的预期是:既然是被规范点名的一层,它应该有个固定模板,每份填同样的小节。我们按 2026-08-10 采集的仓库快照 39634a6 实读之后,结论恰好相反——这一层是整套 harness 结构里模板约束最弱的一层,而它真正稳定下来的东西不在小节名里。
先看统计:没有任何一个小节出现在半数以上的文件里
这一层可以用一条命令把所有候选文件筛出来再汇总二级标题:
ls */agent-harness/*.md \
| grep -viE 'README|CHANGELOG|FIX_NOTES|DEMO|ATTRIBUTION|THIRD_PARTY|/TEST' \
| while read f; do grep -oE '^## .*' "$f"; done \
| sort | uniq -c | sort -rn
我们采集时筛出来是 67 份文件(注意这是文件数不是目录数,有 5 个 harness 同时存在 <APP>.md 和 HARNESS.md 两份)。频次前列如下:
| 小节 | 出现份数 |
|---|---|
## Architecture | 22 |
## Overview | 19 |
## Command Groups | 13 |
## Architecture Summary | 13 |
## Testing Strategy | 9 |
## Testing | 8 |
## Backend | 8 |
## Software Overview | 7 |
## Purpose | 7 |
## State Model | 6 |
最高的 ## Architecture 是 22 份,占比三分之一。而且 Architecture 与 Architecture Summary、Testing 与 Testing Strategy、Overview 与 Software Overview 明显是同一件事的两种叫法,即便合并同类项也没有哪一项能过半。连标题行都不统一:# Blender: Project-Specific Analysis & SOP(blender/agent-harness/BLENDER.md:1)、# Audacity: Project-Specific Analysis & SOP(audacity/agent-harness/AUDACITY.md:1)是一套写法,# AdGuardHome - CLI Harness SOP(adguardhome/agent-harness/ADGUARDHOME.md:1)是另一套,# Browser Harness: DOMShell MCP Integration(browser/agent-harness/HARNESS.md:1)又是第三套。
长度差得也很远:BLENDER.md 92 行、ADGUARDHOME.md 66 行、AUDACITY.md 167 行、browser/agent-harness/HARNESS.md 344 行(wc -l)。
对比一下同一套体系里的另外两层就更明显了:给 agent 读的 SKILL.md 有 cli-anything-plugin/templates/SKILL.md.template 这份 123 行的模板兜底,TEST.md 在 HARNESS.md 里被拆成 Phase 4 和 Phase 6 两段规定了要写哪几块——这两层我们另有专门的篇目在讲。唯独 <APP>.md 这一层,HARNESS.md 只在目录树里给了个名字和一句定位,正文没有给小节清单。
那它固定下来的是什么:三个必答问题
把三份样本的小节序列并排看,模板不一样,但要回答的问题是同一批。
BLENDER.md 的序列是:Architecture Summary(含一张 ASCII 架构图)→ CLI Strategy: JSON Scene + bpy Script Generation → Core Domains(域—模块—操作三列表)→ Modifier Registry → Render Presets → Rendering Gap: Low Risk → Export: bpy Script Generation(对应行号 3、30、39、52、64、75、81)。
AUDACITY.md 的序列是:Architecture Summary → CLI Strategy → Why Not .aup3 Directly? → The Project Format (.audacity-cli.json) → Command Map: GUI Action -> CLI Command → Effect Registry → Export Formats → Rendering Pipeline → Rendering Gap Assessment: Medium → Test Coverage(行号 3、30、39、50、68、95、116、129、140、148)。
ADGUARDHOME.md 短得多,但开头两行把最关键的信息顶到最前面:
Real software: The running AdGuardHome HTTP API (not a binary to invoke directly). CLI role: Generate structured commands - call the real API - verify responses.
(adguardhome/agent-harness/ADGUARDHOME.md:8-9)
抽出来其实是三问:
- 宿主是什么形态。 是一个可以
shutil.which找到再起子进程的二进制,还是一个跑着的 HTTP 服务,还是要经过别的中间层。AdGuardHome 那两行直接把答案写死了,Blender 和 Audacity 则藏在 Architecture Summary 里。 - CLI 拿什么当中间表示。 Blender 是「JSON 场景描述 + 生成 bpy 脚本」,Audacity 专门用一节
Why Not .aup3 Directly?解释为什么不碰原生工程格式,改用.audacity-cli.json。这一节决定了后面core/怎么切模块。 - 渲染差距有多大。 这是这一层唯一称得上「固定自评项」的东西:Blender 写的是
Rendering Gap: Low Risk(BLENDER.md:75),Audacity 写的是Rendering Gap Assessment: Medium(AUDACITY.md:140)。名字都不统一,但两份都自己给了一个等级。
对读者来说,实际用得上的读法是:拿到一个陌生 harness,先在这份文档里 grep Rendering Gap 和 Real software,再看有没有一节在解释「为什么不用原生格式」。这三处能回答的,比通读一遍 ## Architecture 更多。
它和同层那份 README 不是一回事
同一个 harness 里其实有两份说明性文档,位置和用途都不同,混着读很容易搞乱。
一份就是本文说的 <软件目录>/agent-harness/<APP>.md,规范给它的定位是「项目分析与 SOP」。另一份在包里:cli_anything/<app>/README.md,HARNESS.md 的目录树在它后面直接标注了 HOW TO RUN — required。这份 README 的模板约束反而比 <APP>.md 强得多——我们采集时数了这一层的 64 份文件,## Installation 出现 35 次、## Quick Start 27 次、## Prerequisites 26 次。
更实际的差别是分发路径:包内那份 README 会被 setup.py 读成 PyPI 长描述(blender 的写法是 README = ROOT / "cli_anything/blender/README.md",随后 long_description=README.read_text(...),见 blender/agent-harness/setup.py:17-20,26),而 <APP>.md 没有这条路径,它只留在仓库里。所以这两份文档的读者本来就不同:一份是拿到包要跑起来的人,一份是要接着改这个 harness 的人。HARNESS.md 自己也在第 709 行写了「个别软件目录只引用本文件,不要复制」,方法论不往下抄,<APP>.md 里剩下的就该是这个软件独有的判断。
最长的那份文档,恰好是最不按规范来的那个
browser 这个 harness 的同层文档叫 HARNESS.md,344 行,是我们读到的样本里最长的一份。它承担的内容也确实超出常规:文档自述这是「the first CLI-Anything harness to use an MCP server as a backend」(browser/agent-harness/HARNESS.md:110),状态模型那节明写是 Page state (not project state),字段为 current_url / working_dir / history / forward_stack,并标注 No persistence: State is in-memory only(第 131-139 行)。
需要照实标出来的是,这份文档还有一节 ## Future Enhancements(第 312 行),内容属于尚未实现的规划项。同样要照实说的是,browser 虽然在规范里被当作 MCP 范例反复引用,但它是我们采集时统计到的 12 个缺 TEST.md 的 harness 之一。文档篇幅和结构齐整度不是一回事。
命名和位置是双轨的
规范在 HARNESS.md:677 只写了 <SOFTWARE>.md 这一种命名。实读到的情况是:我们采集时另有 6 份同层文档叫 HARNESS.md——browser、lldb、nsight-graphics、renderdoc、safari、shotcut 六个 harness 各一份,其中除 browser 之外的 5 个(lldb、nsight-graphics、renderdoc、safari、shotcut)同时还有 <APP>.md,也就是同一层摆了两份说明。这 5 份重复正好解释了前面那个数:全仓 69 个 agent-harness 目录,减去 7 个没有这一层文档的,是 62 个目录,再加上这 5 份多出来的 HARNESS.md,才凑成 67 份文件。两处命名不一致,以我们实读的仓库状态为准。
另一件要照实说的是:这份文档不是每个 harness 都有。按「排除 README/CHANGELOG/FIX_NOTES/DEMO/ATTRIBUTION/THIRD_PARTY/TEST.md 之后顶层还剩几个 md」来数,comfyui、firefly-iii、minimax、n8n、novita、sketch、zoom 这 7 个是 0 个,也就是没有这一层分析文档。我们采集时全仓 agent-harness 目录共 69 个(find . -type d -name agent-harness | wc -l),而 registry.json 的 clis 数组是 79 条——注册表里有条目,不等于对应的 harness 在本仓齐件,更不等于装上就能驱动宿主软件。整套 harness 的缺件清单我们另有一篇专门在统计,这里只提与本层直接相关的这 7 个。
这一层写的数字,要当「分析口径」读
<APP>.md 里会顺手写一些统计数字,读的时候得知道它属于哪一层口径。
以 Audacity 为例。AUDACITY.md 自述 Unit tests (test_core.py): 60+ tests 与 E2E tests (test_full_e2e.py): 40+ tests(第 150、159 行);同一个 harness 的 tests/TEST.md 表格写的是 test_core.py 10 类 109 个、test_full_e2e.py 7 类 45 个、Total 154(TEST.md:7-9);而我们用 grep -cE '^\s+def test_' 数出来的是 test_core.py 107 个、test_full_e2e.py 56 个,另有这两份文档都没登记的 test_eval.py 3 个。三处各不相同。要补充的是,我们数的是 def test_ 函数定义数,参数化用例展开后的实际数目会更多,所以这里的准确表述是「声明数与函数定义数不符」,不是「实际通过数不符」。说到这里为止,我们不推断原因。
还有一处是规范与这层自述的差异。HARNESS.md 第 486-488 行写着 Do NOT reimplement rendering in Python. Do NOT gracefully degrade to a fallback library.;而 AUDACITY.md 自述 **Python stdlib** (wave, struct, math) handles WAV I/O and audio processing(第 36 行)、Basic effects (gain, fade, reverse, echo, filters) implemented in pure Python(第 143 行)。代码侧可以自己核:audacity/agent-harness/cli_anything/audacity/core/export.py 第 10-16 行只 import os/wave/math/struct 与本地 audio_utils;而 utils/sox_backend.py 在生产代码路径里零引用,只有 tests/test_full_e2e.py import 它(grep -rn 'sox_backend' audacity/agent-harness --include='*.py')。两处不一致,同样只陈述到这里。
可复现的核查动作
想自己验一遍这一层,按顺序做这几步就够了:
- 跑上面那条 H2 汇总命令,看频次分布是不是仍然没有过半项。
- 对任意一个感兴趣的 harness,
ls <软件目录>/agent-harness/*.md看这一层有几份、叫什么名。 - 在这份文档里 grep
Rendering Gap、Real software、CLI Strategy三个串,快速定位那三个必答问题的答案。 - 把文档里自报的测试数与
grep -cE '^\s+def test_' <harness>/cli_anything/<app>/tests/test_*.py的结果对一遍,确认你读到的是计划口径还是代码现状。 - 看
<软件目录>/agent-harness/下打包文件里的Development Status分类器——注意要把setup.py和pyproject.toml两类都数进去(grep -rh 'Development Status' */agent-harness/setup.py */agent-harness/pyproject.toml | sort | uniq -c)。我们采集时这两类打包文件合计 45 处标4 - Beta、2 处标3 - Alpha,没有任何一个标 Production/Stable。只数setup.py一种会少几处,对不上这个总数。
以上命令按仓库中的文件布局组合,未经实测,以你本地 clone 的实际内容为准。
最后一句必要的提醒:这类 harness 的工作方式就是在你本机启动外部程序、发 HTTP 请求或执行生成的脚本——Blender 那条路径是把生成的 bpy 脚本写进临时文件再 subprocess.run 拉起 blender --background --python。宿主软件要你自己装,脚本要在你本机跑。<APP>.md 这一层文档能帮你在动手之前看清「它打算怎么碰你的机器」,但看清不等于安全,是否使用请结合自身环境评估。
本文依据 CLI-Anything 官方仓库(github.com/HKUDS/CLI-Anything)的 README、registry.json、
docs/ 与各 agent-harness/ 下的源码整理,核对日 2026-08-10,对应仓库快照 39634a6。
本文内容为仓库源码与文档口径,我们没有安装或运行过其中任何一个 harness,
也没有在本机驱动过任何一款宿主软件,因此不涉及实际操控效果的任何描述。
这类 harness 会在本机执行外部程序与脚本,是否使用请结合自身环境评估。
注册表与 harness 内容随上游更新而变动,请以仓库最新内容为准。
安全相关做法请结合自身环境评估,本文不构成安全方案建议。