Claude Code Hooks 完整教學:讓 AI 助手自動化你的開發流程
Claude Code Hooks 完整教學:讓 AI 助手自動化你的開發流程

那天我又忘了格式化代碼。
說起來有點丟臉,但這種事真的常發生。用 Claude Code 寫完一段功能,測試通過,開心地準備提交,結果 CI 紅了——ESLint 報了一堆格式問題。我只好默默地跑 prettier --write,然後再提交一次。
更慘的是有一次,我讓 Claude 幫忙改一個配置,結果它「順手」把 .env 文件也改了。雖然我的 .env 有在 .gitignore 裡,但當下那個心跳漏拍的感覺,你懂的。
後來我才知道,這些問題其實都有一個優雅的解決方案:Hooks。
什麼是 Hooks?用管家來比喻
怎麼解釋 Hooks 呢?想像你請了一個超級能幹的 AI 助手(就是 Claude),但這個助手有時候會太有創意,或者忘記一些你很在意的細節。
Hooks 就像是你設定的一套「家規」或者說「自動化管家」。它會在特定時機自動執行你預設的動作:
- Claude 要寫入文件之前,管家先檢查:「等等,這是不是敏感文件?」
- Claude 寫完文件之後,管家自動跑:「來,我幫你格式化一下。」
- Claude 完成整個任務的時候,管家通知你:「老闆,活幹完了!」

關鍵在於:Hooks 是確定性的。
這很重要。AI 模型是機率性的——你告訴 Claude「記得格式化代碼」,它大部分時候會記得,但偶爾就是會忘。而 Hooks 不一樣,只要你設定了,它就一定會執行。不是「應該會」,是「一定會」。
這個差別,在團隊協作或者處理敏感操作時,真的很關鍵。
Hook 事件:在對的時機做對的事
Claude Code 目前支援這些 Hook 事件,每個都有它適合的使用場景:
PreToolUse 是最常用的之一。它在 Claude 執行任何工具(寫檔案、跑命令等)之前觸發。厲害的是,它可以阻止這個操作。想像它是一個門衛,看到可疑的人可以直接擋在門外。
PostToolUse 則是在工具執行之後觸發。它沒辦法阻止操作(因為已經執行了),但很適合做後續處理,像是自動格式化、跑測試、寫日誌這類事情。
Stop 在 Claude 完成整個回應時觸發。適合做最後的驗證或者發送通知。
還有一些其他的:UserPromptSubmit(用戶提交提示時)、SessionStart(會話開始時)、Notification(發送通知時)等等。不過剛入門的話,先掌握前三個就夠用了。
動手配置第一個 Hook
好,說了這麼多,來實際操作一下。
配置 Hooks 有兩種方式。如果你喜歡圖形介面,可以在 Claude Code 裡直接輸入 /hooks,它會引導你一步步設定。但我個人更喜歡直接編輯設定檔,因為可以版本控制,也方便在團隊間分享。
設定檔的位置有三個選擇:
~/.claude/settings.json:全域設定,所有專案都會套用.claude/settings.json:專案設定,可以 commit 到 repo 跟團隊共用.claude/settings.local.json:本地設定,通常放在.gitignore裡
來寫一個最簡單的 Hook:每次 Claude 編輯 TypeScript 文件後,自動跑 Prettier 格式化。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I {} sh -c 'echo {} | grep -q \"\\.ts$\" && npx prettier --write {}'"
}
]
}
]
}
}
這段設定做了什麼?當 Claude 使用 Write 或 Edit 工具後(matcher 用 | 表示「或」),它會讀取被操作的文件路徑,如果是 .ts 結尾的文件,就跑 Prettier 格式化。
看起來有點複雜?讓我拆解一下那個 command:
jq -r '.tool_input.file_path':從 Hook 收到的 JSON 資料中提取文件路徑xargs -I {} sh -c '...':把路徑傳給後面的 shell 命令grep -q "\\.ts$":檢查是不是 TypeScript 文件npx prettier --write {}:如果是的話,格式化它
其實如果邏輯稍微複雜一點,我建議直接寫成獨立的 shell 腳本,然後在 Hook 裡呼叫它。比較好維護,也比較好除錯。

