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/index.d.ts CHANGED
@@ -43,6 +43,7 @@ export { dataflow } from './queries/dataflow.js';
43
43
  export { slice } from './queries/slice.js';
44
44
  export { redundantReexports } from './queries/redundant-reexports.js';
45
45
  export { similarSignatures } from './queries/similar-signatures.js';
46
+ import { Index } from '@c4312/scip';
46
47
  import 'better-sqlite3';
47
48
 
48
49
  /**
@@ -88,6 +89,22 @@ declare const INDEXER_CONFIGS: Record<SupportedLanguage, IndexerConfig>;
88
89
  /** Get the indexer config for a language */
89
90
  declare function getIndexerConfig(language: SupportedLanguage): IndexerConfig;
90
91
 
92
+ interface MergeScipResult {
93
+ documentCount: number;
94
+ externalSymbolCount: number;
95
+ inputCount: number;
96
+ }
97
+ /**
98
+ * Merge multiple SCIP indices into a single index message.
99
+ *
100
+ * A SCIP index is a code graph snapshot: metadata about one indexed project plus
101
+ * the documents and external symbols discovered by one or more indexers. Merging
102
+ * means producing one unified snapshot so later conversion to SQLite sees the
103
+ * whole repo instead of whichever language indexed last.
104
+ */
105
+ declare function mergeScipIndexes(indexes: readonly Index[]): Index;
106
+ declare function mergeScipFiles(inputPaths: readonly string[], outputPath: string): MergeScipResult;
107
+
91
108
  /**
92
109
  * Check if a binary is available on PATH.
93
110
  */
@@ -250,4 +267,4 @@ declare function installSkills(opts?: {
250
267
  quiet?: boolean;
251
268
  }): InstallSkillsResult;
252
269
 
253
- export { INDEXER_CONFIGS, IndexerConfig, ProjectConfig, ScipLocalSymbol, ScipSymbol, SupportedLanguage, Watcher, WatcherStatus, detectLanguages, getIndexerConfig, getScipVersion, initProjectConfig, installSkills, isBinaryAvailable, isIndexerInstalled, isScipInstalled, leafName, loadProjectConfig, parseSymbol, printScipInstallInstructions, reindex, resolveCacheDir, resolveIndexPaths, shortenSymbol, tryInstallIndexer, tryInstallScipCli };
270
+ export { INDEXER_CONFIGS, IndexerConfig, ProjectConfig, ScipLocalSymbol, ScipSymbol, SupportedLanguage, Watcher, WatcherStatus, detectLanguages, getIndexerConfig, getScipVersion, initProjectConfig, installSkills, isBinaryAvailable, isIndexerInstalled, isScipInstalled, leafName, loadProjectConfig, mergeScipFiles, mergeScipIndexes, parseSymbol, printScipInstallInstructions, reindex, resolveCacheDir, resolveIndexPaths, shortenSymbol, tryInstallIndexer, tryInstallScipCli };
package/dist/index.js CHANGED
@@ -1,150 +1,150 @@
1
1
  import "./chunk-FVH3Y44U.js";
2
2
  import {
3
3
  surface
4
- } from "./chunk-MA3B3IUT.js";
4
+ } from "./chunk-UJWI5CBB.js";
5
5
  import {
6
6
  symbols
7
- } from "./chunk-D4I3ZMN5.js";
7
+ } from "./chunk-SYQR4QGK.js";
8
8
  import {
9
9
  system
10
- } from "./chunk-A4GWYETB.js";
10
+ } from "./chunk-POLELLNM.js";
11
11
  import {
12
12
  trace
13
- } from "./chunk-EOHPASDV.js";
13
+ } from "./chunk-ZVZAIIB5.js";
14
14
  import {
15
15
  redundantReexports
16
- } from "./chunk-P42KQKJZ.js";
16
+ } from "./chunk-TWVXFKJA.js";
17
17
  import {
18
18
  refs
19
- } from "./chunk-7YBLWIXY.js";
19
+ } from "./chunk-J6QXMYAQ.js";
20
20
  import {
21
21
  similarChains
22
- } from "./chunk-WNPF2I25.js";
22
+ } from "./chunk-QMXSLHZP.js";
23
23
  import {
24
24
  similarFiles
25
- } from "./chunk-VUBLUTMU.js";
25
+ } from "./chunk-RE7POFGI.js";
26
26
  import {
27
27
  similarSignatures
28
- } from "./chunk-NWCJWA36.js";
28
+ } from "./chunk-7BS4CPJX.js";
29
29
  import {
30
30
  slice
31
- } from "./chunk-A5BGEBM7.js";
31
+ } from "./chunk-5GCORUNV.js";
32
32
  import {
33
33
  hotspots
34
- } from "./chunk-RIEA5DOB.js";
34
+ } from "./chunk-VY2L4TP6.js";
35
35
  import {
36
36
  importedBy,
37
37
  imports,
38
38
  unusedImports
39
- } from "./chunk-N2LH3M2P.js";
39
+ } from "./chunk-DUJNJQPO.js";
40
40
  import {
41
41
  members
42
- } from "./chunk-CQUNEJYM.js";
42
+ } from "./chunk-GNAMV3JC.js";
43
43
  import {
44
44
  methods
45
- } from "./chunk-GIBETK3W.js";
45
+ } from "./chunk-FVJE4MQL.js";
46
46
  import {
47
47
  outline
48
- } from "./chunk-LOVDB4C6.js";
48
+ } from "./chunk-KYPXKV64.js";
49
49
  import {
50
50
  deps,
51
51
  rdeps
52
- } from "./chunk-KDCQJTYW.js";
52
+ } from "./chunk-RJ5GULL6.js";
53
53
  import {
54
54
  diffImpact
55
- } from "./chunk-7HK5ZLOE.js";
55
+ } from "./chunk-PCU455MX.js";
56
56
  import {
57
57
  fanIn,
58
58
  fanOut,
59
59
  topFanIn,
60
60
  topFanOut
61
- } from "./chunk-HNURMDF4.js";
61
+ } from "./chunk-24LF6IZB.js";
62
62
  import {
63
63
  files
64
64
  } from "./chunk-MGNMHKX3.js";
65
65
  import {
66
66
  health
67
- } from "./chunk-2F2WH5WQ.js";
67
+ } from "./chunk-JHVQB4Y5.js";
68
68
  import {
69
69
  stats
70
70
  } from "./chunk-74RFWB5T.js";
