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` 中定義外掛 hook,可選擇加入頂層的 `description` 欄位。外掛啟用時,其 hook 會與您的使用者及專案 hook 合併。629> 在 `hooks/hooks.json` 中定義外掛 hook,可選擇加入頂層的 `description` 欄位。外掛啟用時,其 hook 會與您的使用者及專案 hook 合併。
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如需建立外掛 hook 的詳細資訊,請參閱[外掛元件參考](https://code.claude.com/docs/zh-TW/plugins/components.md#hooks)。654> 如需建立外掛 hook 的詳細資訊,請參閱[外掛元件參考](https://code.claude.com/docs/zh-TW/plugins/components.md#hooks)。
655 655
656### Skill 與 agent 中的 hook656### Skill 與 agent 中的 hook
657 657
1180 1180
1181以下是每種模式的實際範例:1181以下是每種模式的實際範例:
1182 1182
1183**頂層決定**1183> [tab] 頂層決定
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-TW/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-TW/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在您的設定檔中為此事件註冊一個 command hook:1661> 在您的設定檔中為此事件註冊一個 command 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 執行指令碼的 command 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 執行指令碼的 command 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請將 matcher 寫成精確名稱、以 `|` 分隔的清單(例如 `claude-opus-4-6|claude-opus-5`),或正規表示式(例如 `.*opus.*`)。此範例使用精確名稱 matcher,並同時檢查 hook 輸入中的 `to_model`,因此會以代碼 2 結束來拒絕切換到 Opus 4.6,並放行其他任何目標:3357請將 matcher 寫成精確名稱、以 `|` 分隔的清單(例如 `claude-opus-4-6|claude-opus-5`),或正規表示式(例如 `.*opus.*`)。此範例使用精確名稱 matcher,並同時檢查 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
3704 3704
3705此範例會代替使用者回答一個反覆出現的問題。名為 `issue-tracker` 的 MCP 伺服器會在表單中要求專案鍵,而 hook 會填入 `DOCS`。當 `project_key` 是表單唯一的欄位時,指令碼會接受。對於其他任何請求,它不會印出任何內容,因此會出現對話方塊。3705此範例會代替使用者回答一個反覆出現的問題。名為 `issue-tracker` 的 MCP 伺服器會在表單中要求專案鍵,而 hook 會填入 `DOCS`。當 `project_key` 是表單唯一的欄位時,指令碼會接受。對於其他任何請求,它不會印出任何內容,因此會出現對話方塊。
3706 3706
3707**macOS/Linux**3707> [tab] macOS/Linux
3708 3708>
3709在您的設定檔中為此事件註冊命令 hook,並以伺服器名稱作為 matcher:3709> 在您的設定檔中為此事件註冊命令 hook,並以伺服器名稱作為 matcher:
3710 3710>
3711```json theme={null}3711> ```json theme={null}
3712{3712> {
3713 "hooks": {3713> "hooks": {
3714 "Elicitation": [3714> "Elicitation": [
3715 {3715> {
3716 "matcher": "issue-tracker",3716> "matcher": "issue-tracker",
3717 "hooks": [3717> "hooks": [
3718 {3718> {
3719 "type": "command",3719> "type": "command",
3720 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh",3720> "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh",
3721 "args": []3721> "args": []
3722 }3722> }
3723 ]3723> ]
3724 }3724> }
3725 ]3725> ]
3726 }3726> }
3727}3727> }
3728```3728> ```
3729 3729>
3730將此指令碼儲存至專案中的 `.claude/hooks/answer-project-key.sh`,並以 `chmod +x` 設為可執行:3730> 將此指令碼儲存至專案中的 `.claude/hooks/answer-project-key.sh`,並以 `chmod +x` 設為可執行:
3731 3731>
3732```bash theme={null}3732> ```bash theme={null}
3733#!/bin/bash3733> #!/bin/bash
3734input=$(cat)3734> input=$(cat)
3735fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input")3735> fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input")
3736 3736>
3737if [ "$fields" = '["project_key"]' ]; then3737> if [ "$fields" = '["project_key"]' ]; then
3738 jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}'3738> jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}'
3739fi3739> fi
3740```3740> ```
3741
3742**Windows (PowerShell)**
3743
3744註冊一個透過 PowerShell 執行指令碼的命令 hook,並以伺服器名稱作為 matcher:
3745
3746```json theme={null}
3747{
3748 "hooks": {
3749 "Elicitation": [
3750 {
3751 "matcher": "issue-tracker",
3752 "hooks": [
3753 {
3754 "type": "command",
3755 "command": "powershell.exe",
3756 "args": [
3757 "-NoProfile",
3758 "-ExecutionPolicy",
3759 "Bypass",
3760 "-File",
3761 "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1"
3762 ]
3763 }
3764 ]
3765 }
3766 ]
3767 }
3768}
3769```
3770
3771將此指令碼儲存至專案中的 `.claude/hooks/answer-project-key.ps1`:
3772
3773```powershell theme={null}
3774$request = [Console]::In.ReadToEnd() | ConvertFrom-Json
3775$fields = @($request.requested_schema.properties.PSObject.Properties.Name)
3776 3741
3777if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') {3742> [tab] Windows (PowerShell)
3778 @{3743>
3779 hookSpecificOutput = @{3744> 註冊一個透過 PowerShell 執行指令碼的命令 hook,並以伺服器名稱作為 matcher:
3780 hookEventName = "Elicitation"3745>
3781 action = "accept"3746> ```json theme={null}
3782 content = @{ project_key = "DOCS" }3747> {
3783 }3748> "hooks": {
3784 } | ConvertTo-Json -Depth 33749> "Elicitation": [
3785}3750> {
3786```3751> "matcher": "issue-tracker",
3752> "hooks": [
3753> {
3754> "type": "command",
3755> "command": "powershell.exe",
3756> "args": [
3757> "-NoProfile",
3758> "-ExecutionPolicy",
3759> "Bypass",
3760> "-File",
3761> "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1"
3762> ]
3763> }
3764> ]
3765> }
3766> ]
3767> }
3768> }
3769> ```
3770>
3771> 將此指令碼儲存至專案中的 `.claude/hooks/answer-project-key.ps1`:
3772>
3773> ```powershell theme={null}
3774> $request = [Console]::In.ReadToEnd() | ConvertFrom-Json
3775> $fields = @($request.requested_schema.properties.PSObject.Properties.Name)
3776>
3777> if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') {
3778> @{
3779> hookSpecificOutput = @{
3780> hookEventName = "Elicitation"
3781> action = "accept"
3782> content = @{ project_key = "DOCS" }
3783> }
3784> } | ConvertTo-Json -Depth 3
3785> }
3786> ```
3787 3787
3788若要確認 hook 是否正常運作,請以 `claude --debug` 啟動 Claude Code,並給 Claude 一項會讓伺服器要求專案鍵的工作。不會出現對話方塊,且[偵錯日誌](#debug-hooks)中會有一行以 `Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}` 結尾。3788若要確認 hook 是否正常運作,請以 `claude --debug` 啟動 Claude Code,並給 Claude 一項會讓伺服器要求專案鍵的工作。不會出現對話方塊,且[偵錯日誌](#debug-hooks)中會有一行以 `Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}` 結尾。
3789 3789