scip-query 0.1.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.
- package/IMPROVEMENTS.md +143 -0
- package/PLAN.md +320 -0
- package/README.md +1213 -0
- package/dist/chunk-2QZ23IBN.js +55 -0
- package/dist/chunk-2QZ23IBN.js.map +1 -0
- package/dist/chunk-36OMT7ZJ.js +144 -0
- package/dist/chunk-36OMT7ZJ.js.map +1 -0
- package/dist/chunk-3E2X7RIE.js +101 -0
- package/dist/chunk-3E2X7RIE.js.map +1 -0
- package/dist/chunk-3UOUTZQT.js +45 -0
- package/dist/chunk-3UOUTZQT.js.map +1 -0
- package/dist/chunk-3ZZJVBIO.js +88 -0
- package/dist/chunk-3ZZJVBIO.js.map +1 -0
- package/dist/chunk-4TYLS5XX.js +10 -0
- package/dist/chunk-4TYLS5XX.js.map +1 -0
- package/dist/chunk-5FGUEU7N.js +101 -0
- package/dist/chunk-5FGUEU7N.js.map +1 -0
- package/dist/chunk-5WTJAXY2.js +61 -0
- package/dist/chunk-5WTJAXY2.js.map +1 -0
- package/dist/chunk-6NBLIDF4.js +24 -0
- package/dist/chunk-6NBLIDF4.js.map +1 -0
- package/dist/chunk-6SXADWLW.js +43 -0
- package/dist/chunk-6SXADWLW.js.map +1 -0
- package/dist/chunk-6VJ6Q7IE.js +65 -0
- package/dist/chunk-6VJ6Q7IE.js.map +1 -0
- package/dist/chunk-7OZPA5OO.js +258 -0
- package/dist/chunk-7OZPA5OO.js.map +1 -0
- package/dist/chunk-BEPIEVLR.js +76 -0
- package/dist/chunk-BEPIEVLR.js.map +1 -0
- package/dist/chunk-BFSCMC22.js +42 -0
- package/dist/chunk-BFSCMC22.js.map +1 -0
- package/dist/chunk-BP2ATLK2.js +110 -0
- package/dist/chunk-BP2ATLK2.js.map +1 -0
- package/dist/chunk-CM454WL3.js +114 -0
- package/dist/chunk-CM454WL3.js.map +1 -0
- package/dist/chunk-DCKMSTJ4.js +74 -0
- package/dist/chunk-DCKMSTJ4.js.map +1 -0
- package/dist/chunk-DEZKCZXD.js +40 -0
- package/dist/chunk-DEZKCZXD.js.map +1 -0
- package/dist/chunk-DVWGWHFW.js +99 -0
- package/dist/chunk-DVWGWHFW.js.map +1 -0
- package/dist/chunk-EMDQWNYR.js +102 -0
- package/dist/chunk-EMDQWNYR.js.map +1 -0
- package/dist/chunk-FFSWWE5O.js +33 -0
- package/dist/chunk-FFSWWE5O.js.map +1 -0
- package/dist/chunk-FGXRVW7G.js +73 -0
- package/dist/chunk-FGXRVW7G.js.map +1 -0
- package/dist/chunk-FUHJCHS4.js +158 -0
- package/dist/chunk-FUHJCHS4.js.map +1 -0
- package/dist/chunk-GJFURBEW.js +64 -0
- package/dist/chunk-GJFURBEW.js.map +1 -0
- package/dist/chunk-GTILYBH6.js +102 -0
- package/dist/chunk-GTILYBH6.js.map +1 -0
- package/dist/chunk-JJP7KQND.js +1 -0
- package/dist/chunk-JJP7KQND.js.map +1 -0
- package/dist/chunk-JKP5GH6T.js +213 -0
- package/dist/chunk-JKP5GH6T.js.map +1 -0
- package/dist/chunk-KCBMVQL5.js +38 -0
- package/dist/chunk-KCBMVQL5.js.map +1 -0
- package/dist/chunk-KVSW5KYP.js +78 -0
- package/dist/chunk-KVSW5KYP.js.map +1 -0
- package/dist/chunk-LAWMH22O.js +172 -0
- package/dist/chunk-LAWMH22O.js.map +1 -0
- package/dist/chunk-LB7OS35Q.js +72 -0
- package/dist/chunk-LB7OS35Q.js.map +1 -0
- package/dist/chunk-LUSIFBXO.js +57 -0
- package/dist/chunk-LUSIFBXO.js.map +1 -0
- package/dist/chunk-MBVNHJVN.js +44 -0
- package/dist/chunk-MBVNHJVN.js.map +1 -0
- package/dist/chunk-MGNMHKX3.js +15 -0
- package/dist/chunk-MGNMHKX3.js.map +1 -0
- package/dist/chunk-N5KEREIA.js +41 -0
- package/dist/chunk-N5KEREIA.js.map +1 -0
- package/dist/chunk-NDSQYIWT.js +71 -0
- package/dist/chunk-NDSQYIWT.js.map +1 -0
- package/dist/chunk-NUZ4OMU3.js +28 -0
- package/dist/chunk-NUZ4OMU3.js.map +1 -0
- package/dist/chunk-QOV2R2WT.js +170 -0
- package/dist/chunk-QOV2R2WT.js.map +1 -0
- package/dist/chunk-SEFSL2GF.js +78 -0
- package/dist/chunk-SEFSL2GF.js.map +1 -0
- package/dist/chunk-T6ARFSBZ.js +103 -0
- package/dist/chunk-T6ARFSBZ.js.map +1 -0
- package/dist/chunk-TBP6BICL.js +46 -0
- package/dist/chunk-TBP6BICL.js.map +1 -0
- package/dist/chunk-TDNNOR6D.js +97 -0
- package/dist/chunk-TDNNOR6D.js.map +1 -0
- package/dist/chunk-TSPZOMHC.js +195 -0
- package/dist/chunk-TSPZOMHC.js.map +1 -0
- package/dist/chunk-UNTPVD36.js +55 -0
- package/dist/chunk-UNTPVD36.js.map +1 -0
- package/dist/chunk-VRUJH4BO.js +88 -0
- package/dist/chunk-VRUJH4BO.js.map +1 -0
- package/dist/chunk-VZ7AMAFL.js +76 -0
- package/dist/chunk-VZ7AMAFL.js.map +1 -0
- package/dist/chunk-XFXDXEUN.js +24 -0
- package/dist/chunk-XFXDXEUN.js.map +1 -0
- package/dist/chunk-YZAA4LYG.js +169 -0
- package/dist/chunk-YZAA4LYG.js.map +1 -0
- package/dist/chunk-Z73NYSBZ.js +92 -0
- package/dist/chunk-Z73NYSBZ.js.map +1 -0
- package/dist/chunk-ZJRYBOEE.js +125 -0
- package/dist/chunk-ZJRYBOEE.js.map +1 -0
- package/dist/cli.js +5798 -0
- package/dist/cli.js.map +1 -0
- package/dist/db-BxaevAyc.d.ts +683 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +1271 -0
- package/dist/index.js.map +1 -0
- package/dist/postinstall.js +167 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/queries/affected.d.ts +14 -0
- package/dist/queries/affected.js +9 -0
- package/dist/queries/affected.js.map +1 -0
- package/dist/queries/bottlenecks.d.ts +18 -0
- package/dist/queries/bottlenecks.js +8 -0
- package/dist/queries/bottlenecks.js.map +1 -0
- package/dist/queries/by-kind.d.ts +20 -0
- package/dist/queries/by-kind.js +10 -0
- package/dist/queries/by-kind.js.map +1 -0
- package/dist/queries/call-graph.d.ts +13 -0
- package/dist/queries/call-graph.js +9 -0
- package/dist/queries/call-graph.js.map +1 -0
- package/dist/queries/change-surface.d.ts +10 -0
- package/dist/queries/change-surface.js +9 -0
- package/dist/queries/change-surface.js.map +1 -0
- package/dist/queries/clean-signature.d.ts +9 -0
- package/dist/queries/clean-signature.js +7 -0
- package/dist/queries/clean-signature.js.map +1 -0
- package/dist/queries/code.d.ts +17 -0
- package/dist/queries/code.js +9 -0
- package/dist/queries/code.js.map +1 -0
- package/dist/queries/complexity-hotspots.d.ts +19 -0
- package/dist/queries/complexity-hotspots.js +9 -0
- package/dist/queries/complexity-hotspots.js.map +1 -0
- package/dist/queries/complexity.d.ts +13 -0
- package/dist/queries/complexity.js +9 -0
- package/dist/queries/complexity.js.map +1 -0
- package/dist/queries/convergence.d.ts +11 -0
- package/dist/queries/convergence.js +9 -0
- package/dist/queries/convergence.js.map +1 -0
- package/dist/queries/coupling.d.ts +17 -0
- package/dist/queries/coupling.js +9 -0
- package/dist/queries/coupling.js.map +1 -0
- package/dist/queries/cycles.d.ts +16 -0
- package/dist/queries/cycles.js +8 -0
- package/dist/queries/cycles.js.map +1 -0
- package/dist/queries/dataflow.d.ts +19 -0
- package/dist/queries/dataflow.js +9 -0
- package/dist/queries/dataflow.js.map +1 -0
- package/dist/queries/dead.d.ts +10 -0
- package/dist/queries/dead.js +9 -0
- package/dist/queries/dead.js.map +1 -0
- package/dist/queries/deep-chains.d.ts +16 -0
- package/dist/queries/deep-chains.js +8 -0
- package/dist/queries/deep-chains.js.map +1 -0
- package/dist/queries/deps.d.ts +9 -0
- package/dist/queries/deps.js +9 -0
- package/dist/queries/deps.js.map +1 -0
- package/dist/queries/diff-impact.d.ts +13 -0
- package/dist/queries/diff-impact.js +9 -0
- package/dist/queries/diff-impact.js.map +1 -0
- package/dist/queries/doc-coverage.d.ts +14 -0
- package/dist/queries/doc-coverage.js +8 -0
- package/dist/queries/doc-coverage.js.map +1 -0
- package/dist/queries/drift.d.ts +25 -0
- package/dist/queries/drift.js +8 -0
- package/dist/queries/drift.js.map +1 -0
- package/dist/queries/extract-candidates.d.ts +25 -0
- package/dist/queries/extract-candidates.js +9 -0
- package/dist/queries/extract-candidates.js.map +1 -0
- package/dist/queries/fan.d.ts +29 -0
- package/dist/queries/fan.js +14 -0
- package/dist/queries/fan.js.map +1 -0
- package/dist/queries/files.d.ts +6 -0
- package/dist/queries/files.js +7 -0
- package/dist/queries/files.js.map +1 -0
- package/dist/queries/health.d.ts +18 -0
- package/dist/queries/health.js +21 -0
- package/dist/queries/health.js.map +1 -0
- package/dist/queries/hierarchy.d.ts +13 -0
- package/dist/queries/hierarchy.js +8 -0
- package/dist/queries/hierarchy.js.map +1 -0
- package/dist/queries/hotspots.d.ts +13 -0
- package/dist/queries/hotspots.js +8 -0
- package/dist/queries/hotspots.js.map +1 -0
- package/dist/queries/imports.d.ts +19 -0
- package/dist/queries/imports.js +12 -0
- package/dist/queries/imports.js.map +1 -0
- package/dist/queries/index.d.ts +47 -0
- package/dist/queries/index.js +207 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/isolated.d.ts +14 -0
- package/dist/queries/isolated.js +9 -0
- package/dist/queries/isolated.js.map +1 -0
- package/dist/queries/members.d.ts +10 -0
- package/dist/queries/members.js +8 -0
- package/dist/queries/members.js.map +1 -0
- package/dist/queries/methods.d.ts +6 -0
- package/dist/queries/methods.js +8 -0
- package/dist/queries/methods.js.map +1 -0
- package/dist/queries/outline.d.ts +10 -0
- package/dist/queries/outline.js +8 -0
- package/dist/queries/outline.js.map +1 -0
- package/dist/queries/passthrough-candidates.d.ts +18 -0
- package/dist/queries/passthrough-candidates.js +9 -0
- package/dist/queries/passthrough-candidates.js.map +1 -0
- package/dist/queries/redundant-reexports.d.ts +22 -0
- package/dist/queries/redundant-reexports.js +8 -0
- package/dist/queries/redundant-reexports.js.map +1 -0
- package/dist/queries/refs.d.ts +6 -0
- package/dist/queries/refs.js +7 -0
- package/dist/queries/refs.js.map +1 -0
- package/dist/queries/similar-chains.d.ts +29 -0
- package/dist/queries/similar-chains.js +8 -0
- package/dist/queries/similar-chains.js.map +1 -0
- package/dist/queries/similar-files.d.ts +19 -0
- package/dist/queries/similar-files.js +8 -0
- package/dist/queries/similar-files.js.map +1 -0
- package/dist/queries/similar-signatures.d.ts +21 -0
- package/dist/queries/similar-signatures.js +8 -0
- package/dist/queries/similar-signatures.js.map +1 -0
- package/dist/queries/similar.d.ts +34 -0
- package/dist/queries/similar.js +11 -0
- package/dist/queries/similar.js.map +1 -0
- package/dist/queries/slice.d.ts +21 -0
- package/dist/queries/slice.js +9 -0
- package/dist/queries/slice.js.map +1 -0
- package/dist/queries/stale-abstractions.d.ts +18 -0
- package/dist/queries/stale-abstractions.js +9 -0
- package/dist/queries/stale-abstractions.js.map +1 -0
- package/dist/queries/stats.d.ts +6 -0
- package/dist/queries/stats.js +7 -0
- package/dist/queries/stats.js.map +1 -0
- package/dist/queries/surface.d.ts +7 -0
- package/dist/queries/surface.js +8 -0
- package/dist/queries/surface.js.map +1 -0
- package/dist/queries/symbols.d.ts +6 -0
- package/dist/queries/symbols.js +9 -0
- package/dist/queries/symbols.js.map +1 -0
- package/dist/queries/system.d.ts +7 -0
- package/dist/queries/system.js +9 -0
- package/dist/queries/system.js.map +1 -0
- package/dist/queries/test-coverage.d.ts +22 -0
- package/dist/queries/test-coverage.js +11 -0
- package/dist/queries/test-coverage.js.map +1 -0
- package/dist/queries/trace.d.ts +6 -0
- package/dist/queries/trace.js +8 -0
- package/dist/queries/trace.js.map +1 -0
- package/dist/queries/wrapper-candidates.d.ts +17 -0
- package/dist/queries/wrapper-candidates.js +9 -0
- package/dist/queries/wrapper-candidates.js.map +1 -0
- package/dist/reindex-worker.js +368 -0
- package/dist/reindex-worker.js.map +1 -0
- package/docs/AGENT_GUIDE.md +359 -0
- package/package.json +70 -0
- package/reports/debloat/2026-04-10-scip-query-self-audit.md +161 -0
- package/skills/concrete-plan/SKILL.md +318 -0
- package/skills/scip-debloat/SKILL.md +413 -0
- package/skills/scip-explore/SKILL.md +235 -0
- package/skills/scip-verify/SKILL.md +323 -0
- package/src/cli.ts +1480 -0
- package/src/config.ts +117 -0
- package/src/db.ts +127 -0
- package/src/gitignore-filter.ts +143 -0
- package/src/index.ts +11 -0
- package/src/postinstall.ts +8 -0
- package/src/queries/affected.ts +86 -0
- package/src/queries/bottlenecks.ts +67 -0
- package/src/queries/by-kind.ts +204 -0
- package/src/queries/call-graph.ts +66 -0
- package/src/queries/change-surface.ts +110 -0
- package/src/queries/clean-signature.ts +22 -0
- package/src/queries/code.ts +101 -0
- package/src/queries/complexity-hotspots.ts +119 -0
- package/src/queries/complexity.ts +152 -0
- package/src/queries/convergence.ts +82 -0
- package/src/queries/coupling.ts +99 -0
- package/src/queries/cycles.ts +78 -0
- package/src/queries/dataflow.ts +128 -0
- package/src/queries/dead.ts +122 -0
- package/src/queries/deep-chains.ts +59 -0
- package/src/queries/deps.ts +46 -0
- package/src/queries/diff-impact.ts +204 -0
- package/src/queries/doc-coverage.ts +86 -0
- package/src/queries/drift.ts +224 -0
- package/src/queries/extract-candidates.ts +167 -0
- package/src/queries/fan.ts +148 -0
- package/src/queries/files.ts +16 -0
- package/src/queries/health.ts +324 -0
- package/src/queries/hierarchy.ts +49 -0
- package/src/queries/hotspots.ts +53 -0
- package/src/queries/imports.ts +95 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/isolated.ts +67 -0
- package/src/queries/members.ts +54 -0
- package/src/queries/methods.ts +27 -0
- package/src/queries/outline.ts +52 -0
- package/src/queries/passthrough-candidates.ts +94 -0
- package/src/queries/redundant-reexports.ts +170 -0
- package/src/queries/refs.ts +27 -0
- package/src/queries/similar-chains.ts +314 -0
- package/src/queries/similar-files.ts +140 -0
- package/src/queries/similar-signatures.ts +151 -0
- package/src/queries/similar.ts +305 -0
- package/src/queries/slice.ts +154 -0
- package/src/queries/stale-abstractions.ts +82 -0
- package/src/queries/stats.ts +22 -0
- package/src/queries/surface.ts +34 -0
- package/src/queries/symbols.ts +39 -0
- package/src/queries/system.ts +86 -0
- package/src/queries/test-coverage.ts +106 -0
- package/src/queries/trace.ts +55 -0
- package/src/queries/wrapper-candidates.ts +112 -0
- package/src/query-support.ts +226 -0
- package/src/reindex/detect.ts +58 -0
- package/src/reindex/index.ts +153 -0
- package/src/reindex/indexers.ts +220 -0
- package/src/reindex/install.ts +125 -0
- package/src/reindex-worker.ts +35 -0
- package/src/setup.ts +202 -0
- package/src/symbol-parser.ts +278 -0
- package/src/types.ts +654 -0
- package/src/watch.ts +274 -0
- package/tests/gitignore-filter.test.ts +48 -0
- package/tests/queries.test.ts +300 -0
- package/tests/symbol-parser.test.ts +157 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +40 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import type { HierarchyNode } from '../types.js';
|
|
3
|
+
import { shortenSymbol } from '../symbol-parser.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Walk the enclosing_symbol chain upward to show a symbol's ancestry.
|
|
7
|
+
* e.g., method → class → module → file
|
|
8
|
+
*
|
|
9
|
+
* Falls back to parsing the SCIP symbol descriptor chain when
|
|
10
|
+
* enclosing_symbol is not populated by the indexer.
|
|
11
|
+
*/
|
|
12
|
+
export function hierarchy(db: ScipDatabase, symbolPattern: string): HierarchyNode[] {
|
|
13
|
+
// Find the symbol
|
|
14
|
+
const sym = db.get<{ symbol: string; enclosing_symbol: string | null }>(
|
|
15
|
+
`SELECT symbol, enclosing_symbol FROM global_symbols
|
|
16
|
+
WHERE symbol LIKE ? LIMIT 1`,
|
|
17
|
+
`%${symbolPattern}%`,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (!sym) return [];
|
|
21
|
+
|
|
22
|
+
const chain: HierarchyNode[] = [
|
|
23
|
+
{ symbol: sym.symbol, shortName: shortenSymbol(sym.symbol), depth: 0 },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// Walk enclosing_symbol chain if available
|
|
27
|
+
let current = sym.enclosing_symbol;
|
|
28
|
+
let depth = 1;
|
|
29
|
+
const seen = new Set<string>([sym.symbol]);
|
|
30
|
+
|
|
31
|
+
while (current && !seen.has(current) && depth < 20) {
|
|
32
|
+
seen.add(current);
|
|
33
|
+
const parent = db.get<{ symbol: string; enclosing_symbol: string | null }>(
|
|
34
|
+
`SELECT symbol, enclosing_symbol FROM global_symbols WHERE symbol = ?`,
|
|
35
|
+
current,
|
|
36
|
+
);
|
|
37
|
+
if (!parent) break;
|
|
38
|
+
|
|
39
|
+
chain.push({
|
|
40
|
+
symbol: parent.symbol,
|
|
41
|
+
shortName: shortenSymbol(parent.symbol),
|
|
42
|
+
depth,
|
|
43
|
+
});
|
|
44
|
+
current = parent.enclosing_symbol;
|
|
45
|
+
depth++;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return chain;
|
|
49
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import type { HotspotResult } from '../types.js';
|
|
3
|
+
import { shortenSymbol } from '../symbol-parser.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Find the most-referenced symbols in the codebase — the choke points
|
|
7
|
+
* where changes have the widest blast radius.
|
|
8
|
+
*/
|
|
9
|
+
export function hotspots(
|
|
10
|
+
db: ScipDatabase,
|
|
11
|
+
opts: { limit?: number; scope?: string } = {},
|
|
12
|
+
): HotspotResult[] {
|
|
13
|
+
const { limit = 30, scope } = opts;
|
|
14
|
+
|
|
15
|
+
const scopeFilter = scope ? `AND def_d.relative_path LIKE '%${scope}%'` : '';
|
|
16
|
+
|
|
17
|
+
const rows = db.all<{
|
|
18
|
+
symbol: string;
|
|
19
|
+
ref_count: number;
|
|
20
|
+
file_count: number;
|
|
21
|
+
defined_in: string;
|
|
22
|
+
}>(
|
|
23
|
+
`SELECT
|
|
24
|
+
gs.symbol,
|
|
25
|
+
COUNT(*) AS ref_count,
|
|
26
|
+
COUNT(DISTINCT ref_d.id) AS file_count,
|
|
27
|
+
def_d.relative_path AS defined_in
|
|
28
|
+
FROM mentions m
|
|
29
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
30
|
+
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
31
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
32
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
33
|
+
JOIN documents def_d ON der.document_id = def_d.id
|
|
34
|
+
WHERE m.role = 0
|
|
35
|
+
${db.pathExclusionsFor('def_d')}
|
|
36
|
+
${db.symbolNoiseFor('gs')}
|
|
37
|
+
${scopeFilter}
|
|
38
|
+
GROUP BY gs.id
|
|
39
|
+
ORDER BY ref_count DESC
|
|
40
|
+
LIMIT ?`,
|
|
41
|
+
limit,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return rows
|
|
45
|
+
.filter((r) => !db.isIgnored(r.defined_in))
|
|
46
|
+
.map((r) => ({
|
|
47
|
+
symbol: r.symbol,
|
|
48
|
+
shortName: shortenSymbol(r.symbol),
|
|
49
|
+
refCount: r.ref_count,
|
|
50
|
+
fileCount: r.file_count,
|
|
51
|
+
definedIn: r.defined_in,
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import type { ImportResult, UnusedImportResult } from '../types.js';
|
|
3
|
+
import { shortenSymbol } from '../symbol-parser.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* What symbols does this file import?
|
|
7
|
+
* Uses role=2 (import) from the SCIP mentions table.
|
|
8
|
+
*/
|
|
9
|
+
export function imports(db: ScipDatabase, filePattern: string): ImportResult[] {
|
|
10
|
+
const rows = db.all<{
|
|
11
|
+
symbol: string;
|
|
12
|
+
from_file: string;
|
|
13
|
+
}>(
|
|
14
|
+
`SELECT DISTINCT gs.symbol, def_d.relative_path AS from_file
|
|
15
|
+
FROM mentions m
|
|
16
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
17
|
+
JOIN documents imp_d ON c.document_id = imp_d.id
|
|
18
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
19
|
+
LEFT JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
20
|
+
LEFT JOIN documents def_d ON der.document_id = def_d.id
|
|
21
|
+
WHERE imp_d.relative_path LIKE ?
|
|
22
|
+
AND m.role = 2
|
|
23
|
+
ORDER BY def_d.relative_path, gs.symbol`,
|
|
24
|
+
`%${filePattern}%`,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return rows.map((r) => ({
|
|
28
|
+
symbol: r.symbol,
|
|
29
|
+
shortName: shortenSymbol(r.symbol),
|
|
30
|
+
fromFile: r.from_file ?? '(external)',
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Which files import this symbol?
|
|
36
|
+
*/
|
|
37
|
+
export function importedBy(db: ScipDatabase, symbolPattern: string): ImportResult[] {
|
|
38
|
+
const rows = db.all<{
|
|
39
|
+
symbol: string;
|
|
40
|
+
importer: string;
|
|
41
|
+
}>(
|
|
42
|
+
`SELECT DISTINCT gs.symbol, d.relative_path AS importer
|
|
43
|
+
FROM mentions m
|
|
44
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
45
|
+
JOIN documents d ON c.document_id = d.id
|
|
46
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
47
|
+
WHERE gs.symbol LIKE ?
|
|
48
|
+
AND m.role = 2
|
|
49
|
+
ORDER BY d.relative_path`,
|
|
50
|
+
`%${symbolPattern}%`,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return rows
|
|
54
|
+
.filter((r) => !db.isIgnored(r.importer))
|
|
55
|
+
.map((r) => ({
|
|
56
|
+
symbol: r.symbol,
|
|
57
|
+
shortName: shortenSymbol(r.symbol),
|
|
58
|
+
fromFile: r.importer,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Find imports in a file that are never referenced (role=0) in the same file.
|
|
64
|
+
* These are likely unused imports.
|
|
65
|
+
*/
|
|
66
|
+
export function unusedImports(db: ScipDatabase, filePattern: string): UnusedImportResult[] {
|
|
67
|
+
const rows = db.all<{
|
|
68
|
+
symbol: string;
|
|
69
|
+
imported_in: string;
|
|
70
|
+
}>(
|
|
71
|
+
`SELECT gs.symbol, d.relative_path AS imported_in
|
|
72
|
+
FROM mentions m
|
|
73
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
74
|
+
JOIN documents d ON c.document_id = d.id
|
|
75
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
76
|
+
WHERE d.relative_path LIKE ?
|
|
77
|
+
AND m.role = 2
|
|
78
|
+
AND NOT EXISTS (
|
|
79
|
+
SELECT 1
|
|
80
|
+
FROM mentions ref_m
|
|
81
|
+
JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
|
|
82
|
+
WHERE ref_m.symbol_id = gs.id
|
|
83
|
+
AND ref_m.role = 0
|
|
84
|
+
AND ref_c.document_id = d.id
|
|
85
|
+
)
|
|
86
|
+
ORDER BY d.relative_path, gs.symbol`,
|
|
87
|
+
`%${filePattern}%`,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return rows.map((r) => ({
|
|
91
|
+
symbol: r.symbol,
|
|
92
|
+
shortName: shortenSymbol(r.symbol),
|
|
93
|
+
importedIn: r.imported_in,
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export { stats } from './stats.js';
|
|
2
|
+
export { files } from './files.js';
|
|
3
|
+
export { symbols } from './symbols.js';
|
|
4
|
+
export { methods } from './methods.js';
|
|
5
|
+
export { refs } from './refs.js';
|
|
6
|
+
export { trace } from './trace.js';
|
|
7
|
+
export { deps, rdeps } from './deps.js';
|
|
8
|
+
export { system } from './system.js';
|
|
9
|
+
export { surface } from './surface.js';
|
|
10
|
+
export { dead } from './dead.js';
|
|
11
|
+
export { hotspots } from './hotspots.js';
|
|
12
|
+
export { imports, importedBy, unusedImports } from './imports.js';
|
|
13
|
+
export { outline } from './outline.js';
|
|
14
|
+
export { members } from './members.js';
|
|
15
|
+
export { fanIn, fanOut, topFanIn, topFanOut } from './fan.js';
|
|
16
|
+
export { coupling, topCoupling } from './coupling.js';
|
|
17
|
+
export { cycles } from './cycles.js';
|
|
18
|
+
export { bottlenecks } from './bottlenecks.js';
|
|
19
|
+
export { isolated } from './isolated.js';
|
|
20
|
+
export { byKind, kindCounts } from './by-kind.js';
|
|
21
|
+
export { testCoverage, testCoverageSummary } from './test-coverage.js';
|
|
22
|
+
export { docCoverage } from './doc-coverage.js';
|
|
23
|
+
export { deepChains } from './deep-chains.js';
|
|
24
|
+
export { hierarchy } from './hierarchy.js';
|
|
25
|
+
export { callGraph } from './call-graph.js';
|
|
26
|
+
export { similar, similarAll } from './similar.js';
|
|
27
|
+
export { similarFiles } from './similar-files.js';
|
|
28
|
+
export { similarChains } from './similar-chains.js';
|
|
29
|
+
export { extractCandidates } from './extract-candidates.js';
|
|
30
|
+
export { affected } from './affected.js';
|
|
31
|
+
export { changeSurface } from './change-surface.js';
|
|
32
|
+
export { diffImpact } from './diff-impact.js';
|
|
33
|
+
export { drift } from './drift.js';
|
|
34
|
+
export { wrapperCandidates } from './wrapper-candidates.js';
|
|
35
|
+
export { passthroughCandidates } from './passthrough-candidates.js';
|
|
36
|
+
export { staleAbstractions } from './stale-abstractions.js';
|
|
37
|
+
export { complexityHotspots } from './complexity-hotspots.js';
|
|
38
|
+
export { health } from './health.js';
|
|
39
|
+
export { convergence } from './convergence.js';
|
|
40
|
+
export { code } from './code.js';
|
|
41
|
+
export { complexity } from './complexity.js';
|
|
42
|
+
export { dataflow } from './dataflow.js';
|
|
43
|
+
export { slice } from './slice.js';
|
|
44
|
+
export { redundantReexports } from './redundant-reexports.js';
|
|
45
|
+
export { similarSignatures } from './similar-signatures.js';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import { testFileExclusionSql } from '../query-support.js';
|
|
3
|
+
import type { IsolatedResult } from '../types.js';
|
|
4
|
+
import { shortenSymbol } from '../symbol-parser.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Find isolated symbols: defined locally, referenced by nothing,
|
|
8
|
+
* and referencing nothing external. These are truly orphaned code —
|
|
9
|
+
* not just unused exports, but completely disconnected from the graph.
|
|
10
|
+
*/
|
|
11
|
+
export function isolated(
|
|
12
|
+
db: ScipDatabase,
|
|
13
|
+
opts: { scope?: string; minLoc?: number } = {},
|
|
14
|
+
): IsolatedResult[] {
|
|
15
|
+
const { scope, minLoc = 3 } = opts;
|
|
16
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';
|
|
17
|
+
|
|
18
|
+
const rows = db.all<{
|
|
19
|
+
symbol: string;
|
|
20
|
+
relative_path: string;
|
|
21
|
+
start_line: number;
|
|
22
|
+
end_line: number;
|
|
23
|
+
loc: number;
|
|
24
|
+
}>(
|
|
25
|
+
`SELECT
|
|
26
|
+
gs.symbol,
|
|
27
|
+
d.relative_path,
|
|
28
|
+
der.start_line,
|
|
29
|
+
der.end_line,
|
|
30
|
+
(der.end_line - der.start_line + 1) AS loc
|
|
31
|
+
FROM global_symbols gs
|
|
32
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
33
|
+
JOIN documents d ON der.document_id = d.id
|
|
34
|
+
WHERE 1 = 1
|
|
35
|
+
${db.pathExclusionsFor('d')}
|
|
36
|
+
AND ${testFileExclusionSql('d')}
|
|
37
|
+
${db.symbolNoiseFor('gs')}
|
|
38
|
+
AND gs.symbol NOT LIKE '%#%'
|
|
39
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
40
|
+
${scopeFilter}
|
|
41
|
+
-- No cross-file references TO this symbol
|
|
42
|
+
AND NOT EXISTS (
|
|
43
|
+
SELECT 1 FROM mentions m
|
|
44
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
45
|
+
WHERE m.symbol_id = gs.id AND m.role = 0 AND c.document_id != d.id
|
|
46
|
+
)
|
|
47
|
+
-- No same-file references either
|
|
48
|
+
AND NOT EXISTS (
|
|
49
|
+
SELECT 1 FROM mentions m
|
|
50
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
51
|
+
WHERE m.symbol_id = gs.id AND m.role = 0 AND c.document_id = d.id
|
|
52
|
+
)
|
|
53
|
+
ORDER BY loc DESC, d.relative_path`,
|
|
54
|
+
minLoc,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return rows
|
|
58
|
+
.filter((r) => !db.isIgnored(r.relative_path))
|
|
59
|
+
.map((r) => ({
|
|
60
|
+
symbol: r.symbol,
|
|
61
|
+
shortName: shortenSymbol(r.symbol),
|
|
62
|
+
relativePath: r.relative_path,
|
|
63
|
+
startLine: r.start_line,
|
|
64
|
+
endLine: r.end_line,
|
|
65
|
+
loc: r.loc,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import type { MemberResult } from '../types.js';
|
|
3
|
+
import { shortenSymbol, parseSymbol } from '../symbol-parser.js';
|
|
4
|
+
import type { ScipSymbol } from '../types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Find all direct children of a symbol (methods, fields, nested types).
|
|
8
|
+
* Uses the enclosing_symbol relationship in global_symbols.
|
|
9
|
+
*/
|
|
10
|
+
export function members(db: ScipDatabase, symbolPattern: string): MemberResult[] {
|
|
11
|
+
// First find the parent symbol(s)
|
|
12
|
+
const parents = db.all<{ symbol: string }>(
|
|
13
|
+
`SELECT symbol FROM global_symbols WHERE symbol LIKE ?`,
|
|
14
|
+
`%${symbolPattern}%`,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (parents.length === 0) return [];
|
|
18
|
+
|
|
19
|
+
// Find children whose enclosing_symbol matches any parent
|
|
20
|
+
const placeholders = parents.map(() => '?').join(',');
|
|
21
|
+
const parentSymbols = parents.map((p) => p.symbol);
|
|
22
|
+
|
|
23
|
+
const rows = db.all<{
|
|
24
|
+
symbol: string;
|
|
25
|
+
start_line: number;
|
|
26
|
+
end_line: number;
|
|
27
|
+
}>(
|
|
28
|
+
`SELECT gs.symbol, der.start_line, der.end_line
|
|
29
|
+
FROM global_symbols gs
|
|
30
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
31
|
+
WHERE gs.enclosing_symbol IN (${placeholders})
|
|
32
|
+
${db.symbolNoise}
|
|
33
|
+
ORDER BY der.start_line`,
|
|
34
|
+
...parentSymbols,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return rows.map((r) => {
|
|
38
|
+
const parsed = parseSymbol(r.symbol);
|
|
39
|
+
let kind = 'unknown';
|
|
40
|
+
if (!('kind' in parsed)) {
|
|
41
|
+
const sym = parsed as ScipSymbol;
|
|
42
|
+
const last = sym.descriptors[sym.descriptors.length - 1];
|
|
43
|
+
if (last) kind = last.suffix;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
symbol: r.symbol,
|
|
48
|
+
shortName: shortenSymbol(r.symbol),
|
|
49
|
+
startLine: r.start_line,
|
|
50
|
+
endLine: r.end_line,
|
|
51
|
+
kind,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import type { MethodResult } from '../types.js';
|
|
3
|
+
import { leafName } from '../symbol-parser.js';
|
|
4
|
+
|
|
5
|
+
export function methods(db: ScipDatabase, className: string): MethodResult[] {
|
|
6
|
+
const rows = db.all<{
|
|
7
|
+
start_line: number;
|
|
8
|
+
end_line: number;
|
|
9
|
+
symbol: string;
|
|
10
|
+
}>(
|
|
11
|
+
`SELECT der.start_line, der.end_line, gs.symbol
|
|
12
|
+
FROM global_symbols gs
|
|
13
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
14
|
+
WHERE gs.symbol LIKE ?
|
|
15
|
+
AND ${db.localSymbolPredicate}
|
|
16
|
+
AND gs.symbol LIKE '%().%'
|
|
17
|
+
${db.symbolNoise}
|
|
18
|
+
ORDER BY der.start_line`,
|
|
19
|
+
`%${className}#%`,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return rows.map((r) => ({
|
|
23
|
+
startLine: r.start_line,
|
|
24
|
+
endLine: r.end_line,
|
|
25
|
+
name: leafName(r.symbol),
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import type { OutlineNode } from '../types.js';
|
|
3
|
+
import { shortenSymbol } from '../symbol-parser.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build a tree-structured outline of symbols in a file,
|
|
7
|
+
* using the enclosing_symbol field to establish parent-child relationships.
|
|
8
|
+
*/
|
|
9
|
+
export function outline(db: ScipDatabase, filePattern: string): OutlineNode[] {
|
|
10
|
+
const rows = db.all<{
|
|
11
|
+
symbol: string;
|
|
12
|
+
enclosing_symbol: string | null;
|
|
13
|
+
start_line: number;
|
|
14
|
+
end_line: number;
|
|
15
|
+
}>(
|
|
16
|
+
`SELECT gs.symbol, gs.enclosing_symbol, der.start_line, der.end_line
|
|
17
|
+
FROM defn_enclosing_ranges der
|
|
18
|
+
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
19
|
+
JOIN documents d ON der.document_id = d.id
|
|
20
|
+
WHERE d.relative_path LIKE ?
|
|
21
|
+
${db.symbolNoise}
|
|
22
|
+
ORDER BY der.start_line`,
|
|
23
|
+
`%${filePattern}%`,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Build a map of symbol -> node
|
|
27
|
+
const nodeMap = new Map<string, OutlineNode>();
|
|
28
|
+
const roots: OutlineNode[] = [];
|
|
29
|
+
|
|
30
|
+
for (const r of rows) {
|
|
31
|
+
const node: OutlineNode = {
|
|
32
|
+
symbol: r.symbol,
|
|
33
|
+
shortName: shortenSymbol(r.symbol),
|
|
34
|
+
startLine: r.start_line,
|
|
35
|
+
endLine: r.end_line,
|
|
36
|
+
children: [],
|
|
37
|
+
};
|
|
38
|
+
nodeMap.set(r.symbol, node);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Wire up parent-child via enclosing_symbol
|
|
42
|
+
for (const r of rows) {
|
|
43
|
+
const node = nodeMap.get(r.symbol)!;
|
|
44
|
+
if (r.enclosing_symbol && nodeMap.has(r.enclosing_symbol)) {
|
|
45
|
+
nodeMap.get(r.enclosing_symbol)!.children.push(node);
|
|
46
|
+
} else {
|
|
47
|
+
roots.push(node);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return roots;
|
|
52
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ScipDatabase } from '../db.js';
|
|
2
|
+
import { getCalleeRowsForSymbol, testFileExclusionSql } from '../query-support.js';
|
|
3
|
+
import type { PassthroughCandidate } from '../types.js';
|
|
4
|
+
import { shortenSymbol } from '../symbol-parser.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Find passthrough candidates: functions that just forward to one
|
|
8
|
+
* other function.
|
|
9
|
+
*
|
|
10
|
+
* A function with exactly 1 callee and small LOC is likely a thin
|
|
11
|
+
* wrapper that adds no value — it just passes arguments through to
|
|
12
|
+
* the real implementation.
|
|
13
|
+
*/
|
|
14
|
+
export function passthroughCandidates(
|
|
15
|
+
db: ScipDatabase,
|
|
16
|
+
opts?: { scope?: string; maxLoc?: number; limit?: number },
|
|
17
|
+
): PassthroughCandidate[] {
|
|
18
|
+
const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
|
|
19
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';
|
|
20
|
+
|
|
21
|
+
// 1. Find all non-trivial symbols with definition ranges (>= 3 LOC)
|
|
22
|
+
const symbols = db.all<{
|
|
23
|
+
id: number;
|
|
24
|
+
symbol: string;
|
|
25
|
+
document_id: number;
|
|
26
|
+
start_line: number;
|
|
27
|
+
end_line: number;
|
|
28
|
+
loc: number;
|
|
29
|
+
relative_path: string;
|
|
30
|
+
}>(
|
|
31
|
+
`SELECT
|
|
32
|
+
gs.id,
|
|
33
|
+
gs.symbol,
|
|
34
|
+
der.document_id,
|
|
35
|
+
der.start_line,
|
|
36
|
+
der.end_line,
|
|
37
|
+
(der.end_line - der.start_line + 1) AS loc,
|
|
38
|
+
d.relative_path
|
|
39
|
+
FROM global_symbols gs
|
|
40
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
41
|
+
JOIN documents d ON der.document_id = d.id
|
|
42
|
+
WHERE 1 = 1
|
|
43
|
+
${db.pathExclusionsFor('d')}
|
|
44
|
+
AND ${testFileExclusionSql('d')}
|
|
45
|
+
${db.symbolNoiseFor('gs')}
|
|
46
|
+
AND (der.end_line - der.start_line + 1) >= 3
|
|
47
|
+
AND (der.end_line - der.start_line + 1) <= ?
|
|
48
|
+
${scopeFilter}
|
|
49
|
+
ORDER BY d.relative_path`,
|
|
50
|
+
maxLoc,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const results: PassthroughCandidate[] = [];
|
|
54
|
+
|
|
55
|
+
for (const sym of symbols) {
|
|
56
|
+
if (db.isIgnored(sym.relative_path)) continue;
|
|
57
|
+
|
|
58
|
+
// 2. Count callees for this symbol
|
|
59
|
+
const callees = getCalleeRowsForSymbol(db, {
|
|
60
|
+
documentId: sym.document_id,
|
|
61
|
+
startLine: sym.start_line,
|
|
62
|
+
endLine: sym.end_line,
|
|
63
|
+
symbolId: sym.id,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Deduplicate by symbol (same callee may appear in multiple chunks)
|
|
67
|
+
const uniqueCallees = new Map<string, { symbol: string; file: string }>();
|
|
68
|
+
for (const c of callees) {
|
|
69
|
+
if (!uniqueCallees.has(c.symbol)) {
|
|
70
|
+
uniqueCallees.set(c.symbol, { symbol: c.symbol, file: c.file });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 3. Passthrough = exactly 1 unique callee
|
|
75
|
+
if (uniqueCallees.size !== 1) continue;
|
|
76
|
+
|
|
77
|
+
const [, callee] = [...uniqueCallees.entries()][0]!;
|
|
78
|
+
|
|
79
|
+
results.push({
|
|
80
|
+
symbol: sym.symbol,
|
|
81
|
+
shortName: shortenSymbol(sym.symbol),
|
|
82
|
+
file: sym.relative_path,
|
|
83
|
+
startLine: sym.start_line,
|
|
84
|
+
endLine: sym.end_line,
|
|
85
|
+
loc: sym.loc,
|
|
86
|
+
forwardsTo: callee.symbol,
|
|
87
|
+
forwardsToShort: shortenSymbol(callee.symbol),
|
|
88
|
+
forwardsToFile: callee.file,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
results.sort((a, b) => a.loc - b.loc || a.file.localeCompare(b.file));
|
|
93
|
+
return results.slice(0, limit);
|
|
94
|
+
}
|