Reconcile Agent Skills Across Tools
Every skill-capable tool keeps its skills in its own root directory, and the sets drift: a
skill you author for Claude never appears in Codex, one you set up for Antigravity is
missing from Gemini, and so on. This skill computes the union of every skill across
every participating tool and copies that complete set into each tool — faithfully, in the
same on-disk format they all use (<root>/<name>/SKILL.md + the skill's other files) —
without losing or silently changing anything.
Unlike MCP servers (small config-file entries), a skill is a whole directory tree. So
this tool syncs directories, byte-for-byte, not config keys. All the real work is done by
scripts/reconcile_skills.py. Your job is to run it, interpret its output, and handle
conflicts and warnings. Do not hand-copy skill folders yourself — the script handles
discovery, identity, frontmatter adaptation, backups, and idempotency correctly.
What it guarantees
- No skill is ever deleted. Every tool ends with at least the skills it started with. The merge is purely additive (plus any conflict resolution you approve).
- Tool-managed namespaces are left alone. Anything whose name starts with
.is never read or written — the reserved.system/skills the tools install themselves, Cursor's.sync-manifest.json, the script's own.skill-backups/. A directory only counts as a skill if it contains aSKILL.md(so scratch/workspace folders are ignored). - Skills are copied verbatim. The only thing ever rewritten is an over-limit
description:value (see below) — fences, line endings, body, scripts, assets, andagents/openai.yamlall travel exactly as-is. - Overwrites are backed up to
<root>/.skill-backups/<timestamp>/<name>/before writing. - Conflicts stop the process. If the same skill name has genuinely different content in two tools, the tool refuses to guess — it reports the difference and writes nothing until you decide.
Scope (which tools participate)
| Tool key | Skills root | Role |
|---|---|---|
claude |
~/.claude/skills |
read + write |
codex |
~/.codex/skills |
read + write |
gemini (alias antigravity) |
~/.gemini/skills (Gemini CLI + Antigravity CLI & IDE) and ~/.gemini/config/skills (Antigravity 2.0 desktop) |
read + write (both roots) |
agents |
~/.agents/skills (.agents standard) |
read + write, auto-created |
cursor |
~/.cursor/skills (native) |
read-only source |
The Gemini CLI and the Antigravity CLI (agy) & IDE read the same ~/.gemini/skills directory,
while the Antigravity 2.0 desktop app reads a separate root, ~/.gemini/config/skills. They're
one entry covering both roots: each existing root is reconciled independently, a skill in either
counts toward the union, and the union is written back to both. The old key antigravity still
works as an alias on --only / --exclude / --include / --prefer.
~/.agents/skills is the vendor-neutral .agents standard
location (read by Antigravity, Cursor, OpenCode, and others). It's a first-class source and
destination, and is created if missing so the standard location always exists.
Cursor is a read-only source — and why
Per Cursor's docs, Cursor natively loads skills from the
other tools' folders for compatibility — ~/.claude/skills, ~/.codex/skills, plus
~/.agents/skills and its own ~/.cursor/skills. So once this tool syncs the union across
Claude/Codex/Antigravity, Cursor already sees the full union for free — nothing needs to
be written into a Cursor folder. The reconciler therefore:
- Reads
~/.cursor/skills(Cursor's native folder) as a source, so any skill you author directly in Cursor propagates out to the other tools. - Never writes to any Cursor folder.
- Ignores
~/.cursor/skills-cursorentirely — that's a third-party sync tool's folder (it carries a.sync-manifest.json) that Cursor itself does not read.
For the other three tools, each tool's own built-in skills live in a reserved .system/
directory; anything dot-prefixed is skipped by discovery, so those built-ins never propagate
and are never touched.
See references/skill-paths.md for the full path/format/quirk details.
Workflow
1. Run a plan first (always)
Never start with --apply. Run the default dry-run:
python3 <skill_dir>/scripts/reconcile_skills.py
Use
python3on macOS/Linux; on Windows usepython(orpy -3). This engine runs on Python 3.9+ — unlike the MCP engine it has notomllibdependency, so there's no Codex-style version cliff here.
Summarize for the user: how many tools were found, how many unique skills make up the
union, and which skills are missing from which tools (the "Skill coverage" section). The
"Per-tool plan" shows + add, ~ change, and = unchanged counts per tool.
2. Read the WARNINGS section
If a skill's description: exceeds a tool's frontmatter limit (default 1024 chars), the
tool warns and shortens it to fit, changing as little as possible (it trims at a
sentence/word boundary, keeping the leading trigger text; everything else is untouched).
When you see a shorten warning, offer to hand-write a tighter description in the source
skill instead — a human-quality rewrite beats a mechanical trim — then re-run. A name
over 64 chars is only flagged, never auto-changed (renaming a skill directory breaks
references).
3. Handle conflicts (exit code 2)
If the report shows BLOCKING CONFLICTS (exit code 2), do not apply by default. The
script reports the kind (and prints a ready-to-use --prefer suggestion plus the tools
involved):
- body differs — the skills' files/scripts/SKILL.md body genuinely differ.
- description differs — same files, but descriptions diverge in a way that isn't just one being a trimmed copy of the other.
Three ways forward:
- Pick a winner — re-run with
--prefer, a comma-separated tool priority list. For each conflicting skill, the first tool in the list that has it wins and its version is copied everywhere. Example:--prefer claude,codex. To choose a winner for just one skill (overriding the global order), use--prefer-skill <skill>=<tool>[,tool]— handy when different skills should be won by different tools in the same run. - Edit to match — the user reconciles the two copies by hand, then you re-run.
- Skip them for now — re-run with
--skip-conflictsto sync everything except the conflicting skills, leaving each tool's own copy of those untouched (nothing is overwritten or deleted). The conflicts are still reported and the exit code stays 2. Good for getting the non-conflicting skills everywhere immediately; confirm with the user first, since it leaves real divergence unresolved.
Resolving by preference overwrites the losing tools' copies (their originals are backed up). Call that out before applying.
4. Apply on confirmation
Once the plan looks right and conflicts are resolved, get the go-ahead and run with
--apply (carry over the same --prefer/--include you used in the plan):
python3 <skill_dir>/scripts/reconcile_skills.py --apply [--prefer <keys>] [--include cursor]
Report which tools changed, what was added/changed, and that backups are under each root's
.skill-backups/<stamp>/. Tools that were running may need a restart to pick up new skills.
Useful options
--prefer <keys>— priority order to auto-resolve conflicts (see above).--prefer-skill <skill>=<tool>[,...]— per-skill winner, overriding--preferfor that one skill. Repeatable.--skip-conflicts— sync the non-conflicting skills and leave conflicting names alone instead of blocking the whole run (see above). Exit code stays 2; nothing is overwritten.--only <keys>/--exclude <keys>— limit which tools participate (e.g.--exclude cursor).--include <keys>— add a tool that was excluded, without dropping the rest.--only-skill <names>/--skip-skill <names>— restrict the sync to, or hold back, specific skills by name. Use--skip-skillto leave a tool-specific skill where it is.--create-missing— also create skills roots for participating tools that don't have one. Off by default. Only use it if the user explicitly wants to seed a tool that has no root.--max-desc <n>/--max-name <n>— override the frontmatter limits (default 1024 / 64).--json— machine-readable summary instead of the text report.--home <path>— override the home directory (used for testing with a sandbox).
Things worth knowing
- All tools share one format. Despite older notes claiming JSON rule-maps (Cursor),
TOML configs (Codex), or
AGENTS.mdorchestrators (Antigravity), on disk every tool uses the same Anthropic SKILL.md directory format. So syncing is a faithful directory copy; there is no cross-format conversion to do. - Identity ignores the description value. A skill and the auto-shortened copy the tool itself wrote are recognized as the same skill, so re-running is idempotent and never invents a conflict between a full description and its trimmed twin.
agents/openai.yamland assets travel with the skill. They're just files in the skill directory; nothing special is done to them, and nothing is fabricated.- Scope is global skill roots only. Project-local skills (e.g. a repo's
.claude/skills) and the reserved.system/namespace are out of scope and untouched.
For the exact paths, formats, and per-tool quirks, see references/skill-paths.md.