68 68
69要了解事件、匹配器和处理程序如何组合在一起,请考虑这个 `PreToolUse` hook,它阻止破坏性 shell 命令。69要了解事件、匹配器和处理程序如何组合在一起,请考虑这个 `PreToolUse` hook,它阻止破坏性 shell 命令。
70 70
71**macOS/Linux**71> [tab] macOS/Linux
72 72>
73`matcher` 缩小到 Bash 工具调用,`if` 条件进一步缩小到匹配 `rm *` 的 Bash 子命令,因此 `block-rm.sh` 仅在两个过滤器都匹配时生成:73> `matcher` 缩小到 Bash 工具调用,`if` 条件进一步缩小到匹配 `rm *` 的 Bash 子命令,因此 `block-rm.sh` 仅在两个过滤器都匹配时生成:
74 74>
75```json theme={null}75> ```json theme={null}
76{76> {
77 "hooks": {77> "hooks": {
78 "PreToolUse": [78> "PreToolUse": [
79 {79> {
80 "matcher": "Bash",80> "matcher": "Bash",
81 "hooks": [81> "hooks": [
82 {82> {
83 "type": "command",83> "type": "command",
84 "if": "Bash(rm *)",84> "if": "Bash(rm *)",
85 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",85> "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
86 "args": []86> "args": []
87 }87> }
88 ]88> ]
89 }89> }
90 ]90> ]
91 }91> }
92}92> }
93```93> ```
94 94>
95该脚本从 stdin 读取 JSON 输入,提取命令,如果包含 `rm -rf`,则返回 `permissionDecision` 为 `"deny"`。将其保存到项目中的 `.claude/hooks/block-rm.sh`,并使用 `chmod +x .claude/hooks/block-rm.sh` 使其可执行,以便 Claude Code 可以运行它:95> 该脚本从 stdin 读取 JSON 输入,提取命令,如果包含 `rm -rf`,则返回 `permissionDecision` 为 `"deny"`。将其保存到项目中的 `.claude/hooks/block-rm.sh`,并使用 `chmod +x .claude/hooks/block-rm.sh` 使其可执行,以便 Claude Code 可以运行它:
96 96>
97```bash theme={null}97> ```bash theme={null}
98#!/bin/bash98> #!/bin/bash
99# .claude/hooks/block-rm.sh99> # .claude/hooks/block-rm.sh
100COMMAND=$(jq -r '.tool_input.command')100> COMMAND=$(jq -r '.tool_input.command')
101 101>
102if echo "$COMMAND" | grep -q 'rm -rf'; then102> if echo "$COMMAND" | grep -q 'rm -rf'; then
103 jq -n '{103> jq -n '{
104 hookSpecificOutput: {104> hookSpecificOutput: {
105 hookEventName: "PreToolUse",105> hookEventName: "PreToolUse",
106 permissionDecision: "deny",106> permissionDecision: "deny",
107 permissionDecisionReason: "Destructive command blocked by hook"107> permissionDecisionReason: "Destructive command blocked by hook"
108 }108> }
109 }'109> }'
110else110> else
111 exit 0 # no decision; normal permission flow applies111> exit 0 # no decision; normal permission flow applies
112fi112> fi
113```113> ```
114 114>
115此脚本与本页面上解析 JSON 输入的其他 Bash 示例一样,使用 `jq`,因此在尝试它们之前,请安装 `jq` 并确保它在您的 `PATH` 上。115> 此脚本与本页面上解析 JSON 输入的其他 Bash 示例一样,使用 `jq`,因此在尝试它们之前,请安装 `jq` 并确保它在您的 `PATH` 上。
116
117**Windows (PowerShell)**
118
119匹配器 `Bash|PowerShell` 涵盖 [PowerShell 工具](#powershell)以及 Bash。单个 `if` 规则仅匹配一个工具的调用,因此每个工具都有自己的处理程序:第一个缩小到匹配 `rm *` 的 Bash 子命令,第二个缩小到匹配 `Remove-Item *` 的 PowerShell 命令。两者都通过 `powershell.exe` 运行相同的脚本:
120
121```json theme={null}
122{
123 "hooks": {
124 "PreToolUse": [
125 {
126 "matcher": "Bash|PowerShell",
127 "hooks": [
128 {
129 "type": "command",
130 "if": "Bash(rm *)",
131 "command": "powershell.exe",
132 "args": [
133 "-NoProfile",
134 "-ExecutionPolicy",
135 "Bypass",
136 "-File",
137 "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
138 ]
139 },
140 {
141 "type": "command",
142 "if": "PowerShell(Remove-Item *)",
143 "command": "powershell.exe",
144 "args": [
145 "-NoProfile",
146 "-ExecutionPolicy",
147 "Bypass",
148 "-File",
149 "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
150 ]
151 }
152 ]
153 }
154 ]
155 }
156}
157```
158
159`-NoProfile` 标志跳过加载您的 PowerShell 配置文件,以便 hook 快速启动,`-ExecutionPolicy Bypass` 让 PowerShell 运行本地脚本文件。
160
161该脚本从 stdin 读取 JSON 输入,提取命令,如果包含 `rm -rf` 或 `Remove-Item` 后跟 `-Recurse`,则返回 `permissionDecision` 为 `"deny"`。将其保存到项目中的 `.claude/hooks/block-rm.ps1`:
162
163```powershell theme={null}
164# .claude/hooks/block-rm.ps1
165$callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
166$command = $callInput.tool_input.command
167 116
168if ($command -match 'rm -rf|Remove-Item.*-Recurse') {117> [tab] Windows (PowerShell)
169 @{118>
170 hookSpecificOutput = @{119> 匹配器 `Bash|PowerShell` 涵盖 [PowerShell 工具](#powershell)以及 Bash。单个 `if` 规则仅匹配一个工具的调用,因此每个工具都有自己的处理程序:第一个缩小到匹配 `rm *` 的 Bash 子命令,第二个缩小到匹配 `Remove-Item *` 的 PowerShell 命令。两者都通过 `powershell.exe` 运行相同的脚本:
171 hookEventName = "PreToolUse"120>
172 permissionDecision = "deny"121> ```json theme={null}
173 permissionDecisionReason = "Destructive command blocked by hook"122> {
174 }123> "hooks": {
175 } | ConvertTo-Json124> "PreToolUse": [
176} else {125> {
177 exit 0 # no decision; normal permission flow applies126> "matcher": "Bash|PowerShell",
178}127> "hooks": [
179```128> {
129> "type": "command",
130> "if": "Bash(rm *)",
131> "command": "powershell.exe",
132> "args": [
133> "-NoProfile",
134> "-ExecutionPolicy",
135> "Bypass",
136> "-File",
137> "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
138> ]
139> },
140> {
141> "type": "command",
142> "if": "PowerShell(Remove-Item *)",
143> "command": "powershell.exe",
144> "args": [
145> "-NoProfile",
146> "-ExecutionPolicy",
147> "Bypass",
148> "-File",
149> "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
150> ]
151> }
152> ]
153> }
154> ]
155> }
156> }
157> ```
158>
159> `-NoProfile` 标志跳过加载您的 PowerShell 配置文件,以便 hook 快速启动,`-ExecutionPolicy Bypass` 让 PowerShell 运行本地脚本文件。
160>
161> 该脚本从 stdin 读取 JSON 输入,提取命令,如果包含 `rm -rf` 或 `Remove-Item` 后跟 `-Recurse`,则返回 `permissionDecision` 为 `"deny"`。将其保存到项目中的 `.claude/hooks/block-rm.ps1`:
162>
163> ```powershell theme={null}
164> # .claude/hooks/block-rm.ps1
165> $callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
166> $command = $callInput.tool_input.command
167>
168> if ($command -match 'rm -rf|Remove-Item.*-Recurse') {
169> @{
170> hookSpecificOutput = @{
171> hookEventName = "PreToolUse"
172> permissionDecision = "deny"
173> permissionDecisionReason = "Destructive command blocked by hook"
174> }
175> } | ConvertTo-Json
176> } else {
177> exit 0 # no decision; normal permission flow applies
178> }
179> ```
180 180
181现在假设 Claude Code 决定针对 macOS/Linux 配置运行 `Bash "rm -rf /tmp/build"`。以下是发生的情况:181现在假设 Claude Code 决定针对 macOS/Linux 配置运行 `Bash "rm -rf /tmp/build"`。以下是发生的情况:
182 182
601 601
602对于任何引用路径占位符的 hook,优先使用 [exec 形式](#exec-form-and-shell-form)。在 shell 形式中,用双引号包装每个占位符。602对于任何引用路径占位符的 hook,优先使用 [exec 形式](#exec-form-and-shell-form)。在 shell 形式中,用双引号包装每个占位符。
603 603
604**项目脚本**604> [tab] 项目脚本
605 605>
606此示例使用 `${CLAUDE_PROJECT_DIR}` 在任何 `Write` 或 `Edit` 工具调用后从项目的 `.claude/hooks/` 目录运行样式检查器:606> 此示例使用 `${CLAUDE_PROJECT_DIR}` 在任何 `Write` 或 `Edit` 工具调用后从项目的 `.claude/hooks/` 目录运行样式检查器:
607 607>
608```json theme={null}608> ```json theme={null}
609{609> {
610 "hooks": {610> "hooks": {
611 "PostToolUse": [611> "PostToolUse": [
612 {612> {
613 "matcher": "Write|Edit",613> "matcher": "Write|Edit",
614 "hooks": [614> "hooks": [
615 {615> {
616 "type": "command",616> "type": "command",
617 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh",617> "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh",
618 "args": []618> "args": []
619 }619> }
620 ]620> ]
621 }621> }
622 ]622> ]
623 }623> }
624}624> }
625```625> ```
626 626
627**插件脚本**627> [tab] 插件脚本
628 628>
629在 `hooks/hooks.json` 中定义插件 hooks,带有可选的顶级 `description` 字段。启用插件时,其 hooks 与您的用户和项目 hooks 合并。629> 在 `hooks/hooks.json` 中定义插件 hooks,带有可选的顶级 `description` 字段。启用插件时,其 hooks 与您的用户和项目 hooks 合并。
630 630>
631此示例运行与插件捆绑的格式化脚本:631> 此示例运行与插件捆绑的格式化脚本:
632 632>
633```json theme={null}633> ```json theme={null}
634{634> {
635 "description": "Automatic code formatting",635> "description": "Automatic code formatting",
636 "hooks": {636> "hooks": {
637 "PostToolUse": [637> "PostToolUse": [
638 {638> {
639 "matcher": "Write|Edit",639> "matcher": "Write|Edit",
640 "hooks": [640> "hooks": [
641 {641> {
642 "type": "command",642> "type": "command",
643 "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",643> "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
644 "args": [],644> "args": [],
645 "timeout": 30645> "timeout": 30
646 }646> }
647 ]647> ]
648 }648> }
649 ]649> ]
650 }650> }
651}651> }
652```652> ```
653 653>
654有关创建插件 hooks 的详细信息,请参阅 [插件组件参考](https://code.claude.com/docs/zh-CN/plugins/components.md#hooks)。654> 有关创建插件 hooks 的详细信息,请参阅 [插件组件参考](https://code.claude.com/docs/zh-CN/plugins/components.md#hooks)。
655 655
656### Skills 和代理中的 Hooks656### Skills 和代理中的 Hooks
657 657
1180 1180
1181以下是每种模式的实际示例:1181以下是每种模式的实际示例:
1182 1182
1183**顶级 decision**1183> [tab] 顶级 decision
1184 1184>
1185`decision` 的唯一值是 `"block"`。要允许操作继续,请从您的 JSON 中省略 `decision`,或退出 0 且不输出任何 JSON:1185> `decision` 的唯一值是 `"block"`。要允许操作继续,请从您的 JSON 中省略 `decision`,或退出 0 且不输出任何 JSON:
1186 1186>
1187```json theme={null}1187> ```json theme={null}
1188{1188> {
1189 "decision": "block",1189> "decision": "block",
1190 "reason": "Test suite must pass before proceeding"1190> "reason": "Test suite must pass before proceeding"
1191}1191> }
1192```1192> ```
1193 1193
1194**PreToolUse**1194> [tab] PreToolUse
1195 1195>
1196使用 `hookSpecificOutput` 进行更丰富的控制:允许、拒绝或升级给用户。您也可以在工具运行前修改工具输入,或为 Claude 注入额外上下文。请参阅 [PreToolUse 决策控制](#pretooluse-decision-control)了解完整的选项集。1196> 使用 `hookSpecificOutput` 进行更丰富的控制:允许、拒绝或升级给用户。您也可以在工具运行前修改工具输入,或为 Claude 注入额外上下文。请参阅 [PreToolUse 决策控制](#pretooluse-decision-control)了解完整的选项集。
1197 1197>
1198```json theme={null}1198> ```json theme={null}
1199{1199> {
1200 "hookSpecificOutput": {1200> "hookSpecificOutput": {
1201 "hookEventName": "PreToolUse",1201> "hookEventName": "PreToolUse",
1202 "permissionDecision": "deny",1202> "permissionDecision": "deny",
1203 "permissionDecisionReason": "Database writes are not allowed"1203> "permissionDecisionReason": "Database writes are not allowed"
1204 }1204> }
1205}1205> }
1206```1206> ```
1207 1207
1208**PermissionRequest**1208> [tab] PermissionRequest
1209 1209>
1210使用 `hookSpecificOutput` 代表用户允许或拒绝权限请求。允许时,您也可以修改工具的输入或应用权限规则,使用户不会再次被询问。请参阅 [PermissionRequest 决策控制](#permissionrequest-decision-control)了解完整的选项集。1210> 使用 `hookSpecificOutput` 代表用户允许或拒绝权限请求。允许时,您也可以修改工具的输入或应用权限规则,使用户不会再次被询问。请参阅 [PermissionRequest 决策控制](#permissionrequest-decision-control)了解完整的选项集。
1211 1211>
1212```json theme={null}1212> ```json theme={null}
1213{1213> {
1214 "hookSpecificOutput": {1214> "hookSpecificOutput": {
1215 "hookEventName": "PermissionRequest",1215> "hookEventName": "PermissionRequest",
1216 "decision": {1216> "decision": {
1217 "behavior": "allow",1217> "behavior": "allow",
1218 "updatedInput": {1218> "updatedInput": {
1219 "command": "npm run lint"1219> "command": "npm run lint"
1220 }1220> }
1221 }1221> }
1222 }1222> }
1223}1223> }
1224```1224> ```
1225 1225
1226有关扩展示例,包括 Bash 命令验证、提示词过滤和自动批准脚本,请参阅指南中的[您可以自动化什么](https://code.claude.com/docs/zh-CN/hooks-guide.md#what-you-can-automate)和 [Bash 命令验证器参考实现](https://github.com/anthropics/claude-code/blob/main/examples/hooks/bash_command_validator_example.py)。1226有关扩展示例,包括 Bash 命令验证、提示词过滤和自动批准脚本,请参阅指南中的[您可以自动化什么](https://code.claude.com/docs/zh-CN/hooks-guide.md#what-you-can-automate)和 [Bash 命令验证器参考实现](https://github.com/anthropics/claude-code/blob/main/examples/hooks/bash_command_validator_example.py)。
1227 1227
1656 1656
1657此示例从 Claude 的回复中去除 markdown 格式,以进行纯文本显示。该脚本从 stdin 读取每一批,从 `delta` 中移除粗体标记和行内代码反引号,并将结果作为 `displayContent` 返回。1657此示例从 Claude 的回复中去除 markdown 格式,以进行纯文本显示。该脚本从 stdin 读取每一批,从 `delta` 中移除粗体标记和行内代码反引号,并将结果作为 `displayContent` 返回。
1658 1658
1659**macOS/Linux**1659> [tab] macOS/Linux
1660 1660>
1661在您的设置文件中为该事件注册一个命令 hook:1661> 在您的设置文件中为该事件注册一个命令 hook:
1662 1662>
1663```json theme={null}1663> ```json theme={null}
1664{1664> {
1665 "hooks": {1665> "hooks": {
1666 "MessageDisplay": [1666> "MessageDisplay": [
1667 {1667> {
1668 "hooks": [1668> "hooks": [
1669 {1669> {
1670 "type": "command",1670> "type": "command",
1671 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.sh",1671> "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.sh",
1672 "args": []1672> "args": []
1673 }1673> }
1674 ]1674> ]
1675 }1675> }
1676 ]1676> ]
1677 }1677> }
1678}1678> }
1679```1679> ```
1680 1680>
1681将此脚本保存到项目中的 `.claude/hooks/plain-display.sh`,并使用 `chmod +x` 使其可执行:1681> 将此脚本保存到项目中的 `.claude/hooks/plain-display.sh`,并使用 `chmod +x` 使其可执行:
1682 1682>
1683```bash theme={null}1683> ```bash theme={null}
1684#!/bin/bash1684> #!/bin/bash
1685jq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'1685> jq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'
1686```1686> ```
1687
1688**Windows (PowerShell)**
1689
1690注册一个通过 PowerShell 运行脚本的命令 hook:
1691
1692```json theme={null}
1693{
1694 "hooks": {
1695 "MessageDisplay": [
1696 {
1697 "hooks": [
1698 {
1699 "type": "command",
1700 "command": "powershell.exe",
1701 "args": [
1702 "-NoProfile",
1703 "-ExecutionPolicy",
1704 "Bypass",
1705 "-File",
1706 "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.ps1"
1707 ]
1708 }
1709 ]
1710 }
1711 ]
1712 }
1713}
1714```
1715
1716`-NoProfile` 标志会跳过加载您的 PowerShell 配置文件,使 hook 快速启动;`-ExecutionPolicy Bypass` 则允许 PowerShell 运行本地脚本文件。
1717
1718将此脚本保存到项目中的 `.claude/hooks/plain-display.ps1`:
1719 1687
1720```powershell theme={null}1688> [tab] Windows (PowerShell)
1721$batch = [Console]::In.ReadToEnd() | ConvertFrom-Json1689>
1722$text = $batch.delta -replace '\*\*', '' -replace '`', ''1690> 注册一个通过 PowerShell 运行脚本的命令 hook:
1723@{1691>
1724 hookSpecificOutput = @{1692> ```json theme={null}
1725 hookEventName = "MessageDisplay"1693> {
1726 displayContent = $text1694> "hooks": {
1727 }1695> "MessageDisplay": [
1728} | ConvertTo-Json1696> {
1729```1697> "hooks": [
1698> {
1699> "type": "command",
1700> "command": "powershell.exe",
1701> "args": [
1702> "-NoProfile",
1703> "-ExecutionPolicy",
1704> "Bypass",
1705> "-File",
1706> "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.ps1"
1707> ]
1708> }
1709> ]
1710> }
1711> ]
1712> }
1713> }
1714> ```
1715>
1716> `-NoProfile` 标志会跳过加载您的 PowerShell 配置文件,使 hook 快速启动;`-ExecutionPolicy Bypass` 则允许 PowerShell 运行本地脚本文件。
1717>
1718> 将此脚本保存到项目中的 `.claude/hooks/plain-display.ps1`:
1719>
1720> ```powershell theme={null}
1721> $batch = [Console]::In.ReadToEnd() | ConvertFrom-Json
1722> $text = $batch.delta -replace '\*\*', '' -replace '`', ''
1723> @{
1724> hookSpecificOutput = @{
1725> hookEventName = "MessageDisplay"
1726> displayContent = $text
1727> }
1728> } | ConvertTo-Json
1729> ```
1730 1730
1731不含 markdown 的批次会原样通过。如果脚本失败(例如因为缺少 `jq`),Claude Code 会显示原始文本,并且只在[调试输出](#debug-hooks)中记录该失败,而不会在会话中显示。1731不含 markdown 的批次会原样通过。如果脚本失败(例如因为缺少 `jq`),Claude Code 会显示原始文本,并且只在[调试输出](#debug-hooks)中记录该失败,而不会在会话中显示。
1732 1732
3356 3356
3357将匹配器写为精确名称、以 `|` 分隔的列表(例如 `claude-opus-4-6|claude-opus-5`),或正则表达式(例如 `.*opus.*`)。此示例使用精确名称匹配器,同时检查 hook 输入中的 `to_model`,因此它会通过以代码 2 退出来拒绝切换到 Opus 4.6,并允许切换到任何其他目标:3357将匹配器写为精确名称、以 `|` 分隔的列表(例如 `claude-opus-4-6|claude-opus-5`),或正则表达式(例如 `.*opus.*`)。此示例使用精确名称匹配器,同时检查 hook 输入中的 `to_model`,因此它会通过以代码 2 退出来拒绝切换到 Opus 4.6,并允许切换到任何其他目标:
3358 3358
3359**macOS/Linux**3359> [tab] macOS/Linux
3360 3360>
3361该命令使用 `jq` 检查 `to_model`:3361> 该命令使用 `jq` 检查 `to_model`:
3362 3362>
3363```json theme={null}3363> ```json theme={null}
3364{3364> {
3365 "hooks": {3365> "hooks": {
3366 "PreModelSwitch": [3366> "PreModelSwitch": [
3367 {3367> {
3368 "matcher": "claude-opus-4-6",3368> "matcher": "claude-opus-4-6",
3369 "hooks": [3369> "hooks": [
3370 {3370> {
3371 "type": "command",3371> "type": "command",
3372 "command": "jq -e '.to_model | test(\"opus-4-6\")' > /dev/null && { echo 'Opus 4.6 is retired for this project. Use a newer model.' >&2; exit 2; }; exit 0"3372> "command": "jq -e '.to_model | test(\"opus-4-6\")' > /dev/null && { echo 'Opus 4.6 is retired for this project. Use a newer model.' >&2; exit 2; }; exit 0"
3373 }3373> }
3374 ]3374> ]
3375 }3375> }
3376 ]3376> ]
3377 }3377> }
3378}3378> }
3379```3379> ```
3380 3380
3381**Windows (PowerShell)**3381> [tab] Windows (PowerShell)
3382 3382>
3383注册一个通过 PowerShell 运行脚本的命令 hook:3383> 注册一个通过 PowerShell 运行脚本的命令 hook:
3384 3384>
3385```json theme={null}3385> ```json theme={null}
3386{3386> {
3387 "hooks": {3387> "hooks": {
3388 "PreModelSwitch": [3388> "PreModelSwitch": [
3389 {3389> {
3390 "matcher": "claude-opus-4-6",3390> "matcher": "claude-opus-4-6",
3391 "hooks": [3391> "hooks": [
3392 {3392> {
3393 "type": "command",3393> "type": "command",
3394 "command": "powershell.exe",3394> "command": "powershell.exe",
3395 "args": [3395> "args": [
3396 "-NoProfile",3396> "-NoProfile",
3397 "-ExecutionPolicy",3397> "-ExecutionPolicy",
3398 "Bypass",3398> "Bypass",
3399 "-File",3399> "-File",
3400 "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-opus-46.ps1"3400> "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-opus-46.ps1"
3401 ]3401> ]
3402 }3402> }
3403 ]3403> ]
3404 }3404> }
3405 ]3405> ]
3406 }3406> }
3407}3407> }
3408```3408> ```
3409 3409>
3410将此脚本保存到项目中的 `.claude/hooks/block-opus-46.ps1`:3410> 将此脚本保存到项目中的 `.claude/hooks/block-opus-46.ps1`:
3411 3411>
3412```powershell theme={null}3412> ```powershell theme={null}
3413$hookInput = [Console]::In.ReadToEnd() | ConvertFrom-Json3413> $hookInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
3414if ($hookInput.to_model -match 'opus-4-6') {3414> if ($hookInput.to_model -match 'opus-4-6') {
3415 [Console]::Error.WriteLine('Opus 4.6 is retired for this project. Use a newer model.')3415> [Console]::Error.WriteLine('Opus 4.6 is retired for this project. Use a newer model.')
3416 exit 23416> exit 2
3417}3417> }
3418exit 03418> exit 0
3419```3419> ```
3420 3420
3421要确认该 hook 是否正常工作,请在运行其他模型的会话中运行 `/model claude-opus-4-6`。Claude Code 会保留当前模型,并报告 PreModelSwitch hook 阻止了切换,以您的消息作为原因。3421要确认该 hook 是否正常工作,请在运行其他模型的会话中运行 `/model claude-opus-4-6`。Claude Code 会保留当前模型,并报告 PreModelSwitch hook 阻止了切换,以您的消息作为原因。
3422 3422
3703 3703
3704此示例代替用户回答一个反复出现的问题。名为 `issue-tracker` 的 MCP 服务器在表单中询问项目键,hook 会填入 `DOCS`。当 `project_key` 是表单的唯一字段时,脚本会接受。对于其他任何请求,它不打印任何内容,因此会出现对话框。3704此示例代替用户回答一个反复出现的问题。名为 `issue-tracker` 的 MCP 服务器在表单中询问项目键,hook 会填入 `DOCS`。当 `project_key` 是表单的唯一字段时,脚本会接受。对于其他任何请求,它不打印任何内容,因此会出现对话框。
3705 3705
3706**macOS/Linux**3706> [tab] macOS/Linux
3707 3707>
3708在设置文件中为该事件注册一个命令 hook,以服务器名称作为匹配器:3708> 在设置文件中为该事件注册一个命令 hook,以服务器名称作为匹配器:
3709 3709>
3710```json theme={null}3710> ```json theme={null}
3711{3711> {
3712 "hooks": {3712> "hooks": {
3713 "Elicitation": [3713> "Elicitation": [
3714 {3714> {
3715 "matcher": "issue-tracker",3715> "matcher": "issue-tracker",
3716 "hooks": [3716> "hooks": [
3717 {3717> {
3718 "type": "command",3718> "type": "command",
3719 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh",3719> "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh",
3720 "args": []3720> "args": []
3721 }3721> }
3722 ]3722> ]
3723 }3723> }
3724 ]3724> ]
3725 }3725> }
3726}3726> }
3727```3727> ```
3728 3728>
3729将此脚本保存到项目中的 `.claude/hooks/answer-project-key.sh`,并使用 `chmod +x` 使其可执行:3729> 将此脚本保存到项目中的 `.claude/hooks/answer-project-key.sh`,并使用 `chmod +x` 使其可执行:
3730 3730>
3731```bash theme={null}3731> ```bash theme={null}
3732#!/bin/bash3732> #!/bin/bash
3733input=$(cat)3733> input=$(cat)
3734fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input")3734> fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input")
3735 3735>
3736if [ "$fields" = '["project_key"]' ]; then3736> if [ "$fields" = '["project_key"]' ]; then
3737 jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}'3737> jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}'
3738fi3738> fi
3739```3739> ```
3740
3741**Windows (PowerShell)**
3742
3743注册一个通过 PowerShell 运行脚本的命令 hook,以服务器名称作为匹配器:
3744
3745```json theme={null}
3746{
3747 "hooks": {
3748 "Elicitation": [
3749 {
3750 "matcher": "issue-tracker",
3751 "hooks": [
3752 {
3753 "type": "command",
3754 "command": "powershell.exe",
3755 "args": [
3756 "-NoProfile",
3757 "-ExecutionPolicy",
3758 "Bypass",
3759 "-File",
3760 "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1"
3761 ]
3762 }
3763 ]
3764 }
3765 ]
3766 }
3767}
3768```
3769
3770将此脚本保存到项目中的 `.claude/hooks/answer-project-key.ps1`:
3771
3772```powershell theme={null}
3773$request = [Console]::In.ReadToEnd() | ConvertFrom-Json
3774$fields = @($request.requested_schema.properties.PSObject.Properties.Name)
3775 3740
3776if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') {3741> [tab] Windows (PowerShell)
3777 @{3742>
3778 hookSpecificOutput = @{3743> 注册一个通过 PowerShell 运行脚本的命令 hook,以服务器名称作为匹配器:
3779 hookEventName = "Elicitation"3744>
3780 action = "accept"3745> ```json theme={null}
3781 content = @{ project_key = "DOCS" }3746> {
3782 }3747> "hooks": {
3783 } | ConvertTo-Json -Depth 33748> "Elicitation": [
3784}3749> {
3785```3750> "matcher": "issue-tracker",
3751> "hooks": [
3752> {
3753> "type": "command",
3754> "command": "powershell.exe",
3755> "args": [
3756> "-NoProfile",
3757> "-ExecutionPolicy",
3758> "Bypass",
3759> "-File",
3760> "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1"
3761> ]
3762> }
3763> ]
3764> }
3765> ]
3766> }
3767> }
3768> ```
3769>
3770> 将此脚本保存到项目中的 `.claude/hooks/answer-project-key.ps1`:
3771>
3772> ```powershell theme={null}
3773> $request = [Console]::In.ReadToEnd() | ConvertFrom-Json
3774> $fields = @($request.requested_schema.properties.PSObject.Properties.Name)
3775>
3776> if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') {
3777> @{
3778> hookSpecificOutput = @{
3779> hookEventName = "Elicitation"
3780> action = "accept"
3781> content = @{ project_key = "DOCS" }
3782> }
3783> } | ConvertTo-Json -Depth 3
3784> }
3785> ```
3786 3786
3787要确认该 hook 是否正常工作,请使用 `claude --debug` 启动 Claude Code,并给 Claude 一个会让服务器询问项目键的任务。不会出现对话框,并且[调试日志](#debug-hooks)中会有一行以 `Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}` 结尾。3787要确认该 hook 是否正常工作,请使用 `claude --debug` 启动 Claude Code,并给 Claude 一个会让服务器询问项目键的任务。不会出现对话框,并且[调试日志](#debug-hooks)中会有一行以 `Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}` 结尾。
3788 3788