scip-query 0.3.5 → 0.4.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.
Files changed (152) hide show
  1. package/README.md +2 -1
  2. package/dist/{chunk-VMM4SYV4.js → chunk-24LF6IZB.js} +17 -4
  3. package/dist/{chunk-EPWLXXBL.js → chunk-3NJSJ7TE.js} +28 -30
  4. package/dist/{chunk-SMDCNPMK.js → chunk-43A4UCS7.js} +3 -3
  5. package/dist/{chunk-CHDJXYBG.js → chunk-5GCORUNV.js} +3 -3
  6. package/dist/{chunk-OIDHN6GD.js → chunk-6CH23IAS.js} +148 -7
  7. package/dist/chunk-6ECR2FLR.js +60 -0
  8. package/dist/{chunk-C7H5WBTJ.js → chunk-6UZU7DFL.js} +3 -3
  9. package/dist/chunk-7BS4CPJX.js +162 -0
  10. package/dist/{chunk-UGQKAVCD.js → chunk-A6XLXV6W.js} +3 -3
  11. package/dist/chunk-ALUFWH3U.js +2695 -0
  12. package/dist/{chunk-F7XU27LU.js → chunk-CBIWNZZZ.js} +27 -3
  13. package/dist/{chunk-26DOJ63W.js → chunk-DUJNJQPO.js} +14 -3
  14. package/dist/{chunk-GSH2FPKV.js → chunk-EAGKJFDX.js} +3 -3
  15. package/dist/{chunk-VIYSWZCO.js → chunk-ELFGD5EW.js} +32 -4
  16. package/dist/chunk-FVJE4MQL.js +37 -0
  17. package/dist/{chunk-NFS5W3PP.js → chunk-GNAMV3JC.js} +3 -3
  18. package/dist/{chunk-TRESG7OB.js → chunk-J47VSL6I.js} +3 -3
  19. package/dist/chunk-J6QXMYAQ.js +115 -0
  20. package/dist/{chunk-6FKIA6EI.js → chunk-JHVQB4Y5.js} +12 -12
  21. package/dist/{chunk-YY4QGUQ5.js → chunk-JKXHHV4B.js} +2 -2
  22. package/dist/{chunk-EN2Z2CLO.js → chunk-KG4OFQEN.js} +17 -20
  23. package/dist/chunk-KLNKDX6A.js +110 -0
  24. package/dist/{chunk-5OMVSV6E.js → chunk-KYPXKV64.js} +3 -3
  25. package/dist/chunk-NXUIWD6K.js +87 -0
  26. package/dist/{chunk-LFJQVJYJ.js → chunk-OXX3QF24.js} +2 -2
  27. package/dist/{chunk-NG5F43OU.js → chunk-P3VCDYMJ.js} +70 -1
  28. package/dist/{chunk-7HK5ZLOE.js → chunk-PCU455MX.js} +2 -2
  29. package/dist/{chunk-O7Q7FDUJ.js → chunk-POLELLNM.js} +3 -3
  30. package/dist/chunk-PU2254N2.js +115 -0
  31. package/dist/{chunk-WGAD3GNR.js → chunk-QMXSLHZP.js} +5 -5
  32. package/dist/{chunk-YDBXNPYU.js → chunk-R7HPHMRZ.js} +3 -3
  33. package/dist/{chunk-HLUS2HEB.js → chunk-RE7POFGI.js} +5 -4
  34. package/dist/{chunk-VT4JBH6L.js → chunk-RJ5GULL6.js} +2 -2
  35. package/dist/{chunk-7KIMF5PV.js → chunk-RL74LF47.js} +2 -2
  36. package/dist/chunk-SVLUJSY7.js +75 -0
  37. package/dist/chunk-SYQR4QGK.js +38 -0
  38. package/dist/{chunk-QIXNAB5K.js → chunk-TO3L4YNK.js} +1 -2
  39. package/dist/{chunk-P3E6L7KW.js → chunk-TWVXFKJA.js} +84 -4
  40. package/dist/chunk-UJWI5CBB.js +75 -0
  41. package/dist/{chunk-J3JSOSUO.js → chunk-VKBOLNYN.js} +3 -3
  42. package/dist/{chunk-KBOQX573.js → chunk-VY2L4TP6.js} +20 -3
  43. package/dist/{chunk-KKCHYLVI.js → chunk-W46L2BXT.js} +2 -2
  44. package/dist/chunk-XUVPQDXW.js +34 -0
  45. package/dist/{chunk-DH7G3DDV.js → chunk-Z5VSUOEE.js} +2 -2
  46. package/dist/{chunk-GEXE2T6I.js → chunk-ZVZAIIB5.js} +23 -15
  47. package/dist/cli.js +2838 -870
  48. package/dist/{db-viWlyVtv.d.ts → db-C4rPbKkI.d.ts} +6 -1
  49. package/dist/index.d.ts +4 -4
  50. package/dist/index.js +376 -142
  51. package/dist/postinstall.js +9 -3
  52. package/dist/queries/affected.d.ts +1 -1
  53. package/dist/queries/affected.js +3 -3
  54. package/dist/queries/bottlenecks.d.ts +1 -1
  55. package/dist/queries/bottlenecks.js +3 -2
  56. package/dist/queries/by-kind.d.ts +1 -1
  57. package/dist/queries/by-kind.js +3 -2
  58. package/dist/queries/call-graph.d.ts +1 -1
  59. package/dist/queries/call-graph.js +3 -3
  60. package/dist/queries/change-surface.d.ts +1 -1
  61. package/dist/queries/change-surface.js +3 -3
  62. package/dist/queries/code.d.ts +1 -1
  63. package/dist/queries/code.js +3 -3
  64. package/dist/queries/complexity-hotspots.d.ts +1 -1
  65. package/dist/queries/complexity-hotspots.js +3 -3
  66. package/dist/queries/complexity.d.ts +1 -1
  67. package/dist/queries/complexity.js +3 -3
  68. package/dist/queries/convergence.d.ts +1 -1
  69. package/dist/queries/convergence.js +3 -3
  70. package/dist/queries/coupling.d.ts +1 -1
  71. package/dist/queries/coupling.js +3 -3
  72. package/dist/queries/cycles.d.ts +1 -1
  73. package/dist/queries/cycles.js +3 -3
  74. package/dist/queries/dataflow.d.ts +1 -1
  75. package/dist/queries/dataflow.js +3 -3
  76. package/dist/queries/dead.d.ts +1 -1
  77. package/dist/queries/dead.js +4 -4
  78. package/dist/queries/deep-chains.d.ts +1 -1
  79. package/dist/queries/deep-chains.js +3 -3
  80. package/dist/queries/deps.d.ts +1 -1
  81. package/dist/queries/deps.js +3 -3
  82. package/dist/queries/diff-impact.d.ts +1 -1
  83. package/dist/queries/diff-impact.js +2 -2
  84. package/dist/queries/drift.d.ts +1 -1
  85. package/dist/queries/drift.js +3 -3
  86. package/dist/queries/extract-candidates.d.ts +1 -1
  87. package/dist/queries/extract-candidates.js +3 -3
  88. package/dist/queries/fan.d.ts +1 -1
  89. package/dist/queries/fan.js +3 -3
  90. package/dist/queries/files.d.ts +1 -1
  91. package/dist/queries/health.d.ts +1 -1
  92. package/dist/queries/health.js +14 -14
  93. package/dist/queries/hierarchy.d.ts +1 -1
  94. package/dist/queries/hierarchy.js +3 -3
  95. package/dist/queries/hotspots.d.ts +1 -1
  96. package/dist/queries/hotspots.js +3 -2
  97. package/dist/queries/imports.d.ts +1 -1
  98. package/dist/queries/imports.js +3 -3
  99. package/dist/queries/index.d.ts +1 -1
  100. package/dist/queries/index.js +44 -44
  101. package/dist/queries/isolated.d.ts +3 -4
  102. package/dist/queries/isolated.js +4 -4
  103. package/dist/queries/members.d.ts +1 -1
  104. package/dist/queries/members.js +3 -3
  105. package/dist/queries/methods.d.ts +1 -1
  106. package/dist/queries/methods.js +3 -2
  107. package/dist/queries/outline.d.ts +1 -1
  108. package/dist/queries/outline.js +3 -3
  109. package/dist/queries/passthrough-candidates.d.ts +1 -1
  110. package/dist/queries/passthrough-candidates.js +3 -3
  111. package/dist/queries/redundant-reexports.d.ts +1 -1
  112. package/dist/queries/redundant-reexports.js +4 -4
  113. package/dist/queries/refs.d.ts +1 -1
  114. package/dist/queries/refs.js +3 -3
  115. package/dist/queries/similar-chains.d.ts +1 -1
  116. package/dist/queries/similar-chains.js +3 -3
  117. package/dist/queries/similar-files.d.ts +1 -1
  118. package/dist/queries/similar-files.js +3 -3
  119. package/dist/queries/similar-signatures.d.ts +5 -3
  120. package/dist/queries/similar-signatures.js +3 -2
  121. package/dist/queries/similar.d.ts +1 -1
  122. package/dist/queries/similar.js +3 -3
  123. package/dist/queries/slice.d.ts +1 -1
  124. package/dist/queries/slice.js +3 -3
  125. package/dist/queries/stale-abstractions.d.ts +1 -1
  126. package/dist/queries/stale-abstractions.js +3 -3
  127. package/dist/queries/stats.d.ts +1 -1
  128. package/dist/queries/surface.d.ts +1 -1
  129. package/dist/queries/surface.js +3 -3
  130. package/dist/queries/symbols.d.ts +1 -1
  131. package/dist/queries/symbols.js +3 -3
  132. package/dist/queries/system.d.ts +1 -1
  133. package/dist/queries/system.js +3 -3
  134. package/dist/queries/trace.d.ts +1 -1
  135. package/dist/queries/trace.js +3 -3
  136. package/dist/queries/wrapper-candidates.d.ts +1 -1
  137. package/dist/queries/wrapper-candidates.js +3 -3
  138. package/dist/reindex-worker.js +216 -64
  139. package/package.json +5 -1
  140. package/skills/scip-language-playbook/SKILL.md +371 -0
  141. package/dist/chunk-4JCSOF2O.js +0 -97
  142. package/dist/chunk-AXQKUYKF.js +0 -1442
  143. package/dist/chunk-CPVAQJEC.js +0 -46
  144. package/dist/chunk-EOROMIFO.js +0 -41
  145. package/dist/chunk-GU2H5QRN.js +0 -28
  146. package/dist/chunk-LQJUPXQY.js +0 -109
  147. package/dist/chunk-MPGIHELS.js +0 -39
  148. package/dist/chunk-TOIEB3LG.js +0 -78
  149. package/dist/chunk-UQEQ6AHX.js +0 -60
  150. package/dist/chunk-VJJKSGIX.js +0 -121
  151. package/dist/chunk-YGGFLMTM.js +0 -83
  152. package/dist/chunk-ZEUCXQBN.js +0 -71