實戰案例:我每天都在用的三個 Hook
案例一:保護敏感文件
這個是我設定的第一個 Hook,來自那次 .env 被誤改的教訓。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "python3 -c \"import json,sys; d=json.load(sys.stdin); p=d.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(x in p for x in ['.env','.pem','.key','secrets']) else 0)\""
}
]
}
]
}
}
這段程式會檢查 Claude 想要寫入的文件路徑,如果包含 .env、.pem、.key 或 secrets 這些關鍵字,就會回傳 exit code 2,阻止操作。
對了,exit code 很重要:
- 0:允許操作繼續
- 2:阻止操作,並把 stderr 的內容回饋給 Claude
所以你可以在腳本裡輸出一些提示訊息到 stderr,Claude 會看到並理解為什麼被阻止。
案例二:自動跑測試
每次改完代碼手動跑測試?太累了。
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "npm test 2>&1 | head -50 || true"
}
]
}
]
}
}
這個 Hook 在 Claude 完成回應時觸發,自動跑測試。head -50 是為了避免輸出太長,|| true 是確保即使測試失敗也不會阻塞 Claude。
如果你只想在特定文件被修改時才跑測試,可以用 PostToolUse 搭配更精細的條件判斷。
案例三:完成通知
這個真的超實用。有時候讓 Claude 跑一個比較長的任務,我會切去做別的事,但又怕錯過它完成的時機。
macOS 用戶可以這樣設:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"任務完成!\" with title \"Claude Code\"'"
}
]
}
]
}
}
Linux 用戶可以用 notify-send:
notify-send "Claude Code" "任務完成!"
每次 Claude 完成回應,系統通知就會跳出來。再也不用一直盯著終端機了。
踩坑經驗:我替你試過的那些錯
小心無限循環
這個坑我踩過。假設你設了一個 PostToolUse Hook,在 Claude 寫入文件後自動格式化。格式化工具本身也會「寫入」文件,這會不會又觸發 Hook?
好消息是,Claude Code 有做處理,Hook 觸發的操作不會再次觸發 Hook。但如果你的 Hook 邏輯比較複雜,還是要小心。
性能考量
Hook 是同步執行的,意思是 Claude 會等 Hook 跑完才繼續。如果你的 Hook 要跑很久(比如完整的測試套件),Claude 就會卡在那邊。
解法是讓 Hook 腳本在背景執行,或者只跑快速的檢查。比如只跑被修改文件相關的測試,而不是整個測試套件。
除錯技巧
Hook 沒有如預期執行?試試這幾招:
- 用
claude --debug啟動,可以看到詳細的 Hook 執行日誌 - 在 Hook 腳本裡加 log,輸出到一個文件
- 先用簡單的
echo命令測試,確認 Hook 有被觸發
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo \"Hook triggered at $(date)\" >> /tmp/hook-debug.log"
}
]
}
]
}
}
跑幾次操作後,檢查 /tmp/hook-debug.log 看看有沒有內容。
Matcher 的語法
Matcher 支援幾種寫法:
- 精確匹配:
"Bash"只匹配 Bash 工具 - 正則表達式:
"Write|Edit"匹配 Write 或 Edit - 萬用:
""或不設定,匹配所有工具
如果你的 Hook 沒觸發,先檢查 matcher 是不是寫對了。
進階玩法:Prompt 型 Hook
除了 command 類型,Hook 還支援 prompt 類型。這種 Hook 不是執行 shell 命令,而是讓 Claude 用 LLM 來決定怎麼處理。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "prompt",
"prompt": "檢查這個 bash 命令是否安全。如果包含 rm -rf、sudo 或其他危險操作,請阻止並解釋原因。"
}
]
}
]
}
}
這種方式更靈活,可以處理不好用規則定義的情況。但相對的,它會消耗額外的 token,而且因為是 LLM 判斷,所以結果可能不是 100% 確定性的。
我的建議是:能用 command 就用 command,規則清楚又快。只有真的需要「理解」內容時,才考慮 prompt 型 Hook。
團隊協作:分享你的 Hook 配置
如果你在團隊裡工作,可以把 Hook 配置放在專案的 .claude/settings.json 裡,然後 commit 到 repo。這樣團隊成員都會自動套用相同的規則。
但要注意一點:確保 Hook 裡用到的工具(比如 prettier、eslint)在大家的環境裡都有安裝。不然 Hook 會執行失敗,可能造成困惑。
一個好的做法是在專案的 README 或 CLAUDE.md 裡說明:
## Claude Code 設定
本專案使用 Claude Code Hooks 自動化以下流程:
- 編輯 TypeScript 文件後自動格式化
- 阻止寫入敏感配置文件
- 任務完成時發送通知
請確保已安裝:`npm install -D prettier eslint`
該你動手了
說了這麼多,最好的學習方式還是動手試試看。
我建議從最簡單的開始:設一個 PostToolUse Hook,在 Claude 編輯文件後 echo 一句話。確認它有觸發之後,再慢慢加入你真正想要的邏輯。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "echo '文件已更新' && date"
}
]
}
]
}
}
先從這個開始,然後根據你的實際需求慢慢擴展。
Hooks 真的是 Claude Code 裡一個被低估的功能。一旦你開始用了,就會發現很多以前需要手動做的事情,現在都可以自動化了。那種「設定好就不用再管」的感覺,真的很爽。
如果你設定了什麼有趣的 Hook,歡迎分享出來。這種小工具的妙用,往往是社群互相交流才會發現的。
參考資源
- Claude Code Hooks 官方文件
- Hooks 入門指南
- GitButler: Automate Your AI Workflows with Claude Code Hooks
- GitHub: claude-code-hooks-mastery
本文最初發布於 HackMD @BASHCAT。
留言
張貼留言