Files
fish-config/docs/manual/16-agent-tooling.md
T
rootiest 1d714a84d0
CI / github-mirror (pull_request) Skipped
CI / test (pull_request) Successful in 2m24s
CI / docs (pull_request) Successful in 1m37s
docs(agent-tooling): add scenario reference table, cross-reference from code
Adds a 'Scenario reference' subsection to docs/manual/16-agent-tooling.md:
two ruled tables covering every combination of what a directory can hold
(only AGENTS.md, only CLAUDE.md, both identical, both different, an
inverted mirror, an already-settled symlink) crossed with whether the
file is deliberately git-tracked, plus the settled-mirror/later-arrival
case separately. Verified against build-manual.py's actual table parser
and the generated Starlight site output, not just visual inspection.

Also closes the gap this section itself pointed out: nothing previously
linked a reader of agents-init.fish or agents-vault.fish's own doc-header
to this page, and nothing told a future contributor the page has to be
kept in sync. Adds:
- CONTRIBUTING.md: a new 'Dedicated manual sections for complex
  subsystems' subsection documenting the pattern in general (when to use
  one, and the update-it-in-the-same-change obligation verify-manual.py
  cannot check for you).
- functions/agents-init.fish, functions/agents-vault.fish: a NOTES
  pointer to the section from each function's own header, so a reader
  who only sees the header still finds the fuller page.
2026-09-23 22:10:36 -04:00

16 KiB

title, manTitle, sidebar, helpKeywords
title manTitle sidebar helpKeywords
AI Agent Tooling 16. AI AGENT TOOLING
order
20
agent
agents-init
agents-vault
AGENTS.md
claude-code
agy
antigravity

This section explains the machinery behind AI coding agents (Claude Code, Antigravity/agy) working in a project checked out from this configuration: where their instructions live, how they get there, and the safety rules that keep an agent's launch-time bookkeeping from touching a repository's own tracked history. Command-line usage for the functions named here (agents-init, agents-vault) is generated from their own doc headers — see Section 5.

The problem this solves

An AI coding agent needs a persistent, project-scoped place to keep instructions, memory, and working notes. Committing that material directly into a project's normal history mixes two concerns that change at different rates and for different reasons: the project's own code, and an agent's evolving working state. It also means every project accumulates its own copy of agent tooling (hooks, version files, convention documents) that has nothing to do with that project's actual purpose.

agents-init and agents-vault exist to keep that material out of the main repository while still making it feel local: an agent reads and writes AGENTS.md exactly where it would expect to find it, but the real content and its history live in a separate, self-contained git repository that the main project never tracks.

The AGENTS.md convention

AGENTS.md is a plain-text file at a project's root (and, as this configuration extends the idea, at the root of any subdirectory with its own scoped conventions) that an AI agent reads for repository-specific instructions. It has become a convention shared across coding agents, not one tool's proprietary format.

