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 檢查。投資報酬率很高。


📎 相關文章