| 1 | # Skills Manager |
| 2 | |
| 3 | Skills are reusable `SKILL.md` instruction packs. Codewhale discovers them from |
| 4 | several roots, but **only CodeWhale-owned directories are writable**. The unified |
| 5 | `/skills` manager is the interactive surface for audit and mutation; slash |
| 6 | aliases share the same write path. |
| 7 | |
| 8 | For Claude Code plugin boundaries, see [CLAUDE_PLUGIN_COMPAT.md](CLAUDE_PLUGIN_COMPAT.md). |
| 9 | For `skills_dir` and `[skills]` config keys, see [CONFIGURATION.md](CONFIGURATION.md). |
| 10 | |
| 11 | ## Architecture (four layers) |
| 12 | |
| 13 | | Layer | Role | |
| 14 | | --- | --- | |
| 15 | | **Root catalog** | Single source of precedence and ownership (`SkillRootCatalog`). | |
| 16 | | **Audit** | Read-only, unmerged on-disk inventory (status, digest, actions). | |
| 17 | | **Mutation controller** | Only writer for install / import / update / remove / trust. | |
| 18 | | **Skills manager view** | TUI: emits events only; never writes files itself. | |
| 19 | |
| 20 | Runtime discovery (`SkillRegistry`) still merges skills for the model. Audit |
| 21 | intentionally does **not** merge — it shows every on-disk copy so conflicts and |
| 22 | shadowing stay visible. |
| 23 | |
| 24 | ## Ownership and roots |
| 25 | |
| 26 | **Writable (CodeWhale-owned)** |
| 27 | |
| 28 | | Scope | Path | |
| 29 | | --- | --- | |
| 30 | | Project | `<workspace>/.codewhale/skills/` | |
| 31 | | Global | `~/.codewhale/skills/` | |
| 32 | |
| 33 | **Read-only compatible** (discover / import source only — never mutated in place) |
| 34 | |
| 35 | Examples: `<workspace>/.agents/skills`, `./skills`, `.claude/skills`, |
| 36 | `.cursor/skills`, `.opencode/skills`, `~/.agents/skills`, `~/.claude/skills`, |
| 37 | and similar harness layouts. |
| 38 | |
| 39 | **Audit-only (not runtime-active)** |
| 40 | |
| 41 | - `.codex/skills` appears in **compatible** audit scans so operators can see it. |
| 42 | It does **not** join the runtime discovery set. |
| 43 | |
| 44 | Configured `skills_dir` that is not one of the owned CodeWhale roots stays |
| 45 | read-only. Discovery and the manager can list it; mutations still target owned |
| 46 | project/global roots only. |
| 47 | |
| 48 | ## Slash commands |
| 49 | |
| 50 | | Command | Behavior | |
| 51 | | --- | --- | |
| 52 | | `/skills` | Opens the Skills Manager (owned-only scan, **no network**). | |
| 53 | | `/skills <prefix>` | Text list filtered by name prefix. | |
| 54 | | `/skills inspect` | Text discovery mode, searched directories, and source paths. | |
| 55 | | `/skills --remote` | Explicit registry listing (network). | |
| 56 | | `/skills suggest <task>` | Rank up to three remote skills for a task, with matching evidence and an explicit install command (network; no install). | |
| 57 | | `/skills sync` | Explicit registry → local cache sync (network). | |
| 58 | | `/skill <name>` | Activate a skill for the next turn. | |
| 59 | | `/skill install [--project\|--global] <spec>` | Install via mutation controller. | |
| 60 | | `/skill update [--project\|--global] <name>` | Update a managed skill from its registry provenance. | |
| 61 | | `/skill uninstall [--project\|--global] <name>` | Remove a managed skill. | |
| 62 | | `/skill trust [--project\|--global] <name>` | Write digest-bound advisory trust. | |
| 63 | |
| 64 | Notes: |
| 65 | |
| 66 | - There is **no** `/skills audit` subcommand. Use the manager (and `c` to toggle |
| 67 | compatible roots) or `/skills inspect` for discovery details. |
| 68 | - Bare `/skill install <spec>` (no scope flag) installs into the CodeWhale |
| 69 | **global** owned root. |
| 70 | - `/skills suggest` only reads the curated registry through the existing |
| 71 | network policy. It never downloads, trusts, enables, or activates a skill; |
| 72 | each result gives a separate `/skill install <name>` command for the user to |
| 73 | choose. |
| 74 | - If the same name exists in both project and global owned roots, update / |
| 75 | uninstall / trust require `--project` or `--global`. |
| 76 | - If a name exists only under a compatible external root, writes are refused; |
| 77 | import it through `/skills` instead of editing harness directories. |
| 78 | |
| 79 | ## Skills Manager (TUI) |
| 80 | |
| 81 | Default open path: type `/skills` and confirm. The surface is zero-network on |
| 82 | open (owned-only audit). |
| 83 | |
| 84 | | Key | Action | |
| 85 | | --- | --- | |
| 86 | | `↑`/`↓` or `j`/`k` | Move selection | |
| 87 | | `Enter` | Primary available action / confirm pending prompt | |
| 88 | | `i` | Import (external → owned) | |
| 89 | | `u` | Update (managed + registry provenance) | |
| 90 | | `r` | Remove (managed; confirms first) | |
| 91 | | `t` | Trust (managed; digest-bound) | |
| 92 | | `s` | Toggle import target: project ↔ global | |
| 93 | | `c` | Toggle scan: owned-only ↔ compatible (still local disk only) | |
| 94 | | `Esc` | Cancel confirm, or close the manager | |
| 95 | |
| 96 | The view never calls install helpers or touches the filesystem. It emits a |
| 97 | mutation request; the host runs the controller, shows a receipt, and rebuilds |
| 98 | the inventory. |
| 99 | |
| 100 | ## Bundled catalog tiers |
| 101 | |
| 102 | Codewhale presents its shipped skills in two compact tiers so agentic workflows |
| 103 | are not buried under document and integration helpers: |
| 104 | |
| 105 | - **Core agentic** — planning, implementation, debugging, review, verification, |
| 106 | delegation, Fleet, release, and `best-of-n` comparison workflows. |
| 107 | - **Format & tooling** — document formats, data visualization, frontend and web |
| 108 | testing, and skill/plugin/MCP authoring helpers. |
| 109 | |
| 110 | Workspace, user, and compatible-harness skills stay labeled **custom**; Codewhale |
| 111 | does not guess their intent from their name. The shipped pack also does not |
| 112 | advertise capabilities the runtime lacks. In particular, image understanding |
| 113 | is available, but an image-generation skill is not bundled until a real |
| 114 | image-generation tool exists. |
| 115 | |
| 116 | Repository-maintenance and release-operator helpers (the `gh-*` skills and |
| 117 | `codew-release-qa-sweep` under [`skills/`](skills/README.md)) are **not** part |
| 118 | of the end-user starter pack and are never auto-installed; a catalog-matrix |
| 119 | test pins that boundary. Shipping them as an optional bundle is plugin-delivery |
| 120 | work tracked separately in |
| 121 | [#4836](https://github.com/Hmbown/CodeWhale/issues/4836). |
| 122 | |
| 123 | ### Invocation and alias metadata |
| 124 | |
| 125 | Bundled and user skills may declare two runtime-routing fields in frontmatter: |
| 126 | |
| 127 | | Field | Meaning | |
| 128 | | --- | --- | |
| 129 | | `invocation: model+user` | The default; the skill appears in the model's compact catalogue and can be loaded by the model or user. | |
| 130 | | `invocation: explicit-only` | The skill remains loadable by an explicit name, but is omitted from the model catalogue so opt-in instructions do not become ambient context. | |
| 131 | | `aliases-for: name, other-name` | Additional lookup names for the same canonical skill. Aliases are not separate catalogue entries and do not duplicate prompt content. | |
| 132 | |
| 133 | Missing or unknown invocation values retain the historical `model+user` |
| 134 | behavior. Canonical names win over aliases when a collision exists. Loading a |
| 135 | skill reports its canonical invocation and aliases so receipts remain |
| 136 | inspectable. |
| 137 | |
| 138 | ### Starter-pack parity decisions |
| 139 | |
| 140 | The v0.9.2 parity audit in [#4698](https://github.com/Hmbown/CodeWhale/issues/4698) |
| 141 | compared the five `xai-grok-memory` / `xai-grok-shell` reference skills with |
| 142 | the actual Codewhale bundle. This is a decision matrix, not a request to copy |
| 143 | reference text or advertise unsupported tools: |
| 144 | |
| 145 | | Reference skill | Codewhale decision | Runtime grounding | |
| 146 | | --- | --- | --- | |
| 147 | | `check-work` | Canonical alias/compatibility mapping to `verify` | `verify` is the shipped evidence-collection workflow. | |
| 148 | | `code-review` | Canonical alias/compatibility mapping to `review` | `review` is the shipped read-only correctness workflow. | |
| 149 | | `create-skill` | Canonical alias/compatibility mapping to `skill-creator` | `skill-creator` is the shipped authoring workflow. | |
| 150 | | `help` | Bounded `invocation: explicit-only` router, not an ambient manual | Routes to `/help`, `/skills`, `/config`, `doctor`, and the installed `docs/` tree; it embeds no manual text. | |
| 151 | | `imagine` | Intentionally out of scope | Codewhale has no image-generation/edit tool, so the starter pack must not advertise one. | |
| 152 | |
| 153 | Notes on the two non-alias decisions: |
| 154 | |
| 155 | - **`help`** ships as a bundled skill (generation 7) but is `explicit-only`, so |
| 156 | it never appears in the model catalogue and costs zero ambient prompt budget. |
| 157 | Its body is a routing card — which surface owns which fact — and explicitly |
| 158 | forbids pasting a command list or settings table into context. A checked |
| 159 | invariant keeps it under 80 lines and requires it to name the `/help`, |
| 160 | `/skills`, `/config`, and `doctor` surfaces. |
| 161 | - **`imagine`** stays out. The shipped runtime exposes image *understanding*, |
| 162 | not image generation or edit, so no bundled skill may advertise it. The |
| 163 | catalog matrix asserts that `imagine`, `image`, and `image-gen` are absent |
| 164 | from the bundle and resolve to nothing. |
| 165 | |
| 166 | No reference skill body is copied by this compatibility slice. The explicit |
| 167 | aliases and invocation metadata are bounded routing facts; the full skill body |
| 168 | still enters context only through `load_skill`. |
| 169 | |
| 170 | ### Catalog fixture matrix (provider-free) |
| 171 | |
| 172 | [`crates/tui/assets/skills-catalog-matrix.json`](../crates/tui/assets/skills-catalog-matrix.json) |
| 173 | is an **authored** expectation table covering every bundled skill: canonical |
| 174 | name, tier, invocation, aliases, whether it renders as an ambient catalogue |
| 175 | entry, and which of its aliases are shadowed by another canonical name. The |
| 176 | tests in `crates/tui/src/skills/catalog_matrix.rs` assert a bijection between |
| 177 | that fixture and `BUNDLED_SKILLS`, so the shipped pack cannot change without an |
| 178 | explicit fixture update. |
| 179 | |
| 180 | What those tests do and do not claim: |
| 181 | |
| 182 | - They validate **deterministic registry / catalog / resolver behavior**: |
| 183 | install, parse, eligibility, explicit load, non-activation, alias resolution, |
| 184 | explicit-only exclusion, collision precedence, and prompt budget. |
| 185 | - They validate **nothing about semantic LLM routing**. Whether a model chooses |
| 186 | `debug` for a stack trace is a live-provider question; see |
| 187 | [LIVE_SMOKE.md](LIVE_SMOKE.md). |
| 188 | |
| 189 | Collision and prompt-budget invariants asserted today: |
| 190 | |
| 191 | | Invariant | Meaning | |
| 192 | | --- | --- | |
| 193 | | Canonical wins | A canonical bundled name always beats another skill's alias (`docx` → `docx`, never `documents`). | |
| 194 | | Single alias owner | No two bundled skills may claim the same alias. | |
| 195 | | No duplicate entries | Each canonical name renders at most one catalogue line; aliases render zero. | |
| 196 | | Budget headroom | The shipped pack alone renders under `MAX_AVAILABLE_SKILLS_CHARS` (12 000 chars) with **no** "additional skills omitted" line, so user skills are never silently displaced. | |
| 197 | | No context poisoning | Descriptions stay single-line and are truncated to `MAX_SKILL_DESCRIPTION_CHARS` (280) before entering the prompt. | |
| 198 | |
| 199 | ### Locale-aware routing metadata |
| 200 | |
| 201 | `description_<tag>` frontmatter is supported (exact tag, then primary subtag, |
| 202 | then the canonical description — with Traditional Chinese excluded from the |
| 203 | Simplified `zh` fallback). **No bundled skill ships a localized routing |
| 204 | description**, and none is fabricated. The shipped contract is therefore an |
| 205 | explicit, tested fallback: |
| 206 | |
| 207 | - For every skill in the bundle × every locale in `Locale::shipped()` — all 15 |
| 208 | of `en`, `ja`, `zh-Hans`, `zh-Hant`, `pt-BR`, `es-419`, `vi`, `ko`, `ca`, |
| 209 | `de`, `fr`, `id`, `hi`, `ru`, `uk` (`crates/tui/src/localization.rs:70-88`) — |
| 210 | `description_for_locale` returns the canonical English description. |
| 211 | - The rendered catalogue block is byte-identical across all shipped locales. |
| 212 | - Exact-tag match, primary-subtag fallback (`pt-BR` → `description_pt`), and |
| 213 | English fallback are covered against a synthetic authored fixture, so the |
| 214 | resolution paths stay tested even while the bundle itself is English-only. |
| 215 | |
| 216 | If a bundled skill later ships localized routing metadata, the parity test |
| 217 | fails until source-backed coverage is added for it — the fallback contract |
| 218 | cannot silently absorb a translation. |
| 219 | |
| 220 | ## Audit statuses |
| 221 | |
| 222 | Each audited row carries precedence and relationship flags: |
| 223 | |
| 224 | | Status | Meaning | |
| 225 | | --- | --- | |
| 226 | | **Active** | Highest-precedence copy for that canonical name in the scan. | |
| 227 | | **Shadowed** | Same name exists at a higher-precedence root. | |
| 228 | | **Duplicate** | Same canonical name and same package digest as another copy. | |
| 229 | | **Conflict** | Same canonical name, different package digest. | |
| 230 | |
| 231 | External skills with no owned peer (and a valid digest) are **import |
| 232 | candidates**. Externals that conflict with or exactly duplicate an owned copy |
| 233 | can still offer Import — duplicate → already present; conflict → confirm replace |
| 234 | in the selected import scope. |
| 235 | |
| 236 | ## Provenance and markers |
| 237 | |
| 238 | Managed installs write schema **v2** metadata under the skill directory: |
| 239 | |
| 240 | **`.installed-from` (v2)** — written last on successful install/import: |
| 241 | |
| 242 | ```json |
| 243 | { |
| 244 | "schema_version": 2, |
| 245 | "spec": "github:owner/repo", |
| 246 | "url": "https://…", |
| 247 | "source_checksum": "…", |
| 248 | "content_digest": "…", |
| 249 | "installed_name": "my-skill", |
| 250 | "registry_version": null |
| 251 | } |
| 252 | ``` |
| 253 | |
| 254 | - `content_digest` is a bounded package tree hash (not SKILL.md alone). |
| 255 | - Display of URLs strips userinfo, query, and fragment. |
| 256 | - Imports use a local `import:…` provenance and **cannot** be updated from a |
| 257 | registry; re-import or remove them instead. |
| 258 | - Legacy v1 markers are recognized as managed with |
| 259 | `LegacyMetadataUnknown` integrity until refreshed. |
| 260 | |
| 261 | **`.trusted` (v2)** — advisory, digest-bound: |
| 262 | |
| 263 | ```json |
| 264 | { |
| 265 | "schema_version": 2, |
| 266 | "content_digest": "…" |
| 267 | } |
| 268 | ``` |
| 269 | |
| 270 | Trust records review intent. It does **not** sandbox the skill or auto-approve |
| 271 | tools. Content updates clear trust so a stale marker cannot outlive the bytes. |
| 272 | |
| 273 | Manual skills (owned root, no managed marker) are visible but not |
| 274 | update/remove/trust through the managed actions. |
| 275 | |
| 276 | ## Package digest and safety |
| 277 | |
| 278 | Audit and mutation share a bounded package digest: |
| 279 | |
| 280 | - Regular files only; symlinks that escape the skill root or cycle → fail closed. |
| 281 | - Caps on total size, file count, and depth. |
| 282 | - Mutations re-check an expected digest before write (TOCTOU). |
| 283 | - Import/replace keeps a `.bak` until digest + marker finalize succeed; failure |
| 284 | restores the previous owned package. |
| 285 | |
| 286 | ## Readiness |
| 287 | |
| 288 | The audit model has a readiness field and optional provider hook for a future |
| 289 | readiness cache ([#4407](https://github.com/Hmbown/CodeWhale/issues/4407)). |
| 290 | Today, when no cache is wired, readiness is always **`Unknown`**. The manager |
| 291 | does not run readiness probes and does not block mutations on readiness. |
| 292 | |
| 293 | ## Config knobs |
| 294 | |
| 295 | ```toml |
| 296 | # Optional override for discovery preference (not automatically a write target |
| 297 | # unless it is the CodeWhale project/global owned path). |
| 298 | skills_dir = "/path/to/skills" |
| 299 | |
| 300 | [skills] |
| 301 | # When true, runtime discovery skips cross-tool roots (.claude, .agents, …). |
| 302 | # Owned CodeWhale roots and an explicit skills_dir override still apply. |
| 303 | scan_codewhale_only = false |
| 304 | |
| 305 | # Optional registry / install size overrides used by --remote, sync, and install. |
| 306 | # registry_url = "https://…" |
| 307 | # max_install_size_bytes = 5242880 |
| 308 | ``` |
| 309 | |
| 310 | See [CONFIGURATION.md](CONFIGURATION.md) for the full config surface. |
| 311 | |
| 312 | ## Operator checklist |
| 313 | |
| 314 | 1. Prefer `/skills` for day-to-day management; keep `--remote` / `sync` explicit. |
| 315 | 2. Never hand-edit `.claude` / `.agents` / `.cursor` trees to “install” for |
| 316 | Codewhale — import into `.codewhale/skills` instead. |
| 317 | 3. Treat `.trusted` as advisory documentation of review, not a security boundary. |
| 318 | 4. After registry updates that change content, re-trust if you still want the |
| 319 | advisory marker. |
| 320 | 5. Dual project+global copies of the same name need an explicit scope flag on |
| 321 | CLI mutations. |
| 322 |