Ratchet:监督AI代理行为的代码规范检查工具
Ratchet是一个开源工具,旨在解决AI编程代理在长时间会话中可能偏离代码规范(如偏好标准库、保持小变更)而无人察觉的问题。它通过一个`PostToolUse`钩子实时读取代理的每次代码编辑并进行度量分析,将结果反馈给同一个会话。工具会检测新增依赖、重复符号、包装器函数等问题,并按“确定”、“可能”、“启发式”进行分级。它支持建议、守护、严格等模式,允许设置变更预算,并能生成复杂性趋势报告,帮助维护代码库的简洁性。
来源:GitHub
Ratchet是一个开源工具,旨在解决AI编程代理在长时间会话中可能偏离代码规范(如偏好标准库、保持小变更)而无人察觉的问题。它通过一个`PostToolUse`钩子实时读取代理的每次代码编辑并进行度量分析,将结果反馈给同一个会话。工具会检测新增依赖、重复符号、包装器函数等问题,并按“确定”、“可能”、“启发式”进行分级。它支持建议、守护、严格等模式,允许设置变更预算,并能生成复杂性趋势报告,帮助维护代码库的简洁性。
来源:GitHub
PostToolUse 钩子会读取代理进行的每一次编辑,对其进行度量,并在代理仍在工作时,将发现的结果报告回同一个会话。\n\n\n你 为设置页面添加一个日期选择器\n\n代理 [安装 flatpickr,编写一个包装组件,添加样式表]\n\n钩子 ratchet guard · +71 -0 行(净增 +71/150) · 2/3 个新文件 · 1/1 个新依赖\n\n certain(确定)\n package.json:14 dep 新依赖 flatpickr\n 对照标准库和平台证明其合理性,否则移除它\n src/DatePicker.jsx:4 native 日期选择器组件库\n <input type=\"date\">\n likely(可能)\n src/DatePicker.jsx:22 wrapper DatePicker 只是转发给 Flatpickr\n 直接调用它并删除包装器\n\n代理 [撤销,改用 <input type=\"date\">]\n\n\n这里没有任何内容依赖于模型是否被系统提示说服。\n\n—\n\n## 安装\n\nbash\ngit clone https://github.com/0xwilliamortiz/ratchet.git\ncd ratchet\nnpm install -g .\n\n\n然后在每个你想监控的仓库中运行一次:\n\nbash\ncd your-project\nratchet\n\n\n这就是整个设置。它会注册钩子,开始度量,将代码中已有的内容接受为基线,并打开窗口:\n\n\nratchet 正在监控 your-project\n\n hooks C:\\your-project\\.claude\\settings.json\n measuring .ratchet\\ (在 1284 行处标记)\n baseline 接受了 31 个在“现在”之前的发现,只有新的发现会触发\n window 打开了 ratchetui.exe\n plugin C:\\Users\\you\\ratchet\n\n重启你的代理以使其加载钩子。\n\n\n重启你的代理。完成。\n\n要求。 Node 20 或更新版本。度量部分需要 Git 在 PATH 中;没有它,规则集和检测器仍然有效,但标记、账本和审计功能将无法工作。在 Windows 上,hooks/ratchetui.exe 随仓库一起提供并自动打开。\n\nratchet init 和 ratchet baseline 仍然可以用于单独执行这些步骤。\n\n—\n\n## 模式\n\n不是情绪。是钩子可以比较的数字。\n\n| 模式 | 新文件 | 新依赖 | 净增行数 | 超限时行为 |\n|——|———–|———-|—————–|————|\n| advise(建议) | 8 | 3 | 400 | 仅报告发现 |\n| guard(守护) | 3 | 1 | 150 | 报告发现和预算警告 |\n| strict(严格) | 1 | 0 | 60 | 编辑被阻止 |\n| off(关闭) | | | | 不运行任何内容 |\n\n默认为 guard。可以通过 /ratchet strict 在会话中切换,使用 /ratchet default advise 持久化,或设置 RATCHET_MODE。\n\n—\n\n## 测量内容\n\n每个 Edit、MultiEdit 和 Write 都会经过检测器。\n\n| 标记 | 检测内容 |\n|—–|———|\n| dep | 一个出现在 package.json、requirements.txt、pyproject.toml、go.mod、Cargo.toml、Gemfile 或 composer.json 中,而之前不存在的名称 |\n| exists | 一个新的符号,其规范化名称已存在于仓库的其他地方,例如 formatDuration 会找到 format_duration |\n| stdlib | 标准库已提供功能的自己动手实现版本 |\n| native | 一个依赖或代码在做平台已经能做的事情 |\n| wrapper | 一个函数,其函数体只是将相同参数转发给另一个函数 |\n| yagni | 一个只有一个实现的接口、抽象类或协议 |\n| validation | 一个定制的电子邮件正则表达式,这总是错误的 |\n| budget | 新文件、新依赖和净增行数的滚动总数 |\n\n### 发现结果分级\n\n这是工具被保留还是被静音的区别。\n\n| 级别 | 含义 | 示例 |\n|——-|——-|———|\n| certain(确定) | 已解析,非猜测 | 从清单中读取的依赖项 |\n| likely(可能) | 结构性 | 一个只做转发的函数 |\n| heuristic(启发式) | 基于正则表达式的形状匹配 | 一个手动分组循环 |\n\n输出按级别分组。strict 只阻止 certain 级别的发现。\n\n### 如何读取差异\n\n新增行是基于先前内容而非整个文件计算的,因此检测器只看到此会话实际编写的更改。对于 Edit,是替换字符串;对于 Write,是文件相对于其已提交版本。清单始终整体读取,因为部分编辑的 JSON 或 TOML 无法解析。\n\n删除也计入,预算使用净行数。删除五十行为你换来五十行。一个结束时比开始时更小的会话是工具在发挥作用。\n\n符号索引在临时目录中缓存,以文件数量加上最新修改时间戳为键。冷启动时需要几百毫秒,热启动时约 4 毫秒,上限为 3000 个文件。\n\n—\n\n## 关闭一个发现\n\n两种方式,意味着不同的事情。\n\n一行,对于你接受的原因:\n\njs\n// ratchet-ignore: 已分析,克隆是热路径\nconst copy = JSON.parse(JSON.stringify(frame));\n\n\n一个有历史的仓库:\n\nbash\nratchet baseline\n\n\n发现结果是根据标记、路径和形状而非行号进行指纹识别的,因此基线可以经受代码重格式化和在文件内移动位置。\n\n这就是这个名字的全部意义。现有债务被认可。新债务不被接受。\n\n—\n\n## 标记和账本\n\n.ratchet/mark.json 记录了代码库的一个可接受规模:源代码行数、源文件数、日期和写明的理由。会话开始时,仓库会与标记进行比较,并在超出时提示。会话结束时,会在 .ratchet/ledger.jsonl 追加一行。\n\n提高标记是慎重的,需要一句话:\n\nbash\nnode scripts/accept-mark.js \"vendored the parser until upstream ships the fix\"\n\n\n使用 ratchet report 查看所有内容:\n\n\n复杂性趋势,最近 3 次会话\n─────────────────────────────────\n\n ▁█▆ 18 到 22 行\n\nwhen mode added removed deps flagged repo\n2026-07-31 guard +3 -0 0 0 18 new\n2026-07-31 guard +5 -0 1 1 24 ▲ 6\n2026-07-31 guard +0 -2 0 0 22 ▼ 2\n\nmark 14 行,高于标记 8 行\n 理由: initial mark\n\n\n仅依赖提示的工具拒绝报告每个仓库的节省量,它们是正确的:从未写过的版本没有基线可以减去。但会话之前的仓库是真实的基线,账本是真实的记录。没有估计,没有发明的百分比,只有趋势。\n\n—\n\n## 标记一个捷径\n\npython\n# ratchet: 单一全局锁,如果写入吞吐量重要则按账户拆分\n\n\n上限,然后是触发器。/ratchet-ledger 收集它们,并标记任何没有命名触发器的条目,因为那些是会悄然变成永久性的条目。\n\n—\n\n## 命令\n\n### 终端\n\n| 命令 | 功能 |\n|———|——|\n| ratchet | 设置此仓库:钩子、度量、基线、窗口 |\n| ratchet --global | 为此机器上的每个项目设置钩子 |\n| ratchet audit | 扫描整个仓库并列出需要削减的内容 |\n| ratchet baseline | 接受今天存在的内容,只标记新的内容 |\n| ratchet report | 从账本中获得度量的趋势 |\n| ratchet status | 已安装和已度量的内容 |\n| ratchet doctor | 用真实载荷驱动每个钩子并证明其工作 |\n| ratchet log | 钩子在你真实会话期间实际说了什么 |\n| ratchet ui | 重新打开窗口,--debug 查看为何无法打开 |\n| ratchet uninstall | 移除钩子和会话状态 |\n\n--dry-run 打印将更改的内容,不写入任何内容。\n\n### 在你的代理中\n\n| 命令 | 功能 |\n|———|——|\n| /ratchet [advise\\|guard\\|strict\\|off] | 设置预算,或报告当前预算 |\n| /ratchet-review | 对当前差异的过度工程审查 |\n| /ratchet-audit | 跨整个仓库的相同审查 |\n| /ratchet-ledger | 趋势、标记和每个被推迟的捷径 |\n| /ratchet-accept | 记录一个新的标记并附上原因 |\n| /ratchet-help | 参考卡片 |\n\nstop ratchet 会为当前会话关闭它。它必须是整个消息,因此要求代理“添加一个停止 ratchet 按钮”不会在任务中途禁用它。\n\n—\n\n## 配置\n\n.ratchet/config.json,每个项目一份,用于提交:\n\njson\n{\n \"mode\": \"guard\",\n \"budget\": { \"newFiles\": 5, \"newDeps\": 1, \"addedLines\": 200 },\n \"ignore\": [\"migrations\", \"generated\"],\n \"scanTests\": false\n}\n\n\n每个用户的默认设置位于 ~/.config/ratchet/config.json。\n\n环境变量:RATCHET_MODE、RATCHET_LOG、RATCHET_UI 和 RATCHET_SUBAGENT_MATCHER,一个针对子代理类型进行测试的不区分大小写的正则表达式,以便将只读搜索代理排除在规则集之外。\n\n—\n\n## 预算不适用于什么\n\n信任边界处的验证。失败会导致数据丢失的路径上的错误处理。安全控制。无障碍基础设施。硬件校准。用户明确要求的任何内容。每个非平凡逻辑的每段有一个可运行的检查。\n\n这些在规则集内,且超出每个检测器的范围。建议删除冒烟测试的审查已经失败。\n\n—\n\n## 检查它是否工作\n\nnpm test 证明代码是正确的。以下两个证明你机器上的循环已连接好。\n\n**ratchet doctor** 构建一个临时仓库,植入一个包含三个已知问题的文件,驱动会话、守护和报告钩子作为真实子进程运行,并打印你的代理将会收到的确切文本。\n\n\n ok session hook 注入了规则集和预算\n ok guard hook 在植入文件中捕获了 dep、native、exists\n ok report hook 汇总并写入了一行账本记录\n\n\n**ratchet log --on** 然后让每个钩子将其发出的内容追加到 .ratchet/hook.log。进行一个普通会话,然后用 ratchet log 读回。这是看到真实情况而非模拟的唯一方法。\n\n两者都在首次运行时发现了真实的 bug。Doctor 发现符号索引每个名称只保留一个文件,由于 PostToolUse 在写入之后触发,被编辑的文件已在索引中,并掩盖了它本应发现的重复项。Log 发现依赖计数器存储的是整个句子而非包名,因此预算消息读作 drop moment imported for date work。\n\n—\n\n## 窗口\n\nhooks/ratchetui.exe 在你运行 ratchet 时打开,ratchet ui 重新打开它。仅限 Windows。无需配置。\n\n它通过 PowerShell 的 Start-Process 启动,该进程在应用运行后立即返回,因此应用比启动它的短命钩子存活时间更长。命令以 -EncodedCommand base64 形式传递,而非内联文本,因为像 C:\\Program Files (x86)\\... 这样的真实安装路径包含 cmd.exe 在 PowerShell 看到之前就会解释的字符。\n\n该路径中没有任何内容隐藏窗口。早期版本传递了 -WindowStyle Hidden、windowsHide 和 detached,意在隐藏 PowerShell 控制台,却隐藏了应用程序。Start-Process -PassThru 返回一个进程 ID,因此启动器报告实际发生的情况,而非假设返回的生成调用意味着窗口出现。\n\n该可执行文件接收仓库根目录两次,作为其第一个参数和其工作目录,并读取:\n\n| 路径 | 包含内容 |\n|——|———-|\n| %CLAUDE_CONFIG_DIR%\\ratchet\\*.json | 最新的文件是活动会话:mode、addedLines、removedLines、newFiles、deps、findings |\n| <root>\\.ratchet\\ledger.jsonl | 每行一个已完成的会话 |\n| <root>\\.ratchet\\mark.json | 可接受的规模 |\n\n窗口显示的所有内容也可通过 ratchet report、ratchet log 和 ratchet status 获取,这些命令在任何地方都有效。\n\n—\n\n## 值得了解的限制\n\n**检测器是正则表达式和 git grep,而非类型检查器。它们会产生误报。这就是为什么默认是报告而非阻止,为什么 strict 是可选的,以及为什么规则集告诉代理对不同意的发现大声反驳,而不是默默忽略。\n\nexists 比较规范化的名称。它会捕获真正重复的辅助函数,也会捕获两个恰好共享一个名称的无关函数。它排除五个字符以下的名称和一个通用名称列表,并且永远不会被评级为 certain,因为共享名称并非重复的证明。\n\n测试文件完全不被扫描。**夹具本应包含被测试的不良模式,标记它是工具教人们停止阅读它的方式。tests/、__tests__/、spec/、e2e/、fixtures/、*.test.*、*.spec.*、*_test.go、test_*.py、conftest.py 和 *.stories.* 在审计和符号索引中被跳过。设置 `