package/README.md CHANGED
@@ -19,6 +19,7 @@ For goal-oriented usage guides (not just command reference), see **[Agent Guide]
19
19
  ```bash
20
20
  # Install
21
21
  npm install -g scip-query
22
+ scip-query install-skills # installs built-in Codex/Claude skills, including scip-language-playbook
22
23
 
23
24
  # Index your project (auto-detects language)
24
25
  scip-query reindex
@@ -61,7 +62,7 @@ For Python, the npm package is `scip-python-plus`. Depending on which version yo
61
62
  2. The `scip` CLI converts the protobuf to a SQLite database (`index.db`).
62
63
  3. `scip-query` runs SQL queries against that database to answer questions about your codebase.
63
64
 
64
- Because the index comes from the real compiler, results are precise — not grep-based approximations. A reference to `login()` in file A is provably the same `login()` defined in file B, not just a string match.
65
+ Because the index comes from the real compiler, direct symbol, definition, and reference queries are precise — not grep-based approximations. When a language index is missing enough call-site detail for higher-level analyses, `scip-query` can fall back to source parsing and identifier recovery so those commands stay useful, but they should be treated as source-backed heuristics rather than compiler-proof facts.
65
66
 
66
67
  ## Configuration
67
68
 
@@ -1,10 +1,11 @@
1
1
  import {
2
+ buildFileDepGraph,
2
3
  findFirstSymbolMatch,
3
4
  resolveIndexedFile
4
- } from "./chunk-AXQKUYKF.js";
5
+ } from "./chunk-ALUFWH3U.js";
5
6
  import {
6
7
  shortenSymbol
7
- } from "./chunk-QIXNAB5K.js";
8
+ } from "./chunk-TO3L4YNK.js";
8
9
 
9
10
  // src/queries/fan.ts
10
11
  function fanIn(db, symbolPattern) {
@@ -45,10 +46,22 @@ function fanOut(db, filePattern) {
45
46
  ORDER BY symbol_count DESC`,
