scip-query 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/{chunk-JHVQB4Y5.js → chunk-334PCFO3.js} +12 -12
  2. package/dist/{chunk-OXX3QF24.js → chunk-46ZTW4AI.js} +2 -2
  3. package/dist/{chunk-24LF6IZB.js → chunk-4YN3PE57.js} +3 -3
  4. package/dist/{chunk-3NJSJ7TE.js → chunk-5YB6UXQ3.js} +4 -15
  5. package/dist/{chunk-Z5VSUOEE.js → chunk-6PVHJ332.js} +2 -2
  6. package/dist/{chunk-EAGKJFDX.js → chunk-7KGTWDAX.js} +3 -3
  7. package/dist/{chunk-PU2254N2.js → chunk-7OGXSMLY.js} +5 -15
  8. package/dist/{chunk-R7HPHMRZ.js → chunk-AEBM56CO.js} +3 -3
  9. package/dist/{chunk-SYQR4QGK.js → chunk-B747RITP.js} +3 -3
  10. package/dist/{chunk-A6XLXV6W.js → chunk-B7LDMCUS.js} +3 -3
  11. package/dist/{chunk-KLNKDX6A.js → chunk-C2VSV54P.js} +4 -4
  12. package/dist/{chunk-VKBOLNYN.js → chunk-DIYEUFVP.js} +10 -8
  13. package/dist/{chunk-RE7POFGI.js → chunk-E74RY6AQ.js} +2 -2
  14. package/dist/{chunk-RL74LF47.js → chunk-FIMTTUGE.js} +2 -2
  15. package/dist/{chunk-ALUFWH3U.js → chunk-FMAYH7GS.js} +116 -269
  16. package/dist/{chunk-MGNMHKX3.js → chunk-FO2CBB7U.js} +10 -2
  17. package/dist/{chunk-7BS4CPJX.js → chunk-HESWGDIV.js} +3 -3
  18. package/dist/{chunk-FVJE4MQL.js → chunk-HL2LXSBW.js} +5 -8
  19. package/dist/{chunk-KG4OFQEN.js → chunk-HW76DVE4.js} +3 -3
  20. package/dist/{chunk-RJ5GULL6.js → chunk-IC5NTO47.js} +2 -2
  21. package/dist/{chunk-VY2L4TP6.js → chunk-J34HAAEQ.js} +3 -3
  22. package/dist/{chunk-PCU455MX.js → chunk-JSXGC2EH.js} +2 -2
  23. package/dist/{chunk-NXUIWD6K.js → chunk-LQXBFCP2.js} +3 -3
  24. package/dist/{chunk-GNAMV3JC.js → chunk-NML6M5AS.js} +3 -3
  25. package/dist/{chunk-UJWI5CBB.js → chunk-NNFP4ZRF.js} +4 -7
  26. package/dist/{chunk-6ECR2FLR.js → chunk-NWXTQGUE.js} +4 -15
  27. package/dist/{chunk-ZVZAIIB5.js → chunk-NYZ6INK3.js} +3 -3
  28. package/dist/{chunk-JKXHHV4B.js → chunk-OMVF3BHY.js} +2 -2
  29. package/dist/{chunk-CBIWNZZZ.js → chunk-PGQXIAJF.js} +3 -3
  30. package/dist/{chunk-J47VSL6I.js → chunk-PKDFXASW.js} +3 -3
  31. package/dist/{chunk-POLELLNM.js → chunk-PSK5BPFE.js} +3 -3
  32. package/dist/{chunk-6CH23IAS.js → chunk-QZ4FRB65.js} +9 -7
  33. package/dist/{chunk-TWVXFKJA.js → chunk-R2QBMQQN.js} +4 -4
  34. package/dist/{chunk-5GCORUNV.js → chunk-T3ALCNCP.js} +30 -17
  35. package/dist/{chunk-QMXSLHZP.js → chunk-T6UVM534.js} +2 -2
  36. package/dist/{chunk-6UZU7DFL.js → chunk-U74VYTLX.js} +3 -3
  37. package/dist/{chunk-XUVPQDXW.js → chunk-UIRCHPOU.js} +4 -4
  38. package/dist/{chunk-43A4UCS7.js → chunk-UNS6ZQVX.js} +3 -3
  39. package/dist/{chunk-J6QXMYAQ.js → chunk-VJMTX3OR.js} +3 -3
  40. package/dist/{chunk-SVLUJSY7.js → chunk-XJSPWHNT.js} +4 -15
  41. package/dist/{chunk-W46L2BXT.js → chunk-XMZAC2VU.js} +2 -2
  42. package/dist/{chunk-ELFGD5EW.js → chunk-Y7FKURZG.js} +3 -3
  43. package/dist/{chunk-TO3L4YNK.js → chunk-YMSJCSRG.js} +5 -1
  44. package/dist/{chunk-DUJNJQPO.js → chunk-YQIWS5V6.js} +3 -3
  45. package/dist/{chunk-KYPXKV64.js → chunk-ZPEI7DRJ.js} +30 -15
  46. package/dist/cli.js +200 -373
  47. package/dist/{db-C4rPbKkI.d.ts → db-6F9R9e_t.d.ts} +0 -4
  48. package/dist/index.d.ts +2 -4
  49. package/dist/index.js +45 -57
  50. package/dist/queries/affected.d.ts +1 -1
  51. package/dist/queries/affected.js +3 -3
  52. package/dist/queries/bottlenecks.d.ts +1 -1
  53. package/dist/queries/bottlenecks.js +3 -3
  54. package/dist/queries/by-kind.d.ts +1 -1
  55. package/dist/queries/by-kind.js +3 -3
  56. package/dist/queries/call-graph.d.ts +1 -1
  57. package/dist/queries/call-graph.js +3 -3
  58. package/dist/queries/change-surface.d.ts +1 -1
  59. package/dist/queries/change-surface.js +3 -3
  60. package/dist/queries/code.d.ts +1 -1
  61. package/dist/queries/code.js +3 -3
  62. package/dist/queries/complexity-hotspots.d.ts +1 -1
  63. package/dist/queries/complexity-hotspots.js +3 -3
  64. package/dist/queries/complexity.d.ts +1 -1
  65. package/dist/queries/complexity.js +3 -3
  66. package/dist/queries/convergence.d.ts +1 -1
  67. package/dist/queries/convergence.js +3 -3
  68. package/dist/queries/coupling.d.ts +1 -1
  69. package/dist/queries/coupling.js +3 -3
  70. package/dist/queries/cycles.d.ts +1 -1
  71. package/dist/queries/cycles.js +3 -3
  72. package/dist/queries/dataflow.d.ts +1 -1
  73. package/dist/queries/dataflow.js +3 -3
  74. package/dist/queries/dead.d.ts +1 -1
  75. package/dist/queries/dead.js +4 -4
  76. package/dist/queries/deep-chains.d.ts +1 -1
  77. package/dist/queries/deep-chains.js +3 -3
  78. package/dist/queries/deps.d.ts +1 -1
  79. package/dist/queries/deps.js +3 -3
  80. package/dist/queries/diff-impact.d.ts +1 -1
  81. package/dist/queries/diff-impact.js +2 -2
  82. package/dist/queries/drift.d.ts +1 -1
  83. package/dist/queries/drift.js +3 -3
  84. package/dist/queries/extract-candidates.d.ts +1 -1
  85. package/dist/queries/extract-candidates.js +3 -3
  86. package/dist/queries/fan.d.ts +1 -1
  87. package/dist/queries/fan.js +3 -3
  88. package/dist/queries/files.d.ts +1 -1
  89. package/dist/queries/files.js +1 -1
  90. package/dist/queries/health.d.ts +1 -1
  91. package/dist/queries/health.js +14 -14
  92. package/dist/queries/hierarchy.d.ts +1 -1
  93. package/dist/queries/hierarchy.js +3 -3
  94. package/dist/queries/hotspots.d.ts +1 -1
  95. package/dist/queries/hotspots.js +3 -3
  96. package/dist/queries/imports.d.ts +1 -1
  97. package/dist/queries/imports.js +3 -3
  98. package/dist/queries/index.d.ts +1 -1
  99. package/dist/queries/index.js +45 -45
  100. package/dist/queries/isolated.d.ts +1 -1
  101. package/dist/queries/isolated.js +4 -4
  102. package/dist/queries/members.d.ts +1 -1
  103. package/dist/queries/members.js +3 -3
  104. package/dist/queries/methods.d.ts +1 -1
  105. package/dist/queries/methods.js +3 -3
  106. package/dist/queries/outline.d.ts +1 -1
  107. package/dist/queries/outline.js +3 -3
  108. package/dist/queries/passthrough-candidates.d.ts +1 -1
  109. package/dist/queries/passthrough-candidates.js +3 -3
  110. package/dist/queries/redundant-reexports.d.ts +1 -1
  111. package/dist/queries/redundant-reexports.js +4 -4
  112. package/dist/queries/refs.d.ts +1 -1
  113. package/dist/queries/refs.js +3 -3
  114. package/dist/queries/similar-chains.d.ts +1 -1
  115. package/dist/queries/similar-chains.js +3 -3
  116. package/dist/queries/similar-files.d.ts +1 -1
  117. package/dist/queries/similar-files.js +3 -3
  118. package/dist/queries/similar-signatures.d.ts +1 -1
  119. package/dist/queries/similar-signatures.js +3 -3
  120. package/dist/queries/similar.d.ts +2 -1
  121. package/dist/queries/similar.js +3 -3
  122. package/dist/queries/slice.d.ts +4 -3
  123. package/dist/queries/slice.js +3 -3
  124. package/dist/queries/stale-abstractions.d.ts +1 -1
  125. package/dist/queries/stale-abstractions.js +3 -3
  126. package/dist/queries/stats.d.ts +1 -1
  127. package/dist/queries/surface.d.ts +1 -1
  128. package/dist/queries/surface.js +3 -3
  129. package/dist/queries/symbols.d.ts +1 -1
  130. package/dist/queries/symbols.js +3 -3
  131. package/dist/queries/system.d.ts +1 -1
  132. package/dist/queries/system.js +3 -3
  133. package/dist/queries/trace.d.ts +1 -1
  134. package/dist/queries/trace.js +3 -3
  135. package/dist/queries/wrapper-candidates.d.ts +1 -1
  136. package/dist/queries/wrapper-candidates.js +3 -3
  137. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -20,18 +20,10 @@ var ScipDatabase = class {
20
20
  this.db = new Database(config.dbPath, { readonly: true });
21
21
  this.db.pragma("busy_timeout = 5000");
22
22
  }
