# SkillCatalog: Full Agent Reference > The workspace for your team's AI skills. SkillCatalog manages AI skills (prompt instructions stored as Markdown files with YAML frontmatter) in Git-backed catalogs. A desktop app handles interactive authoring. The CLI (`skc`) handles catalog registration, local profile lifecycle, explicit delivery-target settings, catalog inspection, delivery, validation, status reporting, scoring, proposal inspection, and improvement publish flows. Authoring and taxonomy mutation stay in the desktop app and editor-agent workflows. Skills are delivered to AI tools (Claude Code, Cursor, Codex) as SKILL.md files in tool-specific directories. ## CLI reference Binary: `skc` ### Global options (available on every command) --json Render output as JSON when the command supports it. --quiet Suppress non-essential command output. --json takes precedence when both are given. --config-dir PATH Override the SkillCatalog configuration root directory. Defaults to ~/.skillcatalog. Managed catalog clones, profiles, proposals and logs all move with it. ### Commands #### skc init Alias for `skc profile init`. Scaffold a committed `.skillcatalog/skillcatalog.yml` under the current directory. Fails if one already exists. --source SOURCE Remote Git URL via SSH (git@host:path), ssh://, or HTTP(S) without passwords, tokens, query strings, or fragments. --catalog-id CATALOG_ID Intrinsic catalog id to write into the manifest's catalogs block and each entry's `catalog_id` field. If omitted, `skc profile init` derives a starter value from the source URL and you should confirm it matches the catalog's `catalog.yaml.id`. --id SLUG Stable committed profile id. --display-name NAME Human-readable profile display name. --entry KIND[:CATALOG_ID]:SLUG Initial entry (repeatable). KIND is skill, stack, or bundle. Include CATALOG_ID when more than one catalog id is present. `--source` and `--catalog-id` are both repeatable, and the i-th `--source` pairs with the i-th `--catalog-id`. Supplying more `--source` than `--catalog-id` values is a user-input error at exit 10; a `--catalog-id` with no paired `--source` resolves the URL from the registry instead. This is the only command that prompts, and it does NOT check for a terminal first, so an unattended run can block on stdin rather than failing. `--json` disables prompting entirely, which makes `--id` and `--display-name` effectively REQUIRED under `--json`: omitting either exits 10 with `user-input-invalid`. Always pass `--json` plus `--id` and `--display-name` from automation. #### skc install Alias for `skc profile install`. Reads `.skillcatalog/skillcatalog.yml` from the current directory only (no parent search). Creates or refreshes the local child profile, then runs scoped delivery for that profile. A qualifying run first advances the catalogs the checkout's committed and personal manifests declare, from their upstream sources, before validating manifest references, saving the profile, or delivering. A run qualifies when the checkout already has an installed profile, or when it is a first install in which no declared catalog was cloned by that invocation; a first install that clones a declared catalog refreshes nothing. The refreshed set is exactly the declared set, so an undeclared registered catalog is never fetched. A qualifying run therefore needs network access to each declared source. A declared catalog that cannot be refreshed fails the install with catalog-refresh-failed at exit 42 before the profile is saved, so no install-success line or delivery summary is printed. Only up_to_date and updated count as success, matching `skc update`. If scoped delivery reports conflicts or failed writes, install exits 25 with delivery-failed and includes the delivery report in the error payload. No additional arguments. #### skc uninstall Alias for `skc profile uninstall`. Removes the local profile for the current checkout and cleans up delivered files that belong only to the removed profile. No additional arguments. #### skc update Refresh the current directory's manifest-linked catalogs and installed profile. Reads `.skillcatalog/skillcatalog.yml` from the current directory only. Never performs an implicit install or broadens into a global sync. A declared catalog that cannot be refreshed fails with catalog-refresh-failed at exit 42. If scoped delivery reports conflicts or failed writes after refresh, update exits 25 with delivery-failed and includes the delivery report in the error payload. --check Report the pending delivery delta without writing anything. Exits 24 with outcome update-needed when deltas are pending, 24 with outcome delivery-blocked when a delta exists but is blocked, and 0 with up-to-date otherwise. Branch on outcome, not on the code: both non-success shapes share 24. This is the strictest read-only path in the CLI: it does not initialize file logging, run startup sweepers, sync catalogs, apply delivery, or create delivery-manifest locks. #### skc sync [CATALOG_ID...] Bidirectionally reconcile registered catalogs with upstream Git, then deliver. Phases: dirty preflight → optional commit → pull → optional push → deliver. With CATALOG_ID arguments, restricts to the named catalogs. Sync is bidirectional across the CLI, desktop app, and UI. The embedded `skillcatalog-essentials` catalog is read-only across every sync path and is skipped at the application layer. Dirty-tree handling depends on the entry surface: - Interactive TTY: prints a one-line count of the catalogs with pending changes (not a file list or diffstat) and prompts `Commit message (empty to abort):`. Both go to stderr. Non-empty input commits and proceeds; empty input or EOF aborts with exit 0 and no output at all, including under `--json`. Interactive means BOTH stdin and stdout are terminals, so piping or redirecting output takes the non-interactive branch even from a live shell. - Non-interactive (CI, scripts, agent invocations): fails with the typed `dirty-tree` envelope at exit 33 and instructs the operator to rerun with `--message ""` or to clean the worktree manually. - Explicit `--message `: commits with the supplied message, then pulls, pushes, and delivers in both TTY and non-TTY modes. Sync never creates a Git commit without explicit operator intent. Empty messages exit 10 (`empty-message`). Messages over 1000 bytes exit 11 (`commit-message-too-long`). Missing `user.email` or `user.name` is caught in a pre-commit preflight that exits 12 (`missing-config`). Concurrent `.git/index.lock` contention is surfaced as the typed `another-sync-running` envelope at exit 34. Sync commits are auto-prefixed with `[skillcatalog]` in the commit subject, as are desktop saves and desktop publishes, so the prefix does not mean a commit came from the CLI. It is not a complete marker either: app-owned commits in the embedded Essentials catalog and merge commits made during a pull carry no prefix. CATALOG_ID Optional positional arguments. Restrict sync to these catalogs. --message MSG Commit message for pending changes. Required to commit and push when running non-interactively. Optional override for the interactive prompt. --no-deliver Skip the post-reconciliation delivery pass only. This is NOT a read-only flag: the run still commits pending dirty-tree changes and pushes them, on review-branch catalogs to proposal branches. On review-branch catalogs, sync pulls from the configured primary branch and publishes SkillCatalog-created dirty-tree commits as isolated proposal branches under the configured review prefix. Before the post-sync delivery pass, sync refreshes active proposal records and refuses delivery with exit 45 when proposal remote state or proposal metadata cannot be trusted. #### skc deliver [PROFILE_ID] Apply current catalog content to enabled AI tool target files. Does not touch upstream Git in any way. Without PROFILE_ID, delivers every profile. With PROFILE_ID, delivers only that profile. If a selected stack or bundle is not closed over the skill references its delivered content uses, the command fails instead of partially delivering it. `--dry-run` and `--check` are mutually exclusive parser-level modes. Review-branch catalogs run a proposal state preflight before delivery and exit 45 when an active proposal's remote state is unknown or proposal metadata cannot be read. PROFILE_ID Optional positional argument. Deliver only this profile. --dry-run Compute the delivery report without writing files. --check Check for drift only. Returns non-zero exit if drift exists. Per-file outcomes an agent should distinguish. A delivered file that was edited by hand afterwards is SKIPPED, reported under `skipped_drifted`, and does not fail the run; the edit is preserved. A target path that exists but was never delivered by this profile FAILS with reason `unmanaged_target_exists` and the run exits 25 — this is the usual first-run case where a tool directory already holds a skill from another source. A byte-identical pre-existing file is adopted silently instead. To resolve an unmanaged target, delete it and re-deliver, or in the desktop app choose `Replace and deliver`. #### skc profile init Same as `skc init`. See above. #### skc profile install Same as `skc install`. See above. #### skc profile uninstall Same as `skc uninstall`. See above. #### skc profile list List every known local profile. No additional arguments. #### skc profile show PROFILE_ID Show one local profile's metadata and ordered entries. With `--effective`, composes the stored child profile, the checkout's .skillcatalog/skillcatalog.yml, and its sibling .skillcatalog/skillcatalog.local.yml, then prints stored-local, local-manifest, team (annotated when shadowed or excluded), and effective entries. Human labels use kind:slug@catalog_id. `--effective` is inspection only. In `--json` mode, `--effective` adds `local_profile`, `team_profile`, `inherited_profile` (an alias of `team_profile`), and `effective_profile` priority lists; each entry carries `origin` ("local" or "team"), `shadowed_by_local`, and `excluded_by_local`. The per-entry origin value is "team" (not "inherited"); the inherited_profile key is retained only as a backward-compat alias. PROFILE_ID Positional argument. The profile id to inspect. --effective Compose the committed and local manifests and show the stored-local, local-manifest, team, and effective layers. #### skc profile create NAME DIRECTORY Create a new local profile. This path uses the shared safe local-profile creation flow: directory normalization, absolute-path enforcement, generated id allocation, malformed-sibling reservation, and optional first-bind catalog readiness checks. Repeat `--entry KIND:CATALOG_ID:SLUG` to seed the new local profile's priority list; every seeded catalog id must already be registered and resolve to a ready local clone. NAME Positional argument. Human-readable profile name. DIRECTORY Positional argument. Absolute path, or ~ / ~/... --catalog-id CATALOG_ID Optional. Validate first-bind readiness for this catalog id. --entry KIND:CATALOG_ID:SLUG Optional initial priority-list entry (repeatable). The catalog id must be registered and resolve to a ready local clone. #### skc profile delete PROFILE_ID Delete one local profile by id and clean up its delivered files. PROFILE_ID Positional argument. The profile id to delete. #### skc profile add PROFILE_ID Add one entry to a local profile. This command always requires an explicit catalog id on the entry and routes through the shared profile-mutation authority. The entry catalog id must already be registered and ready before the profile is saved. For a profile installed from a committed .skillcatalog/skillcatalog.yml, the entry is written to the checkout's .skillcatalog/skillcatalog.local.yml, not to profile.yml. PROFILE_ID Positional argument. The profile id to mutate. --kind KIND Required. Entry kind: skill, stack, or bundle. --slug SLUG Required. Entry slug. --catalog-id CATALOG_ID Required. Entry catalog id. The catalog must already be registered and ready. --position INDEX Optional. Insert position. Omit to append. #### skc profile remove PROFILE_ID Remove one entry from a local profile. This command always requires an explicit catalog id to identify the entry and routes through the shared profile-mutation authority. Removal stays available even when the referenced catalog is stale. For an installed project-backed profile, removal targets .skillcatalog/skillcatalog.local.yml; when the removed key exists in the committed .skillcatalog/skillcatalog.yml, remove auto-appends it to that file's excludes so the removal survives reinstalls. PROFILE_ID Positional argument. The profile id to mutate. --kind KIND Required. Entry kind: skill, stack, or bundle. --slug SLUG Required. Entry slug. --catalog-id CATALOG_ID Required. Entry catalog id for the entry to remove. #### skc profile reorder PROFILE_ID Replace a profile's ordered entry list. This command keeps `--catalog-id` as a required compatibility flag, but each `--entry CATALOG_ID:KIND:SLUG` carries the catalog identity that is validated for readiness. Reorder cannot add or delete entries by omission: the requested list must contain exactly the existing entries in the new order. Use `profile add` or `profile remove` to change membership. For an installed project-backed profile, reorder rearranges only the local .skillcatalog/skillcatalog.local.yml entries; the requested list must equal the existing local entries. PROFILE_ID Positional argument. The profile id to mutate. --catalog-id CATALOG_ID Required. Compatibility catalog id; entry catalog ids control readiness. --entry CATALOG_ID:KIND:SLUG Required and repeatable. Ordered entry list. Repeat once per existing entry. Each catalog id must already be registered and ready. #### skc settings show Show the current settings snapshot. No additional arguments. `skc settings list` is an alias that delegates here and reports the same `command: "settings.show"` envelope label. #### skc settings enable TARGET_ID Enable one delivery target explicitly through the shared set-style settings authority. After saving the settings change, SkillCatalog attempts delivery for all profiles and reports delivery conflicts, failed writes, or preserved edited files as warnings while keeping the settings change. TARGET_ID Positional argument. One of claude-code, cursor, or codex. #### skc settings disable TARGET_ID Disable one delivery target explicitly through the shared set-style settings authority. After saving the settings change, SkillCatalog attempts delivery for all profiles and reports delivery conflicts, failed writes, or preserved edited files as warnings while keeping the settings change. TARGET_ID Positional argument. One of claude-code, cursor, or codex. #### skc catalog add URL Register and clone a catalog from a remote Git URL. URL Positional argument. Remote Git URL via SSH (git@host:path), ssh://, or HTTP(S) without passwords, tokens, query strings, or fragments. #### skc catalog remove ID Unregister a catalog by its registered id. By default this command refuses to remove a catalog still referenced by installed project profiles. That failure uses `catalog-remove-in-use`, lists referencing profile ids and manifest paths, and exits 32. Pass `--force` to unregister anyway and leave referencing profiles for explicit repair with `skc profile remove --catalog-id `. ID Positional argument. The catalog id to remove. --force Unregister even when installed profiles still reference this catalog. --keep-clone Leave the existing clone directory on disk. #### skc catalog list List registered catalogs and their trusted clone metadata. No additional arguments. #### skc catalog stats CATALOG_ID Count skills, stacks, and bundles in one trusted registered catalog. CATALOG_ID Positional argument. The registered catalog id to inspect. #### skc catalog changes Show pending local file changes across registered catalogs. With no selector, performs the global all-catalog dirty-state scan. CATALOG_ID Optional positional arguments. Scope the scan to these catalogs. --catalog-id CATALOG_ID Repeatable. Also scopes the scan. Positional ids and --catalog-id values are unioned, not mutually exclusive. Scoping the query means unrelated broken catalogs are not inspected. #### skc catalog policy show CATALOG_ID Show the effective sync policy for one catalog. An omitted policy means `direct`. CATALOG_ID Positional argument. Catalog id to inspect. #### skc catalog policy set CATALOG_ID direct Use direct sync behavior for a catalog: SkillCatalog commits on the managed checkout and pushes HEAD to the tracking remote. CATALOG_ID Positional argument. Catalog id to update. #### skc catalog policy set CATALOG_ID review-branch Use protected review-branch behavior for a catalog: SkillCatalog pulls from a configured primary branch but publishes saves, scoring sidecars, accepted improvements, and dirty sync commits as isolated proposal branches under a configured review prefix. The managed checkout stays on primary. CATALOG_ID Positional argument. Catalog id to update. --primary PRIMARY Required. Primary branch to pull from. --review REVIEW Required. Review branch prefix to push proposals under. Branch names must be short Git branch names. Fully qualified refs, refspecs, whitespace or control characters, option-like names, HEAD, and identical primary/review values are rejected before the policy is saved. #### skc proposal list List active local proposal records. Use `--catalog-id ` to scope the list. Use `--include-remote-only` to also surface remote proposal branches under the review prefix that have no local record. --catalog-id CATALOG_ID Optional catalog filter. --include-remote-only Include matching remote branches without local records. #### skc proposal show PROPOSAL_ID Show one active proposal record by id. PROPOSAL_ID 26-character Crockford base32 ULID proposal id. --catalog-id CATALOG_ID Optional catalog filter. #### skc proposal refresh [PROPOSAL_ID] Refresh remote evidence for one active proposal, or every active proposal when the id is omitted. PROPOSAL_ID Optional proposal id to refresh. --catalog-id CATALOG_ID Optional catalog filter. #### skc proposal discard PROPOSAL_ID Mark an active proposal record as discarded. PROPOSAL_ID Proposal id to discard. --catalog-id CATALOG_ID Optional catalog filter. #### skc proposal verify-clean [CATALOG_ID] Verify review-branch managed-checkout invariants across registered catalogs, or one catalog when an id is supplied. CATALOG_ID Optional catalog id. #### skc skill list [CATALOG_ID] List skills from every registered catalog, or scope to one trusted registered catalog. --catalog-id CATALOG_ID Optional string. Canonical catalog filter. CATALOG_ID Optional positional argument. Compatibility alias for --catalog-id. --compact Omit the full skill `body` field from JSON output. Available on `skill list` only, and requires --json: `--compact` without `--json` is rejected as user-input-invalid at exit 10, not ignored. Supplying both the flag and the positional with the same value is accepted; different values fail as user-input-invalid at exit 10. #### skc skill show CATALOG_ID SLUG | skc skill show SLUG --catalog-id CATALOG_ID Show one skill from one trusted registered catalog. CATALOG_ID Positional argument. The registered catalog id to inspect. SLUG Positional argument. The skill slug. #### skc skill score SLUG Score one directory-form skill from the active catalog, or from an explicitly selected catalog when `--catalog-id` is provided. On success this command prints raw scoring-result JSON directly to stdout. The result includes `new_run_id` plus scoring history loaded from `skills/{slug}/SCORE.json`. It does not use the standard CLI JSON envelope. Each scoring run fans out to every enabled provider that is usable for its auth mode. Current runtime providers are local CLI providers such as `claude-code-local` and `codex-local`; they use existing local CLI authentication. On review-branch catalogs, successful score sidecar writes route through the proposal model instead of pushing directly to primary. The command rejects the embedded catalog and flat skill layouts such as `skills/foo.md` and `skills/foo.yaml`; move the skill to directory form at `skills/foo/SKILL.md`, then retry. On failure it emits the typed JSON error envelope to stderr and exits 32, except same-process run contention exits 33. SLUG Positional argument. Skill slug in the selected catalog. --catalog-id CATALOG_ID Intrinsic catalog id from catalog.yaml. If omitted, uses the active catalog. --model MODEL Override the local CLI provider model for this run. #### skc skill improve SLUG Generate or apply a scored improvement proposal for one directory-form skill from the active catalog, or from an explicitly selected catalog when `--catalog-id` is provided. By default this command is a non-mutating preview. It prints the provider, model, old score, new score, score delta, transient provider warnings, and the local SKILL.md diff. `--dry-run` is equivalent to the default preview mode. `--proposal-out ` writes a versioned reviewed proposal document during preview without mutating the catalog. `--apply-proposal ` applies a reviewed proposal document without regenerating it. `--apply` remains the compatibility one-shot: it generates a proposal, validates and rescores the candidate, writes only the accepted `SKILL.md` replacement, then runs standard scoring for the accepted content. Default and `--dry-run` mode never write `SKILL.md`, `SCORE.json`, delivery targets, commits, or remote branches. Improvement providers are never asked to edit `SCORE.json`; scoring remains the only owner of scoring sidecars. The command exits non-zero when no current scoring baseline exists for the current `SKILL.md`, every improvement provider fails, every candidate is invalid, candidate scoring cannot produce a successful provider result, the active catalog is embedded/read-only, or the skill changes before apply. Failed providers or invalid provider candidates are transient warnings when another provider produces a valid, rescored proposal. SLUG Positional argument. Skill slug in the selected catalog. Required UNLESS --apply-proposal is present, so `skc skill improve --proposal-out out.json` with no slug exits 2. --catalog-id CATALOG_ID Intrinsic catalog id from catalog.yaml. If omitted, uses the active catalog. --model MODEL Override the local CLI provider model for this run. --dry-run Preview without writing. This is the default mode and conflicts with BOTH --apply and --apply-proposal, so adding it defensively alongside either exits 2. --proposal-out also conflicts with --apply-proposal, but deliberately parses alongside --apply and is then rejected at runtime as proposal-out-with-apply. --proposal-out PATH Write a reviewed proposal document during preview without mutating the catalog. --apply-proposal PATH Apply a reviewed proposal document without regenerating the proposal. --apply Generate and apply a proposal in one invocation, then persist accepted score evidence. #### skc skill publish-accepted Publish a reviewed improvement that has already been accepted with `skc skill improve --apply-proposal`. The command validates accepted content and score evidence, commits only `skills//SKILL.md` and `skills//SCORE.json`, then pushes and runs scoped delivery. On review-branch catalogs, the commit is routed through the proposal model. Successful runs require commit `Committed` or `NothingToCommit`, push `Pushed`/`NothingToPush`/`LocalOnly`, and delivery `DeliveryAllowed`. --proposal PATH Required. Reviewed proposal document created by `skc skill improve --proposal-out`. --message MESSAGE Required. Commit message for the accepted publish. --update-proposal ID Append to an existing active proposal. Mutually exclusive with --new-proposal. --new-proposal Force a new proposal branch. #### skc skill score-history SLUG Show append-only scoring history for one directory-form skill from the active catalog, or from an explicitly selected catalog when `--catalog-id` is provided. This command prints raw scoring-history JSON directly to stdout on success. It recomputes current validation context, renders one history entry per run, includes per-provider results, preserves `imported_from_legacy`, and derives any endpoint-attributed disagreement signal. It does not use the standard CLI JSON envelope on success. The command rejects flat skill layouts such as `skills/foo.md` and `skills/foo.yaml`; move the skill to directory form at `skills/foo/SKILL.md`, then retry. SLUG Positional argument. Skill slug in the selected catalog. --catalog-id CATALOG_ID Intrinsic catalog id from catalog.yaml. If omitted, uses the active catalog. --repair Rebuild an oversized scoring sidecar before rendering history. Oversized only: a malformed sidecar or one with a newer schema_version is preserved byte-for-byte and reported as an error, not repaired. Acquires the same catalog lifecycle lock used by proposal metadata writes, so it waits for an active writer instead of racing it. Top-level `skc score-history SLUG [--catalog-id CATALOG_ID]` is an alias that delegates here. Successful output stays raw scoring-history JSON, and failure envelopes retain `command: "skill.score-history"`. #### skc scoring providers list List known scoring providers with enabled and usable state. Human output is a provider table. JSON output uses the standard envelope with provider id, display name, enabled state, usability, local readiness when applicable, and any persisted model or effort override. Settings or provider-state read failures exit non-zero. No additional arguments. #### skc scoring providers enable PROVIDER_ID Enable a provider for future scoring runs. The command persists that provider as opted in for scoring. Future `skc skill score` runs fan out to it when it is usable for its auth mode. JSON success and failure envelopes use `command: "scoring.providers.enable"`. Unknown provider ids fail with `unknown-provider`; settings read or write failures fail with `settings-unreadable` or `settings-unwritable`. Human-mode settings failures render human-readable error text instead of a JSON envelope. PROVIDER_ID Positional argument. Provider id to enable for scoring. #### skc scoring providers disable PROVIDER_ID Disable a provider for future scoring runs. The command persists that provider as opted out of scoring. Future `skc skill score` runs skip it. JSON success and failure envelopes use `command: "scoring.providers.disable"`. Unknown provider ids fail with `unknown-provider`; settings read or write failures fail with `settings-unreadable` or `settings-unwritable`. Human-mode settings failures render human-readable error text instead of a JSON envelope. PROVIDER_ID Positional argument. Provider id to disable for scoring. #### skc scoring providers set-model PROVIDER_ID MODEL Persist a model override for one scoring provider. Empty values are rejected; use `clear-model` to remove the override. PROVIDER_ID Positional argument. Provider id. MODEL Positional argument. Provider-specific model id. #### skc scoring providers set-effort PROVIDER_ID EFFORT Persist a reasoning-effort override for one scoring provider. Values outside the provider descriptor's allowed effort set are rejected. PROVIDER_ID Positional argument. Provider id. EFFORT Positional argument. Provider-specific effort level. #### skc scoring providers clear-model PROVIDER_ID Clear the persisted model override for one scoring provider. PROVIDER_ID Positional argument. Provider id. #### skc scoring providers clear-effort PROVIDER_ID Clear the persisted reasoning-effort override for one scoring provider. PROVIDER_ID Positional argument. Provider id. #### skc stack list [CATALOG_ID] List stacks from every registered catalog, or scope to one trusted registered catalog. --catalog-id CATALOG_ID Optional string. Canonical catalog filter. CATALOG_ID Optional positional argument. Compatibility alias for --catalog-id. #### skc stack show CATALOG_ID SLUG | skc stack show SLUG --catalog-id CATALOG_ID Show one stack from one trusted registered catalog. CATALOG_ID Positional argument. The registered catalog id to inspect. SLUG Positional argument. The stack slug. #### skc bundle list [CATALOG_ID] List bundles from every registered catalog, or scope to one trusted registered catalog. --catalog-id CATALOG_ID Optional string. Canonical catalog filter. CATALOG_ID Optional positional argument. Compatibility alias for --catalog-id. #### skc bundle show CATALOG_ID SLUG | skc bundle show SLUG --catalog-id CATALOG_ID Show one bundle from one trusted registered catalog. CATALOG_ID Positional argument. The registered catalog id to inspect. SLUG Positional argument. The bundle slug. #### skc validate Validate managed local state and the current checkout manifest. By default runs all checks, including drift unless `--no-drift` is set. Any validation diagnostic exits non-zero unless `--warnings-ok` is set and every diagnostic is a warning. Use flags to scope. Committed manifests must be regular files. Symlinked or non-regular .skillcatalog/skillcatalog.yml paths fail in the validate report as manifest-malformed and are not followed. When a sibling .skillcatalog/skillcatalog.local.yml is present, validate also checks its excludes and entries: an exclude that matches no committed team entry is reported as a `local-exclude-no-match` warning, and malformed or undeclared-catalog local entries are reported as errors. The committed .skillcatalog/skillcatalog.yml is validated first; local-manifest diagnostics surface only when the committed manifest is error-clean. --drift Run only drift checks. --settings Run only settings-file checks. --profiles Run only local profile-file checks. --manifest Run only committed-manifest checks. --path PATH Limit catalog, drift, or manifest validation to a specific checkout, catalog, catalog.yaml, or manifest path. Other existing files fail with path-not-validatable. Not supported with --settings or --profiles, but runnable checks still run when mixed with those diagnostics. --no-drift Skip drift checks (conflicts with --drift). --warnings-ok Exit successfully when diagnostics are warnings only. Project-manifest diagnostic tags, exactly as they appear in the --json report. Key on these strings rather than on message text. This list is not exhaustive for every check; treat an unrecognized tag as a failure rather than as success: manifest-missing Error. No committed manifest at the selected path. manifest-path-not-canonical Error. The manifest path is not canonical. manifest-reference-missing Error. A committed entry names a slug that does not exist in its catalog. One of the two most common real failures. manifest-reference-wrong-kind Error. A committed entry's declared kind does not match the content that owns the slug. manifest-undeclared-catalog-id Error. A committed entry references a catalog id the catalogs block does not declare. catalog-source-mismatch Error. The manifest and the registry disagree on a catalog's source. catalog-clone-unreadable Error. A referenced catalog's managed clone cannot be read. path-not-validatable Error. --path points at something that is not a validatable target. path-not-supported Error. --path was combined with --settings or --profiles. manifest-layout-conflict Error. Both .skillcatalog/skillcatalog.yml and a legacy root skillcatalog.yml exist. Move the root file into .skillcatalog/ and delete the old one. This exact tag is emitted by `skc validate` only; install and update report the same condition as manifest-malformed. manifest-malformed Error. Committed manifest does not parse, or is a symlink or other non-regular file. local-manifest-malformed Error. Local manifest does not parse. local-manifest-reference-missing Error. A local entry names a slug that does not exist in its catalog. local-manifest-reference-wrong-kind Error. A local entry's declared kind does not match the content owning the slug. local-manifest-undeclared-catalog-id Error. A local entry references a catalog id the local catalogs: block does not declare. local-catalog-clone-unreadable Error. A catalog declared by the local manifest has an unreadable managed clone. local-catalog-source-mismatch Error. Committed and local manifests declare the same catalog id with different sources. A byte-identical source is allowed. local-manifest-migration-repair Error. An installed profile still holds legacy child entries that could not be migrated into the local manifest. Emitted under the Profiles check, not Manifest, so `skc validate --manifest` never produces it. local-exclude-no-match Warning. An excludes: entry matches no committed team entry. Always a no-op. #### skc status Summarize registered catalogs, local profiles, enabled targets, and drift. Fails non-zero with `status-inconsistent-state` when local state cannot be trusted. No additional arguments. #### skc completions SHELL Generate shell completion scripts as raw shell script text. This command does not use the standard JSON envelope, even when `--json` is present. SHELL Positional argument: bash, zsh, fish, or powershell. #### skc hook run Internal protocol endpoint for SkillCatalog-managed Git hooks. It appears in `skc help-json` because it is a real subcommand, but it is not a supported automation entry point: invoke `skc validate` directly instead. Both `--protocol ` and `--path ` are required, the only supported protocol is 1, an unsupported protocol exits 64 as plain text rather than a JSON envelope, and on the success path it delegates to validate, so its envelope reports `command: "validate"` rather than `hook.run`. #### skc help-json Dump the authoritative machine-readable CLI contract as structured JSON. It includes built-in help and version discoverability, aliases, global and local arguments, argument kind and order, repeatability, arity bounds, defaults, allowed values, parser conflicts, and command-level runtime behavior notes. No additional arguments. This command always prints raw JSON to stdout and does not use the standard JSON envelope, even when `--json` is present. ## Exit codes Exit 0 means success. Non-zero exit codes are command-specific: 2 CLI parse or usage error. Also used for domain refusals on two surfaces: `skc scoring providers` unknown-provider and invalid-effort, and `skc proposal discard` proposal-state refusals. Those carry a real outcome tag or message, so a 2 is not proof of bad syntax. 10 User input error (invalid argument, non-canonical checkout path) 11 Manifest malformed (.skillcatalog/skillcatalog.yml parse error) 12 Manifest path not canonical 13 Manifest reference missing (entry slug not found in catalog) 14 Manifest reference wrong kind 15 No profile at checkout / missing install state 16 Catalog source invalid / installed profile missing 17 Catalog not found / missing registered catalog source 18 Manifest already exists / manifest profile mismatch 19 Manifest missing (no .skillcatalog/skillcatalog.yml in current directory) 20 Install path conflict (two profiles write to the same path) 21 Profile id conflict 22 Catalog removed (registered catalog no longer exists) 23 Catalog clone unreadable / registered catalog unreadable 24 Profile write failed 25 Delivery failed 26 Drift detected (with --check), or install URL mismatch 27 Manifest write failed 28 Manifest malformed or unreadable (delivery context), or install intrinsic catalog id mismatch 29 Checkout directory conflict 30 Local state validation failure 31 Manifest validation failure 32 Inconsistent state, catalog remove in use, skill scoring failure, or skill improvement failure 33 Pending changes with no --message and no TTY (sync), catalog clone cleanup pending (catalog remove), or a scoring run already in progress for the same catalog skill in this process, including during improvement apply 34 Another sync is already running for the same catalog 40 Catalog clone failed (git clone error) 41 Sync partial failure (one or more requested catalogs failed to pull), or score-finalization-conflict on `skc skill score` when the catalog left the registry after scoring was prepared 42 Catalog refresh failed 43 Delivery I/O error 44 Composition gap during profile delivery, or sync all failed (every requested catalog failed to pull) 45 Delivery blocked by unknown review-branch proposal remote state or unreadable proposal metadata 46 manifest-changed: the project manifest changed while it was being inspected and nothing was written. Safe to rerun. Emitted by install, update, and deliver; profile add/remove/reorder still render this event as 24. 47 The operation committed state and then failed. Reconcile before rerunning; do not treat as success. 70 Internal error. Also the code for `skc scoring providers` settings-unreadable and settings-unwritable. Codes 46 and 47 are the two halves of one deliberately globally unique pair, so they never collide with a command-specific code: 46 means nothing was written and rerunning is free, 47 means state landed and must be reconciled first. Exit 47 is the committed-state class rather than a single event, so a bare 47 does not identify which committed-state failure occurred; read the envelope kind. `skc proposal` subcommands add their own small-integer scheme on top of the shared families above. A 2 from a proposal command is ambiguous: it may be an ordinary clap parse error, which still exits 2 here as everywhere else, or the proposal outcome below. Read the envelope to tell them apart. 1 invariants-violated. proposal verify-clean only. 2 not-applicable, already-discarded, refresh-failed, or discard-not-applicable. proposal discard only. 3 not-found: no active proposal matches the supplied id. proposal show and proposal discard. 70 Internal error. Reachable from all five proposal subcommands, and not part of the small-integer scheme. Per-command exit surfaces: list {0,70}, show {0,3,70}, refresh {0,70}, discard {0,2,3,70}, verify-clean {0,1,70}. `skc proposal refresh` reports per-record failures in band and exits 0. ## JSON output envelope In `--json` mode, every command except `help-json`, `completions`, `skill score`, and `skill score-history` uses the standard success envelope below. Parser failures for invocations that include `--json` also use this envelope with command `skc` and outcome `cli-parse-error`. `skill score` and `skill score-history` print raw scoring JSON on success and still use the standard typed error envelope on failure. `skill improve` uses the standard envelope in JSON mode. Built-in help and version output remain raw text. Commands that use the standard envelope write a single JSON object with this unified shape: { "command": "", "status": "ok" | "error", "outcome": "", "exit_code": 0, "data": { ... } | null, "error": null | { "message": "", "recovery_hint": "", "details": { ... } | null } } `help-json` emits the raw command tree directly. `completions` emits shell completion script text directly. `skill score` success output is the raw scoring result document. It includes `new_run_id` and the same history shape returned by `skc skill score-history`. `skill score-history` success output is raw scoring-history JSON. History entries include one per-provider merged view per provider envelope, stale status, `imported_from_legacy`, and an endpoint-attributed disagreement signal when provider scores spread enough to show it. No history entry includes a persisted headline number, `cache_key`, or `cached` field. These success paths are intentionally outside the standard envelope so scripts can consume the scoring payloads directly. Top-level aliases preserve the invoked command label in the standard envelope: `skc init` emits command `init`, while `skc profile init` emits command `profile.init`. The same rule applies to `install` and `uninstall`. Known gap: `skc proposal show` and `skc proposal discard` build their non-success payloads as plain text before the JSON branch is reached, and the shared fallback then re-wraps them as the unified envelope with `outcome: "internal-error"`. So a benign no-op such as discarding an already-discarded proposal reports `outcome: "internal-error"` at exit 2 under `--json`, and the real tag (`already-discarded`, `not-applicable`, `refresh-failed`, `discard-not-applicable`, `not-found`) survives only as the first line of `error.message`, in the form `Error: `. Do NOT branch on `outcome` for these two commands: parse the tag out of `error.message`, or treat exit 3 as not-found and exit 2 as a proposal-state refusal. Success outcome tags, by command. These are what a successful run reports in `outcome`: init / profile.init manifest-written install / profile.install installed-fresh | installed-idempotent uninstall uninstalled update project-updated; up-to-date under --check sync sync-complete | no-catalogs deliver delivery-complete | delivery-dry-run-complete status status-report validate validate-report (see the caveat below) settings.show settings-shown settings.enable / .disable delivery-target-enabled | -disabled catalog.add catalog-added | catalog-already-registered catalog.list / .stats catalogs-listed | catalog-stats-shown catalog.changes catalog-changes-listed catalog.remove catalog-removed catalog.policy.show / .set catalog-policy profile.list / .show profiles-listed | profile-shown profile.create / .delete profile-created | profile-deleted profile.add / .remove profile-entry-added | profile-entry-removed profile.reorder profile-reordered profile.uninstall uninstalled proposal.list / .show proposals-listed | proposal-shown proposal.refresh / .discard proposals-refreshed | proposal-discarded proposal.verify-clean verified skill.list / .show skills-listed | skill-shown stack.list / bundle.list stacks-listed | bundles-listed stack.show / bundle.show stack-shown | bundle-shown skill.improve improvement-preview-ready | reviewed-improvement-applied | improvement-generated-and-applied skill.publish-accepted accepted-improvement-published scoring.providers.* scoring-providers-listed, scoring-provider-enabled | -disabled, scoring-provider-model-set | -effort-set, scoring-provider-model-cleared | -effort-cleared Do not treat `outcome` as sufficient to detect failure. `skc validate` emits `outcome: "validate-report"` for a passing run, a warnings-tolerated run, and a failing run alike; branch on `status` or `exit_code`. ## File formats ### Skill file (skills/{slug}/SKILL.md) An official Agent Skills SKILL.md file with YAML frontmatter. The body is plain Markdown. Cross-references to other skills use the syntax @skill:other-slug. Legacy flat files such as skills/foo.md are migration-read tolerance only. --- name: my-skill description: "One-sentence summary of what this skill does." license: "Apache-2.0" compatibility: "Requires git" metadata: skillcatalog/display_name: "My Skill" skillcatalog/category: "my-category" skillcatalog/owner: "my-team" skillcatalog/author: "Jane Doe" skillcatalog/tags: '["debugging","testing"]' skillcatalog/created_at: "2026-01-15T10:00:00Z" skillcatalog/updated_at: "2026-01-15T10:00:00Z" author: "example-org" allowed-tools: "Bash(git:*) Read" --- Skill body in Markdown. Can reference other skills with @skill:other-slug. Required fields: name, description. The name must match the parent directory slug and is capped at 64 characters. description is capped at 500 characters, under a separate 1024-BYTE gate, so mostly multi-byte text can fail below 500 characters; compatibility is capped at 500 BYTES. Over-length values fail to parse, so the skill will not deliver at all. Optional official fields: license, compatibility, metadata, allowed-tools. SkillCatalog metadata lives in the `metadata` map under reserved `skillcatalog/*` keys: display_name, category, owner, author, tags, created_at, updated_at. JSON schema: https://skillcatalog.dev/schemas/skill-frontmatter.schema.json ### Stack file (stacks/{slug}.yaml) YAML frontmatter followed by a YAML body with a skills array. --- name: "My Stack" description: "Groups related skills together." tags: ["workflow"] owner: "my-team" author: "Jane Doe" created_at: "2026-01-15T10:00:00Z" updated_at: "2026-01-15T10:00:00Z" --- skills: - skill-one - skill-two - skill-three Required fields: description, author, created_at, updated_at, skills. Optional fields: name (defaults to slug), tags, owner. The skills list must be non-empty and duplicate-free. If one listed skill references another via `@skill:slug`, that referenced skill must also be included in the same stack. JSON schema: https://skillcatalog.dev/schemas/stack.schema.json ### Bundle file (bundles/{slug}.yaml) YAML frontmatter followed by a YAML body with a stacks array. --- name: "My Bundle" description: "Packages stacks for distribution." owner: "my-team" author: "Jane Doe" created_at: "2026-01-15T10:00:00Z" updated_at: "2026-01-15T10:00:00Z" --- stacks: - stack-one - stack-two Required fields: description, author, created_at, updated_at, stacks. Optional fields: name (defaults to slug), owner. The stacks list must be non-empty and duplicate-free. The effective delivered skill union of those stacks also has to stand on its own. If an included skill references another skill via `@skill:slug`, that referenced skill must be delivered by at least one stack in the same bundle. JSON schema: https://skillcatalog.dev/schemas/bundle.schema.json ### Catalog metadata (catalog.yaml) Root file of a catalog repository. Defines catalog identity and taxonomy. id: "my-team-catalog" name: "My Team Catalog" description: "Shared skills for the engineering team." taxonomy: categories: - slug: "debugging" name: "Debugging" children: - slug: "rust-debugging" name: "Rust Debugging" - slug: "testing" name: "Testing" owners: - slug: "platform" name: "Platform Team" tags: - "performance" - "security" - "workflow" Required fields: id, name. Optional fields: description, taxonomy. The top-level `id` is the catalog's portable identity. Category and owner nesting: max depth 2 (root + one child level). Validation also rejects duplicate category or owner slugs anywhere in the tree and duplicate tag values. JSON schema: https://skillcatalog.dev/schemas/catalog.schema.json ### Project manifest (.skillcatalog/skillcatalog.yml) Committed to the project repository root. Declares the project's catalog references and entries. A single profile can mix entries from any number of registered catalogs because every entry stores its intrinsic `catalog_id` directly. profile: id: "my-project" display_name: "My Project" catalogs: shared-catalog: source: "git@github.com:myteam/skills.git" review-skills: source: "https://github.com/myorg/review-skills.git" entries: - catalog_id: "shared-catalog" kind: "bundle" slug: "core-bundle" - catalog_id: "shared-catalog" kind: "stack" slug: "debugging-stack" - catalog_id: "review-skills" kind: "bundle" slug: "reviewer" Required fields: profile (id, display_name) and entries. `catalogs` is required and non-empty when entries are present. A no-op manifest with `entries: []` may omit `catalogs` or set it to null or `{}`. Entry kinds: skill, stack, bundle. Profile id must not be "home" (reserved for the global home profile). Each non-empty entries list must name intrinsic catalog ids declared in the catalogs block. Each catalog key should match that catalog's `catalog.yaml.id`. Catalog references in a committed manifest use `source:` only. No duplicate entries with the same `catalog_id`, kind, and slug. No duplicate source values across catalog ids in the same catalogs block. Strict schema: unknown fields are rejected. Child profiles (local profile.yml under ~/.skillcatalog/profiles//) point back to the committed manifest with `parent.path` and carry metadata only; for a profile installed from a committed manifest, personal entries and excludes live in the checkout's .skillcatalog/skillcatalog.local.yml instead. An undeclared catalog id is rejected as `profile-catalog-id-undeclared`. A conflicting parent-child source mapping for the same catalog id is rejected as `profile-catalog-conflict`. JSON schema: https://skillcatalog.dev/schemas/skillcatalog-yml.schema.json ### Local project manifest (.skillcatalog/skillcatalog.local.yml) Optional sibling of .skillcatalog/skillcatalog.yml in the same .skillcatalog/ directory. SkillCatalog adds it to the checkout's .gitignore. Holds a user's personal, machine-local entries and excludes for a project-backed profile. Never the profile's identity authority (that stays in .skillcatalog/skillcatalog.yml), so `profile:` is optional here. `skc profile add`, `skc profile remove`, and `skc profile reorder` write to this file for a profile installed from a committed manifest; the shared .skillcatalog/skillcatalog.yml is never rewritten by those personal mutations. profile: # optional; identity stays in the committed manifest id: "my-project" display_name: "My Project" catalogs: # optional personal: source: "https://github.com/acme/personal.git" entries: # optional; personal additions - catalog_id: "personal" kind: "skill" slug: "my-skill" excludes: # optional; drop specific team entries locally - catalog_id: "shared-catalog" kind: "skill" slug: "unwanted" Optional fields: profile, catalogs, entries, excludes (all default to empty). An entry must reference a catalog declared in the local catalogs block; an excludes entry uses the same (catalog_id, kind, slug) shape and does not need a matching local catalog declaration. Strict schema: unknown fields are rejected. Effective delivery merges local entries first, then committed team entries, de-duplicating on (catalog_id, kind, slug): a local entry shadows an identically-keyed team entry. An excludes entry drops the exact matching team entry from delivery (never a local entry); excluding a team bundle or stack also removes its expanded member skills. `skc profile remove` on a key present in the committed .skillcatalog/skillcatalog.yml auto-appends that key to excludes. A catalog id declared by both manifests with a different source is a hard compose error. SkillCatalog writes to the checkout to keep the personal layer out of Git. The root .gitignore gains, in this order: .skillcatalog/skillcatalog.local.yml skillcatalog.local.yml # only while the legacy root path is in use /.skillcatalog/.skc-recovery/ plus .skillcatalog/.skc-recovery/.gitignore containing `*`. A nested .skillcatalog/.gitignore is NOT created: if the repository already has one, SkillCatalog keeps its personal-path rule current, but a checkout without that file never gains one. All writes are additive and no-follow, so a concurrent user edit is preserved rather than replaced. Before the personal file is created or replaced in a Git worktree, real Git inspection must prove the selected path is effectively ignored and not tracked. Tracked or exposed paths and Git inspection errors fail closed; a directory Git specifically reports as not a repository retains compatibility. .skillcatalog/.skc-recovery/ is a private owner-only namespace used for an atomic conditional exchange of the personal manifest. When the live bytes differ from the bytes the mutation loaded, SkillCatalog returns a conflict and retains the displaced editor version at the reported recovery path instead of discarding it. An exclude that matches no committed team entry is a harmless no-op, never a delivery or install error. `skc validate` reports each as a local-exclude-no-match warning; install, update, and deliver emit a log warning for each non-matching exclude but never fail on one. JSON schema: https://skillcatalog.dev/schemas/skillcatalog-local-yml.schema.json ## Catalog directory structure my-catalog/ catalog.yaml skills/ my-skill/ SKILL.md another-skill/ SKILL.md stacks/ my-stack.yaml bundles/ my-bundle.yaml ## Slug conventions Slugs (stack, bundle, category, owner, profile id): - Are 1 to 128 characters. - Start with a lowercase letter or digit. - Contain only lowercase letters, digits, and hyphens. - No trailing or consecutive hyphens. - Pattern: ^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){0,127}$ Source skill directory slugs follow the same character pattern, but they are effectively capped at 64 characters because official Agent Skills `name` must match the parent directory slug. ## Cross-references Skill bodies can reference other skills with @skill:slug-name. These references are validated against catalog content and resolved during delivery. Skill bodies can also use @skill-dir to refer to the delivered directory for the current skill; delivery expands it to an absolute path in the delivered SKILL.md. ## Delivery paths Skills are delivered as one winning directory per slug to tool-specific directories: Claude Code (home profile): ~/.claude/skills/{slug}/SKILL.md Claude Code (project profile): {checkout}/.claude/skills/{slug}/SKILL.md Cursor (home profile): ~/.cursor/skills/{slug}/SKILL.md Cursor (project profile): {checkout}/.cursor/skills/{slug}/SKILL.md Codex (home profile): ~/.agents/skills/{slug}/SKILL.md Codex (project profile): {checkout}/.agents/skills/{slug}/SKILL.md Delivery adapts target-specific fields, rewrites @skill:slug cross-references, and expands @skill-dir only in the delivered SKILL.md. Companion files such as references, scripts, and assets are copied byte-for-byte with their executable bit preserved, except SCORE.json, which remains an in-catalog scoring sidecar. Every delivered file is tracked in the manifest. Quote executable @skill-dir paths when profile directories may contain spaces. Any companion that is a symlink fails the whole skill rather than being copied or skipped. Clean delivered files update to the latest catalog output. Deleted files are restored on the next delivery run. Edited files are preserved and skipped until the edit is reverted or merged into the source catalog. Unreadable tracked files are reported separately and need permission or path repair before redelivery. ## Local state directory ~/.skillcatalog/ settings.toml App settings, targets, provider state logs/ Local diagnostic logs (never uploaded) catalogs/ catalogs.yml Catalog registry my-catalog/ Cloned catalog repo proposals/ my-catalog/ active/ Local review-branch proposal records archive/ Accepted or discarded proposal records profiles/ home/ profile.yml Home profile metadata delivery-manifest.json Delivery tracking my-project/ profile.yml Project profile metadata delivery-manifest.json Delivery tracking Settings delivery roots are derived from the selected profile directory and target id. Legacy path_type and workspace_path values may appear under legacy_ignored for downgrade compatibility, but they do not change delivery destinations. Local CLI provider readiness and opt-in state are stored locally; SkillCatalog does not install those CLIs, sign you in or out, or write provider credential files. ## Diagnostics and support packages Logs and support packages stay local unless explicitly copied or exported. Diagnostic text is redacted before it crosses a persistent-log, Tauri IPC, clipboard, or saved-package boundary. Redaction covers token-shaped secrets, URL credentials and sensitive parameters, raw JSON bodies, panic/backtrace text, unsafe controls, email identity fields, and common host-path variants while preserving useful status, counts, branch names, and non-secret context. ## Environment variables SKILLCATALOG_LOG Tracing filter directive (e.g., skillcatalog_infrastructure=debug) GIT_TIMEOUT_LOCAL Timeout for local git operations (seconds, default 30) GIT_TIMEOUT_NETWORK Timeout for network git operations (seconds, default 120) ## Embedded catalog The desktop app ships with a built-in catalog named SkillCatalog Essentials (catalog id: skillcatalog-essentials). It is copied out of the application bundle into ~/.skillcatalog/catalogs/skillcatalog-essentials/ on first launch so it is available locally. The registry records an app-owned canonical source URL for identity checks, while bundled content refreshes with app releases. The Essentials catalog contains three stacks: sc-guide Evergreen concept skills that teach agents what SkillCatalog is. Included in the Essentials starter set. sc-authoring Schemas, authoring workflows, validation rules, and scaffolding commands. Available as Authoring content. sc-operate Local profile, settings, catalog, and delivery-target management. Included in the Essentials starter set. On first launch, Guide and Operations content are preselected for Home, Authoring content is optional, and users can deliver the selected content immediately or skip content install and add Essentials content to any profile later. The catalog publishes bundles for Essentials, Authoring only, and Everything, but onboarding works from stack choices so the starter set can evolve with app releases. The embedded catalog is a read-only source to user code; the desktop protects it from unregister and local-delete actions. Its content is refreshed from the app bundle whenever the installed version changes. ## Common agent workflows ### Install a project profile cd /path/to/project skc install --json ### Add a catalog skc catalog add git@github.com:team/skills.git --json ### Open an skc:// link Installed desktop builds register the skc:// URL scheme. Supported links launch or focus SkillCatalog and wait for user confirmation before cloning a catalog, writing registry state, or changing profiles. There is no CLI equivalent. Two routes are supported: skc://catalog/add?source=[&name=] skc://share/install?v=1&kind=&catalog_id=&slug=&source= Examples: skc://catalog/add?source=https%3A%2F%2Fgithub.com%2Facme%2Fteam-catalog.git&name=Acme%20Skills skc://share/install?v=1&kind=skill&catalog_id=acme-skills&slug=code-review&source=https%3A%2F%2Fgithub.com%2Facme%2Fteam-catalog.git Query values must be URL-encoded. On both routes the source value follows the same remote-only rules as skc catalog add: SSH, ssh://, or HTTP(S), with no credentials, query string, fragment, whitespace, local path, or file:// URL. For catalog/add, the optional name is 2-100 characters, cannot contain line breaks, path separators, or null bytes, and cannot be exactly `.` or `..`. The share/install route opens the install confirmation for one shared skill, stack, or bundle. It is a moving-source identity link that carries catalog id, item kind, slug, and source only, not a pinned revision. The recipient confirms, SkillCatalog resolves the item from a bounded temporary full-worktree checkout, the recipient chooses existing profiles or one new local profile, and install materializes the catalog without switching the active catalog. Confirmation reruns source preflight from a fresh temporary source and aborts before profile mutation if the source revision changed after the ready modal. Missing catalogs are registered from the accepted worktree. Existing matching catalogs must use the same source, be on the expected default or review-primary branch, have no tracked or untracked local changes, and fast-forward to the accepted revision. Clean legacy partial share-install checkouts are repaired by clearing sparse-checkout state and skip-worktree bits, hydrating missing tracked files, and rechecking the full tracked worktree. Already-installed items are profile-idempotent, but the catalog clone is still refreshed or repaired before returning already_installed. Source mismatch, wrong branch, catalog id mismatch, non-fast-forward divergence, incomplete tracked worktrees, unsafe paths, or worktree bounds failures stop before any selected profile changes. ### Switch a catalog to review-branch mode skc catalog policy set team-catalog review-branch --primary main --review skillcatalog/review --json ### Inspect pending review-branch proposals skc proposal list --catalog-id team-catalog --include-remote-only --json ### Refresh after upstream changes skc update --json ### Check for delivery drift skc deliver --check --json See "Delivery paths" for how clean, deleted, and edited files are handled. ### Validate everything skc validate --json ### Preview a skill improvement skc skill improve my-skill --json ### Publish an accepted reviewed improvement skc skill improve my-skill --proposal-out /tmp/my-skill.proposal.json skc skill improve --apply-proposal /tmp/my-skill.proposal.json skc skill publish-accepted --proposal /tmp/my-skill.proposal.json --message "Improve my skill" --json ### View current state skc status --json ### Discover all CLI commands programmatically skc help-json