@massu/core 2.0.0 → 2.1.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/dist/cli.js CHANGED
@@ -49,27 +49,52 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
49
49
  ));
50
50
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
51
51
 
52
+ // src/lib/node-floor.ts
53
+ function checkNodeVersion(version = process.version) {
54
+ const [major, minor] = version.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10));
55
+ if (!Number.isFinite(major)) {
56
+ return { ok: false, major: 0, message: `could not parse Node version "${version}"` };
57
+ }
58
+ const meets = major > MIN_NODE_MAJOR || major === MIN_NODE_MAJOR && (minor ?? 0) >= MIN_NODE_MINOR;
59
+ if (!meets) {
60
+ return {
61
+ ok: false,
62
+ major,
63
+ message: `Massu requires Node >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0 (its built-in node:sqlite engine). You are on ${version}.`
64
+ };
65
+ }
66
+ return { ok: true, major };
67
+ }
68
+ var MIN_NODE_MAJOR, MIN_NODE_MINOR;
69
+ var init_node_floor = __esm({
70
+ "src/lib/node-floor.ts"() {
71
+ "use strict";
72
+ MIN_NODE_MAJOR = 22;
73
+ MIN_NODE_MINOR = 13;
74
+ }
75
+ });
76
+
52
77
  // src/credentials.ts
53
- import { homedir } from "os";
54
- import { resolve } from "path";
78
+ import { homedir as homedir2 } from "os";
79
+ import { resolve as resolve2 } from "path";
55
80
  import {
56
- existsSync,
81
+ existsSync as existsSync2,
57
82
  readFileSync,
58
83
  writeFileSync,
59
84
  mkdirSync,
60
85
  rmSync,
61
86
  chmodSync
62
87
  } from "fs";
