Applied AI School
v0 · 規劃中
Anthropic

實用 hook 範例集

Formatter / test runner / lint feedback / access control 四個範例。

TL;DR

  • 四個常用範例可以直接抄走:formattertest runnerlint feedbackfile access control
  • 每個給可複製的 settings.json 片段 + 何時用 + 邏輯說明
  • hook 命令可以是 inline shell,不一定要寫獨立 script 檔

這篇怎麼讀

上一篇你已經會寫 hook 了。這篇是 cookbook——掃過去挑你需要的抄,不要從頭讀到尾。

四個範例的觸發點與用途速查:

範例eventmatcher解決什麼
FormatterPostToolUseWrite|EditClaude 寫的檔自動格式化
Test runnerPostToolUseEdit改完立刻知道有沒有壞
Lint feedbackPostToolUseEdit把 lint error 丟回 Claude 自己修
Access controlPreToolUseReadblock 讀 .env 等機密檔

1. Formatter

Claude 寫出來的檔常常空白、引號跟你 repo 不一致。每次都要再叫它跑一次 prettier 很煩,直接掛 hook 自動跑。

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.file_path' | xargs -I{} pnpm exec prettier --write {}"
      }]
    }]
  }
}

從 stdin 拿 hook payload、抽出 file_path、丟給 prettier。改成 eslint --fixruff format 也是同個 pattern。

2. Test runner

改完 foo.ts 自動跑 foo.test.ts。發現壞掉立刻知道,不用等 Claude 整輪做完才發現。

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit",
      "hooks": [{
        "type": "command",
        "command": "f=$(jq -r '.tool_input.file_path'); t=${f%.ts}.test.ts; test -f \"$t\" && pnpm vitest run \"$t\""
      }]
    }]
  }
}

關鍵是 test -f——對應 test 檔不存在就 silent skip,不要每個 edit 都炸錯誤訊息。

3. Lint feedback

跟 formatter 不一樣:lint 抓的是 logic 問題(unused var、any、未處理 promise),需要 Claude 真的改邏輯。把 stderr 丟回 Claude 讓它自己修。

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit",
      "hooks": [{
        "type": "command",
        "command": "f=$(jq -r '.tool_input.file_path'); pnpm eslint \"$f\" 1>&2 || exit 2"
      }]
    }]
  }
}

exit 2 是關鍵——Claude Code 把 stderr 當作 feedback 餵回 Claude,它看到 lint error 會接著修。exit 1 只會 abort,沒這個效果。

4. File access control

讓 Claude 永遠不能讀 .envsecrets/*.pem 這類檔案。比靠 prompt 提醒可靠。

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Read",
      "hooks": [{
        "type": "command",
        "command": "f=$(jq -r '.tool_input.file_path'); case \"$f\" in *.env|*/secrets/*|*.pem) echo \"blocked: $f\" 1>&2; exit 2;; esac"
      }]
    }]
  }
}

PreToolUse + exit 2 會直接 block 那次 Read,stderr 訊息會顯示給 Claude 知道為什麼被擋。比 permissions.deny 更靈活——能寫 pattern、能附理由。

接下來

四個範例都會踩到一些雷:matcher 寫錯、stdin payload 結構搞混、exit 2exit 1 行為差很多。下一篇 Hooks 雷區 把常見坑收齊。