spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / mods / agents-md / README.md
169 lines · 12.0 KBPreviewCodeRaw
1# agents-md
2
3`AGENTS.md` read the way Claude Code reads `CLAUDE.md`, as a plugin, under
4one option, `instructionFiles`:
5
6- `claude-md`: only `CLAUDE.md` is loaded, by the engine, as today. The plugin
7 adds nothing.
8- `claude-md-or-agents-md` (the default): a project with no instruction files
9 of its own gets its `AGENTS.md` files instead, loaded exactly where and how
10 `CLAUDE.md` would be. "Of its own" is read off what the engine loaded for
11 the context: a `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` in any
12 directory from the root down to the working directory leaves the whole
13 project to the engine, and the plugin stays out (the organization's managed
14 file, the person's `~/.claude/CLAUDE.md`, a `.claude/rules` file and an
15 added directory's `CLAUDE.md` do not count, as the nested walk does not see
16 them either). With none, every `AGENTS.md` and `.claude/AGENTS.md` on that
17 path joins the instruction files the engine renders, and a `Read` under a
18 subdirectory attaches that directory's `AGENTS.md` unless a `CLAUDE.md`
19 there claims it.
20- `claude-md-and-agents-md`: every `AGENTS.md` is loaded beside `CLAUDE.md`,
21 up and down the tree; a file `CLAUDE.md` already `@`-imports, or is a link
22 to, is not loaded a second time (compared by path, then by content).
23- `managed-only`: the project's checked-in and private instruction files and
24 the person's own are dropped from the context; the organization's managed
25 `CLAUDE.md` and the engine's memory stay. The engine's nested `CLAUDE.md`
26 attachments on `Read` are not an event yet and still arrive. (The engine's
27 `claudeMdExcludes` setting also exists, for user, project and local files,
28 and applies to the `AGENTS.md` files this plugin reads too.)
29
30How the files reach the model is the engine's doing, not the plugin's:
31`prompt.context` hands a hook the instruction files behind `claudeMd`
32(`{ path, kind, content, parent? }`, kinds `managed`, `user`, `project`,
33`local`, `memory`, in load order) and a hook answers the list changed. The
34engine then renders `claudeMd` from the answered files with its own preamble
35and framing, announces them by name, and keeps only the `managed` ones for an
36agent that omits project instructions (Explore, Plan, a custom agent with
37`omitClaudeMd`). So an `AGENTS.md` this plugin adds as a `project` file is, to
38everything downstream, a project instruction file: same place in the context,
39same framing, same omission rules, same announcement. An organization's
40prepended plugin on `prompt.context` sits above this one and has the last
41word on the files.
42
43`hooks/register.ts` is the module; everything under `hooks/` is its parts,
44importing `claude-code` and one another alone. `tests/` runs under
45`claude plugin test <this folder>`.
46
47## Setting the option
48
49As a built-in its option is the `/config` row "Project instructions", a
50picker over the four values, each described there. By hand it is
51
52```json
53{
54 "pluginConfigs": {
55 "agents-md@builtin": {
56 "options": { "instructionFiles": "claude-md-and-agents-md" }
57 }
58 }
59}
60```
61
62in user settings (`~/.claude/settings.json`), `--settings`, or managed
63settings; a project's `.claude/settings.json` is not read for plugin
64options. Changing it reloads the module, and the next context the engine
65builds (the next turn after the reload, a new conversation, `/clear`, a
66compaction) carries the new mode's files. A hand-typed value outside the
67four is told once in the transcript and reads as the default. `/plugin`
68lists the plugin among the built-ins, where a person can turn it off; with
69it off the engine reads `CLAUDE.md` alone. No hooks setting or CLI mode turns
70it off (`disableAllHooks`, `allowManagedHooksOnly` and `--bare` govern
71settings hooks and installed plugins, not built-ins); where the engine loads
72no instruction files (`--bare` without `--add-dir`, `--safe-mode`,
73`CLAUDE_CODE_DISABLE_CLAUDE_MDS`) its walk finds none and it adds none,
74`CLAUDE.md` and `AGENTS.md` alike.
75
76The option was first keyed `projectInstructions`, with the values `claude`,
77`agents-fallback`, `both` and `none`. A value still stored under that key is
78honoured for now while `instructionFiles` reads as its default: `none` as
79`managed-only`, `claude` as `claude-md`, `agents-fallback` as
80`claude-md-or-agents-md`, `both` as `claude-md-and-agents-md`, any other
81value as `claude-md` (which adds nothing, never as the default, which loads
82`AGENTS.md`); the first `session.start` of a load says in the transcript how
83it was read. Once `instructionFiles` is set to anything but its default, the
84old key is not read and the transcript says to remove it.
85
86Run from this folder instead (`claude --plugin-dir mods/agents-md`), the
87same entry is keyed `"agents-md"`.
88
89## What it hooks
90
91| event | what the hook does |
92| --- | --- |
93| `session.start` | in every mode: passes the start straight through and floats the usage row for the configured mode, never awaited; the first start of a load logs how a stored `projectInstructions` value is read. The session's start never waits on this plugin |
94| `prompt.context` | under `claude-md-or-agents-md` and `claude-md-and-agents-md`: walks `$.fs.ancestors` for the `AGENTS.md` files above the working directory and answers them as `project` instruction files, each `@` import its own entry after its file, each placed where a project file of its directory stands (root first, before the first deeper project file, else after the last project file, before memory); files the engine already holds by path or by content are left out; under `claude-md-or-agents-md` it answers nothing when the project has a `CLAUDE.md` of its own (among the handed files, else found by a `$.fs.ancestors` walk, so a `CLAUDE.md` the engine loaded and then withheld still counts), and logs which files it loaded once, and again after a move to another project root; handed unknown files (a hook above rewrote the `claudeMd` text) it adds nothing; the first context of a load sends the load row (counts) and the feature mark; under `managed-only` (matcher: a `project`, `local` or `user` file present): answers the list without those kinds |
95| `agent.spawn` on `fork: true` | under `claude-md-or-agents-md` and `claude-md-and-agents-md`: a fork the Agent tool starts shares its parent's prompt prefix, so the parent loop's delivered nested files are copied to the fork's loop and not attached to it again (a `/fork` or `/subtask` fork does not raise `agent.spawn` yet and starts from an empty set, as every fork did before; a fork started in the same tool batch as a `Read` inherits that Read's file although its prefix holds a placeholder for it) |
96| `tool.call` on `Read` | under `claude-md-or-agents-md` and `claude-md-and-agents-md`, for a file under the session's project root (`$.session.root()`, read live, so `/cd`, a host's directory change and worktree moves are followed and a moved root starts the delivered sets and the fallback decision over; a file elsewhere gets nothing, as the engine attaches no nested `CLAUDE.md` there; and nothing anywhere in a run where the engine attaches nothing to a turn, `--bare` with its `CLAUDE_CODE_SIMPLE` or `CLAUDE_CODE_DISABLE_ATTACHMENTS`, read on every Read through `$.env.get` as the engine reads them on every turn): walks only the directories strictly between the root and the read file (`$.fs.ancestors` with `below: root`, as the engine walks only those for a nested `CLAUDE.md`, never up to the filesystem root again) and attaches their `AGENTS.md` files not yet given to that agent loop, not already among the context's instruction files (by path or, for a project file, by text) and not claimed by a `CLAUDE.md` of the same directory (or imported by one), as `context` after the tool result, framed `Contents of <path>:` byte for byte as the engine frames a nested `CLAUDE.md`, whatever its size; each file once per loop and conversation (the context's recomputation after a compaction or `/clear` starts the count over), the context's files never; a Read that attached files sends the nested row. A `~` or `~/` path is read under the home directory as the Read tool reads it |
97
98## What it calls on `$`
99
100`fs.ancestors` (with each found file's `parts`: the file and its imports
101apart; with `below` on a Read; it finds nothing on a thin client, whose
102workspace files are remote, as the engine's own walk does), `session.root`,
103`session.cwd`, `env.get` (`HOME` and `USERPROFILE`, once per load, the
104profile first on a Windows spelling of the working directory, so a `~/` path
105the model hands a Read resolves where the Read tool reads it; `CLAUDE_CODE_SIMPLE`
106and `CLAUDE_CODE_DISABLE_ATTACHMENTS` on every Read), `ui.log`,
107`telemetry.log` and `telemetry.mark`.
108
109`$.telemetry` is the [telemetry](../telemetry) plugin's noun; where that
110plugin is not seated the calls find no noun and are dropped without a trace,
111and nothing else changes.
112
113## What it logs
114
115Counts and closed choices only; no path and no file text. Each row goes
116through `$.telemetry.log`, so it exists only where the telemetry plugin
117does:
118
119| event | when | properties |
120| --- | --- | --- |
121| `agents_md_mode` | once per fresh load, at `session.start` | `mode` (`claude-md` \| `claude-md-or-agents-md` \| `claude-md-and-agents-md` \| `managed-only`), `is_interactive` |
122| `agents_md_load` | the first context of a load, under `claude-md-or-agents-md` and `claude-md-and-agents-md` | `mode`, `file_count` (`AGENTS.md` files handed to the engine), `import_count` (their `@` imports), `total_content_length`, `yielded` (`claude-md-or-agents-md` stood down for a `CLAUDE.md` of the project's own), `walk_failed`; with it one `$.telemetry.mark` for feature `agents_md`: `ok`, or `sad` with reason `walk_failed` |
123| `agents_md_nested` | a Read that attached nested files | `mode`, `file_count` |
124
125## Where it still differs from CLAUDE.md
126
127All of these apply only to the modes that load `AGENTS.md`,
128`claude-md-or-agents-md` (the default) and `claude-md-and-agents-md`. Each
129names a loader fact a plugin cannot reach through the events it has today.
130
1311. Nested files attach on a text `Read` only. The engine also attaches a
132 directory's `CLAUDE.md` for a file `@`-mentioned in the prompt, for the
133 IDE's opened file or selection, and for the `Read` tool's notebook, image
134 and PDF results.
1352. A nested file the plugin attaches is not registered in the loop's
136 read-file state, so after a compaction the engine does not restore it
137 among the recently read files (the plugin attaches it again at the next
138 `Read` under that directory instead), and a change to it mid-session is
139 not re-announced.
1403. `/cd` carries the new tree's `CLAUDE.md` in its own notice; the plugin's
141 files for the new tree arrive in the same next request through the
142 engine's instructions announcement instead.
1434. Paths compare by spelling; the engine resolves a symlinked alias of the
144 working directory before deciding a file is inside it.
1455. `--add-dir` directories contribute no `AGENTS.md`, where the engine can
146 load their `CLAUDE.md`.
1476. `/memory` and the `#` shortcut do not know `AGENTS.md` files, and the
148 engine's own initial-load row does not count them (this plugin's
149 `agents_md_load` row does).
1507. An `@` import outside the working directory inside an `AGENTS.md` is
151 honoured only once the approval the engine asks for a `CLAUDE.md`'s
152 external imports has been given (without it the import is left out, as a
153 `CLAUDE.md`'s is); the approval dialog itself is raised for `CLAUDE.md`
154 imports alone.
1558. A subagent that is not a fork gets a nested `AGENTS.md` at its own first
156 `Read` under that directory even when its parent's loop was already given
157 it; the engine does not hand such a subagent the nested `CLAUDE.md` again.
158 A fork matches the engine on both sides.
159
160## Testing
161
162 claude plugin test mods/agents-md
163
164`tests/register.test.ts` covers the default mode: a project with `AGENTS.md`
165alone gets it as a project instruction file and one transcript line naming
166it, a project with a `CLAUDE.md` of its own is left to the engine without a
167walk, a failed walk leaves the context as handed, and the start hands
168`$.telemetry` the mode row alone where a test seats a provider for that
169noun, and goes on untouched where none is seated.