Sync AI assistant skills across providers.
Every AI coding assistant stores custom skills in a different directory: Claude Code uses ~/.claude/skills/, Copilot uses ~/.copilot/skills/, Gemini CLI uses ~/.gemini/skills/, and Factory uses ~/.factory/skills/. They all use the same SKILL.md format (the Agent Skills open standard), but if you use more than one tool, you end up maintaining the same skills in multiple places. When one copy drifts, you have no way to know until something breaks.
skill-sync fixes this. Declare one provider as your source of truth, and skill-sync copies your skills to every target you configure. Then use status to catch drift before it becomes a problem.
# Install
go install github.com/user/skill-sync@latest
# Create a config file
skill-sync init --source claude --targets copilot,gemini,factory
# Sync all skills
skill-sync sync
# Check for drift
skill-sync statusOr skip the config file entirely:
skill-sync sync --source claude --targets copilot,geminiSync your skills to a plain directory (e.g., a git repo) instead of a named provider:
# Sync Claude skills to a git repo for backup
skill-sync sync --source claude --target-dir ~/dev/cc-skills
# Check what's drifted
skill-sync status --source claude --target-dir ~/dev/cc-skills
# Or use a config file
skill-sync init --source claude --target-dir ~/dev/cc-skills
skill-sync syncSkills are written as-is using the same <name>/SKILL.md directory format. This replaces ad-hoc rsync scripts with proper drift detection.
Skip provider names entirely — sync between two directories using just paths:
# Sync a project's skills to your global Claude skills
skill-sync sync --source-dir ~/dev/my-project/.claude/skills --target-dir ~/.claude/skills
# Check for drift between two directories
skill-sync status --source-dir ~/dev/my-project/.claude/skills --target-dir ~/.claude/skillsNo config file or --source flag required.
Use --target-dir multiple times to sync one source to several directories at once:
# Sync a project's skills to both global Claude skills and a backup repo
skill-sync sync --source-dir ~/dev/my-project/.claude/skills \
--target-dir ~/.claude/skills \
--target-dir ~/dev/cc-skills-backup
# Check drift across all targets
skill-sync status --source-dir ~/dev/my-project/.claude/skills \
--target-dir ~/.claude/skills \
--target-dir ~/dev/cc-skills-backupEach target directory is reported separately in status and diff output.
Creates a .skill-sync.yaml config file in the current directory.
skill-sync init --source claude --targets copilot,gemini,factoryCreated .skill-sync.yaml (source: claude, targets: [copilot gemini factory])
Reads skills from your source provider and writes them to all targets.
skill-sync syncSKILL TARGET STATUS
deploy copilot synced
deploy gemini synced
deploy factory synced
search copilot synced
search gemini synced
search factory synced
Synced: 6 Errors: 0
Preview what would happen without writing anything:
skill-sync sync --dry-runSync only specific skills:
skill-sync sync --skill deploy --skill reviewOverride directories:
# Use a custom source directory
skill-sync sync --source-dir /path/to/my/skills
# Use a custom target directory (single target only)
skill-sync sync --source claude --targets copilot --target-dir /path/to/copilot/skillsCompares skills in your source against all targets and reports drift. Exits with code 1 if any drift is detected.
skill-sync statusTarget: copilot
SKILL STATUS
deploy [ok] in-sync
search [!] modified
lint [-] missing
Target: gemini
SKILL STATUS
deploy [ok] in-sync
search [ok] in-sync
lint [ok] in-sync
Status symbols:
| Symbol | Meaning |
|---|---|
[ok] in-sync |
Source and target content match |
[!] modified |
Both exist but content differs |
[-] missing |
Skill exists in source but not in target |
[+] extra |
Skill exists in target but not in source |
Shows unified diffs for skills that differ between source and a target. If no provider is specified, shows diffs for all targets.
skill-sync diff copilot--- a/search
+++ b/search
@@ -1,3 +1,3 @@
# Search codebase
-Search for $ARGUMENTS in ${PROJECT} across all files.
+Search for $ARGUMENTS in src/ only.
Return matching lines with context.
All providers use the Agent Skills open standard -- SKILL.md files with optional YAML frontmatter, one directory per skill.
| Provider | Default Skill Location | Aliases / Compat |
|---|---|---|
| Claude Code | ~/.claude/skills/<name>/SKILL.md |
— |
| GitHub Copilot | ~/.copilot/skills/<name>/SKILL.md |
Also reads ~/.claude/skills/ |
| Gemini CLI | ~/.gemini/skills/<name>/SKILL.md |
Also reads ~/.agents/skills/ |
| Factory AI | ~/.factory/skills/<name>/SKILL.md |
Also reads .agent/skills/ |
All defaults are user-level ($HOME) directories, so skills follow you across projects.
The .skill-sync.yaml file declares your source provider, target providers, and optional directory overrides.
# Source of truth -- skills are read from here
source: claude
# Optional: override the source provider's default directory
# source_dir: /custom/path/to/claude/skills
# Target providers -- skills are synced to all of these
targets:
- copilot
- gemini
- factory
# Optional: override target directories (per-provider)
# target_dirs:
# copilot: /custom/path/to/copilot/skills
# gemini: /custom/path/to/gemini/skills
# Optional: sync only these skills (empty = sync all)
skills: []For directory backup mode, use target_dir instead of targets:
source: claude
target_dir: ~/dev/cc-skills
skills: []targets and target_dir are mutually exclusive.
- CLI flag (
--source-dir/--target-dir) -- highest priority - Config file (
source_dir/target_dirs) - Provider default (
~/.<provider>/skills/)
The --target-dir flag can only be used when there is a single target. For multiple targets, use target_dirs in the config file.
All commands accept --config to use a different config file path:
skill-sync sync --config my-config.yamlYou can also skip the config file entirely with --source and --targets:
skill-sync status --source claude --targets copilot,geminiSince all four providers now use the same SKILL.md format, sync is a straight copy -- no format translation needed. skill-sync reads each <name>/SKILL.md directory from the source and writes it verbatim to each target's skill directory.
status reads skills from both source and all targets, normalizes trailing whitespace, and compares content. It reports each skill as in-sync, modified, missing, or extra. If any drift is detected, it exits with code 1.
diff does the same comparison as status but produces unified diffs (like git diff) for modified skills instead of a status table.
Sync is additive -- it writes source skills to targets but does not delete extra skills found in targets. The status command reports extras so you can handle them manually.
Use status as a CI gate to catch skill drift on every push:
name: Skill Sync Check
on: [push, pull_request]
jobs:
skill-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Build skill-sync
run: go build -o skill-sync .
- name: Check for skill drift
run: ./skill-sync status| Command | Exit 0 | Exit 1 |
|---|---|---|
init |
Config created | Config exists, unknown provider, or validation error |
sync |
All skills synced | One or more skills failed to sync |
status |
All targets in-sync | Drift detected |
diff |
Always (diffs printed) | Provider resolution error |
# Build
go build -o skill-sync .
# Run all tests
go test ./...
# Run smoke tests
go test -tags smoke ./tests/
# Lint
go vet ./...
golangci-lint run ./...The codebase is structured as:
cmd/ CLI commands (cobra)
internal/
config/ Config loading and validation
provider/ Provider interface + shared skillMDProvider
sync/ Sync engine + diff/drift engine
tests/ Smoke tests
When adding a new provider, register a Factory in an init() function -- it takes a baseDir string and returns a Provider. See any of the existing provider files for a one-liner example.
MIT