第 7 章 · 01 事件与配置结构 本节摘要:Hooks 的骨架是"事件 → 配置 → handler"。31 个事件分四组:Tool(工具调用前后)、Session(会话启停与状态)、Task(任务/子代理/停止)、Lifecycle(工作树)。配置写在 settings 文件里,结构是 ;matcher 还能叠加 参数条件,精确到"哪个工具、哪类参数"。五种 handler 各有分工:command 跑脚本、http 发 webhook、prompt 让 LLM 评估、mcptool 直调 MCP 工具、agent 派子代理核查。 学习目标 阅读完本节,你应当能够: 说出 31 个事件四组分类,并指出哪些事件支持阻断。 说出 hooks 的五个配置位置与作用范围。
本节摘要:Hooks 的骨架是"事件 → 配置 → handler"。31 个事件分四组:Tool(工具调用前后)、Session(会话启停与状态)、Task(任务/子代理/停止)、Lifecycle(工作树)。配置写在 settings 文件里,结构是
事件名 → matcher(匹配工具) → hooks(处理器数组);matcher 还能叠加if参数条件,精确到"哪个工具、哪类参数"。五种 handler 各有分工:command 跑脚本、http 发 webhook、prompt 让 LLM 评估、mcp_tool 直调 MCP 工具、agent 派子代理核查。
阅读完本节,你应当能够:
if 条件的基本配置,并解释 matcher 与 if 的分工。Hooks 由事件驱动。31 个事件按触发面分为四组:
| 事件 | 触发时机 | 可阻断 | 典型用途 |
|---|---|---|---|
| UserPromptSubmit | 用户提交提示词 | ✅ | 校验提示词 |
| UserPromptExpansion | 提示词展开(@引用/斜杠命令解析后) | ✅ | 检查展开后的内容 |
| PreToolUse | 工具执行前 | ✅(allow/deny/ask) | 校验/改写工具输入 |
| PermissionRequest | 权限弹窗出现时 | ✅ | 自动批准/拒绝 |
| PermissionDenied | 用户拒绝权限 | ❌ | 日志/策略执行 |
| PostToolUse | 工具成功后 | ❌(可 block 反馈) | 补充上下文/反馈 |
| PostToolUseFailure | 工具失败后 | ❌ | 错误处理/日志 |
| PostToolBatch | 一批工具调用完成后 | ❌ | 聚合报告/批量校验 |
| Notification | 发送通知时 | ❌ | 自定义通知 |
| MessageDisplay | 助手消息显示时 | ❌ | 转换/隐藏显示文本 |
| Elicitation | MCP 服务器请求用户输入时 | ✅ | 输入校验 |
| ElicitationResult | 用户响应 Elicitation 后 | ✅ | 响应处理 |
| 事件 | 触发时机 | 可阻断 | 典型用途 |
|---|---|---|---|
| SessionStart | 会话开始/恢复/清除/压缩/分支 | ❌ | 环境初始化、持久化环境变量 |
| Setup | 每会话一次的环境设置 | ❌ | 安装依赖、准备工具 |
| InstructionsLoaded | CLAUDE.md 等加载后 | ❌ | 修改/过滤指令 |
| ConfigChange | 配置文件变化 | ✅(策略除外) | 响应配置更新 |
| CwdChanged | 工作目录变化 | ❌ | 按目录初始化 |
| DirectoryAdded | 会话中途新增工作目录 | ❌ | 为新目录准备工具链 |
| FileChanged | 被监听文件变化 | ❌ | 文件监控/重建 |
| PreCompact | 上下文压缩前 | ❌ | 压缩前动作 |
| PostCompact | 压缩完成后 | ❌ | 压缩后动作 |
| SessionEnd | 会话终止 | ❌ | 清理/最终日志 |
| 事件 | 触发时机 | 可阻断 | 典型用途 |
|---|---|---|---|
| SubagentStart | 子代理启动 | ❌ | 子代理初始化 |
| SubagentStop | 子代理结束 | ✅ | 子代理产出校验 |
| Stop | Claude 回复结束 | ✅ | 任务完成检查 |
| StopFailure | API 错误终止回合 | ❌ | 错误恢复/日志 |
| TeammateIdle | Agent 团队队员空闲 | ✅ | 队员协调 |
| TaskCompleted | 任务标记完成 | ✅ | 任务后动作 |
| TaskCreated | 任务创建 | ❌ | 任务跟踪/日志 |
| 事件 | 触发时机 | 可阻断 | 典型用途 |
|---|---|---|---|
| WorktreeCreate | 工作树创建中 | ✅(返回路径) | 工作树初始化 |
| WorktreeRemove | 工作树移除 | ❌ | 工作树清理 |
记忆口诀:Tool 管"用工具",Session 管"会话生灭",Task 管"任务与代理",Lifecycle 管"工作树"。能阻断的事件集中在"事情发生之前或刚结束时"——拦截窗口一旦错过,就只剩反馈权。
| 位置 | 范围 |
|---|---|
~/.claude/settings.json |
用户级,所有项目 |
.claude/settings.json |
项目级,可提交共享 |
.claude/settings.local.json |
本地项目级,不提交 |
| 受管策略(managed policy) | 组织级 |
插件 hooks/hooks.json |
插件作用域,用 ${CLAUDE_PLUGIN_ROOT} 引用插件目录 |
| 技能/代理 frontmatter | 组件生命周期钩子,与组件代码同处存放 |
组件级钩子支持的事件只有三个:PreToolUse、PostToolUse、Stop——把钩子直接写进 SKILL.md、agent.md、command.md 的 frontmatter,相关代码聚在一起。特别注意:子代理 frontmatter 里的 Stop 钩子会被自动转换为 SubagentStop 并限定在该代理上,只在它完成时触发,不会连累主会话。
{ "hooks": { "EventName": [ { "matcher": "ToolPattern", "hooks": [ { "type": "command", "command": "your-command-here", "timeout": 60 } ] } ] } }
关键字段:
| 字段 | 说明 | 示例 |
|---|---|---|
matcher |
匹配工具名的模式(区分大小写) | "Write"、"Edit|Write"、"*" |
hooks |
处理器数组,同一事件可挂多个 | [{ "type": "command", ... }] |
type |
handler 类型 | "command"/"prompt"/"http"/"mcp_tool"/"agent" |
command |
Shell 命令 | "$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh" |
timeout |
超时秒数(默认 60) | 30 |
once |
每会话只跑一次 | true |
| 模式 | 说明 | 示例 |
|---|---|---|
| 精确串 | 匹配特定工具 | "Write" |
| 正则 | 匹配多个工具 | "Edit|Write" |
| 逗号分隔 | 命中任一工具(v2.1.191+) | "Write,Edit" |
| 通配 | 全部工具 | "*" 或 "" |
| MCP 工具 | 服务器+工具模式 | "mcp__memory__.*" |
注意(v2.1.195+):matcher 是精确匹配,带连字符的 MCP 工具名不再误伤其他工具;逗号分隔写法在旧版本里是"静默永不触发"的 bug,升级后才生效。InstructionsLoaded 事件特殊,matcher 取 session_start/nested_traversal/path_glob_match 之一。
matcher 按工具名选钩子;要按工具参数过滤(比如只在编辑 src/ 下文件时触发,或拦截读密钥文件),在单个 handler 上加 if。matcher 决定"哪个工具",if 决定"哪次调用":
{ "hooks": { "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "if": "Edit(src/**)", "command": "./hooks/lint-src.sh" } ] }, { "matcher": "Read", "hooks": [ { "type": "command", "if": "Read(.env)", "command": "./hooks/block-secret-read.sh" } ] } ] } }
if 用权限规则语法(ToolName(pattern)),对工具名与参数一并求值;路径遵循 gitignore 语义,锚点与权限规则一致:裸名 .env 匹配任意深度、src/** 相对当前目录、/src/** 相对项目根、~/... 用户主目录、//... 绝对路径。常见写法:Edit(src/**)(编辑 src 下)、Read(~/.ssh/**)(读 SSH 密钥)、Bash(git push *)(仅 git push)。
allowedEnvVars 白名单,防止敏感变量泄漏到远端。沙箱启用时 HTTP 钩子走沙箱路由。Stop/SubagentStop 的"任务是否完成"智能检查。钩子脚本可用的环境变量:所有钩子都有 CLAUDE_PROJECT_DIR(项目根绝对路径);CLAUDE_ENV_FILE 只在 SessionStart/CwdChanged/FileChanged 有(用于持久化环境变量);CLAUDE_CODE_REMOTE 标识远端运行;插件钩子有 ${CLAUDE_PLUGIN_ROOT} 与 ${CLAUDE_PLUGIN_DATA};Bash 子进程还注入 CLAUDE_CODE_SESSION_ID(与钩子 JSON 的 session_id 对应)与 CLAUDE_EFFORT(当前 effort 等级)。
Hooks 的骨架一句话:31 个事件按 Tool/Session/Task/Lifecycle 四组分布,配置按"事件 → matcher → handlers"组织,matcher 选工具、if 选调用,五种 handler 从跑脚本到派子代理各有分工。能阻断的事件集中在"执行前/刚结束时",组件级钩子只有 PreToolUse/PostToolUse/Stop 三件套。下一节精读 8 个官方示例脚本,看真实钩子怎么落地。
下一节预告:第 2 节精读示例脚本——命令校验、安全扫描、自动格式化、智能 Stop、上下文追踪。