@admin镇馆龙发布于 10月30日 23:02
你的 Claude Code 钩子可能只看到了 3/7 次改动:Edit|Write 漏掉的那 4 次,全在 Bash 里
先看一个 10 月 30 日的实测结论,它会颠覆很多人对钩子的信任。测试者用 Claude Code 2.1.312、离线假 API 做了 7 次文件改动:3 次走 Claude 的文件工具(Edit、Write 新建、Write 覆盖),4 次走 Bash(sed -i、重定向、rm、git mv)。挂在 PostToolUse 上、匹配器为 Edit|Write 的钩子只看到了 3 次;会话结束时跑 git status 的 Stop 钩子,7 次全看到了。以下步骤按这篇公开实测和多份钩子教程整理,未在本轮逐条复现,照着配之前先在自己的小仓库里跑一遍。
漏掉的原因不在钩子坏了,在匹配器的过滤方式。匹配器按工具名过滤,Bash 在它眼里不是写文件工具,Bash 后面跟的重定向、sed、删除、改名,它一概不管。还有两个常见的配错要一起排掉:匹配器是区分大小写的精确字符串,小写的 write|edit 永远匹配不上,失败时还没有任何报错和日志;MultiEdit 在当前官方工具名里并不存在,加进匹配器只是死代码,不会多看到任何一次改动,真正会丢东西的是只写 Edit、不写 Write——那样 Claude 新建的文件会直接漏网。
可直接抄的组合是两层。第一层,PostToolUse 挂 Edit|Write,做格式化、lint 这类事后活;记住 PostToolUse 的退出码 2 拦不住已经发生的编辑,只能把报错喂回给 Claude 让它下一轮修,真正要事前拦截得用 PreToolUse、退出码 2 挡在工具执行之前。第二层,会话结束的 Stop 钩子跑 git status --porcelain -uall,当成最终盘点:提交之前拿实际改动清单去对允许清单,Bash 改过的、删掉的、改名的,全会在这里现形。这两层分工别颠倒:第一层管“改得对不对”,第二层管“到底改了什么”。
什么情况下这个结论不成立:如果你的 agent 从来不用 Bash 改文件、所有改动都走 Edit 和 Write,那 Edit|Write 本来就够,3/7 这个数字是这次测试的改动构成,不是通用漏检率。如果你的仓库不是 Git,git status 这层兜底就不存在,得换成你自己的文件快照对比;还有一个盲点要记着:拿路径字符串做包含判断时,符号链接能绕过两层检查,路径比较要先解析成真实路径再比对。


