AI 工程

Claude Code 的 AGENTS.md 是一個內建 plugin:巢狀載入只走 Read 這一條路

一個你很容易踩到、而且不會報錯的坑

你的 monorepo 根目錄有 AGENTS.mdpackages/api/ 底下還有一份專屬的 AGENTS.md,裡面寫著「所有 handler 都要包 withTrace()」。你升級到 Claude Code v2.1.277,啟動時看到對話裡浮出那行 no CLAUDE.md found; AGENTS.md loaded: /home/you/repo/AGENTS.md,很滿意。

接著你在 prompt 裡打 @packages/api/src/server.ts,請 Claude 加一個新的 handler。它寫完了,漂亮、可跑、而且完全沒有 withTrace()

你沒設定錯。這是 v2.1.277 這版 AGENTS.md 支援的實作方式決定的:子目錄的 AGENTS.md 只會透過 Read 工具這一條路附上來。你在 prompt 裡 @ 提及一個檔案不算、IDE 幫你帶進去的開啟檔案/選取不算、Read 工具回傳的 notebook/圖片/PDF 結果也不算。而巢狀 CLAUDE.md 這四種情況全部都會附。

先更正一個在二手摘要裡流傳的講法:有人把這件事寫成「巢狀 AGENTS.md 不再自動載入」。不準確。它自動載入,官方文件寫得很清楚——「As Claude works in subdirectories: a subdirectory's AGENTS.md, when Claude opens a file there with the Read tool」。只是觸發條件比 CLAUDE.md 窄了一圈,而那一圈剛好蓋住最多人用的 @ 提及。

這篇把這個機制拆到你能直接照著設定、照著驗證,並且順便處理另一個同時期進來、但被講得很淺的東西:/diff 窗格跟 checkpoint 到底是不是同一套資料。(劇透:不是,而且這件事會咬人。)

背景:AGENTS.md 支援是一個「內建 plugin」,不是引擎功能

這是整篇最關鍵的一個認知,因為它解釋了後面所有奇怪的行為。

Anthropic 在 v2.1.277(GitHub release 時間 2026-09-18)加入 AGENTS.md 支援。changelog 的原文是:

Added AGENTS.md support: in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead; change it under "Project instructions" in /config (not yet on Bedrock, Vertex or Foundry)

anthropics/claude-code repo 裡還有一個資料夾叫 mods/agents-md/,裡面的 README 把實作講得非常直白:這是一個內建 plugin,plugin ID 是 agents-md@builtin,透過 hook 掛在引擎上。README 第一句就寫:「AGENTS.md read the way Claude Code reads CLAUDE.md, as a plugin, under one option, instructionFiles」。

它掛四個事件:

事件 這個 plugin 做什麼
session.start 不做載入,只是把目前模式記錄下來,而且「never awaited」——session 啟動不會等它
prompt.context 往上走 $.fs.ancestors,把工作目錄及其上層的 AGENTS.md 當成 project 類型的指令檔交還給引擎
tool.call on Read 只走「專案根目錄」與「被讀檔案」之間的目錄,附上那些目錄的 AGENTS.md
agent.spawn on fork: true 把父迴圈已送出的巢狀檔複製給 fork,避免重複附上

為什麼要在意「它是 plugin」?因為 plugin 能拿到的事件就只有這些。README 有一節標題直接叫 "Where it still differs from CLAUDE.md",開頭寫「Each names a loader fact a plugin cannot reach through the events it has today.」——不是忘記做,是今天的事件介面搆不到

這也解釋了為什麼一堆「關掉某某東西」會讓 AGENTS.md 整個消失。它是 plugin,而 plugin 吃 hook。

AGENTS.md 兩條載入路徑的決策流程圖

運作原理:兩條路徑、四個模式

