本文根据 Claude Code 官方文档《Automate actions with hooks》《Hooks reference》《Best practices for Claude Code》《Tools reference》整理,资料核对于 2026-11-05。配图为本站整理的示意图,不是界面截图。本站(bigolab.com)不是 Anthropic 官方网站。
适用于谁
- 已经知道 Hooks 是什么,想要一个能直接套用的实战例子的人;
- 总担心 Claude 没跑测试就提交了的人;
- 配了钩子却发现没生效、不知从哪查的人。
钩子的基础概念和事件列表,站内已有 /guides/claude-code-hooks,本文只做一个例子:提交前自动跑测试。
结论先说
- 用
PreToolUse事件。 官方说它在工具调用执行之前触发,可以阻止。Claude 要运行git commit的那条 Bash 命令,就是被拦的对象。 - 阻止的办法是退出码 2。 官方:脚本往 stderr 写原因并以退出码 2 结束,Claude Code 会取消这次调用,并把 stderr 的内容作为反馈交给 Claude,让它调整做法。
- 退出码 0 不等于批准,只是「没有意见」,调用仍走正常的权限流程。
- 别的退出码(比如 1)一般不会拦截,只是个非阻塞的错误提示,操作照常进行。所以脚本里测试失败时必须明确写
exit 2,不要让脚本靠测试命令自己的退出码「碰巧」失败。 - 用
if字段缩小范围:官方的if用权限规则语法,只有命令匹配时才启动钩子进程,不用每条 Bash 命令都跑一遍。

图:本站根据 Claude Code 官方文档:Hooks 整理的示意图
第一步:写脚本
在项目里建 .claude/hooks/test-before-commit.sh。思路沿用官方「保护文件」例子的结构:从标准输入读 JSON,取出 tool_input.command,决定放行还是拦截。
bash
#!/bin/bash
# .claude/hooks/test-before-commit.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# 只在命令里确实有 git commit 时才跑测试
case "$COMMAND" in
*"git commit"*) ;;
*) exit 0 ;;
esac
cd "$CLAUDE_PROJECT_DIR" || exit 2
OUTPUT=$(npm test 2>&1)
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "测试没有通过,已阻止这次提交。请先修复失败的测试再提交。测试输出的最后 40 行:" >&2
echo "$OUTPUT" | tail -n 40 >&2
exit 2
fi
exit 0
说明:
- 官方的 Bash 钩子输入里,
tool_input.command就是 Claude 要运行的命令;用jq取出来是官方示例的做法。如果你的机器没有jq,官方排错里说可以安装它,或用 Python、Node.js 解析 JSON。 npm test换成你项目的测试命令。想只跑快的那部分测试,用你项目里对应的命令。- 在 macOS 和 Linux 上,官方说钩子脚本必须有可执行权限:
chmod +x .claude/hooks/test-before-commit.sh。 - 测试输出只回传最后 40 行,避免把大量日志灌进上下文,这是本站的做法。
第二步:注册钩子
在项目的 .claude/settings.json 里加入(提交进仓库就是全队共享;只想自己用,放 .claude/settings.local.json):
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|PowerShell",
"hooks": [
{
"type": "command",
"if": "Bash(git commit *)",
"timeout": 300,
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/test-before-commit.sh"
},
{
"type": "command",
"if": "PowerShell(git commit *)",
"timeout": 300,
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/test-before-commit.sh"
}
]
}
]
}
}
对照官方文档:
matcher区分大小写,按工具名匹配;Bash|PowerShell是官方在讲 PowerShell 工具时给的写法,因为 Windows 上 Claude 可能走 PowerShell 工具而不是 Bash 工具,只匹配Bash是不够的。- 官方说,要匹配多个工具名,可以分成几个处理器,各带自己的
if,所以这里写了两条。 timeout单位是秒;官方说命令类钩子默认是 10 分钟。测试跑得久就按需调大,太小会被取消。官方说多数事件里超时的钩子不产生任何决定,也就是说超时不会拦截,提交会照常进行,所以别把超时设得比测试耗时还短。"$CLAUDE_PROJECT_DIR"/.claude/hooks/...是官方「保护文件」例子里引用脚本的写法。
第三步:不靠 Claude,自己先测脚本
官方排错里给了办法:用管道把一段示例 JSON 喂给脚本,再看退出码。
bash
# 不是提交命令:应当直接放行,退出码 0
echo '{"tool_name":"Bash","tool_input":{"command":"git status"}}' | .claude/hooks/test-before-commit.sh; echo $?
# 是提交命令:测试通过返回 0,不通过返回 2
echo '{"tool_name":"Bash","tool_input":{"command":"git commit -m test"}}' | .claude/hooks/test-before-commit.sh; echo $?
故意把一个测试改坏,再跑第二条,应该看到失败输出和退出码 2;改回来后应该是 0。
第四步:在 Claude Code 里验证
- 输入
/hooks,确认PreToolUse下有这个钩子。官方说如果刚改了设置文件、几秒后还没出现,重启会话。 - 故意让测试失败,然后说:「请提交当前改动,提交信息写 test」。
- 按
Ctrl+O打开会话记录,看钩子的结果。官方说被阻止时通常会看到随阻止一起给出的信息。 - 再让它修好测试后重新提交,这次应当放行。
提示词 1:让 Claude 帮你写钩子
text
请为这个项目写一个 Claude Code 钩子:每次 Claude 要执行 git commit 前,先运行【npm test】;测试失败就阻止提交,并把输出的最后 40 行作为原因反馈给 Claude。
要求:1)用 PreToolUse,只针对 git commit 命令;2)脚本放 .claude/hooks/,注册到 .claude/settings.json;3)测试失败必须明确 exit 2;4)写完后用管道喂 JSON 的方法测试放行和拦截两种情况,把结果给我。
写完用 /hooks 让我确认。
提示词 2:被拦截之后
text
刚才提交被钩子拦下了,请读钩子给出的测试输出,找出失败原因并修复。
修好后重新运行测试,通过了再提交。不要为了让测试通过而删除测试或修改它的预期结果,除非先告诉我原因。
提示词 3:让 Claude 审查钩子的漏洞
text
请审查 .claude/hooks/test-before-commit.sh 和 .claude/settings.json 里的钩子配置,列出它拦不住或会误拦的情况,例如:
1)git commit --amend、git commit -a、先 git add 再 && git commit 的组合命令;
2)git -C 某目录 commit 这类带参数的写法;
3)测试命令不存在、测试超时、jq 没安装时脚本会怎样(是放行还是拦截);
4)在 Windows 上由 PowerShell 工具发起的提交能否被拦住。
对每一项说明现状和建议的改法,先只报告,不要修改文件。

