@hawkeyexl/manni 4.0.3 → 4.2.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/README.md +5 -4
- package/dist/{chunk-DEP5AMVA.js → chunk-BNHQWIAE.js} +11 -42
- package/dist/chunk-BNHQWIAE.js.map +1 -0
- package/dist/{chunk-TQG3WCJY.js → chunk-TH565O2N.js} +1450 -750
- package/dist/chunk-TH565O2N.js.map +1 -0
- package/dist/chunk-UYDRDFYF.js +26136 -0
- package/dist/chunk-UYDRDFYF.js.map +1 -0
- package/dist/cli.js +1332 -127
- package/dist/cli.js.map +1 -1
- package/dist/docmeta.js +2 -2
- package/dist/graph/embed.d.ts +154 -0
- package/dist/graph/embed.js +137 -0
- package/dist/graph/embed.js.map +1 -0
- package/dist/graph/runtime.d.ts +751 -0
- package/dist/graph/runtime.js +3067 -0
- package/dist/graph/runtime.js.map +1 -0
- package/dist/index.d.ts +5186 -1339
- package/dist/index.js +526 -6
- package/dist/index.js.map +1 -1
- package/package.json +40 -5
- package/shapes/graph/shapes-1.0.0.ttl +689 -0
- package/dist/chunk-DEP5AMVA.js.map +0 -1
- package/dist/chunk-JYRY7SKD.js +0 -13908
- package/dist/chunk-JYRY7SKD.js.map +0 -1
- package/dist/chunk-TQG3WCJY.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,65 +1,108 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
buildProgram
|
|
4
|
-
|
|
5
|
-
configOption,
|
|
6
|
-
explicitFalse,
|
|
7
|
-
readStdin,
|
|
8
|
-
reportConfig,
|
|
9
|
-
splitList
|
|
10
|
-
} from "./chunk-DEP5AMVA.js";
|
|
3
|
+
buildProgram
|
|
4
|
+
} from "./chunk-BNHQWIAE.js";
|
|
11
5
|
import {
|
|
6
|
+
ALL_FILL_FIELDS,
|
|
7
|
+
CONFIG_SECTION,
|
|
12
8
|
CiteError,
|
|
9
|
+
DEFAULT_CONFIG_FILENAME2 as DEFAULT_CONFIG_FILENAME,
|
|
10
|
+
DEFAULT_EXTENSIONS,
|
|
11
|
+
DocevalsError,
|
|
12
|
+
EXECUTION_GRANTS,
|
|
13
|
+
EXPORT_TARGETS,
|
|
14
|
+
GraphError,
|
|
13
15
|
INFER_FORMATS,
|
|
14
16
|
KeyError,
|
|
15
17
|
LINT_JOBS,
|
|
16
18
|
LintError,
|
|
19
|
+
REPORT_FORMATS as REPORT_FORMATS2,
|
|
17
20
|
RULE_ID_PREFIX,
|
|
21
|
+
SEARCH_MODES,
|
|
18
22
|
SEVERITIES,
|
|
19
23
|
SEVERITY_LIST,
|
|
24
|
+
STDIN,
|
|
25
|
+
SUMMARY_FORMATS,
|
|
20
26
|
TOOLS_BY_JOB,
|
|
21
27
|
asCiteError,
|
|
28
|
+
assertInputFormat,
|
|
29
|
+
bundledShapesPath,
|
|
22
30
|
codeEndAt,
|
|
23
31
|
codeRegions,
|
|
32
|
+
compactIri,
|
|
24
33
|
errorSite,
|
|
25
34
|
findingMessage,
|
|
26
35
|
isCiteRule,
|
|
27
36
|
isInferFormat,
|
|
28
37
|
isSeverity,
|
|
29
|
-
|
|
38
|
+
listReviews,
|
|
39
|
+
loadConfig3 as loadConfig,
|
|
40
|
+
loadGraph,
|
|
41
|
+
loadRunConfig,
|
|
30
42
|
meetsSeverity,
|
|
43
|
+
parseFormat,
|
|
31
44
|
rebaseConfig,
|
|
32
45
|
render,
|
|
46
|
+
render2,
|
|
47
|
+
renderCalibration,
|
|
33
48
|
renderCheckGithub,
|
|
34
49
|
renderCheckJson,
|
|
35
50
|
renderCheckPretty,
|
|
51
|
+
renderEmbed,
|
|
52
|
+
renderFill,
|
|
53
|
+
renderFill2,
|
|
54
|
+
renderList,
|
|
55
|
+
renderQuery,
|
|
36
56
|
renderRemoveJson,
|
|
37
57
|
renderRemovePretty,
|
|
58
|
+
renderReviews,
|
|
59
|
+
renderSearch,
|
|
60
|
+
renderStats,
|
|
38
61
|
renderTemplates,
|
|
39
62
|
renderTools,
|
|
63
|
+
renderTraverse,
|
|
40
64
|
renderUpdateJson,
|
|
41
65
|
renderUpdatePretty,
|
|
42
66
|
resolveStructureTool,
|
|
43
67
|
runAdd,
|
|
68
|
+
runBuild,
|
|
69
|
+
runCalibrate,
|
|
44
70
|
runCheck,
|
|
71
|
+
runEmbed,
|
|
72
|
+
runExport,
|
|
73
|
+
runFill,
|
|
74
|
+
runFill2,
|
|
75
|
+
runGenerate,
|
|
76
|
+
runInit,
|
|
45
77
|
runKeyRotate,
|
|
46
78
|
runKeySet,
|
|
47
79
|
runLint,
|
|
80
|
+
runList,
|
|
81
|
+
runPromote,
|
|
82
|
+
runQuery,
|
|
48
83
|
runRemove,
|
|
84
|
+
runReview,
|
|
85
|
+
runRun,
|
|
86
|
+
runSearch,
|
|
87
|
+
runStats,
|
|
49
88
|
runTemplates,
|
|
50
89
|
runTemplatesInfer,
|
|
51
90
|
runTools,
|
|
91
|
+
runTraverse,
|
|
52
92
|
runUpdate,
|
|
93
|
+
seedGoldenCases,
|
|
53
94
|
shortCommit,
|
|
54
95
|
shortLine,
|
|
55
96
|
shortPin,
|
|
56
97
|
shortSrc,
|
|
57
|
-
spellSource
|
|
58
|
-
|
|
98
|
+
spellSource,
|
|
99
|
+
validateGraph
|
|
100
|
+
} from "./chunk-UYDRDFYF.js";
|
|
59
101
|
import {
|
|
60
102
|
COMMON_FORMAT_LIST,
|
|
61
103
|
CONCEPT_FIELDS,
|
|
62
104
|
FAMILY_CONFIG_NAMES,
|
|
105
|
+
LOCAL_FLAG_HELP,
|
|
63
106
|
OMITTED_WHEN_CLEAN,
|
|
64
107
|
REPORT_FORMATS,
|
|
65
108
|
REPORT_FORMAT_LIST,
|
|
@@ -68,9 +111,12 @@ import {
|
|
|
68
111
|
TERM_RELATION_FIELDS,
|
|
69
112
|
ToolError,
|
|
70
113
|
assertNonEmpty,
|
|
114
|
+
collect,
|
|
115
|
+
configOption,
|
|
71
116
|
errorMessage,
|
|
72
117
|
escapeWorkflowCommandMessage,
|
|
73
118
|
escapeWorkflowCommandProperty,
|
|
119
|
+
explicitFalse,
|
|
74
120
|
extractorByName,
|
|
75
121
|
extractorForExtension,
|
|
76
122
|
fail,
|
|
@@ -87,23 +133,28 @@ import {
|
|
|
87
133
|
palette,
|
|
88
134
|
programName,
|
|
89
135
|
readConfigFile,
|
|
136
|
+
readStdin,
|
|
90
137
|
registerTermLabelSource,
|
|
91
138
|
renderJunit,
|
|
92
139
|
renderSarif,
|
|
140
|
+
reportConfig,
|
|
93
141
|
resolveBaselineRequest,
|
|
94
142
|
resolveTargetSet,
|
|
95
143
|
runIfMain,
|
|
96
144
|
selectCollections,
|
|
97
145
|
settleBaseline,
|
|
98
146
|
shouldColor,
|
|
147
|
+
splitList,
|
|
99
148
|
splitPageArgument,
|
|
100
149
|
terminalConfirm,
|
|
101
150
|
valeConfigPath,
|
|
102
|
-
valueAt
|
|
103
|
-
|
|
151
|
+
valueAt,
|
|
152
|
+
warn,
|
|
153
|
+
writeTextAtomic
|
|
154
|
+
} from "./chunk-TH565O2N.js";
|
|
104
155
|
|
|
105
156
|
// src/cli.ts
|
|
106
|
-
import { Command as
|
|
157
|
+
import { Command as Command8 } from "commander";
|
|
107
158
|
|
|
108
159
|
// src/a11y/cli.ts
|
|
109
160
|
import { Command } from "commander";
|
|
@@ -203,17 +254,19 @@ async function crawl(opts, analyzer) {
|
|
|
203
254
|
url = normalizeUrl(raw);
|
|
204
255
|
key = dedupeKey(url);
|
|
205
256
|
} catch {
|
|
206
|
-
return;
|
|
257
|
+
return { as: "invalid" };
|
|
207
258
|
}
|
|
208
|
-
if (source !== "seed" && !isPageLink(url)) return;
|
|
209
|
-
if (!opts.seeds.some((seed) => sameHost(seed, url))) return;
|
|
210
|
-
|
|
259
|
+
if (source !== "seed" && !isPageLink(url)) return { as: "asset" };
|
|
260
|
+
if (!opts.seeds.some((seed) => sameHost(seed, url))) return { as: "off-host" };
|
|
261
|
+
const pattern = opts.exclude?.match(url) ?? null;
|
|
262
|
+
if (pattern !== null) {
|
|
211
263
|
excluded.add(key);
|
|
212
|
-
return;
|
|
264
|
+
return { as: "excluded", pattern };
|
|
213
265
|
}
|
|
214
|
-
if (seen.has(key)) return;
|
|
266
|
+
if (seen.has(key)) return { as: "seen" };
|
|
215
267
|
seen.add(key);
|
|
216
268
|
frontier.push({ url, key, source });
|
|
269
|
+
return { as: "queued" };
|
|
217
270
|
};
|
|
218
271
|
const landed = (finalUrl) => {
|
|
219
272
|
if (finalUrl === void 0) return;
|
|
@@ -230,6 +283,7 @@ async function crawl(opts, analyzer) {
|
|
|
230
283
|
progress({ kind: "excluded", urls: excluded.size, patterns: opts.exclude?.patterns.length ?? 0 });
|
|
231
284
|
}
|
|
232
285
|
const pages = [];
|
|
286
|
+
const redirects = [];
|
|
233
287
|
let duplicates = 0;
|
|
234
288
|
const capped = () => opts.maxPages !== void 0 && pages.length >= opts.maxPages;
|
|
235
289
|
for (let next = 0; next < frontier.length && !capped(); next++) {
|
|
@@ -242,10 +296,28 @@ async function crawl(opts, analyzer) {
|
|
|
242
296
|
}
|
|
243
297
|
visited.add(key);
|
|
244
298
|
const index = pages.length + 1;
|
|
245
|
-
if (
|
|
299
|
+
if (pages.length + redirects.length === 0) progress({ kind: "browser" });
|
|
246
300
|
progress({ kind: "page", index, queued: frontier.length, url });
|
|
247
301
|
try {
|
|
248
302
|
const analyzed = await analyzer.analyze(url, opts.analyze);
|
|
303
|
+
if ("redirect" in analyzed) {
|
|
304
|
+
const to = normalized(analyzed.redirect);
|
|
305
|
+
redirects.push({ url, to, source });
|
|
306
|
+
progress({ kind: "redirected", index, url, to });
|
|
307
|
+
const outcome2 = enqueue(to, source);
|
|
308
|
+
if (source === "seed") {
|
|
309
|
+
if (!isHttpUrl(to)) {
|
|
310
|
+
throw new A11yError(`${url} redirects to ${to}, which is not an http(s) URL.`);
|
|
311
|
+
}
|
|
312
|
+
if (outcome2.as === "off-host") {
|
|
313
|
+
throw new A11yError(`${url} redirects to ${to}, which is on another host. Check ${to} instead.`);
|
|
314
|
+
}
|
|
315
|
+
if (outcome2.as === "excluded") {
|
|
316
|
+
throw new A11yError(`${outcome2.pattern.source} excludes ${to}, where the seed ${url} redirects.`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
249
321
|
pages.push({ ...analyzed.result, url, source });
|
|
250
322
|
landed(analyzed.finalUrl);
|
|
251
323
|
if (opts.crawl) for (const link of analyzed.links) enqueue(link, "link");
|
|
@@ -257,9 +329,23 @@ async function crawl(opts, analyzer) {
|
|
|
257
329
|
progress({ kind: "checked", index, url, violations: 0, error });
|
|
258
330
|
}
|
|
259
331
|
}
|
|
260
|
-
const skipped2 = frontier.length - pages.length - duplicates;
|
|
332
|
+
const skipped2 = frontier.length - pages.length - duplicates - redirects.length;
|
|
261
333
|
progress({ kind: "done", checked: pages.length, skipped: skipped2 });
|
|
262
|
-
return {
|
|
334
|
+
return {
|
|
335
|
+
pages,
|
|
336
|
+
discovered: frontier.length,
|
|
337
|
+
skipped: skipped2,
|
|
338
|
+
duplicates,
|
|
339
|
+
redirects,
|
|
340
|
+
excluded: excluded.size
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function normalized(url) {
|
|
344
|
+
try {
|
|
345
|
+
return normalizeUrl(url);
|
|
346
|
+
} catch {
|
|
347
|
+
return url;
|
|
348
|
+
}
|
|
263
349
|
}
|
|
264
350
|
|
|
265
351
|
// src/a11y/core/exclude.ts
|
|
@@ -501,6 +587,7 @@ async function runCheck2(opts, deps) {
|
|
|
501
587
|
const results = outcome2.pages.map((page) => finishPage(page, opts.severity));
|
|
502
588
|
return {
|
|
503
589
|
results,
|
|
590
|
+
redirects: outcome2.redirects,
|
|
504
591
|
summary: summarize(results, outcome2, sitemap, opts.crawl)
|
|
505
592
|
};
|
|
506
593
|
} finally {
|
|
@@ -514,7 +601,7 @@ function finishPage(page, floor) {
|
|
|
514
601
|
const score = applicable === 0 ? null : Math.round(100 * page.passes / applicable);
|
|
515
602
|
return { ...page, violations, score };
|
|
516
603
|
}
|
|
517
|
-
function summarize(results, { discovered, skipped: skipped2, duplicates, excluded }, sitemap, crawl2) {
|
|
604
|
+
function summarize(results, { discovered, skipped: skipped2, duplicates, redirects, excluded }, sitemap, crawl2) {
|
|
518
605
|
const bySeverity = { notice: 0, warning: 0, error: 0 };
|
|
519
606
|
let failed = 0;
|
|
520
607
|
let violations = 0;
|
|
@@ -530,6 +617,7 @@ function summarize(results, { discovered, skipped: skipped2, duplicates, exclude
|
|
|
530
617
|
checked: results.length,
|
|
531
618
|
skipped: skipped2,
|
|
532
619
|
duplicates,
|
|
620
|
+
redirects: redirects.length,
|
|
533
621
|
excluded,
|
|
534
622
|
failed,
|
|
535
623
|
violations,
|
|
@@ -574,33 +662,18 @@ function createPlaywrightAnalyzer() {
|
|
|
574
662
|
return {
|
|
575
663
|
async analyze(url, opts) {
|
|
576
664
|
const { page } = await open();
|
|
665
|
+
const navigations = [];
|
|
666
|
+
const onRequest = (request) => {
|
|
667
|
+
if (request.isNavigationRequest() && request.frame() === page.mainFrame() && request.redirectedFrom() === null) {
|
|
668
|
+
navigations.push(request);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
page.on("request", onRequest);
|
|
577
672
|
try {
|
|
578
|
-
await page
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
}
|
|
582
|
-
const links = await page.$$eval(
|
|
583
|
-
"a[href]",
|
|
584
|
-
(anchors) => anchors.map((a) => a.href)
|
|
585
|
-
);
|
|
586
|
-
let builder = new AxeBuilder({ page });
|
|
587
|
-
if (opts.tags.length > 0) builder = builder.withTags(opts.tags);
|
|
588
|
-
let results;
|
|
589
|
-
try {
|
|
590
|
-
results = await builder.analyze();
|
|
591
|
-
} catch (err) {
|
|
592
|
-
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
673
|
+
return await analyzePage(page, url, opts, navigations);
|
|
674
|
+
} finally {
|
|
675
|
+
page.off("request", onRequest);
|
|
593
676
|
}
|
|
594
|
-
return {
|
|
595
|
-
result: {
|
|
596
|
-
url,
|
|
597
|
-
violations: results.violations.map(toViolation),
|
|
598
|
-
passes: results.passes.length,
|
|
599
|
-
incomplete: results.incomplete.length
|
|
600
|
-
},
|
|
601
|
-
links,
|
|
602
|
-
finalUrl: page.url()
|
|
603
|
-
};
|
|
604
677
|
},
|
|
605
678
|
async close() {
|
|
606
679
|
if (session === null) return;
|
|
@@ -614,6 +687,103 @@ function createPlaywrightAnalyzer() {
|
|
|
614
687
|
}
|
|
615
688
|
};
|
|
616
689
|
}
|
|
690
|
+
async function analyzePage(page, url, opts, navigations) {
|
|
691
|
+
let response;
|
|
692
|
+
try {
|
|
693
|
+
response = await page.goto(url, { waitUntil: "load", timeout: opts.timeout });
|
|
694
|
+
} catch (err) {
|
|
695
|
+
throw new A11yError(`Could not load ${url}: ${reason(err)}`);
|
|
696
|
+
}
|
|
697
|
+
const loaded = response?.url() ?? page.url();
|
|
698
|
+
const nextNavigation = () => {
|
|
699
|
+
let first = response?.request() ?? null;
|
|
700
|
+
for (let from = first?.redirectedFrom() ?? null; from !== null; from = from.redirectedFrom()) {
|
|
701
|
+
first = from;
|
|
702
|
+
}
|
|
703
|
+
const own = first === null ? 0 : navigations.indexOf(first);
|
|
704
|
+
return own === -1 ? void 0 : navigations[own + 1];
|
|
705
|
+
};
|
|
706
|
+
const destination = () => nextNavigation()?.url() ?? page.url();
|
|
707
|
+
const navigatedAway = async () => {
|
|
708
|
+
if (nextNavigation() === void 0 && samePage(page.url(), loaded)) return null;
|
|
709
|
+
try {
|
|
710
|
+
await page.waitForURL((u) => !samePage(u.href, loaded), {
|
|
711
|
+
waitUntil: "commit",
|
|
712
|
+
timeout: opts.timeout
|
|
713
|
+
});
|
|
714
|
+
return { redirect: destination() };
|
|
715
|
+
} catch {
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
let read8;
|
|
720
|
+
try {
|
|
721
|
+
read8 = await page.evaluate(readPage);
|
|
722
|
+
} catch (err) {
|
|
723
|
+
const moved = await navigatedAway();
|
|
724
|
+
if (moved !== null) return moved;
|
|
725
|
+
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
726
|
+
}
|
|
727
|
+
const refresh = refreshTarget(read8.refresh, page.url());
|
|
728
|
+
if (refresh !== null) return { redirect: refresh };
|
|
729
|
+
let builder = new AxeBuilder({ page });
|
|
730
|
+
if (opts.tags.length > 0) builder = builder.withTags(opts.tags);
|
|
731
|
+
let results;
|
|
732
|
+
try {
|
|
733
|
+
results = await builder.analyze();
|
|
734
|
+
} catch (err) {
|
|
735
|
+
const moved = await navigatedAway();
|
|
736
|
+
if (moved !== null) return moved;
|
|
737
|
+
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
738
|
+
}
|
|
739
|
+
if (!samePage(page.url(), loaded)) return { redirect: destination() };
|
|
740
|
+
return {
|
|
741
|
+
result: {
|
|
742
|
+
url,
|
|
743
|
+
violations: results.violations.map(toViolation),
|
|
744
|
+
passes: results.passes.length,
|
|
745
|
+
incomplete: results.incomplete.length
|
|
746
|
+
},
|
|
747
|
+
links: read8.links,
|
|
748
|
+
finalUrl: page.url()
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
function readPage() {
|
|
752
|
+
const doc = globalThis.document;
|
|
753
|
+
return {
|
|
754
|
+
links: Array.from(doc.querySelectorAll("a[href]"), (a) => a.href),
|
|
755
|
+
refresh: doc.querySelector('meta[http-equiv="refresh" i]')?.getAttribute("content") ?? null
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
function refreshUrl(content) {
|
|
759
|
+
const match = /^\s*[\d.]*\s*[;,]\s*(?:url\s*=\s*)?(.*)$/is.exec(content);
|
|
760
|
+
let target = match?.[1]?.trim() ?? "";
|
|
761
|
+
const quote = target[0];
|
|
762
|
+
if (quote === "'" || quote === '"') {
|
|
763
|
+
const end = target.indexOf(quote, 1);
|
|
764
|
+
target = (end === -1 ? target.slice(1) : target.slice(1, end)).trim();
|
|
765
|
+
}
|
|
766
|
+
return target === "" ? null : target;
|
|
767
|
+
}
|
|
768
|
+
function refreshTarget(content, base) {
|
|
769
|
+
if (content === null) return null;
|
|
770
|
+
const target = refreshUrl(content);
|
|
771
|
+
if (target === null) return null;
|
|
772
|
+
let to;
|
|
773
|
+
try {
|
|
774
|
+
to = new URL(target, base).href;
|
|
775
|
+
} catch {
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
return samePage(to, base) ? null : to;
|
|
779
|
+
}
|
|
780
|
+
function samePage(a, b) {
|
|
781
|
+
try {
|
|
782
|
+
return dedupeKey(a) === dedupeKey(b);
|
|
783
|
+
} catch {
|
|
784
|
+
return a === b;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
617
787
|
function severityOf(impact) {
|
|
618
788
|
switch (impact) {
|
|
619
789
|
case "critical":
|
|
@@ -798,8 +968,13 @@ function renderPretty(run, opts) {
|
|
|
798
968
|
);
|
|
799
969
|
for (const v of page.violations) lines.push(...violationLines(v, c));
|
|
800
970
|
}
|
|
971
|
+
if (!opts.quiet) {
|
|
972
|
+
for (const r of run.redirects) {
|
|
973
|
+
lines.push(`${c.dim("\u21AA")} ${c.cyan(r.url)} redirects to ${c.cyan(r.to)}`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
801
976
|
const failed = summary.failed;
|
|
802
|
-
const footer = `${summary.violations} violation${summary.violations === 1 ? "" : "s"} on ${failed} of ${summary.checked} pages` + (summary.skipped > 0 ? `; ${summary.skipped} skipped (--max-pages)` : "") + (summary.duplicates > 0 ? `; ${summary.duplicates} duplicate${summary.duplicates === 1 ? "" : "s"} dropped` : "") + (summary.excluded > 0 ? `; ${summary.excluded} excluded` : "");
|
|
977
|
+
const footer = `${summary.violations} violation${summary.violations === 1 ? "" : "s"} on ${failed} of ${summary.checked} pages` + (summary.skipped > 0 ? `; ${summary.skipped} skipped (--max-pages)` : "") + (summary.duplicates > 0 ? `; ${summary.duplicates} duplicate${summary.duplicates === 1 ? "" : "s"} dropped` : "") + (summary.redirects > 0 ? `; ${summary.redirects} redirect${summary.redirects === 1 ? "" : "s"}` : "") + (summary.excluded > 0 ? `; ${summary.excluded} excluded` : "");
|
|
803
978
|
lines.push("");
|
|
804
979
|
lines.push(failed > 0 ? c.red(footer) : c.green(footer));
|
|
805
980
|
return lines.join("\n");
|
|
@@ -858,7 +1033,7 @@ var A11Y_FORMAT_LIST = A11Y_FORMATS.join(" | ");
|
|
|
858
1033
|
function isA11yFormat(value) {
|
|
859
1034
|
return A11Y_FORMATS.includes(value);
|
|
860
1035
|
}
|
|
861
|
-
function
|
|
1036
|
+
function render3(format, run, opts) {
|
|
862
1037
|
switch (format) {
|
|
863
1038
|
case "pretty":
|
|
864
1039
|
return renderPretty(run, opts);
|
|
@@ -918,6 +1093,9 @@ function terminalReporter(opts) {
|
|
|
918
1093
|
case "checked":
|
|
919
1094
|
showUrl(`${c.dim(counter(event.index, queued))} ${outcome(event, c)}`, event.url);
|
|
920
1095
|
return;
|
|
1096
|
+
case "redirected":
|
|
1097
|
+
showUrl(`${c.dim(counter(event.index, queued))} ${c.dim("\u21AA")} redirects to `, event.to);
|
|
1098
|
+
return;
|
|
921
1099
|
case "done":
|
|
922
1100
|
stream.write(CLEAR_LINE);
|
|
923
1101
|
return;
|
|
@@ -958,6 +1136,9 @@ function plainReporter(stream) {
|
|
|
958
1136
|
else say(`${head} \u2717 ${plural(event.violations, "violation")}`);
|
|
959
1137
|
return;
|
|
960
1138
|
}
|
|
1139
|
+
case "redirected":
|
|
1140
|
+
say(`${counter(event.index, queued)} \u21AA redirects to ${event.to}`);
|
|
1141
|
+
return;
|
|
961
1142
|
case "done":
|
|
962
1143
|
say(`checked ${plural(event.checked, "page")}, ${event.skipped} skipped`);
|
|
963
1144
|
return;
|
|
@@ -1109,7 +1290,7 @@ function buildProgram2() {
|
|
|
1109
1290
|
{ analyzer: createPlaywrightAnalyzer(), onProgress: progress.listener }
|
|
1110
1291
|
);
|
|
1111
1292
|
const color = resolveColor(parent, process.stdout);
|
|
1112
|
-
const text2 =
|
|
1293
|
+
const text2 = render3(format, run, { color, quiet: Boolean(options.quiet) });
|
|
1113
1294
|
if (text2.length > 0 || format !== "github") {
|
|
1114
1295
|
process.stdout.write(`${text2}
|
|
1115
1296
|
`);
|
|
@@ -1583,8 +1764,414 @@ function buildProgram3() {
|
|
|
1583
1764
|
return program;
|
|
1584
1765
|
}
|
|
1585
1766
|
|
|
1586
|
-
// src/
|
|
1767
|
+
// src/docevals/cli.ts
|
|
1587
1768
|
import { Command as Command3 } from "commander";
|
|
1769
|
+
function colorFor2(command, isTTY, env) {
|
|
1770
|
+
const noColor = colorOwner2(command).opts().color === false;
|
|
1771
|
+
return shouldColor({ noColor, isTTY, env });
|
|
1772
|
+
}
|
|
1773
|
+
function colorOwner2(command) {
|
|
1774
|
+
for (let c = command; c !== null; c = c.parent) {
|
|
1775
|
+
if (c.options.some((o) => o.long === "--no-color")) return c;
|
|
1776
|
+
}
|
|
1777
|
+
return command;
|
|
1778
|
+
}
|
|
1779
|
+
function buildProgram4() {
|
|
1780
|
+
const program = new Command3();
|
|
1781
|
+
program.name("docevals").description(
|
|
1782
|
+
"Deterministic and LLM-as-judge evals for documentation pages, driven by frontmatter."
|
|
1783
|
+
).version(package_default.version).option("--no-color", "disable colored output").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
1784
|
+
function collectGrant(value, previous) {
|
|
1785
|
+
if (!EXECUTION_GRANTS.includes(value)) {
|
|
1786
|
+
fail(
|
|
1787
|
+
new DocevalsError(
|
|
1788
|
+
`--allow-execution must be one of ${EXECUTION_GRANTS.join(" | ")}, got "${value}"`
|
|
1789
|
+
)
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
return [...previous, value];
|
|
1793
|
+
}
|
|
1794
|
+
function parseIntArg(name) {
|
|
1795
|
+
return (value) => {
|
|
1796
|
+
const n = Number.parseInt(value, 10);
|
|
1797
|
+
if (Number.isNaN(n) || n < 1) {
|
|
1798
|
+
fail(new DocevalsError(`${name} must be a positive integer, got "${value}"`));
|
|
1799
|
+
}
|
|
1800
|
+
return n;
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
function collectArg(value, previous) {
|
|
1804
|
+
return [...previous, value];
|
|
1805
|
+
}
|
|
1806
|
+
function parseFloatArg(name) {
|
|
1807
|
+
return (value) => {
|
|
1808
|
+
const n = Number.parseFloat(value);
|
|
1809
|
+
if (Number.isNaN(n) || n < 0) {
|
|
1810
|
+
fail(new DocevalsError(`${name} must be a non-negative number, got "${value}"`));
|
|
1811
|
+
}
|
|
1812
|
+
return n;
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
function parseFormatArg(name, allowed) {
|
|
1816
|
+
return (value) => {
|
|
1817
|
+
try {
|
|
1818
|
+
return parseFormat(value, allowed, name);
|
|
1819
|
+
} catch (e) {
|
|
1820
|
+
fail(e);
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
function documentInputs(command, verb) {
|
|
1825
|
+
return command.argument(
|
|
1826
|
+
"[paths...]",
|
|
1827
|
+
`Files, directories, or globs to ${verb} (default: the configured collections)`
|
|
1828
|
+
).option(
|
|
1829
|
+
"--collection <name>",
|
|
1830
|
+
"Configured collection to read (repeatable)",
|
|
1831
|
+
collect,
|
|
1832
|
+
[]
|
|
1833
|
+
).option("--exclude <glob>", "Glob to exclude (repeatable)", collect, []).option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file");
|
|
1834
|
+
}
|
|
1835
|
+
function documentOptions(opts) {
|
|
1836
|
+
const { configPath, noConfig } = configOption(opts.config);
|
|
1837
|
+
return {
|
|
1838
|
+
...configPath === void 0 ? {} : { config: configPath },
|
|
1839
|
+
...noConfig === true ? { noConfig } : {},
|
|
1840
|
+
...opts.collection === void 0 ? {} : { collection: opts.collection },
|
|
1841
|
+
...opts.exclude === void 0 ? {} : { exclude: opts.exclude }
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
documentInputs(
|
|
1845
|
+
program.command("list").description("Show the resolved eval plan per page without running anything"),
|
|
1846
|
+
"list"
|
|
1847
|
+
).option(
|
|
1848
|
+
"-f, --format <format>",
|
|
1849
|
+
`Output format: ${SUMMARY_FORMATS.join(" | ")}`,
|
|
1850
|
+
parseFormatArg("--format", SUMMARY_FORMATS),
|
|
1851
|
+
"pretty"
|
|
1852
|
+
).option("--eval <name>", "Show only this eval (repeatable)", collectArg, []).option("--suite <name>", "Show only evals in this suite").action(
|
|
1853
|
+
async (paths, opts, command) => {
|
|
1854
|
+
try {
|
|
1855
|
+
const run = await runList(paths, {
|
|
1856
|
+
...documentOptions(opts),
|
|
1857
|
+
format: opts.format,
|
|
1858
|
+
evalNames: opts.eval,
|
|
1859
|
+
suite: opts.suite
|
|
1860
|
+
});
|
|
1861
|
+
console.log(
|
|
1862
|
+
renderList(run, opts.format, {
|
|
1863
|
+
color: colorFor2(command, process.stdout.isTTY)
|
|
1864
|
+
})
|
|
1865
|
+
);
|
|
1866
|
+
process.exitCode = run.exitCode;
|
|
1867
|
+
} catch (e) {
|
|
1868
|
+
fail(e);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
);
|
|
1872
|
+
documentInputs(
|
|
1873
|
+
program.command("run").description("Run evals against documentation pages"),
|
|
1874
|
+
"evaluate"
|
|
1875
|
+
).option(
|
|
1876
|
+
"-f, --format <format>",
|
|
1877
|
+
`Output format: ${REPORT_FORMATS2.join(" | ")}`,
|
|
1878
|
+
parseFormatArg("--format", REPORT_FORMATS2),
|
|
1879
|
+
"pretty"
|
|
1880
|
+
).option("--deterministic-only", "Run only command/tool graders, skip the AI judge").option("--ai-only", "Run only AI-judged evals, skip deterministic graders").option(
|
|
1881
|
+
// `<kind>`, not `<kind...>`: a variadic option greedily eats the
|
|
1882
|
+
// positional `[paths...]` that follow it, so `--allow-execution
|
|
1883
|
+
// frontmatter-commands docs/**` would silently swallow the glob and run
|
|
1884
|
+
// over the default file set instead. Repeat the flag to grant twice.
|
|
1885
|
+
"--allow-execution <kind>",
|
|
1886
|
+
"Grant content-authored execution: frontmatter-commands",
|
|
1887
|
+
collectGrant,
|
|
1888
|
+
[]
|
|
1889
|
+
).option("--no-execution", "Clear every execution grant for this run").option("--no-generate", "Do not generate scripts for command evals missing a command").option("--no-cache", "Bypass the judge response cache").option("--fail-on-review", "Exit 1 when any eval lands in the human-review zone").option("--provider <name>", "Judge provider: auto (default) | anthropic | openai | claude-cli | llama-cpp").option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).option("--runs <n>", "Ensemble runs per eval", parseIntArg("--runs")).option(
|
|
1890
|
+
"--chunk-chars <n>",
|
|
1891
|
+
"Characters of page per judge call; longer pages are read in parts",
|
|
1892
|
+
parseIntArg("--chunk-chars")
|
|
1893
|
+
).option(
|
|
1894
|
+
"--eval <name>",
|
|
1895
|
+
"Run only this eval (repeatable); suite targets are not evaluated on a filtered run",
|
|
1896
|
+
collectArg,
|
|
1897
|
+
[]
|
|
1898
|
+
).option("--suite <name>", "Run only evals in this suite").option(
|
|
1899
|
+
"--since <ref>",
|
|
1900
|
+
"Evaluate only pages whose file or eval manifest changed between this git ref and HEAD"
|
|
1901
|
+
).option(
|
|
1902
|
+
"--max-turns <n>",
|
|
1903
|
+
"Stop after this many ensemble runs (a cached ensemble costs none)",
|
|
1904
|
+
parseIntArg("--max-turns")
|
|
1905
|
+
).option(
|
|
1906
|
+
"--baseline [path]",
|
|
1907
|
+
"Fail only on findings a recorded baseline does not already hold"
|
|
1908
|
+
).option("--no-baseline", "Ignore the configured baseline for this run").option(
|
|
1909
|
+
"--write-baseline [path]",
|
|
1910
|
+
"Record this run's findings as the baseline; without a path, the configured one"
|
|
1911
|
+
).action(async (paths, opts, command) => {
|
|
1912
|
+
try {
|
|
1913
|
+
const report = await runRun(paths, {
|
|
1914
|
+
...documentOptions({
|
|
1915
|
+
config: opts.config,
|
|
1916
|
+
collection: opts.collection,
|
|
1917
|
+
exclude: opts.exclude
|
|
1918
|
+
}),
|
|
1919
|
+
// parseFormatArg validated this at parse time; the cast only re-narrows
|
|
1920
|
+
// from the `unknown` that the Record-typed options bag erases it to.
|
|
1921
|
+
format: opts.format,
|
|
1922
|
+
deterministicOnly: opts.deterministicOnly,
|
|
1923
|
+
aiOnly: opts.aiOnly,
|
|
1924
|
+
allowExecution: opts.allowExecution,
|
|
1925
|
+
execution: opts.execution,
|
|
1926
|
+
generate: opts.generate,
|
|
1927
|
+
cache: opts.cache,
|
|
1928
|
+
failOnReview: opts.failOnReview,
|
|
1929
|
+
provider: opts.provider,
|
|
1930
|
+
model: opts.model,
|
|
1931
|
+
local: opts.local,
|
|
1932
|
+
runs: opts.runs,
|
|
1933
|
+
chunkChars: opts.chunkChars,
|
|
1934
|
+
maxTurns: opts.maxTurns,
|
|
1935
|
+
evalNames: opts.eval,
|
|
1936
|
+
suite: opts.suite,
|
|
1937
|
+
since: opts.since,
|
|
1938
|
+
// commander collapses `--baseline` to true and `--no-baseline` to
|
|
1939
|
+
// false on the same key; a string is an explicit path.
|
|
1940
|
+
baseline: opts.baseline,
|
|
1941
|
+
writeBaseline: opts.writeBaseline,
|
|
1942
|
+
toolVersion: package_default.version
|
|
1943
|
+
});
|
|
1944
|
+
console.log(
|
|
1945
|
+
render(report, opts.format, {
|
|
1946
|
+
color: colorFor2(command, process.stdout.isTTY)
|
|
1947
|
+
})
|
|
1948
|
+
);
|
|
1949
|
+
process.exitCode = report.exitCode;
|
|
1950
|
+
} catch (e) {
|
|
1951
|
+
fail(e);
|
|
1952
|
+
}
|
|
1953
|
+
});
|
|
1954
|
+
documentInputs(
|
|
1955
|
+
program.command("generate").description(
|
|
1956
|
+
"Generate check scripts for command evals with a plain-language assertion but no command"
|
|
1957
|
+
),
|
|
1958
|
+
"read"
|
|
1959
|
+
).option("--provider <name>", "Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp").option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).action(
|
|
1960
|
+
async (paths, opts, command) => {
|
|
1961
|
+
try {
|
|
1962
|
+
const pc = palette(colorFor2(command, process.stdout.isTTY));
|
|
1963
|
+
const result = await runGenerate(paths, {
|
|
1964
|
+
...documentOptions(opts),
|
|
1965
|
+
provider: opts.provider,
|
|
1966
|
+
model: opts.model,
|
|
1967
|
+
local: opts.local
|
|
1968
|
+
});
|
|
1969
|
+
if (result.targets === 0) {
|
|
1970
|
+
console.log("Nothing to generate \u2014 every command eval has a command.");
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
console.log(
|
|
1974
|
+
`Generated ${result.generatedPaths.length}/${result.targets} check script(s):`
|
|
1975
|
+
);
|
|
1976
|
+
for (const p of result.generatedPaths) console.log(` ${p}`);
|
|
1977
|
+
for (const r of result.refusals) console.log(pc.red(` ${r.message}`));
|
|
1978
|
+
if (result.generatedPaths.length < result.targets) {
|
|
1979
|
+
process.exitCode = 1;
|
|
1980
|
+
}
|
|
1981
|
+
} catch (e) {
|
|
1982
|
+
fail(e);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
);
|
|
1986
|
+
documentInputs(
|
|
1987
|
+
program.command("fill").description(
|
|
1988
|
+
"Propose frontmatter evals for pages with an LLM; writes proposals at or above the confidence threshold"
|
|
1989
|
+
),
|
|
1990
|
+
"fill"
|
|
1991
|
+
).option(
|
|
1992
|
+
"-f, --format <format>",
|
|
1993
|
+
`Output format: ${SUMMARY_FORMATS.join(" | ")}`,
|
|
1994
|
+
parseFormatArg("--format", SUMMARY_FORMATS),
|
|
1995
|
+
"pretty"
|
|
1996
|
+
).option("--dry-run", "Report proposals without writing frontmatter").option(
|
|
1997
|
+
"--confidence <n>",
|
|
1998
|
+
"Minimum confidence to write (0-1, default: config fill.confidenceThreshold)",
|
|
1999
|
+
parseFloatArg("--confidence")
|
|
2000
|
+
).option(
|
|
2001
|
+
"--max-turns <n>",
|
|
2002
|
+
"Stop after this many inference calls (a cached page costs none)",
|
|
2003
|
+
parseIntArg("--max-turns")
|
|
2004
|
+
).option("--no-cache", "Bypass the fill proposal cache").option(
|
|
2005
|
+
"--chunk-chars <n>",
|
|
2006
|
+
"Characters of page per fill call; longer pages are proposed in parts",
|
|
2007
|
+
parseIntArg("--chunk-chars")
|
|
2008
|
+
).option("--provider <name>", "Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp").option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).action(async (paths, opts, command) => {
|
|
2009
|
+
try {
|
|
2010
|
+
const confidence = opts.confidence;
|
|
2011
|
+
if (confidence !== void 0 && confidence > 1) {
|
|
2012
|
+
fail(new DocevalsError(`--confidence must be between 0 and 1, got ${confidence}`));
|
|
2013
|
+
}
|
|
2014
|
+
const report = await runFill(paths, {
|
|
2015
|
+
...documentOptions({
|
|
2016
|
+
config: opts.config,
|
|
2017
|
+
collection: opts.collection,
|
|
2018
|
+
exclude: opts.exclude
|
|
2019
|
+
}),
|
|
2020
|
+
dryRun: opts.dryRun,
|
|
2021
|
+
confidence,
|
|
2022
|
+
maxTurns: opts.maxTurns,
|
|
2023
|
+
noCache: opts.cache === false ? true : void 0,
|
|
2024
|
+
provider: opts.provider,
|
|
2025
|
+
model: opts.model,
|
|
2026
|
+
local: opts.local,
|
|
2027
|
+
// P1 (proposal 0047): offered only on a terminal, where there is
|
|
2028
|
+
// someone to answer. Off one the evals go to the page and the W1
|
|
2029
|
+
// line says so.
|
|
2030
|
+
confirm: terminalConfirm(),
|
|
2031
|
+
onNotice: notice,
|
|
2032
|
+
onRelocated: (result) => {
|
|
2033
|
+
const manifests = result.manifests.map((m) => m.file).join(", ");
|
|
2034
|
+
notice(
|
|
2035
|
+
`moved ${String(result.summary.moved)} value(s) into ${manifests}.`
|
|
2036
|
+
);
|
|
2037
|
+
}
|
|
2038
|
+
});
|
|
2039
|
+
console.log(
|
|
2040
|
+
renderFill(report, opts.format, {
|
|
2041
|
+
color: colorFor2(command, process.stdout.isTTY)
|
|
2042
|
+
})
|
|
2043
|
+
);
|
|
2044
|
+
process.exitCode = report.exitCode;
|
|
2045
|
+
} catch (e) {
|
|
2046
|
+
fail(e);
|
|
2047
|
+
}
|
|
2048
|
+
});
|
|
2049
|
+
documentInputs(
|
|
2050
|
+
program.command("promote").description(
|
|
2051
|
+
"Find ai-graded evals expressible as deterministic checks; --write converts them"
|
|
2052
|
+
),
|
|
2053
|
+
"read"
|
|
2054
|
+
).option("--write", "Apply promotions (write scripts and rewrite evals)").option("--provider <name>", "Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp").option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).action(
|
|
2055
|
+
async (paths, opts, command) => {
|
|
2056
|
+
try {
|
|
2057
|
+
const pc = palette(colorFor2(command, process.stdout.isTTY));
|
|
2058
|
+
const proposals = await runPromote(paths, {
|
|
2059
|
+
...documentOptions(opts),
|
|
2060
|
+
write: opts.write,
|
|
2061
|
+
provider: opts.provider,
|
|
2062
|
+
model: opts.model,
|
|
2063
|
+
local: opts.local
|
|
2064
|
+
});
|
|
2065
|
+
if (proposals.length === 0) {
|
|
2066
|
+
console.log("No ai-graded evals found.");
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
for (const p of proposals) {
|
|
2070
|
+
const tag = p.promotable ? p.applied ? pc.green("promoted") : pc.cyan("promotable") : pc.dim("keep-ai");
|
|
2071
|
+
const script = p.scriptPath ? pc.dim(` -> ${p.scriptPath}`) : "";
|
|
2072
|
+
console.log(`${tag} ${p.evalName} (${p.source}, ${p.file})${script}`);
|
|
2073
|
+
console.log(pc.dim(` ${p.rationale}`));
|
|
2074
|
+
if (p.error !== void 0) console.log(pc.red(` ${p.error}`));
|
|
2075
|
+
}
|
|
2076
|
+
if (!opts.write && proposals.some((p) => p.promotable)) {
|
|
2077
|
+
console.log(pc.cyan("\nRe-run with --write to apply promotions."));
|
|
2078
|
+
}
|
|
2079
|
+
} catch (e) {
|
|
2080
|
+
fail(e);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
);
|
|
2084
|
+
program.command("calibrate").description(
|
|
2085
|
+
"Measure judge agreement against a human-verified golden set (.manni/docevals/golden/*.yaml)"
|
|
2086
|
+
).option("-c, --config <path>", "Path to manni.config.yaml").option("--golden <dir>", "Golden set directory", ".manni/docevals/golden").option(
|
|
2087
|
+
"--seed",
|
|
2088
|
+
"Write golden candidates from recorded reviews and exit; judges nothing, needs no provider"
|
|
2089
|
+
).option("--provider <name>", "Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp").option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).option("--runs <n>", "Ensemble runs per case", parseIntArg("--runs")).option(
|
|
2090
|
+
"--max-turns <n>",
|
|
2091
|
+
"Stop after this many ensemble runs (a cached ensemble costs none)",
|
|
2092
|
+
parseIntArg("--max-turns")
|
|
2093
|
+
).option("--no-cache", "Bypass the judge response cache").action(
|
|
2094
|
+
async (opts, command) => {
|
|
2095
|
+
try {
|
|
2096
|
+
const color = colorFor2(command, process.stdout.isTTY);
|
|
2097
|
+
const pc = palette(color);
|
|
2098
|
+
if (opts.seed) {
|
|
2099
|
+
const seeded = seedGoldenCases({ golden: opts.golden });
|
|
2100
|
+
if (seeded.total === 0) {
|
|
2101
|
+
console.log(
|
|
2102
|
+
"No recorded reviews to seed from \u2014 run `manni docevals review <file> <eval> <pass|fail>` first."
|
|
2103
|
+
);
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
const changed = seeded.added + seeded.updated;
|
|
2107
|
+
console.log(
|
|
2108
|
+
changed === 0 ? `No new reviews to seed \u2014 ${seeded.total} golden case(s) already in ${seeded.path}.` : `Wrote ${changed} golden candidate(s) to ${seeded.path} (${seeded.added} new, ${seeded.updated} updated; ${seeded.total} total).`
|
|
2109
|
+
);
|
|
2110
|
+
if (seeded.unreviewed > 0) {
|
|
2111
|
+
console.log(
|
|
2112
|
+
pc.yellow(
|
|
2113
|
+
`${seeded.unreviewed} case(s) are \`reviewed: false\`. Read them and set \`reviewed: true\` \u2014 a golden set assembled without a human is not one.`
|
|
2114
|
+
)
|
|
2115
|
+
);
|
|
2116
|
+
}
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
const report = await runCalibrate({
|
|
2120
|
+
config: opts.config,
|
|
2121
|
+
golden: opts.golden,
|
|
2122
|
+
provider: opts.provider,
|
|
2123
|
+
model: opts.model,
|
|
2124
|
+
local: opts.local,
|
|
2125
|
+
runs: opts.runs,
|
|
2126
|
+
maxTurns: opts.maxTurns,
|
|
2127
|
+
noCache: opts.cache === false
|
|
2128
|
+
});
|
|
2129
|
+
console.log(renderCalibration(report, { color }));
|
|
2130
|
+
process.exitCode = report.meetsThreshold && report.unjudged === 0 && report.balanced ? 0 : 1;
|
|
2131
|
+
} catch (e) {
|
|
2132
|
+
fail(e);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
);
|
|
2136
|
+
program.command("init").description("Create a starter manni.config.yaml in the current directory").action(() => {
|
|
2137
|
+
try {
|
|
2138
|
+
console.log(`Created ${runInit()}`);
|
|
2139
|
+
} catch (e) {
|
|
2140
|
+
fail(e);
|
|
2141
|
+
}
|
|
2142
|
+
});
|
|
2143
|
+
program.command("review").description(
|
|
2144
|
+
"Record a human verdict for an eval in the human-review zone (or list recorded reviews)"
|
|
2145
|
+
).argument("[file]", "Page path").argument("[eval]", "Eval name").argument("[verdict]", "pass | fail").option("--reviewer <name>", "Reviewer name recorded with the verdict").option("--note <text>", "Optional note").action(
|
|
2146
|
+
(file, evalName, verdict, opts, command) => {
|
|
2147
|
+
try {
|
|
2148
|
+
if (!file) {
|
|
2149
|
+
console.log(
|
|
2150
|
+
renderReviews(listReviews(), {
|
|
2151
|
+
color: colorFor2(command, process.stdout.isTTY)
|
|
2152
|
+
})
|
|
2153
|
+
);
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
2156
|
+
if (!evalName || !verdict) {
|
|
2157
|
+
throw new DocevalsError(
|
|
2158
|
+
"Usage: manni docevals review <file> <eval> <pass|fail>"
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
2161
|
+
const entry = runReview(file, evalName, verdict, opts);
|
|
2162
|
+
console.log(
|
|
2163
|
+
`Recorded ${entry.verdict} for ${entry.evalName} on ${entry.file}`
|
|
2164
|
+
);
|
|
2165
|
+
} catch (e) {
|
|
2166
|
+
fail(e);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
);
|
|
2170
|
+
return program;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
// src/key/cli.ts
|
|
2174
|
+
import { Command as Command4 } from "commander";
|
|
1588
2175
|
|
|
1589
2176
|
// src/key/reporters/json.ts
|
|
1590
2177
|
function renderRotateJson(result) {
|
|
@@ -1681,8 +2268,8 @@ var ROTATE_FORMATS = ["pretty", "json"];
|
|
|
1681
2268
|
function isRotateFormat(value) {
|
|
1682
2269
|
return ROTATE_FORMATS.includes(value);
|
|
1683
2270
|
}
|
|
1684
|
-
function
|
|
1685
|
-
const program = new
|
|
2271
|
+
function buildProgram5() {
|
|
2272
|
+
const program = new Command4();
|
|
1686
2273
|
program.name("key").description("Set and rotate the family key that encrypted values are encrypted with.").version(package_default.version, "-V, --version").option("--no-color", "disable colored output").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
1687
2274
|
const color = () => shouldColor({ noColor: program.opts().color === false, isTTY: process.stdout.isTTY });
|
|
1688
2275
|
program.command("set").description("Write the family encryption key to manni.config.yaml; generate one when no value is given").argument("[value]", "the key, at least 32 hex or base64url characters (default: 64 random hex)").option(
|
|
@@ -1765,8 +2352,616 @@ function buildProgram4() {
|
|
|
1765
2352
|
return program;
|
|
1766
2353
|
}
|
|
1767
2354
|
|
|
2355
|
+
// src/graph/cli.ts
|
|
2356
|
+
import { Command as Command5 } from "commander";
|
|
2357
|
+
|
|
2358
|
+
// src/graph/reporters/index.ts
|
|
2359
|
+
var GRAPH_FORMATS = ["pretty", "json"];
|
|
2360
|
+
var GRAPH_FORMAT_LIST = GRAPH_FORMATS.join(" | ");
|
|
2361
|
+
var CHECK_FORMATS = [...GRAPH_FORMATS, "github"];
|
|
2362
|
+
var CHECK_FORMAT_LIST = CHECK_FORMATS.join(" | ");
|
|
2363
|
+
|
|
2364
|
+
// src/graph/commands/check.ts
|
|
2365
|
+
import { resolve } from "node:path";
|
|
2366
|
+
|
|
2367
|
+
// src/graph/reporters/github.ts
|
|
2368
|
+
function renderCheckGithub2(report) {
|
|
2369
|
+
const lines = [];
|
|
2370
|
+
for (const f of report.findings) {
|
|
2371
|
+
const file = f.docs[0];
|
|
2372
|
+
const where2 = file === void 0 ? "" : ` file=${escapeWorkflowCommandProperty(file)}`;
|
|
2373
|
+
const message = escapeWorkflowCommandMessage(
|
|
2374
|
+
`${f.message} (${compactIri(f.focusNode)})`
|
|
2375
|
+
);
|
|
2376
|
+
lines.push(`::${f.severity}${where2}::${message}`);
|
|
2377
|
+
}
|
|
2378
|
+
return lines.join("\n");
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// src/graph/commands/check.ts
|
|
2382
|
+
async function runCheck3(opts = {}) {
|
|
2383
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
2384
|
+
const config = loadRunConfig(
|
|
2385
|
+
{
|
|
2386
|
+
...opts.config === void 0 ? {} : { configPath: opts.config },
|
|
2387
|
+
...opts.noConfig === void 0 ? {} : { noConfig: opts.noConfig }
|
|
2388
|
+
},
|
|
2389
|
+
cwd
|
|
2390
|
+
);
|
|
2391
|
+
const store = loadGraph(resolve(cwd, opts.graph ?? config.out));
|
|
2392
|
+
const shapesInput = opts.shapes && opts.shapes.length > 0 ? opts.shapes : config.check.shapes;
|
|
2393
|
+
const shapes = shapesInput.length > 0 ? shapesInput.map((p) => resolve(cwd, p)) : [bundledShapesPath(import.meta.url)];
|
|
2394
|
+
const findings = await validateGraph(store, shapes);
|
|
2395
|
+
const count = (severity) => findings.filter((f) => f.severity === severity).length;
|
|
2396
|
+
const errors = count("error");
|
|
2397
|
+
return {
|
|
2398
|
+
findings,
|
|
2399
|
+
errors,
|
|
2400
|
+
warnings: count("warning"),
|
|
2401
|
+
notices: count("notice"),
|
|
2402
|
+
shapes,
|
|
2403
|
+
// Only an `error` fails the run; a warning and a notice are reported and
|
|
2404
|
+
// pass, as they were when they were called violations and warnings.
|
|
2405
|
+
exitCode: errors > 0 ? 1 : 0
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
function plural3(n, word) {
|
|
2409
|
+
return `${String(n)} ${word}${n === 1 ? "" : "s"}`;
|
|
2410
|
+
}
|
|
2411
|
+
function renderCheck(report, format) {
|
|
2412
|
+
if (format === "json") {
|
|
2413
|
+
const { exitCode: _exitCode, ...rest } = report;
|
|
2414
|
+
return JSON.stringify(rest, null, 2);
|
|
2415
|
+
}
|
|
2416
|
+
if (format === "github") return renderCheckGithub2(report);
|
|
2417
|
+
const lines = [];
|
|
2418
|
+
for (const f of report.findings) {
|
|
2419
|
+
const where2 = f.docs.length > 0 ? ` [${f.docs.join(", ")}]` : "";
|
|
2420
|
+
lines.push(
|
|
2421
|
+
`${f.severity}: ${compactIri(f.focusNode)}${f.path ? ` ${compactIri(f.path)}` : ""} \u2014 ${f.message}${where2}`
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
if (report.findings.length > 0) lines.push("");
|
|
2425
|
+
lines.push(
|
|
2426
|
+
`${plural3(report.errors, "error")}, ${plural3(report.warnings, "warning")}, ${plural3(report.notices, "notice")}`
|
|
2427
|
+
);
|
|
2428
|
+
return lines.join("\n");
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// src/graph/commands/init.ts
|
|
2432
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2433
|
+
import { resolve as resolve2 } from "node:path";
|
|
2434
|
+
import { parse as parseYaml } from "yaml";
|
|
2435
|
+
var HEADER = `# manni.config.yaml \u2014 shared configuration for the manni family of tools.
|
|
2436
|
+
# Each tool reads its own top-level key; manni graph reads "graph:".
|
|
2437
|
+
`;
|
|
2438
|
+
var STARTER = `# Base IRI for every minted node. Set this to a namespace you control;
|
|
2439
|
+
# without it, IRIs fall back to the urn:manni:graph: placeholder.
|
|
2440
|
+
# baseIri: https://example.com/graph/
|
|
2441
|
+
|
|
2442
|
+
# There is no document set here. \`manni graph build\` and \`manni graph fill\` read the
|
|
2443
|
+
# paths you name, or the collections declared once for every tool at the TOP
|
|
2444
|
+
# level of this file, beside \`graph:\` rather than inside it:
|
|
2445
|
+
#
|
|
2446
|
+
# collections:
|
|
2447
|
+
# - name: site
|
|
2448
|
+
# paths: ["docs/**/*.md"]
|
|
2449
|
+
#
|
|
2450
|
+
# Output of \`manni graph build\`.
|
|
2451
|
+
out: graph/graph.ttl
|
|
2452
|
+
|
|
2453
|
+
# Map published-site routes back to source files so route-style links
|
|
2454
|
+
# (/docs/actions/find) become graph edges. A route may also carry the BCP-47
|
|
2455
|
+
# \`language\` labelling every document under its root; a page's own \`lang\`
|
|
2456
|
+
# frontmatter wins. Uncomment and adjust:
|
|
2457
|
+
# routes:
|
|
2458
|
+
# - basePath: /docs
|
|
2459
|
+
# root: docs
|
|
2460
|
+
# extensions: [.md, .mdx, .markdown]
|
|
2461
|
+
# indexFiles: [index, README]
|
|
2462
|
+
# - basePath: /docs/de
|
|
2463
|
+
# root: docs/de
|
|
2464
|
+
# language: de
|
|
2465
|
+
|
|
2466
|
+
# What to derive triples from. Remove entries to opt out.
|
|
2467
|
+
build:
|
|
2468
|
+
derive: [frontmatter, sections, links, tags, images, code, provenance]
|
|
2469
|
+
|
|
2470
|
+
# PROV-O settings. Git history is detected, not switched: per-file dates and
|
|
2471
|
+
# author agents, rename -> prov:wasRevisionOf, and the build activity's HEAD
|
|
2472
|
+
# committer date are derived wherever git can run over a repository, and a run
|
|
2473
|
+
# that cannot says so once and builds the rest. Deterministic per commit;
|
|
2474
|
+
# wall-clock time never enters the graph.
|
|
2475
|
+
# qualified: emit prov:qualifiedAttribution/qualifiedAssociation nodes
|
|
2476
|
+
# with roles alongside the direct properties.
|
|
2477
|
+
provenance:
|
|
2478
|
+
qualified: true
|
|
2479
|
+
|
|
2480
|
+
# Which fields a published graph may carry is read from x-manni-graph-output in
|
|
2481
|
+
# the schema set \`manni meta validate\` resolves for each page. Set that under
|
|
2482
|
+
# meta: (schemas, strict, register), not here. The default set keeps the
|
|
2483
|
+
# stewardship people fields (owner, stakeholders, reviewed-by) out.
|
|
2484
|
+
|
|
2485
|
+
# SHACL shapes \`manni graph check\` validates the built graph against. Default:
|
|
2486
|
+
# the shapes contract bundled with manni (shapes/graph/shapes-1.0.0.ttl).
|
|
2487
|
+
# check:
|
|
2488
|
+
# shapes: ["./my-shapes.ttl"]
|
|
2489
|
+
|
|
2490
|
+
# Metadata coverage gate for \`manni graph stats --check\`. A number applies to
|
|
2491
|
+
# every measured field; a map gates named fields only. Unset gates nothing.
|
|
2492
|
+
# stats:
|
|
2493
|
+
# coverageThreshold:
|
|
2494
|
+
# title: 100
|
|
2495
|
+
# description: 50
|
|
2496
|
+
|
|
2497
|
+
# Which provider \`manni graph fill\` sends pages to. Unset, the family's
|
|
2498
|
+
# top-level providers: map decides, and then \`auto\`, which detects one: an
|
|
2499
|
+
# Anthropic key, then an OpenAI key, then the Claude CLI, then a local model.
|
|
2500
|
+
# Connection settings (apiKeyEnv, baseUrl, command) live in that map, declared
|
|
2501
|
+
# once for every manni tool. \`--provider\` wins over this, \`--local\` over both.
|
|
2502
|
+
# provider: anthropic # auto | anthropic | openai | claude-cli | llama-cpp
|
|
2503
|
+
# model: claude-sonnet-4-5 # the provider's default when omitted
|
|
2504
|
+
|
|
2505
|
+
# LLM settings for \`manni graph fill\` (SKOS frontmatter proposals).
|
|
2506
|
+
fill:
|
|
2507
|
+
temperature: 0
|
|
2508
|
+
# Stop after this many inference calls. One page is one turn, and a cached
|
|
2509
|
+
# page spends none. Unset is unbounded.
|
|
2510
|
+
# maxTurns: 50
|
|
2511
|
+
cacheDir: .manni/graph/cache
|
|
2512
|
+
# fill proposes every field; confidence (0..1 per field) gates what is
|
|
2513
|
+
# written. Fields scored below the threshold are reported, not written.
|
|
2514
|
+
confidenceThreshold: 0.7
|
|
2515
|
+
# fields: defaults to every fillable field \u2014 uncomment to restrict.
|
|
2516
|
+
# Record meta-provenance (model + /graph/ pointers + confidence) on filled docs.
|
|
2517
|
+
writeProvenance: true
|
|
2518
|
+
# Reject proposals that would violate the SHACL shapes contract
|
|
2519
|
+
# (broader/narrower cycles, conflicting labels).
|
|
2520
|
+
validateGraph: true
|
|
2521
|
+
# Also propose per-section metadata. Off by default: it costs more output per
|
|
2522
|
+
# call, and section metadata is explicit-only, so review it as carefully.
|
|
2523
|
+
sections: false
|
|
2524
|
+
|
|
2525
|
+
# Local embeddings for semantic search (\`manni graph embed\`). Needs the optional
|
|
2526
|
+
# peer: npm install @huggingface/transformers
|
|
2527
|
+
# embed:
|
|
2528
|
+
# model: onnx-community/granite-embedding-small-english-r2-ONNX
|
|
2529
|
+
# dtype: q8 # q8 keeps embedding reproducible across platforms
|
|
2530
|
+
# out: graph # directory for the per-language vector sidecars
|
|
2531
|
+
# cacheDir: .manni/graph/embed-cache
|
|
2532
|
+
|
|
2533
|
+
# Optional enrichment for \`manni graph export iirds\` (the iiRDS package).
|
|
2534
|
+
# Absent, a minimal valid package is still produced.
|
|
2535
|
+
# export:
|
|
2536
|
+
# iirds:
|
|
2537
|
+
# title: My Docs # package title (default: "manni graph export")
|
|
2538
|
+
# creator: Acme Corp # Creator iirds:Party + vcard:Organization
|
|
2539
|
+
# version: "1.3" # iiRDS version literal: "1.2" | "1.3"
|
|
2540
|
+
`;
|
|
2541
|
+
function starterSection() {
|
|
2542
|
+
const body = STARTER.split("\n").map((line) => line === "" ? "" : ` ${line}`).join("\n");
|
|
2543
|
+
return `${CONFIG_SECTION}:
|
|
2544
|
+
${body}`;
|
|
2545
|
+
}
|
|
2546
|
+
async function runInit2(cwd = process.cwd()) {
|
|
2547
|
+
const path = resolve2(cwd, DEFAULT_CONFIG_FILENAME);
|
|
2548
|
+
if (!existsSync(path)) {
|
|
2549
|
+
await writeTextAtomic(path, `${HEADER}${starterSection()}`);
|
|
2550
|
+
return path;
|
|
2551
|
+
}
|
|
2552
|
+
const text2 = readFileSync(path, "utf8");
|
|
2553
|
+
let doc;
|
|
2554
|
+
try {
|
|
2555
|
+
doc = parseYaml(text2);
|
|
2556
|
+
} catch (e) {
|
|
2557
|
+
throw new GraphError(
|
|
2558
|
+
`Invalid YAML in ${DEFAULT_CONFIG_FILENAME}: ${errorMessage(e)}`
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
if (doc != null && (typeof doc !== "object" || Array.isArray(doc))) {
|
|
2562
|
+
throw new GraphError(
|
|
2563
|
+
`${DEFAULT_CONFIG_FILENAME}: top level must be a mapping \u2014 not adding a \`${CONFIG_SECTION}:\` section to it.`
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
if (doc != null && Object.hasOwn(doc, CONFIG_SECTION)) {
|
|
2567
|
+
throw new GraphError(
|
|
2568
|
+
`${DEFAULT_CONFIG_FILENAME} already has a \`${CONFIG_SECTION}:\` section \u2014 not overwriting.`
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
const separator = text2 === "" || text2.endsWith("\n") ? "" : "\n";
|
|
2572
|
+
const blank = text2 === "" ? "" : "\n";
|
|
2573
|
+
await writeTextAtomic(
|
|
2574
|
+
path,
|
|
2575
|
+
`${text2}${separator}${blank}${starterSection()}`
|
|
2576
|
+
);
|
|
2577
|
+
return path;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
// src/graph/cli.ts
|
|
2581
|
+
function buildProgram6() {
|
|
2582
|
+
const program = new Command5();
|
|
2583
|
+
program.name("graph").description(
|
|
2584
|
+
"Deterministic knowledge graphs derived from documentation frontmatter and formatting."
|
|
2585
|
+
).version(package_default.version).option("--no-color", "disable colored output").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
2586
|
+
function numericOption(flag, { min, max, integer }) {
|
|
2587
|
+
return (raw) => {
|
|
2588
|
+
const value = Number.parseFloat(raw);
|
|
2589
|
+
if (!Number.isFinite(value)) {
|
|
2590
|
+
throw new GraphError(`${flag} expects a number, got "${raw}".`);
|
|
2591
|
+
}
|
|
2592
|
+
if (integer && !Number.isInteger(value)) {
|
|
2593
|
+
throw new GraphError(`${flag} expects a whole number, got ${value}.`);
|
|
2594
|
+
}
|
|
2595
|
+
if (value < min || max !== void 0 && value > max) {
|
|
2596
|
+
const range = max === void 0 ? `>= ${min}` : `${min}..${max}`;
|
|
2597
|
+
throw new GraphError(`${flag} must be ${range}, got ${value}.`);
|
|
2598
|
+
}
|
|
2599
|
+
return value;
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
function countOption(flag, min = 1) {
|
|
2603
|
+
return numericOption(flag, { min, integer: true });
|
|
2604
|
+
}
|
|
2605
|
+
function enumOption(flag, allowed) {
|
|
2606
|
+
return (raw) => {
|
|
2607
|
+
if (!allowed.includes(raw)) {
|
|
2608
|
+
throw new GraphError(
|
|
2609
|
+
`${flag} must be one of ${allowed.join(" | ")}, got "${raw}".`
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
return raw;
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
function choiceOption(flag, allowed) {
|
|
2616
|
+
return (raw) => {
|
|
2617
|
+
if (!allowed.includes(raw)) {
|
|
2618
|
+
throw new GraphError(`Unknown ${flag} "${raw}". Use ${allowed.join(" | ")}.`);
|
|
2619
|
+
}
|
|
2620
|
+
return raw;
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
const formatOption = choiceOption("--format", GRAPH_FORMATS);
|
|
2624
|
+
const checkFormatOption = choiceOption("--format", CHECK_FORMATS);
|
|
2625
|
+
const fieldsOption = (raw) => splitList(raw).map((value) => enumOption("--fields", ALL_FILL_FIELDS)(value));
|
|
2626
|
+
function documentInputs(command, verb) {
|
|
2627
|
+
return command.argument(
|
|
2628
|
+
"[paths...]",
|
|
2629
|
+
`files, directories, or globs to ${verb} (use - for stdin)`
|
|
2630
|
+
).option(
|
|
2631
|
+
"--collection <name>",
|
|
2632
|
+
"Configured collection to read (repeatable)",
|
|
2633
|
+
collect,
|
|
2634
|
+
[]
|
|
2635
|
+
).option("--exclude <glob>", "Glob to exclude (repeatable)", collect, []).option("--as <format>", "force an input format (markdown, mdx)").option(
|
|
2636
|
+
"--ext <list>",
|
|
2637
|
+
"comma-separated extensions for directory walks",
|
|
2638
|
+
DEFAULT_EXTENSIONS.join(",")
|
|
2639
|
+
).option("--allow-empty", "treat zero matched files as success").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file");
|
|
2640
|
+
}
|
|
2641
|
+
async function inputOptions2(paths, opts) {
|
|
2642
|
+
assertInputFormat(paths, opts.as);
|
|
2643
|
+
return {
|
|
2644
|
+
...opts.as === void 0 ? {} : { as: opts.as },
|
|
2645
|
+
ext: splitList(opts.ext),
|
|
2646
|
+
...opts.allowEmpty === true ? { allowEmpty: true } : {},
|
|
2647
|
+
...paths.includes(STDIN) ? { stdinContent: await readStdin() } : {}
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
function configInputs(command) {
|
|
2651
|
+
return command.option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file");
|
|
2652
|
+
}
|
|
2653
|
+
function rest(opts) {
|
|
2654
|
+
const { config, ...others } = opts;
|
|
2655
|
+
return others;
|
|
2656
|
+
}
|
|
2657
|
+
function documentOptions(opts) {
|
|
2658
|
+
const { configPath, noConfig } = configOption(opts.config);
|
|
2659
|
+
return {
|
|
2660
|
+
...configPath === void 0 ? {} : { config: configPath },
|
|
2661
|
+
...noConfig === true ? { noConfig } : {},
|
|
2662
|
+
...opts.collection === void 0 ? {} : { collection: opts.collection },
|
|
2663
|
+
...opts.exclude === void 0 ? {} : { exclude: opts.exclude }
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
program.command("init").description("Add a starter `graph:` section to manni.config.yaml in the current directory").action(async () => {
|
|
2667
|
+
try {
|
|
2668
|
+
console.log(`Created ${await runInit2()}`);
|
|
2669
|
+
} catch (e) {
|
|
2670
|
+
fail(e);
|
|
2671
|
+
}
|
|
2672
|
+
});
|
|
2673
|
+
documentInputs(
|
|
2674
|
+
program.command("build").description("Derive the knowledge graph and write deterministic Turtle"),
|
|
2675
|
+
"build"
|
|
2676
|
+
).option("-o, --out <path>", "Output .ttl path (default: config out)").action(async (paths, opts) => {
|
|
2677
|
+
try {
|
|
2678
|
+
const result = await runBuild({
|
|
2679
|
+
paths,
|
|
2680
|
+
...documentOptions(opts),
|
|
2681
|
+
...await inputOptions2(paths, opts),
|
|
2682
|
+
out: opts.out
|
|
2683
|
+
});
|
|
2684
|
+
for (const warning of result.warnings) warn(warning);
|
|
2685
|
+
console.log(
|
|
2686
|
+
`Wrote ${result.outPath} (${result.docs} docs, ${result.quads} triples)`
|
|
2687
|
+
);
|
|
2688
|
+
} catch (e) {
|
|
2689
|
+
fail(e);
|
|
2690
|
+
}
|
|
2691
|
+
});
|
|
2692
|
+
configInputs(
|
|
2693
|
+
program.command("check").description(
|
|
2694
|
+
"Validate the built graph against the bundled SHACL shapes (errors exit 1)"
|
|
2695
|
+
)
|
|
2696
|
+
).option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2697
|
+
"--shapes <path>",
|
|
2698
|
+
"Shapes .ttl file; repeatable (default: config check.shapes, then bundled)",
|
|
2699
|
+
collect,
|
|
2700
|
+
[]
|
|
2701
|
+
).option(
|
|
2702
|
+
"-f, --format <format>",
|
|
2703
|
+
`Output: ${CHECK_FORMAT_LIST}`,
|
|
2704
|
+
checkFormatOption,
|
|
2705
|
+
"pretty"
|
|
2706
|
+
).action(
|
|
2707
|
+
async (opts) => {
|
|
2708
|
+
try {
|
|
2709
|
+
const report = await runCheck3({ ...rest(opts), ...documentOptions(opts) });
|
|
2710
|
+
console.log(renderCheck(report, opts.format));
|
|
2711
|
+
process.exitCode = report.exitCode;
|
|
2712
|
+
} catch (e) {
|
|
2713
|
+
fail(e);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
);
|
|
2717
|
+
documentInputs(
|
|
2718
|
+
program.command("fill").description(
|
|
2719
|
+
"Propose `graph:` frontmatter fields with an LLM, gated by confidence, and write them back"
|
|
2720
|
+
),
|
|
2721
|
+
"fill"
|
|
2722
|
+
).option(
|
|
2723
|
+
"-f, --format <format>",
|
|
2724
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2725
|
+
formatOption,
|
|
2726
|
+
"pretty"
|
|
2727
|
+
).option("--dry-run", "Report proposals without writing files").option("--force", "Overwrite human-set graph fields").option("--no-cache", "Bypass the proposal cache").option("--no-validate-graph", "Skip the SHACL graph guardrail on proposals").option("--sections", "Also propose per-section metadata").option(
|
|
2728
|
+
"--max-turns <n>",
|
|
2729
|
+
"Stop after this many inference calls (a cached page costs none)",
|
|
2730
|
+
countOption("--max-turns")
|
|
2731
|
+
).option(
|
|
2732
|
+
"--confidence <n>",
|
|
2733
|
+
"Minimum model confidence (0..1) to write a field (default: config, 0.7)",
|
|
2734
|
+
numericOption("--confidence", { min: 0, max: 1 })
|
|
2735
|
+
).option(
|
|
2736
|
+
// Comma-separated and given once: one separator per list (proposal 0034).
|
|
2737
|
+
"--fields <list>",
|
|
2738
|
+
`Fields to propose (comma-separated; default: config, then every field)`,
|
|
2739
|
+
fieldsOption
|
|
2740
|
+
).option(
|
|
2741
|
+
"--provider <name>",
|
|
2742
|
+
"Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp"
|
|
2743
|
+
).option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).action(async (paths, opts) => {
|
|
2744
|
+
try {
|
|
2745
|
+
const report = await runFill2({
|
|
2746
|
+
paths,
|
|
2747
|
+
...documentOptions(opts),
|
|
2748
|
+
...await inputOptions2(paths, opts),
|
|
2749
|
+
dryRun: opts.dryRun,
|
|
2750
|
+
force: opts.force,
|
|
2751
|
+
noCache: opts.cache === false,
|
|
2752
|
+
noValidateGraph: opts.validateGraph === false,
|
|
2753
|
+
sections: opts.sections,
|
|
2754
|
+
maxTurns: opts.maxTurns,
|
|
2755
|
+
confidence: opts.confidence,
|
|
2756
|
+
fields: opts.fields,
|
|
2757
|
+
provider: opts.provider,
|
|
2758
|
+
model: opts.model,
|
|
2759
|
+
local: opts.local
|
|
2760
|
+
});
|
|
2761
|
+
for (const warning of report.warnings) warn(warning);
|
|
2762
|
+
const rendered2 = renderFill2(report, opts.format);
|
|
2763
|
+
if (paths.includes(STDIN)) {
|
|
2764
|
+
if (report.stdinDocument !== void 0) {
|
|
2765
|
+
process.stdout.write(report.stdinDocument);
|
|
2766
|
+
}
|
|
2767
|
+
if (rendered2.length > 0) process.stderr.write(`${rendered2}
|
|
2768
|
+
`);
|
|
2769
|
+
} else {
|
|
2770
|
+
console.log(rendered2);
|
|
2771
|
+
}
|
|
2772
|
+
process.exitCode = report.exitCode;
|
|
2773
|
+
} catch (e) {
|
|
2774
|
+
fail(e);
|
|
2775
|
+
}
|
|
2776
|
+
});
|
|
2777
|
+
program.command("query").description(
|
|
2778
|
+
"Match triple patterns against the built graph (omit a term for wildcard)"
|
|
2779
|
+
).option("--subject <term>", "Subject IRI or prefixed name").option("--predicate <term>", "Predicate IRI or prefixed name").option("--object <term>", "Object IRI, prefixed name, or literal value").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2780
|
+
"-f, --format <format>",
|
|
2781
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2782
|
+
formatOption,
|
|
2783
|
+
"pretty"
|
|
2784
|
+
).action(
|
|
2785
|
+
(opts) => {
|
|
2786
|
+
try {
|
|
2787
|
+
const { subject, predicate, object, ...others } = rest(opts);
|
|
2788
|
+
const result = runQuery({
|
|
2789
|
+
...others,
|
|
2790
|
+
...documentOptions(opts),
|
|
2791
|
+
...subject === void 0 ? {} : { s: subject },
|
|
2792
|
+
...predicate === void 0 ? {} : { p: predicate },
|
|
2793
|
+
...object === void 0 ? {} : { o: object }
|
|
2794
|
+
});
|
|
2795
|
+
console.log(renderQuery(result, opts.format));
|
|
2796
|
+
} catch (e) {
|
|
2797
|
+
fail(e);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
);
|
|
2801
|
+
program.command("stats").description(
|
|
2802
|
+
"Summarize the built graph: counts, orphans, broken links, hubs, metadata coverage"
|
|
2803
|
+
).option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2804
|
+
"-f, --format <format>",
|
|
2805
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2806
|
+
formatOption,
|
|
2807
|
+
"pretty"
|
|
2808
|
+
).option(
|
|
2809
|
+
"--check",
|
|
2810
|
+
"Exit 1 when broken internal links exist or coverage is below threshold"
|
|
2811
|
+
).option(
|
|
2812
|
+
"--top <n>",
|
|
2813
|
+
"How many most-connected docs to list",
|
|
2814
|
+
countOption("--top")
|
|
2815
|
+
).option(
|
|
2816
|
+
"--coverage-threshold <pct>",
|
|
2817
|
+
"Minimum metadata coverage % (all fields); overrides config for this run",
|
|
2818
|
+
// A percentage, so 0..100 rather than the 0..1 of a confidence. NaN here is
|
|
2819
|
+
// worse than a wrong number: `pct < NaN` is false for every field, so the
|
|
2820
|
+
// gate this flag exists to tighten would silently pass instead.
|
|
2821
|
+
numericOption("--coverage-threshold", { min: 0, max: 100 })
|
|
2822
|
+
).action(
|
|
2823
|
+
(opts) => {
|
|
2824
|
+
try {
|
|
2825
|
+
const report = runStats({ ...rest(opts), ...documentOptions(opts) });
|
|
2826
|
+
console.log(renderStats(report, opts.format));
|
|
2827
|
+
process.exitCode = report.exitCode;
|
|
2828
|
+
} catch (e) {
|
|
2829
|
+
fail(e);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
);
|
|
2833
|
+
program.command("search").description("Rank graph nodes for a text query (needs `export search`)").argument("<query>", "Text query").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2834
|
+
"-i, --index <dir>",
|
|
2835
|
+
"Directory holding the indexes and manifest (default: beside the graph)"
|
|
2836
|
+
).option(
|
|
2837
|
+
"--lang <tag>",
|
|
2838
|
+
"Which localization to search (required when the corpus has more than one)"
|
|
2839
|
+
).option("--limit <n>", "Maximum results (default 10)", countOption("--limit")).option(
|
|
2840
|
+
"--vectors <path>",
|
|
2841
|
+
"Vector sidecar path (default: from the manifest)"
|
|
2842
|
+
).option(
|
|
2843
|
+
"--mode <mode>",
|
|
2844
|
+
`Which legs to run: ${SEARCH_MODES.join(" | ")} (default: hybrid when vectors exist)`,
|
|
2845
|
+
choiceOption("--mode", SEARCH_MODES)
|
|
2846
|
+
).option(
|
|
2847
|
+
"-f, --format <format>",
|
|
2848
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2849
|
+
formatOption,
|
|
2850
|
+
"pretty"
|
|
2851
|
+
).action(
|
|
2852
|
+
async (query, opts) => {
|
|
2853
|
+
try {
|
|
2854
|
+
const report = await runSearch({
|
|
2855
|
+
...rest(opts),
|
|
2856
|
+
...documentOptions(opts),
|
|
2857
|
+
query
|
|
2858
|
+
});
|
|
2859
|
+
console.log(renderSearch(report, opts.format));
|
|
2860
|
+
} catch (e) {
|
|
2861
|
+
fail(e);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
);
|
|
2865
|
+
program.command("traverse").description(
|
|
2866
|
+
"Walk the graph from a node, honoring scope rules, with the full trace"
|
|
2867
|
+
).argument("<node>", "Starting node: a full IRI or a prefix:local CURIE").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2868
|
+
"-d, --depth <n>",
|
|
2869
|
+
"Maximum hops from the node (default 1; 3 under --impact)",
|
|
2870
|
+
// Zero is allowed: it means the node itself, which is a real answer.
|
|
2871
|
+
countOption("--depth", 0)
|
|
2872
|
+
).option(
|
|
2873
|
+
"--predicates <list>",
|
|
2874
|
+
"Only follow these predicates (comma-separated)"
|
|
2875
|
+
).option("--reverse", "Follow inbound edges (who points at this node)").option("--impact", "Transitive inbound reach: what a change here affects").option(
|
|
2876
|
+
"--variant <variant>",
|
|
2877
|
+
"Scope filter: product variant IRI, title, or slug"
|
|
2878
|
+
).option("--software-subject <subject>", "Scope filter: software subject").option("--lang <tag>", "Scope filter: BCP-47 language tag, matched in any case").option("--limit <n>", "Stop after this many nodes", countOption("--limit")).option(
|
|
2879
|
+
"-f, --format <format>",
|
|
2880
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2881
|
+
formatOption,
|
|
2882
|
+
"pretty"
|
|
2883
|
+
).action(
|
|
2884
|
+
(node, opts) => {
|
|
2885
|
+
try {
|
|
2886
|
+
const { softwareSubject, ...others } = rest(opts);
|
|
2887
|
+
const report = runTraverse({
|
|
2888
|
+
...others,
|
|
2889
|
+
...documentOptions(opts),
|
|
2890
|
+
...softwareSubject === void 0 ? {} : { subject: softwareSubject },
|
|
2891
|
+
node,
|
|
2892
|
+
predicates: opts.predicates === void 0 ? void 0 : splitList(opts.predicates)
|
|
2893
|
+
});
|
|
2894
|
+
console.log(renderTraverse(report, opts.format));
|
|
2895
|
+
} catch (e) {
|
|
2896
|
+
fail(e);
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
);
|
|
2900
|
+
program.command("embed").description(
|
|
2901
|
+
"Compute local embeddings for the search index (needs @huggingface/transformers)"
|
|
2902
|
+
).option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2903
|
+
"-i, --index <dir>",
|
|
2904
|
+
"Directory holding the indexes and manifest (default: beside the graph)"
|
|
2905
|
+
).option(
|
|
2906
|
+
"-o, --out <dir>",
|
|
2907
|
+
"Directory for the sidecars (default: the index directory)"
|
|
2908
|
+
).option(
|
|
2909
|
+
"--embedding-model <id>",
|
|
2910
|
+
"Embedding model id (any id; `mock` for offline runs)"
|
|
2911
|
+
).option("--dtype <dtype>", "Weight quantization (default q8)").option("--no-cache", "Ignore the vector cache").option(
|
|
2912
|
+
"-f, --format <format>",
|
|
2913
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2914
|
+
formatOption,
|
|
2915
|
+
"pretty"
|
|
2916
|
+
).action(
|
|
2917
|
+
async (opts) => {
|
|
2918
|
+
try {
|
|
2919
|
+
const { embeddingModel, ...others } = rest(opts);
|
|
2920
|
+
const report = await runEmbed({
|
|
2921
|
+
...others,
|
|
2922
|
+
...documentOptions(opts),
|
|
2923
|
+
...embeddingModel === void 0 ? {} : { model: embeddingModel },
|
|
2924
|
+
noCache: opts.cache === false
|
|
2925
|
+
});
|
|
2926
|
+
console.log(renderEmbed(report, opts.format));
|
|
2927
|
+
} catch (e) {
|
|
2928
|
+
fail(e);
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
);
|
|
2932
|
+
program.command("export").description(
|
|
2933
|
+
"Reserialize the built graph into a consumer format (jsonld file, iirds package, or search index)"
|
|
2934
|
+
).argument("<target>", `What to write: ${EXPORT_TARGETS.join(" | ")}`).option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2935
|
+
"-o, --out <path>",
|
|
2936
|
+
"Output path (default: the graph path with the target's extension)"
|
|
2937
|
+
).action(
|
|
2938
|
+
async (target, opts) => {
|
|
2939
|
+
try {
|
|
2940
|
+
if (!EXPORT_TARGETS.includes(target)) {
|
|
2941
|
+
throw new GraphError(
|
|
2942
|
+
`Unknown export target "${target}". Use ${EXPORT_TARGETS.join(" | ")}.`
|
|
2943
|
+
);
|
|
2944
|
+
}
|
|
2945
|
+
const result = await runExport({
|
|
2946
|
+
...documentOptions(opts),
|
|
2947
|
+
graph: opts.graph,
|
|
2948
|
+
format: target,
|
|
2949
|
+
out: opts.out
|
|
2950
|
+
});
|
|
2951
|
+
for (const warning of result.warnings) warn(warning);
|
|
2952
|
+
console.log(
|
|
2953
|
+
`Wrote ${result.nodes} node${result.nodes === 1 ? "" : "s"} to ${result.outPath}`
|
|
2954
|
+
);
|
|
2955
|
+
} catch (e) {
|
|
2956
|
+
fail(e);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
);
|
|
2960
|
+
return program;
|
|
2961
|
+
}
|
|
2962
|
+
|
|
1768
2963
|
// src/lint/cli.ts
|
|
1769
|
-
import { Command as
|
|
2964
|
+
import { Command as Command6 } from "commander";
|
|
1770
2965
|
var LIST_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
|
|
1771
2966
|
var TOOL_OPTIONS_GROUP = "Tool options:";
|
|
1772
2967
|
function resolveColor2(program) {
|
|
@@ -1844,8 +3039,8 @@ function inputOptions(paths, options) {
|
|
|
1844
3039
|
onNotice: notice
|
|
1845
3040
|
};
|
|
1846
3041
|
}
|
|
1847
|
-
function
|
|
1848
|
-
const program = new
|
|
3042
|
+
function buildProgram7() {
|
|
3043
|
+
const program = new Command6();
|
|
1849
3044
|
program.name("lint").description(
|
|
1850
3045
|
"Validate document structure against doctype templates. Deterministic, CI-friendly."
|
|
1851
3046
|
).version(package_default.version, "-V, --version").option("--no-color", "disable colored output").showHelpAfterError().exitOverride();
|
|
@@ -1870,7 +3065,7 @@ function buildProgram5() {
|
|
|
1870
3065
|
});
|
|
1871
3066
|
const color = resolveColor2(command.parent ?? command);
|
|
1872
3067
|
const chosen = explain ? "explain" : format;
|
|
1873
|
-
const text2 =
|
|
3068
|
+
const text2 = render2(run, chosen, { color });
|
|
1874
3069
|
if (text2.length > 0 || !omittedWhenClean(chosen)) {
|
|
1875
3070
|
process.stdout.write(`${text2}
|
|
1876
3071
|
`);
|
|
@@ -2044,13 +3239,13 @@ function buildProgram5() {
|
|
|
2044
3239
|
}
|
|
2045
3240
|
|
|
2046
3241
|
// src/term/cli.ts
|
|
2047
|
-
import { Command as
|
|
3242
|
+
import { Command as Command7 } from "commander";
|
|
2048
3243
|
|
|
2049
3244
|
// src/term/commands/check.ts
|
|
2050
|
-
import { existsSync as
|
|
3245
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2051
3246
|
|
|
2052
3247
|
// src/term/core/config.ts
|
|
2053
|
-
import { resolve } from "node:path";
|
|
3248
|
+
import { resolve as resolve3 } from "node:path";
|
|
2054
3249
|
|
|
2055
3250
|
// src/term/errors.ts
|
|
2056
3251
|
var TermError = class extends ToolError {
|
|
@@ -2230,11 +3425,11 @@ function toLoaded(file) {
|
|
|
2230
3425
|
tools: file.tools
|
|
2231
3426
|
};
|
|
2232
3427
|
}
|
|
2233
|
-
var
|
|
3428
|
+
var STDIN2 = "-";
|
|
2234
3429
|
async function resolveTermRun(opts) {
|
|
2235
|
-
const cwd =
|
|
3430
|
+
const cwd = resolve3(opts.cwd);
|
|
2236
3431
|
const wanted = opts.collection ?? [];
|
|
2237
|
-
if (wanted.length > 0 && opts.inputs.some((input) => input !==
|
|
3432
|
+
if (wanted.length > 0 && opts.inputs.some((input) => input !== STDIN2)) {
|
|
2238
3433
|
throw new TermError(
|
|
2239
3434
|
"--collection selects a configured collection; it cannot be combined with paths."
|
|
2240
3435
|
);
|
|
@@ -2249,7 +3444,7 @@ async function resolveTermRun(opts) {
|
|
|
2249
3444
|
const fromCollections = opts.inputs.length === 0;
|
|
2250
3445
|
const inputs = fromCollections ? collections.flatMap((c) => c.paths) : opts.inputs;
|
|
2251
3446
|
const base = fromCollections && inputs.length > 0 && loaded ? loaded.dir : cwd;
|
|
2252
|
-
const manifests = loaded ? (loaded.config.manifests ?? []).map((written) => ({ path:
|
|
3447
|
+
const manifests = loaded ? (loaded.config.manifests ?? []).map((written) => ({ path: resolve3(loaded.dir, written), written })) : [];
|
|
2253
3448
|
return {
|
|
2254
3449
|
config: loaded ? loaded.config : null,
|
|
2255
3450
|
inputs,
|
|
@@ -2566,12 +3761,12 @@ function compareFindings2(a, b) {
|
|
|
2566
3761
|
}
|
|
2567
3762
|
|
|
2568
3763
|
// src/term/commands/run.ts
|
|
2569
|
-
import { existsSync } from "node:fs";
|
|
2570
|
-
import { isAbsolute, relative, resolve as
|
|
3764
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
3765
|
+
import { isAbsolute, relative, resolve as resolve5 } from "node:path";
|
|
2571
3766
|
|
|
2572
3767
|
// src/term/core/load-set.ts
|
|
2573
3768
|
import { readFile } from "node:fs/promises";
|
|
2574
|
-
import { extname, resolve as
|
|
3769
|
+
import { extname, resolve as resolve4 } from "node:path";
|
|
2575
3770
|
|
|
2576
3771
|
// src/term/core/writers/markup.ts
|
|
2577
3772
|
function xmlText(value) {
|
|
@@ -4153,7 +5348,7 @@ async function loadTermSet(opts) {
|
|
|
4153
5348
|
});
|
|
4154
5349
|
const namedFiles = new Set(named);
|
|
4155
5350
|
for (const file of files) {
|
|
4156
|
-
const path =
|
|
5351
|
+
const path = resolve4(run.base, file);
|
|
4157
5352
|
if (namedFiles.has(file) && MANIFEST_EXTENSIONS.has(extname(file).toLowerCase())) {
|
|
4158
5353
|
let content2;
|
|
4159
5354
|
try {
|
|
@@ -4204,7 +5399,7 @@ async function loadTermSet(opts) {
|
|
|
4204
5399
|
|
|
4205
5400
|
// src/term/commands/run.ts
|
|
4206
5401
|
async function loadTerms(opts) {
|
|
4207
|
-
const cwd =
|
|
5402
|
+
const cwd = resolve5(opts.cwd ?? process.cwd());
|
|
4208
5403
|
const run = await resolveTermRun({
|
|
4209
5404
|
cwd,
|
|
4210
5405
|
inputs: opts.inputs,
|
|
@@ -4250,18 +5445,18 @@ function displayPath(path, cwd) {
|
|
|
4250
5445
|
function valeConfigFor(run) {
|
|
4251
5446
|
if (run.configDir === void 0) return void 0;
|
|
4252
5447
|
const path = valeConfigPath(run.tools, run.configDir);
|
|
4253
|
-
if (path !== void 0 && !
|
|
5448
|
+
if (path !== void 0 && !existsSync2(path)) {
|
|
4254
5449
|
const source = run.configSource ?? "manni.config.yaml";
|
|
4255
5450
|
throw new TermError(`${source}: tools.vale.config "${run.tools.vale?.config ?? path}" does not exist.`);
|
|
4256
5451
|
}
|
|
4257
5452
|
return path;
|
|
4258
5453
|
}
|
|
4259
|
-
function
|
|
5454
|
+
function plural4(n, one, many = `${one}s`) {
|
|
4260
5455
|
return `${String(n)} ${n === 1 ? one : many}`;
|
|
4261
5456
|
}
|
|
4262
5457
|
|
|
4263
5458
|
// src/term/commands/check.ts
|
|
4264
|
-
async function
|
|
5459
|
+
async function runCheck4(opts) {
|
|
4265
5460
|
const { cwd, run, set } = await loadTerms(opts);
|
|
4266
5461
|
const config = run.config;
|
|
4267
5462
|
const findings = checkTermSet(set, {
|
|
@@ -4277,7 +5472,7 @@ async function runCheck3(opts) {
|
|
|
4277
5472
|
cwd,
|
|
4278
5473
|
{ current: DEFAULT_TERM_BASELINE_PATH }
|
|
4279
5474
|
);
|
|
4280
|
-
if (request !== null && !
|
|
5475
|
+
if (request !== null && !existsSync3(request.absPath)) {
|
|
4281
5476
|
if (opts.baseline !== true) {
|
|
4282
5477
|
throw new TermError(
|
|
4283
5478
|
`Baseline "${request.label}" not found. Record one with \`manni term check --baseline\`.`
|
|
@@ -4303,7 +5498,7 @@ async function runCheck3(opts) {
|
|
|
4303
5498
|
import { join } from "node:path";
|
|
4304
5499
|
|
|
4305
5500
|
// src/term/core/writers/page.ts
|
|
4306
|
-
import { parse as
|
|
5501
|
+
import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
|
|
4307
5502
|
function pageMetadata(term) {
|
|
4308
5503
|
const { record } = term;
|
|
4309
5504
|
const metadata = { title: record.label };
|
|
@@ -4330,7 +5525,7 @@ ${stringifyYaml2(metadata, { lineWidth: 0 })}---
|
|
|
4330
5525
|
function metaContent(value) {
|
|
4331
5526
|
if (typeof value === "string" && !value.includes("\n")) {
|
|
4332
5527
|
try {
|
|
4333
|
-
if (
|
|
5528
|
+
if (parseYaml2(value) === value) return value;
|
|
4334
5529
|
} catch {
|
|
4335
5530
|
}
|
|
4336
5531
|
}
|
|
@@ -4661,7 +5856,7 @@ var SCHEME_ID = "";
|
|
|
4661
5856
|
function literal(term, value) {
|
|
4662
5857
|
return term.language === void 0 ? value : { "@value": value, "@language": term.language };
|
|
4663
5858
|
}
|
|
4664
|
-
function
|
|
5859
|
+
function resolve6(terms, self, value) {
|
|
4665
5860
|
const wanted = value.toLowerCase();
|
|
4666
5861
|
const others = terms.filter((t) => t !== self);
|
|
4667
5862
|
return others.find((t) => t.record.label.toLowerCase() === wanted) ?? others.find((t) => t.id.toLowerCase() === wanted);
|
|
@@ -4669,7 +5864,7 @@ function resolve4(terms, self, value) {
|
|
|
4669
5864
|
function references(terms, self, values) {
|
|
4670
5865
|
const ids = [];
|
|
4671
5866
|
for (const value of values ?? []) {
|
|
4672
|
-
const target =
|
|
5867
|
+
const target = resolve6(terms, self, value);
|
|
4673
5868
|
if (target !== void 0 && !ids.includes(target.id)) ids.push(target.id);
|
|
4674
5869
|
}
|
|
4675
5870
|
return ids.map((id) => ({ "@id": `#${id}` }));
|
|
@@ -4995,9 +6190,9 @@ var INTERCHANGE_LABELS = {
|
|
|
4995
6190
|
};
|
|
4996
6191
|
function rendered(writers, format, construct) {
|
|
4997
6192
|
return writers.some((writer) => {
|
|
4998
|
-
const
|
|
4999
|
-
if (
|
|
5000
|
-
return writer.shapes.some((shape) =>
|
|
6193
|
+
const render4 = DOCUMENT_RENDERS[writer.format];
|
|
6194
|
+
if (render4?.input !== format) return false;
|
|
6195
|
+
return writer.shapes.some((shape) => render4.shapes[shape] === construct);
|
|
5001
6196
|
});
|
|
5002
6197
|
}
|
|
5003
6198
|
function listFormats(opts = {}) {
|
|
@@ -5066,7 +6261,7 @@ async function runGet(opts) {
|
|
|
5066
6261
|
set.terms.flatMap((t) => [t.id, t.record.label])
|
|
5067
6262
|
);
|
|
5068
6263
|
const hint = nearest === void 0 ? "." : `; did you mean "${nearest}"?`;
|
|
5069
|
-
throw new TermError(`no term "${opts.term}". ${
|
|
6264
|
+
throw new TermError(`no term "${opts.term}". ${plural4(set.terms.length, "term")}${hint}`);
|
|
5070
6265
|
}
|
|
5071
6266
|
|
|
5072
6267
|
// src/term/core/lint.ts
|
|
@@ -5313,15 +6508,15 @@ async function runLint2(opts) {
|
|
|
5313
6508
|
|
|
5314
6509
|
// src/term/commands/list.ts
|
|
5315
6510
|
var LIST_FORMATS2 = ["pretty", "json", "csv"];
|
|
5316
|
-
async function
|
|
6511
|
+
async function runList2(opts) {
|
|
5317
6512
|
const { set } = await loadTerms(opts);
|
|
5318
6513
|
return { terms: set.terms, set };
|
|
5319
6514
|
}
|
|
5320
6515
|
|
|
5321
6516
|
// src/term/commands/write.ts
|
|
5322
|
-
import { existsSync as
|
|
6517
|
+
import { existsSync as existsSync4, statSync } from "node:fs";
|
|
5323
6518
|
import { lstat, mkdir, readdir, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
5324
|
-
import { dirname, extname as extname2, join as join4, resolve as
|
|
6519
|
+
import { dirname, extname as extname2, join as join4, resolve as resolve7 } from "node:path";
|
|
5325
6520
|
var SUGGESTED_SECTION = "*.md";
|
|
5326
6521
|
var STYLE2 = "Terms";
|
|
5327
6522
|
function writeFormats(writers = TERM_WRITERS) {
|
|
@@ -5355,7 +6550,7 @@ async function existingFor(target) {
|
|
|
5355
6550
|
const existing = /* @__PURE__ */ new Map();
|
|
5356
6551
|
if (target.shape === "directory") {
|
|
5357
6552
|
await filesUnder(target.path, existing);
|
|
5358
|
-
} else if (
|
|
6553
|
+
} else if (existsSync4(target.path) && !isDirectory(target.path)) {
|
|
5359
6554
|
existing.set(target.path, await readFile2(target.path, "utf8"));
|
|
5360
6555
|
}
|
|
5361
6556
|
return existing;
|
|
@@ -5375,7 +6570,7 @@ function droppedFrom(format, shape, readers) {
|
|
|
5375
6570
|
async function applyChanges(files, removals, existing, cwd, write) {
|
|
5376
6571
|
const changes = [];
|
|
5377
6572
|
for (const file of files) {
|
|
5378
|
-
const before = existing.get(file.path) ?? (
|
|
6573
|
+
const before = existing.get(file.path) ?? (existsSync4(file.path) ? await readFile2(file.path, "utf8") : void 0);
|
|
5379
6574
|
if (before === file.content) continue;
|
|
5380
6575
|
changes.push({ path: displayPath(file.path, cwd), action: before === void 0 ? "create" : "change" });
|
|
5381
6576
|
if (write) {
|
|
@@ -5384,14 +6579,14 @@ async function applyChanges(files, removals, existing, cwd, write) {
|
|
|
5384
6579
|
}
|
|
5385
6580
|
}
|
|
5386
6581
|
for (const path of removals) {
|
|
5387
|
-
if (!
|
|
6582
|
+
if (!existsSync4(path)) continue;
|
|
5388
6583
|
changes.push({ path: displayPath(path, cwd), action: "remove" });
|
|
5389
6584
|
if (write) await rm2(path, { force: true });
|
|
5390
6585
|
}
|
|
5391
6586
|
return changes;
|
|
5392
6587
|
}
|
|
5393
6588
|
async function runWrite(opts) {
|
|
5394
|
-
const cwd =
|
|
6589
|
+
const cwd = resolve7(opts.cwd ?? process.cwd());
|
|
5395
6590
|
const writers = opts.writers ?? TERM_WRITERS;
|
|
5396
6591
|
const readers = opts.readers ?? TERM_READERS;
|
|
5397
6592
|
const check = opts.check === true;
|
|
@@ -5419,31 +6614,31 @@ async function runWrite(opts) {
|
|
|
5419
6614
|
"Vale found no config file. Set tools.vale.config in manni.config.yaml, or pass -o <styles directory>."
|
|
5420
6615
|
);
|
|
5421
6616
|
}
|
|
5422
|
-
targetPath =
|
|
6617
|
+
targetPath = resolve7(cwd, resolved.stylesPath);
|
|
5423
6618
|
if (!Object.values(resolved.baseStyles).some((styles) => styles.includes(STYLE2))) {
|
|
5424
6619
|
const first = Object.entries(resolved.baseStyles)[0];
|
|
5425
6620
|
const [section, styles] = first ?? [SUGGESTED_SECTION, []];
|
|
5426
6621
|
wiring = {
|
|
5427
|
-
rootIni: displayPath(
|
|
6622
|
+
rootIni: displayPath(resolve7(cwd, resolved.rootIni), cwd),
|
|
5428
6623
|
section,
|
|
5429
6624
|
styles: [...styles, STYLE2],
|
|
5430
6625
|
addSection: first === void 0
|
|
5431
6626
|
};
|
|
5432
6627
|
}
|
|
5433
6628
|
} else {
|
|
5434
|
-
targetPath =
|
|
6629
|
+
targetPath = resolve7(cwd, opts.out);
|
|
5435
6630
|
}
|
|
5436
6631
|
const shape = format === "vale" || /[\\/]$/.test(opts.out ?? "") || isDirectory(targetPath) ? "directory" : "file";
|
|
5437
6632
|
const target = { path: targetPath, shape };
|
|
5438
6633
|
const existing = await existingFor(target);
|
|
5439
|
-
let
|
|
6634
|
+
let render4;
|
|
5440
6635
|
try {
|
|
5441
|
-
|
|
6636
|
+
render4 = writer.render(set.terms, target, { existing });
|
|
5442
6637
|
} catch (error) {
|
|
5443
6638
|
if (error instanceof TermError) throw relativized(error, [...existing.keys(), targetPath], cwd);
|
|
5444
6639
|
throw error;
|
|
5445
6640
|
}
|
|
5446
|
-
const changes = await applyChanges(
|
|
6641
|
+
const changes = await applyChanges(render4.files, render4.removals, existing, cwd, !check && !dryRun);
|
|
5447
6642
|
const shown = format === "vale" ? displayPath(join4(targetPath, STYLE2), cwd) : opts.out ?? displayPath(targetPath, cwd);
|
|
5448
6643
|
return {
|
|
5449
6644
|
mode: "render",
|
|
@@ -5452,11 +6647,11 @@ async function runWrite(opts) {
|
|
|
5452
6647
|
target: shown,
|
|
5453
6648
|
terms: set.terms.length,
|
|
5454
6649
|
changes,
|
|
5455
|
-
files:
|
|
6650
|
+
files: render4.files.map((file) => ({ path: displayPath(file.path, cwd), content: file.content })),
|
|
5456
6651
|
// A Vale style holds designations only, by design; its dropped definitions are not news.
|
|
5457
|
-
dropped: format === "vale" ? [] :
|
|
6652
|
+
dropped: format === "vale" ? [] : render4.dropped,
|
|
5458
6653
|
droppedFrom: droppedFrom(format, shape, readers),
|
|
5459
|
-
skipped:
|
|
6654
|
+
skipped: render4.skipped,
|
|
5460
6655
|
check,
|
|
5461
6656
|
dryRun,
|
|
5462
6657
|
...wiring === void 0 ? {} : { wiring }
|
|
@@ -5692,7 +6887,7 @@ function renderListPretty(terms) {
|
|
|
5692
6887
|
const rows = terms.map(
|
|
5693
6888
|
(t) => `${t.id.padEnd(idWidth)}${t.record.label.padEnd(labelWidth)}${(t.record["alt-labels"] ?? []).join(", ")}`.trimEnd()
|
|
5694
6889
|
);
|
|
5695
|
-
return [...rows,
|
|
6890
|
+
return [...rows, plural4(terms.length, "term")].join("\n");
|
|
5696
6891
|
}
|
|
5697
6892
|
var GET_FIELDS = [
|
|
5698
6893
|
"alt-labels",
|
|
@@ -5733,21 +6928,21 @@ function severityText(c, severity) {
|
|
|
5733
6928
|
function counts(findings) {
|
|
5734
6929
|
const parts = [];
|
|
5735
6930
|
const n = (severity) => findings.filter((f) => f.severity === severity).length;
|
|
5736
|
-
if (n("error") > 0) parts.push(
|
|
5737
|
-
if (n("warning") > 0) parts.push(
|
|
5738
|
-
if (n("notice") > 0) parts.push(
|
|
6931
|
+
if (n("error") > 0) parts.push(plural4(n("error"), "error"));
|
|
6932
|
+
if (n("warning") > 0) parts.push(plural4(n("warning"), "warning"));
|
|
6933
|
+
if (n("notice") > 0) parts.push(plural4(n("notice"), "notice"));
|
|
5739
6934
|
return parts.join(", ");
|
|
5740
6935
|
}
|
|
5741
6936
|
function renderFindingsPretty(report, opts) {
|
|
5742
6937
|
const c = palette(opts.color);
|
|
5743
6938
|
const { baseline } = report;
|
|
5744
6939
|
if (baseline?.written === true) {
|
|
5745
|
-
return `${c.green("\u2713")} ${
|
|
6940
|
+
return `${c.green("\u2713")} ${plural4(report.found, "finding")} recorded in ${baseline.path}`;
|
|
5746
6941
|
}
|
|
5747
6942
|
const baselined = baseline !== void 0 && baseline.suppressed > 0 ? `, ${String(baseline.suppressed)} baselined` : "";
|
|
5748
|
-
const terms =
|
|
6943
|
+
const terms = plural4(report.terms, "term");
|
|
5749
6944
|
if (report.findings.length === 0) {
|
|
5750
|
-
const references2 = opts.references === false ? "" : `, ${
|
|
6945
|
+
const references2 = opts.references === false ? "" : `, ${plural4(report.references, "reference")}`;
|
|
5751
6946
|
return `${c.green("\u2713")} ${terms}${references2}, no findings${baselined}`;
|
|
5752
6947
|
}
|
|
5753
6948
|
const ruleWidth = Math.max(RULE_WIDTH, ...report.findings.map((f) => f.ruleId.length + 2));
|
|
@@ -5797,11 +6992,11 @@ function valeLines(report) {
|
|
|
5797
6992
|
const acronyms = [];
|
|
5798
6993
|
for (const file of report.files) {
|
|
5799
6994
|
const name = baseName2(file.path);
|
|
5800
|
-
if (CASING_RULES.has(name)) rows.push([name,
|
|
5801
|
-
else if (name === "Deprecated.yml") rows.push([name,
|
|
6995
|
+
if (CASING_RULES.has(name)) rows.push([name, plural4(swapCount(file.content), "label")]);
|
|
6996
|
+
else if (name === "Deprecated.yml") rows.push([name, plural4(swapCount(file.content), "swap")]);
|
|
5802
6997
|
else acronyms.push(name);
|
|
5803
6998
|
}
|
|
5804
|
-
if (acronyms.length > 0) rows.push([acronyms.join(", "),
|
|
6999
|
+
if (acronyms.length > 0) rows.push([acronyms.join(", "), plural4(acronyms.length, "acronym")]);
|
|
5805
7000
|
const width = Math.max(0, ...rows.map(([name]) => name.length)) + 2;
|
|
5806
7001
|
return rows.map(([name, what]) => ` ${name.padEnd(width)}${what}`);
|
|
5807
7002
|
}
|
|
@@ -5814,13 +7009,13 @@ function droppedLine(report) {
|
|
|
5814
7009
|
ids.set(field, set);
|
|
5815
7010
|
}
|
|
5816
7011
|
const fields = [...ids.entries()].map(([field, set]) => ({ field, n: set.size })).sort((a, b) => a.n - b.n || TERM_FIELDS.indexOf(a.field) - TERM_FIELDS.indexOf(b.field));
|
|
5817
|
-
const parts = fields.map(({ field, n }, i) => `${field} on ${i === 0 ?
|
|
7012
|
+
const parts = fields.map(({ field, n }, i) => `${field} on ${i === 0 ? plural4(n, "term") : String(n)}`);
|
|
5818
7013
|
return ` dropped from ${report.droppedFrom ?? report.format ?? "the render"}: ${parts.join(", ")}`;
|
|
5819
7014
|
}
|
|
5820
7015
|
function skippedLine(report) {
|
|
5821
7016
|
if (report.skipped.length === 0) return void 0;
|
|
5822
7017
|
const construct = report.droppedFrom ?? report.format ?? "the render";
|
|
5823
|
-
const count =
|
|
7018
|
+
const count = plural4(report.skipped.length, "term");
|
|
5824
7019
|
return ` skipped ${count} ${construct} cannot read without a definition: ${report.skipped.join(", ")}`;
|
|
5825
7020
|
}
|
|
5826
7021
|
function renderNotes(report) {
|
|
@@ -5838,11 +7033,11 @@ function renderWritePretty(report) {
|
|
|
5838
7033
|
return [...changes, ...renderNotes(report)].join("\n");
|
|
5839
7034
|
}
|
|
5840
7035
|
if (report.mode === "in-place") {
|
|
5841
|
-
return report.changes.length === 0 ? "Nothing to write" : `Wrote ${
|
|
7036
|
+
return report.changes.length === 0 ? "Nothing to write" : `Wrote ${plural4(report.changes.length, "file")}`;
|
|
5842
7037
|
}
|
|
5843
7038
|
const each = report.shape === "directory" && report.format !== "vale" ? ", one file each" : "";
|
|
5844
7039
|
const written = report.terms - report.skipped.length;
|
|
5845
|
-
const lines = [`Wrote ${
|
|
7040
|
+
const lines = [`Wrote ${plural4(written, "term")} to ${report.target ?? ""}${each}`];
|
|
5846
7041
|
if (report.format === "vale") lines.push(...valeLines(report));
|
|
5847
7042
|
lines.push(...renderNotes(report));
|
|
5848
7043
|
return lines.join("\n");
|
|
@@ -5877,7 +7072,7 @@ async function termLabels(request) {
|
|
|
5877
7072
|
|
|
5878
7073
|
// src/term/cli.ts
|
|
5879
7074
|
registerTermLabelSource(termLabels);
|
|
5880
|
-
function
|
|
7075
|
+
function colorFor3(options) {
|
|
5881
7076
|
return shouldColor({ noColor: !options.color, isTTY: process.stdout.isTTY });
|
|
5882
7077
|
}
|
|
5883
7078
|
async function coreOptions(paths, options) {
|
|
@@ -5905,7 +7100,7 @@ function renderFindings(report, format, options, references2) {
|
|
|
5905
7100
|
let text2;
|
|
5906
7101
|
switch (format) {
|
|
5907
7102
|
case "pretty":
|
|
5908
|
-
text2 = renderFindingsPretty(report, { color:
|
|
7103
|
+
text2 = renderFindingsPretty(report, { color: colorFor3(options), references: references2 });
|
|
5909
7104
|
break;
|
|
5910
7105
|
case "json":
|
|
5911
7106
|
text2 = renderFindingsJson(report);
|
|
@@ -5923,8 +7118,8 @@ function renderFindings(report, format, options, references2) {
|
|
|
5923
7118
|
if (text2.length > 0 || !OMITTED_WHEN_CLEAN.has(format)) print(text2);
|
|
5924
7119
|
process.exitCode = report.summary.failed > 0 ? 1 : 0;
|
|
5925
7120
|
}
|
|
5926
|
-
function
|
|
5927
|
-
const program = new
|
|
7121
|
+
function buildProgram8() {
|
|
7122
|
+
const program = new Command7();
|
|
5928
7123
|
program.name("term").description("Check, lint and render a docset's terms and the references into them.").version(package_default.version, "-V, --version").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
5929
7124
|
withInputs(
|
|
5930
7125
|
program.command("list").description("List the resolved entries").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
@@ -5940,7 +7135,7 @@ function buildProgram6() {
|
|
|
5940
7135
|
).action(async (paths, options) => {
|
|
5941
7136
|
try {
|
|
5942
7137
|
const format = formatChoice(options.format, LIST_FORMATS2);
|
|
5943
|
-
const { terms } = await
|
|
7138
|
+
const { terms } = await runList2(await coreOptions(paths, options));
|
|
5944
7139
|
print(format === "json" ? renderListJson(terms) : format === "csv" ? renderListCsv(terms) : renderListPretty(terms));
|
|
5945
7140
|
process.exitCode = 0;
|
|
5946
7141
|
} catch (err) {
|
|
@@ -5956,7 +7151,7 @@ function buildProgram6() {
|
|
|
5956
7151
|
try {
|
|
5957
7152
|
const format = formatChoice(options.format, GET_FORMATS);
|
|
5958
7153
|
const found = await runGet({ ...await coreOptions(paths, options), term });
|
|
5959
|
-
print(format === "json" ? renderGetJson(found) : renderGetPretty(found, { color:
|
|
7154
|
+
print(format === "json" ? renderGetJson(found) : renderGetPretty(found, { color: colorFor3(options) }));
|
|
5960
7155
|
process.exitCode = 0;
|
|
5961
7156
|
} catch (err) {
|
|
5962
7157
|
fail(err);
|
|
@@ -5976,7 +7171,7 @@ function buildProgram6() {
|
|
|
5976
7171
|
).action(async (paths, options) => {
|
|
5977
7172
|
try {
|
|
5978
7173
|
const format = formatChoice(options.format, FINDING_FORMATS);
|
|
5979
|
-
const report = await
|
|
7174
|
+
const report = await runCheck4({
|
|
5980
7175
|
...await coreOptions(paths, options),
|
|
5981
7176
|
...options.baseline === true ? { baseline: true } : {}
|
|
5982
7177
|
});
|
|
@@ -6042,12 +7237,12 @@ function buildProgram6() {
|
|
|
6042
7237
|
}
|
|
6043
7238
|
|
|
6044
7239
|
// src/cli.ts
|
|
6045
|
-
function
|
|
7240
|
+
function buildProgram9() {
|
|
6046
7241
|
const meta = buildProgram().name("meta").description(
|
|
6047
7242
|
"Validate, read, query and fill document metadata against JSON Schema"
|
|
6048
7243
|
);
|
|
6049
7244
|
const metaCommands = new Set(meta.commands.map((c) => c.name()));
|
|
6050
|
-
const program = new
|
|
7245
|
+
const program = new Command8();
|
|
6051
7246
|
program.name("manni").description("Tools for documentation that is meant to be checked.").version(package_default.version, "-V, --version").showHelpAfterError("(add --help for usage)").exitOverride().configureOutput({
|
|
6052
7247
|
outputError: (str, write) => {
|
|
6053
7248
|
write(str);
|
|
@@ -6073,20 +7268,30 @@ function buildProgram7() {
|
|
|
6073
7268
|
)
|
|
6074
7269
|
);
|
|
6075
7270
|
program.addCommand(
|
|
6076
|
-
|
|
7271
|
+
buildProgram7().name("lint").description(
|
|
6077
7272
|
"Validate document structure against doctype templates, routed by a page's type"
|
|
6078
7273
|
)
|
|
6079
7274
|
);
|
|
6080
7275
|
program.addCommand(
|
|
6081
|
-
|
|
7276
|
+
buildProgram4().name("docevals").description(
|
|
7277
|
+
"Deterministic and LLM-as-judge evals for documentation pages, driven by frontmatter"
|
|
7278
|
+
)
|
|
7279
|
+
);
|
|
7280
|
+
program.addCommand(
|
|
7281
|
+
buildProgram8().name("term").description("Check, lint and render a docset's terms and the references into them")
|
|
7282
|
+
);
|
|
7283
|
+
program.addCommand(
|
|
7284
|
+
buildProgram6().name("graph").description(
|
|
7285
|
+
"Deterministic knowledge graphs derived from documentation frontmatter and formatting"
|
|
7286
|
+
)
|
|
6082
7287
|
);
|
|
6083
7288
|
program.addCommand(
|
|
6084
|
-
|
|
7289
|
+
buildProgram5().name("key").description("Set and rotate the family key that encrypted values are encrypted with.")
|
|
6085
7290
|
);
|
|
6086
7291
|
return program;
|
|
6087
7292
|
}
|
|
6088
|
-
runIfMain(import.meta.url,
|
|
7293
|
+
runIfMain(import.meta.url, buildProgram9);
|
|
6089
7294
|
export {
|
|
6090
|
-
|
|
7295
|
+
buildProgram9 as buildProgram
|
|
6091
7296
|
};
|
|
6092
7297
|
//# sourceMappingURL=cli.js.map
|