caveman opencode 插件速查:模式、斜杠命令与自然语言触发的完整解析
本文以 caveman 仓库中 opencode 插件的 caveman-help 命令卡片为核心,完整继承其模式速查表,并结合 plugin.js、caveman-parse.js、caveman-config.js 的源码实现,讲清每个斜杠命令背后的解析逻辑、默认模式的四级解析顺序、自然语言开关的触发规则,以及"代码/提交/安全警告自动退出"这一行为在规则文件中的落地方式。读完可以掌握在 opencode 中激活、切换、退出 caveman 压缩模式的全部途径及其底层机制。
速查卡:caveman-help 的原始内容
caveman-help.md 是 opencode 插件中 /caveman-help 斜杠命令的提示词模板(frontmatter 中的 description 为 "Quick reference card for caveman modes, slash commands, and triggers")。它的正文就是一张速查表,外加两句行为说明:
| Command | What |
|---|---|
/caveman |
Activate at default level (full) |
/caveman lite |
Light compression — ~30% tokens dropped |
/caveman ultra |
Maximum compression |
/caveman wenyan[-lite|-ultra] |
Classical Chinese compression |
/caveman off |
Deactivate |
/caveman-commit |
Terse commit message |
/caveman-review |
One-line review findings |
/caveman-compress <file> |
Compress a Markdown file |
/caveman-stats |
Lifetime token-savings |
速查表之外还有两条关键说明,同样需要纳入日常使用习惯:
- 自然语言也可以切换:"turn on caveman"、"stop caveman"、"normal mode" 都能改变模式状态,不依赖斜杠命令;
- 自动退出(drop out):当任务涉及代码、commit 消息、安全警告时,caveman 风格自动让位于正常英文,无需手动关闭。
下面逐条结合仓库源码展开,验证上述卡片与实际行为是否一致、背后如何实现。
模式分级:/caveman 的合法参数
/caveman 模板 caveman.md 的 frontmatter 声明了完整可传级别:lite | full | ultra | wenyan-lite | wenyan-full | wenyan-ultra | off。与速查表对应关系如下:
- 裸
/caveman:按配置的默认级别激活(默认full); /caveman lite:轻度压缩,约 30% token 缩减;/caveman ultra:最大压缩;/caveman wenyan[-lite|-ultra]:文言文(Classical Chinese)压缩,wenyan-full是规范写法,配置中存储为wenyan;/caveman off:退出模式。
caveman-activate.md 给出了压缩风格的实际规则——"Respond terse like smart caveman. All technical substance stay. Only fluff die.",具体为:丢掉冠词(a/an/the)、填充词(just/really/basically)、客套与模糊表达;允许碎片句;技术术语与代码保持原样;句式遵循 [thing] [action] [reason]. [next step].;反例是 "Sure! I'd be happy to help you with that.",正例是 "Bug in auth middleware. Fix:"。
从源码结构看,合法模式的单一事实来源是 caveman-config.js 中的白名单:
const VALID_MODES = [
'off', 'lite', 'full', 'ultra',
'wenyan-lite', 'wenyan', 'wenyan-full', 'wenyan-ultra',
'commit', 'review', 'compress'
];
注意后三个 commit、review、compress 属于独立模式——它们由各自的斜杠命令触发,不能通过 /caveman <arg> 选择。这一约束在 caveman-parse.js 中显式建模:
// Modes handled by their own slash commands (/caveman-commit, etc.) — not
// selectable via /caveman <arg>.
const INDEPENDENT_MODES = new Set(['commit', 'review', 'compress']);
如果用户真的输入 /caveman commit,resolveModeArg 会返回 unresolved 并指明这是独立模式,而不是静默吞掉或误激活;无法识别的级别同样返回 unresolved(注释明确写着 "never silently overwrite with the default")。插件侧的 applyModeChange 只处理 set/clear 两种动作,其余一律忽略,因此非法参数不会改变任何状态。
默认模式从哪来:四级解析顺序
速查卡说 /caveman 是 "Activate at default level (full)",这个 "default" 的解析顺序在 caveman-config.js 的文件头注释中定义:
- 环境变量
CAVEMAN_DEFAULT_MODE(最高优先级); - 仓库本地配置:从当前工作目录向上查找最近的
<cwd>/.caveman/config.json或<cwd>/.caveman.json(团队可以按项目钉住默认模式,避免污染每个贡献者的用户级配置); - 用户配置文件中的
defaultMode字段:$XDG_CONFIG_HOME/caveman/config.json、~/.config/caveman/config.json(macOS/Linux)或%APPDATA%\caveman\config.json(Windows); - 兜底值
'full'。
查找仓库配置时最多向上走 64 层,且拒绝符号链接(与标志文件写入的 lstat 安全检查保持一致),见 findRepoConfigPath。
独立模式命令:commit / review / compress / stats
速查表中四个独立命令各自对应 commands/ 目录下的一个提示词模板文件,其内容比速查卡的一行描述更具体,值得逐一看清。
/caveman-commit — 简短提交信息
caveman-commit.md 的指令是:为当前已暂存变更生成提交消息,要求遵循 Conventional Commits 格式,subject 不超过 50 字符、祈使句、type 后小写、无句号;body 只在 "why" 无法从 subject 看出的情况下才写,且解释 why 而非 what,去掉填充词与模糊表达。
/caveman-review — 一行一条的发现
caveman-review.md 规定:审查当前 diff(或指定文件),每个问题一行,格式为 L<行号>: <severity> <问题>. <修复>.;严重度用 🔴 critical / 🟡 warn / 🟢 nit 标记;跳过非问题;按文件分组,结尾给一行结论。
/caveman-compress — 压缩 Markdown 文件
caveman-compress.md 的约束值得注意:
- 对给定文件运行
caveman-compressskill,把散文改写为 caveman 风格,但代码块、行内代码、URL、文件路径、命令与 Markdown 结构必须原样保留; - 覆盖前把原文件备份为
<file>.original.md; - 只压缩自然语言文件(
.md、.txt、.typ、.tex、无扩展名),拒绝源码/配置文件(.py、.js、.ts、.json、.yaml、.toml、.sh等); - 绝不压缩已有的
*.original.md备份。
仓库中 tests/caveman-compress/ 目录存放了该 skill 的成对测试样例(如 todo-list.md 与 todo-list.original.md),可直接用来核对压缩前后差异。
/caveman-stats — 累计节省统计
caveman-stats.md 要求读取终身历史日志 ~/.config/caveman/.caveman-history.jsonl(或 caveman-stats 脚本写入的位置),输出总节省 token 数、会话数、平均压缩比,用一张短表呈现。
自然语言触发:不敲斜杠也能开关模式
速查卡写道 "Natural language also works: 'turn on caveman', 'stop caveman', 'normal mode'." 这些短语的实现集中在共享解析器 caveman-parse.js 的 parseModeChange() 中,它被 Claude Code hook 与 opencode 插件共用(源码注释称之为 #602 引入的 "single source of truth",防止两侧正则漂移)。
退出意图优先判定。 激活短语与退出短语可能同时出现(如 "turn caveman mode off"),所以解析器先计算 wantsOff(caveman-parse.js#L150-L160),匹配以下模式之一即返回 { action: 'clear' }:
stop|disable|deactivate|quit|exit|kill (the) cavemancaveman (mode) off|stop|disabledturn off (the) caveman- 以
normal mode开头的指令(允许 "please/go back to/switch back to/return to" 前缀),或句中同时含 "normal mode" 与 "caveman"
normal mode 之所以限制在句首或带 caveman 上下文,是为了不误伤 "how do I exit vim normal mode" 这类讨论编辑器模式的句子。
激活短语带防误触设计。 激活匹配(caveman-parse.js#L184-L207)除了 "turn on caveman"、"activate caveman"、"talk like caveman" 等短语外,还接受简短化请求("less tokens"、"be brief"、"be terse"、"fewer tokens"、"shorter answers")——但排除 "be brief in the summary" 这类限定在单个段落的一次性指令。同时有一个 isQuestion 守卫:以 what/how/why/is/can/explain 等开头的句子("what is caveman mode?")不会被当成激活命令。
引用文本中的触发词会被抹除。 源码注释记录了真实事故(issue #838):用户在 bug 报告里引用了 help 卡片自己的一行文字 "Say 'stop caveman' or 'normal mode'",结果把模式关掉了。修复方式是匹配前先用 QUOTED_SPAN_REGEX 把双引号与反引号包裹的片段替换为空格(caveman-parse.js#L72),只把 " 与 视为定界符——撇号在英文中太常见,若配对会连命令本身一起抹掉。
裸命令与参数清洗。 裸 /caveman 走默认模式;带参数时,normalizeModeArg() 先剥掉首尾粘连的标点(/caveman ultra; 也能解析出 ultra),而 "参数存在但被清洗为空"(如 /caveman ?)返回 unresolved 而不是激活——源码注释的解释是:这种情况更可能是用户在求助,不应被顺手切进模式。
opencode 的模板展开需要特殊处理。 opencode 会把输入的 /caveman ultra 在到达 chat.message hook 之前替换为 caveman.md 模板的散文正文,因此解析器开启 expandedTpl 选项后,从模板固定前缀 ^activate caveman mode:[ \t]*(\S*) 中恢复级别(caveman-parse.js#L170-L182);三个独立命令则按各自模板的首句识别(generate a commit message for the current staged changes → commit,review the current diff → review,compress the file at: → compress)。另外 opencode 非交互 run 路径投递的消息带一层字面引号,unwrapQuotes 选项负责剥掉。
自动退出:代码、提交与安全警告为什么恢复英文
速查卡最后一句 "Code, commits, security warnings: caveman drops out automatically." 的规则定义在 caveman-activate.md:
- Auto-Clarity:遇到安全警告、不可逆操作、用户表现困惑时退出 caveman 风格,之后恢复;
- Boundaries:代码、commit、PR 一律用正常英文书写。
caveman.md 模板尾部还补充了会话语义:"Behavior persists until session ends or user says 'stop caveman' / 'normal mode'. Code, commits, security warnings: write normal English." 也就是说自动退出是局部的(只对当前输出段落生效),模式状态本身在整个会话内保持,直到用户显式关闭或会话结束。
底层实现:opencode 插件的三个钩子
速查卡能生效,靠的是 plugin.js 中的 opencode 插件实现(Bun ESM 模块,默认导出 CavemanPlugin 工厂)。它通过三个 hook 承载全部动态状态:
1. event(session.created)— 会话开始时写标志文件。
handleSessionCreated 调用 getDefaultMode():默认是 off 则删除标志文件,否则用 safeWriteFlag 写入 ~/.config/opencode/.caveman-active。opencode >= 1.15 通过单一 event 分发器按 event.type 路由(旧的直接键名会被静默忽略),插件同时在工厂加载时断言一次标志,覆盖一次性 opencode run 中首个 session.created 早于插件事件装配的竞态。标志目录解析优先 $XDG_CONFIG_HOME,否则 ~/.config/opencode(plugin.js#L99-L106)。
2. chat.message — 解析用户消息中的模式变更。
hook 遍历消息的 text parts,对每个 part 调用 parseModeChange(part.text, { getDefaultMode, expandedTpl: true, unwrapQuotes: true }),有返回值就通过 applyModeChange 落到标志文件上(set 写入、clear 删除)。源码注释明确:返回值被丢弃,状态变更全部经由标志文件完成,不依赖 hook 返回值。
3. experimental.chat.system.transform — 每轮注入强化行。
当标志文件指示处于非独立模式时,插件向系统提示词注入一行强化语:CAVEMAN MODE ACTIVE (<mode>) — session ruleset applies.(reinforcementLine),让模型每轮都记得会话规则。该注入是幂等的:用正则 CAVEMAN MODE ACTIVE \([a-z-]+\) — session ruleset applies\. 查找已存在行并原地替换,避免 opencode 复用 output.system 数组时逐轮堆叠、悄悄吃掉上下文窗口(plugin.js#L183-L210)。
安全细节。 safeWriteFlag 复用 caveman-config.js 的加固实现:O_NOFOLLOW、临时文件+原子 rename、0600 权限、拒绝符号链接、所有权检查。由于 opencode 在编译后的 Bun 二进制中运行插件、require() 磁盘文件会被拒绝,插件没有走模块加载器,而是把 caveman-config.js 与 caveman-parse.js 的源码读出来用 new Function(...) 手工求值,createRequire 只解析 Node 内置模块(fs/path/os)——见 loadConfig() 及其上方注释。
安装与使用前提
按 src/plugins/opencode/README.md 说明:
- 安装命令为
bin/install.js --only opencode,它会复制plugin.js、package.json、commands/*.md以及改名后的caveman-config.cjs到~/.config/opencode/plugins/caveman/,并在opencode.json中补一个"plugin"数组条目; - 没有 statusline 徽章:opencode TUI 不暴露插件可写的 statusline,想在自己的 shell 提示词中显示模式,可直接读取标志文件
~/.config/opencode/.caveman-active; - 没有
session.created系统提示注入:always-on 规则集来自安装器同时写入的~/.config/opencode/AGENTS.md,即使插件运行时损坏规则仍然加载; - 插件代码复用主仓库的
caveman-config.js,以仓库内插件形式发布,避免二次发布节奏以及与第三方opencode-cavemannpm 包的命名冲突。
目录结构总览(安装后):
~/.config/opencode/plugins/caveman/
├── package.json
├── plugin.js # 动态状态:标志写入、命令解析、强化注入
├── caveman-config.cjs # 复制自 src/hooks/caveman-config.js
├── caveman-parse.cjs # 复制自 src/hooks/caveman-parse.js
└── commands/ # 六个斜杠命令模板,含 caveman-help.md 速查卡
适用前提小结:以上行为对应 opencode >= 1.15 的插件事件模型;/caveman-help 本身不改变任何状态,它只是一张由 caveman-help.md 模板驱动的速查卡;所有模式状态都收敛到 ~/.config/opencode/.caveman-active 一个标志文件,这也是排查"为什么现在没压缩/怎么退不出来"时应先检查的位置。
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