主題
Claude Code Hooks
Hooks 是 Claude Code 的事件處理機制,讓你在特定時機點自動執行 shell 命令或腳本。這是實現自動化工作流程的強大功能。
為什麼需要 Hooks?
| 沒有 Hooks | 使用 Hooks |
|---|---|
| 每次寫檔案後手動執行 prettier | 自動在 Write/Edit 後格式化 |
| 每次都要手動允許測試指令 | 自動批准安全的測試命令 |
| 忘記檢查 linter 警告 | 自動在編輯後執行 lint |
| 手動記錄執行過的命令 | 自動記錄所有 Bash 命令 |
傳統方式:
Claude:[寫入 index.ts]
你:[手動執行 prettier]
你:[手動執行 eslint]
使用 Hooks:
Claude:[寫入 index.ts]
[自動執行 prettier]
[自動執行 eslint]
[已完成格式化和檢查]Hook 類型
Claude Code 提供 8 種 Hook 事件:
| Hook | 觸發時機 | 主要用途 |
|---|---|---|
| PreToolUse | 工具執行前 | 阻擋危險命令、驗證輸入、自動批准 |
| PostToolUse | 工具執行後 | 執行格式化、觸發 linter、記錄變更 |
| PermissionRequest | 權限對話框前 | 自動批准重複請求、拒絕敏感存取 |
| SessionStart | Session 開始時 | 注入 git status、載入 TODO |
| UserPromptSubmit | 用戶提交 prompt 時 | 注入上下文、驗證請求 |
| Stop | Claude 完成回應時 | 驗證任務完成、強制繼續 |
| SubagentStop | Sub-agent 完成時 | 驗證輸出、觸發後續動作 |
| PreCompact | Context 壓縮前 | 備份對話記錄 |
設定檔位置
Hooks 設定在 JSON 檔案中,優先順序如下:
| 位置 | 用途 | Git 追蹤 |
|---|---|---|
.claude/settings.local.json | 個人設定(最高優先) | 否(加入 .gitignore) |
.claude/settings.json | 專案共用設定 | 是 |
~/.claude/settings.json | 全域設定 | N/A |
基本結構
json
{
"hooks": {
"HookType": [
{
"matcher": "ToolName",
"hooks": [
{
"type": "command",
"command": "your-command-here"
}
]
}
]
}
}Matcher 語法
Matcher 用於過濾哪些工具會觸發 Hook:
| 語法 | 說明 | 範例 |
|---|---|---|
"Write" | 精確匹配 | 只匹配 Write 工具 |
"Write|Edit" | 多重匹配 | 匹配 Write 或 Edit |
"*" 或 "" | 全部匹配 | 匹配所有工具 |
"Bash(npm test*)" | 參數匹配 | 匹配以 npm test 開頭的命令 |
"mcp__memory__.*" | 模式匹配 | 匹配 MCP 工具 |
注意
Matcher 是 區分大小寫 的。"bash" 不會匹配到 Bash 工具。
實用範例
1. 自動格式化程式碼
每次 Write 或 Edit 後自動執行 Prettier:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "file_path=$(cat | jq -r '.tool_input.file_path'); if [[ \"$file_path\" == *.ts ]] || [[ \"$file_path\" == *.tsx ]]; then npx prettier --write \"$file_path\"; fi"
}
]
}
]
}
}2. 自動批准測試命令
讓測試命令不需要手動確認:
json
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash(npm test*)",
"hooks": [
{
"type": "command",
"command": "echo '{\"decision\": \"allow\"}'"
}
]
},
{
"matcher": "Bash(npm run lint*)",
"hooks": [
{
"type": "command",
"command": "echo '{\"decision\": \"allow\"}'"
}
]
}
]
}
}3. Session 開始時注入上下文
讓 Claude 知道目前的專案狀態:
json
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '=== Git Status ===' && git status --short && echo '\\n=== TODO ===' && head -20 TODO.md 2>/dev/null || echo 'No TODO.md'"
}
]
}
]
}
}4. 記錄所有 Bash 命令
用於稽核或除錯:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/bash-history.log"
}
]
}
]
}
}5. 阻擋危險操作
防止修改 production 設定:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "file_path=$(cat | jq -r '.tool_input.file_path'); if echo \"$file_path\" | grep -q 'production\\|.env.prod'; then echo '{\"decision\": \"block\", \"reason\": \"Cannot modify production files\"}'; fi"
}
]
}
]
}
}6. 強制完成檢查清單
確保 Claude 完成所有項目才停止:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "echo '{\"continue\": false}'"
}
]
}
]
}
}使用 prompt 類型強制驗證:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review if all tasks are complete. If not, continue working."
}
]
}
]
}
}Hook 輸入與輸出
輸入格式(stdin)
所有 Hook 都會收到 JSON 格式的輸入:
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/Users/you/project",
"tool_name": "Write",
"tool_input": {
"file_path": "/Users/you/project/index.ts",
"content": "..."
}
}輸出與 Exit Code
| Exit Code | 效果 |
|---|---|
| 0 | 成功,stdout 會被處理 |
| 2 | 阻擋操作,stderr 成為錯誤訊息 |
| 其他 | 非阻擋性錯誤 |
JSON 回應格式
進階控制可以回傳 JSON:
json
{
"decision": "allow|block|deny",
"reason": "顯示給 Claude 的說明",
"continue": true,
"updatedInput": {
"file_path": "modified/path.js"
}
}| 欄位 | 說明 |
|---|---|
decision | allow/block(PreToolUse)、allow/deny(PermissionRequest) |
reason | 顯示給 Claude 的說明文字 |
continue | 用於 Stop/SubagentStop,設為 true 強制繼續 |
updatedInput | 修改工具參數(v2.0.10+) |
環境變數
Hook 執行時可用的環境變數:
| 變數 | 說明 |
|---|---|
CLAUDE_PROJECT_DIR | 專案根目錄 |
CLAUDE_CODE_REMOTE | 是否為遠端環境 |
CLAUDE_TOOL_INPUT_* | 工具輸入參數(大寫) |
除錯技巧
查看 Transcript 檔案
bash
# 即時查看對話記錄
tail -f /path/to/transcript.jsonl | jq建立 Log Wrapper
bash
#!/bin/bash
# ~/.claude/scripts/log-wrapper.sh
LOG=~/.claude/hooks.log
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name // "n/a"')
echo "=== $(date) | $TOOL ===" >> "$LOG"
echo "$INPUT" >> "$LOG"
# 執行實際命令
echo "$INPUT" | "$@"使用:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "~/.claude/scripts/log-wrapper.sh your-actual-command"
}
]
}
]
}
}安全性考量
重要
Hook 設定檔的直接修改需要在 /hooks 選單中確認才會生效,防止惡意程式碼注入。
最佳實踐
- 驗證輸入:永遠驗證 stdin 的內容
- 引號包裹變數:防止 shell injection
- 使用絕對路徑:腳本路徑使用絕對路徑
- 避免處理敏感檔案:不要在 Hook 中處理
.env或憑證檔案
完整設定範例
一個完整的 .claude/settings.json 範例:
json
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '📋 Project: '$(basename $PWD) && git status --short 2>/dev/null | head -5"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash(rm -rf*)",
"hooks": [
{
"type": "command",
"command": "echo '{\"decision\": \"block\", \"reason\": \"Dangerous: rm -rf is not allowed\"}'"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "file=$(cat | jq -r '.tool_input.file_path'); ext=\"${file##*.}\"; case \"$ext\" in ts|tsx|js|jsx) npx prettier --write \"$file\" 2>/dev/null;; go) gofmt -w \"$file\" 2>/dev/null;; esac"
}
]
}
],
"PermissionRequest": [
{
"matcher": "Bash(npm test*)|Bash(npm run build*)",
"hooks": [
{
"type": "command",
"command": "echo '{\"decision\": \"allow\"}'"
}
]
}
]
}
}參考資源
下一步
- LSP 整合 - 語言伺服器協定
- Plugins 系統 - 擴充功能
- Commands 指令 - 自訂指令