scip-query 0.1.0 → 0.2.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.
- package/README.md +16 -43
- package/dist/chunk-2UELLEBI.js +1 -0
- package/dist/chunk-34JPTNRN.js +601 -0
- package/dist/{chunk-NDSQYIWT.js → chunk-3566TKJ5.js} +3 -3
- package/dist/{chunk-LB7OS35Q.js → chunk-4ACRRQC4.js} +8 -4
- package/dist/{chunk-3E2X7RIE.js → chunk-4BQFSNFI.js} +10 -6
- package/dist/{chunk-BP2ATLK2.js → chunk-6QSHLFSL.js} +4 -4
- package/dist/{chunk-5FGUEU7N.js → chunk-6WVR5K46.js} +18 -10
- package/dist/{chunk-XFXDXEUN.js → chunk-74RFWB5T.js} +2 -2
- package/dist/{chunk-MBVNHJVN.js → chunk-75RQSBTK.js} +2 -2
- package/dist/{chunk-YZAA4LYG.js → chunk-7HK5ZLOE.js} +30 -48
- package/dist/{chunk-T6ARFSBZ.js → chunk-7JFZSOJ7.js} +7 -7
- package/dist/{chunk-6SXADWLW.js → chunk-AKMBBKWV.js} +2 -2
- package/dist/{chunk-ZJRYBOEE.js → chunk-AMNISGYR.js} +5 -5
- package/dist/{chunk-CM454WL3.js → chunk-BFLULBEU.js} +3 -3
- package/dist/{chunk-Z73NYSBZ.js → chunk-CU62ZDHI.js} +2 -2
- package/dist/{chunk-TBP6BICL.js → chunk-DY4AFG2W.js} +13 -11
- package/dist/{chunk-2QZ23IBN.js → chunk-F7XU27LU.js} +4 -4
- package/dist/{chunk-KCBMVQL5.js → chunk-GPJVPT3U.js} +2 -2
- package/dist/{chunk-NUZ4OMU3.js → chunk-GU2H5QRN.js} +2 -2
- package/dist/{chunk-TSPZOMHC.js → chunk-H6WCPKCX.js} +6 -3
- package/dist/{chunk-KVSW5KYP.js → chunk-HDSRORNV.js} +4 -4
- package/dist/{chunk-LUSIFBXO.js → chunk-HMYJJ3HY.js} +9 -6
- package/dist/chunk-IJKLB2JW.js +69 -0
- package/dist/{chunk-6NBLIDF4.js → chunk-ITZ3DDOG.js} +2 -2
- package/dist/{chunk-GTILYBH6.js → chunk-IXPHLF6K.js} +6 -6
- package/dist/{chunk-BFSCMC22.js → chunk-KBOQX573.js} +3 -3
- package/dist/{chunk-FUHJCHS4.js → chunk-LLMPAG56.js} +95 -32
- package/dist/{chunk-FFSWWE5O.js → chunk-LTJC5ZQL.js} +3 -3
- package/dist/{chunk-LAWMH22O.js → chunk-M3NPW3FC.js} +2 -2
- package/dist/{chunk-VRUJH4BO.js → chunk-M4QGEKKD.js} +6 -28
- package/dist/{chunk-7OZPA5OO.js → chunk-MVH45PYK.js} +21 -41
- package/dist/chunk-N4C3H7LH.js +37 -0
- package/dist/chunk-NG5F43OU.js +200 -0
- package/dist/{chunk-6VJ6Q7IE.js → chunk-NVIIM34O.js} +4 -4
- package/dist/{chunk-GJFURBEW.js → chunk-ORINICIZ.js} +4 -4
- package/dist/{chunk-TDNNOR6D.js → chunk-PMJKOXOT.js} +7 -7
- package/dist/{chunk-QOV2R2WT.js → chunk-QIXNAB5K.js} +42 -2
- package/dist/{chunk-JKP5GH6T.js → chunk-R2I3M5B4.js} +2 -2
- package/dist/{chunk-36OMT7ZJ.js → chunk-R56FJU3E.js} +35 -14
- package/dist/{chunk-VZ7AMAFL.js → chunk-RFMT7UAZ.js} +3 -3
- package/dist/{chunk-SEFSL2GF.js → chunk-TOIEB3LG.js} +2 -2
- package/dist/chunk-VO4QI3LS.js +84 -0
- package/dist/{chunk-EMDQWNYR.js → chunk-WVK7AASK.js} +8 -8
- package/dist/{chunk-5WTJAXY2.js → chunk-Y3M323OX.js} +2 -2
- package/dist/{chunk-DCKMSTJ4.js → chunk-Y4JFVQ7C.js} +2 -2
- package/dist/{chunk-UNTPVD36.js → chunk-YAFWL3RA.js} +4 -4
- package/dist/{chunk-FGXRVW7G.js → chunk-YZ6L7GFO.js} +2 -2
- package/dist/cli.js +1401 -717
- package/dist/{db-BxaevAyc.d.ts → db-BHYam4BK.d.ts} +7 -19
- package/dist/index.d.ts +15 -15
- package/dist/index.js +263 -234
- package/dist/postinstall.js +5 -76
- package/dist/queries/affected.d.ts +1 -1
- package/dist/queries/affected.js +3 -3
- package/dist/queries/bottlenecks.d.ts +1 -1
- package/dist/queries/bottlenecks.js +2 -2
- package/dist/queries/by-kind.d.ts +1 -1
- package/dist/queries/by-kind.js +2 -2
- package/dist/queries/call-graph.d.ts +1 -1
- package/dist/queries/call-graph.js +3 -3
- package/dist/queries/change-surface.d.ts +2 -2
- package/dist/queries/change-surface.js +2 -3
- package/dist/queries/code.d.ts +1 -1
- package/dist/queries/code.js +3 -3
- package/dist/queries/complexity-hotspots.d.ts +1 -1
- package/dist/queries/complexity-hotspots.js +3 -3
- package/dist/queries/complexity.d.ts +1 -1
- package/dist/queries/complexity.js +3 -3
- package/dist/queries/convergence.d.ts +1 -1
- package/dist/queries/convergence.js +3 -3
- package/dist/queries/coupling.d.ts +1 -1
- package/dist/queries/coupling.js +1 -1
- package/dist/queries/cycles.d.ts +1 -1
- package/dist/queries/cycles.js +3 -2
- package/dist/queries/dataflow.d.ts +1 -1
- package/dist/queries/dataflow.js +3 -3
- package/dist/queries/dead.d.ts +1 -1
- package/dist/queries/dead.js +4 -3
- package/dist/queries/deep-chains.d.ts +1 -1
- package/dist/queries/deep-chains.js +3 -2
- package/dist/queries/deps.d.ts +1 -1
- package/dist/queries/diff-impact.d.ts +2 -2
- package/dist/queries/diff-impact.js +2 -3
- package/dist/queries/doc-coverage.d.ts +1 -1
- package/dist/queries/doc-coverage.js +2 -2
- package/dist/queries/drift.d.ts +1 -1
- package/dist/queries/drift.js +3 -2
- package/dist/queries/extract-candidates.d.ts +1 -1
- package/dist/queries/extract-candidates.js +3 -3
- package/dist/queries/fan.d.ts +1 -1
- package/dist/queries/fan.js +2 -2
- package/dist/queries/files.d.ts +1 -1
- package/dist/queries/health.d.ts +1 -1
- package/dist/queries/health.js +15 -15
- package/dist/queries/hierarchy.d.ts +1 -1
- package/dist/queries/hierarchy.js +3 -2
- package/dist/queries/hotspots.d.ts +1 -1
- package/dist/queries/hotspots.js +2 -2
- package/dist/queries/imports.d.ts +1 -1
- package/dist/queries/imports.js +3 -2
- package/dist/queries/index.d.ts +1 -2
- package/dist/queries/index.js +46 -51
- package/dist/queries/isolated.d.ts +1 -1
- package/dist/queries/isolated.js +4 -3
- package/dist/queries/members.d.ts +2 -2
- package/dist/queries/members.js +3 -2
- package/dist/queries/methods.d.ts +1 -1
- package/dist/queries/methods.js +2 -2
- package/dist/queries/outline.d.ts +1 -1
- package/dist/queries/outline.js +2 -2
- package/dist/queries/passthrough-candidates.d.ts +1 -1
- package/dist/queries/passthrough-candidates.js +3 -3
- package/dist/queries/redundant-reexports.d.ts +1 -1
- package/dist/queries/redundant-reexports.js +4 -2
- package/dist/queries/refs.d.ts +1 -1
- package/dist/queries/refs.js +1 -1
- package/dist/queries/similar-chains.d.ts +1 -1
- package/dist/queries/similar-chains.js +3 -2
- package/dist/queries/similar-files.d.ts +1 -1
- package/dist/queries/similar-files.js +3 -2
- package/dist/queries/similar-signatures.d.ts +1 -1
- package/dist/queries/similar-signatures.js +2 -2
- package/dist/queries/similar.d.ts +1 -1
- package/dist/queries/similar.js +3 -3
- package/dist/queries/slice.d.ts +1 -1
- package/dist/queries/slice.js +3 -3
- package/dist/queries/stale-abstractions.d.ts +1 -1
- package/dist/queries/stale-abstractions.js +3 -3
- package/dist/queries/stats.d.ts +1 -1
- package/dist/queries/stats.js +1 -1
- package/dist/queries/surface.d.ts +1 -1
- package/dist/queries/surface.js +2 -2
- package/dist/queries/symbols.d.ts +1 -1
- package/dist/queries/symbols.js +2 -2
- package/dist/queries/system.d.ts +1 -1
- package/dist/queries/system.js +2 -2
- package/dist/queries/trace.d.ts +1 -1
- package/dist/queries/trace.js +3 -1
- package/dist/queries/wrapper-candidates.d.ts +1 -1
- package/dist/queries/wrapper-candidates.js +3 -3
- package/dist/reindex-worker.js +24 -12
- package/package.json +6 -1
- package/IMPROVEMENTS.md +0 -143
- package/PLAN.md +0 -320
- package/dist/chunk-2QZ23IBN.js.map +0 -1
- package/dist/chunk-36OMT7ZJ.js.map +0 -1
- package/dist/chunk-3E2X7RIE.js.map +0 -1
- package/dist/chunk-3UOUTZQT.js +0 -45
- package/dist/chunk-3UOUTZQT.js.map +0 -1
- package/dist/chunk-3ZZJVBIO.js +0 -88
- package/dist/chunk-3ZZJVBIO.js.map +0 -1
- package/dist/chunk-4TYLS5XX.js.map +0 -1
- package/dist/chunk-5FGUEU7N.js.map +0 -1
- package/dist/chunk-5WTJAXY2.js.map +0 -1
- package/dist/chunk-6NBLIDF4.js.map +0 -1
- package/dist/chunk-6SXADWLW.js.map +0 -1
- package/dist/chunk-6VJ6Q7IE.js.map +0 -1
- package/dist/chunk-7OZPA5OO.js.map +0 -1
- package/dist/chunk-BEPIEVLR.js +0 -76
- package/dist/chunk-BEPIEVLR.js.map +0 -1
- package/dist/chunk-BFSCMC22.js.map +0 -1
- package/dist/chunk-BP2ATLK2.js.map +0 -1
- package/dist/chunk-CM454WL3.js.map +0 -1
- package/dist/chunk-DCKMSTJ4.js.map +0 -1
- package/dist/chunk-DEZKCZXD.js +0 -40
- package/dist/chunk-DEZKCZXD.js.map +0 -1
- package/dist/chunk-DVWGWHFW.js +0 -99
- package/dist/chunk-DVWGWHFW.js.map +0 -1
- package/dist/chunk-EMDQWNYR.js.map +0 -1
- package/dist/chunk-FFSWWE5O.js.map +0 -1
- package/dist/chunk-FGXRVW7G.js.map +0 -1
- package/dist/chunk-FUHJCHS4.js.map +0 -1
- package/dist/chunk-GJFURBEW.js.map +0 -1
- package/dist/chunk-GTILYBH6.js.map +0 -1
- package/dist/chunk-JJP7KQND.js +0 -1
- package/dist/chunk-JJP7KQND.js.map +0 -1
- package/dist/chunk-JKP5GH6T.js.map +0 -1
- package/dist/chunk-KCBMVQL5.js.map +0 -1
- package/dist/chunk-KVSW5KYP.js.map +0 -1
- package/dist/chunk-LAWMH22O.js.map +0 -1
- package/dist/chunk-LB7OS35Q.js.map +0 -1
- package/dist/chunk-LUSIFBXO.js.map +0 -1
- package/dist/chunk-MBVNHJVN.js.map +0 -1
- package/dist/chunk-MGNMHKX3.js.map +0 -1
- package/dist/chunk-N5KEREIA.js.map +0 -1
- package/dist/chunk-NDSQYIWT.js.map +0 -1
- package/dist/chunk-NUZ4OMU3.js.map +0 -1
- package/dist/chunk-QOV2R2WT.js.map +0 -1
- package/dist/chunk-SEFSL2GF.js.map +0 -1
- package/dist/chunk-T6ARFSBZ.js.map +0 -1
- package/dist/chunk-TBP6BICL.js.map +0 -1
- package/dist/chunk-TDNNOR6D.js.map +0 -1
- package/dist/chunk-TSPZOMHC.js.map +0 -1
- package/dist/chunk-UNTPVD36.js.map +0 -1
- package/dist/chunk-VRUJH4BO.js.map +0 -1
- package/dist/chunk-VZ7AMAFL.js.map +0 -1
- package/dist/chunk-XFXDXEUN.js.map +0 -1
- package/dist/chunk-YZAA4LYG.js.map +0 -1
- package/dist/chunk-Z73NYSBZ.js.map +0 -1
- package/dist/chunk-ZJRYBOEE.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/postinstall.js.map +0 -1
- package/dist/queries/affected.js.map +0 -1
- package/dist/queries/bottlenecks.js.map +0 -1
- package/dist/queries/by-kind.js.map +0 -1
- package/dist/queries/call-graph.js.map +0 -1
- package/dist/queries/change-surface.js.map +0 -1
- package/dist/queries/clean-signature.js.map +0 -1
- package/dist/queries/code.js.map +0 -1
- package/dist/queries/complexity-hotspots.js.map +0 -1
- package/dist/queries/complexity.js.map +0 -1
- package/dist/queries/convergence.js.map +0 -1
- package/dist/queries/coupling.js.map +0 -1
- package/dist/queries/cycles.js.map +0 -1
- package/dist/queries/dataflow.js.map +0 -1
- package/dist/queries/dead.js.map +0 -1
- package/dist/queries/deep-chains.js.map +0 -1
- package/dist/queries/deps.js.map +0 -1
- package/dist/queries/diff-impact.js.map +0 -1
- package/dist/queries/doc-coverage.js.map +0 -1
- package/dist/queries/drift.js.map +0 -1
- package/dist/queries/extract-candidates.js.map +0 -1
- package/dist/queries/fan.js.map +0 -1
- package/dist/queries/files.js.map +0 -1
- package/dist/queries/health.js.map +0 -1
- package/dist/queries/hierarchy.js.map +0 -1
- package/dist/queries/hotspots.js.map +0 -1
- package/dist/queries/imports.js.map +0 -1
- package/dist/queries/index.js.map +0 -1
- package/dist/queries/isolated.js.map +0 -1
- package/dist/queries/members.js.map +0 -1
- package/dist/queries/methods.js.map +0 -1
- package/dist/queries/outline.js.map +0 -1
- package/dist/queries/passthrough-candidates.js.map +0 -1
- package/dist/queries/redundant-reexports.js.map +0 -1
- package/dist/queries/refs.js.map +0 -1
- package/dist/queries/similar-chains.js.map +0 -1
- package/dist/queries/similar-files.js.map +0 -1
- package/dist/queries/similar-signatures.js.map +0 -1
- package/dist/queries/similar.js.map +0 -1
- package/dist/queries/slice.js.map +0 -1
- package/dist/queries/stale-abstractions.js.map +0 -1
- package/dist/queries/stats.js.map +0 -1
- package/dist/queries/surface.js.map +0 -1
- package/dist/queries/symbols.js.map +0 -1
- package/dist/queries/system.js.map +0 -1
- package/dist/queries/test-coverage.d.ts +0 -22
- package/dist/queries/test-coverage.js +0 -11
- package/dist/queries/test-coverage.js.map +0 -1
- package/dist/queries/trace.js.map +0 -1
- package/dist/queries/wrapper-candidates.js.map +0 -1
- package/dist/reindex-worker.js.map +0 -1
- package/docs/AGENT_GUIDE.md +0 -359
- package/reports/debloat/2026-04-10-scip-query-self-audit.md +0 -161
- package/src/cli.ts +0 -1480
- package/src/config.ts +0 -117
- package/src/db.ts +0 -127
- package/src/gitignore-filter.ts +0 -143
- package/src/index.ts +0 -11
- package/src/postinstall.ts +0 -8
- package/src/queries/affected.ts +0 -86
- package/src/queries/bottlenecks.ts +0 -67
- package/src/queries/by-kind.ts +0 -204
- package/src/queries/call-graph.ts +0 -66
- package/src/queries/change-surface.ts +0 -110
- package/src/queries/clean-signature.ts +0 -22
- package/src/queries/code.ts +0 -101
- package/src/queries/complexity-hotspots.ts +0 -119
- package/src/queries/complexity.ts +0 -152
- package/src/queries/convergence.ts +0 -82
- package/src/queries/coupling.ts +0 -99
- package/src/queries/cycles.ts +0 -78
- package/src/queries/dataflow.ts +0 -128
- package/src/queries/dead.ts +0 -122
- package/src/queries/deep-chains.ts +0 -59
- package/src/queries/deps.ts +0 -46
- package/src/queries/diff-impact.ts +0 -204
- package/src/queries/doc-coverage.ts +0 -86
- package/src/queries/drift.ts +0 -224
- package/src/queries/extract-candidates.ts +0 -167
- package/src/queries/fan.ts +0 -148
- package/src/queries/files.ts +0 -16
- package/src/queries/health.ts +0 -324
- package/src/queries/hierarchy.ts +0 -49
- package/src/queries/hotspots.ts +0 -53
- package/src/queries/imports.ts +0 -95
- package/src/queries/index.ts +0 -45
- package/src/queries/isolated.ts +0 -67
- package/src/queries/members.ts +0 -54
- package/src/queries/methods.ts +0 -27
- package/src/queries/outline.ts +0 -52
- package/src/queries/passthrough-candidates.ts +0 -94
- package/src/queries/redundant-reexports.ts +0 -170
- package/src/queries/refs.ts +0 -27
- package/src/queries/similar-chains.ts +0 -314
- package/src/queries/similar-files.ts +0 -140
- package/src/queries/similar-signatures.ts +0 -151
- package/src/queries/similar.ts +0 -305
- package/src/queries/slice.ts +0 -154
- package/src/queries/stale-abstractions.ts +0 -82
- package/src/queries/stats.ts +0 -22
- package/src/queries/surface.ts +0 -34
- package/src/queries/symbols.ts +0 -39
- package/src/queries/system.ts +0 -86
- package/src/queries/test-coverage.ts +0 -106
- package/src/queries/trace.ts +0 -55
- package/src/queries/wrapper-candidates.ts +0 -112
- package/src/query-support.ts +0 -226
- package/src/reindex/detect.ts +0 -58
- package/src/reindex/index.ts +0 -153
- package/src/reindex/indexers.ts +0 -220
- package/src/reindex/install.ts +0 -125
- package/src/reindex-worker.ts +0 -35
- package/src/setup.ts +0 -202
- package/src/symbol-parser.ts +0 -278
- package/src/types.ts +0 -654
- package/src/watch.ts +0 -274
- package/tests/gitignore-filter.test.ts +0 -48
- package/tests/queries.test.ts +0 -300
- package/tests/symbol-parser.test.ts +0 -157
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -40
- package/vitest.config.ts +0 -7
package/dist/chunk-3UOUTZQT.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseSymbol,
|
|
3
|
-
shortenSymbol
|
|
4
|
-
} from "./chunk-QOV2R2WT.js";
|
|
5
|
-
|
|
6
|
-
// src/queries/members.ts
|
|
7
|
-
function members(db, symbolPattern) {
|
|
8
|
-
const parents = db.all(
|
|
9
|
-
`SELECT symbol FROM global_symbols WHERE symbol LIKE ?`,
|
|
10
|
-
`%${symbolPattern}%`
|
|
11
|
-
);
|
|
12
|
-
if (parents.length === 0) return [];
|
|
13
|
-
const placeholders = parents.map(() => "?").join(",");
|
|
14
|
-
const parentSymbols = parents.map((p) => p.symbol);
|
|
15
|
-
const rows = db.all(
|
|
16
|
-
`SELECT gs.symbol, der.start_line, der.end_line
|
|
17
|
-
FROM global_symbols gs
|
|
18
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
19
|
-
WHERE gs.enclosing_symbol IN (${placeholders})
|
|
20
|
-
${db.symbolNoise}
|
|
21
|
-
ORDER BY der.start_line`,
|
|
22
|
-
...parentSymbols
|
|
23
|
-
);
|
|
24
|
-
return rows.map((r) => {
|
|
25
|
-
const parsed = parseSymbol(r.symbol);
|
|
26
|
-
let kind = "unknown";
|
|
27
|
-
if (!("kind" in parsed)) {
|
|
28
|
-
const sym = parsed;
|
|
29
|
-
const last = sym.descriptors[sym.descriptors.length - 1];
|
|
30
|
-
if (last) kind = last.suffix;
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
symbol: r.symbol,
|
|
34
|
-
shortName: shortenSymbol(r.symbol),
|
|
35
|
-
startLine: r.start_line,
|
|
36
|
-
endLine: r.end_line,
|
|
37
|
-
kind
|
|
38
|
-
};
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export {
|
|
43
|
-
members
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=chunk-3UOUTZQT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/members.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { MemberResult } from '../types.js';\nimport { shortenSymbol, parseSymbol } from '../symbol-parser.js';\nimport type { ScipSymbol } from '../types.js';\n\n/**\n * Find all direct children of a symbol (methods, fields, nested types).\n * Uses the enclosing_symbol relationship in global_symbols.\n */\nexport function members(db: ScipDatabase, symbolPattern: string): MemberResult[] {\n // First find the parent symbol(s)\n const parents = db.all<{ symbol: string }>(\n `SELECT symbol FROM global_symbols WHERE symbol LIKE ?`,\n `%${symbolPattern}%`,\n );\n\n if (parents.length === 0) return [];\n\n // Find children whose enclosing_symbol matches any parent\n const placeholders = parents.map(() => '?').join(',');\n const parentSymbols = parents.map((p) => p.symbol);\n\n const rows = db.all<{\n symbol: string;\n start_line: number;\n end_line: number;\n }>(\n `SELECT gs.symbol, der.start_line, der.end_line\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n WHERE gs.enclosing_symbol IN (${placeholders})\n ${db.symbolNoise}\n ORDER BY der.start_line`,\n ...parentSymbols,\n );\n\n return rows.map((r) => {\n const parsed = parseSymbol(r.symbol);\n let kind = 'unknown';\n if (!('kind' in parsed)) {\n const sym = parsed as ScipSymbol;\n const last = sym.descriptors[sym.descriptors.length - 1];\n if (last) kind = last.suffix;\n }\n\n return {\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n startLine: r.start_line,\n endLine: r.end_line,\n kind,\n };\n });\n}\n"],"mappings":";;;;;;AASO,SAAS,QAAQ,IAAkB,eAAuC;AAE/E,QAAM,UAAU,GAAG;AAAA,IACjB;AAAA,IACA,IAAI,aAAa;AAAA,EACnB;AAEA,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAGlC,QAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AACpD,QAAM,gBAAgB,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM;AAEjD,QAAM,OAAO,GAAG;AAAA,IAKd;AAAA;AAAA;AAAA,oCAGgC,YAAY;AAAA,QACxC,GAAG,WAAW;AAAA;AAAA,IAElB,GAAG;AAAA,EACL;AAEA,SAAO,KAAK,IAAI,CAAC,MAAM;AACrB,UAAM,SAAS,YAAY,EAAE,MAAM;AACnC,QAAI,OAAO;AACX,QAAI,EAAE,UAAU,SAAS;AACvB,YAAM,MAAM;AACZ,YAAM,OAAO,IAAI,YAAY,IAAI,YAAY,SAAS,CAAC;AACvD,UAAI,KAAM,QAAO,KAAK;AAAA,IACxB;AAEA,WAAO;AAAA,MACL,QAAQ,EAAE;AAAA,MACV,WAAW,cAAc,EAAE,MAAM;AAAA,MACjC,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,MACX;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
|
package/dist/chunk-3ZZJVBIO.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TEST_FILE_PATTERNS,
|
|
3
|
-
testFileExclusionSql,
|
|
4
|
-
testFileMatchSql
|
|
5
|
-
} from "./chunk-FUHJCHS4.js";
|
|
6
|
-
import {
|
|
7
|
-
shortenSymbol
|
|
8
|
-
} from "./chunk-QOV2R2WT.js";
|
|
9
|
-
|
|
10
|
-
// src/queries/test-coverage.ts
|
|
11
|
-
function testCoverage(db, symbolPattern) {
|
|
12
|
-
const syms = db.all(
|
|
13
|
-
`SELECT gs.id, gs.symbol, 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 gs.symbol LIKE ?
|
|
18
|
-
${db.pathExclusionsFor("d")}
|
|
19
|
-
${db.symbolNoiseFor("gs")}
|
|
20
|
-
ORDER BY d.relative_path`,
|
|
21
|
-
`%${symbolPattern}%`
|
|
22
|
-
);
|
|
23
|
-
const testPatternSql = testFileMatchSql("ref_d", TEST_FILE_PATTERNS);
|
|
24
|
-
return syms.filter((s) => !db.isIgnored(s.relative_path)).map((s) => {
|
|
25
|
-
const testFiles = db.all(
|
|
26
|
-
`SELECT DISTINCT ref_d.relative_path
|
|
27
|
-
FROM mentions m
|
|
28
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
29
|
-
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
30
|
-
WHERE m.symbol_id = ?
|
|
31
|
-
AND m.role = 0
|
|
32
|
-
AND (${testPatternSql})
|
|
33
|
-
ORDER BY ref_d.relative_path`,
|
|
34
|
-
s.id
|
|
35
|
-
).map((r) => r.relative_path);
|
|
36
|
-
return {
|
|
37
|
-
symbol: s.symbol,
|
|
38
|
-
shortName: shortenSymbol(s.symbol),
|
|
39
|
-
definedIn: s.relative_path,
|
|
40
|
-
testFiles,
|
|
41
|
-
covered: testFiles.length > 0
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
function testCoverageSummary(db, opts = {}) {
|
|
46
|
-
const { scope, minLoc = 3 } = opts;
|
|
47
|
-
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
48
|
-
const testPatternSql = testFileExclusionSql("d");
|
|
49
|
-
const symbols = db.all(
|
|
50
|
-
`SELECT gs.id
|
|
51
|
-
FROM global_symbols gs
|
|
52
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
53
|
-
JOIN documents d ON der.document_id = d.id
|
|
54
|
-
WHERE 1 = 1
|
|
55
|
-
${db.pathExclusionsFor("d")}
|
|
56
|
-
AND ${testPatternSql}
|
|
57
|
-
${db.symbolNoiseFor("gs")}
|
|
58
|
-
AND gs.symbol NOT LIKE '%#%'
|
|
59
|
-
AND (der.end_line - der.start_line + 1) >= ?
|
|
60
|
-
${scopeFilter}`,
|
|
61
|
-
minLoc
|
|
62
|
-
);
|
|
63
|
-
const testRefSql = testFileMatchSql("ref_d", TEST_FILE_PATTERNS);
|
|
64
|
-
let covered = 0;
|
|
65
|
-
for (const s of symbols) {
|
|
66
|
-
const hasTest = db.get(
|
|
67
|
-
`SELECT COUNT(*) AS c FROM mentions m
|
|
68
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
69
|
-
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
70
|
-
WHERE m.symbol_id = ? AND m.role = 0 AND (${testRefSql})`,
|
|
71
|
-
s.id
|
|
72
|
-
);
|
|
73
|
-
if (hasTest && hasTest.c > 0) covered++;
|
|
74
|
-
}
|
|
75
|
-
const total = symbols.length;
|
|
76
|
-
return {
|
|
77
|
-
total,
|
|
78
|
-
covered,
|
|
79
|
-
uncovered: total - covered,
|
|
80
|
-
percent: total > 0 ? Math.round(covered / total * 100) : 0
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export {
|
|
85
|
-
testCoverage,
|
|
86
|
-
testCoverageSummary
|
|
87
|
-
};
|
|
88
|
-
//# sourceMappingURL=chunk-3ZZJVBIO.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/test-coverage.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { TEST_FILE_PATTERNS, testFileExclusionSql, testFileMatchSql } from '../query-support.js';\nimport type { TestCoverageResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Check if a symbol is referenced by any test file.\n * Reports which test files cover (reference) each matching symbol.\n */\nexport function testCoverage(\n db: ScipDatabase,\n symbolPattern: string,\n): TestCoverageResult[] {\n // Find matching symbols\n const syms = db.all<{\n id: number;\n symbol: string;\n relative_path: string;\n }>(\n `SELECT gs.id, gs.symbol, d.relative_path\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE gs.symbol LIKE ?\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n ORDER BY d.relative_path`,\n `%${symbolPattern}%`,\n );\n\n const testPatternSql = testFileMatchSql('ref_d', TEST_FILE_PATTERNS);\n\n return syms\n .filter((s) => !db.isIgnored(s.relative_path))\n .map((s) => {\n // Find test files that reference this symbol\n const testFiles = db.all<{ relative_path: string }>(\n `SELECT DISTINCT ref_d.relative_path\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents ref_d ON c.document_id = ref_d.id\n WHERE m.symbol_id = ?\n AND m.role = 0\n AND (${testPatternSql})\n ORDER BY ref_d.relative_path`,\n s.id,\n ).map((r) => r.relative_path);\n\n return {\n symbol: s.symbol,\n shortName: shortenSymbol(s.symbol),\n definedIn: s.relative_path,\n testFiles,\n covered: testFiles.length > 0,\n };\n });\n}\n\n/**\n * Summary: what percentage of symbols in scope are referenced by test files?\n */\nexport function testCoverageSummary(\n db: ScipDatabase,\n opts: { scope?: string; minLoc?: number } = {},\n): { total: number; covered: number; uncovered: number; percent: number } {\n const { scope, minLoc = 3 } = opts;\n const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';\n const testPatternSql = testFileExclusionSql('d');\n\n const symbols = db.all<{ id: number }>(\n `SELECT gs.id\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n AND ${testPatternSql}\n ${db.symbolNoiseFor('gs')}\n AND gs.symbol NOT LIKE '%#%'\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}`,\n minLoc,\n );\n\n const testRefSql = testFileMatchSql('ref_d', TEST_FILE_PATTERNS);\n\n let covered = 0;\n for (const s of symbols) {\n const hasTest = db.get<{ c: number }>(\n `SELECT COUNT(*) AS c FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents ref_d ON c.document_id = ref_d.id\n WHERE m.symbol_id = ? AND m.role = 0 AND (${testRefSql})`,\n s.id,\n );\n if (hasTest && hasTest.c > 0) covered++;\n }\n\n const total = symbols.length;\n return {\n total,\n covered,\n uncovered: total - covered,\n percent: total > 0 ? Math.round((covered / total) * 100) : 0,\n };\n}\n"],"mappings":";;;;;;;;;;AASO,SAAS,aACd,IACA,eACsB;AAEtB,QAAM,OAAO,GAAG;AAAA,IAKd;AAAA;AAAA;AAAA;AAAA;AAAA,QAKI,GAAG,kBAAkB,GAAG,CAAC;AAAA,QACzB,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA,IAE3B,IAAI,aAAa;AAAA,EACnB;AAEA,QAAM,iBAAiB,iBAAiB,SAAS,kBAAkB;AAEnE,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,aAAa,CAAC,EAC5C,IAAI,CAAC,MAAM;AAEV,UAAM,YAAY,GAAG;AAAA,MACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAMS,cAAc;AAAA;AAAA,MAEvB,EAAE;AAAA,IACJ,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa;AAE5B,WAAO;AAAA,MACL,QAAQ,EAAE;AAAA,MACV,WAAW,cAAc,EAAE,MAAM;AAAA,MACjC,WAAW,EAAE;AAAA,MACb;AAAA,MACA,SAAS,UAAU,SAAS;AAAA,IAC9B;AAAA,EACF,CAAC;AACL;AAKO,SAAS,oBACd,IACA,OAA4C,CAAC,GAC2B;AACxE,QAAM,EAAE,OAAO,SAAS,EAAE,IAAI;AAC9B,QAAM,cAAc,QAAQ,8BAA8B,KAAK,OAAO;AACtE,QAAM,iBAAiB,qBAAqB,GAAG;AAE/C,QAAM,UAAU,GAAG;AAAA,IACjB;AAAA;AAAA;AAAA;AAAA;AAAA,QAKI,GAAG,kBAAkB,GAAG,CAAC;AAAA,YACrB,cAAc;AAAA,QAClB,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA,QAGvB,WAAW;AAAA,IACf;AAAA,EACF;AAEA,QAAM,aAAa,iBAAiB,SAAS,kBAAkB;AAE/D,MAAI,UAAU;AACd,aAAW,KAAK,SAAS;AACvB,UAAM,UAAU,GAAG;AAAA,MACjB;AAAA;AAAA;AAAA,mDAG6C,UAAU;AAAA,MACvD,EAAE;AAAA,IACJ;AACA,QAAI,WAAW,QAAQ,IAAI,EAAG;AAAA,EAChC;AAEA,QAAM,QAAQ,QAAQ;AACtB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ,IAAI,KAAK,MAAO,UAAU,QAAS,GAAG,IAAI;AAAA,EAC7D;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/clean-signature.ts"],"sourcesContent":["/**\n * Clean up the raw doc/signature string from the SCIP index.\n *\n * Shared across symbols, trace, and system queries.\n * Previously duplicated as cleanSig/cleanSignature in three files.\n */\nexport function cleanSignature(sig: string | null): string | null {\n if (!sig || !sig.trim()) return null;\n return sig\n .replace(/^```\\w*\\s*/, '')\n .replace(/\\s*```$/, '')\n .replace(/^\\(method\\)\\s*/, '')\n .replace(/^\\(property\\)\\s*/, '')\n .replace(/^\\(function\\)\\s*/, '')\n .replace(/^\\(class\\)\\s*/, '')\n .replace(/^\\(interface\\)\\s*/, '')\n .replace(/^\\(enum\\)\\s*/, '')\n .replace(/^\\(type alias\\)\\s*/, '')\n .replace(/^\\(const\\)\\s*/, '')\n .replace(/^\\(var\\)\\s*/, '')\n .trim() || null;\n}\n"],"mappings":";AAMO,SAAS,eAAe,KAAmC;AAChE,MAAI,CAAC,OAAO,CAAC,IAAI,KAAK,EAAG,QAAO;AAChC,SAAO,IACJ,QAAQ,cAAc,EAAE,EACxB,QAAQ,WAAW,EAAE,EACrB,QAAQ,kBAAkB,EAAE,EAC5B,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,iBAAiB,EAAE,EAC3B,QAAQ,qBAAqB,EAAE,EAC/B,QAAQ,gBAAgB,EAAE,EAC1B,QAAQ,sBAAsB,EAAE,EAChC,QAAQ,iBAAiB,EAAE,EAC3B,QAAQ,eAAe,EAAE,EACzB,KAAK,KAAK;AACf;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/dead.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { TEST_SUPPORT_PATH_PATTERNS, testFileExclusionSql } from '../query-support.js';\nimport type { DeadOptions, DeadSymbolResult, DeadSummary } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find dead exports: symbols defined locally with no cross-file references.\n * Language-agnostic — works with any SCIP index.\n */\nexport function dead(db: ScipDatabase, opts: DeadOptions = {}): DeadSummary {\n const {\n scope,\n minLoc = 1,\n includeTests = false,\n skipBarrels = false,\n includeMembers = false,\n } = opts;\n\n const params: unknown[] = [minLoc];\n let testFileExclusions = '';\n let memberExclusion = '';\n\n if (scope) {\n params.push(`%${scope}%`);\n }\n\n if (!includeTests) {\n testFileExclusions = `\n AND ${testFileExclusionSql('d', TEST_SUPPORT_PATH_PATTERNS)}\n `;\n }\n\n if (!includeMembers) {\n memberExclusion = `AND gs.symbol NOT LIKE '%#%'`;\n }\n\n // Barrel file exclusion for the NOT EXISTS subquery\n const barrelExclusions = skipBarrels\n ? `AND ref_d.relative_path NOT LIKE '%/index.ts'\n AND ref_d.relative_path NOT LIKE '%/index.js'\n AND ref_d.relative_path NOT LIKE '%/mod.rs'\n AND ref_d.relative_path NOT LIKE '%/__init__.py'`\n : '';\n\n const sql = `\n SELECT\n d.relative_path,\n der.start_line,\n der.end_line,\n (der.end_line - der.start_line + 1) AS loc,\n gs.symbol,\n (SELECT COUNT(*) FROM mentions m2\n JOIN chunks c2 ON m2.chunk_id = c2.id\n WHERE m2.symbol_id = gs.id AND m2.role = 0 AND c2.document_id = d.id\n ) AS same_file_refs\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n AND (der.end_line - der.start_line + 1) >= ?\n ${scope ? 'AND d.relative_path LIKE ?' : ''}\n ${testFileExclusions}\n ${memberExclusion}\n AND NOT EXISTS (\n SELECT 1\n FROM mentions ref_m\n JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id\n JOIN documents ref_d ON ref_c.document_id = ref_d.id\n WHERE ref_m.symbol_id = gs.id\n AND ref_m.role = 0\n AND ref_d.id != d.id\n ${barrelExclusions}\n )\n ORDER BY (der.end_line - der.start_line + 1) DESC, d.relative_path, der.start_line\n `;\n\n const rows = db.all<{\n relative_path: string;\n start_line: number;\n end_line: number;\n loc: number;\n symbol: string;\n same_file_refs: number;\n }>(sql, ...params);\n\n let deadCodeCount = 0;\n let fileInternalCount = 0;\n let totalLoc = 0;\n\n const symbols: DeadSymbolResult[] = rows\n .filter((r) => !db.isIgnored(r.relative_path))\n .map((r) => {\n // dead-code: zero references anywhere (not even in same file) — safe to delete\n // file-internal: referenced within same file but never cross-file —\n // may be a private helper (fine) or a forgotten export (needs review)\n const kind = r.same_file_refs === 0 ? 'dead-code' : 'file-internal';\n if (kind === 'dead-code') deadCodeCount++;\n else fileInternalCount++;\n totalLoc += r.loc;\n\n return {\n relativePath: r.relative_path,\n startLine: r.start_line,\n endLine: r.end_line,\n loc: r.loc,\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n sameFileRefs: r.same_file_refs,\n kind,\n };\n });\n\n return {\n symbols,\n totalCount: symbols.length,\n deadCodeCount,\n fileInternalCount,\n totalLoc,\n };\n}\n"],"mappings":";;;;;;;;;AASO,SAAS,KAAK,IAAkB,OAAoB,CAAC,GAAgB;AAC1E,QAAM;AAAA,IACJ;AAAA,IACA,SAAS;AAAA,IACT,eAAe;AAAA,IACf,cAAc;AAAA,IACd,iBAAiB;AAAA,EACnB,IAAI;AAEJ,QAAM,SAAoB,CAAC,MAAM;AACjC,MAAI,qBAAqB;AACzB,MAAI,kBAAkB;AAEtB,MAAI,OAAO;AACT,WAAO,KAAK,IAAI,KAAK,GAAG;AAAA,EAC1B;AAEA,MAAI,CAAC,cAAc;AACjB,yBAAqB;AAAA,YACb,qBAAqB,KAAK,0BAA0B,CAAC;AAAA;AAAA,EAE/D;AAEA,MAAI,CAAC,gBAAgB;AACnB,sBAAkB;AAAA,EACpB;AAGA,QAAM,mBAAmB,cACrB;AAAA;AAAA;AAAA,2DAIA;AAEJ,QAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAeN,GAAG,kBAAkB,GAAG,CAAC;AAAA,QACzB,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA,QAEvB,QAAQ,+BAA+B,EAAE;AAAA,QACzC,kBAAkB;AAAA,QAClB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YASX,gBAAgB;AAAA;AAAA;AAAA;AAK1B,QAAM,OAAO,GAAG,IAOb,KAAK,GAAG,MAAM;AAEjB,MAAI,gBAAgB;AACpB,MAAI,oBAAoB;AACxB,MAAI,WAAW;AAEf,QAAM,UAA8B,KACjC,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,aAAa,CAAC,EAC5C,IAAI,CAAC,MAAM;AAIV,UAAM,OAAO,EAAE,mBAAmB,IAAI,cAAc;AACpD,QAAI,SAAS,YAAa;AAAA,QACrB;AACL,gBAAY,EAAE;AAEd,WAAO;AAAA,MACL,cAAc,EAAE;AAAA,MAChB,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,MACX,KAAK,EAAE;AAAA,MACP,QAAQ,EAAE;AAAA,MACV,WAAW,cAAc,EAAE,MAAM;AAAA,MACjC,cAAc,EAAE;AAAA,MAChB;AAAA,IACF;AAAA,EACF,CAAC;AAEH,SAAO;AAAA,IACL;AAAA,IACA,YAAY,QAAQ;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/cycles.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { buildFileDepGraph } from '../query-support.js';\nimport type { CycleResult } from '../types.js';\n\n/**\n * Detect circular dependency chains between files.\n * A cycle exists when file A depends on B, B depends on C, and C depends on A.\n *\n * Uses the same dependency edges as the `deps` command (symbol definitions\n * referenced across files), then runs DFS cycle detection.\n */\nexport function cycles(\n db: ScipDatabase,\n opts: { scope?: string; maxDepth?: number } = {},\n): CycleResult[] {\n const { scope, maxDepth = 10 } = opts;\n const graph = buildFileDepGraph(db, scope);\n\n // DFS cycle detection\n const allCycles: CycleResult[] = [];\n const visited = new Set<string>();\n const inStack = new Set<string>();\n const stack: string[] = [];\n\n function dfs(node: string, depth: number): void {\n if (depth > maxDepth) return;\n if (inStack.has(node)) {\n // Found a cycle — extract it from the stack\n const cycleStart = stack.indexOf(node);\n if (cycleStart !== -1) {\n const cyclePath = stack.slice(cycleStart).concat(node);\n // Normalize: start from the lexicographically smallest file\n const minIdx = cyclePath.indexOf(\n cyclePath.reduce((a, b) => (a < b ? a : b)),\n );\n const normalized = [\n ...cyclePath.slice(minIdx, -1),\n ...cyclePath.slice(0, minIdx),\n cyclePath[minIdx]!,\n ];\n // Deduplicate\n const key = normalized.join(' -> ');\n if (!seenCycles.has(key)) {\n seenCycles.add(key);\n allCycles.push({ path: normalized });\n }\n }\n return;\n }\n if (visited.has(node)) return;\n\n visited.add(node);\n inStack.add(node);\n stack.push(node);\n\n const neighbors = graph.get(node);\n if (neighbors) {\n for (const neighbor of neighbors) {\n dfs(neighbor, depth + 1);\n }\n }\n\n stack.pop();\n inStack.delete(node);\n }\n\n const seenCycles = new Set<string>();\n for (const node of graph.keys()) {\n if (!visited.has(node)) {\n dfs(node, 0);\n }\n }\n\n // Sort by cycle length (shorter cycles are more actionable)\n allCycles.sort((a, b) => a.path.length - b.path.length);\n\n return allCycles;\n}\n"],"mappings":";;;;;AAWO,SAAS,OACd,IACA,OAA8C,CAAC,GAChC;AACf,QAAM,EAAE,OAAO,WAAW,GAAG,IAAI;AACjC,QAAM,QAAQ,kBAAkB,IAAI,KAAK;AAGzC,QAAM,YAA2B,CAAC;AAClC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,QAAkB,CAAC;AAEzB,WAAS,IAAI,MAAc,OAAqB;AAC9C,QAAI,QAAQ,SAAU;AACtB,QAAI,QAAQ,IAAI,IAAI,GAAG;AAErB,YAAM,aAAa,MAAM,QAAQ,IAAI;AACrC,UAAI,eAAe,IAAI;AACrB,cAAM,YAAY,MAAM,MAAM,UAAU,EAAE,OAAO,IAAI;AAErD,cAAM,SAAS,UAAU;AAAA,UACvB,UAAU,OAAO,CAAC,GAAG,MAAO,IAAI,IAAI,IAAI,CAAE;AAAA,QAC5C;AACA,cAAM,aAAa;AAAA,UACjB,GAAG,UAAU,MAAM,QAAQ,EAAE;AAAA,UAC7B,GAAG,UAAU,MAAM,GAAG,MAAM;AAAA,UAC5B,UAAU,MAAM;AAAA,QAClB;AAEA,cAAM,MAAM,WAAW,KAAK,MAAM;AAClC,YAAI,CAAC,WAAW,IAAI,GAAG,GAAG;AACxB,qBAAW,IAAI,GAAG;AAClB,oBAAU,KAAK,EAAE,MAAM,WAAW,CAAC;AAAA,QACrC;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,QAAQ,IAAI,IAAI,EAAG;AAEvB,YAAQ,IAAI,IAAI;AAChB,YAAQ,IAAI,IAAI;AAChB,UAAM,KAAK,IAAI;AAEf,UAAM,YAAY,MAAM,IAAI,IAAI;AAChC,QAAI,WAAW;AACb,iBAAW,YAAY,WAAW;AAChC,YAAI,UAAU,QAAQ,CAAC;AAAA,MACzB;AAAA,IACF;AAEA,UAAM,IAAI;AACV,YAAQ,OAAO,IAAI;AAAA,EACrB;AAEA,QAAM,aAAa,oBAAI,IAAY;AACnC,aAAW,QAAQ,MAAM,KAAK,GAAG;AAC/B,QAAI,CAAC,QAAQ,IAAI,IAAI,GAAG;AACtB,UAAI,MAAM,CAAC;AAAA,IACb;AAAA,EACF;AAGA,YAAU,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM;AAEtD,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/refs.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { RefResult } from '../types.js';\n\nexport function refs(db: ScipDatabase, symbolPattern: string): RefResult[] {\n const rows = db.all<{\n relative_path: string;\n start_line: number;\n }>(\n `SELECT DISTINCT d.relative_path, c.start_line\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d ON c.document_id = d.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n WHERE gs.symbol LIKE ?\n AND ${db.localSymbolPredicate}\n AND m.role = 0\n ORDER BY d.relative_path, c.start_line`,\n `%${symbolPattern}%`,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.relative_path))\n .map((r) => ({\n relativePath: r.relative_path,\n line: r.start_line,\n }));\n}\n"],"mappings":";AAGO,SAAS,KAAK,IAAkB,eAAoC;AACzE,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMQ,GAAG,oBAAoB;AAAA;AAAA;AAAA,IAG/B,IAAI,aAAa;AAAA,EACnB;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,aAAa,CAAC,EAC5C,IAAI,CAAC,OAAO;AAAA,IACX,cAAc,EAAE;AAAA,IAChB,MAAM,EAAE;AAAA,EACV,EAAE;AACN;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/outline.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { OutlineNode } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Build a tree-structured outline of symbols in a file,\n * using the enclosing_symbol field to establish parent-child relationships.\n */\nexport function outline(db: ScipDatabase, filePattern: string): OutlineNode[] {\n const rows = db.all<{\n symbol: string;\n enclosing_symbol: string | null;\n start_line: number;\n end_line: number;\n }>(\n `SELECT gs.symbol, gs.enclosing_symbol, der.start_line, der.end_line\n FROM defn_enclosing_ranges der\n JOIN global_symbols gs ON der.symbol_id = gs.id\n JOIN documents d ON der.document_id = d.id\n WHERE d.relative_path LIKE ?\n ${db.symbolNoise}\n ORDER BY der.start_line`,\n `%${filePattern}%`,\n );\n\n // Build a map of symbol -> node\n const nodeMap = new Map<string, OutlineNode>();\n const roots: OutlineNode[] = [];\n\n for (const r of rows) {\n const node: OutlineNode = {\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n startLine: r.start_line,\n endLine: r.end_line,\n children: [],\n };\n nodeMap.set(r.symbol, node);\n }\n\n // Wire up parent-child via enclosing_symbol\n for (const r of rows) {\n const node = nodeMap.get(r.symbol)!;\n if (r.enclosing_symbol && nodeMap.has(r.enclosing_symbol)) {\n nodeMap.get(r.enclosing_symbol)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n}\n"],"mappings":";;;;;AAQO,SAAS,QAAQ,IAAkB,aAAoC;AAC5E,QAAM,OAAO,GAAG;AAAA,IAMd;AAAA;AAAA;AAAA;AAAA;AAAA,QAKI,GAAG,WAAW;AAAA;AAAA,IAElB,IAAI,WAAW;AAAA,EACjB;AAGA,QAAM,UAAU,oBAAI,IAAyB;AAC7C,QAAM,QAAuB,CAAC;AAE9B,aAAW,KAAK,MAAM;AACpB,UAAM,OAAoB;AAAA,MACxB,QAAQ,EAAE;AAAA,MACV,WAAW,cAAc,EAAE,MAAM;AAAA,MACjC,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,MACX,UAAU,CAAC;AAAA,IACb;AACA,YAAQ,IAAI,EAAE,QAAQ,IAAI;AAAA,EAC5B;AAGA,aAAW,KAAK,MAAM;AACpB,UAAM,OAAO,QAAQ,IAAI,EAAE,MAAM;AACjC,QAAI,EAAE,oBAAoB,QAAQ,IAAI,EAAE,gBAAgB,GAAG;AACzD,cAAQ,IAAI,EAAE,gBAAgB,EAAG,SAAS,KAAK,IAAI;AAAA,IACrD,OAAO;AACL,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/affected.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { findFirstSymbolMatch } from '../query-support.js';\nimport type { AffectedResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Full transitive closure of symbols that could break if a given symbol changes.\n * BFS from the target through the mention graph: depth 1 = direct consumers,\n * depth 2 = consumers of consumers, etc.\n */\nexport function affected(\n db: ScipDatabase,\n symbolPattern: string,\n opts: { maxDepth?: number; scope?: string } = {},\n): AffectedResult[] {\n const { maxDepth = 5, scope } = opts;\n\n const target = findFirstSymbolMatch(db, symbolPattern);\n if (!target) return [];\n\n const scopeFilter = scope\n ? `AND enc_d.relative_path LIKE '%${scope}%'`\n : '';\n\n const results: AffectedResult[] = [];\n const visited = new Set<number>([target.symbolId]);\n let frontier = new Set<number>([target.symbolId]);\n\n for (let depth = 1; depth <= maxDepth; depth++) {\n if (frontier.size === 0) break;\n\n const placeholders = [...frontier].map(() => '?').join(',');\n const nextFrontier = new Set<number>();\n\n // For each symbol in the frontier, find enclosing symbols whose\n // definition ranges contain a reference (role=0) to that frontier symbol.\n const rows = db.all<{\n symbol_id: number;\n symbol: string;\n relative_path: string;\n }>(\n `SELECT DISTINCT\n enc_gs.id AS symbol_id,\n enc_gs.symbol AS symbol,\n enc_d.relative_path AS relative_path\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents ref_d ON c.document_id = ref_d.id\n JOIN defn_enclosing_ranges enc_der\n ON enc_der.document_id = ref_d.id\n AND c.start_line >= enc_der.start_line\n AND c.end_line <= enc_der.end_line\n JOIN global_symbols enc_gs ON enc_der.symbol_id = enc_gs.id\n JOIN documents enc_d ON enc_der.document_id = enc_d.id\n WHERE m.symbol_id IN (${placeholders})\n AND m.role = 0\n AND enc_gs.id NOT IN (${placeholders})\n ${db.symbolNoiseFor('enc_gs')}\n ${db.pathExclusionsFor('enc_d')}\n ${scopeFilter}`,\n ...[...frontier],\n ...[...frontier],\n );\n\n for (const row of rows) {\n if (visited.has(row.symbol_id)) continue;\n if (db.isIgnored(row.relative_path)) continue;\n\n visited.add(row.symbol_id);\n nextFrontier.add(row.symbol_id);\n\n results.push({\n symbol: row.symbol,\n shortName: shortenSymbol(row.symbol),\n file: row.relative_path,\n depth,\n });\n }\n\n frontier = nextFrontier;\n }\n\n // Sort by depth then file path\n results.sort((a, b) => a.depth - b.depth || a.file.localeCompare(b.file));\n return results;\n}\n"],"mappings":";;;;;;;;AAUO,SAAS,SACd,IACA,eACA,OAA8C,CAAC,GAC7B;AAClB,QAAM,EAAE,WAAW,GAAG,MAAM,IAAI;AAEhC,QAAM,SAAS,qBAAqB,IAAI,aAAa;AACrD,MAAI,CAAC,OAAQ,QAAO,CAAC;AAErB,QAAM,cAAc,QAChB,kCAAkC,KAAK,OACvC;AAEJ,QAAM,UAA4B,CAAC;AACnC,QAAM,UAAU,oBAAI,IAAY,CAAC,OAAO,QAAQ,CAAC;AACjD,MAAI,WAAW,oBAAI,IAAY,CAAC,OAAO,QAAQ,CAAC;AAEhD,WAAS,QAAQ,GAAG,SAAS,UAAU,SAAS;AAC9C,QAAI,SAAS,SAAS,EAAG;AAEzB,UAAM,eAAe,CAAC,GAAG,QAAQ,EAAE,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAC1D,UAAM,eAAe,oBAAI,IAAY;AAIrC,UAAM,OAAO,GAAG;AAAA,MAKd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAawB,YAAY;AAAA;AAAA,gCAEV,YAAY;AAAA,UAClC,GAAG,eAAe,QAAQ,CAAC;AAAA,UAC3B,GAAG,kBAAkB,OAAO,CAAC;AAAA,UAC7B,WAAW;AAAA,MACf,GAAG,CAAC,GAAG,QAAQ;AAAA,MACf,GAAG,CAAC,GAAG,QAAQ;AAAA,IACjB;AAEA,eAAW,OAAO,MAAM;AACtB,UAAI,QAAQ,IAAI,IAAI,SAAS,EAAG;AAChC,UAAI,GAAG,UAAU,IAAI,aAAa,EAAG;AAErC,cAAQ,IAAI,IAAI,SAAS;AACzB,mBAAa,IAAI,IAAI,SAAS;AAE9B,cAAQ,KAAK;AAAA,QACX,QAAQ,IAAI;AAAA,QACZ,WAAW,cAAc,IAAI,MAAM;AAAA,QACnC,MAAM,IAAI;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AAEA,eAAW;AAAA,EACb;AAGA,UAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACxE,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/health.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { dead } from './dead.js';\nimport { isolated } from './isolated.js';\nimport { cycles } from './cycles.js';\nimport { similarAll } from './similar.js';\nimport { extractCandidates } from './extract-candidates.js';\nimport { wrapperCandidates } from './wrapper-candidates.js';\nimport { passthroughCandidates } from './passthrough-candidates.js';\nimport { staleAbstractions } from './stale-abstractions.js';\nimport { drift } from './drift.js';\nimport { complexityHotspots } from './complexity-hotspots.js';\nimport { testCoverageSummary } from './test-coverage.js';\nimport { stats } from './stats.js';\nimport type { HealthAction, HealthReport } from '../types.js';\n\n/**\n * Single composite health report that runs all de-bloat analyses\n * and produces a prioritized action list.\n *\n * The scoring formula accounts for common false positives:\n * - Entry points (CLI, workers, barrels) appearing as \"dead code\"\n * - Typed result interfaces with 1 consumer (normal for APIs)\n * - Consistent import patterns across sibling modules (not duplication)\n * - Barrel and orchestrator files deviating from sibling patterns (expected)\n */\nexport function health(\n db: ScipDatabase,\n opts: { scope?: string } = {},\n): HealthReport {\n const { scope } = opts;\n\n // Run all analyses\n const s = stats(db);\n const deadResult = dead(db, { scope, minLoc: 3, skipBarrels: false });\n const isolatedResult = isolated(db, { scope, minLoc: 3 });\n const cycleResult = cycles(db, { scope });\n const similarResult = similarAll(db, { scope, minSimilarity: 0.6, limit: 50, minCallees: 4 });\n const extractResult = extractCandidates(db, { scope, minLoc: 15, minCallees: 5, limit: 50 });\n const wrapperResult = wrapperCandidates(db, { scope, maxLoc: 15, limit: 50 });\n const passthroughResult = passthroughCandidates(db, { scope, maxLoc: 15, limit: 50 });\n const staleResult = staleAbstractions(db, { scope, minLoc: 3, limit: 50 });\n const driftResult = drift(db, { scope });\n const complexResult = complexityHotspots(db, { scope, minLoc: 10, limit: 10 });\n const testResult = testCoverageSummary(db, { scope, minLoc: 3 });\n\n const isolatedLoc = isolatedResult.reduce((sum, r) => sum + r.loc, 0);\n\n // ── False-positive filtering ─────────────────────────────\n\n // Entry points and barrels appear as dead/isolated because nothing imports them.\n // Filter them out of the scoring (but still report them with a note).\n const entryPointPatterns = ['/index.ts', '/index.js', 'cli.ts', 'worker.ts', 'postinstall.ts', '/mod.rs', '__init__.py', 'main.ts', 'main.rs', 'main.go', 'main.py'];\n const isEntryPoint = (path: string) => entryPointPatterns.some((p) => path.endsWith(p));\n\n // Dead code: only count truly dead symbols (zero refs anywhere),\n // excluding entry points AND file-internal helpers (which are fine).\n const trueDeadSymbols = deadResult.symbols.filter(\n (s) => !isEntryPoint(s.relativePath) && s.kind === 'dead-code',\n );\n const trueDeadCount = trueDeadSymbols.length;\n const trueDeadLoc = trueDeadSymbols.reduce((sum, s) => sum + s.loc, 0);\n const fileInternalCount = deadResult.symbols.filter(\n (s) => !isEntryPoint(s.relativePath) && s.kind === 'file-internal',\n ).length;\n\n // Isolated: same entry-point filtering\n const trueIsolatedCount = isolatedResult.filter(\n (s) => !isEntryPoint(s.relativePath),\n ).length;\n\n // Stale abstractions: the command filters out types.ts single-consumer types.\n // Also filter out 0-consumer types in files that export functions — these are\n // likely parameter/return types consumed through function signatures, which\n // the SCIP index can't track as direct mentions.\n const filesWithFunctions = new Set(\n db.all<{ relative_path: string }>(\n `SELECT DISTINCT d.relative_path\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE gs.symbol LIKE '%().'\n ${db.pathExclusionsFor('d')}`,\n ).map((r) => r.relative_path),\n );\n const trueStaleCount = staleResult.filter((s) => {\n // 0-consumer types in files with functions are likely param/return types\n if (s.consumers === 0 && filesWithFunctions.has(s.file)) return false;\n return true;\n }).length;\n\n // Drift: now uses usage-based detection (unused imports, layer violations, pattern deviations)\n // The drift command already filters structural roles internally.\n const trueDriftCount = driftResult.results.length;\n\n // Similar pairs: the similar command now uses TF-IDF weighted cosine\n // similarity which automatically discounts infrastructure callees.\n // The sharedCallees list only contains significant (above-median IDF) callees.\n // We can trust the count directly.\n const trueSimilarCount = similarResult.length;\n\n // ── Build prioritized action list ────────────────────────\n\n const actions: HealthAction[] = [];\n\n if (trueDeadCount > 0) {\n actions.push({\n category: 'Dead code',\n description: `${trueDeadCount} symbols with zero references anywhere — safe to delete`,\n effort: 'low',\n impact: 'high',\n count: trueDeadCount,\n locRecoverable: trueDeadLoc,\n });\n }\n\n if (testResult.percent < 50) {\n actions.push({\n category: 'Test coverage',\n description: `${testResult.percent}% of symbols referenced by tests (${testResult.uncovered} uncovered)`,\n effort: 'high',\n impact: 'high',\n count: testResult.uncovered,\n locRecoverable: 0,\n });\n }\n\n if (trueIsolatedCount > 0) {\n actions.push({\n category: 'Isolated symbols',\n description: `${trueIsolatedCount} symbols completely disconnected from the codebase graph`,\n effort: 'low',\n impact: 'medium',\n count: trueIsolatedCount,\n locRecoverable: isolatedResult\n .filter((s) => !isEntryPoint(s.relativePath))\n .reduce((sum, s) => sum + s.loc, 0),\n });\n }\n\n if (cycleResult.length > 0) {\n actions.push({\n category: 'Circular dependencies',\n description: `${cycleResult.length} cycle(s) — break with dependency inversion or module restructuring`,\n effort: 'medium',\n impact: 'high',\n count: cycleResult.length,\n locRecoverable: 0,\n });\n }\n\n if (trueSimilarCount > 0) {\n actions.push({\n category: 'Similar functions',\n description: `${trueSimilarCount} pairs with real logic overlap (beyond shared imports) — consolidation candidates`,\n effort: 'medium',\n impact: 'medium',\n count: trueSimilarCount,\n locRecoverable: 0,\n });\n }\n\n if (extractResult.length > 0) {\n actions.push({\n category: 'Extraction candidates',\n description: `${extractResult.length} large functions with isolated callee clusters — extract method opportunities`,\n effort: 'medium',\n impact: 'medium',\n count: extractResult.length,\n locRecoverable: 0,\n });\n }\n\n if (wrapperResult.length > 0) {\n actions.push({\n category: 'Wrapper functions',\n description: `${wrapperResult.length} single-consumer symbols that could be inlined`,\n effort: 'low',\n impact: 'low',\n count: wrapperResult.length,\n locRecoverable: wrapperResult.reduce((sum, r) => sum + r.loc, 0),\n });\n }\n\n if (passthroughResult.length > 0) {\n actions.push({\n category: 'Passthrough functions',\n description: `${passthroughResult.length} functions that just forward to one callee — unnecessary indirection`,\n effort: 'low',\n impact: 'low',\n count: passthroughResult.length,\n locRecoverable: passthroughResult.reduce((sum, r) => sum + r.loc, 0),\n });\n }\n\n if (trueStaleCount > 0) {\n // Count from the filtered set, not the raw result\n const trueStaleSymbols = staleResult.filter((s) => {\n if (s.consumers === 0 && filesWithFunctions.has(s.file)) return false;\n return true;\n });\n const unused = trueStaleSymbols.filter((s) => s.consumers === 0).length;\n const singleUse = trueStaleCount - unused;\n const parts: string[] = [];\n if (unused > 0) parts.push(`${unused} unused`);\n if (singleUse > 0) parts.push(`${singleUse} single-consumer (not in types file)`);\n actions.push({\n category: 'Stale abstractions',\n description: `${parts.join(', ')} — premature abstraction`,\n effort: 'low',\n impact: 'medium',\n count: trueStaleCount,\n locRecoverable: staleResult\n .filter((s) => s.consumers === 0 || !s.file.includes('types'))\n .reduce((sum, r) => sum + r.loc, 0),\n });\n }\n\n if (trueDriftCount > 0) {\n const parts: string[] = [];\n if (driftResult.unusedImports > 0) parts.push(`${driftResult.unusedImports} unused imports`);\n if (driftResult.layerViolations > 0) parts.push(`${driftResult.layerViolations} layer violations`);\n if (driftResult.patternDeviations > 0) parts.push(`${driftResult.patternDeviations} unique deps`);\n actions.push({\n category: 'Structural drift',\n description: parts.join(', '),\n effort: driftResult.layerViolations > 0 ? 'medium' : 'low',\n impact: driftResult.layerViolations > 0 ? 'medium' : 'low',\n count: trueDriftCount,\n locRecoverable: 0,\n });\n }\n\n // Sort: high impact + low effort first\n const impactWeight = { high: 3, medium: 2, low: 1 };\n const effortWeight = { low: 3, medium: 2, high: 1 };\n actions.sort((a, b) => {\n const scoreA = impactWeight[a.impact] * effortWeight[a.effort];\n const scoreB = impactWeight[b.impact] * effortWeight[b.effort];\n return scoreB - scoreA;\n });\n\n // ── Compute health score (0-100) ─────────────────────────\n //\n // Uses filtered counts (false positives removed).\n // Deductions scale with codebase size so a 10-file project\n // and a 1000-file project aren't penalized the same way.\n const fileCount = Math.max(s.documents, 1);\n const symbolCount = Math.max(s.symbols, 1);\n\n let score = 100;\n\n // Dead code: deduct based on % of symbols that are dead, not raw count\n const deadPercent = trueDeadCount / symbolCount;\n score -= Math.min(20, Math.round(deadPercent * 200));\n\n // Isolated: same percentage-based\n const isolatedPercent = trueIsolatedCount / symbolCount;\n score -= Math.min(10, Math.round(isolatedPercent * 200));\n\n // Cycles: these are always bad, flat penalty\n score -= Math.min(15, cycleResult.length * 5);\n\n // Similar pairs: only count true logic overlap, not boilerplate\n score -= Math.min(10, trueSimilarCount * 2);\n\n // Extract candidates: mild penalty\n score -= Math.min(5, extractResult.length * 2);\n\n // Wrappers: mild\n score -= Math.min(3, wrapperResult.length);\n\n // Passthroughs: mild\n score -= Math.min(3, passthroughResult.length);\n\n // Stale abstractions: percentage-based with filtered count\n const stalePercent = trueStaleCount / Math.max(symbolCount * 0.1, 1);\n score -= Math.min(8, Math.round(stalePercent * 10));\n\n // Drift: percentage of files that deviate\n const driftPercent = trueDriftCount / fileCount;\n score -= Math.min(5, Math.round(driftPercent * 50));\n\n // Complexity: only penalize extreme outliers\n const extremeComplexity = complexResult.filter((r) => r.score > 50).length;\n score -= Math.min(5, extremeComplexity * 2);\n\n // Test coverage: significant penalty for low coverage\n // 0% = -15, 25% = -11, 50% = -7, 75% = -4, 100% = 0\n const coverageDeduction = Math.round(15 * (1 - testResult.percent / 100));\n score -= coverageDeduction;\n\n score = Math.max(0, Math.min(100, score));\n\n return {\n score,\n overview: {\n documents: s.documents,\n symbols: s.symbols,\n indexSizeBytes: s.indexSizeBytes,\n },\n findings: {\n deadSymbols: trueDeadCount,\n deadLoc: trueDeadLoc,\n isolatedSymbols: trueIsolatedCount,\n isolatedLoc: isolatedResult\n .filter((s) => !isEntryPoint(s.relativePath))\n .reduce((sum, s) => sum + s.loc, 0),\n cycles: cycleResult.length,\n similarPairs: trueSimilarCount,\n extractionCandidates: extractResult.length,\n wrappers: wrapperResult.length,\n passthroughs: passthroughResult.length,\n staleTypes: trueStaleCount,\n driftedFiles: trueDriftCount,\n complexityHotspotCount: complexResult.length,\n testCoveragePercent: testResult.percent,\n },\n actions,\n topComplexity: complexResult.slice(0, 5).map((r) => ({\n symbol: r.shortName,\n score: r.score,\n })),\n };\n}\n"],"mappings":"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","names":["s"]}
|
package/dist/chunk-BEPIEVLR.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
shortenSymbol
|
|
3
|
-
} from "./chunk-QOV2R2WT.js";
|
|
4
|
-
|
|
5
|
-
// src/queries/imports.ts
|
|
6
|
-
function imports(db, filePattern) {
|
|
7
|
-
const rows = db.all(
|
|
8
|
-
`SELECT DISTINCT gs.symbol, def_d.relative_path AS from_file
|
|
9
|
-
FROM mentions m
|
|
10
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
11
|
-
JOIN documents imp_d ON c.document_id = imp_d.id
|
|
12
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
13
|
-
LEFT JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
14
|
-
LEFT JOIN documents def_d ON der.document_id = def_d.id
|
|
15
|
-
WHERE imp_d.relative_path LIKE ?
|
|
16
|
-
AND m.role = 2
|
|
17
|
-
ORDER BY def_d.relative_path, gs.symbol`,
|
|
18
|
-
`%${filePattern}%`
|
|
19
|
-
);
|
|
20
|
-
return rows.map((r) => ({
|
|
21
|
-
symbol: r.symbol,
|
|
22
|
-
shortName: shortenSymbol(r.symbol),
|
|
23
|
-
fromFile: r.from_file ?? "(external)"
|
|
24
|
-
}));
|
|
25
|
-
}
|
|
26
|
-
function importedBy(db, symbolPattern) {
|
|
27
|
-
const rows = db.all(
|
|
28
|
-
`SELECT DISTINCT gs.symbol, d.relative_path AS importer
|
|
29
|
-
FROM mentions m
|
|
30
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
31
|
-
JOIN documents d ON c.document_id = d.id
|
|
32
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
33
|
-
WHERE gs.symbol LIKE ?
|
|
34
|
-
AND m.role = 2
|
|
35
|
-
ORDER BY d.relative_path`,
|
|
36
|
-
`%${symbolPattern}%`
|
|
37
|
-
);
|
|
38
|
-
return rows.filter((r) => !db.isIgnored(r.importer)).map((r) => ({
|
|
39
|
-
symbol: r.symbol,
|
|
40
|
-
shortName: shortenSymbol(r.symbol),
|
|
41
|
-
fromFile: r.importer
|
|
42
|
-
}));
|
|
43
|
-
}
|
|
44
|
-
function unusedImports(db, filePattern) {
|
|
45
|
-
const rows = db.all(
|
|
46
|
-
`SELECT gs.symbol, d.relative_path AS imported_in
|
|
47
|
-
FROM mentions m
|
|
48
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
49
|
-
JOIN documents d ON c.document_id = d.id
|
|
50
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
51
|
-
WHERE d.relative_path LIKE ?
|
|
52
|
-
AND m.role = 2
|
|
53
|
-
AND NOT EXISTS (
|
|
54
|
-
SELECT 1
|
|
55
|
-
FROM mentions ref_m
|
|
56
|
-
JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
|
|
57
|
-
WHERE ref_m.symbol_id = gs.id
|
|
58
|
-
AND ref_m.role = 0
|
|
59
|
-
AND ref_c.document_id = d.id
|
|
60
|
-
)
|
|
61
|
-
ORDER BY d.relative_path, gs.symbol`,
|
|
62
|
-
`%${filePattern}%`
|
|
63
|
-
);
|
|
64
|
-
return rows.map((r) => ({
|
|
65
|
-
symbol: r.symbol,
|
|
66
|
-
shortName: shortenSymbol(r.symbol),
|
|
67
|
-
importedIn: r.imported_in
|
|
68
|
-
}));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export {
|
|
72
|
-
imports,
|
|
73
|
-
importedBy,
|
|
74
|
-
unusedImports
|
|
75
|
-
};
|
|
76
|
-
//# sourceMappingURL=chunk-BEPIEVLR.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/imports.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { ImportResult, UnusedImportResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * What symbols does this file import?\n * Uses role=2 (import) from the SCIP mentions table.\n */\nexport function imports(db: ScipDatabase, filePattern: string): ImportResult[] {\n const rows = db.all<{\n symbol: string;\n from_file: string;\n }>(\n `SELECT DISTINCT gs.symbol, def_d.relative_path AS from_file\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents imp_d ON c.document_id = imp_d.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n LEFT JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n LEFT JOIN documents def_d ON der.document_id = def_d.id\n WHERE imp_d.relative_path LIKE ?\n AND m.role = 2\n ORDER BY def_d.relative_path, gs.symbol`,\n `%${filePattern}%`,\n );\n\n return rows.map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n fromFile: r.from_file ?? '(external)',\n }));\n}\n\n/**\n * Which files import this symbol?\n */\nexport function importedBy(db: ScipDatabase, symbolPattern: string): ImportResult[] {\n const rows = db.all<{\n symbol: string;\n importer: string;\n }>(\n `SELECT DISTINCT gs.symbol, d.relative_path AS importer\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d ON c.document_id = d.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n WHERE gs.symbol LIKE ?\n AND m.role = 2\n ORDER BY d.relative_path`,\n `%${symbolPattern}%`,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.importer))\n .map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n fromFile: r.importer,\n }));\n}\n\n/**\n * Find imports in a file that are never referenced (role=0) in the same file.\n * These are likely unused imports.\n */\nexport function unusedImports(db: ScipDatabase, filePattern: string): UnusedImportResult[] {\n const rows = db.all<{\n symbol: string;\n imported_in: string;\n }>(\n `SELECT gs.symbol, d.relative_path AS imported_in\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d ON c.document_id = d.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n WHERE d.relative_path LIKE ?\n AND m.role = 2\n AND NOT EXISTS (\n SELECT 1\n FROM mentions ref_m\n JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id\n WHERE ref_m.symbol_id = gs.id\n AND ref_m.role = 0\n AND ref_c.document_id = d.id\n )\n ORDER BY d.relative_path, gs.symbol`,\n `%${filePattern}%`,\n );\n\n return rows.map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n importedIn: r.imported_in,\n }));\n}\n"],"mappings":";;;;;AAQO,SAAS,QAAQ,IAAkB,aAAqC;AAC7E,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,IAAI,WAAW;AAAA,EACjB;AAEA,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,UAAU,EAAE,aAAa;AAAA,EAC3B,EAAE;AACJ;AAKO,SAAS,WAAW,IAAkB,eAAuC;AAClF,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,IAAI,aAAa;AAAA,EACnB;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,QAAQ,CAAC,EACvC,IAAI,CAAC,OAAO;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,UAAU,EAAE;AAAA,EACd,EAAE;AACN;AAMO,SAAS,cAAc,IAAkB,aAA2C;AACzF,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBA,IAAI,WAAW;AAAA,EACjB;AAEA,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,YAAY,EAAE;AAAA,EAChB,EAAE;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/hotspots.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { HotspotResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find the most-referenced symbols in the codebase — the choke points\n * where changes have the widest blast radius.\n */\nexport function hotspots(\n db: ScipDatabase,\n opts: { limit?: number; scope?: string } = {},\n): HotspotResult[] {\n const { limit = 30, scope } = opts;\n\n const scopeFilter = scope ? `AND def_d.relative_path LIKE '%${scope}%'` : '';\n\n const rows = db.all<{\n symbol: string;\n ref_count: number;\n file_count: number;\n defined_in: string;\n }>(\n `SELECT\n gs.symbol,\n COUNT(*) AS ref_count,\n COUNT(DISTINCT ref_d.id) AS file_count,\n def_d.relative_path AS defined_in\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents ref_d ON c.document_id = ref_d.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents def_d ON der.document_id = def_d.id\n WHERE m.role = 0\n ${db.pathExclusionsFor('def_d')}\n ${db.symbolNoiseFor('gs')}\n ${scopeFilter}\n GROUP BY gs.id\n ORDER BY ref_count DESC\n LIMIT ?`,\n limit,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.defined_in))\n .map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n refCount: r.ref_count,\n fileCount: r.file_count,\n definedIn: r.defined_in,\n }));\n}\n"],"mappings":";;;;;AAQO,SAAS,SACd,IACA,OAA2C,CAAC,GAC3B;AACjB,QAAM,EAAE,QAAQ,IAAI,MAAM,IAAI;AAE9B,QAAM,cAAc,QAAQ,kCAAkC,KAAK,OAAO;AAE1E,QAAM,OAAO,GAAG;AAAA,IAMd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAYI,GAAG,kBAAkB,OAAO,CAAC;AAAA,QAC7B,GAAG,eAAe,IAAI,CAAC;AAAA,QACvB,WAAW;AAAA;AAAA;AAAA;AAAA,IAIf;AAAA,EACF;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,UAAU,CAAC,EACzC,IAAI,CAAC,OAAO;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,UAAU,EAAE;AAAA,IACZ,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,EACf,EAAE;AACN;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/complexity.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ScipDatabase } from '../db.js';\nimport { findFirstSymbolMatch, getCalleeRowsForSymbol } from '../query-support.js';\nimport type { ComplexityResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Per-symbol complexity analysis combining source-level branch counting\n * with index-level metrics (fan-in, fan-out, callee count).\n *\n * Branch counting uses language-aware regex. The language is read from\n * the SCIP documents table, so it works for any indexed language.\n */\nexport function complexity(\n db: ScipDatabase,\n symbolPattern: string,\n): ComplexityResult | null {\n const match = findFirstSymbolMatch(db, symbolPattern);\n if (!match) return null;\n\n // Get language\n const doc = db.get<{ language: string | null }>(\n `SELECT language FROM documents WHERE relative_path = ?`,\n match.relativePath,\n );\n const language = doc?.language ?? 'unknown';\n\n // Read source for branch counting\n const filePath = join(db.config.projectRoot, match.relativePath);\n let source = '';\n try {\n const lines = readFileSync(filePath, 'utf-8').split('\\n');\n source = lines.slice(match.startLine, match.endLine + 1).join('\\n');\n } catch {\n // If we can't read the file, just skip branch counting\n }\n\n const branches = countBranches(source, language);\n const loc = match.endLine - match.startLine + 1;\n\n // Callee count\n const callees = getCalleeRowsForSymbol(db, match);\n const uniqueCallees = new Set(callees.map((c) => c.symbol));\n\n // Fan-in\n const fanInRow = db.get<{ c: number }>(\n `SELECT COUNT(DISTINCT c.document_id) AS c\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n WHERE m.symbol_id = ? AND m.role = 0`,\n match.symbolId,\n );\n\n // Fan-out (callees in other files)\n const fanOut = new Set(\n callees.filter((c) => c.file !== match.relativePath).map((c) => c.symbol),\n ).size;\n\n return {\n symbol: match.symbol,\n shortName: shortenSymbol(match.symbol),\n relativePath: match.relativePath,\n startLine: match.startLine,\n endLine: match.endLine,\n loc,\n branches,\n cyclomaticEstimate: branches + 1,\n calleeCount: uniqueCallees.size,\n fanIn: fanInRow?.c ?? 0,\n fanOut,\n };\n}\n\n/**\n * Count branch points in source code using language-aware regex.\n * Works across all SCIP-supported languages.\n */\nfunction countBranches(source: string, language: string): number {\n // Strip comments and strings to avoid false positives\n const stripped = stripCommentsAndStrings(source);\n let count = 0;\n\n // Universal branch keywords (work across most C-family languages)\n const universalPatterns = [\n /\\bif\\b/g,\n /\\belse\\s+if\\b/g,\n /\\belse\\b/g,\n /\\bfor\\b/g,\n /\\bwhile\\b/g,\n /\\bswitch\\b/g,\n /\\bcase\\b/g,\n /\\bcatch\\b/g,\n /\\?\\s*[^?]/g, // ternary (but not ??)\n /&&/g,\n /\\|\\|/g,\n ];\n\n for (const pattern of universalPatterns) {\n const matches = stripped.match(pattern);\n if (matches) count += matches.length;\n }\n\n // Language-specific patterns\n if (language === 'python') {\n const pyPatterns = [/\\belif\\b/g, /\\bexcept\\b/g, /\\bfinally\\b/g];\n for (const p of pyPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n } else if (language === 'rust') {\n const rustPatterns = [/\\bmatch\\b/g, /=>/g, /\\bloop\\b/g];\n for (const p of rustPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n } else if (language === 'ruby') {\n const rubyPatterns = [/\\belsif\\b/g, /\\bunless\\b/g, /\\brescue\\b/g, /\\bwhen\\b/g];\n for (const p of rubyPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n } else if (language === 'go') {\n const goPatterns = [/\\bselect\\b/g, /\\bdefer\\b/g];\n for (const p of goPatterns) {\n const m = stripped.match(p);\n if (m) count += m.length;\n }\n }\n\n return count;\n}\n\n/**\n * Rough strip of comments and string literals to reduce false positives\n * in branch counting. Not perfect but good enough for estimation.\n */\nfunction stripCommentsAndStrings(source: string): string {\n return source\n // Block comments\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n // Line comments\n .replace(/\\/\\/.*/g, '')\n // Python/Ruby line comments\n .replace(/#.*/g, '')\n // Double-quoted strings\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"/g, '\"\"')\n // Single-quoted strings\n .replace(/'(?:[^'\\\\]|\\\\.)*'/g, \"''\")\n // Template literals\n .replace(/`(?:[^`\\\\]|\\\\.)*`/g, '``');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/extract-candidates.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { getCalleeRowsForSymbol } from '../query-support.js';\nimport type { ExtractCandidate } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find functions with natural extraction seams.\n *\n * A large function that references two distinct groups of symbols —\n * where group A's symbols are never co-referenced with group B's —\n * has a natural extraction boundary. The isolated cluster can likely\n * be pulled into its own function.\n *\n * We detect this by:\n * 1. Finding all callees of a function\n * 2. Building a co-occurrence graph (which callees appear in the same chunk)\n * 3. Finding connected components — disconnected components = extraction seams\n * 4. Scoring each cluster by how isolated it is from the rest\n */\nexport function extractCandidates(\n db: ScipDatabase,\n opts: { scope?: string; minLoc?: number; minCallees?: number; limit?: number } = {},\n): ExtractCandidate[] {\n const { scope, minLoc = 10, minCallees = 6, limit = 20 } = opts;\n const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';\n\n // Find functions large enough to consider\n const symbols = db.all<{\n id: number;\n symbol: string;\n document_id: number;\n start_line: number;\n end_line: number;\n relative_path: string;\n }>(\n `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}\n ORDER BY (der.end_line - der.start_line + 1) DESC`,\n minLoc,\n );\n\n const results: ExtractCandidate[] = [];\n\n for (const sym of symbols) {\n if (db.isIgnored(sym.relative_path)) continue;\n\n // Skip pure type files — \"callees\" in a type file are just type references,\n // not function calls. Splitting type files is a cosmetic choice, not an\n // extraction opportunity.\n const basename = sym.relative_path.split('/').pop() ?? '';\n if (basename.includes('types')) continue;\n\n // Get callees with their chunk locations (to build co-occurrence)\n const calleeChunks = getCalleeRowsForSymbol(db, {\n documentId: sym.document_id,\n startLine: sym.start_line,\n endLine: sym.end_line,\n symbolId: sym.id,\n });\n\n // Collect unique callees\n const calleeSet = new Set(calleeChunks.map((c) => c.symbol));\n if (calleeSet.size < minCallees) continue;\n\n // Build co-occurrence graph: two callees are connected if they\n // appear in the same chunk (meaning they're used in proximity)\n const cooccurrence = new Map<string, Set<string>>();\n for (const callee of calleeSet) {\n cooccurrence.set(callee, new Set());\n }\n\n // Group by chunk\n const chunkToCallees = new Map<number, Set<string>>();\n for (const cc of calleeChunks) {\n if (!chunkToCallees.has(cc.chunkId)) chunkToCallees.set(cc.chunkId, new Set());\n chunkToCallees.get(cc.chunkId)!.add(cc.symbol);\n }\n\n // Callees in the same chunk are co-occurring\n for (const callees of chunkToCallees.values()) {\n const arr = [...callees];\n for (let i = 0; i < arr.length; i++) {\n for (let j = i + 1; j < arr.length; j++) {\n cooccurrence.get(arr[i]!)!.add(arr[j]!);\n cooccurrence.get(arr[j]!)!.add(arr[i]!);\n }\n }\n }\n\n // Find connected components via BFS\n const visited = new Set<string>();\n const clusters: Set<string>[] = [];\n\n for (const callee of calleeSet) {\n if (visited.has(callee)) continue;\n const cluster = new Set<string>();\n const queue = [callee];\n while (queue.length > 0) {\n const current = queue.pop()!;\n if (visited.has(current)) continue;\n visited.add(current);\n cluster.add(current);\n for (const neighbor of cooccurrence.get(current) ?? []) {\n if (!visited.has(neighbor)) queue.push(neighbor);\n }\n }\n clusters.push(cluster);\n }\n\n // Only interesting if there are multiple clusters (= extraction seams exist)\n if (clusters.length < 2) continue;\n\n // Score each cluster by isolation:\n // isolation = 1 - (edges to other clusters / total possible edges to other clusters)\n const scoredClusters = clusters\n .filter((c) => c.size >= 2) // single-callee clusters aren't interesting\n .map((cluster) => {\n const otherCallees = new Set<string>();\n for (const c of clusters) {\n if (c !== cluster) {\n for (const s of c) otherCallees.add(s);\n }\n }\n\n // Count cross-cluster edges\n let crossEdges = 0;\n for (const callee of cluster) {\n for (const neighbor of cooccurrence.get(callee) ?? []) {\n if (otherCallees.has(neighbor)) crossEdges++;\n }\n }\n\n const maxCrossEdges = cluster.size * otherCallees.size;\n const isolation = maxCrossEdges > 0 ? 1 - crossEdges / maxCrossEdges : 1;\n\n return {\n callees: [...cluster].map(shortenSymbol),\n isolation,\n };\n })\n .filter((c) => c.isolation > 0.5) // Only report well-isolated clusters\n .sort((a, b) => b.isolation - a.isolation);\n\n if (scoredClusters.length > 0) {\n results.push({\n symbol: sym.symbol,\n shortName: shortenSymbol(sym.symbol),\n relativePath: sym.relative_path,\n startLine: sym.start_line,\n endLine: sym.end_line,\n loc: sym.end_line - sym.start_line + 1,\n totalCallees: calleeSet.size,\n clusters: scoredClusters,\n });\n }\n }\n\n results.sort((a, b) => b.clusters.length - a.clusters.length || b.loc - a.loc);\n return results.slice(0, limit);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/system.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { SystemResult, SymbolResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\nimport { cleanSignature } from './clean-signature.js';\n\n/** Full system map for a module path: files, symbols, deps in/out */\nexport function system(db: ScipDatabase, modulePattern: string): SystemResult {\n // Files in this module\n const fileRows = db.all<{ relative_path: string }>(\n `SELECT relative_path FROM documents\n WHERE relative_path LIKE ?\n ORDER BY relative_path`,\n `%${modulePattern}%`,\n );\n const files = fileRows\n .map((r) => r.relative_path)\n .filter((p) => !db.isIgnored(p));\n\n // Exported symbols\n const symbolRows = db.all<{\n start_line: number;\n end_line: number;\n symbol: string;\n sig: string | null;\n }>(\n `SELECT der.start_line, der.end_line, gs.symbol,\n REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig\n FROM defn_enclosing_ranges der\n JOIN global_symbols gs ON der.symbol_id = gs.id\n JOIN documents d ON der.document_id = d.id\n WHERE d.relative_path LIKE ?\n AND ${db.localSymbolPredicate}\n ${db.symbolNoise}\n AND gs.documentation IS NOT NULL\n ORDER BY d.relative_path, der.start_line`,\n `%${modulePattern}%`,\n );\n const symbols: SymbolResult[] = symbolRows.map((r) => ({\n startLine: r.start_line,\n endLine: r.end_line,\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n signature: cleanSignature(r.sig),\n }));\n\n // Internal dependencies (what this module depends on)\n const depRows = db.all<{ relative_path: string }>(\n `SELECT DISTINCT d2.relative_path\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d1 ON c.document_id = d1.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d2 ON der.document_id = d2.id\n WHERE d1.relative_path LIKE ?\n AND d2.relative_path NOT LIKE ?\n AND ${db.localSymbolPredicate}\n ORDER BY d2.relative_path`,\n `%${modulePattern}%`,\n `%${modulePattern}%`,\n );\n const dependsOn = depRows\n .map((r) => r.relative_path)\n .filter((p) => !db.isIgnored(p));\n\n // Reverse dependencies (who depends on this module)\n const rdepRows = db.all<{ relative_path: string }>(\n `SELECT DISTINCT d1.relative_path\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d1 ON c.document_id = d1.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d2 ON der.document_id = d2.id\n WHERE d2.relative_path LIKE ?\n AND d1.relative_path NOT LIKE ?\n ORDER BY d1.relative_path`,\n `%${modulePattern}%`,\n `%${modulePattern}%`,\n );\n const dependedOnBy = rdepRows\n .map((r) => r.relative_path)\n .filter((p) => !db.isIgnored(p));\n\n return { files, symbols, dependsOn, dependedOnBy };\n}\n"],"mappings":";;;;;;;;AAMO,SAAS,OAAO,IAAkB,eAAqC;AAE5E,QAAM,WAAW,GAAG;AAAA,IAClB;AAAA;AAAA;AAAA,IAGA,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,QAAQ,SACX,IAAI,CAAC,MAAM,EAAE,aAAa,EAC1B,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;AAGjC,QAAM,aAAa,GAAG;AAAA,IAMpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMQ,GAAG,oBAAoB;AAAA,QAC3B,GAAG,WAAW;AAAA;AAAA;AAAA,IAGlB,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,UAA0B,WAAW,IAAI,CAAC,OAAO;AAAA,IACrD,WAAW,EAAE;AAAA,IACb,SAAS,EAAE;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,WAAW,eAAe,EAAE,GAAG;AAAA,EACjC,EAAE;AAGF,QAAM,UAAU,GAAG;AAAA,IACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YASQ,GAAG,oBAAoB;AAAA;AAAA,IAE/B,IAAI,aAAa;AAAA,IACjB,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,YAAY,QACf,IAAI,CAAC,MAAM,EAAE,aAAa,EAC1B,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;AAGjC,QAAM,WAAW,GAAG;AAAA,IAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,IAAI,aAAa;AAAA,IACjB,IAAI,aAAa;AAAA,EACnB;AACA,QAAM,eAAe,SAClB,IAAI,CAAC,MAAM,EAAE,aAAa,EAC1B,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;AAEjC,SAAO,EAAE,OAAO,SAAS,WAAW,aAAa;AACnD;","names":[]}
|
package/dist/chunk-DEZKCZXD.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
shortenSymbol
|
|
3
|
-
} from "./chunk-QOV2R2WT.js";
|
|
4
|
-
|
|
5
|
-
// src/queries/hierarchy.ts
|
|
6
|
-
function hierarchy(db, symbolPattern) {
|
|
7
|
-
const sym = db.get(
|
|
8
|
-
`SELECT symbol, enclosing_symbol FROM global_symbols
|
|
9
|
-
WHERE symbol LIKE ? LIMIT 1`,
|
|
10
|
-
`%${symbolPattern}%`
|
|
11
|
-
);
|
|
12
|
-
if (!sym) return [];
|
|
13
|
-
const chain = [
|
|
14
|
-
{ symbol: sym.symbol, shortName: shortenSymbol(sym.symbol), depth: 0 }
|
|
15
|
-
];
|
|
16
|
-
let current = sym.enclosing_symbol;
|
|
17
|
-
let depth = 1;
|
|
18
|
-
const seen = /* @__PURE__ */ new Set([sym.symbol]);
|
|
19
|
-
while (current && !seen.has(current) && depth < 20) {
|
|
20
|
-
seen.add(current);
|
|
21
|
-
const parent = db.get(
|
|
22
|
-
`SELECT symbol, enclosing_symbol FROM global_symbols WHERE symbol = ?`,
|
|
23
|
-
current
|
|
24
|
-
);
|
|
25
|
-
if (!parent) break;
|
|
26
|
-
chain.push({
|
|
27
|
-
symbol: parent.symbol,
|
|
28
|
-
shortName: shortenSymbol(parent.symbol),
|
|
29
|
-
depth
|
|
30
|
-
});
|
|
31
|
-
current = parent.enclosing_symbol;
|
|
32
|
-
depth++;
|
|
33
|
-
}
|
|
34
|
-
return chain;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
hierarchy
|
|
39
|
-
};
|
|
40
|
-
//# sourceMappingURL=chunk-DEZKCZXD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/hierarchy.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { HierarchyNode } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Walk the enclosing_symbol chain upward to show a symbol's ancestry.\n * e.g., method → class → module → file\n *\n * Falls back to parsing the SCIP symbol descriptor chain when\n * enclosing_symbol is not populated by the indexer.\n */\nexport function hierarchy(db: ScipDatabase, symbolPattern: string): HierarchyNode[] {\n // Find the symbol\n const sym = db.get<{ symbol: string; enclosing_symbol: string | null }>(\n `SELECT symbol, enclosing_symbol FROM global_symbols\n WHERE symbol LIKE ? LIMIT 1`,\n `%${symbolPattern}%`,\n );\n\n if (!sym) return [];\n\n const chain: HierarchyNode[] = [\n { symbol: sym.symbol, shortName: shortenSymbol(sym.symbol), depth: 0 },\n ];\n\n // Walk enclosing_symbol chain if available\n let current = sym.enclosing_symbol;\n let depth = 1;\n const seen = new Set<string>([sym.symbol]);\n\n while (current && !seen.has(current) && depth < 20) {\n seen.add(current);\n const parent = db.get<{ symbol: string; enclosing_symbol: string | null }>(\n `SELECT symbol, enclosing_symbol FROM global_symbols WHERE symbol = ?`,\n current,\n );\n if (!parent) break;\n\n chain.push({\n symbol: parent.symbol,\n shortName: shortenSymbol(parent.symbol),\n depth,\n });\n current = parent.enclosing_symbol;\n depth++;\n }\n\n return chain;\n}\n"],"mappings":";;;;;AAWO,SAAS,UAAU,IAAkB,eAAwC;AAElF,QAAM,MAAM,GAAG;AAAA,IACb;AAAA;AAAA,IAEA,IAAI,aAAa;AAAA,EACnB;AAEA,MAAI,CAAC,IAAK,QAAO,CAAC;AAElB,QAAM,QAAyB;AAAA,IAC7B,EAAE,QAAQ,IAAI,QAAQ,WAAW,cAAc,IAAI,MAAM,GAAG,OAAO,EAAE;AAAA,EACvE;AAGA,MAAI,UAAU,IAAI;AAClB,MAAI,QAAQ;AACZ,QAAM,OAAO,oBAAI,IAAY,CAAC,IAAI,MAAM,CAAC;AAEzC,SAAO,WAAW,CAAC,KAAK,IAAI,OAAO,KAAK,QAAQ,IAAI;AAClD,SAAK,IAAI,OAAO;AAChB,UAAM,SAAS,GAAG;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,OAAQ;AAEb,UAAM,KAAK;AAAA,MACT,QAAQ,OAAO;AAAA,MACf,WAAW,cAAc,OAAO,MAAM;AAAA,MACtC;AAAA,IACF,CAAC;AACD,cAAU,OAAO;AACjB;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
package/dist/chunk-DVWGWHFW.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// src/reindex/install.ts
|
|
2
|
-
import { execFileSync } from "child_process";
|
|
3
|
-
import { platform } from "os";
|
|
4
|
-
var IS_WINDOWS = platform() === "win32";
|
|
5
|
-
function isBinaryAvailable(name) {
|
|
6
|
-
const cmd = IS_WINDOWS ? "where" : "which";
|
|
7
|
-
try {
|
|
8
|
-
execFileSync(cmd, [name], { stdio: "pipe" });
|
|
9
|
-
return true;
|
|
10
|
-
} catch {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
function isIndexerInstalled(config) {
|
|
15
|
-
return isBinaryAvailable(config.indexerBinary);
|
|
16
|
-
}
|
|
17
|
-
function tryInstallIndexer(config, onStatus) {
|
|
18
|
-
const methods = config.installMethods;
|
|
19
|
-
if (!methods?.length) {
|
|
20
|
-
onStatus(`No auto-install method available for ${config.indexerBinary}.`);
|
|
21
|
-
if (config.installUrl) {
|
|
22
|
-
onStatus(`Install manually from: ${config.installUrl}`);
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
for (const method of methods) {
|
|
27
|
-
if (!isBinaryAvailable(method.prerequisite)) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
onStatus(`Installing ${config.indexerBinary} via ${method.label}...`);
|
|
31
|
-
try {
|
|
32
|
-
execFileSync(method.binary, method.args, {
|
|
33
|
-
stdio: "inherit",
|
|
34
|
-
timeout: 3e5,
|
|
35
|
-
env: process.env
|
|
36
|
-
});
|
|
37
|
-
if (isIndexerInstalled(config)) {
|
|
38
|
-
onStatus(`Successfully installed ${config.indexerBinary} via ${method.label}`);
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
onStatus(`${method.label} command completed but ${config.indexerBinary} not found on PATH`);
|
|
42
|
-
} catch (err) {
|
|
43
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
44
|
-
onStatus(`${method.label} install failed: ${msg}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
onStatus(`Could not auto-install ${config.indexerBinary}.`);
|
|
48
|
-
if (config.installUrl) {
|
|
49
|
-
onStatus(`Install manually from: ${config.installUrl}`);
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
function tryInstallScipCli(onStatus) {
|
|
54
|
-
if (platform() === "darwin" && isBinaryAvailable("brew")) {
|
|
55
|
-
onStatus("Installing scip CLI via Homebrew...");
|
|
56
|
-
try {
|
|
57
|
-
execFileSync("brew", ["install", "sourcegraph/scip/scip"], {
|
|
58
|
-
stdio: "inherit",
|
|
59
|
-
timeout: 3e5,
|
|
60
|
-
env: process.env
|
|
61
|
-
});
|
|
62
|
-
if (isBinaryAvailable("scip")) {
|
|
63
|
-
onStatus("Successfully installed scip CLI via Homebrew");
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
} catch (err) {
|
|
67
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
68
|
-
onStatus(`Homebrew install failed: ${msg}`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (isBinaryAvailable("go")) {
|
|
72
|
-
onStatus("Installing scip CLI via go install...");
|
|
73
|
-
try {
|
|
74
|
-
execFileSync("go", ["install", "github.com/sourcegraph/scip/cmd/scip@latest"], {
|
|
75
|
-
stdio: "inherit",
|
|
76
|
-
timeout: 3e5,
|
|
77
|
-
env: process.env
|
|
78
|
-
});
|
|
79
|
-
if (isBinaryAvailable("scip")) {
|
|
80
|
-
onStatus("Successfully installed scip CLI via go install");
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
} catch (err) {
|
|
84
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
85
|
-
onStatus(`go install failed: ${msg}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
onStatus("Could not auto-install scip CLI.");
|
|
89
|
-
onStatus("Install manually from: https://github.com/sourcegraph/scip/releases");
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export {
|
|
94
|
-
isBinaryAvailable,
|
|
95
|
-
isIndexerInstalled,
|
|
96
|
-
tryInstallIndexer,
|
|
97
|
-
tryInstallScipCli
|
|
98
|
-
};
|
|
99
|
-
//# sourceMappingURL=chunk-DVWGWHFW.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reindex/install.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport { platform } from 'node:os';\nimport type { IndexerConfig } from '../types.js';\n\nconst IS_WINDOWS = platform() === 'win32';\n\n/**\n * Check if a binary is available on PATH.\n */\nexport function isBinaryAvailable(name: string): boolean {\n const cmd = IS_WINDOWS ? 'where' : 'which';\n try {\n execFileSync(cmd, [name], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if an indexer's binary is available on PATH.\n */\nexport function isIndexerInstalled(config: IndexerConfig): boolean {\n return isBinaryAvailable(config.indexerBinary);\n}\n\n/**\n * Attempt to auto-install an indexer using its configured install methods.\n * Tries each method in order, checking prerequisites first.\n * Returns true if installation succeeded.\n */\nexport function tryInstallIndexer(\n config: IndexerConfig,\n onStatus: (msg: string) => void,\n): boolean {\n const methods = config.installMethods;\n if (!methods?.length) {\n onStatus(`No auto-install method available for ${config.indexerBinary}.`);\n if (config.installUrl) {\n onStatus(`Install manually from: ${config.installUrl}`);\n }\n return false;\n }\n\n for (const method of methods) {\n if (!isBinaryAvailable(method.prerequisite)) {\n continue;\n }\n\n onStatus(`Installing ${config.indexerBinary} via ${method.label}...`);\n try {\n execFileSync(method.binary, method.args, {\n stdio: 'inherit',\n timeout: 300_000,\n env: process.env,\n });\n\n if (isIndexerInstalled(config)) {\n onStatus(`Successfully installed ${config.indexerBinary} via ${method.label}`);\n return true;\n }\n onStatus(`${method.label} command completed but ${config.indexerBinary} not found on PATH`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n onStatus(`${method.label} install failed: ${msg}`);\n }\n }\n\n onStatus(`Could not auto-install ${config.indexerBinary}.`);\n if (config.installUrl) {\n onStatus(`Install manually from: ${config.installUrl}`);\n }\n return false;\n}\n\n/**\n * Attempt to auto-install the `scip` CLI binary.\n * Tries brew (macOS), then go install, then prints manual instructions.\n * Returns true if installation succeeded.\n */\nexport function tryInstallScipCli(\n onStatus: (msg: string) => void,\n): boolean {\n // macOS: try Homebrew first\n if (platform() === 'darwin' && isBinaryAvailable('brew')) {\n onStatus('Installing scip CLI via Homebrew...');\n try {\n execFileSync('brew', ['install', 'sourcegraph/scip/scip'], {\n stdio: 'inherit',\n timeout: 300_000,\n env: process.env,\n });\n if (isBinaryAvailable('scip')) {\n onStatus('Successfully installed scip CLI via Homebrew');\n return true;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n onStatus(`Homebrew install failed: ${msg}`);\n }\n }\n\n // Any platform: try go install\n if (isBinaryAvailable('go')) {\n onStatus('Installing scip CLI via go install...');\n try {\n execFileSync('go', ['install', 'github.com/sourcegraph/scip/cmd/scip@latest'], {\n stdio: 'inherit',\n timeout: 300_000,\n env: process.env,\n });\n if (isBinaryAvailable('scip')) {\n onStatus('Successfully installed scip CLI via go install');\n return true;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n onStatus(`go install failed: ${msg}`);\n }\n }\n\n onStatus('Could not auto-install scip CLI.');\n onStatus('Install manually from: https://github.com/sourcegraph/scip/releases');\n return false;\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AAGzB,IAAM,aAAa,SAAS,MAAM;AAK3B,SAAS,kBAAkB,MAAuB;AACvD,QAAM,MAAM,aAAa,UAAU;AACnC,MAAI;AACF,iBAAa,KAAK,CAAC,IAAI,GAAG,EAAE,OAAO,OAAO,CAAC;AAC3C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,mBAAmB,QAAgC;AACjE,SAAO,kBAAkB,OAAO,aAAa;AAC/C;AAOO,SAAS,kBACd,QACA,UACS;AACT,QAAM,UAAU,OAAO;AACvB,MAAI,CAAC,SAAS,QAAQ;AACpB,aAAS,wCAAwC,OAAO,aAAa,GAAG;AACxE,QAAI,OAAO,YAAY;AACrB,eAAS,0BAA0B,OAAO,UAAU,EAAE;AAAA,IACxD;AACA,WAAO;AAAA,EACT;AAEA,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,kBAAkB,OAAO,YAAY,GAAG;AAC3C;AAAA,IACF;AAEA,aAAS,cAAc,OAAO,aAAa,QAAQ,OAAO,KAAK,KAAK;AACpE,QAAI;AACF,mBAAa,OAAO,QAAQ,OAAO,MAAM;AAAA,QACvC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,KAAK,QAAQ;AAAA,MACf,CAAC;AAED,UAAI,mBAAmB,MAAM,GAAG;AAC9B,iBAAS,0BAA0B,OAAO,aAAa,QAAQ,OAAO,KAAK,EAAE;AAC7E,eAAO;AAAA,MACT;AACA,eAAS,GAAG,OAAO,KAAK,0BAA0B,OAAO,aAAa,oBAAoB;AAAA,IAC5F,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,eAAS,GAAG,OAAO,KAAK,oBAAoB,GAAG,EAAE;AAAA,IACnD;AAAA,EACF;AAEA,WAAS,0BAA0B,OAAO,aAAa,GAAG;AAC1D,MAAI,OAAO,YAAY;AACrB,aAAS,0BAA0B,OAAO,UAAU,EAAE;AAAA,EACxD;AACA,SAAO;AACT;AAOO,SAAS,kBACd,UACS;AAET,MAAI,SAAS,MAAM,YAAY,kBAAkB,MAAM,GAAG;AACxD,aAAS,qCAAqC;AAC9C,QAAI;AACF,mBAAa,QAAQ,CAAC,WAAW,uBAAuB,GAAG;AAAA,QACzD,OAAO;AAAA,QACP,SAAS;AAAA,QACT,KAAK,QAAQ;AAAA,MACf,CAAC;AACD,UAAI,kBAAkB,MAAM,GAAG;AAC7B,iBAAS,8CAA8C;AACvD,eAAO;AAAA,MACT;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,eAAS,4BAA4B,GAAG,EAAE;AAAA,IAC5C;AAAA,EACF;AAGA,MAAI,kBAAkB,IAAI,GAAG;AAC3B,aAAS,uCAAuC;AAChD,QAAI;AACF,mBAAa,MAAM,CAAC,WAAW,6CAA6C,GAAG;AAAA,QAC7E,OAAO;AAAA,QACP,SAAS;AAAA,QACT,KAAK,QAAQ;AAAA,MACf,CAAC;AACD,UAAI,kBAAkB,MAAM,GAAG;AAC7B,iBAAS,gDAAgD;AACzD,eAAO;AAAA,MACT;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,eAAS,sBAAsB,GAAG,EAAE;AAAA,IACtC;AAAA,EACF;AAEA,WAAS,kCAAkC;AAC3C,WAAS,qEAAqE;AAC9E,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/complexity-hotspots.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { testFileExclusionSql } from '../query-support.js';\nimport type { ComplexityHotspot } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find complexity hotspots: symbols with a composite score based on\n * LOC, fan-in, fan-out, and callee count.\n *\n * Score = (loc / 50) * (fanIn / 5) * max(fanOut / 5, 1)\n *\n * High scores indicate symbols that are large, widely depended upon,\n * AND reach out to many other modules — the riskiest code to change.\n */\nexport function complexityHotspots(\n db: ScipDatabase,\n opts?: { scope?: string; minLoc?: number; limit?: number },\n): ComplexityHotspot[] {\n const { scope, minLoc = 10, limit = 30 } = opts ?? {};\n const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';\n\n const rows = db.all<{\n symbol: string;\n file: string;\n start_line: number;\n end_line: number;\n loc: number;\n fan_in: number;\n fan_out: number;\n callee_count: number;\n }>(\n `SELECT\n gs.symbol,\n d.relative_path AS file,\n der.start_line,\n der.end_line,\n (der.end_line - der.start_line + 1) AS loc,\n -- fanIn: distinct files that reference this symbol\n (SELECT COUNT(DISTINCT ref_c.document_id)\n FROM mentions ref_m\n JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id\n WHERE ref_m.symbol_id = gs.id AND ref_m.role = 0\n ) AS fan_in,\n -- fanOut: distinct symbols referenced within this definition range\n -- that are defined in different files\n (SELECT COUNT(DISTINCT out_gs.id)\n FROM mentions out_m\n JOIN chunks out_c ON out_m.chunk_id = out_c.id\n JOIN global_symbols out_gs ON out_m.symbol_id = out_gs.id\n JOIN defn_enclosing_ranges out_der ON out_gs.id = out_der.symbol_id\n WHERE out_c.document_id = der.document_id\n AND out_c.start_line >= der.start_line\n AND out_c.end_line <= der.end_line\n AND out_m.role = 0\n AND out_gs.id != gs.id\n AND out_der.document_id != der.document_id\n ) AS fan_out,\n -- calleeCount: total distinct callees within definition range\n (SELECT COUNT(DISTINCT callee_gs.id)\n FROM mentions callee_m\n JOIN chunks callee_c ON callee_m.chunk_id = callee_c.id\n JOIN global_symbols callee_gs ON callee_m.symbol_id = callee_gs.id\n WHERE callee_c.document_id = der.document_id\n AND callee_c.start_line >= der.start_line\n AND callee_c.end_line <= der.end_line\n AND callee_m.role = 0\n AND callee_gs.id != gs.id\n ) AS callee_count\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n AND ${testFileExclusionSql('d')}\n ${db.symbolNoiseFor('gs')}\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}\n ORDER BY (\n CAST((der.end_line - der.start_line + 1) AS REAL) / 50.0\n * CAST((SELECT COUNT(DISTINCT ref_c2.document_id)\n FROM mentions ref_m2\n JOIN chunks ref_c2 ON ref_m2.chunk_id = ref_c2.id\n WHERE ref_m2.symbol_id = gs.id AND ref_m2.role = 0\n ) AS REAL) / 5.0\n * MAX(CAST((SELECT COUNT(DISTINCT out_gs2.id)\n FROM mentions out_m2\n JOIN chunks out_c2 ON out_m2.chunk_id = out_c2.id\n JOIN global_symbols out_gs2 ON out_m2.symbol_id = out_gs2.id\n JOIN defn_enclosing_ranges out_der2 ON out_gs2.id = out_der2.symbol_id\n WHERE out_c2.document_id = der.document_id\n AND out_c2.start_line >= der.start_line\n AND out_c2.end_line <= der.end_line\n AND out_m2.role = 0\n AND out_gs2.id != gs.id\n AND out_der2.document_id != der.document_id\n ) AS REAL) / 5.0, 1.0)\n ) DESC\n LIMIT ?`,\n minLoc, limit,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.file))\n .map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n file: r.file,\n startLine: r.start_line,\n endLine: r.end_line,\n loc: r.loc,\n fanIn: r.fan_in,\n fanOut: r.fan_out,\n calleeCount: r.callee_count,\n score:\n Math.round(\n (r.loc / 50) * (r.fan_in / 5) * Math.max(r.fan_out / 5, 1) * 100,\n ) / 100,\n }));\n}\n"],"mappings":";;;;;;;;AAcO,SAAS,mBACd,IACA,MACqB;AACrB,QAAM,EAAE,OAAO,SAAS,IAAI,QAAQ,GAAG,IAAI,QAAQ,CAAC;AACpD,QAAM,cAAc,QAAQ,8BAA8B,KAAK,OAAO;AAEtE,QAAM,OAAO,GAAG;AAAA,IAUd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAyCI,GAAG,kBAAkB,GAAG,CAAC;AAAA,YACrB,qBAAqB,GAAG,CAAC;AAAA,QAC7B,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA,QAEvB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAsBf;AAAA,IAAQ;AAAA,EACV;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,IAAI,CAAC,EACnC,IAAI,CAAC,OAAO;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,MAAM,EAAE;AAAA,IACR,WAAW,EAAE;AAAA,IACb,SAAS,EAAE;AAAA,IACX,KAAK,EAAE;AAAA,IACP,OAAO,EAAE;AAAA,IACT,QAAQ,EAAE;AAAA,IACV,aAAa,EAAE;AAAA,IACf,OACE,KAAK;AAAA,MACF,EAAE,MAAM,MAAO,EAAE,SAAS,KAAK,KAAK,IAAI,EAAE,UAAU,GAAG,CAAC,IAAI;AAAA,IAC/D,IAAI;AAAA,EACR,EAAE;AACN;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/surface.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { SurfaceResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/** Public API surface: what symbols do external consumers actually use from this module? */\nexport function surface(db: ScipDatabase, modulePattern: string): SurfaceResult[] {\n const rows = db.all<{\n relative_path: string;\n symbol: string;\n }>(\n `SELECT DISTINCT d1.relative_path, gs.symbol\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d1 ON c.document_id = d1.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d2 ON der.document_id = d2.id\n WHERE d2.relative_path LIKE ?\n AND d1.relative_path NOT LIKE ?\n AND ${db.localSymbolPredicate}\n AND m.role = 0\n ORDER BY d1.relative_path`,\n `%${modulePattern}%`,\n `%${modulePattern}%`,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.relative_path))\n .map((r) => ({\n consumer: r.relative_path,\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n }));\n}\n"],"mappings":";;;;;AAKO,SAAS,QAAQ,IAAkB,eAAwC;AAChF,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YASQ,GAAG,oBAAoB;AAAA;AAAA;AAAA,IAG/B,IAAI,aAAa;AAAA,IACjB,IAAI,aAAa;AAAA,EACnB;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,aAAa,CAAC,EAC5C,IAAI,CAAC,OAAO;AAAA,IACX,UAAU,EAAE;AAAA,IACZ,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,EACnC,EAAE;AACN;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/doc-coverage.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { DocCoverageResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Check documentation coverage: what percentage of symbols have doc strings?\n * Reports overall stats and lists undocumented symbols.\n */\nexport function docCoverage(\n db: ScipDatabase,\n opts: { scope?: string; minLoc?: number; limit?: number } = {},\n): DocCoverageResult {\n const { scope, minLoc = 3, limit = 50 } = opts;\n const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';\n\n // Count all local symbols meeting the threshold\n const totalRow = db.get<{ c: number }>(\n `SELECT COUNT(*) AS c\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n AND gs.symbol NOT LIKE '%#%'\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}`,\n minLoc,\n );\n\n const docRow = db.get<{ c: number }>(\n `SELECT COUNT(*) AS c\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n AND gs.symbol NOT LIKE '%#%'\n AND (der.end_line - der.start_line + 1) >= ?\n AND gs.documentation IS NOT NULL\n AND gs.documentation != ''\n ${scopeFilter}`,\n minLoc,\n );\n\n const total = totalRow?.c ?? 0;\n const documented = docRow?.c ?? 0;\n\n // Get undocumented symbols\n const undocRows = db.all<{\n symbol: string;\n relative_path: string;\n start_line: number;\n }>(\n `SELECT gs.symbol, d.relative_path, der.start_line\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n AND gs.symbol NOT LIKE '%#%'\n AND (der.end_line - der.start_line + 1) >= ?\n AND (gs.documentation IS NULL OR gs.documentation = '')\n ${scopeFilter}\n ORDER BY d.relative_path, der.start_line\n LIMIT ?`,\n minLoc, limit,\n );\n\n return {\n totalSymbols: total,\n documented,\n undocumented: total - documented,\n coveragePercent: total > 0 ? Math.round((documented / total) * 100) : 0,\n undocumentedSymbols: undocRows\n .filter((r) => !db.isIgnored(r.relative_path))\n .map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n relativePath: r.relative_path,\n startLine: r.start_line,\n })),\n };\n}\n"],"mappings":";;;;;AAQO,SAAS,YACd,IACA,OAA4D,CAAC,GAC1C;AACnB,QAAM,EAAE,OAAO,SAAS,GAAG,QAAQ,GAAG,IAAI;AAC1C,QAAM,cAAc,QAAQ,8BAA8B,KAAK,OAAO;AAGtE,QAAM,WAAW,GAAG;AAAA,IAClB;AAAA;AAAA;AAAA;AAAA;AAAA,QAKI,GAAG,kBAAkB,GAAG,CAAC;AAAA,QACzB,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA,QAGvB,WAAW;AAAA,IACf;AAAA,EACF;AAEA,QAAM,SAAS,GAAG;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA;AAAA,QAKI,GAAG,kBAAkB,GAAG,CAAC;AAAA,QACzB,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKvB,WAAW;AAAA,IACf;AAAA,EACF;AAEA,QAAM,QAAQ,UAAU,KAAK;AAC7B,QAAM,aAAa,QAAQ,KAAK;AAGhC,QAAM,YAAY,GAAG;AAAA,IAKnB;AAAA;AAAA;AAAA;AAAA;AAAA,QAKI,GAAG,kBAAkB,GAAG,CAAC;AAAA,QACzB,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,QAIvB,WAAW;AAAA;AAAA;AAAA,IAGf;AAAA,IAAQ;AAAA,EACV;AAEA,SAAO;AAAA,IACL,cAAc;AAAA,IACd;AAAA,IACA,cAAc,QAAQ;AAAA,IACtB,iBAAiB,QAAQ,IAAI,KAAK,MAAO,aAAa,QAAS,GAAG,IAAI;AAAA,IACtE,qBAAqB,UAClB,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,aAAa,CAAC,EAC5C,IAAI,CAAC,OAAO;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,WAAW,cAAc,EAAE,MAAM;AAAA,MACjC,cAAc,EAAE;AAAA,MAChB,WAAW,EAAE;AAAA,IACf,EAAE;AAAA,EACN;AACF;","names":[]}
|