返回列表
🧠 阿头学 · 💬 讨论题

Claude Code Hook 的价值成立,但这篇 8 个自动化套路文章有严重实操错误

这篇文章抓住了“用可执行约束替代提示词约束”这个真问题,但示例代码大量串台错位,导致它更像一篇方向正确、细节不可靠的营销型经验帖。
打开原文 ↗

2026-04-04 原文链接 ↗
阅读简报
双语对照
完整翻译
原文
讨论归档

核心观点

  • Hook 比 CLAUDE.md 更硬 文章最成立的判断是:自然语言规范只能影响模型,不足以约束模型;PreToolUse/PostToolUse 这类机制化钩子才是真正可执行的边界,尤其适合危险命令拦截、敏感文件保护、测试与 lint 反馈。
  • 反馈回路比“写对一次”更重要 自动格式化、自动测试、自动 lint 的核心价值不在省手,而在把错误暴露提前到代理执行过程里;这个判断对 AI coding 很关键,因为高质量往往来自快速纠偏,而不是一次生成就正确。
  • 团队共享配置是有工程价值的 把 `.claude/settings.json` 放进仓库、让团队共享同一套 AI 行为边界,这个思路明显优于每个人各自写 prompt;它本质上是在把团队规范“代码化”,这点很站得住。
  • 文章实操可信度被严重削弱 多处“问题—脚本—配置”明显对不上,属于技术文章里的硬伤,不是小排版问题;如果读者照抄,极可能把日志、PR 校验、文件保护、自动提交配乱,文章的教学价值因此大打折扣。
  • 自动提交被吹得过头了 “每个任务结束后自动 commit”在个人实验流里可能省事,但在正式团队流程里很可能污染历史、固化未经审阅的 AI 改动;作者把它包装成普适最佳实践,这个判断明显过度。

跟我们的关联

  • 对 ATou 意味着什么 不要再把“让 AI 听话”理解成 prompt engineering 问题,而要理解成工作流治理问题;下一步可以先抽出 3 类高频规则:禁止动作、必须检查、必须留痕,并优先机制化。
  • 对 Neta 意味着什么 如果要做 agent 产品或内部工具,真正的差异化不是模型接口,而是运行时 guardrail 设计;下一步可以把“Pre-check / Post-check / Audit”设计成产品层能力,而不是靠用户自己写提示词。
  • 对 Uota 意味着什么 这篇文章证明“默认安全网”会直接提升用户信任,但也提醒内容包装会掩盖实际复杂度;下一步应区分“小项目默认模板”和“团队生产配置”,避免把重型流程卖成无脑开箱即用。
  • 对通用实践意味着什么 凡是你已经提醒 AI 三次以上的规则,都说明它不该继续留在文档里;下一步应把这些规则按“可阻断 / 可反馈 / 可审计”分类,逐步迁移成自动化钩子。

讨论引子

1. 对 AI coding 来说,最该优先机制化的规则到底是“安全边界”还是“质量检查”? 2. 自动测试、lint、格式化放在每次编辑后执行,在哪个项目规模上会从“增效”变成“拖慢一切”? 3. 自动 commit 是提高可追踪性,还是在给团队制造 Git 噪音和虚假整洁?

有没有遇到过这种情况,让 Claude Code 做点事,它就是没做?

让它格式化代码,它没做。让它别碰那个文件,它偏碰了。

让它在结束前跑测试,它又忘了。

因为 CLAUDE.md 只是建议。

Claude 会读它,但大约只有 80% 的时候会照着做。Hook 不一样。它们是自动动作,每次 Claude 编辑文件、运行命令,或完成任务时都会触发。

下面分享 8 个自己常用的 hook,你可以直接复制进 settings.json,以后就不用再惦记了 👇

正式开始前,Telegram 频道每天分享 AI 和 vibe coding 的笔记:https://t.me/zodchixquant🧠

Hook 是怎么工作的(30 秒版)

什么是 hook?

Hook 是自动动作,Claude Code 每做一件事,比如编辑文件或运行命令,它就会执行。

设置一次,就会在后台持续工作,不用一直盯着。

最常用的两个:

PreToolUse 在 Claude 做事之前运行。你可以检查它要做的动作,并通过返回 exit code 2 把它拦下来。把它当作门口保安。

