A no-nonsense guide to the tool that stops you from going insane copying files around
You've got AI coding skills. Prompts, context files, reusable instructions - whatever you call them. And you've got multiple AI tools: OpenCode, Claude Code, Cursor, maybe a few others you're experimenting with.
Here's the annoying part: each one wants your skills in a different folder.
So you copy my-skill/ to ~/.config/opencode/skill/. Then you copy it again to ~/.claude/skills/. Then Cursor. Then Codex. Now you have five copies. You update one. The others are stale. You forget which is which. Chaos.
Instead of copying, CapSync creates symbolic links (symlinks). Think of them as shortcuts that look like real folders to your AI tools.
You keep one master copy of your skills. CapSync points all your tools at that one copy. Update it once, every tool sees the change instantly.
No copies. No syncing. No forgetting.
You
│
▼
CapSync CLI ──► Config File ──► Symlinks ──► AI Tools
That's it. You run commands. CapSync reads a config file. It creates symlinks. Your tools are happy.
cli.rs - The Command Parser
This is where your typing turns into action. Uses a library called clap (great name) to figure out what you want. Routes to the right handler.
config.rs - The Config Manager
Reads and writes your settings to ~/.config/capsync/config.toml. It's just a TOML file - human readable, easy to edit by hand if you want.
detect.rs - The Finder
Scans your computer for installed AI tools. Just checks if directories exist. Fast, simple, non-invasive.
clone.rs - The Repo Materializer
Handles whole-repository cloning into skills_source, including update vs override prompts, branch selection, and safety checks around replacing an existing checkout.
install.rs - The Skill Materializer
Handles installing one explicit skill reference into skills_source/<slug> by cloning to a temporary checkout, selecting a skill directory, and copying it into the managed source tree.
sync.rs - The Worker
Actually creates and removes symlinks. Handles the messy platform differences (Unix vs Windows). Reports what worked and what didn't.
tools.rs - The Registry
A big list of all supported tools and where they keep their stuff. Currently 40+ tools. Easy to add more.
CapSync doesn't:
- Create skills for you
- Validate your skill format
- Have a GUI
- Run as a daemon
- Do anything fancy
It does sync, and it can materialize remote content when you explicitly point it at a repository or a concrete skill reference. It still does not browse, rank, or discover skills for you.
Pros:
- Zero disk space (just pointers)
- Instant updates (no re-syncing)
- Easy to undo (just delete the link)
- Tools can't tell the difference
Cons:
- Some tools might not follow symlinks (rare)
- Doesn't work across network drives
- Windows handles them differently (we handle this)
Why not copies? Because then we'd need to re-copy every time you change something. That's annoying.
Why not hard links? Because they don't work across filesystems and are confusing.
Because it's fast, safe, and makes distribution easy. One binary, no dependencies. You download it, it works. No "install this runtime first" nonsense.
Plus, if I mess up memory management, the compiler yells at me. That's nice.
Interactive setup. Asks where your skills live. Detects what tools you have. Creates a minimal config with just those tools.
$ capsync init
Welcome to CapSync! Let's set up your configuration.
Enter your skills source directory: $HOME/my-skills
Detecting installed tools...
Detected and enabled: claude, opencodeNotice it only adds tools you actually have. No clutter.
Creates symlinks for all enabled tools. Shows what worked.
$ capsync sync
Syncing skills...
================
Source: /Users/you/my-skills
Synced successfully:
claude
opencodeIf something fails, it tells you. But keeps going with the others.
Use clone when your skills_source should be one whole Git repository.
$ capsync clone vercel-labs/skills
Fetching remote branch info...
Using branch: main
Cloning into /Users/you/my-skills...
Successfully cloned vercel-labs/skills (branch: main)
Running sync...Key behavior:
- Supports
owner/repo,owner/repo.git,https://...,http://..., andgit@... - Auto-detects the remote default branch unless
--branchis provided - If the same repo is already present, offers update or override
- Update is implemented as fetch + hard reset to upstream, not a merge-based pull
- If the requested branch differs from the current local branch, it requires explicit confirmation before re-cloning
Use install when you want one skill from a repo, not the whole repo.
$ capsync install vercel-labs/skills/find-skills
Fetching skill source...
Installed skill 'find-skills' to /Users/you/my-skills/find-skills
Running sync...Supported explicit references in v1:
https://skills.sh/owner/repo/skill-slughttps://github.com/owner/repo/tree/<branch>/path/to/skillowner/repo/skill-slugowner/repo/path/to/skill
Key behavior:
- Rejects repo-only refs like
owner/repo - Rejects
http://skills.sh/...; HTTPS is required forskills.shreferences - For GitHub tree URLs, branch names containing
/must be URL-encoded in the branch segment (for examplefeature%2Fmy-branch) - Clones to a temporary checkout, finds exactly one skill directory, then copies it into
skills_source/<slug> - Refuses to install into a
skills_sourcethat is itself a git repo managed bycapsync clone - Leaves
commands_sourceuntouched in v1
Got a new AI tool? Add it anytime.
$ capsync add cursor
Added 'cursor' to configuration
Running sync...Validates the tool name. If you typo it, you'll know:
$ capsync add cursor-new
Error: Tool 'cursor-new' does not exist or is unsupportedUse --no-sync if you just want to add without syncing immediately.
Removes a tool's symlink. Safe - checks it's actually a symlink first.
$ capsync remove cursor
Removed symlink from /Users/you/.cursor/skillsUse --all to nuke everything and start fresh.
Shows if your source exists and which tools have working symlinks.
$ capsync status
Status:
=======
Source: /Users/you/my-skills
Destinations:
claude: /Users/you/.claude/skills (symlink -> /Users/you/my-skills)
opencode: /Users/you/.config/opencode/skill (symlink -> /Users/you/my-skills)Displays your current config in a readable format.
You have skills in ~/projects/ai-skills. You use Claude Code and OpenCode.
$ capsync init
# Enter: ~/projects/ai-skills
# Detects: claude, opencode
# Creates config
$ capsync sync
# Creates symlinks
# DoneNow both tools see your skills. You edit a skill in ~/projects/ai-skills, both tools see the update immediately.
You install Cursor. Want your skills there too.
$ capsync add cursor
# Validates 'cursor' is supported
# Adds to config
# Runs sync automatically
# Cursor now sees your skillsYou stop using Claude Code. Want to remove it but keep your skills.
$ capsync remove claude
# Removes the symlink
# Your skills are still in ~/projects/ai-skills
# Claude Code just can't see them anymoreLocated at ~/.config/capsync/config.toml. Looks like this:
skills_source = "/Users/you/my-skills"
commands_source = "/Users/you/my-skills/commands"
[destinations.claude]
enabled = true
skills_path = "/Users/you/.claude/skills"
commands_path = "/Users/you/.claude/commands"
[destinations.opencode]
enabled = true
skills_path = "/Users/you/.config/opencode/skill"
commands_path = "/Users/you/.config/opencode/commands"The legacy keys source and destination path are still accepted for backward compatibility, but the canonical config fields are skills_source, commands_source, skills_path, and commands_path.
You can edit this by hand. It's just TOML. Add tools, remove them, change paths. CapSync will respect whatever's there.
Early versions added all 40+ tools to the config, disabled. Made the config huge and ugly. Now we only add what you have. Clean, minimal.
Used to have a default path. But everyone's setup is different. Forcing you to type it means you think about where your skills actually live.
They break in some terminals. They're distracting. Plain text works everywhere.
CapSync still does not browse or rank a public catalog for you. But it can now materialize remote content when you provide an explicit repo (capsync clone) or an explicit skill reference (capsync install).
It's readable. It has comments. It's standard in the Rust world. You can edit it by hand without breaking things.
On Unix (Linux, macOS):
std::os::unix::fs::symlink(source, dest)On Windows:
std::os::windows::fs::symlink_dir(source, dest)Same idea, different system calls. We handle both.
- Check source exists (fail fast if not)
- For each enabled tool:
- Create parent directories if needed
- Remove existing file/symlink at destination (clean slate)
- Create new symlink
- Report results
Atomic? No. If something fails halfway, some tools are synced, some aren't. We tell you which.
Uses anyhow crate. Means we can attach context to errors:
fs::remove_file(dest)
.with_context(|| format!("Failed to remove symlink at {}", dest.display()))?You get helpful error messages, not just "operation failed."
Just checks if directories exist:
Path::new(&home.join(".claude")).exists()Simple. Fast. Works.
What It Won't Do:
- Browse or search a public skills registry for you
- Validate your skill format
- Sync to remote machines (SSH, etc.)
- Run as a background service
- Merge conflicting skills
- Handle tools that don't follow symlinks (rare, but possible)
Edge Cases:
- If a tool changes their skills directory, you need to update the config
- If your source directory moves, you need to update the config
- Windows users might need admin rights for symlinks (depends on Windows version)
- Skill templates (scaffold new skills)
- Import/export (share skills as tarballs)
- Validation (check SKILL.md format)
- Remote sync (SSH to another machine)
- GUI version (for non-terminal folks)
But honestly? It works great as-is. Feature creep is the enemy.
CapSync is a tool that does one thing: keeps your AI skills in sync across multiple tools. It does this by creating symlinks from a single source directory to each tool's expected location.
It's fast. It's simple. It doesn't get in your way. It just works.