scip-query 0.4.1 → 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.
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
  });
@@ -1052,7 +1203,11 @@ Make sure ${binaryLabel} is installed and available on PATH.`,
1052
1203
  { cause: err }
1053
1204
  );
1054
1205
  }
1055
- 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);
1056
1211
  }
1057
1212
  onStatus("Converting to SQLite...");
1058
1213
  if (!existsSync6(outputScip)) {
@@ -1067,6 +1222,12 @@ Make sure ${binaryLabel} is installed and available on PATH.`,
1067
1222
  } catch (err) {
1068
1223
  const msg = err instanceof Error ? err.message : String(err);
1069
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
+ }
1070
1231
  }
1071
1232
  const durationMs = Date.now() - start;
1072
1233
  onStatus(`Done in ${(durationMs / 1e3).toFixed(1)}s`);
@@ -1081,11 +1242,16 @@ function moveDefaultOutputIfNeeded(config, projectRoot, outputScip) {
1081
1242
  renameSync(defaultOutputPath, outputScip);
1082
1243
  }
1083
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
+ }
1084
1250
 
1085
1251
  // src/watch.ts
1086
1252
  import { watch } from "fs";
1087
1253
  import { existsSync as existsSync7, renameSync as renameSync2 } from "fs";
1088
- import { join as join7, relative } from "path";
1254
+ import { join as join7, relative as relative2 } from "path";
1089
1255
  import { fork } from "child_process";
1090
1256
  import ignore2 from "ignore";
