Ratchet Files 是一个源码文件体积约束工具,核心目标是让仓库中的每个源文件都保持在智能体(agent)能够一次性完整读取的范围内。
用途
当某个源文件超出智能体单次可读取的预算时,即视为超预算。默认预算为 8,000 tokens,按字节数除以 4 计算,除非仓库另行设置。scripts/check-file-size.mjs 脚本负责定义什么算源码、预算大小、棘轮机制以及拆分规则。使用前应先运行该脚本的 --help 查看说明。
主要能力
拆分超预算文件:在仓库根目录以 --all 运行脚本(若仓库钩子设置了 --max-tokens,则一并传入),脚本输出的列表即为待处理清单。选择要拆分的文件——可以是当前工作受阻的那个文件,若无阻塞则选择即将被改动且体积最大的文件。按脚本打印的规则拆分,拆分依据是文件本身已承载的概念(seam),而非行数;若安装了 refactor-clean,可用它来寻找拆分点。拆分后需用仓库自身的检查证明行为未变,并单独提交该拆分,再提交需要腾出空间的那次改动。
豁免机制:若某文件的所有拆分方案都得不偿失(例如一张只能整体读取的表格或状态机),可选择豁免。在该文件前 10 行内、使用文件自身的注释语法加入 @ratchet-files-ignore,并说明拆分失败的原因,例如:// @ratchet-files-ignore: one opcode table; split, each half reads without the other. 此后检查会完全跳过该文件,即使它继续增长。因此只有在寻找拆分点且确认无值得付出的方案后才豁免,绝不能为了绕过拒绝而豁免。需告知用户每个被豁免的文件及原因。--all 会统计被豁免的文件,使其保持可见。
完成标准:所选每个文件都在各自提交中降至预算内,行为检查通过,且 --all 不再列出这些文件。
设置棘轮(安装钩子)
未经用户明确同意,绝不安装钩子,即使用户要求设置仓库也不行。安装会冻结所有已超预算的文件,因此应先审计,审计期间保持仓库不变:将脚本复制到仓库外的临时目录,在其中运行 npm install --prefix <scratch> linguist-languages,然后在仓库根目录运行 node <scratch>/check-file-size.mjs --all(需要 Node 18.3 或更高版本)。若仓库缺少该包,脚本会在自身旁边查找。
一次性向用户询问所有需要同意的事项:钩子会冻结多少文件、哪些最大(排除将标记为生成或第三方代码的文件,并列出这些文件名);预算(默认 8,000 tokens,除非用户想要其他值);以及将接入哪个 pre-commit 运行器,包括需要新增的运行器。若仓库没有 Node 工具链,需说明该检查会引入它。
用户同意后,设置检查所需的一切:
脚本:钩子运行的是提交在仓库中的副本,因此每次克隆都运行相同检查。若本技能文件夹已提交在仓库中,将钩子指向其 scripts/check-file-size.mjs;否则将该文件复制到仓库存放工具脚本的位置(若没有则用 scripts/)。
依赖:以精确版本将 linguist-languages 添加为仓库根 package.json 的 dev 依赖,使用锁文件指定的包管理器,若无则用 npm。即使仓库使用其他语言编写,该检查也需要 Node:若没有 package.json,在根目录创建一个仅含 "private": true 及本次设置所添加内容的文件,并确保 .gitignore 覆盖 node_modules/。选择与仓库支持运行时兼容的依赖版本;检查脚本的最低 Node 版本并不代表运行器的最低版本。
钩子:用 node(或仓库已用 bun 运行工具时用 bun)对每个暂存文件运行脚本,预算非默认时追加 --max-tokens N。将其添加到仓库已有 pre-commit 运行器的现有条目旁:
- lint-staged:在其配置中加 "*": "node scripts/check-file-size.mjs"
- lefthook:在 pre-commit.commands 中加 run: node scripts/check-file-size.mjs {staged_files}
- pre-commit:在 .pre-commit-config.yaml 的 repo: local 下加 id: ratchet-files, name: source file size, entry: node scripts/check-file-size.mjs, language: system
- husky 或普通钩子:在 pre-commit 脚本中加 git diff --cached --name-only -z | xargs -0 node scripts/check-file-size.mjs
先检查 git config --show-origin --get core.hooksPath 和现有钩子。配置通常是共享的,而相对路径按检出解析,因此 Husky 的 .husky/_ 只存在于运行过包安装的地方,其他工作树提交时不受检查。仅当现有运行器也能安装到链接工作树时才保留它。
否则,以精确版本添加 lint-staged 作为 dev 依赖,并在根 package 的 prepare 脚本中(在现有 prepare 工作之后)调用 scripts/install-git-hooks.mjs。若技能已提交在仓库中则使用本技能的副本,否则在仓库工具脚本旁提交一份未改动的副本;绝不指向全局技能路径。它在共享钩子目录中安装一个钩子,运行提交检出自身的 lint-staged,对缺少依赖的检出用其锁文件的安装命令拒绝,并保留自定义钩子不动。每个提交的工作树都需要各自安装;绝不要共享其他检出的依赖或构建产物来提供依赖。若其逻辑必须更改,扩展本技能的 node --test scripts/install-git-hooks.test.mjs 以覆盖。
替换仓库自有的运行器(如 Husky)时,将其其他检查移入 lint-staged,并移除其设置和依赖,以免安装器冲突;绝不要取消用户自己的钩子路径。安装旧分支仍可能重新启用旧运行器,直到当前分支再次安装。继续前先检查安装器报告的任何冲突。
排除项:在 .gitattributes 中将生成和第三方代码标记为 linguist-generated 或 linguist-vendored,而不是添加跳过列表。脚本读取暂存的 .gitattributes,因此重新运行 --all 前先暂存它。
文档:在仓库记录其他钩子的地方记录预算、豁免 pragma、包安装如何跨工作树启用钩子、每个提交检出都需要依赖,以及旧分支可能重新启用的被替换运行器;若没有这样的地方,则在 README 中新增一节。
通过新钩子提交设置,不要用 --no-verify。
在一次性仓库夹具中用真实 git commit 尝试验证,使用已提交的安装器、运行器配置和大小检查脚本。安装一次,然后创建链接工作树而不在其中重新运行安装。使其依赖可用,并证明其自身暂存的超预算新文件被拆分规则拒绝,而预算内的提交成功且主检出保持不变。还要证明现有超预算文件无法增长、缺少依赖的检出会以设置说明拒绝、从链接工作树重复安装可行,以及自定义钩子在安装后仍保留。之后移除夹具。
完成标准:这些提交检查在两种检出类型中都通过,且设置提交通过新钩子而未绕过它。
适用场景与限制
适用于希望约束源码文件体积、使智能体能完整读取文件的仓库,尤其是需要跨多个工作树保持一致检查的团队。限制包括:需要 Node 18.3 或更高版本;安装钩子会冻结现有超预算文件,必须事先获得用户同意;豁免机制会使文件完全跳过检查,需谨慎使用;每个提交的工作树都需要各自安装依赖。