@meetless/mla 0.1.4

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 (202) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +81 -0
  3. package/dist/build-info.json +9 -0
  4. package/dist/bundles/ask-core.js +396 -0
  5. package/dist/bundles/mcp.js +16592 -0
  6. package/dist/bundles/trace-core.js +263 -0
  7. package/dist/cli.js +828 -0
  8. package/dist/commands/activate.js +781 -0
  9. package/dist/commands/adoption.js +130 -0
  10. package/dist/commands/ask.js +290 -0
  11. package/dist/commands/context.js +114 -0
  12. package/dist/commands/debug.js +313 -0
  13. package/dist/commands/doctor.js +1021 -0
  14. package/dist/commands/enrich.js +427 -0
  15. package/dist/commands/evidence.js +229 -0
  16. package/dist/commands/flush.js +184 -0
  17. package/dist/commands/graph.js +104 -0
  18. package/dist/commands/init.js +272 -0
  19. package/dist/commands/internal-active-review.js +322 -0
  20. package/dist/commands/internal-auto-index.js +188 -0
  21. package/dist/commands/internal-capture-decisions.js +320 -0
  22. package/dist/commands/internal-evidence-correlate.js +239 -0
  23. package/dist/commands/internal-evidence-hooks.js +240 -0
  24. package/dist/commands/internal-evidence-inject.js +231 -0
  25. package/dist/commands/internal-finalize.js +221 -0
  26. package/dist/commands/internal-pretool-observe.js +225 -0
  27. package/dist/commands/internal-refresh.js +136 -0
  28. package/dist/commands/internal-session-nudge.js +120 -0
  29. package/dist/commands/internal-steer-sync.js +117 -0
  30. package/dist/commands/internal-turn-recap.js +140 -0
  31. package/dist/commands/kb.js +375 -0
  32. package/dist/commands/kb_add.js +681 -0
  33. package/dist/commands/kb_forget.js +283 -0
  34. package/dist/commands/kb_move.js +45 -0
  35. package/dist/commands/kb_pending.js +410 -0
  36. package/dist/commands/kb_personal.js +149 -0
  37. package/dist/commands/kb_promote.js +188 -0
  38. package/dist/commands/kb_purge.js +168 -0
  39. package/dist/commands/kb_reingest.js +335 -0
  40. package/dist/commands/kb_retime.js +170 -0
  41. package/dist/commands/kb_review.js +391 -0
  42. package/dist/commands/kb_revision.js +179 -0
  43. package/dist/commands/kb_show.js +385 -0
  44. package/dist/commands/label.js +226 -0
  45. package/dist/commands/login.js +295 -0
  46. package/dist/commands/logout.js +108 -0
  47. package/dist/commands/mcp-supervisor.js +93 -0
  48. package/dist/commands/mcp.js +227 -0
  49. package/dist/commands/queue-prune.js +98 -0
  50. package/dist/commands/review.js +358 -0
  51. package/dist/commands/rewire.js +124 -0
  52. package/dist/commands/rules.js +728 -0
  53. package/dist/commands/scan-context.js +67 -0
  54. package/dist/commands/session.js +347 -0
  55. package/dist/commands/stats.js +479 -0
  56. package/dist/commands/status.js +61 -0
  57. package/dist/commands/summary.js +250 -0
  58. package/dist/commands/turn.js +114 -0
  59. package/dist/commands/uninstall.js +222 -0
  60. package/dist/commands/whoami.js +102 -0
  61. package/dist/commands/workspace.js +130 -0
  62. package/dist/hooks-template/ce0-post-tool-use.sh +34 -0
  63. package/dist/hooks-template/ce0-session-start.sh +49 -0
  64. package/dist/hooks-template/ce0-stop.sh +29 -0
  65. package/dist/hooks-template/ce0-user-prompt-submit.sh +38 -0
  66. package/dist/hooks-template/common.sh +934 -0
  67. package/dist/hooks-template/event-batch-filter.jq +67 -0
  68. package/dist/hooks-template/flush.sh +503 -0
  69. package/dist/hooks-template/post-tool-use.sh +423 -0
  70. package/dist/hooks-template/pre-tool-use.sh +69 -0
  71. package/dist/hooks-template/session-start.sh +140 -0
  72. package/dist/hooks-template/stop.sh +308 -0
  73. package/dist/hooks-template/user-prompt-submit.sh +1162 -0
  74. package/dist/lib/activation.js +79 -0
  75. package/dist/lib/active-conflict-cache.js +141 -0
  76. package/dist/lib/active-memory.js +59 -0
  77. package/dist/lib/active-review-runner.js +26 -0
  78. package/dist/lib/agent-decision/index.js +25 -0
  79. package/dist/lib/agent-decision/keys.js +49 -0
  80. package/dist/lib/agent-decision/normalize-claude.js +183 -0
  81. package/dist/lib/agent-decision/types.js +21 -0
  82. package/dist/lib/agent-decision/validate.js +216 -0
  83. package/dist/lib/analytics/capture.js +96 -0
  84. package/dist/lib/analytics/command-event.js +267 -0
  85. package/dist/lib/analytics/consent.js +58 -0
  86. package/dist/lib/analytics/coverage-gap.js +96 -0
  87. package/dist/lib/analytics/envelope.js +236 -0
  88. package/dist/lib/analytics/event-id.js +86 -0
  89. package/dist/lib/analytics/evidence.js +150 -0
  90. package/dist/lib/analytics/followthrough.js +194 -0
  91. package/dist/lib/analytics/forwarder.js +109 -0
  92. package/dist/lib/analytics/logs.js +78 -0
  93. package/dist/lib/analytics/metrics.js +78 -0
  94. package/dist/lib/analytics/recorder.js +92 -0
  95. package/dist/lib/analytics/review-analytics.js +75 -0
  96. package/dist/lib/analytics/sequence.js +77 -0
  97. package/dist/lib/analytics/store.js +131 -0
  98. package/dist/lib/analytics/turn-recap.js +279 -0
  99. package/dist/lib/artifact_id.js +108 -0
  100. package/dist/lib/auth-breaker.js +161 -0
  101. package/dist/lib/auto-index.js +112 -0
  102. package/dist/lib/classifier.js +88 -0
  103. package/dist/lib/config.js +298 -0
  104. package/dist/lib/conflict-advisory.js +64 -0
  105. package/dist/lib/debug-bundle.js +520 -0
  106. package/dist/lib/enrichment/ingest.js +301 -0
  107. package/dist/lib/enrichment/plan.js +253 -0
  108. package/dist/lib/enrichment/protocol.js +359 -0
  109. package/dist/lib/enrichment/scout-brief.js +176 -0
  110. package/dist/lib/failure-telemetry.js +444 -0
  111. package/dist/lib/git.js +200 -0
  112. package/dist/lib/governance-cache.js +77 -0
  113. package/dist/lib/governed-path-cache.js +76 -0
  114. package/dist/lib/http.js +677 -0
  115. package/dist/lib/identity-envelope.js +23 -0
  116. package/dist/lib/kb-candidate.js +65 -0
  117. package/dist/lib/kb_acl.js +98 -0
  118. package/dist/lib/login.js +353 -0
  119. package/dist/lib/mcp-fetchers.js +130 -0
  120. package/dist/lib/mcp-restart.js +47 -0
  121. package/dist/lib/observability.js +805 -0
  122. package/dist/lib/open-url.js +33 -0
  123. package/dist/lib/orphan-guard.js +70 -0
  124. package/dist/lib/packaged.js +21 -0
  125. package/dist/lib/reconcile-sessions.js +171 -0
  126. package/dist/lib/redactor.js +89 -0
  127. package/dist/lib/relationship-candidate-query.js +27 -0
  128. package/dist/lib/render.js +611 -0
  129. package/dist/lib/rules/applicability.js +64 -0
  130. package/dist/lib/rules/attest-code-rule-version.js +47 -0
  131. package/dist/lib/rules/attest-notes-location.js +217 -0
  132. package/dist/lib/rules/attest-rule-version.js +69 -0
  133. package/dist/lib/rules/canonical-json.js +97 -0
  134. package/dist/lib/rules/ce0-emit.js +64 -0
  135. package/dist/lib/rules/ce0-evidence.js +281 -0
  136. package/dist/lib/rules/ce0-recall-sample.js +82 -0
  137. package/dist/lib/rules/ce0-rule.js +55 -0
  138. package/dist/lib/rules/ce0-sampling-bucket.js +15 -0
  139. package/dist/lib/rules/ce0-store.js +683 -0
  140. package/dist/lib/rules/ce0-telemetry-project.js +93 -0
  141. package/dist/lib/rules/ce0-telemetry.js +158 -0
  142. package/dist/lib/rules/code-rule-registry.js +17 -0
  143. package/dist/lib/rules/command-match.js +185 -0
  144. package/dist/lib/rules/consult-evidence-binding.js +27 -0
  145. package/dist/lib/rules/consultation-capture-adapter.js +193 -0
  146. package/dist/lib/rules/content-match.js +56 -0
  147. package/dist/lib/rules/deny-admission.js +99 -0
  148. package/dist/lib/rules/durable-observation.js +190 -0
  149. package/dist/lib/rules/enforce-notes-version.js +421 -0
  150. package/dist/lib/rules/evaluation-input-hash.js +126 -0
  151. package/dist/lib/rules/evaluator.js +108 -0
  152. package/dist/lib/rules/inert-rule-families.js +51 -0
  153. package/dist/lib/rules/input-authority-resolver.js +241 -0
  154. package/dist/lib/rules/interception-schema.js +170 -0
  155. package/dist/lib/rules/interception-store.js +267 -0
  156. package/dist/lib/rules/live-input-authority.js +66 -0
  157. package/dist/lib/rules/local-matcher.js +108 -0
  158. package/dist/lib/rules/local-observe.js +79 -0
  159. package/dist/lib/rules/local-rule-version-repo.js +214 -0
  160. package/dist/lib/rules/memory-requirement.js +109 -0
  161. package/dist/lib/rules/notes-observe.js +39 -0
  162. package/dist/lib/rules/notes-path.js +261 -0
  163. package/dist/lib/rules/notes-rule.js +75 -0
  164. package/dist/lib/rules/observe-adapter.js +114 -0
  165. package/dist/lib/rules/observed-rule-hash.js +119 -0
  166. package/dist/lib/rules/prompt-submit-adapter.js +132 -0
  167. package/dist/lib/rules/requirement-subject.js +240 -0
  168. package/dist/lib/rules/rule-activity.js +67 -0
  169. package/dist/lib/rules/rule-version-hash.js +151 -0
  170. package/dist/lib/rules/runtime-scope.js +55 -0
  171. package/dist/lib/rules/stop-adapter.js +116 -0
  172. package/dist/lib/rules/stop-response-snapshot.js +174 -0
  173. package/dist/lib/rules/types.js +10 -0
  174. package/dist/lib/rules/ulid.js +46 -0
  175. package/dist/lib/rules/version-evaluation.js +156 -0
  176. package/dist/lib/scanner/agent-memory.js +99 -0
  177. package/dist/lib/scanner/bootstrap-summary.js +87 -0
  178. package/dist/lib/scanner/cache.js +59 -0
  179. package/dist/lib/scanner/frontmatter.js +42 -0
  180. package/dist/lib/scanner/parse-directives.js +69 -0
  181. package/dist/lib/scanner/parse-structured.js +72 -0
  182. package/dist/lib/scanner/render.js +73 -0
  183. package/dist/lib/scanner/scan.js +132 -0
  184. package/dist/lib/scanner/score.js +38 -0
  185. package/dist/lib/scanner/scout-mission.js +126 -0
  186. package/dist/lib/scanner/types.js +7 -0
  187. package/dist/lib/session-scope.js +195 -0
  188. package/dist/lib/spool.js +355 -0
  189. package/dist/lib/staleness.js +100 -0
  190. package/dist/lib/steer-cache.js +87 -0
  191. package/dist/lib/tagged-reference.js +20 -0
  192. package/dist/lib/temporal.js +109 -0
  193. package/dist/lib/turn-recap-emit.js +67 -0
  194. package/dist/lib/unwire.js +253 -0
  195. package/dist/lib/update-check.js +469 -0
  196. package/dist/lib/update-notifier.js +217 -0
  197. package/dist/lib/upgrade-apply.js +643 -0
  198. package/dist/lib/wire.js +1087 -0
  199. package/dist/lib/workspace.js +96 -0
  200. package/dist/lib/zip.js +154 -0
  201. package/dist/pretool-entry.js +37 -0
  202. package/package.json +75 -0
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ // `mla kb accept <doc-id>` / `mla kb reject <doc-id>` (revision review).
3
+ //
4
+ // A `mla kb reingest` of a doc that has PROMOTED relationship edges does NOT
5
+ // replace the live bytes outright. It parks the new revision at
6
+ // SHADOW_PENDING_REVIEW (the old revision keeps grounding answers) so a human
7
+ // confirms the change before it ships. These two commands are the consumer side
8
+ // of that park:
9
+ //
10
+ // accept -> POST /internal/v1/kb/documents/<id>/accept
11
+ // the pending revision becomes current; it starts grounding answers,
12
+ // the old revision is superseded, KB_REVISION_ACCEPTED is emitted.
13
+ // reject -> POST /internal/v1/kb/documents/<id>/reject
14
+ // the pending revision is discarded; the old revision keeps serving,
15
+ // KB_REVISION_REJECTED is emitted, and the rejected revision's vector
16
+ // chunks are GC'd best-effort.
17
+ //
18
+ // Both act on a DOCUMENT id; the route resolves the doc's single
19
+ // SHADOW_PENDING_REVIEW revision itself (zero or many is a 409). A `note:` or
20
+ // `kbdocrev:` input is therefore a usage error, not a silent path resolution.
21
+ //
22
+ // Mirrors the kb_retime.ts deps-injection shape: a thin public runner loads the
23
+ // real config (readKbConfig) and wires the real intelPost, while every
24
+ // collaborator is injectable so the unit test drives it offline. Auth + actor:
25
+ // the POST carries Authorization via the shared intel HTTP layer; the actor
26
+ // rides in the BODY as actorUserId (intel stamps only Authorization + X-Trace-ID,
27
+ // never an actor header), so it comes from cfg.actorUserId. For a cli-session
28
+ // caller intel ignores the body actorUserId in favor of the session user.
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.parseKbRevisionArgs = parseKbRevisionArgs;
31
+ exports.runKbAccept = runKbAccept;
32
+ exports.runKbReject = runKbReject;
33
+ const config_1 = require("../lib/config");
34
+ const http_1 = require("../lib/http");
35
+ const VALUE_FLAGS = new Set(["--reason"]);
36
+ const BOOLEAN_FLAGS = new Set(["--json"]);
37
+ const KBDOC_PREFIX = "kbdoc:";
38
+ function usage(action) {
39
+ return `Usage: mla kb ${action} <kbdoc:<id>|<doc-id>> [--reason <s>] [--json]`;
40
+ }
41
+ // Parse a single positional document id, plus optional --reason / --json. The id
42
+ // may carry the canonical `kbdoc:` prefix (stripped to the raw id for the URL
43
+ // path) or be bare. A `note:` or `kbdocrev:` input is rejected: the route is
44
+ // keyed on a document id and resolves the pending revision itself, so neither a
45
+ // path nor a specific revision id is a valid target. The action only shapes the
46
+ // error text; both verbs share this grammar.
47
+ function parseKbRevisionArgs(argv, action = "accept") {
48
+ let documentId = null;
49
+ let reason;
50
+ let json = false;
51
+ for (let i = 0; i < argv.length; i++) {
52
+ const a = argv[i];
53
+ if (VALUE_FLAGS.has(a)) {
54
+ const next = argv[i + 1];
55
+ if (next === undefined || next.startsWith("-")) {
56
+ throw new Error(`Missing value for ${a}. ${usage(action)}`);
57
+ }
58
+ if (a === "--reason")
59
+ reason = next;
60
+ i++;
61
+ }
62
+ else if (BOOLEAN_FLAGS.has(a)) {
63
+ json = true;
64
+ }
65
+ else if (a.startsWith("-")) {
66
+ const supported = [...VALUE_FLAGS, ...BOOLEAN_FLAGS].sort().join(", ");
67
+ throw new Error(`Unknown flag: ${a}. Supported: ${supported}. ${usage(action)}`);
68
+ }
69
+ else if (documentId === null) {
70
+ documentId = a;
71
+ }
72
+ else {
73
+ throw new Error(`Unexpected argument: ${a}. ${usage(action)}`);
74
+ }
75
+ }
76
+ if (documentId === null) {
77
+ throw new Error(`mla kb ${action} requires a document id. ${usage(action)}`);
78
+ }
79
+ const trimmed = documentId.trim();
80
+ if (trimmed.startsWith("note:")) {
81
+ throw new Error(`mla kb ${action} acts on a document id, not a note path. ` +
82
+ `Run \`mla kb show ${trimmed}\` to find its kbdoc id, then \`mla kb ${action} kbdoc:<id>\`.`);
83
+ }
84
+ if (trimmed.startsWith("kbdocrev:")) {
85
+ throw new Error(`mla kb ${action} acts on a document id, not a revision id. The route resolves ` +
86
+ `the document's single pending revision itself; pass the kbdoc id instead.`);
87
+ }
88
+ const id = trimmed.startsWith(KBDOC_PREFIX) ? trimmed.slice(KBDOC_PREFIX.length).trim() : trimmed;
89
+ if (!id) {
90
+ throw new Error(`mla kb ${action} requires a non-empty document id. ${usage(action)}`);
91
+ }
92
+ return { documentId: id, reason, json };
93
+ }
94
+ // Surface an intel HTTP failure helpfully, mirroring kb_retime.ts's explainRetimeError.
95
+ function explainReviewError(action, err, intelUrl) {
96
+ if (err.status === 404) {
97
+ return `intel returned 404: document not found in this workspace (or this intel does not expose the KB ${action} route).`;
98
+ }
99
+ if (err.status === 409) {
100
+ return (`intel returned 409: the document has no single pending revision to ${action}. ` +
101
+ `A revision is only reviewable when a reingest parked it at SHADOW_PENDING_REVIEW. ` +
102
+ `Details: ${err.body ?? err.message}`);
103
+ }
104
+ if (err.status === 401 || err.status === 403) {
105
+ return `intel rejected the request (HTTP ${err.status}). Check controlToken / workspace in cli-config.json.`;
106
+ }
107
+ if (err.status === undefined) {
108
+ return `intel not reachable at ${intelUrl}. Is it running? Try \`mla doctor\`.`;
109
+ }
110
+ return err.message;
111
+ }
112
+ // Render the review receipt in plain words. No double-dash range separators
113
+ // (An's AI-smell rule); each line spells out what changed.
114
+ function renderReceipt(r) {
115
+ const out = [];
116
+ if (r.action === "accepted") {
117
+ out.push(`Accepted revision ${r.reviewedRevisionId} on ${r.documentId}.`);
118
+ out.push(` current revision: ${r.currentRevisionId} (now grounding answers)`);
119
+ out.push(` the prior revision was superseded; its lexical chunks were soft-tombstoned.`);
120
+ out.push("");
121
+ out.push("The pending revision is now live. Its vector chunks ground answers; the old revision no longer does.");
122
+ }
123
+ else {
124
+ out.push(`Rejected revision ${r.reviewedRevisionId} on ${r.documentId}.`);
125
+ out.push(` current revision: ${r.currentRevisionId} (unchanged; still grounding answers)`);
126
+ out.push(` lexical chunks deleted: the rejected revision's chunk_fts rows were removed.`);
127
+ out.push(` vector chunks deleted: ${r.weaviateChunksDeleted}`);
128
+ out.push("");
129
+ out.push("The pending revision was discarded. The previously live revision keeps serving unchanged.");
130
+ }
131
+ return out.join("\n");
132
+ }
133
+ async function runRevisionReview(action, argv, deps) {
134
+ let cfg;
135
+ try {
136
+ cfg = deps?.cfg ?? (0, config_1.readKbConfig)();
137
+ }
138
+ catch (e) {
139
+ console.error(e.message);
140
+ return 2;
141
+ }
142
+ let parsed;
143
+ try {
144
+ parsed = parseKbRevisionArgs(argv, action);
145
+ }
146
+ catch (e) {
147
+ console.error(e.message);
148
+ return 2;
149
+ }
150
+ const post = deps?.http?.intelPost ?? http_1.intelPost;
151
+ const body = {
152
+ workspaceId: cfg.workspaceId,
153
+ actorUserId: cfg.actorUserId,
154
+ };
155
+ if (parsed.reason !== undefined)
156
+ body.reason = parsed.reason;
157
+ const path = `/internal/v1/kb/documents/${encodeURIComponent(parsed.documentId)}/${action}`;
158
+ let res;
159
+ try {
160
+ res = (await post(cfg, path, body));
161
+ }
162
+ catch (e) {
163
+ const intelUrl = cfg.intelUrl || http_1.DEFAULT_INTEL_URL;
164
+ console.error(explainReviewError(action, e, intelUrl));
165
+ return 1;
166
+ }
167
+ if (parsed.json) {
168
+ console.log(JSON.stringify(res, null, 2));
169
+ return 0;
170
+ }
171
+ console.log(renderReceipt(res));
172
+ return 0;
173
+ }
174
+ async function runKbAccept(argv, deps) {
175
+ return runRevisionReview("accept", argv, deps);
176
+ }
177
+ async function runKbReject(argv, deps) {
178
+ return runRevisionReview("reject", argv, deps);
179
+ }
@@ -0,0 +1,385 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseKbShowArgs = parseKbShowArgs;
4
+ exports.runKbShow = runKbShow;
5
+ const config_1 = require("../lib/config");
6
+ const workspace_1 = require("../lib/workspace");
7
+ const http_1 = require("../lib/http");
8
+ const open_url_1 = require("../lib/open-url");
9
+ const artifact_id_1 = require("../lib/artifact_id");
10
+ const temporal_1 = require("../lib/temporal");
11
+ const render_1 = require("../lib/render");
12
+ const VALUE_FLAGS = new Set(["--workspace", "--posture", "--as-of"]);
13
+ const BOOLEAN_FLAGS = new Set([
14
+ "--include-tombstoned",
15
+ "--json",
16
+ "--all",
17
+ "--audit-all",
18
+ "--open",
19
+ ]);
20
+ const REVISION_LIMIT_DEFAULT = 20;
21
+ const REVISION_LIMIT_ALL = 200;
22
+ const AUDIT_LIMIT_DEFAULT = 10;
23
+ const AUDIT_LIMIT_ALL = 500;
24
+ function parseKbShowArgs(argv) {
25
+ const out = {
26
+ posture: "both",
27
+ includeTombstoned: false,
28
+ json: false,
29
+ all: false,
30
+ auditAll: false,
31
+ open: false,
32
+ };
33
+ let positional = null;
34
+ for (let i = 0; i < argv.length; i++) {
35
+ const a = argv[i];
36
+ if (VALUE_FLAGS.has(a)) {
37
+ const v = argv[i + 1];
38
+ if (v === undefined) {
39
+ throw new Error(`Missing value for ${a}`);
40
+ }
41
+ if (v.startsWith("--") || v.startsWith("-")) {
42
+ throw new Error(`Missing value for ${a} (got the next flag ${v} instead)`);
43
+ }
44
+ switch (a) {
45
+ case "--workspace":
46
+ out.workspace = v;
47
+ break;
48
+ case "--posture":
49
+ if (v !== "both" && v !== "LIVE" && v !== "SHADOW") {
50
+ throw new Error(`--posture must be 'both', 'LIVE', or 'SHADOW' (got '${v}')`);
51
+ }
52
+ out.posture = v;
53
+ break;
54
+ case "--as-of":
55
+ // parseAsOf throws on a malformed date so a typo never silently
56
+ // answers as-of "now"; runKbShow maps the throw to exit 2.
57
+ out.asOf = (0, temporal_1.parseAsOf)(v);
58
+ break;
59
+ }
60
+ i += 1;
61
+ continue;
62
+ }
63
+ if (BOOLEAN_FLAGS.has(a)) {
64
+ if (a === "--include-tombstoned")
65
+ out.includeTombstoned = true;
66
+ else if (a === "--json")
67
+ out.json = true;
68
+ else if (a === "--all")
69
+ out.all = true;
70
+ else if (a === "--audit-all")
71
+ out.auditAll = true;
72
+ else if (a === "--open")
73
+ out.open = true;
74
+ continue;
75
+ }
76
+ if (a.startsWith("--") || a.startsWith("-")) {
77
+ throw new Error(`Unknown flag: ${a}. Supported flags: ${[...VALUE_FLAGS, ...BOOLEAN_FLAGS].sort().join(", ")}`);
78
+ }
79
+ if (positional !== null) {
80
+ throw new Error(`\`mla kb show\` takes exactly one positional <input> (got '${positional}' and '${a}')`);
81
+ }
82
+ positional = a;
83
+ }
84
+ if (positional === null) {
85
+ throw new Error("`mla kb show` requires a positional <input> (kbdoc:<id> or a note path)");
86
+ }
87
+ return {
88
+ input: positional,
89
+ workspace: out.workspace,
90
+ posture: out.posture,
91
+ includeTombstoned: !!out.includeTombstoned,
92
+ json: !!out.json,
93
+ all: !!out.all,
94
+ auditAll: !!out.auditAll,
95
+ open: !!out.open,
96
+ asOf: out.asOf,
97
+ };
98
+ }
99
+ function explainIntelError(err, intelUrl) {
100
+ if (err.status === 404) {
101
+ return err.body && err.body.includes("KB_DOCUMENT_PATH_NOT_FOUND")
102
+ ? `No KbDocument matches that path in the requested workspace. Try \`mla kb dump\` to list ingested sources.`
103
+ : err.body && err.body.includes("KB_DOCUMENT_NOT_FOUND")
104
+ ? `No KbDocument matches that id in the requested workspace.`
105
+ : `intel returned 404. ${err.body.slice(0, 200)}`;
106
+ }
107
+ if (err.status === 401 || err.status === 403) {
108
+ return `intel rejected the token (HTTP ${err.status}). Check controlToken in cli-config.json.`;
109
+ }
110
+ if (err.status === undefined) {
111
+ return `intel not reachable at ${intelUrl}. Is it running? Try \`mla doctor\`.`;
112
+ }
113
+ return err.message;
114
+ }
115
+ function toShowRevision(r) {
116
+ return {
117
+ id: r.revisionId,
118
+ status: r.status,
119
+ ingestRunId: r.ingestRunId,
120
+ postureAtIngest: r.postureAtIngest,
121
+ normalizedBodyHash: r.normalizedBodyHash,
122
+ frontmatterHash: r.frontmatterHash,
123
+ fullDocumentHash: r.fullDocumentHash,
124
+ chunkCount: r.chunkCount,
125
+ createdAt: r.createdAt,
126
+ failureCode: r.status === "FAILED" ? r.failureCode : null,
127
+ failureReason: r.status === "FAILED" ? r.failureReason : null,
128
+ failedAt: r.status === "FAILED" ? r.failedAt : null,
129
+ provenanceOverride: r.provenanceOverride,
130
+ overrideReason: r.overrideReason,
131
+ };
132
+ }
133
+ function toShowChunk(c) {
134
+ return {
135
+ id: c.chunkUuid,
136
+ ordinal: c.chunkIndex,
137
+ bytes: c.contentLength,
138
+ preview: c.preview,
139
+ revisionId: c.revisionId,
140
+ };
141
+ }
142
+ function postureOf(endpoint) {
143
+ // Intel doesn't carry per-edge posture on candidates/promoted edges in this
144
+ // response shape; use the counterpart's tombstone state as a proxy hint so
145
+ // the renderer still has SOMETHING to print. Both posture filters operate
146
+ // post-render via fmtEdgeLine. The wire string is opaque to the renderer.
147
+ return endpoint.counterpartTombstoneState === "ACTIVE" ||
148
+ endpoint.counterpartTombstoneState === null
149
+ ? "LIVE"
150
+ : "SHADOW";
151
+ }
152
+ function pickCounterpartEndpoint(fromEndpoint, toEndpoint) {
153
+ // The "self" side is whichever endpoint has isThisDoc=true. The counterpart
154
+ // is the other one. Direction is from-this-doc -> counterpart for outgoing,
155
+ // counterpart -> this-doc for incoming.
156
+ if (fromEndpoint.isThisDoc) {
157
+ return { endpoint: toEndpoint, direction: "outgoing" };
158
+ }
159
+ return { endpoint: fromEndpoint, direction: "incoming" };
160
+ }
161
+ function toCandidateRow(c) {
162
+ const pick = pickCounterpartEndpoint(c.fromEndpoint, c.toEndpoint);
163
+ return {
164
+ id: c.candidateId,
165
+ kind: "candidate",
166
+ direction: pick.direction,
167
+ predicate: c.relationType,
168
+ status: c.status,
169
+ posture: postureOf(pick.endpoint),
170
+ counterpart: {
171
+ documentId: pick.endpoint.counterpartKbDocumentId || pick.endpoint.endpointId,
172
+ canonicalPath: null,
173
+ tombstoneState: pick.endpoint.counterpartTombstoneState,
174
+ tombstonedAt: pick.endpoint.counterpartTombstonedAt,
175
+ },
176
+ };
177
+ }
178
+ function toPromotedRow(e) {
179
+ const pick = pickCounterpartEndpoint(e.fromEndpoint, e.toEndpoint);
180
+ return {
181
+ id: e.edgeId,
182
+ kind: "promoted",
183
+ direction: pick.direction,
184
+ predicate: e.relationType,
185
+ status: e.isRetrievable ? "ACTIVE" : "RETIRED",
186
+ posture: postureOf(pick.endpoint),
187
+ counterpart: {
188
+ documentId: pick.endpoint.counterpartKbDocumentId || pick.endpoint.endpointId,
189
+ canonicalPath: null,
190
+ tombstoneState: pick.endpoint.counterpartTombstoneState,
191
+ tombstonedAt: pick.endpoint.counterpartTombstonedAt,
192
+ },
193
+ // Task 5.2: carry the VALID-time window + clock trust onto the row so the
194
+ // renderer shows when the relation was true and how trustworthy that clock
195
+ // is. UNKNOWN is the honest fallback for a legacy edge with no source.
196
+ validity: {
197
+ validAt: e.validAt ?? null,
198
+ invalidAt: e.invalidAt ?? null,
199
+ validTimeSource: e.validTimeSource ?? "UNKNOWN",
200
+ sourceObservedAt: e.sourceObservedAt ?? null,
201
+ },
202
+ };
203
+ }
204
+ function toAuditEntry(a) {
205
+ return {
206
+ id: a.auditEventId,
207
+ eventType: a.eventType,
208
+ occurredAt: a.occurredAt,
209
+ actorId: a.actorUserId,
210
+ subjectId: a.artifactId,
211
+ payloadDigest: null,
212
+ };
213
+ }
214
+ function filterEdgesByPosture(rows, posture, includeTombstoned) {
215
+ return rows.filter((r) => {
216
+ const cpState = r.counterpart.tombstoneState;
217
+ const cpIsTombstoned = cpState !== null && cpState !== "ACTIVE";
218
+ if (cpIsTombstoned && !includeTombstoned)
219
+ return false;
220
+ if (posture === "both")
221
+ return true;
222
+ return r.posture === posture;
223
+ });
224
+ }
225
+ function buildShowView(resp, flags) {
226
+ const candidates = filterEdgesByPosture(resp.candidates.map(toCandidateRow), flags.posture, flags.includeTombstoned);
227
+ const promoted = filterEdgesByPosture(resp.promotedEdges.map(toPromotedRow), flags.posture, flags.includeTombstoned);
228
+ const totalChunks = resp.chunks.active.length +
229
+ resp.chunks.pendingReview.length +
230
+ resp.chunks.superseded.length;
231
+ const activePreview = resp.chunks.active.slice(0, 5).map(toShowChunk);
232
+ const pendingPreview = resp.chunks.pendingReview
233
+ .slice(0, 5)
234
+ .map(toShowChunk);
235
+ const revisionLimit = flags.all ? REVISION_LIMIT_ALL : REVISION_LIMIT_DEFAULT;
236
+ const auditLimit = flags.auditAll ? AUDIT_LIMIT_ALL : AUDIT_LIMIT_DEFAULT;
237
+ return {
238
+ workspaceId: resp.workspaceId,
239
+ document: {
240
+ id: resp.identity.documentId,
241
+ canonicalPath: resp.identity.canonicalPath,
242
+ pathAliases: resp.identity.pathAliases || [],
243
+ parentUuid: resp.identity.parentUuid,
244
+ provenance: resp.identity.provenance,
245
+ currentPosture: resp.identity.currentPosture,
246
+ effectivePosture: resp.identity.effectivePosture,
247
+ tombstoneState: resp.identity.tombstoneState,
248
+ tombstonedAt: resp.identity.tombstonedAt,
249
+ tombstoneReason: resp.identity.tombstoneReason,
250
+ tombstoneActorId: resp.identity.tombstoneActorId,
251
+ deletedAt: resp.identity.deletedAt,
252
+ deletedBy: resp.identity.deletedBy,
253
+ redactedPathHash: null,
254
+ createdAt: resp.identity.createdAt,
255
+ updatedAt: resp.identity.updatedAt,
256
+ },
257
+ currentRevision: resp.currentRevision
258
+ ? toShowRevision(resp.currentRevision)
259
+ : null,
260
+ revisionHistory: resp.revisions.map(toShowRevision),
261
+ revisionHistoryTruncated: resp.revisions.length >= revisionLimit,
262
+ chunks: {
263
+ totalCount: totalChunks,
264
+ totalBytes: resp.chunks.totalContentBytes,
265
+ preview: activePreview,
266
+ pendingReviewPreview: pendingPreview,
267
+ },
268
+ candidates,
269
+ promoted,
270
+ audit: resp.auditTrail.map(toAuditEntry),
271
+ auditTruncated: resp.auditTrail.length >= auditLimit,
272
+ includeTombstoned: flags.includeTombstoned,
273
+ extraction: resp.extraction ?? null,
274
+ };
275
+ }
276
+ async function resolveToDocumentId(cfg, workspaceId, input, intelUrl) {
277
+ if (input.kind === "kbdoc") {
278
+ return input.id;
279
+ }
280
+ if (input.kind === "kbdocrev") {
281
+ throw new Error("`mla kb show` does not accept kbdocrev:<id>. Pass the parent kbdoc:<id> or the note path; revision details are rendered inside the document view.");
282
+ }
283
+ const qs = new URLSearchParams({
284
+ workspaceId,
285
+ path: input.path,
286
+ }).toString();
287
+ try {
288
+ const r = await (0, http_1.intelGet)(cfg, `/internal/v1/kb/documents/resolve?${qs}`, 10000);
289
+ return r.documentId;
290
+ }
291
+ catch (e) {
292
+ const err = e;
293
+ throw new Error(explainIntelError(err, intelUrl));
294
+ }
295
+ }
296
+ async function runKbShow(argv) {
297
+ let cfg;
298
+ try {
299
+ cfg = (0, config_1.readConfig)();
300
+ }
301
+ catch (e) {
302
+ console.error(e.message);
303
+ return 2;
304
+ }
305
+ let flags;
306
+ try {
307
+ flags = parseKbShowArgs(argv);
308
+ }
309
+ catch (e) {
310
+ console.error(e.message);
311
+ return 2;
312
+ }
313
+ let input;
314
+ try {
315
+ input = (0, artifact_id_1.parseArtifactInput)(flags.input);
316
+ }
317
+ catch (e) {
318
+ const msg = e instanceof artifact_id_1.ArtifactInputError ? e.message : e.message;
319
+ console.error(msg);
320
+ return 2;
321
+ }
322
+ // Folder = workspace (T1.1): the workspace comes from the nearest marker;
323
+ // `--workspace <id>` overrides it (admin cross-workspace inspection) and
324
+ // short-circuits marker resolution so an unbound directory does not block it.
325
+ let workspaceId;
326
+ try {
327
+ workspaceId = flags.workspace || (0, workspace_1.resolveWorkspaceId)();
328
+ }
329
+ catch (e) {
330
+ console.error(e.message);
331
+ return 2;
332
+ }
333
+ const intelUrl = cfg.intelUrl || http_1.DEFAULT_INTEL_URL;
334
+ let documentId;
335
+ try {
336
+ documentId = await resolveToDocumentId(cfg, workspaceId, input, intelUrl);
337
+ }
338
+ catch (e) {
339
+ console.error(e.message);
340
+ return 1;
341
+ }
342
+ const revisionLimit = flags.all ? REVISION_LIMIT_ALL : REVISION_LIMIT_DEFAULT;
343
+ const auditLimit = flags.auditAll ? AUDIT_LIMIT_ALL : AUDIT_LIMIT_DEFAULT;
344
+ const qsParams = new URLSearchParams({
345
+ workspaceId,
346
+ revisionLimit: String(revisionLimit),
347
+ auditLimit: String(auditLimit),
348
+ });
349
+ if (flags.asOf) {
350
+ // Point-in-time view: the detail endpoint runs the trusted-source as-of
351
+ // filter so edges not yet valid at this instant (or from an untrusted clock)
352
+ // are excluded. The banner goes to stderr so it never pollutes --json stdout.
353
+ qsParams.set("asOf", flags.asOf);
354
+ console.error(`Point-in-time view as of ${flags.asOf} (relations not yet valid at that instant, or from an untrusted clock, are excluded).`);
355
+ }
356
+ const qs = qsParams.toString();
357
+ let detail;
358
+ try {
359
+ detail = await (0, http_1.intelGet)(cfg, `/internal/v1/kb/documents/${encodeURIComponent(documentId)}/detail?${qs}`, 15000);
360
+ }
361
+ catch (e) {
362
+ console.error(explainIntelError(e, intelUrl));
363
+ return 1;
364
+ }
365
+ const view = buildShowView(detail, flags);
366
+ // B4a: always surface the Console review URL. The renderer stays pure, so the
367
+ // command layer resolves it via getConsoleUrl(cfg) and stamps it on the view.
368
+ view.consoleUrl = `${(0, config_1.getConsoleUrl)(cfg)}/relationships`;
369
+ if (flags.json) {
370
+ console.log(JSON.stringify(view, null, 2));
371
+ }
372
+ else {
373
+ console.log((0, render_1.renderKbShow)(view));
374
+ }
375
+ // B4b: `--open` is opt-in (never auto-open; the URL is always printed above). The
376
+ // status note goes to stderr so it never pollutes `--json` stdout.
377
+ if (flags.open && view.consoleUrl) {
378
+ const res = (0, open_url_1.openUrl)(view.consoleUrl);
379
+ if (res.ok)
380
+ console.error(`opened ${view.consoleUrl} in your browser`);
381
+ else
382
+ console.error(`could not open a browser (${res.error}); the URL is above`);
383
+ }
384
+ return 0;
385
+ }