@rhize/skill-forge 0.9.0 → 0.11.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/LICENSE CHANGED
@@ -22,6 +22,11 @@ Pro Modules
22
22
  - src/organize.ts
23
23
  - src/commands/watch.ts
24
24
  - src/commands/ingest.ts
25
+ - src/commands/routine.ts
26
+ - src/refine/models.ts
27
+ - src/refine/store.ts
28
+ - src/refine/patch.ts
29
+ - src/refine/context.ts
25
30
 
26
31
  ...and the portions of built artifacts (e.g. dist/cli.js in the published npm
27
32
  package) generated from these files. Each Pro Module carries a header
package/README.md CHANGED
@@ -75,16 +75,28 @@ skill-forge find [query] [options] Discover skills via skills.sh and
75
75
  skill-forge watch [options] Drift check across every SOURCES.md provenance ledger (Pro)
76
76
  skill-forge ingest [options] Hand the pending-ingestion queue off to a coding agent for the decide/absorb pass (Pro)
77
77
  skill-forge queue close <id> --status <s> Close a queue entry after the decide pass (Pro)
78
+ skill-forge refine [options] Capture a project-scope override from real usage feedback (Pro)
79
+ skill-forge refine list [options] Show refinement history
80
+ skill-forge refine patterns [options] List tracked/ready/generalized/dismissed patterns
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)
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)
78
88
  skill-forge list List skills currently held in quarantine
79
89
  skill-forge status Show configuration and quarantine summary
90
+ skill-forge guide [topic] Orientation: what this does, your current state, the next command
80
91
  ```
81
92
 
82
93
  ### `init`
83
94
 
84
95
  ```bash
85
96
  skill-forge init # interactive: pick targets, default target, handoff agent
86
- 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)
87
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
88
100
  ```
89
101
 
90
102
  Probes the known agent matrix (`src/agents.ts`) for both project-relative (`.claude/skills`, ...)
@@ -95,6 +107,40 @@ overwrites the fields it's responsible for. If no `config.json` exists yet, `add
95
107
  `status` offer to run this for you on first use (skipped entirely for `--json`/`--yes`/non-TTY
96
108
  invocations, so scripted runs never block on a prompt).
97
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
+
98
144
  **Init now ends by offering the audit (v0.8).** After an interactive run writes its config, it
99
145
  asks "Run the skills & MCP audit now? [Y/n]" (default yes) and, if accepted, runs
