@wrongstack/tools 0.307.0 → 0.307.1

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.
@@ -34,6 +34,8 @@ export interface ImpactAnalysisOutput {
34
34
  affectedTestFiles: string[];
35
35
  callSites: ImpactCallSite[];
36
36
  recommendedActionPlan: string[];
37
+ /** False when the symbol could not be resolved in the index. */
38
+ symbolFound?: boolean;
37
39
  error?: string | undefined;
38
40
  }
39
41
  export declare const codebaseImpactAnalysisTool: Tool<ImpactAnalysisInput, ImpactAnalysisOutput>;
@@ -3592,10 +3592,16 @@ function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
3592
3592
  }
3593
3593
  return { fileNodes, symToFile, fileStats, ensureFileNode };
3594
3594
  }
3595
- function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
3595
+ function buildSymbolGraphNodes(symById, relatedIds, localFiles, packageOf) {
3596
+ const local = new Set(
3597
+ [...typeof localFiles === "string" ? [localFiles] : localFiles].map(
3598
+ (file) => file.replace(/\\/g, "/")
3599
+ )
3600
+ );
3601
+ const isLocal = (file) => local.has(file.replace(/\\/g, "/"));
3596
3602
  return [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
3597
- const aExternal = a.file === fileFilter ? 0 : 1;
3598
- const bExternal = b.file === fileFilter ? 0 : 1;
3603
+ const aExternal = isLocal(a.file) ? 0 : 1;
3604
+ const bExternal = isLocal(b.file) ? 0 : 1;
3599
3605
  return aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id;
3600
3606
  }).map((s) => ({
3601
3607
  id: `sym:${s.id}`,
@@ -3609,7 +3615,7 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
3609
3615
  line: s.line,
3610
3616
  signature: s.signature,
3611
3617
  scope: s.scope,
3612
- external: s.file !== fileFilter
3618
+ external: !isLocal(s.file)
3613
3619
  }));
3614
3620
  }
