← 文章

那天 AI 決定「清理」,專案沒了

· 12 min read · 80 次閱讀
AI 輔助開發 Hooks 護欄 Context 管理 可觀測性 開源
AI 工程實戰

那天 AI 決定「清理」

專案沒了

同事的 AI CLI 工具刪了整個專案資料夾,差點無法復原。現在每個人都開全權限跑 AI——沒有護欄的全自動,就是全自動的災難。

事件

那天 AI 刪了資料夾#

真實事件,兩位同事,兩個不同的 CLI 工具#

我們團隊有人用 OpenAI Codex CLI,有人用 Google Gemini CLI。兩個工具都很強大,但都沒有設定任何 hook。

某天,其中一位同事讓 AI 清理暫存檔。AI 的判斷出了偏差,直接刪掉了整個專案資料夾。

$ rm -rf ./project-name
# AI 認為這是「清理」的一部分
# 沒有任何機制攔截這個指令

幸好有 git history 和 Time Machine 備份,花了不少時間才復原。另一位同事遇到類似情境——AI 在整理檔案時誤判路徑,刪掉了不該刪的目錄。

兩次事件的共同點:CLI 工具本身沒有問題。問題是沒有護欄。

AI 是機率性的系統。即使 prompt 寫了「不要刪除重要檔案」,模型仍然可能在某次推理中做出不同的判斷。Hooks 是確定性的——rm -rf / 永遠會被擋下,不管 AI 怎麼想。

現實

沒有人在一個一個 Allow#

理論上,AI CLI 工具都有權限確認機制。每次執行指令前會問你 allow 或 deny。

實際上,沒有人這樣用。一天按幾百次 allow,開發效率歸零。所以每個人都開最高權限:

# Claude Code — 跳過所有權限確認
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 擋危險指令。

三個月後,情況變成這樣:

1

腳本散落各處#

十幾個 .sh 檔案,有的在 hooks 目錄,有的在 scripts 目錄,有的在 home 目錄。每個都重複實作 JSON 解析和錯誤處理。

2

一個 crash 全部停擺#

某個 hook 因為 JSON 格式異常而 crash。Claude Code 的 hooks 機制是同步的:一個 hook 崩潰,整條 pipeline 中斷。

3

沒有 on/off 開關#

想暫時關掉某個 hook?要到 settings.json 裡手動改。改完測試後,經常忘記改回去。

解法

一個 Dispatcher,管理所有 Hooks#

我們把所有 hooks 整合成一個模組化框架:一個 dispatcher 接收所有事件,再依據設定檔分派到各個 handler。

34
Handler 模組
10
事件類型全覆蓋
1
YAML 設定檔
0
外部依賴
settings.json
Claude Code 原生設定。10 個事件類型,每個指向同一個 dispatcher
Claude Code
dispatcher.py
薄 shim,讀事件類型和 JSON 輸入,呼叫 handler registry
進入點
Two-Phase
Critical handlers 先跑(無預算限制),Deferrable handlers 在 5 秒預算內依序執行
調度策略
config.yaml
每個 handler 獨立開關。一行 YAML 切換,不需要碰 settings.json
設定層

Two-Phase Dispatch 是關鍵設計。安全類的 handler(如擋 rm -rf)標記為 critical,永遠第一個跑、不受時間預算限制。其他 handler 共用一個 5 秒預算——超時就跳過,但不會阻塞 AI 的工作流。

安全護欄

擋住 AI 的危險操作#

這幾個 handler 處理最關鍵的安全問題。它們標記為 critical,無條件優先執行。

bash_safety
PreToolUse · Bash

rm -rf /sudogit push --forcechmod 777 等危險指令。用 quote-aware 的 shell parser 解析命令,不會被引號或子 shell 繞過。

secret_scan
PreToolUse · Bash

git push 前掃描 diff,攔截 AWS Key、GitHub Token、PEM 私鑰等。只在推送時觸發,不干擾本地開發。支援 # nosec 逐行豁免。

skill_security
PreToolUse · Write/Edit

擋 prompt injection 攻擊。偵測「ignore previous instructions」等注入語句、dangerouslyDisableSandbox 等提權操作、curl 外傳資料等竊取行為。三類威脅,獨立掃描。

「當時如果有 bash_safety,同事的資料夾就不會被刪。這是我們第一個寫的 handler,也是整個專案的起點。」

品質閘門

讓 AI 自己檢查自己的作業#

AI 寫完程式碼後,經常直接 commit 而跳過驗證。這幾個 handler 確保 commit 前跑過該跑的檢查。

