@rhize/skill-forge 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  **The supply-chain gate for agent skills.**
4
4
 
5
- Status: pre-release (built 2026-07-11)
5
+ Status: published — `@rhize/skill-forge@0.14.0` on npm (`0.16.0` in this repo, pending release),
6
+ 0.x beta (Pro features free until 1.0)
6
7
 
7
8
  > This project is unrelated to the [`skillforge`](https://www.npmjs.com/package/skillforge)
8
9
  > package on npm, which is a Claude Skills *evaluation* framework. `skill-forge` (this package,
@@ -27,19 +28,15 @@ promote/hold/reject decision**, before it is allowed anywhere near your working
27
28
  npx @rhize/skill-forge init
28
29
  ```
29
30
 
30
- Optional, but recommended first: `init` detects which coding agents you have installed — the
31
- matrix covers 73 known agents (Claude Code, Codex CLI, Cursor, Windsurf, OpenCode, Gemini CLI,
32
- and 67 more; see `src/agents.ts`), and every entry's skill-directory paths are verified directly
33
- against the [vercel-labs/skills](https://github.com/vercel-labs/skills) CLI's own source, not
34
- just its README — none are unverified/community guesses today. (The schema carries a
35
- `verified: false` flag for any future entry that can't be confirmed that way; it's unused as of
36
- this release.) `init` lets you pick which of their skill directories should be gated, a default
37
- promotion target, and an optional agent to hand follow-up prompts off to (see
38
- [`--ingest`](#ingestion-handoff---ingest)). No configuration is
39
- required for a first run either way — skip `init` and skill-forge defaults to `<cwd>/.claude/skills`
40
- as its promotion target and `~/.skill-forge/quarantine` as its sandbox (and offers to run `init` for
41
- you the first time `add`/`scan`/`list`/`status` runs with no config present, in an interactive
42
- terminal). See [docs/configuration.md](docs/configuration.md) for the full field reference.
31
+ Optional, but recommended first: `init` detects which coding agents you have installed (73 known
32
+ agents) and lets you pick which of their skill directories to gate, a default promotion target,
33
+ and an optional agent to hand follow-up prompts off to. It also checks whether each picked root is
34
+ under Git version control yet and, if not, offers to give it a baseline commit (exact commands
35
+ shown first, default No). No configuration is required either way — skip `init` and skill-forge
36
+ defaults to `<cwd>/.claude/skills` as its promotion target (and offers to run `init` for you the
37
+ first time `add`/`scan`/`list`/`status` runs with no config present, in an interactive terminal).
38
+ See [docs/commands/init.md](docs/commands/init.md) for the full detection/menu/Git-preflight
39
+ behavior and [docs/configuration.md](docs/configuration.md) for the config field reference.
43
40
 
44
41
  ```bash
45
42
  npx @rhize/skill-forge add <owner>/<skill-name>
@@ -64,864 +61,31 @@ anything ending in `.git`), or a local filesystem path.
64
61
 
65
62
  ## Commands
66
63
 
67
- ```
68
- skill-forge init [options] Detect installed agents and set gate targets / handoff agent
69
- skill-forge add <source> [options] Quarantine-install a skill and run it through the gate
70
- skill-forge scan <source> [options] Gate a skill without installing it (always cleans up)
71
- skill-forge evolve <skill-dir> [options] Self-evolve an installed skill via SkillOpt-Sleep, re-gate, decide (Pro)
72
- skill-forge audit [options] Doctor-style health check over the configured skill/MCP set (alias: doctor)
73
- skill-forge finding accept <fp-prefix> Acknowledge a LOW/MEDIUM audit finding you've reviewed (human-only, no gate effect)
74
- skill-forge finding revoke <fp-prefix> Remove a stored acceptance so the finding reports again
75
- skill-forge finding list [options] List every accepted finding
76
- skill-forge organize [options] Set-level capability registry + dependency graph across configured skills roots (Pro)
77
- skill-forge find [query] [options] Discover skills via skills.sh and check partner security audits (free)
78
- skill-forge watch [options] Drift check across every SOURCES.md provenance ledger (Pro)
79
- skill-forge ingest [options] Hand the pending-ingestion queue off to a coding agent for the decide/absorb pass (Pro)
80
- skill-forge queue close <id> --status <s> Close a queue entry after the decide pass (Pro)
81
- skill-forge refine [options] Capture a project-scope override from real usage feedback (Pro)
82
- skill-forge refine list [options] Show refinement history
83
- skill-forge refine patterns [options] List tracked/ready/generalized/dismissed patterns
84
- skill-forge refine promote <PATTERN-ID> Merge a ready pattern into the user-scope skill (Pro)
85
- skill-forge refine rollback <backup-id> Restore a promotion backup (Pro)
86
- skill-forge refine which <skill> Print override-resolution order for a skill
87
- skill-forge routine [options] One scheduled maintenance pass: audit + drift + registry, cron-friendly (Pro)
88
- skill-forge promote <id> [options] Re-gate a held quarantine entry and promote it
89
- skill-forge reject <id> [options] Discard a held quarantine entry
90
- skill-forge config <sub> [args] Read/write open-ended preferences (list|get|set|unset|propose|review)
91
- skill-forge list List skills currently held in quarantine
92
- skill-forge status Show configuration and quarantine summary
93
- skill-forge guide [topic] Orientation: what this does, your current state, the next command
94
- ```
95
-
96
- ### `init`
97
-
98
- ```bash
99
- skill-forge init # interactive: pick targets, default target, handoff agent
100
- skill-forge init --defaults # non-interactive: agents found on PATH, first as default (CI)
101
- skill-forge init --list # print detected agent skill roots and exit — no writes
102
- skill-forge init --all-agents # don't narrow to agents whose CLI is on PATH — keep every root
103
- ```
104
-
105
- Probes the known agent matrix (`src/agents.ts`) for both project-relative (`.claude/skills`, ...)
106
- and global (`~/.codex/skills`, ...) skill directories that already exist on disk, then writes
107
- `skillsRoots`, `agents`, `defaultTarget`, and (if you pick a handoff agent) `handoffCommand` to
108
- `config.json`. Safe to re-run any time — it always starts from your existing config and only
109
- overwrites the fields it's responsible for. If no `config.json` exists yet, `add`/`scan`/`list`/
110
- `status` offer to run this for you on first use (skipped entirely for `--json`/`--yes`/non-TTY
111
- invocations, so scripted runs never block on a prompt).
112
-
113
- **Menus are keyed on the PATH, not the agent (v0.11).** Many agents share one skills directory —
114
- 18 entries in the agent matrix use `.agents/skills` as their project root — so the gate-target and
115
- default-target menus list each *distinct* root once, labelled with the agents that resolve to it
116
- (`19 agents (project): Amp, Replit, Universal, +16 more`), and `skillsRoots`/`mcpTargets` are
117
- written deduped. `config.agents` still records every agent, since it's an id→root map. Detection
118
- output says so explicitly when roots collapse (`→ 21 agents share 3 distinct skills root(s)`).
119
- Configs written by earlier versions are deduped on load, so no re-run is required to clean one up.
120
-
121
- **Narrowed to agents you actually have (v0.11).** Because a shared directory can't tell you which
122
- of its 18 agents you use, init also checks PATH: a root is pre-selected when at least one agent
123
- mapped to it has its CLI installed, and roots with none are still listed, just switched off with
124
- the reason shown.
125
-
126
- ```
127
- [x] 1. /Users/you/.agents/skills
128
- on PATH: Codex, Gemini CLI (project) (+17 other agents share this path)
129
- [ ] 3. /Users/you/.openclaw/skills
130
- OpenClaw (global) — no verified CLI name to check
131
- ```
132
-
133
- The PATH check is a **look-up only — nothing found is ever executed** (no `--version` probe), and
134
- binary names are held to the same evidence bar as the skill paths: an agent with no verified
135
- command name reports "unknown", never "not installed". `--all-agents` turns narrowing off, and it
136
- disables itself automatically if no agent CLI is found at all, so it can never reduce a working
137
- detection to an empty config. The handoff menu is only *reordered* by it — installed CLIs float to
138
- the top and everything detected stays pickable.
139
-
140
- **Selecting things (v0.11).** The multi-selects take a whole answer at once — `2`, `1 3`, `2, 4`,
141
- `2-5`, plus `all` and `none` — and Enter confirms. Anything unrecognized is named back to you and
142
- ignored, never silently swallowed. The single-choice prompts (default target, handoff agent)
143
- **re-ask** on an answer that isn't one listed number instead of falling back to option 1, so a
144
- multi-value or mistyped answer can no longer write a target you didn't choose. Ctrl+D at any
145
- prompt exits cleanly.
146
-
147
- **Init now ends by offering the audit (v0.8).** After an interactive run writes its config, it
148
- asks "Run the skills & MCP audit now? [Y/n]" (default yes) and, if accepted, runs
149
- `skill-forge audit` interactively — see [`audit`](#audit-v08) below. `init --defaults` runs it too,
150
- but non-interactively (`--yes`): a report is written, with no business-profile prompt, no
151
- foundation scaffold, and no agent handoff. `init --list` and an aborted/empty selection stay
152
- write-free, so neither writes a config nor runs the audit.
153
-
154
- ### `add`
155
-
156
- ```bash
157
- skill-forge add owner/name
158
- skill-forge add https://github.com/owner/repo.git --target ./.claude/skills
159
- skill-forge add ./local-skill-dir --yes
160
- skill-forge add owner/name --json
161
- skill-forge add owner/name --yes --ingest
162
- ```
163
-
164
- Installs the source into a quarantine sandbox, then runs it through the gate: profile → safety
165
- scan → overlap analysis against the configured skills root (Pro) → report. Nothing touches the
166
- target skills root until you decide.
167
-
168
- | Option | Effect |
169
- |---|---|
170
- | *(none)* | Prompts you to **promote**, **hold**, or **reject** the candidate. |
171
- | `-y, --yes` | Skips the prompt and honors the gate verdict: a `block` safety verdict is rejected (process exits nonzero); anything else (`pass`/`warn`) is promoted. |
172
- | `-t, --target <dir>` | Skills root to promote into. Defaults to the config's `defaultTarget`, then `skillsRoots[0]`. |
173
- | `--json` | Prints the gate result (profile, safety findings, overlap) as JSON instead of the terminal report box. Implies non-interactive: the decision is made the same way `--yes` makes it (verdict decides promote/hold/reject), never an interactive prompt. |
174
- | `--ingest` | Pro (free during the 0.x beta). After a successful promote, hands off to a coding agent — see [Ingestion handoff](#ingestion-handoff---ingest). |
175
- | `--skill-map <path>` | Path to a generated `rhize-plugins` skill map (Phase 4 of that repo's skill-map-graph-substrate plan). When given, ranks the candidate's name/description against every `skill` node in the map — a near-duplicate of an already-shipped marketplace skill is folded into the safety findings as a `HIGH`-severity finding (escalates the verdict to `block`, so it's held rather than silently promoted); a moderate overlap is `MEDIUM` (`warn`). Free (not Pro-gated) — this enforces the marketplace's own curation rule ("close the gap, don't duplicate"), not a premium ranking feature. Missing/unreadable map: printed to stderr, never fatal. **Two map variants exist, with different coverage**: the **static** map (`rhize-plugins/generated/skill-map.static.json`) is first-party-only — it only knows about that marketplace's own skills, so it cannot catch a candidate that duplicates an *installed third-party plugin's* skill. The **resolved** map (`~/.claude/context-manager/skill-map.resolved.json`, produced by `rhize-context-manager`) additionally carries the full third-party ecosystem inventory. Point `--skill-map` at the resolved map when you want ecosystem-wide duplicate detection; the static map is enough only when you're curating a single first-party marketplace against itself. |
176
-
177
- **Bugfix (v0.13.0):** `--skill-map` previously matched nothing against either real map variant — the loader read a `type` field that the real generated artifact never sets (it uses `kind`), so every node was silently invisible to the overlap check. `loadSkillMap()` now normalizes `kind` → `type` at load time; regression coverage lives in `test/mapOverlap.realmap.test.ts` against vendored real-map snapshots. If you were relying on `--skill-map` before v0.13.0, it was a no-op — re-run `add`/`scan` on anything you're unsure about.
178
-
179
- **Extends-declared overlap exemption**: a candidate can declare `metadata.rhize.extends:
180
- ["<skill-name>" | "<plugin>/<skill-name>"]` in its SKILL.md frontmatter to mark itself a
181
- deliberate specialization/layering of an existing map skill. When the top `--skill-map` match is
182
- a skill the candidate declares it extends, that finding is downgraded from a HOLD-level safety
183
- finding to an informational notice printed above the report — it never escalates the verdict.
184
- Overlap with any *other* skill (a different match, or a second candidate/skill pair) still holds
185
- at full severity: the exemption applies per declared pair, not globally, so declaring an
186
- extension of skill X never waives overlap with skill Y.
187
-
188
- A promoted skill gets a provenance entry appended to `<target>/SOURCES.md`, and every promote or
189
- hold decision is recorded to `~/.skill-forge/queue.json` (or `$SKILL_FORGE_HOME/queue.json`) — see
190
- [`docs/queue-schema.md`](docs/queue-schema.md) for the entry schema. A reject writes neither —
191
- nothing is left behind to record. These are Pro features that run free during the 0.x beta (see
192
- [docs/pro.md](docs/pro.md#beta-pricing-0x)): with no valid license, `add` still writes them, and
193
- prints a one-line notice above the report instead of skipping them.
194
-
195
- `add`/`scan` also accept `--artifact mcp` (plus `add`-only `--mcp-target <file>`/`--force`) to gate
196
- an MCP server instead of a skill — see [MCP gating](#mcp-gating-v05) below.
197
-
198
- ### `scan`
199
-
200
- ```bash
201
- skill-forge scan owner/name
202
- skill-forge scan owner/name --json
203
- ```
204
-
205
- Runs the same gate pipeline as `add` (profile → safety → overlap → report) but never promotes
206
- anything — the quarantine sandbox is always cleaned up afterward, on success or failure. Exits
207
- nonzero when the safety verdict is `block`. `--json` prints the same gate-result payload shape as
208
- `add`'s. Also accepts `--skill-map <path>` — see `add`'s option table above.
209
-
210
- ### `evolve` (v0.7)
211
-
212
- ```bash
213
- skill-forge evolve .claude/skills/my-skill
214
- skill-forge evolve .claude/skills/my-skill --yes
215
- skill-forge evolve .claude/skills/my-skill --dry-run
216
- skill-forge evolve .claude/skills/my-skill --backend claude --yes
217
- ```
218
-
219
- Pro (free during the 0.x beta). Orchestrates [microsoft/SkillOpt](https://github.com/microsoft/SkillOpt)'s
220
- `skillopt-sleep` CLI (`pip install skillopt`) to propose a self-evolution of an already-installed
221
- skill — harvest recent sessions, generate a candidate replacement `SKILL.md`/`CLAUDE.md`, and stage
222
- it — then runs the **staged proposal**, never the live skill, back through skill-forge's own static
223
- safety ruleset before you decide anything. SkillOpt-Sleep's own validation gate is score-only; it
224
- never content-vets the generated markdown. This closes that gap.
225
-
226
- | Option | Effect |
227
- |---|---|
228
- | `--project <dir>` | Project dir passed to SkillOpt-Sleep as `--project`. Defaults to cwd. |
229
- | `--dry-run` | Uses SkillOpt-Sleep's `dry-run` subcommand — report only, nothing staged. |
230
- | `--backend <name>` | SkillOpt-Sleep backend. Defaults to `mock` (offline, deterministic, no network). |
231
- | `--lookback-hours <n>` | Hours of session history for SkillOpt-Sleep to harvest. |
232
- | `-y, --yes` | Skips interactive prompts (the disclosure confirm below, and the promote/hold/reject prompt) and honors the re-gate verdict automatically. |
233
- | `--json` | Prints the re-gate result as JSON instead of the terminal report. Implies non-interactive, same as `add`'s `--json`. |
234
- | `--force` | Allows re-adopting a staging dir whose proposed content already matches the live skill (see the double-adopt guard below). |
235
-
236
- **Requires `skillopt-sleep` on PATH** — skill-forge never installs it for you. If it's missing,
237
- `evolve` prints `pip install skillopt` + a docs pointer and exits, rather than attempting an
238
- auto-install (the same "detect, don't install" discipline the rest of the gate follows).
239
-
240
- **Data boundary.** The default `mock` backend is fully offline — harvesting and proposal
241
- generation both run locally, no session data leaves the machine. Any other backend (`claude`,
242
- `codex`, `azure_openai`, ...) sends truncated excerpts from harvested sessions and derived tasks to
243
- the provider you selected; per SkillOpt-Sleep's own docs this is not currently guaranteed to be
244
- secret-free. `evolve` prints that disclosure and requires either `--yes` or an interactive `y/N`
245
- confirmation before a non-`mock` run proceeds — `--json` is non-interactive, so a non-`mock` run
246
- under `--json` without `--yes` is refused rather than silently sending data off-machine.
247
-
248
- **Re-gate.** SkillOpt-Sleep only ever *stages* a proposal (`<project>/.skillopt-sleep/staging/<timestamp>/`
249
- — full replacement files, never auto-adopted). `evolve` copies just `proposed_SKILL.md` (and
250
- `proposed_CLAUDE.md`, if present) into a fresh temporary directory and runs skill-forge's own
251
- static safety ruleset over that copy — the same one `add`/`scan` use, at your configured
252
- `strictness`. `report.json`/`diagnostics.json` (SkillOpt-Sleep's own redacted holdout evidence) are
253
- never scanned. The result renders through the same terminal report / `--json` shape as `add`/`scan`.
254
-
255
- **Decision.** Same promote/hold/reject semantics as `add`: `--yes`/`--json` honor the re-gate
256
- verdict (a `block` is rejected); otherwise you're prompted.
257
-
258
- - **promote** — runs SkillOpt-Sleep's own `adopt --staging <dir>` (which backs up the live file(s)
259
- before copying the proposal over them), then records a provenance entry to the target skill's
260
- `SOURCES.md` and a pending-ingestion queue entry (`origin: "evolve"`, see
261
- [docs/queue-schema.md](docs/queue-schema.md)) so a later ingest pass reviews the evolution rather
262
- than an external source.
263
- - **hold** — leaves the staging dir exactly as SkillOpt-Sleep produced it (its own `status` command
264
- still lists it); `report.md` has the full evidence.
265
- - **reject** — deletes the staging dir.
266
-
267
- **Double-adopt guard.** SkillOpt-Sleep's `adopt` has no confirmation of its own, and adopting the
268
- same staging dir twice overwrites *its* backup — the pre-evolution original would be lost. Before
269
- adopting, `evolve` refuses (unless `--force`) when the staged proposal is already byte-identical to
270
- the live skill it would replace, since that's the signature of a staging dir that was already
271
- adopted once.
272
-
273
- ### `audit` (v0.8)
274
-
275
- ```bash
276
- skill-forge audit
277
- skill-forge doctor # alias
278
- skill-forge audit --json --report ./audit.md
279
- skill-forge audit --yes
280
- skill-forge audit --foundation
281
- skill-forge audit --handoff
282
- ```
283
-
284
- A re-runnable, doctor-style health check over the skill/MCP set you've **already** configured —
285
- unlike `add`/`scan`, which gate a new candidate before it's installed, `audit` inventories what's
286
- already there and looks for hygiene issues and consolidation/refinement opportunities. Requires a
287
- real, saved config (`skill-forge init` first) — it never silently audits an invented default.
288
- `init` now ends by offering to run it (see [`init`](#init) above); it's equally safe to run any
289
- time on its own.
290
-
291
- | Option | Effect |
292
- |---|---|
293
- | *(none)* | Interactive: offers to capture/reuse a business profile, then runs the audit, then offers the foundation scaffold and agent handoff. |
294
- | `--json` | Prints the full audit report as JSON instead of the terminal summary. Non-interactive — skips the business-profile prompt. |
295
- | `-y, --yes` | Skips every interactive prompt (business profile, foundation, handoff). Never implies `--foundation` or `--handoff`. |
296
- | `--report <file>` | Write the report here instead of the default `~/.skill-forge/reports/audit-<ISO-timestamp>.md`. Refuses an existing path — never overwrites. |
297
- | `--foundation` | The one skills-root write this command can make: scaffold a `business-foundation` skill from the captured business profile (see below). |
298
- | `--handoff` | Hand the written report off to your configured coding agent with the bundled curation prompt, via the same handoff plumbing as `add --ingest`. |
299
-
300
- **What it inventories/checks.** Every configured `skillsRoots` entry — symlink-aware, deduped by
301
- realpath so a skill reachable via two roots or an aliased symlink is reported once, with alias
302
- locations kept rather than dropped. Per skill: strict frontmatter validation (a missing/unclosed
303
- fence or missing `name`/`description` is a finding, not silently backfilled), `SKILL.md` size and
304
- an estimated token count, and a full `scanSafety` pass — the same safety ruleset `add`/`scan` run.
305
- Every configured `mcpTargets` file: JSON targets get full server enumeration (name, command
306
- basename, package spec, arg/env **counts** — never values); TOML targets (e.g. Codex CLI's
307
- `config.toml`) get the same textual MCP safety scan `--artifact mcp` uses, since there's no
308
- structured TOML enumeration. A per-item failure (unreadable skill, broken symlink, malformed MCP
309
- config) becomes a finding — it never aborts the run.
310
-
311
- **Accepted findings (v0.12).** Every finding in the report carries a fingerprint
312
- (`(fp a1b2c3d4e5f6)`); `Findings` shows **active** findings only, and a separate
313
- **Accepted findings** section lists whatever you've reviewed and accepted via
314
- [`skill-forge finding accept`](#finding-v012) — permanently, with reason and date, even after it
315
- stops matching (see that section for the full contract). `summary.acceptedCount` and each
316
- finding's `fingerprint` are additive `--json` fields; nothing accepted is ever silently deleted.
317
-
318
- **Opportunity pass.** Beyond hygiene, the report surfaces:
319
-
320
- - **Overlap clusters** (Pro, free during the 0.x beta) — cross-root overlap scoring across every
321
- inventoried skill, grouped into connected components, each with a top pairwise score and a
322
- suggested verb code (`ABSORB`/`FORK`/`DEFER` — `opportunities.overlapLocked` in `--json` output
323
- says whether this ran or was Pro-locked, without string-matching `notices`). The fuller
324
- five-verb matrix (adding `REJECT`/`WATCH`) belongs to the agent-side curation prompt's deeper
325
- decide pass (`--handoff`), not this report. Locked → that section shows the standard upgrade
326
- notice and empty clusters; everything else in the report still runs.
327
- - **Evolve-eligible skills** — structurally valid, safety-passing skills, labeled as eligible for
328
- `skill-forge evolve` — an eligibility list, not a judgment that they need refining.
329
- - **A business-foundation opportunity** — offered whenever `config.foundationSkillPath` isn't set
330
- yet.
331
-
332
- **Business profile.** Interactive runs (never under `--yes`/`--json`/non-TTY) open with a
333
- business-name/industry/audiences/workflows/constraints Q&A, preceded by an explicit "don't enter
334
- credentials or confidential customer data" warning. An existing stored profile is offered for
335
- reuse rather than re-asked, and a fresh capture is shown back as a summary before it's saved to
336
- `config.businessProfile`. This profile grounds both the foundation scaffold and the curation
337
- handoff prompt.
338
-
339
- **Report location + privacy.** Written to `~/.skill-forge/reports/audit-<ISO-timestamp>.md` by
340
- default (`--report <file>` to override), created with `{ flag: 'wx', mode: 0o600 }` — exclusive
341
- create (refuses to overwrite an existing report) and owner-only permissions. MCP `env` values and
342
- arbitrary arg values are never written into the report or the `--json` payload — only server/
343
- command names, package specs, and counts.
344
-
345
- **Explicit opt-ins.** `--foundation` is the *only* way this command writes to a skills root: it
346
- scaffolds `<targetRoot>/business-foundation/SKILL.md` from the captured business profile
347
- (containment-checked against `config.skillsRoots`, refuses an existing or symlinked destination,
348
- writes atomically, and runs `scanSafety` on the generated content before it's placed), then
349
- records the path to `config.foundationSkillPath`. `--handoff` is the *only* way this command
350
- launches an agent — same argv-array, no-shell discipline as `--ingest` (see
351
- [Ingestion handoff](#ingestion-handoff---ingest)), using the bundled `assets/curation-prompt.md`
352
- in place of `ingest-prompt.md`. `--yes` (and `init --defaults`, which passes it through) never
353
- implies either — a non-interactive run writes only the report and, if a profile was already
354
- stored, the config; nothing else.
355
-
356
- ### `finding` (v0.12)
357
-
358
- ```bash
359
- skill-forge finding accept <fingerprint-prefix> --reason "Funnel genuinely needs root; verified"
360
- skill-forge finding revoke <fingerprint-prefix>
361
- skill-forge finding list [--stale] [--json]
362
- ```
363
-
364
- Acknowledge an `audit` finding you've reviewed and accepted, so it stops counting against
365
- `routine --fail-on` and the `add`/`scan` advisory while staying **permanently visible** in its own
366
- report section — an audit you can't triage decays into noise, and noise is how the one real
367
- finding gets skimmed. Every finding in an `audit`/`routine` report now carries a 12-char
368
- fingerprint (`(fp a1b2c3d4e5f6)`) you copy into `accept`/`revoke`.
369
-
370
- | Command | Effect |
371
- |---|---|
372
- | `finding accept <fp-prefix> --reason <text>` | Records an acceptance. Re-runs the read-only audit engine to resolve the prefix against what's **currently active** — you can only accept a finding that exists right now, never from a stale report. `--reason` is mandatory (1-200 chars, no control characters). |
373
- | `finding revoke <fp-prefix>` | Removes a stored acceptance so the finding reports again. Resolves against the **store**, not a fresh audit run — the target may no longer be producible, which is exactly when you'd want to clean up. |
374
- | `finding list [--stale] [--json]` | Lists every accepted finding (severity, rule, target, reason, accepted date, fingerprint). `--stale` filters to records whose target no longer exists on disk. |
375
-
376
- **Identity is content-derived, not a name you pick.** A finding's fingerprint is
377
- `sha256(severity + rule + target + the offending line's text)` — deliberately excluding the LINE
378
- NUMBER (drifts on unrelated edits) and deliberately including SEVERITY (so a rule re-tuned to a
379
- higher severity for the same content can't inherit an ack minted against the lower one). Any change
380
- to the offending content **fails open to re-reporting** — this is the design, not a bug: the
381
- alternative (key on rule+path only) is the classic baseline trap, where accepting one benign line
382
- silently suppresses every future line the same rule matches in that file. Full contract, the
383
- per-rule `context` table, and fail-open read semantics: see
384
- [docs/accepted-findings-schema.md](docs/accepted-findings-schema.md).
385
-
386
- **Severity-capped, human-only, no override.** `finding accept` refuses `HIGH`/`CRITICAL` findings
387
- outright — there is no `--force`. The precision problem this command exists to solve lives at
388
- `LOW`/`MEDIUM`; a `HIGH` false positive is a rule bug to fix, not something to baseline. This is a
389
- human-door CLI (`config set` model), not the agent-facing propose/review queue — both
390
- `assets/ingest-prompt.md` and `assets/curation-prompt.md` instruct agents to never run
391
- `finding accept`/`finding revoke` themselves; an agent that judges a finding a false positive says
392
- so in its summary and lets the user run the command.
393
-
394
- **Never a gate input.** Acceptances only ever affect `audit`'s own report classification (and,
395
- downstream, `routine --fail-on` and the `add`/`scan` advisory) — `add`/`scan`/`promote <id>`/
396
- `evolve`'s re-gate never reads the accepted-findings store. See
397
- [docs/gate-policy.md](docs/gate-policy.md#accepted-findings-v012).
398
-
399
- ### `organize` (v0.9, Pro)
400
-
401
- ```bash
402
- skill-forge organize
403
- skill-forge organize --json
404
- skill-forge organize --out ./registry.json
405
- skill-forge organize --usage-snapshot ./skill-monitor-snapshot.json
406
- ```
407
-
408
- Pro (free during the 0.x beta). Builds a set-level view across every configured `skillsRoots`
409
- entry: a **capability registry** (per-skill row — `tier`/`domain`/`consumes`/`provenance`/
410
- `maturity` frontmatter, plus untagged/rot flags) and a **dependency graph** (nodes, `consumes`
411
- edges, orphans, hot resources, dangling edges, cycles). This is the TS port of the plugin's
412
- `index_skills.py` + `build_dependency_graph.py`, merged into one command — graph nodes are keyed
413
- by realpath-qualified stable IDs (not bare frontmatter names), so duplicate names across roots
414
- don't collapse adjacency; duplicate-name ambiguity and unresolved `consumes` targets are reported
415
- rather than silently dropped.
416
-
417
- | Option | Effect |
418
- |---|---|
419
- | `--json` | Print the registry + graph as JSON instead of the terminal summary. |
420
- | `--out <file>` | Write the full report JSON to this path. Refuses an existing path (`wx`, `0o600` — same discipline as `audit --report`). |
421
- | `--usage-snapshot <file>` | Join per-skill usage counts from a skill-monitor snapshot JSON (`usage_joined` in the output); a zero-match snapshot is warned about, not silently ignored. |
422
-
423
- `organize` performs its own self-contained scan — it does not reuse `audit`'s inventory walker
424
- — and, like every other command, never executes anything found under a scanned skills root.
425
-
426
- ### `find` (v0.9, free)
427
-
428
- ```bash
429
- skill-forge find "pdf form filling"
430
- skill-forge find "pdf form filling" --limit 20 --json
431
- skill-forge find --audit owner/skill-name
432
- skill-forge find --get owner/skill-name
433
- skill-forge find --curated
434
- ```
435
-
436
- Free. TS port of the plugin's `skills_sh.py` — discovery only, exactly one mode per invocation
437
- (search, `--audit`, `--get`, or `--curated`; mutually exclusive):
438
-
439
- | Mode | Effect |
440
- |---|---|
441
- | `<query>` (default) | Search skills.sh; each result shows id, install count, and the install hint `skill-forge add <id>` — **not** `npx skills add`, since `add` runs the full local quarantine/gate. |
442
- | `--audit <id>` | Partner security-audit verdicts (Socket, Snyk, Gen Agent Trust Hub, …) for a specific skills.sh id — pass/warn/fail + risk. No auto-audit of search results. |
443
- | `--get <id>` | Detail + file tree for a specific skills.sh id. |
444
- | `--curated` | List skills.sh's curated skills. |
445
- | `--limit <n>` | Max search results (default 10). |
446
- | `--json` | Print the raw API payload as JSON. |
447
-
448
- Search results are labeled **unvetted** — `find` never installs or gates anything itself. Think
449
- of `find --audit` as the partner-verdict layer and `add`/`scan` as the deep local gate; they're
450
- two independent checks, not a replacement for one another.
451
-
452
- **Auth: `VERCEL_OIDC_TOKEN`.** All requests are HTTPS GETs to `https://skills.sh/api/v1`, using a
453
- bearer token read from `process.env.VERCEL_OIDC_TOKEN` — never stored in config, never printed.
454
- Without it, `find` fails loud (exit code 3) with setup guidance:
455
-
456
- ```
457
- 1. skills.sh authenticates with a short-lived Vercel OIDC token
458
- 2. npm i -g vercel && vercel link && vercel env pull (writes VERCEL_OIDC_TOKEN to .env.local)
459
- 3. export VERCEL_OIDC_TOKEN from .env.local into your shell before running this command
460
- (or set it directly: export VERCEL_OIDC_TOKEN=...) — docs: https://skills.sh/docs/api
461
- ```
462
-
463
- Note step 3: `vercel env pull` writes the token into `.env.local`, it does not export it into your
464
- shell — you (or your shell's dotenv loader) still need to export it before `find` can see it.
465
-
466
- ### `watch` (v0.9, Pro)
467
-
468
- ```bash
469
- skill-forge watch
470
- skill-forge watch --offline
471
- skill-forge watch --json
472
- skill-forge watch --skill-map generated/skill-map.static.json
473
- ```
474
-
475
- Pro (free during the 0.x beta). TS port of the plugin's `record_provenance.py --check-drift`.
476
- Scans **every** configured `skillsRoots` entry's `SOURCES.md` provenance ledger (deduped by
477
- realpath, each reported row names its ledger) and reports, per tracked entry, whether the
478
- recorded upstream ref still matches what the source currently resolves to.
479
-
480
- For entries whose `Source` parses as a git URL and whose `Upstream ref` looks like a commit/tag,
481
- `watch` runs a fixed, first-party `git ls-remote <url> [ref]` (argv array, no shell) to compare —
482
- `--offline` skips all network checks and just lists entries for manual comparison. Every other
483
- entry is listed for manual checking regardless.
484
-
485
- **`watch` NEVER executes a ledger's stored drift-check command string.** That string is
486
- attacker-influenceable data — anyone who can write to a skills root's `SOURCES.md` controls it.
487
- It is only ever printed, sanitized, as a suggestion for you (or an agent) to run yourself.
488
-
489
- **`--skill-map <path>` (Phase 4 of `rhize-plugins`' skill-map-graph-substrate plan)** additionally
490
- drift-checks every `fork-of` edge in a generated skill map: for each edge it resolves the local
491
- `skill` node's `path`/`contentHash` and the upstream (`external`) node's `url`/`path`, fetches or
492
- reads the upstream content, and compares content hashes. Same never-execute posture as the ledger
493
- check above: nothing derived from the map — including a fork-of edge's own `driftCheck`
494
- metadata — is ever executed; only fetch/read/hash/compare. A missing/unreadable map is a warning,
495
- never fatal. A node's repo-relative `path` (e.g. `rhize-context-manager/skills/x/SKILL.md`) is
496
- resolved against cwd, the map's own directory, and that directory's parent (in that order) — not
497
- cwd alone — so `watch --skill-map <path>` gives correct verdicts regardless of the directory you
498
- run it from.
499
-
500
- **Verdict**, per fork-of edge — three-way comparison, four verdict states when both hashes below
501
- are present on the map, else the older two-way fallback (`in-sync`/`drifted`, `contentHash` vs
502
- freshly-fetched upstream):
503
-
504
- | local-normalized vs baseline | upstream-now vs baseline | status | actionable |
505
- |---|---|---|---|
506
- | == | == | `in-sync` | no |
507
- | != | == | `local-only` | no (deliberate fork divergence, e.g. Rhize's added frontmatter) |
508
- | == | != | `upstream-moved` | yes |
509
- | != | != | `diverged` | yes |
510
-
511
- The three-way matrix fires only when the local `skill` node carries `contentHashNormalized` (a
512
- hash of the file with Rhize-injected frontmatter stripped, computed once by the rhize-plugins
513
- compiler) **and** the upstream `external` node carries `baselineHash` (the upstream content hash
514
- as of the last human review, recorded in `rhize-plugins`' SOURCES.md and copied onto the node by
515
- that compiler — skill-forge never computes or fetches it itself). Either field missing on a given
516
- edge falls back to the two-way compare for that edge, so older maps keep working. All four
517
- three-way verdicts — `in-sync`, `local-only`, `upstream-moved`, and `diverged` — carry
518
- `baselineHash`/`upstreamHash` in the JSON output; the two-way-fallback rows and
519
- `upstream-unreachable`/`local-missing` never do (no verdict without a successful fetch/read, so
520
- there is nothing to compare against a baseline). Every row also carries an `actionable` boolean
521
- (from `isActionable`), so callers don't have to re-derive "needs attention" from `status`/`detail`
522
- prose — it's `true` for `drifted`, `upstream-moved`, `diverged`, `upstream-unreachable`, and
523
- `local-missing`; `false` for `in-sync` and `local-only`. **Re-baselining**: after reviewing and
524
- adopting an `upstream-moved`/`diverged` change, re-run `rhize-plugins`' `scripts/baseline_upstreams.py`
525
- and commit the updated SOURCES.md — that's the "I reviewed upstream, accept its state" action that
526
- clears the row back to `in-sync`/`local-only`.
527
-
528
- ### `ingest` + `queue close` (v0.9, Pro)
529
-
530
- ```bash
531
- skill-forge ingest
532
- skill-forge ingest --list
533
- skill-forge ingest --list --json
534
- skill-forge queue close <id> --status ingested
535
- skill-forge queue close <id> --status dismissed
536
- ```
537
-
538
- Pro (free during the 0.x beta). The queue-drain UX for pending ingestions. `ingest` (no args)
539
- validates every pending `~/.skill-forge/queue.json` entry — each entry's `quarantinePath`/`installedPath` must
540
- canonicalize under the configured quarantine dir or a configured skills root/MCP target;
541
- mismatched or escaping entries are reported and excluded — then hands the surviving summary off to
542
- the configured coding agent (same handoff plumbing as `add --ingest`) with `assets/ingest-prompt.md`,
543
- which now carries the full queue-drain workflow. `--list` is read-only: it prints pending entries
544
- (`--json` for machine-readable output) without any handoff.
545
-
546
- For a single new source, use `skill-forge add <source> --ingest` instead — that runs the full
547
- quarantine/gate pipeline first; `ingest` only ever drains what's already queued.
548
-
549
- `skill-forge queue close <id> --status ingested|dismissed` lets an agent close out a queue entry
550
- after its decide pass without hand-editing `queue.json` — writes are atomic (temp file + rename).
551
-
552
- ### `refine` (v0.10, Pro)
553
-
554
- ```bash
555
- skill-forge refine --skill my-skill --category hook --override-type patch \
556
- --action insert-after --marker "Only check paths" \
557
- --content "..." --expected "..." --actual "..." --dry-run
558
- skill-forge refine list [--status <s>] [--skill <s>] [--project <p>]
559
- skill-forge refine patterns [--status tracking|ready|generalized|dismissed] [--skill <s>]
560
- skill-forge refine promote <PATTERN-ID> [--dry-run] [--force] [--user-root <dir>]
561
- skill-forge refine rollback <backup-id> [--force]
562
- skill-forge refine which <skill> [--user-root <dir>]
563
- ```
564
-
565
- Pro (free during the 0.x beta). Captures, applies, and generalizes improvements to installed
566
- skills from real usage feedback (see [CLAUDE.md's "refine (v0.10)" section](CLAUDE.md) for the
567
- full architecture notes, or `docs/refinement-schema.md` for the store shape).
568
-
569
- **Capture never mutates a base `SKILL.md`.** `refine` capture/apply writes ONLY project-scope
570
- override artifacts — `SKILL.patch.md` / `SKILL.extend.md` / `skill-config.json`, or a whole-file
571
- override copy for `full`/`hook`/`script` override types. The only command that ever touches a
572
- user-scope base skill is `refine promote`, and only against a `ready` pattern (or with `--force`).
573
-
574
- **Override precedence** (configurable roots):
575
-
576
- ```
577
- 1. PROJECT LOCAL <cwd>/.claude/skills/<skill>/ highest
578
- 2. PROJECT SHARED <cwd>/skills/<skill>/
579
- 3. USER SCOPE first configured skillsRoots entry outside cwd (fallback ~/.claude/skills)
580
- ```
581
-
582
- `refine which <skill>` prints this resolution order and which override files exist at each scope —
583
- the "why didn't my patch take effect" debugging aid, made explicit and read-only. Both `refine
584
- which` and `refine promote` accept `--user-root <dir>` to override the detected user-scope skills
585
- root (default: the first configured `skillsRoots` entry outside the current working directory,
586
- falling back to `~/.claude/skills`) — useful when the config's default root doesn't match the
587
- skill you're targeting.
588
-
589
- **Non-interactive capture contract.** Capture flags: `--skill --category --target --override-type
590
- <patch|extend|config|full|hook|script|new> --action <append|prepend|replace-section|insert-after|
591
- insert-before|delete-section> --marker --content <text> --content-file <file> --expected --actual
592
- --example --outcome --root-cause --pattern-id --scope <local|shared> --dry-run --json --yes
593
- --handoff`. `--content` takes override content inline; `--content-file <file>` reads it from a
594
- file instead (mutually exclusive with `--content`) — use it for anything multi-line rather than
595
- fighting shell quoting. All seven override types
596
- are supported: `patch`/`extend`/`config` are rendered from the flags; `full`/`hook`/`script` are
597
- verbatim override-file writes at project scope (content required); `new` creates an extension file
598
- for a capability that doesn't exist in the base skill yet. Always run `--dry-run` first and confirm
599
- the preview before writing for real — `--yes` skips re-prompting for a confirmation already given,
600
- it does not replace the dry-run preview.
601
-
602
- **The judgment step is the agent's job**, same pattern as `--ingest`/`audit --handoff`: a
603
- bundled `assets/refine-prompt.md` carries the gap-analysis rubric (category/override-type decision
604
- tables, guided-mode triggers), the patch-action syntax, the pattern-fingerprint/generalization
605
- criteria, and the verification step. `refine --handoff`
606
- (opt-in) launches your configured agent with it, same handoff plumbing as `--ingest`; `--yes` never
607
- implies `--handoff`.
608
-
609
- **Pattern tracking and promotion.** A pattern becomes `ready` only when it recurs in a **second,
610
- genuinely different project** — repeat captures in the same project never flip it (occurrence
611
- `count` is derived from unique project identities, not raw refinement counts; see
612
- `docs/refinement-schema.md`). `refine patterns` lists tracked/ready/generalized/dismissed patterns,
613
- filterable by `--status --skill --project`. `refine promote <PATTERN-ID>` merges a `ready` pattern
614
- into the user-scope base skill: it backs up affected files first (manifest with per-file sha256 +
615
- original bytes + mode, tombstones for files that didn't exist), stages the write, validates, then
616
- renames into place — `--dry-run` previews the diff without writing, `refine rollback <backup-id>`
617
- restores from the manifest (refusing if current files have drifted since promote, unless
618
- `--force`). On promote, a `SOURCES.md` provenance entry is appended (verb `DEFER`, notes
619
- `"generalized from PAT-xxxx via skill-forge refine"`).
620
-
621
- **`evolve` vs. `refine`.** Both improve an already-installed skill, but at different scopes and
622
- triggers: `evolve` (v0.7) is *automated, whole-skill* optimization — it hands the entire skill off
623
- to SkillOpt-Sleep to propose a fresh replacement, re-gates the proposal, and lets you adopt or
624
- reject it wholesale. `refine` is *targeted, human/agent-driven* — it captures one specific observed
625
- gap ("expected X, got Y") and writes the smallest override that closes it, tracked and eventually
626
- generalized only once the same gap recurs elsewhere. A `refine` patch on top of an `evolve`d skill
627
- is fine; both record their own provenance entry, so the `SOURCES.md` ledger shows which change came
628
- from which mechanism.
629
-
630
- **Legacy store — deliberate skip, not a migration.** `~/.claude/skill-refinements/` is a legacy
631
- location some users may have from earlier tooling — three flat markdown notes, no structured
632
- ledgers, no schema — and there is no migration from it. Files there stay readable in place; if
633
- anything in them still matters, re-capture it via `skill-forge refine` against the greenfield JSON
634
- store described in [`docs/refinement-schema.md`](docs/refinement-schema.md). Markdown ledgers like
635
- `refinement-history/*.md`, `aggregated-patterns.md`, or `generalization-queue.md` are not
636
- recreated — JSON is the store; `refine list`/`refine patterns` are the human-readable view over it.
637
-
638
- **Auto-trigger hooks — templates, not automation.** A CLI cannot hook a running Claude Code
639
- session, so two auto-trigger hooks ship here as documented templates instead:
640
- `assets/hooks/refinement-detector.sh` (detects refinement-shaped language in a prompt) and
641
- `assets/hooks/session-end.sh` (prompts after a substantial session). Both are optional, inert if
642
- `skill-forge` isn't on `PATH`, and never call `skill-forge` themselves — they only print a
643
- suggestion. Wire either one in by adding it to your `.claude/settings.json` (or
644
- `~/.claude/settings.json`) hooks section — the exact snippet is in each script's own header
645
- comment:
646
-
647
- ```json
648
- {
649
- "hooks": {
650
- "UserPromptSubmit": [
651
- { "hooks": [{ "type": "command", "command": "bash /path/to/refinement-detector.sh" }] }
652
- ],
653
- "SessionEnd": [
654
- { "hooks": [{ "type": "command", "command": "bash /path/to/session-end.sh" }] }
655
- ]
656
- }
657
- }
658
- ```
659
-
660
- **Security invariants** (same regime as v0.8/v0.9): nothing from a skill being refined is ever
661
- executed. Patch application writes ONLY under the resolved target scope dir for the named skill
662
- (containment-checked realpath, refuses a symlinked destination file); promotion writes ONLY under
663
- user scope, backup first. All child processes are argv arrays (git only, for context gathering).
664
- Control-char sanitization on every untrusted string that lands in human-readable output. `--yes`
665
- never implies `--handoff`. Applying a patch whose target `SKILL.md` is missing is refused; promoting
666
- a non-`ready` pattern without `--force` is refused.
667
-
668
- ### `routine` (v0.11, Pro)
669
-
670
- ```bash
671
- skill-forge routine # audit + drift + registry, one report, writes nothing else
672
- skill-forge routine --json --offline # for a scheduler: one JSON document, no network
673
- skill-forge routine --fail-on high # exit 1 when a high/critical finding exists
674
- skill-forge routine --housekeeping # opt in to bounded writes (see below)
675
- ```
676
-
677
- The whole maintenance pipeline in one command, shaped for cron. Every other command answers one
678
- question; keeping a set healthy means running four and correlating them by hand, which is exactly
679
- what nobody does on a schedule. `routine` runs the audit, drift, and registry **engines**,
680
- correlates them into one report and one summary, and exits with a code a scheduler can alert on.
681
-
682
- **Non-interactive by construction** — it never prompts, so it cannot hang a scheduled job. It is
683
- also the only schedulable command that writes, so what it may write is fenced:
684
-
685
- | | writes |
686
- |---|---|
687
- | always | the combined report + `audit-state.json`, both under `~/.skill-forge` |
688
- | `--housekeeping` | prunes quarantine sandboxes older than `--prune-days` (default 30); closes queue entries whose skill is gone from disk |
689
- | `--handoff` | launches your configured agent with the curation prompt |
690
-
691
- Housekeeping and handoff are **opt-in**: a bare `skill-forge routine` in a crontab reports and
692
- nothing else. A step that was requested but could not run — a crashed drift check, a failed
693
- prune, an unreadable queue — is listed under **Incomplete steps** and **always exits nonzero**,
694
- independent of `--fail-on`: `--fail-on` grades what the audit *found*, whereas a degraded run
695
- means the routine did not do what was scheduled, and a cron job that exits 0 on that is blind. Pruning only ever deletes inside the quarantine dir — never a skills root — is
696
- age-bounded, `--dry-run`-able, and lists every path in the report. A queue entry is only closed
697
- when the skill it points at no longer exists; a pending entry whose skill is still installed is
698
- undone work, not an orphan. `routine` never installs, promotes, or rejects: no candidate enters a
699
- skills root without a human or agent decision, and a cron job is not that.
700
-
701
- **Accepted findings (v0.12)** are honored the same way `audit` honors them: `--fail-on` grades
702
- **active** findings only, so accepting every remaining finding via `skill-forge finding accept`
703
- turns a red `--fail-on any` green, and any new or changed finding turns it red again. A
704
- corrupt/missing accepted-findings store fails open (nothing gets suppressed) and surfaces as a
705
- report **notice**, never as an "Incomplete step" — see
706
- [docs/accepted-findings-schema.md](docs/accepted-findings-schema.md).
707
-
708
- ```
709
- 0 9 * * 1 skill-forge routine --offline --fail-on high --housekeeping
710
- ```
711
-
712
- ### `promote <id>` / `reject <id>` (v0.11)
713
-
714
- Answering **hold** at the gate used to be a dead end: the sandbox sat in quarantine and no command
715
- could ever finish the decision. These close that loop.
716
-
717
- ```bash
718
- skill-forge promote <id> # re-gate the held sandbox, then promote/hold/reject
719
- skill-forge reject <id> # discard it
720
- ```
721
-
722
- Works for both artifact types: a held MCP candidate is re-gated as an MCP server and written into
723
- your MCP config (`--mcp-target`), with env values emptied and unpinned `npx` specs pinned, exactly
724
- as `add --artifact mcp` would.
725
-
726
- Promotion also re-validates the candidate against the state it was gated in: each skill dir must
727
- still be a real directory rather than a symlink, no symlink inside it may resolve out of the
728
- sandbox, and its content digest must still match the one captured at gate time. Otherwise the gap
729
- between "scanned" and "installed" — days, for a hold — is a window in which vetted content can be
730
- swapped for something that never passed the gate.
731
-
732
- `promote` **re-gates** rather than promoting blind. The original run's gate result was never
733
- persisted, so the alternative would be a queue entry with a fabricated gate record — and a hold
734
- may be days old, with the ruleset and your installed set since changed. Re-scanning is static and
735
- cheap. Ids come from `skill-forge list`; anything that resolves outside your quarantine dir
736
- (traversal, absolute path, symlink) is refused.
737
-
738
- ### `config` (v0.11)
739
-
740
- `businessProfile` is five fixed fields, so anything else learned about you has nowhere to live.
741
- Preferences are open-ended key/value facts that accumulate without a schema change:
742
-
743
- ```bash
744
- skill-forge config set prefers-typescript "strict mode, no any"
745
- skill-forge config list
746
- skill-forge config review
747
- ```
748
-
749
- **Agents get a different door.** `set` is for a human at a keyboard and takes effect immediately;
750
- `propose` only ever queues something for review:
751
-
752
- ```bash
753
- skill-forge config propose deploys-on vercel --origin "ingest agent" --note "seen in 3 skills"
754
- ```
755
-
756
- An agent draining the queue reads untrusted skill content, so anything it concludes about you is
757
- downstream of text an attacker may have written. If agents could write preferences directly, a
758
- malicious `SKILL.md` could talk one into persisting an instruction into your config, where every
759
- later run would read it back as *your stated preference* — a durable prompt-injection foothold
760
- with a laundering step in the middle. Routing agent writes through review means the worst case is
761
- a proposal you decline. `review` is interactive, or takes an explicit `--accept-all`/`--reject-all`;
762
- it refuses to fall through to a default in a non-interactive shell.
763
-
764
- Both doors run the same validation: kebab-case keys, a length cap, no control characters, and a
765
- refusal for anything credential-shaped — by key name (`api-key`, `*-token`) or by value shape
766
- (`sk-…`, `ghp_…`, `AKIA…`, PEM blocks, JWTs). `config.json` is plain text; secrets belong in your
767
- keychain. Hand-edited entries that fail those checks are dropped on load rather than rendered.
768
-
769
- ### `list` / `status`
770
-
771
- `list` shows what's currently held in quarantine (installed via `add`, answered "hold", not yet
772
- promoted or rejected). `status` shows the resolved configuration (skills roots, quarantine dir,
773
- strictness) plus a count of held entries.
774
-
775
- ### `guide` (v0.11)
776
-
777
- ```bash
778
- skill-forge guide # what this does, where you are, what to run next
779
- skill-forge guide verbs # a single topic
780
- ```
781
-
782
- Orientation for someone — or some agent — dropping into an existing session. `--help` lists flags
783
- but can't tell you what to do with them, and `status` prints configuration without saying what it
784
- means; `guide` answers "what is my current state, and what is the next command". It reads your
785
- config, quarantine, and queue and names one next step, unfinished work first: nothing configured →
786
- `init`; entries awaiting a decide-pass verb → `ingest`; candidates still held → `list`; no audit
787
- ever run → `audit`.
788
-
789
- ```
790
- Where you are:
791
- config: /Users/you/.skill-forge/config.json
792
- gate targets: 3 (default: /Users/you/.agents/skills)
793
- quarantined: 2
794
- queue: 12 pending
795
-
796
- Suggested next step:
797
- skill-forge ingest
798
- 12 promoted items still awaiting a decide-pass verb
799
- ```
800
-
801
- Topics: `pipeline`, `verbs`, `queue`, `mcp`, `refine`, `maintenance`, `pro`. **Read-only and
802
- non-interactive** — it writes nothing and never prompts, so it's safe to run inside an agent turn,
803
- and with no config it says so rather than reciting default placeholder paths as if they were
804
- settings.
805
-
806
- ## MCP gating (v0.5)
807
-
808
- `add` and `scan` can also gate an **MCP server** instead of a skill — the same quarantine →
809
- profile → safety scan → overlap analysis → report → promote/hold/reject pipeline, applied to an
810
- MCP server candidate rather than a skill:
811
-
812
- ```bash
813
- skill-forge scan ./my-mcp-server --artifact mcp
814
- skill-forge add @scope/some-mcp-server --artifact mcp --mcp-target ~/.claude.json
815
- skill-forge add https://github.com/owner/mcp-server.git --artifact mcp --yes
816
- ```
817
-
818
- Pass `--artifact mcp` explicitly — skill-forge never guesses. In particular, an npm package name
819
- (`@scope/name`, or a bare `some-mcp-server` name) is only treated as an npm source with this flag;
820
- without it, the same string resolves as a skills.sh `owner/name` slug instead (no ambiguity
821
- guessing between the two).
822
-
823
- **Source forms**
824
-
825
- | Form | Example | How it's fetched |
64
+ | Command | Description | Docs |
826
65
  |---|---|---|
827
- | Local directory | `./my-mcp-server` | Copied into quarantine, same as a local skill source. |
828
- | Git URL | `https://github.com/owner/mcp-server.git` | Shallow-cloned into quarantine, same as a git skill source. |
829
- | npm package | `@scope/name` or `some-mcp-server` | `npm pack <name> --ignore-scripts --pack-destination <quarantine>`, then tarball **extraction only** — never `npm install`, never lifecycle scripts. Every tarball member path is validated (absolute paths and `..` segments are rejected) before extraction. |
830
-
831
- **What's gated**
832
-
833
- Safety runs the same built-in deny-pattern ruleset used for skills (curl\|bash, credential-file
834
- access, etc.) plus MCP-specific rules: inline credential values in config/env (quoted or
835
- unquoted), unpinned `npx` launch commands — MEDIUM with `-y`/`--yes` (silent install), **LOW
836
- without it (v0.12)**, since npx still prompts before the first install but the tag floats once
837
- cached (a moving/dist tag like `@latest` counts as unpinned either way, and `npx` is recognized by
838
- basename so a full path or `npx.cmd` can't evade it) — `--dangerously-*`/`--no-sandbox` flags, and
839
- filesystem-root launch args — see the
840
- [MCP safety ruleset table](docs/gate-policy.md#mcp-safety-ruleset). Overlap analysis (Pro, free
841
- during the 0.x beta) ranks the candidate against the server entries already present in your
842
- configured `mcpTargets` files, instead of against a skills root.
843
-
844
- **Capability profile (v0.6).** The report also includes a **static** capability summary — tool,
845
- resource, and prompt counts, plus a `declaredConfidence` (`high`/`partial`/`none`) — parsed from
846
- the candidate's `package.json`, any shipped `.mcp.json`/manifest, and MCP SDK source-text patterns
847
- (`server.tool(...)`, `setRequestHandler(ListToolsRequestSchema, ...)`, etc.):
848
-
849
- ```
850
- Artifact type : mcp
851
- Capabilities : 2 tools, 1 resource, 0 prompts (declaredConfidence: high)
852
- ```
853
-
854
- This is free (it's profiling, not a Pro feature) and **never derived by running the candidate
855
- server** — anything that can't be determined from source text is reported as undetermined rather
856
- than discovered by executing it. `--json` includes the full `tools`/`resources`/`prompts` name
857
- lists under `profile.capabilities`.
858
-
859
- **Promote semantics**
860
-
861
- Promoting an MCP candidate writes ONE server entry into a target MCP config file's `mcpServers`
862
- map — it never touches a skills root:
863
-
864
- ```json
865
- { "mcpServers": { "<name>": { "command": "...", "args": ["..."], "env": { "SOME_KEY": "" } } } }
866
- ```
867
-
868
- - **Env values are never copied** from the candidate — every declared env var is written as an
869
- empty string, and skill-forge prints the var names you need to fill in yourself.
870
- - **Target resolution:** `--mcp-target <file>`, else `config.mcpTargets[0]`, else `<cwd>/.mcp.json`.
871
- - **Existing target file:** backed up first to `<file>.bak-<timestamp>`.
872
- - **Existing same-name server entry:** refused unless `--force` is passed.
873
- - **Missing target file/parent dirs:** created.
874
- - A candidate config listing more than one server gates/promotes the first (same "N found — using
875
- the first" convention `add`/`scan` already use for a multi-skill source), noted on stderr.
876
- - **Version pinning is enforced, including on a candidate's own documented entry (v0.7).** A
877
- candidate's `.mcp.json` server entry is used for `command`/`args` when present, but an unpinned
878
- `npx` spec there (`"args": ["-y", "pkg@latest"]`) is no longer written through as-is: it's
879
- **auto-pinned** to `<package>@<version>` when it matches the version skill-forge scanned from the
880
- candidate's own (self-declared) `package.json`, or **promotion is refused** (with an explanation
881
- and no override flag) when it can't be safely auto-pinned — a different package name, no version
882
- found, or a more complex shape (e.g. a `-p`/`--package` dependency, or more than one spec).
883
- See [docs/gate-policy.md](docs/gate-policy.md#mcp-promote-version-pin-enforcement-v07).
884
-
885
- There's no MCP equivalent of the skill provenance ledger (`SOURCES.md`) — the pending-ingestion
886
- queue (Pro) and `--ingest` handoff both apply the same way, keyed on the written config file path
887
- instead of an installed skill directory. The queued entry carries the candidate's static capability
888
- profile (`capabilities`, v0.6, above), so a `--ingest` handoff run on an MCP promote has real
889
- material to work with: `assets/ingest-prompt.md` branches on `artifactType: "mcp"` and walks the
890
- same five-verb decide (DEFER/ABSORB/FORK/REJECT/WATCH) applied to a server instead of a skill —
891
- compare declared capabilities against what's already configured, then keep/tighten/remove the
892
- promoted config entry accordingly. Same static-only rule as the CLI's own profiling: the deep pass
893
- never runs or installs the candidate server to inspect it.
894
-
895
- **TOML-format agents: detect-only.** `skill-forge init` detects MCP config files for every known
896
- agent, including TOML-format ones (Codex CLI's `config.toml`) — they show up in `init`'s MCP-target
897
- list and can be selected into `config.mcpTargets` for **overlap ranking**. But **promote only
898
- writes JSON-shaped targets** (`{ "mcpServers": { ... } }`); pointing `--mcp-target` at (or letting
899
- `config.mcpTargets[0]` resolve to) a TOML file fails when the promote step tries to parse it as
900
- JSON. Detection/overlap is agent-format-agnostic; writing is JSON-only.
66
+ | `init [options]` | Detect installed agents and set gate targets / handoff agent | [docs/commands/init.md](docs/commands/init.md) |
67
+ | `add <source> [options]` | Quarantine-install a skill and run it through the gate | [docs/commands/add.md](docs/commands/add.md) |
68
+ | `scan <source> [options]` | Gate a skill without installing it (always cleans up) | [docs/commands/scan.md](docs/commands/scan.md) |
69
+ | `evolve <skill-dir> [options]` (Pro) | Self-evolve an installed skill via SkillOpt-Sleep, re-gate, decide | [docs/commands/evolve.md](docs/commands/evolve.md) |
70
+ | `audit [options]` (alias `doctor`) | Doctor-style health check over the configured skill/MCP set | [docs/commands/audit.md](docs/commands/audit.md) |
71
+ | `finding accept\|revoke\|list` | Acknowledge, revoke, or list reviewed audit findings (human-only, no gate effect) | [docs/commands/finding.md](docs/commands/finding.md) |
72
+ | `organize [options]` (Pro) | Set-level capability registry + dependency graph across configured skills roots | [docs/commands/organize.md](docs/commands/organize.md) |
73
+ | `find [query] [options]` | Discover skills via skills.sh and check partner security audits (free) | [docs/commands/find.md](docs/commands/find.md) |
74
+ | `watch [options]` (Pro) | Drift check across every `SOURCES.md` provenance ledger | [docs/commands/watch.md](docs/commands/watch.md) |
75
+ | `ingest [options]` / `queue close <id>` (Pro) | Hand the pending-ingestion queue off to a coding agent for the decide/absorb pass | [docs/commands/ingest.md](docs/commands/ingest.md) |
76
+ | `refine [options]` (Pro) | Capture, list, review, promote, and roll back project-scope skill overrides | [docs/commands/refine.md](docs/commands/refine.md) |
77
+ | `insight <subcommand>` (Pro) | Turn sources into evidence-backed capability plans and Jira manifests | [docs/commands/insight.md](docs/commands/insight.md) |
78
+ | `routine [options]` (Pro) | One scheduled maintenance pass: audit + drift + registry, cron-friendly | [docs/commands/routine.md](docs/commands/routine.md) |
79
+ | `promote <id>` / `reject <id>` [options] | Re-gate and finish a held quarantine decision, or discard it | [docs/commands/promote-reject.md](docs/commands/promote-reject.md) |
80
+ | `config <sub> [args]` | Read/write open-ended preferences (`list`\|`get`\|`set`\|`unset`\|`propose`\|`review`) | [docs/commands/config.md](docs/commands/config.md) |
81
+ | `list` / `status` | List held quarantine entries, or show configuration and quarantine summary | [docs/commands/list-status.md](docs/commands/list-status.md) |
82
+ | `guide [topic]` | Orientation: what this does, your current state, the next command | [docs/commands/guide.md](docs/commands/guide.md) |
83
+
84
+ `add`/`scan` also gate an **MCP server** instead of a skill via `--artifact mcp` — see
85
+ [docs/mcp-gating.md](docs/mcp-gating.md).
901
86
 
902
87
  ## Free vs. Pro
903
88
 
904
- | Capability | Free | Pro |
905
- |---|:---:|:---:|
906
- | Quarantine install (skills.sh slug / git / local path) | ✓ | ✓ |
907
- | Profile (name, version, license, structure, MCP/tool deps) | ✓ | ✓ |
908
- | Safety gate — built-in ruleset + SkillSpector shell-out | ✓ | ✓ |
909
- | Terminal report + `--json` | ✓ | ✓ |
910
- | Promote / hold / reject decision | ✓ | ✓ |
911
- | `init` setup wizard (agent detection, gate targets, handoff agent) | ✓ | ✓ |
912
- | Overlap analysis against your configured skill set | | ✓ |
913
- | Provenance ledger (`SOURCES.md` audit trail) | | ✓ |
914
- | Pending-ingestion queue + `--ingest` handoff | | ✓ |
915
- | `evolve` — SkillOpt-Sleep self-evolution, re-gating, provenance, queueing (v0.7) | | ✓ |
916
- | `audit` — inventory, hygiene findings, report, business profile, foundation scaffold (v0.8) | ✓ | ✓ |
917
- | `audit`'s cross-root overlap clusters (v0.8) | | ✓ |
918
- | `organize` — set-level capability registry + dependency graph (v0.9) | | ✓ |
919
- | `find` — skills.sh discovery + partner audit verdicts (v0.9) | ✓ | ✓ |
920
- | `watch` — provenance drift check across every `SOURCES.md` ledger (v0.9) | | ✓ |
921
- | `ingest` + `queue close` — pending-queue drain handoff (v0.9) | | ✓ |
922
- | `refine` — capture/apply/generalize project-scope skill overrides (v0.10) | | ✓ |
923
- | `finding accept`/`revoke`/`list` — acknowledge audit findings, never a gate input (v0.12) | ✓ | ✓ |
924
-
925
89
  Free is the complete safety gate on its own — quarantine, profile, safety scan, and an explicit
926
90
  promote/reject decision, with nothing held back. Pro is the curation layer on top: whether a new
927
91
  candidate duplicates something you already have, and an ongoing provenance record across your
@@ -929,73 +93,29 @@ whole skill set rather than a single install-time decision.
929
93
 
930
94
  **Everything free until 1.0.** This is a 0.x beta build, and the Pro tier's runtime license check
931
95
  is intentionally asleep for the whole 0.x line: overlap analysis, the provenance ledger, and the
932
- pending-ingestion queue / `--ingest` handoff all run for everyone, licensed or not. Without a valid
933
- license (`SKILL_FORGE_LICENSE` env var or `config.json`'s `licenseKey`, verified offline), `add`
934
- prints a one-line notice — `Pro feature (...) — free during the 0.x beta; will require a license at
935
- 1.0.` — above the report (or in the `--json` payload's `notices` array) and otherwise runs exactly
936
- as a licensed run would. At 1.0 the lock re-arms and these features go back to requiring a valid
937
- key. The set-level organizer and the skills.sh partner-audit enrichment are not yet exposed by any
938
- CLI command, licensed or not, beta or not. See [docs/pro.md](docs/pro.md) for the per-feature
939
- implementation status.
96
+ pending-ingestion queue / `--ingest` handoff all run for everyone, licensed or not — `add` prints a
97
+ one-line "free during the beta" notice above the report when no valid license is set, and otherwise
98
+ runs exactly as a licensed run would.
99
+
100
+ See [docs/pro.md](docs/pro.md#free-vs-pro) for the full Free/Pro capability table and the
101
+ per-feature implementation status.
940
102
 
941
103
  ## Security model
942
104
 
943
- - **Quarantine-first.** Every source — skills.sh slug, git URL, or local path — is installed into
944
- an isolated sandbox (`~/.skill-forge/quarantine/<id>/`) before anything is inspected. Nothing
945
- reaches your working skill set without an explicit promote decision.
105
+ - **Quarantine-first.** Every source is installed into an isolated sandbox before anything is
106
+ inspected — nothing reaches your working skill set without an explicit promote decision.
946
107
  - **Built-in safety ruleset, always on, fully offline.** A deny-pattern scan (curl/wget-into-shell,
947
- base64-obfuscated exec, reverse shells, recursive force-delete, credential-file access/exfil,
948
- dynamic eval, `shell=True` subprocess, persistence via shell rc files or cron, `sudo` usage) runs
949
- against every candidate with no external dependency and no network call. See
950
- [docs/gate-policy.md](docs/gate-policy.md) for the full rule table.
108
+ base64-obfuscated exec, reverse shells, credential-file access/exfil, `shell=True` subprocess,
109
+ persistence via shell rc files or cron, `sudo` usage, and more) runs against every candidate with
110
+ no external dependency and no network call.
951
111
  - **Block on HIGH/CRITICAL.** Any finding at `HIGH` or `CRITICAL` severity blocks the candidate
952
- outright (verdict `block`); a lower-severity finding produces `warn`; a clean scan is `pass`.
953
- `--yes` honors this: `block` is rejected automatically.
954
- - **npm-sourced MCP candidates: `--ignore-scripts` + tarball member validation.** An npm-package
955
- MCP source is fetched with `npm pack --ignore-scripts`, and the tarball's member list is checked
956
- with `tar -tzf` before extraction — any absolute path or `..` path segment refuses the extract
957
- with an error instead of running `tar -xzf` (a path-traversal guard against a malicious tarball
958
- writing outside the quarantine sandbox).
959
- - **SkillSpector, when installed.** If [SkillSpector](https://github.com/NVIDIA/SkillSpector)
960
- (Apache-2.0) is on `PATH`, skill-forge shells out to it (`--no-llm` by default, so scanned skill
961
- content is never sent to an external LLM provider) and merges its findings into the same report.
962
- Purely additive — its absence never blocks the gate.
963
- - **skills.sh partner audits — implemented, not yet wired into the CLI pipeline.** skill-forge
964
- ships a client for skills.sh's documented `/api/v1/skills/audit` endpoint (partner verdicts from
965
- Socket, Snyk, Gen Agent Trust Hub, Runlayer, ZeroLeaks), gated on a user-supplied
966
- `VERCEL_OIDC_TOKEN`. The client exists (`src/gate/skillsSh.ts`) but `add`/`scan` do not call it
967
- yet in this build — see [docs/gate-policy.md](docs/gate-policy.md) for current status.
968
-
969
- ## Ingestion handoff (`--ingest`)
970
-
971
- `skill-forge` deliberately doesn't try to decide *what to extract* from a skill worth adopting —
972
- that deeper judgment (which patterns to keep, whether to absorb into an existing skill vs. fork a
973
- new one, verifying the result beats baseline) is a job for a coding agent, not the gate. `--ingest`
974
- hands a promoted skill off to one, running the bundled, agent-neutral prompt at
975
- `assets/ingest-prompt.md` — works with any agent. ABSORB extractions from an ingest pass route
976
- through `skill-forge refine` in this same package — see [`refine`](#refine-v010-pro) above and
977
- [docs/forge-workflow.md](docs/forge-workflow.md).
978
- The same flag works on an MCP server promote (`--artifact mcp --ingest`, v0.6): the bundled prompt branches on the
979
- queue entry's `artifactType` and runs the matching decide pass — see
980
- [MCP gating](#mcp-gating-v05) above.
981
-
982
- ```bash
983
- skill-forge add owner/name --yes --ingest
984
- ```
985
-
986
- The command that gets run, in order:
112
+ outright; a lower-severity finding produces `warn`; a clean scan is `pass`.
113
+ - **npm-sourced MCP candidates:** fetched with `--ignore-scripts`, and every tarball member path is
114
+ validated before extraction (a path-traversal guard).
115
+ - **SkillSpector, when installed,** is purely additive — its absence never blocks the gate.
987
116
 
988
- 1. `config.handoffCommand` — an argv-style template (`["claude", "-p", "{prompt}"]`-shaped) set by
989
- `skill-forge init`'s "handoff agent" prompt, with `{path}` (the installed skill) and `{prompt}`
990
- (the bundled prompt file) substituted in. Never shell-parsed, so it's safe even if a substituted
991
- path contains shell metacharacters.
992
- 2. Otherwise, the first known agent binary found on `PATH` (`claude`, `codex`, `cursor-agent`,
993
- `windsurf`, `opencode`, `gemini`), invoked generically with the prompt.
994
- 3. Otherwise, skill-forge prints the prompt path and skill path for you to hand off yourself.
995
-
996
- Every promote or hold is recorded to the pending queue (`~/.skill-forge/queue.json`) regardless of
997
- `--ingest` — nothing is lost if you skip the handoff. See [`docs/queue-schema.md`](docs/queue-schema.md)
998
- for the entry schema.
117
+ See [docs/security-model.md](docs/security-model.md) for the full rule table and current status of
118
+ each check.
999
119
 
1000
120
  ## Configuration
1001
121
 
@@ -1047,6 +167,18 @@ the provenance ledger, and the pending-ingestion queue / `--ingest` handoff all
1047
167
  with a one-line "free during the beta" notice if no valid key is set. See
1048
168
  [docs/pro.md](docs/pro.md) for details.
1049
169
 
170
+ **How do I roll back a customization?**
171
+ It depends what changed. If `skill-forge init` gave a skills root a Git baseline (or you'd already
172
+ committed it yourself), rolling back a change under that root is ordinary Git —
173
+ `git checkout -- <path>` or `git reset`, run by you; skill-forge doesn't ship its own rollback for
174
+ that. `refine rollback <backup-id>` is different and narrower: it only undoes a `refine promote`
175
+ (a base-skill update recorded by `skill-forge refine`), not a Git commit or anything else. See
176
+ [Git preflight](docs/commands/init.md#git-preflight-v016) and
177
+ [`refine`](docs/commands/refine.md) for both.
178
+
179
+ **Where's the full documentation?**
180
+ See the [docs index](docs/README.md) for every command reference and background doc.
181
+
1050
182
  ## License
1051
183
 
1052
184
  skill-forge is open-core with a split license (as of v0.2.0):
@@ -1056,7 +188,8 @@ skill-forge is open-core with a split license (as of v0.2.0):
1056
188
  - **Pro modules — Rhize Commercial License.** `src/license.ts`, `src/gate/overlap.ts`,
1057
189
  `src/provenance.ts`, `src/queue.ts` (overlap analysis, provenance ledger, pending
1058
190
  queue / `--ingest` handoff), and `src/refine/` (v0.10 — capture/apply/generalize skill
1059
- overrides). The source is available to read and audit, but production use of Pro
191
+ overrides), plus `src/insights/` (source studies, evidence validation, inventory mapping,
192
+ plans, and Jira manifests). The source is available to read and audit, but production use of Pro
1060
193
  functionality requires a license key — see [LICENSE-COMMERCIAL](LICENSE-COMMERCIAL).
1061
194
 
1062
195
  [LICENSE](LICENSE) is the authoritative map of which files fall under which license.