PostToolUse 在 Claude 做事之后运行。你可以做清理、格式化、跑测试或记录日志。把它当作流水线上的质检。

https://code.claude.com/docs/en/hooks

在项目根目录的 .claude/settings.json 里配置它们。这个文件会提交到 git,所以整个团队都会自动用上同一套 hook。

完整文档:https://code.claude.com/docs/en/hooks

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run test --silent 2>&1 | tail -5; exit 0"
          }
        ]
      }
    ]
  }
}

1. 自动格式化 Claude 触碰过的每个文件

问题: Claude 写出的代码逻辑没问题,却破坏了你的格式化规则。你在 CLAUDE.md 里写了总是运行 Prettier,大多数时候有效,但不是每次都有效。

Hook: 每次写入或编辑文件后,自动运行 Prettier。

#!/usr/bin/env bash
set -euo pipefail
file=$(jq -r '.tool_input.file_path // .tool_input.path // ""')

protected=(
  ".env*"
  ".git/*"
  "package-lock.json"
  "yarn.lock"
  "*.pem"
  "*.key"
  "secrets/*"
)

for pattern in "${protected[@]}"; do
  if echo "$file" | grep -qiE "^${pattern//\*/.*}$"; then
    echo "Blocked: '$file' is protected. Explain why this edit is necessary." >&2
    exit 2
  fi
done
exit 0

npx prettier --write 换成你用的格式化工具即可:Python 用 black,Go 用 gofmt,Rust 用 rustfmt。套路一样。

这是第一个配起来的 hook。说真的,每个项目都该默认有它。再也不会出现 Claude 忘了格式化 的提交。

2. 阻止危险命令

问题: Claude 的能力足够强,它可以去跑 rm -rf、git reset --hard、DROP TABLE,或者 curl 随便一个 URL。它可能不会这么干,但当这是你的生产数据库时,可能 这两个字不够。

Hook: 在执行之前拦住破坏性的命令。

创建 .claude/hooks/block-dangerous.sh:

然后把它加进 settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/require-tests-for-pr.sh"
          }
        ]
      }
    ]
  }
}

关键是 exit code 2。它会阻止动作,并把你的错误信息回传给 Claude,让它换个更安全的做法。exit code 0 表示继续。其他任何退出码只会记录警告,不会拦截。

3. 保护敏感文件不被改动

问题: Claude 可以读取并编辑项目里的任何文件。这包括 .env、package-lock.json、配置文件,以及你不希望它碰的其他东西。

Hook: 对不该碰的文件,一律禁止编辑。

创建 .claude/hooks/protect-files.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/log-commands.sh"
          }
        ]
      }
    ]
  }
}

4. 每次编辑后都跑测试

问题: Claude 改完说 done,你准备提交时才发现测试炸了,已经过去 20 分钟。

Hook: 每次代码改动后自动跑测试套件。只要失败,Claude 立刻能看到失败信息并马上修。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

这里的 tail -5 把输出截短,避免淹没 Claude 的上下文。希望它看到的是 3 tests failed,而不是完整的 200 行测试输出。

Claude Code 的作者 Boris Cherny 说,给 Claude 这样的反馈回路,输出质量能提升 2-3 倍。它不再是写完就祈祷能跑,而是写完立刻看测试结果,失败就自己修。

5. 创建 PR 前必须测试全绿

问题: Claude 做完一个功能就立刻创建 PR。测试在失败。Reviewer 看到 CI 一片红,又给你打回来。

Hook: 没有全绿测试,不允许创建 PR。

创建 .claude/hooks/require-tests-for-pr.sh:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0"
          }
        ]
      }
    ]
  }
}

这是硬门槛。测试不绿,就没有 PR。Claude 会先把失败修好,因为 exit code 2 会告诉它动作被拦住了,以及拦住的原因。

6. 自动 lint 并回报错误

问题: Claude 写的代码能跑,但违反了你的 ESLint 规则、风格规范或类型检查。你在 review 才发现,只能打回去改。

Hook: 每次编辑后都跑 lint。只要 lint 失败,Claude 就能看到报错,并在你看到代码之前先修掉。

这可以和第 1 条的自动格式化串起来。先跑 Prettier,再跑 ESLint。你看到代码时,它已经格式正确、lint 也干净了。

7. 记录 Claude 跑过的每条命令

问题: 一次会话里 Claude 会跑很多 shell 命令。出了问题时,需要知道它在什么时候跑了什么。

Hook: 把每条 Bash 命令按时间戳追加写进日志文件。

创建 .claude/hooks/log-commands.sh:

#!/usr/bin/env bash
set -euo pipefail
git add -A
if ! git diff --cached --quiet; then
  git commit -m "chore(ai): apply Claude edit"
fi
exit 0
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0"
          }
        ]
      }
    ]
  }
}

现在就有了一条带时间戳的审计轨迹,记录 Claude 跑过的每条命令。把 .claude/command-log.txt 加进你的 .gitignore,避免把日志文件提交进仓库。

这对排查问题尤其有用:如果 Claude 三次会话前把什么东西搞坏了,看日志就能找到它到底在什么时候跑了什么。

8. 每个任务结束后自动提交

问题: Claude 做完一个任务,你忘了 commit。它又开始下一个任务,两个不相关的改动就混进了同一个提交。

Hook: Claude 停止处理一个任务时,自动提交所有改动。

创建 .claude/hooks/auto-commit.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/block-dangerous.sh" },
          { "type": "command", "command": ".claude/hooks/log-commands.sh" }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/protect-files.sh" }
        ]
      },
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/require-tests-for-pr.sh" }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0" },
          { "type": "command", "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0" }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/auto-commit.sh" }
        ]
      }
    ]
  }
}

每次 Claude 结束一次回复,改动都会自动提交。你的 git 历史会保持干净,每个任务一个原子提交,而不是一天结束时一坨 Claude changes 大杂烩。

再配合 claude -w feature-branch(worktrees),就能为每个任务拿到隔离的、自动提交的 feature 分支。

完整的 settings.json

下面是把所有内容合并后的一个文件,你可以直接复制粘贴:

方便截图:

把这个文件复制到 .claude/settings.json,在 .claude/hooks/ 里创建这些 hook 脚本,用 chmod +x .claude/hooks/*.sh 让它们可执行,然后把所有东西提交到 git。整个团队都会自动拥有同样的安全网。

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/auto-commit.sh"
          }
        ]
      }
    ]
  }
}

一套不错的 Claude Code 配置和一套很强的配置,差别不在模型,也不在提示词,而在 hooks。

它们会在你不注意的时候执行,把那些你本来要到 code review 才发现的错误,甚至更糟糕,到生产环境才发现的错误,提前兜住。

今天先把第 1 条(自动格式化)和第 2 条(阻止危险命令)配上。这两条就能挡住最常见的 Claude Code 失误。剩下的按需加就行。

Telegram 频道每天分享 AI、金融和 vibe coding 的笔记:https://t.me/zodchixquant

感谢阅读 🙏🏼

```markdown Where hooks live:

.claude/settings.json project-level (shared via git) ~/.claude/settings.json user-level (all your projects) .claude/settings.local.json local only (not committed)

Have you ever told Claude Code to do something and it just didn't?

You said format the code - It didn't. You said don't touch that file - It did.

You said run tests before finishing - It forgot.

That's because CLAUDE.md is a suggestion.

Claude reads it and follows it about 80% of the time. Hooks are different. They're automatic actions that fire every time Claude edits a file, runs a command, or finishes a task.

Below I will share 8 personal hooks you can copy straight into your settings.json and never think about again 👇

Before we dive in, I share daily notes on AI & vibe coding in my Telegram channel: https://t.me/zodchixquant🧠

有没有遇到过这种情况,让 Claude Code 做点事,它就是没做?

让它格式化代码,它没做。让它别碰那个文件,它偏碰了。

让它在结束前跑测试,它又忘了。

因为 CLAUDE.md 只是建议。

Claude 会读它,但大约只有 80% 的时候会照着做。Hook 不一样。它们是自动动作,每次 Claude 编辑文件、运行命令,或完成任务时都会触发。

下面分享 8 个自己常用的 hook,你可以直接复制进 settings.json,以后就不用再惦记了 👇

正式开始前,Telegram 频道每天分享 AI 和 vibe coding 的笔记:https://t.me/zodchixquant🧠

How hooks work (30-second version)

What are hooks?

Hooks are automatic actions that run every time Claude Code does something, like editing a file or running a command.

You set them up once and they work in the background without you thinking about it.

The two you'll use most:

PreToolUse runs before Claude does something. You can inspect the action and block it by returning exit code 2. Think of it as a bouncer.

PostToolUse runs after Claude does something. You can run cleanup, formatting, tests, or logging. Think of it as quality control on the assembly line.

https://code.claude.com/docs/en/hooks

You configure them in .claude/settings.json in your project root. That file gets committed to git, so your whole team gets the same hooks automatically.

Full documentation: https://code.claude.com/docs/en/hooks

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run test --silent 2>&1 | tail -5; exit 0"
          }
        ]
      }
    ]
  }
}

Hook 是怎么工作的(30 秒版)

什么是 hook?

Hook 是自动动作,Claude Code 每做一件事,比如编辑文件或运行命令,它就会执行。

设置一次,就会在后台持续工作,不用一直盯着。

最常用的两个:

PreToolUse 在 Claude 做事之前运行。你可以检查它要做的动作,并通过返回 exit code 2 把它拦下来。把它当作门口保安。

PostToolUse 在 Claude 做事之后运行。你可以做清理、格式化、跑测试或记录日志。把它当作流水线上的质检。

https://code.claude.com/docs/en/hooks

在项目根目录的 .claude/settings.json 里配置它们。这个文件会提交到 git,所以整个团队都会自动用上同一套 hook。

完整文档:https://code.claude.com/docs/en/hooks

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run test --silent 2>&1 | tail -5; exit 0"
          }
        ]
      }
    ]
  }
}

1. Auto-format every file Claude touches

The problem: Claude writes correct code that breaks your formatting rules. You add "always run Prettier" to CLAUDE.md and it works most of the time, but not always.

The hook: Prettier runs automatically after every file write or edit.

#!/usr/bin/env bash
set -euo pipefail
file=$(jq -r '.tool_input.file_path // .tool_input.path // ""')

protected=(
  ".env*"
  ".git/*"
  "package-lock.json"
  "yarn.lock"
  "*.pem"
  "*.key"
  "secrets/*"
)

for pattern in "${protected[@]}"; do
  if echo "$file" | grep -qiE "^${pattern//\*/.*}$"; then
    echo "Blocked: '$file' is protected. Explain why this edit is necessary." >&2
    exit 2
  fi
