@massu/core 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -11159,1239 +11159,43 @@ var init_python_flask = __esm({
11159
11159
  }
11160
11160
  });
11161
11161
 
11162
- // dist/adapter.js
11163
- import { Query as Query2 } from "web-tree-sitter";
11164
- import { createHash as createHash3 } from "crypto";
11165
- import {
11166
- mkdirSync as mkdirSync4,
11167
- readdirSync as readdirSync9,
11168
- readFileSync as readFileSync9,
11169
- writeFileSync as writeFileSync2,
11170
- renameSync as renameSync3,
11171
- unlinkSync as unlinkSync2,
11172
- lstatSync as lstatSync4,
11173
- chmodSync as chmodSync2,
11174
- utimesSync as utimesSync2
11175
- } from "fs";
11176
- import { homedir as homedir3 } from "os";
11177
- import { dirname as dirname5, join as join9 } from "path";
11178
- import { Language as Language2, Parser as Parser7 } from "web-tree-sitter";
11179
- function compileQuery2(language, source, queryName) {
11180
- let perLang = queryCache2.get(language);
11181
- if (!perLang) {
11182
- perLang = /* @__PURE__ */ new Map();
11183
- queryCache2.set(language, perLang);
11184
- }
11185
- const cached = perLang.get(source);
11186
- if (cached) return cached;
11187
- let q2;
11188
- try {
11189
- q2 = new Query2(language, source);
11190
- } catch (e2) {
11191
- throw new InvalidQueryError2(queryName, source, e2);
11192
- }
11193
- perLang.set(source, q2);
11194
- return q2;
11195
- }
11196
- function runQuery2(parser, source, queryText, queryName, filePath) {
11197
- const language = parser.language;
11198
- if (!language) {
11199
- throw new InvalidQueryError2(
11200
- queryName,
11201
- queryText,
11202
- new Error("Parser has no language assigned")
11203
- );
11204
- }
11205
- const query = compileQuery2(language, queryText, queryName);
11206
- const tree = parser.parse(source);
11207
- if (!tree) return [];
11208
- let matches;
11209
- try {
11210
- matches = query.matches(tree.rootNode);
11211
- } catch (e2) {
11212
- throw new InvalidQueryError2(queryName, queryText, e2);
11213
- }
11214
- const out = [];
11215
- for (const match of matches) {
11216
- if (!match.captures || match.captures.length === 0) continue;
11217
- const captures = {};
11218
- let earliestLine = Number.POSITIVE_INFINITY;
11219
- for (const cap of match.captures) {
11220
- const node = cap.node;
11221
- captures[cap.name] = node.text;
11222
- if (node.startPosition.row + 1 < earliestLine) {
11223
- earliestLine = node.startPosition.row + 1;
11224
- }
11225
- }
11226
- out.push({
11227
- captures,
11228
- file: filePath,
11229
- line: Number.isFinite(earliestLine) ? earliestLine : 1,
11230
- queryName
11231
- });
11232
- }
11233
- try {
11234
- tree.delete();
11235
- } catch {
11236
- }
11237
- return out;
11238
- }
11239
- function getCacheDir2() {
11240
- return process.env.MASSU_WASM_CACHE_DIR ?? join9(homedir3(), ".massu", "wasm-cache");
11241
- }
11242
- function getCachedPath2(language, sha) {
11243
- return join9(getCacheDir2(), `${language}-${sha}.wasm`);
11244
- }
11245
- function getCacheRetainCount2() {
11246
- const env = process.env.MASSU_WASM_CACHE_RETAIN;
11247
- if (env) {
11248
- const n = Number(env);
11249
- if (Number.isFinite(n) && n >= 1 && n <= 1024) return Math.floor(n);
11250
- }
11251
- return DEFAULT_CACHE_RETAIN_COUNT2;
11252
- }
11253
- function touchCacheFile2(path) {
11254
- try {
11255
- const now = /* @__PURE__ */ new Date();
11256
- utimesSync2(path, now, now);
11257
- } catch {
11258
- }
11259
- }
11260
- function evictBeyondRetainCount2(retain = getCacheRetainCount2()) {
11261
- const dir = getCacheDir2();
11262
- let entries;
11263
- try {
11264
- entries = readdirSync9(dir);
11265
- } catch {
11266
- return;
11267
- }
11268
- const candidates = [];
11269
- for (const name of entries) {
11270
- if (!name.endsWith(".wasm")) continue;
11271
- const path = join9(dir, name);
11272
- let stat;
11273
- try {
11274
- stat = lstatSync4(path);
11275
- } catch {
11276
- continue;
11277
- }
11278
- if (stat.isSymbolicLink() || !stat.isFile()) {
11279
- console.error(
11280
- `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).`
11281
- );
11282
- continue;
11283
- }
11284
- candidates.push({ path, mtimeMs: stat.mtimeMs });
11285
- }
11286
- if (candidates.length <= retain) return;
11287
- candidates.sort((a2, b2) => b2.mtimeMs - a2.mtimeMs);
11288
- for (const victim of candidates.slice(retain)) {
11289
- try {
11290
- unlinkSync2(victim.path);
11291
- } catch {
11292
- }
11293
- }
11294
- }
11295
- function sha2562(bytes) {
11296
- return createHash3("sha256").update(bytes).digest("hex");
11297
- }
11298
- async function ensureParserInitialized2() {
11299
- if (parserInitPromise2) return parserInitPromise2;
11300
- parserInitPromise2 = Parser7.init();
11301
- return parserInitPromise2;
11302
- }
11303
- async function loadGrammar2(language, options = {}) {
11304
- await ensureParserInitialized2();
11305
- const cached = loadedGrammars2.get(language);
11306
- if (cached) return cached;
11307
- const manifest = options.manifestOverride?.[language] ?? GRAMMAR_MANIFEST2[language];
11308
- if (!manifest) {
11309
- throw new GrammarUnavailableError2(
11310
- language,
11311
- new Error(`No manifest entry for language "${language}". v1 supports: ${Object.keys(GRAMMAR_MANIFEST2).join(", ")}.`)
11312
- );
11313
- }
11314
- const cachePath = getCachedPath2(language, manifest.sha256);
11315
- let cacheLstat;
11316
- try {
11317
- cacheLstat = lstatSync4(cachePath);
11318
- } catch {
11319
- cacheLstat = null;
11320
- }
11321
- if (cacheLstat) {
11322
- if (cacheLstat.isSymbolicLink() || !cacheLstat.isFile()) {
11323
- throw new GrammarCacheSymlinkError2(cachePath);
11324
- }
11325
- let bytes;
11326
- try {
11327
- bytes = readFileSync9(cachePath);
11328
- } catch (e2) {
11329
- bytes = new Uint8Array(0);
11330
- }
11331
- if (bytes.byteLength > 0) {
11332
- const actualSha = sha2562(bytes);
11333
- if (actualSha !== manifest.sha256) {
11334
- throw new GrammarSHAMismatchError2(language, manifest.sha256, actualSha);
11335
- }
11336
- const lang2 = await Language2.load(bytes);
11337
- loadedGrammars2.set(language, lang2);
11338
- touchCacheFile2(cachePath);
11339
- return lang2;
11340
- }
11341
- }
11342
- if (!/^https:\/\//i.test(manifest.url)) {
11343
- throw new GrammarUrlNotHttpsError2(manifest.url);
11344
- }
11345
- const fetchImpl = options.fetchImpl ?? globalThis.fetch;
11346
- if (!fetchImpl) {
11347
- throw new GrammarUnavailableError2(
11348
- language,
11349
- new Error("No fetch implementation available (Node < 18?)")
11350
- );
11351
- }
11352
- let body;
11353
- try {
11354
- const res = await fetchImpl(manifest.url);
11355
- if (!res.ok) {
11356
- throw new Error(`HTTP ${res.status ?? "unknown"} from ${manifest.url}`);
11357
- }
11358
- body = new Uint8Array(await res.arrayBuffer());
11359
- } catch (e2) {
11360
- throw new GrammarUnavailableError2(language, e2);
11361
- }
11362
- const downloadedSha = sha2562(body);
11363
- if (downloadedSha !== manifest.sha256) {
11364
- throw new GrammarSHAMismatchError2(language, manifest.sha256, downloadedSha);
11365
- }
11366
- try {
11367
- mkdirSync4(dirname5(cachePath), { recursive: true, mode: 448 });
11368
- try {
11369
- chmodSync2(dirname5(cachePath), 448);
11370
- } catch {
11371
- }
11372
- const tmpPath = `${cachePath}.tmp.${process.pid}`;
11373
- writeFileSync2(tmpPath, body, { mode: 384 });
11374
- try {
11375
- chmodSync2(tmpPath, 384);
11376
- } catch {
11377
- }
11378
- try {
11379
- renameSync3(tmpPath, cachePath);
11380
- try {
11381
- chmodSync2(cachePath, 384);
11382
- } catch {
11383
- }
11384
- } catch (e2) {
11385
- try {
11386
- unlinkSync2(tmpPath);
11387
- } catch {
11388
- }
11389
- throw e2;
11390
- }
11391
- evictBeyondRetainCount2();
11392
- } catch (e2) {
11393
- console.error(
11394
- `[tree-sitter-loader] cache write failed for ${language}: ${e2 instanceof Error ? e2.message : String(e2)} \u2014 loading directly from memory.`
11395
- );
11396
- }
11397
- const lang = await Language2.load(body);
11398
- loadedGrammars2.set(language, lang);
11399
- return lang;
11400
- }
11401
- function isParsableSource2(source, sizeBytes) {
11402
- const bytes = sizeBytes ?? Buffer.byteLength(source, "utf-8");
11403
- if (bytes > MAX_AST_FILE_BYTES2) {
11404
- return {
11405
- reason: "size-cap",
11406
- detail: `${bytes} bytes > ${MAX_AST_FILE_BYTES2} cap`
11407
- };
11408
- }
11409
- let depth = 0;
11410
- let maxDepth = 0;
11411
- for (let i = 0; i < source.length; i++) {
11412
- const c2 = source.charCodeAt(i);
11413
- if (c2 === 0) {
11414
- return { reason: "control-bytes", detail: "NUL byte at offset " + i };
11415
- }
11416
- if (c2 === 40 || c2 === 91 || c2 === 123) {
11417
- depth++;
11418
- if (depth > maxDepth) maxDepth = depth;
11419
- if (depth > MAX_AST_PARSE_DEPTH2) {
11420
- return {
11421
- reason: "depth-cap",
11422
- detail: `nesting depth exceeded ${MAX_AST_PARSE_DEPTH2}`
11423
- };
11424
- }
11425
- } else if (c2 === 41 || c2 === 93 || c2 === 125) {
11426
- depth = depth > 0 ? depth - 1 : 0;
11427
- }
11428
- }
11429
- return null;
11430
- }
11431
- var InvalidQueryError2, queryCache2, GrammarSHAMismatchError2, GrammarUnavailableError2, GrammarCacheSymlinkError2, GrammarUrlNotHttpsError2, GRAMMAR_MANIFEST2, DEFAULT_CACHE_RETAIN_COUNT2, parserInitPromise2, loadedGrammars2, MAX_AST_FILE_BYTES2, MAX_AST_PARSE_DEPTH2;
11432
- var init_adapter = __esm({
11433
- "dist/adapter.js"() {
11434
- "use strict";
11435
- InvalidQueryError2 = class extends Error {
11436
- queryName;
11437
- querySource;
11438
- cause;
11439
- constructor(queryName, querySource, cause) {
11440
- const causeMsg = cause instanceof Error ? cause.message : String(cause);
11441
- super(
11442
- `[query-helpers] Invalid Tree-sitter query "${queryName}": ${causeMsg}
11443
- Query source:
11444
- ${querySource}`
11445
- );
11446
- this.name = "InvalidQueryError";
11447
- this.queryName = queryName;
11448
- this.querySource = querySource;
11449
- this.cause = cause;
11450
- }
11451
- };
11452
- queryCache2 = /* @__PURE__ */ new WeakMap();
11453
- GrammarSHAMismatchError2 = class extends Error {
11454
- language;
11455
- expected;
11456
- actual;
11457
- constructor(language, expected, actual) {
11458
- super(
11459
- `[tree-sitter-loader] SHA-256 mismatch for grammar "${language}". Expected ${expected}, got ${actual}. REFUSING to load \u2014 see Phase 3.5 audit attack vector #3.`
11460
- );
11461
- this.name = "GrammarSHAMismatchError";
11462
- this.language = language;
11463
- this.expected = expected;
11464
- this.actual = actual;
11465
- }
11466
- };
11467
- GrammarUnavailableError2 = class extends Error {
11468
- language;
11469
- cause;
11470
- constructor(language, cause) {
11471
- const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : "no cached grammar and download failed";
11472
- super(
11473
- `[tree-sitter-loader] Grammar for "${language}" is unavailable: ${causeMsg}. Falling back to regex introspection for files in ${language}.`
11474
- );
11475
- this.name = "GrammarUnavailableError";
11476
- this.language = language;
11477
- this.cause = cause;
11478
- }
11479
- };
11480
- GrammarCacheSymlinkError2 = class extends Error {
11481
- cachePath;
11482
- constructor(cachePath) {
11483
- super(
11484
- `[tree-sitter-loader] Refusing to load grammar \u2014 cache path "${cachePath}" is a symlink or non-regular file. (Phase 3.5 finding #3 \u2014 symlink attack vector.)`
11485
- );
11486
- this.name = "GrammarCacheSymlinkError";
11487
- this.cachePath = cachePath;
11488
- }
11489
- };
11490
- GrammarUrlNotHttpsError2 = class extends Error {
11491
- url;
11492
- constructor(url) {
11493
- super(
11494
- `[tree-sitter-loader] Refusing to download grammar from non-HTTPS URL: ${url}. Only https:// URLs are accepted. (Phase 3.5 finding #3.)`
11495
- );
11496
- this.name = "GrammarUrlNotHttpsError";
11497
- this.url = url;
11498
- }
11499
- };
11500
- GRAMMAR_MANIFEST2 = {
11501
- python: {
11502
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-python.wasm",
11503
- sha256: "9056d0fb0c337810d019fae350e8167786119da98f0f282aceae7ab89ee8253b",
11504
- version: "0.1.13"
11505
- },
11506
- typescript: {
11507
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-typescript.wasm",
11508
- sha256: "8515404dceed38e1ed86aa34b09fcf3379fff1b4ff9dd3967bcd6d1eb5ac3d8f",
11509
- version: "0.1.13"
11510
- },
11511
- javascript: {
11512
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-javascript.wasm",
11513
- sha256: "63812b9e275d26851264734868d27a1656bd44a2ef6eb3e85e6b03728c595ab5",
11514
- version: "0.1.13"
11515
- },
11516
- swift: {
11517
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-swift.wasm",
11518
- sha256: "41c4fdb2249a3aa6d87eed0d383081ff09725c2248b4977043a43825980ffcc7",
11519
- version: "0.1.13"
11520
- },
11521
- // ----------------------------------------------------------------
11522
- // Plan 3c Phase 7 expansion (2026-05-07):
11523
- //
11524
- // Six additional grammars to support the registry-verified framework
11525
- // adapters (go-chi, rails, aspnet, spring, ktor, phoenix) plus the
11526
- // bundled adapters in the same language families (gin/echo/fiber,
11527
- // sinatra, etc.). All entries use the SAME pinned tree-sitter-wasms
11528
- // version (0.1.13) as the v1 four to keep the dependency surface
11529
- // single-source.
11530
- //
11531
- // SHA-256s computed 2026-05-07 via:
11532
- // curl -fsSL <url> | shasum -a 256
11533
- //
11534
- // The unpkg filename for C# uses an underscore (`c_sharp`) while the
11535
- // TreeSitterLanguage identifier uses no separator (`csharp`); the map
11536
- // key is the type identifier, the URL is the storage path — they do
11537
- // NOT need to match, the same as how `python` maps to `tree-sitter-
11538
- // python.wasm`. This is intentional and validated by the manifest
11539
- // shape test in tree-sitter-loader-manifest.test.ts.
11540
- // ----------------------------------------------------------------
11541
- go: {
11542
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-go.wasm",
11543
- sha256: "9963ca89b616eaf04b08a43bc1fb0f07b85395bec313330851f1f1ead2f755b6",
11544
- version: "0.1.13"
11545
- },
11546
- ruby: {
11547
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-ruby.wasm",
11548
- sha256: "93a5022855314cdb45458c7bb026a24a0ebc3a5ff6439e542e881f14dfa13a39",
11549
- version: "0.1.13"
11550
- },
11551
- csharp: {
11552
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-c_sharp.wasm",
11553
- sha256: "6266a7e32d68a3459104d994dc848df15d5672b0ea8e86d327274b694f8e6991",
11554
- version: "0.1.13"
11555
- },
11556
- java: {
11557
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-java.wasm",
11558
- sha256: "637aac4415fb39a211a4f4292d63c66b5ce9c32fa2cd35464af4f681d91b9a1f",
11559
- version: "0.1.13"
11560
- },
11561
- kotlin: {
11562
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-kotlin.wasm",
11563
- sha256: "b5cb00c8d06ed0f10f1dbe497205b437809d7e87db1f638721a8cfb30e044449",
11564
- version: "0.1.13"
11565
- },
11566
- elixir: {
11567
- url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-elixir.wasm",
11568
- sha256: "82e91b9759ddca30d8978ebbfa8e347b4451b64c931f9ae62112e6db9b8fac20",
11569
- version: "0.1.13"
11570
- }
11571
- };
11572
- DEFAULT_CACHE_RETAIN_COUNT2 = 16;
11573
- parserInitPromise2 = null;
11574
- loadedGrammars2 = /* @__PURE__ */ new Map();
11575
- MAX_AST_FILE_BYTES2 = 1 * 1024 * 1024;
11576
- MAX_AST_PARSE_DEPTH2 = 5e3;
11577
- }
11578
- });
11579
-
11580
- // ../adapter-go-chi/dist/index.js
11581
- import { Parser as Parser8 } from "web-tree-sitter";
11582
- function extractPrefixBase4(prefix3) {
11583
- if (!prefix3.startsWith("/"))
11584
- return null;
11585
- const stripped = prefix3.replace(/^\/+/, "");
11586
- const firstSeg = stripped.split("/")[0];
11587
- if (!firstSeg)
11588
- return null;
11589
- return "/" + firstSeg;
11590
- }
11591
- var ROUTE_METHOD_QUERY, MOUNT_PREFIX_QUERY, MIDDLEWARE_USE_QUERY, goChiAdapter;
11592
- var init_dist = __esm({
11593
- "../adapter-go-chi/dist/index.js"() {
11594
- "use strict";
11595
- init_adapter();
11596
- ROUTE_METHOD_QUERY = `
11597
- (call_expression
11598
- function: (selector_expression
11599
- field: (field_identifier) @method (#match? @method "^(Get|Post|Put|Delete|Patch|Head|Options|Connect|Trace)$"))
11600
- arguments: (argument_list
11601
- .
11602
- (interpreted_string_literal) @route_path))
11603
- `;
11604
- MOUNT_PREFIX_QUERY = `
11605
- (call_expression
11606
- function: (selector_expression
11607
- field: (field_identifier) @_field (#eq? @_field "Mount"))
11608
- arguments: (argument_list
11609
- .
11610
- (interpreted_string_literal) @mount_path))
11611
- `;
11612
- MIDDLEWARE_USE_QUERY = `
11613
- (call_expression
11614
- function: (selector_expression
11615
- field: (field_identifier) @_use (#eq? @_use "Use"))
11616
- arguments: (argument_list
11617
- .
11618
- (selector_expression
11619
- operand: (identifier) @_pkg (#eq? @_pkg "middleware")
11620
- field: (field_identifier) @middleware_name)))
11621
- `;
11622
- goChiAdapter = {
11623
- id: "go-chi",
11624
- languages: ["go"],
11625
- matches(signals) {
11626
- if (!signals.goMod)
11627
- return false;
11628
- if (/github\.com\/go-chi\/chi/i.test(signals.goMod))
11629
- return true;
11630
- return false;
11631
- },
11632
- async introspect(files, _rootDir) {
11633
- if (files.length === 0) {
11634
- return { conventions: {}, provenance: [], confidence: "none" };
11635
- }
11636
- let language;
11637
- try {
11638
- language = await loadGrammar2("go");
11639
- } catch (e2) {
11640
- return { conventions: {}, provenance: [], confidence: "none" };
11641
- }
11642
- const parser = new Parser8();
11643
- parser.setLanguage(language);
11644
- const routeMethods = /* @__PURE__ */ new Map();
11645
- const mountBases = /* @__PURE__ */ new Map();
11646
- const middlewareNames = /* @__PURE__ */ new Map();
11647
- try {
11648
- for (const file of files) {
11649
- const skip = isParsableSource2(file.content, file.size);
11650
- if (skip) {
11651
- process.stderr.write(`[massu/ast] WARN: go-chi skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES2}. (Phase 3.5 mitigation)
11652
- `);
11653
- continue;
11654
- }
11655
- try {
11656
- for (const hit of runQuery2(parser, file.content, ROUTE_METHOD_QUERY, "chi-route-method", file.path)) {
11657
- const method = hit.captures.method;
11658
- if (method && !routeMethods.has(method)) {
11659
- routeMethods.set(method, { line: hit.line, file: file.path });
11660
- }
11661
- }
11662
- for (const hit of runQuery2(parser, file.content, MOUNT_PREFIX_QUERY, "chi-mount-prefix", file.path)) {
11663
- const raw = hit.captures.mount_path;
11664
- if (!raw)
11665
- continue;
11666
- const literal = raw.replace(/^["`]/, "").replace(/["`]$/, "");
11667
- const base = extractPrefixBase4(literal);
11668
- if (base && !mountBases.has(base)) {
11669
- mountBases.set(base, { line: hit.line, file: file.path });
11670
- }
11671
- }
11672
- for (const hit of runQuery2(parser, file.content, MIDDLEWARE_USE_QUERY, "chi-middleware-use", file.path)) {
11673
- const name = hit.captures.middleware_name;
11674
- if (name && !middlewareNames.has(name)) {
11675
- middlewareNames.set(name, { line: hit.line, file: file.path });
11676
- }
11677
- }
11678
- } catch (e2) {
11679
- if (e2 instanceof InvalidQueryError2) {
11680
- throw e2;
11681
- }
11682
- continue;
11683
- }
11684
- }
11685
- } finally {
11686
- try {
11687
- parser.delete();
11688
- } catch {
11689
- }
11690
- }
11691
- const conventions = {};
11692
- const provenance = [];
11693
- if (routeMethods.size === 1) {
11694
- const [name, { line, file }] = routeMethods.entries().next().value;
11695
- conventions.route_method = name;
11696
- provenance.push({ field: "route_method", sourceFile: file, line, query: "chi-route-method" });
11697
- } else if (routeMethods.size >= 2) {
11698
- const [name, { line, file }] = routeMethods.entries().next().value;
11699
- conventions.route_method = name;
11700
- provenance.push({ field: "route_method", sourceFile: file, line, query: "chi-route-method" });
11701
- }
11702
- if (mountBases.size >= 1) {
11703
- const [base, { line, file }] = mountBases.entries().next().value;
11704
- conventions.mount_prefix_base = base;
11705
- provenance.push({ field: "mount_prefix_base", sourceFile: file, line, query: "chi-mount-prefix" });
11706
- }
11707
- if (middlewareNames.size >= 1) {
11708
- const [name, { line, file }] = middlewareNames.entries().next().value;
11709
- conventions.middleware_name = name;
11710
- provenance.push({ field: "middleware_name", sourceFile: file, line, query: "chi-middleware-use" });
11711
- }
11712
- let confidence;
11713
- if (Object.keys(conventions).length === 0) {
11714
- confidence = "none";
11715
- } else if (routeMethods.size === 1) {
11716
- confidence = "high";
11717
- } else if (routeMethods.size >= 2) {
11718
- confidence = "low";
11719
- } else {
11720
- confidence = "medium";
11721
- }
11722
- return { conventions, provenance, confidence };
11723
- }
11724
- };
11725
- }
11726
- });
11727
-
11728
- // src/detect/adapters/go-chi.ts
11729
- var init_go_chi = __esm({
11730
- "src/detect/adapters/go-chi.ts"() {
11731
- "use strict";
11732
- init_dist();
11733
- }
11734
- });
11735
-
11736
- // ../adapter-rails/dist/index.js
11737
- import { Parser as Parser9 } from "web-tree-sitter";
11738
- function extractRootController(target) {
11739
- const idx = target.indexOf("#");
11740
- if (idx <= 0)
11741
- return null;
11742
- const controller = target.slice(0, idx).trim();
11743
- return controller || null;
11744
- }
11745
- var ROUTE_METHOD_QUERY2, NAMESPACE_QUERY, ROOT_ROUTE_QUERY, railsAdapter;
11746
- var init_dist2 = __esm({
11747
- "../adapter-rails/dist/index.js"() {
11748
- "use strict";
11749
- init_adapter();
11750
- ROUTE_METHOD_QUERY2 = `
11751
- (call
11752
- method: (identifier) @method (#match? @method "^(get|post|put|patch|delete|options|head)$")
11753
- arguments: (argument_list
11754
- .
11755
- (string) @route_path))
11756
- `;
11757
- NAMESPACE_QUERY = `
11758
- (call
11759
- method: (identifier) @_method (#eq? @_method "namespace")
11760
- arguments: (argument_list
11761
- .
11762
- [
11763
- (simple_symbol) @namespace_symbol
11764
- (string) @namespace_string
11765
- ]))
11766
- `;
11767
- ROOT_ROUTE_QUERY = `
11768
- (call
11769
- method: (identifier) @_method (#eq? @_method "root")
11770
- arguments: (argument_list
11771
- .
11772
- (string) @root_target))
11773
-
11774
- (call
11775
- method: (identifier) @_method (#eq? @_method "root")
11776
- arguments: (argument_list
11777
- (pair
11778
- key: (hash_key_symbol) @_key (#eq? @_key "to")
11779
- value: (string) @root_target)))
11780
- `;
11781
- railsAdapter = {
11782
- id: "rails",
11783
- languages: ["ruby"],
11784
- matches(signals) {
11785
- if (!signals.gemfile)
11786
- return false;
11787
- return /^\s*gem\s+['"]rails['"]/im.test(signals.gemfile);
11788
- },
11789
- async introspect(files, _rootDir) {
11790
- if (files.length === 0) {
11791
- return { conventions: {}, provenance: [], confidence: "none" };
11792
- }
11793
- let language;
11794
- try {
11795
- language = await loadGrammar2("ruby");
11796
- } catch (e2) {
11797
- return { conventions: {}, provenance: [], confidence: "none" };
11798
- }
11799
- const parser = new Parser9();
11800
- parser.setLanguage(language);
11801
- const routeMethods = /* @__PURE__ */ new Map();
11802
- const namespaces = /* @__PURE__ */ new Map();
11803
- const rootControllers = /* @__PURE__ */ new Map();
11804
- try {
11805
- for (const file of files) {
11806
- const skip = isParsableSource2(file.content, file.size);
11807
- if (skip) {
11808
- process.stderr.write(`[massu/ast] WARN: rails skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES2}. (Phase 3.5 mitigation)
11809
- `);
11810
- continue;
11811
- }
11812
- try {
11813
- for (const hit of runQuery2(parser, file.content, ROUTE_METHOD_QUERY2, "rails-route-method", file.path)) {
11814
- const method = hit.captures.method;
11815
- if (method && !routeMethods.has(method)) {
11816
- routeMethods.set(method, { line: hit.line, file: file.path });
11817
- }
11818
- }
11819
- for (const hit of runQuery2(parser, file.content, NAMESPACE_QUERY, "rails-namespace", file.path)) {
11820
- const symbolRaw = hit.captures.namespace_symbol;
11821
- const stringRaw = hit.captures.namespace_string;
11822
- const name = symbolRaw ? symbolRaw.replace(/^:/, "") : stringRaw ? stringRaw.replace(/^['"]/, "").replace(/['"]$/, "") : null;
11823
- if (!name)
11824
- continue;
11825
- const path = "/" + name;
11826
- if (!namespaces.has(path)) {
11827
- namespaces.set(path, { line: hit.line, file: file.path });
11828
- }
11829
- }
11830
- for (const hit of runQuery2(parser, file.content, ROOT_ROUTE_QUERY, "rails-root", file.path)) {
11831
- const raw = hit.captures.root_target;
11832
- if (!raw)
11833
- continue;
11834
- const literal = raw.replace(/^['"]/, "").replace(/['"]$/, "");
11835
- const controller = extractRootController(literal);
11836
- if (controller && !rootControllers.has(controller)) {
11837
- rootControllers.set(controller, { line: hit.line, file: file.path });
11838
- }
11839
- }
11840
- } catch (e2) {
11841
- if (e2 instanceof InvalidQueryError2) {
11842
- throw e2;
11843
- }
11844
- continue;
11845
- }
11846
- }
11847
- } finally {
11848
- try {
11849
- parser.delete();
11850
- } catch {
11851
- }
11852
- }
11853
- const conventions = {};
11854
- const provenance = [];
11855
- if (routeMethods.size === 1) {
11856
- const [name, { line, file }] = routeMethods.entries().next().value;
11857
- conventions.route_method = name;
11858
- provenance.push({ field: "route_method", sourceFile: file, line, query: "rails-route-method" });
11859
- } else if (routeMethods.size >= 2) {
11860
- const [name, { line, file }] = routeMethods.entries().next().value;
11861
- conventions.route_method = name;
11862
- provenance.push({ field: "route_method", sourceFile: file, line, query: "rails-route-method" });
11863
- }
11864
- if (namespaces.size >= 1) {
11865
- const [path, { line, file }] = namespaces.entries().next().value;
11866
- conventions.api_namespace = path;
11867
- provenance.push({ field: "api_namespace", sourceFile: file, line, query: "rails-namespace" });
11868
- }
11869
- if (rootControllers.size >= 1) {
11870
- const [name, { line, file }] = rootControllers.entries().next().value;
11871
- conventions.root_controller = name;
11872
- provenance.push({ field: "root_controller", sourceFile: file, line, query: "rails-root" });
11873
- }
11874
- let confidence;
11875
- if (Object.keys(conventions).length === 0) {
11876
- confidence = "none";
11877
- } else if (routeMethods.size === 1) {
11878
- confidence = "high";
11879
- } else if (routeMethods.size >= 2) {
11880
- confidence = "low";
11881
- } else {
11882
- confidence = "medium";
11883
- }
11884
- return { conventions, provenance, confidence };
11885
- }
11886
- };
11887
- }
11888
- });
11889
-
11890
- // src/detect/adapters/rails.ts
11891
- var init_rails = __esm({
11892
- "src/detect/adapters/rails.ts"() {
11893
- "use strict";
11894
- init_dist2();
11895
- }
11896
- });
11897
-
11898
- // ../adapter-phoenix/dist/index.js
11899
- import { Parser as Parser10 } from "web-tree-sitter";
11900
- function extractPrefixBase5(prefix3) {
11901
- if (!prefix3.startsWith("/"))
11902
- return null;
11903
- const stripped = prefix3.replace(/^\/+/, "");
11904
- const firstSeg = stripped.split("/")[0];
11905
- if (!firstSeg)
11906
- return null;
11907
- return "/" + firstSeg;
11908
- }
11909
- var ROUTE_METHOD_QUERY3, SCOPE_PATH_QUERY, ROUTER_MODULE_QUERY, phoenixAdapter;
11910
- var init_dist3 = __esm({
11911
- "../adapter-phoenix/dist/index.js"() {
11912
- "use strict";
11913
- init_adapter();
11914
- ROUTE_METHOD_QUERY3 = `
11915
- (call
11916
- (identifier) @method (#match? @method "^(get|post|put|patch|delete|options|head)$")
11917
- (arguments
11918
- .
11919
- (string) @route_path))
11920
- `;
11921
- SCOPE_PATH_QUERY = `
11922
- (call
11923
- (identifier) @_method (#eq? @_method "scope")
11924
- (arguments
11925
- .
11926
- (string) @scope_path)
11927
- (do_block))
11928
- `;
11929
- ROUTER_MODULE_QUERY = `
11930
- (call
11931
- (identifier) @_method (#eq? @_method "defmodule")
11932
- (arguments
11933
- .
11934
- (alias) @module_name (#match? @module_name "Router$"))
11935
- (do_block))
11936
- `;
11937
- phoenixAdapter = {
11938
- id: "phoenix",
11939
- languages: ["elixir"],
11940
- matches(signals) {
11941
- if (!signals.mixExs)
11942
- return false;
11943
- return /\{\s*:phoenix\b(?!_)/.test(signals.mixExs);
11944
- },
11945
- async introspect(files, _rootDir) {
11946
- if (files.length === 0) {
11947
- return { conventions: {}, provenance: [], confidence: "none" };
11948
- }
11949
- let language;
11950
- try {
11951
- language = await loadGrammar2("elixir");
11952
- } catch (e2) {
11953
- return { conventions: {}, provenance: [], confidence: "none" };
11954
- }
11955
- const parser = new Parser10();
11956
- parser.setLanguage(language);
11957
- const routeMethods = /* @__PURE__ */ new Map();
11958
- const scopePaths = /* @__PURE__ */ new Map();
11959
- const routerModules = /* @__PURE__ */ new Map();
11960
- try {
11961
- for (const file of files) {
11962
- const skip = isParsableSource2(file.content, file.size);
11963
- if (skip) {
11964
- process.stderr.write(`[massu/ast] WARN: phoenix skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES2}. (Phase 3.5 mitigation)
11965
- `);
11966
- continue;
11967
- }
11968
- try {
11969
- for (const hit of runQuery2(parser, file.content, ROUTE_METHOD_QUERY3, "phoenix-route-method", file.path)) {
11970
- const method = hit.captures.method;
11971
- if (method && !routeMethods.has(method)) {
11972
- routeMethods.set(method, { line: hit.line, file: file.path });
11973
- }
11974
- }
11975
- for (const hit of runQuery2(parser, file.content, SCOPE_PATH_QUERY, "phoenix-scope-path", file.path)) {
11976
- const raw = hit.captures.scope_path;
11977
- if (!raw)
11978
- continue;
11979
- const literal = raw.replace(/^["']/, "").replace(/["']$/, "");
11980
- const base = extractPrefixBase5(literal);
11981
- if (base && !scopePaths.has(base)) {
11982
- scopePaths.set(base, { line: hit.line, file: file.path });
11983
- }
11984
- }
11985
- for (const hit of runQuery2(parser, file.content, ROUTER_MODULE_QUERY, "phoenix-router-module", file.path)) {
11986
- const name = hit.captures.module_name;
11987
- if (name && !routerModules.has(name)) {
11988
- routerModules.set(name, { line: hit.line, file: file.path });
11989
- }
11990
- }
11991
- } catch (e2) {
11992
- if (e2 instanceof InvalidQueryError2) {
11993
- throw e2;
11994
- }
11995
- continue;
11996
- }
11997
- }
11998
- } finally {
11999
- try {
12000
- parser.delete();
12001
- } catch {
12002
- }
12003
- }
12004
- const conventions = {};
12005
- const provenance = [];
12006
- if (routeMethods.size === 1) {
12007
- const [name, { line, file }] = routeMethods.entries().next().value;
12008
- conventions.route_method = name;
12009
- provenance.push({ field: "route_method", sourceFile: file, line, query: "phoenix-route-method" });
12010
- } else if (routeMethods.size >= 2) {
12011
- const [name, { line, file }] = routeMethods.entries().next().value;
12012
- conventions.route_method = name;
12013
- provenance.push({ field: "route_method", sourceFile: file, line, query: "phoenix-route-method" });
12014
- }
12015
- if (scopePaths.size >= 1) {
12016
- const [base, { line, file }] = scopePaths.entries().next().value;
12017
- conventions.scope_prefix_base = base;
12018
- provenance.push({ field: "scope_prefix_base", sourceFile: file, line, query: "phoenix-scope-path" });
12019
- }
12020
- if (routerModules.size >= 1) {
12021
- const [name, { line, file }] = routerModules.entries().next().value;
12022
- conventions.router_module = name;
12023
- provenance.push({ field: "router_module", sourceFile: file, line, query: "phoenix-router-module" });
12024
- }
12025
- let confidence;
12026
- if (Object.keys(conventions).length === 0) {
12027
- confidence = "none";
12028
- } else if (routeMethods.size === 1) {
12029
- confidence = "high";
12030
- } else if (routeMethods.size >= 2) {
12031
- confidence = "low";
12032
- } else {
12033
- confidence = "medium";
12034
- }
12035
- return { conventions, provenance, confidence };
12036
- }
12037
- };
12038
- }
12039
- });
12040
-
12041
- // src/detect/adapters/phoenix.ts
12042
- var init_phoenix = __esm({
12043
- "src/detect/adapters/phoenix.ts"() {
12044
- "use strict";
12045
- init_dist3();
12046
- }
12047
- });
12048
-
12049
- // ../adapter-aspnet/dist/index.js
12050
- import { Parser as Parser11 } from "web-tree-sitter";
12051
- function extractPrefixBase6(prefix3) {
12052
- const stripped = prefix3.replace(/^\/+/, "");
12053
- const firstSeg = stripped.split("/")[0];
12054
- if (!firstSeg)
12055
- return null;
12056
- return "/" + firstSeg;
12057
- }
12058
- var MAP_VERB_QUERY, HTTP_ATTR_QUERY, ROUTE_ATTR_QUERY, CONTROLLER_CLASS_QUERY, aspnetAdapter;
12059
- var init_dist4 = __esm({
12060
- "../adapter-aspnet/dist/index.js"() {
12061
- "use strict";
12062
- init_adapter();
12063
- MAP_VERB_QUERY = `
12064
- (invocation_expression
12065
- function: (member_access_expression
12066
- name: (identifier) @method (#match? @method "^Map(Get|Post|Put|Patch|Delete|Head|Options)$"))
12067
- arguments: (argument_list
12068
- .
12069
- (argument (string_literal) @route_path)))
12070
- `;
12071
- HTTP_ATTR_QUERY = `
12072
- (attribute
12073
- name: (identifier) @attr_name (#match? @attr_name "^Http(Get|Post|Put|Patch|Delete|Head|Options)$"))
12074
- `;
12075
- ROUTE_ATTR_QUERY = `
12076
- (attribute
12077
- name: (identifier) @_attr_name (#eq? @_attr_name "Route")
12078
- (attribute_argument_list
12079
- (attribute_argument (string_literal) @route_template)))
12080
- `;
12081
- CONTROLLER_CLASS_QUERY = `
12082
- (class_declaration
12083
- name: (identifier) @class_name (#match? @class_name "Controller$"))
12084
- `;
12085
- aspnetAdapter = {
12086
- id: "aspnet",
12087
- languages: ["csharp"],
12088
- matches(signals) {
12089
- if (!signals.csproj)
12090
- return false;
12091
- if (/Sdk\s*=\s*["']Microsoft\.NET\.Sdk\.Web["']/i.test(signals.csproj))
12092
- return true;
12093
- if (/Microsoft\.AspNetCore\.App/i.test(signals.csproj))
12094
- return true;
12095
- return false;
12096
- },
12097
- async introspect(files, _rootDir) {
12098
- if (files.length === 0) {
12099
- return { conventions: {}, provenance: [], confidence: "none" };
12100
- }
12101
- let language;
12102
- try {
12103
- language = await loadGrammar2("csharp");
12104
- } catch (e2) {
12105
- return { conventions: {}, provenance: [], confidence: "none" };
12106
- }
12107
- const parser = new Parser11();
12108
- parser.setLanguage(language);
12109
- const routeMethods = /* @__PURE__ */ new Map();
12110
- const prefixBases = /* @__PURE__ */ new Map();
12111
- const controllerClasses = /* @__PURE__ */ new Map();
12112
- try {
12113
- for (const file of files) {
12114
- const skip = isParsableSource2(file.content, file.size);
12115
- if (skip) {
12116
- process.stderr.write(`[massu/ast] WARN: aspnet skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES2}. (Phase 3.5 mitigation)
12117
- `);
12118
- continue;
12119
- }
12120
- try {
12121
- for (const hit of runQuery2(parser, file.content, MAP_VERB_QUERY, "aspnet-map-verb", file.path)) {
12122
- const methodRaw = hit.captures.method;
12123
- if (!methodRaw)
12124
- continue;
12125
- const verb = methodRaw.replace(/^Map/, "");
12126
- if (!routeMethods.has(verb)) {
12127
- routeMethods.set(verb, { line: hit.line, file: file.path });
12128
- }
12129
- const pathRaw = hit.captures.route_path;
12130
- if (pathRaw) {
12131
- const literal = pathRaw.replace(/^["']/, "").replace(/["']$/, "");
12132
- const base = extractPrefixBase6(literal);
12133
- if (base && !prefixBases.has(base)) {
12134
- prefixBases.set(base, { line: hit.line, file: file.path });
12135
- }
12136
- }
12137
- }
12138
- for (const hit of runQuery2(parser, file.content, HTTP_ATTR_QUERY, "aspnet-http-attr", file.path)) {
12139
- const attrRaw = hit.captures.attr_name;
12140
- if (!attrRaw)
12141
- continue;
12142
- const verb = attrRaw.replace(/^Http/, "");
12143
- if (!routeMethods.has(verb)) {
12144
- routeMethods.set(verb, { line: hit.line, file: file.path });
12145
- }
12146
- }
12147
- for (const hit of runQuery2(parser, file.content, ROUTE_ATTR_QUERY, "aspnet-route-attr", file.path)) {
12148
- const tplRaw = hit.captures.route_template;
12149
- if (!tplRaw)
12150
- continue;
12151
- const literal = tplRaw.replace(/^["']/, "").replace(/["']$/, "");
12152
- const base = extractPrefixBase6(literal);
12153
- if (base && !prefixBases.has(base)) {
12154
- prefixBases.set(base, { line: hit.line, file: file.path });
12155
- }
12156
- }
12157
- for (const hit of runQuery2(parser, file.content, CONTROLLER_CLASS_QUERY, "aspnet-controller-class", file.path)) {
12158
- const name = hit.captures.class_name;
12159
- if (name && !controllerClasses.has(name)) {
12160
- controllerClasses.set(name, { line: hit.line, file: file.path });
12161
- }
12162
- }
12163
- } catch (e2) {
12164
- if (e2 instanceof InvalidQueryError2) {
12165
- throw e2;
12166
- }
12167
- continue;
12168
- }
12169
- }
12170
- } finally {
12171
- try {
12172
- parser.delete();
12173
- } catch {
12174
- }
12175
- }
12176
- const conventions = {};
12177
- const provenance = [];
12178
- if (routeMethods.size === 1) {
12179
- const [name, { line, file }] = routeMethods.entries().next().value;
12180
- conventions.route_method = name;
12181
- provenance.push({ field: "route_method", sourceFile: file, line, query: "aspnet-map-verb" });
12182
- } else if (routeMethods.size >= 2) {
12183
- const [name, { line, file }] = routeMethods.entries().next().value;
12184
- conventions.route_method = name;
12185
- provenance.push({ field: "route_method", sourceFile: file, line, query: "aspnet-map-verb" });
12186
- }
12187
- if (prefixBases.size >= 1) {
12188
- const [base, { line, file }] = prefixBases.entries().next().value;
12189
- conventions.route_prefix_base = base;
12190
- provenance.push({ field: "route_prefix_base", sourceFile: file, line, query: "aspnet-route-prefix" });
12191
- }
12192
- if (controllerClasses.size >= 1) {
12193
- const [name, { line, file }] = controllerClasses.entries().next().value;
12194
- conventions.controller_class = name;
12195
- provenance.push({ field: "controller_class", sourceFile: file, line, query: "aspnet-controller-class" });
12196
- }
12197
- let confidence;
12198
- if (Object.keys(conventions).length === 0) {
12199
- confidence = "none";
12200
- } else if (routeMethods.size === 1) {
12201
- confidence = "high";
12202
- } else if (routeMethods.size >= 2) {
12203
- confidence = "low";
12204
- } else {
12205
- confidence = "medium";
12206
- }
12207
- return { conventions, provenance, confidence };
12208
- }
12209
- };
11162
+ // src/detect/adapters/go-chi.ts
11163
+ import { goChiAdapter } from "@massu/adapter-go-chi";
11164
+ var init_go_chi = __esm({
11165
+ "src/detect/adapters/go-chi.ts"() {
11166
+ "use strict";
12210
11167
  }
12211
11168
  });
12212
11169
 
12213
- // src/detect/adapters/aspnet.ts
12214
- var init_aspnet = __esm({
12215
- "src/detect/adapters/aspnet.ts"() {
11170
+ // src/detect/adapters/rails.ts
11171
+ import { railsAdapter } from "@massu/adapter-rails";
11172
+ var init_rails = __esm({
11173
+ "src/detect/adapters/rails.ts"() {
12216
11174
  "use strict";
12217
- init_dist4();
12218
11175
  }
12219
11176
  });
12220
11177
 
12221
- // ../adapter-spring/dist/index.js
12222
- import { Parser as Parser12 } from "web-tree-sitter";
12223
- function extractPrefixBase7(prefix3) {
12224
- const stripped = prefix3.replace(/^\/+/, "");
12225
- const firstSeg = stripped.split("/")[0];
12226
- if (!firstSeg)
12227
- return null;
12228
- return "/" + firstSeg;
12229
- }
12230
- var HTTP_MAPPING_QUERY, HTTP_MAPPING_NO_ARGS_QUERY, REQUEST_MAPPING_QUERY, CONTROLLER_CLASS_QUERY2, springAdapter;
12231
- var init_dist5 = __esm({
12232
- "../adapter-spring/dist/index.js"() {
11178
+ // src/detect/adapters/phoenix.ts
11179
+ import { phoenixAdapter } from "@massu/adapter-phoenix";
11180
+ var init_phoenix = __esm({
11181
+ "src/detect/adapters/phoenix.ts"() {
11182
+ "use strict";
11183
+ }
11184
+ });
11185
+
11186
+ // src/detect/adapters/aspnet.ts
11187
+ import { aspnetAdapter } from "@massu/adapter-aspnet";
11188
+ var init_aspnet = __esm({
11189
+ "src/detect/adapters/aspnet.ts"() {
12233
11190
  "use strict";
12234
- init_adapter();
12235
- HTTP_MAPPING_QUERY = `
12236
- (annotation
12237
- name: (identifier) @method (#match? @method "^(Get|Post|Put|Patch|Delete|Head|Options)Mapping$")
12238
- arguments: (annotation_argument_list
12239
- (string_literal) @route_path))
12240
- `;
12241
- HTTP_MAPPING_NO_ARGS_QUERY = `
12242
- (marker_annotation
12243
- name: (identifier) @method (#match? @method "^(Get|Post|Put|Patch|Delete|Head|Options)Mapping$"))
12244
- `;
12245
- REQUEST_MAPPING_QUERY = `
12246
- (annotation
12247
- name: (identifier) @_name (#eq? @_name "RequestMapping")
12248
- arguments: (annotation_argument_list
12249
- (string_literal) @route_template))
12250
- `;
12251
- CONTROLLER_CLASS_QUERY2 = `
12252
- (class_declaration
12253
- (modifiers
12254
- (marker_annotation
12255
- name: (identifier) @_anno (#match? @_anno "^(RestController|Controller)$")))
12256
- name: (identifier) @class_name)
12257
-
12258
- (class_declaration
12259
- (modifiers
12260
- (annotation
12261
- name: (identifier) @_anno (#match? @_anno "^(RestController|Controller)$")))
12262
- name: (identifier) @class_name)
12263
- `;
12264
- springAdapter = {
12265
- id: "spring",
12266
- languages: ["java"],
12267
- matches(signals) {
12268
- if (signals.pomXml && /\bspring-boot-starter[\w-]*\b/.test(signals.pomXml)) {
12269
- return true;
12270
- }
12271
- if (signals.gradleBuild && /\bspring-boot-starter[\w-]*\b/.test(signals.gradleBuild)) {
12272
- return true;
12273
- }
12274
- if (signals.pomXml && /\borg\.springframework\b/.test(signals.pomXml)) {
12275
- return true;
12276
- }
12277
- if (signals.gradleBuild && /\borg\.springframework\b/.test(signals.gradleBuild)) {
12278
- return true;
12279
- }
12280
- return false;
12281
- },
12282
- async introspect(files, _rootDir) {
12283
- if (files.length === 0) {
12284
- return { conventions: {}, provenance: [], confidence: "none" };
12285
- }
12286
- let language;
12287
- try {
12288
- language = await loadGrammar2("java");
12289
- } catch (e2) {
12290
- return { conventions: {}, provenance: [], confidence: "none" };
12291
- }
12292
- const parser = new Parser12();
12293
- parser.setLanguage(language);
12294
- const routeMethods = /* @__PURE__ */ new Map();
12295
- const prefixBases = /* @__PURE__ */ new Map();
12296
- const controllerClasses = /* @__PURE__ */ new Map();
12297
- try {
12298
- for (const file of files) {
12299
- const skip = isParsableSource2(file.content, file.size);
12300
- if (skip) {
12301
- process.stderr.write(`[massu/ast] WARN: spring skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES2}. (Phase 3.5 mitigation)
12302
- `);
12303
- continue;
12304
- }
12305
- try {
12306
- for (const hit of runQuery2(parser, file.content, HTTP_MAPPING_QUERY, "spring-http-mapping", file.path)) {
12307
- const methodRaw = hit.captures.method;
12308
- if (!methodRaw)
12309
- continue;
12310
- const verb = methodRaw.replace(/Mapping$/, "");
12311
- if (!routeMethods.has(verb)) {
12312
- routeMethods.set(verb, { line: hit.line, file: file.path });
12313
- }
12314
- }
12315
- for (const hit of runQuery2(parser, file.content, HTTP_MAPPING_NO_ARGS_QUERY, "spring-http-mapping-marker", file.path)) {
12316
- const methodRaw = hit.captures.method;
12317
- if (!methodRaw)
12318
- continue;
12319
- const verb = methodRaw.replace(/Mapping$/, "");
12320
- if (!routeMethods.has(verb)) {
12321
- routeMethods.set(verb, { line: hit.line, file: file.path });
12322
- }
12323
- }
12324
- for (const hit of runQuery2(parser, file.content, REQUEST_MAPPING_QUERY, "spring-request-mapping", file.path)) {
12325
- const tplRaw = hit.captures.route_template;
12326
- if (!tplRaw)
12327
- continue;
12328
- const literal = tplRaw.replace(/^["']/, "").replace(/["']$/, "");
12329
- const base = extractPrefixBase7(literal);
12330
- if (base && !prefixBases.has(base)) {
12331
- prefixBases.set(base, { line: hit.line, file: file.path });
12332
- }
12333
- }
12334
- for (const hit of runQuery2(parser, file.content, CONTROLLER_CLASS_QUERY2, "spring-controller-class", file.path)) {
12335
- const name = hit.captures.class_name;
12336
- if (name && !controllerClasses.has(name)) {
12337
- controllerClasses.set(name, { line: hit.line, file: file.path });
12338
- }
12339
- }
12340
- } catch (e2) {
12341
- if (e2 instanceof InvalidQueryError2) {
12342
- throw e2;
12343
- }
12344
- continue;
12345
- }
12346
- }
12347
- } finally {
12348
- try {
12349
- parser.delete();
12350
- } catch {
12351
- }
12352
- }
12353
- const conventions = {};
12354
- const provenance = [];
12355
- if (routeMethods.size === 1) {
12356
- const [name, { line, file }] = routeMethods.entries().next().value;
12357
- conventions.route_method = name;
12358
- provenance.push({ field: "route_method", sourceFile: file, line, query: "spring-http-mapping" });
12359
- } else if (routeMethods.size >= 2) {
12360
- const [name, { line, file }] = routeMethods.entries().next().value;
12361
- conventions.route_method = name;
12362
- provenance.push({ field: "route_method", sourceFile: file, line, query: "spring-http-mapping" });
12363
- }
12364
- if (prefixBases.size >= 1) {
12365
- const [base, { line, file }] = prefixBases.entries().next().value;
12366
- conventions.route_prefix_base = base;
12367
- provenance.push({ field: "route_prefix_base", sourceFile: file, line, query: "spring-request-mapping" });
12368
- }
12369
- if (controllerClasses.size >= 1) {
12370
- const [name, { line, file }] = controllerClasses.entries().next().value;
12371
- conventions.controller_class = name;
12372
- provenance.push({ field: "controller_class", sourceFile: file, line, query: "spring-controller-class" });
12373
- }
12374
- let confidence;
12375
- if (Object.keys(conventions).length === 0) {
12376
- confidence = "none";
12377
- } else if (routeMethods.size === 1) {
12378
- confidence = "high";
12379
- } else if (routeMethods.size >= 2) {
12380
- confidence = "low";
12381
- } else {
12382
- confidence = "medium";
12383
- }
12384
- return { conventions, provenance, confidence };
12385
- }
12386
- };
12387
11191
  }
12388
11192
  });
12389
11193
 
12390
11194
  // src/detect/adapters/spring.ts
11195
+ import { springAdapter } from "@massu/adapter-spring";
12391
11196
  var init_spring = __esm({
12392
11197
  "src/detect/adapters/spring.ts"() {
12393
11198
  "use strict";
12394
- init_dist5();
12395
11199
  }
12396
11200
  });
12397
11201
 
@@ -12402,8 +11206,8 @@ __export(file_sampler_exports, {
12402
11206
  SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
12403
11207
  sampleFilesForAdapter: () => sampleFilesForAdapter
12404
11208
  });
12405
- import { readdirSync as readdirSync10, readFileSync as readFileSync10, lstatSync as lstatSync5 } from "node:fs";
12406
- import { join as join10, extname } from "node:path";
11209
+ import { readdirSync as readdirSync9, readFileSync as readFileSync9, lstatSync as lstatSync4 } from "node:fs";
11210
+ import { join as join9, extname } from "node:path";
12407
11211
  function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
12408
11212
  const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
12409
11213
  const maxFiles = options.maxFilesPerAdapter ?? DEFAULT_MAX_FILES;
@@ -12418,7 +11222,7 @@ function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
12418
11222
  const langDetection = detection.sourceDirs[langKey];
12419
11223
  const candidateDirs = [];
12420
11224
  if (langDetection?.source_dirs && langDetection.source_dirs.length > 0) {
12421
- candidateDirs.push(...langDetection.source_dirs.map((d2) => join10(projectRoot, d2)));
11225
+ candidateDirs.push(...langDetection.source_dirs.map((d2) => join9(projectRoot, d2)));
12422
11226
  } else {
12423
11227
  candidateDirs.push(projectRoot);
12424
11228
  }
@@ -12434,7 +11238,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
12434
11238
  if (out.length >= maxFiles) return;
12435
11239
  let entries;
12436
11240
  try {
12437
- entries = readdirSync10(dir);
11241
+ entries = readdirSync9(dir);
12438
11242
  } catch {
12439
11243
  return;
12440
11244
  }
@@ -12442,10 +11246,10 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
12442
11246
  if (out.length >= maxFiles) return;
12443
11247
  if (entry.startsWith(".")) continue;
12444
11248
  if (IGNORED_DIRS3.has(entry)) continue;
12445
- const fullPath = join10(dir, entry);
11249
+ const fullPath = join9(dir, entry);
12446
11250
  let st;
12447
11251
  try {
12448
- st = lstatSync5(fullPath);
11252
+ st = lstatSync4(fullPath);
12449
11253
  } catch {
12450
11254
  continue;
12451
11255
  }
@@ -12463,7 +11267,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
12463
11267
  seen.add(fullPath);
12464
11268
  let content;
12465
11269
  try {
12466
- content = readFileSync10(fullPath, "utf-8");
11270
+ content = readFileSync9(fullPath, "utf-8");
12467
11271
  } catch {
12468
11272
  continue;
12469
11273
  }
@@ -12701,7 +11505,7 @@ var init_detect = __esm({
12701
11505
  });
12702
11506
 
12703
11507
  // src/detect/drift.ts
12704
- import { createHash as createHash4 } from "crypto";
11508
+ import { createHash as createHash3 } from "crypto";
12705
11509
  function summarizeDetection(det) {
12706
11510
  const languages = Array.from(new Set(det.manifests.map((m3) => m3.language))).sort();
12707
11511
  const frameworks = {};
@@ -12732,7 +11536,7 @@ function summarizeDetection(det) {
12732
11536
  function computeFingerprint(det) {
12733
11537
  const data = summarizeDetection(det);
12734
11538
  const stable = JSON.stringify(data, Object.keys(data).sort());
12735
- return createHash4("sha256").update(stable).digest("hex");
11539
+ return createHash3("sha256").update(stable).digest("hex");
12736
11540
  }
12737
11541
  function stringOf(v3) {
12738
11542
  if (typeof v3 === "string") return v3;
@@ -13082,7 +11886,7 @@ function cD({ input: t = $, output: u2 = j, overwrite: F = true, hideCursor: e2
13082
11886
  };
13083
11887
  }
13084
11888
  var import_sisteransi, import_picocolors, Q, P, X, DD, uD, FD, m, L, N, I, r, tD, eD, iD, v, CD, w, W, rD, R, y, V, z2, ED, _, nD, oD, aD, c, xD, S, AD, pD, h, x, fD, gD, vD, K, dD, bD, mD, Y, wD, yD, _D, Z, kD, SD, $D, q, jD, MD, TD, H, OD, PD;
13085
- var init_dist6 = __esm({
11889
+ var init_dist = __esm({
13086
11890
  "../../node_modules/@clack/core/dist/index.mjs"() {
13087
11891
  import_sisteransi = __toESM(require_src(), 1);
13088
11892
  import_picocolors = __toESM(require_picocolors(), 1);
@@ -13500,10 +12304,10 @@ function X2() {
13500
12304
  return p.platform !== "win32" ? p.env.TERM !== "linux" : !!p.env.CI || !!p.env.WT_SESSION || !!p.env.TERMINUS_SUBLIME || p.env.ConEmuTask === "{cmd::Cmder}" || p.env.TERM_PROGRAM === "Terminus-Sublime" || p.env.TERM_PROGRAM === "vscode" || p.env.TERM === "xterm-256color" || p.env.TERM === "alacritty" || p.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
13501
12305
  }
13502
12306
  var import_picocolors2, import_sisteransi2, E, u, ee, A2, B, S2, te, a, m2, j2, R2, V2, M2, G2, se, N2, re, ie, ne, ae, oe, ce, le, y2, k2, ue, $e, me, de, he, pe, ge, ye, ve, we, fe, v2, L2, be, xe;
13503
- var init_dist7 = __esm({
12307
+ var init_dist2 = __esm({
13504
12308
  "../../node_modules/@clack/prompts/dist/index.mjs"() {
13505
- init_dist6();
13506
- init_dist6();
12309
+ init_dist();
12310
+ init_dist();
13507
12311
  import_picocolors2 = __toESM(require_picocolors(), 1);
13508
12312
  import_sisteransi2 = __toESM(require_src(), 1);
13509
12313
  E = X2();
@@ -13891,10 +12695,10 @@ __export(init_exports, {
13891
12695
  validateWrittenConfig: () => validateWrittenConfig,
13892
12696
  writeConfigAtomic: () => writeConfigAtomic
13893
12697
  });
13894
- import { closeSync as closeSync2, existsSync as existsSync10, fsyncSync as fsyncSync2, openSync as openSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync3, writeSync as writeSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync11, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync7, chmodSync as chmodSync3 } from "fs";
13895
- import { resolve as resolve6, basename as basename4, dirname as dirname6 } from "path";
12698
+ import { closeSync as closeSync2, existsSync as existsSync10, fsyncSync as fsyncSync2, openSync as openSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync2, writeSync as writeSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync10, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync7, chmodSync as chmodSync2 } from "fs";
12699
+ import { resolve as resolve6, basename as basename4, dirname as dirname5 } from "path";
13896
12700
  import { fileURLToPath as fileURLToPath2 } from "url";
13897
- import { homedir as homedir4 } from "os";
12701
+ import { homedir as homedir3 } from "os";
13898
12702
  import { stringify as yamlStringify, parse as yamlParse } from "yaml";
13899
12703
  function detectFramework(projectRoot) {
13900
12704
  const result = {
@@ -13906,7 +12710,7 @@ function detectFramework(projectRoot) {
13906
12710
  const pkgPath = resolve6(projectRoot, "package.json");
13907
12711
  if (!existsSync10(pkgPath)) return result;
13908
12712
  try {
13909
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
12713
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
13910
12714
  const allDeps = {
13911
12715
  ...pkg.dependencies,
13912
12716
  ...pkg.devDependencies
@@ -13953,7 +12757,7 @@ function detectPython(projectRoot) {
13953
12757
  const filePath = resolve6(projectRoot, file);
13954
12758
  if (existsSync10(filePath)) {
13955
12759
  try {
13956
- const content = readFileSync11(filePath, "utf-8").toLowerCase();
12760
+ const content = readFileSync10(filePath, "utf-8").toLowerCase();
13957
12761
  if (content.includes("fastapi")) result.hasFastapi = true;
13958
12762
  if (content.includes("sqlalchemy")) result.hasSqlalchemy = true;
13959
12763
  } catch {
@@ -13978,7 +12782,7 @@ function detectPython(projectRoot) {
13978
12782
  }
13979
12783
  if (existsSync10(candidatePath)) {
13980
12784
  try {
13981
- const files = readdirSync11(candidatePath);
12785
+ const files = readdirSync10(candidatePath);
13982
12786
  if (files.some((f2) => f2.endsWith(".py"))) {
13983
12787
  result.root = candidate;
13984
12788
  break;
@@ -14043,7 +12847,7 @@ function generateConfig(projectRoot, framework) {
14043
12847
  # Documentation: https://massu.ai/docs/getting-started/configuration
14044
12848
 
14045
12849
  ${yamlStringify(config)}`;
14046
- writeFileSync3(configPath, yamlContent, "utf-8");
12850
+ writeFileSync2(configPath, yamlContent, "utf-8");
14047
12851
  return true;
14048
12852
  }
14049
12853
  function monorepoCommonRoot(packages) {
@@ -14214,7 +13018,7 @@ function applyVariantTemplate(config, templatesDir) {
14214
13018
  if (!existsSync10(templatePath)) return config;
14215
13019
  let template;
14216
13020
  try {
14217
- template = yamlParse(readFileSync11(templatePath, "utf-8"));
13021
+ template = yamlParse(readFileSync10(templatePath, "utf-8"));
14218
13022
  } catch {
14219
13023
  return config;
14220
13024
  }
@@ -14281,7 +13085,7 @@ function writeConfigAtomic(configPath, content) {
14281
13085
  }
14282
13086
  }
14283
13087
  try {
14284
- mkdirSync5(dirname6(configPath), { recursive: true });
13088
+ mkdirSync4(dirname5(configPath), { recursive: true });
14285
13089
  const fd = openSync2(tmpPath, "w", 420);
14286
13090
  try {
14287
13091
  const buf = Buffer.from(content, "utf-8");
@@ -14294,10 +13098,10 @@ function writeConfigAtomic(configPath, content) {
14294
13098
  if (parsed === null || typeof parsed !== "object") {
14295
13099
  throw new Error("Generated config is not a valid YAML object");
14296
13100
  }
14297
- renameSync4(tmpPath, configPath);
13101
+ renameSync3(tmpPath, configPath);
14298
13102
  if (existingMode !== void 0) {
14299
13103
  try {
14300
- chmodSync3(configPath, existingMode);
13104
+ chmodSync2(configPath, existingMode);
14301
13105
  } catch {
14302
13106
  }
14303
13107
  }
@@ -14315,7 +13119,7 @@ function writeConfigAtomic(configPath, content) {
14315
13119
  function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
14316
13120
  try {
14317
13121
  if (!existsSync10(configPath)) return "Config file does not exist after write";
14318
- const content = readFileSync11(configPath, "utf-8");
13122
+ const content = readFileSync10(configPath, "utf-8");
14319
13123
  const parsed = yamlParse(content);
14320
13124
  if (parsed === null || typeof parsed !== "object") {
14321
13125
  return "Config is not a valid YAML object";
@@ -14416,9 +13220,9 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
14416
13220
  return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
14417
13221
  }
14418
13222
  try {
14419
- let content = readFileSync11(srcPath, "utf-8");
13223
+ let content = readFileSync10(srcPath, "utf-8");
14420
13224
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
14421
- writeFileSync3(targetPath, content, "utf-8");
13225
+ writeFileSync2(targetPath, content, "utf-8");
14422
13226
  return { success: true };
14423
13227
  } catch (err) {
14424
13228
  return { success: false, error: err instanceof Error ? err.message : String(err) };
@@ -14429,7 +13233,7 @@ function registerMcpServer(projectRoot) {
14429
13233
  let existing = {};
14430
13234
  if (existsSync10(mcpPath)) {
14431
13235
  try {
14432
- existing = JSON.parse(readFileSync11(mcpPath, "utf-8"));
13236
+ existing = JSON.parse(readFileSync10(mcpPath, "utf-8"));
14433
13237
  } catch {
14434
13238
  existing = {};
14435
13239
  }
@@ -14444,7 +13248,7 @@ function registerMcpServer(projectRoot) {
14444
13248
  args: ["-y", "@massu/core"]
14445
13249
  };
14446
13250
  existing.mcpServers = servers;
14447
- writeFileSync3(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
13251
+ writeFileSync2(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
14448
13252
  return true;
14449
13253
  }
14450
13254
  function resolveHooksDir() {
@@ -14549,12 +13353,12 @@ function installHooks(projectRoot) {
14549
13353
  const claudeDir = resolve6(projectRoot, claudeDirName);
14550
13354
  const settingsPath = resolve6(claudeDir, "settings.local.json");
14551
13355
  if (!existsSync10(claudeDir)) {
14552
- mkdirSync5(claudeDir, { recursive: true });
13356
+ mkdirSync4(claudeDir, { recursive: true });
14553
13357
  }
14554
13358
  let settings = {};
14555
13359
  if (existsSync10(settingsPath)) {
14556
13360
  try {
14557
- settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
13361
+ settings = JSON.parse(readFileSync10(settingsPath, "utf-8"));
14558
13362
  } catch {
14559
13363
  settings = {};
14560
13364
  }
@@ -14568,15 +13372,15 @@ function installHooks(projectRoot) {
14568
13372
  }
14569
13373
  }
14570
13374
  settings.hooks = hooksConfig;
14571
- writeFileSync3(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
13375
+ writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
14572
13376
  return { installed: true, count: hookCount };
14573
13377
  }
14574
13378
  function initMemoryDir(projectRoot) {
14575
13379
  const encodedRoot = "-" + projectRoot.replace(/\//g, "-");
14576
- const memoryDir = resolve6(homedir4(), `.claude/projects/${encodedRoot}/memory`);
13380
+ const memoryDir = resolve6(homedir3(), `.claude/projects/${encodedRoot}/memory`);
14577
13381
  let created = false;
14578
13382
  if (!existsSync10(memoryDir)) {
14579
- mkdirSync5(memoryDir, { recursive: true });
13383
+ mkdirSync4(memoryDir, { recursive: true });
14580
13384
  created = true;
14581
13385
  }
14582
13386
  const memoryMdPath = resolve6(memoryDir, "MEMORY.md");
@@ -14597,7 +13401,7 @@ function initMemoryDir(projectRoot) {
14597
13401
  ## File Index
14598
13402
  <!-- Significant files and directories -->
14599
13403
  `;
14600
- writeFileSync3(memoryMdPath, memoryContent, "utf-8");
13404
+ writeFileSync2(memoryMdPath, memoryContent, "utf-8");
14601
13405
  memoryMdCreated = true;
14602
13406
  }
14603
13407
  return { created, memoryMdCreated };
@@ -14849,8 +13653,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
14849
13653
  "\u2014 Massu"
14850
13654
  ].join("\n");
14851
13655
  try {
14852
- mkdirSync5(resolve6(cmdResult.claudeDir, "commands"), { recursive: true });
14853
- writeFileSync3(placeholderPath, placeholderBody, "utf-8");
13656
+ mkdirSync4(resolve6(cmdResult.claudeDir, "commands"), { recursive: true });
13657
+ writeFileSync2(placeholderPath, placeholderBody, "utf-8");
14854
13658
  log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
14855
13659
  } catch {
14856
13660
  }
@@ -14869,8 +13673,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
14869
13673
  (async () => {
14870
13674
  try {
14871
13675
  const encodedRoot = projectRoot.replace(/\//g, "-");
14872
- const memoryDir = resolve6(homedir4(), ".claude", "projects", encodedRoot, "memory");
14873
- const memFiles = existsSync10(memoryDir) ? readdirSync11(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
13676
+ const memoryDir = resolve6(homedir3(), ".claude", "projects", encodedRoot, "memory");
13677
+ const memFiles = existsSync10(memoryDir) ? readdirSync10(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
14874
13678
  if (memFiles.length > 0) {
14875
13679
  const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
14876
13680
  const db = getMemoryDb2();
@@ -14893,7 +13697,7 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
14893
13697
  }
14894
13698
  async function promptOverwrite(configPath) {
14895
13699
  try {
14896
- const { confirm, isCancel } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
13700
+ const { confirm, isCancel } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
14897
13701
  const res = await confirm({
14898
13702
  message: `massu.config.yaml already exists at ${configPath}. Overwrite?`,
14899
13703
  initialValue: false
@@ -14906,7 +13710,7 @@ async function promptOverwrite(configPath) {
14906
13710
  }
14907
13711
  async function promptStackConfirm() {
14908
13712
  try {
14909
- const { confirm, isCancel } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
13713
+ const { confirm, isCancel } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
14910
13714
  const res = await confirm({
14911
13715
  message: "Generate massu.config.yaml from detected stack?",
14912
13716
  initialValue: true
@@ -14930,7 +13734,7 @@ var init_init = __esm({
14930
13734
  init_detect();
14931
13735
  init_drift();
14932
13736
  __filename2 = fileURLToPath2(import.meta.url);
14933
- __dirname2 = dirname6(__filename2);
13737
+ __dirname2 = dirname5(__filename2);
14934
13738
  FRAMEWORK_TO_TEMPLATE_ID = {
14935
13739
  rails: "rails",
14936
13740
  phoenix: "phoenix",
@@ -14952,7 +13756,7 @@ var init_init = __esm({
14952
13756
  });
14953
13757
 
14954
13758
  // src/license.ts
14955
- import { createHash as createHash5 } from "crypto";
13759
+ import { createHash as createHash4 } from "crypto";
14956
13760
  function tierLevel(tier) {
14957
13761
  return TIER_LEVELS[tier] ?? 0;
14958
13762
  }
@@ -14977,7 +13781,7 @@ function annotateToolDefinitions(defs) {
14977
13781
  });
14978
13782
  }
14979
13783
  async function validateLicense(apiKey) {
14980
- const keyHash = createHash5("sha256").update(apiKey).digest("hex");
13784
+ const keyHash = createHash4("sha256").update(apiKey).digest("hex");
14981
13785
  const memDb = getMemoryDb();
14982
13786
  try {
14983
13787
  const cached = memDb.prepare(
@@ -15038,7 +13842,7 @@ async function validateLicense(apiKey) {
15038
13842
  }
15039
13843
  }
15040
13844
  function updateLicenseCache(apiKey, tier, validUntil, features = []) {
15041
- const keyHash = createHash5("sha256").update(apiKey).digest("hex");
13845
+ const keyHash = createHash4("sha256").update(apiKey).digest("hex");
15042
13846
  const memDb = getMemoryDb();
15043
13847
  try {
15044
13848
  memDb.prepare(`
@@ -15246,8 +14050,8 @@ __export(doctor_exports, {
15246
14050
  runDoctor: () => runDoctor,
15247
14051
  runValidateConfig: () => runValidateConfig
15248
14052
  });
15249
- import { existsSync as existsSync11, readFileSync as readFileSync12, readdirSync as readdirSync12 } from "fs";
15250
- import { resolve as resolve7, dirname as dirname7 } from "path";
14053
+ import { existsSync as existsSync11, readFileSync as readFileSync11, readdirSync as readdirSync11 } from "fs";
14054
+ import { resolve as resolve7, dirname as dirname6 } from "path";
15251
14055
  import { fileURLToPath as fileURLToPath3 } from "url";
15252
14056
  import { parse as parseYaml4 } from "yaml";
15253
14057
  function checkConfig(projectRoot) {
@@ -15256,7 +14060,7 @@ function checkConfig(projectRoot) {
15256
14060
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
15257
14061
  }
15258
14062
  try {
15259
- const content = readFileSync12(configPath, "utf-8");
14063
+ const content = readFileSync11(configPath, "utf-8");
15260
14064
  const parsed = parseYaml4(content);
15261
14065
  if (!parsed || typeof parsed !== "object") {
15262
14066
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
@@ -15272,7 +14076,7 @@ function checkMcpServer(projectRoot) {
15272
14076
  return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
15273
14077
  }
15274
14078
  try {
15275
- const content = JSON.parse(readFileSync12(mcpPath, "utf-8"));
14079
+ const content = JSON.parse(readFileSync11(mcpPath, "utf-8"));
15276
14080
  const servers = content.mcpServers ?? {};
15277
14081
  if (!servers.massu) {
15278
14082
  return { name: "MCP Server", status: "fail", detail: "massu not registered in .mcp.json. Run: npx massu init" };
@@ -15288,7 +14092,7 @@ function checkHooksConfig(projectRoot) {
15288
14092
  return { name: "Hooks Config", status: "fail", detail: ".claude/settings.local.json not found. Run: npx massu init" };
15289
14093
  }
15290
14094
  try {
15291
- const content = JSON.parse(readFileSync12(settingsPath, "utf-8"));
14095
+ const content = JSON.parse(readFileSync11(settingsPath, "utf-8"));
15292
14096
  if (!content.hooks) {
15293
14097
  return { name: "Hooks Config", status: "fail", detail: "No hooks configured. Run: npx massu install-hooks" };
15294
14098
  }
@@ -15399,7 +14203,7 @@ function checkShellHooksWired(_projectRoot2) {
15399
14203
  };
15400
14204
  }
15401
14205
  try {
15402
- const content = JSON.parse(readFileSync12(settingsPath, "utf-8"));
14206
+ const content = JSON.parse(readFileSync11(settingsPath, "utf-8"));
15403
14207
  const hooks = content.hooks ?? {};
15404
14208
  const hasSessionStart = Array.isArray(hooks.SessionStart) && hooks.SessionStart.length > 0;
15405
14209
  const hasPreToolUse = Array.isArray(hooks.PreToolUse) && hooks.PreToolUse.length > 0;
@@ -15465,7 +14269,7 @@ function checkPythonHealth(projectRoot) {
15465
14269
  function scanDir(dir, depth) {
15466
14270
  if (depth > 5) return;
15467
14271
  try {
15468
- const entries = readdirSync12(dir, { withFileTypes: true });
14272
+ const entries = readdirSync11(dir, { withFileTypes: true });
15469
14273
  for (const entry of entries) {
15470
14274
  if (entry.isDirectory()) {
15471
14275
  const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
@@ -15473,7 +14277,7 @@ function checkPythonHealth(projectRoot) {
15473
14277
  const subdir = resolve7(dir, entry.name);
15474
14278
  if (depth <= 2 && !existsSync11(resolve7(subdir, "__init__.py"))) {
15475
14279
  try {
15476
- const subEntries = readdirSync12(subdir);
14280
+ const subEntries = readdirSync11(subdir);
15477
14281
  if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
15478
14282
  initPyMissing.push(entry.name);
15479
14283
  }
@@ -15574,7 +14378,7 @@ async function runValidateConfig() {
15574
14378
  return;
15575
14379
  }
15576
14380
  try {
15577
- const content = readFileSync12(configPath, "utf-8");
14381
+ const content = readFileSync11(configPath, "utf-8");
15578
14382
  const parsed = parseYaml4(content);
15579
14383
  if (!parsed || typeof parsed !== "object") {
15580
14384
  console.error("Error: massu.config.yaml is empty or not a valid YAML object");
@@ -15613,7 +14417,7 @@ var init_doctor = __esm({
15613
14417
  init_config();
15614
14418
  init_license();
15615
14419
  __filename3 = fileURLToPath3(import.meta.url);
15616
- __dirname3 = dirname7(__filename3);
14420
+ __dirname3 = dirname6(__filename3);
15617
14421
  EXPECTED_HOOKS = [
15618
14422
  "session-start.js",
15619
14423
  "session-end.js",
@@ -15659,7 +14463,7 @@ var show_template_exports = {};
15659
14463
  __export(show_template_exports, {
15660
14464
  runShowTemplate: () => runShowTemplate
15661
14465
  });
15662
- import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
14466
+ import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
15663
14467
  import { resolve as resolve8 } from "path";
15664
14468
  function normalizeBaseName(input) {
15665
14469
  return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
@@ -15695,7 +14499,7 @@ async function runShowTemplate(args2) {
15695
14499
  process.exit(1);
15696
14500
  return;
15697
14501
  }
15698
- process.stdout.write(readFileSync13(file, "utf-8"));
14502
+ process.stdout.write(readFileSync12(file, "utf-8"));
15699
14503
  }
15700
14504
  var init_show_template = __esm({
15701
14505
  "src/commands/show-template.ts"() {
@@ -15748,12 +14552,12 @@ var init_passthrough = __esm({
15748
14552
  });
15749
14553
 
15750
14554
  // src/lib/fileLock.ts
15751
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
15752
- import { dirname as dirname8 } from "path";
14555
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync13, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
14556
+ import { dirname as dirname7 } from "path";
15753
14557
  import * as lockfile from "proper-lockfile";
15754
14558
  function readLockHolderPid(lockPath) {
15755
14559
  try {
15756
- const raw = readFileSync14(`${lockPath}.pid`, "utf-8").trim();
14560
+ const raw = readFileSync13(`${lockPath}.pid`, "utf-8").trim();
15757
14561
  const pid = Number.parseInt(raw, 10);
15758
14562
  if (!Number.isFinite(pid) || pid <= 0) return null;
15759
14563
  return pid;
@@ -15772,7 +14576,7 @@ function busyWaitSync(ms) {
15772
14576
  Atomics.wait(view, 0, 0, ms);
15773
14577
  }
15774
14578
  function withFileLockSync(lockPath, fn, opts = {}) {
15775
- mkdirSync6(dirname8(lockPath), { recursive: true });
14579
+ mkdirSync5(dirname7(lockPath), { recursive: true });
15776
14580
  const staleMs = opts.staleMs ?? 3e4;
15777
14581
  const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
15778
14582
  const pollIntervalMs = opts.pollIntervalMs ?? 100;
@@ -15789,7 +14593,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
15789
14593
  realpath: false
15790
14594
  });
15791
14595
  try {
15792
- writeFileSync4(`${lockPath}.pid`, String(process.pid), "utf-8");
14596
+ writeFileSync3(`${lockPath}.pid`, String(process.pid), "utf-8");
15793
14597
  } catch {
15794
14598
  }
15795
14599
  break;
@@ -15878,7 +14682,7 @@ __export(config_refresh_exports, {
15878
14682
  mergeRefresh: () => mergeRefresh,
15879
14683
  runConfigRefresh: () => runConfigRefresh
15880
14684
  });
15881
- import { existsSync as existsSync13, readFileSync as readFileSync15, rmSync as rmSync4 } from "fs";
14685
+ import { existsSync as existsSync13, readFileSync as readFileSync14, rmSync as rmSync4 } from "fs";
15882
14686
  import { resolve as resolve10 } from "path";
15883
14687
  import { parse as parseYaml5 } from "yaml";
15884
14688
  function flatten(obj, prefix3 = "") {
@@ -16017,7 +14821,7 @@ async function runConfigRefresh(opts = {}) {
16017
14821
  }
16018
14822
  let existing;
16019
14823
  try {
16020
- const content = readFileSync15(configPath, "utf-8");
14824
+ const content = readFileSync14(configPath, "utf-8");
16021
14825
  const parsed = parseYaml5(content);
16022
14826
  if (!parsed || typeof parsed !== "object") {
16023
14827
  throw new Error("config is not a YAML object");
@@ -16062,7 +14866,7 @@ async function runConfigRefresh(opts = {}) {
16062
14866
  }
16063
14867
  log("Config diff:\n");
16064
14868
  log(renderDiff(diff));
16065
- const { confirm } = await Promise.resolve().then(() => (init_dist7(), dist_exports));
14869
+ const { confirm } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
16066
14870
  const apply = await confirm({ message: "Apply these changes to massu.config.yaml?" });
16067
14871
  if (apply !== true) {
16068
14872
  log("Aborted; no changes written.\n");
@@ -16415,8 +15219,8 @@ var init_paths = __esm({
16415
15219
  });
16416
15220
 
16417
15221
  // src/watch/state.ts
16418
- import { closeSync as closeSync3, existsSync as existsSync15, fsyncSync as fsyncSync3, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync16, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync5, writeSync as writeSync3 } from "fs";
16419
- import { dirname as dirname9, resolve as resolve12 } from "path";
15222
+ import { closeSync as closeSync3, existsSync as existsSync15, fsyncSync as fsyncSync3, mkdirSync as mkdirSync6, openSync as openSync3, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync4, writeSync as writeSync3 } from "fs";
15223
+ import { dirname as dirname8, resolve as resolve12 } from "path";
16420
15224
  function watchStatePath(projectRoot) {
16421
15225
  return resolve12(projectRoot, ".massu", "watch-state.json");
16422
15226
  }
@@ -16426,7 +15230,7 @@ function backupStatePath(projectRoot) {
16426
15230
  function readState(projectRoot) {
16427
15231
  const path = watchStatePath(projectRoot);
16428
15232
  if (!existsSync15(path)) return { ...DEFAULT_STATE };
16429
- const content = readFileSync16(path, "utf-8");
15233
+ const content = readFileSync15(path, "utf-8");
16430
15234
  let raw;
16431
15235
  try {
16432
15236
  raw = JSON.parse(content);
@@ -16468,14 +15272,14 @@ function readState(projectRoot) {
16468
15272
  }
16469
15273
  function archiveCorrupt(projectRoot, content) {
16470
15274
  const bak = backupStatePath(projectRoot);
16471
- mkdirSync7(dirname9(bak), { recursive: true });
16472
- writeFileSync5(bak, content, "utf-8");
15275
+ mkdirSync6(dirname8(bak), { recursive: true });
15276
+ writeFileSync4(bak, content, "utf-8");
16473
15277
  }
16474
15278
  function writeStateAtomic(projectRoot, state) {
16475
15279
  const path = watchStatePath(projectRoot);
16476
15280
  writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
16477
15281
  const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
16478
- mkdirSync7(dirname9(path), { recursive: true });
15282
+ mkdirSync6(dirname8(path), { recursive: true });
16479
15283
  let renamed = false;
16480
15284
  try {
16481
15285
  const fd = openSync3(tmp, "w");
@@ -16486,7 +15290,7 @@ function writeStateAtomic(projectRoot, state) {
16486
15290
  } finally {
16487
15291
  closeSync3(fd);
16488
15292
  }
16489
- renameSync5(tmp, path);
15293
+ renameSync4(tmp, path);
16490
15294
  renamed = true;
16491
15295
  } finally {
16492
15296
  if (!renamed && existsSync15(tmp)) {
@@ -16769,8 +15573,8 @@ __export(watch_exports, {
16769
15573
  runWatch: () => runWatch
16770
15574
  });
16771
15575
  import { spawnSync as spawnSync3 } from "child_process";
16772
- import { basename as basename5, dirname as dirname10, resolve as resolve13 } from "path";
16773
- import { appendFileSync, existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "fs";
15576
+ import { basename as basename5, dirname as dirname9, resolve as resolve13 } from "path";
15577
+ import { appendFileSync, existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync16 } from "fs";
16774
15578
  function parseFlags(args2) {
16775
15579
  const out = {
16776
15580
  foreground: false,
@@ -17015,7 +15819,7 @@ function refreshLogPath(projectRoot) {
17015
15819
  function appendRefreshLog(projectRoot, event) {
17016
15820
  const path = refreshLogPath(projectRoot);
17017
15821
  try {
17018
- mkdirSync8(dirname10(path), { recursive: true });
15822
+ mkdirSync7(dirname9(path), { recursive: true });
17019
15823
  appendFileSync(path, JSON.stringify(event) + "\n", "utf-8");
17020
15824
  } catch {
17021
15825
  }
@@ -17026,7 +15830,7 @@ function readRefreshLog(projectRoot, limit = 10, opts = {}) {
17026
15830
  const warn = opts.warn ?? ((s) => {
17027
15831
  process.stderr.write(s);
17028
15832
  });
17029
- const lines = readFileSync17(path, "utf-8").split("\n").filter(Boolean);
15833
+ const lines = readFileSync16(path, "utf-8").split("\n").filter(Boolean);
17030
15834
  const tail = lines.slice(-limit);
17031
15835
  const out = [];
17032
15836
  let corrupt = 0;
@@ -17097,28 +15901,28 @@ var init_refresh_log = __esm({
17097
15901
 
17098
15902
  // src/security/atomic-write.ts
17099
15903
  import {
17100
- chmodSync as chmodSync4,
15904
+ chmodSync as chmodSync3,
17101
15905
  closeSync as closeSync4,
17102
15906
  existsSync as existsSync17,
17103
15907
  fsyncSync as fsyncSync4,
17104
- mkdirSync as mkdirSync9,
15908
+ mkdirSync as mkdirSync8,
17105
15909
  openSync as openSync4,
17106
- renameSync as renameSync6,
15910
+ renameSync as renameSync5,
17107
15911
  rmSync as rmSync6,
17108
15912
  statSync as statSync9,
17109
15913
  writeSync as writeSync4
17110
15914
  } from "node:fs";
17111
- import { dirname as dirname11 } from "node:path";
15915
+ import { dirname as dirname10 } from "node:path";
17112
15916
  function atomicWrite(path, content, opts = {}) {
17113
15917
  const tmpPath = `${path}.tmp`;
17114
- const parentDir = dirname11(path);
15918
+ const parentDir = dirname10(path);
17115
15919
  try {
17116
15920
  if (!existsSync17(parentDir)) {
17117
15921
  const mkdirOpts = { recursive: true };
17118
15922
  if (opts.ensureParentDirMode !== void 0) {
17119
15923
  mkdirOpts.mode = opts.ensureParentDirMode;
17120
15924
  }
17121
- mkdirSync9(parentDir, mkdirOpts);
15925
+ mkdirSync8(parentDir, mkdirOpts);
17122
15926
  }
17123
15927
  const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
17124
15928
  const openMode = opts.mode ?? 420;
@@ -17130,9 +15934,9 @@ function atomicWrite(path, content, opts = {}) {
17130
15934
  closeSync4(fd);
17131
15935
  }
17132
15936
  if (opts.mode !== void 0) {
17133
- chmodSync4(tmpPath, opts.mode);
15937
+ chmodSync3(tmpPath, opts.mode);
17134
15938
  }
17135
- renameSync6(tmpPath, path);
15939
+ renameSync5(tmpPath, path);
17136
15940
  return { written: true };
17137
15941
  } catch (err) {
17138
15942
  if (existsSync17(tmpPath)) {
@@ -17215,10 +16019,10 @@ var init_registry_pubkey_generated = __esm({
17215
16019
  });
17216
16020
 
17217
16021
  // src/security/adapter-verifier.ts
17218
- import { createHash as createHash6 } from "node:crypto";
16022
+ import { createHash as createHash5 } from "node:crypto";
17219
16023
  import nacl from "tweetnacl";
17220
16024
  function sha256Hex(bytes) {
17221
- return createHash6("sha256").update(bytes).digest("hex");
16025
+ return createHash5("sha256").update(bytes).digest("hex");
17222
16026
  }
17223
16027
  function reviver(key, value) {
17224
16028
  if (key === "__proto__" || key === "constructor" || key === "prototype") {
@@ -17478,12 +16282,12 @@ var init_fetcher = __esm({
17478
16282
  });
17479
16283
 
17480
16284
  // src/security/manifest-cache.ts
17481
- import { existsSync as existsSync18, readFileSync as readFileSync18, statSync as statSync10 } from "node:fs";
17482
- import { homedir as homedir5 } from "node:os";
16285
+ import { existsSync as existsSync18, readFileSync as readFileSync17, statSync as statSync10 } from "node:fs";
16286
+ import { homedir as homedir4 } from "node:os";
17483
16287
  import { resolve as resolve14 } from "node:path";
17484
16288
  import { z as z4 } from "zod";
17485
16289
  function defaultCachePaths() {
17486
- const dir = resolve14(homedir5(), ".massu");
16290
+ const dir = resolve14(homedir4(), ".massu");
17487
16291
  return {
17488
16292
  cachePath: resolve14(dir, "adapter-manifest.json"),
17489
16293
  lockPath: resolve14(dir, ".adapter-manifest.lock")
@@ -17495,7 +16299,7 @@ function loadCachedManifest(paths = defaultCachePaths()) {
17495
16299
  }
17496
16300
  let raw;
17497
16301
  try {
17498
- const content = readFileSync18(paths.cachePath, "utf-8");
16302
+ const content = readFileSync17(paths.cachePath, "utf-8");
17499
16303
  raw = JSON.parse(content);
17500
16304
  } catch (err) {
17501
16305
  return {
@@ -17684,10 +16488,10 @@ var init_adapter_origin = __esm({
17684
16488
  });
17685
16489
 
17686
16490
  // src/security/local-fingerprint.ts
17687
- import { existsSync as existsSync19, readFileSync as readFileSync19, lstatSync as lstatSync6 } from "node:fs";
17688
- import { homedir as homedir6 } from "node:os";
16491
+ import { existsSync as existsSync19, readFileSync as readFileSync18, lstatSync as lstatSync5 } from "node:fs";
16492
+ import { homedir as homedir5 } from "node:os";
17689
16493
  import { resolve as resolve15, isAbsolute } from "node:path";
17690
- import { createHash as createHash7 } from "node:crypto";
16494
+ import { createHash as createHash6 } from "node:crypto";
17691
16495
  import { z as z5 } from "zod";
17692
16496
  function computeLocalFingerprint(localPaths, projectRoot) {
17693
16497
  const tuples = [];
@@ -17695,13 +16499,13 @@ function computeLocalFingerprint(localPaths, projectRoot) {
17695
16499
  const abs = isAbsolute(p19) ? p19 : resolve15(projectRoot, p19);
17696
16500
  let contentTag;
17697
16501
  try {
17698
- const lst = lstatSync6(abs);
16502
+ const lst = lstatSync5(abs);
17699
16503
  if (lst.isSymbolicLink()) {
17700
16504
  contentTag = "<symlink>";
17701
16505
  } else if (!lst.isFile()) {
17702
16506
  contentTag = "<not-a-file>";
17703
16507
  } else {
17704
- contentTag = createHash7("sha256").update(readFileSync19(abs)).digest("hex");
16508
+ contentTag = createHash6("sha256").update(readFileSync18(abs)).digest("hex");
17705
16509
  }
17706
16510
  } catch {
17707
16511
  contentTag = "<missing>";
@@ -17710,13 +16514,13 @@ function computeLocalFingerprint(localPaths, projectRoot) {
17710
16514
  }
17711
16515
  tuples.sort((a2, b2) => a2.path < b2.path ? -1 : a2.path > b2.path ? 1 : 0);
17712
16516
  const canonical = JSON.stringify(tuples);
17713
- return createHash7("sha256").update(canonical).digest("hex");
16517
+ return createHash6("sha256").update(canonical).digest("hex");
17714
16518
  }
17715
16519
  function readFingerprintSentinel(path = FINGERPRINT_PATH) {
17716
16520
  if (!existsSync19(path)) return null;
17717
16521
  let raw;
17718
16522
  try {
17719
- raw = JSON.parse(readFileSync19(path, "utf-8"));
16523
+ raw = JSON.parse(readFileSync18(path, "utf-8"));
17720
16524
  } catch {
17721
16525
  return null;
17722
16526
  }
@@ -17764,7 +16568,7 @@ var init_local_fingerprint = __esm({
17764
16568
  "use strict";
17765
16569
  init_atomic_write();
17766
16570
  init_manifest_schema();
17767
- FINGERPRINT_PATH = resolve15(homedir6(), ".massu", "adapters-local-fingerprint.json");
16571
+ FINGERPRINT_PATH = resolve15(homedir5(), ".massu", "adapters-local-fingerprint.json");
17768
16572
  FingerprintSentinelSchema = z5.object({
17769
16573
  fingerprint: z5.string().regex(/^[0-9a-f]{64}$/),
17770
16574
  source: z5.enum(["cli", "cli-resync"]),
@@ -17780,11 +16584,11 @@ var init_local_fingerprint = __esm({
17780
16584
  });
17781
16585
 
17782
16586
  // src/security/install-tracking.ts
17783
- import { readFileSync as readFileSync20, readdirSync as readdirSync13, lstatSync as lstatSync7, existsSync as existsSync20 } from "node:fs";
17784
- import { join as join11, relative as relative5, sep } from "node:path";
17785
- import { homedir as homedir7 } from "node:os";
16587
+ import { readFileSync as readFileSync19, readdirSync as readdirSync12, lstatSync as lstatSync6, existsSync as existsSync20 } from "node:fs";
16588
+ import { join as join10, relative as relative5, sep } from "node:path";
16589
+ import { homedir as homedir6 } from "node:os";
17786
16590
  import { resolve as resolve16 } from "node:path";
17787
- import { createHash as createHash8 } from "node:crypto";
16591
+ import { createHash as createHash7 } from "node:crypto";
17788
16592
  import { z as z6 } from "zod";
17789
16593
  function containsHiddenDirs(packageDir) {
17790
16594
  for (const hidden of EXCLUDED_DIR_NAMES) {
@@ -17800,15 +16604,15 @@ function sha256OfDir(dir, opts = {}) {
17800
16604
  function walk(currentDir) {
17801
16605
  let entries;
17802
16606
  try {
17803
- entries = readdirSync13(currentDir);
16607
+ entries = readdirSync12(currentDir);
17804
16608
  } catch {
17805
16609
  return;
17806
16610
  }
17807
16611
  for (const entry of entries.sort()) {
17808
- const absPath = join11(currentDir, entry);
16612
+ const absPath = join10(currentDir, entry);
17809
16613
  let lst;
17810
16614
  try {
17811
- lst = lstatSync7(absPath);
16615
+ lst = lstatSync6(absPath);
17812
16616
  } catch {
17813
16617
  continue;
17814
16618
  }
@@ -17832,9 +16636,9 @@ function sha256OfDir(dir, opts = {}) {
17832
16636
  }
17833
16637
  walk(dir);
17834
16638
  files.sort((a2, b2) => a2.relativePath < b2.relativePath ? -1 : a2.relativePath > b2.relativePath ? 1 : 0);
17835
- const top = createHash8("sha256");
16639
+ const top = createHash7("sha256");
17836
16640
  for (const f2 of files) {
17837
- const fileHash = createHash8("sha256").update(readFileSync20(f2.absPath)).digest("hex");
16641
+ const fileHash = createHash7("sha256").update(readFileSync19(f2.absPath)).digest("hex");
17838
16642
  top.update(f2.relativePath, "utf-8");
17839
16643
  top.update("\0", "utf-8");
17840
16644
  top.update(fileHash, "utf-8");
@@ -17846,7 +16650,7 @@ function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
17846
16650
  if (!existsSync20(path)) return {};
17847
16651
  let raw;
17848
16652
  try {
17849
- raw = JSON.parse(readFileSync20(path, "utf-8"));
16653
+ raw = JSON.parse(readFileSync19(path, "utf-8"));
17850
16654
  } catch {
17851
16655
  return {};
17852
16656
  }
@@ -17910,7 +16714,7 @@ var init_install_tracking = __esm({
17910
16714
  "src/security/install-tracking.ts"() {
17911
16715
  "use strict";
17912
16716
  init_atomic_write();
17913
- INSTALLED_MANIFEST_PATH = resolve16(homedir7(), ".massu", "adapter-manifest-installed.json");
16717
+ INSTALLED_MANIFEST_PATH = resolve16(homedir6(), ".massu", "adapter-manifest-installed.json");
17914
16718
  DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
17915
16719
  EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
17916
16720
  InstallEntrySchema = z6.object({
@@ -17933,7 +16737,7 @@ var init_install_tracking = __esm({
17933
16737
  });
17934
16738
 
17935
16739
  // src/detect/adapters/discover.ts
17936
- import { existsSync as existsSync21, readdirSync as readdirSync14, readFileSync as readFileSync21, lstatSync as lstatSync8 } from "node:fs";
16740
+ import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as readFileSync20, lstatSync as lstatSync7 } from "node:fs";
17937
16741
  import { resolve as resolve17, isAbsolute as isAbsolute2 } from "node:path";
17938
16742
  import { z as z7 } from "zod";
17939
16743
  function walkNodeModules(projectRoot, warnings) {
@@ -17944,7 +16748,7 @@ function walkNodeModules(projectRoot, warnings) {
17944
16748
  const candidates = [];
17945
16749
  let topLevelEntries;
17946
16750
  try {
17947
- topLevelEntries = readdirSync14(nodeModulesDir);
16751
+ topLevelEntries = readdirSync13(nodeModulesDir);
17948
16752
  } catch (err) {
17949
16753
  warnings.push(`failed to read node_modules: ${err instanceof Error ? err.message : String(err)}`);
17950
16754
  return [];
@@ -17954,7 +16758,7 @@ function walkNodeModules(projectRoot, warnings) {
17954
16758
  const entryPath = resolve17(nodeModulesDir, entry);
17955
16759
  let entryStat;
17956
16760
  try {
17957
- entryStat = lstatSync8(entryPath);
16761
+ entryStat = lstatSync7(entryPath);
17958
16762
  } catch {
17959
16763
  continue;
17960
16764
  }
@@ -17968,7 +16772,7 @@ function walkNodeModules(projectRoot, warnings) {
17968
16772
  if (entry.startsWith("@")) {
17969
16773
  let scopedEntries;
17970
16774
  try {
17971
- scopedEntries = readdirSync14(entryPath);
16775
+ scopedEntries = readdirSync13(entryPath);
17972
16776
  } catch {
17973
16777
  continue;
17974
16778
  }
@@ -17976,7 +16780,7 @@ function walkNodeModules(projectRoot, warnings) {
17976
16780
  const subPath = resolve17(entryPath, sub);
17977
16781
  let subStat;
17978
16782
  try {
17979
- subStat = lstatSync8(subPath);
16783
+ subStat = lstatSync7(subPath);
17980
16784
  } catch {
17981
16785
  continue;
17982
16786
  }
@@ -17996,7 +16800,7 @@ function tryReadAdapterPackage(packageDir, warnings) {
17996
16800
  if (!existsSync21(pkgJsonPath)) return null;
17997
16801
  let raw;
17998
16802
  try {
17999
- raw = JSON.parse(readFileSync21(pkgJsonPath, "utf-8"));
16803
+ raw = JSON.parse(readFileSync20(pkgJsonPath, "utf-8"));
18000
16804
  } catch (err) {
18001
16805
  warnings.push(
18002
16806
  `skipping ${packageDir}: package.json parse failed (${err instanceof Error ? err.message : String(err)})`
@@ -18212,7 +17016,7 @@ __export(adapters_exports, {
18212
17016
  runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
18213
17017
  runAdaptersSearch: () => runAdaptersSearch
18214
17018
  });
18215
- import { existsSync as existsSync22, readFileSync as readFileSync22 } from "node:fs";
17019
+ import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
18216
17020
  import { resolve as resolve18 } from "node:path";
18217
17021
  import { parseDocument } from "yaml";
18218
17022
  async function handleAdaptersSubcommand(args2) {
@@ -18490,7 +17294,7 @@ function mutateLocalArray(mutator, command) {
18490
17294
  }
18491
17295
  let yamlText;
18492
17296
  try {
18493
- yamlText = readFileSync22(yamlPath, "utf-8");
17297
+ yamlText = readFileSync21(yamlPath, "utf-8");
18494
17298
  } catch (err) {
18495
17299
  process.stderr.write(`${command}: failed to read ${yamlPath}: ${err instanceof Error ? err.message : String(err)}
18496
17300
  `);
@@ -18596,7 +17400,7 @@ async function runAdaptersInstall(args2) {
18596
17400
  }
18597
17401
  let pkgJson;
18598
17402
  try {
18599
- pkgJson = JSON.parse(readFileSync22(pkgJsonPath, "utf-8"));
17403
+ pkgJson = JSON.parse(readFileSync21(pkgJsonPath, "utf-8"));
18600
17404
  } catch (err) {
18601
17405
  process.stderr.write(`install: ${packageName} has malformed package.json: ${err instanceof Error ? err.message : String(err)}
18602
17406
  `);
@@ -18803,12 +17607,12 @@ var init_adapters2 = __esm({
18803
17607
 
18804
17608
  // src/db.ts
18805
17609
  import Database2 from "better-sqlite3";
18806
- import { dirname as dirname12, join as join12 } from "path";
18807
- import { existsSync as existsSync23, mkdirSync as mkdirSync10, readdirSync as readdirSync15, statSync as statSync11 } from "fs";
17610
+ import { dirname as dirname11, join as join11 } from "path";
17611
+ import { existsSync as existsSync23, mkdirSync as mkdirSync9, readdirSync as readdirSync14, statSync as statSync11 } from "fs";
18808
17612
  function getCodeGraphDb() {
18809
17613
  const dbPath = getResolvedPaths().codegraphDbPath;
18810
17614
  if (!existsSync23(dbPath)) {
18811
- throw new Error(`CodeGraph database not found at ${dbPath}. Run 'npx @colbymchenry/codegraph sync' first.`);
17615
+ throw new CodegraphDbNotInitializedError(dbPath);
18812
17616
  }
18813
17617
  const db = new Database2(dbPath, { readonly: true });
18814
17618
  db.pragma("journal_mode = WAL");
@@ -18816,9 +17620,9 @@ function getCodeGraphDb() {
18816
17620
  }
18817
17621
  function getDataDb() {
18818
17622
  const dbPath = getResolvedPaths().dataDbPath;
18819
- const dir = dirname12(dbPath);
17623
+ const dir = dirname11(dbPath);
18820
17624
  if (!existsSync23(dir)) {
18821
- mkdirSync10(dir, { recursive: true });
17625
+ mkdirSync9(dir, { recursive: true });
18822
17626
  }
18823
17627
  const db = new Database2(dbPath);
18824
17628
  db.pragma("journal_mode = WAL");
@@ -19072,25 +17876,25 @@ function initDataSchema(db) {
19072
17876
  END;
19073
17877
  `);
19074
17878
  }
19075
- function isDataStale(dataDb2, codegraphDb2) {
19076
- const lastBuild = dataDb2.prepare("SELECT value FROM massu_meta WHERE key = 'last_build_time'").get();
17879
+ function isDataStale(dataDb, codegraphDb) {
17880
+ const lastBuild = dataDb.prepare("SELECT value FROM massu_meta WHERE key = 'last_build_time'").get();
19077
17881
  if (!lastBuild) return true;
19078
- const latestIndexed = codegraphDb2.prepare("SELECT MAX(indexed_at) as latest FROM files").get();
17882
+ const latestIndexed = codegraphDb.prepare("SELECT MAX(indexed_at) as latest FROM files").get();
19079
17883
  if (!latestIndexed?.latest) return true;
19080
17884
  return latestIndexed.latest * 1e3 > new Date(lastBuild.value).getTime();
19081
17885
  }
19082
- function updateBuildTimestamp(dataDb2) {
19083
- dataDb2.prepare("INSERT OR REPLACE INTO massu_meta (key, value) VALUES ('last_build_time', ?)").run((/* @__PURE__ */ new Date()).toISOString());
17886
+ function updateBuildTimestamp(dataDb) {
17887
+ dataDb.prepare("INSERT OR REPLACE INTO massu_meta (key, value) VALUES ('last_build_time', ?)").run((/* @__PURE__ */ new Date()).toISOString());
19084
17888
  }
19085
- function isPythonDataStale(dataDb2, pythonRoot) {
19086
- const lastBuild = dataDb2.prepare("SELECT value FROM massu_py_meta WHERE key = 'last_build_time'").get();
17889
+ function isPythonDataStale(dataDb, pythonRoot) {
17890
+ const lastBuild = dataDb.prepare("SELECT value FROM massu_py_meta WHERE key = 'last_build_time'").get();
19087
17891
  if (!lastBuild) return true;
19088
17892
  const lastBuildTime = new Date(lastBuild.value).getTime();
19089
17893
  function checkDir(dir) {
19090
17894
  try {
19091
- const entries = readdirSync15(dir, { withFileTypes: true });
17895
+ const entries = readdirSync14(dir, { withFileTypes: true });
19092
17896
  for (const entry of entries) {
19093
- const fullPath = join12(dir, entry.name);
17897
+ const fullPath = join11(dir, entry.name);
19094
17898
  if (entry.isDirectory()) {
19095
17899
  if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
19096
17900
  if (checkDir(fullPath)) return true;
@@ -19104,13 +17908,22 @@ function isPythonDataStale(dataDb2, pythonRoot) {
19104
17908
  }
19105
17909
  return checkDir(pythonRoot);
19106
17910
  }
19107
- function updatePythonBuildTimestamp(dataDb2) {
19108
- dataDb2.prepare("INSERT OR REPLACE INTO massu_py_meta (key, value) VALUES ('last_build_time', ?)").run((/* @__PURE__ */ new Date()).toISOString());
17911
+ function updatePythonBuildTimestamp(dataDb) {
17912
+ dataDb.prepare("INSERT OR REPLACE INTO massu_py_meta (key, value) VALUES ('last_build_time', ?)").run((/* @__PURE__ */ new Date()).toISOString());
19109
17913
  }
17914
+ var CodegraphDbNotInitializedError;
19110
17915
  var init_db = __esm({
19111
17916
  "src/db.ts"() {
19112
17917
  "use strict";
19113
17918
  init_config();
17919
+ CodegraphDbNotInitializedError = class extends Error {
17920
+ dbPath;
17921
+ constructor(dbPath) {
17922
+ super(`CodeGraph database not found at ${dbPath}`);
17923
+ this.name = "CodegraphDbNotInitializedError";
17924
+ this.dbPath = dbPath;
17925
+ }
17926
+ };
19114
17927
  }
19115
17928
  });
19116
17929
 
@@ -19191,8 +18004,8 @@ var init_rules = __esm({
19191
18004
  });
19192
18005
 
19193
18006
  // src/import-resolver.ts
19194
- import { readFileSync as readFileSync23, existsSync as existsSync24, statSync as statSync12 } from "fs";
19195
- import { resolve as resolve20, dirname as dirname13, join as join13 } from "path";
18007
+ import { readFileSync as readFileSync22, existsSync as existsSync24, statSync as statSync12 } from "fs";
18008
+ import { resolve as resolve20, dirname as dirname12, join as join12 } from "path";
19196
18009
  function parseImports(source) {
19197
18010
  const imports = [];
19198
18011
  const lines = source.split("\n");
@@ -19250,7 +18063,7 @@ function resolveImportPath(specifier, fromFile) {
19250
18063
  const paths = getResolvedPaths();
19251
18064
  basePath = resolve20(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
19252
18065
  } else {
19253
- basePath = resolve20(dirname13(fromFile), specifier);
18066
+ basePath = resolve20(dirname12(fromFile), specifier);
19254
18067
  }
19255
18068
  if (existsSync24(basePath) && !isDirectory(basePath)) {
19256
18069
  return toRelative(basePath);
@@ -19263,7 +18076,7 @@ function resolveImportPath(specifier, fromFile) {
19263
18076
  }
19264
18077
  }
19265
18078
  for (const indexFile of resolvedPaths.indexFiles) {
19266
- const indexPath = join13(basePath, indexFile);
18079
+ const indexPath = join12(basePath, indexFile);
19267
18080
  if (existsSync24(indexPath)) {
19268
18081
  return toRelative(indexPath);
19269
18082
  }
@@ -19284,15 +18097,15 @@ function toRelative(absPath) {
19284
18097
  }
19285
18098
  return absPath;
19286
18099
  }
19287
- function buildImportIndex(dataDb2, codegraphDb2) {
19288
- const files = codegraphDb2.prepare("SELECT path FROM files WHERE path LIKE 'src/%'").all();
19289
- dataDb2.exec("DELETE FROM massu_imports");
19290
- const insertStmt = dataDb2.prepare(
18100
+ function buildImportIndex(dataDb, codegraphDb) {
18101
+ const files = codegraphDb.prepare("SELECT path FROM files WHERE path LIKE 'src/%'").all();
18102
+ dataDb.exec("DELETE FROM massu_imports");
18103
+ const insertStmt = dataDb.prepare(
19291
18104
  "INSERT INTO massu_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)"
19292
18105
  );
19293
18106
  let edgeCount = 0;
19294
18107
  const projectRoot = getProjectRoot();
19295
- const insertMany = dataDb2.transaction((edges) => {
18108
+ const insertMany = dataDb.transaction((edges) => {
19296
18109
  for (const edge of edges) {
19297
18110
  insertStmt.run(edge.source_file, edge.target_file, edge.import_type, edge.imported_names, edge.line);
19298
18111
  }
@@ -19304,7 +18117,7 @@ function buildImportIndex(dataDb2, codegraphDb2) {
19304
18117
  if (!existsSync24(absPath)) continue;
19305
18118
  let source;
19306
18119
  try {
19307
- source = readFileSync23(absPath, "utf-8");
18120
+ source = readFileSync22(absPath, "utf-8");
19308
18121
  } catch {
19309
18122
  continue;
19310
18123
  }
@@ -19340,15 +18153,15 @@ var init_import_resolver = __esm({
19340
18153
  });
19341
18154
 
19342
18155
  // src/trpc-index.ts
19343
- import { readFileSync as readFileSync24, existsSync as existsSync25, readdirSync as readdirSync16 } from "fs";
19344
- import { resolve as resolve21, join as join14 } from "path";
18156
+ import { readFileSync as readFileSync23, existsSync as existsSync25, readdirSync as readdirSync15 } from "fs";
18157
+ import { resolve as resolve21, join as join13 } from "path";
19345
18158
  function parseRootRouter() {
19346
18159
  const paths = getResolvedPaths();
19347
18160
  const rootPath = paths.rootRouterPath;
19348
18161
  if (!existsSync25(rootPath)) {
19349
18162
  throw new Error(`Root router not found at ${rootPath}`);
19350
18163
  }
19351
- const source = readFileSync24(rootPath, "utf-8");
18164
+ const source = readFileSync23(rootPath, "utf-8");
19352
18165
  const mappings = [];
19353
18166
  const importMap = /* @__PURE__ */ new Map();
19354
18167
  const importRegex = /import\s+\{[^}]*?(\w+Router)[^}]*\}\s+from\s+['"]\.\/routers\/([^'"]+)['"]/g;
@@ -19364,7 +18177,7 @@ function parseRootRouter() {
19364
18177
  filePath = routersRelPath + "/" + filePath + ext;
19365
18178
  break;
19366
18179
  }
19367
- const indexCandidate = join14(fullPath, "index.ts");
18180
+ const indexCandidate = join13(fullPath, "index.ts");
19368
18181
  if (existsSync25(indexCandidate)) {
19369
18182
  filePath = routersRelPath + "/" + filePath + "/index.ts";
19370
18183
  break;
@@ -19386,7 +18199,7 @@ function parseRootRouter() {
19386
18199
  function extractProcedures(routerFilePath) {
19387
18200
  const absPath = resolve21(getProjectRoot(), routerFilePath);
19388
18201
  if (!existsSync25(absPath)) return [];
19389
- const source = readFileSync24(absPath, "utf-8");
18202
+ const source = readFileSync23(absPath, "utf-8");
19390
18203
  const procedures = [];
19391
18204
  const seen = /* @__PURE__ */ new Set();
19392
18205
  const procRegex = /(\w+)\s*:\s*(protected|public)Procedure/g;
@@ -19421,15 +18234,15 @@ function findUICallSites(routerKey, procedureName) {
19421
18234
  return callSites;
19422
18235
  }
19423
18236
  function searchDirectory(dir, pattern, results) {
19424
- const entries = readdirSync16(dir, { withFileTypes: true });
18237
+ const entries = readdirSync15(dir, { withFileTypes: true });
19425
18238
  for (const entry of entries) {
19426
- const fullPath = join14(dir, entry.name);
18239
+ const fullPath = join13(dir, entry.name);
19427
18240
  if (entry.isDirectory()) {
19428
18241
  if (entry.name === "node_modules" || entry.name === ".next") continue;
19429
18242
  searchDirectory(fullPath, pattern, results);
19430
18243
  } else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
19431
18244
  try {
19432
- const source = readFileSync24(fullPath, "utf-8");
18245
+ const source = readFileSync23(fullPath, "utf-8");
19433
18246
  const lines = source.split("\n");
19434
18247
  for (let i = 0; i < lines.length; i++) {
19435
18248
  if (lines[i].includes(pattern)) {
@@ -19451,20 +18264,20 @@ function searchDirectory(dir, pattern, results) {
19451
18264
  function escapeRegex2(str) {
19452
18265
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
19453
18266
  }
19454
- function buildTrpcIndex(dataDb2) {
19455
- dataDb2.exec("DELETE FROM massu_trpc_call_sites");
19456
- dataDb2.exec("DELETE FROM massu_trpc_procedures");
18267
+ function buildTrpcIndex(dataDb) {
18268
+ dataDb.exec("DELETE FROM massu_trpc_call_sites");
18269
+ dataDb.exec("DELETE FROM massu_trpc_procedures");
19457
18270
  const routerMappings = parseRootRouter();
19458
- const insertProc = dataDb2.prepare(
18271
+ const insertProc = dataDb.prepare(
19459
18272
  "INSERT INTO massu_trpc_procedures (router_file, router_name, procedure_name, procedure_type, has_ui_caller) VALUES (?, ?, ?, ?, ?)"
19460
18273
  );
19461
- const insertCallSite = dataDb2.prepare(
18274
+ const insertCallSite = dataDb.prepare(
19462
18275
  "INSERT INTO massu_trpc_call_sites (procedure_id, file, line, call_pattern) VALUES (?, ?, ?, ?)"
19463
18276
  );
19464
18277
  let totalProcedures = 0;
19465
18278
  let withCallers = 0;
19466
18279
  let withoutCallers = 0;
19467
- const insertAll = dataDb2.transaction(() => {
18280
+ const insertAll = dataDb.transaction(() => {
19468
18281
  for (const router of routerMappings) {
19469
18282
  const procedures = extractProcedures(router.file);
19470
18283
  for (const proc of procedures) {
@@ -19492,7 +18305,7 @@ var init_trpc_index = __esm({
19492
18305
  });
19493
18306
 
19494
18307
  // src/page-deps.ts
19495
- import { readFileSync as readFileSync25, existsSync as existsSync26 } from "fs";
18308
+ import { readFileSync as readFileSync24, existsSync as existsSync26 } from "fs";
19496
18309
  import { resolve as resolve22 } from "path";
19497
18310
  function deriveRoute(pageFile) {
19498
18311
  let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
@@ -19508,10 +18321,10 @@ function derivePortal(route) {
19508
18321
  const parts = route.split("/").filter(Boolean);
19509
18322
  return parts[0] ?? "default";
19510
18323
  }
19511
- function traceImports(startFile, dataDb2, visited, components, hooks, maxDepth = 5) {
18324
+ function traceImports(startFile, dataDb, visited, components, hooks, maxDepth = 5) {
19512
18325
  if (maxDepth <= 0 || visited.has(startFile)) return;
19513
18326
  visited.add(startFile);
19514
- const imports = dataDb2.prepare(
18327
+ const imports = dataDb.prepare(
19515
18328
  "SELECT target_file, imported_names FROM massu_imports WHERE source_file = ?"
19516
18329
  ).all(startFile);
19517
18330
  for (const imp of imports) {
@@ -19523,7 +18336,7 @@ function traceImports(startFile, dataDb2, visited, components, hooks, maxDepth =
19523
18336
  hooks.add(target);
19524
18337
  }
19525
18338
  if (target.startsWith("src/")) {
19526
- traceImports(target, dataDb2, visited, components, hooks, maxDepth - 1);
18339
+ traceImports(target, dataDb, visited, components, hooks, maxDepth - 1);
19527
18340
  }
19528
18341
  }
19529
18342
  }
@@ -19534,7 +18347,7 @@ function findRouterCalls(files) {
19534
18347
  const absPath = ensureWithinRoot(resolve22(projectRoot, file), projectRoot);
19535
18348
  if (!existsSync26(absPath)) continue;
19536
18349
  try {
19537
- const source = readFileSync25(absPath, "utf-8");
18350
+ const source = readFileSync24(absPath, "utf-8");
19538
18351
  const apiCallRegex = /api\.(\w+)\.\w+/g;
19539
18352
  let match;
19540
18353
  while ((match = apiCallRegex.exec(source)) !== null) {
@@ -19545,17 +18358,17 @@ function findRouterCalls(files) {
19545
18358
  }
19546
18359
  return [...routers];
19547
18360
  }
19548
- function findTablesFromRouters(routerNames, dataDb2) {
18361
+ function findTablesFromRouters(routerNames, dataDb) {
19549
18362
  const tables = /* @__PURE__ */ new Set();
19550
18363
  for (const routerName of routerNames) {
19551
- const procs = dataDb2.prepare(
18364
+ const procs = dataDb.prepare(
19552
18365
  "SELECT DISTINCT router_file FROM massu_trpc_procedures WHERE router_name = ?"
19553
18366
  ).all(routerName);
19554
18367
  for (const proc of procs) {
19555
18368
  const absPath = ensureWithinRoot(resolve22(getProjectRoot(), proc.router_file), getProjectRoot());
19556
18369
  if (!existsSync26(absPath)) continue;
19557
18370
  try {
19558
- const source = readFileSync25(absPath, "utf-8");
18371
+ const source = readFileSync24(absPath, "utf-8");
19559
18372
  const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
19560
18373
  const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
19561
18374
  const tableRegex = new RegExp(regexStr + "\\.", "g");
@@ -19569,26 +18382,26 @@ function findTablesFromRouters(routerNames, dataDb2) {
19569
18382
  }
19570
18383
  return [...tables];
19571
18384
  }
19572
- function buildPageDeps(dataDb2, codegraphDb2) {
19573
- dataDb2.exec("DELETE FROM massu_page_deps");
19574
- const pages = codegraphDb2.prepare(
18385
+ function buildPageDeps(dataDb, codegraphDb) {
18386
+ dataDb.exec("DELETE FROM massu_page_deps");
18387
+ const pages = codegraphDb.prepare(
19575
18388
  "SELECT path FROM files WHERE path LIKE 'src/app/%/page.tsx' OR path = 'src/app/page.tsx'"
19576
18389
  ).all();
19577
- const insertStmt = dataDb2.prepare(
18390
+ const insertStmt = dataDb.prepare(
19578
18391
  "INSERT INTO massu_page_deps (page_file, route, portal, components, hooks, routers, tables_touched) VALUES (?, ?, ?, ?, ?, ?, ?)"
19579
18392
  );
19580
18393
  let count = 0;
19581
- const insertAll = dataDb2.transaction(() => {
18394
+ const insertAll = dataDb.transaction(() => {
19582
18395
  for (const page of pages) {
19583
18396
  const route = deriveRoute(page.path);
19584
18397
  const portal = derivePortal(route);
19585
18398
  const visited = /* @__PURE__ */ new Set();
19586
18399
  const components = /* @__PURE__ */ new Set();
19587
18400
  const hooks = /* @__PURE__ */ new Set();
19588
- traceImports(page.path, dataDb2, visited, components, hooks);
18401
+ traceImports(page.path, dataDb, visited, components, hooks);
19589
18402
  const allFiles = [...visited];
19590
18403
  const routers = findRouterCalls(allFiles);
19591
- const tables = findTablesFromRouters(routers, dataDb2);
18404
+ const tables = findTablesFromRouters(routers, dataDb);
19592
18405
  insertStmt.run(
19593
18406
  page.path,
19594
18407
  route,
@@ -19604,8 +18417,8 @@ function buildPageDeps(dataDb2, codegraphDb2) {
19604
18417
  insertAll();
19605
18418
  return count;
19606
18419
  }
19607
- function getPageChain(dataDb2, pageFile) {
19608
- const row = dataDb2.prepare("SELECT * FROM massu_page_deps WHERE page_file = ?").get(pageFile);
18420
+ function getPageChain(dataDb, pageFile) {
18421
+ const row = dataDb.prepare("SELECT * FROM massu_page_deps WHERE page_file = ?").get(pageFile);
19609
18422
  if (!row) return null;
19610
18423
  return {
19611
18424
  page: row.page_file,
@@ -19617,10 +18430,10 @@ function getPageChain(dataDb2, pageFile) {
19617
18430
  tables: JSON.parse(row.tables_touched)
19618
18431
  };
19619
18432
  }
19620
- function findAffectedPages(dataDb2, file) {
19621
- const directPage = getPageChain(dataDb2, file);
18433
+ function findAffectedPages(dataDb, file) {
18434
+ const directPage = getPageChain(dataDb, file);
19622
18435
  if (directPage) return [directPage];
19623
- const importers = dataDb2.prepare(
18436
+ const importers = dataDb.prepare(
19624
18437
  "SELECT source_file FROM massu_imports WHERE target_file = ?"
19625
18438
  ).all(file);
19626
18439
  const affectedFiles = /* @__PURE__ */ new Set([file, ...importers.map((i) => i.source_file)]);
@@ -19631,7 +18444,7 @@ function findAffectedPages(dataDb2, file) {
19631
18444
  while (frontier.length > 0 && depth < maxDepth) {
19632
18445
  const next = [];
19633
18446
  for (const f2 of frontier) {
19634
- const upstreamImporters = dataDb2.prepare(
18447
+ const upstreamImporters = dataDb.prepare(
19635
18448
  "SELECT source_file FROM massu_imports WHERE target_file = ?"
19636
18449
  ).all(f2);
19637
18450
  for (const imp of upstreamImporters) {
@@ -19645,7 +18458,7 @@ function findAffectedPages(dataDb2, file) {
19645
18458
  frontier = next;
19646
18459
  depth++;
19647
18460
  }
19648
- const allPages = dataDb2.prepare("SELECT * FROM massu_page_deps").all();
18461
+ const allPages = dataDb.prepare("SELECT * FROM massu_page_deps").all();
19649
18462
  const results = [];
19650
18463
  for (const row of allPages) {
19651
18464
  if (affectedFiles.has(row.page_file)) {
@@ -19671,8 +18484,8 @@ var init_page_deps = __esm({
19671
18484
  });
19672
18485
 
19673
18486
  // src/middleware-tree.ts
19674
- function buildMiddlewareTree(dataDb2) {
19675
- dataDb2.exec("DELETE FROM massu_middleware_tree");
18487
+ function buildMiddlewareTree(dataDb) {
18488
+ dataDb.exec("DELETE FROM massu_middleware_tree");
19676
18489
  const config = getConfig();
19677
18490
  const middlewareFile = config.paths.middleware ?? "src/middleware.ts";
19678
18491
  if (!middlewareFile) return 0;
@@ -19681,7 +18494,7 @@ function buildMiddlewareTree(dataDb2) {
19681
18494
  visited.add(middlewareFile);
19682
18495
  while (queue.length > 0) {
19683
18496
  const current = queue.shift();
19684
- const imports = dataDb2.prepare(
18497
+ const imports = dataDb.prepare(
19685
18498
  "SELECT target_file FROM massu_imports WHERE source_file = ?"
19686
18499
  ).all(current);
19687
18500
  for (const imp of imports) {
@@ -19691,8 +18504,8 @@ function buildMiddlewareTree(dataDb2) {
19691
18504
  }
19692
18505
  }
19693
18506
  }
19694
- const insertStmt = dataDb2.prepare("INSERT INTO massu_middleware_tree (file) VALUES (?)");
19695
- const insertAll = dataDb2.transaction(() => {
18507
+ const insertStmt = dataDb.prepare("INSERT INTO massu_middleware_tree (file) VALUES (?)");
18508
+ const insertAll = dataDb.transaction(() => {
19696
18509
  for (const file of visited) {
19697
18510
  insertStmt.run(file);
19698
18511
  }
@@ -19700,8 +18513,8 @@ function buildMiddlewareTree(dataDb2) {
19700
18513
  insertAll();
19701
18514
  return visited.size;
19702
18515
  }
19703
- function isInMiddlewareTree(dataDb2, file) {
19704
- const result = dataDb2.prepare("SELECT 1 FROM massu_middleware_tree WHERE file = ?").get(file);
18516
+ function isInMiddlewareTree(dataDb, file) {
18517
+ const result = dataDb.prepare("SELECT 1 FROM massu_middleware_tree WHERE file = ?").get(file);
19705
18518
  return result !== void 0;
19706
18519
  }
19707
18520
  var init_middleware_tree = __esm({
@@ -19763,12 +18576,12 @@ function globMatchSimple(name, pattern) {
19763
18576
  const regexStr = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
19764
18577
  return new RegExp(`^${regexStr}$`).test(name);
19765
18578
  }
19766
- function findCrossDomainImports(dataDb2) {
18579
+ function findCrossDomainImports(dataDb) {
19767
18580
  const domains = getDomains();
19768
18581
  const config = getConfig();
19769
18582
  const srcPrefix = config.paths.source;
19770
18583
  const srcPattern = srcPrefix + "/%";
19771
- const imports = dataDb2.prepare(
18584
+ const imports = dataDb.prepare(
19772
18585
  "SELECT source_file, target_file FROM massu_imports WHERE source_file LIKE ? AND target_file LIKE ?"
19773
18586
  ).all(srcPattern, srcPattern);
19774
18587
  const crossings = [];
@@ -19790,7 +18603,7 @@ function findCrossDomainImports(dataDb2) {
19790
18603
  }
19791
18604
  return crossings;
19792
18605
  }
19793
- function getFilesInDomain(dataDb2, codegraphDb2, domainName) {
18606
+ function getFilesInDomain(dataDb, codegraphDb, domainName) {
19794
18607
  const domains = getDomains();
19795
18608
  const config = getConfig();
19796
18609
  const domain = domains.find((d2) => d2.name === domainName);
@@ -19798,7 +18611,7 @@ function getFilesInDomain(dataDb2, codegraphDb2, domainName) {
19798
18611
  const srcPrefix = config.paths.source;
19799
18612
  const routersPath = config.paths.routers ?? "src/server/api/routers";
19800
18613
  const srcPattern = srcPrefix + "/%";
19801
- const allFiles = codegraphDb2.prepare("SELECT path FROM files WHERE path LIKE ?").all(srcPattern);
18614
+ const allFiles = codegraphDb.prepare("SELECT path FROM files WHERE path LIKE ?").all(srcPattern);
19802
18615
  const routers = [];
19803
18616
  const pages = [];
19804
18617
  const components = [];
@@ -19824,14 +18637,14 @@ var init_domains = __esm({
19824
18637
  });
19825
18638
 
19826
18639
  // src/schema-mapper.ts
19827
- import { readFileSync as readFileSync26, existsSync as existsSync27, readdirSync as readdirSync17 } from "fs";
19828
- import { join as join15 } from "path";
18640
+ import { readFileSync as readFileSync25, existsSync as existsSync27, readdirSync as readdirSync16 } from "fs";
18641
+ import { join as join14 } from "path";
19829
18642
  function parsePrismaSchema() {
19830
18643
  const schemaPath = getResolvedPaths().prismaSchemaPath;
19831
18644
  if (!existsSync27(schemaPath)) {
19832
18645
  throw new Error(`Prisma schema not found at ${schemaPath}`);
19833
18646
  }
19834
- const source = readFileSync26(schemaPath, "utf-8");
18647
+ const source = readFileSync25(schemaPath, "utf-8");
19835
18648
  const models = [];
19836
18649
  const sourceLines = source.split("\n");
19837
18650
  let i = 0;
@@ -19894,9 +18707,9 @@ function findColumnUsageInRouters(tableName) {
19894
18707
  return usage;
19895
18708
  }
19896
18709
  function scanDirectory(dir, tableName, usage) {
19897
- const entries = readdirSync17(dir, { withFileTypes: true });
18710
+ const entries = readdirSync16(dir, { withFileTypes: true });
19898
18711
  for (const entry of entries) {
19899
- const fullPath = join15(dir, entry.name);
18712
+ const fullPath = join14(dir, entry.name);
19900
18713
  if (entry.isDirectory()) {
19901
18714
  scanDirectory(fullPath, tableName, usage);
19902
18715
  } else if (entry.name.endsWith(".ts")) {
@@ -19906,7 +18719,7 @@ function scanDirectory(dir, tableName, usage) {
19906
18719
  }
19907
18720
  function scanFile(absPath, tableName, usage) {
19908
18721
  try {
19909
- const source = readFileSync26(absPath, "utf-8");
18722
+ const source = readFileSync25(absPath, "utf-8");
19910
18723
  if (!source.includes(tableName)) return;
19911
18724
  const relPath = absPath.slice(getProjectRoot().length + 1);
19912
18725
  const lines = source.split("\n");
@@ -19952,14 +18765,14 @@ function detectMismatches(models) {
19952
18765
  function findFilesUsingColumn(dir, column, tableName) {
19953
18766
  const result = [];
19954
18767
  if (!existsSync27(dir)) return result;
19955
- const entries = readdirSync17(dir, { withFileTypes: true });
18768
+ const entries = readdirSync16(dir, { withFileTypes: true });
19956
18769
  for (const entry of entries) {
19957
- const fullPath = join15(dir, entry.name);
18770
+ const fullPath = join14(dir, entry.name);
19958
18771
  if (entry.isDirectory()) {
19959
18772
  result.push(...findFilesUsingColumn(fullPath, column, tableName));
19960
18773
  } else if (entry.name.endsWith(".ts")) {
19961
18774
  try {
19962
- const source = readFileSync26(fullPath, "utf-8");
18775
+ const source = readFileSync25(fullPath, "utf-8");
19963
18776
  if (source.includes(tableName) && source.includes(column)) {
19964
18777
  result.push(fullPath.slice(getProjectRoot().length + 1));
19965
18778
  }
@@ -20104,32 +18917,32 @@ var init_import_parser = __esm({
20104
18917
  });
20105
18918
 
20106
18919
  // src/python/import-resolver.ts
20107
- import { readFileSync as readFileSync27, existsSync as existsSync28, readdirSync as readdirSync18 } from "fs";
20108
- import { resolve as resolve24, join as join16, relative as relative6, dirname as dirname14 } from "path";
18920
+ import { readFileSync as readFileSync26, existsSync as existsSync28, readdirSync as readdirSync17 } from "fs";
18921
+ import { resolve as resolve24, join as join15, relative as relative6, dirname as dirname13 } from "path";
20109
18922
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
20110
18923
  const projectRoot = getProjectRoot();
20111
18924
  if (level > 0) {
20112
- let baseDir = dirname14(resolve24(projectRoot, fromFile));
18925
+ let baseDir = dirname13(resolve24(projectRoot, fromFile));
20113
18926
  for (let i = 1; i < level; i++) {
20114
- baseDir = dirname14(baseDir);
18927
+ baseDir = dirname13(baseDir);
20115
18928
  }
20116
18929
  const modulePart = module.replace(/^\.+/, "");
20117
18930
  if (modulePart) {
20118
18931
  const parts2 = modulePart.split(".");
20119
- return tryResolvePythonPath(join16(baseDir, ...parts2), projectRoot);
18932
+ return tryResolvePythonPath(join15(baseDir, ...parts2), projectRoot);
20120
18933
  }
20121
18934
  return tryResolvePythonPath(baseDir, projectRoot);
20122
18935
  }
20123
18936
  const parts = module.split(".");
20124
- const candidate = join16(resolve24(projectRoot, pythonRoot), ...parts);
18937
+ const candidate = join15(resolve24(projectRoot, pythonRoot), ...parts);
20125
18938
  return tryResolvePythonPath(candidate, projectRoot);
20126
18939
  }
20127
18940
  function tryResolvePythonPath(basePath, projectRoot) {
20128
18941
  if (existsSync28(basePath + ".py")) {
20129
18942
  return relative6(projectRoot, basePath + ".py");
20130
18943
  }
20131
- if (existsSync28(join16(basePath, "__init__.py"))) {
20132
- return relative6(projectRoot, join16(basePath, "__init__.py"));
18944
+ if (existsSync28(join15(basePath, "__init__.py"))) {
18945
+ return relative6(projectRoot, join15(basePath, "__init__.py"));
20133
18946
  }
20134
18947
  if (basePath.endsWith(".py") && existsSync28(basePath)) {
20135
18948
  return relative6(projectRoot, basePath);
@@ -20139,29 +18952,29 @@ function tryResolvePythonPath(basePath, projectRoot) {
20139
18952
  function walkPythonFiles(dir, excludeDirs) {
20140
18953
  const files = [];
20141
18954
  try {
20142
- const entries = readdirSync18(dir, { withFileTypes: true });
18955
+ const entries = readdirSync17(dir, { withFileTypes: true });
20143
18956
  for (const entry of entries) {
20144
18957
  if (entry.isDirectory()) {
20145
18958
  if (excludeDirs.includes(entry.name)) continue;
20146
- files.push(...walkPythonFiles(join16(dir, entry.name), excludeDirs));
18959
+ files.push(...walkPythonFiles(join15(dir, entry.name), excludeDirs));
20147
18960
  } else if (entry.name.endsWith(".py")) {
20148
- files.push(join16(dir, entry.name));
18961
+ files.push(join15(dir, entry.name));
20149
18962
  }
20150
18963
  }
20151
18964
  } catch {
20152
18965
  }
20153
18966
  return files;
20154
18967
  }
20155
- function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
18968
+ function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
20156
18969
  const projectRoot = getProjectRoot();
20157
18970
  const absRoot = resolve24(projectRoot, pythonRoot);
20158
- dataDb2.exec("DELETE FROM massu_py_imports");
20159
- const insertStmt = dataDb2.prepare(
18971
+ dataDb.exec("DELETE FROM massu_py_imports");
18972
+ const insertStmt = dataDb.prepare(
20160
18973
  "INSERT INTO massu_py_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)"
20161
18974
  );
20162
18975
  const files = walkPythonFiles(absRoot, excludeDirs);
20163
18976
  let edgeCount = 0;
20164
- const insertMany = dataDb2.transaction((edges) => {
18977
+ const insertMany = dataDb.transaction((edges) => {
20165
18978
  for (const edge of edges) {
20166
18979
  insertStmt.run(edge.source, edge.target, edge.type, edge.names, edge.line);
20167
18980
  }
@@ -20171,7 +18984,7 @@ function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__
20171
18984
  const relFile = relative6(projectRoot, absFile);
20172
18985
  let source;
20173
18986
  try {
20174
- source = readFileSync27(absFile, "utf-8");
18987
+ source = readFileSync26(absFile, "utf-8");
20175
18988
  } catch {
20176
18989
  continue;
20177
18990
  }
@@ -20437,40 +19250,40 @@ var init_route_parser = __esm({
20437
19250
  });
20438
19251
 
20439
19252
  // src/python/route-indexer.ts
20440
- import { readFileSync as readFileSync28, readdirSync as readdirSync19 } from "fs";
20441
- import { join as join17, relative as relative7 } from "path";
19253
+ import { readFileSync as readFileSync27, readdirSync as readdirSync18 } from "fs";
19254
+ import { join as join16, relative as relative7 } from "path";
20442
19255
  function walkPyFiles(dir, excludeDirs) {
20443
19256
  const files = [];
20444
19257
  try {
20445
- const entries = readdirSync19(dir, { withFileTypes: true });
19258
+ const entries = readdirSync18(dir, { withFileTypes: true });
20446
19259
  for (const entry of entries) {
20447
19260
  if (entry.isDirectory()) {
20448
19261
  if (excludeDirs.includes(entry.name)) continue;
20449
- files.push(...walkPyFiles(join17(dir, entry.name), excludeDirs));
19262
+ files.push(...walkPyFiles(join16(dir, entry.name), excludeDirs));
20450
19263
  } else if (entry.name.endsWith(".py")) {
20451
- files.push(join17(dir, entry.name));
19264
+ files.push(join16(dir, entry.name));
20452
19265
  }
20453
19266
  }
20454
19267
  } catch {
20455
19268
  }
20456
19269
  return files;
20457
19270
  }
20458
- function buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
19271
+ function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
20459
19272
  const projectRoot = getProjectRoot();
20460
- const absRoot = join17(projectRoot, pythonRoot);
20461
- dataDb2.exec("DELETE FROM massu_py_routes");
20462
- dataDb2.exec("DELETE FROM massu_py_route_callers");
20463
- const insertStmt = dataDb2.prepare(
19273
+ const absRoot = join16(projectRoot, pythonRoot);
19274
+ dataDb.exec("DELETE FROM massu_py_routes");
19275
+ dataDb.exec("DELETE FROM massu_py_route_callers");
19276
+ const insertStmt = dataDb.prepare(
20464
19277
  "INSERT INTO massu_py_routes (file, method, path, function_name, dependencies, request_model, response_model, is_authenticated, line) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
20465
19278
  );
20466
19279
  const files = walkPyFiles(absRoot, excludeDirs);
20467
19280
  let count = 0;
20468
- dataDb2.transaction(() => {
19281
+ dataDb.transaction(() => {
20469
19282
  for (const absFile of files) {
20470
19283
  const relFile = relative7(projectRoot, absFile);
20471
19284
  let source;
20472
19285
  try {
20473
- source = readFileSync28(absFile, "utf-8");
19286
+ source = readFileSync27(absFile, "utf-8");
20474
19287
  } catch {
20475
19288
  continue;
20476
19289
  }
@@ -20681,43 +19494,43 @@ var init_model_parser = __esm({
20681
19494
  });
20682
19495
 
20683
19496
  // src/python/model-indexer.ts
20684
- import { readFileSync as readFileSync29, readdirSync as readdirSync20 } from "fs";
20685
- import { join as join18, relative as relative8 } from "path";
19497
+ import { readFileSync as readFileSync28, readdirSync as readdirSync19 } from "fs";
19498
+ import { join as join17, relative as relative8 } from "path";
20686
19499
  function walkPyFiles2(dir, excludeDirs) {
20687
19500
  const files = [];
20688
19501
  try {
20689
- const entries = readdirSync20(dir, { withFileTypes: true });
19502
+ const entries = readdirSync19(dir, { withFileTypes: true });
20690
19503
  for (const entry of entries) {
20691
19504
  if (entry.isDirectory()) {
20692
19505
  if (excludeDirs.includes(entry.name)) continue;
20693
- files.push(...walkPyFiles2(join18(dir, entry.name), excludeDirs));
19506
+ files.push(...walkPyFiles2(join17(dir, entry.name), excludeDirs));
20694
19507
  } else if (entry.name.endsWith(".py")) {
20695
- files.push(join18(dir, entry.name));
19508
+ files.push(join17(dir, entry.name));
20696
19509
  }
20697
19510
  }
20698
19511
  } catch {
20699
19512
  }
20700
19513
  return files;
20701
19514
  }
20702
- function buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
19515
+ function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
20703
19516
  const projectRoot = getProjectRoot();
20704
- const absRoot = join18(projectRoot, pythonRoot);
20705
- dataDb2.exec("DELETE FROM massu_py_models");
20706
- dataDb2.exec("DELETE FROM massu_py_fk_edges");
20707
- const insertModel = dataDb2.prepare(
19517
+ const absRoot = join17(projectRoot, pythonRoot);
19518
+ dataDb.exec("DELETE FROM massu_py_models");
19519
+ dataDb.exec("DELETE FROM massu_py_fk_edges");
19520
+ const insertModel = dataDb.prepare(
20708
19521
  "INSERT INTO massu_py_models (class_name, table_name, file, line, columns, relationships, foreign_keys) VALUES (?, ?, ?, ?, ?, ?, ?)"
20709
19522
  );
20710
- const insertFk = dataDb2.prepare(
19523
+ const insertFk = dataDb.prepare(
20711
19524
  "INSERT INTO massu_py_fk_edges (source_table, source_column, target_table, target_column) VALUES (?, ?, ?, ?)"
20712
19525
  );
20713
19526
  const files = walkPyFiles2(absRoot, excludeDirs);
20714
19527
  let count = 0;
20715
- dataDb2.transaction(() => {
19528
+ dataDb.transaction(() => {
20716
19529
  for (const absFile of files) {
20717
19530
  const relFile = relative8(projectRoot, absFile);
20718
19531
  let source;
20719
19532
  try {
20720
- source = readFileSync29(absFile, "utf-8");
19533
+ source = readFileSync28(absFile, "utf-8");
20721
19534
  } catch {
20722
19535
  continue;
20723
19536
  }
@@ -20977,23 +19790,23 @@ var init_migration_parser = __esm({
20977
19790
  });
20978
19791
 
20979
19792
  // src/python/migration-indexer.ts
20980
- import { readFileSync as readFileSync30, readdirSync as readdirSync21 } from "fs";
20981
- import { join as join19, relative as relative9 } from "path";
20982
- function buildPythonMigrationIndex(dataDb2, alembicDir) {
19793
+ import { readFileSync as readFileSync29, readdirSync as readdirSync20 } from "fs";
19794
+ import { join as join18, relative as relative9 } from "path";
19795
+ function buildPythonMigrationIndex(dataDb, alembicDir) {
20983
19796
  const projectRoot = getProjectRoot();
20984
- const absDir = join19(projectRoot, alembicDir);
20985
- dataDb2.exec("DELETE FROM massu_py_migrations");
20986
- const versionsDir = join19(absDir, "versions");
19797
+ const absDir = join18(projectRoot, alembicDir);
19798
+ dataDb.exec("DELETE FROM massu_py_migrations");
19799
+ const versionsDir = join18(absDir, "versions");
20987
19800
  let files = [];
20988
19801
  try {
20989
- files = readdirSync21(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join19(versionsDir, f2));
19802
+ files = readdirSync20(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join18(versionsDir, f2));
20990
19803
  } catch {
20991
19804
  try {
20992
- files = readdirSync21(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join19(absDir, f2));
19805
+ files = readdirSync20(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join18(absDir, f2));
20993
19806
  } catch {
20994
19807
  }
20995
19808
  }
20996
- const insertStmt = dataDb2.prepare(
19809
+ const insertStmt = dataDb.prepare(
20997
19810
  "INSERT INTO massu_py_migrations (revision, down_revision, file, description, operations, is_head) VALUES (?, ?, ?, ?, ?, ?)"
20998
19811
  );
20999
19812
  let count = 0;
@@ -21003,7 +19816,7 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
21003
19816
  for (const absFile of files) {
21004
19817
  let source;
21005
19818
  try {
21006
- source = readFileSync30(absFile, "utf-8");
19819
+ source = readFileSync29(absFile, "utf-8");
21007
19820
  } catch {
21008
19821
  continue;
21009
19822
  }
@@ -21019,7 +19832,7 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
21019
19832
  operations: JSON.stringify(parsed.operations)
21020
19833
  });
21021
19834
  }
21022
- dataDb2.transaction(() => {
19835
+ dataDb.transaction(() => {
21023
19836
  for (const row of rows) {
21024
19837
  const isHead = !hasDownRef.has(row.revision) ? 1 : 0;
21025
19838
  insertStmt.run(row.revision, row.downRevision, row.file, row.description, row.operations, isHead);
@@ -21037,17 +19850,17 @@ var init_migration_indexer = __esm({
21037
19850
  });
21038
19851
 
21039
19852
  // src/python/coupling-detector.ts
21040
- import { readFileSync as readFileSync31, readdirSync as readdirSync22 } from "fs";
21041
- import { join as join20, relative as relative10 } from "path";
21042
- function buildPythonCouplingIndex(dataDb2) {
19853
+ import { readFileSync as readFileSync30, readdirSync as readdirSync21 } from "fs";
19854
+ import { join as join19, relative as relative10 } from "path";
19855
+ function buildPythonCouplingIndex(dataDb) {
21043
19856
  const projectRoot = getProjectRoot();
21044
19857
  const config = getConfig();
21045
- const srcDir = join20(projectRoot, config.paths.source);
21046
- const routes = dataDb2.prepare("SELECT id, method, path FROM massu_py_routes").all();
19858
+ const srcDir = join19(projectRoot, config.paths.source);
19859
+ const routes = dataDb.prepare("SELECT id, method, path FROM massu_py_routes").all();
21047
19860
  if (routes.length === 0) return 0;
21048
- dataDb2.exec("DELETE FROM massu_py_route_callers");
19861
+ dataDb.exec("DELETE FROM massu_py_route_callers");
21049
19862
  const frontendFiles = walkFrontendFiles(srcDir);
21050
- const insertStmt = dataDb2.prepare(
19863
+ const insertStmt = dataDb.prepare(
21051
19864
  "INSERT INTO massu_py_route_callers (route_id, frontend_file, line, call_pattern) VALUES (?, ?, ?, ?)"
21052
19865
  );
21053
19866
  let count = 0;
@@ -21069,12 +19882,12 @@ function buildPythonCouplingIndex(dataDb2) {
21069
19882
  /\.patch\s*\(\s*[`'"](\/api\/[^`'"]*)[`'"]/g
21070
19883
  // client.patch('/api/...')
21071
19884
  ];
21072
- dataDb2.transaction(() => {
19885
+ dataDb.transaction(() => {
21073
19886
  for (const absFile of frontendFiles) {
21074
19887
  const relFile = relative10(projectRoot, absFile);
21075
19888
  let source;
21076
19889
  try {
21077
- source = readFileSync31(absFile, "utf-8");
19890
+ source = readFileSync30(absFile, "utf-8");
21078
19891
  } catch {
21079
19892
  continue;
21080
19893
  }
@@ -21102,13 +19915,13 @@ function walkFrontendFiles(dir) {
21102
19915
  const files = [];
21103
19916
  const exclude = ["node_modules", ".next", "dist", ".git", "__pycache__", ".venv", "venv"];
21104
19917
  try {
21105
- const entries = readdirSync22(dir, { withFileTypes: true });
19918
+ const entries = readdirSync21(dir, { withFileTypes: true });
21106
19919
  for (const entry of entries) {
21107
19920
  if (entry.isDirectory()) {
21108
19921
  if (exclude.includes(entry.name)) continue;
21109
- files.push(...walkFrontendFiles(join20(dir, entry.name)));
19922
+ files.push(...walkFrontendFiles(join19(dir, entry.name)));
21110
19923
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
21111
- files.push(join20(dir, entry.name));
19924
+ files.push(join19(dir, entry.name));
21112
19925
  }
21113
19926
  }
21114
19927
  } catch {
@@ -21479,7 +20292,7 @@ var init_memory_tools = __esm({
21479
20292
  });
21480
20293
 
21481
20294
  // src/docs-tools.ts
21482
- import { readFileSync as readFileSync32, existsSync as existsSync29 } from "fs";
20295
+ import { readFileSync as readFileSync31, existsSync as existsSync29 } from "fs";
21483
20296
  import { resolve as resolve25, basename as basename6 } from "path";
21484
20297
  function p3(baseName) {
21485
20298
  return `${getConfig().toolPrefix}_${baseName}`;
@@ -21538,7 +20351,7 @@ function loadDocsMap() {
21538
20351
  if (!existsSync29(mapPath)) {
21539
20352
  throw new Error(`docs-map.json not found at ${mapPath}`);
21540
20353
  }
21541
- return JSON.parse(readFileSync32(mapPath, "utf-8"));
20354
+ return JSON.parse(readFileSync31(mapPath, "utf-8"));
21542
20355
  }
21543
20356
  function matchesPattern(filePath, pattern) {
21544
20357
  const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
@@ -21612,9 +20425,9 @@ function extractProcedureNames(routerPath) {
21612
20425
  if (!existsSync29(absPath)) {
21613
20426
  const altPath = ensureWithinRoot(resolve25(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
21614
20427
  if (!existsSync29(altPath)) return [];
21615
- return extractProcedureNamesFromContent(readFileSync32(altPath, "utf-8"));
20428
+ return extractProcedureNamesFromContent(readFileSync31(altPath, "utf-8"));
21616
20429
  }
21617
- return extractProcedureNamesFromContent(readFileSync32(absPath, "utf-8"));
20430
+ return extractProcedureNamesFromContent(readFileSync31(absPath, "utf-8"));
21618
20431
  }
21619
20432
  function extractProcedureNamesFromContent(content) {
21620
20433
  const procRegex = /\.(?:query|mutation)\s*\(/g;
@@ -21667,7 +20480,7 @@ function handleDocsAudit(args2) {
21667
20480
  });
21668
20481
  continue;
21669
20482
  }
21670
- const content = readFileSync32(helpPagePath, "utf-8");
20483
+ const content = readFileSync31(helpPagePath, "utf-8");
21671
20484
  const sections = extractSections(content);
21672
20485
  const frontmatter = extractFrontmatter(content);
21673
20486
  const staleReasons = [];
@@ -21709,7 +20522,7 @@ function handleDocsAudit(args2) {
21709
20522
  if (parentId === mappingId) {
21710
20523
  const guidePath = ensureWithinRoot(resolve25(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
21711
20524
  if (existsSync29(guidePath)) {
21712
- const guideContent = readFileSync32(guidePath, "utf-8");
20525
+ const guideContent = readFileSync31(guidePath, "utf-8");
21713
20526
  const guideFrontmatter = extractFrontmatter(guideContent);
21714
20527
  if (!guideFrontmatter?.lastVerified || status === "STALE") {
21715
20528
  results.push({
@@ -21749,7 +20562,7 @@ function handleDocsCoverage(args2) {
21749
20562
  let lastVerified = null;
21750
20563
  let status = null;
21751
20564
  if (exists) {
21752
- const content = readFileSync32(helpPagePath, "utf-8");
20565
+ const content = readFileSync31(helpPagePath, "utf-8");
21753
20566
  lineCount = content.split("\n").length;
21754
20567
  hasContent = lineCount > 10;
21755
20568
  const frontmatter = extractFrontmatter(content);
@@ -22585,22 +21398,22 @@ function getSentinelToolDefinitions() {
22585
21398
  }
22586
21399
  ];
22587
21400
  }
22588
- function handleSentinelToolCall(name, args2, dataDb2) {
21401
+ function handleSentinelToolCall(name, args2, dataDb) {
22589
21402
  const prefix3 = getConfig().toolPrefix + "_";
22590
21403
  const baseName = name.startsWith(prefix3) ? name.slice(prefix3.length) : name;
22591
21404
  switch (baseName) {
22592
21405
  case "sentinel_search":
22593
- return handleSearch2(args2, dataDb2);
21406
+ return handleSearch2(args2, dataDb);
22594
21407
  case "sentinel_detail":
22595
- return handleDetail2(args2, dataDb2);
21408
+ return handleDetail2(args2, dataDb);
22596
21409
  case "sentinel_impact":
22597
- return handleImpact(args2, dataDb2);
21410
+ return handleImpact(args2, dataDb);
22598
21411
  case "sentinel_validate":
22599
- return handleValidate(args2, dataDb2);
21412
+ return handleValidate(args2, dataDb);
22600
21413
  case "sentinel_register":
22601
- return handleRegister(args2, dataDb2);
21414
+ return handleRegister(args2, dataDb);
22602
21415
  case "sentinel_parity":
22603
- return handleParityCheck(args2, dataDb2);
21416
+ return handleParityCheck(args2, dataDb);
22604
21417
  default:
22605
21418
  return text4(`Unknown sentinel tool: ${name}`);
22606
21419
  }
@@ -22867,8 +21680,8 @@ var init_sentinel_tools = __esm({
22867
21680
  });
22868
21681
 
22869
21682
  // src/sentinel-scanner.ts
22870
- import { readFileSync as readFileSync33, existsSync as existsSync31, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
22871
- import { resolve as resolve27, join as join21, basename as basename7, dirname as dirname15, relative as relative11 } from "path";
21683
+ import { readFileSync as readFileSync32, existsSync as existsSync31, readdirSync as readdirSync22, statSync as statSync13 } from "fs";
21684
+ import { resolve as resolve27, join as join20, basename as basename7, dirname as dirname14, relative as relative11 } from "path";
22872
21685
  function inferDomain(filePath) {
22873
21686
  const domains = getConfig().domains;
22874
21687
  const path = filePath.toLowerCase();
@@ -22914,10 +21727,10 @@ function parseFeatureAnnotations(source) {
22914
21727
  }
22915
21728
  return annotations;
22916
21729
  }
22917
- function scanTrpcProcedures(dataDb2) {
21730
+ function scanTrpcProcedures(dataDb) {
22918
21731
  const features = [];
22919
21732
  const featureMap = /* @__PURE__ */ new Map();
22920
- const procedures = dataDb2.prepare(`
21733
+ const procedures = dataDb.prepare(`
22921
21734
  SELECT router_name, procedure_name, procedure_type, router_file
22922
21735
  FROM massu_trpc_procedures
22923
21736
  ORDER BY router_name, procedure_name
@@ -22957,9 +21770,9 @@ function scanTrpcProcedures(dataDb2) {
22957
21770
  }
22958
21771
  return Array.from(featureMap.values());
22959
21772
  }
22960
- function scanPageRoutes(dataDb2) {
21773
+ function scanPageRoutes(dataDb) {
22961
21774
  const features = [];
22962
- const pages = dataDb2.prepare(`
21775
+ const pages = dataDb.prepare(`
22963
21776
  SELECT page_file, route, portal, components, hooks, routers
22964
21777
  FROM massu_page_deps
22965
21778
  ORDER BY route
@@ -22991,7 +21804,7 @@ function scanPageRoutes(dataDb2) {
22991
21804
  }
22992
21805
  return features;
22993
21806
  }
22994
- function scanComponentExports(dataDb2) {
21807
+ function scanComponentExports(dataDb) {
22995
21808
  const features = [];
22996
21809
  const config = getConfig();
22997
21810
  const projectRoot = getProjectRoot();
@@ -23000,7 +21813,7 @@ function scanComponentExports(dataDb2) {
23000
21813
  const basePath = resolve27(projectRoot, componentsBase);
23001
21814
  if (existsSync31(basePath)) {
23002
21815
  try {
23003
- const entries = readdirSync23(basePath, { withFileTypes: true });
21816
+ const entries = readdirSync22(basePath, { withFileTypes: true });
23004
21817
  for (const entry of entries) {
23005
21818
  if (entry.isDirectory()) {
23006
21819
  componentDirs.push(componentsBase + "/" + entry.name);
@@ -23015,7 +21828,7 @@ function scanComponentExports(dataDb2) {
23015
21828
  const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
23016
21829
  for (const file of files) {
23017
21830
  const relPath = relative11(projectRoot, file);
23018
- const source = readFileSync33(file, "utf-8");
21831
+ const source = readFileSync32(file, "utf-8");
23019
21832
  const annotations = parseFeatureAnnotations(source);
23020
21833
  if (annotations.length > 0) {
23021
21834
  for (const ann of annotations) {
@@ -23041,7 +21854,7 @@ function scanComponentExports(dataDb2) {
23041
21854
  if (hasHandlers && exportMatch) {
23042
21855
  const componentName = exportMatch[1];
23043
21856
  const domain = inferDomain(relPath);
23044
- const subdomain = basename7(dirname15(relPath));
21857
+ const subdomain = basename7(dirname14(relPath));
23045
21858
  const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
23046
21859
  if (!annotations.some((a2) => a2.featureKey === featureKey)) {
23047
21860
  features.push({
@@ -23064,9 +21877,9 @@ function scanComponentExports(dataDb2) {
23064
21877
  function walkDir2(dir) {
23065
21878
  const results = [];
23066
21879
  try {
23067
- const entries = readdirSync23(dir);
21880
+ const entries = readdirSync22(dir);
23068
21881
  for (const entry of entries) {
23069
- const fullPath = join21(dir, entry);
21882
+ const fullPath = join20(dir, entry);
23070
21883
  try {
23071
21884
  const stat = statSync13(fullPath);
23072
21885
  if (stat.isDirectory()) {
@@ -23081,10 +21894,10 @@ function walkDir2(dir) {
23081
21894
  }
23082
21895
  return results;
23083
21896
  }
23084
- function runFeatureScan(dataDb2) {
23085
- const procedureFeatures = scanTrpcProcedures(dataDb2);
23086
- const pageFeatures = scanPageRoutes(dataDb2);
23087
- const componentFeatures = scanComponentExports(dataDb2);
21897
+ function runFeatureScan(dataDb) {
21898
+ const procedureFeatures = scanTrpcProcedures(dataDb);
21899
+ const pageFeatures = scanPageRoutes(dataDb);
21900
+ const componentFeatures = scanComponentExports(dataDb);
23088
21901
  const allFeatures = /* @__PURE__ */ new Map();
23089
21902
  for (const f2 of procedureFeatures) {
23090
21903
  allFeatures.set(f2.feature_key, f2);
@@ -23097,7 +21910,7 @@ function runFeatureScan(dataDb2) {
23097
21910
  }
23098
21911
  let registered = 0;
23099
21912
  for (const feature of allFeatures.values()) {
23100
- const featureId = upsertFeature(dataDb2, {
21913
+ const featureId = upsertFeature(dataDb, {
23101
21914
  feature_key: feature.feature_key,
23102
21915
  domain: feature.domain,
23103
21916
  subdomain: feature.subdomain,
@@ -23108,15 +21921,15 @@ function runFeatureScan(dataDb2) {
23108
21921
  portal_scope: feature.portal_scope
23109
21922
  });
23110
21923
  for (const comp of feature.components) {
23111
- linkComponent(dataDb2, featureId, comp.file, comp.name, comp.role, comp.isPrimary);
21924
+ linkComponent(dataDb, featureId, comp.file, comp.name, comp.role, comp.isPrimary);
23112
21925
  }
23113
21926
  for (const proc of feature.procedures) {
23114
- linkProcedure(dataDb2, featureId, proc.router, proc.procedure, proc.type);
21927
+ linkProcedure(dataDb, featureId, proc.router, proc.procedure, proc.type);
23115
21928
  }
23116
21929
  for (const page of feature.pages) {
23117
- linkPage(dataDb2, featureId, page.route, page.portal ?? void 0);
21930
+ linkPage(dataDb, featureId, page.route, page.portal ?? void 0);
23118
21931
  }
23119
- logChange(dataDb2, featureId, "created", "Auto-discovered by sentinel scanner", void 0, "scanner");
21932
+ logChange(dataDb, featureId, "created", "Auto-discovered by sentinel scanner", void 0, "scanner");
23120
21933
  registered++;
23121
21934
  }
23122
21935
  return {
@@ -24054,7 +22867,7 @@ var init_audit_trail = __esm({
24054
22867
  });
24055
22868
 
24056
22869
  // src/validation-engine.ts
24057
- import { existsSync as existsSync32, readFileSync as readFileSync34 } from "fs";
22870
+ import { existsSync as existsSync32, readFileSync as readFileSync33 } from "fs";
24058
22871
  function p10(baseName) {
24059
22872
  return `${getConfig().toolPrefix}_${baseName}`;
24060
22873
  }
@@ -24094,7 +22907,7 @@ function validateFile(filePath, projectRoot) {
24094
22907
  });
24095
22908
  return checks;
24096
22909
  }
24097
- const source = readFileSync34(absPath, "utf-8");
22910
+ const source = readFileSync33(absPath, "utf-8");
24098
22911
  const lines = source.split("\n");
24099
22912
  if (activeChecks.rule_compliance !== false) {
24100
22913
  for (const ruleSet of config.rules) {
@@ -24539,7 +23352,7 @@ var init_adr_generator = __esm({
24539
23352
  });
24540
23353
 
24541
23354
  // src/security-scorer.ts
24542
- import { existsSync as existsSync33, readFileSync as readFileSync35 } from "fs";
23355
+ import { existsSync as existsSync33, readFileSync as readFileSync34 } from "fs";
24543
23356
  function p12(baseName) {
24544
23357
  return `${getConfig().toolPrefix}_${baseName}`;
24545
23358
  }
@@ -24568,7 +23381,7 @@ function scoreFileSecurity(filePath, projectRoot) {
24568
23381
  }
24569
23382
  let source;
24570
23383
  try {
24571
- source = readFileSync35(absPath, "utf-8");
23384
+ source = readFileSync34(absPath, "utf-8");
24572
23385
  } catch {
24573
23386
  return { riskScore: 0, findings: [] };
24574
23387
  }
@@ -24857,7 +23670,7 @@ var init_security_scorer = __esm({
24857
23670
  });
24858
23671
 
24859
23672
  // src/dependency-scorer.ts
24860
- import { existsSync as existsSync34, readFileSync as readFileSync36 } from "fs";
23673
+ import { existsSync as existsSync34, readFileSync as readFileSync35 } from "fs";
24861
23674
  import { resolve as resolve28 } from "path";
24862
23675
  function p13(baseName) {
24863
23676
  return `${getConfig().toolPrefix}_${baseName}`;
@@ -24893,7 +23706,7 @@ function getInstalledPackages(projectRoot) {
24893
23706
  const pkgPath = resolve28(projectRoot, "package.json");
24894
23707
  if (!existsSync34(pkgPath)) return /* @__PURE__ */ new Map();
24895
23708
  try {
24896
- const pkg = JSON.parse(readFileSync36(pkgPath, "utf-8"));
23709
+ const pkg = JSON.parse(readFileSync35(pkgPath, "utf-8"));
24897
23710
  const packages = /* @__PURE__ */ new Map();
24898
23711
  for (const [name, version] of Object.entries(pkg.dependencies ?? {})) {
24899
23712
  packages.set(name, version);
@@ -25495,8 +24308,8 @@ var init_regression_detector = __esm({
25495
24308
  });
25496
24309
 
25497
24310
  // src/knowledge-indexer.ts
25498
- import { createHash as createHash9 } from "crypto";
25499
- import { readFileSync as readFileSync37, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync35 } from "fs";
24311
+ import { createHash as createHash8 } from "crypto";
24312
+ import { readFileSync as readFileSync36, readdirSync as readdirSync23, statSync as statSync14, existsSync as existsSync35 } from "fs";
25500
24313
  import { resolve as resolve29, relative as relative12, basename as basename8, extname as extname2 } from "path";
25501
24314
  function getKnowledgePaths() {
25502
24315
  const resolved = getResolvedPaths();
@@ -25523,7 +24336,7 @@ function discoverMarkdownFiles(baseDir) {
25523
24336
  const files = [];
25524
24337
  function walk(dir) {
25525
24338
  try {
25526
- const entries = readdirSync24(dir, { withFileTypes: true });
24339
+ const entries = readdirSync23(dir, { withFileTypes: true });
25527
24340
  for (const entry of entries) {
25528
24341
  const fullPath = resolve29(dir, entry.name);
25529
24342
  if (entry.isDirectory()) {
@@ -25583,7 +24396,7 @@ function categorizeFile(filePath) {
25583
24396
  return "root";
25584
24397
  }
25585
24398
  function hashContent2(content) {
25586
- return createHash9("sha256").update(content).digest("hex");
24399
+ return createHash8("sha256").update(content).digest("hex");
25587
24400
  }
25588
24401
  function parseCRTable(content) {
25589
24402
  const rules = [];
@@ -25851,7 +24664,7 @@ function indexAllKnowledge(db) {
25851
24664
  }
25852
24665
  for (const filePath of files) {
25853
24666
  if (!existsSync35(filePath)) continue;
25854
- const content = readFileSync37(filePath, "utf-8");
24667
+ const content = readFileSync36(filePath, "utf-8");
25855
24668
  const hash = hashContent2(content);
25856
24669
  const relPath = filePath.startsWith(paths.claudeDir) ? relative12(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative12(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative12(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative12(paths.memoryDir, filePath)}` : basename8(filePath);
25857
24670
  const category = categorizeFile(filePath);
@@ -26003,7 +24816,7 @@ var init_knowledge_indexer = __esm({
26003
24816
  });
26004
24817
 
26005
24818
  // src/knowledge-tools.ts
26006
- import { readFileSync as readFileSync38, writeFileSync as writeFileSync6, appendFileSync as appendFileSync2, readdirSync as readdirSync25 } from "fs";
24819
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync24 } from "fs";
26007
24820
  import { resolve as resolve30, basename as basename9 } from "path";
26008
24821
  function p16(baseName) {
26009
24822
  return `${getConfig().toolPrefix}_${baseName}`;
@@ -26754,14 +25567,14 @@ ${crRule ? `- **CR**: ${crRule}
26754
25567
  `;
26755
25568
  let existing = "";
26756
25569
  try {
26757
- existing = readFileSync38(correctionsPath, "utf-8");
25570
+ existing = readFileSync37(correctionsPath, "utf-8");
26758
25571
  } catch {
26759
25572
  }
26760
25573
  const archiveIdx = existing.indexOf("## Archived");
26761
25574
  if (archiveIdx > 0) {
26762
25575
  const before = existing.slice(0, archiveIdx);
26763
25576
  const after = existing.slice(archiveIdx);
26764
- writeFileSync6(correctionsPath, before + entry + after);
25577
+ writeFileSync5(correctionsPath, before + entry + after);
26765
25578
  } else {
26766
25579
  appendFileSync2(correctionsPath, entry);
26767
25580
  }
@@ -26892,9 +25705,9 @@ function handleGaps(db, args2) {
26892
25705
  lines.push(`## Knowledge Gap Analysis (${checkType})`);
26893
25706
  lines.push("");
26894
25707
  if (checkType === "features") {
26895
- let dataDb2 = null;
25708
+ let dataDb = null;
26896
25709
  try {
26897
- dataDb2 = getDataDb();
25710
+ dataDb = getDataDb();
26898
25711
  let sql = "SELECT feature_key, title, domain, status FROM massu_sentinel WHERE status = 'active'";
26899
25712
  const params = [];
26900
25713
  if (domain) {
@@ -26902,7 +25715,7 @@ function handleGaps(db, args2) {
26902
25715
  params.push(`%${domain}%`);
26903
25716
  }
26904
25717
  sql += " ORDER BY domain, feature_key";
26905
- const features = dataDb2.prepare(sql).all(...params);
25718
+ const features = dataDb.prepare(sql).all(...params);
26906
25719
  lines.push(`| Feature | Domain | Knowledge Hits | Status |`);
26907
25720
  lines.push(`|---------|--------|----------------|--------|`);
26908
25721
  let gapCount = 0;
@@ -26931,12 +25744,12 @@ function handleGaps(db, args2) {
26931
25744
  } catch (error) {
26932
25745
  lines.push(`Error querying sentinel: ${error instanceof Error ? error.message : String(error)}`);
26933
25746
  } finally {
26934
- dataDb2?.close();
25747
+ dataDb?.close();
26935
25748
  }
26936
25749
  } else if (checkType === "routers") {
26937
25750
  try {
26938
25751
  const routersDir = getResolvedPaths().routersDir;
26939
- const routerFiles = readdirSync25(routersDir).filter((f2) => f2.endsWith(".ts") && !f2.startsWith("_"));
25752
+ const routerFiles = readdirSync24(routersDir).filter((f2) => f2.endsWith(".ts") && !f2.startsWith("_"));
26940
25753
  lines.push(`| Router | Knowledge Hits | Status |`);
26941
25754
  lines.push(`|--------|----------------|--------|`);
26942
25755
  for (const file of routerFiles) {
@@ -26958,10 +25771,10 @@ function handleGaps(db, args2) {
26958
25771
  lines.push("### Pattern Coverage");
26959
25772
  lines.push(`Documented domains: ${[...documentedDomains].join(", ")}`);
26960
25773
  lines.push("");
26961
- let dataDb2 = null;
25774
+ let dataDb = null;
26962
25775
  try {
26963
- dataDb2 = getDataDb();
26964
- const domains = dataDb2.prepare(
25776
+ dataDb = getDataDb();
25777
+ const domains = dataDb.prepare(
26965
25778
  "SELECT DISTINCT domain FROM massu_sentinel WHERE status = 'active' ORDER BY domain"
26966
25779
  ).all();
26967
25780
  for (const d2 of domains) {
@@ -26970,7 +25783,7 @@ function handleGaps(db, args2) {
26970
25783
  }
26971
25784
  } catch {
26972
25785
  } finally {
26973
- dataDb2?.close();
25786
+ dataDb?.close();
26974
25787
  }
26975
25788
  } else if (checkType === "incidents") {
26976
25789
  const incidents = db.prepare(
@@ -27092,13 +25905,13 @@ var init_knowledge_tools = __esm({
27092
25905
 
27093
25906
  // src/knowledge-db.ts
27094
25907
  import Database3 from "better-sqlite3";
27095
- import { dirname as dirname16 } from "path";
27096
- import { existsSync as existsSync37, mkdirSync as mkdirSync11 } from "fs";
25908
+ import { dirname as dirname15 } from "path";
25909
+ import { existsSync as existsSync37, mkdirSync as mkdirSync10 } from "fs";
27097
25910
  function getKnowledgeDb() {
27098
25911
  const dbPath = getResolvedPaths().knowledgeDbPath;
27099
- const dir = dirname16(dbPath);
25912
+ const dir = dirname15(dbPath);
27100
25913
  if (!existsSync37(dir)) {
27101
- mkdirSync11(dir, { recursive: true });
25914
+ mkdirSync10(dir, { recursive: true });
27102
25915
  }
27103
25916
  const db = new Database3(dbPath);
27104
25917
  db.pragma("journal_mode = WAL");
@@ -27413,30 +26226,30 @@ function isPythonTool(name) {
27413
26226
  const base = stripPrefix2(name);
27414
26227
  return base.startsWith("py_");
27415
26228
  }
27416
- function handlePythonToolCall(name, args2, dataDb2) {
26229
+ function handlePythonToolCall(name, args2, dataDb) {
27417
26230
  const baseName = stripPrefix2(name);
27418
26231
  switch (baseName) {
27419
26232
  case "py_imports":
27420
- return handlePyImports(args2, dataDb2);
26233
+ return handlePyImports(args2, dataDb);
27421
26234
  case "py_routes":
27422
- return handlePyRoutes(args2, dataDb2);
26235
+ return handlePyRoutes(args2, dataDb);
27423
26236
  case "py_coupling":
27424
- return handlePyCoupling(args2, dataDb2);
26237
+ return handlePyCoupling(args2, dataDb);
27425
26238
  case "py_models":
27426
- return handlePyModels(args2, dataDb2);
26239
+ return handlePyModels(args2, dataDb);
27427
26240
  case "py_migrations":
27428
- return handlePyMigrations(args2, dataDb2);
26241
+ return handlePyMigrations(args2, dataDb);
27429
26242
  case "py_domains":
27430
- return handlePyDomains(args2, dataDb2);
26243
+ return handlePyDomains(args2, dataDb);
27431
26244
  case "py_impact":
27432
- return handlePyImpact(args2, dataDb2);
26245
+ return handlePyImpact(args2, dataDb);
27433
26246
  case "py_context":
27434
- return handlePyContext(args2, dataDb2);
26247
+ return handlePyContext(args2, dataDb);
27435
26248
  default:
27436
26249
  return text16(`Unknown Python tool: ${name}`);
27437
26250
  }
27438
26251
  }
27439
- function handlePyImports(args2, dataDb2) {
26252
+ function handlePyImports(args2, dataDb) {
27440
26253
  const lines = [];
27441
26254
  const file = args2.file;
27442
26255
  const importedBy = args2.imported_by;
@@ -27447,7 +26260,7 @@ function handlePyImports(args2, dataDb2) {
27447
26260
  let traverse2 = function(f2, depth) {
27448
26261
  if (depth > maxDepth || visited.has(f2)) return;
27449
26262
  visited.add(f2);
27450
- const imports = dataDb2.prepare(
26263
+ const imports = dataDb.prepare(
27451
26264
  "SELECT target_file, imported_names FROM massu_py_imports WHERE source_file = ?"
27452
26265
  ).all(f2);
27453
26266
  for (const imp of imports) {
@@ -27462,7 +26275,7 @@ function handlePyImports(args2, dataDb2) {
27462
26275
  const visited = /* @__PURE__ */ new Set();
27463
26276
  traverse2(file, 0);
27464
26277
  } else {
27465
- const imports = dataDb2.prepare(
26278
+ const imports = dataDb.prepare(
27466
26279
  "SELECT target_file, import_type, imported_names, line FROM massu_py_imports WHERE source_file = ? ORDER BY line"
27467
26280
  ).all(file);
27468
26281
  lines.push(`## Imports from ${file} (${imports.length} edges)`);
@@ -27472,7 +26285,7 @@ function handlePyImports(args2, dataDb2) {
27472
26285
  }
27473
26286
  }
27474
26287
  } else if (importedBy) {
27475
- const importers = dataDb2.prepare(
26288
+ const importers = dataDb.prepare(
27476
26289
  "SELECT source_file, imported_names, line FROM massu_py_imports WHERE target_file = ? ORDER BY source_file"
27477
26290
  ).all(importedBy);
27478
26291
  lines.push(`## Files importing ${importedBy} (${importers.length} edges)`);
@@ -27481,8 +26294,8 @@ function handlePyImports(args2, dataDb2) {
27481
26294
  lines.push(`- ${imp.source_file}:${imp.line}${names.length > 0 ? " (" + names.join(", ") + ")" : ""}`);
27482
26295
  }
27483
26296
  } else {
27484
- const total = dataDb2.prepare("SELECT COUNT(*) as cnt FROM massu_py_imports").get().cnt;
27485
- const files = dataDb2.prepare("SELECT COUNT(DISTINCT source_file) as cnt FROM massu_py_imports").get().cnt;
26297
+ const total = dataDb.prepare("SELECT COUNT(*) as cnt FROM massu_py_imports").get().cnt;
26298
+ const files = dataDb.prepare("SELECT COUNT(DISTINCT source_file) as cnt FROM massu_py_imports").get().cnt;
27486
26299
  lines.push("## Python Import Index Summary");
27487
26300
  lines.push(`- Total import edges: ${total}`);
27488
26301
  lines.push(`- Files with imports: ${files}`);
@@ -27492,7 +26305,7 @@ function handlePyImports(args2, dataDb2) {
27492
26305
  }
27493
26306
  return text16(lines.join("\n") || "No Python import data available. Run sync first.");
27494
26307
  }
27495
- function handlePyRoutes(args2, dataDb2) {
26308
+ function handlePyRoutes(args2, dataDb) {
27496
26309
  const lines = [];
27497
26310
  const limit = Math.min(Math.max(args2.limit || 500, 1), 5e3);
27498
26311
  let query = "SELECT * FROM massu_py_routes WHERE 1=1";
@@ -27513,9 +26326,9 @@ function handlePyRoutes(args2, dataDb2) {
27513
26326
  query += " AND is_authenticated = 0";
27514
26327
  }
27515
26328
  query += ` ORDER BY file, line LIMIT ${limit}`;
27516
- const routes = dataDb2.prepare(query).all(...params);
26329
+ const routes = dataDb.prepare(query).all(...params);
27517
26330
  if (args2.uncoupled) {
27518
- const callerCountStmt = dataDb2.prepare("SELECT COUNT(*) as cnt FROM massu_py_route_callers WHERE route_id = ?");
26331
+ const callerCountStmt = dataDb.prepare("SELECT COUNT(*) as cnt FROM massu_py_route_callers WHERE route_id = ?");
27519
26332
  const uncoupledRoutes = routes.filter((r2) => {
27520
26333
  const callers = callerCountStmt.get(r2.id).cnt;
27521
26334
  return callers === 0;
@@ -27536,7 +26349,7 @@ function handlePyRoutes(args2, dataDb2) {
27536
26349
  }
27537
26350
  return text16(lines.join("\n") || "No Python routes found.");
27538
26351
  }
27539
- function handlePyCoupling(args2, dataDb2) {
26352
+ function handlePyCoupling(args2, dataDb) {
27540
26353
  const lines = [];
27541
26354
  let query = `
27542
26355
  SELECT rc.frontend_file, rc.call_pattern, rc.line as caller_line,
@@ -27555,7 +26368,7 @@ function handlePyCoupling(args2, dataDb2) {
27555
26368
  params.push(`%${args2.backend_path}%`);
27556
26369
  }
27557
26370
  query += " ORDER BY rc.frontend_file, rc.line";
27558
- const couplings = dataDb2.prepare(query).all(...params);
26371
+ const couplings = dataDb.prepare(query).all(...params);
27559
26372
  lines.push(`## Frontend \u2194 Backend Coupling (${couplings.length} connections)`);
27560
26373
  let currentFrontend = "";
27561
26374
  for (const c2 of couplings) {
@@ -27568,17 +26381,17 @@ function handlePyCoupling(args2, dataDb2) {
27568
26381
  }
27569
26382
  return text16(lines.join("\n") || "No coupling data found.");
27570
26383
  }
27571
- function handlePyModels(args2, dataDb2) {
26384
+ function handlePyModels(args2, dataDb) {
27572
26385
  const lines = [];
27573
26386
  if (args2.fk_graph) {
27574
- const edges = dataDb2.prepare("SELECT * FROM massu_py_fk_edges ORDER BY source_table").all();
26387
+ const edges = dataDb.prepare("SELECT * FROM massu_py_fk_edges ORDER BY source_table").all();
27575
26388
  lines.push(`## Foreign Key Graph (${edges.length} edges)`);
27576
26389
  for (const e2 of edges) {
27577
26390
  lines.push(`- ${e2.source_table}.${e2.source_column} \u2192 ${e2.target_table}.${e2.target_column}`);
27578
26391
  }
27579
26392
  } else if (args2.model || args2.table) {
27580
26393
  const search = args2.model || args2.table;
27581
- const model = dataDb2.prepare(
26394
+ const model = dataDb.prepare(
27582
26395
  "SELECT * FROM massu_py_models WHERE class_name = ? OR table_name = ?"
27583
26396
  ).get(search, search);
27584
26397
  if (!model) return text16(`Model "${search}" not found.`);
@@ -27607,7 +26420,7 @@ function handlePyModels(args2, dataDb2) {
27607
26420
  }
27608
26421
  } else {
27609
26422
  const limit = Math.min(Math.max(args2.limit || 500, 1), 5e3);
27610
- const models = dataDb2.prepare(`SELECT class_name, table_name, file, line FROM massu_py_models ORDER BY class_name LIMIT ${limit}`).all();
26423
+ const models = dataDb.prepare(`SELECT class_name, table_name, file, line FROM massu_py_models ORDER BY class_name LIMIT ${limit}`).all();
27611
26424
  lines.push(`## Python Models (${models.length})`);
27612
26425
  for (const m3 of models) {
27613
26426
  lines.push(`- ${m3.class_name} (${m3.table_name || "no table"}) \u2014 ${m3.file}:${m3.line}`);
@@ -27615,11 +26428,11 @@ function handlePyModels(args2, dataDb2) {
27615
26428
  }
27616
26429
  return text16(lines.join("\n") || "No Python models found.");
27617
26430
  }
27618
- function handlePyMigrations(args2, dataDb2) {
26431
+ function handlePyMigrations(args2, dataDb) {
27619
26432
  const lines = [];
27620
26433
  if (args2.drift) {
27621
- const models = dataDb2.prepare("SELECT class_name, table_name, columns FROM massu_py_models").all();
27622
- const migrations = dataDb2.prepare("SELECT revision, operations FROM massu_py_migrations").all();
26434
+ const models = dataDb.prepare("SELECT class_name, table_name, columns FROM massu_py_models").all();
26435
+ const migrations = dataDb.prepare("SELECT revision, operations FROM massu_py_migrations").all();
27623
26436
  const migratedTables = /* @__PURE__ */ new Set();
27624
26437
  for (const m3 of migrations) {
27625
26438
  const ops = safeParseArray(m3.operations);
@@ -27640,14 +26453,14 @@ function handlePyMigrations(args2, dataDb2) {
27640
26453
  }
27641
26454
  } else if (args2.chain) {
27642
26455
  const limit = Math.min(Math.max(args2.limit || 500, 1), 5e3);
27643
- const migrations = dataDb2.prepare(`SELECT * FROM massu_py_migrations ORDER BY id LIMIT ${limit}`).all();
26456
+ const migrations = dataDb.prepare(`SELECT * FROM massu_py_migrations ORDER BY id LIMIT ${limit}`).all();
27644
26457
  lines.push(`## Migration Chain (${migrations.length} revisions)`);
27645
26458
  for (const m3 of migrations) {
27646
26459
  const head = m3.is_head ? " [HEAD]" : "";
27647
26460
  lines.push(`- ${m3.revision}${head} \u2190 ${m3.down_revision || "BASE"}: ${m3.description || "(no description)"} (${m3.file})`);
27648
26461
  }
27649
26462
  } else if (args2.revision) {
27650
- const m3 = dataDb2.prepare("SELECT * FROM massu_py_migrations WHERE revision = ?").get(args2.revision);
26463
+ const m3 = dataDb.prepare("SELECT * FROM massu_py_migrations WHERE revision = ?").get(args2.revision);
27651
26464
  if (!m3) return text16(`Revision "${args2.revision}" not found.`);
27652
26465
  lines.push(`## Revision: ${m3.revision}`);
27653
26466
  lines.push(`Down: ${m3.down_revision || "BASE"}`);
@@ -27661,15 +26474,15 @@ function handlePyMigrations(args2, dataDb2) {
27661
26474
  }
27662
26475
  }
27663
26476
  } else {
27664
- const total = dataDb2.prepare("SELECT COUNT(*) as cnt FROM massu_py_migrations").get().cnt;
27665
- const head = dataDb2.prepare("SELECT revision FROM massu_py_migrations WHERE is_head = 1").get();
26477
+ const total = dataDb.prepare("SELECT COUNT(*) as cnt FROM massu_py_migrations").get().cnt;
26478
+ const head = dataDb.prepare("SELECT revision FROM massu_py_migrations WHERE is_head = 1").get();
27666
26479
  lines.push("## Migration Summary");
27667
26480
  lines.push(`- Total revisions: ${total}`);
27668
26481
  lines.push(`- Current head: ${head?.revision || "N/A"}`);
27669
26482
  }
27670
26483
  return text16(lines.join("\n") || "No Python migration data found.");
27671
26484
  }
27672
- function handlePyDomains(args2, dataDb2) {
26485
+ function handlePyDomains(args2, dataDb) {
27673
26486
  const lines = [];
27674
26487
  const config = getConfig();
27675
26488
  const domains = config.python?.domains || [];
@@ -27683,7 +26496,7 @@ function handlePyDomains(args2, dataDb2) {
27683
26496
  lines.push(`Allowed imports from: ${domainConfig.allowed_imports_from.join(", ") || "any"}`);
27684
26497
  }
27685
26498
  } else if (args2.crossings) {
27686
- const imports = dataDb2.prepare("SELECT source_file, target_file FROM massu_py_imports").all();
26499
+ const imports = dataDb.prepare("SELECT source_file, target_file FROM massu_py_imports").all();
27687
26500
  const violations = [];
27688
26501
  for (const imp of imports) {
27689
26502
  const srcDomain = classifyPythonFileDomain(imp.source_file);
@@ -27702,7 +26515,7 @@ function handlePyDomains(args2, dataDb2) {
27702
26515
  if (violations.length > 50) lines.push(`... and ${violations.length - 50} more`);
27703
26516
  } else if (args2.domain) {
27704
26517
  const domainName = args2.domain;
27705
- const allFiles = dataDb2.prepare("SELECT DISTINCT source_file FROM massu_py_imports UNION SELECT DISTINCT target_file FROM massu_py_imports").all();
26518
+ const allFiles = dataDb.prepare("SELECT DISTINCT source_file FROM massu_py_imports UNION SELECT DISTINCT target_file FROM massu_py_imports").all();
27706
26519
  const filesInDomain = allFiles.filter((f2) => classifyPythonFileDomain(f2.source_file) === domainName);
27707
26520
  lines.push(`## Domain: ${domainName} (${filesInDomain.length} files)`);
27708
26521
  for (const f2 of filesInDomain) {
@@ -27716,17 +26529,17 @@ function handlePyDomains(args2, dataDb2) {
27716
26529
  }
27717
26530
  return text16(lines.join("\n") || "No Python domains configured.");
27718
26531
  }
27719
- function handlePyImpact(args2, dataDb2) {
26532
+ function handlePyImpact(args2, dataDb) {
27720
26533
  const file = args2.file;
27721
26534
  const lines = [`## Impact Analysis: ${file}`, ""];
27722
- const importedBy = dataDb2.prepare(
26535
+ const importedBy = dataDb.prepare(
27723
26536
  "SELECT source_file FROM massu_py_imports WHERE target_file = ?"
27724
26537
  ).all(file);
27725
26538
  lines.push(`### Imported By (${importedBy.length} files)`);
27726
26539
  for (const imp of importedBy.slice(0, 20)) {
27727
26540
  lines.push(`- ${imp.source_file}`);
27728
26541
  }
27729
- const routes = dataDb2.prepare(
26542
+ const routes = dataDb.prepare(
27730
26543
  "SELECT method, path, function_name FROM massu_py_routes WHERE file = ?"
27731
26544
  ).all(file);
27732
26545
  if (routes.length > 0) {
@@ -27736,7 +26549,7 @@ function handlePyImpact(args2, dataDb2) {
27736
26549
  lines.push(`- ${r2.method} ${r2.path} \u2192 ${r2.function_name}`);
27737
26550
  }
27738
26551
  }
27739
- const models = dataDb2.prepare(
26552
+ const models = dataDb.prepare(
27740
26553
  "SELECT class_name, table_name FROM massu_py_models WHERE file = ?"
27741
26554
  ).all(file);
27742
26555
  if (models.length > 0) {
@@ -27746,10 +26559,10 @@ function handlePyImpact(args2, dataDb2) {
27746
26559
  lines.push(`- ${m3.class_name} (${m3.table_name || "no table"})`);
27747
26560
  }
27748
26561
  }
27749
- const routeIds = dataDb2.prepare("SELECT id FROM massu_py_routes WHERE file = ?").all(file);
26562
+ const routeIds = dataDb.prepare("SELECT id FROM massu_py_routes WHERE file = ?").all(file);
27750
26563
  if (routeIds.length > 0) {
27751
26564
  const placeholders = routeIds.map(() => "?").join(",");
27752
- const callers = dataDb2.prepare(
26565
+ const callers = dataDb.prepare(
27753
26566
  `SELECT DISTINCT frontend_file FROM massu_py_route_callers WHERE route_id IN (${placeholders})`
27754
26567
  ).all(...routeIds.map((r2) => r2.id));
27755
26568
  if (callers.length > 0) {
@@ -27766,13 +26579,13 @@ function handlePyImpact(args2, dataDb2) {
27766
26579
  ### Domain: ${domain}`);
27767
26580
  return text16(lines.join("\n"));
27768
26581
  }
27769
- function handlePyContext(args2, dataDb2) {
26582
+ function handlePyContext(args2, dataDb) {
27770
26583
  const file = args2.file;
27771
26584
  const lines = [`## Python Context: ${file}`, ""];
27772
26585
  const config = getConfig();
27773
26586
  const domain = classifyPythonFileDomain(file);
27774
26587
  lines.push(`**Domain**: ${domain}`);
27775
- const imports = dataDb2.prepare(
26588
+ const imports = dataDb.prepare(
27776
26589
  "SELECT target_file, imported_names FROM massu_py_imports WHERE source_file = ? LIMIT 20"
27777
26590
  ).all(file);
27778
26591
  if (imports.length > 0) {
@@ -27782,7 +26595,7 @@ function handlePyContext(args2, dataDb2) {
27782
26595
  lines.push(`- ${imp.target_file}${names.length > 0 ? ": " + names.join(", ") : ""}`);
27783
26596
  }
27784
26597
  }
27785
- const importedBy = dataDb2.prepare(
26598
+ const importedBy = dataDb.prepare(
27786
26599
  "SELECT source_file FROM massu_py_imports WHERE target_file = ? LIMIT 10"
27787
26600
  ).all(file);
27788
26601
  if (importedBy.length > 0) {
@@ -27791,14 +26604,14 @@ function handlePyContext(args2, dataDb2) {
27791
26604
  lines.push(`- ${imp.source_file}`);
27792
26605
  }
27793
26606
  }
27794
- const routes = dataDb2.prepare("SELECT method, path, function_name, line FROM massu_py_routes WHERE file = ?").all(file);
26607
+ const routes = dataDb.prepare("SELECT method, path, function_name, line FROM massu_py_routes WHERE file = ?").all(file);
27795
26608
  if (routes.length > 0) {
27796
26609
  lines.push("\n### Routes");
27797
26610
  for (const r2 of routes) {
27798
26611
  lines.push(`- ${r2.method} ${r2.path} \u2192 ${r2.function_name} (L${r2.line})`);
27799
26612
  }
27800
26613
  }
27801
- const models = dataDb2.prepare("SELECT class_name, table_name, line FROM massu_py_models WHERE file = ?").all(file);
26614
+ const models = dataDb.prepare("SELECT class_name, table_name, line FROM massu_py_models WHERE file = ?").all(file);
27802
26615
  if (models.length > 0) {
27803
26616
  lines.push("\n### Models");
27804
26617
  for (const m3 of models) {
@@ -27831,7 +26644,7 @@ var init_python_tools = __esm({
27831
26644
  });
27832
26645
 
27833
26646
  // src/tools.ts
27834
- import { readFileSync as readFileSync39, existsSync as existsSync38 } from "fs";
26647
+ import { readFileSync as readFileSync38, existsSync as existsSync38 } from "fs";
27835
26648
  import { resolve as resolve31, basename as basename10 } from "path";
27836
26649
  function prefix2() {
27837
26650
  return getConfig().toolPrefix;
@@ -27846,41 +26659,41 @@ function stripPrefix3(name) {
27846
26659
  }
27847
26660
  return name;
27848
26661
  }
27849
- function ensureIndexes(dataDb2, codegraphDb2, force = false) {
26662
+ function ensureIndexes(dataDb, codegraphDb, force = false) {
27850
26663
  const results = [];
27851
26664
  const config = getConfig();
27852
- if (force || isDataStale(dataDb2, codegraphDb2)) {
27853
- const importCount = buildImportIndex(dataDb2, codegraphDb2);
26665
+ if (codegraphDb !== void 0 && (force || isDataStale(dataDb, codegraphDb))) {
26666
+ const importCount = buildImportIndex(dataDb, codegraphDb);
27854
26667
  results.push(`Import edges: ${importCount}`);
27855
26668
  if (config.framework.router === "trpc") {
27856
- const trpcStats = buildTrpcIndex(dataDb2);
26669
+ const trpcStats = buildTrpcIndex(dataDb);
27857
26670
  results.push(`tRPC procedures: ${trpcStats.totalProcedures} (${trpcStats.withCallers} with UI, ${trpcStats.withoutCallers} without)`);
27858
26671
  }
27859
- const pageCount = buildPageDeps(dataDb2, codegraphDb2);
26672
+ const pageCount = buildPageDeps(dataDb, codegraphDb);
27860
26673
  results.push(`Page deps: ${pageCount} pages`);
27861
26674
  if (config.paths.middleware) {
27862
- const middlewareCount = buildMiddlewareTree(dataDb2);
26675
+ const middlewareCount = buildMiddlewareTree(dataDb);
27863
26676
  results.push(`Middleware tree: ${middlewareCount} files`);
27864
26677
  }
27865
- updateBuildTimestamp(dataDb2);
26678
+ updateBuildTimestamp(dataDb);
27866
26679
  }
27867
26680
  if (config.python?.root) {
27868
26681
  const pythonRoot = config.python.root;
27869
26682
  const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
27870
- if (force || isPythonDataStale(dataDb2, resolve31(getProjectRoot(), pythonRoot))) {
27871
- const pyImports = buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs);
26683
+ if (force || isPythonDataStale(dataDb, resolve31(getProjectRoot(), pythonRoot))) {
26684
+ const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
27872
26685
  results.push(`Python imports: ${pyImports}`);
27873
- const pyRoutes = buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs);
26686
+ const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
27874
26687
  results.push(`Python routes: ${pyRoutes}`);
27875
- const pyModels = buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs);
26688
+ const pyModels = buildPythonModelIndex(dataDb, pythonRoot, excludeDirs);
27876
26689
  results.push(`Python models: ${pyModels}`);
27877
26690
  if (config.python.alembic_dir) {
27878
- const pyMigrations = buildPythonMigrationIndex(dataDb2, config.python.alembic_dir);
26691
+ const pyMigrations = buildPythonMigrationIndex(dataDb, config.python.alembic_dir);
27879
26692
  results.push(`Python migrations: ${pyMigrations}`);
27880
26693
  }
27881
- const pyCoupling = buildPythonCouplingIndex(dataDb2);
26694
+ const pyCoupling = buildPythonCouplingIndex(dataDb);
27882
26695
  results.push(`Python coupling: ${pyCoupling}`);
27883
- updatePythonBuildTimestamp(dataDb2);
26696
+ updatePythonBuildTimestamp(dataDb);
27884
26697
  }
27885
26698
  }
27886
26699
  if (results.length === 0) return "Indexes are up-to-date.";
@@ -28008,13 +26821,24 @@ function getToolDefinitions() {
28008
26821
  }] : []
28009
26822
  ]);
28010
26823
  }
28011
- async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
26824
+ function assertDataDb(dataDb, toolName) {
26825
+ if (!dataDb) {
26826
+ throw new Error(`Internal: tool "${toolName}" routed to a Data-DB branch but dispatcher did not resolve Data DB. Check TOOL_DB_NEEDS manifest entry includes 'data'.`);
26827
+ }
26828
+ return dataDb;
26829
+ }
26830
+ function assertCodegraphDb(codegraphDb, toolName) {
26831
+ if (!codegraphDb) {
26832
+ throw new Error(`Internal: tool "${toolName}" routed to a CodeGraph branch but dispatcher did not resolve CodeGraph DB. Check TOOL_DB_NEEDS manifest entry includes 'codegraph'.`);
26833
+ }
26834
+ return codegraphDb;
26835
+ }
26836
+ async function handleToolCall(name, args2, dataDb, codegraphDb) {
28012
26837
  const userTier = await getCurrentTier();
28013
26838
  const requiredTier = getToolTier(name);
28014
26839
  if (!isToolAllowed(name, userTier)) {
28015
26840
  return text17(`This tool requires ${requiredTier} tier. Current tier: ${userTier}. Upgrade at https://massu.ai/pricing`);
28016
26841
  }
28017
- const syncMessage = ensureIndexes(dataDb2, codegraphDb2);
28018
26842
  const pfx = prefix2();
28019
26843
  try {
28020
26844
  if (name.startsWith(pfx + "_memory_")) {
@@ -28037,7 +26861,7 @@ async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
28037
26861
  return handleDocsToolCall(name, args2);
28038
26862
  }
28039
26863
  if (name.startsWith(pfx + "_sentinel_")) {
28040
- return handleSentinelToolCall(name, args2, dataDb2);
26864
+ return handleSentinelToolCall(name, args2, assertDataDb(dataDb, name));
28041
26865
  }
28042
26866
  if (isAnalyticsTool(name)) {
28043
26867
  const memDb = getMemoryDb();
@@ -28128,7 +26952,9 @@ async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
28128
26952
  }
28129
26953
  }
28130
26954
  if (isPythonTool(name)) {
28131
- return handlePythonToolCall(name, args2, dataDb2);
26955
+ const pyDataDb = assertDataDb(dataDb, name);
26956
+ ensureIndexes(pyDataDb);
26957
+ return handlePythonToolCall(name, args2, pyDataDb);
28132
26958
  }
28133
26959
  if (isLicenseTool(name)) {
28134
26960
  const memDb = getMemoryDb();
@@ -28140,18 +26966,40 @@ async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
28140
26966
  }
28141
26967
  const baseName = stripPrefix3(name);
28142
26968
  switch (baseName) {
28143
- case "sync":
28144
- return handleSync(dataDb2, codegraphDb2);
28145
- case "context":
28146
- return handleContext(args2.file, dataDb2, codegraphDb2);
28147
- case "trpc_map":
28148
- return handleTrpcMap(args2, dataDb2);
28149
- case "coupling_check":
28150
- return handleCouplingCheck(args2, dataDb2, codegraphDb2);
28151
- case "impact":
28152
- return handleImpact2(args2.file, dataDb2, codegraphDb2);
28153
- case "domains":
28154
- return handleDomains(args2, dataDb2, codegraphDb2);
26969
+ case "sync": {
26970
+ const d2 = assertDataDb(dataDb, name);
26971
+ const c2 = assertCodegraphDb(codegraphDb, name);
26972
+ return handleSync(d2, c2);
26973
+ }
26974
+ case "context": {
26975
+ const d2 = assertDataDb(dataDb, name);
26976
+ const c2 = assertCodegraphDb(codegraphDb, name);
26977
+ ensureIndexes(d2, c2);
26978
+ return handleContext(args2.file, d2, c2);
26979
+ }
26980
+ case "trpc_map": {
26981
+ const d2 = assertDataDb(dataDb, name);
26982
+ ensureIndexes(d2, codegraphDb);
26983
+ return handleTrpcMap(args2, d2);
26984
+ }
26985
+ case "coupling_check": {
26986
+ const d2 = assertDataDb(dataDb, name);
26987
+ const c2 = assertCodegraphDb(codegraphDb, name);
26988
+ ensureIndexes(d2, c2);
26989
+ return handleCouplingCheck(args2, d2, c2);
26990
+ }
26991
+ case "impact": {
26992
+ const d2 = assertDataDb(dataDb, name);
26993
+ const c2 = assertCodegraphDb(codegraphDb, name);
26994
+ ensureIndexes(d2, c2);
26995
+ return handleImpact2(args2.file, d2, c2);
26996
+ }
26997
+ case "domains": {
26998
+ const d2 = assertDataDb(dataDb, name);
26999
+ const c2 = assertCodegraphDb(codegraphDb, name);
27000
+ ensureIndexes(d2, c2);
27001
+ return handleDomains(args2, d2, c2);
27002
+ }
28155
27003
  case "schema":
28156
27004
  return handleSchema(args2);
28157
27005
  default:
@@ -28166,10 +27014,10 @@ async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
28166
27014
  function text17(content) {
28167
27015
  return { content: [{ type: "text", text: content }] };
28168
27016
  }
28169
- function handleSync(dataDb2, codegraphDb2) {
28170
- const result = ensureIndexes(dataDb2, codegraphDb2, true);
27017
+ function handleSync(dataDb, codegraphDb) {
27018
+ const result = ensureIndexes(dataDb, codegraphDb, true);
28171
27019
  try {
28172
- const scanResult = runFeatureScan(dataDb2);
27020
+ const scanResult = runFeatureScan(dataDb);
28173
27021
  return text17(`${result}
28174
27022
 
28175
27023
  Feature scan: ${scanResult.registered} features registered (${scanResult.fromProcedures} from procedures, ${scanResult.fromPages} from pages, ${scanResult.fromComponents} from components)`);
@@ -28179,9 +27027,9 @@ Feature scan: ${scanResult.registered} features registered (${scanResult.fromPro
28179
27027
  Feature scan failed: ${error instanceof Error ? error.message : String(error)}`);
28180
27028
  }
28181
27029
  }
28182
- function handleContext(file, dataDb2, codegraphDb2) {
27030
+ function handleContext(file, dataDb, codegraphDb) {
28183
27031
  const lines = [];
28184
- const nodes = codegraphDb2.prepare(
27032
+ const nodes = codegraphDb.prepare(
28185
27033
  "SELECT name, kind, start_line, end_line FROM nodes WHERE file_path = ? ORDER BY start_line"
28186
27034
  ).all(file);
28187
27035
  if (nodes.length > 0) {
@@ -28208,7 +27056,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
28208
27056
  }
28209
27057
  lines.push("");
28210
27058
  }
28211
- if (isInMiddlewareTree(dataDb2, file)) {
27059
+ if (isInMiddlewareTree(dataDb, file)) {
28212
27060
  lines.push("## WARNING: Middleware Import Tree");
28213
27061
  lines.push("This file is imported (directly or transitively) by the middleware entry point.");
28214
27062
  lines.push("NO Node.js dependencies allowed (pino, winston, fs, crypto, path, child_process).");
@@ -28217,7 +27065,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
28217
27065
  const domain = classifyFile(file);
28218
27066
  lines.push(`## Domain: ${domain}`);
28219
27067
  lines.push("");
28220
- const imports = dataDb2.prepare(
27068
+ const imports = dataDb.prepare(
28221
27069
  "SELECT target_file, imported_names FROM massu_imports WHERE source_file = ? LIMIT 20"
28222
27070
  ).all(file);
28223
27071
  if (imports.length > 0) {
@@ -28228,7 +27076,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
28228
27076
  }
28229
27077
  lines.push("");
28230
27078
  }
28231
- const importedBy = dataDb2.prepare(
27079
+ const importedBy = dataDb.prepare(
28232
27080
  "SELECT source_file FROM massu_imports WHERE target_file = ? LIMIT 20"
28233
27081
  ).all(file);
28234
27082
  if (importedBy.length > 0) {
@@ -28268,7 +27116,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
28268
27116
  const root = getProjectRoot();
28269
27117
  const absFilePath = ensureWithinRoot(resolve31(resolvedPaths.srcDir, "..", file), root);
28270
27118
  if (existsSync38(absFilePath)) {
28271
- const fileContent = readFileSync39(absFilePath, "utf-8").slice(0, 3e3);
27119
+ const fileContent = readFileSync38(absFilePath, "utf-8").slice(0, 3e3);
28272
27120
  const keywords = [];
28273
27121
  if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
28274
27122
  if (fileContent.includes("BigInt") || fileContent.includes("Decimal")) keywords.push("BigInt", "serialization");
@@ -28392,10 +27240,10 @@ function handleContext(file, dataDb2, codegraphDb2) {
28392
27240
  }
28393
27241
  return text17(lines.join("\n") || "No context available for this file.");
28394
27242
  }
28395
- function handleTrpcMap(args2, dataDb2) {
27243
+ function handleTrpcMap(args2, dataDb) {
28396
27244
  const lines = [];
28397
27245
  if (args2.uncoupled) {
28398
- const uncoupled = dataDb2.prepare(
27246
+ const uncoupled = dataDb.prepare(
28399
27247
  "SELECT router_name, procedure_name, procedure_type, router_file FROM massu_trpc_procedures WHERE has_ui_caller = 0 ORDER BY router_name, procedure_name"
28400
27248
  ).all();
28401
27249
  lines.push(`## Uncoupled Procedures (${uncoupled.length} total)`);
@@ -28410,7 +27258,7 @@ function handleTrpcMap(args2, dataDb2) {
28410
27258
  lines.push(`- ${proc.procedure_name} (${proc.procedure_type})`);
28411
27259
  }
28412
27260
  } else if (args2.router) {
28413
- const procs = dataDb2.prepare(
27261
+ const procs = dataDb.prepare(
28414
27262
  "SELECT id, procedure_name, procedure_type, has_ui_caller FROM massu_trpc_procedures WHERE router_name = ? ORDER BY procedure_name"
28415
27263
  ).all(args2.router);
28416
27264
  lines.push(`## Router: ${args2.router} (${procs.length} procedures)`);
@@ -28418,7 +27266,7 @@ function handleTrpcMap(args2, dataDb2) {
28418
27266
  for (const proc of procs) {
28419
27267
  const status = proc.has_ui_caller ? "" : " [NO UI CALLERS]";
28420
27268
  lines.push(`### ${args2.router}.${proc.procedure_name} (${proc.procedure_type})${status}`);
28421
- const callSites = dataDb2.prepare(
27269
+ const callSites = dataDb.prepare(
28422
27270
  "SELECT file, line, call_pattern FROM massu_trpc_call_sites WHERE procedure_id = ?"
28423
27271
  ).all(proc.id);
28424
27272
  if (callSites.length > 0) {
@@ -28432,7 +27280,7 @@ function handleTrpcMap(args2, dataDb2) {
28432
27280
  lines.push("");
28433
27281
  }
28434
27282
  } else if (args2.procedure) {
28435
- const procs = dataDb2.prepare(
27283
+ const procs = dataDb.prepare(
28436
27284
  "SELECT id, router_name, router_file, procedure_type, has_ui_caller FROM massu_trpc_procedures WHERE procedure_name = ? ORDER BY router_name"
28437
27285
  ).all(args2.procedure);
28438
27286
  lines.push(`## Procedure "${args2.procedure}" found in ${procs.length} routers`);
@@ -28440,7 +27288,7 @@ function handleTrpcMap(args2, dataDb2) {
28440
27288
  for (const proc of procs) {
28441
27289
  lines.push(`### ${proc.router_name}.${args2.procedure} (${proc.procedure_type})`);
28442
27290
  lines.push(`File: ${proc.router_file}`);
28443
- const callSites = dataDb2.prepare(
27291
+ const callSites = dataDb.prepare(
28444
27292
  "SELECT file, line, call_pattern FROM massu_trpc_call_sites WHERE procedure_id = ?"
28445
27293
  ).all(proc.id);
28446
27294
  if (callSites.length > 0) {
@@ -28454,8 +27302,8 @@ function handleTrpcMap(args2, dataDb2) {
28454
27302
  lines.push("");
28455
27303
  }
28456
27304
  } else {
28457
- const total = dataDb2.prepare("SELECT COUNT(*) as count FROM massu_trpc_procedures").get();
28458
- const coupled = dataDb2.prepare("SELECT COUNT(*) as count FROM massu_trpc_procedures WHERE has_ui_caller = 1").get();
27305
+ const total = dataDb.prepare("SELECT COUNT(*) as count FROM massu_trpc_procedures").get();
27306
+ const coupled = dataDb.prepare("SELECT COUNT(*) as count FROM massu_trpc_procedures WHERE has_ui_caller = 1").get();
28459
27307
  const uncoupled = total.count - coupled.count;
28460
27308
  lines.push("## tRPC Procedure Summary");
28461
27309
  lines.push(`- Total procedures: ${total.count}`);
@@ -28467,16 +27315,16 @@ function handleTrpcMap(args2, dataDb2) {
28467
27315
  }
28468
27316
  return text17(lines.join("\n"));
28469
27317
  }
28470
- function handleCouplingCheck(args2, dataDb2, codegraphDb2) {
27318
+ function handleCouplingCheck(args2, dataDb, codegraphDb) {
28471
27319
  const lines = [];
28472
27320
  const stagedFiles = args2.staged_files;
28473
27321
  let uncoupledProcs;
28474
27322
  if (stagedFiles) {
28475
- uncoupledProcs = dataDb2.prepare(
27323
+ uncoupledProcs = dataDb.prepare(
28476
27324
  `SELECT router_name, procedure_name, procedure_type, router_file FROM massu_trpc_procedures WHERE has_ui_caller = 0 AND router_file IN (${stagedFiles.map(() => "?").join(",")})`
28477
27325
  ).all(...stagedFiles);
28478
27326
  } else {
28479
- uncoupledProcs = dataDb2.prepare(
27327
+ uncoupledProcs = dataDb.prepare(
28480
27328
  "SELECT router_name, procedure_name, procedure_type, router_file FROM massu_trpc_procedures WHERE has_ui_caller = 0"
28481
27329
  ).all();
28482
27330
  }
@@ -28492,12 +27340,12 @@ function handleCouplingCheck(args2, dataDb2, codegraphDb2) {
28492
27340
  lines.push("### Uncoupled Procedures: 0 (PASS)");
28493
27341
  lines.push("");
28494
27342
  }
28495
- const allPages = codegraphDb2.prepare(
27343
+ const allPages = codegraphDb.prepare(
28496
27344
  "SELECT path FROM files WHERE path LIKE 'src/app/%/page.tsx' OR path = 'src/app/page.tsx'"
28497
27345
  ).all();
28498
27346
  const pageImports = /* @__PURE__ */ new Set();
28499
27347
  for (const page of allPages) {
28500
- const imports = dataDb2.prepare(
27348
+ const imports = dataDb.prepare(
28501
27349
  "SELECT target_file FROM massu_imports WHERE source_file = ?"
28502
27350
  ).all(page.path);
28503
27351
  for (const imp of imports) {
@@ -28507,7 +27355,7 @@ function handleCouplingCheck(args2, dataDb2, codegraphDb2) {
28507
27355
  let componentFiles;
28508
27356
  if (stagedFiles) {
28509
27357
  const placeholders = stagedFiles.map(() => "?").join(",");
28510
- componentFiles = codegraphDb2.prepare(
27358
+ componentFiles = codegraphDb.prepare(
28511
27359
  `SELECT path FROM files WHERE path LIKE 'src/components/%' AND path IN (${placeholders})`
28512
27360
  ).all(...stagedFiles);
28513
27361
  } else {
@@ -28525,11 +27373,11 @@ function handleCouplingCheck(args2, dataDb2, codegraphDb2) {
28525
27373
  lines.push(`### RESULT: ${totalIssues === 0 ? "PASS" : `FAIL (${totalIssues} issues)`}`);
28526
27374
  return text17(lines.join("\n"));
28527
27375
  }
28528
- function handleImpact2(file, dataDb2, codegraphDb2) {
27376
+ function handleImpact2(file, dataDb, codegraphDb) {
28529
27377
  const lines = [];
28530
27378
  lines.push(`## Impact Analysis: ${file}`);
28531
27379
  lines.push("");
28532
- const affectedPages = findAffectedPages(dataDb2, file);
27380
+ const affectedPages = findAffectedPages(dataDb, file);
28533
27381
  if (affectedPages.length > 0) {
28534
27382
  const portals = [...new Set(affectedPages.map((p19) => p19.portal))];
28535
27383
  const allTables = [...new Set(affectedPages.flatMap((p19) => p19.tables))];
@@ -28559,7 +27407,7 @@ function handleImpact2(file, dataDb2, codegraphDb2) {
28559
27407
  lines.push("No pages affected (file may not be in any page dependency chain).");
28560
27408
  lines.push("");
28561
27409
  }
28562
- const inMiddleware = isInMiddlewareTree(dataDb2, file);
27410
+ const inMiddleware = isInMiddlewareTree(dataDb, file);
28563
27411
  if (inMiddleware) {
28564
27412
  lines.push("### WARNING: In Middleware Import Tree");
28565
27413
  lines.push("Changes to this file affect Edge Runtime. No Node.js deps allowed.");
@@ -28569,7 +27417,7 @@ function handleImpact2(file, dataDb2, codegraphDb2) {
28569
27417
  lines.push("");
28570
27418
  const fileDomain = classifyFile(file);
28571
27419
  lines.push(`### Domain: ${fileDomain}`);
28572
- const imports = dataDb2.prepare(
27420
+ const imports = dataDb.prepare(
28573
27421
  "SELECT target_file FROM massu_imports WHERE source_file = ?"
28574
27422
  ).all(file);
28575
27423
  const crossings = [];
@@ -28587,7 +27435,7 @@ function handleImpact2(file, dataDb2, codegraphDb2) {
28587
27435
  }
28588
27436
  return text17(lines.join("\n"));
28589
27437
  }
28590
- function handleDomains(args2, dataDb2, codegraphDb2) {
27438
+ function handleDomains(args2, dataDb, codegraphDb) {
28591
27439
  const lines = [];
28592
27440
  const domains = getConfig().domains;
28593
27441
  if (args2.file) {
@@ -28600,7 +27448,7 @@ function handleDomains(args2, dataDb2, codegraphDb2) {
28600
27448
  lines.push(`Allowed imports from: ${domainConfig.allowedImportsFrom.join(", ") || "any domain (system)"}`);
28601
27449
  }
28602
27450
  } else if (args2.crossings) {
28603
- const crossings = findCrossDomainImports(dataDb2);
27451
+ const crossings = findCrossDomainImports(dataDb);
28604
27452
  const violations = crossings.filter((c2) => !c2.allowed);
28605
27453
  const allowed = crossings.filter((c2) => c2.allowed);
28606
27454
  lines.push(`## Cross-Domain Import Analysis`);
@@ -28619,7 +27467,7 @@ function handleDomains(args2, dataDb2, codegraphDb2) {
28619
27467
  }
28620
27468
  } else if (args2.domain) {
28621
27469
  const domainName = args2.domain;
28622
- const files = getFilesInDomain(dataDb2, codegraphDb2, domainName);
27470
+ const files = getFilesInDomain(dataDb, codegraphDb, domainName);
28623
27471
  const config = domains.find((d2) => d2.name === domainName);
28624
27472
  lines.push(`## Domain: ${domainName}`);
28625
27473
  if (config) {
@@ -28696,7 +27544,7 @@ function handleSchema(args2) {
28696
27544
  if (!existsSync38(absPath)) {
28697
27545
  return text17(`File not found: ${file}`);
28698
27546
  }
28699
- const source = readFileSync39(absPath, "utf-8");
27547
+ const source = readFileSync38(absPath, "utf-8");
28700
27548
  const config = getConfig();
28701
27549
  const dbPattern = config.dbAccessPattern ?? "ctx.db.{table}";
28702
27550
  const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
@@ -28772,15 +27620,163 @@ var init_tools = __esm({
28772
27620
  }
28773
27621
  });
28774
27622
 
27623
+ // src/tool-db-needs.ts
27624
+ function stripConfiguredPrefix(toolName, prefix3) {
27625
+ const pfx = `${prefix3}_`;
27626
+ return toolName.startsWith(pfx) ? toolName.slice(pfx.length) : toolName;
27627
+ }
27628
+ function getToolDbNeeds(toolName, prefix3) {
27629
+ const shortName = stripConfiguredPrefix(toolName, prefix3);
27630
+ const needs = TOOL_DB_NEEDS[shortName];
27631
+ if (needs === void 0) {
27632
+ throw new UnknownToolError(toolName);
27633
+ }
27634
+ return needs;
27635
+ }
27636
+ var UnknownToolError, TOOL_DB_NEEDS;
27637
+ var init_tool_db_needs = __esm({
27638
+ "src/tool-db-needs.ts"() {
27639
+ "use strict";
27640
+ UnknownToolError = class extends Error {
27641
+ toolName;
27642
+ constructor(toolName) {
27643
+ super(`Tool not registered in TOOL_DB_NEEDS manifest: ${toolName}. Add an entry to packages/core/src/tool-db-needs.ts.`);
27644
+ this.name = "UnknownToolError";
27645
+ this.toolName = toolName;
27646
+ }
27647
+ };
27648
+ TOOL_DB_NEEDS = {
27649
+ // === Core code-intel tools (tools.ts:393-406) ===
27650
+ // Use CodeGraph DB (read-only AST) + Data DB (Massu's import/trpc/sentinel
27651
+ // tables). All call `ensureIndexes` directly or via shared infrastructure.
27652
+ sync: ["codegraph", "data"],
27653
+ context: ["codegraph", "data"],
27654
+ coupling_check: ["codegraph", "data"],
27655
+ impact: ["codegraph", "data"],
27656
+ domains: ["codegraph", "data"],
27657
+ // `trpc_map` reads only Data DB (tRPC index lives there); no CodeGraph access.
27658
+ trpc_map: ["data"],
27659
+ // `schema` reads filesystem (Prisma schema files); no DB access at all.
27660
+ schema: [],
27661
+ // === Memory tools (memory-tools.ts) ===
27662
+ // Routed via `name.startsWith(pfx + '_memory_')` at tools.ts:284-290.
27663
+ // Handler opens memory DB per-call (with try/finally close).
27664
+ memory_search: ["memory"],
27665
+ memory_timeline: ["memory"],
27666
+ memory_detail: ["memory"],
27667
+ memory_sessions: ["memory"],
27668
+ memory_failures: ["memory"],
27669
+ memory_ingest: ["memory"],
27670
+ memory_backfill: ["memory"],
27671
+ // === Observability tools (observability-tools.ts) ===
27672
+ // Routed via `isObservabilityTool(name)` at tools.ts:294-300. Memory DB only.
27673
+ session_replay: ["memory"],
27674
+ session_stats: ["memory"],
27675
+ tool_patterns: ["memory"],
27676
+ prompt_analysis: ["memory"],
27677
+ // === Docs tools (docs-tools.ts) ===
27678
+ // Routed via `name.startsWith(pfx + '_docs_')` at tools.ts:303-306.
27679
+ // No DB access — pure filesystem traversal.
27680
+ docs_audit: [],
27681
+ docs_coverage: [],
27682
+ // === Sentinel registry tools (sentinel-tools.ts:180-184) ===
27683
+ // Routed via `name.startsWith(pfx + '_sentinel_')` at tools.ts:308.
27684
+ // Handler signature: `(name, args, dataDb)` — Data DB only.
27685
+ sentinel_register: ["data"],
27686
+ sentinel_validate: ["data"],
27687
+ sentinel_search: ["data"],
27688
+ sentinel_detail: ["data"],
27689
+ sentinel_impact: ["data"],
27690
+ sentinel_parity: ["data"],
27691
+ // === Knowledge layer tools (knowledge-tools.ts) ===
27692
+ // Routed via `isKnowledgeTool(name)` at tools.ts:372-376. Primary DB is
27693
+ // `knowledgeDb` (separate SQLite file). Handlers ALSO call `getDataDb()`
27694
+ // (knowledge-tools.ts:1187,1275) and `getMemoryDb()` (knowledge-tools.ts:1332)
27695
+ // for cross-DB joins — declare all three so the AST completeness test
27696
+ // (P-B-002) verifies the full access pattern.
27697
+ knowledge_search: ["knowledge", "data", "memory"],
27698
+ knowledge_pattern: ["knowledge", "data", "memory"],
27699
+ knowledge_rule: ["knowledge", "data", "memory"],
27700
+ knowledge_correct: ["knowledge", "data", "memory"],
27701
+ knowledge_incident: ["knowledge", "data", "memory"],
27702
+ knowledge_plan: ["knowledge", "data", "memory"],
27703
+ knowledge_command: ["knowledge", "data", "memory"],
27704
+ knowledge_gaps: ["knowledge", "data", "memory"],
27705
+ knowledge_verification: ["knowledge", "data", "memory"],
27706
+ knowledge_effectiveness: ["knowledge", "data", "memory"],
27707
+ knowledge_graph: ["knowledge", "data", "memory"],
27708
+ knowledge_schema_check: ["knowledge", "data", "memory"],
27709
+ // === Analytics / quality (analytics.ts) ===
27710
+ // Routed via `isAnalyticsTool(name)`. Memory DB only.
27711
+ quality_score: ["memory"],
27712
+ quality_report: ["memory"],
27713
+ quality_trend: ["memory"],
27714
+ // === Cost tracker (cost-tracker.ts) ===
27715
+ cost_session: ["memory"],
27716
+ cost_feature: ["memory"],
27717
+ cost_trend: ["memory"],
27718
+ // === Prompt analyzer (prompt-analyzer.ts) ===
27719
+ prompt_effectiveness: ["memory"],
27720
+ prompt_suggestions: ["memory"],
27721
+ // === Audit trail (audit-trail.ts) ===
27722
+ audit_chain: ["memory"],
27723
+ audit_log: ["memory"],
27724
+ audit_report: ["memory"],
27725
+ // === Validation engine (validation-engine.ts) ===
27726
+ validation_check: ["memory"],
27727
+ validation_report: ["memory"],
27728
+ // === ADR generator (adr-generator.ts) ===
27729
+ adr_create: ["memory"],
27730
+ adr_list: ["memory"],
27731
+ adr_detail: ["memory"],
27732
+ // === Security scorer (security-scorer.ts) ===
27733
+ security_score: ["memory"],
27734
+ security_heatmap: ["memory"],
27735
+ security_trend: ["memory"],
27736
+ // === Dependency scorer (dependency-scorer.ts) ===
27737
+ dep_score: ["memory"],
27738
+ dep_alternatives: ["memory"],
27739
+ // === Team knowledge (team-knowledge.ts) ===
27740
+ team_expertise: ["memory"],
27741
+ team_conflicts: ["memory"],
27742
+ team_search: ["memory"],
27743
+ // === Regression detector (regression-detector.ts) ===
27744
+ regression_risk: ["memory"],
27745
+ feature_health: ["memory"],
27746
+ // === Python code-intel tools (python-tools.ts) ===
27747
+ // Routed via `isPythonTool(name)` at tools.ts:379-381. Data DB only.
27748
+ py_imports: ["data"],
27749
+ py_routes: ["data"],
27750
+ py_models: ["data"],
27751
+ py_migrations: ["data"],
27752
+ py_coupling: ["data"],
27753
+ py_context: ["data"],
27754
+ py_impact: ["data"],
27755
+ py_domains: ["data"],
27756
+ // === License tool (license.ts) ===
27757
+ license_status: ["memory"]
27758
+ };
27759
+ }
27760
+ });
27761
+
28775
27762
  // src/server.ts
28776
27763
  var server_exports = {};
28777
- import { readFileSync as readFileSync40 } from "fs";
28778
- import { resolve as resolve32, dirname as dirname17 } from "path";
27764
+ import { readFileSync as readFileSync39 } from "fs";
27765
+ import { resolve as resolve32, dirname as dirname16 } from "path";
28779
27766
  import { fileURLToPath as fileURLToPath4 } from "url";
28780
- function getDb() {
28781
- if (!codegraphDb) codegraphDb = getCodeGraphDb();
28782
- if (!dataDb) dataDb = getDataDb();
28783
- return { codegraphDb, dataDb };
27767
+ function resolveDbsForTool(toolName) {
27768
+ const needs = getToolDbNeeds(toolName, getConfig().toolPrefix);
27769
+ let dataDbResolved;
27770
+ let codegraphDbResolved;
27771
+ if (needs.includes("data")) {
27772
+ if (!dataDbCache) dataDbCache = getDataDb();
27773
+ dataDbResolved = dataDbCache;
27774
+ }
27775
+ if (needs.includes("codegraph")) {
27776
+ if (!codegraphDbCache) codegraphDbCache = getCodeGraphDb();
27777
+ codegraphDbResolved = codegraphDbCache;
27778
+ }
27779
+ return { needs, dataDb: dataDbResolved, codegraphDb: codegraphDbResolved };
28784
27780
  }
28785
27781
  async function handleRequest(request) {
28786
27782
  const { method, params, id } = request;
@@ -28815,13 +27811,46 @@ async function handleRequest(request) {
28815
27811
  case "tools/call": {
28816
27812
  const toolName = params?.name;
28817
27813
  const toolArgs = params?.arguments ?? {};
28818
- const { codegraphDb: cgDb, dataDb: lDb } = getDb();
28819
- const result = await handleToolCall(toolName, toolArgs, lDb, cgDb);
28820
- return {
28821
- jsonrpc: "2.0",
28822
- id: id ?? null,
28823
- result
28824
- };
27814
+ try {
27815
+ const { dataDb: lDb, codegraphDb: cgDb } = resolveDbsForTool(toolName);
27816
+ const result = await handleToolCall(toolName, toolArgs, lDb, cgDb);
27817
+ return {
27818
+ jsonrpc: "2.0",
27819
+ id: id ?? null,
27820
+ result
27821
+ };
27822
+ } catch (err) {
27823
+ if (err instanceof CodegraphDbNotInitializedError) {
27824
+ return {
27825
+ jsonrpc: "2.0",
27826
+ id: id ?? null,
27827
+ error: {
27828
+ code: -32001,
27829
+ message: `Tool requires CodeGraph database which is not initialized for this repo`,
27830
+ data: {
27831
+ remedy: "npx @colbymchenry/codegraph@0.7.4 init . && npx @colbymchenry/codegraph@0.7.4 index .",
27832
+ codegraphDbPath: err.dbPath,
27833
+ tool: toolName
27834
+ }
27835
+ }
27836
+ };
27837
+ }
27838
+ if (err instanceof UnknownToolError) {
27839
+ return {
27840
+ jsonrpc: "2.0",
27841
+ id: id ?? null,
27842
+ error: {
27843
+ code: -32602,
27844
+ message: `Unknown tool: ${err.toolName}`,
27845
+ data: {
27846
+ remedy: "Tool not registered in TOOL_DB_NEEDS manifest. See packages/core/src/tool-db-needs.ts.",
27847
+ tool: toolName
27848
+ }
27849
+ }
27850
+ };
27851
+ }
27852
+ throw err;
27853
+ }
28825
27854
  }
28826
27855
  case "ping": {
28827
27856
  return { jsonrpc: "2.0", id: id ?? null, result: {} };
@@ -28858,7 +27887,7 @@ function pruneMemoryOnStartup() {
28858
27887
  );
28859
27888
  }
28860
27889
  }
28861
- var __dirname4, PKG_VERSION, codegraphDb, dataDb, buffer;
27890
+ var __dirname4, PKG_VERSION, codegraphDbCache, dataDbCache, buffer;
28862
27891
  var init_server = __esm({
28863
27892
  "src/server.ts"() {
28864
27893
  "use strict";
@@ -28867,17 +27896,18 @@ var init_server = __esm({
28867
27896
  init_tools();
28868
27897
  init_memory_db();
28869
27898
  init_license();
28870
- __dirname4 = dirname17(fileURLToPath4(import.meta.url));
27899
+ init_tool_db_needs();
27900
+ __dirname4 = dirname16(fileURLToPath4(import.meta.url));
28871
27901
  PKG_VERSION = (() => {
28872
27902
  try {
28873
- const pkg = JSON.parse(readFileSync40(resolve32(__dirname4, "..", "package.json"), "utf-8"));
27903
+ const pkg = JSON.parse(readFileSync39(resolve32(__dirname4, "..", "package.json"), "utf-8"));
28874
27904
  return pkg.version ?? "0.0.0";
28875
27905
  } catch {
28876
27906
  return "0.0.0";
28877
27907
  }
28878
27908
  })();
28879
- codegraphDb = null;
28880
- dataDb = null;
27909
+ codegraphDbCache = null;
27910
+ dataDbCache = null;
28881
27911
  pruneMemoryOnStartup();
28882
27912
  getCurrentTier().then((tier) => {
28883
27913
  process.stderr.write(`massu: License tier: ${tier}
@@ -28897,8 +27927,22 @@ var init_server = __esm({
28897
27927
  const line = buffer.slice(0, newlineIndex).trim();
28898
27928
  buffer = buffer.slice(newlineIndex + 1);
28899
27929
  if (!line) continue;
27930
+ let request = null;
27931
+ try {
27932
+ request = JSON.parse(line);
27933
+ } catch (parseError) {
27934
+ const errorResponse = {
27935
+ jsonrpc: "2.0",
27936
+ id: null,
27937
+ error: {
27938
+ code: -32700,
27939
+ message: `Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`
27940
+ }
27941
+ };
27942
+ process.stdout.write(JSON.stringify(errorResponse) + "\n");
27943
+ continue;
27944
+ }
28900
27945
  try {
28901
- const request = JSON.parse(line);
28902
27946
  const response = await handleRequest(request);
28903
27947
  if (request.id !== void 0) {
28904
27948
  const responseStr = JSON.stringify(response);
@@ -28907,10 +27951,10 @@ var init_server = __esm({
28907
27951
  } catch (error) {
28908
27952
  const errorResponse = {
28909
27953
  jsonrpc: "2.0",
28910
- id: null,
27954
+ id: request.id ?? null,
28911
27955
  error: {
28912
- code: -32700,
28913
- message: `Parse error: ${error instanceof Error ? error.message : String(error)}`
27956
+ code: -32603,
27957
+ message: `Internal error: ${error instanceof Error ? error.message : String(error)}`
28914
27958
  }
28915
27959
  };
28916
27960
  process.stdout.write(JSON.stringify(errorResponse) + "\n");
@@ -28918,8 +27962,8 @@ var init_server = __esm({
28918
27962
  }
28919
27963
  });
28920
27964
  process.stdin.on("end", () => {
28921
- if (codegraphDb) codegraphDb.close();
28922
- if (dataDb) dataDb.close();
27965
+ if (codegraphDbCache) codegraphDbCache.close();
27966
+ if (dataDbCache) dataDbCache.close();
28923
27967
  process.exit(0);
28924
27968
  });
28925
27969
  process.on("uncaughtException", (error) => {
@@ -29173,7 +28217,7 @@ var config_upgrade_exports = {};
29173
28217
  __export(config_upgrade_exports, {
29174
28218
  runConfigUpgrade: () => runConfigUpgrade
29175
28219
  });
29176
- import { existsSync as existsSync39, readFileSync as readFileSync41, writeFileSync as writeFileSync7, copyFileSync, unlinkSync as unlinkSync3 } from "fs";
28220
+ import { existsSync as existsSync39, readFileSync as readFileSync40, writeFileSync as writeFileSync6, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
29177
28221
  import { resolve as resolve33 } from "path";
29178
28222
  import { parse as parseYaml6 } from "yaml";
29179
28223
  async function runConfigUpgrade(opts = {}) {
@@ -29192,7 +28236,7 @@ async function runConfigUpgrade(opts = {}) {
29192
28236
  }
29193
28237
  try {
29194
28238
  copyFileSync(bakPath, configPath);
29195
- unlinkSync3(bakPath);
28239
+ unlinkSync2(bakPath);
29196
28240
  log("Config restored from backup.\n");
29197
28241
  return { exitCode: 0, action: "rolled-back" };
29198
28242
  } catch (e2) {
@@ -29208,7 +28252,7 @@ async function runConfigUpgrade(opts = {}) {
29208
28252
  }
29209
28253
  let existing;
29210
28254
  try {
29211
- const content = readFileSync41(configPath, "utf-8");
28255
+ const content = readFileSync40(configPath, "utf-8");
29212
28256
  const parsed = parseYaml6(content);
29213
28257
  if (!parsed || typeof parsed !== "object") {
29214
28258
  throw new Error("config is not a YAML object");
@@ -29231,8 +28275,8 @@ async function runConfigUpgrade(opts = {}) {
29231
28275
  fingerprint: computeFingerprint(detection)
29232
28276
  };
29233
28277
  try {
29234
- const original = readFileSync41(configPath, "utf-8");
29235
- writeFileSync7(bakPath, original, "utf-8");
28278
+ const original = readFileSync40(configPath, "utf-8");
28279
+ writeFileSync6(bakPath, original, "utf-8");
29236
28280
  } catch (e2) {
29237
28281
  const message = `Failed to write backup: ${e2 instanceof Error ? e2.message : String(e2)}`;
29238
28282
  err(message + "\n");
@@ -29265,7 +28309,7 @@ var config_check_drift_exports = {};
29265
28309
  __export(config_check_drift_exports, {
29266
28310
  runConfigCheckDrift: () => runConfigCheckDrift
29267
28311
  });
29268
- import { existsSync as existsSync40, readFileSync as readFileSync42 } from "fs";
28312
+ import { existsSync as existsSync40, readFileSync as readFileSync41 } from "fs";
29269
28313
  import { resolve as resolve34 } from "path";
29270
28314
  import { parse as parseYaml7 } from "yaml";
29271
28315
  function renderChanges(changes) {
@@ -29293,7 +28337,7 @@ async function runConfigCheckDrift(opts = {}) {
29293
28337
  }
29294
28338
  let config;
29295
28339
  try {
29296
- const content = readFileSync42(configPath, "utf-8");
28340
+ const content = readFileSync41(configPath, "utf-8");
29297
28341
  const parsed = parseYaml7(content);
29298
28342
  if (!parsed || typeof parsed !== "object") {
29299
28343
  throw new Error("config is not a YAML object");
@@ -29360,11 +28404,11 @@ var init_config_check_drift = __esm({
29360
28404
  });
29361
28405
 
29362
28406
  // src/cli.ts
29363
- import { readFileSync as readFileSync43 } from "fs";
29364
- import { resolve as resolve35, dirname as dirname18 } from "path";
28407
+ import { readFileSync as readFileSync42 } from "fs";
28408
+ import { resolve as resolve35, dirname as dirname17 } from "path";
29365
28409
  import { fileURLToPath as fileURLToPath5 } from "url";
29366
28410
  var __filename4 = fileURLToPath5(import.meta.url);
29367
- var __dirname5 = dirname18(__filename4);
28411
+ var __dirname5 = dirname17(__filename4);
29368
28412
  var args = process.argv.slice(2);
29369
28413
  var subcommand = args[0];
29370
28414
  async function main() {
@@ -29546,7 +28590,7 @@ Examples:
29546
28590
  }
29547
28591
  function printVersion() {
29548
28592
  try {
29549
- const pkg = JSON.parse(readFileSync43(resolve35(__dirname5, "../package.json"), "utf-8"));
28593
+ const pkg = JSON.parse(readFileSync42(resolve35(__dirname5, "../package.json"), "utf-8"));
29550
28594
  console.log(`massu v${pkg.version}`);
29551
28595
  } catch {
29552
28596
  console.log("massu v0.1.0");