一眼看懂:27 个事件,5 类职责
来自 HOOK_EVENTS 常量,是 rebuild 里能被 hook 系统识别的事件全集。
command、prompt、http、agent。另有内部 callback hook。
工具权限、会话生命周期、上下文工作区、子代理任务、交互询问。
Ralph Loop 主要加工的是 Stop:用 block 决策阻止结束并回灌 prompt。
关系图:hook 在一次会话里的位置
Session lifecycle
Tool and permission path
Context and workspace path
Agent, task and elicitation path
分组统计
工具与权限
围绕工具调用前、调用后、失败和权限拒绝建立控制点。
PermissionRequest · PreToolUse · PostToolUse · PostToolUseFailure · PermissionDenied会话生命周期
把启动、用户输入、通知、停止和退出变成可拦截事件。
Setup · SessionStart · UserPromptSubmit · Notification · Stop · StopFailure · SessionEnd上下文与工作区
覆盖 compact、说明加载、目录变化、文件变化和 worktree 生命周期。
PreCompact · PostCompact · InstructionsLoaded · CwdChanged · FileChanged · WorktreeCreate · WorktreeRemove子代理与任务
用于观察 subagent、task、teammate 与配置变化。
SubagentStart · SubagentStop · TaskCreated · TaskCompleted · TeammateIdle · ConfigChange交互询问
处理 runtime 向外发起的询问和结果回收。
Elicitation · ElicitationResultHook 输出能控制什么
展示信息
systemMessage 可向用户显示 hook 给出的提示、警告或状态。
阻止继续
continue:false 或部分事件里的 decision:"block" 可打断流程。
注入上下文
hookSpecificOutput.additionalContext 可把检查结果送回模型上下文。
修改工具输入
PreToolUse、PermissionRequest 可带 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 重新送回模型。