mirror of
https://github.com/anthropics/claude-code.git
synced 2026-10-02 05:25:05 +08:00
mods/agents-md: a Read attaches no nested AGENTS.md where the engine attaches nothing to a turn (#95417)
This commit is contained in:
@@ -66,7 +66,12 @@ builds (the next turn after the reload, a new conversation, `/clear`, a
|
||||
compaction) carries the new mode's files. A hand-typed value outside the
|
||||
four is told once in the transcript and reads as the default. `/plugin`
|
||||
lists the plugin among the built-ins, where a person can turn it off; with
|
||||
it off the engine reads `CLAUDE.md` alone.
|
||||
it off the engine reads `CLAUDE.md` alone. No hooks setting or CLI mode turns
|
||||
it off (`disableAllHooks`, `allowManagedHooksOnly` and `--bare` govern
|
||||
settings hooks and installed plugins, not built-ins); where the engine loads
|
||||
no instruction files (`--bare` without `--add-dir`, `--safe-mode`,
|
||||
`CLAUDE_CODE_DISABLE_CLAUDE_MDS`) its walk finds none and it adds none,
|
||||
`CLAUDE.md` and `AGENTS.md` alike.
|
||||
|
||||
The option was first keyed `projectInstructions`, with the values `claude`,
|
||||
`agents-fallback`, `both` and `none`. A value still stored under that key is
|
||||
@@ -88,7 +93,7 @@ same entry is keyed `"agents-md"`.
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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) |
|
||||
| `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): 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 |
|
||||
| `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 |
|
||||
|
||||
## What it calls on `$`
|
||||
|
||||
@@ -97,7 +102,8 @@ apart; with `below` on a Read; it finds nothing on a thin client, whose
|
||||
workspace files are remote, as the engine's own walk does), `session.root`,
|
||||
`session.cwd`, `env.get` (`HOME` and `USERPROFILE`, once per load, the
|
||||
profile first on a Windows spelling of the working directory, so a `~/` path
|
||||
the model hands a Read resolves where the Read tool reads it), `ui.log`,
|
||||
the model hands a Read resolves where the Read tool reads it; `CLAUDE_CODE_SIMPLE`
|
||||
and `CLAUDE_CODE_DISABLE_ATTACHMENTS` on every Read), `ui.log`,
|
||||
`telemetry.log` and `telemetry.mark`.
|
||||
|
||||
`$.telemetry` is the [telemetry](../telemetry) plugin's noun; where that
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"description": "AGENTS.md up and down the tree like CLAUDE.md, by the instructionFiles option: under claude-md-or-agents-md (the default; where the project has no CLAUDE.md of its own on the walk) or claude-md-and-agents-md, a prompt.context hook hands every ancestor AGENTS.md to the engine among its instruction files, skipping ones it already loaded, and a tool.call hook on Read attaches the AGENTS.md files of the directories between the project root and a read file, once a conversation and agent loop (an Agent-tool fork inherits its parent's); under managed-only, a prompt.context hook keeps only the organization's managed files and memory; under claude-md, nothing is added; in every mode a session.start hook sends the plugin's usage row through $.telemetry where that noun is seated",
|
||||
"description": "AGENTS.md up and down the tree like CLAUDE.md, by the instructionFiles option: under claude-md-or-agents-md (the default; where the project has no CLAUDE.md of its own on the walk) or claude-md-and-agents-md, a prompt.context hook hands every ancestor AGENTS.md to the engine among its instruction files, skipping ones it already loaded, and a tool.call hook on Read attaches the AGENTS.md files of the directories between the project root and a read file, once a conversation and agent loop (an Agent-tool fork inherits its parent's), except in a run where the engine attaches nothing to a turn (--bare, CLAUDE_CODE_DISABLE_ATTACHMENTS); under managed-only, a prompt.context hook keeps only the organization's managed files and memory; under claude-md, nothing is added; in every mode a session.start hook sends the plugin's usage row through $.telemetry where that noun is seated",
|
||||
"modules": ["./register.ts"]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ export * from './frames'
|
||||
export * from './modes'
|
||||
export * from './names'
|
||||
export * from './register.js'
|
||||
export * from './switches'
|
||||
export * from './telemetry'
|
||||
|
||||
export * as default from '.'
|
||||
|
||||
@@ -10,6 +10,7 @@ import Files from './files'
|
||||
import Frames from './frames'
|
||||
import Modes from './modes'
|
||||
import Names from './names'
|
||||
import Switches from './switches'
|
||||
import Telemetry from './telemetry'
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,9 @@ const NONE: readonly FsAncestor[] = []
|
||||
* standing alone. `managed-only`: the project's and the person's instruction
|
||||
* files dropped, the organization's kept. `claude-md-or-agents-md` (a project
|
||||
* with none of its own) and `claude-md-and-agents-md`: AGENTS.md files joined
|
||||
* to the engine's instruction files, nested ones on a Read. Every mode sends
|
||||
* to the engine's instruction files, nested ones on a Read except in a run
|
||||
* where the engine attaches nothing to a turn (--bare, which sets
|
||||
* CLAUDE_CODE_SIMPLE, or CLAUDE_CODE_DISABLE_ATTACHMENTS). Every mode sends
|
||||
* its usage rows through `$.telemetry` where that noun is seated and drops
|
||||
* them where it is not.
|
||||
*
|
||||
@@ -182,6 +185,10 @@ export function register(on: On, options: PluginOptions): void {
|
||||
return result
|
||||
}
|
||||
|
||||
if (!(await attachesOnRead($))) {
|
||||
return result
|
||||
}
|
||||
|
||||
const [root, cwd] = await Promise.all([$.session.root(), $.session.cwd()])
|
||||
home ??= await homeOf($, cwd)
|
||||
const read = Frames.absoluteOf(e.file_path, cwd, home)
|
||||
@@ -247,6 +254,31 @@ export function register(on: On, options: PluginOptions): void {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a Read attaches nested AGENTS.md files in this run: not where the
|
||||
* engine attaches nothing to a turn, a nested CLAUDE.md included, which is a
|
||||
* --bare run (it sets CLAUDE_CODE_SIMPLE) or one with
|
||||
* CLAUDE_CODE_DISABLE_ATTACHMENTS on.
|
||||
*
|
||||
* Read on every Read, as the engine reads them on every turn: a settings
|
||||
* `env` block or a managed delivery can flip either mid-session. The files of
|
||||
* the walk itself need no such check: where the engine loads no instruction
|
||||
* files `$.fs.ancestors` finds none.
|
||||
*
|
||||
* @param $ the engine, as the `tool.call` hook holds it
|
||||
* @returns whether nested files ride a Read's result here
|
||||
*/
|
||||
async function attachesOnRead($: EngineInterface): Promise<boolean> {
|
||||
const [simple, attachmentsOff] = await Promise.all([
|
||||
$.env.get('CLAUDE_CODE_SIMPLE'),
|
||||
$.env.get('CLAUDE_CODE_DISABLE_ATTACHMENTS'),
|
||||
])
|
||||
|
||||
return (
|
||||
!Switches.isSwitchedOn(simple) && !Switches.isSwitchedOn(attachmentsOff)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The home directory a `~` in a Read's path stands for, read the way the
|
||||
* Read tool reads it.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './is-switched-on.js'
|
||||
|
||||
export * as default from '.'
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* The spellings an environment switch is on with, as the engine reads its own.
|
||||
*/
|
||||
const ON_SPELLINGS: ReadonlySet<string> = new Set(['1', 'true', 'yes', 'on'])
|
||||
|
||||
/**
|
||||
* Whether an environment variable's value turns a switch on, read the way the
|
||||
* engine reads its own switches: `1`, `true`, `yes` or `on`, any case, trimmed.
|
||||
*
|
||||
* @param value the variable's value, or undefined when it is unset
|
||||
* @returns whether the switch is on
|
||||
*/
|
||||
export const isSwitchedOn = (value: string | undefined): boolean =>
|
||||
value !== undefined && ON_SPELLINGS.has(value.trim().toLowerCase())
|
||||
Reference in New Issue
Block a user