Skip to content

One AI Coding Config for Claude Code, Cursor, Copilot, and Codex

A practical repo layout that keeps Claude Code, Codex, Copilot, Cursor, and OpenCode reading the same rules, with honest notes on where portability breaks.

Ayhan Sipahi Ayhan Sipahi

Teams rarely converge on a single AI coding tool; a repo that only works with one of Claude Code, Codex, Copilot, Cursor, or OpenCode loses every contributor who picked a different tool. The layout below keeps one file authoritative: AGENTS.md as the single source of truth, symlinks or @-imports for every other tool’s expected filename, and MCP for cross-tool tooling. It breaks in three places: skills, slash commands, and Windows symlink traps.

The Problem#

A team adopts Claude Code, then a new hire prefers Cursor. The CLAUDE.md at the repo root sits quiet while Cursor reads .cursor/rules/, so someone hand-copies the rules. Later the same rule lands in AGENTS.md with slightly different wording. Three files drift, and nobody owns the sync.

Add a few more pains that show up in real repos:

  • A monorepo with per-package conventions. Claude Code walks up the tree and concatenates nested CLAUDE.md files. OpenCode reads the nearest AGENTS.md. Copilot only reads .github/copilot-instructions.md at the root. Same code, different behaviour.
  • MCP servers (GitHub, database, filesystem) must live in .mcp.json (project root, Claude Code), .codex/config.toml, .vscode/mcp.json, and .cursor/mcp.json. Same URL, same auth, four formats.
  • Skills and slash commands live in .claude/skills/ and .claude/commands/. They do not automatically port to Codex, Copilot, or Cursor.
  • Windows contributors clone the repo. The symlinks arrive as plain text files reading AGENTS.md. Their AI tool silently loads nothing.

What Each Tool Reads (April 2026)#

Before choosing a layout, know what each tool actually looks for on startup. I cross-checked each entry with the tool’s April 2026 docs (links at the end).

