@launchsecure/launch-kit 0.0.17 → 0.0.19

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 (78) hide show
  1. package/dist/chart-client/assets/index--120d9P9.css +1 -0
  2. package/dist/chart-client/assets/index-C8ANseEa.js +441 -0
  3. package/dist/chart-client/index.html +2 -2
  4. package/dist/client/assets/index-Bf8zdL3x.css +32 -0
  5. package/dist/client/assets/index-Ds9UP_cj.js +291 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/council-client/assets/index-CofZh7pS.css +1 -0
  8. package/dist/council-client/assets/index-Dc41S-R2.js +198 -0
  9. package/dist/council-client/index.html +21 -0
  10. package/dist/deck-client/assets/{_baseUniq-BbqvoK-V.js → _baseUniq-DsfOm3t_.js} +1 -1
  11. package/dist/deck-client/assets/{arc-CMtYsIZt.js → arc-NJuvkBv1.js} +1 -1
  12. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-BEN5hESa.js → architectureDiagram-Q4EWVU46-BgrcgZs0.js} +1 -1
  13. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-BV4CZ6k8.js → blockDiagram-DXYQGD6D-C3XoLi15.js} +1 -1
  14. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-fLcBXqdD.js → c4Diagram-AHTNJAMY-FX2PjLfb.js} +1 -1
  15. package/dist/deck-client/assets/channel-ChQjD1T1.js +1 -0
  16. package/dist/deck-client/assets/{chunk-4BX2VUAB-BO_19zwB.js → chunk-4BX2VUAB-D0aqsJV0.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-4TB4RGXK-iYegd5fu.js → chunk-4TB4RGXK-7qRCCAgK.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-55IACEB6-DM3QwYFL.js → chunk-55IACEB6-DfHG-iqb.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-EDXVE4YY-DGznOul1.js → chunk-EDXVE4YY-DrR52j3B.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-FMBD7UC4-DsANJqYW.js → chunk-FMBD7UC4-D5KSGATB.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-OYMX7WX6-6PGH1F7d.js → chunk-OYMX7WX6-M7hsLRNU.js} +1 -1
  22. package/dist/deck-client/assets/{chunk-QZHKN3VN-Dihf0Uq7.js → chunk-QZHKN3VN-1ynAWO2m.js} +1 -1
  23. package/dist/deck-client/assets/{chunk-YZCP3GAM-Cali2wW5.js → chunk-YZCP3GAM-S2-nGw3D.js} +1 -1
  24. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-B_9iqK1S.js +1 -0
  25. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-B_9iqK1S.js +1 -0
  26. package/dist/deck-client/assets/clone-BYt1AMfz.js +1 -0
  27. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-DsRY4vbI.js → cose-bilkent-S5V4N54A-BcMwozS2.js} +1 -1
  28. package/dist/deck-client/assets/cytoscape.esm-BQk4lpUV.js +331 -0
  29. package/dist/deck-client/assets/{dagre-KV5264BT-DJIKE_pI.js → dagre-KV5264BT-DtKMhl_1.js} +1 -1
  30. package/dist/deck-client/assets/{diagram-5BDNPKRD-Ckgli1SP.js → diagram-5BDNPKRD-1plH69us.js} +1 -1
  31. package/dist/deck-client/assets/{diagram-G4DWMVQ6-CozcDzae.js → diagram-G4DWMVQ6-D_o-BHO3.js} +1 -1
  32. package/dist/deck-client/assets/{diagram-MMDJMWI5-xVSwW3f_.js → diagram-MMDJMWI5-ClZ1LIx6.js} +1 -1
  33. package/dist/deck-client/assets/{diagram-TYMM5635-CJeZVY-P.js → diagram-TYMM5635-B8dKHfRh.js} +1 -1
  34. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-j4wjAERH.js → erDiagram-SMLLAGMA-CY2aCH7-.js} +1 -1
  35. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-CVLZ1efi.js → flowDiagram-DWJPFMVM-DZZWHti8.js} +1 -1
  36. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-CcIJ7pkP.js → ganttDiagram-T4ZO3ILL-OwGGa6Lu.js} +1 -1
  37. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-BZRhQX-a.js → gitGraphDiagram-UUTBAWPF-GKyWD4Qt.js} +1 -1
  38. package/dist/deck-client/assets/{graph-D0l25xfo.js → graph-CORzYQdB.js} +1 -1
  39. package/dist/deck-client/assets/index-765AIQ9z.css +1 -0
  40. package/dist/deck-client/assets/{index-BXcoHWVM.js → index-hiIpM7EP.js} +93 -93
  41. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-BLwgxnYT.js → infoDiagram-42DDH7IO-DmgqJCcF.js} +1 -1
  42. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-BfOLoWv3.js → ishikawaDiagram-UXIWVN3A-D-1v7knu.js} +1 -1
  43. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-CPuL6C9h.js → journeyDiagram-VCZTEJTY-CYrGQE7b.js} +1 -1
  44. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-D3uf7_tx.js → kanban-definition-6JOO6SKY-BJFDWiH-.js} +1 -1
  45. package/dist/deck-client/assets/{layout-CzToiXdK.js → layout-BTFFcaxF.js} +1 -1
  46. package/dist/deck-client/assets/{linear-BU36t460.js → linear-DAbl6COS.js} +1 -1
  47. package/dist/deck-client/assets/{min-DX_q-lqP.js → min-oWHBrFBm.js} +1 -1
  48. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-Ccty4O16.js → mindmap-definition-QFDTVHPH-BTCB0VLO.js} +1 -1
  49. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DVjsvH19.js → pieDiagram-DEJITSTG-CUZChWNA.js} +1 -1
  50. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-DtOXFVW9.js → quadrantDiagram-34T5L4WZ-4M1Um_e4.js} +1 -1
  51. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BbO_kKg6.js → requirementDiagram-MS252O5E-DLzQZ0B3.js} +1 -1
  52. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-qbbj-CmC.js → sankeyDiagram-XADWPNL6-DcNgzV3E.js} +1 -1
  53. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-JNKZAgfQ.js → sequenceDiagram-FGHM5R23-CAcI2vC9.js} +1 -1
  54. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-dtFalcNx.js → stateDiagram-FHFEXIEX-CntjTTm5.js} +1 -1
  55. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-YiaphOU_.js +1 -0
  56. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-Dpp5nqSJ.js → timeline-definition-GMOUNBTQ-D8zrit4U.js} +1 -1
  57. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-D8qEutX7.js → vennDiagram-DHZGUBPP-C4SuFPgo.js} +1 -1
  58. package/dist/deck-client/assets/{wardley-RL74JXVD-BwMqiNcL.js → wardley-RL74JXVD-B3F-Olcq.js} +58 -58
  59. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-Bxl9X3CK.js → wardleyDiagram-NUSXRM2D-kj73r6f-.js} +1 -1
  60. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-DYcvxLhi.js → xychartDiagram-5P7HB3ND-CC_d_Ey3.js} +1 -1
  61. package/dist/deck-client/index.html +2 -2
  62. package/dist/server/chart-serve.js +452 -244
  63. package/dist/server/cli.js +558 -750
  64. package/dist/server/council-entry.js +1418 -0
  65. package/dist/server/council-serve.js +1039 -0
  66. package/dist/server/graph-mcp-entry.js +486 -695
  67. package/package.json +10 -9
  68. package/dist/chart-client/assets/index-BUhuLBaw.js +0 -441
  69. package/dist/chart-client/assets/index-CWRZxjqR.css +0 -1
  70. package/dist/client/assets/index-CAAipH3V.js +0 -291
  71. package/dist/client/assets/index-DtbN793z.css +0 -32
  72. package/dist/deck-client/assets/channel-Nf-B3Qor.js +0 -1
  73. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-3i3-miMR.js +0 -1
  74. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-3i3-miMR.js +0 -1
  75. package/dist/deck-client/assets/clone-DXBuQlG8.js +0 -1
  76. package/dist/deck-client/assets/cytoscape.esm-BiciSPf8.js +0 -331
  77. package/dist/deck-client/assets/index-Cdh-f3-c.css +0 -1
  78. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CviYYulW.js +0 -1
@@ -155,36 +155,248 @@ var init_config = __esm({
155
155
  }
156
156
  });
157
157
 
158
+ // src/server/graph/core/walk.ts
159
+ function walk(dir, exts) {
160
+ const results = [];
161
+ if (!(0, import_node_fs3.existsSync)(dir)) return results;
162
+ for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
163
+ const full = (0, import_node_path3.join)(dir, entry.name);
164
+ if (entry.isDirectory()) {
165
+ results.push(...walk(full, exts));
166
+ } else if (exts.includes((0, import_node_path3.extname)(entry.name))) {
167
+ results.push(full);
168
+ }
169
+ }
170
+ return results;
171
+ }
172
+ function walkWithIgnore(dir, exts, opts = {}) {
173
+ const results = [];
174
+ if (!(0, import_node_fs3.existsSync)(dir)) return results;
175
+ const skip = opts.extraIgnore ? /* @__PURE__ */ new Set([...DEFAULT_IGNORE_DIRS, ...opts.extraIgnore]) : DEFAULT_IGNORE_DIRS;
176
+ for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
177
+ if (entry.isDirectory()) {
178
+ if (skip.has(entry.name)) continue;
179
+ results.push(...walkWithIgnore((0, import_node_path3.join)(dir, entry.name), exts, opts));
180
+ } else if (exts.includes((0, import_node_path3.extname)(entry.name))) {
181
+ results.push((0, import_node_path3.join)(dir, entry.name));
182
+ }
183
+ }
184
+ return results;
185
+ }
186
+ var import_node_fs3, import_node_path3, DEFAULT_IGNORE_DIRS;
187
+ var init_walk = __esm({
188
+ "src/server/graph/core/walk.ts"() {
189
+ "use strict";
190
+ import_node_fs3 = require("node:fs");
191
+ import_node_path3 = require("node:path");
192
+ DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
193
+ "node_modules",
194
+ ".git",
195
+ ".next",
196
+ ".launchsecure",
197
+ ".claude",
198
+ "dist",
199
+ "build",
200
+ "out",
201
+ ".turbo",
202
+ ".vercel",
203
+ "coverage"
204
+ ]);
205
+ }
206
+ });
207
+
158
208
  // src/server/graph/core/resolve-paths.ts
159
- function detectDbDir(rootDir, config) {
160
- if (config.paths?.dbDir) return (0, import_node_path3.join)(rootDir, config.paths.dbDir);
161
- const prismaDir = (0, import_node_path3.join)(rootDir, "prisma");
162
- if ((0, import_node_fs3.existsSync)(prismaDir)) return prismaDir;
163
- return null;
209
+ function hasSqlFiles(dir) {
210
+ if (!(0, import_node_fs4.existsSync)(dir)) return false;
211
+ try {
212
+ return (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true }).some(
213
+ (e) => e.isFile() && e.name.endsWith(".sql")
214
+ );
215
+ } catch {
216
+ return false;
217
+ }
164
218
  }
