Claude Code mods は、2026年9月に公式リポジトリへ姿を現した新しい拡張層だ。settings.json の hooks がシェルコマンドでイベントの前後に反応するのに対し、mod は TypeScript の関数でエンジンのイベントそのものを包む。そして 2.1.277(2026-09-18)で入った「AGENTS.md を標準で読む」機能は、エンジン本体の変更ではなく agents-md という同梱 mod として実装された。本記事はその公開ソース(mods/agents-md・75ファイル)を読み、4つの instructionFiles モードを Linux の Claude Code 2.1.277 で陽性対照つきに実測し、CLAUDE.md とまだ違う点を整理する。
- ・正体:振る舞いが
register(on, options)1本の hooks モジュールに収まる Claude Code プラグイン。同梱4本(sec-default・diff・telemetry・agents-md)のソースがmods/に公開されている。 - ・何ができる:
prompt.contextやtool.callといったエンジンのイベントを($, e, next)で包み、指示ファイルの一覧やツール結果を書き換えてから次へ渡す。 - ・agents-md mod:作業ディレクトリから上に CLAUDE.md が1つも無いとき、AGENTS.md を CLAUDE.md と同じ場所・同じ枠組みで渡す。既定モードが
claude-md-or-agents-md。 - ・実測できたこと:4モードすべてと、ネストした AGENTS.md の Read 時添付、プロジェクトの
.claude/settings.jsonが無視されること。claude plugin testは 2.1.277 の CLI に存在しなかった。 - ・注意:設定経由で読んだ AGENTS.md は
/memoryと/contextに出ない。Bedrock・Vertex・Foundry では未対応。
Claude Code そのものの導入から CLAUDE.md・Hooks・本番運用まではClaude Code|2026年版・インストールからCLAUDE.md・Hooks・本番運用までの実装手引きにまとめてある。mod はその Hooks の「一段下」に位置する仕組みなので、Hooks の節を押さえてから読むと差分が掴みやすい。
Claude Code modsとは——hooksの次に来た「エンジンのイベントを包む」層
公式リポジトリの mods/README.md は、mod を次のように定義している。「振る舞いが hooks モジュールに収まる Claude Code プラグインで、register(on, options) という1つの入口がエンジンのイベントを関数 ($, e, next) としてフックする」。4本が Claude Code 本体に同梱され、mods/ はそのソースを「バイナリに組み込まれた姿のまま公開」したものだ。
| mod | 何をするか | 位置 |
|---|---|---|
sec-default |
組織の従来型 hooks・プロンプト内容・管理設定・ツールポリシーを、利用者が入れたプラグインの手の届かない所に置く。自身はポリシーを足さない | 最外周(管理設定のある端末、Team/Enterprise) |
diff |
/diff:セッションの未コミット変更をトランスクリプト横のペインにファイル・ハンク単位で表示し、編集やコマンド実行に合わせて更新 |
同梱 |
telemetry |
engine.create の折り返しで $.telemetry(log・mark)を足し、プラグインがファーストパーティの分析行を書けるようにする。分析が無効の環境では何も送らない |
同梱 |
agents-md |
instructionFiles という1つのオプションで AGENTS.md をプロジェクト指示として読む |
同梱 |
各フォルダは完全なプラグインの形をしていて、.claude-plugin/plugin.json、モジュールを指す hooks/hooks.json、そして claude-code から型を import する TypeScript が hooks/ の下にある。型宣言は /plugin-types が書き出すもので、mods/types/ に同じものが置かれている。ソースから動かすには claude --plugin-dir mods/diff のように渡す。
テストの仕組みも独特だ。README によれば claude plugin test mods/diff で走り、テストはエンジン自身の $ とプラグインの on を受け取る。$ への呼び出しはエンジンが実際に行うものと同じ経路を通り、テストが on で登録したフックは mod の「下」、つまり外の世界の位置に座る。答えられなかった呼び出しはイベント名を挙げて例外になる。ただし、検証環境の Claude Code 2.1.277 で claude plugin test を打つと error: unknown command 'test' が返った。README が先行していて、CLI 側にはまだ入っていない(この点は後述の実測表にも残した)。
読者の3問に答えるなら、次のようになる。何ができるか:エンジンのイベントを関数で包み、値を書き換えて流す。何を解決するか:hooks では手が届かなかった「指示ファイルの一覧」「ツール結果への添付」といったエンジン内部の状態を扱える。何を代替するか:agents-md に限れば、AGENTS.md を読ませるための symlink と @import を代替する。
agents-md modが解決すること——AGENTS.mdをCLAUDE.mdと同じ場所に渡す
CHANGELOG 2.1.277 の1行目はこうだ。「AGENTS.md 対応を追加:CLAUDE.md の無いプロジェクトでは Claude Code が代わりに AGENTS.md を読む。/config の Project instructions で変更できる(Bedrock・Vertex・Foundry では未対応)」。実装は PR #95409(poteat・2026-09-18 マージ)で、2.1.276 と 2.1.277 で検証したと書かれている。
肝心なのは「どこに渡すか」だ。README の説明を要約すると、ファイルがモデルに届く経路はエンジンの仕事であって mod の仕事ではない。エンジンは prompt.context で claudeMd の裏にある指示ファイルの一覧({ path, kind, content, parent? }、kind は managed・user・project・local・memory)をフックに手渡し、フックは変更後の一覧を返す。エンジンはその一覧から自分の前置きと枠組みで claudeMd を描画し、ファイル名を告知し、プロジェクト指示を省くエージェント(Explore・Plan・omitClaudeMd を指定した独自エージェント)には managed だけを残す。
つまり agents-md mod が project として足した AGENTS.md は、下流のすべてにとって普通のプロジェクト指示ファイルになる。同じ位置・同じ枠組み・同じ省略規則・同じ告知。symlink や @import で CLAUDE.md を経由していた頃と違い、CLAUDE.md というファイルを1つも置かずに済む。
「プロジェクトが CLAUDE.md を持つか」の判定基準も明文化されている。ルートから作業ディレクトリまでのどこかに CLAUDE.md・.claude/CLAUDE.md・CLAUDE.local.md があれば、そのプロジェクトはエンジンに任せて mod は手を引く。一方、組織の managed ファイル、個人の ~/.claude/CLAUDE.md、.claude/rules の各ファイル、--add-dir で足したディレクトリの CLAUDE.md は「持つ」に数えない。エンジンのネスト探索もそれらを見ないからだ。
既定モード claude-md-or-agents-md"] --> B{"ルート〜作業ディレクトリに
CLAUDE.md/.claude/CLAUDE.md/CLAUDE.local.md がある?"} B -->|ある| C["エンジンの CLAUDE.md だけ
mod は何も足さない"] B -->|ない| D["$.fs.ancestors で
AGENTS.md と .claude/AGENTS.md を集める"] D --> E["project 指示ファイルとして一覧に追加
@import は各ファイルの後ろに個別エントリ"] E --> F["エンジンが CLAUDE.md と同じ前置き・枠組みで描画"] F --> G["Read で下位ディレクトリを読むと
その階層の AGENTS.md を添付"]
4つのinstructionFilesモード——何が読まれ、何が落ちるか
plugin.json の userConfig には instructionFiles の4値が列挙され、/config では「Project instructions」という行のピッカーとして現れる。
| 値 | 読まれるもの | 落ちるもの | 想定場面 |
|---|---|---|---|
claude-md-or-agents-md(既定) |
CLAUDE.md 群。無ければ AGENTS.md と .claude/AGENTS.md |
CLAUDE.md がある場合の AGENTS.md | AGENTS.md だけで運用するリポジトリ |
claude-md-and-agents-md |
各階層で CLAUDE.md → AGENTS.md の順に両方 | CLAUDE.md が @import または symlink で既に読んだ AGENTS.md(パス、次いで内容で比較) |
Claude 固有の指示を CLAUDE.md に足しつつ AGENTS.md も維持 |
claude-md |
エンジンの CLAUDE.md 探索だけ | mod の追加分すべて | 2.1.276 以前と同じ挙動に固定したい |
managed-only |
組織の managed CLAUDE.md と auto memory | project・local・user の CLAUDE.md、.claude/rules、すべての AGENTS.md |
管理下の端末で個人・プロジェクトの指示を切る |
設定は ~/.claude/settings.json に次のように書く。README は「--settings と管理設定でも可、ただしプロジェクトの .claude/settings.json はプラグインのオプションを読まない」と明記している。値を変えるとモジュールが再読込され、次にエンジンが文脈を組むとき(次のターン、新しい会話、/clear、コンパクション)から新しいモードのファイルが載る。4値以外の文字列を手で書くと、トランスクリプトに1回だけ告げられ、既定として読まれる。
{
"pluginConfigs": {
"agents-md@builtin": {
"options": { "instructionFiles": "claude-md-and-agents-md" }
}
}
}
オプションには前史がある。最初は projectInstructions というキーで、値は claude・agents-fallback・both・none だった。旧キーの値は当面読み替えられ(none → managed-only、claude → claude-md、agents-fallback → claude-md-or-agents-md、both → claude-md-and-agents-md、それ以外は claude-md)、最初の session.start でどう読んだかがトランスクリプトに出る。instructionFiles を既定以外に設定した時点で旧キーは読まれなくなり、削除を促すメッセージが出る。ソースにはこの互換処理に COMPAT_BREAK(agents-md-project-instructions) という印がついていて、いずれ落とす予定であることが分かる。
mod を切るには /plugin の同梱一覧から無効にする。disableAllHooks・allowManagedHooksOnly・--bare は設定 hooks とインストール済みプラグインを制御するもので、同梱 mod には効かない。ただし --bare(--add-dir なし)・--safe-mode・CLAUDE_CODE_DISABLE_CLAUDE_MDS のようにエンジン自身が指示ファイルを読まない状態では、mod の探索も何も見つけないので、CLAUDE.md も AGENTS.md も載らない。
実測:Claude Code 2.1.277で5条件を陽性対照つきに試す
理屈は README のとおりだが、本当にそう動くかは手元で確かめた。方法は単純で、ディレクトリごとに CLAUDE.md と AGENTS.md に別々の合言葉を仕込み、claude -p の返答にどの合言葉が出るかを見る。CLAUDE.md だけのケースが陽性対照になる。
# 合言葉を仕込んだ4ディレクトリを作る(c は CLAUDE.md が先頭・AGENTS.md が末尾の語を要求)
mkdir -p a b c d/sub
echo "Project rule: begin every reply with the exact word PINEAPPLE." > a/CLAUDE.md
echo "Project rule: begin every reply with the exact word MANGO." > b/AGENTS.md
cp a/CLAUDE.md c/ && echo "Project rule: end every reply with the exact word MANGO." > c/AGENTS.md
cp b/AGENTS.md d/ && echo "Rule for this directory: when you read a file here, end your reply with the exact word KIWI." > d/sub/AGENTS.md
echo "hello world" > d/sub/note.txt
# 既定モードで順に聞く。モードを変えるときは --settings に JSON を渡す
(cd b && claude -p "Reply with one short greeting." --model haiku)
(cd c && claude -p "Reply with one short greeting." --model haiku \
--settings '{"pluginConfigs":{"agents-md@builtin":{"options":{"instructionFiles":"claude-md-and-agents-md"}}}}')
(cd d && claude -p "Read the file sub/note.txt and tell me its contents in one line." --model haiku)
結果を1枚の表にまとめる。返答は先頭と末尾の語だけを抜き出した。
| # | ディレクトリの中身 | モード | 返答(要旨) | 読まれた指示ファイル |
|---|---|---|---|---|
| 1 | CLAUDE.md のみ |
既定 | PINEAPPLE! Hello… | CLAUDE.md(陽性対照) |
| 2 | AGENTS.md のみ |
既定 | MANGO! Hello there… | AGENTS.md |
| 3 | 両方(AGENTS.md は末尾語を要求) | 既定 | PINEAPPLE! Hi there… | CLAUDE.md だけ。AGENTS.md は無視 |
| 4 | 両方 | claude-md-and-agents-md(--settings) |
PINEAPPLE … MANGO | 両方 |
| 5 | AGENTS.md のみ |
claude-md(--settings) |
Hi! Ready to help… | どちらも読まれない |
| 6 | AGENTS.md のみ |
managed-only(--settings) |
Hey! Ready to help… | どちらも読まれない |
| 7 | AGENTS.md + sub/AGENTS.md、sub/note.txt を Read |
既定 | MANGO … “hello world”. KIWI | ルートの AGENTS.md + Read で添付されたネスト分 |
| 8 | 両方 + プロジェクトの .claude/settings.json で and モード指定 |
(無視される) | PINEAPPLE Hello… | CLAUDE.md だけ。README どおりプロジェクト設定は読まれない |
#3 と #4 の差が、この mod の既定を端的に表している。CLAUDE.md が1つでもあれば AGENTS.md は「無かったこと」になり、両方読ませるには明示的にモードを変える必要がある。#7 はネスト添付の実証で、sub/note.txt を Read した瞬間に sub/AGENTS.md の指示(末尾に KIWI)が効いた。#8 はドキュメントの但し書きどおり、プロジェクトの設定ファイルに書いても効かない。
検証環境:Linux x86_64/Claude Code 2.1.277/モデルは --model haiku/2026-09-19。claude -p(非対話)で実行したため、対話セッションに出るはずの「no CLAUDE.md found; AGENTS.md loaded: …」の行は未確認。--bare での挙動は認証エラーで比較できず未検証。Bedrock・Vertex・Foundry、Windows、テレメトリ無効環境も未検証。
ソースで読む——register.tsの3つのフックと「ネスト添付」の仕組み
mods/agents-md/hooks/register.ts(9,507バイト)が本体で、files・frames・modes・names・switches・telemetry の各ディレクトリがその部品だ。登録するフックは実質3つに、fork 用の1つが加わる。
・session.start:全モードで通る。モードの利用行を $.telemetry.log に流す(await しない)。旧キー projectInstructions があれば、読み替えたか無視したかを1回だけ $.ui.log で告げる。セッション開始はこの mod を待たない
・prompt.context:既定モードと and モードで登録。既定モードではまず $.session.root() を取り、渡された一覧に「探索経路上の CLAUDE.md」があるか、なければ $.fs.ancestors({ names: CLAUDE_NAMES }) で実際に探して isClaudeProject を決める。ソースのコメントが理由を説明している——「エンジンは読み込んだプロジェクト CLAUDE.md を差し控えることがあり、それでもプロジェクトは CLAUDE.md を持っている」。持っていなければ AGENTS_NAMES で探索し、既に載っているものを除いて project として一覧に加える。初回だけ「no CLAUDE.md found; AGENTS.md loaded: …」を出す
・tool.call(Read):Read が拒否もエラーもされず、CLAUDE_CODE_SIMPLE と CLAUDE_CODE_DISABLE_ATTACHMENTS のどちらも立っていないとき、読んだファイルからプロジェクトルートまでの AGENTS.md と CLAUDE.md を並行に集める。既定モードでは CLAUDE.md が「主張している」ディレクトリの AGENTS.md を外し、まだ文脈に無く、この agent loop にまだ送っていないものだけを nestedFrame として結果の context に添付する
・agent.spawn(fork: true):Agent ツールが起動した fork は親のプロンプト接頭辞を共有するので、親の loop に送済みのネストファイル集合を子の loop にコピーし、二重添付を防ぐ
設計上の細部で目を引くのは3点ある。第一に、送済み管理が agent loop ごと(given: Map<loop, Set<path>>)で、prompt.context が終わるたびに空にされる。第二に、ホームディレクトリの解決を Read ツールと同じ規則で行う(Windows 風の作業ディレクトリなら USERPROFILE を先に、そうでなければ HOME)。モデルが ~/ で始まるパスを Read に渡したとき、mod も同じ場所を見るためだ。第三に、テレメトリは件数と閉じた選択肢だけで、パスもファイル本文も送らない。agents_md_mode(モードと対話かどうか)、agents_md_load(ファイル数・import 数・総文字数など)、agents_md_nested(添付数)の3種類で、telemetry mod が座っていない環境では呼び出し自体が痕跡なく落ちる。
CLAUDE.mdとまだ違う8箇所と、使えない環境
README は「まだ CLAUDE.md と違う所」を8項目、正直に列挙している。いずれも「今日のイベントではプラグインから届かないローダーの事実」だ。公式ドキュメントの差分表と合わせて、実務に効く順に並べ直す。
| 観点 | CLAUDE.md | 設定経由で読んだ AGENTS.md |
|---|---|---|
/memory と /context の Memory files |
載る | 載らない。確認は「AGENTS.md loaded」行で |
InstructionsLoaded hooks |
発火する | 発火しない(CLAUDE.md が import/symlink した AGENTS.md なら発火) |
| ネストファイルの添付契機 | Read に加えて、プロンプト内の @ 言及、IDE の開いているファイル・選択範囲、Read のノートブック・画像・PDF |
テキストの Read だけ |
| コンパクション後 | 最近読んだファイルとして復元される | 復元されず、次の Read で再添付。セッション中の変更も再告知されない |
--add-dir のディレクトリ |
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD 設定時に CLAUDE.md が載る |
AGENTS.md は載らない |
作業ディレクトリ外への @import |
承認ダイアログが出る | 承認済みなら無言で読み、未承認なら無言で省く。ダイアログは出ない |
| fork でないサブエージェント | 親が受け取ったネスト CLAUDE.md を再送しない | 自身の最初の Read で再び受け取る |
| パスの比較 | 作業ディレクトリの symlink 別名を解決してから判定 | 綴りで比較 |
使えない環境も明確だ。CHANGELOG は Bedrock・Vertex・Foundry で未対応と書き、公式ドキュメントは「フィーチャーフラグを Anthropic から取得しないセッション(サードパーティ経由、テレメトリ無効)」と「対応版へインストール・更新した直後の最初のセッション」を挙げている。これらでは /config に Project instructions の行自体が出ない。代替は従来どおり CLAUDE.md からの @AGENTS.md だ。
判断——symlink・@importをいつ外すか
公式ドキュメントには「以前の回避策をどうするか」の節が新設された。要点は3つで、いずれも当記事の実測と矛盾しない。
・@AGENTS.md を含む CLAUDE.md:残してよい。どのモードでも AGENTS.md が二重に読まれることはない。Claude 固有の指示が無いなら CLAUDE.md を消して直接読ませる
・symlink の CLAUDE.md:残してよい。and モードでも、パスか内容が一致する AGENTS.md は飛ばされる。Bedrock 等を使う人がチームにいるなら残す価値がある
・「AGENTS.md を読め」と文章で書いた CLAUDE.md:これは Claude が自分で開く気になったときしか効かない。CLAUDE.md を消すか、@AGENTS.md に置き換える
判断の分かれ目は「CLAUDE.md を残す理由があるか」に尽きる。Claude 固有の指示(plan mode の使い方、特定ディレクトリの扱いなど)を書きたいなら、CLAUDE.md に @AGENTS.md を書くか、claude-md-and-agents-md に切り替えるかの二択になる。前者は設定不要でチーム全員に同じ挙動が届き、後者は各自の ~/.claude/settings.json に依存する。チームで揃えるなら前者が安全だ。逆に AGENTS.md だけで足りるなら、CLAUDE.md を消すのが最も摩擦が少ない。ただし /context に出なくなるので、確認手順を「AGENTS.md loaded の行を見る」に改める必要がある。
symlink と @import の具体的な違い、Next.js や Airflow の実物、Codex 側の 32 KiB 上限はAGENTS.mdをClaude Codeに読ませる|v2.1.277で標準対応、橋が要る場合とCodex 32KiB上限で扱っている。AGENTS.md 自体の書き方はAGENTS.mdとは|書き方・ベストプラクティスとCLAUDE.mdの違いをCodex/Devinで解説、CLAUDE.md 側の設計はCLAUDE.mdの書き方?AIに仕事を任せる人が最初に学ぶべきセクション設計と失敗パターンを参照してほしい。
mod という層が公開されたことの意味は、agents-md 1本にとどまらない。prompt.context で指示ファイルの一覧を、tool.call でツール結果を関数として書き換えられる以上、「文脈に何を載せるか」を決める場所がエンジンの外に出た。README が「組織が前置したプラグインはこの mod の上に座り、ファイルについて最終決定権を持つ」と書いているのは、その順序付けが既に設計に含まれているということだ。同梱4本の次に何が来るかは、mods/ を追っていれば分かる。
参照ソース
・anthropics/claude-code — mods/agents-md(README.md・.claude-plugin/plugin.json・hooks/register.ts・hooks/hooks.json、75ファイル)
・anthropics/claude-code — mods/README.md(mod の定義、同梱4本、テストの仕組み)
・PR #95409 mods/agents-md: the AGENTS.md project-instructions mod(poteat・2026-09-18 マージ・2.1.276/2.1.277 で検証)
・Claude Code CHANGELOG 2.1.277(「Added AGENTS.md support」の行と Bedrock・Vertex・Foundry の但し書き)
・Claude Code 公式ドキュメント: How Claude remembers your project(§AGENTS.md:読まれる条件、Project instructions の4値、差分表、回避策の扱い)