Claude Code Hooks 实战:5 个钩子让 AI 编程效率翻倍
用 Claude Code 写代码,你有没有遇到这种情况:Claude 修改完文件忘记格式化、执行危险命令没有阻拦、等了半天不知道 AI 还在工作……这些问题都可以用 Hooks(钩子) 一次性解决。Hooks 是 Claude Code 最被低估的特性——它允许你在 AI 工作流的任意时间点插入自定义脚本,让 AI 的每个动作都符合你的开发规范。本文用 5 个真实场景,带你从零配置一套完整的 Hooks 自动化系统。
- Claude Code Hooks 的工作原理与 14 个事件类型
settings.json配置格式(项目级 & 全局级)- 5 个即用型钩子脚本:自动格式化、危险命令拦截、运行测试、桌面通知、Git 自动存档
- 使用
/hooks命令管理钩子的完整流程
核心概念:Hooks 是什么?
把 Claude Code 的工作流想象成一条流水线。Hooks 就是这条流水线上的"检查站"——在特定时机自动触发你写好的脚本,接收当前操作的 JSON 上下文,并可以输出决策影响 Claude 的行为。
Hook 的生命周期:
用户提问 → [UserPromptSubmit Hook] → Claude 思考
→ [PreToolUse Hook] → Claude 执行工具
→ [PostToolUse Hook] → Claude 完成动作
→ [Stop Hook] → 会话结束
14 个事件类型速查:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
| SessionStart | 会话开始时 | 初始化环境、加载项目规则 |
| UserPromptSubmit | 用户发送消息时 | 注入上下文、记录提问 |
| PreToolUse | 工具调用前(可阻止) | 危险命令拦截、权限检查 |
| PermissionRequest | 弹出权限对话框时 | 自动批准/拒绝特定操作 |
| PostToolUse | 工具调用成功后 | 自动格式化、运行测试 |
| PostToolUseFailure | 工具调用失败后 | 错误日志、自动重试 |
| Notification | Claude 需要用户关注时 | 桌面通知、手机推送 |
| Stop | Claude 完成所有工作时 | Git 存档、生成报告 |
| PreCompact | 上下文压缩前 | 保存重要信息 |
| SubagentStart/Stop | 子 Agent 启动/停止时 | 多 Agent 协调 |
实战步骤
了解配置结构
Hooks 配置通过 settings.json 文件管理,有三个作用域:
# 全局钩子(对所有项目生效)
~/.claude/settings.json
# 项目钩子(仅当前项目生效)
.claude/settings.json
# 本地钩子(仅本人可见,不纳入 Git)
.claude/settings.local.json
配置格式模板:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "your-script-here"
}
]
}
]
}
}
~/.claude/settings.json),项目特定的放 .claude/settings.json,敏感的放 .claude/settings.local.json 并加入 .gitignore。用 /hooks 命令管理钩子(推荐入口)
Claude Code 提供交互式的钩子管理命令,比直接编辑 JSON 更安全:
# 在 Claude Code 对话框中输入:
/hooks
操作流程:选择事件类型 → 选择"Add new hook" → 输入 matcher → 输入命令 → 保存。
验证配置是否生效:
# 查看全局配置
cat ~/.claude/settings.json
# 期望看到 hooks 字段
{
"hooks": {
"PostToolUse": [...]
}
}
钩子1 — 自动格式化代码(PostToolUse + Write)
场景:Claude 每次写完文件,自动运行格式化工具,告别手动 black . 或 prettier。
创建脚本文件:
mkdir -p ~/.claude/hooks
cat > ~/.claude/hooks/auto-format.sh << 'EOF'
#!/usr/bin/env bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [ -z "$FILE_PATH" ]; then
exit 0
fi
case "$FILE_PATH" in
*.py)
python -m black "$FILE_PATH" 2>/dev/null && echo "✅ Python 格式化完成: $FILE_PATH"
;;
*.ts|*.tsx|*.js|*.jsx)
npx prettier --write "$FILE_PATH" 2>/dev/null && echo "✅ JS/TS 格式化完成: $FILE_PATH"
;;
*.go)
gofmt -w "$FILE_PATH" 2>/dev/null && echo "✅ Go 格式化完成: $FILE_PATH"
;;
esac
EOF
chmod +x ~/.claude/hooks/auto-format.sh
添加到配置(~/.claude/settings.json):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [{"type": "command", "command": "~/.claude/hooks/auto-format.sh"}]
},
{
"matcher": "Edit",
"hooks": [{"type": "command", "command": "~/.claude/hooks/auto-format.sh"}]
}
]
}
}
钩子2 — 拦截危险命令(PreToolUse + Bash)
场景:防止 Claude 执行 rm -rf、git reset --hard 等高风险命令,在执行前提示确认。
cat > ~/.claude/hooks/block-dangerous.sh << 'EOF'
#!/usr/bin/env bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if [ -z "$COMMAND" ]; then
exit 0
fi
DANGEROUS_PATTERNS=(
"rm -rf /"
"rm -rf ~"
"rm -rf \."
"git reset --hard"
"git push --force"
)
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qF "$pattern"; then
jq -n \
--arg reason "⚠️ 危险命令被钩子拦截: $pattern" \
'{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: $reason
}
}'
exit 0
fi
done
exit 0
EOF
chmod +x ~/.claude/hooks/block-dangerous.sh
添加到全局配置:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "~/.claude/hooks/block-dangerous.sh"}]
}
]
}
}
permissionDecision: "deny" 时,Claude 会收到拒绝原因并改变策略,不会强行执行。钩子3 — 桌面通知(Notification Hook)
场景:Claude 需要你输入时,自动弹出系统通知(macOS/Linux 均支持)。
macOS 通知脚本:
cat > ~/.claude/hooks/notify-mac.sh << 'EOF'
#!/usr/bin/env bash
INPUT=$(cat)
MESSAGE=$(echo "$INPUT" | jq -r '.message // "Claude Code 需要你的关注"')
osascript -e "display notification \"$MESSAGE\" with title \"Claude Code\" sound name \"Ping\""
EOF
chmod +x ~/.claude/hooks/notify-mac.sh
添加到全局配置(~/.claude/settings.json):
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [{"type": "command", "command": "~/.claude/hooks/notify-mac.sh"}]
}
]
}
}
钩子4 & 5 — 运行测试 + Git 自动存档
钩子4:写文件后自动运行测试:
cat > ~/.claude/hooks/run-tests.sh << 'EOF'
#!/usr/bin/env bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if echo "$FILE_PATH" | grep -qE '\.(test|spec)\.(ts|js|py)$'; then
echo "🧪 检测到测试文件变更,运行测试..."
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
if [ -f "package.json" ]; then
npm test --silent 2>&1 | tail -5
elif [ -f "pyproject.toml" ] || [ -f "setup.py" ]; then
python -m pytest --tb=short -q 2>&1 | tail -10
fi
fi
EOF
chmod +x ~/.claude/hooks/run-tests.sh
钩子5:每次 Claude 完成工作自动 Git 存档:
cat > ~/.claude/hooks/git-checkpoint.sh << 'EOF'
#!/usr/bin/env bash
cd "$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
if ! git diff --quiet || ! git diff --staged --quiet || \
[ -n "$(git ls-files --others --exclude-standard)" ]; then
TIMESTAMP=$(date '+%Y-%m-%d %H:%M')
git add -A
git commit -m "checkpoint: Claude Code 自动存档 [$TIMESTAMP]" --no-verify 2>/dev/null
echo "📦 Git 存档完成: [$TIMESTAMP]"
fi
EOF
chmod +x ~/.claude/hooks/git-checkpoint.sh
在项目配置中启用(.claude/settings.json):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [{"type": "command", "command": "~/.claude/hooks/run-tests.sh"}]
}
],
"Stop": [
{
"matcher": "",
"hooks": [{"type": "command", "command": "~/.claude/hooks/git-checkpoint.sh"}]
}
]
}
}
效果验证
配置完成后,在 Claude Code 中执行以下操作验证钩子工作:
# 1. 验证格式化钩子:让 Claude 写一个 Python 文件,观察是否自动运行 black
# 2. 验证拦截钩子:让 Claude 执行 "rm -rf /tmp/test",期望看到拒绝提示
# 3. 验证 Notification:长时间运行任务后,期望收到桌面通知
# 4. 验证 Stop 钩子:完成一次对话后查看 git log
git log --oneline -3
# 期望输出:
# abc1234 checkpoint: Claude Code 自动存档 [2026-02-28 20:30]
# def5678 上一次手动提交
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 钩子没有触发 | 配置 JSON 格式错误 | 运行 jq . ~/.claude/settings.json 验证语法 |
| command not found: jq | jq 未安装 | brew install jq 或 apt install jq |
| 脚本权限不足 | 没有执行权限 | chmod +x ~/.claude/hooks/*.sh |
| PreToolUse 没有阻止命令 | JSON 输出格式错误 | 确保输出完整的 hookSpecificOutput JSON 结构 |
| 格式化钩子报错 | 格式化工具未安装 | 安装对应工具(black/prettier/gofmt) |
进阶技巧
~/.claude/hook-log.txt,追踪 Claude 的完整操作历史"matcher": "Write|Edit|MultiEdit" 正则匹配多个工具,减少不必要的触发[ -z "$CI" ],在 CI 环境下跳过桌面通知等本地操作完整日志钩子(通用,建议全局挂载):
cat > ~/.claude/hooks/logger.sh << 'EOF'
#!/usr/bin/env bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name // "unknown"')
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] $TOOL: $(echo "$INPUT" | jq -c '.tool_input' | cut -c1-200)" \
>> ~/.claude/hook-log.txt
EOF
chmod +x ~/.claude/hooks/logger.sh
总结
- ✅ 格式化钩子:每次 AI 写文件后自动运行 black / prettier / gofmt
- ✅ 拦截钩子:危险命令在执行前被阻止,返回清晰的拒绝原因
- ✅ 通知钩子:AI 等待输入时弹出系统通知,无需盯屏幕
- ✅ 测试钩子:写测试文件后自动运行测试套件
- ✅ Git 存档钩子:每次 AI 工作结束自动创建检查点提交
下一步探索:Claude Code Hooks 官方文档(code.claude.com/docs/en/hooks)、MCP 服务器开发、Claude Code 记忆系统实战。