71
71
  import {
72
72
  wrapperCandidates
73
- } from "./chunk-AS7N27JK.js";
73
+ } from "./chunk-PU2254N2.js";
74
74
  import {
75
75
  similar,
76
76
  similarAll
77
- } from "./chunk-I2JM34UV.js";
77
+ } from "./chunk-6CH23IAS.js";
78
78
  import {
79
79
  staleAbstractions
80
- } from "./chunk-5RKYZSQ6.js";
80
+ } from "./chunk-SVLUJSY7.js";
81
81
  import {
82
82
  isolated
83
- } from "./chunk-VISMEWYP.js";
83
+ } from "./chunk-XUVPQDXW.js";
84
84
  import {
85
85
  passthroughCandidates
86
- } from "./chunk-5AJJGPZE.js";
86
+ } from "./chunk-6ECR2FLR.js";
87
87
  import {
88
88
  drift
89
- } from "./chunk-HRDPUTIQ.js";
89
+ } from "./chunk-RL74LF47.js";
90
90
  import {
91
91
  extractCandidates
92
- } from "./chunk-IV6NZ426.js";
92
+ } from "./chunk-3NJSJ7TE.js";
93
93
  import {
94
94
  hierarchy
95
- } from "./chunk-PGHN5UTM.js";
95
+ } from "./chunk-R7HPHMRZ.js";
96
96
  import {
97
97
  complexityHotspots
98
- } from "./chunk-SL674KAW.js";
98
+ } from "./chunk-ELFGD5EW.js";
99
99
  import {
100
100
  complexity
101
- } from "./chunk-QCYR4S6T.js";
101
+ } from "./chunk-A6XLXV6W.js";
102
102
  import {
103
103
  convergence
104
- } from "./chunk-VCOJRQPP.js";
104
+ } from "./chunk-J47VSL6I.js";
105
105
  import {
106
106
  coupling,
107
107
  topCoupling
108
- } from "./chunk-A7YY7IDA.js";
108
+ } from "./chunk-W46L2BXT.js";
109
109
  import {
110
110
  cycles
111
- } from "./chunk-VU7FDTWV.js";
111
+ } from "./chunk-OXX3QF24.js";
112
112
  import {
113
113
  dataflow
114
- } from "./chunk-XH56HXLC.js";
114
+ } from "./chunk-EAGKJFDX.js";
115
115
  import {
116
116
  dead
117
- } from "./chunk-SRELHCMG.js";
118
- import "./chunk-X3J4VPWM.js";
117
+ } from "./chunk-KLNKDX6A.js";
118
+ import "./chunk-JKXHHV4B.js";
119
119
  import {
120
120
  deepChains
121
- } from "./chunk-ZU2AQQB5.js";
121
+ } from "./chunk-Z5VSUOEE.js";
122
122
  import {
123
123
  affected
124
- } from "./chunk-UTRKBUCB.js";
124
+ } from "./chunk-NXUIWD6K.js";
125
125
  import {
126
126
  bottlenecks
127
- } from "./chunk-CQRYLK33.js";
127
+ } from "./chunk-CBIWNZZZ.js";
128
128
  import {
129
129
  byKind,
130
130
  kindCounts
131
- } from "./chunk-4ZT7UGWW.js";
131
+ } from "./chunk-KG4OFQEN.js";
132
132
  import {
133
133
  callGraph
134
- } from "./chunk-2UISVZGQ.js";
134
+ } from "./chunk-6UZU7DFL.js";
135
135
  import {
136
136
  changeSurface
137
- } from "./chunk-H3FPW5YN.js";
137
+ } from "./chunk-43A4UCS7.js";
138
138
  import "./chunk-4TYLS5XX.js";
139
139
  import {
140
140
  code
141
- } from "./chunk-E7J7Q7UW.js";
142
- import "./chunk-QGCEAVJD.js";
141
+ } from "./chunk-VKBOLNYN.js";
142
+ import "./chunk-ALUFWH3U.js";
143
143
  import {
144
144
  leafName,
145
145
  parseSymbol,
146
146
  shortenSymbol
147
- } from "./chunk-QIXNAB5K.js";
147
+ } from "./chunk-TO3L4YNK.js";
148
148
 
149
149
  // src/db.ts
150
150
  import Database from "better-sqlite3";
