Hooks(钩子)是 Claude Code 中在特定生命周期节点自动触发的用户自定义逻辑——可以是 Shell 命令、HTTP 接口,也可以是 LLM Prompt。本文基于官方 Hooks 参考文档,梳理 Hook 的运行环境、事件节奏,以及各类事件的适用场景。
Hook 能在哪些地方运行
Hooks 在 Claude Code 运行的所有场景下都会触发同一套事件:
- 终端会话
- IDE 插件(VS Code 等)
- 桌面应用
- Claude Code on the web
这意味着你写的 Hook 逻辑是跨界面通用的——不管用户是在终端、IDE 还是网页版里使用 Claude Code,只要触发了对应事件,Hook 就会执行,不需要为不同界面单独适配。
Hook 处理器如何接收数据
Command hooks(命令型) → 事件的 JSON 上下文通过 stdin 传入
HTTP hooks(接口型) → JSON 上下文作为 POST 请求体传入
你的处理器可以检查这份输入、执行相应动作,并且可以选择性地返回一个决策(比如拦截某个操作)。
三种事件触发节奏
官方把所有 Hook 事件按触发频率分成三类,理解这个分类对于选对 Hook 类型非常关键:
每个会话一次:
SessionStart, SessionEnd
每一轮对话一次:
UserPromptSubmit, Stop, StopFailure
每次工具调用都会触发(agentic loop 内部):
PreToolUse, PostToolUse
(EndConversation 调用会跳过这两个事件)
关键 Hook 事件速查
SessionStart 会话开始或恢复时触发
Setup 用 --init-only,或 -p 模式下配合 --init/
--maintenance 启动时触发;用于 CI/脚本中
的一次性准备工作
UserPromptSubmit 你提交一条 prompt、Claude 开始处理之前触发
UserPromptExpansion 用户输入的命令展开成 prompt、到达 Claude
之前触发;可以阻止这次展开
PreToolUse 工具调用执行之前触发
PostToolUse 工具调用执行之后触发
Stop / StopFailure 每轮对话结束(或失败结束)时触发
SessionEnd 会话结束时触发
从完整的生命周期图来看,还有更多细分事件——比如 PermissionRequest(权限请求)、PermissionDenied(auto mode 下的拒绝分支)、WorktreeCreate/WorktreeRemove(工作树创建/移除)、FileChanged、ConfigChange、SubagentStart/Stop、TaskCreated/TaskCompleted 等,覆盖了从工具调用到子代理管理、从文件变化到配置变更的几乎所有关键节点。
该用哪个 Hook:几个典型场景
场景:每次会话开始时自动加载项目特定的上下文信息
→ 用 SessionStart
场景:拦截某类危险的工具调用(比如删除生产数据库的命令)
→ 用 PreToolUse,在执行前做检查并决定是否放行
场景:每次文件修改后自动跑一次 Linter
→ 用 PostToolUse,在工具执行后触发检查
场景:强制要求某个检查通过才能结束当前轮次
→ 用 Stop hook,作为确定性的硬门禁
(前文《验证闭环》一文中提到的"确定性硬门禁"策略,
正是通过 Stop hook 实现的)
场景:CI 流水线里做一次性环境准备
→ 用 Setup,配合 --init-only 或 -p 模式的 --init/--maintenance
实战建议
1. 明确你要控制的是"某个动作发生之前"还是"之后",
决定用 PreToolUse 还是 PostToolUse
2. 需要跨会话持久生效的规则(比如安全策略),
优先考虑放在 PreToolUse 而不是散落在各个 prompt 里
3. 涉及"必须通过才能算完成"的强约束场景,
Stop hook 是比口头要求更可靠的落地方式
4. Hook 处理器的输入输出格式(JSON schema)建议先查官方
参考文档确认字段结构,避免因格式不对导致 Hook 静默失效
总结
理解 Hooks 生命周期的关键,不在于记住每一个事件名,而在于建立起"这个事件多久触发一次、在流程的哪个阶段触发"这个心智模型——一旦想清楚你要控制的是会话级、轮次级还是工具调用级的行为,选对 Hook 类型就会变得很直观。
来源:Hooks reference — Claude Code 官方文档,Anthropic