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.