@launchsecure/launch-kit 0.0.28 → 0.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/dist/beacon/beacon.mjs +2759 -1246
  2. package/dist/beacon/beacon.mjs.map +1 -1
  3. package/dist/beacon/beacon.umd.js +710 -95
  4. package/dist/beacon/beacon.umd.js.map +1 -1
  5. package/dist/beacon/types/core.d.ts +14 -0
  6. package/dist/beacon/types/core.d.ts.map +1 -0
  7. package/dist/beacon/types/ctx.d.ts +14 -0
  8. package/dist/beacon/types/ctx.d.ts.map +1 -0
  9. package/dist/beacon/types/element.d.ts +16 -48
  10. package/dist/beacon/types/element.d.ts.map +1 -1
  11. package/dist/beacon/types/index.d.ts +5 -4
  12. package/dist/beacon/types/index.d.ts.map +1 -1
  13. package/dist/beacon/types/internal/annotation-cache.d.ts +10 -0
  14. package/dist/beacon/types/internal/annotation-cache.d.ts.map +1 -0
  15. package/dist/beacon/types/internal/element-capture.d.ts +19 -0
  16. package/dist/beacon/types/internal/element-capture.d.ts.map +1 -0
  17. package/dist/beacon/types/internal/event-buffer.d.ts +16 -0
  18. package/dist/beacon/types/internal/event-buffer.d.ts.map +1 -0
  19. package/dist/beacon/types/internal/framework-detect.d.ts +6 -0
  20. package/dist/beacon/types/internal/framework-detect.d.ts.map +1 -0
  21. package/dist/beacon/types/internal/markers.d.ts +17 -0
  22. package/dist/beacon/types/internal/markers.d.ts.map +1 -0
  23. package/dist/beacon/types/internal/monitor/capture-dom.d.ts +14 -0
  24. package/dist/beacon/types/internal/monitor/capture-dom.d.ts.map +1 -0
  25. package/dist/beacon/types/internal/monitor/capture-network.d.ts +12 -0
  26. package/dist/beacon/types/internal/monitor/capture-network.d.ts.map +1 -0
  27. package/dist/beacon/types/internal/monitor/overlay.d.ts +16 -0
  28. package/dist/beacon/types/internal/monitor/overlay.d.ts.map +1 -0
  29. package/dist/beacon/types/internal/monitor/session.d.ts +41 -0
  30. package/dist/beacon/types/internal/monitor/session.d.ts.map +1 -0
  31. package/dist/beacon/types/{monitor → internal/monitor}/transport.d.ts +3 -3
  32. package/dist/beacon/types/internal/monitor/transport.d.ts.map +1 -0
  33. package/dist/beacon/types/{monitor/types.d.ts → internal/monitor/wire.d.ts} +69 -27
  34. package/dist/beacon/types/internal/monitor/wire.d.ts.map +1 -0
  35. package/dist/beacon/types/{ui → internal}/pick-mode-overlay.d.ts +4 -5
  36. package/dist/beacon/types/internal/pick-mode-overlay.d.ts.map +1 -0
  37. package/dist/beacon/types/{capture → internal}/picker.d.ts +0 -1
  38. package/dist/beacon/types/internal/picker.d.ts.map +1 -0
  39. package/dist/beacon/types/{ui → internal}/pin-popover.d.ts +1 -1
  40. package/dist/beacon/types/internal/pin-popover.d.ts.map +1 -0
  41. package/dist/beacon/types/{capture → internal}/screenshot.d.ts +1 -0
  42. package/dist/beacon/types/internal/screenshot.d.ts.map +1 -0
  43. package/dist/beacon/types/internal/selector.d.ts.map +1 -0
  44. package/dist/beacon/types/plugins/domEle.d.ts +14 -0
  45. package/dist/beacon/types/plugins/domEle.d.ts.map +1 -0
  46. package/dist/beacon/types/plugins/domSS.d.ts +8 -0
  47. package/dist/beacon/types/plugins/domSS.d.ts.map +1 -0
  48. package/dist/beacon/types/plugins/errors.d.ts +3 -0
  49. package/dist/beacon/types/plugins/errors.d.ts.map +1 -0
  50. package/dist/beacon/types/plugins/index.d.ts +8 -0
  51. package/dist/beacon/types/plugins/index.d.ts.map +1 -0
  52. package/dist/beacon/types/plugins/liveMonitor.d.ts +14 -0
  53. package/dist/beacon/types/plugins/liveMonitor.d.ts.map +1 -0
  54. package/dist/beacon/types/plugins/metadata.d.ts +3 -0
  55. package/dist/beacon/types/plugins/metadata.d.ts.map +1 -0
  56. package/dist/beacon/types/registry.d.ts +33 -0
  57. package/dist/beacon/types/registry.d.ts.map +1 -0
  58. package/dist/beacon/types/styles.d.ts +8 -0
  59. package/dist/beacon/types/styles.d.ts.map +1 -0
  60. package/dist/beacon/types/transport.d.ts +3 -0
  61. package/dist/beacon/types/transport.d.ts.map +1 -0
  62. package/dist/beacon/types/types.d.ts +152 -68
  63. package/dist/beacon/types/types.d.ts.map +1 -1
  64. package/dist/beacon/types/ui/dialog.d.ts +53 -0
  65. package/dist/beacon/types/ui/dialog.d.ts.map +1 -0
  66. package/dist/beacon/types/ui/form.d.ts +7 -0
  67. package/dist/beacon/types/ui/form.d.ts.map +1 -0
  68. package/dist/beacon/types/ui/overlay.d.ts +6 -0
  69. package/dist/beacon/types/ui/overlay.d.ts.map +1 -0
  70. package/dist/deck-client/assets/{_baseUniq-W2JQDmje.js → _baseUniq-DCt2IMRR.js} +1 -1
  71. package/dist/deck-client/assets/{arc-DIBWAId9.js → arc-h-ifqmNR.js} +1 -1
  72. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-CAIRMvJK.js → architectureDiagram-Q4EWVU46-C9dITSPv.js} +1 -1
  73. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-BeNaNiOi.js → blockDiagram-DXYQGD6D-BHuJT34t.js} +1 -1
  74. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-B9Ozi62h.js → c4Diagram-AHTNJAMY-CpvMGtDG.js} +1 -1
  75. package/dist/deck-client/assets/channel-2PZVMiXf.js +1 -0
  76. package/dist/deck-client/assets/{chunk-4BX2VUAB-D7AZ47dt.js → chunk-4BX2VUAB-B6md1VIm.js} +1 -1
  77. package/dist/deck-client/assets/{chunk-4TB4RGXK-DnVnNPcI.js → chunk-4TB4RGXK-BmEnX8ik.js} +1 -1
  78. package/dist/deck-client/assets/{chunk-55IACEB6-UKYs-YNd.js → chunk-55IACEB6-BZPUyZAZ.js} +1 -1
  79. package/dist/deck-client/assets/{chunk-EDXVE4YY-D43b-SKn.js → chunk-EDXVE4YY-BWwNUK-l.js} +1 -1
  80. package/dist/deck-client/assets/{chunk-FMBD7UC4-QzBAoyyW.js → chunk-FMBD7UC4-o7gSppGI.js} +1 -1
  81. package/dist/deck-client/assets/{chunk-OYMX7WX6-Cjif4r6W.js → chunk-OYMX7WX6-C4KoTL5p.js} +1 -1
  82. package/dist/deck-client/assets/{chunk-QZHKN3VN-CqLDirEI.js → chunk-QZHKN3VN-jkf68sDs.js} +1 -1
  83. package/dist/deck-client/assets/{chunk-YZCP3GAM-_FQvmMs4.js → chunk-YZCP3GAM-Cd4yBE7o.js} +1 -1
  84. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-Bt8xBAof.js +1 -0
  85. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-Bt8xBAof.js +1 -0
  86. package/dist/deck-client/assets/clone-BHQryoDl.js +1 -0
  87. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-rfrocesE.js → cose-bilkent-S5V4N54A-DeGFUgAV.js} +1 -1
  88. package/dist/deck-client/assets/{dagre-KV5264BT-Bv_7DJat.js → dagre-KV5264BT-ekcYJuUV.js} +1 -1
  89. package/dist/deck-client/assets/{diagram-5BDNPKRD-4F1414G5.js → diagram-5BDNPKRD-YHPk4rV2.js} +1 -1
  90. package/dist/deck-client/assets/{diagram-G4DWMVQ6-C4-Pszqm.js → diagram-G4DWMVQ6-DM-JCd_B.js} +1 -1
  91. package/dist/deck-client/assets/{diagram-MMDJMWI5-B647TIx9.js → diagram-MMDJMWI5-l5FK1ybk.js} +1 -1
  92. package/dist/deck-client/assets/{diagram-TYMM5635-BFAqpezd.js → diagram-TYMM5635-CIN4_1-j.js} +1 -1
  93. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-BfBfrJOC.js → erDiagram-SMLLAGMA-MyinSkEl.js} +1 -1
  94. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-DX9YAYes.js → flowDiagram-DWJPFMVM-Dk8nn42x.js} +1 -1
  95. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-DCuiy7wF.js → ganttDiagram-T4ZO3ILL-BU1ihicu.js} +1 -1
  96. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-CGp1IXUh.js → gitGraphDiagram-UUTBAWPF-BjsTL13C.js} +1 -1
  97. package/dist/deck-client/assets/{graph-B7g8aoxv.js → graph-DJmh-xi7.js} +1 -1
  98. package/dist/deck-client/assets/{index-Dg1r-WSN.js → index-KsShfCV-.js} +3 -3
  99. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-L3fahMkF.js → infoDiagram-42DDH7IO-Dxvy_RB4.js} +1 -1
  100. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-aS_EjWBZ.js → ishikawaDiagram-UXIWVN3A-DPOaNF1l.js} +1 -1
  101. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-djTSQZF9.js → journeyDiagram-VCZTEJTY-DMew3K5c.js} +1 -1
  102. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-CcTHo4CM.js → kanban-definition-6JOO6SKY-csciJFuk.js} +1 -1
  103. package/dist/deck-client/assets/{layout-mEJiadb7.js → layout-Dg4yyms2.js} +1 -1
  104. package/dist/deck-client/assets/{linear-XgTKqyRu.js → linear-BA3zU6gq.js} +1 -1
  105. package/dist/deck-client/assets/{min-Ct9jZdpd.js → min-lz-Ird-p.js} +1 -1
  106. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-BaFxCGNU.js → mindmap-definition-QFDTVHPH-CCEN8OQV.js} +1 -1
  107. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-CIbYYjtw.js → pieDiagram-DEJITSTG-DM6n1HY7.js} +1 -1
  108. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-D9EtCOvh.js → quadrantDiagram-34T5L4WZ-_ULoR66n.js} +1 -1
  109. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-xeni9eVG.js → requirementDiagram-MS252O5E-BuwJs7Tn.js} +1 -1
  110. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-LYeknz9h.js → sankeyDiagram-XADWPNL6-BEsuzkW4.js} +1 -1
  111. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-RDbsKFZf.js → sequenceDiagram-FGHM5R23-CP2H0YWf.js} +1 -1
  112. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-BH1Zjglk.js → stateDiagram-FHFEXIEX-B5Gw_NNL.js} +1 -1
  113. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-4T4wMDXr.js +1 -0
  114. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-IFXxKptt.js → timeline-definition-GMOUNBTQ-DsoYydQa.js} +1 -1
  115. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-D-sLkQs9.js → vennDiagram-DHZGUBPP-Dz8JT_ob.js} +1 -1
  116. package/dist/deck-client/assets/wardley-RL74JXVD-DGHQ_Ijv.js +162 -0
  117. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-BTjjuDU3.js → wardleyDiagram-NUSXRM2D-DN1LJMB1.js} +1 -1
  118. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-AYbv92n-.js → xychartDiagram-5P7HB3ND-nb0oSfrQ.js} +1 -1
  119. package/dist/deck-client/index.html +1 -1
  120. package/dist/server/beacon-monitor-entry.js +548 -6
  121. package/dist/server/chart-serve.js +920 -249
  122. package/dist/server/cli.js +1599 -595
  123. package/dist/server/course-entry.js +3 -3
  124. package/dist/server/graph-mcp-entry.js +1361 -394
  125. package/dist/server/init-entry.js +799 -195
  126. package/dist/server/orbit-entry.js +135 -7
  127. package/dist/server/parse-worker-entry.js +918 -247
  128. package/package.json +3 -2
  129. package/scaffolds/ls-marketplace/.claude-plugin/marketplace.json +4 -4
  130. package/scaffolds/ls-marketplace/plugins/{ls → kit}/.claude-plugin/plugin.json +1 -10
  131. package/scaffolds/ls-marketplace/plugins/{ls → kit}/commands/activate-beacon.md +2 -2
  132. package/scaffolds/ls-marketplace/plugins/kit/commands/activate-statusline.md +46 -0
  133. package/scaffolds/ls-marketplace/plugins/kit/commands/deactivate-statusline.md +34 -0
  134. package/scaffolds/ls-marketplace/plugins/{ls → kit}/commands/standup.md +52 -38
  135. package/scaffolds/ls-marketplace/plugins/kit/skills/beacon-array.md +107 -0
  136. package/scaffolds/ls-marketplace/plugins/kit/skills/beacon-clear.md +94 -0
  137. package/scaffolds/ls-marketplace/plugins/kit/skills/beacon-pulse.md +82 -0
  138. package/scaffolds/ls-marketplace/plugins/kit/skills/beacon-scan.md +66 -0
  139. package/scaffolds/ls-marketplace/plugins/kit/skills/blast-radius.md +101 -0
  140. package/scaffolds/ls-marketplace/plugins/kit/skills/brief.md +112 -0
  141. package/scaffolds/ls-marketplace/plugins/kit/skills/course.md +84 -0
  142. package/scaffolds/ls-marketplace/plugins/kit/skills/debug.md +92 -0
  143. package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check.md +160 -0
  144. package/scaffolds/ls-marketplace/plugins/kit/skills/diagram.md +134 -0
  145. package/scaffolds/ls-marketplace/plugins/kit/skills/orbit.md +87 -0
  146. package/scaffolds/ls-marketplace/plugins/kit/skills/prototype.md +90 -0
  147. package/scaffolds/ls-marketplace/plugins/kit/skills/recall.md +83 -0
  148. package/scaffolds/ls-marketplace/plugins/{ls/commands → kit/skills}/show-mcp-status.md +8 -8
  149. package/scaffolds/ls-marketplace/plugins/kit/skills/wireframe.md +70 -0
  150. package/scaffolds/statusline/statusline-mcp.sh +204 -0
  151. package/scaffolds/statusline/statusline-wrapper.sh +50 -0
  152. package/dist/beacon/types/capture/element.d.ts +0 -3
  153. package/dist/beacon/types/capture/element.d.ts.map +0 -1
  154. package/dist/beacon/types/capture/events.d.ts +0 -20
  155. package/dist/beacon/types/capture/events.d.ts.map +0 -1
  156. package/dist/beacon/types/capture/framework.d.ts +0 -3
  157. package/dist/beacon/types/capture/framework.d.ts.map +0 -1
  158. package/dist/beacon/types/capture/metadata.d.ts +0 -3
  159. package/dist/beacon/types/capture/metadata.d.ts.map +0 -1
  160. package/dist/beacon/types/capture/overlay.d.ts +0 -7
  161. package/dist/beacon/types/capture/overlay.d.ts.map +0 -1
  162. package/dist/beacon/types/capture/picker.d.ts.map +0 -1
  163. package/dist/beacon/types/capture/screenshot.d.ts.map +0 -1
  164. package/dist/beacon/types/capture/selector.d.ts.map +0 -1
  165. package/dist/beacon/types/monitor/dom.d.ts +0 -13
  166. package/dist/beacon/types/monitor/dom.d.ts.map +0 -1
  167. package/dist/beacon/types/monitor/index.d.ts +0 -19
  168. package/dist/beacon/types/monitor/index.d.ts.map +0 -1
  169. package/dist/beacon/types/monitor/network.d.ts +0 -12
  170. package/dist/beacon/types/monitor/network.d.ts.map +0 -1
  171. package/dist/beacon/types/monitor/transport.d.ts.map +0 -1
  172. package/dist/beacon/types/monitor/types.d.ts.map +0 -1
  173. package/dist/beacon/types/transport/submit.d.ts +0 -3
  174. package/dist/beacon/types/transport/submit.d.ts.map +0 -1
  175. package/dist/beacon/types/ui/button.d.ts +0 -2
  176. package/dist/beacon/types/ui/button.d.ts.map +0 -1
  177. package/dist/beacon/types/ui/drawer.d.ts +0 -33
  178. package/dist/beacon/types/ui/drawer.d.ts.map +0 -1
  179. package/dist/beacon/types/ui/icons.d.ts +0 -9
  180. package/dist/beacon/types/ui/icons.d.ts.map +0 -1
  181. package/dist/beacon/types/ui/monitor-panel.d.ts +0 -19
  182. package/dist/beacon/types/ui/monitor-panel.d.ts.map +0 -1
  183. package/dist/beacon/types/ui/pick-mode-overlay.d.ts.map +0 -1
  184. package/dist/beacon/types/ui/pin-popover.d.ts.map +0 -1
  185. package/dist/deck-client/assets/channel-CRdozqbp.js +0 -1
  186. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-lIZMp57W.js +0 -1
  187. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-lIZMp57W.js +0 -1
  188. package/dist/deck-client/assets/clone-BtWeSTyJ.js +0 -1
  189. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BrV78NDR.js +0 -1
  190. package/dist/deck-client/assets/wardley-RL74JXVD-C010F8l4.js +0 -162
  191. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-array.md +0 -92
  192. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-clear.md +0 -68
  193. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-pulse.md +0 -80
  194. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-scan.md +0 -62
  195. /package/dist/beacon/types/{capture → internal}/selector.d.ts +0 -0
@@ -653,7 +653,7 @@ function resolveWorkerPath() {
653
653
  );
654
654
  }
