CLI Reference

skill manages declared skills. All resolution, installation, state, and recovery operations use AgentsCore and AgentsSkills. Commands match the skills operations of agents; instruction declarations, locked records, and target files remain untouched.

Commands

skill add <source> [names…] [--all]
skill list [source] [--json]
skill remove <names…>
skill update [names…] [--apply | --json]
skill init [name]
skill watch
skill watch add <source> [--path <dir>] [requirement options] [--scope project|user]
skill watch status [--json] [--scope project|user]
skill watch diff <source> [name] [--kind skill|instructions|artifact] [--json] [--scope project|user]
skill watch seen <source> [--revision <rev>] [--scope project|user]
skill watch remove <source> [--scope project|user]
skill install [--json]
skill doctor [--root <dir>…] [--json]

skill --help, skill watch --help, and each command's --help describe their options. Running without a subcommand prints help. skill watch runs watch status and accepts its options. skill --version prints skill X.Y.Z and is available only on the root command; the version constant lives in Sources/CLI/EntryPoint.swift.

Root help includes:

Examples:
  skill add github.com/owner/skills pdf-forms
  skill list
  skill install
  skill update               Preview what re-resolving would change
  skill update --apply       Write the updates and the new lock

Shared Options

Option Meaning Commands
--scope project|user Where state lives: project (./.agent) or user (~/.agents). Default: project. All lifecycle and read commands; init uses the current directory
--agent <agent> Agent to install for: codex, claude-code, cursor, gemini-cli or opencode. Repeat for several. Default: the agents already recorded for this skill or source, else the declaration's agents, else codex and claude-code. add
--agent <agent> Limit to this agent: codex, claude-code, cursor, gemini-cli or opencode. Repeat for several. Default: all recipients. list, remove, doctor
--path <subdir> Search this subdirectory of the source. add, list <source>, watch add
--full-depth Discover recursively beyond conventional skill directories add, list <source>
--mode link|copy|edit How to install: link (default), copy, or edit (a live link to a local source). add
--root <dir>… Audit these project directories read-only. Repeat for several. doctor
--json Emit a versioned JSON report on stdout list, install, doctor, update previews, watch status, watch diff

Agents are codex, claude-code, cursor, gemini-cli, and opencode. Core also accepts claude and gemini as input aliases. Output uses canonical IDs. On add, omitted recipients use the agents already recorded for the skill or source, then declaration defaults (initially Codex and Claude Code). On installed list, remove, and doctor, omission covers all recipients. Source listing shows available skills regardless of recipients. Doctor's agent filter limits installation checks; source checks cover the scope.

Updates use declared paths, recipients, and modes. Installation restores those recorded in the lock. Neither update nor install accepts a recipient or mode override. --path always refers to source discovery, never a project root; change directory for project operations or use doctor's --root for auditing. Unsupported options are usage errors.

Adding and Inspecting Sources

source is a local path, Git URL or shorthand accepted by core, well-known HTTP source, or an existing declaration alias. Add writes the declaration, installations, and lock together. A skill directory, file, or link that the scope's lock does not manage is never replaced: add and install stop before any change and name the path, so move or remove it first.

<names>: Skills to add. Omit to add the source's only skill, or to list the choices when it has several. Explicit names select those skills. --all selects all current and future members on unnamed updates; combining names and --all is a usage error.

link links verified, pinned cache content. copy installs pinned copies. Both require a Git repository for local sources and read committed content. edit links live local directories and accepts no requirement.

Requirement options on add map directly to declaration forms:

Option Declaration Matching versions
--branch main "branch": "main" Git branch
--revision <rev> "revision": "<rev>" Git revision
--exact 1.2.3 "exact": "1.2.3" Exact semantic version
--from 1.2.3 "from": "1.2.3" At least 1.2.3, below 2.0.0
--up-to-next-minor-from 1.2.3 "upToNextMinor": "1.2.3" At least 1.2.3, below 1.3.0
--from 1.2.3 --to 1.8.0 "from": "1.2.3", "to": "1.8.0" Half-open range [1.2.3, 1.8.0)