3615
3621
  function addWeightedEdge(edgeMap, source, target, callType, weight) {
@@ -3644,6 +3650,56 @@ function materializeWeightedEdges(edgeMap, idPrefix) {
3644
3650
  return edges;
3645
3651
  }
3646
3652
 
3653
+ // src/codebase-index/writer-helpers.ts
3654
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
3655
+ function escapeLike(value) {
3656
+ return value.replace(/[\\%_]/g, (char) => `\\${char}`);
3657
+ }
3658
+ function posixIndexPath(file) {
3659
+ return file.replace(/\\/g, "/").replace(/^\.\//, "");
3660
+ }
3661
+ function indexedFileMatchSql(column = "file") {
3662
+ return `(${column} = ? OR replace(${column}, '\\', '/') = ? OR replace(${column}, '\\', '/') LIKE ? ESCAPE '\\')`;
3663
+ }
3664
+ function indexedFileMatchArgs(file) {
3665
+ const posix4 = posixIndexPath(file.trim());
3666
+ return [file, posix4, `%/${escapeLike(posix4)}`];
3667
+ }
3668
+ function matchesIndexedPackageFilter(storedFile, packageLabel, filter) {
3669
+ if (packageLabel === filter) return true;
3670
+ const posixFile = posixIndexPath(storedFile);
3671
+ const posixFilter = posixIndexPath(filter.trim());
3672
+ if (!posixFilter) return false;
3673
+ return posixFile === posixFilter || posixFile.endsWith(`/${posixFilter}`) || posixFile.includes(`/${posixFilter}/`);
3674
+ }
3675
+ function assignRefsToSymbols(refs, symbols) {
3676
+ if (refs.length === 0 || symbols.length === 0) return [];
3677
+ const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
3678
+ const seen = /* @__PURE__ */ new Set();
3679
+ const assigned = [];
3680
+ for (const ref of refs) {
3681
+ let owner;
3682
+ for (const symbol of ordered) {
3683
+ if (symbol.line > ref.line) break;
3684
+ owner = symbol;
3685
+ }
3686
+ if (!owner && ref.callType === "import") owner = ordered[0];
3687
+ if (!owner || owner.id <= 0) continue;
3688
+ const key = `${owner.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
3689
+ if (seen.has(key)) continue;
3690
+ seen.add(key);
3691
+ assigned.push({ ...ref, fromId: owner.id });
3692
+ }
3693
+ return assigned;
3694
+ }
3695
+ function resolveIndexDir(projectRoot, override) {
3696
+ return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
3697
+ }
3698
+ function codebaseIndexDirOverride(ctx) {
3699
+ const v = ctx.meta?.["codebaseIndexDir"];
3700
+ return typeof v === "string" ? v : void 0;
3701
+ }
3702
+
3647
3703
  // src/codebase-index/writer-ref-mapper.ts
3648
3704
  function mapWriterRefRow(row) {
3649
3705
  return {
@@ -3699,10 +3755,23 @@ function mapCallSiteRow(row) {
3699
3755
  line: row.ref_line
3700
3756
  };
3701
3757
  }
3758
+ function resolveIndexedFiles(stmt, file) {
3759
+ const rows = stmt(
3760
+ `SELECT DISTINCT file FROM symbols WHERE ${indexedFileMatchSql("file")} ORDER BY length(file), file`
3761
+ ).all(...indexedFileMatchArgs(file));
3762
+ return rows.map((row) => row.file);
3763
+ }
3702
3764
  function resolveSymbolIds(stmt, symbolName, file) {
3703
- const baseSql = file ? `SELECT id FROM symbols WHERE name = ? AND file = ? ORDER BY id` : `SELECT id FROM symbols WHERE name = ? ORDER BY id`;
3704
- const args = file ? [symbolName, file] : [symbolName];
3705
- const rows = stmt(baseSql).all(...args);
3765
+ if (!file) {
3766
+ const rows2 = stmt("SELECT id FROM symbols WHERE name = ? ORDER BY id").all(symbolName);
3767
+ return rows2.map((r) => r.id);
3768
+ }
3769
+ const indexedFiles = resolveIndexedFiles(stmt, file);
3770
+ if (indexedFiles.length === 0) return [];
3771
+ const placeholders = indexedFiles.map(() => "?").join(",");
3772
+ const rows = stmt(
3773
+ `SELECT id FROM symbols WHERE name = ? AND file IN (${placeholders}) ORDER BY id`
3774
+ ).all(symbolName, ...indexedFiles);
3706
3775
  return rows.map((r) => r.id);
3707
3776
  }
3708
3777
  function findIncomingCallsByName(stmt, symbolName, file, limit) {
@@ -4024,7 +4093,7 @@ function getFileGraphWithStatement(stmt, packageFilter) {
4024
4093
  const allFiles = stmt("SELECT DISTINCT file FROM symbols").all();
4025
4094
  const packageOf = readPackageLabeller(stmt);
4026
4095
  const langOf = (file) => detectLang(file) ?? "other";
4027
- const pkgFilePaths = allFiles.filter((f) => packageOf(f.file) === packageFilter).map((f) => f.file);
4096
+ const pkgFilePaths = allFiles.filter((f) => matchesIndexedPackageFilter(f.file, packageOf(f.file), packageFilter)).map((f) => f.file);
4028
4097
  const localFiles = new Set(pkgFilePaths);
4029
4098
  if (localFiles.size === 0) return { nodes: [], edges: [] };
4030
4099
  const filePlaceholders = [...localFiles].map(() => "?").join(",");
@@ -4100,9 +4169,12 @@ function getFileGraphWithStatement(stmt, packageFilter) {
4100
4169
  return { nodes: [...fileNodes.values()], edges };
4101
4170
  }
4102
4171
  function getSymbolGraphWithStatement(stmt, fileFilter) {
4172
+ const indexedFiles = resolveIndexedFiles(stmt, fileFilter);
4173
+ if (indexedFiles.length === 0) return { nodes: [], edges: [] };
4174
+ const filePlaceholders = indexedFiles.map(() => "?").join(",");
4103
4175
  const syms = stmt(
4104
- "SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id"
4105
- ).all(fileFilter);
4176
+ `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY line, id`
4177
+ ).all(...indexedFiles);
4106
4178
  if (syms.length === 0) return { nodes: [], edges: [] };
4107
4179
  const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
4108
4180
  const relatedIds = new Set(syms.map((symbol) => symbol.id));
@@ -4112,16 +4184,16 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
4112
4184
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
4113
4185
  FROM refs r
4114
4186
  JOIN symbols s ON s.id = r.from_id
4115
- WHERE s.file = ?
4187
+ WHERE s.file IN (${filePlaceholders})
4116
4188
  UNION
4117
4189
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
4118
4190
  FROM refs r
4119
4191
  JOIN symbols s ON s.id = r.to_id
4120
- WHERE s.file = ?
4192
+ WHERE s.file IN (${filePlaceholders})
4121
4193
  )
4122
4194
  WHERE to_id IS NOT NULL
4123
4195
  GROUP BY from_id, to_id, call_type`
4124
- ).all(fileFilter, fileFilter);
4196
+ ).all(...indexedFiles, ...indexedFiles);
4125
4197
  const edgeMap = /* @__PURE__ */ new Map();
4126
4198
  for (const r of refRows) {
4127
4199
  if (r.to_id == null) continue;
@@ -4140,43 +4212,15 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
4140
4212
  ).all(...missingIds);
4141
4213
  for (const s of extras) symById.set(s.id, s);
4142
4214
  }
4143
- const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter, readPackageLabeller(stmt));
4215
+ const nodes = buildSymbolGraphNodes(
4216
+ symById,
4217
+ relatedIds,
4218
+ new Set(syms.map((symbol) => symbol.file)),
4219
+ readPackageLabeller(stmt)
4220
+ );
4144
4221
  return { nodes, edges };
4145
4222
  }
4146
4223
 
4147
- // src/codebase-index/writer-helpers.ts
4148
- import { resolveWstackPaths } from "@wrongstack/core/utils";
4149
- function escapeLike(value) {
4150
- return value.replace(/[\\%_]/g, (char) => `\\${char}`);
4151
- }
4152
- function assignRefsToSymbols(refs, symbols) {
4153
- if (refs.length === 0 || symbols.length === 0) return [];
4154
- const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
4155
- const seen = /* @__PURE__ */ new Set();
4156
- const assigned = [];
4157
- for (const ref of refs) {
4158
- let owner;
4159
- for (const symbol of ordered) {
4160
- if (symbol.line > ref.line) break;
4161
- owner = symbol;
4162
- }
4163
- if (!owner && ref.callType === "import") owner = ordered[0];
4164
- if (!owner || owner.id <= 0) continue;
4165
- const key = `${owner.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
4166
- if (seen.has(key)) continue;
4167
- seen.add(key);
4168
- assigned.push({ ...ref, fromId: owner.id });
4169
- }
4170
- return assigned;
4171
- }
4172
- function resolveIndexDir(projectRoot, override) {
4173
- return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
4174
- }
4175
- function codebaseIndexDirOverride(ctx) {
4176
- const v = ctx.meta?.["codebaseIndexDir"];
4177
- return typeof v === "string" ? v : void 0;
4178
- }
4179
-
4180
4224
  // src/codebase-index/vector-search.ts
4181
4225
  var RRF_K = 60;
4182
4226
  var VECTOR_DIMENSIONS = 384;
@@ -9265,6 +9309,9 @@ var codebaseImpactAnalysisTool = {
9265
9309
  const limit = 200;
9266
9310
  const transitive = input.transitive ?? true;
9267
9311
  let rawSites = [];
9312
+ let symbolFound = true;
9313
+ let indexUnavailable = false;
9314
+ let indexError;
9268
9315
  try {
9269
9316
  const serviced = await incomingCallsService2({
9270
9317
  projectRoot,
@@ -9275,7 +9322,43 @@ var codebaseImpactAnalysisTool = {
9275
9322
  transitive
9276
9323
  });
9277
9324
  rawSites = serviced.calls;
9278
- } catch {
9325
+ symbolFound = serviced.symbolFound;
9326
+ } catch (err) {
9327
+ symbolFound = false;
9328
+ indexUnavailable = true;
9329
+ indexError = toErrorMessage4(err);
9330
+ }
9331
+ if (indexUnavailable) {
9332
+ const detail = indexError ? ` (${indexError})` : "";
9333
+ const reason = `Index query failed for '${input.symbol}'. Run codebase-index, then retry.${detail}`;
9334
+ return {
9335
+ status: "error",
9336
+ symbol: input.symbol,
9337
+ riskLevel: "low",
9338
+ summary: reason,
9339
+ totalCallSites: 0,
9340
+ affectedProductionFiles: [],
9341
+ affectedTestFiles: [],
9342
+ callSites: [],
9343
+ recommendedActionPlan: [reason],
9344
+ symbolFound: false,
9345
+ error: reason
9346
+ };
9347
+ }
9348
+ if (!symbolFound) {
9349
+ const reason = `Symbol '${input.symbol}' was not found in the index` + (input.file ? ` for file filter '${input.file}'` : "") + ". Use codebase-search to verify the name.";
9350
+ return {
9351
+ status: "ok",
9352
+ symbol: input.symbol,
9353
+ riskLevel: "low",
9354
+ summary: reason,
9355
+ totalCallSites: 0,
9356
+ affectedProductionFiles: [],
9357
+ affectedTestFiles: [],
9358
+ callSites: [],
9359
+ recommendedActionPlan: [reason],
9360
+ symbolFound: false
9361
+ };
9279
9362
  }
9280
9363
  const prodFilesSet = /* @__PURE__ */ new Set();
9281
9364
  const testFilesSet = /* @__PURE__ */ new Set();
@@ -9331,7 +9414,8 @@ var codebaseImpactAnalysisTool = {
9331
9414
  affectedProductionFiles: prodFiles,
9332
9415
  affectedTestFiles: testFiles,
9333
9416
  callSites,
9334
- recommendedActionPlan
9417
+ recommendedActionPlan,
9418
+ symbolFound: true
9335
9419
  };
9336
9420
  } catch (err) {
9337
9421
  return {
@@ -9344,6 +9428,7 @@ var codebaseImpactAnalysisTool = {
9344
9428
  affectedTestFiles: [],
9345
9429
  callSites: [],
9346
9430
  recommendedActionPlan: [],
9431
+ symbolFound: false,
9347
9432
  error: toErrorMessage4(err)
9348
9433
  };
9349
9434
  }
@@ -4362,10 +4362,16 @@ function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
4362
4362
  }
4363
4363
  return { fileNodes, symToFile, fileStats, ensureFileNode };
4364
4364
  }
4365
- function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
4365
+ function buildSymbolGraphNodes(symById, relatedIds, localFiles, packageOf) {
4366
+ const local = new Set(
4367
+ [...typeof localFiles === "string" ? [localFiles] : localFiles].map(
4368
+ (file) => file.replace(/\\/g, "/")
4369
+ )
4370
+ );
4371
+ const isLocal = (file) => local.has(file.replace(/\\/g, "/"));
4366
4372
  return [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
4367
- const aExternal = a.file === fileFilter ? 0 : 1;
4368
- const bExternal = b.file === fileFilter ? 0 : 1;
4373
+ const aExternal = isLocal(a.file) ? 0 : 1;
4374
+ const bExternal = isLocal(b.file) ? 0 : 1;
4369
4375
  return aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id;
4370
4376
  }).map((s) => ({
4371
4377
  id: `sym:${s.id}`,
@@ -4379,7 +4385,7 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
4379
4385
  line: s.line,
4380
4386
  signature: s.signature,
4381
4387
  scope: s.scope,
4382
- external: s.file !== fileFilter
4388
+ external: !isLocal(s.file)
4383
4389
  }));
4384
4390
  }
4385
4391
  function addWeightedEdge(edgeMap, source, target, callType, weight) {
@@ -4414,6 +4420,52 @@ function materializeWeightedEdges(edgeMap, idPrefix) {
4414
4420
  return edges;
4415
4421
  }
4416
4422
 
4423
+ // src/codebase-index/writer-helpers.ts
4424
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
4425
+ function escapeLike(value) {
4426
+ return value.replace(/[\\%_]/g, (char) => `\\${char}`);
4427
+ }
4428
+ function posixIndexPath(file) {
4429
+ return file.replace(/\\/g, "/").replace(/^\.\//, "");
4430
+ }
4431
+ function indexedFileMatchSql(column = "file") {
4432
+ return `(${column} = ? OR replace(${column}, '\\', '/') = ? OR replace(${column}, '\\', '/') LIKE ? ESCAPE '\\')`;
4433
+ }
4434
+ function indexedFileMatchArgs(file) {
4435
+ const posix4 = posixIndexPath(file.trim());
4436
+ return [file, posix4, `%/${escapeLike(posix4)}`];
4437
+ }
4438
+ function matchesIndexedPackageFilter(storedFile, packageLabel, filter) {
4439
+ if (packageLabel === filter) return true;
4440
+ const posixFile = posixIndexPath(storedFile);
4441
+ const posixFilter = posixIndexPath(filter.trim());
4442
+ if (!posixFilter) return false;
4443
+ return posixFile === posixFilter || posixFile.endsWith(`/${posixFilter}`) || posixFile.includes(`/${posixFilter}/`);
4444
+ }
4445
+ function assignRefsToSymbols(refs, symbols) {
4446
+ if (refs.length === 0 || symbols.length === 0) return [];
4447
+ const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
4448
+ const seen = /* @__PURE__ */ new Set();
4449
+ const assigned = [];
4450
+ for (const ref of refs) {
4451
+ let owner;
4452
+ for (const symbol of ordered) {
4453
+ if (symbol.line > ref.line) break;
4454
+ owner = symbol;
4455
+ }
4456
+ if (!owner && ref.callType === "import") owner = ordered[0];
4457
+ if (!owner || owner.id <= 0) continue;
4458
+ const key = `${owner.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
4459
+ if (seen.has(key)) continue;
4460
+ seen.add(key);
4461
+ assigned.push({ ...ref, fromId: owner.id });
4462
+ }
4463
+ return assigned;
4464
+ }
4465
+ function resolveIndexDir(projectRoot2, override) {
4466
+ return override ?? resolveWstackPaths({ projectRoot: projectRoot2 }).projectCodebaseIndex;
4467
+ }
4468
+
4417
4469
  // src/codebase-index/writer-ref-mapper.ts
4418
4470
  function mapWriterRefRow(row) {
4419
4471
  return {
@@ -4469,10 +4521,23 @@ function mapCallSiteRow(row) {
4469
4521
  line: row.ref_line
4470
4522
  };
4471
4523
  }
4524
+ function resolveIndexedFiles(stmt, file) {
4525
+ const rows = stmt(
4526
+ `SELECT DISTINCT file FROM symbols WHERE ${indexedFileMatchSql("file")} ORDER BY length(file), file`
4527
+ ).all(...indexedFileMatchArgs(file));
4528
+ return rows.map((row) => row.file);
4529
+ }
4472
4530
  function resolveSymbolIds(stmt, symbolName, file) {
4473
- const baseSql = file ? `SELECT id FROM symbols WHERE name = ? AND file = ? ORDER BY id` : `SELECT id FROM symbols WHERE name = ? ORDER BY id`;
4474
- const args = file ? [symbolName, file] : [symbolName];
4475
- const rows = stmt(baseSql).all(...args);
4531
+ if (!file) {
4532
+ const rows2 = stmt("SELECT id FROM symbols WHERE name = ? ORDER BY id").all(symbolName);
4533
+ return rows2.map((r) => r.id);
4534
+ }
4535
+ const indexedFiles = resolveIndexedFiles(stmt, file);
4536
+ if (indexedFiles.length === 0) return [];
4537
+ const placeholders = indexedFiles.map(() => "?").join(",");
4538
+ const rows = stmt(
4539
+ `SELECT id FROM symbols WHERE name = ? AND file IN (${placeholders}) ORDER BY id`
4540
+ ).all(symbolName, ...indexedFiles);
4476
4541
  return rows.map((r) => r.id);
4477
4542
  }
4478
4543
  function findIncomingCallsByName(stmt, symbolName, file, limit) {
@@ -4794,7 +4859,7 @@ function getFileGraphWithStatement(stmt, packageFilter) {
4794
4859
  const allFiles = stmt("SELECT DISTINCT file FROM symbols").all();
4795
4860
  const packageOf = readPackageLabeller(stmt);
4796
4861
  const langOf = (file) => detectLang(file) ?? "other";
4797
- const pkgFilePaths = allFiles.filter((f) => packageOf(f.file) === packageFilter).map((f) => f.file);
4862
+ const pkgFilePaths = allFiles.filter((f) => matchesIndexedPackageFilter(f.file, packageOf(f.file), packageFilter)).map((f) => f.file);
4798
4863
  const localFiles = new Set(pkgFilePaths);
4799
4864
  if (localFiles.size === 0) return { nodes: [], edges: [] };
4800
4865
  const filePlaceholders = [...localFiles].map(() => "?").join(",");
@@ -4870,9 +4935,12 @@ function getFileGraphWithStatement(stmt, packageFilter) {
4870
4935
  return { nodes: [...fileNodes.values()], edges };
4871
4936
  }
4872
4937
  function getSymbolGraphWithStatement(stmt, fileFilter) {
4938
+ const indexedFiles = resolveIndexedFiles(stmt, fileFilter);
4939
+ if (indexedFiles.length === 0) return { nodes: [], edges: [] };
4940
+ const filePlaceholders = indexedFiles.map(() => "?").join(",");
4873
4941
  const syms = stmt(
4874
- "SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id"
4875
- ).all(fileFilter);
4942
+ `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY line, id`
4943
+ ).all(...indexedFiles);
4876
4944
  if (syms.length === 0) return { nodes: [], edges: [] };
4877
4945
  const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
4878
4946
  const relatedIds = new Set(syms.map((symbol) => symbol.id));
@@ -4882,16 +4950,16 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
4882
4950
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
4883
4951
  FROM refs r
4884
4952
  JOIN symbols s ON s.id = r.from_id
4885
- WHERE s.file = ?
4953
+ WHERE s.file IN (${filePlaceholders})
4886
4954
  UNION
4887
4955
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
4888
4956
  FROM refs r
4889
4957
  JOIN symbols s ON s.id = r.to_id
4890
- WHERE s.file = ?
4958
+ WHERE s.file IN (${filePlaceholders})
4891
4959
  )
4892
4960
  WHERE to_id IS NOT NULL
4893
4961
  GROUP BY from_id, to_id, call_type`
4894
- ).all(fileFilter, fileFilter);
4962
+ ).all(...indexedFiles, ...indexedFiles);
4895
4963
  const edgeMap = /* @__PURE__ */ new Map();
4896
4964
  for (const r of refRows) {
4897
4965
  if (r.to_id == null) continue;
@@ -4910,39 +4978,15 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
4910
4978
  ).all(...missingIds);
4911
4979
  for (const s of extras) symById.set(s.id, s);
4912
4980
  }
4913
- const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter, readPackageLabeller(stmt));
4981
+ const nodes = buildSymbolGraphNodes(
4982
+ symById,
4983
+ relatedIds,
4984
+ new Set(syms.map((symbol) => symbol.file)),
4985
+ readPackageLabeller(stmt)
4986
+ );
4914
4987
  return { nodes, edges };
4915
4988
  }
4916
4989
 
4917
- // src/codebase-index/writer-helpers.ts
4918
- import { resolveWstackPaths } from "@wrongstack/core/utils";
4919
- function escapeLike(value) {
4920
- return value.replace(/[\\%_]/g, (char) => `\\${char}`);
4921
- }
4922
- function assignRefsToSymbols(refs, symbols) {
4923
- if (refs.length === 0 || symbols.length === 0) return [];
4924
- const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
4925
- const seen = /* @__PURE__ */ new Set();
4926
- const assigned = [];
4927
- for (const ref of refs) {
4928
- let owner;
4929
- for (const symbol of ordered) {
4930
- if (symbol.line > ref.line) break;
4931
- owner = symbol;
4932
- }
4933
- if (!owner && ref.callType === "import") owner = ordered[0];
4934
- if (!owner || owner.id <= 0) continue;
4935
- const key = `${owner.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
4936
- if (seen.has(key)) continue;
4937
- seen.add(key);
4938
- assigned.push({ ...ref, fromId: owner.id });
4939
- }
4940
- return assigned;
4941
- }
4942
- function resolveIndexDir(projectRoot2, override) {
4943
- return override ?? resolveWstackPaths({ projectRoot: projectRoot2 }).projectCodebaseIndex;
4944
- }
4945
-
4946
4990
  // src/codebase-index/vector-search.ts
4947
4991
  var RRF_K = 60;
4948
4992
  var VECTOR_DIMENSIONS = 384;
@@ -4347,10 +4347,16 @@ function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
4347
4347
  }
4348
4348
  return { fileNodes, symToFile, fileStats, ensureFileNode };
4349
4349
  }
