# Hook 与 CLI ## Hook → 子命令映射 | Claude Code 事件 | 子命令 | 作用 | |------------------|--------|------| | `UserPromptSubmit` | `notify save` | 记录前台窗口、prompt、WT 标签、调用方图标路径 | | `Stop` | `notify notify` | 弹"任务完成"通知(自动消失,聚焦时更短) | | `Notification` | `notify input` | 弹"需要输入"通知(常驻),按类型分标题 | | `PreToolUse`(`AskUserQuestion`/`ExitPlanMode`) | `notify input` | 提问 / 出 Plan 时弹常驻通知 | | `SessionEnd` | `notify cleanup` | 删除该会话状态文件 | `hooks/hooks.json`(插件形式)里命令为 `${CLAUDE_PLUGIN_ROOT}/bin/notify.exe <子命令>`;直连 `settings.json` 时可写 `notify <子命令>` 或绝对路径。 ## stdin JSON Claude Code 通过 **stdin** 把事件数据以 JSON 传入。`HookInput` 关心这几个字段: | 字段 | 用途 | |------|------| | `session_id` | 状态文件隔离;为空则忽略本次 | | `prompt` | UserPromptSubmit 的用户输入,用作"完成"通知正文 | | `notification_type` | Notification 类型:`permission_prompt` / `idle_prompt` / `elicitation_dialog` / … | | `message` | Notification / 提问的文本 | | `tool_name` | PreToolUse 工具名:`AskUserQuestion` / `ExitPlanMode` | > **注意**:stdin 用 `OpenStandardInput()` 读**原始字节**再按 **UTF-8** 解码。不能用 `Console.In`——WinExe 下它不可靠,且会用控制台代码页(中文系统是 GBK)把中文解成乱码。 ## 标题分流(input) | 条件 | 标题 | |------|------| | `tool_name == AskUserQuestion` | Claude is Asking | | `tool_name == ExitPlanMode` | Plan Ready for Approval | | `notification_type == permission_prompt` | Permission Required | | `notification_type == idle_prompt` | Claude is Waiting | | `notification_type == elicitation_dialog` | MCP Asks | | 其它 | Input Required | 被**过滤**(不弹)的类型:`auth_success` / `elicitation_complete` / `elicitation_response`。 ## 状态文件 路径:`%TEMP%\claude-notify-{session_id}.json`(`session_id` 做了文件名安全过滤)。 ```jsonc { "Hwnd": 329712, // 触发时前台窗口句柄 "Prompt": "重构通知模块", // 完成通知正文 "WtRuntimeId": "42.288...",// WT 当前标签 RuntimeId(非 WT 为空) "CallerExePath": "...\\WindowsTerminal.exe" // 调用方 App,用于取图标 } ``` - `notify`/`input` 读它来填 `NotifyMessage`(含点击要激活的 `TargetHwnd`、要切的标签、要显示的图标)。 - `cleanup` 删它。若 `SessionEnd` 没触发(崩溃等),文件会残留在 `%TEMP%`,无害。 ## 消息清洗 `notify`/`input` 投递前会把正文里的换行 / 制表 / 多余空格折叠成单行,避免撑乱 toast 布局;超长部分由 toast 的两行省略号截断。