Claude Code Hooks——自动化你写代码时的那些重复动作
Claude Code Hooks——自动化你写代码时的那些重复动作
适读人群:每天用 Claude Code 做开发、想减少重复操作的工程师 | 阅读时长:约13分钟 | 核心价值:用真实配置案例告诉你 Hooks 能做什么,哪些配置真正提升了效率
有一天我意识到,我和 Claude Code 一起工作的过程里,有很多动作是固定模式的:
Claude 写完一段代码,我去跑测试,测试结果反馈给 Claude,Claude 再修。
Claude 修改了文件,我手动格式化一下,然后继续。
Claude 完成了一个任务,我去看看 git diff,决定要不要提交。
这些动作不复杂,但都是我手动触发的。每次都要我主动去做。
后来我发现 Claude Code 有一个 Hooks 系统,可以在特定事件发生的时候自动执行命令。我配置了一批,现在很多之前需要手动的操作变成了自动的。
这篇文章讲我实际配置了哪些 hooks,以及用下来真实的体验。
Hooks 是什么机制
Claude Code 的 Hooks 是配置在 settings.json 里的规则,基本格式是:
当 [某个事件发生] 的时候,执行 [某个命令]支持的事件类型有几个核心的:
PreToolUse:Claude 即将调用某个工具之前PostToolUse:Claude 调用工具之后Notification:Claude 发出通知时Stop:Claude 完成响应停下来时
配置文件位置:~/.claude/settings.json(全局)或者项目目录下的 .claude/settings.json(项目级)。
我的完整 Hooks 配置
先把我的配置文件放出来,然后逐个解释:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash -c 'cd \"$CLAUDE_PROJECT_DIR\" && if [ -f package.json ]; then npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\" 2>/dev/null; elif [ -f pom.xml ] || [ -f build.gradle ]; then echo \"Java project, skip prettier\"; fi'"
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'if echo \"$CLAUDE_TOOL_INPUT_COMMAND\" | grep -q \"^mvn\\|^gradle\\|^npm test\\|^pytest\"; then echo \"[Hook] 测试命令执行完毕,结果已记录\" >> /tmp/claude_test_log.txt; echo \"$(date): $CLAUDE_TOOL_OUTPUT\" >> /tmp/claude_test_log.txt; fi'"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'DANGEROUS_PATTERNS=\"rm -rf|DROP TABLE|DELETE FROM.*WHERE 1|truncate\"; if echo \"$CLAUDE_TOOL_INPUT_COMMAND\" | grep -iE \"$DANGEROUS_PATTERNS\" > /dev/null 2>&1; then echo \"[安全检查] 检测到高危命令,请确认\"; fi'"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'cd \"$CLAUDE_PROJECT_DIR\" && if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then CHANGES=$(git diff --stat 2>/dev/null); if [ -n \"$CHANGES\" ]; then echo \"[Git] 未提交变更:\" && git diff --stat; fi; fi'"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'if command -v osascript > /dev/null 2>&1; then osascript -e \"display notification \\\"$CLAUDE_NOTIFICATION\\\" with title \\\"Claude Code\\\"\"; fi'"
}
]
}
]
}
}逐个解释每个 Hook
1. 文件修改后自动格式化
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash -c 'cd \"$CLAUDE_PROJECT_DIR\" && if [ -f package.json ]; then npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\" 2>/dev/null; fi'"
}
]
}这个 hook 在 Claude 写入或编辑文件之后触发,自动运行 Prettier 格式化。
触发条件是 Write|Edit|MultiEdit,覆盖了所有文件写入的工具调用类型。
实际效果:Claude 写完代码之后,我不需要手动格式化了。特别是在 Claude 生成大段代码的时候,缩进和格式有时候不太对,这个 hook 在 Claude 写完文件之后立即自动修正。
这个 hook 的一个细节:我加了 if [ -f package.json ] 的判断,只在前端/Node 项目里运行 Prettier,Java 项目里不运行(Java 用的是 Google Java Format,需要单独处理)。
2. 测试命令结果记录
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'if echo \"$CLAUDE_TOOL_INPUT_COMMAND\" | grep -q \"^mvn\\|^npm test\\|^pytest\"; then echo \"$(date): $CLAUDE_TOOL_OUTPUT\" >> /tmp/claude_test_log.txt; fi'"
}
]
}这个 hook 在 Claude 执行了测试命令(mvn、npm test、pytest)之后,把结果记录到一个临时文件里。
原因:有时候 Claude 跑了多轮测试,每次结果在对话里刷上去,很难回看哪轮通过了哪轮没过。有了这个日志文件,我可以随时 cat /tmp/claude_test_log.txt 看全部的测试历史。
3. 高危命令预警
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'DANGEROUS_PATTERNS=\"rm -rf|DROP TABLE|DELETE FROM.*WHERE 1|truncate\"; if echo \"$CLAUDE_TOOL_INPUT_COMMAND\" | grep -iE \"$DANGEROUS_PATTERNS\" > /dev/null 2>&1; then echo \"[安全检查] 检测到高危命令,请确认\"; fi'"
}
]
}这个 hook 在 Claude 执行命令之前触发,检查命令里是否包含高危操作模式。
我加了 rm -rf、DROP TABLE、DELETE FROM...WHERE 1=1、TRUNCATE 这几个模式。
注意:这个 hook 是 PreToolUse,在命令执行之前触发。它目前只是输出警告,不会阻止命令执行。如果你想阻止,需要把 hook 的返回值设为非零退出码。
这个 hook 我踩过一个坑。
有一次 Claude 在做清理脚本,命令里有 rm -rf ./target(Maven 的 target 目录),这是正常的。但 hook 检测到了 rm -rf,输出了警告,Claude 看到警告之后有点困惑,开始解释它为什么要 rm,然后改用了更安全的方式。这个行为有点出乎意料,但也不是坏事。
后来我把这个 hook 改成了只在 rm -rf / 或者 rm -rf ~ 这种真正危险的模式才触发,而不是所有 rm -rf。
4. 任务完成后显示 Git 状态
{
"hooks": [
{
"type": "command",
"command": "bash -c 'cd \"$CLAUDE_PROJECT_DIR\" && if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then CHANGES=$(git diff --stat 2>/dev/null); if [ -n \"$CHANGES\" ]; then echo \"[Git] 未提交变更:\" && git diff --stat; fi; fi'"
}
]
}这个 hook 在 Claude 每次停止响应时触发,如果当前目录是 git 仓库,而且有未提交的变更,就显示 git diff --stat。
这个 hook 给我一个习惯性的提醒:Claude 帮我做完了什么,我现在有哪些文件改了。不是让 Claude 自动提交,只是提醒我。
实际使用中,这个 hook 让我减少了"以为自己提交了,其实没提交"的情况。
5. macOS 系统通知
{
"hooks": [
{
"type": "command",
"command": "bash -c 'if command -v osascript > /dev/null 2>&1; then osascript -e \"display notification \\\"$CLAUDE_NOTIFICATION\\\" with title \\\"Claude Code\\\"\"; fi'"
}
]
}这个用 macOS 的 AppleScript 发系统通知。
场景是:我让 Claude 做一个比较长的任务(比如重构一批文件),然后我去做别的事,等 Claude 做完了用系统通知提醒我。不用一直盯着终端。
这个 hook 是最轻量但实际很实用的一个。
几个写 Hook 的实践经验
环境变量要了解清楚
Claude Code 在执行 hook 时会注入几个环境变量:
$CLAUDE_PROJECT_DIR:当前项目目录$CLAUDE_TOOL_INPUT_FILE_PATH:被操作的文件路径(Write/Edit 触发时)$CLAUDE_TOOL_INPUT_COMMAND:执行的命令(Bash 触发时)$CLAUDE_TOOL_OUTPUT:工具的输出结果$CLAUDE_NOTIFICATION:通知内容(Notification 触发时)
不同的事件类型,可用的环境变量不一样,要对着文档看。
Hook 命令的失败不会影响 Claude 的行为
如果你的 hook 命令报错了,Claude 不会停下来,它只是会看到 hook 的输出(包括错误信息)。所以 hook 里要做好错误处理,不要因为 hook 的错误输出让 Claude 误解情况。
Hook 会增加每次响应的延迟
特别是 PostToolUse 里的 hook,如果你的命令执行时间长(比如格式化一大批文件),Claude 下一步要等 hook 完成之后才继续。控制 hook 命令的执行时间。
项目级 Hook 优于全局 Hook
不同项目的需求不一样。建议把项目特定的 hook(比如格式化命令)放在项目的 .claude/settings.json 里,而不是全局配置。
哪些 Hook 真的有价值
用了几个月,有几个 hook 是我每天都在用的,有几个装了基本没感觉。
最有价值的:
- 任务完成后显示 Git 状态——实际降低了遗漏提交的频率
- macOS 系统通知——让长任务不需要盯着终端
- 文件修改后自动格式化——消除了一个手动步骤
价值一般的:
- 测试结果记录——有用,但查看日志文件的频率不高
- 高危命令预警——有安全价值,但触发场景很少
Hooks 系统的价值在于:你可以把那些你每次都要手动做、但又不应该花脑子在上面的事情自动化掉。找到你工作流里真正重复的动作,做成 hook,才有意义。
