@massu/core 1.9.3 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -39,6 +39,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  mod
40
40
  ));
41
41
 
42
+ // src/lib/memory-path.ts
43
+ function encodeMemoryDirName(projectRoot) {
44
+ return projectRoot.replace(/\//g, "-");
45
+ }
46
+ var init_memory_path = __esm({
47
+ "src/lib/memory-path.ts"() {
48
+ "use strict";
49
+ }
50
+ });
51
+
42
52
  // src/config.ts
43
53
  import { resolve, dirname } from "path";
44
54
  import { existsSync, readFileSync } from "fs";
@@ -193,7 +203,7 @@ function getResolvedPaths() {
193
203
  plansDir: resolve(root, "docs/plans"),
194
204
  docsDir: resolve(root, "docs"),
195
205
  claudeDir: resolve(root, claudeDirName),
196
- memoryDir: resolve(homedir(), claudeDirName, "projects", root.replace(/\//g, "-"), "memory"),
206
+ memoryDir: resolve(homedir(), claudeDirName, "projects", encodeMemoryDirName(root), "memory"),
197
207
  sessionStatePath: resolve(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
198
208
  sessionArchivePath: resolve(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
199
209
  mcpJsonPath: resolve(root, ".mcp.json"),
@@ -208,6 +218,7 @@ var DomainConfigSchema, PatternRuleConfigSchema, CostModelSchema, AnalyticsConfi
208
218
  var init_config = __esm({
209
219
  "src/config.ts"() {
210
220
  "use strict";
221
+ init_memory_path();
211
222
  DomainConfigSchema = z.object({
212
223
  name: z.string().default("Unknown"),
213
224
  routers: z.array(z.string()).default([]),
@@ -1761,6 +1772,13 @@ function renderTemplate(template, vars) {
1761
1772
  throw new TemplateParseError("unclosed `{{` (no matching `}}`)", tokenStart);
1762
1773
  }
1763
1774
  const inner = template.slice(i + 2, closeIdx);
1775
+ if (isJsxPassThrough(inner)) {
1776
+ out.push("{{");
1777
+ out.push(inner);
1778
+ out.push("}}");
1779
+ i = closeIdx + 2;
1780
+ continue;
1781
+ }
1764
1782
  const rendered = renderToken(inner, vars, tokenStart);
1765
1783
  out.push(rendered);
1766
1784
  i = closeIdx + 2;
@@ -1802,6 +1820,9 @@ function findTokenClose(template, start) {
1802
1820
  }
1803
1821
  return -1;
1804
1822
  }
1823
+ function isJsxPassThrough(inner) {
1824
+ return inner.includes("\n");
1825
+ }
1805
1826
  function renderToken(inner, vars, tokenStart) {
1806
1827
  const trimmed = inner.trim();
1807
1828
  if (trimmed === "") {
@@ -2451,7 +2472,17 @@ function buildTemplateVars() {
2451
2472
  framework: config.framework,
2452
2473
  paths: config.paths,
2453
2474
  detected: config.detected ?? {},
2454
- config
2475
+ config,
2476
+ // P-H006 (plan-stage-c-high-batch): RESERVED CLAUDE CODE PLACEHOLDER.
2477
+ // Claude Code reads `{{ARGUMENTS}}` as a runtime placeholder inside
2478
+ // slash-command files. The Massu template engine has no native concept
2479
+ // of reserved literals; we model the placeholder as a variable whose
2480
+ // value IS the literal `{{ARGUMENTS}}` string. Because the engine never
2481
+ // re-renders output, this passes through verbatim. Closes the bug class
2482
+ // where `/massu-article-review`, `/massu-autoresearch`, etc. silently
2483
+ // failed to install because the engine threw MissingVariableError on
2484
+ // their {{ARGUMENTS}} usage.
2485
+ ARGUMENTS: "{{ARGUMENTS}}"
2455
2486
  };
2456
2487
  }
2457
2488
  function installCommands(projectRoot, opts = {}) {
@@ -8045,41 +8076,41 @@ var require_queue = __commonJS({
8045
8076
  queue.drained = drained;
8046
8077
  return queue;
8047
8078
  function push(value) {
8048
- var p19 = new Promise(function(resolve40, reject) {
8079
+ var p19 = new Promise(function(resolve41, reject) {
8049
8080
  pushCb(value, function(err, result) {
8050
8081
  if (err) {
8051
8082
  reject(err);
8052
8083
  return;
8053
8084
  }
8054
- resolve40(result);
8085
+ resolve41(result);
8055
8086
  });
8056
8087
  });
8057
8088
  p19.catch(noop);
8058
8089
  return p19;
8059
8090
  }
8060
8091
  function unshift(value) {
8061
- var p19 = new Promise(function(resolve40, reject) {
8092
+ var p19 = new Promise(function(resolve41, reject) {
8062
8093
  unshiftCb(value, function(err, result) {
8063
8094
  if (err) {
8064
8095
  reject(err);
8065
8096
  return;
8066
8097
  }
8067
- resolve40(result);
8098
+ resolve41(result);
8068
8099
  });
8069
8100
  });
8070
8101
  p19.catch(noop);
8071
8102
  return p19;
8072
8103
  }
8073
8104
  function drained() {
8074
- var p19 = new Promise(function(resolve40) {
8105
+ var p19 = new Promise(function(resolve41) {
8075
8106
  process.nextTick(function() {
8076
8107
  if (queue.idle()) {
8077
- resolve40();
8108
+ resolve41();
8078
8109
  } else {
8079
8110
  var previousDrain = queue.drain;
8080
8111
  queue.drain = function() {
8081
8112
  if (typeof previousDrain === "function") previousDrain();
8082
- resolve40();
8113
+ resolve41();
8083
8114
  queue.drain = previousDrain;
8084
8115
  };
8085
8116
  }
@@ -8565,9 +8596,9 @@ var require_stream3 = __commonJS({
8565
8596
  });
8566
8597
  }
8567
8598
  _getStat(filepath) {
8568
- return new Promise((resolve40, reject) => {
8599
+ return new Promise((resolve41, reject) => {
8569
8600
  this._stat(filepath, this._fsStatSettings, (error, stats) => {
8570
- return error === null ? resolve40(stats) : reject(error);
8601
+ return error === null ? resolve41(stats) : reject(error);
8571
8602
  });
8572
8603
  });
8573
8604
  }
@@ -8591,10 +8622,10 @@ var require_async5 = __commonJS({
8591
8622
  this._readerStream = new stream_1.default(this._settings);
8592
8623
  }
8593
8624
  dynamic(root, options) {
8594
- return new Promise((resolve40, reject) => {
8625
+ return new Promise((resolve41, reject) => {
8595
8626
  this._walkAsync(root, options, (error, entries) => {
8596
8627
  if (error === null) {
8597
- resolve40(entries);
8628
+ resolve41(entries);
8598
8629
  } else {
8599
8630
  reject(error);
8600
8631
  }
@@ -8604,10 +8635,10 @@ var require_async5 = __commonJS({
8604
8635
  async static(patterns, options) {
8605
8636
  const entries = [];
8606
8637
  const stream = this._readerStream.static(patterns, options);
8607
- return new Promise((resolve40, reject) => {
8638
+ return new Promise((resolve41, reject) => {
8608
8639
  stream.once("error", reject);
8609
8640
  stream.on("data", (entry) => entries.push(entry));
8610
- stream.once("end", () => resolve40(entries));
8641
+ stream.once("end", () => resolve41(entries));
8611
8642
  });
8612
8643
  }
8613
8644
  };
@@ -12982,10 +13013,12 @@ __export(init_exports, {
12982
13013
  detectFramework: () => detectFramework,
12983
13014
  detectPython: () => detectPython,
12984
13015
  generateConfig: () => generateConfig,
13016
+ getInstallerVersion: () => getInstallerVersion,
12985
13017
  initMemoryDir: () => initMemoryDir,
12986
13018
  installHooks: () => installHooks,
12987
13019
  isTemplateName: () => isTemplateName,
12988
13020
  listTemplates: () => listTemplates,
13021
+ mergeHooksConfig: () => mergeHooksConfig,
12989
13022
  parseInitArgs: () => parseInitArgs,
12990
13023
  printInitHelp: () => printInitHelp,
12991
13024
  registerMcpServer: () => registerMcpServer,
@@ -13213,6 +13246,15 @@ function buildConfigFromDetection(opts) {
13213
13246
  pathsSource = monorepoCommonRoot(detection.monorepo.packages);
13214
13247
  }
13215
13248
  }
13249
+ if (pathsSource === "src" && !existsSync11(resolve7(projectRoot, "src"))) {
13250
+ const fallbacks = ["app", "pages", "."];
13251
+ for (const fallback of fallbacks) {
13252
+ if (fallback === "." || existsSync11(resolve7(projectRoot, fallback))) {
13253
+ pathsSource = fallback;
13254
+ break;
13255
+ }
13256
+ }
13257
+ }
13216
13258
  let monorepoRoots;
13217
13259
  if (detection.monorepo.type !== "single") {
13218
13260
  if (detection.monorepo.packages.length > 0) {
@@ -13529,6 +13571,26 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
13529
13571
  return { success: false, error: err instanceof Error ? err.message : String(err) };
13530
13572
  }
13531
13573
  }
13574
+ function getInstallerVersion() {
13575
+ const candidates = [
13576
+ resolve7(__dirname2, "../package.json"),
13577
+ resolve7(__dirname2, "../../package.json")
13578
+ ];
13579
+ for (const candidate of candidates) {
13580
+ if (existsSync11(candidate)) {
13581
+ try {
13582
+ const pkg = JSON.parse(readFileSync11(candidate, "utf-8"));
13583
+ if (typeof pkg.version === "string" && pkg.version.length > 0 && pkg.name === "@massu/core") {
13584
+ return pkg.version;
13585
+ }
13586
+ } catch {
13587
+ }
13588
+ }
13589
+ }
13590
+ throw new Error(
13591
+ "getInstallerVersion: could not resolve @massu/core package.json. This indicates a corrupt install. Re-install via `npx -y @massu/core init`."
13592
+ );
13593
+ }
13532
13594
  function registerMcpServer(projectRoot) {
13533
13595
  const mcpPath = resolve7(projectRoot, ".mcp.json");
13534
13596
  let existing = {};
@@ -13543,10 +13605,11 @@ function registerMcpServer(projectRoot) {
13543
13605
  if (servers.massu) {
13544
13606
  return false;
13545
13607
  }
13608
+ const version = getInstallerVersion();
13546
13609
  servers.massu = {
13547
13610
  type: "stdio",
13548
13611
  command: "npx",
13549
- args: ["-y", "@massu/core"]
13612
+ args: ["-y", `@massu/core@${version}`]
13550
13613
  };
13551
13614
  existing.mcpServers = servers;
13552
13615
  writeFileSync2(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
@@ -13564,15 +13627,16 @@ function resolveHooksDir() {
13564
13627
  }
13565
13628
  return "node_modules/@massu/core/dist/hooks";
13566
13629
  }
13567
- function hookCmd(hooksDir, hookFile) {
13568
- return `node ${hooksDir}/${hookFile}`;
13630
+ function hookCmd(version, hookName) {
13631
+ return `npx -y @massu/core@${version} hook-runner ${hookName}`;
13569
13632
  }
13570
- function buildHooksConfig(hooksDir) {
13633
+ function buildHooksConfig(_hooksDir) {
13634
+ const version = getInstallerVersion();
13571
13635
  return {
13572
13636
  SessionStart: [
13573
13637
  {
13574
13638
  hooks: [
13575
- { type: "command", command: hookCmd(hooksDir, "session-start.js"), timeout: 10 }
13639
+ { type: "command", command: hookCmd(version, "session-start"), timeout: 10 }
13576
13640
  ]
13577
13641
  }
13578
13642
  ],
@@ -13580,32 +13644,32 @@ function buildHooksConfig(hooksDir) {
13580
13644
  {
13581
13645
  matcher: "Bash",
13582
13646
  hooks: [
13583
- { type: "command", command: hookCmd(hooksDir, "security-gate.js"), timeout: 5 }
13647
+ { type: "command", command: hookCmd(version, "security-gate"), timeout: 5 }
13584
13648
  ]
13585
13649
  },
13586
13650
  {
13587
13651
  matcher: "Bash|Write",
13588
13652
  hooks: [
13589
- { type: "command", command: hookCmd(hooksDir, "pre-delete-check.js"), timeout: 5 }
13653
+ { type: "command", command: hookCmd(version, "pre-delete-check"), timeout: 5 }
13590
13654
  ]
13591
13655
  }
13592
13656
  ],
13593
13657
  PostToolUse: [
13594
13658
  {
13595
13659
  hooks: [
13596
- { type: "command", command: hookCmd(hooksDir, "post-tool-use.js"), timeout: 10 },
13597
- { type: "command", command: hookCmd(hooksDir, "quality-event.js"), timeout: 5 },
13598
- { type: "command", command: hookCmd(hooksDir, "cost-tracker.js"), timeout: 5 }
13660
+ { type: "command", command: hookCmd(version, "post-tool-use"), timeout: 10 },
13661
+ { type: "command", command: hookCmd(version, "quality-event"), timeout: 5 },
13662
+ { type: "command", command: hookCmd(version, "cost-tracker"), timeout: 5 }
13599
13663
  ]
13600
13664
  },
13601
13665
  {
13602
13666
  matcher: "Edit|Write",
13603
13667
  hooks: [
13604
- { type: "command", command: hookCmd(hooksDir, "post-edit-context.js"), timeout: 5 },
13668
+ { type: "command", command: hookCmd(version, "post-edit-context"), timeout: 5 },
13605
13669
  // Auto-learning pipeline — classifies failures and detects fixes on
13606
13670
  // file changes. See Phase 5-6 of the autodetect plan.
13607
- { type: "command", command: hookCmd(hooksDir, "fix-detector.js"), timeout: 5 },
13608
- { type: "command", command: hookCmd(hooksDir, "classify-failure.js"), timeout: 5 }
13671
+ { type: "command", command: hookCmd(version, "fix-detector"), timeout: 5 },
13672
+ { type: "command", command: hookCmd(version, "classify-failure"), timeout: 5 }
13609
13673
  ]
13610
13674
  },
13611
13675
  {
@@ -13613,37 +13677,86 @@ function buildHooksConfig(hooksDir) {
13613
13677
  hooks: [
13614
13678
  // Incident + rule enforcement pipelines fire on Write-only (incidents
13615
13679
  // are authored as .md files; rules are enforced after new-file drops).
13616
- { type: "command", command: hookCmd(hooksDir, "incident-pipeline.js"), timeout: 5 },
13617
- { type: "command", command: hookCmd(hooksDir, "rule-enforcement-pipeline.js"), timeout: 5 }
13680
+ { type: "command", command: hookCmd(version, "incident-pipeline"), timeout: 5 },
13681
+ { type: "command", command: hookCmd(version, "rule-enforcement-pipeline"), timeout: 5 }
13618
13682
  ]
13619
13683
  }
13620
13684
  ],
13621
13685
  Stop: [
13622
13686
  {
13623
13687
  hooks: [
13624
- { type: "command", command: hookCmd(hooksDir, "session-end.js"), timeout: 15 },
13688
+ { type: "command", command: hookCmd(version, "session-end"), timeout: 15 },
13625
13689
  // Session-end auto-learning aggregation (failure-class roll-up).
13626
- { type: "command", command: hookCmd(hooksDir, "auto-learning-pipeline.js"), timeout: 10 }
13690
+ { type: "command", command: hookCmd(version, "auto-learning-pipeline"), timeout: 10 }
13627
13691
  ]
13628
13692
  }
13629
13693
  ],
13630
13694
  PreCompact: [
13631
13695
  {
13632
13696
  hooks: [
13633
- { type: "command", command: hookCmd(hooksDir, "pre-compact.js"), timeout: 10 }
13697
+ { type: "command", command: hookCmd(version, "pre-compact"), timeout: 10 }
13634
13698
  ]
13635
13699
  }
13636
13700
  ],
13637
13701
  UserPromptSubmit: [
13638
13702
  {
13639
13703
  hooks: [
13640
- { type: "command", command: hookCmd(hooksDir, "user-prompt.js"), timeout: 5 },
13641
- { type: "command", command: hookCmd(hooksDir, "intent-suggester.js"), timeout: 5 }
13704
+ { type: "command", command: hookCmd(version, "user-prompt"), timeout: 5 },
13705
+ { type: "command", command: hookCmd(version, "intent-suggester"), timeout: 5 }
13642
13706
  ]
13643
13707
  }
13644
13708
  ]
13645
13709
  };
13646
13710
  }
13711
+ function mergeHooksConfig(existing, additions) {
13712
+ const eventNames = /* @__PURE__ */ new Set([
13713
+ ...Object.keys(existing ?? {}),
13714
+ ...Object.keys(additions ?? {})
13715
+ ]);
13716
+ const merged = {};
13717
+ for (const event of eventNames) {
13718
+ const existingGroups = existing?.[event] ?? [];
13719
+ const additionGroups = additions?.[event] ?? [];
13720
+ const byMatcher = /* @__PURE__ */ new Map();
13721
+ for (const group of existingGroups) {
13722
+ const key = group.matcher ?? "";
13723
+ const existingGroup = byMatcher.get(key);
13724
+ if (existingGroup) {
13725
+ existingGroup.hooks = mergeHookEntries(existingGroup.hooks, group.hooks);
13726
+ } else {
13727
+ byMatcher.set(key, { ...group, hooks: [...group.hooks ?? []] });
13728
+ }
13729
+ }
13730
+ for (const group of additionGroups) {
13731
+ const key = group.matcher ?? "";
13732
+ const existingGroup = byMatcher.get(key);
13733
+ if (existingGroup) {
13734
+ existingGroup.hooks = mergeHookEntries(existingGroup.hooks, group.hooks);
13735
+ } else {
13736
+ byMatcher.set(key, { ...group, hooks: [...group.hooks ?? []] });
13737
+ }
13738
+ }
13739
+ merged[event] = Array.from(byMatcher.values());
13740
+ }
13741
+ return merged;
13742
+ }
13743
+ function mergeHookEntries(existing, additions) {
13744
+ const seen = /* @__PURE__ */ new Set();
13745
+ const result = [];
13746
+ for (const entry of additions ?? []) {
13747
+ if (!entry || typeof entry.command !== "string") continue;
13748
+ if (seen.has(entry.command)) continue;
13749
+ seen.add(entry.command);
13750
+ result.push(entry);
13751
+ }
13752
+ for (const entry of existing ?? []) {
13753
+ if (!entry || typeof entry.command !== "string") continue;
13754
+ if (seen.has(entry.command)) continue;
13755
+ seen.add(entry.command);
13756
+ result.push(entry);
13757
+ }
13758
+ return result;
13759
+ }
13647
13760
  function installHooks(projectRoot) {
13648
13761
  let claudeDirName = ".claude";
13649
13762
  try {
@@ -13656,21 +13769,39 @@ function installHooks(projectRoot) {
13656
13769
  mkdirSync5(claudeDir, { recursive: true });
13657
13770
  }
13658
13771
  const settings = readSettingsLocal(claudeDir);
13659
- const hooksDir = resolveHooksDir();
13660
- const hooksConfig = buildHooksConfig(hooksDir);
13772
+ const hooksConfig = buildHooksConfig(resolveHooksDir());
13773
+ const existingHooks = settings.hooks ?? {};
13774
+ const mergedHooks = mergeHooksConfig(existingHooks, hooksConfig);
13661
13775
  let hookCount = 0;
13662
- for (const groups of Object.values(hooksConfig)) {
13776
+ for (const groups of Object.values(mergedHooks)) {
13663
13777
  for (const group of groups) {
13664
13778
  hookCount += group.hooks.length;
13665
13779
  }
13666
13780
  }
13667
- settings.hooks = hooksConfig;
13781
+ settings.hooks = mergedHooks;
13668
13782
  writeSettingsLocalAtomic(claudeDir, settings);
13669
13783
  return { installed: true, count: hookCount };
13670
13784
  }
13671
13785
  function initMemoryDir(projectRoot) {
13672
- const encodedRoot = "-" + projectRoot.replace(/\//g, "-");
13786
+ const encodedRoot = encodeMemoryDirName(projectRoot);
13673
13787
  const memoryDir = resolve7(homedir4(), `.claude/projects/${encodedRoot}/memory`);
13788
+ let migratedFromLegacy = false;
13789
+ const legacyDir = resolve7(homedir4(), `.claude/projects/-${encodedRoot}/memory`);
13790
+ if (existsSync11(legacyDir) && !existsSync11(memoryDir)) {
13791
+ try {
13792
+ mkdirSync5(resolve7(memoryDir, ".."), { recursive: true });
13793
+ renameSync3(legacyDir, memoryDir);
13794
+ try {
13795
+ const legacyParent = resolve7(legacyDir, "..");
13796
+ if (existsSync11(legacyParent) && readdirSync10(legacyParent).length === 0) {
13797
+ rmSync2(legacyParent, { recursive: false });
13798
+ }
13799
+ } catch {
13800
+ }
13801
+ migratedFromLegacy = true;
13802
+ } catch {
13803
+ }
13804
+ }
13674
13805
  let created = false;
13675
13806
  if (!existsSync11(memoryDir)) {
13676
13807
  mkdirSync5(memoryDir, { recursive: true });
@@ -13697,7 +13828,7 @@ function initMemoryDir(projectRoot) {
13697
13828
  writeFileSync2(memoryMdPath, memoryContent, "utf-8");
13698
13829
  memoryMdCreated = true;
13699
13830
  }
13700
- return { created, memoryMdCreated };
13831
+ return { created, memoryMdCreated, migratedFromLegacy };
13701
13832
  }
13702
13833
  function parseInitArgs(argv) {
13703
13834
  const opts = {};
@@ -13956,16 +14087,19 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
13956
14087
  } catch {
13957
14088
  }
13958
14089
  }
13959
- const { created: memDirCreated, memoryMdCreated } = initMemoryDir(projectRoot);
14090
+ const { created: memDirCreated, memoryMdCreated, migratedFromLegacy } = initMemoryDir(projectRoot);
13960
14091
  if (memDirCreated) {
13961
14092
  log(" Created memory directory");
13962
14093
  }
13963
14094
  if (memoryMdCreated) {
13964
14095
  log(" Created initial MEMORY.md");
13965
14096
  }
14097
+ if (migratedFromLegacy) {
14098
+ log(" Migrated memory directory from legacy double-dash path (pre-1.9.4)");
14099
+ }
13966
14100
  (async () => {
13967
14101
  try {
13968
- const encodedRoot = projectRoot.replace(/\//g, "-");
14102
+ const encodedRoot = encodeMemoryDirName(projectRoot);
13969
14103
  const memoryDir = resolve7(homedir4(), ".claude", "projects", encodedRoot, "memory");
13970
14104
  const memFiles = existsSync11(memoryDir) ? readdirSync10(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
13971
14105
  if (memFiles.length > 0) {
@@ -14025,6 +14159,7 @@ var init_init = __esm({
14025
14159
  init_config();
14026
14160
  init_install_commands();
14027
14161
  init_settings_local();
14162
+ init_memory_path();
14028
14163
  init_detect();
14029
14164
  init_drift();
14030
14165
  __filename2 = fileURLToPath2(import.meta.url);
@@ -14341,6 +14476,35 @@ var init_license = __esm({
14341
14476
  }
14342
14477
  });
14343
14478
 
14479
+ // src/lib/hook-registry.ts
14480
+ function getExpectedHookFiles() {
14481
+ return REGISTERED_HOOKS.map((name) => `${name}.js`);
14482
+ }
14483
+ var REGISTERED_HOOKS;
14484
+ var init_hook_registry = __esm({
14485
+ "src/lib/hook-registry.ts"() {
14486
+ "use strict";
14487
+ REGISTERED_HOOKS = [
14488
+ "auto-learning-pipeline",
14489
+ "classify-failure",
14490
+ "cost-tracker",
14491
+ "fix-detector",
14492
+ "incident-pipeline",
14493
+ "intent-suggester",
14494
+ "post-edit-context",
14495
+ "post-tool-use",
14496
+ "pre-compact",
14497
+ "pre-delete-check",
14498
+ "quality-event",
14499
+ "rule-enforcement-pipeline",
14500
+ "security-gate",
14501
+ "session-end",
14502
+ "session-start",
14503
+ "user-prompt"
14504
+ ];
14505
+ }
14506
+ });
14507
+
14344
14508
  // src/commands/doctor.ts
14345
14509
  var doctor_exports = {};
14346
14510
  __export(doctor_exports, {
@@ -14714,21 +14878,10 @@ var init_doctor = __esm({
14714
14878
  init_config();
14715
14879
  init_license();
14716
14880
  init_settings_local();
14881
+ init_hook_registry();
14717
14882
  __filename3 = fileURLToPath3(import.meta.url);
14718
14883
  __dirname3 = dirname7(__filename3);
14719
- EXPECTED_HOOKS = [
14720
- "session-start.js",
14721
- "session-end.js",
14722
- "post-tool-use.js",
14723
- "user-prompt.js",
14724
- "pre-compact.js",
14725
- "pre-delete-check.js",
14726
- "post-edit-context.js",
14727
- "security-gate.js",
14728
- "cost-tracker.js",
14729
- "quality-event.js",
14730
- "intent-suggester.js"
14731
- ];
14884
+ EXPECTED_HOOKS = getExpectedHookFiles();
14732
14885
  }
14733
14886
  });
14734
14887
 
@@ -14756,13 +14909,113 @@ var init_install_hooks = __esm({
14756
14909
  }
14757
14910
  });
14758
14911
 
14912
+ // src/commands/hook-runner.ts
14913
+ var hook_runner_exports = {};
14914
+ __export(hook_runner_exports, {
14915
+ HOOK_NAME_TO_FILE: () => HOOK_NAME_TO_FILE,
14916
+ resolveHookFile: () => resolveHookFile,
14917
+ runHookRunner: () => runHookRunner
14918
+ });
14919
+ import { existsSync as existsSync13 } from "fs";
14920
+ import { resolve as resolve9, dirname as dirname8 } from "path";
14921
+ import { fileURLToPath as fileURLToPath4 } from "url";
14922
+ import { spawn } from "child_process";
14923
+ function resolveHookFile(hookName) {
14924
+ const file = HOOK_NAME_TO_FILE[hookName];
14925
+ if (!file) {
14926
+ throw new Error(
14927
+ `Unknown hook: "${hookName}". Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}`
14928
+ );
14929
+ }
14930
+ const candidates = [
14931
+ // Bundled compiled layout: dist/cli.js → ./hooks/<file>.js
14932
+ resolve9(__dirname4, "hooks", file),
14933
+ // TS-source dev / sibling layout: src/commands/ → ../hooks/<file>
14934
+ resolve9(__dirname4, "../hooks", file),
14935
+ // TS-source dev fallback: src/commands/ → ../../dist/hooks/<file>
14936
+ resolve9(__dirname4, "../../dist/hooks", file)
14937
+ ];
14938
+ for (const candidate of candidates) {
14939
+ if (existsSync13(candidate)) {
14940
+ return candidate;
14941
+ }
14942
+ }
14943
+ throw new Error(
14944
+ `Hook file not found for "${hookName}". Searched: ${candidates.join(", ")}. This indicates a broken @massu/core install. Re-run \`npx -y @massu/core init\`.`
14945
+ );
14946
+ }
14947
+ async function runHookRunner(args2) {
14948
+ const hookName = args2[0];
14949
+ if (!hookName) {
14950
+ process.stderr.write(
14951
+ `massu hook-runner: missing hook name.
14952
+ Usage: massu hook-runner <hook-name>
14953
+ Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}
14954
+ `
14955
+ );
14956
+ return { exitCode: 2 };
14957
+ }
14958
+ let hookFile;
14959
+ try {
14960
+ hookFile = resolveHookFile(hookName);
14961
+ } catch (err) {
14962
+ process.stderr.write(`massu hook-runner: ${err instanceof Error ? err.message : String(err)}
14963
+ `);
14964
+ return { exitCode: 2 };
14965
+ }
14966
+ return new Promise((resolvePromise) => {
14967
+ const child = spawn(process.execPath, [hookFile], {
14968
+ stdio: ["inherit", "inherit", "inherit"],
14969
+ env: process.env
14970
+ });
14971
+ child.on("exit", (code, signal) => {
14972
+ if (signal) {
14973
+ resolvePromise({ exitCode: 128 });
14974
+ return;
14975
+ }
14976
+ resolvePromise({ exitCode: code ?? 0 });
14977
+ });
14978
+ child.on("error", (err) => {
14979
+ process.stderr.write(`massu hook-runner: failed to spawn hook "${hookName}": ${err.message}
14980
+ `);
14981
+ resolvePromise({ exitCode: 2 });
14982
+ });
14983
+ });
14984
+ }
14985
+ var __filename4, __dirname4, HOOK_NAME_TO_FILE;
14986
+ var init_hook_runner = __esm({
14987
+ "src/commands/hook-runner.ts"() {
14988
+ "use strict";
14989
+ __filename4 = fileURLToPath4(import.meta.url);
14990
+ __dirname4 = dirname8(__filename4);
14991
+ HOOK_NAME_TO_FILE = {
14992
+ "session-start": "session-start.js",
14993
+ "session-end": "session-end.js",
14994
+ "security-gate": "security-gate.js",
14995
+ "pre-delete-check": "pre-delete-check.js",
14996
+ "post-tool-use": "post-tool-use.js",
14997
+ "post-edit-context": "post-edit-context.js",
14998
+ "quality-event": "quality-event.js",
14999
+ "cost-tracker": "cost-tracker.js",
15000
+ "fix-detector": "fix-detector.js",
15001
+ "classify-failure": "classify-failure.js",
15002
+ "incident-pipeline": "incident-pipeline.js",
15003
+ "rule-enforcement-pipeline": "rule-enforcement-pipeline.js",
15004
+ "auto-learning-pipeline": "auto-learning-pipeline.js",
15005
+ "pre-compact": "pre-compact.js",
15006
+ "user-prompt": "user-prompt.js",
15007
+ "intent-suggester": "intent-suggester.js"
15008
+ };
15009
+ }
15010
+ });
15011
+
14759
15012
  // src/commands/permissions.ts
14760
15013
  var permissions_exports = {};
14761
15014
  __export(permissions_exports, {
14762
15015
  handlePermissionsSubcommand: () => handlePermissionsSubcommand,
14763
15016
  printPermissionsHelp: () => printPermissionsHelp
14764
15017
  });
14765
- import { resolve as resolve9 } from "path";
15018
+ import { resolve as resolve10 } from "path";
14766
15019
  function resolveClaudeDir2() {
14767
15020
  let claudeDirName = ".claude";
14768
15021
  try {
@@ -14770,7 +15023,7 @@ function resolveClaudeDir2() {
14770
15023
  } catch {
14771
15024
  claudeDirName = ".claude";
14772
15025
  }
14773
- return resolve9(process.cwd(), claudeDirName);
15026
+ return resolve10(process.cwd(), claudeDirName);
14774
15027
  }
14775
15028
  async function handlePermissionsSubcommand(args2) {
14776
15029
  const sub = args2[0];
@@ -14884,8 +15137,8 @@ var init_permissions2 = __esm({
14884
15137
  });
14885
15138
 
14886
15139
  // src/changelog-generator.ts
14887
- import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync12 } from "fs";
14888
- import { resolve as resolve10 } from "path";
15140
+ import { existsSync as existsSync14, readFileSync as readFileSync13, readdirSync as readdirSync12 } from "fs";
15141
+ import { resolve as resolve11 } from "path";
14889
15142
  function parseCommitsForPlanTokens(subjects) {
14890
15143
  const tokens = /* @__PURE__ */ new Set();
14891
15144
  const maintenance = [];
@@ -14902,7 +15155,7 @@ function parseCommitsForPlanTokens(subjects) {
14902
15155
  function loadPlanSummaries(tokens, planDir) {
14903
15156
  const result = /* @__PURE__ */ new Map();
14904
15157
  if (tokens.size === 0) return result;
14905
- if (!existsSync13(planDir)) {
15158
+ if (!existsSync14(planDir)) {
14906
15159
  throw new Error(`Plan directory does not exist: ${planDir}`);
14907
15160
  }
14908
15161
  const files = readdirSync12(planDir).filter((f2) => f2.endsWith(".md"));
@@ -14910,7 +15163,7 @@ function loadPlanSummaries(tokens, planDir) {
14910
15163
  let matchedFile = null;
14911
15164
  let content = "";
14912
15165
  for (const file of files) {
14913
- const path = resolve10(planDir, file);
15166
+ const path = resolve11(planDir, file);
14914
15167
  const text18 = readFileSync13(path, "utf-8");
14915
15168
  const tokenRe = new RegExp(
14916
15169
  `^\\*\\*Plan Token\\*\\*:\\s*\`?${token.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\`?(\\s|$)`,
@@ -14992,8 +15245,8 @@ __export(changelog_exports, {
14992
15245
  printChangelogHelp: () => printChangelogHelp
14993
15246
  });
14994
15247
  import { execSync } from "child_process";
14995
- import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
14996
- import { resolve as resolve11 } from "path";
15248
+ import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
15249
+ import { resolve as resolve12 } from "path";
14997
15250
  function resolveRepoRoot() {
14998
15251
  try {
14999
15252
  return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
@@ -15017,8 +15270,8 @@ function getCommitSubjects(range) {
15017
15270
  }
15018
15271
  }
15019
15272
  function getCurrentVersion(repoRoot) {
15020
- const pkgPath = resolve11(repoRoot, "packages/core/package.json");
15021
- if (!existsSync14(pkgPath)) return "0.0.0";
15273
+ const pkgPath = resolve12(repoRoot, "packages/core/package.json");
15274
+ if (!existsSync15(pkgPath)) return "0.0.0";
15022
15275
  const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
15023
15276
  return pkg.version || "0.0.0";
15024
15277
  }
@@ -15026,8 +15279,8 @@ function todayDate() {
15026
15279
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
15027
15280
  }
15028
15281
  function getLatestChangelogEntryBody(repoRoot) {
15029
- const path = resolve11(repoRoot, "CHANGELOG.md");
15030
- if (!existsSync14(path)) return "";
15282
+ const path = resolve12(repoRoot, "CHANGELOG.md");
15283
+ if (!existsSync15(path)) return "";
15031
15284
  const content = readFileSync14(path, "utf-8");
15032
15285
  const m3 = content.match(/^## \[[\d.]+\][^\n]*\n([\s\S]*?)(?=\n## \[|$)/m);
15033
15286
  return m3 ? m3[1] : "";
@@ -15035,7 +15288,7 @@ function getLatestChangelogEntryBody(repoRoot) {
15035
15288
  async function handleChangelogSubcommand(args2) {
15036
15289
  const sub = args2[0];
15037
15290
  const repoRoot = resolveRepoRoot();
15038
- const planDir = resolve11(repoRoot, "docs/plans");
15291
+ const planDir = resolve12(repoRoot, "docs/plans");
15039
15292
  switch (sub) {
15040
15293
  case "generate": {
15041
15294
  const lastTag = getLastTag();
@@ -15127,8 +15380,8 @@ var show_template_exports = {};
15127
15380
  __export(show_template_exports, {
15128
15381
  runShowTemplate: () => runShowTemplate
15129
15382
  });
15130
- import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
15131
- import { resolve as resolve12 } from "path";
15383
+ import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
15384
+ import { resolve as resolve13 } from "path";
15132
15385
  function normalizeBaseName(input) {
15133
15386
  return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
15134
15387
  }
@@ -15156,8 +15409,8 @@ async function runShowTemplate(args2) {
15156
15409
  return;
15157
15410
  }
15158
15411
  const suffix = choice.kind === "hit" ? choice.suffix : "";
15159
- const file = suffix === "" ? resolve12(sourceDir, `${baseName}.md`) : resolve12(sourceDir, `${baseName}${suffix}.md`);
15160
- if (!existsSync15(file)) {
15412
+ const file = suffix === "" ? resolve13(sourceDir, `${baseName}.md`) : resolve13(sourceDir, `${baseName}${suffix}.md`);
15413
+ if (!existsSync16(file)) {
15161
15414
  process.stderr.write(`massu: resolved template "${file}" no longer exists
15162
15415
  `);
15163
15416
  process.exit(1);
@@ -15217,7 +15470,7 @@ var init_passthrough = __esm({
15217
15470
 
15218
15471
  // src/lib/fileLock.ts
15219
15472
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync16, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
15220
- import { dirname as dirname8 } from "path";
15473
+ import { dirname as dirname9 } from "path";
15221
15474
  import * as lockfile from "proper-lockfile";
15222
15475
  function readLockHolderPid(lockPath) {
15223
15476
  try {
@@ -15240,7 +15493,7 @@ function busyWaitSync(ms) {
15240
15493
  Atomics.wait(view, 0, 0, ms);
15241
15494
  }
15242
15495
  function withFileLockSync(lockPath, fn, opts = {}) {
15243
- mkdirSync6(dirname8(lockPath), { recursive: true });
15496
+ mkdirSync6(dirname9(lockPath), { recursive: true });
15244
15497
  const staleMs = opts.staleMs ?? 3e4;
15245
15498
  const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
15246
15499
  const pollIntervalMs = opts.pollIntervalMs ?? 100;
@@ -15308,9 +15561,9 @@ var init_fileLock = __esm({
15308
15561
  });
15309
15562
 
15310
15563
  // src/lib/installLock.ts
15311
- import { resolve as resolve13 } from "path";
15564
+ import { resolve as resolve14 } from "path";
15312
15565
  function withInstallLock(projectRoot, fn, opts = {}) {
15313
- const lockPath = resolve13(projectRoot, ".massu", "installAll.lock");
15566
+ const lockPath = resolve14(projectRoot, ".massu", "installAll.lock");
15314
15567
  return withFileLockSync(
15315
15568
  lockPath,
15316
15569
  fn,
@@ -15346,8 +15599,8 @@ __export(config_refresh_exports, {
15346
15599
  mergeRefresh: () => mergeRefresh,
15347
15600
  runConfigRefresh: () => runConfigRefresh
15348
15601
  });
15349
- import { existsSync as existsSync16, readFileSync as readFileSync17, rmSync as rmSync4 } from "fs";
15350
- import { resolve as resolve14 } from "path";
15602
+ import { existsSync as existsSync17, readFileSync as readFileSync17, rmSync as rmSync4 } from "fs";
15603
+ import { resolve as resolve15 } from "path";
15351
15604
  import { parse as parseYaml5 } from "yaml";
15352
15605
  function flatten(obj, prefix3 = "") {
15353
15606
  const out = {};
@@ -15475,10 +15728,10 @@ function renderDiff(diff) {
15475
15728
  }
15476
15729
  async function runConfigRefresh(opts = {}) {
15477
15730
  const cwd = opts.cwd ?? process.cwd();
15478
- const configPath = resolve14(cwd, "massu.config.yaml");
15731
+ const configPath = resolve15(cwd, "massu.config.yaml");
15479
15732
  const log = opts.silent ? () => {
15480
15733
  } : (s) => process.stdout.write(s);
15481
- if (!existsSync16(configPath)) {
15734
+ if (!existsSync17(configPath)) {
15482
15735
  const message = "massu.config.yaml not found. Run: npx massu init";
15483
15736
  if (!opts.silent) process.stderr.write(message + "\n");
15484
15737
  return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
@@ -15558,8 +15811,8 @@ async function runConfigRefresh(opts = {}) {
15558
15811
  `);
15559
15812
  const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
15560
15813
  if (stackResolved) {
15561
- const placeholderPath = resolve14(installResult.claudeDir, "commands", "_massu-needs-stack.md");
15562
- if (existsSync16(placeholderPath)) {
15814
+ const placeholderPath = resolve15(installResult.claudeDir, "commands", "_massu-needs-stack.md");
15815
+ if (existsSync17(placeholderPath)) {
15563
15816
  try {
15564
15817
  rmSync4(placeholderPath, { force: true });
15565
15818
  log("Removed _massu-needs-stack.md (stack now declared).\n");
@@ -15668,12 +15921,12 @@ var init_gitToplevel = __esm({
15668
15921
  });
15669
15922
 
15670
15923
  // src/watch/lockfile-detector.ts
15671
- import { existsSync as existsSync17, statSync as statSync8 } from "fs";
15672
- import { resolve as resolve15 } from "path";
15924
+ import { existsSync as existsSync18, statSync as statSync8 } from "fs";
15925
+ import { resolve as resolve16 } from "path";
15673
15926
  function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
15674
15927
  for (const lf of KNOWN_LOCKFILES) {
15675
- const p19 = resolve15(projectRoot, lf);
15676
- if (!existsSync17(p19)) continue;
15928
+ const p19 = resolve16(projectRoot, lf);
15929
+ if (!existsSync18(p19)) continue;
15677
15930
  try {
15678
15931
  const stat = statSync8(p19);
15679
15932
  const delta = now - stat.mtimeMs;
@@ -15686,7 +15939,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
15686
15939
  function gitMidOperation(projectRoot) {
15687
15940
  const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
15688
15941
  for (const s of sentinels) {
15689
- if (existsSync17(resolve15(projectRoot, ".git", s))) return true;
15942
+ if (existsSync18(resolve16(projectRoot, ".git", s))) return true;
15690
15943
  }
15691
15944
  return false;
15692
15945
  }
@@ -15883,17 +16136,17 @@ var init_paths = __esm({
15883
16136
  });
15884
16137
 
15885
16138
  // src/watch/state.ts
15886
- import { closeSync as closeSync3, existsSync as existsSync18, fsyncSync as fsyncSync3, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync18, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync4, writeSync as writeSync3 } from "fs";
15887
- import { dirname as dirname9, resolve as resolve16 } from "path";
16139
+ import { closeSync as closeSync3, existsSync as existsSync19, fsyncSync as fsyncSync3, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync18, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync4, writeSync as writeSync3 } from "fs";
16140
+ import { dirname as dirname10, resolve as resolve17 } from "path";
15888
16141
  function watchStatePath(projectRoot) {
15889
- return resolve16(projectRoot, ".massu", "watch-state.json");
16142
+ return resolve17(projectRoot, ".massu", "watch-state.json");
15890
16143
  }
15891
16144
  function backupStatePath(projectRoot) {
15892
- return resolve16(projectRoot, ".massu", "watch-state.v0.bak.json");
16145
+ return resolve17(projectRoot, ".massu", "watch-state.v0.bak.json");
15893
16146
  }
15894
16147
  function readState(projectRoot) {
15895
16148
  const path = watchStatePath(projectRoot);
15896
- if (!existsSync18(path)) return { ...DEFAULT_STATE };
16149
+ if (!existsSync19(path)) return { ...DEFAULT_STATE };
15897
16150
  const content = readFileSync18(path, "utf-8");
15898
16151
  let raw;
15899
16152
  try {
@@ -15936,14 +16189,14 @@ function readState(projectRoot) {
15936
16189
  }
15937
16190
  function archiveCorrupt(projectRoot, content) {
15938
16191
  const bak = backupStatePath(projectRoot);
15939
- mkdirSync7(dirname9(bak), { recursive: true });
16192
+ mkdirSync7(dirname10(bak), { recursive: true });
15940
16193
  writeFileSync4(bak, content, "utf-8");
15941
16194
  }
15942
16195
  function writeStateAtomic(projectRoot, state) {
15943
16196
  const path = watchStatePath(projectRoot);
15944
16197
  writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
15945
16198
  const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
15946
- mkdirSync7(dirname9(path), { recursive: true });
16199
+ mkdirSync7(dirname10(path), { recursive: true });
15947
16200
  let renamed = false;
15948
16201
  try {
15949
16202
  const fd = openSync3(tmp, "w");
@@ -15957,7 +16210,7 @@ function writeStateAtomic(projectRoot, state) {
15957
16210
  renameSync4(tmp, path);
15958
16211
  renamed = true;
15959
16212
  } finally {
15960
- if (!renamed && existsSync18(tmp)) {
16213
+ if (!renamed && existsSync19(tmp)) {
15961
16214
  try {
15962
16215
  rmSync5(tmp, { force: true });
15963
16216
  } catch {
@@ -16237,8 +16490,8 @@ __export(watch_exports, {
16237
16490
  runWatch: () => runWatch
16238
16491
  });
16239
16492
  import { spawnSync as spawnSync3 } from "child_process";
16240
- import { basename as basename5, dirname as dirname10, resolve as resolve17 } from "path";
16241
- import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync19 } from "fs";
16493
+ import { basename as basename5, dirname as dirname11, resolve as resolve18 } from "path";
16494
+ import { appendFileSync, existsSync as existsSync20, mkdirSync as mkdirSync8, readFileSync as readFileSync19 } from "fs";
16242
16495
  function parseFlags(args2) {
16243
16496
  const out = {
16244
16497
  foreground: false,
@@ -16263,8 +16516,8 @@ function parseFlags(args2) {
16263
16516
  }
16264
16517
  function findClaudeBg() {
16265
16518
  const home = process.env.HOME ?? "";
16266
- const fixed = home ? resolve17(home, ".claude", "bin", "claude-bg") : null;
16267
- if (fixed && existsSync19(fixed)) return fixed;
16519
+ const fixed = home ? resolve18(home, ".claude", "bin", "claude-bg") : null;
16520
+ if (fixed && existsSync20(fixed)) return fixed;
16268
16521
  const which = spawnSync3("which", ["claude-bg"], { encoding: "utf-8" });
16269
16522
  if (which.status === 0 && which.stdout) {
16270
16523
  const p19 = which.stdout.trim();
@@ -16325,7 +16578,7 @@ async function runWatch(args2) {
16325
16578
  }
16326
16579
  function runStatus(root) {
16327
16580
  const path = watchStatePath(root);
16328
- if (!existsSync19(path)) {
16581
+ if (!existsSync20(path)) {
16329
16582
  process.stdout.write("massu watch: not running (no state file)\n");
16330
16583
  return { exitCode: 0 };
16331
16584
  }
@@ -16407,7 +16660,7 @@ async function runForeground(root) {
16407
16660
  }
16408
16661
  throw err;
16409
16662
  }
16410
- return new Promise((resolve40) => {
16663
+ return new Promise((resolve41) => {
16411
16664
  const shutdown = async () => {
16412
16665
  if (stopped) return;
16413
16666
  stopped = true;
@@ -16417,7 +16670,7 @@ async function runForeground(root) {
16417
16670
  process.chdir(priorCwd);
16418
16671
  } catch {
16419
16672
  }
16420
- resolve40({ exitCode: 0 });
16673
+ resolve41({ exitCode: 0 });
16421
16674
  };
16422
16675
  process.on("SIGINT", () => {
16423
16676
  void shutdown();
@@ -16478,19 +16731,19 @@ async function runOnQuiescent(projectRoot) {
16478
16731
  );
16479
16732
  }
16480
16733
  function refreshLogPath(projectRoot) {
16481
- return resolve17(projectRoot, ".massu", "refresh-log.jsonl");
16734
+ return resolve18(projectRoot, ".massu", "refresh-log.jsonl");
16482
16735
  }
16483
16736
  function appendRefreshLog(projectRoot, event) {
16484
16737
  const path = refreshLogPath(projectRoot);
16485
16738
  try {
16486
- mkdirSync8(dirname10(path), { recursive: true });
16739
+ mkdirSync8(dirname11(path), { recursive: true });
16487
16740
  appendFileSync(path, JSON.stringify(event) + "\n", "utf-8");
16488
16741
  } catch {
16489
16742
  }
16490
16743
  }
16491
16744
  function readRefreshLog(projectRoot, limit = 10, opts = {}) {
16492
16745
  const path = refreshLogPath(projectRoot);
16493
- if (!existsSync19(path)) return [];
16746
+ if (!existsSync20(path)) return [];
16494
16747
  const warn = opts.warn ?? ((s) => {
16495
16748
  process.stderr.write(s);
16496
16749
  });
@@ -16567,7 +16820,7 @@ var init_refresh_log = __esm({
16567
16820
  import {
16568
16821
  chmodSync as chmodSync3,
16569
16822
  closeSync as closeSync4,
16570
- existsSync as existsSync20,
16823
+ existsSync as existsSync21,
16571
16824
  fsyncSync as fsyncSync4,
16572
16825
  mkdirSync as mkdirSync9,
16573
16826
  openSync as openSync4,
@@ -16576,12 +16829,12 @@ import {
16576
16829
  statSync as statSync9,
16577
16830
  writeSync as writeSync4
16578
16831
  } from "node:fs";
16579
- import { dirname as dirname11 } from "node:path";
16832
+ import { dirname as dirname12 } from "node:path";
16580
16833
  function atomicWrite(path, content, opts = {}) {
16581
16834
  const tmpPath = `${path}.tmp`;
16582
- const parentDir = dirname11(path);
16835
+ const parentDir = dirname12(path);
16583
16836
  try {
16584
- if (!existsSync20(parentDir)) {
16837
+ if (!existsSync21(parentDir)) {
16585
16838
  const mkdirOpts = { recursive: true };
16586
16839
  if (opts.ensureParentDirMode !== void 0) {
16587
16840
  mkdirOpts.mode = opts.ensureParentDirMode;
@@ -16603,7 +16856,7 @@ function atomicWrite(path, content, opts = {}) {
16603
16856
  renameSync5(tmpPath, path);
16604
16857
  return { written: true };
16605
16858
  } catch (err) {
16606
- if (existsSync20(tmpPath)) {
16859
+ if (existsSync21(tmpPath)) {
16607
16860
  try {
16608
16861
  rmSync6(tmpPath, { force: true });
16609
16862
  } catch {
@@ -16946,19 +17199,19 @@ var init_fetcher = __esm({
16946
17199
  });
16947
17200
 
16948
17201
  // src/security/manifest-cache.ts
16949
- import { existsSync as existsSync21, readFileSync as readFileSync20, statSync as statSync10 } from "node:fs";
17202
+ import { existsSync as existsSync22, readFileSync as readFileSync20, statSync as statSync10 } from "node:fs";
16950
17203
  import { homedir as homedir5 } from "node:os";
16951
- import { resolve as resolve18 } from "node:path";
17204
+ import { resolve as resolve19 } from "node:path";
16952
17205
  import { z as z4 } from "zod";
16953
17206
  function defaultCachePaths() {
16954
- const dir = resolve18(homedir5(), ".massu");
17207
+ const dir = resolve19(homedir5(), ".massu");
16955
17208
  return {
16956
- cachePath: resolve18(dir, "adapter-manifest.json"),
16957
- lockPath: resolve18(dir, ".adapter-manifest.lock")
17209
+ cachePath: resolve19(dir, "adapter-manifest.json"),
17210
+ lockPath: resolve19(dir, ".adapter-manifest.lock")
16958
17211
  };
16959
17212
  }
16960
17213
  function loadCachedManifest(paths = defaultCachePaths()) {
16961
- if (!existsSync21(paths.cachePath)) {
17214
+ if (!existsSync22(paths.cachePath)) {
16962
17215
  return { kind: "absent" };
16963
17216
  }
16964
17217
  let raw;
@@ -17152,15 +17405,15 @@ var init_adapter_origin = __esm({
17152
17405
  });
17153
17406
 
17154
17407
  // src/security/local-fingerprint.ts
17155
- import { existsSync as existsSync22, readFileSync as readFileSync21, lstatSync as lstatSync5 } from "node:fs";
17408
+ import { existsSync as existsSync23, readFileSync as readFileSync21, lstatSync as lstatSync5 } from "node:fs";
17156
17409
  import { homedir as homedir6 } from "node:os";
17157
- import { resolve as resolve19, isAbsolute } from "node:path";
17410
+ import { resolve as resolve20, isAbsolute } from "node:path";
17158
17411
  import { createHash as createHash7 } from "node:crypto";
17159
17412
  import { z as z5 } from "zod";
17160
17413
  function computeLocalFingerprint(localPaths, projectRoot) {
17161
17414
  const tuples = [];
17162
17415
  for (const p19 of localPaths) {
17163
- const abs = isAbsolute(p19) ? p19 : resolve19(projectRoot, p19);
17416
+ const abs = isAbsolute(p19) ? p19 : resolve20(projectRoot, p19);
17164
17417
  let contentTag;
17165
17418
  try {
17166
17419
  const lst = lstatSync5(abs);
@@ -17181,7 +17434,7 @@ function computeLocalFingerprint(localPaths, projectRoot) {
17181
17434
  return createHash7("sha256").update(canonical).digest("hex");
17182
17435
  }
17183
17436
  function readFingerprintSentinel(path = FINGERPRINT_PATH) {
17184
- if (!existsSync22(path)) return null;
17437
+ if (!existsSync23(path)) return null;
17185
17438
  let raw;
17186
17439
  try {
17187
17440
  raw = JSON.parse(readFileSync21(path, "utf-8"));
@@ -17232,7 +17485,7 @@ var init_local_fingerprint = __esm({
17232
17485
  "use strict";
17233
17486
  init_atomic_write();
17234
17487
  init_manifest_schema();
17235
- FINGERPRINT_PATH = resolve19(homedir6(), ".massu", "adapters-local-fingerprint.json");
17488
+ FINGERPRINT_PATH = resolve20(homedir6(), ".massu", "adapters-local-fingerprint.json");
17236
17489
  FingerprintSentinelSchema = z5.object({
17237
17490
  fingerprint: z5.string().regex(/^[0-9a-f]{64}$/),
17238
17491
  source: z5.enum(["cli", "cli-resync"]),
@@ -17248,15 +17501,15 @@ var init_local_fingerprint = __esm({
17248
17501
  });
17249
17502
 
17250
17503
  // src/security/install-tracking.ts
17251
- import { readFileSync as readFileSync22, readdirSync as readdirSync13, lstatSync as lstatSync6, existsSync as existsSync23 } from "node:fs";
17504
+ import { readFileSync as readFileSync22, readdirSync as readdirSync13, lstatSync as lstatSync6, existsSync as existsSync24 } from "node:fs";
17252
17505
  import { join as join11, relative as relative5, sep } from "node:path";
17253
17506
  import { homedir as homedir7 } from "node:os";
17254
- import { resolve as resolve20 } from "node:path";
17507
+ import { resolve as resolve21 } from "node:path";
17255
17508
  import { createHash as createHash8 } from "node:crypto";
17256
17509
  import { z as z6 } from "zod";
17257
17510
  function containsHiddenDirs(packageDir) {
17258
17511
  for (const hidden of EXCLUDED_DIR_NAMES) {
17259
- if (existsSync23(`${packageDir}/${hidden}`)) {
17512
+ if (existsSync24(`${packageDir}/${hidden}`)) {
17260
17513
  return hidden;
17261
17514
  }
17262
17515
  }
@@ -17311,7 +17564,7 @@ function sha256OfDir(dir, opts = {}) {
17311
17564
  return top.digest("hex");
17312
17565
  }
17313
17566
  function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
17314
- if (!existsSync23(path)) return {};
17567
+ if (!existsSync24(path)) return {};
17315
17568
  let raw;
17316
17569
  try {
17317
17570
  raw = JSON.parse(readFileSync22(path, "utf-8"));
@@ -17378,7 +17631,7 @@ var init_install_tracking = __esm({
17378
17631
  "src/security/install-tracking.ts"() {
17379
17632
  "use strict";
17380
17633
  init_atomic_write();
17381
- INSTALLED_MANIFEST_PATH = resolve20(homedir7(), ".massu", "adapter-manifest-installed.json");
17634
+ INSTALLED_MANIFEST_PATH = resolve21(homedir7(), ".massu", "adapter-manifest-installed.json");
17382
17635
  DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
17383
17636
  EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
17384
17637
  InstallEntrySchema = z6.object({
@@ -17401,12 +17654,12 @@ var init_install_tracking = __esm({
17401
17654
  });
17402
17655
 
17403
17656
  // src/detect/adapters/discover.ts
17404
- import { existsSync as existsSync24, readdirSync as readdirSync14, readFileSync as readFileSync23, lstatSync as lstatSync7 } from "node:fs";
17405
- import { resolve as resolve21, isAbsolute as isAbsolute2 } from "node:path";
17657
+ import { existsSync as existsSync25, readdirSync as readdirSync14, readFileSync as readFileSync23, lstatSync as lstatSync7 } from "node:fs";
17658
+ import { resolve as resolve22, isAbsolute as isAbsolute2 } from "node:path";
17406
17659
  import { z as z7 } from "zod";
17407
17660
  function walkNodeModules(projectRoot, warnings) {
17408
- const nodeModulesDir = resolve21(projectRoot, "node_modules");
17409
- if (!existsSync24(nodeModulesDir)) {
17661
+ const nodeModulesDir = resolve22(projectRoot, "node_modules");
17662
+ if (!existsSync25(nodeModulesDir)) {
17410
17663
  return [];
17411
17664
  }
17412
17665
  const candidates = [];
@@ -17419,7 +17672,7 @@ function walkNodeModules(projectRoot, warnings) {
17419
17672
  }
17420
17673
  for (const entry of topLevelEntries) {
17421
17674
  if (entry.startsWith(".")) continue;
17422
- const entryPath = resolve21(nodeModulesDir, entry);
17675
+ const entryPath = resolve22(nodeModulesDir, entry);
17423
17676
  let entryStat;
17424
17677
  try {
17425
17678
  entryStat = lstatSync7(entryPath);
@@ -17441,7 +17694,7 @@ function walkNodeModules(projectRoot, warnings) {
17441
17694
  continue;
17442
17695
  }
17443
17696
  for (const sub of scopedEntries) {
17444
- const subPath = resolve21(entryPath, sub);
17697
+ const subPath = resolve22(entryPath, sub);
17445
17698
  let subStat;
17446
17699
  try {
17447
17700
  subStat = lstatSync7(subPath);
@@ -17460,8 +17713,8 @@ function walkNodeModules(projectRoot, warnings) {
17460
17713
  return candidates;
17461
17714
  }
17462
17715
  function tryReadAdapterPackage(packageDir, warnings) {
17463
- const pkgJsonPath = resolve21(packageDir, "package.json");
17464
- if (!existsSync24(pkgJsonPath)) return null;
17716
+ const pkgJsonPath = resolve22(packageDir, "package.json");
17717
+ if (!existsSync25(pkgJsonPath)) return null;
17465
17718
  let raw;
17466
17719
  try {
17467
17720
  raw = JSON.parse(readFileSync23(pkgJsonPath, "utf-8"));
@@ -17598,8 +17851,8 @@ function discoverAdapters(opts) {
17598
17851
  const localSet = new Set(opts.configLocalPaths);
17599
17852
  for (const localPath of opts.configLocalPaths) {
17600
17853
  if (seenIds.has(localPath)) continue;
17601
- const absPath = isAbsolute2(localPath) ? localPath : resolve21(opts.projectRoot, localPath);
17602
- if (!existsSync24(absPath)) {
17854
+ const absPath = isAbsolute2(localPath) ? localPath : resolve22(opts.projectRoot, localPath);
17855
+ if (!existsSync25(absPath)) {
17603
17856
  warnings.push(
17604
17857
  `local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
17605
17858
  );
@@ -17680,8 +17933,8 @@ __export(adapters_exports, {
17680
17933
  runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
17681
17934
  runAdaptersSearch: () => runAdaptersSearch
17682
17935
  });
17683
- import { existsSync as existsSync25, readFileSync as readFileSync24 } from "node:fs";
17684
- import { resolve as resolve22 } from "node:path";
17936
+ import { existsSync as existsSync26, readFileSync as readFileSync24 } from "node:fs";
17937
+ import { resolve as resolve23 } from "node:path";
17685
17938
  import { parseDocument } from "yaml";
17686
17939
  async function handleAdaptersSubcommand(args2) {
17687
17940
  const sub = args2[0];
@@ -17948,8 +18201,8 @@ function mutateLocalArray(mutator, command) {
17948
18201
  );
17949
18202
  return { exitCode: 2 };
17950
18203
  }
17951
- const yamlPath = resolve22(projectRoot, "massu.config.yaml");
17952
- if (!existsSync25(yamlPath)) {
18204
+ const yamlPath = resolve23(projectRoot, "massu.config.yaml");
18205
+ if (!existsSync26(yamlPath)) {
17953
18206
  process.stderr.write(
17954
18207
  `${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
17955
18208
  `
@@ -17979,7 +18232,7 @@ function mutateLocalArray(mutator, command) {
17979
18232
  if (next === null) {
17980
18233
  return { exitCode: 0 };
17981
18234
  }
17982
- const lockPath = resolve22(projectRoot, ".massu", "adapters-local-mutate.lock");
18235
+ const lockPath = resolve23(projectRoot, ".massu", "adapters-local-mutate.lock");
17983
18236
  return withFileLockSync(lockPath, () => {
17984
18237
  doc.setIn(["adapters", "local"], next);
17985
18238
  const newYaml = doc.toString();
@@ -18048,16 +18301,16 @@ async function runAdaptersInstall(args2) {
18048
18301
  `);
18049
18302
  return { exitCode: 1 };
18050
18303
  }
18051
- const packageDir = resolve22(projectRoot, "node_modules", ...packageName.split("/"));
18052
- if (!existsSync25(packageDir)) {
18304
+ const packageDir = resolve23(projectRoot, "node_modules", ...packageName.split("/"));
18305
+ if (!existsSync26(packageDir)) {
18053
18306
  process.stderr.write(
18054
18307
  `install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
18055
18308
  `
18056
18309
  );
18057
18310
  return { exitCode: 1 };
18058
18311
  }
18059
- const pkgJsonPath = resolve22(packageDir, "package.json");
18060
- if (!existsSync25(pkgJsonPath)) {
18312
+ const pkgJsonPath = resolve23(packageDir, "package.json");
18313
+ if (!existsSync26(pkgJsonPath)) {
18061
18314
  process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
18062
18315
  `);
18063
18316
  return { exitCode: 1 };
@@ -18183,8 +18436,8 @@ async function runAdaptersResign(_args) {
18183
18436
  removeInstalledManifestEntry(name);
18184
18437
  continue;
18185
18438
  }
18186
- const packageDir = resolve22(projectRoot, "node_modules", ...name.split("/"));
18187
- if (!existsSync25(packageDir)) {
18439
+ const packageDir = resolve23(projectRoot, "node_modules", ...name.split("/"));
18440
+ if (!existsSync26(packageDir)) {
18188
18441
  removed++;
18189
18442
  warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
18190
18443
  removeInstalledManifestEntry(name);
@@ -18271,11 +18524,11 @@ var init_adapters2 = __esm({
18271
18524
 
18272
18525
  // src/db.ts
18273
18526
  import Database2 from "better-sqlite3";
18274
- import { dirname as dirname12, join as join12 } from "path";
18275
- import { existsSync as existsSync26, mkdirSync as mkdirSync10, readdirSync as readdirSync15, statSync as statSync11 } from "fs";
18527
+ import { dirname as dirname13, join as join12 } from "path";
18528
+ import { existsSync as existsSync27, mkdirSync as mkdirSync10, readdirSync as readdirSync15, statSync as statSync11 } from "fs";
18276
18529
  function getCodeGraphDb() {
18277
18530
  const dbPath = getResolvedPaths().codegraphDbPath;
18278
- if (!existsSync26(dbPath)) {
18531
+ if (!existsSync27(dbPath)) {
18279
18532
  throw new CodegraphDbNotInitializedError(dbPath);
18280
18533
  }
18281
18534
  const db = new Database2(dbPath, { readonly: true });
@@ -18284,8 +18537,8 @@ function getCodeGraphDb() {
18284
18537
  }
18285
18538
  function getDataDb() {
18286
18539
  const dbPath = getResolvedPaths().dataDbPath;
18287
- const dir = dirname12(dbPath);
18288
- if (!existsSync26(dir)) {
18540
+ const dir = dirname13(dbPath);
18541
+ if (!existsSync27(dir)) {
18289
18542
  mkdirSync10(dir, { recursive: true });
18290
18543
  }
18291
18544
  const db = new Database2(dbPath);
@@ -18592,10 +18845,10 @@ var init_db = __esm({
18592
18845
  });
18593
18846
 
18594
18847
  // src/security-utils.ts
18595
- import { resolve as resolve23, normalize } from "path";
18848
+ import { resolve as resolve24, normalize } from "path";
18596
18849
  function ensureWithinRoot(filePath, projectRoot) {
18597
- const resolvedRoot = resolve23(projectRoot);
18598
- const resolvedPath = resolve23(resolvedRoot, filePath);
18850
+ const resolvedRoot = resolve24(projectRoot);
18851
+ const resolvedPath = resolve24(resolvedRoot, filePath);
18599
18852
  const normalizedPath = normalize(resolvedPath);
18600
18853
  const normalizedRoot = normalize(resolvedRoot);
18601
18854
  if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
@@ -18668,8 +18921,8 @@ var init_rules = __esm({
18668
18921
  });
18669
18922
 
18670
18923
  // src/import-resolver.ts
18671
- import { readFileSync as readFileSync25, existsSync as existsSync27, statSync as statSync12 } from "fs";
18672
- import { resolve as resolve24, dirname as dirname13, join as join13 } from "path";
18924
+ import { readFileSync as readFileSync25, existsSync as existsSync28, statSync as statSync12 } from "fs";
18925
+ import { resolve as resolve25, dirname as dirname14, join as join13 } from "path";
18673
18926
  function parseImports(source) {
18674
18927
  const imports = [];
18675
18928
  const lines = source.split("\n");
@@ -18725,23 +18978,23 @@ function resolveImportPath(specifier, fromFile) {
18725
18978
  let basePath;
18726
18979
  if (specifier.startsWith("@/")) {
18727
18980
  const paths = getResolvedPaths();
18728
- basePath = resolve24(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
18981
+ basePath = resolve25(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
18729
18982
  } else {
18730
- basePath = resolve24(dirname13(fromFile), specifier);
18983
+ basePath = resolve25(dirname14(fromFile), specifier);
18731
18984
  }
18732
- if (existsSync27(basePath) && !isDirectory(basePath)) {
18985
+ if (existsSync28(basePath) && !isDirectory(basePath)) {
18733
18986
  return toRelative(basePath);
18734
18987
  }
18735
18988
  const resolvedPaths = getResolvedPaths();
18736
18989
  for (const ext of resolvedPaths.extensions) {
18737
18990
  const withExt = basePath + ext;
18738
- if (existsSync27(withExt)) {
18991
+ if (existsSync28(withExt)) {
18739
18992
  return toRelative(withExt);
18740
18993
  }
18741
18994
  }
18742
18995
  for (const indexFile of resolvedPaths.indexFiles) {
18743
18996
  const indexPath = join13(basePath, indexFile);
18744
- if (existsSync27(indexPath)) {
18997
+ if (existsSync28(indexPath)) {
18745
18998
  return toRelative(indexPath);
18746
18999
  }
18747
19000
  }
@@ -18777,8 +19030,8 @@ function buildImportIndex(dataDb, codegraphDb) {
18777
19030
  const batchSize = 500;
18778
19031
  let batch = [];
18779
19032
  for (const file of files) {
18780
- const absPath = ensureWithinRoot(resolve24(projectRoot, file.path), projectRoot);
18781
- if (!existsSync27(absPath)) continue;
19033
+ const absPath = ensureWithinRoot(resolve25(projectRoot, file.path), projectRoot);
19034
+ if (!existsSync28(absPath)) continue;
18782
19035
  let source;
18783
19036
  try {
18784
19037
  source = readFileSync25(absPath, "utf-8");
@@ -18817,12 +19070,12 @@ var init_import_resolver = __esm({
18817
19070
  });
18818
19071
 
18819
19072
  // src/trpc-index.ts
18820
- import { readFileSync as readFileSync26, existsSync as existsSync28, readdirSync as readdirSync16 } from "fs";
18821
- import { resolve as resolve25, join as join14 } from "path";
19073
+ import { readFileSync as readFileSync26, existsSync as existsSync29, readdirSync as readdirSync16 } from "fs";
19074
+ import { resolve as resolve26, join as join14 } from "path";
18822
19075
  function parseRootRouter() {
18823
19076
  const paths = getResolvedPaths();
18824
19077
  const rootPath = paths.rootRouterPath;
18825
- if (!existsSync28(rootPath)) {
19078
+ if (!existsSync29(rootPath)) {
18826
19079
  throw new Error(`Root router not found at ${rootPath}`);
18827
19080
  }
18828
19081
  const source = readFileSync26(rootPath, "utf-8");
@@ -18833,16 +19086,16 @@ function parseRootRouter() {
18833
19086
  while ((match = importRegex.exec(source)) !== null) {
18834
19087
  const variable = match[1];
18835
19088
  let filePath = match[2];
18836
- const fullPath = resolve25(paths.routersDir, filePath);
19089
+ const fullPath = resolve26(paths.routersDir, filePath);
18837
19090
  for (const ext of [".ts", ".tsx", ""]) {
18838
19091
  const candidate = fullPath + ext;
18839
19092
  const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
18840
- if (existsSync28(candidate)) {
19093
+ if (existsSync29(candidate)) {
18841
19094
  filePath = routersRelPath + "/" + filePath + ext;
18842
19095
  break;
18843
19096
  }
18844
19097
  const indexCandidate = join14(fullPath, "index.ts");
18845
- if (existsSync28(indexCandidate)) {
19098
+ if (existsSync29(indexCandidate)) {
18846
19099
  filePath = routersRelPath + "/" + filePath + "/index.ts";
18847
19100
  break;
18848
19101
  }
@@ -18861,8 +19114,8 @@ function parseRootRouter() {
18861
19114
  return mappings;
18862
19115
  }
18863
19116
  function extractProcedures(routerFilePath) {
18864
- const absPath = resolve25(getProjectRoot(), routerFilePath);
18865
- if (!existsSync28(absPath)) return [];
19117
+ const absPath = resolve26(getProjectRoot(), routerFilePath);
19118
+ if (!existsSync29(absPath)) return [];
18866
19119
  const source = readFileSync26(absPath, "utf-8");
18867
19120
  const procedures = [];
18868
19121
  const seen = /* @__PURE__ */ new Set();
@@ -18886,13 +19139,13 @@ function findUICallSites(routerKey, procedureName) {
18886
19139
  const root = getProjectRoot();
18887
19140
  const src = config.paths.source;
18888
19141
  const searchDirs = [
18889
- resolve25(root, config.paths.pages ?? src + "/app"),
18890
- resolve25(root, config.paths.components ?? src + "/components"),
18891
- resolve25(root, config.paths.hooks ?? src + "/hooks")
19142
+ resolve26(root, config.paths.pages ?? src + "/app"),
19143
+ resolve26(root, config.paths.components ?? src + "/components"),
19144
+ resolve26(root, config.paths.hooks ?? src + "/hooks")
18892
19145
  ];
18893
19146
  const searchPattern = `api.${routerKey}.${procedureName}`;
18894
19147
  for (const dir of searchDirs) {
18895
- if (!existsSync28(dir)) continue;
19148
+ if (!existsSync29(dir)) continue;
18896
19149
  searchDirectory(dir, searchPattern, callSites);
18897
19150
  }
18898
19151
  return callSites;
@@ -18969,8 +19222,8 @@ var init_trpc_index = __esm({
18969
19222
  });
18970
19223
 
18971
19224
  // src/page-deps.ts
18972
- import { readFileSync as readFileSync27, existsSync as existsSync29 } from "fs";
18973
- import { resolve as resolve26 } from "path";
19225
+ import { readFileSync as readFileSync27, existsSync as existsSync30 } from "fs";
19226
+ import { resolve as resolve27 } from "path";
18974
19227
  function deriveRoute(pageFile) {
18975
19228
  let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
18976
19229
  return route || "/";
@@ -19008,8 +19261,8 @@ function findRouterCalls(files) {
19008
19261
  const routers = /* @__PURE__ */ new Set();
19009
19262
  const projectRoot = getProjectRoot();
19010
19263
  for (const file of files) {
19011
- const absPath = ensureWithinRoot(resolve26(projectRoot, file), projectRoot);
19012
- if (!existsSync29(absPath)) continue;
19264
+ const absPath = ensureWithinRoot(resolve27(projectRoot, file), projectRoot);
19265
+ if (!existsSync30(absPath)) continue;
19013
19266
  try {
19014
19267
  const source = readFileSync27(absPath, "utf-8");
19015
19268
  const apiCallRegex = /api\.(\w+)\.\w+/g;
@@ -19029,8 +19282,8 @@ function findTablesFromRouters(routerNames, dataDb) {
19029
19282
  "SELECT DISTINCT router_file FROM massu_trpc_procedures WHERE router_name = ?"
19030
19283
  ).all(routerName);
19031
19284
  for (const proc of procs) {
19032
- const absPath = ensureWithinRoot(resolve26(getProjectRoot(), proc.router_file), getProjectRoot());
19033
- if (!existsSync29(absPath)) continue;
19285
+ const absPath = ensureWithinRoot(resolve27(getProjectRoot(), proc.router_file), getProjectRoot());
19286
+ if (!existsSync30(absPath)) continue;
19034
19287
  try {
19035
19288
  const source = readFileSync27(absPath, "utf-8");
19036
19289
  const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
@@ -19301,11 +19554,11 @@ var init_domains = __esm({
19301
19554
  });
19302
19555
 
19303
19556
  // src/schema-mapper.ts
19304
- import { readFileSync as readFileSync28, existsSync as existsSync30, readdirSync as readdirSync17 } from "fs";
19557
+ import { readFileSync as readFileSync28, existsSync as existsSync31, readdirSync as readdirSync17 } from "fs";
19305
19558
  import { join as join15 } from "path";
19306
19559
  function parsePrismaSchema() {
19307
19560
  const schemaPath = getResolvedPaths().prismaSchemaPath;
19308
- if (!existsSync30(schemaPath)) {
19561
+ if (!existsSync31(schemaPath)) {
19309
19562
  throw new Error(`Prisma schema not found at ${schemaPath}`);
19310
19563
  }
19311
19564
  const source = readFileSync28(schemaPath, "utf-8");
@@ -19366,7 +19619,7 @@ function toSnakeCase(str) {
19366
19619
  function findColumnUsageInRouters(tableName) {
19367
19620
  const usage = /* @__PURE__ */ new Map();
19368
19621
  const routersDir = getResolvedPaths().routersDir;
19369
- if (!existsSync30(routersDir)) return usage;
19622
+ if (!existsSync31(routersDir)) return usage;
19370
19623
  scanDirectory(routersDir, tableName, usage);
19371
19624
  return usage;
19372
19625
  }
@@ -19428,7 +19681,7 @@ function detectMismatches(models) {
19428
19681
  }
19429
19682
  function findFilesUsingColumn(dir, column, tableName) {
19430
19683
  const result = [];
19431
- if (!existsSync30(dir)) return result;
19684
+ if (!existsSync31(dir)) return result;
19432
19685
  const entries = readdirSync17(dir, { withFileTypes: true });
19433
19686
  for (const entry of entries) {
19434
19687
  const fullPath = join15(dir, entry.name);
@@ -19581,14 +19834,14 @@ var init_import_parser = __esm({
19581
19834
  });
19582
19835
 
19583
19836
  // src/python/import-resolver.ts
19584
- import { readFileSync as readFileSync29, existsSync as existsSync31, readdirSync as readdirSync18 } from "fs";
19585
- import { resolve as resolve28, join as join16, relative as relative6, dirname as dirname14 } from "path";
19837
+ import { readFileSync as readFileSync29, existsSync as existsSync32, readdirSync as readdirSync18 } from "fs";
19838
+ import { resolve as resolve29, join as join16, relative as relative6, dirname as dirname15 } from "path";
19586
19839
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
19587
19840
  const projectRoot = getProjectRoot();
19588
19841
  if (level > 0) {
19589
- let baseDir = dirname14(resolve28(projectRoot, fromFile));
19842
+ let baseDir = dirname15(resolve29(projectRoot, fromFile));
19590
19843
  for (let i = 1; i < level; i++) {
19591
- baseDir = dirname14(baseDir);
19844
+ baseDir = dirname15(baseDir);
19592
19845
  }
19593
19846
  const modulePart = module.replace(/^\.+/, "");
19594
19847
  if (modulePart) {
@@ -19598,17 +19851,17 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
19598
19851
  return tryResolvePythonPath(baseDir, projectRoot);
19599
19852
  }
19600
19853
  const parts = module.split(".");
19601
- const candidate = join16(resolve28(projectRoot, pythonRoot), ...parts);
19854
+ const candidate = join16(resolve29(projectRoot, pythonRoot), ...parts);
19602
19855
  return tryResolvePythonPath(candidate, projectRoot);
19603
19856
  }
19604
19857
  function tryResolvePythonPath(basePath, projectRoot) {
19605
- if (existsSync31(basePath + ".py")) {
19858
+ if (existsSync32(basePath + ".py")) {
19606
19859
  return relative6(projectRoot, basePath + ".py");
19607
19860
  }
19608
- if (existsSync31(join16(basePath, "__init__.py"))) {
19861
+ if (existsSync32(join16(basePath, "__init__.py"))) {
19609
19862
  return relative6(projectRoot, join16(basePath, "__init__.py"));
19610
19863
  }
19611
- if (basePath.endsWith(".py") && existsSync31(basePath)) {
19864
+ if (basePath.endsWith(".py") && existsSync32(basePath)) {
19612
19865
  return relative6(projectRoot, basePath);
19613
19866
  }
19614
19867
  return null;
@@ -19631,7 +19884,7 @@ function walkPythonFiles(dir, excludeDirs) {
19631
19884
  }
19632
19885
  function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
19633
19886
  const projectRoot = getProjectRoot();
19634
- const absRoot = resolve28(projectRoot, pythonRoot);
19887
+ const absRoot = resolve29(projectRoot, pythonRoot);
19635
19888
  dataDb.exec("DELETE FROM massu_py_imports");
19636
19889
  const insertStmt = dataDb.prepare(
19637
19890
  "INSERT INTO massu_py_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)"
@@ -20956,8 +21209,8 @@ var init_memory_tools = __esm({
20956
21209
  });
20957
21210
 
20958
21211
  // src/docs-tools.ts
20959
- import { readFileSync as readFileSync34, existsSync as existsSync32 } from "fs";
20960
- import { resolve as resolve29, basename as basename6 } from "path";
21212
+ import { readFileSync as readFileSync34, existsSync as existsSync33 } from "fs";
21213
+ import { resolve as resolve30, basename as basename6 } from "path";
20961
21214
  function p3(baseName) {
20962
21215
  return `${getConfig().toolPrefix}_${baseName}`;
20963
21216
  }
@@ -21012,7 +21265,7 @@ function handleDocsToolCall(name, args2) {
21012
21265
  }
21013
21266
  function loadDocsMap() {
21014
21267
  const mapPath = getResolvedPaths().docsMapPath;
21015
- if (!existsSync32(mapPath)) {
21268
+ if (!existsSync33(mapPath)) {
21016
21269
  throw new Error(`docs-map.json not found at ${mapPath}`);
21017
21270
  }
21018
21271
  return JSON.parse(readFileSync34(mapPath, "utf-8"));
@@ -21085,10 +21338,10 @@ function extractFrontmatter(content) {
21085
21338
  }
21086
21339
  function extractProcedureNames(routerPath) {
21087
21340
  const root = getProjectRoot();
21088
- const absPath = ensureWithinRoot(resolve29(getResolvedPaths().srcDir, "..", routerPath), root);
21089
- if (!existsSync32(absPath)) {
21090
- const altPath = ensureWithinRoot(resolve29(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
21091
- if (!existsSync32(altPath)) return [];
21341
+ const absPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "..", routerPath), root);
21342
+ if (!existsSync33(absPath)) {
21343
+ const altPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
21344
+ if (!existsSync33(altPath)) return [];
21092
21345
  return extractProcedureNamesFromContent(readFileSync34(altPath, "utf-8"));
21093
21346
  }
21094
21347
  return extractProcedureNamesFromContent(readFileSync34(absPath, "utf-8"));
@@ -21131,8 +21384,8 @@ function handleDocsAudit(args2) {
21131
21384
  for (const [mappingId, triggeringFiles] of affectedMappings) {
21132
21385
  const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
21133
21386
  if (!mapping) continue;
21134
- const helpPagePath = ensureWithinRoot(resolve29(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
21135
- if (!existsSync32(helpPagePath)) {
21387
+ const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
21388
+ if (!existsSync33(helpPagePath)) {
21136
21389
  results.push({
21137
21390
  helpPage: mapping.helpPage,
21138
21391
  mappingId,
@@ -21184,8 +21437,8 @@ function handleDocsAudit(args2) {
21184
21437
  });
21185
21438
  for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
21186
21439
  if (parentId === mappingId) {
21187
- const guidePath = ensureWithinRoot(resolve29(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
21188
- if (existsSync32(guidePath)) {
21440
+ const guidePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
21441
+ if (existsSync33(guidePath)) {
21189
21442
  const guideContent = readFileSync34(guidePath, "utf-8");
21190
21443
  const guideFrontmatter = extractFrontmatter(guideContent);
21191
21444
  if (!guideFrontmatter?.lastVerified || status === "STALE") {
@@ -21219,8 +21472,8 @@ function handleDocsCoverage(args2) {
21219
21472
  const gaps = [];
21220
21473
  const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
21221
21474
  for (const mapping of mappings) {
21222
- const helpPagePath = ensureWithinRoot(resolve29(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
21223
- const exists = existsSync32(helpPagePath);
21475
+ const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
21476
+ const exists = existsSync33(helpPagePath);
21224
21477
  let hasContent = false;
21225
21478
  let lineCount = 0;
21226
21479
  let lastVerified = null;
@@ -21567,8 +21820,8 @@ var init_observability_tools = __esm({
21567
21820
  });
21568
21821
 
21569
21822
  // src/sentinel-db.ts
21570
- import { existsSync as existsSync33 } from "fs";
21571
- import { resolve as resolve30 } from "path";
21823
+ import { existsSync as existsSync34 } from "fs";
21824
+ import { resolve as resolve31 } from "path";
21572
21825
  function parsePortalScope(raw) {
21573
21826
  if (!raw) return [];
21574
21827
  try {
@@ -21804,23 +22057,23 @@ function validateFeatures(db, domainFilter) {
21804
22057
  const missingProcedures = [];
21805
22058
  const missingPages = [];
21806
22059
  for (const comp of components) {
21807
- const absPath = resolve30(PROJECT_ROOT, comp.component_file);
21808
- if (!existsSync33(absPath)) {
22060
+ const absPath = resolve31(PROJECT_ROOT, comp.component_file);
22061
+ if (!existsSync34(absPath)) {
21809
22062
  missingComponents.push(comp.component_file);
21810
22063
  }
21811
22064
  }
21812
22065
  for (const proc of procedures) {
21813
- const routerPath = resolve30(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
21814
- if (!existsSync33(routerPath)) {
22066
+ const routerPath = resolve31(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
22067
+ if (!existsSync34(routerPath)) {
21815
22068
  missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
21816
22069
  }
21817
22070
  }
21818
22071
  for (const page of pages) {
21819
22072
  const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
21820
- const absPath = resolve30(PROJECT_ROOT, routeToPath);
21821
- if (page.page_route.startsWith("/") && !existsSync33(absPath)) {
21822
- const altPath = resolve30(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
21823
- if (!existsSync33(altPath)) {
22073
+ const absPath = resolve31(PROJECT_ROOT, routeToPath);
22074
+ if (page.page_route.startsWith("/") && !existsSync34(absPath)) {
22075
+ const altPath = resolve31(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
22076
+ if (!existsSync34(altPath)) {
21824
22077
  missingPages.push(page.page_route);
21825
22078
  }
21826
22079
  }
@@ -22344,8 +22597,8 @@ var init_sentinel_tools = __esm({
22344
22597
  });
22345
22598
 
22346
22599
  // src/sentinel-scanner.ts
22347
- import { readFileSync as readFileSync35, existsSync as existsSync34, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
22348
- import { resolve as resolve31, join as join21, basename as basename7, dirname as dirname15, relative as relative11 } from "path";
22600
+ import { readFileSync as readFileSync35, existsSync as existsSync35, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
22601
+ import { resolve as resolve32, join as join21, basename as basename7, dirname as dirname16, relative as relative11 } from "path";
22349
22602
  function inferDomain(filePath) {
22350
22603
  const domains = getConfig().domains;
22351
22604
  const path = filePath.toLowerCase();
@@ -22474,8 +22727,8 @@ function scanComponentExports(dataDb) {
22474
22727
  const projectRoot = getProjectRoot();
22475
22728
  const componentsBase = config.paths.components ?? config.paths.source + "/components";
22476
22729
  const componentDirs = [];
22477
- const basePath = resolve31(projectRoot, componentsBase);
22478
- if (existsSync34(basePath)) {
22730
+ const basePath = resolve32(projectRoot, componentsBase);
22731
+ if (existsSync35(basePath)) {
22479
22732
  try {
22480
22733
  const entries = readdirSync23(basePath, { withFileTypes: true });
22481
22734
  for (const entry of entries) {
@@ -22487,8 +22740,8 @@ function scanComponentExports(dataDb) {
22487
22740
  }
22488
22741
  }
22489
22742
  for (const dir of componentDirs) {
22490
- const absDir = resolve31(projectRoot, dir);
22491
- if (!existsSync34(absDir)) continue;
22743
+ const absDir = resolve32(projectRoot, dir);
22744
+ if (!existsSync35(absDir)) continue;
22492
22745
  const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
22493
22746
  for (const file of files) {
22494
22747
  const relPath = relative11(projectRoot, file);
@@ -22518,7 +22771,7 @@ function scanComponentExports(dataDb) {
22518
22771
  if (hasHandlers && exportMatch) {
22519
22772
  const componentName = exportMatch[1];
22520
22773
  const domain = inferDomain(relPath);
22521
- const subdomain = basename7(dirname15(relPath));
22774
+ const subdomain = basename7(dirname16(relPath));
22522
22775
  const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
22523
22776
  if (!annotations.some((a2) => a2.featureKey === featureKey)) {
22524
22777
  features.push({
@@ -23531,7 +23784,7 @@ var init_audit_trail = __esm({
23531
23784
  });
23532
23785
 
23533
23786
  // src/validation-engine.ts
23534
- import { existsSync as existsSync35, readFileSync as readFileSync36 } from "fs";
23787
+ import { existsSync as existsSync36, readFileSync as readFileSync36 } from "fs";
23535
23788
  function p10(baseName) {
23536
23789
  return `${getConfig().toolPrefix}_${baseName}`;
23537
23790
  }
@@ -23562,7 +23815,7 @@ function validateFile(filePath, projectRoot) {
23562
23815
  });
23563
23816
  return checks;
23564
23817
  }
23565
- if (!existsSync35(absPath)) {
23818
+ if (!existsSync36(absPath)) {
23566
23819
  checks.push({
23567
23820
  name: "file_exists",
23568
23821
  severity: "error",
@@ -24016,7 +24269,7 @@ var init_adr_generator = __esm({
24016
24269
  });
24017
24270
 
24018
24271
  // src/security-scorer.ts
24019
- import { existsSync as existsSync36, readFileSync as readFileSync37 } from "fs";
24272
+ import { existsSync as existsSync37, readFileSync as readFileSync37 } from "fs";
24020
24273
  function p12(baseName) {
24021
24274
  return `${getConfig().toolPrefix}_${baseName}`;
24022
24275
  }
@@ -24040,7 +24293,7 @@ function scoreFileSecurity(filePath, projectRoot) {
24040
24293
  }]
24041
24294
  };
24042
24295
  }
24043
- if (!existsSync36(absPath)) {
24296
+ if (!existsSync37(absPath)) {
24044
24297
  return { riskScore: 0, findings: [] };
24045
24298
  }
24046
24299
  let source;
@@ -24334,8 +24587,8 @@ var init_security_scorer = __esm({
24334
24587
  });
24335
24588
 
24336
24589
  // src/dependency-scorer.ts
24337
- import { existsSync as existsSync37, readFileSync as readFileSync38 } from "fs";
24338
- import { resolve as resolve32 } from "path";
24590
+ import { existsSync as existsSync38, readFileSync as readFileSync38 } from "fs";
24591
+ import { resolve as resolve33 } from "path";
24339
24592
  function p13(baseName) {
24340
24593
  return `${getConfig().toolPrefix}_${baseName}`;
24341
24594
  }
@@ -24367,8 +24620,8 @@ function calculateDepRisk(factors) {
24367
24620
  return Math.min(100, risk);
24368
24621
  }
24369
24622
  function getInstalledPackages(projectRoot) {
24370
- const pkgPath = resolve32(projectRoot, "package.json");
24371
- if (!existsSync37(pkgPath)) return /* @__PURE__ */ new Map();
24623
+ const pkgPath = resolve33(projectRoot, "package.json");
24624
+ if (!existsSync38(pkgPath)) return /* @__PURE__ */ new Map();
24372
24625
  try {
24373
24626
  const pkg = JSON.parse(readFileSync38(pkgPath, "utf-8"));
24374
24627
  const packages = /* @__PURE__ */ new Map();
@@ -24973,8 +25226,8 @@ var init_regression_detector = __esm({
24973
25226
 
24974
25227
  // src/knowledge-indexer.ts
24975
25228
  import { createHash as createHash9 } from "crypto";
24976
- import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync38 } from "fs";
24977
- import { resolve as resolve33, relative as relative12, basename as basename8, extname as extname2 } from "path";
25229
+ import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync39 } from "fs";
25230
+ import { resolve as resolve34, relative as relative12, basename as basename8, extname as extname2 } from "path";
24978
25231
  function getKnowledgePaths() {
24979
25232
  const resolved = getResolvedPaths();
24980
25233
  const config = getConfig();
@@ -25002,7 +25255,7 @@ function discoverMarkdownFiles(baseDir) {
25002
25255
  try {
25003
25256
  const entries = readdirSync24(dir, { withFileTypes: true });
25004
25257
  for (const entry of entries) {
25005
- const fullPath = resolve33(dir, entry.name);
25258
+ const fullPath = resolve34(dir, entry.name);
25006
25259
  if (entry.isDirectory()) {
25007
25260
  if (entry.name === "archive" && dir.includes("session-state")) continue;
25008
25261
  if (entry.name === "archive" && dir.includes("status")) continue;
@@ -25280,11 +25533,11 @@ function indexAllKnowledge(db) {
25280
25533
  files.push(...memFiles);
25281
25534
  } catch {
25282
25535
  }
25283
- if (existsSync38(paths.plansDir)) {
25536
+ if (existsSync39(paths.plansDir)) {
25284
25537
  const planFiles = discoverMarkdownFiles(paths.plansDir);
25285
25538
  files.push(...planFiles);
25286
25539
  }
25287
- if (existsSync38(paths.docsDir)) {
25540
+ if (existsSync39(paths.docsDir)) {
25288
25541
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
25289
25542
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
25290
25543
  files.push(...docsFiles);
@@ -25327,7 +25580,7 @@ function indexAllKnowledge(db) {
25327
25580
  } catch {
25328
25581
  }
25329
25582
  for (const filePath of files) {
25330
- if (!existsSync38(filePath)) continue;
25583
+ if (!existsSync39(filePath)) continue;
25331
25584
  const content = readFileSync39(filePath, "utf-8");
25332
25585
  const hash = hashContent2(content);
25333
25586
  const relPath = filePath.startsWith(paths.claudeDir) ? relative12(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative12(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative12(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative12(paths.memoryDir, filePath)}` : basename8(filePath);
@@ -25448,10 +25701,10 @@ function isKnowledgeStale(db) {
25448
25701
  files.push(...discoverMarkdownFiles(paths.memoryDir));
25449
25702
  } catch {
25450
25703
  }
25451
- if (existsSync38(paths.plansDir)) {
25704
+ if (existsSync39(paths.plansDir)) {
25452
25705
  files.push(...discoverMarkdownFiles(paths.plansDir));
25453
25706
  }
25454
- if (existsSync38(paths.docsDir)) {
25707
+ if (existsSync39(paths.docsDir)) {
25455
25708
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
25456
25709
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
25457
25710
  files.push(...docsFiles);
@@ -25481,7 +25734,7 @@ var init_knowledge_indexer = __esm({
25481
25734
 
25482
25735
  // src/knowledge-tools.ts
25483
25736
  import { readFileSync as readFileSync40, writeFileSync as writeFileSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync25 } from "fs";
25484
- import { resolve as resolve34, basename as basename9 } from "path";
25737
+ import { resolve as resolve35, basename as basename9 } from "path";
25485
25738
  function p16(baseName) {
25486
25739
  return `${getConfig().toolPrefix}_${baseName}`;
25487
25740
  }
@@ -26218,7 +26471,7 @@ function handleCorrect(db, args2) {
26218
26471
  if (!wrong || !correction || !rule) {
26219
26472
  return text15("Error: wrong, correction, and rule are all required.");
26220
26473
  }
26221
- const correctionsPath = resolve34(getResolvedPaths().memoryDir, "corrections.md");
26474
+ const correctionsPath = resolve35(getResolvedPaths().memoryDir, "corrections.md");
26222
26475
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
26223
26476
  const title = rule.slice(0, 60);
26224
26477
  const entry = `
@@ -26569,12 +26822,12 @@ var init_knowledge_tools = __esm({
26569
26822
 
26570
26823
  // src/knowledge-db.ts
26571
26824
  import Database3 from "better-sqlite3";
26572
- import { dirname as dirname16 } from "path";
26573
- import { existsSync as existsSync40, mkdirSync as mkdirSync11 } from "fs";
26825
+ import { dirname as dirname17 } from "path";
26826
+ import { existsSync as existsSync41, mkdirSync as mkdirSync11 } from "fs";
26574
26827
  function getKnowledgeDb() {
26575
26828
  const dbPath = getResolvedPaths().knowledgeDbPath;
26576
- const dir = dirname16(dbPath);
26577
- if (!existsSync40(dir)) {
26829
+ const dir = dirname17(dbPath);
26830
+ if (!existsSync41(dir)) {
26578
26831
  mkdirSync11(dir, { recursive: true });
26579
26832
  }
26580
26833
  const db = new Database3(dbPath);
@@ -27308,8 +27561,8 @@ var init_python_tools = __esm({
27308
27561
  });
27309
27562
 
27310
27563
  // src/tools.ts
27311
- import { readFileSync as readFileSync41, existsSync as existsSync41 } from "fs";
27312
- import { resolve as resolve35, basename as basename10 } from "path";
27564
+ import { readFileSync as readFileSync41, existsSync as existsSync42 } from "fs";
27565
+ import { resolve as resolve36, basename as basename10 } from "path";
27313
27566
  function prefix2() {
27314
27567
  return getConfig().toolPrefix;
27315
27568
  }
@@ -27344,7 +27597,7 @@ function ensureIndexes(dataDb, codegraphDb, force = false) {
27344
27597
  if (config.python?.root) {
27345
27598
  const pythonRoot = config.python.root;
27346
27599
  const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
27347
- if (force || isPythonDataStale(dataDb, resolve35(getProjectRoot(), pythonRoot))) {
27600
+ if (force || isPythonDataStale(dataDb, resolve36(getProjectRoot(), pythonRoot))) {
27348
27601
  const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
27349
27602
  results.push(`Python imports: ${pyImports}`);
27350
27603
  const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
@@ -27779,8 +28032,8 @@ function handleContext(file, dataDb, codegraphDb) {
27779
28032
  try {
27780
28033
  const resolvedPaths = getResolvedPaths();
27781
28034
  const root = getProjectRoot();
27782
- const absFilePath = ensureWithinRoot(resolve35(resolvedPaths.srcDir, "..", file), root);
27783
- if (existsSync41(absFilePath)) {
28035
+ const absFilePath = ensureWithinRoot(resolve36(resolvedPaths.srcDir, "..", file), root);
28036
+ if (existsSync42(absFilePath)) {
27784
28037
  const fileContent = readFileSync41(absFilePath, "utf-8").slice(0, 3e3);
27785
28038
  const keywords = [];
27786
28039
  if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
@@ -27970,13 +28223,26 @@ function handleTrpcMap(args2, dataDb) {
27970
28223
  const total = dataDb.prepare("SELECT COUNT(*) as count FROM massu_trpc_procedures").get();
27971
28224
  const coupled = dataDb.prepare("SELECT COUNT(*) as count FROM massu_trpc_procedures WHERE has_ui_caller = 1").get();
27972
28225
  const uncoupled = total.count - coupled.count;
27973
- lines.push("## tRPC Procedure Summary");
27974
- lines.push(`- Total procedures: ${total.count}`);
27975
- lines.push(`- With UI callers: ${coupled.count}`);
27976
- lines.push(`- Without UI callers: ${uncoupled}`);
27977
- lines.push("");
27978
- lines.push('Use { router: "name" } to see details for a specific router.');
27979
- lines.push("Use { uncoupled: true } to see all procedures without UI callers.");
28226
+ if (total.count === 0) {
28227
+ lines.push("## tRPC Index Empty");
28228
+ lines.push("");
28229
+ lines.push("No tRPC procedures indexed yet. Either this repo has no tRPC");
28230
+ lines.push("routers, or the CodeGraph index has not been built. To rebuild:");
28231
+ lines.push("");
28232
+ lines.push(" npx massu sync");
28233
+ lines.push("");
28234
+ lines.push("If `massu sync` completes successfully but `trpc_map` still");
28235
+ lines.push("returns 0, the repo likely has no tRPC procedures (this is");
28236
+ lines.push("expected for non-tRPC stacks \u2014 try `schema` or `domains` tools).");
28237
+ } else {
28238
+ lines.push("## tRPC Procedure Summary");
28239
+ lines.push(`- Total procedures: ${total.count}`);
28240
+ lines.push(`- With UI callers: ${coupled.count}`);
28241
+ lines.push(`- Without UI callers: ${uncoupled}`);
28242
+ lines.push("");
28243
+ lines.push('Use { router: "name" } to see details for a specific router.');
28244
+ lines.push("Use { uncoupled: true } to see all procedures without UI callers.");
28245
+ }
27980
28246
  }
27981
28247
  return text17(lines.join("\n"));
27982
28248
  }
@@ -28205,8 +28471,8 @@ function handleSchema(args2) {
28205
28471
  lines.push("Checking all column references against Prisma schema...");
28206
28472
  lines.push("");
28207
28473
  const projectRoot = getProjectRoot();
28208
- const absPath = ensureWithinRoot(resolve35(projectRoot, file), projectRoot);
28209
- if (!existsSync41(absPath)) {
28474
+ const absPath = ensureWithinRoot(resolve36(projectRoot, file), projectRoot);
28475
+ if (!existsSync42(absPath)) {
28210
28476
  return text17(`File not found: ${file}`);
28211
28477
  }
28212
28478
  const source = readFileSync41(absPath, "utf-8");
@@ -28319,8 +28585,14 @@ var init_tool_db_needs = __esm({
28319
28585
  coupling_check: ["codegraph", "data"],
28320
28586
  impact: ["codegraph", "data"],
28321
28587
  domains: ["codegraph", "data"],
28322
- // `trpc_map` reads only Data DB (tRPC index lives there); no CodeGraph access.
28323
- trpc_map: ["data"],
28588
+ // P-H009 (plan-stage-c-high-batch): `trpc_map` queries Data DB tables
28589
+ // populated by `ensureIndexes(d, codegraphDb)` — without CodeGraph the
28590
+ // index never builds and the flagship code-intel tool silently returns
28591
+ // "0 procedures" on fresh installs. Declaring `codegraph` here makes the
28592
+ // dispatcher open the CodeGraph DB so `buildTrpcIndex` can run; the
28593
+ // handler also surfaces an actionable hint when no procedures + no
28594
+ // codegraph (covered by `trpc-map-empty-codegraph-hint.test.ts`).
28595
+ trpc_map: ["codegraph", "data"],
28324
28596
  // `schema` reads filesystem (Prisma schema files); no DB access at all.
28325
28597
  schema: [],
28326
28598
  // === Memory tools (memory-tools.ts) ===
@@ -28579,8 +28851,8 @@ var init_server_dispatch = __esm({
28579
28851
  // src/server.ts
28580
28852
  var server_exports = {};
28581
28853
  import { readFileSync as readFileSync42 } from "fs";
28582
- import { resolve as resolve36, dirname as dirname17 } from "path";
28583
- import { fileURLToPath as fileURLToPath4 } from "url";
28854
+ import { resolve as resolve37, dirname as dirname18 } from "path";
28855
+ import { fileURLToPath as fileURLToPath5 } from "url";
28584
28856
  function pruneMemoryOnStartup() {
28585
28857
  try {
28586
28858
  const memDb = getMemoryDb();
@@ -28604,17 +28876,17 @@ function pruneMemoryOnStartup() {
28604
28876
  );
28605
28877
  }
28606
28878
  }
28607
- var __dirname4, PKG_VERSION, dispatcher, buffer;
28879
+ var __dirname5, PKG_VERSION, dispatcher, buffer;
28608
28880
  var init_server = __esm({
28609
28881
  "src/server.ts"() {
28610
28882
  "use strict";
28611
28883
  init_memory_db();
28612
28884
  init_license();
28613
28885
  init_server_dispatch();
28614
- __dirname4 = dirname17(fileURLToPath4(import.meta.url));
28886
+ __dirname5 = dirname18(fileURLToPath5(import.meta.url));
28615
28887
  PKG_VERSION = (() => {
28616
28888
  try {
28617
- const pkg = JSON.parse(readFileSync42(resolve36(__dirname4, "..", "package.json"), "utf-8"));
28889
+ const pkg = JSON.parse(readFileSync42(resolve37(__dirname5, "..", "package.json"), "utf-8"));
28618
28890
  return pkg.version ?? "0.0.0";
28619
28891
  } catch {
28620
28892
  return "0.0.0";
@@ -28900,19 +29172,19 @@ var config_upgrade_exports = {};
28900
29172
  __export(config_upgrade_exports, {
28901
29173
  runConfigUpgrade: () => runConfigUpgrade
28902
29174
  });
28903
- import { existsSync as existsSync42, readFileSync as readFileSync43, writeFileSync as writeFileSync6, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
28904
- import { resolve as resolve37 } from "path";
29175
+ import { existsSync as existsSync43, readFileSync as readFileSync43, writeFileSync as writeFileSync6, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
29176
+ import { resolve as resolve38 } from "path";
28905
29177
  import { parse as parseYaml6 } from "yaml";
28906
29178
  async function runConfigUpgrade(opts = {}) {
28907
29179
  const cwd = opts.cwd ?? process.cwd();
28908
- const configPath = resolve37(cwd, "massu.config.yaml");
29180
+ const configPath = resolve38(cwd, "massu.config.yaml");
28909
29181
  const bakPath = `${configPath}.bak`;
28910
29182
  const log = opts.silent ? () => {
28911
29183
  } : (s) => process.stdout.write(s);
28912
29184
  const err = opts.silent ? () => {
28913
29185
  } : (s) => process.stderr.write(s);
28914
29186
  if (opts.rollback) {
28915
- if (!existsSync42(bakPath)) {
29187
+ if (!existsSync43(bakPath)) {
28916
29188
  const message = `No backup found at ${bakPath}`;
28917
29189
  err(message + "\n");
28918
29190
  return { exitCode: 1, action: "none", message };
@@ -28928,7 +29200,7 @@ async function runConfigUpgrade(opts = {}) {
28928
29200
  return { exitCode: 2, action: "none", message };
28929
29201
  }
28930
29202
  }
28931
- if (!existsSync42(configPath)) {
29203
+ if (!existsSync43(configPath)) {
28932
29204
  const message = "massu.config.yaml not found. Run: npx massu init";
28933
29205
  err(message + "\n");
28934
29206
  return { exitCode: 1, action: "none", message };
@@ -28992,8 +29264,8 @@ var config_check_drift_exports = {};
28992
29264
  __export(config_check_drift_exports, {
28993
29265
  runConfigCheckDrift: () => runConfigCheckDrift
28994
29266
  });
28995
- import { existsSync as existsSync43, readFileSync as readFileSync44 } from "fs";
28996
- import { resolve as resolve38 } from "path";
29267
+ import { existsSync as existsSync44, readFileSync as readFileSync44 } from "fs";
29268
+ import { resolve as resolve39 } from "path";
28997
29269
  import { parse as parseYaml7 } from "yaml";
28998
29270
  function renderChanges(changes) {
28999
29271
  if (changes.length === 0) return "(none)\n";
@@ -29001,12 +29273,12 @@ function renderChanges(changes) {
29001
29273
  }
29002
29274
  async function runConfigCheckDrift(opts = {}) {
29003
29275
  const cwd = opts.cwd ?? process.cwd();
29004
- const configPath = resolve38(cwd, "massu.config.yaml");
29276
+ const configPath = resolve39(cwd, "massu.config.yaml");
29005
29277
  const log = opts.silent ? () => {
29006
29278
  } : (s) => process.stdout.write(s);
29007
29279
  const err = opts.silent ? () => {
29008
29280
  } : (s) => process.stderr.write(s);
29009
- if (!existsSync43(configPath)) {
29281
+ if (!existsSync44(configPath)) {
29010
29282
  const message = "massu.config.yaml not found. Run: npx massu init";
29011
29283
  err(message + "\n");
29012
29284
  return {
@@ -29088,10 +29360,10 @@ var init_config_check_drift = __esm({
29088
29360
 
29089
29361
  // src/cli.ts
29090
29362
  import { readFileSync as readFileSync45 } from "fs";
29091
- import { resolve as resolve39, dirname as dirname18 } from "path";
29092
- import { fileURLToPath as fileURLToPath5 } from "url";
29093
- var __filename4 = fileURLToPath5(import.meta.url);
29094
- var __dirname5 = dirname18(__filename4);
29363
+ import { resolve as resolve40, dirname as dirname19 } from "path";
29364
+ import { fileURLToPath as fileURLToPath6 } from "url";
29365
+ var __filename5 = fileURLToPath6(import.meta.url);
29366
+ var __dirname6 = dirname19(__filename5);
29095
29367
  var args = process.argv.slice(2);
29096
29368
  var subcommand = args[0];
29097
29369
  async function main() {
@@ -29111,6 +29383,12 @@ async function main() {
29111
29383
  await runInstallHooks2();
29112
29384
  break;
29113
29385
  }
29386
+ case "hook-runner": {
29387
+ const { runHookRunner: runHookRunner2 } = await Promise.resolve().then(() => (init_hook_runner(), hook_runner_exports));
29388
+ const result = await runHookRunner2(args.slice(1));
29389
+ process.exit(result.exitCode);
29390
+ return;
29391
+ }
29114
29392
  case "install-commands": {
29115
29393
  const { runInstallCommands: runInstallCommands2 } = await Promise.resolve().then(() => (init_install_commands(), install_commands_exports));
29116
29394
  await runInstallCommands2();
@@ -29287,7 +29565,7 @@ Examples:
29287
29565
  }
29288
29566
  function printVersion() {
29289
29567
  try {
29290
- const pkg = JSON.parse(readFileSync45(resolve39(__dirname5, "../package.json"), "utf-8"));
29568
+ const pkg = JSON.parse(readFileSync45(resolve40(__dirname6, "../package.json"), "utf-8"));
29291
29569
  console.log(`massu v${pkg.version}`);
29292
29570
  } catch {
29293
29571
  console.log("massu v0.1.0");