I run coding agents on my laptop, a Mac Studio, a Mac Mini, and a Linux box running Omarchy. Each one has Claude Code and Codex, and each one had skill directories I filled by hand, one clone or copy at a time. They drifted almost immediately. A skill I added on one machine never made it to the others, or made it at an older commit.

Everything else on those machines already comes from my dotfiles, and I wanted skills to work the same way. I tried npx skills and a couple of other installers first. They’re good at adding a skill, but none of them gave me a command I could run on every machine, as often as I liked, and trust to converge on the same state. So I had an agent build skl, a small Rust CLI that reads a manifest and a lockfile and makes the machine match them.

skl's mascot, a cartoon skull waving and carrying a backpack full of skill cards
# ~/.config/skl/skills.toml
agents = ["claude-code", "codex"]

[skills]
skill-creator = { source = "anthropics/skills", subdir = "skills/skill-creator" }

skl sync fetches each skill, pins it to an exact commit in skills.lock, and links it into each agent’s skills directory. Claude Code and Codex are the defaults, but skl agents set takes any harness it knows about or any directory a harness reads from. The manifest and lockfile both live in the dotfiles. On the other machines, skl sync --locked installs the same commits and fails if the manifest would need a lockfile change.

Running it a second time does nothing, which was the whole point. skl records every link it creates and only repoints or removes those, so a skill I dropped in by hand shows up as a conflict instead of getting overwritten. That makes it safe to run from a bootstrap script or a git hook without reading the output.

The machines don’t need identical skills either. Xcode skills have no business on the Omarchy box, so os = ["macos"] keeps them off it. A skill tagged profiles = ["work"] only installs where skl profile set work has run, and that setting stays out of the dotfiles so every machine shares one lockfile.

It’s on Homebrew for macOS and Linux.

brew tap nanoxd/skl https://github.com/nanoxd/skl
brew install nanoxd/skl/skl

Skills that are already installed don’t need to be reinstalled by hand. skl list shows every skill directory skl doesn’t manage. If a skill came from a repository, skl add takes any Git URL, or owner/repo@skill as a shorthand, and starts tracking it. If it was copied in by hand with no source to point at, skl adopt snapshots it into the dotfiles beside the manifest.

skl list
skl add vercel-labs/agent-skills@web-design-guidelines
skl adopt review --from claude-code
skl sync --dry-run
skl sync --force

skl still leaves the originals in place and reports them as conflicts. Once the dry run looks right, --force backs them up and replaces them with links skl owns.

The last piece is a post-merge hook in the dotfiles repo.

#!/bin/sh
command -v skl >/dev/null && skl sync

Pulling my dotfiles on any machine now brings the skills along with everything else.1

#Footnotes

  1. Assuming I remember to pull. ↩