你对 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 -rfgit reset --hardDROP 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 失误。