caveman-review:caveman 项目中"一行一发现"的压缩式代码评审技能
在 AI 编码代理(如 Claude Code、Codex、Gemini CLI)参与代码评审的场景中,评审意见往往冗长、充满寒暄与模糊措辞,既浪费 token 又降低信息密度。caveman 项目的 caveman-review 技能正是为这一问题而生:它通过一份面向 LLM 的行为指令文件(skills/caveman-review/SKILL.md),把代码评审输出约束为严格的"一行一发现"格式——位置、问题、修复,三要素齐备,无任何铺垫性废话。读完本文,你将完整掌握该技能的输出格式规范、严重度分级体系、Auto-Clarity 降级机制与行为边界,并能从仓库源码层面理解 /caveman-review 命令从解析、状态跟踪到多平台分发的完整实现链路。
技能定位:独立加载、零副作用的评审输出契约
caveman-review 是 caveman 仓库中一个完全独立的技能(fully independent skill)。它的 SKILL.md 带有独立的 name 与 description frontmatter,因此可以脱离主 caveman 输出风格单独加载,而不需要激活整个"穴居人模式":
---
name: caveman-review
description: >
Compressed code review - one line per finding with location, problem and fix.
Use for /caveman-review, "review this PR", or "review the diff".
---
frontmatter 中的 description 同时承担了自然语言触发的职责——当用户输入 "review this PR" 或 "review the diff" 这类表述时,代理的技能匹配机制会据此加载该指令。
该技能在仓库中的输出契约(output contract)有明确定义。从 docs/technical/skills-hooks-and-plugins.md 的 focused skills 对照表可以看到,caveman-review 的契约是 "Line-scoped review findings",副作用一栏明确写着 "Does not approve, request changes, or run linters"——它只产出评审意见文本,不代写修复代码、不执行 approve/request-changes 操作、不运行 linter。这一点在 skills/caveman-review/README.md 与 skills/caveman-review/SKILL.md 中反复强调,属于该技能的核心设计原则:输出即可粘贴进 PR 的评论文本,仅此而已。
核心格式规范:一行一发现
技能的第一条指令是:
Write code review comments terse and actionable. One line per finding. Location, problem, fix. No throat-clearing.
即:每个发现只写一行,包含位置、问题、修复三部分,禁止任何铺垫。具体格式规则如下:
基础格式
L<line>: <problem>. <fix>.
多文件 diff 场景,由于同一文件内的行号会冲突,格式扩展为带文件前缀的形式:
<file>:L<line>: <problem>. <fix>.
格式要求行号精确(L42 或区间 L88-140),符号名使用反引号包裹,修复方案必须是具体可执行的动作而非"考虑重构一下"这类空泛建议。
严重度前缀(Severity Prefix)
当一次评审混合了不同性质的发现时,可以在行首加严重度前缀,四级定义如下:
| 前缀 | 含义 | 典型场景 |
|---|---|---|
🔴 bug: |
行为错误,会引发事故 | 空指针、越界、状态错乱 |
🟡 risk: |
能跑但脆弱 | 竞态条件、缺失 null 检查、吞掉的异常 |
🔵 nit: |
风格、命名、微优化 | 作者可以忽略 |
❓ q: |
真正的问题,不是建议 | 需要作者解释意图 |
四级前缀的语义边界值得注意:nit: 是"作者可以安全忽略的",q: 严格限定为"genuine question, not a suggestion"——即当你对某行代码的真实意图存疑、且没有把握给出修复方向时,应该用 q: 提问而不是猜测着给建议。
Drop 与 Keep:措辞黑名单与必留要素
这份 SKILL.md 最有价值的部分,是它对评审措辞的显式正/负清单——这在本质上是把"人类资深评审员的沟通习惯"蒸馏成了可被 LLM 直接执行的规则。
Drop(必须去掉的表达)
- "I noticed that..."、"It seems like..."、"You might want to consider..."——一切试探性开场白;
- "This is just a suggestion but..."——想弱化建议时,直接改用
nit:前缀表达,不需要口头免责; - "Great work!"、"Looks good overall but..."——整体性夸赞只允许在评论顶部说一次,而不是每条评论前都来一遍;
- 复述这行代码做了什么——评审者自己会读 diff,复述纯属浪费 token;
- 对冲措辞(hedging):"perhaps"、"maybe"、"I think"——如果不确定,改用
q:前缀把"不确定"显式化,而不是藏在措辞里。
Keep(必须保留的要素)
- 精确的行号;
- 反引号包裹的精确符号名(函数、变量);
- 具体修复方案,而非 "consider refactoring this";
- 当问题本身不足以自明修复方向时,给出 why(修复原因)。
最后一条体现了一个务实的取舍:"简洁"不等于"信息不完整"——当一行格式装不下必要的解释时,why 是允许存在的。
示例对比:同一发现的两种写法
原文档给出了三组 ❌/✅ 对照,完整继承如下,可以视为该技能的行为基准(ground truth):
例 1:空指针风险
❌
I noticed that on line 42 you're not checking if the user object is null before accessing the email property. This could potentially cause a crash if the user is not found in the database. You might want to add a null check here.
✅
L42: 🔴 bug: user can be null after .find(). Add guard before .email.
例 2:函数职责过多
❌
It looks like this function is doing a lot of things and might benefit from being broken up into smaller functions for readability.
✅
L88-140: 🔵 nit: 50-line fn does 4 things. Extract validate/normalize/persist.
例 3:缺失重试机制
❌
Have you considered what happens if the API returns a 429? I think we should probably handle that case.
✅
L23: 🟡 risk: no retry on 429. Wrap in withBackoff(3).
注意 ✅ 版本的三个共性特征:行号精确(L42、L88-140、L23)、符号名具体(.find()、.email)、修复是可执行的动作(Add guard before .email、Extract validate/normalize/persist、Wrap in withBackoff(3))。caveman-help 技能中的速查表也给出了同款示例:L42: bug: user null. Add guard.(见 skills/caveman-help/SKILL.md)。
完整的四类输出示例(见 skills/caveman-review/README.md 的 "Example output" 一节):
L42: 🔴 bug: user can be null after .find(). Add guard before .email.
L88-140: 🔵 nit: 50-line fn does 4 things. Extract validate/normalize/persist.
L23: 🟡 risk: no retry on 429. Wrap in withBackoff(3).
L107: ❓ q: why drop the cache here? Reads on next request will miss.
Auto-Clarity:简洁模式的受控退出
无条件的一行化会产生反效果,因此 SKILL.md 定义了 Auto-Clarity(自动清晰)机制——以下三类场景必须放弃 terse 模式,改用正常段落展开说明:
- 安全类发现(CVE 级漏洞):需要完整解释 + 参考依据,一行装不下且不允许装;
- 架构分歧:需要给出论证(rationale),而不只是一行结论;
- 新人 onboarding 场景:作者是新成员,需要解释 "why" 才能理解并成长。
处理方式同样明确:在该场景内写正常段落,其余部分恢复 terse。这是一个"规则 + 逃生舱"的组合设计——默认走压缩格式,遇到高风险/高解释成本的场景自动降级,而不是让使用者每次手工开关。
行为边界与退出机制
SKILL.md 的 "Boundaries" 一节划定了该技能的完整行为边界:
- 只产出评审:不写修复代码、不 approve、不 request-changes、不运行 linter;
- 输出即成品:产出的评论应当是可以直接粘贴进 PR 的状态;
- 显式退出:用户说
"stop caveman-review"或"normal mode"时,恢复到冗长的常规评审风格。
这些边界不是文档层面的口头承诺,而是被测试固化下来的契约——后文会看到仓库中针对命令分发与模式解析的测试如何覆盖这些行为。
源码级实现:/caveman-review 的完整链路
SKILL.md 定义了"输出长什么样",而仓库中的 CLI 侧代码负责"这个技能如何被触发、激活、跨窗口追踪状态"。这条链路分为四段。
1. 命令定义:commands/caveman-review.toml
caveman-review.toml 以 TOML 声明了斜杠命令的 prompt,全文仅两行:
description = "One-line code review comments"
prompt = "Review the current code changes. One-line per finding. Format: L<line>: <severity> <problem>. <fix>. Severity: bug, risk, nit, q. Skip praise. Skip obvious. If code look good, say 'LGTM' and stop."
这段 prompt 与 SKILL.md 是互补关系:命令 prompt 是每次调用时注入的短指令(内联了格式与四级严重度,并补充了 SKILL.md 中未显式写出的 "LGTM 终止条件"——代码没问题就说 LGTM 并停止,避免无意义的 nit 填充);SKILL.md 则是更完整的技能级行为描述。两者共同保证了无论技能文件是否被完整加载,格式约束都已生效。
2. 命令解析:caveman-parse.js
用户输入被 UserPromptSubmit 钩子捕获后,由 src/hooks/caveman-parse.js 解析。关键分支在 caveman-parse.js#L221-L223:
if (cmd === '/caveman-review' || cmd === '/caveman:caveman-review') {
return { action: 'set', mode: 'review' };
}
两个细节值得注意:
- 双形式兼容:marketplace 插件安装时命令带命名空间前缀(
/caveman:caveman-review),因此解析器对裸形式与命名空间形式都接受。源码注释指出这是针对 issue #599 的修复——此前只有 compress 和 stats 支持命名空间变体; - 独立模式(independent mode):
review与commit、compress一起被标记为独立模式,它们各自拥有独立的 SKILL.md 和行为契约,不依赖主 caveman 的 lite/full/ultra 强度档位。
3. 模式跟踪与状态行徽标:caveman-mode-tracker.js
src/hooks/caveman-mode-tracker.js#L94 中,独立模式被显式枚举:
INDEPENDENT_MODES: new Set(['commit', 'review', 'compress']),
该文件中的模式切换逻辑(如 caveman-mode-tracker.js#L254 附近的注释)确保了一个重要的恢复语义:进入独立模式前记住当前模式,退出 review 模式后恢复原模式——例如先 /caveman ultra 再 /caveman-review,评审结束后应回到 ultra 而非默认档位。
状态行(statusline)方面,src/hooks/README.md 说明了徽标映射:/caveman-review 激活后,终端状态行显示 [CAVEMAN:REVIEW](与 /caveman-commit → [CAVEMAN:COMMIT]、/caveman ultra → [CAVEMAN:ULTRA] 等并列)。状态行脚本按会话(session)读取模式标志文件,每个 Claude Code 窗口各自维护独立的模式状态。
4. 注册表保留与跨平台分发
skills/registry.json 中有一个 preserved_skill_ids 列表,caveman-review 位列其中(与 cavecrew、caveman-commit、caveman-help、caveman-stats 并列)。从源码结构看,该列表用于在技能注册表的 v2 重组织中被显式保留——即 review 这类独立技能不会被新注册体系吞并或改写,保持其独立加载语义。CLAUDE.md 也确认了这一点:"Independent skills in skills/caveman-review/SKILL.md... Both have own description and name frontmatter so they load independently",并说明这类技能不经 CI 镜像到 Claude Code 插件分发,而是走 standalone hook + skill 安装路径,其他代理则通过 npx skills add 获得。
分发层面的证据在 bin/install.js 中:
- bin/install.js#L632:
HERMES_SKILL_DIRS包含caveman-review; - bin/install.js#L698-L700:opencode 的安装器同时分发技能目录与
caveman-review.md命令文件(OPENCODE_COMMAND_FILES),使其在 opencode 中也能以/caveman-review斜杠命令形式调用。
src/plugins/opencode/README.md 与 src/plugins/opencode/commands/caveman-help.md 中的命令卡片同样列出了 /caveman-review("One-line review findings"),与 README.md 主文档的命令表保持一致。
5. 测试固化
以下测试文件把上述行为固化成了可回归验证的断言:
- tests/test_caveman_parse.js#L123-L131:断言
parseModeChange('/caveman-review')与命名空间形式/caveman:caveman-review都返回{ action: 'set', mode: 'review' }; - tests/test_mode_tracker.py#L205-L216:通过真实 stdin 驱动 mode tracker,发送
/caveman-review与/caveman:caveman-review,验证状态写入; - tests/installer/slash-commands.test.mjs:把
caveman-review列入DOCUMENTED_COMMANDS,保证命令在文档与安装产物中不缺失; - tests/installer/opencode.test.mjs 与 tests/installer/hermes.test.mjs:分别验证 opencode 命令文件/技能目录、Hermes 技能目录安装中
caveman-review的存在性,以及冲突时"部分复制前整体失败"的原子性(hermes 测试断言冲突场景下caveman-review目录不存在); - tests/verify_repo.py:仓库级一致性校验,检查
skills/caveman-review/SKILL.md等路径的完整存在。
安装与调用方式
caveman-review 随 caveman 安装路径按平台分发,无需单独配置:
| 目标 | 获取方式 | 调用形式 |
|---|---|---|
| Claude Code | standalone hook + skill 安装路径(统一安装器自动接线) | /caveman-review 或自然语言 "review this PR" / "review the diff" |
| opencode | 安装器写入技能目录 + 命令文件(见 bin/install.js#L698-L700) | /caveman-review |
| Hermes 及其他代理 | npx skills add(HERMES_SKILL_DIRS 包含该技能) |
技能描述匹配触发 |
使用时的三个实用要点:
- 直接粘贴输出:技能约定输出即可粘贴进 PR 的评论,不需要二次编辑;
- 混合发现时加前缀:单一无严重度争议的小 review 可以省略前缀,一旦混有 bug/risk/nit/question 就按四级前缀标注,评审者可以按
🔴优先处理; - 显式退出:会话中随时用
"stop caveman-review"或"normal mode"回到冗长风格,评审结束后 tracker 会自动恢复激活前的主模式档位。
小结
caveman-review 的设计可以用三句话概括:格式上,L<line>: <severity> <problem>. <fix>. 一行一发现,多文件加文件前缀,四级严重度前缀可选;措辞上,用显式的 Drop/Keep 清单消灭寒暄、复述与对冲,强制精确行号、符号名与可执行修复;边界上,Auto-Clarity 在安全漏洞、架构分歧、新人场景自动降级为正常段落,且技能严格只输出评论、不执行任何 PR 操作。仓库侧的 commands/caveman-review.toml 命令 prompt、caveman-parse.js 的双形式命令解析、caveman-mode-tracker.js 的独立模式状态管理,以及覆盖解析、状态跟踪、多平台安装的测试套件,共同把这份"LLM 评审行为契约"变成了可安装、可验证、可回归的工程组件。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00