CrabNote螃蟹笔记

8 个 Claude Code Hooks 实战(译)

8 个 Claude Code Hooks 实战(译)

8 个 Claude Code Hooks 实战(译) 8 个 Claude Code Hooks 实战(译) Modified April 5 Code block JSON Copy { "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "npx eslint fix $(jq r '.tool input.file path') 2 &1 | tail 10; exit 0" } ] } ] } } 跟 1 搭配:Prettier 管格式,ESLint 管规则。到手就是干净的。 日志 :上个月 Claude 在某次会话里覆盖了一个配置文件,我三天后才发现。问题是我不知道是哪次会话、哪条命令干的。翻了一下午 commit 也没头绪。从那以后我装了这个: .claude/hooks/log commands.sh: Code block Bash Copy !/usr/bin/env bash set euo pipefail cmd=$(jq r '.tool input.command // ""') printf '%s %s\n' "$(date Is)" "$cmd" .claude/command log.txt exit 0 Code block JSON Copy { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": ".claude/hooks/log commands.sh" } ] } ] } } .claude/command log.txt 加 .gitignore。出事打开,精确到秒。 8. 自动 commit 放最后是因为这个最有争议。在我团队引发了一场小战争。 反对派说:自动 commit?这是对 git 的亵渎。commit message 呢?review 呢? 我说:你是想要每个任务一个干净 commit,还是要一天结束时那个叫 "various changes" 的垃圾山? 吵了半天,最后怎么着?两派各用各的,谁也没服谁。到今天还是这样。 .claude/hooks/auto commit.sh: Code block Plain Text Copy !/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 Code block JSON Copy { "hooks": { "Stop": [ { "matcher": "", "hooks": [ { "type": "command", "command": ".claude/hooks/auto commit.sh" } ] } ] } } 配合 claude w feature branch(worktrees),每个任务一个隔离分支。用过之后 git 历史第一次变得可读了。 你可以不喜欢。但建议先试一周再下结论。 完整 settings.json 这里把所有内容放一起,方便复制。 复制到 .claude/settings.json,脚本放 .claude/hooks/,chmod +x .claude/hooks/ .sh,commit。全组同步。 Code block JSON Copy { "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" } ] } ] } } 今天先装 1 和 2。 就这俩,最常见的翻车已经干掉一大半。 实践哥:翻译完这里我突然想到一件事。 一年前我还在嘲笑那些不会用 AI 的同事。现在呢?我每天的工作流是:让 AI 写代码,然后检查 AI 写的代码,然后给 AI 装护栏防止它写出问题代码。你的 title 还是 Software Engineer,但你每天干的事越来越像 AI Babysitter。 Hooks 就是这个转变里唯一让我觉得舒服的部分——至少 babysitting 这件事,也可以自动化。 这个变化好不好?老实说我也没想明白。但它已经在发生了,不管你接不接受。 最后,那个问题我是真想听你的答案:该不该让 AI 自动 commit? 我见过两派人为这事差点闹翻。 A. 该,反正 squash merge 时会压掉,过程 commit 无所谓 B. 不该,commit 是工程师最后的体面 C. 我有更野的操作——说来听听 跟 1 搭配:Prettier 管格式,ESLint 管规则。到手就是干净的。 日志 :上个月 Claude 在某次会话里覆盖了一个配置文件,我三天后才发现。问题是我不知道是哪次会话、哪条命令干的。翻了一下午 commit 也没头绪。从那以后我装了这个: .claude/hooks/log commands.sh: .claude/command log.txt 加 .gitignore。出事打开,精确到秒。 8. 自动 commit 放最后是因为这个最有争议。在我团队引发了一场小战争。 反对派说:自动 commit?这是对 git 的亵渎。commit message 呢?review 呢? 我说:你是想要每个任务一个干净 commit,还是要一天结束时那个叫 "various changes" 的垃圾山? 吵了半天,最后怎么着?两派各用各的,谁也没服谁。到今天还是这样。 .claude/hooks/auto commit.sh: 配合 claude w feature branch(worktrees),每个任务一个隔离分支。用过之后 git 历史第一次变得可读了。 你可以不喜欢。但建议先试一周再下结论。 完整 settings.json 这里把所有内容放一起,方便复制。 复制到 .claude/settings.json,脚本放 .claude/hooks/,chmod +x .claude/hooks/ .sh,commit。全组同步。 今天先装 1 和 2。 就这俩,最常见的翻车已经干掉一大半。 实践哥:翻译完这里我突然想到一件事。 一年前我还在嘲笑那些不会用 AI 的同事。现在呢?我每天的工作流是:让 AI 写代码,然后检查 AI 写的代码,然后给 AI 装护栏防止它写出问题代码。你的 title 还是 Software Engineer,但你每天干的事越来越像 AI Babysitter。 Hooks 就是这个转变里唯一让我觉得舒服的部分——至少 babysitting 这件事,也可以自动化。 这个变化好不好?老实说我也没想明白。但它已经在发生了,不管你接不接受。 最后,那个问题我是真想听你的答案:该不该让 AI 自动 commit? 我见过两派人为这事差点闹翻。 A. 该,反正 squash merge 时会压掉,过程 commit 无所谓 B. 不该,commit 是工程师最后的体面 C. 我有更野的操作——说来听听 🔗 原文链接: https://x.com/MinLiBuilds/status/20... https://x.com/MinLiBuilds/status/20... 上周四晚上十一点多,我正准备关电脑。 瞄了一眼 git diff,发现 Claude 把我的 .env 改了。加了一行调试变量。本身没啥——但我当时连着生产库。 导读:用了仨月 Claude Code,之前天天帮它擦屁股。后来发现 Hooks 之后 10 分钟设好,之前手动检查的活全自动了。回不去了。原文 下面是翻译+实测。 你有没有过这种经历:让 Claude Code 做某件事,它就是不做? 你说"格式化代码"——它没做。你说"别碰那个文件"——它碰了。 你说"完成前跑一下测试"——它忘了。 原因在于,CLAUDE.md 只是一个建议。 Claude 读了它,大概 80% 的时间会遵守。但 Hooks 不同。它们是自动触发的动作——每当 Claude 编辑文件、执行命令或完成任务时,都会自动执行。 下面 8 个 Hooks,直接复制到 settings.json,设完就忘。 30 秒搞懂原理 Hooks = 绑在 Claude Code 动作上的脚本。设一次,后台一直跑。 • PreToolUse :Claude 动手之前。exit code 2 = 拦住。门卫。 • PostToolUse :Claude 动手之后。跑格式化、跑测试。质检员。 文档: https://code.claude.com/docs/en/hooks 1. 自动格式化 Python 换 black,Go 换 gofmt,Rust 换 rustfmt。 之前在 CLAUDE.md 里写了"永远运行 Prettier"。管用,但偶尔会忘。偶尔忘一次就够你在 PR 里丢一次脸了。 装完之后再没出过格式问题。所有项目标配,没有之一。 2. 拦截危险命令 Claude 真的会跑 rm rf。概率低,但概率低跟零之间,隔着你的生产数据库。 创建.claude/hooks/block dangerous.sh: 再往 settings.json 添加: exit code 2 = 出错拦截,0 = 放行,这个很简单,告诉 Claude 原因出错了,并让它换方案。 装了这个之后晚上终于能睡踏实了。之前有段时间做梦都梦到 Claude 跑了个什么奇怪的命令,醒来第一件事就是打开终端检查。你说荒不荒谬,一个程序员被自己的工具搞出 PTSD 了。 3. 敏感文件上锁 Claude 重构的时候顺手改了 package lock.json。后面两个小时我都在跟诡异的依赖冲突搏斗,最后发现就是它动了不该动的文件。 停一下。 这个 Hook 干的事说白了就一个字:防。 有人会觉得矛盾——你用 AI 写代码,又防着它?你花钱请它来帮忙,结果第一件事是给它画红线? 但我越想越觉得,这可能就是我们这代程序员跟 AI 打交道的常态了。你离不开它,但你也不敢完全放手。就像你第一次把车钥匙交给刚拿驾照的孩子——你坐在副驾上,脚一直悬在刹车上方。 区别在于,Hooks 让你可以把脚从刹车上挪开。因为刹车已经自动化了。 顺便说: 不会拖慢 Claude 。毫秒级的事,实测过。 说真的,我一开始也觉得没必要搞这些。我的项目又不大,Claude 又挺聪明的。直到那天晚上 .env 的事。10 分钟设好这些 Hook,之后它们拦住的那一次事故,可能值你一整天。 顺便问一句——你用 Claude Code 多久了?有没有被它坑过?我特别好奇有多少人跟我有类似经历。 4 & 5. 测试这件事,我踩了两次坑才学乖 第一次:Claude 改完代码说"搞定了"。我信了。20 分钟后要 commit,测试全红。 第二次:Claude 写完功能一激动直接建了 PR。CI 全红。Reviewer 留了俩字:"认真?" 那个 Reviewer 是我 lead。当时恨不得找个地缝钻进去。 两个坑,两个 Hook。 改完代码自动跑测试: tail 5 只留最后几行,别让 200 行日志撑爆 context。 建 PR 前强制测试通过: .claude/hooks/require tests for pr.sh: Claude Code 创造者 Boris Cherny 说过,这种实时反馈能让输出质量翻 2 3 倍。我自己的体感是确实快了不少,Claude 修 bug 不再是"盲猜"了。 6. 自动 Lint + 7. 审计日志 这俩不像前面几个有什么惊天大坑,但不装你会在小地方持续被恶心到。 Lint :有一次 merge 之后队友给我发了个白眼 emoji,一个字没说。我看了半天才明白——ESLint 没跑,一堆规则违反。那种被无声审判的感觉,比被骂还难受。加上了: