@ctxr/skill-llm-wiki 1.0.1 → 1.0.2

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/SKILL.md CHANGED
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: skill-llm-wiki
3
3
  description: Use when the user explicitly asks to build, extend, validate, repair, rebuild, or merge an LLM-optimized knowledge wiki from markdown notes, documentation, source code, or mixed folders. Default output is a single stable sibling `<source>.wiki/` with full history in a private git repo under `.llmwiki/git/`; `--layout-mode in-place` transforms the source folder itself, and `--layout-mode hosted --target <path>` honours a user-provided `.llmwiki.layout.yaml` contract. SKILL.md is the entry point; detailed operation instructions are loaded on demand from `guide/` per the routing procedure below.
4
+ format_version: 1
4
5
  ---
5
6
 
6
7
  # skill-llm-wiki
@@ -41,6 +42,12 @@ Every operation is a git sequence: `preflight → pre-op snapshot → phase comm
41
42
 
42
43
  **Ambiguous invocations refuse and prompt.** If the user's request could mean two things (a default sibling would stomp on a foreign directory, a hosted target has no contract, `--layout-mode in-place` is combined with `--target`, …), the CLI exits with code 2 and a structured `INT-NN` error rather than guessing. See `guide/ux/user-intent.md` for the full list.
43
44
 
45
+ ## Integrating another skill or agent as a consumer
46
+
47
+ If the user is building a skill, agent, or CI job that calls this skill programmatically (rather than asking you to build a wiki for them in this session), route them to `guide/consumers/index.md`. That subtree answers: how to gate on `format_version`, how to `init` a topic wiki in one command, how to `heal` after every leaf write, how to detect the skill-absent case, how to write consumer tests against the shipped `scripts/testkit/` helpers. Every consumer recipe is dispatched from `guide/consumers/index.md`; do not re-derive the integration path from SKILL.md alone.
48
+
49
+ Quick probe summary: `skill-llm-wiki contract --json` returns `format_version` + the full CLI + frontmatter schema, and `skill-llm-wiki where --json` returns absolute install paths. Both are exempt from the runtime-dep preflight so consumers can probe before the skill is fully set up.
50
+
44
51
  ## Non-automation contract
45
52
 
46
53
  This skill has **no hooks, no filesystem watchers, no PostToolUse listeners, no install-time wiring, no background processes**. Every action happens only in direct response to an explicit user request against an explicit target directory. If the user did not ask, do nothing. Do not propose automation. Do not create hooks. Do not schedule anything.
package/guide/cli.md CHANGED
@@ -12,7 +12,7 @@ covers:
12
12
  - "remote mirroring: remote add/list/remove, sync with tag-only default refspec"
13
13
  - "layout mode flags (--layout-mode sibling|in-place|hosted, --target)"
14
14
  - "tiered-AI flags (--quality-mode tiered-fast|claude-first|tier0-only)"
15
- - "UX flags (--no-prompt, --json-errors, --accept-dirty, --accept-foreign-target, --review)"
15
+ - "UX flags (--no-prompt, --json, --json-errors (legacy alias), --accept-dirty, --accept-foreign-target, --review)"
16
16
  - "internal helpers: ingest, draft-leaf, draft-category, index-rebuild, index-rebuild-one, shape-check"
17
17
  - "exit code summary (0 ok, 1 usage, 2 validation/ambiguity/review-abort, 3 resolve miss, 4 node too old, 5 git missing/too old, 6 wiki corrupt, 7 NEEDS_TIER2 suspend-and-resume, 8 DEPS_MISSING runtime dependency missing)"
18
18
  tags:
@@ -235,7 +235,8 @@ All top-level operations accept:
235
235
  ## UX flags
236
236
 
237
237
  - `--no-prompt` / env `LLM_WIKI_NO_PROMPT=1` — fail loudly on any ambiguity instead of prompting; emits `INT-12` if the skill would otherwise ask a TTY question.
238
- - `--json-errors` — emit `INT-NN` ambiguity errors as JSON on stderr instead of numbered-options text.
238
+ - `--json` — canonical machine-output flag. Enables the `skill-llm-wiki/v1` envelope on subcommands that emit one (validate, init, heal, rollback) and switches `INT-NN` ambiguity errors to JSON on stderr. The consumer-facing probe subcommands `contract` and `where` always emit JSON when this flag is present.
239
+ - `--json-errors` — legacy alias for `--json`, kept for consumers that adopted the flag before the envelope shipped. Triggers identical behaviour. New code should pass `--json`.
239
240
  - `--accept-dirty` — operate on a source inside a dirty user git repo (escape hatch for `INT-08`).
240
241
  - `--review` — enable the `rebuild` interactive review cycle. See `guide/operations/rebuild.md`.
241
242
 
@@ -0,0 +1,106 @@
1
+ ---
2
+ id: consumers
3
+ type: index
4
+ depth_role: subcategory
5
+ depth: 1
6
+ focus: "Integrating another skill or agent as a consumer of skill-llm-wiki"
7
+ parents:
8
+ - ../index.md
9
+ tags:
10
+ - consumers
11
+ - integration
12
+ - agent
13
+ - skill
14
+ - recipes
15
+ activation:
16
+ keyword_matches:
17
+ - consumer
18
+ - consume
19
+ - integrate
20
+ - integration
21
+ - my skill uses
22
+ - my agent uses
23
+ - wrap
24
+ - depend on
25
+ - hard dependency
26
+ tag_matches:
27
+ - consumers
28
+ - integration
29
+ escalation_from:
30
+ - build
31
+ - init
32
+ - heal
33
+ - contract
34
+ - where
35
+ orientation: |
36
+ Route here when a user is building another skill, agent, or CI job
37
+ that calls skill-llm-wiki programmatically. The recipes under
38
+ recipes/ cover the canonical patterns: seed a dated or subject
39
+ wiki, run heal after every leaf write, gate CI on validate, handle
40
+ the skill-absent case, and wire up a consumer test suite. Each
41
+ recipe names the exact commands and the envelope fields consumers
42
+ read.
43
+
44
+ Not for: end users who are building a wiki for themselves (see
45
+ SKILL.md + guide/operations/ instead).
46
+
47
+ entries:
48
+ - id: consumers-quickstart
49
+ file: quickstart.md
50
+ type: primary
51
+ focus: "Ten-minute integration path: detect the skill, init a topic, heal after writes"
52
+ tags: [quickstart, integration]
53
+ - id: recipe-dated-wiki
54
+ file: recipes/dated-wiki.md
55
+ type: primary
56
+ focus: "Init a dated topic (reports, sessions, regressions) with one command"
57
+ tags: [dated, init, hosted]
58
+ - id: recipe-subject-wiki
59
+ file: recipes/subject-wiki.md
60
+ type: primary
61
+ focus: "Init a subject topic (runbooks, adrs) with nested categories"
62
+ tags: [subject, init, hosted]
63
+ - id: recipe-post-write-heal
64
+ file: recipes/post-write-heal.md
65
+ type: primary
66
+ focus: "The canonical after-every-leaf-write heal invocation"
67
+ tags: [heal, envelope, post-write]
68
+ - id: recipe-ci-gate
69
+ file: recipes/ci-gate.md
70
+ type: primary
71
+ focus: "Gate CI on skill-llm-wiki validate --json"
72
+ tags: [ci, validate, gate]
73
+ - id: recipe-skill-absent
74
+ file: recipes/skill-absent.md
75
+ type: primary
76
+ focus: "Detect the skill is missing and surface an upgrade path without silently degrading"
77
+ tags: [skill-absent, preflight, install-hint]
78
+ - id: recipe-testing
79
+ file: recipes/testing.md
80
+ type: primary
81
+ focus: "Use the shipped testkit in consumer test suites"
82
+ tags: [testing, testkit, fixtures]
83
+ - id: recipe-format-gate
84
+ file: recipes/format-gate.md
85
+ type: primary
86
+ focus: "Gate consumer CI on skill-llm-wiki contract --json format_version"
87
+ tags: [contract, format-version, compatibility]
88
+
89
+ generator: "skill-llm-wiki/v1"
90
+ ---
91
+
92
+ # Consumers
93
+
94
+ Every consumer recipe in this subtree follows the same shape:
95
+
96
+ 1. **Trigger** — when a consumer should activate this recipe.
97
+ 2. **Commands** — the exact CLI invocations with every flag.
98
+ 3. **Envelope fields** — which fields the consumer reads from the
99
+ `--json` envelope to decide what to do next.
100
+ 4. **Minimum checked-in code** — the consumer-side shell or script
101
+ that the recipe maps to, kept to the smallest honest example.
102
+ 5. **Failure modes** — what happens when each step goes wrong and
103
+ how the recipe tells the consumer to react.
104
+
105
+ Consumers should read [quickstart.md](quickstart.md) first, then
106
+ pick the recipes matching their integration shape.
@@ -0,0 +1,96 @@
1
+ ---
2
+ id: consumers-quickstart
3
+ type: primary
4
+ depth_role: leaf
5
+ focus: "Ten-minute integration path for any consumer that shells out to skill-llm-wiki"
6
+ parents:
7
+ - index.md
8
+ tags:
9
+ - quickstart
10
+ - integration
11
+ - consumers
12
+ activation:
13
+ keyword_matches:
14
+ - quickstart
15
+ - getting started
16
+ - first integration
17
+ - how do i call
18
+ - new consumer
19
+ tag_matches:
20
+ - quickstart
21
+
22
+ generator: "skill-llm-wiki/v1"
23
+ ---
24
+
25
+ # Quickstart
26
+
27
+ You are building a skill, agent, or CI job that wants to manage docs via `skill-llm-wiki`. Here is the shortest path that gets you working without reinventing the common scaffolding.
28
+
29
+ ## 1. Declare the dependency and gate on format_version
30
+
31
+ `skill-llm-wiki` ships a machine-readable version contract. Read it in your preflight:
32
+
33
+ ```bash
34
+ skill-llm-wiki contract --json | jq -r '.format_version'
35
+ ```
36
+
37
+ Fail fast if that number is below your required minimum. See [recipes/format-gate.md](recipes/format-gate.md) for the canonical gate.
38
+
39
+ ## 2. Locate the install path once
40
+
41
+ Every other consumer concern (templates, testkit, SKILL.md) hangs off one canonical probe:
42
+
43
+ ```bash
44
+ skill-llm-wiki where --json
45
+ ```
46
+
47
+ Fields you will actually use: `skill_root`, `skill_md`, `templates_dir`, `testkit_dir`. Stop hard-coding `~/.claude/skills/ctxr-skill-llm-wiki/...`; read it from the probe.
48
+
49
+ ## 3. Init a topic wiki with one command
50
+
51
+ Instead of copying a layout contract and then running build with the right flags, use:
52
+
53
+ ```bash
54
+ skill-llm-wiki init .development/shared/reports --kind dated --template reports --json
55
+ ```
56
+
57
+ The skill seeds the contract from its own shipped templates and prints the exact `build` command to run next. See [recipes/dated-wiki.md](recipes/dated-wiki.md) and [recipes/subject-wiki.md](recipes/subject-wiki.md).
58
+
59
+ ## 4. After every leaf write, run heal
60
+
61
+ Your consumer writes a leaf. Immediately call:
62
+
63
+ ```bash
64
+ skill-llm-wiki heal .development/shared/reports --json
65
+ ```
66
+
67
+ Parse the envelope. Switch on `verdict`:
68
+
69
+ - `ok`: nothing to do.
70
+ - `fixable`: run `skill-llm-wiki fix <wiki>`.
71
+ - `needs-rebuild`: run `skill-llm-wiki rebuild <wiki>`.
72
+ - `broken`: surface the diagnostics to the user; do not auto-mutate.
73
+
74
+ See [recipes/post-write-heal.md](recipes/post-write-heal.md) for the full envelope handling.
75
+
76
+ ## 5. Gate CI on validate
77
+
78
+ Your CI job runs `skill-llm-wiki validate <wiki> --json` against every wiki the project ships. See [recipes/ci-gate.md](recipes/ci-gate.md).
79
+
80
+ ## 6. Handle the skill-absent case explicitly
81
+
82
+ If your consumer is a hard dependency on `skill-llm-wiki`, refuse to run when the skill is missing and point the user at the install command. See [recipes/skill-absent.md](recipes/skill-absent.md) for the canonical message and exit shape.
83
+
84
+ ## 7. Use the shipped testkit in your test suite
85
+
86
+ Hand-rolled stubs and fixtures drift over time. Import from `scripts/testkit/` (path discoverable via `where --json`). See [recipes/testing.md](recipes/testing.md).
87
+
88
+ ## What this replaces
89
+
90
+ - Hand-written layout YAML files duplicated across every consumer.
91
+ - `validate → fix → rebuild` ladders reimplemented per consumer.
92
+ - Three-step path discovery for SKILL.md.
93
+ - Drift-detection tests against SKILL.md prose.
94
+ - Hand-rolled presence stubs.
95
+
96
+ The envelope schema is stable across `format_version` 1; consumers gate on that integer and bump when the skill does.
@@ -0,0 +1,125 @@
1
+ ---
2
+ id: recipe-ci-gate
3
+ type: primary
4
+ depth_role: leaf
5
+ focus: "Gate CI on skill-llm-wiki validate --json"
6
+ parents:
7
+ - ../index.md
8
+ tags:
9
+ - ci
10
+ - validate
11
+ - gate
12
+ - consumers
13
+ activation:
14
+ keyword_matches:
15
+ - ci gate
16
+ - ci validation
17
+ - github actions
18
+ - gitlab pipeline
19
+ - pre-commit wiki
20
+ tag_matches:
21
+ - ci
22
+ - gate
23
+
24
+ generator: "skill-llm-wiki/v1"
25
+ ---
26
+
27
+ # Recipe: CI gate
28
+
29
+ ## Trigger
30
+
31
+ Your consumer wants CI to reject PRs that break wikis shipped in the repository.
32
+
33
+ ## Commands
34
+
35
+ ```bash
36
+ skill-llm-wiki validate .development/shared/reports --json
37
+ ```
38
+
39
+ Exit code `0` → clean. Exit code `2` → errors found; CI should fail.
40
+
41
+ ## Envelope fields
42
+
43
+ ```json
44
+ {
45
+ "schema": "skill-llm-wiki/v1",
46
+ "command": "validate",
47
+ "target": "/abs/.../reports",
48
+ "verdict": "ok" | "broken",
49
+ "exit": 0 | 2,
50
+ "diagnostics": [
51
+ { "code": "IDX-01", "severity": "warning", "path": "...", "message": "..." },
52
+ { "code": "PARSE", "severity": "error", "path": "...", "message": "..." }
53
+ ],
54
+ "timing_ms": 412
55
+ }
56
+ ```
57
+
58
+ CI should fail when `exit !== 0` AND any diagnostic with `severity: "error"` is present. A warning-only run exits 0.
59
+
60
+ ## GitHub Actions example
61
+
62
+ ```yaml
63
+ # .github/workflows/wiki-validate.yml
64
+ name: wiki-validate
65
+ on: [pull_request]
66
+ jobs:
67
+ validate:
68
+ runs-on: ubuntu-latest
69
+ steps:
70
+ - uses: actions/checkout@v4
71
+ - uses: actions/setup-node@v4
72
+ with: { node-version: "20" }
73
+ - run: npm i -g @ctxr/skill-llm-wiki
74
+ - name: contract gate
75
+ run: |
76
+ FV=$(skill-llm-wiki contract --json | jq -r '.format_version')
77
+ if [ "$FV" -lt 1 ]; then
78
+ echo "skill-llm-wiki format_version=$FV is below required 1" >&2
79
+ exit 1
80
+ fi
81
+ - name: validate reports
82
+ run: skill-llm-wiki validate .development/shared/reports --json | tee validate.json
83
+ - name: fail on error diagnostics
84
+ run: |
85
+ errs=$(jq '[.diagnostics[] | select(.severity == "error")] | length' validate.json)
86
+ if [ "$errs" -gt 0 ]; then
87
+ jq '.diagnostics[] | select(.severity == "error")' validate.json
88
+ exit 1
89
+ fi
90
+ ```
91
+
92
+ ## Pre-commit / husky example
93
+
94
+ ```json
95
+ // package.json
96
+ "scripts": {
97
+ "wiki:validate": "skill-llm-wiki validate .development/shared/reports --json | node scripts/wiki-validate-check.mjs"
98
+ }
99
+ ```
100
+
101
+ ```js
102
+ // scripts/wiki-validate-check.mjs
103
+ import { readFileSync } from "node:fs";
104
+ const env = JSON.parse(readFileSync(0, "utf8"));
105
+ const errors = env.diagnostics.filter((d) => d.severity === "error");
106
+ if (errors.length > 0) {
107
+ for (const e of errors) console.error(`[${e.code}] ${e.path}: ${e.message}`);
108
+ process.exit(1);
109
+ }
110
+ ```
111
+
112
+ ## Failure modes
113
+
114
+ - Validate exits `2`: error-severity findings present in the wiki; `.diagnostics` contains one entry per finding with its code, severity, path, and message.
115
+ - Validate exits `4`: Node.js too old on the CI runner. Upgrade to Node 20 or the version pinned in the skill's `.nvmrc`.
116
+ - Validate exits `5`: git missing or too old on the runner.
117
+ - Validate exits `7`: impossible for `validate` alone; only reachable from build/extend/rebuild (Tier 2 suspend-and-resume).
118
+ - Validate exits `8`: runtime deps missing on CI runner. Install `@ctxr/skill-llm-wiki` (and its transitive deps) in the CI image.
119
+ - For wiki-substrate corruption (missing `.llmwiki/git/`, divergent refs), run `skill-llm-wiki heal <wiki> --json` instead — `heal` is the subcommand that classifies substrate state and surfaces verdict `broken` with exit 6.
120
+
121
+ ## Do not
122
+
123
+ - Parse `stderr` for errors. The envelope on stdout is the contract; stderr is free-form logging.
124
+ - Silence errors with `|| true`. Validate failures are real; a broken wiki in `main` propagates to every downstream consumer.
125
+ - Rerun validate in a loop until it passes. If it fails once, it fails deterministically.
@@ -0,0 +1,131 @@
1
+ ---
2
+ id: recipe-dated-wiki
3
+ type: primary
4
+ depth_role: leaf
5
+ focus: "Init a dated topic wiki (reports, sessions, regressions) with one command"
6
+ parents:
7
+ - ../index.md
8
+ tags:
9
+ - dated
10
+ - init
11
+ - hosted
12
+ - consumers
13
+ activation:
14
+ keyword_matches:
15
+ - dated wiki
16
+ - reports wiki
17
+ - sessions wiki
18
+ - regressions wiki
19
+ - yyyy/mm/dd
20
+ tag_matches:
21
+ - dated
22
+ - init
23
+
24
+ generator: "skill-llm-wiki/v1"
25
+ ---
26
+
27
+ # Recipe: dated wiki
28
+
29
+ ## Trigger
30
+
31
+ Your consumer wants to manage a topic whose entries accrete by date (reports, sessions, regression notes, anything with a timestamp). A flat list of date-prefixed siblings will break past a few dozen entries; a nested `{yyyy}/{mm}/{dd}` structure scales indefinitely.
32
+
33
+ ## Commands
34
+
35
+ ```bash
36
+ # Seed the layout contract from a shipped starter template and
37
+ # emit the envelope:
38
+ skill-llm-wiki init .development/shared/reports \
39
+ --kind dated --template reports --json
40
+
41
+ # The envelope names the next command to run. Typically:
42
+ skill-llm-wiki build .development/shared/reports \
43
+ --layout-mode hosted --target .development/shared/reports --json
44
+ ```
45
+
46
+ Available dated templates (from `skill-llm-wiki contract --json` → `layout_tokens`, and `scripts/lib/templates.mjs`):
47
+
48
+ | Template | Default path template | Best for |
49
+ |---|---|---|
50
+ | `reports` | `{yyyy}/{mm}/{dd}` | review reports, investigations |
51
+ | `sessions` | `{yyyy}/{mm}/{dd}` | daily session logs |
52
+ | `regressions` | `{yyyy}/{mm}` | bug triage, post-mortems |
53
+ | `plans` | `{yyyy}/{mm}/{dd}` | implementation plans + subject families |
54
+
55
+ If you omit `--template`, `--kind dated` falls back to `reports`.
56
+
57
+ ## Envelope fields
58
+
59
+ After `init` succeeds, the envelope is:
60
+
61
+ ```json
62
+ {
63
+ "schema": "skill-llm-wiki/v1",
64
+ "command": "init",
65
+ "target": "/abs/path/to/.development/shared/reports",
66
+ "verdict": "initialised",
67
+ "exit": 0,
68
+ "diagnostics": [
69
+ {
70
+ "code": "NEXT-01",
71
+ "severity": "info",
72
+ "path": "/abs/path/to/.development/shared/reports",
73
+ "message": "contract seeded; next step: skill-llm-wiki build ..."
74
+ }
75
+ ],
76
+ "artifacts": {
77
+ "created": ["/abs/.../.llmwiki.layout.yaml"],
78
+ "modified": [],
79
+ "deleted": []
80
+ },
81
+ "next": {
82
+ "command": "skill-llm-wiki",
83
+ "args": ["build", "/abs/.../reports", "--layout-mode", "hosted", "--target", "/abs/.../reports", "--json"]
84
+ },
85
+ "timing_ms": 12
86
+ }
87
+ ```
88
+
89
+ Consumers read the structured `next` field to get the exact build command: `env.next.command` + `env.next.args` go directly into `spawnSync`. The `NEXT-01` diagnostic carries the same hint as human-readable prose for operators tailing stdout, but it is not the canonical machine form.
90
+
91
+ ## Minimum consumer code
92
+
93
+ ```js
94
+ import { spawnSync } from "node:child_process";
95
+
96
+ function initDated(topicPath, template = "reports") {
97
+ const r = spawnSync(
98
+ "skill-llm-wiki",
99
+ ["init", topicPath, "--kind", "dated", "--template", template, "--json"],
100
+ { encoding: "utf8" },
101
+ );
102
+ if (r.status !== 0) throw new Error(`init failed: ${r.stderr}`);
103
+ const env = JSON.parse(r.stdout);
104
+ if (env.verdict !== "initialised") throw new Error(env.diagnostics?.[0]?.message ?? "unexpected");
105
+ // Prefer the structured `next` field over parsing NEXT-01.
106
+ return {
107
+ contractPath: env.artifacts.created[0],
108
+ next: env.next, // { command: "skill-llm-wiki", args: ["build", ...] }
109
+ };
110
+ }
111
+
112
+ // Consumers that want to run the build immediately:
113
+ function initAndBuild(topicPath, template = "reports") {
114
+ const { next } = initDated(topicPath, template);
115
+ if (!next) throw new Error("init envelope missing `next` field");
116
+ const r2 = spawnSync(next.command, next.args, { encoding: "utf8", stdio: "inherit" });
117
+ if (r2.status !== 0) throw new Error(`build exited ${r2.status}`);
118
+ }
119
+ ```
120
+
121
+ ## Failure modes
122
+
123
+ - `verdict: "ambiguous"` with `INIT-02`: you passed neither `--kind` nor `--template`.
124
+ - `verdict: "ambiguous"` with `INIT-05`: `--kind` and `--template` disagree (e.g. `--kind dated --template runbooks`).
125
+ - `verdict: "ambiguous"` with `INIT-07`: a `.llmwiki.layout.yaml` already exists. Pass `--force` only if you are sure the existing contract is wrong; otherwise `skill-llm-wiki rebuild` against the existing contract instead.
126
+ - `verdict: "ambiguous"` with `INIT-08`: the topic path (or the contract path inside it) exists as a symbolic link. `init` refuses to follow symlinks into unknown targets for security. Resolve the symlink explicitly (`realpath <topic>`) and pass the resolved path, or remove the symlink.
127
+
128
+ ## Do not
129
+
130
+ - Copy the template file by hand. The skill's `init` command reads the same file; there is no reason to duplicate it in your own repo.
131
+ - Pick `{yyyy}-{mm}-{dd}` as a filename prefix. Flat date-prefixed siblings are refused by `validate`.
@@ -0,0 +1,126 @@
1
+ ---
2
+ id: recipe-format-gate
3
+ type: primary
4
+ depth_role: leaf
5
+ focus: "Gate consumer CI on skill-llm-wiki contract --json format_version"
6
+ parents:
7
+ - ../index.md
8
+ tags:
9
+ - contract
10
+ - format-version
11
+ - compatibility
12
+ - consumers
13
+ activation:
14
+ keyword_matches:
15
+ - format version
16
+ - contract gate
17
+ - compatibility check
18
+ - version gate
19
+ - drift detection
20
+ tag_matches:
21
+ - contract
22
+ - format-version
23
+
24
+ generator: "skill-llm-wiki/v1"
25
+ ---
26
+
27
+ # Recipe: format_version gate
28
+
29
+ ## Trigger
30
+
31
+ Your consumer's tests used to drift-test against SKILL.md prose. Replace that with a single integer comparison against `format_version`.
32
+
33
+ ## Commands
34
+
35
+ ```bash
36
+ skill-llm-wiki contract --json
37
+ ```
38
+
39
+ Parse the contract JSON; assert `format_version >= <your required>`. Note that `contract --json` emits the contract shape (schema `skill-llm-wiki/contract/v1`), NOT the operational envelope (`skill-llm-wiki/v1`). It carries no `verdict` or `diagnostics` fields.
40
+
41
+ ## Contract JSON fields
42
+
43
+ ```json
44
+ {
45
+ "schema": "skill-llm-wiki/contract/v1",
46
+ "format_version": 1,
47
+ "min_consumer_format_version": 1,
48
+ "package_version": "1.0.1",
49
+ "frontmatter_schema": { "leaf": { "required": [...], "fields": {...} } },
50
+ "layout_tokens": [{ "token": "{yyyy}", "description": "..." }, ...],
51
+ "subcommands": { "build": { "positionals": ["source"], "flags": [...] }, ... },
52
+ "envelope_schema": { "schema": "skill-llm-wiki/v1", "fields": {...} },
53
+ "exit_codes": { "0": "ok", "1": "usage error", ... }
54
+ }
55
+ ```
56
+
57
+ Fields consumers care about:
58
+
59
+ - `format_version`: integer. Bumps on breaking changes.
60
+ - `min_consumer_format_version`: integer. The oldest consumer format_version the current skill still speaks to. A consumer whose required version is below this refuses to run; between this and `format_version` runs unchanged.
61
+ - `package_version`: semver string. For display only; gate on `format_version`, not on semver.
62
+
63
+ ## Minimum consumer code
64
+
65
+ ```js
66
+ import { spawnSync } from "node:child_process";
67
+
68
+ export const REQUIRED_WIKI_FORMAT_VERSION = 1;
69
+
70
+ export function enforceContract(required = REQUIRED_WIKI_FORMAT_VERSION) {
71
+ const r = spawnSync("skill-llm-wiki", ["contract", "--json"], {
72
+ encoding: "utf8",
73
+ });
74
+ if (r.status !== 0) {
75
+ throw new Error(
76
+ `skill-llm-wiki contract failed (${r.status}): ${r.stderr}`,
77
+ );
78
+ }
79
+ const env = JSON.parse(r.stdout);
80
+ if (env.format_version < required) {
81
+ throw new Error(
82
+ `skill-llm-wiki format_version=${env.format_version} is below the required ${required}. ` +
83
+ `Upgrade: npm i -g @ctxr/skill-llm-wiki@latest`,
84
+ );
85
+ }
86
+ if (env.min_consumer_format_version > required) {
87
+ throw new Error(
88
+ `skill-llm-wiki dropped support for format_version=${required}; ` +
89
+ `current min_consumer_format_version=${env.min_consumer_format_version}. ` +
90
+ `Your consumer must upgrade its integration to format_version >= ${env.min_consumer_format_version}.`,
91
+ );
92
+ }
93
+ return env;
94
+ }
95
+ ```
96
+
97
+ ## CI one-liner
98
+
99
+ ```bash
100
+ # Fail CI if the installed skill is below the required format_version.
101
+ FV=$(skill-llm-wiki contract --json | jq -r '.format_version')
102
+ if [ "$FV" -lt 1 ]; then echo "skill format_version=$FV below required 1" >&2; exit 1; fi
103
+ ```
104
+
105
+ ## When to bump your required version
106
+
107
+ Bump `REQUIRED_WIKI_FORMAT_VERSION` when:
108
+
109
+ - The skill bumps `format_version` and your consumer depends on the new behaviour.
110
+ - A leaf frontmatter field your consumer reads is added or removed.
111
+ - A CLI flag your consumer passes becomes mandatory or is removed.
112
+ - The operational envelope shape (schema `skill-llm-wiki/v1`) changes a field your consumer parses from validate/init/heal/rollback output.
113
+
114
+ Do NOT bump when the skill ships a `package_version` patch or minor release that doesn't touch the contract.
115
+
116
+ ## Failure modes
117
+
118
+ - `format_version` missing from the contract JSON: the skill is too old to declare a format version (pre-1). Treat as `< 1` and fail with an upgrade message.
119
+ - Contract JSON is unparseable: the skill's `contract` subcommand is broken. Open an issue upstream.
120
+ - `min_consumer_format_version` exceeds your required version: the skill intentionally dropped support for your version. You must upgrade your consumer.
121
+
122
+ ## Do not
123
+
124
+ - Drift-test against SKILL.md prose. The contract subcommand exists specifically so consumers do not need to read SKILL.md programmatically.
125
+ - Gate on `package_version` semver. A patch release can ship bug fixes without bumping `format_version`; a `format_version` bump is the only breaking-change signal.
126
+ - Cache the contract across PR runs. It's a ~100ms probe; run it every time.