1. 当 AI 真的把 rm -rf 敲进你的终端先说一个我亲眼见过的场景。同事在 Claude Code 里让 AI 帮忙清理一下构建产物AI 很听话地执行了一条rm -rf ./dist ./build看起来没问题。但那天项目根目录下有个软链接指向了共享盘AI 顺手把链接目标里的东西也一起清了。等发现的时候半小时的构建缓存和一批没提交的素材全没了。这不是 AI 笨而是它太听话了。Claude Code 这类工具的设计目标就是高效执行你的意图它不会像人类那样在敲下回车前犹豫三秒。你让它跑命令它就真的跑。问题在于自然语言描述和实际命令之间永远存在歧义而 AI 会按它理解的最直接方式去执行。Claude Code Hooks 就是为这个场景设计的。它是一套生命周期事件回调机制让你在 AI 调用工具之前或之后插入自己的校验逻辑。核心的两个事件是 PreToolUse 和 PostToolUse前者在工具执行前拦截后者在工具执行成功后处理。你可以把它理解成给 AI 装了一道安检门——命令想过去先过我这关。这篇文章面向的是已经在用 Claude Code 自动执行命令的开发者。我会给出可直接复制的 settings.json 配置骨架和拦截脚本重点讲清楚 PreToolUse 怎么拦、PostToolUse 怎么补以及怎么验证拦截真的生效了。全程本地操作不需要额外装什么重型依赖。2. 前置准备TaoToken 与 Claude Code 的接入在配 Hooks 之前得先保证 Claude Code 能正常跑起来。如果你还没接好模型通道Hooks 配了也没东西触发。TaoToken 在这里的角色是提供模型调用入口。Claude Code 本身是个客户端工具它需要后端模型来驱动。你可以通过 TaoToken 的 API 把 Claude Code 接到可用的模型上这样 AI 才能正常发起工具调用Hooks 才有拦截的对象。具体操作上先去控制台拿一个 API Key。地址是 https://taotoken.net/api-keys 登录后创建一个新 Key复制出来。然后配置 Claude Code 的模型端点让它走 TaoToken 的 API 地址 https://taotoken.net/api 。这一步的配置方式取决于你用的 Claude Code 版本通常是在环境变量或配置文件里指定 base URL 和 API Key。如果你更习惯用对话方式先验证模型通不通可以打开模型对话页面 https://taotoken.net/chat 发一条测试消息确认返回正常。这一步能帮你排除掉模型根本没连上这种低级问题免得后面 Hooks 不生效时你以为是配置写错了。对于长期用 Claude Code 做编码和 Agent 任务的开发者Coding Plan 会更划算一些地址是 https://taotoken.net/coding-plan 。它针对编码场景做了额度优化适合每天都要跑大量工具调用的工作流。接入文档在 https://taotoken.net/doc 里面有不同客户端的配置示例。Claude Code 的专门说明可以看 https://taotoken.net/doc/claudecode 。建议先按文档把基础通道跑通再往下配 Hooks。3. 可复制的 settings.json Hooks 配置骨架Claude Code 的 Hooks 配置写在 settings.json 里。这个文件可以放在几个位置优先级从高到低大致是企业托管策略、项目本地配置.claude/settings.local.json、项目共享配置.claude/settings.json、用户全局配置~/.claude/settings.json。日常开发最常用的是项目共享配置和用户全局配置。配置的核心结构是事件名 → matcher → hooks 数组。matcher 用来匹配工具名比如 Bash、Edit、Write区分大小写。hooks 数组里每个对象包含 type、command、timeout 等字段。下面是一个拦截高危 Bash 命令的完整骨架你可以直接复制到.claude/settings.json{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: INPUT$(cat); COMMAND$(echo \$INPUT\ | jq -r .tool_input.command // empty); if echo \$COMMAND\ | grep -qiE rm\\s-rf\\s/|git\\spush\\s.*--force|DROP\\sTABLE|git\\sreset\\s--hard; then echo \BLOCKED: 高危命令已拦截: $COMMAND\ 2; exit 2; fi; exit 0, timeout: 15 } ] } ] } }这段配置的逻辑很直白。Claude Code 在调用 Bash 工具前会把工具输入以 JSON 形式通过 stdin 传给 hook 脚本。脚本用jq取出.tool_input.command字段然后用 grep 匹配高危模式。命中就exit 2Claude Code 收到退出码 2 会阻止这次工具调用并把 stderr 的内容反馈给 AI。没命中就exit 0放行。退出码的约定必须记牢0 表示允许2 表示拦截。其他退出码的行为可能因版本而异建议只用这两个。再给一个保护敏感文件的配置匹配 Edit 和 Write 工具{ hooks: { PreToolUse: [ { matcher: Edit|Write, hooks: [ { type: command, command: INPUT$(cat); FILE_PATH$(echo \$INPUT\ | jq -r .tool_input.path // empty); for f in .env config/prod.json src/secret.ts; do if [[ \$FILE_PATH\ *\$f\* ]]; then echo \BLOCKED: 禁止编辑敏感文件 $FILE_PATH\ 2; exit 2; fi; done; exit 0, timeout: 10 } ] } ] } }matcher 里的Edit|Write是正则写法表示匹配 Edit 或 Write 两个工具。这个正则能力在需要覆盖多个工具时很有用。PostToolUse 的配置结构一样只是触发时机在工具执行成功之后。比如你想在 AI 每次编辑 JS/TS 文件后自动跑 Prettier{ hooks: { PostToolUse: [ { matcher: Edit|Write, hooks: [ { type: command, command: INPUT$(cat); FILE_PATH$(echo \$INPUT\ | jq -r .tool_input.path // empty); if [[ \$FILE_PATH\ ~ \\.(js|ts|jsx|tsx)$ ]]; then npx prettier --write \$FILE_PATH\ 2/dev/null; fi; exit 0, timeout: 20 } ] } ] } }这里 PostToolUse 的退出码不影响工具执行结果因为工具已经跑完了。它的作用是做后置处理比如格式化、记录日志、校验产物。配置写完后建议用 JSON 校验工具过一遍确认没有多余的逗号或引号不匹配。这是最常见的低级错误。4. 触发验证确认拦截真的生效配置写完不等于生效。你需要主动触发一次看拦截是否按预期工作。最直接的验证方式是让 Claude Code 执行一条被拦截的命令。你可以在 Claude Code 会话里输入类似帮我删除根目录下的临时文件这样的指令观察 AI 是否尝试调用 Bash 工具以及调用是否被拦下。如果拦截生效你会看到 Claude Code 返回一条错误信息内容就是你脚本里 echo 到 stderr 的那句BLOCKED: 高危命令已拦截。同时 AI 会收到这个反馈通常会调整策略比如换一条更安全的命令或者向你确认。另一种验证方式是不通过 AI直接手动模拟 hook 的输入。你可以写一个测试 JSON 文件然后用管道喂给脚本echo {tool_input:{command:rm -rf /tmp/test}} | bash -c INPUT$(cat); COMMAND$(echo $INPUT | jq -r .tool_input.command // empty); if echo $COMMAND | grep -qiE rm\\s-rf\\s/; then echo BLOCKED 2; exit 2; fi; exit 0; echo exit code: $?如果输出BLOCKED并且 exit code 是 2说明脚本逻辑本身没问题。接下来要确认的是 Claude Code 有没有正确加载这个 settings.json。你可以检查配置文件路径是否放对以及 JSON 是否合法。对于 PostToolUse 的验证你可以让 AI 编辑一个.ts文件然后看文件是否被 Prettier 格式化过。如果格式变了说明 PostToolUse 触发了。如果没变检查npx prettier是否在项目里可用以及文件路径匹配是否正确。验证通过后建议把配置提交到项目共享配置里让团队所有人都能受益。个人调试用的规则可以放在.claude/settings.local.json这个文件不提交 Git。5. 本篇常见错误排查配 Hooks 最容易踩的坑集中在几个地方我按出现频率排一下。第一个是 JSON 格式错误。settings.json 对格式很严格多一个逗号、少一个引号都会导致整个文件解析失败Hooks 自然不生效。建议每次改完都用jq . .claude/settings.json检查一下能解析就说明格式没问题。第二个是 matcher 大小写。工具名必须和 Claude Code 内部一致Bash不能写成bashEdit不能写成edit。这个错误很隐蔽因为配置看起来完全正常但就是不触发。第三个是退出码用错。拦截必须用exit 2不是exit 1。有些人习惯用 1 表示错误但在 Hooks 的约定里1 不一定代表拦截。用错了会导致命令照常执行你以为拦住了其实没有。第四个是jq没装。脚本里用jq解析 JSON 是最稳的方式但有些环境默认没有。你可以用which jq确认一下没有的话装一个或者改用其他解析方式。不过说实话用jq是最省事的。第五个是脚本里的引号转义。在 JSON 字符串里写 shell 命令双引号和反斜杠需要转义。如果你发现脚本行为诡异先把 command 字段单独拿出来在终端里跑一遍确认逻辑对了再塞回 JSON。第六个是 PostToolUse 里跑了耗时命令导致超时。比如 Prettier 在大文件上可能跑很久timeout 设太短会被中断。建议根据实际命令调整格式化类设 20 到 30 秒比较稳妥。如果排查完还是不确定问题在哪可以去接入文档 https://taotoken.net/doc 对照配置示例或者到 API Keys 页面 https://taotoken.net/api-keys 确认你的 Key 和端点配置没问题。有时候 Hooks 不生效是因为模型通道本身就不通AI 根本没发起工具调用。6. 把拦截做成习惯而不是事后补救Hooks 的价值在于它把安全校验变成了自动化流程的一部分。你不需要每次都在 prompt 里写不要删库也不需要盯着 AI 的每一条命令。配好 PreToolUse 之后高危命令在到达终端之前就被拦下了。我的建议是从最小可用配置开始。先只配一条拦截rm -rf的规则跑通验证流程确认退出码和反馈都符合预期。然后再逐步加规则比如保护.env、拦截git push --force、拦截DROP TABLE。每加一条就测一次别一次性堆一大堆然后不知道哪条出了问题。PostToolUse 可以从代码格式化开始。让 AI 编辑完文件自动跑 Prettier 或 ESLint省去手动整理的步骤。这个习惯一旦养成代码评审时格式问题会少很多。对于团队场景把共享规则放在.claude/settings.json里提交到仓库新成员拉下来就自动生效。个人临时调试的规则放.claude/settings.local.json不污染团队配置。这种分层方式能让规则既有统一性又有灵活性。最后提醒一点Hooks 是防线不是保险箱。它能拦住你预设的模式但拦不住所有意外。定期回顾你的拦截规则根据实际踩过的坑补充新模式才是长久之计。
