Git Hooks + CLAUDE.md:自動化提醒團隊更新文檔
如果你還不熟悉 CLAUDE.md,建議先閱讀: 📖 打造 AI 友善的專案文檔:CLAUDE.md 完整指南(上)
🎯 前言
CLAUDE.md 寫好了,但三個月後呢?
專案在演進,架構在改變,指令在更新。CLAUDE.md 如果沒有跟著更新,Claude 就會照著過時的文檔工作,產出錯誤的結果。更糟的是——你可能根本不會發現文檔過期了,直到 Claude 做了一件莫名其妙的事。
文檔最大的敵人不是寫不好,是沒更新。
這篇文章分享如何用 Git Hooks 和 CI/CD 來自動檢查 CLAUDE.md 的健康度,讓「文檔過期」這件事被自動偵測出來。
📂 方案總覽
我用兩層機制來處理這個問題:
| 層級 | 時機 | 做什麼 |
|---|---|---|
| Git pre-commit hook | 每次 commit 前 | 提醒開發者檢查 CLAUDE.md |
| CI/CD 檢查 | 每次 push / PR | 驗證文檔的結構完整性 |
兩層的定位不同:pre-commit 是「善意提醒」,CI/CD 是「強制檢查」。
🔧 Git Pre-commit Hook:善意提醒
基本款:改了程式碼就提醒檢查文檔
最簡單的 hook——如果這次 commit 修改了關鍵檔案(路由、設定、架構),就提醒開發者看一下 CLAUDE.md 是否需要更新。
.git/hooks/pre-commit:
#!/bin/bash
# 定義「關鍵檔案」的模式
CRITICAL_PATTERNS=(
"src/pages/" # 路由變更
"astro.config" # 建置設定變更
"src/content.config" # Content Collections schema 變更
"package.json" # 相依套件變更
"tsconfig.json" # TypeScript 設定變更
)
# 取得這次 commit 的暫存檔案
STAGED_FILES=$(git diff --cached --name-only)
MATCHED=0
for pattern in "${CRITICAL_PATTERNS[@]}"; do
if echo "$STAGED_FILES" | grep -q "$pattern"; then
MATCHED=1
break
fi
done
if [ $MATCHED -eq 1 ]; then
# 檢查 CLAUDE.md 是否也在這次 commit 中
if ! echo "$STAGED_FILES" | grep -q "CLAUDE.md"; then
echo ""
echo "⚠️ 你修改了關鍵檔案,但 CLAUDE.md 沒有更新。"
echo ""
echo " 被修改的關鍵檔案:"
for pattern in "${CRITICAL_PATTERNS[@]}"; do
echo "$STAGED_FILES" | grep "$pattern" | sed 's/^/ - /'
done
echo ""
echo " 請確認 CLAUDE.md 是否需要同步更新。"
echo " 如果不需要,用 git commit --no-verify 跳過此檢查。"
echo ""
exit 1
fi
fi
exit 0
效果:
⚠️ 你修改了關鍵檔案,但 CLAUDE.md 沒有更新。
被修改的關鍵檔案:
- src/pages/blog/[...slug].astro
- astro.config.mjs
請確認 CLAUDE.md 是否需要同步更新。
如果不需要,用 git commit --no-verify 跳過此檢查。
不是每次修改路由都需要更新 CLAUDE.md,但至少提醒你想一下。真的不需要更新就用 --no-verify 跳過。
進階款:檢查 CLAUDE.md 的最後更新時間
如果 CLAUDE.md 超過 30 天沒有更新,在每次 commit 時溫和提醒:
#!/bin/bash
CLAUDE_MD="CLAUDE.md"
if [ -f "$CLAUDE_MD" ]; then
# 取得 CLAUDE.md 最後一次 commit 的時間戳
LAST_MODIFIED=$(git log -1 --format="%at" -- "$CLAUDE_MD" 2>/dev/null)
if [ -n "$LAST_MODIFIED" ]; then
NOW=$(date +%s)
DAYS_AGO=$(( (NOW - LAST_MODIFIED) / 86400 ))
if [ $DAYS_AGO -gt 30 ]; then
echo ""
echo "📋 CLAUDE.md 已經 ${DAYS_AGO} 天沒有更新了。"
echo " 最後更新:$(git log -1 --format='%ci' -- $CLAUDE_MD)"
echo " 建議檢查內容是否仍然正確。"
echo ""
# 這裡用 exit 0,只是提醒,不阻擋 commit
fi
fi
fi
exit 0
注意這個 hook 用 exit 0 結尾——只是提醒,不阻擋。避免因為文檔沒更新就卡住整個開發流程。
🔍 CI/CD 檢查:結構驗證
CI/CD 層適合做更嚴格的檢查。以下是幾個實用的檢查項目。
檢查 1:CLAUDE.md 是否存在
最基本的——確認 CLAUDE.md 沒有被不小心刪除:
# .github/workflows/docs-check.yml
name: Documentation Check
on:
pull_request:
branches: [main]
jobs:
check-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check CLAUDE.md exists
run: |
if [ ! -f "CLAUDE.md" ]; then
echo "❌ CLAUDE.md not found!"
exit 1
fi
echo "✅ CLAUDE.md exists"
檢查 2:必要章節是否完整
確認 CLAUDE.md 包含幾個關鍵章節:
#!/bin/bash
# scripts/check_claude_md_structure.sh
CLAUDE_MD="CLAUDE.md"
MISSING=0
REQUIRED_SECTIONS=(
"## 專案概述"
"## 開發指令"
"## 架構"
)
for section in "${REQUIRED_SECTIONS[@]}"; do
if ! grep -q "$section" "$CLAUDE_MD"; then
echo "❌ 缺少必要章節: $section"
MISSING=1
fi
done
if [ $MISSING -eq 0 ]; then
echo "✅ 所有必要章節都存在"
fi
exit $MISSING
檢查 3:開發指令是否可執行
確認 CLAUDE.md 中記錄的指令在 package.json 中確實存在:
#!/bin/bash
# scripts/check_commands.sh
# 從 CLAUDE.md 提取 npm run 指令
COMMANDS=$(grep -oP 'npm run \K\w+' CLAUDE.md | sort -u)
MISSING=0
for cmd in $COMMANDS; do
if ! jq -e ".scripts.\"$cmd\"" package.json > /dev/null 2>&1; then
echo "❌ CLAUDE.md 提到 'npm run $cmd',但 package.json 中不存在"
MISSING=1
fi
done
if [ $MISSING -eq 0 ]; then
echo "✅ 所有指令都存在於 package.json"
fi
exit $MISSING
這個檢查特別實用:如果有人重新命名了 npm script 但忘了更新 CLAUDE.md,CI 會抓到。
完整的 GitHub Actions Workflow
把上面的檢查組合成一個完整的 workflow:
name: Documentation Check
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
check-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 需要 git history 來檢查最後更新時間
- name: Check CLAUDE.md exists
run: |
if [ ! -f "CLAUDE.md" ]; then
echo "❌ CLAUDE.md not found!"
exit 1
fi
- name: Check required sections
run: bash scripts/check_claude_md_structure.sh
- name: Check npm commands
run: bash scripts/check_commands.sh
- name: Check last update
run: |
LAST_MODIFIED=$(git log -1 --format="%at" -- CLAUDE.md)
NOW=$(date +%s)
DAYS_AGO=$(( (NOW - LAST_MODIFIED) / 86400 ))
echo "CLAUDE.md 最後更新於 ${DAYS_AGO} 天前"
if [ $DAYS_AGO -gt 90 ]; then
echo "⚠️ CLAUDE.md 超過 90 天沒有更新,建議檢查內容是否仍然正確"
fi
💡 團隊協作的實務建議
誰該更新 CLAUDE.md?
規則很簡單:誰改了架構,誰更新文檔。
但現實中,大家都會忘。所以 pre-commit hook 的作用是「在你最有可能記得的時候提醒你」——你剛改完程式碼,對改動的細節記憶最清楚,這時候更新文檔最有效率。
不要把 hook 設得太煩
如果每次 commit 都跳出提醒,開發者會直接養成 --no-verify 的習慣,hook 就失去意義了。
好的做法:
- 只在修改「關鍵檔案」時才觸發提醒
- 用
exit 0(提醒但不阻擋)而不是exit 1(阻擋) - 保持訊息簡短,不要長篇大論
搭配 CLAUDE.md 的版本標記
在 CLAUDE.md 開頭加上最後審查日期,方便一眼看出文檔的新鮮度:
# CLAUDE.md
> 最後審查:2026-04-02
This file provides guidance to Claude Code...
CI 可以讀取這個日期做更精確的檢查。
📋 用 Husky 管理 Git Hooks
手動把腳本放進 .git/hooks/ 有個問題:.git/ 目錄不會被 git 追蹤,團隊成員 clone 後拿不到 hook。
用 Husky 解決:
# 安裝 Husky
npm install -D husky
# 初始化
npx husky init
建立 pre-commit hook:
# .husky/pre-commit
bash scripts/check_claude_md.sh
這樣 hook 就會跟著 git 走,團隊所有成員 npm install 後自動生效。
⚠️ 注意事項
不要在 hook 中做太重的事
pre-commit hook 在每次 commit 都會執行。如果你的檢查腳本要花 10 秒跑完,開發者會很痛苦。
建議:pre-commit 做輕量檢查(grep、檔案存在性),重的檢查(跑測試、執行指令)放 CI/CD。
不要強制阻擋,用提醒代替
除了「CLAUDE.md 被刪除」這種明顯錯誤,其他情況建議用提醒(exit 0)而不是阻擋(exit 1)。
原因:不是每次修改路由都需要更新 CLAUDE.md。如果 hook 太嚴格,開發者要嘛養成 --no-verify 的壞習慣,要嘛開始怨恨這個 hook。
Windows 相容性
如果團隊有人用 Windows,注意 shell 腳本的相容性。Husky 在 Windows 上預設用 Git Bash 執行,通常沒問題,但要避免用到只有 Linux 才有的工具。
🎉 結語
Git Hooks + CI/CD 不是要替你寫文檔,而是在你忘記更新文檔時提醒你。
這套機制的核心思想是:
- pre-commit:在你記憶最清楚的時候,善意提醒「文檔要不要也改一下?」
- CI/CD:在合併前,確認文檔的基本結構沒壞掉
兩層都不需要花很多時間設定。pre-commit hook 就是一個幾十行的 bash 腳本,CI 就是幾個 grep 檢查。投資報酬率很高。
📎 相關文章: