@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,87 @@
1
+ "use strict";
2
+ // Cross-session steer transport: the zero-network hand-off between the CLI
3
+ // `_internal steer-sync` hop (which pulls steers from control) and the
4
+ // UserPromptSubmit hook (which injects them).
5
+ //
6
+ // steer-sync writes the deliverable set to steer-<sid>.json; the hook reads it
7
+ // with NO network call (same hot-path constraint as the governance nudge). The
8
+ // hook records which steer ids it injected in inject-<sid>.json; steer-sync reads
9
+ // THAT to mark them injected (PULLED -> INJECTED). Both files live under
10
+ // $MEETLESS_HOME/logs/steer/ and the paths + shapes MUST stay byte-identical to
11
+ // the bash helpers in common.sh (steer_cache_file / steer_inject_file). The
12
+ // session id is opaque (CLAUDE_CODE_SESSION_ID); like governance_inject_file it
13
+ // is used verbatim, no sanitization.
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.steerCachePath = steerCachePath;
49
+ exports.steerInjectStatePath = steerInjectStatePath;
50
+ exports.writeSteerCache = writeSteerCache;
51
+ exports.readInjectedIds = readInjectedIds;
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const config_1 = require("./config");
55
+ function steerCachePath(sessionId, home = config_1.HOME) {
56
+ return path.join(home, "logs", "steer", `steer-${sessionId}.json`);
57
+ }
58
+ function steerInjectStatePath(sessionId, home = config_1.HOME) {
59
+ return path.join(home, "logs", "steer", `inject-${sessionId}.json`);
60
+ }
61
+ // Best-effort: a failed cache write must never break the steer-sync hop (which is
62
+ // itself best-effort inside flush.sh). Worst case the hook keeps reading the prior
63
+ // cache until the next successful write.
64
+ function writeSteerCache(sessionId, steers, home = config_1.HOME) {
65
+ try {
66
+ const file = steerCachePath(sessionId, home);
67
+ fs.mkdirSync(path.dirname(file), { recursive: true });
68
+ fs.writeFileSync(file, JSON.stringify({ steers, ts: Math.floor(Date.now() / 1000) }));
69
+ }
70
+ catch {
71
+ /* non-fatal */
72
+ }
73
+ }
74
+ // Read the ids the hook recorded as injected. Returns [] on any absence/parse
75
+ // failure (the mark-injected loop then simply has nothing to flip this round).
76
+ function readInjectedIds(sessionId, home = config_1.HOME) {
77
+ try {
78
+ const file = steerInjectStatePath(sessionId, home);
79
+ const body = JSON.parse(fs.readFileSync(file, "utf8"));
80
+ if (!Array.isArray(body.injected))
81
+ return [];
82
+ return body.injected.filter((x) => typeof x === "string");
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.supersessionAdvisory = supersessionAdvisory;
4
+ function supersessionAdvisory(referencedPaths, facts) {
5
+ const refs = new Set(referencedPaths);
6
+ const out = [];
7
+ for (const f of facts) {
8
+ if (!refs.has(f.fromPath))
9
+ continue;
10
+ if (f.posture !== "LIVE" || f.status !== "ACCEPTED")
11
+ continue; // approved facts only
12
+ if (f.relationType === "SUPERSEDED_BY") {
13
+ out.push({ citedKbId: f.toKbId, message: `${f.fromPath} is superseded by ${f.toPath} (${f.toKbId}); prefer the newer doc.` });
14
+ }
15
+ else if (f.relationType === "CONTRADICTS") {
16
+ out.push({ citedKbId: f.toKbId, message: `${f.fromPath} contradicts ${f.toPath} (${f.toKbId}).` });
17
+ }
18
+ }
19
+ return out;
20
+ }
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ // Shared temporal helpers for the MLA CLI's as-of surfaces (B9).
3
+ //
4
+ // `parseAsOf` normalizes an operator-supplied date into a UTC ISO-8601 instant
5
+ // (a VALID-time cutoff: "what was true as of this instant"). It is the front
6
+ // door for `mla ask --as-of` and `mla kb show --as-of`; the intel side reads it
7
+ // as `AskRequest.as_of` and runs the point-in-time filter. We reject anything
8
+ // malformed so a typo never silently answers as-of "now".
9
+ //
10
+ // Render helpers (renderValidWindow, renderTrustLabel) live here too so every
11
+ // as-of surface formats the bi-temporal window and the clock trust the same
12
+ // way. `kb show` is the first consumer (Task 5.2); the help surfaces (cli.ts
13
+ // USAGE + kb.ts KB_USAGE) teach the same two temporal axes valid-time and
14
+ // observation-time that these helpers render (Task 5.4).
15
+ //
16
+ // Plan: notes/20260605-mla-full-temporal-awareness-implementation-plan.md, Tasks 5.1-5.4.
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.parseAsOf = parseAsOf;
19
+ exports.renderTrustLabel = renderTrustLabel;
20
+ exports.renderValidWindow = renderValidWindow;
21
+ const DASHED = /^(\d{4})-(\d{2})-(\d{2})$/;
22
+ const COMPACT = /^(\d{4})(\d{2})(\d{2})$/;
23
+ // Build a midnight-UTC ISO instant from year/month/day, rejecting rolled-over
24
+ // dates: JS `Date` silently turns 2026-02-30 into Mar 2, so we round-trip the
25
+ // components and refuse any that do not survive intact.
26
+ function isoFromYmd(y, mo, d) {
27
+ if (mo < 1 || mo > 12 || d < 1 || d > 31)
28
+ return null;
29
+ const dt = new Date(Date.UTC(y, mo - 1, d));
30
+ if (dt.getUTCFullYear() !== y || dt.getUTCMonth() !== mo - 1 || dt.getUTCDate() !== d) {
31
+ return null;
32
+ }
33
+ return dt.toISOString();
34
+ }
35
+ // Parse an `--as-of` argument into a UTC ISO-8601 instant. Accepts:
36
+ // - `YYYY-MM-DD` and compact `YYYYMMDD` -> midnight UTC of that day
37
+ // - a full ISO-8601 datetime (e.g. 2026-04-10T12:30:00Z) -> normalized instant
38
+ // Throws on anything else.
39
+ function parseAsOf(raw) {
40
+ const s = (raw ?? "").trim();
41
+ if (!s) {
42
+ throw new Error("`--as-of` requires a date (YYYY-MM-DD or YYYYMMDD).");
43
+ }
44
+ const m = DASHED.exec(s) ?? COMPACT.exec(s);
45
+ if (m) {
46
+ const iso = isoFromYmd(Number(m[1]), Number(m[2]), Number(m[3]));
47
+ if (!iso) {
48
+ throw new Error(`Invalid --as-of date: '${raw}'. Use YYYY-MM-DD or YYYYMMDD.`);
49
+ }
50
+ return iso;
51
+ }
52
+ // Full ISO-8601 datetime: pin a precise instant. Date.parse is lenient, so we
53
+ // require the string to at least carry a 'T' before trusting it as a datetime,
54
+ // keeping bare junk ("not-a-date") on the reject path.
55
+ if (s.includes("T")) {
56
+ const t = Date.parse(s);
57
+ if (!Number.isNaN(t)) {
58
+ return new Date(t).toISOString();
59
+ }
60
+ }
61
+ throw new Error(`Invalid --as-of date: '${raw}'. Use YYYY-MM-DD or YYYYMMDD.`);
62
+ }
63
+ // The clock sources Meetless trusts as VALID-time authority. Mirrors intel's
64
+ // TRUSTED_ASOF_SOURCES (app/graphs/ask/retrieval/asof.py): an explicit author
65
+ // stamp, a document date, a diff audit-log entry, or a source-event time.
66
+ // INGESTION_TIME_PROXY and UNKNOWN are observed-only, never validated.
67
+ const TRUSTED_TIME_SOURCES = new Set([
68
+ "EXPLICIT_METADATA",
69
+ "DOCUMENT_DATE",
70
+ "DIFF_AUDIT_LOG",
71
+ "SOURCE_EVENT_TIME",
72
+ ]);
73
+ // Plain-words trust label for a relation's valid_time_source. Trusted sources
74
+ // read as "trusted"; anything observed-only reads as "approximate (observed,
75
+ // not validated)" so an operator never mistakes an ingestion guess for a
76
+ // validated clock. Two axes, two answers (B17): this is the trust of the
77
+ // VALID-time clock, separate from whether the edge survives an as-of filter.
78
+ function renderTrustLabel(source) {
79
+ if (source && TRUSTED_TIME_SOURCES.has(source)) {
80
+ return "trusted";
81
+ }
82
+ return "approximate (observed, not validated)";
83
+ }
84
+ // Reduce a stored ISO instant to its calendar date for a compact window line.
85
+ // Falls back to the raw value if it is not an ISO date so we never drop signal.
86
+ function calendarDate(iso) {
87
+ if (!iso)
88
+ return null;
89
+ const m = /^(\d{4}-\d{2}-\d{2})/.exec(iso);
90
+ return m ? m[1] : iso;
91
+ }
92
+ // Render a bi-temporal VALID-time window in plain words. No double-dash range
93
+ // separators (An's AI-smell rule); the open end is spelled out so an operator
94
+ // reads "still in force" rather than guessing at a blank. Cases:
95
+ // both bounds -> "valid from <from> until <until>"
96
+ // open-ended -> "valid from <from> (open-ended)"
97
+ // close-only -> "valid until <until>" (rare; close recorded, open missing)
98
+ // neither -> "validity window not recorded"
99
+ function renderValidWindow(window) {
100
+ const from = calendarDate(window.validAt);
101
+ const until = calendarDate(window.invalidAt);
102
+ if (from && until)
103
+ return `valid from ${from} until ${until}`;
104
+ if (from)
105
+ return `valid from ${from} (open-ended)`;
106
+ if (until)
107
+ return `valid until ${until}`;
108
+ return "validity window not recorded";
109
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ // Layer D emitter (notes/20260609-mla-per-turn-assist-recap-plan.md §4.4).
3
+ //
4
+ // POST a just-finished turn's recap to intel's POST /v1/observability/turn-recap
5
+ // so intel attaches the mla_ran / mla_assist Langfuse scores to that turn's
6
+ // trace. Spawned detached from stop.sh via `mla _internal turn-recap
7
+ // --emit-langfuse`; runInternalTurnRecap wires this as the default emitter.
8
+ //
9
+ // Why not the generic intelPost helper: intelPost stamps X-Trace-ID from the
10
+ // CURRENT run's trace id. Here the trace we score is the JUST-FINISHED turn's
11
+ // (recap.trace_id), which differs from this detached invocation's run id. We pin
12
+ // X-Trace-ID to recap.trace_id so header and body name the same trace and the
13
+ // endpoint's mismatch guard stays satisfied.
14
+ //
15
+ // Best-effort: a missing trace id is a silent no-op (nothing to score); any
16
+ // transport / non-2xx failure throws so the caller can swallow it without ever
17
+ // disturbing the agent. Langfuse keys live in intel, never here, so the
18
+ // soon-to-be-OSS CLI ships no observability credentials.
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.postTurnRecapToIntel = postTurnRecapToIntel;
21
+ const http_1 = require("./http");
22
+ const turn_recap_1 = require("./analytics/turn-recap");
23
+ // Detached + best-effort, so a tight deadline: the turn already ended, this only
24
+ // decorates its trace. 1.5s matches the trace-flush HTTP deadline.
25
+ const EMIT_TIMEOUT_MS = 1500;
26
+ async function postTurnRecapToIntel(cfg, recap, deps = {}) {
27
+ // No trace id -> no Langfuse trace to attach a score to. Silent no-op.
28
+ if (!recap.trace_id)
29
+ return;
30
+ const base = cfg.intelUrl || http_1.DEFAULT_INTEL_URL;
31
+ const url = `${base}/v1/observability/turn-recap`;
32
+ const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
33
+ const timeoutMs = deps.timeoutMs ?? EMIT_TIMEOUT_MS;
34
+ const body = {
35
+ traceId: recap.trace_id,
36
+ sessionId: recap.session_id,
37
+ turnIndex: recap.turn_index,
38
+ verdict: recap.verdict,
39
+ // One-line footer rides as the score comment.
40
+ footer: (0, turn_recap_1.renderFooter)(recap),
41
+ notRunReason: recap.not_run_reason,
42
+ // Full recap -> trace metadata for drilldown.
43
+ recap,
44
+ };
45
+ const controller = new AbortController();
46
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
47
+ try {
48
+ const res = await fetchImpl(url, {
49
+ method: "POST",
50
+ headers: {
51
+ Authorization: `Bearer ${cfg.controlToken}`,
52
+ "Content-Type": "application/json",
53
+ // Pinned to the just-finished turn's trace, not this run's.
54
+ "X-Trace-ID": recap.trace_id,
55
+ },
56
+ body: JSON.stringify(body),
57
+ signal: controller.signal,
58
+ });
59
+ if (!res.ok) {
60
+ const text = await res.text().catch(() => "");
61
+ throw new Error(`POST ${url} -> HTTP ${res.status}: ${text.slice(0, 200)}`);
62
+ }
63
+ }
64
+ finally {
65
+ clearTimeout(timer);
66
+ }
67
+ }
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.backupFile = backupFile;
37
+ exports.removeDir = removeDir;
38
+ exports.countQueuedSessions = countQueuedSessions;
39
+ exports.countQueuedEvents = countQueuedEvents;
40
+ exports.resolveMlaBinary = resolveMlaBinary;
41
+ exports.removeMeetlessHooks = removeMeetlessHooks;
42
+ exports.removeMeetlessMcp = removeMeetlessMcp;
43
+ exports.detectBinaryRemovalHint = detectBinaryRemovalHint;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const config_1 = require("./config");
47
+ const wire_1 = require("./wire");
48
+ // Inverse primitives of lib/wire.ts. Pure-ish and best-effort: a step that
49
+ // cannot complete returns a structured result rather than throwing, so the
50
+ // orchestrator can report every outcome and never abort halfway.
51
+ // Copy `p` to `p.bak.<now>` and return the backup path. Caller writes the new
52
+ // content only after this returns, so a botched edit is always recoverable.
53
+ function backupFile(p) {
54
+ const backupPath = `${p}.bak.${Date.now()}`;
55
+ fs.copyFileSync(p, backupPath);
56
+ return backupPath;
57
+ }
58
+ // rm -rf one directory. removed=false (no error) means "was not there"; an
59
+ // error string means it existed but could not be removed.
60
+ function removeDir(dir) {
61
+ try {
62
+ if (!fs.existsSync(dir))
63
+ return { removed: false };
64
+ fs.rmSync(dir, { recursive: true, force: true });
65
+ return { removed: true };
66
+ }
67
+ catch (e) {
68
+ return { removed: false, error: e.message };
69
+ }
70
+ }
71
+ // Number of capture sessions with a queue file: one `<sid>.jsonl` per session
72
+ // (`.lock` / `.turn` sidecars do not count). A flush drains and truncates the
73
+ // events it sent, so a counted file may hold only a small un-flushed tail (or
74
+ // none) rather than a whole un-flushed session. Missing dir -> 0.
75
+ function countQueuedSessions(queueDir) {
76
+ try {
77
+ return fs.readdirSync(queueDir).filter((f) => f.endsWith(".jsonl")).length;
78
+ }
79
+ catch {
80
+ return 0;
81
+ }
82
+ }
83
+ // Total un-flushed events still sitting in the queue: the sum of event lines
84
+ // across every `<sid>.jsonl` (one JSON object per line; blank lines and `.lock`
85
+ // / `.turn` sidecars do not count). This is the honest measure of what an
86
+ // uninstall discards, since most session files hold only a short tail of events
87
+ // and the file count alone overstates the magnitude. Missing dir -> 0.
88
+ function countQueuedEvents(queueDir) {
89
+ try {
90
+ let total = 0;
91
+ for (const f of fs.readdirSync(queueDir)) {
92
+ if (!f.endsWith(".jsonl"))
93
+ continue;
94
+ const content = fs.readFileSync(path.join(queueDir, f), "utf8");
95
+ total += content.split("\n").filter((line) => line.trim().length > 0).length;
96
+ }
97
+ return total;
98
+ }
99
+ catch {
100
+ return 0;
101
+ }
102
+ }
103
+ // Best-effort PATH search for the `mla` launcher. Returns the on-PATH path and
104
+ // its resolved realpath (the symlink target, when it is one). Pure over `env`
105
+ // so a test can hand it a synthetic PATH.
106
+ function resolveMlaBinary(env = process.env) {
107
+ const PATH = env.PATH || "";
108
+ for (const dir of PATH.split(path.delimiter)) {
109
+ if (!dir)
110
+ continue;
111
+ const candidate = path.join(dir, "mla");
112
+ try {
113
+ const st = fs.lstatSync(candidate);
114
+ if (st.isFile() || st.isSymbolicLink()) {
115
+ let realPath = null;
116
+ try {
117
+ realPath = fs.realpathSync(candidate);
118
+ }
119
+ catch {
120
+ realPath = null;
121
+ }
122
+ return { binPath: candidate, realPath };
123
+ }
124
+ }
125
+ catch {
126
+ // not in this dir
127
+ }
128
+ }
129
+ return { binPath: null, realPath: null };
130
+ }
131
+ // Inverse of ensureClaudeSettings. For each managed event, drop entries that are
132
+ // EXCLUSIVELY ours (exactly one hook whose command isManagedHookCommand for that
133
+ // event's script). Operator hooks, and multi-hook entries an operator merged our
134
+ // command into, are never touched (same conservatism install uses). Emptied
135
+ // event arrays are deleted; an emptied `hooks` object is dropped. Best-effort:
136
+ // a missing or unparseable file is a no-op, not an error.
137
+ function removeMeetlessHooks(settingsPath) {
138
+ const noop = { removed: [], changed: false, backupPath: null, settingsPath };
139
+ if (!fs.existsSync(settingsPath))
140
+ return noop;
141
+ const current = fs.readFileSync(settingsPath, "utf8");
142
+ let parsed;
143
+ try {
144
+ parsed = JSON.parse(current);
145
+ }
146
+ catch {
147
+ return noop;
148
+ }
149
+ if (!parsed || typeof parsed.hooks !== "object" || parsed.hooks === null)
150
+ return noop;
151
+ const removed = [];
152
+ for (const { event, script } of wire_1.MANAGED_HOOK_SCRIPTS) {
153
+ const list = parsed.hooks[event];
154
+ if (!Array.isArray(list))
155
+ continue;
156
+ const cmd = path.join(config_1.HOOKS_DIR, script);
157
+ const isOurs = (entry) => {
158
+ const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];
159
+ if (hooks.length !== 1)
160
+ return false;
161
+ const c = hooks[0];
162
+ return (c?.type === "command" &&
163
+ typeof c?.command === "string" &&
164
+ (0, wire_1.isManagedHookCommand)(c.command, script, cmd));
165
+ };
166
+ const kept = list.filter((e) => !isOurs(e));
167
+ if (kept.length !== list.length) {
168
+ removed.push(event);
169
+ if (kept.length === 0)
170
+ delete parsed.hooks[event];
171
+ else
172
+ parsed.hooks[event] = kept;
173
+ }
174
+ }
175
+ if (removed.length === 0)
176
+ return noop;
177
+ if (Object.keys(parsed.hooks).length === 0)
178
+ delete parsed.hooks;
179
+ const next = JSON.stringify(parsed, null, 2) + "\n";
180
+ const backupPath = backupFile(settingsPath);
181
+ fs.writeFileSync(settingsPath, next, "utf8");
182
+ return { removed, changed: true, backupPath, settingsPath };
183
+ }
184
+ // Delete the single `meetless` server from ~/.claude.json: the top-level
185
+ // mcpServers map plus every projects[*].mcpServers map. Other servers are kept;
186
+ // an emptied mcpServers object is dropped. Best-effort over a missing/unparseable
187
+ // file. The file is re-serialized with 2-space indent (it may have been
188
+ // minified); the timestamped backup preserves the byte-exact original.
189
+ function removeMeetlessMcp(claudeJsonPath) {
190
+ const noop = { removedFrom: [], changed: false, backupPath: null, claudeJsonPath };
191
+ if (!fs.existsSync(claudeJsonPath))
192
+ return noop;
193
+ const current = fs.readFileSync(claudeJsonPath, "utf8");
194
+ let parsed;
195
+ try {
196
+ parsed = JSON.parse(current);
197
+ }
198
+ catch {
199
+ return noop;
200
+ }
201
+ const removedFrom = [];
202
+ const dropFrom = (container, label) => {
203
+ const servers = container?.mcpServers;
204
+ if (servers && typeof servers === "object" && wire_1.MCP_SERVER_KEY in servers) {
205
+ delete servers[wire_1.MCP_SERVER_KEY];
206
+ removedFrom.push(label);
207
+ if (Object.keys(servers).length === 0)
208
+ delete container.mcpServers;
209
+ }
210
+ };
211
+ dropFrom(parsed, "(top level)");
212
+ if (parsed && typeof parsed.projects === "object" && parsed.projects !== null) {
213
+ for (const projPath of Object.keys(parsed.projects)) {
214
+ dropFrom(parsed.projects[projPath], `projects/${projPath}`);
215
+ }
216
+ }
217
+ if (removedFrom.length === 0)
218
+ return noop;
219
+ const next = JSON.stringify(parsed, null, 2) + "\n";
220
+ const backupPath = backupFile(claudeJsonPath);
221
+ fs.writeFileSync(claudeJsonPath, next, "utf8");
222
+ return { removedFrom, changed: true, backupPath, claudeJsonPath };
223
+ }
224
+ // Map (binPath, realPath) to the human removal instruction. Classifies by the
225
+ // realpath shape, no shelling out:
226
+ // - realpath under node_modules/@meetless/mla -> a package-manager global
227
+ // (pnpm if the path mentions pnpm, else npm)
228
+ // - otherwise a dev symlink into a source checkout -> rm the launcher, and
229
+ // name the repo root (the segment before /packages/cli/) so the operator
230
+ // can delete the clone too.
231
+ function detectBinaryRemovalHint(binPath, realPath) {
232
+ if (!binPath) {
233
+ return ["Could not find `mla` on your PATH. If a copy remains, remove it manually."];
234
+ }
235
+ const probe = realPath ?? "";
236
+ const pkgMarker = `${path.sep}node_modules${path.sep}@meetless${path.sep}mla`;
237
+ if (probe.includes(pkgMarker)) {
238
+ const isPnpm = /pnpm/.test(probe) || /pnpm/.test(binPath);
239
+ return [
240
+ "Remove the globally-installed package:",
241
+ isPnpm ? " pnpm rm -g @meetless/mla" : " npm uninstall -g @meetless/mla",
242
+ ];
243
+ }
244
+ const lines = ["Remove the `mla` launcher (a dev symlink):", ` rm ${binPath}`];
245
+ if (realPath) {
246
+ const marker = `${path.sep}packages${path.sep}cli${path.sep}`;
247
+ const idx = realPath.indexOf(marker);
248
+ if (idx > 0) {
249
+ lines.push(`If you no longer need the source, delete the checkout at ${realPath.slice(0, idx)}`);
250
+ }
251
+ }
252
+ return lines;
253
+ }