Commands reference
Full reference for every zskills subcommand. Flags are shown with their defaults. Run zskills <cmd> --help for the up-to-date help.
Conventions
- CLI group to manifest key:
plugin→[[skills]];skill→[[agent_skills]];mcp→[[mcps]]. <name>accepts the unqualified skill name (e.g.servarr) when unambiguous, orname@marketplace(e.g.servarr@zot24-skills) when multiple marketplaces declare the same skill. This matches Claude Code’s own syntax.- Most commands print colored output. Set
NO_COLOR=1to disable, or pipe throughcatif you need plain text. CLAUDE_HOME=/custom/pathoverrides~/.claudefor testing.AGENTS_HOMEoverrides~/.agents.XDG_CONFIG_HOMEandXDG_CACHE_HOMEare respected for manifest/cache locations.
list
What’s currently installed, with each item’s enabled/disabled/orphaned status. Covers both Claude Code plugins and Agent Skills. Each plugin and Agent Skill line names which harnesses can see it: claude, pi, hermes, kimi, grok, codex. A Claude-only plugin is not a Pi install. Harnesses that have no cited skill root are listed as skipped (unsupported), not as present.
zskills list [--json] [-v] [--paths]
| Flag | Default | Description |
|---|---|---|
--json | off | Emit a machine-readable JSON document for scripting |
-v, --verbose | off | Expand grouped agent skills (show every skill name in each source group) |
--paths | off | Show the on-disk location of each entry: plugin install path under ~/.claude/plugins/cache/..., agent skill directory under ~/.agents/skills/..., or the settings file an MCP server is declared in |
The non-JSON output groups results into four plugin buckets (active, installed-but-disabled, enabled-but-not-installed, installed-from-missing-marketplace) plus two Agent Skill buckets (managed by zskills, on-disk-but-untracked), plus a final MCP Servers section that aggregates every server visible to Claude Code from all of:
~/.claude.jsonand~/.claude/settings.json— user scope<cwd>/.mcp.jsonand<cwd>/.claude/settings.json— project scope<cwd>/.claude.local/settings.json— local (gitignored) scope/Library/Application Support/ClaudeCode/managed-settings.json(macOS) //etc/claude-code/managed-settings.json(Linux) — managed (org-deployed) scope- An attribution column reads each enabled plugin’s
plugin.json/ sibling.mcp.jsonand marks entries with★ plugin:<name>when the server is bundled by a plugin.
Servers are grouped by scope (managed → local → project → user) and only the keys of env / headers are surfaced (no values), so the output never leaks secrets even when ${VAR} refs aren’t used. Set ZSKILLS_MANAGED_SETTINGS=<path> to override the managed-settings probe (useful in CI).
install
Three accepted spec shapes:
zskills plugin install <name> # plugin from a registered marketplace
zskills plugin install <name>@<marketplace> # qualified plugin
zskills plugin install <name> --harness pi,grok # copy the plugin's skill trees into the shared Agent Skill hub
zskills skill install <owner>/<repo> # Agent Skill(s) from a git repo
zskills skill install <git-url> # same, for https://, git@, file://
zskills skill install <owner>/<repo> --path REL # Agent Skills under a non-conventional root
zskills plugin install -i # interactive picker over marketplace plugins
Plugin path (<name> / <name>@<marketplace>). Resolves against registered marketplaces. --harness is a one-shot override of [defaults].harnesses (comma-separated: claude, pi, hermes, kimi, grok, codex). Missing [defaults] and missing --harness keep 1.0 behaviour: Claude only. claude in the set flips enabledPlugins in ~/.claude/settings.json and materializes the marketplace plugin. pi / grok / codex / hermes copy each of the plugin’s skill trees into the shared hub ~/.agents/skills/<name>/ so SKILL.md sits at the scan root. Which trees ship is the plugin author’s call: when .claude-plugin/plugin.json carries a skills array, zskills resolves every entry against the plugin root and ships exactly those, the same array Claude Code reads — an empty array ships nothing. Without the array, zskills walks skills/ the way it surveys any Agent Skill root: a child holding a SKILL.md is a skill, a child without one is a category read one level deeper, so skills/<category>/<name>/SKILL.md works too. A declared entry that resolves outside the plugin root is refused and the whole plugin fails. Pi and Grok scan the hub itself; Codex and Hermes are symlinked from it into ~/.codex/skills/<name> and ~/.hermes/skills/<category>/<name>. Copies into the hub are real directories — a plugin cache path is version-stamped, so a link there would break on upgrade. kimi prints unsupported and does not invent a folder.
Repo path (<owner>/<repo> or git URL). Clones the repo via git clone --depth 1 into ~/.cache/zskills/agent-skills/<owner>-<repo>/, surveys the tree, and:
- Agent Skills (any directory under
skills/<name>/SKILL.mdor.agents/skills/<name>/SKILL.md) — installed to~/.agents/skills/<name>/(the cross-client convention; visible to Claude Code, Grok CLI, and any other compliant client). Inventory tagged with the source. --path REL— walkRELinside the clone instead of those default roots. IfREL/SKILL.mdexists, that directory is the skill. Else every child withSKILL.mdis a skill.--pathis sparse-intent: a repo with.claude-plugin/marketplace.jsonstill installs from that path instead of redirecting tomarketplace add. After a successful install, zskills appends an[[agent_skills]]row:marketplacewhen the spec matches a registered marketplace (and the copy reuses that clone), elsesource.- Marketplace (
.claude-plugin/marketplace.jsonat repo root) — without--path/--skill/-i, prints a redirect:use zskills marketplace add <owner/repo>and installs nothing from this repo. - MCP servers (
.mcp.jsonormcpServersinplugin.json) — surfaced as a hint; not auto-installed (use[[mcps]]inskills.toml+zskills sync).
| Flag | Default | Description |
|---|---|---|
-i, --interactive | off | For plugin specs: without any <name>, browse all marketplace plugins with a fuzzy picker. For repo specs: always opens a multi-select picker over the Agent Skills found in the repo. |
--all | off | For repo specs only: when the repo contains more than 5 Agent Skills, confirm “yes, install every one.” Without --all, large collections abort with a sample summary so they don’t silently flood ~/.agents/skills/. Ignored for repos with ≤5 skills (those install everything by default). |
--skill <name> | none | For repo specs only: install exactly one skill by name (the manifest name field) — the non-interactive counterpart to -i for multi-skill repos. Bypasses the >5-skill size policy (the selection is explicit) and conflicts with --all. |
--path <rel> | none | For repo specs only: relative path inside the clone to a directory of Agent Skills. Replaces the default walk of .agents/skills and skills/. Sparse-intent like --skill / -i: do not redirect a marketplace root. Writes [[agent_skills]] with marketplace or source. Ban .., absolute form, \, :. |
--harness <list> | [defaults].harnesses, or Claude only | Comma-separated harness names. One-shot override. Not --to. |
Repo-path count behavior:
| Skills in repo | Default (no flags) | With -i | With --all | With --skill <name> |
|---|---|---|---|---|
| 0 | error | error | error | error |
| 1 | install it | install it | install it | install if it matches |
| 2–5 | install all | picker | install all | install just that one |
| > 5 | abort + summary + next-step hint | picker | install all | install just that one |
Sparse installs (root-level SKILL.md). A repo whose SKILL.md sits at the repo root inside a larger project (source code, lockfiles, websites…) is installed sparsely: only SKILL.md, the conventional skill dirs (references/, assets/, scripts/), and relative paths that SKILL.md links to are copied out — never the whole source tree, and never .git/. Installs from the skills/<name>/SKILL.md layout copy the skill subdirectory as before. Legacy full-repo installs are flagged by zskills doctor and slimmed on the next zskills skill upgrade (or doctor --fix).
remove / purge
remove is apt-style: disable in enabledPlugins, drop the inventory entry, and drop the matching [[skills]] row from the manifest so sync does not re-enable the plugin. Bytes stay on disk so re-enabling is instant. purge does the same plus deletes the bytes from ~/.claude/plugins/cache/.
zskills plugin remove <name>...
zskills plugin remove -i
zskills plugin purge <name>...
| Flag | Default | Description |
|---|---|---|
-i, --interactive | off | (remove only) When passed without any <name>, browse enabled plugins with a multi-select picker and remove the selection. |
enable / disable
Flip a plugin’s enabledPlugins flag without (un)installing.
zskills plugin enable <name>...
zskills plugin disable <name>...
plugin enable fails if the plugin is not in enabledPlugins or the plugin inventory. plugin disable keeps bytes and inventory; use plugin purge to delete bytes.
skill
Agent Skills live in ~/.agents/skills/. The group writes [[agent_skills]], not [[skills]].
zskills skill install <owner/repo> [--path REL] [--skill NAME | --all | -i]
zskills skill remove <name> [--force] [--file path]
zskills skill upgrade [<name>...]
--path REL installs Agent Skills that do not live at .agents/skills or skills/. Example: zskills skill install nvk/llm-wiki --path plugins/llm-wiki-opencode/skills --skill wiki-manager. That command does not redirect to marketplace add. After install, the manifest row uses marketplace when the spec matches a registered marketplace, else source.
skill remove deletes bytes. It is not plugin remove. --force is required when inventory source starts with plugin:, or when a source-only [[agent_skills]] row owns the name. A marketplace: hub copy of the same name removes without --force and without disabling the plugin.
sync (headline command)
Apply a declarative skills.toml manifest. Diffs intent against current state, then atomically writes the necessary settings.json and inventory changes.
zskills sync [--file <path>] [--dry-run] [--prune | --adopt]
| Flag | Default | Description |
|---|---|---|
--file <path> | $XDG_CONFIG_HOME/zskills/skills.toml (then ~/.config/zskills/skills.toml) | Path to skills.toml. ./skills.toml is NOT auto-loaded — pass --file ./skills.toml to use a project-local manifest. (This caused destructive surprises in v0.5; the v0.5.1 default is safer.) |
--dry-run | off | Print the plan; do not write |
--prune | off | Allow destructive removals. Without --prune, agent skills present on disk but absent from the manifest are reported as skip and left untouched. With --prune, their bytes are deleted from ~/.agents/skills/. |
--adopt | off | Inverse of --prune. Append every orphan (installed agent skill, enabled plugin, configured MCP that isn’t yet in the manifest) to skills.toml and exit. Useful for capturing a hand-curated environment into your manifest in one shot. Mutually exclusive with --prune. |
What sync does:
- For each
[[marketplaces]]entry withrepoorurlthat is not yet registered: clone the source and writeknown_marketplaces.jsonplusextraKnownMarketplaces(same asmarketplace add). This runs before plugin resolve, so a fresh machine can recreate the clone. Unqualified[[skills]]names (nameonly) are resolved against the map after that clone, so they can match a marketplace that did not exist at plan time. - For each
[[skills]]entry: resolvename@marketplace. Ifharnesses(or[defaults].harnesses) containsclaude, write toenabledPlugins. Entries currently enabled but not in the manifest get flipped off. Hub-backed harnesses (pi,grok,codex) copy nestedskills/<name>/trees into~/.agents/skills/in the same pass.syncrefuses to write anenabledPluginskey whose marketplace is not registered. It reports the unresolved plugins and exits non-zero instead of printing✓ applied.Hub copies for those plugins are skipped too. - For each
[[agent_skills]]entry: classify the row by kind.sourceclones/pulls the Agent Skill cache.marketplace+pathcopies from the registered marketplace clone (hard error if the clone is missing — this is not a local-only row). Ifpathis set, copy from that relative directory inside the clone instead of the default skill roots. Ifnpmis present, runnpm install -g --no-fund --no-audit <pkg>(orinstall_cmd), then claim all matchingclaimsglobs. If none ofsource/marketplace/npmis present (justname), register the existing on-disk skill in inventory without fetching anything. Nested plugin hub copies skip names claimed by[[agent_skills]]. Same-marketplaceplugin:hub copies are taken over. A copy or resolve error increments a failure count:syncthen skips✓ applied.and exits non-zero. - Agent skills tracked in inventory but missing from the manifest are reported. With
--prunethey’re deleted; with--adoptthey’re appended to the manifest; otherwise they’re skipped.
--adopt details
When you pass --adopt, sync writes new entries to skills.toml instead of removing anything:
- Registered marketplace not in manifest → new
[[marketplaces]]row withnameplusrepo(owner/repo) orurl(non-GitHub git source). Source comes fromknown_marketplaces.json/extraKnownMarketplaces. Remote-index entries are skipped. This is what makes a latersyncon a fresh machine able to clone. - Registered marketplace already in the manifest with only
name/pin→ fillrepoorurl. Existingpinis left untouched. A row that already has a source is skipped. - Enabled plugin not in manifest → new
[[skills]]row withname+marketplace. - Agent skill in inventory but not in manifest → new
[[agent_skills]]row. The fields are reconstructed from the inventory tag:localbecomes a name-only entry,npm:pkgbecomesnpm = "pkg",marketplace:<name>:<path>becomesmarketplacepluspath(rsplit on the last:),source:<git>:<path>becomessourcepluspath(rsplit on the last:, so a git URL that contains:stays intact),plugin:…is not adopted as[[agent_skills]], and any other string becomessource = "..."only. - MCP server configured but not in manifest → new
[[mcps]]row with full transport details (command/args/envfor stdio,url/headersfor http/sse),scopepreserved. Env and header values are copied verbatim — if any contain literal secrets, eyeball the resulting manifest and replace them with${VAR}references before committing.
Adoption is idempotent and de-duplicates against existing entries, so re-running sync --adopt after editing the manifest is safe.
Sync is idempotent. Run it on every fresh machine to reproduce your global state from a single file.
If a ./skills.toml exists in CWD when you run sync without --file, zskills prints a yellow warning telling you it’s being ignored — pass --file ./skills.toml if that’s actually what you wanted.
upgrade
The one command for refreshing everything zskills manages — marketplaces, git agent skills, and npm agent skills.
zskills skill upgrade [<name>...]
| Source kind | What upgrade does |
|---|---|
| Plugins (marketplace-based) | For each registered marketplace tap, git pull --ff-only if it’s a git working tree; otherwise fetch the GitHub archive tarball from the source recorded in known_marketplaces.json and atomically swap the tree. Claude Code picks up new plugin versions on next start. |
Git agent skills (source = "owner/repo") | git pull the cached source clone + re-copy bytes. Honour path when set. |
Marketplace Agent Skills (marketplace + path) | Re-copy from the marketplace clone after marketplace refresh. Does not clone into the Agent Skill cache. Filter matches the marketplace name. Unnamed rows refresh names already tagged marketplace:<name>:<path>. |
npm agent skills (npm = "pkg") | Run npm install -g <pkg> (or install_cmd), then re-apply the claims glob to retag inventory |
Pass specific names to upgrade just those; empty = upgrade everything. The name filter matches against the manifest’s npm, source, name, or marketplace fields.
MCP servers manifest schema
Add [[mcps]] tables to skills.toml to declaratively manage MCP servers. sync writes them to the appropriate runtime config file based on scope:
# Stdio: a process the runtime spawns directly.
[[mcps]]
name = "github"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
env = { GITHUB_TOKEN = "${GITHUB_TOKEN}" }
scope = "user" # default; also "project" or "local"
# HTTP: a remote server reachable over HTTP.
[[mcps]]
name = "linear"
url = "https://mcp.linear.app/mcp"
transport = "http" # optional; inferred from `url`
scope = "user"
# Stdio + mcp-remote proxy pattern (real-world): args reference ${VAR} too.
[[mcps]]
name = "honcho"
command = "npx"
args = [
"mcp-remote",
"https://mcp.honcho.dev",
"--header", "Authorization:${HONCHO_AUTH}",
"--header", "X-Honcho-User-Name:${USER_NAME}",
]
env = { HONCHO_AUTH = "${HONCHO_AUTH}", USER_NAME = "${USER}" }
scope = "user"
| Field | Purpose |
|---|---|
name (required) | MCP server name. Becomes the key under mcpServers. |
command | Stdio: the executable to run. Required for stdio transports. |
args | Stdio: list of args. ${VAR} refs are allowed and recommended for credentials. |
env | Stdio: env-var map. Values should use ${VAR} refs. |
url | HTTP/SSE: server URL. |
headers | HTTP/SSE: header map. Same ${VAR} policy as env. |
transport | "stdio" | "http" | "sse". Optional; inferred from command or url. SSE is the deprecated spec form — prefer http. |
scope | "user" (default) | "project" | "local". "managed" is not accepted (read-only). |
Write targets per scope:
| Scope | File | Notes |
|---|---|---|
user | ~/.claude.json | The file claude mcp add --scope user writes to. Wrapped JSON ({"mcpServers": {...}}). |
project | <cwd>/.mcp.json | Spec-recommended team-shared file. Created wrapped; if it already exists with the legacy flat schema, the existing shape is preserved. |
local | <cwd>/.claude.local/settings.json | Gitignored personal+creds file. Wrapped. |
Sync semantics.
syncinstalls every MCP from the manifest and overwrites overlapping entries (manifest is source of truth).- Without
--prune, MCP entries currently in the runtime config but absent from the manifest are reported asskip— same safety pattern as[[agent_skills]]. - With
--prune, those entries are removed from their settings file. - Plugin-injected MCPs are never pruned. If a server name matches one declared by an enabled plugin, it’s owned by that plugin and zskills won’t touch it.
- Managed scope is never written. Entries appear in
list/doctorbutsyncignores them.
Secret handling. Values in env/headers should be ${VAR} references; the actual secret stays in your shell. Literal values are not blocked but they land in the JSON verbatim — which means they’d go straight into git-committed files at project scope. The recommendation is documentation, not enforcement.
Agent skills manifest schema
The [[agent_skills]] table supports these fields, used in combination:
# Git-sourced single skill
[[agent_skills]]
source = "jakubkrehel/make-interfaces-feel-better"
# Git-sourced multi-skill repo, install just one
[[agent_skills]]
source = "owner/multi-skill-repo"
name = "specific-skill"
# Nested tree: Agent Skills that are not at `.agents/skills` or `skills/`
[[agent_skills]]
source = "owner/odd-layout"
path = "packages/foo/skills"
name = "foo"
# Reuse a registered marketplace clone (one clone, several packaging trees).
# Pair with [[skills]] harnesses = ["claude"] — see llm-wiki example below.
[[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"]
# npm-distributed package
[[agent_skills]]
npm = "get-shit-done-cc"
claims = ["gsd-*"] # glob patterns; every match in ~/.agents/skills/ is owned by this entry
# npm with custom installer command
[[agent_skills]]
npm = "some-tool"
install_cmd = "npx some-tool setup"
# Local-only (just tracked, never refreshed from a remote)
[[agent_skills]]
name = "my-internal-tool"
# Many skills from one source, one stanza
[[agent_skills]]
source = "mattpocock/skills"
skills = ["prototype", "research", "tdd", "wayfinder"]
| Field | Purpose |
|---|---|
source | Git source: owner/repo (GitHub) or full git URL. sync/upgrade clone/pull and copy. Mutex with npm and marketplace. |
marketplace | Name of a registered marketplace. Reuse that clone instead of cloning source into the Agent Skill cache. Requires the clone to exist (hard error if missing). Mutex with source and npm. Inventory tag is marketplace:<name>:<path>. |
path | Relative directory inside the cloned source or marketplace. Replaces the default walk of .agents/skills and skills/. If path/SKILL.md exists, that directory is the skill (name = last segment). Else every child with SKILL.md is a skill. Requires source or marketplace. Must be relative: no .., no absolute form, no \, no :. Inventory tag is source:<source>:<path> or marketplace:<name>:<path>. |
npm | npm package name. sync/upgrade runs npm install -g <pkg>. |
install_cmd | Custom installer command — overrides the default npm install -g. Used for packages with their own setup CLI. |
name | Optional. For source entries, pick a single skill out of a multi-skill repo. For local-only entries, required — names the on-disk skill to track. |
skills | Optional list. The plural form of name: one stanza picks many skills out of the same source, marketplace or npm package, and writes source once instead of once per name. Every other key on the stanza (path, harnesses, claims, install_cmd) applies to every name in the list. An entry declares name or skills, never both — a stanza carrying both is refused when the manifest loads. An empty skills = [] means what an absent key means: the row keeps whatever it meant before. |
harnesses | Optional list. Names which harnesses can see this Agent Skill. Empty inherits [defaults].harnesses, then every harness whose home exists. Set it on marketplace+path rows. The llm-wiki recipe uses ["pi", "grok"] so Claude does not also get the OpenCode tree. |
claims | Glob patterns (e.g., ["gsd-*"]) matched against ~/.agents/skills/. After install, every match is tagged with this entry’s source. Used for npm packages whose installer touches pre-existing directories — so the diff-after-install discovers nothing, but claims retroactively claims ownership. |
llm-wiki for Claude, Pi, and Grok
nvk/llm-wiki is one source repo. It ships a Claude plugin and OpenCode Agent Skills. A plugin and an Agent Skill are different things. Declare both.
Requires zskills 1.3.0 or later. 1.3.0 is the series after 1.2.0 that copies Agent Skills from a marketplace clone via marketplace and path. A 1.2 binary ignores unknown keys. It treats marketplace and path as absent. The [[agent_skills]] rows then become local-only. sync prints ✓ applied. and copies nothing from the OpenCode tree.
[[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"]
Set harnesses on both rows. Do not omit them. A plugin row that omits harnesses inherits [defaults].harnesses. If that default includes pi or grok, sync copies the Claude nested skill onto the hub. An Agent Skill row that omits harnesses inherits every harness whose home exists. That can symlink the OpenCode tree into ~/.claude/skills/.
repo lets sync clone the marketplace on a fresh machine. The Agent Skill rows reuse that clone. They do not clone nvk/llm-wiki a second time.
Do not run zskills skill install nvk/llm-wiki. That command sees .claude-plugin/marketplace.json and redirects to marketplace add. Write the [[agent_skills]] rows.
What each harness consumes:
| Harness | What it consumes |
|---|---|
| Claude Code | Plugin wiki@llm-wiki. Slash commands /wiki:*. Nested Claude wiki-manager. bin/llm-wiki in the plugin cache. |
| Pi | Hub Agent Skills wiki-manager and wiki-query at ~/.agents/skills/. Pi lists them under [Skills]. wiki-query is the read-only lookup skill. |
| Grok | The same hub Agent Skills. Grok scans ~/.agents/skills/. Slash /wiki-manager and /wiki-query. |
What this recipe does not provide:
- zskills does not install
scripts/pi-wiki-query. That is a launcher. It is not an Agent Skill. - zskills does not write
~/.pi/agent/prompts/wiki-query.md. That path is a Pi prompt file. Official lookup is the hub Agent Skillwiki-query. - zskills does not provide Grok slash
/wiki:*. zskills does not write~/.grok/config.toml. It does not install a Grok plugin. - zskills copies
SKILL.mdas-is. Pi warns when a skilldescriptionexceeds 1024 characters. OpenCodewiki-manageris over that limit innvk/llm-wiki. Truncate it upstream. Do not edit the hub copy.syncoverwrites local edits.
Apply with zskills sync. Workflow: Use cases → Declare llm-wiki.
update
Refresh every registered marketplace’s git cache (or just the named one).
zskills update [<marketplace-name>]
Runs git pull --ff-only against each marketplace clone. Claude Code reads the cache on next start to detect new versions.
doctor
Reconcile disk ↔ inventory ↔ settings, and statically validate every configured MCP server. Reports drift in four categories:
- Plugins enabled in
enabledPluginsbut not present ininstalled_plugins.json(broken references — Claude Code will fetch on next start). - Plugins in inventory whose marketplace tap is no longer registered.
- Agent skills tracked in inventory but missing from
~/.agents/skills/on disk. - MCP server issues — static checks against every server returned by
list:- stdio:
commandmust resolve on$PATH(useswhich-style lookup). - any transport: every
${VAR}reference inenv(stdio) orheaders(http/sse) must be set in the user’s environment. - sse transport: flagged as deprecated; the spec recommends migrating to
http.
- stdio:
zskills doctor [--fix]
With --fix, dangling plugin/inventory references are removed. --fix is a no-op for MCP issues — none of them are auto-fixable (we won’t install a missing binary or invent an env var), so doctor’s job there is purely to surface what’s broken. --fix never deletes installed bytes — that’s what purge is for.
Doctor never spawns or talks to an MCP server. Running-state diagnosis (whether a server is actually connected, last error, latency) is Claude Code’s job — replicating it here would risk divergent diagnoses. If you need that, run the server directly or check Claude Code’s own logs.
scan
Walk a directory tree looking for project-scope skills.
zskills scan [<path>] [--depth N] [--json] [--mcp]
| Flag | Default | Description |
|---|---|---|
<path> | . | Tree to walk |
--depth N | 6 | Maximum directory recursion depth |
--json | off | Machine-readable output |
--mcp | off | Also report each project’s MCP servers (name and kind only; never env or header values). Without this flag, MCP-only projects are not listed and the JSON has no mcps key. |
Detects:
.claude/settings.jsonor.claude/settings.local.jsonwithenabledPlugins/extraKnownMarketplaces(project-scope plugin enables).claude/skills/<name>/SKILL.md(project-scope Agent Skills)- with
--mcp:<project>/.mcp.json(wrapped{"mcpServers": {…}}or a flat top-level map) andmcpServersin.claude/settings.json/settings.local.json
The default depth of 6 catches both patterns from a ~/Desktop/code-style parent directory (Agent Skills are at depth 5 from the project root).
migrate
Promote ONE project’s enabled plugins, project-scope Agent Skills, and MCP servers to user scope.
zskills migrate <project> [--remove-from-project] [--dry-run]
Reads <project>/.claude/settings.json (or settings.local.json) and <project>/.claude/skills/<name>/. Writes promoted plugin enables into ~/.claude/settings.json’s enabledPlugins and copies Agent Skill directories into ~/.agents/skills/.
MCP servers are promoted too. Sources are <project>/.mcp.json (both schemas), <project>/.claude.local/settings.json, and the .claude/settings*.json files above. Each raw JSON entry is copied verbatim into ~/.claude.json, env and header values included. Plugin-provided servers are skipped. A name already present at user scope is left alone.
--remove-from-project clears enabledPlugins, extraKnownMarketplaces, .claude/skills/, and promoted MCP keys from the project after a successful promote.
migrate-skill
Promote ONE Agent Skill that appears in many projects across the tree, in a single operation.
zskills migrate-skill <name> [--root <dir>] [--source <ref>]
[--remove-from-all] [--dry-run]
| Flag | Default | Description |
|---|---|---|
--root <dir> | . | Tree to search |
--source <ref> | none (local-only) | If set, install from upstream (owner/repo or git URL) instead of copying canonical |
--remove-from-all | off | Delete the skill’s .claude/skills/<name>/ from every matched project |
--dry-run | off | Print the plan; do not write |
For each matched project, the skill’s directory is hashed and compared. If content diverges, the first project (alphabetical) wins as canonical and a warning is printed showing which projects have which hash. The promoted skill gets a [[agent_skills]] entry appended to your skills.toml so the migration is reproducible.
migrate-all
Interactive sweep across a tree.
zskills migrate-all <dir> [--threshold N] [--yes] [--dry-run]
| Flag | Default | Description |
|---|---|---|
<dir> | required | Tree to walk |
--threshold N | 2 | Only consider skills appearing in ≥N projects |
--yes, -y | off | Skip prompts; accept defaults (no source, keep project copies) |
--dry-run | off | Print planned action per skill; do not write |
For each duplicated skill above the threshold, prompts:
- “Promote ‘
’ to user scope? [Y/n]” - “Upstream source [owner/repo, URL, or blank for local-only]:”
- “Remove project copies from N project(s)? [y/N]”
Calls migrate-skill under the hood for each accepted prompt.
marketplace
Tap management — register, list, refresh, and remove Claude Code marketplaces.
zskills marketplace add <owner/repo | git-url>
zskills marketplace add-recommended
zskills marketplace remove <name>
zskills marketplace list [--json]
zskills marketplace update [<name>]
add clones the marketplace repo into ~/.claude/plugins/marketplaces/<name>/ and writes both known_marketplaces.json and settings.json’s extraKnownMarketplaces. <name> is the name field in the clone’s .claude-plugin/marketplace.json. If that file is missing, unparseable, or its name is not a single safe path segment, add falls back to the repo basename. Two repos that declare the same name collide: the second add is refused and the first tap is left as-is. Mirrors what /plugin marketplace add does inside Claude Code.
Adding a marketplace does not install plugins and does not change the manifest. After a successful add, zskills reads .claude-plugin/marketplace.json and prints the plugins the marketplace offers, plus the next command.
If the clone also has Agent Skill trees at plugins/*/skills/ that are not inside a plugin source from marketplace.json, add prints those Agent Skill names and a [[agent_skills]] stanza. It does not write the stanza to the manifest.
✓ added marketplace llm-wiki
1 plugin: wiki
Next: zskills plugin install wiki@llm-wiki
Agent Skills under plugins/llm-wiki-opencode/skills: wiki-manager, wiki-query
[[agent_skills]]
marketplace = "llm-wiki"
path = "plugins/llm-wiki-opencode/skills"
name = "wiki-manager"
[[agent_skills]]
marketplace = "llm-wiki"
path = "plugins/llm-wiki-opencode/skills"
name = "wiki-query"
If marketplace.json is missing, add still registers the clone and warns. list also prints a dimmed hint when no plugins are active and a registered marketplace offers at least one.
add-recommended seeds the trusted defaults (currently just anthropics/claude-plugins-official). Idempotent — safe to re-run; existing marketplaces are left as-is.
Pinning a marketplace
By default marketplace update, update and upgrade all git pull every registered
marketplace, so a marketplace tracks whatever its default branch moves to. Declare a pin in
skills.toml to hold one at a tag, branch, or full sha:
[[marketplaces]]
name = "llm-wiki"
repo = "nvk/llm-wiki" # or url = "https://…" — required so sync can clone on a fresh machine
pin = "v0.23.0" # tag, branch, or full sha
repo is GitHub owner/repo. url is any git URL. When both are set, repo wins. sync registers any declared marketplace that is not yet in known_marketplaces.json (clone + extraKnownMarketplaces) before it resolves [[skills]]. A [[marketplaces]] row with only name and pin still pins an already-registered marketplace; it cannot recreate the clone.
A pinned marketplace is checked out at that ref and never pulled. marketplace list
marks it [pinned v0.23.0]. If the clone has drifted, the next update puts it back:
$ zskills update
Updating llm-wiki ... pinned @ d02cbcb (restored)
Three details worth knowing:
- The ref is resolved from what the clone already has. Only if that fails does zskills
git fetch, which cannot moveHEAD. - A pin that cannot be resolved is an error, not a fallback. zskills will not pull a marketplace it was told to hold, so a typo in a pin freezes the marketplace rather than floating it.
- A pinned clone sits on a detached
HEADon purpose. Leaving it on a branch invites the next pull to move it.
Remove the pin line (or blank it) to let the marketplace float again.
The pin lives in skills.toml, not in known_marketplaces.json. That file belongs to
Claude Code, which validates it and rejects the whole file when it disagrees — one entry
missing lastUpdated is enough to break every claude plugin install. skills.toml is
also the half of the configuration that is meant to be shared between machines;
known_marketplaces.json holds machine-local absolute paths.
add skills.sh is recognized only when zskills was built with --features skills-sh. It registers skills.sh as a remote-index source type (no git clone) and is dispatched by search and install via the HTTP API. See Optional features below.
search
Keyword search across every registered marketplace. Substring-matches <query> against name + description in each marketplace’s cached marketplace.json. Purely local — no network calls.
zskills search <query> [--limit <n>] [--json] [-i]
| Flag | Default | Description |
|---|---|---|
--limit <n> | 25 | Maximum results per marketplace |
--json | off | Emit results as a JSON array for scripting |
-i, --interactive | off | After printing results, open a picker; selecting one installs it. |
With the skills-sh cargo feature compiled in AND ZSKILLS_SKILLS_SH_API_KEY set, search also federates to the skills.sh remote index and tags those results [skill]. Without the env var, the registered remote-index is skipped with a one-line hint and local search continues uninterrupted.
Optional features
zskills ships vanilla by default. Optional capabilities are gated behind cargo features so they aren’t even compiled into the binary unless you ask for them.
| Feature | What it adds | How to enable |
|---|---|---|
skills-sh | Federated search + install against the skills.sh remote index. Registers a new remote-index source type. Runtime activation requires ZSKILLS_SKILLS_SH_API_KEY. | cargo install --git https://github.com/zot24/zskills --features skills-sh |
Without the feature, zskills marketplace add skills.sh returns “unrecognized marketplace source” — there’s no dormant code, no env-var detection, nothing. The compiled binary is byte-identical to a feature-free build except for what you explicitly asked for.
fzf integration (auto-detected)
install -i, search -i, and remove -i automatically use fzf when it’s on $PATH, which gets you full fuzzy filtering and the familiar fzf keybindings. When fzf is not installed, zskills falls back to the built-in dialoguer picker (FuzzySelect for single-select, MultiSelect for multi). Set ZSKILLS_NO_FZF=1 to force the dialoguer path even when fzf is installed.
install fallback (skills.sh feature only)
When skills-sh is built in and a remote index is registered with a valid key, install <name> will fall through to skills.sh if the spec doesn’t resolve in any local plugin marketplace. It performs an exact-slug match against the skills.sh search API and, on hit, routes through the existing Agent Skill install path (git clone source/repo → drop SKILL.md into ~/.agents/skills/<name>/). No enabledPlugins flip — agent skills don’t use that gate.