Command reference
The complete skillsvault command set. Every command returns through a single dispatcher that prints any error and sets the exit code; the one exception is gate, whose exit code is the protocol the harness hook reads.
skillsvault <command> [flags]
Exit codes at a glance
Most commands exit 0 on success, 1 on error. try exits 0 even when it shows a block — it is a demonstration, not a gate. scan exits 0 even when it flags something unless you pass --fail-on-flag. gate is special: 0 for allow and warn, 2 for block. A Ctrl-C interrupt exits 130 (128 + SIGINT). An unknown command exits 1.
login
Sign in and authorize this machine via the browser device-code flow. Mints an agent key for you — no key needed up front. See Install & connect.
skillsvault login [--url <base>] [--headless] [--machine <name>] [--import-scan [--with-content]]
skillsvault login --key <agent-key> --org <slug> [--url <base>] [--machine <name>]
| Flag | Default | Meaning |
|---|---|---|
--url | https://skillsvault.io | control-plane base URL (see below) |
--machine | hostname | machine name recorded in the fleet |
--headless | off | print the URL + code instead of opening a browser |
--import-scan | off | after connecting, upload this machine's scan — metadata only; see Importing a scan |
--with-content | off | with --import-scan: also upload the files of unmanaged skills, subagents and commands, so the console can import them |
--key | — | agent key: connect without the browser, exactly like configure |
--org | — | org slug; required with --key |
The control-plane URL is resolved as: --url flag, then the SKILLSVAULT_URL environment variable, then the URL this machine is already configured for, then https://skillsvault.io. For local development: SKILLSVAULT_URL=http://localhost:3000 skillsvault login.
Once authorized, login syncs the bundle straight away (CLI 0.4.0 and later) — there is no separate sync step — and prints the next step: skillsvault try, then skillsvault install-hook, when your org already bans a skill; otherwise skillsvault pull --all and a link to the dashboard. configure does the same.
✓ authorized · org acme
✓ bundle v1750848000 verified & synced · 4 skills · 3 active policies
→ next: skillsvault try — watch a banned skill get blocked here
→ then: skillsvault install-hook — enforce it in Claude Code
If that first sync fails, the machine stays connected and the hint is → next: skillsvault sync.
Exit: 0 authorized · 1 denied / expired / timed out, --with-content without --import-scan, or (with --import-scan) the upload was refused — the machine stays connected · 130 Ctrl-C.
logout
Remove this machine's local config (org + agent key) and, unless told otherwise, the synced bundle. Idempotent — running it when nothing is configured is a no-op.
skillsvault logout [--keep-bundle]
--keep-bundle clears credentials but leaves ~/.skillsvault/bundle.json in place. Exit: 0 / 1.
configure
Connect non-interactively with an org slug and an agent key (CI, headless). Pins the signing key, then syncs and prints the next step exactly like login. login --key <key> --org <slug> is the same command.
skillsvault configure --org <slug> --key <agent-key> [--url <base>] [--machine <name>] [--import-scan [--with-content]]
| Flag | Default | Meaning |
|---|---|---|
--org | — (required) | organisation slug |
--key | — (required) | agent key (skillsvault_ak_…) |
--url | https://skillsvault.io | control-plane base URL — same resolution as login (--url > SKILLSVAULT_URL > saved config > default) |
--machine | hostname | machine name |
--import-scan | off | upload this machine's scan after connecting — as for login |
--with-content | off | with --import-scan: include the files of unmanaged skills |
Exit: 0 / 1 (missing --org/--key is an error, as is --with-content without --import-scan).
sync
Pull and verify the signed policy bundle, then store it locally. A bundle whose Ed25519 signature does not verify against the pinned key is rejected and the last-good bundle is kept.
skillsvault sync [--harness <h>] [--quiet]
✓ bundle v1750848000 verified & synced · 4 skills · 3 active policies
→ next: skillsvault pull
login, configure and try all sync for you, and from CLI 0.6.0 the gate refreshes a bundle older than an hour in the background (as sync --quiet, which prints nothing on success); run sync on its own to pick up a policy change straight away. --harness (default claude_code) is the harness this machine reports as in the fleet. sync also tells the control plane this machine is alive and what its hook covers, and prints an upgrade hint on stderr when a newer CLI is out.
sync also sends any audit events a gate had to queue while the control plane was unreachable (~/.skillsvault/audit-spool.jsonl, CLI 0.6.0 and later), and says so on stderr: ✓ delivered 3 audit events queued while the control plane was unreachable. pull, try and login do the same. See The runtime gate.
It also reports which invocation paths this machine's Claude Code hook gates. If the hook covers only some of them (the "Skill"-only config from before 0.5.1), it prints one line to stderr naming what isn't gated and the settings file to paste install-hook's output into. detect prints the same line.
Exit: 0 / 1 (network error, non-200 status, bad envelope, or signature failure).
try
The first-run demonstration: see a skill your org banned get blocked on this machine. try syncs the bundle (exactly as sync does), picks a skill, gates it exactly as gate does — local evaluation, the decision printed, then the audit event shipped (or queued, if the control plane doesn't answer within 1.5 seconds).
skillsvault try [--skill <ns/skill>]
| Flag | Default | Meaning |
|---|---|---|
--skill | a banned skill | the skill to try; otherwise the first skill in the bundle whose status is banned, preferring one whose name contains demo |
✓ bundle v1750848000 verified & synced · 4 skills · 3 active policies
▸ skillsvault try gating acme/demo-banned as an agent on this machine would…
⛔ BLOCKED acme/demo-banned@1.0.0
Skill version is banned by the organisation.
This is what your agents will see.
audit: https://skillsvault.io/acme/audit
→ skillsvault install-hook — enforce it in Claude Code
Without --skill, try picks a skill the gate blocks — banned, or (from CLI 0.6.7) covered by a Block policy — preferring one named "demo". When nothing is blocked yet, try says so and links the console's skills page (/<org>/skills), where you can ban one and run try again.
Exit: 0 — even when the skill is blocked. try is a demonstration, not a gate; use gate (exit 2 on block) wherever an exit code must veto something. 1 on error (not configured, sync failed).
pull
Install the org's approved skills into a harness; a banned skill already present in the folder pull installs into for that harness is removed (the governance action). Copies elsewhere on disk — a project's own skills folder, say, or an agent this pull didn't install into — aren't deleted (they may be someone's edits); from CLI 0.6.7, pull names each one at the end of its output so it can be removed. Writes SKILL.md plus any bundled scripts//references//assets/ files, rejecting unsafe paths (absolute or ..). Reports the installed inventory back to the fleet via a heartbeat.
skillsvault pull [--harness <h>] [--all] [--dir <skills dir>]
| Flag | Default | Meaning |
|---|---|---|
--harness | claude_code | target a single harness by key |
--all | off | install into every detected harness on the machine |
--dir | — | override the skills directory (e.g. for tests / unknown harnesses) |
✓ acme/hubspot-crm-entry@1.3.0
✓ acme/code-reviewer@1.0.0 (subagent)
⛔ removed banned acme/teamleader-crm-entry
✓ pulled into /Users/me/.claude/skills
2 installed · 1 banned removed (1 banned total)
With --all, each detected harness gets its own ▶ <harness> <skills dir> heading and summary line.
pull also records, in ~/.skillsvault/names.json, the name Claude Code will invoke each installed subagent or skill by when its frontmatter name differs from its file name — the gate uses it to recognise the artifact. Run pull after upgrading the CLI so that file is written.
Each harness gets its artifacts exactly where it reads them — see Harness coverage. A subagent or command goes only to a harness that loads that kind as markdown; elsewhere it is skipped with a note. A harness with no Agent Skills support (Aider) is skipped by --all and refused by --harness. Every skill folder pull writes carries a .skillsvault-pulled marker; copies older CLI versions left in directories a harness doesn't read are removed when pull can prove it wrote them, and nothing else there is touched.
With --all and no harness detected, it errors and suggests --harness or --dir. Exit: 0 / 1.
publish
Publish a skill folder to the org registry, creating a new immutable, current version. Reads SKILL.md (required) and an optional skillsvault.json manifest. The registry stores the SKILL.md frontmatter description as the skill's description; the manifest's description is used only when the frontmatter has none. Bundles every file under scripts/, references/, and assets/.
skillsvault publish <path> [--name <org/skill>] [--version <semver>]
| Input | Source | Notes |
|---|---|---|
| path | first non-flag dir arg, else . | folder containing SKILL.md |
| name | --name, else skillsvault.json.name | required; namespace/skill-name |
| version | --version, else skillsvault.json.version | defaults to 1.0.0 |
| intent / targets / description | skillsvault.json | matcher inputs |
✓ published acme/hubspot-crm-entry@1.3.0 (2 bundled files)
Publishing needs the owner, admin or governance officer role. A member's publish is recorded as a submission for review instead; nothing is published until a reviewer approves it:
✓ submitted acme/hubspot-crm-entry@1.3.0 for review
Not published yet: an owner, admin or governance officer approves it.
https://skillsvault.io/acme/requests/…
(CLIs before 0.6.5 print this as publish refused (202): Not published yet: submitted for review… and exit 1; the submission is recorded either way.)
Exit: 0 (published or submitted) / 1 (no SKILL.md, missing name, or a refusal from the registry). See Authoring & publishing.
detect
List the AI agents/harnesses found on this machine and how each is governed. Claude Code, the harness with a vetoing hook, is labelled from the hook actually installed — read from .claude/settings.json and .claude/settings.local.json in the current directory and ~/.claude/settings.json, as sync reports it (CLI 0.6.0 and later):
| Label | Meaning |
|---|---|
| enforced (hook) | a hook runs skillsvault gate for skills, subagents and typed /commands |
| partially enforced (hook misses …) | a hook runs the gate but not for every path — typically the "Skill"-only config from before 0.5.1 |
| not enforced (no gate hook) | nothing runs the gate: skills are distributed, nothing vetoes them |
Every other harness is distribution only.
skillsvault detect
skillsvault detect — AI agents found on this machine
• claude_code /Users/me/.claude/skills not enforced (no gate hook)
• cursor /Users/me/.cursor/skills distribution only
• aider no Agent Skills support — pull skips it
→ skillsvault pull --all installs approved skills into all of the above
detect prints one line to stderr naming the fix when Claude Code's hook covers only some invocation paths (as sync does) or when Claude Code is installed with no gate hook at all:
⚠ no skillsvault gate hook is installed — Claude Code runs every skill unchecked; run skillsvault install-hook and paste its output into .claude/settings.json
Exit: 0. See Harness coverage.
scan
Inventory every skill, subagent, command and rule file your AI agents already have, with simple local risk flags. Available from CLI 0.3.0. Needs no account, no config, and no network — it is safe to run on any machine before you sign up.
skillsvault scan [--dir <path>] [--json] [--quiet] [--fail-on-flag]
| Flag | Default | Meaning |
|---|---|---|
--dir | — | also scan this directory (folders with SKILL.md are skills, other files are rules) |
--json | off | machine-readable output (schema below) |
--quiet | off | print only flagged items — no header, summary, or call to action |
--fail-on-flag | off | exit 1 if anything is flagged (for CI) |
Where it looks. Every location each harness skillsvault knows reads from (Harness coverage) — the same table pull installs from — both globally under $HOME and per project under the current directory: skill directories, the subagent and command directories where the harness has them, and rule/instruction files (.cursor/rules, .clinerules, GEMINI.md, .github/copilot-instructions.md, …), where each file is a rule. A directory several harnesses share (~/.agents/skills) is listed once.
What it reports per item: name, path, kind (skill / subagent / command / rule), whether a skill has a SKILL.md, whether its frontmatter carries name + description (commands need only description), and whether it is managed — it carries a skillsvault.json manifest, it carries the marker pull writes into every skill folder it installs, or it resolves to an artifact of the same kind in this machine's synced bundle.
Risk flags. Each flag is a narrow, explainable heuristic with a one-line reason — a prompt to look, never a verdict. Only risk flags mark an item as flagged; info flags are shown for context because they are common in perfectly legitimate skills.
pipe-to-shell(risk) — a download piped into a shell:curl … | sh,wget … | bash,sh -c "$(curl …)",bash <(curl …).powerful-tools(risk) —allowed-tools(or a subagent'stools) grantsBash,Write, orWebFetch.secret-paths(risk) — references~/.ssh/ SSH key names, a.envfile, or a credentials file (.aws/credentials,.git-credentials,credentials.json,.netrc,.docker/config.json).base64-blob(risk) — a base64 run longer than 1 KB.scripts(info) — bundles executable scripts (.sh,.py,.js, …, anything underscripts/, or anything with the executable bit).external-urls(info) — links to external domains (loopback andexample.*ignored).
skillsvault scan — what your AI agents already have (local only, nothing sent)
▶ claude_code · global ~/.claude
✓ code-review skill managed
⚠ deploy-helper skill unmanaged flagged
! pipe-to-shell downloads and pipes into a shell (curl|sh) in SKILL.md
! powerful-tools allowed-tools grants Bash, Write
! secret-paths references ~/.ssh (scripts/setup.sh)
· scripts bundles 1 script(s): setup.sh
· external-urls links to 1 external domain(s): get.example-tools.io
• linked-skill skill unmanaged symlink outside scanned dir — not read
• ship command unmanaged
▶ cursor · project ./.cursor
• style.mdc rule unmanaged
3 skills, 1 command, 1 rule file across 2 agents · 4 unmanaged · 1 flagged
Govern these → skillsvault login --import-scan
The summary counts each kind separately (CLI 0.6.0 and later). With nothing found it says so for the 17 agents skillsvault supports — Aider, which has no skill discovery, and the shared .agents/skills location aren't counted as agents. The last line appears only when this machine is not yet connected to an org: it connects the machine and brings this inventory with it (see Importing a scan). Colour follows the usual rules (NO_COLOR, TERM=dumb, --no-color, or a non-terminal stdout turn it off).
Safety guarantees. scan itself sends nothing over the network (only login --import-scan does, and only what is listed below), reads at most 256 KB of any file, and never follows a symlink that resolves outside the directory being scanned — such an entry is listed with symlink_escape: true and not read.
--json schema
schema_version is bumped on any breaking change; new fields may be added without a bump. Flag ids and severity values are part of the contract.
{
"schema_version": 1,
"cli_version": "0.6.5",
"configured": false,
"summary": { "items": 5, "harnesses": 2, "unmanaged": 4, "flagged": 1 },
"groups": [
{
"harness": "claude_code",
"scope": "global",
"root": "/Users/me/.claude",
"items": [
{
"name": "deploy-helper",
"kind": "skill",
"path": "/Users/me/.claude/skills/deploy-helper",
"has_skill_md": true,
"frontmatter_valid": true,
"has_manifest": false,
"managed": false,
"symlink_escape": false,
"flagged": true,
"flags": [
{ "id": "powerful-tools", "severity": "risk", "reason": "allowed-tools grants Bash, Write" }
]
}
]
}
],
"errors": []
}
| Field | Meaning |
|---|---|
scope | global (under $HOME), project (under the current directory), or dir (from --dir, harness custom) |
kind | skill, subagent, command, or rule |
managed_by | present when managed is true: manifest, pull (installed by skillsvault pull) or bundle |
flagged | the item has at least one risk flag |
errors | unreadable directories, reported without failing the scan |
Exit: 0 — even with flagged items — unless --dir is missing or output cannot be written (1). With --fail-on-flag, 1 when summary.flagged > 0.
Importing a scan (--import-scan)
Available from CLI 0.5.0. skillsvault login --import-scan (or configure --import-scan) connects the machine, then runs the same scan and uploads it, so your org's console shows what this machine's agents already have — under Harnesses → Skills found on your machines — instead of an empty fleet. Without the flag, login and configure upload nothing about local skills.
What is uploaded, by default — metadata only:
- per item: harness, scope (
global/project), name, kind, managed or not (and how), whether it is flagged, each flag'sid, severity and one-line reason, and asha256content hash (the same hash the registry gives a published version with identical content); - the path as
scanshows it —~/…for an item under your home directory,./…for one under the directory you ran the command in. Never an absolute path, and only items inside the harness skill directories (--dirlocations are never uploaded); - the machine name and CLI version.
With --with-content it also uploads the files of unmanaged skills, subagents and commands — SKILL.md and the folder's text files, through the same 256 KB read cap and symlink boundary as scan. Managed items, rule files and anything a symlink points outside the scanned directory never carry content. The CLI says which of the two it is sending before it sends it.
Caps. At most 500 items per upload (the output says when more were found); content at most 512 KB and 100 files per item and 2.5 MB per upload — an item over a cap is listed without its content. Each upload replaces the machine's previous one.
▸ import-scan uploading this machine's scan (metadata only: names, kinds, risk flags and content hashes — no file contents)
✓ imported 5 items · 4 unmanaged · 1 flagged
→ review them: https://skillsvault.io/acme/harnesses
In the console, every org member sees each machine's items with their unmanaged / flagged badges and flag reasons. A writer (owner, admin, governance officer) can, for each unmanaged item:
- Import into registry — when its content was uploaded: creates
<your-namespace>/<name>at0.1.0. Otherwise the console shows the command to run on that machine, e.g.skillsvault publish ~/.claude/skills/deploy-helper --name acme/deploy-helper. - Ban this name — creates an active Specific skill policy with action Block for
<your-namespace>/<name>, with the flag reason as its explanation. It ships in the next bundle.
A machine attributed to one member cannot have its scan replaced by another member's key (409).
gate
Evaluate a skill against the synced bundle and emit allow / warn / block. Full behaviour, including hook mode, is on The runtime gate.
skillsvault gate [--skill <name>] [--harness <h>] [--machine <name>] [--quiet]
| Flag | Default | Meaning |
|---|---|---|
--skill | — | skill to evaluate; if omitted, read from the hook payload on stdin |
--harness | claude_code | harness label recorded in the audit event |
--machine | config machine | machine-name override for the audit event |
--quiet | off | only emit on warn / block |
--skill takes any name the harness might send — acme/deploy, deploy, /deploy, acme:deploy — resolved as described on The runtime gate.
Exit: 0 allow · 0 warn · 2 block · 1 when the machine has no bundle (or isn't configured and has none). The decision needs only the bundle, so a machine signed out with logout --keep-bundle still gates. In Claude Code hook mode the gate always exits 0: the veto is in the JSON, and with no bundle it fails open with a message rather than an error.
whoami
Show which organisation this machine is connected to — the org name, slug, and plan, confirmed with the control plane. Offline (or with a revoked key) it still prints the locally configured org, marked (could not verify online).
skillsvault whoami
Exit: 0, or 1 when the machine isn't configured.
status
Show the current config and synced-bundle freshness.
skillsvault status # human-readable
skillsvault status --json # machine-readable
The --json shape:
{
"org": "acme",
"base_url": "https://skillsvault.io",
"machine": "macbook-pro",
"bundle": {
"version": 1750848000,
"skills": 4,
"policies": 3,
"received_at": "2026-06-25T10:00:00Z"
}
}
bundle is null when nothing has been synced yet. Exit: 0 / 1.
install-hook
Print the Claude Code hook config that wires the gate in: a PreToolUse hook with matcher Skill|Agent|Task (skills, and subagents under both tool names) and a UserPromptExpansion hook (a /command the user types). It only prints — paste the block into your Claude Code settings (~/.claude/settings.json for every project, or a project's .claude/settings.json). The command path is the absolute path of the running binary.
skillsvault install-hook
Add this to your Claude Code settings (.claude/settings.json) to gate skills,
subagents and commands:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Skill|Agent|Task",
"hooks": [
{
"type": "command",
"command": "/usr/local/bin/skillsvault gate"
}
]
}
],
"UserPromptExpansion": [
{
"hooks": [
{
"type": "command",
"command": "/usr/local/bin/skillsvault gate"
}
]
}
]
}
}
A machine set up before CLI 0.5.1 has a hook with matcher "Skill" and no UserPromptExpansion entry, which never sees subagents or typed commands: run install-hook again and replace the old skillsvault entry with the new block. See Harness coverage. Exit: 0.
version
Print the binary version.
skillsvault version # also: --version, -v
# -> skillsvault 0.6.5