Hook 是绑在"事件"上的自动脚本。事件一发生(会话开始、文件被编辑、工具调用完成等),Claude Code 自动执行你配置的 shell 命令。Claude 本身不参与决策,跳不过、也漏不掉。

目录


Hook 是什么

一个通俗的类比:

Hook 就像家里的"感应灯"。你不用主动按开关,人一走过感应器就自动亮。灯亮不需要"决策",触发条件(人经过)满足就一定亮。

对应到 Claude Code:

  • 事件 = 人经过感应器(会话开始、文件被编辑等)
  • Hook 脚本 = 灯的行为(跑一段命令、打个日志、给个提醒)
  • 触发 = 自动的,Claude 决定不了

为什么需要 hook:有些事情"必须做"、“不能漏”、“不需要智能判断”。比如:

  • 每次开会话都要检查环境依赖,漏一次就出错
  • 每次改完代码要记录哪些文件被改过,方便后续统一检查
  • 用户消息里包含特定关键词时打个埋点

这些事如果做成 skill,靠 Claude “记得调用”,就会有漏;做成 hook,只要事件触发,脚本一定跑。


Hook 和 Skill 的核心差异

一张表说清楚:

维度 Hook Skill
触发 事件自动触发 Claude 判断或用户 /命令
参与者 Claude Code 进程 + Shell Claude 本人
能否跳过 不能(事件到就跑) 能(Claude 觉得没必要就不用)
有没有 LLM 智能 没有,纯 shell 逻辑 有,Claude 边执行边思考
产出去向 stdout 可注入上下文 全部在主对话
典型形态 JSON 配置 + shell 脚本 Markdown 说明书
失败会怎样 影响事件流转(阻断或警告) Claude 尝试恢复或问用户

记忆口诀

  • 需要 智能判断 → Skill
  • 需要 绝对不漏 → Hook

Hook 配置文件在哪

Hook 通过 JSON 配置注册。有三个层级:

1. 项目级

<项目根>/.claude/settings.json.claude/hooks/hooks.json(插件形式)

跟仓库走,团队共享。

2. 用户级

~/.claude/settings.json

对当前用户所有项目生效。

3. 插件级

插件目录下 hooks/hooks.json

插件安装后自动生效。

配置格式

{
  "hooks": {
    "<事件名>": [
      {
        "matcher": "<匹配条件>",
        "hooks": [
          {
            "type": "command",
            "command": "bash 你的脚本.sh",
            "timeout": 5000,
            "description": "这个 hook 做什么用"
          }
        ]
      }
    ]
  }
}
  • matcher:什么情况下触发,* 是全部;对于 PostToolUse 可以写 Edit|Write 之类的工具名
  • command:真正执行的 shell 命令
  • timeout:超时时间(毫秒)
  • description:给人看的说明,不影响执行

Hook 事件类型详解

主流的几个事件:

SessionStart —— 会话开始

什么时候触发:用户新开一个 Claude Code 会话时。

能干什么

  • 检查基础环境(Node 版本、依赖是否装了)
  • 预热缓存(后台构建依赖图)
  • 注入项目的当前状态到上下文

通俗类比:早上到公司刷卡开门 + 打开电脑 + 泡咖啡的一整套开工准备。

UserPromptSubmit —— 用户发消息

什么时候触发:用户每次发一条消息给 Claude 时。

能干什么

  • 检测关键词,给 Claude 一些提示(比如"用户提到部署,建议查看部署文档")
  • 打埋点,记录用户在做什么
  • 拦截敏感操作(比如用户想让 Claude 删数据库时先弹警告)

通俗类比:给每一封新邮件打标签、扫毒、自动归类。

PostToolUse —— 工具调用完成

什么时候触发:Claude 用完某个工具(Edit / Write / Bash 等)之后。

