@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.
Files changed (51) hide show
  1. package/README.md +2 -2
  2. package/dist/batch-7JJWXWSF.js +18 -0
  3. package/dist/batch-7JJWXWSF.js.map +1 -0
  4. package/dist/calibrate-IA2ARFMH.js +13 -0
  5. package/dist/calibrate-IA2ARFMH.js.map +1 -0
  6. package/dist/capture-C32JXIFN.js +15 -0
  7. package/dist/capture-C32JXIFN.js.map +1 -0
  8. package/dist/chunk-3R23C5DT.js +120 -0
  9. package/dist/chunk-3R23C5DT.js.map +1 -0
  10. package/dist/chunk-CI2GOU3A.js +4447 -0
  11. package/dist/chunk-CI2GOU3A.js.map +1 -0
  12. package/dist/{chunk-I57KBF45.js → chunk-DP2ZA3WH.js} +1203 -142
  13. package/dist/chunk-DP2ZA3WH.js.map +1 -0
  14. package/dist/chunk-ESJ5W7EV.js +297 -0
  15. package/dist/chunk-ESJ5W7EV.js.map +1 -0
  16. package/dist/chunk-IOYXPBCL.js +37 -0
  17. package/dist/chunk-IOYXPBCL.js.map +1 -0
  18. package/dist/chunk-KTSJ2ZHA.js +40 -0
  19. package/dist/chunk-KTSJ2ZHA.js.map +1 -0
  20. package/dist/{chunk-JODRT4NF.js → chunk-MFDD3YGU.js} +8 -37
  21. package/dist/chunk-MFDD3YGU.js.map +1 -0
  22. package/dist/chunk-MOQXWXSG.js +1941 -0
  23. package/dist/chunk-MOQXWXSG.js.map +1 -0
  24. package/dist/{chunk-S3NDEIOW.js → chunk-O6RVQKK6.js} +10502 -3017
  25. package/dist/chunk-O6RVQKK6.js.map +1 -0
  26. package/dist/chunk-QROCFW5S.js +174 -0
  27. package/dist/chunk-QROCFW5S.js.map +1 -0
  28. package/dist/chunk-RVVCA4WL.js +379 -0
  29. package/dist/chunk-RVVCA4WL.js.map +1 -0
  30. package/dist/cli.js +1293 -140
  31. package/dist/cli.js.map +1 -1
  32. package/dist/docmeta.js +3 -2
  33. package/dist/docmeta.js.map +1 -1
  34. package/dist/graph/embed.d.ts +154 -0
  35. package/dist/graph/embed.js +137 -0
  36. package/dist/graph/embed.js.map +1 -0
  37. package/dist/graph/runtime.d.ts +751 -0
  38. package/dist/graph/runtime.js +3067 -0
  39. package/dist/graph/runtime.js.map +1 -0
  40. package/dist/index.d.ts +6149 -799
  41. package/dist/index.js +682 -7
  42. package/dist/index.js.map +1 -1
  43. package/dist/picker-6HRMETE7.js +12 -0
  44. package/dist/picker-6HRMETE7.js.map +1 -0
  45. package/package.json +32 -4
  46. package/plugin/tracevals/.claude-plugin/plugin.json +9 -0
  47. package/plugin/tracevals/hooks/hooks.json +17 -0
  48. package/shapes/graph/shapes-1.0.0.ttl +689 -0
  49. package/dist/chunk-I57KBF45.js.map +0 -1
  50. package/dist/chunk-JODRT4NF.js.map +0 -1
  51. 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
