用 Hook 把開發規範變成自動執行的基礎設施
寫進文件的規範由人記得執行,寫進 hook 的規範在每個關鍵時機自動執行。兩者的差別不在規範的內容,在執行率——文件版的執行率取決於當下有沒有想起來,hook 版的執行率是一。
把 hook 當成「跑幾個簡單檢查」的輔助工具,會低估它的位置:它可以是完整的品質控制基礎設施,在每個關鍵時機介入,執行那些應該做而容易漏掉的檢查。
Hook 的執行時機
Claude Code Hook 有五個觸發點——SessionStart、UserPromptSubmit、PreToolUse、PostToolUse、Stop——涵蓋整個開發互動的生命週期。
有了時機的清單,設計問題就變成:哪些規範該在哪個時機執行。
掛在各時機上的檢查
Session 啟動檢查(SessionStart):確認 git 遠端有沒有需要同步的變更、開發環境依賴是否完整、工作日誌狀態。這些檢查不阻止啟動,作用是讓工作一開始就有完整的情境。
任務逃避偵測(UserPromptSubmit):掃描內容裡有沒有出現「太複雜先跳過」「暫時不處理」這類詞彙,同時檢查行為模式——程式碼變更了而測試沒有對應變更、技術債務累積超過閾值。偵測到之後建立一個 block 標記檔案,後續所有工具呼叫都被阻止,直到問題被處理。
程式異味即時偵測(PostToolUse):每次檔案編輯後掃描變更的程式碼。函數超過 30 行、巢狀超過 4 層、參數超過 5 個、依賴數超過 10 個,觸發記錄並建議重構。這個 hook 採非阻塞設計——記錄,不中斷。
版本推進建議(Stop):分析當前的工作狀態——有沒有未提交的變更、工作日誌有沒有標記完成、TodoList 是否達成——據此建議接下來做小版本推進還是繼續開發。
文件同步提醒:程式碼變更後依檔案類型判斷哪些文件需要同步更新。API 異動對應 API 文件、架構異動對應架構文件。這類對應關係很難靠記憶維持,而它是規則,適合交給程式。
一條由重複事故確立的規則
Claude Code hook 系統的設計是:任何寫入 stderr 的輸出都會被視為 hook error 顯示給使用者。Python 的 logging 模組預設輸出到 stderr,所以 hook 即使正常執行,只要有 logging 輸出,UI 上就會出現 hook error 警告。
這個組合會重複發生,因為 logging 是寫 Python 腳本的默認動作。系統性的修法是一條規則:hook 禁止寫入 stderr,所有輸出走 stdout。新 hook 用一個指令驗證:
1grep -r "sys\.stderr" .claude/hooks/ --include="*.py"這個指令的預期結果永遠是空。
從各自實作到共用模組
hook 腳本各自獨立實作時,讀取 hook 輸入、輸出決策結果這些通用邏輯會在每個腳本裡重複一遍。
引入共用模組之後,.claude/lib/ 底下有幾個核心模組:hook_io.py 負責標準化 I/O、hook_logging.py 負責日誌、config_loader.py 載入配置、git_utils.py 封裝 git 操作。
換到兩件事:腳本只剩判斷邏輯,結構變薄;共用模組可以寫獨立的單元測試,hook 的正確性從難以驗證變成可驗證。
幾個設計原則
非阻塞優先。多數品質檢查的作用是記錄、追蹤、提示,不該中斷開發流程。只有關鍵違規——任務逃避、阻止狀態——才完全阻斷操作。
漸進式強制。從警告到記錄到追蹤到阻止,中間留出理解與修正的空間,而不是一次跳到拒絕。
可觀測性。hook 系統自己也需要被監控:一個 performance monitor hook 追蹤其他 hook 的執行時間,超過 5 秒視為需要處理。
配置外部化。品質規則的閾值、代理人分派規則放在 YAML 配置檔,不硬編碼在腳本裡,調整時只改配置。
規範進了 hook 之後,品質基線的維持方式從「每次記得執行」變成「不執行就過不去」——而規範本身的內容一個字都沒變。
程式異味的判定標準與閾值,走 Code Smell 品質閘門;任務逃避的完整偵測與三層防護,走 AI 任務逃避偵測。