@@ -254,7 +254,7 @@ var ScipDatabase = class {
254
254
  // src/gitignore-filter.ts
255
255
  import ignore from "ignore";
256
256
  import { readFileSync, existsSync } from "fs";
257
- import { join, dirname } from "path";
257
+ import { dirname, isAbsolute, join, relative, resolve } from "path";
258
258
  function createGitignoreFilter(projectRoot) {
259
259
  const ig = ignore();
260
260
  let loaded = false;
@@ -271,8 +271,8 @@ function createGitignoreFilter(projectRoot) {
271
271
  ig.add(DEFAULT_IGNORES);
272
272
  }
273
273
  return {
274
- isIgnored: (relativePath) => ig.ignores(relativePath),
275
- filter: (paths) => paths.filter((p) => !ig.ignores(p))
274
+ isIgnored: (relativePath) => safeIgnores(ig, projectRoot, relativePath),
275
+ filter: (paths) => paths.filter((p) => !safeIgnores(ig, projectRoot, p))
276
276
  };
277
277
  }
278
278
  function findGitignoreFiles(projectRoot) {
@@ -352,11 +352,36 @@ Thumbs.db
352
352
  # Type definitions (often noise in queries)
353
353
  *.d.ts
354
354
  `;
355
+ function safeIgnores(ig, projectRoot, inputPath) {
356
+ const relativePath = normalizeForIgnore(projectRoot, inputPath);
357
+ if (!relativePath) {
358
+ return false;
359
+ }
360
+ try {
361
+ return ig.ignores(relativePath);
362
+ } catch {
363
+ return false;
364
+ }
365
+ }
366
+ function normalizeForIgnore(projectRoot, inputPath) {
367
+ if (!inputPath || inputPath === ".") {
368
+ return null;
369
+ }
370
+ if (!isAbsolute(inputPath) && !inputPath.startsWith("..")) {
371
+ return inputPath.replaceAll("\\", "/");
372
+ }
373
+ const absolutePath = isAbsolute(inputPath) ? inputPath : resolve(projectRoot, inputPath);
374
+ const relativePath = relative(projectRoot, absolutePath).replaceAll("\\", "/");
375
+ if (!relativePath || relativePath === "." || relativePath.startsWith("..")) {
376
+ return null;
377
+ }
378
+ return relativePath;
379
+ }
355
380
 
356
381
  // src/reindex/index.ts
357
382
  import { execFileSync as execFileSync3 } from "child_process";
358
- import { existsSync as existsSync5, renameSync } from "fs";
359
- import { join as join5 } from "path";
383
+ import { existsSync as existsSync5, renameSync, rmSync } from "fs";
384
+ import { basename, dirname as dirname2, extname as extname2, join as join5 } from "path";
360
385
 
361
386
  // src/scip-cli.ts
362
387
  import { execFileSync as execFileSync2 } from "child_process";
@@ -991,6 +1016,128 @@ function resolveDotnetProject(projectRoot, suffixes) {
991
1016
  return null;
992
1017
  }
993
1018
 
1019
+ // src/reindex/merge.ts
1020
+ import { readFileSync as readFileSync2, writeFileSync } from "fs";
1021
+ import { create } from "@bufbuild/protobuf";
1022
+ import {
1023
+ deserializeSCIP,
1024
+ serializeSCIP,
1025
+ DocumentSchema,
1026
+ IndexSchema,
1027
+ SymbolInformationSchema
1028
+ } from "@c4312/scip";
1029
+ function mergeScipIndexes(indexes) {
1030
+ if (indexes.length === 0) {
1031
+ throw new Error("Cannot merge zero SCIP indexes");
1032
+ }
1033
+ if (indexes.length === 1) {
1034
+ return indexes[0];
1035
+ }
1036
+ const metadata = mergeMetadata(indexes);
1037
+ const documents = mergeDocuments(indexes.flatMap((index) => index.documents ?? []));
1038
+ const externalSymbols = mergeSymbolInfos(indexes.flatMap((index) => index.externalSymbols ?? []));
1039
+ return create(IndexSchema, {
1040
+ metadata,
1041
+ documents,
1042
+ externalSymbols
1043
+ });
1044
+ }
1045
+ function mergeScipFiles(inputPaths, outputPath) {
1046
+ if (inputPaths.length === 0) {
1047
+ throw new Error("Cannot merge zero SCIP files");
1048
+ }
1049
+ const indexes = inputPaths.map((path) => deserializeSCIP(readFileSync2(path)));
1050
+ const merged = mergeScipIndexes(indexes);
1051
+ writeFileSync(outputPath, Buffer.from(serializeSCIP(merged)));
1052
+ return {
1053
+ documentCount: merged.documents.length,
1054
+ externalSymbolCount: merged.externalSymbols.length,
1055
+ inputCount: inputPaths.length
1056
+ };
1057
+ }
1058
+ function mergeMetadata(indexes) {
1059
+ const first = indexes[0]?.metadata;
1060
+ if (!first) {
1061
+ return void 0;
1062
+ }
1063
+ const expectedProjectRoot = first.projectRoot;
1064
+ for (const index of indexes.slice(1)) {
1065
+ const actualProjectRoot = index.metadata?.projectRoot;
1066
+ if (expectedProjectRoot && actualProjectRoot && actualProjectRoot !== expectedProjectRoot) {
1067
+ throw new Error(
1068
+ `Cannot merge SCIP indexes with different project roots: ${expectedProjectRoot} vs ${actualProjectRoot}`
1069
+ );
1070
+ }
1071
+ }
1072
+ return first;
1073
+ }
1074
+ function mergeDocuments(documents) {
1075
+ const byPath = /* @__PURE__ */ new Map();
1076
+ for (const document of documents) {
1077
+ const existing = byPath.get(document.relativePath);
1078
+ if (!existing) {
1079
+ byPath.set(document.relativePath, document);
1080
+ continue;
1081
+ }
1082
+ byPath.set(document.relativePath, create(DocumentSchema, {
1083
+ language: existing.language || document.language,
1084
+ relativePath: existing.relativePath || document.relativePath,
1085
+ occurrences: [...existing.occurrences, ...document.occurrences],
1086
+ symbols: mergeSymbolInfos([...existing.symbols, ...document.symbols]),
1087
+ text: chooseText(existing.text, document.text),
1088
+ positionEncoding: existing.positionEncoding || document.positionEncoding
1089
+ }));
1090
+ }
1091
+ return [...byPath.values()];
1092
+ }
1093
+ function mergeSymbolInfos(symbols2) {
1094
+ const bySymbol = /* @__PURE__ */ new Map();
1095
+ for (const symbol of symbols2) {
1096
+ const existing = bySymbol.get(symbol.symbol);
1097
+ if (!existing) {
1098
+ bySymbol.set(symbol.symbol, symbol);
1099
+ continue;
1100
+ }
1101
+ bySymbol.set(symbol.symbol, create(SymbolInformationSchema, {
1102
+ symbol: existing.symbol,
1103
+ documentation: uniqueStrings([...existing.documentation, ...symbol.documentation]),
1104
+ relationships: mergeRelationships([...existing.relationships, ...symbol.relationships]),
1105
+ kind: existing.kind || symbol.kind,
1106
+ displayName: existing.displayName || symbol.displayName,
1107
+ enclosingSymbol: existing.enclosingSymbol || symbol.enclosingSymbol,
1108
+ signatureDocumentation: existing.signatureDocumentation ?? symbol.signatureDocumentation
1109
+ }));
1110
+ }
1111
+ return [...bySymbol.values()];
1112
+ }
1113
+ function mergeRelationships(relationships) {
1114
+ const seen = /* @__PURE__ */ new Set();
1115
+ const merged = [];
1116
+ for (const relationship of relationships) {
1117
+ const key = [
1118
+ relationship.symbol,
1119
+ relationship.isReference ? "1" : "0",
1120
+ relationship.isImplementation ? "1" : "0",
1121
+ relationship.isTypeDefinition ? "1" : "0",
1122
+ relationship.isDefinition ? "1" : "0"
1123
+ ].join("|");
1124
+ if (seen.has(key)) {
1125
+ continue;
1126
+ }
1127
+ seen.add(key);
1128
+ merged.push(relationship);
1129
+ }
1130
+ return merged;
1131
+ }
1132
+ function chooseText(left, right) {
1133
+ if (!left) return right;
1134
+ if (!right) return left;
1135
+ return left.length >= right.length ? left : right;
1136
+ }
1137
+ function uniqueStrings(values) {
1138
+ return [...new Set(values)];
1139
+ }
1140
+
994
1141
  // src/reindex/index.ts
995
1142
  async function reindex(opts) {
996
1143
  const {
@@ -1026,7 +1173,11 @@ async function reindex(opts) {
1026
1173
  ...process.env,
1027
1174
  NODE_OPTIONS: `--max-old-space-size=${maxHeapMb}`
1028
1175
  };
1029
- for (const lang of languages) {
1176
+ const languageOutputs = languages.map((language, index) => ({
1177
+ language,
1178
+ scipPath: languages.length > 1 ? tempScipPath(outputScip, language, index) : outputScip
1179
+ }));
1180
+ for (const { language: lang, scipPath } of languageOutputs) {
1030
1181
  const config = getIndexerConfig(lang);
1031
1182
  const binaryLabel = describeIndexerBinary(config);
1032
1183
  const projectLocalBinary = resolveProjectLocalIndexerBinary(config, projectRoot);
@@ -1056,7 +1207,7 @@ async function reindex(opts) {
1056
1207
  const indexerEnv = getIndexerExecutionEnv(config, env, resolvedBinary);
1057
1208
  const { binary, args } = config.indexArgs({
1058
1209
  projectRoot,
1059
- outputPath: outputScip,
1210
+ outputPath: scipPath,
1060
1211
  pnpmWorkspaces: opts.pnpmWorkspaces,
1061
1212
  indexerBinary: resolvedBinary
1062
1213
  });
@@ -1071,10 +1222,15 @@ async function reindex(opts) {
1071
1222
  const msg = err instanceof Error ? err.message : String(err);
1072
1223
  throw new Error(
1073
1224
  `Failed to index ${lang} with ${resolvedBinary}: ${msg}
1074
- Make sure ${binaryLabel} is installed and available on PATH.`
1225
+ Make sure ${binaryLabel} is installed and available on PATH.`,
1226
+ { cause: err }
1075
1227
  );
1076
1228
  }
