scip-query 0.3.5 → 0.4.0

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 (149) hide show
  1. package/README.md +1 -0
  2. package/dist/{chunk-6FKIA6EI.js → chunk-2F2WH5WQ.js} +12 -12
  3. package/dist/{chunk-C7H5WBTJ.js → chunk-2UISVZGQ.js} +2 -2
  4. package/dist/{chunk-EN2Z2CLO.js → chunk-4ZT7UGWW.js} +16 -19
  5. package/dist/chunk-5AJJGPZE.js +60 -0
  6. package/dist/chunk-5RKYZSQ6.js +75 -0
  7. package/dist/chunk-7YBLWIXY.js +115 -0
  8. package/dist/{chunk-O7Q7FDUJ.js → chunk-A4GWYETB.js} +2 -2
  9. package/dist/{chunk-CHDJXYBG.js → chunk-A5BGEBM7.js} +2 -2
  10. package/dist/{chunk-KKCHYLVI.js → chunk-A7YY7IDA.js} +2 -2
  11. package/dist/chunk-AS7N27JK.js +115 -0
  12. package/dist/{chunk-F7XU27LU.js → chunk-CQRYLK33.js} +26 -2
  13. package/dist/{chunk-NFS5W3PP.js → chunk-CQUNEJYM.js} +2 -2
  14. package/dist/chunk-D4I3ZMN5.js +38 -0
  15. package/dist/{chunk-J3JSOSUO.js → chunk-E7J7Q7UW.js} +2 -2
  16. package/dist/{chunk-GEXE2T6I.js → chunk-EOHPASDV.js} +22 -14
  17. package/dist/chunk-GIBETK3W.js +37 -0
  18. package/dist/{chunk-SMDCNPMK.js → chunk-H3FPW5YN.js} +2 -2
  19. package/dist/{chunk-VMM4SYV4.js → chunk-HNURMDF4.js} +16 -3
  20. package/dist/{chunk-7KIMF5PV.js → chunk-HRDPUTIQ.js} +2 -2
  21. package/dist/{chunk-OIDHN6GD.js → chunk-I2JM34UV.js} +146 -5
  22. package/dist/{chunk-EPWLXXBL.js → chunk-IV6NZ426.js} +27 -29
  23. package/dist/{chunk-VT4JBH6L.js → chunk-KDCQJTYW.js} +2 -2
  24. package/dist/{chunk-5OMVSV6E.js → chunk-LOVDB4C6.js} +2 -2
  25. package/dist/chunk-MA3B3IUT.js +75 -0
  26. package/dist/{chunk-26DOJ63W.js → chunk-N2LH3M2P.js} +13 -2
  27. package/dist/chunk-NWCJWA36.js +162 -0
  28. package/dist/{chunk-NG5F43OU.js → chunk-P3VCDYMJ.js} +70 -1
  29. package/dist/{chunk-P3E6L7KW.js → chunk-P42KQKJZ.js} +83 -3
  30. package/dist/{chunk-YDBXNPYU.js → chunk-PGHN5UTM.js} +2 -2
  31. package/dist/{chunk-UGQKAVCD.js → chunk-QCYR4S6T.js} +2 -2
  32. package/dist/chunk-QGCEAVJD.js +2529 -0
  33. package/dist/{chunk-KBOQX573.js → chunk-RIEA5DOB.js} +19 -2
  34. package/dist/{chunk-VIYSWZCO.js → chunk-SL674KAW.js} +31 -3
  35. package/dist/chunk-SRELHCMG.js +110 -0
  36. package/dist/chunk-UTRKBUCB.js +87 -0
  37. package/dist/{chunk-TRESG7OB.js → chunk-VCOJRQPP.js} +2 -2
  38. package/dist/chunk-VISMEWYP.js +34 -0
  39. package/dist/{chunk-LFJQVJYJ.js → chunk-VU7FDTWV.js} +2 -2
  40. package/dist/{chunk-HLUS2HEB.js → chunk-VUBLUTMU.js} +5 -4
  41. package/dist/{chunk-WGAD3GNR.js → chunk-WNPF2I25.js} +5 -5
  42. package/dist/{chunk-YY4QGUQ5.js → chunk-X3J4VPWM.js} +2 -2
  43. package/dist/{chunk-GSH2FPKV.js → chunk-XH56HXLC.js} +2 -2
  44. package/dist/{chunk-DH7G3DDV.js → chunk-ZU2AQQB5.js} +2 -2
  45. package/dist/cli.js +2607 -805
  46. package/dist/{db-viWlyVtv.d.ts → db-C4rPbKkI.d.ts} +6 -1
  47. package/dist/index.d.ts +4 -4
  48. package/dist/index.js +371 -138
  49. package/dist/postinstall.js +9 -3
  50. package/dist/queries/affected.d.ts +1 -1
  51. package/dist/queries/affected.js +2 -2
  52. package/dist/queries/bottlenecks.d.ts +1 -1
  53. package/dist/queries/bottlenecks.js +2 -1
  54. package/dist/queries/by-kind.d.ts +1 -1
  55. package/dist/queries/by-kind.js +2 -1
  56. package/dist/queries/call-graph.d.ts +1 -1
  57. package/dist/queries/call-graph.js +2 -2
  58. package/dist/queries/change-surface.d.ts +1 -1
  59. package/dist/queries/change-surface.js +2 -2
  60. package/dist/queries/code.d.ts +1 -1
  61. package/dist/queries/code.js +2 -2
  62. package/dist/queries/complexity-hotspots.d.ts +1 -1
  63. package/dist/queries/complexity-hotspots.js +2 -2
  64. package/dist/queries/complexity.d.ts +1 -1
  65. package/dist/queries/complexity.js +2 -2
  66. package/dist/queries/convergence.d.ts +1 -1
  67. package/dist/queries/convergence.js +2 -2
  68. package/dist/queries/coupling.d.ts +1 -1
  69. package/dist/queries/coupling.js +2 -2
  70. package/dist/queries/cycles.d.ts +1 -1
  71. package/dist/queries/cycles.js +2 -2
  72. package/dist/queries/dataflow.d.ts +1 -1
  73. package/dist/queries/dataflow.js +2 -2
  74. package/dist/queries/dead.d.ts +1 -1
  75. package/dist/queries/dead.js +3 -3
  76. package/dist/queries/deep-chains.d.ts +1 -1
  77. package/dist/queries/deep-chains.js +2 -2
  78. package/dist/queries/deps.d.ts +1 -1
  79. package/dist/queries/deps.js +2 -2
  80. package/dist/queries/diff-impact.d.ts +1 -1
  81. package/dist/queries/drift.d.ts +1 -1
  82. package/dist/queries/drift.js +2 -2
  83. package/dist/queries/extract-candidates.d.ts +1 -1
  84. package/dist/queries/extract-candidates.js +2 -2
  85. package/dist/queries/fan.d.ts +1 -1
  86. package/dist/queries/fan.js +2 -2
  87. package/dist/queries/files.d.ts +1 -1
  88. package/dist/queries/health.d.ts +1 -1
  89. package/dist/queries/health.js +13 -13
  90. package/dist/queries/hierarchy.d.ts +1 -1
  91. package/dist/queries/hierarchy.js +2 -2
  92. package/dist/queries/hotspots.d.ts +1 -1
  93. package/dist/queries/hotspots.js +2 -1
  94. package/dist/queries/imports.d.ts +1 -1
  95. package/dist/queries/imports.js +2 -2
  96. package/dist/queries/index.d.ts +1 -1
  97. package/dist/queries/index.js +42 -42
  98. package/dist/queries/isolated.d.ts +3 -4
  99. package/dist/queries/isolated.js +3 -3
  100. package/dist/queries/members.d.ts +1 -1
  101. package/dist/queries/members.js +2 -2
  102. package/dist/queries/methods.d.ts +1 -1
  103. package/dist/queries/methods.js +2 -1
  104. package/dist/queries/outline.d.ts +1 -1
  105. package/dist/queries/outline.js +2 -2
  106. package/dist/queries/passthrough-candidates.d.ts +1 -1
  107. package/dist/queries/passthrough-candidates.js +2 -2
  108. package/dist/queries/redundant-reexports.d.ts +1 -1
  109. package/dist/queries/redundant-reexports.js +3 -3
  110. package/dist/queries/refs.d.ts +1 -1
  111. package/dist/queries/refs.js +2 -2
  112. package/dist/queries/similar-chains.d.ts +1 -1
  113. package/dist/queries/similar-chains.js +2 -2
  114. package/dist/queries/similar-files.d.ts +1 -1
  115. package/dist/queries/similar-files.js +2 -2
  116. package/dist/queries/similar-signatures.d.ts +5 -3
  117. package/dist/queries/similar-signatures.js +2 -1
  118. package/dist/queries/similar.d.ts +1 -1
  119. package/dist/queries/similar.js +2 -2
  120. package/dist/queries/slice.d.ts +1 -1
  121. package/dist/queries/slice.js +2 -2
  122. package/dist/queries/stale-abstractions.d.ts +1 -1
  123. package/dist/queries/stale-abstractions.js +2 -2
  124. package/dist/queries/stats.d.ts +1 -1
  125. package/dist/queries/surface.d.ts +1 -1
  126. package/dist/queries/surface.js +2 -2
  127. package/dist/queries/symbols.d.ts +1 -1
  128. package/dist/queries/symbols.js +2 -2
  129. package/dist/queries/system.d.ts +1 -1
  130. package/dist/queries/system.js +2 -2
  131. package/dist/queries/trace.d.ts +1 -1
  132. package/dist/queries/trace.js +2 -2
  133. package/dist/queries/wrapper-candidates.d.ts +1 -1
  134. package/dist/queries/wrapper-candidates.js +2 -2
  135. package/dist/reindex-worker.js +213 -62
  136. package/package.json +1 -1
  137. package/skills/scip-language-playbook/SKILL.md +371 -0
  138. package/dist/chunk-4JCSOF2O.js +0 -97
  139. package/dist/chunk-AXQKUYKF.js +0 -1442
  140. package/dist/chunk-CPVAQJEC.js +0 -46
  141. package/dist/chunk-EOROMIFO.js +0 -41
  142. package/dist/chunk-GU2H5QRN.js +0 -28
  143. package/dist/chunk-LQJUPXQY.js +0 -109
  144. package/dist/chunk-MPGIHELS.js +0 -39
  145. package/dist/chunk-TOIEB3LG.js +0 -78
  146. package/dist/chunk-UQEQ6AHX.js +0 -60
  147. package/dist/chunk-VJJKSGIX.js +0 -121
  148. package/dist/chunk-YGGFLMTM.js +0 -83
  149. 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
@@ -3,37 +3,37 @@ import {
3
3
  } from "./chunk-74RFWB5T.js";
4
4
  import {
5
5
  wrapperCandidates
6
- } from "./chunk-4JCSOF2O.js";
6
+ } from "./chunk-AS7N27JK.js";
7
7
  import {
8
8
  similarAll
9
- } from "./chunk-OIDHN6GD.js";
9
+ } from "./chunk-I2JM34UV.js";
10
10
  import {
11
11
  staleAbstractions
12
- } from "./chunk-YGGFLMTM.js";
12
+ } from "./chunk-5RKYZSQ6.js";
13
13
  import {
14
14
  isolated
15
- } from "./chunk-UQEQ6AHX.js";
15
+ } from "./chunk-VISMEWYP.js";
16
16
  import {
17
17
  passthroughCandidates
18
- } from "./chunk-ZEUCXQBN.js";
18
+ } from "./chunk-5AJJGPZE.js";
19
19
  import {
20
20
  drift
21
- } from "./chunk-7KIMF5PV.js";
21
+ } from "./chunk-HRDPUTIQ.js";
22
22
  import {
23
23
  extractCandidates
24
- } from "./chunk-EPWLXXBL.js";
24
+ } from "./chunk-IV6NZ426.js";
25
25
  import {
26
26
  complexityHotspots
27
- } from "./chunk-VIYSWZCO.js";
27
+ } from "./chunk-SL674KAW.js";
28
28
  import {
29
29
  cycles
30
- } from "./chunk-LFJQVJYJ.js";
30
+ } from "./chunk-VU7FDTWV.js";
31
31
  import {
32
32
  dead
33
- } from "./chunk-LQJUPXQY.js";
33
+ } from "./chunk-SRELHCMG.js";
34
34
  import {
35
35
  isEntrySurface
36
- } from "./chunk-YY4QGUQ5.js";
36
+ } from "./chunk-X3J4VPWM.js";
37
37
 
38
38
  // src/queries/health.ts
39
39
  function health(db, opts = {}) {
@@ -218,4 +218,4 @@ function health(db, opts = {}) {
218
218
  export {
219
219
  health
220
220
  };
221
- //# sourceMappingURL=chunk-6FKIA6EI.js.map
221
+ //# sourceMappingURL=chunk-2F2WH5WQ.js.map
@@ -2,7 +2,7 @@ import {
2
2
  findFirstSymbolMatch,
3
3
  getCalleeRowsForSymbol,
4
4
  getCallerRowsForSymbol
5
- } from "./chunk-AXQKUYKF.js";
5
+ } from "./chunk-QGCEAVJD.js";
6
6
  import {
7
7
  shortenSymbol
8
8
  } from "./chunk-QIXNAB5K.js";
@@ -43,4 +43,4 @@ function uniqueRows(rows) {
43
43
  export {
44
44
  callGraph
45
45
  };
46
- //# sourceMappingURL=chunk-C7H5WBTJ.js.map
46
+ //# sourceMappingURL=chunk-2UISVZGQ.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ getAllDefinitions
3
+ } from "./chunk-QGCEAVJD.js";
1
4
  import {
2
5
  leafSuffix,
3
6
  parseSymbol,
@@ -143,24 +146,18 @@ function kindCounts(db, opts = {}) {
143
146
  }));
