Skill Sync
Three tools, three skill directories, and a skill edited in one of them silently ages in the other two. This skill finds the directories, shows the drift, and syncs them with the newest copy winning.
All commands run scripts/skill_sync.py from this skill's folder. Python 3 only, no dependencies.
Where skills live
| tool |
user-level |
project-level |
| Claude Code |
~/.claude/skills |
<repo>/.claude/skills |
| Codex |
~/.codex/skills |
<repo>/.codex/skills |
| Cursor |
~/.cursor/skills |
<repo>/.cursor/skills |
The script checks all six, plus any --store NAME=PATH you add or SKILL_SYNC_STORES=name=path:name=path in the environment. Two paths that resolve to the same directory, which is what a symlink setup looks like, are treated as one store and reported as such. Cursor's own bundled skills (~/.cursor/skills-cursor) are never touched.
A skill is a directory containing SKILL.md. Anything else in a store is ignored.
Workflow
Look first.
python3 scripts/skill_sync.py status
One row per skill, one column per store: same, newest, older, or missing, and the decision the sync would make. Read this to the user before doing anything. If two stores are symlinked, say so; there is nothing to sync between them.
Show the plan.
python3 scripts/skill_sync.py plan
Every copy the sync would make, with the winning store and its timestamp, and every conflict. A conflict is two copies that differ but carry the same modification time; the script will not guess. Ask the user which store is right, or pass --prefer STORE.
Apply.
python3 scripts/skill_sync.py apply
Copies newest over older or missing, preserving file timestamps so the next run sees everything in sync. Every overwritten copy is moved to ~/.skill-sync/backups/<timestamp>/<store>/<skill> first, and each run appends to ~/.skill-sync/log.jsonl. Then run status again and confirm every row reads same.
Options worth knowing
--only NAME (repeatable) syncs one skill. --exclude NAME or --exclude prefix* keeps private or tool-specific skills out.
--no-project ignores project-level stores; --project DIR points at a repo other than the current one.
--prefer STORE breaks ties. It does not override a clearly newer copy elsewhere.
--json for machine-readable output.
Rules
- Never run
apply without showing plan or status first. The user should see what will move.
- Newest wins is a heuristic, not a truth. If a store looks like a stale snapshot (dozens of
older rows, one date), say that and consider --prefer for the live store instead of trusting timestamps blindly.
- Do not sync skills that contain personal, client, or credential material into a store that syncs to a cloud account unless the user says so.
--exclude exists for this.
- If a tool does not follow symlinks, keep it as a real directory and let the sync copy into it; do not replace it with a symlink on the user's behalf.
- Report what changed, where the backups are, and any conflict left open.
1---2name: skill-sync3description: Find every place agent skills live on this machine (Claude Code, Codex, Cursor; user-level and project-level) and bring them into sync, bidirectionally, newest copy wins. Use when the user asks to sync skills between tools, says a skill is missing or stale in one tool, wants to know which tools share a skills directory, or after editing a skill that other tools should see. Reports before it changes anything; backs up anything it overwrites; never deletes.4---56# Skill Sync78Three tools, three skill directories, and a skill edited in one of them silently ages in the other two. This skill finds the directories, shows the drift, and syncs them with the newest copy winning.910All commands run `scripts/skill_sync.py` from this skill's folder. Python 3 only, no dependencies.1112## Where skills live1314| tool | user-level | project-level |15|---|---|---|16| Claude Code | `~/.claude/skills` | `<repo>/.claude/skills` |17| Codex | `~/.codex/skills` | `<repo>/.codex/skills` |18| Cursor | `~/.cursor/skills` | `<repo>/.cursor/skills` |1920The script checks all six, plus any `--store NAME=PATH` you add or `SKILL_SYNC_STORES=name=path:name=path` in the environment. Two paths that resolve to the same directory, which is what a symlink setup looks like, are treated as one store and reported as such. Cursor's own bundled skills (`~/.cursor/skills-cursor`) are never touched.2122A skill is a directory containing `SKILL.md`. Anything else in a store is ignored.2324## Workflow25261. **Look first.**2728 ```bash29 python3 scripts/skill_sync.py status30 ```3132 One row per skill, one column per store: `same`, `newest`, `older`, or `missing`, and the decision the sync would make. Read this to the user before doing anything. If two stores are symlinked, say so; there is nothing to sync between them.33342. **Show the plan.**3536 ```bash37 python3 scripts/skill_sync.py plan38 ```3940 Every copy the sync would make, with the winning store and its timestamp, and every conflict. A conflict is two copies that differ but carry the same modification time; the script will not guess. Ask the user which store is right, or pass `--prefer STORE`.41423. **Apply.**4344 ```bash45 python3 scripts/skill_sync.py apply46 ```4748 Copies newest over older or missing, preserving file timestamps so the next run sees everything in sync. Every overwritten copy is moved to `~/.skill-sync/backups/<timestamp>/<store>/<skill>` first, and each run appends to `~/.skill-sync/log.jsonl`. Then run `status` again and confirm every row reads `same`.4950## Options worth knowing5152- `--only NAME` (repeatable) syncs one skill. `--exclude NAME` or `--exclude prefix*` keeps private or tool-specific skills out.53- `--no-project` ignores project-level stores; `--project DIR` points at a repo other than the current one.54- `--prefer STORE` breaks ties. It does not override a clearly newer copy elsewhere.55- `--json` for machine-readable output.5657## Rules5859- Never run `apply` without showing `plan` or `status` first. The user should see what will move.60- Newest wins is a heuristic, not a truth. If a store looks like a stale snapshot (dozens of `older` rows, one date), say that and consider `--prefer` for the live store instead of trusting timestamps blindly.61- Do not sync skills that contain personal, client, or credential material into a store that syncs to a cloud account unless the user says so. `--exclude` exists for this.62- If a tool does not follow symlinks, keep it as a real directory and let the sync copy into it; do not replace it with a symlink on the user's behalf.63- Report what changed, where the backups are, and any conflict left open.