done
exit 0

Swap npx prettier --write for whatever formatter you use: black for Python, gofmt for Go, rustfmt for Rust. The pattern is the same.

This was the first hook I set up and honestly it should be the default for every project. No more "Claude forgot to format" commits.

1. 自动格式化 Claude 触碰过的每个文件

问题: Claude 写出的代码逻辑没问题,却破坏了你的格式化规则。你在 CLAUDE.md 里写了总是运行 Prettier,大多数时候有效,但不是每次都有效。

Hook: 每次写入或编辑文件后,自动运行 Prettier。

#!/usr/bin/env bash
set -euo pipefail
file=$(jq -r '.tool_input.file_path // .tool_input.path // ""')

protected=(
  ".env*"
  ".git/*"
  "package-lock.json"
  "yarn.lock"
  "*.pem"
  "*.key"
  "secrets/*"
)

for pattern in "${protected[@]}"; do
  if echo "$file" | grep -qiE "^${pattern//\*/.*}$"; then
    echo "Blocked: '$file' is protected. Explain why this edit is necessary." >&2
    exit 2
  fi
done
exit 0

npx prettier --write 换成你用的格式化工具即可:Python 用 black,Go 用 gofmt,Rust 用 rustfmt。套路一样。

这是第一个配起来的 hook。说真的,每个项目都该默认有它。再也不会出现 Claude 忘了格式化 的提交。

2. Block dangerous commands

The problem: Claude is powerful enough to run rm -rf, git reset --hard, DROP TABLE, or curl to random URLs. It probably won't, but "probably" isn't good enough when it's your production database.

The hook: Block destructive commands before they execute.

Create .claude/hooks/block-dangerous.sh:

Then add to your settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/require-tests-for-pr.sh"
          }
        ]
      }
    ]
  }
}

Exit code 2 is the key. It blocks the action and sends your error message back to Claude so it can try a safer approach. Exit code 0 means "go ahead." Anything else logs a warning but doesn't block.

2. 阻止危险命令

