# 策展 · X (Twitter) 🔥🔥

> 作者：Vince 聊开发 (@vincemask) · 平台：X (Twitter) · 日期：2026-05-14

> 原始來源：https://x.com/vincemask/status/2054457804057100405

## 中文摘要

# 從 0 開始：用 Hooks 打造自動化 Claude Code 工作流

你讓 Claude Code 寫功能、補測試、格式化文件，最後順手提交。

它功能寫了，測試也補了大半，格式化跑了兩個文件，但偏偏漏了一個。然後它很自然地告訴你：「搞完了。」

你一看：沒提交，格式也沒完全對齊。

但這些問題，其實不該靠你事後檢查。在 Claude Code 裡，它們都可以配置成自動流程。

這就是 Hook 存在的理由。

---

## Hook 是什麼

Claude Code hooks 是在 Claude 生命週期的固定節點自動執行的 shell 指令。不需要你給 Claude 下任何指令，Hook 就已經跑完了。

Claude 可能不跑格式化工具。掛在 PostToolUse 上的 Hook 會在每次文件編輯後跑 Prettier。

Claude 可能忘了通知你。掛在 Notification 上的 Hook 每次 Claude 需要你介入時都會彈窗。

Hook 把行為從 Claude 的判斷裡抽出來，放到你的專案規則裡。程式碼品質、通知、安全檢查。這些最要緊的事應該放在 Hook 裡，不是 prompt 裡。

GitButler 的指南：從三個開始：PostToolUse 自動格式化、PreToolUse 攔截危險指令、Stop 桌面通知，覆蓋最廣，上手最容易。

---

## 先看這 5 個生命週期事件

Claude Code 有 20 多個生命週期事件，從 SessionStart 到 SessionEnd。不用全記。下面 5 個是實際工作中真會用的：

*   **PostToolUse**：Claude 用完工具後觸發。自動格式化、跑 linter、記變更日誌。
*   **PreToolUse**：Claude 呼叫工具之前觸發，可以攔截。保護敏感文件（.env、package-lock.json）、阻止危險指令、在 Claude 寫任何東西之前卡一刀。
*   **Notification**：Claude 需要你關注時觸發。彈桌面通知，你不用盯著終端，該幹嘛幹嘛。
*   **Stop**：Claude 回覆結束時觸發。跑測試、跑 CI、自動提交——Claude 說「搞完了」之後自動執行。
*   **SessionStart**：會話啟動或恢復時觸發。壓縮（compaction）之後重新灌上下文，或者載入環境相關的提醒。

剩下事件（SubagentStart、WorktreeCreate、InstructionsLoaded 等）給更複雜的流程。先從上面 5 個上手。

![](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1778723055219-iaHIH7U5hbUAAFQmpjpg.jpg)

---

## 三種值得用的 Hook type

除了標準的 shell 指令 Hook（"type": "command"），還有三種：

**Prompt Hook（"type": "prompt"）** 把 Hook 的輸入丟給 Claude 模型（預設用 Haiku）做判斷，返回是/否。適合需要模型理解的場景，比如在 Claude 停止前檢查是不是所有任務都完成了：

```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}
```

比寫正規表達式強。

**Agent Hook（"type": "agent"）** 起一個子 Agent，能讀文件、搜尋程式碼、跑工具，最多 50 輪。適合需要對照實際程式碼狀態做判斷的情況——比如 Claude 停之前確認測試確實過了。

**HTTP Hook（"type": "http"）** 把事件資料 POST 到 URL，不跑 shell。適合團隊稽核日誌、共享通知服務、webhook 整合。

多數場景 "type": "command" 夠用。

---

## 怎麼配 Hook

Hook 配置放在三個位置之一：

```plaintext
~/.claude/settings.json 所有專案
.claude/settings.json 當前專案（提交到程式庫，團隊共享）
.claude/settings.local.json 當前專案，不提交
```

結構都一樣：

```json
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolName|OtherTool",
        "hooks": [
          {
            "type": "command",
            "command": "your shell command here"
          }
        ]
      }
    ]
  }
}
```

Matcher 是正規表達式，過濾觸發條件。在 PostToolUse 和 PreToolUse 裡匹配工具名稱："Edit|Write" 在文件編輯後觸發，"Bash" 在 shell 指令後觸發，"mcp__github_.*" 在任意 GitHub MCP 工具呼叫後觸發。

最簡單的方式是用 `/hooks` 互動選單。在 Claude Code 裡敲 `/hooks`，選事件、設 matcher、填指令，不用手寫 JSON。加了就生效。

---

## 立即能配好的 5 個 Hook

### 1. Claude 需要你時彈通知