144
147
  }
145
148
  function loadKindRows(db, scope) {
146
- const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
147
- return db.all(
148
- `SELECT
149
- gs.symbol,
150
- gs.kind,
151
- gs.documentation,
152
- gs.enclosing_symbol,
153
- d.relative_path,
154
- der.start_line,
155
- der.end_line
156
- FROM global_symbols gs
157
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
158
- JOIN documents d ON der.document_id = d.id
159
- WHERE 1 = 1
160
- ${db.pathExclusionsFor("d")}
161
- ${scopeFilter}
162
- ORDER BY d.relative_path, der.start_line`
163
- ).filter((row) => !db.isIgnored(row.relative_path));
149
+ return getAllDefinitions(db, { scope }).map(mapDefinitionToKindRow);
150
+ }
151
+ function mapDefinitionToKindRow(definition) {
152
+ return {
153
+ symbol: definition.symbol,
154
+ kind: definition.kind,
155
+ documentation: definition.documentation,
156
+ enclosing_symbol: definition.enclosingSymbol,
157
+ relative_path: definition.relativePath,
158
+ start_line: definition.startLine,
159
+ end_line: definition.endLine
160
+ };
164
161
  }
165
162
  function resolveKindNumber(row) {
166
163
  if (row.kind !== null && row.kind !== 0) {
@@ -216,4 +213,4 @@ export {
216
213
  byKind,
217
214
  kindCounts
218
215
  };
219
- //# sourceMappingURL=chunk-EN2Z2CLO.js.map
216
+ //# sourceMappingURL=chunk-4ZT7UGWW.js.map
@@ -0,0 +1,60 @@
1
+ import {
2
+ getCalleeRowsForSymbol,
3
+ getDefinitionsForFile
4
+ } from "./chunk-QGCEAVJD.js";
5
+ import {
6
+ isFunctionLikeSymbol,
7
+ shortenSymbol
8
+ } from "./chunk-QIXNAB5K.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-5AJJGPZE.js.map
@@ -0,0 +1,75 @@
1
+ import {
2
+ getDefinitionsForFile
3
+ } from "./chunk-QGCEAVJD.js";
4
+ import {
5
+ shortenSymbol
6
+ } from "./chunk-QIXNAB5K.js";
7
+
8
+ // src/queries/stale-abstractions.ts
9
+ function staleAbstractions(db, opts) {
10
+ const { scope, minLoc = 3, limit = 30 } = opts ?? {};
11
+ const rows = getScopedDefinitions(db, scope).filter((definition) => definition.isTypeLike && definitionLoc(definition) >= minLoc).map((definition) => ({
12
+ symbol: definition.symbol,
13
+ file: definition.relativePath,
14
+ start_line: definition.startLine,
15
+ end_line: definition.endLine,
16
+ loc: definitionLoc(definition),
17
+ consumers: countCrossFileConsumers(db, definition)
18
+ })).filter((row) => row.consumers <= 1).sort((left, right) => right.loc - left.loc || left.file.localeCompare(right.file) || left.start_line - right.start_line);
19
+ const filesWithFunctions = getFilesWithFunctions(db, scope);
20
+ return rows.filter((r) => !db.isIgnored(r.file)).filter((r) => isTrueStaleAbstraction(r, filesWithFunctions)).map((r) => ({
21
+ symbol: r.symbol,
22
+ shortName: shortenSymbol(r.symbol),
23
+ file: r.file,
24
+ startLine: r.start_line,
25
+ endLine: r.end_line,
26
+ loc: r.loc,
27
+ consumers: r.consumers
28
+ })).slice(0, limit);
29
+ }
30
+ function getFilesWithFunctions(db, scope) {
31
+ return new Set(getScopedDefinitions(db, scope).filter((definition) => definition.isFunctionLike).map((definition) => definition.relativePath));
32
+ }
33
+ function isTrueStaleAbstraction(row, filesWithFunctions) {
34
+ const basename = row.file.split("/").pop() ?? "";
35
+ const isTypeFile = basename.includes("types") || row.file.includes("/types/");
36
+ if (isTypeFile && row.consumers > 0) {
37
+ return false;
38
+ }
39
+ if (row.consumers === 0 && filesWithFunctions.has(row.file)) {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
44
+ function getScopedDefinitions(db, scope) {
45
+ const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
46
+ return db.all(
47
+ `SELECT relative_path
48
+ FROM documents
49
+ WHERE 1 = 1
50
+ ${db.pathExclusionsFor("documents")}
51
+ ${scopeFilter}
52
+ ORDER BY relative_path`
53
+ ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
54
+ }
55
+ function countCrossFileConsumers(db, definition) {
56
+ const callers = db.all(
57
+ `SELECT DISTINCT d.relative_path
58
+ FROM mentions m
59
+ JOIN chunks c ON m.chunk_id = c.id
60
+ JOIN documents d ON c.document_id = d.id
61
+ WHERE m.symbol_id = ?
62
+ AND m.role != 1
63
+ ${db.pathExclusionsFor("d")}`,
64
+ definition.symbolId
65
+ ).map((row) => row.relative_path).filter((relativePath) => relativePath !== definition.relativePath && !db.isIgnored(relativePath));
66
+ return new Set(callers).size;
67
+ }
68
+ function definitionLoc(definition) {
69
+ return definition.endLine - definition.startLine + 1;
70
+ }
71
+
72
+ export {
73
+ staleAbstractions
74
+ };
75
+ //# sourceMappingURL=chunk-5RKYZSQ6.js.map
@@ -0,0 +1,115 @@
1
+ import {
2
+ findFirstSymbolMatch,
3
+ getSourceReferenceSites,
4
+ getSourceText
5
+ } from "./chunk-QGCEAVJD.js";
6
+ import {
7
+ isFunctionLikeSymbol
8
+ } from "./chunk-QIXNAB5K.js";
9
+
10
+ // src/queries/refs.ts
11
+ function refs(db, symbolPattern) {
12
+ const match = findFirstSymbolMatch(db, symbolPattern);
13
+ if (match) {
14
+ const includeDefinitionSite = !isFunctionLikeSymbol(match.symbol);
15
+ const definitionRows = includeDefinitionSite ? [{
16
+ relativePath: match.relativePath,
17
+ line: match.startLine
18
+ }] : [];
19
+ const sourceSites = getSourceReferenceSites(db, match).filter((site) => !db.isIgnored(site.file)).map((site) => ({
20
+ relativePath: site.file,
21
+ line: site.line
22
+ }));
23
+ if (sourceSites.length > 0) {
24
+ const seen2 = /* @__PURE__ */ new Set();
25
+ const rows2 = [...definitionRows, ...sourceSites, ...getRubySemanticRefs(db, match)].filter((site) => {
26
+ const key = `${site.relativePath}:${site.line}`;
27
+ if (seen2.has(key)) return false;
28
+ seen2.add(key);
29
+ return true;
30
+ });
31
+ return rows2;
32
+ }
33
+ }
34
+ const rows = db.all(
35
+ `SELECT DISTINCT d.relative_path, c.start_line
36
+ FROM mentions m
37
+ JOIN chunks c ON m.chunk_id = c.id
38
+ JOIN documents d ON c.document_id = d.id
39
+ JOIN global_symbols gs ON m.symbol_id = gs.id
40
+ WHERE m.symbol_id = ?
41
+ AND ${db.localSymbolPredicate}
42
+ AND m.role != 1
43
+ ORDER BY d.relative_path, c.start_line`,
44
+ match?.symbolId ?? -1
45
+ );
46
+ const referenceRows = rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
47
+ relativePath: r.relative_path,
48
+ line: r.start_line
49
+ }));
50
+ if (!match || db.isIgnored(match.relativePath) || isFunctionLikeSymbol(match.symbol)) {
51
+ return referenceRows;
52
+ }
53
+ const seen = new Set(referenceRows.map((row) => `${row.relativePath}:${row.line}`));
54
+ if (!seen.has(`${match.relativePath}:${match.startLine}`)) {
55
+ referenceRows.unshift({
56
+ relativePath: match.relativePath,
57
+ line: match.startLine
58
+ });
59
+ }
60
+ for (const row of getRubySemanticRefs(db, match)) {
61
+ const key = `${row.relativePath}:${row.line}`;
62
+ if (seen.has(key)) continue;
63
+ seen.add(key);
64
+ referenceRows.push(row);
65
+ }
66
+ return referenceRows;
67
+ }
68
+ function getRubySemanticRefs(db, match) {
69
+ if (!match.relativePath.endsWith(".rb")) {
70
+ return [];
71
+ }
72
+ const tokens = rubyReferenceTokens(match.symbol);
73
+ if (tokens.length === 0) {
74
+ return [];
75
+ }
76
+ const rows = db.all(
77
+ `SELECT relative_path
78
+ FROM documents
79
+ WHERE relative_path LIKE '%.rb'
80
+ ${db.pathExclusionsFor("documents")}
81
+ ORDER BY relative_path`
82
+ );
83
+ const results = [];
84
+ for (const row of rows) {
85
+ if (db.isIgnored(row.relative_path)) continue;
86
+ const source = getSourceText(db, row.relative_path);
87
+ if (!source) continue;
88
+ const lines = source.split("\n");
89
+ for (let index = 0; index < lines.length; index++) {
90
+ const line = lines[index] ?? "";
91
+ if (tokens.some((token) => new RegExp(`@${token}\\b|\\b${token}:`).test(line))) {
92
+ results.push({
93
+ relativePath: row.relative_path,
94
+ line: index
95
+ });
96
+ }
97
+ }
98
+ }
99
+ return results;
100
+ }
101
+ function rubyReferenceTokens(rawSymbol) {
102
+ const leaf = rawSymbol.split(":").pop() ?? rawSymbol;
103
+ const snake = leaf.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9_]+/g, "_").toLowerCase().replace(/^_+|_+$/g, "");
104
+ const parts = snake.split("_").filter(Boolean);
105
+ const candidates = /* @__PURE__ */ new Set();
106
+ if (snake) candidates.add(snake);
107
+ if (parts.length >= 1) candidates.add(parts[parts.length - 1]);
108
+ if (parts.length >= 2) candidates.add(parts.slice(-2).join("_"));
109
+ return [...candidates];
110
+ }
111
+
112
+ export {
113
+ refs
114
+ };
115
+ //# sourceMappingURL=chunk-7YBLWIXY.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-4TYLS5XX.js";
4
4
  import {
5
5
  resolveIndexedPaths
6
- } from "./chunk-AXQKUYKF.js";
6
+ } from "./chunk-QGCEAVJD.js";
7
7
  import {
8
8
  shortenSymbol
9
9
  } from "./chunk-QIXNAB5K.js";
