@hawkeyexl/manni 4.1.0 → 4.3.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 +2 -2
- package/dist/batch-7JJWXWSF.js +18 -0
- package/dist/batch-7JJWXWSF.js.map +1 -0
- package/dist/calibrate-IA2ARFMH.js +13 -0
- package/dist/calibrate-IA2ARFMH.js.map +1 -0
- package/dist/capture-C32JXIFN.js +15 -0
- package/dist/capture-C32JXIFN.js.map +1 -0
- package/dist/chunk-3R23C5DT.js +120 -0
- package/dist/chunk-3R23C5DT.js.map +1 -0
- package/dist/chunk-CI2GOU3A.js +4447 -0
- package/dist/chunk-CI2GOU3A.js.map +1 -0
- package/dist/{chunk-I57KBF45.js → chunk-DP2ZA3WH.js} +1203 -142
- package/dist/chunk-DP2ZA3WH.js.map +1 -0
- package/dist/chunk-ESJ5W7EV.js +297 -0
- package/dist/chunk-ESJ5W7EV.js.map +1 -0
- package/dist/chunk-IOYXPBCL.js +37 -0
- package/dist/chunk-IOYXPBCL.js.map +1 -0
- package/dist/chunk-KTSJ2ZHA.js +40 -0
- package/dist/chunk-KTSJ2ZHA.js.map +1 -0
- package/dist/{chunk-JODRT4NF.js → chunk-MFDD3YGU.js} +8 -37
- package/dist/chunk-MFDD3YGU.js.map +1 -0
- package/dist/chunk-MOQXWXSG.js +1941 -0
- package/dist/chunk-MOQXWXSG.js.map +1 -0
- package/dist/{chunk-S3NDEIOW.js → chunk-O6RVQKK6.js} +10502 -3017
- package/dist/chunk-O6RVQKK6.js.map +1 -0
- package/dist/chunk-QROCFW5S.js +174 -0
- package/dist/chunk-QROCFW5S.js.map +1 -0
- package/dist/chunk-RVVCA4WL.js +379 -0
- package/dist/chunk-RVVCA4WL.js.map +1 -0
- package/dist/cli.js +1293 -140
- package/dist/cli.js.map +1 -1
- package/dist/docmeta.js +3 -2
- package/dist/docmeta.js.map +1 -1
- 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 +6149 -799
- package/dist/index.js +682 -7
- package/dist/index.js.map +1 -1
- package/dist/picker-6HRMETE7.js +12 -0
- package/dist/picker-6HRMETE7.js.map +1 -0
- package/package.json +32 -4
- package/plugin/tracevals/.claude-plugin/plugin.json +9 -0
- package/plugin/tracevals/hooks/hooks.json +17 -0
- package/shapes/graph/shapes-1.0.0.ttl +689 -0
- package/dist/chunk-I57KBF45.js.map +0 -1
- package/dist/chunk-JODRT4NF.js.map +0 -1
- package/dist/chunk-S3NDEIOW.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,37 +1,43 @@
|
|
|
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-JODRT4NF.js";
|
|
3
|
+
buildProgram
|
|
4
|
+
} from "./chunk-MFDD3YGU.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,
|
|
13
11
|
DocevalsError,
|
|
14
|
-
|
|
12
|
+
EXPORT_TARGETS,
|
|
13
|
+
GraphError,
|
|
15
14
|
INFER_FORMATS,
|
|
16
15
|
KeyError,
|
|
17
16
|
LINT_JOBS,
|
|
18
17
|
LintError,
|
|
19
18
|
REPORT_FORMATS as REPORT_FORMATS2,
|
|
20
19
|
RULE_ID_PREFIX,
|
|
20
|
+
SEARCH_MODES,
|
|
21
21
|
SEVERITIES,
|
|
22
22
|
SEVERITY_LIST,
|
|
23
|
+
STDIN,
|
|
23
24
|
SUMMARY_FORMATS,
|
|
24
25
|
TOOLS_BY_JOB,
|
|
25
26
|
asCiteError,
|
|
27
|
+
assertInputFormat,
|
|
28
|
+
bundledShapesPath,
|
|
26
29
|
codeEndAt,
|
|
27
30
|
codeRegions,
|
|
31
|
+
compactIri,
|
|
28
32
|
errorSite,
|
|
29
33
|
findingMessage,
|
|
30
34
|
isCiteRule,
|
|
31
35
|
isInferFormat,
|
|
32
36
|
isSeverity,
|
|
33
37
|
listReviews,
|
|
34
|
-
|
|
38
|
+
loadConfig3 as loadConfig,
|
|
39
|
+
loadGraph,
|
|
40
|
+
loadRunConfig,
|
|
35
41
|
meetsSeverity,
|
|
36
42
|
parseFormat,
|
|
37
43
|
rebaseConfig,
|
|
@@ -41,41 +47,71 @@ import {
|
|
|
41
47
|
renderCheckGithub,
|
|
42
48
|
renderCheckJson,
|
|
43
49
|
renderCheckPretty,
|
|
50
|
+
renderEmbed,
|
|
44
51
|
renderFill,
|
|
52
|
+
renderFill2,
|
|
45
53
|
renderList,
|
|
54
|
+
renderList2,
|
|
55
|
+
renderQuery,
|
|
46
56
|
renderRemoveJson,
|
|
47
57
|
renderRemovePretty,
|
|
48
58
|
renderReviews,
|
|
59
|
+
renderSearch,
|
|
60
|
+
renderStats,
|
|
49
61
|
renderTemplates,
|
|
50
62
|
renderTools,
|
|
63
|
+
renderTraverse,
|
|
51
64
|
renderUpdateJson,
|
|
52
65
|
renderUpdatePretty,
|
|
53
66
|
resolveStructureTool,
|
|
54
67
|
runAdd,
|
|
68
|
+
runBuild,
|
|
55
69
|
runCalibrate,
|
|
56
70
|
runCheck,
|
|
71
|
+
runEmbed,
|
|
72
|
+
runExport,
|
|
57
73
|
runFill,
|
|
74
|
+
runFill2,
|
|
75
|
+
runFill3,
|
|
58
76
|
runGenerate,
|
|
59
77
|
runInit,
|
|
60
78
|
runKeyRotate,
|
|
61
79
|
runKeySet,
|
|
62
80
|
runLint,
|
|
63
81
|
runList,
|
|
82
|
+
runList2,
|
|
64
83
|
runPromote,
|
|
84
|
+
runQuery,
|
|
65
85
|
runRemove,
|
|
66
86
|
runReview,
|
|
67
87
|
runRun,
|
|
88
|
+
runSearch,
|
|
89
|
+
runStats,
|
|
68
90
|
runTemplates,
|
|
69
91
|
runTemplatesInfer,
|
|
70
92
|
runTools,
|
|
93
|
+
runTraverse,
|
|
71
94
|
runUpdate,
|
|
72
95
|
seedGoldenCases,
|
|
73
96
|
shortCommit,
|
|
74
97
|
shortLine,
|
|
75
98
|
shortPin,
|
|
76
99
|
shortSrc,
|
|
77
|
-
spellSource
|
|
78
|
-
|
|
100
|
+
spellSource,
|
|
101
|
+
validateGraph
|
|
102
|
+
} from "./chunk-O6RVQKK6.js";
|
|
103
|
+
import {
|
|
104
|
+
CALIBRATE_FORMATS,
|
|
105
|
+
REPORT_FORMATS as REPORT_FORMATS3,
|
|
106
|
+
SUMMARY_FORMATS as SUMMARY_FORMATS2,
|
|
107
|
+
parseFormat as parseFormat2,
|
|
108
|
+
runRun as runRun2
|
|
109
|
+
} from "./chunk-CI2GOU3A.js";
|
|
110
|
+
import "./chunk-QROCFW5S.js";
|
|
111
|
+
import {
|
|
112
|
+
allowExecutionMessage,
|
|
113
|
+
isExecutionGrant
|
|
114
|
+
} from "./chunk-MOQXWXSG.js";
|
|
79
115
|
import {
|
|
80
116
|
COMMON_FORMAT_LIST,
|
|
81
117
|
CONCEPT_FIELDS,
|
|
@@ -87,11 +123,13 @@ import {
|
|
|
87
123
|
STDIN_LABEL,
|
|
88
124
|
STDIN_TOKEN,
|
|
89
125
|
TERM_RELATION_FIELDS,
|
|
90
|
-
ToolError,
|
|
91
126
|
assertNonEmpty,
|
|
92
|
-
|
|
127
|
+
collect,
|
|
128
|
+
colorFor,
|
|
129
|
+
configOption,
|
|
93
130
|
escapeWorkflowCommandMessage,
|
|
94
131
|
escapeWorkflowCommandProperty,
|
|
132
|
+
explicitFalse,
|
|
95
133
|
extractorByName,
|
|
96
134
|
extractorForExtension,
|
|
97
135
|
fail,
|
|
@@ -108,23 +146,35 @@ import {
|
|
|
108
146
|
palette,
|
|
109
147
|
programName,
|
|
110
148
|
readConfigFile,
|
|
149
|
+
readStdin,
|
|
111
150
|
registerTermLabelSource,
|
|
112
151
|
renderJunit,
|
|
113
152
|
renderSarif,
|
|
153
|
+
reportConfig,
|
|
114
154
|
resolveBaselineRequest,
|
|
115
155
|
resolveTargetSet,
|
|
116
156
|
runIfMain,
|
|
117
157
|
selectCollections,
|
|
118
158
|
settleBaseline,
|
|
119
159
|
shouldColor,
|
|
160
|
+
splitList,
|
|
120
161
|
splitPageArgument,
|
|
121
162
|
terminalConfirm,
|
|
122
163
|
valeConfigPath,
|
|
123
|
-
valueAt
|
|
124
|
-
|
|
164
|
+
valueAt,
|
|
165
|
+
warn,
|
|
166
|
+
writeTextAtomic
|
|
167
|
+
} from "./chunk-DP2ZA3WH.js";
|
|
168
|
+
import {
|
|
169
|
+
TracevalsError
|
|
170
|
+
} from "./chunk-3R23C5DT.js";
|
|
171
|
+
import {
|
|
172
|
+
ToolError,
|
|
173
|
+
errorMessage
|
|
174
|
+
} from "./chunk-IOYXPBCL.js";
|
|
125
175
|
|
|
126
176
|
// src/cli.ts
|
|
127
|
-
import { Command as
|
|
177
|
+
import { Command as Command9 } from "commander";
|
|
128
178
|
|
|
129
179
|
// src/a11y/cli.ts
|
|
130
180
|
import { Command } from "commander";
|
|
@@ -224,17 +274,19 @@ async function crawl(opts, analyzer) {
|
|
|
224
274
|
url = normalizeUrl(raw);
|
|
225
275
|
key = dedupeKey(url);
|
|
226
276
|
} catch {
|
|
227
|
-
return;
|
|
277
|
+
return { as: "invalid" };
|
|
228
278
|
}
|
|
229
|
-
if (source !== "seed" && !isPageLink(url)) return;
|
|
230
|
-
if (!opts.seeds.some((seed) => sameHost(seed, url))) return;
|
|
231
|
-
|
|
279
|
+
if (source !== "seed" && !isPageLink(url)) return { as: "asset" };
|
|
280
|
+
if (!opts.seeds.some((seed) => sameHost(seed, url))) return { as: "off-host" };
|
|
281
|
+
const pattern = opts.exclude?.match(url) ?? null;
|
|
282
|
+
if (pattern !== null) {
|
|
232
283
|
excluded.add(key);
|
|
233
|
-
return;
|
|
284
|
+
return { as: "excluded", pattern };
|
|
234
285
|
}
|
|
235
|
-
if (seen.has(key)) return;
|
|
286
|
+
if (seen.has(key)) return { as: "seen" };
|
|
236
287
|
seen.add(key);
|
|
237
288
|
frontier.push({ url, key, source });
|
|
289
|
+
return { as: "queued" };
|
|
238
290
|
};
|
|
239
291
|
const landed = (finalUrl) => {
|
|
240
292
|
if (finalUrl === void 0) return;
|
|
@@ -251,6 +303,7 @@ async function crawl(opts, analyzer) {
|
|
|
251
303
|
progress({ kind: "excluded", urls: excluded.size, patterns: opts.exclude?.patterns.length ?? 0 });
|
|
252
304
|
}
|
|
253
305
|
const pages = [];
|
|
306
|
+
const redirects = [];
|
|
254
307
|
let duplicates = 0;
|
|
255
308
|
const capped = () => opts.maxPages !== void 0 && pages.length >= opts.maxPages;
|
|
256
309
|
for (let next = 0; next < frontier.length && !capped(); next++) {
|
|
@@ -263,10 +316,28 @@ async function crawl(opts, analyzer) {
|
|
|
263
316
|
}
|
|
264
317
|
visited.add(key);
|
|
265
318
|
const index = pages.length + 1;
|
|
266
|
-
if (
|
|
319
|
+
if (pages.length + redirects.length === 0) progress({ kind: "browser" });
|
|
267
320
|
progress({ kind: "page", index, queued: frontier.length, url });
|
|
268
321
|
try {
|
|
269
322
|
const analyzed = await analyzer.analyze(url, opts.analyze);
|
|
323
|
+
if ("redirect" in analyzed) {
|
|
324
|
+
const to = normalized(analyzed.redirect);
|
|
325
|
+
redirects.push({ url, to, source });
|
|
326
|
+
progress({ kind: "redirected", index, url, to });
|
|
327
|
+
const outcome2 = enqueue(to, source);
|
|
328
|
+
if (source === "seed") {
|
|
329
|
+
if (!isHttpUrl(to)) {
|
|
330
|
+
throw new A11yError(`${url} redirects to ${to}, which is not an http(s) URL.`);
|
|
331
|
+
}
|
|
332
|
+
if (outcome2.as === "off-host") {
|
|
333
|
+
throw new A11yError(`${url} redirects to ${to}, which is on another host. Check ${to} instead.`);
|
|
334
|
+
}
|
|
335
|
+
if (outcome2.as === "excluded") {
|
|
336
|
+
throw new A11yError(`${outcome2.pattern.source} excludes ${to}, where the seed ${url} redirects.`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
270
341
|
pages.push({ ...analyzed.result, url, source });
|
|
271
342
|
landed(analyzed.finalUrl);
|
|
272
343
|
if (opts.crawl) for (const link of analyzed.links) enqueue(link, "link");
|
|
@@ -278,9 +349,23 @@ async function crawl(opts, analyzer) {
|
|
|
278
349
|
progress({ kind: "checked", index, url, violations: 0, error });
|
|
279
350
|
}
|
|
280
351
|
}
|
|
281
|
-
const skipped2 = frontier.length - pages.length - duplicates;
|
|
352
|
+
const skipped2 = frontier.length - pages.length - duplicates - redirects.length;
|
|
282
353
|
progress({ kind: "done", checked: pages.length, skipped: skipped2 });
|
|
283
|
-
return {
|
|
354
|
+
return {
|
|
355
|
+
pages,
|
|
356
|
+
discovered: frontier.length,
|
|
357
|
+
skipped: skipped2,
|
|
358
|
+
duplicates,
|
|
359
|
+
redirects,
|
|
360
|
+
excluded: excluded.size
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function normalized(url) {
|
|
364
|
+
try {
|
|
365
|
+
return normalizeUrl(url);
|
|
366
|
+
} catch {
|
|
367
|
+
return url;
|
|
368
|
+
}
|
|
284
369
|
}
|
|
285
370
|
|
|
286
371
|
// src/a11y/core/exclude.ts
|
|
@@ -522,6 +607,7 @@ async function runCheck2(opts, deps) {
|
|
|
522
607
|
const results = outcome2.pages.map((page) => finishPage(page, opts.severity));
|
|
523
608
|
return {
|
|
524
609
|
results,
|
|
610
|
+
redirects: outcome2.redirects,
|
|
525
611
|
summary: summarize(results, outcome2, sitemap, opts.crawl)
|
|
526
612
|
};
|
|
527
613
|
} finally {
|
|
@@ -535,7 +621,7 @@ function finishPage(page, floor) {
|
|
|
535
621
|
const score = applicable === 0 ? null : Math.round(100 * page.passes / applicable);
|
|
536
622
|
return { ...page, violations, score };
|
|
537
623
|
}
|
|
538
|
-
function summarize(results, { discovered, skipped: skipped2, duplicates, excluded }, sitemap, crawl2) {
|
|
624
|
+
function summarize(results, { discovered, skipped: skipped2, duplicates, redirects, excluded }, sitemap, crawl2) {
|
|
539
625
|
const bySeverity = { notice: 0, warning: 0, error: 0 };
|
|
540
626
|
let failed = 0;
|
|
541
627
|
let violations = 0;
|
|
@@ -551,6 +637,7 @@ function summarize(results, { discovered, skipped: skipped2, duplicates, exclude
|
|
|
551
637
|
checked: results.length,
|
|
552
638
|
skipped: skipped2,
|
|
553
639
|
duplicates,
|
|
640
|
+
redirects: redirects.length,
|
|
554
641
|
excluded,
|
|
555
642
|
failed,
|
|
556
643
|
violations,
|
|
@@ -595,33 +682,18 @@ function createPlaywrightAnalyzer() {
|
|
|
595
682
|
return {
|
|
596
683
|
async analyze(url, opts) {
|
|
597
684
|
const { page } = await open();
|
|
685
|
+
const navigations = [];
|
|
686
|
+
const onRequest = (request) => {
|
|
687
|
+
if (request.isNavigationRequest() && request.frame() === page.mainFrame() && request.redirectedFrom() === null) {
|
|
688
|
+
navigations.push(request);
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
page.on("request", onRequest);
|
|
598
692
|
try {
|
|
599
|
-
await page
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
const links = await page.$$eval(
|
|
604
|
-
"a[href]",
|
|
605
|
-
(anchors) => anchors.map((a) => a.href)
|
|
606
|
-
);
|
|
607
|
-
let builder = new AxeBuilder({ page });
|
|
608
|
-
if (opts.tags.length > 0) builder = builder.withTags(opts.tags);
|
|
609
|
-
let results;
|
|
610
|
-
try {
|
|
611
|
-
results = await builder.analyze();
|
|
612
|
-
} catch (err) {
|
|
613
|
-
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
693
|
+
return await analyzePage(page, url, opts, navigations);
|
|
694
|
+
} finally {
|
|
695
|
+
page.off("request", onRequest);
|
|
614
696
|
}
|
|
615
|
-
return {
|
|
616
|
-
result: {
|
|
617
|
-
url,
|
|
618
|
-
violations: results.violations.map(toViolation),
|
|
619
|
-
passes: results.passes.length,
|
|
620
|
-
incomplete: results.incomplete.length
|
|
621
|
-
},
|
|
622
|
-
links,
|
|
623
|
-
finalUrl: page.url()
|
|
624
|
-
};
|
|
625
697
|
},
|
|
626
698
|
async close() {
|
|
627
699
|
if (session === null) return;
|
|
@@ -635,6 +707,103 @@ function createPlaywrightAnalyzer() {
|
|
|
635
707
|
}
|
|
636
708
|
};
|
|
637
709
|
}
|
|
710
|
+
async function analyzePage(page, url, opts, navigations) {
|
|
711
|
+
let response;
|
|
712
|
+
try {
|
|
713
|
+
response = await page.goto(url, { waitUntil: "load", timeout: opts.timeout });
|
|
714
|
+
} catch (err) {
|
|
715
|
+
throw new A11yError(`Could not load ${url}: ${reason(err)}`);
|
|
716
|
+
}
|
|
717
|
+
const loaded = response?.url() ?? page.url();
|
|
718
|
+
const nextNavigation = () => {
|
|
719
|
+
let first = response?.request() ?? null;
|
|
720
|
+
for (let from = first?.redirectedFrom() ?? null; from !== null; from = from.redirectedFrom()) {
|
|
721
|
+
first = from;
|
|
722
|
+
}
|
|
723
|
+
const own = first === null ? 0 : navigations.indexOf(first);
|
|
724
|
+
return own === -1 ? void 0 : navigations[own + 1];
|
|
725
|
+
};
|
|
726
|
+
const destination = () => nextNavigation()?.url() ?? page.url();
|
|
727
|
+
const navigatedAway = async () => {
|
|
728
|
+
if (nextNavigation() === void 0 && samePage(page.url(), loaded)) return null;
|
|
729
|
+
try {
|
|
730
|
+
await page.waitForURL((u) => !samePage(u.href, loaded), {
|
|
731
|
+
waitUntil: "commit",
|
|
732
|
+
timeout: opts.timeout
|
|
733
|
+
});
|
|
734
|
+
return { redirect: destination() };
|
|
735
|
+
} catch {
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
let read8;
|
|
740
|
+
try {
|
|
741
|
+
read8 = await page.evaluate(readPage);
|
|
742
|
+
} catch (err) {
|
|
743
|
+
const moved = await navigatedAway();
|
|
744
|
+
if (moved !== null) return moved;
|
|
745
|
+
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
746
|
+
}
|
|
747
|
+
const refresh = refreshTarget(read8.refresh, page.url());
|
|
748
|
+
if (refresh !== null) return { redirect: refresh };
|
|
749
|
+
let builder = new AxeBuilder({ page });
|
|
750
|
+
if (opts.tags.length > 0) builder = builder.withTags(opts.tags);
|
|
751
|
+
let results;
|
|
752
|
+
try {
|
|
753
|
+
results = await builder.analyze();
|
|
754
|
+
} catch (err) {
|
|
755
|
+
const moved = await navigatedAway();
|
|
756
|
+
if (moved !== null) return moved;
|
|
757
|
+
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
758
|
+
}
|
|
759
|
+
if (!samePage(page.url(), loaded)) return { redirect: destination() };
|
|
760
|
+
return {
|
|
761
|
+
result: {
|
|
762
|
+
url,
|
|
763
|
+
violations: results.violations.map(toViolation),
|
|
764
|
+
passes: results.passes.length,
|
|
765
|
+
incomplete: results.incomplete.length
|
|
766
|
+
},
|
|
767
|
+
links: read8.links,
|
|
768
|
+
finalUrl: page.url()
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
function readPage() {
|
|
772
|
+
const doc = globalThis.document;
|
|
773
|
+
return {
|
|
774
|
+
links: Array.from(doc.querySelectorAll("a[href]"), (a) => a.href),
|
|
775
|
+
refresh: doc.querySelector('meta[http-equiv="refresh" i]')?.getAttribute("content") ?? null
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
function refreshUrl(content) {
|
|
779
|
+
const match = /^\s*[\d.]*\s*[;,]\s*(?:url\s*=\s*)?(.*)$/is.exec(content);
|
|
780
|
+
let target = match?.[1]?.trim() ?? "";
|
|
781
|
+
const quote = target[0];
|
|
782
|
+
if (quote === "'" || quote === '"') {
|
|
783
|
+
const end = target.indexOf(quote, 1);
|
|
784
|
+
target = (end === -1 ? target.slice(1) : target.slice(1, end)).trim();
|
|
785
|
+
}
|
|
786
|
+
return target === "" ? null : target;
|
|
787
|
+
}
|
|
788
|
+
function refreshTarget(content, base) {
|
|
789
|
+
if (content === null) return null;
|
|
790
|
+
const target = refreshUrl(content);
|
|
791
|
+
if (target === null) return null;
|
|
792
|
+
let to;
|
|
793
|
+
try {
|
|
794
|
+
to = new URL(target, base).href;
|
|
795
|
+
} catch {
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
return samePage(to, base) ? null : to;
|
|
799
|
+
}
|
|
800
|
+
function samePage(a, b) {
|
|
801
|
+
try {
|
|
802
|
+
return dedupeKey(a) === dedupeKey(b);
|
|
803
|
+
} catch {
|
|
804
|
+
return a === b;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
638
807
|
function severityOf(impact) {
|
|
639
808
|
switch (impact) {
|
|
640
809
|
case "critical":
|
|
@@ -819,8 +988,13 @@ function renderPretty(run, opts) {
|
|
|
819
988
|
);
|
|
820
989
|
for (const v of page.violations) lines.push(...violationLines(v, c));
|
|
821
990
|
}
|
|
991
|
+
if (!opts.quiet) {
|
|
992
|
+
for (const r of run.redirects) {
|
|
993
|
+
lines.push(`${c.dim("\u21AA")} ${c.cyan(r.url)} redirects to ${c.cyan(r.to)}`);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
822
996
|
const failed = summary.failed;
|
|
823
|
-
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` : "");
|
|
997
|
+
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` : "");
|
|
824
998
|
lines.push("");
|
|
825
999
|
lines.push(failed > 0 ? c.red(footer) : c.green(footer));
|
|
826
1000
|
return lines.join("\n");
|
|
@@ -939,6 +1113,9 @@ function terminalReporter(opts) {
|
|
|
939
1113
|
case "checked":
|
|
940
1114
|
showUrl(`${c.dim(counter(event.index, queued))} ${outcome(event, c)}`, event.url);
|
|
941
1115
|
return;
|
|
1116
|
+
case "redirected":
|
|
1117
|
+
showUrl(`${c.dim(counter(event.index, queued))} ${c.dim("\u21AA")} redirects to `, event.to);
|
|
1118
|
+
return;
|
|
942
1119
|
case "done":
|
|
943
1120
|
stream.write(CLEAR_LINE);
|
|
944
1121
|
return;
|
|
@@ -979,6 +1156,9 @@ function plainReporter(stream) {
|
|
|
979
1156
|
else say(`${head} \u2717 ${plural(event.violations, "violation")}`);
|
|
980
1157
|
return;
|
|
981
1158
|
}
|
|
1159
|
+
case "redirected":
|
|
1160
|
+
say(`${counter(event.index, queued)} \u21AA redirects to ${event.to}`);
|
|
1161
|
+
return;
|
|
982
1162
|
case "done":
|
|
983
1163
|
say(`checked ${plural(event.checked, "page")}, ${event.skipped} skipped`);
|
|
984
1164
|
return;
|
|
@@ -1215,7 +1395,7 @@ function renderCheckJunit(run) {
|
|
|
1215
1395
|
// src/cite/cli.ts
|
|
1216
1396
|
var UPDATE_FORMATS = ["pretty", "json"];
|
|
1217
1397
|
var REMOVE_FORMATS = UPDATE_FORMATS;
|
|
1218
|
-
function
|
|
1398
|
+
function colorFor2(command, isTTY, env) {
|
|
1219
1399
|
const noColor = colorOwner(command).opts().color === false;
|
|
1220
1400
|
return shouldColor({ noColor, isTTY, env });
|
|
1221
1401
|
}
|
|
@@ -1342,7 +1522,7 @@ function buildProgram3() {
|
|
|
1342
1522
|
switch (format) {
|
|
1343
1523
|
case "pretty":
|
|
1344
1524
|
text2 = renderCheckPretty(run, {
|
|
1345
|
-
color:
|
|
1525
|
+
color: colorFor2(command, process.stdout.isTTY),
|
|
1346
1526
|
quiet: Boolean(options.quiet),
|
|
1347
1527
|
showDiff: Boolean(options.showDiff),
|
|
1348
1528
|
reveal: Boolean(options.reveal)
|
|
@@ -1506,7 +1686,7 @@ function buildProgram3() {
|
|
|
1506
1686
|
dryRun
|
|
1507
1687
|
});
|
|
1508
1688
|
const text2 = format === "json" ? renderUpdateJson(run) : renderUpdatePretty(run, {
|
|
1509
|
-
color:
|
|
1689
|
+
color: colorFor2(command, process.stdout.isTTY),
|
|
1510
1690
|
// The diffs are the point of a dry run; a real run prints
|
|
1511
1691
|
// what it rewrote and leaves the file to say the rest.
|
|
1512
1692
|
showDiff: dryRun
|
|
@@ -1581,7 +1761,7 @@ function buildProgram3() {
|
|
|
1581
1761
|
dryRun
|
|
1582
1762
|
});
|
|
1583
1763
|
const text2 = format === "json" ? renderRemoveJson(run) : renderRemovePretty(run, {
|
|
1584
|
-
color:
|
|
1764
|
+
color: colorFor2(command, process.stdout.isTTY),
|
|
1585
1765
|
// The diffs are the point of a dry run; a real run prints
|
|
1586
1766
|
// what it removed and leaves the file to say the rest.
|
|
1587
1767
|
showDiff: dryRun,
|
|
@@ -1606,28 +1786,14 @@ function buildProgram3() {
|
|
|
1606
1786
|
|
|
1607
1787
|
// src/docevals/cli.ts
|
|
1608
1788
|
import { Command as Command3 } from "commander";
|
|
1609
|
-
function colorFor2(command, isTTY, env) {
|
|
1610
|
-
const noColor = colorOwner2(command).opts().color === false;
|
|
1611
|
-
return shouldColor({ noColor, isTTY, env });
|
|
1612
|
-
}
|
|
1613
|
-
function colorOwner2(command) {
|
|
1614
|
-
for (let c = command; c !== null; c = c.parent) {
|
|
1615
|
-
if (c.options.some((o) => o.long === "--no-color")) return c;
|
|
1616
|
-
}
|
|
1617
|
-
return command;
|
|
1618
|
-
}
|
|
1619
1789
|
function buildProgram4() {
|
|
1620
1790
|
const program = new Command3();
|
|
1621
1791
|
program.name("docevals").description(
|
|
1622
1792
|
"Deterministic and LLM-as-judge evals for documentation pages, driven by frontmatter."
|
|
1623
1793
|
).version(package_default.version).option("--no-color", "disable colored output").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
1624
1794
|
function collectGrant(value, previous) {
|
|
1625
|
-
if (!
|
|
1626
|
-
fail(
|
|
1627
|
-
new DocevalsError(
|
|
1628
|
-
`--allow-execution must be one of ${EXECUTION_GRANTS.join(" | ")}, got "${value}"`
|
|
1629
|
-
)
|
|
1630
|
-
);
|
|
1795
|
+
if (!isExecutionGrant(value)) {
|
|
1796
|
+
fail(new DocevalsError(allowExecutionMessage(value)));
|
|
1631
1797
|
}
|
|
1632
1798
|
return [...previous, value];
|
|
1633
1799
|
}
|
|
@@ -1700,7 +1866,7 @@ function buildProgram4() {
|
|
|
1700
1866
|
});
|
|
1701
1867
|
console.log(
|
|
1702
1868
|
renderList(run, opts.format, {
|
|
1703
|
-
color:
|
|
1869
|
+
color: colorFor(command, process.stdout.isTTY)
|
|
1704
1870
|
})
|
|
1705
1871
|
);
|
|
1706
1872
|
process.exitCode = run.exitCode;
|
|
@@ -1723,7 +1889,7 @@ function buildProgram4() {
|
|
|
1723
1889
|
// frontmatter-commands docs/**` would silently swallow the glob and run
|
|
1724
1890
|
// over the default file set instead. Repeat the flag to grant twice.
|
|
1725
1891
|
"--allow-execution <kind>",
|
|
1726
|
-
"
|
|
1892
|
+
"Run only these execution grants (repeatable): frontmatter-commands",
|
|
1727
1893
|
collectGrant,
|
|
1728
1894
|
[]
|
|
1729
1895
|
).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(
|
|
@@ -1738,6 +1904,9 @@ function buildProgram4() {
|
|
|
1738
1904
|
).option("--suite <name>", "Run only evals in this suite").option(
|
|
1739
1905
|
"--since <ref>",
|
|
1740
1906
|
"Evaluate only pages whose file or eval manifest changed between this git ref and HEAD"
|
|
1907
|
+
).option(
|
|
1908
|
+
"--newer-than <duration>",
|
|
1909
|
+
"Evaluate only pages whose file or eval manifest changed within this window, such as 30m, 24h, 7d or 2w; in CI, prefer --since"
|
|
1741
1910
|
).option(
|
|
1742
1911
|
"--max-turns <n>",
|
|
1743
1912
|
"Stop after this many ensemble runs (a cached ensemble costs none)",
|
|
@@ -1761,7 +1930,9 @@ function buildProgram4() {
|
|
|
1761
1930
|
format: opts.format,
|
|
1762
1931
|
deterministicOnly: opts.deterministicOnly,
|
|
1763
1932
|
aiOnly: opts.aiOnly,
|
|
1764
|
-
|
|
1933
|
+
// Commander's default is `[]`, which is "no flag", not "run nothing":
|
|
1934
|
+
// only a list someone typed narrows the run.
|
|
1935
|
+
allowExecution: opts.allowExecution.length > 0 ? opts.allowExecution : void 0,
|
|
1765
1936
|
execution: opts.execution,
|
|
1766
1937
|
generate: opts.generate,
|
|
1767
1938
|
cache: opts.cache,
|
|
@@ -1775,6 +1946,7 @@ function buildProgram4() {
|
|
|
1775
1946
|
evalNames: opts.eval,
|
|
1776
1947
|
suite: opts.suite,
|
|
1777
1948
|
since: opts.since,
|
|
1949
|
+
newerThan: opts.newerThan,
|
|
1778
1950
|
// commander collapses `--baseline` to true and `--no-baseline` to
|
|
1779
1951
|
// false on the same key; a string is an explicit path.
|
|
1780
1952
|
baseline: opts.baseline,
|
|
@@ -1783,7 +1955,7 @@ function buildProgram4() {
|
|
|
1783
1955
|
});
|
|
1784
1956
|
console.log(
|
|
1785
1957
|
render(report, opts.format, {
|
|
1786
|
-
color:
|
|
1958
|
+
color: colorFor(command, process.stdout.isTTY)
|
|
1787
1959
|
})
|
|
1788
1960
|
);
|
|
1789
1961
|
process.exitCode = report.exitCode;
|
|
@@ -1799,7 +1971,7 @@ function buildProgram4() {
|
|
|
1799
1971
|
).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(
|
|
1800
1972
|
async (paths, opts, command) => {
|
|
1801
1973
|
try {
|
|
1802
|
-
const pc = palette(
|
|
1974
|
+
const pc = palette(colorFor(command, process.stdout.isTTY));
|
|
1803
1975
|
const result = await runGenerate(paths, {
|
|
1804
1976
|
...documentOptions(opts),
|
|
1805
1977
|
provider: opts.provider,
|
|
@@ -1878,7 +2050,7 @@ function buildProgram4() {
|
|
|
1878
2050
|
});
|
|
1879
2051
|
console.log(
|
|
1880
2052
|
renderFill(report, opts.format, {
|
|
1881
|
-
color:
|
|
2053
|
+
color: colorFor(command, process.stdout.isTTY)
|
|
1882
2054
|
})
|
|
1883
2055
|
);
|
|
1884
2056
|
process.exitCode = report.exitCode;
|
|
@@ -1894,7 +2066,7 @@ function buildProgram4() {
|
|
|
1894
2066
|
).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(
|
|
1895
2067
|
async (paths, opts, command) => {
|
|
1896
2068
|
try {
|
|
1897
|
-
const pc = palette(
|
|
2069
|
+
const pc = palette(colorFor(command, process.stdout.isTTY));
|
|
1898
2070
|
const proposals = await runPromote(paths, {
|
|
1899
2071
|
...documentOptions(opts),
|
|
1900
2072
|
write: opts.write,
|
|
@@ -1933,7 +2105,7 @@ function buildProgram4() {
|
|
|
1933
2105
|
).option("--no-cache", "Bypass the judge response cache").action(
|
|
1934
2106
|
async (opts, command) => {
|
|
1935
2107
|
try {
|
|
1936
|
-
const color =
|
|
2108
|
+
const color = colorFor(command, process.stdout.isTTY);
|
|
1937
2109
|
const pc = palette(color);
|
|
1938
2110
|
if (opts.seed) {
|
|
1939
2111
|
const seeded = seedGoldenCases({ golden: opts.golden });
|
|
@@ -1988,7 +2160,7 @@ function buildProgram4() {
|
|
|
1988
2160
|
if (!file) {
|
|
1989
2161
|
console.log(
|
|
1990
2162
|
renderReviews(listReviews(), {
|
|
1991
|
-
color:
|
|
2163
|
+
color: colorFor(command, process.stdout.isTTY)
|
|
1992
2164
|
})
|
|
1993
2165
|
);
|
|
1994
2166
|
return;
|
|
@@ -2192,8 +2364,616 @@ function buildProgram5() {
|
|
|
2192
2364
|
return program;
|
|
2193
2365
|
}
|
|
2194
2366
|
|
|
2195
|
-
// src/
|
|
2367
|
+
// src/graph/cli.ts
|
|
2196
2368
|
import { Command as Command5 } from "commander";
|
|
2369
|
+
|
|
2370
|
+
// src/graph/reporters/index.ts
|
|
2371
|
+
var GRAPH_FORMATS = ["pretty", "json"];
|
|
2372
|
+
var GRAPH_FORMAT_LIST = GRAPH_FORMATS.join(" | ");
|
|
2373
|
+
var CHECK_FORMATS = [...GRAPH_FORMATS, "github"];
|
|
2374
|
+
var CHECK_FORMAT_LIST = CHECK_FORMATS.join(" | ");
|
|
2375
|
+
|
|
2376
|
+
// src/graph/commands/check.ts
|
|
2377
|
+
import { resolve } from "node:path";
|
|
2378
|
+
|
|
2379
|
+
// src/graph/reporters/github.ts
|
|
2380
|
+
function renderCheckGithub2(report) {
|
|
2381
|
+
const lines = [];
|
|
2382
|
+
for (const f of report.findings) {
|
|
2383
|
+
const file = f.docs[0];
|
|
2384
|
+
const where2 = file === void 0 ? "" : ` file=${escapeWorkflowCommandProperty(file)}`;
|
|
2385
|
+
const message = escapeWorkflowCommandMessage(
|
|
2386
|
+
`${f.message} (${compactIri(f.focusNode)})`
|
|
2387
|
+
);
|
|
2388
|
+
lines.push(`::${f.severity}${where2}::${message}`);
|
|
2389
|
+
}
|
|
2390
|
+
return lines.join("\n");
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
// src/graph/commands/check.ts
|
|
2394
|
+
async function runCheck3(opts = {}) {
|
|
2395
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
2396
|
+
const config = loadRunConfig(
|
|
2397
|
+
{
|
|
2398
|
+
...opts.config === void 0 ? {} : { configPath: opts.config },
|
|
2399
|
+
...opts.noConfig === void 0 ? {} : { noConfig: opts.noConfig }
|
|
2400
|
+
},
|
|
2401
|
+
cwd
|
|
2402
|
+
);
|
|
2403
|
+
const store = loadGraph(resolve(cwd, opts.graph ?? config.out));
|
|
2404
|
+
const shapesInput = opts.shapes && opts.shapes.length > 0 ? opts.shapes : config.check.shapes;
|
|
2405
|
+
const shapes = shapesInput.length > 0 ? shapesInput.map((p) => resolve(cwd, p)) : [bundledShapesPath(import.meta.url)];
|
|
2406
|
+
const findings = await validateGraph(store, shapes);
|
|
2407
|
+
const count = (severity) => findings.filter((f) => f.severity === severity).length;
|
|
2408
|
+
const errors = count("error");
|
|
2409
|
+
return {
|
|
2410
|
+
findings,
|
|
2411
|
+
errors,
|
|
2412
|
+
warnings: count("warning"),
|
|
2413
|
+
notices: count("notice"),
|
|
2414
|
+
shapes,
|
|
2415
|
+
// Only an `error` fails the run; a warning and a notice are reported and
|
|
2416
|
+
// pass, as they were when they were called violations and warnings.
|
|
2417
|
+
exitCode: errors > 0 ? 1 : 0
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
function plural3(n, word) {
|
|
2421
|
+
return `${String(n)} ${word}${n === 1 ? "" : "s"}`;
|
|
2422
|
+
}
|
|
2423
|
+
function renderCheck(report, format) {
|
|
2424
|
+
if (format === "json") {
|
|
2425
|
+
const { exitCode: _exitCode, ...rest } = report;
|
|
2426
|
+
return JSON.stringify(rest, null, 2);
|
|
2427
|
+
}
|
|
2428
|
+
if (format === "github") return renderCheckGithub2(report);
|
|
2429
|
+
const lines = [];
|
|
2430
|
+
for (const f of report.findings) {
|
|
2431
|
+
const where2 = f.docs.length > 0 ? ` [${f.docs.join(", ")}]` : "";
|
|
2432
|
+
lines.push(
|
|
2433
|
+
`${f.severity}: ${compactIri(f.focusNode)}${f.path ? ` ${compactIri(f.path)}` : ""} \u2014 ${f.message}${where2}`
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
if (report.findings.length > 0) lines.push("");
|
|
2437
|
+
lines.push(
|
|
2438
|
+
`${plural3(report.errors, "error")}, ${plural3(report.warnings, "warning")}, ${plural3(report.notices, "notice")}`
|
|
2439
|
+
);
|
|
2440
|
+
return lines.join("\n");
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
// src/graph/commands/init.ts
|
|
2444
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2445
|
+
import { resolve as resolve2 } from "node:path";
|
|
2446
|
+
import { parse as parseYaml } from "yaml";
|
|
2447
|
+
var HEADER = `# manni.config.yaml \u2014 shared configuration for the manni family of tools.
|
|
2448
|
+
# Each tool reads its own top-level key; manni graph reads "graph:".
|
|
2449
|
+
`;
|
|
2450
|
+
var STARTER = `# Base IRI for every minted node. Set this to a namespace you control;
|
|
2451
|
+
# without it, IRIs fall back to the urn:manni:graph: placeholder.
|
|
2452
|
+
# baseIri: https://example.com/graph/
|
|
2453
|
+
|
|
2454
|
+
# There is no document set here. \`manni graph build\` and \`manni graph fill\` read the
|
|
2455
|
+
# paths you name, or the collections declared once for every tool at the TOP
|
|
2456
|
+
# level of this file, beside \`graph:\` rather than inside it:
|
|
2457
|
+
#
|
|
2458
|
+
# collections:
|
|
2459
|
+
# - name: site
|
|
2460
|
+
# paths: ["docs/**/*.md"]
|
|
2461
|
+
#
|
|
2462
|
+
# Output of \`manni graph build\`.
|
|
2463
|
+
out: graph/graph.ttl
|
|
2464
|
+
|
|
2465
|
+
# Map published-site routes back to source files so route-style links
|
|
2466
|
+
# (/docs/actions/find) become graph edges. A route may also carry the BCP-47
|
|
2467
|
+
# \`language\` labelling every document under its root; a page's own \`lang\`
|
|
2468
|
+
# frontmatter wins. Uncomment and adjust:
|
|
2469
|
+
# routes:
|
|
2470
|
+
# - basePath: /docs
|
|
2471
|
+
# root: docs
|
|
2472
|
+
# extensions: [.md, .mdx, .markdown]
|
|
2473
|
+
# indexFiles: [index, README]
|
|
2474
|
+
# - basePath: /docs/de
|
|
2475
|
+
# root: docs/de
|
|
2476
|
+
# language: de
|
|
2477
|
+
|
|
2478
|
+
# What to derive triples from. Remove entries to opt out.
|
|
2479
|
+
build:
|
|
2480
|
+
derive: [frontmatter, sections, links, tags, images, code, provenance]
|
|
2481
|
+
|
|
2482
|
+
# PROV-O settings. Git history is detected, not switched: per-file dates and
|
|
2483
|
+
# author agents, rename -> prov:wasRevisionOf, and the build activity's HEAD
|
|
2484
|
+
# committer date are derived wherever git can run over a repository, and a run
|
|
2485
|
+
# that cannot says so once and builds the rest. Deterministic per commit;
|
|
2486
|
+
# wall-clock time never enters the graph.
|
|
2487
|
+
# qualified: emit prov:qualifiedAttribution/qualifiedAssociation nodes
|
|
2488
|
+
# with roles alongside the direct properties.
|
|
2489
|
+
provenance:
|
|
2490
|
+
qualified: true
|
|
2491
|
+
|
|
2492
|
+
# Which fields a published graph may carry is read from x-manni-graph-output in
|
|
2493
|
+
# the schema set \`manni meta validate\` resolves for each page. Set that under
|
|
2494
|
+
# meta: (schemas, strict, register), not here. The default set keeps the
|
|
2495
|
+
# stewardship people fields (owner, stakeholders, reviewed-by) out.
|
|
2496
|
+
|
|
2497
|
+
# SHACL shapes \`manni graph check\` validates the built graph against. Default:
|
|
2498
|
+
# the shapes contract bundled with manni (shapes/graph/shapes-1.0.0.ttl).
|
|
2499
|
+
# check:
|
|
2500
|
+
# shapes: ["./my-shapes.ttl"]
|
|
2501
|
+
|
|
2502
|
+
# Metadata coverage gate for \`manni graph stats --check\`. A number applies to
|
|
2503
|
+
# every measured field; a map gates named fields only. Unset gates nothing.
|
|
2504
|
+
# stats:
|
|
2505
|
+
# coverageThreshold:
|
|
2506
|
+
# title: 100
|
|
2507
|
+
# description: 50
|
|
2508
|
+
|
|
2509
|
+
# Which provider \`manni graph fill\` sends pages to. Unset, the family's
|
|
2510
|
+
# top-level providers: map decides, and then \`auto\`, which detects one: an
|
|
2511
|
+
# Anthropic key, then an OpenAI key, then the Claude CLI, then a local model.
|
|
2512
|
+
# Connection settings (apiKeyEnv, baseUrl, command) live in that map, declared
|
|
2513
|
+
# once for every manni tool. \`--provider\` wins over this, \`--local\` over both.
|
|
2514
|
+
# provider: anthropic # auto | anthropic | openai | claude-cli | llama-cpp
|
|
2515
|
+
# model: claude-sonnet-4-5 # the provider's default when omitted
|
|
2516
|
+
|
|
2517
|
+
# LLM settings for \`manni graph fill\` (SKOS frontmatter proposals).
|
|
2518
|
+
fill:
|
|
2519
|
+
temperature: 0
|
|
2520
|
+
# Stop after this many inference calls. One page is one turn, and a cached
|
|
2521
|
+
# page spends none. Unset is unbounded.
|
|
2522
|
+
# maxTurns: 50
|
|
2523
|
+
cacheDir: .manni/graph/cache
|
|
2524
|
+
# fill proposes every field; confidence (0..1 per field) gates what is
|
|
2525
|
+
# written. Fields scored below the threshold are reported, not written.
|
|
2526
|
+
confidenceThreshold: 0.7
|
|
2527
|
+
# fields: defaults to every fillable field \u2014 uncomment to restrict.
|
|
2528
|
+
# Record meta-provenance (model + /graph/ pointers + confidence) on filled docs.
|
|
2529
|
+
writeProvenance: true
|
|
2530
|
+
# Reject proposals that would violate the SHACL shapes contract
|
|
2531
|
+
# (broader/narrower cycles, conflicting labels).
|
|
2532
|
+
validateGraph: true
|
|
2533
|
+
# Also propose per-section metadata. Off by default: it costs more output per
|
|
2534
|
+
# call, and section metadata is explicit-only, so review it as carefully.
|
|
2535
|
+
sections: false
|
|
2536
|
+
|
|
2537
|
+
# Local embeddings for semantic search (\`manni graph embed\`). Needs the optional
|
|
2538
|
+
# peer: npm install @huggingface/transformers
|
|
2539
|
+
# embed:
|
|
2540
|
+
# model: onnx-community/granite-embedding-small-english-r2-ONNX
|
|
2541
|
+
# dtype: q8 # q8 keeps embedding reproducible across platforms
|
|
2542
|
+
# out: graph # directory for the per-language vector sidecars
|
|
2543
|
+
# cacheDir: .manni/graph/embed-cache
|
|
2544
|
+
|
|
2545
|
+
# Optional enrichment for \`manni graph export iirds\` (the iiRDS package).
|
|
2546
|
+
# Absent, a minimal valid package is still produced.
|
|
2547
|
+
# export:
|
|
2548
|
+
# iirds:
|
|
2549
|
+
# title: My Docs # package title (default: "manni graph export")
|
|
2550
|
+
# creator: Acme Corp # Creator iirds:Party + vcard:Organization
|
|
2551
|
+
# version: "1.3" # iiRDS version literal: "1.2" | "1.3"
|
|
2552
|
+
`;
|
|
2553
|
+
function starterSection() {
|
|
2554
|
+
const body = STARTER.split("\n").map((line) => line === "" ? "" : ` ${line}`).join("\n");
|
|
2555
|
+
return `${CONFIG_SECTION}:
|
|
2556
|
+
${body}`;
|
|
2557
|
+
}
|
|
2558
|
+
async function runInit2(cwd = process.cwd()) {
|
|
2559
|
+
const path = resolve2(cwd, DEFAULT_CONFIG_FILENAME);
|
|
2560
|
+
if (!existsSync(path)) {
|
|
2561
|
+
await writeTextAtomic(path, `${HEADER}${starterSection()}`);
|
|
2562
|
+
return path;
|
|
2563
|
+
}
|
|
2564
|
+
const text2 = readFileSync(path, "utf8");
|
|
2565
|
+
let doc;
|
|
2566
|
+
try {
|
|
2567
|
+
doc = parseYaml(text2);
|
|
2568
|
+
} catch (e) {
|
|
2569
|
+
throw new GraphError(
|
|
2570
|
+
`Invalid YAML in ${DEFAULT_CONFIG_FILENAME}: ${errorMessage(e)}`
|
|
2571
|
+
);
|
|
2572
|
+
}
|
|
2573
|
+
if (doc != null && (typeof doc !== "object" || Array.isArray(doc))) {
|
|
2574
|
+
throw new GraphError(
|
|
2575
|
+
`${DEFAULT_CONFIG_FILENAME}: top level must be a mapping \u2014 not adding a \`${CONFIG_SECTION}:\` section to it.`
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
if (doc != null && Object.hasOwn(doc, CONFIG_SECTION)) {
|
|
2579
|
+
throw new GraphError(
|
|
2580
|
+
`${DEFAULT_CONFIG_FILENAME} already has a \`${CONFIG_SECTION}:\` section \u2014 not overwriting.`
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
const separator = text2 === "" || text2.endsWith("\n") ? "" : "\n";
|
|
2584
|
+
const blank = text2 === "" ? "" : "\n";
|
|
2585
|
+
await writeTextAtomic(
|
|
2586
|
+
path,
|
|
2587
|
+
`${text2}${separator}${blank}${starterSection()}`
|
|
2588
|
+
);
|
|
2589
|
+
return path;
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
// src/graph/cli.ts
|
|
2593
|
+
function buildProgram6() {
|
|
2594
|
+
const program = new Command5();
|
|
2595
|
+
program.name("graph").description(
|
|
2596
|
+
"Deterministic knowledge graphs derived from documentation frontmatter and formatting."
|
|
2597
|
+
).version(package_default.version).option("--no-color", "disable colored output").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
2598
|
+
function numericOption(flag, { min, max, integer }) {
|
|
2599
|
+
return (raw) => {
|
|
2600
|
+
const value = Number.parseFloat(raw);
|
|
2601
|
+
if (!Number.isFinite(value)) {
|
|
2602
|
+
throw new GraphError(`${flag} expects a number, got "${raw}".`);
|
|
2603
|
+
}
|
|
2604
|
+
if (integer && !Number.isInteger(value)) {
|
|
2605
|
+
throw new GraphError(`${flag} expects a whole number, got ${value}.`);
|
|
2606
|
+
}
|
|
2607
|
+
if (value < min || max !== void 0 && value > max) {
|
|
2608
|
+
const range = max === void 0 ? `>= ${min}` : `${min}..${max}`;
|
|
2609
|
+
throw new GraphError(`${flag} must be ${range}, got ${value}.`);
|
|
2610
|
+
}
|
|
2611
|
+
return value;
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
function countOption(flag, min = 1) {
|
|
2615
|
+
return numericOption(flag, { min, integer: true });
|
|
2616
|
+
}
|
|
2617
|
+
function enumOption(flag, allowed) {
|
|
2618
|
+
return (raw) => {
|
|
2619
|
+
if (!allowed.includes(raw)) {
|
|
2620
|
+
throw new GraphError(
|
|
2621
|
+
`${flag} must be one of ${allowed.join(" | ")}, got "${raw}".`
|
|
2622
|
+
);
|
|
2623
|
+
}
|
|
2624
|
+
return raw;
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
function choiceOption(flag, allowed) {
|
|
2628
|
+
return (raw) => {
|
|
2629
|
+
if (!allowed.includes(raw)) {
|
|
2630
|
+
throw new GraphError(`Unknown ${flag} "${raw}". Use ${allowed.join(" | ")}.`);
|
|
2631
|
+
}
|
|
2632
|
+
return raw;
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
const formatOption = choiceOption("--format", GRAPH_FORMATS);
|
|
2636
|
+
const checkFormatOption = choiceOption("--format", CHECK_FORMATS);
|
|
2637
|
+
const fieldsOption = (raw) => splitList(raw).map((value) => enumOption("--fields", ALL_FILL_FIELDS)(value));
|
|
2638
|
+
function documentInputs(command, verb) {
|
|
2639
|
+
return command.argument(
|
|
2640
|
+
"[paths...]",
|
|
2641
|
+
`files, directories, or globs to ${verb} (use - for stdin)`
|
|
2642
|
+
).option(
|
|
2643
|
+
"--collection <name>",
|
|
2644
|
+
"Configured collection to read (repeatable)",
|
|
2645
|
+
collect,
|
|
2646
|
+
[]
|
|
2647
|
+
).option("--exclude <glob>", "Glob to exclude (repeatable)", collect, []).option("--as <format>", "force an input format (markdown, mdx)").option(
|
|
2648
|
+
"--ext <list>",
|
|
2649
|
+
"comma-separated extensions for directory walks",
|
|
2650
|
+
DEFAULT_EXTENSIONS.join(",")
|
|
2651
|
+
).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");
|
|
2652
|
+
}
|
|
2653
|
+
async function inputOptions2(paths, opts) {
|
|
2654
|
+
assertInputFormat(paths, opts.as);
|
|
2655
|
+
return {
|
|
2656
|
+
...opts.as === void 0 ? {} : { as: opts.as },
|
|
2657
|
+
ext: splitList(opts.ext),
|
|
2658
|
+
...opts.allowEmpty === true ? { allowEmpty: true } : {},
|
|
2659
|
+
...paths.includes(STDIN) ? { stdinContent: await readStdin() } : {}
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
function configInputs(command) {
|
|
2663
|
+
return command.option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file");
|
|
2664
|
+
}
|
|
2665
|
+
function rest(opts) {
|
|
2666
|
+
const { config, ...others } = opts;
|
|
2667
|
+
return others;
|
|
2668
|
+
}
|
|
2669
|
+
function documentOptions(opts) {
|
|
2670
|
+
const { configPath, noConfig } = configOption(opts.config);
|
|
2671
|
+
return {
|
|
2672
|
+
...configPath === void 0 ? {} : { config: configPath },
|
|
2673
|
+
...noConfig === true ? { noConfig } : {},
|
|
2674
|
+
...opts.collection === void 0 ? {} : { collection: opts.collection },
|
|
2675
|
+
...opts.exclude === void 0 ? {} : { exclude: opts.exclude }
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
program.command("init").description("Add a starter `graph:` section to manni.config.yaml in the current directory").action(async () => {
|
|
2679
|
+
try {
|
|
2680
|
+
console.log(`Created ${await runInit2()}`);
|
|
2681
|
+
} catch (e) {
|
|
2682
|
+
fail(e);
|
|
2683
|
+
}
|
|
2684
|
+
});
|
|
2685
|
+
documentInputs(
|
|
2686
|
+
program.command("build").description("Derive the knowledge graph and write deterministic Turtle"),
|
|
2687
|
+
"build"
|
|
2688
|
+
).option("-o, --out <path>", "Output .ttl path (default: config out)").action(async (paths, opts) => {
|
|
2689
|
+
try {
|
|
2690
|
+
const result = await runBuild({
|
|
2691
|
+
paths,
|
|
2692
|
+
...documentOptions(opts),
|
|
2693
|
+
...await inputOptions2(paths, opts),
|
|
2694
|
+
out: opts.out
|
|
2695
|
+
});
|
|
2696
|
+
for (const warning of result.warnings) warn(warning);
|
|
2697
|
+
console.log(
|
|
2698
|
+
`Wrote ${result.outPath} (${result.docs} docs, ${result.quads} triples)`
|
|
2699
|
+
);
|
|
2700
|
+
} catch (e) {
|
|
2701
|
+
fail(e);
|
|
2702
|
+
}
|
|
2703
|
+
});
|
|
2704
|
+
configInputs(
|
|
2705
|
+
program.command("check").description(
|
|
2706
|
+
"Validate the built graph against the bundled SHACL shapes (errors exit 1)"
|
|
2707
|
+
)
|
|
2708
|
+
).option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2709
|
+
"--shapes <path>",
|
|
2710
|
+
"Shapes .ttl file; repeatable (default: config check.shapes, then bundled)",
|
|
2711
|
+
collect,
|
|
2712
|
+
[]
|
|
2713
|
+
).option(
|
|
2714
|
+
"-f, --format <format>",
|
|
2715
|
+
`Output: ${CHECK_FORMAT_LIST}`,
|
|
2716
|
+
checkFormatOption,
|
|
2717
|
+
"pretty"
|
|
2718
|
+
).action(
|
|
2719
|
+
async (opts) => {
|
|
2720
|
+
try {
|
|
2721
|
+
const report = await runCheck3({ ...rest(opts), ...documentOptions(opts) });
|
|
2722
|
+
console.log(renderCheck(report, opts.format));
|
|
2723
|
+
process.exitCode = report.exitCode;
|
|
2724
|
+
} catch (e) {
|
|
2725
|
+
fail(e);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
);
|
|
2729
|
+
documentInputs(
|
|
2730
|
+
program.command("fill").description(
|
|
2731
|
+
"Propose `graph:` frontmatter fields with an LLM, gated by confidence, and write them back"
|
|
2732
|
+
),
|
|
2733
|
+
"fill"
|
|
2734
|
+
).option(
|
|
2735
|
+
"-f, --format <format>",
|
|
2736
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2737
|
+
formatOption,
|
|
2738
|
+
"pretty"
|
|
2739
|
+
).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(
|
|
2740
|
+
"--max-turns <n>",
|
|
2741
|
+
"Stop after this many inference calls (a cached page costs none)",
|
|
2742
|
+
countOption("--max-turns")
|
|
2743
|
+
).option(
|
|
2744
|
+
"--confidence <n>",
|
|
2745
|
+
"Minimum model confidence (0..1) to write a field (default: config, 0.7)",
|
|
2746
|
+
numericOption("--confidence", { min: 0, max: 1 })
|
|
2747
|
+
).option(
|
|
2748
|
+
// Comma-separated and given once: one separator per list (proposal 0034).
|
|
2749
|
+
"--fields <list>",
|
|
2750
|
+
`Fields to propose (comma-separated; default: config, then every field)`,
|
|
2751
|
+
fieldsOption
|
|
2752
|
+
).option(
|
|
2753
|
+
"--provider <name>",
|
|
2754
|
+
"Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp"
|
|
2755
|
+
).option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).action(async (paths, opts) => {
|
|
2756
|
+
try {
|
|
2757
|
+
const report = await runFill2({
|
|
2758
|
+
paths,
|
|
2759
|
+
...documentOptions(opts),
|
|
2760
|
+
...await inputOptions2(paths, opts),
|
|
2761
|
+
dryRun: opts.dryRun,
|
|
2762
|
+
force: opts.force,
|
|
2763
|
+
noCache: opts.cache === false,
|
|
2764
|
+
noValidateGraph: opts.validateGraph === false,
|
|
2765
|
+
sections: opts.sections,
|
|
2766
|
+
maxTurns: opts.maxTurns,
|
|
2767
|
+
confidence: opts.confidence,
|
|
2768
|
+
fields: opts.fields,
|
|
2769
|
+
provider: opts.provider,
|
|
2770
|
+
model: opts.model,
|
|
2771
|
+
local: opts.local
|
|
2772
|
+
});
|
|
2773
|
+
for (const warning of report.warnings) warn(warning);
|
|
2774
|
+
const rendered2 = renderFill2(report, opts.format);
|
|
2775
|
+
if (paths.includes(STDIN)) {
|
|
2776
|
+
if (report.stdinDocument !== void 0) {
|
|
2777
|
+
process.stdout.write(report.stdinDocument);
|
|
2778
|
+
}
|
|
2779
|
+
if (rendered2.length > 0) process.stderr.write(`${rendered2}
|
|
2780
|
+
`);
|
|
2781
|
+
} else {
|
|
2782
|
+
console.log(rendered2);
|
|
2783
|
+
}
|
|
2784
|
+
process.exitCode = report.exitCode;
|
|
2785
|
+
} catch (e) {
|
|
2786
|
+
fail(e);
|
|
2787
|
+
}
|
|
2788
|
+
});
|
|
2789
|
+
program.command("query").description(
|
|
2790
|
+
"Match triple patterns against the built graph (omit a term for wildcard)"
|
|
2791
|
+
).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(
|
|
2792
|
+
"-f, --format <format>",
|
|
2793
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2794
|
+
formatOption,
|
|
2795
|
+
"pretty"
|
|
2796
|
+
).action(
|
|
2797
|
+
(opts) => {
|
|
2798
|
+
try {
|
|
2799
|
+
const { subject, predicate, object, ...others } = rest(opts);
|
|
2800
|
+
const result = runQuery({
|
|
2801
|
+
...others,
|
|
2802
|
+
...documentOptions(opts),
|
|
2803
|
+
...subject === void 0 ? {} : { s: subject },
|
|
2804
|
+
...predicate === void 0 ? {} : { p: predicate },
|
|
2805
|
+
...object === void 0 ? {} : { o: object }
|
|
2806
|
+
});
|
|
2807
|
+
console.log(renderQuery(result, opts.format));
|
|
2808
|
+
} catch (e) {
|
|
2809
|
+
fail(e);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
);
|
|
2813
|
+
program.command("stats").description(
|
|
2814
|
+
"Summarize the built graph: counts, orphans, broken links, hubs, metadata coverage"
|
|
2815
|
+
).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(
|
|
2816
|
+
"-f, --format <format>",
|
|
2817
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2818
|
+
formatOption,
|
|
2819
|
+
"pretty"
|
|
2820
|
+
).option(
|
|
2821
|
+
"--check",
|
|
2822
|
+
"Exit 1 when broken internal links exist or coverage is below threshold"
|
|
2823
|
+
).option(
|
|
2824
|
+
"--top <n>",
|
|
2825
|
+
"How many most-connected docs to list",
|
|
2826
|
+
countOption("--top")
|
|
2827
|
+
).option(
|
|
2828
|
+
"--coverage-threshold <pct>",
|
|
2829
|
+
"Minimum metadata coverage % (all fields); overrides config for this run",
|
|
2830
|
+
// A percentage, so 0..100 rather than the 0..1 of a confidence. NaN here is
|
|
2831
|
+
// worse than a wrong number: `pct < NaN` is false for every field, so the
|
|
2832
|
+
// gate this flag exists to tighten would silently pass instead.
|
|
2833
|
+
numericOption("--coverage-threshold", { min: 0, max: 100 })
|
|
2834
|
+
).action(
|
|
2835
|
+
(opts) => {
|
|
2836
|
+
try {
|
|
2837
|
+
const report = runStats({ ...rest(opts), ...documentOptions(opts) });
|
|
2838
|
+
console.log(renderStats(report, opts.format));
|
|
2839
|
+
process.exitCode = report.exitCode;
|
|
2840
|
+
} catch (e) {
|
|
2841
|
+
fail(e);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
);
|
|
2845
|
+
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(
|
|
2846
|
+
"-i, --index <dir>",
|
|
2847
|
+
"Directory holding the indexes and manifest (default: beside the graph)"
|
|
2848
|
+
).option(
|
|
2849
|
+
"--lang <tag>",
|
|
2850
|
+
"Which localization to search (required when the corpus has more than one)"
|
|
2851
|
+
).option("--limit <n>", "Maximum results (default 10)", countOption("--limit")).option(
|
|
2852
|
+
"--vectors <path>",
|
|
2853
|
+
"Vector sidecar path (default: from the manifest)"
|
|
2854
|
+
).option(
|
|
2855
|
+
"--mode <mode>",
|
|
2856
|
+
`Which legs to run: ${SEARCH_MODES.join(" | ")} (default: hybrid when vectors exist)`,
|
|
2857
|
+
choiceOption("--mode", SEARCH_MODES)
|
|
2858
|
+
).option(
|
|
2859
|
+
"-f, --format <format>",
|
|
2860
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2861
|
+
formatOption,
|
|
2862
|
+
"pretty"
|
|
2863
|
+
).action(
|
|
2864
|
+
async (query, opts) => {
|
|
2865
|
+
try {
|
|
2866
|
+
const report = await runSearch({
|
|
2867
|
+
...rest(opts),
|
|
2868
|
+
...documentOptions(opts),
|
|
2869
|
+
query
|
|
2870
|
+
});
|
|
2871
|
+
console.log(renderSearch(report, opts.format));
|
|
2872
|
+
} catch (e) {
|
|
2873
|
+
fail(e);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
);
|
|
2877
|
+
program.command("traverse").description(
|
|
2878
|
+
"Walk the graph from a node, honoring scope rules, with the full trace"
|
|
2879
|
+
).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(
|
|
2880
|
+
"-d, --depth <n>",
|
|
2881
|
+
"Maximum hops from the node (default 1; 3 under --impact)",
|
|
2882
|
+
// Zero is allowed: it means the node itself, which is a real answer.
|
|
2883
|
+
countOption("--depth", 0)
|
|
2884
|
+
).option(
|
|
2885
|
+
"--predicates <list>",
|
|
2886
|
+
"Only follow these predicates (comma-separated)"
|
|
2887
|
+
).option("--reverse", "Follow inbound edges (who points at this node)").option("--impact", "Transitive inbound reach: what a change here affects").option(
|
|
2888
|
+
"--variant <variant>",
|
|
2889
|
+
"Scope filter: product variant IRI, title, or slug"
|
|
2890
|
+
).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(
|
|
2891
|
+
"-f, --format <format>",
|
|
2892
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2893
|
+
formatOption,
|
|
2894
|
+
"pretty"
|
|
2895
|
+
).action(
|
|
2896
|
+
(node, opts) => {
|
|
2897
|
+
try {
|
|
2898
|
+
const { softwareSubject, ...others } = rest(opts);
|
|
2899
|
+
const report = runTraverse({
|
|
2900
|
+
...others,
|
|
2901
|
+
...documentOptions(opts),
|
|
2902
|
+
...softwareSubject === void 0 ? {} : { subject: softwareSubject },
|
|
2903
|
+
node,
|
|
2904
|
+
predicates: opts.predicates === void 0 ? void 0 : splitList(opts.predicates)
|
|
2905
|
+
});
|
|
2906
|
+
console.log(renderTraverse(report, opts.format));
|
|
2907
|
+
} catch (e) {
|
|
2908
|
+
fail(e);
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
);
|
|
2912
|
+
program.command("embed").description(
|
|
2913
|
+
"Compute local embeddings for the search index (needs @huggingface/transformers)"
|
|
2914
|
+
).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(
|
|
2915
|
+
"-i, --index <dir>",
|
|
2916
|
+
"Directory holding the indexes and manifest (default: beside the graph)"
|
|
2917
|
+
).option(
|
|
2918
|
+
"-o, --out <dir>",
|
|
2919
|
+
"Directory for the sidecars (default: the index directory)"
|
|
2920
|
+
).option(
|
|
2921
|
+
"--embedding-model <id>",
|
|
2922
|
+
"Embedding model id (any id; `mock` for offline runs)"
|
|
2923
|
+
).option("--dtype <dtype>", "Weight quantization (default q8)").option("--no-cache", "Ignore the vector cache").option(
|
|
2924
|
+
"-f, --format <format>",
|
|
2925
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2926
|
+
formatOption,
|
|
2927
|
+
"pretty"
|
|
2928
|
+
).action(
|
|
2929
|
+
async (opts) => {
|
|
2930
|
+
try {
|
|
2931
|
+
const { embeddingModel, ...others } = rest(opts);
|
|
2932
|
+
const report = await runEmbed({
|
|
2933
|
+
...others,
|
|
2934
|
+
...documentOptions(opts),
|
|
2935
|
+
...embeddingModel === void 0 ? {} : { model: embeddingModel },
|
|
2936
|
+
noCache: opts.cache === false
|
|
2937
|
+
});
|
|
2938
|
+
console.log(renderEmbed(report, opts.format));
|
|
2939
|
+
} catch (e) {
|
|
2940
|
+
fail(e);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
);
|
|
2944
|
+
program.command("export").description(
|
|
2945
|
+
"Reserialize the built graph into a consumer format (jsonld file, iirds package, or search index)"
|
|
2946
|
+
).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(
|
|
2947
|
+
"-o, --out <path>",
|
|
2948
|
+
"Output path (default: the graph path with the target's extension)"
|
|
2949
|
+
).action(
|
|
2950
|
+
async (target, opts) => {
|
|
2951
|
+
try {
|
|
2952
|
+
if (!EXPORT_TARGETS.includes(target)) {
|
|
2953
|
+
throw new GraphError(
|
|
2954
|
+
`Unknown export target "${target}". Use ${EXPORT_TARGETS.join(" | ")}.`
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
const result = await runExport({
|
|
2958
|
+
...documentOptions(opts),
|
|
2959
|
+
graph: opts.graph,
|
|
2960
|
+
format: target,
|
|
2961
|
+
out: opts.out
|
|
2962
|
+
});
|
|
2963
|
+
for (const warning of result.warnings) warn(warning);
|
|
2964
|
+
console.log(
|
|
2965
|
+
`Wrote ${result.nodes} node${result.nodes === 1 ? "" : "s"} to ${result.outPath}`
|
|
2966
|
+
);
|
|
2967
|
+
} catch (e) {
|
|
2968
|
+
fail(e);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
);
|
|
2972
|
+
return program;
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
// src/lint/cli.ts
|
|
2976
|
+
import { Command as Command6 } from "commander";
|
|
2197
2977
|
var LIST_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
|
|
2198
2978
|
var TOOL_OPTIONS_GROUP = "Tool options:";
|
|
2199
2979
|
function resolveColor2(program) {
|
|
@@ -2271,8 +3051,8 @@ function inputOptions(paths, options) {
|
|
|
2271
3051
|
onNotice: notice
|
|
2272
3052
|
};
|
|
2273
3053
|
}
|
|
2274
|
-
function
|
|
2275
|
-
const program = new
|
|
3054
|
+
function buildProgram7() {
|
|
3055
|
+
const program = new Command6();
|
|
2276
3056
|
program.name("lint").description(
|
|
2277
3057
|
"Validate document structure against doctype templates. Deterministic, CI-friendly."
|
|
2278
3058
|
).version(package_default.version, "-V, --version").option("--no-color", "disable colored output").showHelpAfterError().exitOverride();
|
|
@@ -2471,13 +3251,13 @@ function buildProgram6() {
|
|
|
2471
3251
|
}
|
|
2472
3252
|
|
|
2473
3253
|
// src/term/cli.ts
|
|
2474
|
-
import { Command as
|
|
3254
|
+
import { Command as Command7 } from "commander";
|
|
2475
3255
|
|
|
2476
3256
|
// src/term/commands/check.ts
|
|
2477
|
-
import { existsSync as
|
|
3257
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2478
3258
|
|
|
2479
3259
|
// src/term/core/config.ts
|
|
2480
|
-
import { resolve } from "node:path";
|
|
3260
|
+
import { resolve as resolve3 } from "node:path";
|
|
2481
3261
|
|
|
2482
3262
|
// src/term/errors.ts
|
|
2483
3263
|
var TermError = class extends ToolError {
|
|
@@ -2657,11 +3437,11 @@ function toLoaded(file) {
|
|
|
2657
3437
|
tools: file.tools
|
|
2658
3438
|
};
|
|
2659
3439
|
}
|
|
2660
|
-
var
|
|
3440
|
+
var STDIN2 = "-";
|
|
2661
3441
|
async function resolveTermRun(opts) {
|
|
2662
|
-
const cwd =
|
|
3442
|
+
const cwd = resolve3(opts.cwd);
|
|
2663
3443
|
const wanted = opts.collection ?? [];
|
|
2664
|
-
if (wanted.length > 0 && opts.inputs.some((input) => input !==
|
|
3444
|
+
if (wanted.length > 0 && opts.inputs.some((input) => input !== STDIN2)) {
|
|
2665
3445
|
throw new TermError(
|
|
2666
3446
|
"--collection selects a configured collection; it cannot be combined with paths."
|
|
2667
3447
|
);
|
|
@@ -2676,7 +3456,7 @@ async function resolveTermRun(opts) {
|
|
|
2676
3456
|
const fromCollections = opts.inputs.length === 0;
|
|
2677
3457
|
const inputs = fromCollections ? collections.flatMap((c) => c.paths) : opts.inputs;
|
|
2678
3458
|
const base = fromCollections && inputs.length > 0 && loaded ? loaded.dir : cwd;
|
|
2679
|
-
const manifests = loaded ? (loaded.config.manifests ?? []).map((written) => ({ path:
|
|
3459
|
+
const manifests = loaded ? (loaded.config.manifests ?? []).map((written) => ({ path: resolve3(loaded.dir, written), written })) : [];
|
|
2680
3460
|
return {
|
|
2681
3461
|
config: loaded ? loaded.config : null,
|
|
2682
3462
|
inputs,
|
|
@@ -2993,12 +3773,12 @@ function compareFindings2(a, b) {
|
|
|
2993
3773
|
}
|
|
2994
3774
|
|
|
2995
3775
|
// src/term/commands/run.ts
|
|
2996
|
-
import { existsSync } from "node:fs";
|
|
2997
|
-
import { isAbsolute, relative, resolve as
|
|
3776
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
3777
|
+
import { isAbsolute, relative, resolve as resolve5 } from "node:path";
|
|
2998
3778
|
|
|
2999
3779
|
// src/term/core/load-set.ts
|
|
3000
3780
|
import { readFile } from "node:fs/promises";
|
|
3001
|
-
import { extname, resolve as
|
|
3781
|
+
import { extname, resolve as resolve4 } from "node:path";
|
|
3002
3782
|
|
|
3003
3783
|
// src/term/core/writers/markup.ts
|
|
3004
3784
|
function xmlText(value) {
|
|
@@ -4580,7 +5360,7 @@ async function loadTermSet(opts) {
|
|
|
4580
5360
|
});
|
|
4581
5361
|
const namedFiles = new Set(named);
|
|
4582
5362
|
for (const file of files) {
|
|
4583
|
-
const path =
|
|
5363
|
+
const path = resolve4(run.base, file);
|
|
4584
5364
|
if (namedFiles.has(file) && MANIFEST_EXTENSIONS.has(extname(file).toLowerCase())) {
|
|
4585
5365
|
let content2;
|
|
4586
5366
|
try {
|
|
@@ -4631,7 +5411,7 @@ async function loadTermSet(opts) {
|
|
|
4631
5411
|
|
|
4632
5412
|
// src/term/commands/run.ts
|
|
4633
5413
|
async function loadTerms(opts) {
|
|
4634
|
-
const cwd =
|
|
5414
|
+
const cwd = resolve5(opts.cwd ?? process.cwd());
|
|
4635
5415
|
const run = await resolveTermRun({
|
|
4636
5416
|
cwd,
|
|
4637
5417
|
inputs: opts.inputs,
|
|
@@ -4677,18 +5457,18 @@ function displayPath(path, cwd) {
|
|
|
4677
5457
|
function valeConfigFor(run) {
|
|
4678
5458
|
if (run.configDir === void 0) return void 0;
|
|
4679
5459
|
const path = valeConfigPath(run.tools, run.configDir);
|
|
4680
|
-
if (path !== void 0 && !
|
|
5460
|
+
if (path !== void 0 && !existsSync2(path)) {
|
|
4681
5461
|
const source = run.configSource ?? "manni.config.yaml";
|
|
4682
5462
|
throw new TermError(`${source}: tools.vale.config "${run.tools.vale?.config ?? path}" does not exist.`);
|
|
4683
5463
|
}
|
|
4684
5464
|
return path;
|
|
4685
5465
|
}
|
|
4686
|
-
function
|
|
5466
|
+
function plural4(n, one, many = `${one}s`) {
|
|
4687
5467
|
return `${String(n)} ${n === 1 ? one : many}`;
|
|
4688
5468
|
}
|
|
4689
5469
|
|
|
4690
5470
|
// src/term/commands/check.ts
|
|
4691
|
-
async function
|
|
5471
|
+
async function runCheck4(opts) {
|
|
4692
5472
|
const { cwd, run, set } = await loadTerms(opts);
|
|
4693
5473
|
const config = run.config;
|
|
4694
5474
|
const findings = checkTermSet(set, {
|
|
@@ -4704,7 +5484,7 @@ async function runCheck3(opts) {
|
|
|
4704
5484
|
cwd,
|
|
4705
5485
|
{ current: DEFAULT_TERM_BASELINE_PATH }
|
|
4706
5486
|
);
|
|
4707
|
-
if (request !== null && !
|
|
5487
|
+
if (request !== null && !existsSync3(request.absPath)) {
|
|
4708
5488
|
if (opts.baseline !== true) {
|
|
4709
5489
|
throw new TermError(
|
|
4710
5490
|
`Baseline "${request.label}" not found. Record one with \`manni term check --baseline\`.`
|
|
@@ -4730,7 +5510,7 @@ async function runCheck3(opts) {
|
|
|
4730
5510
|
import { join } from "node:path";
|
|
4731
5511
|
|
|
4732
5512
|
// src/term/core/writers/page.ts
|
|
4733
|
-
import { parse as
|
|
5513
|
+
import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
|
|
4734
5514
|
function pageMetadata(term) {
|
|
4735
5515
|
const { record } = term;
|
|
4736
5516
|
const metadata = { title: record.label };
|
|
@@ -4757,7 +5537,7 @@ ${stringifyYaml2(metadata, { lineWidth: 0 })}---
|
|
|
4757
5537
|
function metaContent(value) {
|
|
4758
5538
|
if (typeof value === "string" && !value.includes("\n")) {
|
|
4759
5539
|
try {
|
|
4760
|
-
if (
|
|
5540
|
+
if (parseYaml2(value) === value) return value;
|
|
4761
5541
|
} catch {
|
|
4762
5542
|
}
|
|
4763
5543
|
}
|
|
@@ -5088,7 +5868,7 @@ var SCHEME_ID = "";
|
|
|
5088
5868
|
function literal(term, value) {
|
|
5089
5869
|
return term.language === void 0 ? value : { "@value": value, "@language": term.language };
|
|
5090
5870
|
}
|
|
5091
|
-
function
|
|
5871
|
+
function resolve6(terms, self, value) {
|
|
5092
5872
|
const wanted = value.toLowerCase();
|
|
5093
5873
|
const others = terms.filter((t) => t !== self);
|
|
5094
5874
|
return others.find((t) => t.record.label.toLowerCase() === wanted) ?? others.find((t) => t.id.toLowerCase() === wanted);
|
|
@@ -5096,7 +5876,7 @@ function resolve4(terms, self, value) {
|
|
|
5096
5876
|
function references(terms, self, values) {
|
|
5097
5877
|
const ids = [];
|
|
5098
5878
|
for (const value of values ?? []) {
|
|
5099
|
-
const target =
|
|
5879
|
+
const target = resolve6(terms, self, value);
|
|
5100
5880
|
if (target !== void 0 && !ids.includes(target.id)) ids.push(target.id);
|
|
5101
5881
|
}
|
|
5102
5882
|
return ids.map((id) => ({ "@id": `#${id}` }));
|
|
@@ -5493,7 +6273,7 @@ async function runGet(opts) {
|
|
|
5493
6273
|
set.terms.flatMap((t) => [t.id, t.record.label])
|
|
5494
6274
|
);
|
|
5495
6275
|
const hint = nearest === void 0 ? "." : `; did you mean "${nearest}"?`;
|
|
5496
|
-
throw new TermError(`no term "${opts.term}". ${
|
|
6276
|
+
throw new TermError(`no term "${opts.term}". ${plural4(set.terms.length, "term")}${hint}`);
|
|
5497
6277
|
}
|
|
5498
6278
|
|
|
5499
6279
|
// src/term/core/lint.ts
|
|
@@ -5740,15 +6520,15 @@ async function runLint2(opts) {
|
|
|
5740
6520
|
|
|
5741
6521
|
// src/term/commands/list.ts
|
|
5742
6522
|
var LIST_FORMATS2 = ["pretty", "json", "csv"];
|
|
5743
|
-
async function
|
|
6523
|
+
async function runList3(opts) {
|
|
5744
6524
|
const { set } = await loadTerms(opts);
|
|
5745
6525
|
return { terms: set.terms, set };
|
|
5746
6526
|
}
|
|
5747
6527
|
|
|
5748
6528
|
// src/term/commands/write.ts
|
|
5749
|
-
import { existsSync as
|
|
6529
|
+
import { existsSync as existsSync4, statSync } from "node:fs";
|
|
5750
6530
|
import { lstat, mkdir, readdir, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
5751
|
-
import { dirname, extname as extname2, join as join4, resolve as
|
|
6531
|
+
import { dirname, extname as extname2, join as join4, resolve as resolve7 } from "node:path";
|
|
5752
6532
|
var SUGGESTED_SECTION = "*.md";
|
|
5753
6533
|
var STYLE2 = "Terms";
|
|
5754
6534
|
function writeFormats(writers = TERM_WRITERS) {
|
|
@@ -5782,7 +6562,7 @@ async function existingFor(target) {
|
|
|
5782
6562
|
const existing = /* @__PURE__ */ new Map();
|
|
5783
6563
|
if (target.shape === "directory") {
|
|
5784
6564
|
await filesUnder(target.path, existing);
|
|
5785
|
-
} else if (
|
|
6565
|
+
} else if (existsSync4(target.path) && !isDirectory(target.path)) {
|
|
5786
6566
|
existing.set(target.path, await readFile2(target.path, "utf8"));
|
|
5787
6567
|
}
|
|
5788
6568
|
return existing;
|
|
@@ -5802,7 +6582,7 @@ function droppedFrom(format, shape, readers) {
|
|
|
5802
6582
|
async function applyChanges(files, removals, existing, cwd, write) {
|
|
5803
6583
|
const changes = [];
|
|
5804
6584
|
for (const file of files) {
|
|
5805
|
-
const before = existing.get(file.path) ?? (
|
|
6585
|
+
const before = existing.get(file.path) ?? (existsSync4(file.path) ? await readFile2(file.path, "utf8") : void 0);
|
|
5806
6586
|
if (before === file.content) continue;
|
|
5807
6587
|
changes.push({ path: displayPath(file.path, cwd), action: before === void 0 ? "create" : "change" });
|
|
5808
6588
|
if (write) {
|
|
@@ -5811,14 +6591,14 @@ async function applyChanges(files, removals, existing, cwd, write) {
|
|
|
5811
6591
|
}
|
|
5812
6592
|
}
|
|
5813
6593
|
for (const path of removals) {
|
|
5814
|
-
if (!
|
|
6594
|
+
if (!existsSync4(path)) continue;
|
|
5815
6595
|
changes.push({ path: displayPath(path, cwd), action: "remove" });
|
|
5816
6596
|
if (write) await rm2(path, { force: true });
|
|
5817
6597
|
}
|
|
5818
6598
|
return changes;
|
|
5819
6599
|
}
|
|
5820
6600
|
async function runWrite(opts) {
|
|
5821
|
-
const cwd =
|
|
6601
|
+
const cwd = resolve7(opts.cwd ?? process.cwd());
|
|
5822
6602
|
const writers = opts.writers ?? TERM_WRITERS;
|
|
5823
6603
|
const readers = opts.readers ?? TERM_READERS;
|
|
5824
6604
|
const check = opts.check === true;
|
|
@@ -5846,19 +6626,19 @@ async function runWrite(opts) {
|
|
|
5846
6626
|
"Vale found no config file. Set tools.vale.config in manni.config.yaml, or pass -o <styles directory>."
|
|
5847
6627
|
);
|
|
5848
6628
|
}
|
|
5849
|
-
targetPath =
|
|
6629
|
+
targetPath = resolve7(cwd, resolved.stylesPath);
|
|
5850
6630
|
if (!Object.values(resolved.baseStyles).some((styles) => styles.includes(STYLE2))) {
|
|
5851
6631
|
const first = Object.entries(resolved.baseStyles)[0];
|
|
5852
6632
|
const [section, styles] = first ?? [SUGGESTED_SECTION, []];
|
|
5853
6633
|
wiring = {
|
|
5854
|
-
rootIni: displayPath(
|
|
6634
|
+
rootIni: displayPath(resolve7(cwd, resolved.rootIni), cwd),
|
|
5855
6635
|
section,
|
|
5856
6636
|
styles: [...styles, STYLE2],
|
|
5857
6637
|
addSection: first === void 0
|
|
5858
6638
|
};
|
|
5859
6639
|
}
|
|
5860
6640
|
} else {
|
|
5861
|
-
targetPath =
|
|
6641
|
+
targetPath = resolve7(cwd, opts.out);
|
|
5862
6642
|
}
|
|
5863
6643
|
const shape = format === "vale" || /[\\/]$/.test(opts.out ?? "") || isDirectory(targetPath) ? "directory" : "file";
|
|
5864
6644
|
const target = { path: targetPath, shape };
|
|
@@ -6119,7 +6899,7 @@ function renderListPretty(terms) {
|
|
|
6119
6899
|
const rows = terms.map(
|
|
6120
6900
|
(t) => `${t.id.padEnd(idWidth)}${t.record.label.padEnd(labelWidth)}${(t.record["alt-labels"] ?? []).join(", ")}`.trimEnd()
|
|
6121
6901
|
);
|
|
6122
|
-
return [...rows,
|
|
6902
|
+
return [...rows, plural4(terms.length, "term")].join("\n");
|
|
6123
6903
|
}
|
|
6124
6904
|
var GET_FIELDS = [
|
|
6125
6905
|
"alt-labels",
|
|
@@ -6160,21 +6940,21 @@ function severityText(c, severity) {
|
|
|
6160
6940
|
function counts(findings) {
|
|
6161
6941
|
const parts = [];
|
|
6162
6942
|
const n = (severity) => findings.filter((f) => f.severity === severity).length;
|
|
6163
|
-
if (n("error") > 0) parts.push(
|
|
6164
|
-
if (n("warning") > 0) parts.push(
|
|
6165
|
-
if (n("notice") > 0) parts.push(
|
|
6943
|
+
if (n("error") > 0) parts.push(plural4(n("error"), "error"));
|
|
6944
|
+
if (n("warning") > 0) parts.push(plural4(n("warning"), "warning"));
|
|
6945
|
+
if (n("notice") > 0) parts.push(plural4(n("notice"), "notice"));
|
|
6166
6946
|
return parts.join(", ");
|
|
6167
6947
|
}
|
|
6168
6948
|
function renderFindingsPretty(report, opts) {
|
|
6169
6949
|
const c = palette(opts.color);
|
|
6170
6950
|
const { baseline } = report;
|
|
6171
6951
|
if (baseline?.written === true) {
|
|
6172
|
-
return `${c.green("\u2713")} ${
|
|
6952
|
+
return `${c.green("\u2713")} ${plural4(report.found, "finding")} recorded in ${baseline.path}`;
|
|
6173
6953
|
}
|
|
6174
6954
|
const baselined = baseline !== void 0 && baseline.suppressed > 0 ? `, ${String(baseline.suppressed)} baselined` : "";
|
|
6175
|
-
const terms =
|
|
6955
|
+
const terms = plural4(report.terms, "term");
|
|
6176
6956
|
if (report.findings.length === 0) {
|
|
6177
|
-
const references2 = opts.references === false ? "" : `, ${
|
|
6957
|
+
const references2 = opts.references === false ? "" : `, ${plural4(report.references, "reference")}`;
|
|
6178
6958
|
return `${c.green("\u2713")} ${terms}${references2}, no findings${baselined}`;
|
|
6179
6959
|
}
|
|
6180
6960
|
const ruleWidth = Math.max(RULE_WIDTH, ...report.findings.map((f) => f.ruleId.length + 2));
|
|
@@ -6224,11 +7004,11 @@ function valeLines(report) {
|
|
|
6224
7004
|
const acronyms = [];
|
|
6225
7005
|
for (const file of report.files) {
|
|
6226
7006
|
const name = baseName2(file.path);
|
|
6227
|
-
if (CASING_RULES.has(name)) rows.push([name,
|
|
6228
|
-
else if (name === "Deprecated.yml") rows.push([name,
|
|
7007
|
+
if (CASING_RULES.has(name)) rows.push([name, plural4(swapCount(file.content), "label")]);
|
|
7008
|
+
else if (name === "Deprecated.yml") rows.push([name, plural4(swapCount(file.content), "swap")]);
|
|
6229
7009
|
else acronyms.push(name);
|
|
6230
7010
|
}
|
|
6231
|
-
if (acronyms.length > 0) rows.push([acronyms.join(", "),
|
|
7011
|
+
if (acronyms.length > 0) rows.push([acronyms.join(", "), plural4(acronyms.length, "acronym")]);
|
|
6232
7012
|
const width = Math.max(0, ...rows.map(([name]) => name.length)) + 2;
|
|
6233
7013
|
return rows.map(([name, what]) => ` ${name.padEnd(width)}${what}`);
|
|
6234
7014
|
}
|
|
@@ -6241,13 +7021,13 @@ function droppedLine(report) {
|
|
|
6241
7021
|
ids.set(field, set);
|
|
6242
7022
|
}
|
|
6243
7023
|
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));
|
|
6244
|
-
const parts = fields.map(({ field, n }, i) => `${field} on ${i === 0 ?
|
|
7024
|
+
const parts = fields.map(({ field, n }, i) => `${field} on ${i === 0 ? plural4(n, "term") : String(n)}`);
|
|
6245
7025
|
return ` dropped from ${report.droppedFrom ?? report.format ?? "the render"}: ${parts.join(", ")}`;
|
|
6246
7026
|
}
|
|
6247
7027
|
function skippedLine(report) {
|
|
6248
7028
|
if (report.skipped.length === 0) return void 0;
|
|
6249
7029
|
const construct = report.droppedFrom ?? report.format ?? "the render";
|
|
6250
|
-
const count =
|
|
7030
|
+
const count = plural4(report.skipped.length, "term");
|
|
6251
7031
|
return ` skipped ${count} ${construct} cannot read without a definition: ${report.skipped.join(", ")}`;
|
|
6252
7032
|
}
|
|
6253
7033
|
function renderNotes(report) {
|
|
@@ -6265,11 +7045,11 @@ function renderWritePretty(report) {
|
|
|
6265
7045
|
return [...changes, ...renderNotes(report)].join("\n");
|
|
6266
7046
|
}
|
|
6267
7047
|
if (report.mode === "in-place") {
|
|
6268
|
-
return report.changes.length === 0 ? "Nothing to write" : `Wrote ${
|
|
7048
|
+
return report.changes.length === 0 ? "Nothing to write" : `Wrote ${plural4(report.changes.length, "file")}`;
|
|
6269
7049
|
}
|
|
6270
7050
|
const each = report.shape === "directory" && report.format !== "vale" ? ", one file each" : "";
|
|
6271
7051
|
const written = report.terms - report.skipped.length;
|
|
6272
|
-
const lines = [`Wrote ${
|
|
7052
|
+
const lines = [`Wrote ${plural4(written, "term")} to ${report.target ?? ""}${each}`];
|
|
6273
7053
|
if (report.format === "vale") lines.push(...valeLines(report));
|
|
6274
7054
|
lines.push(...renderNotes(report));
|
|
6275
7055
|
return lines.join("\n");
|
|
@@ -6350,8 +7130,8 @@ function renderFindings(report, format, options, references2) {
|
|
|
6350
7130
|
if (text2.length > 0 || !OMITTED_WHEN_CLEAN.has(format)) print(text2);
|
|
6351
7131
|
process.exitCode = report.summary.failed > 0 ? 1 : 0;
|
|
6352
7132
|
}
|
|
6353
|
-
function
|
|
6354
|
-
const program = new
|
|
7133
|
+
function buildProgram8() {
|
|
7134
|
+
const program = new Command7();
|
|
6355
7135
|
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();
|
|
6356
7136
|
withInputs(
|
|
6357
7137
|
program.command("list").description("List the resolved entries").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
@@ -6367,7 +7147,7 @@ function buildProgram7() {
|
|
|
6367
7147
|
).action(async (paths, options) => {
|
|
6368
7148
|
try {
|
|
6369
7149
|
const format = formatChoice(options.format, LIST_FORMATS2);
|
|
6370
|
-
const { terms } = await
|
|
7150
|
+
const { terms } = await runList3(await coreOptions(paths, options));
|
|
6371
7151
|
print(format === "json" ? renderListJson(terms) : format === "csv" ? renderListCsv(terms) : renderListPretty(terms));
|
|
6372
7152
|
process.exitCode = 0;
|
|
6373
7153
|
} catch (err) {
|
|
@@ -6403,7 +7183,7 @@ function buildProgram7() {
|
|
|
6403
7183
|
).action(async (paths, options) => {
|
|
6404
7184
|
try {
|
|
6405
7185
|
const format = formatChoice(options.format, FINDING_FORMATS);
|
|
6406
|
-
const report = await
|
|
7186
|
+
const report = await runCheck4({
|
|
6407
7187
|
...await coreOptions(paths, options),
|
|
6408
7188
|
...options.baseline === true ? { baseline: true } : {}
|
|
6409
7189
|
});
|
|
@@ -6468,13 +7248,376 @@ function buildProgram7() {
|
|
|
6468
7248
|
return program;
|
|
6469
7249
|
}
|
|
6470
7250
|
|
|
7251
|
+
// src/tracevals/cli.ts
|
|
7252
|
+
import { Command as Command8 } from "commander";
|
|
7253
|
+
var { version } = package_default;
|
|
7254
|
+
function buildProgram9() {
|
|
7255
|
+
const program = new Command8();
|
|
7256
|
+
program.name("tracevals").description(
|
|
7257
|
+
"Deterministic and LLM-as-judge adherence evals for AI agent session traces."
|
|
7258
|
+
).version(version).option("--no-color", "disable colored output").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
7259
|
+
function collectGrant(value, previous) {
|
|
7260
|
+
if (!isExecutionGrant(value)) fail(new TracevalsError(allowExecutionMessage(value)));
|
|
7261
|
+
return [...previous, value];
|
|
7262
|
+
}
|
|
7263
|
+
function addConfigFlags(cmd) {
|
|
7264
|
+
return cmd.option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file");
|
|
7265
|
+
}
|
|
7266
|
+
function configOptions(opts) {
|
|
7267
|
+
const { configPath, noConfig } = configOption(opts.config);
|
|
7268
|
+
return {
|
|
7269
|
+
...configPath === void 0 ? {} : { config: configPath },
|
|
7270
|
+
...noConfig === true ? { noConfig } : {}
|
|
7271
|
+
};
|
|
7272
|
+
}
|
|
7273
|
+
function parseFormatArg(name, allowed) {
|
|
7274
|
+
return (value) => {
|
|
7275
|
+
try {
|
|
7276
|
+
return parseFormat2(value, allowed, name);
|
|
7277
|
+
} catch (e) {
|
|
7278
|
+
fail(e);
|
|
7279
|
+
}
|
|
7280
|
+
};
|
|
7281
|
+
}
|
|
7282
|
+
const range = (min, max) => Number.isFinite(max) ? `between ${min} and ${max}` : `of at least ${min}`;
|
|
7283
|
+
function numeric(name, value, min, max = Number.POSITIVE_INFINITY) {
|
|
7284
|
+
if (value === void 0) return;
|
|
7285
|
+
if (Number.isFinite(value) && value >= min && value <= max) return;
|
|
7286
|
+
throw new TracevalsError(
|
|
7287
|
+
`${name} must be a number ${range(min, max)}, got ${value}`
|
|
7288
|
+
);
|
|
7289
|
+
}
|
|
7290
|
+
function whole(name, value, min, max = Number.POSITIVE_INFINITY) {
|
|
7291
|
+
if (value === void 0) return;
|
|
7292
|
+
if (Number.isInteger(value) && value >= min && value <= max) return;
|
|
7293
|
+
throw new TracevalsError(
|
|
7294
|
+
`${name} must be a whole number ${range(min, max)}, got ${value}`
|
|
7295
|
+
);
|
|
7296
|
+
}
|
|
7297
|
+
function sharedRunOptions(opts) {
|
|
7298
|
+
whole("--runs", opts.runs, 1);
|
|
7299
|
+
whole("--limit", opts.limit, 1);
|
|
7300
|
+
whole("--max-turns", opts.maxTurns, 1);
|
|
7301
|
+
return {
|
|
7302
|
+
...configOptions(opts),
|
|
7303
|
+
project: opts.project,
|
|
7304
|
+
provider: opts.provider,
|
|
7305
|
+
model: opts.model,
|
|
7306
|
+
local: opts.local,
|
|
7307
|
+
// `undefined` rather than `false` when absent, so a future config
|
|
7308
|
+
// `offline:` still decides on a run where the flag was never typed.
|
|
7309
|
+
offline: opts.offline ? true : void 0,
|
|
7310
|
+
runs: opts.runs,
|
|
7311
|
+
deterministicOnly: opts.deterministicOnly,
|
|
7312
|
+
noCache: opts.cache === false,
|
|
7313
|
+
maxTurns: opts.maxTurns,
|
|
7314
|
+
format: opts.format ?? "pretty",
|
|
7315
|
+
output: opts.output,
|
|
7316
|
+
// Undefined when neither spelling is passed, so the config still decides.
|
|
7317
|
+
// Both spellings are declared for precisely that reason: commander
|
|
7318
|
+
// defaults a lone `--no-x` to `true`, which makes the config's own value
|
|
7319
|
+
// unreachable from the command line.
|
|
7320
|
+
failOnReview: opts.failOnReview,
|
|
7321
|
+
// Commander's default is `[]`, which is "no flag", not "run nothing": only
|
|
7322
|
+
// a list someone typed narrows the run, and `--no-execution` runs none.
|
|
7323
|
+
...opts.execution === false ? { allowExecution: [] } : (opts.allowExecution ?? []).length > 0 ? { allowExecution: opts.allowExecution } : {},
|
|
7324
|
+
reportUnusedArtifacts: opts.reportUnusedArtifacts,
|
|
7325
|
+
...opts.require !== void 0 ? { require: opts.require } : {},
|
|
7326
|
+
...opts.allProjects !== void 0 ? { allProjects: opts.allProjects } : {},
|
|
7327
|
+
...opts.newerThan !== void 0 ? { newerThan: opts.newerThan } : {},
|
|
7328
|
+
...opts.limit !== void 0 ? { limit: opts.limit } : {}
|
|
7329
|
+
};
|
|
7330
|
+
}
|
|
7331
|
+
async function executeRun(traces, opts, color) {
|
|
7332
|
+
const shared = {
|
|
7333
|
+
...sharedRunOptions(opts),
|
|
7334
|
+
color,
|
|
7335
|
+
// Run-only, both of them: `--history` is a point in one session's
|
|
7336
|
+
// timeline, and a manifest belongs to exactly one session (ADR 01024).
|
|
7337
|
+
history: opts.history,
|
|
7338
|
+
...opts.manifest !== void 0 ? { manifest: opts.manifest } : {}
|
|
7339
|
+
};
|
|
7340
|
+
const selecting = opts.allProjects === true || opts.newerThan !== void 0 || opts.limit !== void 0;
|
|
7341
|
+
if (traces.length > 1 || selecting) {
|
|
7342
|
+
if (opts.manifest !== void 0) {
|
|
7343
|
+
throw new TracevalsError(
|
|
7344
|
+
"--manifest names one session's manifest, so it cannot be used with a corpus; drop it and each trace will find its own"
|
|
7345
|
+
);
|
|
7346
|
+
}
|
|
7347
|
+
const { runBatch } = await import("./batch-7JJWXWSF.js");
|
|
7348
|
+
const { report: report2, rendered: rendered3 } = await runBatch({
|
|
7349
|
+
...shared,
|
|
7350
|
+
...traces.length > 0 ? { traces } : {}
|
|
7351
|
+
});
|
|
7352
|
+
console.log(rendered3);
|
|
7353
|
+
process.exitCode = report2.exitCode;
|
|
7354
|
+
return;
|
|
7355
|
+
}
|
|
7356
|
+
let trace = traces[0];
|
|
7357
|
+
if (trace === void 0) {
|
|
7358
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
7359
|
+
throw new TracevalsError(
|
|
7360
|
+
"no trace given; pass a trace file or run `manni tracevals list`"
|
|
7361
|
+
);
|
|
7362
|
+
}
|
|
7363
|
+
const { pickTrace } = await import("./picker-6HRMETE7.js");
|
|
7364
|
+
trace = await pickTrace();
|
|
7365
|
+
}
|
|
7366
|
+
const { report, rendered: rendered2 } = await runRun2({ ...shared, tracePath: trace });
|
|
7367
|
+
console.log(rendered2);
|
|
7368
|
+
process.exitCode = report.exitCode;
|
|
7369
|
+
}
|
|
7370
|
+
function addRunFlags(cmd, options = {}) {
|
|
7371
|
+
const withHistory = options.history !== false;
|
|
7372
|
+
const formats = options.formats ?? REPORT_FORMATS3;
|
|
7373
|
+
const base = addConfigFlags(cmd).option(
|
|
7374
|
+
"--project <dir>",
|
|
7375
|
+
"artifact-lookup root (overrides the trace's recorded cwd)"
|
|
7376
|
+
).option(
|
|
7377
|
+
"--provider <name>",
|
|
7378
|
+
"Judge provider: auto (default) | anthropic | openai | claude-cli | llama-cpp"
|
|
7379
|
+
).option(
|
|
7380
|
+
"--model <model>",
|
|
7381
|
+
"Model override; needs a named provider, from here or config"
|
|
7382
|
+
).option("--local", LOCAL_FLAG_HELP).option("--runs <n>", "ensemble runs per eval", (v) => Number(v)).option("--deterministic-only", "skip LLM-judged evals").option(
|
|
7383
|
+
"--offline",
|
|
7384
|
+
"never fetch a remote manifest; read external metadata from local manifests"
|
|
7385
|
+
).option("--no-cache", "bypass the judge cache").option(
|
|
7386
|
+
"--max-turns <n>",
|
|
7387
|
+
"Stop after this many ensemble runs (a cached ensemble costs none)",
|
|
7388
|
+
(v) => Number(v)
|
|
7389
|
+
).option(
|
|
7390
|
+
"-f, --format <format>",
|
|
7391
|
+
`Output format: ${formats.join(" | ")}`,
|
|
7392
|
+
parseFormatArg("--format", formats),
|
|
7393
|
+
"pretty"
|
|
7394
|
+
).option("-o, --output <file>", "also write the report to a file").option(
|
|
7395
|
+
"--report-unused-artifacts",
|
|
7396
|
+
"list every skill and agent the session was offered and never used"
|
|
7397
|
+
).option("--fail-on-review", "Exit 1 when any eval lands in the human-review zone (the default)").option("--no-fail-on-review", "Do not fail the run on evals left for human review").option(
|
|
7398
|
+
"--allow-execution <kind>",
|
|
7399
|
+
"Run only these execution grants (repeatable): frontmatter-commands",
|
|
7400
|
+
collectGrant,
|
|
7401
|
+
[]
|
|
7402
|
+
).option("--no-execution", "Clear every execution grant for this run").option(
|
|
7403
|
+
"--require <module>",
|
|
7404
|
+
"load a grader plugin; repeatable, and added to config plugins",
|
|
7405
|
+
collect,
|
|
7406
|
+
[]
|
|
7407
|
+
).option("--all-projects", "evaluate every project's traces in the session store").option(
|
|
7408
|
+
"--newer-than <duration>",
|
|
7409
|
+
"only traces modified within this window, such as 30m, 24h, 7d or 2w"
|
|
7410
|
+
).option("--limit <n>", "maximum traces to evaluate", (v) => Number(v));
|
|
7411
|
+
return withHistory ? base.option(
|
|
7412
|
+
"--history",
|
|
7413
|
+
"append to history and compare with the previous run"
|
|
7414
|
+
) : base;
|
|
7415
|
+
}
|
|
7416
|
+
addRunFlags(
|
|
7417
|
+
// No `isDefault` (proposal 0034): `run` is a verb, so a bare
|
|
7418
|
+
// `manni tracevals` prints the help and exits 2, and a trace path with no
|
|
7419
|
+
// verb is commander's unknown-command refusal rather than a silent run.
|
|
7420
|
+
program.command("run [traces...]").description(
|
|
7421
|
+
"Evaluate one or more traces against the skills and instructions they used"
|
|
7422
|
+
)
|
|
7423
|
+
).option(
|
|
7424
|
+
"--manifest <file>",
|
|
7425
|
+
"session manifest to compare artifacts against; without it one is looked for beside the trace and under the project"
|
|
7426
|
+
).action(async (traces, opts, command) => {
|
|
7427
|
+
try {
|
|
7428
|
+
await executeRun(traces, opts, colorFor(command, process.stdout.isTTY));
|
|
7429
|
+
} catch (e) {
|
|
7430
|
+
fail(e);
|
|
7431
|
+
}
|
|
7432
|
+
});
|
|
7433
|
+
addRunFlags(
|
|
7434
|
+
program.command("calibrate [traces...]").description(
|
|
7435
|
+
"Measure judged and graded verdicts against a labels file: false passes, false fails, and review volume"
|
|
7436
|
+
).option(
|
|
7437
|
+
"--labels <file>",
|
|
7438
|
+
"calibration labels sidecar (default: calibrate.labels)"
|
|
7439
|
+
).option(
|
|
7440
|
+
"--sweep",
|
|
7441
|
+
"re-score the corpus across the configured grid of zones and ensemble sizes, from cached verdicts"
|
|
7442
|
+
).option(
|
|
7443
|
+
"--max-false-pass <n>",
|
|
7444
|
+
"exit 1 above this many false passes",
|
|
7445
|
+
(v) => Number(v)
|
|
7446
|
+
).option(
|
|
7447
|
+
"--max-false-fail <n>",
|
|
7448
|
+
"exit 1 above this many false fails",
|
|
7449
|
+
(v) => Number(v)
|
|
7450
|
+
).option(
|
|
7451
|
+
"--max-review <n>",
|
|
7452
|
+
"exit 1 above this many needs-review outcomes",
|
|
7453
|
+
(v) => Number(v)
|
|
7454
|
+
),
|
|
7455
|
+
{ history: false, formats: CALIBRATE_FORMATS }
|
|
7456
|
+
).action(
|
|
7457
|
+
async (traces, opts, command) => {
|
|
7458
|
+
try {
|
|
7459
|
+
const shared = {
|
|
7460
|
+
...sharedRunOptions(opts),
|
|
7461
|
+
color: colorFor(command, process.stdout.isTTY)
|
|
7462
|
+
};
|
|
7463
|
+
whole("--max-false-pass", opts.maxFalsePass, 0);
|
|
7464
|
+
whole("--max-false-fail", opts.maxFalseFail, 0);
|
|
7465
|
+
whole("--max-review", opts.maxReview, 0);
|
|
7466
|
+
const { runCalibrate: runCalibrate2 } = await import("./calibrate-IA2ARFMH.js");
|
|
7467
|
+
const { report, rendered: rendered2 } = await runCalibrate2({
|
|
7468
|
+
...shared,
|
|
7469
|
+
...traces.length > 0 ? { traces } : {},
|
|
7470
|
+
...opts.labels !== void 0 ? { labels: opts.labels } : {},
|
|
7471
|
+
...opts.sweep !== void 0 ? { sweep: opts.sweep } : {},
|
|
7472
|
+
...opts.maxFalsePass !== void 0 ? { maxFalsePass: opts.maxFalsePass } : {},
|
|
7473
|
+
...opts.maxFalseFail !== void 0 ? { maxFalseFail: opts.maxFalseFail } : {},
|
|
7474
|
+
...opts.maxReview !== void 0 ? { maxReview: opts.maxReview } : {}
|
|
7475
|
+
});
|
|
7476
|
+
console.log(rendered2);
|
|
7477
|
+
process.exitCode = report.exitCode;
|
|
7478
|
+
} catch (e) {
|
|
7479
|
+
fail(e);
|
|
7480
|
+
}
|
|
7481
|
+
}
|
|
7482
|
+
);
|
|
7483
|
+
addConfigFlags(
|
|
7484
|
+
program.command("fill [paths...]").description(
|
|
7485
|
+
"Propose evals for skills, agent definitions, and project rules, and write those above the confidence threshold"
|
|
7486
|
+
)
|
|
7487
|
+
).option("--project <dir>", "project root to scan (default: current directory)").option("--exclude <glob>", "Glob to exclude (repeatable)", collect, []).option("--dry-run", "report proposals without writing them").option(
|
|
7488
|
+
"--confidence <n>",
|
|
7489
|
+
"minimum confidence to write (0-1)",
|
|
7490
|
+
(v) => parseFloat(v)
|
|
7491
|
+
).option(
|
|
7492
|
+
"--max-evals <n>",
|
|
7493
|
+
"maximum evals per artifact, including existing ones",
|
|
7494
|
+
(v) => Number(v)
|
|
7495
|
+
).option(
|
|
7496
|
+
"--max-turns <n>",
|
|
7497
|
+
"Stop after this many inference calls (a cached artifact costs none)",
|
|
7498
|
+
(v) => Number(v)
|
|
7499
|
+
).option("--no-cache", "bypass the proposal cache").option(
|
|
7500
|
+
"--provider <name>",
|
|
7501
|
+
"Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp"
|
|
7502
|
+
).option(
|
|
7503
|
+
"--model <model>",
|
|
7504
|
+
"Model override; needs a named provider, from here or config"
|
|
7505
|
+
).option("--local", LOCAL_FLAG_HELP).option(
|
|
7506
|
+
"--offline",
|
|
7507
|
+
"never fetch a remote manifest; read external metadata from local manifests"
|
|
7508
|
+
).option(
|
|
7509
|
+
"--require <module>",
|
|
7510
|
+
"load a grader plugin; repeatable, and added to config plugins",
|
|
7511
|
+
collect,
|
|
7512
|
+
[]
|
|
7513
|
+
).option(
|
|
7514
|
+
"-f, --format <format>",
|
|
7515
|
+
`Output format: ${SUMMARY_FORMATS2.join(" | ")}`,
|
|
7516
|
+
parseFormatArg("--format", SUMMARY_FORMATS2),
|
|
7517
|
+
"pretty"
|
|
7518
|
+
).action(async (paths, opts, command) => {
|
|
7519
|
+
try {
|
|
7520
|
+
numeric("--confidence", opts.confidence, 0, 1);
|
|
7521
|
+
whole("--max-evals", opts.maxEvals, 1);
|
|
7522
|
+
whole("--max-turns", opts.maxTurns, 1);
|
|
7523
|
+
const { report, rendered: rendered2 } = await runFill3({
|
|
7524
|
+
...configOptions(opts),
|
|
7525
|
+
...paths.length > 0 ? { paths } : {},
|
|
7526
|
+
...opts.project !== void 0 ? { project: opts.project } : {},
|
|
7527
|
+
...opts.exclude !== void 0 ? { exclude: opts.exclude } : {},
|
|
7528
|
+
...opts.dryRun !== void 0 ? { dryRun: opts.dryRun } : {},
|
|
7529
|
+
...opts.confidence !== void 0 ? { confidence: opts.confidence } : {},
|
|
7530
|
+
...opts.maxEvals !== void 0 ? { maxEvals: opts.maxEvals } : {},
|
|
7531
|
+
...opts.maxTurns !== void 0 ? { maxTurns: opts.maxTurns } : {},
|
|
7532
|
+
...opts.cache === false ? { noCache: true } : {},
|
|
7533
|
+
...opts.provider !== void 0 ? { provider: opts.provider } : {},
|
|
7534
|
+
...opts.model !== void 0 ? { model: opts.model } : {},
|
|
7535
|
+
...opts.local !== void 0 ? { local: opts.local } : {},
|
|
7536
|
+
...opts.offline === true ? { offline: true } : {},
|
|
7537
|
+
...opts.require !== void 0 ? { require: opts.require } : {},
|
|
7538
|
+
// P1 (proposal 0047): offered only on a terminal, where there is
|
|
7539
|
+
// someone to answer. Off one the block goes to the artifact and the
|
|
7540
|
+
// W1 line says so.
|
|
7541
|
+
confirm: terminalConfirm(),
|
|
7542
|
+
onNotice: notice,
|
|
7543
|
+
onRelocated: (result) => {
|
|
7544
|
+
const manifests = result.manifests.map((m) => m.file).join(", ");
|
|
7545
|
+
notice(`moved ${String(result.summary.moved)} value(s) into ${manifests}.`);
|
|
7546
|
+
},
|
|
7547
|
+
color: colorFor(command, process.stdout.isTTY)
|
|
7548
|
+
});
|
|
7549
|
+
console.log(
|
|
7550
|
+
opts.format === "json" ? JSON.stringify(report, null, 2) : rendered2
|
|
7551
|
+
);
|
|
7552
|
+
process.exitCode = report.exitCode;
|
|
7553
|
+
} catch (e) {
|
|
7554
|
+
fail(e);
|
|
7555
|
+
}
|
|
7556
|
+
});
|
|
7557
|
+
addConfigFlags(
|
|
7558
|
+
program.command("capture").description(
|
|
7559
|
+
"Record a session manifest: sha256 of every instruction artifact plus the git SHA, read from a Claude Code hook payload on stdin"
|
|
7560
|
+
)
|
|
7561
|
+
).option("--project <dir>", "project root to scan (default: the payload's cwd)").option("--session-id <id>", "session id (default: the payload's session_id)").option("-o, --out <file>", "write here instead of the configured directory").option(
|
|
7562
|
+
"-f, --format <format>",
|
|
7563
|
+
`Output format: ${SUMMARY_FORMATS2.join(" | ")}`,
|
|
7564
|
+
parseFormatArg("--format", SUMMARY_FORMATS2),
|
|
7565
|
+
"pretty"
|
|
7566
|
+
).action(async (opts) => {
|
|
7567
|
+
try {
|
|
7568
|
+
const { runCapture } = await import("./capture-C32JXIFN.js");
|
|
7569
|
+
const result = await runCapture({
|
|
7570
|
+
version,
|
|
7571
|
+
...configOptions(opts),
|
|
7572
|
+
...opts.project !== void 0 ? { project: opts.project } : {},
|
|
7573
|
+
...opts.sessionId !== void 0 ? { sessionId: opts.sessionId } : {},
|
|
7574
|
+
...opts.out !== void 0 ? { out: opts.out } : {},
|
|
7575
|
+
format: opts.format ?? "pretty"
|
|
7576
|
+
});
|
|
7577
|
+
if (result.stdout !== "") console.log(result.stdout);
|
|
7578
|
+
if (result.stderr !== "") notice(result.stderr);
|
|
7579
|
+
process.exitCode = result.exitCode;
|
|
7580
|
+
} catch (e) {
|
|
7581
|
+
fail(e);
|
|
7582
|
+
}
|
|
7583
|
+
});
|
|
7584
|
+
program.command("list").description("List discoverable traces (Claude Code session files)").option(
|
|
7585
|
+
"--project <dir>",
|
|
7586
|
+
"project directory to scope to (default: current directory)"
|
|
7587
|
+
).option("--all-projects", "scan every project in the session store").option(
|
|
7588
|
+
"--newer-than <duration>",
|
|
7589
|
+
"only traces modified within this window, such as 30m, 24h, 7d or 2w"
|
|
7590
|
+
).option("--limit <n>", "maximum traces to list", (v) => Number(v)).option(
|
|
7591
|
+
"-f, --format <format>",
|
|
7592
|
+
`Output format: ${SUMMARY_FORMATS2.join(" | ")}`,
|
|
7593
|
+
parseFormatArg("--format", SUMMARY_FORMATS2),
|
|
7594
|
+
"pretty"
|
|
7595
|
+
).action(async (opts, command) => {
|
|
7596
|
+
try {
|
|
7597
|
+
whole("--limit", opts.limit, 1);
|
|
7598
|
+
const run = await runList2({
|
|
7599
|
+
project: opts.project,
|
|
7600
|
+
allProjects: opts.allProjects,
|
|
7601
|
+
...opts.newerThan !== void 0 ? { newerThan: opts.newerThan } : {},
|
|
7602
|
+
limit: opts.limit
|
|
7603
|
+
});
|
|
7604
|
+
console.log(
|
|
7605
|
+
opts.format === "json" ? JSON.stringify(run, null, 2) : renderList2(run, { color: colorFor(command, process.stdout.isTTY) })
|
|
7606
|
+
);
|
|
7607
|
+
} catch (e) {
|
|
7608
|
+
fail(e);
|
|
7609
|
+
}
|
|
7610
|
+
});
|
|
7611
|
+
return program;
|
|
7612
|
+
}
|
|
7613
|
+
|
|
6471
7614
|
// src/cli.ts
|
|
6472
|
-
function
|
|
7615
|
+
function buildProgram10() {
|
|
6473
7616
|
const meta = buildProgram().name("meta").description(
|
|
6474
7617
|
"Validate, read, query and fill document metadata against JSON Schema"
|
|
6475
7618
|
);
|
|
6476
7619
|
const metaCommands = new Set(meta.commands.map((c) => c.name()));
|
|
6477
|
-
const program = new
|
|
7620
|
+
const program = new Command9();
|
|
6478
7621
|
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({
|
|
6479
7622
|
outputError: (str, write) => {
|
|
6480
7623
|
write(str);
|
|
@@ -6500,7 +7643,7 @@ function buildProgram8() {
|
|
|
6500
7643
|
)
|
|
6501
7644
|
);
|
|
6502
7645
|
program.addCommand(
|
|
6503
|
-
|
|
7646
|
+
buildProgram7().name("lint").description(
|
|
6504
7647
|
"Validate document structure against doctype templates, routed by a page's type"
|
|
6505
7648
|
)
|
|
6506
7649
|
);
|
|
@@ -6510,15 +7653,25 @@ function buildProgram8() {
|
|
|
6510
7653
|
)
|
|
6511
7654
|
);
|
|
6512
7655
|
program.addCommand(
|
|
6513
|
-
|
|
7656
|
+
buildProgram8().name("term").description("Check, lint and render a docset's terms and the references into them")
|
|
7657
|
+
);
|
|
7658
|
+
program.addCommand(
|
|
7659
|
+
buildProgram6().name("graph").description(
|
|
7660
|
+
"Deterministic knowledge graphs derived from documentation frontmatter and formatting"
|
|
7661
|
+
)
|
|
7662
|
+
);
|
|
7663
|
+
program.addCommand(
|
|
7664
|
+
buildProgram9().name("tracevals").description(
|
|
7665
|
+
"Deterministic and LLM-as-judge adherence evals for AI agent session traces"
|
|
7666
|
+
)
|
|
6514
7667
|
);
|
|
6515
7668
|
program.addCommand(
|
|
6516
7669
|
buildProgram5().name("key").description("Set and rotate the family key that encrypted values are encrypted with.")
|
|
6517
7670
|
);
|
|
6518
7671
|
return program;
|
|
6519
7672
|
}
|
|
6520
|
-
runIfMain(import.meta.url,
|
|
7673
|
+
runIfMain(import.meta.url, buildProgram10);
|
|
6521
7674
|
export {
|
|
6522
|
-
|
|
7675
|
+
buildProgram10 as buildProgram
|
|
6523
7676
|
};
|
|
6524
7677
|
//# sourceMappingURL=cli.js.map
|