图:本站根据官方文档整理的示意图
排错对照(官方「Limitations and troubleshooting」)
- 钩子没有触发:
/hooks里是否在PreToolUse下;matcher 是否完全匹配工具名(区分大小写);事件类型对不对,PreToolUse在执行前、PostToolUse在执行后,而后者无法撤销已发生的动作。 - 看到
PreToolUse hook error:脚本意外返回了非零退出码。用上面的管道办法手动测试,看是不是jq没装、路径找不到,或脚本没有可执行权限。 - 想看细节:官方说用
claude --debug启动,或会话中运行/debug打开日志。
Windows 的两处坑
- 需要有 Bash 来运行这个脚本。官方说,Windows 上没有 Git Bash 时,shell 形式的钩子默认用 PowerShell 运行,Bash 写的脚本可能失败;安装 Git for Windows,或用
CLAUDE_CODE_GIT_BASH_PATH指向 Git Bash,然后开新会话。另外官方还提醒:Windows 没有 Git Bash 时,PreToolUse的钩子失败或超时可能直接阻止工具调用,错误信息里会写着blocking because Git Bash wasn't found。 - 路径带反斜杠。官方说,Windows 上文件工具传给钩子的路径是反斜杠,用正斜杠写的比较永远匹配不上,钩子会「什么也没拦」。本例只看命令文本,不受影响;如果你改写成按路径拦截,务必先把反斜杠换成正斜杠。
更多 Windows 上的使用细节见 /guides/claude-code-windows-pitfalls-paths-git-bash-powershell。
另一种思路:Stop 钩子
官方最佳实践里有一个相近的例子:Stop 钩子在 Claude 想结束回复时运行检查,退出码 2 会让它回去继续干活,官方示例写的是 pnpm tsc --noEmit 1>&2 || exit 2。把测试命令放进去,就是「每次回复结束前测试必须通过」。注意官方说:连续阻止八次、中间没有工具调用时,Claude Code 会放行;脚本里应读取输入里的 stop_hook_active 字段,已在续跑时提前退出。两种方式可以按需选:想在「提交」这个节点把关用本文的方法,想要「每一轮结束都验证」用 Stop。
它拦不住什么
PreToolUse 钩子只管 Claude 发起的命令。你自己在终端里手敲的 git commit、别的编辑器里的提交,都不经过它。想对所有提交生效,要用 Git 自带的 pre-commit 钩子,那是 Git 的机制,不属于 Claude Code。另外官方明确说 if 过滤是尽力而为的,要硬性禁止某类命令,应该用权限规则,见 /guides/claude-code-permission-modes。
订阅和额度
钩子本身不占额度,但每次被拦截后 Claude 会读测试输出并重试,测试输出越长越占上下文,所以只回传末尾几十行。订阅选择见 /chongzhi/claude-code,价格和额度以官方页面当前显示为准。
相关教程
- /guides/claude-code-hooks:Hooks 钩子基础
- /guides/claude-code-custom-skill-slash-command-example:自定义 Skill 实战
- /guides/claude-code-git-commit-pr-merge-conflict:提交、PR 与合并冲突
参考资料
- Claude Code 文档:Automate actions with hooks — https://code.claude.com/docs/en/hooks-guide
- Claude Code 文档:Hooks reference — https://code.claude.com/docs/en/hooks
- Claude Code 文档:Best practices for Claude Code — https://code.claude.com/docs/en/best-practices
- Claude Code 文档:Tools reference — https://code.claude.com/docs/en/tools-reference
0 条评论
还没有评论,来抢沙发~