100
146
  `skill-forge audit` interactively — see [`audit`](#audit-v08) below. `init --defaults` runs it too,
@@ -384,9 +430,8 @@ skill-forge queue close <id> --status ingested
384
430
  skill-forge queue close <id> --status dismissed
385
431
  ```
386
432
 
387
- Pro (free during the 0.x beta). The queue-drain UX that previously only existed as the
388
- `rhize-meta` plugin's `/rhize-meta:forge-ingest` slash command. `ingest` (no args) validates every
389
- 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
390
435
  canonicalize under the configured quarantine dir or a configured skills root/MCP target;
391
436
  mismatched or escaping entries are reported and excluded — then hands the surviving summary off to
392
437
  the configured coding agent (same handoff plumbing as `add --ingest`) with `assets/ingest-prompt.md`,
@@ -399,12 +444,247 @@ quarantine/gate pipeline first; `ingest` only ever drains what's already queued.
399
444
  `skill-forge queue close <id> --status ingested|dismissed` lets an agent close out a queue entry
400
445
  after its decide pass without hand-editing `queue.json` — writes are atomic (temp file + rename).
401
446
 
447
+ ### `refine` (v0.10, Pro)
448
+
449
+ ```bash
450
+ skill-forge refine --skill my-skill --category hook --override-type patch \
451
+ --action insert-after --marker "Only check paths" \
452
+ --content "..." --expected "..." --actual "..." --dry-run
453
+ skill-forge refine list [--status <s>] [--skill <s>] [--project <p>]
454
+ skill-forge refine patterns [--status tracking|ready|generalized|dismissed] [--skill <s>]
455
+ skill-forge refine promote <PATTERN-ID> [--dry-run] [--force] [--user-root <dir>]
456
+ skill-forge refine rollback <backup-id> [--force]
457
+ skill-forge refine which <skill> [--user-root <dir>]
458
+ ```
459
+
460
+ Pro (free during the 0.x beta). Captures, applies, and generalizes improvements to installed
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).
463
+
464
+ **Capture never mutates a base `SKILL.md`.** `refine` capture/apply writes ONLY project-scope
465
+ override artifacts — `SKILL.patch.md` / `SKILL.extend.md` / `skill-config.json`, or a whole-file
466
+ override copy for `full`/`hook`/`script` override types. The only command that ever touches a
467
+ user-scope base skill is `refine promote`, and only against a `ready` pattern (or with `--force`).
468
+
469
+ **Override precedence** (configurable roots):
470
+
471
+ ```
472
+ 1. PROJECT LOCAL <cwd>/.claude/skills/<skill>/ highest
473
+ 2. PROJECT SHARED <cwd>/skills/<skill>/
474
+ 3. USER SCOPE first configured skillsRoots entry outside cwd (fallback ~/.claude/skills)
475
+ ```
476
+
477
+ `refine which <skill>` prints this resolution order and which override files exist at each scope —
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.
483
+
484
+ **Non-interactive capture contract.** Capture flags: `--skill --category --target --override-type
485
+ <patch|extend|config|full|hook|script|new> --action <append|prepend|replace-section|insert-after|
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
491
+ are supported: `patch`/`extend`/`config` are rendered from the flags; `full`/`hook`/`script` are
492
+ verbatim override-file writes at project scope (content required); `new` creates an extension file
493
+ for a capability that doesn't exist in the base skill yet. Always run `--dry-run` first and confirm
494
+ the preview before writing for real — `--yes` skips re-prompting for a confirmation already given,
495
+ it does not replace the dry-run preview.
496
+
497
+ **The judgment step is the agent's job**, same pattern as `--ingest`/`audit --handoff`: a
498
+ bundled `assets/refine-prompt.md` carries the gap-analysis rubric (category/override-type decision
499
+ tables, guided-mode triggers), the patch-action syntax, the pattern-fingerprint/generalization
500
+ criteria, and the verification step. `refine --handoff`
501
+ (opt-in) launches your configured agent with it, same handoff plumbing as `--ingest`; `--yes` never
502
+ implies `--handoff`.
503
+
504
+ **Pattern tracking and promotion.** A pattern becomes `ready` only when it recurs in a **second,
505
+ genuinely different project** — repeat captures in the same project never flip it (occurrence
506
+ `count` is derived from unique project identities, not raw refinement counts; see
507
+ `docs/refinement-schema.md`). `refine patterns` lists tracked/ready/generalized/dismissed patterns,
508
+ filterable by `--status --skill --project`. `refine promote <PATTERN-ID>` merges a `ready` pattern
509
+ into the user-scope base skill: it backs up affected files first (manifest with per-file sha256 +
510
+ original bytes + mode, tombstones for files that didn't exist), stages the write, validates, then
511
+ renames into place — `--dry-run` previews the diff without writing, `refine rollback <backup-id>`
512
+ restores from the manifest (refusing if current files have drifted since promote, unless
513
+ `--force`). On promote, a `SOURCES.md` provenance entry is appended (verb `DEFER`, notes
514
+ `"generalized from PAT-xxxx via skill-forge refine"`).
515
+
516
+ **`evolve` vs. `refine`.** Both improve an already-installed skill, but at different scopes and
517
+ triggers: `evolve` (v0.7) is *automated, whole-skill* optimization — it hands the entire skill off
518
+ to SkillOpt-Sleep to propose a fresh replacement, re-gates the proposal, and lets you adopt or
519
+ reject it wholesale. `refine` is *targeted, human/agent-driven* — it captures one specific observed
520
+ gap ("expected X, got Y") and writes the smallest override that closes it, tracked and eventually
521
+ generalized only once the same gap recurs elsewhere. A `refine` patch on top of an `evolve`d skill
522
+ is fine; both record their own provenance entry, so the `SOURCES.md` ledger shows which change came
523
+ from which mechanism.
524
+
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
531
+ recreated — JSON is the store; `refine list`/`refine patterns` are the human-readable view over it.
532
+
533
+ **Auto-trigger hooks — templates, not automation.** A CLI cannot hook a running Claude Code
534
+ session, so two auto-trigger hooks ship here as documented templates instead:
535
+ `assets/hooks/refinement-detector.sh` (detects refinement-shaped language in a prompt) and
536
+ `assets/hooks/session-end.sh` (prompts after a substantial session). Both are optional, inert if
537
+ `skill-forge` isn't on `PATH`, and never call `skill-forge` themselves — they only print a
538
+ suggestion. Wire either one in by adding it to your `.claude/settings.json` (or
539
+ `~/.claude/settings.json`) hooks section — the exact snippet is in each script's own header
540
+ comment:
541
+
542
+ ```json
543
+ {
544
+ "hooks": {
545
+ "UserPromptSubmit": [
546
+ { "hooks": [{ "type": "command", "command": "bash /path/to/refinement-detector.sh" }] }
547
+ ],
548
+ "SessionEnd": [
549
+ { "hooks": [{ "type": "command", "command": "bash /path/to/session-end.sh" }] }
550
+ ]
551
+ }
552
+ }
553
+ ```
554
+
555
+ **Security invariants** (same regime as v0.8/v0.9): nothing from a skill being refined is ever
556
+ executed. Patch application writes ONLY under the resolved target scope dir for the named skill
557
+ (containment-checked realpath, refuses a symlinked destination file); promotion writes ONLY under
558
+ user scope, backup first. All child processes are argv arrays (git only, for context gathering).
559
+ Control-char sanitization on every untrusted string that lands in human-readable output. `--yes`
560
+ never implies `--handoff`. Applying a patch whose target `SKILL.md` is missing is refused; promoting
561
+ a non-`ready` pattern without `--force` is refused.
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
+ `promote` **re-gates** rather than promoting blind. The original run's gate result was never
615
+ persisted, so the alternative would be a queue entry with a fabricated gate record — and a hold
616
+ may be days old, with the ruleset and your installed set since changed. Re-scanning is static and
617
+ cheap. Ids come from `skill-forge list`; anything that resolves outside your quarantine dir
618
+ (traversal, absolute path, symlink) is refused.
619
+
620
+ ### `config` (v0.11)
621
+
622
+ `businessProfile` is five fixed fields, so anything else learned about you has nowhere to live.
623
+ Preferences are open-ended key/value facts that accumulate without a schema change:
624
+
625
+ ```bash
626
+ skill-forge config set prefers-typescript "strict mode, no any"
627
+ skill-forge config list
628
+ skill-forge config review
629
+ ```
630
+
631
+ **Agents get a different door.** `set` is for a human at a keyboard and takes effect immediately;
632
+ `propose` only ever queues something for review:
633
+
634
+ ```bash
635
+ skill-forge config propose deploys-on vercel --origin "ingest agent" --note "seen in 3 skills"
636
+ ```
637
+
638
+ An agent draining the queue reads untrusted skill content, so anything it concludes about you is
639
+ downstream of text an attacker may have written. If agents could write preferences directly, a
640
+ malicious `SKILL.md` could talk one into persisting an instruction into your config, where every
641
+ later run would read it back as *your stated preference* — a durable prompt-injection foothold
642
+ with a laundering step in the middle. Routing agent writes through review means the worst case is
643
+ a proposal you decline. `review` is interactive, or takes an explicit `--accept-all`/`--reject-all`;
644
+ it refuses to fall through to a default in a non-interactive shell.
645
+
646
+ Both doors run the same validation: kebab-case keys, a length cap, no control characters, and a
647
+ refusal for anything credential-shaped — by key name (`api-key`, `*-token`) or by value shape
648
+ (`sk-…`, `ghp_…`, `AKIA…`, PEM blocks, JWTs). `config.json` is plain text; secrets belong in your
649
+ keychain. Hand-edited entries that fail those checks are dropped on load rather than rendered.
650
+
402
651
  ### `list` / `status`
403
652
 
404
653
  `list` shows what's currently held in quarantine (installed via `add`, answered "hold", not yet
405
654
  promoted or rejected). `status` shows the resolved configuration (skills roots, quarantine dir,
406
655
  strictness) plus a count of held entries.
407
656
 
657
+ ### `guide` (v0.11)
658
+
659
+ ```bash
660
+ skill-forge guide # what this does, where you are, what to run next
661
+ skill-forge guide verbs # a single topic
662
+ ```
663
+
664
+ Orientation for someone — or some agent — dropping into an existing session. `--help` lists flags
665
+ but can't tell you what to do with them, and `status` prints configuration without saying what it
666
+ means; `guide` answers "what is my current state, and what is the next command". It reads your
667
+ config, quarantine, and queue and names one next step, unfinished work first: nothing configured →
668
+ `init`; entries awaiting a decide-pass verb → `ingest`; candidates still held → `list`; no audit
669
+ ever run → `audit`.
670
+
671
+ ```
672
+ Where you are:
673
+ config: /Users/you/.skill-forge/config.json
674
+ gate targets: 3 (default: /Users/you/.agents/skills)
675
+ quarantined: 2
676
+ queue: 12 pending
677
+
678
+ Suggested next step:
679
+ skill-forge ingest
680
+ 12 promoted items still awaiting a decide-pass verb
681
+ ```
682
+
683
+ Topics: `pipeline`, `verbs`, `queue`, `mcp`, `refine`, `maintenance`, `pro`. **Read-only and
684
+ non-interactive** — it writes nothing and never prompts, so it's safe to run inside an agent turn,
685
+ and with no config it says so rather than reciting default placeholder paths as if they were
686
+ settings.
687
+
408
688
  ## MCP gating (v0.5)
409
689
 
410
690
  `add` and `scan` can also gate an **MCP server** instead of a skill — the same quarantine →
@@ -519,6 +799,7 @@ JSON. Detection/overlap is agent-format-agnostic; writing is JSON-only.
519
799
  | `find` — skills.sh discovery + partner audit verdicts (v0.9) | ✓ | ✓ |
520
800
  | `watch` — provenance drift check across every `SOURCES.md` ledger (v0.9) | | ✓ |
521
801
  | `ingest` + `queue close` — pending-queue drain handoff (v0.9) | | ✓ |
802
+ | `refine` — capture/apply/generalize project-scope skill overrides (v0.10) | | ✓ |
522
803
 
523
804
  Free is the complete safety gate on its own — quarantine, profile, safety scan, and an explicit
524
805
  promote/reject decision, with nothing held back. Pro is the curation layer on top: whether a new
@@ -570,11 +851,9 @@ implementation status.
570
851
  that deeper judgment (which patterns to keep, whether to absorb into an existing skill vs. fork a
571
852
  new one, verifying the result beats baseline) is a job for a coding agent, not the gate. `--ingest`
572
853
  hands a promoted skill off to one, running the bundled, agent-neutral prompt at
573
- `assets/ingest-prompt.md` — works with any agent. (As of v0.9, this fully replaces the `rhize-meta`
574
- plugin's `rhize-skill-forge` skill and its `/rhize-meta:forge-ingest`/`forge-scan`/`forge-watch`/
575
- `skill-find`/`skill-doctor` commands, which are removed; skill vetting/governance now lives
576
- entirely in this npm package. The plugin's remaining `skill-refinement` skill can still receive
577
- ABSORB extractions from an ingest pass — see [docs/forge-workflow.md](docs/forge-workflow.md).)
854
+ `assets/ingest-prompt.md` — works with any agent. ABSORB extractions from an ingest pass route
855
+ through `skill-forge refine` in this same package — see [`refine`](#refine-v010-pro) above and
856
+ [docs/forge-workflow.md](docs/forge-workflow.md).
578
857
  The same flag works on an MCP server promote (`--artifact mcp --ingest`, v0.6): the bundled prompt branches on the
579
858
  queue entry's `artifactType` and runs the matching decide pass — see
580
859
  [MCP gating](#mcp-gating-v05) above.
@@ -655,9 +934,9 @@ skill-forge is open-core with a split license (as of v0.2.0):
655
934
  safety gate, report, promote/hold/reject. See [LICENSE-MIT](LICENSE-MIT).
656
935
  - **Pro modules — Rhize Commercial License.** `src/license.ts`, `src/gate/overlap.ts`,
657
936
  `src/provenance.ts`, `src/queue.ts` (overlap analysis, provenance ledger, pending
658
- queue / `--ingest` handoff). The source is available to read and audit, but production
659
- use of Pro functionality requires a license key — see
660
- [LICENSE-COMMERCIAL](LICENSE-COMMERCIAL).
937
+ queue / `--ingest` handoff), and `src/refine/` (v0.10 — capture/apply/generalize skill
938
+ overrides). The source is available to read and audit, but production use of Pro
939
+ functionality requires a license key — see [LICENSE-COMMERCIAL](LICENSE-COMMERCIAL).
661
940
 
662
941
  [LICENSE](LICENSE) is the authoritative map of which files fall under which license.
663
942
  Versions up to and including 0.1.0 were published entirely under MIT.