使用 Hooks
Claude Code Hooks 在生命週期事件上執行確定性自動化。學會事件、matcher、if、exit code、JSON 輸出、command/prompt/agent/HTTP hooks 和安全邊界。
Hook 是把“希望 Claude 記得做”變成“事件發生時一定執行”。凡是必須發生、可指令碼化、可判定的動作,都不應該只寫在 prompt 裡。——翔宇
這一章用 16 分鐘換什麼:前面講了 Skills 和 Subagents。Skills 給 Claude 方法,Subagents 做隔離任務,Hooks 則在 Claude Code 生命週期上做確定性自動化。讀完你應該能配置通知、格式化、阻斷、審計、環境過載、permission 自動處理,並知道什麼時候該用 command、prompt、agent 或 HTTP hook。
1. Hook 解決什麼問題
Hook 是在 Claude Code 生命週期事件上自動執行的動作。
典型用途:
- Claude 等待輸入時發通知。
- Edit / Write 後自動格式化。
- PreToolUse 阻止危險命令。
- 禁止修改
.env、.git/、lockfile 等受保護檔案。 - compaction 後重新注入關鍵上下文。
- 配置檔案變化時審計。
- 目錄變化時過載
direnv、devbox、nix 環境。 - PermissionRequest 出現時自動處理特定低風險請求。
- Subagent 開始和結束時做 setup / cleanup。
Hook 和 prompt 的區別:
- Prompt 是讓 Claude 記住並判斷。
- Hook 是事件發生時直接執行。
第一性原理:需要推理和自由裁量,用 Skill 或 Subagent;需要每次固定執行,用 Hook;需要安全硬邊界,優先 permissions + Hook。
flowchart TD
Rule["一條規則或動作"]
MustRun["必須每次發生?"]
NeedsLLM["需要模型判斷?"]
NeedsTools["需要讀檔案或跑命令判斷?"]
External["需要通知外部系統?"]
Prompt["CLAUDE.md / Skill"]
CommandHook["Command Hook"]
PromptHook["Prompt Hook"]
AgentHook["Agent Hook"]
HttpHook["HTTP Hook"]
Rule --> MustRun
MustRun -->|否| Prompt
MustRun -->|是| NeedsLLM
NeedsLLM -->|否| External
External -->|是| HttpHook
External -->|否| CommandHook
NeedsLLM -->|是| NeedsTools
NeedsTools -->|否| PromptHook
NeedsTools -->|是| AgentHook
style CommandHook fill:#dcfce7,stroke:#22c55e,stroke-width:2px
style PromptHook fill:#e0f2fe,stroke:#0284c7,stroke-width:2px
style AgentHook fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
style HttpHook fill:#fee2e2,stroke:#ef4444,stroke-width:2px
2. Hook 和 Skill 的邊界
Skill 適合:
- 釋出 checklist。
- 程式碼審查流程。
- API 風格指南。
- 除錯 playbook。
- 需要 Claude 推理、取捨、適配上下文的工作方法。
Hook 適合:
- 每次編輯後跑 formatter。
- 每次 Bash 前檢查命令。
- 每次會話結束寫日誌。
- 每次 permission prompt 出現時處理某類低風險請求。
- 每次配置檔案變化時審計。
錯誤用法:
- “不要改
.env”只寫在 Skill 裡。 - “每次儲存後格式化”只寫在
CLAUDE.md裡。 - “釋出前一定跑測試”只靠 Claude 自覺。
正確做法:
- 知識和流程寫 Skill。
- 確定動作寫 Hook。
- 安全拒絕寫 permissions deny,必要時再加 Hook 給反饋。
Hook 不是更強提示詞:它是自動化執行點。不要把需要人類判斷的危險動作做成無確認 Hook。
3. Hook 配在哪裡
Hook 寫在 settings 裡:
- User:
~/.claude/settings.json - Project:
.claude/settings.json - Local:
.claude/settings.local.json - Managed:組織級 managed settings
- Plugin:plugin 裡的 hook 配置
- Subagent frontmatter:只在該 subagent 生命週期內生效
基礎結構:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}同一個 hooks object 裡可以有多個事件。不要新增一個 hooks key 把舊配置覆蓋掉。
檢視入口:
/hooks官方說明 /hooks 是隻讀瀏覽器。新增、修改、刪除仍然要編輯 settings JSON,或讓 Claude 幫你改配置檔案。
Project Hook 適合團隊規則;User Hook 適合個人通知和日誌;Local Hook 適合本機路徑;Managed Hook 適合組織強制自動化。
4. 第一個 Hook:等待輸入時發通知
macOS 示例:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}驗證:
- 寫入
~/.claude/settings.json。 - 執行
/hooks,確認Notification下有配置。 - 觸發一個 permission prompt 或讓 Claude 完成任務等待輸入。
macOS 如果沒有通知,先在 Terminal 跑一次:
osascript -e 'display notification "test"'然後去 System Settings 裡給 Script Editor 開通知許可權。
Notification 的空 matcher 表示全部通知型別。只想匹配許可權提示時,用 permission_prompt。
5. 常見事件:先記這幾類
Hook 事件很多,新手先記這幾類。
SessionStart:會話開始或恢復。UserPromptSubmit:使用者 prompt 提交後、Claude 處理前。PreToolUse:工具執行前,可以阻斷。PermissionRequest:許可權彈出視窗即將出現。PermissionDenied:auto mode 分類器拒絕工具呼叫。PostToolUse:工具成功執行後。PostToolUseFailure:工具失敗後。PostToolBatch:一批並行工具呼叫完成後。Notification:Claude Code 傳送通知時。SubagentStart/SubagentStop:subagent 開始和結束。TaskCreated/TaskCompleted:任務建立和完成。Stop:Claude 完成響應。StopFailure:本輪因 API 錯誤結束。ConfigChange:配置檔案變化。CwdChanged:工作目錄變化。FileChanged:被監聽檔案變化。WorktreeCreate/WorktreeRemove:worktree 建立和移除。PreCompact/PostCompact:compaction 前後。InstructionsLoaded:CLAUDE.md或 rules 載入到上下文。Elicitation/ElicitationResult:MCP elicitation 相關事件。
事件決定能力邊界:阻斷工具要用 PreToolUse;工具完成後格式化用 PostToolUse;許可權彈出視窗自動處理用 PermissionRequest;上下文補充用 SessionStart 或 PostCompact。
6. Hook 怎麼收到輸入
Command hook 會從 stdin 收到 JSON。
常見欄位包括:
hook_event_namesession_idtranscript_pathcwdtool_nametool_input- 事件專屬欄位
例如 PreToolUse / PostToolUse 裡,通常會有:
{
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "git status"
}
}指令碼里一般用 jq 解析:
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')先儲存輸入樣本:寫複雜 Hook 前,先把 stdin JSON append 到臨時日誌,確認欄位名和事件結構。
7. Exit code 語義
Command hook 透過 exit code 和 stdout / stderr 告訴 Claude Code 下一步怎麼做。
- Exit
0:繼續。對UserPromptSubmit、UserPromptExpansion、SessionStart等事件,stdout 會加入 Claude 上下文。 - Exit
2:嘗試阻斷。stderr 會反饋給 Claude 或使用者。對部分事件不能阻斷,只會顯示錯誤後繼續。 - 其他 exit code:非阻斷錯誤。流程繼續,stderr 第一行顯示在 transcript,完整 stderr 寫 debug log。
例子:阻止 Bash 裡出現危險 SQL。
#!/usr/bin/env bash
set -euo pipefail
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
if echo "$command" | rg -i "drop table|delete from|truncate table" >/dev/null; then
echo "Blocked: destructive SQL is not allowed" >&2
exit 2
fi
exit 0不要混用 exit 2 和 JSON allow/deny:官方說明 exit 2 時 JSON 會被忽略。要結構化控制,用 exit 0 + stdout JSON。
8. Structured JSON 輸出
Exit code 只能粗略 allow / block。需要更細控制時,exit 0 並向 stdout 輸出 JSON。
PreToolUse 可以返回:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep for repository search."
}
}效果:
- 工具呼叫被取消。
permissionDecisionReason會反饋給 Claude。- Claude 可以調整做法後繼續。
PermissionRequest 可以返回 allow:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow"
}
}
}也可以更新當前 session 的許可權模式:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedPermissions": [
{ "type": "setMode", "mode": "acceptEdits", "destination": "session" }
]
}
}
}自動 allow 要極窄:PermissionRequest matcher 不要寫空或 .*,否則可能自動批准檔案寫入、shell 命令和外部工具。
9. matcher 怎麼工作
matcher 用來篩選某類事件。
不同事件的 matcher 匹配物件不同:
PreToolUse/PostToolUse:工具名,例如Bash、Edit、Write、mcp__github__.*。Notification:通知型別,例如permission_prompt、idle_prompt。SubagentStart/SubagentStop:agent type,例如Explore、Plan、custom agent name。PreCompact/PostCompact:manual或auto。ConfigChange:user_settings、project_settings、local_settings、policy_settings、skills。FileChanged:要監聽的 literal filenames,例如.envrc|.env。SessionEnd:結束原因,例如clear。
一些事件不支援 matcher,會每次觸發:
UserPromptSubmitPostToolBatchStopTaskCreatedTaskCompletedCwdChangedWorktreeCreateWorktreeRemove
先用 matcher 粗篩:例如 Edit|Write。再用 if 或指令碼內部邏輯做細篩。
10. if 欄位:按工具名和引數過濾
if 欄位要求 Claude Code v2.1.85 或更高。舊版本會忽略它,導致 hook 對所有 matcher 命中的呼叫執行。
if 使用和 permissions 相同的規則語法。
例子:只檢查 git 命令,而不是所有 Bash。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
}
]
}
]
}
}if 只適用於工具類事件:
PreToolUsePostToolUsePostToolUseFailurePermissionRequestPermissionDenied
把 if 放到其他事件上,會阻止 hook 執行。
matcher 按事件物件過濾,if 按工具和引數過濾。需要按 Bash 子命令、Edit 路徑、MCP tool 細分時,用 if。
11. PostToolUse:編輯後自動格式化
專案級格式化適合放 .claude/settings.json。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}這個 Hook 的執行邏輯:
- Claude 用
Edit或Write改檔案。 - Hook 從 JSON 裡取
.tool_input.file_path。 - 把檔案路徑傳給 Prettier。
邊界:
- 如果專案不用 Prettier,不要套用這個例子。
- Python、Go、Rust 等專案應該替換成對應 formatter。
- 格式化失敗不要靜默吞掉,要讓 Claude 看到錯誤。
格式化適合 PostToolUse:不要讓 Claude 自己記住“改完跑 formatter”。讓 Hook 負責。
12. PreToolUse:阻止受保護檔案
指令碼示例:.claude/hooks/protect-files.sh
#!/usr/bin/env bash
set -euo pipefail
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
case "$file_path" in
*.env|*.env.*|*/.git/*|*package-lock.json)
echo "Blocked: protected file path $file_path" >&2
exit 2
;;
esac
exit 0註冊:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}指令碼要可執行:
chmod +x .claude/hooks/protect-files.sh保護敏感檔案優先用 permissions deny。Hook 的價值是給更具體的反饋和處理複雜條件。
13. SessionStart / PostCompact:注入上下文
SessionStart 可以在會話開始、恢復、clear 或 compact 後注入上下文。
示例:compaction 後提醒關鍵規則。
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Reminder: use pnpm, run pnpm test before committing, and do not edit generated files.'"
}
]
}
]
}
}注意:
- 每次都要知道的穩定規則,優先寫
CLAUDE.md。 - Hook 注入適合動態資訊,例如最近 commit、當前 sprint、環境狀態。
- 不要注入大段內容,避免上下文汙染。
靜態規則進 CLAUDE.md,動態上下文用 Hook。
14. ConfigChange:審計配置變化
ConfigChange 在配置檔案被外部程序或編輯器修改時觸發。
示例:記錄配置變化。
{
"hooks": {
"ConfigChange": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
}
]
}
]
}
}matcher 可以過濾:
user_settingsproject_settingslocal_settingspolicy_settingsskills
要阻止變化生效,可以 exit 2,或返回 structured JSON block。
審計日誌不要寫敏感值:只記錄檔案、來源、時間和動作,避免把 token、路徑隱私和賬號寫進日誌。
15. CwdChanged / FileChanged:過載環境
有些專案依賴 direnv、devbox、nix,根據目錄或檔案變化載入環境。
SessionStart + CwdChanged 示例:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
],
"CwdChanged": [
{
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}監聽特定檔案:
{
"hooks": {
"FileChanged": [
{
"matcher": ".envrc|.env",
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}FileChanged 的 matcher 是 literal filenames 列表,不是正則。
載入 .env 要小心:不要把敏感值列印到 stdout。環境注入寫 CLAUDE_ENV_FILE,不要回顯到對話。
16. PermissionRequest:自動批准要極窄
自動批准某個低風險許可權請求可以用 PermissionRequest。
示例:只自動批准 ExitPlanMode。
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PermissionRequest\",\"decision\":{\"behavior\":\"allow\"}}}'"
}
]
}
]
}
}不要這樣做:
- 空 matcher 自動批准所有請求。
.*自動批准所有請求。- 對
Bash、Edit、MCP 寫操作做無條件 allow。
PermissionRequest Hook 是高風險能力:只自動處理極小、明確、低風險的許可權項。其他保持 ask。
17. PermissionDenied:讓 auto mode 重試
PermissionDenied 在 auto mode 分類器拒絕工具呼叫時觸發。
某些情況下,你可以返回:
{
"retry": true
}告訴模型這次可以重試。
適合:
- 分類器誤拒絕了低風險操作。
- 你有額外指令碼確認這次行為安全。
不適合:
- 用來繞過真實風險。
- 對高風險 Bash / Edit / MCP 寫操作統一 retry。
retry 不是 allow all:它只是讓模型可重試被 auto mode 拒絕的呼叫。仍要窄匹配、可解釋。
18. Prompt-based hooks
Prompt hook 用 LLM 做判斷,適合輸入資料本身就足夠判斷的場景。
示例:Stop 時檢查任務是否完成。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all requested tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
}
]
}
]
}
}輸出協議:
{"ok": true}:繼續結束。{"ok": false, "reason": "..."}:根據事件執行阻斷或反饋。
適合:
- 檢查回答是否覆蓋使用者請求。
- 判斷 summary 是否缺關鍵資訊。
- 基於 hook input 做輕量評估。
不適合:
- 需要讀檔案、跑測試、查日誌。
- 生產級確定性規則。
Prompt hook 是判斷,不是執行:需要實際查程式碼狀態時用 agent hook 或 command hook。
19. Agent-based hooks
Agent hook 會 spawn subagent。官方標註為 experimental,生產工作流優先用 command hooks。
適合:
- 需要讀檔案。
- 需要搜尋程式碼。
- 需要跑命令驗證。
- 單次 prompt 判斷不夠。
示例:Stop 前檢查測試是否透過。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}邊界:
- 預設 timeout 比 prompt hook 長。
- 最多會使用一定數量的 tool-use turns。
- 更貴、更慢、更復雜。
Agent hook 不適合替代 CI:它可以做本地預檢,但真正釋出前仍要依賴可重複的測試和 CI。
20. HTTP hooks
HTTP hook 適合把事件傳送到外部系統:
- Slack。
- Discord。
- 內部 webhook。
- 審計平臺。
- 任務系統。
- 監控系統。
適合做通知和記錄,不適合直接做高風險生產操作。
安全要點:
- 使用 HTTPS。
- token 放在環境變數或憑據系統。
- 不要把完整 prompt、完整 diff、金鑰、路徑隱私發出去。
- 給外部系統加最小許可權。
- 設定合理 timeout。
HTTP hook 是資料外發通道:預設只傳送必要欄位。不要把 transcript 或 tool input 原樣推到第三方。
21. Async hooks
Async hook 適合不需要阻塞 Claude 的後臺動作。
適合:
- 傳送通知。
- 寫審計日誌。
- 上傳指標。
- 非同步觸發慢任務。
不適合:
- 阻止工具呼叫。
- 決定是否繼續。
- 必須在下一步前完成的檢查。
阻斷用同步,旁路用 async:需要影響當前流程就不要非同步。
22. Subagent hooks
Subagent 可以在自己的 frontmatter 裡定義 hooks。
示例:
---
name: db-reader
description: Execute read-only database queries.
tools: Bash
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/validate-readonly-query.sh"
---
You are a database analyst with read-only access.
Only execute SELECT queries.Subagent hooks 的特點:
- 只在這個 subagent 生命週期內生效。
- agent 作為 subagent 或透過
--agent作為 main session 時都會執行。 - plugin-shipped agents 不支援
hooks、mcpServers、permissionMode這幾個欄位。
Project settings 也有:
SubagentStartSubagentStop
適合做 setup / cleanup。
Subagent 工具邊界粗,Hook 邊界細:例如只允許 SELECT,要用 PreToolUse 校驗 Bash 內容。
23. Hooks 和 permission modes
Hooks 與 permission modes 是兩層不同機制。
PreToolUse在工具呼叫前觸發,可以阻斷。PermissionRequest在許可權彈出視窗前觸發,可以自動回答某些請求。PermissionDenied可以處理 auto mode 拒絕。bypassPermissions仍然會影響許可權提示出現與否。
不要假設 Hook 可以解決所有模式差異。尤其是:
- auto mode 下部分請求可能先被分類器拒絕。
- background subagents 預批准後會 auto-deny 未批准項。
- managed settings 可能停用 bypass 或強制策略。
Hook 不是許可權系統替代品:許可權邊界寫 permissions;Hook 用來補自動化、反饋、審計和複雜條件判斷。
24. Hook 合併和執行模型
官方說明:事件觸發時,所有匹配 hooks 會並行執行。相同命令會自動去重。
這帶來幾個後果:
- 多個 scope 的 hooks 可能都會執行。
- 不要假設 hooks 按順序序列。
- 如果一個 hook 依賴另一個 hook 的輸出,設計就不穩。
- 要序列流程,就寫成一個指令碼內部步驟。
- Hook 輸出越多,越可能汙染上下文。
Hook 要獨立冪等:同一事件下每個 Hook 都應該能獨立執行,多次執行也不破壞狀態。
25. 安全邊界
Hook 是自動執行命令,所以風險很真實。
預設原則:
- 不要在 Hook 裡刪除檔案。
- 不要自動釋出生產。
- 不要自動
git push。 - 不要把金鑰寫到日誌。
- 不要把完整 transcript 發給外部服務。
- 不要對許可權請求做寬 matcher allow。
- 不要執行儲存庫裡不可信指令碼,除非已信任 workspace。
- Project Hook 進 git 前要 review。
- Managed Hook 應由管理員維護。
指令碼建議:
- shell 指令碼首行使用 shebang。
- 使用
set -euo pipefail。 - 用
jq解析 JSON,避免字串硬拆。 - 對路徑做白名單或嚴格匹配。
- 出錯時 stderr 寫明確原因。
- 對網路呼叫設定 timeout。
Hook 的能力取決於本機許可權:Claude Code 能執行什麼,Hook 就可能執行什麼。不要把不可信 Hook 當普通提示詞看待。
26. 常見故障:Hook 不觸發
按這個順序查:
- 執行
/hooks,確認事件下有配置。 - 檢查寫入的是正確 settings scope。
- 確認 JSON 沒有被另一個
hookskey 覆蓋。 - 檢查 event name 拼寫。
- 檢查 matcher 是否匹配事件物件。
- 如果用了
if,確認 Claude Code 版本至少 v2.1.85。 - 確認指令碼有執行許可權。
- 檢查指令碼路徑,優先用
$CLAUDE_PROJECT_DIR。 - 開啟 verbose / debug log 看 stderr。
先寫最小 echo hook:確認事件能觸發,再逐步加 matcher、if、指令碼邏輯。
27. 常見故障:Hook 報 JSON validation failed
常見原因:
- stdout 輸出了非 JSON 內容,但事件期待 JSON。
- prompt hook 返回的不是
{"ok": true}或{"ok": false, "reason": "..."}。 - command hook 同時寫了 JSON 和普通日誌到 stdout。
- exit code 2 時還期待 stdout JSON 生效。
- JSON 引號被 shell 轉義破壞。
修法:
- 普通日誌寫 stderr。
- stdout 只寫機器要讀的 JSON。
- 用
jq -n生成 JSON,減少轉義錯誤。 - 需要阻斷就 exit 2 + stderr,不要混 JSON。
stdout 是協議通道:不要隨便 echo debug 到 stdout。除錯日誌寫 stderr 或檔案。
28. 常見故障:Stop hook 無限迴圈
Stop hook 如果一直返回 ok: false,Claude 會繼續工作,然後再次觸發 Stop。
常見原因:
- 判定條件過於理想化。
- reason 不可執行。
- Claude 無法滿足 Hook 要求。
- Hook 沒有最大嘗試次數。
修法:
- reason 寫具體下一步。
- 增加外部狀態計數。
- 只檢查本輪使用者明確要求。
- 複雜驗證交給 CI,不要用 Stop hook 無限追求完美。
Stop hook 要能收斂:它應該推動完成,不應該把會話鎖進無法滿足的迴圈。
29. 常見故障:Hook 影響太大
表現:
- 所有 Bash 都被檢查,速度變慢。
- 所有許可權都被自動批准。
- 每次編輯都跑整個測試套件。
- 大量 stdout 被塞進上下文。
- 多個 Hook 並行寫同一個檔案導致日誌亂序。
處理:
- 縮小 matcher。
- 加
if。 - 用檔案路徑篩選。
- 把慢任務改 async 或 CI。
- stdout 限制在必要內容。
- 日誌寫檔案並加鎖。
Hook 要窄、快、可解釋:越靠近高頻事件,越要剋制。
30. 推薦落地順序
不要一上來做複雜 Hook 系統。按這個順序:
- User scope 加 Notification。
- Project scope 加格式化 Hook。
- Project scope 加敏感檔案保護。
- 給 Bash / MCP 高風險呼叫加 PreToolUse 校驗。
- 需要時加 ConfigChange 審計。
- 複雜專案再加 CwdChanged / FileChanged 環境過載。
- 只對低風險、明確事件加 PermissionRequest 自動處理。
- 最後再考慮 prompt / agent / HTTP / async hooks。
先從低風險、可觀察開始:通知和格式化最適合入門;自動批准和生產動作最晚考慮。
31. 自檢清單
學完這一章,你應該能做到:
- 我能解釋 Hook 和 prompt / Skill 的區別。
- 我知道 Hook 配在 settings 的
hooksblock 裡。 - 我能用
/hooks檢視當前配置。 - 我知道
PreToolUse、PostToolUse、PermissionRequest、Notification、Stop的用途。 - 我知道 matcher 匹配物件隨事件變化。
- 我知道
if使用 permission rule syntax。 - 我知道 exit 0、exit 2 和其他 exit code 的差別。
- 我知道 structured JSON 輸出必須走 stdout。
- 我知道 command、prompt、agent、HTTP、async hooks 的取捨。
- 我知道 Hook 不是 permissions 的替代品。
- 我知道自動批准許可權請求必須極窄。
- 我知道怎麼排查 hook 不觸發和 JSON validation failed。
32. 術語速查
Hook:Claude Code 生命週期事件上的自動化動作。Command hook:執行本機 shell 命令的 Hook。Prompt hook:用 LLM 根據 hook input 做判斷的 Hook。Agent hook:spawn subagent 做驗證的 Hook。HTTP hook:把事件發到 HTTP endpoint 的 Hook。Async hook:後臺執行、不阻塞當前流程的 Hook。matcher:按事件物件篩選 hook group。if:按工具名和引數進一步篩選工具類 hook。PreToolUse:工具執行前事件。PostToolUse:工具成功執行後事件。PermissionRequest:許可權彈出視窗出現前事件。PermissionDenied:auto mode 拒絕工具呼叫後事件。Notification:Claude Code 傳送通知時事件。Stop:Claude 完成響應時事件。ConfigChange:配置變化事件。CwdChanged:工作目錄變化事件。FileChanged:監聽檔案變化事件。CLAUDE_ENV_FILE:Claude Code Bash preamble 環境檔案。hookSpecificOutput:structured JSON 輸出裡的事件專屬控制欄位。