Claude Code Hooks 系統與遷移指南:工作流程自動化與平滑升級
Claude Code 2.0 核心功能深度解析系列
本文是系列文章的第 3 篇(完結篇),完整系列包括:
- Skills 與 Sandbox - 擴展性與安全性雙提升
- Subagents 與 Plan Mode - 智能協作新紀元
- Hooks 系統與遷移指南 - 工作流程自動化與平滑升級(本文)
Claude Code 2.0.30 引入了強大的 Prompt-based Stop Hooks 功能,同時也帶來了重要的遷移需求:Output Styles 棄用和 SDK 更新。本文將深入探討 Hooks 系統的完整應用,並提供詳細的遷移指南,確保您能平滑升級到最新版本。
Hooks 系統更新
Prompt-based Stop Hooks 概述
Stop Hooks 是在 Claude 準備結束對話時執行的鉤子函數,可以檢查工作是否完成、驗證狀態,甚至阻止 Claude 停止並要求繼續工作。
運作流程
graph TD
A[Claude 完成任務] --> B[準備停止對話]
B --> C{有 Stop Hook?}
C -->|否| D[正常結束]
C -->|是| E[執行 Stop Hook]
E --> F[Hook 腳本檢查]
F --> G{檢查結果}
G -->|Exit Code 0| H[允許停止]
G -->|Exit Code 2| I[阻止停止]
G -->|其他 Exit Code| J[非阻擋性警告]
I --> K[Hook 回傳訊息給 Claude]
K --> L[Claude 繼續工作]
L --> B
H --> D
J --> M[顯示警告但允許停止]
M --> D
style A fill:#e1f5fe
style I fill:#ff6b6b
style H fill:#c8e6c9
配置 Stop Hooks
settings.json 基本配置
{
"hooks": {
"Stop": [
{
"matcher": "", // 空字串表示匹配所有 Stop 事件
"hooks": [
{
"type": "command",
"command": "/path/to/stop_hook.sh"
}
]
}
]
}
}
Exit Code 行為規範
| Exit Code | 行為 | 說明 | 使用時機 |
|---|---|---|---|
| 0 | 成功,允許停止 | Hook 檢查通過,Claude 可以結束 | 所有檢查都通過 |
| 2 | 阻擋停止 | Hook 發現問題,要求 Claude 繼續工作 | 發現未完成的任務 |
| 其他 | 非阻擋性錯誤 | 顯示警告但允許停止 | Hook 本身出錯 |
JSON 回應格式
Hook 可以回傳 JSON 格式提供更多控制:
{
"continue": false, // false = 阻擋停止, true = 允許停止
"stopReason": "原因說明", // 為何阻擋(當 continue=false 時)
"suppressOutput": false, // 是否隱藏 stdout 輸出
"systemMessage": "給 Claude 的訊息" // 額外的系統訊息
}
JSON vs Exit Code
# 方式 1:使用 Exit Code
#!/bin/bash
if [ 檢查失敗 ]; then
echo "發現問題:待辦事項未完成" >&2
exit 2 # 阻擋停止
fi
exit 0 # 允許停止
# 方式 2:使用 JSON(更靈活)
#!/bin/bash
if [ 檢查失敗 ]; then
echo '{
"continue": false,
"stopReason": "待辦事項未完成",
"suppressOutput": true,
"systemMessage": "請完成所有待辦事項後再結束"
}'
exit 0
fi
echo '{"continue": true}'
exit 0
實際應用案例
案例 1:待辦清單完成檢查
確保 Claude 在結束前完成所有待辦事項。
stop_hook.sh:
#!/bin/bash
# 檢查是否在 stop hook 內執行(防止無限迴圈)
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
echo '{"continue": true}' # 如果已在 hook 中,直接允許
exit 0
fi
# 檢查待辦清單
TODO_FILE=".claude/todos.json"
if [ ! -f "$TODO_FILE" ]; then
# 沒有待辦清單,允許停止
echo '{"continue": true}'
exit 0
fi
# 使用 jq 解析待辦清單
PENDING_COUNT=$(jq '[.todos[] | select(.status == "pending" or .status == "in_progress")] | length' "$TODO_FILE")
if [ "$PENDING_COUNT" -gt 0 ]; then
# 有未完成的待辦事項,阻擋停止
PENDING_ITEMS=$(jq -r '.todos[] | select(.status == "pending" or .status == "in_progress") | "- [\(.status)] \(.content)"' "$TODO_FILE")
cat <<EOF
{
"continue": false,
"stopReason": "發現 ${PENDING_COUNT} 個未完成的待辦事項",
"suppressOutput": false,
"systemMessage": "請完成以下待辦事項:\n${PENDING_ITEMS}\n\n完成後再結束對話。"
}
EOF
exit 0
fi
# 所有待辦事項都完成,允許停止
echo '{"continue": true}'
exit 0
settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "/Users/yourname/.claude/hooks/stop_hook.sh"
}
]
}
]
}
}
使用效果:
使用者:幫我實作登入功能
Claude:[建立待辦清單]
1. [ ] 建立登入 API 端點
2. [ ] 實作密碼驗證
3. [ ] 加入 JWT token 生成
4. [ ] 撰寫測試
Claude:[完成第 1、2 項]
Claude:任務完成!
Stop Hook:❌ 阻擋停止
訊息:發現 2 個未完成的待辦事項:
- [pending] 加入 JWT token 生成
- [pending] 撰寫測試
請完成以下待辦事項後再結束對話。
Claude:抱歉,我還需要完成剩餘的待辦事項...
[繼續完成 JWT 和測試]
案例 2:測試驗證 Hook
確保所有測試通過後才結束。
test_verification_hook.sh:
#!/bin/bash
# 防止無限迴圈
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
echo '{"continue": true}'
exit 0
fi
# 檢查是否有 package.json(Node.js 專案)
if [ -f "package.json" ]; then
# 執行測試
npm test > /tmp/test_output.txt 2>&1
TEST_EXIT_CODE=$?
if [ $TEST_EXIT_CODE -ne 0 ]; then
# 測試失敗,阻擋停止
TEST_OUTPUT=$(cat /tmp/test_output.txt | tail -n 20)
cat <<EOF
{
"continue": false,
"stopReason": "測試失敗",
"systemMessage": "測試未通過,請修復以下問題:\n\n\`\`\`\n${TEST_OUTPUT}\n\`\`\`"
}
EOF
exit 0
fi
fi
# 檢查是否有 pytest(Python 專案)
if [ -f "pytest.ini" ] || [ -f "setup.py" ]; then
pytest --tb=short > /tmp/test_output.txt 2>&1
TEST_EXIT_CODE=$?
if [ $TEST_EXIT_CODE -ne 0 ]; then
TEST_OUTPUT=$(cat /tmp/test_output.txt | tail -n 20)
cat <<EOF
{
"continue": false,
"stopReason": "測試失敗",
"systemMessage": "Python 測試未通過:\n\n\`\`\`\n${TEST_OUTPUT}\n\`\`\`"
}
EOF
exit 0
fi
fi
# 測試通過或沒有測試,允許停止
echo '{"continue": true}'
exit 0
案例 3:程式碼品質檢查 Hook
確保程式碼符合 linting 標準。
lint_check_hook.sh:
#!/bin/bash
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
echo '{"continue": true}'
exit 0
fi
# 檢查是否有 eslint
if [ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ]; then
npx eslint . --max-warnings 0 > /tmp/lint_output.txt 2>&1
LINT_EXIT_CODE=$?
if [ $LINT_EXIT_CODE -ne 0 ]; then
LINT_OUTPUT=$(cat /tmp/lint_output.txt)
cat <<EOF
{
"continue": false,
"stopReason": "Linting 失敗",
"systemMessage": "程式碼不符合 linting 標準:\n\n\`\`\`\n${LINT_OUTPUT}\n\`\`\`\n\n請修復這些問題。"
}
EOF
exit 0
fi
fi
# 檢查 TypeScript 編譯
if [ -f "tsconfig.json" ]; then
npx tsc --noEmit > /tmp/tsc_output.txt 2>&1
TSC_EXIT_CODE=$?
if [ $TSC_EXIT_CODE -ne 0 ]; then
TSC_OUTPUT=$(cat /tmp/tsc_output.txt | head -n 30)
cat <<EOF
{
"continue": false,
"stopReason": "TypeScript 編譯錯誤",
"systemMessage": "發現型別錯誤:\n\n\`\`\`\n${TSC_OUTPUT}\n\`\`\`"
}
EOF
exit 0
fi
fi
echo '{"continue": true}'
exit 0
防止無限迴圈
問題場景
Claude 完成工作 → Stop Hook 發現問題 → 要求 Claude 繼續
→ Claude 嘗試修復 → 再次停止 → Stop Hook 再次發現問題
→ 要求 Claude 繼續 → ... (無限迴圈)
解決方案
方法 1:使用 stop_hook_active 環境變數
#!/bin/bash
# Hook 開始時檢查
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
# 已經在 hook 執行中,避免遞迴
echo '{"continue": true}'
exit 0
fi
# 正常檢查邏輯...
方法 2:檢查對話歷史
#!/bin/bash
# 檢查最近是否已經觸發過此 hook
HOOK_LOG=".claude/hook_history.log"
CURRENT_TIME=$(date +%s)
if [ -f "$HOOK_LOG" ]; then
LAST_TRIGGER=$(tail -n 1 "$HOOK_LOG")
TIME_DIFF=$((CURRENT_TIME - LAST_TRIGGER))
# 如果 30 秒內已觸發,避免重複
if [ $TIME_DIFF -lt 30 ]; then
echo '{"continue": true, "systemMessage": "防止 hook 迴圈觸發"}'
exit 0
fi
fi
# 記錄此次觸發
echo "$CURRENT_TIME" >> "$HOOK_LOG"
# 正常檢查邏輯...
方法 3:限制觸發次數
#!/bin/bash
COUNTER_FILE=".claude/stop_hook_counter.txt"
# 讀取計數器
if [ -f "$COUNTER_FILE" ]; then
COUNTER=$(cat "$COUNTER_FILE")
else
COUNTER=0
fi
# 增加計數
COUNTER=$((COUNTER + 1))
echo "$COUNTER" > "$COUNTER_FILE"
# 超過 3 次就強制允許停止
if [ $COUNTER -gt 3 ]; then
echo '{"continue": true, "systemMessage": "Hook 已觸發 3 次,強制允許停止"}'
rm "$COUNTER_FILE" # 重置計數器
exit 0
fi
# 正常檢查邏輯...
Output Styles 遷移指南
棄用原因與影響
從 v2.0.30 開始,Output Styles 功能正式棄用。
官方說明
Output styles are now deprecated. Review options in
/output-styleand use--system-prompt-file,--system-prompt,--append-system-prompt, CLAUDE.md, or plugins instead.
影響範圍
- 不受影響:CLAUDE.md、plugins、命令列參數
- 受影響:使用
/output-style命令或.claude/output-styles/目錄的使用者 - 建議行動:在未來版本移除前盡快遷移
遷移路徑
graph TD
A[Output Styles 使用者] --> B{使用場景}
B -->|簡單提示詞調整| C[遷移到 CLAUDE.md]
B -->|複雜行為定義| D[遷移到 Plugin 系統]
B -->|臨時實驗| E[使用命令列參數]
C --> F[編輯 .claude/CLAUDE.md]
D --> G[建立 Plugin]
E --> H[--append-system-prompt]
F --> I[完成遷移]
G --> I
H --> I
style A fill:#e1f5fe
style I fill:#c8e6c9
遷移步驟
步驟 1:檢視現有配置
# 執行此命令檢視目前的 output style
/output-style
輸出範例:
Current Output Style: explanatory
Content:
---
name: Explanatory
description: Provides educational insights while working
---
# Explanatory Mode
When performing tasks, provide "Insights" sections that explain:
- Why certain approaches were chosen
- Key concepts involved
- Best practices being followed
...
步驟 2:選擇遷移目標
選項 A:遷移到 CLAUDE.md(推薦)
適用於:
- 專案特定的行為調整
- 團隊共享的規範
- 長期穩定的配置
操作:
# 建立或編輯 CLAUDE.md
vim .claude/CLAUDE.md
將 Output Style 內容加入:
# CLAUDE.md
## 程式碼風格指南
當執行任務時,請遵循以下原則:
### 解釋性輸出
在執行重要操作時,提供「洞察」區塊說明:
- 為何選擇特定方法
- 涉及的關鍵概念
- 遵循的最佳實踐
### 範例
\```typescript
// ✅ 洞察:使用依賴注入提升可測試性
class UserService {
constructor(private db: Database) {}
// ...
}
\```
## 程式碼審查重點
審查程式碼時,請檢查:
1. 型別安全性
2. 錯誤處理
3. 效能考量
4. 安全性問題
選項 B:遷移到 Plugin 系統
適用於:
- 複雜的行為定義
- 需要版本控制
- 跨專案共享
建立 Plugin 結構:
mkdir -p my-code-style-plugin
cd my-code-style-plugin
plugin.json:
{
"name": "my-code-style",
"version": "1.0.0",
"description": "My custom code style and behavior",
"systemPrompt": "system-prompt.md"
}
system-prompt.md:
# Custom Code Style
When writing code, follow these guidelines:
## Code Quality
- Use TypeScript strict mode
- Implement comprehensive error handling
- Write descriptive variable names
## Documentation
- Add JSDoc comments for public APIs
- Include usage examples
- Document edge cases
## Testing
- Write tests for all new features
- Cover edge cases
- Use descriptive test names
安裝 Plugin:
# 本地安裝
claude plugin install ./my-code-style-plugin
# 或推送到 Git 並安裝
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/yourname/my-code-style-plugin.git
git push -u origin main
claude plugin install git@github.com:yourname/my-code-style-plugin.git
選項 C:使用命令列參數(臨時使用)
適用於:
- 一次性實驗
- 快速測試
- 不需要持久化
# 使用檔案
claude --system-prompt-file custom-prompt.md
# 直接指定提示詞
claude --append-system-prompt "Focus on performance optimization"
# 完全覆蓋系統提示詞(不推薦)
claude --system-prompt "You are a code reviewer..."
替代方案比較
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| CLAUDE.md | • 簡單直接 • 專案內建 • Git 版本控制 |
• 每個專案需單獨配置 • 不易跨專案共享 |
專案特定規範 |
| Plugin | • 可重用 • 版本管理 • 易於分享 |
• 設定較複雜 • 需額外維護 |
團隊共享標準 |
| –system-prompt-file | • 靈活 • 臨時覆蓋 |
• 每次需手動指定 • 不持久化 |
實驗和測試 |
| –append-system-prompt | • 快速 • 不覆蓋現有設定 |
• 命令列參數冗長 | 快速調整 |
| –system-prompt | • 完全控制 | • 失去預設行為 • 不推薦 |
特殊需求 |
遷移決策樹
graph TD
A[Output Style 遷移] --> B{需要跨專案共享?}
B -->|是| C{是否需要版本控制?}
B -->|否| D[CLAUDE.md]
C -->|是| E[Plugin 系統]
C -->|否| F[全域 CLAUDE.md]
D --> G[在專案根目錄建立 .claude/CLAUDE.md]
E --> H[建立 Plugin 並發布到 Git]
F --> I[在 ~/.claude/CLAUDE.md 建立全域配置]
style A fill:#e1f5fe
style D fill:#c8e6c9
style E fill:#fff3e0
SDK 更新與向後相容性
SDK 重新命名
變更內容
# 舊版(已棄用)
@anthropic-ai/claude-code
# 新版
@anthropic-ai/claude-agent-sdk
遷移步驟
1. 更新 package.json:
{
"dependencies": {
// 移除舊版
// "@anthropic-ai/claude-code": "^1.0.0",
// 加入新版
"@anthropic-ai/claude-agent-sdk": "^1.0.0"
}
}
2. 更新 import 語句:
// 舊版
import { Agent } from '@anthropic-ai/claude-code';
// 新版
import { Agent } from '@anthropic-ai/claude-agent-sdk';
3. 安裝並測試:
# 移除舊版
npm uninstall @anthropic-ai/claude-code
# 安裝新版
npm install @anthropic-ai/claude-agent-sdk
# 執行測試
npm test
行為變更
1. 系統提示詞不再預設
舊版行為:
const agent = new Agent({
apiKey: process.env.ANTHROPIC_API_KEY
});
// 自動包含 Claude Code 的系統提示詞
新版行為:
const agent = new Agent({
apiKey: process.env.ANTHROPIC_API_KEY
// 不再自動包含系統提示詞
});
// 如需 Claude Code 行為,需明確指定
const agent = new Agent({
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: customSystemPrompt // 需明確提供
});
2. 設定檔案讀取變更
舊版行為:
// 自動從 ~/.claude/settings.json 讀取設定
const agent = new Agent({ apiKey: '...' });
新版行為:
// 需明確指定設定來源
import { loadSettings } from '@anthropic-ai/claude-agent-sdk';
const settings = loadSettings(); // 手動載入
const agent = new Agent({
apiKey: process.env.ANTHROPIC_API_KEY,
settings: settings
});
3. 必要的程式碼調整
遷移範例:
// --- 舊版程式碼 ---
import { Agent } from '@anthropic-ai/claude-code';
const agent = new Agent({
apiKey: process.env.ANTHROPIC_API_KEY
});
await agent.run('Create a new feature');
// --- 新版程式碼 ---
import { Agent, loadSettings } from '@anthropic-ai/claude-agent-sdk';
// 載入設定
const settings = loadSettings({
settingsPath: '.claude/settings.json' // 可選
});
// 建立 agent
const agent = new Agent({
apiKey: process.env.ANTHROPIC_API_KEY,
settings: settings,
// 如需 Claude Code 的系統提示詞行為
systemPromptFile: '.claude/system-prompt.md',
// 或直接提供
systemPrompt: `
You are a helpful coding assistant...
`
});
await agent.run('Create a new feature');
其他向後相容性問題
Ripgrep 配置移除
影響:
- 移除自訂 ripgrep 配置支援
- 解決搜尋無結果和配置發現失敗的問題
行動:
- 移除任何
.ripgreprc或自訂配置 - 使用 Claude Code 的預設搜尋設定
VSCode 快捷鍵變更(Windows)
v2.0.31 變更:
| 功能 | 舊快捷鍵 | 新快捷鍵 |
|---|---|---|
| 模式切換 | Alt + M |
Shift + Tab |
原因:
Alt + M與某些系統快捷鍵衝突Shift + Tab更符合原生安裝的行為
已修復的 Bugs
v2.0.28:
- ✅ 修復 Explore agent 建立不必要的 .md 調查檔案
- ✅ 修復
/context失敗並出現 “max_tokens must be greater than thinking.budget_tokens” 錯誤
v2.0.30:
- ✅ 修復
/compact因prompt_too_long失敗的問題 - ✅ 修復 plugin uninstall 無法移除 plugins 的問題
v2.0.31:
- ✅ 修復 VSCode respectGitIgnore 配置問題
完整遷移檢查清單
Output Styles 遷移
- 執行
/output-style檢視目前配置 - 決定遷移目標(CLAUDE.md / Plugin / 命令列)
- 建立新配置
- 測試新配置是否正常運作
- 移除舊的 output styles 檔案
- 更新團隊文件
SDK 更新
- 更新 package.json 依賴
- 更新 import 語句
- 加入明確的系統提示詞(如需要)
- 加入設定載入邏輯
- 執行完整測試套件
- 更新 CI/CD 配置
- 更新部署腳本
配置檔案檢查
- 移除
.ripgreprc自訂配置 - 檢查 settings.json 是否相容
- 確認 Hooks 配置正確
- 驗證 Sandbox 設定
- 檢查 MCP 伺服器配置
測試驗證
- 本地開發環境測試
- CI/CD 管道測試
- 團隊成員驗證
- 文件更新
- 版本控制提交
系列總結
經過三篇文章的深入探討,我們完整涵蓋了 Claude Code 2.0 的核心功能:
第 1 篇回顧:Skills 與 Sandbox
- Skills:模型驅動的智能擴展系統
- Sandbox:OS 層級安全隔離
- 整合應用:安全且強大的開發環境
第 2 篇回顧:Subagents 與 Plan Mode
- Plan Subagent:專注規劃的唯讀助手
- Interactive Questions:互動式需求澄清
- Subagent 增強:Resumption、動態模型、工具封鎖
第 3 篇重點:Hooks 與遷移
- Stop Hooks:工作流程自動化驗證
- Output Styles 遷移:平滑升級路徑
- SDK 更新:向後相容性處理
實踐建議
- 從小處開始:先在單一專案試用新功能
- 逐步遷移:按優先順序逐一遷移功能
- 團隊溝通:確保團隊成員了解變更
- 文件更新:保持文件與配置同步
- 持續學習:關注官方更新和最佳實踐
系列導航
← 上一篇:Subagents 與 Plan Mode - 智能協作新紀元
查看完整系列:Claude Code 2.0 核心功能深度解析
感謝您閱讀本系列文章!希望這些內容能幫助您更好地使用 Claude Code 2.0 的強大功能。如有任何問題或建議,歡迎在評論區交流討論。