问题: Claude 的能力足够强,它可以去跑 rm -rf、git reset --hard、DROP TABLE,或者 curl 随便一个 URL。它可能不会这么干,但当这是你的生产数据库时,可能 这两个字不够。

Hook: 在执行之前拦住破坏性的命令。

创建 .claude/hooks/block-dangerous.sh:

然后把它加进 settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/require-tests-for-pr.sh"
          }
        ]
      }
    ]
  }
}

关键是 exit code 2。它会阻止动作,并把你的错误信息回传给 Claude,让它换个更安全的做法。exit code 0 表示继续。其他任何退出码只会记录警告,不会拦截。

3. Protect sensitive files from edits

The problem: Claude can read and edit any file in your project. That includes .env, package-lock.json, config files, and anything else you'd rather it didn't touch.

The hook: Block edits to files that should be off-limits.

Create .claude/hooks/protect-files.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/log-commands.sh"
          }
        ]
      }
    ]
  }
}

3. 保护敏感文件不被改动

问题: Claude 可以读取并编辑项目里的任何文件。这包括 .env、package-lock.json、配置文件,以及你不希望它碰的其他东西。

Hook: 对不该碰的文件,一律禁止编辑。

创建 .claude/hooks/protect-files.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/log-commands.sh"
          }
        ]
      }
    ]
  }
}

4. Run tests after every edit

The problem: Claude makes a change, says "done," and you discover the tests are broken 20 minutes later when you try to commit.

The hook: Run your test suite automatically after every code change. If tests fail, Claude sees the failure and can fix it immediately.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

The tail -5 keeps the output short so it doesn't flood Claude's context. You want Claude to see "3 tests failed" not the full 200-line test output.

Boris Cherny, the creator of Claude Code, says giving Claude a feedback loop like this improves output quality by 2-3x. Instead of writing code and hoping it works, Claude writes code, sees the test results, and fixes failures on its own.

4. 每次编辑后都跑测试

问题: Claude 改完说 done,你准备提交时才发现测试炸了,已经过去 20 分钟。

Hook: 每次代码改动后自动跑测试套件。只要失败,Claude 立刻能看到失败信息并马上修。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

这里的 tail -5 把输出截短,避免淹没 Claude 的上下文。希望它看到的是 3 tests failed,而不是完整的 200 行测试输出。

Claude Code 的作者 Boris Cherny 说,给 Claude 这样的反馈回路,输出质量能提升 2-3 倍。它不再是写完就祈祷能跑,而是写完立刻看测试结果,失败就自己修。

5. Require passing tests before creating a PR

The problem: Claude finishes a feature and immediately creates a PR. Tests are failing. Your reviewer sees red CI and sends it back.

The hook: Block PR creation unless all tests pass.

Create .claude/hooks/require-tests-for-pr.sh:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0"
          }
        ]
      }
    ]
  }
}

This is a hard gate. No green tests, no PR. Claude will fix the failures first because exit code 2 tells it the action was blocked and why.

5. 创建 PR 前必须测试全绿

问题: Claude 做完一个功能就立刻创建 PR。测试在失败。Reviewer 看到 CI 一片红,又给你打回来。

Hook: 没有全绿测试,不允许创建 PR。

创建 .claude/hooks/require-tests-for-pr.sh:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0"
          }
        ]
      }
    ]
  }
}

这是硬门槛。测试不绿,就没有 PR。Claude 会先把失败修好,因为 exit code 2 会告诉它动作被拦住了,以及拦住的原因。

6. Auto-lint and report errors

The problem: Claude writes code that works but violates your ESLint rules, style guide, or type checks. You catch it during review and send it back.

The hook: Lint after every edit. If lint fails, Claude sees the errors and fixes them before you ever look at the code.

You can chain this with the auto-format hook from #1. Prettier runs first, then ESLint. By the time you see the code, it's formatted and lint-clean.

6. 自动 lint 并回报错误

问题: Claude 写的代码能跑,但违反了你的 ESLint 规则、风格规范或类型检查。你在 review 才发现,只能打回去改。

Hook: 每次编辑后都跑 lint。只要 lint 失败,Claude 就能看到报错,并在你看到代码之前先修掉。

这可以和第 1 条的自动格式化串起来。先跑 Prettier,再跑 ESLint。你看到代码时,它已经格式正确、lint 也干净了。