63
- function credentialsDir(home = homedir()) {
64
- return resolve(home, ".massu");
88
+ function credentialsDir(home = homedir2()) {
89
+ return resolve2(home, ".massu");
65
90
  }
66
- function credentialsPath(home = homedir()) {
67
- return resolve(credentialsDir(home), "credentials");
91
+ function credentialsPath(home = homedir2()) {
92
+ return resolve2(credentialsDir(home), "credentials");
68
93
  }
69
- function readUserCredentials(home = homedir()) {
94
+ function readUserCredentials(home = homedir2()) {
70
95
  try {
71
96
  const p19 = credentialsPath(home);
72
- if (!existsSync(p19)) return void 0;
97
+ if (!existsSync2(p19)) return void 0;
73
98
  const parsed = JSON.parse(readFileSync(p19, "utf-8"));
74
99
  const key = typeof parsed?.apiKey === "string" ? parsed.apiKey.trim() : "";
75
100
  return key.length > 0 ? key : void 0;
@@ -77,7 +102,7 @@ function readUserCredentials(home = homedir()) {
77
102
  return void 0;
78
103
  }
79
104
  }
80
- function writeUserCredentials(apiKey, home = homedir()) {
105
+ function writeUserCredentials(apiKey, home = homedir2()) {
81
106
  const dir = credentialsDir(home);
82
107
  mkdirSync(dir, { recursive: true, mode: 448 });
83
108
  try {
@@ -94,9 +119,9 @@ function writeUserCredentials(apiKey, home = homedir()) {
94
119
  }
95
120
  return p19;
96
121
  }
97
- function removeUserCredentials(home = homedir()) {
122
+ function removeUserCredentials(home = homedir2()) {
98
123
  const p19 = credentialsPath(home);
99
- if (!existsSync(p19)) return false;
124
+ if (!existsSync2(p19)) return false;
100
125
  try {
101
126
  rmSync(p19);
102
127
  return true;
@@ -109,7 +134,7 @@ function isUnresolvedLiteral(v3) {
109
134
  }
110
135
  function resolveApiKey(opts = {}) {
111
136
  const env = opts.env ?? process.env;
112
- const home = opts.home ?? homedir();
137
+ const home = opts.home ?? homedir2();
113
138
  const cfg = typeof opts.configApiKey === "string" ? opts.configApiKey.trim() : "";
114
139
  if (cfg.length > 0 && !isUnresolvedLiteral(cfg)) {
115
140
  return { apiKey: cfg, source: "config" };
@@ -256,8 +281,8 @@ var init_fileLock = __esm({
256
281
 
257
282
  // src/lib/sqlite-loader.ts
258
283
  import { createRequire } from "module";
259
- import { spawnSync } from "child_process";
260
- import { accessSync, appendFileSync, chmodSync as chmodSync2, constants as fsConstants, existsSync as existsSync2, mkdirSync as mkdirSync3 } from "fs";
284
+ import { spawnSync as spawnSync2 } from "child_process";
285
+ import { accessSync, appendFileSync, chmodSync as chmodSync2, constants as fsConstants, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
261
286
  import { dirname as dirname2, join } from "path";
262
287
  function isNativeAbiError(err) {
263
288
  const code = err?.code;
@@ -329,7 +354,7 @@ function probeMemoryDbUsable(opts = {}) {
329
354
  };
330
355
  }
331
356
  const dbPath = opts.dbPath;
332
- if (dbPath && dbPath !== ":memory:" && existsSync2(dbPath)) {
357
+ if (dbPath && dbPath !== ":memory:" && existsSync3(dbPath)) {
333
358
  try {
334
359
  const rdb = openDatabase(dbPath, { readonly: true, selfHeal: false });
335
360
  try {
@@ -396,7 +421,7 @@ function runRebuild(pkgDir, abiFrom, abiTo, start) {
396
421
  }
397
422
  const prebuildBin = resolveLocalBin(pkgDir, "prebuild-install");
398
423
  if (prebuildBin) {
399
- const r2 = spawnSync(process.execPath, [prebuildBin], {
424
+ const r2 = spawnSync2(process.execPath, [prebuildBin], {
400
425
  cwd: pkgDir,
401
426
  encoding: "utf-8",
402
427
  timeout: 12e4
@@ -409,7 +434,7 @@ function runRebuild(pkgDir, abiFrom, abiTo, start) {
409
434
  }
410
435
  const gypBin = resolveNodeGypBin();
411
436
  if (gypBin && hasCompiler()) {
412
- const r2 = spawnSync(process.execPath, [gypBin, "rebuild", "--release"], {
437
+ const r2 = spawnSync2(process.execPath, [gypBin, "rebuild", "--release"], {
413
438
  cwd: pkgDir,
414
439
  encoding: "utf-8",
415
440
  timeout: 3e5
@@ -479,7 +504,7 @@ function resolveLocalBin(pkgDir, name) {
479
504
  // workspace hoist
480
505
  ];
481
506
  for (const c2 of candidates) {
482
- if (existsSync2(c2)) return c2;
507
+ if (existsSync3(c2)) return c2;
483
508
  }
484
509
  return void 0;
485
510
  }
@@ -493,7 +518,7 @@ function resolveNodeGypBin() {
493
518
  function hasCompiler() {
494
519
  for (const cc of ["cc", "clang", "gcc"]) {
495
520
  try {
496
- const r2 = spawnSync(cc, ["--version"], { encoding: "utf-8", timeout: 5e3 });
521
+ const r2 = spawnSync2(cc, ["--version"], { encoding: "utf-8", timeout: 5e3 });
497
522
  if (r2.status === 0) return true;
498
523
  } catch {
499
524
  }
@@ -529,7 +554,7 @@ var init_sqlite_loader = __esm({
529
554
 
530
555
  // src/db-driver.ts
531
556
  import { createRequire as createRequire2 } from "module";
532
- import { closeSync, existsSync as existsSync3, mkdtempSync, openSync, readFileSync as readFileSync3, rmSync as rmSync3, fsyncSync } from "fs";
557
+ import { closeSync, existsSync as existsSync4, mkdtempSync, openSync, readFileSync as readFileSync3, rmSync as rmSync3, fsyncSync } from "fs";
533
558
  import { tmpdir } from "os";
534
559
  import { join as join2 } from "path";
535
560
  function resolveDbEngine() {
@@ -664,7 +689,7 @@ function probeMemoryDbUsable2(opts = {}) {
664
689
  };
665
690
  }
666
691
  const dbPath = opts.dbPath;
667
- if (dbPath && dbPath !== ":memory:" && existsSync3(dbPath)) {
692
+ if (dbPath && dbPath !== ":memory:" && existsSync4(dbPath)) {
668
693
  try {
669
694
  const rdb = openDatabase2(dbPath, { readonly: true });
670
695
  try {
@@ -882,16 +907,16 @@ __export(config_exports, {
882
907
  getResolvedPaths: () => getResolvedPaths,
883
908
  resetConfig: () => resetConfig
884
909
  });
885
- import { resolve as resolve2, dirname as dirname3 } from "path";
886
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
887
- import { homedir as homedir2 } from "os";
910
+ import { resolve as resolve3, dirname as dirname3 } from "path";
911
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
912
+ import { homedir as homedir3 } from "os";
888
913
  import { parse as parseYaml } from "yaml";
889
914
  import { z as z2 } from "zod";
890
915
  function findProjectRoot() {
891
916
  const cwd = process.cwd();
892
917
  let dir = cwd;
893
918
  while (true) {
894
- if (existsSync4(resolve2(dir, "massu.config.yaml"))) {
919
+ if (existsSync5(resolve3(dir, "massu.config.yaml"))) {
895
920
  return dir;
896
921
  }
897
922
  const parent = dirname3(dir);
@@ -900,10 +925,10 @@ function findProjectRoot() {
900
925
  }
901
926
  dir = cwd;
902
927
  while (true) {
903
- if (existsSync4(resolve2(dir, "package.json"))) {
928
+ if (existsSync5(resolve3(dir, "package.json"))) {
904
929
  return dir;
905
930
  }
906
- if (existsSync4(resolve2(dir, ".git"))) {
931
+ if (existsSync5(resolve3(dir, ".git"))) {
907
932
  return dir;
908
933
  }
909
934
  const parent = dirname3(dir);
@@ -921,9 +946,9 @@ function getProjectRoot() {
921
946
  function getConfig() {
922
947
  if (_config) return _config;
923
948
  const root = getProjectRoot();
924
- const configPath = resolve2(root, "massu.config.yaml");
949
+ const configPath = resolve3(root, "massu.config.yaml");
925
950
  let rawYaml = {};
926
- if (existsSync4(configPath)) {
951
+ if (existsSync5(configPath)) {
927
952
  const content = readFileSync4(configPath, "utf-8");
928
953
  rawYaml = parseYaml(content) ?? {};
929
954
  }
@@ -941,7 +966,7 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
941
966
  );
942
967
  }
943
968
  const parsed = result.data;
944
- const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve2(root, parsed.project.root);
969
+ const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve3(root, parsed.project.root);
945
970
  const fw = parsed.framework;
946
971
  let router = fw.router;
947
972
  let orm = fw.orm;
@@ -1029,34 +1054,35 @@ function getResolvedPaths() {
1029
1054
  const root = getProjectRoot();
1030
1055
  const claudeDirName = config.conventions?.claudeDirName ?? ".claude";
1031
1056
  return {
1032
- codegraphDbPath: resolve2(root, ".codegraph/codegraph.db"),
1033
- dataDbPath: resolve2(root, ".massu/data.db"),
1034
- prismaSchemaPath: resolve2(root, config.paths.schema ?? "prisma/schema.prisma"),
1035
- rootRouterPath: resolve2(root, config.paths.routerRoot ?? "src/server/api/root.ts"),
1036
- routersDir: resolve2(root, config.paths.routers ?? "src/server/api/routers"),
1037
- srcDir: resolve2(root, config.paths.source),
1057
+ codegraphDbPath: resolve3(root, ".codegraph/codegraph.db"),
1058
+ dataDbPath: resolve3(root, ".massu/data.db"),
1059
+ prismaSchemaPath: resolve3(root, config.paths.schema ?? "prisma/schema.prisma"),
1060
+ rootRouterPath: resolve3(root, config.paths.routerRoot ?? "src/server/api/root.ts"),
1061
+ routersDir: resolve3(root, config.paths.routers ?? "src/server/api/routers"),
1062
+ srcDir: resolve3(root, config.paths.source),
1038
1063
  pathAlias: Object.fromEntries(
1039
1064
  Object.entries(config.paths.aliases).map(([alias, target]) => [
1040
1065
  alias,
1041
- resolve2(root, target)
1066
+ resolve3(root, target)
1042
1067
  ])
1043
1068
  ),
1044
1069
  extensions: [".ts", ".tsx", ".js", ".jsx"],
1045
1070
  indexFiles: ["index.ts", "index.tsx", "index.js", "index.jsx"],
1046
- patternsDir: resolve2(root, claudeDirName, "patterns"),
1047
- claudeMdPath: resolve2(root, claudeDirName, "CLAUDE.md"),
1048
- docsMapPath: resolve2(root, ".massu/docs-map.json"),
1049
- helpSitePath: resolve2(root, "../" + config.project.name + "-help"),
1050
- memoryDbPath: resolve2(root, ".massu/memory.db"),
1051
- knowledgeDbPath: resolve2(root, ".massu/knowledge.db"),
1052
- plansDir: resolve2(root, "docs/plans"),
1053
- docsDir: resolve2(root, "docs"),
1054
- claudeDir: resolve2(root, claudeDirName),
1055
- memoryDir: resolve2(homedir2(), claudeDirName, "projects", encodeMemoryDirName(root), "memory"),
1056
- sessionStatePath: resolve2(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
1057
- sessionArchivePath: resolve2(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
1058
- mcpJsonPath: resolve2(root, ".mcp.json"),
1059
- settingsLocalPath: resolve2(root, claudeDirName, "settings.local.json")
1071
+ patternsDir: resolve3(root, claudeDirName, "patterns"),
1072
+ claudeMdPath: resolve3(root, claudeDirName, "CLAUDE.md"),
1073
+ docsMapPath: resolve3(root, ".massu/docs-map.json"),
1074
+ helpSitePath: resolve3(root, "../" + config.project.name + "-help"),
1075
+ memoryDbPath: resolve3(root, ".massu/memory.db"),
1076
+ knowledgeDbPath: resolve3(root, ".massu/knowledge.db"),
1077
+ plansDir: resolve3(root, "docs/plans"),
1078
+ docsDir: resolve3(root, "docs"),
1079
+ claudeDir: resolve3(root, claudeDirName),
1080
+ memoryDir: resolve3(homedir3(), claudeDirName, "projects", encodeMemoryDirName(root), "memory"),
1081
+ sessionStatePath: resolve3(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
1082
+ sessionArchivePath: resolve3(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
1083
+ mcpJsonPath: resolve3(root, ".mcp.json"),
1084
+ settingsPath: resolve3(root, claudeDirName, "settings.json"),
1085
+ settingsLocalPath: resolve3(root, claudeDirName, "settings.local.json")
1060
1086
  };
1061
1087
  }
1062
1088
  function resetConfig() {
@@ -1512,39 +1538,39 @@ var init_memory_vector = __esm({
1512
1538
  });
1513
1539
 
1514
1540
  // src/db-backup.ts
1515
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
1516
- import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
1517
- import { homedir as homedir3 } from "os";
1518
- function dbBackupsRoot(home = homedir3()) {
1519
- return resolve3(home, ".massu", "db-backups");
1541
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
1542
+ import { resolve as resolve4, join as join3, basename, dirname as dirname4 } from "path";
1543
+ import { homedir as homedir4 } from "os";
1544
+ function dbBackupsRoot(home = homedir4()) {
1545
+ return resolve4(home, ".massu", "db-backups");
1520
1546
  }
1521
- function projectBackupDir(projectRoot, home = homedir3()) {
1547
+ function projectBackupDir(projectRoot, home = homedir4()) {
1522
1548
  const slug = basename(projectRoot).replace(/[^A-Za-z0-9._-]/g, "_") || "project";
1523
1549
  return join3(dbBackupsRoot(home), slug);
1524
1550
  }
1525
1551
  function backupStamp(nowMs) {
1526
1552
  return new Date(nowMs).toISOString().replace(/[:.]/g, "-");
1527
1553
  }
1528
- function listDbBackups(projectRoot, home = homedir3()) {
1554
+ function listDbBackups(projectRoot, home = homedir4()) {
1529
1555
  const dir = projectBackupDir(projectRoot, home);
1530
- if (!existsSync5(dir)) return [];
1556
+ if (!existsSync6(dir)) return [];
1531
1557
  const out = [];
1532
- for (const f2 of readdirSync(dir)) {
1558
+ for (const f2 of readdirSync2(dir)) {
1533
1559
  if (!f2.endsWith(".db")) continue;
1534
1560
  const p19 = join3(dir, f2);
1535
- const st = statSync(p19);
1561
+ const st = statSync2(p19);
1536
1562
  const db = f2.replace(/-\d{4}-\d{2}-\d{2}T.*$/, "");
1537
1563
  out.push({ db, path: p19, bytes: st.size, mtimeMs: st.mtimeMs });
1538
1564
  }
1539
1565
  return out.sort((a2, b2) => b2.mtimeMs - a2.mtimeMs);
1540
1566
  }
1541
- function hasFreshDbBackup(projectRoot, dbPath, nowMs = Date.now(), home = homedir3()) {
1567
+ function hasFreshDbBackup(projectRoot, dbPath, nowMs = Date.now(), home = homedir4()) {
1542
1568
  const name = basename(dbPath, ".db");
1543
1569
  const backups = listDbBackups(projectRoot, home).filter((b2) => b2.db === name);
1544
1570
  if (backups.length === 0) return false;
1545
1571
  const newest = backups[0];
1546
1572
  if (nowMs - newest.mtimeMs > FRESH_WINDOW_MS) return false;
1547
- if (existsSync5(dbPath) && statSync(dbPath).mtimeMs > newest.mtimeMs) return false;
1573
+ if (existsSync6(dbPath) && statSync2(dbPath).mtimeMs > newest.mtimeMs) return false;
1548
1574
  return true;
1549
1575
  }
1550
1576
  function integrityOk(dbPath) {
@@ -1562,8 +1588,8 @@ function integrityOk(dbPath) {
1562
1588
  }
1563
1589
  }
1564
1590
  }
1565
- function backupDb(projectRoot, dbPath, nowMs = Date.now(), home = homedir3()) {
1566
- if (!existsSync5(dbPath)) {
1591
+ function backupDb(projectRoot, dbPath, nowMs = Date.now(), home = homedir4()) {
1592
+ if (!existsSync6(dbPath)) {
1567
1593
  throw new DbBackupError(`cannot back up a database that does not exist: ${dbPath}`);
1568
1594
  }
1569
1595
  const dir = projectBackupDir(projectRoot, home);
@@ -1594,10 +1620,10 @@ function backupDb(projectRoot, dbPath, nowMs = Date.now(), home = homedir3()) {
1594
1620
  );
1595
1621
  }
1596
1622
  pruneDbBackups(projectRoot, name, DEFAULT_RETENTION, home);
1597
- const st = statSync(dest);
1623
+ const st = statSync2(dest);
1598
1624
  return { db: name, path: dest, bytes: st.size, mtimeMs: st.mtimeMs };
1599
1625
  }
1600
- function pruneDbBackups(projectRoot, dbName, keep = DEFAULT_RETENTION, home = homedir3()) {
1626
+ function pruneDbBackups(projectRoot, dbName, keep = DEFAULT_RETENTION, home = homedir4()) {
1601
1627
  const mine = listDbBackups(projectRoot, home).filter((b2) => b2.db === dbName);
1602
1628
  let removed = 0;
1603
1629
  for (const old of mine.slice(keep)) {
@@ -1609,8 +1635,8 @@ function pruneDbBackups(projectRoot, dbName, keep = DEFAULT_RETENTION, home = ho
1609
1635
  }
1610
1636
  return removed;
1611
1637
  }
1612
- function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir3()) {
1613
- if (!existsSync5(backupPath)) {
1638
+ function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir4()) {
1639
+ if (!existsSync6(backupPath)) {
1614
1640
  throw new DbBackupError(`backup not found: ${backupPath}`);
1615
1641
  }
1616
1642
  if (!integrityOk(backupPath)) {
@@ -1619,7 +1645,7 @@ function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir3()) {
1619
1645
  );
1620
1646
  }
1621
1647
  let preserved = null;
1622
- if (existsSync5(dbPath)) {
1648
+ if (existsSync6(dbPath)) {
1623
1649
  const dir = dirname4(backupPath) || projectBackupDir(process.cwd(), home);
1624
1650
  mkdirSync4(dir, { recursive: true });
1625
1651
  preserved = join3(dir, `${basename(dbPath, ".db")}.corrupt-${backupStamp(nowMs)}.db`);
@@ -1627,7 +1653,7 @@ function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir3()) {
1627
1653
  }
1628
1654
  for (const suffix of ["", "-wal", "-shm"]) {
1629
1655
  const p19 = `${dbPath}${suffix}`;
1630
- if (existsSync5(p19)) rmSync4(p19, { force: true });
1656
+ if (existsSync6(p19)) rmSync4(p19, { force: true });
1631
1657
  }
1632
1658
  copyFileSync(backupPath, dbPath);
1633
1659
  if (!integrityOk(dbPath)) {
@@ -1637,9 +1663,9 @@ function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir3()) {
1637
1663
  }
1638
1664
  return { restoredFrom: backupPath, preservedOriginal: preserved };
1639
1665
  }
1640
- function backupBeforeSchemaChange(projectRoot, dbPath, onError, nowMs = Date.now(), home = homedir3()) {
1666
+ function backupBeforeSchemaChange(projectRoot, dbPath, onError, nowMs = Date.now(), home = homedir4()) {
1641
1667
  try {
1642
- if (!existsSync5(dbPath)) return null;
1668
+ if (!existsSync6(dbPath)) return null;
1643
1669
  if (hasFreshDbBackup(projectRoot, dbPath, nowMs, home)) return null;
1644
1670
  return backupDb(projectRoot, dbPath, nowMs, home);
1645
1671
  } catch (err) {
@@ -1814,14 +1840,14 @@ var init_rule_candidate_store = __esm({
1814
1840
  });
1815
1841
 
1816
1842
  // src/hooks/lib/hook-failure-signal.ts
1817
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
1843
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
1818
1844
  import { join as join4, dirname as dirname5 } from "path";
1819
1845
  function resolveFailureLogPath() {
1820
1846
  const explicit = process.env.MASSU_HOOK_FAILURE_LOG;
1821
1847
  if (explicit) return explicit;
1822
1848
  let dir = process.cwd();
1823
1849
  for (let i = 0; i < 12; i++) {
1824
- if (existsSync6(join4(dir, ".massu")) || existsSync6(join4(dir, "massu.config.yaml"))) {
1850
+ if (existsSync7(join4(dir, ".massu")) || existsSync7(join4(dir, "massu.config.yaml"))) {
1825
1851
  return join4(dir, ".massu", "hook-failures.jsonl");
1826
1852
  }
1827
1853
  const parent = dirname5(dir);
@@ -2008,7 +2034,7 @@ var init_memory_embedder_tokenizer = __esm({
2008
2034
  // src/memory-embedder.ts
2009
2035
  import { fileURLToPath } from "url";
2010
2036
  import { dirname as dirname6, join as join5 } from "path";
2011
- import { existsSync as existsSync7 } from "fs";
2037
+ import { existsSync as existsSync8 } from "fs";
2012
2038
  import { createRequire as createRequire3 } from "module";
2013
2039
  function getActiveEmbedModel() {
2014
2040
  return _activeModel;
@@ -2033,7 +2059,7 @@ function resolveModelDir() {
2033
2059
  let dir = dirname6(fileURLToPath(import.meta.url));
2034
2060
  let root = null;
2035
2061
  for (let i = 0; i < 8; i++) {
2036
- if (existsSync7(join5(dir, "package.json"))) {
2062
+ if (existsSync8(join5(dir, "package.json"))) {
2037
2063
  root = dir;
2038
2064
  break;
2039
2065
  }
@@ -2044,7 +2070,7 @@ function resolveModelDir() {
2044
2070
  if (!root) return null;
2045
2071
  for (const rel of ["dist/embedder", "assets/embedder"]) {
2046
2072
  const candidate = join5(root, rel);
2047
- if (existsSync7(join5(candidate, "model_quantized.onnx"))) return candidate;
2073
+ if (existsSync8(join5(candidate, "model_quantized.onnx"))) return candidate;
2048
2074
  }
2049
2075
  return null;
2050
2076
  } catch {
@@ -2430,7 +2456,7 @@ __export(memory_db_exports, {
2430
2456
  usageWarmupElapsed: () => usageWarmupElapsed
2431
2457
  });
2432
2458
  import { dirname as dirname7, basename as basename2 } from "path";
2433
- import { existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
2459
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
2434
2460
  function sanitizeFts5Query(raw) {
2435
2461
  const trimmed = raw.trim();
2436
2462
  if (!trimmed) return '""';
@@ -2447,10 +2473,10 @@ function sanitizeFts5QueryOr(raw) {
2447
2473
  function getMemoryDb() {
2448
2474
  const dbPath = getResolvedPaths().memoryDbPath;
2449
2475
  const dir = dirname7(dbPath);
2450
- if (!existsSync8(dir)) {
2476
+ if (!existsSync9(dir)) {
2451
2477
  mkdirSync6(dir, { recursive: true });
2452
2478
  }
2453
- const preExisting = existsSync8(dbPath);
2479
+ const preExisting = existsSync9(dbPath);
2454
2480
  const db = openDatabase2(dbPath);
2455
2481
  db.pragma("journal_mode = WAL");
2456
2482
  db.pragma("foreign_keys = ON");
@@ -4238,7 +4264,7 @@ var init_memory_db = __esm({
4238
4264
  });
4239
4265
 
4240
4266
  // src/memory-file-ingest.ts
4241
- import { readFileSync as readFileSync6, existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
4267
+ import { readFileSync as readFileSync6, existsSync as existsSync10, readdirSync as readdirSync3 } from "fs";
4242
4268
  import { join as join6 } from "path";
4243
4269
  import { parse as parseYaml2 } from "yaml";
4244
4270
  import { basename as pathBasename } from "path";
@@ -4345,7 +4371,7 @@ function upsertMemoryFileMirror(db, f2) {
4345
4371
  return "inserted";
4346
4372
  }
4347
4373
  function ingestMemoryFile(db, sessionId, filePath) {
4348
- if (!existsSync9(filePath)) return "skipped";
4374
+ if (!existsSync10(filePath)) return "skipped";
4349
4375
  const content = readFileSync6(filePath, "utf-8");
4350
4376
  if (Buffer.byteLength(content, "utf-8") > MAX_MEMORY_FILE_BYTES) {
4351
4377
  process.stderr.write(
@@ -4416,8 +4442,8 @@ ${body}` : body;
4416
4442
  }
4417
4443
  function backfillMemoryFiles(db, memoryDir, sessionId) {
4418
4444
  const stats = { inserted: 0, updated: 0, skipped: 0, total: 0 };
4419
- if (!existsSync9(memoryDir)) return stats;
4420
- const files = readdirSync2(memoryDir).filter(
4445
+ if (!existsSync10(memoryDir)) return stats;
4446
+ const files = readdirSync3(memoryDir).filter(
4421
4447
  (f2) => f2.endsWith(".md") && f2 !== "MEMORY.md"
4422
4448
  );
4423
4449
  stats.total = files.length;
@@ -4632,7 +4658,7 @@ var init_template_engine = __esm({
4632
4658
  // src/lib/settings-local.ts
4633
4659
  import {
4634
4660
  closeSync as closeSync2,
4635
- existsSync as existsSync10,
4661
+ existsSync as existsSync11,
4636
4662
  fsyncSync as fsyncSync2,
4637
4663
  mkdirSync as mkdirSync7,
4638
4664
  openSync as openSync2,
@@ -4641,7 +4667,7 @@ import {
4641
4667
  rmSync as rmSync5,
4642
4668
  writeSync
4643
4669
  } from "fs";
4644
- import { dirname as dirname8, resolve as resolve5 } from "path";
4670
+ import { dirname as dirname8, resolve as resolve6 } from "path";
4645
4671
  function atomicWriteFile(targetPath, content, mode = 420) {
4646
4672
  const tmpPath = `${targetPath}.${process.pid}.tmp`;
4647
4673
  try {
@@ -4655,7 +4681,7 @@ function atomicWriteFile(targetPath, content, mode = 420) {
4655
4681
  }
4656
4682
  renameSync(tmpPath, targetPath);
4657
4683
  } catch (err) {
4658
- if (existsSync10(tmpPath)) {
4684
+ if (existsSync11(tmpPath)) {
4659
4685
  try {
4660
4686
  rmSync5(tmpPath, { force: true });
4661
4687
  } catch {
@@ -4665,7 +4691,7 @@ function atomicWriteFile(targetPath, content, mode = 420) {
4665
4691
  }
4666
4692
  }
4667
4693
  function readSettingsAtPath(absolutePath) {
4668
- if (!existsSync10(absolutePath)) {
4694
+ if (!existsSync11(absolutePath)) {
4669
4695
  return {};
4670
4696
  }
4671
4697
  try {
@@ -4680,12 +4706,12 @@ function readSettingsAtPath(absolutePath) {
4680
4706
  }
4681
4707
  }
4682
4708
  function readSettingsLocal(claudeDir) {
4683
- return readSettingsAtPath(resolve5(claudeDir, "settings.local.json"));
4709
+ return readSettingsAtPath(resolve6(claudeDir, "settings.local.json"));
4684
4710
  }
4685
4711
  function writeSettingsLocalAtomic(claudeDir, settings) {
4686
- const targetPath = resolve5(claudeDir, "settings.local.json");
4712
+ const targetPath = resolve6(claudeDir, "settings.local.json");
4687
4713
  const dir = dirname8(targetPath);
4688
- if (!existsSync10(dir)) {
4714
+ if (!existsSync11(dir)) {
4689
4715
  mkdirSync7(dir, { recursive: true });
4690
4716
  }
4691
4717
  const content = JSON.stringify(settings, null, 2) + "\n";
@@ -4699,7 +4725,7 @@ var init_settings_local = __esm({
4699
4725
 
4700
4726
  // src/permissions.ts
4701
4727
  import { createHash as createHash2 } from "crypto";
4702
- import { homedir as homedir4 } from "os";
4728
+ import { homedir as homedir5 } from "os";
4703
4729
  import { join as join7 } from "path";
4704
4730
  function canonicalJson(value) {
4705
4731
  if (value === null || typeof value !== "object") {
@@ -4739,7 +4765,7 @@ function detectInvalidDefaultMode(settings) {
4739
4765
  return { invalid: false };
4740
4766
  }
4741
4767
  function readGlobalSettings() {
4742
- return readSettingsAtPath(join7(homedir4(), ".claude", "settings.json"));
4768
+ return readSettingsAtPath(join7(homedir5(), ".claude", "settings.json"));
4743
4769
  }
4744
4770
  function mergedPermissionState(global2, local, canonical2) {
4745
4771
  const globalPerm = global2.permissions ?? {};
@@ -4927,20 +4953,20 @@ __export(install_commands_exports, {
4927
4953
  syncDirectory: () => syncDirectory
4928
4954
  });
4929
4955
  import {
4930
- existsSync as existsSync11,
4956
+ existsSync as existsSync12,
4931
4957
  readFileSync as readFileSync8,
4932
4958
  mkdirSync as mkdirSync8,
4933
- readdirSync as readdirSync3,
4934
- statSync as statSync2
4959
+ readdirSync as readdirSync4,
4960
+ statSync as statSync3
4935
4961
  } from "fs";
4936
- import { resolve as resolve6, dirname as dirname9, join as join8 } from "path";
4962
+ import { resolve as resolve7, dirname as dirname9, join as join8 } from "path";
4937
4963
  import { fileURLToPath as fileURLToPath2 } from "url";
4938
4964
  import { createHash as createHash3 } from "crypto";
4939
4965
  function dirHoldsAssets(dir) {
4940
- if (!existsSync11(dir)) return false;
4966
+ if (!existsSync12(dir)) return false;
4941
4967
  try {
4942
- return readdirSync3(dir).some(
4943
- (e2) => e2.endsWith(".md") || statSync2(resolve6(dir, e2)).isDirectory()
4968
+ return readdirSync4(dir).some(
4969
+ (e2) => e2.endsWith(".md") || statSync3(resolve7(dir, e2)).isDirectory()
4944
4970
  );
4945
4971
  } catch {
4946
4972
  return false;
@@ -4950,11 +4976,11 @@ function resolveAssetDir(assetName) {
4950
4976
  const cwd = process.cwd();
4951
4977
  const candidates = [
4952
4978
  // 1. npm-installed: node_modules/@massu/core/{assetName}
4953
- resolve6(cwd, "node_modules/@massu/core", assetName),
4979
+ resolve7(cwd, "node_modules/@massu/core", assetName),
4954
4980
  // 2. Relative to compiled dist/cli.js → ../{assetName}
4955
- resolve6(__dirname, "..", assetName),
4981
+ resolve7(__dirname, "..", assetName),
4956
4982
  // 3. Relative to source src/commands/ → ../../{assetName}
4957
- resolve6(__dirname, "../..", assetName)
4983
+ resolve7(__dirname, "../..", assetName)
4958
4984
  ];
4959
4985
  for (const c2 of candidates) {
4960
4986
  if (dirHoldsAssets(c2)) return c2;
@@ -4972,7 +4998,7 @@ function canonicalizeOwned(content) {
4972
4998
  }
4973
4999
  function massuOwnedSourceIsValid(resolvedSourcePath, content) {
4974
5000
  const sidecar = `${resolvedSourcePath}.sha256`;
4975
- if (!existsSync11(sidecar)) return false;
5001
+ if (!existsSync12(sidecar)) return false;
4976
5002
  let expected;
4977
5003
  try {
4978
5004
  expected = readFileSync8(sidecar, "utf-8").trim();
@@ -4985,8 +5011,8 @@ function hashContent(content) {
4985
5011
  return createHash3("sha256").update(content, "utf-8").digest("hex");
4986
5012
  }
4987
5013
  function loadManifest(claudeDir) {
4988
- const path = resolve6(claudeDir, MANIFEST_RELPATH);
4989
- if (!existsSync11(path)) {
5014
+ const path = resolve7(claudeDir, MANIFEST_RELPATH);
5015
+ if (!existsSync12(path)) {
4990
5016
  return emptyManifest();
4991
5017
  }
4992
5018
  try {
@@ -5001,11 +5027,11 @@ function loadManifest(claudeDir) {
5001
5027
  }
5002
5028
  }
5003
5029
  function saveManifest(claudeDir, manifest) {
5004
- const dir = resolve6(claudeDir, ".massu");
5005
- if (!existsSync11(dir)) {
5030
+ const dir = resolve7(claudeDir, ".massu");
5031
+ if (!existsSync12(dir)) {
5006
5032
  mkdirSync8(dir, { recursive: true });
5007
5033
  }
5008
- const finalPath = resolve6(dir, "install-manifest.json");
5034
+ const finalPath = resolve7(dir, "install-manifest.json");
5009
5035
  manifest.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
5010
5036
  atomicWriteFile(finalPath, JSON.stringify(manifest, null, 2));
5011
5037
  }
@@ -5067,18 +5093,18 @@ function pickVariant(baseName, sourceDir, framework) {
5067
5093
  }
5068
5094
  for (const cand of candidates) {
5069
5095
  if (cand.subFramework) {
5070
- const subPath = resolve6(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
5071
- if (existsSync11(subPath)) {
5096
+ const subPath = resolve7(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
5097
+ if (existsSync12(subPath)) {
5072
5098
  return { kind: "hit", suffix: `.${cand.lang}-${cand.subFramework}` };
5073
5099
  }
5074
5100
  }
5075
- const langPath = resolve6(sourceDir, `${baseName}.${cand.lang}.md`);
5076
- if (existsSync11(langPath)) {
5101
+ const langPath = resolve7(sourceDir, `${baseName}.${cand.lang}.md`);
5102
+ if (existsSync12(langPath)) {
5077
5103
  return { kind: "hit", suffix: `.${cand.lang}` };
5078
5104
  }
5079
5105
  }
5080
- const defaultPath = resolve6(sourceDir, `${baseName}.md`);
5081
- if (existsSync11(defaultPath)) {
5106
+ const defaultPath = resolve7(sourceDir, `${baseName}.md`);
5107
+ if (existsSync12(defaultPath)) {
5082
5108
  return { kind: "hit", suffix: "" };
5083
5109
  }
5084
5110
  if (framework.type === "multi" && !framework.primary) {
@@ -5094,15 +5120,15 @@ function isVariantFilename(entry) {
5094
5120
  }
5095
5121
  function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPrefix, topLevel = true, templateVars = {}) {
5096
5122
  const stats = { installed: 0, updated: 0, skipped: 0, kept: 0 };
5097
- if (!existsSync11(targetDir)) {
5123
+ if (!existsSync12(targetDir)) {
5098
5124
  mkdirSync8(targetDir, { recursive: true });
5099
5125
  }
5100
- const entries = readdirSync3(sourceDir);
5126
+ const entries = readdirSync4(sourceDir);
5101
5127
  for (const entry of entries) {
5102
- const sourcePath = resolve6(sourceDir, entry);
5103
- const entryStat = statSync2(sourcePath);
5128
+ const sourcePath = resolve7(sourceDir, entry);
5129
+ const entryStat = statSync3(sourcePath);
5104
5130
  if (entryStat.isDirectory()) {
5105
- const subTargetDir = resolve6(targetDir, entry);
5131
+ const subTargetDir = resolve7(targetDir, entry);
5106
5132
  const subPrefix = manifestKeyPrefix === "" ? entry : `${manifestKeyPrefix}/${entry}`;
5107
5133
  const subStats = syncDirectory(
5108
5134
  sourcePath,
@@ -5131,12 +5157,12 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
5131
5157
  const suffix = choice.kind === "hit" ? choice.suffix : "";
5132
5158
  sourceFilename = suffix === "" ? `${baseName}.md` : `${baseName}${suffix}.md`;
5133
5159
  }
5134
- const resolvedSourcePath = resolve6(sourceDir, sourceFilename);
5135
- if (!existsSync11(resolvedSourcePath)) {
5160
+ const resolvedSourcePath = resolve7(sourceDir, sourceFilename);
5161
+ if (!existsSync12(resolvedSourcePath)) {
5136
5162
  continue;
5137
5163
  }
5138
5164
  const targetFilename = topLevel ? `${baseName}.md` : entry;
5139
- const targetPath = resolve6(targetDir, targetFilename);
5165
+ const targetPath = resolve7(targetDir, targetFilename);
5140
5166
  const rawContent = readFileSync8(resolvedSourcePath, "utf-8");
5141
5167
  let sourceContent;
5142
5168
  try {
@@ -5155,10 +5181,10 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
5155
5181
  const sourceHash = hashContent(sourceContent);
5156
5182
  const manifestKey = manifestKeyPrefix === "" ? targetFilename : `${manifestKeyPrefix}/${targetFilename}`;
5157
5183
  const lastInstalledHash = manifest.entries[manifestKey];
5158
- if (existsSync11(targetPath)) {
5184
+ if (existsSync12(targetPath)) {
5159
5185
  let existingContent;
5160
5186
  try {
5161
- if (!statSync2(targetPath).isFile()) throw new Error("not a regular file");
5187
+ if (!statSync3(targetPath).isFile()) throw new Error("not a regular file");
5162
5188
  existingContent = readFileSync8(targetPath, "utf-8");
5163
5189
  } catch (err) {
5164
5190
  process.stderr.write(
@@ -5240,9 +5266,9 @@ function buildTemplateVars() {
5240
5266
  }
5241
5267
  function installCommands(projectRoot, opts = {}) {
5242
5268
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
5243
- const claudeDir = resolve6(projectRoot, claudeDirName);
5244
- const targetDir = resolve6(claudeDir, "commands");
5245
- if (!existsSync11(targetDir)) {
5269
+ const claudeDir = resolve7(projectRoot, claudeDirName);
5270
+ const targetDir = resolve7(claudeDir, "commands");
5271
+ if (!existsSync12(targetDir)) {
5246
5272
  mkdirSync8(targetDir, { recursive: true });
5247
5273
  }
5248
5274
  const sourceDir = resolveAssetDir("commands");
@@ -5272,7 +5298,7 @@ function installCommands(projectRoot, opts = {}) {
5272
5298
  }
5273
5299
  function installAll(projectRoot, opts = {}) {
5274
5300
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
5275
- const claudeDir = resolve6(projectRoot, claudeDirName);
5301
+ const claudeDir = resolve7(projectRoot, claudeDirName);
5276
5302
  const assets = {};
5277
5303
  let totalInstalled = 0;
5278
5304
  let totalUpdated = 0;
@@ -5285,7 +5311,7 @@ function installAll(projectRoot, opts = {}) {
5285
5311
  for (const assetType of ASSET_TYPES) {
5286
5312
  const sourceDir = resolveAssetDir(assetType.name);
5287
5313
  if (!sourceDir) continue;
5288
- const targetDir = resolve6(claudeDir, assetType.targetSubdir);
5314
+ const targetDir = resolve7(claudeDir, assetType.targetSubdir);
5289
5315
  const stats = syncDirectory(
5290
5316
  sourceDir,
5291
5317
  targetDir,
@@ -5594,15 +5620,15 @@ var init_manifest_registry = __esm({
5594
5620
  });
5595
5621
 
5596
5622
  // src/detect/package-detector.ts
5597
- import { readFileSync as readFileSync9, existsSync as existsSync12, statSync as statSync3, lstatSync, readdirSync as readdirSync4 } from "fs";
5623
+ import { readFileSync as readFileSync9, existsSync as existsSync13, statSync as statSync4, lstatSync, readdirSync as readdirSync5 } from "fs";
5598
5624
  import { join as join9, relative as relative2 } from "path";
5599
5625
  import { parse as parseToml } from "smol-toml";
5600
5626
  function safeRead(path) {
5601
5627
  try {
5602
- if (!existsSync12(path)) return null;
5628
+ if (!existsSync13(path)) return null;
5603
5629
  const ls = lstatSync(path);
5604
5630
  if (ls.isSymbolicLink()) return null;
5605
- const st = statSync3(path);
5631
+ const st = statSync4(path);
5606
5632
  if (!st.isFile()) return null;
5607
5633
  return readFileSync9(path, "utf-8");
5608
5634
  } catch {
@@ -5635,7 +5661,7 @@ function parsePackageJson(path, directory, root, warnings) {
5635
5661
  const peer = Object.keys(
5636
5662
  pkg.peerDependencies ?? {}
5637
5663
  );
5638
- const hasTs = deps.includes("typescript") || devDeps.includes("typescript") || existsSync12(join9(directory, "tsconfig.json"));
5664
+ const hasTs = deps.includes("typescript") || devDeps.includes("typescript") || existsSync13(join9(directory, "tsconfig.json"));
5639
5665
  const language = hasTs ? "typescript" : "javascript";
5640
5666
  const scripts = Object.keys(
5641
5667
  pkg.scripts ?? {}
@@ -6062,13 +6088,13 @@ function detectManifestsInDir(dir, root, warnings) {
6062
6088
  for (const entry of getManifestRegistry2()) {
6063
6089
  if (!entry.pattern.startsWith("*")) {
6064
6090
  const path = join9(dir, entry.pattern);
6065
- if (!existsSync12(path)) continue;
6091
+ if (!existsSync13(path)) continue;
6066
6092
  const m3 = entry.parse(path, dir, root, warnings);
6067
6093
  if (m3 !== null) out.push(m3);
6068
6094
  } else {
6069
6095
  if (dirEntries === null) {
6070
6096
  try {
6071
- dirEntries = readdirSync4(dir);
6097
+ dirEntries = readdirSync5(dir);
6072
6098
  } catch {
6073
6099
  dirEntries = [];
6074
6100
  }
@@ -6076,7 +6102,7 @@ function detectManifestsInDir(dir, root, warnings) {
6076
6102
  for (const fname of dirEntries) {
6077
6103
  if (!matchManifestPattern2(fname, entry.pattern)) continue;
6078
6104
  const path = join9(dir, fname);
6079
- if (!existsSync12(path)) continue;
6105
+ if (!existsSync13(path)) continue;
6080
6106
  const m3 = entry.parse(path, dir, root, warnings);
6081
6107
  if (m3 !== null) out.push(m3);
6082
6108
  }
@@ -6086,7 +6112,7 @@ function detectManifestsInDir(dir, root, warnings) {
6086
6112
  }
6087
6113
  function listSubdirs(dir) {
6088
6114
  try {
6089
- return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS.has(e2.name)).map((e2) => join9(dir, e2.name));
6115
+ return readdirSync5(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS.has(e2.name)).map((e2) => join9(dir, e2.name));
6090
6116
  } catch {
6091
6117
  return [];
6092
6118
  }
@@ -6097,7 +6123,7 @@ function detectPackageManifests(projectRoot) {
6097
6123
  manifests.push(...detectManifestsInDir(projectRoot, projectRoot, warnings));
6098
6124
  for (const ws of WORKSPACE_DIRS) {
6099
6125
  const wsRoot = join9(projectRoot, ws);
6100
- if (!existsSync12(wsRoot)) continue;
6126
+ if (!existsSync13(wsRoot)) continue;
6101
6127
  for (const sub of listSubdirs(wsRoot)) {
6102
6128
  manifests.push(...detectManifestsInDir(sub, projectRoot, warnings));
6103
6129
  for (const sub2 of listSubdirs(sub)) {
@@ -6349,22 +6375,22 @@ import {
6349
6375
  openSync as openSync3,
6350
6376
  fsyncSync as fsyncSync3,
6351
6377
  closeSync as closeSync3,
6352
- existsSync as existsSync13,
6378
+ existsSync as existsSync14,
6353
6379
  realpathSync,
6354
6380
  unlinkSync as unlinkSync2,
6355
6381
  accessSync as accessSync2,
6356
- statSync as statSync4,
6382
+ statSync as statSync5,
6357
6383
  chmodSync as chmodSync3,
6358
6384
  constants
6359
6385
  } from "fs";
6360
- import { dirname as dirname10, resolve as resolve7, relative as relative3, isAbsolute, basename as basename3, sep } from "path";
6386
+ import { dirname as dirname10, resolve as resolve8, relative as relative3, isAbsolute as isAbsolute2, basename as basename3, sep } from "path";
6361
6387
  import { createHash as createHash4 } from "crypto";
6362
6388
  function assertContainedIn(rootDir, candidate, opts = {}) {
6363
6389
  if (candidate.includes("\0")) {
6364
6390
  throw new PathEscapeError("path contains a NUL byte");
6365
6391
  }
6366
- const root = resolve7(rootDir);
6367
- const abs = resolve7(root, candidate);
6392
+ const root = resolve8(rootDir);
6393
+ const abs = resolve8(root, candidate);
6368
6394
  if (!opts.allowNested) {
6369
6395
  const rel = relative3(root, abs);
6370
6396
  if (rel.includes(sep)) {
@@ -6383,13 +6409,13 @@ function assertContainedIn(rootDir, candidate, opts = {}) {
6383
6409
  }
6384
6410
  function isContainedIn(rootDir, candidate, opts = {}) {
6385
6411
  if (candidate.includes("\0")) return false;
6386
- const root = resolve7(rootDir);
6387
- const abs = resolve7(root, candidate);
6412
+ const root = resolve8(rootDir);
6413
+ const abs = resolve8(root, candidate);
6388
6414
  const rel = relative3(root, abs);
6389
6415
  if (rel === "" && !opts.allowRoot) return false;
6390
- if (rel.startsWith("..") || isAbsolute(rel)) return false;
6416
+ if (rel.startsWith("..") || isAbsolute2(rel)) return false;
6391
6417
  let probe = abs;
6392
- while (!existsSync13(probe) && probe.length > 1) {
6418
+ while (!existsSync14(probe) && probe.length > 1) {
6393
6419
  const parent = dirname10(probe);
6394
6420
  if (parent === probe) break;
6395
6421
  probe = parent;
@@ -6404,15 +6430,15 @@ function isContainedIn(rootDir, candidate, opts = {}) {
6404
6430
  }
6405
6431
  const realRel = relative3(realRoot, realProbe);
6406
6432
  if (realRel === "") return true;
6407
- return !realRel.startsWith("..") && !isAbsolute(realRel);
6433
+ return !realRel.startsWith("..") && !isAbsolute2(realRel);
6408
6434
  }
6409
6435
  function atomicWriteFileSync(destPath, contents) {
6410
6436
  const dir = dirname10(destPath);
6411
- const tmp = resolve7(dir, `.${basename3(destPath)}.massu-tmp-${process.pid}`);
6437
+ const tmp = resolve8(dir, `.${basename3(destPath)}.massu-tmp-${process.pid}`);
6412
6438
  let destMode;
6413
- if (existsSync13(destPath)) {
6439
+ if (existsSync14(destPath)) {
6414
6440
  accessSync2(destPath, constants.W_OK);
6415
- destMode = statSync4(destPath).mode & 511;
6441
+ destMode = statSync5(destPath).mode & 511;
6416
6442
  }
6417
6443
  let fd;
6418
6444
  try {
@@ -6431,7 +6457,7 @@ function atomicWriteFileSync(destPath, contents) {
6431
6457
  }
6432
6458
  }
6433
6459
  try {
6434
- if (existsSync13(tmp)) unlinkSync2(tmp);
6460
+ if (existsSync14(tmp)) unlinkSync2(tmp);
6435
6461
  } catch {
6436
6462
  }
6437
6463
  throw err;
@@ -9938,7 +9964,7 @@ var require_pattern = __commonJS({
9938
9964
  const absolute = [];
9939
9965
  const relative15 = [];
9940
9966
  for (const pattern of patterns) {
9941
- if (isAbsolute5(pattern)) {
9967
+ if (isAbsolute6(pattern)) {
9942
9968
  absolute.push(pattern);
9943
9969
  } else {
9944
9970
  relative15.push(pattern);
@@ -9947,10 +9973,10 @@ var require_pattern = __commonJS({
9947
9973
  return [absolute, relative15];
9948
9974
  }
9949
9975
  exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
9950
- function isAbsolute5(pattern) {
9976
+ function isAbsolute6(pattern) {
9951
9977
  return path.isAbsolute(pattern);
9952
9978
  }
9953
- exports.isAbsolute = isAbsolute5;
9979
+ exports.isAbsolute = isAbsolute6;
9954
9980
  }
9955
9981
  });
9956
9982
 
@@ -10365,11 +10391,11 @@ var require_out = __commonJS({
10365
10391
  async.read(path, getSettings(optionsOrSettingsOrCallback), callback);
10366
10392
  }
10367
10393
  exports.stat = stat;
10368
- function statSync21(path, optionsOrSettings) {
10394
+ function statSync22(path, optionsOrSettings) {
10369
10395
  const settings = getSettings(optionsOrSettings);
10370
10396
  return sync.read(path, settings);
10371
10397
  }
10372
- exports.statSync = statSync21;
10398
+ exports.statSync = statSync22;
10373
10399
  function getSettings(settingsOrOptions = {}) {
10374
10400
  if (settingsOrOptions instanceof settings_1.default) {
10375
10401
  return settingsOrOptions;
@@ -11034,41 +11060,41 @@ var require_queue = __commonJS({
11034
11060
  queue.drained = drained;
11035
11061
  return queue;
11036
11062
  function push(value) {
11037
- var p19 = new Promise(function(resolve45, reject) {
11063
+ var p19 = new Promise(function(resolve46, reject) {
11038
11064
  pushCb(value, function(err, result) {
11039
11065
  if (err) {
11040
11066
  reject(err);
11041
11067
  return;
11042
11068
  }
11043
- resolve45(result);
11069
+ resolve46(result);
11044
11070
  });
11045
11071
  });
11046
11072
  p19.catch(noop);
11047
11073
  return p19;
11048
11074
  }
11049
11075
  function unshift(value) {
11050
- var p19 = new Promise(function(resolve45, reject) {
11076
+ var p19 = new Promise(function(resolve46, reject) {
11051
11077
  unshiftCb(value, function(err, result) {
11052
11078
  if (err) {
11053
11079
  reject(err);
11054
11080
  return;
11055
11081
  }
11056
- resolve45(result);
11082
+ resolve46(result);
11057
11083
  });
11058
11084
  });
11059
11085
  p19.catch(noop);
11060
11086
  return p19;
11061
11087
  }
11062
11088
  function drained() {
11063
- var p19 = new Promise(function(resolve45) {
11089
+ var p19 = new Promise(function(resolve46) {
11064
11090
  process.nextTick(function() {
11065
11091
  if (queue.idle()) {
11066
- resolve45();
11092
+ resolve46();
11067
11093
  } else {
11068
11094
  var previousDrain = queue.drain;
11069
11095
  queue.drain = function() {
11070
11096
  if (typeof previousDrain === "function") previousDrain();
11071
- resolve45();
11097
+ resolve46();
11072
11098
  queue.drain = previousDrain;
11073
11099
  };
11074
11100
  }
@@ -11554,9 +11580,9 @@ var require_stream3 = __commonJS({
11554
11580
  });
11555
11581
  }
11556
11582
  _getStat(filepath) {
11557
- return new Promise((resolve45, reject) => {
11583
+ return new Promise((resolve46, reject) => {
11558
11584
  this._stat(filepath, this._fsStatSettings, (error, stats) => {
11559
- return error === null ? resolve45(stats) : reject(error);
11585
+ return error === null ? resolve46(stats) : reject(error);
11560
11586
  });
11561
11587
  });
11562
11588
  }
@@ -11580,10 +11606,10 @@ var require_async5 = __commonJS({
11580
11606
  this._readerStream = new stream_1.default(this._settings);
11581
11607
  }
11582
11608
  dynamic(root, options) {
11583
- return new Promise((resolve45, reject) => {
11609
+ return new Promise((resolve46, reject) => {
11584
11610
  this._walkAsync(root, options, (error, entries) => {
11585
11611
  if (error === null) {
11586
- resolve45(entries);
11612
+ resolve46(entries);
11587
11613
  } else {
11588
11614
  reject(error);
11589
11615
  }
@@ -11593,10 +11619,10 @@ var require_async5 = __commonJS({
11593
11619
  async static(patterns, options) {
11594
11620
  const entries = [];
11595
11621
  const stream = this._readerStream.static(patterns, options);
11596
- return new Promise((resolve45, reject) => {
11622
+ return new Promise((resolve46, reject) => {
11597
11623
  stream.once("error", reject);
11598
11624
  stream.on("data", (entry) => entries.push(entry));
11599
- stream.once("end", () => resolve45(entries));
11625
+ stream.once("end", () => resolve46(entries));
11600
11626
  });
11601
11627
  }
11602
11628
  };
@@ -12442,16 +12468,16 @@ var init_source_dir_detector = __esm({
12442
12468
  });
12443
12469
 
12444
12470
  // src/detect/monorepo-detector.ts
12445
- import { readFileSync as readFileSync10, existsSync as existsSync14, statSync as statSync5, lstatSync as lstatSync2, readdirSync as readdirSync5 } from "fs";
12471
+ import { readFileSync as readFileSync10, existsSync as existsSync15, statSync as statSync6, lstatSync as lstatSync2, readdirSync as readdirSync6 } from "fs";
12446
12472
  import { join as join10, relative as relative4 } from "path";
12447
12473
  import { parse as parseYaml3 } from "yaml";
12448
12474
  import { parse as parseToml2 } from "smol-toml";
12449
12475
  function safeReadText(path) {
12450
12476
  try {
12451
- if (!existsSync14(path)) return null;
12477
+ if (!existsSync15(path)) return null;
12452
12478
  const ls = lstatSync2(path);
12453
12479
  if (ls.isSymbolicLink()) return null;
12454
- const st = statSync5(path);
12480
+ const st = statSync6(path);
12455
12481
  if (!st.isFile()) return null;
12456
12482
  return readFileSync10(path, "utf-8");
12457
12483
  } catch {
@@ -12460,7 +12486,7 @@ function safeReadText(path) {
12460
12486
  }
12461
12487
  function firstManifestIn(dir) {
12462
12488
  for (const m3 of MANIFEST_PRIORITY) {
12463
- if (existsSync14(join10(dir, m3))) return m3;
12489
+ if (existsSync15(join10(dir, m3))) return m3;
12464
12490
  }
12465
12491
  return null;
12466
12492
  }
@@ -12516,7 +12542,7 @@ function pkgFromDir(root, dir) {
12516
12542
  }
12517
12543
  function listSubdirs2(dir) {
12518
12544
  try {
12519
- return readdirSync5(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS2.has(e2.name)).map((e2) => join10(dir, e2.name));
12545
+ return readdirSync6(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS2.has(e2.name)).map((e2) => join10(dir, e2.name));
12520
12546
  } catch {
12521
12547
  return [];
12522
12548
  }
@@ -12525,7 +12551,7 @@ function genericWorkspaces(root) {
12525
12551
  const out = [];
12526
12552
  for (const parent of CONVENTIONAL_WORKSPACE_PARENTS) {
12527
12553
  const p19 = join10(root, parent);
12528
- if (!existsSync14(p19)) continue;
12554
+ if (!existsSync15(p19)) continue;
12529
12555
  for (const sub of listSubdirs2(p19)) {
12530
12556
  const pkg = pkgFromDir(root, sub);
12531
12557
  if (pkg) out.push(pkg);
@@ -12566,7 +12592,7 @@ function expandWorkspaceGlobs(root, globs) {
12566
12592
  const parts = pattern.split("/");
12567
12593
  if (parts.length === 2 && (parts[1] === "*" || parts[1] === "**")) {
12568
12594
  const parent = join10(root, parts[0]);
12569
- if (!existsSync14(parent)) continue;
12595
+ if (!existsSync15(parent)) continue;
12570
12596
  for (const sub of listSubdirs2(parent)) {
12571
12597
  const pkg = pkgFromDir(root, sub);
12572
12598
  if (pkg && !seen.has(pkg.path)) {
@@ -12577,7 +12603,7 @@ function expandWorkspaceGlobs(root, globs) {
12577
12603
  continue;
12578
12604
  }
12579
12605
  const direct = join10(root, pattern);
12580
- if (existsSync14(direct)) {
12606
+ if (existsSync15(direct)) {
12581
12607
  const pkg = pkgFromDir(root, direct);
12582
12608
  if (pkg && !seen.has(pkg.path)) {
12583
12609
  seen.add(pkg.path);
@@ -12588,16 +12614,16 @@ function expandWorkspaceGlobs(root, globs) {
12588
12614
  return out;
12589
12615
  }
12590
12616
  function hasTurbo(root) {
12591
- return existsSync14(join10(root, "turbo.json"));
12617
+ return existsSync15(join10(root, "turbo.json"));
12592
12618
  }
12593
12619
  function hasNx(root) {
12594
- return existsSync14(join10(root, "nx.json"));
12620
+ return existsSync15(join10(root, "nx.json"));
12595
12621
  }
12596
12622
  function hasLerna(root) {
12597
- return existsSync14(join10(root, "lerna.json"));
12623
+ return existsSync15(join10(root, "lerna.json"));
12598
12624
  }
12599
12625
  function hasBazel(root) {
12600
- return existsSync14(join10(root, "WORKSPACE")) || existsSync14(join10(root, "WORKSPACE.bazel")) || existsSync14(join10(root, "MODULE.bazel"));
12626
+ return existsSync15(join10(root, "WORKSPACE")) || existsSync15(join10(root, "WORKSPACE.bazel")) || existsSync15(join10(root, "MODULE.bazel"));
12601
12627
  }
12602
12628
  function detectMonorepo(projectRoot) {
12603
12629
  const nested = [];
@@ -12788,7 +12814,7 @@ var init_vr_command_map = __esm({
12788
12814
  });
12789
12815
 
12790
12816
  // src/detect/domain-inferrer.ts
12791
- import { existsSync as existsSync15, readdirSync as readdirSync6 } from "fs";
12817
+ import { existsSync as existsSync16, readdirSync as readdirSync7 } from "fs";
12792
12818
  import { join as join11 } from "path";
12793
12819
  function titleCase(s) {
12794
12820
  if (!s) return s;
@@ -12810,9 +12836,9 @@ function topLevelSrcSubdirs(root, sourceDirs) {
12810
12836
  const seen = /* @__PURE__ */ new Set();
12811
12837
  for (const rel of effective) {
12812
12838
  const abs = join11(root, rel);
12813
- if (!existsSync15(abs)) continue;
12839
+ if (!existsSync16(abs)) continue;
12814
12840
  try {
12815
- for (const e2 of readdirSync6(abs, { withFileTypes: true })) {
12841
+ for (const e2 of readdirSync7(abs, { withFileTypes: true })) {
12816
12842
  if (!e2.isDirectory()) continue;
12817
12843
  if (IGNORED_SUBDIRS.has(e2.name)) continue;
12818
12844
  seen.add(e2.name);
@@ -12897,8 +12923,8 @@ var init_domain_inferrer = __esm({
12897
12923
  });
12898
12924
 
12899
12925
  // src/detect/regex-fallback.ts
12900
- import { existsSync as existsSync16, readdirSync as readdirSync7, readFileSync as readFileSync11, statSync as statSync6 } from "fs";
12901
- import { resolve as resolve8, join as join12, basename as basename4 } from "path";
12926
+ import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync11, statSync as statSync7 } from "fs";
12927
+ import { resolve as resolve9, join as join12, basename as basename4 } from "path";
12902
12928
  function introspectPython(detection, projectRoot) {
12903
12929
  const sourceDir = resolveSourceDir(detection, "python", projectRoot);
12904
12930
  if (!sourceDir) return null;
@@ -13064,10 +13090,10 @@ function resolveSourceDir(detection, lang, projectRoot) {
13064
13090
  const list = info?.source_dirs ?? [];
13065
13091
  if (list.length > 0) {
13066
13092
  const first = list[0];
13067
- const abs = resolve8(projectRoot, first);
13068
- return existsSync16(abs) ? abs : null;
13093
+ const abs = resolve9(projectRoot, first);
13094
+ return existsSync17(abs) ? abs : null;
13069
13095
  }
13070
- return existsSync16(projectRoot) ? projectRoot : null;
13096
+ return existsSync17(projectRoot) ? projectRoot : null;
13071
13097
  }
13072
13098
  function sampleFiles(dir, nameRegex, pathFilter) {
13073
13099
  const out = [];
@@ -13077,7 +13103,7 @@ function sampleFiles(dir, nameRegex, pathFilter) {
13077
13103
  if (depth > MAX_DIR_DEPTH) continue;
13078
13104
  let entries;
13079
13105
  try {
13080
- entries = readdirSync7(path);
13106
+ entries = readdirSync8(path);
13081
13107
  } catch {
13082
13108
  continue;
13083
13109
  }
@@ -13090,7 +13116,7 @@ function sampleFiles(dir, nameRegex, pathFilter) {
13090
13116
  const child = join12(path, entry);
13091
13117
  let st;
13092
13118
  try {
13093
- st = statSync6(child);
13119
+ st = statSync7(child);
13094
13120
  } catch {
13095
13121
  continue;
13096
13122
  }
@@ -13110,7 +13136,7 @@ function sampleFiles(dir, nameRegex, pathFilter) {
13110
13136
  }
13111
13137
  function readSafe(path) {
13112
13138
  try {
13113
- const st = statSync6(path);
13139
+ const st = statSync7(path);
13114
13140
  if (st.size > MAX_FILE_BYTES) return null;
13115
13141
  return readFileSync11(path, "utf-8");
13116
13142
  } catch {
@@ -13193,7 +13219,7 @@ var init_parse_guard = __esm({
13193
13219
 
13194
13220
  // src/detect/adapters/runner.ts
13195
13221
  import { basename as basename5, relative as relative5 } from "path";
13196
- import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync7 } from "fs";
13222
+ import { existsSync as existsSync18, readdirSync as readdirSync9, readFileSync as readFileSync12, statSync as statSync8 } from "fs";
13197
13223
  import { join as join13 } from "path";
13198
13224
  async function runAdapters(adapters, rootDir, signals, options = {}) {
13199
13225
  const out = {
@@ -13288,10 +13314,10 @@ function buildDetectionSignals(rootDir) {
13288
13314
  const presentDirs = /* @__PURE__ */ new Set();
13289
13315
  const presentFiles = /* @__PURE__ */ new Set();
13290
13316
  try {
13291
- for (const entry of readdirSync8(rootDir)) {
13317
+ for (const entry of readdirSync9(rootDir)) {
13292
13318
  if (entry.startsWith(".")) continue;
13293
13319
  try {
13294
- const st = statSync7(join13(rootDir, entry));
13320
+ const st = statSync8(join13(rootDir, entry));
13295
13321
  if (st.isDirectory()) presentDirs.add(entry);
13296
13322
  else if (st.isFile()) presentFiles.add(entry);
13297
13323
  } catch {
@@ -13328,7 +13354,7 @@ function tryReadGradleBuild(rootDir, presentFiles) {
13328
13354
  return void 0;
13329
13355
  }
13330
13356
  function tryReadString(path) {
13331
- if (!existsSync17(path)) return void 0;
13357
+ if (!existsSync18(path)) return void 0;
13332
13358
  try {
13333
13359
  return readFileSync12(path, "utf-8");
13334
13360
  } catch {
@@ -13448,7 +13474,7 @@ ${querySource}`
13448
13474
  import { createHash as createHash5 } from "crypto";
13449
13475
  import {
13450
13476
  mkdirSync as mkdirSync9,
13451
- readdirSync as readdirSync9,
13477
+ readdirSync as readdirSync10,
13452
13478
  readFileSync as readFileSync13,
13453
13479
  writeFileSync as writeFileSync4,
13454
13480
  renameSync as renameSync3,
@@ -13457,11 +13483,11 @@ import {
13457
13483
  chmodSync as chmodSync4,
13458
13484
  utimesSync
13459
13485
  } from "fs";
13460
- import { homedir as homedir5 } from "os";
13486
+ import { homedir as homedir6 } from "os";
13461
13487
  import { dirname as dirname11, join as join14 } from "path";
13462
13488
  import { Language, Parser } from "web-tree-sitter";
13463
13489
  function getCacheDir() {
13464
- return process.env.MASSU_WASM_CACHE_DIR ?? join14(homedir5(), ".massu", "wasm-cache");
13490
+ return process.env.MASSU_WASM_CACHE_DIR ?? join14(homedir6(), ".massu", "wasm-cache");
13465
13491
  }
13466
13492
  function getCachedPath(language, sha) {
13467
13493
  return join14(getCacheDir(), `${language}-${sha}.wasm`);
@@ -13485,7 +13511,7 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
13485
13511
  const dir = getCacheDir();
13486
13512
  let entries;
13487
13513
  try {
13488
- entries = readdirSync9(dir);
13514
+ entries = readdirSync10(dir);
13489
13515
  } catch {
13490
13516
  return;
13491
13517
  }
@@ -14467,7 +14493,7 @@ __export(file_sampler_exports, {
14467
14493
  SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
14468
14494
  sampleFilesForAdapter: () => sampleFilesForAdapter
14469
14495
  });
14470
- import { readdirSync as readdirSync10, readFileSync as readFileSync14, lstatSync as lstatSync4 } from "node:fs";
14496
+ import { readdirSync as readdirSync11, readFileSync as readFileSync14, lstatSync as lstatSync4 } from "node:fs";
14471
14497
  import { join as join15, extname } from "node:path";
14472
14498
  function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
14473
14499
  const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
@@ -14499,7 +14525,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
14499
14525
  if (out.length >= maxFiles) return;
14500
14526
  let entries;
14501
14527
  try {
14502
- entries = readdirSync10(dir);
14528
+ entries = readdirSync11(dir);
14503
14529
  } catch {
14504
14530
  return;
14505
14531
  }
@@ -15952,10 +15978,10 @@ __export(init_exports, {
15952
15978
  validateWrittenConfig: () => validateWrittenConfig,
15953
15979
  writeConfigAtomic: () => writeConfigAtomic
15954
15980
  });
15955
- import { closeSync as closeSync4, existsSync as existsSync18, fsyncSync as fsyncSync4, openSync as openSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync5, writeSync as writeSync2, mkdirSync as mkdirSync10, readdirSync as readdirSync11, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync9, chmodSync as chmodSync5 } from "fs";
15956
- import { resolve as resolve9, basename as basename6, dirname as dirname12 } from "path";
15981
+ import { closeSync as closeSync4, existsSync as existsSync19, fsyncSync as fsyncSync4, openSync as openSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync5, writeSync as writeSync2, mkdirSync as mkdirSync10, readdirSync as readdirSync12, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync10, chmodSync as chmodSync5 } from "fs";
15982
+ import { resolve as resolve10, basename as basename6, dirname as dirname12 } from "path";
15957
15983
  import { fileURLToPath as fileURLToPath3 } from "url";
15958
- import { homedir as homedir6 } from "os";
15984
+ import { homedir as homedir7 } from "os";
15959
15985
  import { stringify as yamlStringify, parse as yamlParse } from "yaml";
15960
15986
  function detectFramework(projectRoot) {
15961
15987
  const result = {
@@ -15964,8 +15990,8 @@ function detectFramework(projectRoot) {
15964
15990
  orm: "none",
15965
15991
  ui: "none"
15966
15992
  };
15967
- const pkgPath = resolve9(projectRoot, "package.json");
15968
- if (!existsSync18(pkgPath)) return result;
15993
+ const pkgPath = resolve10(projectRoot, "package.json");
15994
+ if (!existsSync19(pkgPath)) return result;
15969
15995
  try {
15970
15996
  const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
15971
15997
  const allDeps = {
@@ -16001,7 +16027,7 @@ function detectPython(projectRoot) {
16001
16027
  alembicDir: null
16002
16028
  };
16003
16029
  const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
16004
- const hasMarker = markers.some((m3) => existsSync18(resolve9(projectRoot, m3)));
16030
+ const hasMarker = markers.some((m3) => existsSync19(resolve10(projectRoot, m3)));
16005
16031
  if (!hasMarker) return result;
16006
16032
  result.detected = true;
16007
16033
  const depFiles = [
@@ -16011,8 +16037,8 @@ function detectPython(projectRoot) {
16011
16037
  { file: "Pipfile" }
16012
16038
  ];
16013
16039
  for (const { file } of depFiles) {
16014
- const filePath = resolve9(projectRoot, file);
16015
- if (existsSync18(filePath)) {
16040
+ const filePath = resolve10(projectRoot, file);
16041
+ if (existsSync19(filePath)) {
16016
16042
  try {
16017
16043
  const content = readFileSync15(filePath, "utf-8").toLowerCase();
16018
16044
  if (content.includes("fastapi")) result.hasFastapi = true;
@@ -16021,25 +16047,25 @@ function detectPython(projectRoot) {
16021
16047
  }
16022
16048
  }
16023
16049
  }
16024
- if (existsSync18(resolve9(projectRoot, "alembic.ini"))) {
16050
+ if (existsSync19(resolve10(projectRoot, "alembic.ini"))) {
16025
16051
  result.hasAlembic = true;
16026
- if (existsSync18(resolve9(projectRoot, "alembic"))) {
16052
+ if (existsSync19(resolve10(projectRoot, "alembic"))) {
16027
16053
  result.alembicDir = "alembic";
16028
16054
  }
16029
- } else if (existsSync18(resolve9(projectRoot, "alembic"))) {
16055
+ } else if (existsSync19(resolve10(projectRoot, "alembic"))) {
16030
16056
  result.hasAlembic = true;
16031
16057
  result.alembicDir = "alembic";
16032
16058
  }
16033
16059
  const candidateRoots = ["app", "src", "backend", "api"];
16034
16060
  for (const candidate of candidateRoots) {
16035
- const candidatePath = resolve9(projectRoot, candidate);
16036
- if (existsSync18(candidatePath) && existsSync18(resolve9(candidatePath, "__init__.py"))) {
16061
+ const candidatePath = resolve10(projectRoot, candidate);
16062
+ if (existsSync19(candidatePath) && existsSync19(resolve10(candidatePath, "__init__.py"))) {
16037
16063
  result.root = candidate;
16038
16064
  break;
16039
16065
  }
16040
- if (existsSync18(candidatePath)) {
16066
+ if (existsSync19(candidatePath)) {
16041
16067
  try {
16042
- const files = readdirSync11(candidatePath);
16068
+ const files = readdirSync12(candidatePath);
16043
16069
  if (files.some((f2) => f2.endsWith(".py"))) {
16044
16070
  result.root = candidate;
16045
16071
  break;
@@ -16057,8 +16083,8 @@ function generateConfig(projectRoot, framework) {
16057
16083
  console.warn(
16058
16084
  "[@massu/core] generateConfig() is deprecated since 1.2.1 \u2014 use buildConfigFromDetection instead. It cannot produce valid configs for monorepos."
16059
16085
  );
16060
- const configPath = resolve9(projectRoot, "massu.config.yaml");
16061
- if (existsSync18(configPath)) {
16086
+ const configPath = resolve10(projectRoot, "massu.config.yaml");
16087
+ if (existsSync19(configPath)) {
16062
16088
  return false;
16063
16089
  }
16064
16090
  const projectName = basename6(projectRoot);
@@ -16169,10 +16195,10 @@ function buildConfigFromDetection(opts) {
16169
16195
  pathsSource = monorepoCommonRoot(detection.monorepo.packages);
16170
16196
  }
16171
16197
  }
16172
- if (pathsSource === "src" && !existsSync18(resolve9(projectRoot, "src"))) {
16198
+ if (pathsSource === "src" && !existsSync19(resolve10(projectRoot, "src"))) {
16173
16199
  const fallbacks = ["app", "pages", "."];
16174
16200
  for (const fallback of fallbacks) {
16175
- if (fallback === "." || existsSync18(resolve9(projectRoot, fallback))) {
16201
+ if (fallback === "." || existsSync19(resolve10(projectRoot, fallback))) {
16176
16202
  pathsSource = fallback;
16177
16203
  break;
16178
16204
  }
@@ -16256,7 +16282,7 @@ function buildConfigFromDetection(opts) {
16256
16282
  };
16257
16283
  if (pyFw?.framework) pythonBlock.framework = pyFw.framework;
16258
16284
  if (pyFw?.orm) pythonBlock.orm = pyFw.orm;
16259
- if (existsSync18(resolve9(projectRoot, "alembic.ini")) || existsSync18(resolve9(projectRoot, "alembic"))) {
16285
+ if (existsSync19(resolve10(projectRoot, "alembic.ini")) || existsSync19(resolve10(projectRoot, "alembic"))) {
16260
16286
  pythonBlock.alembic_dir = "alembic";
16261
16287
  }
16262
16288
  config.python = pythonBlock;
@@ -16280,8 +16306,8 @@ function applyVariantTemplate(config, templatesDir) {
16280
16306
  }
16281
16307
  }
16282
16308
  if (templateId === null) return config;
16283
- const templatePath = resolve9(templatesDir, templateId, "massu.config.yaml");
16284
- if (!existsSync18(templatePath)) return config;
16309
+ const templatePath = resolve10(templatesDir, templateId, "massu.config.yaml");
16310
+ if (!existsSync19(templatePath)) return config;
16285
16311
  let template;
16286
16312
  try {
16287
16313
  template = yamlParse(readFileSync15(templatePath, "utf-8"));
@@ -16343,9 +16369,9 @@ ${yamlStringify(config)}`;
16343
16369
  function writeConfigAtomic(configPath, content) {
16344
16370
  const tmpPath = `${configPath}.tmp`;
16345
16371
  let existingMode;
16346
- if (existsSync18(configPath)) {
16372
+ if (existsSync19(configPath)) {
16347
16373
  try {
16348
- existingMode = statSync9(configPath).mode;
16374
+ existingMode = statSync10(configPath).mode;
16349
16375
  } catch {
16350
16376
  existingMode = void 0;
16351
16377
  }
@@ -16373,7 +16399,7 @@ function writeConfigAtomic(configPath, content) {
16373
16399
  }
16374
16400
  return { validated: true };
16375
16401
  } catch (err) {
16376
- if (existsSync18(tmpPath)) {
16402
+ if (existsSync19(tmpPath)) {
16377
16403
  try {
16378
16404
  rmSync6(tmpPath, { force: true });
16379
16405
  } catch {
@@ -16384,7 +16410,7 @@ function writeConfigAtomic(configPath, content) {
16384
16410
  }
16385
16411
  function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
16386
16412
  try {
16387
- if (!existsSync18(configPath)) return "Config file does not exist after write";
16413
+ if (!existsSync19(configPath)) return "Config file does not exist after write";
16388
16414
  const content = readFileSync15(configPath, "utf-8");
16389
16415
  const parsed = yamlParse(content);
16390
16416
  if (parsed === null || typeof parsed !== "object") {
@@ -16405,8 +16431,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
16405
16431
  if (checkPaths) {
16406
16432
  const src = cfg.paths.source;
16407
16433
  if (src && src !== ".") {
16408
- const srcAbs = resolve9(projectRoot, src);
16409
- if (!existsSync18(srcAbs)) {
16434
+ const srcAbs = resolve10(projectRoot, src);
16435
+ if (!existsSync19(srcAbs)) {
16410
16436
  return `paths.source '${src}' does not exist on disk`;
16411
16437
  }
16412
16438
  }
@@ -16416,8 +16442,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
16416
16442
  if (!Array.isArray(rawDirs)) continue;
16417
16443
  for (const d2 of rawDirs) {
16418
16444
  if (typeof d2 !== "string" || d2 === ".") continue;
16419
- const abs = resolve9(projectRoot, d2);
16420
- if (!existsSync18(abs)) {
16445
+ const abs = resolve10(projectRoot, d2);
16446
+ if (!existsSync19(abs)) {
16421
16447
  return `framework.languages.${lang}.source_dirs '${d2}' does not exist on disk`;
16422
16448
  }
16423
16449
  }
@@ -16426,7 +16452,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
16426
16452
  if (Array.isArray(mRoots)) {
16427
16453
  for (const r2 of mRoots) {
16428
16454
  if (typeof r2 !== "string" || r2 === ".") continue;
16429
- if (!existsSync18(resolve9(projectRoot, r2))) {
16455
+ if (!existsSync19(resolve10(projectRoot, r2))) {
16430
16456
  return `paths.monorepo_roots '${r2}' does not exist on disk`;
16431
16457
  }
16432
16458
  }
@@ -16458,21 +16484,21 @@ function resolveTemplatesDir() {
16458
16484
  const cwd = process.cwd();
16459
16485
  const candidates = [
16460
16486
  // Project-local install: `<project>/node_modules/@massu/core/templates`.
16461
- resolve9(cwd, "node_modules/@massu/core/templates"),
16487
+ resolve10(cwd, "node_modules/@massu/core/templates"),
16462
16488
  // Bundled cli.js layout: cli.js sits at `<package>/dist/cli.js`, so
16463
16489
  // templates live one level up at `<package>/templates`. (Plan 1.5.1
16464
16490
  // bug discovery: pre-existing layout assumed `dist/commands/init.js`
16465
16491
  // depth which never matched the bundled cli, so resolveTemplatesDir
16466
16492
  // returned null in production for both `--template` mode AND the
16467
16493
  // applyVariantTemplate path.)
16468
- resolve9(__dirname2, "../templates"),
16494
+ resolve10(__dirname2, "../templates"),
16469
16495
  // Legacy nested layouts retained as fallbacks (in case a future
16470
16496
  // build moves cli.js back into a subdirectory).
16471
- resolve9(__dirname2, "../../templates"),
16472
- resolve9(__dirname2, "../../../templates")
16497
+ resolve10(__dirname2, "../../templates"),
16498
+ resolve10(__dirname2, "../../../templates")
16473
16499
  ];
16474
16500
  for (const c2 of candidates) {
16475
- if (existsSync18(c2)) return c2;
16501
+ if (existsSync19(c2)) return c2;
16476
16502
  }
16477
16503
  return null;
16478
16504
  }
@@ -16481,8 +16507,8 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
16481
16507
  if (!templatesDir) {
16482
16508
  return { success: false, error: `Templates directory not found (looked in node_modules and dist/src)` };
16483
16509
  }
16484
- const srcPath = resolve9(templatesDir, templateName, "massu.config.yaml");
16485
- if (!existsSync18(srcPath)) {
16510
+ const srcPath = resolve10(templatesDir, templateName, "massu.config.yaml");
16511
+ if (!existsSync19(srcPath)) {
16486
16512
  return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
16487
16513
  }
16488
16514
  try {
@@ -16496,11 +16522,11 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
16496
16522
  }
16497
16523
  function getInstallerVersion() {
16498
16524
  const candidates = [
16499
- resolve9(__dirname2, "../package.json"),
16500
- resolve9(__dirname2, "../../package.json")
16525
+ resolve10(__dirname2, "../package.json"),
16526
+ resolve10(__dirname2, "../../package.json")
16501
16527
  ];
16502
16528
  for (const candidate of candidates) {
16503
- if (existsSync18(candidate)) {
16529
+ if (existsSync19(candidate)) {
16504
16530
  try {
16505
16531
  const pkg = JSON.parse(readFileSync15(candidate, "utf-8"));
16506
16532
  if (typeof pkg.version === "string" && pkg.version.length > 0 && pkg.name === "@massu/core") {
@@ -16515,9 +16541,9 @@ function getInstallerVersion() {
16515
16541
  );
16516
16542
  }
16517
16543
  function registerMcpServer(projectRoot) {
16518
- const mcpPath = resolve9(projectRoot, ".mcp.json");
16544
+ const mcpPath = resolve10(projectRoot, ".mcp.json");
16519
16545
  let existing = {};
16520
- if (existsSync18(mcpPath)) {
16546
+ if (existsSync19(mcpPath)) {
16521
16547
  try {
16522
16548
  existing = JSON.parse(readFileSync15(mcpPath, "utf-8"));
16523
16549
  } catch {
@@ -16540,12 +16566,12 @@ function registerMcpServer(projectRoot) {
16540
16566
  }
16541
16567
  function resolveHooksDir() {
16542
16568
  const cwd = process.cwd();
16543
- const nodeModulesPath = resolve9(cwd, "node_modules/@massu/core/dist/hooks");
16544
- if (existsSync18(nodeModulesPath)) {
16569
+ const nodeModulesPath = resolve10(cwd, "node_modules/@massu/core/dist/hooks");
16570
+ if (existsSync19(nodeModulesPath)) {
16545
16571
  return "node_modules/@massu/core/dist/hooks";
16546
16572
  }
16547
- const localPath = resolve9(__dirname2, "../dist/hooks");
16548
- if (existsSync18(localPath)) {
16573
+ const localPath = resolve10(__dirname2, "../dist/hooks");
16574
+ if (existsSync19(localPath)) {
16549
16575
  return localPath;
16550
16576
  }
16551
16577
  return "node_modules/@massu/core/dist/hooks";
@@ -16694,8 +16720,8 @@ function installHooks(projectRoot) {
16694
16720
  } catch {
16695
16721
  claudeDirName = ".claude";
16696
16722
  }
16697
- const claudeDir = resolve9(projectRoot, claudeDirName);
16698
- if (!existsSync18(claudeDir)) {
16723
+ const claudeDir = resolve10(projectRoot, claudeDirName);
16724
+ if (!existsSync19(claudeDir)) {
16699
16725
  mkdirSync10(claudeDir, { recursive: true });
16700
16726
  }
16701
16727
  const settings = readSettingsLocal(claudeDir);
@@ -16714,16 +16740,16 @@ function installHooks(projectRoot) {
16714
16740
  }
16715
16741
  function initMemoryDir(projectRoot) {
16716
16742
  const encodedRoot = encodeMemoryDirName(projectRoot);
16717
- const memoryDir = resolve9(homedir6(), `.claude/projects/${encodedRoot}/memory`);
16743
+ const memoryDir = resolve10(homedir7(), `.claude/projects/${encodedRoot}/memory`);
16718
16744
  let migratedFromLegacy = false;
16719
- const legacyDir = resolve9(homedir6(), `.claude/projects/-${encodedRoot}/memory`);
16720
- if (existsSync18(legacyDir) && !existsSync18(memoryDir)) {
16745
+ const legacyDir = resolve10(homedir7(), `.claude/projects/-${encodedRoot}/memory`);
16746
+ if (existsSync19(legacyDir) && !existsSync19(memoryDir)) {
16721
16747
  try {
16722
- mkdirSync10(resolve9(memoryDir, ".."), { recursive: true });
16748
+ mkdirSync10(resolve10(memoryDir, ".."), { recursive: true });
16723
16749
  renameSync4(legacyDir, memoryDir);
16724
16750
  try {
16725
- const legacyParent = resolve9(legacyDir, "..");
16726
- if (existsSync18(legacyParent) && readdirSync11(legacyParent).length === 0) {
16751
+ const legacyParent = resolve10(legacyDir, "..");
16752
+ if (existsSync19(legacyParent) && readdirSync12(legacyParent).length === 0) {
16727
16753
  rmSync6(legacyParent, { recursive: false });
16728
16754
  }
16729
16755
  } catch {
@@ -16733,13 +16759,13 @@ function initMemoryDir(projectRoot) {
16733
16759
  }
16734
16760
  }
16735
16761
  let created = false;
16736
- if (!existsSync18(memoryDir)) {
16762
+ if (!existsSync19(memoryDir)) {
16737
16763
  mkdirSync10(memoryDir, { recursive: true });
16738
16764
  created = true;
16739
16765
  }
16740
- const memoryMdPath = resolve9(memoryDir, "MEMORY.md");
16766
+ const memoryMdPath = resolve10(memoryDir, "MEMORY.md");
16741
16767
  let memoryMdCreated = false;
16742
- if (!existsSync18(memoryMdPath)) {
16768
+ if (!existsSync19(memoryMdPath)) {
16743
16769
  const projectName = basename6(projectRoot);
16744
16770
  const memoryContent = `# ${projectName} - Massu Memory
16745
16771
 
@@ -16853,8 +16879,8 @@ async function runInit(argv, overrides) {
16853
16879
  log("Massu AI - Project Setup");
16854
16880
  log("========================");
16855
16881
  log("");
16856
- const configPath = resolve9(projectRoot, "massu.config.yaml");
16857
- if (existsSync18(configPath)) {
16882
+ const configPath = resolve10(projectRoot, "massu.config.yaml");
16883
+ if (existsSync19(configPath)) {
16858
16884
  if (opts.ci && !opts.force) {
16859
16885
  errLog(`error: massu.config.yaml already exists at ${configPath}`);
16860
16886
  errLog(" rerun with --force to overwrite, or remove the file first");
@@ -16993,8 +17019,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
16993
17019
  const commandStats = cmdResult.assets.commands;
16994
17020
  const stackResolved = !emptyStack && commandStats && (commandStats.installed > 0 || commandStats.updated > 0 || commandStats.kept > 0);
16995
17021
  if (!stackResolved) {
16996
- const placeholderPath = resolve9(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
16997
- if (!existsSync18(placeholderPath)) {
17022
+ const placeholderPath = resolve10(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
17023
+ if (!existsSync19(placeholderPath)) {
16998
17024
  const placeholderBody = [
16999
17025
  "# Massu \u2014 stack not yet detected",
17000
17026
  "",
@@ -17015,7 +17041,7 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
17015
17041
  "\u2014 Massu"
17016
17042
  ].join("\n");
17017
17043
  try {
17018
- mkdirSync10(resolve9(cmdResult.claudeDir, "commands"), { recursive: true });
17044
+ mkdirSync10(resolve10(cmdResult.claudeDir, "commands"), { recursive: true });
17019
17045
  writeFileSync5(placeholderPath, placeholderBody, "utf-8");
17020
17046
  log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
17021
17047
  } catch {
@@ -17038,8 +17064,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
17038
17064
  (async () => {
17039
17065
  try {
17040
17066
  const encodedRoot = encodeMemoryDirName(projectRoot);
17041
- const memoryDir = resolve9(homedir6(), ".claude", "projects", encodedRoot, "memory");
17042
- const memFiles = existsSync18(memoryDir) ? readdirSync11(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
17067
+ const memoryDir = resolve10(homedir7(), ".claude", "projects", encodedRoot, "memory");
17068
+ const memFiles = existsSync19(memoryDir) ? readdirSync12(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
17043
17069
  if (memFiles.length > 0) {
17044
17070
  const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
17045
17071
  const db = getMemoryDb2();
@@ -17720,21 +17746,195 @@ var init_hook_registry = __esm({
17720
17746
  }
17721
17747
  });
17722
17748
 
17749
+ // src/preflight.ts
17750
+ import { existsSync as existsSync20 } from "fs";
17751
+ function assertCodegraphUsable(dbPath) {
17752
+ const path = dbPath ?? getResolvedPaths().codegraphDbPath;
17753
+ if (!existsSync20(path)) {
17754
+ throw new CodegraphDbUnusableError(path, "missing");
17755
+ }
17756
+ let db = null;
17757
+ try {
17758
+ db = openDatabase2(path, { readonly: true });
17759
+ const row = db.prepare("SELECT COUNT(*) AS c FROM files").get();
17760
+ const files = row?.c ?? 0;
17761
+ if (files === 0) {
17762
+ throw new CodegraphDbUnusableError(path, "empty");
17763
+ }
17764
+ } catch (err) {
17765
+ if (err instanceof CodegraphDbUnusableError) throw err;
17766
+ throw new CodegraphDbUnusableError(
17767
+ path,
17768
+ "unreadable",
17769
+ err instanceof Error ? err.message : String(err)
17770
+ );
17771
+ } finally {
17772
+ try {
17773
+ db?.close();
17774
+ } catch {
17775
+ }
17776
+ }
17777
+ }
17778
+ var CodegraphDbNotInitializedError, CodegraphDbUnusableError;
17779
+ var init_preflight = __esm({
17780
+ "src/preflight.ts"() {
17781
+ "use strict";
17782
+ init_db_driver();
17783
+ init_config();
17784
+ init_node_floor();
17785
+ CodegraphDbNotInitializedError = class extends Error {
17786
+ dbPath;
17787
+ constructor(dbPath) {
17788
+ super(`CodeGraph database not found at ${dbPath}`);
17789
+ this.name = "CodegraphDbNotInitializedError";
17790
+ this.dbPath = dbPath;
17791
+ }
17792
+ };
17793
+ CodegraphDbUnusableError = class extends CodegraphDbNotInitializedError {
17794
+ reason;
17795
+ constructor(dbPath, reason, detail) {
17796
+ super(dbPath);
17797
+ this.message = reason === "missing" ? `CodeGraph database not found at ${dbPath}` : reason === "empty" ? `CodeGraph database at ${dbPath} exists but is EMPTY (0 files indexed)` : `CodeGraph database at ${dbPath} could not be read: ${detail ?? "unknown"}`;
17798
+ this.name = "CodegraphDbUnusableError";
17799
+ this.reason = reason;
17800
+ }
17801
+ /**
17802
+ * The remedy, per reason. VERIFIED BY EXECUTION — `init` is a no-op on an
17803
+ * already-initialized repo and will NOT populate the graph. `index` is the command
17804
+ * that does. Getting this wrong sends the customer in a circle.
17805
+ */
17806
+ get remedy() {
17807
+ switch (this.reason) {
17808
+ case "missing":
17809
+ return "npx @colbymchenry/codegraph init && npx @colbymchenry/codegraph index";
17810
+ case "empty":
17811
+ return "npx @colbymchenry/codegraph index";
17812
+ case "unreadable":
17813
+ return "npx @colbymchenry/codegraph index (the database may be corrupt \u2014 re-indexing rebuilds it)";
17814
+ }
17815
+ }
17816
+ /** Plain-English explanation for a human. */
17817
+ get humanExplanation() {
17818
+ switch (this.reason) {
17819
+ case "missing":
17820
+ return "Massu needs a map of your code to answer questions about it. That map has not been built yet.";
17821
+ case "empty":
17822
+ return 'The code map exists but is EMPTY \u2014 nothing was ever indexed into it. Massu would answer "no impact" to every question, which is indistinguishable from a real answer. It is refusing rather than misleading you.';
17823
+ case "unreadable":
17824
+ return "The code map exists but cannot be read. It may be corrupt.";
17825
+ }
17826
+ }
17827
+ };
17828
+ }
17829
+ });
17830
+
17831
+ // src/commands/hook-runner.ts
17832
+ var hook_runner_exports = {};
17833
+ __export(hook_runner_exports, {
17834
+ HOOK_NAME_TO_FILE: () => HOOK_NAME_TO_FILE,
17835
+ resolveHookFile: () => resolveHookFile,
17836
+ runHookRunner: () => runHookRunner
17837
+ });
17838
+ import { existsSync as existsSync21 } from "fs";
17839
+ import { resolve as resolve11, dirname as dirname13 } from "path";
17840
+ import { fileURLToPath as fileURLToPath4 } from "url";
17841
+ import { spawn } from "child_process";
17842
+ function resolveHookFile(hookName) {
17843
+ const file = HOOK_NAME_TO_FILE[hookName];
17844
+ if (!file) {
17845
+ throw new Error(
17846
+ `Unknown hook: "${hookName}". Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}`
17847
+ );
17848
+ }
17849
+ const candidates = [
17850
+ // Bundled compiled layout: dist/cli.js → ./hooks/<file>.js
17851
+ resolve11(__dirname3, "hooks", file),
17852
+ // TS-source dev / sibling layout: src/commands/ → ../hooks/<file>
17853
+ resolve11(__dirname3, "../hooks", file),
17854
+ // TS-source dev fallback: src/commands/ → ../../dist/hooks/<file>
17855
+ resolve11(__dirname3, "../../dist/hooks", file)
17856
+ ];
17857
+ for (const candidate of candidates) {
17858
+ if (existsSync21(candidate)) {
17859
+ return candidate;
17860
+ }
17861
+ }
17862
+ throw new Error(
17863
+ `Hook file not found for "${hookName}". Searched: ${candidates.join(", ")}. This indicates a broken @massu/core install. Re-run \`npx -y @massu/core init\`.`
17864
+ );
17865
+ }
17866
+ async function runHookRunner(args2) {
17867
+ const hookName = args2[0];
17868
+ if (!hookName) {
17869
+ process.stderr.write(
17870
+ `massu hook-runner: missing hook name.
17871
+ Usage: massu hook-runner <hook-name>
17872
+ Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}
17873
+ `
17874
+ );
17875
+ return { exitCode: 2 };
17876
+ }
17877
+ let hookFile;
17878
+ try {
17879
+ hookFile = resolveHookFile(hookName);
17880
+ } catch (err) {
17881
+ process.stderr.write(`massu hook-runner: ${err instanceof Error ? err.message : String(err)}
17882
+ `);
17883
+ return { exitCode: 2 };
17884
+ }
17885
+ return new Promise((resolvePromise) => {
17886
+ const child = spawn(process.execPath, [hookFile], {
17887
+ stdio: ["inherit", "inherit", "inherit"],
17888
+ // MASSU_HOOK_RUNTIME marks the hook process so the SSOT SQLite loader NEVER
17889
+ // self-heals here (5s budget / CR-12, CR-65 P0-003) — even for indirect opens
17890
+ // via getMemoryDb(). The next server/CLI touch performs the rebuild.
17891
+ env: { ...process.env, MASSU_HOOK_RUNTIME: "1" }
17892
+ });
17893
+ child.on("exit", (code, signal) => {
17894
+ if (signal) {
17895
+ resolvePromise({ exitCode: 128 });
17896
+ return;
17897
+ }
17898
+ resolvePromise({ exitCode: code ?? 0 });
17899
+ });
17900
+ child.on("error", (err) => {
17901
+ process.stderr.write(`massu hook-runner: failed to spawn hook "${hookName}": ${err.message}
17902
+ `);
17903
+ resolvePromise({ exitCode: 2 });
17904
+ });
17905
+ });
17906
+ }
17907
+ var __filename3, __dirname3, HOOK_NAME_TO_FILE;
17908
+ var init_hook_runner = __esm({
17909
+ "src/commands/hook-runner.ts"() {
17910
+ "use strict";
17911
+ init_hook_registry();
17912
+ __filename3 = fileURLToPath4(import.meta.url);
17913
+ __dirname3 = dirname13(__filename3);
17914
+ HOOK_NAME_TO_FILE = Object.freeze(
17915
+ Object.fromEntries(REGISTERED_HOOKS.map((name) => [name, `${name}.js`]))
17916
+ );
17917
+ }
17918
+ });
17919
+
17723
17920
  // src/commands/doctor.ts
17724
17921
  var doctor_exports = {};
17725
17922
  __export(doctor_exports, {
17923
+ checkHookExecution: () => checkHookExecution,
17924
+ checkHooksConfig: () => checkHooksConfig,
17726
17925
  checkNativeModules: () => checkNativeModules,
17926
+ checkShellHooksWired: () => checkShellHooksWired,
17727
17927
  formatLicenseCheck: () => formatLicenseCheck,
17728
17928
  runDoctor: () => runDoctor,
17729
17929
  runValidateConfig: () => runValidateConfig
17730
17930
  });
17731
- import { existsSync as existsSync19, readFileSync as readFileSync16, readdirSync as readdirSync12 } from "fs";
17732
- import { resolve as resolve10, dirname as dirname13 } from "path";
17733
- import { fileURLToPath as fileURLToPath4 } from "url";
17931
+ import { existsSync as existsSync22, readFileSync as readFileSync16, readdirSync as readdirSync13 } from "fs";
17932
+ import { resolve as resolve12, dirname as dirname14 } from "path";
17933
+ import { fileURLToPath as fileURLToPath5 } from "url";
17734
17934
  import { parse as parseYaml4 } from "yaml";
17735
17935
  function checkConfig(projectRoot) {
17736
- const configPath = resolve10(projectRoot, "massu.config.yaml");
17737
- if (!existsSync19(configPath)) {
17936
+ const configPath = resolve12(projectRoot, "massu.config.yaml");
17937
+ if (!existsSync22(configPath)) {
17738
17938
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
17739
17939
  }
17740
17940
  try {
@@ -17750,7 +17950,7 @@ function checkConfig(projectRoot) {
17750
17950
  }
17751
17951
  function checkMcpServer(projectRoot) {
17752
17952
  const mcpPath = getResolvedPaths().mcpJsonPath;
17753
- if (!existsSync19(mcpPath)) {
17953
+ if (!existsSync22(mcpPath)) {
17754
17954
  return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
17755
17955
  }
17756
17956
  try {
@@ -17764,41 +17964,54 @@ function checkMcpServer(projectRoot) {
17764
17964
  return { name: "MCP Server", status: "fail", detail: `.mcp.json parse error: ${err instanceof Error ? err.message : String(err)}` };
17765
17965
  }
17766
17966
  }
17767
- function checkHooksConfig(projectRoot) {
17768
- const settingsPath = getResolvedPaths().settingsLocalPath;
17769
- if (!existsSync19(settingsPath)) {
17770
- return { name: "Hooks Config", status: "fail", detail: ".claude/settings.local.json not found. Run: npx massu init" };
17967
+ function readHookSources() {
17968
+ const { settingsPath, settingsLocalPath } = getResolvedPaths();
17969
+ const out = [];
17970
+ for (const [label, p19] of [["settings.json", settingsPath], ["settings.local.json", settingsLocalPath]]) {
17971
+ if (!existsSync22(p19)) continue;
17972
+ const content = readSettingsAtPath(p19);
17973
+ out.push({ label, hooks: content.hooks });
17974
+ }
17975
+ return out;
17976
+ }
17977
+ function checkHooksConfig(_projectRoot2) {
17978
+ const sources = readHookSources();
17979
+ if (sources.length === 0) {
17980
+ return { name: "Hooks Config", status: "fail", detail: ".claude/settings.json or settings.local.json not found. Run: npx massu init" };
17771
17981
  }
17772
17982
  try {
17773
- const content = readSettingsAtPath(settingsPath);
17774
- if (!content.hooks) {
17775
- return { name: "Hooks Config", status: "fail", detail: "No hooks configured. Run: npx massu install-hooks" };
17776
- }
17777
17983
  let hookCount = 0;
17778
- for (const groups of Object.values(content.hooks)) {
17779
- if (Array.isArray(groups)) {
17780
- for (const group of groups) {
17781
- const g2 = group;
17782
- if (Array.isArray(g2.hooks)) {
17783
- hookCount += g2.hooks.length;
17984
+ const from = [];
17985
+ for (const { label, hooks } of sources) {
17986
+ if (!hooks) continue;
17987
+ let n = 0;
17988
+ for (const groups of Object.values(hooks)) {
17989
+ if (Array.isArray(groups)) {
17990
+ for (const group of groups) {
17991
+ const g2 = group;
17992
+ if (Array.isArray(g2.hooks)) n += g2.hooks.length;
17784
17993
  }
17785
17994
  }
17786
17995
  }
17996
+ if (n > 0) {
17997
+ hookCount += n;
17998
+ from.push(label);
17999
+ }
17787
18000
  }
17788
18001
  if (hookCount === 0) {
17789
- return { name: "Hooks Config", status: "fail", detail: "Hooks section exists but no hooks configured" };
18002
+ return { name: "Hooks Config", status: "fail", detail: "No hooks configured. Run: npx massu install-hooks" };
17790
18003
  }
17791
- return { name: "Hooks Config", status: "pass", detail: `${hookCount} hooks configured` };
18004
+ return { name: "Hooks Config", status: "pass", detail: `${hookCount} hooks configured (${from.join(" + ")})` };
17792
18005
  } catch (err) {
17793
- return { name: "Hooks Config", status: "fail", detail: `settings.local.json parse error: ${err instanceof Error ? err.message : String(err)}` };
18006
+ return { name: "Hooks Config", status: "fail", detail: `settings parse error: ${err instanceof Error ? err.message : String(err)}` };
17794
18007
  }
17795
18008
  }
17796
18009
  function checkHookFiles(projectRoot) {
17797
- const nodeModulesHooksDir = resolve10(projectRoot, "node_modules/@massu/core/dist/hooks");
18010
+ const nodeModulesHooksDir = resolve12(projectRoot, "node_modules/@massu/core/dist/hooks");
17798
18011
  let hooksDir = nodeModulesHooksDir;
17799
- if (!existsSync19(nodeModulesHooksDir)) {
17800
- const devHooksDir = resolve10(__dirname3, "../../dist/hooks");
17801
- if (existsSync19(devHooksDir)) {
18012
+ if (!existsSync22(nodeModulesHooksDir)) {
18013
+ const devHooksDir = resolve12(__dirname4, "../../dist/hooks");
18014
+ if (existsSync22(devHooksDir)) {
17802
18015
  hooksDir = devHooksDir;
17803
18016
  } else {
17804
18017
  return { name: "Hook Files", status: "fail", detail: "Compiled hooks not found. Run: npm install @massu/core" };
@@ -17806,7 +18019,7 @@ function checkHookFiles(projectRoot) {
17806
18019
  }
17807
18020
  const missing = [];
17808
18021
  for (const hookFile of EXPECTED_HOOKS) {
17809
- if (!existsSync19(resolve10(hooksDir, hookFile))) {
18022
+ if (!existsSync22(resolve12(hooksDir, hookFile))) {
17810
18023
  missing.push(hookFile);
17811
18024
  }
17812
18025
  }
@@ -17815,6 +18028,67 @@ function checkHookFiles(projectRoot) {
17815
18028
  }
17816
18029
  return { name: "Hook Files", status: "pass", detail: `${EXPECTED_HOOKS.length}/${EXPECTED_HOOKS.length} compiled hooks present` };
17817
18030
  }
18031
+ async function checkHookExecution(projectRoot, opts = {}) {
18032
+ const { spawnSync: spawnSync6 } = await import("child_process");
18033
+ const { mkdtempSync: mkdtempSync2, copyFileSync: copyFileSync4, rmSync: rmSync12 } = await import("fs");
18034
+ const { tmpdir: tmpdir2 } = await import("os");
18035
+ const node = opts.node ?? process.execPath;
18036
+ const timeoutMs = opts.timeoutMs ?? 1e4;
18037
+ const event = opts.event ?? JSON.stringify({ hook_event_name: "SessionStart", source: "massu-doctor-canary" });
18038
+ let hookFile;
18039
+ try {
18040
+ hookFile = opts.hookFile ?? resolveHookFile("session-start");
18041
+ } catch (err) {
18042
+ return {
18043
+ name: "Hook Runtime",
18044
+ status: "fail",
18045
+ detail: `Canary hook file unresolved: ${err instanceof Error ? err.message : String(err)}. Re-run: npx -y @massu/core init`
18046
+ };
18047
+ }
18048
+ const tmpCwd = mkdtempSync2(resolve12(tmpdir2(), "massu-hook-canary-"));
18049
+ try {
18050
+ const realConfig = resolve12(projectRoot, "massu.config.yaml");
18051
+ if (existsSync22(realConfig)) {
18052
+ try {
18053
+ copyFileSync4(realConfig, resolve12(tmpCwd, "massu.config.yaml"));
18054
+ } catch {
18055
+ }
18056
+ }
18057
+ const res = spawnSync6(node, [hookFile], {
18058
+ input: event,
18059
+ cwd: tmpCwd,
18060
+ timeout: timeoutMs,
18061
+ encoding: "utf-8",
18062
+ env: { ...process.env, MASSU_HOOK_RUNTIME: "1" }
18063
+ });
18064
+ if (res.error) {
18065
+ const killed = res.error.code === "ETIMEDOUT" ? " (timed out)" : "";
18066
+ return {
18067
+ name: "Hook Runtime",
18068
+ status: "fail",
18069
+ detail: `Canary hook did not run under Node ${process.version}${killed}: ${res.error.message}. Hooks would crash at runtime \u2014 upgrade Node to >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0.`
18070
+ };
18071
+ }
18072
+ if (res.status !== 0) {
18073
+ const tail = (res.stderr || "").trim().split("\n").slice(-2).join(" ");
18074
+ return {
18075
+ name: "Hook Runtime",
18076
+ status: "fail",
18077
+ detail: `Canary hook exited ${res.status ?? "null"} under Node ${process.version} \u2014 a configured hook FAILS at runtime.${tail ? ` ${tail}` : ""}`
18078
+ };
18079
+ }
18080
+ return {
18081
+ name: "Hook Runtime",
18082
+ status: "pass",
18083
+ detail: `Canary hook executes (exit 0) under Node ${process.version}`
18084
+ };
18085
+ } finally {
18086
+ try {
18087
+ rmSync12(tmpCwd, { recursive: true, force: true });
18088
+ } catch {
18089
+ }
18090
+ }
18091
+ }
17818
18092
  async function checkNativeModules() {
17819
18093
  const engine = resolveDbEngine();
17820
18094
  const engineLabel = engine === "better-sqlite3" ? "better-sqlite3 (native fallback)" : "node:sqlite (native-free, Node built-in)";
@@ -17830,23 +18104,22 @@ async function checkNativeModules() {
17830
18104
  detail: `${engineLabel} unusable \u2014 ${verdict.reason}${because}. ${verdict.remedy ?? NATIVE_DB_REMEDY}`
17831
18105
  };
17832
18106
  }
17833
- function checkNodeVersion() {
18107
+ function checkNodeVersion2() {
17834
18108
  const version = process.versions.node;
17835
- const [major, minor] = version.split(".").map((n) => parseInt(n, 10));
17836
- const meets = major > 22 || major === 22 && minor >= 13;
17837
- if (meets) {
17838
- return { name: "Node.js", status: "pass", detail: `v${version} (>= 22.13.0 required for node:sqlite)` };
18109
+ const floor = `${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0`;
18110
+ if (checkNodeVersion().ok) {
18111
+ return { name: "Node.js", status: "pass", detail: `v${version} (>= ${floor} required for node:sqlite)` };
17839
18112
  }
17840
- return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js >= 22.13.0 is required (node:sqlite engine)` };
18113
+ return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js >= ${floor} is required (node:sqlite engine)` };
17841
18114
  }
17842
18115
  async function checkGitRepo(projectRoot) {
17843
- const gitDir = resolve10(projectRoot, ".git");
17844
- if (!existsSync19(gitDir)) {
18116
+ const gitDir = resolve12(projectRoot, ".git");
18117
+ if (!existsSync22(gitDir)) {
17845
18118
  return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
17846
18119
  }
17847
18120
  try {
17848
- const { spawnSync: spawnSync5 } = await import("child_process");
17849
- const result = spawnSync5("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
18121
+ const { spawnSync: spawnSync6 } = await import("child_process");
18122
+ const result = spawnSync6("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
17850
18123
  encoding: "utf-8",
17851
18124
  timeout: 5e3,
17852
18125
  cwd: projectRoot
@@ -17859,7 +18132,7 @@ async function checkGitRepo(projectRoot) {
17859
18132
  }
17860
18133
  function checkKnowledgeDb(projectRoot) {
17861
18134
  const knowledgeDbPath = getResolvedPaths().memoryDbPath;
17862
- if (!existsSync19(knowledgeDbPath)) {
18135
+ if (!existsSync22(knowledgeDbPath)) {
17863
18136
  return {
17864
18137
  name: "Knowledge DB",
17865
18138
  status: "warn",
@@ -17870,7 +18143,7 @@ function checkKnowledgeDb(projectRoot) {
17870
18143
  }
17871
18144
  function checkMemoryDir(_projectRoot2) {
17872
18145
  const memoryDir = getResolvedPaths().memoryDir;
17873
- if (!existsSync19(memoryDir)) {
18146
+ if (!existsSync22(memoryDir)) {
17874
18147
  return {
17875
18148
  name: "Memory Directory",
17876
18149
  status: "warn",
@@ -17880,19 +18153,22 @@ function checkMemoryDir(_projectRoot2) {
17880
18153
  return { name: "Memory Directory", status: "pass", detail: `Memory directory exists` };
17881
18154
  }
17882
18155
  function checkShellHooksWired(_projectRoot2) {
17883
- const settingsPath = getResolvedPaths().settingsLocalPath;
17884
- if (!existsSync19(settingsPath)) {
18156
+ const sources = readHookSources();
18157
+ if (sources.length === 0) {
17885
18158
  return {
17886
18159
  name: "Shell Hooks",
17887
18160
  status: "fail",
17888
- detail: "settings.local.json not found. Run: npx massu install-hooks"
18161
+ detail: "settings.json / settings.local.json not found. Run: npx massu install-hooks"
17889
18162
  };
17890
18163
  }
17891
18164
  try {
17892
- const content = readSettingsAtPath(settingsPath);
17893
- const hooks = content.hooks ?? {};
17894
- const hasSessionStart = Array.isArray(hooks.SessionStart) && hooks.SessionStart.length > 0;
17895
- const hasPreToolUse = Array.isArray(hooks.PreToolUse) && hooks.PreToolUse.length > 0;
18165
+ let hasSessionStart = false;
18166
+ let hasPreToolUse = false;
18167
+ for (const { hooks } of sources) {
18168
+ const h2 = hooks ?? {};
18169
+ if (Array.isArray(h2.SessionStart) && h2.SessionStart.length > 0) hasSessionStart = true;
18170
+ if (Array.isArray(h2.PreToolUse) && h2.PreToolUse.length > 0) hasPreToolUse = true;
18171
+ }
17896
18172
  if (!hasSessionStart && !hasPreToolUse) {
17897
18173
  return {
17898
18174
  name: "Shell Hooks",
@@ -17900,12 +18176,12 @@ function checkShellHooksWired(_projectRoot2) {
17900
18176
  detail: "No lifecycle hooks wired. Run: npx massu install-hooks"
17901
18177
  };
17902
18178
  }
17903
- return { name: "Shell Hooks", status: "pass", detail: "Lifecycle hooks wired in settings.local.json" };
18179
+ return { name: "Shell Hooks", status: "pass", detail: "Lifecycle hooks wired (settings.json / settings.local.json)" };
17904
18180
  } catch (err) {
17905
18181
  return {
17906
18182
  name: "Shell Hooks",
17907
18183
  status: "fail",
17908
- detail: `settings.local.json parse error: ${err instanceof Error ? err.message : String(err)}`
18184
+ detail: `settings parse error: ${err instanceof Error ? err.message : String(err)}`
17909
18185
  };
17910
18186
  }
17911
18187
  }
@@ -17957,8 +18233,8 @@ async function checkLicenseStatus() {
17957
18233
  async function checkApiKeyHygiene(projectRoot) {
17958
18234
  if (getResolvedApiKeySource() !== "config") return null;
17959
18235
  try {
17960
- const { spawnSync: spawnSync5 } = await import("child_process");
17961
- const res = spawnSync5("git", ["ls-files", "--error-unmatch", "massu.config.yaml"], {
18236
+ const { spawnSync: spawnSync6 } = await import("child_process");
18237
+ const res = spawnSync6("git", ["ls-files", "--error-unmatch", "massu.config.yaml"], {
17962
18238
  cwd: projectRoot,
17963
18239
  timeout: 5e3,
17964
18240
  encoding: "utf-8"
@@ -17977,8 +18253,8 @@ async function checkApiKeyHygiene(projectRoot) {
17977
18253
  function checkPythonHealth(projectRoot) {
17978
18254
  const config = getConfig();
17979
18255
  if (!config.python?.root) return null;
17980
- const pythonRoot = resolve10(projectRoot, config.python.root);
17981
- if (!existsSync19(pythonRoot)) {
18256
+ const pythonRoot = resolve12(projectRoot, config.python.root);
18257
+ if (!existsSync22(pythonRoot)) {
17982
18258
  return {
17983
18259
  name: "Python",
17984
18260
  status: "fail",
@@ -17992,15 +18268,15 @@ function checkPythonHealth(projectRoot) {
17992
18268
  function scanDir(dir, depth) {
17993
18269
  if (depth > 5) return;
17994
18270
  try {
17995
- const entries = readdirSync12(dir, { withFileTypes: true });
18271
+ const entries = readdirSync13(dir, { withFileTypes: true });
17996
18272
  for (const entry of entries) {
17997
18273
  if (entry.isDirectory()) {
17998
18274
  const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
17999
18275
  if (!excludeDirs.includes(entry.name)) {
18000
- const subdir = resolve10(dir, entry.name);
18001
- if (depth <= 2 && !existsSync19(resolve10(subdir, "__init__.py"))) {
18276
+ const subdir = resolve12(dir, entry.name);
18277
+ if (depth <= 2 && !existsSync22(resolve12(subdir, "__init__.py"))) {
18002
18278
  try {
18003
- const subEntries = readdirSync12(subdir);
18279
+ const subEntries = readdirSync13(subdir);
18004
18280
  if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
18005
18281
  initPyMissing.push(entry.name);
18006
18282
  }
@@ -18058,11 +18334,12 @@ async function runDoctor() {
18058
18334
  checkMcpServer(projectRoot),
18059
18335
  checkHooksConfig(projectRoot),
18060
18336
  checkHookFiles(projectRoot),
18337
+ await checkHookExecution(projectRoot),
18061
18338
  checkKnowledgeDb(projectRoot),
18062
18339
  checkMemoryDir(projectRoot),
18063
18340
  checkShellHooksWired(projectRoot),
18064
18341
  await checkNativeModules(),
18065
- checkNodeVersion(),
18342
+ checkNodeVersion2(),
18066
18343
  await checkGitRepo(projectRoot),
18067
18344
  await checkLicenseStatus()
18068
18345
  ];
@@ -18095,8 +18372,8 @@ async function runDoctor() {
18095
18372
  }
18096
18373
  async function runValidateConfig() {
18097
18374
  const projectRoot = process.cwd();
18098
- const configPath = resolve10(projectRoot, "massu.config.yaml");
18099
- if (!existsSync19(configPath)) {
18375
+ const configPath = resolve12(projectRoot, "massu.config.yaml");
18376
+ if (!existsSync22(configPath)) {
18100
18377
  console.error("Error: massu.config.yaml not found in current directory");
18101
18378
  console.error("Run: npx massu init");
18102
18379
  process.exit(1);
@@ -18135,7 +18412,7 @@ async function runValidateConfig() {
18135
18412
  process.exit(1);
18136
18413
  }
18137
18414
  }
18138
- var __filename3, __dirname3, EXPECTED_HOOKS;
18415
+ var __filename4, __dirname4, EXPECTED_HOOKS;
18139
18416
  var init_doctor = __esm({
18140
18417
  "src/commands/doctor.ts"() {
18141
18418
  "use strict";
@@ -18145,8 +18422,10 @@ var init_doctor = __esm({
18145
18422
  init_settings_local();
18146
18423
  init_hook_registry();
18147
18424
  init_db_driver();
18148
- __filename3 = fileURLToPath4(import.meta.url);
18149
- __dirname3 = dirname13(__filename3);
18425
+ init_preflight();
18426
+ init_hook_runner();
18427
+ __filename4 = fileURLToPath5(import.meta.url);
18428
+ __dirname4 = dirname14(__filename4);
18150
18429
  EXPECTED_HOOKS = getExpectedHookFiles();
18151
18430
  }
18152
18431
  });
@@ -18435,102 +18714,13 @@ var init_install_hooks = __esm({
18435
18714
  }
18436
18715
  });
18437
18716
 
18438
- // src/commands/hook-runner.ts
18439
- var hook_runner_exports = {};
18440
- __export(hook_runner_exports, {
18441
- HOOK_NAME_TO_FILE: () => HOOK_NAME_TO_FILE,
18442
- resolveHookFile: () => resolveHookFile,
18443
- runHookRunner: () => runHookRunner
18444
- });
18445
- import { existsSync as existsSync20 } from "fs";
18446
- import { resolve as resolve11, dirname as dirname14 } from "path";
18447
- import { fileURLToPath as fileURLToPath5 } from "url";
18448
- import { spawn } from "child_process";
18449
- function resolveHookFile(hookName) {
18450
- const file = HOOK_NAME_TO_FILE[hookName];
18451
- if (!file) {
18452
- throw new Error(
18453
- `Unknown hook: "${hookName}". Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}`
18454
- );
18455
- }
18456
- const candidates = [
18457
- // Bundled compiled layout: dist/cli.js → ./hooks/<file>.js
18458
- resolve11(__dirname4, "hooks", file),
18459
- // TS-source dev / sibling layout: src/commands/ → ../hooks/<file>
18460
- resolve11(__dirname4, "../hooks", file),
18461
- // TS-source dev fallback: src/commands/ → ../../dist/hooks/<file>
18462
- resolve11(__dirname4, "../../dist/hooks", file)
18463
- ];
18464
- for (const candidate of candidates) {
18465
- if (existsSync20(candidate)) {
18466
- return candidate;
18467
- }
18468
- }
18469
- throw new Error(
18470
- `Hook file not found for "${hookName}". Searched: ${candidates.join(", ")}. This indicates a broken @massu/core install. Re-run \`npx -y @massu/core init\`.`
18471
- );
18472
- }
18473
- async function runHookRunner(args2) {
18474
- const hookName = args2[0];
18475
- if (!hookName) {
18476
- process.stderr.write(
18477
- `massu hook-runner: missing hook name.
18478
- Usage: massu hook-runner <hook-name>
18479
- Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}
18480
- `
18481
- );
18482
- return { exitCode: 2 };
18483
- }
18484
- let hookFile;
18485
- try {
18486
- hookFile = resolveHookFile(hookName);
18487
- } catch (err) {
18488
- process.stderr.write(`massu hook-runner: ${err instanceof Error ? err.message : String(err)}
18489
- `);
18490
- return { exitCode: 2 };
18491
- }
18492
- return new Promise((resolvePromise) => {
18493
- const child = spawn(process.execPath, [hookFile], {
18494
- stdio: ["inherit", "inherit", "inherit"],
18495
- // MASSU_HOOK_RUNTIME marks the hook process so the SSOT SQLite loader NEVER
18496
- // self-heals here (5s budget / CR-12, CR-65 P0-003) — even for indirect opens
18497
- // via getMemoryDb(). The next server/CLI touch performs the rebuild.
18498
- env: { ...process.env, MASSU_HOOK_RUNTIME: "1" }
18499
- });
18500
- child.on("exit", (code, signal) => {
18501
- if (signal) {
18502
- resolvePromise({ exitCode: 128 });
18503
- return;
18504
- }
18505
- resolvePromise({ exitCode: code ?? 0 });
18506
- });
18507
- child.on("error", (err) => {
18508
- process.stderr.write(`massu hook-runner: failed to spawn hook "${hookName}": ${err.message}
18509
- `);
18510
- resolvePromise({ exitCode: 2 });
18511
- });
18512
- });
18513
- }
18514
- var __filename4, __dirname4, HOOK_NAME_TO_FILE;
18515
- var init_hook_runner = __esm({
18516
- "src/commands/hook-runner.ts"() {
18517
- "use strict";
18518
- init_hook_registry();
18519
- __filename4 = fileURLToPath5(import.meta.url);
18520
- __dirname4 = dirname14(__filename4);
18521
- HOOK_NAME_TO_FILE = Object.freeze(
18522
- Object.fromEntries(REGISTERED_HOOKS.map((name) => [name, `${name}.js`]))
18523
- );
18524
- }
18525
- });
18526
-
18527
18717
  // src/commands/permissions.ts
18528
18718
  var permissions_exports = {};
18529
18719
  __export(permissions_exports, {
18530
18720
  handlePermissionsSubcommand: () => handlePermissionsSubcommand,
18531
18721
  printPermissionsHelp: () => printPermissionsHelp
18532
18722
  });
18533
- import { resolve as resolve12 } from "path";
18723
+ import { resolve as resolve13 } from "path";
18534
18724
  function resolveClaudeDir2() {
18535
18725
  let claudeDirName = ".claude";
18536
18726
  try {
@@ -18538,7 +18728,7 @@ function resolveClaudeDir2() {
18538
18728
  } catch {
18539
18729
  claudeDirName = ".claude";
18540
18730
  }
18541
- return resolve12(process.cwd(), claudeDirName);
18731
+ return resolve13(process.cwd(), claudeDirName);
18542
18732
  }
18543
18733
  async function handlePermissionsSubcommand(args2) {
18544
18734
  const sub = args2[0];
@@ -18656,7 +18846,7 @@ var db_exports = {};
18656
18846
  __export(db_exports, {
18657
18847
  handleDbSubcommand: () => handleDbSubcommand
18658
18848
  });
18659
- import { existsSync as existsSync21, statSync as statSync10 } from "fs";
18849
+ import { existsSync as existsSync23, statSync as statSync11 } from "fs";
18660
18850
  import { basename as basename7 } from "path";
18661
18851
  function human(bytes) {
18662
18852
  if (bytes < 1024) return `${bytes} B`;
@@ -18701,7 +18891,7 @@ async function handleDbSubcommand(args2) {
18701
18891
 
18702
18892
  `);
18703
18893
  for (const store of allStores()) {
18704
- if (!existsSync21(store.path)) {
18894
+ if (!existsSync23(store.path)) {
18705
18895
  process.stdout.write(` ${"\u2014"} ${store.name.padEnd(10)} not present, skipped
18706
18896
  `);
18707
18897
  continue;
@@ -18799,12 +18989,12 @@ Restore with: massu db restore --latest memory
18799
18989
  target = found.path;
18800
18990
  }
18801
18991
  try {
18802
- const before = existsSync21(target) ? statSync10(target).size : 0;
18992
+ const before = existsSync23(target) ? statSync11(target).size : 0;
18803
18993
  const res = restoreDb(backupPath, target);
18804
18994
  process.stdout.write(
18805
18995
  `Restored ${basename7(target)}
18806
18996
  from : ${res.restoredFrom}
18807
- size : ${human(before)} -> ${human(statSync10(target).size)}
18997
+ size : ${human(before)} -> ${human(statSync11(target).size)}
18808
18998
  ` + (res.preservedOriginal ? ` the previous file was NOT destroyed \u2014 kept at:
18809
18999
  ${res.preservedOriginal}
18810
19000
  ` : "") + ` integrity_check: ok (verified before AND after the restore)
@@ -18965,8 +19155,8 @@ var init_license2 = __esm({
18965
19155
  });
18966
19156
 
18967
19157
  // src/changelog-generator.ts
18968
- import { existsSync as existsSync22, readFileSync as readFileSync17, readdirSync as readdirSync13 } from "fs";
18969
- import { resolve as resolve13 } from "path";
19158
+ import { existsSync as existsSync24, readFileSync as readFileSync17, readdirSync as readdirSync14 } from "fs";
19159
+ import { resolve as resolve14 } from "path";
18970
19160
  function parseCommitsForPlanTokens(subjects) {
18971
19161
  const tokens = /* @__PURE__ */ new Set();
18972
19162
  const maintenance = [];
@@ -18983,15 +19173,15 @@ function parseCommitsForPlanTokens(subjects) {
18983
19173
  function loadPlanSummaries(tokens, planDir) {
18984
19174
  const result = /* @__PURE__ */ new Map();
18985
19175
  if (tokens.size === 0) return result;
18986
- if (!existsSync22(planDir)) {
19176
+ if (!existsSync24(planDir)) {
18987
19177
  throw new Error(`Plan directory does not exist: ${planDir}`);
18988
19178
  }
18989
- const files = readdirSync13(planDir).filter((f2) => f2.endsWith(".md"));
19179
+ const files = readdirSync14(planDir).filter((f2) => f2.endsWith(".md"));
18990
19180
  for (const token of tokens) {
18991
19181
  let matchedFile = null;
18992
19182
  let content = "";
18993
19183
  for (const file of files) {
18994
- const path = resolve13(planDir, file);
19184
+ const path = resolve14(planDir, file);
18995
19185
  const text18 = readFileSync17(path, "utf-8");
18996
19186
  const tokenRe = new RegExp(
18997
19187
  `^\\*\\*Plan Token\\*\\*:\\s*\`?${token.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\`?(\\s|$)`,
@@ -19073,8 +19263,8 @@ __export(changelog_exports, {
19073
19263
  printChangelogHelp: () => printChangelogHelp
19074
19264
  });
19075
19265
  import { execSync } from "child_process";
19076
- import { existsSync as existsSync23, readFileSync as readFileSync18 } from "fs";
19077
- import { resolve as resolve14 } from "path";
19266
+ import { existsSync as existsSync25, readFileSync as readFileSync18 } from "fs";
19267
+ import { resolve as resolve15 } from "path";
19078
19268
  function resolveRepoRoot() {
19079
19269
  try {
19080
19270
  return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
@@ -19098,8 +19288,8 @@ function getCommitSubjects(range) {
19098
19288
  }
19099
19289
  }
19100
19290
  function getCurrentVersion(repoRoot) {
19101
- const pkgPath = resolve14(repoRoot, "packages/core/package.json");
19102
- if (!existsSync23(pkgPath)) return "0.0.0";
19291
+ const pkgPath = resolve15(repoRoot, "packages/core/package.json");
19292
+ if (!existsSync25(pkgPath)) return "0.0.0";
19103
19293
  const pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
19104
19294
  return pkg.version || "0.0.0";
19105
19295
  }
@@ -19107,8 +19297,8 @@ function todayDate() {
19107
19297
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
19108
19298
  }
19109
19299
  function getLatestChangelogEntryBody(repoRoot) {
19110
- const path = resolve14(repoRoot, "CHANGELOG.md");
19111
- if (!existsSync23(path)) return "";
19300
+ const path = resolve15(repoRoot, "CHANGELOG.md");
19301
+ if (!existsSync25(path)) return "";
19112
19302
  const content = readFileSync18(path, "utf-8");
19113
19303
  const m3 = content.match(/^## \[[\d.]+\][^\n]*\n([\s\S]*?)(?=\n## \[|$)/m);
19114
19304
  return m3 ? m3[1] : "";
@@ -19116,7 +19306,7 @@ function getLatestChangelogEntryBody(repoRoot) {
19116
19306
  async function handleChangelogSubcommand(args2) {
19117
19307
  const sub = args2[0];
19118
19308
  const repoRoot = resolveRepoRoot();
19119
- const planDir = resolve14(repoRoot, "docs/plans");
19309
+ const planDir = resolve15(repoRoot, "docs/plans");
19120
19310
  switch (sub) {
19121
19311
  case "generate": {
19122
19312
  const lastTag = getLastTag();
@@ -19205,11 +19395,11 @@ var init_changelog = __esm({
19205
19395
 
19206
19396
  // src/knowledge-db.ts
19207
19397
  import { dirname as dirname15 } from "path";
19208
- import { existsSync as existsSync24, mkdirSync as mkdirSync11 } from "fs";
19398
+ import { existsSync as existsSync26, mkdirSync as mkdirSync11 } from "fs";
19209
19399
  function getKnowledgeDb() {
19210
19400
  const dbPath = getResolvedPaths().knowledgeDbPath;
19211
19401
  const dir = dirname15(dbPath);
19212
- if (!existsSync24(dir)) {
19402
+ if (!existsSync26(dir)) {
19213
19403
  mkdirSync11(dir, { recursive: true });
19214
19404
  }
19215
19405
  const db = openDatabase2(dbPath);
@@ -19436,7 +19626,7 @@ var memory_embed_backfill_exports = {};
19436
19626
  __export(memory_embed_backfill_exports, {
19437
19627
  runMemoryEmbedBackfill: () => runMemoryEmbedBackfill
19438
19628
  });
19439
- import { existsSync as existsSync25 } from "fs";
19629
+ import { existsSync as existsSync27 } from "fs";
19440
19630
  async function runMemoryEmbedBackfill(_args = []) {
19441
19631
  await embed("massu memory embed backfill probe");
19442
19632
  const active = getActiveEmbedModel();
@@ -19463,7 +19653,7 @@ async function runMemoryEmbedBackfill(_args = []) {
19463
19653
  );
19464
19654
  let chunks = { embedded: 0, scanned: 0 };
19465
19655
  const knowledgeDbPath = getResolvedPaths().knowledgeDbPath;
19466
- if (existsSync25(knowledgeDbPath)) {
19656
+ if (existsSync27(knowledgeDbPath)) {
19467
19657
  const knowledgeDb = getKnowledgeDb();
19468
19658
  try {
19469
19659
  chunks = await embedMissingChunks(knowledgeDb);
@@ -19496,16 +19686,16 @@ var init_memory_embed_backfill = __esm({
19496
19686
 
19497
19687
  // src/memory-authorship.ts
19498
19688
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
19499
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync6, existsSync as existsSync26, mkdirSync as mkdirSync12, chmodSync as chmodSync6, statSync as statSync11 } from "fs";
19500
- import { homedir as homedir7 } from "os";
19501
- import { resolve as resolve15 } from "path";
19502
- function renderKeyPath(home = homedir7()) {
19503
- return resolve15(home, ".massu", "render-key");
19689
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync6, existsSync as existsSync28, mkdirSync as mkdirSync12, chmodSync as chmodSync6, statSync as statSync12 } from "fs";
19690
+ import { homedir as homedir8 } from "os";
19691
+ import { resolve as resolve16 } from "path";
19692
+ function renderKeyPath(home = homedir8()) {
19693
+ return resolve16(home, ".massu", "render-key");
19504
19694
  }
19505
- function readRenderKey(home = homedir7()) {
19695
+ function readRenderKey(home = homedir8()) {
19506
19696
  const p19 = renderKeyPath(home);
19507
19697
  try {
19508
- if (!existsSync26(p19)) return void 0;
19698
+ if (!existsSync28(p19)) return void 0;
19509
19699
  const raw = readFileSync19(p19);
19510
19700
  if (raw.length !== 32) return void 0;
19511
19701
  return raw;
@@ -19513,12 +19703,12 @@ function readRenderKey(home = homedir7()) {
19513
19703
  return void 0;
19514
19704
  }
19515
19705
  }
19516
- function ensureRenderKey(home = homedir7()) {
19706
+ function ensureRenderKey(home = homedir8()) {
19517
19707
  const existing = readRenderKey(home);
19518
19708
  if (existing) return existing;
19519
19709
  const p19 = renderKeyPath(home);
19520
19710
  try {
19521
- const dir = resolve15(home, ".massu");
19711
+ const dir = resolve16(home, ".massu");
19522
19712
  mkdirSync12(dir, { recursive: true, mode: 448 });
19523
19713
  try {
19524
19714
  chmodSync6(dir, 448);
@@ -19535,7 +19725,7 @@ function ensureRenderKey(home = homedir7()) {
19535
19725
  return void 0;
19536
19726
  }
19537
19727
  }
19538
- function mintAuthorship(body, home = homedir7()) {
19728
+ function mintAuthorship(body, home = homedir8()) {
19539
19729
  try {
19540
19730
  const key = ensureRenderKey(home);
19541
19731
  if (!key) return null;
@@ -19544,7 +19734,7 @@ function mintAuthorship(body, home = homedir7()) {
19544
19734
  return null;
19545
19735
  }
19546
19736
  }
19547
- function verifyAuthorship(body, frontmatter, storeRow, home = homedir7()) {
19737
+ function verifyAuthorship(body, frontmatter, storeRow, home = homedir8()) {
19548
19738
  try {
19549
19739
  if (storeRow?.adopted_human_at_epoch != null) return false;
19550
19740
  const key = readRenderKey(home);
@@ -19564,7 +19754,7 @@ function extractRenderMac(frontmatter) {
19564
19754
  const v3 = frontmatter?.[RENDER_MAC_KEY];
19565
19755
  return typeof v3 === "string" && v3.length > 0 ? v3 : null;
19566
19756
  }
19567
- function renderKeyExists(home = homedir7()) {
19757
+ function renderKeyExists(home = homedir8()) {
19568
19758
  return readRenderKey(home) !== void 0;
19569
19759
  }
19570
19760
  var RENDER_MAC_KEY;
@@ -19576,11 +19766,11 @@ var init_memory_authorship = __esm({
19576
19766
  });
19577
19767
 
19578
19768
  // src/rule-candidate-snapshot.ts
19579
- import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4 } from "fs";
19769
+ import { existsSync as existsSync29, readFileSync as readFileSync20, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4 } from "fs";
19580
19770
  function takeSnapshots(paths) {
19581
19771
  const out = /* @__PURE__ */ new Map();
19582
19772
  for (const p19 of paths) {
19583
- if (existsSync27(p19)) out.set(p19, readFileSync20(p19, "utf-8"));
19773
+ if (existsSync29(p19)) out.set(p19, readFileSync20(p19, "utf-8"));
19584
19774
  else out.set(p19, null);
19585
19775
  }
19586
19776
  return out;
@@ -19590,7 +19780,7 @@ function restoreSnapshots(snapshot) {
19590
19780
  for (const [path, content] of snapshot) {
19591
19781
  try {
19592
19782
  if (content === null) {
19593
- if (existsSync27(path)) unlinkSync4(path);
19783
+ if (existsSync29(path)) unlinkSync4(path);
19594
19784
  } else {
19595
19785
  writeFileSync7(path, content, "utf-8");
19596
19786
  }
@@ -19670,7 +19860,7 @@ var init_memory_render_path = __esm({
19670
19860
  });
19671
19861
 
19672
19862
  // src/memory-tombstones.ts
19673
- import { readFileSync as readFileSync21, appendFileSync as appendFileSync3, existsSync as existsSync28 } from "fs";
19863
+ import { readFileSync as readFileSync21, appendFileSync as appendFileSync3, existsSync as existsSync30 } from "fs";
19674
19864
  import { join as join16 } from "path";
19675
19865
  function tombstoneLedgerPath(memoryDir) {
19676
19866
  return join16(memoryDir, TOMBSTONE_LEDGER);
@@ -19678,7 +19868,7 @@ function tombstoneLedgerPath(memoryDir) {
19678
19868
  function readTombstones(memoryDir) {
19679
19869
  const out = /* @__PURE__ */ new Map();
19680
19870
  const p19 = tombstoneLedgerPath(memoryDir);
19681
- if (!existsSync28(p19)) return out;
19871
+ if (!existsSync30(p19)) return out;
19682
19872
  let raw;
19683
19873
  try {
19684
19874
  raw = readFileSync21(p19, "utf8");
@@ -19920,29 +20110,29 @@ var init_memory_llm = __esm({
19920
20110
 
19921
20111
  // src/memory-backup.ts
19922
20112
  import {
19923
- readdirSync as readdirSync14,
20113
+ readdirSync as readdirSync15,
19924
20114
  mkdirSync as mkdirSync13,
19925
20115
  copyFileSync as copyFileSync2,
19926
- statSync as statSync12,
19927
- existsSync as existsSync29,
20116
+ statSync as statSync13,
20117
+ existsSync as existsSync31,
19928
20118
  rmSync as rmSync7,
19929
20119
  readFileSync as readFileSync22
19930
20120
  } from "fs";
19931
- import { homedir as homedir8 } from "os";
19932
- import { join as join17, resolve as resolve16 } from "path";
19933
- function backupsRoot(home = homedir8()) {
19934
- return resolve16(home, ".massu", "memory-backups");
20121
+ import { homedir as homedir9 } from "os";
20122
+ import { join as join17, resolve as resolve17 } from "path";
20123
+ function backupsRoot(home = homedir9()) {
20124
+ return resolve17(home, ".massu", "memory-backups");
19935
20125
  }
19936
20126
  function backupStamp2(nowMs) {
19937
20127
  return new Date(nowMs).toISOString().replace(/:/g, "-");
19938
20128
  }
19939
20129
  function newestMtimeMs(dir) {
19940
- if (!existsSync29(dir)) return 0;
20130
+ if (!existsSync31(dir)) return 0;
19941
20131
  let newest = 0;
19942
- for (const entry of readdirSync14(dir)) {
20132
+ for (const entry of readdirSync15(dir)) {
19943
20133
  const p19 = join17(dir, entry);
19944
20134
  try {
19945
- const st = statSync12(p19);
20135
+ const st = statSync13(p19);
19946
20136
  if (st.isFile() && st.mtimeMs > newest) newest = st.mtimeMs;
19947
20137
  } catch {
19948
20138
  continue;
@@ -19950,14 +20140,14 @@ function newestMtimeMs(dir) {
19950
20140
  }
19951
20141
  return newest;
19952
20142
  }
19953
- function listBackups(home = homedir8()) {
20143
+ function listBackups(home = homedir9()) {
19954
20144
  const root = backupsRoot(home);
19955
- if (!existsSync29(root)) return [];
20145
+ if (!existsSync31(root)) return [];
19956
20146
  const out = [];
19957
- for (const stamp of readdirSync14(root)) {
20147
+ for (const stamp of readdirSync15(root)) {
19958
20148
  const dir = join17(root, stamp);
19959
20149
  try {
19960
- const st = statSync12(dir);
20150
+ const st = statSync13(dir);
19961
20151
  if (!st.isDirectory()) continue;
19962
20152
  out.push({ stamp, dir, createdMs: st.mtimeMs });
19963
20153
  } catch {
@@ -19966,24 +20156,24 @@ function listBackups(home = homedir8()) {
19966
20156
  }
19967
20157
  return out.sort((a2, b2) => b2.createdMs - a2.createdMs);
19968
20158
  }
19969
- function hasFreshBackup(memoryDir, home = homedir8()) {
20159
+ function hasFreshBackup(memoryDir, home = homedir9()) {
19970
20160
  const backups = listBackups(home);
19971
20161
  if (backups.length === 0) return false;
19972
20162
  const newestChange = newestMtimeMs(memoryDir);
19973
20163
  return backups[0].createdMs >= newestChange;
19974
20164
  }
19975
- function takeBackup(memoryDir, nowMs, home = homedir8(), retention = DEFAULT_RETENTION2) {
19976
- if (!existsSync29(memoryDir)) {
20165
+ function takeBackup(memoryDir, nowMs, home = homedir9(), retention = DEFAULT_RETENTION2) {
20166
+ if (!existsSync31(memoryDir)) {
19977
20167
  throw new BackupError(`memory directory does not exist: ${memoryDir}`);
19978
20168
  }
19979
20169
  const stamp = backupStamp2(nowMs);
19980
20170
  const dir = join17(backupsRoot(home), stamp);
19981
20171
  try {
19982
20172
  mkdirSync13(dir, { recursive: true, mode: 448 });
19983
- for (const entry of readdirSync14(memoryDir)) {
20173
+ for (const entry of readdirSync15(memoryDir)) {
19984
20174
  const srcPath = join17(memoryDir, entry);
19985
20175
  try {
19986
- if (!statSync12(srcPath).isFile()) continue;
20176
+ if (!statSync13(srcPath).isFile()) continue;
19987
20177
  } catch {
19988
20178
  continue;
19989
20179
  }
@@ -19995,7 +20185,7 @@ function takeBackup(memoryDir, nowMs, home = homedir8(), retention = DEFAULT_RET
19995
20185
  pruneBackups(home, retention);
19996
20186
  return { stamp, dir, createdMs: nowMs };
19997
20187
  }
19998
- function pruneBackups(home = homedir8(), retention = DEFAULT_RETENTION2) {
20188
+ function pruneBackups(home = homedir9(), retention = DEFAULT_RETENTION2) {
19999
20189
  const backups = listBackups(home);
20000
20190
  for (const stale of backups.slice(Math.max(retention, 1))) {
20001
20191
  try {
@@ -20005,22 +20195,22 @@ function pruneBackups(home = homedir8(), retention = DEFAULT_RETENTION2) {
20005
20195
  }
20006
20196
  }
20007
20197
  function restoreBackup(memoryDir, opts = {}) {
20008
- const home = opts.home ?? homedir8();
20198
+ const home = opts.home ?? homedir9();
20009
20199
  const backups = listBackups(home);
20010
20200
  if (backups.length === 0) throw new BackupError("no backups exist");
20011
20201
  const chosen = opts.from ? backups.find((b2) => b2.stamp === opts.from) : backups[0];
20012
20202
  if (!chosen) throw new BackupError(`no backup with stamp: ${opts.from}`);
20013
20203
  const wouldRestore = [];
20014
- for (const entry of readdirSync14(chosen.dir)) {
20204
+ for (const entry of readdirSync15(chosen.dir)) {
20015
20205
  try {
20016
- if (statSync12(join17(chosen.dir, entry)).isFile()) wouldRestore.push(entry);
20206
+ if (statSync13(join17(chosen.dir, entry)).isFile()) wouldRestore.push(entry);
20017
20207
  } catch {
20018
20208
  continue;
20019
20209
  }
20020
20210
  }
20021
- const currentFiles = existsSync29(memoryDir) ? readdirSync14(memoryDir).filter((e2) => {
20211
+ const currentFiles = existsSync31(memoryDir) ? readdirSync15(memoryDir).filter((e2) => {
20022
20212
  try {
20023
- return statSync12(join17(memoryDir, e2)).isFile();
20213
+ return statSync13(join17(memoryDir, e2)).isFile();
20024
20214
  } catch {
20025
20215
  return false;
20026
20216
  }
@@ -20064,7 +20254,7 @@ var init_memory_backup = __esm({
20064
20254
  });
20065
20255
 
20066
20256
  // src/memory-index-region.ts
20067
- import { readFileSync as readFileSync23, existsSync as existsSync30 } from "fs";
20257
+ import { readFileSync as readFileSync23, existsSync as existsSync32 } from "fs";
20068
20258
  import { dirname as dirname16, join as join18 } from "path";
20069
20259
  function parseRegion(content) {
20070
20260
  const begins = countOccurrences(content, BEGIN_SENTINEL);
@@ -20169,7 +20359,7 @@ function withMemoryIndexLock(memoryDir, fn) {
20169
20359
  });
20170
20360
  }
20171
20361
  function readMemoryIndex(indexPath) {
20172
- if (!existsSync30(indexPath)) return void 0;
20362
+ if (!existsSync32(indexPath)) return void 0;
20173
20363
  try {
20174
20364
  return readFileSync23(indexPath, "utf8");
20175
20365
  } catch {
@@ -20242,9 +20432,9 @@ var init_memory_files_config = __esm({
20242
20432
  });
20243
20433
 
20244
20434
  // src/memory-renderer.ts
20245
- import { existsSync as existsSync31, readFileSync as readFileSync24 } from "fs";
20435
+ import { existsSync as existsSync33, readFileSync as readFileSync24 } from "fs";
20246
20436
  import { join as join19 } from "path";
20247
- import { homedir as homedir9 } from "os";
20437
+ import { homedir as homedir10 } from "os";
20248
20438
  import { createHash as createHash8 } from "crypto";
20249
20439
  function renderMemoryFiles(db, candidates, opts) {
20250
20440
  const config = opts.config ?? resolveMemoryFilesConfig();
@@ -20253,11 +20443,11 @@ function renderMemoryFiles(db, candidates, opts) {
20253
20443
  }
20254
20444
  const dryRun = opts.dryRun === true;
20255
20445
  const now = opts.now ?? Date.now();
20256
- const home = opts.home ?? homedir9();
20446
+ const home = opts.home ?? homedir10();
20257
20447
  const audit = opts.audit ?? (() => {
20258
20448
  });
20259
20449
  const { memoryDir } = opts;
20260
- if (!existsSync31(memoryDir)) {
20450
+ if (!existsSync33(memoryDir)) {
20261
20451
  return EMPTY("no_memory_dir");
20262
20452
  }
20263
20453
  try {
@@ -20325,7 +20515,7 @@ function renderLocked(db, candidates, o) {
20325
20515
  refusals.push({ name: c2.name, reason: "tombstoned" });
20326
20516
  continue;
20327
20517
  }
20328
- if (existsSync31(absPath)) {
20518
+ if (existsSync33(absPath)) {
20329
20519
  const existing = readFileSync24(absPath, "utf8");
20330
20520
  const fm = readMemoryFileFrontmatter(existing);
20331
20521
  const storeRow = db.prepare(
@@ -20548,22 +20738,22 @@ var init_memory_renderer = __esm({
20548
20738
  });
20549
20739
 
20550
20740
  // src/commands/memory-render-cli.ts
20551
- import { readdirSync as readdirSync15, readFileSync as readFileSync25, existsSync as existsSync32, rmSync as rmSync8, statSync as statSync13 } from "fs";
20741
+ import { readdirSync as readdirSync16, readFileSync as readFileSync25, existsSync as existsSync34, rmSync as rmSync8, statSync as statSync14 } from "fs";
20552
20742
  import { join as join20 } from "path";
20553
- import { homedir as homedir10 } from "os";
20743
+ import { homedir as homedir11 } from "os";
20554
20744
  function memoryFiles(memoryDir) {
20555
- if (!existsSync32(memoryDir)) return [];
20556
- return readdirSync15(memoryDir).filter((f2) => {
20745
+ if (!existsSync34(memoryDir)) return [];
20746
+ return readdirSync16(memoryDir).filter((f2) => {
20557
20747
  if (!f2.endsWith(".md")) return false;
20558
20748
  if (f2 === "MEMORY.md") return false;
20559
20749
  try {
20560
- return statSync13(join20(memoryDir, f2)).isFile();
20750
+ return statSync14(join20(memoryDir, f2)).isFile();
20561
20751
  } catch {
20562
20752
  return false;
20563
20753
  }
20564
20754
  });
20565
20755
  }
20566
- function massuAuthoredFiles(db, memoryDir, home = homedir10()) {
20756
+ function massuAuthoredFiles(db, memoryDir, home = homedir11()) {
20567
20757
  const out = [];
20568
20758
  for (const f2 of memoryFiles(memoryDir)) {
20569
20759
  let content;
@@ -20583,7 +20773,7 @@ function massuAuthoredFiles(db, memoryDir, home = homedir10()) {
20583
20773
  return out;
20584
20774
  }
20585
20775
  function memoryAdopt(db, memoryDir, opts = {}) {
20586
- const home = opts.home ?? homedir10();
20776
+ const home = opts.home ?? homedir11();
20587
20777
  const now = opts.now ?? Math.floor(Date.now() / 1e3);
20588
20778
  const lines = [];
20589
20779
  if (!renderKeyExists(home)) {
@@ -20649,7 +20839,7 @@ function memoryAdopt(db, memoryDir, opts = {}) {
20649
20839
  return { exitCode: 0, output: lines.join("\n") };
20650
20840
  }
20651
20841
  function memoryUnrender(db, memoryDir, opts = {}) {
20652
- const home = opts.home ?? homedir10();
20842
+ const home = opts.home ?? homedir11();
20653
20843
  const now = opts.now ?? Math.floor(Date.now() / 1e3);
20654
20844
  const lines = [];
20655
20845
  const ours = massuAuthoredFiles(db, memoryDir, home);
@@ -20697,7 +20887,7 @@ function memoryUnrender(db, memoryDir, opts = {}) {
20697
20887
  return { exitCode: 0, output: lines.join("\n") };
20698
20888
  }
20699
20889
  function memoryRestore(memoryDir, opts = {}) {
20700
- const home = opts.home ?? homedir10();
20890
+ const home = opts.home ?? homedir11();
20701
20891
  const backups = listBackups(home);
20702
20892
  if (backups.length === 0) {
20703
20893
  return { exitCode: 1, output: "No backups exist yet. Nothing to restore." };
@@ -20766,16 +20956,16 @@ var init_memory_render_candidates = __esm({
20766
20956
  });
20767
20957
 
20768
20958
  // src/capability-advisor.ts
20769
- import { existsSync as existsSync33, mkdirSync as mkdirSync14, readFileSync as readFileSync26, writeFileSync as writeFileSync8 } from "fs";
20770
- import { homedir as homedir11 } from "os";
20959
+ import { existsSync as existsSync35, mkdirSync as mkdirSync14, readFileSync as readFileSync26, writeFileSync as writeFileSync8 } from "fs";
20960
+ import { homedir as homedir12 } from "os";
20771
20961
  import { dirname as dirname17, join as join21 } from "path";
20772
- function advisorStatePath(home = homedir11()) {
20962
+ function advisorStatePath(home = homedir12()) {
20773
20963
  return join21(home, ".massu", "advisor-state.json");
20774
20964
  }
20775
20965
  function readAdvisorState(home) {
20776
20966
  try {
20777
20967
  const p19 = advisorStatePath(home);
20778
- if (!existsSync33(p19)) return {};
20968
+ if (!existsSync35(p19)) return {};
20779
20969
  return JSON.parse(readFileSync26(p19, "utf-8"));
20780
20970
  } catch {
20781
20971
  return {};
@@ -21011,22 +21201,22 @@ var init_memory_repo_identity = __esm({
21011
21201
  // src/memory-repos-registry.ts
21012
21202
  import {
21013
21203
  chmodSync as chmodSync7,
21014
- existsSync as existsSync34,
21204
+ existsSync as existsSync36,
21015
21205
  mkdirSync as mkdirSync15,
21016
21206
  readFileSync as readFileSync27,
21017
21207
  writeFileSync as writeFileSync9
21018
21208
  } from "fs";
21019
- import { homedir as homedir12 } from "os";
21209
+ import { homedir as homedir13 } from "os";
21020
21210
  import { dirname as dirname18, join as join22 } from "path";
21021
- function reposRegistryPath(home = homedir12()) {
21211
+ function reposRegistryPath(home = homedir13()) {
21022
21212
  return join22(home, ".massu", "repos.json");
21023
21213
  }
21024
21214
  function emptyRegistry() {
21025
21215
  return { version: REPOS_REGISTRY_VERSION, repos: [] };
21026
21216
  }
21027
- function readReposRegistry(home = homedir12()) {
21217
+ function readReposRegistry(home = homedir13()) {
21028
21218
  const p19 = reposRegistryPath(home);
21029
- if (!existsSync34(p19)) return emptyRegistry();
21219
+ if (!existsSync36(p19)) return emptyRegistry();
21030
21220
  try {
21031
21221
  const parsed = JSON.parse(readFileSync27(p19, "utf-8"));
21032
21222
  if (!parsed || typeof parsed !== "object") return emptyRegistry();
@@ -21076,12 +21266,12 @@ var init_shared_memory_envelope = __esm({
21076
21266
  // src/security/local-share-signer.ts
21077
21267
  import {
21078
21268
  chmodSync as chmodSync8,
21079
- existsSync as existsSync35,
21269
+ existsSync as existsSync37,
21080
21270
  mkdirSync as mkdirSync16,
21081
21271
  readFileSync as readFileSync28,
21082
21272
  writeFileSync as writeFileSync10
21083
21273
  } from "fs";
21084
- import { homedir as homedir13 } from "os";
21274
+ import { homedir as homedir14 } from "os";
21085
21275
  import { join as join23 } from "path";
21086
21276
  import {
21087
21277
  createHash as createHash10,
@@ -21090,18 +21280,18 @@ import {
21090
21280
  generateKeyPairSync,
21091
21281
  sign as cryptoSign
21092
21282
  } from "crypto";
21093
- function localShareKeyDir(home = homedir13()) {
21283
+ function localShareKeyDir(home = homedir14()) {
21094
21284
  return join23(home, ".massu", "keys");
21095
21285
  }
21096
- function localSharePubKeyPath(home = homedir13()) {
21286
+ function localSharePubKeyPath(home = homedir14()) {
21097
21287
  return join23(localShareKeyDir(home), "local-share.pub");
21098
21288
  }
21099
- function readLocalSharePublicKeyRaw(home = homedir13()) {
21289
+ function readLocalSharePublicKeyRaw(home = homedir14()) {
21100
21290
  const pubPem = readFileSync28(localSharePubKeyPath(home), "utf-8");
21101
21291
  const der = createPublicKey2(pubPem).export({ format: "der", type: "spki" });
21102
21292
  return Uint8Array.from(der.subarray(SPKI_ED25519_PREFIX_LEN));
21103
21293
  }
21104
- function localSharePubkeyFingerprint(home = homedir13()) {
21294
+ function localSharePubkeyFingerprint(home = homedir14()) {
21105
21295
  return createHash10("sha256").update(Buffer.from(readLocalSharePublicKeyRaw(home))).digest("hex");
21106
21296
  }
21107
21297
  var SPKI_ED25519_PREFIX_LEN;
@@ -21114,8 +21304,8 @@ var init_local_share_signer = __esm({
21114
21304
  });
21115
21305
 
21116
21306
  // src/security/local-share-verifier.ts
21117
- import { homedir as homedir14 } from "os";
21118
- function verifyLocalShareEnvelope(envelope, pinnedFingerprint, home = homedir14()) {
21307
+ import { homedir as homedir15 } from "os";
21308
+ function verifyLocalShareEnvelope(envelope, pinnedFingerprint, home = homedir15()) {
21119
21309
  let key;
21120
21310
  try {
21121
21311
  const pubkeyBytes = readLocalSharePublicKeyRaw(home);
@@ -21189,9 +21379,9 @@ var init_shared_memory_audit = __esm({
21189
21379
  });
21190
21380
 
21191
21381
  // src/shared-memory-sync.ts
21192
- import { homedir as homedir15 } from "os";
21382
+ import { homedir as homedir16 } from "os";
21193
21383
  function acceptSharedMemory(db, recordHash, opts = {}) {
21194
- const home = opts.home ?? homedir15();
21384
+ const home = opts.home ?? homedir16();
21195
21385
  const nowEpoch = opts.nowEpoch ?? Math.floor(Date.now() / 1e3);
21196
21386
  const row = loadPending(db, recordHash);
21197
21387
  if (!row) return refuse(db, recordHash, "not_found");
@@ -21296,7 +21486,7 @@ function refuseSharedMemory(db, recordHash, opts = {}) {
21296
21486
  return { ok: true };
21297
21487
  }
21298
21488
  function listPendingSharedMemories(db, opts = {}) {
21299
- const home = opts.home ?? homedir15();
21489
+ const home = opts.home ?? homedir16();
21300
21490
  const rows = db.prepare(
21301
21491
  `SELECT record_hash, origin_repo_id, origin_repo_label, envelope_raw, record_json, received_at_epoch
21302
21492
  FROM shared_memory_pending
@@ -21401,9 +21591,9 @@ var memory_share_cli_exports = {};
21401
21591
  __export(memory_share_cli_exports, {
21402
21592
  runMemoryShareCli: () => runMemoryShareCli
21403
21593
  });
21404
- import { homedir as homedir16 } from "os";
21594
+ import { homedir as homedir17 } from "os";
21405
21595
  async function runMemoryShareCli(sub, args2, opts = {}) {
21406
- const home = opts.home ?? homedir16();
21596
+ const home = opts.home ?? homedir17();
21407
21597
  const ownDb = !opts.db;
21408
21598
  const db = opts.db ?? getMemoryDb();
21409
21599
  try {
@@ -21996,14 +22186,14 @@ var init_rule_candidate_hardened = __esm({
21996
22186
 
21997
22187
  // src/security/promotion-apply-mac.ts
21998
22188
  import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
21999
- import { homedir as homedir17 } from "os";
22189
+ import { homedir as homedir18 } from "os";
22000
22190
  function canonical(f2) {
22001
22191
  return [DOMAIN, f2.origin, f2.org_id, f2.prompt_hash, f2.destination, f2.draft_text].join("\n");
22002
22192
  }
22003
22193
  function deriveApplyKey(renderKey) {
22004
22194
  return createHmac2("sha256", renderKey).update(DOMAIN, "utf8").digest();
22005
22195
  }
22006
- function computePromotionApplyMac(fields, home = homedir17()) {
22196
+ function computePromotionApplyMac(fields, home = homedir18()) {
22007
22197
  try {
22008
22198
  const key = ensureRenderKey(home);
22009
22199
  if (!key) return null;
@@ -22012,7 +22202,7 @@ function computePromotionApplyMac(fields, home = homedir17()) {
22012
22202
  return null;
22013
22203
  }
22014
22204
  }
22015
- function verifyPromotionApplyMac(claimed, fields, home = homedir17()) {
22205
+ function verifyPromotionApplyMac(claimed, fields, home = homedir18()) {
22016
22206
  try {
22017
22207
  if (typeof claimed !== "string" || claimed.length === 0) return false;
22018
22208
  const key = readRenderKey(home);
@@ -22036,9 +22226,9 @@ var init_promotion_apply_mac = __esm({
22036
22226
  });
22037
22227
 
22038
22228
  // src/rule-candidate-applier.ts
22039
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync11, existsSync as existsSync36, unlinkSync as unlinkSync5, mkdirSync as mkdirSync17 } from "fs";
22040
- import { join as join24, dirname as dirname19, isAbsolute as isAbsolute2 } from "path";
22041
- import { homedir as homedir18 } from "os";
22229
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync11, existsSync as existsSync38, unlinkSync as unlinkSync5, mkdirSync as mkdirSync17 } from "fs";
22230
+ import { join as join24, dirname as dirname19, isAbsolute as isAbsolute3 } from "path";
22231
+ import { homedir as homedir19 } from "os";
22042
22232
  import { createHash as createHash11 } from "crypto";
22043
22233
  function isRuleDestination(value) {
22044
22234
  return typeof value === "string" && ALL_RULE_DESTINATIONS.includes(value);
@@ -22046,10 +22236,10 @@ function isRuleDestination(value) {
22046
22236
  function isTeamShareableDestination(destination) {
22047
22237
  return TEAM_SHAREABLE_DESTINATIONS.includes(destination);
22048
22238
  }
22049
- function resolveMemoryDir(projectRoot = getProjectRoot(), home = homedir18()) {
22239
+ function resolveMemoryDir(projectRoot = getProjectRoot(), home = homedir19()) {
22050
22240
  return join24(home, ".claude", "projects", encodeMemoryDirName(projectRoot), "memory");
22051
22241
  }
22052
- function resolveMemoryIndexPath(projectRoot = getProjectRoot(), home = homedir18()) {
22242
+ function resolveMemoryIndexPath(projectRoot = getProjectRoot(), home = homedir19()) {
22053
22243
  return join24(resolveMemoryDir(projectRoot, home), "MEMORY.md");
22054
22244
  }
22055
22245
  function deriveSlug2(prompt) {
@@ -22079,7 +22269,7 @@ function readCandidate(candidateId, projectRoot = getProjectRoot()) {
22079
22269
  throw new InvalidCandidateIdError(candidateId);
22080
22270
  }
22081
22271
  const path = join24(projectRoot, ".massu", "rule-candidates", `${candidateId}.json`);
22082
- if (!existsSync36(path)) throw new CandidateNotFoundError(path);
22272
+ if (!existsSync38(path)) throw new CandidateNotFoundError(path);
22083
22273
  const raw = readFileSync29(path, "utf-8");
22084
22274
  let parsed;
22085
22275
  try {
@@ -22172,7 +22362,7 @@ async function applyRuleCandidate(db, opts) {
22172
22362
  throw new InvalidCandidateIdError(opts.candidateId);
22173
22363
  }
22174
22364
  const projectRoot = opts.projectRoot ?? getProjectRoot();
22175
- const home = opts.home ?? homedir18();
22365
+ const home = opts.home ?? homedir19();
22176
22366
  const candidate = readCandidate(opts.candidateId, projectRoot);
22177
22367
  if (candidate.provenance !== void 0) {
22178
22368
  const provOrigin = candidate.provenance.origin;
@@ -22223,7 +22413,7 @@ async function applyRuleCandidate(db, opts) {
22223
22413
  throw new InvalidSlugError(slug);
22224
22414
  }
22225
22415
  const memoryIndex = resolveMemoryIndexPath(projectRoot, home);
22226
- if (!existsSync36(memoryIndex)) {
22416
+ if (!existsSync38(memoryIndex)) {
22227
22417
  throw new MemoryIndexMissingError(memoryIndex);
22228
22418
  }
22229
22419
  let destTargets;
@@ -22279,7 +22469,7 @@ async function applyRuleCandidate(db, opts) {
22279
22469
  throw err;
22280
22470
  }
22281
22471
  }
22282
- if (existsSync36(candidatePath)) unlinkSync5(candidatePath);
22472
+ if (existsSync38(candidatePath)) unlinkSync5(candidatePath);
22283
22473
  let teamShared = false;
22284
22474
  const localOrigin = candidate.provenance === void 0;
22285
22475
  const entitledTeam = entitledForTeamSharedPromotion(ent.currentTier);
@@ -22340,7 +22530,7 @@ function computeDestinationTargets(opts, candidate, slug, projectRoot, home) {
22340
22530
  throw new DestinationConfigError("custom-destination requires customDestination config entry");
22341
22531
  }
22342
22532
  const raw = opts.customDestination.path;
22343
- const absolute = isAbsolute2(raw) ? raw : join24(projectRoot, raw);
22533
+ const absolute = isAbsolute3(raw) ? raw : join24(projectRoot, raw);
22344
22534
  try {
22345
22535
  assertContainedIn(projectRoot, absolute, { allowNested: true });
22346
22536
  } catch (err) {
@@ -22380,8 +22570,8 @@ function writeCorrectionsMd(opts, candidate, slug, targets, auditRowId) {
22380
22570
  audit_log_id: auditRowId
22381
22571
  }
22382
22572
  });
22383
- if (!existsSync36(dirname19(correctionsMd))) mkdirSync17(dirname19(correctionsMd), { recursive: true });
22384
- const correctionsPre = existsSync36(correctionsMd) ? readFileSync29(correctionsMd, "utf-8") : "# Corrections\n";
22573
+ if (!existsSync38(dirname19(correctionsMd))) mkdirSync17(dirname19(correctionsMd), { recursive: true });
22574
+ const correctionsPre = existsSync38(correctionsMd) ? readFileSync29(correctionsMd, "utf-8") : "# Corrections\n";
22385
22575
  writeFileSync11(correctionsMd, correctionsPre + correctionsEntry, "utf-8");
22386
22576
  const body = `${opts.draftText}
22387
22577
  `;
@@ -22407,7 +22597,7 @@ function writeCorrectionsMd(opts, candidate, slug, targets, auditRowId) {
22407
22597
  function writePatternScanner(opts, candidate, slug, targets) {
22408
22598
  const [scannerPath, driftGuardPath] = targets.paths;
22409
22599
  const checkNumber = opts.patternScannerCheckNumber;
22410
- const scannerPre = existsSync36(scannerPath) ? readFileSync29(scannerPath, "utf-8") : "";
22600
+ const scannerPre = existsSync38(scannerPath) ? readFileSync29(scannerPath, "utf-8") : "";
22411
22601
  if (scannerPre.includes(`${SCANNER_AUTOGEN_MARKER_PREFIX} ${candidate.prompt_hash}`)) {
22412
22602
  throw new DestinationConfigError(
22413
22603
  `scanner already contains a check auto-generated for prompt_hash ${candidate.prompt_hash}`
@@ -22443,13 +22633,13 @@ function writePatternScanner(opts, candidate, slug, targets) {
22443
22633
  `});`,
22444
22634
  ""
22445
22635
  ].join("\n");
22446
- if (!existsSync36(dirname19(driftGuardPath))) mkdirSync17(dirname19(driftGuardPath), { recursive: true });
22636
+ if (!existsSync38(dirname19(driftGuardPath))) mkdirSync17(dirname19(driftGuardPath), { recursive: true });
22447
22637
  writeFileSync11(driftGuardPath, driftGuard, "utf-8");
22448
22638
  }
22449
22639
  function writeClaudeMdCr(opts, candidate, slug, targets) {
22450
22640
  const [claudePath] = targets.paths;
22451
22641
  const crNumber = opts.claudeMdCrNumber;
22452
- const pre = existsSync36(claudePath) ? readFileSync29(claudePath, "utf-8") : "";
22642
+ const pre = existsSync38(claudePath) ? readFileSync29(claudePath, "utf-8") : "";
22453
22643
  if (new RegExp(`\\bCR-${crNumber}\\b`).test(pre)) {
22454
22644
  throw new DestinationConfigError(`CLAUDE.md already contains a CR-${crNumber} entry`);
22455
22645
  }
@@ -22491,8 +22681,8 @@ function writeCustomDestination(opts, candidate, slug, targets) {
22491
22681
  destination_name: entry.name
22492
22682
  };
22493
22683
  const rendered = renderTemplate2(entry.template, vars);
22494
- if (!existsSync36(dirname19(destPath))) mkdirSync17(dirname19(destPath), { recursive: true });
22495
- const pre = existsSync36(destPath) ? readFileSync29(destPath, "utf-8") : "";
22684
+ if (!existsSync38(dirname19(destPath))) mkdirSync17(dirname19(destPath), { recursive: true });
22685
+ const pre = existsSync38(destPath) ? readFileSync29(destPath, "utf-8") : "";
22496
22686
  const sep3 = pre && !pre.endsWith("\n") ? "\n" : "";
22497
22687
  writeFileSync11(destPath, pre + sep3 + rendered + (rendered.endsWith("\n") ? "" : "\n"), "utf-8");
22498
22688
  }
@@ -22545,8 +22735,8 @@ function dismissRuleCandidate(db, opts) {
22545
22735
  }
22546
22736
  try {
22547
22737
  const dismissedLog = join24(projectRoot, ".massu", "rule-candidates", ".dismissed.jsonl");
22548
- if (!existsSync36(dirname19(dismissedLog))) mkdirSync17(dirname19(dismissedLog), { recursive: true });
22549
- const pre = existsSync36(dismissedLog) ? readFileSync29(dismissedLog, "utf-8") : "";
22738
+ if (!existsSync38(dirname19(dismissedLog))) mkdirSync17(dirname19(dismissedLog), { recursive: true });
22739
+ const pre = existsSync38(dismissedLog) ? readFileSync29(dismissedLog, "utf-8") : "";
22550
22740
  const sep3 = pre && !pre.endsWith("\n") ? "\n" : "";
22551
22741
  writeFileSync11(dismissedLog, pre + sep3 + JSON.stringify({
22552
22742
  id: opts.candidateId,
@@ -22557,7 +22747,7 @@ function dismissRuleCandidate(db, opts) {
22557
22747
  audit_log_id: auditRowId,
22558
22748
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
22559
22749
  }) + "\n", "utf-8");
22560
- if (existsSync36(candidatePath)) unlinkSync5(candidatePath);
22750
+ if (existsSync38(candidatePath)) unlinkSync5(candidatePath);
22561
22751
  } catch (err) {
22562
22752
  appendFailureLog(projectRoot, {
22563
22753
  candidate_id: opts.candidateId,
@@ -22576,9 +22766,9 @@ function dismissRuleCandidate(db, opts) {
22576
22766
  function appendFailureLog(projectRoot, entry) {
22577
22767
  try {
22578
22768
  const dir = join24(projectRoot, ".massu", "rule-candidates");
22579
- if (!existsSync36(dir)) mkdirSync17(dir, { recursive: true });
22769
+ if (!existsSync38(dir)) mkdirSync17(dir, { recursive: true });
22580
22770
  const logPath = join24(dir, ".failures.jsonl");
22581
- const pre = existsSync36(logPath) ? readFileSync29(logPath, "utf-8") : "";
22771
+ const pre = existsSync38(logPath) ? readFileSync29(logPath, "utf-8") : "";
22582
22772
  const sep3 = pre && !pre.endsWith("\n") ? "\n" : "";
22583
22773
  writeFileSync11(logPath, pre + sep3 + JSON.stringify(entry) + "\n", "utf-8");
22584
22774
  } catch {
@@ -22663,7 +22853,7 @@ var init_rule_candidate_applier = __esm({
22663
22853
  });
22664
22854
 
22665
22855
  // src/rule-candidate-preview.ts
22666
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync12, existsSync as existsSync37 } from "node:fs";
22856
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync12, existsSync as existsSync39 } from "node:fs";
22667
22857
  function renderHardenedPreview(destination, body) {
22668
22858
  const rendered = String(body ?? "");
22669
22859
  const riskFindings = RISK_PATTERNS.filter((p19) => p19.re.test(rendered)).map((p19) => p19.label);
@@ -22691,7 +22881,7 @@ function validateReviewAttestation(att) {
22691
22881
  };
22692
22882
  }
22693
22883
  function recordHardenedReviewAttestation(sidecarPath2, attestation) {
22694
- if (!existsSync37(sidecarPath2)) {
22884
+ if (!existsSync39(sidecarPath2)) {
22695
22885
  throw new Error(`candidate sidecar not found: ${sidecarPath2}`);
22696
22886
  }
22697
22887
  const validated = validateReviewAttestation(attestation);
@@ -22803,7 +22993,7 @@ var init_rule_candidate_renderer = __esm({
22803
22993
  });
22804
22994
 
22805
22995
  // src/rule-promotion-effectiveness.ts
22806
- import { readFileSync as readFileSync31, existsSync as existsSync38 } from "fs";
22996
+ import { readFileSync as readFileSync31, existsSync as existsSync40 } from "fs";
22807
22997
  function evaluateCr53Effectiveness(inputs) {
22808
22998
  const { db, failureLogPath, knownLimitations } = inputs;
22809
22999
  const allowlist = new Set(knownLimitations.map((k3) => k3.promptHash));
@@ -22834,7 +23024,7 @@ function evaluateCr53Effectiveness(inputs) {
22834
23024
  });
22835
23025
  }
22836
23026
  const failureLogViolations = [];
22837
- if (existsSync38(failureLogPath)) {
23027
+ if (existsSync40(failureLogPath)) {
22838
23028
  try {
22839
23029
  const raw = readFileSync31(failureLogPath, "utf-8");
22840
23030
  const cutoff = Date.now() - SEVEN_DAYS_MS;
@@ -23013,8 +23203,8 @@ var init_rule_pack_schema = __esm({
23013
23203
  });
23014
23204
 
23015
23205
  // src/rule-pack-sync.ts
23016
- import { existsSync as existsSync39, writeFileSync as writeFileSync13, mkdirSync as mkdirSync18 } from "fs";
23017
- import { homedir as homedir19 } from "os";
23206
+ import { existsSync as existsSync41, writeFileSync as writeFileSync13, mkdirSync as mkdirSync18 } from "fs";
23207
+ import { homedir as homedir20 } from "os";
23018
23208
  import { join as join25, dirname as dirname20 } from "path";
23019
23209
  async function pullInstalledPackRules(db, opts = {}) {
23020
23210
  const config = getConfig();
@@ -23078,11 +23268,11 @@ async function pullInstalledPackRules(db, opts = {}) {
23078
23268
  result.pulled += 1;
23079
23269
  const promptHash = packRulePromptHash(pack.slug, rule);
23080
23270
  const candidatePath = sidecarPath(projectRoot, promptHash);
23081
- if (existsSync39(candidatePath) || alreadyApplied(db, promptHash)) {
23271
+ if (existsSync41(candidatePath) || alreadyApplied(db, promptHash)) {
23082
23272
  result.skipped += 1;
23083
23273
  continue;
23084
23274
  }
23085
- materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, signedOrgId, nowIso, opts.home ?? homedir19());
23275
+ materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, signedOrgId, nowIso, opts.home ?? homedir20());
23086
23276
  result.materialized += 1;
23087
23277
  }
23088
23278
  }
@@ -23171,7 +23361,7 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
23171
23361
  draft_text: draftText
23172
23362
  };
23173
23363
  const dir = dirname20(candidatePath);
23174
- if (!existsSync39(dir)) mkdirSync18(dir, { recursive: true });
23364
+ if (!existsSync41(dir)) mkdirSync18(dir, { recursive: true });
23175
23365
  writeFileSync13(candidatePath, JSON.stringify(sidecar, null, 2), "utf-8");
23176
23366
  }
23177
23367
  function emitDropTelemetry(db, eventType, data) {
@@ -24043,7 +24233,7 @@ var init_memory_supersede = __esm({
24043
24233
 
24044
24234
  // src/memory-consolidate.ts
24045
24235
  import { createHash as createHash12 } from "crypto";
24046
- import { existsSync as existsSync40, mkdirSync as mkdirSync19, writeFileSync as writeFileSync14 } from "fs";
24236
+ import { existsSync as existsSync42, mkdirSync as mkdirSync19, writeFileSync as writeFileSync14 } from "fs";
24047
24237
  import { join as join27 } from "path";
24048
24238
  function emptyResult() {
24049
24239
  return {
@@ -24220,7 +24410,7 @@ ${representative.detail}` : ""}`;
24220
24410
  const clusterKey = cluster.map((c2) => c2.title.toLowerCase().replace(/\s+/g, " ").trim()).sort().join("|");
24221
24411
  const promptHash = createHash12("sha256").update(clusterKey).digest("hex").slice(0, 16);
24222
24412
  const candidatePath = join27(candidateDir, `${promptHash}.json`);
24223
- if (existsSync40(candidatePath)) continue;
24413
+ if (existsSync42(candidatePath)) continue;
24224
24414
  if (dryRun) {
24225
24415
  promoted++;
24226
24416
  continue;
@@ -24513,8 +24703,8 @@ var show_template_exports = {};
24513
24703
  __export(show_template_exports, {
24514
24704
  runShowTemplate: () => runShowTemplate
24515
24705
  });
24516
- import { existsSync as existsSync41, readFileSync as readFileSync32 } from "fs";
24517
- import { resolve as resolve17 } from "path";
24706
+ import { existsSync as existsSync43, readFileSync as readFileSync32 } from "fs";
24707
+ import { resolve as resolve18 } from "path";
24518
24708
  function normalizeBaseName(input) {
24519
24709
  return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
24520
24710
  }
@@ -24542,8 +24732,8 @@ async function runShowTemplate(args2) {
24542
24732
  return;
24543
24733
  }
24544
24734
  const suffix = choice.kind === "hit" ? choice.suffix : "";
24545
- const file = suffix === "" ? resolve17(sourceDir, `${baseName}.md`) : resolve17(sourceDir, `${baseName}${suffix}.md`);
24546
- if (!existsSync41(file)) {
24735
+ const file = suffix === "" ? resolve18(sourceDir, `${baseName}.md`) : resolve18(sourceDir, `${baseName}${suffix}.md`);
24736
+ if (!existsSync43(file)) {
24547
24737
  process.stderr.write(`massu: resolved template "${file}" no longer exists
24548
24738
  `);
24549
24739
  process.exit(1);
@@ -24602,9 +24792,9 @@ var init_passthrough = __esm({
24602
24792
  });
24603
24793
 
24604
24794
  // src/lib/installLock.ts
24605
- import { resolve as resolve18 } from "path";
24795
+ import { resolve as resolve19 } from "path";
24606
24796
  function withInstallLock(projectRoot, fn, opts = {}) {
24607
- const lockPath = resolve18(projectRoot, ".massu", "installAll.lock");
24797
+ const lockPath = resolve19(projectRoot, ".massu", "installAll.lock");
24608
24798
  return withFileLockSync(
24609
24799
  lockPath,
24610
24800
  fn,
@@ -24644,8 +24834,8 @@ __export(config_refresh_exports, {
24644
24834
  mergeRefresh: () => mergeRefresh,
24645
24835
  runConfigRefresh: () => runConfigRefresh
24646
24836
  });
24647
- import { existsSync as existsSync42, readFileSync as readFileSync33, rmSync as rmSync9 } from "fs";
24648
- import { resolve as resolve19 } from "path";
24837
+ import { existsSync as existsSync44, readFileSync as readFileSync33, rmSync as rmSync9 } from "fs";
24838
+ import { resolve as resolve20 } from "path";
24649
24839
  import { parse as parseYaml5 } from "yaml";
24650
24840
  function flatten(obj, prefix3 = "") {
24651
24841
  const out = {};
@@ -24773,10 +24963,10 @@ function renderDiff(diff) {
24773
24963
  }
24774
24964
  async function runConfigRefresh(opts = {}) {
24775
24965
  const cwd = opts.cwd ?? process.cwd();
24776
- const configPath = resolve19(cwd, "massu.config.yaml");
24966
+ const configPath = resolve20(cwd, "massu.config.yaml");
24777
24967
  const log = opts.silent ? () => {
24778
24968
  } : (s) => process.stdout.write(s);
24779
- if (!existsSync42(configPath)) {
24969
+ if (!existsSync44(configPath)) {
24780
24970
  const message = "massu.config.yaml not found. Run: npx massu init";
24781
24971
  if (!opts.silent) process.stderr.write(message + "\n");
24782
24972
  return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
@@ -24856,8 +25046,8 @@ async function runConfigRefresh(opts = {}) {
24856
25046
  `);
24857
25047
  const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
24858
25048
  if (stackResolved) {
24859
- const placeholderPath = resolve19(installResult.claudeDir, "commands", "_massu-needs-stack.md");
24860
- if (existsSync42(placeholderPath)) {
25049
+ const placeholderPath = resolve20(installResult.claudeDir, "commands", "_massu-needs-stack.md");
25050
+ if (existsSync44(placeholderPath)) {
24861
25051
  try {
24862
25052
  rmSync9(placeholderPath, { force: true });
24863
25053
  log("Removed _massu-needs-stack.md (stack now declared).\n");
@@ -24907,7 +25097,7 @@ var init_config_refresh = __esm({
24907
25097
  });
24908
25098
 
24909
25099
  // src/lib/pidLiveness.ts
24910
- import { spawnSync as spawnSync2 } from "child_process";
25100
+ import { spawnSync as spawnSync3 } from "child_process";
24911
25101
  function isPidAlive(pid, opts = {}) {
24912
25102
  if (!Number.isFinite(pid) || pid <= 0) return false;
24913
25103
  const platform = opts.platformOverride ?? process.platform;
@@ -24932,7 +25122,7 @@ function checkPosix(pid, killOverride) {
24932
25122
  }
24933
25123
  function checkWindows(pid) {
24934
25124
  try {
24935
- const res = spawnSync2("tasklist", ["/FI", `PID eq ${pid}`, "/NH"], {
25125
+ const res = spawnSync3("tasklist", ["/FI", `PID eq ${pid}`, "/NH"], {
24936
25126
  encoding: "utf-8",
24937
25127
  windowsHide: true
24938
25128
  });
@@ -24950,9 +25140,9 @@ var init_pidLiveness = __esm({
24950
25140
  });
24951
25141
 
24952
25142
  // src/lib/gitToplevel.ts
24953
- import { spawnSync as spawnSync3 } from "child_process";
25143
+ import { spawnSync as spawnSync4 } from "child_process";
24954
25144
  function gitToplevel(cwd) {
24955
- const res = spawnSync3("git", ["rev-parse", "--show-toplevel"], {
25145
+ const res = spawnSync4("git", ["rev-parse", "--show-toplevel"], {
24956
25146
  cwd,
24957
25147
  encoding: "utf-8"
24958
25148
  });
@@ -24966,14 +25156,14 @@ var init_gitToplevel = __esm({
24966
25156
  });
24967
25157
 
24968
25158
  // src/watch/lockfile-detector.ts
24969
- import { existsSync as existsSync43, statSync as statSync14 } from "fs";
24970
- import { resolve as resolve20 } from "path";
25159
+ import { existsSync as existsSync45, statSync as statSync15 } from "fs";
25160
+ import { resolve as resolve21 } from "path";
24971
25161
  function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
24972
25162
  for (const lf of KNOWN_LOCKFILES) {
24973
- const p19 = resolve20(projectRoot, lf);
24974
- if (!existsSync43(p19)) continue;
25163
+ const p19 = resolve21(projectRoot, lf);
25164
+ if (!existsSync45(p19)) continue;
24975
25165
  try {
24976
- const stat = statSync14(p19);
25166
+ const stat = statSync15(p19);
24977
25167
  const delta = now - stat.mtimeMs;
24978
25168
  if (delta >= 0 && delta < windowMs) return true;
24979
25169
  } catch {
@@ -24984,7 +25174,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
24984
25174
  function gitMidOperation(projectRoot) {
24985
25175
  const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
24986
25176
  for (const s of sentinels) {
24987
- if (existsSync43(resolve20(projectRoot, ".git", s))) return true;
25177
+ if (existsSync45(resolve21(projectRoot, ".git", s))) return true;
24988
25178
  }
24989
25179
  return false;
24990
25180
  }
@@ -25183,17 +25373,17 @@ var init_paths = __esm({
25183
25373
  });
25184
25374
 
25185
25375
  // src/watch/state.ts
25186
- import { closeSync as closeSync5, existsSync as existsSync44, fsyncSync as fsyncSync5, mkdirSync as mkdirSync20, openSync as openSync5, readFileSync as readFileSync34, renameSync as renameSync5, rmSync as rmSync10, writeFileSync as writeFileSync15, writeSync as writeSync3 } from "fs";
25187
- import { dirname as dirname21, resolve as resolve21 } from "path";
25376
+ import { closeSync as closeSync5, existsSync as existsSync46, fsyncSync as fsyncSync5, mkdirSync as mkdirSync20, openSync as openSync5, readFileSync as readFileSync34, renameSync as renameSync5, rmSync as rmSync10, writeFileSync as writeFileSync15, writeSync as writeSync3 } from "fs";
25377
+ import { dirname as dirname21, resolve as resolve22 } from "path";
25188
25378
  function watchStatePath(projectRoot) {
25189
- return resolve21(projectRoot, ".massu", "watch-state.json");
25379
+ return resolve22(projectRoot, ".massu", "watch-state.json");
25190
25380
  }
25191
25381
  function backupStatePath(projectRoot) {
25192
- return resolve21(projectRoot, ".massu", "watch-state.v0.bak.json");
25382
+ return resolve22(projectRoot, ".massu", "watch-state.v0.bak.json");
25193
25383
  }
25194
25384
  function readState(projectRoot) {
25195
25385
  const path = watchStatePath(projectRoot);
25196
- if (!existsSync44(path)) return { ...DEFAULT_STATE };
25386
+ if (!existsSync46(path)) return { ...DEFAULT_STATE };
25197
25387
  const content = readFileSync34(path, "utf-8");
25198
25388
  let raw;
25199
25389
  try {
@@ -25257,7 +25447,7 @@ function writeStateAtomic(projectRoot, state) {
25257
25447
  renameSync5(tmp, path);
25258
25448
  renamed = true;
25259
25449
  } finally {
25260
- if (!renamed && existsSync44(tmp)) {
25450
+ if (!renamed && existsSync46(tmp)) {
25261
25451
  try {
25262
25452
  rmSync10(tmp, { force: true });
25263
25453
  } catch {
@@ -25538,9 +25728,9 @@ __export(watch_exports, {
25538
25728
  runOnQuiescent: () => runOnQuiescent,
25539
25729
  runWatch: () => runWatch
25540
25730
  });
25541
- import { spawnSync as spawnSync4 } from "child_process";
25542
- import { basename as basename8, dirname as dirname22, resolve as resolve22 } from "path";
25543
- import { appendFileSync as appendFileSync4, existsSync as existsSync45, mkdirSync as mkdirSync21, readFileSync as readFileSync35 } from "fs";
25731
+ import { spawnSync as spawnSync5 } from "child_process";
25732
+ import { basename as basename8, dirname as dirname22, resolve as resolve23 } from "path";
25733
+ import { appendFileSync as appendFileSync4, existsSync as existsSync47, mkdirSync as mkdirSync21, readFileSync as readFileSync35 } from "fs";
25544
25734
  function parseFlags(args2) {
25545
25735
  const out = {
25546
25736
  foreground: false,
@@ -25565,9 +25755,9 @@ function parseFlags(args2) {
25565
25755
  }
25566
25756
  function findClaudeBg() {
25567
25757
  const home = process.env.HOME ?? "";
25568
- const fixed = home ? resolve22(home, ".claude", "bin", "claude-bg") : null;
25569
- if (fixed && existsSync45(fixed)) return fixed;
25570
- const which = spawnSync4("which", ["claude-bg"], { encoding: "utf-8" });
25758
+ const fixed = home ? resolve23(home, ".claude", "bin", "claude-bg") : null;
25759
+ if (fixed && existsSync47(fixed)) return fixed;
25760
+ const which = spawnSync5("which", ["claude-bg"], { encoding: "utf-8" });
25571
25761
  if (which.status === 0 && which.stdout) {
25572
25762
  const p19 = which.stdout.trim();
25573
25763
  if (p19) return p19;
@@ -25609,7 +25799,7 @@ async function runWatch(args2) {
25609
25799
  return { exitCode: 1, message: msg };
25610
25800
  }
25611
25801
  const name = watchName(root);
25612
- const res = spawnSync4(claudeBg, [
25802
+ const res = spawnSync5(claudeBg, [
25613
25803
  "start",
25614
25804
  "--name",
25615
25805
  name,
@@ -25627,7 +25817,7 @@ async function runWatch(args2) {
25627
25817
  }
25628
25818
  function runStatus(root) {
25629
25819
  const path = watchStatePath(root);
25630
- if (!existsSync45(path)) {
25820
+ if (!existsSync47(path)) {
25631
25821
  process.stdout.write("massu watch: not running (no state file)\n");
25632
25822
  return { exitCode: 0 };
25633
25823
  }
@@ -25665,7 +25855,7 @@ function runStop(root) {
25665
25855
  process.stderr.write("massu watch --stop: claude-bg not found; cannot kill registered daemon\n");
25666
25856
  return { exitCode: 1 };
25667
25857
  }
25668
- const res = spawnSync4(claudeBg, ["kill", watchName(root)], { stdio: "inherit" });
25858
+ const res = spawnSync5(claudeBg, ["kill", watchName(root)], { stdio: "inherit" });
25669
25859
  return { exitCode: (res.status ?? 1) === 0 ? 0 : 1 };
25670
25860
  }
25671
25861
  async function runApplyNow(root) {
@@ -25709,7 +25899,7 @@ async function runForeground(root) {
25709
25899
  }
25710
25900
  throw err;
25711
25901
  }
25712
- return new Promise((resolve45) => {
25902
+ return new Promise((resolve46) => {
25713
25903
  const shutdown = async () => {
25714
25904
  if (stopped) return;
25715
25905
  stopped = true;
@@ -25719,7 +25909,7 @@ async function runForeground(root) {
25719
25909
  process.chdir(priorCwd);
25720
25910
  } catch {
25721
25911
  }
25722
- resolve45({ exitCode: 0 });
25912
+ resolve46({ exitCode: 0 });
25723
25913
  };
25724
25914
  process.on("SIGINT", () => {
25725
25915
  void shutdown();
@@ -25780,7 +25970,7 @@ async function runOnQuiescent(projectRoot) {
25780
25970
  );
25781
25971
  }
25782
25972
  function refreshLogPath(projectRoot) {
25783
- return resolve22(projectRoot, ".massu", "refresh-log.jsonl");
25973
+ return resolve23(projectRoot, ".massu", "refresh-log.jsonl");
25784
25974
  }
25785
25975
  function appendRefreshLog(projectRoot, event) {
25786
25976
  const path = refreshLogPath(projectRoot);
@@ -25792,7 +25982,7 @@ function appendRefreshLog(projectRoot, event) {
25792
25982
  }
25793
25983
  function readRefreshLog(projectRoot, limit = 10, opts = {}) {
25794
25984
  const path = refreshLogPath(projectRoot);
25795
- if (!existsSync45(path)) return [];
25985
+ if (!existsSync47(path)) return [];
25796
25986
  const warn = opts.warn ?? ((s) => {
25797
25987
  process.stderr.write(s);
25798
25988
  });
@@ -25869,13 +26059,13 @@ var init_refresh_log = __esm({
25869
26059
  import {
25870
26060
  chmodSync as chmodSync9,
25871
26061
  closeSync as closeSync6,
25872
- existsSync as existsSync46,
26062
+ existsSync as existsSync48,
25873
26063
  fsyncSync as fsyncSync6,
25874
26064
  mkdirSync as mkdirSync22,
25875
26065
  openSync as openSync6,
25876
26066
  renameSync as renameSync6,
25877
26067
  rmSync as rmSync11,
25878
- statSync as statSync15,
26068
+ statSync as statSync16,
25879
26069
  writeSync as writeSync4
25880
26070
  } from "node:fs";
25881
26071
  import { dirname as dirname23 } from "node:path";
@@ -25883,7 +26073,7 @@ function atomicWrite(path, content, opts = {}) {
25883
26073
  const tmpPath = `${path}.tmp`;
25884
26074
  const parentDir = dirname23(path);
25885
26075
  try {
25886
- if (!existsSync46(parentDir)) {
26076
+ if (!existsSync48(parentDir)) {
25887
26077
  const mkdirOpts = { recursive: true };
25888
26078
  if (opts.ensureParentDirMode !== void 0) {
25889
26079
  mkdirOpts.mode = opts.ensureParentDirMode;
@@ -25905,7 +26095,7 @@ function atomicWrite(path, content, opts = {}) {
25905
26095
  renameSync6(tmpPath, path);
25906
26096
  return { written: true };
25907
26097
  } catch (err) {
25908
- if (existsSync46(tmpPath)) {
26098
+ if (existsSync48(tmpPath)) {
25909
26099
  try {
25910
26100
  rmSync11(tmpPath, { force: true });
25911
26101
  } catch {
@@ -26254,19 +26444,19 @@ var init_fetcher = __esm({
26254
26444
  });
26255
26445
 
26256
26446
  // src/security/manifest-cache.ts
26257
- import { existsSync as existsSync47, readFileSync as readFileSync36, statSync as statSync16 } from "node:fs";
26258
- import { homedir as homedir20 } from "node:os";
26259
- import { resolve as resolve23 } from "node:path";
26447
+ import { existsSync as existsSync49, readFileSync as readFileSync36, statSync as statSync17 } from "node:fs";
26448
+ import { homedir as homedir21 } from "node:os";
26449
+ import { resolve as resolve24 } from "node:path";
26260
26450
  import { z as z5 } from "zod";
26261
26451
  function defaultCachePaths() {
26262
- const dir = resolve23(homedir20(), ".massu");
26452
+ const dir = resolve24(homedir21(), ".massu");
26263
26453
  return {
26264
- cachePath: resolve23(dir, "adapter-manifest.json"),
26265
- lockPath: resolve23(dir, ".adapter-manifest.lock")
26454
+ cachePath: resolve24(dir, "adapter-manifest.json"),
26455
+ lockPath: resolve24(dir, ".adapter-manifest.lock")
26266
26456
  };
26267
26457
  }
26268
26458
  function loadCachedManifest(paths = defaultCachePaths()) {
26269
- if (!existsSync47(paths.cachePath)) {
26459
+ if (!existsSync49(paths.cachePath)) {
26270
26460
  return { kind: "absent" };
26271
26461
  }
26272
26462
  let raw;
@@ -26460,15 +26650,15 @@ var init_adapter_origin = __esm({
26460
26650
  });
26461
26651
 
26462
26652
  // src/security/local-fingerprint.ts
26463
- import { existsSync as existsSync48, readFileSync as readFileSync37, lstatSync as lstatSync5 } from "node:fs";
26464
- import { homedir as homedir21 } from "node:os";
26465
- import { resolve as resolve24, isAbsolute as isAbsolute3 } from "node:path";
26653
+ import { existsSync as existsSync50, readFileSync as readFileSync37, lstatSync as lstatSync5 } from "node:fs";
26654
+ import { homedir as homedir22 } from "node:os";
26655
+ import { resolve as resolve25, isAbsolute as isAbsolute4 } from "node:path";
26466
26656
  import { createHash as createHash14 } from "node:crypto";
26467
26657
  import { z as z6 } from "zod";
26468
26658
  function computeLocalFingerprint(localPaths, projectRoot) {
26469
26659
  const tuples = [];
26470
26660
  for (const p19 of localPaths) {
26471
- const abs = isAbsolute3(p19) ? p19 : resolve24(projectRoot, p19);
26661
+ const abs = isAbsolute4(p19) ? p19 : resolve25(projectRoot, p19);
26472
26662
  let contentTag;
26473
26663
  try {
26474
26664
  const lst = lstatSync5(abs);
@@ -26489,7 +26679,7 @@ function computeLocalFingerprint(localPaths, projectRoot) {
26489
26679
  return createHash14("sha256").update(canonical2).digest("hex");
26490
26680
  }
26491
26681
  function readFingerprintSentinel(path = FINGERPRINT_PATH) {
26492
- if (!existsSync48(path)) return null;
26682
+ if (!existsSync50(path)) return null;
26493
26683
  let raw;
26494
26684
  try {
26495
26685
  raw = JSON.parse(readFileSync37(path, "utf-8"));
@@ -26540,7 +26730,7 @@ var init_local_fingerprint = __esm({
26540
26730
  "use strict";
26541
26731
  init_atomic_write();
26542
26732
  init_manifest_schema();
26543
- FINGERPRINT_PATH = resolve24(homedir21(), ".massu", "adapters-local-fingerprint.json");
26733
+ FINGERPRINT_PATH = resolve25(homedir22(), ".massu", "adapters-local-fingerprint.json");
26544
26734
  FingerprintSentinelSchema = z6.object({
26545
26735
  fingerprint: z6.string().regex(/^[0-9a-f]{64}$/),
26546
26736
  source: z6.enum(["cli", "cli-resync"]),
@@ -26556,15 +26746,15 @@ var init_local_fingerprint = __esm({
26556
26746
  });
26557
26747
 
26558
26748
  // src/security/install-tracking.ts
26559
- import { readFileSync as readFileSync38, readdirSync as readdirSync16, lstatSync as lstatSync6, existsSync as existsSync49 } from "node:fs";
26749
+ import { readFileSync as readFileSync38, readdirSync as readdirSync17, lstatSync as lstatSync6, existsSync as existsSync51 } from "node:fs";
26560
26750
  import { join as join28, relative as relative7, sep as sep2 } from "node:path";
26561
- import { homedir as homedir22 } from "node:os";
26562
- import { resolve as resolve25 } from "node:path";
26751
+ import { homedir as homedir23 } from "node:os";
26752
+ import { resolve as resolve26 } from "node:path";
26563
26753
  import { createHash as createHash15 } from "node:crypto";
26564
26754
  import { z as z7 } from "zod";
26565
26755
  function containsHiddenDirs(packageDir) {
26566
26756
  for (const hidden of EXCLUDED_DIR_NAMES) {
26567
- if (existsSync49(`${packageDir}/${hidden}`)) {
26757
+ if (existsSync51(`${packageDir}/${hidden}`)) {
26568
26758
  return hidden;
26569
26759
  }
26570
26760
  }
@@ -26576,7 +26766,7 @@ function sha256OfDir(dir, opts = {}) {
26576
26766
  function walk(currentDir) {
26577
26767
  let entries;
26578
26768
  try {
26579
- entries = readdirSync16(currentDir);
26769
+ entries = readdirSync17(currentDir);
26580
26770
  } catch {
26581
26771
  return;
26582
26772
  }
@@ -26619,7 +26809,7 @@ function sha256OfDir(dir, opts = {}) {
26619
26809
  return top.digest("hex");
26620
26810
  }
26621
26811
  function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
26622
- if (!existsSync49(path)) return {};
26812
+ if (!existsSync51(path)) return {};
26623
26813
  let raw;
26624
26814
  try {
26625
26815
  raw = JSON.parse(readFileSync38(path, "utf-8"));
@@ -26686,7 +26876,7 @@ var init_install_tracking = __esm({
26686
26876
  "src/security/install-tracking.ts"() {
26687
26877
  "use strict";
26688
26878
  init_atomic_write();
26689
- INSTALLED_MANIFEST_PATH = resolve25(homedir22(), ".massu", "adapter-manifest-installed.json");
26879
+ INSTALLED_MANIFEST_PATH = resolve26(homedir23(), ".massu", "adapter-manifest-installed.json");
26690
26880
  DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
26691
26881
  EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
26692
26882
  InstallEntrySchema = z7.object({
@@ -26709,25 +26899,25 @@ var init_install_tracking = __esm({
26709
26899
  });
26710
26900
 
26711
26901
  // src/detect/adapters/discover.ts
26712
- import { existsSync as existsSync50, readdirSync as readdirSync17, readFileSync as readFileSync39, lstatSync as lstatSync7 } from "node:fs";
26713
- import { resolve as resolve26, isAbsolute as isAbsolute4 } from "node:path";
26902
+ import { existsSync as existsSync52, readdirSync as readdirSync18, readFileSync as readFileSync39, lstatSync as lstatSync7 } from "node:fs";
26903
+ import { resolve as resolve27, isAbsolute as isAbsolute5 } from "node:path";
26714
26904
  import { z as z8 } from "zod";
26715
26905
  function walkNodeModules(projectRoot, warnings) {
26716
- const nodeModulesDir = resolve26(projectRoot, "node_modules");
26717
- if (!existsSync50(nodeModulesDir)) {
26906
+ const nodeModulesDir = resolve27(projectRoot, "node_modules");
26907
+ if (!existsSync52(nodeModulesDir)) {
26718
26908
  return [];
26719
26909
  }
26720
26910
  const candidates = [];
26721
26911
  let topLevelEntries;
26722
26912
  try {
26723
- topLevelEntries = readdirSync17(nodeModulesDir);
26913
+ topLevelEntries = readdirSync18(nodeModulesDir);
26724
26914
  } catch (err) {
26725
26915
  warnings.push(`failed to read node_modules: ${err instanceof Error ? err.message : String(err)}`);
26726
26916
  return [];
26727
26917
  }
26728
26918
  for (const entry of topLevelEntries) {
26729
26919
  if (entry.startsWith(".")) continue;
26730
- const entryPath = resolve26(nodeModulesDir, entry);
26920
+ const entryPath = resolve27(nodeModulesDir, entry);
26731
26921
  let entryStat;
26732
26922
  try {
26733
26923
  entryStat = lstatSync7(entryPath);
@@ -26744,12 +26934,12 @@ function walkNodeModules(projectRoot, warnings) {
26744
26934
  if (entry.startsWith("@")) {
26745
26935
  let scopedEntries;
26746
26936
  try {
26747
- scopedEntries = readdirSync17(entryPath);
26937
+ scopedEntries = readdirSync18(entryPath);
26748
26938
  } catch {
26749
26939
  continue;
26750
26940
  }
26751
26941
  for (const sub of scopedEntries) {
26752
- const subPath = resolve26(entryPath, sub);
26942
+ const subPath = resolve27(entryPath, sub);
26753
26943
  let subStat;
26754
26944
  try {
26755
26945
  subStat = lstatSync7(subPath);
@@ -26768,8 +26958,8 @@ function walkNodeModules(projectRoot, warnings) {
26768
26958
  return candidates;
26769
26959
  }
26770
26960
  function tryReadAdapterPackage(packageDir, warnings) {
26771
- const pkgJsonPath = resolve26(packageDir, "package.json");
26772
- if (!existsSync50(pkgJsonPath)) return null;
26961
+ const pkgJsonPath = resolve27(packageDir, "package.json");
26962
+ if (!existsSync52(pkgJsonPath)) return null;
26773
26963
  let raw;
26774
26964
  try {
26775
26965
  raw = JSON.parse(readFileSync39(pkgJsonPath, "utf-8"));
@@ -26906,8 +27096,8 @@ function discoverAdapters(opts) {
26906
27096
  const localSet = new Set(opts.configLocalPaths);
26907
27097
  for (const localPath of opts.configLocalPaths) {
26908
27098
  if (seenIds.has(localPath)) continue;
26909
- const absPath = isAbsolute4(localPath) ? localPath : resolve26(opts.projectRoot, localPath);
26910
- if (!existsSync50(absPath)) {
27099
+ const absPath = isAbsolute5(localPath) ? localPath : resolve27(opts.projectRoot, localPath);
27100
+ if (!existsSync52(absPath)) {
26911
27101
  warnings.push(
26912
27102
  `local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
26913
27103
  );
@@ -26989,8 +27179,8 @@ __export(adapters_exports, {
26989
27179
  runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
26990
27180
  runAdaptersSearch: () => runAdaptersSearch
26991
27181
  });
26992
- import { existsSync as existsSync51, readFileSync as readFileSync40 } from "node:fs";
26993
- import { resolve as resolve27 } from "node:path";
27182
+ import { existsSync as existsSync53, readFileSync as readFileSync40 } from "node:fs";
27183
+ import { resolve as resolve28 } from "node:path";
26994
27184
  import { parseDocument } from "yaml";
26995
27185
  async function handleAdaptersSubcommand(args2) {
26996
27186
  const sub = args2[0];
@@ -27257,8 +27447,8 @@ function mutateLocalArray(mutator, command) {
27257
27447
  );
27258
27448
  return { exitCode: 2 };
27259
27449
  }
27260
- const yamlPath = resolve27(projectRoot, "massu.config.yaml");
27261
- if (!existsSync51(yamlPath)) {
27450
+ const yamlPath = resolve28(projectRoot, "massu.config.yaml");
27451
+ if (!existsSync53(yamlPath)) {
27262
27452
  process.stderr.write(
27263
27453
  `${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
27264
27454
  `
@@ -27288,7 +27478,7 @@ function mutateLocalArray(mutator, command) {
27288
27478
  if (next === null) {
27289
27479
  return { exitCode: 0 };
27290
27480
  }
27291
- const lockPath = resolve27(projectRoot, ".massu", "adapters-local-mutate.lock");
27481
+ const lockPath = resolve28(projectRoot, ".massu", "adapters-local-mutate.lock");
27292
27482
  return withFileLockSync(lockPath, () => {
27293
27483
  doc.setIn(["adapters", "local"], next);
27294
27484
  const newYaml = doc.toString();
@@ -27357,16 +27547,16 @@ async function runAdaptersInstall(args2) {
27357
27547
  `);
27358
27548
  return { exitCode: 1 };
27359
27549
  }
27360
- const packageDir = resolve27(projectRoot, "node_modules", ...packageName.split("/"));
27361
- if (!existsSync51(packageDir)) {
27550
+ const packageDir = resolve28(projectRoot, "node_modules", ...packageName.split("/"));
27551
+ if (!existsSync53(packageDir)) {
27362
27552
  process.stderr.write(
27363
27553
  `install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
27364
27554
  `
27365
27555
  );
27366
27556
  return { exitCode: 1 };
27367
27557
  }
27368
- const pkgJsonPath = resolve27(packageDir, "package.json");
27369
- if (!existsSync51(pkgJsonPath)) {
27558
+ const pkgJsonPath = resolve28(packageDir, "package.json");
27559
+ if (!existsSync53(pkgJsonPath)) {
27370
27560
  process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
27371
27561
  `);
27372
27562
  return { exitCode: 1 };
@@ -27492,8 +27682,8 @@ async function runAdaptersResign(_args) {
27492
27682
  removeInstalledManifestEntry(name);
27493
27683
  continue;
27494
27684
  }
27495
- const packageDir = resolve27(projectRoot, "node_modules", ...name.split("/"));
27496
- if (!existsSync51(packageDir)) {
27685
+ const packageDir = resolve28(projectRoot, "node_modules", ...name.split("/"));
27686
+ if (!existsSync53(packageDir)) {
27497
27687
  removed++;
27498
27688
  warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
27499
27689
  removeInstalledManifestEntry(name);
@@ -27578,87 +27768,6 @@ var init_adapters2 = __esm({
27578
27768
  }
27579
27769
  });
27580
27770
 
27581
- // src/preflight.ts
27582
- import { existsSync as existsSync52 } from "fs";
27583
- function assertCodegraphUsable(dbPath) {
27584
- const path = dbPath ?? getResolvedPaths().codegraphDbPath;
27585
- if (!existsSync52(path)) {
27586
- throw new CodegraphDbUnusableError(path, "missing");
27587
- }
27588
- let db = null;
27589
- try {
27590
- db = openDatabase2(path, { readonly: true });
27591
- const row = db.prepare("SELECT COUNT(*) AS c FROM files").get();
27592
- const files = row?.c ?? 0;
27593
- if (files === 0) {
27594
- throw new CodegraphDbUnusableError(path, "empty");
27595
- }
27596
- } catch (err) {
27597
- if (err instanceof CodegraphDbUnusableError) throw err;
27598
- throw new CodegraphDbUnusableError(
27599
- path,
27600
- "unreadable",
27601
- err instanceof Error ? err.message : String(err)
27602
- );
27603
- } finally {
27604
- try {
27605
- db?.close();
27606
- } catch {
27607
- }
27608
- }
27609
- }
27610
- var CodegraphDbNotInitializedError, CodegraphDbUnusableError;
27611
- var init_preflight = __esm({
27612
- "src/preflight.ts"() {
27613
- "use strict";
27614
- init_db_driver();
27615
- init_config();
27616
- CodegraphDbNotInitializedError = class extends Error {
27617
- dbPath;
27618
- constructor(dbPath) {
27619
- super(`CodeGraph database not found at ${dbPath}`);
27620
- this.name = "CodegraphDbNotInitializedError";
27621
- this.dbPath = dbPath;
27622
- }
27623
- };
27624
- CodegraphDbUnusableError = class extends CodegraphDbNotInitializedError {
27625
- reason;
27626
- constructor(dbPath, reason, detail) {
27627
- super(dbPath);
27628
- this.message = reason === "missing" ? `CodeGraph database not found at ${dbPath}` : reason === "empty" ? `CodeGraph database at ${dbPath} exists but is EMPTY (0 files indexed)` : `CodeGraph database at ${dbPath} could not be read: ${detail ?? "unknown"}`;
27629
- this.name = "CodegraphDbUnusableError";
27630
- this.reason = reason;
27631
- }
27632
- /**
27633
- * The remedy, per reason. VERIFIED BY EXECUTION — `init` is a no-op on an
27634
- * already-initialized repo and will NOT populate the graph. `index` is the command
27635
- * that does. Getting this wrong sends the customer in a circle.
27636
- */
27637
- get remedy() {
27638
- switch (this.reason) {
27639
- case "missing":
27640
- return "npx @colbymchenry/codegraph init && npx @colbymchenry/codegraph index";
27641
- case "empty":
27642
- return "npx @colbymchenry/codegraph index";
27643
- case "unreadable":
27644
- return "npx @colbymchenry/codegraph index (the database may be corrupt \u2014 re-indexing rebuilds it)";
27645
- }
27646
- }
27647
- /** Plain-English explanation for a human. */
27648
- get humanExplanation() {
27649
- switch (this.reason) {
27650
- case "missing":
27651
- return "Massu needs a map of your code to answer questions about it. That map has not been built yet.";
27652
- case "empty":
27653
- return 'The code map exists but is EMPTY \u2014 nothing was ever indexed into it. Massu would answer "no impact" to every question, which is indistinguishable from a real answer. It is refusing rather than misleading you.';
27654
- case "unreadable":
27655
- return "The code map exists but cannot be read. It may be corrupt.";
27656
- }
27657
- }
27658
- };
27659
- }
27660
- });
27661
-
27662
27771
  // src/lib/sql-table-names.ts
27663
27772
  function t(suffix) {
27664
27773
  return `${getConfig().toolPrefix}_${suffix}`;
@@ -27672,7 +27781,7 @@ var init_sql_table_names = __esm({
27672
27781
 
27673
27782
  // src/db.ts
27674
27783
  import { dirname as dirname24, join as join29 } from "path";
27675
- import { existsSync as existsSync53, mkdirSync as mkdirSync23, readdirSync as readdirSync18, statSync as statSync17 } from "fs";
27784
+ import { existsSync as existsSync54, mkdirSync as mkdirSync23, readdirSync as readdirSync19, statSync as statSync18 } from "fs";
27676
27785
  function getCodeGraphDb() {
27677
27786
  const dbPath = getResolvedPaths().codegraphDbPath;
27678
27787
  assertCodegraphUsable(dbPath);
@@ -27683,7 +27792,7 @@ function getCodeGraphDb() {
27683
27792
  function getDataDb() {
27684
27793
  const dbPath = getResolvedPaths().dataDbPath;
27685
27794
  const dir = dirname24(dbPath);
27686
- if (!existsSync53(dir)) {
27795
+ if (!existsSync54(dir)) {
27687
27796
  mkdirSync23(dir, { recursive: true });
27688
27797
  }
27689
27798
  const db = openDatabase2(dbPath);
@@ -27954,14 +28063,14 @@ function isPythonDataStale(dataDb, pythonRoot) {
27954
28063
  const lastBuildTime = new Date(lastBuild.value).getTime();
27955
28064
  function checkDir(dir) {
27956
28065
  try {
27957
- const entries = readdirSync18(dir, { withFileTypes: true });
28066
+ const entries = readdirSync19(dir, { withFileTypes: true });
27958
28067
  for (const entry of entries) {
27959
28068
  const fullPath = join29(dir, entry.name);
27960
28069
  if (entry.isDirectory()) {
27961
28070
  if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
27962
28071
  if (checkDir(fullPath)) return true;
27963
28072
  } else if (entry.name.endsWith(".py")) {
27964
- if (statSync17(fullPath).mtimeMs > lastBuildTime) return true;
28073
+ if (statSync18(fullPath).mtimeMs > lastBuildTime) return true;
27965
28074
  }
27966
28075
  }
27967
28076
  } catch {
@@ -27985,10 +28094,10 @@ var init_db2 = __esm({
27985
28094
  });
27986
28095
 
27987
28096
  // src/security-utils.ts
27988
- import { resolve as resolve28, normalize } from "path";
28097
+ import { resolve as resolve29, normalize } from "path";
27989
28098
  function ensureWithinRoot(filePath, projectRoot) {
27990
- const resolvedRoot = resolve28(projectRoot);
27991
- const resolvedPath = resolve28(resolvedRoot, filePath);
28099
+ const resolvedRoot = resolve29(projectRoot);
28100
+ const resolvedPath = resolve29(resolvedRoot, filePath);
27992
28101
  const normalizedPath = normalize(resolvedPath);
27993
28102
  const normalizedRoot = normalize(resolvedRoot);
27994
28103
  if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
@@ -28061,8 +28170,8 @@ var init_rules = __esm({
28061
28170
  });
28062
28171
 
28063
28172
  // src/import-resolver.ts
28064
- import { readFileSync as readFileSync41, existsSync as existsSync54, statSync as statSync18 } from "fs";
28065
- import { resolve as resolve29, dirname as dirname25, join as join30 } from "path";
28173
+ import { readFileSync as readFileSync41, existsSync as existsSync55, statSync as statSync19 } from "fs";
28174
+ import { resolve as resolve30, dirname as dirname25, join as join30 } from "path";
28066
28175
  function parseImports(source) {
28067
28176
  const imports = [];
28068
28177
  const lines = source.split("\n");
@@ -28118,23 +28227,23 @@ function resolveImportPath(specifier, fromFile) {
28118
28227
  let basePath;
28119
28228
  if (specifier.startsWith("@/")) {
28120
28229
  const paths = getResolvedPaths();
28121
- basePath = resolve29(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
28230
+ basePath = resolve30(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
28122
28231
  } else {
28123
- basePath = resolve29(dirname25(fromFile), specifier);
28232
+ basePath = resolve30(dirname25(fromFile), specifier);
28124
28233
  }
28125
- if (existsSync54(basePath) && !isDirectory(basePath)) {
28234
+ if (existsSync55(basePath) && !isDirectory(basePath)) {
28126
28235
  return toRelative(basePath);
28127
28236
  }
28128
28237
  const resolvedPaths = getResolvedPaths();
28129
28238
  for (const ext of resolvedPaths.extensions) {
28130
28239
  const withExt = basePath + ext;
28131
- if (existsSync54(withExt)) {
28240
+ if (existsSync55(withExt)) {
28132
28241
  return toRelative(withExt);
28133
28242
  }
28134
28243
  }
28135
28244
  for (const indexFile of resolvedPaths.indexFiles) {
28136
28245
  const indexPath = join30(basePath, indexFile);
28137
- if (existsSync54(indexPath)) {
28246
+ if (existsSync55(indexPath)) {
28138
28247
  return toRelative(indexPath);
28139
28248
  }
28140
28249
  }
@@ -28142,7 +28251,7 @@ function resolveImportPath(specifier, fromFile) {
28142
28251
  }
28143
28252
  function isDirectory(path) {
28144
28253
  try {
28145
- return statSync18(path).isDirectory();
28254
+ return statSync19(path).isDirectory();
28146
28255
  } catch {
28147
28256
  return false;
28148
28257
  }
@@ -28170,8 +28279,8 @@ function buildImportIndex(dataDb, codegraphDb) {
28170
28279
  const batchSize = 500;
28171
28280
  let batch = [];
28172
28281
  for (const file of files) {
28173
- const absPath = ensureWithinRoot(resolve29(projectRoot, file.path), projectRoot);
28174
- if (!existsSync54(absPath)) continue;
28282
+ const absPath = ensureWithinRoot(resolve30(projectRoot, file.path), projectRoot);
28283
+ if (!existsSync55(absPath)) continue;
28175
28284
  let source;
28176
28285
  try {
28177
28286
  source = readFileSync41(absPath, "utf-8");
@@ -28211,12 +28320,12 @@ var init_import_resolver = __esm({
28211
28320
  });
28212
28321
 
28213
28322
  // src/trpc-index.ts
28214
- import { readFileSync as readFileSync42, existsSync as existsSync55, readdirSync as readdirSync19 } from "fs";
28215
- import { resolve as resolve30, join as join31 } from "path";
28323
+ import { readFileSync as readFileSync42, existsSync as existsSync56, readdirSync as readdirSync20 } from "fs";
28324
+ import { resolve as resolve31, join as join31 } from "path";
28216
28325
  function parseRootRouter() {
28217
28326
  const paths = getResolvedPaths();
28218
28327
  const rootPath = paths.rootRouterPath;
28219
- if (!existsSync55(rootPath)) {
28328
+ if (!existsSync56(rootPath)) {
28220
28329
  throw new Error(`Root router not found at ${rootPath}`);
28221
28330
  }
28222
28331
  const source = readFileSync42(rootPath, "utf-8");
@@ -28227,16 +28336,16 @@ function parseRootRouter() {
28227
28336
  while ((match = importRegex.exec(source)) !== null) {
28228
28337
  const variable = match[1];
28229
28338
  let filePath = match[2];
28230
- const fullPath = resolve30(paths.routersDir, filePath);
28339
+ const fullPath = resolve31(paths.routersDir, filePath);
28231
28340
  for (const ext of [".ts", ".tsx", ""]) {
28232
28341
  const candidate = fullPath + ext;
28233
28342
  const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
28234
- if (existsSync55(candidate)) {
28343
+ if (existsSync56(candidate)) {
28235
28344
  filePath = routersRelPath + "/" + filePath + ext;
28236
28345
  break;
28237
28346
  }
28238
28347
  const indexCandidate = join31(fullPath, "index.ts");
28239
- if (existsSync55(indexCandidate)) {
28348
+ if (existsSync56(indexCandidate)) {
28240
28349
  filePath = routersRelPath + "/" + filePath + "/index.ts";
28241
28350
  break;
28242
28351
  }
@@ -28255,8 +28364,8 @@ function parseRootRouter() {
28255
28364
  return mappings;
28256
28365
  }
28257
28366
  function extractProcedures(routerFilePath) {
28258
- const absPath = resolve30(getProjectRoot(), routerFilePath);
28259
- if (!existsSync55(absPath)) return [];
28367
+ const absPath = resolve31(getProjectRoot(), routerFilePath);
28368
+ if (!existsSync56(absPath)) return [];
28260
28369
  const source = readFileSync42(absPath, "utf-8");
28261
28370
  const procedures = [];
28262
28371
  const seen = /* @__PURE__ */ new Set();
@@ -28280,19 +28389,19 @@ function findUICallSites(routerKey, procedureName) {
28280
28389
  const root = getProjectRoot();
28281
28390
  const src = config.paths.source;
28282
28391
  const searchDirs = [
28283
- resolve30(root, config.paths.pages ?? src + "/app"),
28284
- resolve30(root, config.paths.components ?? src + "/components"),
28285
- resolve30(root, config.paths.hooks ?? src + "/hooks")
28392
+ resolve31(root, config.paths.pages ?? src + "/app"),
28393
+ resolve31(root, config.paths.components ?? src + "/components"),
28394
+ resolve31(root, config.paths.hooks ?? src + "/hooks")
28286
28395
  ];
28287
28396
  const searchPattern = `api.${routerKey}.${procedureName}`;
28288
28397
  for (const dir of searchDirs) {
28289
- if (!existsSync55(dir)) continue;
28398
+ if (!existsSync56(dir)) continue;
28290
28399
  searchDirectory(dir, searchPattern, callSites);
28291
28400
  }
28292
28401
  return callSites;
28293
28402
  }
28294
28403
  function searchDirectory(dir, pattern, results) {
28295
- const entries = readdirSync19(dir, { withFileTypes: true });
28404
+ const entries = readdirSync20(dir, { withFileTypes: true });
28296
28405
  for (const entry of entries) {
28297
28406
  const fullPath = join31(dir, entry.name);
28298
28407
  if (entry.isDirectory()) {
@@ -28364,8 +28473,8 @@ var init_trpc_index = __esm({
28364
28473
  });
28365
28474
 
28366
28475
  // src/page-deps.ts
28367
- import { readFileSync as readFileSync43, existsSync as existsSync56 } from "fs";
28368
- import { resolve as resolve31 } from "path";
28476
+ import { readFileSync as readFileSync43, existsSync as existsSync57 } from "fs";
28477
+ import { resolve as resolve32 } from "path";
28369
28478
  function deriveRoute(pageFile) {
28370
28479
  let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
28371
28480
  return route || "/";
@@ -28403,8 +28512,8 @@ function findRouterCalls(files) {
28403
28512
  const routers = /* @__PURE__ */ new Set();
28404
28513
  const projectRoot = getProjectRoot();
28405
28514
  for (const file of files) {
28406
- const absPath = ensureWithinRoot(resolve31(projectRoot, file), projectRoot);
28407
- if (!existsSync56(absPath)) continue;
28515
+ const absPath = ensureWithinRoot(resolve32(projectRoot, file), projectRoot);
28516
+ if (!existsSync57(absPath)) continue;
28408
28517
  try {
28409
28518
  const source = readFileSync43(absPath, "utf-8");
28410
28519
  const apiCallRegex = /api\.(\w+)\.\w+/g;
@@ -28424,8 +28533,8 @@ function findTablesFromRouters(routerNames, dataDb) {
28424
28533
  `SELECT DISTINCT router_file FROM ${t("trpc_procedures")} WHERE router_name = ? LIMIT 10000`
28425
28534
  ).all(routerName);
28426
28535
  for (const proc of procs) {
28427
- const absPath = ensureWithinRoot(resolve31(getProjectRoot(), proc.router_file), getProjectRoot());
28428
- if (!existsSync56(absPath)) continue;
28536
+ const absPath = ensureWithinRoot(resolve32(getProjectRoot(), proc.router_file), getProjectRoot());
28537
+ if (!existsSync57(absPath)) continue;
28429
28538
  try {
28430
28539
  const source = readFileSync43(absPath, "utf-8");
28431
28540
  const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
@@ -28699,11 +28808,11 @@ var init_domains = __esm({
28699
28808
  });
28700
28809
 
28701
28810
  // src/schema-mapper.ts
28702
- import { readFileSync as readFileSync44, existsSync as existsSync57, readdirSync as readdirSync20 } from "fs";
28811
+ import { readFileSync as readFileSync44, existsSync as existsSync58, readdirSync as readdirSync21 } from "fs";
28703
28812
  import { join as join32 } from "path";
28704
28813
  function parsePrismaSchema() {
28705
28814
  const schemaPath = getResolvedPaths().prismaSchemaPath;
28706
- if (!existsSync57(schemaPath)) {
28815
+ if (!existsSync58(schemaPath)) {
28707
28816
  throw new Error(`Prisma schema not found at ${schemaPath}`);
28708
28817
  }
28709
28818
  const source = readFileSync44(schemaPath, "utf-8");
@@ -28764,12 +28873,12 @@ function toSnakeCase(str) {
28764
28873
  function findColumnUsageInRouters(tableName) {
28765
28874
  const usage2 = /* @__PURE__ */ new Map();
28766
28875
  const routersDir = getResolvedPaths().routersDir;
28767
- if (!existsSync57(routersDir)) return usage2;
28876
+ if (!existsSync58(routersDir)) return usage2;
28768
28877
  scanDirectory(routersDir, tableName, usage2);
28769
28878
  return usage2;
28770
28879
  }
28771
28880
  function scanDirectory(dir, tableName, usage2) {
28772
- const entries = readdirSync20(dir, { withFileTypes: true });
28881
+ const entries = readdirSync21(dir, { withFileTypes: true });
28773
28882
  for (const entry of entries) {
28774
28883
  const fullPath = join32(dir, entry.name);
28775
28884
  if (entry.isDirectory()) {
@@ -28826,8 +28935,8 @@ function detectMismatches(models) {
28826
28935
  }
28827
28936
  function findFilesUsingColumn(dir, column, tableName) {
28828
28937
  const result = [];
28829
- if (!existsSync57(dir)) return result;
28830
- const entries = readdirSync20(dir, { withFileTypes: true });
28938
+ if (!existsSync58(dir)) return result;
28939
+ const entries = readdirSync21(dir, { withFileTypes: true });
28831
28940
  for (const entry of entries) {
28832
28941
  const fullPath = join32(dir, entry.name);
28833
28942
  if (entry.isDirectory()) {
@@ -28979,12 +29088,12 @@ var init_import_parser = __esm({
28979
29088
  });
28980
29089
 
28981
29090
  // src/python/import-resolver.ts
28982
- import { readFileSync as readFileSync45, existsSync as existsSync58, readdirSync as readdirSync21 } from "fs";
28983
- import { resolve as resolve33, join as join33, relative as relative8, dirname as dirname26 } from "path";
29091
+ import { readFileSync as readFileSync45, existsSync as existsSync59, readdirSync as readdirSync22 } from "fs";
29092
+ import { resolve as resolve34, join as join33, relative as relative8, dirname as dirname26 } from "path";
28984
29093
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
28985
29094
  const projectRoot = getProjectRoot();
28986
29095
  if (level > 0) {
28987
- let baseDir = dirname26(resolve33(projectRoot, fromFile));
29096
+ let baseDir = dirname26(resolve34(projectRoot, fromFile));
28988
29097
  for (let i = 1; i < level; i++) {
28989
29098
  baseDir = dirname26(baseDir);
28990
29099
  }
@@ -28996,17 +29105,17 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
28996
29105
  return tryResolvePythonPath(baseDir, projectRoot);
28997
29106
  }
28998
29107
  const parts = module.split(".");
28999
- const candidate = join33(resolve33(projectRoot, pythonRoot), ...parts);
29108
+ const candidate = join33(resolve34(projectRoot, pythonRoot), ...parts);
29000
29109
  return tryResolvePythonPath(candidate, projectRoot);
29001
29110
  }
29002
29111
  function tryResolvePythonPath(basePath, projectRoot) {
29003
- if (existsSync58(basePath + ".py")) {
29112
+ if (existsSync59(basePath + ".py")) {
29004
29113
  return relative8(projectRoot, basePath + ".py");
29005
29114
  }
29006
- if (existsSync58(join33(basePath, "__init__.py"))) {
29115
+ if (existsSync59(join33(basePath, "__init__.py"))) {
29007
29116
  return relative8(projectRoot, join33(basePath, "__init__.py"));
29008
29117
  }
29009
- if (basePath.endsWith(".py") && existsSync58(basePath)) {
29118
+ if (basePath.endsWith(".py") && existsSync59(basePath)) {
29010
29119
  return relative8(projectRoot, basePath);
29011
29120
  }
29012
29121
  return null;
@@ -29014,7 +29123,7 @@ function tryResolvePythonPath(basePath, projectRoot) {
29014
29123
  function walkPythonFiles(dir, excludeDirs) {
29015
29124
  const files = [];
29016
29125
  try {
29017
- const entries = readdirSync21(dir, { withFileTypes: true });
29126
+ const entries = readdirSync22(dir, { withFileTypes: true });
29018
29127
  for (const entry of entries) {
29019
29128
  if (entry.isDirectory()) {
29020
29129
  if (excludeDirs.includes(entry.name)) continue;
@@ -29029,7 +29138,7 @@ function walkPythonFiles(dir, excludeDirs) {
29029
29138
  }
29030
29139
  function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
29031
29140
  const projectRoot = getProjectRoot();
29032
- const absRoot = resolve33(projectRoot, pythonRoot);
29141
+ const absRoot = resolve34(projectRoot, pythonRoot);
29033
29142
  dataDb.exec(`DELETE FROM ${t("py_imports")}`);
29034
29143
  const insertStmt = dataDb.prepare(
29035
29144
  `INSERT INTO ${t("py_imports")} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
@@ -29313,12 +29422,12 @@ var init_route_parser = __esm({
29313
29422
  });
29314
29423
 
29315
29424
  // src/python/route-indexer.ts
29316
- import { readFileSync as readFileSync46, readdirSync as readdirSync22 } from "fs";
29425
+ import { readFileSync as readFileSync46, readdirSync as readdirSync23 } from "fs";
29317
29426
  import { join as join34, relative as relative9 } from "path";
29318
29427
  function walkPyFiles(dir, excludeDirs) {
29319
29428
  const files = [];
29320
29429
  try {
29321
- const entries = readdirSync22(dir, { withFileTypes: true });
29430
+ const entries = readdirSync23(dir, { withFileTypes: true });
29322
29431
  for (const entry of entries) {
29323
29432
  if (entry.isDirectory()) {
29324
29433
  if (excludeDirs.includes(entry.name)) continue;
@@ -29558,12 +29667,12 @@ var init_model_parser = __esm({
29558
29667
  });
29559
29668
 
29560
29669
  // src/python/model-indexer.ts
29561
- import { readFileSync as readFileSync47, readdirSync as readdirSync23 } from "fs";
29670
+ import { readFileSync as readFileSync47, readdirSync as readdirSync24 } from "fs";
29562
29671
  import { join as join35, relative as relative10 } from "path";
29563
29672
  function walkPyFiles2(dir, excludeDirs) {
29564
29673
  const files = [];
29565
29674
  try {
29566
- const entries = readdirSync23(dir, { withFileTypes: true });
29675
+ const entries = readdirSync24(dir, { withFileTypes: true });
29567
29676
  for (const entry of entries) {
29568
29677
  if (entry.isDirectory()) {
29569
29678
  if (excludeDirs.includes(entry.name)) continue;
@@ -29855,7 +29964,7 @@ var init_migration_parser = __esm({
29855
29964
  });
29856
29965
 
29857
29966
  // src/python/migration-indexer.ts
29858
- import { readFileSync as readFileSync48, readdirSync as readdirSync24 } from "fs";
29967
+ import { readFileSync as readFileSync48, readdirSync as readdirSync25 } from "fs";
29859
29968
  import { join as join36, relative as relative11 } from "path";
29860
29969
  function buildPythonMigrationIndex(dataDb, alembicDir) {
29861
29970
  const projectRoot = getProjectRoot();
@@ -29864,10 +29973,10 @@ function buildPythonMigrationIndex(dataDb, alembicDir) {
29864
29973
  const versionsDir = join36(absDir, "versions");
29865
29974
  let files = [];
29866
29975
  try {
29867
- files = readdirSync24(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join36(versionsDir, f2));
29976
+ files = readdirSync25(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join36(versionsDir, f2));
29868
29977
  } catch {
29869
29978
  try {
29870
- files = readdirSync24(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join36(absDir, f2));
29979
+ files = readdirSync25(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join36(absDir, f2));
29871
29980
  } catch {
29872
29981
  }
29873
29982
  }
@@ -29916,7 +30025,7 @@ var init_migration_indexer = __esm({
29916
30025
  });
29917
30026
 
29918
30027
  // src/python/coupling-detector.ts
29919
- import { readFileSync as readFileSync49, readdirSync as readdirSync25 } from "fs";
30028
+ import { readFileSync as readFileSync49, readdirSync as readdirSync26 } from "fs";
29920
30029
  import { join as join37, relative as relative12 } from "path";
29921
30030
  function buildPythonCouplingIndex(dataDb) {
29922
30031
  const projectRoot = getProjectRoot();
@@ -29981,7 +30090,7 @@ function walkFrontendFiles(dir) {
29981
30090
  const files = [];
29982
30091
  const exclude = ["node_modules", ".next", "dist", ".git", "__pycache__", ".venv", "venv"];
29983
30092
  try {
29984
- const entries = readdirSync25(dir, { withFileTypes: true });
30093
+ const entries = readdirSync26(dir, { withFileTypes: true });
29985
30094
  for (const entry of entries) {
29986
30095
  if (entry.isDirectory()) {
29987
30096
  if (exclude.includes(entry.name)) continue;
@@ -30359,8 +30468,8 @@ var init_memory_tools = __esm({
30359
30468
  });
30360
30469
 
30361
30470
  // src/docs-tools.ts
30362
- import { readFileSync as readFileSync50, existsSync as existsSync59 } from "fs";
30363
- import { resolve as resolve34, basename as basename9 } from "path";
30471
+ import { readFileSync as readFileSync50, existsSync as existsSync60 } from "fs";
30472
+ import { resolve as resolve35, basename as basename9 } from "path";
30364
30473
  function p4(baseName) {
30365
30474
  return `${getConfig().toolPrefix}_${baseName}`;
30366
30475
  }
@@ -30415,7 +30524,7 @@ function handleDocsToolCall(name, args2) {
30415
30524
  }
30416
30525
  function loadDocsMap() {
30417
30526
  const mapPath = getResolvedPaths().docsMapPath;
30418
- if (!existsSync59(mapPath)) {
30527
+ if (!existsSync60(mapPath)) {
30419
30528
  throw new Error(`docs-map.json not found at ${mapPath}`);
30420
30529
  }
30421
30530
  return JSON.parse(readFileSync50(mapPath, "utf-8"));
@@ -30488,10 +30597,10 @@ function extractFrontmatter(content) {
30488
30597
  }
30489
30598
  function extractProcedureNames(routerPath) {
30490
30599
  const root = getProjectRoot();
30491
- const absPath = ensureWithinRoot(resolve34(getResolvedPaths().srcDir, "..", routerPath), root);
30492
- if (!existsSync59(absPath)) {
30493
- const altPath = ensureWithinRoot(resolve34(getResolvedPaths().srcDir, "../server/api/routers", basename9(routerPath)), root);
30494
- if (!existsSync59(altPath)) return [];
30600
+ const absPath = ensureWithinRoot(resolve35(getResolvedPaths().srcDir, "..", routerPath), root);
30601
+ if (!existsSync60(absPath)) {
30602
+ const altPath = ensureWithinRoot(resolve35(getResolvedPaths().srcDir, "../server/api/routers", basename9(routerPath)), root);
30603
+ if (!existsSync60(altPath)) return [];
30495
30604
  return extractProcedureNamesFromContent(readFileSync50(altPath, "utf-8"));
30496
30605
  }
30497
30606
  return extractProcedureNamesFromContent(readFileSync50(absPath, "utf-8"));
@@ -30534,8 +30643,8 @@ function handleDocsAudit(args2) {
30534
30643
  for (const [mappingId, triggeringFiles] of affectedMappings) {
30535
30644
  const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
30536
30645
  if (!mapping) continue;
30537
- const helpPagePath = ensureWithinRoot(resolve34(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
30538
- if (!existsSync59(helpPagePath)) {
30646
+ const helpPagePath = ensureWithinRoot(resolve35(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
30647
+ if (!existsSync60(helpPagePath)) {
30539
30648
  results.push({
30540
30649
  helpPage: mapping.helpPage,
30541
30650
  mappingId,
@@ -30587,8 +30696,8 @@ function handleDocsAudit(args2) {
30587
30696
  });
30588
30697
  for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
30589
30698
  if (parentId === mappingId) {
30590
- const guidePath = ensureWithinRoot(resolve34(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
30591
- if (existsSync59(guidePath)) {
30699
+ const guidePath = ensureWithinRoot(resolve35(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
30700
+ if (existsSync60(guidePath)) {
30592
30701
  const guideContent = readFileSync50(guidePath, "utf-8");
30593
30702
  const guideFrontmatter = extractFrontmatter(guideContent);
30594
30703
  if (!guideFrontmatter?.lastVerified || status === "STALE") {
@@ -30622,8 +30731,8 @@ function handleDocsCoverage(args2) {
30622
30731
  const gaps = [];
30623
30732
  const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
30624
30733
  for (const mapping of mappings) {
30625
- const helpPagePath = ensureWithinRoot(resolve34(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
30626
- const exists = existsSync59(helpPagePath);
30734
+ const helpPagePath = ensureWithinRoot(resolve35(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
30735
+ const exists = existsSync60(helpPagePath);
30627
30736
  let hasContent = false;
30628
30737
  let lineCount = 0;
30629
30738
  let lastVerified = null;
@@ -30970,8 +31079,8 @@ var init_observability_tools = __esm({
30970
31079
  });
30971
31080
 
30972
31081
  // src/sentinel-db.ts
30973
- import { existsSync as existsSync60 } from "fs";
30974
- import { resolve as resolve35 } from "path";
31082
+ import { existsSync as existsSync61 } from "fs";
31083
+ import { resolve as resolve36 } from "path";
30975
31084
  function parsePortalScope(raw) {
30976
31085
  if (!raw) return [];
30977
31086
  try {
@@ -31207,23 +31316,23 @@ function validateFeatures(db, domainFilter) {
31207
31316
  const missingProcedures = [];
31208
31317
  const missingPages = [];
31209
31318
  for (const comp of components) {
31210
- const absPath = resolve35(PROJECT_ROOT, comp.component_file);
31211
- if (!existsSync60(absPath)) {
31319
+ const absPath = resolve36(PROJECT_ROOT, comp.component_file);
31320
+ if (!existsSync61(absPath)) {
31212
31321
  missingComponents.push(comp.component_file);
31213
31322
  }
31214
31323
  }
31215
31324
  for (const proc of procedures) {
31216
- const routerPath = resolve35(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
31217
- if (!existsSync60(routerPath)) {
31325
+ const routerPath = resolve36(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
31326
+ if (!existsSync61(routerPath)) {
31218
31327
  missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
31219
31328
  }
31220
31329
  }
31221
31330
  for (const page of pages) {
31222
31331
  const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
31223
- const absPath = resolve35(PROJECT_ROOT, routeToPath);
31224
- if (page.page_route.startsWith("/") && !existsSync60(absPath)) {
31225
- const altPath = resolve35(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
31226
- if (!existsSync60(altPath)) {
31332
+ const absPath = resolve36(PROJECT_ROOT, routeToPath);
31333
+ if (page.page_route.startsWith("/") && !existsSync61(absPath)) {
31334
+ const altPath = resolve36(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
31335
+ if (!existsSync61(altPath)) {
31227
31336
  missingPages.push(page.page_route);
31228
31337
  }
31229
31338
  }
@@ -31749,8 +31858,8 @@ var init_sentinel_tools = __esm({
31749
31858
  });
31750
31859
 
31751
31860
  // src/sentinel-scanner.ts
31752
- import { readFileSync as readFileSync51, existsSync as existsSync61, readdirSync as readdirSync26, statSync as statSync19 } from "fs";
31753
- import { resolve as resolve36, join as join38, basename as basename10, dirname as dirname27, relative as relative13 } from "path";
31861
+ import { readFileSync as readFileSync51, existsSync as existsSync62, readdirSync as readdirSync27, statSync as statSync20 } from "fs";
31862
+ import { resolve as resolve37, join as join38, basename as basename10, dirname as dirname27, relative as relative13 } from "path";
31754
31863
  function inferDomain(filePath) {
31755
31864
  const domains = getConfig().domains;
31756
31865
  const path = filePath.toLowerCase();
@@ -31881,10 +31990,10 @@ function scanComponentExports(dataDb) {
31881
31990
  const projectRoot = getProjectRoot();
31882
31991
  const componentsBase = config.paths.components ?? config.paths.source + "/components";
31883
31992
  const componentDirs = [];
31884
- const basePath = resolve36(projectRoot, componentsBase);
31885
- if (existsSync61(basePath)) {
31993
+ const basePath = resolve37(projectRoot, componentsBase);
31994
+ if (existsSync62(basePath)) {
31886
31995
  try {
31887
- const entries = readdirSync26(basePath, { withFileTypes: true });
31996
+ const entries = readdirSync27(basePath, { withFileTypes: true });
31888
31997
  for (const entry of entries) {
31889
31998
  if (entry.isDirectory()) {
31890
31999
  componentDirs.push(componentsBase + "/" + entry.name);
@@ -31894,8 +32003,8 @@ function scanComponentExports(dataDb) {
31894
32003
  }
31895
32004
  }
31896
32005
  for (const dir of componentDirs) {
31897
- const absDir = resolve36(projectRoot, dir);
31898
- if (!existsSync61(absDir)) continue;
32006
+ const absDir = resolve37(projectRoot, dir);
32007
+ if (!existsSync62(absDir)) continue;
31899
32008
  const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
31900
32009
  for (const file of files) {
31901
32010
  const relPath = relative13(projectRoot, file);
@@ -31948,11 +32057,11 @@ function scanComponentExports(dataDb) {
31948
32057
  function walkDir2(dir) {
31949
32058
  const results = [];
31950
32059
  try {
31951
- const entries = readdirSync26(dir);
32060
+ const entries = readdirSync27(dir);
31952
32061
  for (const entry of entries) {
31953
32062
  const fullPath = join38(dir, entry);
31954
32063
  try {
31955
- const stat = statSync19(fullPath);
32064
+ const stat = statSync20(fullPath);
31956
32065
  if (stat.isDirectory()) {
31957
32066
  results.push(...walkDir2(fullPath));
31958
32067
  } else {
@@ -32660,7 +32769,7 @@ var init_prompt_analyzer = __esm({
32660
32769
  });
32661
32770
 
32662
32771
  // src/validation-engine.ts
32663
- import { existsSync as existsSync62, readFileSync as readFileSync52 } from "fs";
32772
+ import { existsSync as existsSync63, readFileSync as readFileSync52 } from "fs";
32664
32773
  function p10(baseName) {
32665
32774
  return `${getConfig().toolPrefix}_${baseName}`;
32666
32775
  }
@@ -32691,7 +32800,7 @@ function validateFile(filePath, projectRoot) {
32691
32800
  });
32692
32801
  return checks;
32693
32802
  }
32694
- if (!existsSync62(absPath)) {
32803
+ if (!existsSync63(absPath)) {
32695
32804
  checks.push({
32696
32805
  name: "file_exists",
32697
32806
  severity: "error",
@@ -33159,7 +33268,7 @@ var init_adr_generator = __esm({
33159
33268
  });
33160
33269
 
33161
33270
  // src/security-scorer.ts
33162
- import { existsSync as existsSync63, readFileSync as readFileSync53 } from "fs";
33271
+ import { existsSync as existsSync64, readFileSync as readFileSync53 } from "fs";
33163
33272
  function p12(baseName) {
33164
33273
  return `${getConfig().toolPrefix}_${baseName}`;
33165
33274
  }
@@ -33183,7 +33292,7 @@ function scoreFileSecurity(filePath, projectRoot) {
33183
33292
  }]
33184
33293
  };
33185
33294
  }
33186
- if (!existsSync63(absPath)) {
33295
+ if (!existsSync64(absPath)) {
33187
33296
  return { riskScore: 0, findings: [] };
33188
33297
  }
33189
33298
  let source;
@@ -33478,8 +33587,8 @@ var init_security_scorer = __esm({
33478
33587
  });
33479
33588
 
33480
33589
  // src/dependency-scorer.ts
33481
- import { existsSync as existsSync64, readFileSync as readFileSync54 } from "fs";
33482
- import { resolve as resolve37 } from "path";
33590
+ import { existsSync as existsSync65, readFileSync as readFileSync54 } from "fs";
33591
+ import { resolve as resolve38 } from "path";
33483
33592
  function p13(baseName) {
33484
33593
  return `${getConfig().toolPrefix}_${baseName}`;
33485
33594
  }
@@ -33511,8 +33620,8 @@ function calculateDepRisk(factors) {
33511
33620
  return Math.min(100, risk);
33512
33621
  }
33513
33622
  function getInstalledPackages(projectRoot) {
33514
- const pkgPath = resolve37(projectRoot, "package.json");
33515
- if (!existsSync64(pkgPath)) return /* @__PURE__ */ new Map();
33623
+ const pkgPath = resolve38(projectRoot, "package.json");
33624
+ if (!existsSync65(pkgPath)) return /* @__PURE__ */ new Map();
33516
33625
  try {
33517
33626
  const pkg = JSON.parse(readFileSync54(pkgPath, "utf-8"));
33518
33627
  const packages = /* @__PURE__ */ new Map();
@@ -34120,8 +34229,8 @@ var init_regression_detector = __esm({
34120
34229
 
34121
34230
  // src/knowledge-indexer.ts
34122
34231
  import { createHash as createHash16 } from "crypto";
34123
- import { readFileSync as readFileSync55, readdirSync as readdirSync27, statSync as statSync20, existsSync as existsSync65 } from "fs";
34124
- import { resolve as resolve38, relative as relative14, basename as basename11, extname as extname2 } from "path";
34232
+ import { readFileSync as readFileSync55, readdirSync as readdirSync28, statSync as statSync21, existsSync as existsSync66 } from "fs";
34233
+ import { resolve as resolve39, relative as relative14, basename as basename11, extname as extname2 } from "path";
34125
34234
  function getKnowledgePaths() {
34126
34235
  const resolved = getResolvedPaths();
34127
34236
  const config = getConfig();
@@ -34147,9 +34256,9 @@ function discoverMarkdownFiles(baseDir) {
34147
34256
  const files = [];
34148
34257
  function walk(dir) {
34149
34258
  try {
34150
- const entries = readdirSync27(dir, { withFileTypes: true });
34259
+ const entries = readdirSync28(dir, { withFileTypes: true });
34151
34260
  for (const entry of entries) {
34152
- const fullPath = resolve38(dir, entry.name);
34261
+ const fullPath = resolve39(dir, entry.name);
34153
34262
  if (entry.isDirectory()) {
34154
34263
  if (entry.name === "archive" && dir.includes("session-state")) continue;
34155
34264
  if (entry.name === "archive" && dir.includes("status")) continue;
@@ -34428,11 +34537,11 @@ function indexAllKnowledge(db) {
34428
34537
  files.push(...memFiles);
34429
34538
  } catch {
34430
34539
  }
34431
- if (existsSync65(paths.plansDir)) {
34540
+ if (existsSync66(paths.plansDir)) {
34432
34541
  const planFiles = discoverMarkdownFiles(paths.plansDir);
34433
34542
  files.push(...planFiles);
34434
34543
  }
34435
- if (existsSync65(paths.docsDir)) {
34544
+ if (existsSync66(paths.docsDir)) {
34436
34545
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
34437
34546
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
34438
34547
  files.push(...docsFiles);
@@ -34475,7 +34584,7 @@ function indexAllKnowledge(db) {
34475
34584
  } catch {
34476
34585
  }
34477
34586
  for (const filePath of files) {
34478
- if (!existsSync65(filePath)) continue;
34587
+ if (!existsSync66(filePath)) continue;
34479
34588
  const content = readFileSync55(filePath, "utf-8");
34480
34589
  const hash = hashContent2(content);
34481
34590
  const relPath = filePath.startsWith(paths.claudeDir) ? relative14(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative14(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative14(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative14(paths.memoryDir, filePath)}` : basename11(filePath);
@@ -34611,17 +34720,17 @@ function isKnowledgeStale(db) {
34611
34720
  files.push(...discoverMarkdownFiles(paths.memoryDir));
34612
34721
  } catch {
34613
34722
  }
34614
- if (existsSync65(paths.plansDir)) {
34723
+ if (existsSync66(paths.plansDir)) {
34615
34724
  files.push(...discoverMarkdownFiles(paths.plansDir));
34616
34725
  }
34617
- if (existsSync65(paths.docsDir)) {
34726
+ if (existsSync66(paths.docsDir)) {
34618
34727
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
34619
34728
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
34620
34729
  files.push(...docsFiles);
34621
34730
  }
34622
34731
  for (const filePath of files) {
34623
34732
  try {
34624
- const stat = statSync20(filePath);
34733
+ const stat = statSync21(filePath);
34625
34734
  if (stat.mtimeMs > lastIndexTime) return true;
34626
34735
  } catch {
34627
34736
  continue;
@@ -34643,8 +34752,8 @@ var init_knowledge_indexer = __esm({
34643
34752
  });
34644
34753
 
34645
34754
  // src/knowledge-tools.ts
34646
- import { readFileSync as readFileSync56, writeFileSync as writeFileSync16, appendFileSync as appendFileSync5, readdirSync as readdirSync28 } from "fs";
34647
- import { resolve as resolve39, basename as basename12 } from "path";
34755
+ import { readFileSync as readFileSync56, writeFileSync as writeFileSync16, appendFileSync as appendFileSync5, readdirSync as readdirSync29 } from "fs";
34756
+ import { resolve as resolve40, basename as basename12 } from "path";
34648
34757
  function p16(baseName) {
34649
34758
  return `${getConfig().toolPrefix}_${baseName}`;
34650
34759
  }
@@ -35383,7 +35492,7 @@ function handleCorrect(db, args2) {
35383
35492
  if (!wrong || !correction || !rule) {
35384
35493
  return text15("Error: wrong, correction, and rule are all required.");
35385
35494
  }
35386
- const correctionsPath = resolve39(getResolvedPaths().memoryDir, "corrections.md");
35495
+ const correctionsPath = resolve40(getResolvedPaths().memoryDir, "corrections.md");
35387
35496
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
35388
35497
  const title = rule.slice(0, 60);
35389
35498
  const entry = formatCorrectionEntry({
@@ -35578,7 +35687,7 @@ function handleGaps(db, args2) {
35578
35687
  } else if (checkType === "routers") {
35579
35688
  try {
35580
35689
  const routersDir = getResolvedPaths().routersDir;
35581
- const routerFiles = readdirSync28(routersDir).filter((f2) => f2.endsWith(".ts") && !f2.startsWith("_"));
35690
+ const routerFiles = readdirSync29(routersDir).filter((f2) => f2.endsWith(".ts") && !f2.startsWith("_"));
35582
35691
  lines.push(`| Router | Knowledge Hits | Status |`);
35583
35692
  lines.push(`|--------|----------------|--------|`);
35584
35693
  for (const file of routerFiles) {
@@ -36352,8 +36461,8 @@ var init_framework_supports = __esm({
36352
36461
  });
36353
36462
 
36354
36463
  // src/tools.ts
36355
- import { readFileSync as readFileSync57, existsSync as existsSync67 } from "fs";
36356
- import { resolve as resolve40, basename as basename13 } from "path";
36464
+ import { readFileSync as readFileSync57, existsSync as existsSync68 } from "fs";
36465
+ import { resolve as resolve41, basename as basename13 } from "path";
36357
36466
  function prefix2() {
36358
36467
  return getConfig().toolPrefix;
36359
36468
  }
@@ -36388,7 +36497,7 @@ function ensureIndexes(dataDb, codegraphDb, force = false) {
36388
36497
  if (config.python?.root) {
36389
36498
  const pythonRoot = config.python.root;
36390
36499
  const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
36391
- if (force || isPythonDataStale(dataDb, resolve40(getProjectRoot(), pythonRoot))) {
36500
+ if (force || isPythonDataStale(dataDb, resolve41(getProjectRoot(), pythonRoot))) {
36392
36501
  const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
36393
36502
  results.push(`Python imports: ${pyImports}`);
36394
36503
  const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
@@ -36771,8 +36880,8 @@ function handleContext(file, dataDb, codegraphDb) {
36771
36880
  try {
36772
36881
  const resolvedPaths = getResolvedPaths();
36773
36882
  const root = getProjectRoot();
36774
- const absFilePath = ensureWithinRoot(resolve40(resolvedPaths.srcDir, "..", file), root);
36775
- if (existsSync67(absFilePath)) {
36883
+ const absFilePath = ensureWithinRoot(resolve41(resolvedPaths.srcDir, "..", file), root);
36884
+ if (existsSync68(absFilePath)) {
36776
36885
  const fileContent = readFileSync57(absFilePath, "utf-8").slice(0, 3e3);
36777
36886
  const keywords = [];
36778
36887
  if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
@@ -37210,8 +37319,8 @@ function handleSchema(args2) {
37210
37319
  lines.push("Checking all column references against Prisma schema...");
37211
37320
  lines.push("");
37212
37321
  const projectRoot = getProjectRoot();
37213
- const absPath = ensureWithinRoot(resolve40(projectRoot, file), projectRoot);
37214
- if (!existsSync67(absPath)) {
37322
+ const absPath = ensureWithinRoot(resolve41(projectRoot, file), projectRoot);
37323
+ if (!existsSync68(absPath)) {
37215
37324
  return text17(`File not found: ${file}`);
37216
37325
  }
37217
37326
  const source = readFileSync57(absPath, "utf-8");
@@ -37647,7 +37756,7 @@ var init_startup_health = __esm({
37647
37756
  // src/server.ts
37648
37757
  var server_exports = {};
37649
37758
  import { readFileSync as readFileSync58 } from "fs";
37650
- import { resolve as resolve41, dirname as dirname28 } from "path";
37759
+ import { resolve as resolve42, dirname as dirname28 } from "path";
37651
37760
  import { fileURLToPath as fileURLToPath6 } from "url";
37652
37761
  function pruneMemoryOnStartup() {
37653
37762
  try {
@@ -37695,7 +37804,7 @@ var init_server = __esm({
37695
37804
  __dirname5 = dirname28(fileURLToPath6(import.meta.url));
37696
37805
  PKG_VERSION = (() => {
37697
37806
  try {
37698
- const pkg = JSON.parse(readFileSync58(resolve41(__dirname5, "..", "package.json"), "utf-8"));
37807
+ const pkg = JSON.parse(readFileSync58(resolve42(__dirname5, "..", "package.json"), "utf-8"));
37699
37808
  return pkg.version ?? "0.0.0";
37700
37809
  } catch {
37701
37810
  return "0.0.0";
@@ -37998,19 +38107,19 @@ var config_upgrade_exports = {};
37998
38107
  __export(config_upgrade_exports, {
37999
38108
  runConfigUpgrade: () => runConfigUpgrade
38000
38109
  });
38001
- import { existsSync as existsSync68, readFileSync as readFileSync59, writeFileSync as writeFileSync17, copyFileSync as copyFileSync3, unlinkSync as unlinkSync6 } from "fs";
38002
- import { resolve as resolve42 } from "path";
38110
+ import { existsSync as existsSync69, readFileSync as readFileSync59, writeFileSync as writeFileSync17, copyFileSync as copyFileSync3, unlinkSync as unlinkSync6 } from "fs";
38111
+ import { resolve as resolve43 } from "path";
38003
38112
  import { parse as parseYaml6 } from "yaml";
38004
38113
  async function runConfigUpgrade(opts = {}) {
38005
38114
  const cwd = opts.cwd ?? process.cwd();
38006
- const configPath = resolve42(cwd, "massu.config.yaml");
38115
+ const configPath = resolve43(cwd, "massu.config.yaml");
38007
38116
  const bakPath = `${configPath}.bak`;
38008
38117
  const log = opts.silent ? () => {
38009
38118
  } : (s) => process.stdout.write(s);
38010
38119
  const err = opts.silent ? () => {
38011
38120
  } : (s) => process.stderr.write(s);
38012
38121
  if (opts.rollback) {
38013
- if (!existsSync68(bakPath)) {
38122
+ if (!existsSync69(bakPath)) {
38014
38123
  const message = `No backup found at ${bakPath}`;
38015
38124
  err(message + "\n");
38016
38125
  return { exitCode: 1, action: "none", message };
@@ -38026,7 +38135,7 @@ async function runConfigUpgrade(opts = {}) {
38026
38135
  return { exitCode: 2, action: "none", message };
38027
38136
  }
38028
38137
  }
38029
- if (!existsSync68(configPath)) {
38138
+ if (!existsSync69(configPath)) {
38030
38139
  const message = "massu.config.yaml not found. Run: npx massu init";
38031
38140
  err(message + "\n");
38032
38141
  return { exitCode: 1, action: "none", message };
@@ -38090,8 +38199,8 @@ var config_check_drift_exports = {};
38090
38199
  __export(config_check_drift_exports, {
38091
38200
  runConfigCheckDrift: () => runConfigCheckDrift
38092
38201
  });
38093
- import { existsSync as existsSync69, readFileSync as readFileSync60 } from "fs";
38094
- import { resolve as resolve43 } from "path";
38202
+ import { existsSync as existsSync70, readFileSync as readFileSync60 } from "fs";
38203
+ import { resolve as resolve44 } from "path";
38095
38204
  import { parse as parseYaml7 } from "yaml";
38096
38205
  function renderChanges(changes) {
38097
38206
  if (changes.length === 0) return "(none)\n";
@@ -38099,12 +38208,12 @@ function renderChanges(changes) {
38099
38208
  }
38100
38209
  async function runConfigCheckDrift(opts = {}) {
38101
38210
  const cwd = opts.cwd ?? process.cwd();
38102
- const configPath = resolve43(cwd, "massu.config.yaml");
38211
+ const configPath = resolve44(cwd, "massu.config.yaml");
38103
38212
  const log = opts.silent ? () => {
38104
38213
  } : (s) => process.stdout.write(s);
38105
38214
  const err = opts.silent ? () => {
38106
38215
  } : (s) => process.stderr.write(s);
38107
- if (!existsSync69(configPath)) {
38216
+ if (!existsSync70(configPath)) {
38108
38217
  const message = "massu.config.yaml not found. Run: npx massu init";
38109
38218
  err(message + "\n");
38110
38219
  return {
@@ -38186,13 +38295,160 @@ var init_config_check_drift = __esm({
38186
38295
 
38187
38296
  // src/cli.ts
38188
38297
  import { readFileSync as readFileSync61 } from "fs";
38189
- import { resolve as resolve44, dirname as dirname29 } from "path";
38298
+ import { resolve as resolve45, dirname as dirname29 } from "path";
38190
38299
  import { fileURLToPath as fileURLToPath7 } from "url";
38300
+
38301
+ // src/lib/node-bootstrap.ts
38302
+ init_node_floor();
38303
+ import { existsSync, readdirSync, statSync } from "fs";
38304
+ import { spawnSync, execFileSync } from "child_process";
38305
+ import { homedir } from "os";
38306
+ import { isAbsolute, resolve } from "path";
38307
+ var REEXEC_SENTINEL_ENV = "MASSU_BOOTSTRAP_REEXEC";
38308
+ var OPT_OUT_ENV = "MASSU_NO_NODE_BOOTSTRAP";
38309
+ function allowlistPatterns(env) {
38310
+ const patterns = [];
38311
+ const rawHome = env.HOME || homedir();
38312
+ const home = rawHome && isAbsolute(rawHome) ? rawHome : null;
38313
+ const addAbs = (dir, suffix) => {
38314
+ if (dir && isAbsolute(dir)) patterns.push(resolve(dir, suffix));
38315
+ };
38316
+ addAbs(env.NVM_DIR ?? (home && resolve(home, ".nvm")), "versions/node/*/bin/node");
38317
+ addAbs(env.FNM_DIR ?? (home && resolve(home, ".local/share/fnm")), "node-versions/*/installation/bin/node");
38318
+ addAbs(env.VOLTA_HOME ?? (home && resolve(home, ".volta")), "tools/image/node/*/bin/node");
38319
+ addAbs(env.ASDF_DATA_DIR ?? (home && resolve(home, ".asdf")), "installs/nodejs/*/bin/node");
38320
+ addAbs(env.N_PREFIX ?? (home && resolve(home, "n")), "bin/node");
38321
+ patterns.push(
38322
+ "/opt/homebrew/opt/node@*/bin/node",
38323
+ "/usr/local/opt/node@*/bin/node",
38324
+ "/opt/homebrew/bin/node",
38325
+ "/usr/local/bin/node"
38326
+ );
38327
+ return patterns;
38328
+ }
38329
+ function isOwnershipTrusted(candidate) {
38330
+ const getuid = process.getuid;
38331
+ if (typeof getuid !== "function") return true;
38332
+ try {
38333
+ const uid = statSync(candidate).uid;
38334
+ return uid === 0 || uid === getuid.call(process);
38335
+ } catch {
38336
+ return false;
38337
+ }
38338
+ }
38339
+ function expandSingleStarGlob(pattern) {
38340
+ const parts = pattern.split("/");
38341
+ const starIdx = parts.findIndex((p19) => p19.includes("*"));
38342
+ if (starIdx === -1) {
38343
+ return existsSync(pattern) ? [pattern] : [];
38344
+ }
38345
+ const parentDir = parts.slice(0, starIdx).join("/") || "/";
38346
+ const seg = parts[starIdx];
38347
+ const rest = parts.slice(starIdx + 1).join("/");
38348
+ const [pre, post] = seg.split("*");
38349
+ let entries;
38350
+ try {
38351
+ entries = readdirSync(parentDir);
38352
+ } catch {
38353
+ return [];
38354
+ }
38355
+ const out = [];
38356
+ for (const e2 of entries) {
38357
+ if (!e2.startsWith(pre) || !e2.endsWith(post ?? "")) continue;
38358
+ out.push(rest ? `${parentDir}/${e2}/${rest}` : `${parentDir}/${e2}`);
38359
+ }
38360
+ return out;
38361
+ }
38362
+ function probeNodeVersion(candidate) {
38363
+ try {
38364
+ const out = execFileSync(candidate, ["-p", "process.versions.node"], {
38365
+ encoding: "utf8",
38366
+ timeout: 5e3
38367
+ });
38368
+ const v3 = out.trim();
38369
+ return /^\d+\.\d+\.\d+/.test(v3) ? v3 : null;
38370
+ } catch {
38371
+ return null;
38372
+ }
38373
+ }
38374
+ function discoverCompatibleNode(env = process.env) {
38375
+ const seen = /* @__PURE__ */ new Set();
38376
+ let best = null;
38377
+ for (const pattern of allowlistPatterns(env)) {
38378
+ for (const candidate of expandSingleStarGlob(pattern)) {
38379
+ if (!candidate.startsWith("/")) continue;
38380
+ if (seen.has(candidate)) continue;
38381
+ seen.add(candidate);
38382
+ if (!existsSync(candidate)) continue;
38383
+ if (!isOwnershipTrusted(candidate)) continue;
38384
+ const v3 = probeNodeVersion(candidate);
38385
+ if (!v3) continue;
38386
+ if (!checkNodeVersion(`v${v3}`).ok) continue;
38387
+ const [major, minor, patch] = v3.split(".").map((n) => Number.parseInt(n, 10));
38388
+ const lower = best === null || major < best.major || major === best.major && minor < best.minor || major === best.major && minor === best.minor && patch < best.patch;
38389
+ if (lower) best = { path: candidate, major, minor, patch };
38390
+ }
38391
+ }
38392
+ return best?.path ?? null;
38393
+ }
38394
+ function reexecUnder(nodePath, selfEntry, argv, env) {
38395
+ const result = spawnSync(nodePath, [selfEntry, ...argv], {
38396
+ stdio: "inherit",
38397
+ env: { ...env, [REEXEC_SENTINEL_ENV]: "1" }
38398
+ });
38399
+ return result.status ?? 1;
38400
+ }
38401
+ function writeLoudRemedy(floorMessage, stderr) {
38402
+ const floor = `${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0`;
38403
+ const installLine = process.platform === "win32" ? ` winget install OpenJS.NodeJS # or download Node >= ${floor} from https://nodejs.org` : ` nvm install ${MIN_NODE_MAJOR} # or: brew install node@${MIN_NODE_MAJOR}`;
38404
+ stderr(
38405
+ `
38406
+ massu: ${floorMessage}
38407
+ No compatible Node was found on this machine, so massu will not launch under a Node it cannot run on (that would crash your hooks silently).
38408
+ Fix it once: install Node >= ${floor}, e.g.
38409
+ ${installLine}
38410
+ then re-run. (Set ${OPT_OUT_ENV}=1 to skip auto-discovery if you pin your own Node.)
38411
+
38412
+ `
38413
+ );
38414
+ }
38415
+ function bootstrapNodeOrExit(argv, env = process.env, deps = {}) {
38416
+ const nodeVersion = deps.nodeVersion ?? process.version;
38417
+ const stderr = deps.stderr ?? ((m3) => void process.stderr.write(m3));
38418
+ const exit = deps.exit ?? ((c2) => process.exit(c2));
38419
+ const check = checkNodeVersion(nodeVersion);
38420
+ if (env[REEXEC_SENTINEL_ENV] === "1") {
38421
+ if (check.ok) return;
38422
+ writeLoudRemedy(check.message ?? "", stderr);
38423
+ exit(1);
38424
+ return;
38425
+ }
38426
+ if (check.ok) return;
38427
+ if (env[OPT_OUT_ENV] === "1") {
38428
+ writeLoudRemedy(check.message ?? "", stderr);
38429
+ exit(1);
38430
+ return;
38431
+ }
38432
+ const discover = deps.discover ?? discoverCompatibleNode;
38433
+ const nodePath = discover(env);
38434
+ if (!nodePath) {
38435
+ writeLoudRemedy(check.message ?? "", stderr);
38436
+ exit(1);
38437
+ return;
38438
+ }
38439
+ const selfEntry = deps.selfEntry ?? process.argv[1];
38440
+ const reexec = deps.reexec ?? reexecUnder;
38441
+ const status = reexec(nodePath, selfEntry, argv, env);
38442
+ exit(status);
38443
+ }
38444
+
38445
+ // src/cli.ts
38191
38446
  var __filename5 = fileURLToPath7(import.meta.url);
38192
38447
  var __dirname6 = dirname29(__filename5);
38193
38448
  var args = process.argv.slice(2);
38194
38449
  var subcommand = args[0];
38195
38450
  async function main() {
38451
+ bootstrapNodeOrExit(process.argv.slice(2));
38196
38452
  switch (subcommand) {
38197
38453
  case "init": {
38198
38454
  const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
@@ -38481,7 +38737,7 @@ Examples:
38481
38737
  }
38482
38738
  function printVersion() {
38483
38739
  try {
38484
- const pkg = JSON.parse(readFileSync61(resolve44(__dirname6, "../package.json"), "utf-8"));
38740
+ const pkg = JSON.parse(readFileSync61(resolve45(__dirname6, "../package.json"), "utf-8"));
38485
38741
  console.log(`massu v${pkg.version}`);
38486
38742
  } catch {
38487
38743
  console.log("massu v0.1.0");