那天 AI 決定「清理」
專案沒了
同事的 AI CLI 工具刪了整個專案資料夾,差點無法復原。現在每個人都開全權限跑 AI——沒有護欄的全自動,就是全自動的災難。
那天 AI 刪了資料夾#
真實事件,兩位同事,兩個不同的 CLI 工具#
我們團隊有人用 OpenAI Codex CLI,有人用 Google Gemini CLI。兩個工具都很強大,但都沒有設定任何 hook。
某天,其中一位同事讓 AI 清理暫存檔。AI 的判斷出了偏差,直接刪掉了整個專案資料夾。
# AI 認為這是「清理」的一部分
# 沒有任何機制攔截這個指令
幸好有 git history 和 Time Machine 備份,花了不少時間才復原。另一位同事遇到類似情境——AI 在整理檔案時誤判路徑,刪掉了不該刪的目錄。
兩次事件的共同點:CLI 工具本身沒有問題。問題是沒有護欄。
AI 是機率性的系統。即使 prompt 寫了「不要刪除重要檔案」,模型仍然可能在某次推理中做出不同的判斷。Hooks 是確定性的——rm -rf / 永遠會被擋下,不管 AI 怎麼想。
沒有人在一個一個 Allow#
理論上,AI CLI 工具都有權限確認機制。每次執行指令前會問你 allow 或 deny。
實際上,沒有人這樣用。一天按幾百次 allow,開發效率歸零。所以每個人都開最高權限:
claude --dangerously-skip-permissions
# OpenAI Codex CLI — YOLO 模式,自動批准一切
codex --approval-mode full-auto
# Google Gemini CLI — 同樣的 YOLO 模式
gemini --sandbox=false
這不是少數人的行為,這是常態。開全權限是唯一能讓 AI agent 真正發揮效率的方式。
但全權限意味著 AI 可以執行任何指令:刪檔案、force push、寫入系統目錄、推送含密鑰的 commit。沒有護欄的全自動,等於把你的開發環境交給一個「大部分時候很聰明,偶爾會犯錯」的系統。
問題不是「要不要開全權限」——而是「開了之後,誰來擋住那個偶爾?」
散落的 Shell Scripts 會失控#
那次事件後,我們開始幫 Claude Code 加 hooks。一開始很自然:寫幾個 shell script 擋危險指令。
三個月後,情況變成這樣:
一個 Dispatcher,管理所有 Hooks#
我們把所有 hooks 整合成一個模組化框架:一個 dispatcher 接收所有事件,再依據設定檔分派到各個 handler。
Two-Phase Dispatch 是關鍵設計。安全類的 handler(如擋 rm -rf)標記為 critical,永遠第一個跑、不受時間預算限制。其他 handler 共用一個 5 秒預算——超時就跳過,但不會阻塞 AI 的工作流。
擋住 AI 的危險操作#
這幾個 handler 處理最關鍵的安全問題。它們標記為 critical,無條件優先執行。
擋 rm -rf /、sudo、git push --force、chmod 777 等危險指令。用 quote-aware 的 shell parser 解析命令,不會被引號或子 shell 繞過。
在 git push 前掃描 diff,攔截 AWS Key、GitHub Token、PEM 私鑰等。只在推送時觸發,不干擾本地開發。支援 # nosec 逐行豁免。
擋 prompt injection 攻擊。偵測「ignore previous instructions」等注入語句、dangerouslyDisableSandbox 等提權操作、curl 外傳資料等竊取行為。三類威脅,獨立掃描。
「當時如果有 bash_safety,同事的資料夾就不會被刪。這是我們第一個寫的 handler,也是整個專案的起點。」
讓 AI 自己檢查自己的作業#
AI 寫完程式碼後,經常直接 commit 而跳過驗證。這幾個 handler 確保 commit 前跑過該跑的檢查。
攔截 git commit 和 gh pr create。要求 AI 先跑完驗證(lint、typecheck),通過後寫入一個 marker 檔案,才放行 commit。一次性令牌:用完即消。
Sub-agent 完成任務時,自動跑 lint 和 typecheck。如果不通過,把 agent 打回去修。最多 5 次迴圈、30 分鐘上限——超過就放行,避免卡死。
AI 編輯完檔案後,自動跑 ruff(Python)或 biome(JS/TS)格式化。永遠不會阻擋——即使 formatter crash 也靜默放行。格式統一是好事,但不值得中斷工作流。
Claude Code session 結束時,檢查是否有未 commit 的程式碼變更。預設只發出提醒(不阻擋),需要手動 opt-in 才會真正 block。避免 AI 做完事就走,改動留在 working tree 裡。
讓 AI 不要失憶#
AI Agent 的 context window 有限。Sub-agent 生出來時是空白的。這幾個 handler 處理「記憶」問題。
Sub-agent 啟動時,自動注入專案文件。讀取 .context/ 目錄下的 JSONL 設定檔,每個 agent type 可以有不同的注入內容。上限 8000 字元,避免塞爆 context。
Session 之間的記憶接力。上一個 session 結束前寫好的交接文件,在新 session 啟動時自動注入。5 分鐘 TTL,防止過期資料干擾。
AI 退出 Plan Mode 時,提醒先把計畫存到記憶裡再開始實作。因為 Plan Mode 本身消耗大量 context,不存就浪費了。一次性提醒,不重複。
看見 AI 在做什麼#
AI Agent 在背景同時跑好幾個 sub-agent。如果不知道它們在做什麼,出問題時完全無從追查。
強制每個 sub-agent 有描述性名稱。沒取名就擋下來。同時根據 prompt 內容建議合適的 agent type——寫程式碼用 worker,查資料用 explorer,寫文件用 writer。
每個 hook 事件都寫一行 JSONL 到 spool 檔案。延遲 <1ms,不影響任何操作。背景程式每 2 秒批量寫入資料庫,供 dashboard 查詢。整個鏈路零網路延遲。
AI 執行 service restart 或 docker 操作時,通知監控系統進入維護窗口。操作結束後自動解除。讓監控系統知道「這段停機是預期的」,不會誤報警。
AI 做完工作時,用 TTS 語音通知你。不用盯著螢幕等——去泡咖啡,完成了它會叫你。需要你回答問題時也會用語音提示。七層 guard 機制過濾 sub-agent 的中間狀態,只在真正完成時才發聲。
三個原則,貫穿所有 Handler#
Block-at-Submit,不是 Block-at-Write#
AI 寫檔案時不擋。在它要 commit 或 push 時才擋。
原因:AI 的工作流是連貫的。如果在 Edit 時就 block,AI 會失去上下文,不知道自己寫到哪。等到它要提交成果時再審查,更合理。
Fail-Open,永遠#
每個 handler 都包在 try/except 裡。Handler crash 了?放行。Config 讀取失敗?放行。
原因:護欄不應該比它保護的東西更危險。一個 crash 的 handler 不該讓整個 AI 工作流停擺。
每條規則都有逃生口#
secret_scan 有 # nosec,verify_commit 有 marker 機制,bash_safety 有環境變數開關。
原因:沒有逃生口的規則,最終會被整個關掉。給使用者繞過的方式,反而能讓規則活更久。
比做了什麼更重要的,是沒做什麼#
- 不攔截 Edit / Write AI 寫檔案時不干預。中途打斷 AI 的寫入流程,會造成不完整的檔案狀態。所有審查都延遲到 commit 或 push 時進行。
- 不在閘門裡自動跑測試 verify_completion 只跑 lint 和 typecheck,刻意排除測試。測試太慢、有副作用、可能改變資料庫狀態。放在自動閘門裡,風險比收益大。
- 不自動修復,只阻擋並解釋 bash_safety 不會幫 AI 改寫「更安全的指令」。它只說「這個指令被擋了,因為 X」。讓 AI 自己決定下一步。自動改寫容易引入更詭異的 bug。
- 不做 context budget 評分 我們寫過一個 context_supervisor handler(800 行),嘗試計算 context 使用量並建議壓縮時機。概念很好,但評分模型不準確——context 的「重要性」無法用啟發式規則判斷。已禁用。
- 不 Fail-Closed 所有 handler crash 都放行。有人會問「這不危險嗎?」——更危險的是護欄自己把整個系統卡住。AI 不動了,使用者也不知道為什麼。
hook-observatory#
MIT 授權,零外部依賴,裝完即用。
三種安裝方式:Homebrew、Git Clone、或桌面安裝器。
macOS 推薦。兩行搞定,不需要 clone。
brew install hook-observatory
# 安裝到 Claude Code
hook-observatory
所有平台通用。直接跑安裝腳本。
cd hook-observatory
python3 install.py
GUI 引導式安裝,勾選 handler、設定工具路徑。
macOS 首次開啟需執行:xattr -cr 安裝器.app
複製給你的 AI Agent#
如果你也在用 AI coding CLI,把這段貼給它,讓它幫你評估需要哪些護欄。
延伸閱讀#
過程中實際參考過、影響過設計決策的資源。
| 資源 | 為什麼重要 |
|---|---|
| operonlab/hook-observatory | 本文討論的開源專案 |
| Blake Crosley: Why Each of My 95 Hooks Exists | incident-driven 的 hook 建構思路。每個 hook 背後都有一個事故。 |
| GitButler: Automate Your AI Workflows with Claude Code Hooks | 最早的 hooks 實戰分享之一,啟發了我們的初始設計 |
| aiorg.dev: Claude Code Hooks Complete Guide | 「確定性 vs 機率性」的框架描述,精準表達了 hooks 的價值定位 |
| Jackle: Claude Code Voice Mode | 用 hooks 實現語音通知的實作參考,啟發了 voice_notify handler 的設計 |