verify_commit
PreToolUse · Bash

攔截 git commitgh pr create。要求 AI 先跑完驗證(lint、typecheck),通過後寫入一個 marker 檔案,才放行 commit。一次性令牌:用完即消。

verify_completion
SubagentStop

Sub-agent 完成任務時,自動跑 lint 和 typecheck。如果不通過,把 agent 打回去修。最多 5 次迴圈、30 分鐘上限——超過就放行,避免卡死。

auto_format
PostToolUse · Edit/Write

AI 編輯完檔案後,自動跑 ruff(Python)或 biome(JS/TS)格式化。永遠不會阻擋——即使 formatter crash 也靜默放行。格式統一是好事,但不值得中斷工作流。

review_gate
Stop

Claude Code session 結束時,檢查是否有未 commit 的程式碼變更。預設只發出提醒(不阻擋),需要手動 opt-in 才會真正 block。避免 AI 做完事就走,改動留在 working tree 裡。

Context 管理

讓 AI 不要失憶#

AI Agent 的 context window 有限。Sub-agent 生出來時是空白的。這幾個 handler 處理「記憶」問題。

context_inject
SubagentStart

Sub-agent 啟動時,自動注入專案文件。讀取 .context/ 目錄下的 JSONL 設定檔,每個 agent type 可以有不同的注入內容。上限 8000 字元,避免塞爆 context。

context_relay
SessionStart · PreCompact

Session 之間的記憶接力。上一個 session 結束前寫好的交接文件,在新 session 啟動時自動注入。5 分鐘 TTL,防止過期資料干擾。

plan_impl_gate
PostToolUse · ExitPlanMode

AI 退出 Plan Mode 時,提醒先把計畫存到記憶裡再開始實作。因為 Plan Mode 本身消耗大量 context,不存就浪費了。一次性提醒,不重複。

治理與觀測

看見 AI 在做什麼#

AI Agent 在背景同時跑好幾個 sub-agent。如果不知道它們在做什麼,出問題時完全無從追查。

agent_naming
PreToolUse · Agent

強制每個 sub-agent 有描述性名稱。沒取名就擋下來。同時根據 prompt 內容建議合適的 agent type——寫程式碼用 worker,查資料用 explorer,寫文件用 writer。

observability
所有事件

每個 hook 事件都寫一行 JSONL 到 spool 檔案。延遲 <1ms,不影響任何操作。背景程式每 2 秒批量寫入資料庫,供 dashboard 查詢。整個鏈路零網路延遲。

sentinel_notify
PreToolUse · PostToolUse · Bash

AI 執行 service restart 或 docker 操作時,通知監控系統進入維護窗口。操作結束後自動解除。讓監控系統知道「這段停機是預期的」,不會誤報警。

voice_notify
Stop · AskUserQuestion

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、或桌面安裝器。

★ GitHub ↓ 下載 Installer

Homebrew

macOS 推薦。兩行搞定,不需要 clone。

brew tap operonlab/tap
brew install hook-observatory

# 安裝到 Claude Code
hook-observatory
Git Clone

所有平台通用。直接跑安裝腳本。

git clone https://github.com/operonlab/hook-observatory.git
cd hook-observatory
python3 install.py
安裝器

GUI 引導式安裝,勾選 handler、設定工具路徑。

macOS 首次開啟需執行:xattr -cr 安裝器.app

.dmg Apple Silicon .dmg Intel Mac .exe Windows

帶走這段

複製給你的 AI Agent#

如果你也在用 AI coding CLI,把這段貼給它,讓它幫你評估需要哪些護欄。

幫我評估目前的 AI coding 工作流需要哪些安全護欄。 我的情境: - 使用 [你的 CLI 工具名稱] 進行日常開發 - AI 可以執行 Bash 指令、編輯檔案、建立 sub-agent - 目前沒有任何 hook 或 pre-commit 機制 請幫我盤點: 1. 哪些破壞性指令需要被攔截?(rm -rf, force push, sudo 等) 2. git push 前需要掃描哪些 secret pattern? 3. commit 前應該自動執行哪些檢查?(lint, typecheck, 格式化) 4. sub-agent 需要哪些治理規則?(命名、context 注入、完成驗證) 5. 以上每條規則,對應的逃生口應該怎麼設計? 參考框架:https://github.com/operonlab/hook-observatory 先列出優先順序最高的 5 項建議。
參考資料

延伸閱讀#

過程中實際參考過、影響過設計決策的資源。

資源 為什麼重要
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 的設計
✦ 帶走這段