@maind-dev/cli 0.8.1 → 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}
@@ -1016,15 +1016,15 @@ function warn4(line) {
1016
1016
  }
1017
1017
 
1018
1018
  // src/index-cmd.ts
1019
- import { execFileSync as execFileSync4, spawnSync } from "node:child_process";
1020
- import { existsSync as existsSync2, mkdtempSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
1019
+ import { execFileSync as execFileSync5, spawnSync } from "node:child_process";
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 posix4 } 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";
@@ -1431,8 +1431,8 @@ var TreeSitterParser = class {
1431
1431
  };
1432
1432
 
1433
1433
  // ../../packages/code-index/build/query/engine.js
1434
- import { readFileSync as readFileSync4 } from "node:fs";
1435
- import { posix as posix3 } from "node:path";
1434
+ import { readFileSync as readFileSync5 } from "node:fs";
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,
@@ -2015,6 +2020,669 @@ function readFileHistory(repoRoot2, paths, opts = {}) {
2015
2020
  };
2016
2021
  }
2017
2022
 
2023
+ // ../../packages/code-index/build/repo-health/divergence.js
2024
+ import { execFileSync as execFileSync4 } from "node:child_process";
2025
+ import { existsSync as existsSync2, readFileSync as readFileSync4 } from "node:fs";
2026
+ import { posix as posix3 } from "node:path";
2027
+ var DEFAULT_WORKTREE_PROBE_PATHS = [
2028
+ "supabase/config.toml",
2029
+ "supabase/.temp",
2030
+ ".env"
2031
+ ];
2032
+ var WORKTREE_REQUIREMENTS_RELPATH = ".maind/worktree-requirements";
2033
+ function computeDivergence(facts) {
2034
+ const out2 = [];
2035
+ if (!facts.isRepo)
2036
+ return out2;
2037
+ const onFeatureBranch = facts.branch != null && facts.defaultBranch != null && facts.branch !== facts.defaultBranch && !facts.detachedHead;
2038
+ if (facts.detachedHead) {
2039
+ out2.push({
2040
+ id: "worktree.detached-head",
2041
+ status: "warn",
2042
+ scope: "worktree",
2043
+ title: "Detached HEAD",
2044
+ detail: "HEAD points at a commit, not a branch \u2014 commits here are easily lost.",
2045
+ hint: "git switch -c <branch> to keep this work, or checkout an existing branch."
2046
+ });
2047
+ }
2048
+ if (onFeatureBranch) {
2049
+ if (facts.upstream == null) {
2050
+ out2.push({
2051
+ id: "branch.no-upstream",
2052
+ status: "open",
2053
+ scope: "branch",
2054
+ title: "No upstream set",
2055
+ detail: `'${facts.branch}' tracks nothing \u2014 a plain 'git push' goes nowhere or to the wrong ref, with no error.`,
2056
+ hint: `git push -u origin ${facts.branch}`
2057
+ });
2058
+ } else {
2059
+ const upstreamBranch = facts.upstream.split("/").slice(1).join("/");
2060
+ if (upstreamBranch && upstreamBranch !== facts.branch) {
2061
+ out2.push({
2062
+ id: "branch.upstream-mismatch",
2063
+ status: "open",
2064
+ scope: "branch",
2065
+ title: "Upstream points at a different branch",
2066
+ detail: `'${facts.branch}' tracks '${facts.upstream}' \u2014 pushes land on the wrong remote branch silently.`,
2067
+ hint: `git branch --set-upstream-to=origin/${facts.branch} (then push -u)`
2068
+ });
2069
+ }
2070
+ if (facts.remoteTrackingExists === false) {
2071
+ out2.push({
2072
+ id: "branch.unpushed-missing-remote",
2073
+ status: "open",
2074
+ scope: "branch",
2075
+ title: "Branch not on remote",
2076
+ detail: `'${facts.branch}' has an upstream configured but no matching remote-tracking ref \u2014 the branch was never pushed.`,
2077
+ hint: `git push -u origin ${facts.branch}`
2078
+ });
2079
+ }
2080
+ }
2081
+ }
2082
+ const behind = facts.behind ?? 0;
2083
+ const ahead = facts.ahead ?? 0;
2084
+ if (behind > 0) {
2085
+ out2.push({
2086
+ id: "branch.behind",
2087
+ status: "info",
2088
+ scope: "branch",
2089
+ title: "Behind base",
2090
+ detail: `${behind} commit(s) behind origin/${facts.defaultBranch}${ahead > 0 ? ` (and ${ahead} ahead)` : ""} \u2014 rebase/merge base before merging to avoid a stale PR.`,
2091
+ hint: `git fetch && git rebase origin/${facts.defaultBranch}`
2092
+ });
2093
+ }
2094
+ if (facts.upstream != null && facts.headMatchesUpstream === false) {
2095
+ out2.push({
2096
+ id: "branch.unpushed-commits",
2097
+ status: "warn",
2098
+ scope: "branch",
2099
+ title: "Unpushed commits",
2100
+ detail: `Local HEAD is ahead of ${facts.upstream} \u2014 commits exist only on this machine.`,
2101
+ hint: "git push"
2102
+ });
2103
+ }
2104
+ if (facts.unmergedMigrations.length > 0) {
2105
+ const n = facts.unmergedMigrations.length;
2106
+ out2.push({
2107
+ id: "branch.unmerged-migrations",
2108
+ status: "info",
2109
+ scope: "branch",
2110
+ title: "Migrations not yet on the base branch",
2111
+ detail: `${n} migration file(s) added on '${facts.branch}' are not on origin/${facts.defaultBranch} yet \u2014 a cloud DB push run from ${facts.defaultBranch} will NOT include them until this branch is merged.`,
2112
+ hint: `Merge to ${facts.defaultBranch} first (promote), then run 'supabase db push --linked' from ${facts.defaultBranch}.`
2113
+ });
2114
+ }
2115
+ if (facts.isSharedMainTree && facts.otherWorktrees > 0 && facts.uncommitted > 0 && !onFeatureBranch) {
2116
+ out2.push({
2117
+ id: "worktree.direct-on-shared-base",
2118
+ status: "warn",
2119
+ scope: "worktree",
2120
+ title: "Uncommitted work on the shared base tree",
2121
+ detail: `You are editing the shared '${facts.defaultBranch}' checkout while ${facts.otherWorktrees} feature worktree(s) exist \u2014 the workflow reaches base only via PR merge.`,
2122
+ hint: "Move the work into a feature worktree (scripts/feature.sh start <name>)."
2123
+ });
2124
+ }
2125
+ if (facts.inProgressOp) {
2126
+ out2.push({
2127
+ id: "worktree.in-progress-op",
2128
+ status: "warn",
2129
+ scope: "worktree",
2130
+ title: `${facts.inProgressOp} in progress`,
2131
+ detail: `A ${facts.inProgressOp} is mid-flight \u2014 git operations behave unexpectedly until it is finished or aborted.`,
2132
+ hint: `git ${facts.inProgressOp} --continue | --abort`
2133
+ });
2134
+ }
2135
+ if (facts.worktreeParity && facts.worktreeParity.missing.length > 0) {
2136
+ const miss = facts.worktreeParity.missing;
2137
+ out2.push({
2138
+ id: "worktree.parity-missing",
2139
+ status: "open",
2140
+ scope: "worktree",
2141
+ title: "Tooling files missing vs main checkout",
2142
+ detail: `${miss.join(", ")} exist in the main checkout but not here \u2014 tools (e.g. supabase) may silently target the wrong/no project and report success.`,
2143
+ hint: `Copy the missing file(s) from ${facts.worktreeParity.mainWorktreePath}, or declare them in ${WORKTREE_REQUIREMENTS_RELPATH}.`
2144
+ });
2145
+ }
2146
+ if (facts.release?.allRed) {
2147
+ out2.push({
2148
+ id: "release.all-checks-red",
2149
+ status: "warn",
2150
+ scope: "release",
2151
+ title: "All checks failing \u2014 suspect account/billing/quota",
2152
+ detail: `All ${facts.release.totalChecks} check(s) on the current PR failed. When every check goes red at once, the hypothesis is account/billing/quota, not your code (e.g. Actions spending-limit blocks the whole repo, including publisher jobs).`,
2153
+ hint: "Check the CI provider's billing/spending status before debugging code."
2154
+ });
2155
+ }
2156
+ return sortFindings(out2);
2157
+ }
2158
+ var STATUS_ORDER = { open: 0, warn: 1, info: 2, passed: 3 };
2159
+ function sortFindings(f) {
2160
+ return [...f].sort((a, b) => STATUS_ORDER[a.status] - STATUS_ORDER[b.status] || a.id.localeCompare(b.id));
2161
+ }
2162
+ function isHealthy(findings) {
2163
+ return !findings.some((x) => x.status === "open" || x.status === "warn");
2164
+ }
2165
+ function trimOrNull(s) {
2166
+ const t = s?.trim();
2167
+ return t ? t : null;
2168
+ }
2169
+ function resolveDefaultBranch2(repoRoot2) {
2170
+ const sym = trimOrNull(runGit(repoRoot2, ["symbolic-ref", "--quiet", "refs/remotes/origin/HEAD"]));
2171
+ if (sym) {
2172
+ const m = /refs\/remotes\/origin\/(.+)$/.exec(sym);
2173
+ if (m)
2174
+ return m[1];
2175
+ }
2176
+ for (const cand of ["main", "master"]) {
2177
+ if (runGit(repoRoot2, ["rev-parse", "--verify", "--quiet", `origin/${cand}`]) !== null)
2178
+ return cand;
2179
+ }
2180
+ return null;
2181
+ }
2182
+ function detectInProgressOp(repoRoot2) {
2183
+ const gitDir = trimOrNull(runGit(repoRoot2, ["rev-parse", "--git-dir"]));
2184
+ if (!gitDir)
2185
+ return null;
2186
+ const base = posix3.isAbsolute(gitDir) ? gitDir : posix3.join(repoRoot2, gitDir);
2187
+ if (existsSync2(posix3.join(base, "rebase-merge")) || existsSync2(posix3.join(base, "rebase-apply"))) {
2188
+ return "rebase";
2189
+ }
2190
+ if (existsSync2(posix3.join(base, "MERGE_HEAD")))
2191
+ return "merge";
2192
+ if (existsSync2(posix3.join(base, "CHERRY_PICK_HEAD")))
2193
+ return "cherry-pick";
2194
+ return null;
2195
+ }
2196
+ function parseWorktrees(raw) {
2197
+ const out2 = [];
2198
+ let cur = null;
2199
+ const flush = () => {
2200
+ if (cur?.path)
2201
+ out2.push({ path: cur.path, branch: cur.branch, detached: cur.detached ?? false });
2202
+ };
2203
+ for (const line of raw.split("\n")) {
2204
+ if (line.startsWith("worktree ")) {
2205
+ flush();
2206
+ cur = { path: line.slice("worktree ".length).trim(), detached: false };
2207
+ } else if (!cur) {
2208
+ continue;
2209
+ } else if (line.startsWith("branch ")) {
2210
+ cur.branch = line.slice("branch ".length).trim().replace(/^refs\/heads\//, "");
2211
+ } else if (line.trim() === "detached") {
2212
+ cur.detached = true;
2213
+ }
2214
+ }
2215
+ flush();
2216
+ return out2;
2217
+ }
2218
+ function readWorktreeRequirements(repoRoot2) {
2219
+ const p = posix3.join(repoRoot2, WORKTREE_REQUIREMENTS_RELPATH);
2220
+ if (!existsSync2(p))
2221
+ return [];
2222
+ try {
2223
+ return readFileSync4(p, "utf8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
2224
+ } catch {
2225
+ return [];
2226
+ }
2227
+ }
2228
+ async function gatherRepoHealthFacts(repoRoot2, opts = {}) {
2229
+ const inside = trimOrNull(runGit(repoRoot2, ["rev-parse", "--is-inside-work-tree"]));
2230
+ if (inside !== "true") {
2231
+ return {
2232
+ isRepo: false,
2233
+ branch: null,
2234
+ defaultBranch: null,
2235
+ upstream: null,
2236
+ headMatchesUpstream: null,
2237
+ remoteTrackingExists: null,
2238
+ ahead: null,
2239
+ behind: null,
2240
+ uncommitted: 0,
2241
+ detachedHead: false,
2242
+ inProgressOp: null,
2243
+ isSharedMainTree: false,
2244
+ otherWorktrees: 0,
2245
+ worktreeParity: null,
2246
+ release: null,
2247
+ unmergedMigrations: []
2248
+ };
2249
+ }
2250
+ const branchRaw = trimOrNull(runGit(repoRoot2, ["rev-parse", "--abbrev-ref", "HEAD"]));
2251
+ const detachedHead = branchRaw === "HEAD";
2252
+ const branch = detachedHead ? null : branchRaw;
2253
+ const defaultBranch = resolveDefaultBranch2(repoRoot2);
2254
+ const upstream = trimOrNull(runGit(repoRoot2, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]));
2255
+ let headMatchesUpstream = null;
2256
+ let remoteTrackingExists = null;
2257
+ if (upstream) {
2258
+ const head = trimOrNull(runGit(repoRoot2, ["rev-parse", "HEAD"]));
2259
+ const up = trimOrNull(runGit(repoRoot2, ["rev-parse", "@{u}"]));
2260
+ headMatchesUpstream = head != null && up != null ? head === up : null;
2261
+ remoteTrackingExists = runGit(repoRoot2, ["rev-parse", "--verify", "--quiet", `refs/remotes/${upstream}`]) !== null;
2262
+ }
2263
+ let ahead = null;
2264
+ let behind = null;
2265
+ if (defaultBranch) {
2266
+ const counts = trimOrNull(runGit(repoRoot2, ["rev-list", "--left-right", "--count", `origin/${defaultBranch}...HEAD`]));
2267
+ if (counts) {
2268
+ const [b, a] = counts.split(/\s+/).map((n) => Number.parseInt(n, 10));
2269
+ if (Number.isFinite(b))
2270
+ behind = b;
2271
+ if (Number.isFinite(a))
2272
+ ahead = a;
2273
+ }
2274
+ }
2275
+ const porcelain = runGit(repoRoot2, ["status", "--porcelain"]) ?? "";
2276
+ const uncommitted = porcelain.split("\n").filter((l) => l.trim().length > 0).length;
2277
+ const inProgressOp = detectInProgressOp(repoRoot2);
2278
+ const toplevel = trimOrNull(runGit(repoRoot2, ["rev-parse", "--show-toplevel"])) ?? repoRoot2;
2279
+ const worktrees = parseWorktrees(runGit(repoRoot2, ["worktree", "list", "--porcelain"]) ?? "");
2280
+ const mainWorktree = worktrees[0]?.path ?? null;
2281
+ const isSharedMainTree = mainWorktree != null && samePath(mainWorktree, toplevel);
2282
+ const otherWorktrees = Math.max(0, worktrees.length - 1);
2283
+ let worktreeParity = null;
2284
+ if (mainWorktree && !samePath(mainWorktree, toplevel)) {
2285
+ const probes = [
2286
+ ...DEFAULT_WORKTREE_PROBE_PATHS,
2287
+ ...readWorktreeRequirements(toplevel),
2288
+ ...opts.probePaths ?? []
2289
+ ];
2290
+ const missing = probes.filter((rel) => existsSync2(posix3.join(mainWorktree, rel)) && !existsSync2(posix3.join(toplevel, rel)));
2291
+ worktreeParity = {
2292
+ mainWorktreePath: mainWorktree,
2293
+ currentWorktreePath: toplevel,
2294
+ missing: [...new Set(missing)]
2295
+ };
2296
+ }
2297
+ const release = opts.includeRelease === false ? null : gatherReleaseFact(repoRoot2, branch);
2298
+ let unmergedMigrations = [];
2299
+ if (defaultBranch) {
2300
+ const raw = runGit(repoRoot2, [
2301
+ "diff",
2302
+ "--name-only",
2303
+ "--diff-filter=A",
2304
+ `origin/${defaultBranch}...HEAD`,
2305
+ "--",
2306
+ "*supabase/migrations/*.sql"
2307
+ ]);
2308
+ unmergedMigrations = (raw ?? "").split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
2309
+ }
2310
+ return {
2311
+ isRepo: true,
2312
+ branch,
2313
+ defaultBranch,
2314
+ upstream,
2315
+ headMatchesUpstream,
2316
+ remoteTrackingExists,
2317
+ ahead,
2318
+ behind,
2319
+ uncommitted,
2320
+ detachedHead,
2321
+ inProgressOp,
2322
+ isSharedMainTree,
2323
+ otherWorktrees,
2324
+ worktreeParity,
2325
+ release,
2326
+ unmergedMigrations
2327
+ };
2328
+ }
2329
+ function samePath(a, b) {
2330
+ const norm = (p) => p.replace(/\/+$/, "");
2331
+ return norm(a) === norm(b);
2332
+ }
2333
+ function gatherReleaseFact(repoRoot2, branch) {
2334
+ if (!branch)
2335
+ return null;
2336
+ const raw = runGh(repoRoot2, ["pr", "checks", branch, "--json", "bucket"]);
2337
+ if (raw == null)
2338
+ return null;
2339
+ let rows;
2340
+ try {
2341
+ rows = JSON.parse(raw);
2342
+ } catch {
2343
+ return null;
2344
+ }
2345
+ if (rows.length === 0)
2346
+ return { prNumber: null, totalChecks: 0, failedChecks: 0, allRed: false };
2347
+ const failed = rows.filter((r) => r.bucket === "fail").length;
2348
+ return {
2349
+ prNumber: null,
2350
+ totalChecks: rows.length,
2351
+ failedChecks: failed,
2352
+ allRed: failed === rows.length && rows.length > 0
2353
+ };
2354
+ }
2355
+ function runGh(repoRoot2, args) {
2356
+ try {
2357
+ return execFileSync4("gh", args, {
2358
+ cwd: repoRoot2,
2359
+ encoding: "utf8",
2360
+ stdio: ["ignore", "pipe", "ignore"],
2361
+ timeout: 1e4,
2362
+ maxBuffer: 8 * 1024 * 1024
2363
+ });
2364
+ } catch {
2365
+ return null;
2366
+ }
2367
+ }
2368
+ async function checkDivergence(repoRoot2, opts = {}) {
2369
+ const facts = await gatherRepoHealthFacts(repoRoot2, opts);
2370
+ const findings = computeDivergence(facts);
2371
+ return { ok: isHealthy(findings), findings, facts };
2372
+ }
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
+
2018
2686
  // ../../packages/code-index/build/review/risk-rules.js
2019
2687
  var RISK_RULES = [
2020
2688
  {
@@ -2339,13 +3007,13 @@ var CodeIndexEngine = class {
2339
3007
  await this.store.init();
2340
3008
  const limit = clampLimit(opts.limit, 20, 100);
2341
3009
  const hits = await this.store.searchFts(query, opts.kind ?? null, limit);
2342
- const out = [];
3010
+ const out2 = [];
2343
3011
  for (const h of hits) {
2344
3012
  const n = await this.store.getNode(h.nodeKey);
2345
3013
  if (n)
2346
- out.push({ ...toHit(n), score: h.score });
3014
+ out2.push({ ...toHit(n), score: h.score });
2347
3015
  }
2348
- return out;
3016
+ return out2;
2349
3017
  }
2350
3018
  async semanticQuery(query, opts = {}) {
2351
3019
  await this.store.init();
@@ -2355,13 +3023,13 @@ var CodeIndexEngine = class {
2355
3023
  const qvec = await this.embedder.embedQuery(query);
2356
3024
  const all = await this.store.allEmbeddings();
2357
3025
  const scored = all.map((e) => ({ nodeKey: e.nodeKey, score: cosineSimilarity(qvec, e.vec) })).sort((a, b) => b.score - a.score).slice(0, limit);
2358
- const out = [];
3026
+ const out2 = [];
2359
3027
  for (const s of scored) {
2360
3028
  const n = await this.store.getNode(s.nodeKey);
2361
3029
  if (n)
2362
- out.push({ ...toHit(n), score: s.score });
3030
+ out2.push({ ...toHit(n), score: s.score });
2363
3031
  }
2364
- return out;
3032
+ return out2;
2365
3033
  }
2366
3034
  async tracePath(nodeKey, opts = {}) {
2367
3035
  await this.store.init();
@@ -2403,7 +3071,7 @@ var CodeIndexEngine = class {
2403
3071
  return null;
2404
3072
  let source = "";
2405
3073
  try {
2406
- const full = readFileSync4(posix3.join(this.repoRoot, n.filePath), "utf8");
3074
+ const full = readFileSync5(posix5.join(this.repoRoot, n.filePath), "utf8");
2407
3075
  source = full.slice(n.startByte, n.endByte);
2408
3076
  } catch {
2409
3077
  return null;
@@ -2471,6 +3139,15 @@ var CodeIndexEngine = class {
2471
3139
  }
2472
3140
  return readFileHistory(this.repoRoot, paths, opts);
2473
3141
  }
3142
+ async checkDivergence(opts = {}) {
3143
+ return checkDivergence(this.repoRoot, opts);
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
+ }
2474
3151
  async reviewChanges(opts = {}) {
2475
3152
  await this.store.init();
2476
3153
  const rawPaths = enumerateChangedPaths(this.repoRoot, opts);
@@ -2534,7 +3211,7 @@ var CodeIndexEngine = class {
2534
3211
  }
2535
3212
  readRepoFile(relPath) {
2536
3213
  try {
2537
- return readFileSync4(posix3.join(this.repoRoot, relPath), "utf8");
3214
+ return readFileSync5(posix5.join(this.repoRoot, relPath), "utf8");
2538
3215
  } catch {
2539
3216
  return null;
2540
3217
  }
@@ -2593,8 +3270,109 @@ function classifyReviewSymbol(row, fileStatus, fh, stale) {
2593
3270
  return { ...row, change: "co_located", changedLineCount: 0, staleIndex: false };
2594
3271
  }
2595
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
+
2596
3375
  // ../../packages/code-index/build/store/node-sqlite.js
2597
- import Database from "better-sqlite3";
2598
3376
  function toNodeRow(r) {
2599
3377
  return {
2600
3378
  nodeKey: r.node_key,
@@ -2614,10 +3392,12 @@ function toNodeRow(r) {
2614
3392
  var CHUNK = 400;
2615
3393
  var NodeSqliteStore = class {
2616
3394
  db;
3395
+ /** Welches Backend diesen Store trägt — für Diagnose/Status-Ausgaben. */
3396
+ backend;
2617
3397
  constructor(dbPath) {
2618
- this.db = new Database(dbPath);
2619
- this.db.pragma("journal_mode = WAL");
2620
- this.db.pragma("foreign_keys = ON");
3398
+ const opened = openSqliteDatabase(dbPath);
3399
+ this.db = opened.db;
3400
+ this.backend = opened.backend;
2621
3401
  }
2622
3402
  async init() {
2623
3403
  this.db.exec(SCHEMA_SQL);
@@ -2748,7 +3528,7 @@ var NodeSqliteStore = class {
2748
3528
  }
2749
3529
  async pruneOrphanEmbeddings() {
2750
3530
  const info = this.db.prepare("DELETE FROM embeddings WHERE node_key NOT IN (SELECT node_key FROM nodes)").run();
2751
- return info.changes;
3531
+ return Number(info.changes);
2752
3532
  }
2753
3533
  async countEmbeddings() {
2754
3534
  return this.db.prepare("SELECT COUNT(*) AS c FROM embeddings").get().c;
@@ -3090,10 +3870,10 @@ var CodeIndexSync = class {
3090
3870
  };
3091
3871
  var EMBED_CHUNK = 250;
3092
3872
  function chunkArray(items, size) {
3093
- const out = [];
3873
+ const out2 = [];
3094
3874
  for (let i = 0; i < items.length; i += size)
3095
- out.push(items.slice(i, i + size));
3096
- return out;
3875
+ out2.push(items.slice(i, i + size));
3876
+ return out2;
3097
3877
  }
3098
3878
  function numOrNull(v) {
3099
3879
  if (v === null)
@@ -3108,8 +3888,8 @@ function numOr0(v) {
3108
3888
  // ../../packages/code-index/build/node.js
3109
3889
  var DEFAULT_DB_RELPATH = ".maind/code-index.sqlite";
3110
3890
  function createNodeCodeIndex(opts) {
3111
- const dbPath = opts.dbPath ?? posix4.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3112
- mkdirSync3(posix4.dirname(dbPath), { recursive: true });
3891
+ const dbPath = opts.dbPath ?? posix6.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3892
+ mkdirSync3(posix6.dirname(dbPath), { recursive: true });
3113
3893
  const store = new NodeSqliteStore(dbPath);
3114
3894
  const parser = new TreeSitterParser();
3115
3895
  return new CodeIndexEngine({
@@ -3120,7 +3900,7 @@ function createNodeCodeIndex(opts) {
3120
3900
  });
3121
3901
  }
3122
3902
  function createCodeIndexSync(opts) {
3123
- const dbPath = opts.dbPath ?? posix4.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3903
+ const dbPath = opts.dbPath ?? posix6.join(opts.repoRoot, DEFAULT_DB_RELPATH);
3124
3904
  const projectKey = opts.projectKey ?? computeProjectKey(opts.repoRoot) ?? void 0;
3125
3905
  if (!projectKey) {
3126
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.");
@@ -3163,8 +3943,8 @@ function createEmbedder() {
3163
3943
  if (texts.length === 0)
3164
3944
  return [];
3165
3945
  const extractor = await getExtractor();
3166
- const out = await extractor(texts, { pooling: "cls", normalize: true });
3167
- 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));
3168
3948
  },
3169
3949
  async embedQuery(text) {
3170
3950
  const [vec] = await embedder.embedDocuments([
@@ -3209,7 +3989,7 @@ function runOrtIsolated(argv) {
3209
3989
  stdio: ["ignore", "inherit", "inherit"],
3210
3990
  env: { ...process.env, [OK_FILE_ENV]: okFile }
3211
3991
  });
3212
- const ok = existsSync2(okFile);
3992
+ const ok = existsSync4(okFile);
3213
3993
  try {
3214
3994
  rmSync(dirname3(okFile), { recursive: true, force: true });
3215
3995
  } catch {
@@ -3237,7 +4017,7 @@ function value(argv, name) {
3237
4017
  function resolveRoot(explicit) {
3238
4018
  if (explicit) return explicit;
3239
4019
  try {
3240
- return execFileSync4("git", ["rev-parse", "--show-toplevel"], {
4020
+ return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
3241
4021
  encoding: "utf8",
3242
4022
  stdio: ["ignore", "pipe", "ignore"]
3243
4023
  }).trim();
@@ -3411,40 +4191,1397 @@ async function runIndexArch(opts) {
3411
4191
  }
3412
4192
  }
3413
4193
 
3414
- // src/review.ts
3415
- import { execFileSync as execFileSync5 } from "node:child_process";
3416
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
3417
- import { dirname as dirname4 } from "node:path";
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";
3418
4199
 
3419
- // ../../packages/code-index/build/index.js
3420
- var CODE_INDEX_VERSION = "0.1.0";
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
+ }
4221
+ }
4222
+ return codeblock;
4223
+ }
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}
3421
4232
 
3422
- // src/review-render.ts
3423
- var COMMENT_MARKER = "<!-- maind-review -->";
3424
- var COMMENT_FINDINGS_CAP = 30;
3425
- var FOOTER = "maind review \xB7 deterministic, provenance-backed";
3426
- function buildReviewManifest(data, verdict, meta) {
3427
- const review = data.review;
3428
- return {
3429
- schema: "maind.review.manifest/v1",
3430
- generated_at: meta.generatedAt,
3431
- key_prefix: meta.keyPrefix,
3432
- run_ref: meta.runRef,
3433
- base: meta.base,
3434
- diff_stats: meta.diffStats,
3435
- symbol_count: meta.symbolCount,
3436
- risk_flags: review?.risk_flags ?? [],
3437
- findings: (review?.convention_findings ?? []).map((f) => ({
3438
- convention_id: f.convention_id,
3439
- version: f.convention_version,
3440
- severity: f.severity,
3441
- grade: f.grade,
3442
- matched_symbol_count: f.matched_symbols.length,
3443
- matched_changed_symbol_count: f.matched_changed_symbols?.length ?? 0
3444
- })),
3445
- served: (review?.lesson_hints ?? []).map((l) => ({
3446
- id: l.id,
3447
- version: typeof l.version === "number" ? l.version : null,
4233
+ ${codeblock}`, options);
4234
+ this.line = line;
4235
+ this.column = column;
4236
+ this.codeblock = codeblock;
4237
+ }
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
+ });
4299
+ }
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;
4317
+ }
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
+ }
4349
+ }
4350
+ super(date);
4351
+ if (!isNaN(this.getTime())) {
4352
+ this.#hasDate = hasDate;
4353
+ this.#hasTime = hasTime;
4354
+ this.#offset = offset;
4355
+ }
4356
+ }
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;
4408
+ }
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,
3448
5585
  tier: l.tier ?? null
3449
5586
  })),
3450
5587
  verdict: verdict?.ok === true && typeof verdict.verdict === "string" ? {
@@ -3500,132 +5637,132 @@ function renderReviewMarkdown(data, verdict, meta) {
3500
5637
  const findings = review?.convention_findings ?? [];
3501
5638
  const hints = review?.lesson_hints ?? [];
3502
5639
  const riskFlags = review?.risk_flags ?? [];
3503
- const lines = [];
3504
- lines.push("<!-- Generated by `maind review` \u2014 do not edit by hand. -->");
3505
- lines.push(
5640
+ const lines2 = [];
5641
+ lines2.push("<!-- Generated by `maind review` \u2014 do not edit by hand. -->");
5642
+ lines2.push(
3506
5643
  `<!-- generated_at: ${meta.generatedAt} | key: ${meta.keyPrefix}${meta.runRef ? ` | run: ${meta.runRef}` : ""} -->`
3507
5644
  );
3508
- lines.push("");
3509
- lines.push("# maind review");
3510
- lines.push("");
3511
- lines.push(
5645
+ lines2.push("");
5646
+ lines2.push("# maind review");
5647
+ lines2.push("");
5648
+ lines2.push(
3512
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}`
3513
5650
  );
3514
- lines.push("");
5651
+ lines2.push("");
3515
5652
  if (verdict?.ok === true && verdict.verdict) {
3516
- lines.push(verdictLabel(verdict));
5653
+ lines2.push(verdictLabel(verdict));
3517
5654
  const attest = attestationLine(verdict);
3518
5655
  if (attest) {
3519
- lines.push("");
3520
- lines.push(attest);
5656
+ lines2.push("");
5657
+ lines2.push(attest);
3521
5658
  }
3522
5659
  if (verdict.trust_note) {
3523
- lines.push("");
3524
- lines.push(`_${verdict.trust_note}_`);
5660
+ lines2.push("");
5661
+ lines2.push(`_${verdict.trust_note}_`);
3525
5662
  }
3526
- lines.push("");
5663
+ lines2.push("");
3527
5664
  }
3528
5665
  if (findings.length > 0) {
3529
- lines.push("## Convention findings");
3530
- lines.push("");
3531
- for (const f of findings) lines.push(findingLine(f));
3532
- lines.push("");
5666
+ lines2.push("## Convention findings");
5667
+ lines2.push("");
5668
+ for (const f of findings) lines2.push(findingLine(f));
5669
+ lines2.push("");
3533
5670
  } else {
3534
- lines.push("## Convention findings");
3535
- lines.push("");
3536
- lines.push("_No convention findings for this change set._");
3537
- lines.push("");
5671
+ lines2.push("## Convention findings");
5672
+ lines2.push("");
5673
+ lines2.push("_No convention findings for this change set._");
5674
+ lines2.push("");
3538
5675
  }
3539
5676
  if (riskFlags.length > 0) {
3540
- lines.push("## Risk flags");
3541
- lines.push("");
3542
- lines.push(
5677
+ lines2.push("## Risk flags");
5678
+ lines2.push("");
5679
+ lines2.push(
3543
5680
  "_Path heuristics from the local diff \u2014 informational, they never block._"
3544
5681
  );
3545
- lines.push("");
3546
- for (const flag2 of riskFlags) lines.push(`- \`${flag2}\``);
3547
- lines.push("");
5682
+ lines2.push("");
5683
+ for (const flag2 of riskFlags) lines2.push(`- \`${flag2}\``);
5684
+ lines2.push("");
3548
5685
  }
3549
5686
  if (hints.length > 0) {
3550
- lines.push("## Lesson hints");
3551
- lines.push("");
3552
- lines.push("_Semantic suggestions \u2014 advisory, never part of a verdict._");
3553
- lines.push("");
5687
+ lines2.push("## Lesson hints");
5688
+ lines2.push("");
5689
+ lines2.push("_Semantic suggestions \u2014 advisory, never part of a verdict._");
5690
+ lines2.push("");
3554
5691
  for (const h of hints) {
3555
5692
  const parts = [`\`${h.id}\``];
3556
5693
  if (h.tier) parts.push(h.tier);
3557
5694
  if (h.match_reason) parts.push(h.match_reason);
3558
- lines.push(`- **${h.title}** (${parts.join(" \xB7 ")})`);
5695
+ lines2.push(`- **${h.title}** (${parts.join(" \xB7 ")})`);
3559
5696
  }
3560
- lines.push("");
5697
+ lines2.push("");
3561
5698
  }
3562
5699
  const rc = review?.regression_context;
3563
5700
  if (rc && (rc.hot_files ?? 0) > 0) {
3564
- lines.push("");
3565
- lines.push(
5701
+ lines2.push("");
5702
+ lines2.push(
3566
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))._`
3567
5704
  );
3568
5705
  }
3569
5706
  if (review?.coverage_note) {
3570
- lines.push("## Coverage");
3571
- lines.push("");
3572
- lines.push(`> ${review.coverage_note}`);
3573
- lines.push("");
5707
+ lines2.push("## Coverage");
5708
+ lines2.push("");
5709
+ lines2.push(`> ${review.coverage_note}`);
5710
+ lines2.push("");
3574
5711
  }
3575
- lines.push("---");
3576
- lines.push(FOOTER);
3577
- lines.push("");
3578
- return lines.join("\n");
5712
+ lines2.push("---");
5713
+ lines2.push(FOOTER);
5714
+ lines2.push("");
5715
+ return lines2.join("\n");
3579
5716
  }
3580
5717
  function renderReviewComment(data, verdict, meta, reportPath) {
3581
5718
  const review = data.review;
3582
5719
  const findings = review?.convention_findings ?? [];
3583
5720
  const riskFlags = review?.risk_flags ?? [];
3584
5721
  const hints = review?.lesson_hints ?? [];
3585
- const lines = [];
3586
- lines.push(COMMENT_MARKER);
3587
- lines.push("## maind review");
3588
- lines.push("");
5722
+ const lines2 = [];
5723
+ lines2.push(COMMENT_MARKER);
5724
+ lines2.push("## maind review");
5725
+ lines2.push("");
3589
5726
  if (verdict?.ok === true && verdict.verdict) {
3590
- lines.push(verdictLabel(verdict));
5727
+ lines2.push(verdictLabel(verdict));
3591
5728
  const attest = attestationLine(verdict);
3592
- if (attest) lines.push(attest);
3593
- lines.push("");
5729
+ if (attest) lines2.push(attest);
5730
+ lines2.push("");
3594
5731
  }
3595
5732
  if (findings.length === 0) {
3596
- lines.push(
5733
+ lines2.push(
3597
5734
  `No convention findings for this change set (${meta.symbolCount} changed symbol(s) reviewed ${baseLabel(meta)}).`
3598
5735
  );
3599
- lines.push("");
5736
+ lines2.push("");
3600
5737
  } else {
3601
5738
  const shown = findings.slice(0, COMMENT_FINDINGS_CAP);
3602
- for (const f of shown) lines.push(findingLine(f));
5739
+ for (const f of shown) lines2.push(findingLine(f));
3603
5740
  if (findings.length > shown.length) {
3604
- lines.push("");
3605
- lines.push(
5741
+ lines2.push("");
5742
+ lines2.push(
3606
5743
  `\u2026and ${findings.length - shown.length} more finding(s) \u2014 see the \`${reportPath}\` build artifact.`
3607
5744
  );
3608
5745
  }
3609
- lines.push("");
5746
+ lines2.push("");
3610
5747
  }
3611
5748
  if (riskFlags.length > 0) {
3612
- lines.push(
5749
+ lines2.push(
3613
5750
  `**Risk flags:** ${riskFlags.map((f) => `\`${f}\``).join(" \xB7 ")} _(heuristics, never blocking)_`
3614
5751
  );
3615
- lines.push("");
5752
+ lines2.push("");
3616
5753
  }
3617
5754
  if (hints.length > 0) {
3618
- lines.push("**Related lessons:** " + hints.map((h) => `\`${h.id}\``).join(" \xB7 "));
3619
- lines.push("");
5755
+ lines2.push("**Related lessons:** " + hints.map((h) => `\`${h.id}\``).join(" \xB7 "));
5756
+ lines2.push("");
3620
5757
  }
3621
5758
  if (review?.coverage_note) {
3622
- lines.push(`> ${review.coverage_note}`);
3623
- lines.push("");
5759
+ lines2.push(`> ${review.coverage_note}`);
5760
+ lines2.push("");
3624
5761
  }
3625
- lines.push("---");
3626
- lines.push(`_${FOOTER}_`);
3627
- lines.push("");
3628
- return lines.join("\n");
5762
+ lines2.push("---");
5763
+ lines2.push(`_${FOOTER}_`);
5764
+ lines2.push("");
5765
+ return lines2.join("\n");
3629
5766
  }
3630
5767
  function renderFailOpenReview(meta, reason) {
3631
5768
  return [
@@ -3655,7 +5792,7 @@ function renderFailOpenComment(meta, reason) {
3655
5792
 
3656
5793
  // src/review.ts
3657
5794
  var PROJECT_KEY_RE3 = /^[a-f0-9]{64}$/;
3658
- var CLI_CLIENT_VERSION = "0.7.0";
5795
+ var CLI_CLIENT_VERSION = VERSION;
3659
5796
  var DEFAULT_COMMENT_PATH = ".maind/review-comment.md";
3660
5797
  var SYMBOL_CAP = 64;
3661
5798
  function parseReviewArgs(argv) {
@@ -3694,12 +5831,12 @@ function nowIso3() {
3694
5831
  return (/* @__PURE__ */ new Date()).toISOString();
3695
5832
  }
3696
5833
  function writeArtifact3(path, content) {
3697
- const dir = dirname4(path);
3698
- if (dir && dir !== ".") mkdirSync4(dir, { recursive: true });
3699
- writeFileSync4(path, content, "utf8");
5834
+ const dir = dirname5(path);
5835
+ if (dir && dir !== ".") mkdirSync5(dir, { recursive: true });
5836
+ writeFileSync5(path, content, "utf8");
3700
5837
  }
3701
- function manifestPathFor(out) {
3702
- 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";
3703
5840
  }
3704
5841
  function verdictCrossesFailOn(verdict, failOn) {
3705
5842
  return failOn === "block" ? verdict === "block" : verdict === "block" || verdict === "warn";
@@ -3721,7 +5858,7 @@ function buildDiffStats(result) {
3721
5858
  }
3722
5859
  function resolveRepoRoot() {
3723
5860
  try {
3724
- return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
5861
+ return execFileSync7("git", ["rev-parse", "--show-toplevel"], {
3725
5862
  encoding: "utf8",
3726
5863
  stdio: ["ignore", "pipe", "ignore"]
3727
5864
  }).trim();
@@ -3744,25 +5881,27 @@ async function runReview(opts) {
3744
5881
  if (opts.failOn && !opts.gate) {
3745
5882
  warn5("--fail-on has no effect without --gate (advisory runs always exit 0).");
3746
5883
  }
3747
- const failOpen = (reason) => {
3748
- warn5(`review unavailable, ${opts.strict ? "failing (--strict)" : "continuing"}: ${reason}`);
5884
+ const failOpen = (reason, klass = "infra") => {
5885
+ const hard = opts.strict && klass === "infra";
5886
+ warn5(`review unavailable, ${hard ? "failing (--strict)" : "continuing"}: ${reason}`);
3749
5887
  try {
3750
5888
  writeArtifact3(opts.out, renderFailOpenReview(meta, reason));
3751
5889
  const manifest = buildReviewManifest({}, null, meta);
3752
5890
  manifest.error = reason;
5891
+ manifest.error_class = klass;
3753
5892
  writeArtifact3(manifestPathFor(opts.out), JSON.stringify(manifest, null, 2) + "\n");
3754
5893
  if (opts.comment) writeArtifact3(opts.comment, renderFailOpenComment(meta, reason));
3755
5894
  if (opts.json) process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
3756
5895
  } catch (writeErr) {
3757
- warn5(`could not write fail-open artifacts: ${msg5(writeErr)}`);
5896
+ warn5(`could not write fail-open artifacts: ${msg6(writeErr)}`);
3758
5897
  return opts.strict ? 2 : 1;
3759
5898
  }
3760
- return opts.strict ? 2 : 0;
5899
+ return hard ? 2 : 0;
3761
5900
  };
3762
5901
  try {
3763
5902
  const root = resolveRepoRoot();
3764
5903
  if (!root) {
3765
- return failOpen("not a git repository \u2014 review is diff-based.");
5904
+ return failOpen("not a git repository \u2014 review is diff-based.", "expected");
3766
5905
  }
3767
5906
  const engine = createNodeCodeIndex({ repoRoot: root });
3768
5907
  let result;
@@ -3843,7 +5982,8 @@ async function runReview(opts) {
3843
5982
  const review = await client.callTool("review_changes", reviewArgs);
3844
5983
  if (review.ok !== true || !review.review) {
3845
5984
  return failOpen(
3846
- `review_changes rejected: ${review.error ?? "unknown_error"}${review.message ? ` \u2014 ${review.message}` : ""}`
5985
+ `review_changes rejected: ${review.error ?? "unknown_error"}${review.message ? ` \u2014 ${review.message}` : ""}`,
5986
+ classifyServerError(review.error)
3847
5987
  );
3848
5988
  }
3849
5989
  let verdict = null;
@@ -3875,9 +6015,11 @@ async function runReview(opts) {
3875
6015
  );
3876
6016
  verdict = null;
3877
6017
  } else {
3878
- if (opts.strict) {
6018
+ const klass = classifyServerError(verdict.error);
6019
+ if (opts.strict && klass === "infra") {
3879
6020
  return failOpen(
3880
- `review_verdict failed: ${verdict.error ?? "unknown_error"}${verdict.message ? ` \u2014 ${verdict.message}` : ""}`
6021
+ `review_verdict failed: ${verdict.error ?? "unknown_error"}${verdict.message ? ` \u2014 ${verdict.message}` : ""}`,
6022
+ klass
3881
6023
  );
3882
6024
  }
3883
6025
  warn5(
@@ -3886,9 +6028,9 @@ async function runReview(opts) {
3886
6028
  verdict = null;
3887
6029
  }
3888
6030
  }
3889
- } catch (err2) {
3890
- if (opts.strict) return failOpen(`review_verdict failed: ${msg5(err2)}`);
3891
- 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)}`);
3892
6034
  verdict = null;
3893
6035
  }
3894
6036
  }
@@ -3909,10 +6051,21 @@ async function runReview(opts) {
3909
6051
  }
3910
6052
  }
3911
6053
  return 0;
3912
- } catch (err2) {
3913
- const reason = err2 instanceof MissingApiKeyError ? err2.message : msg5(err2);
3914
- return failOpen(reason);
3915
- }
6054
+ } catch (err4) {
6055
+ if (err4 instanceof MissingApiKeyError) return failOpen(err4.message, "expected");
6056
+ return failOpen(msg6(err4), "infra");
6057
+ }
6058
+ }
6059
+ var EXPECTED_SERVER_ERRORS = /* @__PURE__ */ new Set([
6060
+ "unauthorized",
6061
+ "invalid_api_key",
6062
+ "requires_team",
6063
+ "requires_enterprise",
6064
+ "scope_not_in_plan",
6065
+ "rate_limited"
6066
+ ]);
6067
+ function classifyServerError(code) {
6068
+ return code && EXPECTED_SERVER_ERRORS.has(code) ? "expected" : "infra";
3916
6069
  }
3917
6070
  function writeAll(review, verdict, meta, opts) {
3918
6071
  writeArtifact3(opts.out, renderReviewMarkdown(review, verdict, meta));
@@ -3923,25 +6076,524 @@ function writeAll(review, verdict, meta, opts) {
3923
6076
  }
3924
6077
  if (opts.json) process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
3925
6078
  }
3926
- function msg5(err2) {
3927
- return err2 instanceof Error ? err2.message : String(err2);
6079
+ function msg6(err4) {
6080
+ return err4 instanceof Error ? err4.message : String(err4);
3928
6081
  }
3929
6082
  function warn5(line) {
3930
6083
  process.stderr.write(`maind review: ${line}
3931
6084
  `);
3932
6085
  }
3933
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
+
3934
6586
  // src/sync-cmd.ts
3935
- import { execFileSync as execFileSync6 } from "node:child_process";
6587
+ import { execFileSync as execFileSync8 } from "node:child_process";
3936
6588
  var CONSENT_POLICY_VERSION = "2026-07-05";
3937
6589
  var DEFAULT_DASHBOARD_URL = "https://app.maind.dev";
3938
- function err(line) {
6590
+ function err3(line) {
3939
6591
  process.stderr.write(`maind: ${line}
3940
6592
  `);
3941
6593
  }
3942
6594
  function repoRoot() {
3943
6595
  try {
3944
- return execFileSync6("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
6596
+ return execFileSync8("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
3945
6597
  } catch {
3946
6598
  return process.cwd();
3947
6599
  }
@@ -3956,7 +6608,7 @@ function makeSync(json) {
3956
6608
  auth = resolveAuth();
3957
6609
  } catch (e) {
3958
6610
  if (e instanceof MissingApiKeyError) {
3959
- err(e.message);
6611
+ err3(e.message);
3960
6612
  return null;
3961
6613
  }
3962
6614
  throw e;
@@ -3970,11 +6622,11 @@ function makeSync(json) {
3970
6622
  onProgress: json ? void 0 : (p) => process.stderr.write(` ${p.phase}: ${p.done}/${p.total}\r`)
3971
6623
  });
3972
6624
  } catch (e) {
3973
- err(e instanceof Error ? e.message : String(e));
6625
+ err3(e instanceof Error ? e.message : String(e));
3974
6626
  return null;
3975
6627
  }
3976
6628
  }
3977
- function msg6(e) {
6629
+ function msg7(e) {
3978
6630
  return e instanceof Error ? e.message : String(e);
3979
6631
  }
3980
6632
  function parseSyncArgs(argv) {
@@ -3998,10 +6650,10 @@ async function runSync(opts) {
3998
6650
  }
3999
6651
  } catch (e) {
4000
6652
  if (e instanceof SyncError && e.code === "consent_required") {
4001
- 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.");
4002
6654
  return 1;
4003
6655
  }
4004
- err(`sync failed: ${msg6(e)}`);
6656
+ err3(`sync failed: ${msg7(e)}`);
4005
6657
  return 1;
4006
6658
  } finally {
4007
6659
  await sync.close().catch(() => {
@@ -4037,7 +6689,7 @@ cloud: ${remote.indexed ? `${remote.node_count ?? "?"} symbols \xB7 ${remote.ed
4037
6689
  async function push(sync, json) {
4038
6690
  const local = await sync.localStatus();
4039
6691
  if (!local.indexed || local.nodeCount === 0) {
4040
- err("no local index found \u2014 run `maind index` first.");
6692
+ err3("no local index found \u2014 run `maind index` first.");
4041
6693
  return 1;
4042
6694
  }
4043
6695
  const res = await sync.push();
@@ -4054,7 +6706,6 @@ async function push(sync, json) {
4054
6706
  }
4055
6707
 
4056
6708
  // src/index.ts
4057
- var VERSION = "0.7.0";
4058
6709
  var HELP = `maind ${VERSION} \u2014 headless maind CLI for CI + git context
4059
6710
 
4060
6711
  Usage:
@@ -4069,6 +6720,8 @@ Usage:
4069
6720
  maind sync Push the local index to the cloud (opt-in; hashed rows only)
4070
6721
  maind sync status [--json] Show local vs cloud sync state
4071
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)
4072
6725
  maind review [options] Provenance-backed review findings for a change set (advisory by default)
4073
6726
  maind ci-report [options] Report a CI failure as a draft-lesson candidate (on failure)
4074
6727
  maind ci-result [options] Report a build outcome + served manifest (end of job)
@@ -4086,13 +6739,34 @@ review options (advisory by default \u2014 exits 0 unless --gate + --fail-on):
4086
6739
  --json Print the review manifest JSON to stdout (pipeable)
4087
6740
  --gate Aggregate findings into a server-side pass|warn|block verdict (Team plan)
4088
6741
  --fail-on block|warn With --gate: exit 1 when the verdict crosses this threshold
4089
- --strict Infra errors exit 2 instead of failing open with exit 0
6742
+ --strict Infra errors (native module / transport / unexpected server
6743
+ error) exit 2 instead of failing open with exit 0. Expected
6744
+ unavailability \u2014 missing or unauthorized API key, rate limit,
6745
+ non-git dir \u2014 still exits 0 with a loud warning.
4090
6746
  --no-report With --gate: omit the commit anchors so the server does NOT
4091
6747
  persist a review_runs attestation record (ADR-230) \u2014 the
4092
6748
  verdict is then advisory-only, without a replayable proof
4093
6749
  --project-key <hex> Repo project_key override (default: auto-detect from git)
4094
6750
  --mcp-url <url> Override MCP endpoint (default $MAIND_MCP_URL or production)
4095
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
+
4096
6770
  git-context options (run at session-start; reads local git + gh, all fail-open):
4097
6771
  --out <path> Briefing markdown output (default .maind/git-context.md)
4098
6772
  --manifest <path> Optional structured JSON output (git_context manifest)
@@ -4118,6 +6792,16 @@ ci-result options (run at the end of a job, success OR failure):
4118
6792
  --run-url <url> Link to the CI run (default derived from GitHub env)
4119
6793
  --result-url <url> Override ingest endpoint (default $MAIND_RESULT_URL or prod)
4120
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
+
4121
6805
  context options:
4122
6806
  --stack <a,b> Stack hint (seeds the platforms filter), e.g. nextjs,supabase
4123
6807
  --platforms <a,b> Explicit platforms filter (overrides --stack)
@@ -4161,6 +6845,9 @@ async function main() {
4161
6845
  process.stdout.write(key + "\n");
4162
6846
  return 0;
4163
6847
  }
6848
+ if (cmd === "launch") {
6849
+ return runLaunch(parseLaunchArgs(argv.slice(1)));
6850
+ }
4164
6851
  const usesOrt = cmd === "index" && argv.includes("--embed") || cmd === "index-semantic";
4165
6852
  if (usesOrt && !isOrtWorker(argv)) {
4166
6853
  return runOrtIsolated(argv);
@@ -4183,6 +6870,13 @@ async function main() {
4183
6870
  if (cmd === "sync") {
4184
6871
  return runSync(parseSyncArgs(argv.slice(1)));
4185
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
+ }
4186
6880
  if (cmd === "review") {
4187
6881
  return runReview(parseReviewArgs(argv.slice(1)));
4188
6882
  }
@@ -4196,8 +6890,271 @@ async function main() {
4196
6890
  `);
4197
6891
  return 2;
4198
6892
  }
4199
- main().then((code) => process.exit(code)).catch((err2) => {
4200
- 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)}
4201
6895
  `);
4202
6896
  process.exit(1);
4203
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
+ */