在 Manual 模式下,平常一小時的工作大概是這樣:代理讀取專案、提出修改、請您執行測試,接著又修改、又測試。到了第二十次提問,您幾乎沒看就按了 "Yes"。而在這些提問之間,混進了一次對 legacy/ 資料夾的修改 - 那裡早就沒人碰,也不該再碰了。
這篇文章要談的是,如何只在真正需要您決定時才讓代理提問。Claude Code 提供了權限規則:設定檔中的三份清單,分別列出代理可立即執行、必須先詢問,以及完全禁止的操作。接下來會說明這些規則的用途、讀法,以及如何整理您很可能已經有的規則檔。
一種模式管全部
權限模式就像控制整個工作階段的一個旋鈕。您可以往任一方向轉,但只能整個一起調整。
在限制較嚴格的 Manual 模式中,代理每次修改前、幾乎每次執行指令前都會詢問。只要您有仔細看,提問就能提供保護。問題一多,人就不再逐一閱讀,保護也就形同虛設。
在較自由的 acceptEdits 模式中,修改會直接通過,像 rm 和 mv 這類檔案指令也一樣。處理您自己的程式碼時很方便,但這個模式不會區分資料夾:代理修改 legacy/ 時,也會和修改專案其他位置一樣,不發出任何提示。
您可以在 CLAUDE.md 寫上 "不要碰 legacy/"。代理會盡量遵守,但這只是對模型的要求,不是禁令。操作執行前,Claude Code 會自行檢查權限規則;CLAUDE.md 或提示中的文字不會改變這些規則。
問題在於,不同操作出錯的代價不同,權限模式卻一律用同一種方式處理。測試可以直接執行,不必每次都問;修改別人的模組時,您會想先看過;代理則完全不該進入已封存的資料夾。三種操作需要三種不同的處理方式,單靠模式做不到。
三份清單
權限規則正好能提供這三種處理方式:
allow- 免詢問直接執行。減少雜訊:常用又安全的操作不再打斷您;ask- 一律先詢問。需要由您決定的操作會保留確認步驟,而且不受模式影響,即使在最自由的模式中也一樣;deny- 禁止執行。代理會收到拒絕,您甚至不會看到確認提示。同樣不受模式影響。
規則會寫入設定檔,例如專案中的 .claude/settings.json。前面提到的專案可以這樣設定:
{
"permissions": {
"allow": [
"Bash(npm test)"
],
"ask": [
"Edit(src/partner/**)"
],
"deny": [
"Edit(legacy/**)",
"Bash(git push *)"
]
}
}這裡的 src/partner/ 是您會謹慎修改的他人模組,legacy/ 是已封存的專案部分,而 npm test 則是代理每項任務都會執行好幾次的檢查。兩個星號代表"資料夾內任意深度的所有內容"。
接著看看工作階段會有什麼變化。比較三種設定下的相同五項操作:
| 操作 | Manual,無規則 | acceptEdits,無規則 | acceptEdits,有規則 |
|---|---|---|---|
修改 src/ 的其他部分 | 詢問 | 免詢問 | 免詢問 |
修改 src/partner/ | 詢問 | 免詢問 | 詢問 |
修改 legacy/ | 詢問 | 免詢問 | 拒絕 |
npm test | 詢問 | 詢問 | 免詢問 |
git push | 詢問 | 詢問 | 拒絕 |
請看最後一欄。工作階段可以快速進行:修改自己的程式碼不必停下來,測試也會自動執行。只剩下一個提問,而且正好是您確實想先檢視修改的地方。至於兩種不允許的操作,現在也不會因為您到了晚上比較疲倦就偷偷通過。
這就是權限規則的用途。重點不是增加代理能做的事,而是依照出錯代價安排不同操作。這樣您就能使用較自由的模式,同時保有對重要部分的掌控。
同一項操作可能同時符合多條規則,因此 Claude Code 會依照固定順序檢查清單:
請由上往下讀這張圖,每項代理操作都會重新走過一次檢查流程。一旦符合某條規則,檢查就會結束,更具體的規則也不會因此取得優先權。如果 deny 中有 Bash(git push *),那麼 allow 中的任何規則都無法放行 push。禁止操作不會被意外覆蓋,這樣才對。
最下面的方框值得特別記住。操作不符合任何清單,不代表 "允許"。最後仍由模式決定:在 Manual 中代理會詢問,在 acceptEdits 中則會直接修改。也就是說,規則不會取消模式,只是在您指定的地方調整模式的做法。
如何讀懂規則
規則寫得似是而非,比沒有規則更糟。沒有規則時,您知道這裡沒有界線;有了不精確的規則,您以為界線已經設好,實際上它限制的卻不是您想的內容。
所有規則格式都一樣:工具名稱,後面用括號指定範圍。不加括號時,規則會涵蓋該工具的所有呼叫,因此在 deny 中加入 Bash,就會完全禁止代理使用終端機。
檔案
管理檔案時會用到兩種工具:Edit 負責所有修改,Read 負責讀取。路徑可以使用類似 .gitignore 的樣式:一個星號代表同一層資料夾中的任意名稱,兩個星號則涵蓋任意深度。開頭沒有斜線的路徑會以工作階段啟動位置為基準,所以請從專案根目錄啟動 Claude Code。
常見的錯誤是寫成 Write(legacy/**)。代理確實有名為 Write 的工具,但 Claude Code 只會用 Edit 和 Read 比對檔案規則。它會接受含有 Write 的規則,並在啟動時發出警告,之後就不再檢查這條規則。Edit 則涵蓋所有內建的檔案修改方式,包括建立新檔案。
還有一個方便之處:在 deny 中禁止讀取,例如加入 Read(./.env),也會一併禁止代理修改這個檔案。
指令
Bash(...) 規則會比對指令文字。星號可以代表任意文字,包括空格;以下三個細節會決定規則是否符合:
- 沒有星號時,規則必須與單一指令完全相同,逐字比對;
- 星號通常放在子指令後面。
Bash(git log *)只會放行帶有任意旗標的git log,而Bash(git *)則會放行任何 git 指令,包括 push; - 星號前的空格也是規則的一部分:
Bash(ls *)不會符合lsof,但Bash(ls*)會。
藍色部分會與指令逐字比對;橘色部分則由星號完整涵蓋,無論後面寫了什麼。因此,規則的範圍取決於星號前的文字。第一條是 git log,只會放行查看記錄的指令。第二條只有 git,星號後面可能接著 push、刪除分支或還原變更。閱讀別人的規則時,先問自己:星號前面寫了什麼?
| 規則 | 符合 | 不符合 |
|---|---|---|
Bash(npm test) | npm test | npm test --watch |
Bash(npm run *) | npm run build, npm run lint --fix | npm install |
Bash(git log *) | git log, git log --oneline | git push |
Bash(git *) | 任何 git 指令,包括 git push | - |
Edit(src/partner/**) | 修改 src/partner/ 中的任意檔案 | 修改 src/app.js |
Read(./.env) | 讀取專案根目錄中的 .env | 讀取 .env.example |
Claude Code 會把 a && b 這類複合指令拆開處理。allow 只有在每個部分都符合各自的授權時才會放行;ask 和 deny 則只要有一個部分符合就會生效。因此,授權 npm test 不會順便放行 npm test && git push。
其他工具也能設定規則,例如 mcp__github__* 可涵蓋同一個 MCP server 的所有工具,WebFetch(domain:example.com) 則適用於從特定網站載入頁面。它們的運作邏輯相同,這裡就不另外說明了。
規則應放在哪個檔案
把規則放錯檔案,問題會悄悄發生。您把禁止項目放進個人檔案,同事那邊就沒有這條規則,他們的代理仍會照常進入 legacy/。若把只適合自己的授權放進共用檔案,所有下載專案的人都會一併拿到。
| 檔案 | 規則適用對象 | 是否納入 Git |
|---|---|---|
~/.claude/settings.json | 您自己的設定,適用於這台電腦上的所有專案 | 否,檔案位於家目錄 |
.claude/settings.json | 專案所有協作者共用 | 是,會提交到 Git |
.claude/settings.local.json | 您自己的設定,只適用於這個專案 | 否 |
除了這三種檔案,組織也可能提供自己的設定檔,其中的規則無法覆蓋。不過,練習專案通常不會有這種設定。
不同檔案中的清單不會互相衝突,而是合併生效:您個人檔案中的 allow 會加到共用檔案的 allow 中。檢查順序則對所有檔案都相同,任何檔案中的 deny 都比其他檔案中的 allow 優先。檔案分工也就很清楚了:像 legacy/、.env 或 push 這些專案界線,放在共用檔案,才能隨儲存庫一起提供給大家;個人工作習慣則放在本機檔案。
共用檔案有一項保護機制:其中的 allow 只有在您確認信任該資料夾後才會生效。第一次在專案中啟動 Claude Code 時,它會詢問您,並說明該檔案準備授予哪些權限。ask 和 deny 則會立即生效,因為它們只會限制操作。您尚未同意前,其他人的儲存庫無法藉由共用檔案取得整個終端機的使用權。
您可以用兩個指令查看實際生效的設定。/permissions 會顯示所有規則及其來源檔案,也能在這裡新增或刪除規則。/status 的 Setting sources 一列則會列出已載入的檔案。Claude Code 會自動讀取檔案變更,不必重新啟動。設定檔必須符合嚴格的 JSON 格式:使用 // 加註解,或在最後一個項目後多加逗號,都會讓檔案失效;Claude Code 啟動時會提醒您。
代理詢問是否執行指令時,如果您選擇 "Yes, and don't ask again",Claude Code 就會將 allow 規則寫入 .claude/settings.local.json。若檔案尚不存在,它會建立檔案,並確保不會將它提交到 Git。檔案修改則不同:這類同意只在目前工作階段有效,不會寫入檔案。
拆解實際的 settings.local.json
您很可能已經有這個檔案,但裡面的規則不是您親手寫的。每次選擇 "不要再詢問",都會多加一條規則;幾週後,它可能授權代理做出比您想像中更多的事。以下是某個 Python 專案在幾週工作中實際累積的設定區塊:
{
"permissions": {
"allow": [
"Bash(python3 *)",
"Bash(PYTHONIOENCODING=utf-8 python3 *)",
"Bash(git status *)",
"Bash(git log *)",
"Bash(git diff *)",
"Bash(git show *)",
"Bash(git branch *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git fetch *)"
]
}
}先別急著看解析,試著自己判斷:哪些項目會保留、哪些要縮小範圍,又有哪些應該刪除?
Bash(python3 *)
這是檔案中範圍最廣的一項。它允許執行任何腳本,也允許透過 python3 -c 執行任意程式碼。Python 幾乎什麼都能做:刪除檔案、連上網路、啟動其他程式。說穿了,這條規則等於允許代理免詢問執行任何操作。建議將範圍縮小到當初需要執行的腳本,例如 Bash(python3 scripts/build.py *)。
Bash(PYTHONIOENCODING=utf-8 python3 *)
這是同一個指令,只是在前面加了環境變數。allow 只有在變數屬於一小組預先認定安全的名稱時,才會放行這種寫法。如果變數不在清單中,Bash(python3 *) 就不會涵蓋這條指令,代理會另外詢問,而按鈕則會再儲存一條規則。這一項很可能就是這樣累積出來的。縮小範圍的方法相同,限定到特定腳本即可。
git status, git log, git diff, git show
一般情況下,這些指令只會讀取資料,而 Claude Code 不需要任何規則就能直接執行讀取操作。留下這些項目沒有危險,但幾乎沒有幫助。把它們刪掉,設定檔會更精簡,也更容易看懂。
Bash(git branch *)
看起來無害,只是列出分支清單。但規則只比對文字,而 git branch -D feature 也是以 git branch 開頭。這樣一來,刪除分支也會免詢問通過。建議移除這一項。如果列出分支時的提問開始造成困擾,再精確授權 Bash(git branch),不要加星號。
git add 和 git fetch
第一個指令會把變更加入待提交內容,第二個會從伺服器下載最新記錄,但不會碰工作檔案。兩者都很容易還原,可以保留。
Bash(git commit *)
代理會在您檢視 diff 之前直接建立 commit。這正是需要先詢問的情況:commit 代表您同意這項變更。建議將這條規則移到 ask。單純刪除還不夠。在 Manual 模式下,代理本來就會詢問;但 ask 能在任何模式中保留確認步驟,包括最自由的模式。
檔案中沒有的項目
git push。這很好,但目前只是因為還沒有人在 push 的提問中按過 "不要再詢問"。明確寫下禁止規則會更可靠。
整理完之後,結果如下:
{
"permissions": {
"allow": [
"Bash(python3 scripts/build.py *)",
"Bash(PYTHONIOENCODING=utf-8 python3 scripts/build.py *)",
"Bash(git add *)",
"Bash(git fetch *)"
],
"ask": [
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)"
]
}
}
十條規則最後只剩六條,而且每一條都有明確用途。工作中幾乎沒有增加提問:建置會自行執行,代理只會在 commit 前詢問。若 push 禁令適用於整個團隊,建議依照上一節的原則,將它移到共用的 .claude/settings.json。
Bash(python3 *)、Bash(node *)、Bash(npm *) 或 Bash(npx *) 這類規則,看起來像是授權使用某個工具。實際上,它們允許執行該工具能啟動的任何程式碼。請只授權特定腳本或指令,不要授權整個程式。
規則無法攔截的情況
代理收到拒絕後,會把它當成一般的工具回覆,然後繼續工作。有時它會嘗試用其他方式完成相同操作,所以您得先了解規則究竟檢查什麼。
Bash(...) 規則比對的是指令文字,而不是將要啟動的程式。因此,禁止規則能攔截一般寫法,但無法涵蓋所有變體:
deny中的Bash(git push *)無法攔截git -C . push。指令相同,寫法不同;Bash(rm *)無法攔截/bin/rm或bash -c 'rm ...';- 禁止使用
Read(...),也無法阻止 Python 腳本自行開啟檔案。
不必因此放棄使用規則。代理幾乎總是以一般形式輸入指令,而 deny 正好能攔截這種寫法。不過,規則不是萬能屏障(Claude Code 的文件也明確說明了這一點)。為了不讓代理另找方法繞過限制,請在 CLAUDE.md 用文字說明:"legacy/ 已封存;如果任務需要修改其中內容,請停下來詢問。"規則負責攔截,CLAUDE.md 負責解釋,兩者搭配比單獨使用更有效。
如果必須確實守住界線,還有其他機制可用:hook 會在每個指令啟動前進行檢查,sandbox 則會從系統層級限制存取。這些是另外的主題。
日常使用規則
簡單整理成幾個日常習慣:
- 在
deny中列出這個專案無論如何都不允許的操作:已封存的資料夾、.env、push; - 在
ask中只列出需要由您決定的邊界情況。什麼都設成ask,又會回到提問太多、讓人疲憊的狀況; - 在
allow中只設定範圍明確、會重複執行的項目:特定檢查、特定腳本; - 專案界線放在共用的
.claude/settings.json,個人化的便利設定則放在.claude/settings.local.json; - 偶爾打開
/permissions,清除按下 "不要再詢問" 後累積的規則; - 新增規則後要實際測試:請代理執行一項已禁止的操作,確認它確實遭到拒絕。
用文字告訴代理專案中各區域的規則:哪些可以直接修改,哪些需要先詢問,哪些完全不能碰。請它建議一段 permissions 設定,並在儲存前檢查 JSON,問問自己前面提過的問題:規則範圍是否過大?它會被寫進正確的檔案嗎?
模式決定整體工作節奏,規則則決定哪些地方要放慢。兩者搭配,才能做到單靠模式無法兼顧的事:出錯代價低的地方快速執行,代價高的地方先詢問或直接拒絕。
GO TO FULL VERSION