Argus 研究筆記¶
資料來源¶
| 項目 | 連結 |
|---|---|
| GitHub repo(★5 / 0 forks / JavaScript) | github.com/NathanWu8343/Argus |
| 作者 | nathawu(NathanWu8343) |
| Topics | claude-code、openai-codex |
Metadata(2026-09-16 即時抓取): 建立於 2026-09-14、最後 push 2026-09-15(只有兩天大,3 個 commit)· 預設分支
master· repo size 35 KB · 無 LICENSE 檔 · plugin 版本0.4.0。本筆記的判斷來自
git clone後實際讀完全部原始碼並在本機跑過測試(node --test "tests/*.test.js"→ 33 tests / 32 pass / 1 skip,skip 的那個需要設定CODEX_CLI指向 Codex CLI 進入點)。
三個要先知道的事
- 沒有 LICENSE 檔。 repo 公開但沒授權聲明,法律上預設是「保留所有權利」——可以讀、可以學,但要拿去改造散布或塞進公司流程前,最好先問作者。
- 它是 fail-open,不是 fail-safe。
core.js最外層用try/catch把所有錯誤吞掉,註解寫得很明白:「A failure inside argus always lets the action through; it must never block normal use」。所以 Node.js 沒裝、狀態目錄不可寫、JSON 解析失敗,閘門會安靜地打開。當成「防手滑」很好,當成「安全邊界」不行。 - 很新、很小。 兩天大、3 個 commit、5 顆星。設計品質不錯,但還沒被時間驗證過。
專案概述¶
Argus(百眼巨人)解決的是一個每天都在發生的問題:你講 A、AI 做 B,等你發現時它已經改了七個檔案。
作法是在 AI 動手前插一道閘門:
- 你用
/argus:confirm-first <需求>(Claude Code)或$confirm-first <需求>(Codex)發動。 - AI 不准碰任何工具,只能根據你的文字,列出最多 5 點白話需求重點;自己補的部分要另外列並前綴「(假設)」。
- AI 用
AskUserQuestion跳出三選一:正確,開始執行/需要修正/取消此請求。 - 你選「正確,開始執行」,閘門才放行。
README 給的範例長這樣:
架構:一個核心 + 兩個 adapter¶
整包程式碼小到可以全部讀完:
| 檔案 | 行數 | 作用 |
|---|---|---|
hooks/core.js |
125 | 共用狀態機與決策邏輯 |
hooks/argus.js |
23 | Claude Code adapter |
codex/src/argus.js |
83 | Codex adapter |
scripts/build-codex.js |
46 | 從 codex/src/ 產生可安裝的 Codex 套件 |
tests/*.js |
487 | 33 個測試 |
測試行數是實作的 1.7 倍,這在只有 3 個 commit 的個人專案裡很少見。
core.js 開頭的註解直接定義了 adapter 介面,宿主之間所有差異都收斂在這裡:
// stateNamespace host subdirectory when ARGUS_STATE_DIR overrides the default
// stateDir default directory for state files
// invocation RegExp matching a prompt that starts the gate
// questionTool tool name allowed while pending
// answerOf (tool_response, question) → the label the user picked, or undefined
// denyReason text returned when a tool is denied
// stopReason when set, ending the turn is blocked once while pending
兩個宿主的差異:
| Claude Code | OpenAI Codex | |
|---|---|---|
| 觸發 | /argus:confirm-first |
$confirm-first(也吃 [$confirm-first](...) 連結形式) |
| 允許的提問工具 | AskUserQuestion |
request_user_input |
| 狀態目錄 | ~/.claude/argus |
$CODEX_HOME/argus(預設 ~/.codex/argus) |
| 答案取法 | response.answers[question.question] |
answers[question.id].answers(陣列,長度必須是 1) |
Stop hook |
有,會擋一次讓回合結束在提問上 | 沒有(答案是以下一則 prompt 進來的,回合必須能結束) |
狀態機:一個 .pending 檔就是全部¶
狀態只有 idle 與 pending 兩種,而 pending 的定義就是「狀態檔存在」:
沒有資料庫、沒有 JSON schema、沒有記憶體常駐,檔案在就是 pending,檔案不在就是 idle。副作用是閘門會跨 session 存活——你關掉再 --resume,閘門還在。被放生的狀態檔則在每次 prompt 時掃一遍,超過 24 小時就清掉。
四個 hook 的分工:
UserPromptSubmit:比對invocation正則決定要不要進 pending;如果這則 prompt 剛好是確認標籤,就解除。PreToolUse(matcher*):pending 期間,除了提問工具一律回permissionDecision: "deny",並在permissionDecisionReason裡告訴模型「現在在對齊需求,把覆述寫完然後用 AskUserQuestion 問」。PostToolUse:抓提問工具的回答,正確,開始執行→ 解除並注入additionalContext要模型照最後一版覆述執行。Stop(僅 Claude):pending 且還沒擋過就decision: "block",逼模型把回合結束在提問上。用stop_hook_active防止無限迴圈。
三個設計細節值得抄¶
1. 確認必須是精確字串,語意上的「好」不算
SKILL.md 也寫死:「Free text that merely means confirm or cancel is not one; ask again.」模型不能自己判斷「使用者應該是同意了」——這正是這類閘門最容易被繞過的地方。
2. Codex 版把 skill 內文塞進 context,因為閘門擋住了檔案讀取
閘門連 Read 都擋,所以 Codex adapter 不能叫模型自己去讀 SKILL.md,它直接在 UserPromptSubmit 時把 skill 全文當 additionalContext 注入:
// The gate denies file reads, so the skill text travels with the prompt instead of being read from disk.
這是「把自己的規則擋在門外」的典型陷阱,作者有意識地處理掉了。
3. 明令禁用 async 提問工具
Codex adapter 的 deny reason 特別寫「Never use request_user_input_async(its question card is dismissed when the turn ends), sleep or any other tool while waiting」。這種宿主層級的坑只有實際踩過才寫得出來。
安裝¶
需要 Node.js。
Codex 的 default_mode_request_user_input 是讓 Default 模式支援選項卡片用的實驗功能;嫌警告吵可在 config.toml 加 suppress_unstable_features_warning = true。
開發與除錯¶
node scripts/build-codex.js # 從 codex/src/ 產生 codex/plugins/argus/
node scripts/build-codex.js --check # 檢查產生套件與原始碼是否同步
node --test "tests/*.test.js"
ARGUS_DEBUG=1 會把每次 hook 的 stdin 原文寫進狀態目錄的 debug.log。另外 ARGUS_STATE_DIR 可以把狀態目錄整個搬走(測試隔離就是靠這個,tests/isolation.test.js)。
評估¶
適合:架構重構、跨模組修改、DB schema 或生產設定這類不可逆操作,以及需求本身就長又模糊的時候。用一次對齊換掉「AI 改錯 → git reset → 重來」的成本。
不適合:當成安全機制。fail-open 的設計意味著它防的是誤解,不是惡意;真的要擋危險操作應該用 permission rules 或 deny hook。
還缺的:
- 沒有 LICENSE。
- 沒有 CI,測試要手動跑。
- disable-model-invocation: true 代表只能手動觸發,沒有「偵測到高風險操作就自動開閘」的模式。
- 狀態檔用 session_id 當檔名,多個併發 session 各自獨立(這點是對的),但沒有跨 session 的全域開關。
相關筆記¶
- Claude Code 最佳實踐(Boris Cherny) — hooks 與權限控制的官方觀點
- Coding Agent 工具總覽