From dc906840120f8796d3411331d0438f84b5d6baa9 Mon Sep 17 00:00:00 2001 From: Ersan Bilik Date: Thu, 22 Oct 2026 21:16:28 +0300 Subject: [PATCH] feat(init): write agent instructions to AGENTS.md, make CLAUDE.md an @AGENTS.md bridge AGENTS.md is the vendor-neutral instruction file (agents.md, stewarded by the Agentic AI Foundation under the Linux Foundation) read by Codex, Copilot, Cursor, Gemini CLI, OpenCode, Pi and, since 2.1.277, by Claude Code. ctx init wrote the bootstrap instructions only to CLAUDE.md while ctx setup codex|opencode|pi deployed a second, older AGENTS.md template, so mixed-tool teams carried two diverging texts and non-Claude users got nothing from ctx init. ctx init now creates or merges AGENTS.md between the ctx:agents markers the setup deployers already recognise, from the one template every agent shares. CLAUDE.md is still created or merged, but its ctx block is only an @AGENTS.md import: Claude Code's own AGENTS.md fallback needs 2.1.277, is unavailable in some sessions, and switches off as soon as any CLAUDE.md exists in the tree, which is exactly the --merge case. Existing projects migrate with ctx init --reset --merge. Changes: - internal/cli/initialize/core/agents: new HandleMd, wired into init before the CLAUDE.md handler; failures stay non-fatal - internal/assets/integrations/agents.md: tool-neutral version of the former CLAUDE.md content, closed by the new marker.AgentsEnd - internal/assets/claude/CLAUDE.md: reduced to the marker-bounded @AGENTS.md import - merge.OrCreate inserts only the marker-bounded block into an existing file; the template's own title no longer leaks in as a second H1 - docs, command help, flag text and package doc comments follow the new output; DECISIONS.md records the choice and supersedes the 2026-01-25 "Removed AGENTS.md from project root" entry Verification: gofmt, go vet and golangci-lint v2.15.0 (CI pin) are clean; the full go test run shows no new failures against a clean upstream/main worktree on the same Windows checkout (the pre-existing environment failures are identical in both runs). Hand-driven ctx init --merge against a project with its own AGENTS.md and CLAUDE.md inserts the block after each H1 and leaves the rest untouched. Closes ActiveMemory/ctx#197 Spec: specs/init-agents-md.md Signed-off-by: Ersan Bilik --- .context/DECISIONS.md | 14 ++ docs/cli/init-status.md | 8 +- docs/cli/setup.md | 2 +- docs/home/common-workflows.md | 6 +- docs/home/configuration.md | 2 +- docs/operations/integrations.md | 8 +- docs/operations/migration.md | 102 +++++--- .../operations/runbooks/breaking-migration.md | 8 +- docs/operations/upgrading.md | 8 +- internal/assets/README.md | 4 +- internal/assets/claude/CLAUDE.md | 167 +------------- internal/assets/commands/commands.yaml | 3 +- internal/assets/commands/flags.yaml | 2 +- internal/assets/integrations/agents.md | 217 +++++++++++------- internal/assets/project_test.go | 35 ++- internal/assets/read/claude/claude.go | 5 +- internal/assets/read/claude/doc.go | 9 +- internal/cli/initialize/cmd/root/cmd.go | 2 +- internal/cli/initialize/cmd/root/doc.go | 5 +- internal/cli/initialize/cmd/root/run.go | 15 +- internal/cli/initialize/core/agents/agents.go | 65 ++++++ internal/cli/initialize/core/agents/doc.go | 42 ++++ internal/cli/initialize/core/claude/doc.go | 19 +- internal/cli/initialize/core/doc.go | 6 +- internal/cli/initialize/core/merge/create.go | 29 ++- internal/cli/initialize/core/merge/section.go | 36 +++ internal/cli/initialize/init_test.go | 167 ++++++++++++++ internal/config/marker/marker.go | 2 + specs/init-agents-md.md | 169 ++++++++++++++ 29 files changed, 823 insertions(+), 334 deletions(-) create mode 100644 internal/cli/initialize/core/agents/agents.go create mode 100644 internal/cli/initialize/core/agents/doc.go create mode 100644 internal/cli/initialize/core/merge/section.go create mode 100644 specs/init-agents-md.md diff --git a/.context/DECISIONS.md b/.context/DECISIONS.md index d194c7c01..27383ffb8 100644 --- a/.context/DECISIONS.md +++ b/.context/DECISIONS.md @@ -58,6 +58,20 @@ For significant decisions: --> +## [2026-10-22-205524] ctx init writes the agent instructions to AGENTS.md; CLAUDE.md becomes an @AGENTS.md import + +**Status**: Accepted + +**Context**: AGENTS.md (agents.md, stewarded by the Agentic AI Foundation under the Linux Foundation since 2025-12-09) is read natively by Codex, Copilot, Cursor, Gemini CLI, OpenCode, Pi and, since Claude Code 2.1.277 (2026-09-18), by Claude Code when no CLAUDE.md is present in the tree. ctx init wrote the bootstrap instructions only to CLAUDE.md, while ctx setup codex/opencode/pi deployed a second, older AGENTS.md template; mixed-tool teams got two diverging texts and non-Claude users got nothing from ctx init. Upstream issue ActiveMemory/ctx#197. + +**Decision**: ctx init writes the agent instructions to AGENTS.md; CLAUDE.md becomes an @AGENTS.md import + +**Rationale**: One tool-neutral template (internal/assets/integrations/agents.md), loaded once by every agent. ctx init creates or merges AGENTS.md with the same merge.OrCreate semantics CLAUDE.md had, between the ctx:agents markers the setup deployers already recognise. CLAUDE.md keeps a ctx block that is only an @AGENTS.md import: the Claude Code fallback needs 2.1.277, is unavailable in some sessions, and any hand-written CLAUDE.md in the tree switches it off, which is exactly the --merge case. Alternatives rejected: AGENTS.md only (silent loss on older Claude Code or once a CLAUDE.md appears), full content in both files (two copies, loaded twice), a symlink (not portable to Windows checkouts; agents.Deploy rejects symlinks). + +**Consequence**: ctx init writes AGENTS.md (ctx:agents ... ctx:agents:end) and a CLAUDE.md whose ctx:context block is the bridge; existing projects migrate with ctx init --reset --merge, which turns the old full block into the bridge and creates AGENTS.md. ctx setup agents|codex|opencode|pi now ship the richer shared template. Supersedes the 2026-01-25 decision 'Removed AGENTS.md from project root' (archive/decisions-consolidated-2026-02-26.md), whose premise that no tool auto-loads AGENTS.md no longer holds. Spec: specs/init-agents-md.md. + +--- + ## [2026-09-05-195906] Pi extension deploys as a flat single file (.pi/extensions/ctx.ts) with no package.json **Status**: Accepted diff --git a/docs/cli/init-status.md b/docs/cli/init-status.md index 8595d3bb1..76807fbbd 100644 --- a/docs/cli/init-status.md +++ b/docs/cli/init-status.md @@ -41,7 +41,7 @@ ctx init [flags] |-------------|-------|-----------------------------------------------------------------------------| | `--force` | `-f` | Overwrite existing context files | | `--minimal` | `-m` | Only create essential files (`TASKS.md`, `DECISIONS.md`, `CONSTITUTION.md`) | -| `--merge` | | Auto-merge `ctx` content into existing `CLAUDE.md` | +| `--merge` | | Auto-merge `ctx` content into existing `AGENTS.md` and `CLAUDE.md` | **Creates**: @@ -53,7 +53,11 @@ ctx init [flags] ([Phase KB](../recipes/build-a-knowledge-base.md)). Embedded templates are copied; existing files are preserved. - `.claude/settings.local.json` with pre-approved `ctx` permissions -- `CLAUDE.md` with bootstrap instructions (or merges into existing) +- `AGENTS.md` with the agent bootstrap instructions (or merges them into an + existing one): the open standard read by Claude Code, Codex, Copilot, + Cursor and others +- `CLAUDE.md` as a bridge whose `ctx` block is an `@AGENTS.md` import, so + Claude Code loads the same text (or merges the bridge into an existing one) Claude Code hooks and skills are provided by the **`ctx` plugin** (see [Integrations](../operations/integrations.md#claude-code-full-integration)). diff --git a/docs/cli/setup.md b/docs/cli/setup.md index a35f38ff2..89e4d47fb 100644 --- a/docs/cli/setup.md +++ b/docs/cli/setup.md @@ -29,7 +29,7 @@ ctx setup [flags] | Tool | Description | |---------------|--------------------------------------------------------------------| -| `agents` | Generic `AGENTS.md` (read natively by Codex, OpenCode, and others) | +| `agents` | Generic `AGENTS.md`, the file `ctx init` also writes (read natively by Claude Code, Codex, OpenCode, and others) | | `claude-code` | Redirects to plugin install instructions | | `codex` | OpenAI Codex CLI (hooks, MCP, skills, `AGENTS.md`) | | `cursor` | Cursor IDE | diff --git a/docs/home/common-workflows.md b/docs/home/common-workflows.md index c9fca5fe8..606ae3b1c 100644 --- a/docs/home/common-workflows.md +++ b/docs/home/common-workflows.md @@ -259,8 +259,8 @@ ctx system bootstrap ``` This prints the resolved context directory, the files in it, and the -operating rules. The `CLAUDE.md` template instructs the agent to run this -automatically. See [CLI Reference: bootstrap](../cli/bootstrap.md#ctx-system-bootstrap). +operating rules. The `AGENTS.md` template (imported by `CLAUDE.md`) instructs +the agent to run this automatically. See [CLI Reference: bootstrap](../cli/bootstrap.md#ctx-system-bootstrap). ## The Two Skills You Should Always Use @@ -278,7 +278,7 @@ session end are the **highest-value skills** in the entire catalog: Let's provide some **context**, because this is **important**: Although the agent *will* **eventually** discover your context through -`CLAUDE.md → AGENT_PLAYBOOK.md`, `/ctx-remember` +`AGENTS.md → AGENT_PLAYBOOK.md`, `/ctx-remember` **hydrates the full context up front** (*tasks, decisions, recent sessions*) so the agent **starts informed** rather than piecing things together over several turns. diff --git a/docs/home/configuration.md b/docs/home/configuration.md index 603eba20d..a14493ec7 100644 --- a/docs/home/configuration.md +++ b/docs/home/configuration.md @@ -391,7 +391,7 @@ ctx system bootstrap -q # just the context directory path ctx system bootstrap --json # structured output for automation ``` -The `CLAUDE.md` template instructs the agent to run this as its first action. +The `AGENTS.md` template instructs the agent to run this as its first action. Every nudge (*context checkpoint, persistence reminder, etc.*) also includes a `Context: ` footer that re-anchors the agent to the correct directory throughout the session. diff --git a/docs/operations/integrations.md b/docs/operations/integrations.md index 1928423c8..97b8ac810 100644 --- a/docs/operations/integrations.md +++ b/docs/operations/integrations.md @@ -61,7 +61,8 @@ This gives you: | Component | Purpose | |-------------------------------|------------------------| | `.context/` | All context files | -| `CLAUDE.md` | Bootstrap instructions | +| `AGENTS.md` | Bootstrap instructions | +| `CLAUDE.md` | `@AGENTS.md` import | | Plugin hooks | Lifecycle automation | | Plugin skills | Agent Skills | @@ -69,14 +70,15 @@ This gives you: ```mermaid graph TD - A[Session Start] --> B[Claude reads CLAUDE.md] + A[Session Start] --> B[Claude reads CLAUDE.md, which imports AGENTS.md] B --> C[PreToolUse hook runs] C --> D[ctx agent loads context] D --> E[Work happens] E --> F[Session End] ``` -1. **Session start**: Claude reads `CLAUDE.md`, which tells it to check `.context/` +1. **Session start**: Claude reads `CLAUDE.md`, which imports `AGENTS.md`; the + instructions there tell it to check `.context/` 2. **First tool use**: `PreToolUse` hook runs `ctx agent` and emits the context packet (*subsequent invocations within the cooldown window are silent*) 3. **Next session**: Claude reads context files and continues with context diff --git a/docs/operations/migration.md b/docs/operations/migration.md index 50d49fb96..93a1c375e 100644 --- a/docs/operations/migration.md +++ b/docs/operations/migration.md @@ -29,30 +29,41 @@ which tools your team uses. | You have... | Command | What happens | |--------------------------------------|---------------------------------------------------|------------------------------------------------------| -| Nothing (*greenfield*) | `ctx init` | Creates `.context/`, `CLAUDE.md`, permissions | -| Existing `CLAUDE.md` | `ctx init --merge` | Backs up your file, inserts `ctx` block after the H1 | -| Existing `CLAUDE.md` + `ctx` markers | `ctx init --reset` | Replaces the `ctx` block, leaves your content intact | +| Nothing (*greenfield*) | `ctx init` | Creates `.context/`, `AGENTS.md`, `CLAUDE.md`, permissions | +| Existing `AGENTS.md` or `CLAUDE.md` | `ctx init --merge` | Backs up the file, inserts the `ctx` block after its H1 | +| Existing file + `ctx` markers | `ctx init --reset` | Replaces the `ctx` block, leaves your content intact | | `.cursorrules` / `.aider.conf.yml` | `ctx init` | `ctx` ignores those files: they coexist cleanly | -| Team repo, first adopter | `ctx init --merge && git add .context/ CLAUDE.md` | Initialize and commit for the team | +| Team repo, first adopter | `ctx init --merge && git add .context/ AGENTS.md CLAUDE.md` | Initialize and commit for the team | --- -## Existing `CLAUDE.md` +## Existing `AGENTS.md` or `CLAUDE.md` This is the most common scenario: -You have a `CLAUDE.md` with project-specific instructions and don't want to +You have an `AGENTS.md` or `CLAUDE.md` with project-specific instructions and +don't want to lose them. -!!! tip "You Own `CLAUDE.md`" - **After initialization, `CLAUDE.md` is yours: edit it freely**. +`ctx` writes its instructions to `AGENTS.md`, the file Claude Code, Codex, +Copilot, Cursor and other agents read, between `` and +``. `CLAUDE.md` gets a much smaller block between +`` and ``: an `@AGENTS.md` import that +makes Claude Code load the same text (a `CLAUDE.md` without it would make +Claude Code ignore `AGENTS.md` altogether). Both files are handled the same +way below. + +!!! tip "You Own Both Files" + **After initialization, `AGENTS.md` and `CLAUDE.md` are yours: edit them + freely**. Add project instructions, remove sections you don't need, reorganize as you see fit. - The only part `ctx` manages is the block between the `` - and `` markers; everything outside those markers is yours - to change at any time. + The only parts `ctx` manages are the blocks between the markers + (`` ... `` in `AGENTS.md`, + `` ... `` in `CLAUDE.md`); everything + outside those markers is yours to change at any time. If you remove the markers, nothing breaks: `ctx` simply treats the file as having no `ctx` content and will offer to merge again on the next @@ -61,12 +72,12 @@ lose them. ### What `ctx init` Does -When `ctx init` detects an existing `CLAUDE.md`, it checks for `ctx` markers -(`` ... ``): +When `ctx init` detects an existing `AGENTS.md` or `CLAUDE.md`, it checks the +file for its `ctx` markers: | State | Default behavior | With `--merge` | With `--force` | |----------------------------|--------------------------|---------------------------|---------------------------------| -| No `CLAUDE.md` | Creates from template | Creates from template | Creates from template | +| No file | Creates from template | Creates from template | Creates from template | | Exists, no `ctx` markers | **Prompts** to merge | Auto-merges (*no prompt*) | Auto-merges (*no prompt*) | | Exists, has `ctx` markers | Skips (*already set up*) | Skips | Replaces the `ctx` block only | @@ -74,7 +85,7 @@ When `ctx init` detects an existing `CLAUDE.md`, it checks for `ctx` markers `--merge` auto-merges without prompting. The merge process: -1. **Backs up** your existing `CLAUDE.md` to `CLAUDE.md..bak`; +1. **Backs up** the existing file to `..bak`; 2. **Finds the H1 heading** (e.g., `# My Project`) in your file; 3. **Inserts** the `ctx` block immediately after it; 4. **Preserves** everything else untouched. @@ -83,7 +94,7 @@ Your content before and after the `ctx` block remains exactly as it was. ### Before / After Example -**Before**: your existing `CLAUDE.md`: +**Before**: your existing `AGENTS.md`: ```markdown # My Project @@ -104,7 +115,7 @@ Your content before and after the `ctx` block remains exactly as it was. ```markdown # My Project - + ## IMPORTANT: You Have Persistent Memory @@ -112,7 +123,7 @@ Your content before and after the `ctx` block remains exactly as it was. This project uses Context (`ctx`) for context persistence across sessions. ... - + ## Build Commands @@ -128,39 +139,61 @@ This project uses Context (`ctx`) for context persistence across sessions. Your build commands and code style sections are untouched. The `ctx` block sits between markers and can be updated independently. +Your `CLAUDE.md`, if you have one, receives only the bridge: + +```markdown +# My Project + + + + +@AGENTS.md + + +## Build Commands +... +``` + ### The `--force` Flag -If your `CLAUDE.md` already has `ctx` markers (from a previous `ctx init`), the -default behavior is to skip it. Use `--force` to replace the `ctx` block with the +If your `AGENTS.md` or `CLAUDE.md` already has `ctx` markers (from a previous +`ctx init`), the default behavior is to skip it. Use `--force` to replace the `ctx` block with the latest template: This is useful after **upgrading** `ctx`: ```bash ctx init --reset ``` -This only replaces content between `` and ``. -Your own content outside the markers is preserved. A timestamped backup is -created before any changes. +This only replaces the content between the markers. Your own content outside +them is preserved. A timestamped backup is created before any changes. + +Upgrading from a `ctx` release that wrote the full instructions into +`CLAUDE.md`? The same command moves them: the old `CLAUDE.md` block becomes +the `@AGENTS.md` import and `AGENTS.md` is created with the instructions. ### Undoing a Merge Every merge creates a backup: ```bash -$ ls CLAUDE.md*.bak +$ ls AGENTS.md*.bak CLAUDE.md*.bak +AGENTS.md.1738000000.bak CLAUDE.md.1738000000.bak ``` To restore: ```bash +cp AGENTS.md.1738000000.bak AGENTS.md cp CLAUDE.md.1738000000.bak CLAUDE.md ``` Or if you are using `git`, simply: ```bash -git checkout CLAUDE.md +git checkout AGENTS.md CLAUDE.md ``` --- @@ -168,7 +201,8 @@ git checkout CLAUDE.md ## Existing `.cursorrules` / Aider / Copilot `ctx` doesn't touch tool-specific config files. It creates its own files -(`.context/`, `CLAUDE.md`) and coexists with whatever you already have. +(`.context/`, `AGENTS.md`, `CLAUDE.md`) and coexists with whatever you already +have. ### What Does `ctx` Create? @@ -177,7 +211,7 @@ git checkout CLAUDE.md | `.context/` directory | `.cursorrules` | | `CLAUDE.md` (*or merges into*) | `.aider.conf.yml` | | `.claude/settings.local.json` (*seeded by `ctx init`; the plugin manages hooks and skills*) | `.github/copilot-instructions.md` | -| | `.windsurfrules` | +| `AGENTS.md` (*or merges into*) | `.windsurfrules` | | | Any other tool-specific config | Claude Code hooks and skills are provided by the **`ctx` plugin**, @@ -254,7 +288,7 @@ With those in place, committing is straightforward: ctx init --merge # Commit context files (journals and keys are already gitignored) -git add .context/ CLAUDE.md +git add .context/ AGENTS.md CLAUDE.md git commit -m "Add ctx context management" git push ``` @@ -295,7 +329,8 @@ Common conflict scenarios: You don't need the whole team to switch at once: 1. One person runs `ctx init --merge` and commits; -2. `CLAUDE.md` instructions work immediately for Claude Code users; +2. `AGENTS.md` works immediately for Claude Code, Codex, Copilot, Cursor and + every other agent that reads it (`CLAUDE.md` imports it for Claude Code); 3. Other tool users can adopt at their own pace using `ctx setup `; 4. Context files benefit everyone who reads them, even without tool integration. @@ -334,11 +369,12 @@ If you used `--merge`, check that your original content is intact: ```bash # Your original content should still be there -cat CLAUDE.md +cat AGENTS.md -# The ctx block should be between markers -grep -c "ctx:context" CLAUDE.md # Should print 1 -grep -c "ctx:end" CLAUDE.md # Should print 1 +# The ctx block should be between markers, and CLAUDE.md should import it +grep -c "" AGENTS.md # Should print 1 +grep -c "ctx:agents:end" AGENTS.md # Should print 1 +grep -c "@AGENTS.md" CLAUDE.md # Should print 1 ``` --- diff --git a/docs/operations/runbooks/breaking-migration.md b/docs/operations/runbooks/breaking-migration.md index 65aa78572..2052b915b 100644 --- a/docs/operations/runbooks/breaking-migration.md +++ b/docs/operations/runbooks/breaking-migration.md @@ -47,13 +47,13 @@ Or use the `/_ctx-command-audit` skill after the rename. # Install the new binary make build && sudo make install -# Regenerate CLAUDE.md and permissions +# Regenerate AGENTS.md, CLAUDE.md and permissions ctx init --reset --merge ``` `--merge` preserves your knowledge files (TASKS.md, DECISIONS.md, -etc.) while regenerating infrastructure (permissions, CLAUDE.md -managed sections). +etc.) while regenerating infrastructure (permissions, AGENTS.md and +CLAUDE.md managed sections). ## Step 3: Update the Plugin @@ -114,7 +114,7 @@ in the release notes using this template: The old name is removed (no deprecation alias). **Action required**: -1. Run `ctx init --reset --merge` to update CLAUDE.md +1. Run `ctx init --reset --merge` to update AGENTS.md and CLAUDE.md 2. Update any scripts referencing `ctx old-command` 3. Update hook configs if applicable diff --git a/docs/operations/upgrading.md b/docs/operations/upgrading.md index 5e57e6dff..268977201 100644 --- a/docs/operations/upgrading.md +++ b/docs/operations/upgrading.md @@ -13,7 +13,7 @@ icon: lucide/arrow-up-circle ## Upgrade -New versions of `ctx` may ship updated permissions, `CLAUDE.md` directives, +New versions of `ctx` may ship updated permissions, `AGENTS.md` directives, or plugin hooks and skills. !!! tip "Claude Code User?" @@ -45,7 +45,7 @@ ctx init --reset --merge | Category | Examples | Changes between versions? | |--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------| -| **Infrastructure** | `.claude/settings.local.json` (permissions), ctx-managed sections in `CLAUDE.md`, **`ctx` plugin** (hooks + skills) | Yes | +| **Infrastructure** | `.claude/settings.local.json` (permissions), ctx-managed sections in `AGENTS.md` and `CLAUDE.md`, **`ctx` plugin** (hooks + skills) | Yes | | **Knowledge** | `.context/TASKS.md`, `DECISIONS.md`, `LEARNINGS.md`, `CONVENTIONS.md`, `ARCHITECTURE.md`, `GLOSSARY.md`, `CONSTITUTION.md`, `AGENT_PLAYBOOK.md` | **No**: this is **your** data | Infrastructure is regenerated by `ctx init` and plugin updates. @@ -72,7 +72,9 @@ ctx init --reset --merge ``` * `--force` regenerates infrastructure files (permissions, ctx-managed - sections in `CLAUDE.md`). + sections in `AGENTS.md` and `CLAUDE.md`). Coming from a release that + wrote the instructions into `CLAUDE.md`, this moves them into + `AGENTS.md` and leaves an `@AGENTS.md` import behind. * `--merge` preserves your content outside `ctx` markers. Knowledge files (`.context/TASKS.md`, `DECISIONS.md`, etc.) are **preserved diff --git a/internal/assets/README.md b/internal/assets/README.md index c76550cca..a71e5b1d5 100644 --- a/internal/assets/README.md +++ b/internal/assets/README.md @@ -99,7 +99,7 @@ subtree. Moving assets out of this tree without also moving | Path | Language(s) | Consumer | Deployed to | |----------------------------------------------|------------------------|-------------------------------|-------------------------------------------| -| `claude/CLAUDE.md` | Markdown | Claude Code plugin host | user project root | +| `claude/CLAUDE.md` | Markdown | Claude Code, imports AGENTS.md | user project root | | `claude/.claude-plugin/plugin.json` | JSON | Claude Code | plugin manifest | | `claude/skills/*/SKILL.md` | Markdown + frontmatter | Claude Code skills | skill registry | | `claude/skills/*/references/*.md` | Markdown | Claude Code skill body | referenced from SKILL.md | @@ -114,7 +114,7 @@ subtree. Moving assets out of this tree without also moving | `hooks/messages/*/*.txt` | Plain text | ctx hooks | rendered to stdout/stderr in-process | | `hooks/messages/registry.yaml` | YAML | ctx hook router | parsed in-process | | `hooks/trace/*.sh` | Bash | git tracing | written to `.git/hooks/` | -| `integrations/agents.md` | Markdown | ctx (`ctx setup` flows) | written to consumer-tool paths | +| `integrations/agents.md` | Markdown | ctx (`ctx init`, `ctx setup`) | AGENTS.md in the user project root | | `integrations/copilot/*.md` | Markdown | GitHub Copilot | repo instructions | | `integrations/copilot-cli/*.{json,md}` | JSON + Markdown | Copilot CLI | hook config + instructions | | `integrations/copilot-cli/skills/*/SKILL.md` | Markdown + frontmatter | Copilot CLI skills | skill registry | diff --git a/internal/assets/claude/CLAUDE.md b/internal/assets/claude/CLAUDE.md index 8e832002c..c25b37829 100644 --- a/internal/assets/claude/CLAUDE.md +++ b/internal/assets/claude/CLAUDE.md @@ -2,167 +2,8 @@ - -## IMPORTANT: You Have Persistent Memory - -This project uses Context (`ctx`) for context persistence across sessions. -When `ctx` is installed, **your memory is NOT ephemeral**: it lives in the -context directory. - -`ctx` is an optional companion tool: helpful, but never required to build, -test, or contribute to this project. If it is not installed, offer the -setup note in "On Session Start" once, then skip every `ctx`-dependent -instruction in this file and help with the task at hand as usual. - -## On Session Start - -1. **Run `ctx system bootstrap`**: it tells you where the context - directory is. - - **If the `ctx` command is not found**: `ctx` is optional, so this is - not an error. Mention once that the user can enable persistent context - by cloning the repository and installing the plugin from the local - clone (releases are infrequent, so marketplace versions may lag behind - the repository): - - ```bash - git clone https://github.com/ActiveMemory/ctx.git ~/WORKSPACE/ctx - ``` - - Then, inside Claude Code: - - ``` - /plugin marketplace add ~/WORKSPACE/ctx - /plugin install ctx@activememory-ctx - ``` - - The `ctx` binary is installed separately; see - https://ctx.ist/home/getting-started/. Afterward, continue with the - user's task and work without persistent context until `ctx` is set up. - - **If `ctx` is installed but returns an error**: relay the error output - to the user verbatim, point them at - https://ctx.ist/home/getting-started/ for setup, and STOP. Do not try - to activate, initialize, or otherwise recover: **those are the user's - decisions**. Wait for their next instruction. -2. **Read AGENT_PLAYBOOK.md** from the context directory: it explains - how to use this system -3. **Run `ctx agent`** for a content summary - -## When Asked "Do You Remember?" - -When the user asks "Do you remember?", "What were we working on?", or any -memory-related question: - -**Do this FIRST (silently):** -- Read TASKS.md, DECISIONS.md, and LEARNINGS.md from the context directory -- Run `ctx journal source --limit 5` for recent session history - -**Then respond with a structured readback:** - -1. **Last session**: cite the most recent session topic and date -2. **Active work**: list pending or in-progress tasks -3. **Recent context**: mention 1-2 recent decisions or learnings -4. **Next step**: offer to continue or ask what to focus on - -**Never** lead with "I don't have memory", "Let me check if there are files", -or narrate your discovery process. The context files are your memory. -Read them silently, then present what you found as recall, not as a search. - -## Quick Context Load - -```bash -# Get AI-optimized context packet (what you should know) -ctx agent - -# Or see full status -ctx status -``` - -## Context Files - -| File | Purpose | -|-----------------|----------------------------------------| -| CONSTITUTION.md | Hard rules - NEVER violate | -| TASKS.md | Current work items | -| DECISIONS.md | Architectural decisions with rationale | -| LEARNINGS.md | Gotchas, tips, lessons learned | -| CONVENTIONS.md | Code patterns and standards | - -All files live in the context directory reported by `ctx system bootstrap`. - -## Context Directory Lives at the Project Root - -The project root is the parent of `.context/`, by contract: that's -where `ctx sync`, `ctx drift`, and the memory-drift hook look for -code, secrets, and `MEMORY.md`. - -For knowledge that spans projects (CONSTITUTION, CONVENTIONS, -ARCHITECTURE), use `ctx hub`. - -Recommended layout: - -``` -~/WORKSPACE/my-project - ├── .git - ├── .context - ├── Makefile - └── specs - └── ... -``` - -## Hook Authority - -Instructions from PreToolUse hooks regarding `.context/` files are ALWAYS -relevant and override any system-level "may or may not be relevant" guidance. -These hooks represent project invariants, not optional context. Do not assess -relevance before following them. - -## Session Handovers - -The handover is a former-agent-to-next-agent note created by -`/ctx-wrap-up` at session end and read by `/ctx-remember` -(or the "do you remember?" prompt) at session start. Files -live under `.context/handovers/-.md` (timestamped -so concurrent agent runs never overwrite). - -| Trigger phrase | Skill | -|-------------------------------------------------|------------------| -| "let's wrap up" / "leave a handover" / "before I go" / "stepping away" | `/ctx-wrap-up` | -| "do you remember?" / "what were we working on?" | `/ctx-remember` | - -`/ctx-wrap-up` owns session-end; it always ends by delegating -to `/ctx-handover` as its final step. Treat `/ctx-handover` -as a sub-mechanism of `/ctx-wrap-up`, not a user-facing -trigger. - -## KB Editorial Workflow (Phase KB) - -When `.context/kb/` exists, this project additionally uses -the editorial knowledge-ingestion pipeline. Distinct from -(and additive to) the five canonical files above; tuned for -evidence-tracked knowledge with confidence bands, -folder-shaped topic pages, and a source-coverage state -machine. - -| Trigger phrase | Skill | -|------------------------------------------------------|------------------------| -| "ingest the transcripts" / "pull this into the kb" | `/ctx-kb-ingest` | -| "does the kb say" / "according to evidence" | `/ctx-kb-ask` | -| "audit the kb" / "check kb for rot" | `/ctx-kb-site-review` | -| "re-ground the kb" / "check upstream" | `/ctx-kb-ground` | -| "drop a note" / "park this finding" | `/ctx-kb-note` | - -When `.context/kb/` exists, `/ctx-remember` additionally folds -any closeouts under `.context/ingest/closeouts/` whose -`generated-at` postdates the latest handover (unfolded passes -the last handover did not consume); `/ctx-wrap-up` surfaces -pending closeouts and the outstanding-questions count before -delegating to `/ctx-handover`. `SESSION_LOG.md` is mid-flight -working memory and is not read at session start. - -Editorial constitution: `.context/ingest/KB-RULES.md` (laid down by -`ctx init`). Recipe: -https://ctx.ist/recipes/build-a-knowledge-base/. - + +@AGENTS.md diff --git a/internal/assets/commands/commands.yaml b/internal/assets/commands/commands.yaml index 96b92ad60..e7c041cb8 100644 --- a/internal/assets/commands/commands.yaml +++ b/internal/assets/commands/commands.yaml @@ -507,7 +507,8 @@ initialize: - AGENT_PLAYBOOK.md: How AI agents should use this system Additionally, in the project root: - - CLAUDE.md : Claude Code configuration + - AGENTS.md : Agent instructions (open standard, read by every agent) + - CLAUDE.md : Claude Code bridge that imports AGENTS.md Use --minimal to only create essential files (TASKS.md, DECISIONS.md, CONSTITUTION.md). diff --git a/internal/assets/commands/flags.yaml b/internal/assets/commands/flags.yaml index 65547f30e..11184dfdc 100644 --- a/internal/assets/commands/flags.yaml +++ b/internal/assets/commands/flags.yaml @@ -88,7 +88,7 @@ setup.write: initialize.reset: short: Reset an existing context (interactive only; backs up existing files to .context/.backup-init-/ before overwriting) initialize.merge: - short: Auto-merge ctx content into existing CLAUDE.md + short: Auto-merge ctx content into existing AGENTS.md and CLAUDE.md initialize.minimal: short: Only create essential files (TASKS.md, DECISIONS.md, CONSTITUTION.md) initialize.no-plugin-enable: diff --git a/internal/assets/integrations/agents.md b/internal/assets/integrations/agents.md index 313e22247..2e5305ee1 100644 --- a/internal/assets/integrations/agents.md +++ b/internal/assets/integrations/agents.md @@ -3,122 +3,167 @@ -## You Have Persistent Memory +## IMPORTANT: You Have Persistent Memory -This project uses Context (`ctx`) for context persistence across -sessions. Your memory is NOT ephemeral: it lives in `.context/`. +This project uses Context (`ctx`) for context persistence across sessions. +When `ctx` is installed, **your memory is NOT ephemeral**: it lives in the +context directory. + +`ctx` is an optional companion tool: helpful, but never required to build, +test, or contribute to this project. If it is not installed, offer the +setup note in "On Session Start" once, then skip every `ctx`-dependent +instruction in this file and help with the task at hand as usual. ## On Session Start -Read these files **in order** before starting any work: +1. **Run `ctx system bootstrap`**: it tells you where the context + directory is. + + **If the `ctx` command is not found**: `ctx` is optional, so this is + not an error. Mention once that the user can enable persistent context + by installing `ctx` (https://ctx.ist/home/getting-started/) and wiring + up their agent. Claude Code users install the plugin from a local + clone of the repository (releases are infrequent, so marketplace + versions may lag behind the repository): + + ```bash + git clone https://github.com/ActiveMemory/ctx.git ~/WORKSPACE/ctx + ``` + + Then, inside Claude Code: -1. `.context/CONSTITUTION.md`: Hard rules, NEVER violate -2. `.context/TASKS.md`: Current work items -3. `.context/CONVENTIONS.md`: Code patterns and standards -4. `.context/ARCHITECTURE.md`: System structure -5. `.context/DECISIONS.md`: Architectural decisions with rationale -6. `.context/LEARNINGS.md`: Gotchas, tips, lessons learned -7. `.context/AGENT_PLAYBOOK.md`: How to use this context system + ``` + /plugin marketplace add ~/WORKSPACE/ctx + /plugin install ctx@activememory-ctx + ``` -If `ctx` is installed (check with `which ctx` or `Get-Command ctx`): -- Run `ctx agent --budget 4000` for a token-budgeted context summary -- Run `ctx status` for a health check + Other agents run `ctx setup ` (`codex`, `opencode`, `pi`, + `copilot`). Afterward, continue with the user's task and work without + persistent context until `ctx` is set up. -After reading, confirm: "I have read the required context files and I'm -following project conventions." + **If `ctx` is installed but returns an error**: relay the error output + to the user verbatim, point them at + https://ctx.ist/home/getting-started/ for setup, and STOP. Do not try + to activate, initialize, or otherwise recover: **those are the user's + decisions**. Wait for their next instruction. +2. **Read AGENT_PLAYBOOK.md** from the context directory: it explains + how to use this system +3. **Run `ctx agent`** for a content summary ## When Asked "Do You Remember?" +When the user asks "Do you remember?", "What were we working on?", or any +memory-related question: + **Do this FIRST (silently):** -- Read `.context/TASKS.md` -- Read `.context/DECISIONS.md` and `.context/LEARNINGS.md` -- Check `.context/sessions/` for recent session files +- Read TASKS.md, DECISIONS.md, and LEARNINGS.md from the context directory +- Run `ctx journal source --limit 5` for recent session history **Then respond with a structured readback:** + 1. **Last session**: cite the most recent session topic and date 2. **Active work**: list pending or in-progress tasks 3. **Recent context**: mention 1-2 recent decisions or learnings 4. **Next step**: offer to continue or ask what to focus on -**Never** lead with "I don't have memory." The `.context/` files are -your memory. Read them silently, then present what you found as recall. - -## Context Files - -| File | Purpose | -|-------------------|----------------------------------------| -| CONSTITUTION.md | Hard rules, NEVER violate | -| TASKS.md | Current work items | -| CONVENTIONS.md | Code patterns and standards | -| ARCHITECTURE.md | System structure | -| DECISIONS.md | Architectural decisions with rationale | -| LEARNINGS.md | Gotchas, tips, lessons learned | -| AGENT_PLAYBOOK.md | How to use this context system | - -All files live in `.context/`. - -## Session Persistence - -After completing meaningful work, save a session summary to -`.context/sessions/`. - -### Session File Format +**Never** lead with "I don't have memory", "Let me check if there are files", +or narrate your discovery process. The context files are your memory. +Read them silently, then present what you found as recall, not as a search. -Create a file named `YYYY-MM-DD-topic.md`: +## Quick Context Load -```markdown -# Session: YYYY-MM-DD - Brief Topic Description - -## What Was Done -- Describe completed work items - -## Decisions -- Key decisions made and their rationale - -## Learnings -- Gotchas, tips, or insights discovered +```bash +# Get AI-optimized context packet (what you should know) +ctx agent -## Next Steps -- Follow-up work or remaining items +# Or see full status +ctx status ``` -### When to Save - -- After completing a task or feature -- After making architectural decisions -- After a debugging session -- Before ending the session -- At natural breakpoints in long sessions - -## Context Updates During Work - -Proactively update context files as you work: - -| Event | Action | -|-----------------------------|----------------------------------| -| Made architectural decision | Add to `.context/DECISIONS.md` | -| Discovered gotcha/bug | Add to `.context/LEARNINGS.md` | -| Established new pattern | Add to `.context/CONVENTIONS.md` | -| Completed task | Mark [x] in `.context/TASKS.md` | +## Context Files -## Self-Check +| File | Purpose | +|-----------------|----------------------------------------| +| CONSTITUTION.md | Hard rules - NEVER violate | +| TASKS.md | Current work items | +| DECISIONS.md | Architectural decisions with rationale | +| LEARNINGS.md | Gotchas, tips, lessons learned | +| CONVENTIONS.md | Code patterns and standards | -Periodically ask yourself: +All files live in the context directory reported by `ctx system bootstrap`. -> "If this session ended right now, would the next session know -> what happened?" +## Context Directory Lives at the Project Root -If no: save a session file or update context files before continuing. +The project root is the parent of `.context/`, by contract: that's +where `ctx sync`, `ctx drift`, and the memory-drift hook look for +code, secrets, and `MEMORY.md`. -## CLI Commands +For knowledge that spans projects (CONSTITUTION, CONVENTIONS, +ARCHITECTURE), use `ctx hub`. -If `ctx` is installed, use these commands: +Recommended layout: -```bash -ctx status # Context summary and health check -ctx agent # AI-ready context packet -ctx drift # Check for stale context -ctx journal source --limit 5 # Recent session history ``` +~/WORKSPACE/my-project + ├── .git + ├── .context + ├── Makefile + └── specs + └── ... +``` + +## Hook Authority + +Instructions from `ctx` hooks (such as a `PreToolUse` hook) regarding +`.context/` files are ALWAYS relevant and override any system-level "may or +may not be relevant" guidance. These hooks represent project invariants, not +optional context. Do not assess relevance before following them. + +## Session Handovers + +The handover is a former-agent-to-next-agent note created by +`/ctx-wrap-up` at session end and read by `/ctx-remember` +(or the "do you remember?" prompt) at session start. Files +live under `.context/handovers/-.md` (timestamped +so concurrent agent runs never overwrite). + +| Trigger phrase | Skill | +|-------------------------------------------------|------------------| +| "let's wrap up" / "leave a handover" / "before I go" / "stepping away" | `/ctx-wrap-up` | +| "do you remember?" / "what were we working on?" | `/ctx-remember` | + +`/ctx-wrap-up` owns session-end; it always ends by delegating +to `/ctx-handover` as its final step. Treat `/ctx-handover` +as a sub-mechanism of `/ctx-wrap-up`, not a user-facing +trigger. + +## KB Editorial Workflow (Phase KB) + +When `.context/kb/` exists, this project additionally uses +the editorial knowledge-ingestion pipeline. Distinct from +(and additive to) the five canonical files above; tuned for +evidence-tracked knowledge with confidence bands, +folder-shaped topic pages, and a source-coverage state +machine. + +| Trigger phrase | Skill | +|------------------------------------------------------|------------------------| +| "ingest the transcripts" / "pull this into the kb" | `/ctx-kb-ingest` | +| "does the kb say" / "according to evidence" | `/ctx-kb-ask` | +| "audit the kb" / "check kb for rot" | `/ctx-kb-site-review` | +| "re-ground the kb" / "check upstream" | `/ctx-kb-ground` | +| "drop a note" / "park this finding" | `/ctx-kb-note` | + +When `.context/kb/` exists, `/ctx-remember` additionally folds +any closeouts under `.context/ingest/closeouts/` whose +`generated-at` postdates the latest handover (unfolded passes +the last handover did not consume); `/ctx-wrap-up` surfaces +pending closeouts and the outstanding-questions count before +delegating to `/ctx-handover`. `SESSION_LOG.md` is mid-flight +working memory and is not read at session start. + +Editorial constitution: `.context/ingest/KB-RULES.md` (laid down by +`ctx init`). Recipe: +https://ctx.ist/recipes/build-a-knowledge-base/. diff --git a/internal/assets/project_test.go b/internal/assets/project_test.go index e4b0e80c5..15bf1c9c7 100644 --- a/internal/assets/project_test.go +++ b/internal/assets/project_test.go @@ -12,16 +12,49 @@ import ( "testing" "github.com/ActiveMemory/ctx/internal/config/asset" + "github.com/ActiveMemory/ctx/internal/config/marker" ) +// TestClaudeMd verifies the CLAUDE.md template is the bridge that +// imports AGENTS.md: ctx markers around an @AGENTS.md line and none of +// the instructions themselves, which live in the AGENTS.md template. func TestClaudeMd(t *testing.T) { content, err := FS.ReadFile(asset.PathCLAUDEMd) if err != nil { t.Fatalf("unexpected error: %v", err) } - if !strings.Contains(string(content), "Context") { + text := string(content) + if !strings.Contains(text, "Context") { t.Error("CLAUDE.md does not contain 'Context'") } + for _, want := range []string{ + marker.CtxStart, marker.CtxEnd, "\n@AGENTS.md\n", + } { + if !strings.Contains(text, want) { + t.Errorf("CLAUDE.md template missing %q", want) + } + } + if strings.Contains(text, "ctx system bootstrap") { + t.Error("CLAUDE.md template should import AGENTS.md, not repeat it") + } +} + +// TestAgentsMd verifies the AGENTS.md template carries the ctx block +// markers the init merge and the setup deployers key on, plus the +// bootstrap instruction every agent must follow. +func TestAgentsMd(t *testing.T) { + content, err := FS.ReadFile(asset.PathAgentsMd) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + text := string(content) + for _, want := range []string{ + marker.AgentsStart, marker.AgentsEnd, "ctx system bootstrap", + } { + if !strings.Contains(text, want) { + t.Errorf("AGENTS.md template missing %q", want) + } + } } func TestProjectFile(t *testing.T) { diff --git a/internal/assets/read/claude/claude.go b/internal/assets/read/claude/claude.go index 68a54cb7c..5870a7e56 100644 --- a/internal/assets/read/claude/claude.go +++ b/internal/assets/read/claude/claude.go @@ -13,8 +13,9 @@ import ( // Md reads the CLAUDE.md template from the embedded filesystem. // -// CLAUDE.md is deployed to the project root by a dedicated handler -// during initialization, separate from the .context/ templates. +// CLAUDE.md, the @AGENTS.md bridge, is deployed to the project root by +// a dedicated handler during initialization, separate from the +// .context/ templates. // // Returns: // - []byte: CLAUDE.md content diff --git a/internal/assets/read/claude/doc.go b/internal/assets/read/claude/doc.go index a845337d1..0bf644d78 100644 --- a/internal/assets/read/claude/doc.go +++ b/internal/assets/read/claude/doc.go @@ -9,10 +9,11 @@ // // # CLAUDE.md Template // -// Md returns the embedded CLAUDE.md template for -// project-level Claude Code instructions. This file -// is deployed to the project root during ctx init, -// separate from the .context/ template files. +// Md returns the embedded CLAUDE.md template: the +// bridge whose ctx-managed block imports AGENTS.md, +// where the project-level instructions live. This +// file is deployed to the project root during ctx +// init, separate from the .context/ template files. // // content, err := claude.Md() // diff --git a/internal/cli/initialize/cmd/root/cmd.go b/internal/cli/initialize/cmd/root/cmd.go index 27b013abf..e9ce3bbf9 100644 --- a/internal/cli/initialize/cmd/root/cmd.go +++ b/internal/cli/initialize/cmd/root/cmd.go @@ -37,7 +37,7 @@ import ( // overwriting. Refuses when --caller is set. // - --minimal, -m: Only create essential files // (TASKS, DECISIONS, CONSTITUTION) -// - --merge: Auto-merge ctx content into existing CLAUDE.md +// - --merge: Auto-merge ctx content into existing AGENTS.md and CLAUDE.md // - --no-plugin-enable: Skip auto-enabling the ctx plugin in // ~/.claude/settings.json // - --no-steering-init: Skip scaffolding foundation steering diff --git a/internal/cli/initialize/cmd/root/doc.go b/internal/cli/initialize/cmd/root/doc.go index 5815f18da..43e7023fa 100644 --- a/internal/cli/initialize/cmd/root/doc.go +++ b/internal/cli/initialize/cmd/root/doc.go @@ -27,7 +27,8 @@ // 8. Merges permissions into settings.local.json. // 9. Auto-enables the ctx plugin globally and // locally. -// 10. Creates or merges CLAUDE.md. +// 10. Creates or merges AGENTS.md and the CLAUDE.md +// bridge that imports it. // 11. Deploys Makefile.ctx and amends the user // Makefile. // 12. Updates .gitignore with recommended entries. @@ -39,7 +40,7 @@ // - --minimal, -m: Only create essential files // (TASKS, DECISIONS, CONSTITUTION). // - --merge: Auto-merge ctx content into existing -// CLAUDE.md without prompting. +// AGENTS.md and CLAUDE.md without prompting. // - --no-plugin-enable: Skip auto-enabling the ctx // plugin in Claude settings. // - --no-steering-init: Skip scaffolding foundation diff --git a/internal/cli/initialize/cmd/root/run.go b/internal/cli/initialize/cmd/root/run.go index 745476377..fd2193cfd 100644 --- a/internal/cli/initialize/cmd/root/run.go +++ b/internal/cli/initialize/cmd/root/run.go @@ -17,6 +17,7 @@ import ( "github.com/ActiveMemory/ctx/internal/assets/read/catalog" "github.com/ActiveMemory/ctx/internal/assets/read/desc" "github.com/ActiveMemory/ctx/internal/assets/read/template" + coreAgents "github.com/ActiveMemory/ctx/internal/cli/initialize/core/agents" "github.com/ActiveMemory/ctx/internal/cli/initialize/core/backup" coreClaude "github.com/ActiveMemory/ctx/internal/cli/initialize/core/claude" coreCC "github.com/ActiveMemory/ctx/internal/cli/initialize/core/claudecheck" @@ -36,6 +37,7 @@ import ( "github.com/ActiveMemory/ctx/internal/config/embed/text" "github.com/ActiveMemory/ctx/internal/config/file" "github.com/ActiveMemory/ctx/internal/config/fs" + cfgHook "github.com/ActiveMemory/ctx/internal/config/hook" "github.com/ActiveMemory/ctx/internal/config/sync" "github.com/ActiveMemory/ctx/internal/config/token" errFs "github.com/ActiveMemory/ctx/internal/err/fs" @@ -49,7 +51,8 @@ import ( // Run executes the init command logic. // // Creates a .context/ directory with template files. Handles existing -// directories, minimal mode, and CLAUDE.md merge operations. +// directories, minimal mode, and the AGENTS.md and CLAUDE.md merge +// operations. // // Under the cwd-anchored resolution model // (spec: specs/cwd-anchored-context.md), init is the command that @@ -280,7 +283,15 @@ func Run( } } - // Handle CLAUDE.md creation/merge + // Handle AGENTS.md creation/merge: the tool-neutral instruction + // file every agent reads; CLAUDE.md below only imports it + // (specs/init-agents-md.md). + if agentsErr := coreAgents.HandleMd(cmd, reset, merge); agentsErr != nil { + // Non-fatal: warn but continue + initialize.InfoWarnNonFatal(cmd, cfgHook.FileAgentsMd, agentsErr) + } + + // Handle CLAUDE.md creation/merge (the @AGENTS.md bridge) if claudeErr := coreClaude.HandleMd(cmd, reset, merge); claudeErr != nil { // Non-fatal: warn but continue initialize.InfoWarnNonFatal(cmd, claude.Md, claudeErr) diff --git a/internal/cli/initialize/core/agents/agents.go b/internal/cli/initialize/core/agents/agents.go new file mode 100644 index 000000000..5cbf59e24 --- /dev/null +++ b/internal/cli/initialize/core/agents/agents.go @@ -0,0 +1,65 @@ +// / ctx: https://ctx.ist +// ,'`./ do you remember? +// `.,'\ +// \ Copyright 2026-present Context contributors. +// SPDX-License-Identifier: Apache-2.0 + +package agents + +import ( + "github.com/spf13/cobra" + + "github.com/ActiveMemory/ctx/internal/assets/read/agent" + "github.com/ActiveMemory/ctx/internal/assets/read/desc" + "github.com/ActiveMemory/ctx/internal/cli/initialize/core/merge" + "github.com/ActiveMemory/ctx/internal/config/embed/text" + cfgHook "github.com/ActiveMemory/ctx/internal/config/hook" + "github.com/ActiveMemory/ctx/internal/config/marker" + "github.com/ActiveMemory/ctx/internal/entity" + errInit "github.com/ActiveMemory/ctx/internal/err/initialize" + "github.com/ActiveMemory/ctx/internal/write/initialize" +) + +// HandleMd creates or merges AGENTS.md with ctx content. +// +// AGENTS.md is the tool-neutral instruction file read by Claude Code, +// Codex, Copilot, Cursor, OpenCode, Pi and others, so it carries the +// full ctx instructions; CLAUDE.md only imports it. The ctx block sits +// between marker.AgentsStart and marker.AgentsEnd, the markers +// ctx setup agents|codex|opencode|pi key on, so a later setup run +// leaves the file alone (specs/init-agents-md.md). +// +// Parameters: +// - cmd: Cobra command for output +// - force: If true, overwrite the existing ctx section +// - autoMerge: If true, skip interactive confirmation +// +// Returns: +// - error: Non-nil if file operations fail +func HandleMd(cmd *cobra.Command, force, autoMerge bool) error { + templateContent, err := agent.AgentsMd() + if err != nil { + return errInit.ReadTemplate(cfgHook.FileAgentsMd, err) + } + + created, mergeErr := merge.OrCreate(cmd, entity.MergeParams{ + Filename: cfgHook.FileAgentsMd, + MarkerStart: marker.AgentsStart, + MarkerEnd: marker.AgentsEnd, + TemplateContent: templateContent, + Force: force, + AutoMerge: autoMerge, + ConfirmPrompt: desc.Text(text.DescKeyInitConfirmClaude), + UpdateTextKey: text.DescKeyWriteInitUpdatedCtxSection, + }) + + if mergeErr != nil { + return mergeErr + } + + if created { + initialize.Created(cmd, cfgHook.FileAgentsMd) + } + + return nil +} diff --git a/internal/cli/initialize/core/agents/doc.go b/internal/cli/initialize/core/agents/doc.go new file mode 100644 index 000000000..eebad96a6 --- /dev/null +++ b/internal/cli/initialize/core/agents/doc.go @@ -0,0 +1,42 @@ +// / ctx: https://ctx.ist +// ,'`./ do you remember? +// `.,'\ +// \ Copyright 2026-present Context contributors. +// SPDX-License-Identifier: Apache-2.0 + +// Package agents handles AGENTS.md creation and merging +// during the ctx init pipeline. +// +// # Overview +// +// AGENTS.md is the vendor-neutral instruction file that +// tells coding agents how to work with a project; Claude +// Code, Codex, Copilot, Cursor, OpenCode and Pi read it. +// During init, this package either creates a new AGENTS.md +// from the embedded template it shares with ctx setup, or +// merges the ctx section into an existing file using +// marker-delimited regions (specs/init-agents-md.md). +// +// # Behavior +// +// [HandleMd] creates a new AGENTS.md from the embedded +// template, or merges the ctx-managed section into an +// existing file using marker-delimited regions. +// +// # Data Flow +// +// When [HandleMd] is called it: +// +// 1. Reads the embedded AGENTS.md template from the +// assets package (integrations/agents.md). +// 2. Delegates to the merge sub-package via +// merge.OrCreate, passing the ctx:agents marker +// boundaries that delimit the ctx-managed section. +// 3. If no AGENTS.md exists, the template is written +// as a new file and a creation message is printed. +// 4. If AGENTS.md exists without markers, the section +// is inserted after the H1, after a prompt unless +// auto-merge is set. With markers present the file +// is left alone unless force is set, which replaces +// the section. +package agents diff --git a/internal/cli/initialize/core/claude/doc.go b/internal/cli/initialize/core/claude/doc.go index 1c4c840d5..d4d782e6b 100644 --- a/internal/cli/initialize/core/claude/doc.go +++ b/internal/cli/initialize/core/claude/doc.go @@ -9,17 +9,22 @@ // // # Overview // -// CLAUDE.md is the project-level instruction file that -// tells Claude Code how to work with a project. During -// init, this package either creates a new CLAUDE.md -// from a built-in template or merges the ctx section -// into an existing file using marker-delimited regions. +// CLAUDE.md is the project-level instruction file Claude +// Code reads. The ctx instructions themselves live in +// AGENTS.md (see the sibling agents package), so the +// ctx-managed section of CLAUDE.md is a bridge: an +// "@AGENTS.md" import line that Claude Code expands at +// launch. The bridge keeps Claude Code releases before +// 2.1.277, sessions that cannot read AGENTS.md directly, +// and hand-written CLAUDE.md files (which otherwise +// suppress AGENTS.md) on the same instructions +// (specs/init-agents-md.md). // // # Behavior // // [HandleMd] creates a new CLAUDE.md from the embedded -// template, or merges the ctx-managed section into an -// existing file using marker-delimited regions. +// bridge template, or merges the ctx-managed section into +// an existing file using marker-delimited regions. // // # Data Flow // diff --git a/internal/cli/initialize/core/doc.go b/internal/cli/initialize/core/doc.go index 92ade992a..e013e8ecf 100644 --- a/internal/cli/initialize/core/doc.go +++ b/internal/cli/initialize/core/doc.go @@ -17,17 +17,19 @@ // // # Sub-packages // +// - agents: creates or merges AGENTS.md with the +// ctx-managed instructions every agent reads. // - backup: creates timestamped .bak copies before // overwriting existing files. // - claude: creates or merges CLAUDE.md with the -// ctx-managed section. +// ctx-managed @AGENTS.md import. // - claudecheck: validates CLAUDE.md structure and // renders diagnostic hints. // - entry: creates context file templates (TASKS.md, // DECISIONS.md, etc.) and locates insertion points // in existing files. // - merge: marker-delimited section merging for -// CLAUDE.md and prompt files. +// AGENTS.md, CLAUDE.md and prompt files. // - pad: sets up the encrypted or plaintext // scratchpad. // - plugin: detects and enables the ctx companion diff --git a/internal/cli/initialize/core/merge/create.go b/internal/cli/initialize/core/merge/create.go index f86fd8df6..49782c94b 100644 --- a/internal/cli/initialize/core/merge/create.go +++ b/internal/cli/initialize/core/merge/create.go @@ -87,13 +87,24 @@ func OrCreate(cmd *cobra.Command, p entity.MergeParams) (bool, error) { return false, bkErr } + // Insert only the marker-bounded ctx block: the template's own + // title belongs to a fresh file, not to one that has a title. + section, sectionErr := markedSection( + p.Filename, string(p.TemplateContent), p.MarkerStart, p.MarkerEnd, + ) + if sectionErr != nil { + return false, sectionErr + } + gap := token.NewlineLF + token.NewlineLF insertPos := entry.FindInsertionPoint(existingStr) var mergedContent string if insertPos == 0 { - mergedContent = string(p.TemplateContent) + token.NewlineLF + existingStr + mergedContent = section + gap + + strings.TrimLeft(existingStr, token.NewlineLF) } else { - mergedContent = existingStr[:insertPos] + token.NewlineLF + - string(p.TemplateContent) + token.NewlineLF + existingStr[insertPos:] + mergedContent = strings.TrimRight(existingStr[:insertPos], token.NewlineLF) + + gap + section + gap + + strings.TrimLeft(existingStr[insertPos:], token.NewlineLF) } if writeErr := io.SafeWriteFile( @@ -138,14 +149,12 @@ func UpdateMarkedSection( endIdx += len(markerEnd) } - templateStr := string(newTemplate) - templateStart := strings.Index(templateStr, markerStart) - templateEnd := strings.Index(templateStr, markerEnd) - if templateStart == -1 || templateEnd == -1 { - return errPrompt.TemplateMissingMarkers(filename) + sectionContent, sectionErr := markedSection( + filename, string(newTemplate), markerStart, markerEnd, + ) + if sectionErr != nil { + return sectionErr } - - sectionContent := templateStr[templateStart : templateEnd+len(markerEnd)] newContent := existing[:startIdx] + sectionContent + existing[endIdx:] if bkErr := backup.File(cmd, filename, []byte(existing)); bkErr != nil { diff --git a/internal/cli/initialize/core/merge/section.go b/internal/cli/initialize/core/merge/section.go new file mode 100644 index 000000000..ad2b6f62f --- /dev/null +++ b/internal/cli/initialize/core/merge/section.go @@ -0,0 +1,36 @@ +// / ctx: https://ctx.ist +// ,'`./ do you remember? +// `.,'\ +// \ Copyright 2026-present Context contributors. +// SPDX-License-Identifier: Apache-2.0 + +package merge + +import ( + "strings" + + errPrompt "github.com/ActiveMemory/ctx/internal/err/prompt" +) + +// markedSection returns the ctx block of a template: the opening +// marker through the closing marker, inclusive. +// +// Parameters: +// - filename: Target file name, for the error message +// - template: Full template content +// - markerStart: Opening marker string +// - markerEnd: Closing marker string +// +// Returns: +// - string: The block from markerStart through markerEnd +// - error: Non-nil if the template lacks either marker +func markedSection( + filename, template, markerStart, markerEnd string, +) (string, error) { + start := strings.Index(template, markerStart) + end := strings.Index(template, markerEnd) + if start == -1 || end == -1 { + return "", errPrompt.TemplateMissingMarkers(filename) + } + return template[start : end+len(markerEnd)], nil +} diff --git a/internal/cli/initialize/init_test.go b/internal/cli/initialize/init_test.go index 960beca2d..559a50a34 100644 --- a/internal/cli/initialize/init_test.go +++ b/internal/cli/initialize/init_test.go @@ -18,6 +18,7 @@ import ( cfgClaude "github.com/ActiveMemory/ctx/internal/config/claude" "github.com/ActiveMemory/ctx/internal/config/ctx" "github.com/ActiveMemory/ctx/internal/config/env" + "github.com/ActiveMemory/ctx/internal/config/marker" errInit "github.com/ActiveMemory/ctx/internal/err/initialize" ) @@ -202,6 +203,9 @@ func TestInitMergeInsertsAfterH1(t *testing.T) { if !strings.Contains(contentStr, "Run make build") { t.Error("original content was lost") } + if strings.Contains(contentStr, "# Project Context") { + t.Error("merge should insert only the ctx block, not the template heading") + } } func TestInitMergeInsertsAtTopWhenNoH1(t *testing.T) { @@ -822,3 +826,166 @@ func TestInitNoSteeringInitFlagSkipsScaffold(t *testing.T) { // `$PWD/.context/`; the env-vs-cwd mismatch class can no longer // occur. The mismatch guard (envmatch package and ErrEnvCwdMismatch // sentinel) was deleted in the same change. + +// TestInitCreatesAgentsMdAndClaudeBridge verifies that a fresh init +// writes the ctx instructions into AGENTS.md and leaves CLAUDE.md as +// an @AGENTS.md import, so the text is loaded once by every agent. +func TestInitCreatesAgentsMdAndClaudeBridge(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "cli-init-agents-md-*") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer func() { _ = os.RemoveAll(tmpDir) }() + + origDir, _ := os.Getwd() + if err = os.Chdir(tmpDir); err != nil { + t.Fatalf("failed to chdir: %v", err) + } + defer func() { _ = os.Chdir(origDir) }() + t.Setenv("HOME", tmpDir) + t.Setenv(env.SkipPathCheck, env.True) + + initCmd := Cmd() + initCmd.SetArgs([]string{}) + if err = initCmd.Execute(); err != nil { + t.Fatalf("init failed: %v", err) + } + + agents, err := os.ReadFile("AGENTS.md") + if err != nil { + t.Fatalf("AGENTS.md not created: %v", err) + } + agentsStr := string(agents) + for _, want := range []string{ + marker.AgentsStart, marker.AgentsEnd, "ctx system bootstrap", + } { + if !strings.Contains(agentsStr, want) { + t.Errorf("AGENTS.md missing %q", want) + } + } + + claudeMd, err := os.ReadFile("CLAUDE.md") + if err != nil { + t.Fatalf("CLAUDE.md not created: %v", err) + } + claudeStr := string(claudeMd) + for _, want := range []string{ + marker.CtxStart, marker.CtxEnd, "\n@AGENTS.md\n", + } { + if !strings.Contains(claudeStr, want) { + t.Errorf("CLAUDE.md missing %q", want) + } + } + if strings.Contains(claudeStr, "ctx system bootstrap") { + t.Error("CLAUDE.md should import AGENTS.md, not repeat its instructions") + } +} + +// TestInitMergeAgentsMdInsertsAfterH1 verifies that --merge inserts +// the ctx block into an existing AGENTS.md right after its H1 and +// keeps the rest of the file intact, mirroring the CLAUDE.md merge. +func TestInitMergeAgentsMdInsertsAfterH1(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "cli-init-agents-merge-*") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer func() { _ = os.RemoveAll(tmpDir) }() + + origDir, _ := os.Getwd() + if err = os.Chdir(tmpDir); err != nil { + t.Fatalf("failed to chdir: %v", err) + } + defer func() { _ = os.Chdir(origDir) }() + t.Setenv("HOME", tmpDir) + t.Setenv(env.SkipPathCheck, env.True) + + existingContent := "# My Amazing Project\n\n" + + "This is the project description.\n\n" + + "## Build Instructions\n\nRun make build.\n" + if err = os.WriteFile("AGENTS.md", []byte(existingContent), 0600); err != nil { + t.Fatalf("failed to create AGENTS.md: %v", err) + } + + initCmd := Cmd() + initCmd.SetArgs([]string{"--merge"}) + if err = initCmd.Execute(); err != nil { + t.Fatalf("init failed: %v", err) + } + + content, err := os.ReadFile("AGENTS.md") + if err != nil { + t.Fatalf("failed to read AGENTS.md: %v", err) + } + contentStr := string(content) + + if !strings.HasPrefix(contentStr, "# My Amazing Project") { + t.Error("H1 heading should remain at the start") + } + ctxIdx := strings.Index(contentStr, marker.AgentsStart) + buildIdx := strings.Index(contentStr, "## Build Instructions") + if ctxIdx == -1 { + t.Fatal("ctx:agents marker not found") + } + if buildIdx == -1 { + t.Fatal("Build Instructions section not found") + } + if ctxIdx > buildIdx { + t.Error("ctx content should appear before Build Instructions, not after") + } + if !strings.Contains(contentStr, "Run make build") { + t.Error("original content was lost") + } + if strings.Contains(contentStr, "# Project Context") { + t.Error("merge should insert only the ctx block, not the template heading") + } + if _, statErr := os.Stat("CLAUDE.md"); statErr != nil { + t.Errorf("CLAUDE.md bridge not created alongside AGENTS.md: %v", statErr) + } +} + +// TestInitExistingAgentsMdWithMarkersUntouched verifies that an +// AGENTS.md already carrying a ctx block (for example one deployed by +// ctx setup codex) is left alone unless --reset is given. +func TestInitExistingAgentsMdWithMarkersUntouched(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "cli-init-agents-existing-*") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer func() { _ = os.RemoveAll(tmpDir) }() + + origDir, _ := os.Getwd() + if err = os.Chdir(tmpDir); err != nil { + t.Fatalf("failed to chdir: %v", err) + } + defer func() { _ = os.Chdir(origDir) }() + t.Setenv("HOME", tmpDir) + t.Setenv(env.SkipPathCheck, env.True) + + existingContent := "# My Project\n\n" + + marker.AgentsStart + "\nOld agents block\n" + marker.AgentsEnd + + "\n\n## Custom Section\n\nSome custom content here.\n" + if err = os.WriteFile("AGENTS.md", []byte(existingContent), 0600); err != nil { + t.Fatalf("failed to create AGENTS.md: %v", err) + } + + initCmd := Cmd() + initCmd.SetArgs([]string{}) + if err = initCmd.Execute(); err != nil { + t.Fatalf("init failed: %v", err) + } + + content, err := os.ReadFile("AGENTS.md") + if err != nil { + t.Fatalf("failed to read AGENTS.md: %v", err) + } + contentStr := string(content) + if !strings.Contains(contentStr, "Old agents block") { + t.Error("existing ctx block was replaced without --reset") + } + if !strings.Contains(contentStr, "Custom Section") { + t.Error("AGENTS.md lost custom section") + } + if strings.Contains(contentStr, "ctx system bootstrap") { + t.Error("template merged into a file that already has ctx markers") + } +} diff --git a/internal/config/marker/marker.go b/internal/config/marker/marker.go index 5c8f45bab..13ee0b17a 100644 --- a/internal/config/marker/marker.go +++ b/internal/config/marker/marker.go @@ -78,6 +78,8 @@ const ( const ( // AgentsStart marks the beginning of ctx-managed AGENTS.md content. AgentsStart = "" + // AgentsEnd marks the end of ctx-managed AGENTS.md content. + AgentsEnd = "" ) // Index markers for auto-generated table of contents sections. diff --git a/specs/init-agents-md.md b/specs/init-agents-md.md new file mode 100644 index 000000000..a479da7cf --- /dev/null +++ b/specs/init-agents-md.md @@ -0,0 +1,169 @@ +# `ctx init` writes AGENTS.md; CLAUDE.md imports it + +## Problem + +`ctx init` writes the agent bootstrap instructions (run +`ctx system bootstrap`, read `AGENT_PLAYBOOK.md`, the "do you +remember?" readback, hook authority, handovers, the KB workflow) only +into `CLAUDE.md`. `AGENTS.md` is the vendor-neutral instruction file: +stewarded by the Agentic AI Foundation under the Linux Foundation +since 2025-12-09 and read natively by Codex, GitHub Copilot, Cursor, +Gemini CLI, Jules, OpenCode, Pi, Zed, Aider and others. Claude Code +2.1.277 (2026-09-18) reads it too, but only when no `CLAUDE.md` +(or `.claude/CLAUDE.md`, `CLAUDE.local.md`) exists in the working +directory or above it; when one exists, Claude reads `CLAUDE.md` only, +unless that file imports `AGENTS.md` with an `@AGENTS.md` line. + +Two consequences today: + +- A Codex, Cursor or Copilot user who runs `ctx init` gets no file + their agent reads. +- `ctx setup agents|codex|opencode|pi` deploy `AGENTS.md` from a + second, older template (`internal/assets/integrations/agents.md`): + no bootstrap step, no handovers, no KB workflow, and it still points + at `.context/sessions/`. A team that mixes tools gets two diverging + instruction texts. + +The 2026-01-25 decision "Removed AGENTS.md from project root" rested +on "no tool auto-loads AGENTS.md"; that premise no longer holds. +Upstream issue: ActiveMemory/ctx#197. + +## Approach + +One template, loaded once by every agent: + +- `AGENTS.md` carries the ctx block between `` and + ``, created or merged by `ctx init` with the + same `merge.OrCreate` semantics `CLAUDE.md` has today: create when + absent, prompt (or `--merge`) to insert after the H1 of an existing + file, `--reset` replaces the block. The embedded template is + `internal/assets/integrations/agents.md`, rewritten as the + tool-neutral version of the current CLAUDE.md content. + `ctx setup agents|codex|opencode|pi` keep deploying that same file + through `agents.Deploy`, which already skips a file that carries the + `ctx:agents` marker, so `ctx setup codex` after `ctx init` is a + no-op for `AGENTS.md`. +- `CLAUDE.md` is still created or merged, but its ctx block + (`` ... ``) shrinks to a bridge: + an explanatory comment and the line `@AGENTS.md`. Claude Code + expands the import at launch, relative to the importing file. The + bridge is written unconditionally rather than relying on the native + fallback because the fallback needs Claude Code 2.1.277 or later, is + unavailable in some sessions (the Claude Code docs recommend the + import there), and is switched off by any hand-written `CLAUDE.md` + in the tree, which is exactly the `--merge` case. + +Alternatives considered: + +- `AGENTS.md` only, no `CLAUDE.md`: silently loses the instructions on + older Claude Code and the moment the user adds their own + `CLAUDE.md`. Rejected. +- Full content in both files: two copies to keep in sync, loaded + twice. Rejected. +- Keep the full `CLAUDE.md` and symlink `AGENTS.md` to it (the + agents.md migration tip): symlinks do not survive Windows checkouts + and `agents.Deploy` rejects them. Rejected. + +## Behavior + +### Happy Path + +1. `ctx init` in a project with neither file writes `AGENTS.md` + (title, ctx block) and `CLAUDE.md` (title, bridge block). Both are + reported as created. +2. Claude Code loads `CLAUDE.md` and expands `@AGENTS.md`; Codex, + Copilot, Cursor, OpenCode and Pi load `AGENTS.md` directly. Every + agent sees one copy of the instructions. +3. A later `ctx setup codex` finds the `ctx:agents` marker in + `AGENTS.md` and skips it (unchanged behavior). + +### Edge Cases + +| Case | Expected behavior | +|------|-------------------| +| `AGENTS.md` exists without ctx markers (hand-written) | Default: prompt "Would you like to append ctx context management instructions?"; `--merge`: no prompt. On yes: backup, block inserted after the H1 (at the top when there is no H1), rest untouched. On no: skipped. | +| `AGENTS.md` exists with a ctx block (earlier `ctx init`, or `ctx setup codex` with the old template) | Left alone: "ctx content exists". `ctx init --reset` replaces the block with the current template after a backup. | +| `CLAUDE.md` exists with the old full ctx block (project initialized by an earlier ctx) | Left alone by default, so the old block and the new `AGENTS.md` coexist: the instructions load twice but stay consistent. `ctx init --reset --merge`, the documented upgrade step, replaces the block with the bridge. | +| `CLAUDE.md` exists, hand-written, no markers | Prompt or `--merge`, then the bridge block is inserted after the H1; the user's text is untouched. Without the bridge Claude Code would ignore `AGENTS.md` because a `CLAUDE.md` is present. | +| `AGENTS.md` is a directory or unreadable | The read fails, the write fails, init prints the existing non-fatal warning labelled `AGENTS.md` and continues, as for `CLAUDE.md` today. | +| `--minimal` | Unchanged: both root files are infrastructure, not knowledge files, and are still written. | +| Re-running `ctx init` | Idempotent: both files report "ctx content exists". | + +### Validation Rules + +None beyond the existing marker handling: a template must contain both +markers or `merge.UpdateMarkedSection` returns `TemplateMissingMarkers`. +The asset tests guard the markers and the bootstrap line in both +templates. + +### Error Handling + +| Error condition | User-facing message | Recovery | +|-----------------|---------------------|----------| +| Embedded template unreadable | existing `ReadTemplate` error as a non-fatal warning labelled `AGENTS.md` | rebuild the binary | +| `AGENTS.md` write fails | existing `FileWrite` error as a non-fatal warning | fix permissions, re-run `ctx init` | +| Merge declined at the prompt | "skipped AGENTS.md" | re-run with `--merge` | + +## Interface + +### CLI + +``` +ctx init [--merge] [--reset] +``` + +No new flags. `--merge` also auto-merges into an existing `AGENTS.md`; +`--reset` also refreshes its ctx block. The `--merge` help text and +the init long description name both files. + +## Implementation + +### Files to Create/Modify + +| File | Change | +|------|--------| +| `internal/config/marker/marker.go` | add `AgentsEnd = ""` | +| `internal/cli/initialize/core/agents/{agents.go,doc.go}` | new: `HandleMd(cmd, force, autoMerge)` for `AGENTS.md`; mirrors `core/claude.HandleMd` with `agent.AgentsMd()`, `cfgHook.FileAgentsMd`, `marker.AgentsStart`/`AgentsEnd` | +| `internal/cli/initialize/cmd/root/run.go` | call `coreAgents.HandleMd` before `coreClaude.HandleMd` | +| `internal/cli/initialize/core/merge/create.go`, `section.go` | `OrCreate` inserts only the marker-bounded block into an existing file (the template's own `# Project Context` title used to come along as a second H1); the block extraction moves to a `markedSection` helper in its own file, shared with `UpdateMarkedSection` | +| `internal/assets/integrations/agents.md` | tool-neutral version of the CLAUDE.md content between `ctx:agents` markers | +| `internal/assets/claude/CLAUDE.md` | bridge: comment plus `@AGENTS.md` inside the `ctx:context` markers | +| `internal/cli/initialize/core/claude/doc.go`, `core/doc.go`, `cmd/root/doc.go`, `cmd/root/cmd.go` | doc comments | +| `internal/assets/commands/commands.yaml`, `flags.yaml` | init long help and `--merge` text | +| `internal/assets/README.md` | directory map rows | +| `docs/cli/init-status.md`, `docs/cli/setup.md`, `docs/operations/migration.md`, `docs/operations/integrations.md`, `docs/operations/upgrading.md`, `docs/operations/runbooks/breaking-migration.md`, `docs/home/common-workflows.md`, `docs/home/configuration.md` | describe `AGENTS.md` plus the bridge | +| `.context/DECISIONS.md` | decision superseding the 2026-01-25 entry | + +### Helpers to Reuse + +`merge.OrCreate` and `merge.UpdateMarkedSection` (create, prompt, +backup, H1 insertion, `--reset`), `agent.AgentsMd()` (template +reader), `agents.Deploy` (setup path, untouched), +`write/initialize.Created`. + +## Testing + +- `internal/cli/initialize/init_test.go`: a fresh init creates + `AGENTS.md` with both markers and the bootstrap line, and `CLAUDE.md` + with the ctx markers, an `@AGENTS.md` line and no bootstrap text; + `--merge` into a hand-written `AGENTS.md` inserts after the H1 and + keeps the content; an `AGENTS.md` with an existing ctx block is left + alone. The existing CLAUDE.md merge tests keep passing against the + bridge, and the merge tests for both files assert that the + template heading does not leak into the merged file. +- `internal/assets/project_test.go`: both templates carry their + markers; the CLAUDE.md template has the import and not the + instructions. +- `internal/cli/setup/core/{agents,codex,pi}` tests: unchanged, and + pass against the new template because they key on the marker only. + +## Non-Goals + +- Changing the `ctx setup agents|codex|opencode|pi` deployment logic. +- Flipping the ctx repository's own root `CLAUDE.md` / `AGENTS.md` + (follow-up). +- Auto-migrating an old full CLAUDE.md block without `--reset`, or + printing a hint about it (follow-up). +- Nested or per-directory `AGENTS.md` files. +- Copilot's `.github/copilot-instructions.md` and the other per-tool + files `ctx setup` writes.