1. 为什么你的 Claude Code 需要一个保险丝Claude Code 是 Anthropic 推出的终端 AI 编码助手能直接读写项目文件、执行 shell 命令、跑测试、改配置。它适合所有在本地做开发的工程师尤其是让 AI 深度参与重构、批量改文件、自动化脚本的场景。但正因为权限大一旦它理解偏了后果可能是删掉.env、改坏认证中间件、往生产依赖里塞包。Claude Code Hooks 就是解决这个问题的机制你可以在settings.json里定义规则让 Claude Code 在特定时机自动执行你指定的脚本实现改文件前拦截、改文件后格式化、任务完成发通知等自动化动作。我试过让 Claude Code 帮忙改一段 API 认证逻辑它顺手把.env里的数据库密码清空了部署后服务全挂排查到凌晨才发现。从那之后我开始认真研究 Hooks把 PreToolUse 和 PostToolUse 两类钩子跑通现在关键文件再没被 AI 碰过。这篇教程会从settings.json骨架配置讲起覆盖两类钩子的注册方式、触发时机、源码示例以及逐步验证钩子是否生效的操作动作配置片段可以直接复制。Hooks 的核心价值在于它把AI 自主操作变成AI 操作 你的规则校验。PreToolUse 是唯一能拦截动作的钩子脚本返回非零退出码就能拒绝执行PostToolUse 只能做事后处理比如格式化、记录日志。理解这个区别后面的配置就不会乱。2. 前置准备版本、目录与 TaoToken 接入在写 Hooks 之前先把环境和模型接入搞定。Claude Code 需要 2026.1 以上版本才支持完整的 Hooks 系统先确认版本claude --version # 需要 2026.1 以上版本不够就更新npm install -g anthropic-ai/claude-codelatest然后在项目根目录创建配置目录和文件mkdir -p .claude/hooks touch .claude/settings.json chmod x .claude/hooks/*.sh模型接入方面如果你希望统一管理 API Key、方便切换模型、查看调用日志可以用 TaoToken 做接入层。它的 API 地址是https://taotoken.net/api兼容 Anthropic 风格的调用方式。先去控制台创建 API Key控制台入口https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code_hooks_console创建好 Key 之后在 Claude Code 里配置环境变量指向 TaoToken 的 API 地址即可。如果你还没决定用哪个模型可以先去模型对话页面体验一下模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code_hooks_chat对于长期用 Claude Code 做编码、跑 Agent 任务的场景Coding Plan 会更划算额度稳定适合每天都要跑 Hooks 校验的重度用户Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code_hooks_planAPI Key 管理页面在这里可以随时创建、吊销、查看用量API Keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code_hooks_keys接入文档里有完整的参数说明和示例配置遇到问题可以先翻一遍接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code_hooks_doc环境准备好之后就可以开始写 Hooks 了。下面所有配置都基于项目根目录的.claude/settings.json脚本放在.claude/hooks/下。3. settings.json 骨架与 PreToolUse 拦截配置settings.json的 Hooks 结构是事件名 → 匹配器数组 → 钩子数组。先看最小骨架{ hooks: { PreToolUse: [ { matcher: Edit|Write|MultiEdit, hooks: [ { type: command, command: bash .claude/hooks/block-critical.sh } ] } ] } }几个关键点必须说清楚。matcher是正则表达式匹配的是工具名不是文件名。Edit|Write|MultiEdit覆盖所有文件编辑操作Bash匹配命令执行。别写成Edit,Write那是错的。type目前支持command、prompt、agent三种日常拦截用command就够。PreToolUse 的拦截逻辑靠退出码脚本exit 0放行exit 2拦截并把 stderr 内容反馈给 Claude其他非零码也会拦截但提示方式不同。这是它和 PostToolUse 最大的区别——只有 PreToolUse 能真正阻止动作。下面写一个保护关键文件的脚本.claude/hooks/block-critical.sh#!/bin/bash # 从 stdin 读取 Claude 传来的 JSON INPUT$(cat) FILE_PATH$(echo $INPUT | jq -r .tool_input.file_path // empty) BLOCKED_FILES( .env .env.production src/middleware.ts src/app/api/auth/route.ts docker-compose.yml ) for file in ${BLOCKED_FILES[]}; do if [[ $FILE_PATH *$file* ]]; then echo BLOCKED: $FILE_PATH 是受保护文件需要手动编辑 2 exit 2 fi done exit 0注意这里没有用set -e。Claude Code 靠退出码判断结果set -e会让一些预期内的失败变成意外退出反而干扰判断。脚本里用jq提取tool_input.file_path这是 Claude 通过 stdin 传进来的 JSON 结构。如果文件路径命中黑名单就往 stderr 写提示并exit 2。再写一个拦截危险命令的脚本.claude/hooks/block-dangerous-cmd.sh#!/bin/bash INPUT$(cat) CMD$(echo $INPUT | jq -r .tool_input.command // empty) DANGEROUS_PATTERNS( rm -rf / rm -rf ~ DROP TABLE DROP DATABASE truncate ) for pattern in ${DANGEROUS_PATTERNS[]}; do if [[ $CMD *$pattern* ]]; then echo DANGER: 检测到危险命令 $CMD已拦截 2 exit 2 fi done # 拦截往生产依赖装包 if echo $CMD | grep -qE npm install|yarn add|pnpm add; then if ! echo $CMD | grep -qE \-\-save-dev|-D; then echo 拦截: 往生产依赖装包需要人工确认加 --save-dev 可绕过 2 exit 2 fi fi exit 0把两个脚本注册到settings.json{ hooks: { PreToolUse: [ { matcher: Edit|Write|MultiEdit, hooks: [ { type: command, command: bash .claude/hooks/block-critical.sh } ] }, { matcher: Bash, hooks: [ { type: command, command: bash .claude/hooks/block-dangerous-cmd.sh } ] } ] } }到这里 PreToolUse 的拦截就配好了。它的触发时机是 Claude 决定调用某个工具、但还没真正执行之前。你可以把它理解成门卫Claude 递上申请单stdin JSON门卫检查不合格就退回。4. PostToolUse 格式化与 Stop 通知配置PostToolUse 在工具执行之后触发不能拦截但适合做后续处理。最常见的用法是改完代码自动格式化、跑 lint、记录日志。先看格式化配置。前端项目用 prettier eslint{ hooks: { PostToolUse: [ { matcher: Edit|Write|MultiEdit, hooks: [ { type: command, command: npx prettier --write \$CLAUDE_FILE_PATH\ npx eslint --fix \$CLAUDE_FILE_PATH\ 2/dev/null || true } ] } ] } }Python 项目换成 black isort{ hooks: { PostToolUse: [ { matcher: Edit|Write|MultiEdit, hooks: [ { type: command, command: black \$CLAUDE_FILE_PATH\ isort \$CLAUDE_FILE_PATH\ 2/dev/null || true } ] } ] } }这里2/dev/null || true很关键。PostToolUse 的钩子如果失败默认会中断 Claude 的后续操作加上这两个可以确保格式化失败也不影响正常流程。$CLAUDE_FILE_PATH是 Claude Code 注入的环境变量指向当前操作的文件路径。再配一个 Stop 钩子任务完成时发桌面通知。macOS 用 osascript{ hooks: { Stop: [ { hooks: [ { type: command, command: osascript -e display notification \Claude Code 任务完成\ with title \提示\ sound name \default\ 2/dev/null || true } ] } ] } }Linux 用 notify-send{ hooks: { Stop: [ { hooks: [ { type: command, command: notify-send Claude Code 任务完成可以回来看了 2/dev/null || true } ] } ] } }Stop 的触发时机是 Claude 完成整轮回答之后适合跑长任务时用。它不能拦截只能做事后动作。把 PreToolUse、PostToolUse、Stop 合到一起就是一份完整的settings.json{ hooks: { PreToolUse: [ { matcher: Edit|Write|MultiEdit, hooks: [ { type: command, command: bash .claude/hooks/block-critical.sh } ] }, { matcher: Bash, hooks: [ { type: command, command: bash .claude/hooks/block-dangerous-cmd.sh } ] } ], PostToolUse: [ { matcher: Edit|Write|MultiEdit, hooks: [ { type: command, command: npx prettier --write \$CLAUDE_FILE_PATH\ 2/dev/null || true } ] } ], Stop: [ { hooks: [ { type: command, command: osascript -e display notification \Claude 干完活了\ with title \Claude Code\ 2/dev/null || true } ] } ] } }5. 验证钩子是否生效逐步操作与成功结果配置写完不代表生效必须逐步验证。下面是我实测的验证流程每一步都有明确的预期结果。第一步验证 JSON 格式。用 jq 跑一遍没报错说明格式正确jq . .claude/settings.json如果输出格式化后的 JSON说明语法没问题。报错就按提示改常见错误是多了逗号、少了引号。第二步验证脚本可执行。手动跑一次拦截脚本模拟 Claude 传来的 JSONecho {tool_input:{file_path:.env}} | bash .claude/hooks/block-critical.sh echo 退出码: $?预期结果是输出BLOCKED: .env 是受保护文件需要手动编辑退出码为 2。如果退出码是 0说明匹配逻辑有问题检查BLOCKED_FILES数组和FILE_PATH提取。第三步验证放行逻辑。换一个不在黑名单里的文件echo {tool_input:{file_path:src/utils/helper.ts}} | bash .claude/hooks/block-critical.sh echo 退出码: $?预期退出码为 0无输出。这一步确认脚本不会误伤正常文件。第四步在 Claude Code 里实测。启动 Claude Code让它改一个受保护文件claude # 在对话里输入帮我把 .env 里的 DEBUG 改成 true预期结果是 Claude 收到拦截反馈回复类似操作被 hook 拦截.env是受保护文件。如果 Claude 真的改了文件说明 hook 没生效回到第二步检查脚本和settings.json路径。第五步验证 PostToolUse。让 Claude 改一个普通文件观察是否自动格式化# 在 Claude Code 里输入帮我在 src/utils/helper.ts 里加一个空函数改完后用git diff看文件如果格式被 prettier 统一过缩进、引号、分号说明 PostToolUse 生效。第六步验证 Stop 通知。让 Claude 跑一个稍长的任务任务结束时应该弹出桌面通知。如果没弹检查osascript或notify-send是否安装以及命令里的引号转义是否正确。六步走完PreToolUse、PostToolUse、Stop 三类钩子都验证过了。任何一步不符合预期就回到对应脚本单独调试别急着改settings.json。6. 常见报错与排查清单Hooks 配置过程中最容易踩的坑集中在路径、权限、JSON 格式、退出码这几类。下面按报错现象整理排查清单。现象一配置了 hook 但完全没反应。先确认settings.json的位置。它必须在项目根目录的.claude/settings.json不是用户目录也不是.claude.json。然后确认脚本有执行权限chmod x .claude/hooks/*.sh ls -l .claude/hooks/输出里应该有-rwxr-xr-x。没有 x 就说明权限没加上。现象二hook 脚本报jq: command not found。说明系统没装 jq。macOS 用brew install jqUbuntu 用sudo apt install jq。不想装 jq 也可以用 Python 解析FILE_PATH$(echo $INPUT | python3 -c import sys,json; print(json.load(sys.stdin).get(tool_input,{}).get(file_path,)))现象三matcher 不生效。检查是不是把正则写成了 glob。Edit|Write是对的Edit,Write是错的*.ts也是错的。matcher 匹配的是工具名不是文件名。工具名只有Edit、Write、MultiEdit、Bash、Read这些。现象四PostToolUse 导致 Claude 卡住。大概率是钩子里跑了全量测试或全量 lint。PostToolUse 是同步的Claude 会等钩子跑完才继续。只对单个文件做轻量操作别跑npm test这种全量命令。加2/dev/null || true也能避免失败中断。现象五PreToolUse 拦截了但 Claude 没收到提示。检查提示是不是写到了 stdout。拦截提示要写到 stderr用echo ... 2。写到 stdout 的话 Claude 可能读不到。现象六Windows 下脚本不执行。Windows 用户注意 command 里的路径分隔符用/不要用\。另外 bash 脚本需要 Git Bash 或 WSL 环境纯 CMD 跑不了。现象七prompt 类型 hook 延迟太高。prompt 类型会多调一次 API通常多 2-5 秒。只在认证、支付、数据库这类关键操作上用日常编辑用 command 类型就够。现象八hook 脚本里用了set -e导致意外退出。去掉set -e。Claude Code 靠退出码判断结果set -e会让预期内的失败变成意外退出干扰判断。排查顺序建议先jq . .claude/settings.json验证格式再手动跑脚本验证逻辑最后在 Claude Code 里实测。三层都过了基本不会出问题。7. 下一步把 Hooks 接进你的日常编码流Hooks 跑通之后可以按场景逐步扩展。日常编辑用 command 类型的 PreToolUse 做文件保护PostToolUse 做格式化关键模块认证、支付、数据库迁移加 prompt 类型做语义审查长任务用 Stop 发通知。三种类型配合覆盖大部分自动化需求。如果你还在用默认的模型接入方式建议把 API Key 统一到 TaoToken 管理方便查看调用日志、切换模型、控制额度。接入文档里有完整的配置示例接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code_hooks_doc_cta长期跑 Claude Code 做编码和 Agent 任务的话Coding Plan 的额度更稳定适合每天都要跑 Hooks 校验的场景Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code_hooks_plan_cta最后提醒一句Hooks 目前还在快速迭代新事件类型和配置项会陆续加。建议每隔一两周翻一下官方文档的更新日志看看有没有新钩子可以用。配置本身不复杂难的是想清楚哪些操作必须拦、哪些操作可以放这个边界定好了Hooks 才真正帮你省心。
