在前几篇文章里,我一直在整理自己使用 Claude Code 的经验。

从最开始的 Prompt,到后来的 CLAUDE.md,再到 Rules,我的目标其实一直很简单:让 Claude Code 在项目里表现得更稳定。

一开始我以为,只要把需求讲清楚,Claude Code 就能少犯很多错。后来发现确实如此。任务目标、项目背景、限制范围、验收标准写得越清楚,它越不容易乱跑。

但在真实项目里,这还不够。

因为很多规则不是某一次任务才需要,而是整个项目长期都需要。

比如:

  • 不要改敏感配置
  • 不要顺手重构无关代码
  • 修复前先分析原因
  • 修改后必须验证
  • 命令连续失败后不要一直重试
  • 不确定时先问,不要自己猜

这些内容每次都手动重复,非常低效。

于是我开始写 Rules。

1. Rules 能解决什么,不能解决什么

Rules 的效果是明显的。

它能把一些稳定要求沉淀下来,让 Claude Code 每次进入项目时都能看到。以前 Claude Code 的行为有点随机,同样一个任务,今天可能非常谨慎,明天可能非常激进。

写了 Rules 之后,它做事更像同一个人了。

但 Rules 不是万能的。

我后来发现,Claude Code 还是会出现一些问题:

  • 交付时说“已经完成”,但没有提供验证证据
  • 命令失败后继续尝试类似命令
  • 工具返回异常结果后没有停下来分析
  • 上下文变长后忘记某些关键限制
  • 对敏感文件和危险操作不够谨慎

最开始我以为,是 Rules 写得不够细。

于是我继续补规则。

不能动哪些文件,什么操作必须确认,失败几次要停下来,交付前必须跑测试,哪些情况不能直接说完成。

但规则越写越多,我越发现一个问题:

Rules 解决的是“模型倾向”,不是“系统强制”。

你在 Rules 里写“不要动 .env”,Claude Code 会更谨慎,但这不代表它永远不会碰。

你在 Rules 里写“交付前必须验证”,Claude Code 会更倾向于验证,但这不代表它每一次都会真的完成验证。

你在 Rules 里写“失败三次要停下来分析”,它可能会遵守,但也可能在复杂任务里滑过去。

所以我后来对 Rules 的定位变了。

Rules 适合定义工作习惯。

但不适合单独承担安全边界和强制检查。

这时候就需要 Hook。

2. Hook 的核心价值:不靠模型自觉

Hook 和 Rules 最大的区别在于:

Rules 是写给模型看的。

Hook 是写给系统跑的。

Rules 告诉 Claude Code:“你应该怎么做。”

Hook 则是在 Claude Code 生命周期的某个节点自动触发:“到这里了,就执行这个动作。”

这点非常关键。

因为模型是会忘的,也会误判,还会在上下文很长时忽略一部分规则。但 Hook 不是靠模型想起来才执行,而是由事件触发。

也就是说,Hook 更适合处理那些不能靠自觉完成的事情。

比如:

  • 工具执行前拦截危险命令
  • 修改文件后自动格式化
  • 工具失败后注入提醒
  • 任务结束后检查是否有验证证据
  • 会话开始时恢复上下文
  • 上下文压缩后重新注入关键规则

这些动作如果全部写进 Rules,也不是不行,但稳定性会差一些。

因为 Rules 的前提是:模型得记得,并且愿意执行。

Hook 的前提是:事件发生,自动触发。

3. Hook 可以按生命周期理解

Hook 的事件名称看起来比较多,但不用一开始就全部背下来。

我后来更喜欢按 Claude Code 的任务生命周期理解 Hook。

大致可以分成四类。

3.1 开始前:SessionStart / UserPromptSubmit

这一类 Hook 发生在任务真正执行之前。

SessionStart 可以在会话开始或恢复时触发。

UserPromptSubmit 可以在用户提交提示词之后、Claude 正式处理之前触发。

