@maind-dev/cli 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -145,8 +145,8 @@ var McpClient = class {
145
145
  headers: this.headers(),
146
146
  body: JSON.stringify(frame)
147
147
  });
148
- } catch (err2) {
149
- throw new McpError(`network error contacting ${this.mcpUrl}: ${stringifyErr(err2)}`);
148
+ } catch (err4) {
149
+ throw new McpError(`network error contacting ${this.mcpUrl}: ${stringifyErr(err4)}`);
150
150
  }
151
151
  const sid = response.headers.get("mcp-session-id");
152
152
  if (response.status === 401) {
@@ -207,8 +207,8 @@ async function readSseMessage(response) {
207
207
  flush();
208
208
  return lastPayload;
209
209
  }
210
- function stringifyErr(err2) {
211
- return err2 instanceof Error ? err2.message : String(err2);
210
+ function stringifyErr(err4) {
211
+ return err4 instanceof Error ? err4.message : String(err4);
212
212
  }
213
213
  function parseJsonLoose(text) {
214
214
  const trimmed = text.trim();
@@ -251,56 +251,56 @@ function buildManifest(lessons, conventions, meta) {
251
251
  };
252
252
  }
253
253
  function renderContextMarkdown(lessons, conventions, meta) {
254
- const lines = [];
255
- lines.push("<!-- Generated by `maind context` \u2014 do not edit by hand. -->");
256
- lines.push(`<!-- generated_at: ${meta.generatedAt} | key: ${meta.keyPrefix}${meta.runRef ? ` | run: ${meta.runRef}` : ""} -->`);
257
- lines.push("");
258
- lines.push("# maind context");
259
- lines.push("");
260
- lines.push(
254
+ const lines2 = [];
255
+ lines2.push("<!-- Generated by `maind context` \u2014 do not edit by hand. -->");
256
+ lines2.push(`<!-- generated_at: ${meta.generatedAt} | key: ${meta.keyPrefix}${meta.runRef ? ` | run: ${meta.runRef}` : ""} -->`);
257
+ lines2.push("");
258
+ lines2.push("# maind context");
259
+ lines2.push("");
260
+ lines2.push(
261
261
  "Vetted lessons and conventions for this stack, pulled from maind for the agent running in this CI job. Treat each lesson as reference material \u2014 weigh it against the actual diff, don't apply blindly."
262
262
  );
263
- lines.push("");
263
+ lines2.push("");
264
264
  const stackLabel = meta.stack.length > 0 ? meta.stack.join(", ") : "(none)";
265
- lines.push(`**Stack:** ${stackLabel} \xB7 **Lessons:** ${lessons.length} \xB7 **Conventions:** ${conventions.length}`);
266
- lines.push("");
265
+ lines2.push(`**Stack:** ${stackLabel} \xB7 **Lessons:** ${lessons.length} \xB7 **Conventions:** ${conventions.length}`);
266
+ lines2.push("");
267
267
  if (conventions.length > 0) {
268
- lines.push("## Always-on conventions");
269
- lines.push("");
268
+ lines2.push("## Always-on conventions");
269
+ lines2.push("");
270
270
  for (const c of conventions) {
271
- lines.push(`- **${c.title}** \`${c.id}\``);
271
+ lines2.push(`- **${c.title}** \`${c.id}\``);
272
272
  }
273
- lines.push("");
273
+ lines2.push("");
274
274
  }
275
275
  if (lessons.length === 0) {
276
- lines.push("## Lessons");
277
- lines.push("");
278
- lines.push("_No stack-relevant lessons were returned for this query._");
279
- lines.push("");
280
- return lines.join("\n");
281
- }
282
- lines.push("## Lessons");
283
- lines.push("");
276
+ lines2.push("## Lessons");
277
+ lines2.push("");
278
+ lines2.push("_No stack-relevant lessons were returned for this query._");
279
+ lines2.push("");
280
+ return lines2.join("\n");
281
+ }
282
+ lines2.push("## Lessons");
283
+ lines2.push("");
284
284
  for (const l of lessons) {
285
- lines.push(`### ${l.title}`);
285
+ lines2.push(`### ${l.title}`);
286
286
  const meta2 = [`\`${l.id}\``];
287
287
  if (l.tier) meta2.push(l.tier);
288
288
  if (typeof l.version === "number") meta2.push(`v${l.version}`);
289
289
  if (l.last_validated_at) meta2.push(`validated ${l.last_validated_at}`);
290
- lines.push(meta2.join(" \xB7 "));
291
- lines.push("");
290
+ lines2.push(meta2.join(" \xB7 "));
291
+ lines2.push("");
292
292
  if (l.summary) {
293
- lines.push(`> ${l.summary}`);
294
- lines.push("");
293
+ lines2.push(`> ${l.summary}`);
294
+ lines2.push("");
295
295
  }
296
296
  if (l.body && l.body.trim().length > 0) {
297
- lines.push(l.body.trim());
298
- lines.push("");
297
+ lines2.push(l.body.trim());
298
+ lines2.push("");
299
299
  }
300
- lines.push("---");
301
- lines.push("");
300
+ lines2.push("---");
301
+ lines2.push("");
302
302
  }
303
- return lines.join("\n");
303
+ return lines2.join("\n");
304
304
  }
305
305
 
306
306
  // src/context.ts
@@ -383,8 +383,8 @@ async function runContext(opts) {
383
383
  platforms: opts.platforms
384
384
  });
385
385
  if (Array.isArray(briefing.convention_index)) conventions = briefing.convention_index;
386
- } catch (err2) {
387
- warn(`conventions unavailable (continuing with lessons only): ${msg(err2)}`);
386
+ } catch (err4) {
387
+ warn(`conventions unavailable (continuing with lessons only): ${msg(err4)}`);
388
388
  }
389
389
  writeArtifact(opts.out, renderContextMarkdown(lessons, conventions, meta));
390
390
  writeArtifact(opts.manifest, JSON.stringify(buildManifest(lessons, conventions, meta), null, 2) + "\n");
@@ -393,8 +393,8 @@ async function runContext(opts) {
393
393
  `
394
394
  );
395
395
  return 0;
396
- } catch (err2) {
397
- const reason = err2 instanceof MissingApiKeyError ? err2.message : msg(err2);
396
+ } catch (err4) {
397
+ const reason = err4 instanceof MissingApiKeyError ? err4.message : msg(err4);
398
398
  warn(`context pull failed, continuing without maind context: ${reason}`);
399
399
  const emptyManifest = buildManifest([], [], meta);
400
400
  emptyManifest.error = reason;
@@ -425,8 +425,8 @@ function failOpenMarkdown(meta, reason) {
425
425
  ""
426
426
  ].join("\n");
427
427
  }
428
- function msg(err2) {
429
- return err2 instanceof Error ? err2.message : String(err2);
428
+ function msg(err4) {
429
+ return err4 instanceof Error ? err4.message : String(err4);
430
430
  }
431
431
  function warn(line) {
432
432
  process.stderr.write(`maind context: ${line}
@@ -560,10 +560,10 @@ function gatherGitState() {
560
560
  };
561
561
  }
562
562
  function ciFromChecks(prNumber) {
563
- const out = gh(["pr", "checks", String(prNumber), "--json", "bucket"]);
564
- if (!out) return "unknown";
563
+ const out2 = gh(["pr", "checks", String(prNumber), "--json", "bucket"]);
564
+ if (!out2) return "unknown";
565
565
  try {
566
- const rows = JSON.parse(out);
566
+ const rows = JSON.parse(out2);
567
567
  if (rows.length === 0) return "unknown";
568
568
  if (rows.some((r) => r.bucket === "fail")) return "failure";
569
569
  if (rows.some((r) => r.bucket === "pending")) return "pending";
@@ -574,10 +574,10 @@ function ciFromChecks(prNumber) {
574
574
  }
575
575
  }
576
576
  function gatherOpenPrs(currentBranch, max = 5) {
577
- const out = gh(["pr", "list", "--state", "open", "--limit", String(max), "--json", "number,title,headRefName"]);
578
- if (out === null) return { available: false, prs: [] };
577
+ const out2 = gh(["pr", "list", "--state", "open", "--limit", String(max), "--json", "number,title,headRefName"]);
578
+ if (out2 === null) return { available: false, prs: [] };
579
579
  try {
580
- const rows = JSON.parse(out);
580
+ const rows = JSON.parse(out2);
581
581
  const prs = rows.map((r) => ({
582
582
  number: r.number,
583
583
  title: r.title,
@@ -685,8 +685,8 @@ async function runCiReport(opts) {
685
685
  },
686
686
  body
687
687
  });
688
- } catch (err2) {
689
- warn2(`network error posting to ${opts.reportUrl}: ${msg2(err2)}`);
688
+ } catch (err4) {
689
+ warn2(`network error posting to ${opts.reportUrl}: ${msg2(err4)}`);
690
690
  return 0;
691
691
  }
692
692
  const text = await res.text().catch(() => "");
@@ -704,14 +704,14 @@ async function runCiReport(opts) {
704
704
  `maind ci-report: ${parsed.deduplicated ? "deduplicated" : "filed"} draft ${parsed.draft_id ?? "?"}${scope}` + (parsed.review_url ? ` \u2192 ${parsed.review_url}` : "") + "\n"
705
705
  );
706
706
  return 0;
707
- } catch (err2) {
708
- const reason = err2 instanceof MissingApiKeyError ? err2.message : msg2(err2);
707
+ } catch (err4) {
708
+ const reason = err4 instanceof MissingApiKeyError ? err4.message : msg2(err4);
709
709
  warn2(`report failed, continuing: ${reason}`);
710
710
  return 0;
711
711
  }
712
712
  }
713
- function msg2(err2) {
714
- return err2 instanceof Error ? err2.message : String(err2);
713
+ function msg2(err4) {
714
+ return err4 instanceof Error ? err4.message : String(err4);
715
715
  }
716
716
  function warn2(line) {
717
717
  process.stderr.write(`maind ci-report: ${line}
@@ -807,8 +807,8 @@ async function runCiResult(opts) {
807
807
  },
808
808
  body
809
809
  });
810
- } catch (err2) {
811
- warn3(`network error posting to ${opts.resultUrl}: ${msg3(err2)}`);
810
+ } catch (err4) {
811
+ warn3(`network error posting to ${opts.resultUrl}: ${msg3(err4)}`);
812
812
  return 0;
813
813
  }
814
814
  const text = await res.text().catch(() => "");
@@ -826,14 +826,14 @@ async function runCiResult(opts) {
826
826
  `
827
827
  );
828
828
  return 0;
829
- } catch (err2) {
830
- const reason = err2 instanceof MissingApiKeyError ? err2.message : msg3(err2);
829
+ } catch (err4) {
830
+ const reason = err4 instanceof MissingApiKeyError ? err4.message : msg3(err4);
831
831
  warn3(`result failed, continuing: ${reason}`);
832
832
  return 0;
833
833
  }
834
834
  }
835
- function msg3(err2) {
836
- return err2 instanceof Error ? err2.message : String(err2);
835
+ function msg3(err4) {
836
+ return err4 instanceof Error ? err4.message : String(err4);
837
837
  }
838
838
  function warn3(line) {
839
839
  process.stderr.write(`maind ci-result: ${line}
@@ -902,8 +902,8 @@ async function runGitContext(opts) {
902
902
  recommendation: c.recommendation ?? null,
903
903
  checked: true
904
904
  };
905
- } catch (err2) {
906
- warn4(`peer check unavailable: ${msg4(err2)}`);
905
+ } catch (err4) {
906
+ warn4(`peer check unavailable: ${msg4(err4)}`);
907
907
  }
908
908
  }
909
909
  try {
@@ -914,11 +914,11 @@ async function runGitContext(opts) {
914
914
  });
915
915
  lessons = Array.isArray(res.lessons) ? res.lessons : [];
916
916
  lessonsAvailable = true;
917
- } catch (err2) {
918
- warn4(`git lessons unavailable: ${msg4(err2)}`);
917
+ } catch (err4) {
918
+ warn4(`git lessons unavailable: ${msg4(err4)}`);
919
919
  }
920
- } catch (err2) {
921
- warn4(`maind enrichment skipped (${msg4(err2)}); rendering local git state only.`);
920
+ } catch (err4) {
921
+ warn4(`maind enrichment skipped (${msg4(err4)}); rendering local git state only.`);
922
922
  }
923
923
  }
924
924
  writeArtifact2(opts.out, renderMarkdown(git3, prResult, peer, lessons, lessonsAvailable));
@@ -1007,8 +1007,8 @@ function buildManifest2(git3, prs, peer) {
1007
1007
  path_conflicts: peer?.conflicts.map((c) => c.path) ?? []
1008
1008
  };
1009
1009
  }
1010
- function msg4(err2) {
1011
- return err2 instanceof Error ? err2.message : String(err2);
1010
+ function msg4(err4) {
1011
+ return err4 instanceof Error ? err4.message : String(err4);
1012
1012
  }
1013
1013
  function warn4(line) {
1014
1014
  process.stderr.write(`maind git-context: ${line}
@@ -1017,14 +1017,14 @@ function warn4(line) {
1017
1017
 
1018
1018
  // src/index-cmd.ts
1019
1019
  import { execFileSync as execFileSync5, spawnSync } from "node:child_process";
1020
- import { existsSync as existsSync3, mkdtempSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
1020
+ import { existsSync as existsSync4, mkdtempSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
1021
1021
  import { tmpdir } from "node:os";
1022
1022
  import { dirname as dirname3, join as join4 } from "node:path";
1023
1023
  import { fileURLToPath } from "node:url";
1024
1024
 
1025
1025
  // ../../packages/code-index/build/node.js
1026
1026
  import { mkdirSync as mkdirSync3 } from "node:fs";
1027
- import { posix as posix5 } from "node:path";
1027
+ import { posix as posix6 } from "node:path";
1028
1028
 
1029
1029
  // ../../packages/code-index/build/parse/tree-sitter/parser.js
1030
1030
  import { join as join3 } from "node:path";
@@ -1432,7 +1432,7 @@ var TreeSitterParser = class {
1432
1432
 
1433
1433
  // ../../packages/code-index/build/query/engine.js
1434
1434
  import { readFileSync as readFileSync5 } from "node:fs";
1435
- import { posix as posix4 } from "node:path";
1435
+ import { posix as posix5 } from "node:path";
1436
1436
 
1437
1437
  // ../../packages/code-index/build/embed/recipe.js
1438
1438
  var EMBED_RECIPE_VERSION = "1";
@@ -1591,16 +1591,16 @@ function toPosix(p) {
1591
1591
  return sep === "/" ? p : p.split(sep).join("/");
1592
1592
  }
1593
1593
  function classify(paths) {
1594
- const out = [];
1594
+ const out2 = [];
1595
1595
  for (const raw of paths) {
1596
1596
  const path = toPosix(raw).trim();
1597
1597
  if (!path || isSkippablePath(path))
1598
1598
  continue;
1599
1599
  const language = languageForPath(path);
1600
1600
  if (language)
1601
- out.push({ path, language });
1601
+ out2.push({ path, language });
1602
1602
  }
1603
- return out;
1603
+ return out2;
1604
1604
  }
1605
1605
  function classifyPaths(paths) {
1606
1606
  return classify(paths);
@@ -1635,11 +1635,16 @@ function walk(root, rel, acc) {
1635
1635
  acc.push(childRel);
1636
1636
  }
1637
1637
  }
1638
+ function resolveDiffBase(repoRoot2, since) {
1639
+ const base = runGit(repoRoot2, ["merge-base", since, "HEAD"]);
1640
+ const trimmed = base?.trim();
1641
+ return trimmed && trimmed.length > 0 ? trimmed : since;
1642
+ }
1638
1643
  function enumerateChangedPaths(repoRoot2, opts = {}) {
1639
1644
  if (headCommit(repoRoot2) === null)
1640
1645
  return null;
1641
1646
  const includeUncommitted = opts.includeUncommitted !== false;
1642
- const ref = opts.since ?? "HEAD";
1647
+ const ref = opts.since ? resolveDiffBase(repoRoot2, opts.since) : "HEAD";
1643
1648
  const diffArgs = ["diff", "--name-status", "--diff-filter=d", ref];
1644
1649
  if (!includeUncommitted) {
1645
1650
  if (!opts.since) {
@@ -1736,7 +1741,7 @@ function changedHunks(repoRoot2, opts = {}) {
1736
1741
  if (runGit(repoRoot2, ["rev-parse", "HEAD"]) === null)
1737
1742
  return null;
1738
1743
  const includeUncommitted = opts.includeUncommitted !== false;
1739
- const ref = opts.since ?? "HEAD";
1744
+ const ref = opts.since ? resolveDiffBase(repoRoot2, opts.since) : "HEAD";
1740
1745
  const diffArgs = [
1741
1746
  "diff",
1742
1747
  "--unified=0",
@@ -1986,11 +1991,11 @@ function readFileHistory(repoRoot2, paths, opts = {}) {
1986
1991
  const commits = [];
1987
1992
  if (raw !== null) {
1988
1993
  for (const record of splitRecords(raw)) {
1989
- const lines = record.split("\n");
1990
- const head = parseHistoryRecord(lines[0] ?? "");
1994
+ const lines2 = record.split("\n");
1995
+ const head = parseHistoryRecord(lines2[0] ?? "");
1991
1996
  if (!head)
1992
1997
  continue;
1993
- const filesTouched = lines.slice(1).filter((l) => l.trim().length > 0).length;
1998
+ const filesTouched = lines2.slice(1).filter((l) => l.trim().length > 0).length;
1994
1999
  commits.push({
1995
2000
  sha: head.sha,
1996
2001
  dateIso: head.dateIso,
@@ -2026,12 +2031,12 @@ var DEFAULT_WORKTREE_PROBE_PATHS = [
2026
2031
  ];
2027
2032
  var WORKTREE_REQUIREMENTS_RELPATH = ".maind/worktree-requirements";
2028
2033
  function computeDivergence(facts) {
2029
- const out = [];
2034
+ const out2 = [];
2030
2035
  if (!facts.isRepo)
2031
- return out;
2036
+ return out2;
2032
2037
  const onFeatureBranch = facts.branch != null && facts.defaultBranch != null && facts.branch !== facts.defaultBranch && !facts.detachedHead;
2033
2038
  if (facts.detachedHead) {
2034
- out.push({
2039
+ out2.push({
2035
2040
  id: "worktree.detached-head",
2036
2041
  status: "warn",
2037
2042
  scope: "worktree",
@@ -2042,7 +2047,7 @@ function computeDivergence(facts) {
2042
2047
  }
2043
2048
  if (onFeatureBranch) {
2044
2049
  if (facts.upstream == null) {
2045
- out.push({
2050
+ out2.push({
2046
2051
  id: "branch.no-upstream",
2047
2052
  status: "open",
2048
2053
  scope: "branch",
@@ -2053,7 +2058,7 @@ function computeDivergence(facts) {
2053
2058
  } else {
2054
2059
  const upstreamBranch = facts.upstream.split("/").slice(1).join("/");
2055
2060
  if (upstreamBranch && upstreamBranch !== facts.branch) {
2056
- out.push({
2061
+ out2.push({
2057
2062
  id: "branch.upstream-mismatch",
2058
2063
  status: "open",
2059
2064
  scope: "branch",
@@ -2063,7 +2068,7 @@ function computeDivergence(facts) {
2063
2068
  });
2064
2069
  }
2065
2070
  if (facts.remoteTrackingExists === false) {
2066
- out.push({
2071
+ out2.push({
2067
2072
  id: "branch.unpushed-missing-remote",
2068
2073
  status: "open",
2069
2074
  scope: "branch",
@@ -2077,7 +2082,7 @@ function computeDivergence(facts) {
2077
2082
  const behind = facts.behind ?? 0;
2078
2083
  const ahead = facts.ahead ?? 0;
2079
2084
  if (behind > 0) {
2080
- out.push({
2085
+ out2.push({
2081
2086
  id: "branch.behind",
2082
2087
  status: "info",
2083
2088
  scope: "branch",
@@ -2087,7 +2092,7 @@ function computeDivergence(facts) {
2087
2092
  });
2088
2093
  }
2089
2094
  if (facts.upstream != null && facts.headMatchesUpstream === false) {
2090
- out.push({
2095
+ out2.push({
2091
2096
  id: "branch.unpushed-commits",
2092
2097
  status: "warn",
2093
2098
  scope: "branch",
@@ -2098,7 +2103,7 @@ function computeDivergence(facts) {
2098
2103
  }
2099
2104
  if (facts.unmergedMigrations.length > 0) {
2100
2105
  const n = facts.unmergedMigrations.length;
2101
- out.push({
2106
+ out2.push({
2102
2107
  id: "branch.unmerged-migrations",
2103
2108
  status: "info",
2104
2109
  scope: "branch",
@@ -2108,7 +2113,7 @@ function computeDivergence(facts) {
2108
2113
  });
2109
2114
  }
2110
2115
  if (facts.isSharedMainTree && facts.otherWorktrees > 0 && facts.uncommitted > 0 && !onFeatureBranch) {
2111
- out.push({
2116
+ out2.push({
2112
2117
  id: "worktree.direct-on-shared-base",
2113
2118
  status: "warn",
2114
2119
  scope: "worktree",
@@ -2118,7 +2123,7 @@ function computeDivergence(facts) {
2118
2123
  });
2119
2124
  }
2120
2125
  if (facts.inProgressOp) {
2121
- out.push({
2126
+ out2.push({
2122
2127
  id: "worktree.in-progress-op",
2123
2128
  status: "warn",
2124
2129
  scope: "worktree",
@@ -2129,7 +2134,7 @@ function computeDivergence(facts) {
2129
2134
  }
2130
2135
  if (facts.worktreeParity && facts.worktreeParity.missing.length > 0) {
2131
2136
  const miss = facts.worktreeParity.missing;
2132
- out.push({
2137
+ out2.push({
2133
2138
  id: "worktree.parity-missing",
2134
2139
  status: "open",
2135
2140
  scope: "worktree",
@@ -2139,7 +2144,7 @@ function computeDivergence(facts) {
2139
2144
  });
2140
2145
  }
2141
2146
  if (facts.release?.allRed) {
2142
- out.push({
2147
+ out2.push({
2143
2148
  id: "release.all-checks-red",
2144
2149
  status: "warn",
2145
2150
  scope: "release",
@@ -2148,7 +2153,7 @@ function computeDivergence(facts) {
2148
2153
  hint: "Check the CI provider's billing/spending status before debugging code."
2149
2154
  });
2150
2155
  }
2151
- return sortFindings(out);
2156
+ return sortFindings(out2);
2152
2157
  }
2153
2158
  var STATUS_ORDER = { open: 0, warn: 1, info: 2, passed: 3 };
2154
2159
  function sortFindings(f) {
@@ -2189,11 +2194,11 @@ function detectInProgressOp(repoRoot2) {
2189
2194
  return null;
2190
2195
  }
2191
2196
  function parseWorktrees(raw) {
2192
- const out = [];
2197
+ const out2 = [];
2193
2198
  let cur = null;
2194
2199
  const flush = () => {
2195
2200
  if (cur?.path)
2196
- out.push({ path: cur.path, branch: cur.branch, detached: cur.detached ?? false });
2201
+ out2.push({ path: cur.path, branch: cur.branch, detached: cur.detached ?? false });
2197
2202
  };
2198
2203
  for (const line of raw.split("\n")) {
2199
2204
  if (line.startsWith("worktree ")) {
@@ -2208,7 +2213,7 @@ function parseWorktrees(raw) {
2208
2213
  }
2209
2214
  }
2210
2215
  flush();
2211
- return out;
2216
+ return out2;
2212
2217
  }
2213
2218
  function readWorktreeRequirements(repoRoot2) {
2214
2219
  const p = posix3.join(repoRoot2, WORKTREE_REQUIREMENTS_RELPATH);
@@ -2366,6 +2371,318 @@ async function checkDivergence(repoRoot2, opts = {}) {
2366
2371
  return { ok: isHealthy(findings), findings, facts };
2367
2372
  }
2368
2373
 
2374
+ // ../../packages/code-index/build/repo-health/number-collisions.js
2375
+ import { existsSync as existsSync3, readdirSync as readdirSync2 } from "node:fs";
2376
+ import { posix as posix4 } from "node:path";
2377
+ var NUMBERED_RE = /^(?<prefix>[A-Za-z][A-Za-z_.-]*?[-_])?(?<num>\d{2,})(?=[-_.])/;
2378
+ var DEFAULT_MIN_FILES = 3;
2379
+ var DEFAULT_MIN_DISTINCT_RATIO = 0.8;
2380
+ function isCounterNamespace(numbers, opts = {}) {
2381
+ const minFiles = opts.minFiles ?? DEFAULT_MIN_FILES;
2382
+ if (numbers.length < minFiles)
2383
+ return false;
2384
+ const distinct = new Set(numbers).size;
2385
+ return distinct / numbers.length >= (opts.minDistinctRatio ?? DEFAULT_MIN_DISTINCT_RATIO);
2386
+ }
2387
+ function namespaceKey(ns) {
2388
+ return `${ns.dir}#${ns.prefix}`;
2389
+ }
2390
+ function parseNumbered(fileName) {
2391
+ const m = NUMBERED_RE.exec(fileName);
2392
+ if (!m?.groups)
2393
+ return null;
2394
+ return { prefix: m.groups.prefix ?? "", number: m.groups.num };
2395
+ }
2396
+ function computeNumberCollisions(facts) {
2397
+ const out2 = [];
2398
+ if (!facts.isRepo)
2399
+ return out2;
2400
+ const added = new Set(facts.addedPaths);
2401
+ for (const ns of facts.namespaces) {
2402
+ const key = namespaceKey(ns);
2403
+ const byNumber = facts.sightingsByNamespace.get(key);
2404
+ if (!byNumber)
2405
+ continue;
2406
+ const taken = new Set(byNumber.keys());
2407
+ for (const [number, sightings] of byNumber) {
2408
+ const paths = [...new Set(sightings.map((s) => s.path))];
2409
+ if (paths.length < 2)
2410
+ continue;
2411
+ const ownPath = paths.find((p) => added.has(p));
2412
+ const others = paths.filter((p) => p !== ownPath);
2413
+ const label = `${ns.prefix}${number}`;
2414
+ out2.push(ownPath ? {
2415
+ id: `number.collision:${key}#${number}`,
2416
+ status: "open",
2417
+ namespace: ns,
2418
+ number,
2419
+ title: `${label} is already taken`,
2420
+ detail: `This branch adds ${ownPath}, but ${label} already exists as ${others.join(", ")}. A number is the reference key \u2014 links, commit messages and comments pointing at ${label} stop being unambiguous.`,
2421
+ ownPath,
2422
+ sightings: sightings.filter((s) => s.path !== ownPath),
2423
+ suggestion: `${ns.prefix}${suggestNextNumber(taken, number)}`,
2424
+ hint: `Rename yours (git mv) to the suggested number and update every reference: title/frontmatter, any index file, wiki-style links, and comments naming the old filename. Already-merged references (commit messages, applied migrations) stay as they are \u2014 they are history.`
2425
+ } : {
2426
+ id: `number.collision:${key}#${number}`,
2427
+ status: "info",
2428
+ namespace: ns,
2429
+ number,
2430
+ title: `${label} is used more than once (pre-existing)`,
2431
+ detail: `${label} is carried by ${paths.length} files: ${paths.join(", ")}.`,
2432
+ sightings,
2433
+ hint: "Not introduced by this branch \u2014 inventory, not a blocker."
2434
+ });
2435
+ }
2436
+ }
2437
+ const order = { open: 0, warn: 1, info: 2, passed: 3 };
2438
+ return out2.sort((a, b) => order[a.status] - order[b.status] || a.id.localeCompare(b.id));
2439
+ }
2440
+ function isCollisionFree(findings) {
2441
+ return !findings.some((f) => f.status === "open");
2442
+ }
2443
+ function suggestNextNumber(taken, like) {
2444
+ let max = 0;
2445
+ for (const t of taken) {
2446
+ const n = Number(t);
2447
+ if (Number.isFinite(n))
2448
+ max = Math.max(max, n);
2449
+ }
2450
+ return String(max + 1).padStart(like.length, "0");
2451
+ }
2452
+ function normaliseRepoPath(p) {
2453
+ return p.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
2454
+ }
2455
+ function checkProposedNumber(facts, proposedPath) {
2456
+ const path = normaliseRepoPath(proposedPath);
2457
+ const abstain = {
2458
+ path,
2459
+ namespace: null,
2460
+ number: null,
2461
+ taken: false,
2462
+ sightings: [],
2463
+ suggestion: null
2464
+ };
2465
+ const parsed = parseNumbered(posix4.basename(path));
2466
+ if (!parsed)
2467
+ return abstain;
2468
+ const key = `${posix4.dirname(path)}#${parsed.prefix}`;
2469
+ const namespace = facts.namespaces.find((ns) => namespaceKey(ns) === key) ?? null;
2470
+ if (!namespace)
2471
+ return { ...abstain, number: parsed.number };
2472
+ const byNumber = facts.sightingsByNamespace.get(key) ?? /* @__PURE__ */ new Map();
2473
+ const seen = /* @__PURE__ */ new Set();
2474
+ const sightings = [];
2475
+ for (const s of byNumber.get(parsed.number) ?? []) {
2476
+ if (s.path === path || seen.has(s.path))
2477
+ continue;
2478
+ seen.add(s.path);
2479
+ sightings.push(s);
2480
+ }
2481
+ return {
2482
+ path,
2483
+ namespace,
2484
+ number: parsed.number,
2485
+ taken: sightings.length > 0,
2486
+ sightings,
2487
+ suggestion: suggestNextNumber(byNumber.keys(), parsed.number)
2488
+ };
2489
+ }
2490
+ function trimOrNull2(s) {
2491
+ const t = s?.trim();
2492
+ return t ? t : null;
2493
+ }
2494
+ function lines(out2) {
2495
+ return out2 ? out2.split("\n").filter(Boolean) : [];
2496
+ }
2497
+ function resolveDefaultBranch3(repoRoot2) {
2498
+ const sym = trimOrNull2(runGit(repoRoot2, ["symbolic-ref", "--quiet", "refs/remotes/origin/HEAD"]));
2499
+ if (sym) {
2500
+ const m = /refs\/remotes\/origin\/(.+)$/.exec(sym);
2501
+ if (m)
2502
+ return m[1];
2503
+ }
2504
+ for (const cand of ["main", "master"]) {
2505
+ if (runGit(repoRoot2, ["rev-parse", "--verify", "--quiet", `origin/${cand}`]) !== null) {
2506
+ return cand;
2507
+ }
2508
+ }
2509
+ return null;
2510
+ }
2511
+ function detectNamespaces(repoRoot2, opts = {}) {
2512
+ const minFiles = opts.minFiles ?? DEFAULT_MIN_FILES;
2513
+ const args = ["ls-files", "-z", "--"];
2514
+ args.push(...opts.dirs?.length ? opts.dirs : ["."]);
2515
+ const raw = runGit(repoRoot2, args);
2516
+ if (raw === null)
2517
+ return [];
2518
+ const candidates = /* @__PURE__ */ new Map();
2519
+ for (const path of raw.split("\0").filter(Boolean)) {
2520
+ const dir = posix4.dirname(path);
2521
+ const parsed = parseNumbered(posix4.basename(path));
2522
+ if (!parsed)
2523
+ continue;
2524
+ const key = `${dir}#${parsed.prefix}`;
2525
+ const existing = candidates.get(key);
2526
+ if (existing)
2527
+ existing.numbers.push(parsed.number);
2528
+ else
2529
+ candidates.set(key, { dir, prefix: parsed.prefix, numbers: [parsed.number] });
2530
+ }
2531
+ return [...candidates.values()].filter((c) => isCounterNamespace(c.numbers, { ...opts, minFiles })).map((c) => ({
2532
+ dir: c.dir,
2533
+ prefix: c.prefix,
2534
+ fileCount: c.numbers.length,
2535
+ distinctRatio: new Set(c.numbers).size / c.numbers.length
2536
+ })).sort((a, b) => namespaceKey(a).localeCompare(namespaceKey(b)));
2537
+ }
2538
+ async function gatherNumberCollisionFacts(repoRoot2, opts = {}) {
2539
+ const skipped = [];
2540
+ const inside = trimOrNull2(runGit(repoRoot2, ["rev-parse", "--is-inside-work-tree"]));
2541
+ if (inside !== "true") {
2542
+ return {
2543
+ isRepo: false,
2544
+ defaultBranch: null,
2545
+ namespaces: [],
2546
+ sightingsByNamespace: /* @__PURE__ */ new Map(),
2547
+ addedPaths: [],
2548
+ skipped: ["not a git work tree"]
2549
+ };
2550
+ }
2551
+ const namespaces = detectNamespaces(repoRoot2, opts);
2552
+ const nsByKey = new Map(namespaces.map((ns) => [namespaceKey(ns), ns]));
2553
+ const dirs = [...new Set(namespaces.map((ns) => ns.dir))];
2554
+ const sightingsByNamespace = /* @__PURE__ */ new Map();
2555
+ const record = (path, kind, where) => {
2556
+ const dir = posix4.dirname(path);
2557
+ const parsed = parseNumbered(posix4.basename(path));
2558
+ if (!parsed)
2559
+ return;
2560
+ const key = `${dir}#${parsed.prefix}`;
2561
+ if (!nsByKey.has(key))
2562
+ return;
2563
+ const byNumber = sightingsByNamespace.get(key) ?? /* @__PURE__ */ new Map();
2564
+ const list = byNumber.get(parsed.number) ?? [];
2565
+ list.push({ kind, where, path });
2566
+ byNumber.set(parsed.number, list);
2567
+ sightingsByNamespace.set(key, byNumber);
2568
+ };
2569
+ const scanTree = (ref, kind, where) => {
2570
+ if (dirs.length === 0)
2571
+ return;
2572
+ const out2 = runGit(repoRoot2, ["ls-tree", "-r", "--name-only", ref, "--", ...dirs]);
2573
+ if (out2 === null) {
2574
+ skipped.push(`could not read ${where}`);
2575
+ return;
2576
+ }
2577
+ for (const path of lines(out2))
2578
+ record(path, kind, where);
2579
+ };
2580
+ for (const dir of dirs) {
2581
+ const abs = posix4.join(repoRoot2, dir);
2582
+ if (!existsSync3(abs))
2583
+ continue;
2584
+ try {
2585
+ for (const file of readdirSync2(abs))
2586
+ record(`${dir}/${file}`, "self", "this worktree");
2587
+ } catch {
2588
+ skipped.push(`could not read ${dir}`);
2589
+ }
2590
+ }
2591
+ const defaultBranch = resolveDefaultBranch3(repoRoot2);
2592
+ if (defaultBranch) {
2593
+ scanTree(`origin/${defaultBranch}`, "default_branch", `origin/${defaultBranch}`);
2594
+ } else {
2595
+ skipped.push("no default branch resolvable (offline clone?) \u2014 base comparison skipped");
2596
+ }
2597
+ const currentBranch = trimOrNull2(runGit(repoRoot2, ["rev-parse", "--abbrev-ref", "HEAD"]));
2598
+ for (const branch of lines(runGit(repoRoot2, ["for-each-ref", "--format=%(refname:short)", "refs/heads"]))) {
2599
+ if (branch === currentBranch)
2600
+ continue;
2601
+ scanTree(branch, "branch", `Branch ${branch}`);
2602
+ }
2603
+ if (opts.includeWorktrees !== false) {
2604
+ const selfRoot = trimOrNull2(runGit(repoRoot2, ["rev-parse", "--show-toplevel"]));
2605
+ for (const line of lines(runGit(repoRoot2, ["worktree", "list", "--porcelain"]))) {
2606
+ if (!line.startsWith("worktree "))
2607
+ continue;
2608
+ const root = line.slice("worktree ".length).trim();
2609
+ if (!root || root === selfRoot || !existsSync3(root))
2610
+ continue;
2611
+ for (const dir of dirs) {
2612
+ const abs = posix4.join(root, dir);
2613
+ if (!existsSync3(abs))
2614
+ continue;
2615
+ try {
2616
+ for (const file of readdirSync2(abs)) {
2617
+ record(`${dir}/${file}`, "worktree", `Worktree ${root}`);
2618
+ }
2619
+ } catch {
2620
+ skipped.push(`could not read ${dir} in ${root}`);
2621
+ }
2622
+ }
2623
+ }
2624
+ }
2625
+ let addedPaths = [];
2626
+ if (defaultBranch && dirs.length > 0) {
2627
+ const diff = runGit(repoRoot2, [
2628
+ "diff",
2629
+ "--name-only",
2630
+ "--diff-filter=A",
2631
+ `origin/${defaultBranch}...HEAD`,
2632
+ "--",
2633
+ ...dirs
2634
+ ]);
2635
+ if (diff === null) {
2636
+ skipped.push("could not diff against the default branch \u2014 everything reported as info");
2637
+ } else {
2638
+ addedPaths = lines(diff);
2639
+ }
2640
+ const untracked = runGit(repoRoot2, [
2641
+ "ls-files",
2642
+ "--others",
2643
+ "--exclude-standard",
2644
+ "--",
2645
+ ...dirs
2646
+ ]);
2647
+ if (untracked !== null)
2648
+ addedPaths = [...addedPaths, ...lines(untracked)];
2649
+ const stagedAdds = runGit(repoRoot2, [
2650
+ "diff",
2651
+ "--cached",
2652
+ "--name-only",
2653
+ "--diff-filter=A",
2654
+ "--",
2655
+ ...dirs
2656
+ ]);
2657
+ if (stagedAdds !== null)
2658
+ addedPaths = [...addedPaths, ...lines(stagedAdds)];
2659
+ }
2660
+ return {
2661
+ isRepo: true,
2662
+ defaultBranch,
2663
+ namespaces,
2664
+ sightingsByNamespace,
2665
+ addedPaths,
2666
+ skipped
2667
+ };
2668
+ }
2669
+ async function checkNumberCollisions(repoRoot2, opts = {}) {
2670
+ const facts = await gatherNumberCollisionFacts(repoRoot2, opts);
2671
+ const findings = computeNumberCollisions(facts);
2672
+ return { ok: isCollisionFree(findings), findings, facts };
2673
+ }
2674
+ async function checkNumberForPath(repoRoot2, proposedPath, opts = {}) {
2675
+ const path = normaliseRepoPath(proposedPath);
2676
+ if (!parseNumbered(posix4.basename(path))) {
2677
+ return { path, namespace: null, number: null, taken: false, sightings: [], suggestion: null };
2678
+ }
2679
+ const facts = await gatherNumberCollisionFacts(repoRoot2, {
2680
+ ...opts,
2681
+ dirs: opts.dirs ?? [posix4.dirname(path)]
2682
+ });
2683
+ return checkProposedNumber(facts, path);
2684
+ }
2685
+
2369
2686
  // ../../packages/code-index/build/review/risk-rules.js
2370
2687
  var RISK_RULES = [
2371
2688
  {
@@ -2690,13 +3007,13 @@ var CodeIndexEngine = class {
2690
3007
  await this.store.init();
2691
3008
  const limit = clampLimit(opts.limit, 20, 100);
2692
3009
  const hits = await this.store.searchFts(query, opts.kind ?? null, limit);
2693
- const out = [];
3010
+ const out2 = [];
2694
3011
  for (const h of hits) {
2695
3012
  const n = await this.store.getNode(h.nodeKey);
2696
3013
  if (n)
2697
- out.push({ ...toHit(n), score: h.score });
3014
+ out2.push({ ...toHit(n), score: h.score });
2698
3015
  }
2699
- return out;
3016
+ return out2;
2700
3017
  }
2701
3018
  async semanticQuery(query, opts = {}) {
2702
3019
  await this.store.init();
@@ -2706,13 +3023,13 @@ var CodeIndexEngine = class {
2706
3023
  const qvec = await this.embedder.embedQuery(query);
2707
3024
  const all = await this.store.allEmbeddings();
2708
3025
  const scored = all.map((e) => ({ nodeKey: e.nodeKey, score: cosineSimilarity(qvec, e.vec) })).sort((a, b) => b.score - a.score).slice(0, limit);
2709
- const out = [];
3026
+ const out2 = [];
2710
3027
  for (const s of scored) {
2711
3028
  const n = await this.store.getNode(s.nodeKey);
2712
3029
  if (n)
2713
- out.push({ ...toHit(n), score: s.score });
3030
+ out2.push({ ...toHit(n), score: s.score });
2714
3031
  }
2715
- return out;
3032
+ return out2;
2716
3033
  }
2717
3034
  async tracePath(nodeKey, opts = {}) {
2718
3035
  await this.store.init();
@@ -2754,7 +3071,7 @@ var CodeIndexEngine = class {
2754
3071
  return null;
2755
3072
  let source = "";
2756
3073
  try {
2757
- const full = readFileSync5(posix4.join(this.repoRoot, n.filePath), "utf8");
3074
+ const full = readFileSync5(posix5.join(this.repoRoot, n.filePath), "utf8");
2758
3075
  source = full.slice(n.startByte, n.endByte);
2759
3076
  } catch {
2760
3077
  return null;
@@ -2825,6 +3142,12 @@ var CodeIndexEngine = class {
2825
3142
  async checkDivergence(opts = {}) {
2826
3143
  return checkDivergence(this.repoRoot, opts);
2827
3144
  }
3145
+ async checkNumberCollisions(opts = {}) {
3146
+ return checkNumberCollisions(this.repoRoot, opts);
3147
+ }
3148
+ async checkNumberForPath(proposedPath, opts = {}) {
3149
+ return checkNumberForPath(this.repoRoot, proposedPath, opts);
3150
+ }
2828
3151
  async reviewChanges(opts = {}) {
2829
3152
  await this.store.init();
2830
3153
  const rawPaths = enumerateChangedPaths(this.repoRoot, opts);
@@ -2888,7 +3211,7 @@ var CodeIndexEngine = class {
2888
3211
  }
2889
3212
  readRepoFile(relPath) {
2890
3213
  try {
2891
- return readFileSync5(posix4.join(this.repoRoot, relPath), "utf8");
3214
+ return readFileSync5(posix5.join(this.repoRoot, relPath), "utf8");
2892
3215
  } catch {
2893
3216
  return null;
2894
3217
  }
@@ -2947,8 +3270,109 @@ function classifyReviewSymbol(row, fileStatus, fh, stale) {
2947
3270
  return { ...row, change: "co_located", changedLineCount: 0, staleIndex: false };
2948
3271
  }
2949
3272
 
3273
+ // ../../packages/code-index/build/store/sqlite-driver.js
3274
+ import { createRequire as createRequire2 } from "node:module";
3275
+ var requireHost = createRequire2(import.meta.url);
3276
+ function loadBuiltin() {
3277
+ return requireHost("node:sqlite");
3278
+ }
3279
+ var builtinProbeCache = null;
3280
+ function probeBuiltinSqlite() {
3281
+ if (builtinProbeCache)
3282
+ return builtinProbeCache;
3283
+ try {
3284
+ const { DatabaseSync } = loadBuiltin();
3285
+ const db = new DatabaseSync(":memory:");
3286
+ try {
3287
+ db.exec("CREATE VIRTUAL TABLE probe_fts USING fts5(t)");
3288
+ db.exec("INSERT INTO probe_fts (t) VALUES ('probe')");
3289
+ db.prepare("SELECT bm25(probe_fts) AS s FROM probe_fts WHERE probe_fts MATCH 'probe'").get();
3290
+ builtinProbeCache = { ok: true };
3291
+ } finally {
3292
+ db.close();
3293
+ }
3294
+ } catch (err4) {
3295
+ builtinProbeCache = {
3296
+ ok: false,
3297
+ reason: err4 instanceof Error ? err4.message : String(err4)
3298
+ };
3299
+ }
3300
+ return builtinProbeCache;
3301
+ }
3302
+ function openBuiltin(dbPath) {
3303
+ const { DatabaseSync } = loadBuiltin();
3304
+ const raw = new DatabaseSync(dbPath);
3305
+ return {
3306
+ prepare: (sql) => raw.prepare(sql),
3307
+ exec: (sql) => raw.exec(sql),
3308
+ close: () => raw.close(),
3309
+ // node:sqlite hat keinen .transaction()-Helper — manuelle Klammer.
3310
+ transaction: (fn) => () => {
3311
+ raw.exec("BEGIN");
3312
+ try {
3313
+ fn();
3314
+ raw.exec("COMMIT");
3315
+ } catch (err4) {
3316
+ try {
3317
+ raw.exec("ROLLBACK");
3318
+ } catch {
3319
+ }
3320
+ throw err4;
3321
+ }
3322
+ }
3323
+ };
3324
+ }
3325
+ function openBetter(dbPath) {
3326
+ const Database = requireHost("better-sqlite3");
3327
+ const raw = new Database(dbPath);
3328
+ return {
3329
+ prepare: (sql) => raw.prepare(sql),
3330
+ exec: (sql) => {
3331
+ raw.exec(sql);
3332
+ },
3333
+ close: () => {
3334
+ raw.close();
3335
+ },
3336
+ transaction: (fn) => raw.transaction(fn)
3337
+ };
3338
+ }
3339
+ function backendOverride() {
3340
+ const v = (process.env.MAIND_SQLITE_BACKEND ?? "").trim().toLowerCase();
3341
+ if (v === "builtin" || v === "better")
3342
+ return v;
3343
+ return null;
3344
+ }
3345
+ function openSqliteDatabase(dbPath) {
3346
+ const override = backendOverride();
3347
+ const openAs = (backend) => {
3348
+ const db = backend === "node:sqlite" ? openBuiltin(dbPath) : openBetter(dbPath);
3349
+ db.exec("PRAGMA journal_mode = WAL");
3350
+ db.exec("PRAGMA foreign_keys = ON");
3351
+ return { db, backend };
3352
+ };
3353
+ if (override === "builtin") {
3354
+ const probe2 = probeBuiltinSqlite();
3355
+ if (!probe2.ok) {
3356
+ throw new Error(`MAIND_SQLITE_BACKEND=builtin, but node:sqlite is unusable: ${probe2.reason}`);
3357
+ }
3358
+ return openAs("node:sqlite");
3359
+ }
3360
+ if (override === "better") {
3361
+ return openAs("better-sqlite3");
3362
+ }
3363
+ const probe = probeBuiltinSqlite();
3364
+ if (probe.ok) {
3365
+ return openAs("node:sqlite");
3366
+ }
3367
+ try {
3368
+ return openAs("better-sqlite3");
3369
+ } catch (err4) {
3370
+ const betterReason = err4 instanceof Error ? err4.message : String(err4);
3371
+ throw new Error(`no usable SQLite backend: node:sqlite unavailable (${probe.reason}); better-sqlite3 unavailable (${betterReason})`);
3372
+ }
3373
+ }
3374
+
2950
3375
  // ../../packages/code-index/build/store/node-sqlite.js
2951
- import Database from "better-sqlite3";
2952
3376
  function toNodeRow(r) {
2953
3377
  return {
2954
3378
  nodeKey: r.node_key,
@@ -2968,10 +3392,12 @@ function toNodeRow(r) {
2968
3392
  var CHUNK = 400;
2969
3393
  var NodeSqliteStore = class {
2970
3394
  db;
3395
+ /** Welches Backend diesen Store trägt — für Diagnose/Status-Ausgaben. */
3396
+ backend;
2971
3397
  constructor(dbPath) {
2972
- this.db = new Database(dbPath);
2973
- this.db.pragma("journal_mode = WAL");
2974
- this.db.pragma("foreign_keys = ON");
3398
+ const opened = openSqliteDatabase(dbPath);
3399
+ this.db = opened.db;
3400
+ this.backend = opened.backend;
2975
3401
  }
2976
3402
  async init() {
2977
3403
  this.db.exec(SCHEMA_SQL);
@@ -3102,7 +3528,7 @@ var NodeSqliteStore = class {
3102
3528
  }
3103
3529
  async pruneOrphanEmbeddings() {
3104
3530
  const info = this.db.prepare("DELETE FROM embeddings WHERE node_key NOT IN (SELECT node_key FROM nodes)").run();
3105
- return info.changes;
3531
+ return Number(info.changes);
3106
3532
  }
3107
3533
  async countEmbeddings() {
3108
3534
  return this.db.prepare("SELECT COUNT(*) AS c FROM embeddings").get().c;
@@ -3444,10 +3870,10 @@ var CodeIndexSync = class {
3444
3870
  };
3445
3871
  var EMBED_CHUNK = 250;
3446
3872
  function chunkArray(items, size) {
3447
- const out = [];
3873
+ const out2 = [];
3448
3874
  for (let i = 0; i < items.length; i += size)
3449
- out.push(items.slice(i, i + size));
3450
- return out;
3875
+ out2.push(items.slice(i, i + size));
3876
+ return out2;
3451
3877
  }
3452
3878
  function numOrNull(v) {
3453
3879
  if (v === null)
@@ -3462,8 +3888,8 @@ function numOr0(v) {
3462
3888
  // ../../packages/code-index/build/node.js
3463
3889
  var DEFAULT_DB_RELPATH = ".maind/code-index.sqlite";
3464
3890
  function createNodeCodeIndex(opts) {
3465
- const dbPath = opts.dbPath ?? posix5.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3466
- mkdirSync3(posix5.dirname(dbPath), { recursive: true });
3891
+ const dbPath = opts.dbPath ?? posix6.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3892
+ mkdirSync3(posix6.dirname(dbPath), { recursive: true });
3467
3893
  const store = new NodeSqliteStore(dbPath);
3468
3894
  const parser = new TreeSitterParser();
3469
3895
  return new CodeIndexEngine({
@@ -3474,7 +3900,7 @@ function createNodeCodeIndex(opts) {
3474
3900
  });
3475
3901
  }
3476
3902
  function createCodeIndexSync(opts) {
3477
- const dbPath = opts.dbPath ?? posix5.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3903
+ const dbPath = opts.dbPath ?? posix6.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3478
3904
  const projectKey = opts.projectKey ?? computeProjectKey(opts.repoRoot) ?? void 0;
3479
3905
  if (!projectKey) {
3480
3906
  throw new Error("No project_key \u2014 the repo has no git remote. Cloud sync scopes by sha256(remote); commit a remote or pass an explicit key.");
@@ -3517,8 +3943,8 @@ function createEmbedder() {
3517
3943
  if (texts.length === 0)
3518
3944
  return [];
3519
3945
  const extractor = await getExtractor();
3520
- const out = await extractor(texts, { pooling: "cls", normalize: true });
3521
- return out.tolist().map((v) => Float32Array.from(v));
3946
+ const out2 = await extractor(texts, { pooling: "cls", normalize: true });
3947
+ return out2.tolist().map((v) => Float32Array.from(v));
3522
3948
  },
3523
3949
  async embedQuery(text) {
3524
3950
  const [vec] = await embedder.embedDocuments([
@@ -3563,7 +3989,7 @@ function runOrtIsolated(argv) {
3563
3989
  stdio: ["ignore", "inherit", "inherit"],
3564
3990
  env: { ...process.env, [OK_FILE_ENV]: okFile }
3565
3991
  });
3566
- const ok = existsSync3(okFile);
3992
+ const ok = existsSync4(okFile);
3567
3993
  try {
3568
3994
  rmSync(dirname3(okFile), { recursive: true, force: true });
3569
3995
  } catch {
@@ -3765,234 +4191,1578 @@ async function runIndexArch(opts) {
3765
4191
  }
3766
4192
  }
3767
4193
 
3768
- // src/review.ts
3769
- import { execFileSync as execFileSync6 } from "node:child_process";
3770
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
3771
- import { dirname as dirname4 } from "node:path";
3772
-
3773
- // ../../packages/code-index/build/index.js
3774
- var CODE_INDEX_VERSION = "0.1.0";
4194
+ // src/launch-cmd.ts
4195
+ import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync6 } from "node:child_process";
4196
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
4197
+ import { homedir as homedir2 } from "node:os";
4198
+ import { dirname as dirname4, join as join5 } from "node:path";
3775
4199
 
3776
- // src/version.ts
3777
- import { createRequire as createRequire2 } from "node:module";
3778
- function resolveVersion() {
3779
- if (true) return "0.9.0";
3780
- try {
3781
- const require2 = createRequire2(import.meta.url);
3782
- return require2("../package.json").version;
3783
- } catch {
3784
- return "0.0.0-unknown";
4200
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
4201
+ function getLineColFromPtr(string, ptr) {
4202
+ let lines2 = string.slice(0, ptr).split(/\r\n|\n|\r/g);
4203
+ return [lines2.length, lines2.pop().length + 1];
4204
+ }
4205
+ function makeCodeBlock(string, line, column) {
4206
+ let lines2 = string.split(/\r\n|\n|\r/g);
4207
+ let codeblock = "";
4208
+ let numberLen = (Math.log10(line + 1) | 0) + 1;
4209
+ for (let i = line - 1; i <= line + 1; i++) {
4210
+ let l = lines2[i - 1];
4211
+ if (!l)
4212
+ continue;
4213
+ codeblock += i.toString().padEnd(numberLen, " ");
4214
+ codeblock += ": ";
4215
+ codeblock += l;
4216
+ codeblock += "\n";
4217
+ if (i === line) {
4218
+ codeblock += " ".repeat(numberLen + column + 2);
4219
+ codeblock += "^\n";
4220
+ }
3785
4221
  }
4222
+ return codeblock;
3786
4223
  }
3787
- var VERSION = resolveVersion();
4224
+ var TomlError = class extends Error {
4225
+ line;
4226
+ column;
4227
+ codeblock;
4228
+ constructor(message, options) {
4229
+ const [line, column] = getLineColFromPtr(options.toml, options.ptr);
4230
+ const codeblock = makeCodeBlock(options.toml, line, column);
4231
+ super(`Invalid TOML document: ${message}
3788
4232
 
3789
- // src/review-render.ts
3790
- var COMMENT_MARKER = "<!-- maind-review -->";
3791
- var COMMENT_FINDINGS_CAP = 30;
3792
- var FOOTER = "maind review \xB7 deterministic, provenance-backed";
3793
- function buildReviewManifest(data, verdict, meta) {
3794
- const review = data.review;
3795
- return {
3796
- schema: "maind.review.manifest/v1",
3797
- generated_at: meta.generatedAt,
3798
- key_prefix: meta.keyPrefix,
3799
- run_ref: meta.runRef,
3800
- base: meta.base,
3801
- diff_stats: meta.diffStats,
3802
- symbol_count: meta.symbolCount,
3803
- risk_flags: review?.risk_flags ?? [],
3804
- findings: (review?.convention_findings ?? []).map((f) => ({
3805
- convention_id: f.convention_id,
3806
- version: f.convention_version,
3807
- severity: f.severity,
3808
- grade: f.grade,
3809
- matched_symbol_count: f.matched_symbols.length,
3810
- matched_changed_symbol_count: f.matched_changed_symbols?.length ?? 0
3811
- })),
3812
- served: (review?.lesson_hints ?? []).map((l) => ({
3813
- id: l.id,
3814
- version: typeof l.version === "number" ? l.version : null,
3815
- tier: l.tier ?? null
3816
- })),
3817
- verdict: verdict?.ok === true && typeof verdict.verdict === "string" ? {
3818
- verdict: verdict.verdict,
3819
- counts: {
3820
- block: verdict.counts?.block ?? 0,
3821
- warn: verdict.counts?.warn ?? 0,
3822
- note: verdict.counts?.note ?? 0
3823
- },
3824
- fail_on: meta.failOn
3825
- } : null,
3826
- run: verdict?.ok === true ? verdict.run ?? null : null,
3827
- ...verdict?.ok === true && verdict.run_note ? { run_note: verdict.run_note } : {},
3828
- coverage_note: review?.coverage_note ?? null,
3829
- regression_context: review?.regression_context ?? null
3830
- };
3831
- }
3832
- function severityBadge(f) {
3833
- return `**[${f.severity}]**`;
3834
- }
3835
- function findingLine(f) {
3836
- const cite = `\`${f.convention_id}@v${f.convention_version}\``;
3837
- const grade = f.grade === "verdict" ? " \xB7 verdict-grade" : "";
3838
- const changed = f.matched_changed_symbols?.length ?? 0;
3839
- const matchInfo = changed > 0 ? `matched on changed symbols: ${formatSymbols(f.matched_changed_symbols ?? [])}` : `matched (co-located): ${formatSymbols(f.matched_symbols)}`;
3840
- const summary = f.summary?.trim() ? ` \u2014 ${f.summary.trim()}` : "";
3841
- return `- ${severityBadge(f)} ${cite}${summary}${grade}
3842
- ${matchInfo}`;
3843
- }
3844
- function formatSymbols(symbols, max = 4) {
3845
- const shown = symbols.slice(0, max).map((s) => `\`${s}\``);
3846
- const rest = symbols.length - shown.length;
3847
- return shown.join(", ") + (rest > 0 ? ` (+${rest} more)` : "");
3848
- }
3849
- function verdictLabel(verdict) {
3850
- const counts = verdict.counts ?? {};
3851
- return `**Verdict: ${(verdict.verdict ?? "?").toUpperCase()}** (block: ${counts.block ?? 0} \xB7 warn: ${counts.warn ?? 0} \xB7 note: ${counts.note ?? 0})`;
3852
- }
3853
- function attestationLine(verdict) {
3854
- const r = verdict.run;
3855
- if (r) {
3856
- const grade = r.attestation_grade ? "attestation-grade" : "recorded, not attestation-grade";
3857
- return `Attestation: run \`${r.id.slice(0, 8)}\` (${grade}) \xB7 commit \`${r.commit_sha.slice(0, 12)}\` \xB7 input \`${r.input_hash.slice(0, 12)}\` \xB7 policy \`${r.policy_hash.slice(0, 12)}\``;
4233
+ ${codeblock}`, options);
4234
+ this.line = line;
4235
+ this.column = column;
4236
+ this.codeblock = codeblock;
3858
4237
  }
3859
- if (verdict.run_note) return `Attestation: none \u2014 ${verdict.run_note}`;
3860
- return null;
4238
+ };
4239
+
4240
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/util.js
4241
+ function isEscaped(str, ptr) {
4242
+ let i = 0;
4243
+ while (str[ptr - ++i] === "\\")
4244
+ ;
4245
+ return --i && i % 2;
4246
+ }
4247
+ function indexOfNewline(str, start = 0, end = str.length) {
4248
+ let idx = str.indexOf("\n", start);
4249
+ if (str[idx - 1] === "\r")
4250
+ idx--;
4251
+ return idx <= end ? idx : -1;
4252
+ }
4253
+ function skipComment(str, ptr) {
4254
+ for (let i = ptr; i < str.length; i++) {
4255
+ let c = str[i];
4256
+ if (c === "\n")
4257
+ return i;
4258
+ if (c === "\r" && str[i + 1] === "\n")
4259
+ return i + 1;
4260
+ if (c < " " && c !== " " || c === "\x7F") {
4261
+ throw new TomlError("control characters are not allowed in comments", {
4262
+ toml: str,
4263
+ ptr
4264
+ });
4265
+ }
4266
+ }
4267
+ return str.length;
4268
+ }
4269
+ function skipVoid(str, ptr, banNewLines, banComments) {
4270
+ let c;
4271
+ while (1) {
4272
+ while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
4273
+ ptr++;
4274
+ if (banComments || c !== "#")
4275
+ break;
4276
+ ptr = skipComment(str, ptr);
4277
+ }
4278
+ return ptr;
4279
+ }
4280
+ function skipUntil(str, ptr, sep2, end, banNewLines = false) {
4281
+ if (!end) {
4282
+ ptr = indexOfNewline(str, ptr);
4283
+ return ptr < 0 ? str.length : ptr;
4284
+ }
4285
+ for (let i = ptr; i < str.length; i++) {
4286
+ let c = str[i];
4287
+ if (c === "#") {
4288
+ i = indexOfNewline(str, i);
4289
+ } else if (c === sep2) {
4290
+ return i + 1;
4291
+ } else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
4292
+ return i;
4293
+ }
4294
+ }
4295
+ throw new TomlError("cannot find end of structure", {
4296
+ toml: str,
4297
+ ptr
4298
+ });
3861
4299
  }
3862
- function baseLabel(meta) {
3863
- return meta.base ? `against \`${meta.base}\`` : "uncommitted work";
4300
+ function getStringEnd(str, seek) {
4301
+ let first = str[seek];
4302
+ let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2] ? str.slice(seek, seek + 3) : first;
4303
+ seek += target.length - 1;
4304
+ do
4305
+ seek = str.indexOf(target, ++seek);
4306
+ while (seek > -1 && first !== "'" && isEscaped(str, seek));
4307
+ if (seek > -1) {
4308
+ seek += target.length;
4309
+ if (target.length > 1) {
4310
+ if (str[seek] === first)
4311
+ seek++;
4312
+ if (str[seek] === first)
4313
+ seek++;
4314
+ }
4315
+ }
4316
+ return seek;
3864
4317
  }
3865
- function renderReviewMarkdown(data, verdict, meta) {
3866
- const review = data.review;
3867
- const findings = review?.convention_findings ?? [];
3868
- const hints = review?.lesson_hints ?? [];
3869
- const riskFlags = review?.risk_flags ?? [];
3870
- const lines = [];
3871
- lines.push("<!-- Generated by `maind review` \u2014 do not edit by hand. -->");
3872
- lines.push(
3873
- `<!-- generated_at: ${meta.generatedAt} | key: ${meta.keyPrefix}${meta.runRef ? ` | run: ${meta.runRef}` : ""} -->`
3874
- );
3875
- lines.push("");
3876
- lines.push("# maind review");
3877
- lines.push("");
3878
- lines.push(
3879
- `Reviewed ${baseLabel(meta)}: ${meta.symbolCount} changed symbol(s) across ${meta.diffStats?.changed_file_count ?? 0} file(s) \xB7 findings: ${findings.length} \xB7 risk flags: ${riskFlags.length} \xB7 lesson hints: ${hints.length}`
3880
- );
3881
- lines.push("");
3882
- if (verdict?.ok === true && verdict.verdict) {
3883
- lines.push(verdictLabel(verdict));
3884
- const attest = attestationLine(verdict);
3885
- if (attest) {
3886
- lines.push("");
3887
- lines.push(attest);
4318
+
4319
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/date.js
4320
+ var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
4321
+ var TomlDate = class _TomlDate extends Date {
4322
+ #hasDate = false;
4323
+ #hasTime = false;
4324
+ #offset = null;
4325
+ constructor(date) {
4326
+ let hasDate = true;
4327
+ let hasTime = true;
4328
+ let offset = "Z";
4329
+ if (typeof date === "string") {
4330
+ let match = date.match(DATE_TIME_RE);
4331
+ if (match) {
4332
+ if (!match[1]) {
4333
+ hasDate = false;
4334
+ date = `0000-01-01T${date}`;
4335
+ }
4336
+ hasTime = !!match[2];
4337
+ hasTime && date[10] === " " && (date = date.replace(" ", "T"));
4338
+ if (match[2] && +match[2] > 23) {
4339
+ date = "";
4340
+ } else {
4341
+ offset = match[3] || null;
4342
+ date = date.toUpperCase();
4343
+ if (!offset && hasTime)
4344
+ date += "Z";
4345
+ }
4346
+ } else {
4347
+ date = "";
4348
+ }
3888
4349
  }
3889
- if (verdict.trust_note) {
3890
- lines.push("");
3891
- lines.push(`_${verdict.trust_note}_`);
4350
+ super(date);
4351
+ if (!isNaN(this.getTime())) {
4352
+ this.#hasDate = hasDate;
4353
+ this.#hasTime = hasTime;
4354
+ this.#offset = offset;
3892
4355
  }
3893
- lines.push("");
3894
- }
3895
- if (findings.length > 0) {
3896
- lines.push("## Convention findings");
3897
- lines.push("");
3898
- for (const f of findings) lines.push(findingLine(f));
3899
- lines.push("");
3900
- } else {
3901
- lines.push("## Convention findings");
3902
- lines.push("");
3903
- lines.push("_No convention findings for this change set._");
3904
- lines.push("");
3905
4356
  }
3906
- if (riskFlags.length > 0) {
3907
- lines.push("## Risk flags");
3908
- lines.push("");
3909
- lines.push(
3910
- "_Path heuristics from the local diff \u2014 informational, they never block._"
3911
- );
3912
- lines.push("");
3913
- for (const flag2 of riskFlags) lines.push(`- \`${flag2}\``);
3914
- lines.push("");
4357
+ isDateTime() {
4358
+ return this.#hasDate && this.#hasTime;
4359
+ }
4360
+ isLocal() {
4361
+ return !this.#hasDate || !this.#hasTime || !this.#offset;
4362
+ }
4363
+ isDate() {
4364
+ return this.#hasDate && !this.#hasTime;
4365
+ }
4366
+ isTime() {
4367
+ return this.#hasTime && !this.#hasDate;
4368
+ }
4369
+ isValid() {
4370
+ return this.#hasDate || this.#hasTime;
4371
+ }
4372
+ toISOString() {
4373
+ let iso = super.toISOString();
4374
+ if (this.isDate())
4375
+ return iso.slice(0, 10);
4376
+ if (this.isTime())
4377
+ return iso.slice(11, 23);
4378
+ if (this.#offset === null)
4379
+ return iso.slice(0, -1);
4380
+ if (this.#offset === "Z")
4381
+ return iso;
4382
+ let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
4383
+ offset = this.#offset[0] === "-" ? offset : -offset;
4384
+ let offsetDate = new Date(this.getTime() - offset * 6e4);
4385
+ return offsetDate.toISOString().slice(0, -1) + this.#offset;
4386
+ }
4387
+ static wrapAsOffsetDateTime(jsDate, offset = "Z") {
4388
+ let date = new _TomlDate(jsDate);
4389
+ date.#offset = offset;
4390
+ return date;
4391
+ }
4392
+ static wrapAsLocalDateTime(jsDate) {
4393
+ let date = new _TomlDate(jsDate);
4394
+ date.#offset = null;
4395
+ return date;
4396
+ }
4397
+ static wrapAsLocalDate(jsDate) {
4398
+ let date = new _TomlDate(jsDate);
4399
+ date.#hasTime = false;
4400
+ date.#offset = null;
4401
+ return date;
4402
+ }
4403
+ static wrapAsLocalTime(jsDate) {
4404
+ let date = new _TomlDate(jsDate);
4405
+ date.#hasDate = false;
4406
+ date.#offset = null;
4407
+ return date;
3915
4408
  }
3916
- if (hints.length > 0) {
3917
- lines.push("## Lesson hints");
3918
- lines.push("");
3919
- lines.push("_Semantic suggestions \u2014 advisory, never part of a verdict._");
3920
- lines.push("");
3921
- for (const h of hints) {
3922
- const parts = [`\`${h.id}\``];
3923
- if (h.tier) parts.push(h.tier);
3924
- if (h.match_reason) parts.push(h.match_reason);
3925
- lines.push(`- **${h.title}** (${parts.join(" \xB7 ")})`);
4409
+ };
4410
+
4411
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/primitive.js
4412
+ var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
4413
+ var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
4414
+ var LEADING_ZERO = /^[+-]?0[0-9_]/;
4415
+ var ESCAPE_REGEX = /^[0-9a-f]{2,8}$/i;
4416
+ var ESC_MAP = {
4417
+ b: "\b",
4418
+ t: " ",
4419
+ n: "\n",
4420
+ f: "\f",
4421
+ r: "\r",
4422
+ e: "\x1B",
4423
+ '"': '"',
4424
+ "\\": "\\"
4425
+ };
4426
+ function parseString(str, ptr = 0, endPtr = str.length) {
4427
+ let isLiteral = str[ptr] === "'";
4428
+ let isMultiline = str[ptr++] === str[ptr] && str[ptr] === str[ptr + 1];
4429
+ if (isMultiline) {
4430
+ endPtr -= 2;
4431
+ if (str[ptr += 2] === "\r")
4432
+ ptr++;
4433
+ if (str[ptr] === "\n")
4434
+ ptr++;
4435
+ }
4436
+ let tmp = 0;
4437
+ let isEscape;
4438
+ let parsed = "";
4439
+ let sliceStart = ptr;
4440
+ while (ptr < endPtr - 1) {
4441
+ let c = str[ptr++];
4442
+ if (c === "\n" || c === "\r" && str[ptr] === "\n") {
4443
+ if (!isMultiline) {
4444
+ throw new TomlError("newlines are not allowed in strings", {
4445
+ toml: str,
4446
+ ptr: ptr - 1
4447
+ });
4448
+ }
4449
+ } else if (c < " " && c !== " " || c === "\x7F") {
4450
+ throw new TomlError("control characters are not allowed in strings", {
4451
+ toml: str,
4452
+ ptr: ptr - 1
4453
+ });
4454
+ }
4455
+ if (isEscape) {
4456
+ isEscape = false;
4457
+ if (c === "x" || c === "u" || c === "U") {
4458
+ let code = str.slice(ptr, ptr += c === "x" ? 2 : c === "u" ? 4 : 8);
4459
+ if (!ESCAPE_REGEX.test(code)) {
4460
+ throw new TomlError("invalid unicode escape", {
4461
+ toml: str,
4462
+ ptr: tmp
4463
+ });
4464
+ }
4465
+ try {
4466
+ parsed += String.fromCodePoint(parseInt(code, 16));
4467
+ } catch {
4468
+ throw new TomlError("invalid unicode escape", {
4469
+ toml: str,
4470
+ ptr: tmp
4471
+ });
4472
+ }
4473
+ } else if (isMultiline && (c === "\n" || c === " " || c === " " || c === "\r")) {
4474
+ ptr = skipVoid(str, ptr - 1, true);
4475
+ if (str[ptr] !== "\n" && str[ptr] !== "\r") {
4476
+ throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
4477
+ toml: str,
4478
+ ptr: tmp
4479
+ });
4480
+ }
4481
+ ptr = skipVoid(str, ptr);
4482
+ } else if (c in ESC_MAP) {
4483
+ parsed += ESC_MAP[c];
4484
+ } else {
4485
+ throw new TomlError("unrecognized escape sequence", {
4486
+ toml: str,
4487
+ ptr: tmp
4488
+ });
4489
+ }
4490
+ sliceStart = ptr;
4491
+ } else if (!isLiteral && c === "\\") {
4492
+ tmp = ptr - 1;
4493
+ isEscape = true;
4494
+ parsed += str.slice(sliceStart, tmp);
4495
+ }
4496
+ }
4497
+ return parsed + str.slice(sliceStart, endPtr - 1);
4498
+ }
4499
+ function parseValue(value2, toml, ptr, integersAsBigInt) {
4500
+ if (value2 === "true")
4501
+ return true;
4502
+ if (value2 === "false")
4503
+ return false;
4504
+ if (value2 === "-inf")
4505
+ return -Infinity;
4506
+ if (value2 === "inf" || value2 === "+inf")
4507
+ return Infinity;
4508
+ if (value2 === "nan" || value2 === "+nan" || value2 === "-nan")
4509
+ return NaN;
4510
+ if (value2 === "-0")
4511
+ return integersAsBigInt ? 0n : 0;
4512
+ let isInt = INT_REGEX.test(value2);
4513
+ if (isInt || FLOAT_REGEX.test(value2)) {
4514
+ if (LEADING_ZERO.test(value2)) {
4515
+ throw new TomlError("leading zeroes are not allowed", {
4516
+ toml,
4517
+ ptr
4518
+ });
4519
+ }
4520
+ value2 = value2.replace(/_/g, "");
4521
+ let numeric = +value2;
4522
+ if (isNaN(numeric)) {
4523
+ throw new TomlError("invalid number", {
4524
+ toml,
4525
+ ptr
4526
+ });
4527
+ }
4528
+ if (isInt) {
4529
+ if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
4530
+ throw new TomlError("integer value cannot be represented losslessly", {
4531
+ toml,
4532
+ ptr
4533
+ });
4534
+ }
4535
+ if (isInt || integersAsBigInt === true)
4536
+ numeric = BigInt(value2);
4537
+ }
4538
+ return numeric;
4539
+ }
4540
+ const date = new TomlDate(value2);
4541
+ if (!date.isValid()) {
4542
+ throw new TomlError("invalid value", {
4543
+ toml,
4544
+ ptr
4545
+ });
4546
+ }
4547
+ return date;
4548
+ }
4549
+
4550
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/extract.js
4551
+ function sliceAndTrimEndOf(str, startPtr, endPtr) {
4552
+ let value2 = str.slice(startPtr, endPtr);
4553
+ let commentIdx = value2.indexOf("#");
4554
+ if (commentIdx > -1) {
4555
+ skipComment(str, commentIdx);
4556
+ value2 = value2.slice(0, commentIdx);
4557
+ }
4558
+ return [value2.trimEnd(), commentIdx];
4559
+ }
4560
+ function extractValue(str, ptr, end, depth, integersAsBigInt) {
4561
+ if (depth === 0) {
4562
+ throw new TomlError("document contains excessively nested structures. aborting.", {
4563
+ toml: str,
4564
+ ptr
4565
+ });
4566
+ }
4567
+ let c = str[ptr];
4568
+ if (c === "[" || c === "{") {
4569
+ let [value2, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
4570
+ if (end) {
4571
+ endPtr2 = skipVoid(str, endPtr2);
4572
+ if (str[endPtr2] === ",")
4573
+ endPtr2++;
4574
+ else if (str[endPtr2] !== end) {
4575
+ throw new TomlError("expected comma or end of structure", {
4576
+ toml: str,
4577
+ ptr: endPtr2
4578
+ });
4579
+ }
4580
+ }
4581
+ return [value2, endPtr2];
4582
+ }
4583
+ let endPtr;
4584
+ if (c === '"' || c === "'") {
4585
+ endPtr = getStringEnd(str, ptr);
4586
+ let parsed = parseString(str, ptr, endPtr);
4587
+ if (end) {
4588
+ endPtr = skipVoid(str, endPtr);
4589
+ if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== "\n" && str[endPtr] !== "\r") {
4590
+ throw new TomlError("unexpected character encountered", {
4591
+ toml: str,
4592
+ ptr: endPtr
4593
+ });
4594
+ }
4595
+ endPtr += +(str[endPtr] === ",");
4596
+ }
4597
+ return [parsed, endPtr];
4598
+ }
4599
+ endPtr = skipUntil(str, ptr, ",", end);
4600
+ let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ","));
4601
+ if (!slice[0]) {
4602
+ throw new TomlError("incomplete key-value declaration: no value specified", {
4603
+ toml: str,
4604
+ ptr
4605
+ });
4606
+ }
4607
+ if (end && slice[1] > -1) {
4608
+ endPtr = skipVoid(str, ptr + slice[1]);
4609
+ endPtr += +(str[endPtr] === ",");
4610
+ }
4611
+ return [
4612
+ parseValue(slice[0], str, ptr, integersAsBigInt),
4613
+ endPtr
4614
+ ];
4615
+ }
4616
+
4617
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/struct.js
4618
+ var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
4619
+ function parseKey(str, ptr, end = "=") {
4620
+ let dot = ptr - 1;
4621
+ let parsed = [];
4622
+ let endPtr = str.indexOf(end, ptr);
4623
+ if (endPtr < 0) {
4624
+ throw new TomlError("incomplete key-value: cannot find end of key", {
4625
+ toml: str,
4626
+ ptr
4627
+ });
4628
+ }
4629
+ do {
4630
+ let c = str[ptr = ++dot];
4631
+ if (c !== " " && c !== " ") {
4632
+ if (c === '"' || c === "'") {
4633
+ if (c === str[ptr + 1] && c === str[ptr + 2]) {
4634
+ throw new TomlError("multiline strings are not allowed in keys", {
4635
+ toml: str,
4636
+ ptr
4637
+ });
4638
+ }
4639
+ let eos = getStringEnd(str, ptr);
4640
+ if (eos < 0) {
4641
+ throw new TomlError("unfinished string encountered", {
4642
+ toml: str,
4643
+ ptr
4644
+ });
4645
+ }
4646
+ dot = str.indexOf(".", eos);
4647
+ let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
4648
+ let newLine = indexOfNewline(strEnd);
4649
+ if (newLine > -1) {
4650
+ throw new TomlError("newlines are not allowed in keys", {
4651
+ toml: str,
4652
+ ptr: ptr + dot + newLine
4653
+ });
4654
+ }
4655
+ if (strEnd.trimStart()) {
4656
+ throw new TomlError("found extra tokens after the string part", {
4657
+ toml: str,
4658
+ ptr: eos
4659
+ });
4660
+ }
4661
+ if (endPtr < eos) {
4662
+ endPtr = str.indexOf(end, eos);
4663
+ if (endPtr < 0) {
4664
+ throw new TomlError("incomplete key-value: cannot find end of key", {
4665
+ toml: str,
4666
+ ptr
4667
+ });
4668
+ }
4669
+ }
4670
+ parsed.push(parseString(str, ptr, eos));
4671
+ } else {
4672
+ dot = str.indexOf(".", ptr);
4673
+ let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
4674
+ if (!KEY_PART_RE.test(part)) {
4675
+ throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
4676
+ toml: str,
4677
+ ptr
4678
+ });
4679
+ }
4680
+ parsed.push(part.trimEnd());
4681
+ }
4682
+ }
4683
+ } while (dot + 1 && dot < endPtr);
4684
+ return [parsed, skipVoid(str, endPtr + 1, true, true)];
4685
+ }
4686
+ function parseInlineTable(str, ptr, depth, integersAsBigInt) {
4687
+ let res = {};
4688
+ let seen = /* @__PURE__ */ new Set();
4689
+ let c;
4690
+ ptr++;
4691
+ while ((c = str[ptr++]) !== "}" && c) {
4692
+ if (c === ",") {
4693
+ throw new TomlError("expected value, found comma", {
4694
+ toml: str,
4695
+ ptr: ptr - 1
4696
+ });
4697
+ } else if (c === "#")
4698
+ ptr = skipComment(str, ptr);
4699
+ else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
4700
+ let k;
4701
+ let t = res;
4702
+ let hasOwn = false;
4703
+ let [key, keyEndPtr] = parseKey(str, ptr - 1);
4704
+ for (let i = 0; i < key.length; i++) {
4705
+ if (i)
4706
+ t = hasOwn ? t[k] : t[k] = {};
4707
+ k = key[i];
4708
+ if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
4709
+ throw new TomlError("trying to redefine an already defined value", {
4710
+ toml: str,
4711
+ ptr
4712
+ });
4713
+ }
4714
+ if (!hasOwn && k === "__proto__") {
4715
+ Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
4716
+ }
4717
+ }
4718
+ if (hasOwn) {
4719
+ throw new TomlError("trying to redefine an already defined value", {
4720
+ toml: str,
4721
+ ptr
4722
+ });
4723
+ }
4724
+ let [value2, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt);
4725
+ seen.add(value2);
4726
+ t[k] = value2;
4727
+ ptr = valueEndPtr;
4728
+ }
4729
+ }
4730
+ if (!c) {
4731
+ throw new TomlError("unfinished table encountered", {
4732
+ toml: str,
4733
+ ptr
4734
+ });
4735
+ }
4736
+ return [res, ptr];
4737
+ }
4738
+ function parseArray(str, ptr, depth, integersAsBigInt) {
4739
+ let res = [];
4740
+ let c;
4741
+ ptr++;
4742
+ while ((c = str[ptr++]) !== "]" && c) {
4743
+ if (c === ",") {
4744
+ throw new TomlError("expected value, found comma", {
4745
+ toml: str,
4746
+ ptr: ptr - 1
4747
+ });
4748
+ } else if (c === "#")
4749
+ ptr = skipComment(str, ptr);
4750
+ else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
4751
+ let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt);
4752
+ res.push(e[0]);
4753
+ ptr = e[1];
4754
+ }
4755
+ }
4756
+ if (!c) {
4757
+ throw new TomlError("unfinished array encountered", {
4758
+ toml: str,
4759
+ ptr
4760
+ });
4761
+ }
4762
+ return [res, ptr];
4763
+ }
4764
+
4765
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/parse.js
4766
+ function peekTable(key, table, meta, type) {
4767
+ let t = table;
4768
+ let m = meta;
4769
+ let k;
4770
+ let hasOwn = false;
4771
+ let state;
4772
+ for (let i = 0; i < key.length; i++) {
4773
+ if (i) {
4774
+ t = hasOwn ? t[k] : t[k] = {};
4775
+ m = (state = m[k]).c;
4776
+ if (type === 0 && (state.t === 1 || state.t === 2)) {
4777
+ return null;
4778
+ }
4779
+ if (state.t === 2) {
4780
+ let l = t.length - 1;
4781
+ t = t[l];
4782
+ m = m[l].c;
4783
+ }
4784
+ }
4785
+ k = key[i];
4786
+ if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
4787
+ return null;
4788
+ }
4789
+ if (!hasOwn) {
4790
+ if (k === "__proto__") {
4791
+ Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
4792
+ Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
4793
+ }
4794
+ m[k] = {
4795
+ t: i < key.length - 1 && type === 2 ? 3 : type,
4796
+ d: false,
4797
+ i: 0,
4798
+ c: {}
4799
+ };
4800
+ }
4801
+ }
4802
+ state = m[k];
4803
+ if (state.t !== type && !(type === 1 && state.t === 3)) {
4804
+ return null;
4805
+ }
4806
+ if (type === 2) {
4807
+ if (!state.d) {
4808
+ state.d = true;
4809
+ t[k] = [];
4810
+ }
4811
+ t[k].push(t = {});
4812
+ state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
4813
+ }
4814
+ if (state.d) {
4815
+ return null;
4816
+ }
4817
+ state.d = true;
4818
+ if (type === 1) {
4819
+ t = hasOwn ? t[k] : t[k] = {};
4820
+ } else if (type === 0 && hasOwn) {
4821
+ return null;
4822
+ }
4823
+ return [k, t, state.c];
4824
+ }
4825
+ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
4826
+ let res = {};
4827
+ let meta = {};
4828
+ let tbl = res;
4829
+ let m = meta;
4830
+ for (let ptr = skipVoid(toml, 0); ptr < toml.length; ) {
4831
+ if (toml[ptr] === "[") {
4832
+ let isTableArray = toml[++ptr] === "[";
4833
+ let k = parseKey(toml, ptr += +isTableArray, "]");
4834
+ if (isTableArray) {
4835
+ if (toml[k[1] - 1] !== "]") {
4836
+ throw new TomlError("expected end of table declaration", {
4837
+ toml,
4838
+ ptr: k[1] - 1
4839
+ });
4840
+ }
4841
+ k[1]++;
4842
+ }
4843
+ let p = peekTable(
4844
+ k[0],
4845
+ res,
4846
+ meta,
4847
+ isTableArray ? 2 : 1
4848
+ /* Type.EXPLICIT */
4849
+ );
4850
+ if (!p) {
4851
+ throw new TomlError("trying to redefine an already defined table or value", {
4852
+ toml,
4853
+ ptr
4854
+ });
4855
+ }
4856
+ m = p[2];
4857
+ tbl = p[1];
4858
+ ptr = k[1];
4859
+ } else {
4860
+ let k = parseKey(toml, ptr);
4861
+ let p = peekTable(
4862
+ k[0],
4863
+ tbl,
4864
+ m,
4865
+ 0
4866
+ /* Type.DOTTED */
4867
+ );
4868
+ if (!p) {
4869
+ throw new TomlError("trying to redefine an already defined table or value", {
4870
+ toml,
4871
+ ptr
4872
+ });
4873
+ }
4874
+ let v = extractValue(toml, k[1], void 0, maxDepth, integersAsBigInt);
4875
+ p[1][p[0]] = v[0];
4876
+ ptr = v[1];
4877
+ }
4878
+ ptr = skipVoid(toml, ptr, true);
4879
+ if (toml[ptr] && toml[ptr] !== "\n" && toml[ptr] !== "\r") {
4880
+ throw new TomlError("each key-value declaration must be followed by an end-of-line", {
4881
+ toml,
4882
+ ptr
4883
+ });
4884
+ }
4885
+ ptr = skipVoid(toml, ptr);
4886
+ }
4887
+ return res;
4888
+ }
4889
+
4890
+ // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/stringify.js
4891
+ var BARE_KEY = /^[a-z0-9-_]+$/i;
4892
+ function extendedTypeOf(obj) {
4893
+ let type = typeof obj;
4894
+ if (type === "object") {
4895
+ if (Array.isArray(obj))
4896
+ return "array";
4897
+ if (obj instanceof Date)
4898
+ return "date";
4899
+ }
4900
+ return type;
4901
+ }
4902
+ function isArrayOfTables(obj) {
4903
+ for (let i = 0; i < obj.length; i++) {
4904
+ if (extendedTypeOf(obj[i]) !== "object")
4905
+ return false;
4906
+ }
4907
+ return obj.length != 0;
4908
+ }
4909
+ function formatString(s) {
4910
+ return JSON.stringify(s).replace(/\x7f/g, "\\u007f");
4911
+ }
4912
+ function stringifyValue(val, type, depth, numberAsFloat) {
4913
+ if (depth === 0) {
4914
+ throw new Error("Could not stringify the object: maximum object depth exceeded");
4915
+ }
4916
+ if (type === "number") {
4917
+ if (isNaN(val))
4918
+ return "nan";
4919
+ if (val === Infinity)
4920
+ return "inf";
4921
+ if (val === -Infinity)
4922
+ return "-inf";
4923
+ if (numberAsFloat && Number.isInteger(val))
4924
+ return val.toFixed(1);
4925
+ return val.toString();
4926
+ }
4927
+ if (type === "bigint" || type === "boolean") {
4928
+ return val.toString();
4929
+ }
4930
+ if (type === "string") {
4931
+ return formatString(val);
4932
+ }
4933
+ if (type === "date") {
4934
+ if (isNaN(val.getTime())) {
4935
+ throw new TypeError("cannot serialize invalid date");
4936
+ }
4937
+ return val.toISOString();
4938
+ }
4939
+ if (type === "object") {
4940
+ return stringifyInlineTable(val, depth, numberAsFloat);
4941
+ }
4942
+ if (type === "array") {
4943
+ return stringifyArray(val, depth, numberAsFloat);
4944
+ }
4945
+ }
4946
+ function stringifyInlineTable(obj, depth, numberAsFloat) {
4947
+ let keys = Object.keys(obj);
4948
+ if (keys.length === 0)
4949
+ return "{}";
4950
+ let res = "{ ";
4951
+ for (let i = 0; i < keys.length; i++) {
4952
+ let k = keys[i];
4953
+ if (i)
4954
+ res += ", ";
4955
+ res += BARE_KEY.test(k) ? k : formatString(k);
4956
+ res += " = ";
4957
+ res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat);
4958
+ }
4959
+ return res + " }";
4960
+ }
4961
+ function stringifyArray(array, depth, numberAsFloat) {
4962
+ if (array.length === 0)
4963
+ return "[]";
4964
+ let res = "[ ";
4965
+ for (let i = 0; i < array.length; i++) {
4966
+ if (i)
4967
+ res += ", ";
4968
+ if (array[i] === null || array[i] === void 0) {
4969
+ throw new TypeError("arrays cannot contain null or undefined values");
4970
+ }
4971
+ res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat);
4972
+ }
4973
+ return res + " ]";
4974
+ }
4975
+ function stringifyArrayTable(array, key, depth, numberAsFloat) {
4976
+ if (depth === 0) {
4977
+ throw new Error("Could not stringify the object: maximum object depth exceeded");
4978
+ }
4979
+ let res = "";
4980
+ for (let i = 0; i < array.length; i++) {
4981
+ res += `${res && "\n"}[[${key}]]
4982
+ `;
4983
+ res += stringifyTable(0, array[i], key, depth, numberAsFloat);
4984
+ }
4985
+ return res;
4986
+ }
4987
+ function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
4988
+ if (depth === 0) {
4989
+ throw new Error("Could not stringify the object: maximum object depth exceeded");
4990
+ }
4991
+ let preamble = "";
4992
+ let tables = "";
4993
+ let keys = Object.keys(obj);
4994
+ for (let i = 0; i < keys.length; i++) {
4995
+ let k = keys[i];
4996
+ if (obj[k] !== null && obj[k] !== void 0) {
4997
+ let type = extendedTypeOf(obj[k]);
4998
+ if (type === "symbol" || type === "function") {
4999
+ throw new TypeError(`cannot serialize values of type '${type}'`);
5000
+ }
5001
+ let key = BARE_KEY.test(k) ? k : formatString(k);
5002
+ if (type === "array" && isArrayOfTables(obj[k])) {
5003
+ tables += (tables && "\n") + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat);
5004
+ } else if (type === "object") {
5005
+ let tblKey = prefix ? `${prefix}.${key}` : key;
5006
+ tables += (tables && "\n") + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat);
5007
+ } else {
5008
+ preamble += key;
5009
+ preamble += " = ";
5010
+ preamble += stringifyValue(obj[k], type, depth, numberAsFloat);
5011
+ preamble += "\n";
5012
+ }
5013
+ }
5014
+ }
5015
+ if (tableKey && (preamble || !tables))
5016
+ preamble = preamble ? `[${tableKey}]
5017
+ ${preamble}` : `[${tableKey}]`;
5018
+ return preamble && tables ? `${preamble}
5019
+ ${tables}` : preamble || tables;
5020
+ }
5021
+ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
5022
+ if (extendedTypeOf(obj) !== "object") {
5023
+ throw new TypeError("stringify can only be called with an object");
5024
+ }
5025
+ let str = stringifyTable(0, obj, "", maxDepth, numbersAsFloat);
5026
+ if (str[str.length - 1] !== "\n")
5027
+ return str + "\n";
5028
+ return str;
5029
+ }
5030
+
5031
+ // src/switchyard.ts
5032
+ var SWITCHYARD_BIN = "switchyard";
5033
+ var SWITCHYARD_INSTALL_ARGV = [
5034
+ "tool",
5035
+ "install",
5036
+ "--python",
5037
+ "3.12",
5038
+ "nemo-switchyard[cli]"
5039
+ ];
5040
+ var SWITCHYARD_INSTALL_ONELINER = 'uv tool install --python 3.12 "nemo-switchyard[cli]"';
5041
+ var UV_INSTALL_DOCS = "https://docs.astral.sh/uv/getting-started/installation/";
5042
+ var DEFAULT_ROUTE = "switchyard";
5043
+ var ROUTES_FILENAME = "switchyard-routes.toml";
5044
+ var STARTUP_GRACE_MS = 5e3;
5045
+ var LAUNCH_TEMPLATE_ID = "tmpl_switchyard_routes_default";
5046
+ var ANTHROPIC_CAPABLE_ID = "anthropic/claude-opus-5";
5047
+ var ANTHROPIC_EFFICIENT_ID = "anthropic/claude-haiku-4-5-20251001";
5048
+ var OPENROUTER_CAPABLE_ID = "openrouter/anthropic/claude-opus-5";
5049
+ var OPENROUTER_EFFICIENT_ID = "openrouter/anthropic/claude-haiku-4-5";
5050
+ var SUPPORTED_AGENTS = ["claude"];
5051
+ var PLANNED_AGENTS = ["codex", "openclaw"];
5052
+ function classifyAgent(agent) {
5053
+ if (agent === "claude") return { kind: "supported", agent };
5054
+ if (agent !== null && PLANNED_AGENTS.includes(agent)) return { kind: "planned", agent };
5055
+ return { kind: "unknown", agent };
5056
+ }
5057
+ function chooseBackend(env) {
5058
+ const hasAnthropic = (env.ANTHROPIC_API_KEY ?? "").trim().length > 0;
5059
+ const hasOpenRouter = (env.OPENROUTER_API_KEY ?? "").trim().length > 0;
5060
+ if (hasAnthropic) {
5061
+ return {
5062
+ kind: "anthropic",
5063
+ capableId: ANTHROPIC_CAPABLE_ID,
5064
+ efficientId: ANTHROPIC_EFFICIENT_ID,
5065
+ warning: null
5066
+ };
5067
+ }
5068
+ if (hasOpenRouter) {
5069
+ return {
5070
+ kind: "openrouter",
5071
+ capableId: OPENROUTER_CAPABLE_ID,
5072
+ efficientId: OPENROUTER_EFFICIENT_ID,
5073
+ warning: null
5074
+ };
5075
+ }
5076
+ return {
5077
+ kind: "none",
5078
+ capableId: OPENROUTER_CAPABLE_ID,
5079
+ efficientId: OPENROUTER_EFFICIENT_ID,
5080
+ warning: "no backend API key found (ANTHROPIC_API_KEY or OPENROUTER_API_KEY) \u2014 the proxy will start but upstream requests will fail until one is exported."
5081
+ };
5082
+ }
5083
+ var CAPABLE_TARGET = "capable";
5084
+ var EFFICIENT_TARGET = "efficient";
5085
+ function policyHeader(backend, policySource) {
5086
+ return [
5087
+ "# Generated by `maind launch` (Agent Switchyard Phase 1, maind.dev \u2014 ADR-272).",
5088
+ "# Regenerated on every launch; do not hand-edit. For a custom policy use",
5089
+ "# `maind launch <agent> --config <path>`.",
5090
+ "#",
5091
+ `# Routing policy: ${policySource === "curated" ? "curated (maind content plane)" : "builtin default"}`,
5092
+ "# \u2014 deterministic stage routing (NVIDIA NeMo Switchyard `stage_router`).",
5093
+ "# effort low/medium -> efficient_target (fast tier)",
5094
+ "# effort high/xhigh/max -> capable_target (strong tier)",
5095
+ "# picker `efficient_first` + confidence_threshold 0.5: steady write progress",
5096
+ "# keeps the efficient tier; corroborating error/exploration signals escalate",
5097
+ "# to the capable tier.",
5098
+ "#",
5099
+ `# Backend: ${backend.kind === "none" ? "openrouter (EXAMPLE \u2014 no API key detected)" : backend.kind}`,
5100
+ "# \u2014 detected locally from the environment, never from the policy source.",
5101
+ "# API keys are read from the environment by the proxy at runtime \u2014 never",
5102
+ "# stored in this file.",
5103
+ ""
5104
+ ].join("\n");
5105
+ }
5106
+ function defaultRouteTable(routeName) {
5107
+ return {
5108
+ [routeName]: {
5109
+ type: "stage_router",
5110
+ capable_target: CAPABLE_TARGET,
5111
+ efficient_target: EFFICIENT_TARGET,
5112
+ picker: "efficient_first",
5113
+ confidence_threshold: 0.5
5114
+ }
5115
+ };
5116
+ }
5117
+ function buildRoutesToml(routeName, backend, curatedRoutes = null) {
5118
+ const doc = {
5119
+ targets: {
5120
+ [CAPABLE_TARGET]: { id: backend.capableId },
5121
+ [EFFICIENT_TARGET]: { id: backend.efficientId }
5122
+ },
5123
+ routes: curatedRoutes ?? defaultRouteTable(routeName)
5124
+ };
5125
+ return policyHeader(backend, curatedRoutes ? "curated" : "builtin") + stringify(doc) + "\n";
5126
+ }
5127
+ function acceptPolicyTemplate(body, routeName) {
5128
+ let parsed;
5129
+ try {
5130
+ parsed = parse(body);
5131
+ } catch {
5132
+ return null;
5133
+ }
5134
+ if (typeof parsed !== "object" || parsed === null) return null;
5135
+ const doc = parsed;
5136
+ if ("targets" in doc) return null;
5137
+ const routes = doc.routes;
5138
+ if (typeof routes !== "object" || routes === null) return null;
5139
+ const table = routes;
5140
+ const route = table[routeName];
5141
+ if (typeof route !== "object" || route === null) return null;
5142
+ for (const key of ["capable_target", "efficient_target"]) {
5143
+ const ref = route[key];
5144
+ if (ref !== void 0 && ref !== CAPABLE_TARGET && ref !== EFFICIENT_TARGET) return null;
5145
+ }
5146
+ return table;
5147
+ }
5148
+ function acceptRoutesToml(body, routeName) {
5149
+ let parsed;
5150
+ try {
5151
+ parsed = parse(body);
5152
+ } catch {
5153
+ return null;
5154
+ }
5155
+ if (typeof parsed !== "object" || parsed === null) return null;
5156
+ const routes = parsed.routes;
5157
+ if (typeof routes !== "object" || routes === null) return null;
5158
+ if (!(routeName in routes)) return null;
5159
+ return body;
5160
+ }
5161
+ function buildSwitchyardArgv(bin, agent, routeName, configPath) {
5162
+ return [bin, "launch", agent, "--model", routeName, "--config", configPath];
5163
+ }
5164
+ function buildDirectArgv(agent) {
5165
+ return [agent];
5166
+ }
5167
+ var SIGNAL_NUMBERS = {
5168
+ SIGHUP: 1,
5169
+ SIGINT: 2,
5170
+ SIGQUIT: 3,
5171
+ SIGKILL: 9,
5172
+ SIGTERM: 15
5173
+ };
5174
+ function exitCodeFromChild(code, signal) {
5175
+ if (code !== null) return code;
5176
+ if (signal !== null) {
5177
+ const num2 = SIGNAL_NUMBERS[signal];
5178
+ return num2 !== void 0 ? 128 + num2 : 1;
5179
+ }
5180
+ return 1;
5181
+ }
5182
+ function classifyChildExit(code, signal, elapsedMs) {
5183
+ if (signal !== null) return "propagate";
5184
+ if (code === 0) return "propagate";
5185
+ return elapsedMs < STARTUP_GRACE_MS ? "fallback-eligible" : "propagate";
5186
+ }
5187
+ function buildInstallAnnouncement() {
5188
+ return `switchyard not found \u2014 installing via uv: ${SWITCHYARD_INSTALL_ONELINER}`;
5189
+ }
5190
+ function buildUvMissingMessage() {
5191
+ return `switchyard is not installed and \`uv\` is missing, so maind will not install it for you.
5192
+ maind: install uv (${UV_INSTALL_DOCS}), then run:
5193
+ maind: ${SWITCHYARD_INSTALL_ONELINER}
5194
+ maind: and re-run \`maind launch claude\`. (Or launch without routing: \`maind launch claude --direct\`.)`;
5195
+ }
5196
+ function buildInstallStillMissingMessage() {
5197
+ return "switchyard still not reachable after install \u2014 uv's bin dir may be missing from PATH (try `uv tool update-shell`, then open a new terminal).";
5198
+ }
5199
+ function buildFallbackBanner(reason) {
5200
+ return [
5201
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
5202
+ ` switchyard proxy unavailable: ${reason}`,
5203
+ " launching claude DIRECTLY \u2014 no model routing for this session.",
5204
+ " (disable this fallback with --no-fallback)",
5205
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
5206
+ ].join("\n");
5207
+ }
5208
+ function buildDirectBanner() {
5209
+ return "launching claude directly (--direct) \u2014 no switchyard proxy, no model routing.";
5210
+ }
5211
+ function renderDryRun(plan, json) {
5212
+ if (json) {
5213
+ const payload = {
5214
+ agent: plan.agent,
5215
+ config_path: plan.configPath,
5216
+ config_source: plan.configSource,
5217
+ toml: plan.toml,
5218
+ argv: plan.argv,
5219
+ backend: plan.backendKind,
5220
+ switchyard_detected: plan.switchyardDetected
5221
+ };
5222
+ return { stdout: JSON.stringify(payload, null, 2) + "\n", stderr: "" };
5223
+ }
5224
+ const stderr = [
5225
+ `maind: dry-run \u2014 nothing installed, written, or spawned.`,
5226
+ `maind: config ${plan.configPath} (${plan.configSource})`,
5227
+ `maind: switchyard detected: ${plan.switchyardDetected ? "yes" : "no"}`,
5228
+ `maind: would run: ${plan.argv.join(" ")}`
5229
+ ].join("\n");
5230
+ return {
5231
+ stdout: plan.toml ?? `(using existing config: ${plan.configPath})
5232
+ `,
5233
+ stderr: stderr + "\n"
5234
+ };
5235
+ }
5236
+
5237
+ // src/version.ts
5238
+ import { createRequire as createRequire3 } from "node:module";
5239
+ function resolveVersion() {
5240
+ if (true) return "0.10.0";
5241
+ try {
5242
+ const require2 = createRequire3(import.meta.url);
5243
+ return require2("../package.json").version;
5244
+ } catch {
5245
+ return "0.0.0-unknown";
5246
+ }
5247
+ }
5248
+ var VERSION = resolveVersion();
5249
+
5250
+ // src/launch-cmd.ts
5251
+ var TEMPLATE_FETCH_TIMEOUT_MS = 5e3;
5252
+ function err(line) {
5253
+ process.stderr.write(`maind: ${line}
5254
+ `);
5255
+ }
5256
+ function msg5(e) {
5257
+ return e instanceof Error ? e.message : String(e);
5258
+ }
5259
+ var VALUE_FLAGS = /* @__PURE__ */ new Set(["--config", "--route", "--mcp-url"]);
5260
+ function parseLaunchArgs(argv) {
5261
+ const get = (name) => {
5262
+ const i = argv.indexOf(name);
5263
+ if (i === -1 || i + 1 >= argv.length) return void 0;
5264
+ return argv[i + 1];
5265
+ };
5266
+ const has = (name) => argv.includes(name);
5267
+ let agent = null;
5268
+ for (let i = 0; i < argv.length; i++) {
5269
+ const tok = argv[i];
5270
+ if (tok.startsWith("-")) {
5271
+ if (VALUE_FLAGS.has(tok)) i++;
5272
+ continue;
5273
+ }
5274
+ agent = tok;
5275
+ break;
5276
+ }
5277
+ return {
5278
+ agent,
5279
+ dryRun: has("--dry-run"),
5280
+ config: get("--config") ?? null,
5281
+ route: get("--route") ?? DEFAULT_ROUTE,
5282
+ direct: has("--direct"),
5283
+ noFallback: has("--no-fallback"),
5284
+ noInstall: has("--no-install"),
5285
+ json: has("--json"),
5286
+ mcpUrl: get("--mcp-url") ?? null
5287
+ };
5288
+ }
5289
+ var realLaunchDeps = {
5290
+ probe(bin, args) {
5291
+ const res = spawnSync2(bin, args, {
5292
+ stdio: ["ignore", "pipe", "pipe"],
5293
+ encoding: "utf8",
5294
+ timeout: 1e4
5295
+ });
5296
+ const ok = res.status === 0 && !res.error;
5297
+ const out2 = typeof res.stdout === "string" ? res.stdout.trim() : "";
5298
+ return { ok, stdout: out2.length > 0 ? out2 : null };
5299
+ },
5300
+ install(argv) {
5301
+ const res = spawnSync2("uv", [...argv], { stdio: ["ignore", "inherit", "inherit"] });
5302
+ return res.status;
5303
+ },
5304
+ spawnInteractive(argv) {
5305
+ const start = Date.now();
5306
+ return new Promise((resolve) => {
5307
+ let settled = false;
5308
+ const child = spawn(argv[0], argv.slice(1), { stdio: "inherit", env: process.env });
5309
+ const onSigint = () => {
5310
+ };
5311
+ const makeForward = (sig) => () => {
5312
+ try {
5313
+ child.kill(sig);
5314
+ } catch {
5315
+ }
5316
+ };
5317
+ const onSigterm = makeForward("SIGTERM");
5318
+ const onSighup = makeForward("SIGHUP");
5319
+ process.on("SIGINT", onSigint);
5320
+ process.on("SIGTERM", onSigterm);
5321
+ process.on("SIGHUP", onSighup);
5322
+ const cleanup = () => {
5323
+ process.removeListener("SIGINT", onSigint);
5324
+ process.removeListener("SIGTERM", onSigterm);
5325
+ process.removeListener("SIGHUP", onSighup);
5326
+ };
5327
+ child.on("error", (e) => {
5328
+ if (settled) return;
5329
+ settled = true;
5330
+ cleanup();
5331
+ resolve({ code: null, signal: null, elapsedMs: Date.now() - start, spawnError: e.message });
5332
+ });
5333
+ child.on("exit", (code, signal) => {
5334
+ if (settled) return;
5335
+ settled = true;
5336
+ cleanup();
5337
+ resolve({ code, signal, elapsedMs: Date.now() - start, spawnError: null });
5338
+ });
5339
+ });
5340
+ },
5341
+ async fetchTemplate(mcpUrl, projectKey) {
5342
+ let auth;
5343
+ try {
5344
+ auth = resolveAuth();
5345
+ } catch (e) {
5346
+ if (e instanceof MissingApiKeyError) return null;
5347
+ throw e;
5348
+ }
5349
+ try {
5350
+ const client = new McpClient({
5351
+ apiKey: auth.apiKey,
5352
+ deviceId: auth.deviceId,
5353
+ clientVersion: VERSION,
5354
+ mcpUrl: mcpUrl ?? void 0,
5355
+ projectKey
5356
+ });
5357
+ const timeout = new Promise((resolve) => {
5358
+ const t = setTimeout(() => resolve(null), TEMPLATE_FETCH_TIMEOUT_MS);
5359
+ t.unref();
5360
+ });
5361
+ const call = client.callTool("get_template", { template_id: LAUNCH_TEMPLATE_ID }).then(
5362
+ (r) => typeof r.template_body === "string" && r.template_body.trim().length > 0 ? r.template_body : null
5363
+ ).catch(() => null);
5364
+ return await Promise.race([call, timeout]);
5365
+ } catch {
5366
+ return null;
5367
+ }
5368
+ },
5369
+ writeConfig(path, content) {
5370
+ const dir = dirname4(path);
5371
+ if (dir && dir !== ".") mkdirSync4(dir, { recursive: true });
5372
+ writeFileSync4(path, content, "utf8");
5373
+ },
5374
+ readFile(path) {
5375
+ try {
5376
+ return readFileSync6(path, "utf8");
5377
+ } catch {
5378
+ return null;
5379
+ }
5380
+ },
5381
+ fileExists(path) {
5382
+ return existsSync5(path);
5383
+ },
5384
+ repoRoot() {
5385
+ try {
5386
+ const out2 = execFileSync6("git", ["rev-parse", "--show-toplevel"], {
5387
+ encoding: "utf8",
5388
+ stdio: ["ignore", "pipe", "ignore"],
5389
+ timeout: 5e3
5390
+ }).trim();
5391
+ return out2.length > 0 ? out2 : null;
5392
+ } catch {
5393
+ return null;
5394
+ }
5395
+ },
5396
+ projectKey() {
5397
+ return gatherGitState().projectKey;
5398
+ },
5399
+ userConfigDir() {
5400
+ return configDir();
5401
+ },
5402
+ homeDir() {
5403
+ return homedir2();
5404
+ },
5405
+ env: process.env,
5406
+ platform: process.platform
5407
+ };
5408
+ function resolveRoutesPath(repoRoot2, userConfigDir) {
5409
+ return repoRoot2 !== null ? join5(repoRoot2, ".maind", ROUTES_FILENAME) : join5(userConfigDir, ROUTES_FILENAME);
5410
+ }
5411
+ async function runLaunch(opts, deps = realLaunchDeps) {
5412
+ const verdict = classifyAgent(opts.agent);
5413
+ if (verdict.kind === "unknown") {
5414
+ err(
5415
+ `usage: maind launch <agent> [--dry-run] [--config <path>] [--route <name>] [--direct] [--no-fallback] [--no-install] \u2014 supported: ${SUPPORTED_AGENTS.join(", ")}`
5416
+ );
5417
+ return 2;
5418
+ }
5419
+ if (verdict.kind === "planned") {
5420
+ err(
5421
+ `agent '${verdict.agent}' is planned but not yet supported \u2014 v1 supports: ${SUPPORTED_AGENTS.join(", ")}.`
5422
+ );
5423
+ return 2;
5424
+ }
5425
+ if (opts.json && !opts.dryRun) {
5426
+ err("--json is only valid together with --dry-run (stdout belongs to the agent session).");
5427
+ return 2;
5428
+ }
5429
+ const agent = verdict.agent;
5430
+ if (opts.direct) {
5431
+ err(buildDirectBanner());
5432
+ return spawnAndPropagate(deps, buildDirectArgv(agent));
5433
+ }
5434
+ const backend = chooseBackend(deps.env);
5435
+ if (backend.warning !== null) err(backend.warning);
5436
+ let configPath;
5437
+ let toml;
5438
+ let source;
5439
+ if (opts.config !== null) {
5440
+ if (!deps.fileExists(opts.config)) {
5441
+ err(`config not found: ${opts.config}`);
5442
+ return 2;
5443
+ }
5444
+ const body = deps.readFile(opts.config);
5445
+ if (body === null || acceptRoutesToml(body, opts.route) === null) {
5446
+ err(`config ${opts.config} is not valid routes TOML with a [routes.${opts.route}] entry.`);
5447
+ return 2;
5448
+ }
5449
+ configPath = opts.config;
5450
+ toml = null;
5451
+ source = "flag";
5452
+ } else {
5453
+ const template = await deps.fetchTemplate(opts.mcpUrl, deps.projectKey());
5454
+ const curatedRoutes = template === null ? null : acceptPolicyTemplate(template, opts.route);
5455
+ toml = buildRoutesToml(opts.route, backend, curatedRoutes);
5456
+ if (curatedRoutes !== null) {
5457
+ source = "template";
5458
+ } else {
5459
+ source = "builtin";
5460
+ const reason = template === null ? "no curated template (offline, unauthenticated, or not published)" : `curated template unusable (missing route '${opts.route}', or it pins its own targets)`;
5461
+ err(`using builtin routing policy (${reason}).`);
5462
+ }
5463
+ configPath = resolveRoutesPath(deps.repoRoot(), deps.userConfigDir());
5464
+ }
5465
+ let bin = SWITCHYARD_BIN;
5466
+ let detected = deps.probe(SWITCHYARD_BIN, ["--version"]).ok;
5467
+ if (opts.dryRun) {
5468
+ const plan = {
5469
+ agent,
5470
+ configPath,
5471
+ configSource: source,
5472
+ toml,
5473
+ argv: buildSwitchyardArgv(bin, agent, opts.route, configPath),
5474
+ backendKind: backend.kind,
5475
+ switchyardDetected: detected
5476
+ };
5477
+ const out2 = renderDryRun(plan, opts.json);
5478
+ if (out2.stderr.length > 0) process.stderr.write(out2.stderr);
5479
+ process.stdout.write(out2.stdout);
5480
+ return 0;
5481
+ }
5482
+ if (!detected) {
5483
+ if (opts.noInstall) {
5484
+ err(`switchyard not found (--no-install). Install it with:
5485
+ maind: ${SWITCHYARD_INSTALL_ONELINER}`);
5486
+ return 1;
5487
+ }
5488
+ if (!deps.probe("uv", ["--version"]).ok) {
5489
+ err(buildUvMissingMessage());
5490
+ return 1;
5491
+ }
5492
+ err(buildInstallAnnouncement());
5493
+ deps.install(SWITCHYARD_INSTALL_ARGV);
5494
+ if (deps.probe(SWITCHYARD_BIN, ["--version"]).ok) {
5495
+ detected = true;
5496
+ } else {
5497
+ const shim = join5(
5498
+ deps.homeDir(),
5499
+ ".local",
5500
+ "bin",
5501
+ deps.platform === "win32" ? "switchyard.exe" : "switchyard"
5502
+ );
5503
+ if (deps.probe(shim, ["--version"]).ok) {
5504
+ bin = shim;
5505
+ detected = true;
5506
+ }
5507
+ }
5508
+ if (!detected) {
5509
+ return degradeOrFail(deps, opts, agent, buildInstallStillMissingMessage());
5510
+ }
5511
+ }
5512
+ if (source !== "flag" && toml !== null) {
5513
+ try {
5514
+ deps.writeConfig(configPath, toml);
5515
+ } catch (e) {
5516
+ err(`could not write ${configPath}: ${msg5(e)}`);
5517
+ return 1;
5518
+ }
5519
+ }
5520
+ const result = await deps.spawnInteractive(buildSwitchyardArgv(bin, agent, opts.route, configPath));
5521
+ if (result.spawnError !== null) {
5522
+ return degradeOrFail(deps, opts, agent, `could not start switchyard (${result.spawnError})`);
5523
+ }
5524
+ if (classifyChildExit(result.code, result.signal, result.elapsedMs) === "fallback-eligible") {
5525
+ return degradeOrFail(
5526
+ deps,
5527
+ opts,
5528
+ agent,
5529
+ `switchyard exited with code ${result.code} after ${(result.elapsedMs / 1e3).toFixed(1)}s`
5530
+ );
5531
+ }
5532
+ return exitCodeFromChild(result.code, result.signal);
5533
+ }
5534
+ async function degradeOrFail(deps, opts, agent, reason) {
5535
+ if (opts.noFallback) {
5536
+ err(`${reason} (--no-fallback set \u2014 not degrading to a direct spawn).`);
5537
+ return 1;
5538
+ }
5539
+ process.stderr.write(buildFallbackBanner(reason) + "\n");
5540
+ return spawnAndPropagate(deps, buildDirectArgv(agent));
5541
+ }
5542
+ async function spawnAndPropagate(deps, argv) {
5543
+ const result = await deps.spawnInteractive(argv);
5544
+ if (result.spawnError !== null) {
5545
+ err(`'${argv[0]}' not found on PATH \u2014 install the agent CLI first (${result.spawnError}).`);
5546
+ return 1;
5547
+ }
5548
+ return exitCodeFromChild(result.code, result.signal);
5549
+ }
5550
+
5551
+ // src/review.ts
5552
+ import { execFileSync as execFileSync7 } from "node:child_process";
5553
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
5554
+ import { dirname as dirname5 } from "node:path";
5555
+
5556
+ // ../../packages/code-index/build/index.js
5557
+ var CODE_INDEX_VERSION = "0.1.0";
5558
+
5559
+ // src/review-render.ts
5560
+ var COMMENT_MARKER = "<!-- maind-review -->";
5561
+ var COMMENT_FINDINGS_CAP = 30;
5562
+ var FOOTER = "maind review \xB7 deterministic, provenance-backed";
5563
+ function buildReviewManifest(data, verdict, meta) {
5564
+ const review = data.review;
5565
+ return {
5566
+ schema: "maind.review.manifest/v1",
5567
+ generated_at: meta.generatedAt,
5568
+ key_prefix: meta.keyPrefix,
5569
+ run_ref: meta.runRef,
5570
+ base: meta.base,
5571
+ diff_stats: meta.diffStats,
5572
+ symbol_count: meta.symbolCount,
5573
+ risk_flags: review?.risk_flags ?? [],
5574
+ findings: (review?.convention_findings ?? []).map((f) => ({
5575
+ convention_id: f.convention_id,
5576
+ version: f.convention_version,
5577
+ severity: f.severity,
5578
+ grade: f.grade,
5579
+ matched_symbol_count: f.matched_symbols.length,
5580
+ matched_changed_symbol_count: f.matched_changed_symbols?.length ?? 0
5581
+ })),
5582
+ served: (review?.lesson_hints ?? []).map((l) => ({
5583
+ id: l.id,
5584
+ version: typeof l.version === "number" ? l.version : null,
5585
+ tier: l.tier ?? null
5586
+ })),
5587
+ verdict: verdict?.ok === true && typeof verdict.verdict === "string" ? {
5588
+ verdict: verdict.verdict,
5589
+ counts: {
5590
+ block: verdict.counts?.block ?? 0,
5591
+ warn: verdict.counts?.warn ?? 0,
5592
+ note: verdict.counts?.note ?? 0
5593
+ },
5594
+ fail_on: meta.failOn
5595
+ } : null,
5596
+ run: verdict?.ok === true ? verdict.run ?? null : null,
5597
+ ...verdict?.ok === true && verdict.run_note ? { run_note: verdict.run_note } : {},
5598
+ coverage_note: review?.coverage_note ?? null,
5599
+ regression_context: review?.regression_context ?? null
5600
+ };
5601
+ }
5602
+ function severityBadge(f) {
5603
+ return `**[${f.severity}]**`;
5604
+ }
5605
+ function findingLine(f) {
5606
+ const cite = `\`${f.convention_id}@v${f.convention_version}\``;
5607
+ const grade = f.grade === "verdict" ? " \xB7 verdict-grade" : "";
5608
+ const changed = f.matched_changed_symbols?.length ?? 0;
5609
+ const matchInfo = changed > 0 ? `matched on changed symbols: ${formatSymbols(f.matched_changed_symbols ?? [])}` : `matched (co-located): ${formatSymbols(f.matched_symbols)}`;
5610
+ const summary = f.summary?.trim() ? ` \u2014 ${f.summary.trim()}` : "";
5611
+ return `- ${severityBadge(f)} ${cite}${summary}${grade}
5612
+ ${matchInfo}`;
5613
+ }
5614
+ function formatSymbols(symbols, max = 4) {
5615
+ const shown = symbols.slice(0, max).map((s) => `\`${s}\``);
5616
+ const rest = symbols.length - shown.length;
5617
+ return shown.join(", ") + (rest > 0 ? ` (+${rest} more)` : "");
5618
+ }
5619
+ function verdictLabel(verdict) {
5620
+ const counts = verdict.counts ?? {};
5621
+ return `**Verdict: ${(verdict.verdict ?? "?").toUpperCase()}** (block: ${counts.block ?? 0} \xB7 warn: ${counts.warn ?? 0} \xB7 note: ${counts.note ?? 0})`;
5622
+ }
5623
+ function attestationLine(verdict) {
5624
+ const r = verdict.run;
5625
+ if (r) {
5626
+ const grade = r.attestation_grade ? "attestation-grade" : "recorded, not attestation-grade";
5627
+ return `Attestation: run \`${r.id.slice(0, 8)}\` (${grade}) \xB7 commit \`${r.commit_sha.slice(0, 12)}\` \xB7 input \`${r.input_hash.slice(0, 12)}\` \xB7 policy \`${r.policy_hash.slice(0, 12)}\``;
5628
+ }
5629
+ if (verdict.run_note) return `Attestation: none \u2014 ${verdict.run_note}`;
5630
+ return null;
5631
+ }
5632
+ function baseLabel(meta) {
5633
+ return meta.base ? `against \`${meta.base}\`` : "uncommitted work";
5634
+ }
5635
+ function renderReviewMarkdown(data, verdict, meta) {
5636
+ const review = data.review;
5637
+ const findings = review?.convention_findings ?? [];
5638
+ const hints = review?.lesson_hints ?? [];
5639
+ const riskFlags = review?.risk_flags ?? [];
5640
+ const lines2 = [];
5641
+ lines2.push("<!-- Generated by `maind review` \u2014 do not edit by hand. -->");
5642
+ lines2.push(
5643
+ `<!-- generated_at: ${meta.generatedAt} | key: ${meta.keyPrefix}${meta.runRef ? ` | run: ${meta.runRef}` : ""} -->`
5644
+ );
5645
+ lines2.push("");
5646
+ lines2.push("# maind review");
5647
+ lines2.push("");
5648
+ lines2.push(
5649
+ `Reviewed ${baseLabel(meta)}: ${meta.symbolCount} changed symbol(s) across ${meta.diffStats?.changed_file_count ?? 0} file(s) \xB7 findings: ${findings.length} \xB7 risk flags: ${riskFlags.length} \xB7 lesson hints: ${hints.length}`
5650
+ );
5651
+ lines2.push("");
5652
+ if (verdict?.ok === true && verdict.verdict) {
5653
+ lines2.push(verdictLabel(verdict));
5654
+ const attest = attestationLine(verdict);
5655
+ if (attest) {
5656
+ lines2.push("");
5657
+ lines2.push(attest);
5658
+ }
5659
+ if (verdict.trust_note) {
5660
+ lines2.push("");
5661
+ lines2.push(`_${verdict.trust_note}_`);
5662
+ }
5663
+ lines2.push("");
5664
+ }
5665
+ if (findings.length > 0) {
5666
+ lines2.push("## Convention findings");
5667
+ lines2.push("");
5668
+ for (const f of findings) lines2.push(findingLine(f));
5669
+ lines2.push("");
5670
+ } else {
5671
+ lines2.push("## Convention findings");
5672
+ lines2.push("");
5673
+ lines2.push("_No convention findings for this change set._");
5674
+ lines2.push("");
5675
+ }
5676
+ if (riskFlags.length > 0) {
5677
+ lines2.push("## Risk flags");
5678
+ lines2.push("");
5679
+ lines2.push(
5680
+ "_Path heuristics from the local diff \u2014 informational, they never block._"
5681
+ );
5682
+ lines2.push("");
5683
+ for (const flag2 of riskFlags) lines2.push(`- \`${flag2}\``);
5684
+ lines2.push("");
5685
+ }
5686
+ if (hints.length > 0) {
5687
+ lines2.push("## Lesson hints");
5688
+ lines2.push("");
5689
+ lines2.push("_Semantic suggestions \u2014 advisory, never part of a verdict._");
5690
+ lines2.push("");
5691
+ for (const h of hints) {
5692
+ const parts = [`\`${h.id}\``];
5693
+ if (h.tier) parts.push(h.tier);
5694
+ if (h.match_reason) parts.push(h.match_reason);
5695
+ lines2.push(`- **${h.title}** (${parts.join(" \xB7 ")})`);
3926
5696
  }
3927
- lines.push("");
5697
+ lines2.push("");
3928
5698
  }
3929
5699
  const rc = review?.regression_context;
3930
5700
  if (rc && (rc.hot_files ?? 0) > 0) {
3931
- lines.push("");
3932
- lines.push(
5701
+ lines2.push("");
5702
+ lines2.push(
3933
5703
  `_History: ${rc.hot_files}/${rc.files_scanned ?? "?"} changed file(s) high-churn (max ${rc.max_recent_commits ?? "?"} commits in the last ${rc.window_commits ?? "?"}-commit window, ${rc.distinct_authors ?? "?"} author(s))._`
3934
5704
  );
3935
5705
  }
3936
5706
  if (review?.coverage_note) {
3937
- lines.push("## Coverage");
3938
- lines.push("");
3939
- lines.push(`> ${review.coverage_note}`);
3940
- lines.push("");
5707
+ lines2.push("## Coverage");
5708
+ lines2.push("");
5709
+ lines2.push(`> ${review.coverage_note}`);
5710
+ lines2.push("");
3941
5711
  }
3942
- lines.push("---");
3943
- lines.push(FOOTER);
3944
- lines.push("");
3945
- return lines.join("\n");
5712
+ lines2.push("---");
5713
+ lines2.push(FOOTER);
5714
+ lines2.push("");
5715
+ return lines2.join("\n");
3946
5716
  }
3947
5717
  function renderReviewComment(data, verdict, meta, reportPath) {
3948
5718
  const review = data.review;
3949
5719
  const findings = review?.convention_findings ?? [];
3950
5720
  const riskFlags = review?.risk_flags ?? [];
3951
5721
  const hints = review?.lesson_hints ?? [];
3952
- const lines = [];
3953
- lines.push(COMMENT_MARKER);
3954
- lines.push("## maind review");
3955
- lines.push("");
5722
+ const lines2 = [];
5723
+ lines2.push(COMMENT_MARKER);
5724
+ lines2.push("## maind review");
5725
+ lines2.push("");
3956
5726
  if (verdict?.ok === true && verdict.verdict) {
3957
- lines.push(verdictLabel(verdict));
5727
+ lines2.push(verdictLabel(verdict));
3958
5728
  const attest = attestationLine(verdict);
3959
- if (attest) lines.push(attest);
3960
- lines.push("");
5729
+ if (attest) lines2.push(attest);
5730
+ lines2.push("");
3961
5731
  }
3962
5732
  if (findings.length === 0) {
3963
- lines.push(
5733
+ lines2.push(
3964
5734
  `No convention findings for this change set (${meta.symbolCount} changed symbol(s) reviewed ${baseLabel(meta)}).`
3965
5735
  );
3966
- lines.push("");
5736
+ lines2.push("");
3967
5737
  } else {
3968
5738
  const shown = findings.slice(0, COMMENT_FINDINGS_CAP);
3969
- for (const f of shown) lines.push(findingLine(f));
5739
+ for (const f of shown) lines2.push(findingLine(f));
3970
5740
  if (findings.length > shown.length) {
3971
- lines.push("");
3972
- lines.push(
5741
+ lines2.push("");
5742
+ lines2.push(
3973
5743
  `\u2026and ${findings.length - shown.length} more finding(s) \u2014 see the \`${reportPath}\` build artifact.`
3974
5744
  );
3975
5745
  }
3976
- lines.push("");
5746
+ lines2.push("");
3977
5747
  }
3978
5748
  if (riskFlags.length > 0) {
3979
- lines.push(
5749
+ lines2.push(
3980
5750
  `**Risk flags:** ${riskFlags.map((f) => `\`${f}\``).join(" \xB7 ")} _(heuristics, never blocking)_`
3981
5751
  );
3982
- lines.push("");
5752
+ lines2.push("");
3983
5753
  }
3984
5754
  if (hints.length > 0) {
3985
- lines.push("**Related lessons:** " + hints.map((h) => `\`${h.id}\``).join(" \xB7 "));
3986
- lines.push("");
5755
+ lines2.push("**Related lessons:** " + hints.map((h) => `\`${h.id}\``).join(" \xB7 "));
5756
+ lines2.push("");
3987
5757
  }
3988
5758
  if (review?.coverage_note) {
3989
- lines.push(`> ${review.coverage_note}`);
3990
- lines.push("");
5759
+ lines2.push(`> ${review.coverage_note}`);
5760
+ lines2.push("");
3991
5761
  }
3992
- lines.push("---");
3993
- lines.push(`_${FOOTER}_`);
3994
- lines.push("");
3995
- return lines.join("\n");
5762
+ lines2.push("---");
5763
+ lines2.push(`_${FOOTER}_`);
5764
+ lines2.push("");
5765
+ return lines2.join("\n");
3996
5766
  }
3997
5767
  function renderFailOpenReview(meta, reason) {
3998
5768
  return [
@@ -4061,12 +5831,12 @@ function nowIso3() {
4061
5831
  return (/* @__PURE__ */ new Date()).toISOString();
4062
5832
  }
4063
5833
  function writeArtifact3(path, content) {
4064
- const dir = dirname4(path);
4065
- if (dir && dir !== ".") mkdirSync4(dir, { recursive: true });
4066
- writeFileSync4(path, content, "utf8");
5834
+ const dir = dirname5(path);
5835
+ if (dir && dir !== ".") mkdirSync5(dir, { recursive: true });
5836
+ writeFileSync5(path, content, "utf8");
4067
5837
  }
4068
- function manifestPathFor(out) {
4069
- return out.endsWith(".md") ? out.slice(0, -3) + ".json" : out + ".json";
5838
+ function manifestPathFor(out2) {
5839
+ return out2.endsWith(".md") ? out2.slice(0, -3) + ".json" : out2 + ".json";
4070
5840
  }
4071
5841
  function verdictCrossesFailOn(verdict, failOn) {
4072
5842
  return failOn === "block" ? verdict === "block" : verdict === "block" || verdict === "warn";
@@ -4088,7 +5858,7 @@ function buildDiffStats(result) {
4088
5858
  }
4089
5859
  function resolveRepoRoot() {
4090
5860
  try {
4091
- return execFileSync6("git", ["rev-parse", "--show-toplevel"], {
5861
+ return execFileSync7("git", ["rev-parse", "--show-toplevel"], {
4092
5862
  encoding: "utf8",
4093
5863
  stdio: ["ignore", "pipe", "ignore"]
4094
5864
  }).trim();
@@ -4123,7 +5893,7 @@ async function runReview(opts) {
4123
5893
  if (opts.comment) writeArtifact3(opts.comment, renderFailOpenComment(meta, reason));
4124
5894
  if (opts.json) process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
4125
5895
  } catch (writeErr) {
4126
- warn5(`could not write fail-open artifacts: ${msg5(writeErr)}`);
5896
+ warn5(`could not write fail-open artifacts: ${msg6(writeErr)}`);
4127
5897
  return opts.strict ? 2 : 1;
4128
5898
  }
4129
5899
  return hard ? 2 : 0;
@@ -4258,9 +6028,9 @@ async function runReview(opts) {
4258
6028
  verdict = null;
4259
6029
  }
4260
6030
  }
4261
- } catch (err2) {
4262
- if (opts.strict) return failOpen(`review_verdict failed: ${msg5(err2)}`, "infra");
4263
- warn5(`gate unavailable (continuing advisory): ${msg5(err2)}`);
6031
+ } catch (err4) {
6032
+ if (opts.strict) return failOpen(`review_verdict failed: ${msg6(err4)}`, "infra");
6033
+ warn5(`gate unavailable (continuing advisory): ${msg6(err4)}`);
4264
6034
  verdict = null;
4265
6035
  }
4266
6036
  }
@@ -4281,9 +6051,9 @@ async function runReview(opts) {
4281
6051
  }
4282
6052
  }
4283
6053
  return 0;
4284
- } catch (err2) {
4285
- if (err2 instanceof MissingApiKeyError) return failOpen(err2.message, "expected");
4286
- return failOpen(msg5(err2), "infra");
6054
+ } catch (err4) {
6055
+ if (err4 instanceof MissingApiKeyError) return failOpen(err4.message, "expected");
6056
+ return failOpen(msg6(err4), "infra");
4287
6057
  }
4288
6058
  }
4289
6059
  var EXPECTED_SERVER_ERRORS = /* @__PURE__ */ new Set([
@@ -4306,25 +6076,524 @@ function writeAll(review, verdict, meta, opts) {
4306
6076
  }
4307
6077
  if (opts.json) process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
4308
6078
  }
4309
- function msg5(err2) {
4310
- return err2 instanceof Error ? err2.message : String(err2);
6079
+ function msg6(err4) {
6080
+ return err4 instanceof Error ? err4.message : String(err4);
4311
6081
  }
4312
6082
  function warn5(line) {
4313
6083
  process.stderr.write(`maind review: ${line}
4314
6084
  `);
4315
6085
  }
4316
6086
 
6087
+ // ../../packages/claude-code-telemetry/dist/session-analysis.js
6088
+ import { createReadStream, existsSync as existsSync6 } from "node:fs";
6089
+ import { createInterface } from "node:readline";
6090
+
6091
+ // ../../packages/claude-code-telemetry/dist/effort.js
6092
+ var LEVELS = /* @__PURE__ */ new Set([
6093
+ "low",
6094
+ "medium",
6095
+ "high",
6096
+ "xhigh",
6097
+ "max"
6098
+ ]);
6099
+ var DIRECTIVE_RE = /(?:^|\s)\/(low|medium|high|xhigh|max)(?=\s|$|[!?.,;:])/gi;
6100
+ function detectEffortLevel(messageText) {
6101
+ if (!messageText)
6102
+ return null;
6103
+ let last = null;
6104
+ for (const match of messageText.matchAll(DIRECTIVE_RE)) {
6105
+ const lvl = match[1].toLowerCase();
6106
+ if (LEVELS.has(lvl))
6107
+ last = lvl;
6108
+ }
6109
+ return last;
6110
+ }
6111
+
6112
+ // ../../packages/claude-code-telemetry/dist/session-analysis.js
6113
+ var ANALYZER_VERSION = "2026-08-04";
6114
+ var REWORK_RE = /(\brückgängig\b|zurücknehmen|\bnochmal(s)?\b|noch mal|\brevert\b|\bundo\b|\bredo\b|stattdessen|\binstead\b|mach(e)? das (nochmal|neu)|try again)/i;
6115
+ var CORRECTION_RE = /(\bnein\b|\bfalsch\b|stimmt nicht|das ist nicht|so nicht|\bnicht so\b|warum hast du|wieso hast du|\bno\b|\bnope\b|\bwrong\b|\bincorrect\b|that'?s not|that is not|\bdon'?t\b|\bdoesn'?t\b|shouldn'?t)/i;
6116
+ var ACCEPTANCE_RE = /(\bdanke\b|\bperfekt\b|\bsuper\b|\bpasst\b|\bgenau\b|sieht gut|\bcommit\b|\bship\b|\bmerge\b|\bpush\b|looks good|\blgtm\b|\bthanks\b|thank you|\bperfect\b|\bgreat\b|\bexactly\b|\bnice\b|well done|👍|✅|🎉)/i;
6117
+ var INTERRUPT_MARKER = "[Request interrupted by user";
6118
+ var DENIAL_MARKER = "The user doesn't want to proceed with this tool use";
6119
+ var SYNTHETIC_MODEL = "<synthetic>";
6120
+ var MOMENTUM_DECAY = 0.7;
6121
+ var MAG = {
6122
+ denial: 1.5,
6123
+ interrupt: 1.25,
6124
+ rework: 1.5,
6125
+ correction: 1,
6126
+ acceptance: 1,
6127
+ repeat: 0.75
6128
+ };
6129
+ function num(v) {
6130
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
6131
+ }
6132
+ function bumpModel(acc, model, sidechain, outputTokens, ts) {
6133
+ const key = `${sidechain ? "s" : "m"}:${model}`;
6134
+ const cur = acc.get(key);
6135
+ if (!cur) {
6136
+ acc.set(key, {
6137
+ model,
6138
+ turns: 1,
6139
+ output_tokens: outputTokens,
6140
+ first_ts: ts,
6141
+ last_ts: ts,
6142
+ sidechain
6143
+ });
6144
+ return;
6145
+ }
6146
+ cur.turns++;
6147
+ cur.output_tokens += outputTokens;
6148
+ if (ts > 0) {
6149
+ if (cur.first_ts === 0 || ts < cur.first_ts)
6150
+ cur.first_ts = ts;
6151
+ if (ts > cur.last_ts)
6152
+ cur.last_ts = ts;
6153
+ }
6154
+ }
6155
+ function asBlocks(content) {
6156
+ if (typeof content === "string")
6157
+ return [{ type: "text", text: content }];
6158
+ if (!Array.isArray(content))
6159
+ return [];
6160
+ return content.filter((b) => b !== null && typeof b === "object");
6161
+ }
6162
+ function textOf(blocks) {
6163
+ const parts = [];
6164
+ for (const b of blocks) {
6165
+ if (b.type === "text" && typeof b.text === "string")
6166
+ parts.push(b.text);
6167
+ }
6168
+ return parts.join("\n");
6169
+ }
6170
+ function isRepeat(prev, cur) {
6171
+ const toks = (s) => new Set(s.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((w) => w.length > 2));
6172
+ const a = toks(prev);
6173
+ const b = toks(cur);
6174
+ if (a.size < 3 || b.size < 3)
6175
+ return false;
6176
+ const [small, big] = a.size <= b.size ? [a, b] : [b, a];
6177
+ let shared = 0;
6178
+ for (const w of small)
6179
+ if (big.has(w))
6180
+ shared++;
6181
+ return shared / small.size >= 0.8;
6182
+ }
6183
+ function classifyGenuine(text, prevText, priorWork) {
6184
+ if (REWORK_RE.test(text))
6185
+ return { polarity: -1, magnitude: MAG.rework, kind: "rework" };
6186
+ if (priorWork && CORRECTION_RE.test(text))
6187
+ return { polarity: -1, magnitude: MAG.correction, kind: "correction" };
6188
+ if (ACCEPTANCE_RE.test(text))
6189
+ return { polarity: 1, magnitude: MAG.acceptance, kind: "acceptance" };
6190
+ if (prevText !== null && isRepeat(prevText, text))
6191
+ return { polarity: -1, magnitude: MAG.repeat, kind: "repeat" };
6192
+ return { polarity: 0, magnitude: 0, kind: "neutral" };
6193
+ }
6194
+ function emptyAnalysis(sessionId) {
6195
+ return {
6196
+ session_id: sessionId,
6197
+ user_turns: 0,
6198
+ assistant_turns: 0,
6199
+ tool_calls: 0,
6200
+ agent_questions: 0,
6201
+ input_tokens: 0,
6202
+ output_tokens: 0,
6203
+ cache_read_tokens: 0,
6204
+ cache_creation_tokens: 0,
6205
+ user_chars: 0,
6206
+ user_token_estimate: 0,
6207
+ human_share: 0,
6208
+ reactions_pos: 0,
6209
+ reactions_neg: 0,
6210
+ net_sentiment: 0,
6211
+ corrections: 0,
6212
+ reworks: 0,
6213
+ interrupts: 0,
6214
+ tool_denials: 0,
6215
+ repeated_instructions: 0,
6216
+ acceptances: 0,
6217
+ momentum_series: [],
6218
+ momentum_final: 0,
6219
+ momentum_auc: 0,
6220
+ ended_positive: false,
6221
+ recovery_events: 0,
6222
+ effort_at_end: null,
6223
+ model_mix: [],
6224
+ ai_title: null,
6225
+ session_slug: null,
6226
+ lines_seen: 0,
6227
+ analyzer_version: ANALYZER_VERSION
6228
+ };
6229
+ }
6230
+ async function analyzeSession(path) {
6231
+ const a = emptyAnalysis("");
6232
+ if (!path || !existsSync6(path))
6233
+ return a;
6234
+ const reactions = [];
6235
+ const modelMix = /* @__PURE__ */ new Map();
6236
+ let effort = null;
6237
+ let priorAssistantWorked = false;
6238
+ let prevUserText = null;
6239
+ await new Promise((resolve) => {
6240
+ const stream = createReadStream(path, { encoding: "utf-8" });
6241
+ const rl = createInterface({ input: stream });
6242
+ const processLine = (line) => {
6243
+ const trimmed = line.trim();
6244
+ if (!trimmed)
6245
+ return;
6246
+ let parsed;
6247
+ try {
6248
+ parsed = JSON.parse(trimmed);
6249
+ } catch {
6250
+ return;
6251
+ }
6252
+ a.lines_seen++;
6253
+ if (!a.session_id) {
6254
+ const sid = parsed.sessionId ?? parsed.session_id;
6255
+ if (typeof sid === "string" && sid)
6256
+ a.session_id = sid;
6257
+ }
6258
+ if (parsed.type === "ai-title" && typeof parsed.aiTitle === "string" && parsed.aiTitle) {
6259
+ a.ai_title = parsed.aiTitle;
6260
+ }
6261
+ if (typeof parsed.slug === "string" && parsed.slug)
6262
+ a.session_slug = parsed.slug;
6263
+ const role = parsed.message?.role;
6264
+ const isSide = parsed.isSidechain === true;
6265
+ const usage = parsed.message?.usage;
6266
+ if (role === "assistant" && usage) {
6267
+ a.input_tokens += num(usage.input_tokens);
6268
+ a.output_tokens += num(usage.output_tokens);
6269
+ a.cache_read_tokens += num(usage.cache_read_input_tokens);
6270
+ a.cache_creation_tokens += num(usage.cache_creation_input_tokens);
6271
+ }
6272
+ if (role === "assistant" && typeof parsed.message?.model === "string" && parsed.message.model && parsed.message.model !== SYNTHETIC_MODEL) {
6273
+ const rawTs = parsed.timestamp ? Date.parse(parsed.timestamp) : Number.NaN;
6274
+ bumpModel(modelMix, parsed.message.model, isSide, num(usage?.output_tokens), Number.isFinite(rawTs) ? rawTs : 0);
6275
+ }
6276
+ if (isSide)
6277
+ return;
6278
+ if (role === "assistant") {
6279
+ a.assistant_turns++;
6280
+ const blocks2 = asBlocks(parsed.message?.content);
6281
+ let worked = false;
6282
+ for (const b of blocks2) {
6283
+ if (b.type === "tool_use") {
6284
+ a.tool_calls++;
6285
+ worked = true;
6286
+ if (b.name === "AskUserQuestion")
6287
+ a.agent_questions++;
6288
+ }
6289
+ }
6290
+ priorAssistantWorked = worked;
6291
+ return;
6292
+ }
6293
+ if (role !== "user")
6294
+ return;
6295
+ const blocks = asBlocks(parsed.message?.content);
6296
+ const text = textOf(blocks);
6297
+ if (text.includes(INTERRUPT_MARKER)) {
6298
+ a.interrupts++;
6299
+ reactions.push({ polarity: -1, magnitude: MAG.interrupt, kind: "interrupt" });
6300
+ return;
6301
+ }
6302
+ const denied = blocks.some((b) => b.type === "tool_result" && b.is_error === true && typeof b.content === "string" && b.content.includes(DENIAL_MARKER));
6303
+ if (denied) {
6304
+ a.tool_denials++;
6305
+ reactions.push({ polarity: -1, magnitude: MAG.denial, kind: "denial" });
6306
+ return;
6307
+ }
6308
+ if (blocks.some((b) => b.type === "tool_result"))
6309
+ return;
6310
+ if (!text.trim())
6311
+ return;
6312
+ a.user_turns++;
6313
+ a.user_chars += text.length;
6314
+ const directive = detectEffortLevel(text);
6315
+ if (directive)
6316
+ effort = directive;
6317
+ const r = classifyGenuine(text, prevUserText, priorAssistantWorked);
6318
+ prevUserText = text;
6319
+ if (r.kind === "correction")
6320
+ a.corrections++;
6321
+ else if (r.kind === "rework")
6322
+ a.reworks++;
6323
+ else if (r.kind === "repeat")
6324
+ a.repeated_instructions++;
6325
+ else if (r.kind === "acceptance")
6326
+ a.acceptances++;
6327
+ if (r.polarity !== 0)
6328
+ reactions.push(r);
6329
+ priorAssistantWorked = false;
6330
+ };
6331
+ rl.on("line", (line) => {
6332
+ try {
6333
+ processLine(line);
6334
+ } catch {
6335
+ }
6336
+ });
6337
+ rl.on("close", () => resolve());
6338
+ rl.on("error", () => resolve());
6339
+ stream.on("error", () => resolve());
6340
+ });
6341
+ a.effort_at_end = effort;
6342
+ a.model_mix = [...modelMix.values()].sort((x, y) => x.first_ts - y.first_ts || x.model.localeCompare(y.model));
6343
+ a.user_token_estimate = Math.round(a.user_chars / 4);
6344
+ const denom = a.user_token_estimate + a.output_tokens;
6345
+ a.human_share = denom > 0 ? Number((a.user_token_estimate / denom).toFixed(4)) : 0;
6346
+ let momentum = 0;
6347
+ let auc = 0;
6348
+ const series = [];
6349
+ reactions.forEach((r, i) => {
6350
+ if (r.polarity > 0)
6351
+ a.reactions_pos++;
6352
+ else if (r.polarity < 0)
6353
+ a.reactions_neg++;
6354
+ momentum = momentum * MOMENTUM_DECAY + r.polarity * r.magnitude;
6355
+ const value2 = Number(momentum.toFixed(3));
6356
+ series.push({
6357
+ t: i,
6358
+ value: value2,
6359
+ ...r.kind !== "neutral" ? { kind: r.kind } : {}
6360
+ });
6361
+ auc += value2;
6362
+ if (i > 0 && reactions[i - 1].polarity < 0 && r.polarity > 0)
6363
+ a.recovery_events++;
6364
+ });
6365
+ a.net_sentiment = a.reactions_pos - a.reactions_neg;
6366
+ a.momentum_series = series;
6367
+ a.momentum_final = series.length ? series[series.length - 1].value : 0;
6368
+ a.momentum_auc = Number(auc.toFixed(3));
6369
+ a.ended_positive = a.momentum_final > 0;
6370
+ return a;
6371
+ }
6372
+
6373
+ // ../../packages/claude-code-telemetry/dist/ingest.js
6374
+ function ingestBody(analytics, span, ctx) {
6375
+ const body = { session_analytics: analytics };
6376
+ if (span && span.started_at) {
6377
+ body.session_span = ctx ? { ...span, ...ctx } : span;
6378
+ }
6379
+ return body;
6380
+ }
6381
+ function ingestHeaders(auth) {
6382
+ const headers = {
6383
+ "content-type": "application/json",
6384
+ authorization: `Bearer ${auth.apiKey}`
6385
+ };
6386
+ if (auth.deviceId)
6387
+ headers["x-maind-device-id"] = auth.deviceId;
6388
+ return headers;
6389
+ }
6390
+ async function postSessionAnalytics(target, analytics, span, ctx) {
6391
+ try {
6392
+ const res = await fetch(target.endpoint, {
6393
+ method: "POST",
6394
+ headers: ingestHeaders(target),
6395
+ body: JSON.stringify(ingestBody(analytics, span, ctx))
6396
+ });
6397
+ if (!res.ok) {
6398
+ const text = await res.text().catch(() => "");
6399
+ return { ok: false, inserted: 0, status: res.status, error: text.slice(0, 500) };
6400
+ }
6401
+ const json = await res.json().catch(() => null);
6402
+ return {
6403
+ ok: true,
6404
+ inserted: json?.analytics_stored ? 1 : 0,
6405
+ spanStored: json?.span_stored === true,
6406
+ status: res.status
6407
+ };
6408
+ } catch (err4) {
6409
+ return {
6410
+ ok: false,
6411
+ inserted: 0,
6412
+ error: err4 instanceof Error ? err4.message : String(err4)
6413
+ };
6414
+ }
6415
+ }
6416
+
6417
+ // ../../packages/claude-code-telemetry/dist/windows.js
6418
+ var FIVE_HOURS_MS = 5 * 60 * 60 * 1e3;
6419
+ var WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
6420
+
6421
+ // ../../packages/claude-code-telemetry/dist/projects.js
6422
+ import { readdir, stat } from "node:fs/promises";
6423
+ import { homedir as homedir3 } from "node:os";
6424
+ import { join as join6 } from "node:path";
6425
+ var HOME = homedir3();
6426
+ var DEFAULT_PROJECTS_DIR = join6(HOME, ".claude", "projects");
6427
+ function cwdFromProjectDir(encoded) {
6428
+ if (!encoded.startsWith("-"))
6429
+ return encoded;
6430
+ return encoded.replace(/-/g, "/");
6431
+ }
6432
+ async function listProjects(projectsDir = DEFAULT_PROJECTS_DIR) {
6433
+ let entries;
6434
+ try {
6435
+ entries = await readdir(projectsDir);
6436
+ } catch {
6437
+ return [];
6438
+ }
6439
+ const out2 = [];
6440
+ for (const entry of entries) {
6441
+ if (!entry.startsWith("-"))
6442
+ continue;
6443
+ const dir = join6(projectsDir, entry);
6444
+ let files;
6445
+ try {
6446
+ files = await readdir(dir);
6447
+ } catch {
6448
+ continue;
6449
+ }
6450
+ const transcripts = files.filter((f) => f.endsWith(".jsonl"));
6451
+ if (transcripts.length === 0)
6452
+ continue;
6453
+ let mtimeMs = 0;
6454
+ for (const f of transcripts) {
6455
+ try {
6456
+ const st = await stat(join6(dir, f));
6457
+ if (st.mtimeMs > mtimeMs)
6458
+ mtimeMs = st.mtimeMs;
6459
+ } catch {
6460
+ }
6461
+ }
6462
+ out2.push({
6463
+ encodedDir: entry,
6464
+ cwd: cwdFromProjectDir(entry),
6465
+ transcripts: transcripts.map((f) => join6(dir, f)),
6466
+ mtimeMs
6467
+ });
6468
+ }
6469
+ out2.sort((a, b) => b.mtimeMs - a.mtimeMs);
6470
+ return out2.map(({ mtimeMs: _mtime, ...rest }) => rest);
6471
+ }
6472
+
6473
+ // src/sessions-cmd.ts
6474
+ var DEFAULT_ENDPOINT = "https://vsddmiosisvfrairepxv.supabase.co/functions/v1/mcp-client-metrics-ingest";
6475
+ var THROTTLE_MS = 120;
6476
+ function err2(line) {
6477
+ process.stderr.write(`maind: ${line}
6478
+ `);
6479
+ }
6480
+ function out(line) {
6481
+ process.stdout.write(line + "\n");
6482
+ }
6483
+ function parseSessionsArgs(argv) {
6484
+ const args = { dryRun: false, since: null, json: false };
6485
+ for (let i = 0; i < argv.length; i++) {
6486
+ const a = argv[i];
6487
+ if (a === "--dry-run") args.dryRun = true;
6488
+ else if (a === "--json") args.json = true;
6489
+ else if (a === "--since") {
6490
+ const raw = argv[++i];
6491
+ const ts = raw ? Date.parse(raw) : Number.NaN;
6492
+ if (Number.isFinite(ts)) args.since = ts;
6493
+ else err2(`ignoring unparseable --since '${raw ?? ""}' (expected YYYY-MM-DD)`);
6494
+ }
6495
+ }
6496
+ return args;
6497
+ }
6498
+ function endpoint() {
6499
+ const raw = process.env.MAIND_INGEST_URL?.trim();
6500
+ return raw && raw.length > 0 ? raw : DEFAULT_ENDPOINT;
6501
+ }
6502
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
6503
+ async function runSessionsBackfill(args) {
6504
+ let auth;
6505
+ try {
6506
+ auth = resolveAuth();
6507
+ } catch (e) {
6508
+ if (e instanceof MissingApiKeyError) {
6509
+ err2(e.message);
6510
+ return 1;
6511
+ }
6512
+ throw e;
6513
+ }
6514
+ const projects = await listProjects();
6515
+ const transcripts = projects.flatMap((p) => p.transcripts);
6516
+ if (transcripts.length === 0) {
6517
+ err2("no Claude Code transcripts found under ~/.claude/projects.");
6518
+ return 0;
6519
+ }
6520
+ const target = endpoint();
6521
+ const rows = [];
6522
+ let skipped = 0;
6523
+ let failed = 0;
6524
+ for (const path of transcripts) {
6525
+ const analysis = await analyzeSession(path);
6526
+ if (!analysis.session_id) {
6527
+ skipped++;
6528
+ continue;
6529
+ }
6530
+ if (args.since !== null) {
6531
+ const last = analysis.model_mix.reduce((m, s) => Math.max(m, s.last_ts), 0);
6532
+ if (last > 0 && last < args.since) {
6533
+ skipped++;
6534
+ continue;
6535
+ }
6536
+ }
6537
+ const main2 = analysis.model_mix.filter((m) => !m.sidechain);
6538
+ const row = {
6539
+ session_id: analysis.session_id,
6540
+ title: analysis.ai_title,
6541
+ models: main2.map((m) => `${m.model}\xD7${m.turns}`).join(", ") || "\u2014",
6542
+ turns: analysis.assistant_turns,
6543
+ lines: analysis.lines_seen,
6544
+ posted: false
6545
+ };
6546
+ if (!args.dryRun) {
6547
+ const res = await postSessionAnalytics(
6548
+ { endpoint: target, apiKey: auth.apiKey, deviceId: auth.deviceId },
6549
+ analysis
6550
+ );
6551
+ row.posted = res.ok;
6552
+ if (!res.ok) {
6553
+ failed++;
6554
+ row.error = res.error ?? `status ${res.status ?? "?"}`;
6555
+ }
6556
+ await sleep(THROTTLE_MS);
6557
+ }
6558
+ rows.push(row);
6559
+ }
6560
+ if (args.json) {
6561
+ out(JSON.stringify({ ok: failed === 0, dry_run: args.dryRun, skipped, failed, sessions: rows }, null, 2));
6562
+ return failed === 0 ? 0 : 1;
6563
+ }
6564
+ out(
6565
+ args.dryRun ? `Would upload ${rows.length} session(s) to ${target}:` : `Uploaded ${rows.length - failed}/${rows.length} session(s) to ${target}:`
6566
+ );
6567
+ out("");
6568
+ for (const r of rows) {
6569
+ const mark = args.dryRun ? " " : r.posted ? "\u2713" : "\u2717";
6570
+ out(`${mark} ${r.session_id.slice(0, 8)} ${String(r.turns).padStart(4)} turns ${r.models}`);
6571
+ out(` title: ${r.title ?? "\u2014"}`);
6572
+ if (r.error) out(` error: ${r.error}`);
6573
+ }
6574
+ out("");
6575
+ if (skipped > 0) out(`${skipped} transcript(s) skipped (no session id, or older than --since).`);
6576
+ if (args.dryRun) {
6577
+ out("Dry run \u2014 nothing was uploaded.");
6578
+ out(
6579
+ "Note: a real run uploads the session TITLES listed above (content-derived) for every project on this machine. They are only stored if you enabled 'Session titles' in the consent center; without it the numbers land and the titles are dropped."
6580
+ );
6581
+ }
6582
+ if (failed > 0) err2(`${failed} session(s) failed \u2014 re-running is safe, the server upserts.`);
6583
+ return failed === 0 ? 0 : 1;
6584
+ }
6585
+
4317
6586
  // src/sync-cmd.ts
4318
- import { execFileSync as execFileSync7 } from "node:child_process";
6587
+ import { execFileSync as execFileSync8 } from "node:child_process";
4319
6588
  var CONSENT_POLICY_VERSION = "2026-07-05";
4320
6589
  var DEFAULT_DASHBOARD_URL = "https://app.maind.dev";
4321
- function err(line) {
6590
+ function err3(line) {
4322
6591
  process.stderr.write(`maind: ${line}
4323
6592
  `);
4324
6593
  }
4325
6594
  function repoRoot() {
4326
6595
  try {
4327
- return execFileSync7("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
6596
+ return execFileSync8("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
4328
6597
  } catch {
4329
6598
  return process.cwd();
4330
6599
  }
@@ -4339,7 +6608,7 @@ function makeSync(json) {
4339
6608
  auth = resolveAuth();
4340
6609
  } catch (e) {
4341
6610
  if (e instanceof MissingApiKeyError) {
4342
- err(e.message);
6611
+ err3(e.message);
4343
6612
  return null;
4344
6613
  }
4345
6614
  throw e;
@@ -4353,11 +6622,11 @@ function makeSync(json) {
4353
6622
  onProgress: json ? void 0 : (p) => process.stderr.write(` ${p.phase}: ${p.done}/${p.total}\r`)
4354
6623
  });
4355
6624
  } catch (e) {
4356
- err(e instanceof Error ? e.message : String(e));
6625
+ err3(e instanceof Error ? e.message : String(e));
4357
6626
  return null;
4358
6627
  }
4359
6628
  }
4360
- function msg6(e) {
6629
+ function msg7(e) {
4361
6630
  return e instanceof Error ? e.message : String(e);
4362
6631
  }
4363
6632
  function parseSyncArgs(argv) {
@@ -4381,10 +6650,10 @@ async function runSync(opts) {
4381
6650
  }
4382
6651
  } catch (e) {
4383
6652
  if (e instanceof SyncError && e.code === "consent_required") {
4384
- err("codebase cloud-sync is not enabled \u2014 run `maind sync enable` first.");
6653
+ err3("codebase cloud-sync is not enabled \u2014 run `maind sync enable` first.");
4385
6654
  return 1;
4386
6655
  }
4387
- err(`sync failed: ${msg6(e)}`);
6656
+ err3(`sync failed: ${msg7(e)}`);
4388
6657
  return 1;
4389
6658
  } finally {
4390
6659
  await sync.close().catch(() => {
@@ -4420,7 +6689,7 @@ cloud: ${remote.indexed ? `${remote.node_count ?? "?"} symbols \xB7 ${remote.ed
4420
6689
  async function push(sync, json) {
4421
6690
  const local = await sync.localStatus();
4422
6691
  if (!local.indexed || local.nodeCount === 0) {
4423
- err("no local index found \u2014 run `maind index` first.");
6692
+ err3("no local index found \u2014 run `maind index` first.");
4424
6693
  return 1;
4425
6694
  }
4426
6695
  const res = await sync.push();
@@ -4451,6 +6720,8 @@ Usage:
4451
6720
  maind sync Push the local index to the cloud (opt-in; hashed rows only)
4452
6721
  maind sync status [--json] Show local vs cloud sync state
4453
6722
  maind sync enable|disable Opt in/out of codebase cloud-sync (consent)
6723
+ maind sessions backfill [.] Recompute session analytics for transcripts already on disk
6724
+ maind launch <agent> [..] Plug&play: local Switchyard model-routing proxy + agent behind it (v1: claude)
4454
6725
  maind review [options] Provenance-backed review findings for a change set (advisory by default)
4455
6726
  maind ci-report [options] Report a CI failure as a draft-lesson candidate (on failure)
4456
6727
  maind ci-result [options] Report a build outcome + served manifest (end of job)
@@ -4478,6 +6749,24 @@ review options (advisory by default \u2014 exits 0 unless --gate + --fail-on):
4478
6749
  --project-key <hex> Repo project_key override (default: auto-detect from git)
4479
6750
  --mcp-url <url> Override MCP endpoint (default $MAIND_MCP_URL or production)
4480
6751
 
6752
+ launch options (v1 agent: claude; codex/openclaw planned \u2014 ADR-272):
6753
+ --dry-run Print the generated routes TOML (stdout) + config path, detection
6754
+ state and the exact spawn command (stderr); installs, writes and
6755
+ spawns nothing
6756
+ --json With --dry-run: machine-readable plan JSON on stdout
6757
+ --config <path> Use an existing routes.toml \u2014 skips generation + template fetch
6758
+ --route <name> Route name for --model and the generated [routes.<name>] (default: switchyard)
6759
+ --direct Skip the proxy \u2014 spawn the agent directly (no routing, visible banner)
6760
+ --no-fallback On switchyard startup failure exit 1 instead of degrading to a
6761
+ direct agent spawn
6762
+ --no-install Never auto-install; print the install one-liner and exit 1 when missing
6763
+ --mcp-url <url> Override MCP endpoint for the curated-template fetch
6764
+ Prereqs: installs nemo-switchyard[cli] via uv automatically when uv exists \u2014 never
6765
+ installs uv itself (prints the official instruction instead). The generated config
6766
+ lands in <repoRoot>/.maind/switchyard-routes.toml (or the maind user config dir
6767
+ outside a repo). Backend keys (ANTHROPIC_API_KEY / OPENROUTER_API_KEY) are read
6768
+ from the environment by the proxy at runtime \u2014 never written into the TOML.
6769
+
4481
6770
  git-context options (run at session-start; reads local git + gh, all fail-open):
4482
6771
  --out <path> Briefing markdown output (default .maind/git-context.md)
4483
6772
  --manifest <path> Optional structured JSON output (git_context manifest)
@@ -4503,6 +6792,16 @@ ci-result options (run at the end of a job, success OR failure):
4503
6792
  --run-url <url> Link to the CI run (default derived from GitHub env)
4504
6793
  --result-url <url> Override ingest endpoint (default $MAIND_RESULT_URL or prod)
4505
6794
 
6795
+ sessions backfill options:
6796
+ --dry-run List what WOULD be uploaded (incl. the session titles) and stop.
6797
+ Recommended before the first real run \u2014 a backfill covers every
6798
+ project on this machine, and titles are content-derived.
6799
+ --since <YYYY-MM-DD> Only sessions whose last turn is on/after this date
6800
+ --json Machine-readable result
6801
+ Idempotent: the server upserts on (user_id, session_id), so re-running is safe.
6802
+ Titles are only stored if 'Session titles' is enabled in the consent center;
6803
+ otherwise the numbers land and the title is dropped server-side.
6804
+
4506
6805
  context options:
4507
6806
  --stack <a,b> Stack hint (seeds the platforms filter), e.g. nextjs,supabase
4508
6807
  --platforms <a,b> Explicit platforms filter (overrides --stack)
@@ -4546,6 +6845,9 @@ async function main() {
4546
6845
  process.stdout.write(key + "\n");
4547
6846
  return 0;
4548
6847
  }
6848
+ if (cmd === "launch") {
6849
+ return runLaunch(parseLaunchArgs(argv.slice(1)));
6850
+ }
4549
6851
  const usesOrt = cmd === "index" && argv.includes("--embed") || cmd === "index-semantic";
4550
6852
  if (usesOrt && !isOrtWorker(argv)) {
4551
6853
  return runOrtIsolated(argv);
@@ -4568,6 +6870,13 @@ async function main() {
4568
6870
  if (cmd === "sync") {
4569
6871
  return runSync(parseSyncArgs(argv.slice(1)));
4570
6872
  }
6873
+ if (cmd === "sessions") {
6874
+ if (argv[1] !== "backfill") {
6875
+ process.stderr.write("maind: usage: maind sessions backfill [--dry-run] [--since YYYY-MM-DD] [--json]\n");
6876
+ return 2;
6877
+ }
6878
+ return runSessionsBackfill(parseSessionsArgs(argv.slice(2)));
6879
+ }
4571
6880
  if (cmd === "review") {
4572
6881
  return runReview(parseReviewArgs(argv.slice(1)));
4573
6882
  }
@@ -4581,8 +6890,271 @@ async function main() {
4581
6890
  `);
4582
6891
  return 2;
4583
6892
  }
4584
- main().then((code) => process.exit(code)).catch((err2) => {
4585
- process.stderr.write(`maind: fatal: ${err2 instanceof Error ? err2.stack ?? err2.message : String(err2)}
6893
+ main().then((code) => process.exit(code)).catch((err4) => {
6894
+ process.stderr.write(`maind: fatal: ${err4 instanceof Error ? err4.stack ?? err4.message : String(err4)}
4586
6895
  `);
4587
6896
  process.exit(1);
4588
6897
  });
6898
+ /*! Bundled license information:
6899
+
6900
+ smol-toml/dist/error.js:
6901
+ (*!
6902
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
6903
+ * SPDX-License-Identifier: BSD-3-Clause
6904
+ *
6905
+ * Redistribution and use in source and binary forms, with or without
6906
+ * modification, are permitted provided that the following conditions are met:
6907
+ *
6908
+ * 1. Redistributions of source code must retain the above copyright notice, this
6909
+ * list of conditions and the following disclaimer.
6910
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
6911
+ * this list of conditions and the following disclaimer in the
6912
+ * documentation and/or other materials provided with the distribution.
6913
+ * 3. Neither the name of the copyright holder nor the names of its contributors
6914
+ * may be used to endorse or promote products derived from this software without
6915
+ * specific prior written permission.
6916
+ *
6917
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
6918
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
6919
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
6920
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
6921
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6922
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
6923
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
6924
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
6925
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
6926
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6927
+ *)
6928
+
6929
+ smol-toml/dist/util.js:
6930
+ (*!
6931
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
6932
+ * SPDX-License-Identifier: BSD-3-Clause
6933
+ *
6934
+ * Redistribution and use in source and binary forms, with or without
6935
+ * modification, are permitted provided that the following conditions are met:
6936
+ *
6937
+ * 1. Redistributions of source code must retain the above copyright notice, this
6938
+ * list of conditions and the following disclaimer.
6939
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
6940
+ * this list of conditions and the following disclaimer in the
6941
+ * documentation and/or other materials provided with the distribution.
6942
+ * 3. Neither the name of the copyright holder nor the names of its contributors
6943
+ * may be used to endorse or promote products derived from this software without
6944
+ * specific prior written permission.
6945
+ *
6946
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
6947
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
6948
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
6949
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
6950
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6951
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
6952
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
6953
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
6954
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
6955
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6956
+ *)
6957
+
6958
+ smol-toml/dist/date.js:
6959
+ (*!
6960
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
6961
+ * SPDX-License-Identifier: BSD-3-Clause
6962
+ *
6963
+ * Redistribution and use in source and binary forms, with or without
6964
+ * modification, are permitted provided that the following conditions are met:
6965
+ *
6966
+ * 1. Redistributions of source code must retain the above copyright notice, this
6967
+ * list of conditions and the following disclaimer.
6968
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
6969
+ * this list of conditions and the following disclaimer in the
6970
+ * documentation and/or other materials provided with the distribution.
6971
+ * 3. Neither the name of the copyright holder nor the names of its contributors
6972
+ * may be used to endorse or promote products derived from this software without
6973
+ * specific prior written permission.
6974
+ *
6975
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
6976
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
6977
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
6978
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
6979
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6980
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
6981
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
6982
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
6983
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
6984
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6985
+ *)
6986
+
6987
+ smol-toml/dist/primitive.js:
6988
+ (*!
6989
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
6990
+ * SPDX-License-Identifier: BSD-3-Clause
6991
+ *
6992
+ * Redistribution and use in source and binary forms, with or without
6993
+ * modification, are permitted provided that the following conditions are met:
6994
+ *
6995
+ * 1. Redistributions of source code must retain the above copyright notice, this
6996
+ * list of conditions and the following disclaimer.
6997
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
6998
+ * this list of conditions and the following disclaimer in the
6999
+ * documentation and/or other materials provided with the distribution.
7000
+ * 3. Neither the name of the copyright holder nor the names of its contributors
7001
+ * may be used to endorse or promote products derived from this software without
7002
+ * specific prior written permission.
7003
+ *
7004
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7005
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7006
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7007
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
7008
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7009
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
7010
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
7011
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
7012
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
7013
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7014
+ *)
7015
+
7016
+ smol-toml/dist/extract.js:
7017
+ (*!
7018
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
7019
+ * SPDX-License-Identifier: BSD-3-Clause
7020
+ *
7021
+ * Redistribution and use in source and binary forms, with or without
7022
+ * modification, are permitted provided that the following conditions are met:
7023
+ *
7024
+ * 1. Redistributions of source code must retain the above copyright notice, this
7025
+ * list of conditions and the following disclaimer.
7026
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
7027
+ * this list of conditions and the following disclaimer in the
7028
+ * documentation and/or other materials provided with the distribution.
7029
+ * 3. Neither the name of the copyright holder nor the names of its contributors
7030
+ * may be used to endorse or promote products derived from this software without
7031
+ * specific prior written permission.
7032
+ *
7033
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7034
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7035
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7036
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
7037
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7038
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
7039
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
7040
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
7041
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
7042
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7043
+ *)
7044
+
7045
+ smol-toml/dist/struct.js:
7046
+ (*!
7047
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
7048
+ * SPDX-License-Identifier: BSD-3-Clause
7049
+ *
7050
+ * Redistribution and use in source and binary forms, with or without
7051
+ * modification, are permitted provided that the following conditions are met:
7052
+ *
7053
+ * 1. Redistributions of source code must retain the above copyright notice, this
7054
+ * list of conditions and the following disclaimer.
7055
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
7056
+ * this list of conditions and the following disclaimer in the
7057
+ * documentation and/or other materials provided with the distribution.
7058
+ * 3. Neither the name of the copyright holder nor the names of its contributors
7059
+ * may be used to endorse or promote products derived from this software without
7060
+ * specific prior written permission.
7061
+ *
7062
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7063
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7064
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7065
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
7066
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7067
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
7068
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
7069
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
7070
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
7071
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7072
+ *)
7073
+
7074
+ smol-toml/dist/parse.js:
7075
+ (*!
7076
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
7077
+ * SPDX-License-Identifier: BSD-3-Clause
7078
+ *
7079
+ * Redistribution and use in source and binary forms, with or without
7080
+ * modification, are permitted provided that the following conditions are met:
7081
+ *
7082
+ * 1. Redistributions of source code must retain the above copyright notice, this
7083
+ * list of conditions and the following disclaimer.
7084
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
7085
+ * this list of conditions and the following disclaimer in the
7086
+ * documentation and/or other materials provided with the distribution.
7087
+ * 3. Neither the name of the copyright holder nor the names of its contributors
7088
+ * may be used to endorse or promote products derived from this software without
7089
+ * specific prior written permission.
7090
+ *
7091
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7092
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7093
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7094
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
7095
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7096
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
7097
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
7098
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
7099
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
7100
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7101
+ *)
7102
+
7103
+ smol-toml/dist/stringify.js:
7104
+ (*!
7105
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
7106
+ * SPDX-License-Identifier: BSD-3-Clause
7107
+ *
7108
+ * Redistribution and use in source and binary forms, with or without
7109
+ * modification, are permitted provided that the following conditions are met:
7110
+ *
7111
+ * 1. Redistributions of source code must retain the above copyright notice, this
7112
+ * list of conditions and the following disclaimer.
7113
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
7114
+ * this list of conditions and the following disclaimer in the
7115
+ * documentation and/or other materials provided with the distribution.
7116
+ * 3. Neither the name of the copyright holder nor the names of its contributors
7117
+ * may be used to endorse or promote products derived from this software without
7118
+ * specific prior written permission.
7119
+ *
7120
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7121
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7122
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7123
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
7124
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7125
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
7126
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
7127
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
7128
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
7129
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7130
+ *)
7131
+
7132
+ smol-toml/dist/index.js:
7133
+ (*!
7134
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
7135
+ * SPDX-License-Identifier: BSD-3-Clause
7136
+ *
7137
+ * Redistribution and use in source and binary forms, with or without
7138
+ * modification, are permitted provided that the following conditions are met:
7139
+ *
7140
+ * 1. Redistributions of source code must retain the above copyright notice, this
7141
+ * list of conditions and the following disclaimer.
7142
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
7143
+ * this list of conditions and the following disclaimer in the
7144
+ * documentation and/or other materials provided with the distribution.
7145
+ * 3. Neither the name of the copyright holder nor the names of its contributors
7146
+ * may be used to endorse or promote products derived from this software without
7147
+ * specific prior written permission.
7148
+ *
7149
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7150
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7151
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7152
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
7153
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7154
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
7155
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
7156
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
7157
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
7158
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7159
+ *)
7160
+ */