caveman 的 opencode 插件:/caveman-review 一行式代码评审命令的设计与实现
caveman 是一个以“用最少的 token 表达最多信息”为核心卖点的 Claude Code 技能/工具集,其评审能力被拆分成多种可复用表面(slash command、skill、子代理)。本篇以 opencode 插件中的命令模板 caveman-review.md 为主体,完整解读这条 /caveman-review 命令的提示词契约、格式规范,并结合 plugin.js、caveman-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.
逐句拆解这段提示词,可以看出它把“代码评审”约束成了一个非常严格的输出协议:
descriptionfrontmatter: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.js 用 new 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.js 中
OPENCODE_COMMAND_FILES明确包含caveman-review.md,OPENCODE_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.js 的 chat.message 解析链路(/caveman-review → 独立 review 模式 → flag 文件)、SKILL.md 的 Drop/Keep 措辞纪律、cavecrew-reviewer.md 的子代理变体,caveman 实现了同一评审契约在命令、技能、子代理三个层面的一致性,且每一层都有 tests/test_caveman_parse.js 与 tests/installer/opencode.test.mjs 级别的测试锚点可供核验。
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