A practical guide for making any repository work with any AI coding agent without vendor lock-in. Covers creating new repos agent-agnostic from day one (Part A) and transforming existing repos (Part B).
The Core Principle #
Every piece of agent configuration falls into exactly one of three categories:
| Category | Strategy | Examples |
|---|---|---|
| Portable (same format across agents) | Store once in .agents/, symlink to agent dirs |
AGENTS.md, SKILL.md files |
| Generated (same data, different format) | Canonical source in .agents/, sync script renders per-agent |
MCP config (JSON vs TOML) |
| Agent-specific (no cross-agent equivalent) | Leave in agent-native directory | .cursor/rules/*.mdc, .claude/settings.json |
If you remember nothing else: portable things get symlinks, generated things get a sync script, agent-specific things stay put.
Part A: New Repo Template #
A.1 Directory Layout
repo/
AGENTS.md # Universal instructions (AAIF standard)
CLAUDE.md # Pointer: "@AGENTS.md" + Claude-only overrides
.agents/
AGENTS.md # Self-management doc for this directory
skills/ # Shared skills (committed)
<skill-name>/
SKILL.md # agentskills.io spec format
references/ # Optional supporting docs
scripts/ # Optional automation
skills-local/ # Private skills (gitignored)
.gitkeep
mcp/
servers.json # Canonical MCP server definitions
scripts/
link-skills.sh # Symlink skills to agent-native dirs
sync-mcp.sh # Generate agent-native MCP configs
.claude/ # Generated + agent-specific
skills/<name> -> ../../.agents/skills/<name> # Symlinks
settings.json # Agent-specific (hooks, etc.)
.cursor/ # Generated + agent-specific
skills/<name> -> ../../.agents/skills/<name> # Symlinks
rules/ # Agent-specific (MDC format, not portable)
mcp.json # Generated from .agents/mcp/servers.json
.codex/
config.toml # Generated from .agents/mcp/servers.json
.mcp.json # Generated: Claude project-level MCP
A.2 Layer 1: Instructions (AGENTS.md)
What: Project context, conventions, routing, coding standards. Portable? Yes. AGENTS.md is the AAIF/Linux Foundation standard, read by 20+ agents including Codex, Cursor, Copilot, Gemini CLI, Devin, and more. Sync mechanism: None needed. Most agents read it natively.
Setup:
- Create
AGENTS.mdat repo root with project overview, directory structure, setup commands, coding standards, git conventions. - Create
CLAUDE.mdas a pointer:
@AGENTS.md
This repo keeps reusable agent assets in `.agents/` and syncs tool-specific
adapters with `.agents/scripts/`.
- For nested packages, create sub-
AGENTS.mdfiles (e.g.,src/package/AGENTS.md). Both Claude Code and Codex support hierarchical AGENTS.md — deeper files provide directory-scoped context.
Sources: AGENTS.md spec, AAIF / Linux Foundation
A.3 Layer 2: Skills (SKILL.md)
What: Reusable workflows agents can invoke — /rally, /learn, /deploy, etc.
Portable? Yes. The Agent Skills spec defines a universal SKILL.md format adopted by 16+ agents.
Canonical location: .agents/skills/<skill-name>/SKILL.md
Sync mechanism: Symlinks from agent-native dirs.
SKILL.md format (agentskills.io spec):
---
name: my-skill
description: What this skill does and when to use it.
---
Step-by-step instructions for the agent...
Required fields: name (lowercase, hyphens, matches directory name), description.
Keep SKILL.md under 500 lines. Use references/ for detailed docs.
The link script (.agents/scripts/link-skills.sh):
#!/usr/bin/env bash
set -euo pipefail
AGENTS_DIR="$(cd "$(dirname "$0")/.." && pwd)"
REPO_ROOT="$(cd "$AGENTS_DIR/.." && pwd)"
TARGETS=(".claude/skills" ".cursor/skills")
declare -A valid_skills
for target_dir in "${TARGETS[@]}"; do
mkdir -p "$REPO_ROOT/$target_dir"
for skill_dir in "$AGENTS_DIR"/skills/*/; do
[ -d "$skill_dir" ] || continue
name="$(basename "$skill_dir")"
valid_skills["$name"]=1
link_target="../../.agents/skills/$name"
link_path="$REPO_ROOT/$target_dir/$name"
if [ -L "$link_path" ]; then
[ "$(readlink "$link_path")" = "$link_target" ] && continue
rm "$link_path"
elif [ -e "$link_path" ]; then
echo "ERROR: $link_path exists but is not a symlink." >&2; exit 1
fi
ln -s "$link_target" "$link_path"
echo "Linked: $link_path -> $link_target"
done
for link in "$REPO_ROOT/$target_dir"/*/; do
[ -L "${link%/}" ] || continue
link="${link%/}"
target="$(readlink "$link")"
if [[ "$target" == *"/.agents/"* ]]; then
name="$(basename "$link")"
[ -z "${valid_skills[$name]+x}" ] && rm "$link" && echo "Pruned: $link"
fi
done
done
Installing third-party skills (Vercel skills CLI):
npx skills add <repo>@<skill-name> -y
The Vercel CLI natively understands the .agents/skills/ convention and creates symlinks to .claude/skills/ automatically. The link script is a safety net for manual skill creation.
Private skills: .agents/skills-local/ is gitignored. The link script creates symlinks for these too, but those symlinks are machine-local and should not be committed.
Sources: Agent Skills spec, Client implementation guide, npx skills, SSW Rules: symlink agents to claude
A.4 Layer 3: MCP Configuration
What: Model Context Protocol server definitions — which external tools the agent can call.
Portable? No. Same data, different format per agent.
Canonical location: .agents/mcp/servers.json
Sync mechanism: Generation script renders per-agent files.
Why generation, not symlinks:
| Agent | File | Format difference |
|---|---|---|
| Claude Code | .mcp.json |
JSON. URL servers need "type": "http" |
| Cursor | .cursor/mcp.json |
JSON. Direct copy, no type field needed |
| Codex | .codex/config.toml |
TOML. [mcp_servers.<name>] tables |
Canonical format (.agents/mcp/servers.json):
{
"figma": {
"type": "http",
"url": "https://mcp.figma.com/mcp"
},
"stacks": {
"type": "http",
"url": "https://stacksmcp.int.stackoverflow.net/mcp"
}
}
Codex special case: Codex does not auto-load repo-local .codex/config.toml. The sync script should support an install-codex command that merges a managed block into ~/.codex/config.toml with sentinel comments (# BEGIN <repo> managed MCP / # END <repo> managed MCP).
Sources: MCP specification, AAIF
A.5 Layer 4: Agent-Specific (Leave As-Is)
These have no cross-agent equivalent. Do not try to abstract them.
| File | Agent | Why not portable |
|---|---|---|
.cursor/rules/*.mdc |
Cursor | MDC format with glob scoping (globs: "**/*.py"), conditional (alwaysApply) — no equivalent elsewhere |
.claude/settings.json |
Claude Code | Hooks, metadata updaters — Claude-specific harness features |
.claude/settings.local.json |
Claude Code | Machine-local permissions — always gitignored |
.claude/agents/*.md |
Claude Code | Subagent definitions — Claude-specific feature |
If you want the same knowledge available in multiple agents, put it in AGENTS.md (universally read) rather than trying to sync between .cursor/rules/ and .claude/ formats.
A.6 Automation
Pre-commit hooks (.pre-commit-config.yaml):
- repo: local
hooks:
- id: link-agent-skills
name: "Sync .agents/skills symlinks"
entry: .agents/scripts/link-skills.sh
language: script
pass_filenames: false
files: ^\.agents/skills/|^\.claude/skills/|^\.cursor/skills/
- id: check-agent-mcp
name: "Verify MCP configs are current"
entry: .agents/scripts/sync-mcp.sh check
language: script
pass_filenames: false
files: ^\.agents/mcp/
A.7 .gitignore
.agents/skills-local/*
!.agents/skills-local/.gitkeep
.claude/settings.local.json
.claude/worktrees/
For whitelist repos (meta workspace pattern where * ignores everything):
!.agents/
!.agents/**
!.claude/
!.claude/**
!.codex/
!.codex/**
!.cursor/
!.cursor/**
!.mcp.json
!AGENTS.md
!CLAUDE.md
A.8 .agents/AGENTS.md Template
This is the self-management doc that lets any agent maintain the directory:
Agent-agnostic configuration following the [Agent Skills](https://agentskills.io)
open standard and [AGENTS.md](https://agents.md) conventions.
## Layout
- `skills/` — Shared skills (committed). Each is a directory with `SKILL.md`.
- `skills-local/` — Private skills (gitignored).
- `mcp/servers.json` — Canonical MCP server config.
- `scripts/` — Sync and validation scripts.
## Adding a skill
1. Create `.agents/skills/<name>/SKILL.md` with `name` + `description` frontmatter
2. Run `.agents/scripts/link-skills.sh`
3. Commit the skill directory and the new symlink
## Installing third-party skills
npx skills add <repo>@<skill> -y
## Adding an MCP server
1. Edit `.agents/mcp/servers.json`
2. Run `.agents/scripts/sync-mcp.sh`
3. Commit all generated files
## Rules
- Edit skills in `.agents/skills/`, never in `.claude/skills/` or `.cursor/skills/`
- Edit MCP in `.agents/mcp/servers.json`, never in `.mcp.json` or `.cursor/mcp.json`
- Pre-commit hooks enforce sync automatically
A.9 New Repo Checklist
- Create
AGENTS.mdat repo root - Create
CLAUDE.mdwith@AGENTS.mdpointer - Create
.agents/withskills/,skills-local/.gitkeep,mcp/servers.json,scripts/,AGENTS.md - Create initial skills in
.agents/skills/ - Run
link-skills.shto create symlinks - Create or adapt sync script for MCP
- Run sync to generate
.mcp.json,.cursor/mcp.json,.codex/config.toml - Add pre-commit hooks
- Update
.gitignore - Verify: symlinks resolve, sync check passes, skills discoverable
Part B: Migration Guide #
B.1 Assessment: Audit What You Have
find . -maxdepth 3 \( \
-name "AGENTS.md" -o -name "CLAUDE.md" -o \
-name ".mcp.json" -o -name "mcp.json" -o \
-name "*.mdc" -o -name "SKILL.md" -o \
-name "settings.json" -o -name "settings.local.json" -o \
-name "config.toml" \
\) -not -path "*/.venv/*" -not -path "*/node_modules/*"
For each file, classify as Portable, Generated, or Agent-specific.
B.2 Classification Matrix
| Asset | Category | Action |
|---|---|---|
AGENTS.md |
Portable | Keep at root. Create if missing. |
CLAUDE.md (full instructions) |
Portable | Move content to AGENTS.md, replace with @AGENTS.md pointer |
.claude/skills/*/SKILL.md (real files) |
Should be portable | git mv to .agents/skills/, replace with symlinks |
.agents/skills/*/SKILL.md |
Already portable | No change needed |
.cursor/commands/*.md (from skills) |
Generated | Mark as generated output, or leave if hand-authored |
.cursor/rules/*.mdc |
Agent-specific | Leave in place. MDC format not portable. |
.mcp.json |
Generated | Create .agents/mcp/servers.json as canonical, generate from it |
.cursor/mcp.json |
Generated | Generate from canonical source |
.codex/config.toml |
Generated | Generate from canonical source |
.claude/settings.json |
Agent-specific | Leave (committed hooks) |
.claude/settings.local.json |
Agent-specific | Leave (gitignored permissions) |
B.3 Migration Steps by Layer
Order matters. Start with the lowest-risk layer:
Step 1: Instructions (lowest risk)
- If
AGENTS.mdexists: review, keep as canonical - If only
CLAUDE.mdexists with full instructions: rename toAGENTS.md, create pointerCLAUDE.md - If both exist with different content: merge into
AGENTS.md, reduceCLAUDE.mdto pointer + overrides
Step 2: Skills
- Inventory: check
.claude/skills/,.cursor/commands/,.agents/skills/ - If real files in
.claude/skills/:git mvto.agents/skills/, replace with symlinks - If already in
.agents/skills/: addlink-skills.shand symlinks - Add pre-commit hook
Step 3: MCP
- Find the most complete MCP config (usually
.mcp.json) - Copy to
.agents/mcp/servers.jsonas canonical - Create or adapt sync script
- Run sync, diff outputs against originals to verify
- Add to pre-commit or CI
Step 4: Cleanup
- Add
.agents/AGENTS.mdand.agents/README.md - Update
.gitignore - Verify with sync check
B.4 Archetype-Specific Guidance
Archetype 1: Code Project (already partially migrated)
Pattern: meta-stack, sofa — Python/UV repos with some .agents/ structure already.
Typical state: Has .agents/skills/, maybe a sync script that copies (not symlinks), AGENTS.md at root.
Migration:
- Replace copy-based sync with symlinks for skills
- Add
link-skills.shand pre-commit hook - Ensure MCP generation covers all agents
- Single atomic commit
Reference implementation: sofa PR #94 demonstrates the full migration.
Archetype 2: Cursor-Heavy with Submodules
Pattern: meta-salley — .cursor/rules/ with MDC files, git submodules each with their own agent config.
Key decisions:
-
Do NOT migrate Cursor rules. The
.cursor/rules/*.mdcfiles use MDC-specific features (glob scoping,alwaysApply,descriptionfor context-aware ) that have no cross-agent equivalent. Leave them in.cursor/rules/. If you want Claude Code to see the same knowledge, put it inAGENTS.md— don't create a new abstraction layer. -
Each submodule manages its own config. Don't centralize submodule agent config in the parent repo. Apply the migration independently to each submodule that needs it. The parent's
.cursor/rules/handles meta-level navigation only. -
MCP across submodules. If submodules need different MCP servers, each gets its own
.agents/mcp/servers.json. If they share the same set, the parent's config applies in the multi-root workspace context.
Archetype 3: Meta Workspace / Coordination Hub
Pattern: iCloud Documents — portfolio coordination with 27+ sub-projects, not a code project.
Key decisions:
-
Whitelist
.gitignoreneeds explicit allows for every agent directory. When a new agent tool appears, add it to the whitelist. -
Skills are operational workflows, not code-generation —
/rallytriages tickets,/routedispatches to sub-projects. The SKILL.md format works identically for these. -
AGENTS.mdis a router, not a code guide:
## Routing
- **gear tools**: See `gear/AGENTS.md`
- **Jira tasks**: Read `sherpa/guides/career-sherpa/gear/jira-ticket-template.md`
- MCP servers are workspace-wide. Individual sub-projects have their own repos with their own MCP configs.
B.5 Decision Tree
Is this file read identically by multiple agents?
YES -> Portable. Move to .agents/ or root, symlink from agent dirs.
NO -> Is the underlying data the same, just different format?
YES -> Generated. Canonical source in .agents/, sync script renders.
NO -> Agent-specific. Leave where it is.
B.6 Ongoing Maintenance
| Task | How |
|---|---|
| Add a skill | Create in .agents/skills/, run link-skills.sh, commit both |
| Install third-party skill | npx skills add <repo>@<name> -y |
| Add MCP server | Edit .agents/mcp/servers.json, run sync, commit all generated files |
| Add new agent tool | Add rendering to sync script, add target to link-skills.sh TARGETS array |
| Validate skills | skills-ref validate .agents/skills/<name> |
| Check sync freshness | sync-mcp.sh check (exits non-zero if stale) |
Sources #
| Standard | What it governs | Link |
|---|---|---|
| Agent Skills spec | SKILL.md format, skill discovery | agentskills.io/specification |
| AGENTS.md / AAIF | Universal agent instructions | agents.md, AAIF (Linux Foundation) |
| Model Context Protocol | MCP server definitions | modelcontextprotocol.io |
| Vercel Skills CLI | npx skills add, skill marketplace |
github.com/vercel-labs/skills |
| skills-ref | Skill validation CLI | github.com/agentskills/agentskills |
| SSW Rules | Symlink best practices | ssw.com.au/rules/symlink-agents-to-claude |
| Claude Code Skills Docs | Claude-specific skill discovery | code.claude.com/docs/en/skills |
| Client Implementation Guide | How agents should scan .agents/skills/ |
agentskills.io/client-implementation |
| Claude Code .agents/ support request | Community request for native scanning | github.com/anthropics/claude-code#31005 |