首页
/ caveman 的 opencode 插件:/caveman-review 一行式代码评审命令的设计与实现

caveman 的 opencode 插件:/caveman-review 一行式代码评审命令的设计与实现

2026-09-06 13:33:19作者:凤尚柏Louis

caveman 是一个以“用最少的 token 表达最多信息”为核心卖点的 Claude Code 技能/工具集,其评审能力被拆分成多种可复用表面(slash command、skill、子代理)。本篇以 opencode 插件中的命令模板 caveman-review.md 为主体,完整解读这条 /caveman-review 命令的提示词契约、格式规范,并结合 plugin.jscaveman-parse.js 与对应测试,说明该命令在 opencode 插件运行时中是如何被解析、展开和跟踪的,读者读完后能理解 caveman 一行式评审(one-line finding)格式的设计动机与跨端一致性。

命令模板本体:8 行提示词的完整契约

caveman-review.md 是 opencode 插件自带的 6 条 slash-command 模板之一。整个文件仅由 YAML frontmatter 和一段提示词组成,全文如下:

---
description: Caveman-style code review — one-line findings with severity
---
Review the current diff (or files: $ARGUMENTS).

One line per finding. Format: `L<line>: <severity> <problem>. <fix>.`
Severity emoji: 🔴 critical · 🟡 warn · 🟢 nit. Skip non-issues.
Group by file. End with a one-line verdict.

逐句拆解这段提示词,可以看出它把“代码评审”约束成了一个非常严格的输出协议:

  • description frontmatter:opencode 用它渲染命令列表。该命令注册为 “Caveman-style code review — one-line findings with severity”,即“带严重度标记的 caveman 风格代码评审”。
  • Review the current diff (or files: $ARGUMENTS)$ARGUMENTS 是 opencode slash-command 的占位符。无参数时评审当前 diff;带参数时把参数当作文件列表来评审。这让一条命令同时覆盖“评审 PR/diff”和“评审指定文件”两个场景。
  • One line per finding. Format: L<line>: <severity> <problem>. <fix>.:每个发现(finding)占且仅占一行,结构固定为「行号 → 严重度 → 问题 → 修复建议」。问题与建议各以句号结尾,读者无需解析长段落即可逐条定位。
  • Severity emoji: 🔴 critical · 🟡 warn · 🟢 nit. Skip non-issues.:三级严重度用 emoji 前缀编码——🔴 严重(critical)、🟡 警告(warn)、🟢 吹毛求疵级(nit);并显式要求跳过非问题,杜绝无信息量的评论。
  • Group by file. End with a one-line verdict.:多文件 diff 时按文件分组输出,最后给出一行总结判定(verdict),使输出天然适合作为 PR 评论整体粘贴。

这个模板与仓库中其他评审表面共享同一套核心约定。例如 Claude Code 的 TOML 命令 commands/caveman-review.toml 使用完全相同的行格式,只是严重度词汇略有差异:

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."

独立技能 skills/caveman-review/SKILL.md 则给出了更完整的四档严重度(🔴 bug / 🟡 risk / 🔵 nit / ❓ q)与多文件场景的前缀规则(<file>:L<line>: ...)。三个表面的格式骨架一致(L<line>: <severity> <problem>. <fix>.),差异仅在词汇表——这是 caveman 刻意保持的跨端一致性:无论用户在哪个 agent 里触发评审,产出的行格式都可直接粘贴到 PR 评论中。

opencode 插件中命令模板的加载与展开