7. Log every command Claude runs

The problem: Claude runs a lot of shell commands during a session. If something goes wrong, you want to know exactly what it did and when.

The hook: Append every Bash command to a log file with timestamps.

Create .claude/hooks/log-commands.sh:

#!/usr/bin/env bash
set -euo pipefail
git add -A
if ! git diff --cached --quiet; then
  git commit -m "chore(ai): apply Claude edit"
fi
exit 0
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0"
          }
        ]
      }
    ]
  }
}

Now you have a timestamped audit trail of every command Claude ran. Add .claude/command-log.txt to your .gitignore so it doesn't pollute your repo.

This is especially useful for debugging: if Claude broke something three sessions ago, you can look at the log and find exactly when and what it ran.

7. 记录 Claude 跑过的每条命令

问题: 一次会话里 Claude 会跑很多 shell 命令。出了问题时,需要知道它在什么时候跑了什么。

Hook: 把每条 Bash 命令按时间戳追加写进日志文件。

创建 .claude/hooks/log-commands.sh:

#!/usr/bin/env bash
set -euo pipefail
git add -A
if ! git diff --cached --quiet; then
  git commit -m "chore(ai): apply Claude edit"
fi
exit 0
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0"
          }
        ]
      }
    ]
  }
}

现在就有了一条带时间戳的审计轨迹,记录 Claude 跑过的每条命令。把 .claude/command-log.txt 加进你的 .gitignore,避免把日志文件提交进仓库。

这对排查问题尤其有用:如果 Claude 三次会话前把什么东西搞坏了,看日志就能找到它到底在什么时候跑了什么。

8. Auto-commit after each completed task

The problem: Claude finishes a task and you forget to commit. Then it starts another task and now you have two unrelated changes mixed together in one commit.

The hook: Automatically commit all changes when Claude stops working on a task.

Create .claude/hooks/auto-commit.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/block-dangerous.sh" },
          { "type": "command", "command": ".claude/hooks/log-commands.sh" }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/protect-files.sh" }
        ]
      },
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/require-tests-for-pr.sh" }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0" },
          { "type": "command", "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0" }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/auto-commit.sh" }
        ]
      }
    ]
  }
}

Every time Claude finishes a response, changes get committed automatically. Your git history stays clean with atomic commits per task instead of one massive "Claude changes" blob at the end of the day.

Combine this with claude -w feature-branch (worktrees) and you get isolated, auto-committed feature branches for every task.

8. 每个任务结束后自动提交

问题: Claude 做完一个任务,你忘了 commit。它又开始下一个任务,两个不相关的改动就混进了同一个提交。

Hook: Claude 停止处理一个任务时,自动提交所有改动。

创建 .claude/hooks/auto-commit.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/block-dangerous.sh" },
          { "type": "command", "command": ".claude/hooks/log-commands.sh" }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/protect-files.sh" }
        ]
      },
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/require-tests-for-pr.sh" }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0" },
          { "type": "command", "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0" }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/auto-commit.sh" }
        ]
      }
    ]
  }
}

每次 Claude 结束一次回复,改动都会自动提交。你的 git 历史会保持干净,每个任务一个原子提交,而不是一天结束时一坨 Claude changes 大杂烩。

再配合 claude -w feature-branch(worktrees),就能为每个任务拿到隔离的、自动提交的 feature 分支。

The complete settings.json

Here's everything combined into one file you can copy-paste:

Screenshot-friendly:

Copy this file into .claude/settings.json, create the hook scripts in .claude/hooks/, make them executable with chmod +x .claude/hooks/*.sh, and commit everything to git. Your whole team gets the same safety nets automatically.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/auto-commit.sh"
          }
        ]
      }
    ]
  }
}

The difference between a good Claude Code setup and a great one isn't the model or the prompts. It's the hooks.

They're the part that runs when you're not paying attention, catching the mistakes you'd otherwise find during code review or worse, in production.

Set up hook #1 (auto-format) and #2 (block dangerous commands) today. That alone will save you from the most common Claude Code mistakes. Add the rest as you need them.

I share daily notes on AI, finance, and vibe coding in my Telegram channel: https://t.me/zodchixquant

Thanks for reading 🙏🏼

```markdown Where hooks live:

.claude/settings.json project-level (shared via git) ~/.claude/settings.json user-level (all your projects) .claude/settings.local.json local only (not committed)

完整的 settings.json

下面是把所有内容合并后的一个文件,你可以直接复制粘贴:

方便截图:

把这个文件复制到 .claude/settings.json,在 .claude/hooks/ 里创建这些 hook 脚本,用 chmod +x .claude/hooks/*.sh 让它们可执行,然后把所有东西提交到 git。整个团队都会自动拥有同样的安全网。

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/auto-commit.sh"
          }
        ]
      }
    ]
  }
}

一套不错的 Claude Code 配置和一套很强的配置,差别不在模型,也不在提示词,而在 hooks。

它们会在你不注意的时候执行,把那些你本来要到 code review 才发现的错误,甚至更糟糕,到生产环境才发现的错误,提前兜住。

今天先把第 1 条(自动格式化)和第 2 条(阻止危险命令)配上。这两条就能挡住最常见的 Claude Code 失误。剩下的按需加就行。

Telegram 频道每天分享 AI、金融和 vibe coding 的笔记:https://t.me/zodchixquant

感谢阅读 🙏🏼

```markdown Where hooks live:

.claude/settings.json project-level (shared via git) ~/.claude/settings.json user-level (all your projects) .claude/settings.local.json local only (not committed)

Have you ever told Claude Code to do something and it just didn't?

You said format the code - It didn't. You said don't touch that file - It did.

You said run tests before finishing - It forgot.

That's because CLAUDE.md is a suggestion.

Claude reads it and follows it about 80% of the time. Hooks are different. They're automatic actions that fire every time Claude edits a file, runs a command, or finishes a task.

Below I will share 8 personal hooks you can copy straight into your settings.json and never think about again 👇

Before we dive in, I share daily notes on AI & vibe coding in my Telegram channel: https://t.me/zodchixquant🧠

How hooks work (30-second version)

What are hooks?

Hooks are automatic actions that run every time Claude Code does something, like editing a file or running a command.

You set them up once and they work in the background without you thinking about it.

The two you'll use most:

PreToolUse runs before Claude does something. You can inspect the action and block it by returning exit code 2. Think of it as a bouncer.

PostToolUse runs after Claude does something. You can run cleanup, formatting, tests, or logging. Think of it as quality control on the assembly line.

https://code.claude.com/docs/en/hooks

You configure them in .claude/settings.json in your project root. That file gets committed to git, so your whole team gets the same hooks automatically.

Full documentation: https://code.claude.com/docs/en/hooks

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run test --silent 2>&1 | tail -5; exit 0"
          }
        ]
      }
    ]
  }
}

1. Auto-format every file Claude touches

The problem: Claude writes correct code that breaks your formatting rules. You add "always run Prettier" to CLAUDE.md and it works most of the time, but not always.

The hook: Prettier runs automatically after every file write or edit.

#!/usr/bin/env bash
set -euo pipefail
file=$(jq -r '.tool_input.file_path // .tool_input.path // ""')

protected=(
  ".env*"
  ".git/*"
  "package-lock.json"
  "yarn.lock"
  "*.pem"
  "*.key"
  "secrets/*"
)

for pattern in "${protected[@]}"; do
  if echo "$file" | grep -qiE "^${pattern//\*/.*}$"; then
    echo "Blocked: '$file' is protected. Explain why this edit is necessary." >&2
    exit 2
  fi
done
exit 0

Swap npx prettier --write for whatever formatter you use: black for Python, gofmt for Go, rustfmt for Rust. The pattern is the same.

This was the first hook I set up and honestly it should be the default for every project. No more "Claude forgot to format" commits.

2. Block dangerous commands

The problem: Claude is powerful enough to run rm -rf, git reset --hard, DROP TABLE, or curl to random URLs. It probably won't, but "probably" isn't good enough when it's your production database.

The hook: Block destructive commands before they execute.

Create .claude/hooks/block-dangerous.sh:

Then add to your settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/require-tests-for-pr.sh"
          }
        ]
      }
    ]
  }
}

Exit code 2 is the key. It blocks the action and sends your error message back to Claude so it can try a safer approach. Exit code 0 means "go ahead." Anything else logs a warning but doesn't block.

3. Protect sensitive files from edits