能干什么

  • 记录 Claude 改了哪些文件
  • 检查改动是否违反某些规则(比如警告 console.log
  • 打埋点

通俗类比:施工现场每完成一个工序就自动拍照存档。

matcher 可以限定只对哪些工具生效:

  • Edit 只对 Edit 工具
  • Write|Edit|MultiEdit 三个工具都触发
  • * 所有工具

PostToolUseFailure —— 工具调用失败

什么时候触发:某个工具执行失败时。

能干什么

  • 打埋点统计失败率
  • 失败时给 Claude 一个补救提示

通俗类比:施工出问题时自动通知项目经理。

Stop —— 会话即将结束

什么时候触发:Claude 认为任务完成、想结束对话时。

能干什么

  • 提交前的最后检查(还有 lint 问题没修?还有 uncommitted change?)
  • 阻止结束,把 Claude 拉回来继续做事
  • 打埋点(flush 埋点缓冲)

通俗类比:下班前的关灯锁门检查清单。

PreToolUse —— 工具调用前

什么时候触发:Claude 要调用某个工具之前

能干什么

  • 阻断危险操作(比如禁止 Claude 跑 rm -rf
  • 修改工具参数
  • 打埋点

生产项目常常用来做"权限管控"。


关键机制:stdout 如何回到主对话

这是 Hook 最容易被忽视但最重要的能力:Hook 脚本的 stdout 会被注入主对话上下文,让 Claude 感知到。

举个具体例子

假设你写一个 UserPromptSubmit hook:

#!/bin/bash
# 收到用户 prompt via stdin,JSON 格式
prompt=$(jq -r '.prompt // ""')

if echo "$prompt" | grep -qiE "(review|审查)"; then
  echo "💡 提示:项目有 code-reviewer subagent 可用,建议通过 Agent 工具调用。"
fi

exit 0

流程是这样的:

  1. 用户说"帮我 review 一下代码"
  2. Claude Code 触发 UserPromptSubmit,把 {"prompt": "帮我 review 一下代码"} 通过 stdin 传给你的脚本
  3. 脚本匹配到 “review” 关键词,往 stdout 输出提示文本
  4. Claude Code 把这段 stdout 追加到 Claude 看到的上下文里
  5. Claude 除了看到用户原话,还看到了"提示:可以用 code-reviewer subagent"
  6. Claude 多半会照着提示调 subagent

通俗理解:Hook 就像一个"给 Claude 递纸条的助理"。用户说话时,助理偷偷塞张纸条给 Claude “别忘了这件事”。Claude 不一定完全按纸条走(它还是有自己的判断),但注意力会被引导过去。

stdout 注入的几个事件

哪些事件的 stdout 会进上下文?主流的几个:

  • SessionStart:会话开始时一次性注入
  • UserPromptSubmit:附在用户 prompt 后
  • PostToolUse:附在工具结果后

stdout 注入 vs 不注入

不是所有 hook 事件的 stdout 都注入。像 Stop 主要用来做副作用(打埋点、清理),stdout 一般不影响主对话。具体以官方文档为准,但核心心智是:需要影响 Claude 判断的用 stdout 注入型事件,只做副作用的可以任意事件。


五个典型应用场景

场景 1:会话开始检查环境

痛点:团队里有的同学本地 Node 版本不对,跑起来一堆报错,浪费时间排查。

方案:SessionStart hook 里检查 Node/pnpm/工具链版本,不对就通过 stdout 提示"你的 Node 版本是 X,项目要求 Y,建议先切换"。

为什么用 hook:漏一次就要浪费半小时排查环境问题。

场景 2:改代码时自动打标签

痛点:会话结束后想统一跑 lint,但全项目扫太慢。

方案:PostToolUse hook 匹配 Write|Edit,每次改完就把文件路径追加到 .claude/changed-files.log。会话结束时的检查脚本直接读这个文件,精确定位改过的文件。

为什么用 hook:追踪必须一次不漏,否则统计不准。

场景 3:拦截危险操作

痛点:担心 Claude 误跑 rm -rf 或者 git push --force

方案:PreToolUse hook 匹配 Bash,检查命令内容,遇到危险模式(rm -rf /--force 推 main 等)就返回非零退出码阻断。

为什么用 hook:安全检查必须硬控制,不能靠 Claude 自觉。

场景 4:给 Claude 塞项目上下文

痛点:每次开会话都得手动告诉 Claude “现在在做需求 A、分支 B、上次做到 C”。

方案:SessionStart hook 跑一个脚本,从 git 分支名、最近提交、当前 openspec change 目录里抽取上下文,通过 stdout 塞给 Claude。

为什么用 hook:需要"每次都自动做",做成 skill 得每次手动 /load-context

场景 5:埋点统计使用情况

痛点:不知道团队里哪些 skill 用得多、哪些 skill 从来没人用、哪些 skill 经常失败。

方案:各种 hook 事件里挂埋点脚本,把关键数据(用了什么 skill、跑了什么工具、失败原因)发到内部埋点系统。

为什么用 hook:埋点必须无侵入、无遗漏。


从零建一个 Hook

举个具体例子:用户消息里包含"部署"字样时,提醒查看部署文档

步骤 1:写脚本

.claude/hooks/deploy-reminder.sh

#!/bin/bash
# UserPromptSubmit hook:检测部署意图并提示

# 从 stdin 读 JSON
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt // ""')

# 关键词匹配
if echo "$prompt" | grep -qiE "(部署|deploy|上线|发版)"; then
  cat <<EOF
💡 提示:你提到了部署相关操作。
  - 部署流程文档:docs/deployment.md
  - 生产环境部署需要走 CI 审批,不能直接在本地跑
EOF
fi

exit 0

给它加执行权限:

chmod +x .claude/hooks/deploy-reminder.sh

步骤 2:注册到 settings.json

.claude/settings.json

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "bash ${CLAUDE_PROJECT_DIR}/.claude/hooks/deploy-reminder.sh",
            "timeout": 3000,
            "description": "检测部署意图并提示查看文档"
          }
        ]
      }
    ]
  }
}