別盯終端了。每次 Claude 等你輸入——權限確認、空閒等待、任何需要你介入的時刻——自動彈：

```json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
```

放 `~/.claude/settings.json`。這個要全域生效。

### 2. Claude 碰過的文件自動格式化

每次 Edit 或 Write 之後跑 Prettier。Claude 逐文件改程式碼導致的格式不一致，一勞永逸：

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}
```

需要 jq 做 JSON 解析，macOS 上安裝：

```bash
brew install jq
```

放專案的 `.claude/settings.json` 並提交。這是團隊規範。

### 3. 不讓 Claude 碰受保護文件

攔住 .env、package-lock.json、.git/。被攔後 Claude 會收到回饋說明原因：

```bash
#!/bin/bash
# .claude/hooks/protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0
```

`chmod +x .claude/hooks/protect-files.sh`，然後註冊：

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}
```

### 4. 壓縮後把上下文填回去

上下文視窗滿了，Claude 會壓縮對話總結。有時候重要細節被丟掉了。這個 Hook 在每次壓縮後觸發，提醒 Claude 最核心的事：

```json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}
```

echo 換成動態指令也行：`git log --oneline -5` 看最近提交，或 `cat .claude/sprint-context.md` 載入獨立上下文文件。

### 5. Git 提交強制規範

Claude 有時候按自己喜好寫 commit message——type 前綴不對、用了句子大小寫、末尾拖個句號。這個 Hook 在提交落地前攔一道：

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Look at tool_input.command. If it does NOT start with 'git commit', return {\"ok\": true}. If it is a git commit command, extract the commit message and check only the FIRST LINE (subject line) — ignore any body lines after the first newline. Check: (1) format is 'type: Capitalized description' where type is one of feat/fix/refactor/docs/chore/perf/revert, (2) description starts with a capital letter, (3) no trailing period, (4) no 'Co-Authored-By' trailer anywhere in the command. Return {\"ok\": false, \"reason\": \"[specific issue. Corrected subject line: type: Description]\"} if any check fails. Return {\"ok\": true} if all pass."
          }
        ]
      }
    ]
  }
}
```

這是 Prompt Hook——不寫 shell 腳本，不寫正規表達式，讓 Haiku 模型讀指令、對照你的規範判斷，直接返回結果。

放 `~/.claude/settings.json`，所有專案生效。

---

## 退出碼怎麼用

Hook 腳本透過三個通道和 Claude Code 通信：

*   **Stdout**: 會被加入 Claude 的上下文中
    *   適用於 SessionStart、UserPromptSubmit 這些 hooks
*   **Stderr**: 當你以退出碼 2 退出時，會顯示給 Claude
*   **Exit code**: 決定接下來發生什麼：
    *   `0` → 繼續執行
    *   `2` → 阻止當前動作
        *   stderr 中的訊息會作為回饋傳給 Claude
    *   其他退出碼 → 繼續執行
        *   stderr 會被記錄下來
        *   可以透過 Ctrl+O 查看

PreToolUse Hook 真正的威力就靠這個退出碼體系：你的腳本讀到 Claude 即將執行的操作，對照規則判斷，要麼放行，要麼返回具體原因。

![](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1778723055873-iaHIH7lpsacAAPjb4jpg.jpg)

---

## 三步上手

**第一步：全域加通知 Hook**
打開 `~/.claude/settings.json`，貼上面 macOS 通知的配置。最實用、零風險，純附加行為，不攔截任何東西。讓 Claude 做一件會觸發權限確認的事，看看彈沒彈。

**第二步：當前專案加 Prettier 格式化**
在專案根目錄打開 `.claude/settings.json`（沒有就新建），加 PostToolUse 格式化 Hook。沒裝 jq 先裝。讓 Claude 改個文件，確認自動格式化了。

**第三步：打開 `/hooks` 翻一翻**
互動選單列出全部事件和說明。想想你的工作流裡還有哪些可以自動化。Stop 事件（Claude 完成後跑測試）和 PreToolUse（攔截特定模式）是下一個選擇。

---

## Hook 的使用總結

沒有 Hook 時，你可能需要結束後追問和檢查：格式化做了嗎？測試跑了嗎？程式碼提交了嗎？

配置 Hook 之後，這些問題解決。格式化一定會執行，測試一定會運行，通知一定會彈出。它們不再依賴 Claude 是否記得、是否理解、是否照做，而是變成專案環境本身的預設行為。

不要把關鍵流程寄託在 prompt 約束上。應透過環境機制強制執行關鍵動作，確保它們在每次運行中穩定執行。

## 標籤

Claude Code, CLI, 自動化, 教學資源, Anthropic, Claude