655
655
  function runParseInWorker(req) {
656
- return new Promise((resolve5, reject) => {
656
+ return new Promise((resolve6, reject) => {
657
657
  let workerPath;
658
658
  try {
659
659
  workerPath = resolveWorkerPath();
@@ -672,7 +672,7 @@ function runParseInWorker(req) {
672
672
  };
673
673
  worker.on("message", (reply) => {
674
674
  if (reply.ok) {
675
- finish(() => resolve5({ results: reply.results, failedFiles: reply.failedFiles }));
675
+ finish(() => resolve6({ results: reply.results, failedFiles: reply.failedFiles }));
676
676
  } else {
677
677
  const err2 = new Error(reply.error.message);
678
678
  err2.name = reply.error.name;
@@ -752,9 +752,9 @@ function buildEffectsIndex(layerOutputs) {
752
752
  return idx;
753
753
  }
754
754
  function writeEffectsIndex(rootDir, idx) {
755
- const path3 = (0, import_node_path8.join)(rootDir, LAUNCHSECURE_DIR, "graphs", "effects-index.json");
756
- atomicWriteFileSync(path3, JSON.stringify(idx, null, 2) + "\n");
757
- return path3;
755
+ const path2 = (0, import_node_path8.join)(rootDir, LAUNCHSECURE_DIR, "graphs", "effects-index.json");
756
+ atomicWriteFileSync(path2, JSON.stringify(idx, null, 2) + "\n");
757
+ return path2;
758
758
  }
759
759
  var import_node_path8;
760
760
  var init_effects_index = __esm({
@@ -916,7 +916,7 @@ var init_freshness = __esm({
916
916
  function getAvailableLayers(rootDir) {
917
917
  const dir = (0, import_node_path10.join)(rootDir, GRAPHS_DIR2);
918
918
  if (!(0, import_node_fs8.existsSync)(dir)) return [];
919
- return (0, import_node_fs8.readdirSync)(dir).filter((f) => f.endsWith(".json") && !NON_LAYER_GRAPH_FILES.has(f)).map((f) => f.replace(".json", ""));
919
+ return (0, import_node_fs8.readdirSync)(dir).filter((f) => f.endsWith(".json") && !f.startsWith(".") && !NON_LAYER_GRAPH_FILES.has(f)).map((f) => f.replace(".json", ""));
920
920
  }
921
921
  function graphsDir(rootDir) {
922
922
  return (0, import_node_path10.join)(rootDir, GRAPHS_DIR2);
@@ -1065,10 +1065,10 @@ async function generateGraph(rootDir, layer) {
1065
1065
  return results;
1066
1066
  }
1067
1067
  function readEffectsIndex(rootDir) {
1068
- const path3 = (0, import_node_path10.join)(rootDir, GRAPHS_DIR2, "effects-index.json");
1069
- if (!(0, import_node_fs8.existsSync)(path3)) return null;
1068
+ const path2 = (0, import_node_path10.join)(rootDir, GRAPHS_DIR2, "effects-index.json");
1069
+ if (!(0, import_node_fs8.existsSync)(path2)) return null;
1070
1070
  try {
1071
- return JSON.parse((0, import_node_fs8.readFileSync)(path3, "utf-8"));
1071
+ return JSON.parse((0, import_node_fs8.readFileSync)(path2, "utf-8"));
1072
1072
  } catch {
1073
1073
  return null;
1074
1074
  }
@@ -2354,6 +2354,8 @@ function extractDeep(absPath) {
2354
2354
  false
2355
2355
  );
2356
2356
  const hasEffects = Object.keys(fileEffects).length > 0;
2357
+ const uiLabels = collectUiLabels(root);
2358
+ const notes = collectNotes(root);
2357
2359
  return {
2358
2360
  elements,
2359
2361
  stateVars,
@@ -2361,10 +2363,77 @@ function extractDeep(absPath) {
2361
2363
  variables,
2362
2364
  responses,
2363
2365
  params,
2364
- ...hasEffects ? { effects: fileEffects } : {}
2366
+ ...hasEffects ? { effects: fileEffects } : {},
2367
+ ...uiLabels.length > 0 ? { ui_labels: uiLabels } : {},
2368
+ ...notes.length > 0 ? { notes } : {}
2365
2369
  };
2366
2370
  }
2367
- var import_node_fs11, import_node_path13, tsxLanguage, parserInstance, TreeSitterCtor, initPromise, initialized, queriesDir, queryCache, MAX_PARSEABLE_BYTES, MAX_CONSECUTIVE_PARSE_FAILURES, consecutiveParseFailures, ParseCascadeError, PRISMA_MUTATION_METHODS_BUILTIN, SUPABASE_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods, INLINE_AUTH_IMPORTS, EXEMPT_NAME_PATTERNS, PROTECT_NAME_PATTERNS, TRUST_AS_PROTECT_KEYS, TIMER_FNS, DOM_METHOD_NAMES, CLASSLIST_METHODS, STORAGE_OBJECTS, HISTORY_METHODS, LOCATION_METHODS, ASSIGN_DOM_PROPS;
2371
+ function collectNotes(root) {
2372
+ const out = [];
2373
+ const seen = /* @__PURE__ */ new Set();
2374
+ function visit(node) {
2375
+ if (out.length >= NOTES_MAX) return;
2376
+ if (node.type === "comment") {
2377
+ const text = node.text;
2378
+ const startRow = node.startPosition.row;
2379
+ NOTE_REGEX.lastIndex = 0;
2380
+ let m;
2381
+ while ((m = NOTE_REGEX.exec(text)) !== null) {
2382
+ const kind = m[1];
2383
+ const author = m[2];
2384
+ const body = m[3];
2385
+ if (!kind || !body) continue;
2386
+ const newlinesBefore = (text.slice(0, m.index).match(/\n/g) ?? []).length;
2387
+ const line = startRow + 1 + newlinesBefore;
2388
+ const key = `${line}:${kind}`;
2389
+ if (seen.has(key)) continue;
2390
+ seen.add(key);
2391
+ const note = {
2392
+ kind,
2393
+ text: body.length <= 200 ? body : body.slice(0, 200) + "...",
2394
+ line
2395
+ };
2396
+ if (author) note.author = author;
2397
+ out.push(note);
2398
+ if (out.length >= NOTES_MAX) return;
2399
+ }
2400
+ }
2401
+ for (const child of node.namedChildren) {
2402
+ visit(child);
2403
+ if (out.length >= NOTES_MAX) return;
2404
+ }
2405
+ }
2406
+ visit(root);
2407
+ return out;
2408
+ }
2409
+ function collectUiLabels(root) {
2410
+ const out = [];
2411
+ const seen = /* @__PURE__ */ new Set();
2412
+ function visit(node) {
2413
+ if (out.length >= UI_LABELS_MAX) return;
2414
+ if (node.type === "pair") {
2415
+ const key = node.childForFieldName("key");
2416
+ const val = node.childForFieldName("value");
2417
+ if (key && val) {
2418
+ const keyText = key.type === "property_identifier" ? key.text : stringLiteralValue(key) ?? key.text;
2419
+ if (UI_LABEL_KEYS.has(keyText)) {
2420
+ const strVal = stringLiteralValue(val);
2421
+ if (strVal && strVal.length > 0 && strVal.length <= 200 && !seen.has(strVal)) {
2422
+ seen.add(strVal);
2423
+ out.push(strVal);
2424
+ }
2425
+ }
2426
+ }
2427
+ }
2428
+ for (const child of node.namedChildren) {
2429
+ visit(child);
2430
+ if (out.length >= UI_LABELS_MAX) return;
2431
+ }
2432
+ }
2433
+ visit(root);
2434
+ return out;
2435
+ }
2436
+ var import_node_fs11, import_node_path13, tsxLanguage, parserInstance, TreeSitterCtor, initPromise, initialized, queriesDir, queryCache, MAX_PARSEABLE_BYTES, MAX_CONSECUTIVE_PARSE_FAILURES, consecutiveParseFailures, ParseCascadeError, PRISMA_MUTATION_METHODS_BUILTIN, SUPABASE_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods, INLINE_AUTH_IMPORTS, EXEMPT_NAME_PATTERNS, PROTECT_NAME_PATTERNS, TRUST_AS_PROTECT_KEYS, TIMER_FNS, DOM_METHOD_NAMES, CLASSLIST_METHODS, STORAGE_OBJECTS, HISTORY_METHODS, LOCATION_METHODS, ASSIGN_DOM_PROPS, UI_LABEL_KEYS, UI_LABELS_MAX, NOTE_REGEX, NOTES_MAX;
2368
2437
  var init_ts_extractor = __esm({
2369
2438
  "src/server/graph/core/ts-extractor.ts"() {
2370
2439
  "use strict";
@@ -2483,6 +2552,27 @@ var init_ts_extractor = __esm({
2483
2552
  "selected",
2484
2553
  "disabled"
2485
2554
  ]);
2555
+ UI_LABEL_KEYS = /* @__PURE__ */ new Set([
2556
+ "label",
2557
+ "title",
2558
+ "name",
2559
+ "text",
2560
+ "description",
2561
+ "placeholder",
2562
+ "tooltip",
2563
+ "heading",
2564
+ "subheading",
2565
+ "sheetTitle",
2566
+ "sheetDescription",
2567
+ "caption",
2568
+ "cta",
2569
+ "buttonText",
2570
+ "emptyText",
2571
+ "subtitle"
2572
+ ]);
2573
+ UI_LABELS_MAX = 200;
2574
+ NOTE_REGEX = /^\s*(?:\/\/|\/\*+|\*)\s*([A-Z][A-Z0-9_]{1,15})(?:\(([^)]+)\))?:\s*(\S.*?)\s*(?:\*\/)?$/gm;
2575
+ NOTES_MAX = 100;
2486
2576
  }
2487
2577
  });
2488
2578
 
@@ -2881,6 +2971,7 @@ function generate(rootDir) {
2881
2971
  responses: deep.responses,
2882
2972
  params: deep.params,
2883
2973
  ...deep.effects ? { effects: deep.effects } : {},
2974
+ ...deep.notes ? { notes: deep.notes } : {},
2884
2975
  _dbCalls: dbCalls
2885
2976
  // temp: used for cross-ref building below
2886
2977
  });
@@ -2901,6 +2992,8 @@ function generate(rootDir) {
2901
2992
  conditions: deep.conditions,
2902
2993
  variables: deep.variables,
2903
2994
  ...deep.effects ? { effects: deep.effects } : {},
2995
+ ...deep.ui_labels ? { ui_labels: deep.ui_labels } : {},
2996
+ ...deep.notes ? { notes: deep.notes } : {},
2904
2997
  ...authWrappers.length > 0 ? { auth: authWrappers } : {},
2905
2998
  ...dbCalls.length > 0 ? { _dbCalls: dbCalls } : {}
2906
2999
  });
@@ -3539,241 +3632,6 @@ function pgTypeToPrisma(pgType) {
3539
3632
  const upper = pgType.toUpperCase().trim();
3540
3633
  return PG_TO_PRISMA[upper] ?? upper;
3541
3634
  }
3542
- function bareName(captured) {
3543
- const parts = captured.split(".");
3544
- const last = parts[parts.length - 1];
3545
- return last.replace(/^"(.*)"$/, "$1").trim();
3546
- }
3547
- function parseCreateTable(sql, state) {
3548
- const re = new RegExp(
3549
- `CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${QID})\\s*\\(([\\s\\S]*?)\\);`,
3550
- "gi"
3551
- );
3552
- let m;
3553
- while ((m = re.exec(sql)) !== null) {
3554
- const tableName = bareName(m[1]);
3555
- const body = m[2];
3556
- const columns = /* @__PURE__ */ new Map();
3557
- let primaryCol = null;
3558
- const inlineFks = [];
3559
- const lines = splitTopLevelCommas(body);
3560
- for (const raw of lines) {
3561
- const trimmed = raw.trim().replace(/,\s*$/, "");
3562
- if (!trimmed || trimmed.startsWith("--")) continue;
3563
- const namedPk = trimmed.match(new RegExp(`^CONSTRAINT\\s+${ID}\\s+PRIMARY\\s+KEY\\s*\\(\\s*(${QID})`, "i"));
3564
- if (namedPk) {
3565
- primaryCol = bareName(namedPk[1]);
3566
- continue;
3567
- }
3568
- const tablePk = trimmed.match(new RegExp(`^PRIMARY\\s+KEY\\s*\\(\\s*(${QID})`, "i"));
3569
- if (tablePk) {
3570
- primaryCol = bareName(tablePk[1]);
3571
- continue;
3572
- }
3573
- if (/^UNIQUE\s*\(/i.test(trimmed)) continue;
3574
- const namedFk = trimmed.match(new RegExp(
3575
- `^CONSTRAINT\\s+(${ID})\\s+FOREIGN\\s+KEY\\s*\\(\\s*(${QID})\\s*\\)\\s+REFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
3576
- "i"
3577
- ));
3578
- if (namedFk) {
3579
- inlineFks.push({
3580
- constraintName: bareName(namedFk[1]),
3581
- sourceTable: tableName,
3582
- sourceColumn: bareName(namedFk[2]),
3583
- targetTable: bareName(namedFk[3]),
3584
- targetColumn: bareName(namedFk[4]),
3585
- onDelete: namedFk[5] ?? null
3586
- });
3587
- continue;
3588
- }
3589
- const bareFk = trimmed.match(new RegExp(
3590
- `^FOREIGN\\s+KEY\\s*\\(\\s*(${QID})\\s*\\)\\s+REFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
3591
- "i"
3592
- ));
3593
- if (bareFk) {
3594
- inlineFks.push({
3595
- constraintName: `${tableName}_${bareName(bareFk[1])}_fkey`,
3596
- sourceTable: tableName,
3597
- sourceColumn: bareName(bareFk[1]),
3598
- targetTable: bareName(bareFk[2]),
3599
- targetColumn: bareName(bareFk[3]),
3600
- onDelete: bareFk[4] ?? null
3601
- });
3602
- continue;
3603
- }
3604
- if (/^CONSTRAINT\s/i.test(trimmed)) continue;
3605
- const colMatch = trimmed.match(new RegExp(`^(${ID})\\s+(.+)`, "i"));
3606
- if (!colMatch) continue;
3607
- const colName = bareName(colMatch[1]);
3608
- let rest = colMatch[2];
3609
- const inlineRefMatch = rest.match(new RegExp(
3610
- `\\bREFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
3611
- "i"
3612
- ));
3613
- if (inlineRefMatch) {
3614
- inlineFks.push({
3615
- constraintName: `${tableName}_${colName}_fkey`,
3616
- sourceTable: tableName,
3617
- sourceColumn: colName,
3618
- targetTable: bareName(inlineRefMatch[1]),
3619
- targetColumn: bareName(inlineRefMatch[2]),
3620
- onDelete: inlineRefMatch[3] ?? null
3621
- });
3622
- rest = rest.replace(inlineRefMatch[0], "").trim();
3623
- }
3624
- const isNotNull = /\bNOT\s+NULL\b/i.test(rest);
3625
- const isPrimaryKey = /\bPRIMARY\s+KEY\b/i.test(rest);
3626
- const isUnique = /\bUNIQUE\b/i.test(rest);
3627
- const defaultMatch = rest.match(/\bDEFAULT\s+(.+?)(?:\s*,?\s*$)/i);
3628
- const defaultVal = defaultMatch ? defaultMatch[1].trim() : null;
3629
- let colType = rest.replace(/\bNOT\s+NULL\b/gi, "").replace(/\bPRIMARY\s+KEY\b/gi, "").replace(/\bUNIQUE\b/gi, "").replace(/\bDEFAULT\s+.*/gi, "").trim().replace(/,\s*$/, "").trim();
3630
- columns.set(colName, {
3631
- name: colName,
3632
- type: colType,
3633
- nullable: !isNotNull && !isPrimaryKey,
3634
- primary: isPrimaryKey,
3635
- unique: isUnique,
3636
- default: defaultVal
3637
- });
3638
- if (isPrimaryKey) primaryCol = colName;
3639
- }
3640
- if (primaryCol && columns.has(primaryCol)) {
3641
- columns.get(primaryCol).primary = true;
3642
- }
3643
- state.tables.set(tableName, { name: tableName, columns });
3644
- state.fks.push(...inlineFks);
3645
- }
3646
- }
3647
- function splitTopLevelCommas(body) {
3648
- const out = [];
3649
- let depth = 0;
3650
- let buf = "";
3651
- let inString = null;
3652
- for (const ch of body) {
3653
- if (inString) {
3654
- buf += ch;
3655
- if (ch === inString) inString = null;
3656
- continue;
3657
- }
3658
- if (ch === "'" || ch === '"') {
3659
- inString = ch;
3660
- buf += ch;
3661
- continue;
3662
- }
3663
- if (ch === "(") depth++;
3664
- else if (ch === ")") depth--;
3665
- if (ch === "," && depth === 0) {
3666
- out.push(buf);
3667
- buf = "";
3668
- continue;
3669
- }
3670
- buf += ch;
3671
- }
3672
- if (buf.trim()) out.push(buf);
3673
- return out;
3674
- }
3675
- function parseCreateEnum(sql, state) {
3676
- const re = new RegExp(
3677
- `CREATE\\s+TYPE\\s+(${QID})\\s+AS\\s+ENUM\\s*\\(([^)]+)\\)`,
3678
- "gi"
3679
- );
3680
- let m;
3681
- while ((m = re.exec(sql)) !== null) {
3682
- const enumName = bareName(m[1]);
3683
- const valuesStr = m[2];
3684
- const values = new Set(
3685
- valuesStr.split(",").map((v) => v.trim().replace(/^'(.*)'$/, "$1")).filter(Boolean)
3686
- );
3687
- state.enums.set(enumName, { name: enumName, values });
3688
- }
3689
- }
3690
- function parseAlterTable(sql, state) {
3691
- const addColRe = new RegExp(
3692
- `ALTER\\s+TABLE\\s+(${QID})\\s+ADD\\s+COLUMN\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${QID})\\s+(.+?);`,
3693
- "gi"
3694
- );
3695
- let m;
3696
- while ((m = addColRe.exec(sql)) !== null) {
3697
- const tableName = bareName(m[1]);
3698
- const colName = bareName(m[2]);
3699
- let rest = m[3];
3700
- const table = state.tables.get(tableName);
3701
- if (!table) continue;
3702
- const isNotNull = /\bNOT\s+NULL\b/i.test(rest);
3703
- const defaultMatch = rest.match(/\bDEFAULT\s+(.+?)$/i);
3704
- const defaultVal = defaultMatch ? defaultMatch[1].trim() : null;
3705
- let colType = rest.replace(/\bNOT\s+NULL\b/gi, "").replace(/\bDEFAULT\s+.*/gi, "").trim();
3706
- table.columns.set(colName, {
3707
- name: colName,
3708
- type: colType,
3709
- nullable: !isNotNull,
3710
- primary: false,
3711
- unique: false,
3712
- default: defaultVal
3713
- });
3714
- }
3715
- const dropColRe = new RegExp(
3716
- `ALTER\\s+TABLE\\s+(${QID})\\s+DROP\\s+COLUMN\\s+(?:IF\\s+EXISTS\\s+)?(${QID})`,
3717
- "gi"
3718
- );
3719
- while ((m = dropColRe.exec(sql)) !== null) {
3720
- const table = state.tables.get(bareName(m[1]));
3721
- if (table) table.columns.delete(bareName(m[2]));
3722
- }
3723
- const fkRe = new RegExp(
3724
- `ALTER\\s+TABLE\\s+(${QID})\\s+ADD\\s+CONSTRAINT\\s+(${ID})\\s+FOREIGN\\s+KEY\\s*\\(\\s*(${QID})\\s*\\)\\s+REFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
3725
- "gi"
3726
- );
3727
- while ((m = fkRe.exec(sql)) !== null) {
3728
- state.fks.push({
3729
- constraintName: bareName(m[2]),
3730
- sourceTable: bareName(m[1]),
3731
- sourceColumn: bareName(m[3]),
3732
- targetTable: bareName(m[4]),
3733
- targetColumn: bareName(m[5]),
3734
- onDelete: m[6] ?? null
3735
- });
3736
- }
3737
- }
3738
- function parseAlterEnum(sql, state) {
3739
- const re = new RegExp(
3740
- `ALTER\\s+TYPE\\s+(${QID})\\s+ADD\\s+VALUE\\s+'([^']+)'`,
3741
- "gi"
3742
- );
3743
- let m;
3744
- while ((m = re.exec(sql)) !== null) {
3745
- const en = state.enums.get(bareName(m[1]));
3746
- if (en) en.values.add(m[2]);
3747
- }
3748
- }
3749
- function parseDropTable(sql, state) {
3750
- const re = new RegExp(
3751
- `DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(${QID})`,
3752
- "gi"
3753
- );
3754
- let m;
3755
- while ((m = re.exec(sql)) !== null) {
3756
- const dropped = bareName(m[1]);
3757
- state.tables.delete(dropped);
3758
- state.fks = state.fks.filter((fk) => fk.sourceTable !== dropped && fk.targetTable !== dropped);
3759
- }
3760
- }
3761
- function parseUniqueIndex(sql, state) {
3762
- const re = new RegExp(
3763
- `CREATE\\s+UNIQUE\\s+INDEX\\s+(?:(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:${ID}\\s+)?)?ON\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)`,
3764
- "gi"
3765
- );
3766
- let m;
3767
- while ((m = re.exec(sql)) !== null) {
3768
- const tableName = bareName(m[1]);
3769
- const colName = bareName(m[2]);
3770
- const table = state.tables.get(tableName);
3771
- const col = table?.columns.get(colName);
3772
- if (col) col.unique = true;
3773
- if (!state.uniqueIndexes.has(tableName)) state.uniqueIndexes.set(tableName, /* @__PURE__ */ new Set());
3774
- state.uniqueIndexes.get(tableName).add(colName);
3775
- }
3776
- }
3777
3635
  function discoverMigrationFiles(migrationsDir) {
3778
3636
  if (!(0, import_node_fs14.existsSync)(migrationsDir)) return [];
3779
3637
  const out = [];
@@ -3788,25 +3646,589 @@ function discoverMigrationFiles(migrationsDir) {
3788
3646
  }
3789
3647
  return out;
3790
3648
  }
3791
- function parseMigrations(migrationsDir) {
3649
+ function parseMigrations(migrationsDir, dialect = postgresDialect) {
3792
3650
  const state = {
3793
3651
  tables: /* @__PURE__ */ new Map(),
3794
3652
  enums: /* @__PURE__ */ new Map(),
3795
3653
  fks: [],
3796
- uniqueIndexes: /* @__PURE__ */ new Map()
3654
+ uniqueIndexes: /* @__PURE__ */ new Map(),
3655
+ indexes: [],
3656
+ policies: [],
3657
+ extensions: [],
3658
+ triggers: [],
3659
+ functions: [],
3660
+ views: []
3797
3661
  };
3798
3662
  if (!migrationsDir) return state;
3799
3663
  for (const sqlPath of discoverMigrationFiles(migrationsDir)) {
3800
3664
  const sql = (0, import_node_fs14.readFileSync)(sqlPath, "utf-8");
3801
- parseCreateEnum(sql, state);
3802
- parseCreateTable(sql, state);
3803
- parseAlterTable(sql, state);
3804
- parseAlterEnum(sql, state);
3805
- parseDropTable(sql, state);
3806
- parseUniqueIndex(sql, state);
3665
+ let ast;
3666
+ try {
3667
+ ast = dialect.parse(sql);
3668
+ } catch {
3669
+ continue;
3670
+ }
3671
+ dialect.applyAll(ast, state, sqlPath);
3807
3672
  }
3808
3673
  return state;
3809
3674
  }
3675
+ function extractIndexesFromStmts(stmts, state, filepath) {
3676
+ for (const wrap of stmts) {
3677
+ const stmt = wrap.stmt ?? {};
3678
+ const ix = stmt.IndexStmt;
3679
+ if (!ix) continue;
3680
+ const name = ix.idxname ?? "";
3681
+ if (!name) continue;
3682
+ const table = ix.relation?.relname ?? "";
3683
+ const unique = !!ix.unique;
3684
+ const method = String(ix.accessMethod ?? "btree").toLowerCase();
3685
+ const params = ix.indexParams ?? [];
3686
+ const columns = [];
3687
+ let hasExpressions = false;
3688
+ for (const p of params) {
3689
+ const elem = p.IndexElem;
3690
+ if (!elem) continue;
3691
+ if (elem.name) columns.push(elem.name);
3692
+ else if (elem.expr) hasExpressions = true;
3693
+ }
3694
+ const hasPredicate = !!ix.whereClause;
3695
+ const existing = state.indexes.findIndex((i) => i.name === name);
3696
+ const next = { name, table, unique, method, columns, hasExpressions, hasPredicate, filepath };
3697
+ if (existing >= 0) state.indexes[existing] = next;
3698
+ else state.indexes.push(next);
3699
+ if (unique && columns.length === 1 && !hasPredicate && !hasExpressions) {
3700
+ const t = state.tables.get(table);
3701
+ const col = t?.columns.get(columns[0]);
3702
+ if (col) col.unique = true;
3703
+ if (!state.uniqueIndexes.has(table)) state.uniqueIndexes.set(table, /* @__PURE__ */ new Set());
3704
+ state.uniqueIndexes.get(table).add(columns[0]);
3705
+ }
3706
+ }
3707
+ }
3708
+ function applyDropIndexes(stmts, state) {
3709
+ for (const wrap of stmts) {
3710
+ const drop = wrap.stmt?.DropStmt;
3711
+ if (!drop || drop.removeType !== "OBJECT_INDEX") continue;
3712
+ const objects = drop.objects ?? [];
3713
+ const droppedNames = /* @__PURE__ */ new Set();
3714
+ for (const obj of objects) {
3715
+ const items = obj.List?.items ?? [];
3716
+ const last = items[items.length - 1]?.String?.sval;
3717
+ if (last) droppedNames.add(last);
3718
+ }
3719
+ if (droppedNames.size > 0) {
3720
+ state.indexes = state.indexes.filter((i) => !droppedNames.has(i.name));
3721
+ }
3722
+ }
3723
+ }
3724
+ function formatPgTypeName(typeName) {
3725
+ const names = (typeName?.names ?? []).map((n) => n.String?.sval ?? "").filter(Boolean);
3726
+ const base = (names[names.length - 1] ?? "").toLowerCase();
3727
+ const PG_INTERNAL_MAP = {
3728
+ int4: "INTEGER",
3729
+ int8: "BIGINT",
3730
+ int2: "SMALLINT",
3731
+ float8: "DOUBLE PRECISION",
3732
+ float4: "REAL",
3733
+ bool: "BOOLEAN",
3734
+ bpchar: "CHAR",
3735
+ timestamptz: "TIMESTAMPTZ",
3736
+ timestamp: "TIMESTAMP",
3737
+ numeric: "NUMERIC",
3738
+ text: "TEXT",
3739
+ varchar: "VARCHAR",
3740
+ jsonb: "JSONB",
3741
+ json: "JSON",
3742
+ uuid: "UUID",
3743
+ date: "DATE",
3744
+ bytea: "BYTEA"
3745
+ };
3746
+ return PG_INTERNAL_MAP[base] ?? base.toUpperCase();
3747
+ }
3748
+ function applyAstAlterations(stmts, state) {
3749
+ for (const wrap of stmts) {
3750
+ const stmt = wrap.stmt ?? {};
3751
+ const kind = Object.keys(stmt)[0];
3752
+ if (!kind) continue;
3753
+ if (kind === "AlterTableStmt") {
3754
+ const body = stmt.AlterTableStmt;
3755
+ const tableName = body.relation?.relname ?? "";
3756
+ const table = state.tables.get(tableName);
3757
+ if (!table) continue;
3758
+ const cmds = body.cmds ?? [];
3759
+ for (const c of cmds) {
3760
+ const cmd = c.AlterTableCmd;
3761
+ if (!cmd) continue;
3762
+ const subtype = cmd.subtype ?? "";
3763
+ const colName = cmd.name ?? "";
3764
+ const col = colName ? table.columns.get(colName) : void 0;
3765
+ switch (subtype) {
3766
+ case "AT_AlterColumnType": {
3767
+ if (!col) break;
3768
+ const typeName = cmd.def?.ColumnDef?.typeName ?? cmd.def?.typeName;
3769
+ if (typeName) col.type = formatPgTypeNameWithMods(typeName);
3770
+ break;
3771
+ }
3772
+ case "AT_SetNotNull":
3773
+ if (col) col.nullable = false;
3774
+ break;
3775
+ case "AT_DropNotNull":
3776
+ if (col) col.nullable = true;
3777
+ break;
3778
+ case "AT_AddColumn": {
3779
+ const cd = cmd.def?.ColumnDef;
3780
+ if (!cd) break;
3781
+ const newColName = cd.colname ?? "";
3782
+ if (!newColName) break;
3783
+ if (table.columns.has(newColName)) break;
3784
+ let nullable = true;
3785
+ let primary = false;
3786
+ let unique = false;
3787
+ let defaultVal = null;
3788
+ for (const c2 of cd.constraints ?? []) {
3789
+ const ct = c2.Constraint;
3790
+ if (!ct) continue;
3791
+ if (ct.contype === "CONSTR_NOTNULL") nullable = false;
3792
+ else if (ct.contype === "CONSTR_PRIMARY") {
3793
+ primary = true;
3794
+ nullable = false;
3795
+ } else if (ct.contype === "CONSTR_UNIQUE") unique = true;
3796
+ else if (ct.contype === "CONSTR_DEFAULT") defaultVal = "<expr>";
3797
+ }
3798
+ table.columns.set(newColName, {
3799
+ name: newColName,
3800
+ type: formatPgTypeNameWithMods(cd.typeName),
3801
+ nullable,
3802
+ primary,
3803
+ unique,
3804
+ default: defaultVal
3805
+ });
3806
+ break;
3807
+ }
3808
+ case "AT_DropColumn":
3809
+ if (colName) table.columns.delete(colName);
3810
+ break;
3811
+ case "AT_AddConstraint": {
3812
+ const ct = cmd.def?.Constraint;
3813
+ if (!ct) break;
3814
+ if (ct.contype !== "CONSTR_FOREIGN") break;
3815
+ const fkCols = (ct.fk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
3816
+ const pkCols = (ct.pk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
3817
+ const pkTable = ct.pktable?.relname ?? "";
3818
+ if (fkCols.length && pkCols.length && pkTable) {
3819
+ state.fks.push({
3820
+ constraintName: ct.conname || `${tableName}_${fkCols[0]}_fkey`,
3821
+ sourceTable: tableName,
3822
+ sourceColumn: fkCols[0],
3823
+ targetTable: pkTable,
3824
+ targetColumn: pkCols[0],
3825
+ onDelete: mapFkAction(ct.fk_del_action)
3826
+ });
3827
+ }
3828
+ break;
3829
+ }
3830
+ }
3831
+ }
3832
+ } else if (kind === "RenameStmt") {
3833
+ const body = stmt.RenameStmt;
3834
+ const renameType = body.renameType ?? "";
3835
+ const newName = body.newname ?? "";
3836
+ if (renameType === "OBJECT_COLUMN") {
3837
+ const tableName = body.relation?.relname ?? "";
3838
+ const oldName = body.subname ?? "";
3839
+ const table = state.tables.get(tableName);
3840
+ if (!table || !oldName || !newName) continue;
3841
+ const col = table.columns.get(oldName);
3842
+ if (col) {
3843
+ col.name = newName;
3844
+ table.columns.delete(oldName);
3845
+ table.columns.set(newName, col);
3846
+ }
3847
+ } else if (renameType === "OBJECT_TABLE") {
3848
+ const oldName = body.relation?.relname ?? "";
3849
+ if (!oldName || !newName) continue;
3850
+ const t = state.tables.get(oldName);
3851
+ if (!t) continue;
3852
+ state.tables.delete(oldName);
3853
+ t.name = newName;
3854
+ state.tables.set(newName, t);
3855
+ for (const fk of state.fks) {
3856
+ if (fk.sourceTable === oldName) fk.sourceTable = newName;
3857
+ if (fk.targetTable === oldName) fk.targetTable = newName;
3858
+ }
3859
+ }
3860
+ }
3861
+ }
3862
+ }
3863
+ function extractPoliciesFromStmts(stmts, state, filepath) {
3864
+ for (const wrap of stmts) {
3865
+ const body = wrap.stmt?.CreatePolicyStmt;
3866
+ if (!body) continue;
3867
+ const name = body.policy_name ?? "";
3868
+ if (!name) continue;
3869
+ const table = body.table?.relname ?? "";
3870
+ const cmdRaw = String(body.cmd_name ?? "all").toUpperCase();
3871
+ const command = ["SELECT", "INSERT", "UPDATE", "DELETE", "ALL"].includes(cmdRaw) ? cmdRaw : "ALL";
3872
+ const permissive = body.permissive === true;
3873
+ const roles = (body.roles ?? []).map((r) => {
3874
+ const rs = r.RoleSpec;
3875
+ if (!rs) return "";
3876
+ if (rs.roletype === "ROLESPEC_PUBLIC") return "public";
3877
+ if (rs.roletype === "ROLESPEC_CURRENT_USER") return "current_user";
3878
+ if (rs.roletype === "ROLESPEC_CSTRING" && rs.rolename) return rs.rolename;
3879
+ return "";
3880
+ }).filter(Boolean);
3881
+ const hasUsing = !!body.qual;
3882
+ const hasWithCheck = !!body.with_check;
3883
+ const existing = state.policies.findIndex((p) => p.table === table && p.name === name);
3884
+ const next = { name, table, command, permissive, roles, hasUsing, hasWithCheck, filepath };
3885
+ if (existing >= 0) state.policies[existing] = next;
3886
+ else state.policies.push(next);
3887
+ }
3888
+ }
3889
+ function applyDropPolicies(stmts, state) {
3890
+ for (const wrap of stmts) {
3891
+ const drop = wrap.stmt?.DropStmt;
3892
+ if (!drop || drop.removeType !== "OBJECT_POLICY") continue;
3893
+ const objects = drop.objects ?? [];
3894
+ for (const obj of objects) {
3895
+ const items = obj.List?.items ?? [];
3896
+ if (items.length < 2) continue;
3897
+ const table = items[0]?.String?.sval ?? "";
3898
+ const policyName = items[items.length - 1]?.String?.sval ?? "";
3899
+ if (!table || !policyName) continue;
3900
+ state.policies = state.policies.filter((p) => !(p.table === table && p.name === policyName));
3901
+ }
3902
+ }
3903
+ }
3904
+ function mapFkAction(action) {
3905
+ if (!action) return null;
3906
+ const m = {
3907
+ r: "RESTRICT",
3908
+ c: "CASCADE",
3909
+ s: "SET NULL",
3910
+ d: "SET DEFAULT",
3911
+ a: "NO ACTION",
3912
+ // pgsql-parser may also emit FKCONSTR_ACTION_* enum strings:
3913
+ FKCONSTR_ACTION_RESTRICT: "RESTRICT",
3914
+ FKCONSTR_ACTION_CASCADE: "CASCADE",
3915
+ FKCONSTR_ACTION_SETNULL: "SET NULL",
3916
+ FKCONSTR_ACTION_SETDEFAULT: "SET DEFAULT",
3917
+ FKCONSTR_ACTION_NOACTION: "NO ACTION"
3918
+ };
3919
+ return m[action] ?? null;
3920
+ }
3921
+ function formatPgTypeNameWithMods(typeName) {
3922
+ const base = formatPgTypeName(typeName);
3923
+ if (base === "String" || base === "unknown") return base;
3924
+ const typmods = [];
3925
+ for (const m of typeName?.typmods ?? []) {
3926
+ const v = m.A_Const?.ival?.ival;
3927
+ if (typeof v === "number") typmods.push(v);
3928
+ }
3929
+ return typmods.length ? `${base}(${typmods.join(",")})` : base;
3930
+ }
3931
+ function extractTablesFromStmts(stmts, state) {
3932
+ for (const wrap of stmts) {
3933
+ const body = wrap.stmt?.CreateStmt;
3934
+ if (!body) continue;
3935
+ const tableName = body.relation?.relname ?? "";
3936
+ if (!tableName) continue;
3937
+ const columns = /* @__PURE__ */ new Map();
3938
+ const fks = [];
3939
+ let primaryCol = null;
3940
+ for (const elt of body.tableElts ?? []) {
3941
+ if (elt.ColumnDef) {
3942
+ const cd = elt.ColumnDef;
3943
+ const colName = cd.colname ?? "";
3944
+ if (!colName) continue;
3945
+ const colType = formatPgTypeNameWithMods(cd.typeName);
3946
+ let nullable = true;
3947
+ let primary = false;
3948
+ let unique = false;
3949
+ let defaultVal = null;
3950
+ for (const c of cd.constraints ?? []) {
3951
+ const ct = c.Constraint;
3952
+ if (!ct) continue;
3953
+ switch (ct.contype) {
3954
+ case "CONSTR_NOTNULL":
3955
+ nullable = false;
3956
+ break;
3957
+ case "CONSTR_PRIMARY":
3958
+ primary = true;
3959
+ nullable = false;
3960
+ primaryCol = colName;
3961
+ break;
3962
+ case "CONSTR_UNIQUE":
3963
+ unique = true;
3964
+ break;
3965
+ case "CONSTR_DEFAULT":
3966
+ defaultVal = "<expr>";
3967
+ break;
3968
+ case "CONSTR_FOREIGN": {
3969
+ const pkTable = ct.pktable?.relname ?? "";
3970
+ const pkCols = (ct.pk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
3971
+ if (pkTable && pkCols.length) {
3972
+ fks.push({
3973
+ constraintName: ct.conname || `${tableName}_${colName}_fkey`,
3974
+ sourceTable: tableName,
3975
+ sourceColumn: colName,
3976
+ targetTable: pkTable,
3977
+ targetColumn: pkCols[0],
3978
+ onDelete: mapFkAction(ct.fk_del_action)
3979
+ });
3980
+ }
3981
+ break;
3982
+ }
3983
+ }
3984
+ }
3985
+ columns.set(colName, {
3986
+ name: colName,
3987
+ type: colType,
3988
+ nullable,
3989
+ primary,
3990
+ unique,
3991
+ default: defaultVal
3992
+ });
3993
+ } else if (elt.Constraint) {
3994
+ const ct = elt.Constraint;
3995
+ switch (ct.contype) {
3996
+ case "CONSTR_PRIMARY": {
3997
+ const keys = (ct.keys ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
3998
+ if (keys.length) primaryCol = keys[0];
3999
+ break;
4000
+ }
4001
+ case "CONSTR_FOREIGN": {
4002
+ const fkCols = (ct.fk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
4003
+ const pkCols = (ct.pk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
4004
+ const pkTable = ct.pktable?.relname ?? "";
4005
+ if (fkCols.length && pkCols.length && pkTable) {
4006
+ fks.push({
4007
+ constraintName: ct.conname || `${tableName}_${fkCols[0]}_fkey`,
4008
+ sourceTable: tableName,
4009
+ sourceColumn: fkCols[0],
4010
+ targetTable: pkTable,
4011
+ targetColumn: pkCols[0],
4012
+ onDelete: mapFkAction(ct.fk_del_action)
4013
+ });
4014
+ }
4015
+ break;
4016
+ }
4017
+ }
4018
+ }
4019
+ }
4020
+ if (primaryCol && columns.has(primaryCol)) {
4021
+ columns.get(primaryCol).primary = true;
4022
+ columns.get(primaryCol).nullable = false;
4023
+ }
4024
+ state.tables.set(tableName, { name: tableName, columns });
4025
+ state.fks.push(...fks);
4026
+ }
4027
+ }
4028
+ function extractEnumsFromStmts(stmts, state) {
4029
+ for (const wrap of stmts) {
4030
+ const body = wrap.stmt?.CreateEnumStmt;
4031
+ if (!body) continue;
4032
+ const names = (body.typeName ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
4033
+ const enumName = names[names.length - 1] ?? "";
4034
+ if (!enumName) continue;
4035
+ const vals = new Set(
4036
+ (body.vals ?? []).map((s) => s.String?.sval ?? "").filter(Boolean)
4037
+ );
4038
+ state.enums.set(enumName, { name: enumName, values: vals });
4039
+ }
4040
+ }
4041
+ function applyAstAlterEnums(stmts, state) {
4042
+ for (const wrap of stmts) {
4043
+ const body = wrap.stmt?.AlterEnumStmt;
4044
+ if (!body) continue;
4045
+ const names = (body.typeName ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
4046
+ const enumName = names[names.length - 1] ?? "";
4047
+ const en = state.enums.get(enumName);
4048
+ if (!en) continue;
4049
+ if (body.newVal) en.values.add(String(body.newVal));
4050
+ }
4051
+ }
4052
+ function extractExtensionsFromStmts(stmts, state, filepath) {
4053
+ for (const wrap of stmts) {
4054
+ const body = wrap.stmt?.CreateExtensionStmt;
4055
+ if (!body) continue;
4056
+ const name = body.extname ?? "";
4057
+ if (!name) continue;
4058
+ let schema = null;
4059
+ let version = null;
4060
+ for (const opt of body.options ?? []) {
4061
+ const de = opt.DefElem;
4062
+ if (!de) continue;
4063
+ if (de.defname === "schema" && de.arg?.String?.sval) schema = de.arg.String.sval;
4064
+ else if (de.defname === "new_version" && de.arg?.String?.sval) version = de.arg.String.sval;
4065
+ }
4066
+ const next = { name, schema, version, filepath };
4067
+ const existing = state.extensions.findIndex((e) => e.name === name);
4068
+ if (existing >= 0) state.extensions[existing] = next;
4069
+ else state.extensions.push(next);
4070
+ }
4071
+ }
4072
+ function extractTriggersFromStmts(stmts, state, filepath) {
4073
+ for (const wrap of stmts) {
4074
+ const body = wrap.stmt?.CreateTrigStmt;
4075
+ if (!body) continue;
4076
+ const name = body.trigname ?? "";
4077
+ if (!name) continue;
4078
+ const table = body.relation?.relname ?? "";
4079
+ const timingVal = body.timing ?? 0;
4080
+ const eventsVal = body.events ?? 0;
4081
+ const timing = timingVal & 2 ? "BEFORE" : timingVal & 64 ? "INSTEAD OF" : "AFTER";
4082
+ const events = [];
4083
+ if (eventsVal & 4) events.push("INSERT");
4084
+ if (eventsVal & 8) events.push("DELETE");
4085
+ if (eventsVal & 16) events.push("UPDATE");
4086
+ if (eventsVal & 32) events.push("TRUNCATE");
4087
+ const funcname = body.funcname ?? [];
4088
+ const funcCall = funcname[funcname.length - 1]?.String?.sval ?? "";
4089
+ const forEach = body.row ? "ROW" : "STATEMENT";
4090
+ const hasWhen = !!body.whenClause;
4091
+ const next = { name, table, timing, events, function: funcCall, hasWhen, forEach, filepath };
4092
+ const existing = state.triggers.findIndex((t) => t.table === table && t.name === name);
4093
+ if (existing >= 0) state.triggers[existing] = next;
4094
+ else state.triggers.push(next);
4095
+ }
4096
+ }
4097
+ function functionIdFor(name, schema) {
4098
+ return schema ? `${schema}.${name}` : name;
4099
+ }
4100
+ function extractFunctionsFromStmts(stmts, state, filepath) {
4101
+ for (const wrap of stmts) {
4102
+ const body = wrap.stmt?.CreateFunctionStmt;
4103
+ if (!body) continue;
4104
+ const fn = body.funcname ?? [];
4105
+ if (fn.length === 0) continue;
4106
+ const name = fn[fn.length - 1]?.String?.sval ?? "";
4107
+ if (!name) continue;
4108
+ const schema = fn.length > 1 ? fn[fn.length - 2]?.String?.sval ?? null : null;
4109
+ let language = "sql";
4110
+ for (const opt of body.options ?? []) {
4111
+ const de = opt.DefElem;
4112
+ if (de?.defname === "language" && de.arg?.String?.sval) language = de.arg.String.sval;
4113
+ }
4114
+ const returnType = body.returnType ? formatPgTypeName(body.returnType) : "";
4115
+ const isProcedure = !!body.is_procedure;
4116
+ const next = { name, schema, language, returnType, isProcedure, filepath };
4117
+ const id = functionIdFor(name, schema);
4118
+ const existing = state.functions.findIndex((f) => functionIdFor(f.name, f.schema) === id);
4119
+ if (existing >= 0) state.functions[existing] = next;
4120
+ else state.functions.push(next);
4121
+ }
4122
+ }
4123
+ function extractViewsFromStmts(stmts, state, filepath) {
4124
+ for (const wrap of stmts) {
4125
+ const stmt = wrap.stmt ?? {};
4126
+ const view = stmt.ViewStmt;
4127
+ if (view) {
4128
+ const name = view.view?.relname ?? "";
4129
+ if (!name) continue;
4130
+ const schema = view.view?.schemaname ?? null;
4131
+ const next = {
4132
+ name,
4133
+ schema,
4134
+ isMaterialized: false,
4135
+ withCheckOption: String(view.withCheckOption ?? "NO_CHECK_OPTION"),
4136
+ filepath
4137
+ };
4138
+ const id = functionIdFor(name, schema);
4139
+ const existing = state.views.findIndex((v) => functionIdFor(v.name, v.schema) === id);
4140
+ if (existing >= 0) state.views[existing] = next;
4141
+ else state.views.push(next);
4142
+ }
4143
+ const ctas = stmt.CreateTableAsStmt;
4144
+ if (ctas && ctas.objtype === "OBJECT_MATVIEW") {
4145
+ const name = ctas.into?.rel?.relname ?? "";
4146
+ if (!name) continue;
4147
+ const schema = ctas.into?.rel?.schemaname ?? null;
4148
+ const next = {
4149
+ name,
4150
+ schema,
4151
+ isMaterialized: true,
4152
+ withCheckOption: "N/A",
4153
+ filepath
4154
+ };
4155
+ const id = functionIdFor(name, schema);
4156
+ const existing = state.views.findIndex((v) => functionIdFor(v.name, v.schema) === id);
4157
+ if (existing >= 0) state.views[existing] = next;
4158
+ else state.views.push(next);
4159
+ }
4160
+ }
4161
+ }
4162
+ function applyDropsForSchemaObjects(stmts, state) {
4163
+ for (const wrap of stmts) {
4164
+ const drop = wrap.stmt?.DropStmt;
4165
+ if (!drop) continue;
4166
+ const removeType = drop.removeType ?? "";
4167
+ const objects = drop.objects ?? [];
4168
+ if (removeType === "OBJECT_TABLE") {
4169
+ const droppedTables = /* @__PURE__ */ new Set();
4170
+ for (const obj of objects) {
4171
+ const items = obj.List?.items ?? [];
4172
+ const last = items[items.length - 1]?.String?.sval;
4173
+ if (last) droppedTables.add(last);
4174
+ }
4175
+ if (droppedTables.size > 0) {
4176
+ for (const t of droppedTables) state.tables.delete(t);
4177
+ state.fks = state.fks.filter((fk) => !droppedTables.has(fk.sourceTable) && !droppedTables.has(fk.targetTable));
4178
+ }
4179
+ } else if (removeType === "OBJECT_TYPE") {
4180
+ const droppedEnums = /* @__PURE__ */ new Set();
4181
+ for (const obj of objects) {
4182
+ const tn = obj.TypeName;
4183
+ if (!tn) continue;
4184
+ const names = (tn.names ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
4185
+ const last = names[names.length - 1];
4186
+ if (last) droppedEnums.add(last);
4187
+ }
4188
+ for (const e of droppedEnums) state.enums.delete(e);
4189
+ } else if (removeType === "OBJECT_EXTENSION") {
4190
+ const names = /* @__PURE__ */ new Set();
4191
+ for (const obj of objects) {
4192
+ const sval = obj.String?.sval;
4193
+ if (sval) names.add(sval);
4194
+ }
4195
+ if (names.size > 0) state.extensions = state.extensions.filter((e) => !names.has(e.name));
4196
+ } else if (removeType === "OBJECT_TRIGGER") {
4197
+ for (const obj of objects) {
4198
+ const items = obj.List?.items ?? [];
4199
+ if (items.length < 2) continue;
4200
+ const table = items[0]?.String?.sval ?? "";
4201
+ const trigName = items[items.length - 1]?.String?.sval ?? "";
4202
+ if (!table || !trigName) continue;
4203
+ state.triggers = state.triggers.filter((t) => !(t.table === table && t.name === trigName));
4204
+ }
4205
+ } else if (removeType === "OBJECT_FUNCTION" || removeType === "OBJECT_PROCEDURE") {
4206
+ for (const obj of objects) {
4207
+ const items = obj.ObjectWithArgs?.objname?.items ?? obj.ObjectWithArgs?.objname ?? obj.List?.items ?? [];
4208
+ if (!items.length) continue;
4209
+ const segs = items.map((s) => s.String?.sval ?? "").filter(Boolean);
4210
+ if (!segs.length) continue;
4211
+ const name = segs[segs.length - 1];
4212
+ const schema = segs.length > 1 ? segs[segs.length - 2] : null;
4213
+ const id = functionIdFor(name, schema);
4214
+ state.functions = state.functions.filter((f) => functionIdFor(f.name, f.schema) !== id);
4215
+ }
4216
+ } else if (removeType === "OBJECT_VIEW" || removeType === "OBJECT_MATVIEW") {
4217
+ for (const obj of objects) {
4218
+ const items = obj.List?.items ?? [];
4219
+ if (!items.length) continue;
4220
+ const name = items[items.length - 1]?.String?.sval ?? "";
4221
+ const schema = items.length > 1 ? items[items.length - 2]?.String?.sval ?? null : null;
4222
+ if (!name) continue;
4223
+ const id = functionIdFor(name, schema);
4224
+ state.views = state.views.filter((v) => functionIdFor(v.name, v.schema) !== id);
4225
+ }
4226
+ }
4227
+ }
4228
+ }
4229
+ function indexIsPrismaUncoverable(idx) {
4230
+ return idx.hasPredicate || idx.hasExpressions || idx.method !== "btree";
4231
+ }
3810
4232
  function loadPrismaState(schemaPath) {
3811
4233
  if (!schemaPath || !(0, import_node_fs14.existsSync)(schemaPath)) return null;
3812
4234
  const content = (0, import_node_fs14.readFileSync)(schemaPath, "utf-8");
@@ -3973,6 +4395,96 @@ function verify(sqlState, prisma) {
3973
4395
  }
3974
4396
  return { contradictions, flaggedEdges };
3975
4397
  }
4398
+ function deriveMigrationName(sqlPath) {
4399
+ const segments = sqlPath.split(/[\\/]/);
4400
+ const last = segments[segments.length - 1];
4401
+ if (last === "migration.sql" && segments.length >= 2) {
4402
+ return segments[segments.length - 2];
4403
+ }
4404
+ return last.replace(/\.sql$/, "");
4405
+ }
4406
+ function extractMigrationInfoFromStmts(stmts, name, filepath) {
4407
+ let isDestructive = false;
4408
+ let hasOrphanCheck = false;
4409
+ let hasSidecarBackup = false;
4410
+ let hasPreFlightNotice = false;
4411
+ let containsBackfill = false;
4412
+ let containsDropColumn = false;
4413
+ let containsDropTable = false;
4414
+ for (const wrap of stmts) {
4415
+ const stmt = wrap.stmt ?? {};
4416
+ const kind = Object.keys(stmt)[0];
4417
+ if (!kind) continue;
4418
+ const body = stmt[kind] ?? {};
4419
+ switch (kind) {
4420
+ case "AlterTableStmt": {
4421
+ const cmds = body.cmds ?? [];
4422
+ for (const c of cmds) {
4423
+ const subtype = c.AlterTableCmd?.subtype;
4424
+ if (subtype === "AT_DropColumn") {
4425
+ containsDropColumn = true;
4426
+ isDestructive = true;
4427
+ } else if (subtype === "AT_AlterColumnType" || subtype === "AT_DropNotNull" || subtype === "AT_DropConstraint") {
4428
+ isDestructive = true;
4429
+ }
4430
+ }
4431
+ break;
4432
+ }
4433
+ case "DropStmt": {
4434
+ const removeType = body.removeType ?? "";
4435
+ if (removeType === "OBJECT_TABLE") {
4436
+ containsDropTable = true;
4437
+ isDestructive = true;
4438
+ } else if (removeType === "OBJECT_TYPE" || removeType === "OBJECT_COLUMN" || removeType === "OBJECT_INDEX" || removeType === "OBJECT_POLICY") {
4439
+ isDestructive = true;
4440
+ }
4441
+ break;
4442
+ }
4443
+ case "CreateStmt": {
4444
+ const relname = body.relation?.relname ?? "";
4445
+ if (relname.startsWith("_backup_")) hasSidecarBackup = true;
4446
+ break;
4447
+ }
4448
+ case "CreateTableAsStmt": {
4449
+ const relname = body.into?.rel?.relname ?? "";
4450
+ if (relname.startsWith("_backup_")) hasSidecarBackup = true;
4451
+ break;
4452
+ }
4453
+ case "UpdateStmt":
4454
+ case "InsertStmt":
4455
+ case "DeleteStmt": {
4456
+ containsBackfill = true;
4457
+ break;
4458
+ }
4459
+ case "DoStmt": {
4460
+ const args = body.args ?? [];
4461
+ for (const arg of args) {
4462
+ const def = arg.DefElem;
4463
+ if (!def || def.defname !== "as") continue;
4464
+ const code = def.arg?.String?.sval ?? "";
4465
+ if (/\bRAISE\s+EXCEPTION\b/i.test(code)) hasOrphanCheck = true;
4466
+ if (/\bRAISE\s+NOTICE\b/i.test(code)) hasPreFlightNotice = true;
4467
+ }
4468
+ break;
4469
+ }
4470
+ }
4471
+ }
4472
+ const tsMatch = name.match(/^(\d{8,14})/);
4473
+ const timestamp = tsMatch ? tsMatch[1] : null;
4474
+ return {
4475
+ name,
4476
+ filepath,
4477
+ timestamp,
4478
+ isDestructive,
4479
+ hasOrphanCheck,
4480
+ hasSidecarBackup,
4481
+ hasPreFlightNotice,
4482
+ containsBackfill,
4483
+ containsDropColumn,
4484
+ containsDropTable,
4485
+ statementCount: stmts.length
4486
+ };
4487
+ }
3976
4488
  function migrationsDirFor(rootDir) {
3977
4489
  const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
3978
4490
  if (!paths) return null;
@@ -4027,6 +4539,132 @@ function generate3(rootDir) {
4027
4539
  values: [...sqlEnum.values]
4028
4540
  });
4029
4541
  }
4542
+ let indexNodeCount = 0;
4543
+ for (const idx of sqlState.indexes) {
4544
+ if (!indexIsPrismaUncoverable(idx)) continue;
4545
+ nodes.push({
4546
+ id: `index:${idx.name}`,
4547
+ type: "index",
4548
+ name: idx.name,
4549
+ source: "sql",
4550
+ table: idx.table,
4551
+ unique: idx.unique,
4552
+ method: idx.method,
4553
+ columns: idx.columns,
4554
+ has_expressions: idx.hasExpressions,
4555
+ has_predicate: idx.hasPredicate,
4556
+ filepath: idx.filepath
4557
+ });
4558
+ indexNodeCount++;
4559
+ }
4560
+ let extensionNodeCount = 0;
4561
+ for (const ext of sqlState.extensions) {
4562
+ nodes.push({
4563
+ id: `extension:${ext.name}`,
4564
+ type: "extension",
4565
+ name: ext.name,
4566
+ source: "sql",
4567
+ schema: ext.schema,
4568
+ version: ext.version,
4569
+ filepath: ext.filepath
4570
+ });
4571
+ extensionNodeCount++;
4572
+ }
4573
+ let triggerNodeCount = 0;
4574
+ for (const trg of sqlState.triggers) {
4575
+ nodes.push({
4576
+ id: `trigger:${trg.table}:${trg.name}`,
4577
+ type: "trigger",
4578
+ name: trg.name,
4579
+ source: "sql",
4580
+ table: trg.table,
4581
+ timing: trg.timing,
4582
+ events: trg.events,
4583
+ function: trg.function,
4584
+ has_when: trg.hasWhen,
4585
+ for_each: trg.forEach,
4586
+ filepath: trg.filepath
4587
+ });
4588
+ triggerNodeCount++;
4589
+ }
4590
+ let functionNodeCount = 0;
4591
+ for (const fn of sqlState.functions) {
4592
+ const qualified = fn.schema ? `${fn.schema}.${fn.name}` : fn.name;
4593
+ nodes.push({
4594
+ id: `function:${qualified}`,
4595
+ type: "function",
4596
+ name: fn.name,
4597
+ source: "sql",
4598
+ schema: fn.schema,
4599
+ language: fn.language,
4600
+ return_type: fn.returnType,
4601
+ is_procedure: fn.isProcedure,
4602
+ filepath: fn.filepath
4603
+ });
4604
+ functionNodeCount++;
4605
+ }
4606
+ let viewNodeCount = 0;
4607
+ for (const vw of sqlState.views) {
4608
+ const qualified = vw.schema ? `${vw.schema}.${vw.name}` : vw.name;
4609
+ nodes.push({
4610
+ id: `${vw.isMaterialized ? "matview" : "view"}:${qualified}`,
4611
+ type: vw.isMaterialized ? "materialized_view" : "view",
4612
+ name: vw.name,
4613
+ source: "sql",
4614
+ schema: vw.schema,
4615
+ is_materialized: vw.isMaterialized,
4616
+ with_check_option: vw.withCheckOption,
4617
+ filepath: vw.filepath
4618
+ });
4619
+ viewNodeCount++;
4620
+ }
4621
+ let policyNodeCount = 0;
4622
+ for (const pol of sqlState.policies) {
4623
+ nodes.push({
4624
+ id: `policy:${pol.table}:${pol.name}`,
4625
+ type: "policy",
4626
+ name: pol.name,
4627
+ source: "sql",
4628
+ table: pol.table,
4629
+ command: pol.command,
4630
+ permissive: pol.permissive,
4631
+ roles: pol.roles,
4632
+ has_using: pol.hasUsing,
4633
+ has_with_check: pol.hasWithCheck,
4634
+ filepath: pol.filepath
4635
+ });
4636
+ policyNodeCount++;
4637
+ }
4638
+ const migrationFiles = migrationsDir ? discoverMigrationFiles(migrationsDir) : [];
4639
+ let migrationNodeCount = 0;
4640
+ for (const sqlPath of migrationFiles) {
4641
+ const sql = (0, import_node_fs14.readFileSync)(sqlPath, "utf-8");
4642
+ const name = deriveMigrationName(sqlPath);
4643
+ let ast;
4644
+ try {
4645
+ ast = postgresDialect.parse(sql);
4646
+ } catch {
4647
+ ast = { stmts: [] };
4648
+ }
4649
+ const info = postgresDialect.extractMigrationInfo(ast, name, sqlPath);
4650
+ nodes.push({
4651
+ id: `migration:${name}`,
4652
+ type: "migration",
4653
+ name,
4654
+ source: "sql",
4655
+ filepath: info.filepath,
4656
+ timestamp: info.timestamp,
4657
+ is_destructive: info.isDestructive,
4658
+ has_orphan_check: info.hasOrphanCheck,
4659
+ has_sidecar_backup: info.hasSidecarBackup,
4660
+ has_pre_flight_notice: info.hasPreFlightNotice,
4661
+ contains_backfill: info.containsBackfill,
4662
+ contains_drop_column: info.containsDropColumn,
4663
+ contains_drop_table: info.containsDropTable,
4664
+ statement_count: info.statementCount
4665
+ });
4666
+ migrationNodeCount++;
4667
+ }
4030
4668
  const sqlOnlyTables = new Set(nodes.filter((n) => n.type === "table").map((n) => n.id));
4031
4669
  const edges = sqlState.fks.filter((fk) => sqlOnlyTables.has(fk.sourceTable)).map((fk) => ({
4032
4670
  source: fk.sourceTable,
@@ -4045,6 +4683,13 @@ function generate3(rootDir) {
4045
4683
  sql_tables: sqlState.tables.size,
4046
4684
  sql_enums: sqlState.enums.size,
4047
4685
  sql_fks: sqlState.fks.length,
4686
+ sql_index_nodes: indexNodeCount,
4687
+ sql_policy_nodes: policyNodeCount,
4688
+ sql_extension_nodes: extensionNodeCount,
4689
+ sql_trigger_nodes: triggerNodeCount,
4690
+ sql_function_nodes: functionNodeCount,
4691
+ sql_view_nodes: viewNodeCount,
4692
+ sql_migration_nodes: migrationNodeCount,
4048
4693
  additive_nodes: nodes.length,
4049
4694
  contradictions_found: contradictions.length,
4050
4695
  flagged_edges_found: flaggedEdges.length
@@ -4057,12 +4702,13 @@ function generate3(rootDir) {
4057
4702
  flagged_edges: flaggedEdges
4058
4703
  };
4059
4704
  }
4060
- var import_node_fs14, import_node_path15, PG_TO_PRISMA, ID, QID, sqlMigrationsParser;
4705
+ var import_node_fs14, import_node_path15, import_pgsql_parser, PG_TO_PRISMA, postgresDialect, sqlMigrationsParser;
4061
4706
  var init_sql_migrations = __esm({
4062
4707
  "src/server/graph/parsers/db/sql-migrations.ts"() {
4063
4708
  "use strict";
4064
4709
  import_node_fs14 = require("node:fs");
4065
4710
  import_node_path15 = require("node:path");
4711
+ import_pgsql_parser = require("pgsql-parser");
4066
4712
  init_config();
4067
4713
  init_resolve_paths();
4068
4714
  PG_TO_PRISMA = {
@@ -4091,8 +4737,31 @@ var init_sql_migrations = __esm({
4091
4737
  "UUID": "String",
4092
4738
  "TEXT[]": "String[]"
4093
4739
  };
4094
- ID = `(?:"[\\w$]+"|[\\w$]+)`;
4095
- QID = `(?:${ID}\\.)?${ID}`;
4740
+ postgresDialect = {
4741
+ parse(sql) {
4742
+ return (0, import_pgsql_parser.parseSync)(sql);
4743
+ },
4744
+ applyAll(ast, state, filepath) {
4745
+ const stmts = ast.stmts ?? [];
4746
+ extractTablesFromStmts(stmts, state);
4747
+ extractEnumsFromStmts(stmts, state);
4748
+ extractIndexesFromStmts(stmts, state, filepath);
4749
+ extractPoliciesFromStmts(stmts, state, filepath);
4750
+ extractExtensionsFromStmts(stmts, state, filepath);
4751
+ extractTriggersFromStmts(stmts, state, filepath);
4752
+ extractFunctionsFromStmts(stmts, state, filepath);
4753
+ extractViewsFromStmts(stmts, state, filepath);
4754
+ applyDropIndexes(stmts, state);
4755
+ applyDropPolicies(stmts, state);
4756
+ applyDropsForSchemaObjects(stmts, state);
4757
+ applyAstAlterEnums(stmts, state);
4758
+ applyAstAlterations(stmts, state);
4759
+ },
4760
+ extractMigrationInfo(ast, name, filepath) {
4761
+ const stmts = ast.stmts ?? [];
4762
+ return extractMigrationInfoFromStmts(stmts, name, filepath);
4763
+ }
4764
+ };
4096
4765
  sqlMigrationsParser = {
4097
4766
  id: "sql-migrations",
4098
4767
  layer: "db",
@@ -4106,12 +4775,12 @@ var init_sql_migrations = __esm({
4106
4775
  function loadApiRoutesFromOutput(apiOutput) {
4107
4776
  const routes = [];
4108
4777
  for (const n of apiOutput.nodes) {
4109
- const path3 = n.path;
4110
- if (!path3 || typeof path3 !== "string") continue;
4778
+ const path2 = n.path;
4779
+ if (!path2 || typeof path2 !== "string") continue;
4111
4780
  routes.push({
4112
- path: path3,
4781
+ path: path2,
4113
4782
  nodeId: n.id,
4114
- segments: path3.split("/").filter(Boolean)
4783
+ segments: path2.split("/").filter(Boolean)
4115
4784
  });
4116
4785
  }
4117
4786
  return routes;
@@ -4203,16 +4872,16 @@ function resolveFetchCall(call, apiPathMap, apiRoutes) {
4203
4872
  if (call.isConcat) {
4204
4873
  return { kind: "dynamic", normalizedUrl: raw };
4205
4874
  }
4206
- const { path: path3, hadInterpolation } = normalizeFetchUrl(raw);
4207
- if (!path3.startsWith("/")) {
4208
- return { kind: "unresolved", normalizedUrl: path3 };
4875
+ const { path: path2, hadInterpolation } = normalizeFetchUrl(raw);
4876
+ if (!path2.startsWith("/")) {
4877
+ return { kind: "unresolved", normalizedUrl: path2 };
4209
4878
  }
4210
- const segs = path3.split("/").filter(Boolean);
4879
+ const segs = path2.split("/").filter(Boolean);
4211
4880
  if (hadInterpolation && segs.length > 0 && segs[0].startsWith(":")) {
4212
- return { kind: "dynamic", normalizedUrl: path3 };
4881
+ return { kind: "dynamic", normalizedUrl: path2 };
4213
4882
  }
4214
- const exact = apiPathMap.get(path3);
4215
- if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path3 };
4883
+ const exact = apiPathMap.get(path2);
4884
+ if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path2 };
4216
4885
  let bestScore = -1;
4217
4886
  let bestId = null;
4218
4887
  for (const r of apiRoutes) {
@@ -4223,21 +4892,21 @@ function resolveFetchCall(call, apiPathMap, apiRoutes) {
4223
4892
  }
4224
4893
  }
4225
4894
  if (bestId && bestScore > 0) {
4226
- return { kind: "resolved", nodeId: bestId, normalizedUrl: path3 };
4895
+ return { kind: "resolved", nodeId: bestId, normalizedUrl: path2 };
4227
4896
  }
4228
- return { kind: "unresolved", normalizedUrl: path3 };
4897
+ return { kind: "unresolved", normalizedUrl: path2 };
4229
4898
  }
4230
4899
  function resolveUrlPath(urlPath, apiPathMap, apiRoutes) {
4231
- const { path: path3, hadInterpolation } = normalizeFetchUrl(urlPath);
4232
- if (!path3.startsWith("/")) {
4233
- return { kind: "unresolved", normalizedUrl: path3 };
4900
+ const { path: path2, hadInterpolation } = normalizeFetchUrl(urlPath);
4901
+ if (!path2.startsWith("/")) {
4902
+ return { kind: "unresolved", normalizedUrl: path2 };
4234
4903
  }
4235
- const segs = path3.split("/").filter(Boolean);
4904
+ const segs = path2.split("/").filter(Boolean);
4236
4905
  if (hadInterpolation && segs.length > 0 && segs[0].startsWith(":")) {
4237
- return { kind: "dynamic", normalizedUrl: path3 };
4906
+ return { kind: "dynamic", normalizedUrl: path2 };
4238
4907
  }
4239
- const exact = apiPathMap.get(path3);
4240
- if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path3 };
4908
+ const exact = apiPathMap.get(path2);
4909
+ if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path2 };
4241
4910
  let bestScore = -1;
4242
4911
  let bestId = null;
4243
4912
  for (const r of apiRoutes) {
@@ -4248,9 +4917,9 @@ function resolveUrlPath(urlPath, apiPathMap, apiRoutes) {
4248
4917
  }
4249
4918
  }
4250
4919
  if (bestId && bestScore > 0) {
4251
- return { kind: "resolved", nodeId: bestId, normalizedUrl: path3 };
4920
+ return { kind: "resolved", nodeId: bestId, normalizedUrl: path2 };
4252
4921
  }
4253
- return { kind: "unresolved", normalizedUrl: path3 };
4922
+ return { kind: "unresolved", normalizedUrl: path2 };
4254
4923
  }
4255
4924
  var init_api_route_matching = __esm({
4256
4925
  "src/server/graph/core/api-route-matching.ts"() {
@@ -5271,9 +5940,9 @@ function collectTargets(apiOutput, uiOutput) {
5271
5940
  const out = [];
5272
5941
  for (const n of apiOutput?.nodes ?? []) {
5273
5942
  if (n.type !== "endpoint") continue;
5274
- const path3 = n.path;
5275
- if (typeof path3 !== "string" || !path3) continue;
5276
- out.push({ id: n.id, route: path3, layer: "api" });
5943
+ const path2 = n.path;
5944
+ if (typeof path2 !== "string" || !path2) continue;
5945
+ out.push({ id: n.id, route: path2, layer: "api" });
5277
5946
  }
5278
5947
  for (const n of uiOutput?.nodes ?? []) {
5279
5948
  if (n.type !== "page") continue;
@@ -6270,14 +6939,14 @@ function serveIndex(res, clientDir) {
6270
6939
  serveStatic(res, indexPath);
6271
6940
  }
6272
6941
  function tryListen(server, port) {
6273
- return new Promise((resolve5, reject) => {
6942
+ return new Promise((resolve6, reject) => {
6274
6943
  const onError = (err2) => {
6275
6944
  server.off("listening", onListening);
6276
6945
  reject(err2);
6277
6946
  };
6278
6947
  const onListening = () => {
6279
6948
  server.off("error", onError);
6280
- resolve5(port);
6949
+ resolve6(port);
6281
6950
  };
6282
6951
  server.once("error", onError);
6283
6952
  server.once("listening", onListening);
@@ -6446,8 +7115,10 @@ async function startChartServer(opts = {}) {
6446
7115
  req.on("end", () => {
6447
7116
  try {
6448
7117
  const newConfig = JSON.parse(body);
7118
+ const existingConfig = loadConfig(reqRoot);
7119
+ const merged = { ...existingConfig, ...newConfig };
6449
7120
  const configPath = import_node_path24.default.join(reqRoot, LAUNCHCHART_CONFIG_FILE);
6450
- import_node_fs21.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
7121
+ import_node_fs21.default.writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
6451
7122
  res.writeHead(200, { "Content-Type": "application/json" });
6452
7123
  res.end(JSON.stringify({ ok: true }));
6453
7124
  } catch (err2) {
@@ -6715,6 +7386,73 @@ var init_chart_serve = __esm({
6715
7386
  }
6716
7387
  });
6717
7388
 
7389
+ // src/server/orbit/registry.ts
7390
+ function emptyRegistry() {
7391
+ return { version: 1, worktrees: {} };
7392
+ }
7393
+ function readRegistry() {
7394
+ if (!(0, import_node_fs22.existsSync)(REGISTRY_PATH)) return emptyRegistry();
7395
+ try {
7396
+ const parsed = JSON.parse((0, import_node_fs22.readFileSync)(REGISTRY_PATH, "utf-8"));
7397
+ if (parsed?.version === 1 && parsed.worktrees && typeof parsed.worktrees === "object") {
7398
+ return parsed;
7399
+ }
7400
+ } catch {
7401
+ }
7402
+ return emptyRegistry();
7403
+ }
7404
+ function listWorktrees() {
7405
+ return Object.values(readRegistry().worktrees);
7406
+ }
7407
+ var import_node_fs22, import_node_os2, import_node_path25, REGISTRY_DIR, REGISTRY_PATH, LOCK_PATH;
7408
+ var init_registry = __esm({
7409
+ "src/server/orbit/registry.ts"() {
7410
+ "use strict";
7411
+ import_node_fs22 = require("node:fs");
7412
+ import_node_os2 = require("node:os");
7413
+ import_node_path25 = require("node:path");
7414
+ init_launch_kit_paths();
7415
+ REGISTRY_DIR = (0, import_node_path25.join)((0, import_node_os2.homedir)(), LAUNCHSECURE_DIR, "orbit");
7416
+ REGISTRY_PATH = (0, import_node_path25.join)(REGISTRY_DIR, "state.json");
7417
+ LOCK_PATH = (0, import_node_path25.join)(REGISTRY_DIR, "state.json.lock");
7418
+ }
7419
+ });
7420
+
7421
+ // src/server/lib/worktree.ts
7422
+ function resolveWorktreeRoot(slug, monorepoRoot) {
7423
+ const local = listWorktrees().filter((w) => w.projectRoot === monorepoRoot);
7424
+ const match = local.find((w) => w.slug === slug);
7425
+ if (match) return match.path;
7426
+ if (local.length === 0) {
7427
+ throw new Error(
7428
+ `worktree="${slug}" requested but no worktrees are registered for this project. Run \`launch-orbit create <branch>\` first, or use \`project_root\` to point at an arbitrary path.`
7429
+ );
7430
+ }
7431
+ const available = local.map((w) => `"${w.slug}" (${w.branch})`).join(", ");
7432
+ throw new Error(`Unknown worktree "${slug}". Available: ${available}.`);
7433
+ }
7434
+ function resolveWorktreeOrProjectRoot(args, monorepoRoot) {
7435
+ const projectRoot = typeof args.project_root === "string" ? args.project_root.trim() : "";
7436
+ if (projectRoot) {
7437
+ return (0, import_node_path26.isAbsolute)(projectRoot) ? projectRoot : (0, import_node_path26.resolve)(monorepoRoot, projectRoot);
7438
+ }
7439
+ const worktree = typeof args.worktree === "string" ? args.worktree.trim() : "";
7440
+ if (worktree) {
7441
+ return resolveWorktreeRoot(worktree, monorepoRoot);
7442
+ }
7443
+ return null;
7444
+ }
7445
+ var import_node_path26, WORKTREE_PARAM_DESCRIPTION, PROJECT_ROOT_PARAM_DESCRIPTION;
7446
+ var init_worktree = __esm({
7447
+ "src/server/lib/worktree.ts"() {
7448
+ "use strict";
7449
+ import_node_path26 = require("node:path");
7450
+ init_registry();
7451
+ WORKTREE_PARAM_DESCRIPTION = "Optional orbit worktree slug (from `launch-orbit create`). Resolves to the worktree's path via the orbit registry (~/.launchsecure/orbit/state.json). Lets you query a worktree's state from a Claude Code session pinned to the main repo. Superseded by `project_root`.";
7452
+ PROJECT_ROOT_PARAM_DESCRIPTION = "Optional explicit project root. Accepts an absolute path or a path relative to the monorepo root. Escape hatch when `worktree` doesn't fit. Takes precedence over all other root args.";
7453
+ }
7454
+ });
7455
+
6718
7456
  // src/server/graph/core/projects.ts
6719
7457
  function listProjects(monorepoRoot) {
6720
7458
  const cfg = loadConfig(monorepoRoot);
@@ -6722,7 +7460,7 @@ function listProjects(monorepoRoot) {
6722
7460
  return entries.map((p) => ({
6723
7461
  name: p.name,
6724
7462
  root: p.root,
6725
- absoluteRoot: (0, import_node_path25.resolve)(monorepoRoot, p.root)
7463
+ absoluteRoot: (0, import_node_path27.resolve)(monorepoRoot, p.root)
6726
7464
  }));
6727
7465
  }
6728
7466
  function resolveProject(name, projects) {
@@ -6744,12 +7482,21 @@ function resolveProjectRoot(project, monorepoRoot) {
6744
7482
  }
6745
7483
  return resolveProject(raw, projects).absoluteRoot;
6746
7484
  }
6747
- var import_node_path25, PROJECT_PARAM_DESCRIPTION;
7485
+ function resolveRequestRoot2(args, monorepoRoot) {
7486
+ const fromArgs = resolveWorktreeOrProjectRoot(args, monorepoRoot);
7487
+ if (fromArgs) return fromArgs;
7488
+ const project = typeof args.project === "string" ? args.project : void 0;
7489
+ return resolveProjectRoot(project, monorepoRoot);
7490
+ }
7491
+ var import_node_path27, WORKTREE_PARAM_DESCRIPTION2, PROJECT_ROOT_PARAM_DESCRIPTION2, PROJECT_PARAM_DESCRIPTION;
6748
7492
  var init_projects = __esm({
6749
7493
  "src/server/graph/core/projects.ts"() {
6750
7494
  "use strict";
6751
- import_node_path25 = require("node:path");
7495
+ import_node_path27 = require("node:path");
7496
+ init_worktree();
6752
7497
  init_config();
7498
+ WORKTREE_PARAM_DESCRIPTION2 = WORKTREE_PARAM_DESCRIPTION;
7499
+ PROJECT_ROOT_PARAM_DESCRIPTION2 = PROJECT_ROOT_PARAM_DESCRIPTION;
6753
7500
  PROJECT_PARAM_DESCRIPTION = "Optional sub-project name (or root path) from .launchchart.json projects[]. Defaults to the monorepo root. Run detect_project_stack to list configured projects.";
6754
7501
  }
6755
7502
  });
@@ -6888,10 +7635,10 @@ var init_graph_cli = __esm({
6888
7635
  // src/server/graph/core/language-detection.ts
6889
7636
  function walkForExtensions(dir, extCounts, depth = 0) {
6890
7637
  if (depth > 10) return;
6891
- if (!(0, import_node_fs22.existsSync)(dir)) return;
7638
+ if (!(0, import_node_fs23.existsSync)(dir)) return;
6892
7639
  let entries;
6893
7640
  try {
6894
- entries = (0, import_node_fs22.readdirSync)(dir, { withFileTypes: true });
7641
+ entries = (0, import_node_fs23.readdirSync)(dir, { withFileTypes: true });
6895
7642
  } catch {
6896
7643
  return;
6897
7644
  }
@@ -6899,9 +7646,9 @@ function walkForExtensions(dir, extCounts, depth = 0) {
6899
7646
  if (entry.name.startsWith(".") && entry.isDirectory()) continue;
6900
7647
  if (entry.isDirectory()) {
6901
7648
  if (IGNORE_DIRS.has(entry.name)) continue;
6902
- walkForExtensions((0, import_node_path26.join)(dir, entry.name), extCounts, depth + 1);
7649
+ walkForExtensions((0, import_node_path28.join)(dir, entry.name), extCounts, depth + 1);
6903
7650
  } else {
6904
- const ext = (0, import_node_path26.extname)(entry.name).toLowerCase();
7651
+ const ext = (0, import_node_path28.extname)(entry.name).toLowerCase();
6905
7652
  if (ext && EXTENSION_TO_LANGUAGE[ext]) {
6906
7653
  extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
6907
7654
  }
@@ -6940,12 +7687,12 @@ function detectLanguages(rootDir, supportedLanguages) {
6940
7687
  });
6941
7688
  return results;
6942
7689
  }
6943
- var import_node_fs22, import_node_path26, EXTENSION_TO_LANGUAGE, IGNORE_DIRS, AUXILIARY_LANGUAGES;
7690
+ var import_node_fs23, import_node_path28, EXTENSION_TO_LANGUAGE, IGNORE_DIRS, AUXILIARY_LANGUAGES;
6944
7691
  var init_language_detection = __esm({
6945
7692
  "src/server/graph/core/language-detection.ts"() {
6946
7693
  "use strict";
6947
- import_node_fs22 = require("node:fs");
6948
- import_node_path26 = require("node:path");
7694
+ import_node_fs23 = require("node:fs");
7695
+ import_node_path28 = require("node:path");
6949
7696
  init_launch_kit_paths();
6950
7697
  EXTENSION_TO_LANGUAGE = {
6951
7698
  // Web / Frontend
@@ -7067,7 +7814,7 @@ __export(watcher_exports, {
7067
7814
  function isIgnoredPath(rel) {
7068
7815
  if (rel.startsWith(GRAPHS_RELATIVE)) return true;
7069
7816
  if (rel.endsWith(".lock") || rel.endsWith(".log")) return true;
7070
- for (const part of rel.split(import_node_path27.sep)) {
7817
+ for (const part of rel.split(import_node_path29.sep)) {
7071
7818
  if (IGNORE_SEGMENTS.has(part)) return true;
7072
7819
  }
7073
7820
  return false;
@@ -7109,7 +7856,7 @@ function startGraphWatcher(rootDir, opts = {}) {
7109
7856
  regenerating = false;
7110
7857
  }
7111
7858
  }
7112
- const watcher = (0, import_node_fs23.watch)(rootDir, { recursive: true }, (event, filename) => {
7859
+ const watcher = (0, import_node_fs24.watch)(rootDir, { recursive: true }, (event, filename) => {
7113
7860
  if (!filename) return;
7114
7861
  const rel = filename.toString();
7115
7862
  if (process.env.LAUNCH_CHART_WATCH_TRACE === "1") {
@@ -7142,12 +7889,12 @@ function startGraphWatcher(rootDir, opts = {}) {
7142
7889
  freshness: () => getFreshnessTracker(rootDir).get()
7143
7890
  };
7144
7891
  }
7145
- var import_node_fs23, import_node_path27, IGNORE_SEGMENTS, TRIGGER_EXTENSIONS, GRAPHS_RELATIVE;
7892
+ var import_node_fs24, import_node_path29, IGNORE_SEGMENTS, TRIGGER_EXTENSIONS, GRAPHS_RELATIVE;
7146
7893
  var init_watcher = __esm({
7147
7894
  "src/server/graph/core/watcher.ts"() {
7148
7895
  "use strict";
7149
- import_node_fs23 = require("node:fs");
7150
- import_node_path27 = require("node:path");
7896
+ import_node_fs24 = require("node:fs");
7897
+ import_node_path29 = require("node:path");
7151
7898
  init_launch_kit_paths();
7152
7899
  init_graph();
7153
7900
  init_freshness();
@@ -7176,7 +7923,7 @@ var init_watcher = __esm({
7176
7923
  ".prisma",
7177
7924
  ".sql"
7178
7925
  ]);
7179
- GRAPHS_RELATIVE = (0, import_node_path27.join)(LAUNCHSECURE_DIR, "graphs");
7926
+ GRAPHS_RELATIVE = (0, import_node_path29.join)(LAUNCHSECURE_DIR, "graphs");
7180
7927
  }
7181
7928
  });
7182
7929
 
@@ -7192,14 +7939,43 @@ function matchesSearch(node, query) {
7192
7939
  if (node.name.toLowerCase().includes(q)) return true;
7193
7940
  const route = node.route;
7194
7941
  if (route && route.toLowerCase().includes(q)) return true;
7942
+ const elements = node.elements;
7943
+ if (elements) {
7944
+ for (const el of elements) {
7945
+ if (el.text && el.text.toLowerCase().includes(q)) return true;
7946
+ if (el.props) {
7947
+ for (const v of Object.values(el.props)) {
7948
+ if (typeof v !== "string") continue;
7949
+ if (v.includes("=>") || v.startsWith("(") || v.startsWith("{")) continue;
7950
+ if (v.toLowerCase().includes(q)) return true;
7951
+ }
7952
+ }
7953
+ }
7954
+ }
7955
+ const uiLabels = node.ui_labels;
7956
+ if (uiLabels) {
7957
+ for (const lbl of uiLabels) {
7958
+ if (typeof lbl === "string" && lbl.toLowerCase().includes(q)) return true;
7959
+ }
7960
+ }
7961
+ const notes = node.notes;
7962
+ if (notes) {
7963
+ for (const n of notes) {
7964
+ if (n.kind && n.kind.toLowerCase().includes(q)) return true;
7965
+ if (n.text && n.text.toLowerCase().includes(q)) return true;
7966
+ }
7967
+ }
7195
7968
  return false;
7196
7969
  }
7197
7970
  function toMinimal(nodes) {
7198
7971
  return nodes.map((n) => {
7199
7972
  const out = { id: n.id, type: n.type, name: n.name };
7200
- if (n.tags != null) out.tags = n.tags;
7201
- if (n.route != null) out.route = n.route;
7202
- if (n.methods != null) out.methods = n.methods;
7973
+ for (const [k, v] of Object.entries(n)) {
7974
+ if (k === "id" || k === "type" || k === "name") continue;
7975
+ if (MINIMAL_STRIP_FIELDS.has(k)) continue;
7976
+ if (DEEP_FIELDS.has(k)) continue;
7977
+ if (v != null) out[k] = v;
7978
+ }
7203
7979
  return out;
7204
7980
  });
7205
7981
  }
@@ -7211,6 +7987,9 @@ function crossRefsAsEdges(graph) {
7211
7987
  target_layer: c.layer
7212
7988
  }));
7213
7989
  }
7990
+ function categorizeNoteKind(kind) {
7991
+ return NOTE_KIND_CATEGORY[kind] ?? "custom";
7992
+ }
7214
7993
  function toCompactNode(n) {
7215
7994
  const out = { i: n.id, t: n.type, n: n.name };
7216
7995
  const tags = n.tags;
@@ -7465,7 +8244,7 @@ function withFreshnessMeta(result, args) {
7465
8244
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return result;
7466
8245
  let rootDir;
7467
8246
  try {
7468
- rootDir = resolveProjectRoot(args.project, process.cwd());
8247
+ rootDir = resolveRequestRoot2(args, process.cwd());
7469
8248
  } catch {
7470
8249
  return result;
7471
8250
  }
@@ -7480,7 +8259,7 @@ function withFreshnessMeta(result, args) {
7480
8259
  }
7481
8260
  function resolveOrErr(args) {
7482
8261
  try {
7483
- return { rootDir: resolveProjectRoot(args.project, process.cwd()) };
8262
+ return { rootDir: resolveRequestRoot2(args, process.cwd()) };
7484
8263
  } catch (e) {
7485
8264
  return err(e.message);
7486
8265
  }
@@ -7489,6 +8268,8 @@ async function handleGenerateGraph(args) {
7489
8268
  const monorepoRoot = process.cwd();
7490
8269
  const layer = args.layer;
7491
8270
  const projectArg = typeof args.project === "string" ? args.project.trim() : "";
8271
+ const worktreeArg = typeof args.worktree === "string" ? args.worktree.trim() : "";
8272
+ const projectRootArg = typeof args.project_root === "string" ? args.project_root.trim() : "";
7492
8273
  function formatProjectResult(results2, relativeRoot) {
7493
8274
  return results2.map((r) => {
7494
8275
  const warnings = r.output.warnings.length;
@@ -7496,25 +8277,27 @@ async function handleGenerateGraph(args) {
7496
8277
  }).join("\n") + `
7497
8278
  \u2192 ${relativeRoot}/.launchsecure/graphs/`;
7498
8279
  }
7499
- if (projectArg) {
8280
+ if (projectArg || worktreeArg || projectRootArg) {
7500
8281
  let rootDir;
7501
8282
  try {
7502
- rootDir = resolveProjectRoot(projectArg, monorepoRoot);
8283
+ rootDir = resolveRequestRoot2(args, monorepoRoot);
7503
8284
  } catch (e) {
7504
8285
  return err(e.message);
7505
8286
  }
7506
8287
  const results2 = await generateGraph(rootDir, layer);
8288
+ const label = worktreeArg ? `worktree "${worktreeArg}"` : projectArg ? `project "${projectArg}"` : `root "${projectRootArg}"`;
8289
+ const queryHint = worktreeArg ? `read_graph (with worktree="${worktreeArg}")` : projectArg ? `read_graph (with project="${projectArg}")` : `read_graph (with project_root="${projectRootArg}")`;
7507
8290
  if (results2.length === 0) {
7508
8291
  return err(
7509
- layer ? `No parser detected for the "${layer}" layer in project "${projectArg}".` : `No parsers detected for project "${projectArg}". Check that the project root has the expected structure.`
8292
+ layer ? `No parser detected for the "${layer}" layer in ${label}.` : `No parsers detected for ${label}. Check that the root has the expected structure.`
7510
8293
  );
7511
8294
  }
7512
8295
  return ok(
7513
- `Graph generated successfully for project "${projectArg}".
8296
+ `Graph generated successfully for ${label}.
7514
8297
 
7515
- ${formatProjectResult(results2, projectArg)}
8298
+ ${formatProjectResult(results2, rootDir)}
7516
8299
 
7517
- Use read_graph (with project="${projectArg}") to query.`
8300
+ Use ${queryHint} to query.`
7518
8301
  );
7519
8302
  }
7520
8303
  const projects = listProjects(monorepoRoot);
@@ -7577,6 +8360,7 @@ function runReadGraphQueryRaw(rootDir, args) {
7577
8360
  const layerIsDb = args.layer === "db";
7578
8361
  const minimal = args.minimal ?? layerIsDb;
7579
8362
  const includeEdges = args.include_edges;
8363
+ const includeFindings = args.include_findings === true;
7580
8364
  const offset = args.offset ?? 0;
7581
8365
  const limit = args.limit;
7582
8366
  const hasFilter = !!(search || type || module_ || nodeId || tagKey && tagValue);
@@ -7626,14 +8410,23 @@ function runReadGraphQueryRaw(rootDir, args) {
7626
8410
  result2.budget_exceeded = true;
7627
8411
  result2.hint = `Neighborhood truncated at hop ${nb.stoppedAtHop} (projected size exceeded budget). To explore further, call read_graph with node_id set to a specific neighbor from the returned nodes, or rerun with hops=${Math.max(1, nb.stoppedAtHop)} to confirm the partial view is what you wanted.`;
7628
8412
  }
8413
+ if (includeFindings && layer === "db") {
8414
+ result2.contradictions = graph.contradictions ?? [];
8415
+ result2.flagged_edges = graph.flagged_edges ?? [];
8416
+ }
7629
8417
  return result2;
7630
8418
  }
7631
8419
  if (!hasFilter) {
7632
- return {
8420
+ const summaryResult = {
7633
8421
  hint: "No filter specified \u2014 returning summary only. Use search/type/module/node_id to retrieve nodes.",
7634
8422
  layer,
7635
8423
  summary: layerSummary(graph)
7636
8424
  };
8425
+ if (includeFindings && layer === "db") {
8426
+ summaryResult.contradictions = graph.contradictions ?? [];
8427
+ summaryResult.flagged_edges = graph.flagged_edges ?? [];
8428
+ }
8429
+ return summaryResult;
7637
8430
  }
7638
8431
  const matched = graph.nodes.filter((n) => {
7639
8432
  if (search && !matchesSearch(n, search)) return false;
@@ -7684,6 +8477,10 @@ function runReadGraphQueryRaw(rootDir, args) {
7684
8477
  } else if (returnedEdges.length > 0) {
7685
8478
  result.edges_hint = `${returnedEdges.length} edges between matched nodes omitted. Pass include_edges:true to retrieve them (only do this when you actually need edge data).`;
7686
8479
  }
8480
+ if (includeFindings && layer === "db") {
8481
+ result.contradictions = graph.contradictions ?? [];
8482
+ result.flagged_edges = graph.flagged_edges ?? [];
8483
+ }
7687
8484
  return result;
7688
8485
  }
7689
8486
  function runReadGraphQuery(rootDir, args) {
@@ -7698,6 +8495,8 @@ function handleReadGraph(args) {
7698
8495
  return err("queries array is empty. Provide at least one query object.");
7699
8496
  }
7700
8497
  const inheritedProject = typeof args.project === "string" ? args.project : void 0;
8498
+ const inheritedWorktree = typeof args.worktree === "string" ? args.worktree : void 0;
8499
+ const inheritedProjectRoot = typeof args.project_root === "string" ? args.project_root : void 0;
7701
8500
  const results = [];
7702
8501
  let cumulativeChars = 0;
7703
8502
  let budgetHit = false;
@@ -7715,15 +8514,18 @@ function handleReadGraph(args) {
7715
8514
  });
7716
8515
  continue;
7717
8516
  }
7718
- const qWithProject = inheritedProject && !q.project ? { ...q, project: inheritedProject } : q;
8517
+ const qInherited = { ...q };
8518
+ if (inheritedProject && !qInherited.project) qInherited.project = inheritedProject;
8519
+ if (inheritedWorktree && !qInherited.worktree) qInherited.worktree = inheritedWorktree;
8520
+ if (inheritedProjectRoot && !qInherited.project_root) qInherited.project_root = inheritedProjectRoot;
7719
8521
  let perQueryRoot;
7720
8522
  try {
7721
- perQueryRoot = resolveProjectRoot(qWithProject.project, monorepoRoot);
8523
+ perQueryRoot = resolveRequestRoot2(qInherited, monorepoRoot);
7722
8524
  } catch (e) {
7723
8525
  results.push({ index: i, query: q, result: { error: e.message } });
7724
8526
  continue;
7725
8527
  }
7726
- const r = runReadGraphQuery(perQueryRoot, qWithProject);
8528
+ const r = runReadGraphQuery(perQueryRoot, qInherited);
7727
8529
  const entry = { index: i, query: q, result: r };
7728
8530
  const entrySize = JSON.stringify(entry, null, 2).length;
7729
8531
  if (cumulativeChars + entrySize > BATCH_BUDGET_CHARS && results.length > 0) {
@@ -7756,12 +8558,12 @@ function handleReadGraph(args) {
7756
8558
  return okJson(result);
7757
8559
  }
7758
8560
  function nodeToFilePath(rootDir, layer, nodeId) {
7759
- if (layer === "ui" || layer === "api") return (0, import_node_path28.join)(rootDir, "src", nodeId);
7760
- if (layer === "db") return (0, import_node_path28.join)(rootDir, "prisma", "schema.prisma");
7761
- const withSrc = (0, import_node_path28.join)(rootDir, "src", nodeId);
7762
- if ((0, import_node_fs24.existsSync)(withSrc)) return withSrc;
7763
- const direct = (0, import_node_path28.join)(rootDir, nodeId);
7764
- if ((0, import_node_fs24.existsSync)(direct)) return direct;
8561
+ if (layer === "ui" || layer === "api") return (0, import_node_path30.join)(rootDir, "src", nodeId);
8562
+ if (layer === "db") return (0, import_node_path30.join)(rootDir, "prisma", "schema.prisma");
8563
+ const withSrc = (0, import_node_path30.join)(rootDir, "src", nodeId);
8564
+ if ((0, import_node_fs25.existsSync)(withSrc)) return withSrc;
8565
+ const direct = (0, import_node_path30.join)(rootDir, nodeId);
8566
+ if ((0, import_node_fs25.existsSync)(direct)) return direct;
7765
8567
  return null;
7766
8568
  }
7767
8569
  function handleInspectNode(args) {
@@ -7791,7 +8593,7 @@ function handleInspectNode(args) {
7791
8593
  } else {
7792
8594
  matched = graph.nodes;
7793
8595
  }
7794
- const allDeepFields = ["elements", "stateVars", "conditions", "variables", "responses", "params", "effects"];
8596
+ const allDeepFields = ["elements", "stateVars", "conditions", "variables", "responses", "params", "effects", "ui_labels", "notes"];
7795
8597
  const requestedFields = fields ?? allDeepFields;
7796
8598
  let filterRegex = null;
7797
8599
  if (filter) {
@@ -7846,6 +8648,101 @@ function handleInspectNode(args) {
7846
8648
  nodes: results
7847
8649
  });
7848
8650
  }
8651
+ function handleListNotes(args) {
8652
+ const __resolved = resolveOrErr(args);
8653
+ if ("content" in __resolved) return __resolved;
8654
+ const { rootDir } = __resolved;
8655
+ const layerArg = args.layer;
8656
+ const kindArg = args.kind?.toUpperCase();
8657
+ const categoryArg = args.category ?? "actionable,warning";
8658
+ const moduleArg = args.module;
8659
+ const patternArg = args.pattern;
8660
+ const authorArg = args.author?.toLowerCase();
8661
+ const limit = args.limit ?? 100;
8662
+ const offset = args.offset ?? 0;
8663
+ let patternRegex = null;
8664
+ if (patternArg) {
8665
+ try {
8666
+ patternRegex = new RegExp(patternArg, "i");
8667
+ } catch {
8668
+ return err(`Invalid regex pattern: "${patternArg}"`);
8669
+ }
8670
+ }
8671
+ const wantedCategories = (() => {
8672
+ const trimmed = categoryArg.trim().toLowerCase();
8673
+ if (trimmed === "all") return "all";
8674
+ const out = /* @__PURE__ */ new Set();
8675
+ for (const part of trimmed.split(",").map((s) => s.trim()).filter(Boolean)) {
8676
+ if (part === "actionable" || part === "warning" || part === "doc" || part === "custom") {
8677
+ out.add(part);
8678
+ }
8679
+ }
8680
+ if (out.size === 0) {
8681
+ out.add("actionable");
8682
+ out.add("warning");
8683
+ }
8684
+ return out;
8685
+ })();
8686
+ const layers = layerArg ? [layerArg] : getAvailableLayers(rootDir);
8687
+ const items = [];
8688
+ for (const layer of layers) {
8689
+ const graph = readGraph(rootDir, layer);
8690
+ if (!graph) continue;
8691
+ for (const node of graph.nodes) {
8692
+ const nodeNotes = node.notes;
8693
+ if (!nodeNotes || nodeNotes.length === 0) continue;
8694
+ const tags = node.tags;
8695
+ const nodeModule = tags?.module;
8696
+ if (moduleArg && nodeModule !== moduleArg) continue;
8697
+ for (const n of nodeNotes) {
8698
+ if (!n.kind || !n.text || typeof n.line !== "number") continue;
8699
+ const category = categorizeNoteKind(n.kind);
8700
+ if (wantedCategories !== "all" && !wantedCategories.has(category)) continue;
8701
+ if (kindArg && n.kind.toUpperCase() !== kindArg) continue;
8702
+ if (authorArg && (!n.author || n.author.toLowerCase() !== authorArg)) continue;
8703
+ if (patternRegex && !patternRegex.test(n.text)) continue;
8704
+ const item = {
8705
+ file: node.id,
8706
+ line: n.line,
8707
+ kind: n.kind,
8708
+ category,
8709
+ text: n.text
8710
+ };
8711
+ if (n.author) item.author = n.author;
8712
+ if (nodeModule) item.module = nodeModule;
8713
+ items.push(item);
8714
+ }
8715
+ }
8716
+ }
8717
+ const catOrder = { actionable: 0, warning: 1, doc: 2, custom: 3 };
8718
+ items.sort((a, b) => {
8719
+ const c = catOrder[a.category] - catOrder[b.category];
8720
+ if (c !== 0) return c;
8721
+ const k = a.kind.localeCompare(b.kind);
8722
+ if (k !== 0) return k;
8723
+ const f = a.file.localeCompare(b.file);
8724
+ if (f !== 0) return f;
8725
+ return a.line - b.line;
8726
+ });
8727
+ const total = items.length;
8728
+ const paged = items.slice(offset, offset + limit);
8729
+ const hasMore = offset + paged.length < total;
8730
+ return okJson({
8731
+ total,
8732
+ returned: paged.length,
8733
+ has_more: hasMore,
8734
+ ...hasMore ? { next_offset: offset + paged.length } : {},
8735
+ filter: {
8736
+ layer: layerArg ?? "all",
8737
+ kind: kindArg ?? null,
8738
+ category: categoryArg,
8739
+ module: moduleArg ?? null,
8740
+ pattern: patternArg ?? null,
8741
+ author: authorArg ?? null
8742
+ },
8743
+ items: paged
8744
+ });
8745
+ }
7849
8746
  function handleGrepNodes(args) {
7850
8747
  const __resolved = resolveOrErr(args);
7851
8748
  if ("content" in __resolved) return __resolved;
@@ -7908,11 +8805,11 @@ function handleGrepNodes(args) {
7908
8805
  let filesSearched = 0;
7909
8806
  let truncated = false;
7910
8807
  for (const [filePath, nodeId] of filePaths) {
7911
- if (!(0, import_node_fs24.existsSync)(filePath)) continue;
8808
+ if (!(0, import_node_fs25.existsSync)(filePath)) continue;
7912
8809
  filesSearched++;
7913
8810
  let content;
7914
8811
  try {
7915
- content = (0, import_node_fs24.readFileSync)(filePath, "utf-8");
8812
+ content = (0, import_node_fs25.readFileSync)(filePath, "utf-8");
7916
8813
  } catch {
7917
8814
  continue;
7918
8815
  }
@@ -8034,16 +8931,15 @@ function handleStartChartServer(args) {
8034
8931
  });
8035
8932
  }
8036
8933
  const entryPath = process.argv[1];
8037
- const logDir = (0, import_node_path28.join)((0, import_node_os2.homedir)(), LAUNCHSECURE_DIR);
8038
- (0, import_node_fs24.mkdirSync)(logDir, { recursive: true });
8039
- const logPath = (0, import_node_path28.join)(logDir, "launch-chart.log");
8040
- const out = (0, import_node_fs24.openSync)(logPath, "a");
8041
- const err2 = (0, import_node_fs24.openSync)(logPath, "a");
8934
+ const logDir = (0, import_node_path30.join)((0, import_node_os3.homedir)(), LAUNCHSECURE_DIR);
8935
+ (0, import_node_fs25.mkdirSync)(logDir, { recursive: true });
8936
+ const logPath = (0, import_node_path30.join)(logDir, "launch-chart.log");
8937
+ const out = (0, import_node_fs25.openSync)(logPath, "a");
8938
+ const err2 = (0, import_node_fs25.openSync)(logPath, "a");
8042
8939
  const portArgs = args.port ? ["--port", String(args.port)] : [];
8043
8940
  const child = (0, import_node_child_process2.spawn)(process.execPath, [entryPath, "serve", ...portArgs], {
8044
8941
  detached: true,
8045
- stdio: ["ignore", out, err2],
8046
- env: { ...process.env, LAUNCH_CHART_AUTOSERVE: "" }
8942
+ stdio: ["ignore", out, err2]
8047
8943
  });
8048
8944
  child.unref();
8049
8945
  return okJson({
@@ -8168,20 +9064,20 @@ function handleDetectProjectStack() {
8168
9064
  if (ref.type === "references_api") stats.references_api++;
8169
9065
  }
8170
9066
  }
8171
- const srcDir = (0, import_node_path28.join)(rootDir, "src");
8172
- if ((0, import_node_fs24.existsSync)(srcDir)) {
9067
+ const srcDir = (0, import_node_path30.join)(rootDir, "src");
9068
+ if ((0, import_node_fs25.existsSync)(srcDir)) {
8173
9069
  const scanDir = (dir) => {
8174
- if (!(0, import_node_fs24.existsSync)(dir)) return;
8175
- for (const entry of (0, import_node_fs24.readdirSync)(dir, { withFileTypes: true })) {
9070
+ if (!(0, import_node_fs25.existsSync)(dir)) return;
9071
+ for (const entry of (0, import_node_fs25.readdirSync)(dir, { withFileTypes: true })) {
8176
9072
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
8177
- const full = (0, import_node_path28.join)(dir, entry.name);
9073
+ const full = (0, import_node_path30.join)(dir, entry.name);
8178
9074
  if (entry.isDirectory()) {
8179
9075
  scanDir(full);
8180
9076
  continue;
8181
9077
  }
8182
- if (![".ts", ".tsx"].includes((0, import_node_path28.extname)(entry.name))) continue;
9078
+ if (![".ts", ".tsx"].includes((0, import_node_path30.extname)(entry.name))) continue;
8183
9079
  try {
8184
- const content = (0, import_node_fs24.readFileSync)(full, "utf-8");
9080
+ const content = (0, import_node_fs25.readFileSync)(full, "utf-8");
8185
9081
  const matches = content.match(/@api\s+(GET|POST|PUT|DELETE|PATCH)\s+\/\S+/g);
8186
9082
  if (matches) stats.annotations += matches.length;
8187
9083
  } catch {
@@ -8200,7 +9096,7 @@ function handleDetectProjectStack() {
8200
9096
  name: p.name,
8201
9097
  root: p.root,
8202
9098
  absolute_root: p.absoluteRoot,
8203
- has_graph: (0, import_node_fs24.existsSync)((0, import_node_path28.join)(p.absoluteRoot, LAUNCHSECURE_DIR, "graphs"))
9099
+ has_graph: (0, import_node_fs25.existsSync)((0, import_node_path30.join)(p.absoluteRoot, LAUNCHSECURE_DIR, "graphs"))
8204
9100
  }));
8205
9101
  return okJson({
8206
9102
  languages,
@@ -8314,6 +9210,10 @@ async function handleMessage(msg) {
8314
9210
  respond(id ?? null, withFreshnessMeta(handleBlastPoints(args), args));
8315
9211
  return;
8316
9212
  }
9213
+ if (toolName === "list_notes") {
9214
+ respond(id ?? null, withFreshnessMeta(handleListNotes(args), args));
9215
+ return;
9216
+ }
8317
9217
  respondError(id ?? null, -32601, `Unknown tool: ${toolName}`);
8318
9218
  return;
8319
9219
  }
@@ -8374,14 +9274,14 @@ function startGraphMcpServer() {
8374
9274
  process.stderr.write(`[launchsecure-graph] MCP server started (cwd: ${process.cwd()})
8375
9275
  `);
8376
9276
  }
8377
- var import_node_fs24, import_node_path28, import_node_child_process2, import_node_os2, SERVER_INFO, TOOLS, COMPACT_SCHEMA, COMPACT_NODE_KNOWN_KEYS, DEEP_FIELDS, EST_CHARS_PER_NODE_FULL, EST_CHARS_PER_NODE_MIN, EST_CHARS_PER_EDGE, DEFAULT_EST_NODE_FULL, DEFAULT_EST_NODE_MIN, DEFAULT_EST_EDGE, NEIGHBORHOOD_BUDGET_CHARS, MAX_FILTER_EDGES, BATCH_BUDGET_CHARS, watcherHandle;
9277
+ var import_node_fs25, import_node_path30, import_node_child_process2, import_node_os3, SERVER_INFO, TOOLS, MINIMAL_STRIP_FIELDS, COMPACT_SCHEMA, COMPACT_NODE_KNOWN_KEYS, DEEP_FIELDS, NOTE_KIND_CATEGORY, EST_CHARS_PER_NODE_FULL, EST_CHARS_PER_NODE_MIN, EST_CHARS_PER_EDGE, DEFAULT_EST_NODE_FULL, DEFAULT_EST_NODE_MIN, DEFAULT_EST_EDGE, NEIGHBORHOOD_BUDGET_CHARS, MAX_FILTER_EDGES, BATCH_BUDGET_CHARS, watcherHandle;
8378
9278
  var init_graph_mcp = __esm({
8379
9279
  "src/server/graph-mcp.ts"() {
8380
9280
  "use strict";
8381
- import_node_fs24 = require("node:fs");
8382
- import_node_path28 = require("node:path");
9281
+ import_node_fs25 = require("node:fs");
9282
+ import_node_path30 = require("node:path");
8383
9283
  import_node_child_process2 = require("node:child_process");
8384
- import_node_os2 = require("node:os");
9284
+ import_node_os3 = require("node:os");
8385
9285
  init_launch_kit_paths();
8386
9286
  init_graph();
8387
9287
  init_lockfile();
@@ -8409,13 +9309,21 @@ var init_graph_mcp = __esm({
8409
9309
  project: {
8410
9310
  type: "string",
8411
9311
  description: PROJECT_PARAM_DESCRIPTION + " Special: omit to regenerate ALL configured projects."
9312
+ },
9313
+ worktree: {
9314
+ type: "string",
9315
+ description: WORKTREE_PARAM_DESCRIPTION2
9316
+ },
9317
+ project_root: {
9318
+ type: "string",
9319
+ description: PROJECT_ROOT_PARAM_DESCRIPTION2
8412
9320
  }
8413
9321
  }
8414
9322
  }
8415
9323
  },
8416
9324
  {
8417
9325
  name: "read_graph",
8418
- description: 'Query the structural project graph \u2014 use INSTEAD of Glob and Grep for locating files, understanding structure, and navigating the codebase. Faster and more accurate than file-system search because it returns typed nodes with metadata and relationships. \n\nUSE THIS FOR: "where is X", "what files are in module Y", "what pages exist under /admin", "what components does Z render", "what tables relate to User", "list all hooks in auth module", "which endpoints touch the User table", "what auth strategy does this endpoint use". \n\nDO NOT USE FOR: understanding what\'s INSIDE a component (use inspect_node for elements, conditions, state, variables, responses), reading actual source code (use Read). \n\nQUERY PARAMS (at least one required for node data \u2014 unfiltered calls return summary only to stay in context):\n- search: substring match on node id, name, or route\n- type: filter by node type (ui layer: page, layout, component, ui, hook, context, config, util; api layer: endpoint; db layer: table, enum)\n- module: filter by module tag (computed from directory structure, e.g. "auth", "admin", "settings")\n- node_id: return this node + its neighborhood (incoming+outgoing edges within `hops`)\n- hops: neighborhood radius when node_id is set (default 1)\n- minimal: return only id/type/name/module/route per node (skip heavy fields like columns, exports)\n- include_edges: return the actual edge list. Default: TRUE for neighborhood queries (node_id), FALSE for filter queries (search/type/module). Filter responses always include `edge_count`; only pass include_edges:true when you actually need to inspect individual edges (e.g. "which components render X"). This default cuts typical filter responses in half.\n\nBATCH MODE: pass `queries` (array of query objects) to run multiple independent queries in a single call. Each query object uses the same params (layer/search/type/module/node_id/hops/minimal). Returns { batch: true, count, results: [{index, query, result}, ...] }. Use this when you need multiple graph views up-front (e.g. scoping a feature across ui+api+db layers) to save round-trips. When batch mode is used, top-level params are ignored.\n\nReturns: filtered nodes + edges between them. If no filter given, returns per-layer counts and type breakdown only.\n\nWIRE FORMAT (compact): responses that include nodes/edges use short keys and edge-by-index refs to cut payload ~40-60%. Every such response carries a `_schema` legend. Quick reference:\n nodes[]: { i: id, t: type, n: name, m: module, r: route, mt: methods, x: exports, c: columns }\n edges[]: { s: source_node_index, d: target_node_index, t: type, l: label }\nedges.s / edges.d are 0-based indices into THIS response\'s nodes array. If a referenced node is not in the response (boundary case), s/d may instead contain the full node id string \u2014 always check the type.\n\nPAGINATION (filter queries):\n- Use `offset` and `limit` to paginate through large result sets.\n- Response includes: `total` (matched), `returned` (in this page), `has_more`, `next_offset`.\n- If `has_more: true`, call again with `offset: next_offset` to get the next page.\n\nBUDGET GUARDS:\n- Neighborhood queries stop expanding when the projected response exceeds budget. The response then contains `budget_exceeded: true` plus `hops_traversed < hops_requested`. When this happens, drill into a specific neighbor with another node_id call rather than retrying with larger hops \u2014 it will just truncate again.\n- Batch mode caps total response size. Once the budget is hit, later queries return `{skipped: true, reason: "batch_budget_exhausted"}` and you must re-run them individually.\n\nMONOREPOS: pass `project: "<name>"` to query a sub-project graph (defined in .launchchart.json projects[]). Omitting `project` targets the monorepo root. In batch mode the top-level `project` is inherited by sub-queries that do not set their own. Run detect_project_stack to list configured projects.',
9326
+ description: 'Query the structural project graph \u2014 use INSTEAD of Glob and Grep for locating files, understanding structure, and navigating the codebase. Faster and more accurate than file-system search because it returns typed nodes with metadata and relationships. \n\nUSE THIS FOR: "where is X", "what files are in module Y", "what pages exist under /admin", "what components does Z render", "what tables relate to User", "list all hooks in auth module", "which endpoints touch the User table", "what auth strategy does this endpoint use". \n\nDO NOT USE FOR: understanding what\'s INSIDE a component (use inspect_node for elements, conditions, state, variables, responses), reading actual source code (use Read). \n\nQUERY PARAMS (at least one required for node data \u2014 unfiltered calls return summary only to stay in context):\n- search: substring match on node id, name, route, JSX element text/string-prop literals, ui_labels (strings from `const X = [{label:\'\u2026\'}]` data arrays), and notes (tagged comments). Lets queries like search:"Briefs" find a page where "Briefs" is a tab label inside an array, or search:"FIXME" find every file with a FIXME comment. For exhaustive note listings use list_notes.\n- type: filter by node type (ui layer: page, layout, component, ui, hook, context, config, util; api layer: endpoint; db layer: table, enum, migration). Migration nodes carry safety attributes \u2014 is_destructive, has_orphan_check, has_sidecar_backup, has_pre_flight_notice, contains_backfill, contains_drop_column, contains_drop_table, statement_count, timestamp \u2014 queryable via tag_key filters or by inspecting returned node fields.\n- include_findings: db layer only. When true, response includes `contradictions` and `flagged_edges` arrays surfacing SQL\u2194ORM schema drift detected by the SQL migrations parser. Use this to audit "schema vs migrations" disagreement (missing columns, type mismatches, nullability drift, FKs declared in SQL but not ORM).\n- module: filter by module tag (computed from directory structure, e.g. "auth", "admin", "settings")\n- node_id: return this node + its neighborhood (incoming+outgoing edges within `hops`)\n- hops: neighborhood radius when node_id is set (default 1)\n- minimal: return only id/type/name/module/route per node (skip heavy fields like columns, exports)\n- include_edges: return the actual edge list. Default: TRUE for neighborhood queries (node_id), FALSE for filter queries (search/type/module). Filter responses always include `edge_count`; only pass include_edges:true when you actually need to inspect individual edges (e.g. "which components render X"). This default cuts typical filter responses in half.\n\nBATCH MODE: pass `queries` (array of query objects) to run multiple independent queries in a single call. Each query object uses the same params (layer/search/type/module/node_id/hops/minimal). Returns { batch: true, count, results: [{index, query, result}, ...] }. Use this when you need multiple graph views up-front (e.g. scoping a feature across ui+api+db layers) to save round-trips. When batch mode is used, top-level params are ignored.\n\nReturns: filtered nodes + edges between them. If no filter given, returns per-layer counts and type breakdown only.\n\nWIRE FORMAT (compact): responses that include nodes/edges use short keys and edge-by-index refs to cut payload ~40-60%. Every such response carries a `_schema` legend. Quick reference:\n nodes[]: { i: id, t: type, n: name, m: module, r: route, mt: methods, x: exports, c: columns }\n edges[]: { s: source_node_index, d: target_node_index, t: type, l: label }\nedges.s / edges.d are 0-based indices into THIS response\'s nodes array. If a referenced node is not in the response (boundary case), s/d may instead contain the full node id string \u2014 always check the type.\n\nPAGINATION (filter queries):\n- Use `offset` and `limit` to paginate through large result sets.\n- Response includes: `total` (matched), `returned` (in this page), `has_more`, `next_offset`.\n- If `has_more: true`, call again with `offset: next_offset` to get the next page.\n\nBUDGET GUARDS:\n- Neighborhood queries stop expanding when the projected response exceeds budget. The response then contains `budget_exceeded: true` plus `hops_traversed < hops_requested`. When this happens, drill into a specific neighbor with another node_id call rather than retrying with larger hops \u2014 it will just truncate again.\n- Batch mode caps total response size. Once the budget is hit, later queries return `{skipped: true, reason: "batch_budget_exhausted"}` and you must re-run them individually.\n\nMONOREPOS: pass `project: "<name>"` to query a sub-project graph (defined in .launchchart.json projects[]). Omitting `project` targets the monorepo root. In batch mode the top-level `project` is inherited by sub-queries that do not set their own. Run detect_project_stack to list configured projects.',
8419
9327
  inputSchema: {
8420
9328
  type: "object",
8421
9329
  properties: {
@@ -8425,7 +9333,7 @@ var init_graph_mcp = __esm({
8425
9333
  },
8426
9334
  search: {
8427
9335
  type: "string",
8428
- description: "Case-insensitive substring match against node id, name, or route."
9336
+ description: "Case-insensitive substring match against node id, name, route, JSX element text/string-prop literals, and ui_labels (strings from `const X = [{label:'\u2026'}]` data arrays)."
8429
9337
  },
8430
9338
  type: {
8431
9339
  type: "string",
@@ -8459,6 +9367,10 @@ var init_graph_mcp = __esm({
8459
9367
  type: "boolean",
8460
9368
  description: "Include the edge list in the response. Default TRUE for neighborhood queries (node_id), FALSE for filter queries. Filter responses always include edge_count. Only set true on filter queries when you actually need edge data."
8461
9369
  },
9370
+ include_findings: {
9371
+ type: "boolean",
9372
+ description: "DB layer only. When true, response includes `contradictions[]` and `flagged_edges[]` arrays from the SQL migrations parser \u2014 SQL\u2194ORM schema drift findings (missing columns/tables, type mismatches, nullability drift, FKs in SQL but no ORM @relation). Default false."
9373
+ },
8462
9374
  offset: {
8463
9375
  type: "number",
8464
9376
  description: "Skip first N matched nodes (pagination). Default 0. Use next_offset from a previous response to get the next page."
@@ -8469,7 +9381,7 @@ var init_graph_mcp = __esm({
8469
9381
  },
8470
9382
  queries: {
8471
9383
  type: "array",
8472
- description: "Batch mode \u2014 array of query objects to run in a single call. Each uses the same param schema (including `project`). When set, top-level params are ignored. Subject to an aggregate size budget \u2014 later queries may return a skipped stub.",
9384
+ description: "Batch mode \u2014 array of query objects to run in a single call. Each uses the same param schema (including `project` / `worktree` / `project_root`). Top-level root args are inherited by sub-queries that don't specify their own. Subject to an aggregate size budget \u2014 later queries may return a skipped stub.",
8473
9385
  items: {
8474
9386
  type: "object",
8475
9387
  properties: {
@@ -8481,13 +9393,24 @@ var init_graph_mcp = __esm({
8481
9393
  hops: { type: "number" },
8482
9394
  minimal: { type: "boolean" },
8483
9395
  include_edges: { type: "boolean" },
8484
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9396
+ include_findings: { type: "boolean" },
9397
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9398
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9399
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8485
9400
  }
8486
9401
  }
8487
9402
  },
8488
9403
  project: {
8489
9404
  type: "string",
8490
9405
  description: PROJECT_PARAM_DESCRIPTION
9406
+ },
9407
+ worktree: {
9408
+ type: "string",
9409
+ description: WORKTREE_PARAM_DESCRIPTION2
9410
+ },
9411
+ project_root: {
9412
+ type: "string",
9413
+ description: PROJECT_ROOT_PARAM_DESCRIPTION2
8491
9414
  }
8492
9415
  }
8493
9416
  }
@@ -8535,7 +9458,9 @@ Returns: { pattern, filter, files_searched, total_matches, matches: [{file, line
8535
9458
  context: { type: "number", description: "Context lines around each match. Default 2." },
8536
9459
  max_matches: { type: "number", description: "Max matches to return total. Default 50." },
8537
9460
  max_files: { type: "number", description: "Max files to search. Default 50." },
8538
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9461
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9462
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9463
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8539
9464
  },
8540
9465
  required: ["layer", "pattern"]
8541
9466
  }
@@ -8577,7 +9502,9 @@ Returns deep fields only \u2014 not structural metadata (use read_graph for that
8577
9502
  type: "boolean",
8578
9503
  description: "Case-insensitive filter matching. Default true."
8579
9504
  },
8580
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9505
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9506
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9507
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8581
9508
  },
8582
9509
  required: ["layer"]
8583
9510
  }
@@ -8628,7 +9555,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
8628
9555
  type: "string",
8629
9556
  description: 'Optional specific key to look up within the chosen kind (e.g. "moon-shadow-blur"). When omitted, returns the full {key:nodes} map for the kind.'
8630
9557
  },
8631
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9558
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9559
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9560
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8632
9561
  }
8633
9562
  }
8634
9563
  },
@@ -8658,7 +9587,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
8658
9587
  type: "string",
8659
9588
  description: 'Tag value (e.g. "auth", "alice", "true").'
8660
9589
  },
8661
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9590
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9591
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9592
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8662
9593
  },
8663
9594
  required: ["node_id", "key", "value"]
8664
9595
  }
@@ -8677,7 +9608,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
8677
9608
  type: "string",
8678
9609
  description: "Tag key to remove."
8679
9610
  },
8680
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9611
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9612
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9613
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8681
9614
  },
8682
9615
  required: ["node_id", "key"]
8683
9616
  }
@@ -8696,7 +9629,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
8696
9629
  type: "string",
8697
9630
  description: "Specific check to run (e.g. 'schema_drift', 'unprotected_routes'). Omit to run all checks for the layer."
8698
9631
  },
8699
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9632
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9633
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9634
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8700
9635
  },
8701
9636
  required: ["layer"]
8702
9637
  }
@@ -8732,12 +9667,59 @@ Example: blast_points(node_id: "server/auth/middleware.ts", hops: 2) \u2192 retu
8732
9667
  enum: ["reverse", "both"],
8733
9668
  description: "'reverse' (default) = only what depends on this node. 'both' = full neighborhood."
8734
9669
  },
8735
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
9670
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9671
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9672
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
8736
9673
  },
8737
9674
  required: ["node_id"]
8738
9675
  }
9676
+ },
9677
+ {
9678
+ name: "list_notes",
9679
+ description: 'List tagged comments across the project graph \u2014 TODO, FIXME, HACK, NOTE, SECURITY, etc. Any `// KIND: body` or `/* KIND: body */` comment where KIND is an all-caps identifier (2\u201316 chars) is captured automatically; team conventions like `// CLAUDE:` are discovered without configuration.\n\nKinds are categorized at read time:\n actionable: TODO, FIXME, HACK, XXX, REFACTOR, OPTIMIZE, REVIEW\n warning: SECURITY, WARNING, IMPORTANT, CAUTION, DANGER, DEPRECATED\n doc: NOTE, TIP, SEE, NB, INFO, EXAMPLE\n custom: anything else (team-specific conventions)\n\nDefault category filter is `actionable,warning` \u2014 the "what should I act on" view. Pass category:"all" for everything including doc + custom.\n\nReturns: { total, returned, has_more, items: [{file, line, kind, category, text, author?, module?}] }.',
9680
+ inputSchema: {
9681
+ type: "object",
9682
+ properties: {
9683
+ layer: {
9684
+ type: "string",
9685
+ description: "Restrict to one layer ('ui', 'api', 'db'). Default: scan all layers that have notes."
9686
+ },
9687
+ kind: {
9688
+ type: "string",
9689
+ description: 'Exact kind match, case-insensitive (e.g. "TODO", "FIXME", "SECURITY").'
9690
+ },
9691
+ category: {
9692
+ type: "string",
9693
+ description: "'actionable' | 'warning' | 'doc' | 'custom' | 'all'. Comma-separated list also accepted. Default: 'actionable,warning'."
9694
+ },
9695
+ module: {
9696
+ type: "string",
9697
+ description: 'Restrict to nodes carrying this module tag (e.g. "auth", "work-items").'
9698
+ },
9699
+ pattern: {
9700
+ type: "string",
9701
+ description: 'Regex applied to note body text (case-insensitive). Use to grep within bodies, e.g. "race condition".'
9702
+ },
9703
+ author: {
9704
+ type: "string",
9705
+ description: "Match author from `// TODO(alice): \u2026` syntax, case-insensitive."
9706
+ },
9707
+ limit: {
9708
+ type: "number",
9709
+ description: "Max items returned. Default 100. Pair with offset for pagination."
9710
+ },
9711
+ offset: {
9712
+ type: "number",
9713
+ description: "Skip first N items. Default 0."
9714
+ },
9715
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
9716
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
9717
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
9718
+ }
9719
+ }
8739
9720
  }
8740
9721
  ];
9722
+ MINIMAL_STRIP_FIELDS = /* @__PURE__ */ new Set(["columns"]);
8741
9723
  COMPACT_SCHEMA = {
8742
9724
  nodes: {
8743
9725
  i: "id",
@@ -8777,8 +9759,31 @@ Example: blast_points(node_id: "server/auth/middleware.ts", hops: 2) \u2192 retu
8777
9759
  "variables",
8778
9760
  "responses",
8779
9761
  "params",
8780
- "effects"
9762
+ "effects",
9763
+ "ui_labels",
9764
+ "notes"
8781
9765
  ]);
9766
+ NOTE_KIND_CATEGORY = {
9767
+ TODO: "actionable",
9768
+ FIXME: "actionable",
9769
+ HACK: "actionable",
9770
+ XXX: "actionable",
9771
+ REFACTOR: "actionable",
9772
+ OPTIMIZE: "actionable",
9773
+ REVIEW: "actionable",
9774
+ SECURITY: "warning",
9775
+ WARNING: "warning",
9776
+ IMPORTANT: "warning",
9777
+ CAUTION: "warning",
9778
+ DANGER: "warning",
9779
+ DEPRECATED: "warning",
9780
+ NOTE: "doc",
9781
+ TIP: "doc",
9782
+ SEE: "doc",
9783
+ NB: "doc",
9784
+ INFO: "doc",
9785
+ EXAMPLE: "doc"
9786
+ };
8782
9787
  EST_CHARS_PER_NODE_FULL = {
8783
9788
  ui: 300,
8784
9789
  api: 300,
@@ -8805,44 +9810,7 @@ Example: blast_points(node_id: "server/auth/middleware.ts", hops: 2) \u2192 retu
8805
9810
  });
8806
9811
 
8807
9812
  // src/server/graph-mcp-entry.ts
8808
- var import_node_child_process3 = require("node:child_process");
8809
- var import_node_fs25 = require("node:fs");
8810
- var import_node_path29 = __toESM(require("node:path"));
8811
- var import_node_os3 = require("node:os");
8812
- var import_node_fs26 = require("node:fs");
8813
- init_launch_kit_paths();
8814
9813
  init_lockfile();
8815
- function logStderr(msg) {
8816
- process.stderr.write(`[launch-chart] ${msg}
8817
- `);
8818
- }
8819
- function maybeAutoServe() {
8820
- if (process.env.LAUNCH_CHART_AUTOSERVE !== "1") return;
8821
- const rootDir = process.cwd();
8822
- setProjectRoot(rootDir);
8823
- const existing = getLiveLock(rootDir);
8824
- if (existing) {
8825
- logStderr(`autoserve: reusing existing server at ${existing.url}`);
8826
- return;
8827
- }
8828
- try {
8829
- const logDir = import_node_path29.default.join((0, import_node_os3.homedir)(), LAUNCHSECURE_DIR);
8830
- (0, import_node_fs26.mkdirSync)(logDir, { recursive: true });
8831
- const logPath = import_node_path29.default.join(logDir, "launch-chart.log");
8832
- const out = (0, import_node_fs25.openSync)(logPath, "a");
8833
- const err2 = (0, import_node_fs25.openSync)(logPath, "a");
8834
- const entryPath = process.argv[1];
8835
- const child = (0, import_node_child_process3.spawn)(process.execPath, [entryPath, "serve"], {
8836
- detached: true,
8837
- stdio: ["ignore", out, err2],
8838
- env: { ...process.env, LAUNCH_CHART_AUTOSERVE: "" }
8839
- });
8840
- child.unref();
8841
- logStderr(`autoserve: spawned detached serve process (pid ${child.pid}, log: ${logPath})`);
8842
- } catch (err2) {
8843
- logStderr(`autoserve: failed to spawn \u2014 ${err2}`);
8844
- }
8845
- }
8846
9814
  async function main() {
8847
9815
  setProjectRoot(process.cwd());
8848
9816
  const argv = process.argv.slice(2);
@@ -8858,7 +9826,6 @@ async function main() {
8858
9826
  await handleGraphCommand2(mapped, argv.slice(1));
8859
9827
  process.exit(0);
8860
9828
  }
8861
- maybeAutoServe();
8862
9829
  const { startGraphMcpServer: startGraphMcpServer2 } = await Promise.resolve().then(() => (init_graph_mcp(), graph_mcp_exports));
8863
9830
  startGraphMcpServer2();
8864
9831
  }