@ctxr/skill-llm-wiki 1.0.1 → 1.1.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/README.md +2 -2
  3. package/SKILL.md +7 -0
  4. package/guide/cli.md +6 -4
  5. package/guide/consumers/index.md +106 -0
  6. package/guide/consumers/quickstart.md +96 -0
  7. package/guide/consumers/recipes/ci-gate.md +125 -0
  8. package/guide/consumers/recipes/dated-wiki.md +131 -0
  9. package/guide/consumers/recipes/format-gate.md +126 -0
  10. package/guide/consumers/recipes/post-write-heal.md +125 -0
  11. package/guide/consumers/recipes/skill-absent.md +111 -0
  12. package/guide/consumers/recipes/subject-wiki.md +110 -0
  13. package/guide/consumers/recipes/testing.md +149 -0
  14. package/guide/index.md +9 -0
  15. package/guide/substrate/operators.md +1 -1
  16. package/guide/substrate/tiered-ai.md +6 -5
  17. package/guide/ux/user-intent.md +6 -5
  18. package/package.json +9 -3
  19. package/scripts/cli.mjs +565 -15
  20. package/scripts/lib/balance.mjs +579 -0
  21. package/scripts/lib/cluster-detect.mjs +482 -4
  22. package/scripts/lib/contract.mjs +257 -0
  23. package/scripts/lib/decision-log.mjs +121 -15
  24. package/scripts/lib/heal.mjs +167 -0
  25. package/scripts/lib/init.mjs +210 -0
  26. package/scripts/lib/intent.mjs +370 -4
  27. package/scripts/lib/join-constants.mjs +22 -0
  28. package/scripts/lib/join.mjs +917 -0
  29. package/scripts/lib/json-envelope.mjs +190 -0
  30. package/scripts/lib/nest-applier.mjs +395 -32
  31. package/scripts/lib/operators.mjs +472 -38
  32. package/scripts/lib/orchestrator.mjs +419 -12
  33. package/scripts/lib/root-containment.mjs +351 -0
  34. package/scripts/lib/similarity-cache.mjs +115 -20
  35. package/scripts/lib/similarity.mjs +11 -0
  36. package/scripts/lib/soft-dag.mjs +726 -0
  37. package/scripts/lib/templates.mjs +78 -0
  38. package/scripts/lib/tiered.mjs +42 -18
  39. package/scripts/lib/validate.mjs +22 -0
  40. package/scripts/lib/where.mjs +71 -0
  41. package/scripts/testkit/assert-frontmatter.mjs +171 -0
  42. package/scripts/testkit/cli-run.mjs +95 -0
  43. package/scripts/testkit/make-wiki-fixture.mjs +301 -0
  44. package/scripts/testkit/stub-skill.mjs +107 -0
  45. package/templates/adrs.llmwiki.layout.yaml +33 -0
  46. package/templates/plans.llmwiki.layout.yaml +34 -0
  47. package/templates/regressions.llmwiki.layout.yaml +34 -0
  48. package/templates/reports.llmwiki.layout.yaml +33 -0
  49. package/templates/runbooks.llmwiki.layout.yaml +33 -0
  50. package/templates/sessions.llmwiki.layout.yaml +34 -0
@@ -0,0 +1,190 @@
1
+ // json-envelope.mjs — shared JSON stdout shape for consumer-facing
2
+ // operational subcommands that use the envelope (validate, init,
3
+ // heal, rollback). Those commands emit exactly one envelope object
4
+ // on stdout; no surrounding prose, no multiple envelopes per
5
+ // invocation. stderr stays free-form for logs.
6
+ //
7
+ // Probe-style commands — `contract --json` and `where --json` —
8
+ // intentionally return their own non-envelope JSON shapes because
9
+ // they pre-date the envelope contract and have stable consumer
10
+ // schemas of their own (skill-llm-wiki/contract/v1 and
11
+ // skill-llm-wiki/where/v1).
12
+ //
13
+ // Consumers validate the `schema` discriminator as their first
14
+ // check. See guide/consumers/recipes/post-write-heal.md and
15
+ // recipes/ci-gate.md for canonical parsing patterns.
16
+ //
17
+ // Envelope shape (format_version 1):
18
+ // {
19
+ // "schema": "skill-llm-wiki/v1",
20
+ // "command": "validate",
21
+ // "target": "/abs/path" | null,
22
+ // "verdict": "ok" | "fixable" | "needs-rebuild" | "broken"
23
+ // | "built" | "extended" | "healed" | "initialised"
24
+ // | "aborted" | "ambiguous",
25
+ // "exit": <integer>,
26
+ // "diagnostics": [
27
+ // { "code": "IDX-01", "severity": "warning", "path": "...", "message": "..." }
28
+ // ],
29
+ // "artifacts": { "created": [...], "modified": [...], "deleted": [...] },
30
+ // "timing_ms": <integer>,
31
+ // "next": null | { "command": "skill-llm-wiki", "args": ["fix", "/wiki", "--json"] }
32
+ // }
33
+ //
34
+ // `next` is present only when the subcommand wants to hand the
35
+ // consumer a machine-readable follow-up command (init emits the
36
+ // build invocation; heal emits the fix or rebuild invocation).
37
+ // Consumers that receive `verdict: "fixable"` / `"needs-rebuild"`
38
+ // should invoke `next.command` with `next.args` rather than parse
39
+ // the `NEXT-01` info diagnostic's free-text message.
40
+
41
+ export const ENVELOPE_SCHEMA = "skill-llm-wiki/v1";
42
+
43
+ // Every known verdict string. Consumers can switch on these without
44
+ // fearing a surprise value; adding a new verdict is a format_version
45
+ // bump.
46
+ export const VERDICTS = Object.freeze([
47
+ "ok",
48
+ "fixable",
49
+ "needs-rebuild",
50
+ "broken",
51
+ "built",
52
+ "extended",
53
+ "healed",
54
+ "initialised",
55
+ "aborted",
56
+ "ambiguous",
57
+ ]);
58
+
59
+ // Diagnostic severity levels. Consumers gate their CI on `error`.
60
+ export const SEVERITIES = Object.freeze(["error", "warning", "info"]);
61
+
62
+ // Build an envelope from a minimal set of inputs. Missing artifact
63
+ // buckets default to empty arrays so consumers never have to
64
+ // defensively check for undefined.
65
+ //
66
+ // `next` is an optional structured hint for consumers that also
67
+ // carries a human-readable form in an info diagnostic. It is the
68
+ // machine-readable sibling of the NEXT-01 diagnostic consumers may
69
+ // still parse today.
70
+ export function makeEnvelope({
71
+ command,
72
+ target = null,
73
+ verdict,
74
+ exit,
75
+ diagnostics = [],
76
+ artifacts = {},
77
+ timing_ms = 0,
78
+ next = null,
79
+ } = {}) {
80
+ if (!command || typeof command !== "string") {
81
+ throw new Error("makeEnvelope: command is required");
82
+ }
83
+ if (!verdict || typeof verdict !== "string") {
84
+ throw new Error("makeEnvelope: verdict is required");
85
+ }
86
+ if (!VERDICTS.includes(verdict)) {
87
+ throw new Error(
88
+ `makeEnvelope: unknown verdict "${verdict}". Known: ${VERDICTS.join(", ")}`,
89
+ );
90
+ }
91
+ if (!Number.isInteger(exit)) {
92
+ throw new Error("makeEnvelope: exit must be an integer");
93
+ }
94
+ const envelope = {
95
+ schema: ENVELOPE_SCHEMA,
96
+ command,
97
+ target,
98
+ verdict,
99
+ exit,
100
+ diagnostics: Array.isArray(diagnostics) ? diagnostics : [],
101
+ artifacts: {
102
+ created: artifacts.created ?? [],
103
+ modified: artifacts.modified ?? [],
104
+ deleted: artifacts.deleted ?? [],
105
+ },
106
+ timing_ms: Number.isInteger(timing_ms) ? timing_ms : 0,
107
+ };
108
+ if (next !== null) {
109
+ if (
110
+ typeof next !== "object" ||
111
+ typeof next.command !== "string" ||
112
+ !Array.isArray(next.args)
113
+ ) {
114
+ throw new Error(
115
+ "makeEnvelope: next must be { command: string, args: string[] } or null",
116
+ );
117
+ }
118
+ envelope.next = { command: next.command, args: next.args.slice() };
119
+ }
120
+ return envelope;
121
+ }
122
+
123
+ // Shared error-envelope builder used by consumer subcommands that
124
+ // need to surface a structured failure without reinventing the
125
+ // envelope shape. Verdict defaults to "ambiguous" (the canonical
126
+ // error verdict) and exit defaults to 2 (validation / ambiguity per
127
+ // the skill-wide scheme). Usage-error callers pass exit=1 explicitly.
128
+ export function makeErrorEnvelope({
129
+ command,
130
+ code,
131
+ message,
132
+ target = null,
133
+ verdict = "ambiguous",
134
+ exit = 2,
135
+ } = {}) {
136
+ if (!command || typeof command !== "string") {
137
+ throw new Error("makeErrorEnvelope: command is required");
138
+ }
139
+ if (!code || typeof code !== "string") {
140
+ throw new Error("makeErrorEnvelope: code is required");
141
+ }
142
+ return makeEnvelope({
143
+ command,
144
+ target,
145
+ verdict,
146
+ exit,
147
+ diagnostics: [
148
+ { code, severity: "error", path: target, message: message ?? "" },
149
+ ],
150
+ });
151
+ }
152
+
153
+ // Write an envelope to stdout as one line of JSON followed by a
154
+ // newline. Single-line output is easier to pipe through `jq` and
155
+ // also to `grep`-assert in test harnesses.
156
+ export function writeEnvelope(envelope, stream = process.stdout) {
157
+ stream.write(JSON.stringify(envelope) + "\n");
158
+ }
159
+
160
+ // Detect whether --json or --json-errors (legacy alias) was passed.
161
+ // Returns true on the first positive match. `--json-errors`
162
+ // predates the envelope; we accept it as an alias rather than
163
+ // deprecating it loudly, because every existing consumer passes it
164
+ // to get structured intent errors.
165
+ //
166
+ // Only the bare flag forms are supported. `--json=1` / `--json=true`
167
+ // are NOT accepted: the skill's shared arg parser (parseSubArgv)
168
+ // rejects inline values on boolean flags, and hasJsonFlag would
169
+ // otherwise diverge from that contract and silently accept an
170
+ // argument shape that build/extend/rebuild/... reject.
171
+ export function hasJsonFlag(args) {
172
+ if (!Array.isArray(args)) return false;
173
+ for (const tok of args) {
174
+ if (typeof tok !== "string") continue;
175
+ if (tok === "--json" || tok === "--json-errors") return true;
176
+ }
177
+ return false;
178
+ }
179
+
180
+ // Convert a validate-style finding (code, severity, target, message)
181
+ // into a diagnostic object in the envelope's canonical shape. Shared
182
+ // so consumers see the same field names everywhere.
183
+ export function findingToDiagnostic(finding) {
184
+ return {
185
+ code: finding.code ?? "UNKNOWN",
186
+ severity: finding.severity ?? "info",
187
+ path: finding.target ?? null,
188
+ message: finding.message ?? "",
189
+ };
190
+ }