你对 Claude Code 说过"格式化代码"但它没做,说过"别动那个文件"但它动了,说过"完成后跑测试"但它忘了。
原因:CLAUDE.md 只是建议,执行率约 80%。Hooks 不同——它们是自动触发的动作,每次 Claude 编辑文件、运行命令或完成任务时都会触发。
两个核心类型:
- PreToolUse:操作前触发,可通过 exit code 2 阻止行为,像是门卫
- PostToolUse:操作后触发,可运行清理、格式化、测试或日志,像是流水线上的质检
Hook 1:自动格式化(最该默认配置)
Claude 写了正确但违反格式规则的代码。在 CLAUDE.md 加"运行 Prettier"有用,但不是 100%。
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0"
}]
}]
}
}
把 npx prettier --write 换成你的格式化工具:Python 用 black,Go 用 gofmt,Rust 用 rustfmt。
Hook 2:阻止危险命令(生产环境必备)
Claude 足够强大到能运行 rm -rf、git reset --hard、DROP TABLE。它大概率不会做,但"大概率"不够。
创建 .claude/hooks/block-dangerous.sh:
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
dangerous_patterns=("rm -rf" "git reset --hard" "DROP TABLE" "curl.*|.*sh")
for pattern in "${dangerous_patterns[@]}"; do
if echo "$cmd" | grep -qiE "$pattern"; then
echo "Blocked: '$cmd'" >&2; exit 2
fi
done
exit 0
Exit code 2 阻止操作并回传错误信息;exit code 0 表示放行。
Hook 3:保护敏感文件
阻止 Claude 编辑 .env、.git/、package-lock.json、.pem、secrets/* 等文件。
Hook 4:自动运行测试
Claude 写完代码说"完成了",你跑 commit 时发现测试已坏。PostToolUse 钩子每次写文件后自动跑测试,失败立即修复。
Claude Code 创建者 Boris Cherny 说过:给 Claude 一个反馈循环,输出质量提升 2-3 倍。
Hook 5:PR 创建门槛
创建 .claude/hooks/require-tests-for-pr.sh,没有绿色测试就不让创建 PR。Exit code 2 强制 Claude 先修问题。
Hook 6:Lint 检查
ESLint 检查接在格式化后面,代码到你眼前时已经格式规范且 lint 干净。
Hook 7:命令日志
每次 Bash 命令带上时间戳记入日志文件。方便回溯:"Claude 三天前搞坏了什么"。
Hook 8:自动 Commit
Claude 停止工作时自动 commit 所有变更。每个任务原子 commit,而不是一天结束时的"Claude changes"巨型 blob。
配合 claude -w feature-branch(worktrees)使用,每个任务有独立的自动提交分支。
好与伟大的差距
不是模型,不是提示词,是 Hooks。它们在你没注意时运行,拦截你本来会在 code review 或生产环境才发现的错误。
今天就配置 Hook #1(自动格式化)和 #2(阻止危险命令)。仅这两条就能让你远离最常见的 Claude Code 失误。
Hooks 机制本质上是给 AI 加了一层编译器级别的强制约束,而不是依赖提示词软控制。这个设计思路很值得借鉴——无论是给 Claude Code 配 Hooks,还是在自己的 Agent 系统里加 Pre/Post 执行拦截层。这比"在提示词里强调多次"有效得多,也更符合工程思维。