scip-query 0.4.0 → 0.4.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.
Files changed (92) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-HNURMDF4.js → chunk-24LF6IZB.js} +3 -3
  3. package/dist/{chunk-IV6NZ426.js → chunk-3NJSJ7TE.js} +3 -3
  4. package/dist/{chunk-H3FPW5YN.js → chunk-43A4UCS7.js} +3 -3
  5. package/dist/{chunk-A5BGEBM7.js → chunk-5GCORUNV.js} +3 -3
  6. package/dist/{chunk-I2JM34UV.js → chunk-6CH23IAS.js} +4 -4
  7. package/dist/{chunk-5AJJGPZE.js → chunk-6ECR2FLR.js} +3 -3
  8. package/dist/{chunk-2UISVZGQ.js → chunk-6UZU7DFL.js} +3 -3
  9. package/dist/{chunk-NWCJWA36.js → chunk-7BS4CPJX.js} +5 -5
  10. package/dist/{chunk-QCYR4S6T.js → chunk-A6XLXV6W.js} +3 -3
  11. package/dist/{chunk-QGCEAVJD.js → chunk-ALUFWH3U.js} +232 -66
  12. package/dist/{chunk-CQRYLK33.js → chunk-CBIWNZZZ.js} +3 -3
  13. package/dist/{chunk-N2LH3M2P.js → chunk-DUJNJQPO.js} +3 -3
  14. package/dist/{chunk-XH56HXLC.js → chunk-EAGKJFDX.js} +3 -3
  15. package/dist/{chunk-SL674KAW.js → chunk-ELFGD5EW.js} +3 -3
  16. package/dist/{chunk-GIBETK3W.js → chunk-FVJE4MQL.js} +3 -3
  17. package/dist/{chunk-CQUNEJYM.js → chunk-GNAMV3JC.js} +3 -3
  18. package/dist/{chunk-VCOJRQPP.js → chunk-J47VSL6I.js} +3 -3
  19. package/dist/{chunk-7YBLWIXY.js → chunk-J6QXMYAQ.js} +3 -3
  20. package/dist/{chunk-2F2WH5WQ.js → chunk-JHVQB4Y5.js} +12 -12
  21. package/dist/{chunk-X3J4VPWM.js → chunk-JKXHHV4B.js} +2 -2
  22. package/dist/{chunk-4ZT7UGWW.js → chunk-KG4OFQEN.js} +3 -3
  23. package/dist/{chunk-SRELHCMG.js → chunk-KLNKDX6A.js} +4 -4
  24. package/dist/{chunk-LOVDB4C6.js → chunk-KYPXKV64.js} +3 -3
  25. package/dist/{chunk-UTRKBUCB.js → chunk-NXUIWD6K.js} +3 -3
  26. package/dist/{chunk-VU7FDTWV.js → chunk-OXX3QF24.js} +2 -2
  27. package/dist/{chunk-7HK5ZLOE.js → chunk-PCU455MX.js} +2 -2
  28. package/dist/{chunk-A4GWYETB.js → chunk-POLELLNM.js} +3 -3
  29. package/dist/{chunk-AS7N27JK.js → chunk-PU2254N2.js} +3 -3
  30. package/dist/{chunk-WNPF2I25.js → chunk-QMXSLHZP.js} +2 -2
  31. package/dist/{chunk-PGHN5UTM.js → chunk-R7HPHMRZ.js} +3 -3
  32. package/dist/{chunk-VUBLUTMU.js → chunk-RE7POFGI.js} +2 -2
  33. package/dist/{chunk-KDCQJTYW.js → chunk-RJ5GULL6.js} +2 -2
  34. package/dist/{chunk-HRDPUTIQ.js → chunk-RL74LF47.js} +2 -2
  35. package/dist/{chunk-5RKYZSQ6.js → chunk-SVLUJSY7.js} +3 -3
  36. package/dist/{chunk-D4I3ZMN5.js → chunk-SYQR4QGK.js} +3 -3
  37. package/dist/{chunk-QIXNAB5K.js → chunk-TO3L4YNK.js} +1 -2
  38. package/dist/{chunk-P42KQKJZ.js → chunk-TWVXFKJA.js} +4 -4
  39. package/dist/{chunk-MA3B3IUT.js → chunk-UJWI5CBB.js} +3 -3
  40. package/dist/{chunk-E7J7Q7UW.js → chunk-VKBOLNYN.js} +3 -3
  41. package/dist/{chunk-RIEA5DOB.js → chunk-VY2L4TP6.js} +3 -3
  42. package/dist/{chunk-A7YY7IDA.js → chunk-W46L2BXT.js} +2 -2
  43. package/dist/{chunk-VISMEWYP.js → chunk-XUVPQDXW.js} +4 -4
  44. package/dist/{chunk-ZU2AQQB5.js → chunk-Z5VSUOEE.js} +2 -2
  45. package/dist/{chunk-EOHPASDV.js → chunk-ZVZAIIB5.js} +3 -3
  46. package/dist/cli.js +516 -179
  47. package/dist/index.d.ts +18 -1
  48. package/dist/index.js +239 -70
  49. package/dist/queries/affected.js +3 -3
  50. package/dist/queries/bottlenecks.js +3 -3
  51. package/dist/queries/by-kind.js +3 -3
  52. package/dist/queries/call-graph.js +3 -3
  53. package/dist/queries/change-surface.js +3 -3
  54. package/dist/queries/code.js +3 -3
  55. package/dist/queries/complexity-hotspots.js +3 -3
  56. package/dist/queries/complexity.js +3 -3
  57. package/dist/queries/convergence.js +3 -3
  58. package/dist/queries/coupling.js +3 -3
  59. package/dist/queries/cycles.js +3 -3
  60. package/dist/queries/dataflow.js +3 -3
  61. package/dist/queries/dead.js +4 -4
  62. package/dist/queries/deep-chains.js +3 -3
  63. package/dist/queries/deps.js +3 -3
  64. package/dist/queries/diff-impact.js +2 -2
  65. package/dist/queries/drift.js +3 -3
  66. package/dist/queries/extract-candidates.js +3 -3
  67. package/dist/queries/fan.js +3 -3
  68. package/dist/queries/health.js +14 -14
  69. package/dist/queries/hierarchy.js +3 -3
  70. package/dist/queries/hotspots.js +3 -3
  71. package/dist/queries/imports.js +3 -3
  72. package/dist/queries/index.js +44 -44
  73. package/dist/queries/isolated.js +4 -4
  74. package/dist/queries/members.js +3 -3
  75. package/dist/queries/methods.js +3 -3
  76. package/dist/queries/outline.js +3 -3
  77. package/dist/queries/passthrough-candidates.js +3 -3
  78. package/dist/queries/redundant-reexports.js +4 -4
  79. package/dist/queries/refs.js +3 -3
  80. package/dist/queries/similar-chains.js +3 -3
  81. package/dist/queries/similar-files.js +3 -3
  82. package/dist/queries/similar-signatures.js +3 -3
  83. package/dist/queries/similar.js +3 -3
  84. package/dist/queries/slice.js +3 -3
  85. package/dist/queries/stale-abstractions.js +3 -3
  86. package/dist/queries/surface.js +3 -3
  87. package/dist/queries/symbols.js +3 -3
  88. package/dist/queries/system.js +3 -3
  89. package/dist/queries/trace.js +3 -3
  90. package/dist/queries/wrapper-candidates.js +3 -3
  91. package/dist/reindex-worker.js +149 -7
  92. package/package.json +7 -1
package/dist/cli.js CHANGED
@@ -115,7 +115,7 @@ var ScipDatabase = class {
115
115
  // src/gitignore-filter.ts
116
116
  import ignore from "ignore";
117
117
  import { readFileSync, existsSync } from "fs";
118
- import { join, dirname } from "path";
118
+ import { dirname, isAbsolute, join, relative, resolve } from "path";
119
119
  function createGitignoreFilter(projectRoot) {
120
120
  const ig = ignore();
121
121
  let loaded = false;
@@ -132,8 +132,8 @@ function createGitignoreFilter(projectRoot) {
132
132
  ig.add(DEFAULT_IGNORES);
133
133
  }
134
134
  return {
135
- isIgnored: (relativePath) => ig.ignores(relativePath),
136
- filter: (paths) => paths.filter((p) => !ig.ignores(p))
135
+ isIgnored: (relativePath) => safeIgnores(ig, projectRoot, relativePath),
136
+ filter: (paths) => paths.filter((p) => !safeIgnores(ig, projectRoot, p))
137
137
  };
138
138
  }
139
139
  function findGitignoreFiles(projectRoot) {
@@ -213,10 +213,35 @@ Thumbs.db
213
213
  # Type definitions (often noise in queries)
214
214
  *.d.ts
215
215
  `;
216
+ function safeIgnores(ig, projectRoot, inputPath) {
217
+ const relativePath = normalizeForIgnore(projectRoot, inputPath);
218
+ if (!relativePath) {
219
+ return false;
220
+ }
221
+ try {
222
+ return ig.ignores(relativePath);
223
+ } catch {
224
+ return false;
225
+ }
226
+ }
227
+ function normalizeForIgnore(projectRoot, inputPath) {
228
+ if (!inputPath || inputPath === ".") {
229
+ return null;
230
+ }
231
+ if (!isAbsolute(inputPath) && !inputPath.startsWith("..")) {
232
+ return inputPath.replaceAll("\\", "/");
233
+ }
234
+ const absolutePath = isAbsolute(inputPath) ? inputPath : resolve(projectRoot, inputPath);
235
+ const relativePath = relative(projectRoot, absolutePath).replaceAll("\\", "/");
236
+ if (!relativePath || relativePath === "." || relativePath.startsWith("..")) {
237
+ return null;
238
+ }
239
+ return relativePath;
240
+ }
216
241
 
217
242
  // src/config.ts
218
243
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
219
- import { join as join2, resolve } from "path";
244
+ import { join as join2, resolve as resolve2 } from "path";
220
245
  import { createHash } from "crypto";
221
246
  import { homedir } from "os";
222
247
  var CONFIG_FILENAME = ".scipquery.json";
@@ -247,10 +272,10 @@ function resolveWatchConfig(config) {
247
272
  function resolveCacheDir(projectRoot, config) {
248
273
  const envOverride = process.env["SCIP_QUERY_CACHE_DIR"];
249
274
  if (envOverride) return ensureDir(envOverride);
250
- if (config?.dbPath) return ensureDir(resolve(projectRoot, config.dbPath));
275
+ if (config?.dbPath) return ensureDir(resolve2(projectRoot, config.dbPath));
251
276
  const xdgCache = process.env["XDG_CACHE_HOME"];
252
277
  const cacheBase = xdgCache || join2(homedir(), ".cache");
253
- const projectHash = createHash("sha256").update(resolve(projectRoot)).digest("hex").slice(0, 12);
278
+ const projectHash = createHash("sha256").update(resolve2(projectRoot)).digest("hex").slice(0, 12);
254
279
  const dir = join2(cacheBase, "scip-query", "projects", projectHash);
255
280
  return ensureDir(dir);
256
281
  }
@@ -286,8 +311,8 @@ function ensureDir(dir) {
286
311
 
287
312
  // src/reindex/index.ts
288
313
  import { execFileSync as execFileSync3 } from "child_process";
289
- import { existsSync as existsSync6, renameSync } from "fs";
290
- import { join as join6 } from "path";
314
+ import { existsSync as existsSync6, renameSync, rmSync } from "fs";
315
+ import { basename, dirname as dirname2, extname as extname2, join as join6 } from "path";
291
316
 
292
317
  // src/scip-cli.ts
293
318
  import { execFileSync as execFileSync2 } from "child_process";
@@ -968,6 +993,128 @@ function resolveDotnetProject(projectRoot, suffixes) {
968
993
  return null;
969
994
  }
970
995
 
996
+ // src/reindex/merge.ts
997
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
998
+ import { create } from "@bufbuild/protobuf";
999
+ import {
1000
+ deserializeSCIP,
1001
+ serializeSCIP,
1002
+ DocumentSchema,
1003
+ IndexSchema,
1004
+ SymbolInformationSchema
1005
+ } from "@c4312/scip";
1006
+ function mergeScipIndexes(indexes) {
1007
+ if (indexes.length === 0) {
1008
+ throw new Error("Cannot merge zero SCIP indexes");
1009
+ }
1010
+ if (indexes.length === 1) {
1011
+ return indexes[0];
1012
+ }
1013
+ const metadata = mergeMetadata(indexes);
1014
+ const documents = mergeDocuments(indexes.flatMap((index) => index.documents ?? []));
1015
+ const externalSymbols = mergeSymbolInfos(indexes.flatMap((index) => index.externalSymbols ?? []));
1016
+ return create(IndexSchema, {
1017
+ metadata,
1018
+ documents,
1019
+ externalSymbols
1020
+ });
1021
+ }
1022
+ function mergeScipFiles(inputPaths, outputPath) {
1023
+ if (inputPaths.length === 0) {
1024
+ throw new Error("Cannot merge zero SCIP files");
1025
+ }
1026
+ const indexes = inputPaths.map((path2) => deserializeSCIP(readFileSync3(path2)));
1027
+ const merged = mergeScipIndexes(indexes);
1028
+ writeFileSync2(outputPath, Buffer.from(serializeSCIP(merged)));
1029
+ return {
1030
+ documentCount: merged.documents.length,
1031
+ externalSymbolCount: merged.externalSymbols.length,
1032
+ inputCount: inputPaths.length
1033
+ };
1034
+ }
1035
+ function mergeMetadata(indexes) {
1036
+ const first = indexes[0]?.metadata;
1037
+ if (!first) {
1038
+ return void 0;
1039
+ }
1040
+ const expectedProjectRoot = first.projectRoot;
1041
+ for (const index of indexes.slice(1)) {
1042
+ const actualProjectRoot = index.metadata?.projectRoot;
1043
+ if (expectedProjectRoot && actualProjectRoot && actualProjectRoot !== expectedProjectRoot) {
1044
+ throw new Error(
1045
+ `Cannot merge SCIP indexes with different project roots: ${expectedProjectRoot} vs ${actualProjectRoot}`
1046
+ );
1047
+ }
1048
+ }
1049
+ return first;
1050
+ }
1051
+ function mergeDocuments(documents) {
1052
+ const byPath = /* @__PURE__ */ new Map();
1053
+ for (const document of documents) {
1054
+ const existing = byPath.get(document.relativePath);
1055
+ if (!existing) {
1056
+ byPath.set(document.relativePath, document);
1057
+ continue;
1058
+ }
1059
+ byPath.set(document.relativePath, create(DocumentSchema, {
1060
+ language: existing.language || document.language,
1061
+ relativePath: existing.relativePath || document.relativePath,
1062
+ occurrences: [...existing.occurrences, ...document.occurrences],
1063
+ symbols: mergeSymbolInfos([...existing.symbols, ...document.symbols]),
1064
+ text: chooseText(existing.text, document.text),
1065
+ positionEncoding: existing.positionEncoding || document.positionEncoding
1066
+ }));
1067
+ }
1068
+ return [...byPath.values()];
1069
+ }
1070
+ function mergeSymbolInfos(symbols2) {
1071
+ const bySymbol = /* @__PURE__ */ new Map();
1072
+ for (const symbol of symbols2) {
1073
+ const existing = bySymbol.get(symbol.symbol);
1074
+ if (!existing) {
1075
+ bySymbol.set(symbol.symbol, symbol);
1076
+ continue;
1077
+ }
1078
+ bySymbol.set(symbol.symbol, create(SymbolInformationSchema, {
1079
+ symbol: existing.symbol,
1080
+ documentation: uniqueStrings([...existing.documentation, ...symbol.documentation]),
1081
+ relationships: mergeRelationships([...existing.relationships, ...symbol.relationships]),
1082
+ kind: existing.kind || symbol.kind,
1083
+ displayName: existing.displayName || symbol.displayName,
1084
+ enclosingSymbol: existing.enclosingSymbol || symbol.enclosingSymbol,
1085
+ signatureDocumentation: existing.signatureDocumentation ?? symbol.signatureDocumentation
1086
+ }));
1087
+ }
1088
+ return [...bySymbol.values()];
1089
+ }
1090
+ function mergeRelationships(relationships) {
1091
+ const seen = /* @__PURE__ */ new Set();
1092
+ const merged = [];
1093
+ for (const relationship of relationships) {
1094
+ const key = [
1095
+ relationship.symbol,
1096
+ relationship.isReference ? "1" : "0",
1097
+ relationship.isImplementation ? "1" : "0",
1098
+ relationship.isTypeDefinition ? "1" : "0",
1099
+ relationship.isDefinition ? "1" : "0"
1100
+ ].join("|");
1101
+ if (seen.has(key)) {
1102
+ continue;
1103
+ }
1104
+ seen.add(key);
1105
+ merged.push(relationship);
1106
+ }
1107
+ return merged;
1108
+ }
1109
+ function chooseText(left, right) {
1110
+ if (!left) return right;
1111
+ if (!right) return left;
1112
+ return left.length >= right.length ? left : right;
1113
+ }
1114
+ function uniqueStrings(values) {
1115
+ return [...new Set(values)];
1116
+ }
1117
+
971
1118
  // src/reindex/index.ts
972
1119
  async function reindex(opts) {
973
1120
  const {
@@ -1003,7 +1150,11 @@ async function reindex(opts) {
1003
1150
  ...process.env,
1004
1151
  NODE_OPTIONS: `--max-old-space-size=${maxHeapMb}`
1005
1152
  };
1006
- for (const lang of languages) {
1153
+ const languageOutputs = languages.map((language, index) => ({
1154
+ language,
1155
+ scipPath: languages.length > 1 ? tempScipPath(outputScip, language, index) : outputScip
1156
+ }));
1157
+ for (const { language: lang, scipPath } of languageOutputs) {
1007
1158
  const config = getIndexerConfig(lang);
1008
1159
  const binaryLabel = describeIndexerBinary(config);
1009
1160
  const projectLocalBinary = resolveProjectLocalIndexerBinary(config, projectRoot);
@@ -1033,7 +1184,7 @@ async function reindex(opts) {
1033
1184
  const indexerEnv = getIndexerExecutionEnv(config, env, resolvedBinary);
1034
1185
  const { binary, args } = config.indexArgs({
1035
1186
  projectRoot,
1036
- outputPath: outputScip,
1187
+ outputPath: scipPath,
1037
1188
  pnpmWorkspaces: opts.pnpmWorkspaces,
1038
1189
  indexerBinary: resolvedBinary
1039
1190
  });
@@ -1048,10 +1199,15 @@ async function reindex(opts) {
1048
1199
  const msg = err instanceof Error ? err.message : String(err);
1049
1200
  throw new Error(
1050
1201
  `Failed to index ${lang} with ${resolvedBinary}: ${msg}
1051
- Make sure ${binaryLabel} is installed and available on PATH.`
1202
+ Make sure ${binaryLabel} is installed and available on PATH.`,
1203
+ { cause: err }
1052
1204
  );
1053
1205
  }
1054
- moveDefaultOutputIfNeeded(config, projectRoot, outputScip);
1206
+ moveDefaultOutputIfNeeded(config, projectRoot, scipPath);
1207
+ }
1208
+ if (languageOutputs.length > 1) {
1209
+ onStatus(`Merging ${languageOutputs.length} language indexes...`);
1210
+ mergeScipFiles(languageOutputs.map((entry) => entry.scipPath), outputScip);
1055
1211
  }
1056
1212
  onStatus("Converting to SQLite...");
1057
1213
  if (!existsSync6(outputScip)) {
@@ -1065,7 +1221,13 @@ Make sure ${binaryLabel} is installed and available on PATH.`
1065
1221
  });
1066
1222
  } catch (err) {
1067
1223
  const msg = err instanceof Error ? err.message : String(err);
1068
- throw new Error(`Failed to convert SCIP index to SQLite: ${msg}`);
1224
+ throw new Error(`Failed to convert SCIP index to SQLite: ${msg}`, { cause: err });
1225
+ } finally {
1226
+ for (const { scipPath } of languageOutputs) {
1227
+ if (scipPath !== outputScip) {
1228
+ rmSync(scipPath, { force: true });
1229
+ }
1230
+ }
1069
1231
  }
1070
1232
  const durationMs = Date.now() - start;
1071
1233
  onStatus(`Done in ${(durationMs / 1e3).toFixed(1)}s`);
@@ -1080,11 +1242,16 @@ function moveDefaultOutputIfNeeded(config, projectRoot, outputScip) {
1080
1242
  renameSync(defaultOutputPath, outputScip);
1081
1243
  }
1082
1244
  }
1245
+ function tempScipPath(outputScip, language, index) {
1246
+ const extension = extname2(outputScip) || ".scip";
1247
+ const stem = basename(outputScip, extension);
1248
+ return join6(dirname2(outputScip), `${stem}.${index + 1}.${language}${extension}`);
1249
+ }
1083
1250
 
1084
1251
  // src/watch.ts
1085
1252
  import { watch } from "fs";
1086
1253
  import { existsSync as existsSync7, renameSync as renameSync2 } from "fs";
1087
- import { join as join7, relative } from "path";
1254
+ import { join as join7, relative as relative2 } from "path";
1088
1255
  import { fork } from "child_process";
1089
1256
  import ignore2 from "ignore";
1090
1257
  var Watcher = class {
@@ -1162,7 +1329,7 @@ var Watcher = class {
1162
1329
  }
1163
1330
  // ── Internal ─────────────────────────────────────────────
1164
1331
  handleFileChange(filename) {
1165
- const rel = relative(this.projectRoot, join7(this.projectRoot, filename));
1332
+ const rel = relative2(this.projectRoot, join7(this.projectRoot, filename));
1166
1333
  if (this.gitignoreFilter.isIgnored(rel)) return;
1167
1334
  if (this.extraIgnore.ignores(rel)) return;
1168
1335
  if (filename.endsWith("index.db") || filename.endsWith("index.scip") || filename.endsWith("index.db.tmp") || filename.endsWith(".scipquery.json")) {
@@ -1243,10 +1410,10 @@ var Watcher = class {
1243
1410
  * Writes to index.db.tmp, then atomically renames to index.db.
1244
1411
  */
1245
1412
  runReindex() {
1246
- return new Promise((resolve4, reject) => {
1413
+ return new Promise((resolve5, reject) => {
1247
1414
  const start = Date.now();
1248
1415
  const tmpDb = this.indexPaths.dbPath + ".tmp";
1249
- const tmpScip = tempScipPath(this.indexPaths.indexPath);
1416
+ const tmpScip = tempScipPath2(this.indexPaths.indexPath);
1250
1417
  const child = fork(
1251
1418
  new URL("./reindex-worker.js", import.meta.url).pathname,
1252
1419
  [],
@@ -1271,7 +1438,7 @@ var Watcher = class {
1271
1438
  if (existsSync7(tmpScip)) {
1272
1439
  renameSync2(tmpScip, this.indexPaths.indexPath);
1273
1440
  }
1274
- resolve4(Date.now() - start);
1441
+ resolve5(Date.now() - start);
1275
1442
  } catch (err) {
1276
1443
  reject(new Error(`Atomic swap failed: ${err}`));
1277
1444
  }
@@ -1287,7 +1454,7 @@ var Watcher = class {
1287
1454
  this.onStatus(status);
1288
1455
  }
1289
1456
  };
1290
- function tempScipPath(indexPath) {
1457
+ function tempScipPath2(indexPath) {
1291
1458
  return indexPath.endsWith(".scip") ? indexPath.slice(0, -".scip".length) + ".tmp.scip" : indexPath + ".tmp.scip";
1292
1459
  }
1293
1460
 
@@ -1323,7 +1490,7 @@ function files(db, pattern) {
1323
1490
  }
1324
1491
 
1325
1492
  // src/query-support.ts
1326
- import { basename as basename2 } from "path";
1493
+ import { basename as basename3 } from "path";
1327
1494
 
1328
1495
  // src/source-analysis.ts
1329
1496
  import {
@@ -1331,12 +1498,12 @@ import {
1331
1498
  readFileSync as readFileSync4
1332
1499
  } from "fs";
1333
1500
  import {
1334
- basename,
1335
- dirname as dirname2,
1336
- extname as extname2,
1501
+ basename as basename2,
1502
+ dirname as dirname3,
1503
+ extname as extname3,
1337
1504
  join as join8,
1338
- relative as relative2,
1339
- resolve as resolve2
1505
+ relative as relative3,
1506
+ resolve as resolve3
1340
1507
  } from "path";
1341
1508
  var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
1342
1509
  var SOURCE_EXPORT_CACHE = /* @__PURE__ */ new WeakMap();
@@ -1679,7 +1846,7 @@ function parseRubyImports(db, importerPath, source) {
1679
1846
  return statements;
1680
1847
  }
1681
1848
  function rubyConstantName(specifier) {
1682
- return basename(specifier).replace(/\.[^.]+$/, "").split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1849
+ return basename2(specifier).replace(/\.[^.]+$/, "").split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1683
1850
  }
1684
1851
  function parseCLikeImports(db, importerPath, source) {
1685
1852
  const statements = [];
@@ -1688,7 +1855,7 @@ function parseCLikeImports(db, importerPath, source) {
1688
1855
  const full = match[0];
1689
1856
  if (!specifier || !full || typeof match.index !== "number") continue;
1690
1857
  const body = buildUsageBody(source, match.index, match.index + full.length);
1691
- const localName = basename(specifier).replace(/\.[^.]+$/, "");
1858
+ const localName = basename2(specifier).replace(/\.[^.]+$/, "");
1692
1859
  statements.push({
1693
1860
  importedName: specifier,
1694
1861
  localName,
@@ -2163,16 +2330,16 @@ function resolveJavaScriptImportPath(db, importerPath, specifier) {
2163
2330
  if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
2164
2331
  return null;
2165
2332
  }
2166
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2167
- const absolute = resolve2(importerDir, specifier);
2333
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2334
+ const absolute = resolve3(importerDir, specifier);
2168
2335
  const indexedPaths = getIndexedPaths(db);
2169
2336
  for (const candidate of candidateImportPaths(absolute)) {
2170
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2337
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2171
2338
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2172
2339
  return relativeCandidate;
2173
2340
  }
2174
2341
  }
2175
- return normalizePath(relative2(db.config.projectRoot, absolute));
2342
+ return normalizePath(relative3(db.config.projectRoot, absolute));
2176
2343
  }
2177
2344
  function resolvePythonImportPath(db, importerPath, specifier) {
2178
2345
  const indexedPaths = getIndexedPaths(db);
@@ -2182,16 +2349,16 @@ function resolvePythonImportPath(db, importerPath, specifier) {
2182
2349
  if (!match) return null;
2183
2350
  const dots = match[1].length;
2184
2351
  const remainder = match[2].replace(/^\./, "");
2185
- let baseDir = dirname2(join8(db.config.projectRoot, importerPath));
2352
+ let baseDir = dirname3(join8(db.config.projectRoot, importerPath));
2186
2353
  for (let i = 1; i < dots; i++) {
2187
- baseDir = dirname2(baseDir);
2354
+ baseDir = dirname3(baseDir);
2188
2355
  }
2189
- basePath = remainder ? resolve2(baseDir, remainder.replace(/\./g, "/")) : baseDir;
2356
+ basePath = remainder ? resolve3(baseDir, remainder.replace(/\./g, "/")) : baseDir;
2190
2357
  } else {
2191
- basePath = resolve2(db.config.projectRoot, specifier.replace(/\./g, "/"));
2358
+ basePath = resolve3(db.config.projectRoot, specifier.replace(/\./g, "/"));
2192
2359
  }
2193
2360
  for (const candidate of pythonCandidateImportPaths(basePath)) {
2194
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2361
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2195
2362
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2196
2363
  return relativeCandidate;
2197
2364
  }
@@ -2204,17 +2371,17 @@ function resolveRustImportPath(db, importerPath, specifier) {
2204
2371
  if (!normalizedSpecifier.startsWith("crate::") && !normalizedSpecifier.startsWith("self::") && !normalizedSpecifier.startsWith("super::")) {
2205
2372
  return null;
2206
2373
  }
2207
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2374
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2208
2375
  let basePath;
2209
2376
  if (normalizedSpecifier.startsWith("crate::")) {
2210
- basePath = resolve2(db.config.projectRoot, "src", normalizedSpecifier.slice("crate::".length).replace(/::/g, "/"));
2377
+ basePath = resolve3(db.config.projectRoot, "src", normalizedSpecifier.slice("crate::".length).replace(/::/g, "/"));
2211
2378
  } else if (normalizedSpecifier.startsWith("self::")) {
2212
- basePath = resolve2(importerDir, normalizedSpecifier.slice("self::".length).replace(/::/g, "/"));
2379
+ basePath = resolve3(importerDir, normalizedSpecifier.slice("self::".length).replace(/::/g, "/"));
2213
2380
  } else {
2214
- basePath = resolve2(dirname2(importerDir), normalizedSpecifier.slice("super::".length).replace(/::/g, "/"));
2381
+ basePath = resolve3(dirname3(importerDir), normalizedSpecifier.slice("super::".length).replace(/::/g, "/"));
2215
2382
  }
2216
2383
  for (const candidate of rustCandidateImportPaths(basePath)) {
2217
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2384
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2218
2385
  if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
2219
2386
  return relativeCandidate;
2220
2387
  }
@@ -2222,10 +2389,10 @@ function resolveRustImportPath(db, importerPath, specifier) {
2222
2389
  return null;
2223
2390
  }
2224
2391
  function resolveRubyImportPath(db, importerPath, specifier) {
2225
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2226
- const absolute = resolve2(importerDir, specifier);
2392
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2393
+ const absolute = resolve3(importerDir, specifier);
2227
2394
  for (const candidate of rubyCandidateImportPaths(absolute)) {
2228
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2395
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2229
2396
  if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
2230
2397
  return relativeCandidate;
2231
2398
  }
@@ -2234,15 +2401,15 @@ function resolveRubyImportPath(db, importerPath, specifier) {
2234
2401
  }
2235
2402
  function resolveCLikeImportPath(db, importerPath, specifier) {
2236
2403
  const indexedPaths = getIndexedPaths(db);
2237
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2404
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2238
2405
  const candidates = [
2239
- resolve2(importerDir, specifier),
2240
- resolve2(db.config.projectRoot, specifier),
2241
- resolve2(db.config.projectRoot, "include", specifier),
2242
- resolve2(db.config.projectRoot, "src", specifier)
2406
+ resolve3(importerDir, specifier),
2407
+ resolve3(db.config.projectRoot, specifier),
2408
+ resolve3(db.config.projectRoot, "include", specifier),
2409
+ resolve3(db.config.projectRoot, "src", specifier)
2243
2410
  ];
2244
2411
  for (const candidate of candidates) {
2245
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2412
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2246
2413
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2247
2414
  return relativeCandidate;
2248
2415
  }
@@ -2260,10 +2427,10 @@ function resolveQualifiedImportPath(db, specifier, extensions) {
2260
2427
  if (exact) return exact;
2261
2428
  }
2262
2429
  for (const ext of extensions) {
2263
- const basenameMatch = [...indexedPaths].find((relativePath) => basename(relativePath) === `${basenameOnly}${ext}`);
2430
+ const basenameMatch = [...indexedPaths].find((relativePath) => basename2(relativePath) === `${basenameOnly}${ext}`);
2264
2431
  if (basenameMatch) return basenameMatch;
2265
2432
  }
2266
- const folderMatches = [...indexedPaths].filter((relativePath) => extensions.includes(extname2(relativePath).toLowerCase())).filter((relativePath) => relativePath.includes(`/${pathified}/`) || relativePath.includes(`/${basenameOnly}/`)).sort((left, right) => left.localeCompare(right));
2433
+ const folderMatches = [...indexedPaths].filter((relativePath) => extensions.includes(extname3(relativePath).toLowerCase())).filter((relativePath) => relativePath.includes(`/${pathified}/`) || relativePath.includes(`/${basenameOnly}/`)).sort((left, right) => left.localeCompare(right));
2267
2434
  if (folderMatches.length === 1) {
2268
2435
  return folderMatches[0];
2269
2436
  }
@@ -2280,10 +2447,10 @@ function resolveDartImportPath(db, importerPath, specifier) {
2280
2447
  if (indexedPaths.has(candidate)) return candidate;
2281
2448
  return null;
2282
2449
  }
2283
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2284
- const absolute = resolve2(importerDir, specifier);
2450
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2451
+ const absolute = resolve3(importerDir, specifier);
2285
2452
  for (const candidate of dartCandidateImportPaths(absolute)) {
2286
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2453
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2287
2454
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2288
2455
  return relativeCandidate;
2289
2456
  }
@@ -2291,7 +2458,7 @@ function resolveDartImportPath(db, importerPath, specifier) {
2291
2458
  return null;
2292
2459
  }
2293
2460
  function pythonCandidateImportPaths(basePath) {
2294
- const ext = extname2(basePath);
2461
+ const ext = extname3(basePath);
2295
2462
  if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
2296
2463
  return [basePath];
2297
2464
  }
@@ -2303,7 +2470,7 @@ function pythonCandidateImportPaths(basePath) {
2303
2470
  ];
2304
2471
  }
2305
2472
  function rustCandidateImportPaths(basePath) {
2306
- const ext = extname2(basePath);
2473
+ const ext = extname3(basePath);
2307
2474
  if (RUST_SOURCE_EXTENSIONS.includes(ext)) {
2308
2475
  return [basePath];
2309
2476
  }
@@ -2313,7 +2480,7 @@ function rustCandidateImportPaths(basePath) {
2313
2480
  ];
2314
2481
  }
2315
2482
  function rubyCandidateImportPaths(basePath) {
2316
- const ext = extname2(basePath);
2483
+ const ext = extname3(basePath);
2317
2484
  if (RUBY_SOURCE_EXTENSIONS.includes(ext)) {
2318
2485
  return [basePath];
2319
2486
  }
@@ -2323,14 +2490,14 @@ function rubyCandidateImportPaths(basePath) {
2323
2490
  ];
2324
2491
  }
2325
2492
  function dartCandidateImportPaths(basePath) {
2326
- const ext = extname2(basePath);
2493
+ const ext = extname3(basePath);
2327
2494
  if (DART_SOURCE_EXTENSIONS.includes(ext)) {
2328
2495
  return [basePath];
2329
2496
  }
2330
2497
  return [`${basePath}.dart`, basePath];
2331
2498
  }
2332
2499
  function candidateImportPaths(absolute) {
2333
- const ext = extname2(absolute);
2500
+ const ext = extname3(absolute);
2334
2501
  const candidates = /* @__PURE__ */ new Set();
2335
2502
  if (ext) {
2336
2503
  candidates.add(absolute);
@@ -2373,34 +2540,34 @@ function normalizePath(path2) {
2373
2540
  return path2.replace(/\\/g, "/");
2374
2541
  }
2375
2542
  function isJavaScriptSourcePath(relativePath) {
2376
- return SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2543
+ return SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2377
2544
  }
2378
2545
  function isPythonSourcePath(relativePath) {
2379
- return PYTHON_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2546
+ return PYTHON_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2380
2547
  }
2381
2548
  function isJvmSourcePath(relativePath) {
2382
- return JVM_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2549
+ return JVM_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2383
2550
  }
2384
2551
  function isRustSourcePath(relativePath) {
2385
- return RUST_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2552
+ return RUST_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2386
2553
  }
2387
2554
  function isRubySourcePath(relativePath) {
2388
- return RUBY_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2555
+ return RUBY_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2389
2556
  }
2390
2557
  function isCLikeSourcePath(relativePath) {
2391
- return C_LIKE_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2558
+ return C_LIKE_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2392
2559
  }
2393
2560
  function isDotNetSourcePath(relativePath) {
2394
- return DOTNET_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2561
+ return DOTNET_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2395
2562
  }
2396
2563
  function isVisualBasicSourcePath(relativePath) {
2397
- return extname2(relativePath).toLowerCase() === ".vb";
2564
+ return extname3(relativePath).toLowerCase() === ".vb";
2398
2565
  }
2399
2566
  function isDartSourcePath(relativePath) {
2400
- return DART_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2567
+ return DART_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2401
2568
  }
2402
2569
  function isPhpSourcePath(relativePath) {
2403
- return PHP_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2570
+ return PHP_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2404
2571
  }
2405
2572
  function extensionFamilyFor(relativePath) {
2406
2573
  if (isJvmSourcePath(relativePath)) return JVM_SOURCE_EXTENSIONS;
@@ -2526,7 +2693,6 @@ function parseDescriptors(input) {
2526
2693
  const closingTick = input.indexOf("`", i + 1);
2527
2694
  if (closingTick === -1) {
2528
2695
  name = input.slice(i + 1);
2529
- i = input.length;
2530
2696
  descriptors.push({ name, suffix: "term" });
2531
2697
  break;
2532
2698
  }
@@ -2707,6 +2873,10 @@ function buildFileDepGraph(db, scope) {
2707
2873
  return graph;
2708
2874
  }
2709
2875
  function findFirstSymbolMatch(db, symbolPattern) {
2876
+ const exact = findExactSymbolMatch(db, symbolPattern.trim());
2877
+ if (exact) {
2878
+ return exact;
2879
+ }
2710
2880
  const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
2711
2881
  if (fileLineMatch) {
2712
2882
  const [, filePath, startStr, endStr] = fileLineMatch;
@@ -2744,19 +2914,16 @@ function findFirstSymbolMatch(db, symbolPattern) {
2744
2914
  );
2745
2915
  }
2746
2916
  if (row && !db.isIgnored(row.relative_path)) {
2747
- return {
2748
- symbolId: row.id,
2749
- symbol: row.symbol,
2750
- documentId: row.document_id,
2751
- startLine: row.start_line,
2752
- endLine: row.end_line,
2753
- relativePath: row.relative_path
2754
- };
2917
+ return hydrateSymbolMatch(db, row);
2755
2918
  }
2756
2919
  }
2757
2920
  const cleaned = normalizeLookupPattern(symbolPattern);
2758
2921
  const tokens = lookupTokens(symbolPattern);
2759
2922
  const candidates = getSymbolLookupCandidates(db, tokens);
2923
+ const direct = findDirectSymbolCandidate(candidates, symbolPattern, cleaned);
2924
+ if (direct && !db.isIgnored(direct.relative_path)) {
2925
+ return hydrateSymbolMatch(db, direct);
2926
+ }
2760
2927
  let best = null;
2761
2928
  for (const row of candidates) {
2762
2929
  if (db.isIgnored(row.relative_path)) continue;
@@ -2767,14 +2934,7 @@ function findFirstSymbolMatch(db, symbolPattern) {
2767
2934
  }
2768
2935
  }
2769
2936
  if (best) {
2770
- return {
2771
- symbolId: best.row.id,
2772
- symbol: best.row.symbol,
2773
- documentId: best.row.document_id,
2774
- startLine: best.row.start_line,
2775
- endLine: best.row.end_line,
2776
- relativePath: best.row.relative_path
2777
- };
2937
+ return hydrateSymbolMatch(db, best.row);
2778
2938
  }
2779
2939
  return null;
2780
2940
  }
@@ -2793,14 +2953,7 @@ function findExactSymbolMatch(db, symbol) {
2793
2953
  if (!row || db.isIgnored(row.relative_path)) {
2794
2954
  return null;
2795
2955
  }
2796
- return {
2797
- symbolId: row.id,
2798
- symbol: row.symbol,
2799
- documentId: row.document_id,
2800
- startLine: row.start_line,
2801
- endLine: row.end_line,
2802
- relativePath: row.relative_path
2803
- };
2956
+ return hydrateSymbolMatch(db, row);
2804
2957
  }
2805
2958
  function resolveIndexedFile(db, filePattern) {
2806
2959
  return resolveDocumentCandidates(db, filePattern, { allowMultiple: false })[0]?.relativePath ?? null;
@@ -2941,27 +3094,10 @@ function getCalleeRowsForSymbol(db, symbol, opts = {}) {
2941
3094
  });
2942
3095
  }
2943
3096
  const sourceFallback = getSourceBackedCalleeRows(db, symbol, opts.limit);
2944
- if (sourceFallback.length === 0) {
2945
- return primary;
2946
- }
2947
- if (primary.length === 0) {
3097
+ if (sourceFallback.length > 0) {
2948
3098
  return applyLimit(sourceFallback, opts.limit);
2949
3099
  }
2950
- const merged = [...sourceFallback];
2951
- const seen = new Set(merged.map((row) => `${row.symbol}|${row.file}`));
2952
- const preferredFallbackLeaves = new Set(
2953
- sourceFallback.filter((row) => !isLikelyTestPath(row.file)).map((row) => leafName(row.symbol))
2954
- );
2955
- for (const row of primary) {
2956
- if (isLikelyTestPath(row.file) && preferredFallbackLeaves.has(leafName(row.symbol))) {
2957
- continue;
2958
- }
2959
- const key = `${row.symbol}|${row.file}`;
2960
- if (seen.has(key)) continue;
2961
- seen.add(key);
2962
- merged.push(row);
2963
- }
2964
- return applyLimit(merged, opts.limit);
3100
+ return primary;
2965
3101
  }