23
- /** Attach a gitignore-based path filter for query results */
24
- setPathFilter(filter) {
25
- this.pathFilter = filter;
26
- }
27
23
  /** Check if a path should be excluded based on .gitignore rules */
28
24
  isIgnored(relativePath) {
29
25
  return this.pathFilter?.isIgnored(relativePath) ?? false;
30
26
  }
31
- /** Filter an array of paths using the gitignore filter */
32
- filterPaths(paths) {
33
- return this.pathFilter?.filter(paths) ?? paths;
34
- }
35
27
  /**
36
28
  * The local-symbol predicate: only match symbols that are defined
37
29
  * in files NOT excluded by gitignore. This replaces the old hardcoded
@@ -1323,10 +1315,6 @@ var Watcher = class {
1323
1315
  if (this.cooldownTimer) clearTimeout(this.cooldownTimer);
1324
1316
  this.setStatus({ state: "idle" });
1325
1317
  }
1326
- /** Get current watcher status */
1327
- getStatus() {
1328
- return this.status;
1329
- }
1330
1318
  // ── Internal ─────────────────────────────────────────────
1331
1319
  handleFileChange(filename) {
1332
1320
  const rel = relative2(this.projectRoot, join7(this.projectRoot, filename));
@@ -1479,12 +1467,20 @@ function stats(db) {
1479
1467
  }
1480
1468
 
1481
1469
  // src/queries/files.ts
1470
+ function globToLike(pattern) {
1471
+ const hasGlobChars = /[*?]/.test(pattern);
1472
+ if (!hasGlobChars) {
1473
+ return `%${pattern}%`;
1474
+ }
1475
+ return pattern.replace(/\*\*/g, "%").replace(/\*/g, "%").replace(/\?/g, "_");
1476
+ }
1482
1477
  function files(db, pattern) {
1478
+ const likePattern = globToLike(pattern);
1483
1479
  const rows = db.all(
1484
1480
  `SELECT relative_path FROM documents
1485
1481
  WHERE relative_path LIKE ?
1486
1482
  ORDER BY relative_path`,
1487
- `%${pattern}%`
1483
+ likePattern
1488
1484
  );
1489
1485
  return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({ relativePath: r.relative_path }));
1490
1486
  }
