Caveman `/caveman:init` 命令深度解析:一条命令为全部 IDE Agent 写入常驻精简规则
/caveman:init 是 caveman 仓库("why use many token when few token do trick")中一个面向多 IDE 的按仓库初始化工具:它以 Claude Code 斜杠命令的形式定义,底层调用自包含的 Node 脚本 caveman-init.js,把"always-on caveman"精简沟通规则一次性写入 Cursor、Windsurf、Cline、GitHub Copilot、OpenCode、AGENTS.md 等各个 Agent 的规则位置。读完本文,你将掌握该命令的两种运行路径(仓内运行与 curl | node 独立运行)、完整的目标文件清单与写入模式、幂等/围栏/原子写的底层实现,以及 --dry-run、--force、--only 三个参数的实际行为边界。
命令定义与用途
该命令以 Claude Code 命令文件格式存放于 commands/caveman-init.md,其 frontmatter 声明了命令元数据:
---
description: Drop the always-on caveman activation rule into the current repo for every IDE agent
argument-hint: "[--dry-run|--force] [--only <agent>]"
---
命令正文只描述了三件事:
- 把每仓库的 caveman 规则文件(Cursor、Windsurf、Cline、Copilot、AGENTS.md)写入当前仓库,并汇报结果;
- 按"取第一个适用项"的策略选择运行方式:
- 若当前仓库存在
src/tools/caveman-init.js(即你正处在一个 caveman 检出目录中),运行node src/tools/caveman-init.js $ARGUMENTS; - 否则下载独立脚本执行——该脚本自包含、支持 stdin 执行:
curl -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/src/tools/caveman-init.js | node - $ARGUMENTS;
- 若当前仓库存在
- 如果用户没有传
--force,先使用--dry-run,确保绝不悄悄覆盖已有规则文件。
同一命令还有 commands/caveman-init.toml 版本(其他 Agent 使用的 TOML 格式命令定义),内容与 .md 版等价。
这里的"always-on 规则"解决的是一个具体问题:Caveman 技能(npx skills add JuliusBrussee/caveman)默认是逐会话按需激活的,而 Cursor、Windsurf、Cline、Copilot 这类 Agent 没有 hook 系统,想要"常驻精简模式"只能靠静态规则文件。init 命令就是这条常驻路径的按仓库分发器——这一点在 INSTALL.md 的 "always-on" 章节中也有对应说明,且两者的规则内容共用同一份单一事实源。
规则内容从哪来:单一事实源
写入各 IDE 的规则正文并非散落在各处,脚本采用"仓库内源优先、内嵌副本兜底"的双保险策略。从 src/tools/caveman-init.js 的 loadRuleBody() 可以看到:
function loadRuleBody() {
// Prefer the in-repo source-of-truth when available.
try {
const local = path.join(__dirname, '..', 'rules', 'caveman-activate.md');
if (fs.existsSync(local)) return fs.readFileSync(local, 'utf8').trimEnd() + '\n';
} catch (e) {}
return RULE_BODY;
}
- 首选读取 src/rules/caveman-activate.md——仓库中规则正文的唯一权威来源;
- 若该文件不存在(即
curl | node独立运行场景),回退到脚本内嵌的RULE_BODY常量(src/tools/caveman-init.js),源码注释明确要求两者保持同步。
规则正文本身定义了精简沟通的行为契约:
Respond terse like smart caveman. All technical substance stay. Only fluff die.
Rules:
- Drop: articles (a/an/the), filler (just/really/basically), pleasantries, hedging
- Fragments OK. Short synonyms. Technical terms exact. Code unchanged.
- Pattern: [thing] [action] [reason]. [next step].
- Not: "Sure! I'd be happy to help you with that."
- Yes: "Bug in auth middleware. Fix:"
Switch level: /caveman lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra
Stop: "stop caveman" or "normal mode"
Auto-Clarity: drop caveman for security warnings, irreversible actions, user confused. Resume after.
Boundaries: code/commits/PRs written normal.
要点包括:保留全部技术实质、只删虚词与客套;支持 lite | full | ultra 及对应文言档位切换;安全警告、不可逆操作、用户困惑时自动恢复正常措辞(Auto-Clarity);代码/commit/PR 永远按正常风格书写。仓库的维护约定(见 CLAUDE.md)也明确:改自动激活规则应编辑 src/rules/caveman-activate.md,不要编辑任何用户机器上的 per-agent 规则副本。
完整写入目标:7 个 Agent、两种写入模式
命令文档摘要中列出 Cursor/Windsurf/Cline/Copilot/AGENTS.md 五类目标,而源码中的 AGENTS 注册表(src/tools/caveman-init.js)实际覆盖了 7 个目标,每个目标由"文件路径 + frontmatter + 模式"三元组描述:
| agent id | 目标文件 | 写入模式 | 附加 frontmatter |
|---|---|---|---|
cursor |
.cursor/rules/caveman.mdc |
replace(整文件覆盖) | description: ... + alwaysApply: true |
windsurf |
.windsurf/rules/caveman.md |
replace | trigger: always_on |
cline |
.clinerules/caveman.md |
replace | 无 |
copilot |
.github/copilot-instructions.md |
append(追加围栏块) | 无 |
opencode |
.opencode/AGENTS.md |
append | 无 |
agents |
AGENTS.md |
append | 无 |
openclaw |
~/.openclaw/workspace/{skills/caveman/, SOUL.md} |
特殊 installer 分支 | 见下文 |
两种模式的分工在源码中有清晰注释(src/tools/caveman-init.js):
- replace 模式:目标文件是 caveman 独占的单用途文件(如
.cursor/rules/caveman.mdc),直接由 frontmatter + 规则正文组成,caveman 完整拥有该文件; - append 模式:目标文件(
AGENTS.md、copilot-instructions.md等)是用户自己也在维护的共享文件,因此 caveman 的贡献必须用 HTML 注释围栏包裹:
const FENCE_BEGIN = '<!-- caveman-begin -->';
const FENCE_END = '<!-- caveman-end -->';
没有这对围栏,规则块既无法在升级时原地刷新,也无法在卸载时干净移除——源码里把这个后果形容为"fossilize in the user's repo"(化石化在用户仓库里)。
OpenClaw 特殊分支
OpenClaw 是全局工作区工具而非按仓库安装,且需要写两个目标(技能目录 + SOUL.md 引导块),因此它不走"文件/frontmatter/模式"三元组,而是通过 installer: 'openclaw' 回调逃生舱转交给共享助手 bin/lib/openclaw.js(src/tools/caveman-init.js 中的 loadOpenclawHelper() 惰性加载)。注意:在 curl | node 独立模式下磁盘上没有这个助手文件,此时该目标会报告 unsupported-standalone 状态并提示改用 npx 方式运行——这是独立模式的已知边界。
幂等性设计:状态机与损坏围栏防御
caveman-init.js 文件头注释第一句就是 "Idempotent. Safe to re-run.",processAgent()(src/tools/caveman-init.js)实现了一台相当细致的状态机。核心识别机制是哨兵字符串:
const SENTINEL = 'Respond terse like smart caveman';
append 模式的状态流转
- 文件不存在 → 写入围栏块,状态
added(+); - 文件存在且围栏配对完整(
caveman-begin与caveman-end各恰好出现一次且 begin 在 end 之前):- 块内容与期望完全一致 →
skipped-already-installed(=),不做任何写入; - 块内容过期(规则正文变了)→ 原地刷新,仅替换围栏之间的字节,围栏两侧的用户内容原样保留,状态
refreshed(~);
- 块内容与期望完全一致 →
- 围栏损坏(孤儿 BEGIN、BEGIN/END 数量不为 1、END 出现在 BEGIN 之前)→
skipped-damaged-fence(!),一个字节都不写。源码注释解释了为什么必须如此:一个孤儿 BEGIN 若被"聪明地"与后面块的 END 配对,刷新会替换两者之间的全部字节——包括用户自己的内容;把损坏标记当作损坏处理、报告并停手,才能保证第二次运行不会让损失复利; - 旧版无围栏安装(文件里只有裸的哨兵文本)→
skipped-legacy-unfenced(?):无法区分哪些字节是 caveman 写的、哪些是用户改的,静默重写被跟踪的仓库文件比留一个过期块更糟; - 全新共享文件(既无围栏也无哨兵)→ 追加围栏块,状态
appended(~)。
replace 模式的状态流转
replace 模式的文件没有围栏,哨兵命中无法可靠区分"我们装过的旧规则"和"用户恰好引用了规则原文的文件",因此策略更保守:
- 文件不存在 →
added; - 文件已存在且含哨兵 →
skipped-already-installed(不做猜测性覆盖); - 文件已存在、无哨兵、且用户传了
--force→ 整文件覆盖,状态overwritten(!)——--force是 replace 模式下唯一的重置路径; - 其余情况 →
skipped-exists(?)。
这套状态机与命令文档中"先 --dry-run、不悄悄覆盖"的原则互为表里:默认路径下,任何已有文件都只会被"跳过"或"围栏内原地刷新",绝不被整文件覆盖。
原子写与跨平台落盘细节
规则文件最终落在用户仓库的受跟踪路径上,一次中断的半截写入会破坏用户已提交的文件。writeAtomic()(src/tools/caveman-init.js)因此采用"临时文件 + rename"策略,并针对同步盘做了重试:
for (let attempt = 0; ; attempt++) {
try {
fs.renameSync(tmp, fullPath);
break;
} catch (error) {
const transient = error.code === 'EPERM' || error.code === 'EBUSY' ||
error.code === 'EACCES' || error.code === 'EEXIST';
if (!transient || attempt >= 2) throw error;
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10 * (attempt + 1));
}
}
从源码注释可知:rename 到已存在的目标需要 DELETE 访问权,而 OneDrive/Dropbox 同步客户端和杀毒扫描器恰好持有带 FILE_SHARE_DELETE 之外的句柄;对瞬态错误按 10ms 递增退避重试最多两次,失败则清理临时文件并抛出。这与 caveman-config.js 中 safeWriteFlag() 采用同一策略。
命令行参数与输出格式
参数解析在 parseArgs()(src/tools/caveman-init.js):
| 参数 | 含义 |
|---|---|
[target-dir] |
位置参数,指定目标仓库目录;缺省为当前工作目录 |
--dry-run |
只报告将发生的变更,不写任何文件 |
--force / -f |
覆盖已存在的规则文件(默认:跳过) |
--only <id> |
仅为一个 agent 安装,id 取自目标清单 |
-h / --help |
打印用法,含完整目标清单 |
一个值得注意的健壮性细节:--only 的值缺失或拼错(如 --only nope)时,脚本会直接以退出码 2 报错 caveman-init: --only requires one of: cursor, windsurf, cline, copilot, opencode, agents, openclaw。源码注释解释了动机——这类错误过去会"看起来像成功":要么静默退化为全量安装,要么匹配不到目标而报告一个全零的假成功。
main() 的输出格式为逐行状态 + 汇总计数:
🪨 caveman init — /path/to/repo (dry run)
+ .cursor/rules/caveman.mdc (added)
~ AGENTS.md (appended)
...
N added, N appended, N refreshed, N overwritten, N skipped
(dry run — no files were written)
状态到计数的归并规则(src/tools/caveman-init.js):added/installed/would-add 计入 added,appended、refreshed、overwritten 各自单列,其余状态(各类 skipped)计入 skipped。
独立运行的隐藏坑:stdin 执行守卫
curl … | node - 是命令文档指定的独立运行路径,但脚本末尾的执行守卫比经典的 require.main === module 多写了一半(src/tools/caveman-init.js):
// Run when executed directly AND when piped via `curl … | node -` (the
// documented standalone path, #603): under stdin execution require.main is
// undefined and module.id is '[stdin]', so the classic guard alone silently
// no-ops with exit code 0 — the worst kind of failure.
if (require.main === module || (!require.main && module.id === '[stdin]')) main();
在 stdin 管道执行下 require.main 为 undefined、module.id 是 '[stdin]',经典守卫会导致脚本静默什么都不做且退出码为 0。仓库内还有回归测试守着这一行为:tests/installer/slash-commands.test.mjs 中 issue #603 的断言要求 commands/caveman-init.md / .toml 凡引用仓库相对路径 src/tools/caveman-init.js,必须同时提供 raw.githubusercontent.com … caveman-init.js 的 curl 独立回退,防止命令对"已安装用户"(他们本地没有 src/tools/ 目录)整体失效。
测试对关键行为的验证
tests/test_caveman_init.js 是一组 fixture 式端到端测试(node tests/test_caveman_init.js 即可运行),覆盖了状态机的主要分支,可作为行为验收清单:
- greenfield:全新目录一次写入全部规则文件,且 frontmatter 正确(
.cursor含alwaysApply: true,.windsurf含trigger: always_on); - 幂等:对刚装好的目录重跑,输出
7 skipped,无 added; - append 不替换:已含用户内容的
AGENTS.md追加规则后原文Do not delete me.仍在; - 无
--force不覆盖:预置的.cursor/rules/caveman.mdc内容逐字节不变; --force覆盖:同样文件被替换为带 frontmatter 的新规则;--dry-run:输出(dry run)与6 added,但六个目标路径一个都不存在;--only cline:只生成.clinerules/caveman.md;- 哨兵检测:手写了含哨兵的 cline 文件 →
skipped-already-installed; - 围栏刷新:围栏块内容过期时原地刷新,
caveman-begin标记数量保持 1(不叠加第二块),围栏两侧用户内容保留; --only nope/ 裸--only:退出码 2,错误信息--only requires one of;- 孤儿 BEGIN 标记:连续重跑 3 次均报
skipped-damaged-fence,文件与初始内容逐字节一致; - END 在 BEGIN 之前:同样拒绝处理。
测试还通过把 OPENCLAW_WORKSPACE 指向 fixture 内不存在的目录,确保 openclaw 分支不会触碰开发者真实的 ~/.openclaw/workspace。
与 --with-init 安装器的关系
/caveman:init 斜杠命令并不是唯一的入口,它是同一脚本的"命令面"。完整安装器 bin/install.js 的 --with-init 标志走同一条路:INSTALL.md 的参数表说明 --with-init 会"Drop always-on rule files into the current repo(.cursor/、.windsurf/、.clinerules/、.github/copilot-instructions.md、.opencode/AGENTS.md、AGENTS.md),并在检测到 OpenClaw 时追加 ~/.openclaw/workspace/SOUL.md 引导块",规则单一事实源同样是 src/rules/caveman-activate.md。卸载文档亦说明:--with-init 写出的这些按仓库规则文件属于可选残留,需要时手动删除即可。
实操建议:推荐的运行顺序
结合命令文档的约束与源码行为,在目标仓库中启用 always-on caveman 的稳妥流程是:
- 先干跑:
node src/tools/caveman-init.js --dry-run(仓内)或curl -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/src/tools/caveman-init.js | node - --dry-run(任意仓库),确认added/refreshed/skipped-*明细符合预期; - 正式执行:去掉
--dry-run再跑一次;共享文件(AGENTS.md等)会以围栏块追加或原地刷新,独占文件直接生成; - 只装一个 Agent:加
--only <id>,id 必须取自帮助输出中的清单,否则以退出码 2 失败; - 确需重置独占规则文件:显式加
--force——这是唯一会覆盖非 caveman 内容的路径,故命令文档要求"用户未传--force时先 dry-run"; - 遇到
skipped-damaged-fence/skipped-legacy-unfenced:这是防御性停手而非故障。前者需要人工检查AGENTS.md等文件里的围栏标记(孤儿或错序的<!-- caveman-begin -->/<!-- caveman-end -->),后者需要手动删掉旧规则块后重跑以获得围栏化安装。
总体而言,/caveman:init 虽然只有几行命令文档,其背后的 caveman-init.js 实现把"往用户受跟踪仓库里写文件"这件事的边角——幂等重跑、共享文件围栏、损坏标记防御、原子落盘、stdin 静默失败——都做了显式处理,并配有逐分支的 fixture 测试,是 caveman 常驻规则分发链中值得细读的一环。
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