Claude Code originally required its own CLAUDE.md filename specifically. It now reads AGENTS.md natively whenever no CLAUDE.md is present, which retired the need for this configuration to create, maintain, or symlink CLAUDE.md at all. A project scaffolded by agents-init today carries only AGENTS.md — at the root, and in any subdirectory that has grown its own scoped conventions (functions/, docs/, and so on, in this repository's own case). A leftover CLAUDE.md from before this change is retired automatically the next time agents-init runs: renamed, not preserved under its old name, so nothing is ever left tracking two copies of the same instructions under two different filenames.

The AGENTS/ sub-repository

agents-init scaffolds a directory named AGENTS/ at a project's root. It is a self-contained git repository — its own .git, its own commit history, its own hooks — and it is gitignored from the project it lives inside. The project's own AGENTS.md (and every subdirectory's) is a symlink into it:

$PROJECT/AGENTS/
├── AGENTS.md         Canonical root agent spec (real file)
├── functions/AGENTS.md Canonical spec for functions/, and likewise for any other scoped subdirectory
├── plans/            Superpowers implementation plans
├── specs/            Superpowers design specs
├── devlogs/          Agent development logs
├── .version          MAJOR.MINOR.PATCH structure version
└── .agents-tools/    Version-bump script and git hook shims (committed)

An agent editing $PROJECT/AGENTS.md is, transparently, editing $PROJECT/AGENTS/AGENTS.md — the file-editing tools most agents ship with resolve a symlinked directory's contents normally, but they cannot write through a symlinked file itself, which is why the seed content agents-init writes for a brand-new project spells this out directly to the agent reading it.

IMPORTANT: This means an agent must never try to write to a symlink named AGENTS.md directly. The seed instructions agents-init writes for a fresh project tell the agent this explicitly, pointing it at the real file inside AGENTS/.

Version tracking and hooks

Every AGENTS/ repository carries a .version file (seeded 1.0.0) and a self-contained version bumper, wired through core.hooksPath rather than the ordinary .git/hooks/ directory:

  • A pre-commit hook bumps .version on every commit: the MINOR field moves when the set of tracked top-level directories changes (a new subdirectory convention was adopted, or one was dropped), the PATCH field otherwise. The MAJOR field is manual-only.
  • A prepare-commit-msg hook appends (vX.Y.Z) to the commit subject, so the version history is legible from git log alone.

Each hook shim then chains to whatever hook of the same name the project's global or system core.hooksPath already points at — a credential scanner like ggshield, Git LFS, or anything else already wired in ahead of this. Pointing core.hooksPath at .agents-tools/hooks locally does not shadow those; it runs both.

The .agents-tools/ scripts themselves are copied in from this configuration's own scripts/agents-tools/ and refreshed automatically whenever their version marker moves, so every project's AGENTS/ repository stays current with this configuration without any manual step.

Downstream tooling that wants to know whether a project's AGENTS/ structure changed — as opposed to just its content — can read the .version file's MINOR field directly rather than diffing the tree.

Per-directory discovery

The convention is not limited to a project's root. Any directory that carries its own AGENTS.md — functions/, docs/, or a subdirectory of a much larger project with genuinely distinct conventions of its own — gets the identical treatment: a real file inside AGENTS/<that path>/, and a symlink at the project location pointing back to it. agents-init finds these automatically on every run, rather than working from a fixed list, by walking the project tree for any file literally named AGENTS.md or CLAUDE.md.

Each directory found is settled into exactly one of four states, in order, so a later run only ever sees a directory that is already consistent:

  1. An inverted mirror (an older layout, where CLAUDE.md was the real file inside AGENTS/ and AGENTS.md was symlinked to it) is flipped in place — same bytes, new name.
  2. A real file at the project level, with no real file inside AGENTS/ yet, is adopted: a lone AGENTS.md moves in as-is; a lone CLAUDE.md is renamed on the way in, never preserved under its own name. When both AGENTS.md and CLAUDE.md are real files at once, byte-identical content is deduplicated (the AGENTS.md side is kept); different content is left exactly as it is, with a warning — this function has no way to know which one is authoritative, and guessing wrong would silently discard the other.
  3. A stray CLAUDE.md inside AGENTS/ left over once AGENTS.md is settled there is removed — nothing named CLAUDE.md survives inside the mirror.
  4. The project-level symlink is created or repaired if missing or stale, and any CLAUDE.md still at the project level is removed. A real file that turns up here after the mirror already settled (for instance, an agent's own /init-style command writing a fresh CLAUDE.md) is held to the same identical-or-differ rule as step 2: a duplicate is dropped, anything different is left alone with a warning rather than silently overwritten.

Safety: what discovery will never touch

Because discovery walks the whole project tree rather than a fixed list, it deliberately prunes several classes of directory before it ever considers what's inside them:

  • Anything outside the project entirely. A directory that has no git repository of its own, but happens to carry a lone AGENTS.md or CLAUDE.md (a home directory scaffolded this way, for instance), is synced at that single location only — no recursive walk runs at all. Recursive discovery only ever runs inside a real git repository.
  • Nested repositories. Any subdirectory that is itself a git repository — a submodule, a nested clone, a plugin checked out inside a tool's own state directory — belongs to a different project and is never walked into.
  • Dot-directories. Anything named starting with . (.git, .claude, .gemini, .github, and so on) is a tool's own state or configuration, not a project's own scoped convention, and is skipped unconditionally.
  • Generated output. build/, dist/, out/, and target/ directories are never inspected — nothing generated by a build step is a source of hand-authored instructions.
  • node_modules/, and any directory literally named AGENTS other than the current project's own mirror.

Safety: deliberately tracked files are left alone

Discovery can reach a directory whose AGENTS.md or CLAUDE.md is already committed to the project's own history on purpose — a team's shared conventions file in a monorepo subdirectory, for instance, tracked long before this configuration's owner ever cloned it. Replacing that file with a symlink would change it from an ordinary tracked file into a link pointing outside the repository the moment agents-init next runs, which is not a decision this tool should make unattended on someone else's behalf.

A real file is left untouched, instead of adopted or replaced, whenever both of the following hold:

  • it is tracked in git's index — staged or committed, checked with git ls-files. A file that has never been git added is not tracked by this definition, even if it sits right next to files that are.
  • the project's .gitignore actually exists and has content in it.

Neither condition alone is enough to protect a file. An untracked file is always safe to adopt, regardless of what .gitignore says about it (nothing has been committed yet, so nothing is lost). A tracked file in a project with no established ignore conventions at all — no .gitignore, or an empty one — is treated as the very first time this convention has been applied to that project, rather than a deliberate choice to keep tracking it: agents-init adopts it the same way it would adopt any other real file, which is the same behavior this tool has always had for a project's own root file.

NOTE: In practice, this means a mature project with an established .gitignore will have any already-committed AGENTS.md/CLAUDE.md left alone across the board — root included — and will only ever adopt one during that project's first encounter with this convention, before a .gitignore entry for it exists yet.

When a directory is skipped for this reason, agents-init prints a warning naming the file and explaining why, rather than staying silent about a directory it chose not to touch.

Scenario reference

Every combination of what a directory can hold, laid out directly. "No" in the tracked column also covers a tracked file in a project with no populated .gitignore (the bootstrap case, above) — both behave the same way. Whenever the tracked column reads "Yes", that reason always wins over the identical-or-different comparison below it, and the warning printed names the file as tracked rather than as differing — the outcome (left alone) is the same either way, only the explanation differs.

Settling a directory for the first time — a real AGENTS.md, a real CLAUDE.md, both, or neither, discovered fresh:

Found                             Deliberately tracked?  Result
--------------------------------  ---------------------  -----------------------------------------------
Only AGENTS.md (real)             No                     Adopted into AGENTS/, symlinked back.
Only AGENTS.md (real)             Yes                    Left exactly as it is; not adopted.
Only CLAUDE.md (real)             No                     Adopted, renamed to AGENTS.md, symlinked back.
Only CLAUDE.md (real)             Yes                    Left exactly as it is; not adopted or renamed.
Both, byte-identical              No                     AGENTS.md adopted; duplicate CLAUDE.md dropped.
Both, byte-identical              Yes (either)           Left exactly as they are; neither touched.
Both, different content           n/a                    Neither touched; warns, resolve by hand.
Mirror has CLAUDE.md (real)       n/a                    Flipped in place: renamed, nothing lost.
Correct AGENTS.md symlink exists  n/a                    Nothing happens -- already settled.

A new real file appearing after a directory's mirror has already settled — an agent's own /init-style command, for instance, writing a fresh CLAUDE.md where an AGENTS.md is already symlinked:

New file vs. mirror  Deliberately tracked?  Result
-------------------  ---------------------  --------------------------------------------------------
Byte-identical        No                     Adopted as a duplicate; the new file is dropped.
Byte-identical        Yes                    Left as it is; not adopted, even though content matches.
Different content     No                     Left as it is; warns that it differs, resolve by hand.
Different content     Yes                    Left as it is; warns that it's tracked, not adopted.

And whatever a directory holds, it never gets this far at all if discovery pruned it outright — see the containment rules above: nested repositories, dot-directories, node_modules/, generated-output directories, and any directory literally named AGENTS.

plans/, specs/, and devlogs/

agents-init --plugins (the second half of what a bare agents-init run does) wires up three more directories inside AGENTS/: plans/ and specs/ for the superpowers skills' implementation plans and design documents, and devlogs/ for agent-authored development notes. Real content from every legacy location this configuration has ever used for these (docs/plans, docs/superpowers/plans, and an older AGENTS/plugins/ layer from before the sub-repository consolidated them) is merged into the canonical AGENTS/plans and AGENTS/specs on first run, and the legacy locations are removed once merged.

docs/superpowers/plans and docs/superpowers/specs are always symlinked to their AGENTS/ counterparts, because the superpowers skills expect to find them there by default. docs/plans, docs/specs, and docs/devlogs are only created as symlinks when a project already had a real directory by that name — nothing forces those paths to exist for a project that never used them.

The launch lifecycle

The claude and agy wrapper functions each run agents-init --quiet (full setup: both the AGENTS.md symlink step and the plans/specs/devlogs wiring) before launching the real CLI, on every invocation. This is what makes the whole system self-healing: a project that has drifted from the expected layout — a stale symlink, a newly-added subdirectory's instructions not yet adopted, a leftover CLAUDE.md — is corrected automatically the next time an agent is launched there, with no separate setup step for a person to remember.

At the end of every agents-init run, any uncommitted change inside AGENTS/ is committed automatically, so whatever an agent wrote during its session is captured without anyone needing to run git add on a repository they were never meant to think about directly. That commit is strictly local: agents-init never fetches or pushes, because a network round trip running synchronously ahead of every agent launch would block the launch itself for as long as an unreachable remote takes to time out. A project's AGENTS/ repository that has its own upstream is pulled and pushed by hand, on its owner's own schedule.

agents-vault is a related but distinct tool: where AGENTS/ holds one project's agent state, agents-vault backs up curated agent memory that lives outside any project tree entirely — ~/.claude/projects/*/memory and similar host-scoped locations — into its own host-scoped repository. The claude/agy wrappers sync both on every launch. See __fish_agent_vault_autopush in Section 7 for its one user-facing configuration variable; command-line usage for both tools is in Section 5.