@@ -2539,44 +2535,55 @@ function getCachedMap(cache, db) {
2539
2535
  function normalizePath(path2) {
2540
2536
  return path2.replace(/\\/g, "/");
2541
2537
  }
2538
+ var LANGUAGE_EXTENSION_FAMILIES = [
2539
+ SOURCE_EXTENSIONS,
2540
+ PYTHON_SOURCE_EXTENSIONS,
2541
+ JVM_SOURCE_EXTENSIONS,
2542
+ RUST_SOURCE_EXTENSIONS,
2543
+ RUBY_SOURCE_EXTENSIONS,
2544
+ C_LIKE_SOURCE_EXTENSIONS,
2545
+ DOTNET_SOURCE_EXTENSIONS,
2546
+ DART_SOURCE_EXTENSIONS,
2547
+ PHP_SOURCE_EXTENSIONS
2548
+ ];
2549
+ function hasExtensionIn(relativePath, extensions) {
2550
+ return extensions.includes(extname3(relativePath).toLowerCase());
2551
+ }
2542
2552
  function isJavaScriptSourcePath(relativePath) {
2543
- return SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2553
+ return hasExtensionIn(relativePath, SOURCE_EXTENSIONS);
2544
2554
  }
2545
2555
  function isPythonSourcePath(relativePath) {
2546
- return PYTHON_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2556
+ return hasExtensionIn(relativePath, PYTHON_SOURCE_EXTENSIONS);
2547
2557
  }
2548
2558
  function isJvmSourcePath(relativePath) {
2549
- return JVM_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2559
+ return hasExtensionIn(relativePath, JVM_SOURCE_EXTENSIONS);
2550
2560
  }
2551
2561
  function isRustSourcePath(relativePath) {
2552
- return RUST_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2562
+ return hasExtensionIn(relativePath, RUST_SOURCE_EXTENSIONS);
2553
2563
  }
2554
2564
  function isRubySourcePath(relativePath) {
2555
- return RUBY_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2565
+ return hasExtensionIn(relativePath, RUBY_SOURCE_EXTENSIONS);
2556
2566
  }
2557
2567
  function isCLikeSourcePath(relativePath) {
2558
- return C_LIKE_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2568
+ return hasExtensionIn(relativePath, C_LIKE_SOURCE_EXTENSIONS);
2559
2569
  }
2560
2570
  function isDotNetSourcePath(relativePath) {
2561
- return DOTNET_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2571
+ return hasExtensionIn(relativePath, DOTNET_SOURCE_EXTENSIONS);
2562
2572
  }
2563
2573
  function isVisualBasicSourcePath(relativePath) {
2564
2574
  return extname3(relativePath).toLowerCase() === ".vb";
2565
2575
  }
2566
2576
  function isDartSourcePath(relativePath) {
2567
- return DART_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2577
+ return hasExtensionIn(relativePath, DART_SOURCE_EXTENSIONS);
2568
2578
  }
2569
2579
  function isPhpSourcePath(relativePath) {
2570
- return PHP_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2580
+ return hasExtensionIn(relativePath, PHP_SOURCE_EXTENSIONS);
2571
2581
  }
2572
2582
  function extensionFamilyFor(relativePath) {
2573
- if (isJvmSourcePath(relativePath)) return JVM_SOURCE_EXTENSIONS;
2574
- if (isDotNetSourcePath(relativePath)) return DOTNET_SOURCE_EXTENSIONS;
2575
- if (isPhpSourcePath(relativePath)) return PHP_SOURCE_EXTENSIONS;
2576
- if (isDartSourcePath(relativePath)) return DART_SOURCE_EXTENSIONS;
2577
- if (isCLikeSourcePath(relativePath)) return C_LIKE_SOURCE_EXTENSIONS;
2578
- if (isRustSourcePath(relativePath)) return RUST_SOURCE_EXTENSIONS;
2579
- if (isRubySourcePath(relativePath)) return RUBY_SOURCE_EXTENSIONS;
2583
+ const ext = extname3(relativePath).toLowerCase();
2584
+ for (const family of LANGUAGE_EXTENSION_FAMILIES) {
2585
+ if (family.includes(ext)) return family;
2586
+ }
2580
2587
  return SOURCE_EXTENSIONS;
2581
2588
  }
2582
2589
  function getSourceText(db, relativePath) {
@@ -2778,6 +2785,9 @@ function leafSuffix(raw) {
2778
2785
  const last = sym.descriptors[sym.descriptors.length - 1];
2779
2786
  return last?.suffix ?? null;
2780
2787
  }
2788
+ function isCallableSymbol(raw) {
2789
+ return raw.endsWith("().") || leafSuffix(raw) === "method";
2790
+ }
2781
2791
  function isFunctionLikeSymbol(raw) {
2782
2792
  const suffix = leafSuffix(raw);
2783
2793
  return suffix === "method" || suffix === "term";
@@ -3231,56 +3241,41 @@ function findEnclosingDefinition(definitions, line) {
3231
3241
  }
3232
3242
  return best;
3233
3243
  }
3234
- function getPythonSourceCalleeRows(db, symbol, limit) {
3235
- const match = getFullSymbolMatch(db, symbol);
3236
- if (!match || !isPythonDocument(db, match.relativePath)) {
3237
- return [];
3238
- }
3239
- const definitions = getDefinitionsForFile(db, match.relativePath);
3240
- const current = definitions.find((definition) => definition.symbolId === match.symbolId);
3241
- if (!current) {
3242
- return [];
3243
- }
3244
- const imports2 = getSourceImports(db, match.relativePath);
3245
- const bindings = new Map(
3246
- getSourceConstructorBindings(db, match.relativePath, {
3247
- startLine: match.startLine,
3248
- endLine: match.endLine
3249
- }).map((binding) => [binding.localName, binding.typeName])
3250
- );
3251
- const rows = [];
3252
- const seen = /* @__PURE__ */ new Set();
3253
- for (const call of getSourceCalls(db, match.relativePath, {
3254
- startLine: match.startLine,
3255
- endLine: match.endLine
3256
- })) {
3257
- const resolved = resolvePythonCallTarget(
3258
- db,
3259
- current,
3260
- definitions,
3261
- imports2,
3262
- bindings,
3263
- call.receiverName,
3264
- call.calleeName
3265
- );
3266
- if (!resolved || resolved.symbolId === match.symbolId || db.isIgnored(resolved.relativePath)) continue;
3267
- const chunkId = 1e9 + call.line;
3268
- const key = `${resolved.symbol}|${resolved.relativePath}|${chunkId}`;
3269
- if (seen.has(key)) continue;
3270
- seen.add(key);
3271
- rows.push({
3272
- symbol: resolved.symbol,
3273
- file: resolved.relativePath,
3274
- chunkId
3275
- });
3276
- }
3277
- return applyLimit(rows, limit);
3278
- }
3279
- function getJavaScriptSourceCalleeRows(db, symbol, limit) {
3280
- const match = getFullSymbolMatch(db, symbol);
3281
- if (!match || !isJavaScriptDocument(db, match.relativePath)) {
3282
- return [];
3283
- }
3244
+ var LANGUAGE_CALLEE_CONFIGS = [
3245
+ // Python (index 0) — complex resolver
3246
+ { kind: "complex", languageIndex: 0, resolver: resolvePythonCallTarget },
3247
+ // JavaScript/TypeScript (index 1) — complex resolver
3248
+ { kind: "complex", languageIndex: 1, resolver: resolveJavaScriptCallTarget },
3249
+ // Java (index 2) — simple with field bindings
3250
+ { kind: "simple", languageIndex: 2, parseBindings: (_db, source) => parseJavaFieldBindings(source) },
3251
+ // Kotlin (index 3) — simple with field bindings
3252
+ { kind: "simple", languageIndex: 3, parseBindings: (_db, source) => parseKotlinFieldBindings(source) },
3253
+ // Scala (index 4) — simple, no bindings
3254
+ { kind: "simple", languageIndex: 4, parseBindings: null },
3255
+ // C# (index 5) — simple, no bindings
3256
+ { kind: "simple", languageIndex: 5, parseBindings: null },
3257
+ // Visual Basic (index 6) — simple, no bindings
3258
+ { kind: "simple", languageIndex: 6, parseBindings: null },
3259
+ // C++ (index 7) simple with receiver bindings
3260
+ { kind: "simple", languageIndex: 7, parseBindings: (_db, source) => parseCppReceiverBindings(source) },
3261
+ // Rust (index 8) — simple, no bindings
3262
+ { kind: "simple", languageIndex: 8, parseBindings: null },
3263
+ // Ruby (index 9) simple with dual-attempt logic
3264
+ {
3265
+ kind: "simple",
3266
+ languageIndex: 9,
3267
+ parseBindings: (db, source) => parseRubyReceiverBindings(db, source),
3268
+ dualAttempt: {
3269
+ baseOpts: { allowInstanceVariables: true },
3270
+ extendedOpts: { allowInstanceVariables: true, allowBareMemberCalls: true }
3271
+ }
3272
+ },
3273
+ // Dart (index 10) — simple, no bindings
3274
+ { kind: "simple", languageIndex: 10, parseBindings: null },
3275
+ // PHP (index 11) — simple, no bindings
3276
+ { kind: "simple", languageIndex: 11, parseBindings: null }
3277
+ ];
3278
+ function getComplexSourceCalleeRows(db, match, config, limit) {
3284
3279
  const definitions = getDefinitionsForFile(db, match.relativePath);
3285
3280
  const current = definitions.find((definition) => definition.symbolId === match.symbolId);
3286
3281
  if (!current) {
@@ -3299,7 +3294,7 @@ function getJavaScriptSourceCalleeRows(db, symbol, limit) {
3299
3294
  startLine: match.startLine,
3300
3295
  endLine: match.endLine
3301
3296
  })) {
3302
- const resolved = resolveJavaScriptCallTarget(
3297
+ const resolved = config.resolver(
3303
3298
  db,
3304
3299
  current,
3305
3300
  definitions,
@@ -3321,134 +3316,31 @@ function getJavaScriptSourceCalleeRows(db, symbol, limit) {
3321
3316
  }
3322
3317
  return applyLimit(rows, limit);
3323
3318
  }
3324
- function getJavaSourceCalleeRows(db, symbol, limit) {
3325
- const match = getFullSymbolMatch(db, symbol);
3326
- if (!match || !isJavaDocument(db, match.relativePath)) {
3327
- return [];
3328
- }
3329
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3330
- const bindings = parseJavaFieldBindings(getSourceText(db, match.relativePath));
3331
- return resolveSimpleSourceCallees(db, match, calls, bindings, limit);
3332
- }
3333
- function getKotlinSourceCalleeRows(db, symbol, limit) {
3334
- const match = getFullSymbolMatch(db, symbol);
3335
- if (!match || !isKotlinDocument(db, match.relativePath)) {
3336
- return [];
3337
- }
3338
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3339
- const bindings = parseKotlinFieldBindings(getSourceText(db, match.relativePath));
3340
- return resolveSimpleSourceCallees(db, match, calls, bindings, limit);
3341
- }
3342
- function getScalaSourceCalleeRows(db, symbol, limit) {
3343
- const match = getFullSymbolMatch(db, symbol);
3344
- if (!match || !isScalaDocument(db, match.relativePath)) {
3345
- return [];
3346
- }
3347
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3348
- return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
3349
- }
3350
- function getCSharpSourceCalleeRows(db, symbol, limit) {
3351
- const match = getFullSymbolMatch(db, symbol);
3352
- if (!match || !isCSharpDocument(db, match.relativePath)) {
3353
- return [];
3354
- }
3355
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3356
- return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
3357
- }
3358
- function getVisualBasicSourceCalleeRows(db, symbol, limit) {
3359
- const match = getFullSymbolMatch(db, symbol);
3360
- if (!match || !isVisualBasicDocument(db, match.relativePath)) {
3361
- return [];
3362
- }
3363
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3364
- return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
3365
- }
3366
- function getCppSourceCalleeRows(db, symbol, limit) {
3367
- const match = getFullSymbolMatch(db, symbol);
3368
- if (!match || !isCppDocument(db, match.relativePath)) {
3369
- return [];
3319
+ function getSimpleLanguageCalleeRows(db, match, config, limit) {
3320
+ let calls;
3321
+ if (config.dualAttempt) {
3322
+ const baseCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.dualAttempt.baseOpts);
3323
+ const extendedCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.dualAttempt.extendedOpts);
3324
+ calls = extendedCalls.length > 0 ? extendedCalls : baseCalls;
3325
+ } else {
3326
+ calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.sourceCallOpts);
3370
3327
  }
3371
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3372
- const bindings = parseCppReceiverBindings(getSourceText(db, match.relativePath));
3328
+ const bindings = config.parseBindings ? config.parseBindings(db, getSourceText(db, match.relativePath)) : /* @__PURE__ */ new Map();
3373
3329
  return resolveSimpleSourceCallees(db, match, calls, bindings, limit);
3374
3330
  }
3375
- function getRustSourceCalleeRows(db, symbol, limit) {
3376
- const match = getFullSymbolMatch(db, symbol);
3377
- if (!match || !isRustDocument(db, match.relativePath)) {
3378
- return [];
3379
- }
3380
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3381
- return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
3382
- }
3383
- function getRubySourceCalleeRows(db, symbol, limit) {
3384
- const match = getFullSymbolMatch(db, symbol);
3385
- if (!match || !isRubyDocument(db, match.relativePath)) {
3386
- return [];
3387
- }
3388
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, { allowInstanceVariables: true });
3389
- const rubyCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, {
3390
- allowInstanceVariables: true,
3391
- allowBareMemberCalls: true
3392
- });
3393
- const bindings = parseRubyReceiverBindings(db, getSourceText(db, match.relativePath));
3394
- return resolveSimpleSourceCallees(db, match, rubyCalls.length > 0 ? rubyCalls : calls, bindings, limit);
3395
- }
3396
- function getDartSourceCalleeRows(db, symbol, limit) {
3397
- const match = getFullSymbolMatch(db, symbol);
3398
- if (!match || !isDartDocument(db, match.relativePath)) {
3399
- return [];
3400
- }
3401
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3402
- return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
3403
- }
3404
- function getPhpSourceCalleeRows(db, symbol, limit) {
3405
- const match = getFullSymbolMatch(db, symbol);
3406
- if (!match || !isPhpDocument(db, match.relativePath)) {
3407
- return [];
3408
- }
3409
- const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
3410
- return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
3411
- }
3412
3331
  function getSourceBackedCalleeRows(db, symbol, limit) {
3413
3332
  const match = getFullSymbolMatch(db, symbol);
3414
3333
  if (!match) {
3415
3334
  return [];
3416
3335
  }
3417
- if (isPythonDocument(db, match.relativePath)) {
3418
- return getPythonSourceCalleeRows(db, match, limit);
3419
- }
3420
- if (isJavaScriptDocument(db, match.relativePath)) {
3421
- return getJavaScriptSourceCalleeRows(db, match, limit);
3422
- }
3423
- if (isJavaDocument(db, match.relativePath)) {
3424
- return getJavaSourceCalleeRows(db, match, limit);
3425
- }
3426
- if (isScalaDocument(db, match.relativePath)) {
3427
- return getScalaSourceCalleeRows(db, match, limit);
3428
- }
3429
- if (isKotlinDocument(db, match.relativePath)) {
3430
- return getKotlinSourceCalleeRows(db, match, limit);
3431
- }
3432
- if (isCSharpDocument(db, match.relativePath)) {
3433
- return getCSharpSourceCalleeRows(db, match, limit);
3434
- }
3435
- if (isVisualBasicDocument(db, match.relativePath)) {
3436
- return getVisualBasicSourceCalleeRows(db, match, limit);
3437
- }
3438
- if (isCppDocument(db, match.relativePath)) {
3439
- return getCppSourceCalleeRows(db, match, limit);
3440
- }
3441
- if (isRustDocument(db, match.relativePath)) {
3442
- return getRustSourceCalleeRows(db, match, limit);
3443
- }
3444
- if (isRubyDocument(db, match.relativePath)) {
3445
- return getRubySourceCalleeRows(db, match, limit);
3446
- }
3447
- if (isDartDocument(db, match.relativePath)) {
3448
- return getDartSourceCalleeRows(db, match, limit);
3449
- }
3450
- if (isPhpDocument(db, match.relativePath)) {
3451
- return getPhpSourceCalleeRows(db, match, limit);
3336
+ for (const config of LANGUAGE_CALLEE_CONFIGS) {
3337
+ if (!isDocumentLanguage(db, match.relativePath, DOCUMENT_LANGUAGE_TABLE[config.languageIndex])) {
3338
+ continue;
3339
+ }
3340
+ if (config.kind === "complex") {
3341
+ return getComplexSourceCalleeRows(db, match, config, limit);
3342
+ }
3343
+ return getSimpleLanguageCalleeRows(db, match, config, limit);
3452
3344
  }
3453
3345
  return [];
3454
3346
  }
@@ -3458,9 +3350,11 @@ function getPythonSourceCallerRows(db, target, limit) {
3458
3350
  }
3459
3351
  const rows = [];
3460
3352
  const seen = /* @__PURE__ */ new Set();
3353
+ const pythonConfig = LANGUAGE_CALLEE_CONFIGS[0];
3461
3354
  for (const candidate of getAllFunctionLikeDefinitions(db)) {
3462
3355
  if (candidate.symbolId === target.symbolId) continue;
3463
- const callees = getPythonSourceCalleeRows(db, candidate);
3356
+ if (!isDocumentLanguage(db, candidate.relativePath, DOCUMENT_LANGUAGE_TABLE[pythonConfig.languageIndex])) continue;
3357
+ const callees = getComplexSourceCalleeRows(db, candidate, pythonConfig);
3464
3358
  if (!callees.some((callee) => callee.symbol === target.symbol)) continue;
3465
3359
  const key = `${candidate.symbol}|${candidate.relativePath}`;
3466
3360
  if (seen.has(key) || db.isIgnored(candidate.relativePath)) continue;
@@ -3802,6 +3696,17 @@ function getAllDefinitions(db, opts = {}) {
3802
3696
  );
3803
3697
  return rows.filter((row) => !db.isIgnored(row.relative_path)).flatMap((row) => getDefinitionsForFile(db, row.relative_path));
3804
3698
  }
3699
+ function getScopedDefinitions(db, scope) {
3700
+ const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
3701
+ return db.all(
3702
+ `SELECT relative_path
3703
+ FROM documents
3704
+ WHERE 1 = 1
3705
+ ${db.pathExclusionsFor("documents")}
3706
+ ${scopeFilter}
3707
+ ORDER BY relative_path`
3708
+ ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
3709
+ }
3805
3710
  function getAllFunctionLikeDefinitions(db) {
3806
3711
  return getAllDefinitions(db).filter((definition) => definition.isFunctionLike);
3807
3712
  }
@@ -4219,89 +4124,29 @@ function parentTypeName(rawSymbol) {
4219
4124
  }
4220
4125
  return null;
4221
4126
  }
4222
- function isPythonDocument(db, relativePath) {
4223
- const row = db.get(
4224
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4225
- relativePath
4226
- );
4227
- return row?.language === "python" || relativePath.endsWith(".py") || relativePath.endsWith(".pyi");
4228
- }
4229
- function isJavaScriptDocument(db, relativePath) {
4230
- const row = db.get(
4231
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4232
- relativePath
4233
- );
4234
- return row?.language === "typescript" || row?.language === "javascript" || /\.(?:[cm]?[jt]sx?)$/.test(relativePath);
4235
- }
4236
- function isJavaDocument(db, relativePath) {
4237
- const row = db.get(
4238
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4239
- relativePath
4240
- );
4241
- return row?.language === "java" || relativePath.endsWith(".java");
4242
- }
4243
- function isKotlinDocument(db, relativePath) {
4244
- const row = db.get(
4245
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4246
- relativePath
4247
- );
4248
- return row?.language === "kotlin" || relativePath.endsWith(".kt") || relativePath.endsWith(".kts");
4249
- }
4250
- function isScalaDocument(db, relativePath) {
4251
- const row = db.get(
4252
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4253
- relativePath
4254
- );
4255
- return row?.language === "scala" || relativePath.endsWith(".scala");
4256
- }
4257
- function isCSharpDocument(db, relativePath) {
4258
- const row = db.get(
4259
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4260
- relativePath
4261
- );
4262
- return row?.language === "C#" || relativePath.endsWith(".cs");
4263
- }
4264
- function isVisualBasicDocument(db, relativePath) {
4265
- const row = db.get(
4266
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4267
- relativePath
4268
- );
4269
- return row?.language === "Visual Basic" || relativePath.endsWith(".vb");
4270
- }
4271
- function isCppDocument(db, relativePath) {
4272
- const row = db.get(
4273
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4274
- relativePath
4275
- );
4276
- return row?.language === "CPP" || /\.(?:cc|cpp|cxx|hpp|hh|hxx)$/.test(relativePath);
4277
- }
4278
- function isRustDocument(db, relativePath) {
4279
- const row = db.get(
4280
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4281
- relativePath
4282
- );
4283
- return row?.language === "Rust" || relativePath.endsWith(".rs");
4284
- }
4285
- function isRubyDocument(db, relativePath) {
4286
- const row = db.get(
4287
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4288
- relativePath
4289
- );
4290
- return row?.language === "ruby" || relativePath.endsWith(".rb");
4291
- }
4292
- function isDartDocument(db, relativePath) {
4127
+ var DOCUMENT_LANGUAGE_TABLE = [
4128
+ { languages: ["python"], extensionPattern: /\.(?:py|pyi)$/ },
4129
+ { languages: ["typescript", "javascript"], extensionPattern: /\.(?:[cm]?[jt]sx?)$/ },
4130
+ { languages: ["java"], extensionPattern: /\.java$/ },
4131
+ { languages: ["kotlin"], extensionPattern: /\.(?:kt|kts)$/ },
4132
+ { languages: ["scala"], extensionPattern: /\.scala$/ },
4133
+ { languages: ["C#"], extensionPattern: /\.cs$/ },
4134
+ { languages: ["Visual Basic"], extensionPattern: /\.vb$/ },
4135
+ { languages: ["CPP"], extensionPattern: /\.(?:cc|cpp|cxx|hpp|hh|hxx)$/ },
4136
+ { languages: ["Rust"], extensionPattern: /\.rs$/ },
4137
+ { languages: ["ruby"], extensionPattern: /\.rb$/ },
4138
+ { languages: ["Dart"], extensionPattern: /\.dart$/ },
4139
+ { languages: ["PHP"], extensionPattern: /\.php$/ }
4140
+ ];
4141
+ function isDocumentLanguage(db, relativePath, entry) {
4293
4142
  const row = db.get(
4294
4143
  `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4295
4144
  relativePath
4296
4145
  );
4297
- return row?.language === "Dart" || relativePath.endsWith(".dart");
4146
+ return entry.languages.includes(row?.language ?? "") || entry.extensionPattern.test(relativePath);
4298
4147
  }
4299
- function isPhpDocument(db, relativePath) {
4300
- const row = db.get(
4301
- `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
4302
- relativePath
4303
- );
4304
- return row?.language === "PHP" || relativePath.endsWith(".php");
4148
+ function isPythonDocument(db, relativePath) {
4149
+ return isDocumentLanguage(db, relativePath, DOCUMENT_LANGUAGE_TABLE[0]);
4305
4150
  }
4306
4151
  function applyLimit(values, limit) {
4307
4152
  return typeof limit === "number" ? values.slice(0, limit) : values;
@@ -4403,9 +4248,6 @@ function methods(db, className) {
4403
4248
  name: leafName(definition.symbol)
4404
4249
  }));
4405
4250
  }
4406
- function isCallableSymbol(rawSymbol) {
4407
- return rawSymbol.endsWith("().") || leafSuffix(rawSymbol) === "method";
4408
- }
4409
4251
  function stripExtension(relativePath) {
4410
4252
  return relativePath.replace(/\.[^.]+$/, "");
4411
4253
  }
@@ -4743,7 +4585,7 @@ function surface(db, modulePattern) {
4743
4585
  ...matchedPaths
4744
4586
  );
4745
4587
  const exposedDefinitions = matchedPaths.flatMap(
4746
- (relativePath) => getDefinitionsForFile(db, relativePath).filter((definition) => isCallableSymbol2(definition.symbol)).map((definition) => ({
4588
+ (relativePath) => getDefinitionsForFile(db, relativePath).filter((definition) => isCallableSymbol(definition.symbol)).map((definition) => ({
4747
4589
  relative_path: relativePath,
4748
4590
  symbol: definition.symbol
4749
4591
  }))
@@ -4760,9 +4602,6 @@ function surface(db, modulePattern) {
4760
4602
  shortName: shortenSymbol(r.symbol)
4761
4603
  }));
4762
4604
  }
4763
- function isCallableSymbol2(rawSymbol) {
4764
- return rawSymbol.endsWith("().") || leafSuffix(rawSymbol) === "method";
4765
- }
4766
4605
 
4767
4606
  // src/entry-surfaces.ts
4768
4607
  var liveBarrelCache = /* @__PURE__ */ new WeakMap();
@@ -5148,22 +4987,37 @@ function outline(db, filePattern) {
5148
4987
  ORDER BY d.relative_path, der.start_line`,
5149
4988
  ...resolvedPaths
5150
4989
  );
4990
+ const nodes = rows.map((r) => ({
4991
+ symbol: r.symbol,
4992
+ shortName: shortenSymbol(r.symbol),
4993
+ startLine: r.start_line,
4994
+ endLine: r.end_line,
4995
+ children: []
4996
+ }));
5151
4997
  const nodeMap = /* @__PURE__ */ new Map();
4998
+ for (const n of nodes) nodeMap.set(n.symbol, n);
5152
4999
  const roots = [];
5153
- for (const r of rows) {
5154
- const node = {
5155
- symbol: r.symbol,
5156
- shortName: shortenSymbol(r.symbol),
5157
- startLine: r.start_line,
5158
- endLine: r.end_line,
5159
- children: []
5160
- };
5161
- nodeMap.set(r.symbol, node);
5162
- }
5163
- for (const r of rows) {
5164
- const node = nodeMap.get(r.symbol);
5000
+ for (let i = 0; i < rows.length; i++) {
5001
+ const r = rows[i];
5002
+ const node = nodes[i];
5165
5003
  if (r.enclosing_symbol && nodeMap.has(r.enclosing_symbol)) {
5166
5004
  nodeMap.get(r.enclosing_symbol).children.push(node);
5005
+ continue;
5006
+ }
5007
+ let bestParent = null;
5008
+ let bestSize = Infinity;
5009
+ for (const candidate of nodes) {
5010
+ if (candidate === node) continue;
5011
+ if (candidate.startLine <= node.startLine && candidate.endLine >= node.endLine) {
5012
+ const size = candidate.endLine - candidate.startLine;
5013
+ if (size < bestSize) {
5014
+ bestSize = size;
5015
+ bestParent = candidate;
5016
+ }
5017
+ }
5018
+ }
5019
+ if (bestParent) {
5020
+ bestParent.children.push(node);
5167
5021
  } else {
5168
5022
  roots.push(node);
5169
5023
  }
@@ -5885,7 +5739,7 @@ function similar(db, symbolPattern, opts = {}) {
5885
5739
  return similarBySourceShape(db, symbolPattern, { minSimilarity, limit });
5886
5740
  }
5887
5741
  function similarAll(db, opts = {}) {
5888
- const { minSimilarity = 0.5, limit = 20, scope, minCallees = 4 } = opts;
5742
+ const { minSimilarity = 0.5, limit = 20, scope, minCallees = 4, crossFileOnly = false } = opts;
5889
5743
  const all = getAllCalleeFingerprints(db, { minCallees, scope });
5890
5744
  const idfWeights = computeIdf(all);
5891
5745
  const results = [];
@@ -5893,14 +5747,16 @@ function similarAll(db, opts = {}) {
5893
5747
  for (let j = i + 1; j < all.length; j++) {
5894
5748
  const a = all[i];
5895
5749
  const b = all[j];
5896
- if (a.file === b.file) continue;
5750
+ if (crossFileOnly && a.file === b.file) continue;
5897
5751
  const { similarity, significantShared } = weightedSimilarity(
5898
5752
  a.callees,
5899
5753
  b.callees,
5900
5754
  idfWeights
5901
5755
  );
5902
5756
  if (similarity < minSimilarity) continue;
5903
- if (significantShared.length < 2) continue;
5757
+ const sharedCount = intersection(a.callees, b.callees).size;
5758
+ if (significantShared.length < 2 && sharedCount < 4) continue;
5759
+ const displayShared = significantShared.length > 0 ? significantShared : [...intersection(a.callees, b.callees)];
5904
5760
  results.push({
5905
5761
  symbolA: a.symbol,
5906
5762
  shortNameA: shortenSymbol(a.symbol),
@@ -5909,7 +5765,7 @@ function similarAll(db, opts = {}) {
5909
5765
  shortNameB: shortenSymbol(b.symbol),
5910
5766
  fileB: b.file,
5911
5767
  similarity,
5912
- sharedCallees: significantShared.map(shortenSymbol),
5768
+ sharedCallees: displayShared.map(shortenSymbol),
5913
5769
  uniqueToA: [...difference(a.callees, b.callees)].map(shortenSymbol),
5914
5770
  uniqueToB: [...difference(b.callees, a.callees)].map(shortenSymbol)
5915
5771
  });
@@ -6539,17 +6395,6 @@ function extractCandidates(db, opts = {}) {
6539
6395
  results.sort((a, b) => b.clusters.length - a.clusters.length || b.loc - a.loc);
6540
6396
  return results.slice(0, limit);
6541
6397
  }
6542
- function getScopedDefinitions(db, scope) {
6543
- const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
6544
- return db.all(
6545
- `SELECT relative_path
6546
- FROM documents
6547
- WHERE 1 = 1
6548
- ${db.pathExclusionsFor("documents")}
6549
- ${scopeFilter}
6550
- ORDER BY relative_path`
6551
- ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
6552
- }
6553
6398
  function definitionLoc(definition) {
6554
6399
  return definition.endLine - definition.startLine + 1;
6555
6400
  }
@@ -6993,7 +6838,7 @@ import { basename as basename5, extname as extname4 } from "path";
6993
6838
  function wrapperCandidates(db, opts) {
6994
6839
  const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
6995
6840
  const reverseFanIn = buildReverseFileFanIn(buildFileDepGraph(db, scope));
6996
- const symbols2 = getScopedDefinitions2(db, scope).filter((definition) => definitionLoc2(definition) <= maxLoc && definitionLoc2(definition) >= 2);
6841
+ const symbols2 = getScopedDefinitions(db, scope).filter((definition) => definitionLoc2(definition) <= maxLoc && definitionLoc2(definition) >= 2);
6997
6842
  const results = [];
6998
6843
  for (const symbol of symbols2) {
6999
6844
  if (db.isIgnored(symbol.relativePath) || !isFunctionLikeSymbol(symbol.symbol)) continue;
@@ -7032,17 +6877,6 @@ function wrapperCandidates(db, opts) {
7032
6877
  function definitionLoc2(definition) {
7033
6878
  return definition.endLine - definition.startLine + 1;
7034
6879
  }
7035
- function getScopedDefinitions2(db, scope) {
7036
- const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
7037
- return db.all(
7038
- `SELECT relative_path
7039
- FROM documents
7040
- WHERE 1 = 1
7041
- ${db.pathExclusionsFor("documents")}
7042
- ${scopeFilter}
7043
- ORDER BY relative_path`
7044
- ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
7045
- }
7046
6880
  function dedupeRows(rows) {
7047
6881
  const seen = /* @__PURE__ */ new Set();
7048
6882
  const unique = [];
@@ -7092,7 +6926,7 @@ function fallbackCallerFanIn(db, reverseFanIn, callerFile) {
7092
6926
  // src/queries/passthrough-candidates.ts
7093
6927
  function passthroughCandidates(db, opts) {
7094
6928
  const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
7095
- const symbols2 = getScopedDefinitions3(db, scope).filter((definition) => definitionLoc3(definition) >= 3 && definitionLoc3(definition) <= maxLoc);
6929
+ const symbols2 = getScopedDefinitions(db, scope).filter((definition) => definitionLoc3(definition) >= 3 && definitionLoc3(definition) <= maxLoc);
7096
6930
  const results = [];
7097
6931
  for (const sym of symbols2) {
7098
6932
  if (db.isIgnored(sym.relativePath) || !isFunctionLikeSymbol(sym.symbol)) continue;
@@ -7121,17 +6955,6 @@ function passthroughCandidates(db, opts) {
7121
6955
  results.sort((a, b) => a.loc - b.loc || a.file.localeCompare(b.file));
7122
6956
  return results.slice(0, limit);
7123
6957
  }
7124
- function getScopedDefinitions3(db, scope) {
7125
- const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
7126
- return db.all(
7127
- `SELECT relative_path
7128
- FROM documents
7129
- WHERE 1 = 1
7130
- ${db.pathExclusionsFor("documents")}
7131
- ${scopeFilter}
7132
- ORDER BY relative_path`
7133
- ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
7134
- }
7135
6958
  function definitionLoc3(definition) {
7136
6959
  return definition.endLine - definition.startLine + 1;
7137
6960
  }
@@ -7139,7 +6962,7 @@ function definitionLoc3(definition) {
7139
6962
  // src/queries/stale-abstractions.ts
7140
6963
  function staleAbstractions(db, opts) {
7141
6964
  const { scope, minLoc = 3, limit = 30 } = opts ?? {};
7142
- const rows = getScopedDefinitions4(db, scope).filter((definition) => definition.isTypeLike && definitionLoc4(definition) >= minLoc).map((definition) => ({
6965
+ const rows = getScopedDefinitions(db, scope).filter((definition) => definition.isTypeLike && definitionLoc4(definition) >= minLoc).map((definition) => ({
7143
6966
  symbol: definition.symbol,
7144
6967
  file: definition.relativePath,
7145
6968
  start_line: definition.startLine,
@@ -7159,7 +6982,7 @@ function staleAbstractions(db, opts) {
7159
6982
  })).slice(0, limit);
7160
6983
  }
7161
6984
  function getFilesWithFunctions(db, scope) {
7162
- return new Set(getScopedDefinitions4(db, scope).filter((definition) => definition.isFunctionLike).map((definition) => definition.relativePath));
6985
+ return new Set(getScopedDefinitions(db, scope).filter((definition) => definition.isFunctionLike).map((definition) => definition.relativePath));
7163
6986
  }
7164
6987
  function isTrueStaleAbstraction(row, filesWithFunctions) {
7165
6988
  const basename6 = row.file.split("/").pop() ?? "";
@@ -7172,17 +6995,6 @@ function isTrueStaleAbstraction(row, filesWithFunctions) {
7172
6995
  }
7173
6996
  return true;
7174
6997
  }
7175
- function getScopedDefinitions4(db, scope) {
7176
- const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
7177
- return db.all(
7178
- `SELECT relative_path
7179
- FROM documents
7180
- WHERE 1 = 1
7181
- ${db.pathExclusionsFor("documents")}
7182
- ${scopeFilter}
7183
- ORDER BY relative_path`
7184
- ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
7185
- }
7186
6998
  function countCrossFileConsumers(db, definition) {
7187
6999
  const callers = db.all(
7188
7000
  `SELECT DISTINCT d.relative_path
@@ -7590,9 +7402,11 @@ function code(db, symbolPattern, opts = {}) {
7590
7402
  symbol: match.symbol,
7591
7403
  shortName: shortenSymbol(match.symbol),
7592
7404
  relativePath: match.relativePath,
7593
- startLine: startLine + 1,
7594
- // 1-indexed for display
7595
- endLine: endLine + 1,
7405
+ // 0-indexed, like every other query result. The CLI's displayLine()
7406
+ // converts once at render time. Returning 1-indexed here caused a
7407
+ // double-conversion in the CLI and printed labels off by +1.
7408
+ startLine,
7409
+ endLine,
7596
7410
  language: doc?.language ?? null,
7597
7411
  source
7598
7412
  };
@@ -7620,8 +7434,8 @@ function readFileRange(db, filePath, startLine, endLine, context) {
7620
7434
  symbol: `${doc.relative_path}:${startLine}-${endLine}`,
7621
7435
  shortName: `${doc.relative_path}:${startLine}-${endLine}`,
7622
7436
  relativePath: doc.relative_path,
7623
- startLine: start + 1,
7624
- endLine: end + 1,
7437
+ startLine: start,
7438
+ endLine: end,
7625
7439
  language: doc.language,
7626
7440
  source
7627
7441
  };
@@ -7801,28 +7615,40 @@ function uniqueSymbolRows(rows) {
7801
7615
 
7802
7616
  // src/queries/slice.ts
7803
7617
  function slice(db, symbolPattern, opts = {}) {
7804
- const { direction = "backward" } = opts;
7618
+ const { direction = "backward", maxDepth = 3 } = opts;
7805
7619
  const match = findFirstSymbolMatch(db, symbolPattern);
7806
7620
  if (!match) return null;
7807
7621
  if (direction === "backward") {
7808
- return backwardSlice(db, match);
7622
+ return backwardSlice(db, match, maxDepth);
7809
7623
  } else {
7810
7624
  return forwardSlice(db, match);
7811
7625
  }
7812
7626
  }
7813
- function backwardSlice(db, match) {
7814
- const callees = getCalleeRowsForSymbol(db, match);
7815
- const seen = /* @__PURE__ */ new Set();
7627
+ function backwardSlice(db, match, maxDepth) {
7816
7628
  const connected = [];
7817
- for (const c of callees) {
7818
- if (seen.has(c.symbol)) continue;
7819
- seen.add(c.symbol);
7820
- connected.push({
7821
- symbol: c.symbol,
7822
- shortName: shortenSymbol(c.symbol),
7823
- file: c.file,
7824
- relationship: "referenced within definition (callee)"
7825
- });
7629
+ const visited = /* @__PURE__ */ new Set([match.symbol]);
7630
+ let frontier = [match];
7631
+ for (let depth = 1; depth <= maxDepth; depth++) {
7632
+ if (frontier.length === 0) break;
7633
+ const nextFrontier = [];
7634
+ for (const current of frontier) {
7635
+ const callees = getCalleeRowsForSymbol(db, current);
7636
+ for (const c of callees) {
7637
+ if (visited.has(c.symbol)) continue;
7638
+ visited.add(c.symbol);
7639
+ connected.push({
7640
+ symbol: c.symbol,
7641
+ shortName: shortenSymbol(c.symbol),
7642
+ file: c.file,
7643
+ relationship: depth === 1 ? "referenced within definition (callee)" : `depth ${depth} callee`
7644
+ });
7645
+ const calleeMatch = findExactSymbolMatch(db, c.symbol);
7646
+ if (calleeMatch && !db.isIgnored(calleeMatch.relativePath)) {
7647
+ nextFrontier.push(calleeMatch);
7648
+ }
7649
+ }
7650
+ }
7651
+ frontier = nextFrontier;
7826
7652
  }
7827
7653
  return {
7828
7654
  symbol: match.symbol,
@@ -8833,7 +8659,7 @@ program.command("call-graph <symbol>").description("Show incoming callers and ou
8833
8659
  }
8834
8660
  );
8835
8661
  });
8836
- program.command("similar [symbol]").description("Find functions with similar callee fingerprints (consolidation candidates)").option("--min-similarity <n>", "Minimum Jaccard similarity (0-1)", parseFloat, 0.4).option("-n, --limit <n>", "Number of results", parseIntSafe, 20).option("-s, --scope <path>", "Limit to files matching path").option("--min-callees <n>", "Minimum callees to consider", parseIntSafe, 4).action((symbol, opts) => {
8662
+ program.command("similar [symbol]").description("Find functions with similar callee fingerprints (consolidation candidates)").option("--min-similarity <n>", "Minimum Jaccard similarity (0-1)", parseFloat, 0.4).option("-n, --limit <n>", "Number of results", parseIntSafe, 20).option("-s, --scope <path>", "Limit to files matching path").option("--min-callees <n>", "Minimum callees to consider", parseIntSafe, 4).option("--cross-file-only", "Only show cross-file pairs (skip same-file matches)").action((symbol, opts) => {
8837
8663
  withDb((db) => {
8838
8664
  if (symbol) {
8839
8665
  const results = queries.similar(db, symbol, {
@@ -8858,7 +8684,8 @@ ${Math.round(r.similarity * 100)}% similar:`);
8858
8684
  minSimilarity: opts.minSimilarity,
8859
8685
  limit: opts.limit,
8860
8686
  scope: opts.scope,
8861
- minCallees: opts.minCallees
8687
+ minCallees: opts.minCallees,
8688
+ crossFileOnly: opts.crossFileOnly
8862
8689
  });
8863
8690
  if (results.length === 0) {
8864
8691
  console.log("No similar symbol pairs found.");
@@ -9253,10 +9080,10 @@ program.command("dataflow <symbol>").description("Reference-level dataflow: defi
9253
9080
  }
9254
9081
  db.close();
9255
9082
  });
9256
- program.command("slice <symbol>").description("Reference-level program slice: what affects this (backward) or what this affects (forward)").option("--forward", "Forward slice (what does this affect). Default is backward.").action((symbol, opts) => {
9083
+ program.command("slice <symbol>").description("Reference-level program slice: what affects this (backward) or what this affects (forward)").option("--forward", "Forward slice (what does this affect). Default is backward.").option("--depth <n>", "Max transitive depth for backward slice", parseIntSafe, 3).action((symbol, opts) => {
9257
9084
  const db = openDb();
9258
9085
  const direction = opts.forward ? "forward" : "backward";
9259
- const result = queries.slice(db, symbol, { direction });
9086
+ const result = queries.slice(db, symbol, { direction, maxDepth: opts.depth });
9260
9087
  if (!result) {
9261
9088
  console.log("Symbol not found.");
9262
9089
  db.close();