2966
3102
  function getCallerRowsForSymbol(db, symbol, opts = {}) {
2967
3103
  const match = getFullSymbolMatch(db, symbol);
@@ -2994,11 +3130,13 @@ function getCallerRowsForSymbol(db, symbol, opts = {}) {
2994
3130
  ...getGenericSourceCallerRows(db, match, opts.limit)
2995
3131
  ]);
2996
3132
  if (sourceFallback.length === 0) {
2997
- return primary;
3133
+ return dedupeCallerRows(primary);
2998
3134
  }
2999
3135
  const merged = [...sourceFallback];
3136
+ const fallbackFiles = new Set(sourceFallback.map((row) => row.file));
3000
3137
  const seen = new Set(merged.map((row) => `${row.symbol}|${row.file}`));
3001
3138
  for (const row of primary) {
3139
+ if (fallbackFiles.has(row.file)) continue;
3002
3140
  const key = `${row.symbol}|${row.file}`;
3003
3141
  if (seen.has(key)) continue;
3004
3142
  if (isFunctionLikeSymbol(row.symbol) || merged.length === 0) {
@@ -3379,14 +3517,7 @@ function getFullSymbolMatch(db, symbol) {
3379
3517
  if (!row) {
3380
3518
  return null;
3381
3519
  }
3382
- return {
3383
- symbolId: row.id,
3384
- documentId: row.document_id,
3385
- startLine: row.start_line,
3386
- endLine: row.end_line,
3387
- symbol: row.symbol,
3388
- relativePath: row.relative_path
3389
- };
3520
+ return hydrateSymbolMatch(db, row);
3390
3521
  }
3391
3522
  function getDefinitionsForFile(db, relativePath) {
3392
3523
  let cache = FILE_DEFINITION_CACHE.get(db);
@@ -3406,6 +3537,7 @@ function getDefinitionsForFile(db, relativePath) {
3406
3537
  der.start_line,
3407
3538
  der.end_line,
3408
3539
  d.relative_path,
3540
+ gs.display_name,
3409
3541
  gs.kind,
3410
3542
  gs.documentation,
3411
3543
  gs.enclosing_symbol
@@ -3425,6 +3557,7 @@ function getDefinitionsForFile(db, relativePath) {
3425
3557
  MIN(c.start_line) AS start_line,
3426
3558
  MAX(c.end_line) AS end_line,
3427
3559
  d.relative_path,
3560
+ gs.display_name,
3428
3561
  gs.kind,
3429
3562
  gs.documentation,
3430
3563
  gs.enclosing_symbol
@@ -3439,23 +3572,222 @@ function getDefinitionsForFile(db, relativePath) {
3439
3572
  ORDER BY start_line, end_line`,
3440
3573
  relativePath
3441
3574
  );
3442
- const definitions = (primary.length > 0 ? primary : fallback).map((row) => ({
3575
+ const definitions = correctDefinitionRangesFromSource(
3576
+ db,
3577
+ relativePath,
3578
+ (primary.length > 0 ? primary : fallback).map((row) => ({
3579
+ symbolId: row.id,
3580
+ symbol: row.symbol,
3581
+ documentId: row.document_id,
3582
+ startLine: row.start_line,
3583
+ endLine: row.end_line,
3584
+ relativePath: row.relative_path,
3585
+ leaf: leafName(row.symbol),
3586
+ parentTypeName: parentTypeName(row.symbol),
3587
+ isFunctionLike: isFunctionLikeSymbol(row.symbol),
3588
+ isTypeLike: leafSuffix(row.symbol) === "type",
3589
+ kind: row.kind ?? null,
3590
+ documentation: row.documentation ?? null,
3591
+ enclosingSymbol: row.enclosing_symbol ?? null
3592
+ }))
3593
+ );
3594
+ cache.set(relativePath, definitions);
3595
+ return definitions;
3596
+ }
3597
+ function hydrateSymbolMatch(db, row) {
3598
+ const corrected = getDefinitionsForFile(db, row.relative_path).find((definition) => definition.symbolId === row.id);
3599
+ if (corrected) {
3600
+ return {
3601
+ symbolId: corrected.symbolId,
3602
+ symbol: corrected.symbol,
3603
+ documentId: corrected.documentId,
3604
+ startLine: corrected.startLine,
3605
+ endLine: corrected.endLine,
3606
+ relativePath: corrected.relativePath
3607
+ };
3608
+ }
3609
+ return {
3443
3610
  symbolId: row.id,
3444
3611
  symbol: row.symbol,
3445
3612
  documentId: row.document_id,
3446
3613
  startLine: row.start_line,
3447
3614
  endLine: row.end_line,
3448
- relativePath: row.relative_path,
3449
- leaf: leafName(row.symbol),
3450
- parentTypeName: parentTypeName(row.symbol),
3451
- isFunctionLike: isFunctionLikeSymbol(row.symbol),
3452
- isTypeLike: leafSuffix(row.symbol) === "type",
3453
- kind: row.kind,
3454
- documentation: row.documentation,
3455
- enclosingSymbol: row.enclosing_symbol
3456
- }));
3457
- cache.set(relativePath, definitions);
3458
- return definitions;
3615
+ relativePath: row.relative_path
3616
+ };
3617
+ }
3618
+ function findDirectSymbolCandidate(candidates, symbolPattern, cleanedPattern) {
3619
+ const trimmed = symbolPattern.trim();
3620
+ const directMatches = candidates.filter((row) => {
3621
+ const short = shortenSymbol(row.symbol);
3622
+ const display = (row.display_name ?? "").trim();
3623
+ return row.symbol === trimmed || short === trimmed || short === cleanedPattern || display === trimmed || display === cleanedPattern || `${display}()` === trimmed || row.relative_path === trimmed;
3624
+ });
3625
+ if (directMatches.length === 0) {
3626
+ return null;
3627
+ }
3628
+ directMatches.sort(
3629
+ (left, right) => left.end_line - left.start_line - (right.end_line - right.start_line) || left.relative_path.localeCompare(right.relative_path) || left.symbol.localeCompare(right.symbol)
3630
+ );
3631
+ return directMatches[0] ?? null;
3632
+ }
3633
+ function correctDefinitionRangesFromSource(db, relativePath, definitions) {
3634
+ const source = getSourceText(db, relativePath);
3635
+ if (!source) {
3636
+ return definitions;
3637
+ }
3638
+ const lines = source.split(/\r?\n/);
3639
+ const correctedStarts = /* @__PURE__ */ new Map();
3640
+ for (const definition of definitions) {
3641
+ correctedStarts.set(
3642
+ definition.symbolId,
3643
+ resolveCallableDefinitionStartLine(lines, definition)
3644
+ );
3645
+ }
3646
+ const correctedRanges = /* @__PURE__ */ new Map();
3647
+ const callableDefinitions = definitions.filter((definition) => isCallableDefinition(definition.symbol)).map((definition) => ({
3648
+ definition,
3649
+ startLine: correctedStarts.get(definition.symbolId) ?? definition.startLine
3650
+ })).sort(
3651
+ (left, right) => left.startLine - right.startLine || left.definition.startLine - right.definition.startLine || left.definition.symbol.localeCompare(right.definition.symbol)
3652
+ );
3653
+ for (let index = 0; index < callableDefinitions.length; index += 1) {
3654
+ const current = callableDefinitions[index];
3655
+ const next = callableDefinitions[index + 1];
3656
+ const maxEndLine = next ? Math.max(current.startLine, next.startLine - 1) : lines.length - 1;
3657
+ correctedRanges.set(current.definition.symbolId, {
3658
+ startLine: current.startLine,
3659
+ endLine: resolveCallableDefinitionEndLine(
3660
+ lines,
3661
+ current.definition,
3662
+ current.startLine,
3663
+ maxEndLine
3664
+ )
3665
+ });
3666
+ }
3667
+ return definitions.map((definition) => {
3668
+ const corrected = correctedRanges.get(definition.symbolId);
3669
+ if (!corrected) {
3670
+ return definition;
3671
+ }
3672
+ return {
3673
+ ...definition,
3674
+ startLine: corrected.startLine,
3675
+ endLine: corrected.endLine
3676
+ };
3677
+ });
3678
+ }
3679
+ function resolveCallableDefinitionStartLine(lines, definition) {
3680
+ if (!isCallableDefinition(definition.symbol)) {
3681
+ return definition.startLine;
3682
+ }
3683
+ const escapedLeaf = escapeRegex2(definition.leaf);
3684
+ const strongPatterns = [
3685
+ new RegExp(`\\b(?:function|def|fn)\\s+${escapedLeaf}\\b`),
3686
+ new RegExp(`\\b${escapedLeaf}\\b\\s*[:=]\\s*(?:async\\s*)?(?:function\\b|\\()`)
3687
+ ];
3688
+ const fallbackPatterns = [
3689
+ new RegExp(`\\b${escapedLeaf}\\b\\s*\\(`)
3690
+ ];
3691
+ return findNearestMatchingLine(
3692
+ lines,
3693
+ [...strongPatterns, ...fallbackPatterns],
3694
+ definition.startLine,
3695
+ definition.endLine
3696
+ );
3697
+ }
3698
+ function findNearestMatchingLine(lines, patterns, preferredStartLine, preferredEndLine) {
3699
+ const windowStart = Math.max(0, preferredStartLine - 40);
3700
+ const windowEnd = Math.min(lines.length - 1, Math.max(preferredEndLine + 40, preferredStartLine + 5));
3701
+ const windowMatch = matchNearestLine(lines, patterns, preferredStartLine, windowStart, windowEnd);
3702
+ if (windowMatch !== null) {
3703
+ return windowMatch;
3704
+ }
3705
+ const fullMatch = matchNearestLine(lines, patterns, preferredStartLine, 0, lines.length - 1);
3706
+ return fullMatch ?? Math.max(0, Math.min(preferredStartLine, lines.length - 1));
3707
+ }
3708
+ function matchNearestLine(lines, patterns, preferredLine, startLine, endLine) {
3709
+ let best = null;
3710
+ for (let lineIndex = startLine; lineIndex <= endLine; lineIndex += 1) {
3711
+ const line = lines[lineIndex] ?? "";
3712
+ if (!patterns.some((pattern) => pattern.test(line))) continue;
3713
+ const distance = Math.abs(lineIndex - preferredLine);
3714
+ if (!best || distance < best.distance) {
3715
+ best = { line: lineIndex, distance };
3716
+ }
3717
+ }
3718
+ return best?.line ?? null;
3719
+ }
3720
+ function resolveCallableDefinitionEndLine(lines, definition, startLine, maxEndLine) {
3721
+ const boundedEndLine = Math.max(startLine, Math.min(lines.length - 1, maxEndLine));
3722
+ const fallbackEndLine = Math.max(startLine, Math.min(boundedEndLine, definition.endLine));
3723
+ let braceDepth = 0;
3724
+ let parenDepth = 0;
3725
+ let sawOpeningBrace = false;
3726
+ for (let lineIndex = startLine; lineIndex <= boundedEndLine; lineIndex += 1) {
3727
+ const masked = maskStructuralLine(lines[lineIndex] ?? "");
3728
+ for (const char of masked) {
3729
+ if (char === "{") {
3730
+ braceDepth += 1;
3731
+ sawOpeningBrace = true;
3732
+ } else if (char === "}") {
3733
+ braceDepth = Math.max(0, braceDepth - 1);
3734
+ } else if (char === "(") {
3735
+ parenDepth += 1;
3736
+ } else if (char === ")") {
3737
+ parenDepth = Math.max(0, parenDepth - 1);
3738
+ }
3739
+ }
3740
+ if (sawOpeningBrace && braceDepth === 0) {
3741
+ return lineIndex;
3742
+ }
3743
+ if (!sawOpeningBrace && parenDepth === 0 && lineIndex >= fallbackEndLine) {
3744
+ return lineIndex;
3745
+ }
3746
+ }
3747
+ return fallbackEndLine;
3748
+ }
3749
+ function maskStructuralLine(line) {
3750
+ let masked = "";
3751
+ let quote = null;
3752
+ let escaping = false;
3753
+ for (let index = 0; index < line.length; index += 1) {
3754
+ const char = line[index];
3755
+ const next = line[index + 1];
3756
+ if (!quote && char === "/" && next === "/") {
3757
+ masked += " ".repeat(line.length - index);
3758
+ break;
3759
+ }
3760
+ if (quote) {
3761
+ if (escaping) {
3762
+ escaping = false;
3763
+ masked += " ";
3764
+ continue;
3765
+ }
3766
+ if (char === "\\") {
3767
+ escaping = true;
3768
+ masked += " ";
3769
+ continue;
3770
+ }
3771
+ if (char === quote) {
3772
+ quote = null;
3773
+ }
3774
+ masked += " ";
3775
+ continue;
3776
+ }
3777
+ if (char === '"' || char === "'" || char === "`") {
3778
+ quote = char;
3779
+ masked += " ";
3780
+ continue;
3781
+ }
3782
+ masked += char;
3783
+ }
3784
+ return masked;
3785
+ }
3786
+ function isCallableDefinition(symbol) {
3787
+ return symbol.includes("().");
3788
+ }
3789
+ function escapeRegex2(value) {
3790
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3459
3791
  }
3460
3792
  function getAllDefinitions(db, opts = {}) {
3461
3793
  const { scope } = opts;
@@ -4008,8 +4340,8 @@ function resolveDocumentCandidates(db, filePattern, opts) {
4008
4340
  }
4009
4341
  function scoreDocumentPath(relativePath, rawPattern) {
4010
4342
  const normalizedPath = normalizeLookupPath(relativePath);
4011
- const pathBase = basename2(normalizedPath);
4012
- const patternBase = basename2(rawPattern);
4343
+ const pathBase = basename3(normalizedPath);
4344
+ const patternBase = basename3(rawPattern);
4013
4345
  let score = 0;
4014
4346
  if (normalizedPath === rawPattern) score += 1200;
4015
4347
  if (normalizedPath.endsWith(`/${rawPattern}`)) score += 1100;
@@ -4055,7 +4387,7 @@ function symbols(db, filePattern) {
4055
4387
  }
4056
4388
 
4057
4389
  // src/queries/methods.ts
4058
- import { basename as basename3 } from "path";
4390
+ import { basename as basename4 } from "path";
4059
4391
  function methods(db, className) {
4060
4392
  const classMatch = findFirstSymbolMatch(db, className);
4061
4393
  if (!classMatch) {
@@ -4064,7 +4396,7 @@ function methods(db, className) {
4064
4396
  const ownerName = leafName(classMatch.symbol);
4065
4397
  const definitions = getDefinitionsForFile(db, classMatch.relativePath).filter((definition) => isCallableSymbol(definition.symbol));
4066
4398
  const directMethods = definitions.filter((definition) => definition.parentTypeName === ownerName || definition.symbol.includes(ownerName));
4067
- const fileScopedMethods = directMethods.length > 0 ? directMethods : stripExtension(basename3(classMatch.relativePath)) === ownerName ? definitions.filter((definition) => definition.symbol.includes("<invalid-global-code>")) : [];
4399
+ const fileScopedMethods = directMethods.length > 0 ? directMethods : stripExtension(basename4(classMatch.relativePath)) === ownerName ? definitions.filter((definition) => definition.symbol.includes("<invalid-global-code>")) : [];
4068
4400
  return fileScopedMethods.map((definition) => ({
4069
4401
  startLine: definition.startLine,
4070
4402
  endLine: definition.endLine,
@@ -4448,11 +4780,11 @@ function isWorkerEntrySurface(path2) {
4448
4780
  function isStructuralEntrySurface(path2) {
4449
4781
  const normalized = normalizePath2(path2);
4450
4782
  const segments = normalized.split("/");
4451
- const basename5 = segments[segments.length - 1] ?? normalized;
4452
- if (basename5 === "cli.ts" || basename5 === "cli.js" || basename5 === "postinstall.ts" || basename5 === "postinstall.js" || basename5 === "main.ts" || basename5 === "main.js" || basename5 === "main.rs" || basename5 === "main.go" || basename5 === "main.py") {
4783
+ const basename6 = segments[segments.length - 1] ?? normalized;
4784
+ if (basename6 === "cli.ts" || basename6 === "cli.js" || basename6 === "postinstall.ts" || basename6 === "postinstall.js" || basename6 === "main.ts" || basename6 === "main.js" || basename6 === "main.rs" || basename6 === "main.go" || basename6 === "main.py") {
4453
4785
  return true;
4454
4786
  }
4455
- if (basename5 === "index.ts" || basename5 === "index.js") {
4787
+ if (basename6 === "index.ts" || basename6 === "index.js") {
4456
4788
  return segments.length <= 2;
4457
4789
  }
4458
4790
  return normalized.endsWith("/mod.rs") || normalized.endsWith("/__init__.py");
@@ -5526,7 +5858,7 @@ function similar(db, symbolPattern, opts = {}) {
5526
5858
  const results = [];
5527
5859
  for (const candidate of candidates) {
5528
5860
  if (candidate.callees.size < 3) continue;
5529
- const { similarity, significantShared, trivialShared } = weightedSimilarity(
5861
+ const { similarity, significantShared } = weightedSimilarity(
5530
5862
  target.callees,
5531
5863
  candidate.callees,
5532
5864
  idfWeights
@@ -5763,11 +6095,11 @@ function definitionSnippet(db, relativePath, startLine, endLine, leaf) {
5763
6095
  return lines.slice(startLine, endLine + 1).join("\n");
5764
6096
  }
5765
6097
  const markerPatterns = [
5766
- new RegExp(`\\bdef\\s+${escapeRegex2(leaf)}\\b`),
5767
- new RegExp(`\\bfun\\s+${escapeRegex2(leaf)}\\b`),
5768
- new RegExp(`\\bfn\\s+${escapeRegex2(leaf)}\\b`),
5769
- new RegExp(`\\bfunction\\s+${escapeRegex2(leaf)}\\b`),
5770
- new RegExp(`\\b${escapeRegex2(leaf)}\\s*\\(`)
6098
+ new RegExp(`\\bdef\\s+${escapeRegex3(leaf)}\\b`),
6099
+ new RegExp(`\\bfun\\s+${escapeRegex3(leaf)}\\b`),
6100
+ new RegExp(`\\bfn\\s+${escapeRegex3(leaf)}\\b`),
6101
+ new RegExp(`\\bfunction\\s+${escapeRegex3(leaf)}\\b`),
6102
+ new RegExp(`\\b${escapeRegex3(leaf)}\\s*\\(`)
5771
6103
  ];
5772
6104
  const definitionStart = lines.findIndex((line) => markerPatterns.some((pattern) => pattern.test(line)));
5773
6105
  if (definitionStart >= 0) {
@@ -5829,7 +6161,7 @@ function splitIdentifier(value) {
5829
6161
  function looksLikeDefinitionBoundary(line) {
5830
6162
  return /^\s*(?:def|fun|fn|function|class|trait|module|object|enum|interface|public|private|protected)\b/.test(line);
5831
6163
  }
5832
- function escapeRegex2(value) {
6164
+ function escapeRegex3(value) {
5833
6165
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5834
6166
  }
5835
6167
 
@@ -5955,8 +6287,8 @@ function similarChains(db, opts = {}) {
5955
6287
  }
5956
6288
  const structuralNames = ["index.ts", "index.js", "cli.ts", "main.ts", "health.ts", "health.js"];
5957
6289
  for (const node of nodeFreq.keys()) {
5958
- const basename5 = node.split("/").pop() ?? "";
5959
- if (structuralNames.includes(basename5)) infraNodes.add(node);
6290
+ const basename6 = node.split("/").pop() ?? "";
6291
+ if (structuralNames.includes(basename6)) infraNodes.add(node);
5960
6292
  }
5961
6293
  const filteredChains = [];
5962
6294
  for (const chain of rawChains) {
@@ -6130,8 +6462,8 @@ function extractCandidates(db, opts = {}) {
6130
6462
  const results = [];
6131
6463
  for (const sym of symbols2) {
6132
6464
  if (db.isIgnored(sym.relativePath)) continue;
6133
- const basename5 = sym.relativePath.split("/").pop() ?? "";
6134
- if (basename5.includes("types")) continue;
6465
+ const basename6 = sym.relativePath.split("/").pop() ?? "";
6466
+ if (basename6.includes("types")) continue;
6135
6467
  const calleeChunks = getCalleeRowsForSymbol(db, sym);
6136
6468
  const calleeSet = new Set(calleeChunks.map((c) => c.symbol));
6137
6469
  if (calleeSet.size < minCallees) continue;
@@ -6643,21 +6975,21 @@ function isLikelyTypeOnlyDep(dep) {
6643
6975
  function shouldSkipDriftFile(filePath) {
6644
6976
  return isStructuralRole(path.basename(filePath)) || isTestLikePath(filePath);
6645
6977
  }
6646
- function isStructuralRole(basename5) {
6647
- if (basename5 === "index.ts" || basename5 === "index.js") return true;
6648
- if (basename5 === "cli.ts" || basename5 === "main.ts" || basename5 === "main.rs") return true;
6649
- if (basename5.includes("worker.") || basename5.includes("postinstall.")) return true;
6650
- if (basename5 === "health.ts" || basename5 === "health.js") return true;
6978
+ function isStructuralRole(basename6) {
6979
+ if (basename6 === "index.ts" || basename6 === "index.js") return true;
6980
+ if (basename6 === "cli.ts" || basename6 === "main.ts" || basename6 === "main.rs") return true;
6981
+ if (basename6.includes("worker.") || basename6.includes("postinstall.")) return true;
6982
+ if (basename6 === "health.ts" || basename6 === "health.js") return true;
6651
6983
  return false;
6652
6984
  }
6653
6985
  function isTestLikePath(filePath) {
6654
6986
  const normalized = filePath.replace(/\\/g, "/");
6655
- const basename5 = path.basename(normalized);
6656
- return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(basename5) || /_(test|spec)\.[A-Za-z0-9]+$/.test(basename5) || /^test[_-]/.test(basename5) || /^test\./.test(basename5);
6987
+ const basename6 = path.basename(normalized);
6988
+ return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(basename6) || /_(test|spec)\.[A-Za-z0-9]+$/.test(basename6) || /^test[_-]/.test(basename6) || /^test\./.test(basename6);
6657
6989
  }
6658
6990
 
6659
6991
  // src/queries/wrapper-candidates.ts
6660
- import { basename as basename4, extname as extname3 } from "path";
6992
+ import { basename as basename5, extname as extname4 } from "path";
6661
6993
  function wrapperCandidates(db, opts) {
6662
6994
  const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
6663
6995
  const reverseFanIn = buildReverseFileFanIn(buildFileDepGraph(db, scope));
@@ -6665,10 +6997,10 @@ function wrapperCandidates(db, opts) {
6665
6997
  const results = [];
6666
6998
  for (const symbol of symbols2) {
6667
6999
  if (db.isIgnored(symbol.relativePath) || !isFunctionLikeSymbol(symbol.symbol)) continue;
6668
- const symbolStem = basename4(symbol.relativePath, extname3(symbol.relativePath));
7000
+ const symbolStem = basename5(symbol.relativePath, extname4(symbol.relativePath));
6669
7001
  const callerRows = dedupeRows(
6670
7002
  getCallerRowsForSymbol(db, symbol, { limit: 200 }).filter((row) => row.file !== symbol.relativePath)
6671
- ).filter((row) => basename4(row.file, extname3(row.file)) !== symbolStem);
7003
+ ).filter((row) => basename5(row.file, extname4(row.file)) !== symbolStem);
6672
7004
  if (callerRows.length !== 1) continue;
6673
7005
  const caller = callerRows[0];
6674
7006
  const callerDefinition = getDefinitionsForFile(db, caller.file).find((definition) => definition.symbol === caller.symbol);
@@ -6690,7 +7022,7 @@ function wrapperCandidates(db, opts) {
6690
7022
  endLine: symbol.endLine,
6691
7023
  loc: definitionLoc2(symbol),
6692
7024
  singleCaller: caller.symbol,
6693
- singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) : basename4(caller.file),
7025
+ singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) : basename5(caller.file),
6694
7026
  callerFanIn
6695
7027
  });
6696
7028
  }
@@ -6745,11 +7077,11 @@ function fallbackCallerFanIn(db, reverseFanIn, callerFile) {
6745
7077
  if (direct > 0) {
6746
7078
  return direct;
6747
7079
  }
6748
- const stem = basename4(callerFile, extname3(callerFile));
7080
+ const stem = basename5(callerFile, extname4(callerFile));
6749
7081
  let best = 0;
6750
7082
  for (const [file, fanIn2] of reverseFanIn) {
6751
7083
  if (file === callerFile) continue;
6752
- if (basename4(file, extname3(file)) !== stem) continue;
7084
+ if (basename5(file, extname4(file)) !== stem) continue;
6753
7085
  if (fanIn2 > best) {
6754
7086
  best = fanIn2;
6755
7087
  }
@@ -6830,8 +7162,8 @@ function getFilesWithFunctions(db, scope) {
6830
7162
  return new Set(getScopedDefinitions4(db, scope).filter((definition) => definition.isFunctionLike).map((definition) => definition.relativePath));
6831
7163
  }
6832
7164
  function isTrueStaleAbstraction(row, filesWithFunctions) {
6833
- const basename5 = row.file.split("/").pop() ?? "";
6834
- const isTypeFile = basename5.includes("types") || row.file.includes("/types/");
7165
+ const basename6 = row.file.split("/").pop() ?? "";
7166
+ const isTypeFile = basename6.includes("types") || row.file.includes("/types/");
6835
7167
  if (isTypeFile && row.consumers > 0) {
6836
7168
  return false;
6837
7169
  }
@@ -7820,17 +8152,17 @@ function normalizeSignature(raw) {
7820
8152
  }
7821
8153
  function normalizeSourceSignature(raw, leaf) {
7822
8154
  if (!raw || !raw.trim()) return null;
7823
- let declaration = raw.replace(/\s+/g, " ").trim();
8155
+ const declaration = raw.replace(/\s+/g, " ").trim();
7824
8156
  const parenIdx = declaration.indexOf("(");
7825
8157
  if (parenIdx === -1) return null;
7826
8158
  let prefix = declaration.slice(0, parenIdx);
7827
- const leafPattern = new RegExp(`\\b${escapeRegex3(leaf)}\\b`, "i");
8159
+ const leafPattern = new RegExp(`\\b${escapeRegex4(leaf)}\\b`, "i");
7828
8160
  const leafMatch = leafPattern.exec(prefix);
7829
8161
  if (leafMatch && typeof leafMatch.index === "number") {
7830
8162
  prefix = prefix.slice(0, leafMatch.index);
7831
8163
  }
7832
8164
  prefix = prefix.replace(/\b(public|private|protected|internal|final|static|abstract|sealed|virtual|override|async|suspend|inline|constexpr|consteval|constinit|const|pub|fn|function|def|sub|friend|shared|readonly|new|open|partial|export)\b/gi, " ").replace(/\s+/g, " ").trim();
7833
- let suffix = declaration.slice(parenIdx).replace(/\s*\{[\s\S]*$/, "").replace(/\s*=>[\s\S]*$/, "").replace(/\)\s*=\s*[\s\S]*$/, ")").replace(/\s+throws\s+[^={]+$/i, "").replace(/\s+where\s+.+$/i, "").replace(/\s+/g, " ").trim();
8165
+ const suffix = declaration.slice(parenIdx).replace(/\s*\{[\s\S]*$/, "").replace(/\s*=>[\s\S]*$/, "").replace(/\)\s*=\s*[\s\S]*$/, ")").replace(/\s+throws\s+[^={]+$/i, "").replace(/\s+where\s+.+$/i, "").replace(/\s+/g, " ").trim();
7834
8166
  if (!suffix.startsWith("(")) {
7835
8167
  return null;
7836
8168
  }
@@ -7838,7 +8170,7 @@ function normalizeSourceSignature(raw, leaf) {
7838
8170
  return normalized.length >= 3 ? normalized : null;
7839
8171
  }
7840
8172
  function declarationStartLines(lines, startLine, endLine, leaf) {
7841
- const escapedLeaf = escapeRegex3(leaf);
8173
+ const escapedLeaf = escapeRegex4(leaf);
7842
8174
  const callablePattern = new RegExp(`\\b${escapedLeaf}\\b\\s*\\(`, "i");
7843
8175
  const rubyPattern = new RegExp(`\\bdef\\s+${escapedLeaf}\\b`, "i");
7844
8176
  const candidates = [];
@@ -7869,7 +8201,7 @@ function parenBalance(value) {
7869
8201
  }
7870
8202
  return balance;
7871
8203
  }
7872
- function escapeRegex3(value) {
8204
+ function escapeRegex4(value) {
7873
8205
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7874
8206
  }
7875
8207
 
@@ -7881,7 +8213,7 @@ import {
7881
8213
  readlinkSync,
7882
8214
  unlinkSync
7883
8215
  } from "fs";
7884
- import { join as join11, dirname as dirname3, resolve as resolve3 } from "path";
8216
+ import { join as join11, dirname as dirname4, resolve as resolve4 } from "path";
7885
8217
  import { homedir as homedir2, platform as platform3 } from "os";
7886
8218
  import { fileURLToPath } from "url";
7887
8219
  var IS_WINDOWS3 = platform3() === "win32";
@@ -7896,7 +8228,7 @@ function installSkills(opts = {}) {
7896
8228
  const log = opts.quiet ? () => {
7897
8229
  } : console.log;
7898
8230
  const thisFile = fileURLToPath(import.meta.url);
7899
- const skillsSource = resolve3(dirname3(thisFile), "..", "skills");
8231
+ const skillsSource = resolve4(dirname4(thisFile), "..", "skills");
7900
8232
  const targets = [
7901
8233
  join11(homedir2(), ".claude", "skills"),
7902
8234
  join11(homedir2(), ".codex", "skills")
@@ -7907,7 +8239,7 @@ function installSkills(opts = {}) {
7907
8239
  alreadyLinked: []
7908
8240
  };
7909
8241
  for (const targetDir of targets) {
7910
- const parentDir = dirname3(targetDir);
8242
+ const parentDir = dirname4(targetDir);
7911
8243
  if (!existsSync9(parentDir)) {
7912
8244
  continue;
7913
8245
  }
@@ -7923,7 +8255,7 @@ function installSkills(opts = {}) {
7923
8255
  if (existsSync9(target)) {
7924
8256
  try {
7925
8257
  const existing = readlinkSync(target);
7926
- if (resolve3(existing) === resolve3(source)) {
8258
+ if (resolve4(existing) === resolve4(source)) {
7927
8259
  result.alreadyLinked.push(`${toolName}/${skill}`);
7928
8260
  log(` ok: ${skill} \u2192 ${toolName} (already linked)`);
7929
8261
  continue;
@@ -8050,11 +8382,15 @@ var queries = {
8050
8382
  program.name("scip-query").description("Language-agnostic code intelligence CLI powered by SCIP indexes").version(cliVersion);
8051
8383
  program.command("reindex").description("Index the codebase and convert to SQLite").option("-l, --language <lang>", "Index only this language (can be repeated)", collect, []).option("--pnpm-workspaces", "Enable pnpm workspace support (TypeScript)").action(async (opts) => {
8052
8384
  const projectRoot = resolveProjectRoot();
8385
+ const config = loadProjectConfig(projectRoot);
8386
+ const paths = resolveIndexPaths(projectRoot, config);
8053
8387
  try {
8054
8388
  const result = await reindex({
8055
8389
  projectRoot,
8056
- languages: opts.language.length > 0 ? opts.language : void 0,
8057
- pnpmWorkspaces: opts.pnpmWorkspaces
8390
+ languages: opts.language.length > 0 ? opts.language : config.languages,
8391
+ outputScip: paths.indexPath,
8392
+ outputDb: paths.dbPath,
8393
+ pnpmWorkspaces: opts.pnpmWorkspaces || config.indexer?.typescript?.pnpmWorkspaces
8058
8394
  });
8059
8395
  console.log(`Indexed ${result.languages.join(", ")} in ${(result.durationMs / 1e3).toFixed(1)}s`);
8060
8396
  } catch (err) {
@@ -9039,6 +9375,7 @@ program.command("watch").description("Watch for file changes and reindex automat
9039
9375
  const watcher = new Watcher({
9040
9376
  projectRoot,
9041
9377
  config,
9378
+ languages: config.languages,
9042
9379
  onStatus: (status) => {
9043
9380
  process.stdout.write(`\r\x1B[K${formatStatus(status)}`);
9044
9381
  },