opencode 插件的目录结构与各文件的职责在 src/plugins/opencode/README.md 中有明确说明:commands/*.md 是 6 条 slash-command 提示词模板(/caveman/caveman-commit/caveman-review/caveman-compress/caveman-stats/caveman-help)。插件通过 bin/install.js --only opencode 安装:这些模板连同 plugin.js 被复制到 ~/.config/opencode/plugins/caveman/,并向 opencode.json 写入一个 "plugin" 数组条目。

理解 caveman-review.md 如何真正生效,需要看 plugin.js 的 hook 实现。关键机制有四个:

1. 插件工厂与 flag 断言。 CavemanPlugin 工厂在插件加载时即调用 handleSessionCreated()(见 plugin.js 的注释:一次性 opencode run 中首个 session.created 事件可能在事件分发接线前就已发布,所以工厂时刻要抢先写一次 flag)。该函数读取配置的默认模式,非 off 时用 safeWriteFlag 把模式写入 flag 文件:

const flagPath = path.join(opencodeConfigDir(), '.caveman-active');

opencodeConfigDir() 优先取 $XDG_CONFIG_HOME/opencode,否则回落到 ~/.config/opencode(Windows 上同样是 %USERPROFILE%\.config\opencode)。safeWriteFlag 复用了主仓库 caveman-config.js 中的符号链接安全写入助手(O_NOFOLLOW、原子 temp+rename、0600 权限、拒绝符号链接、属主检查),安装时该文件被重命名为 caveman-config.cjs 以适配插件目录的 "type": "module"

2. chat.message hook:slash 命令与模式解析。 用户消息在进入模型前会经过 'chat.message' hook(plugin.js):遍历 output.parts 中的文本 part,对每段文本调用共享解析器 parseModeChange(part.text, { getDefaultMode, expandedTpl: true, unwrapQuotes: true })。两个参数值得注意:

  • expandedTpl: true:opencode 会把用户输入的 slash 命令替换成命令文件里的提示词正文后再交给 hook——即你输入 /caveman-review,模型最终看到的是 caveman-review.md 中那段“Review the current diff …”正文;
  • unwrapQuotes: true:非交互式 run 路径会把消息包裹在字面引号内,需要先剥壳再解析。

解析器本身是单一事实源 caveman-parse.js,同时被 Claude Code 的 caveman-mode-tracker.js 复用(plugin.jsnew Function 手工求值加载它,原因注释写明:opencode 在编译后的 Bun 二进制里运行插件,require() 磁盘文件会被拒绝、await import() CJS 文件返回空命名空间,两条常规路径都会静默失败)。

3. /caveman-review 被解析为独立的 review 模式。 解析结果通过 applyModeChange 落盘(set 写 flag,clear 删除 flag)。测试 tests/test_caveman_parse.js 固化了这一行为:

assert.deepStrictEqual(parseModeChange('/caveman-review', defaultFull), { action: 'set', mode: 'review' });
assert.deepStrictEqual(parseModeChange('/caveman:caveman-review', defaultFull), { action: 'set', mode: 'review' });

/caveman-review(及 /caveman:caveman-review 命名空间写法)都会把模式置为 review。Claude Code 侧的映射在 src/hooks/README.md 中记录为 /caveman-review[CAVEMAN:REVIEW]

4. 独立模式跳过每轮强化。 插件的 'experimental.chat.system.transform' hook 在每次 LLM 请求前向 system prompt 注入一行 CAVEMAN MODE ACTIVE (<mode>) — session ruleset applies.,但有一个关键条件(plugin.js):

const active = readFlag(flagPath);
if (active && !INDEPENDENT_MODES.has(active)) {

只有非独立模式(如持续性的 lite/full/ultra)才需要每轮强化,因为 review 这类独立模式的作用域就是本次命令——提示词模板本身已经把评审行为完整表达出来了,无需每轮重复提醒。该 hook 还用正则做幂等改写(找到旧强化行就原地替换,而不是追加),注释解释这是为了防止 opencode 若跨轮复用 system 数组导致系统提示无限膨胀、悄悄吃掉上下文窗口。

一行评审格式的深度规范:SKILL.md 的 Drop / Keep 清单

caveman-review.md 命令模板只有 5 句正文,但与之配套的独立技能 skills/caveman-review/SKILL.md 把格式契约扩展为可执行的规则集,二者是同一评审行为的“模板层”与“规则层”。SKILL.md 的核心内容值得完整继承:

行格式。 L<line>: <problem>. <fix>.;多文件 diff 时用 <file>:L<line>: ...。严重度前缀在混合输出时选用:

前缀 含义
🔴 bug: 行为已损坏,会引发事故
🟡 risk: 能跑但脆弱(竞态、缺空值检查、吞异常)
🔵 nit: 风格、命名、微优化——作者可忽略
❓ q: 真问题,不是建议

必须删掉(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);一切模糊措辞(“perhaps”“maybe”“I think”)——不确定就标 q:

必须保留(Keep)的要素: 精确行号;反引号包裹的精确符号/函数/变量名;具体修复而非“建议重构一下”;当修复方案从问题陈述推不出来时,写出 why

正反对比例(原文即“压缩前 vs 压缩后”):

❌ "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.`

❌ "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.`

❌ "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).`

Auto-Clarity(自动转正常体)。 三类场景放弃 terse 模式、改写成正常段落,其余部分继续用一行式:安全类发现(CVE 级 bug 需要完整解释与引用)、架构分歧(需要理由而非一句话)、作者为新人的 onboarding 语境(需要解释 why)。

边界。 SKILL.md 明确:只评审,不写修复代码、不 approve/request-changes、不跑 linter;输出即“可直接粘贴进 PR 的评论”;说 “stop caveman-review” 或 “normal mode” 可退回冗长评审风格。这与 opencode 命令模板中 Skip non-issues 和“一行 verdict”的要求互为补充:模板定义输出骨架,技能定义措辞纪律。

同一契约的子代理形态:cavecrew-reviewer

除 slash command 与 skill 外,caveman 还把这个评审契约实装为可委派的子代理 agents/cavecrew-reviewer.md,用于 “review this PR / review my diff / audit this file” 场景。它与命令模板的差异值得对照阅读:

  • 输出格式扩展为 path:line: <emoji> <severity>: <problem>. <fix>.,并附一行汇总 totals: 1🔴 1🟡 1❓
  • 严重度表细分为 🔴 bug(错误输出、崩溃、安全漏洞、数据丢失)/ 🟡 risk(边界情况、竞态、泄漏、性能悬崖、缺守卫)/ 🔵 nit(仅在用户要求 thorough 时输出)/ ❓ question(需作者意图才能判断);
  • 零发现时只输出 No issues.;按文件顺序、文件内行号升序排列;
  • 边界约束与 SKILL.md 一致:只评眼前的代码,不做 “while we're here” 式扩展,不提大重构;需要更多上下文时标注 (see L<n> in <file>) 而不是猜测;
  • 工具面收窄:Bash 仅限 git diff / git log -p / git show,禁止任何变更类命令;frontmatter 中指定 tools: [Read, Grep, Bash]model: haiku,即评审走轻量模型、只读工具,控制委派成本。

也就是说,同一个“一行式 finding”契约在 caveman 中有三种载体:给用户的命令模板(caveman-review.md)、给模型的规则技能(SKILL.md)、给子代理的系统提示(cavecrew-reviewer.md)。三者格式骨架一致,严重度词汇在各表面间按上下文微调。

安装、校验与回归测试

安装与部署路径可通过仓库文件直接核对:

  • 安装器 bin/install.jsOPENCODE_COMMAND_FILES 明确包含 caveman-review.mdOPENCODE_SKILL_DIRS 包含 caveman-review 技能目录;安装即“复制命令模板 + 补丁 opencode.json”两步。
  • 安装后回归由 tests/installer/opencode.test.mjs 覆盖:它断言 6 个命令文件(含 caveman-review.md)全部就位,且 7 个技能目录(含 caveman-review)存在于产物中。
  • 解析层回归由 tests/test_caveman_parse.js 覆盖,其中 /caveman-review/caveman:caveman-review/caveman-compress 等被验证为“设置独立模式”这一组行为({ action: 'set', mode: 'review' })。

适用前提与限制:该命令模板依赖 opencode 插件运行时(hook 映射注释标明适用于 opencode >= 1.15.x 的单一 event 分发器模型);opencode TUI 不提供插件可写的 statusline,因此没有模式徽章,若想在 shell 提示符中显示模式,可读取 flag 文件 ~/.config/opencode/.caveman-active。另外,opencode 表面使用三级词汇(🔴 critical · 🟡 warn · 🟢 nit),而 SKILL.md 与子代理使用四级词汇(bug / risk / nit / question),跨表面阅读评审输出时应以触发该评审的具体表面为准。

小结

caveman-review.md 这条 8 行模板的价值不在行数,而在它把代码评审压缩为一个机器可对齐的输出协议:固定行格式、emoji 严重度、按文件分组、一行 verdict。配合 plugin.jschat.message 解析链路(/caveman-review → 独立 review 模式 → flag 文件)、SKILL.md 的 Drop/Keep 措辞纪律、cavecrew-reviewer.md 的子代理变体,caveman 实现了同一评审契约在命令、技能、子代理三个层面的一致性,且每一层都有 tests/test_caveman_parse.jstests/installer/opencode.test.mjs 级别的测试锚点可供核验。

登录后查看全文
热门项目推荐
相关项目推荐