zskills
A declarative package manager for agentic coding CLIs — skills, plugins, and MCP servers managed from a single TOML manifest. Written in Rust.
Think brew bundle for your AI coding setup: skills.toml declares intent, the runtime's config (e.g. Claude Code's ~/.claude/settings.json, installed_plugins.json, MCP server entries) gets reconciled atomically. Works with any marketplace tap and any GitHub repo that exposes a skill under skills/<name>/SKILL.md.
Supported runtimes:
The data model in src/mcp.rs is intentionally runtime-agnostic — adding a new runtime is a new loader, not a new package manager.
Documentation
- Commands reference — every subcommand, every flag, with defaults and notes
- Use cases — 10 common workflows (bootstrap a machine, centralize duplicates, etc.)
- Architecture — the three-state model (intent / inventory / activation) and how reconciliation works
- Troubleshooting — diagnostic recipes for common errors
- Changelog
Install
cargo install --git https://github.com/zot24/zskills
Requires git on $PATH.
Companion Agent Skill
This repo also ships an Agent Skill that teaches Claude how to use zskills. Install it directly:
zskills skill install zot24/zskills
zskills clones this repo, finds skills/zskills/SKILL.md, and drops it at ~/.claude/skills/zskills/. If you'd rather declare it in the manifest for cross-machine reproducibility:
[[agent_skills]]
source = "zot24/zskills"
zskills sync
Either way, Claude reaches for zskills with confidence for install / search / sync / doctor / MCP management on its next session.
Why
Existing tooling is per-runtime, per-language, and per-primitive: there's a JavaScript shim for Claude skills, a separate flow for MCP servers, no way to track ownership across machines, no declarative reproducibility. zskills is a single static binary that:
- Manages skills, plugins, and MCP servers from one manifest.
- Preserves every unknown field in your settings JSON (hooks, permissions, env, anything the runtime adds later) — atomic round-trips, never clobbers.
- Reconciles intent (manifest) ↔ inventory (what's actually installed) ↔ activation (what's enabled) in one pass.
- Treats secrets carefully: only
${VAR}references and key names ever land in zskills's data structures or output, never values. - Is designed for multiple agentic CLI runtimes — Claude Code today, more as their primitives stabilize.
Commands
zskills list [-v] # plugins, Agent Skills, MCP servers (prints which harnesses can see each name)
zskills plugin install <name@marketplace> # add to enabledPlugins
zskills plugin install <name> --harness pi,grok # copy nested skills/<name>/ into ~/.agents/skills/ (the shared hub)
zskills plugin install -i # browse marketplace plugins
zskills plugin remove <name> # drop inventory, keep bytes
zskills plugin purge <name> # also delete bytes
zskills plugin enable|disable <name>
zskills skill install <owner/repo>
zskills skill remove <name> # deletes bytes
zskills skill upgrade [<name>...]
zskills mcp add <name> --url|--command ...
zskills mcp remove <name> [--scope user]
zskills sync [--file f.toml] # apply skills.toml
zskills marketplace add|remove|list|update
zskills doctor [--fix]
zskills scan|migrate|migrate-skill|migrate-all|search
Bare install/remove/purge/enable/disable/update/upgrade exit 2 and name the typed replacement.
<name> accepts unqualified (servarr) when unambiguous, or name@marketplace (servarr@zot24-skills) to disambiguate.
-i / --interactive is available on install, remove, and search. If fzf is on $PATH it gets used automatically; otherwise zskills falls back to a built-in fuzzy picker. Disable fzf detection with ZSKILLS_NO_FZF=1.
Declarative manifest (skills.toml)
CLI group to manifest key:
[[skills]] → plugins → zskills plugin install|remove|purge|enable|disable
[[agent_skills]] → Agent Skills → zskills skill install|remove|upgrade
[[mcps]] → MCP servers → zskills mcp add|remove
The group skill writes [[agent_skills]]. The key [[skills]] is plugins.
# Claude Code plugins (marketplace-based, controlled via enabledPlugins)
[defaults]
harnesses = ["claude", "pi", "hermes", "kimi", "grok", "codex"]
mcp_harnesses = ["claude", "pi", "hermes", "kimi", "grok", "codex"]
[[skills]]
name = "umbrel-app"
marketplace = "zot24-skills"
[[skills]]
name = "pi"
marketplace = "zot24-skills"
# harnesses = ["claude", "pi"] # optional override; inherit [defaults].harnesses if omitted
[[skills]]
name = "github"
marketplace = "claude-plugins-official"
[[skills]]
name = "cloudflare"
marketplace = "cloudflare"
# One source repo, two primitives. Requires zskills 1.3.0 or later.
# Plugin for Claude. OpenCode Agent Skills for Pi and Grok.
# Set harnesses on both rows — omit inherits [defaults].harnesses.
[[marketplaces]]
name = "llm-wiki"
repo = "nvk/llm-wiki"
[[skills]]
name = "wiki"
marketplace = "llm-wiki"
harnesses = ["claude"]
[[agent_skills]]
marketplace = "llm-wiki"
path = "plugins/llm-wiki-opencode/skills"
name = "wiki-manager"
harnesses = ["pi", "grok"]
[[agent_skills]]
marketplace = "llm-wiki"
path = "plugins/llm-wiki-opencode/skills"
name = "wiki-query"
harnesses = ["pi", "grok"]
# Agent Skills (older raw-SKILL.md format, installed to ~/.claude/skills/)
[[agent_skills]]
source = "jakubkrehel/make-interfaces-feel-better"
# Install only one specific skill out of a multi-skill repo:
[[agent_skills]]
source = "owner/multi-skill-repo"
name = "specific-skill"
# Many skills from one repo: `skills` is the plural of `name`, so one stanza
# carries one `source`. An entry declares `name` or `skills`, not both.
[[agent_skills]]
source = "mattpocock/skills"
skills = ["prototype", "research", "tdd", "wayfinder"]
# npm-distributed agent skills (npm install -g <pkg> + post-install)
[[agent_skills]]
npm = "get-shit-done-cc"
# Packages that need a custom installer command:
[[agent_skills]]
npm = "some-tool"
install_cmd = "npx some-tool setup"
# Optional skillspector gate. Absent means off.
[skillspector]
enabled = true
command = "skillspector"
args = []
fail_on = "do_not_install"
on_missing = "error"
zskills sync # apply: enables anything in the manifest, disables anything not
zskills sync --dry-run # preview, including planned skillspector scans
zskills skill inspect # rescan installed Agent Skills when the gate is on
[skillspector] enabled = true scans a staged copy on skill install, skill upgrade, plugin install, and sync. zskills copies into ~/.agents/skills/ only after the scan passes. Every fail_on value rejects max severity CRITICAL, including when skillspector exits 0. --skip-inspect skips that scan and records no pass. A reviewed inspect_override on an [[agent_skills]] row names one exact skill and a reason. That skill is still scanned, and its findings print as a warning instead of blocking the install. Full fields: Commands → skillspector.
zskills sync is idempotent. Run it anywhere — same machine, new machine — and the result matches the manifest. Plugins flip via enabledPlugins. Agent Skills get git cloned and copied into ~/.claude/skills/<name>/. Run it on every fresh checkout.
The llm-wiki rows require zskills 1.3.0 or later. Set harnesses on both the plugin and the Agent Skill rows. Claude Code consumes plugin wiki@llm-wiki (/wiki:*). Pi and Grok consume hub Agent Skills wiki-manager and wiki-query. Pi lists those names under [Skills]. zskills does not write ~/.pi/agent/prompts/wiki-query.md. zskills does not install scripts/pi-wiki-query. zskills does not provide Grok slash /wiki:*. A 1.2 binary ignores marketplace and path on [[agent_skills]] and treats those rows as local-only. Full recipe: Use cases.
Scanning project-scope skills
If you've enabled skills inside .claude/settings.json or dropped Agent Skills into .claude/skills/<name>/ across many repos, zskills can find them all:
zskills scan ~/Desktop/code
Then promote a project's skills to user scope (so they're available everywhere):
zskills migrate ~/Desktop/code/some-project # add to user; leave project alone
zskills migrate ~/Desktop/code/some-project --remove-from-project
zskills migrate ~/Desktop/code/some-project --dry-run
Both plugin enables (enabledPlugins) and Agent Skill directories under .claude/skills/ get promoted.
Promoting duplicated skills across a tree
If the same skill exists in many projects (common with authored-in-place agent skills), migrate-skill and migrate-all move it to user scope and optionally clean every project.
# Promote one skill found in many projects
zskills migrate-skill performance-tracking-skill --root ~/Desktop/code --dry-run
zskills migrate-skill performance-tracking-skill --root ~/Desktop/code --remove-from-all
zskills migrate-skill performance-tracking-skill --root ~/Desktop/code --source owner/repo
# Interactive sweep: walk the whole tree and prompt per duplicated skill
zskills migrate-all ~/Desktop/code --threshold 3 # only skills in ≥3 projects
zskills migrate-all ~/Desktop/code --threshold 2 -y # non-interactive (no source, keep project copies)
migrate-skill hashes each project's copy of the named skill and warns if content has diverged before picking the first as canonical. Both commands append [[agent_skills]] entries to your skills.toml so the migration is reproducible — and accept --source owner/repo (or prompt for it interactively) so skills with an upstream repo get tracked and refreshed by future sync runs. Skills without a known upstream get a name-only manifest entry; they're tracked in inventory but sync won't fetch them.
Design
~/.claude/settings.jsonis authoritative for what runs.installed_plugins.jsonis the inventory. Three states matter: on-disk-and-enabled, on-disk-and-disabled, on-disk-and-orphaned.doctorreconciles them.- Atomic JSON writes. Tempfile + rename. Preserves every unknown field —
hooks,permissions,env, anything Claude Code adds — round-trip safe. name@marketplacequalification. Same syntax Claude Code uses. No invention.- Git is shelled out. Reuses your credential helpers; no
libgit2to bundle. - No async network unless needed. Marketplace caches live on disk;
git pulldoes the work.
Optional features
zskills ships vanilla by default — the binary only talks to local marketplace caches you already trust. Optional capabilities are gated behind cargo features so they aren't even compiled in unless you ask for them.
Without the feature, zskills marketplace add skills.sh returns "unrecognized marketplace source" — no dormant code paths, no env-var detection, nothing.
Status
v0.6 — search <query> across registered marketplaces, marketplace add-recommended seeder for trusted defaults, optional skills-sh federation behind a cargo feature.
v0.3 — adds migrate-skill (promote ONE skill across all projects in a tree) and migrate-all (interactive sweep with per-skill prompts for source + cleanup). Agent skill entries now support optional source (local-only entries are valid). Manifest writes preserve existing comments via toml_edit.
v0.2 — declarative sync for both plugins and Agent Skills, scan/migrate over both, atomic settings.json round-trip, doctor reconciliation across all three states (settings, inventory, disk).
Lockfile semantics, info, and full version pinning still to come.
Roadmap: third-party marketplace drivers
The skills-sh cargo feature is a holding pattern for "one known remote index, ship it as opt-in code." It's the right shape today; it stops being the right shape if two or three other remote indexes want to plug in.
When that happens, the planned move is a subprocess plugin protocol rather than more cargo features:
- Drivers ship as separate binaries on
$PATH, namedzskills-driver-<name>(git-style extension pattern). - Marketplaces with
source.source = "remote-index"get matched to a driver by URL host (or an explicitdriverfield on the entry). - zskills exec's the driver with a JSON request on stdin (
{"method": "search", "params": {"query": "stripe", "limit": 25}}) and reads a JSON response from stdout. Methods:search,resolve(slug → install coordinates), and optionallyaudit. - Drivers can be written in any language, version independently, and ship under their own trust models.
What this buys: third parties can publish drivers without forking zskills, and the core binary doesn't grow with each new index. What it costs: a stable wire protocol commitment, subprocess overhead, and a public surface to support long-term. It's worth it once there are at least two or three confirmed driver consumers — building it for one half-confirmed consumer (skills.sh, gated behind API keys) is premature abstraction. Until then, --features skills-sh is the right ergonomic.
If you maintain a remote index that would want to plug in, open an issue so we can size demand.
Sister project
The zot24/skills marketplace is what this tool was originally built to manage.
License
MIT