路徑一:啟動時(prompt.context

預設模式 claude-md-or-agents-md 的判斷規則是「這個專案有沒有自己的指令檔」。官方文件列得很精確:

  • 算數(有它就只讀 CLAUDE.md):工作目錄或其任一上層目錄裡的 CLAUDE.md.claude/CLAUDE.mdCLAUDE.local.md
  • 不算數(可以跟 AGENTS.md 併存):你的 ~/.claude/CLAUDE.md、組織的 managed CLAUDE.md.claude/rules/ 底下的檔案

三個都沒有,引擎才會把工作目錄及上層每一個 AGENTS.md.claude/AGENTS.md 都載進來。

這裡有一個非常容易中的陷阱,文件自己用 Note 標出來:CLAUDE.local.md 算數。所以你為了放個人筆記、在一個純 AGENTS.md 的專案裡加了一份 gitignore 掉的 CLAUDE.local.md——恭喜,你單方面把整個專案的 AGENTS.md 關掉了,而且只有你自己中,隊友完全看不出來。解法是把模式改成 claude-md-and-agents-md

另外,AGENTS.local.mdAGENTS.override.md.agents/ 底下的任何東西,都不讀。別自己發明。

路徑二:工作中(tool.call on Read

Claude 用 Read 工具打開 packages/api/src/server.ts 時,plugin 會走「專案根目錄到這個檔案之間、嚴格中間的那些目錄」(README 寫 $.fs.ancestorsbelow: root),把它們的 AGENTS.md 附上去,條件是:

  1. 這個 agent 迴圈還沒拿過它
  2. 它不在目前 context 的指令檔裡(比對路徑,project 類型再比內容)
  3. 同一個目錄沒有 CLAUDE.md 把它蓋掉(或被某個 CLAUDE.md @ 進來)

附上的格式跟引擎附巢狀 CLAUDE.md 一模一樣:Contents of <path>:,逐位元組相同。每個檔案每個迴圈、每段對話只附一次。

四個模式怎麼選

讀什麼
claude-md-or-agents-md 預設。有自己的 CLAUDE.mdCLAUDE.local.md 就只讀它;沒有才讀 AGENTS.md
claude-md-and-agents-md 兩邊都讀,每個目錄 CLAUDE.md 在前、AGENTS.md 在後;已經被 import 或 symlink 進來的不會讀第二次
claude-md 只讀 CLAUDE.md,等於把這個 plugin 關成無作用
managed-only 啟動時只留組織的 managed CLAUDE.md 和 auto memory;專案/個人/.claude/rules/ 和所有 AGENTS.md 都丟掉

設定方式有兩個。互動式:session 裡打 /config,找 Project instructions 那一列。或者寫進設定檔:

{
  "pluginConfigs": {
    "agents-md@builtin": {
      "options": { "instructionFiles": "claude-md-and-agents-md" }
    }
  }
}

這段要放對地方。 文件明講:只讀 ~/.claude/settings.json--settings 指定的檔案、或 managed settings。專案的 .claude/settings.json 不會被讀——plugin 選項一律不吃專案層設定。你把它 commit 進 repo 想讓全隊生效,不會有任何效果,也不會報錯。

如果你是從更早的 beta 一路用過來的,舊 key 叫 projectInstructions,值是 claude / agents-fallback / both / none。README 說舊 key 目前還會被認(對應到 claude-md / claude-md-or-agents-md / claude-md-and-agents-md / managed-only),但一旦你把 instructionFiles 設成非預設值,舊 key 就不讀了,transcript 會叫你把它刪掉。

巢狀載入的八個差異:照著檢查一遍

mods/agents-md/README.md 的 "Where it still differs from CLAUDE.md" 列了八條。挑會真的咬到你的講:

1. 巢狀檔只在「文字 Read」時附上。 引擎附巢狀 CLAUDE.md 的時機還包括:prompt 裡 @ 提及的檔案、IDE 開啟的檔案或選取、Read 工具的 notebook/圖片/PDF 結果。這四種都不會帶 AGENTS.md。這就是開頭那個坑。

實務上的招式:別只靠 @ 提及。如果你要 Claude 遵守某個子目錄的規則,在 prompt 裡直接說「先讀 packages/api/src/server.ts」,讓它走 Read 工具。或者更省事——把那個子目錄的規則升級成 packages/api/CLAUDE.md

2. 附上的巢狀檔不會登記進「已讀檔案」狀態。 後果有兩個:compaction 之後引擎不會把它復原到「最近讀過的檔案」裡(要等下一次 Read 那個目錄才會再附);而且你 session 中途改了那份 AGENTS.md,不會有重新宣告。

3. /cd 之後時機不同。 /cd 會用自己的通知把新樹的 CLAUDE.md 帶進來;plugin 的檔案是在同一個下一次 request、走引擎的 instructions 宣告進來。

4. 路徑比對是「照字面拼法」。 引擎會先解析工作目錄的 symlink 別名再判斷檔案在不在裡面,plugin 不會。用 symlink 拼出來的 workspace 要小心。

5. --add-dir 加進來的目錄不貢獻 AGENTS.md 引擎在 CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD 開著時會載那些目錄的 CLAUDE.mdAGENTS.md 不會。

6. /memory# 快捷鍵不認識 AGENTS.md 這也表示 /contextMemory files 清單裡看不到它——除非它是被某個 CLAUDE.md @ import 進來的。

7. 外部 @ import 的核准行為不一樣。 CLAUDE.md 裡 import 工作目錄外的檔案,Claude Code 會跳出核准對話框;AGENTS.md 裡的同樣 import 不會跳,而是只在你「先前已經為這個專案核准過外部 import」時才載入,否則直接略過。

8. 非 fork 的 subagent 會重拿一次。 一個不是 fork 的 subagent 在自己第一次 Read 那個目錄時還是會拿到巢狀 AGENTS.md,即使父迴圈已經拿過了。引擎對巢狀 CLAUDE.md 不會這樣重發。fork 則兩邊行為一致。

還有一條在官方文件而不在 README:InstructionsLoaded hook 不會 為直接讀取的 AGENTS.md 觸發(被 CLAUDE.md import 或 symlink 的那種照常觸發)。你如果寫了 hook 在稽核「這個 session 載了哪些指令檔」,它會漏掉 AGENTS.md

什麼情況下整個功能不存在

這一段值得單獨記,因為失敗是靜默的——/config 裡連 Project instructions 那一列都不會出現。官方列了四種:

  • 版本早於 v2.1.277
  • session 沒有跟 Anthropic 拿 feature flag。文件舉的例子是 Amazon Bedrock 或其他第三方 provider,或是你關掉了 telemetry。changelog 括號裡那句 "not yet on Bedrock, Vertex or Foundry" 講的就是這件事
  • 安裝或升級後的第一個 session。要從下一個 session 才讀得到
  • 你或組織設了 disableAllHooksallowManagedHooksOnly,或你在 /plugin 裡把內建的 agents-md 關掉了

第二點值得停一下:為了隱私或公司政策關掉 telemetry 的團隊,會連帶失去 AGENTS.md 直讀。這是個真實的 trade-off,不是 bug,但也沒有人會在設定 telemetry 時想到它。

這些情況下的標準解法只有一個:在 AGENTS.md 旁邊放一份 CLAUDE.md,內容就一行:

@AGENTS.md

文件說這個 import 留著永遠不會害 Claude 讀兩次,不管你的 Project instructions 設成哪個值。所以如果你的團隊裡有人在 Bedrock 上、有人在 API 上,就把這個 import 留著,這是最穩的最小公倍數。

順帶把幾個舊 workaround 的處置一次講完(文件的 "Remove an earlier AGENTS.md workaround"):

  • CLAUDE.md 裡寫 @AGENTS.md → 可以留,也可以在它沒別的內容時刪掉
  • CLAUDE.md 裡用文字叫 Claude「去讀 AGENTS.md」→ 刪掉或換成 @AGENTS.md。這種寫法要 Claude 自己決定去開檔案才有用,最不可靠
  • CLAUDE.md symlink 到 AGENTS.md → 留著或刪掉都行,內容只會讀一次。但注意 Edit/Write 工具拒絕寫穿 symlink,而且 Windows 上 git 可能把它 checkout 成一行純文字
  • SessionStart hook 印出 AGENTS.md 內容 → 一定要移除,否則 context 裡會有兩份

一份可以照跑的驗證流程

設完之後怎麼確認它真的生效?因為 AGENTS.md 不出現在 /memory/context,你得換方法:

# 1. 確認版本
claude --version          # 要 >= 2.1.277

# 2. 確認專案路徑上沒有會蓋掉 AGENTS.md 的檔案
#    (從工作目錄往上找到 repo root)
find . -maxdepth 3 \( -name 'CLAUDE.md' -o -name 'CLAUDE.local.md' \) -not -path '*/node_modules/*'
ls .claude/CLAUDE.md 2>/dev/null

# 3. 臨時試一個模式,不動到你的常駐設定
echo '{"pluginConfigs":{"agents-md@builtin":{"options":{"instructionFiles":"claude-md-and-agents-md"}}}}' > /tmp/agents-md.json
claude --settings /tmp/agents-md.json

進 session 之後:

  1. 看對話裡有沒有 no CLAUDE.md found; AGENTS.md loaded: ... 那一行
  2. /config,確認 Project instructions 這一列存在(不存在=你的 session 拿不到 feature flag)
  3. 直接問它:「你目前的 project instructions 裡,關於 handler 的規則是什麼?」——這是文件自己建議的驗證法
  4. 測巢狀:請它「Read packages/api/src/server.ts」,然後再問一次同樣的問題,看答案有沒有變

第 4 步的前後對比,就是你檢驗「巢狀載入有沒有發生」最直接的方式。

換個題目:/diff 窗格和 checkpoint 不是同一套資料

同一個月進來的另一個東西是 /diff 窗格(v2.1.260,2026-09-03 release)。changelog:

Added a diff panel that opens beside the conversation in fullscreen mode and shows your uncommitted changes as Claude edits; toggle it with /diff

很多人把它跟 checkpoint//rewind 當成同一件事的兩個 UI。它們的資料來源不同,而這個差異會在你最需要它的時候咬你。

diff 窗格與 checkpoint 的資料來源對照

/diff 窗格讀的是 git

窗格列出變更檔案與增刪行數,每次 Claude 編輯檔案或跑 shell 指令之後都會刷新。開啟條件是:fullscreen rendering、在 git repo 裡、終端機至少 110 欄、v2.1.260 以上。終端機到 144 欄以上它會自己開;你手動 /diff 開過一次之後,往後的 session 只要寬度夠、Claude 一開始編輯檔案它就會自己開。關掉之後它會一直記得關著,直到你再打一次 /diff

窗格開著的時候,這幾招很實用:

  • 用滑鼠選取幾行,Claude Code 會把選取內容掛到你的下一個 prompt,輸入框會顯示行數。想送出但不要帶選取,把游標移到行數標記後面按 Backspace 刪掉它(需要 v2.1.271 以上)
  • Ctrl+X B 切換比較基準:這個 session 的變更 → 你所有未 commit 的變更 → 從分支切出來之後的全部變更。Claude Code 會記住你每個專案的選擇
  • 清單預設跳過測試檔和生成檔,並把 session 之前的變更收成底部一行,點一下展開

舊的 classic renderer 是另一個東西(diff viewer),它的 Current 視圖來自 git,另外每個「有編輯檔案的 turn」有一個 turn 視圖——turn 視圖是從 Claude 的檔案編輯紀錄建的,不是從 git。所以 Claude 用 shell 指令改的東西只會出現在 Current。這句話幾乎就是 checkpoint 限制的鏡像。

checkpoint 記的是 Claude 的編輯工具

官方 checkpointing 文件講得很清楚:

  • 每個開啟一個 turn 的 prompt 會產生一個 checkpoint
  • 一個 session 保留最近 100 個 checkpoint 的檔案快照
  • 快照跟對話一起存,resume 之後還能 /rewind
  • 預設在 session 最後一次存快照之後約 30 天被清掉,想留久一點調 cleanupPeriodDays

/rewind(或空輸入框連按兩次 Esc)打開之後可以選:還原程式碼與對話、只還原對話、只還原程式碼、從這裡往後摘要、從這裡往前摘要。注意兩個還原程式碼的選項只有在那個 checkpoint 真的有記到檔案變更時才會出現

然後是四個限制,每一個都是真實的破口:

  1. Bash 改的檔案不記錄。 rmmvcp 造成的變動 rewind 救不回來。只有 Claude 的檔案編輯工具算數
  2. subagent 的編輯通常不還原。 唯一例外是前景執行的 forked skillcontext: forkbackground: false)——它在你自己的 turn 裡動工作樹,所以會被還原。背景 fork skill、背景 /code-review --fix 都不算,只能用 git 救
  3. symlink 和 hard link 路徑會被跳過,並顯示 Restored the code, but skipped N files。dotfile manager symlink 進專案的 config、pnpm hard link 的檔案都中。想知道跳了哪些,還原前先 /debug,debug log 在 ~/.claude/debug/<session-id>.txt
  4. turn 進行中插隊的訊息沒有 checkpoint。 你在 Claude 工作時 queue 的訊息如果併進了正在跑的 turn,rewind 選單裡不會有它。要撤銷只能 rewind 到啟動那個 turn 的 prompt,連 Claude 在你訊息到達前做的工作一起倒掉

把兩邊疊起來,結論很簡單:/diff 窗格看得到的東西,/rewind 不一定救得回來。 Claude 跑了 mv src/a.ts src/b.ts,窗格會如實顯示,rewind 完全無感。這就是為什麼官方文件最後一句寫 "Not a replacement for version control"。

順便一提,checkpoint 也不是免費的。v2.1.208 那次改動的 changelog 寫:「Reduced session transcript size (up to 79x in edit-heavy sessions) and bounded checkpoint disk usage by pruning superseded file-history backups」——79 倍是官方回報的數字,前提是「edit-heavy sessions」,不要當成一般情況的期望值。

該怎麼用:給工程團隊的具體建議

如果你的 repo 只有 AGENTS.md、要餵多種 coding agent:
保持預設 claude-md-or-agents-md,然後在根目錄留一份只有 @AGENTS.md 一行的 CLAUDE.md。這一行幫你蓋掉 Bedrock/Vertex/Foundry/關 telemetry/升級後第一個 session 這五種靜默失效,而且不會造成重複載入。成本是一個檔案。

如果你的 repo 兩種都有、而且內容不重複:
claude-md-and-agents-md。但記得它只能寫在 ~/.claude/settings.json--settings 或 managed settings——要全隊生效,得走 managed settings 或在啟動腳本裡帶 --settings

如果你是 monorepo,靠子目錄規則吃飯:
認真考慮把子目錄的規則寫成 CLAUDE.md 而不是 AGENTS.md。上面八條差異有一半以上只影響巢狀路徑,@ 提及不觸發這條尤其致命。或者改用 .claude/rules/ 搭配 paths: frontmatter——那是引擎原生的路徑範圍機制,不經過這個 plugin。

如果你在寫稽核或合規工具:
InstructionsLoaded hook 看不到直讀的 AGENTS.md/context 的 Memory files 也看不到。要完整清單,目前得靠 agents_md_load 這類 telemetry 事件(README 說它只在 telemetry plugin 在場時存在),或者乾脆強制全隊用 @AGENTS.md import 的寫法,讓所有東西回到 CLAUDE.md 的路徑上。

工作流上:
/diff 窗格當檢視工具,把 git 當還原工具,/rewind 只當「剛剛那輪不對,倒回去」的快速鍵。任何你打算之後可能要回頭的節點,先 commit。checkpoint 的 100 個上限、30 天保留、Bash 不記錄、subagent 不還原,四條加起來足以讓你在最不想出事的時候出事。

誠實標註不確定的地方

  • mods/agents-md/README.md 描述的是 repo main 分支當下的狀態。README 自己用了 "not an event yet"、"does not raise agent.spawn yet" 這種措辭,代表這些差異是暫時的,事件介面補上之後行為會改。寫死在你的 runbook 裡之前先看一眼那份 README
  • 「巢狀 AGENTS.md 只在文字 Read 時附上」這條,我是從 README 的 "Where it still differs from CLAUDE.md" 第 1 點讀出來的,官方 memory 文件只正面陳述了 Read 這條路徑、沒有反面列舉 @ 提及不觸發。兩份文件不衝突,但嚴謹程度不同
  • 本文沒有實際跑過 v2.1.277 逐條驗證,所有機制描述都來自 changelog、mods/agents-md/README.md 和官方文件。上面那份驗證流程是照文件寫的,值得你自己在自己的 repo 上跑一遍——特別是第 4 步的前後對比

來源

整理:DataAgent · Coding Agent 實戰教學

發表迴響

%d 位部落客按了讚: