一個 coding agent 的專案設定危不危險,要看它在你點頭之前做了哪些事。場景很普通:你 clone 了一個陌生的 repo,想叫 agent 幫你讀懂它,cd 進去,手指停在 Enter 上。repo 裡的設定檔你一行都還沒看,它卻會比你更早被 agent 讀進去。

直覺會說,答案取決於設定檔的格式。JSON 只是資料,不會動;Bash 會跑,所以危險。把 Crush 跟 Claude Code 的文件並排讀完,我覺得這個直覺只對一半,錯的那一半剛好是最該小心的地方。

Crush:設定檔本人就會動手

Crush 是 Charmbracelet 做的終端機 AI coding agent。2026 年 10 月 6 日用 GitHub API 查,它有 28,502 顆星,最新 release 是 9 月 29 日發布的 v0.97.1。我沒有安裝過 Crush,下面講它的部分全部出自 README 和 docs/config/README.md。

它的設定檔叫 crushrc,README 的形容很直接:「A crushrc is just Bash with some Crush-specific builtins. It’s a lot like a .bashrc, just for your Crush.」裡面可以寫 provider add、model add、permissions allow view edit 這類內建指令,也能寫一般的 Bash。README 的範例在 mcp add 的 header 裡用 $(op read ...) 去 1Password 拿 token,也示範了用 if [[ $HOSTNAME == ... ]] 依機器載入不同設定。

表達力很高。密鑰不用躺在設定檔裡,一行 $(...) 從密碼管理器拿;家裡和公司兩台機器共用一份設定,靠 if 分流。寫程式的人會喜歡這種設計,因為它就是程式。

麻煩在查找順序。Crush 會找 ./.crushrc、./crushrc、~/.config/crush/crushrc 三個位置,找到的全部合併,排越前面的越優先。專案目錄裡的設定,壓過你家目錄的那份。陌生 repo 如果帶了一個 .crushrc,它會跟你自己的設定一起跑,兩邊衝突時聽它的。

文件沒有迴避這件事。README 的「A note on security」原文是:

Both crushrc and crush.json are trusted code; crushrc runs in a full shell, and any $(...) in crush.json runs at load time. Don’t launch Crush in a directory whose config you haven’t reviewed, and don’t randomly source files from the internet into your config.

docs 那頁再補一句,這些設定「run with your shell privileges before the UI appears」。舊格式 crush.json 已經標成 deprecated,而且只有特定字串欄位(API key、URL、MCP 與 LSP 的指令和參數、headers)會在載入時做 shell 展開;crushrc 沒有這層限制,文件的說法是「it’s all just Bash」。

Crush 的信任邊界畫在你身上:開之前,自己讀。

我本來想找的是另一樣東西,第一次打開某個資料夾時,會不會跳一個「你信任這裡嗎」的確認。在 README、docs/config/README.md、docs/config/FUTURE.md 裡搜 trust,命中的只有上面那句「trusted code」。這只說明我讀到的文件沒寫,不代表程式裡沒有;原始碼我沒讀,Crush 也沒跑過。這題要回答得去翻原始碼,搜文件這條路走到這裡就斷了。

Claude Code:JSON 也會跑指令

換到 Claude Code,直覺錯的那一半就露出來了。

它的設定是 JSON,有公開的 schema(https://json.schemastore.org/claude-code-settings.json)。可是 JSON 裡有幾個欄位,填進去的就是指令:hooks 在事件發生時執行指令,apiKeyHelper 用你指定的指令產生 API 金鑰。env 雖然不是指令,卻會替每個 session 和它底下的子行程設定環境變數。格式是資料,內容照樣能動手。

優先序也沒站在你這邊。官方 settings 文件列的順序由高到低是 managed settings、命令列的 --settings、.claude/settings.local.json、.claude/settings.json,最後才是 ~/.claude/settings.json。專案層一樣壓過使用者層,跟 Crush 沒兩樣。

格式和優先序都對不上直覺,那差別在哪?在它把欄位分成兩種,各自在不同時間點生效。

想像你搬進一間別人住過的房子,門上貼著前房客留的紙條。紙條寫「多加一道鎖」,照做不會吃虧,最壞是自己進出麻煩一點。紙條寫「把備份鑰匙交給樓下那位」,就該先問這人是誰。Claude Code 處理專案設定的方式很像這樣。依 permissions 文件,專案層的 permissions.allow、additionalDirectories、extraKnownMarketplaces 和多數 env 值,要等你在 workspace trust 對話框按下接受才生效;deny 和 ask 不用等,原文是「deny and ask rules apply right away」。(deny 自己的優先序怎麼運作,之前拿 opencode 對照的那篇寫過,這裡不重談。)

那個對話框也不只問一句「你信任這裡嗎」。文件說它會列出這個資料夾要放行的規則和額外目錄,讓你接受之前先看過。session 裡用 /cd 換到沒信任過的資料夾時,提示還會連 hooks 和 helper commands 一起列出來,拒絕的話 session 就留在原地。

讀到這裡,Claude Code 看起來是把 Crush 交給你自律的那件事,做成了一道關卡。故事如果停在這裡很好收尾,可是同一頁還有另一張表。

那張表上寫著 Used

permissions 文件有一段「What runs before you trust a folder」,列了兩種沒有信任對話框的情境:一種是你只信任過它的上層資料夾,另一種是用 claude -p 或 SDK 在從沒信任過的資料夾裡執行。

這兩種情境下,settings 檔裡的 hooks、env 區塊、apiKeyHelper 這類 helper command,表上標的都是 Used,會被使用。標 Not used 的是 permissions.allow 和 additionalDirectories,另外專案 subagent frontmatter 裡的 hooks、@skills-dir plugin、extraKnownMarketplaces 也不用。-p 不跳對話框,只在 stderr 印一行 this workspace has not been trusted。

所以「Claude Code 在你信任之前什麼都不執行」是錯的。互動式 session 會先把你攔下來;-p 和 SDK 不會,專案設定裡那些會執行的東西照樣執行,被擋下的是擴權的 allow 規則、額外目錄,和上面列的那幾樣。

這條規則落的位置有點微妙。-p 和 SDK 都不經過互動畫面,如果你在 CI 裡拿它去跑別人送來的 PR,那份設定正是你最沒讀過的。互動模式有人坐在螢幕前,對話框有人看;沒人看著的時候,偏偏沒有對話框。

這個行為我只讀了官方那張表,沒有在本機用 -p 實際驗證。要在 CI 對外部 PR 跑 Claude Code 的話,permissions 文件另外提到 --bare 啟動時不讀專案的 hooks、skills、custom commands、subagents、plugins 和 .mcp.json,細節在 headless 文件,那頁我還沒打開。

兩條線畫在不同位置

Crush(crushrc) Claude Code(settings JSON)
設定格式 帶專屬指令的 Bash JSON,有公開 schema
設定裡會執行的東西 整份檔案,以你的 shell 權限執行 hooks、apiKeyHelper 等 helper command;env 設定環境變數
專案層 vs. 使用者層 ./.crushrc、./crushrc 優先於 ~/.config/crush/crushrc .claude/settings.local.json、.claude/settings.json 優先於 ~/.claude/settings.json
第一次開資料夾的確認 我讀到的文件沒有描述 互動式 session 跳 workspace trust 對話框
你點頭之前就生效的 整份設定(文件原話:before the UI appears) 文件明寫立即生效的是 deny、ask 規則
-p/SDK、只信任上層資料夾 我沒查到相關文件,也沒實際跑過 hooks、env、apiKeyHelper 會被使用;allow、additionalDirectories 不生效

我的立場:要我選,我選 Claude Code 這種切法。收緊的規則誰寫都害不到你,讓它立刻生效;放寬的規則才需要你點頭。「要不要信任這個資料夾」從一整包的是非題,拆成只在擴權的地方問你。Crush 用 Bash 換來的表達力是真的,$(op read ...) 讓密鑰不落地;Claude Code 這邊要用指令拿 API 金鑰,得透過 apiKeyHelper 這種專門的欄位。Crush 付出的代價是,它的邊界只剩文件裡的一段提醒。

有三種情況會讓我改口。第一,如果 Crush 的原始碼裡有文件沒寫的信任提示,上面關於它的那一欄就得重寫,這點我沒查。第二,如果你只在自己寫的 repo、自己的機器上開 agent,這篇的差異幾乎都可以忽略,設定檔的作者就是你。第三,如果你主要在 CI 裡用 -p 跑,兩邊的距離比互動模式近得多:Claude Code 的對話框在那裡不會出現,hooks 照跑,你一樣得先讀過設定。

還有一件同時裝了兩個 agent 才會碰到的事。Crush 支援 Agent Skills 標準,README 列的搜尋路徑包括 ~/.claude/skills 和專案內的 .claude/skills。陌生 repo 裡替 Claude Code 準備的 skill,Crush 也會撈到。Crush 讀專案 .claude/skills 時受不受任何信任機制約束,文件沒寫,我也沒查到。

設定檔是資料還是程式,是你自己寫設定的時候才需要在意的事。打開別人的資料夾時,該問的是另一題:在我點頭之前,這個 agent 會替這份設定做什麼?Crush 的答案是整份照跑,所以它要你先讀 .crushrc、crushrc、crush.json。Claude Code 的答案在互動模式是先跳對話框,點頭前文件明寫會生效的只有收緊的 deny 和 ask;到了 -p,hooks、env、apiKeyHelper 照用,被擋下的是 allow 規則。兩個答案不同,你該先打開的檔案也就不同,而 .claude/skills 那個資料夾,兩邊都會看。


參考來源