@rhize/skill-forge 0.10.0 → 0.11.1

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/LICENSE CHANGED
@@ -22,6 +22,7 @@ Pro Modules
22
22
  - src/organize.ts
23
23
  - src/commands/watch.ts
24
24
  - src/commands/ingest.ts
25
+ - src/commands/routine.ts
25
26
  - src/refine/models.ts
26
27
  - src/refine/store.ts
27
28
  - src/refine/patch.ts
package/README.md CHANGED
@@ -79,17 +79,24 @@ skill-forge refine [options] Capture a project-scope override
79
79
  skill-forge refine list [options] Show refinement history
80
80
  skill-forge refine patterns [options] List tracked/ready/generalized/dismissed patterns
81
81
  skill-forge refine promote <PATTERN-ID> Merge a ready pattern into the user-scope skill (Pro)
82
+ skill-forge refine rollback <backup-id> Restore a promotion backup (Pro)
82
83
  skill-forge refine which <skill> Print override-resolution order for a skill
84
+ skill-forge routine [options] One scheduled maintenance pass: audit + drift + registry, cron-friendly (Pro)
85
+ skill-forge promote <id> [options] Re-gate a held quarantine entry and promote it
86
+ skill-forge reject <id> [options] Discard a held quarantine entry
87
+ skill-forge config <sub> [args] Read/write open-ended preferences (list|get|set|unset|propose|review)
83
88
  skill-forge list List skills currently held in quarantine
84
89
  skill-forge status Show configuration and quarantine summary
90
+ skill-forge guide [topic] Orientation: what this does, your current state, the next command
85
91
  ```
86
92
 
87
93
  ### `init`
88
94
 
89
95
  ```bash
90
96
  skill-forge init # interactive: pick targets, default target, handoff agent
91
- skill-forge init --defaults # non-interactive: all detected agents, first as default (CI)
97
+ skill-forge init --defaults # non-interactive: agents found on PATH, first as default (CI)
92
98
  skill-forge init --list # print detected agent skill roots and exit — no writes
99
+ skill-forge init --all-agents # don't narrow to agents whose CLI is on PATH — keep every root
93
100
  ```
94
101
 
95
102
  Probes the known agent matrix (`src/agents.ts`) for both project-relative (`.claude/skills`, ...)
@@ -100,6 +107,40 @@ overwrites the fields it's responsible for. If no `config.json` exists yet, `add
100
107
  `status` offer to run this for you on first use (skipped entirely for `--json`/`--yes`/non-TTY
101
108
  invocations, so scripted runs never block on a prompt).
102
109
 
110
+ **Menus are keyed on the PATH, not the agent (v0.11).** Many agents share one skills directory —
111
+ 18 entries in the agent matrix use `.agents/skills` as their project root — so the gate-target and
112
+ default-target menus list each *distinct* root once, labelled with the agents that resolve to it
113
+ (`19 agents (project): Amp, Replit, Universal, +16 more`), and `skillsRoots`/`mcpTargets` are
114
+ written deduped. `config.agents` still records every agent, since it's an id→root map. Detection
115
+ output says so explicitly when roots collapse (`→ 21 agents share 3 distinct skills root(s)`).
116
+ Configs written by earlier versions are deduped on load, so no re-run is required to clean one up.
117
+
118
+ **Narrowed to agents you actually have (v0.11).** Because a shared directory can't tell you which
119
+ of its 18 agents you use, init also checks PATH: a root is pre-selected when at least one agent
120
+ mapped to it has its CLI installed, and roots with none are still listed, just switched off with
121
+ the reason shown.
122
+
123
+ ```
124
+ [x] 1. /Users/you/.agents/skills
125
+ on PATH: Codex, Gemini CLI (project) (+17 other agents share this path)
126
+ [ ] 3. /Users/you/.openclaw/skills
127
+ OpenClaw (global) — no verified CLI name to check
128
+ ```
129
+
130
+ The PATH check is a **look-up only — nothing found is ever executed** (no `--version` probe), and
131
+ binary names are held to the same evidence bar as the skill paths: an agent with no verified
132
+ command name reports "unknown", never "not installed". `--all-agents` turns narrowing off, and it
133
+ disables itself automatically if no agent CLI is found at all, so it can never reduce a working
134
+ detection to an empty config. The handoff menu is only *reordered* by it — installed CLIs float to
135
+ the top and everything detected stays pickable.
136
+
137
+ **Selecting things (v0.11).** The multi-selects take a whole answer at once — `2`, `1 3`, `2, 4`,
138
+ `2-5`, plus `all` and `none` — and Enter confirms. Anything unrecognized is named back to you and
139
+ ignored, never silently swallowed. The single-choice prompts (default target, handoff agent)
140
+ **re-ask** on an answer that isn't one listed number instead of falling back to option 1, so a
141
+ multi-value or mistyped answer can no longer write a target you didn't choose. Ctrl+D at any
142
+ prompt exits cleanly.
143
+
103
144
  **Init now ends by offering the audit (v0.8).** After an interactive run writes its config, it
