← 返回 金色时刻微光照亮的开发者桌,带机械键盘的双显示器显示终端输出,和一杯冷茶

Claude Code Hooks:我希望早点设置的自动化层

三个月。我就这样让 Claude Code hooks 躺在文档里没人看,同时抱怨 AI 生成的代码无视我的格式约定。我在一月份设置了 Claude Code,几乎立即开始用它发布功能,然后告诉自己"稍后再处理 hooks"。太典型了。

事实证明 hooks 不是锦上添花。它们是缺失的一块拼图,让 Claude Code 真正融入真实的代理工作流,而不是一个偶尔忽视 ESLint 的非常昂贵的自动完成。

Claude Code Hooks 实际上是什么(没有模糊抽象)

Hooks 是 Claude Code 在其自身操作的特定点触发的 shell 命令。把它们想象成生命周期事件,类似于 Git hooks(如果你写过 pre-commit 脚本的话),但连接到 AI 的工具使用循环而不是 Git 的提交管道。

目前有四种事件类型:

  • PreToolUse,在 Claude 调用任何工具前运行(文件编辑、bash 命令等)
  • PostToolUse,在工具调用完成后运行
  • Notification,在 Claude 发送通知时触发
  • Stop,在 Claude 完成完整响应轮次时运行

你在项目的 .claude/ 文件夹内的 settings.json 文件中配置它们,或者在 ~/.claude/settings.json 全局配置(如果你想在所有地方都用)。每个 hook 都有一个 matcher(哪个工具或事件触发它)和一个 hooks 数组来存放要运行的 shell 命令。

你的 hook 输出会被反馈到 Claude 的上下文中。这最后一点是让它真正有趣的原因,而不仅仅是个花哨的 cron 工作。

为什么这不同于自己手动运行脚本

你可以在每次 Claude 编辑后手动运行 Prettier。我试过,大约两周,直到在一个截止日期的压力下忘记了,推送了一个有 47 个格式违规的 PR。Hooks 自动运行,在会话内运行,Claude 可以读取它们的输出。所以如果你的 linter 抛出警告,Claude 看到它并可以在同一会话中采取行动。这个反馈循环才是全部要点。

在 Seahawk 项目中真正有效的设置

我要在这里具体说明,因为大多数文章中你能找到的笼统"在配置中添加 hooks"的建议如果没有上下文就毫无用处。

在 Seahawk,我们工作的很大一部分是 WordPress 构建和 WooCommerce 定制。我们也为 headless 设置做 React 前端,自 2022 年以来一直大量使用 Next.js。我最终采用的 hook 配置解决了特定于这些堆栈的问题。

以下是我用于 Next.js 项目的 settings.json 结构:

`` { "hooks": { "PostToolUse": [ { "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "npx prettier --write $CLAUDE_FILE_PATHS && npx eslint --fix $CLAUDE_FILE_PATHS" } ] } ], "Stop": [ { "matcher": ".*", "hooks": [ { "type": "command", "command": "npx tsc --noEmit 2>&1 | head -20" } ] } ] } } ``

PostToolUse hook 在 Claude 接触文件后立即触发 Prettier 和 ESLint。Stop hook 在每轮末尾运行 TypeScript 类型检查,并显示任何错误的前 20 行。Claude 读取该输出,如果有类型错误,它在我看到响应之前自行修复。

仅那个 TypeScript 检查上个月就为我节省了可能四小时,在一个金融科技仪表板项目中,客户设置了严格的 noImplicitAny。Claude 不断在实用函数中生成 any 类型。在我添加 Stop hook 后,它开始在同一轮内自我修正。

我在 WordPress / PHP 项目中使用的钩子

WordPress 是另一回事。显然没有 TypeScript,但 PHP_CodeSniffer 配合 WordPress 编码标准规则集能让一切保持理智。2022 年我有个初级开发者在 WooCommerce 项目上,两周没运行过 PHPCS。代码审查……不太愉快。

对于 PHP 繁重的项目,我的 PostToolUse 钩子运行:

`` vendor/bin/phpcs --standard=WordPress $CLAUDE_FILE_PATHS 2>&1 | tail -30 ``

我用一个 bash 命令的 PreToolUse 钩子来配套:

`` { "matcher": "Bash", "hooks": [ { "type": "command", "command": "echo 'Bash tool triggered' >> ~/.claude/audit.log && date >> ~/.claude/audit.log" } ] } ``

第二个纯粹是偏执。它把 Claude 运行的每个 bash 命令都写入审计日志。当你在实时预发布环境运行 Claude Code 时(是的,我做过,是的有点冒险),知道确切执行了哪些 shell 命令真的很安心。

用钩子退出代码阻止行为

文档的这部分花了我不少时间才找到。如果你的钩子以代码 2 退出,Claude Code 会把它视为阻止,不会继续执行工具调用。退出代码 0 表示成功,任何非零值(但不是 2)会把标准错误作为上下文反馈回来。

所以你可以写一个 PreToolUse 钩子来真正阻止 Claude 做某事。我在有 migrations/ 目录且不想让 Claude 自动触及的项目中用这个:

`` #!/bin/bash if echo "$CLAUDE_FILE_PATHS" | grep -q "migrations/"; then echo "Migrations folder is protected. Do not edit migration files autonomously." exit 2 fi exit 0 ``

那个脚本位于 .claude/hooks/guard-migrations.sh。当 Claude 尝试写入 migrations/ 下的任何内容时,它会被阻止并看到消息。然后它会让我先确认再继续。简单有效。

这种控制力才是区分"我多少信任这个 AI 处理我的代码库"和"我真的信任它"的关键。

值得借鉴的实际钩子模式

这些不是理论。每一个都来自具体的痛点。

  1. 文件编辑后自动运行测试。我在 PostToolUse 钩子中运行 npx jest --testPathPattern=$CLAUDE_FILE_PATHS --passWithNoTests。它只运行 Claude 刚编辑的文件相关的测试,不是整个套件。快到不烦人的程度。
  2. 提交就绪的格式化快照。一个 Stop 钩子运行 git diff --stat 并把摘要反馈给 Claude。它能看到整个会话中的确切变化,这帮助它在我要求时写出合理的提交消息。
  3. 环境变量安全检查。Write 上的 PreToolUse 钩子,用 grep 搜索硬编码的密钥模式(看起来像 API 密钥或密码的东西)。如果发现可疑内容,退出代码 2。我应该在 18 个月前就构建这个。
  4. 长任务通知钩子。当 Claude 发送通知(Notification 事件)时,我触发一个 curl 调用到 Pushover 端点,在手机上收到推送通知。当你启动大型重构去泡茶时真的很有用。
  5. Bash 前的 PHP 语法检查。在 WordPress 项目上,任何 bash 执行前进行快速 php -l $CLAUDE_FILE_PATHS。在语法错误破坏预发布服务器前捕获它们。

官方 Claude Code 钩子文档列出了钩子脚本内可用的所有环境变量的完整参考。值得收藏。

钩子无法解决的问题

诚实很重要。钩子不是解决 Claude 生成逻辑错误代码的方案。它们解决流程问题:格式化、linting、类型安全、测试覆盖。如果 Claude 误解了你的数据模型并构建了错误功能,再多的后编辑 linting 都抓不住。

钩子也会增加延迟。如果你的 Prettier + ESLint 通过需要四秒钟,每次文件编辑现在需要多花四秒。在一个有 200 次文件编辑的会话中,那是 13 分钟的等待。分析你的钩子命令。保持它们快速。我用 --fix 变体(原地修改文件)而不是仅报告变体,正是因为单次快速通过胜过慢通过加第二次纠正通过。

它们需要你提前思考项目的失败模式。如果 Claude 编辑错误的文件会怎样?哪些标准绝对必须执行?无论如何这种思考都有价值,但它确实意味着钩子对经验丰富的开发者比初学者更有帮助。

设置钩子:分步指南

对于从零开始的任何人:

  1. 如果项目根目录中不存在 .claude/ 文件夹,请创建一个。
  2. 添加一个 settings.json 文件,其中包含你的钩子配置(结构如上所示)。
  3. 对于任何超过一行的代码,请编写一个单独的 shell 脚本(.claude/hooks/your-script.sh),chmod +x 它,然后从配置中调用它,而不是内联命令。
  4. 通过在你的项目中运行 claude 并故意触发钩子条件来测试。读取会话上下文中返回的内容。
  5. 如果某些内容没有按预期触发,请检查 ~/.claude/logs/ 中的钩子执行日志。

Anthropic 开发者文档涵盖了完整的设置模式。如果你在思考钩子如何融入更广泛的 AI 编码工作流,Simon Willison 的博客是我推荐给任何想更仔细思考真实项目中代理 AI 工具的人的地方。

我最初犯的一个错误:我把所有钩子放在全局 ~/.claude/settings.json 中,然后想知道为什么我的 PHP 钩子在 JavaScript 项目上触发了。项目级设置会覆盖全局设置。将特定技术栈的钩子放在项目的 .claude/settings.json 中,将全局配置保留给应该应用于所有地方的内容(如审计日志和通知钩子)。

FAQ

Claude Code 钩子在 Windows 上工作吗?

钩子命令在你的系统使用的任何 shell 中运行。在 Windows 上,默认是 PowerShell 或 CMD,这意味着 bash 风格的脚本无法原生工作。WSL2 是实际的解决方案。我在 macOS 和 Ubuntu 开发机上工作,所以我没有亲身经历过这个问题,但 Anthropic 的文档明确指出了 shell 依赖。

钩子能访问 Claude 的对话上下文吗?

不能直接访问。钩子作为 shell 命令运行,接收环境变量如 CLAUDE_FILE_PATHS CLAUDE_TOOL_NAME,但不会获得完整的对话记录。它们能做的是向 stdout 写入输出,Claude 在钩子运行后将其作为上下文读取。

钩子会明显减慢我的 Claude Code 会话吗?

完全取决于你的钩子做什么。对单个文件进行 php -l 语法检查不到 100 毫秒。在每次文件编辑时运行完整的 Jest 套件会很烦人。保持单个钩子命令在两到三秒内,你几乎不会注意到延迟。

在生产环境中使用钩子安全吗?

我会反过来问这个问题:你在生产环境中直接运行 Claude Code 吗?如果是的话,钩子是你最不用担心的问题。在暂存环境中使用它们,使用 PreToolUse 阻止模式保护敏感目录,让 Claude 完全远离生产数据库。

项目级钩子和全局钩子有什么区别?

全局钩子位于 ~/.claude/settings.json 中,适用于你的机器上的每个 Claude Code 会话。项目级钩子位于特定项目内的 .claude/settings.json 中,仅在你在该项目中时触发。项目级设置在两者都定义相同事件时优先。

---

诚实总结:钩子不是很性感。没人会写一篇关于运行 Prettier 的 shell 脚本的美妙架构的博客文章。但它们决定了 Claude Code 是原型玩具还是你实际上信任用于客户工作的东西。我应该在第一天就设置它们。你可能也应该这样做。

← 返回