@@ -79,4 +79,4 @@ function system(db, modulePattern) {
79
79
  export {
80
80
  system
81
81
  };
82
- //# sourceMappingURL=chunk-O7Q7FDUJ.js.map
82
+ //# sourceMappingURL=chunk-A4GWYETB.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  findFirstSymbolMatch,
3
3
  getCalleeRowsForSymbol
4
- } from "./chunk-AXQKUYKF.js";
4
+ } from "./chunk-QGCEAVJD.js";
5
5
  import {
6
6
  shortenSymbol
7
7
  } from "./chunk-QIXNAB5K.js";
@@ -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-A5BGEBM7.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveIndexedFile
3
- } from "./chunk-AXQKUYKF.js";
3
+ } from "./chunk-QGCEAVJD.js";
4
4
 
5
5
  // src/queries/coupling.ts
6
6
  function coupling(db, file1, file2) {
@@ -81,4 +81,4 @@ export {
81
81
  coupling,
82
82
  topCoupling
83
83
  };
84
- //# sourceMappingURL=chunk-KKCHYLVI.js.map
84
+ //# sourceMappingURL=chunk-A7YY7IDA.js.map
@@ -0,0 +1,115 @@
1
+ import {
2
+ buildFileDepGraph,
3
+ getCallerRowsForSymbol,
4
+ getDefinitionsForFile
5
+ } from "./chunk-QGCEAVJD.js";
6
+ import {
7
+ isFunctionLikeSymbol,
8
+ shortenSymbol
9
+ } from "./chunk-QIXNAB5K.js";
10
+
11
+ // src/queries/wrapper-candidates.ts
12
+ import { basename, extname } from "path";
13
+ function wrapperCandidates(db, opts) {
14
+ const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
15
+ const reverseFanIn = buildReverseFileFanIn(buildFileDepGraph(db, scope));
16
+ const symbols = getScopedDefinitions(db, scope).filter((definition) => definitionLoc(definition) <= maxLoc && definitionLoc(definition) >= 2);
17
+ const results = [];
18
+ for (const symbol of symbols) {
19
+ if (db.isIgnored(symbol.relativePath) || !isFunctionLikeSymbol(symbol.symbol)) continue;
20
+ const symbolStem = basename(symbol.relativePath, extname(symbol.relativePath));
21
+ const callerRows = dedupeRows(
22
+ getCallerRowsForSymbol(db, symbol, { limit: 200 }).filter((row) => row.file !== symbol.relativePath)
23
+ ).filter((row) => basename(row.file, extname(row.file)) !== symbolStem);
24
+ if (callerRows.length !== 1) continue;
25
+ const caller = callerRows[0];
26
+ const callerDefinition = getDefinitionsForFile(db, caller.file).find((definition) => definition.symbol === caller.symbol);
27
+ const useDefinitionFanIn = callerDefinition?.isFunctionLike ?? false;
28
+ let callerFanIn;
29
+ if (useDefinitionFanIn && callerDefinition) {
30
+ callerFanIn = new Set(
31
+ getCallerRowsForSymbol(db, callerDefinition, { limit: 500 }).filter((row) => row.file !== callerDefinition.relativePath).map((row) => row.file)
32
+ ).size;
33
+ } else {
34
+ callerFanIn = fallbackCallerFanIn(db, reverseFanIn, caller.file);
35
+ }
36
+ if (callerFanIn <= 3) continue;
37
+ results.push({
38
+ symbol: symbol.symbol,
39
+ shortName: shortenSymbol(symbol.symbol),
40
+ file: symbol.relativePath,
41
+ startLine: symbol.startLine,
42
+ endLine: symbol.endLine,
43
+ loc: definitionLoc(symbol),
44
+ singleCaller: caller.symbol,
45
+ singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) : basename(caller.file),
46
+ callerFanIn
47
+ });
48
+ }
49
+ results.sort((left, right) => right.callerFanIn - left.callerFanIn || right.loc - left.loc);
50
+ return results.slice(0, limit);
51
+ }
52
+ function definitionLoc(definition) {
53
+ return definition.endLine - definition.startLine + 1;
54
+ }
55
+ function getScopedDefinitions(db, scope) {
56
+ const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
57
+ return db.all(
58
+ `SELECT relative_path
59
+ FROM documents
60
+ WHERE 1 = 1
61
+ ${db.pathExclusionsFor("documents")}
62
+ ${scopeFilter}
63
+ ORDER BY relative_path`
64
+ ).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
65
+ }
66
+ function dedupeRows(rows) {
67
+ const seen = /* @__PURE__ */ new Set();
68
+ const unique = [];
69
+ for (const row of rows) {
70
+ const key = `${row.symbol}|${row.file}`;
71
+ if (seen.has(key)) continue;
72
+ seen.add(key);
73
+ unique.push(row);
74
+ }
75
+ return unique;
76
+ }
77
+ function buildReverseFileFanIn(graph) {
78
+ const reverse = /* @__PURE__ */ new Map();
79
+ for (const [fromFile, deps] of graph) {
80
+ if (!reverse.has(fromFile)) {
81
+ reverse.set(fromFile, reverse.get(fromFile) ?? 0);
82
+ }
83
+ for (const dep of deps) {
84
+ reverse.set(dep, (reverse.get(dep) ?? 0) + 1);
85
+ }
86
+ }
87
+ return reverse;
88
+ }
89
+ function fallbackCallerFanIn(db, reverseFanIn, callerFile) {
90
+ const functionLikeFanIn = getDefinitionsForFile(db, callerFile).filter((definition) => definition.isFunctionLike).map((definition) => new Set(
91
+ getCallerRowsForSymbol(db, definition, { limit: 500 }).filter((row) => row.file !== definition.relativePath).map((row) => row.file)
92
+ ).size).sort((left, right) => right - left)[0] ?? 0;
93
+ if (functionLikeFanIn > 0) {
94
+ return functionLikeFanIn;
95
+ }
96
+ const direct = reverseFanIn.get(callerFile) ?? 0;
97
+ if (direct > 0) {
98
+ return direct;
99
+ }
100
+ const stem = basename(callerFile, extname(callerFile));
101
+ let best = 0;
102
+ for (const [file, fanIn] of reverseFanIn) {
103
+ if (file === callerFile) continue;
104
+ if (basename(file, extname(file)) !== stem) continue;
105
+ if (fanIn > best) {
106
+ best = fanIn;
107
+ }
108
+ }
109
+ return best;
110
+ }
111
+
112
+ export {
113
+ wrapperCandidates
114
+ };
115
+ //# sourceMappingURL=chunk-AS7N27JK.js.map
@@ -1,3 +1,8 @@
1
+ import {
2
+ getAllDefinitions,
3
+ getCalleeRowsForSymbol,
4
+ getCallerRowsForSymbol
5
+ } from "./chunk-QGCEAVJD.js";
1
6
  import {
2
7
  shortenSymbol
3
8
  } from "./chunk-QIXNAB5K.js";
@@ -39,7 +44,7 @@ function bottlenecks(db, opts = {}) {
39
44
  minFanOut,
40
45
  limit
41
46
  );
42
- return rows.filter((r) => !db.isIgnored(r.defined_in)).map((r) => ({
47
+ const indexedResults = rows.filter((r) => !db.isIgnored(r.defined_in)).map((r) => ({
43
48
  symbol: r.symbol,
44
49
  shortName: shortenSymbol(r.symbol),
45
50
  fanIn: r.fan_in,
@@ -47,9 +52,28 @@ function bottlenecks(db, opts = {}) {
47
52
  score: r.fan_in * r.fan_out,
48
53
  definedIn: r.defined_in
49
54
  }));
55
+ if (indexedResults.length > 0) {
56
+ return indexedResults;
57
+ }
58
+ return getAllDefinitions(db, { scope }).filter((definition) => !db.isIgnored(definition.relativePath)).map((definition) => {
59
+ const fanIn = new Set(
60
+ getCallerRowsForSymbol(db, definition, { limit: 500 }).map((row) => row.file)
61
+ ).size;
62
+ const fanOut = new Set(
63
+ getCalleeRowsForSymbol(db, definition, { limit: 500 }).filter((row) => row.file !== definition.relativePath).map((row) => `${row.symbol}|${row.file}`)
64
+ ).size;
65
+ return {
66
+ symbol: definition.symbol,
67
+ shortName: shortenSymbol(definition.symbol),
68
+ fanIn,
69
+ fanOut,
70
+ score: fanIn * fanOut,
71
+ definedIn: definition.relativePath
72
+ };
73
+ }).filter((row) => row.fanIn >= minFanIn && row.fanOut >= minFanOut).sort((left, right) => right.score - left.score || right.fanIn - left.fanIn).slice(0, limit);
50
74
  }
51
75
 
52
76
  export {
53
77
  bottlenecks
54
78
  };
55
- //# sourceMappingURL=chunk-F7XU27LU.js.map
79
+ //# sourceMappingURL=chunk-CQRYLK33.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  findFirstSymbolMatch
3
- } from "./chunk-AXQKUYKF.js";
3
+ } from "./chunk-QGCEAVJD.js";
4
4
  import {
5
5
  isDirectChildSymbol,
6
6
  leafSuffix,
@@ -34,4 +34,4 @@ function members(db, symbolPattern) {
34
34
  export {
35
35
  members
36
36
  };
37
- //# sourceMappingURL=chunk-NFS5W3PP.js.map
37
+ //# sourceMappingURL=chunk-CQUNEJYM.js.map
@@ -0,0 +1,38 @@
1
+ import {
2
+ cleanSignature
3
+ } from "./chunk-4TYLS5XX.js";
4
+ import {
5
+ getDefinitionsForFile,
6
+ resolveIndexedPaths
7
+ } from "./chunk-QGCEAVJD.js";
8
+ import {
9
+ shortenSymbol
10
+ } from "./chunk-QIXNAB5K.js";
11
+
12
+ // src/queries/symbols.ts
13
+ function symbols(db, filePattern) {
14
+ const resolvedPaths = resolveIndexedPaths(db, filePattern);
15
+ if (resolvedPaths.length === 0) {
16
+ return [];
17
+ }
18
+ return resolvedPaths.flatMap((relativePath) => getDefinitionsForFile(db, relativePath)).filter((row) => !db.isIgnored(row.relativePath)).map((row) => {
19
+ const docRow = db.get(
20
+ `SELECT REPLACE(SUBSTR(documentation, INSTR(documentation, '|') + 1), char(10), ' ') AS sig
21
+ FROM global_symbols
22
+ WHERE id = ?`,
23
+ row.symbolId
24
+ );
25
+ return {
26
+ startLine: row.startLine,
27
+ endLine: row.endLine,
28
+ symbol: row.symbol,
29
+ shortName: shortenSymbol(row.symbol),
30
+ signature: cleanSignature(docRow?.sig ?? null)
31
+ };
32
+ });
33
+ }
34
+
35
+ export {
36
+ symbols
37
+ };
38
+ //# sourceMappingURL=chunk-D4I3ZMN5.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  findFirstSymbolMatch,
3
3
  resolveIndexedFile
4
- } from "./chunk-AXQKUYKF.js";
4
+ } from "./chunk-QGCEAVJD.js";
5
5
  import {
6
6
  shortenSymbol
7
7
  } from "./chunk-QIXNAB5K.js";
@@ -76,4 +76,4 @@ function readFileRange(db, filePath, startLine, endLine, context) {
76
76
  export {
77
77
  code
78
78
  };
79
- //# sourceMappingURL=chunk-J3JSOSUO.js.map
79
+ //# sourceMappingURL=chunk-E7J7Q7UW.js.map