Claude Code runtime hooks 27 events

Hook Runtime 专题
把 Claude Code 的钩子系统画清楚

Hook 不是模型本身的魔法,而是 runtime/harness 在关键事件点触发的回调系统。 这页把 claude-code-rebuild 中的 hook 事件、作用、输出控制与 Ralph Loop 的用法收束到一张图里。

来源文件 src/entrypoints/sdk/coreTypes.ts src/types/hooks.ts src/schemas/hooks.ts src/utils/plugins/loadPluginHooks.ts

一眼看懂:27 个事件,5 类职责

27 runtime hook event

来自 HOOK_EVENTS 常量,是 rebuild 里能被 hook 系统识别的事件全集。

4 可配置 hook 类型

commandprompthttpagent。另有内部 callback hook。

5 事件分组

工具权限、会话生命周期、上下文工作区、子代理任务、交互询问。

1 Ralph Loop 关键钩子

Ralph Loop 主要加工的是 Stop:用 block 决策阻止结束并回灌 prompt。

关系图:hook 在一次会话里的位置

Session lifecycle

Setup SessionStart UserPromptSubmit Stop SessionEnd

Tool and permission path

PermissionRequest PreToolUse Tool runs PostToolUse
PermissionDenied PostToolUseFailure Notification

Context and workspace path

PreCompact PostCompact InstructionsLoaded CwdChanged FileChanged WorktreeCreate WorktreeRemove

Agent, task and elicitation path

SubagentStart SubagentStop TaskCreated TaskCompleted TeammateIdle ConfigChange Elicitation ElicitationResult

分组统计

5

工具与权限

围绕工具调用前、调用后、失败和权限拒绝建立控制点。

PermissionRequest · PreToolUse · PostToolUse · PostToolUseFailure · PermissionDenied
7

会话生命周期

把启动、用户输入、通知、停止和退出变成可拦截事件。

Setup · SessionStart · UserPromptSubmit · Notification · Stop · StopFailure · SessionEnd
7

上下文与工作区

覆盖 compact、说明加载、目录变化、文件变化和 worktree 生命周期。

PreCompact · PostCompact · InstructionsLoaded · CwdChanged · FileChanged · WorktreeCreate · WorktreeRemove
6

子代理与任务

用于观察 subagent、task、teammate 与配置变化。

SubagentStart · SubagentStop · TaskCreated · TaskCompleted · TeammateIdle · ConfigChange
2

交互询问

处理 runtime 向外发起的询问和结果回收。

Elicitation · ElicitationResult

Hook 输出能控制什么

展示信息

systemMessage 可向用户显示 hook 给出的提示、警告或状态。

阻止继续

continue:false 或部分事件里的 decision:"block" 可打断流程。

注入上下文

hookSpecificOutput.additionalContext 可把检查结果送回模型上下文。

修改工具输入

PreToolUsePermissionRequest 可带 updatedInput

Hook 清单:按分组筛选

Hook 分组 触发点 典型用途
PermissionRequest工具权限权限提示前允许、拒绝、修改输入或更新权限。
PreToolUse工具权限工具执行前安全检查、拦截危险 Bash、改写工具输入。
PostToolUse工具权限工具成功后格式化文件、跑测试、把结果注入上下文。
PostToolUseFailure工具权限工具失败后失败诊断、记录日志、补充修复提示。
PermissionDenied工具权限权限被拒绝时解释拒绝原因、建议替代操作、触发 retry。
Setup会话生命周期runtime 准备阶段初始化环境、预加载上下文。
SessionStart会话生命周期session 开始注入初始上下文、设置文件监听路径。
UserPromptSubmit会话生命周期用户提交 prompt检查输入、补充上下文、阻止不合规请求。
Notification会话生命周期通知事件转发提醒、记录运行时状态。
Stop会话生命周期Claude 准备停止收尾检查、阻止结束、Ralph Loop 的循环入口。
StopFailure会话生命周期停止流程失败诊断 Stop hook 或收尾流程异常。
SessionEnd会话生命周期session 结束归档、清理、统计。
PreCompact上下文工作区compact 前提醒保存关键事实、检查摘要约束。
PostCompact上下文工作区compact 后拿到 summary 后补上下文或写入记忆。
InstructionsLoaded上下文工作区指令加载后检查项目指令、追加运行时约束。
CwdChanged上下文工作区工作目录变化更新监听路径、切换项目上下文。
FileChanged上下文工作区被监听文件变化刷新索引、触发检查、同步外部状态。
WorktreeCreate上下文工作区创建 worktree初始化分支工作区、登记路径。
WorktreeRemove上下文工作区移除 worktree清理状态、释放监听。
SubagentStart子代理任务subagent 启动注入任务上下文、登记 worker。
SubagentStop子代理任务subagent 停止汇总结果、收敛子任务状态。
TaskCreated子代理任务任务创建写任务台账、触发外部看板。
TaskCompleted子代理任务任务完成归档产物、更新进度、通知协作者。
TeammateIdle子代理任务teammate 空闲分配下一步、维持协作循环。
ConfigChange子代理任务配置变化热加载 hook/plugin 配置。
Elicitation交互询问runtime 发起询问自动接受、拒绝、取消或补内容。
ElicitationResult交互询问询问结果返回记录用户选择、驱动后续策略。

