@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
@@ -553,8 +553,8 @@ var require_claude_bridge = __commonJS({
553
553
  "../claude-code-web/src/claude-bridge.js"(exports2, module2) {
554
554
  "use strict";
555
555
  var { spawn: spawn4 } = require("node-pty");
556
- var path12 = require("path");
557
- var fs10 = require("fs");
556
+ var path13 = require("path");
557
+ var fs11 = require("fs");
558
558
  var ClaudeBridge = class {
559
559
  constructor() {
560
560
  this.sessions = /* @__PURE__ */ new Map();
@@ -565,14 +565,14 @@ var require_claude_bridge = __commonJS({
565
565
  "/home/ec2-user/.claude/local/claude",
566
566
  "claude",
567
567
  "claude-code",
568
- path12.join(process.env.HOME || "/", ".claude", "local", "claude"),
569
- path12.join(process.env.HOME || "/", ".local", "bin", "claude"),
568
+ path13.join(process.env.HOME || "/", ".claude", "local", "claude"),
569
+ path13.join(process.env.HOME || "/", ".local", "bin", "claude"),
570
570
  "/usr/local/bin/claude",
571
571
  "/usr/bin/claude"
572
572
  ];
573
573
  for (const cmd of possibleCommands) {
574
574
  try {
575
- if (fs10.existsSync(cmd) || this.commandExists(cmd)) {
575
+ if (fs11.existsSync(cmd) || this.commandExists(cmd)) {
576
576
  console.log(`Found Claude command at: ${cmd}`);
577
577
  return cmd;
578
578
  }
@@ -797,8 +797,8 @@ var require_codex_bridge = __commonJS({
797
797
  "../claude-code-web/src/codex-bridge.js"(exports2, module2) {
798
798
  "use strict";
799
799
  var { spawn: spawn4 } = require("node-pty");
800
- var path12 = require("path");
801
- var fs10 = require("fs");
800
+ var path13 = require("path");
801
+ var fs11 = require("fs");
802
802
  var CodexBridge = class {
803
803
  constructor() {
804
804
  this.sessions = /* @__PURE__ */ new Map();
@@ -806,16 +806,16 @@ var require_codex_bridge = __commonJS({
806
806
  }
807
807
  findCodexCommand() {
808
808
  const possibleCommands = [
809
- path12.join(process.env.HOME || "/", ".codex", "local", "codex"),
809
+ path13.join(process.env.HOME || "/", ".codex", "local", "codex"),
810
810
  "codex",
811
811
  "codex-code",
812
- path12.join(process.env.HOME || "/", ".local", "bin", "codex"),
812
+ path13.join(process.env.HOME || "/", ".local", "bin", "codex"),
813
813
  "/usr/local/bin/codex",
814
814
  "/usr/bin/codex"
815
815
  ];
816
816
  for (const cmd of possibleCommands) {
817
817
  try {
818
- if (fs10.existsSync(cmd) || this.commandExists(cmd)) {
818
+ if (fs11.existsSync(cmd) || this.commandExists(cmd)) {
819
819
  console.log(`Found Codex command at: ${cmd}`);
820
820
  return cmd;
821
821
  }
@@ -990,8 +990,8 @@ var require_agent_bridge = __commonJS({
990
990
  "../claude-code-web/src/agent-bridge.js"(exports2, module2) {
991
991
  "use strict";
992
992
  var { spawn: spawn4 } = require("node-pty");
993
- var path12 = require("path");
994
- var fs10 = require("fs");
993
+ var path13 = require("path");
994
+ var fs11 = require("fs");
995
995
  var AgentBridge = class {
996
996
  constructor() {
997
997
  this.sessions = /* @__PURE__ */ new Map();
@@ -999,15 +999,15 @@ var require_agent_bridge = __commonJS({
999
999
  }
1000
1000
  findAgentCommand() {
1001
1001
  const possibleCommands = [
1002
- path12.join(process.env.HOME || "/", ".cursor", "local", "cursor-agent"),
1002
+ path13.join(process.env.HOME || "/", ".cursor", "local", "cursor-agent"),
1003
1003
  "cursor-agent",
1004
- path12.join(process.env.HOME || "/", ".local", "bin", "cursor-agent"),
1004
+ path13.join(process.env.HOME || "/", ".local", "bin", "cursor-agent"),
1005
1005
  "/usr/local/bin/cursor-agent",
1006
1006
  "/usr/bin/cursor-agent"
1007
1007
  ];
1008
1008
  for (const cmd of possibleCommands) {
1009
1009
  try {
1010
- if (fs10.existsSync(cmd) || this.commandExists(cmd)) {
1010
+ if (fs11.existsSync(cmd) || this.commandExists(cmd)) {
1011
1011
  console.log(`Found Agent command at: ${cmd}`);
1012
1012
  return cmd;
1013
1013
  }
@@ -1331,25 +1331,25 @@ var require_script_bridge = __commonJS({
1331
1331
  var require_session_store = __commonJS({
1332
1332
  "../claude-code-web/src/utils/session-store.js"(exports2, module2) {
1333
1333
  "use strict";
1334
- var fs10 = require("fs").promises;
1335
- var path12 = require("path");
1334
+ var fs11 = require("fs").promises;
1335
+ var path13 = require("path");
1336
1336
  var os4 = require("os");
1337
1337
  var SessionStore = class {
1338
1338
  constructor(options = {}) {
1339
- this.storageDir = options.storageDir || path12.join(os4.homedir(), ".claude-code-web");
1340
- this.sessionsFile = path12.join(this.storageDir, "sessions.json");
1339
+ this.storageDir = options.storageDir || path13.join(os4.homedir(), ".claude-code-web");
1340
+ this.sessionsFile = path13.join(this.storageDir, "sessions.json");
1341
1341
  this.initializeStorage();
1342
1342
  }
1343
1343
  async initializeStorage() {
1344
1344
  try {
1345
- await fs10.mkdir(this.storageDir, { recursive: true });
1345
+ await fs11.mkdir(this.storageDir, { recursive: true });
1346
1346
  } catch (error) {
1347
1347
  console.error("Failed to create storage directory:", error);
1348
1348
  }
1349
1349
  }
1350
1350
  async saveSessions(sessions) {
1351
1351
  try {
1352
- await fs10.mkdir(this.storageDir, { recursive: true });
1352
+ await fs11.mkdir(this.storageDir, { recursive: true });
1353
1353
  const sessionsArray = Array.from(sessions.entries()).map(([id, session]) => ({
1354
1354
  id,
1355
1355
  name: session.name || "Unnamed Session",
@@ -1380,9 +1380,9 @@ var require_session_store = __commonJS({
1380
1380
  sessions: sessionsArray
1381
1381
  };
1382
1382
  const tempFile = `${this.sessionsFile}.tmp`;
1383
- await fs10.writeFile(tempFile, JSON.stringify(data, null, 2));
1384
- await fs10.mkdir(this.storageDir, { recursive: true });
1385
- await fs10.rename(tempFile, this.sessionsFile);
1383
+ await fs11.writeFile(tempFile, JSON.stringify(data, null, 2));
1384
+ await fs11.mkdir(this.storageDir, { recursive: true });
1385
+ await fs11.rename(tempFile, this.sessionsFile);
1386
1386
  return true;
1387
1387
  } catch (error) {
1388
1388
  console.error("Failed to save sessions:", error.message);
@@ -1391,8 +1391,8 @@ var require_session_store = __commonJS({
1391
1391
  }
1392
1392
  async loadSessions() {
1393
1393
  try {
1394
- await fs10.access(this.sessionsFile);
1395
- const data = await fs10.readFile(this.sessionsFile, "utf8");
1394
+ await fs11.access(this.sessionsFile);
1395
+ const data = await fs11.readFile(this.sessionsFile, "utf8");
1396
1396
  if (!data || !data.trim()) {
1397
1397
  console.log("Sessions file is empty, starting fresh");
1398
1398
  return /* @__PURE__ */ new Map();
@@ -1403,7 +1403,7 @@ var require_session_store = __commonJS({
1403
1403
  } catch (parseError) {
1404
1404
  console.error("Sessions file is corrupted, starting fresh:", parseError.message);
1405
1405
  try {
1406
- await fs10.rename(this.sessionsFile, `${this.sessionsFile}.corrupted.${Date.now()}`);
1406
+ await fs11.rename(this.sessionsFile, `${this.sessionsFile}.corrupted.${Date.now()}`);
1407
1407
  } catch (renameError) {
1408
1408
  }
1409
1409
  return /* @__PURE__ */ new Map();
@@ -1447,7 +1447,7 @@ var require_session_store = __commonJS({
1447
1447
  }
1448
1448
  async clearOldSessions() {
1449
1449
  try {
1450
- await fs10.unlink(this.sessionsFile);
1450
+ await fs11.unlink(this.sessionsFile);
1451
1451
  console.log("Cleared old sessions");
1452
1452
  return true;
1453
1453
  } catch (error) {
@@ -1459,9 +1459,9 @@ var require_session_store = __commonJS({
1459
1459
  }
1460
1460
  async getSessionMetadata() {
1461
1461
  try {
1462
- await fs10.access(this.sessionsFile);
1463
- const stats = await fs10.stat(this.sessionsFile);
1464
- const data = await fs10.readFile(this.sessionsFile, "utf8");
1462
+ await fs11.access(this.sessionsFile);
1463
+ const stats = await fs11.stat(this.sessionsFile);
1464
+ const data = await fs11.readFile(this.sessionsFile, "utf8");
1465
1465
  const parsed = JSON.parse(data);
1466
1466
  return {
1467
1467
  exists: true,
@@ -1486,13 +1486,13 @@ var require_session_store = __commonJS({
1486
1486
  var require_usage_reader = __commonJS({
1487
1487
  "../claude-code-web/src/usage-reader.js"(exports2, module2) {
1488
1488
  "use strict";
1489
- var fs10 = require("fs").promises;
1490
- var path12 = require("path");
1489
+ var fs11 = require("fs").promises;
1490
+ var path13 = require("path");
1491
1491
  var readline = require("readline");
1492
1492
  var { createReadStream } = require("fs");
1493
1493
  var UsageReader = class {
1494
1494
  constructor(sessionDurationHours = 5) {
1495
- this.claudeProjectsPath = path12.join(process.env.HOME, ".claude", "projects");
1495
+ this.claudeProjectsPath = path13.join(process.env.HOME, ".claude", "projects");
1496
1496
  this.cache = null;
1497
1497
  this.cacheTime = null;
1498
1498
  this.cacheTimeout = 5e3;
@@ -1692,14 +1692,14 @@ var require_usage_reader = __commonJS({
1692
1692
  try {
1693
1693
  const cwd = process.cwd();
1694
1694
  const projectDirName = cwd.replace(/\//g, "-");
1695
- let projectPath = path12.join(this.claudeProjectsPath, projectDirName);
1695
+ let projectPath = path13.join(this.claudeProjectsPath, projectDirName);
1696
1696
  try {
1697
- await fs10.access(projectPath);
1697
+ await fs11.access(projectPath);
1698
1698
  } catch (err2) {
1699
1699
  console.log(`Project directory not found: ${projectPath}`);
1700
1700
  return null;
1701
1701
  }
1702
- const files = await fs10.readdir(projectPath);
1702
+ const files = await fs11.readdir(projectPath);
1703
1703
  const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
1704
1704
  if (jsonlFiles.length === 0) {
1705
1705
  return null;
@@ -1707,8 +1707,8 @@ var require_usage_reader = __commonJS({
1707
1707
  let mostRecentFile = null;
1708
1708
  let mostRecentTime = 0;
1709
1709
  for (const file of jsonlFiles) {
1710
- const filePath = path12.join(projectPath, file);
1711
- const stat = await fs10.stat(filePath);
1710
+ const filePath = path13.join(projectPath, file);
1711
+ const stat = await fs11.stat(filePath);
1712
1712
  if (stat.mtime.getTime() > mostRecentTime) {
1713
1713
  mostRecentTime = stat.mtime.getTime();
1714
1714
  mostRecentFile = filePath;
@@ -1723,17 +1723,17 @@ var require_usage_reader = __commonJS({
1723
1723
  async findJsonlFiles(onlyRecent = false) {
1724
1724
  const files = [];
1725
1725
  try {
1726
- const projectDirs = await fs10.readdir(this.claudeProjectsPath);
1726
+ const projectDirs = await fs11.readdir(this.claudeProjectsPath);
1727
1727
  for (const projectDir of projectDirs) {
1728
- const projectPath = path12.join(this.claudeProjectsPath, projectDir);
1729
- const stat = await fs10.stat(projectPath);
1728
+ const projectPath = path13.join(this.claudeProjectsPath, projectDir);
1729
+ const stat = await fs11.stat(projectPath);
1730
1730
  if (stat.isDirectory()) {
1731
- const projectFiles = await fs10.readdir(projectPath);
1731
+ const projectFiles = await fs11.readdir(projectPath);
1732
1732
  const jsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
1733
1733
  for (const jsonlFile of jsonlFiles) {
1734
- const filePath = path12.join(projectPath, jsonlFile);
1734
+ const filePath = path13.join(projectPath, jsonlFile);
1735
1735
  if (onlyRecent) {
1736
- const fileStat = await fs10.stat(filePath);
1736
+ const fileStat = await fs11.stat(filePath);
1737
1737
  const hoursSinceModified = (Date.now() - fileStat.mtime.getTime()) / (1e3 * 60 * 60);
1738
1738
  if (hoursSinceModified <= 24) {
1739
1739
  files.push(filePath);
@@ -1752,7 +1752,7 @@ var require_usage_reader = __commonJS({
1752
1752
  async readJsonlFile(filePath, cutoffTime) {
1753
1753
  const entries = [];
1754
1754
  const fileProcessedEntries = /* @__PURE__ */ new Set();
1755
- return new Promise((resolve5) => {
1755
+ return new Promise((resolve6) => {
1756
1756
  const rl = readline.createInterface({
1757
1757
  input: createReadStream(filePath),
1758
1758
  crlfDelay: Infinity
@@ -1810,11 +1810,11 @@ var require_usage_reader = __commonJS({
1810
1810
  }
1811
1811
  });
1812
1812
  rl.on("close", () => {
1813
- resolve5(entries);
1813
+ resolve6(entries);
1814
1814
  });
1815
1815
  rl.on("error", (error) => {
1816
1816
  console.error("Error reading file:", filePath, error);
1817
- resolve5(entries);
1817
+ resolve6(entries);
1818
1818
  });
1819
1819
  });
1820
1820
  }
@@ -1894,9 +1894,9 @@ var require_usage_reader = __commonJS({
1894
1894
  if (!sessionId) {
1895
1895
  return null;
1896
1896
  }
1897
- const sessionFile = path12.join(this.claudeProjectsPath, path12.basename(process.cwd()).replace(/[^a-zA-Z0-9-]/g, "-"), `${sessionId}.jsonl`);
1897
+ const sessionFile = path13.join(this.claudeProjectsPath, path13.basename(process.cwd()).replace(/[^a-zA-Z0-9-]/g, "-"), `${sessionId}.jsonl`);
1898
1898
  try {
1899
- await fs10.access(sessionFile);
1899
+ await fs11.access(sessionFile);
1900
1900
  } catch (err2) {
1901
1901
  return null;
1902
1902
  }
@@ -2552,7 +2552,7 @@ var require_usage_analytics = __commonJS({
2552
2552
  var require_src = __commonJS({
2553
2553
  "../claude-code-web/src/index.js"(exports2, module2) {
2554
2554
  "use strict";
2555
- var path12 = require("path");
2555
+ var path13 = require("path");
2556
2556
  var WebSocket2 = require("ws");
2557
2557
  var { v4: uuidv4 } = (init_esm_node(), __toCommonJS(esm_node_exports));
2558
2558
  var ClaudeBridge = require_claude_bridge();
@@ -2562,7 +2562,7 @@ var require_src = __commonJS({
2562
2562
  var SessionStore = require_session_store();
2563
2563
  var UsageReader = require_usage_reader();
2564
2564
  var UsageAnalytics = require_usage_analytics();
2565
- var fs10 = require("fs");
2565
+ var fs11 = require("fs");
2566
2566
  function stripAnsi2(str) {
2567
2567
  return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\x1b[()][AB012]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").replace(/\r\n?/g, "\n").trim();
2568
2568
  }
@@ -2637,8 +2637,8 @@ var require_src = __commonJS({
2637
2637
  // ── Path validation ──────────────────────────────────────────────────
2638
2638
  isPathWithinBase(targetPath) {
2639
2639
  try {
2640
- const resolvedTarget = path12.resolve(targetPath);
2641
- const resolvedBase = path12.resolve(this.baseFolder);
2640
+ const resolvedTarget = path13.resolve(targetPath);
2641
+ const resolvedBase = path13.resolve(this.baseFolder);
2642
2642
  return resolvedTarget.startsWith(resolvedBase);
2643
2643
  } catch (error) {
2644
2644
  return false;
@@ -2648,7 +2648,7 @@ var require_src = __commonJS({
2648
2648
  if (!targetPath) {
2649
2649
  return { valid: false, error: "Path is required" };
2650
2650
  }
2651
- const resolvedPath = path12.resolve(targetPath);
2651
+ const resolvedPath = path13.resolve(targetPath);
2652
2652
  if (!this.isPathWithinBase(resolvedPath)) {
2653
2653
  return {
2654
2654
  valid: false,
@@ -2820,14 +2820,14 @@ var require_src = __commonJS({
2820
2820
  return true;
2821
2821
  }
2822
2822
  try {
2823
- const items = fs10.readdirSync(validation.path, { withFileTypes: true });
2823
+ const items = fs11.readdirSync(validation.path, { withFileTypes: true });
2824
2824
  const showHidden = url.searchParams.get("showHidden") === "true";
2825
2825
  const folders = items.filter((item) => item.isDirectory()).filter((item) => !item.name.startsWith(".") || showHidden).map((item) => ({
2826
2826
  name: item.name,
2827
- path: path12.join(validation.path, item.name),
2827
+ path: path13.join(validation.path, item.name),
2828
2828
  isDirectory: true
2829
2829
  })).sort((a, b) => a.name.localeCompare(b.name));
2830
- const parentDir = path12.dirname(validation.path);
2830
+ const parentDir = path13.dirname(validation.path);
2831
2831
  const canGoUp = this.isPathWithinBase(parentDir) && parentDir !== validation.path;
2832
2832
  res.writeHead(200, { "Content-Type": "application/json" });
2833
2833
  res.end(JSON.stringify({
@@ -2855,7 +2855,7 @@ var require_src = __commonJS({
2855
2855
  res.end(JSON.stringify({ error: validation.error }));
2856
2856
  return;
2857
2857
  }
2858
- if (!fs10.existsSync(validation.path) || !fs10.statSync(validation.path).isDirectory()) {
2858
+ if (!fs11.existsSync(validation.path) || !fs11.statSync(validation.path).isDirectory()) {
2859
2859
  res.writeHead(400, { "Content-Type": "application/json" });
2860
2860
  res.end(JSON.stringify({ error: "Not a valid directory" }));
2861
2861
  return;
@@ -2882,7 +2882,7 @@ var require_src = __commonJS({
2882
2882
  res.end(JSON.stringify({ error: validation.error }));
2883
2883
  return;
2884
2884
  }
2885
- if (!fs10.existsSync(validation.path) || !fs10.statSync(validation.path).isDirectory()) {
2885
+ if (!fs11.existsSync(validation.path) || !fs11.statSync(validation.path).isDirectory()) {
2886
2886
  res.writeHead(400, { "Content-Type": "application/json" });
2887
2887
  res.end(JSON.stringify({ error: "Invalid directory path" }));
2888
2888
  return;
@@ -2909,7 +2909,7 @@ var require_src = __commonJS({
2909
2909
  return;
2910
2910
  }
2911
2911
  const basePath = parentPath || this.baseFolder;
2912
- const fullPath = path12.join(basePath, folderName);
2912
+ const fullPath = path13.join(basePath, folderName);
2913
2913
  const parentValidation = this.validatePath(basePath);
2914
2914
  const fullValidation = this.validatePath(fullPath);
2915
2915
  if (!parentValidation.valid || !fullValidation.valid) {
@@ -2917,12 +2917,12 @@ var require_src = __commonJS({
2917
2917
  res.end(JSON.stringify({ message: "Cannot create folder outside the allowed area" }));
2918
2918
  return;
2919
2919
  }
2920
- if (fs10.existsSync(fullValidation.path)) {
2920
+ if (fs11.existsSync(fullValidation.path)) {
2921
2921
  res.writeHead(409, { "Content-Type": "application/json" });
2922
2922
  res.end(JSON.stringify({ message: "Folder already exists" }));
2923
2923
  return;
2924
2924
  }
2925
- fs10.mkdirSync(fullValidation.path, { recursive: true });
2925
+ fs11.mkdirSync(fullValidation.path, { recursive: true });
2926
2926
  res.writeHead(200, { "Content-Type": "application/json" });
2927
2927
  res.end(JSON.stringify({ success: true, path: fullValidation.path }));
2928
2928
  } catch (e) {
@@ -2946,10 +2946,10 @@ var require_src = __commonJS({
2946
2946
  const url = new URL(req.url || "/", `http://localhost`);
2947
2947
  let filePath = url.pathname.replace(/^\/terminal\/?/, "");
2948
2948
  if (!filePath || filePath === "") filePath = "index.html";
2949
- const fullPath = path12.join(__dirname, "public", filePath);
2949
+ const fullPath = path13.join(__dirname, "public", filePath);
2950
2950
  try {
2951
- if (!fs10.existsSync(fullPath)) return false;
2952
- const ext = path12.extname(fullPath);
2951
+ if (!fs11.existsSync(fullPath)) return false;
2952
+ const ext = path13.extname(fullPath);
2953
2953
  const mimeTypes = {
2954
2954
  ".html": "text/html",
2955
2955
  ".js": "application/javascript",
@@ -2960,7 +2960,7 @@ var require_src = __commonJS({
2960
2960
  ".ico": "image/x-icon"
2961
2961
  };
2962
2962
  const mime = mimeTypes[ext] || "application/octet-stream";
2963
- const content = fs10.readFileSync(fullPath);
2963
+ const content = fs11.readFileSync(fullPath);
2964
2964
  res.writeHead(200, { "Content-Type": mime });
2965
2965
  res.end(content);
2966
2966
  return true;
@@ -3623,7 +3623,7 @@ var require_src = __commonJS({
3623
3623
  if (session.active) throw new Error(`Agent already running in session ${sessionId}`);
3624
3624
  const { command, args = [], env = {} } = options;
3625
3625
  if (!command) throw new Error("startScriptInSession requires a command");
3626
- return new Promise((resolve5, reject) => {
3626
+ return new Promise((resolve6, reject) => {
3627
3627
  this.scriptBridge.startSession(sessionId, {
3628
3628
  command,
3629
3629
  args,
@@ -3645,7 +3645,7 @@ var require_src = __commonJS({
3645
3645
  session.lastActivity = /* @__PURE__ */ new Date();
3646
3646
  this.broadcastToSession(sessionId, { type: "script_stopped", sessionId });
3647
3647
  if (exitCode === 0) {
3648
- resolve5({ code: exitCode, signal });
3648
+ resolve6({ code: exitCode, signal });
3649
3649
  } else {
3650
3650
  reject(new Error(`Script exited with code ${exitCode}`));
3651
3651
  }
@@ -4553,14 +4553,14 @@ var require_util = __commonJS({
4553
4553
  }
4554
4554
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
4555
4555
  let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
4556
- let path12 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
4556
+ let path13 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
4557
4557
  if (origin[origin.length - 1] === "/") {
4558
4558
  origin = origin.slice(0, origin.length - 1);
4559
4559
  }
4560
- if (path12 && path12[0] !== "/") {
4561
- path12 = `/${path12}`;
4560
+ if (path13 && path13[0] !== "/") {
4561
+ path13 = `/${path13}`;
4562
4562
  }
4563
- return new URL(`${origin}${path12}`);
4563
+ return new URL(`${origin}${path13}`);
4564
4564
  }
4565
4565
  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
4566
4566
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -5011,39 +5011,39 @@ var require_diagnostics = __commonJS({
5011
5011
  });
5012
5012
  diagnosticsChannel.channel("undici:client:sendHeaders").subscribe((evt) => {
5013
5013
  const {
5014
- request: { method, path: path12, origin }
5014
+ request: { method, path: path13, origin }
5015
5015
  } = evt;
5016
- debuglog("sending request to %s %s/%s", method, origin, path12);
5016
+ debuglog("sending request to %s %s/%s", method, origin, path13);
5017
5017
  });
5018
5018
  diagnosticsChannel.channel("undici:request:headers").subscribe((evt) => {
5019
5019
  const {
5020
- request: { method, path: path12, origin },
5020
+ request: { method, path: path13, origin },
5021
5021
  response: { statusCode }
5022
5022
  } = evt;
5023
5023
  debuglog(
5024
5024
  "received response to %s %s/%s - HTTP %d",
5025
5025
  method,
5026
5026
  origin,
5027
- path12,
5027
+ path13,
5028
5028
  statusCode
5029
5029
  );
5030
5030
  });
5031
5031
  diagnosticsChannel.channel("undici:request:trailers").subscribe((evt) => {
5032
5032
  const {
5033
- request: { method, path: path12, origin }
5033
+ request: { method, path: path13, origin }
5034
5034
  } = evt;
5035
- debuglog("trailers received from %s %s/%s", method, origin, path12);
5035
+ debuglog("trailers received from %s %s/%s", method, origin, path13);
5036
5036
  });
5037
5037
  diagnosticsChannel.channel("undici:request:error").subscribe((evt) => {
5038
5038
  const {
5039
- request: { method, path: path12, origin },
5039
+ request: { method, path: path13, origin },
5040
5040
  error
5041
5041
  } = evt;
5042
5042
  debuglog(
5043
5043
  "request to %s %s/%s errored - %s",
5044
5044
  method,
5045
5045
  origin,
5046
- path12,
5046
+ path13,
5047
5047
  error.message
5048
5048
  );
5049
5049
  });
@@ -5092,9 +5092,9 @@ var require_diagnostics = __commonJS({
5092
5092
  });
5093
5093
  diagnosticsChannel.channel("undici:client:sendHeaders").subscribe((evt) => {
5094
5094
  const {
5095
- request: { method, path: path12, origin }
5095
+ request: { method, path: path13, origin }
5096
5096
  } = evt;
5097
- debuglog("sending request to %s %s/%s", method, origin, path12);
5097
+ debuglog("sending request to %s %s/%s", method, origin, path13);
5098
5098
  });
5099
5099
  }
5100
5100
  diagnosticsChannel.channel("undici:websocket:open").subscribe((evt) => {
@@ -5157,7 +5157,7 @@ var require_request = __commonJS({
5157
5157
  var kHandler = /* @__PURE__ */ Symbol("handler");
5158
5158
  var Request = class {
5159
5159
  constructor(origin, {
5160
- path: path12,
5160
+ path: path13,
5161
5161
  method,
5162
5162
  body,
5163
5163
  headers,
@@ -5172,11 +5172,11 @@ var require_request = __commonJS({
5172
5172
  expectContinue,
5173
5173
  servername
5174
5174
  }, handler2) {
5175
- if (typeof path12 !== "string") {
5175
+ if (typeof path13 !== "string") {
5176
5176
  throw new InvalidArgumentError("path must be a string");
5177
- } else if (path12[0] !== "/" && !(path12.startsWith("http://") || path12.startsWith("https://")) && method !== "CONNECT") {
5177
+ } else if (path13[0] !== "/" && !(path13.startsWith("http://") || path13.startsWith("https://")) && method !== "CONNECT") {
5178
5178
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
5179
- } else if (invalidPathRegex.test(path12)) {
5179
+ } else if (invalidPathRegex.test(path13)) {
5180
5180
  throw new InvalidArgumentError("invalid request path");
5181
5181
  }
5182
5182
  if (typeof method !== "string") {
@@ -5242,7 +5242,7 @@ var require_request = __commonJS({
5242
5242
  this.completed = false;
5243
5243
  this.aborted = false;
5244
5244
  this.upgrade = upgrade || null;
5245
- this.path = query ? buildURL(path12, query) : path12;
5245
+ this.path = query ? buildURL(path13, query) : path13;
5246
5246
  this.origin = origin;
5247
5247
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
5248
5248
  this.blocking = blocking == null ? false : blocking;
@@ -5567,9 +5567,9 @@ var require_dispatcher_base = __commonJS({
5567
5567
  }
5568
5568
  close(callback) {
5569
5569
  if (callback === void 0) {
5570
- return new Promise((resolve5, reject) => {
5570
+ return new Promise((resolve6, reject) => {
5571
5571
  this.close((err2, data) => {
5572
- return err2 ? reject(err2) : resolve5(data);
5572
+ return err2 ? reject(err2) : resolve6(data);
5573
5573
  });
5574
5574
  });
5575
5575
  }
@@ -5607,12 +5607,12 @@ var require_dispatcher_base = __commonJS({
5607
5607
  err2 = null;
5608
5608
  }
5609
5609
  if (callback === void 0) {
5610
- return new Promise((resolve5, reject) => {
5610
+ return new Promise((resolve6, reject) => {
5611
5611
  this.destroy(err2, (err3, data) => {
5612
5612
  return err3 ? (
5613
5613
  /* istanbul ignore next: should never error */
5614
5614
  reject(err3)
5615
- ) : resolve5(data);
5615
+ ) : resolve6(data);
5616
5616
  });
5617
5617
  });
5618
5618
  }
@@ -7879,8 +7879,8 @@ var require_util2 = __commonJS({
7879
7879
  function createDeferredPromise() {
7880
7880
  let res;
7881
7881
  let rej;
7882
- const promise = new Promise((resolve5, reject) => {
7883
- res = resolve5;
7882
+ const promise = new Promise((resolve6, reject) => {
7883
+ res = resolve6;
7884
7884
  rej = reject;
7885
7885
  });
7886
7886
  return { promise, resolve: res, reject: rej };
@@ -9768,7 +9768,7 @@ var require_client_h1 = __commonJS({
9768
9768
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
9769
9769
  }
9770
9770
  function writeH1(client, request) {
9771
- const { method, path: path12, host, upgrade, blocking, reset } = request;
9771
+ const { method, path: path13, host, upgrade, blocking, reset } = request;
9772
9772
  let { body, headers, contentLength } = request;
9773
9773
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
9774
9774
  if (util.isFormDataLike(body)) {
@@ -9834,7 +9834,7 @@ var require_client_h1 = __commonJS({
9834
9834
  if (blocking) {
9835
9835
  socket[kBlocking] = true;
9836
9836
  }
9837
- let header = `${method} ${path12} HTTP/1.1\r
9837
+ let header = `${method} ${path13} HTTP/1.1\r
9838
9838
  `;
9839
9839
  if (typeof host === "string") {
9840
9840
  header += `host: ${host}\r
@@ -10021,12 +10021,12 @@ upgrade: ${upgrade}\r
10021
10021
  cb();
10022
10022
  }
10023
10023
  }
10024
- const waitForDrain = () => new Promise((resolve5, reject) => {
10024
+ const waitForDrain = () => new Promise((resolve6, reject) => {
10025
10025
  assert(callback === null);
10026
10026
  if (socket[kError]) {
10027
10027
  reject(socket[kError]);
10028
10028
  } else {
10029
- callback = resolve5;
10029
+ callback = resolve6;
10030
10030
  }
10031
10031
  });
10032
10032
  socket.on("close", onDrain).on("drain", onDrain);
@@ -10360,7 +10360,7 @@ var require_client_h2 = __commonJS({
10360
10360
  }
10361
10361
  function writeH2(client, request) {
10362
10362
  const session = client[kHTTP2Session];
10363
- const { method, path: path12, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
10363
+ const { method, path: path13, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
10364
10364
  let { body } = request;
10365
10365
  if (upgrade) {
10366
10366
  util.errorRequest(client, request, new Error("Upgrade not supported for H2"));
@@ -10427,7 +10427,7 @@ var require_client_h2 = __commonJS({
10427
10427
  });
10428
10428
  return true;
10429
10429
  }
10430
- headers[HTTP2_HEADER_PATH] = path12;
10430
+ headers[HTTP2_HEADER_PATH] = path13;
10431
10431
  headers[HTTP2_HEADER_SCHEME] = "https";
10432
10432
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
10433
10433
  if (body && typeof body.read === "function") {
@@ -10663,12 +10663,12 @@ var require_client_h2 = __commonJS({
10663
10663
  cb();
10664
10664
  }
10665
10665
  }
10666
- const waitForDrain = () => new Promise((resolve5, reject) => {
10666
+ const waitForDrain = () => new Promise((resolve6, reject) => {
10667
10667
  assert(callback === null);
10668
10668
  if (socket[kError]) {
10669
10669
  reject(socket[kError]);
10670
10670
  } else {
10671
- callback = resolve5;
10671
+ callback = resolve6;
10672
10672
  }
10673
10673
  });
10674
10674
  h2stream.on("close", onDrain).on("drain", onDrain);
@@ -10780,9 +10780,9 @@ var require_redirect_handler = __commonJS({
10780
10780
  return this.handler.onHeaders(statusCode, headers, resume, statusText);
10781
10781
  }
10782
10782
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
10783
- const path12 = search ? `${pathname}${search}` : pathname;
10783
+ const path13 = search ? `${pathname}${search}` : pathname;
10784
10784
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
10785
- this.opts.path = path12;
10785
+ this.opts.path = path13;
10786
10786
  this.opts.origin = origin;
10787
10787
  this.opts.maxRedirections = 0;
10788
10788
  this.opts.query = null;
@@ -11146,16 +11146,16 @@ var require_client = __commonJS({
11146
11146
  return this[kNeedDrain] < 2;
11147
11147
  }
11148
11148
  async [kClose]() {
11149
- return new Promise((resolve5) => {
11149
+ return new Promise((resolve6) => {
11150
11150
  if (this[kSize]) {
11151
- this[kClosedResolve] = resolve5;
11151
+ this[kClosedResolve] = resolve6;
11152
11152
  } else {
11153
- resolve5(null);
11153
+ resolve6(null);
11154
11154
  }
11155
11155
  });
11156
11156
  }
11157
11157
  async [kDestroy](err2) {
11158
- return new Promise((resolve5) => {
11158
+ return new Promise((resolve6) => {
11159
11159
  const requests = this[kQueue].splice(this[kPendingIdx]);
11160
11160
  for (let i = 0; i < requests.length; i++) {
11161
11161
  const request = requests[i];
@@ -11166,7 +11166,7 @@ var require_client = __commonJS({
11166
11166
  this[kClosedResolve]();
11167
11167
  this[kClosedResolve] = null;
11168
11168
  }
11169
- resolve5(null);
11169
+ resolve6(null);
11170
11170
  };
11171
11171
  if (this[kHTTPContext]) {
11172
11172
  this[kHTTPContext].destroy(err2, callback);
@@ -11217,7 +11217,7 @@ var require_client = __commonJS({
11217
11217
  });
11218
11218
  }
11219
11219
  try {
11220
- const socket = await new Promise((resolve5, reject) => {
11220
+ const socket = await new Promise((resolve6, reject) => {
11221
11221
  client[kConnector]({
11222
11222
  host,
11223
11223
  hostname,
@@ -11229,7 +11229,7 @@ var require_client = __commonJS({
11229
11229
  if (err2) {
11230
11230
  reject(err2);
11231
11231
  } else {
11232
- resolve5(socket2);
11232
+ resolve6(socket2);
11233
11233
  }
11234
11234
  });
11235
11235
  });
@@ -11565,8 +11565,8 @@ var require_pool_base = __commonJS({
11565
11565
  if (this[kQueue].isEmpty()) {
11566
11566
  await Promise.all(this[kClients].map((c) => c.close()));
11567
11567
  } else {
11568
- await new Promise((resolve5) => {
11569
- this[kClosedResolve] = resolve5;
11568
+ await new Promise((resolve6) => {
11569
+ this[kClosedResolve] = resolve6;
11570
11570
  });
11571
11571
  }
11572
11572
  }
@@ -12017,10 +12017,10 @@ var require_proxy_agent = __commonJS({
12017
12017
  };
12018
12018
  const {
12019
12019
  origin,
12020
- path: path12 = "/",
12020
+ path: path13 = "/",
12021
12021
  headers = {}
12022
12022
  } = opts;
12023
- opts.path = origin + path12;
12023
+ opts.path = origin + path13;
12024
12024
  if (!("host" in headers) && !("Host" in headers)) {
12025
12025
  const { host } = new URL3(origin);
12026
12026
  headers.host = host;
@@ -12781,7 +12781,7 @@ var require_readable = __commonJS({
12781
12781
  if (this._readableState.closeEmitted) {
12782
12782
  return null;
12783
12783
  }
12784
- return await new Promise((resolve5, reject) => {
12784
+ return await new Promise((resolve6, reject) => {
12785
12785
  if (this[kContentLength] > limit) {
12786
12786
  this.destroy(new AbortError());
12787
12787
  }
@@ -12794,7 +12794,7 @@ var require_readable = __commonJS({
12794
12794
  if (signal?.aborted) {
12795
12795
  reject(signal.reason ?? new AbortError());
12796
12796
  } else {
12797
- resolve5(null);
12797
+ resolve6(null);
12798
12798
  }
12799
12799
  }).on("error", noop).on("data", function(chunk) {
12800
12800
  limit -= chunk.length;
@@ -12813,7 +12813,7 @@ var require_readable = __commonJS({
12813
12813
  }
12814
12814
  async function consume(stream, type) {
12815
12815
  assert(!stream[kConsume]);
12816
- return new Promise((resolve5, reject) => {
12816
+ return new Promise((resolve6, reject) => {
12817
12817
  if (isUnusable(stream)) {
12818
12818
  const rState = stream._readableState;
12819
12819
  if (rState.destroyed && rState.closeEmitted === false) {
@@ -12830,7 +12830,7 @@ var require_readable = __commonJS({
12830
12830
  stream[kConsume] = {
12831
12831
  type,
12832
12832
  stream,
12833
- resolve: resolve5,
12833
+ resolve: resolve6,
12834
12834
  reject,
12835
12835
  length: 0,
12836
12836
  body: []
@@ -12900,18 +12900,18 @@ var require_readable = __commonJS({
12900
12900
  return buffer;
12901
12901
  }
12902
12902
  function consumeEnd(consume2) {
12903
- const { type, body, resolve: resolve5, stream, length } = consume2;
12903
+ const { type, body, resolve: resolve6, stream, length } = consume2;
12904
12904
  try {
12905
12905
  if (type === "text") {
12906
- resolve5(chunksDecode(body, length));
12906
+ resolve6(chunksDecode(body, length));
12907
12907
  } else if (type === "json") {
12908
- resolve5(JSON.parse(chunksDecode(body, length)));
12908
+ resolve6(JSON.parse(chunksDecode(body, length)));
12909
12909
  } else if (type === "arrayBuffer") {
12910
- resolve5(chunksConcat(body, length).buffer);
12910
+ resolve6(chunksConcat(body, length).buffer);
12911
12911
  } else if (type === "blob") {
12912
- resolve5(new Blob(body, { type: stream[kContentType] }));
12912
+ resolve6(new Blob(body, { type: stream[kContentType] }));
12913
12913
  } else if (type === "bytes") {
12914
- resolve5(chunksConcat(body, length));
12914
+ resolve6(chunksConcat(body, length));
12915
12915
  }
12916
12916
  consumeFinish(consume2);
12917
12917
  } catch (err2) {
@@ -13168,9 +13168,9 @@ var require_api_request = __commonJS({
13168
13168
  };
13169
13169
  function request(opts, callback) {
13170
13170
  if (callback === void 0) {
13171
- return new Promise((resolve5, reject) => {
13171
+ return new Promise((resolve6, reject) => {
13172
13172
  request.call(this, opts, (err2, data) => {
13173
- return err2 ? reject(err2) : resolve5(data);
13173
+ return err2 ? reject(err2) : resolve6(data);
13174
13174
  });
13175
13175
  });
13176
13176
  }
@@ -13393,9 +13393,9 @@ var require_api_stream = __commonJS({
13393
13393
  };
13394
13394
  function stream(opts, factory, callback) {
13395
13395
  if (callback === void 0) {
13396
- return new Promise((resolve5, reject) => {
13396
+ return new Promise((resolve6, reject) => {
13397
13397
  stream.call(this, opts, factory, (err2, data) => {
13398
- return err2 ? reject(err2) : resolve5(data);
13398
+ return err2 ? reject(err2) : resolve6(data);
13399
13399
  });
13400
13400
  });
13401
13401
  }
@@ -13680,9 +13680,9 @@ var require_api_upgrade = __commonJS({
13680
13680
  };
13681
13681
  function upgrade(opts, callback) {
13682
13682
  if (callback === void 0) {
13683
- return new Promise((resolve5, reject) => {
13683
+ return new Promise((resolve6, reject) => {
13684
13684
  upgrade.call(this, opts, (err2, data) => {
13685
- return err2 ? reject(err2) : resolve5(data);
13685
+ return err2 ? reject(err2) : resolve6(data);
13686
13686
  });
13687
13687
  });
13688
13688
  }
@@ -13774,9 +13774,9 @@ var require_api_connect = __commonJS({
13774
13774
  };
13775
13775
  function connect(opts, callback) {
13776
13776
  if (callback === void 0) {
13777
- return new Promise((resolve5, reject) => {
13777
+ return new Promise((resolve6, reject) => {
13778
13778
  connect.call(this, opts, (err2, data) => {
13779
- return err2 ? reject(err2) : resolve5(data);
13779
+ return err2 ? reject(err2) : resolve6(data);
13780
13780
  });
13781
13781
  });
13782
13782
  }
@@ -13941,20 +13941,20 @@ var require_mock_utils = __commonJS({
13941
13941
  }
13942
13942
  return true;
13943
13943
  }
13944
- function safeUrl(path12) {
13945
- if (typeof path12 !== "string") {
13946
- return path12;
13944
+ function safeUrl(path13) {
13945
+ if (typeof path13 !== "string") {
13946
+ return path13;
13947
13947
  }
13948
- const pathSegments = path12.split("?");
13948
+ const pathSegments = path13.split("?");
13949
13949
  if (pathSegments.length !== 2) {
13950
- return path12;
13950
+ return path13;
13951
13951
  }
13952
13952
  const qp = new URLSearchParams(pathSegments.pop());
13953
13953
  qp.sort();
13954
13954
  return [...pathSegments, qp.toString()].join("?");
13955
13955
  }
13956
- function matchKey(mockDispatch2, { path: path12, method, body, headers }) {
13957
- const pathMatch = matchValue(mockDispatch2.path, path12);
13956
+ function matchKey(mockDispatch2, { path: path13, method, body, headers }) {
13957
+ const pathMatch = matchValue(mockDispatch2.path, path13);
13958
13958
  const methodMatch = matchValue(mockDispatch2.method, method);
13959
13959
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
13960
13960
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -13976,7 +13976,7 @@ var require_mock_utils = __commonJS({
13976
13976
  function getMockDispatch(mockDispatches, key) {
13977
13977
  const basePath = key.query ? buildURL(key.path, key.query) : key.path;
13978
13978
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
13979
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path12 }) => matchValue(safeUrl(path12), resolvedPath));
13979
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path13 }) => matchValue(safeUrl(path13), resolvedPath));
13980
13980
  if (matchedMockDispatches.length === 0) {
13981
13981
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
13982
13982
  }
@@ -14014,9 +14014,9 @@ var require_mock_utils = __commonJS({
14014
14014
  }
14015
14015
  }
14016
14016
  function buildKey(opts) {
14017
- const { path: path12, method, body, headers, query } = opts;
14017
+ const { path: path13, method, body, headers, query } = opts;
14018
14018
  return {
14019
- path: path12,
14019
+ path: path13,
14020
14020
  method,
14021
14021
  body,
14022
14022
  headers,
@@ -14479,10 +14479,10 @@ var require_pending_interceptors_formatter = __commonJS({
14479
14479
  }
14480
14480
  format(pendingInterceptors) {
14481
14481
  const withPrettyHeaders = pendingInterceptors.map(
14482
- ({ method, path: path12, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
14482
+ ({ method, path: path13, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
14483
14483
  Method: method,
14484
14484
  Origin: origin,
14485
- Path: path12,
14485
+ Path: path13,
14486
14486
  "Status code": statusCode,
14487
14487
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
14488
14488
  Invocations: timesInvoked,
@@ -17638,7 +17638,7 @@ var require_fetch = __commonJS({
17638
17638
  function dispatch({ body }) {
17639
17639
  const url = requestCurrentURL(request);
17640
17640
  const agent = fetchParams.controller.dispatcher;
17641
- return new Promise((resolve5, reject) => agent.dispatch(
17641
+ return new Promise((resolve6, reject) => agent.dispatch(
17642
17642
  {
17643
17643
  path: url.pathname + url.search,
17644
17644
  origin: url.origin,
@@ -17714,7 +17714,7 @@ var require_fetch = __commonJS({
17714
17714
  }
17715
17715
  }
17716
17716
  const onError = this.onError.bind(this);
17717
- resolve5({
17717
+ resolve6({
17718
17718
  status,
17719
17719
  statusText,
17720
17720
  headersList,
@@ -17760,7 +17760,7 @@ var require_fetch = __commonJS({
17760
17760
  for (let i = 0; i < rawHeaders.length; i += 2) {
17761
17761
  headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
17762
17762
  }
17763
- resolve5({
17763
+ resolve6({
17764
17764
  status,
17765
17765
  statusText: STATUS_CODES[status],
17766
17766
  headersList,
@@ -19363,9 +19363,9 @@ var require_util6 = __commonJS({
19363
19363
  }
19364
19364
  }
19365
19365
  }
19366
- function validateCookiePath(path12) {
19367
- for (let i = 0; i < path12.length; ++i) {
19368
- const code = path12.charCodeAt(i);
19366
+ function validateCookiePath(path13) {
19367
+ for (let i = 0; i < path13.length; ++i) {
19368
+ const code = path13.charCodeAt(i);
19369
19369
  if (code < 32 || // exclude CTLs (0-31)
19370
19370
  code === 127 || // DEL
19371
19371
  code === 59) {
@@ -21436,8 +21436,8 @@ var require_util8 = __commonJS({
21436
21436
  return true;
21437
21437
  }
21438
21438
  function delay(ms) {
21439
- return new Promise((resolve5) => {
21440
- setTimeout(resolve5, ms).unref();
21439
+ return new Promise((resolve6) => {
21440
+ setTimeout(resolve6, ms).unref();
21441
21441
  });
21442
21442
  }
21443
21443
  module2.exports = {
@@ -22042,11 +22042,11 @@ var require_undici = __commonJS({
22042
22042
  if (typeof opts.path !== "string") {
22043
22043
  throw new InvalidArgumentError("invalid opts.path");
22044
22044
  }
22045
- let path12 = opts.path;
22045
+ let path13 = opts.path;
22046
22046
  if (!opts.path.startsWith("/")) {
22047
- path12 = `/${path12}`;
22047
+ path13 = `/${path13}`;
22048
22048
  }
22049
- url = new URL(util.parseOrigin(url).origin + path12);
22049
+ url = new URL(util.parseOrigin(url).origin + path13);
22050
22050
  } else {
22051
22051
  if (!opts) {
22052
22052
  opts = typeof url === "object" ? url : {};
@@ -22628,7 +22628,7 @@ function resolveWorkerPath() {
22628
22628
  );
22629
22629
  }
22630
22630
  function runParseInWorker(req) {
22631
- return new Promise((resolve5, reject) => {
22631
+ return new Promise((resolve6, reject) => {
22632
22632
  let workerPath;
22633
22633
  try {
22634
22634
  workerPath = resolveWorkerPath();
@@ -22647,7 +22647,7 @@ function runParseInWorker(req) {
22647
22647
  };
22648
22648
  worker.on("message", (reply) => {
22649
22649
  if (reply.ok) {
22650
- finish(() => resolve5({ results: reply.results, failedFiles: reply.failedFiles }));
22650
+ finish(() => resolve6({ results: reply.results, failedFiles: reply.failedFiles }));
22651
22651
  } else {
22652
22652
  const err2 = new Error(reply.error.message);
22653
22653
  err2.name = reply.error.name;
@@ -22727,9 +22727,9 @@ function buildEffectsIndex(layerOutputs) {
22727
22727
  return idx;
22728
22728
  }
22729
22729
  function writeEffectsIndex(rootDir, idx) {
22730
- const path12 = (0, import_node_path11.join)(rootDir, LAUNCHSECURE_DIR, "graphs", "effects-index.json");
22731
- atomicWriteFileSync(path12, JSON.stringify(idx, null, 2) + "\n");
22732
- return path12;
22730
+ const path13 = (0, import_node_path11.join)(rootDir, LAUNCHSECURE_DIR, "graphs", "effects-index.json");
22731
+ atomicWriteFileSync(path13, JSON.stringify(idx, null, 2) + "\n");
22732
+ return path13;
22733
22733
  }
22734
22734
  var import_node_path11;
22735
22735
  var init_effects_index = __esm({
@@ -22891,7 +22891,7 @@ var init_freshness = __esm({
22891
22891
  function getAvailableLayers(rootDir) {
22892
22892
  const dir = (0, import_node_path13.join)(rootDir, GRAPHS_DIR2);
22893
22893
  if (!(0, import_node_fs11.existsSync)(dir)) return [];
22894
- return (0, import_node_fs11.readdirSync)(dir).filter((f) => f.endsWith(".json") && !NON_LAYER_GRAPH_FILES.has(f)).map((f) => f.replace(".json", ""));
22894
+ return (0, import_node_fs11.readdirSync)(dir).filter((f) => f.endsWith(".json") && !f.startsWith(".") && !NON_LAYER_GRAPH_FILES.has(f)).map((f) => f.replace(".json", ""));
22895
22895
  }
22896
22896
  function graphsDir(rootDir) {
22897
22897
  return (0, import_node_path13.join)(rootDir, GRAPHS_DIR2);
@@ -23040,10 +23040,10 @@ async function generateGraph(rootDir, layer) {
23040
23040
  return results;
23041
23041
  }
23042
23042
  function readEffectsIndex(rootDir) {
23043
- const path12 = (0, import_node_path13.join)(rootDir, GRAPHS_DIR2, "effects-index.json");
23044
- if (!(0, import_node_fs11.existsSync)(path12)) return null;
23043
+ const path13 = (0, import_node_path13.join)(rootDir, GRAPHS_DIR2, "effects-index.json");
23044
+ if (!(0, import_node_fs11.existsSync)(path13)) return null;
23045
23045
  try {
23046
- return JSON.parse((0, import_node_fs11.readFileSync)(path12, "utf-8"));
23046
+ return JSON.parse((0, import_node_fs11.readFileSync)(path13, "utf-8"));
23047
23047
  } catch {
23048
23048
  return null;
23049
23049
  }
@@ -24089,6 +24089,8 @@ function extractDeep(absPath) {
24089
24089
  false
24090
24090
  );
24091
24091
  const hasEffects = Object.keys(fileEffects).length > 0;
24092
+ const uiLabels = collectUiLabels(root);
24093
+ const notes = collectNotes(root);
24092
24094
  return {
24093
24095
  elements,
24094
24096
  stateVars,
@@ -24096,10 +24098,77 @@ function extractDeep(absPath) {
24096
24098
  variables,
24097
24099
  responses,
24098
24100
  params,
24099
- ...hasEffects ? { effects: fileEffects } : {}
24101
+ ...hasEffects ? { effects: fileEffects } : {},
24102
+ ...uiLabels.length > 0 ? { ui_labels: uiLabels } : {},
24103
+ ...notes.length > 0 ? { notes } : {}
24100
24104
  };
24101
24105
  }
24102
- var import_node_fs15, import_node_path17, 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;
24106
+ function collectNotes(root) {
24107
+ const out = [];
24108
+ const seen = /* @__PURE__ */ new Set();
24109
+ function visit(node) {
24110
+ if (out.length >= NOTES_MAX) return;
24111
+ if (node.type === "comment") {
24112
+ const text = node.text;
24113
+ const startRow = node.startPosition.row;
24114
+ NOTE_REGEX.lastIndex = 0;
24115
+ let m;
24116
+ while ((m = NOTE_REGEX.exec(text)) !== null) {
24117
+ const kind = m[1];
24118
+ const author = m[2];
24119
+ const body = m[3];
24120
+ if (!kind || !body) continue;
24121
+ const newlinesBefore = (text.slice(0, m.index).match(/\n/g) ?? []).length;
24122
+ const line = startRow + 1 + newlinesBefore;
24123
+ const key = `${line}:${kind}`;
24124
+ if (seen.has(key)) continue;
24125
+ seen.add(key);
24126
+ const note = {
24127
+ kind,
24128
+ text: body.length <= 200 ? body : body.slice(0, 200) + "...",
24129
+ line
24130
+ };
24131
+ if (author) note.author = author;
24132
+ out.push(note);
24133
+ if (out.length >= NOTES_MAX) return;
24134
+ }
24135
+ }
24136
+ for (const child of node.namedChildren) {
24137
+ visit(child);
24138
+ if (out.length >= NOTES_MAX) return;
24139
+ }
24140
+ }
24141
+ visit(root);
24142
+ return out;
24143
+ }
24144
+ function collectUiLabels(root) {
24145
+ const out = [];
24146
+ const seen = /* @__PURE__ */ new Set();
24147
+ function visit(node) {
24148
+ if (out.length >= UI_LABELS_MAX) return;
24149
+ if (node.type === "pair") {
24150
+ const key = node.childForFieldName("key");
24151
+ const val = node.childForFieldName("value");
24152
+ if (key && val) {
24153
+ const keyText = key.type === "property_identifier" ? key.text : stringLiteralValue(key) ?? key.text;
24154
+ if (UI_LABEL_KEYS.has(keyText)) {
24155
+ const strVal = stringLiteralValue(val);
24156
+ if (strVal && strVal.length > 0 && strVal.length <= 200 && !seen.has(strVal)) {
24157
+ seen.add(strVal);
24158
+ out.push(strVal);
24159
+ }
24160
+ }
24161
+ }
24162
+ }
24163
+ for (const child of node.namedChildren) {
24164
+ visit(child);
24165
+ if (out.length >= UI_LABELS_MAX) return;
24166
+ }
24167
+ }
24168
+ visit(root);
24169
+ return out;
24170
+ }
24171
+ var import_node_fs15, import_node_path17, 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;
24103
24172
  var init_ts_extractor = __esm({
24104
24173
  "src/server/graph/core/ts-extractor.ts"() {
24105
24174
  "use strict";
@@ -24218,6 +24287,27 @@ var init_ts_extractor = __esm({
24218
24287
  "selected",
24219
24288
  "disabled"
24220
24289
  ]);
24290
+ UI_LABEL_KEYS = /* @__PURE__ */ new Set([
24291
+ "label",
24292
+ "title",
24293
+ "name",
24294
+ "text",
24295
+ "description",
24296
+ "placeholder",
24297
+ "tooltip",
24298
+ "heading",
24299
+ "subheading",
24300
+ "sheetTitle",
24301
+ "sheetDescription",
24302
+ "caption",
24303
+ "cta",
24304
+ "buttonText",
24305
+ "emptyText",
24306
+ "subtitle"
24307
+ ]);
24308
+ UI_LABELS_MAX = 200;
24309
+ NOTE_REGEX = /^\s*(?:\/\/|\/\*+|\*)\s*([A-Z][A-Z0-9_]{1,15})(?:\(([^)]+)\))?:\s*(\S.*?)\s*(?:\*\/)?$/gm;
24310
+ NOTES_MAX = 100;
24221
24311
  }
24222
24312
  });
24223
24313
 
@@ -24229,7 +24319,7 @@ __export(watcher_exports, {
24229
24319
  function isIgnoredPath(rel) {
24230
24320
  if (rel.startsWith(GRAPHS_RELATIVE)) return true;
24231
24321
  if (rel.endsWith(".lock") || rel.endsWith(".log")) return true;
24232
- for (const part of rel.split(import_node_path30.sep)) {
24322
+ for (const part of rel.split(import_node_path32.sep)) {
24233
24323
  if (IGNORE_SEGMENTS.has(part)) return true;
24234
24324
  }
24235
24325
  return false;
@@ -24271,7 +24361,7 @@ function startGraphWatcher(rootDir, opts = {}) {
24271
24361
  regenerating = false;
24272
24362
  }
24273
24363
  }
24274
- const watcher = (0, import_node_fs26.watch)(rootDir, { recursive: true }, (event, filename) => {
24364
+ const watcher = (0, import_node_fs27.watch)(rootDir, { recursive: true }, (event, filename) => {
24275
24365
  if (!filename) return;
24276
24366
  const rel = filename.toString();
24277
24367
  if (process.env.LAUNCH_CHART_WATCH_TRACE === "1") {
@@ -24304,12 +24394,12 @@ function startGraphWatcher(rootDir, opts = {}) {
24304
24394
  freshness: () => getFreshnessTracker(rootDir).get()
24305
24395
  };
24306
24396
  }
24307
- var import_node_fs26, import_node_path30, IGNORE_SEGMENTS, TRIGGER_EXTENSIONS, GRAPHS_RELATIVE;
24397
+ var import_node_fs27, import_node_path32, IGNORE_SEGMENTS, TRIGGER_EXTENSIONS, GRAPHS_RELATIVE;
24308
24398
  var init_watcher = __esm({
24309
24399
  "src/server/graph/core/watcher.ts"() {
24310
24400
  "use strict";
24311
- import_node_fs26 = require("node:fs");
24312
- import_node_path30 = require("node:path");
24401
+ import_node_fs27 = require("node:fs");
24402
+ import_node_path32 = require("node:path");
24313
24403
  init_launch_kit_paths();
24314
24404
  init_graph();
24315
24405
  init_freshness();
@@ -24338,7 +24428,7 @@ var init_watcher = __esm({
24338
24428
  ".prisma",
24339
24429
  ".sql"
24340
24430
  ]);
24341
- GRAPHS_RELATIVE = (0, import_node_path30.join)(LAUNCHSECURE_DIR, "graphs");
24431
+ GRAPHS_RELATIVE = (0, import_node_path32.join)(LAUNCHSECURE_DIR, "graphs");
24342
24432
  }
24343
24433
  });
24344
24434
 
@@ -24463,6 +24553,18 @@ function initLaunchPodTerminalBridge(httpServer, projectDir) {
24463
24553
  var import_fs = __toESM(require("fs"));
24464
24554
  var import_path2 = __toESM(require("path"));
24465
24555
  var MANAGED_SERVERS = ["launch-pod", "launch-chart"];
24556
+ function mergeMcpEntry(existing, ours) {
24557
+ if (!existing || typeof existing !== "object") return ours;
24558
+ const prev = existing;
24559
+ const merged = { ...prev, ...ours };
24560
+ if (prev.headers || ours.headers) {
24561
+ merged.headers = { ...prev.headers ?? {}, ...ours.headers ?? {} };
24562
+ }
24563
+ if (prev.env || ours.env) {
24564
+ merged.env = { ...prev.env ?? {}, ...ours.env ?? {} };
24565
+ }
24566
+ return merged;
24567
+ }
24466
24568
  var writtenPaths = [];
24467
24569
  function writeMcpConfigs(opts) {
24468
24570
  const { projectDir, token, serverUrl } = opts;
@@ -24524,17 +24626,17 @@ function writeClaudeConfig(projectDir, mcpUrl, token) {
24524
24626
  }
24525
24627
  }
24526
24628
  const servers = existing.mcpServers ?? {};
24527
- servers["launch-pod"] = {
24629
+ servers["launch-pod"] = mergeMcpEntry(servers["launch-pod"], {
24528
24630
  type: "http",
24529
24631
  url: mcpUrl,
24530
24632
  headers: {
24531
24633
  Authorization: `Bearer ${token}`
24532
24634
  }
24533
- };
24534
- servers["launch-chart"] = {
24635
+ });
24636
+ servers["launch-chart"] = mergeMcpEntry(servers["launch-chart"], {
24535
24637
  command: "launch-chart",
24536
24638
  args: []
24537
- };
24639
+ });
24538
24640
  existing.mcpServers = servers;
24539
24641
  import_fs.default.writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
24540
24642
  writtenPaths.push(filePath);
@@ -26003,7 +26105,7 @@ var PostImplLaunchExecutor = class {
26003
26105
  return 3001;
26004
26106
  }
26005
26107
  startDevServer(port, databaseUrl) {
26006
- return new Promise((resolve5) => {
26108
+ return new Promise((resolve6) => {
26007
26109
  const env = { ...process.env, PORT: String(port), ...databaseUrl ? { DATABASE_URL: databaseUrl } : {} };
26008
26110
  this.devProcess = (0, import_child_process3.spawn)("npm", ["run", "dev"], {
26009
26111
  cwd: this.workingDir,
@@ -26015,7 +26117,7 @@ var PostImplLaunchExecutor = class {
26015
26117
  const timeout = setTimeout(() => {
26016
26118
  if (!resolved) {
26017
26119
  resolved = true;
26018
- this.healthCheck(port).then(resolve5);
26120
+ this.healthCheck(port).then(resolve6);
26019
26121
  }
26020
26122
  }, 15e3);
26021
26123
  const onData = (data) => {
@@ -26024,7 +26126,7 @@ var PostImplLaunchExecutor = class {
26024
26126
  if (!resolved) {
26025
26127
  resolved = true;
26026
26128
  clearTimeout(timeout);
26027
- resolve5(true);
26129
+ resolve6(true);
26028
26130
  }
26029
26131
  }
26030
26132
  };
@@ -26035,7 +26137,7 @@ var PostImplLaunchExecutor = class {
26035
26137
  if (!resolved) {
26036
26138
  resolved = true;
26037
26139
  clearTimeout(timeout);
26038
- resolve5(false);
26140
+ resolve6(false);
26039
26141
  }
26040
26142
  });
26041
26143
  this.devProcess.unref();
@@ -27072,8 +27174,7 @@ function buildAnalyzerMcpConfig() {
27072
27174
  "launch-chart": {
27073
27175
  command: process.execPath,
27074
27176
  // current node binary, guaranteed-resolvable
27075
- args: [graphEntry],
27076
- env: { LAUNCH_CHART_AUTOSERVE: "1" }
27177
+ args: [graphEntry]
27077
27178
  }
27078
27179
  }
27079
27180
  });
@@ -27381,7 +27482,7 @@ var ProjectMcpClient = class {
27381
27482
  this.initialized = true;
27382
27483
  }
27383
27484
  send(body) {
27384
- return new Promise((resolve5, reject) => {
27485
+ return new Promise((resolve6, reject) => {
27385
27486
  const headers = {
27386
27487
  ...this.headers,
27387
27488
  "Content-Length": String(Buffer.byteLength(body))
@@ -27406,7 +27507,7 @@ var ProjectMcpClient = class {
27406
27507
  return;
27407
27508
  }
27408
27509
  const sid = res.headers["mcp-session-id"];
27409
- resolve5({ body: text, sessionId: typeof sid === "string" ? sid : void 0 });
27510
+ resolve6({ body: text, sessionId: typeof sid === "string" ? sid : void 0 });
27410
27511
  });
27411
27512
  }
27412
27513
  );
@@ -27762,19 +27863,19 @@ var import_node_path3 = require("node:path");
27762
27863
  init_launch_kit_paths();
27763
27864
  var MAX_DEDUPE_SET = 1e3;
27764
27865
  var MAX_PINGS = 200;
27765
- function readJson(path12, fallback) {
27766
- if (!(0, import_node_fs2.existsSync)(path12)) return fallback;
27866
+ function readJson(path13, fallback) {
27867
+ if (!(0, import_node_fs2.existsSync)(path13)) return fallback;
27767
27868
  try {
27768
- return JSON.parse((0, import_node_fs2.readFileSync)(path12, "utf-8"));
27869
+ return JSON.parse((0, import_node_fs2.readFileSync)(path13, "utf-8"));
27769
27870
  } catch {
27770
27871
  return fallback;
27771
27872
  }
27772
27873
  }
27773
- function writeJsonAtomic(path12, value) {
27774
- (0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(path12), { recursive: true });
27775
- const tmp = `${path12}.tmp.${process.pid}.${Date.now()}`;
27874
+ function writeJsonAtomic(path13, value) {
27875
+ (0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(path13), { recursive: true });
27876
+ const tmp = `${path13}.tmp.${process.pid}.${Date.now()}`;
27776
27877
  (0, import_node_fs2.writeFileSync)(tmp, JSON.stringify(value, null, 2), "utf-8");
27777
- (0, import_node_fs2.renameSync)(tmp, path12);
27878
+ (0, import_node_fs2.renameSync)(tmp, path13);
27778
27879
  }
27779
27880
  var RadarState = class {
27780
27881
  constructor(projectRoot) {
@@ -27907,9 +28008,9 @@ var RadarState = class {
27907
28008
  });
27908
28009
  }
27909
28010
  /** Persist the local path of a successfully-prefetched screenshot. */
27910
- setScreenshotLocalPath(id, path12) {
28011
+ setScreenshotLocalPath(id, path13) {
27911
28012
  return this.mutate(id, (p) => {
27912
- p.context.screenshotLocalPath = path12;
28013
+ p.context.screenshotLocalPath = path13;
27913
28014
  });
27914
28015
  }
27915
28016
  /**
@@ -28333,16 +28434,16 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28333
28434
  const tun = this.tunnel;
28334
28435
  this.tunnel = null;
28335
28436
  if (!tun) return;
28336
- return new Promise((resolve5) => {
28437
+ return new Promise((resolve6) => {
28337
28438
  tun.once("exit", () => {
28338
28439
  this.emit("stopped");
28339
- resolve5();
28440
+ resolve6();
28340
28441
  });
28341
28442
  try {
28342
28443
  tun.stop();
28343
28444
  } catch {
28344
28445
  }
28345
- setTimeout(() => resolve5(), 6e3);
28446
+ setTimeout(() => resolve6(), 6e3);
28346
28447
  });
28347
28448
  }
28348
28449
  spawnOnce() {
@@ -28996,10 +29097,10 @@ function streamTranscript(opts) {
28996
29097
  }
28997
29098
 
28998
29099
  // src/server/graph-mcp.ts
28999
- var import_node_fs27 = require("node:fs");
29000
- var import_node_path31 = require("node:path");
29100
+ var import_node_fs28 = require("node:fs");
29101
+ var import_node_path33 = require("node:path");
29001
29102
  var import_node_child_process3 = require("node:child_process");
29002
- var import_node_os5 = require("node:os");
29103
+ var import_node_os6 = require("node:os");
29003
29104
  init_launch_kit_paths();
29004
29105
  init_graph();
29005
29106
 
@@ -29742,6 +29843,7 @@ function generate(rootDir) {
29742
29843
  responses: deep.responses,
29743
29844
  params: deep.params,
29744
29845
  ...deep.effects ? { effects: deep.effects } : {},
29846
+ ...deep.notes ? { notes: deep.notes } : {},
29745
29847
  _dbCalls: dbCalls
29746
29848
  // temp: used for cross-ref building below
29747
29849
  });
@@ -29762,6 +29864,8 @@ function generate(rootDir) {
29762
29864
  conditions: deep.conditions,
29763
29865
  variables: deep.variables,
29764
29866
  ...deep.effects ? { effects: deep.effects } : {},
29867
+ ...deep.ui_labels ? { ui_labels: deep.ui_labels } : {},
29868
+ ...deep.notes ? { notes: deep.notes } : {},
29765
29869
  ...authWrappers.length > 0 ? { auth: authWrappers } : {},
29766
29870
  ...dbCalls.length > 0 ? { _dbCalls: dbCalls } : {}
29767
29871
  });
@@ -30363,6 +30467,7 @@ var prismaSchemaParser = {
30363
30467
  // src/server/graph/parsers/db/sql-migrations.ts
30364
30468
  var import_node_fs18 = require("node:fs");
30365
30469
  var import_node_path19 = require("node:path");
30470
+ var import_pgsql_parser = require("pgsql-parser");
30366
30471
  init_config();
30367
30472
  var PG_TO_PRISMA = {
30368
30473
  "TEXT": "String",
@@ -30394,243 +30499,6 @@ function pgTypeToPrisma(pgType) {
30394
30499
  const upper = pgType.toUpperCase().trim();
30395
30500
  return PG_TO_PRISMA[upper] ?? upper;
30396
30501
  }
30397
- var ID = `(?:"[\\w$]+"|[\\w$]+)`;
30398
- var QID = `(?:${ID}\\.)?${ID}`;
30399
- function bareName(captured) {
30400
- const parts = captured.split(".");
30401
- const last = parts[parts.length - 1];
30402
- return last.replace(/^"(.*)"$/, "$1").trim();
30403
- }
30404
- function parseCreateTable(sql, state) {
30405
- const re = new RegExp(
30406
- `CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${QID})\\s*\\(([\\s\\S]*?)\\);`,
30407
- "gi"
30408
- );
30409
- let m;
30410
- while ((m = re.exec(sql)) !== null) {
30411
- const tableName = bareName(m[1]);
30412
- const body = m[2];
30413
- const columns = /* @__PURE__ */ new Map();
30414
- let primaryCol = null;
30415
- const inlineFks = [];
30416
- const lines = splitTopLevelCommas(body);
30417
- for (const raw of lines) {
30418
- const trimmed = raw.trim().replace(/,\s*$/, "");
30419
- if (!trimmed || trimmed.startsWith("--")) continue;
30420
- const namedPk = trimmed.match(new RegExp(`^CONSTRAINT\\s+${ID}\\s+PRIMARY\\s+KEY\\s*\\(\\s*(${QID})`, "i"));
30421
- if (namedPk) {
30422
- primaryCol = bareName(namedPk[1]);
30423
- continue;
30424
- }
30425
- const tablePk = trimmed.match(new RegExp(`^PRIMARY\\s+KEY\\s*\\(\\s*(${QID})`, "i"));
30426
- if (tablePk) {
30427
- primaryCol = bareName(tablePk[1]);
30428
- continue;
30429
- }
30430
- if (/^UNIQUE\s*\(/i.test(trimmed)) continue;
30431
- const namedFk = trimmed.match(new RegExp(
30432
- `^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+)?))?`,
30433
- "i"
30434
- ));
30435
- if (namedFk) {
30436
- inlineFks.push({
30437
- constraintName: bareName(namedFk[1]),
30438
- sourceTable: tableName,
30439
- sourceColumn: bareName(namedFk[2]),
30440
- targetTable: bareName(namedFk[3]),
30441
- targetColumn: bareName(namedFk[4]),
30442
- onDelete: namedFk[5] ?? null
30443
- });
30444
- continue;
30445
- }
30446
- const bareFk = trimmed.match(new RegExp(
30447
- `^FOREIGN\\s+KEY\\s*\\(\\s*(${QID})\\s*\\)\\s+REFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
30448
- "i"
30449
- ));
30450
- if (bareFk) {
30451
- inlineFks.push({
30452
- constraintName: `${tableName}_${bareName(bareFk[1])}_fkey`,
30453
- sourceTable: tableName,
30454
- sourceColumn: bareName(bareFk[1]),
30455
- targetTable: bareName(bareFk[2]),
30456
- targetColumn: bareName(bareFk[3]),
30457
- onDelete: bareFk[4] ?? null
30458
- });
30459
- continue;
30460
- }
30461
- if (/^CONSTRAINT\s/i.test(trimmed)) continue;
30462
- const colMatch = trimmed.match(new RegExp(`^(${ID})\\s+(.+)`, "i"));
30463
- if (!colMatch) continue;
30464
- const colName = bareName(colMatch[1]);
30465
- let rest = colMatch[2];
30466
- const inlineRefMatch = rest.match(new RegExp(
30467
- `\\bREFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
30468
- "i"
30469
- ));
30470
- if (inlineRefMatch) {
30471
- inlineFks.push({
30472
- constraintName: `${tableName}_${colName}_fkey`,
30473
- sourceTable: tableName,
30474
- sourceColumn: colName,
30475
- targetTable: bareName(inlineRefMatch[1]),
30476
- targetColumn: bareName(inlineRefMatch[2]),
30477
- onDelete: inlineRefMatch[3] ?? null
30478
- });
30479
- rest = rest.replace(inlineRefMatch[0], "").trim();
30480
- }
30481
- const isNotNull = /\bNOT\s+NULL\b/i.test(rest);
30482
- const isPrimaryKey = /\bPRIMARY\s+KEY\b/i.test(rest);
30483
- const isUnique = /\bUNIQUE\b/i.test(rest);
30484
- const defaultMatch = rest.match(/\bDEFAULT\s+(.+?)(?:\s*,?\s*$)/i);
30485
- const defaultVal = defaultMatch ? defaultMatch[1].trim() : null;
30486
- 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();
30487
- columns.set(colName, {
30488
- name: colName,
30489
- type: colType,
30490
- nullable: !isNotNull && !isPrimaryKey,
30491
- primary: isPrimaryKey,
30492
- unique: isUnique,
30493
- default: defaultVal
30494
- });
30495
- if (isPrimaryKey) primaryCol = colName;
30496
- }
30497
- if (primaryCol && columns.has(primaryCol)) {
30498
- columns.get(primaryCol).primary = true;
30499
- }
30500
- state.tables.set(tableName, { name: tableName, columns });
30501
- state.fks.push(...inlineFks);
30502
- }
30503
- }
30504
- function splitTopLevelCommas(body) {
30505
- const out = [];
30506
- let depth = 0;
30507
- let buf = "";
30508
- let inString = null;
30509
- for (const ch of body) {
30510
- if (inString) {
30511
- buf += ch;
30512
- if (ch === inString) inString = null;
30513
- continue;
30514
- }
30515
- if (ch === "'" || ch === '"') {
30516
- inString = ch;
30517
- buf += ch;
30518
- continue;
30519
- }
30520
- if (ch === "(") depth++;
30521
- else if (ch === ")") depth--;
30522
- if (ch === "," && depth === 0) {
30523
- out.push(buf);
30524
- buf = "";
30525
- continue;
30526
- }
30527
- buf += ch;
30528
- }
30529
- if (buf.trim()) out.push(buf);
30530
- return out;
30531
- }
30532
- function parseCreateEnum(sql, state) {
30533
- const re = new RegExp(
30534
- `CREATE\\s+TYPE\\s+(${QID})\\s+AS\\s+ENUM\\s*\\(([^)]+)\\)`,
30535
- "gi"
30536
- );
30537
- let m;
30538
- while ((m = re.exec(sql)) !== null) {
30539
- const enumName = bareName(m[1]);
30540
- const valuesStr = m[2];
30541
- const values = new Set(
30542
- valuesStr.split(",").map((v) => v.trim().replace(/^'(.*)'$/, "$1")).filter(Boolean)
30543
- );
30544
- state.enums.set(enumName, { name: enumName, values });
30545
- }
30546
- }
30547
- function parseAlterTable(sql, state) {
30548
- const addColRe = new RegExp(
30549
- `ALTER\\s+TABLE\\s+(${QID})\\s+ADD\\s+COLUMN\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${QID})\\s+(.+?);`,
30550
- "gi"
30551
- );
30552
- let m;
30553
- while ((m = addColRe.exec(sql)) !== null) {
30554
- const tableName = bareName(m[1]);
30555
- const colName = bareName(m[2]);
30556
- let rest = m[3];
30557
- const table = state.tables.get(tableName);
30558
- if (!table) continue;
30559
- const isNotNull = /\bNOT\s+NULL\b/i.test(rest);
30560
- const defaultMatch = rest.match(/\bDEFAULT\s+(.+?)$/i);
30561
- const defaultVal = defaultMatch ? defaultMatch[1].trim() : null;
30562
- let colType = rest.replace(/\bNOT\s+NULL\b/gi, "").replace(/\bDEFAULT\s+.*/gi, "").trim();
30563
- table.columns.set(colName, {
30564
- name: colName,
30565
- type: colType,
30566
- nullable: !isNotNull,
30567
- primary: false,
30568
- unique: false,
30569
- default: defaultVal
30570
- });
30571
- }
30572
- const dropColRe = new RegExp(
30573
- `ALTER\\s+TABLE\\s+(${QID})\\s+DROP\\s+COLUMN\\s+(?:IF\\s+EXISTS\\s+)?(${QID})`,
30574
- "gi"
30575
- );
30576
- while ((m = dropColRe.exec(sql)) !== null) {
30577
- const table = state.tables.get(bareName(m[1]));
30578
- if (table) table.columns.delete(bareName(m[2]));
30579
- }
30580
- const fkRe = new RegExp(
30581
- `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+)?))?`,
30582
- "gi"
30583
- );
30584
- while ((m = fkRe.exec(sql)) !== null) {
30585
- state.fks.push({
30586
- constraintName: bareName(m[2]),
30587
- sourceTable: bareName(m[1]),
30588
- sourceColumn: bareName(m[3]),
30589
- targetTable: bareName(m[4]),
30590
- targetColumn: bareName(m[5]),
30591
- onDelete: m[6] ?? null
30592
- });
30593
- }
30594
- }
30595
- function parseAlterEnum(sql, state) {
30596
- const re = new RegExp(
30597
- `ALTER\\s+TYPE\\s+(${QID})\\s+ADD\\s+VALUE\\s+'([^']+)'`,
30598
- "gi"
30599
- );
30600
- let m;
30601
- while ((m = re.exec(sql)) !== null) {
30602
- const en = state.enums.get(bareName(m[1]));
30603
- if (en) en.values.add(m[2]);
30604
- }
30605
- }
30606
- function parseDropTable(sql, state) {
30607
- const re = new RegExp(
30608
- `DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(${QID})`,
30609
- "gi"
30610
- );
30611
- let m;
30612
- while ((m = re.exec(sql)) !== null) {
30613
- const dropped = bareName(m[1]);
30614
- state.tables.delete(dropped);
30615
- state.fks = state.fks.filter((fk) => fk.sourceTable !== dropped && fk.targetTable !== dropped);
30616
- }
30617
- }
30618
- function parseUniqueIndex(sql, state) {
30619
- const re = new RegExp(
30620
- `CREATE\\s+UNIQUE\\s+INDEX\\s+(?:(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:${ID}\\s+)?)?ON\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)`,
30621
- "gi"
30622
- );
30623
- let m;
30624
- while ((m = re.exec(sql)) !== null) {
30625
- const tableName = bareName(m[1]);
30626
- const colName = bareName(m[2]);
30627
- const table = state.tables.get(tableName);
30628
- const col = table?.columns.get(colName);
30629
- if (col) col.unique = true;
30630
- if (!state.uniqueIndexes.has(tableName)) state.uniqueIndexes.set(tableName, /* @__PURE__ */ new Set());
30631
- state.uniqueIndexes.get(tableName).add(colName);
30632
- }
30633
- }
30634
30502
  function discoverMigrationFiles(migrationsDir) {
30635
30503
  if (!(0, import_node_fs18.existsSync)(migrationsDir)) return [];
30636
30504
  const out = [];
@@ -30645,25 +30513,614 @@ function discoverMigrationFiles(migrationsDir) {
30645
30513
  }
30646
30514
  return out;
30647
30515
  }
30648
- function parseMigrations(migrationsDir) {
30516
+ var postgresDialect = {
30517
+ parse(sql) {
30518
+ return (0, import_pgsql_parser.parseSync)(sql);
30519
+ },
30520
+ applyAll(ast, state, filepath) {
30521
+ const stmts = ast.stmts ?? [];
30522
+ extractTablesFromStmts(stmts, state);
30523
+ extractEnumsFromStmts(stmts, state);
30524
+ extractIndexesFromStmts(stmts, state, filepath);
30525
+ extractPoliciesFromStmts(stmts, state, filepath);
30526
+ extractExtensionsFromStmts(stmts, state, filepath);
30527
+ extractTriggersFromStmts(stmts, state, filepath);
30528
+ extractFunctionsFromStmts(stmts, state, filepath);
30529
+ extractViewsFromStmts(stmts, state, filepath);
30530
+ applyDropIndexes(stmts, state);
30531
+ applyDropPolicies(stmts, state);
30532
+ applyDropsForSchemaObjects(stmts, state);
30533
+ applyAstAlterEnums(stmts, state);
30534
+ applyAstAlterations(stmts, state);
30535
+ },
30536
+ extractMigrationInfo(ast, name, filepath) {
30537
+ const stmts = ast.stmts ?? [];
30538
+ return extractMigrationInfoFromStmts(stmts, name, filepath);
30539
+ }
30540
+ };
30541
+ function parseMigrations(migrationsDir, dialect = postgresDialect) {
30649
30542
  const state = {
30650
30543
  tables: /* @__PURE__ */ new Map(),
30651
30544
  enums: /* @__PURE__ */ new Map(),
30652
30545
  fks: [],
30653
- uniqueIndexes: /* @__PURE__ */ new Map()
30546
+ uniqueIndexes: /* @__PURE__ */ new Map(),
30547
+ indexes: [],
30548
+ policies: [],
30549
+ extensions: [],
30550
+ triggers: [],
30551
+ functions: [],
30552
+ views: []
30654
30553
  };
30655
30554
  if (!migrationsDir) return state;
30656
30555
  for (const sqlPath of discoverMigrationFiles(migrationsDir)) {
30657
30556
  const sql = (0, import_node_fs18.readFileSync)(sqlPath, "utf-8");
30658
- parseCreateEnum(sql, state);
30659
- parseCreateTable(sql, state);
30660
- parseAlterTable(sql, state);
30661
- parseAlterEnum(sql, state);
30662
- parseDropTable(sql, state);
30663
- parseUniqueIndex(sql, state);
30557
+ let ast;
30558
+ try {
30559
+ ast = dialect.parse(sql);
30560
+ } catch {
30561
+ continue;
30562
+ }
30563
+ dialect.applyAll(ast, state, sqlPath);
30664
30564
  }
30665
30565
  return state;
30666
30566
  }
30567
+ function extractIndexesFromStmts(stmts, state, filepath) {
30568
+ for (const wrap of stmts) {
30569
+ const stmt = wrap.stmt ?? {};
30570
+ const ix = stmt.IndexStmt;
30571
+ if (!ix) continue;
30572
+ const name = ix.idxname ?? "";
30573
+ if (!name) continue;
30574
+ const table = ix.relation?.relname ?? "";
30575
+ const unique = !!ix.unique;
30576
+ const method = String(ix.accessMethod ?? "btree").toLowerCase();
30577
+ const params = ix.indexParams ?? [];
30578
+ const columns = [];
30579
+ let hasExpressions = false;
30580
+ for (const p of params) {
30581
+ const elem = p.IndexElem;
30582
+ if (!elem) continue;
30583
+ if (elem.name) columns.push(elem.name);
30584
+ else if (elem.expr) hasExpressions = true;
30585
+ }
30586
+ const hasPredicate = !!ix.whereClause;
30587
+ const existing = state.indexes.findIndex((i) => i.name === name);
30588
+ const next = { name, table, unique, method, columns, hasExpressions, hasPredicate, filepath };
30589
+ if (existing >= 0) state.indexes[existing] = next;
30590
+ else state.indexes.push(next);
30591
+ if (unique && columns.length === 1 && !hasPredicate && !hasExpressions) {
30592
+ const t = state.tables.get(table);
30593
+ const col = t?.columns.get(columns[0]);
30594
+ if (col) col.unique = true;
30595
+ if (!state.uniqueIndexes.has(table)) state.uniqueIndexes.set(table, /* @__PURE__ */ new Set());
30596
+ state.uniqueIndexes.get(table).add(columns[0]);
30597
+ }
30598
+ }
30599
+ }
30600
+ function applyDropIndexes(stmts, state) {
30601
+ for (const wrap of stmts) {
30602
+ const drop = wrap.stmt?.DropStmt;
30603
+ if (!drop || drop.removeType !== "OBJECT_INDEX") continue;
30604
+ const objects = drop.objects ?? [];
30605
+ const droppedNames = /* @__PURE__ */ new Set();
30606
+ for (const obj of objects) {
30607
+ const items = obj.List?.items ?? [];
30608
+ const last = items[items.length - 1]?.String?.sval;
30609
+ if (last) droppedNames.add(last);
30610
+ }
30611
+ if (droppedNames.size > 0) {
30612
+ state.indexes = state.indexes.filter((i) => !droppedNames.has(i.name));
30613
+ }
30614
+ }
30615
+ }
30616
+ function formatPgTypeName(typeName) {
30617
+ const names = (typeName?.names ?? []).map((n) => n.String?.sval ?? "").filter(Boolean);
30618
+ const base = (names[names.length - 1] ?? "").toLowerCase();
30619
+ const PG_INTERNAL_MAP = {
30620
+ int4: "INTEGER",
30621
+ int8: "BIGINT",
30622
+ int2: "SMALLINT",
30623
+ float8: "DOUBLE PRECISION",
30624
+ float4: "REAL",
30625
+ bool: "BOOLEAN",
30626
+ bpchar: "CHAR",
30627
+ timestamptz: "TIMESTAMPTZ",
30628
+ timestamp: "TIMESTAMP",
30629
+ numeric: "NUMERIC",
30630
+ text: "TEXT",
30631
+ varchar: "VARCHAR",
30632
+ jsonb: "JSONB",
30633
+ json: "JSON",
30634
+ uuid: "UUID",
30635
+ date: "DATE",
30636
+ bytea: "BYTEA"
30637
+ };
30638
+ return PG_INTERNAL_MAP[base] ?? base.toUpperCase();
30639
+ }
30640
+ function applyAstAlterations(stmts, state) {
30641
+ for (const wrap of stmts) {
30642
+ const stmt = wrap.stmt ?? {};
30643
+ const kind = Object.keys(stmt)[0];
30644
+ if (!kind) continue;
30645
+ if (kind === "AlterTableStmt") {
30646
+ const body = stmt.AlterTableStmt;
30647
+ const tableName = body.relation?.relname ?? "";
30648
+ const table = state.tables.get(tableName);
30649
+ if (!table) continue;
30650
+ const cmds = body.cmds ?? [];
30651
+ for (const c of cmds) {
30652
+ const cmd = c.AlterTableCmd;
30653
+ if (!cmd) continue;
30654
+ const subtype = cmd.subtype ?? "";
30655
+ const colName = cmd.name ?? "";
30656
+ const col = colName ? table.columns.get(colName) : void 0;
30657
+ switch (subtype) {
30658
+ case "AT_AlterColumnType": {
30659
+ if (!col) break;
30660
+ const typeName = cmd.def?.ColumnDef?.typeName ?? cmd.def?.typeName;
30661
+ if (typeName) col.type = formatPgTypeNameWithMods(typeName);
30662
+ break;
30663
+ }
30664
+ case "AT_SetNotNull":
30665
+ if (col) col.nullable = false;
30666
+ break;
30667
+ case "AT_DropNotNull":
30668
+ if (col) col.nullable = true;
30669
+ break;
30670
+ case "AT_AddColumn": {
30671
+ const cd = cmd.def?.ColumnDef;
30672
+ if (!cd) break;
30673
+ const newColName = cd.colname ?? "";
30674
+ if (!newColName) break;
30675
+ if (table.columns.has(newColName)) break;
30676
+ let nullable = true;
30677
+ let primary = false;
30678
+ let unique = false;
30679
+ let defaultVal = null;
30680
+ for (const c2 of cd.constraints ?? []) {
30681
+ const ct = c2.Constraint;
30682
+ if (!ct) continue;
30683
+ if (ct.contype === "CONSTR_NOTNULL") nullable = false;
30684
+ else if (ct.contype === "CONSTR_PRIMARY") {
30685
+ primary = true;
30686
+ nullable = false;
30687
+ } else if (ct.contype === "CONSTR_UNIQUE") unique = true;
30688
+ else if (ct.contype === "CONSTR_DEFAULT") defaultVal = "<expr>";
30689
+ }
30690
+ table.columns.set(newColName, {
30691
+ name: newColName,
30692
+ type: formatPgTypeNameWithMods(cd.typeName),
30693
+ nullable,
30694
+ primary,
30695
+ unique,
30696
+ default: defaultVal
30697
+ });
30698
+ break;
30699
+ }
30700
+ case "AT_DropColumn":
30701
+ if (colName) table.columns.delete(colName);
30702
+ break;
30703
+ case "AT_AddConstraint": {
30704
+ const ct = cmd.def?.Constraint;
30705
+ if (!ct) break;
30706
+ if (ct.contype !== "CONSTR_FOREIGN") break;
30707
+ const fkCols = (ct.fk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30708
+ const pkCols = (ct.pk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30709
+ const pkTable = ct.pktable?.relname ?? "";
30710
+ if (fkCols.length && pkCols.length && pkTable) {
30711
+ state.fks.push({
30712
+ constraintName: ct.conname || `${tableName}_${fkCols[0]}_fkey`,
30713
+ sourceTable: tableName,
30714
+ sourceColumn: fkCols[0],
30715
+ targetTable: pkTable,
30716
+ targetColumn: pkCols[0],
30717
+ onDelete: mapFkAction(ct.fk_del_action)
30718
+ });
30719
+ }
30720
+ break;
30721
+ }
30722
+ }
30723
+ }
30724
+ } else if (kind === "RenameStmt") {
30725
+ const body = stmt.RenameStmt;
30726
+ const renameType = body.renameType ?? "";
30727
+ const newName = body.newname ?? "";
30728
+ if (renameType === "OBJECT_COLUMN") {
30729
+ const tableName = body.relation?.relname ?? "";
30730
+ const oldName = body.subname ?? "";
30731
+ const table = state.tables.get(tableName);
30732
+ if (!table || !oldName || !newName) continue;
30733
+ const col = table.columns.get(oldName);
30734
+ if (col) {
30735
+ col.name = newName;
30736
+ table.columns.delete(oldName);
30737
+ table.columns.set(newName, col);
30738
+ }
30739
+ } else if (renameType === "OBJECT_TABLE") {
30740
+ const oldName = body.relation?.relname ?? "";
30741
+ if (!oldName || !newName) continue;
30742
+ const t = state.tables.get(oldName);
30743
+ if (!t) continue;
30744
+ state.tables.delete(oldName);
30745
+ t.name = newName;
30746
+ state.tables.set(newName, t);
30747
+ for (const fk of state.fks) {
30748
+ if (fk.sourceTable === oldName) fk.sourceTable = newName;
30749
+ if (fk.targetTable === oldName) fk.targetTable = newName;
30750
+ }
30751
+ }
30752
+ }
30753
+ }
30754
+ }
30755
+ function extractPoliciesFromStmts(stmts, state, filepath) {
30756
+ for (const wrap of stmts) {
30757
+ const body = wrap.stmt?.CreatePolicyStmt;
30758
+ if (!body) continue;
30759
+ const name = body.policy_name ?? "";
30760
+ if (!name) continue;
30761
+ const table = body.table?.relname ?? "";
30762
+ const cmdRaw = String(body.cmd_name ?? "all").toUpperCase();
30763
+ const command = ["SELECT", "INSERT", "UPDATE", "DELETE", "ALL"].includes(cmdRaw) ? cmdRaw : "ALL";
30764
+ const permissive = body.permissive === true;
30765
+ const roles = (body.roles ?? []).map((r) => {
30766
+ const rs = r.RoleSpec;
30767
+ if (!rs) return "";
30768
+ if (rs.roletype === "ROLESPEC_PUBLIC") return "public";
30769
+ if (rs.roletype === "ROLESPEC_CURRENT_USER") return "current_user";
30770
+ if (rs.roletype === "ROLESPEC_CSTRING" && rs.rolename) return rs.rolename;
30771
+ return "";
30772
+ }).filter(Boolean);
30773
+ const hasUsing = !!body.qual;
30774
+ const hasWithCheck = !!body.with_check;
30775
+ const existing = state.policies.findIndex((p) => p.table === table && p.name === name);
30776
+ const next = { name, table, command, permissive, roles, hasUsing, hasWithCheck, filepath };
30777
+ if (existing >= 0) state.policies[existing] = next;
30778
+ else state.policies.push(next);
30779
+ }
30780
+ }
30781
+ function applyDropPolicies(stmts, state) {
30782
+ for (const wrap of stmts) {
30783
+ const drop = wrap.stmt?.DropStmt;
30784
+ if (!drop || drop.removeType !== "OBJECT_POLICY") continue;
30785
+ const objects = drop.objects ?? [];
30786
+ for (const obj of objects) {
30787
+ const items = obj.List?.items ?? [];
30788
+ if (items.length < 2) continue;
30789
+ const table = items[0]?.String?.sval ?? "";
30790
+ const policyName = items[items.length - 1]?.String?.sval ?? "";
30791
+ if (!table || !policyName) continue;
30792
+ state.policies = state.policies.filter((p) => !(p.table === table && p.name === policyName));
30793
+ }
30794
+ }
30795
+ }
30796
+ function mapFkAction(action) {
30797
+ if (!action) return null;
30798
+ const m = {
30799
+ r: "RESTRICT",
30800
+ c: "CASCADE",
30801
+ s: "SET NULL",
30802
+ d: "SET DEFAULT",
30803
+ a: "NO ACTION",
30804
+ // pgsql-parser may also emit FKCONSTR_ACTION_* enum strings:
30805
+ FKCONSTR_ACTION_RESTRICT: "RESTRICT",
30806
+ FKCONSTR_ACTION_CASCADE: "CASCADE",
30807
+ FKCONSTR_ACTION_SETNULL: "SET NULL",
30808
+ FKCONSTR_ACTION_SETDEFAULT: "SET DEFAULT",
30809
+ FKCONSTR_ACTION_NOACTION: "NO ACTION"
30810
+ };
30811
+ return m[action] ?? null;
30812
+ }
30813
+ function formatPgTypeNameWithMods(typeName) {
30814
+ const base = formatPgTypeName(typeName);
30815
+ if (base === "String" || base === "unknown") return base;
30816
+ const typmods = [];
30817
+ for (const m of typeName?.typmods ?? []) {
30818
+ const v = m.A_Const?.ival?.ival;
30819
+ if (typeof v === "number") typmods.push(v);
30820
+ }
30821
+ return typmods.length ? `${base}(${typmods.join(",")})` : base;
30822
+ }
30823
+ function extractTablesFromStmts(stmts, state) {
30824
+ for (const wrap of stmts) {
30825
+ const body = wrap.stmt?.CreateStmt;
30826
+ if (!body) continue;
30827
+ const tableName = body.relation?.relname ?? "";
30828
+ if (!tableName) continue;
30829
+ const columns = /* @__PURE__ */ new Map();
30830
+ const fks = [];
30831
+ let primaryCol = null;
30832
+ for (const elt of body.tableElts ?? []) {
30833
+ if (elt.ColumnDef) {
30834
+ const cd = elt.ColumnDef;
30835
+ const colName = cd.colname ?? "";
30836
+ if (!colName) continue;
30837
+ const colType = formatPgTypeNameWithMods(cd.typeName);
30838
+ let nullable = true;
30839
+ let primary = false;
30840
+ let unique = false;
30841
+ let defaultVal = null;
30842
+ for (const c of cd.constraints ?? []) {
30843
+ const ct = c.Constraint;
30844
+ if (!ct) continue;
30845
+ switch (ct.contype) {
30846
+ case "CONSTR_NOTNULL":
30847
+ nullable = false;
30848
+ break;
30849
+ case "CONSTR_PRIMARY":
30850
+ primary = true;
30851
+ nullable = false;
30852
+ primaryCol = colName;
30853
+ break;
30854
+ case "CONSTR_UNIQUE":
30855
+ unique = true;
30856
+ break;
30857
+ case "CONSTR_DEFAULT":
30858
+ defaultVal = "<expr>";
30859
+ break;
30860
+ case "CONSTR_FOREIGN": {
30861
+ const pkTable = ct.pktable?.relname ?? "";
30862
+ const pkCols = (ct.pk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30863
+ if (pkTable && pkCols.length) {
30864
+ fks.push({
30865
+ constraintName: ct.conname || `${tableName}_${colName}_fkey`,
30866
+ sourceTable: tableName,
30867
+ sourceColumn: colName,
30868
+ targetTable: pkTable,
30869
+ targetColumn: pkCols[0],
30870
+ onDelete: mapFkAction(ct.fk_del_action)
30871
+ });
30872
+ }
30873
+ break;
30874
+ }
30875
+ }
30876
+ }
30877
+ columns.set(colName, {
30878
+ name: colName,
30879
+ type: colType,
30880
+ nullable,
30881
+ primary,
30882
+ unique,
30883
+ default: defaultVal
30884
+ });
30885
+ } else if (elt.Constraint) {
30886
+ const ct = elt.Constraint;
30887
+ switch (ct.contype) {
30888
+ case "CONSTR_PRIMARY": {
30889
+ const keys = (ct.keys ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30890
+ if (keys.length) primaryCol = keys[0];
30891
+ break;
30892
+ }
30893
+ case "CONSTR_FOREIGN": {
30894
+ const fkCols = (ct.fk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30895
+ const pkCols = (ct.pk_attrs ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30896
+ const pkTable = ct.pktable?.relname ?? "";
30897
+ if (fkCols.length && pkCols.length && pkTable) {
30898
+ fks.push({
30899
+ constraintName: ct.conname || `${tableName}_${fkCols[0]}_fkey`,
30900
+ sourceTable: tableName,
30901
+ sourceColumn: fkCols[0],
30902
+ targetTable: pkTable,
30903
+ targetColumn: pkCols[0],
30904
+ onDelete: mapFkAction(ct.fk_del_action)
30905
+ });
30906
+ }
30907
+ break;
30908
+ }
30909
+ }
30910
+ }
30911
+ }
30912
+ if (primaryCol && columns.has(primaryCol)) {
30913
+ columns.get(primaryCol).primary = true;
30914
+ columns.get(primaryCol).nullable = false;
30915
+ }
30916
+ state.tables.set(tableName, { name: tableName, columns });
30917
+ state.fks.push(...fks);
30918
+ }
30919
+ }
30920
+ function extractEnumsFromStmts(stmts, state) {
30921
+ for (const wrap of stmts) {
30922
+ const body = wrap.stmt?.CreateEnumStmt;
30923
+ if (!body) continue;
30924
+ const names = (body.typeName ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30925
+ const enumName = names[names.length - 1] ?? "";
30926
+ if (!enumName) continue;
30927
+ const vals = new Set(
30928
+ (body.vals ?? []).map((s) => s.String?.sval ?? "").filter(Boolean)
30929
+ );
30930
+ state.enums.set(enumName, { name: enumName, values: vals });
30931
+ }
30932
+ }
30933
+ function applyAstAlterEnums(stmts, state) {
30934
+ for (const wrap of stmts) {
30935
+ const body = wrap.stmt?.AlterEnumStmt;
30936
+ if (!body) continue;
30937
+ const names = (body.typeName ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
30938
+ const enumName = names[names.length - 1] ?? "";
30939
+ const en = state.enums.get(enumName);
30940
+ if (!en) continue;
30941
+ if (body.newVal) en.values.add(String(body.newVal));
30942
+ }
30943
+ }
30944
+ function extractExtensionsFromStmts(stmts, state, filepath) {
30945
+ for (const wrap of stmts) {
30946
+ const body = wrap.stmt?.CreateExtensionStmt;
30947
+ if (!body) continue;
30948
+ const name = body.extname ?? "";
30949
+ if (!name) continue;
30950
+ let schema = null;
30951
+ let version2 = null;
30952
+ for (const opt of body.options ?? []) {
30953
+ const de = opt.DefElem;
30954
+ if (!de) continue;
30955
+ if (de.defname === "schema" && de.arg?.String?.sval) schema = de.arg.String.sval;
30956
+ else if (de.defname === "new_version" && de.arg?.String?.sval) version2 = de.arg.String.sval;
30957
+ }
30958
+ const next = { name, schema, version: version2, filepath };
30959
+ const existing = state.extensions.findIndex((e) => e.name === name);
30960
+ if (existing >= 0) state.extensions[existing] = next;
30961
+ else state.extensions.push(next);
30962
+ }
30963
+ }
30964
+ function extractTriggersFromStmts(stmts, state, filepath) {
30965
+ for (const wrap of stmts) {
30966
+ const body = wrap.stmt?.CreateTrigStmt;
30967
+ if (!body) continue;
30968
+ const name = body.trigname ?? "";
30969
+ if (!name) continue;
30970
+ const table = body.relation?.relname ?? "";
30971
+ const timingVal = body.timing ?? 0;
30972
+ const eventsVal = body.events ?? 0;
30973
+ const timing = timingVal & 2 ? "BEFORE" : timingVal & 64 ? "INSTEAD OF" : "AFTER";
30974
+ const events = [];
30975
+ if (eventsVal & 4) events.push("INSERT");
30976
+ if (eventsVal & 8) events.push("DELETE");
30977
+ if (eventsVal & 16) events.push("UPDATE");
30978
+ if (eventsVal & 32) events.push("TRUNCATE");
30979
+ const funcname = body.funcname ?? [];
30980
+ const funcCall = funcname[funcname.length - 1]?.String?.sval ?? "";
30981
+ const forEach = body.row ? "ROW" : "STATEMENT";
30982
+ const hasWhen = !!body.whenClause;
30983
+ const next = { name, table, timing, events, function: funcCall, hasWhen, forEach, filepath };
30984
+ const existing = state.triggers.findIndex((t) => t.table === table && t.name === name);
30985
+ if (existing >= 0) state.triggers[existing] = next;
30986
+ else state.triggers.push(next);
30987
+ }
30988
+ }
30989
+ function functionIdFor(name, schema) {
30990
+ return schema ? `${schema}.${name}` : name;
30991
+ }
30992
+ function extractFunctionsFromStmts(stmts, state, filepath) {
30993
+ for (const wrap of stmts) {
30994
+ const body = wrap.stmt?.CreateFunctionStmt;
30995
+ if (!body) continue;
30996
+ const fn = body.funcname ?? [];
30997
+ if (fn.length === 0) continue;
30998
+ const name = fn[fn.length - 1]?.String?.sval ?? "";
30999
+ if (!name) continue;
31000
+ const schema = fn.length > 1 ? fn[fn.length - 2]?.String?.sval ?? null : null;
31001
+ let language = "sql";
31002
+ for (const opt of body.options ?? []) {
31003
+ const de = opt.DefElem;
31004
+ if (de?.defname === "language" && de.arg?.String?.sval) language = de.arg.String.sval;
31005
+ }
31006
+ const returnType = body.returnType ? formatPgTypeName(body.returnType) : "";
31007
+ const isProcedure = !!body.is_procedure;
31008
+ const next = { name, schema, language, returnType, isProcedure, filepath };
31009
+ const id = functionIdFor(name, schema);
31010
+ const existing = state.functions.findIndex((f) => functionIdFor(f.name, f.schema) === id);
31011
+ if (existing >= 0) state.functions[existing] = next;
31012
+ else state.functions.push(next);
31013
+ }
31014
+ }
31015
+ function extractViewsFromStmts(stmts, state, filepath) {
31016
+ for (const wrap of stmts) {
31017
+ const stmt = wrap.stmt ?? {};
31018
+ const view = stmt.ViewStmt;
31019
+ if (view) {
31020
+ const name = view.view?.relname ?? "";
31021
+ if (!name) continue;
31022
+ const schema = view.view?.schemaname ?? null;
31023
+ const next = {
31024
+ name,
31025
+ schema,
31026
+ isMaterialized: false,
31027
+ withCheckOption: String(view.withCheckOption ?? "NO_CHECK_OPTION"),
31028
+ filepath
31029
+ };
31030
+ const id = functionIdFor(name, schema);
31031
+ const existing = state.views.findIndex((v) => functionIdFor(v.name, v.schema) === id);
31032
+ if (existing >= 0) state.views[existing] = next;
31033
+ else state.views.push(next);
31034
+ }
31035
+ const ctas = stmt.CreateTableAsStmt;
31036
+ if (ctas && ctas.objtype === "OBJECT_MATVIEW") {
31037
+ const name = ctas.into?.rel?.relname ?? "";
31038
+ if (!name) continue;
31039
+ const schema = ctas.into?.rel?.schemaname ?? null;
31040
+ const next = {
31041
+ name,
31042
+ schema,
31043
+ isMaterialized: true,
31044
+ withCheckOption: "N/A",
31045
+ filepath
31046
+ };
31047
+ const id = functionIdFor(name, schema);
31048
+ const existing = state.views.findIndex((v) => functionIdFor(v.name, v.schema) === id);
31049
+ if (existing >= 0) state.views[existing] = next;
31050
+ else state.views.push(next);
31051
+ }
31052
+ }
31053
+ }
31054
+ function applyDropsForSchemaObjects(stmts, state) {
31055
+ for (const wrap of stmts) {
31056
+ const drop = wrap.stmt?.DropStmt;
31057
+ if (!drop) continue;
31058
+ const removeType = drop.removeType ?? "";
31059
+ const objects = drop.objects ?? [];
31060
+ if (removeType === "OBJECT_TABLE") {
31061
+ const droppedTables = /* @__PURE__ */ new Set();
31062
+ for (const obj of objects) {
31063
+ const items = obj.List?.items ?? [];
31064
+ const last = items[items.length - 1]?.String?.sval;
31065
+ if (last) droppedTables.add(last);
31066
+ }
31067
+ if (droppedTables.size > 0) {
31068
+ for (const t of droppedTables) state.tables.delete(t);
31069
+ state.fks = state.fks.filter((fk) => !droppedTables.has(fk.sourceTable) && !droppedTables.has(fk.targetTable));
31070
+ }
31071
+ } else if (removeType === "OBJECT_TYPE") {
31072
+ const droppedEnums = /* @__PURE__ */ new Set();
31073
+ for (const obj of objects) {
31074
+ const tn = obj.TypeName;
31075
+ if (!tn) continue;
31076
+ const names = (tn.names ?? []).map((s) => s.String?.sval ?? "").filter(Boolean);
31077
+ const last = names[names.length - 1];
31078
+ if (last) droppedEnums.add(last);
31079
+ }
31080
+ for (const e of droppedEnums) state.enums.delete(e);
31081
+ } else if (removeType === "OBJECT_EXTENSION") {
31082
+ const names = /* @__PURE__ */ new Set();
31083
+ for (const obj of objects) {
31084
+ const sval = obj.String?.sval;
31085
+ if (sval) names.add(sval);
31086
+ }
31087
+ if (names.size > 0) state.extensions = state.extensions.filter((e) => !names.has(e.name));
31088
+ } else if (removeType === "OBJECT_TRIGGER") {
31089
+ for (const obj of objects) {
31090
+ const items = obj.List?.items ?? [];
31091
+ if (items.length < 2) continue;
31092
+ const table = items[0]?.String?.sval ?? "";
31093
+ const trigName = items[items.length - 1]?.String?.sval ?? "";
31094
+ if (!table || !trigName) continue;
31095
+ state.triggers = state.triggers.filter((t) => !(t.table === table && t.name === trigName));
31096
+ }
31097
+ } else if (removeType === "OBJECT_FUNCTION" || removeType === "OBJECT_PROCEDURE") {
31098
+ for (const obj of objects) {
31099
+ const items = obj.ObjectWithArgs?.objname?.items ?? obj.ObjectWithArgs?.objname ?? obj.List?.items ?? [];
31100
+ if (!items.length) continue;
31101
+ const segs = items.map((s) => s.String?.sval ?? "").filter(Boolean);
31102
+ if (!segs.length) continue;
31103
+ const name = segs[segs.length - 1];
31104
+ const schema = segs.length > 1 ? segs[segs.length - 2] : null;
31105
+ const id = functionIdFor(name, schema);
31106
+ state.functions = state.functions.filter((f) => functionIdFor(f.name, f.schema) !== id);
31107
+ }
31108
+ } else if (removeType === "OBJECT_VIEW" || removeType === "OBJECT_MATVIEW") {
31109
+ for (const obj of objects) {
31110
+ const items = obj.List?.items ?? [];
31111
+ if (!items.length) continue;
31112
+ const name = items[items.length - 1]?.String?.sval ?? "";
31113
+ const schema = items.length > 1 ? items[items.length - 2]?.String?.sval ?? null : null;
31114
+ if (!name) continue;
31115
+ const id = functionIdFor(name, schema);
31116
+ state.views = state.views.filter((v) => functionIdFor(v.name, v.schema) !== id);
31117
+ }
31118
+ }
31119
+ }
31120
+ }
31121
+ function indexIsPrismaUncoverable(idx) {
31122
+ return idx.hasPredicate || idx.hasExpressions || idx.method !== "btree";
31123
+ }
30667
31124
  function loadPrismaState(schemaPath) {
30668
31125
  if (!schemaPath || !(0, import_node_fs18.existsSync)(schemaPath)) return null;
30669
31126
  const content = (0, import_node_fs18.readFileSync)(schemaPath, "utf-8");
@@ -30830,6 +31287,96 @@ function verify(sqlState, prisma) {
30830
31287
  }
30831
31288
  return { contradictions, flaggedEdges };
30832
31289
  }
31290
+ function deriveMigrationName(sqlPath) {
31291
+ const segments = sqlPath.split(/[\\/]/);
31292
+ const last = segments[segments.length - 1];
31293
+ if (last === "migration.sql" && segments.length >= 2) {
31294
+ return segments[segments.length - 2];
31295
+ }
31296
+ return last.replace(/\.sql$/, "");
31297
+ }
31298
+ function extractMigrationInfoFromStmts(stmts, name, filepath) {
31299
+ let isDestructive = false;
31300
+ let hasOrphanCheck = false;
31301
+ let hasSidecarBackup = false;
31302
+ let hasPreFlightNotice = false;
31303
+ let containsBackfill = false;
31304
+ let containsDropColumn = false;
31305
+ let containsDropTable = false;
31306
+ for (const wrap of stmts) {
31307
+ const stmt = wrap.stmt ?? {};
31308
+ const kind = Object.keys(stmt)[0];
31309
+ if (!kind) continue;
31310
+ const body = stmt[kind] ?? {};
31311
+ switch (kind) {
31312
+ case "AlterTableStmt": {
31313
+ const cmds = body.cmds ?? [];
31314
+ for (const c of cmds) {
31315
+ const subtype = c.AlterTableCmd?.subtype;
31316
+ if (subtype === "AT_DropColumn") {
31317
+ containsDropColumn = true;
31318
+ isDestructive = true;
31319
+ } else if (subtype === "AT_AlterColumnType" || subtype === "AT_DropNotNull" || subtype === "AT_DropConstraint") {
31320
+ isDestructive = true;
31321
+ }
31322
+ }
31323
+ break;
31324
+ }
31325
+ case "DropStmt": {
31326
+ const removeType = body.removeType ?? "";
31327
+ if (removeType === "OBJECT_TABLE") {
31328
+ containsDropTable = true;
31329
+ isDestructive = true;
31330
+ } else if (removeType === "OBJECT_TYPE" || removeType === "OBJECT_COLUMN" || removeType === "OBJECT_INDEX" || removeType === "OBJECT_POLICY") {
31331
+ isDestructive = true;
31332
+ }
31333
+ break;
31334
+ }
31335
+ case "CreateStmt": {
31336
+ const relname = body.relation?.relname ?? "";
31337
+ if (relname.startsWith("_backup_")) hasSidecarBackup = true;
31338
+ break;
31339
+ }
31340
+ case "CreateTableAsStmt": {
31341
+ const relname = body.into?.rel?.relname ?? "";
31342
+ if (relname.startsWith("_backup_")) hasSidecarBackup = true;
31343
+ break;
31344
+ }
31345
+ case "UpdateStmt":
31346
+ case "InsertStmt":
31347
+ case "DeleteStmt": {
31348
+ containsBackfill = true;
31349
+ break;
31350
+ }
31351
+ case "DoStmt": {
31352
+ const args = body.args ?? [];
31353
+ for (const arg of args) {
31354
+ const def = arg.DefElem;
31355
+ if (!def || def.defname !== "as") continue;
31356
+ const code = def.arg?.String?.sval ?? "";
31357
+ if (/\bRAISE\s+EXCEPTION\b/i.test(code)) hasOrphanCheck = true;
31358
+ if (/\bRAISE\s+NOTICE\b/i.test(code)) hasPreFlightNotice = true;
31359
+ }
31360
+ break;
31361
+ }
31362
+ }
31363
+ }
31364
+ const tsMatch = name.match(/^(\d{8,14})/);
31365
+ const timestamp = tsMatch ? tsMatch[1] : null;
31366
+ return {
31367
+ name,
31368
+ filepath,
31369
+ timestamp,
31370
+ isDestructive,
31371
+ hasOrphanCheck,
31372
+ hasSidecarBackup,
31373
+ hasPreFlightNotice,
31374
+ containsBackfill,
31375
+ containsDropColumn,
31376
+ containsDropTable,
31377
+ statementCount: stmts.length
31378
+ };
31379
+ }
30833
31380
  function migrationsDirFor(rootDir) {
30834
31381
  const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
30835
31382
  if (!paths) return null;
@@ -30884,6 +31431,132 @@ function generate3(rootDir) {
30884
31431
  values: [...sqlEnum.values]
30885
31432
  });
30886
31433
  }
31434
+ let indexNodeCount = 0;
31435
+ for (const idx of sqlState.indexes) {
31436
+ if (!indexIsPrismaUncoverable(idx)) continue;
31437
+ nodes.push({
31438
+ id: `index:${idx.name}`,
31439
+ type: "index",
31440
+ name: idx.name,
31441
+ source: "sql",
31442
+ table: idx.table,
31443
+ unique: idx.unique,
31444
+ method: idx.method,
31445
+ columns: idx.columns,
31446
+ has_expressions: idx.hasExpressions,
31447
+ has_predicate: idx.hasPredicate,
31448
+ filepath: idx.filepath
31449
+ });
31450
+ indexNodeCount++;
31451
+ }
31452
+ let extensionNodeCount = 0;
31453
+ for (const ext of sqlState.extensions) {
31454
+ nodes.push({
31455
+ id: `extension:${ext.name}`,
31456
+ type: "extension",
31457
+ name: ext.name,
31458
+ source: "sql",
31459
+ schema: ext.schema,
31460
+ version: ext.version,
31461
+ filepath: ext.filepath
31462
+ });
31463
+ extensionNodeCount++;
31464
+ }
31465
+ let triggerNodeCount = 0;
31466
+ for (const trg of sqlState.triggers) {
31467
+ nodes.push({
31468
+ id: `trigger:${trg.table}:${trg.name}`,
31469
+ type: "trigger",
31470
+ name: trg.name,
31471
+ source: "sql",
31472
+ table: trg.table,
31473
+ timing: trg.timing,
31474
+ events: trg.events,
31475
+ function: trg.function,
31476
+ has_when: trg.hasWhen,
31477
+ for_each: trg.forEach,
31478
+ filepath: trg.filepath
31479
+ });
31480
+ triggerNodeCount++;
31481
+ }
31482
+ let functionNodeCount = 0;
31483
+ for (const fn of sqlState.functions) {
31484
+ const qualified = fn.schema ? `${fn.schema}.${fn.name}` : fn.name;
31485
+ nodes.push({
31486
+ id: `function:${qualified}`,
31487
+ type: "function",
31488
+ name: fn.name,
31489
+ source: "sql",
31490
+ schema: fn.schema,
31491
+ language: fn.language,
31492
+ return_type: fn.returnType,
31493
+ is_procedure: fn.isProcedure,
31494
+ filepath: fn.filepath
31495
+ });
31496
+ functionNodeCount++;
31497
+ }
31498
+ let viewNodeCount = 0;
31499
+ for (const vw of sqlState.views) {
31500
+ const qualified = vw.schema ? `${vw.schema}.${vw.name}` : vw.name;
31501
+ nodes.push({
31502
+ id: `${vw.isMaterialized ? "matview" : "view"}:${qualified}`,
31503
+ type: vw.isMaterialized ? "materialized_view" : "view",
31504
+ name: vw.name,
31505
+ source: "sql",
31506
+ schema: vw.schema,
31507
+ is_materialized: vw.isMaterialized,
31508
+ with_check_option: vw.withCheckOption,
31509
+ filepath: vw.filepath
31510
+ });
31511
+ viewNodeCount++;
31512
+ }
31513
+ let policyNodeCount = 0;
31514
+ for (const pol of sqlState.policies) {
31515
+ nodes.push({
31516
+ id: `policy:${pol.table}:${pol.name}`,
31517
+ type: "policy",
31518
+ name: pol.name,
31519
+ source: "sql",
31520
+ table: pol.table,
31521
+ command: pol.command,
31522
+ permissive: pol.permissive,
31523
+ roles: pol.roles,
31524
+ has_using: pol.hasUsing,
31525
+ has_with_check: pol.hasWithCheck,
31526
+ filepath: pol.filepath
31527
+ });
31528
+ policyNodeCount++;
31529
+ }
31530
+ const migrationFiles = migrationsDir ? discoverMigrationFiles(migrationsDir) : [];
31531
+ let migrationNodeCount = 0;
31532
+ for (const sqlPath of migrationFiles) {
31533
+ const sql = (0, import_node_fs18.readFileSync)(sqlPath, "utf-8");
31534
+ const name = deriveMigrationName(sqlPath);
31535
+ let ast;
31536
+ try {
31537
+ ast = postgresDialect.parse(sql);
31538
+ } catch {
31539
+ ast = { stmts: [] };
31540
+ }
31541
+ const info = postgresDialect.extractMigrationInfo(ast, name, sqlPath);
31542
+ nodes.push({
31543
+ id: `migration:${name}`,
31544
+ type: "migration",
31545
+ name,
31546
+ source: "sql",
31547
+ filepath: info.filepath,
31548
+ timestamp: info.timestamp,
31549
+ is_destructive: info.isDestructive,
31550
+ has_orphan_check: info.hasOrphanCheck,
31551
+ has_sidecar_backup: info.hasSidecarBackup,
31552
+ has_pre_flight_notice: info.hasPreFlightNotice,
31553
+ contains_backfill: info.containsBackfill,
31554
+ contains_drop_column: info.containsDropColumn,
31555
+ contains_drop_table: info.containsDropTable,
31556
+ statement_count: info.statementCount
31557
+ });
31558
+ migrationNodeCount++;
31559
+ }
30887
31560
  const sqlOnlyTables = new Set(nodes.filter((n) => n.type === "table").map((n) => n.id));
30888
31561
  const edges = sqlState.fks.filter((fk) => sqlOnlyTables.has(fk.sourceTable)).map((fk) => ({
30889
31562
  source: fk.sourceTable,
@@ -30902,6 +31575,13 @@ function generate3(rootDir) {
30902
31575
  sql_tables: sqlState.tables.size,
30903
31576
  sql_enums: sqlState.enums.size,
30904
31577
  sql_fks: sqlState.fks.length,
31578
+ sql_index_nodes: indexNodeCount,
31579
+ sql_policy_nodes: policyNodeCount,
31580
+ sql_extension_nodes: extensionNodeCount,
31581
+ sql_trigger_nodes: triggerNodeCount,
31582
+ sql_function_nodes: functionNodeCount,
31583
+ sql_view_nodes: viewNodeCount,
31584
+ sql_migration_nodes: migrationNodeCount,
30905
31585
  additive_nodes: nodes.length,
30906
31586
  contradictions_found: contradictions.length,
30907
31587
  flagged_edges_found: flaggedEdges.length
@@ -30926,12 +31606,12 @@ init_launch_kit_paths();
30926
31606
  function loadApiRoutesFromOutput(apiOutput) {
30927
31607
  const routes = [];
30928
31608
  for (const n of apiOutput.nodes) {
30929
- const path12 = n.path;
30930
- if (!path12 || typeof path12 !== "string") continue;
31609
+ const path13 = n.path;
31610
+ if (!path13 || typeof path13 !== "string") continue;
30931
31611
  routes.push({
30932
- path: path12,
31612
+ path: path13,
30933
31613
  nodeId: n.id,
30934
- segments: path12.split("/").filter(Boolean)
31614
+ segments: path13.split("/").filter(Boolean)
30935
31615
  });
30936
31616
  }
30937
31617
  return routes;
@@ -31023,16 +31703,16 @@ function resolveFetchCall(call, apiPathMap, apiRoutes) {
31023
31703
  if (call.isConcat) {
31024
31704
  return { kind: "dynamic", normalizedUrl: raw };
31025
31705
  }
31026
- const { path: path12, hadInterpolation } = normalizeFetchUrl(raw);
31027
- if (!path12.startsWith("/")) {
31028
- return { kind: "unresolved", normalizedUrl: path12 };
31706
+ const { path: path13, hadInterpolation } = normalizeFetchUrl(raw);
31707
+ if (!path13.startsWith("/")) {
31708
+ return { kind: "unresolved", normalizedUrl: path13 };
31029
31709
  }
31030
- const segs = path12.split("/").filter(Boolean);
31710
+ const segs = path13.split("/").filter(Boolean);
31031
31711
  if (hadInterpolation && segs.length > 0 && segs[0].startsWith(":")) {
31032
- return { kind: "dynamic", normalizedUrl: path12 };
31712
+ return { kind: "dynamic", normalizedUrl: path13 };
31033
31713
  }
31034
- const exact = apiPathMap.get(path12);
31035
- if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path12 };
31714
+ const exact = apiPathMap.get(path13);
31715
+ if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path13 };
31036
31716
  let bestScore = -1;
31037
31717
  let bestId = null;
31038
31718
  for (const r of apiRoutes) {
@@ -31043,21 +31723,21 @@ function resolveFetchCall(call, apiPathMap, apiRoutes) {
31043
31723
  }
31044
31724
  }
31045
31725
  if (bestId && bestScore > 0) {
31046
- return { kind: "resolved", nodeId: bestId, normalizedUrl: path12 };
31726
+ return { kind: "resolved", nodeId: bestId, normalizedUrl: path13 };
31047
31727
  }
31048
- return { kind: "unresolved", normalizedUrl: path12 };
31728
+ return { kind: "unresolved", normalizedUrl: path13 };
31049
31729
  }
31050
31730
  function resolveUrlPath(urlPath, apiPathMap, apiRoutes) {
31051
- const { path: path12, hadInterpolation } = normalizeFetchUrl(urlPath);
31052
- if (!path12.startsWith("/")) {
31053
- return { kind: "unresolved", normalizedUrl: path12 };
31731
+ const { path: path13, hadInterpolation } = normalizeFetchUrl(urlPath);
31732
+ if (!path13.startsWith("/")) {
31733
+ return { kind: "unresolved", normalizedUrl: path13 };
31054
31734
  }
31055
- const segs = path12.split("/").filter(Boolean);
31735
+ const segs = path13.split("/").filter(Boolean);
31056
31736
  if (hadInterpolation && segs.length > 0 && segs[0].startsWith(":")) {
31057
- return { kind: "dynamic", normalizedUrl: path12 };
31737
+ return { kind: "dynamic", normalizedUrl: path13 };
31058
31738
  }
31059
- const exact = apiPathMap.get(path12);
31060
- if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path12 };
31739
+ const exact = apiPathMap.get(path13);
31740
+ if (exact) return { kind: "resolved", nodeId: exact, normalizedUrl: path13 };
31061
31741
  let bestScore = -1;
31062
31742
  let bestId = null;
31063
31743
  for (const r of apiRoutes) {
@@ -31068,9 +31748,9 @@ function resolveUrlPath(urlPath, apiPathMap, apiRoutes) {
31068
31748
  }
31069
31749
  }
31070
31750
  if (bestId && bestScore > 0) {
31071
- return { kind: "resolved", nodeId: bestId, normalizedUrl: path12 };
31751
+ return { kind: "resolved", nodeId: bestId, normalizedUrl: path13 };
31072
31752
  }
31073
- return { kind: "unresolved", normalizedUrl: path12 };
31753
+ return { kind: "unresolved", normalizedUrl: path13 };
31074
31754
  }
31075
31755
 
31076
31756
  // src/server/graph/parsers/crosslayer/fetch-resolver.ts
@@ -32048,9 +32728,9 @@ function collectTargets(apiOutput, uiOutput) {
32048
32728
  const out = [];
32049
32729
  for (const n of apiOutput?.nodes ?? []) {
32050
32730
  if (n.type !== "endpoint") continue;
32051
- const path12 = n.path;
32052
- if (typeof path12 !== "string" || !path12) continue;
32053
- out.push({ id: n.id, route: path12, layer: "api" });
32731
+ const path13 = n.path;
32732
+ if (typeof path13 !== "string" || !path13) continue;
32733
+ out.push({ id: n.id, route: path13, layer: "api" });
32054
32734
  }
32055
32735
  for (const n of uiOutput?.nodes ?? []) {
32056
32736
  if (n.type !== "page") continue;
@@ -33042,15 +33722,75 @@ function runAudit(rootDir, layer, check) {
33042
33722
  }
33043
33723
 
33044
33724
  // src/server/graph/core/projects.ts
33725
+ var import_node_path31 = require("node:path");
33726
+
33727
+ // src/server/lib/worktree.ts
33728
+ var import_node_path30 = require("node:path");
33729
+
33730
+ // src/server/orbit/registry.ts
33731
+ var import_node_fs26 = require("node:fs");
33732
+ var import_node_os5 = require("node:os");
33045
33733
  var import_node_path29 = require("node:path");
33734
+ init_launch_kit_paths();
33735
+ var REGISTRY_DIR = (0, import_node_path29.join)((0, import_node_os5.homedir)(), LAUNCHSECURE_DIR, "orbit");
33736
+ var REGISTRY_PATH = (0, import_node_path29.join)(REGISTRY_DIR, "state.json");
33737
+ var LOCK_PATH = (0, import_node_path29.join)(REGISTRY_DIR, "state.json.lock");
33738
+ function emptyRegistry() {
33739
+ return { version: 1, worktrees: {} };
33740
+ }
33741
+ function readRegistry() {
33742
+ if (!(0, import_node_fs26.existsSync)(REGISTRY_PATH)) return emptyRegistry();
33743
+ try {
33744
+ const parsed = JSON.parse((0, import_node_fs26.readFileSync)(REGISTRY_PATH, "utf-8"));
33745
+ if (parsed?.version === 1 && parsed.worktrees && typeof parsed.worktrees === "object") {
33746
+ return parsed;
33747
+ }
33748
+ } catch {
33749
+ }
33750
+ return emptyRegistry();
33751
+ }
33752
+ function listWorktrees() {
33753
+ return Object.values(readRegistry().worktrees);
33754
+ }
33755
+
33756
+ // src/server/lib/worktree.ts
33757
+ var 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`.";
33758
+ var 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.";
33759
+ function resolveWorktreeRoot(slug, monorepoRoot) {
33760
+ const local = listWorktrees().filter((w) => w.projectRoot === monorepoRoot);
33761
+ const match = local.find((w) => w.slug === slug);
33762
+ if (match) return match.path;
33763
+ if (local.length === 0) {
33764
+ throw new Error(
33765
+ `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.`
33766
+ );
33767
+ }
33768
+ const available = local.map((w) => `"${w.slug}" (${w.branch})`).join(", ");
33769
+ throw new Error(`Unknown worktree "${slug}". Available: ${available}.`);
33770
+ }
33771
+ function resolveWorktreeOrProjectRoot(args, monorepoRoot) {
33772
+ const projectRoot = typeof args.project_root === "string" ? args.project_root.trim() : "";
33773
+ if (projectRoot) {
33774
+ return (0, import_node_path30.isAbsolute)(projectRoot) ? projectRoot : (0, import_node_path30.resolve)(monorepoRoot, projectRoot);
33775
+ }
33776
+ const worktree = typeof args.worktree === "string" ? args.worktree.trim() : "";
33777
+ if (worktree) {
33778
+ return resolveWorktreeRoot(worktree, monorepoRoot);
33779
+ }
33780
+ return null;
33781
+ }
33782
+
33783
+ // src/server/graph/core/projects.ts
33046
33784
  init_config();
33785
+ var WORKTREE_PARAM_DESCRIPTION2 = WORKTREE_PARAM_DESCRIPTION;
33786
+ var PROJECT_ROOT_PARAM_DESCRIPTION2 = PROJECT_ROOT_PARAM_DESCRIPTION;
33047
33787
  function listProjects(monorepoRoot) {
33048
33788
  const cfg = loadConfig(monorepoRoot);
33049
33789
  const entries = cfg.projects ?? [];
33050
33790
  return entries.map((p) => ({
33051
33791
  name: p.name,
33052
33792
  root: p.root,
33053
- absoluteRoot: (0, import_node_path29.resolve)(monorepoRoot, p.root)
33793
+ absoluteRoot: (0, import_node_path31.resolve)(monorepoRoot, p.root)
33054
33794
  }));
33055
33795
  }
33056
33796
  function resolveProject(name, projects) {
@@ -33073,6 +33813,12 @@ function resolveProjectRoot(project, monorepoRoot) {
33073
33813
  return resolveProject(raw, projects).absoluteRoot;
33074
33814
  }
33075
33815
  var 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.";
33816
+ function resolveRequestRoot(args, monorepoRoot) {
33817
+ const fromArgs = resolveWorktreeOrProjectRoot(args, monorepoRoot);
33818
+ if (fromArgs) return fromArgs;
33819
+ const project = typeof args.project === "string" ? args.project : void 0;
33820
+ return resolveProjectRoot(project, monorepoRoot);
33821
+ }
33076
33822
 
33077
33823
  // src/server/graph-mcp.ts
33078
33824
  init_freshness();
@@ -33094,13 +33840,21 @@ var TOOLS = [
33094
33840
  project: {
33095
33841
  type: "string",
33096
33842
  description: PROJECT_PARAM_DESCRIPTION + " Special: omit to regenerate ALL configured projects."
33843
+ },
33844
+ worktree: {
33845
+ type: "string",
33846
+ description: WORKTREE_PARAM_DESCRIPTION2
33847
+ },
33848
+ project_root: {
33849
+ type: "string",
33850
+ description: PROJECT_ROOT_PARAM_DESCRIPTION2
33097
33851
  }
33098
33852
  }
33099
33853
  }
33100
33854
  },
33101
33855
  {
33102
33856
  name: "read_graph",
33103
- 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.',
33857
+ 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.',
33104
33858
  inputSchema: {
33105
33859
  type: "object",
33106
33860
  properties: {
@@ -33110,7 +33864,7 @@ var TOOLS = [
33110
33864
  },
33111
33865
  search: {
33112
33866
  type: "string",
33113
- description: "Case-insensitive substring match against node id, name, or route."
33867
+ 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)."
33114
33868
  },
33115
33869
  type: {
33116
33870
  type: "string",
@@ -33144,6 +33898,10 @@ var TOOLS = [
33144
33898
  type: "boolean",
33145
33899
  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."
33146
33900
  },
33901
+ include_findings: {
33902
+ type: "boolean",
33903
+ 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."
33904
+ },
33147
33905
  offset: {
33148
33906
  type: "number",
33149
33907
  description: "Skip first N matched nodes (pagination). Default 0. Use next_offset from a previous response to get the next page."
@@ -33154,7 +33912,7 @@ var TOOLS = [
33154
33912
  },
33155
33913
  queries: {
33156
33914
  type: "array",
33157
- 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.",
33915
+ 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.",
33158
33916
  items: {
33159
33917
  type: "object",
33160
33918
  properties: {
@@ -33166,13 +33924,24 @@ var TOOLS = [
33166
33924
  hops: { type: "number" },
33167
33925
  minimal: { type: "boolean" },
33168
33926
  include_edges: { type: "boolean" },
33169
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
33927
+ include_findings: { type: "boolean" },
33928
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
33929
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
33930
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33170
33931
  }
33171
33932
  }
33172
33933
  },
33173
33934
  project: {
33174
33935
  type: "string",
33175
33936
  description: PROJECT_PARAM_DESCRIPTION
33937
+ },
33938
+ worktree: {
33939
+ type: "string",
33940
+ description: WORKTREE_PARAM_DESCRIPTION2
33941
+ },
33942
+ project_root: {
33943
+ type: "string",
33944
+ description: PROJECT_ROOT_PARAM_DESCRIPTION2
33176
33945
  }
33177
33946
  }
33178
33947
  }
@@ -33220,7 +33989,9 @@ Returns: { pattern, filter, files_searched, total_matches, matches: [{file, line
33220
33989
  context: { type: "number", description: "Context lines around each match. Default 2." },
33221
33990
  max_matches: { type: "number", description: "Max matches to return total. Default 50." },
33222
33991
  max_files: { type: "number", description: "Max files to search. Default 50." },
33223
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
33992
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
33993
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
33994
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33224
33995
  },
33225
33996
  required: ["layer", "pattern"]
33226
33997
  }
@@ -33262,7 +34033,9 @@ Returns deep fields only \u2014 not structural metadata (use read_graph for that
33262
34033
  type: "boolean",
33263
34034
  description: "Case-insensitive filter matching. Default true."
33264
34035
  },
33265
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
34036
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
34037
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
34038
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33266
34039
  },
33267
34040
  required: ["layer"]
33268
34041
  }
@@ -33313,7 +34086,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
33313
34086
  type: "string",
33314
34087
  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.'
33315
34088
  },
33316
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
34089
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
34090
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
34091
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33317
34092
  }
33318
34093
  }
33319
34094
  },
@@ -33343,7 +34118,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
33343
34118
  type: "string",
33344
34119
  description: 'Tag value (e.g. "auth", "alice", "true").'
33345
34120
  },
33346
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
34121
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
34122
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
34123
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33347
34124
  },
33348
34125
  required: ["node_id", "key", "value"]
33349
34126
  }
@@ -33362,7 +34139,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
33362
34139
  type: "string",
33363
34140
  description: "Tag key to remove."
33364
34141
  },
33365
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
34142
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
34143
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
34144
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33366
34145
  },
33367
34146
  required: ["node_id", "key"]
33368
34147
  }
@@ -33381,7 +34160,9 @@ Use this when the user asks "is the chart running", "show me the project graph U
33381
34160
  type: "string",
33382
34161
  description: "Specific check to run (e.g. 'schema_drift', 'unprotected_routes'). Omit to run all checks for the layer."
33383
34162
  },
33384
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
34163
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
34164
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
34165
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33385
34166
  },
33386
34167
  required: ["layer"]
33387
34168
  }
@@ -33417,10 +34198,56 @@ Example: blast_points(node_id: "server/auth/middleware.ts", hops: 2) \u2192 retu
33417
34198
  enum: ["reverse", "both"],
33418
34199
  description: "'reverse' (default) = only what depends on this node. 'both' = full neighborhood."
33419
34200
  },
33420
- project: { type: "string", description: PROJECT_PARAM_DESCRIPTION }
34201
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
34202
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
34203
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
33421
34204
  },
33422
34205
  required: ["node_id"]
33423
34206
  }
34207
+ },
34208
+ {
34209
+ name: "list_notes",
34210
+ 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?}] }.',
34211
+ inputSchema: {
34212
+ type: "object",
34213
+ properties: {
34214
+ layer: {
34215
+ type: "string",
34216
+ description: "Restrict to one layer ('ui', 'api', 'db'). Default: scan all layers that have notes."
34217
+ },
34218
+ kind: {
34219
+ type: "string",
34220
+ description: 'Exact kind match, case-insensitive (e.g. "TODO", "FIXME", "SECURITY").'
34221
+ },
34222
+ category: {
34223
+ type: "string",
34224
+ description: "'actionable' | 'warning' | 'doc' | 'custom' | 'all'. Comma-separated list also accepted. Default: 'actionable,warning'."
34225
+ },
34226
+ module: {
34227
+ type: "string",
34228
+ description: 'Restrict to nodes carrying this module tag (e.g. "auth", "work-items").'
34229
+ },
34230
+ pattern: {
34231
+ type: "string",
34232
+ description: 'Regex applied to note body text (case-insensitive). Use to grep within bodies, e.g. "race condition".'
34233
+ },
34234
+ author: {
34235
+ type: "string",
34236
+ description: "Match author from `// TODO(alice): \u2026` syntax, case-insensitive."
34237
+ },
34238
+ limit: {
34239
+ type: "number",
34240
+ description: "Max items returned. Default 100. Pair with offset for pagination."
34241
+ },
34242
+ offset: {
34243
+ type: "number",
34244
+ description: "Skip first N items. Default 0."
34245
+ },
34246
+ project: { type: "string", description: PROJECT_PARAM_DESCRIPTION },
34247
+ worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION2 },
34248
+ project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION2 }
34249
+ }
34250
+ }
33424
34251
  }
33425
34252
  ];
33426
34253
  function matchesSearch(node, query) {
@@ -33429,14 +34256,44 @@ function matchesSearch(node, query) {
33429
34256
  if (node.name.toLowerCase().includes(q)) return true;
33430
34257
  const route = node.route;
33431
34258
  if (route && route.toLowerCase().includes(q)) return true;
34259
+ const elements = node.elements;
34260
+ if (elements) {
34261
+ for (const el of elements) {
34262
+ if (el.text && el.text.toLowerCase().includes(q)) return true;
34263
+ if (el.props) {
34264
+ for (const v of Object.values(el.props)) {
34265
+ if (typeof v !== "string") continue;
34266
+ if (v.includes("=>") || v.startsWith("(") || v.startsWith("{")) continue;
34267
+ if (v.toLowerCase().includes(q)) return true;
34268
+ }
34269
+ }
34270
+ }
34271
+ }
34272
+ const uiLabels = node.ui_labels;
34273
+ if (uiLabels) {
34274
+ for (const lbl of uiLabels) {
34275
+ if (typeof lbl === "string" && lbl.toLowerCase().includes(q)) return true;
34276
+ }
34277
+ }
34278
+ const notes = node.notes;
34279
+ if (notes) {
34280
+ for (const n of notes) {
34281
+ if (n.kind && n.kind.toLowerCase().includes(q)) return true;
34282
+ if (n.text && n.text.toLowerCase().includes(q)) return true;
34283
+ }
34284
+ }
33432
34285
  return false;
33433
34286
  }
34287
+ var MINIMAL_STRIP_FIELDS = /* @__PURE__ */ new Set(["columns"]);
33434
34288
  function toMinimal(nodes) {
33435
34289
  return nodes.map((n) => {
33436
34290
  const out = { id: n.id, type: n.type, name: n.name };
33437
- if (n.tags != null) out.tags = n.tags;
33438
- if (n.route != null) out.route = n.route;
33439
- if (n.methods != null) out.methods = n.methods;
34291
+ for (const [k, v] of Object.entries(n)) {
34292
+ if (k === "id" || k === "type" || k === "name") continue;
34293
+ if (MINIMAL_STRIP_FIELDS.has(k)) continue;
34294
+ if (DEEP_FIELDS.has(k)) continue;
34295
+ if (v != null) out[k] = v;
34296
+ }
33440
34297
  return out;
33441
34298
  });
33442
34299
  }
@@ -33487,8 +34344,34 @@ var DEEP_FIELDS = /* @__PURE__ */ new Set([
33487
34344
  "variables",
33488
34345
  "responses",
33489
34346
  "params",
33490
- "effects"
34347
+ "effects",
34348
+ "ui_labels",
34349
+ "notes"
33491
34350
  ]);
34351
+ var NOTE_KIND_CATEGORY = {
34352
+ TODO: "actionable",
34353
+ FIXME: "actionable",
34354
+ HACK: "actionable",
34355
+ XXX: "actionable",
34356
+ REFACTOR: "actionable",
34357
+ OPTIMIZE: "actionable",
34358
+ REVIEW: "actionable",
34359
+ SECURITY: "warning",
34360
+ WARNING: "warning",
34361
+ IMPORTANT: "warning",
34362
+ CAUTION: "warning",
34363
+ DANGER: "warning",
34364
+ DEPRECATED: "warning",
34365
+ NOTE: "doc",
34366
+ TIP: "doc",
34367
+ SEE: "doc",
34368
+ NB: "doc",
34369
+ INFO: "doc",
34370
+ EXAMPLE: "doc"
34371
+ };
34372
+ function categorizeNoteKind(kind) {
34373
+ return NOTE_KIND_CATEGORY[kind] ?? "custom";
34374
+ }
33492
34375
  var EST_CHARS_PER_NODE_FULL = {
33493
34376
  ui: 300,
33494
34377
  api: 300,
@@ -33764,7 +34647,7 @@ function withFreshnessMeta(result, args) {
33764
34647
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return result;
33765
34648
  let rootDir;
33766
34649
  try {
33767
- rootDir = resolveProjectRoot(args.project, process.cwd());
34650
+ rootDir = resolveRequestRoot(args, process.cwd());
33768
34651
  } catch {
33769
34652
  return result;
33770
34653
  }
@@ -33779,7 +34662,7 @@ function withFreshnessMeta(result, args) {
33779
34662
  }
33780
34663
  function resolveOrErr(args) {
33781
34664
  try {
33782
- return { rootDir: resolveProjectRoot(args.project, process.cwd()) };
34665
+ return { rootDir: resolveRequestRoot(args, process.cwd()) };
33783
34666
  } catch (e) {
33784
34667
  return err(e.message);
33785
34668
  }
@@ -33788,6 +34671,8 @@ async function handleGenerateGraph(args) {
33788
34671
  const monorepoRoot = process.cwd();
33789
34672
  const layer = args.layer;
33790
34673
  const projectArg = typeof args.project === "string" ? args.project.trim() : "";
34674
+ const worktreeArg = typeof args.worktree === "string" ? args.worktree.trim() : "";
34675
+ const projectRootArg = typeof args.project_root === "string" ? args.project_root.trim() : "";
33791
34676
  function formatProjectResult(results2, relativeRoot) {
33792
34677
  return results2.map((r) => {
33793
34678
  const warnings = r.output.warnings.length;
@@ -33795,25 +34680,27 @@ async function handleGenerateGraph(args) {
33795
34680
  }).join("\n") + `
33796
34681
  \u2192 ${relativeRoot}/.launchsecure/graphs/`;
33797
34682
  }
33798
- if (projectArg) {
34683
+ if (projectArg || worktreeArg || projectRootArg) {
33799
34684
  let rootDir;
33800
34685
  try {
33801
- rootDir = resolveProjectRoot(projectArg, monorepoRoot);
34686
+ rootDir = resolveRequestRoot(args, monorepoRoot);
33802
34687
  } catch (e) {
33803
34688
  return err(e.message);
33804
34689
  }
33805
34690
  const results2 = await generateGraph(rootDir, layer);
34691
+ const label = worktreeArg ? `worktree "${worktreeArg}"` : projectArg ? `project "${projectArg}"` : `root "${projectRootArg}"`;
34692
+ const queryHint = worktreeArg ? `read_graph (with worktree="${worktreeArg}")` : projectArg ? `read_graph (with project="${projectArg}")` : `read_graph (with project_root="${projectRootArg}")`;
33806
34693
  if (results2.length === 0) {
33807
34694
  return err(
33808
- 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.`
34695
+ layer ? `No parser detected for the "${layer}" layer in ${label}.` : `No parsers detected for ${label}. Check that the root has the expected structure.`
33809
34696
  );
33810
34697
  }
33811
34698
  return ok(
33812
- `Graph generated successfully for project "${projectArg}".
34699
+ `Graph generated successfully for ${label}.
33813
34700
 
33814
- ${formatProjectResult(results2, projectArg)}
34701
+ ${formatProjectResult(results2, rootDir)}
33815
34702
 
33816
- Use read_graph (with project="${projectArg}") to query.`
34703
+ Use ${queryHint} to query.`
33817
34704
  );
33818
34705
  }
33819
34706
  const projects = listProjects(monorepoRoot);
@@ -33876,6 +34763,7 @@ function runReadGraphQueryRaw(rootDir, args) {
33876
34763
  const layerIsDb = args.layer === "db";
33877
34764
  const minimal = args.minimal ?? layerIsDb;
33878
34765
  const includeEdges = args.include_edges;
34766
+ const includeFindings = args.include_findings === true;
33879
34767
  const offset = args.offset ?? 0;
33880
34768
  const limit = args.limit;
33881
34769
  const hasFilter = !!(search || type || module_ || nodeId || tagKey && tagValue);
@@ -33925,14 +34813,23 @@ function runReadGraphQueryRaw(rootDir, args) {
33925
34813
  result2.budget_exceeded = true;
33926
34814
  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.`;
33927
34815
  }
34816
+ if (includeFindings && layer === "db") {
34817
+ result2.contradictions = graph.contradictions ?? [];
34818
+ result2.flagged_edges = graph.flagged_edges ?? [];
34819
+ }
33928
34820
  return result2;
33929
34821
  }
33930
34822
  if (!hasFilter) {
33931
- return {
34823
+ const summaryResult = {
33932
34824
  hint: "No filter specified \u2014 returning summary only. Use search/type/module/node_id to retrieve nodes.",
33933
34825
  layer,
33934
34826
  summary: layerSummary(graph)
33935
34827
  };
34828
+ if (includeFindings && layer === "db") {
34829
+ summaryResult.contradictions = graph.contradictions ?? [];
34830
+ summaryResult.flagged_edges = graph.flagged_edges ?? [];
34831
+ }
34832
+ return summaryResult;
33936
34833
  }
33937
34834
  const matched = graph.nodes.filter((n) => {
33938
34835
  if (search && !matchesSearch(n, search)) return false;
@@ -33983,6 +34880,10 @@ function runReadGraphQueryRaw(rootDir, args) {
33983
34880
  } else if (returnedEdges.length > 0) {
33984
34881
  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).`;
33985
34882
  }
34883
+ if (includeFindings && layer === "db") {
34884
+ result.contradictions = graph.contradictions ?? [];
34885
+ result.flagged_edges = graph.flagged_edges ?? [];
34886
+ }
33986
34887
  return result;
33987
34888
  }
33988
34889
  function runReadGraphQuery(rootDir, args) {
@@ -33997,6 +34898,8 @@ function handleReadGraph(args) {
33997
34898
  return err("queries array is empty. Provide at least one query object.");
33998
34899
  }
33999
34900
  const inheritedProject = typeof args.project === "string" ? args.project : void 0;
34901
+ const inheritedWorktree = typeof args.worktree === "string" ? args.worktree : void 0;
34902
+ const inheritedProjectRoot = typeof args.project_root === "string" ? args.project_root : void 0;
34000
34903
  const results = [];
34001
34904
  let cumulativeChars = 0;
34002
34905
  let budgetHit = false;
@@ -34014,15 +34917,18 @@ function handleReadGraph(args) {
34014
34917
  });
34015
34918
  continue;
34016
34919
  }
34017
- const qWithProject = inheritedProject && !q.project ? { ...q, project: inheritedProject } : q;
34920
+ const qInherited = { ...q };
34921
+ if (inheritedProject && !qInherited.project) qInherited.project = inheritedProject;
34922
+ if (inheritedWorktree && !qInherited.worktree) qInherited.worktree = inheritedWorktree;
34923
+ if (inheritedProjectRoot && !qInherited.project_root) qInherited.project_root = inheritedProjectRoot;
34018
34924
  let perQueryRoot;
34019
34925
  try {
34020
- perQueryRoot = resolveProjectRoot(qWithProject.project, monorepoRoot);
34926
+ perQueryRoot = resolveRequestRoot(qInherited, monorepoRoot);
34021
34927
  } catch (e) {
34022
34928
  results.push({ index: i, query: q, result: { error: e.message } });
34023
34929
  continue;
34024
34930
  }
34025
- const r = runReadGraphQuery(perQueryRoot, qWithProject);
34931
+ const r = runReadGraphQuery(perQueryRoot, qInherited);
34026
34932
  const entry = { index: i, query: q, result: r };
34027
34933
  const entrySize = JSON.stringify(entry, null, 2).length;
34028
34934
  if (cumulativeChars + entrySize > BATCH_BUDGET_CHARS && results.length > 0) {
@@ -34055,12 +34961,12 @@ function handleReadGraph(args) {
34055
34961
  return okJson(result);
34056
34962
  }
34057
34963
  function nodeToFilePath(rootDir, layer, nodeId) {
34058
- if (layer === "ui" || layer === "api") return (0, import_node_path31.join)(rootDir, "src", nodeId);
34059
- if (layer === "db") return (0, import_node_path31.join)(rootDir, "prisma", "schema.prisma");
34060
- const withSrc = (0, import_node_path31.join)(rootDir, "src", nodeId);
34061
- if ((0, import_node_fs27.existsSync)(withSrc)) return withSrc;
34062
- const direct = (0, import_node_path31.join)(rootDir, nodeId);
34063
- if ((0, import_node_fs27.existsSync)(direct)) return direct;
34964
+ if (layer === "ui" || layer === "api") return (0, import_node_path33.join)(rootDir, "src", nodeId);
34965
+ if (layer === "db") return (0, import_node_path33.join)(rootDir, "prisma", "schema.prisma");
34966
+ const withSrc = (0, import_node_path33.join)(rootDir, "src", nodeId);
34967
+ if ((0, import_node_fs28.existsSync)(withSrc)) return withSrc;
34968
+ const direct = (0, import_node_path33.join)(rootDir, nodeId);
34969
+ if ((0, import_node_fs28.existsSync)(direct)) return direct;
34064
34970
  return null;
34065
34971
  }
34066
34972
  function handleInspectNode(args) {
@@ -34090,7 +34996,7 @@ function handleInspectNode(args) {
34090
34996
  } else {
34091
34997
  matched = graph.nodes;
34092
34998
  }
34093
- const allDeepFields = ["elements", "stateVars", "conditions", "variables", "responses", "params", "effects"];
34999
+ const allDeepFields = ["elements", "stateVars", "conditions", "variables", "responses", "params", "effects", "ui_labels", "notes"];
34094
35000
  const requestedFields = fields ?? allDeepFields;
34095
35001
  let filterRegex = null;
34096
35002
  if (filter) {
@@ -34145,6 +35051,101 @@ function handleInspectNode(args) {
34145
35051
  nodes: results
34146
35052
  });
34147
35053
  }
35054
+ function handleListNotes(args) {
35055
+ const __resolved = resolveOrErr(args);
35056
+ if ("content" in __resolved) return __resolved;
35057
+ const { rootDir } = __resolved;
35058
+ const layerArg = args.layer;
35059
+ const kindArg = args.kind?.toUpperCase();
35060
+ const categoryArg = args.category ?? "actionable,warning";
35061
+ const moduleArg = args.module;
35062
+ const patternArg = args.pattern;
35063
+ const authorArg = args.author?.toLowerCase();
35064
+ const limit = args.limit ?? 100;
35065
+ const offset = args.offset ?? 0;
35066
+ let patternRegex = null;
35067
+ if (patternArg) {
35068
+ try {
35069
+ patternRegex = new RegExp(patternArg, "i");
35070
+ } catch {
35071
+ return err(`Invalid regex pattern: "${patternArg}"`);
35072
+ }
35073
+ }
35074
+ const wantedCategories = (() => {
35075
+ const trimmed = categoryArg.trim().toLowerCase();
35076
+ if (trimmed === "all") return "all";
35077
+ const out = /* @__PURE__ */ new Set();
35078
+ for (const part of trimmed.split(",").map((s) => s.trim()).filter(Boolean)) {
35079
+ if (part === "actionable" || part === "warning" || part === "doc" || part === "custom") {
35080
+ out.add(part);
35081
+ }
35082
+ }
35083
+ if (out.size === 0) {
35084
+ out.add("actionable");
35085
+ out.add("warning");
35086
+ }
35087
+ return out;
35088
+ })();
35089
+ const layers = layerArg ? [layerArg] : getAvailableLayers(rootDir);
35090
+ const items = [];
35091
+ for (const layer of layers) {
35092
+ const graph = readGraph(rootDir, layer);
35093
+ if (!graph) continue;
35094
+ for (const node of graph.nodes) {
35095
+ const nodeNotes = node.notes;
35096
+ if (!nodeNotes || nodeNotes.length === 0) continue;
35097
+ const tags = node.tags;
35098
+ const nodeModule = tags?.module;
35099
+ if (moduleArg && nodeModule !== moduleArg) continue;
35100
+ for (const n of nodeNotes) {
35101
+ if (!n.kind || !n.text || typeof n.line !== "number") continue;
35102
+ const category = categorizeNoteKind(n.kind);
35103
+ if (wantedCategories !== "all" && !wantedCategories.has(category)) continue;
35104
+ if (kindArg && n.kind.toUpperCase() !== kindArg) continue;
35105
+ if (authorArg && (!n.author || n.author.toLowerCase() !== authorArg)) continue;
35106
+ if (patternRegex && !patternRegex.test(n.text)) continue;
35107
+ const item = {
35108
+ file: node.id,
35109
+ line: n.line,
35110
+ kind: n.kind,
35111
+ category,
35112
+ text: n.text
35113
+ };
35114
+ if (n.author) item.author = n.author;
35115
+ if (nodeModule) item.module = nodeModule;
35116
+ items.push(item);
35117
+ }
35118
+ }
35119
+ }
35120
+ const catOrder = { actionable: 0, warning: 1, doc: 2, custom: 3 };
35121
+ items.sort((a, b) => {
35122
+ const c = catOrder[a.category] - catOrder[b.category];
35123
+ if (c !== 0) return c;
35124
+ const k = a.kind.localeCompare(b.kind);
35125
+ if (k !== 0) return k;
35126
+ const f = a.file.localeCompare(b.file);
35127
+ if (f !== 0) return f;
35128
+ return a.line - b.line;
35129
+ });
35130
+ const total = items.length;
35131
+ const paged = items.slice(offset, offset + limit);
35132
+ const hasMore = offset + paged.length < total;
35133
+ return okJson({
35134
+ total,
35135
+ returned: paged.length,
35136
+ has_more: hasMore,
35137
+ ...hasMore ? { next_offset: offset + paged.length } : {},
35138
+ filter: {
35139
+ layer: layerArg ?? "all",
35140
+ kind: kindArg ?? null,
35141
+ category: categoryArg,
35142
+ module: moduleArg ?? null,
35143
+ pattern: patternArg ?? null,
35144
+ author: authorArg ?? null
35145
+ },
35146
+ items: paged
35147
+ });
35148
+ }
34148
35149
  function handleGrepNodes(args) {
34149
35150
  const __resolved = resolveOrErr(args);
34150
35151
  if ("content" in __resolved) return __resolved;
@@ -34207,11 +35208,11 @@ function handleGrepNodes(args) {
34207
35208
  let filesSearched = 0;
34208
35209
  let truncated = false;
34209
35210
  for (const [filePath, nodeId] of filePaths) {
34210
- if (!(0, import_node_fs27.existsSync)(filePath)) continue;
35211
+ if (!(0, import_node_fs28.existsSync)(filePath)) continue;
34211
35212
  filesSearched++;
34212
35213
  let content;
34213
35214
  try {
34214
- content = (0, import_node_fs27.readFileSync)(filePath, "utf-8");
35215
+ content = (0, import_node_fs28.readFileSync)(filePath, "utf-8");
34215
35216
  } catch {
34216
35217
  continue;
34217
35218
  }
@@ -34333,16 +35334,15 @@ function handleStartChartServer(args) {
34333
35334
  });
34334
35335
  }
34335
35336
  const entryPath = process.argv[1];
34336
- const logDir = (0, import_node_path31.join)((0, import_node_os5.homedir)(), LAUNCHSECURE_DIR);
34337
- (0, import_node_fs27.mkdirSync)(logDir, { recursive: true });
34338
- const logPath = (0, import_node_path31.join)(logDir, "launch-chart.log");
34339
- const out = (0, import_node_fs27.openSync)(logPath, "a");
34340
- const err2 = (0, import_node_fs27.openSync)(logPath, "a");
35337
+ const logDir = (0, import_node_path33.join)((0, import_node_os6.homedir)(), LAUNCHSECURE_DIR);
35338
+ (0, import_node_fs28.mkdirSync)(logDir, { recursive: true });
35339
+ const logPath = (0, import_node_path33.join)(logDir, "launch-chart.log");
35340
+ const out = (0, import_node_fs28.openSync)(logPath, "a");
35341
+ const err2 = (0, import_node_fs28.openSync)(logPath, "a");
34341
35342
  const portArgs = args.port ? ["--port", String(args.port)] : [];
34342
35343
  const child = (0, import_node_child_process3.spawn)(process.execPath, [entryPath, "serve", ...portArgs], {
34343
35344
  detached: true,
34344
- stdio: ["ignore", out, err2],
34345
- env: { ...process.env, LAUNCH_CHART_AUTOSERVE: "" }
35345
+ stdio: ["ignore", out, err2]
34346
35346
  });
34347
35347
  child.unref();
34348
35348
  return okJson({
@@ -34467,20 +35467,20 @@ function handleDetectProjectStack() {
34467
35467
  if (ref.type === "references_api") stats.references_api++;
34468
35468
  }
34469
35469
  }
34470
- const srcDir = (0, import_node_path31.join)(rootDir, "src");
34471
- if ((0, import_node_fs27.existsSync)(srcDir)) {
35470
+ const srcDir = (0, import_node_path33.join)(rootDir, "src");
35471
+ if ((0, import_node_fs28.existsSync)(srcDir)) {
34472
35472
  const scanDir = (dir) => {
34473
- if (!(0, import_node_fs27.existsSync)(dir)) return;
34474
- for (const entry of (0, import_node_fs27.readdirSync)(dir, { withFileTypes: true })) {
35473
+ if (!(0, import_node_fs28.existsSync)(dir)) return;
35474
+ for (const entry of (0, import_node_fs28.readdirSync)(dir, { withFileTypes: true })) {
34475
35475
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
34476
- const full = (0, import_node_path31.join)(dir, entry.name);
35476
+ const full = (0, import_node_path33.join)(dir, entry.name);
34477
35477
  if (entry.isDirectory()) {
34478
35478
  scanDir(full);
34479
35479
  continue;
34480
35480
  }
34481
- if (![".ts", ".tsx"].includes((0, import_node_path31.extname)(entry.name))) continue;
35481
+ if (![".ts", ".tsx"].includes((0, import_node_path33.extname)(entry.name))) continue;
34482
35482
  try {
34483
- const content = (0, import_node_fs27.readFileSync)(full, "utf-8");
35483
+ const content = (0, import_node_fs28.readFileSync)(full, "utf-8");
34484
35484
  const matches = content.match(/@api\s+(GET|POST|PUT|DELETE|PATCH)\s+\/\S+/g);
34485
35485
  if (matches) stats.annotations += matches.length;
34486
35486
  } catch {
@@ -34499,7 +35499,7 @@ function handleDetectProjectStack() {
34499
35499
  name: p.name,
34500
35500
  root: p.root,
34501
35501
  absolute_root: p.absoluteRoot,
34502
- has_graph: (0, import_node_fs27.existsSync)((0, import_node_path31.join)(p.absoluteRoot, LAUNCHSECURE_DIR, "graphs"))
35502
+ has_graph: (0, import_node_fs28.existsSync)((0, import_node_path33.join)(p.absoluteRoot, LAUNCHSECURE_DIR, "graphs"))
34503
35503
  }));
34504
35504
  return okJson({
34505
35505
  languages,
@@ -34613,6 +35613,10 @@ async function handleMessage(msg) {
34613
35613
  respond(id ?? null, withFreshnessMeta(handleBlastPoints(args), args));
34614
35614
  return;
34615
35615
  }
35616
+ if (toolName === "list_notes") {
35617
+ respond(id ?? null, withFreshnessMeta(handleListNotes(args), args));
35618
+ return;
35619
+ }
34616
35620
  respondError(id ?? null, -32601, `Unknown tool: ${toolName}`);
34617
35621
  return;
34618
35622
  }
@@ -34677,6 +35681,53 @@ function startGraphMcpServer() {
34677
35681
 
34678
35682
  // src/server/cli.ts
34679
35683
  init_launch_kit_paths();
35684
+
35685
+ // src/server/cred-shape.ts
35686
+ var fs9 = __toESM(require("node:fs"));
35687
+ var path11 = __toESM(require("node:path"));
35688
+ var CONFIG_FILENAME = ".launch-secure.cred.config";
35689
+ function inferCourseName(serverUrl) {
35690
+ try {
35691
+ const host = new URL(serverUrl).hostname.toLowerCase();
35692
+ if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".local")) return "local";
35693
+ if (host.includes("staging")) return "staging";
35694
+ if (host.endsWith(".vercel.app")) return "prod";
35695
+ return host.split(".")[0] || "default";
35696
+ } catch {
35697
+ return "default";
35698
+ }
35699
+ }
35700
+ function toNested(cred) {
35701
+ if (cred.profiles && cred.active && cred.profiles[cred.active]) {
35702
+ return { active: cred.active, profiles: cred.profiles };
35703
+ }
35704
+ if (!cred.pat || !cred.orgSlug || !cred.projectSlug || !cred.serverUrl) {
35705
+ return null;
35706
+ }
35707
+ const name = inferCourseName(cred.serverUrl);
35708
+ return {
35709
+ active: name,
35710
+ profiles: {
35711
+ [name]: {
35712
+ pat: cred.pat,
35713
+ orgSlug: cred.orgSlug,
35714
+ projectSlug: cred.projectSlug,
35715
+ serverUrl: cred.serverUrl
35716
+ }
35717
+ }
35718
+ };
35719
+ }
35720
+ function readCredFile(repoRoot) {
35721
+ const p = path11.join(repoRoot, CONFIG_FILENAME);
35722
+ if (!fs9.existsSync(p)) return null;
35723
+ try {
35724
+ return JSON.parse(fs9.readFileSync(p, "utf-8"));
35725
+ } catch (err2) {
35726
+ throw new Error(`could not parse ${CONFIG_FILENAME}: ${err2 instanceof Error ? err2.message : String(err2)}`);
35727
+ }
35728
+ }
35729
+
35730
+ // src/server/cli.ts
34680
35731
  var DEFAULT_CAPABILITIES = {
34681
35732
  workspace_setup: true,
34682
35733
  terminal: true
@@ -34740,7 +35791,7 @@ function parseArgs() {
34740
35791
  return { port, token, serverUrl: LAUNCHSECURE_URL, subcommand };
34741
35792
  }
34742
35793
  function tryListen(server, port, maxRetries = 10) {
34743
- return new Promise((resolve5, reject) => {
35794
+ return new Promise((resolve6, reject) => {
34744
35795
  let attempts = 0;
34745
35796
  function attempt(p) {
34746
35797
  server.once("error", (err2) => {
@@ -34751,7 +35802,7 @@ function tryListen(server, port, maxRetries = 10) {
34751
35802
  reject(err2);
34752
35803
  }
34753
35804
  });
34754
- server.listen(p, "127.0.0.1", () => resolve5(p));
35805
+ server.listen(p, "127.0.0.1", () => resolve6(p));
34755
35806
  }
34756
35807
  attempt(port);
34757
35808
  });
@@ -34772,7 +35823,7 @@ function saveCredentials(creds) {
34772
35823
  });
34773
35824
  }
34774
35825
  function verifyToken(serverUrl, token) {
34775
- return new Promise((resolve5) => {
35826
+ return new Promise((resolve6) => {
34776
35827
  const url = new URL("/api/mcp/verify", serverUrl);
34777
35828
  const body = JSON.stringify({ token });
34778
35829
  const mod = url.protocol === "https:" ? import_https.default : import_http.default;
@@ -34787,30 +35838,30 @@ function verifyToken(serverUrl, token) {
34787
35838
  res.on("data", (chunk) => data += chunk);
34788
35839
  res.on("end", () => {
34789
35840
  try {
34790
- resolve5(JSON.parse(data));
35841
+ resolve6(JSON.parse(data));
34791
35842
  } catch {
34792
- resolve5({ valid: false, error: "Invalid response from server" });
35843
+ resolve6({ valid: false, error: "Invalid response from server" });
34793
35844
  }
34794
35845
  });
34795
35846
  });
34796
35847
  req.on("error", (err2) => {
34797
- resolve5({ valid: false, error: `Cannot reach server: ${err2.message}` });
35848
+ resolve6({ valid: false, error: `Cannot reach server: ${err2.message}` });
34798
35849
  });
34799
35850
  req.setTimeout(1e4, () => {
34800
35851
  req.destroy();
34801
- resolve5({ valid: false, error: "Connection timed out" });
35852
+ resolve6({ valid: false, error: "Connection timed out" });
34802
35853
  });
34803
35854
  req.write(body);
34804
35855
  req.end();
34805
35856
  });
34806
35857
  }
34807
35858
  function httpRequest2(reqUrl, options, body, timeout = 3e4) {
34808
- return new Promise((resolve5, reject) => {
35859
+ return new Promise((resolve6, reject) => {
34809
35860
  const mod = reqUrl.protocol === "https:" ? import_https.default : import_http.default;
34810
35861
  const r = mod.request(reqUrl, options, (resp) => {
34811
35862
  let data = "";
34812
35863
  resp.on("data", (chunk) => data += chunk);
34813
- resp.on("end", () => resolve5({ status: resp.statusCode || 0, headers: resp.headers, body: data }));
35864
+ resp.on("end", () => resolve6({ status: resp.statusCode || 0, headers: resp.headers, body: data }));
34814
35865
  });
34815
35866
  r.on("error", reject);
34816
35867
  r.setTimeout(timeout, () => {
@@ -35290,7 +36341,7 @@ if (parsedArgs.subcommand === "graph:generate" || parsedArgs.subcommand === "gra
35290
36341
  const result = (0, import_child_process4.spawnSync)(
35291
36342
  process.execPath,
35292
36343
  [chartEntry, chartSubcommand, ...process.argv.slice(3)],
35293
- { stdio: "inherit", env: { ...process.env, LAUNCH_CHART_AUTOSERVE: "" } }
36344
+ { stdio: "inherit" }
35294
36345
  );
35295
36346
  process.exit(result.status ?? 1);
35296
36347
  }
@@ -35300,75 +36351,28 @@ if (parsedArgs.subcommand === "mcp:graph") {
35300
36351
  var __isMcpMode = parsedArgs.subcommand === "mcp:graph";
35301
36352
  var __isRadarMode = parsedArgs.subcommand === "radar";
35302
36353
  var radar = null;
35303
- var CRED_CONFIG_FILENAME = ".launch-secure.cred.config";
35304
- var LEGACY_CRED_CONFIG_FILENAME = ".launch-secure.config";
35305
- function migrateLegacyCredConfig(repoRoot) {
35306
- const legacy = import_path9.default.join(repoRoot, LEGACY_CRED_CONFIG_FILENAME);
35307
- const dest = import_path9.default.join(repoRoot, CRED_CONFIG_FILENAME);
35308
- if (!import_fs8.default.existsSync(legacy) || import_fs8.default.existsSync(dest)) return;
35309
- let parsed;
35310
- try {
35311
- parsed = JSON.parse(import_fs8.default.readFileSync(legacy, "utf-8"));
35312
- } catch {
35313
- return;
35314
- }
35315
- const pat = parsed?.pat;
35316
- if (typeof pat !== "string" || !pat.startsWith("ls_pat_")) return;
35317
- import_fs8.default.renameSync(legacy, dest);
35318
- console.warn(`[launchpod] migrated legacy ${LEGACY_CRED_CONFIG_FILENAME} \u2192 ${CRED_CONFIG_FILENAME} (the old name is now reserved for file-backed-config)`);
35319
- }
35320
36354
  function readLaunchSecureMcpConfig() {
35321
- migrateLegacyCredConfig(REPO_ROOT);
35322
- const configPath = import_path9.default.join(REPO_ROOT, CRED_CONFIG_FILENAME);
35323
- if (import_fs8.default.existsSync(configPath)) {
35324
- let parsed;
35325
- try {
35326
- parsed = JSON.parse(import_fs8.default.readFileSync(configPath, "utf-8"));
35327
- } catch (err2) {
35328
- throw new Error(`Could not parse ${configPath}: ${err2 instanceof Error ? err2.message : String(err2)}`);
35329
- }
35330
- const block = parsed.profiles && parsed.active ? parsed.profiles[parsed.active] : parsed;
35331
- if (!block) {
35332
- throw new Error(`${CRED_CONFIG_FILENAME} active course "${parsed.active}" not found in profiles.`);
35333
- }
35334
- const { pat: pat2, orgSlug: orgSlug2, projectSlug: projectSlug2, serverUrl } = block;
35335
- if (!pat2 || !pat2.startsWith("ls_pat_")) {
35336
- throw new Error(`${CRED_CONFIG_FILENAME} "pat" is missing or not a LaunchSecure PAT (ls_pat_...).`);
35337
- }
35338
- if (!orgSlug2 || !projectSlug2 || !serverUrl) {
35339
- throw new Error(`${CRED_CONFIG_FILENAME} is missing required fields (orgSlug, projectSlug, serverUrl).`);
35340
- }
35341
- return { pat: pat2, orgSlug: orgSlug2, projectSlug: projectSlug2, serverUrl, source: CRED_CONFIG_FILENAME };
36355
+ const fix = `Run \`npx @launchsecure/launch-kit@latest refresh\` to re-sync this project (or \`init\` if you haven't bootstrapped yet).`;
36356
+ const cred = readCredFile(REPO_ROOT);
36357
+ if (!cred) {
36358
+ throw new Error(`${CONFIG_FILENAME} not found in ${REPO_ROOT}. ${fix}`);
35342
36359
  }
35343
- const mcpPath = import_path9.default.join(REPO_ROOT, ".mcp.json");
35344
- if (!import_fs8.default.existsSync(mcpPath)) {
35345
- throw new Error(
35346
- `Neither ${CRED_CONFIG_FILENAME} nor .mcp.json found in ${REPO_ROOT}. Run \`npx launch-kit init --token=... --org=... --project=...\` to bootstrap.`
35347
- );
36360
+ const nested = toNested(cred);
36361
+ if (!nested) {
36362
+ throw new Error(`${CONFIG_FILENAME} is malformed or missing required fields (pat/orgSlug/projectSlug/serverUrl). ${fix}`);
35348
36363
  }
35349
- const raw = JSON.parse(import_fs8.default.readFileSync(mcpPath, "utf-8"));
35350
- const envName = process.env.LAUNCHPOD_MCP_NAME;
35351
- const entryName = envName ?? (raw.mcpServers?.["local-launch-secure"] ? "local-launch-secure" : "launch-secure");
35352
- const entry = raw.mcpServers?.[entryName];
35353
- if (!entry?.url) {
35354
- throw new Error(`No "${entryName}" entry with url found in .mcp.json (set LAUNCHPOD_MCP_NAME=<name> to pick a different one, or migrate to ${CRED_CONFIG_FILENAME} via \`launch-kit init\`)`);
36364
+ const profile = nested.profiles[nested.active];
36365
+ if (!profile) {
36366
+ throw new Error(`${CONFIG_FILENAME} active course "${nested.active}" not found in profiles. ${fix}`);
35355
36367
  }
35356
- const auth = entry.headers?.["Authorization"] ?? "";
35357
- const pat = auth.replace(/^Bearer\s+/i, "").trim();
35358
- if (!pat.startsWith("ls_pat_")) {
35359
- throw new Error(
35360
- `.mcp.json launch-secure Authorization header is not a PAT (ls_pat_...). Migrate to ${CRED_CONFIG_FILENAME} via \`launch-kit init\`, or update the header.`
35361
- );
36368
+ const { pat, orgSlug, projectSlug, serverUrl } = profile;
36369
+ if (!pat || !pat.startsWith("ls_pat_")) {
36370
+ throw new Error(`${CONFIG_FILENAME} "pat" is missing or not a LaunchSecure PAT (ls_pat_...). ${fix}`);
35362
36371
  }
35363
- const orgSlug = entry.headers?.["X-Org-Slug"];
35364
- const projectSlug = entry.headers?.["X-Project-Slug"];
35365
- if (!orgSlug || !projectSlug) {
35366
- throw new Error(
35367
- "Missing X-Org-Slug / X-Project-Slug headers in .mcp.json launch-secure entry."
35368
- );
36372
+ if (!orgSlug || !projectSlug || !serverUrl) {
36373
+ throw new Error(`${CONFIG_FILENAME} is missing required fields (orgSlug, projectSlug, serverUrl). ${fix}`);
35369
36374
  }
35370
- const url = new URL(entry.url);
35371
- return { pat, orgSlug, projectSlug, serverUrl: `${url.protocol}//${url.host}`, source: `.mcp.json[${entryName}]` };
36375
+ return { pat, orgSlug, projectSlug, serverUrl, source: `${CONFIG_FILENAME}[${nested.active}]` };
35372
36376
  }
35373
36377
  if (!__isMcpMode) {
35374
36378
  let gracefulShutdown = function() {