- collect,
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
- EXECUTION_GRANTS,
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
- loadConfig2 as loadConfig,
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
- } from "./chunk-S3NDEIOW.js";
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
- errorMessage,
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
- } from "./chunk-I57KBF45.js";
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 Command7 } from "commander";
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
- if (opts.exclude?.matches(url) === true) {
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 (index === 1) progress({ kind: "browser" });
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 { pages, discovered: frontier.length, skipped: skipped2, duplicates, excluded: excluded.size };
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.goto(url, { waitUntil: "load", timeout: opts.timeout });
600
- } catch (err) {
601
- throw new A11yError(`Could not load ${url}: ${reason(err)}`);
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 colorFor(command, isTTY, env) {
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: colorFor(command, process.stdout.isTTY),
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: colorFor(command, process.stdout.isTTY),
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: colorFor(command, process.stdout.isTTY),
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 (!EXECUTION_GRANTS.includes(value)) {
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: colorFor2(command, process.stdout.isTTY)
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
- "Grant content-authored execution: frontmatter-commands",
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
- allowExecution: opts.allowExecution,
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: colorFor2(command, process.stdout.isTTY)
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(colorFor2(command, process.stdout.isTTY));
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: colorFor2(command, process.stdout.isTTY)
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(colorFor2(command, process.stdout.isTTY));
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 = colorFor2(command, process.stdout.isTTY);
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: colorFor2(command, process.stdout.isTTY)
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/lint/cli.ts
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 buildProgram6() {
2275
- const program = new Command5();
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 Command6 } from "commander";
3254
+ import { Command as Command7 } from "commander";
2475
3255
 
2476
3256
  // src/term/commands/check.ts
2477
- import { existsSync as existsSync2 } from "node:fs";
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 STDIN = "-";
3440
+ var STDIN2 = "-";
2661
3441
  async function resolveTermRun(opts) {
2662
- const cwd = resolve(opts.cwd);
3442
+ const cwd = resolve3(opts.cwd);
2663
3443
  const wanted = opts.collection ?? [];
2664
- if (wanted.length > 0 && opts.inputs.some((input) => input !== STDIN)) {
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: resolve(loaded.dir, written), written })) : [];
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 resolve3 } from "node:path";
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 resolve2 } from "node:path";
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 = resolve2(run.base, file);
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 = resolve3(opts.cwd ?? process.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 && !existsSync(path)) {
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 plural3(n, one, many = `${one}s`) {
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 runCheck3(opts) {
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 && !existsSync2(request.absPath)) {
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 parseYaml, stringify as stringifyYaml2 } from "yaml";
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 (parseYaml(value) === value) return value;
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 resolve4(terms, self, value) {
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 = resolve4(terms, self, value);
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}". ${plural3(set.terms.length, "term")}${hint}`);
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 runList2(opts) {
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 existsSync3, statSync } from "node:fs";
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 resolve5 } from "node:path";
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 (existsSync3(target.path) && !isDirectory(target.path)) {
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) ?? (existsSync3(file.path) ? await readFile2(file.path, "utf8") : void 0);
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 (!existsSync3(path)) continue;
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 = resolve5(opts.cwd ?? process.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 = resolve5(cwd, resolved.stylesPath);
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(resolve5(cwd, resolved.rootIni), cwd),
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 = resolve5(cwd, opts.out);
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, plural3(terms.length, "term")].join("\n");
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(plural3(n("error"), "error"));
6164
- if (n("warning") > 0) parts.push(plural3(n("warning"), "warning"));
6165
- if (n("notice") > 0) parts.push(plural3(n("notice"), "notice"));
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")} ${plural3(report.found, "finding")} recorded in ${baseline.path}`;
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 = plural3(report.terms, "term");
6955
+ const terms = plural4(report.terms, "term");
6176
6956
  if (report.findings.length === 0) {
6177
- const references2 = opts.references === false ? "" : `, ${plural3(report.references, "reference")}`;
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, plural3(swapCount(file.content), "label")]);
6228
- else if (name === "Deprecated.yml") rows.push([name, plural3(swapCount(file.content), "swap")]);
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(", "), plural3(acronyms.length, "acronym")]);
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 ? plural3(n, "term") : String(n)}`);
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 = plural3(report.skipped.length, "term");
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 ${plural3(report.changes.length, "file")}`;
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 ${plural3(written, "term")} to ${report.target ?? ""}${each}`];
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 buildProgram7() {
6354
- const program = new Command6();
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 runList2(await coreOptions(paths, options));
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 runCheck3({
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 buildProgram8() {
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 Command7();
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
- buildProgram6().name("lint").description(
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
- buildProgram7().name("term").description("Check, lint and render a docset's terms and the references into them")
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, buildProgram8);
7673
+ runIfMain(import.meta.url, buildProgram10);
6521
7674
  export {
6522
- buildProgram8 as buildProgram
7675
+ buildProgram10 as buildProgram
6523
7676
  };
6524
7677
  //# sourceMappingURL=cli.js.map