Claude Code Hooks 实战「提交前自动跑测试」:PreToolUse 拦住 git commit,测试不过就退出码 2,附 Windows 注意点

只写一个完整例子:让 Claude Code 每次要执行 git commit 之前,先自动跑项目测试,没通过就拦下并把输出交给它修。本文按官方 Hooks 文档给出脚本、settings.json 配置、测试方法、排错和 Windows 的两处坑。

NNathaniel bigo··原创首发·AI 辅助撰写
10 分钟读完
资料核对于 2026-11-05 · 依据官方文档与公开资料整理 Pro 账号
本文根据 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,本文只做一个例子:提交前自动跑测试。

结论先说

  1. 用 PreToolUse 事件。 官方说它在工具调用执行之前触发,可以阻止。Claude 要运行 git commit 的那条 Bash 命令,就是被拦的对象。
  2. 阻止的办法是退出码 2。 官方:脚本往 stderr 写原因并以退出码 2 结束,Claude Code 会取消这次调用,并把 stderr 的内容作为反馈交给 Claude,让它调整做法。
  3. 退出码 0 不等于批准,只是「没有意见」,调用仍走正常的权限流程。
  4. 别的退出码(比如 1)一般不会拦截,只是个非阻塞的错误提示,操作照常进行。所以脚本里测试失败时必须明确写 exit 2,不要让脚本靠测试命令自己的退出码「碰巧」失败。
  5. 用 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 里验证

  1. 输入 /hooks,确认 PreToolUse 下有这个钩子。官方说如果刚改了设置文件、几秒后还没出现,重启会话。
  2. 故意让测试失败,然后说:「请提交当前改动,提交信息写 test」。
  3. 按 Ctrl+O 打开会话记录,看钩子的结果。官方说被阻止时通常会看到随阻止一起给出的信息。
  4. 再让它修好测试后重新提交,这次应当放行。

提示词 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,价格和额度以官方页面当前显示为准。

相关教程

参考资料

  • 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

Nathaniel 的更多内容

  1. 01

    Claude Code 在 Windows 上的常见坑:Git Bash 还是 PowerShell、路径反斜杠、假报错、WSL 怎么选

    Windows 上用 Claude Code,装好之后最容易踩的是:用哪个 shell、路径写法不一致、grep 没匹配就报错、钩子没生效、WSL 和原生怎么选。本文逐条按官方文档整理原因和处理办法,附三段提示词和一张对照表。

    Claude0
  2. 02

    Claude Code 自定义斜杠命令与 Skill 实战:从零写一个「生成更新日志」的 /release-notes,含传参、预执行命令和调试

    概念看过了,还是不知道从哪下手?本文按 Claude Code 官方「Extend Claude with skills」写一个完整可用的例子:用 /release-notes 根据 git 记录生成更新日志,覆盖目录、frontmatter、传参、预执行命令、预授权和调试。

    Claude0
  3. 03

    Claude Code 部署网站:先写 Dockerfile 和 compose 在本机跑通,再按清单上云服务器,别让它直接改线上

    想让 Claude Code 帮你把网站部署到云服务器。本文只写通用做法,按 Docker 官方文档讲:让它写 Dockerfile、.dockerignore 和 compose,在本机跑通,端口和数据卷怎么设,再给出上线清单,并说明为什么不要让它直接动线上机器。

    Claude0
  4. 04

    Claude Code 写爬虫与数据清洗:先查 robots.txt 和网站条款,控制频率,原始数据留一份再清洗

    想让 Claude Code 写爬虫抓数据再清洗。本文先讲合规:robots.txt 不是授权也不是安全措施、网站条款要自己读;再讲怎么用 Python 标准库检查规则、小样本先试、保存原始数据,以及用 pandas 清洗,附四段提示词。

    Claude0
  5. 05

    Claude Code 写 Chrome 扩展:按 Manifest V3 起步,加载未打包的扩展,改了哪些文件要重新加载

    想让 Claude Code 帮你写一个 Chrome 扩展。本文按 Chrome 官方扩展文档讲:manifest.json 的最小写法、在 chrome://extensions 加载未打包的扩展、哪些改动要刷新、怎么看控制台和报错,再给四段提示词,以及权限要尽量少。

    Claude0
  6. 06

    Claude Code 写安卓 App:Android Studio 建项目、让它用 gradlew 编译安装,报错和日志怎么喂给它

    想让 Claude Code 帮你写安卓 App。官方文档没有专门的安卓教程,本文把 Claude Code 官方工作流和安卓官方命令行文档拼在一起:Android Studio 建项目、gradlew 编译安装、模拟器和真机、测试与日志,附四段提示词。

    Claude0

同产品的其他教程

  1. 01

    Claude Code 用数据库 MCP 查数据、出报表:只读账号、先看口径再看数字,结果别一次拉太多行

    想用自然语言让 Claude Code 查数据库、出统计报表,而不是写后端接口。本文按官方 MCP 页面和 DBHub 说明讲:怎么连 PostgreSQL、MySQL、SQLite、输出太大怎么办、怎么让它先说明口径、怎么核对数字,附三段提示词。

    Claude0
  2. 02

    Claude Code 配 Figma MCP 还原设计稿:插件或一条命令安装,复制图层链接,小块小块地让它出代码

    想让 Claude Code 照 Figma 设计稿写前端代码。本文按 Figma 官方开发者文档讲:远程服务器地址、插件和手动两种安装、怎么复制图层链接、为什么要选小块、座位类型与调用次数限制,并给三段还原设计稿的提示词。

    Claude0
  3. 03

    Claude Code 管理 GitHub issue:用 gh 命令还是 GitHub MCP,怎么装、怎么限权、怎么让它分拣和回复 issue

    让 Claude Code 读 issue、分拣标签、起草回复、关联 PR,有两条路:装好 gh 命令行直接用,或连 GitHub 官方 MCP 服务器。本文按两边官方文档讲安装命令、令牌权限、只读模式,并给出三段分拣提示词和限权设置。

    Claude0
  4. 04

    Claude Code 配 Playwright MCP 做浏览器测试:一条命令安装,让它打开页面、点按钮、读控制台,再把流程写成测试

    想让 Claude Code 自己打开浏览器测试你的网页。本文按微软 Playwright MCP 官方仓库和 Claude Code 官方 MCP 文档讲:安装命令、怎么确认连上、有头无头与隔离参数、一次测试的提示词,以及它不是安全边界这件事。

    Claude0
  5. 05

    Claude Code 写文档、README 和代码注释:先让它读代码再动笔,指定风格,写完逐条核对命令能不能跑

    想让 Claude Code 给项目补 README、接口文档和注释,又怕它编造不存在的命令和参数。本文按官方「Handle documentation」工作流讲:先找没有文档的代码、指定风格、写完核对,附四段可复制提示词和验证办法。

    Claude0
  6. 06

    用 Claude Code 整理电脑文件、批量重命名、处理表格:先出清单再执行,副本操作和权限设置

    Claude Code 不只能写代码,官方说它能在任何文件夹里搜索、编辑和整理内容。本文讲非程序员怎么用它整理下载文件夹、批量重命名、处理表格:先出整理清单、用副本操作、权限怎么设,附四段提示词和撤销办法。

    Claude0

0 条评论

登录 后参与评论

还没有评论,来抢沙发~