4350
- function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
4350
+ function buildSymbolGraphNodes(symById, relatedIds, localFiles, packageOf) {
4351
+ const local = new Set(
4352
+ [...typeof localFiles === "string" ? [localFiles] : localFiles].map(
4353
+ (file) => file.replace(/\\/g, "/")
4354
+ )
4355
+ );
4356
+ const isLocal = (file) => local.has(file.replace(/\\/g, "/"));
4351
4357
  return [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
4352
- const aExternal = a.file === fileFilter ? 0 : 1;
4353
- const bExternal = b.file === fileFilter ? 0 : 1;
4358
+ const aExternal = isLocal(a.file) ? 0 : 1;
4359
+ const bExternal = isLocal(b.file) ? 0 : 1;
4354
4360
  return aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id;
4355
4361
  }).map((s) => ({
4356
4362
  id: `sym:${s.id}`,
@@ -4364,7 +4370,7 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
4364
4370
  line: s.line,
4365
4371
  signature: s.signature,
4366
4372
  scope: s.scope,
4367
- external: s.file !== fileFilter
4373
+ external: !isLocal(s.file)
4368
4374
  }));
4369
4375
  }
4370
4376
  function addWeightedEdge(edgeMap, source, target, callType, weight) {
@@ -4399,6 +4405,52 @@ function materializeWeightedEdges(edgeMap, idPrefix) {
4399
4405
  return edges;
4400
4406
  }
4401
4407
 
4408
+ // src/codebase-index/writer-helpers.ts
4409
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
4410
+ function escapeLike(value) {
4411
+ return value.replace(/[\\%_]/g, (char) => `\\${char}`);
4412
+ }
4413
+ function posixIndexPath(file) {
4414
+ return file.replace(/\\/g, "/").replace(/^\.\//, "");
4415
+ }
4416
+ function indexedFileMatchSql(column = "file") {
4417
+ return `(${column} = ? OR replace(${column}, '\\', '/') = ? OR replace(${column}, '\\', '/') LIKE ? ESCAPE '\\')`;
4418
+ }
4419
+ function indexedFileMatchArgs(file) {
4420
+ const posix4 = posixIndexPath(file.trim());
4421
+ return [file, posix4, `%/${escapeLike(posix4)}`];
4422
+ }
4423
+ function matchesIndexedPackageFilter(storedFile, packageLabel, filter) {
4424
+ if (packageLabel === filter) return true;
4425
+ const posixFile = posixIndexPath(storedFile);
4426
+ const posixFilter = posixIndexPath(filter.trim());
4427
+ if (!posixFilter) return false;
4428
+ return posixFile === posixFilter || posixFile.endsWith(`/${posixFilter}`) || posixFile.includes(`/${posixFilter}/`);
4429
+ }
4430
+ function assignRefsToSymbols(refs, symbols) {
4431
+ if (refs.length === 0 || symbols.length === 0) return [];
4432
+ const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
4433
+ const seen = /* @__PURE__ */ new Set();
4434
+ const assigned = [];
4435
+ for (const ref of refs) {
4436
+ let owner;
4437
+ for (const symbol of ordered) {
4438
+ if (symbol.line > ref.line) break;
4439
+ owner = symbol;
4440
+ }
4441
+ if (!owner && ref.callType === "import") owner = ordered[0];
4442
+ if (!owner || owner.id <= 0) continue;
4443
+ const key = `${owner.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
4444
+ if (seen.has(key)) continue;
4445
+ seen.add(key);
4446
+ assigned.push({ ...ref, fromId: owner.id });
4447
+ }
4448
+ return assigned;
4449
+ }
4450
+ function resolveIndexDir(projectRoot, override) {
4451
+ return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
4452
+ }
4453
+
4402
4454
  // src/codebase-index/writer-ref-mapper.ts
4403
4455
  function mapWriterRefRow(row) {
4404
4456
  return {
@@ -4454,10 +4506,23 @@ function mapCallSiteRow(row) {
4454
4506
  line: row.ref_line
4455
4507
  };
4456
4508
  }
4509
+ function resolveIndexedFiles(stmt, file) {
4510
+ const rows = stmt(
4511
+ `SELECT DISTINCT file FROM symbols WHERE ${indexedFileMatchSql("file")} ORDER BY length(file), file`
4512
+ ).all(...indexedFileMatchArgs(file));
4513
+ return rows.map((row) => row.file);
4514
+ }
4457
4515
  function resolveSymbolIds(stmt, symbolName, file) {
4458
- const baseSql = file ? `SELECT id FROM symbols WHERE name = ? AND file = ? ORDER BY id` : `SELECT id FROM symbols WHERE name = ? ORDER BY id`;
4459
- const args = file ? [symbolName, file] : [symbolName];
4460
- const rows = stmt(baseSql).all(...args);
4516
+ if (!file) {
4517
+ const rows2 = stmt("SELECT id FROM symbols WHERE name = ? ORDER BY id").all(symbolName);
4518
+ return rows2.map((r) => r.id);
4519
+ }
4520
+ const indexedFiles = resolveIndexedFiles(stmt, file);
4521
+ if (indexedFiles.length === 0) return [];
4522
+ const placeholders = indexedFiles.map(() => "?").join(",");
4523
+ const rows = stmt(
4524
+ `SELECT id FROM symbols WHERE name = ? AND file IN (${placeholders}) ORDER BY id`
4525
+ ).all(symbolName, ...indexedFiles);
4461
4526
  return rows.map((r) => r.id);
4462
4527
  }
4463
4528
  function findIncomingCallsByName(stmt, symbolName, file, limit) {
@@ -4779,7 +4844,7 @@ function getFileGraphWithStatement(stmt, packageFilter) {
4779
4844
  const allFiles = stmt("SELECT DISTINCT file FROM symbols").all();
4780
4845
  const packageOf = readPackageLabeller(stmt);
4781
4846
  const langOf = (file) => detectLang(file) ?? "other";
4782
- const pkgFilePaths = allFiles.filter((f) => packageOf(f.file) === packageFilter).map((f) => f.file);
4847
+ const pkgFilePaths = allFiles.filter((f) => matchesIndexedPackageFilter(f.file, packageOf(f.file), packageFilter)).map((f) => f.file);
4783
4848
  const localFiles = new Set(pkgFilePaths);
4784
4849
  if (localFiles.size === 0) return { nodes: [], edges: [] };
4785
4850
  const filePlaceholders = [...localFiles].map(() => "?").join(",");
@@ -4855,9 +4920,12 @@ function getFileGraphWithStatement(stmt, packageFilter) {
4855
4920
  return { nodes: [...fileNodes.values()], edges };
4856
4921
  }
4857
4922
  function getSymbolGraphWithStatement(stmt, fileFilter) {
4923
+ const indexedFiles = resolveIndexedFiles(stmt, fileFilter);
4924
+ if (indexedFiles.length === 0) return { nodes: [], edges: [] };
4925
+ const filePlaceholders = indexedFiles.map(() => "?").join(",");
4858
4926
  const syms = stmt(
4859
- "SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id"
4860
- ).all(fileFilter);
4927
+ `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY line, id`
4928
+ ).all(...indexedFiles);
4861
4929
  if (syms.length === 0) return { nodes: [], edges: [] };
4862
4930
  const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
4863
4931
  const relatedIds = new Set(syms.map((symbol) => symbol.id));
@@ -4867,16 +4935,16 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
4867
4935
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
4868
4936
  FROM refs r
4869
4937
  JOIN symbols s ON s.id = r.from_id
4870
- WHERE s.file = ?
4938
+ WHERE s.file IN (${filePlaceholders})
4871
4939
  UNION
4872
4940
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
4873
4941
  FROM refs r
4874
4942
  JOIN symbols s ON s.id = r.to_id
4875
- WHERE s.file = ?
4943
+ WHERE s.file IN (${filePlaceholders})
4876
4944
  )
4877
4945
  WHERE to_id IS NOT NULL
4878
4946
  GROUP BY from_id, to_id, call_type`
4879
- ).all(fileFilter, fileFilter);
4947
+ ).all(...indexedFiles, ...indexedFiles);
4880
4948
  const edgeMap = /* @__PURE__ */ new Map();
4881
4949
  for (const r of refRows) {
4882
4950
  if (r.to_id == null) continue;
@@ -4895,39 +4963,15 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
4895
4963
  ).all(...missingIds);
4896
4964
  for (const s of extras) symById.set(s.id, s);
4897
4965
  }
4898
- const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter, readPackageLabeller(stmt));
4966
+ const nodes = buildSymbolGraphNodes(
4967
+ symById,
4968
+ relatedIds,
4969
+ new Set(syms.map((symbol) => symbol.file)),
4970
+ readPackageLabeller(stmt)
4971
+ );
4899
4972
  return { nodes, edges };