Choose one requirement form; --to requires --from. With no requirement, core uses the latest version tag or the default branch if there are no tags.

A source can end in typed selectors instead of options. A selector starts at an @ followed by one of these labels and :, so SSH locations such as git@host:owner/repo keep their @:

Selector Same as
@branch:main --branch main
@ref:v1.2.3 --revision v1.2.3: any branch, tag, or commit
@revision:<rev> --revision <rev>
@exact:1.2.3 --exact 1.2.3
@from:1.2.3 --from 1.2.3
@minor:1.2.3 --up-to-next-minor-from 1.2.3
@path:<subdir> --path <subdir>
@skill:<name> one skill name after the source

skill add larksuite/cli@from:1.0.0@skill:lark-base is skill add larksuite/cli lark-base --from 1.0.0. Use at most one requirement selector, one @path:, and one @skill:; percent-encode an @ inside a value as %40. A selector and an option that give the same field must agree; both replace the branch and path of a hosted tree URL. agents.json stores the structured fields, and the alias comes from the location. watch add accepts requirement and path selectors; @skill: applies to add and list <source>.

A source added by location keeps the alias that already declares it, or takes its last path component. When another source holds that alias, preceding components are prefixed, as in anthropics-skills, then a number is appended.

list lists managed lock records, including missing or drifted installs, one row per recipient. list <source> inspects what a source provides. Source discovery paths can refer to cache materializations and are not stable identifiers; use the source ID and skill name for identity.

Watch

skill watch shares subscriptions with agents watch and reports skills, instruction units, and well-known artifacts through the same core operations.

watch add <source> subscribes without installing content and records the current baseline as seen. Repeating add acknowledges current content again. Sources can be declared aliases, source locations, or existing source IDs. --path and requirement options use the same forms as add. A local source must be a Git repository. Git subscriptions follow the declared branch, or the default branch; version and revision requirements do not select the watched head. Well-known subscriptions follow the index.

watch status, also available as skill watch, reports subscriptions in core's order. Git revisions are abbreviated to seven characters:

local:~/Developer/team-rules  1 commit since 6c09c94 (now 9661957)
  ~ changed  code-review (instructions)   1 file
  ~ changed  pdf   1 file

The Git count is commits reachable from the current head but not the seen revision. A well-known source shows N artifacts changed (singular for one), or up to date. Instruction and artifact names carry (instructions) or (artifact); skills have no suffix. Changed items show 1 file or N files; new and removed items omit the file count. An empty scope prints No watched sources.

watch diff <source> [name] prints each changed item's heading and unified line diffs with three lines of context. Skill paths are relative to the skill root and prefixed by the skill name, such as pdf/SKILL.md. Instruction and artifact paths are relative to the item. --kind skill|instructions|artifact filters changes and disambiguates shared names. With no matching changes, diff prints up to date.

~ changed  code-review (instructions)  local:~/Developer/team-rules  6c09c94 -> 9661957
--- a/code-review.md
+++ b/code-review.md
@@ -2,7 +2,7 @@
 Review checklist:
 Read the change.
 Check correctness.
-Review the code.
+Review the code and tests.
 Check tests.
 Check edge cases.
 Record findings.

Mode changes print mode pdf/run.sh: 644 -> 755; unchanged modes are omitted. Binary content prints Binary file <path> differs. Links print link <path>: <old> -> <new> (none for an absent link); directories print directory <path> added or directory <path> removed.

watch seen <source> acknowledges current content. --revision <rev> resolves a Git revision to its full commit ID and can select an older commit. Well-known sources do not accept --revision. watch remove <source> removes the subscription and baseline while preserving declarations, locks, and installed content. Diff, seen, and remove accept a subscribed alias, source ID, or location.

Mutation confirmations use normalized source IDs:

Watching local:~/Developer/team-rules from 6c09c94.
Marked local:~/Developer/team-rules seen at 9661957.
Stopped watching local:~/Developer/team-rules; installations are unchanged.

Well-known confirmations omit revisions: Watching <source ID>. and Marked <source ID> seen. Add, seen, and remove do not accept --json. Status and diff emit one JSON object each with --json; revisions remain full. They can fetch upstream content without changing declarations, locks, watch baselines, installations, or source working trees. Uncommitted source files are ignored. Scheduling checks is left to the caller.

Watch baselines live in .agent/agents.watch for project scope or ~/.agents/agents.watch for user scope. agents.json owns subscriptions. A missing subscription suggests Run 'skill watch' to list subscriptions; an invalid revision suggests providing a Git revision that resolves to a commit in the watched source. Operation failures exit 1; invalid options and unknown subcommands exit 64, as elsewhere in the CLI.

Update, Restore, Remove, and Initialize

update previews by default, comparing newly resolved declarations against disk. update <names…> limits resolution to named skills; Unnamed update reconciles every declared skill, including membership changes in all selections.

Human update item lines use + new, ~ changed, = repinned, and - removed. Repinned means only the pin moved: content, executable flags, and managed membership are identical. Items sort in that order, then by name. Revisions use seven characters; a resolved version displays as 1.2.0 (abc1234). Without a version, the revision is shown alone; an absent side of an addition or removal shows none. When both sides have neither a version nor a revision, as with live edit links, the entire pin suffix is omitted. Item summaries omit hashes. Managed membership determines whether a pinless skill is new or removed, even when there is no physical file difference.

For example, skill update can report a pin change without a content diff:

= repinned  pdf  local:~/Developer/team-rules  6c09c94 -> 9661957
  = unchanged  codex (link)  .agents/skills/pdf
Preview only. Run again with --apply to write these changes.

Human diffs are for reading. JSON reports carry exact content bytes in base64. Tabs print verbatim; other control characters in payload lines and upstream text use visible escapes such as \r, \x1b, \x00, or \u{202e}.

File comparisons use unified line diffs with three lines of context and --- a/<path>, +++ b/<path>, and @@ hunk headers. New files use --- /dev/null; removed files use +++ /dev/null. Missing trailing newlines are marked with \ No newline at end of file. Empty file creation or removal prints only empty file <path> added or empty file <path> removed. Binary, mode, link, and directory changes use the same rendering as watch diffs. Skill updates include executable-only changes even when the revision and content hash are unchanged. Mode labels 644 and 755 represent the absence or presence of any execute bit, not a complete POSIX permission mask. Content hashes exclude modes.

Skill content differences appear once per skill, using paths relative to its root prefixed by its name. Identical changes across installations are combined. Recipient-specific differences, such as divergent local copies, are labeled by recipient. Installation links moving between cached snapshots are omitted. Links to live edit targets and changes between link, copy, and edit modes remain visible, as do symbolic links within the skill content.

~ changed  pdf  local:~/Developer/team-rules  6c09c94 -> 9661957
  = unchanged  codex (link)  .agents/skills/pdf
--- a/pdf/SKILL.md
+++ b/pdf/SKILL.md
@@ -6,7 +6,7 @@
 PDF checklist:
 Open the document.
 Inspect page sizes.
-Inspect PDF pages.
+Inspect PDF pages and metadata.
 Check metadata.
 Check annotations.
 Review extracted text.
Preview only. Run again with --apply to write these changes.

Each changed skill includes one membership row per previous or prospective agent. Rows show + added, - removed, or = unchanged with the managed installation mode and physical target path. A mode change shows ~ changed and the old and new modes. These labels describe membership, not content: an unchanged recipient can still receive updated files. Shared paths retain a row for each agent while file diffs appear once.

~ changed  pdf  local:~/Developer/team-rules
  = unchanged  claude-code (edit)  .claude/skills/pdf
  - removed  codex (edit)  .agents/skills/pdf
  + added  cursor (edit)  .agents/skills/pdf
Preview only. Run again with --apply to write these changes.

When agents.resolved is missing, the preview begins with Lock missing: earlier membership is unknown. Membership additions do not imply that target files were absent. Applying still rejects existing content that the scope does not manage, including an already-correct live link. A recorded but missing target retains its before membership.

--apply prints the preview before core writes. After success, completion lines use Removed, Added, and Updated for removed, new, and retained recipients of changed skills, respectively, with agents in alphabetical order. Updated includes the old and new pins when either side has a version or revision, using the same pin format as the preview. With no pins it prints Updated <skill> for <agents>. Additions and removals print Added <skill> for <agents>. and Removed <skill> for <agents>.

~ changed  pdf  local:~/Developer/team-rules  1.2.0 (abc1234) -> 1.3.0 (def5678)
  = unchanged  codex (copy)  .agents/skills/pdf
mode pdf/run.sh: 644 -> 755
Updated pdf for codex: 1.2.0 (abc1234) -> 1.3.0 (def5678).

Completions appear only after the skill operation succeeds. Named and all-skill updates print No skill changes. when unchanged, including with --apply. A preview can become stale; --apply resolves again and displays the content it will actually write. --json --apply is a usage error.

Completion lines name their content and destinations. For example, skill add <source> pdf, skill update pdf --apply, and skill remove pdf print:

Installed pdf for claude-code, codex (link).
Updated pdf for claude-code, codex: 6c09c94 -> 9661957.
Removed pdf for claude-code, codex.

install restores exact locked pins, hashes, modes, and recipients. It does not resolve newer versions and takes no source argument. Unavailable pins or content that fails digest verification cause failure.

remove requires names and removes their declarations, files, and lock entries. With --agent, it removes only those recipients. Removing a member of an all selection records an exclusion so an update does not reinstall it.

init [name] creates a skill directory and SKILL.md in the current directory; without a name it creates SKILL.md directly there. Existing files are errors.

Doctor and State

Doctor reports core's skill checks, including missing installations, drift, broken links, newer matching source revisions, and uncommitted source content. --root accepts multiple directories, either after one option or repeated. Each root is an explicit project directory; --root cannot combine with user scope. A nonexistent root is an error. Without roots, project scope uses the current directory and user scope uses HOME.

Scope Declaration Lock Cache
Project .agent/agents.json .agent/agents.resolved .agent/cache/
User ~/.agents/agents.json ~/.agents/agents.resolved ~/.agents/cache/

Project roots are the current directory, not a search up the Git ancestry. Core resolves installation locations, including CODEX_HOME, CLAUDE_CONFIG_DIR, and XDG_CONFIG_HOME when applicable. User state remains under HOME's .agents directory.

Read commands and previews do not recover interrupted transactions or write declarations, locks, or installations. Doctor inspects existing state without fetching sources, writing caches, or creating locks. A pending transaction is reported as skill check pending-recovery. It is an error check: the state is inconsistent until recovery, so doctor sets ok to false and exits 1. Human output includes the affected state directory. To finish recovery, run skill install --scope user for ~/.agents, or run skill install in the project root for that project's .agent directory.

Commands that change a scope finish pending recovery before proceeding. When core reports recovery, the CLI prints a notice on stderr and retries that operation once. A second recovery or any other failure stops the command.

Output and Exit Status

Human-readable stdout is the default. Commands are non-interactive and never prompt; add, remove, init, and install perform their named actions immediately. Only update --apply writes an update. Diagnostics and recovery notices go to stderr. Human paths are project-relative inside the project in project scope, ~/… under HOME, and absolute otherwise. Local source IDs abbreviate HOME, for example local:~/Developer/team-rules. Installed skill listings use the same path rules. JSON retains full revisions, hashes, and absolute paths.

Errors preserve line breaks. The first line starts with Error: ; every continuation line is indented by two spaces, including Git stderr and remote text that itself begins with Error:. Trailing blank lines are dropped. Punctuation is added only when the error's last non-empty line lacks it. The capitalized remedy is its own final sentence on a separate line, indented by two spaces. Tabs print verbatim; all other controls within each line, including ESC, CR, C1, and bidi controls, use visible escapes:

Error: Unknown command 'unknown'.
  Run 'skill --help' for usage.

A directory without a Git repository produces a Git failure and exits 1:

