@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.
- package/CHANGELOG.md +118 -0
- package/README.md +2 -2
- package/SKILL.md +7 -0
- package/guide/cli.md +6 -4
- package/guide/consumers/index.md +106 -0
- package/guide/consumers/quickstart.md +96 -0
- package/guide/consumers/recipes/ci-gate.md +125 -0
- package/guide/consumers/recipes/dated-wiki.md +131 -0
- package/guide/consumers/recipes/format-gate.md +126 -0
- package/guide/consumers/recipes/post-write-heal.md +125 -0
- package/guide/consumers/recipes/skill-absent.md +111 -0
- package/guide/consumers/recipes/subject-wiki.md +110 -0
- package/guide/consumers/recipes/testing.md +149 -0
- package/guide/index.md +9 -0
- package/guide/substrate/operators.md +1 -1
- package/guide/substrate/tiered-ai.md +6 -5
- package/guide/ux/user-intent.md +6 -5
- package/package.json +9 -3
- package/scripts/cli.mjs +565 -15
- package/scripts/lib/balance.mjs +579 -0
- package/scripts/lib/cluster-detect.mjs +482 -4
- package/scripts/lib/contract.mjs +257 -0
- package/scripts/lib/decision-log.mjs +121 -15
- package/scripts/lib/heal.mjs +167 -0
- package/scripts/lib/init.mjs +210 -0
- package/scripts/lib/intent.mjs +370 -4
- package/scripts/lib/join-constants.mjs +22 -0
- package/scripts/lib/join.mjs +917 -0
- package/scripts/lib/json-envelope.mjs +190 -0
- package/scripts/lib/nest-applier.mjs +395 -32
- package/scripts/lib/operators.mjs +472 -38
- package/scripts/lib/orchestrator.mjs +419 -12
- package/scripts/lib/root-containment.mjs +351 -0
- package/scripts/lib/similarity-cache.mjs +115 -20
- package/scripts/lib/similarity.mjs +11 -0
- package/scripts/lib/soft-dag.mjs +726 -0
- package/scripts/lib/templates.mjs +78 -0
- package/scripts/lib/tiered.mjs +42 -18
- package/scripts/lib/validate.mjs +22 -0
- package/scripts/lib/where.mjs +71 -0
- package/scripts/testkit/assert-frontmatter.mjs +171 -0
- package/scripts/testkit/cli-run.mjs +95 -0
- package/scripts/testkit/make-wiki-fixture.mjs +301 -0
- package/scripts/testkit/stub-skill.mjs +107 -0
- package/templates/adrs.llmwiki.layout.yaml +33 -0
- package/templates/plans.llmwiki.layout.yaml +34 -0
- package/templates/regressions.llmwiki.layout.yaml +34 -0
- package/templates/reports.llmwiki.layout.yaml +33 -0
- package/templates/runbooks.llmwiki.layout.yaml +33 -0
- 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
|
+
}
|