1091
1257
  var Watcher = class {
@@ -1163,7 +1329,7 @@ var Watcher = class {
1163
1329
  }
1164
1330
  // ── Internal ─────────────────────────────────────────────
1165
1331
  handleFileChange(filename) {
1166
- const rel = relative(this.projectRoot, join7(this.projectRoot, filename));
1332
+ const rel = relative2(this.projectRoot, join7(this.projectRoot, filename));
1167
1333
  if (this.gitignoreFilter.isIgnored(rel)) return;
1168
1334
  if (this.extraIgnore.ignores(rel)) return;
1169
1335
  if (filename.endsWith("index.db") || filename.endsWith("index.scip") || filename.endsWith("index.db.tmp") || filename.endsWith(".scipquery.json")) {
@@ -1244,10 +1410,10 @@ var Watcher = class {
1244
1410
  * Writes to index.db.tmp, then atomically renames to index.db.
1245
1411
  */
1246
1412
  runReindex() {
1247
- return new Promise((resolve4, reject) => {
1413
+ return new Promise((resolve5, reject) => {
1248
1414
  const start = Date.now();
1249
1415
  const tmpDb = this.indexPaths.dbPath + ".tmp";
1250
- const tmpScip = tempScipPath(this.indexPaths.indexPath);
1416
+ const tmpScip = tempScipPath2(this.indexPaths.indexPath);
1251
1417
  const child = fork(
1252
1418
  new URL("./reindex-worker.js", import.meta.url).pathname,
1253
1419
  [],
@@ -1272,7 +1438,7 @@ var Watcher = class {
1272
1438
  if (existsSync7(tmpScip)) {
1273
1439
  renameSync2(tmpScip, this.indexPaths.indexPath);
1274
1440
  }
1275
- resolve4(Date.now() - start);
1441
+ resolve5(Date.now() - start);
1276
1442
  } catch (err) {
1277
1443
  reject(new Error(`Atomic swap failed: ${err}`));
1278
1444
  }
@@ -1288,7 +1454,7 @@ var Watcher = class {
1288
1454
  this.onStatus(status);
1289
1455
  }
1290
1456
  };
1291
- function tempScipPath(indexPath) {
1457
+ function tempScipPath2(indexPath) {
1292
1458
  return indexPath.endsWith(".scip") ? indexPath.slice(0, -".scip".length) + ".tmp.scip" : indexPath + ".tmp.scip";
1293
1459
  }
1294
1460
 
@@ -1324,20 +1490,20 @@ function files(db, pattern) {
1324
1490
  }
1325
1491
 
1326
1492
  // src/query-support.ts
1327
- import { basename as basename2 } from "path";
1493
+ import { basename as basename3 } from "path";
1328
1494
 
1329
1495
  // src/source-analysis.ts
1330
1496
  import {
1331
1497
  existsSync as existsSync8,
1332
- readFileSync as readFileSync3
1498
+ readFileSync as readFileSync4
1333
1499
  } from "fs";
1334
1500
  import {
1335
- basename,
1336
- dirname as dirname2,
1337
- extname as extname2,
1501
+ basename as basename2,
1502
+ dirname as dirname3,
1503
+ extname as extname3,
1338
1504
  join as join8,
1339
- relative as relative2,
1340
- resolve as resolve2
1505
+ relative as relative3,
1506
+ resolve as resolve3
1341
1507
  } from "path";
1342
1508
  var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
1343
1509
  var SOURCE_EXPORT_CACHE = /* @__PURE__ */ new WeakMap();
@@ -1366,7 +1532,7 @@ function getSourceImports(db, relativePath) {
1366
1532
  cache.set(normalized, []);
1367
1533
  return [];
1368
1534
  }
1369
- const source = readFileSync3(fullPath, "utf-8");
1535
+ const source = readFileSync4(fullPath, "utf-8");
1370
1536
  const parsed = isPythonSourcePath(normalized) ? parsePythonImports(db, normalized, source) : isJavaScriptSourcePath(normalized) ? parseJavaScriptImports(db, normalized, source) : isJvmSourcePath(normalized) ? parseJvmImports(db, normalized, source) : isRustSourcePath(normalized) ? parseRustImports(db, normalized, source) : isRubySourcePath(normalized) ? parseRubyImports(db, normalized, source) : isCLikeSourcePath(normalized) ? parseCLikeImports(db, normalized, source) : isDotNetSourcePath(normalized) ? parseDotNetImports(db, normalized, source) : isDartSourcePath(normalized) ? parseDartImports(db, normalized, source) : isPhpSourcePath(normalized) ? parsePhpImports(db, normalized, source) : [];
1371
1537
  cache.set(normalized, parsed);
1372
1538
  return parsed;
@@ -1383,7 +1549,7 @@ function getSourceExports(db, relativePath) {
1383
1549
  cache.set(normalized, []);
1384
1550
  return [];
1385
1551
  }
1386
- const source = readFileSync3(fullPath, "utf-8");
1552
+ const source = readFileSync4(fullPath, "utf-8");
1387
1553
  const parsed = isDartSourcePath(normalized) ? parseDartExports(db, normalized, source) : isRustSourcePath(normalized) ? parseRustExports(db, normalized, source) : [];
1388
1554
  cache.set(normalized, parsed);
1389
1555
  return parsed;
@@ -1680,7 +1846,7 @@ function parseRubyImports(db, importerPath, source) {
1680
1846
  return statements;
1681
1847
  }
1682
1848
  function rubyConstantName(specifier) {
1683
- 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("");
1684
1850
  }
1685
1851
  function parseCLikeImports(db, importerPath, source) {
1686
1852
  const statements = [];
@@ -1689,7 +1855,7 @@ function parseCLikeImports(db, importerPath, source) {
1689
1855
  const full = match[0];
1690
1856
  if (!specifier || !full || typeof match.index !== "number") continue;
1691
1857
  const body = buildUsageBody(source, match.index, match.index + full.length);
1692
- const localName = basename(specifier).replace(/\.[^.]+$/, "");
1858
+ const localName = basename2(specifier).replace(/\.[^.]+$/, "");
1693
1859
  statements.push({
1694
1860
  importedName: specifier,
1695
1861
  localName,
@@ -2164,16 +2330,16 @@ function resolveJavaScriptImportPath(db, importerPath, specifier) {
2164
2330
  if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
2165
2331
  return null;
2166
2332
  }
2167
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2168
- const absolute = resolve2(importerDir, specifier);
2333
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2334
+ const absolute = resolve3(importerDir, specifier);
2169
2335
  const indexedPaths = getIndexedPaths(db);
2170
2336
  for (const candidate of candidateImportPaths(absolute)) {
2171
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2337
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2172
2338
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2173
2339
  return relativeCandidate;
2174
2340
  }
2175
2341
  }
2176
- return normalizePath(relative2(db.config.projectRoot, absolute));
2342
+ return normalizePath(relative3(db.config.projectRoot, absolute));
2177
2343
  }
2178
2344
  function resolvePythonImportPath(db, importerPath, specifier) {
2179
2345
  const indexedPaths = getIndexedPaths(db);
@@ -2183,16 +2349,16 @@ function resolvePythonImportPath(db, importerPath, specifier) {
2183
2349
  if (!match) return null;
2184
2350
  const dots = match[1].length;
2185
2351
  const remainder = match[2].replace(/^\./, "");
2186
- let baseDir = dirname2(join8(db.config.projectRoot, importerPath));
2352
+ let baseDir = dirname3(join8(db.config.projectRoot, importerPath));
2187
2353
  for (let i = 1; i < dots; i++) {
2188
- baseDir = dirname2(baseDir);
2354
+ baseDir = dirname3(baseDir);
2189
2355
  }
2190
- basePath = remainder ? resolve2(baseDir, remainder.replace(/\./g, "/")) : baseDir;
2356
+ basePath = remainder ? resolve3(baseDir, remainder.replace(/\./g, "/")) : baseDir;
2191
2357
  } else {
2192
- basePath = resolve2(db.config.projectRoot, specifier.replace(/\./g, "/"));
2358
+ basePath = resolve3(db.config.projectRoot, specifier.replace(/\./g, "/"));
2193
2359
  }
2194
2360
  for (const candidate of pythonCandidateImportPaths(basePath)) {
2195
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2361
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2196
2362
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2197
2363
  return relativeCandidate;
2198
2364
  }
@@ -2205,17 +2371,17 @@ function resolveRustImportPath(db, importerPath, specifier) {
2205
2371
  if (!normalizedSpecifier.startsWith("crate::") && !normalizedSpecifier.startsWith("self::") && !normalizedSpecifier.startsWith("super::")) {
2206
2372
  return null;
2207
2373
  }
2208
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2374
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2209
2375
  let basePath;
2210
2376
  if (normalizedSpecifier.startsWith("crate::")) {
2211
- 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, "/"));
2212
2378
  } else if (normalizedSpecifier.startsWith("self::")) {
2213
- basePath = resolve2(importerDir, normalizedSpecifier.slice("self::".length).replace(/::/g, "/"));
2379
+ basePath = resolve3(importerDir, normalizedSpecifier.slice("self::".length).replace(/::/g, "/"));
2214
2380
  } else {
2215
- basePath = resolve2(dirname2(importerDir), normalizedSpecifier.slice("super::".length).replace(/::/g, "/"));
2381
+ basePath = resolve3(dirname3(importerDir), normalizedSpecifier.slice("super::".length).replace(/::/g, "/"));
2216
2382
  }
2217
2383
  for (const candidate of rustCandidateImportPaths(basePath)) {
2218
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2384
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2219
2385
  if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
2220
2386
  return relativeCandidate;
2221
2387
  }
@@ -2223,10 +2389,10 @@ function resolveRustImportPath(db, importerPath, specifier) {
2223
2389
  return null;
2224
2390
  }
2225
2391
  function resolveRubyImportPath(db, importerPath, specifier) {
2226
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2227
- const absolute = resolve2(importerDir, specifier);
2392
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2393
+ const absolute = resolve3(importerDir, specifier);
2228
2394
  for (const candidate of rubyCandidateImportPaths(absolute)) {
2229
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2395
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2230
2396
  if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
2231
2397
  return relativeCandidate;
2232
2398
  }
@@ -2235,15 +2401,15 @@ function resolveRubyImportPath(db, importerPath, specifier) {
2235
2401
  }
2236
2402
  function resolveCLikeImportPath(db, importerPath, specifier) {
2237
2403
  const indexedPaths = getIndexedPaths(db);
2238
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2404
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2239
2405
  const candidates = [
2240
- resolve2(importerDir, specifier),
2241
- resolve2(db.config.projectRoot, specifier),
2242
- resolve2(db.config.projectRoot, "include", specifier),
2243
- 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)
2244
2410
  ];
2245
2411
  for (const candidate of candidates) {
2246
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2412
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2247
2413
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2248
2414
  return relativeCandidate;
2249
2415
  }
@@ -2261,10 +2427,10 @@ function resolveQualifiedImportPath(db, specifier, extensions) {
2261
2427
  if (exact) return exact;
2262
2428
  }
2263
2429
  for (const ext of extensions) {
2264
- const basenameMatch = [...indexedPaths].find((relativePath) => basename(relativePath) === `${basenameOnly}${ext}`);
2430
+ const basenameMatch = [...indexedPaths].find((relativePath) => basename2(relativePath) === `${basenameOnly}${ext}`);
2265
2431
  if (basenameMatch) return basenameMatch;
2266
2432
  }
2267
- 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));
2268
2434
  if (folderMatches.length === 1) {
2269
2435
  return folderMatches[0];
2270
2436
  }
@@ -2281,10 +2447,10 @@ function resolveDartImportPath(db, importerPath, specifier) {
2281
2447
  if (indexedPaths.has(candidate)) return candidate;
2282
2448
  return null;
2283
2449
  }
2284
- const importerDir = dirname2(join8(db.config.projectRoot, importerPath));
2285
- const absolute = resolve2(importerDir, specifier);
2450
+ const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
2451
+ const absolute = resolve3(importerDir, specifier);
2286
2452
  for (const candidate of dartCandidateImportPaths(absolute)) {
2287
- const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
2453
+ const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
2288
2454
  if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
2289
2455
  return relativeCandidate;
2290
2456
  }
@@ -2292,7 +2458,7 @@ function resolveDartImportPath(db, importerPath, specifier) {
2292
2458
  return null;
2293
2459
  }
2294
2460
  function pythonCandidateImportPaths(basePath) {
2295
- const ext = extname2(basePath);
2461
+ const ext = extname3(basePath);
2296
2462
  if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
2297
2463
  return [basePath];
2298
2464
  }
@@ -2304,7 +2470,7 @@ function pythonCandidateImportPaths(basePath) {
2304
2470
  ];
2305
2471
  }
2306
2472
  function rustCandidateImportPaths(basePath) {
2307
- const ext = extname2(basePath);
2473
+ const ext = extname3(basePath);
2308
2474
  if (RUST_SOURCE_EXTENSIONS.includes(ext)) {
2309
2475
  return [basePath];
2310
2476
  }
@@ -2314,7 +2480,7 @@ function rustCandidateImportPaths(basePath) {
2314
2480
  ];
2315
2481
  }
2316
2482
  function rubyCandidateImportPaths(basePath) {
2317
- const ext = extname2(basePath);
2483
+ const ext = extname3(basePath);
2318
2484
  if (RUBY_SOURCE_EXTENSIONS.includes(ext)) {
2319
2485
  return [basePath];
2320
2486
  }
@@ -2324,14 +2490,14 @@ function rubyCandidateImportPaths(basePath) {
2324
2490
  ];
2325
2491
  }
2326
2492
  function dartCandidateImportPaths(basePath) {
2327
- const ext = extname2(basePath);
2493
+ const ext = extname3(basePath);
2328
2494
  if (DART_SOURCE_EXTENSIONS.includes(ext)) {
2329
2495
  return [basePath];
2330
2496
  }
2331
2497
  return [`${basePath}.dart`, basePath];
2332
2498
  }
2333
2499
  function candidateImportPaths(absolute) {
2334
- const ext = extname2(absolute);
2500
+ const ext = extname3(absolute);
2335
2501
  const candidates = /* @__PURE__ */ new Set();
2336
2502
  if (ext) {
2337
2503
  candidates.add(absolute);
@@ -2374,34 +2540,34 @@ function normalizePath(path2) {
2374
2540
  return path2.replace(/\\/g, "/");
2375
2541
  }
2376
2542
  function isJavaScriptSourcePath(relativePath) {
2377
- return SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2543
+ return SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2378
2544
  }
2379
2545
  function isPythonSourcePath(relativePath) {
2380
- return PYTHON_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2546
+ return PYTHON_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2381
2547
  }
2382
2548
  function isJvmSourcePath(relativePath) {
2383
- return JVM_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2549
+ return JVM_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2384
2550
  }
2385
2551
  function isRustSourcePath(relativePath) {
2386
- return RUST_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2552
+ return RUST_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2387
2553
  }
2388
2554
  function isRubySourcePath(relativePath) {
2389
- return RUBY_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2555
+ return RUBY_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2390
2556
  }
2391
2557
  function isCLikeSourcePath(relativePath) {
2392
- return C_LIKE_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2558
+ return C_LIKE_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2393
2559
  }
2394
2560
  function isDotNetSourcePath(relativePath) {
2395
- return DOTNET_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2561
+ return DOTNET_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2396
2562
  }
2397
2563
  function isVisualBasicSourcePath(relativePath) {
2398
- return extname2(relativePath).toLowerCase() === ".vb";
2564
+ return extname3(relativePath).toLowerCase() === ".vb";
2399
2565
  }
2400
2566
  function isDartSourcePath(relativePath) {
2401
- return DART_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2567
+ return DART_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2402
2568
  }
2403
2569
  function isPhpSourcePath(relativePath) {
2404
- return PHP_SOURCE_EXTENSIONS.includes(extname2(relativePath).toLowerCase());
2570
+ return PHP_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
2405
2571
  }
2406
2572
  function extensionFamilyFor(relativePath) {
2407
2573
  if (isJvmSourcePath(relativePath)) return JVM_SOURCE_EXTENSIONS;
@@ -2425,7 +2591,7 @@ function getSourceText(db, relativePath) {
2425
2591
  cache.set(normalized, "");
2426
2592
  return "";
2427
2593
  }
2428
- const source = readFileSync3(fullPath, "utf-8");
2594
+ const source = readFileSync4(fullPath, "utf-8");
2429
2595
  cache.set(normalized, source);
2430
2596
  return source;
2431
2597
  }
@@ -4174,8 +4340,8 @@ function resolveDocumentCandidates(db, filePattern, opts) {
4174
4340
  }
4175
4341
  function scoreDocumentPath(relativePath, rawPattern) {
4176
4342
  const normalizedPath = normalizeLookupPath(relativePath);
4177
- const pathBase = basename2(normalizedPath);
4178
- const patternBase = basename2(rawPattern);
4343
+ const pathBase = basename3(normalizedPath);
4344
+ const patternBase = basename3(rawPattern);
4179
4345
  let score = 0;
4180
4346
  if (normalizedPath === rawPattern) score += 1200;
4181
4347
  if (normalizedPath.endsWith(`/${rawPattern}`)) score += 1100;
@@ -4221,7 +4387,7 @@ function symbols(db, filePattern) {
4221
4387
  }
4222
4388
 
4223
4389
  // src/queries/methods.ts
4224
- import { basename as basename3 } from "path";
4390
+ import { basename as basename4 } from "path";
4225
4391
  function methods(db, className) {
4226
4392
  const classMatch = findFirstSymbolMatch(db, className);
4227
4393
  if (!classMatch) {
@@ -4230,7 +4396,7 @@ function methods(db, className) {
4230
4396
  const ownerName = leafName(classMatch.symbol);
4231
4397
  const definitions = getDefinitionsForFile(db, classMatch.relativePath).filter((definition) => isCallableSymbol(definition.symbol));
4232
4398
  const directMethods = definitions.filter((definition) => definition.parentTypeName === ownerName || definition.symbol.includes(ownerName));
4233
- 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>")) : [];
4234
4400
  return fileScopedMethods.map((definition) => ({
4235
4401
  startLine: definition.startLine,
4236
4402
  endLine: definition.endLine,
@@ -4614,11 +4780,11 @@ function isWorkerEntrySurface(path2) {
4614
4780
  function isStructuralEntrySurface(path2) {
4615
4781
  const normalized = normalizePath2(path2);
4616
4782
  const segments = normalized.split("/");
4617
- const basename5 = segments[segments.length - 1] ?? normalized;
4618
- 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") {
4619
4785
  return true;
4620
4786
  }
4621
- if (basename5 === "index.ts" || basename5 === "index.js") {
4787
+ if (basename6 === "index.ts" || basename6 === "index.js") {
4622
4788
  return segments.length <= 2;
4623
4789
  }
4624
4790
  return normalized.endsWith("/mod.rs") || normalized.endsWith("/__init__.py");
@@ -6121,8 +6287,8 @@ function similarChains(db, opts = {}) {
6121
6287
  }
6122
6288
  const structuralNames = ["index.ts", "index.js", "cli.ts", "main.ts", "health.ts", "health.js"];
6123
6289
  for (const node of nodeFreq.keys()) {
6124
- const basename5 = node.split("/").pop() ?? "";
6125
- if (structuralNames.includes(basename5)) infraNodes.add(node);
6290
+ const basename6 = node.split("/").pop() ?? "";
6291
+ if (structuralNames.includes(basename6)) infraNodes.add(node);
6126
6292
  }
6127
6293
  const filteredChains = [];
6128
6294
  for (const chain of rawChains) {
@@ -6296,8 +6462,8 @@ function extractCandidates(db, opts = {}) {
6296
6462
  const results = [];
6297
6463
  for (const sym of symbols2) {
6298
6464
  if (db.isIgnored(sym.relativePath)) continue;
6299
- const basename5 = sym.relativePath.split("/").pop() ?? "";
6300
- if (basename5.includes("types")) continue;
6465
+ const basename6 = sym.relativePath.split("/").pop() ?? "";
6466
+ if (basename6.includes("types")) continue;
6301
6467
  const calleeChunks = getCalleeRowsForSymbol(db, sym);
6302
6468
  const calleeSet = new Set(calleeChunks.map((c) => c.symbol));
6303
6469
  if (calleeSet.size < minCallees) continue;
@@ -6809,21 +6975,21 @@ function isLikelyTypeOnlyDep(dep) {
6809
6975
  function shouldSkipDriftFile(filePath) {
6810
6976
  return isStructuralRole(path.basename(filePath)) || isTestLikePath(filePath);
6811
6977
  }
6812
- function isStructuralRole(basename5) {
6813
- if (basename5 === "index.ts" || basename5 === "index.js") return true;
6814
- if (basename5 === "cli.ts" || basename5 === "main.ts" || basename5 === "main.rs") return true;
6815
- if (basename5.includes("worker.") || basename5.includes("postinstall.")) return true;
6816
- 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;
6817
6983
  return false;
6818
6984
  }
6819
6985
  function isTestLikePath(filePath) {
6820
6986
  const normalized = filePath.replace(/\\/g, "/");
6821
- const basename5 = path.basename(normalized);
6822
- 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);
6823
6989
  }
6824
6990
 
6825
6991
  // src/queries/wrapper-candidates.ts
6826
- import { basename as basename4, extname as extname3 } from "path";
6992
+ import { basename as basename5, extname as extname4 } from "path";
6827
6993
  function wrapperCandidates(db, opts) {
6828
6994
  const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
6829
6995
  const reverseFanIn = buildReverseFileFanIn(buildFileDepGraph(db, scope));
@@ -6831,10 +6997,10 @@ function wrapperCandidates(db, opts) {
6831
6997
  const results = [];
6832
6998
  for (const symbol of symbols2) {
6833
6999
  if (db.isIgnored(symbol.relativePath) || !isFunctionLikeSymbol(symbol.symbol)) continue;
6834
- const symbolStem = basename4(symbol.relativePath, extname3(symbol.relativePath));
7000
+ const symbolStem = basename5(symbol.relativePath, extname4(symbol.relativePath));
6835
7001
  const callerRows = dedupeRows(
6836
7002
  getCallerRowsForSymbol(db, symbol, { limit: 200 }).filter((row) => row.file !== symbol.relativePath)
6837
- ).filter((row) => basename4(row.file, extname3(row.file)) !== symbolStem);
7003
+ ).filter((row) => basename5(row.file, extname4(row.file)) !== symbolStem);
6838
7004
  if (callerRows.length !== 1) continue;
6839
7005
  const caller = callerRows[0];
6840
7006
  const callerDefinition = getDefinitionsForFile(db, caller.file).find((definition) => definition.symbol === caller.symbol);
@@ -6856,7 +7022,7 @@ function wrapperCandidates(db, opts) {
6856
7022
  endLine: symbol.endLine,
6857
7023
  loc: definitionLoc2(symbol),
6858
7024
  singleCaller: caller.symbol,
6859
- singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) : basename4(caller.file),
7025
+ singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) : basename5(caller.file),
6860
7026
  callerFanIn
6861
7027
  });
6862
7028
  }
@@ -6911,11 +7077,11 @@ function fallbackCallerFanIn(db, reverseFanIn, callerFile) {
6911
7077
  if (direct > 0) {
6912
7078
  return direct;
6913
7079
  }
6914
- const stem = basename4(callerFile, extname3(callerFile));
7080
+ const stem = basename5(callerFile, extname4(callerFile));
6915
7081
  let best = 0;
6916
7082
  for (const [file, fanIn2] of reverseFanIn) {
6917
7083
  if (file === callerFile) continue;
6918
- if (basename4(file, extname3(file)) !== stem) continue;
7084
+ if (basename5(file, extname4(file)) !== stem) continue;
6919
7085
  if (fanIn2 > best) {
6920
7086
  best = fanIn2;
6921
7087
  }
@@ -6996,8 +7162,8 @@ function getFilesWithFunctions(db, scope) {
6996
7162
  return new Set(getScopedDefinitions4(db, scope).filter((definition) => definition.isFunctionLike).map((definition) => definition.relativePath));
6997
7163
  }
6998
7164
  function isTrueStaleAbstraction(row, filesWithFunctions) {
6999
- const basename5 = row.file.split("/").pop() ?? "";
7000
- 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/");
7001
7167
  if (isTypeFile && row.consumers > 0) {
7002
7168
  return false;
7003
7169
  }
@@ -7395,7 +7561,7 @@ function convergence(db, symbolPatternA, symbolPatternB) {
7395
7561
  }
7396
7562
 
7397
7563
  // src/queries/code.ts
7398
- import { readFileSync as readFileSync4 } from "fs";
7564
+ import { readFileSync as readFileSync5 } from "fs";
7399
7565
  import { join as join9 } from "path";
7400
7566
  function code(db, symbolPattern, opts = {}) {
7401
7567
  const { context = 0 } = opts;
@@ -7412,7 +7578,7 @@ function code(db, symbolPattern, opts = {}) {
7412
7578
  const filePath = join9(db.config.projectRoot, match.relativePath);
7413
7579
  let fileContent;
7414
7580
  try {
7415
- fileContent = readFileSync4(filePath, "utf-8");
7581
+ fileContent = readFileSync5(filePath, "utf-8");
7416
7582
  } catch {
7417
7583
  return null;
7418
7584
  }
@@ -7442,7 +7608,7 @@ function readFileRange(db, filePath, startLine, endLine, context) {
7442
7608
  const fullPath = join9(db.config.projectRoot, doc.relative_path);
7443
7609
  let fileContent;
7444
7610
  try {
7445
- fileContent = readFileSync4(fullPath, "utf-8");
7611
+ fileContent = readFileSync5(fullPath, "utf-8");
7446
7612
  } catch {
7447
7613
  return null;
7448
7614
  }
@@ -7462,7 +7628,7 @@ function readFileRange(db, filePath, startLine, endLine, context) {
7462
7628
  }
7463
7629
 
7464
7630
  // src/queries/complexity.ts
7465
- import { readFileSync as readFileSync5 } from "fs";
7631
+ import { readFileSync as readFileSync6 } from "fs";
7466
7632
  import { join as join10 } from "path";
7467
7633
  function complexity(db, symbolPattern) {
7468
7634
  const match = findFirstSymbolMatch(db, symbolPattern);
@@ -7475,7 +7641,7 @@ function complexity(db, symbolPattern) {
7475
7641
  const filePath = join10(db.config.projectRoot, match.relativePath);
7476
7642
  let source = "";
7477
7643
  try {
7478
- const lines = readFileSync5(filePath, "utf-8").split("\n");
7644
+ const lines = readFileSync6(filePath, "utf-8").split("\n");
7479
7645
  source = lines.slice(match.startLine, match.endLine + 1).join("\n");
7480
7646
  } catch {
7481
7647
  }
@@ -8047,7 +8213,7 @@ import {
8047
8213
  readlinkSync,
8048
8214
  unlinkSync
8049
8215
  } from "fs";
8050
- 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";
8051
8217
  import { homedir as homedir2, platform as platform3 } from "os";
8052
8218
  import { fileURLToPath } from "url";
8053
8219
  var IS_WINDOWS3 = platform3() === "win32";
@@ -8062,7 +8228,7 @@ function installSkills(opts = {}) {
8062
8228
  const log = opts.quiet ? () => {
8063
8229
  } : console.log;
8064
8230
  const thisFile = fileURLToPath(import.meta.url);
8065
- const skillsSource = resolve3(dirname3(thisFile), "..", "skills");
8231
+ const skillsSource = resolve4(dirname4(thisFile), "..", "skills");
8066
8232
  const targets = [
8067
8233
  join11(homedir2(), ".claude", "skills"),
8068
8234
  join11(homedir2(), ".codex", "skills")
@@ -8073,7 +8239,7 @@ function installSkills(opts = {}) {
8073
8239
  alreadyLinked: []
8074
8240
  };
8075
8241
  for (const targetDir of targets) {
8076
- const parentDir = dirname3(targetDir);
8242
+ const parentDir = dirname4(targetDir);
8077
8243
  if (!existsSync9(parentDir)) {
8078
8244
  continue;
8079
8245
  }
@@ -8089,7 +8255,7 @@ function installSkills(opts = {}) {
8089
8255
  if (existsSync9(target)) {
8090
8256
  try {
8091
8257
  const existing = readlinkSync(target);
8092
- if (resolve3(existing) === resolve3(source)) {
8258
+ if (resolve4(existing) === resolve4(source)) {
8093
8259
  result.alreadyLinked.push(`${toolName}/${skill}`);
8094
8260
  log(` ok: ${skill} \u2192 ${toolName} (already linked)`);
8095
8261
  continue;
@@ -8216,11 +8382,15 @@ var queries = {
8216
8382
  program.name("scip-query").description("Language-agnostic code intelligence CLI powered by SCIP indexes").version(cliVersion);
8217
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) => {
8218
8384
  const projectRoot = resolveProjectRoot();
8385
+ const config = loadProjectConfig(projectRoot);
8386
+ const paths = resolveIndexPaths(projectRoot, config);
8219
8387
  try {
8220
8388
  const result = await reindex({
8221
8389
  projectRoot,
8222
- languages: opts.language.length > 0 ? opts.language : void 0,
8223
- 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
8224
8394
  });
8225
8395
  console.log(`Indexed ${result.languages.join(", ")} in ${(result.durationMs / 1e3).toFixed(1)}s`);
8226
8396
  } catch (err) {
@@ -9205,6 +9375,7 @@ program.command("watch").description("Watch for file changes and reindex automat
9205
9375
  const watcher = new Watcher({
9206
9376
  projectRoot,
9207
9377
  config,
9378
+ languages: config.languages,
9208
9379
  onStatus: (status) => {
9209
9380
  process.stdout.write(`\r\x1B[K${formatStatus(status)}`);
9210
9381
  },