@nimiplatform/nimi-coding 0.3.0 → 0.4.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 (118) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/CONTRIBUTING.md +3 -3
  3. package/README.md +285 -41
  4. package/README.zh-CN.md +294 -31
  5. package/bin/nimicoding.mjs +1 -1
  6. package/cli/commands/authority.mjs +849 -0
  7. package/cli/commands/clear.mjs +27 -7
  8. package/cli/commands/doctor.mjs +10 -1
  9. package/cli/commands/start.mjs +23 -27
  10. package/cli/commands/sync.mjs +21 -101
  11. package/cli/constants.mjs +7 -31
  12. package/cli/dev/check-authority-surface.mjs +173 -0
  13. package/cli/dev/check-product-conformance.mjs +917 -0
  14. package/cli/help.mjs +10 -17
  15. package/cli/index.mjs +6 -47
  16. package/cli/lib/authority/anchors.mjs +491 -0
  17. package/cli/lib/authority/audit.mjs +490 -0
  18. package/cli/lib/authority/closed-sets.mjs +646 -0
  19. package/cli/lib/authority/compile.mjs +110 -0
  20. package/cli/lib/authority/consumers.mjs +470 -0
  21. package/cli/lib/authority/diagnostics.mjs +87 -0
  22. package/cli/lib/authority/diff.mjs +287 -0
  23. package/cli/lib/authority/discover.mjs +447 -0
  24. package/cli/lib/authority/evidence-snapshot.mjs +1 -0
  25. package/cli/lib/authority/evidence.mjs +1432 -0
  26. package/cli/lib/authority/format.mjs +197 -0
  27. package/cli/lib/authority/git-snapshot.mjs +1065 -0
  28. package/cli/lib/authority/graph.mjs +351 -0
  29. package/cli/lib/authority/impact.mjs +252 -0
  30. package/cli/lib/authority/lexicon.mjs +55 -0
  31. package/cli/lib/authority/query.mjs +162 -0
  32. package/cli/lib/authority/repository-corpus.mjs +37 -0
  33. package/cli/lib/authority/repository-inventory.mjs +34 -0
  34. package/cli/lib/authority/review.mjs +222 -0
  35. package/cli/lib/authority/sarif.mjs +181 -0
  36. package/cli/lib/authority/scope-bindings.mjs +225 -0
  37. package/cli/lib/authority/source-map.mjs +105 -0
  38. package/cli/lib/authority/source-markdown.mjs +167 -0
  39. package/cli/lib/authority/source-yaml.mjs +312 -0
  40. package/cli/lib/authority/surface.mjs +79 -0
  41. package/cli/lib/authority/terms.mjs +421 -0
  42. package/cli/lib/authority/tracked-text.mjs +70 -0
  43. package/cli/lib/authority/validate.mjs +308 -0
  44. package/cli/lib/bootstrap.mjs +55 -71
  45. package/cli/lib/doctor.mjs +40 -123
  46. package/cli/lib/entrypoints.mjs +103 -105
  47. package/cli/lib/fs-helpers.mjs +139 -15
  48. package/cli/lib/internal/governance/config.mjs +0 -36
  49. package/cli/lib/shared.mjs +8 -21
  50. package/cli/lib/sync.mjs +81 -92
  51. package/cli/lib/value-helpers.mjs +0 -26
  52. package/cli/lib/yaml-helpers.mjs +1 -124
  53. package/cli/seeds/bootstrap.mjs +31 -87
  54. package/cli/seeds/seed-policy.yaml +9 -63
  55. package/contracts/authority-evidence-bindings.schema.yaml +53 -0
  56. package/contracts/authority-evidence-probe-results.schema.yaml +38 -0
  57. package/contracts/authority-impact-dispositions.schema.yaml +13 -0
  58. package/contracts/authority-scope-bindings.schema.yaml +15 -0
  59. package/contracts/authority-source.schema.yaml +64 -0
  60. package/contracts/authority-verifier-bindings.schema.yaml +20 -0
  61. package/methodology/authority-authoring.yaml +144 -0
  62. package/methodology/core.yaml +31 -26
  63. package/package.json +10 -6
  64. package/spec/authority-anchors.yaml +99 -0
  65. package/spec/authority-audit.yaml +85 -0
  66. package/spec/authority-authoring.yaml +150 -0
  67. package/spec/authority-closed-sets.yaml +65 -0
  68. package/spec/authority-consumers.yaml +70 -0
  69. package/spec/authority-context.yaml +50 -0
  70. package/spec/authority-discovery.yaml +119 -0
  71. package/spec/authority-evidence.yaml +200 -0
  72. package/spec/authority-graph.yaml +59 -0
  73. package/spec/authority-impact.yaml +84 -0
  74. package/spec/authority-review.yaml +89 -0
  75. package/spec/authority-terms.yaml +59 -0
  76. package/spec/product-scope.yaml +36 -23
  77. package/cli/commands/blueprint-audit.mjs +0 -91
  78. package/cli/commands/classify-spec-tree.mjs +0 -5
  79. package/cli/commands/generate-spec-derived-docs.mjs +0 -151
  80. package/cli/commands/generate-spec-migration-plan.mjs +0 -30
  81. package/cli/commands/surface-validator-command.mjs +0 -49
  82. package/cli/commands/validate-domain-admission.mjs +0 -5
  83. package/cli/commands/validate-guidance-bodies.mjs +0 -5
  84. package/cli/commands/validate-placement.mjs +0 -5
  85. package/cli/commands/validate-projection-edges.mjs +0 -5
  86. package/cli/commands/validate-spec-audit.mjs +0 -27
  87. package/cli/commands/validate-spec-governance.mjs +0 -124
  88. package/cli/commands/validate-spec-tree.mjs +0 -27
  89. package/cli/commands/validate-table-family.mjs +0 -5
  90. package/cli/commands/validate-tracked-output-admission.mjs +0 -5
  91. package/cli/lib/blueprint-audit.mjs +0 -363
  92. package/cli/lib/contracts.mjs +0 -21
  93. package/cli/lib/internal/contracts-loaders.mjs +0 -47
  94. package/cli/lib/internal/contracts-parse.mjs +0 -192
  95. package/cli/lib/internal/governance/runner.mjs +0 -35
  96. package/cli/lib/internal/surface-taxonomy-validators.mjs +0 -1147
  97. package/cli/lib/internal/validators-shared.mjs +0 -12
  98. package/cli/lib/internal/validators-spec-helpers.mjs +0 -52
  99. package/cli/lib/internal/validators-spec.mjs +0 -380
  100. package/cli/lib/validators.mjs +0 -30
  101. package/config/bootstrap.yaml +0 -6
  102. package/config/spec-generation-inputs.yaml +0 -41
  103. package/contracts/domain-admission.schema.yaml +0 -56
  104. package/contracts/migration-inventory.schema.yaml +0 -15
  105. package/contracts/negative-fixtures.yaml +0 -26
  106. package/contracts/placement-contract.schema.yaml +0 -127
  107. package/contracts/projection-edge.schema.yaml +0 -46
  108. package/contracts/shared-enums.yaml +0 -63
  109. package/contracts/spec-generation-audit.schema.yaml +0 -46
  110. package/contracts/spec-generation-inputs.schema.yaml +0 -43
  111. package/contracts/spec-layout.schema.yaml +0 -32
  112. package/contracts/surface-taxonomy.schema.yaml +0 -139
  113. package/contracts/table-family.schema.yaml +0 -133
  114. package/contracts/tracked-output-admission.schema.yaml +0 -17
  115. package/methodology/four-closure-policy.yaml +0 -20
  116. package/methodology/role-separation-policy.yaml +0 -22
  117. package/methodology/spec-reconstruction.yaml +0 -69
  118. package/spec/_meta/spec-tree-model.yaml +0 -44