46
47
  resolvedFile
47
48
  );
48
- return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
49
+ const indexedResults = rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
49
50
  name: r.relative_path,
50
51
  count: r.symbol_count
51
52
  }));
53
+ if (indexedResults.length > 0) {
54
+ return indexedResults;
55
+ }
56
+ const graph = buildFileDepGraph(db);
57
+ const deps = graph.get(resolvedFile);
58
+ if (!deps || deps.size === 0) {
59
+ return [];
60
+ }
61
+ return [{
62
+ name: resolvedFile,
63
+ count: deps.size
64
+ }];
52
65
  }
53
66
  function topFanIn(db, opts = {}) {
54
67
  const { limit = 30, scope } = opts;
@@ -108,4 +121,4 @@ export {
108
121
  topFanIn,
109
122
  topFanOut
110
123
  };
111
- //# sourceMappingURL=chunk-VMM4SYV4.js.map
124
+ //# sourceMappingURL=chunk-24LF6IZB.js.map
@@ -1,38 +1,22 @@
1
1
  import {
2
- getCalleeRowsForSymbol
3
- } from "./chunk-AXQKUYKF.js";
2
+ getCalleeRowsForSymbol,
3
+ getDefinitionsForFile
4
+ } from "./chunk-ALUFWH3U.js";
4
5
  import {
6
+ isFunctionLikeSymbol,
5
7
  shortenSymbol
6
- } from "./chunk-QIXNAB5K.js";
8
+ } from "./chunk-TO3L4YNK.js";
7
9
 
8
10
  // src/queries/extract-candidates.ts