实战配方:社区验证过的三条护栏

Hooks 不是模型层面的魔法,是代码层面的硬拦截。prompt 里的「别删我 .env」模型今天听明天忘,但 exit 2 不会。

以下三条配方来自社区重度用户的生产实践,覆盖「拦截危险操作」「自动化收尾」「防失忆」三类核心场景。来源:微信公众号「彭涛聊 Python」

配方一:给 rm -rf 装死闸

事件:PreToolUse · 匹配:Bash(rm *)

#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
  exit 2
else
  exit 0
fi

要点:exit 2 是唯一的「拒绝」信号;exit 1 只会警告,不会拦截。v2.1 之后 if: "Bash(rm *)" 可限定子命令匹配范围,避免每次工具调用都 spawn 进程。

配方二:编辑完自动格式化

事件:PostToolUse · 匹配:Edit|Write

#!/bin/bash
FILE=$(jq -r '.tool_input.file_path')
case "$FILE" in
  *.ts|*.tsx|*.js|*.jsx) npx prettier --write "$FILE" ;;
  *.py) ruff format "$FILE" ;;
  *.go) gofmt -w "$FILE" ;;
esac

matcher 用 Edit|Write 覆盖所有文件操作。v2.1.119 后 PostToolUse 输入带 duration_ms,想做性能监控可直接 log 该字段,无需自己计时。

配方三:compact 后回灌上下文

事件:SessionStart · 匹配:compact

#!/bin/bash
echo "# Reminder: use pnpm, not npm. Run pnpm test before commit."
echo ""
echo "# Recent commits:"
git log --oneline -10
echo ""
echo "# Current branch: $(git branch --show-current)"
echo ""
echo "# Open TODOs:"
rg "TODO" --type py -c | head -20

长 session 跑 6 小时以上,compact 后的 Claude 常「失忆」。hook 的 stdout 会被自动塞回上下文。静态提醒或动态 git/rg 输出均可,看项目习惯。

进阶:prompt-based 与 MCP tool 调用

v2.1 之后 hook 类型不限于 shell 脚本,prompt-based 让另一个 LLM 做判断:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "prompt",
        "if": "Bash(git commit*)",
        "prompt": "Check if the pending changes include any hardcoded secrets, API keys, or passwords. If yes, respond with 'BLOCK: '. If clean, respond with 'ALLOW'."
      }]
    }]
  }
}

v2.1.118 起支持 type: "mcp_tool",例如 Stop 触发时自动向 Linear 创建 ticket 记录 session 产出。hook 的事件点 × 触发类型组合空间很大,重度用户值得投入。

Ralph Loop 在这张图里的位置

Ralph Loop 不是直接调用 Claude API

它利用 Claude Code 的 Stop hook:当 Claude 准备结束时,hook 脚本读取本地状态、检查完成承诺和迭代次数。如果任务未完成,就返回 decision:"block"reason,runtime 因此阻止停止,并把 reason 重新送回模型。

Claude wants to stop Stop hook check promise and iteration decision:block reason re-enters model context