Opt-in activation for agent skills (agentskills.io
SKILL.md format): symlink individual skills from a skills repo into the
provider-agnostic .agents/skills/ directory that cross-compliant agents scan.
Because skills are symlinked, not copied, a git pull in the skills repo
updates every loaded skill.
Stdlib-only Python, no runtime dependencies, no vendor-specific tooling.
uv tool install git+https://github.com/daniel-m-campos/skillctl # or pipx install, or pip installReinstalling from a local checkout at the same version makes uv reuse the
cached wheel, so edits or a git pull can silently reinstall stale code
(--force overwrites the install but doesn't rebuild). Bump the version in
pyproject.toml, or force a rebuild:
uv tool install --force --reinstall .1. Point it at a skills repo — keep skills anywhere, tell skillctl where:
skillctl list --source ~/work/org-skills # flag (skills dir or its repo root)
export SKILLCTL_SOURCE=~/work/org-skills # or env var
echo "SKILLCTL_SOURCE=~/work/org-skills" >> ~/.agents/.env # or set once in .env.env lookup: <git-root-of-cwd>/.env first, then ~/.agents/.env.
Precedence: --source > $SKILLCTL_SOURCE > .env files > fork mode.
2. Fork mode — fork this repo, add your skills under skills/, and run
skillctl from inside it (no configuration needed): the skills/ dir at the
git root is found automatically. The fork ships ready to use: template,
example skill, validator CI, CONTRIBUTING, CODEOWNERS.
skillctl list # available skills + load status
skillctl load legacy-billing-quirks # symlink into ~/.agents/skills
skillctl load all
skillctl status # everything loaded, from any source
skillctl unload legacy-billing-quirks
skillctl clean # remove every symlink pointing into the sourceDefault target is your personal dir ~/.agents/skills (all projects).
Add --repo to target the current project instead
(<git-root>/.agents/skills); it finds the git root from any subdirectory and
errors if you aren't in a git repo. $SKILLCTL_TARGET overrides the target
entirely (used by tests).
With --repo, skillctl writes a self-ignoring .gitignore into
<git-root>/.agents/skills/ so the generated symlinks — absolute, per-machine
paths — are never tracked by git. Commit that .gitignore and your
manifest; never commit the links. Teammates run skillctl locally to
regenerate links on their own machines.
A manifest pins a default skill set — plain text, one skill per line
(# comments; all means every skill). See skills.manifest.
skillctl sync # ~/.agents/skills <- ~/.agents/skills.manifest
skillctl sync --repo # <git-root>/.agents/... <- <git-root>/.agents/skills.manifestsync loads everything listed and prunes any source-managed symlink that
isn't listed, leaving foreign symlinks alone. Note: pruning compares against
the currently resolved source — links made from a different source are
treated as foreign and never touched.
skillctl validate # every skill in the source
skillctl validate skills/my-skill # one skill
skillctl validate --strict # warnings fail tooERROR = spec violation (blocks CI), WARN = best-practice smell. See CONTRIBUTING.md for the rule table.
statusandunloadare source-independent: they act on whatever is symlinked into the target, so they work across multiple source repos and need no--source.statuslists every link (with where it points);unload NAMEremoves a link by name from any source, andunload allremoves every loaded skill. Pass--source Xto either to restrict to just that source's links.- Configuration comes from
.envfiles, including the current repo's — only theSKILLCTL_SOURCE/SKILLCTL_TARGETkeys are read, and values are paths, never commands. - Symlink-based: macOS/Linux. Windows needs developer mode; untested.