适合做什么?

适合做初始化和输入检查。

比如:

  • 加载项目上下文
  • 注入关键规则
  • 检查当前工作目录
  • 提醒当前项目限制
  • 判断用户需求是否涉及高风险操作
  • 在执行前要求补充必要信息

这类 Hook 解决的是“任务开始前先摆正方向”。

很多时候 Claude Code 跑偏,不是因为它不会写代码,而是它一开始就没有站在正确上下文里。

如果某些上下文每次都需要提醒,那就可以考虑放到开始前的 Hook 里。

3.2 执行前:PreToolUse

PreToolUse 是我认为最重要的一类 Hook。

它会在工具执行前触发。

比如 Claude Code 准备执行 Bash 命令,准备编辑文件,准备读取某些内容,这时候 PreToolUse 可以先检查这个操作是否安全。

适合做什么?

适合做安全拦截和权限判断。

比如:

  • 禁止读取或修改 .env
  • 禁止触碰密钥文件
  • 禁止修改生产配置
  • 禁止执行 rm -rf
  • 禁止 force push
  • 禁止 reset hard
  • 对高风险操作要求用户确认

这些场景不应该只写在 Rules 里。

因为一旦出错,代价太高。

安全边界应该尽量放在工具执行前,而不是事后发现。

换句话说:

危险操作,能在 PreToolUse 拦,就不要只靠 Rules 提醒。

3.3 执行后:PostToolUse

PostToolUse 会在工具执行成功后触发。

这一类 Hook 适合做重复动作和结果处理。

比如:

  • 代码修改后自动格式化
  • 命令执行后记录日志
  • 工具返回空结果时提醒
  • 工具返回异常时注入警告
  • 编辑文件后触发检查
  • 统计工具调用次数

这类动作有一个共同点:它们不需要模型创造性思考。

格式化就是格式化。

记录就是记录。

失败检测就是失败检测。

越是这种固定、重复、机械的动作,越适合 Hook。

因为它不应该依赖模型“记得”。

3.4 结束后:Stop / StopFailure / SessionEnd

这一类 Hook 发生在 Claude Code 准备结束响应或会话时。

我自己最早真正用起来的,就是 Stop Hook。

原因也很简单:Claude Code 太喜欢说“已经完成”了。

但“已经完成”到底是什么意思?

是代码改了?

是测试跑了?

是构建通过了?

是问题复现过了?

还是只是它觉得自己应该完成了?

这就是 Stop Hook 的价值。

它可以在 Claude Code 结束任务前,要求它做最后检查。

比如:

  • 是否有验证证据
  • 是否说明改了哪些文件
  • 是否跑了必要命令
  • 是否有失败项
  • 是否有未验证内容
  • 是否需要提醒用户下一步手动确认

这类 Hook 不会让 Claude Code 变成完美工程师,但能减少“看起来完成了,其实没完成”的情况。

4. 哪些地方应该用 Hook,甚至必须用 Hook

我的判断标准很简单。

如果只是希望 Claude Code 更稳定,可以先写 Rules。

如果不能接受 Claude Code 忘记,就应该考虑 Hook。

具体来说,我觉得这几类场景非常适合用 Hook。

4.1 安全边界

安全边界优先级最高。

比如:

  • 敏感文件
  • 密钥
  • 生产配置
  • 数据库结构
  • 删除操作
  • Git 高风险命令
  • 外部网络请求

这些操作一旦出错,不是“下次注意”就能解决的。

所以它们不适合只放在 Rules 里。

正确思路应该是:

Rules 里写原则。

Hook 里做拦截。

比如 Rules 可以写“不要修改敏感配置”,但 PreToolUse 里应该真正判断:当前工具调用是否试图访问敏感文件。如果是,就拦住。

4.2 交付验证

交付验证也非常适合 Hook。

Claude Code 最常见的问题之一,就是把“代码改完了”当成“任务完成了”。