9
11
  function extractCandidates(db, opts = {}) {
10
12
  const { scope, minLoc = 10, minCallees = 6, limit = 20 } = opts;
11
- const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
12
- const symbols = db.all(
13
- `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
14
- FROM global_symbols gs
15
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
16
- JOIN documents d ON der.document_id = d.id
17
- WHERE 1 = 1
18
- ${db.pathExclusionsFor("d")}
19
- ${db.symbolNoiseFor("gs")}
20
- AND (der.end_line - der.start_line + 1) >= ?
21
- ${scopeFilter}
22
- ORDER BY (der.end_line - der.start_line + 1) DESC`,
23
- minLoc
24
- );
13
+ const symbols = getScopedDefinitions(db, scope).filter((definition) => definitionLoc(definition) >= minLoc && isFunctionLikeSymbol(definition.symbol)).sort((left, right) => definitionLoc(right) - definitionLoc(left));
25
14
  const results = [];
26
15
  for (const sym of symbols) {
27
- if (db.isIgnored(sym.relative_path)) continue;
28
- const basename = sym.relative_path.split("/").pop() ?? "";
16
+ if (db.isIgnored(sym.relativePath)) continue;
17
+ const basename = sym.relativePath.split("/").pop() ?? "";
29
18
  if (basename.includes("types")) continue;
30
- const calleeChunks = getCalleeRowsForSymbol(db, {
31
- documentId: sym.document_id,
32
- startLine: sym.start_line,
33
- endLine: sym.end_line,
34
- symbolId: sym.id
35
- });
19
+ const calleeChunks = getCalleeRowsForSymbol(db, sym);
36
20
  const calleeSet = new Set(calleeChunks.map((c) => c.symbol));
37
21
  if (calleeSet.size < minCallees) continue;
38
22
  const cooccurrence = /* @__PURE__ */ new Map();
@@ -95,10 +79,10 @@ function extractCandidates(db, opts = {}) {
95
79
  results.push({
96
80
  symbol: sym.symbol,
97
81
  shortName: shortenSymbol(sym.symbol),
98
- relativePath: sym.relative_path,
99
- startLine: sym.start_line,
100
- endLine: sym.end_line,
101
- loc: sym.end_line - sym.start_line + 1,
82
+ relativePath: sym.relativePath,
83
+ startLine: sym.startLine,
84
+ endLine: sym.endLine,
85
+ loc: definitionLoc(sym),
102
86
  totalCallees: calleeSet.size,
103
87
  clusters: scoredClusters
104
88
  });
@@ -107,8 +91,22 @@ function extractCandidates(db, opts = {}) {
107
91
  results.sort((a, b) => b.clusters.length - a.clusters.length || b.loc - a.loc);
108
92
  return results.slice(0, limit);
109
93
  }
94
+ function getScopedDefinitions(db, scope) {
95
+ const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
96
+ return db.all(
97
+ `SELECT relative_path
98
+ FROM documents
99
+ WHERE 1 = 1
100
+ ${db.pathExclusionsFor("documents")}
101
+ ${scopeFilter}
102
+ ORDER BY relative_path`
103
+ ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
104
+ }
105
+ function definitionLoc(definition) {
106
+ return definition.endLine - definition.startLine + 1;
107
+ }
110
108
 
111
109
  export {
112
110
  extractCandidates
113
111
  };
114
- //# sourceMappingURL=chunk-EPWLXXBL.js.map
112
+ //# sourceMappingURL=chunk-3NJSJ7TE.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  resolveIndexedFile
3
- } from "./chunk-AXQKUYKF.js";
3
+ } from "./chunk-ALUFWH3U.js";
4
4
  import {
5
5
  shortenSymbol
6
- } from "./chunk-QIXNAB5K.js";
6
+ } from "./chunk-TO3L4YNK.js";
7
7
 
8
8
  // src/queries/change-surface.ts
9
9
  function changeSurface(db, filePattern) {
@@ -68,4 +68,4 @@ function changeSurface(db, filePattern) {
68
68
  export {
69
69
  changeSurface
70
70
  };
71
- //# sourceMappingURL=chunk-SMDCNPMK.js.map
71
+ //# sourceMappingURL=chunk-43A4UCS7.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  findFirstSymbolMatch,
3
3
  getCalleeRowsForSymbol
4
- } from "./chunk-AXQKUYKF.js";
4
+ } from "./chunk-ALUFWH3U.js";
5
5
  import {
6
6
  shortenSymbol
7
- } from "./chunk-QIXNAB5K.js";
7
+ } from "./chunk-TO3L4YNK.js";
8
8
 
9
9
  // src/queries/slice.ts
10
10
  function slice(db, symbolPattern, opts = {}) {
@@ -97,4 +97,4 @@ function forwardSlice(db, match) {
97
97
  export {
98
98
  slice
99
99
  };
100
- //# sourceMappingURL=chunk-CHDJXYBG.js.map
100
+ //# sourceMappingURL=chunk-5GCORUNV.js.map
@@ -1,17 +1,20 @@
1
1
  import {
2
2
  findFirstSymbolMatch,
3
- getCalleeRowsForSymbol
4
- } from "./chunk-AXQKUYKF.js";
3
+ getAllDefinitions,
4
+ getCalleeRowsForSymbol,
5
+ getSourceText
6
+ } from "./chunk-ALUFWH3U.js";
5
7
  import {
6
8
  isFunctionLikeSymbol,
9
+ leafName,
7
10
  shortenSymbol
8
- } from "./chunk-QIXNAB5K.js";
11
+ } from "./chunk-TO3L4YNK.js";
9
12
 
10
13
  // src/queries/similar.ts