$ mkdir -p "$HOME/not-a-repository"
$ skill add "$HOME/not-a-repository" --all
Error: Git could not clone ~/not-a-repository.
  fatal: repository '~/not-a-repository' does not exist.
  Check Git access and the source location.

Human-readable source text preserves tabs and visibly escapes other control characters.

Code Meaning
0 Success, including help/version, unchanged previews, and doctor warnings
1 Operation failure or a completed doctor report containing an error check
64 Invalid usage, including unknown commands, options, or invalid option combinations

Unknown commands and subcommands report Unknown command '<name>' and exit 64 even when --help or -h comes before or after the unknown name. Command-path validation stops at --; leaf command positionals remain arguments.

Doctor's ok means there are no error checks; warnings do not make it false. A failed operation emits no successful JSON report. JSON stdout contains only one object and a trailing newline; recovery notices remain on stderr.

JSON Schemas

All reports have integer schemaVersion: 1. Object keys are sorted. Arrays follow core's deterministic ordering; doctor roots retain input order. Optional properties are omitted when unavailable, never encoded as null. Filesystem paths, including update file paths, are absolute. Watch item oldPath and newPath are relative to the source search root (--path when present); watch diff file path is item-relative. File bytes use standard base64. Missing before or after means that entry is absent, distinct from an empty file or directory. Clients should ignore unknown properties; incompatible changes require a new schema version. Diagnostic IDs are core-defined strings, not a closed enum.

Command Schema Required root fields
list --json Installed list schemaVersion, kind: "installed", scope, skills
list <source> --json Source list schemaVersion, kind: "source", scope, source, skills
update --json Update schemaVersion, scope, changed, skills
watch status --json, watch --json Watch status schemaVersion, scope, sources
watch diff --json Watch diff schemaVersion, scope, source
install --json Install schemaVersion, scope, skills
doctor --json Doctor schemaVersion, ok, roots

An empty installed list:

{"schemaVersion":1,"kind":"installed","scope":"project","skills":[]}

An empty update preview:

{"schemaVersion":1,"scope":"project","changed":false,"skills":[]}

A healthy doctor report:

{"schemaVersion":1,"ok":true,"roots":[{"root":"/project","scope":"project","ok":true,"checks":[]}]}

Doctor checks include kind: "skills" alongside their IDs, severity, message, and optional path. pending-recovery is an error; other severities come from core. install --json reports restored skill rows with name, agent, mode, and absolute path. These definitions are CLI-owned and do not expose core's encoded lock objects.

Update skill rows include optional oldVersion and newVersion from the resolved version tags. Absent sides, live edit installations, well-known artifacts, and Git resolutions without a selected version tag omit those properties. Full oldRevision/newRevision and oldHash/newHash retain their existing meaning. Regular file snapshots add boolean executable alongside kind and base64.

Each skill row also includes lockMissing and membership. Membership rows have agent, absolute physical path, before, and after. Each side is either { "kind": "absent" } or { "kind": "installed", "mode": "link|copy|edit" }. Rows cover the union of recorded and prospective recipients, sorted by canonical agent ID, including unchanged results. Agents sharing a path keep separate membership rows while physical file changes remain deduplicated. Membership is managed state, independent of physical presence: a missing target can still have installed before membership. lockMissing: true means before membership is unrecorded; it does not establish ownership of an existing target. These fields are additive within schema version 1; older reports can omit them.

Watch sources entries (and the single diff source object) include alias, normalized source, seen, current, optional Git commitCount, and changes. Each baseline is either { "revision": "<full commit ID>" } or { "artifacts": { "<name>": "sha256:…" } }. Changes include name, kind: "skill" | "instructions" | "artifact", change: "new" | "changed" | "removed", fileCount, and available oldPath, newPath, oldDigest, and newDigest. Diff adds files with path and optional before/after snapshots; regular files include kind: "file", base64, and boolean executable. Directory and symlink entries use the same shapes as update previews. Status omits file snapshots. Artifact digests may change with zero file differences; fileCount remains zero.