你的 PreToolUse hook 沒擋到 @ 引用的檔案:CLAUDE_CODE_DISABLE_FILE_MENTIONS 與兩種補法
假設你替專案寫了一條 PreToolUse hook,專門擋 Claude 讀 .env。Claude 自己去 Read .env,hook 攔得下來,你測過,很滿意。
然後某天你在對話框裡打了 @.env,隨手想問一句「這個設定怎麼改」。
這個假設的場景我沒有真的跑過,下面是照官方文件推出來的:那一次,你的 hook 一次都不會被叫到。
警衛只看走進門的人
文件在 common-workflows 那一頁寫得很直接:
Your
PreToolUsehooks don’t run when Claude Code includes a file you reference with@, because it makes no tool call.
PreToolUse 的定義是「Before a tool call executes. Can block it」,守的是工具呼叫之前那一道門。@ 的路徑不一樣。你打了 @某個檔案,是 Claude Code 自己把內容放進對話裡。沒有 Read,沒有 tool call,警衛站在大門口,而你剛剛親手把文件從旁邊的窗口遞了進去。
預設會遞進去多少?同一頁寫的是:檔案內容在 Read 工具的 token 上限內就會進對話,預設 25,000 tokens,超過 256KB 的文字檔不放。目錄只給檔案清單,不給內容。@ 的檔案所在目錄和上層目錄的 CLAUDE.md 也會一起加進 context。
1 | { |
這是官方給的第一種補法:加一條 Read deny 規則。文件說,規則比對到的檔案,Claude Code 不會把它放進對話。
第二種補法,和它被放在哪裡
第二種是環境變數。changelog 在 2.1.299(2026 年 10 月 14 日)的 Added 條目寫:
Added
CLAUDE_CODE_DISABLE_FILE_MENTIONSenvironment variable: Claude gets the paths of@file mentions to read with its file tools, not the files’ contents
設成 1,@ 的檔案與目錄就不再展開,Claude 拿到的是路徑,外加一句請它自己用檔案工具去讀的提示。這一來它得走 Read,你的 PreToolUse hook 才會輪到。文件的原話是「even for paths your hooks don’t check」,意思是這個開關不挑路徑,hook 沒列到的檔案也一樣不再被帶進對話。
1 | { |
寫進哪份設定檔,這裡有個容易踩空的地方。文件說可以放在 shell,或是 user settings、managed settings 的 env 區塊。專案層與 local 設定裡的它會被忽略。
照文件的寫法,把它放進專案的 .claude/settings.json,結果是「被忽略」,沒有任何跡象說會報錯。我沒有真的這樣放過,所以不知道畫面上會不會有提示,文件也沒寫。為什麼專案層要被忽略,官方頁面沒有解釋,我不猜。版本下限也在表格那一列:Requires Claude Code v2.1.299 or later。我本機 claude --version 實際輸出 2.1.310 (Claude Code),這是我唯一真的執行過的指令。
該選哪一種
我的立場是這樣:想擋的是某幾個已知路徑,用 Read deny,不要為了這件事把整個 @ 關掉。
理由在代價。關掉變數以後,你每次打 @ 引用的檔案,Claude 都得自己再讀一次。這會多出一輪工具呼叫,我猜是這樣,文件沒有這樣寫,我也沒量過。為了幾個機密檔案,讓所有 @ 都變成這個樣子,不划算。
但 Read deny 有一個我不太放心的字眼。permissions 那一頁的原文是:
Claude makes a best-effort attempt to apply
Readrules to all built-in tools that read files like Grep and Glob, to@filementions in your prompts, and to the selection and open-file context that a connected IDE shares with Claude.
best-effort。文件同一段另外說:貼進 prompt 的圖片路徑與你 @ 的目錄也適用,目錄清單會把規則比對到的名字拿掉,這部分要 v2.1.290 以後的版本才檢查。
我改變想法的條件有兩個。第一,你的 hook 不只是擋,還要留紀錄:誰讀了哪個檔案。Read deny 只負責「不放進去」,我推測 hook 的稽核紀錄不會因此多出 @ 那幾筆([推測],文件沒寫)。而文件列的兩種補法裡,只有變數會讓 hook 輪到。第二,你需要的是「一個也不能漏」的保證。看到 best-effort 這幾個字,我會寧可選變數。它的說法是整個展開動作都停掉,沒有比對這一步可以漏。
這篇沒有講的
文件裡還有幾件事我拿不到,列在這裡免得你以為我知道:
.claudeignore:文件說專案裡的.claudeignore沒有作用,要把裡面的項目搬進Readdeny 規則。- 2.1.299 之前,
@是不是一律展開內容:文件沒寫,我不斷言。 - 2.1.295 到 2.1.298 的 changelog 我沒讀到,不知道這個變數有沒有更早的雛形,所以「2.1.299 起」是以 changelog 條目加環境變數頁的版本要求為據。
- 我也沒看過別的工具(Cursor、Copilot)對同樣的問題怎麼處理。
動手前先查一格
打開你現在的 settings,找有沒有針對 .env 這類路徑的 Read deny。只靠 PreToolUse hook 守的人,@ 這條路照文件的說法是開著的。hook 沒壞,只是那條路它本來就管不到。
接下來可以往兩個方向讀:permissions 那一頁的 Read 與 Edit 規則涵蓋範圍,以及 hooks 那一頁的 PreToolUse。如果你對 @ 的另一頭有興趣,也就是自動完成選單從哪裡拿清單,我在 9 月寫過一篇:它沒報錯,只是換回內建的那一份:拆開 @ 檔案建議的兩道閘門。
參考來源:環境變數、Common workflows、Permissions、Changelog(2026 年 10 月 26 日取得)


















