在 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 清單:符合就拒絕。若不符合,再比對 ask 清單:符合就在任何模式下詢問。若仍不符合,再比對 allow 清單:符合就免詢問直接執行。若都不符合,則由模式決定:Manual 會詢問,acceptEdits 則會直接修改。代理操作指令、修改、讀取符合 deny?符合 ask?符合 allow?拒絕不會執行操作詢問任何模式皆適用免詢問立即執行都不符合時,由模式決定Manual 會詢問,acceptEdits 則直接修改是是是否否否
從上往下檢查,第一個符合的規則生效

請由上往下讀這張圖,每項代理操作都會重新走過一次檢查流程。一旦符合某條規則,檢查就會結束,更具體的規則也不會因此取得優先權。如果 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*) 會。
指令規則如何比對文字。規則 Bash(git log *):git log 部分會逐字比對,星號涵蓋 --oneline main,因此只放行帶有任意旗標的 git log。規則 Bash(git *):只有 git 會逐字比對,星號涵蓋 push origin main,因此任何 git 指令都會通過,包括 push。Bash(git log *)Bash(git *)僅限 git log,可帶任意旗標任何 git 指令,包括 pushgit log--oneline maingitpush origin main星號之前:逐字比對星號代表:任意文字
星號前的文字決定規則範圍

藍色部分會與指令逐字比對;橘色部分則由星號完整涵蓋,無論後面寫了什麼。因此,規則的範圍取決於星號前的文字。第一條是 git log,只會放行查看記錄的指令。第二條只有 git,星號後面可能接著 push、刪除分支或還原變更。閱讀別人的規則時,先問自己:星號前面寫了什麼?

規則符合不符合
Bash(npm test)npm testnpm test --watch
Bash(npm run *)npm run build, npm run lint --fixnpm install
Bash(git log *)git log, git log --onelinegit 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,問問自己前面提過的問題:規則範圍是否過大?它會被寫進正確的檔案嗎?

模式決定整體工作節奏,規則則決定哪些地方要放慢。兩者搭配,才能做到單靠模式無法兼顧的事:出錯代價低的地方快速執行,代價高的地方先詢問或直接拒絕。