4900
4973
  }
4901
4974
 
4902
- // src/codebase-index/writer-helpers.ts
4903
- import { resolveWstackPaths } from "@wrongstack/core/utils";
4904
- function escapeLike(value) {
4905
- return value.replace(/[\\%_]/g, (char) => `\\${char}`);
4906
- }
4907
- function assignRefsToSymbols(refs, symbols) {
4908
- if (refs.length === 0 || symbols.length === 0) return [];
4909
- const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
4910
- const seen = /* @__PURE__ */ new Set();
4911
- const assigned = [];
4912
- for (const ref of refs) {
4913
- let owner;
4914
- for (const symbol of ordered) {
4915
- if (symbol.line > ref.line) break;
4916
- owner = symbol;
4917
- }
4918
- if (!owner && ref.callType === "import") owner = ordered[0];
4919
- if (!owner || owner.id <= 0) continue;
4920
- const key = `${owner.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
4921
- if (seen.has(key)) continue;
4922
- seen.add(key);
4923
- assigned.push({ ...ref, fromId: owner.id });
4924
- }
4925
- return assigned;
4926
- }
4927
- function resolveIndexDir(projectRoot, override) {
4928
- return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
4929
- }
4930
-
4931
4975
  // src/codebase-index/vector-search.ts
4932
4976
  var RRF_K = 60;
4933
4977
  var VECTOR_DIMENSIONS = 384;