1077
- moveDefaultOutputIfNeeded(config, projectRoot, outputScip);
1229
+ moveDefaultOutputIfNeeded(config, projectRoot, scipPath);
1230
+ }
1231
+ if (languageOutputs.length > 1) {
1232
+ onStatus(`Merging ${languageOutputs.length} language indexes...`);
1233
+ mergeScipFiles(languageOutputs.map((entry) => entry.scipPath), outputScip);
1078
1234
  }
1079
1235
  onStatus("Converting to SQLite...");
1080
1236
  if (!existsSync5(outputScip)) {
@@ -1088,7 +1244,13 @@ Make sure ${binaryLabel} is installed and available on PATH.`
1088
1244
  });
1089
1245
  } catch (err) {
1090
1246
  const msg = err instanceof Error ? err.message : String(err);
1091
- throw new Error(`Failed to convert SCIP index to SQLite: ${msg}`);
1247
+ throw new Error(`Failed to convert SCIP index to SQLite: ${msg}`, { cause: err });
1248
+ } finally {
1249
+ for (const { scipPath } of languageOutputs) {
1250
+ if (scipPath !== outputScip) {
1251
+ rmSync(scipPath, { force: true });
1252
+ }
1253
+ }
1092
1254
  }
1093
1255
  const durationMs = Date.now() - start;
1094
1256
  onStatus(`Done in ${(durationMs / 1e3).toFixed(1)}s`);
@@ -1103,10 +1265,15 @@ function moveDefaultOutputIfNeeded(config, projectRoot, outputScip) {
1103
1265
  renameSync(defaultOutputPath, outputScip);
1104
1266
  }
1105
1267
  }
1268
+ function tempScipPath(outputScip, language, index) {
1269
+ const extension = extname2(outputScip) || ".scip";
1270
+ const stem = basename(outputScip, extension);
1271
+ return join5(dirname2(outputScip), `${stem}.${index + 1}.${language}${extension}`);
1272
+ }
1106
1273
 
1107
1274
  // src/config.ts
1108
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync6, mkdirSync } from "fs";
1109
- import { join as join6, resolve } from "path";
1275
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync } from "fs";
1276
+ import { join as join6, resolve as resolve2 } from "path";
1110
1277
  import { createHash } from "crypto";
1111
1278
  import { homedir } from "os";
1112
1279
  var CONFIG_FILENAME = ".scipquery.json";
@@ -1122,7 +1289,7 @@ function loadProjectConfig(projectRoot) {
1122
1289
  return {};
1123
1290
  }
1124
1291
  try {
1125
- const raw = readFileSync2(configPath, "utf-8");
1292
+ const raw = readFileSync3(configPath, "utf-8");
1126
1293
  return JSON.parse(raw);
1127
1294
  } catch {
1128
1295
  return {};
@@ -1137,10 +1304,10 @@ function resolveWatchConfig(config) {
1137
1304
  function resolveCacheDir(projectRoot, config) {
1138
1305
  const envOverride = process.env["SCIP_QUERY_CACHE_DIR"];
1139
1306
  if (envOverride) return ensureDir(envOverride);
1140
- if (config?.dbPath) return ensureDir(resolve(projectRoot, config.dbPath));
1307
+ if (config?.dbPath) return ensureDir(resolve2(projectRoot, config.dbPath));
1141
1308
  const xdgCache = process.env["XDG_CACHE_HOME"];
1142
1309
  const cacheBase = xdgCache || join6(homedir(), ".cache");
1143
- const projectHash = createHash("sha256").update(resolve(projectRoot)).digest("hex").slice(0, 12);
1310
+ const projectHash = createHash("sha256").update(resolve2(projectRoot)).digest("hex").slice(0, 12);
1144
1311
  const dir = join6(cacheBase, "scip-query", "projects", projectHash);
1145
1312
  return ensureDir(dir);
1146
1313
  }
@@ -1166,7 +1333,7 @@ function initProjectConfig(projectRoot, languages) {
1166
1333
  cooldownMs: 6e4
1167
1334
  }
1168
1335
  };
1169
- writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
1336
+ writeFileSync2(configPath, JSON.stringify(config, null, 2) + "\n");
1170
1337
  return configPath;
1171
1338
  }
1172
1339
  function ensureDir(dir) {
@@ -1177,7 +1344,7 @@ function ensureDir(dir) {
1177
1344
  // src/watch.ts
1178
1345
  import { watch } from "fs";
1179
1346
  import { existsSync as existsSync7, renameSync as renameSync2 } from "fs";
1180
- import { join as join7, relative } from "path";
1347
+ import { join as join7, relative as relative2 } from "path";
1181
1348
  import { fork } from "child_process";
1182
1349
  import ignore2 from "ignore";
1183
1350
  var Watcher = class {
@@ -1255,7 +1422,7 @@ var Watcher = class {
1255
1422
  }
1256
1423
  // ── Internal ─────────────────────────────────────────────
1257
1424
  handleFileChange(filename) {
1258
- const rel = relative(this.projectRoot, join7(this.projectRoot, filename));
1425
+ const rel = relative2(this.projectRoot, join7(this.projectRoot, filename));
1259
1426
  if (this.gitignoreFilter.isIgnored(rel)) return;
1260
1427
  if (this.extraIgnore.ignores(rel)) return;
1261
1428
  if (filename.endsWith("index.db") || filename.endsWith("index.scip") || filename.endsWith("index.db.tmp") || filename.endsWith(".scipquery.json")) {
@@ -1336,10 +1503,10 @@ var Watcher = class {
1336
1503
  * Writes to index.db.tmp, then atomically renames to index.db.
1337
1504
  */
1338
1505
  runReindex() {
1339
- return new Promise((resolve3, reject) => {
1506
+ return new Promise((resolve4, reject) => {
1340
1507
  const start = Date.now();
1341
1508
  const tmpDb = this.indexPaths.dbPath + ".tmp";
1342
- const tmpScip = tempScipPath(this.indexPaths.indexPath);
1509
+ const tmpScip = tempScipPath2(this.indexPaths.indexPath);
1343
1510
  const child = fork(
1344
1511
  new URL("./reindex-worker.js", import.meta.url).pathname,
1345
1512
  [],
@@ -1364,7 +1531,7 @@ var Watcher = class {
1364
1531
  if (existsSync7(tmpScip)) {
1365
1532
  renameSync2(tmpScip, this.indexPaths.indexPath);
1366
1533
  }
1367
- resolve3(Date.now() - start);
1534
+ resolve4(Date.now() - start);
1368
1535
  } catch (err) {
1369
1536
  reject(new Error(`Atomic swap failed: ${err}`));
1370
1537
  }
@@ -1380,7 +1547,7 @@ var Watcher = class {
1380
1547
  this.onStatus(status);
1381
1548
  }
1382
1549
  };
1383
- function tempScipPath(indexPath) {
1550
+ function tempScipPath2(indexPath) {
1384
1551
  return indexPath.endsWith(".scip") ? indexPath.slice(0, -".scip".length) + ".tmp.scip" : indexPath + ".tmp.scip";
1385
1552
  }
1386
1553
 
@@ -1392,7 +1559,7 @@ import {
1392
1559
  readlinkSync,
1393
1560
  unlinkSync
1394
1561
  } from "fs";
1395
- import { join as join8, dirname as dirname2, resolve as resolve2 } from "path";
1562
+ import { join as join8, dirname as dirname3, resolve as resolve3 } from "path";
1396
1563
  import { homedir as homedir2, platform as platform3 } from "os";
1397
1564
  import { fileURLToPath } from "url";
1398
1565
  var IS_WINDOWS3 = platform3() === "win32";
@@ -1407,7 +1574,7 @@ function installSkills(opts = {}) {
1407
1574
  const log = opts.quiet ? () => {
1408
1575
  } : console.log;
1409
1576
  const thisFile = fileURLToPath(import.meta.url);
1410
- const skillsSource = resolve2(dirname2(thisFile), "..", "skills");
1577
+ const skillsSource = resolve3(dirname3(thisFile), "..", "skills");
1411
1578
  const targets = [
1412
1579
  join8(homedir2(), ".claude", "skills"),
1413
1580
  join8(homedir2(), ".codex", "skills")
@@ -1418,7 +1585,7 @@ function installSkills(opts = {}) {
1418
1585
  alreadyLinked: []
1419
1586
  };
1420
1587
  for (const targetDir of targets) {
1421
- const parentDir = dirname2(targetDir);
1588
+ const parentDir = dirname3(targetDir);
1422
1589
  if (!existsSync8(parentDir)) {
1423
1590
  continue;
1424
1591
  }
@@ -1434,7 +1601,7 @@ function installSkills(opts = {}) {
1434
1601
  if (existsSync8(target)) {
1435
1602
  try {
1436
1603
  const existing = readlinkSync(target);
1437
- if (resolve2(existing) === resolve2(source)) {
1604
+ if (resolve3(existing) === resolve3(source)) {
1438
1605
  result.alreadyLinked.push(`${toolName}/${skill}`);
1439
1606
  log(` ok: ${skill} \u2192 ${toolName} (already linked)`);
1440
1607
  continue;
@@ -1497,6 +1664,8 @@ export {
1497
1664
  leafName,
1498
1665
  loadProjectConfig,
1499
1666
  members,
1667
+ mergeScipFiles,
1668
+ mergeScipIndexes,
1500
1669
  methods,
1501
1670
  outline,
1502
1671
  parseSymbol,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  affected
3
- } from "../chunk-UTRKBUCB.js";
4
- import "../chunk-QGCEAVJD.js";
5
- import "../chunk-QIXNAB5K.js";
3
+ } from "../chunk-NXUIWD6K.js";
4
+ import "../chunk-ALUFWH3U.js";
5
+ import "../chunk-TO3L4YNK.js";
6
6
  export {
7
7
  affected
8
8
  };
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  bottlenecks
3
- } from "../chunk-CQRYLK33.js";
4
- import "../chunk-QGCEAVJD.js";
5
- import "../chunk-QIXNAB5K.js";
3
+ } from "../chunk-CBIWNZZZ.js";
4
+ import "../chunk-ALUFWH3U.js";
5
+ import "../chunk-TO3L4YNK.js";
6
6
  export {
7
7
  bottlenecks
8
8
  };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  byKind,
3
3
  kindCounts
4
- } from "../chunk-4ZT7UGWW.js";
5
- import "../chunk-QGCEAVJD.js";
6
- import "../chunk-QIXNAB5K.js";
4
+ } from "../chunk-KG4OFQEN.js";
5
+ import "../chunk-ALUFWH3U.js";
6
+ import "../chunk-TO3L4YNK.js";
7
7
  export {
8
8
  byKind,
9
9
  kindCounts
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  callGraph
3
- } from "../chunk-2UISVZGQ.js";
4
- import "../chunk-QGCEAVJD.js";
5
- import "../chunk-QIXNAB5K.js";
3
+ } from "../chunk-6UZU7DFL.js";
4
+ import "../chunk-ALUFWH3U.js";
5
+ import "../chunk-TO3L4YNK.js";
6
6
  export {
7
7
  callGraph
8
8
  };
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  changeSurface
3
- } from "../chunk-H3FPW5YN.js";
4
- import "../chunk-QGCEAVJD.js";
5
- import "../chunk-QIXNAB5K.js";
3
+ } from "../chunk-43A4UCS7.js";
4
+ import "../chunk-ALUFWH3U.js";
5
+ import "../chunk-TO3L4YNK.js";
6
6
  export {
7
7
  changeSurface
8
8
  };