165
- function resolveProjectPaths(rootDir, config) {
166
- const dbDir = detectDbDir(rootDir, config);
167
- if (config.paths?.appDir) {
168
- const appDir = (0, import_node_path3.join)(rootDir, config.paths.appDir);
169
- const srcDir = config.paths.srcDir ? (0, import_node_path3.join)(rootDir, config.paths.srcDir) : (0, import_node_path3.dirname)(appDir);
170
- return { srcDir, appDir, apiDir: (0, import_node_path3.join)(appDir, "api"), dbDir };
219
+ function hasNestedMigrationSql(dir) {
220
+ if (!(0, import_node_fs4.existsSync)(dir)) return false;
221
+ try {
222
+ return (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true }).some(
223
+ (e) => e.isDirectory() && (0, import_node_fs4.existsSync)((0, import_node_path4.join)(dir, e.name, "migration.sql"))
224
+ );
225
+ } catch {
226
+ return false;
227
+ }
228
+ }
229
+ function resolveDbFromDir(dir) {
230
+ if (!(0, import_node_fs4.existsSync)(dir)) return { kind: "none", schemaPath: null, migrationsDir: null };
231
+ const schemaPath = (0, import_node_path4.join)(dir, "schema.prisma");
232
+ if ((0, import_node_fs4.existsSync)(schemaPath)) {
233
+ const migrationsDir2 = (0, import_node_path4.join)(dir, "migrations");
234
+ return {
235
+ kind: "prisma",
236
+ schemaPath,
237
+ migrationsDir: (0, import_node_fs4.existsSync)(migrationsDir2) ? migrationsDir2 : null
238
+ };
171
239
  }
172
- const srcApp = (0, import_node_path3.join)(rootDir, "src", "app");
173
- if ((0, import_node_fs3.existsSync)(srcApp)) {
174
- return { srcDir: (0, import_node_path3.join)(rootDir, "src"), appDir: srcApp, apiDir: (0, import_node_path3.join)(srcApp, "api"), dbDir };
240
+ const migrationsDir = (0, import_node_path4.join)(dir, "migrations");
241
+ if (hasSqlFiles(migrationsDir) || hasNestedMigrationSql(migrationsDir)) {
242
+ return { kind: "sql-migrations", migrationsDir, schemaPath: null };
175
243
  }
176
- const rootApp = (0, import_node_path3.join)(rootDir, "app");
177
- if ((0, import_node_fs3.existsSync)(rootApp)) {
178
- return { srcDir: rootDir, appDir: rootApp, apiDir: (0, import_node_path3.join)(rootApp, "api"), dbDir };
244
+ if (hasSqlFiles(dir) || hasNestedMigrationSql(dir)) {
245
+ return { kind: "sql-migrations", migrationsDir: dir, schemaPath: null };
179
246
  }
247
+ return { kind: "none", schemaPath: null, migrationsDir: null };
248
+ }
249
+ function detectDbConfig(rootDir, config) {
250
+ if (config.paths?.dbDir) {
251
+ return resolveDbFromDir((0, import_node_path4.join)(rootDir, config.paths.dbDir));
252
+ }
253
+ const candidates = ["prisma", "supabase", "drizzle", (0, import_node_path4.join)("db", "migrations"), "migrations"];
254
+ for (const c of candidates) {
255
+ const dir = (0, import_node_path4.join)(rootDir, c);
256
+ const resolved = resolveDbFromDir(dir);
257
+ if (resolved.kind !== "none") return resolved;
258
+ }
259
+ return { kind: "none", schemaPath: null, migrationsDir: null };
260
+ }
261
+ function detectDbDir(rootDir, config, dbConfig) {
262
+ if (config.paths?.dbDir) return (0, import_node_path4.join)(rootDir, config.paths.dbDir);
263
+ if (dbConfig.kind === "prisma") return (0, import_node_path4.dirname)(dbConfig.schemaPath);
264
+ if (dbConfig.kind === "sql-migrations") return dbConfig.migrationsDir;
180
265
  return null;
181
266
  }
182
- var import_node_fs3, import_node_path3;
267
+ function dirHasTSFiles(dir) {
268
+ if (!(0, import_node_fs4.existsSync)(dir)) return false;
269
+ try {
270
+ const stack = [dir];
271
+ while (stack.length > 0) {
272
+ const cur = stack.pop();
273
+ const entries = (0, import_node_fs4.readdirSync)(cur, { withFileTypes: true });
274
+ for (const e of entries) {
275
+ if (e.isFile() && (e.name.endsWith(".ts") || e.name.endsWith(".tsx"))) return true;
276
+ if (e.isDirectory() && !e.name.startsWith(".") && !DEFAULT_IGNORE_DIRS.has(e.name)) {
277
+ stack.push((0, import_node_path4.join)(cur, e.name));
278
+ }
279
+ }
280
+ }
281
+ } catch {
282
+ }
283
+ return false;
284
+ }
285
+ function collectCodeBearingChildren(dir, extraSkip) {
286
+ if (!(0, import_node_fs4.existsSync)(dir)) return [];
287
+ const out = [];
288
+ try {
289
+ for (const entry of (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true })) {
290
+ if (!entry.isDirectory()) continue;
291
+ if (entry.name.startsWith(".")) continue;
292
+ if (NON_SOURCE_DIRS.has(entry.name)) continue;
293
+ if (extraSkip?.has(entry.name)) continue;
294
+ const full = (0, import_node_path4.join)(dir, entry.name);
295
+ if (dirHasTSFiles(full)) out.push(full);
296
+ }
297
+ } catch {
298
+ }
299
+ return out;
300
+ }
301
+ function detectSrcRoots(rootDir, srcDir, appDir, config) {
302
+ if (config.paths?.srcRoots && config.paths.srcRoots.length > 0) {
303
+ const roots2 = /* @__PURE__ */ new Set();
304
+ roots2.add(appDir);
305
+ for (const r of config.paths.srcRoots) {
306
+ const abs = (0, import_node_path4.isAbsolute)(r) ? r : (0, import_node_path4.resolve)(rootDir, r);
307
+ roots2.add(abs);
308
+ }
309
+ return [...roots2];
310
+ }
311
+ const roots = /* @__PURE__ */ new Set();
312
+ roots.add(appDir);
313
+ for (const c of collectCodeBearingChildren(srcDir)) roots.add(c);
314
+ if (srcDir !== rootDir) {
315
+ const skipSrcWrapper = /* @__PURE__ */ new Set([(0, import_node_path4.basename)(srcDir)]);
316
+ for (const c of collectCodeBearingChildren(rootDir, skipSrcWrapper)) roots.add(c);
317
+ }
318
+ return [...roots];
319
+ }
320
+ function detectConventionFiles(rootDir, srcDir) {
321
+ const out = [];
322
+ const seen = /* @__PURE__ */ new Set();
323
+ const dirs = srcDir === rootDir ? [rootDir] : [srcDir, rootDir];
324
+ for (const dir of dirs) {
325
+ for (const name of CONVENTION_NAMES) {
326
+ const full = (0, import_node_path4.join)(dir, name);
327
+ if (!seen.has(full) && (0, import_node_fs4.existsSync)(full)) {
328
+ try {
329
+ if ((0, import_node_fs4.statSync)(full).isFile()) {
330
+ seen.add(full);
331
+ out.push(full);
332
+ }
333
+ } catch {
334
+ }
335
+ }
336
+ }
337
+ }
338
+ return out;
339
+ }
340
+ function resolveProjectPaths(rootDir, config) {
341
+ let srcDir;
342
+ let appDir;
343
+ if (config.paths?.appDir) {
344
+ appDir = (0, import_node_path4.join)(rootDir, config.paths.appDir);
345
+ srcDir = config.paths.srcDir ? (0, import_node_path4.join)(rootDir, config.paths.srcDir) : (0, import_node_path4.dirname)(appDir);
346
+ } else {
347
+ const srcApp = (0, import_node_path4.join)(rootDir, "src", "app");
348
+ const rootApp = (0, import_node_path4.join)(rootDir, "app");
349
+ if ((0, import_node_fs4.existsSync)(srcApp)) {
350
+ srcDir = (0, import_node_path4.join)(rootDir, "src");
351
+ appDir = srcApp;
352
+ } else if ((0, import_node_fs4.existsSync)(rootApp)) {
353
+ srcDir = rootDir;
354
+ appDir = rootApp;
355
+ } else {
356
+ return null;
357
+ }
358
+ }
359
+ const apiDir = (0, import_node_path4.join)(appDir, "api");
360
+ const dbConfig = detectDbConfig(rootDir, config);
361
+ const dbDir = detectDbDir(rootDir, config, dbConfig);
362
+ const srcRoots = detectSrcRoots(rootDir, srcDir, appDir, config);
363
+ const conventionFiles = detectConventionFiles(rootDir, srcDir);
364
+ return { srcDir, appDir, apiDir, dbDir, srcRoots, conventionFiles, dbConfig };
365
+ }
366
+ var import_node_fs4, import_node_path4, NON_SOURCE_DIRS, CONVENTION_NAMES;
183
367
  var init_resolve_paths = __esm({
184
368
  "src/server/graph/core/resolve-paths.ts"() {
185
369
  "use strict";
186
- import_node_fs3 = require("node:fs");
187
- import_node_path3 = require("node:path");
370
+ import_node_fs4 = require("node:fs");
371
+ import_node_path4 = require("node:path");
372
+ init_walk();
373
+ NON_SOURCE_DIRS = /* @__PURE__ */ new Set([
374
+ ...DEFAULT_IGNORE_DIRS,
375
+ // DB conventions (handled by db parsers)
376
+ "prisma",
377
+ "supabase",
378
+ "drizzle",
379
+ "migrations",
380
+ // Web assets
381
+ "public",
382
+ "static",
383
+ "assets",
384
+ // Docs
385
+ "docs",
386
+ "documentation",
387
+ // Test dirs (project tests aren't part of the structural graph)
388
+ "tests",
389
+ "__tests__",
390
+ "e2e",
391
+ "playwright",
392
+ "cypress",
393
+ // Monorepo workspace roots — separate graph projects per .launchchart.json
394
+ "packages",
395
+ "apps",
396
+ "services",
397
+ "libs"
398
+ ]);
399
+ CONVENTION_NAMES = ["middleware.ts", "middleware.tsx", "instrumentation.ts", "instrumentation.tsx"];
188
400
  }
189
401
  });
190
402
 
@@ -224,15 +436,15 @@ function getQuery(name) {
224
436
  ensureInit();
225
437
  const cached = queryCache.get(name);
226
438
  if (cached) return cached;
227
- const scmPath = (0, import_node_path4.join)(queriesDir, `${name}.scm`);
228
- const scm = (0, import_node_fs4.readFileSync)(scmPath, "utf-8");
439
+ const scmPath = (0, import_node_path5.join)(queriesDir, `${name}.scm`);
440
+ const scm = (0, import_node_fs5.readFileSync)(scmPath, "utf-8");
229
441
  const query = tsxLanguage.query(scm);
230
442
  queryCache.set(name, query);
231
443
  return query;
232
444
  }
233
445
  function parseSource(absPath) {
234
446
  ensureInit();
235
- const content = (0, import_node_fs4.readFileSync)(absPath, "utf-8");
447
+ const content = (0, import_node_fs5.readFileSync)(absPath, "utf-8");
236
448
  return parserInstance.parse(content);
237
449
  }
238
450
  function parseCodeTS(code) {
@@ -697,17 +909,17 @@ function extractDeep(absPath) {
697
909
  }
698
910
  return { elements, stateVars, conditions, variables, responses, params };
699
911
  }
700
- var import_node_fs4, import_node_path4, tsxLanguage, parserInstance, initPromise, initialized, queriesDir, queryCache, PRISMA_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods;
912
+ var import_node_fs5, import_node_path5, tsxLanguage, parserInstance, initPromise, initialized, queriesDir, queryCache, PRISMA_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods;
701
913
  var init_ts_extractor = __esm({
702
914
  "src/server/graph/core/ts-extractor.ts"() {
703
915
  "use strict";
704
- import_node_fs4 = require("node:fs");
705
- import_node_path4 = require("node:path");
916
+ import_node_fs5 = require("node:fs");
917
+ import_node_path5 = require("node:path");
706
918
  initialized = false;
707
919
  queriesDir = (() => {
708
- const srcPath = (0, import_node_path4.join)((0, import_node_path4.dirname)(__filename), "..", "queries");
920
+ const srcPath = (0, import_node_path5.join)((0, import_node_path5.dirname)(__filename), "..", "queries");
709
921
  if (require("fs").existsSync(srcPath)) return srcPath;
710
- return (0, import_node_path4.join)((0, import_node_path4.dirname)(__filename), "graph", "queries");
922
+ return (0, import_node_path5.join)((0, import_node_path5.dirname)(__filename), "graph", "queries");
711
923
  })();
712
924
  queryCache = /* @__PURE__ */ new Map();
713
925
  PRISMA_MUTATION_METHODS_BUILTIN = [
@@ -728,48 +940,26 @@ var init_ts_extractor = __esm({
728
940
  });
729
941
 
730
942
  // src/server/graph/parsers/ts/typescript-project.ts
731
- function walk(dir, exts) {
732
- const results = [];
733
- if (!(0, import_node_fs5.existsSync)(dir)) return results;
734
- for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
735
- const full = (0, import_node_path5.join)(dir, entry.name);
736
- if (entry.isDirectory()) {
737
- results.push(...walk(full, exts));
738
- } else if (exts.includes((0, import_node_path5.extname)(entry.name))) {
739
- results.push(full);
740
- }
943
+ function toNodeId(srcDir, rootDir, absPath) {
944
+ const relFromSrc = (0, import_node_path6.relative)(srcDir, absPath).replace(/\\/g, "/");
945
+ if (relFromSrc.startsWith("..")) {
946
+ return (0, import_node_path6.relative)(rootDir, absPath).replace(/\\/g, "/");
741
947
  }
742
- return results;
743
- }
744
- function walkWithIgnore(dir, exts, ignoreDirs) {
745
- const results = [];
746
- if (!(0, import_node_fs5.existsSync)(dir)) return results;
747
- for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
748
- if (entry.isDirectory()) {
749
- if (ignoreDirs.has(entry.name)) continue;
750
- results.push(...walkWithIgnore((0, import_node_path5.join)(dir, entry.name), exts, ignoreDirs));
751
- } else if (exts.includes((0, import_node_path5.extname)(entry.name))) {
752
- results.push((0, import_node_path5.join)(dir, entry.name));
753
- }
754
- }
755
- return results;
756
- }
757
- function toNodeId(srcDir, absPath) {
758
- return (0, import_node_path5.relative)(srcDir, absPath).replace(/\\/g, "/");
948
+ return relFromSrc;
759
949
  }
760
950
  function resolveImport(srcDir, specifier) {
761
951
  if (!specifier.startsWith("@/")) return null;
762
952
  const rel = specifier.slice(2);
763
- const base = (0, import_node_path5.join)(srcDir, rel);
764
- for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path5.join)(base, "index.ts"), (0, import_node_path5.join)(base, "index.tsx")]) {
765
- if ((0, import_node_fs5.existsSync)(c) && (0, import_node_fs5.statSync)(c).isFile()) return c;
953
+ const base = (0, import_node_path6.join)(srcDir, rel);
954
+ for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path6.join)(base, "index.ts"), (0, import_node_path6.join)(base, "index.tsx")]) {
955
+ if ((0, import_node_fs6.existsSync)(c) && (0, import_node_fs6.statSync)(c).isFile()) return c;
766
956
  }
767
957
  return null;
768
958
  }
769
959
  function resolveRelativeImport(fromFile, specifier) {
770
- const base = (0, import_node_path5.join)((0, import_node_path5.dirname)(fromFile), specifier);
771
- for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path5.join)(base, "index.ts"), (0, import_node_path5.join)(base, "index.tsx")]) {
772
- if ((0, import_node_fs5.existsSync)(c) && (0, import_node_fs5.statSync)(c).isFile()) return c;
960
+ const base = (0, import_node_path6.join)((0, import_node_path6.dirname)(fromFile), specifier);
961
+ for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path6.join)(base, "index.ts"), (0, import_node_path6.join)(base, "index.tsx")]) {
962
+ if ((0, import_node_fs6.existsSync)(c) && (0, import_node_fs6.statSync)(c).isFile()) return c;
773
963
  }
774
964
  return null;
775
965
  }