The problem: Claude can read and edit any file in your project. That includes .env, package-lock.json, config files, and anything else you'd rather it didn't touch.

The hook: Block edits to files that should be off-limits.

Create .claude/hooks/protect-files.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/log-commands.sh"
          }
        ]
      }
    ]
  }
}

4. Run tests after every edit

The problem: Claude makes a change, says "done," and you discover the tests are broken 20 minutes later when you try to commit.

The hook: Run your test suite automatically after every code change. If tests fail, Claude sees the failure and can fix it immediately.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

The tail -5 keeps the output short so it doesn't flood Claude's context. You want Claude to see "3 tests failed" not the full 200-line test output.

Boris Cherny, the creator of Claude Code, says giving Claude a feedback loop like this improves output quality by 2-3x. Instead of writing code and hoping it works, Claude writes code, sees the test results, and fixes failures on its own.

5. Require passing tests before creating a PR

The problem: Claude finishes a feature and immediately creates a PR. Tests are failing. Your reviewer sees red CI and sends it back.

The hook: Block PR creation unless all tests pass.

Create .claude/hooks/require-tests-for-pr.sh:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0"
          }
        ]
      }
    ]
  }
}

This is a hard gate. No green tests, no PR. Claude will fix the failures first because exit code 2 tells it the action was blocked and why.

6. Auto-lint and report errors

The problem: Claude writes code that works but violates your ESLint rules, style guide, or type checks. You catch it during review and send it back.

The hook: Lint after every edit. If lint fails, Claude sees the errors and fixes them before you ever look at the code.

You can chain this with the auto-format hook from #1. Prettier runs first, then ESLint. By the time you see the code, it's formatted and lint-clean.

7. Log every command Claude runs

The problem: Claude runs a lot of shell commands during a session. If something goes wrong, you want to know exactly what it did and when.

The hook: Append every Bash command to a log file with timestamps.

Create .claude/hooks/log-commands.sh:

#!/usr/bin/env bash
set -euo pipefail
git add -A
if ! git diff --cached --quiet; then
  git commit -m "chore(ai): apply Claude edit"
fi
exit 0
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0"
          }
        ]
      }
    ]
  }
}

Now you have a timestamped audit trail of every command Claude ran. Add .claude/command-log.txt to your .gitignore so it doesn't pollute your repo.

This is especially useful for debugging: if Claude broke something three sessions ago, you can look at the log and find exactly when and what it ran.

8. Auto-commit after each completed task

The problem: Claude finishes a task and you forget to commit. Then it starts another task and now you have two unrelated changes mixed together in one commit.

The hook: Automatically commit all changes when Claude stops working on a task.

Create .claude/hooks/auto-commit.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/block-dangerous.sh" },
          { "type": "command", "command": ".claude/hooks/log-commands.sh" }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/protect-files.sh" }
        ]
      },
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/require-tests-for-pr.sh" }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0" },
          { "type": "command", "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0" }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/auto-commit.sh" }
        ]
      }
    ]
  }
}

Every time Claude finishes a response, changes get committed automatically. Your git history stays clean with atomic commits per task instead of one massive "Claude changes" blob at the end of the day.

Combine this with claude -w feature-branch (worktrees) and you get isolated, auto-committed feature branches for every task.

The complete settings.json

Here's everything combined into one file you can copy-paste:

Screenshot-friendly:

Copy this file into .claude/settings.json, create the hook scripts in .claude/hooks/, make them executable with chmod +x .claude/hooks/*.sh, and commit everything to git. Your whole team gets the same safety nets automatically.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/auto-commit.sh"
          }
        ]
      }
    ]
  }
}

The difference between a good Claude Code setup and a great one isn't the model or the prompts. It's the hooks.

They're the part that runs when you're not paying attention, catching the mistakes you'd otherwise find during code review or worse, in production.

Set up hook #1 (auto-format) and #2 (block dangerous commands) today. That alone will save you from the most common Claude Code mistakes. Add the rest as you need them.

I share daily notes on AI, finance, and vibe coding in my Telegram channel: https://t.me/zodchixquant

Thanks for reading 🙏🏼

```markdown Where hooks live:

.claude/settings.json project-level (shared via git) ~/.claude/settings.json user-level (all your projects) .claude/settings.local.json local only (not committed)

📋 讨论归档

讨论进行中…