104
145
  asks "Run the skills & MCP audit now? [Y/n]" (default yes) and, if accepted, runs
105
146
  `skill-forge audit` interactively — see [`audit`](#audit-v08) below. `init --defaults` runs it too,
@@ -389,9 +430,8 @@ skill-forge queue close <id> --status ingested
389
430
  skill-forge queue close <id> --status dismissed
390
431
  ```
391
432
 
392
- Pro (free during the 0.x beta). The queue-drain UX that previously only existed as the
393
- `rhize-meta` plugin's `/rhize-meta:forge-ingest` slash command. `ingest` (no args) validates every
394
- pending `~/.skill-forge/queue.json` entry — each entry's `quarantinePath`/`installedPath` must
433
+ Pro (free during the 0.x beta). The queue-drain UX for pending ingestions. `ingest` (no args)
434
+ validates every pending `~/.skill-forge/queue.json` entry — each entry's `quarantinePath`/`installedPath` must
395
435
  canonicalize under the configured quarantine dir or a configured skills root/MCP target;
396
436
  mismatched or escaping entries are reported and excluded — then hands the surviving summary off to
397
437
  the configured coding agent (same handoff plumbing as `add --ingest`) with `assets/ingest-prompt.md`,
@@ -410,26 +450,23 @@ after its decide pass without hand-editing `queue.json` — writes are atomic (t
410
450
  skill-forge refine --skill my-skill --category hook --override-type patch \
411
451
  --action insert-after --marker "Only check paths" \
412
452
  --content "..." --expected "..." --actual "..." --dry-run
413
- skill-forge refine list [--skill <s>] [--project <p>]
453
+ skill-forge refine list [--status <s>] [--skill <s>] [--project <p>]
414
454
  skill-forge refine patterns [--status tracking|ready|generalized|dismissed] [--skill <s>]
415
- skill-forge refine promote <PATTERN-ID> [--dry-run] [--force]
455
+ skill-forge refine promote <PATTERN-ID> [--dry-run] [--force] [--user-root <dir>]
416
456
  skill-forge refine rollback <backup-id> [--force]
417
- skill-forge refine which <skill>
457
+ skill-forge refine which <skill> [--user-root <dir>]
418
458
  ```
419
459
 
420
460
  Pro (free during the 0.x beta). Captures, applies, and generalizes improvements to installed
421
- skills from real usage feedback — the npm-package absorption of what was previously the
422
- `rhize-plugins` repo's `rhize-meta` plugin `skill-refinement` skill (`/refine-skills`,
423
- `/review-patterns`, `/apply-generalization`); that plugin skill no longer exists (see
424
- [CLAUDE.md's "refine (v0.10)" section](CLAUDE.md) for the full merge notes, or
425
- `docs/refinement-schema.md` for the store shape).
461
+ skills from real usage feedback (see [CLAUDE.md's "refine (v0.10)" section](CLAUDE.md) for the
462
+ full architecture notes, or `docs/refinement-schema.md` for the store shape).
426
463
 
427
464
  **Capture never mutates a base `SKILL.md`.** `refine` capture/apply writes ONLY project-scope
428
465
  override artifacts — `SKILL.patch.md` / `SKILL.extend.md` / `skill-config.json`, or a whole-file
429
466
  override copy for `full`/`hook`/`script` override types. The only command that ever touches a
430
467
  user-scope base skill is `refine promote`, and only against a `ready` pattern (or with `--force`).
431
468
 
432
- **Override precedence** (unchanged semantics from the plugin, configurable roots):
469
+ **Override precedence** (configurable roots):
433
470
 
434
471
  ```
435
472
  1. PROJECT LOCAL <cwd>/.claude/skills/<skill>/ highest
@@ -438,22 +475,29 @@ user-scope base skill is `refine promote`, and only against a `ready` pattern (o
438
475
  ```
439
476
 
440
477
  `refine which <skill>` prints this resolution order and which override files exist at each scope —
441
- the "why didn't my patch take effect" debugging aid, made explicit and read-only.
478
+ the "why didn't my patch take effect" debugging aid, made explicit and read-only. Both `refine
479
+ which` and `refine promote` accept `--user-root <dir>` to override the detected user-scope skills
480
+ root (default: the first configured `skillsRoots` entry outside the current working directory,
481
+ falling back to `~/.claude/skills`) — useful when the config's default root doesn't match the
482
+ skill you're targeting.
442
483
 
443
484
  **Non-interactive capture contract.** Capture flags: `--skill --category --target --override-type
444
485
  <patch|extend|config|full|hook|script|new> --action <append|prepend|replace-section|insert-after|
445
- insert-before|delete-section> --marker --content(-file) --expected --actual --example --outcome
446
- --root-cause --pattern-id --scope <local|shared> --dry-run --json --yes`. All seven override types
486
+ insert-before|delete-section> --marker --content <text> --content-file <file> --expected --actual
487
+ --example --outcome --root-cause --pattern-id --scope <local|shared> --dry-run --json --yes
488
+ --handoff`. `--content` takes override content inline; `--content-file <file>` reads it from a
489
+ file instead (mutually exclusive with `--content`) — use it for anything multi-line rather than
490
+ fighting shell quoting. All seven override types
447
491
  are supported: `patch`/`extend`/`config` are rendered from the flags; `full`/`hook`/`script` are
448
492
  verbatim override-file writes at project scope (content required); `new` creates an extension file
449
493
  for a capability that doesn't exist in the base skill yet. Always run `--dry-run` first and confirm
450
494
  the preview before writing for real — `--yes` skips re-prompting for a confirmation already given,
451
495
  it does not replace the dry-run preview.
452
496
 
453
- **The judgment step is the agent's job**, same pattern as `--ingest`/`audit --handoff`: a new
497
+ **The judgment step is the agent's job**, same pattern as `--ingest`/`audit --handoff`: a
454
498
  bundled `assets/refine-prompt.md` carries the gap-analysis rubric (category/override-type decision
455
- tables, guided-mode triggers ported from the plugin's `analyze_gap.py`), the patch-action syntax,
456
- the pattern-fingerprint/generalization criteria, and the verification step. `refine --handoff`
499
+ tables, guided-mode triggers), the patch-action syntax, the pattern-fingerprint/generalization
500
+ criteria, and the verification step. `refine --handoff`
457
501
  (opt-in) launches your configured agent with it, same handoff plumbing as `--ingest`; `--yes` never
458
502
  implies `--handoff`.
459
503
 
@@ -478,16 +522,16 @@ generalized only once the same gap recurs elsewhere. A `refine` patch on top of
478
522
  is fine; both record their own provenance entry, so the `SOURCES.md` ledger shows which change came
479
523
  from which mechanism.
480
524
 
481
- **Legacy store — deliberate skip, not a migration.** The plugin's `~/.claude/skill-refinements/`
482
- held three flat markdown notes (no structured ledgers, no schema) — there is no migration from it.
483
- Files there stay readable in place; if anything in them still matters, re-capture it via
484
- `skill-forge refine` against the greenfield JSON store described in
485
- [`docs/refinement-schema.md`](docs/refinement-schema.md). The plugin's markdown ledgers
486
- (`refinement-history/*.md`, `aggregated-patterns.md`, `generalization-queue.md`) are not
525
+ **Legacy store — deliberate skip, not a migration.** `~/.claude/skill-refinements/` is a legacy
526
+ location some users may have from earlier tooling — three flat markdown notes, no structured
527
+ ledgers, no schema — and there is no migration from it. Files there stay readable in place; if
528
+ anything in them still matters, re-capture it via `skill-forge refine` against the greenfield JSON
529
+ store described in [`docs/refinement-schema.md`](docs/refinement-schema.md). Markdown ledgers like
530
+ `refinement-history/*.md`, `aggregated-patterns.md`, or `generalization-queue.md` are not
487
531
  recreated — JSON is the store; `refine list`/`refine patterns` are the human-readable view over it.
488
532
 
489
533
  **Auto-trigger hooks — templates, not automation.** A CLI cannot hook a running Claude Code
490
- session. The plugin's two auto-trigger hooks ship here as documented templates instead:
534
+ session, so two auto-trigger hooks ship here as documented templates instead:
491
535
  `assets/hooks/refinement-detector.sh` (detects refinement-shaped language in a prompt) and
492
536
  `assets/hooks/session-end.sh` (prompts after a substantial session). Both are optional, inert if
493
537
  `skill-forge` isn't on `PATH`, and never call `skill-forge` themselves — they only print a
@@ -516,12 +560,137 @@ Control-char sanitization on every untrusted string that lands in human-readable
516
560
  never implies `--handoff`. Applying a patch whose target `SKILL.md` is missing is refused; promoting
517
561
  a non-`ready` pattern without `--force` is refused.
518
562
 
563
+ ### `routine` (v0.11, Pro)
564
+
565
+ ```bash
566
+ skill-forge routine # audit + drift + registry, one report, writes nothing else
567
+ skill-forge routine --json --offline # for a scheduler: one JSON document, no network
568
+ skill-forge routine --fail-on high # exit 1 when a high/critical finding exists
569
+ skill-forge routine --housekeeping # opt in to bounded writes (see below)
570
+ ```
571
+
572
+ The whole maintenance pipeline in one command, shaped for cron. Every other command answers one
573
+ question; keeping a set healthy means running four and correlating them by hand, which is exactly
574
+ what nobody does on a schedule. `routine` runs the audit, drift, and registry **engines**,
575
+ correlates them into one report and one summary, and exits with a code a scheduler can alert on.
576
+
577
+ **Non-interactive by construction** — it never prompts, so it cannot hang a scheduled job. It is
578
+ also the only schedulable command that writes, so what it may write is fenced:
579
+
580
+ | | writes |
581
+ |---|---|
582
+ | always | the combined report + `audit-state.json`, both under `~/.skill-forge` |
583
+ | `--housekeeping` | prunes quarantine sandboxes older than `--prune-days` (default 30); closes queue entries whose skill is gone from disk |
584
+ | `--handoff` | launches your configured agent with the curation prompt |
585
+
586
+ Housekeeping and handoff are **opt-in**: a bare `skill-forge routine` in a crontab reports and
587
+ nothing else. A step that was requested but could not run — a crashed drift check, a failed
588
+ prune, an unreadable queue — is listed under **Incomplete steps** and **always exits nonzero**,
589
+ independent of `--fail-on`: `--fail-on` grades what the audit *found*, whereas a degraded run
590
+ 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
591
+ age-bounded, `--dry-run`-able, and lists every path in the report. A queue entry is only closed
592
+ when the skill it points at no longer exists; a pending entry whose skill is still installed is
593
+ undone work, not an orphan. `routine` never installs, promotes, or rejects: no candidate enters a
594
+ skills root without a human or agent decision, and a cron job is not that.
595
+
596
+ ```
597
+ 0 9 * * 1 skill-forge routine --offline --fail-on high --housekeeping
598
+ ```
599
+
600
+ ### `promote <id>` / `reject <id>` (v0.11)
601
+
602
+ Answering **hold** at the gate used to be a dead end: the sandbox sat in quarantine and no command
603
+ could ever finish the decision. These close that loop.
604
+
605
+ ```bash
606
+ skill-forge promote <id> # re-gate the held sandbox, then promote/hold/reject
607
+ skill-forge reject <id> # discard it
608
+ ```
609
+
610
+ Works for both artifact types: a held MCP candidate is re-gated as an MCP server and written into
611
+ your MCP config (`--mcp-target`), with env values emptied and unpinned `npx` specs pinned, exactly
612
+ as `add --artifact mcp` would.
613
+
614
+ Promotion also re-validates the candidate against the state it was gated in: each skill dir must
615
+ still be a real directory rather than a symlink, no symlink inside it may resolve out of the
616
+ sandbox, and its content digest must still match the one captured at gate time. Otherwise the gap
617
+ between "scanned" and "installed" — days, for a hold — is a window in which vetted content can be
618
+ swapped for something that never passed the gate.
619
+
620
+ `promote` **re-gates** rather than promoting blind. The original run's gate result was never
621
+ persisted, so the alternative would be a queue entry with a fabricated gate record — and a hold
622
+ may be days old, with the ruleset and your installed set since changed. Re-scanning is static and
623
+ cheap. Ids come from `skill-forge list`; anything that resolves outside your quarantine dir
624
+ (traversal, absolute path, symlink) is refused.
625
+
626
+ ### `config` (v0.11)
627
+
628
+ `businessProfile` is five fixed fields, so anything else learned about you has nowhere to live.
629
+ Preferences are open-ended key/value facts that accumulate without a schema change:
630
+
631
+ ```bash
632
+ skill-forge config set prefers-typescript "strict mode, no any"
633
+ skill-forge config list
634
+ skill-forge config review
635
+ ```
636
+
637
+ **Agents get a different door.** `set` is for a human at a keyboard and takes effect immediately;
638
+ `propose` only ever queues something for review:
639
+
640
+ ```bash
641
+ skill-forge config propose deploys-on vercel --origin "ingest agent" --note "seen in 3 skills"
642
+ ```
643
+
644
+ An agent draining the queue reads untrusted skill content, so anything it concludes about you is
645
+ downstream of text an attacker may have written. If agents could write preferences directly, a
646
+ malicious `SKILL.md` could talk one into persisting an instruction into your config, where every
647
+ later run would read it back as *your stated preference* — a durable prompt-injection foothold
648
+ with a laundering step in the middle. Routing agent writes through review means the worst case is
649
+ a proposal you decline. `review` is interactive, or takes an explicit `--accept-all`/`--reject-all`;
650
+ it refuses to fall through to a default in a non-interactive shell.
651
+
652
+ Both doors run the same validation: kebab-case keys, a length cap, no control characters, and a
653
+ refusal for anything credential-shaped — by key name (`api-key`, `*-token`) or by value shape
654
+ (`sk-…`, `ghp_…`, `AKIA…`, PEM blocks, JWTs). `config.json` is plain text; secrets belong in your
655
+ keychain. Hand-edited entries that fail those checks are dropped on load rather than rendered.
656
+
519
657
  ### `list` / `status`
520
658
 
521
659
  `list` shows what's currently held in quarantine (installed via `add`, answered "hold", not yet
522
660
  promoted or rejected). `status` shows the resolved configuration (skills roots, quarantine dir,
523
661
  strictness) plus a count of held entries.
524
662
 
663
+ ### `guide` (v0.11)
664
+
665
+ ```bash
666
+ skill-forge guide # what this does, where you are, what to run next
667
+ skill-forge guide verbs # a single topic
668
+ ```
669
+
670
+ Orientation for someone — or some agent — dropping into an existing session. `--help` lists flags
671
+ but can't tell you what to do with them, and `status` prints configuration without saying what it
672
+ means; `guide` answers "what is my current state, and what is the next command". It reads your
673
+ config, quarantine, and queue and names one next step, unfinished work first: nothing configured →
674
+ `init`; entries awaiting a decide-pass verb → `ingest`; candidates still held → `list`; no audit
675
+ ever run → `audit`.
676
+
677
+ ```
678
+ Where you are:
679
+ config: /Users/you/.skill-forge/config.json
680
+ gate targets: 3 (default: /Users/you/.agents/skills)
681
+ quarantined: 2
682
+ queue: 12 pending
683
+
684
+ Suggested next step:
685
+ skill-forge ingest
686
+ 12 promoted items still awaiting a decide-pass verb
687
+ ```
688
+
689
+ Topics: `pipeline`, `verbs`, `queue`, `mcp`, `refine`, `maintenance`, `pro`. **Read-only and
690
+ non-interactive** — it writes nothing and never prompts, so it's safe to run inside an agent turn,
691
+ and with no config it says so rather than reciting default placeholder paths as if they were
692
+ settings.
693
+
525
694
  ## MCP gating (v0.5)
526
695
 
527
696
  `add` and `scan` can also gate an **MCP server** instead of a skill — the same quarantine →
@@ -688,13 +857,9 @@ implementation status.
688
857
  that deeper judgment (which patterns to keep, whether to absorb into an existing skill vs. fork a
689
858
  new one, verifying the result beats baseline) is a job for a coding agent, not the gate. `--ingest`
690
859
  hands a promoted skill off to one, running the bundled, agent-neutral prompt at
691
- `assets/ingest-prompt.md` — works with any agent. (As of v0.9, this fully replaces the `rhize-meta`
692
- plugin's `rhize-skill-forge` skill and its `/rhize-meta:forge-ingest`/`forge-scan`/`forge-watch`/
693
- `skill-find`/`skill-doctor` commands, which are removed; skill vetting/governance now lives
694
- entirely in this npm package. As of v0.10, the `rhize-meta` plugin's remaining `skill-refinement`
695
- skill is ALSO removed — ABSORB extractions from an ingest pass now route through
696
- `skill-forge refine` in this same package instead — see [`refine`](#refine-v010-pro) above and
697
- [docs/forge-workflow.md](docs/forge-workflow.md).)
860
+ `assets/ingest-prompt.md` — works with any agent. ABSORB extractions from an ingest pass route
861
+ through `skill-forge refine` in this same package — see [`refine`](#refine-v010-pro) above and
862
+ [docs/forge-workflow.md](docs/forge-workflow.md).
698
863
  The same flag works on an MCP server promote (`--artifact mcp --ingest`, v0.6): the bundled prompt branches on the
699
864
  queue entry's `artifactType` and runs the matching decide pass — see
700
865
  [MCP gating](#mcp-gating-v05) above.