CLI Commands
The full spm command surface. Run spm --help or spm <command> --help for the authoritative, version-specific usage.
spm init [--target amp|claude|cline|codex|copilot|cursor|gemini|windsurf ...] [-g] # scaffold ai.json (repeatable / comma-separated)
spm add <git> (--tag|--branch|--commit <v>) \ # add + install a skill
[--path <subdir>] [--name <local-name>] [--all] [--force] [-g] # --all: add every skill under --path
[--plugin] # --plugin: add a full plugin instead of a skill
spm target add [vendor ...] # add target vendor(s); no arg = pick interactively
spm remove <name> [--plugin] [-g] # drop a skill (or a plugin with --plugin)
spm update [name] [-g] # re-resolve branches/tags to latest
spm install [-g] # rebuild from ai.lock (after clone)
spm list [-g] # show skills + pinned commits
spm status [-g] # check skills are materialized in this checkout
spm clean [-g] # remove generated vendor config
spm prune [--yes] # wipe the global fetch cache ($SPM_HOME/store, default ~/.spm/store)
spm scan [path] # scan skill content for suspicious patterns (default: .)Scope: project (default) vs. global (-g)
Every command except target add and prune accepts -g / --global. Without it, the command operates on the project in the current directory. With it, the command manages a user-global set of skills available to your AI tools in every project:
- The global manifest + lock live under
$SPM_HOME(default~/.spm/ai.json/~/.spm/ai.lock) and reuse the same fetch cache as project installs. - Global skills materialize into user-global vendor locations:
~/.copilot/skills/<name>/for Copilot,~/.gemini/skills/<name>/for Gemini,~/.agents/skills/<name>/for Codex,~/.cursor/skills/<name>/for Cursor,~/.cline/skills/<name>/for Cline,~/.codeium/windsurf/skills/<name>/for Windsurf,~/.config/agents/skills/<name>/for Amp, and a marketplace under$SPM_HOME/claude-global/registered in~/.claude/settings.jsonasspm-global(skills invoked as/spm-global:<name>) for Claude. - The shared-dir tools' global dirs (Copilot, Gemini, Codex, Cursor, Cline, Windsurf, Amp) are shared with your hand-authored skills, so spm only touches the entries it manages there — it never wipes the directory.
spm statuswarns when a skill name is installed in both scopes, since the two collide by name at discovery time.
spm init -g --target copilot
spm add -g https://github.com/org/repo --tag v1.0.0 --name reviewer
spm status -gCommand details
spm init
Scaffolds an ai.json. --target is repeatable and comma-separated to declare one or more vendors up front.
spm add
Adds a skill to ai.json, resolves it to an immutable commit, pins it in ai.lock, and materializes it — in one step. Provide exactly one version selector:
| flag | meaning |
|---|---|
--tag | git tag (annotated tags deref to commit) |
--branch | branch tip at install/update time |
--commit | exact commit |
--path <subdir>selects a subdirectory (for monorepos holding many skills).--name <local-name>sets theai.jsonkey for the skill.--alladds every skill under--path(each immediate subdirectory with its ownSKILL.md), keyed by directory name.--allcannot be combined with--name.--pluginadds a full plugin instead of a single skill (see below). Point--pathat the plugin root (the directory holding.claude-plugin/plugin.json).--plugincannot be combined with--all.
spm add https://github.com/org/repo --tag v1.0.0 --path skills --allAdding a full plugin (--plugin)
Beyond individual skills, spm can install a Claude Code plugin — one that bundles agents, MCP servers, hooks and scripts in addition to (or instead of) skills. Pass --plugin and point --path at the plugin root:
spm add https://github.com/camunda/design-system --branch main \
--path plugins/camunda-design-system --plugin --name design-systemThe plugin is written to the plugins map in ai.json, pinned in ai.lock, and materialized:
- Claude gets the whole plugin registered under a dedicated, project-local
spm-pluginsmarketplace (.spm/claude-plugins/), so its agents, MCP servers, hooks and scripts all load. - Every other target (Copilot, Gemini, Codex, …) gets the plugin's bundled skills, flattened into that target's normal skills location.
A bundled skill whose name collides with a standalone skills entry (or another plugin's skill) is a hard error, never a silent overwrite.
spm target add
Adds one or more target vendors. With no argument, prompts you to pick interactively.
spm remove <name>
Drops a skill from ai.json (and its materialized output). Pass --plugin to remove a full plugin (from the plugins map) instead of a skill:
spm remove reviewer # drop a skill
spm remove design-system --plugin # drop a pluginspm update [name]
Re-resolves branch/tag selectors to their latest commit and updates ai.lock. With no name, updates all skills.
spm install
Rebuilds the materialized skills from ai.lock. This is the command teammates run on a fresh clone and in each new worktree.
spm list
Shows declared skills and their pinned commits.
spm status
Checks that declared skills are materialized in the current checkout. Exits non-zero when anything is missing or a Claude marketplace pointer is stale — so it works in scripts and git hooks. See Worktrees & Fresh Clones.
spm clean
Removes generated vendor config from the project.
spm prune [--yes]
Wipes the global fetch cache ($SPM_HOME/store, default ~/.spm/store). --yes skips the confirmation prompt.
spm scan [path]
Runs spm's deterministic content scanner over a path — a single file or a directory (default: the current directory) — and prints every finding. Exits non-zero when any blocking (high/critical) finding is present, so it works as a CI gate on skill sources.
The same scan runs automatically as a pre-materialize gate: every skill and plugin is scanned right after it is fetched into the store and before it is copied into any agent-discovered directory, on spm add, spm install, and spm update. High/critical findings abort the command (nothing is materialized and ai.lock is not written); medium/low findings are surfaced as warnings.
Detected pattern categories:
| category | example signals | severity |
|---|---|---|
| prompt injection | "ignore previous instructions", "disregard your system prompt" | high |
| secret exfiltration | ~/.ssh/id_rsa, .aws/credentials, GITHUB_TOKEN (escalated when paired with curl/post/send) | low → critical |
| obfuscation | zero-width/bidi Unicode; base64/hex blobs decoding to shell; files exceeding the 8 MiB scan cap | high / critical |
| command execution | curl | bash, /dev/tcp/…, nc -e reverse shells | critical |
| path traversal | ../../ requested in skill text | medium |
| auto-run | postinstall scripts, git hooks, bundled Makefile | low / medium |
spm scan # scan the current directory
spm scan ./my-skill # scan a specific pathTo override the gate for content you trust (or a false positive), set SPM_ALLOW_SUSPICIOUS=1: findings are printed as warnings but never block.
Repo URLs (HTTPS & SSH)
git accepts any URL the system git understands:
spm add https://github.com/org/repo --tag v1.0.0 # HTTPS
spm add git@github.com:org/repo.git --branch main # SSH (scp-style)
spm add ssh://git@github.com/org/repo.git --branch main # SSH (url form)SSH auth goes through your ssh-agent / keys — spm never handles credentials. Private HTTPS repos use your git credential helper. spm runs git with GIT_TERMINAL_PROMPT=0, so a missing credential fails with a clear error instead of hanging on a prompt (helpers and ssh-agent still work).