本层判断标准

让助手去读一次 .env,它会被拒绝吗?换一台机器、换一种工具呢?

答「不确定」,说明拦截还停在提示词里。钩子只有真跑一次、看到拒绝信息时,才算存在。

目录结构

钩子配置按工具落在三个位置,脚本统一从仓库根执行。Claude Code 没有独立配置文件,写在 settings.json 的 hooks 字段里。

仓库根目录下的钩子配置与脚本

仓库根目录/
├── .cursor/
│   ├── hooks.json              # version: 1;事件名小写 preToolUse
│   └── hooks/
│       └── block-env.py        # 从仓库根执行;stdin 读 JSON、stdout 写 JSON
├── .claude/
│   └── settings.json           # 写在 hooks 字段里,不是 hooks.json;事件名 PreToolUse
└── .trae/
    └── hooks.json              # 事件名 PreToolUse;用 timeout 限时

判据落在位置上:Claude Code 的钩子写成独立的 hooks.json 不会被读;三处写错位置都不报错、只是不生效。配完要先实测一次:让助手去读一次 .env,看它是否被拒绝。

典型应用示例:助手想读 .env 的那一刻

场景:排查「本地为什么连不上数据库」,助手的第一反应是读 .env 里的连接串——对排查来说这很合理,但正是要挡的动作。

有它 工具调用发出之前,钩子脚本拿到路径、按文件名判定:恰好是 .env,退出码 2,拒绝。助手收到拒绝原因,改读 .env.example,用里面的变量名继续排查。
没有它 只靠仓库说明里那句「不读」——助手可能照做,也可能在「排查需要」的理由下读它;而读没读,事后不一定知道。

什么时候该用钩子

判断只看一句:这个动作是「必须被挡住」还是「希望被记得」。前者才配钩子,后者有更合适的落点。

要做的事做成什么怎么判
读取密钥文件、写生产库、删除分支这类动作必须被挡住钩子一旦发生就必须拦下,靠不住自觉?是 → 钩子(这是它唯一的适用场景)
希望助手每次记住一条做法仓库说明删掉它,不相关的任务会不会变差?会 → 仓库说明
只对某一类文件成立的约束规则这句话只在改这类文件时才用得到吗?是 → 规则
需要人主动触发的多步检查命令需不需要人记得去用、由人拍板?是 → 命令
要求合并前必须通过的检查持续集成门禁换台机器、换个人还要挡得住吗?要 → 门禁
可以这么做 只为「一旦发生就必须拦下」的动作配钩子;一个事件一个脚本;命中时把原因写清楚,让看到的人知道被拦的理由。
不要这么做 为了凑齐事件每个都加一个脚本;用钩子代替门禁;把「记得写测试」这类希望写进钩子。

钩子拦不住绕过:助手换用没覆盖的工具,或人在命令行里手工读取文件,钩子都不经过。所以它只降低概率,不构成门禁——真正兜底的是质量门禁里的密钥扫描。

落在哪

三家的配置文件位置、事件名、失败时放不放行都不一样,写错位置不报错、只是不生效。规则文件本身也要一起保护:它能被助手改写,一条约束就被这次任务自己放开了。

工具钩子配置落地页
Cursor.cursor/hooks.json(failClosed 显式设 true,崩溃也不放行)Cursor 钩子落地页
TraeCode.trae/hooks.jsonTraeCode 钩子落地页
Claude Code.claude/settings.json 的 hooks 字段(不是 hooks.json)Claude Code 钩子落地页