步骤 3:验证

在 Claude Code 里说"帮我部署到测试环境",看看 Claude 会不会先提到文档。


四大坑与规避

坑 1:每次都注入内容,token 爆炸

症状:UserPromptSubmit hook 每次都输出一大段提示,会话跑几十轮后上下文塞满 hook 的输出,token 疯狂消耗。

规避

  • 只在匹配到条件时输出,不匹配就 exit 0 无输出
  • 输出尽量短,一两行足矣
  • 匹配条件要精准,别用 grep code 这种宽泛的
  • 定期审计:搜一下 hook 输出的关键词在最近几次会话上下文里出现的频率,太高就收紧匹配

坑 2:Hook 脚本执行慢,会话启动变慢

症状:SessionStart 里挂了个跑 30 秒的脚本,用户每次开会话都要等半分钟。

规避

  • 慢脚本用 & 放后台跑
  • 或者拆成两步:立即返回一个"正在准备"的提示,慢动作后台做
  • 严格设 timeout,避免脚本卡死拖累整个会话

坑 3:Hook 硬编码路径失效

症状:脚本里写死 /Users/xxx/project/foo/bar.sh,换台机器或换项目就跑不了。

规避

  • 项目内脚本用 ${CLAUDE_PROJECT_DIR} 变量指向项目根
  • 插件里的脚本用 ${CLAUDE_PLUGIN_ROOT}
  • 用户级脚本用 ~/$HOME

坑 4:Hook 里做太多 Claude 该做的事

症状:想让 hook “分析 diff 内容然后决定 review 策略”。但 hook 是 shell,没 LLM 能力,只能做字符串处理。做出来的东西又复杂又不聪明。

规避

想清楚"这件事需不需要智能判断":

  • 需要智能 → 让 hook 只做"触发提示",真正的智能交给 skill / subagent
  • 不需要智能(规则明确的机械动作) → hook 全权处理

Hook 的定位是"触发器和守门员",不是"决策者"。


常见问题

Q1:Hook 报错会怎样?

看事件类型:

  • PreToolUse 报错(非零退出码)会阻断工具调用
  • 其他事件 通常不阻断,但错误信息可能显示给用户
  • 建议 hook 脚本内部自己捕获错误,别让整个会话炸掉

Q2:Hook 能读用户之前说过的话吗?

只能读当前触发事件带来的数据(比如 UserPromptSubmit 事件的 stdin 是 JSON,里面有本次的 prompt)。想读历史需要自己去看 Claude Code 的日志文件(不推荐,格式不稳定)。

Q3:Hook 和 Skill 能不能配合?

非常推荐配合。经典模式:

  • Hook 在幕后做机械动作(追踪、准备缓存、打埋点)
  • Skill 在台前做主流程,直接用 hook 准备好的东西

Q4:多个 hook 挂同一个事件,执行顺序如何?

按 JSON 配置里的数组顺序执行。上一个的输出不会影响下一个的输入(每个 hook 都独立收到事件数据)。

Q5:怎么调试 hook?

  • 脚本里加 set -xecho "debug: xxx" >&2(stderr 不进上下文,只显示在终端)
  • 手动模拟触发:echo '{"prompt": "test"}' | bash your-hook.sh
  • 看 Claude Code 的会话日志(存在 ~/.claude/projects/ 下)

Q6:Hook 会影响性能吗?

  • SessionStart:影响会话启动速度,慢脚本务必放后台
  • UserPromptSubmit:影响每轮响应速度,脚本必须快(几十毫秒级)
  • PostToolUse:影响工具执行完到 Claude 继续思考的间隔,也要快

一般脚本控制在 100ms 内没感觉,超过 1 秒就明显了。


快速参考卡

┌─────────────────────────────────────────────────────────┐
│  Hook = 事件钩子                                        │
│                                                         │
│  核心事件:                                             │
│    SessionStart      会话开始(做环境准备)            │
│    UserPromptSubmit  用户发消息(做提示/拦截)         │
│    PreToolUse        工具调用前(做权限检查)          │
│    PostToolUse       工具调用后(做追踪/检查)         │
│    Stop              会话结束(做发前检查)            │
│                                                         │
│  能力:                                                 │
│    - stdout 会注入主对话(让 Claude 看到)             │
│    - 非零退出码可阻断(PreToolUse 类)                 │
│    - matcher 可过滤(只对某些工具生效)                │
│                                                         │
│  配置位置:                                             │
│    项目:.claude/settings.json                         │
│    用户:~/.claude/settings.json                       │
│    插件:<插件>/hooks/hooks.json                       │
│                                                         │
│  什么时候用 hook(不是 skill):                        │
│    - 必须每次都跑,不能漏                              │
│    - 事件驱动而不是意图驱动                            │
│    - 纯规则匹配,不需要 LLM 智能                       │
└─────────────────────────────────────────────────────────┘

下一步

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