但在真实开发里,完成至少应该包含验证。

比如:

  • build 是否通过
  • test 是否通过
  • lint 是否通过
  • 类型检查是否通过
  • Bug 是否复现并确认修复
  • 修改范围是否符合预期

这些检查不应该只靠 Claude Code 最后自觉汇报。

Stop Hook 可以作为最后一道验收门。

它不一定替你完成所有测试,但至少可以要求 Claude Code 明确说明:验证了什么,没验证什么,结果是什么。

4.3 重复动作

重复动作也很适合 Hook。

比如:

  • 自动格式化
  • 自动记录日志
  • 自动统计失败次数
  • 自动通知
  • 自动恢复上下文
  • 自动注入规则

这些动作本身不复杂,但很容易被忘。

而且越是高频操作,忘一次的成本就越高。

如果一个动作每次都要做,那就不要每次都提醒模型做。

把它变成 Hook。

4.4 模型容易自信但不可靠的地方

还有一类问题比较隐蔽。

不是 Claude Code 不会做,而是它有时候会错得很自然。

比如:

  • 命令失败了,但它继续尝试类似方案
  • 工具返回空结果,它当成正常结果继续分析
  • 测试没跑完,它误以为成功
  • 修改了多个文件,但没有检查影响范围
  • 上下文压缩后,忘了前面的关键限制

这类问题如果靠人工盯,会很累。

Hook 可以在这些节点加入提醒或拦截。

它不能保证所有问题都消失,但可以让错误不那么容易一路滑到底。

5. 我建议先从 3 个 Hook 开始

如果你还没用过 Hook,不建议一开始就写很多。

可以先从 3 个最实用的开始。

第一个:Stop Hook

解决问题:

Claude Code 说完成了,但你不知道它到底有没有验证。

适合动作:

  • 要求输出验证证据
  • 要求列出修改文件
  • 要求说明执行过的命令
  • 要求说明未验证内容
  • 要求标记风险点

这是最适合新手开始的 Hook。

因为它直接解决“已完成不可信”的问题。

第二个:PreToolUse Hook

解决问题:

Claude Code 可能执行高风险操作。

适合动作:

  • 拦截敏感文件
  • 拦截危险命令
  • 拦截强制推送
  • 拦截删除操作
  • 对高风险操作要求确认

这是安全边界的第一道门。

如果你的项目里有 .env、密钥、生产配置,建议尽早加。

第三个:PostToolUse Hook

解决问题:

Claude Code 执行完工具后,可能忘记处理后续动作。

适合动作:

  • 自动格式化
  • 自动记录工具结果
  • 检测失败输出
  • 检测空结果
  • 统计工具调用次数
  • 触发后续检查

这个 Hook 适合把重复动作自动化。

6. 结论:Rules 是提醒,Hook 是刹车

我现在不会说 Rules 不重要。

恰恰相反,Rules 很重要。

它能让 Claude Code 形成稳定的工作习惯,让它知道项目里应该怎么做、不应该怎么做。

但 Rules 不能替代 Hook。

因为规则是提醒,Hook 是系统动作。

当一件事只是“希望它做得更好”,Rules 足够。

当一件事是“不能接受它忘记”,就应该考虑 Hook。

尤其是安全边界、交付验证、重复动作和高代价错误。

这些地方不能只靠模型自觉。

Claude Code 很强,但它不是永远可靠的执行者。真正稳定的用法,不是期待它每次都完美发挥,而是把关键节点变成流程,把高风险动作放进拦截,把必须验证的事情变成系统检查。

我以前总想让 Claude Code 更听话。

后来我发现,更重要的是让它更可控。

如果你已经开始写 Rules,下一步可以试试 Hook。

先别追求复杂。

先问自己一个问题:

我的项目里,哪件事最不能靠 Claude Code 自觉?

然后,把它做成 Hook。

更多推荐