Claude Code hooks 教學:PreToolUse / PostToolUse 心智模型
PreToolUse / PostToolUse 心智模型,為什麼 hook 比 prompt instruction 穩。
TL;DR
- PreToolUse hook:tool 跑之前執行,可以 block tool call、把錯誤訊息丟回給 Claude
- PostToolUse hook:tool 跑之後執行,做後續動作(formatter、test、lint)但不能 block——事情已經發生了
- 比「叫 Claude 記得 X」可靠——LLM 的「記得」是機率性的,hook 是 deterministic enforcement
一個情境:那個總是忘記跑 prettier 的 agent
你 CLAUDE.md 寫了大大一行:
改完任何
.ts/.tsx檔案,一定要跑pnpm prettier --write。
它記得 80% 的時候。剩下 20% 你 review PR 看到一堆混亂縮排,又要回去 nag 它一次。
問題不在 prompt 寫得不夠用力——你已經用 caps、用粗體、用驚嘆號了。問題是這個規則住錯地方了:它住在 prompt instruction,靠 LLM 自律去執行。LLM 的「記得」是機率分布,不是 if-statement。
Hook 是把這條規則從 prompt 搬到 enforcement layer——Claude Code 自己幫你跑,不問 LLM 的意見。
Hook 在流程的哪一格
正常的 tool call 流程:
你的 prompt → Claude 決定用 Edit tool → Claude Code 執行 → 結果回給 Claude → 下一輪
把 hook 插進去就變成:
Claude 決定用 Edit tool
↓
PreToolUse hook ← 可以在這裡 block
↓
Claude Code 執行 Edit
↓
PostToolUse hook ← 可以在這裡跑 formatter
↓
結果回給 Claude
每一個 tool call 都有這兩個格子可以填。你想塞什麼指令都可以——shell command、Node script、Python——只要它會回傳 exit code,Claude Code 就接得住。
Pre 跟 Post 的能力對照
| PreToolUse | PostToolUse | |
|---|---|---|
| 跑的時機 | tool 執行前 | tool 執行後 |
| 可以 block tool | ✅ | ❌(事情已經發生了) |
| 拿到的 input | Claude 想呼叫的 tool name + 參數 | tool 執行的結果(含 stdout / 錯誤) |
| 適合的場景 | 擋掉危險命令、保護敏感檔案、enforce 命名規則 | 跑 formatter、跑 test、回 lint 錯誤給 Claude 自己修 |
| Block 怎麼回 Claude | exit code 非 0 + stderr 訊息 | 不能 block,但可以把訊息回給 Claude(它會看到並反應) |
一句話分:Pre 是守門人,Post 是清潔工。
為什麼 hook 比 prompt 穩
把同一條規則寫在兩個地方比一下:
| 把規則放在 prompt | 把規則放在 hook |
|---|---|
| LLM 可能記得 | Claude Code 一定執行 |
| 吃 context window 額度 | 不吃 context |
| 換個 model 行為可能跑掉 | 跟 model 無關 |
| Debug 是「為什麼這次沒記得」 | Debug 是看 hook script 的 log |
這跟 git pre-commit hook 是同一個哲學——你不會在 commit message 裡寫「請記得跑 lint」,你會裝一個 pre-commit hook 把它擋下來。Claude Code 的 hook 只是把這個概念從 commit-level 拉到 tool-call-level:每一次 read / write / edit / bash,都有一個可以攔截的位置。
兩個典型例子(先有畫面就好)
| 想做的事 | Pre 還是 Post | 為什麼 |
|---|---|---|
改完 .ts 自動跑 prettier | Post | 改完才有東西能 format |
不准 Claude 讀 .env | Pre | 要在它真的讀之前擋下 |
改完跑 tsc --noEmit,型別錯把錯誤丟回給 Claude 自己修 | Post | 已經改了才有得 type-check;用 Post 的「回訊息給 Claude」能力 |
擋掉所有 rm -rf | Pre | 等它跑完就來不及了 |
選 Pre 還是 Post 的判準很簡單:你要的是「擋下來」還是「事後處理」?
接下來
下一篇 寫第一個 hook 把 settings.json 打開,從一個最小的 PostToolUse hook 開始——edit 完一個檔案就 echo 一行到 log,看整個 pipeline 怎麼接起來。

