10 · 怎麼讓操作 100% 執行
Hooks 不是提醒 Claude Code 記得做事,而是在工具呼叫、提示提交、停止前後設定確定性檢查點。能容忍遺漏寫 CLAUDE.md,不能容忍就用 Hook。
翔宇有一條工程習慣:凡是“忘一次就會出事”的規則,不要只寫進提示詞裡。提示詞讓 AI 儘量記住,Hooks(鉤子)讓系統在關鍵節點強制檢查。前者是提醒,後者是門禁。——翔宇
這一篇用 14 分鐘換什麼:第 9 篇講 MCP 讓 Claude Code 夠到外部系統。夠得到以後,風險也變大了。這一篇講 Hooks:怎麼在 Claude Code 準備呼叫工具、呼叫之後、提交提示、準備停止等時間點插入自動檢查,讓“必須發生的事”不再只靠 Claude 記得。
1. 先從一個危險的小動作開始
你在 CLAUDE.md 裡寫了一條規則:
永远不要修改 `.env` 文件。這條規則有用嗎?有用。Claude Code 會讀到,也大機率遵守。
但“大機率”不是“保證”。
想象某一次任務裡,上下文很長、檔案很多、你又讓它“直接修好本地配置”。Claude 準備寫 .env。如果只靠 CLAUDE.md,它可能會想起規則,也可能在複雜上下文裡漏掉。
而 .env 不是普通檔案。改壞一次,可能就是資料庫連線、生產 token、第三方金鑰出問題。
這時你不應該再問“怎麼讓提示詞寫得更嚴厲”。你應該換一個機制。
第一性原理:Hooks 解決的不是“Claude 不理解規則”,而是“某些規則不能靠模型記憶來執行”。
Claude Code 官方 Hooks guide 對 Hooks 的定位很清楚:Hooks 會在 Claude Code 生命週期的特定點自動執行,用來 enforce project rules、automate repetitive tasks、integrate with existing tools。也就是說,它是確定性自動化,不是又一段勸 Claude 聽話的提示詞。
2. CLAUDE.md、Permissions、Hooks 各管什麼
先把三個概念擺清楚。
| 機制 | 中文理解 | 強度 | 適合什麼 |
|---|---|---|---|
CLAUDE.md | 工作說明書 | 建議 / 習慣 | 風格、流程、偏好、專案背景 |
| Permissions | 許可權閘門 | 允許 / 詢問 / 拒絕 | 工具級訪問控制 |
| Hooks | 自動檢查點 | 到點必觸發 | 格式化、審計、阻止危險動作、補上下文 |
舉一個 TypeScript 專案:
- “優先用現有 service 層,不要新造抽象”適合寫
CLAUDE.md。 - “Bash 裡執行
rm -rf要詢問”適合許可權規則。 - “每次改
.ts檔案後跑 formatter”適合 PostToolUse Hook。 - “任何寫
.env的嘗試都攔下來”適合 PreToolUse Hook。
這三者不是互相替代。
CLAUDE.md 給方向,Permissions 管門檻,Hooks 做檢查點。
一句話理解:能容忍偶爾遺漏的,寫進 CLAUDE.md;需要工具級邊界的,用 Permissions;到某個事件點必須自動發生的,用 Hooks。
3. Hook 其實是生命週期檢查點
Hook 的本質是:Claude Code 執行到某個時間點,自動把一段 JSON 輸入交給你配置的 handler(處理器)。handler 可以是命令、HTTP 端點、MCP 工具、prompt 或 agent。
先看一個簡化時間線:
flowchart LR
Prompt["使用者提交提示"]
Think["Claude 思考"]
Pre["PreToolUse<br/>工具執行前"]
Tool["工具呼叫<br/>Edit / Write / Bash / MCP"]
Post["PostToolUse<br/>工具成功後"]
Stop["Stop<br/>準備停止"]
Prompt --> Think --> Pre --> Tool --> Post --> Think --> Stop
style Pre fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
style Post fill:#dcfce7,stroke:#22c55e,stroke-width:2px
style Stop fill:#f3e8ff,stroke:#a855f7,stroke-width:2px
Hooks 不是隻在“寫檔案前”能用。官方 Hooks reference 列了很多事件,比如:
UserPromptSubmit:使用者提示提交時觸發,適合注入上下文、攔截危險請求。PreToolUse:工具執行前觸發,適合阻止危險命令、改寫工具輸入。PermissionRequest:即將彈許可權請求時觸發,適合自動允許/拒絕某類請求。PostToolUse:工具成功後觸發,適合格式化、審計、補充反饋。PostToolUseFailure:工具失敗後觸發,適合記錄失敗、給 Claude 修復線索。Stop:Claude 準備停下時觸發,適合檢查任務是否真的完成。SubagentStop:子代理準備結束時觸發,適合審查子代理結果。PreCompact/PostCompact:上下文壓縮前後觸發,適合歸檔或重新注入關鍵上下文。Elicitation/ElicitationResult:MCP server 請求使用者輸入前後觸發,適合審計或自動處理資訊徵詢。
新手不需要一口氣記完。先記三個最常用的:
PreToolUse:動手前攔。PostToolUse:動手後補。Stop:收工前查。
Hook 觸發順序到底是什麼樣:一次完整工具呼叫的事件鏈是 UserPromptSubmit → (思考) → PreToolUse → (Permission 檢查) → 工具執行 → PostToolUse / PostToolUseFailure → (思考) → Stop。關鍵事實:① PreToolUse 在 Permission 檢查之前觸發——Hook 可以提前攔掉危險操作不彈許可權窗;② Stop 在準備結束本輪之前觸發——可以阻止 Claude 過早收工。所以 Hook 不是"事後審計",是主動嵌入決策鏈路——這是它跟"日誌"的本質區別。
新手坑:不要為了“控制感”給每個事件都掛 Hook。Hook 越多,執行越慢,干擾越多,排查也越難。
4. 一個最小 Hook 長什麼樣
Hooks 寫在 settings JSON 裡。結構有三層:
- 選事件,比如
PreToolUse。 - 選 matcher group,比如只匹配
Write或Bash。 - 放一個或多個 hook handler。
比如阻止寫 .env:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/protect-env.sh"
}
]
}
]
}
}指令碼大概這樣:
#!/usr/bin/env bash
set -euo pipefail
INPUT="$(cat)"
FILE_PATH="$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')"
case "$FILE_PATH" in
*.env|*/.env|*.env.*)
echo "Blocked: environment files may contain secrets and must be edited manually." >&2
exit 2
;;
esac
exit 0Claude Code 會把事件 JSON 透過 stdin 傳給 command hook。上面指令碼讀 tool_input.file_path,如果目標是環境檔案,就返回 exit code 2。
關鍵點:Hook 不需要說服 Claude。它在工具執行前已經拿到了結構化輸入,可以直接基於檔案路徑、命令、許可權模式做判斷。
5. exit code 2 不只是“報錯”
官方 Hooks reference 明確說:exit 0 表示成功;exit 2 表示 blocking error;其他退出碼對多數事件來說是 non-blocking error。
這很重要:
exit 0:成功,動作繼續;stdout 裡的 JSON 可能被解析。exit 2:阻止;stderr 會反饋給 Claude。exit 1或其他:多數事件下只是非阻塞錯誤,動作繼續。
所以不要用傳統直覺寫:
exit 1如果你想阻止操作,要寫:
echo "Blocked: do not edit .env" >&2
exit 2並且記住一個細節:exit 2 時,Claude Code 忽略 stdout 和 stdout 裡的 JSON,只看 stderr 作為反饋。 如果你要用 JSON 做更細控制,通常是 exit 0 然後在 stdout 輸出 JSON。
例如 PreToolUse 可以返回:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database writes are not allowed."
}
}實操判斷:簡單阻止用 stderr + exit 2;需要更細的 allow / deny / ask / 修改輸入,用 exit 0 + JSON output。
6. 五種 handler:不只有 shell
舊理解裡,Hook 常被理解成“自動跑一個 shell 指令碼”。現在已經不止這樣。
官方 Hooks reference 裡的 handler type 有五類:
command:執行 shell 命令,適合格式化、檔案保護、日誌。http:POST 到一個 URL,適合通知、審計、接外部系統。mcp_tool:調已連線 MCP server 的工具,適合複用現有 MCP 能力。prompt:讓 Claude 模型做一次判斷,適合檢查是否真的完成、是否符合複雜規則。agent:啟動帶工具的 verifier agent,適合需要讀檔案、搜尋後再判斷的場景。
這五種放在一條光譜上看:
确定性强 -> 判断力强
command -> http -> mcp_tool -> prompt -> agent例子:
- 改
.ts後跑 Prettier:command。 - 每次許可權請求發審計系統:
http。 - 複用一個內部 policy MCP server:
mcp_tool。 - Stop 前判斷“任務是否真的完成”:
prompt。 - Stop 前讓一個 verifier agent 搜尋測試結果、讀 diff 再判斷:
agent。
不要反過來用重武器:能用 command 判斷的,就不要用 prompt;能用 prompt 判斷的,不一定要開 agent。判斷越智慧,成本和不確定性也越高。
為什麼 Anthropic 設計 5 種 handler 而不是隻給 command:用 shell command 解決一切是最簡單——但有些判斷 shell 寫不出來。"任務真的完成了嗎"需要語義理解,正則做不到——這就是 prompt handler 的位置。"任務完成了但程式碼風格還有 3 處問題"需要讀程式碼 + 跑命令——這就是 agent handler。5 種遞進的設計哲學是:讓你按"判斷複雜度"選合適的工具,不強迫所有規則都在 shell 裡寫——避免出現"寫 100 行 shell 模擬模型推理"的反模式。
7. Block-at-Write 還是 Block-at-Submit
Hook 很容易被用重。
比如你想保證測試透過。你可以在每次 Edit 後立刻跑全量測試嗎?技術上可以。但這會讓 Claude 每改一個檔案就等很久,任務體驗會崩。
更好的方式是區分兩類規則:
- Block-at-Write:動手前或剛動手後立即攔,適合改
.env、刪庫、寫生產配置。 - Block-at-Submit:收工前統一檢查,適合測試、lint、文件完整性、驗收清單。
危險動作要早攔,因為一旦發生就可能造成損失。
質量檢查可以晚一點,因為 Claude 需要先完成一組相關修改,再統一修 lint、測試和格式。
Stop hook 就適合 Block-at-Submit:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "复盘本轮结果。如果还缺测试或必要的验证步骤,就阻止停止:$ARGUMENTS"
}
]
}
]
}
}底層邏輯:早攔保護不可逆風險,晚查保護交付質量。不要把所有質量規則都塞到每一次檔案寫入前。
8. Hook 配置放在哪裡
Hook 遵守 Claude Code settings scope:
~/.claude/settings.json:作用於你所有專案,適合個人通知、個人日誌。.claude/settings.json:作用於當前儲存庫所有協作者,適合團隊規範、安全規則。.claude/settings.local.json:只作用於當前儲存庫你自己,適合本機實驗、臨時除錯。- plugin:作用於外掛啟用處,適合外掛隨帶的 Hook。
- managed settings:作用於組織級,適合安全和合規強制策略。
官方配置文件裡,scope 優先順序大致是:managed 最高,其次命令列引數、local、project、user。專案級能覆蓋使用者級,managed 不能被普通使用者覆蓋。
你可以用:
/hooks開啟只讀 hooks 選單,檢查當前有哪些 hook、來自哪個來源、匹配什麼事件。官方說明裡 /hooks 是 read-only:想改配置,還是要編輯 settings JSON 或讓 Claude 修改檔案。
團隊規則別放錯:團隊必須共同遵守的 Hook 放 .claude/settings.json;只對你本機有意義的通知、路徑和實驗,放 .claude/settings.local.json 或使用者級。
9. 冪等性和效能
Hooks 是程式碼。程式碼就有工程質量。
第一條是冪等性。
冪等性就是:跑一次和跑多次,結果不應該越來越壞。
好的 Hook:
格式化文件
检查文件路径
追加带唯一 id 的审计日志危險 Hook:
每触发一次就插一条数据库记录
每触发一次就覆盖一个全局配置
每触发一次就发一条无法去重的外部通知Claude Code 可能多次編輯同一個檔案,PostToolUse 就可能多次觸發。如果 Hook 沒有冪等性,就會製造重複副作用。
第二條是效能。
PreToolUse 是熱路徑。你在每次工具呼叫前跑一個 10 秒指令碼,Claude Code 體驗會立刻變慢。
減少成本的方法:
- 用
matcher限定工具名。 - 用
if進一步限定命令或檔案型別。 - 小檢查用 command,不要動不動 prompt/agent。
- 長任務考慮 async,但不要用 async 做必須同步攔截的安全規則。
Hook 設計原則:越靠前、越高頻的事件,Hook 越要短、準、可預測。
10. 這一篇和前幾篇怎麼連起來
到這裡,Claude Code 的控制層可以串成一張圖。
flowchart TB
Need["你想控制 Claude Code 行為"]
Know["長期知識和偏好<br/>CLAUDE.md"]
Reuse["重複任務流程<br/>Skills"]
Reach["連線外部系統<br/>MCP"]
People["隔離或協作<br/>SubAgents / Agent Teams"]
Guard["確定性檢查點<br/>Hooks"]
Perm["工具訪問邊界<br/>Permissions"]
Need --> Know
Need --> Reuse
Need --> Reach
Need --> People
Need --> Guard
Need --> Perm
style Guard fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
style Perm fill:#fee2e2,stroke:#ef4444,stroke-width:2px
每一層解決的問題不同:
CLAUDE.md:讓 Claude 知道專案規則,關鍵詞是 should。- Skills:複用任務流程,關鍵詞是 reusable workflow。
- SubAgents:隔離旁支任務,關鍵詞是 separate context。
- Agent Teams:多會話協作,關鍵詞是 shared task list。
- MCP:夠到外部系統,關鍵詞是 external tools。
- Hooks:到點自動檢查,關鍵詞是 deterministic checkpoint。
- Permissions:哪些工具能用,關鍵詞是 access control。
第 11 篇會專門講 Permissions。先記住邊界:Hook 可以在事件點做檢查,Permissions 決定工具訪問規則。兩者經常一起用,但不是一回事。
一句話理解:Hooks 是“到點必查”的檢查點;Permissions 是“能不能用工具”的訪問邊界;CLAUDE.md 是“希望你怎麼做”的說明書。
11. 本章自檢
試著用自己的話回答:
- 為什麼"我已經寫進 CLAUDE.md"不能等同於"這件事一定會發生"?對應 §1-§2。
- exit 2 和 exit 1 在 Hooks 裡有什麼關鍵區別?如果你想阻止寫
.env,應該用哪個?對應 §5。 - 動手題 ⭐:在你專案根新建
.claude/hooks/protect-secrets.sh,寫一個 PreToolUse Hook:當 Claude 想 Edit / Write 任何檔名包含.env/secret/credentials的檔案時,用 stderr 輸出"危險檔案不允許編輯"+ exit 2 阻止。然後在.claude/settings.json註冊。自檢:手動讓 Claude 試著改.env,看 Hook 有沒有攔下來 + 看 stderr 資訊有沒有進 Claude 上下文。這一道題做完你會知道:CLAUDE.md 提醒 ≠ 真攔得住,Hook 才是確定性。對應 §1 + §4。
過關標準:能用一句話說清:Hooks 是 Claude Code 生命週期裡的確定性檢查點,用來自動執行、阻止或反饋那些不能只靠模型記憶完成的規則。
本篇術語速查表
- Hook:鉤子,在 Claude Code 生命週期特定點自動執行的處理器。
- handler:處理器,Hook 觸發後真正執行的 command、http、prompt 等。
- matcher:匹配器,限定 Hook 對哪些工具或事件生效。
PreToolUse:工具執行前,工具呼叫前觸發,可阻止危險動作。PostToolUse:工具執行後,工具成功後觸發,常用於格式化和審計。Stop:停止前,Claude 準備結束當前回合時觸發。- exit code:退出碼,指令碼結束時返回給 Claude Code 的狀態數字。
- stderr:標準錯誤輸出,exit 2 時反饋給 Claude 的錯誤資訊來源。
- idempotency:冪等性,多次執行結果仍然安全穩定。
- Permissions:許可權系統,控制 Claude Code 工具訪問邊界的機制。
官方資料
接下來去哪
11 · 許可權怎麼管
Hooks 是檢查點,Permissions 是訪問邊界。下一篇拆 Claude Code 怎麼決定某個工具能不能用、要不要問你。
09 · 怎麼連外部服務(上一篇)
MCP 讓 Claude Code 夠到外部系統。Hooks 幫你在這些外部操作前後加檢查和審計。
03 · 怎麼記住你的習慣
分不清 CLAUDE.md 和 Hooks 時,回到記憶篇:長期習慣寫說明,強制檢查寫自動化。
如果只記一個判斷:凡是“漏一次就會出事”的規則,不要只寫成提醒,要放到 Hook 或 Permission 裡。