Claude Code Hooks 实战:5个配置让AI编程助手自动守规矩
用 Claude Code 写代码的人越来越多了,但有个问题一直在:Claude 改完文件,你得手动跑 prettier;Claude 跑了个 rm 命令,你心里一紧;Claude 干完活了,你还得自己切到终端检查结果。
这些重复操作,Hooks 可以全部自动化。
我在日常开发中配了 5 个 Hooks,跑了两个月,这篇文章把完整配置和踩坑经验写出来。你看完直接复制到自己项目里就能用。
Hooks 是什么
Hooks 是 Claude Code 的生命周期钩子。你定义一段 shell 命令,绑定到某个事件上,Claude 执行到那个节点时自动触发。
比如 Claude 要执行一个 shell 命令(PreToolUse 事件),你的 hook 先跑一遍,检查命令里有没有 rm -rf。有的话直接拦截,Claude 收到一条"被阻止"的消息,不会真的执行。
整个过程不需要你盯着屏幕,不需要你按确认键。
Hooks 支持 20 多种事件,常用的就这几个:
- PreToolUse:工具调用前触发,可以拦截
- PostToolUse:工具调用后触发,可以做格式化、校验
- Stop:Claude 回复完毕时触发,可以做收尾工作
- SessionStart:会话开始时触发,可以做环境初始化
- Notification:Claude 发通知时触发,可以转发到飞书/Slack
配置文件放哪
Hooks 配置写在 JSON 文件里,放在不同位置作用域不同:
~/.claude/settings.json → 全局生效,所有项目
.claude/settings.json → 当前项目,可以提交到 Git
.claude/settings.local.json → 当前项目,不入库
团队协作推荐用 .claude/settings.json,个人习惯放全局。
实战配置 1:拦截危险命令
这个最刚需。Claude 有时候会执行 rm -rf 或者 git push --force 这种不可逆操作。配一个 PreToolUse 钩子拦住它。
.claude/settings.json 里加:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-dangerous.sh"
}
]
}
]
}
}
钩子脚本 .claude/hooks/block-dangerous.sh:
#!/bin/bash
COMMAND=$(cat | jq -r '.tool_input.command')
# 危险命令黑名单
BLOCKED_PATTERNS=(
"rm -rf /"
"rm -rf ~"
"git push --force"
"git push -f"
"DROP TABLE"
"DROP DATABASE"
)
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qi "$pattern"; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "危险命令被拦截: '"$pattern"'"
}
}'
exit 0
fi
done
exit 0
记得加执行权限:chmod +x .claude/hooks/block-dangerous.sh
踩坑提醒:exit code 很讲究。PreToolUse 里 exit 0 表示放行,返回 JSON 里带 permissionDecision: "deny" 表示拦截。别搞混了。exit code 2 也可以直接拦截,但不能附带原因说明,不推荐。
实战配置 2:写完文件自动格式化
Claude 改完代码经常不符合项目的格式规范。配一个 PostToolUse 钩子,每次写文件后自动跑 prettier。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-format.sh"
}
]
}
]
}
}
脚本 .claude/hooks/auto-format.sh:
#!/bin/bash
FILE_PATH=$(cat | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# 只格式化前端相关文件
case "$FILE_PATH" in
*.js|*.ts|*.jsx|*.tsx|*.css|*.json|*.md)
npx prettier --write "$FILE_PATH" 2>/dev/null
;;
*.py)
black "$FILE_PATH" 2>/dev/null
;;
esac
exit 0
实测数据:我的项目里 Claude 一天平均改 30-50 个文件。没配这个钩子前,每天手动跑 prettier 大概花 10-15 分钟。配了之后完全省掉了。
踩坑提醒:PostToolUse 钩子不要做太慢的事。它是同步执行的,跑太久会拖慢 Claude 的响应。prettier 格式化一个文件通常 200ms 以内,没问题。但如果你想在这里跑完整的 eslint fix,可能需要改成异步钩子(加 "async": true)。
实战配置 3:完成任务后自动跑测试
Claude 写完代码说"搞定了",但测试有没有过?配一个 Stop 钩子,Claude 每次回复完自动跑测试。
{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/run-tests.sh",
"async": true
}
]
}
]
}
}
脚本 .claude/hooks/run-tests.sh:
#!/bin/bash
cd "$CLAUDE_PROJECT_DIR" || exit 0
# 检查是否有改动的测试文件
CHANGED_FILES=$(git diff --name-only HEAD 2>/dev/null)
if echo "$CHANGED_FILES" | grep -qE '\.(test|spec)\.(js|ts|jsx|tsx)$'; then
# 只跑改动相关的测试
npm test -- --changedSince=HEAD~1 2>&1 | tail -20 > /tmp/claude-test-result.txt
if [ $? -ne 0 ]; then
# 测试失败,发通知
osascript -e 'display notification "有测试用例失败了" with title "Claude Code"' 2>/dev/null
fi
fi
exit 0
这里用了 "async": true,测试在后台跑,不阻塞 Claude 的下一轮对话。测试结果写到 /tmp/claude-test-result.txt,随时可以查。
踩坑提醒:Stop 事件每次 Claude 回复完都会触发,包括它只是回答一个问题、没改任何代码的时候。所以脚本里要先判断有没有文件改动,不要无脑跑测试。
实战配置 4:会话开始时检查环境
有时候切换项目忘了启动 Docker、忘了切 Node 版本。SessionStart 钩子可以在每次开始对话时做环境检查。
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-env.sh"
}
]
}
]
}
}
脚本 .claude/hooks/check-env.sh:
#!/bin/bash
WARNINGS=""
# 检查 Node 版本
REQUIRED_NODE="20"
CURRENT_NODE=$(node -v 2>/dev/null | grep -oE '[0-9]+' | head -1)
if [ "$CURRENT_NODE" != "$REQUIRED_NODE" ]; then
WARNINGS="${WARNINGS}Node 版本不对,当前 v${CURRENT_NODE},需要 v${REQUIRED_NODE}\n"
fi
# 检查 Docker
if ! docker info > /dev/null 2>&1; then
WARNINGS="${WARNINGS}Docker 没启动\n"
fi
# 检查 .env 文件
if [ ! -f "$CLAUDE_PROJECT_DIR/.env" ]; then
WARNINGS="${WARNINGS}.env 文件不存在\n"
fi
if [ -n "$WARNINGS" ]; then
echo "⚠️ 环境检查发现问题:"
echo -e "$WARNINGS"
fi
exit 0
钩子的 stdout 输出会被 Claude 读到。所以环境检查的警告信息 Claude 能看见,它会在后续操作中考虑这些情况。
实战配置 5:任务完成发通知到飞书
Claude 跑一个耗时任务,你不想干等着。配一个 Notification 钩子把消息转发到飞书群。
{
"hooks": {
"Notification": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/notify-feishu.sh",
"async": true
}
]
}
]
}
}
脚本 .claude/hooks/notify-feishu.sh:
#!/bin/bash
INPUT=$(cat)
MESSAGE=$(echo "$INPUT" | jq -r '.message // "Claude Code 发来通知"')
# 飞书群机器人 webhook
WEBHOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/你的webhook地址"
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{
\"msg_type\": \"text\",
\"content\": {
\"text\": \"[Claude Code] $MESSAGE\"
}
}" > /dev/null
exit 0
这样 Claude 干完活或者遇到问题需要你介入时,飞书群里会收到消息。比轮询终端窗口舒服多了。
完整的 settings.json 参考
把上面 5 个配置合在一起:
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-env.sh" }
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-dangerous.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-format.sh" }
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/run-tests.sh", "async": true }
]
}
],
"Notification": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/notify-feishu.sh", "async": true }
]
}
]
}
}
调试技巧
钩子不生效?几个排查方向:
- 脚本有没有执行权限:
chmod +x .claude/hooks/*.sh先跑一遍 - jq 有没有装:钩子的输入是 JSON,解析靠 jq。
brew install jq或apt install jq - 手动测试脚本:
echo '{"tool_input":{"command":"rm -rf /"}}' | bash .claude/hooks/block-dangerous.sh看输出对不对 - 看 Claude Code 日志:运行
claude --debug可以看到钩子的触发和输出
环境变量 $CLAUDE_PROJECT_DIR 是 Claude Code 自动注入的,指向当前项目根目录。如果你的脚本在非项目目录下测试,这个变量是空的,要注意。
写在最后
Hooks 的设计思路很简单:Claude 是非确定性的(LLM 嘛),但你的工程流程需要确定性。Hooks 在两者之间加了一层确定性的控制点。
这 5 个配置覆盖了我日常 80% 的场景。你可以根据自己的项目需求改脚本内容,整体框架直接复制就行。
如果你想了解更多事件类型(比如 SubagentStart、FileChanged、WorktreeCreate),可以查看官方文档 code.claude.com/docs/en/hooks。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)