ToolCanonical fileFallbackTool directory
OpenAI Codex CLIAGENTS.mdnone.codex/config.toml
GitHub Copilot.github/copilot-instructions.md + AGENTS.md (merged)none.github/instructions/*.instructions.md
Claude CodeCLAUDE.mdAGENTS.md (if CLAUDE.md is absent).claude/{commands,skills} + .mcp.json at project root
OpenCode (sst)AGENTS.mdCLAUDE.md (legacy compat)~/.config/opencode/*
Cursor.cursor/rules/*.mdclegacy .cursorrules (deprecated).cursor/
Gemini CLIGEMINI.md (hierarchical, walks up to .git root)none~/.gemini/
AiderCONVENTIONS.md or AGENTS.md (via config)none.aider.conf.yml

Note

Anthropic’s April 2026 memory docs describe AGENTS.md as a fallback for when CLAUDE.md is absent. If both files exist, Claude Code reads CLAUDE.md and ignores AGENTS.md. Community guides phrase this differently, so treat the official docs as the tiebreaker.

The Canonical Layout#

Pick AGENTS.md. It is the widest-adopted name in 2026 and the one OpenAI, Copilot, OpenCode, Cursor, and others treat as canonical.

repo-root/
  AGENTS.md  # single source of truth, committed
  CLAUDE.md  # symlink -> AGENTS.md
  .mcp.json  # MCP servers (Claude Code, project root)
  .github/
    copilot-instructions.md  # symlink -> ../AGENTS.md
    instructions/  # path-specific rules (Copilot only)
  .cursor/
    rules/
      main.mdc  # symlink -> ../../AGENTS.md or thin shim
  .claude/
    commands/  # Claude-specific slash commands
    skills/  # Claude-specific skills
  .codex/
    config.toml  # MCP servers (Codex format)
  .vscode/
    mcp.json  # MCP servers (Copilot format)
  GEMINI.md  # symlink -> AGENTS.md (optional)

The unix-native approach. Git tracks symlinks, and every editor and CLI tool follows them.

ln -sfn AGENTS.md CLAUDE.md
ln -sfn ../AGENTS.md .github/copilot-instructions.md
mkdir -p .cursor/rules && ln -sfn ../../AGENTS.md .cursor/rules/main.mdc

For Windows teammates, add git config --global core.symlinks true before cloning. Without that flag, git checks out the symlink as a text file whose only content is the string AGENTS.md. The tool then reads that single line and thinks it is the whole project rulebook.

Option B: @-imports#

Claude Code and OpenCode both inline referenced files when they see an @ prefix. This is the cleanest option if you want a shared base plus a few tool-specific lines.

# CLAUDE.md
@AGENTS.md

## Claude-only additions
- Use the `.claude/hooks` scripts before running tests.

Copilot and Cursor do not understand @-imports; they read the literal text, so route those two tools through symlinks or pointer files instead.

Option C: Pointer files#

The crudest option, but the most portable: LLM adherence is probabilistic, the pointer gets ignored some of the time, and nothing tells you when. Fine for a solo repo; not fine for a compliance-sensitive one. A one-line CLAUDE.md:

READ AGENTS.md FIRST. All project rules live there.

Picking a Strategy#

No, Claude Code only

Yes or expected

No

Yes

No

Yes

New or existing repo?

Multiple AI tools in use?

CLAUDE.md at root, skip symlinks

Team on Windows?

AGENTS.md + symlinks

AGENTS.md + @-imports or pointer files

Need MCP servers?

Done

Generate per-tool MCP configs from a single YAML

When symlinks win: pure unix team, no Windows contributors, you want zero duplication.

When @-imports win: you want tool-specific additions on top of a shared base, and your team is on Claude Code or OpenCode.

When neither is worth it: single tool today, no plans to switch. CLAUDE.md or AGENTS.md alone is fine.

The MCP Layer#

MCP (Model Context Protocol) is the only real cross-tool contract at the tools layer. Rules stay outside it. Every serious assistant reads MCP server definitions, but each reads from a different path and format:

  • Claude Code: .mcp.json at project root (not .claude/mcp.json, a common trap)
  • Codex CLI: .codex/config.toml per project, or ~/.codex/config.toml globally; most teams use the global file
  • Copilot (VS Code): .vscode/mcp.json (uses the servers key, not mcpServers)
  • Cursor: .cursor/mcp.json (uses mcpServers)

Four files. Same URL, same auth token, four formats. Community tooling (chezmoi recipes, YAML-to-many generators posted to Hacker News in early 2026) can generate all four from one source. For now, budget for four config files if your repo ships real MCP servers.

Automating the Setup (projen and friends)#

Three symlinks and a .mcp.json take an hour to set up. A year later, someone renames a file, Windows symlinks break, and the four MCP configs drift. Automation makes CI the enforcement layer instead of goodwill.

The options split by what your team already uses.

projen: TypeScript synth#

projen (opens in new tab) treats project files as code. You subclass a project, declare the files you want generated, and npx projen regenerates them. Any drift fails the synth check.

// .projenrc.ts
import { Project, TextFile, JsonFile, Component } from 'projen';
import { readFileSync, existsSync, unlinkSync, symlinkSync } from 'fs';
import { dirname, relative } from 'path';

class AgentSymlinks extends Component {
  constructor(project: Project, private aliases: string[], private target: string) {
    super(project);
  }
  synthesize() {
    for (const alias of this.aliases) {
      if (existsSync(alias)) unlinkSync(alias);
      symlinkSync(relative(dirname(alias), this.target), alias);
    }
  }
}

const project = new Project({ name: 'my-repo' });

new TextFile(project, 'AGENTS.md', {
  lines: readFileSync('.rules/agents-source.md', 'utf8').split('\n'),
});

new AgentSymlinks(project, [
  'CLAUDE.md',
  '.github/copilot-instructions.md',
  '.cursor/rules/main.mdc',
], 'AGENTS.md');

// One YAML source, four MCP config formats
new JsonFile(project, '.mcp.json',  { obj: mcpFrom('mcp-servers.yml', 'claude') });
new JsonFile(project, '.vscode/mcp.json', { obj: mcpFrom('mcp-servers.yml', 'vscode') });
new JsonFile(project, '.cursor/mcp.json', { obj: mcpFrom('mcp-servers.yml', 'cursor') });

project.synth();

Trade-off: projen is AWS CDK territory. If your team is not already synthesizing package.json, tsconfig, and GitHub Actions with projen, adopting it just for AI config is over-budget. The win comes from synthesizing all of your config in one place; the AI files are a small slice of that.

chezmoi for user-scoped config#

chezmoi (opens in new tab) is a dotfile manager with Go text/template support. When most of your AI config lives in $HOME (user-scoped Claude, Cursor, Codex globals), chezmoi fits better than per-repo synth. One .chezmoitemplates/AGENTS.md, then chezmoi apply renders it to every tool’s expected path. Per-developer overrides use the same template system.

Purpose-built and plain scripts#

A small dedicated CLI or a thirty-line shell script plus a pre-commit hook is enough for a single repo with no other automation. Do not adopt a framework you do not already use elsewhere.

Picking the tool:

  • Already on projen (AWS CDK shop)? Add an AgentConfig component. Zero new tooling.
  • Already on chezmoi for dotfiles? Add .chezmoitemplates/AGENTS.md. Zero new tooling.
  • Neither? A shell script and a CI check clears the bar. Upgrade later if the pain shows up.

The Lowest-Common-Denominator Wall#

Not everything unifies. A few things stay tool-specific, and pretending otherwise misleads your team.

Slash commands. .claude/commands/ holds Claude-specific slash commands. Codex, Copilot, and Cursor do not read them. If your team depends on /deploy as a Claude slash command, that workflow does not port.

Skills. Anthropic published the SKILL.md specification as an open standard in December 2025. By April 2026, adoption is broad: VS Code ships native Skills support, GitHub Copilot’s version is still labelled experimental, and Cursor, Codex CLI, OpenCode, and Goose read the same SKILL.md format. A skill written for Claude Code typically loads in the others without changes. What still differs is where each tool looks: .claude/skills/ is Claude-specific, and Copilot, Cursor, and the rest each define their own discovery paths, so the format ports but the convention does not yet.

Path-specific instructions. Copilot supports .github/instructions/*.instructions.md with glob patterns. Cursor has globs: frontmatter in .mdc files. Claude has its own path scoping. These do not port. Keep the root AGENTS.md generic. Let each tool use its own path-scoping mechanism.

When Not to Do This#

Not every repo needs any of this. If one of these describes your situation, pick a single tool and skip the layout.

Single-vendor enterprise shops skip all of this. Procurement chose Copilot, security signed off on one vendor, and the onboarding docs point at one install script. Symlinks and @-imports buy nothing here except maintenance overhead: a single copilot-instructions.md file is the whole job.

Regulated environments land on the same answer for a different reason. SOC 2, ISO 27001, and HIPAA auditors want a single-vendor trail for AI-assisted code generation: who saw what, when, with what system prompt. Several AI vendors writing code in the same repo complicate that trail, so picking one vendor and documenting it is the whole exercise.

Solo repos and first-time adoptions share the same logic. A single developer has no drift problem yet: CLAUDE.md or AGENTS.md alone is enough, and there is nothing to build until a second contributor shows up. A team on its first AI coding tool is in the same spot: do not build infrastructure for a fleet you do not have. Symlinks become interesting the day someone opens Cursor while the rest of the team is on Claude Code.

Where the layout earns its keep: open source repos (maintainers cannot dictate contributor tooling), agencies and consultancies (each client repo meets a different tool stack), mid-stage teams where adoption grew organically and standardization would now be retroactive political work, and any team that consciously values tool-choice autonomy.

Where Drift Creeps Back In#

Checking in personal overrides. CLAUDE.local.md and .claude/settings.local.json are for personal overrides. Add them to .gitignore on day one. A committed CLAUDE.local.md with someone’s private experiments will confuse every teammate.

Assuming Copilot reads AGENTS.md alone. Copilot merges AGENTS.md and .github/copilot-instructions.md. If you put the full ruleset only in AGENTS.md and leave copilot-instructions.md empty, Copilot’s web PR review may miss context. The symlink fix solves this.

Nested AGENTS.md in monorepos. OpenCode and Copilot read the nearest one. Claude Code concatenates up the tree. Same repo, different rule composition. Document the nesting behaviour you rely on.

Windows symlinks without core.symlinks=true. The failure mode is silent: the file appears, it reads AGENTS.md as plain text, and the AI tool treats that single line as the whole rulebook. Fix: add a CI check (test -L CLAUDE.md) that fails if any symlink has been replaced with a text file.

Drift after month three. Without a check, someone will “fix” a broken symlink on Windows by replacing it with a real copy of AGENTS.md. Two weeks later, the copy has drifted. Add a pre-commit hook or a CI job that enforces the symlink.

Metrics Worth Tracking#

Not every team needs dashboards here, but a few simple checks catch most drift:

  • Number of instruction files that must change per rule update. Target: one.
  • Percent of developers whose tool of choice reads the canonical file on first clone. Target: 100 percent.
  • Drift incidents per quarter. Cases where tool A and tool B behaved differently on the same repo because they read different files.
  • Time to onboard a new AI tool to the repo. Target: under thirty minutes (add symlink, add MCP config, done).

Principles from the Field#

The mechanics (symlinks, @-imports, per-tool directories) solve the storage problem. Practitioners who work with coding agents every day have stronger views on the content problem: what belongs in AGENTS.md and how to keep it from bloating.

Addy Osmani argues for spec-before-code discipline (opens in new tab): AGENTS.md holds standing conventions, while a per-feature spec.md carries intent. He cites the “Curse of Instructions” finding; as you pile requirements into one prompt, per-rule adherence drops, and even frontier models struggle with dozens of simultaneous rules. Practical cap: keep AGENTS.md under ~200 lines and push path-specific rules into .github/instructions/*.instructions.md or .cursor/rules/*.mdc.

Simon Willison frames the job as “hoarding things you know how to do” (opens in new tab): a pattern library your agent recombines, not a rule book it obeys. AGENTS.md earns its keep by pointing at known patterns; re-specifying the obvious only adds lines.

Geoffrey Huntley (Sourcegraph Amp (opens in new tab)) hits two notes: too many active MCP tools pollute the context window, and tool descriptions are not standardized. His fix: deploy tools to the stages that need them; Jira MCP during planning, GitHub MCP during review, off otherwise. The same discipline applies to rules: the always-loaded root file stays small; path-scoped rules carry the weight.

ruler (intellectronica/ruler (opens in new tab)) is a small CLI that applies one rules source to every agent’s native file. Fits alongside projen and chezmoi when you want something purpose-built and lighter than either.

One security note. Rules files are readable by humans and the agent, but the agent reads every character. The “Rules File Backdoor” technique (SC Media, 2026 (opens in new tab)) injects invisible Unicode into rules files to steer the agent. Treat AGENTS.md like executable code; add a CI check for non-ASCII or non-printable characters in rules-file diffs.

Closing#

The layout is worth the setup as long as more than one AI tool needs to read the same rules. Drop it the moment a single vendor becomes policy, and keep the pieces that never port (slash commands, skills, path-specific instructions) scoped and documented per tool.

Teams still hand-copying rules between CLAUDE.md and .cursor/rules/main.mdc can close the gap in about an hour: pick AGENTS.md, add the three symlinks, and wire a CI check that fails on drift.

References#

Related posts