3.6 另一个有用的Hook
除了课程中讨论的 PreToolUse 和 PostToolUse,Claude Code 还支持更多类型的 Hooks:
Notification:当 Claude Code 发送通知时触发(如需要工具使用权限,或空闲 60 秒后)。
Stop:当 Claude Code 完成回应时触发。
SubagentStop:当子代理(在界面中显示为“Task”)完成时触发。
PreCompact:在进行 compact 操作前触发(手动或自动)。
UserPromptSubmit:当用户提交提示词、在 Claude 处理之前触发。
SessionStart:会话开始或恢复时触发。
SessionEnd:会话结束时触发。
难点:不同 Hook 的输入结构不同
你的命令接收的 stdin 输入会因 Hook 类型(PreToolUse、PostToolUse、Notification 等)而变化。
对于 PreToolUse 和 PostToolUse,stdin 中的 tool_input 会因具体被调用的工具而不同。
示例:监听 TodoWrite 工具的 PostToolUse Hook 的 stdin 输入:
{
“session_id”: “…”,
“transcript_path”: “<path_to_transcript>”,
“hook_event_name”: “PostToolUse”,
“tool_name”: “TodoWrite”,
“tool_input”: {
“todos”: [{ “content”: “write a readme”, “status”: “pending”, “priority”: “medium”, “id”: “1” }]
},
“tool_response”: {
“oldTodos”: [],
“newTodos”: [{ “content”: “write a readme”, “status”: “pending”, “priority”: “medium”, “id”: “1” }]
}
}对比:Stop Hook 的输入:
{
“session_id”: “…”,
“transcript_path”: “<path_to_transcript>”,
“hook_event_name”: “Stop”,
“stop_hook_active”: false
}
可见,不同 Hook 类型及其 matcher(针对 PreToolUse、PostToolUse)会导致命令接收到的 stdin 结构显著不同,这会让编写 Hooks 变得困难,因为你可能不清楚命令的输入结构。
解决思路:辅助 Hook 记录输入
可以创建一个辅助 Hook,把输入记录到文件以便查看:
“PostToolUse”: // 或 “PreToolUse”、”Stop” 等
[
{
“matcher”: “*”,
“hooks”: [
{
“type”: “command”,
“command”: “jq . > post-log.json”
}
]
}
]
该命令会把当前 Hook 的输入写入 post-log.json,便于你精确检查命令将接收到的数据,从而更容易确定你的命令应当解析与使用哪些字段。