RFD 0002: AGENTS.md primary, tool-specific files via translator plugins¶
Summary¶
The v2 framework writes AGENTS.md as the canonical project / per-folder agent-instruction file. Tool-specific variants (CLAUDE.md for Claude Code, .cursor/rules/*.md for Cursor, .codex/ for Codex, etc.) are produced by translator plugins the user installs on demand. The framework binary has zero references to CLAUDE.md or .claude/ — enforced by CI.
Translator plugins are symmetric: claude-translator, codex-translator, cursor-translator, continue-translator. None is privileged. v2.0 ships claude-translator and codex-translator ([[0007|RFD 0007]]); other tools' translators are community plugins.
The default agent = "claude" in samuel.toml is a default, not lock-in — users change it without reinstalling anything, and the framework treats Claude no differently than any other agent.
Problem statement¶
v1 writes both CLAUDE.md and AGENTS.md with identical content as a mirror. The duplication is a defensive cross-tool gesture from a time when Claude Code was the assumed-primary tool and AGENTS.md was the just-added cross-tool format ([[../../wiki/entities/sync-claude-md]]).
In 2026, the agents.md standard has matured. Cursor, Codex, Claude Code, GitHub Copilot, Continue, and Aider all read AGENTS.md (or are converging on it). Writing two files is no longer defensive — it's redundant for users of the standard, and presumptuous for users whose tool doesn't read CLAUDE.md.
More importantly: v2 positions as "Rails for coding assistants" plural ([[../../wiki/synthesis/positioning-rails-for-coding-assistants]]). Hardcoding Claude Code's filename as primary undermines the positioning. A user who installs Samuel because it's tool-agnostic should not get a CLAUDE.md by default.
Requirements¶
- The framework writes one canonical agent-instruction file: AGENTS.md (root + per-folder).
- Tool-specific files (CLAUDE.md, Cursor rules, Codex configs) are written by translator plugins that the user installs.
- No translator plugin is privileged in the framework — all are equal-status plugins resolved through the standard plugin loader ([[0001|RFD 0001]]).
- A user who installs no translator plugin gets a fully functional, tool-agnostic project layout.
- A user with multiple tools (Claude + Codex) installs both translators; each emits its tool's files.
- The agnostic invariant is enforceable in CI — grep for
"CLAUDE.md"literals or".claude/"paths ininternal/fails the build.
Constraints¶
- agents.md is a young standard. v2 follows it as-is at v2.0 launch but accepts the spec may evolve.
- v1 users have existing
CLAUDE.mdfiles. v2 must coexist gracefully (detect, warn, don't clobber). - Translator plugins are WASM-tier ([[0001|RFD 0001]]) — small, sandboxed, no host runtime needed.
Background¶
Where v1 sits¶
v1's sync.go ([[../../wiki/entities/sync-claude-md]]) emits both CLAUDE.md and AGENTS.md at every per-folder write. The bytes are identical. The autogen marker (<!-- Auto-generated by Samuel) is in both. User-customization preservation works against both.
v1's template template/CLAUDE.md has one line distinguishing it from template/AGENTS.md (different "this is the primary" callout); the rest is byte-identical.
Effectively: v1 already supports AGENTS.md. It just doesn't commit to it as primary.
The agents.md standard ecosystem¶
By mid-2026, AGENTS.md is the de facto cross-tool standard:
- Claude Code: reads
AGENTS.mdandCLAUDE.md(the latter as backward compat). - Cursor: reads
AGENTS.mdplus.cursor/rules/*.mdfor finer-grained scoping. - Codex: reads
AGENTS.md; some Codex setups also use.codex/config.toml. - GitHub Copilot: reads
AGENTS.md(recent addition; was.github/copilot-instructions.mdoriginally). - Continue: reads
AGENTS.mdplus.continue/config. - Aider: reads
AGENTS.mdplus.aider.conf.yml.
Writing AGENTS.md is the highest-coverage single file. Tool-specific files are additive value when the user has that specific tool.
What translator plugins do¶
A translator plugin's job: read the AGENTS.md the framework wrote, emit the tool-specific equivalent. Same content, tool-specific format and location.
claude-translator is mostly trivial — Claude Code reads AGENTS.md natively in 2026. The translator's value is:
- Emit
CLAUDE.mdas a verbatim copy for users on older Claude Code versions. - Install
.claude/settings.jsonwith PreToolUse hook scaffolding ([[../../wiki/concepts/claude-code-hooks]]). - Emit per-folder CLAUDE.md mirrors when AGENTS.md is per-folder.
codex-translator is similar — Codex aligns with AGENTS.md too. The plugin emits .codex/config.toml if the user's Codex setup uses one. Minimal work; the value is in the symmetry with claude-translator (proving the agnostic story).
Translator plugins for tools that don't read AGENTS.md (a hypothetical 2026 holdout) do more work: parse AGENTS.md, emit the tool-specific format under the tool's filesystem layout.
Options considered¶
Option A: AGENTS.md primary + translator plugins (chosen)¶
Framework writes AGENTS.md only. Tool-specific files come from translator plugins.
Pros: - Single source of truth — content lives in AGENTS.md, translators are projections. - Agnostic by construction (CI-enforced via [[../../wiki/concepts/agnostic-by-design]]). - Symmetric across tools — claude-translator and codex-translator are peer plugins, not "Claude has special status." - Scales to any future agent — new tools get a new translator plugin without framework changes. - Aligns with the agents.md standard's stated goal of cross-tool unification. - Users who don't install any translator still have a fully functional agent-instruction setup (every modern coding assistant reads AGENTS.md).
Cons: - v1 users who depended on CLAUDE.md being there by default lose that. Mitigated by claude-translator being trivially installable. - Two files (AGENTS.md + CLAUDE.md) for Claude users with translator installed — temporary redundancy until Claude Code drops CLAUDE.md. - Translator plugin authors carry the per-tool format knowledge — Cursor's rules dir, Codex's config, etc.
Effort: Low for the framework (drop one file write from sync.go). Medium for the two translator plugins ([[0007|RFD 0007]]).
Option B: CLAUDE.md primary (v1 status quo)¶
Framework writes CLAUDE.md as primary, AGENTS.md as compat mirror. Same as v1.
Pros: - Zero migration for v1 users. - "It works with Claude Code" out of the box.
Cons: - Reverses the agnostic positioning. v2 ships claiming "Rails for coding assistants" plural while privileging one in the default output. - Codex users get a CLAUDE.md they don't read, must rename or symlink manually. - Cursor users get a CLAUDE.md they don't read. - Doesn't scale — adding "and write Cursor rules too" requires framework changes.
Effort: Zero. Lowest in code but highest in narrative inconsistency.
Option C: Both written by the framework (v1's actual behavior — duplicate)¶
Framework writes AGENTS.md and CLAUDE.md identically every sync, regardless of tool.
Pros: - Most users covered by default. - v1 users' muscle memory works.
Cons: - Same duplication v1 has. Two identical files on disk per folder. Twice the autogen-marker management. Twice the file-watcher noise. - Still doesn't address Cursor, Codex's .codex/, etc. - Doesn't make the framework agnostic — it embeds the assumption that "Claude Code is one of the two tools to support by default."
Effort: Zero (it's v1's behavior). But the agnostic invariant is broken.
Option D: Tool-detected — framework auto-picks based on installed tools¶
samuel init runs which claude / which cursor / which codex. If found, writes the matching file format(s).
Pros: - Zero user intervention. The right files for the user's actual tooling appear.
Cons: - Detection is fragile — users uninstall tools, swap between machines, run in CI containers without their tools installed. - Framework code now knows about every supported tool's CLI name. Bloats with each new tool. - Re-introduces the per-tool branching that translator plugins are designed to remove. - Detection-time logic differs from sync-time logic (a tool installed later doesn't trigger a file emission).
Effort: Medium. Wrong design — tool detection is plugin-installation territory.
Option E: User-selected at init¶
samuel init --tool claude picks Claude as the format. --tool codex picks Codex. Sets a flag in samuel.toml.
Pros: - Explicit, predictable. - No detection.
Cons: - Forces the user to pick one tool. What if they use both? - Couples the framework to the list of supported tools — a flag enum like --tool {claude,cursor,codex,...} doesn't scale. - Migrating between tools requires re-init.
Effort: Medium. Worse UX than installing a translator plugin.
Decision¶
Adopt Option A: AGENTS.md primary, tool-specific files via translator plugins.
The decision rests on four judgments:
-
AGENTS.md is the right canonical file in 2026. Every modern coding assistant reads it. Writing it alone covers the broadest user base. Backward-compat (older Claude Code without AGENTS.md support) is the translator plugin's job, not the framework's.
-
Translator plugins scale; framework branching doesn't. Option D and Option E couple the framework to specific tools' filenames. Option A defers tool-specific logic to plugins; new tools get new plugins, framework unchanged.
-
The agnostic invariant is enforceable. Option A makes "framework doesn't write CLAUDE.md" a property checkable by
grep -r '"CLAUDE\.md"' internal/in CI. Easier to enforce than nuanced "framework writes CLAUDE.md but treats it as one of two equivalent outputs" semantics from Option C. -
Plugin authors mirror v2's positioning. Plugin authors writing translators for other tools (Cursor, Continue, Aider) follow the same pattern: read AGENTS.md, emit tool-specific. The framework's choice imprints on every plugin author who follows.
Implementation plan¶
Phase 1 — framework writes AGENTS.md only (PRD 0002, week 3)¶
internal/sync/ produces only AGENTS.md (root + per-folder). Drop v1's CLAUDE.md emission.
// internal/sync/write.go
func WriteFolderContext(dir string, content string, opts WriteOptions) error {
return writeWithAutogenMarker(filepath.Join(dir, "AGENTS.md"), content, opts)
// No CLAUDE.md write. That's the claude-translator plugin's job.
}
The autogen marker convention carries forward unchanged:
<!-- Auto-generated by Samuel. Customize with folder-specific instructions. -->
<!-- AI agents load this file when working in this directory. -->
User-customized files (missing the marker) are skipped unless --force passed.
Phase 2 — sync hooks expose AGENTS.md to translator plugins (PRD 0002, week 3)¶
The sync system fires hooks per [[0004|RFD 0004]]:
sync.before → plugins can pre-process (e.g., add language-detection metadata)
sync.analyze-folder → default: heuristic scan; plugin can replace per folder
sync.write-agents-md → default: write AGENTS.md
sync.after → plugin slot: translators run here
Translator plugins register handlers for sync.after. The framework passes the list of AGENTS.md paths just written; the plugin emits its tool-specific files alongside.
WASM plugin API (per [[0001|RFD 0001]]):
host functions exposed to translator plugins:
samuel.fs.read(path) → reads AGENTS.md content
samuel.fs.write(path, bytes) → writes tool-specific file (capability-gated)
samuel.log(level, message) → emits warnings
Phase 3 — agnostic invariant CI check (PRD 0001, week 2)¶
.github/workflows/agnostic-check.yml:
name: Agnostic-by-design invariant
on: [pull_request, push]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: No CLAUDE.md literal in framework code
run: |
if grep -rn '"CLAUDE\.md"' internal/ cmd/ template/; then
echo "::error::Framework references CLAUDE.md by literal."
echo "::error::Tool-specific filenames belong in translator plugins, not framework."
echo "::error::See docs/rfd/0002.md for the agnostic-by-design invariant."
exit 1
fi
- name: No .claude/ path in framework code
run: |
if grep -rn '"\.claude/' internal/ cmd/ template/; then
echo "::error::Framework writes to .claude/ path."
echo "::error::Move this to the claude-translator plugin."
exit 1
fi
- run: echo "✓ Agnostic invariant holds"
Plugins are exempt — they live in their own repos, not in internal/.
Phase 4 — translator plugins (PRD 0005)¶
Per [[0007|RFD 0007]]:
samuel-claude-translator: WASM plugin. Hook handlers forsync.after. Reads each AGENTS.md the framework wrote, emits sibling CLAUDE.md. Installs.claude/settings.jsonwith PreToolUse hook stubs at install time.samuel-codex-translator: WASM plugin. Hook handlers forsync.after. Emits.codex/files matching Codex's 2026 conventions.
Both ship at v2.0 launch. Future translators (cursor-translator, continue-translator) are community plugins.
Phase 5 — v1 coexistence handling (PRD 0002, week 5)¶
samuel init in a directory that already has a v1 CLAUDE.md:
- Detect via the v1 autogen marker (
<!-- Auto-generated by Samuel— same marker). - Warn: "v1 CLAUDE.md detected at
. v2 manages AGENTS.md instead. The existing CLAUDE.md is untouched." - Do not delete the file.
- If
claude-translatoris later installed, the plugin'sinit.afterhook adopts the existing CLAUDE.md (replaces with translator-managed copy on next sync).
samuel doctor reports stale v1 files informational.
Acceptance criteria¶
-
samuel initin a clean project writesAGENTS.mdonly — noCLAUDE.md. -
samuel syncregeneratesAGENTS.mdfiles; does not writeCLAUDE.md. -
grep -rn '"CLAUDE\.md"' internal/returns no matches (CI enforced). -
grep -rn '"\.claude/' internal/returns no matches (CI enforced). - Installing
claude-translatorcauses subsequentsamuel syncruns to also emitCLAUDE.mdat each AGENTS.md sibling. - Uninstalling
claude-translatorstops the CLAUDE.md emission (plugin'sUninstallreverses). - Installing
codex-translatoremits Codex-specific files; uninstall reverses. - Installing both translators produces both sets of files without interference.
- Existing v1
CLAUDE.mdfiles are detected at init, warned about, not modified by the framework. -
samuel doctorreports unmanaged v1CLAUDE.mdfiles informationally. - AGENTS.md template at ≤150 lines passes the
agents-md-check.ymlCI gate.
Compatibility and migration¶
- v1 users with
CLAUDE.md: file stays untouched. After installingclaude-translator, the next sync overwrites with translator-managed content (autogen marker preserved). Users who hand-edited their v1 CLAUDE.md without the marker have the file preserved indefinitely. - v1 projects with
AGENTS.md: same content as CLAUDE.md in v1. v2's sync continues managing it. - v1
.claude/skills/content: untouched. v2 uses.samuel/plugins/for its own plugins. v1 skill content is left as a historical artifact until users manually clean it up.
The migration notice in docs/getting-started/migration-v1.md explains.
Risks¶
| Risk | Likelihood | Mitigation |
|---|---|---|
| Users expect CLAUDE.md and don't see it | High | Migration notice covers this. claude-translator install is one command. samuel doctor could detect Claude on PATH and suggest the translator install. |
| Translator plugins are essential but mark as "optional" — discoverability problem | High | samuel doctor detects installed coding assistants and suggests matching translators. v2 docs prominently feature the translator pattern. |
| Translator plugin maintenance lags Claude / Codex format evolution | Medium | Plugin repos have their own release cadence. Bugfixes per-plugin without framework version bumps. |
| Two-write race condition: framework writes AGENTS.md, translator writes CLAUDE.md concurrently | Low | Hooks fire serially after the framework's writes complete. Sync is single-threaded per project. |
WASM translator plugin can't fully emit tool-specific files (e.g., complex .cursor/rules/ structure) | Medium | WASM filesystem capability scoped to allow recursive writes within the target dir. If insufficient, escalate to OCI tier for that specific translator. |
| The agents.md standard changes mid-2026 and our v2.0 output goes stale | Medium | Framework writes are aligned with the standard; if the standard changes, update the AGENTS.md template (Milestone 6+ patch release). Translator plugins absorb their own tool's format changes independently. |
samuel init detection of existing v1 CLAUDE.md misses edge cases | Low | Conservative approach: warn for any CLAUDE.md regardless of marker. Never modify without explicit confirmation. |
Resolved decisions (2026-05-12)¶
-
samuel doctorauto-suggests translator plugins: yes. Detectsclaude/cursor/codexon PATH; prints "you may wantsamuel install <tool>-translator." Helpful nudge without privileging any tool. -
Pre-existing user-managed
.cursor/directories (and equivalents): respect via autogen-marker convention. Same rule as everywhere else in the framework. -
Per-tool feature disparity: each translator plugin does what its target tool supports, no more.
claude-translatorinstalls.claude/settings.jsonhooks;codex-translatordoes whatever Codex's 2026 conventions are; future translators map to their own tools' surfaces. Document per-plugin. -
Translator plugin update flow:
samuel update <plugin>runs the plugin'sInstallagain, which re-emits tool-specific files. Already in plugin loader behavior — no special handling needed. -
AGENTS.md template content for v2 (final): keep these sections (rendered ≤150 lines):
- 4D methodology (~30 lines)
- Boundaries / Do Not Touch (~15 lines)
- Quick Reference (task classification, autonomous mode quick-ref, ~20 lines)
<!-- SAMUEL_PLUGINS_START/END -->block (auto-populated from installed plugins)<!-- SAMUEL_GUARDRAILS_START/END -->block (fromsamuel.toml [guardrails])- Project Context fillable (~10 lines)
Drop (moved to mkdocs site): full skill links table, language guide links, framework guide links, anti-patterns, when-stuck, embedded changelog.
- Per-folder translation: yes — translators emit per-folder equivalents wherever the framework emits per-folder AGENTS.md. Symmetric pattern.
Outcome¶
To be filled in post-v2.0 launch. Expected outcomes:
- ~70% of v2 users install
claude-translator(Claude Code's user base is large in 2026). - ~20% install
codex-translator(smaller but growing). - ~5% of users install neither — they're on AGENTS.md-native tools (Cursor, Continue) or are tool-agnostic users.
- No user reports "Samuel forced me to use Claude" — confirms the agnostic invariant landed.
- Community publishes at least one third-party translator (most likely
cursor-translatororaider-translator) within six months.
Related artifacts¶
- [[0001|RFD 0001]] — three plugin tiers (translator plugins use WASM tier)
- [[0004|RFD 0004]] — methodology hooks (
sync.afterhook fires translators) - [[0007|RFD 0007]] — plugin migration (creates claude-translator + codex-translator)
- [[../../wiki/concepts/agents-md-primary]] — wiki concept this RFD ports
- [[../../wiki/concepts/agnostic-by-design]] — invariant this RFD upholds
- [[../../wiki/concepts/claude-code-hooks]] — Claude-specific hook details for claude-translator
- [[../../wiki/entities/sync-claude-md]] — v1 sync system being ported
- agents.md standard — the cross-tool spec