@@ -0,0 +1,849 @@
1
+ import { VERSION } from "../constants.mjs";
2
+ import {
3
+ anchorsAuthorityRepository,
4
+ ANCHORS_FORMAT,
5
+ AUTHORITY_ANCHOR_GRAMMAR_HELP,
6
+ refuseAnchorsOutputBudget,
7
+ } from "../lib/authority/anchors.mjs";
8
+ import { auditAuthorityPath } from "../lib/authority/audit.mjs";
9
+ import { checkAuthorityPath, compileAuthorityPath } from "../lib/authority/compile.mjs";
10
+ import { closedSetsAuthorityRepository } from "../lib/authority/closed-sets.mjs";
11
+ import { consumersAuthorityRepository } from "../lib/authority/consumers.mjs";
12
+ import { diffAuthorityPaths } from "../lib/authority/diff.mjs";
13
+ import { discoverAuthorityPath } from "../lib/authority/discover.mjs";
14
+ import { evidenceAuthorityRepository } from "../lib/authority/evidence.mjs";
15
+ import { AuthorityInputError, formatAuthorityFile } from "../lib/authority/format.mjs";
16
+ import { pathAuthorityPath, refsAuthorityPath, subgraphAuthorityPath } from "../lib/authority/graph.mjs";
17
+ import { impactAuthorityPaths } from "../lib/authority/impact.mjs";
18
+ import { contextAuthorityPath, queryAuthorityPath } from "../lib/authority/query.mjs";
19
+ import { reviewAuthorityRepository } from "../lib/authority/review.mjs";
20
+ import { authorityAuditResultToSarif } from "../lib/authority/sarif.mjs";
21
+ import { loadAuthorityContract } from "../lib/authority/source-yaml.mjs";
22
+ import { AUTHORING_SURFACE, AUTHORITY_SURFACE, integerFlagAdmission, booleanFlagAdmission } from "../lib/authority/surface.mjs";
23
+ import { AUTHORITY_TERM_GRAMMAR_HELP, termsAuthorityRepository } from "../lib/authority/terms.mjs";
24
+
25
+ const AUTHORITY_SOURCE_CONTRACT = await loadAuthorityContract();
26
+ const ADMITTED_AUTHORITY_FORMATS = new Set([
27
+ AUTHORITY_SOURCE_CONTRACT.format,
28
+ AUTHORITY_SOURCE_CONTRACT.migration_inputs?.format,
29
+ ].filter((format) => typeof format === "string"));
30
+
31
+ // The single source for the authority command list; the root help renders it
32
+ // rather than keeping a second copy that drifts.
33
+ export const AUTHORITY_COMMAND_USAGE = [
34
+ "nimicoding authority fmt <file> [--check] [--upgrade] [--json]",
35
+ "nimicoding authority check <path> [--scope-bindings <file>] [--require-format <format>] [--json]",
36
+ "nimicoding authority compile <path> [--json]",
37
+ "nimicoding authority anchors <repository-path> --spec <corpus-path> [--scope-bindings <file>] [--embedded-paths] --max-units <positive-safe-integer> --max-anchors <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
38
+ "nimicoding authority consumers <repository-path> --spec <corpus-path> [--external-root <scheme>=<path>]... --max-files <positive-safe-integer> --max-refs <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
39
+ "nimicoding authority closed-sets <repository-path> --spec <corpus-path> --max-units <positive-safe-integer> --max-sets <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
40
+ "nimicoding authority terms <repository-path> --spec <corpus-path> [--term <t>] [--code] --max-units <positive-safe-integer> --max-terms <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
41
+ "nimicoding authority discover <path> <query> [--kind <definition|rule>] [--owner <exact-owner>] [--scope <exact-scope>] [--lifecycle <active|removed>] --max-candidates <positive-safe-integer> --max-snippet-terms <positive-safe-integer> --max-bytes <positive-safe-integer> [--preview-direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --max-edges <positive-safe-integer>] [--json]",
42
+ "nimicoding authority query <path> <id> --max-bytes <positive-integer> [--json]",
43
+ "nimicoding authority context <path> <id> --max-units <positive-integer> --max-bytes <positive-integer> [--json]",
44
+ "nimicoding authority refs <path> <id> --direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
45
+ "nimicoding authority path <path> <from-id> <to-id> --traversal <directed|incidence> --relations <comma-separated-relation-types> --max-hops <positive-safe-integer> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
46
+ "nimicoding authority subgraph <path> <id> --direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --depth <positive-safe-integer> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
47
+ "nimicoding authority audit <path> --bindings <file> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json|--sarif]",
48
+ "nimicoding authority diff <before-path> <after-path> --max-bytes <positive-integer> [--json]",
49
+ "nimicoding authority impact <before-path> <after-path> --dispositions <file> --max-bytes <positive-integer> [--json]",
50
+ "nimicoding authority review <repository-path> --base <git-ref> --bindings <file> --dispositions <file> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
51
+ "nimicoding authority evidence <repository-path> --bindings <tracked-.nimi/config-path> [--probe-results <.nimi/local-path>] --max-units <positive-safe-integer> --max-bindings <positive-safe-integer> --max-locators <positive-safe-integer> --max-edges <positive-safe-integer> --max-input-bytes <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
52
+ ];
53
+
54
+ const USAGE = [
55
+ ...AUTHORITY_COMMAND_USAGE,
56
+ "",
57
+ AUTHORITY_ANCHOR_GRAMMAR_HELP,
58
+ "",
59
+ AUTHORITY_TERM_GRAMMAR_HELP,
60
+ ].join("\n");
61
+
62
+ const AUTHORITY_IDENTIFIER = /^[a-z](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z](?:[a-z0-9-]*[a-z0-9])?)+$/;
63
+
64
+ function parseOptions(subcommand, args) {
65
+ const options = { json: false, sarif: false, check: false, upgrade: false, embeddedPaths: false, code: false, path: null, repositoryPath: null, spec: null, term: null, requireFormat: null, base: null, id: null, fromId: null, toId: null, query: null, beforePath: null, afterPath: null, dispositions: null, bindings: null, scopeBindings: null, probeResults: null, kind: null, owner: null, scope: null, lifecycle: null, direction: null, previewDirection: null, traversal: null, relations: null, depth: null, maxHops: null, maxCandidates: null, maxSnippetTerms: null, maxUnits: null, maxTerms: null, maxSets: null, maxAnchors: null, maxFiles: null, maxRefs: null, maxBindings: null, maxLocators: null, maxEdges: null, maxInputBytes: null, maxBytes: null, externalRoots: [] };
66
+ const graphCommands = ["refs", "path", "subgraph"];
67
+ const reportIntegerAdmission = integerFlagAdmission();
68
+ const reportIntegerCommands = (flag) => reportIntegerAdmission[flag] ?? [];
69
+ const integerOptions = {
70
+ "--max-candidates": ["maxCandidates", ["discover"]],
71
+ "--max-snippet-terms": ["maxSnippetTerms", ["discover"]],
72
+ // The report products' integer flags come from the enforced surface table,
73
+ // so the parser and the product contracts cannot drift apart silently.
74
+ "--max-units": ["maxUnits", ["context", "audit", "review", "evidence", ...reportIntegerCommands("--max-units"), ...graphCommands]],
75
+ "--max-terms": ["maxTerms", reportIntegerCommands("--max-terms")],
76
+ "--max-sets": ["maxSets", reportIntegerCommands("--max-sets")],
77
+ "--max-anchors": ["maxAnchors", reportIntegerCommands("--max-anchors")],
78
+ "--max-files": ["maxFiles", reportIntegerCommands("--max-files")],
79
+ "--max-refs": ["maxRefs", reportIntegerCommands("--max-refs")],
80
+ "--max-bindings": ["maxBindings", ["evidence"]],
81
+ "--max-locators": ["maxLocators", ["evidence"]],
82
+ "--max-edges": ["maxEdges", ["audit", "review", "evidence", "discover", ...graphCommands]],
83
+ "--max-input-bytes": ["maxInputBytes", ["evidence"]],
84
+ "--max-bytes": ["maxBytes", ["discover", "query", "context", "audit", "diff", "impact", "review", "evidence", "anchors", "consumers", "terms", "closed-sets", ...graphCommands]],
85
+ "--max-hops": ["maxHops", ["path"]],
86
+ "--depth": ["depth", ["subgraph"]],
87
+ };
88
+ const positionals = [];
89
+ let terminated = false;
90
+ for (let index = 0; index < args.length; index += 1) {
91
+ const arg = args[index];
92
+ if (!terminated && arg === "--") {
93
+ terminated = true;
94
+ continue;
95
+ }
96
+ if (!terminated && arg.startsWith("--")) {
97
+ if (arg === "--json" && !options.json) {
98
+ if (options.sarif) return { ok: false, error: "authority audit requires --json and --sarif to be mutually exclusive" };
99
+ options.json = true;
100
+ } else if (arg === "--sarif" && subcommand === "audit" && !options.sarif) {
101
+ if (options.json) return { ok: false, error: "authority audit requires --json and --sarif to be mutually exclusive" };
102
+ options.sarif = true;
103
+ } else if (arg === "--check" && AUTHORING_SURFACE[subcommand]?.booleanFlags.includes("check") && !options.check) options.check = true;
104
+ else if (arg === "--upgrade" && AUTHORING_SURFACE[subcommand]?.booleanFlags.includes("upgrade") && !options.upgrade) options.upgrade = true;
105
+ else if (arg === "--embedded-paths" && booleanFlagAdmission("embedded-paths").includes(subcommand) && !options.embeddedPaths) options.embeddedPaths = true;
106
+ else if (arg === "--code" && booleanFlagAdmission("code").includes(subcommand) && !options.code) options.code = true;
107
+ else if (arg === "--external-root" && (AUTHORITY_SURFACE[subcommand]?.repeatableFlags ?? []).includes("external-root")) {
108
+ const value = args[index + 1];
109
+ const separator = value === undefined ? -1 : value.indexOf("=");
110
+ const scheme = separator === -1 ? "" : value.slice(0, separator);
111
+ const root = separator === -1 ? "" : value.slice(separator + 1);
112
+ if (!/^[a-z][a-z0-9-]*$/u.test(scheme) || root.length === 0) {
113
+ return { ok: false, error: "authority consumers requires --external-root <scheme>=<path> with one lowercase scheme" };
114
+ }
115
+ if (options.externalRoots.some((entry) => entry[0] === scheme)) {
116
+ return { ok: false, error: `authority consumers refused a repeated --external-root scheme: ${scheme}` };
117
+ }
118
+ options.externalRoots.push([scheme, root]);
119
+ index += 1;
120
+ }
121
+ else if (Object.hasOwn(integerOptions, arg)) {
122
+ const [key, admitted] = integerOptions[arg];
123
+ if (!admitted.includes(subcommand) || options[key] !== null) return { ok: false, error: `authority ${subcommand} refused unknown or repeated option: ${arg}` };
124
+ const value = args[index + 1];
125
+ if (!value || !/^[1-9][0-9]*$/.test(value)) return { ok: false, error: `authority ${subcommand} requires ${arg} followed by a positive integer` };
126
+ options[key] = Number(value);
127
+ if (!Number.isSafeInteger(options[key])) return { ok: false, error: `authority ${subcommand} ${arg.slice(2)} exceeds the supported integer range` };
128
+ index += 1;
129
+ } else if (arg === "--direction" && ["refs", "subgraph"].includes(subcommand) && options.direction === null) {
130
+ const value = args[index + 1];
131
+ if (!["incoming", "outgoing", "both"].includes(value)) return { ok: false, error: `authority ${subcommand} requires --direction incoming, outgoing, or both` };
132
+ options.direction = value;
133
+ index += 1;
134
+ } else if (arg === "--preview-direction" && subcommand === "discover" && options.previewDirection === null) {
135
+ const value = args[index + 1];
136
+ if (!["incoming", "outgoing", "both"].includes(value)) return { ok: false, error: "authority discover requires --preview-direction incoming, outgoing, or both" };
137
+ options.previewDirection = value;
138
+ index += 1;
139
+ } else if (arg === "--kind" && subcommand === "discover" && options.kind === null) {
140
+ const value = args[index + 1];
141
+ if (!["definition", "rule"].includes(value)) return { ok: false, error: "authority discover requires --kind definition or rule" };
142
+ options.kind = value;
143
+ index += 1;
144
+ } else if (["--owner", "--scope"].includes(arg) && subcommand === "discover" && options[arg.slice(2)] === null) {
145
+ const value = args[index + 1];
146
+ if (!value || !AUTHORITY_IDENTIFIER.test(value)) return { ok: false, error: `authority discover requires ${arg} followed by one exact dotted lowercase identifier` };
147
+ options[arg.slice(2)] = value;
148
+ index += 1;
149
+ } else if (arg === "--lifecycle" && subcommand === "discover" && options.lifecycle === null) {
150
+ const value = args[index + 1];
151
+ if (!["active", "removed"].includes(value)) return { ok: false, error: "authority discover requires --lifecycle active or removed" };
152
+ options.lifecycle = value;
153
+ index += 1;
154
+ } else if (arg === "--traversal" && subcommand === "path" && options.traversal === null) {
155
+ const value = args[index + 1];
156
+ if (!["directed", "incidence"].includes(value)) return { ok: false, error: "authority path requires --traversal directed or incidence" };
157
+ options.traversal = value;
158
+ index += 1;
159
+ } else if (arg === "--relations" && [...graphCommands, "discover"].includes(subcommand) && options.relations === null) {
160
+ const value = args[index + 1];
161
+ const relations = value?.split(",") ?? [];
162
+ if (relations.length === 0 || relations.some((entry) => entry.length === 0 || !["applies_to", "supersedes"].includes(entry)) || new Set(relations).size !== relations.length) {
163
+ return { ok: false, error: `authority ${subcommand} requires a non-empty unique closed --relations set containing only applies_to or supersedes` };
164
+ }
165
+ options.relations = relations.sort();
166
+ index += 1;
167
+ } else if (arg === "--dispositions" && ["impact", "review"].includes(subcommand) && options.dispositions === null) {
168
+ const value = args[index + 1];
169
+ if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --dispositions followed by one file` };
170
+ options.dispositions = value;
171
+ index += 1;
172
+ } else if (arg === "--bindings" && ["audit", "review", "evidence"].includes(subcommand) && options.bindings === null) {
173
+ const value = args[index + 1];
174
+ if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --bindings followed by one file` };
175
+ options.bindings = value;
176
+ index += 1;
177
+ } else if (arg === "--scope-bindings" && ["check", "anchors"].includes(subcommand) && options.scopeBindings === null) {
178
+ const value = args[index + 1];
179
+ if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --scope-bindings followed by one file` };
180
+ options.scopeBindings = value;
181
+ index += 1;
182
+ } else if (arg === "--require-format" && subcommand === "check" && options.requireFormat === null) {
183
+ const value = args[index + 1];
184
+ if (!ADMITTED_AUTHORITY_FORMATS.has(value)) {
185
+ return { ok: false, error: `authority check requires --require-format followed by an admitted source format: ${[...ADMITTED_AUTHORITY_FORMATS].join(" or ")}` };
186
+ }
187
+ options.requireFormat = value;
188
+ index += 1;
189
+ } else if (arg === "--spec" && ["anchors", "consumers", "terms", "closed-sets"].includes(subcommand) && options.spec === null) {
190
+ const value = args[index + 1];
191
+ if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --spec followed by one corpus path` };
192
+ options.spec = value;
193
+ index += 1;
194
+ } else if (arg === "--term" && subcommand === "terms" && options.term === null) {
195
+ const value = args[index + 1];
196
+ if (!value || value.startsWith("--") || /[\0\r\n]/u.test(value)) return { ok: false, error: "authority terms requires --term followed by one non-empty single-line term" };
197
+ options.term = value;
198
+ index += 1;
199
+ } else if (arg === "--probe-results" && subcommand === "evidence" && options.probeResults === null) {
200
+ const value = args[index + 1];
201
+ if (!value || value.startsWith("--")) return { ok: false, error: "authority evidence requires --probe-results followed by one repository-relative file" };
202
+ options.probeResults = value;
203
+ index += 1;
204
+ } else if (arg === "--base" && subcommand === "review" && options.base === null) {
205
+ const value = args[index + 1];
206
+ if (!value || value.startsWith("-")) return { ok: false, error: "authority review requires --base followed by one non-option Git ref" };
207
+ options.base = value;
208
+ index += 1;
209
+ } else return { ok: false, error: `authority ${subcommand} refused unknown or repeated option: ${arg}` };
210
+ continue;
211
+ }
212
+ positionals.push(arg);
213
+ }
214
+ const expected = subcommand === "path" ? 3 : ["discover", "query", "context", "refs", "subgraph", "diff", "impact"].includes(subcommand) ? 2 : 1;
215
+ if (positionals.length !== expected) {
216
+ const requirement = subcommand === "discover"
217
+ ? "one path and one query"
218
+ : ["diff", "impact"].includes(subcommand)
219
+ ? "one before path and one after path"
220
+ : subcommand === "path" ? "one path, one exact from ID, and one exact to ID" : ["review", "evidence", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand) ? "one repository path" : expected === 1 ? "one path" : "one path and one exact ID";
221
+ return { ok: false, error: `authority ${subcommand} requires exactly ${requirement}` };
222
+ }
223
+ if (["diff", "impact"].includes(subcommand)) [options.beforePath, options.afterPath] = positionals;
224
+ else if (["review", "evidence", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand)) [options.repositoryPath] = positionals;
225
+ else if (subcommand === "path") [options.path, options.fromId, options.toId] = positionals;
226
+ else [options.path, options.id] = positionals;
227
+ if (subcommand === "discover") {
228
+ options.query = options.id;
229
+ options.id = null;
230
+ }
231
+ if (subcommand === "discover" && options.maxCandidates === null) return { ok: false, error: "authority discover requires --max-candidates followed by a positive integer" };
232
+ if (subcommand === "discover" && options.maxSnippetTerms === null) return { ok: false, error: "authority discover requires --max-snippet-terms followed by a positive integer" };
233
+ if (subcommand === "discover") {
234
+ const previewOptions = [options.previewDirection, options.relations, options.maxEdges];
235
+ const provided = previewOptions.filter((value) => value !== null).length;
236
+ if (provided !== 0 && provided !== previewOptions.length) return { ok: false, error: "authority discover requires --preview-direction, --relations, and --max-edges together" };
237
+ }
238
+ if (["context", "anchors", "terms", "closed-sets", ...graphCommands].includes(subcommand) && options.maxUnits === null) return { ok: false, error: `authority ${subcommand} requires --max-units followed by a positive integer` };
239
+ if (graphCommands.includes(subcommand) && options.maxEdges === null) return { ok: false, error: `authority ${subcommand} requires --max-edges followed by a positive integer` };
240
+ if (["discover", "query", "context", "audit", "diff", "impact", "review", "evidence", "anchors", "consumers", "terms", "closed-sets", ...graphCommands].includes(subcommand) && options.maxBytes === null) return { ok: false, error: `authority ${subcommand} requires --max-bytes followed by a positive integer` };
241
+ if (["refs", "subgraph"].includes(subcommand) && options.direction === null) return { ok: false, error: `authority ${subcommand} requires --direction` };
242
+ if (subcommand === "path" && options.traversal === null) return { ok: false, error: "authority path requires --traversal" };
243
+ if (graphCommands.includes(subcommand) && options.relations === null) return { ok: false, error: `authority ${subcommand} requires --relations` };
244
+ if (subcommand === "path" && options.maxHops === null) return { ok: false, error: "authority path requires --max-hops followed by a positive integer" };
245
+ if (subcommand === "subgraph" && options.depth === null) return { ok: false, error: "authority subgraph requires --depth followed by a positive integer" };
246
+ if (subcommand === "impact" && options.dispositions === null) return { ok: false, error: "authority impact requires --dispositions followed by one file" };
247
+ if (subcommand === "audit" && options.bindings === null) return { ok: false, error: "authority audit requires --bindings followed by one file" };
248
+ if (subcommand === "audit" && options.maxUnits === null) return { ok: false, error: "authority audit requires --max-units followed by a positive integer" };
249
+ if (subcommand === "audit" && options.maxEdges === null) return { ok: false, error: "authority audit requires --max-edges followed by a positive integer" };
250
+ if (subcommand === "review" && options.base === null) return { ok: false, error: "authority review requires --base followed by one Git ref" };
251
+ if (subcommand === "review" && options.bindings === null) return { ok: false, error: "authority review requires --bindings followed by one file" };
252
+ if (subcommand === "review" && options.dispositions === null) return { ok: false, error: "authority review requires --dispositions followed by one file" };
253
+ if (subcommand === "review" && options.maxUnits === null) return { ok: false, error: "authority review requires --max-units followed by a positive integer" };
254
+ if (subcommand === "review" && options.maxEdges === null) return { ok: false, error: "authority review requires --max-edges followed by a positive integer" };
255
+ if (subcommand === "evidence" && options.bindings === null) return { ok: false, error: "authority evidence requires --bindings followed by one repository-relative file" };
256
+ if (subcommand === "evidence" && options.maxUnits === null) return { ok: false, error: "authority evidence requires --max-units followed by a positive integer" };
257
+ if (subcommand === "evidence" && options.maxBindings === null) return { ok: false, error: "authority evidence requires --max-bindings followed by a positive integer" };
258
+ if (subcommand === "evidence" && options.maxLocators === null) return { ok: false, error: "authority evidence requires --max-locators followed by a positive integer" };
259
+ if (subcommand === "evidence" && options.maxEdges === null) return { ok: false, error: "authority evidence requires --max-edges followed by a positive integer" };
260
+ if (subcommand === "evidence" && options.maxInputBytes === null) return { ok: false, error: "authority evidence requires --max-input-bytes followed by a positive integer" };
261
+ if (subcommand === "anchors" && options.spec === null) return { ok: false, error: "authority anchors requires --spec followed by one corpus path" };
262
+ if (subcommand === "anchors" && options.maxAnchors === null) return { ok: false, error: "authority anchors requires --max-anchors followed by a positive integer" };
263
+ if (subcommand === "consumers" && options.spec === null) return { ok: false, error: "authority consumers requires --spec followed by one corpus path" };
264
+ if (subcommand === "consumers" && options.maxFiles === null) return { ok: false, error: "authority consumers requires --max-files followed by a positive integer" };
265
+ if (subcommand === "consumers" && options.maxRefs === null) return { ok: false, error: "authority consumers requires --max-refs followed by a positive integer" };
266
+ if (subcommand === "terms" && options.spec === null) return { ok: false, error: "authority terms requires --spec followed by one corpus path" };
267
+ if (subcommand === "terms" && options.maxTerms === null) return { ok: false, error: "authority terms requires --max-terms followed by a positive integer" };
268
+ if (subcommand === "closed-sets" && options.spec === null) return { ok: false, error: "authority closed-sets requires --spec followed by one corpus path" };
269
+ if (subcommand === "closed-sets" && options.maxSets === null) return { ok: false, error: "authority closed-sets requires --max-sets followed by a positive integer" };
270
+ if (subcommand === "terms" && options.code && options.term === null) return { ok: false, error: "authority terms refuses --code without --term; unbounded cross-product scanning is forbidden" };
271
+ return { ok: true, options };
272
+ }
273
+
274
+ function humanDiagnostic(diagnostic) {
275
+ const { line, column } = diagnostic.range.start;
276
+ return `${diagnostic.path}:${line}:${column} [${diagnostic.code}] ${diagnostic.reason}\n repair: ${diagnostic.repair}`;
277
+ }
278
+
279
+ function humanAuditLocation(location) {
280
+ const { line, column } = location.range.start;
281
+ return `${location.file}:${line}:${column}`;
282
+ }
283
+
284
+ function formatAuditHuman(report) {
285
+ const audit = report.audit;
286
+ const operationStatus = audit?.operationStatus ?? report.semantic_status;
287
+ const policyStatus = audit?.policyStatus ?? "indeterminate";
288
+ const complete = audit?.complete ?? false;
289
+ const lines = [
290
+ `nimicoding authority audit: operation=${operationStatus}; policy=${policyStatus}; complete=${complete}`,
291
+ `files: ${report.summary.files}; units: ${report.summary.units}; diagnostics: ${report.summary.diagnostics}`,
292
+ `audit bytes: ${report.audit_bytes}`,
293
+ ];
294
+ if (audit) {
295
+ lines.push(`bindings: required=${audit.counts.bindings.required}; configured=${audit.counts.bindings.configured}; evaluated=${audit.counts.bindings.evaluated}`);
296
+ lines.push(`returned: observations=${audit.counts.returned.observations}; findings=${audit.counts.returned.findings}; gaps=${audit.counts.returned.gaps}`);
297
+ lines.push(`traversal: units=${audit.counts.traversal.units}; edges=${audit.counts.traversal.edges}`);
298
+ for (const finding of audit.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
299
+ for (const gap of audit.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
300
+ } else lines.push("counts: unavailable (audit refused before evaluation)");
301
+ for (const diagnostic of report.diagnostics) lines.push(humanDiagnostic(diagnostic));
302
+ return `${lines.join("\n")}\n`;
303
+ }
304
+
305
+ function outputReport(report, json) {
306
+ if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
307
+ else {
308
+ process.stdout.write(`nimicoding authority ${report.operation}: ${report.ok ? "ok" : "failed"}\n`);
309
+ process.stdout.write(`files: ${report.summary.files}; units: ${report.summary.units}; diagnostics: ${report.summary.diagnostics}\n`);
310
+ if (report.changed !== undefined) process.stdout.write(`changed: ${report.changed}\n`);
311
+ if (report.packet_bytes !== undefined) process.stdout.write(`packet bytes: ${report.packet_bytes}\n`);
312
+ if (report.discovery_bytes !== undefined) process.stdout.write(`discovery bytes: ${report.discovery_bytes}\n`);
313
+ if (report.payload_bytes !== undefined) process.stdout.write(`semantic payload bytes: ${report.payload_bytes}\n`);
314
+ if (report.graph_bytes !== undefined) process.stdout.write(`graph bytes: ${report.graph_bytes}\n`);
315
+ if (report.packet) {
316
+ process.stdout.write(`root: ${report.packet.root}\n`);
317
+ process.stdout.write(`context units: ${report.packet.units.map((unit) => unit.id).join(", ")}\n`);
318
+ }
319
+ if (report.discovery) {
320
+ process.stdout.write(`eligible units: ${report.discovery.counts.eligibleUnits}; lexical matches: ${report.discovery.counts.matchedUnits}; returned candidates: ${report.discovery.counts.returnedCandidates}\n`);
321
+ process.stdout.write(`candidates: ${report.discovery.candidates.map((candidate) => candidate.id).join(", ")}\n`);
322
+ if (report.discovery.relationPreview) process.stdout.write(`relation preview: units=${report.discovery.relationPreview.counts.units}; edges=${report.discovery.relationPreview.counts.edges}; complete=${report.discovery.relationPreview.complete}\n`);
323
+ }
324
+ if (report.diff) process.stdout.write(`semantic changes: ${report.diff.summary.changes}\n`);
325
+ if (report.impact) process.stdout.write(`impacted units: ${report.impact.impactedUnits.map((unit) => unit.id).join(", ")}\n`);
326
+ if (report.graph) {
327
+ process.stdout.write(`graph complete: ${report.graph.complete}\n`);
328
+ process.stdout.write(`graph nodes: ${report.graph.nodes.map((node) => node.id).join(", ")}\n`);
329
+ process.stdout.write(`graph edges: ${report.graph.edges.length}\n`);
330
+ for (const edge of report.graph.edges) process.stdout.write(` ${edge.source} -[${edge.type}]-> ${edge.target}\n`);
331
+ if (report.graph.operation === "path") {
332
+ process.stdout.write(`path found: ${report.graph.found}\n`);
333
+ process.stdout.write(`path steps: ${report.graph.steps.length}\n`);
334
+ report.graph.steps.forEach((step, index) => {
335
+ process.stdout.write(` ${index + 1}. ${step.traversal} authored ${step.source} -[${step.type}]-> ${step.target}\n`);
336
+ });
337
+ }
338
+ }
339
+ for (const diagnostic of report.diagnostics) process.stdout.write(`${humanDiagnostic(diagnostic)}\n`);
340
+ }
341
+ }
342
+
343
+ function outputAuditReport(report, output) {
344
+ if (output === "json") process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
345
+ else process.stdout.write(formatAuditHuman(report));
346
+ }
347
+
348
+ function formatReviewHuman(report) {
349
+ const review = report.review;
350
+ const lines = [
351
+ `nimicoding authority review: operation=${review?.operationStatus ?? "refused"}; policy=${review?.policyStatus ?? "indeterminate"}; complete=${review?.complete ?? false}`,
352
+ ];
353
+ if (review) {
354
+ const base = review.snapshots.base;
355
+ const worktree = review.snapshots.worktree;
356
+ const unresolved = review.impact.obligations.filter((obligation) => obligation.disposition === null).length;
357
+ lines.push(`base: commit=${base.commitOid}; identity=${base.contentIdentity}; files=${base.counts.files}; units=${base.counts.units}`);
358
+ lines.push(`worktree: identity=${worktree.contentIdentity}; files=${worktree.counts.files}; units=${worktree.counts.units}`);
359
+ lines.push(`semantic changes: ${review.diff.summary.changes}; impacted units: ${review.impact.impactedUnits.length}; unresolved obligations: ${unresolved}`);
360
+ lines.push(`current audit: policy=${review.audit.policyStatus}; findings=${review.audit.findings.length}; gaps=${review.audit.gaps.length}`);
361
+ lines.push(`review bytes: ${report.review_bytes}`);
362
+ for (const finding of review.audit.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
363
+ for (const gap of review.audit.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
364
+ } else {
365
+ lines.push("review unavailable; partial=false");
366
+ }
367
+ for (const diagnostic of report.diagnostics) lines.push(humanDiagnostic(diagnostic));
368
+ return `${lines.join("\n")}\n`;
369
+ }
370
+
371
+ function outputReviewReport(report, json) {
372
+ if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
373
+ else process.stdout.write(formatReviewHuman(report));
374
+ }
375
+
376
+ function formatEvidenceHuman(report) {
377
+ const evidence = report.evidence;
378
+ const lines = [
379
+ `nimicoding authority evidence: operation=${evidence?.operationStatus ?? "refused"}; evidence=${evidence?.evidenceStatus ?? "indeterminate"}; complete=${evidence?.complete ?? false}; conformance=${evidence?.conformanceStatus ?? "not_evaluated"}`,
380
+ ];
381
+ if (evidence) {
382
+ lines.push(`snapshot: head=${evidence.identities.headCommitOid}; authority=${evidence.identities.authorityContentIdentity}; repository-input=${evidence.identities.repositoryInputIdentity}`);
383
+ lines.push(`bindings: required=${evidence.counts.bindings.required}; configured=${evidence.counts.bindings.configured}; evaluated=${evidence.counts.bindings.evaluated}`);
384
+ lines.push(`returned: observations=${evidence.counts.returned.observations}; findings=${evidence.counts.returned.findings}; gaps=${evidence.counts.returned.gaps}`);
385
+ lines.push(`evidence bytes: ${report.evidence_bytes}`);
386
+ for (const binding of evidence.bindings) {
387
+ lines.push(`binding ${binding.id}: required=${binding.required}; reachability=${binding.packageProbe.reachability}; execution=${binding.packageProbe.execution}; conformance=${binding.packageProbe.conformance}`);
388
+ if (binding.externalProbe) lines.push(` external ${binding.externalProbe.id}@${binding.externalProbe.version}: ${binding.externalProbe.status}${binding.externalProbe.reportedOutcome ? `/${binding.externalProbe.reportedOutcome}` : ""}; package-attestation=false`);
389
+ }
390
+ for (const finding of evidence.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
391
+ for (const gap of evidence.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
392
+ } else lines.push("evidence unavailable; partial=false");
393
+ for (const diagnostic of report.diagnostics) lines.push(humanDiagnostic(diagnostic));
394
+ return `${lines.join("\n")}\n`;
395
+ }
396
+
397
+ function outputEvidenceReport(report, json) {
398
+ if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
399
+ else process.stdout.write(formatEvidenceHuman(report));
400
+ }
401
+
402
+ function formatAnchorsReport(report, json) {
403
+ if (json) return `${JSON.stringify(report, null, 2)}\n`;
404
+ const lines = [
405
+ `nimicoding authority anchors: ${report.semantic_status}; complete=${report.complete}`,
406
+ `units: ${report.summary.units}; anchors checked: ${report.summary.anchorsChecked}; diagnostics: ${report.summary.diagnostics}`,
407
+ `anchor bytes: ${report.anchors_bytes}`,
408
+ ...report.diagnostics.map((diagnostic) => humanDiagnostic(diagnostic)),
409
+ ];
410
+ return `${lines.join("\n")}\n`;
411
+ }
412
+
413
+ function stabilizeAnchorsReportBytes(report, json) {
414
+ let candidate = report;
415
+ for (let attempt = 0; attempt < 10; attempt += 1) {
416
+ const bytes = Buffer.byteLength(formatAnchorsReport(candidate, json), "utf8");
417
+ if (candidate.anchors_bytes === bytes) return candidate;
418
+ candidate = { ...candidate, anchors_bytes: bytes };
419
+ }
420
+ throw new Error("anchor report byte count did not converge");
421
+ }
422
+
423
+ function outputAnchorsReport(report, json) {
424
+ process.stdout.write(formatAnchorsReport(report, json));
425
+ }
426
+
427
+ function outputConsumersReport(report, json) {
428
+ if (json) {
429
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
430
+ return;
431
+ }
432
+ process.stdout.write(`nimicoding authority consumers: ${report.semantic_status}; complete=${report.complete}\n`);
433
+ process.stdout.write(`scanned files: ${report.summary.filesScanned}; references: ${report.summary.references}; scanned bytes: ${report.scanned_bytes}\n`);
434
+ if (report.consumers) {
435
+ const consumption = report.consumers.specConsumption;
436
+ process.stdout.write(
437
+ `spec consumption: canonical=${consumption.counts.canonical}; `
438
+ + `canonical-dead=${consumption.counts.canonicalDead}; `
439
+ + `external-authority=${consumption.counts.externalAuthority}; `
440
+ + `external-dead=${consumption.counts.externalDead}; `
441
+ + `external-unresolved=${consumption.counts.externalUnresolved}; `
442
+ + `unresolved=${consumption.counts.unresolved}\n`,
443
+ );
444
+ for (const [label, entries] of [
445
+ ["canonical-dead", consumption.references.canonicalDead],
446
+ ["external-dead", consumption.references.externalDead],
447
+ ["external-unresolved", consumption.references.externalUnresolved],
448
+ ]) {
449
+ process.stdout.write(`${label}:\n`);
450
+ for (const reference of entries) {
451
+ process.stdout.write(` ${reference.consumer}:${reference.line} -> ${reference.target}\n`);
452
+ }
453
+ }
454
+ process.stdout.write("unresolved:\n");
455
+ for (const reference of consumption.unresolved) {
456
+ process.stdout.write(` ${reference.consumer}:${reference.line} -> ${reference.target} (${reference.reason})\n`);
457
+ }
458
+ const wiring = report.consumers.gateWiring;
459
+ process.stdout.write(`gate wiring: total keys=${wiring.counts.totalKeys}; workflow-reachable=${wiring.counts.workflowReachable}; script-chained-only=${wiring.counts.scriptChainedOnly}; orphan=${wiring.counts.orphan}\n`);
460
+ process.stdout.write("orphan gates:\n");
461
+ for (const script of wiring.orphans) process.stdout.write(` ${script}\n`);
462
+ }
463
+ for (const diagnostic of report.diagnostics) process.stdout.write(`${humanDiagnostic(diagnostic)}\n`);
464
+ }
465
+
466
+ function outputTermsReport(report, json) {
467
+ if (json) {
468
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
469
+ return;
470
+ }
471
+ process.stdout.write(`nimicoding authority terms: ${report.semantic_status}; complete=${report.complete}\n`);
472
+ process.stdout.write(`units: ${report.summary.units}; distinct terms: ${report.summary.distinctTerms}; scanned bytes: ${report.scanned_bytes}; input bytes: ${report.input_bytes}\n`);
473
+ if (report.terms?.mode === "index") {
474
+ const classes = report.terms.counts.classes;
475
+ process.stdout.write(`classes: ${Object.entries(classes).map(([name, count]) => `${name}=${count}`).join("; ")}\n`);
476
+ process.stdout.write("top terms:\n");
477
+ const top = [...report.terms.index]
478
+ .sort((left, right) => right.unitCount - left.unitCount || (left.term === right.term ? 0 : left.term < right.term ? -1 : 1))
479
+ .slice(0, 30);
480
+ for (const entry of top) process.stdout.write(` ${entry.term}: units=${entry.unitCount}; classes=${entry.classes.join(",")}\n`);
481
+ } else if (report.terms?.mode === "detail") {
482
+ process.stdout.write(`term: ${report.terms.term}; units: ${report.terms.unitCount}\n`);
483
+ for (const unit of report.terms.units) process.stdout.write(` ${unit.unitId}: fields=${unit.fields.join(",")}\n`);
484
+ if (report.terms.code) {
485
+ const inventory = report.terms.codeInventory;
486
+ process.stdout.write(
487
+ `code inventory: tracked=${inventory.trackedFiles}; scanned=${inventory.scannedFiles}; `
488
+ + `excluded=${Object.entries(inventory.excludedByReason).map(([reason, count]) => `${reason}=${count}`).join(",") || "none"}`
489
+ + " (full list in --json)\n",
490
+ );
491
+ process.stdout.write(`code references: ${report.terms.codeReferences.length}\n`);
492
+ for (const reference of report.terms.codeReferences) process.stdout.write(` ${reference.file}:${reference.line}\n`);
493
+ }
494
+ }
495
+ for (const diagnostic of report.diagnostics) process.stdout.write(`${humanDiagnostic(diagnostic)}\n`);
496
+ }
497
+
498
+ function outputClosedSetsReport(report, json) {
499
+ if (json) {
500
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
501
+ return;
502
+ }
503
+ process.stdout.write(`nimicoding authority closed-sets: ${report.semantic_status}; complete=${report.complete}\n`);
504
+ const sets = report.summary.sets;
505
+ process.stdout.write(`groups: ${report.summary.groups}; sets: proto-enums=${sets.protoEnums}; config-yaml-sequences=${sets.configYamlSequences}; total=${sets.total}\n`);
506
+ process.stdout.write(`pairs: ${report.summary.pairs}; pairs with deltas: ${report.summary.pairsWithDeltas}; subset pairs (suppressed in text): ${report.summary.subsetPairs}; nested-row pairs (json only)\n`);
507
+ process.stdout.write(`unpaired: groups=${report.summary.unpairedGroups}; sets=${report.summary.unpairedSets.total}\n`);
508
+ process.stdout.write(`scanned bytes: ${report.scanned_bytes}; input bytes: ${report.input_bytes}\n`);
509
+ if (report.closed_sets) {
510
+ for (const pair of report.closed_sets.pairs) {
511
+ if (pair.missingInCanonical.length === 0 && pair.missingInImplementation.length === 0) continue;
512
+ if (pair.subset) continue;
513
+ if (pair.implementation.kind !== "proto_enum" && pair.implementation.name.includes("[")) continue;
514
+ process.stdout.write(`${pair.canonical.unitId} [${pair.canonical.field}] <-> ${pair.implementation.file}#${pair.implementation.name} (${pair.implementation.kind})\n`);
515
+ if (pair.missingInCanonical.length > 0) process.stdout.write(` missing in canonical: ${pair.missingInCanonical.join(", ")}\n`);
516
+ if (pair.missingInImplementation.length > 0) process.stdout.write(` missing in implementation: ${pair.missingInImplementation.join(", ")}\n`);
517
+ }
518
+ }
519
+ for (const diagnostic of report.diagnostics) process.stdout.write(`${humanDiagnostic(diagnostic)}\n`);
520
+ }
521
+
522
+ function makeReport(operation, result, semanticStatus, extra = {}) {
523
+ return {
524
+ operation,
525
+ ok: result.ok,
526
+ semantic_status: semanticStatus,
527
+ ...extra,
528
+ summary: {
529
+ files: result.fileCount ?? 0,
530
+ units: result.unitCount ?? 0,
531
+ diagnostics: result.diagnostics.length,
532
+ },
533
+ diagnostics: result.diagnostics,
534
+ };
535
+ }
536
+
537
+ export async function runAuthority(args) {
538
+ const [subcommand, ...rest] = args;
539
+ if (!subcommand || subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
540
+ if (rest.length > 0) {
541
+ process.stderr.write(`nimicoding authority help refused unexpected arguments\n`);
542
+ return 2;
543
+ }
544
+ process.stdout.write(`${USAGE}\n`);
545
+ return 0;
546
+ }
547
+ if (!["fmt", "check", "compile", "anchors", "consumers", "terms", "closed-sets", "discover", "query", "context", "refs", "path", "subgraph", "audit", "diff", "impact", "review", "evidence"].includes(subcommand)) {
548
+ process.stderr.write(`nimicoding authority refused unknown subcommand: ${subcommand}\n${USAGE}\n`);
549
+ return 2;
550
+ }
551
+ const parsed = parseOptions(subcommand, rest);
552
+ if (!parsed.ok) {
553
+ process.stderr.write(`${parsed.error}\n${USAGE}\n`);
554
+ return 2;
555
+ }
556
+ try {
557
+ if (subcommand === "fmt") {
558
+ const result = await formatAuthorityFile(parsed.options.path, { check: parsed.options.check, upgrade: parsed.options.upgrade });
559
+ const report = makeReport("fmt", result, "not_evaluated", { changed: result.changed, check: parsed.options.check, upgrade: parsed.options.upgrade });
560
+ outputReport(report, parsed.options.json);
561
+ return result.ok ? 0 : 1;
562
+ }
563
+ if (subcommand === "check") {
564
+ const result = await checkAuthorityPath(parsed.options.path, {
565
+ scopeBindings: parsed.options.scopeBindings,
566
+ requireFormat: parsed.options.requireFormat,
567
+ });
568
+ const report = makeReport("check", result, result.ok ? "valid" : "invalid");
569
+ outputReport(report, parsed.options.json);
570
+ return result.ok ? 0 : 1;
571
+ }
572
+ if (subcommand === "compile") {
573
+ const result = await compileAuthorityPath(parsed.options.path);
574
+ const report = makeReport("compile", result, result.ok ? "valid" : "invalid");
575
+ outputReport(report, parsed.options.json);
576
+ return result.ok ? 0 : 1;
577
+ }
578
+ if (subcommand === "discover") {
579
+ const result = await discoverAuthorityPath(parsed.options.path, parsed.options.query, {
580
+ maxCandidates: parsed.options.maxCandidates,
581
+ maxSnippetTerms: parsed.options.maxSnippetTerms,
582
+ maxBytes: parsed.options.maxBytes,
583
+ kind: parsed.options.kind,
584
+ owner: parsed.options.owner,
585
+ scope: parsed.options.scope,
586
+ lifecycle: parsed.options.lifecycle,
587
+ previewDirection: parsed.options.previewDirection,
588
+ relations: parsed.options.relations,
589
+ maxEdges: parsed.options.maxEdges,
590
+ });
591
+ const report = makeReport("discover", result, result.ok ? "valid" : "invalid", { discovery_bytes: result.discoveryBytes, discovery: result.discovery });
592
+ outputReport(report, parsed.options.json);
593
+ return result.ok ? 0 : 1;
594
+ }
595
+ if (subcommand === "query") {
596
+ const result = await queryAuthorityPath(parsed.options.path, parsed.options.id, { maxBytes: parsed.options.maxBytes });
597
+ const report = makeReport("query", result, result.ok ? "valid" : "invalid", { packet_bytes: result.packetBytes, packet: result.packet });
598
+ outputReport(report, parsed.options.json);
599
+ return result.ok ? 0 : 1;
600
+ }
601
+ if (subcommand === "context") {
602
+ const result = await contextAuthorityPath(parsed.options.path, parsed.options.id, { maxUnits: parsed.options.maxUnits, maxBytes: parsed.options.maxBytes });
603
+ const report = makeReport("context", result, result.ok ? "valid" : "invalid", { packet_bytes: result.packetBytes, packet: result.packet });
604
+ outputReport(report, parsed.options.json);
605
+ return result.ok ? 0 : 1;
606
+ }
607
+ if (["refs", "path", "subgraph"].includes(subcommand)) {
608
+ const common = {
609
+ relations: parsed.options.relations,
610
+ maxUnits: parsed.options.maxUnits,
611
+ maxEdges: parsed.options.maxEdges,
612
+ maxBytes: parsed.options.maxBytes,
613
+ };
614
+ const result = subcommand === "refs"
615
+ ? await refsAuthorityPath(parsed.options.path, parsed.options.id, { ...common, direction: parsed.options.direction })
616
+ : subcommand === "path"
617
+ ? await pathAuthorityPath(parsed.options.path, parsed.options.fromId, parsed.options.toId, { ...common, traversal: parsed.options.traversal, maxHops: parsed.options.maxHops })
618
+ : await subgraphAuthorityPath(parsed.options.path, parsed.options.id, { ...common, direction: parsed.options.direction, depth: parsed.options.depth });
619
+ const report = makeReport(subcommand, result, result.ok ? "completed" : "refused", { graph_bytes: result.graphBytes, graph: result.graph, partial: result.partial });
620
+ outputReport(report, parsed.options.json);
621
+ return result.ok ? 0 : 1;
622
+ }
623
+ if (subcommand === "audit") {
624
+ const result = await auditAuthorityPath(parsed.options.path, parsed.options.bindings, {
625
+ maxUnits: parsed.options.maxUnits,
626
+ maxEdges: parsed.options.maxEdges,
627
+ maxBytes: parsed.options.maxBytes,
628
+ });
629
+ if (parsed.options.sarif) process.stdout.write(`${JSON.stringify(authorityAuditResultToSarif(result, { toolVersion: VERSION }), null, 2)}\n`);
630
+ else {
631
+ const report = makeReport("audit", result, result.audit?.operationStatus ?? "refused", {
632
+ audit_bytes: result.auditBytes,
633
+ audit: result.audit,
634
+ partial: result.partial,
635
+ });
636
+ outputAuditReport(report, parsed.options.json ? "json" : "human");
637
+ }
638
+ return result.audit?.operationStatus === "completed" && result.audit.policyStatus === "passed" ? 0 : 1;
639
+ }
640
+ if (subcommand === "diff") {
641
+ const result = await diffAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, { maxBytes: parsed.options.maxBytes });
642
+ const report = makeReport("diff", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff });
643
+ outputReport(report, parsed.options.json);
644
+ return result.ok ? 0 : 1;
645
+ }
646
+ if (subcommand === "impact") {
647
+ const result = await impactAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, parsed.options.dispositions, { maxBytes: parsed.options.maxBytes });
648
+ const report = makeReport("impact", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff, impact: result.impact });
649
+ outputReport(report, parsed.options.json);
650
+ return result.ok ? 0 : 1;
651
+ }
652
+ if (subcommand === "anchors") {
653
+ const result = await anchorsAuthorityRepository(
654
+ parsed.options.repositoryPath,
655
+ parsed.options.spec,
656
+ parsed.options.scopeBindings,
657
+ {
658
+ maxUnits: parsed.options.maxUnits,
659
+ maxAnchors: parsed.options.maxAnchors,
660
+ maxBytes: parsed.options.maxBytes,
661
+ embeddedPaths: parsed.options.embeddedPaths,
662
+ },
663
+ );
664
+ let report = {
665
+ format: ANCHORS_FORMAT,
666
+ operation: "anchors",
667
+ ok: result.ok,
668
+ semantic_status: result.complete ? result.ok ? "valid" : "invalid" : "refused",
669
+ complete: result.complete,
670
+ partial: result.partial,
671
+ trackedPathSetDigest: result.trackedPathSetDigest ?? null,
672
+ anchors_bytes: result.anchorsBytes,
673
+ summary: {
674
+ units: result.unitCount,
675
+ anchorsChecked: result.anchorsChecked,
676
+ diagnostics: result.diagnostics.length,
677
+ },
678
+ diagnostics: result.diagnostics,
679
+ };
680
+ if (result.complete) report = stabilizeAnchorsReportBytes(report, parsed.options.json);
681
+ const renderedBytes = Buffer.byteLength(formatAnchorsReport(report, parsed.options.json), "utf8");
682
+ if (result.complete && renderedBytes > parsed.options.maxBytes) {
683
+ const refusal = refuseAnchorsOutputBudget(renderedBytes, parsed.options.maxBytes);
684
+ report = {
685
+ format: ANCHORS_FORMAT,
686
+ operation: "anchors",
687
+ ok: false,
688
+ semantic_status: "refused",
689
+ complete: false,
690
+ partial: false,
691
+ trackedPathSetDigest: null,
692
+ anchors_bytes: refusal.anchorsBytes,
693
+ summary: {
694
+ units: 0,
695
+ anchorsChecked: 0,
696
+ diagnostics: refusal.diagnostics.length,
697
+ },
698
+ diagnostics: refusal.diagnostics,
699
+ };
700
+ }
701
+ outputAnchorsReport(report, parsed.options.json);
702
+ return report.ok ? 0 : 1;
703
+ }
704
+ if (subcommand === "consumers") {
705
+ const result = await consumersAuthorityRepository(
706
+ parsed.options.repositoryPath,
707
+ parsed.options.spec,
708
+ {
709
+ maxFiles: parsed.options.maxFiles,
710
+ maxRefs: parsed.options.maxRefs,
711
+ externalRoots: parsed.options.externalRoots,
712
+ maxBytes: parsed.options.maxBytes,
713
+ },
714
+ );
715
+ const report = {
716
+ operation: "consumers",
717
+ ok: result.ok,
718
+ semantic_status: result.complete ? "completed" : "refused",
719
+ complete: result.complete,
720
+ partial: result.partial,
721
+ trackedPathSetDigest: result.trackedPathSetDigest ?? null,
722
+ scanned_bytes: result.scannedBytes,
723
+ summary: {
724
+ filesScanned: result.fileCount,
725
+ references: result.referenceCount,
726
+ diagnostics: result.diagnostics.length,
727
+ },
728
+ consumers: result.consumers,
729
+ diagnostics: result.diagnostics,
730
+ };
731
+ outputConsumersReport(report, parsed.options.json);
732
+ return result.ok ? 0 : 1;
733
+ }
734
+ if (subcommand === "terms") {
735
+ const result = await termsAuthorityRepository(
736
+ parsed.options.repositoryPath,
737
+ parsed.options.spec,
738
+ {
739
+ term: parsed.options.term,
740
+ code: parsed.options.code,
741
+ maxUnits: parsed.options.maxUnits,
742
+ maxTerms: parsed.options.maxTerms,
743
+ maxBytes: parsed.options.maxBytes,
744
+ },
745
+ );
746
+ const report = {
747
+ operation: "terms",
748
+ ok: result.ok,
749
+ semantic_status: result.complete ? "completed" : "refused",
750
+ complete: result.complete,
751
+ partial: result.partial,
752
+ trackedPathSetDigest: result.trackedPathSetDigest ?? null,
753
+ scanned_bytes: result.scannedBytes,
754
+ input_bytes: result.inputBytes,
755
+ summary: {
756
+ files: result.fileCount,
757
+ units: result.unitCount,
758
+ distinctTerms: result.termCount,
759
+ diagnostics: result.diagnostics.length,
760
+ },
761
+ terms: result.terms,
762
+ diagnostics: result.diagnostics,
763
+ };
764
+ outputTermsReport(report, parsed.options.json);
765
+ return result.ok ? 0 : 1;
766
+ }
767
+ if (subcommand === "closed-sets") {
768
+ const result = await closedSetsAuthorityRepository(
769
+ parsed.options.repositoryPath,
770
+ parsed.options.spec,
771
+ {
772
+ maxUnits: parsed.options.maxUnits,
773
+ maxSets: parsed.options.maxSets,
774
+ maxBytes: parsed.options.maxBytes,
775
+ },
776
+ );
777
+ const emptySummary = {
778
+ groups: 0,
779
+ sets: { total: 0, protoEnums: 0, configYamlSequences: 0 },
780
+ pairs: 0,
781
+ pairsWithDeltas: 0,
782
+ unpairedGroups: 0,
783
+ unpairedSets: { total: 0, protoEnums: 0, configYamlSequences: 0 },
784
+ };
785
+ const report = {
786
+ operation: "closed-sets",
787
+ ok: result.ok,
788
+ semantic_status: result.complete ? "completed" : "refused",
789
+ complete: result.complete,
790
+ partial: result.partial,
791
+ trackedPathSetDigest: result.trackedPathSetDigest ?? null,
792
+ scanned_bytes: result.scannedBytes,
793
+ input_bytes: result.inputBytes,
794
+ summary: {
795
+ files: result.fileCount,
796
+ units: result.unitCount,
797
+ ...(result.summary ?? emptySummary),
798
+ diagnostics: result.diagnostics.length,
799
+ },
800
+ closed_sets: result.closedSets,
801
+ diagnostics: result.diagnostics,
802
+ };
803
+ outputClosedSetsReport(report, parsed.options.json);
804
+ return result.ok ? 0 : 1;
805
+ }
806
+ if (subcommand === "evidence") {
807
+ const result = await evidenceAuthorityRepository(
808
+ parsed.options.repositoryPath,
809
+ parsed.options.bindings,
810
+ parsed.options.probeResults,
811
+ {
812
+ maxUnits: parsed.options.maxUnits,
813
+ maxBindings: parsed.options.maxBindings,
814
+ maxLocators: parsed.options.maxLocators,
815
+ maxEdges: parsed.options.maxEdges,
816
+ maxInputBytes: parsed.options.maxInputBytes,
817
+ maxBytes: parsed.options.maxBytes,
818
+ },
819
+ );
820
+ const report = makeReport("evidence", result, result.evidence?.operationStatus ?? "refused", {
821
+ evidence_bytes: result.evidenceBytes,
822
+ evidence: result.evidence,
823
+ partial: result.partial,
824
+ });
825
+ outputEvidenceReport(report, parsed.options.json);
826
+ return result.evidence?.complete && result.evidence.evidenceStatus === "available" ? 0 : 1;
827
+ }
828
+ const result = await reviewAuthorityRepository(
829
+ parsed.options.repositoryPath,
830
+ parsed.options.base,
831
+ parsed.options.bindings,
832
+ parsed.options.dispositions,
833
+ { maxUnits: parsed.options.maxUnits, maxEdges: parsed.options.maxEdges, maxBytes: parsed.options.maxBytes },
834
+ );
835
+ const report = makeReport("review", result, result.review?.operationStatus ?? "refused", {
836
+ review_bytes: result.reviewBytes,
837
+ review: result.review,
838
+ partial: result.partial,
839
+ });
840
+ outputReviewReport(report, parsed.options.json);
841
+ return result.review?.complete && result.review.policyStatus === "passed" ? 0 : 1;
842
+ } catch (error) {
843
+ const message = error instanceof AuthorityInputError ? error.message : `authority ${subcommand} could not evaluate input: ${error.message}`;
844
+ process.stderr.write(`${message}\n`);
845
+ return 2;
846
+ }
847
+ }
848
+
849
+ export { parseOptions };