@@ -790,7 +980,7 @@ function resolveBarrelMap(barrelAbsPath, parsedByPath, memo, visiting) {
790
980
  const resolved = resolveRelativeImport(barrelAbsPath, re.from);
791
981
  if (!resolved) continue;
792
982
  if (re.isWildcard) {
793
- const targetBn = (0, import_node_path5.basename)(resolved);
983
+ const targetBn = (0, import_node_path6.basename)(resolved);
794
984
  const targetIsBarrel = targetBn === "index.ts" || targetBn === "index.tsx";
795
985
  if (targetIsBarrel) {
796
986
  const nested = resolveBarrelMap(resolved, parsedByPath, memo, visiting);
@@ -817,12 +1007,12 @@ function buildAllBarrelMaps(srcDir, parsedByPath) {
817
1007
  const barrels = /* @__PURE__ */ new Map();
818
1008
  const memo = /* @__PURE__ */ new Map();
819
1009
  for (const [absPath, parsed] of parsedByPath) {
820
- const bn = (0, import_node_path5.basename)(absPath);
1010
+ const bn = (0, import_node_path6.basename)(absPath);
821
1011
  if (bn !== "index.ts" && bn !== "index.tsx") continue;
822
1012
  if (parsed.reExports.length === 0) continue;
823
1013
  const map = resolveBarrelMap(absPath, parsedByPath, memo, /* @__PURE__ */ new Set());
824
1014
  if (map.size > 0) {
825
- const barrelId = (0, import_node_path5.relative)(srcDir, (0, import_node_path5.dirname)(absPath)).replace(/\\/g, "/");
1015
+ const barrelId = (0, import_node_path6.relative)(srcDir, (0, import_node_path6.dirname)(absPath)).replace(/\\/g, "/");
826
1016
  barrels.set(barrelId, map);
827
1017
  }
828
1018
  }
@@ -843,14 +1033,15 @@ function extractRoute(id) {
843
1033
  return route || "/";
844
1034
  }
845
1035
  function nameFromFilename(absPath) {
846
- return (0, import_node_path5.basename)(absPath, (0, import_node_path5.extname)(absPath)).replace(/[-_](\w)/g, (_, c) => c.toUpperCase()).replace(/^(\w)/, (_, c) => c.toUpperCase());
1036
+ return (0, import_node_path6.basename)(absPath, (0, import_node_path6.extname)(absPath)).replace(/[-_](\w)/g, (_, c) => c.toUpperCase()).replace(/^(\w)/, (_, c) => c.toUpperCase());
847
1037
  }
848
- function filePathToApiRoute(apiDir, absPath) {
849
- let route = "/" + (0, import_node_path5.relative)(apiDir, absPath).replace(/\\/g, "/").replace(/\/route\.tsx?$/, "");
1038
+ function filePathToAppRoute(appDir, absPath) {
1039
+ let route = ("/" + (0, import_node_path6.relative)(appDir, absPath).replace(/\\/g, "/")).replace(/\/route\.tsx?$/, "");
1040
+ route = route.replace(/\/\([^)]+\)/g, "");
1041
+ route = route.replace(/\[\.\.\.([^\]]+)\]/g, "*$1");
850
1042
  route = route.replace(/\[([^\]]+)\]/g, ":$1");
851
1043
  route = route.replace(/\/+/g, "/");
852
- if (route === "/") return "/api";
853
- return "/api" + route;
1044
+ return route === "" ? "/" : route;
854
1045
  }
855
1046
  function camelToPascal(s) {
856
1047
  if (!s) return s;
@@ -935,7 +1126,7 @@ function matchRouteToPage(route, routeToNodeId) {
935
1126
  if (routeToNodeId.has(normalized)) return routeToNodeId.get(normalized);
936
1127
  return null;
937
1128
  }
938
- function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
1129
+ function extractEdges(srcDir, rootDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
939
1130
  const edges = [];
940
1131
  const flagged = [];
941
1132
  const seen = /* @__PURE__ */ new Set();
@@ -963,7 +1154,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
963
1154
  for (const name of names) {
964
1155
  const targetAbs = barrelMap.get(name);
965
1156
  if (targetAbs) {
966
- const targetId = toNodeId(srcDir, targetAbs);
1157
+ const targetId = toNodeId(srcDir, rootDir, targetAbs);
967
1158
  if (nodeIdSet.has(targetId)) {
968
1159
  if (!byTarget.has(targetId)) byTarget.set(targetId, []);
969
1160
  byTarget.get(targetId).push(name);
@@ -977,7 +1168,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
977
1168
  } else {
978
1169
  const resolved = resolveImport(srcDir, specifier);
979
1170
  if (resolved) {
980
- const targetId = toNodeId(srcDir, resolved);
1171
+ const targetId = toNodeId(srcDir, rootDir, resolved);
981
1172
  if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
982
1173
  addEdge(targetId, edgeTypeFor(isTypeOnly, names));
983
1174
  }
@@ -986,7 +1177,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
986
1177
  } else if (specifier.startsWith(".")) {
987
1178
  const resolved = resolveRelativeImport(absPath, specifier);
988
1179
  if (resolved) {
989
- const targetId = toNodeId(srcDir, resolved);
1180
+ const targetId = toNodeId(srcDir, rootDir, resolved);
990
1181
  if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
991
1182
  addEdge(targetId, edgeTypeFor(isTypeOnly, names));
992
1183
  }
@@ -1030,24 +1221,30 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
1030
1221
  }
1031
1222
  return { edges, flagged };
1032
1223
  }
1033
- function hasNextConfig(rootDir) {
1034
- return (0, import_node_fs5.existsSync)((0, import_node_path5.join)(rootDir, "next.config.ts")) || (0, import_node_fs5.existsSync)((0, import_node_path5.join)(rootDir, "next.config.js")) || (0, import_node_fs5.existsSync)((0, import_node_path5.join)(rootDir, "next.config.mjs"));
1035
- }
1036
1224
  function detect(rootDir) {
1037
1225
  const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
1038
- return paths !== null && hasNextConfig(rootDir);
1226
+ return paths !== null;
1039
1227
  }
1040
1228
  function generate(rootDir) {
1041
1229
  const config = loadConfig(rootDir);
1042
1230
  const paths = resolveProjectPaths(rootDir, config);
1043
1231
  const srcDir = paths.srcDir;
1044
- const apiDir = paths.apiDir;
1045
- const appFiles = walk(paths.appDir, [".tsx", ".ts"]);
1046
- const clientFiles = walk((0, import_node_path5.join)(srcDir, "client"), [".tsx", ".ts"]);
1047
- const serverFiles = walk((0, import_node_path5.join)(srcDir, "server"), [".ts", ".tsx"]);
1048
- const libFiles = walk((0, import_node_path5.join)(srcDir, "lib"), [".ts", ".tsx"]);
1049
- const configFiles = walk((0, import_node_path5.join)(srcDir, "config"), [".ts", ".tsx"]);
1050
- const allDiscovered = [...appFiles, ...clientFiles, ...serverFiles, ...libFiles, ...configFiles];
1232
+ const allDiscovered = [];
1233
+ const discoveredSet = /* @__PURE__ */ new Set();
1234
+ for (const root of paths.srcRoots) {
1235
+ for (const f of walk(root, [".tsx", ".ts"])) {
1236
+ if (!discoveredSet.has(f)) {
1237
+ discoveredSet.add(f);
1238
+ allDiscovered.push(f);
1239
+ }
1240
+ }
1241
+ }
1242
+ for (const conv of paths.conventionFiles) {
1243
+ if (!discoveredSet.has(conv)) {
1244
+ discoveredSet.add(conv);
1245
+ allDiscovered.push(conv);
1246
+ }
1247
+ }
1051
1248
  const parsedByPath = /* @__PURE__ */ new Map();
1052
1249
  for (const absPath of allDiscovered) {
1053
1250
  parsedByPath.set(absPath, parseFileTS(absPath));
@@ -1057,9 +1254,9 @@ function generate(rootDir) {
1057
1254
  const apiNodes = [];
1058
1255
  const nodeIdSet = /* @__PURE__ */ new Set();
1059
1256
  const routeToNodeId = /* @__PURE__ */ new Map();
1060
- const fileSet = allDiscovered.filter((f) => !(0, import_node_path5.basename)(f).startsWith("index."));
1257
+ const fileSet = allDiscovered.filter((f) => !(0, import_node_path6.basename)(f).startsWith("index."));
1061
1258
  for (const absPath of fileSet) {
1062
- const id = toNodeId(srcDir, absPath);
1259
+ const id = toNodeId(srcDir, rootDir, absPath);
1063
1260
  const type = classifyType(absPath, id);
1064
1261
  if (type === "test" || type === "story") continue;
1065
1262
  const parsed = parsedByPath.get(absPath);
@@ -1074,7 +1271,7 @@ function generate(rootDir) {
1074
1271
  const dbCalls = extractDbCallsTS(absPath);
1075
1272
  const authWrappers = extractAuthWrappersTS(absPath);
1076
1273
  const deep = extractDeep(absPath);
1077
- const routePath = (0, import_node_fs5.existsSync)(apiDir) ? filePathToApiRoute(apiDir, absPath) : `/api/${id.replace(/\/route\.tsx?$/, "")}`;
1274
+ const routePath = filePathToAppRoute(paths.appDir, absPath);
1078
1275
  const mutations = dbCalls.filter((c) => c.isMutation);
1079
1276
  const mutates = mutations.length > 0;
1080
1277
  const authStrategy = [...authWrappers];
@@ -1118,11 +1315,12 @@ function generate(rootDir) {
1118
1315
  const uiEdges = [];
1119
1316
  const uiFlagged = [];
1120
1317
  for (const absPath of fileSet) {
1121
- const id = toNodeId(srcDir, absPath);
1318
+ const id = toNodeId(srcDir, rootDir, absPath);
1122
1319
  if (!nodeIdSet.has(id)) continue;
1123
1320
  const parsed = parsedByPath.get(absPath);
1124
1321
  const { edges, flagged } = extractEdges(
1125
1322
  srcDir,
1323
+ rootDir,
1126
1324
  absPath,
1127
1325
  id,
1128
1326
  parsed,
@@ -1135,7 +1333,7 @@ function generate(rootDir) {
1135
1333
  }
1136
1334
  const fetchCallEntries = [];
1137
1335
  for (const absPath of fileSet) {
1138
- const sourceId = toNodeId(srcDir, absPath);
1336
+ const sourceId = toNodeId(srcDir, rootDir, absPath);
1139
1337
  if (!nodeIdSet.has(sourceId)) continue;
1140
1338
  const parsed = parsedByPath.get(absPath);
1141
1339
  if (parsed.fetchCalls.length === 0) continue;
@@ -1151,20 +1349,7 @@ function generate(rootDir) {
1151
1349
  });
1152
1350
  }
1153
1351
  const externalScanned = new Set(allDiscovered.map((f) => f.replace(/\\/g, "/")));
1154
- const IGNORE_DIRS2 = /* @__PURE__ */ new Set([
1155
- "node_modules",
1156
- ".next",
1157
- "dist",
1158
- ".launchsecure",
1159
- ".git",
1160
- "src",
1161
- "coverage",
1162
- ".turbo",
1163
- "build",
1164
- "out",
1165
- ".vercel"
1166
- ]);
1167
- const externalCandidates = walkWithIgnore(rootDir, [".ts", ".tsx"], IGNORE_DIRS2);
1352
+ const externalCandidates = walkWithIgnore(rootDir, [".ts", ".tsx"], { extraIgnore: /* @__PURE__ */ new Set(["src"]) });
1168
1353
  for (const absPath of externalCandidates) {
1169
1354
  const normalized = absPath.replace(/\\/g, "/");
1170
1355
  if (externalScanned.has(normalized)) continue;
@@ -1174,7 +1359,7 @@ function generate(rootDir) {
1174
1359
  } catch {
1175
1360
  continue;
1176
1361
  }
1177
- const externalId = (0, import_node_path5.relative)(rootDir, absPath).replace(/\\/g, "/");
1362
+ const externalId = (0, import_node_path6.relative)(rootDir, absPath).replace(/\\/g, "/");
1178
1363
  const edgesFromThis = [];
1179
1364
  const seen = /* @__PURE__ */ new Set();
1180
1365
  for (const imp of parsed.imports) {
@@ -1187,7 +1372,7 @@ function generate(rootDir) {
1187
1372
  for (const name of names) {
1188
1373
  const targetAbs = barrelMap.get(name);
1189
1374
  if (!targetAbs) continue;
1190
- const targetId2 = toNodeId(srcDir, targetAbs);
1375
+ const targetId2 = toNodeId(srcDir, rootDir, targetAbs);
1191
1376
  if (!nodeIdSet.has(targetId2)) continue;
1192
1377
  const key2 = `${externalId}\u2192${targetId2}`;
1193
1378
  if (seen.has(key2)) continue;
@@ -1201,7 +1386,7 @@ function generate(rootDir) {
1201
1386
  resolved = resolveRelativeImport(absPath, specifier);
1202
1387
  }
1203
1388
  if (!resolved) continue;
1204
- const targetId = toNodeId(srcDir, resolved);
1389
+ const targetId = toNodeId(srcDir, rootDir, resolved);
1205
1390
  if (!nodeIdSet.has(targetId)) continue;
1206
1391
  if (targetId.endsWith("/index.ts") || targetId.endsWith("/index.tsx")) continue;
1207
1392
  const key = `${externalId}\u2192${targetId}`;
@@ -1370,14 +1555,15 @@ function generate(rootDir) {
1370
1555
  }
1371
1556
  return result;
1372
1557
  }
1373
- var import_node_fs5, import_node_path5, HTTP_METHODS, CLASSIFICATION_TO_LAYER, typescriptProjectParser;
1558
+ var import_node_fs6, import_node_path6, HTTP_METHODS, CLASSIFICATION_TO_LAYER, typescriptProjectParser;
1374
1559
  var init_typescript_project = __esm({
1375
1560
  "src/server/graph/parsers/ts/typescript-project.ts"() {
1376
1561
  "use strict";
1377
- import_node_fs5 = require("node:fs");
1378
- import_node_path5 = require("node:path");
1562
+ import_node_fs6 = require("node:fs");
1563
+ import_node_path6 = require("node:path");
1379
1564
  init_config();
1380
1565
  init_resolve_paths();
1566
+ init_walk();
1381
1567
  init_ts_extractor();
1382
1568
  HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]);
1383
1569
  CLASSIFICATION_TO_LAYER = {
@@ -1493,11 +1679,25 @@ function parseEnums(content) {
1493
1679
  return nodes;
1494
1680
  }
1495
1681
  function detect2(rootDir) {
1496
- return (0, import_node_fs6.existsSync)((0, import_node_path6.join)(rootDir, "prisma", "schema.prisma"));
1682
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
1683
+ return paths?.dbConfig.kind === "prisma" && (0, import_node_fs7.existsSync)(paths.dbConfig.schemaPath);
1497
1684
  }
1498
1685
  function generate2(rootDir) {
1499
- const schemaPath = (0, import_node_path6.join)(rootDir, "prisma", "schema.prisma");
1500
- const content = (0, import_node_fs6.readFileSync)(schemaPath, "utf-8");
1686
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
1687
+ if (paths.dbConfig.kind !== "prisma") {
1688
+ return {
1689
+ metadata: { generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), layer: "db", source: "none" },
1690
+ nodes: [],
1691
+ edges: [],
1692
+ cross_refs: [],
1693
+ contradictions: [],
1694
+ warnings: [],
1695
+ flagged_edges: [],
1696
+ patterns: { total_tables: 0, total_enums: 0, total_relations: 0 }
1697
+ };
1698
+ }
1699
+ const schemaPath = paths.dbConfig.schemaPath;
1700
+ const content = (0, import_node_fs7.readFileSync)(schemaPath, "utf-8");
1501
1701
  const { nodes: modelNodes, relations } = parseModels(content);
1502
1702
  const enumNodes = parseEnums(content);
1503
1703
  const allNodes = [...modelNodes, ...enumNodes];
@@ -1517,7 +1717,7 @@ function generate2(rootDir) {
1517
1717
  metadata: {
1518
1718
  generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
1519
1719
  scope: "prisma-schema",
1520
- source: "prisma/schema.prisma",
1720
+ source: schemaPath,
1521
1721
  provider: "postgresql",
1522
1722
  layer: "db",
1523
1723
  total_models: modelNodes.length,
@@ -1546,12 +1746,13 @@ function generate2(rootDir) {
1546
1746
  }
1547
1747
  };
1548
1748
  }
1549
- var import_node_fs6, import_node_path6, prismaSchemaParser;
1749
+ var import_node_fs7, prismaSchemaParser;
1550
1750
  var init_prisma_schema = __esm({
1551
1751
  "src/server/graph/parsers/db/prisma-schema.ts"() {
1552
1752
  "use strict";
1553
- import_node_fs6 = require("node:fs");
1554
- import_node_path6 = require("node:path");
1753
+ import_node_fs7 = require("node:fs");
1754
+ init_config();
1755
+ init_resolve_paths();
1555
1756
  prismaSchemaParser = {
1556
1757
  id: "prisma-schema",
1557
1758
  layer: "db",
@@ -1684,20 +1885,30 @@ function parseUniqueIndex(sql, state) {
1684
1885
  state.uniqueIndexes.get(m[1]).add(m[2]);
1685
1886
  }
1686
1887
  }
1687
- function parseMigrations(rootDir) {
1688
- const migrationsDir = (0, import_node_path7.join)(rootDir, "prisma", "migrations");
1888
+ function discoverMigrationFiles(migrationsDir) {
1889
+ if (!(0, import_node_fs8.existsSync)(migrationsDir)) return [];
1890
+ const out = [];
1891
+ const entries = (0, import_node_fs8.readdirSync)(migrationsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1892
+ for (const entry of entries) {
1893
+ if (entry.isDirectory()) {
1894
+ const sqlPath = (0, import_node_path7.join)(migrationsDir, entry.name, "migration.sql");
1895
+ if ((0, import_node_fs8.existsSync)(sqlPath)) out.push(sqlPath);
1896
+ } else if (entry.isFile() && entry.name.endsWith(".sql")) {
1897
+ out.push((0, import_node_path7.join)(migrationsDir, entry.name));
1898
+ }
1899
+ }
1900
+ return out;
1901
+ }
1902
+ function parseMigrations(migrationsDir) {
1689
1903
  const state = {
1690
1904
  tables: /* @__PURE__ */ new Map(),
1691
1905
  enums: /* @__PURE__ */ new Map(),
1692
1906
  fks: [],
1693
1907
  uniqueIndexes: /* @__PURE__ */ new Map()
1694
1908
  };
1695
- if (!(0, import_node_fs7.existsSync)(migrationsDir)) return state;
1696
- const dirs = (0, import_node_fs7.readdirSync)(migrationsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
1697
- for (const dir of dirs) {
1698
- const sqlPath = (0, import_node_path7.join)(migrationsDir, dir, "migration.sql");
1699
- if (!(0, import_node_fs7.existsSync)(sqlPath)) continue;
1700
- const sql = (0, import_node_fs7.readFileSync)(sqlPath, "utf-8");
1909
+ if (!migrationsDir) return state;
1910
+ for (const sqlPath of discoverMigrationFiles(migrationsDir)) {
1911
+ const sql = (0, import_node_fs8.readFileSync)(sqlPath, "utf-8");
1701
1912
  parseCreateEnum(sql, state);
1702
1913
  parseCreateTable(sql, state);
1703
1914
  parseAlterTable(sql, state);
@@ -1707,10 +1918,9 @@ function parseMigrations(rootDir) {
1707
1918
  }
1708
1919
  return state;
1709
1920
  }
1710
- function loadPrismaState(rootDir) {
1711
- const schemaPath = (0, import_node_path7.join)(rootDir, "prisma", "schema.prisma");
1712
- if (!(0, import_node_fs7.existsSync)(schemaPath)) return null;
1713
- const content = (0, import_node_fs7.readFileSync)(schemaPath, "utf-8");
1921
+ function loadPrismaState(schemaPath) {
1922
+ if (!schemaPath || !(0, import_node_fs8.existsSync)(schemaPath)) return null;
1923
+ const content = (0, import_node_fs8.readFileSync)(schemaPath, "utf-8");
1714
1924
  const tables = /* @__PURE__ */ new Map();
1715
1925
  const enums = /* @__PURE__ */ new Map();
1716
1926
  const relations = [];
@@ -1874,14 +2084,28 @@ function verify(sqlState, prisma) {
1874
2084
  }
1875
2085
  return { contradictions, flaggedEdges };
1876
2086
  }
2087
+ function migrationsDirFor(rootDir) {
2088
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
2089
+ if (!paths) return null;
2090
+ if (paths.dbConfig.kind === "prisma" || paths.dbConfig.kind === "sql-migrations") {
2091
+ return paths.dbConfig.migrationsDir;
2092
+ }
2093
+ return null;
2094
+ }
2095
+ function schemaPathFor(rootDir) {
2096
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
2097
+ if (!paths) return null;
2098
+ return paths.dbConfig.kind === "prisma" ? paths.dbConfig.schemaPath : null;
2099
+ }
1877
2100
  function detect3(rootDir) {
1878
- const migrationsDir = (0, import_node_path7.join)(rootDir, "prisma", "migrations");
1879
- if (!(0, import_node_fs7.existsSync)(migrationsDir)) return false;
1880
- return (0, import_node_fs7.readdirSync)(migrationsDir, { withFileTypes: true }).some((d) => d.isDirectory() && (0, import_node_fs7.existsSync)((0, import_node_path7.join)(migrationsDir, d.name, "migration.sql")));
2101
+ const dir = migrationsDirFor(rootDir);
2102
+ if (!dir) return false;
2103
+ return discoverMigrationFiles(dir).length > 0;
1881
2104
  }
1882
2105
  function generate3(rootDir) {
1883
- const sqlState = parseMigrations(rootDir);
1884
- const prisma = loadPrismaState(rootDir);
2106
+ const migrationsDir = migrationsDirFor(rootDir);
2107
+ const sqlState = parseMigrations(migrationsDir);
2108
+ const prisma = loadPrismaState(schemaPathFor(rootDir));
1885
2109
  const prismaTableIds = prisma ? new Set(prisma.tables.keys()) : /* @__PURE__ */ new Set();
1886
2110
  const prismaEnumIds = prisma ? new Set(prisma.enums.keys()) : /* @__PURE__ */ new Set();
1887
2111
  const nodes = [];
@@ -1927,7 +2151,7 @@ function generate3(rootDir) {
1927
2151
  metadata: {
1928
2152
  generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
1929
2153
  scope: "sql-migrations",
1930
- source: "prisma/migrations/",
2154
+ source: migrationsDir ?? "none",
1931
2155
  layer: "db",
1932
2156
  sql_tables: sqlState.tables.size,
1933
2157
  sql_enums: sqlState.enums.size,
@@ -1944,12 +2168,14 @@ function generate3(rootDir) {
1944
2168
  flagged_edges: flaggedEdges
1945
2169
  };
1946
2170
  }
1947
- var import_node_fs7, import_node_path7, PG_TO_PRISMA, sqlMigrationsParser;
2171
+ var import_node_fs8, import_node_path7, PG_TO_PRISMA, sqlMigrationsParser;
1948
2172
  var init_sql_migrations = __esm({
1949
2173
  "src/server/graph/parsers/db/sql-migrations.ts"() {
1950
2174
  "use strict";
1951
- import_node_fs7 = require("node:fs");
2175
+ import_node_fs8 = require("node:fs");
1952
2176
  import_node_path7 = require("node:path");
2177
+ init_config();
2178
+ init_resolve_paths();
1953
2179
  PG_TO_PRISMA = {
1954
2180
  "TEXT": "String",
1955
2181
  "VARCHAR": "String",
@@ -2212,9 +2438,9 @@ var init_fetch_resolver = __esm({
2212
2438
 
2213
2439
  // src/server/graph/parsers/crosslayer/api-annotations.ts
2214
2440
  function walk2(dir, exts) {
2215
- if (!(0, import_node_fs8.existsSync)(dir)) return [];
2441
+ if (!(0, import_node_fs9.existsSync)(dir)) return [];
2216
2442
  const results = [];
2217
- for (const entry of (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true })) {
2443
+ for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
2218
2444
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2219
2445
  const full = (0, import_node_path8.join)(dir, entry.name);
2220
2446
  if (entry.isDirectory()) {
@@ -2228,11 +2454,11 @@ function walk2(dir, exts) {
2228
2454
  function toNodeId2(srcDir, absPath) {
2229
2455
  return (0, import_node_path8.relative)(srcDir, absPath).replace(/\\/g, "/");
2230
2456
  }
2231
- var import_node_fs8, import_node_path8, API_ANNOTATION_RE, apiAnnotationsParser;
2457
+ var import_node_fs9, import_node_path8, API_ANNOTATION_RE, apiAnnotationsParser;
2232
2458
  var init_api_annotations = __esm({
2233
2459
  "src/server/graph/parsers/crosslayer/api-annotations.ts"() {
2234
2460
  "use strict";
2235
- import_node_fs8 = require("node:fs");
2461
+ import_node_fs9 = require("node:fs");
2236
2462
  import_node_path8 = require("node:path");
2237
2463
  init_api_route_matching();
2238
2464
  API_ANNOTATION_RE = /@api\s+(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+(\/\S+)/g;
@@ -2241,7 +2467,7 @@ var init_api_annotations = __esm({
2241
2467
  layer: "crosslayer",
2242
2468
  concern: "api-binding",
2243
2469
  detect(rootDir) {
2244
- return (0, import_node_fs8.existsSync)((0, import_node_path8.join)(rootDir, "src"));
2470
+ return (0, import_node_fs9.existsSync)((0, import_node_path8.join)(rootDir, "src"));
2245
2471
  },
2246
2472
  generate(rootDir, layerOutputs) {
2247
2473
  const apiOutput = layerOutputs.get("api");
@@ -2258,7 +2484,7 @@ var init_api_annotations = __esm({
2258
2484
  const flaggedEdges = [];
2259
2485
  const seen = /* @__PURE__ */ new Set();
2260
2486
  for (const absPath of files) {
2261
- const content = (0, import_node_fs8.readFileSync)(absPath, "utf-8");
2487
+ const content = (0, import_node_fs9.readFileSync)(absPath, "utf-8");
2262
2488
  const sourceId = toNodeId2(srcDir, absPath);
2263
2489
  if (!uiNodeIds.has(sourceId)) continue;
2264
2490
  let match;
@@ -2305,9 +2531,9 @@ var init_api_annotations = __esm({
2305
2531
 
2306
2532
  // src/server/graph/parsers/crosslayer/url-literal-scanner.ts
2307
2533
  function walk3(dir, exts) {
2308
- if (!(0, import_node_fs9.existsSync)(dir)) return [];
2534
+ if (!(0, import_node_fs10.existsSync)(dir)) return [];
2309
2535
  const results = [];
2310
- for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
2536
+ for (const entry of (0, import_node_fs10.readdirSync)(dir, { withFileTypes: true })) {
2311
2537
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2312
2538
  const full = (0, import_node_path9.join)(dir, entry.name);
2313
2539
  if (entry.isDirectory()) {
@@ -2321,11 +2547,11 @@ function walk3(dir, exts) {
2321
2547
  function toNodeId3(srcDir, absPath) {
2322
2548
  return (0, import_node_path9.relative)(srcDir, absPath).replace(/\\/g, "/");
2323
2549
  }
2324
- var import_node_fs9, import_node_path9, URL_LITERAL_RE, urlLiteralScannerParser;
2550
+ var import_node_fs10, import_node_path9, URL_LITERAL_RE, urlLiteralScannerParser;
2325
2551
  var init_url_literal_scanner = __esm({
2326
2552
  "src/server/graph/parsers/crosslayer/url-literal-scanner.ts"() {
2327
2553
  "use strict";
2328
- import_node_fs9 = require("node:fs");
2554
+ import_node_fs10 = require("node:fs");
2329
2555
  import_node_path9 = require("node:path");
2330
2556
  init_api_route_matching();
2331
2557
  init_config();
@@ -2360,7 +2586,7 @@ var init_url_literal_scanner = __esm({
2360
2586
  for (const absPath of files) {
2361
2587
  const sourceId = toNodeId3(srcDir, absPath);
2362
2588
  if (!uiNodeIds.has(sourceId)) continue;
2363
- const content = (0, import_node_fs9.readFileSync)(absPath, "utf-8");
2589
+ const content = (0, import_node_fs10.readFileSync)(absPath, "utf-8");
2364
2590
  let match;
2365
2591
  URL_LITERAL_RE.lastIndex = 0;
2366
2592
  while ((match = URL_LITERAL_RE.exec(content)) !== null) {
@@ -2427,14 +2653,14 @@ function extractEnumValues(rootDir) {
2427
2653
  ];
2428
2654
  let content = "";
2429
2655
  for (const p of schemaPaths) {
2430
- if ((0, import_node_fs10.existsSync)(p)) {
2656
+ if ((0, import_node_fs11.existsSync)(p)) {
2431
2657
  try {
2432
- const stat = (0, import_node_fs10.statSync)(p);
2658
+ const stat = (0, import_node_fs11.statSync)(p);
2433
2659
  if (stat.isFile()) {
2434
- content = (0, import_node_fs10.readFileSync)(p, "utf-8");
2660
+ content = (0, import_node_fs11.readFileSync)(p, "utf-8");
2435
2661
  } else if (stat.isDirectory()) {
2436
- const files = (0, import_node_fs10.readdirSync)(p).filter((f) => f.endsWith(".prisma"));
2437
- content = files.map((f) => (0, import_node_fs10.readFileSync)((0, import_node_path10.join)(p, f), "utf-8")).join("\n");
2662
+ const files = (0, import_node_fs11.readdirSync)(p).filter((f) => f.endsWith(".prisma"));
2663
+ content = files.map((f) => (0, import_node_fs11.readFileSync)((0, import_node_path10.join)(p, f), "utf-8")).join("\n");
2438
2664
  }
2439
2665
  } catch {
2440
2666
  continue;
@@ -2508,7 +2734,7 @@ function extractStringArrayFromNode(node) {
2508
2734
  return values;
2509
2735
  }
2510
2736
  function findArrayDecl(root, varName) {
2511
- function walk5(node) {
2737
+ function walk4(node) {
2512
2738
  if (node.type === "variable_declarator") {
2513
2739
  const nameNode = node.childForFieldName("name");
2514
2740
  const valueNode = node.childForFieldName("value");
@@ -2521,12 +2747,12 @@ function findArrayDecl(root, varName) {
2521
2747
  }
2522
2748
  }
2523
2749
  for (const child of node.namedChildren) {
2524
- const found = walk5(child);
2750
+ const found = walk4(child);
2525
2751
  if (found) return found;
2526
2752
  }
2527
2753
  return null;
2528
2754
  }
2529
- return walk5(root);
2755
+ return walk4(root);
2530
2756
  }
2531
2757
  function extractObjectPropsRegex(objStr) {
2532
2758
  const props = {};
@@ -2593,10 +2819,10 @@ function extractSeedData(rootDir) {
2593
2819
  (0, import_node_path10.join)(rootDir, "prisma", "seed.ts"),
2594
2820
  (0, import_node_path10.join)(rootDir, "prisma", "seed.js"),
2595
2821
  (0, import_node_path10.join)(rootDir, "src", "server", "lib", "system-tags.ts")
2596
- ].filter(import_node_fs10.existsSync);
2822
+ ].filter(import_node_fs11.existsSync);
2597
2823
  const useTreeSitter = tryLoadTreeSitter();
2598
2824
  for (const filePath of seedFiles) {
2599
- const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
2825
+ const content = (0, import_node_fs11.readFileSync)(filePath, "utf-8");
2600
2826
  const relPath = (0, import_node_path10.relative)(rootDir, filePath);
2601
2827
  const seeded = detectSeededArrays(content, relPath);
2602
2828
  let astRoot = null;
@@ -2691,9 +2917,9 @@ function extractSeedData(rootDir) {
2691
2917
  return { nodes, edges };
2692
2918
  }
2693
2919
  function walkDir(dir, exts) {
2694
- if (!(0, import_node_fs10.existsSync)(dir)) return [];
2920
+ if (!(0, import_node_fs11.existsSync)(dir)) return [];
2695
2921
  const results = [];
2696
- for (const entry of (0, import_node_fs10.readdirSync)(dir, { withFileTypes: true })) {
2922
+ for (const entry of (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true })) {
2697
2923
  if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist") continue;
2698
2924
  const full = (0, import_node_path10.join)(dir, entry.name);
2699
2925
  if (entry.isDirectory()) results.push(...walkDir(full, exts));
@@ -2704,9 +2930,9 @@ function walkDir(dir, exts) {
2704
2930
  function extractConstants(rootDir) {
2705
2931
  const nodes = [];
2706
2932
  const srcDir = (0, import_node_path10.join)(rootDir, "src");
2707
- if (!(0, import_node_fs10.existsSync)(srcDir)) return { nodes };
2933
+ if (!(0, import_node_fs11.existsSync)(srcDir)) return { nodes };
2708
2934
  for (const filePath of walkDir(srcDir, [".ts", ".tsx"])) {
2709
- const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
2935
+ const content = (0, import_node_fs11.readFileSync)(filePath, "utf-8");
2710
2936
  const relPath = (0, import_node_path10.relative)(rootDir, filePath);
2711
2937
  const constArrayRe = /export\s+const\s+([A-Z][A-Z_0-9]+)\s*(?::[^=]+)?\s*=\s*\[/g;
2712
2938
  let cm;
@@ -2740,7 +2966,7 @@ function extractConstants(rootDir) {
2740
2966
  return { nodes };
2741
2967
  }
2742
2968
  function detect4(rootDir) {
2743
- return (0, import_node_fs10.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "schema.prisma")) || (0, import_node_fs10.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "seed.ts"));
2969
+ return (0, import_node_fs11.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "schema.prisma")) || (0, import_node_fs11.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "seed.ts"));
2744
2970
  }
2745
2971
  function generate4(rootDir) {
2746
2972
  const enumResult = extractEnumValues(rootDir);
@@ -2807,11 +3033,11 @@ function generate4(rootDir) {
2807
3033
  }
2808
3034
  };
2809
3035
  }
2810
- var import_node_fs10, import_node_path10, parseCode, SHARED_MODELS, DB_MODELS, staticValuesParser;
3036
+ var import_node_fs11, import_node_path10, parseCode, SHARED_MODELS, DB_MODELS, staticValuesParser;
2811
3037
  var init_static_values = __esm({
2812
3038
  "src/server/graph/parsers/static/static-values.ts"() {
2813
3039
  "use strict";
2814
- import_node_fs10 = require("node:fs");
3040
+ import_node_fs11 = require("node:fs");
2815
3041
  import_node_path10 = require("node:path");
2816
3042
  parseCode = null;
2817
3043
  SHARED_MODELS = /* @__PURE__ */ new Set(["permission", "role", "tag"]);
@@ -2826,23 +3052,6 @@ var init_static_values = __esm({
2826
3052
  });
2827
3053
 
2828
3054
  // src/server/graph/parsers/crosslayer/static-ref-scanner.ts
2829
- function walk4(dir, exts) {
2830
- if (!(0, import_node_fs11.existsSync)(dir)) return [];
2831
- const results = [];
2832
- function recurse(d) {
2833
- for (const entry of (0, import_node_fs11.readdirSync)(d, { withFileTypes: true })) {
2834
- const full = (0, import_node_path11.join)(d, entry.name);
2835
- if (entry.isDirectory()) {
2836
- if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist") continue;
2837
- recurse(full);
2838
- } else if (exts.some((ext) => entry.name.endsWith(ext))) {
2839
- results.push(full);
2840
- }
2841
- }
2842
- }
2843
- recurse(dir);
2844
- return results;
2845
- }
2846
3055
  function isInCommentOrType(node) {
2847
3056
  let current = node.parent;
2848
3057
  while (current) {
@@ -2927,14 +3136,15 @@ function collectStaticRefsRegex(content, valueLookup, allValues) {
2927
3136
  }
2928
3137
  return refs;
2929
3138
  }
2930
- var import_node_fs11, import_node_path11, MIN_VALUE_LENGTH, SKIP_VALUES, staticRefScannerParser;
3139
+ var import_node_fs12, import_node_path11, MIN_VALUE_LENGTH, SKIP_VALUES, staticRefScannerParser;
2931
3140
  var init_static_ref_scanner = __esm({
2932
3141
  "src/server/graph/parsers/crosslayer/static-ref-scanner.ts"() {
2933
3142
  "use strict";
2934
- import_node_fs11 = require("node:fs");
3143
+ import_node_fs12 = require("node:fs");
2935
3144
  import_node_path11 = require("node:path");
2936
3145
  init_config();
2937
3146
  init_resolve_paths();
3147
+ init_walk();
2938
3148
  MIN_VALUE_LENGTH = 4;
2939
3149
  SKIP_VALUES = /* @__PURE__ */ new Set([
2940
3150
  "true",
@@ -2993,11 +3203,11 @@ var init_static_ref_scanner = __esm({
2993
3203
  if (!paths) return { cross_refs: [], flagged_edges: [], warnings: [] };
2994
3204
  const srcDir = paths.srcDir;
2995
3205
  const files = [
2996
- ...walk4((0, import_node_path11.join)(srcDir, "client"), [".ts", ".tsx"]),
2997
- ...walk4(paths.appDir, [".ts", ".tsx"]),
2998
- ...walk4((0, import_node_path11.join)(srcDir, "server"), [".ts", ".tsx"]),
2999
- ...walk4((0, import_node_path11.join)(srcDir, "lib"), [".ts", ".tsx"]),
3000
- ...walk4((0, import_node_path11.join)(srcDir, "config"), [".ts", ".tsx"])
3206
+ ...walkWithIgnore((0, import_node_path11.join)(srcDir, "client"), [".ts", ".tsx"]),
3207
+ ...walkWithIgnore(paths.appDir, [".ts", ".tsx"]),
3208
+ ...walkWithIgnore((0, import_node_path11.join)(srcDir, "server"), [".ts", ".tsx"]),
3209
+ ...walkWithIgnore((0, import_node_path11.join)(srcDir, "lib"), [".ts", ".tsx"]),
3210
+ ...walkWithIgnore((0, import_node_path11.join)(srcDir, "config"), [".ts", ".tsx"])
3001
3211
  ];
3002
3212
  const uiOutput = layerOutputs.get("ui");
3003
3213
  const apiOutput = layerOutputs.get("api");
@@ -3018,7 +3228,7 @@ var init_static_ref_scanner = __esm({
3018
3228
  const sourceId = (0, import_node_path11.relative)(srcDir, absPath).replace(/\\/g, "/");
3019
3229
  const sourceLayer = uiNodeIds.has(sourceId) ? "ui" : apiNodeIds.has(sourceId) ? "api" : null;
3020
3230
  if (!sourceLayer) continue;
3021
- const content = (0, import_node_fs11.readFileSync)(absPath, "utf-8");
3231
+ const content = (0, import_node_fs12.readFileSync)(absPath, "utf-8");
3022
3232
  filesScanned++;
3023
3233
  let fileRefs;
3024
3234
  if (parseCode2) {
@@ -3287,9 +3497,9 @@ var init_merge = __esm({
3287
3497
  // src/server/graph/core/graph-builder.ts
3288
3498
  function readGraphFromDisk(rootDir, layer) {
3289
3499
  const filePath = (0, import_node_path13.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
3290
- if (!(0, import_node_fs12.existsSync)(filePath)) return null;
3500
+ if (!(0, import_node_fs13.existsSync)(filePath)) return null;
3291
3501
  try {
3292
- return JSON.parse((0, import_node_fs12.readFileSync)(filePath, "utf-8"));
3502
+ return JSON.parse((0, import_node_fs13.readFileSync)(filePath, "utf-8"));
3293
3503
  } catch {
3294
3504
  return null;
3295
3505
  }
@@ -3385,11 +3595,11 @@ function generateAll(rootDir) {
3385
3595
  const extras = [...byLayer.keys()].filter((l) => !wellKnownOrder.includes(l)).sort();
3386
3596
  return [...wellKnownOrder, ...extras].map((l) => byLayer.get(l)).filter((r) => !!r);
3387
3597
  }
3388
- var import_node_fs12, import_node_path13;
3598
+ var import_node_fs13, import_node_path13;
3389
3599
  var init_graph_builder = __esm({
3390
3600
  "src/server/graph/core/graph-builder.ts"() {
3391
3601
  "use strict";
3392
- import_node_fs12 = require("node:fs");
3602
+ import_node_fs13 = require("node:fs");
3393
3603
  import_node_path13 = require("node:path");
3394
3604
  init_config();
3395
3605
  init_parser_registry();
@@ -3436,11 +3646,11 @@ function detectConventionDirs(rootDir, extraConventionDirs = []) {
3436
3646
  for (const base of searchDirs) {
3437
3647
  for (const convention of conventionDirs) {
3438
3648
  const dir = (0, import_node_path14.join)(base, convention);
3439
- if (!(0, import_node_fs13.existsSync)(dir)) continue;
3649
+ if (!(0, import_node_fs14.existsSync)(dir)) continue;
3440
3650
  try {
3441
- const stat = (0, import_node_fs13.statSync)(dir);
3651
+ const stat = (0, import_node_fs14.statSync)(dir);
3442
3652
  if (!stat.isDirectory()) continue;
3443
- const entries = (0, import_node_fs13.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
3653
+ const entries = (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
3444
3654
  if (entries.length > 0) {
3445
3655
  const relPath = dir.replace(rootDir + "/", "").replace(rootDir + "\\", "");
3446
3656
  result.set(relPath, entries);
@@ -3512,11 +3722,11 @@ function extractModuleFromPath(id, extraTrivial, extraSkipSegments) {
3512
3722
  }
3513
3723
  return "root";
3514
3724
  }
3515
- var import_node_fs13, import_node_path14, CONVENTION_DIRS_BUILTIN, GENERIC_ROLE_NAMES_BUILTIN, SKIP_SEGMENTS_BUILTIN, TRIVIAL_GROUPS, cachedRootDir, cachedConventionDirs, moduleTagger;
3725
+ var import_node_fs14, import_node_path14, CONVENTION_DIRS_BUILTIN, GENERIC_ROLE_NAMES_BUILTIN, SKIP_SEGMENTS_BUILTIN, TRIVIAL_GROUPS, cachedRootDir, cachedConventionDirs, moduleTagger;
3516
3726
  var init_module_tagger = __esm({
3517
3727
  "src/server/graph/taggers/module-tagger.ts"() {
3518
3728
  "use strict";
3519
- import_node_fs13 = require("node:fs");
3729
+ import_node_fs14 = require("node:fs");
3520
3730
  import_node_path14 = require("node:path");
3521
3731
  CONVENTION_DIRS_BUILTIN = ["features", "modules", "domains", "areas"];
3522
3732
  GENERIC_ROLE_NAMES_BUILTIN = /* @__PURE__ */ new Set([
@@ -3786,14 +3996,14 @@ function tagsFilePath(rootDir) {
3786
3996
  }
3787
3997
  function readTagStore(rootDir) {
3788
3998
  const filePath = tagsFilePath(rootDir);
3789
- if (!(0, import_node_fs14.existsSync)(filePath)) return {};
3790
- const stat = (0, import_node_fs14.statSync)(filePath);
3999
+ if (!(0, import_node_fs15.existsSync)(filePath)) return {};
4000
+ const stat = (0, import_node_fs15.statSync)(filePath);
3791
4001
  const cached = tagCache.get(filePath);
3792
4002
  if (cached && cached.mtimeMs === stat.mtimeMs) {
3793
4003
  return cached.store;
3794
4004
  }
3795
4005
  try {
3796
- const content = (0, import_node_fs14.readFileSync)(filePath, "utf-8");
4006
+ const content = (0, import_node_fs15.readFileSync)(filePath, "utf-8");
3797
4007
  const store = JSON.parse(content);
3798
4008
  tagCache.set(filePath, { mtimeMs: stat.mtimeMs, store });
3799
4009
  return store;
@@ -3804,14 +4014,14 @@ function readTagStore(rootDir) {
3804
4014
  function writeTagStore(rootDir, store) {
3805
4015
  const filePath = tagsFilePath(rootDir);
3806
4016
  const dir = (0, import_node_path16.dirname)(filePath);
3807
- (0, import_node_fs14.mkdirSync)(dir, { recursive: true });
4017
+ (0, import_node_fs15.mkdirSync)(dir, { recursive: true });
3808
4018
  const cleaned = {};
3809
4019
  for (const [nodeId, tags] of Object.entries(store)) {
3810
4020
  if (Object.keys(tags).length > 0) {
3811
4021
  cleaned[nodeId] = tags;
3812
4022
  }
3813
4023
  }
3814
- (0, import_node_fs14.writeFileSync)(filePath, JSON.stringify(cleaned, null, 2) + "\n", "utf-8");
4024
+ (0, import_node_fs15.writeFileSync)(filePath, JSON.stringify(cleaned, null, 2) + "\n", "utf-8");
3815
4025
  tagCache.delete(filePath);
3816
4026
  }
3817
4027
  function setTag(rootDir, nodeId, key, value) {
@@ -3829,11 +4039,11 @@ function removeTag(rootDir, nodeId, key) {
3829
4039
  }
3830
4040
  writeTagStore(rootDir, store);
3831
4041
  }
3832
- var import_node_fs14, import_node_path16, TAGS_FILENAME, GRAPHS_DIR, tagCache;
4042
+ var import_node_fs15, import_node_path16, TAGS_FILENAME, GRAPHS_DIR, tagCache;
3833
4043
  var init_tag_store = __esm({
3834
4044
  "src/server/graph/core/tag-store.ts"() {
3835
4045
  "use strict";
3836
- import_node_fs14 = require("node:fs");
4046
+ import_node_fs15 = require("node:fs");
3837
4047
  import_node_path16 = require("node:path");
3838
4048
  TAGS_FILENAME = "tags.json";
3839
4049
  GRAPHS_DIR = ".launchsecure/graphs";
@@ -3844,8 +4054,8 @@ var init_tag_store = __esm({
3844
4054
  // src/server/graph/index.ts
3845
4055
  function getAvailableLayers(rootDir) {
3846
4056
  const dir = (0, import_node_path17.join)(rootDir, GRAPHS_DIR2);
3847
- if (!(0, import_node_fs15.existsSync)(dir)) return [];
3848
- return (0, import_node_fs15.readdirSync)(dir).filter((f) => f.endsWith(".json") && f !== "tags.json").map((f) => f.replace(".json", ""));
4057
+ if (!(0, import_node_fs16.existsSync)(dir)) return [];
4058
+ return (0, import_node_fs16.readdirSync)(dir).filter((f) => f.endsWith(".json") && f !== "tags.json").map((f) => f.replace(".json", ""));
3849
4059
  }
3850
4060
  function graphsDir(rootDir) {
3851
4061
  return (0, import_node_path17.join)(rootDir, GRAPHS_DIR2);
@@ -3857,8 +4067,8 @@ function tagsFilePath2(rootDir) {
3857
4067
  return (0, import_node_path17.join)(graphsDir(rootDir), "tags.json");
3858
4068
  }
3859
4069
  function getMtimeMs(filePath) {
3860
- if (!(0, import_node_fs15.existsSync)(filePath)) return 0;
3861
- return (0, import_node_fs15.statSync)(filePath).mtimeMs;
4070
+ if (!(0, import_node_fs16.existsSync)(filePath)) return 0;
4071
+ return (0, import_node_fs16.statSync)(filePath).mtimeMs;
3862
4072
  }
3863
4073
  function invalidateCache(filePath) {
3864
4074
  graphCache.delete(filePath);
@@ -3897,20 +4107,20 @@ function applyTags(graph, layer, rootDir) {
3897
4107
  }
3898
4108
  function readGraphRaw(rootDir, layer) {
3899
4109
  const filePath = graphFilePath(rootDir, layer);
3900
- if (!(0, import_node_fs15.existsSync)(filePath)) return null;
3901
- const stat = (0, import_node_fs15.statSync)(filePath);
4110
+ if (!(0, import_node_fs16.existsSync)(filePath)) return null;
4111
+ const stat = (0, import_node_fs16.statSync)(filePath);
3902
4112
  const cached = graphCache.get(filePath);
3903
4113
  if (cached && cached.mtimeMs === stat.mtimeMs) {
3904
4114
  return cached.graph;
3905
4115
  }
3906
- const content = (0, import_node_fs15.readFileSync)(filePath, "utf-8");
4116
+ const content = (0, import_node_fs16.readFileSync)(filePath, "utf-8");
3907
4117
  const graph = JSON.parse(content);
3908
4118
  graphCache.set(filePath, { mtimeMs: stat.mtimeMs, graph });
3909
4119
  return graph;
3910
4120
  }
3911
4121
  function readGraph(rootDir, layer) {
3912
4122
  const rawFilePath = graphFilePath(rootDir, layer);
3913
- if (!(0, import_node_fs15.existsSync)(rawFilePath)) return null;
4123
+ if (!(0, import_node_fs16.existsSync)(rawFilePath)) return null;
3914
4124
  const rawMtime = getMtimeMs(rawFilePath);
3915
4125
  const tagsMtime = getMtimeMs(tagsFilePath2(rootDir));
3916
4126
  const cacheKey = `${rootDir}:${layer}`;
@@ -3940,21 +4150,21 @@ async function generateGraph(rootDir, layer) {
3940
4150
  mutationMethods: config.parsers?.patterns?.mutationMethods
3941
4151
  });
3942
4152
  const dir = graphsDir(rootDir);
3943
- (0, import_node_fs15.mkdirSync)(dir, { recursive: true });
4153
+ (0, import_node_fs16.mkdirSync)(dir, { recursive: true });
3944
4154
  const results = layer ? [generateLayer(rootDir, layer)].filter((r) => r !== null) : generateAll(rootDir);
3945
4155
  for (const result of results) {
3946
4156
  const filePath = graphFilePath(rootDir, result.layer);
3947
- (0, import_node_fs15.writeFileSync)(filePath, JSON.stringify(result.output, null, 2) + "\n", "utf-8");
4157
+ (0, import_node_fs16.writeFileSync)(filePath, JSON.stringify(result.output, null, 2) + "\n", "utf-8");
3948
4158
  invalidateCache(filePath);
3949
4159
  invalidateTaggedCache(rootDir, result.layer);
3950
4160
  }
3951
4161
  return results;
3952
4162
  }
3953
- var import_node_fs15, import_node_path17, GRAPHS_DIR2, graphCache, taggedCache;
4163
+ var import_node_fs16, import_node_path17, GRAPHS_DIR2, graphCache, taggedCache;
3954
4164
  var init_graph = __esm({
3955
4165
  "src/server/graph/index.ts"() {
3956
4166
  "use strict";
3957
- import_node_fs15 = require("node:fs");
4167
+ import_node_fs16 = require("node:fs");
3958
4168
  import_node_path17 = require("node:path");
3959
4169
  init_graph_builder();
3960
4170
  init_config();
@@ -3971,9 +4181,9 @@ var init_graph = __esm({
3971
4181
  // src/server/graph/core/audit-core.ts
3972
4182
  function readGraphFile(rootDir, layer) {
3973
4183
  const filePath = (0, import_node_path18.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
3974
- if (!(0, import_node_fs16.existsSync)(filePath)) return null;
4184
+ if (!(0, import_node_fs17.existsSync)(filePath)) return null;
3975
4185
  try {
3976
- return JSON.parse((0, import_node_fs16.readFileSync)(filePath, "utf-8"));
4186
+ return JSON.parse((0, import_node_fs17.readFileSync)(filePath, "utf-8"));
3977
4187
  } catch {
3978
4188
  return null;
3979
4189
  }
@@ -4019,8 +4229,8 @@ function checkUnprotectedRoutes(rootDir) {
4019
4229
  if (!api) return buildReport("api", "unprotected_routes", findings);
4020
4230
  const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
4021
4231
  let routePermsContent = "";
4022
- if ((0, import_node_fs16.existsSync)(routePermsPath)) {
4023
- routePermsContent = (0, import_node_fs16.readFileSync)(routePermsPath, "utf-8");
4232
+ if ((0, import_node_fs17.existsSync)(routePermsPath)) {
4233
+ routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
4024
4234
  }
4025
4235
  const registeredRoutes = /* @__PURE__ */ new Set();
4026
4236
  const routeEntryRe = /path:\s*'([^']+)'/g;
@@ -4099,8 +4309,8 @@ function checkUnenforcedPermissions(rootDir) {
4099
4309
  const permissions = staticGraph.nodes.filter((n) => n.type === "seed_permission").map((n) => ({ id: n.id, key: n.value, name: n.name }));
4100
4310
  const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
4101
4311
  let routePermsContent = "";
4102
- if ((0, import_node_fs16.existsSync)(routePermsPath)) {
4103
- routePermsContent = (0, import_node_fs16.readFileSync)(routePermsPath, "utf-8");
4312
+ if ((0, import_node_fs17.existsSync)(routePermsPath)) {
4313
+ routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
4104
4314
  }
4105
4315
  for (const perm of permissions) {
4106
4316
  const regex = new RegExp(`permission:\\s*['"]${perm.key}['"]`);
@@ -4131,8 +4341,8 @@ function checkHardcodedValues(rootDir) {
4131
4341
  for (const node of api.nodes) {
4132
4342
  if (node.type !== "endpoint") continue;
4133
4343
  const filePath = (0, import_node_path18.join)(rootDir, "src", node.id);
4134
- if (!(0, import_node_fs16.existsSync)(filePath)) continue;
4135
- const content = (0, import_node_fs16.readFileSync)(filePath, "utf-8");
4344
+ if (!(0, import_node_fs17.existsSync)(filePath)) continue;
4345
+ const content = (0, import_node_fs17.readFileSync)(filePath, "utf-8");
4136
4346
  let m;
4137
4347
  allCapsRe.lastIndex = 0;
4138
4348
  while ((m = allCapsRe.exec(content)) !== null) {
@@ -4226,11 +4436,11 @@ function formatAsPrompt(reports) {
4226
4436
  if (lines.length === 0) return "No audit findings.";
4227
4437
  return lines.join("\n");
4228
4438
  }
4229
- var import_node_fs16, import_node_path18, CHECKS;
4439
+ var import_node_fs17, import_node_path18, CHECKS;
4230
4440
  var init_audit_core = __esm({
4231
4441
  "src/server/graph/core/audit-core.ts"() {
4232
4442
  "use strict";
4233
- import_node_fs16 = require("node:fs");
4443
+ import_node_fs17 = require("node:fs");
4234
4444
  import_node_path18 = require("node:path");
4235
4445
  CHECKS = {
4236
4446
  db: {
@@ -4264,14 +4474,14 @@ function findProjectRoot(startDir) {
4264
4474
  let dir = startDir;
4265
4475
  for (let i = 0; i < 8; i++) {
4266
4476
  const graphsDir2 = import_node_path19.default.join(dir, ".launchsecure", "graphs");
4267
- if (import_node_fs17.default.existsSync(import_node_path19.default.join(graphsDir2, "ui.json")) || import_node_fs17.default.existsSync(import_node_path19.default.join(graphsDir2, "api.json")) || import_node_fs17.default.existsSync(import_node_path19.default.join(graphsDir2, "db.json"))) return dir;
4477
+ if (import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "ui.json")) || import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "api.json")) || import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "db.json"))) return dir;
4268
4478
  const parent = import_node_path19.default.dirname(dir);
4269
4479
  if (parent === dir) break;
4270
4480
  dir = parent;
4271
4481
  }
4272
4482
  dir = startDir;
4273
4483
  for (let i = 0; i < 8; i++) {
4274
- if (import_node_fs17.default.existsSync(import_node_path19.default.join(dir, ".git"))) return dir;
4484
+ if (import_node_fs18.default.existsSync(import_node_path19.default.join(dir, ".git"))) return dir;
4275
4485
  const parent = import_node_path19.default.dirname(dir);
4276
4486
  if (parent === dir) break;
4277
4487
  dir = parent;
@@ -4332,16 +4542,16 @@ async function buildMergedGraph(root) {
4332
4542
  };
4333
4543
  }
4334
4544
  function serveStatic(res, filePath) {
4335
- if (!import_node_fs17.default.existsSync(filePath) || !import_node_fs17.default.statSync(filePath).isFile()) return false;
4545
+ if (!import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) return false;
4336
4546
  const ext = import_node_path19.default.extname(filePath).toLowerCase();
4337
4547
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
4338
4548
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
4339
- import_node_fs17.default.createReadStream(filePath).pipe(res);
4549
+ import_node_fs18.default.createReadStream(filePath).pipe(res);
4340
4550
  return true;
4341
4551
  }
4342
4552
  function serveIndex(res, clientDir) {
4343
4553
  const indexPath = import_node_path19.default.join(clientDir, "index.html");
4344
- if (!import_node_fs17.default.existsSync(indexPath)) {
4554
+ if (!import_node_fs18.default.existsSync(indexPath)) {
4345
4555
  res.writeHead(500, { "Content-Type": "text/plain" });
4346
4556
  res.end(`LaunchChart client bundle not found at ${clientDir}. Run 'npm run build:chart-client'.`);
4347
4557
  return;
@@ -4349,14 +4559,14 @@ function serveIndex(res, clientDir) {
4349
4559
  serveStatic(res, indexPath);
4350
4560
  }
4351
4561
  function tryListen(server, port) {
4352
- return new Promise((resolve3, reject) => {
4562
+ return new Promise((resolve4, reject) => {
4353
4563
  const onError = (err2) => {
4354
4564
  server.off("listening", onListening);
4355
4565
  reject(err2);
4356
4566
  };
4357
4567
  const onListening = () => {
4358
4568
  server.off("error", onError);
4359
- resolve3(port);
4569
+ resolve4(port);
4360
4570
  };
4361
4571
  server.once("error", onError);
4362
4572
  server.once("listening", onListening);
@@ -4410,10 +4620,10 @@ async function startChartServer(opts = {}) {
4410
4620
  if (req.method === "GET" && url2.pathname === "/api/projects") {
4411
4621
  const projectList = projects.length > 0 ? projects.map((p) => {
4412
4622
  const absRoot = import_node_path19.default.resolve(projectRoot, p.root);
4413
- const hasGraphs = import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, ".launchsecure", "graphs"));
4414
- const hasNextConfig2 = import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, "next.config.ts")) || import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, "next.config.js")) || import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, "next.config.mjs"));
4415
- return { name: p.name, root: p.root, hasGraphs, hasNextConfig: hasNextConfig2 };
4416
- }) : [{ name: import_node_path19.default.basename(projectRoot), root: ".", hasGraphs: import_node_fs17.default.existsSync(import_node_path19.default.join(projectRoot, ".launchsecure", "graphs")), hasNextConfig: true }];
4623
+ const hasGraphs = import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, ".launchsecure", "graphs"));
4624
+ const hasNextConfig = import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.ts")) || import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.js")) || import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.mjs"));
4625
+ return { name: p.name, root: p.root, hasGraphs, hasNextConfig };
4626
+ }) : [{ name: import_node_path19.default.basename(projectRoot), root: ".", hasGraphs: import_node_fs18.default.existsSync(import_node_path19.default.join(projectRoot, ".launchsecure", "graphs")), hasNextConfig: true }];
4417
4627
  res.writeHead(200, { "Content-Type": "application/json" });
4418
4628
  res.end(JSON.stringify({ projects: projectList, monorepoRoot: projectRoot }));
4419
4629
  return;
@@ -4465,14 +4675,14 @@ async function startChartServer(opts = {}) {
4465
4675
  return;
4466
4676
  }
4467
4677
  const filePath = import_node_path19.default.join(reqRoot, relPath);
4468
- if (!filePath.startsWith(reqRoot) || !import_node_fs17.default.existsSync(filePath) || !import_node_fs17.default.statSync(filePath).isFile()) {
4678
+ if (!filePath.startsWith(reqRoot) || !import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) {
4469
4679
  res.writeHead(404, { "Content-Type": "application/json" });
4470
4680
  res.end(JSON.stringify({ error: "File not found" }));
4471
4681
  return;
4472
4682
  }
4473
4683
  const ext = import_node_path19.default.extname(filePath).toLowerCase();
4474
4684
  const langMap = { ".ts": "typescript", ".tsx": "tsx", ".js": "javascript", ".jsx": "jsx", ".prisma": "prisma", ".json": "json", ".css": "css" };
4475
- const content = import_node_fs17.default.readFileSync(filePath, "utf-8");
4685
+ const content = import_node_fs18.default.readFileSync(filePath, "utf-8");
4476
4686
  res.writeHead(200, { "Content-Type": "application/json" });
4477
4687
  res.end(JSON.stringify({ content, language: langMap[ext] ?? "text", path: relPath }));
4478
4688
  return;
@@ -4515,7 +4725,7 @@ async function startChartServer(opts = {}) {
4515
4725
  try {
4516
4726
  const newConfig = JSON.parse(body);
4517
4727
  const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
4518
- import_node_fs17.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
4728
+ import_node_fs18.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
4519
4729
  res.writeHead(200, { "Content-Type": "application/json" });
4520
4730
  res.end(JSON.stringify({ ok: true }));
4521
4731
  } catch (err2) {
@@ -4549,7 +4759,7 @@ async function startChartServer(opts = {}) {
4549
4759
  const config2 = loadConfig(reqRoot);
4550
4760
  config2.taggers = taggerConfig;
4551
4761
  const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
4552
- import_node_fs17.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4762
+ import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4553
4763
  res.writeHead(200, { "Content-Type": "application/json" });
4554
4764
  res.end(JSON.stringify({ ok: true }));
4555
4765
  } catch (err2) {
@@ -4616,16 +4826,23 @@ async function startChartServer(opts = {}) {
4616
4826
  const paths = resolveProjectPaths(reqRoot, config2);
4617
4827
  const overrides = {
4618
4828
  appDir: !!config2.paths?.appDir,
4619
- dbDir: !!config2.paths?.dbDir
4829
+ dbDir: !!config2.paths?.dbDir,
4830
+ srcRoots: !!(config2.paths?.srcRoots && config2.paths.srcRoots.length > 0)
4620
4831
  };
4832
+ const relFromRoot = (abs) => import_node_path19.default.relative(reqRoot, abs) || ".";
4621
4833
  res.writeHead(200, { "Content-Type": "application/json" });
4622
4834
  res.end(JSON.stringify({
4623
4835
  projectRoot: reqRoot,
4624
4836
  detected: paths ? {
4625
- srcDir: import_node_path19.default.relative(reqRoot, paths.srcDir) || ".",
4626
- appDir: import_node_path19.default.relative(reqRoot, paths.appDir),
4627
- apiDir: import_node_path19.default.relative(reqRoot, paths.apiDir),
4628
- dbDir: paths.dbDir ? import_node_path19.default.relative(reqRoot, paths.dbDir) : null
4837
+ srcDir: relFromRoot(paths.srcDir),
4838
+ appDir: relFromRoot(paths.appDir),
4839
+ apiDir: relFromRoot(paths.apiDir),
4840
+ dbDir: paths.dbDir ? relFromRoot(paths.dbDir) : null,
4841
+ dbKind: paths.dbConfig.kind,
4842
+ dbSchemaPath: paths.dbConfig.schemaPath ? relFromRoot(paths.dbConfig.schemaPath) : null,
4843
+ dbMigrationsDir: paths.dbConfig.migrationsDir ? relFromRoot(paths.dbConfig.migrationsDir) : null,
4844
+ srcRoots: paths.srcRoots.map(relFromRoot),
4845
+ conventionFiles: paths.conventionFiles.map(relFromRoot)
4629
4846
  } : null,
4630
4847
  overrides,
4631
4848
  isOverride: overrides.appDir
@@ -4642,7 +4859,7 @@ async function startChartServer(opts = {}) {
4642
4859
  return;
4643
4860
  }
4644
4861
  try {
4645
- const entries = import_node_fs17.default.readdirSync(abs, { withFileTypes: true });
4862
+ const entries = import_node_fs18.default.readdirSync(abs, { withFileTypes: true });
4646
4863
  const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist" && e.name !== ".next").map((e) => e.name).sort();
4647
4864
  const parent = abs !== twoUp ? import_node_path19.default.dirname(abs) : null;
4648
4865
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -4673,7 +4890,7 @@ async function startChartServer(opts = {}) {
4673
4890
  const config2 = loadConfig(projectRoot);
4674
4891
  config2.projects = newProjects.length > 0 ? newProjects : void 0;
4675
4892
  const configPath = import_node_path19.default.join(projectRoot, ".launchchart.json");
4676
- import_node_fs17.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4893
+ import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4677
4894
  projects.length = 0;
4678
4895
  if (config2.projects) projects.push(...config2.projects);
4679
4896
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -4746,12 +4963,12 @@ function runServeCli(argv) {
4746
4963
  process.exit(1);
4747
4964
  });
4748
4965
  }
4749
- var import_node_http, import_node_fs17, import_node_path19, MAX_PORT_SCAN, MIME_TYPES;
4966
+ var import_node_http, import_node_fs18, import_node_path19, MAX_PORT_SCAN, MIME_TYPES;
4750
4967
  var init_chart_serve = __esm({
4751
4968
  "src/server/chart-serve.ts"() {
4752
4969
  "use strict";
4753
4970
  import_node_http = __toESM(require("node:http"));
4754
- import_node_fs17 = __toESM(require("node:fs"));
4971
+ import_node_fs18 = __toESM(require("node:fs"));
4755
4972
  import_node_path19 = __toESM(require("node:path"));
4756
4973
  init_graph();
4757
4974
  init_lockfile();
@@ -4774,214 +4991,6 @@ var init_chart_serve = __esm({
4774
4991
  }
4775
4992
  });
4776
4993
 
4777
- // src/server/blast-radius-builder.ts
4778
- function loadDefaults(rootDir) {
4779
- const filePath = (0, import_node_path20.join)(rootDir, ".launchsecure", "blast-radius-defaults.json");
4780
- try {
4781
- if (import_node_fs18.default.existsSync(filePath)) {
4782
- const raw = import_node_fs18.default.readFileSync(filePath, "utf-8");
4783
- return JSON.parse(raw);
4784
- }
4785
- } catch {
4786
- }
4787
- return FALLBACK_DEFAULTS;
4788
- }
4789
- function generateAcceptance(node, inspect) {
4790
- const criteria = [];
4791
- const t = node.type?.toLowerCase() ?? "";
4792
- if (t === "endpoint" || t === "mcp-tool") {
4793
- const methods = inspect?.methods ?? [];
4794
- const path3 = inspect?.path ?? node.id;
4795
- if (methods.length > 0) {
4796
- criteria.push(`${methods.join("/")} ${path3} still returns correct responses for authorized users`);
4797
- } else {
4798
- criteria.push(`${path3} still responds correctly`);
4799
- }
4800
- if (inspect?.auth && inspect.auth.includes("withAuth")) {
4801
- criteria.push("Authentication and authorization still enforced");
4802
- }
4803
- if (inspect?.db_models && inspect.db_models.length > 0) {
4804
- criteria.push(`DB operations on ${inspect.db_models.join(", ")} still work correctly`);
4805
- }
4806
- } else if (t === "page" || t === "component" || t === "layout") {
4807
- criteria.push(`${node.name} renders without errors`);
4808
- if (inspect?.stateVars && inspect.stateVars.length > 0) {
4809
- criteria.push("State management still works correctly");
4810
- }
4811
- if (inspect?.elements && inspect.elements.length > 5) {
4812
- criteria.push("All child components render correctly");
4813
- }
4814
- } else if (t === "table" || t === "enum") {
4815
- criteria.push(`${node.name} schema unchanged or migration applies cleanly`);
4816
- criteria.push("Existing queries against this table still work");
4817
- } else if (t === "hook") {
4818
- criteria.push(`${node.name} returns expected shape`);
4819
- if (inspect?.stateVars && inspect.stateVars.length > 0) {
4820
- criteria.push(`State variables [${inspect.stateVars.map((s) => s.name).join(", ")}] still returned`);
4821
- }
4822
- } else if (t === "context") {
4823
- criteria.push(`${node.name} provides correct context to consumers`);
4824
- } else if (t === "lib" || t === "config" || t === "types") {
4825
- criteria.push(`${node.name} exports still conform to expected interface`);
4826
- } else if (t === "seed" || t === "seed_role" || t === "seed_permission") {
4827
- criteria.push("Seed runs without errors");
4828
- criteria.push("Expected rows created in database");
4829
- } else {
4830
- criteria.push("Verify no regression");
4831
- }
4832
- return criteria;
4833
- }
4834
- function buildManifest(input) {
4835
- const { mode, title, description, subtitle, blastResults, createNodes, inspectData, defaults } = input;
4836
- const nodeMap = /* @__PURE__ */ new Map();
4837
- const centerNodeIds = /* @__PURE__ */ new Set();
4838
- for (const result of blastResults) {
4839
- centerNodeIds.add(result.center.id);
4840
- for (const node of result.affected) {
4841
- const existing = nodeMap.get(node.id);
4842
- if (!existing || node.hop < existing.hop) {
4843
- nodeMap.set(node.id, node);
4844
- }
4845
- }
4846
- }
4847
- for (const id of centerNodeIds) {
4848
- nodeMap.delete(id);
4849
- }
4850
- const manifestNodes = [];
4851
- for (const result of blastResults) {
4852
- const c = result.center;
4853
- if (manifestNodes.some((n) => n.id === c.id)) continue;
4854
- const inspect = inspectData[c.id];
4855
- manifestNodes.push({
4856
- id: c.id,
4857
- name: c.name,
4858
- layer: c.layer,
4859
- ring: "modify",
4860
- type: c.type,
4861
- reason: `Direct change target`,
4862
- acceptance: generateAcceptance(
4863
- { id: c.id, name: c.name, type: c.type, layer: c.layer, hop: 0 },
4864
- inspect
4865
- )
4866
- });
4867
- }
4868
- for (const [, node] of nodeMap) {
4869
- const ring = node.hop <= 1 ? "modify" : "ripple";
4870
- const inspect = inspectData[node.id];
4871
- const reason = node.hop <= 1 ? `Directly depends on changed node` : `Indirect dependency (${node.hop} hops away)`;
4872
- manifestNodes.push({
4873
- id: node.id,
4874
- name: node.name,
4875
- layer: node.layer,
4876
- ring,
4877
- type: node.type,
4878
- reason,
4879
- acceptance: generateAcceptance(node, inspect)
4880
- });
4881
- }
4882
- for (const cn of createNodes) {
4883
- manifestNodes.push({
4884
- id: cn.id,
4885
- name: cn.name,
4886
- layer: cn.layer,
4887
- ring: "create",
4888
- type: cn.type ?? "unknown",
4889
- reason: cn.reason,
4890
- acceptance: cn.acceptance ?? ["Verify implementation matches spec"]
4891
- });
4892
- }
4893
- const layerIds = /* @__PURE__ */ new Set();
4894
- for (const n of manifestNodes) {
4895
- layerIds.add(n.layer);
4896
- }
4897
- const layers = [];
4898
- for (const id of layerIds) {
4899
- const def = defaults.layers[id];
4900
- if (def) {
4901
- layers.push({ id, name: def.name, icon: def.icon, color: def.color });
4902
- } else {
4903
- layers.push({ id, name: id, icon: "box", color: "#cbd5e1" });
4904
- }
4905
- }
4906
- const edgeSet = /* @__PURE__ */ new Set();
4907
- const edges = [];
4908
- const allNodeIds = new Set(manifestNodes.map((n) => n.id));
4909
- for (const cId of centerNodeIds) {
4910
- for (const result of blastResults) {
4911
- for (const affected of result.affected) {
4912
- if (affected.hop === 1 && result.center.id === cId && allNodeIds.has(affected.id)) {
4913
- const key = `${cId}->${affected.id}`;
4914
- if (!edgeSet.has(key)) {
4915
- edgeSet.add(key);
4916
- edges.push({ source: cId, target: affected.id });
4917
- }
4918
- }
4919
- }
4920
- }
4921
- }
4922
- for (const result of blastResults) {
4923
- if (result.edges) {
4924
- for (const edge of result.edges) {
4925
- if (allNodeIds.has(edge.source) && allNodeIds.has(edge.target)) {
4926
- const key = `${edge.source}->${edge.target}`;
4927
- if (!edgeSet.has(key)) {
4928
- edgeSet.add(key);
4929
- edges.push({ source: edge.source, target: edge.target });
4930
- }
4931
- }
4932
- }
4933
- }
4934
- }
4935
- for (const cn of createNodes) {
4936
- edges.push({ source: "center", target: cn.id });
4937
- if (cn.connects_to) {
4938
- for (const targetId of cn.connects_to) {
4939
- if (allNodeIds.has(targetId) || createNodes.some((c) => c.id === targetId)) {
4940
- const key = `${cn.id}->${targetId}`;
4941
- if (!edgeSet.has(key)) {
4942
- edgeSet.add(key);
4943
- edges.push({ source: cn.id, target: targetId });
4944
- }
4945
- }
4946
- }
4947
- }
4948
- }
4949
- return {
4950
- mode,
4951
- title,
4952
- subtitle,
4953
- layers,
4954
- rings: defaults.rings,
4955
- center: { name: title, description },
4956
- nodes: manifestNodes,
4957
- edges
4958
- };
4959
- }
4960
- var import_node_fs18, import_node_path20, FALLBACK_DEFAULTS;
4961
- var init_blast_radius_builder = __esm({
4962
- "src/server/blast-radius-builder.ts"() {
4963
- "use strict";
4964
- import_node_fs18 = __toESM(require("node:fs"));
4965
- import_node_path20 = require("node:path");
4966
- FALLBACK_DEFAULTS = {
4967
- rings: [
4968
- { id: "modify", name: "Modify", color: "#ff6b00" },
4969
- { id: "ripple", name: "Ripple (verify)", color: "#ffff00" },
4970
- { id: "create", name: "Create", color: "#00ff00" }
4971
- ],
4972
- layers: {
4973
- db: { name: "Database", icon: "database", color: "#cbd5e1" },
4974
- api: { name: "API", icon: "server", color: "#cbd5e1" },
4975
- middleware: { name: "Middleware", icon: "shield", color: "#cbd5e1" },
4976
- ui: { name: "UI", icon: "layout-dashboard", color: "#cbd5e1" },
4977
- config: { name: "Config / Seed", icon: "settings", color: "#cbd5e1" },
4978
- shared: { name: "Shared Types", icon: "box", color: "#cbd5e1" }
4979
- },
4980
- center: { color: "#ff0000" }
4981
- };
4982
- }
4983
- });
4984
-
4985
4994
  // src/server/graph/core/language-detection.ts
4986
4995
  function walkForExtensions(dir, extCounts, depth = 0) {
4987
4996
  if (depth > 10) return;
@@ -4996,9 +5005,9 @@ function walkForExtensions(dir, extCounts, depth = 0) {
4996
5005
  if (entry.name.startsWith(".") && entry.isDirectory()) continue;
4997
5006
  if (entry.isDirectory()) {
4998
5007
  if (IGNORE_DIRS.has(entry.name)) continue;
4999
- walkForExtensions((0, import_node_path21.join)(dir, entry.name), extCounts, depth + 1);
5008
+ walkForExtensions((0, import_node_path20.join)(dir, entry.name), extCounts, depth + 1);
5000
5009
  } else {
5001
- const ext = (0, import_node_path21.extname)(entry.name).toLowerCase();
5010
+ const ext = (0, import_node_path20.extname)(entry.name).toLowerCase();
5002
5011
  if (ext && EXTENSION_TO_LANGUAGE[ext]) {
5003
5012
  extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
5004
5013
  }
@@ -5037,12 +5046,12 @@ function detectLanguages(rootDir, supportedLanguages) {
5037
5046
  });
5038
5047
  return results;
5039
5048
  }
5040
- var import_node_fs19, import_node_path21, EXTENSION_TO_LANGUAGE, IGNORE_DIRS, AUXILIARY_LANGUAGES;
5049
+ var import_node_fs19, import_node_path20, EXTENSION_TO_LANGUAGE, IGNORE_DIRS, AUXILIARY_LANGUAGES;
5041
5050
  var init_language_detection = __esm({
5042
5051
  "src/server/graph/core/language-detection.ts"() {
5043
5052
  "use strict";
5044
5053
  import_node_fs19 = require("node:fs");
5045
- import_node_path21 = require("node:path");
5054
+ import_node_path20 = require("node:path");
5046
5055
  EXTENSION_TO_LANGUAGE = {
5047
5056
  // Web / Frontend
5048
5057
  ".ts": "typescript",
@@ -5375,144 +5384,6 @@ function handleBlastPoints(args) {
5375
5384
  }
5376
5385
  });
5377
5386
  }
5378
- function handleGenerateBlastRadius(args) {
5379
- const rootDir = process.cwd();
5380
- const mode = args.mode ?? "structural";
5381
- const title = args.title;
5382
- const description = args.description ?? title;
5383
- const subtitle = args.subtitle;
5384
- const hops = args.hops ?? 2;
5385
- const defaults = loadDefaults(rootDir);
5386
- let centerNodeIds = [];
5387
- if (mode === "structural") {
5388
- const nodeId = args.node_id;
5389
- if (!nodeId) return err("structural mode requires node_id");
5390
- centerNodeIds = [nodeId];
5391
- } else {
5392
- centerNodeIds = args.center_nodes ?? [];
5393
- if (centerNodeIds.length === 0) return err("feature mode requires center_nodes[]");
5394
- }
5395
- const createNodes = args.create_nodes ?? [];
5396
- const blastResults = [];
5397
- for (const nodeId of centerNodeIds) {
5398
- let targetLayer;
5399
- const graphs = readAllGraphs(rootDir);
5400
- for (const [layer, graph2] of Object.entries(graphs)) {
5401
- if (graph2 && graph2.nodes.some((n) => n.id === nodeId)) {
5402
- targetLayer = layer;
5403
- break;
5404
- }
5405
- }
5406
- if (!targetLayer) continue;
5407
- const graph = readGraph(rootDir, targetLayer);
5408
- if (!graph) continue;
5409
- const center = graph.nodes.find((n) => n.id === nodeId);
5410
- if (!center) continue;
5411
- const result2 = reverseNeighborhood(graph, nodeId, hops, "reverse");
5412
- const affected = [];
5413
- for (const [id, { node, hop }] of result2.nodes) {
5414
- if (id === nodeId) continue;
5415
- const tags = node.tags;
5416
- affected.push({ id: node.id, name: node.name, type: node.type, layer: targetLayer, hop, module: tags?.module });
5417
- }
5418
- const otherLayers = getAvailableLayers(rootDir).filter((l) => l !== targetLayer && l !== "static");
5419
- for (const otherLayer of otherLayers) {
5420
- const otherGraph = readGraph(rootDir, otherLayer);
5421
- if (!otherGraph) continue;
5422
- for (const edge of otherGraph.edges) {
5423
- if (edge.target === nodeId || edge.source === nodeId) {
5424
- const dependentId = edge.target === nodeId ? edge.source : edge.target;
5425
- if (affected.some((a) => a.id === dependentId)) continue;
5426
- const depNode = otherGraph.nodes.find((n) => n.id === dependentId);
5427
- if (depNode) {
5428
- const tags = depNode.tags;
5429
- affected.push({ id: depNode.id, name: depNode.name, type: depNode.type, layer: otherLayer, hop: 1, module: tags?.module });
5430
- }
5431
- }
5432
- }
5433
- }
5434
- const centerTags = center.tags;
5435
- const edges = result2.edges.map((e) => ({ source: e.source, target: e.target }));
5436
- blastResults.push({
5437
- center: { id: center.id, name: center.name, type: center.type, layer: targetLayer, module: centerTags?.module },
5438
- affected,
5439
- edges
5440
- });
5441
- }
5442
- if (blastResults.length === 0) {
5443
- return err(`None of the center nodes were found in any graph layer: ${centerNodeIds.join(", ")}`);
5444
- }
5445
- const inspectData = {};
5446
- const allAffectedIds = /* @__PURE__ */ new Set();
5447
- for (const r of blastResults) {
5448
- allAffectedIds.add(r.center.id);
5449
- for (const a of r.affected) allAffectedIds.add(a.id);
5450
- }
5451
- const allGraphs = readAllGraphs(rootDir);
5452
- for (const id of allAffectedIds) {
5453
- for (const [, graph] of Object.entries(allGraphs)) {
5454
- if (!graph) continue;
5455
- const node = graph.nodes.find((n) => n.id === id);
5456
- if (node) {
5457
- inspectData[id] = {
5458
- type: node.type,
5459
- name: node.name,
5460
- methods: node.methods,
5461
- path: node.path ?? node.handler,
5462
- auth: node.auth,
5463
- db_models: node.db_models
5464
- };
5465
- break;
5466
- }
5467
- }
5468
- }
5469
- const manifest = buildManifest({
5470
- mode,
5471
- title,
5472
- description,
5473
- subtitle,
5474
- blastResults,
5475
- createNodes,
5476
- inspectData,
5477
- defaults
5478
- });
5479
- const pushToDeck = args.push_to_deck;
5480
- const session = args.session;
5481
- let deckResult;
5482
- if (pushToDeck) {
5483
- if (!session) return err("push_to_deck requires a session name");
5484
- const deckLockPath = (0, import_node_path22.join)(rootDir, ".launchsecure", "launch-deck.lock");
5485
- if (!(0, import_node_fs20.existsSync)(deckLockPath)) {
5486
- deckResult = { pushed: false, reason: "Deck server not running (no lock file). Push manually via deck tool." };
5487
- } else {
5488
- try {
5489
- const lock = JSON.parse((0, import_node_fs20.readFileSync)(deckLockPath, "utf-8"));
5490
- const deckUrl = lock.url;
5491
- const body = JSON.stringify({
5492
- session,
5493
- mode: "show",
5494
- blocks: [{ type: "blast-radius", label: title, manifest }]
5495
- });
5496
- (0, import_node_child_process2.execFileSync)("curl", [
5497
- "-s",
5498
- "-X",
5499
- "POST",
5500
- deckUrl + "/api/deck",
5501
- "-H",
5502
- "Content-Type: application/json",
5503
- "-d",
5504
- body
5505
- ], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] });
5506
- deckResult = { pushed: true, session, url: deckUrl };
5507
- } catch (e) {
5508
- deckResult = { pushed: false, reason: `Failed to push to deck: ${e}` };
5509
- }
5510
- }
5511
- }
5512
- const result = { ...manifest };
5513
- if (deckResult) result._deck = deckResult;
5514
- return okJson(result);
5515
- }
5516
5387
  function layerSummary(graph) {
5517
5388
  const typeCounts = {};
5518
5389
  const moduleCounts = {};
@@ -5741,11 +5612,11 @@ function handleReadGraph(args) {
5741
5612
  return okJson(result);
5742
5613
  }
5743
5614
  function nodeToFilePath(rootDir, layer, nodeId) {
5744
- if (layer === "ui" || layer === "api") return (0, import_node_path22.join)(rootDir, "src", nodeId);
5745
- if (layer === "db") return (0, import_node_path22.join)(rootDir, "prisma", "schema.prisma");
5746
- const withSrc = (0, import_node_path22.join)(rootDir, "src", nodeId);
5615
+ if (layer === "ui" || layer === "api") return (0, import_node_path21.join)(rootDir, "src", nodeId);
5616
+ if (layer === "db") return (0, import_node_path21.join)(rootDir, "prisma", "schema.prisma");
5617
+ const withSrc = (0, import_node_path21.join)(rootDir, "src", nodeId);
5747
5618
  if ((0, import_node_fs20.existsSync)(withSrc)) return withSrc;
5748
- const direct = (0, import_node_path22.join)(rootDir, nodeId);
5619
+ const direct = (0, import_node_path21.join)(rootDir, nodeId);
5749
5620
  if ((0, import_node_fs20.existsSync)(direct)) return direct;
5750
5621
  return null;
5751
5622
  }
@@ -5958,9 +5829,9 @@ function handleStartChartServer(args) {
5958
5829
  });
5959
5830
  }
5960
5831
  const entryPath = process.argv[1];
5961
- const logDir = (0, import_node_path22.join)((0, import_node_os2.homedir)(), ".launchsecure");
5832
+ const logDir = (0, import_node_path21.join)((0, import_node_os2.homedir)(), ".launchsecure");
5962
5833
  (0, import_node_fs20.mkdirSync)(logDir, { recursive: true });
5963
- const logPath = (0, import_node_path22.join)(logDir, "launch-chart.log");
5834
+ const logPath = (0, import_node_path21.join)(logDir, "launch-chart.log");
5964
5835
  const out = (0, import_node_fs20.openSync)(logPath, "a");
5965
5836
  const err2 = (0, import_node_fs20.openSync)(logPath, "a");
5966
5837
  const portArgs = args.port ? ["--port", String(args.port)] : [];
@@ -6082,18 +5953,18 @@ function handleDetectProjectStack() {
6082
5953
  if (ref.type === "references_api") stats.references_api++;
6083
5954
  }
6084
5955
  }
6085
- const srcDir = (0, import_node_path22.join)(rootDir, "src");
5956
+ const srcDir = (0, import_node_path21.join)(rootDir, "src");
6086
5957
  if ((0, import_node_fs20.existsSync)(srcDir)) {
6087
5958
  const scanDir = (dir) => {
6088
5959
  if (!(0, import_node_fs20.existsSync)(dir)) return;
6089
5960
  for (const entry of (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true })) {
6090
5961
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
6091
- const full = (0, import_node_path22.join)(dir, entry.name);
5962
+ const full = (0, import_node_path21.join)(dir, entry.name);
6092
5963
  if (entry.isDirectory()) {
6093
5964
  scanDir(full);
6094
5965
  continue;
6095
5966
  }
6096
- if (![".ts", ".tsx"].includes((0, import_node_path22.extname)(entry.name))) continue;
5967
+ if (![".ts", ".tsx"].includes((0, import_node_path21.extname)(entry.name))) continue;
6097
5968
  try {
6098
5969
  const content = (0, import_node_fs20.readFileSync)(full, "utf-8");
6099
5970
  const matches = content.match(/@api\s+(GET|POST|PUT|DELETE|PATCH)\s+\/\S+/g);
@@ -6214,10 +6085,6 @@ async function handleMessage(msg) {
6214
6085
  respond(id ?? null, handleBlastPoints(args));
6215
6086
  return;
6216
6087
  }
6217
- if (toolName === "generate_blast_radius") {
6218
- respond(id ?? null, handleGenerateBlastRadius(args));
6219
- return;
6220
- }
6221
6088
  respondError(id ?? null, -32601, `Unknown tool: ${toolName}`);
6222
6089
  return;
6223
6090
  }
@@ -6253,16 +6120,15 @@ function startGraphMcpServer() {
6253
6120
  process.stderr.write(`[launchsecure-graph] MCP server started (cwd: ${process.cwd()})
6254
6121
  `);
6255
6122
  }
6256
- var import_node_fs20, import_node_path22, import_node_child_process2, import_node_os2, SERVER_INFO, TOOLS, COMPACT_SCHEMA, COMPACT_NODE_KNOWN_KEYS, DEEP_FIELDS, EST_CHARS_PER_NODE_FULL, EST_CHARS_PER_NODE_MIN, EST_CHARS_PER_EDGE, DEFAULT_EST_NODE_FULL, DEFAULT_EST_NODE_MIN, DEFAULT_EST_EDGE, NEIGHBORHOOD_BUDGET_CHARS, BATCH_BUDGET_CHARS;
6123
+ var import_node_fs20, import_node_path21, import_node_child_process2, import_node_os2, SERVER_INFO, TOOLS, COMPACT_SCHEMA, COMPACT_NODE_KNOWN_KEYS, DEEP_FIELDS, EST_CHARS_PER_NODE_FULL, EST_CHARS_PER_NODE_MIN, EST_CHARS_PER_EDGE, DEFAULT_EST_NODE_FULL, DEFAULT_EST_NODE_MIN, DEFAULT_EST_EDGE, NEIGHBORHOOD_BUDGET_CHARS, BATCH_BUDGET_CHARS;
6257
6124
  var init_graph_mcp = __esm({
6258
6125
  "src/server/graph-mcp.ts"() {
6259
6126
  "use strict";
6260
6127
  import_node_fs20 = require("node:fs");
6261
- import_node_path22 = require("node:path");
6128
+ import_node_path21 = require("node:path");
6262
6129
  import_node_child_process2 = require("node:child_process");
6263
6130
  import_node_os2 = require("node:os");
6264
6131
  init_graph();
6265
- init_blast_radius_builder();
6266
6132
  init_lockfile();
6267
6133
  init_config();
6268
6134
  init_parser_registry();
@@ -6579,81 +6445,6 @@ Example: blast_points(node_id: "server/auth/middleware.ts", hops: 2) \u2192 retu
6579
6445
  },
6580
6446
  required: ["node_id"]
6581
6447
  }
6582
- },
6583
- {
6584
- name: "generate_blast_radius",
6585
- description: `Generate a complete BlastRadiusManifest from graph data \u2014 ready to push to deck.
6586
-
6587
- Two modes:
6588
- - **Structural**: single node changed \u2192 auto-discover what's affected via reverse BFS
6589
- Example: generate_blast_radius({ mode: "structural", node_id: "CommentChannel", title: "CommentChannel refactor" })
6590
- - **Feature**: new feature \u2192 multiple starting nodes + new nodes to create
6591
- Example: generate_blast_radius({ mode: "feature", title: "Client Role", description: "...", center_nodes: ["CommentChannel", "ProjectMember"], create_nodes: [{ id: "ChannelMember", name: "ChannelMember table", layer: "db", reason: "..." }] })
6592
-
6593
- Output is a BlastRadiusManifest JSON that passes directly to the deck tool's blast-radius block.
6594
- Reads ring/layer/center colors from .launchsecure/blast-radius-defaults.json.
6595
- Auto-generates acceptance criteria per node using inspect_node AST data.`,
6596
- inputSchema: {
6597
- type: "object",
6598
- properties: {
6599
- mode: {
6600
- type: "string",
6601
- enum: ["structural", "feature"],
6602
- description: '"structural" = single node changed. "feature" = new feature with multiple nodes.'
6603
- },
6604
- title: {
6605
- type: "string",
6606
- description: "Title for the blast radius (shown in center node and header)."
6607
- },
6608
- description: {
6609
- type: "string",
6610
- description: "Description of the change or feature."
6611
- },
6612
- subtitle: {
6613
- type: "string",
6614
- description: "Optional subtitle shown above title in the viz."
6615
- },
6616
- node_id: {
6617
- type: "string",
6618
- description: "Structural mode only: the node being changed."
6619
- },
6620
- center_nodes: {
6621
- type: "array",
6622
- items: { type: "string" },
6623
- description: "Feature mode: existing graph node IDs that are the starting points for traversal."
6624
- },
6625
- create_nodes: {
6626
- type: "array",
6627
- items: {
6628
- type: "object",
6629
- properties: {
6630
- id: { type: "string" },
6631
- name: { type: "string" },
6632
- layer: { type: "string" },
6633
- type: { type: "string" },
6634
- reason: { type: "string" },
6635
- acceptance: { type: "array", items: { type: "string" } },
6636
- connects_to: { type: "array", items: { type: "string" }, description: "IDs of existing nodes this new node has FK/relationship edges to." }
6637
- },
6638
- required: ["id", "name", "layer", "reason"]
6639
- },
6640
- description: "Feature mode: new nodes that need to be created (not in graph yet)."
6641
- },
6642
- hops: {
6643
- type: "number",
6644
- description: "Max hops for traversal. Default 2. Hop 1 = modify ring, hop 2+ = ripple ring."
6645
- },
6646
- push_to_deck: {
6647
- type: "boolean",
6648
- description: "If true, pushes the manifest directly to LaunchDeck browser (requires deck server running). Default false."
6649
- },
6650
- session: {
6651
- type: "string",
6652
- description: "Session name for the deck tab. Required when push_to_deck is true."
6653
- }
6654
- },
6655
- required: ["title"]
6656
- }
6657
6448
  }
6658
6449
  ];
6659
6450
  COMPACT_SCHEMA = {
@@ -6721,7 +6512,7 @@ Auto-generates acceptance criteria per node using inspect_node AST data.`,
6721
6512
  // src/server/graph-mcp-entry.ts
6722
6513
  var import_node_child_process3 = require("node:child_process");
6723
6514
  var import_node_fs21 = require("node:fs");
6724
- var import_node_path23 = __toESM(require("node:path"));
6515
+ var import_node_path22 = __toESM(require("node:path"));
6725
6516
  var import_node_os3 = require("node:os");
6726
6517
  var import_node_fs22 = require("node:fs");
6727
6518
  init_lockfile();
@@ -6739,9 +6530,9 @@ function maybeAutoServe() {
6739
6530
  return;
6740
6531
  }
6741
6532
  try {
6742
- const logDir = import_node_path23.default.join((0, import_node_os3.homedir)(), ".launchsecure");
6533
+ const logDir = import_node_path22.default.join((0, import_node_os3.homedir)(), ".launchsecure");
6743
6534
  (0, import_node_fs22.mkdirSync)(logDir, { recursive: true });
6744
- const logPath = import_node_path23.default.join(logDir, "launch-chart.log");
6535
+ const logPath = import_node_path22.default.join(logDir, "launch-chart.log");
6745
6536
  const out = (0, import_node_fs21.openSync)(logPath, "a");
6746
6537
  const err2 = (0, import_node_fs21.openSync)(logPath, "a");
6747
6538
  const entryPath = process.argv[1];