11
14
  function similar(db, symbolPattern, opts = {}) {
12
15
  const { minSimilarity = 0.4, limit = 20 } = opts;
13
16
  const target = findCallees(db, symbolPattern);
14
- if (!target || target.callees.size === 0) return [];
17
+ if (!target) return [];
15
18
  if (!isFunctionLikeSymbol(target.symbol)) return [];
16
19
  const candidates = getAllCalleeFingerprints(db, {
17
20
  minCallees: 3,
@@ -22,7 +25,7 @@ function similar(db, symbolPattern, opts = {}) {
22
25
  const results = [];
23
26
  for (const candidate of candidates) {
24
27
  if (candidate.callees.size < 3) continue;
25
- const { similarity, significantShared, trivialShared } = weightedSimilarity(
28
+ const { similarity, significantShared } = weightedSimilarity(
26
29
  target.callees,
27
30
  candidate.callees,
28
31
  idfWeights
@@ -43,7 +46,10 @@ function similar(db, symbolPattern, opts = {}) {
43
46
  });
44
47
  }
45
48
  results.sort((a, b) => b.similarity - a.similarity);
46
- return results.slice(0, limit);
49
+ if (results.length > 0) {
50
+ return results.slice(0, limit);
51
+ }
52
+ return similarBySourceShape(db, symbolPattern, { minSimilarity, limit });
47
53
  }
48
54
  function similarAll(db, opts = {}) {
49
55
  const { minSimilarity = 0.5, limit = 20, scope, minCallees = 4 } = opts;
@@ -190,9 +196,144 @@ function difference(a, b) {
190
196
  }
191
197
  return result;
192
198
  }
199
+ function similarBySourceShape(db, symbolPattern, opts) {
200
+ const target = findSourceFingerprint(db, symbolPattern);
201
+ if (!target || target.tokens.size < 3) {
202
+ return [];
203
+ }
204
+ const minSimilarity = opts.minSimilarity >= 0.5 ? opts.minSimilarity : 0.3;
205
+ const results = [];
206
+ for (const candidate of getAllSourceFingerprints(db)) {
207
+ if (candidate.symbol === target.symbol || candidate.tokens.size < 3) continue;
208
+ const shared = intersection(target.tokens, candidate.tokens);
209
+ if (shared.size < 2) continue;
210
+ const union = /* @__PURE__ */ new Set([...target.tokens, ...candidate.tokens]);
211
+ const similarity = union.size > 0 ? shared.size / union.size : 0;
212
+ if (similarity < minSimilarity) continue;
213
+ results.push({
214
+ symbolA: target.symbol,
215
+ shortNameA: shortenSymbol(target.symbol),
216
+ fileA: target.file,
217
+ symbolB: candidate.symbol,
218
+ shortNameB: shortenSymbol(candidate.symbol),
219
+ fileB: candidate.file,
220
+ similarity,
221
+ sharedCallees: [...shared].sort(),
222
+ uniqueToA: [...difference(target.tokens, candidate.tokens)].sort(),
223
+ uniqueToB: [...difference(candidate.tokens, target.tokens)].sort()
224
+ });
225
+ }
226
+ results.sort((a, b) => b.similarity - a.similarity || a.shortNameB.localeCompare(b.shortNameB));
227
+ return results.slice(0, opts.limit);
228
+ }
229
+ function findSourceFingerprint(db, symbolPattern) {
230
+ const match = findFirstSymbolMatch(db, symbolPattern);
231
+ if (!match || !isFunctionLikeSymbol(match.symbol)) {
232
+ return null;
233
+ }
234
+ const snippet = definitionSnippet(db, match.relativePath, match.startLine, match.endLine, leafName(match.symbol));
235
+ const tokens = sourceTokens(snippet, leafName(match.symbol));
236
+ if (tokens.size === 0) {
237
+ return null;
238
+ }
239
+ return {
240
+ symbol: match.symbol,
241
+ file: match.relativePath,
242
+ tokens
243
+ };
244
+ }
245
+ function getAllSourceFingerprints(db) {
246
+ return getAllDefinitions(db).filter((definition) => definition.isFunctionLike).map((definition) => ({
247
+ symbol: definition.symbol,
248
+ file: definition.relativePath,
249
+ tokens: sourceTokens(
250
+ definitionSnippet(db, definition.relativePath, definition.startLine, definition.endLine, definition.leaf),
251
+ definition.leaf
252
+ )
253
+ })).filter((fingerprint) => fingerprint.tokens.size > 0);
254
+ }
255
+ function definitionSnippet(db, relativePath, startLine, endLine, leaf) {
256
+ const source = getSourceText(db, relativePath);
257
+ if (!source) {
258
+ return "";
259
+ }
260
+ const lines = source.split("\n");
261
+ if (endLine >= startLine && endLine - startLine <= 12) {
262
+ return lines.slice(startLine, endLine + 1).join("\n");
263
+ }
264
+ const markerPatterns = [
265
+ new RegExp(`\\bdef\\s+${escapeRegex(leaf)}\\b`),
266
+ new RegExp(`\\bfun\\s+${escapeRegex(leaf)}\\b`),
267
+ new RegExp(`\\bfn\\s+${escapeRegex(leaf)}\\b`),
268
+ new RegExp(`\\bfunction\\s+${escapeRegex(leaf)}\\b`),
269
+ new RegExp(`\\b${escapeRegex(leaf)}\\s*\\(`)
270
+ ];
271
+ const definitionStart = lines.findIndex((line) => markerPatterns.some((pattern) => pattern.test(line)));
272
+ if (definitionStart >= 0) {
273
+ let definitionEnd = definitionStart;
274
+ for (let index = definitionStart + 1; index < lines.length && index <= definitionStart + 8; index++) {
275
+ const line = lines[index] ?? "";
276
+ if (index > definitionStart && looksLikeDefinitionBoundary(line)) {
277
+ break;
278
+ }
279
+ definitionEnd = index;
280
+ if (line.trim() === "" && index > definitionStart + 1) {
281
+ break;
282
+ }
283
+ }
284
+ return lines.slice(definitionStart, definitionEnd + 1).join("\n");
285
+ }
286
+ return lines.slice(startLine, Math.min(lines.length, startLine + 8)).join("\n");
287
+ }
288
+ function sourceTokens(snippet, leaf) {
289
+ if (!snippet) {
290
+ return /* @__PURE__ */ new Set();
291
+ }
292
+ const stopWords = /* @__PURE__ */ new Set([
293
+ "public",
294
+ "private",
295
+ "protected",
296
+ "final",
297
+ "static",
298
+ "class",
299
+ "def",
300
+ "fun",
301
+ "fn",
302
+ "function",
303
+ "return",
304
+ "string",
305
+ "bool",
306
+ "boolean",
307
+ "void",
308
+ "unit",
309
+ "self",
310
+ "this",
311
+ "new",
312
+ "const",
313
+ "let",
314
+ "var",
315
+ "end",
316
+ "pub"
317
+ ]);
318
+ const normalizedLeafParts = splitIdentifier(leaf);
319
+ const normalized = snippet.replace(/["'`]/g, " ").replace(/\b\d+\b/g, " NUM ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^A-Za-z0-9_]+/g, " ").replace(/_/g, " ").toLowerCase();
320
+ const tokens = normalized.split(/\s+/).map((token) => token.trim()).filter((token) => token.length > 1).filter((token) => !stopWords.has(token)).filter((token) => !normalizedLeafParts.has(token));
321
+ return new Set(tokens);
322
+ }
323
+ function splitIdentifier(value) {
324
+ return new Set(
325
+ value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9_]+|_/).map((part) => part.toLowerCase()).filter((part) => part.length > 1)
326
+ );
327
+ }
328
+ function looksLikeDefinitionBoundary(line) {
329
+ return /^\s*(?:def|fun|fn|function|class|trait|module|object|enum|interface|public|private|protected)\b/.test(line);
330
+ }
331
+ function escapeRegex(value) {
332
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
333
+ }
193
334
 
194
335
  export {
195
336
  similar,
196
337
  similarAll
197
338
  };
198
- //# sourceMappingURL=chunk-OIDHN6GD.js.map
339
+ //# sourceMappingURL=chunk-6CH23IAS.js.map
@@ -0,0 +1,60 @@
1
+ import {
2
+ getCalleeRowsForSymbol,
3
+ getDefinitionsForFile
4
+ } from "./chunk-ALUFWH3U.js";
5
+ import {
6
+ isFunctionLikeSymbol,
7
+ shortenSymbol
8
+ } from "./chunk-TO3L4YNK.js";
9
+
10
+ // src/queries/passthrough-candidates.ts
11
+ function passthroughCandidates(db, opts) {
12
+ const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
13
+ const symbols = getScopedDefinitions(db, scope).filter((definition) => definitionLoc(definition) >= 3 && definitionLoc(definition) <= maxLoc);
14
+ const results = [];
15
+ for (const sym of symbols) {
16
+ if (db.isIgnored(sym.relativePath) || !isFunctionLikeSymbol(sym.symbol)) continue;
17
+ const rawCallees = getCalleeRowsForSymbol(db, sym);
18
+ const callees = rawCallees.some((callee2) => isFunctionLikeSymbol(callee2.symbol)) ? rawCallees.filter((callee2) => isFunctionLikeSymbol(callee2.symbol)) : rawCallees;
19
+ const uniqueCallees = /* @__PURE__ */ new Map();
20
+ for (const c of callees) {
21
+ if (!uniqueCallees.has(c.symbol)) {
22
+ uniqueCallees.set(c.symbol, { symbol: c.symbol, file: c.file });
23
+ }
24
+ }
25
+ if (uniqueCallees.size !== 1) continue;
26
+ const [, callee] = [...uniqueCallees.entries()][0];
27
+ results.push({
28
+ symbol: sym.symbol,
29
+ shortName: shortenSymbol(sym.symbol),
30
+ file: sym.relativePath,
31
+ startLine: sym.startLine,
32
+ endLine: sym.endLine,
33
+ loc: definitionLoc(sym),
34
+ forwardsTo: callee.symbol,
35
+ forwardsToShort: shortenSymbol(callee.symbol),
36
+ forwardsToFile: callee.file
37
+ });
38
+ }
39
+ results.sort((a, b) => a.loc - b.loc || a.file.localeCompare(b.file));
40
+ return results.slice(0, limit);
41
+ }
42
+ function getScopedDefinitions(db, scope) {
43
+ const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
44
+ return db.all(
45
+ `SELECT relative_path
46
+ FROM documents
47
+ WHERE 1 = 1
48
+ ${db.pathExclusionsFor("documents")}
49
+ ${scopeFilter}
50
+ ORDER BY relative_path`
51
+ ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
52
+ }
53
+ function definitionLoc(definition) {
54
+ return definition.endLine - definition.startLine + 1;
55
+ }
56
+
57
+ export {
58
+ passthroughCandidates
59
+ };
60
+ //# sourceMappingURL=chunk-6ECR2FLR.js.map
@@ -2,10 +2,10 @@ import {
2
2
  findFirstSymbolMatch,
3
3
  getCalleeRowsForSymbol,
4
4
  getCallerRowsForSymbol
5
- } from "./chunk-AXQKUYKF.js";
5
+ } from "./chunk-ALUFWH3U.js";
6
6
  import {
7
7
  shortenSymbol
8
- } from "./chunk-QIXNAB5K.js";
8
+ } from "./chunk-TO3L4YNK.js";
9
9
 
10
10
  // src/queries/call-graph.ts
11
11
  function callGraph(db, symbolPattern) {
@@ -43,4 +43,4 @@ function uniqueRows(rows) {
43
43
  export {
44
44
  callGraph
45
45
  };
46
- //# sourceMappingURL=chunk-C7H5WBTJ.js.map
46
+ //# sourceMappingURL=chunk-6UZU7DFL.js.map
@@ -0,0 +1,162 @@
1
+ import {
2
+ getAllDefinitions,
3
+ getSourceText
4
+ } from "./chunk-ALUFWH3U.js";
5
+ import {
6
+ shortenSymbol
7
+ } from "./chunk-TO3L4YNK.js";
8
+
9
+ // src/queries/similar-signatures.ts
10
+ function similarSignatures(db, opts = {}) {
11
+ const { scope, minLoc = 1, limit } = opts;
12
+ const sigGroups = /* @__PURE__ */ new Map();
13
+ for (const definition of getAllDefinitions(db, { scope })) {
14
+ if (!definition.isFunctionLike || db.isIgnored(definition.relativePath)) continue;
15
+ const loc = definition.endLine - definition.startLine + 1;
16
+ if (loc < minLoc) continue;
17
+ const normalized = resolveNormalizedSignature(db, definition);
18
+ if (!normalized) continue;
19
+ const entry = {
20
+ symbol: definition.symbol,
21
+ shortName: shortenSymbol(definition.symbol),
22
+ file: definition.relativePath,
23
+ startLine: definition.startLine,
24
+ endLine: definition.endLine,
25
+ loc
26
+ };
27
+ const existing = sigGroups.get(normalized);
28
+ if (existing) {
29
+ existing.push(entry);
30
+ } else {
31
+ sigGroups.set(normalized, [entry]);
32
+ }
33
+ }
34
+ const results = [];
35
+ for (const [signature, functions] of sigGroups) {
36
+ if (functions.length < 2) continue;
37
+ results.push({ signature, functions });
38
+ }
39
+ results.sort((a, b) => {
40
+ const sizeDiff = b.functions.length - a.functions.length;
41
+ if (sizeDiff !== 0) return sizeDiff;
42
+ const locA = a.functions.reduce((sum, f) => sum + f.loc, 0);
43
+ const locB = b.functions.reduce((sum, f) => sum + f.loc, 0);
44
+ return locB - locA;
45
+ });
46
+ return limit ? results.slice(0, limit) : results;
47
+ }
48
+ function resolveNormalizedSignature(db, definition) {
49
+ const documented = extractDocumentedSignature(definition.documentation);
50
+ const normalizedDocumented = documented ? normalizeSignature(documented) : null;
51
+ if (normalizedDocumented) {
52
+ return normalizedDocumented;
53
+ }
54
+ return normalizeSourceSignature(
55
+ extractDeclarationHead(db, definition.relativePath, definition.startLine, definition.endLine, definition.leaf),
56
+ definition.leaf
57
+ );
58
+ }
59
+ function extractDocumentedSignature(documentation) {
60
+ if (!documentation || !documentation.includes("|")) {
61
+ return null;
62
+ }
63
+ const extracted = documentation.slice(documentation.indexOf("|") + 1).replace(/\n/g, " ").trim();
64
+ return extracted.length > 0 ? extracted : null;
65
+ }
66
+ function extractDeclarationHead(db, relativePath, startLine, endLine, leaf) {
67
+ const source = getSourceText(db, relativePath);
68
+ if (!source) return null;
69
+ const lines = source.split(/\r?\n/);
70
+ const candidates = declarationStartLines(lines, startLine, endLine, leaf);
71
+ for (const candidate of candidates) {
72
+ const maxLine = Math.min(lines.length - 1, Math.max(candidate, candidate + 4));
73
+ let collected = "";
74
+ for (let lineIndex = candidate; lineIndex <= maxLine; lineIndex += 1) {
75
+ const line = lines[lineIndex]?.trim();
76
+ if (!line) continue;
77
+ collected = collected ? `${collected} ${line}` : line;
78
+ if (looksCompleteDeclaration(collected)) {
79
+ return collected;
80
+ }
81
+ }
82
+ if (collected && collected.includes("(")) {
83
+ return collected;
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+ function looksCompleteDeclaration(declaration) {
89
+ const normalized = declaration.replace(/\s+/g, " ").trim();
90
+ if (!normalized.includes("(")) return false;
91
+ if (parenBalance(normalized) > 0) return false;
92
+ return /[;{]$/.test(normalized) || /\)\s*(?::\s*[^={]+)?\s*(?:=>|=|throws\b|where\b|$)/i.test(normalized) || /\)\s*As\s+.+$/i.test(normalized);
93
+ }
94
+ function normalizeSignature(raw) {
95
+ if (!raw || !raw.trim()) return null;
96
+ let sig = raw.replace(/^```\w*\s*/, "").replace(/\s*```$/, "").replace(/^\(method\)\s*/, "").replace(/^\(property\)\s*/, "").replace(/^\(function\)\s*/, "").replace(/^\(class\)\s*/, "").replace(/^\(interface\)\s*/, "").replace(/^\(enum\)\s*/, "").replace(/^\(type alias\)\s*/, "").replace(/^\(const\)\s*/, "").replace(/^\(var\)\s*/, "").trim();
97
+ const parenIdx = sig.indexOf("(");
98
+ if (parenIdx === -1) return null;
99
+ sig = sig.slice(parenIdx);
100
+ sig = sig.replace(/\s+/g, "").toLowerCase();
101
+ if (sig.length < 3) return null;
102
+ return sig;
103
+ }
104
+ function normalizeSourceSignature(raw, leaf) {
105
+ if (!raw || !raw.trim()) return null;
106
+ const declaration = raw.replace(/\s+/g, " ").trim();
107
+ const parenIdx = declaration.indexOf("(");
108
+ if (parenIdx === -1) return null;
109
+ let prefix = declaration.slice(0, parenIdx);
110
+ const leafPattern = new RegExp(`\\b${escapeRegex(leaf)}\\b`, "i");
111
+ const leafMatch = leafPattern.exec(prefix);
112
+ if (leafMatch && typeof leafMatch.index === "number") {
113
+ prefix = prefix.slice(0, leafMatch.index);
114
+ }
115
+ prefix = prefix.replace(/\b(public|private|protected|internal|final|static|abstract|sealed|virtual|override|async|suspend|inline|constexpr|consteval|constinit|const|pub|fn|function|def|sub|friend|shared|readonly|new|open|partial|export)\b/gi, " ").replace(/\s+/g, " ").trim();
116
+ const suffix = declaration.slice(parenIdx).replace(/\s*\{[\s\S]*$/, "").replace(/\s*=>[\s\S]*$/, "").replace(/\)\s*=\s*[\s\S]*$/, ")").replace(/\s+throws\s+[^={]+$/i, "").replace(/\s+where\s+.+$/i, "").replace(/\s+/g, " ").trim();
117
+ if (!suffix.startsWith("(")) {
118
+ return null;
119
+ }
120
+ const normalized = `${prefix ? `${prefix} ` : ""}${suffix}`.replace(/\s+/g, "").toLowerCase();
121
+ return normalized.length >= 3 ? normalized : null;
122
+ }
123
+ function declarationStartLines(lines, startLine, endLine, leaf) {
124
+ const escapedLeaf = escapeRegex(leaf);
125
+ const callablePattern = new RegExp(`\\b${escapedLeaf}\\b\\s*\\(`, "i");
126
+ const rubyPattern = new RegExp(`\\bdef\\s+${escapedLeaf}\\b`, "i");
127
+ const candidates = [];
128
+ const seen = /* @__PURE__ */ new Set();
129
+ const preferredStart = Math.max(0, Math.min(startLine, lines.length - 1));
130
+ const preferredEnd = Math.max(preferredStart, Math.min(lines.length - 1, Math.max(endLine, startLine + 4)));
131
+ for (let lineIndex = preferredStart; lineIndex <= preferredEnd; lineIndex += 1) {
132
+ const line = lines[lineIndex] ?? "";
133
+ if ((callablePattern.test(line) || rubyPattern.test(line)) && !seen.has(lineIndex)) {
134
+ seen.add(lineIndex);
135
+ candidates.push(lineIndex);
136
+ }
137
+ }
138
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
139
+ const line = lines[lineIndex] ?? "";
140
+ if ((callablePattern.test(line) || rubyPattern.test(line)) && !seen.has(lineIndex)) {
141
+ seen.add(lineIndex);
142
+ candidates.push(lineIndex);
143
+ }
144
+ }
145
+ return candidates;
146
+ }
147
+ function parenBalance(value) {
148
+ let balance = 0;
149
+ for (const char of value) {
150
+ if (char === "(") balance += 1;
151
+ if (char === ")") balance -= 1;
152
+ }
153
+ return balance;
154
+ }
155
+ function escapeRegex(value) {
156
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
157
+ }
158
+
159
+ export {
160
+ similarSignatures
161
+ };
162
+ //# sourceMappingURL=chunk-7BS4CPJX.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  findFirstSymbolMatch,
3
3
  getCalleeRowsForSymbol
4
- } from "./chunk-AXQKUYKF.js";
4
+ } from "./chunk-ALUFWH3U.js";
5
5
  import {
6
6
  shortenSymbol
7
- } from "./chunk-QIXNAB5K.js";
7
+ } from "./chunk-TO3L4YNK.js";
8
8
 
9
9
  // src/queries/complexity.ts
10
10
  import { readFileSync } from "fs";
@@ -107,4 +107,4 @@ function stripCommentsAndStrings(source) {
107
107
  export {
108
108
  complexity
109
109
  };
110
- //# sourceMappingURL=chunk-UGQKAVCD.js.map
110
+ //# sourceMappingURL=chunk-A6XLXV6W.js.map