scip-query 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/IMPROVEMENTS.md +143 -0
- package/PLAN.md +320 -0
- package/README.md +1213 -0
- package/dist/chunk-2QZ23IBN.js +55 -0
- package/dist/chunk-2QZ23IBN.js.map +1 -0
- package/dist/chunk-36OMT7ZJ.js +144 -0
- package/dist/chunk-36OMT7ZJ.js.map +1 -0
- package/dist/chunk-3E2X7RIE.js +101 -0
- package/dist/chunk-3E2X7RIE.js.map +1 -0
- package/dist/chunk-3UOUTZQT.js +45 -0
- package/dist/chunk-3UOUTZQT.js.map +1 -0
- package/dist/chunk-3ZZJVBIO.js +88 -0
- package/dist/chunk-3ZZJVBIO.js.map +1 -0
- package/dist/chunk-4TYLS5XX.js +10 -0
- package/dist/chunk-4TYLS5XX.js.map +1 -0
- package/dist/chunk-5FGUEU7N.js +101 -0
- package/dist/chunk-5FGUEU7N.js.map +1 -0
- package/dist/chunk-5WTJAXY2.js +61 -0
- package/dist/chunk-5WTJAXY2.js.map +1 -0
- package/dist/chunk-6NBLIDF4.js +24 -0
- package/dist/chunk-6NBLIDF4.js.map +1 -0
- package/dist/chunk-6SXADWLW.js +43 -0
- package/dist/chunk-6SXADWLW.js.map +1 -0
- package/dist/chunk-6VJ6Q7IE.js +65 -0
- package/dist/chunk-6VJ6Q7IE.js.map +1 -0
- package/dist/chunk-7OZPA5OO.js +258 -0
- package/dist/chunk-7OZPA5OO.js.map +1 -0
- package/dist/chunk-BEPIEVLR.js +76 -0
- package/dist/chunk-BEPIEVLR.js.map +1 -0
- package/dist/chunk-BFSCMC22.js +42 -0
- package/dist/chunk-BFSCMC22.js.map +1 -0
- package/dist/chunk-BP2ATLK2.js +110 -0
- package/dist/chunk-BP2ATLK2.js.map +1 -0
- package/dist/chunk-CM454WL3.js +114 -0
- package/dist/chunk-CM454WL3.js.map +1 -0
- package/dist/chunk-DCKMSTJ4.js +74 -0
- package/dist/chunk-DCKMSTJ4.js.map +1 -0
- package/dist/chunk-DEZKCZXD.js +40 -0
- package/dist/chunk-DEZKCZXD.js.map +1 -0
- package/dist/chunk-DVWGWHFW.js +99 -0
- package/dist/chunk-DVWGWHFW.js.map +1 -0
- package/dist/chunk-EMDQWNYR.js +102 -0
- package/dist/chunk-EMDQWNYR.js.map +1 -0
- package/dist/chunk-FFSWWE5O.js +33 -0
- package/dist/chunk-FFSWWE5O.js.map +1 -0
- package/dist/chunk-FGXRVW7G.js +73 -0
- package/dist/chunk-FGXRVW7G.js.map +1 -0
- package/dist/chunk-FUHJCHS4.js +158 -0
- package/dist/chunk-FUHJCHS4.js.map +1 -0
- package/dist/chunk-GJFURBEW.js +64 -0
- package/dist/chunk-GJFURBEW.js.map +1 -0
- package/dist/chunk-GTILYBH6.js +102 -0
- package/dist/chunk-GTILYBH6.js.map +1 -0
- package/dist/chunk-JJP7KQND.js +1 -0
- package/dist/chunk-JJP7KQND.js.map +1 -0
- package/dist/chunk-JKP5GH6T.js +213 -0
- package/dist/chunk-JKP5GH6T.js.map +1 -0
- package/dist/chunk-KCBMVQL5.js +38 -0
- package/dist/chunk-KCBMVQL5.js.map +1 -0
- package/dist/chunk-KVSW5KYP.js +78 -0
- package/dist/chunk-KVSW5KYP.js.map +1 -0
- package/dist/chunk-LAWMH22O.js +172 -0
- package/dist/chunk-LAWMH22O.js.map +1 -0
- package/dist/chunk-LB7OS35Q.js +72 -0
- package/dist/chunk-LB7OS35Q.js.map +1 -0
- package/dist/chunk-LUSIFBXO.js +57 -0
- package/dist/chunk-LUSIFBXO.js.map +1 -0
- package/dist/chunk-MBVNHJVN.js +44 -0
- package/dist/chunk-MBVNHJVN.js.map +1 -0
- package/dist/chunk-MGNMHKX3.js +15 -0
- package/dist/chunk-MGNMHKX3.js.map +1 -0
- package/dist/chunk-N5KEREIA.js +41 -0
- package/dist/chunk-N5KEREIA.js.map +1 -0
- package/dist/chunk-NDSQYIWT.js +71 -0
- package/dist/chunk-NDSQYIWT.js.map +1 -0
- package/dist/chunk-NUZ4OMU3.js +28 -0
- package/dist/chunk-NUZ4OMU3.js.map +1 -0
- package/dist/chunk-QOV2R2WT.js +170 -0
- package/dist/chunk-QOV2R2WT.js.map +1 -0
- package/dist/chunk-SEFSL2GF.js +78 -0
- package/dist/chunk-SEFSL2GF.js.map +1 -0
- package/dist/chunk-T6ARFSBZ.js +103 -0
- package/dist/chunk-T6ARFSBZ.js.map +1 -0
- package/dist/chunk-TBP6BICL.js +46 -0
- package/dist/chunk-TBP6BICL.js.map +1 -0
- package/dist/chunk-TDNNOR6D.js +97 -0
- package/dist/chunk-TDNNOR6D.js.map +1 -0
- package/dist/chunk-TSPZOMHC.js +195 -0
- package/dist/chunk-TSPZOMHC.js.map +1 -0
- package/dist/chunk-UNTPVD36.js +55 -0
- package/dist/chunk-UNTPVD36.js.map +1 -0
- package/dist/chunk-VRUJH4BO.js +88 -0
- package/dist/chunk-VRUJH4BO.js.map +1 -0
- package/dist/chunk-VZ7AMAFL.js +76 -0
- package/dist/chunk-VZ7AMAFL.js.map +1 -0
- package/dist/chunk-XFXDXEUN.js +24 -0
- package/dist/chunk-XFXDXEUN.js.map +1 -0
- package/dist/chunk-YZAA4LYG.js +169 -0
- package/dist/chunk-YZAA4LYG.js.map +1 -0
- package/dist/chunk-Z73NYSBZ.js +92 -0
- package/dist/chunk-Z73NYSBZ.js.map +1 -0
- package/dist/chunk-ZJRYBOEE.js +125 -0
- package/dist/chunk-ZJRYBOEE.js.map +1 -0
- package/dist/cli.js +5798 -0
- package/dist/cli.js.map +1 -0
- package/dist/db-BxaevAyc.d.ts +683 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +1271 -0
- package/dist/index.js.map +1 -0
- package/dist/postinstall.js +167 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/queries/affected.d.ts +14 -0
- package/dist/queries/affected.js +9 -0
- package/dist/queries/affected.js.map +1 -0
- package/dist/queries/bottlenecks.d.ts +18 -0
- package/dist/queries/bottlenecks.js +8 -0
- package/dist/queries/bottlenecks.js.map +1 -0
- package/dist/queries/by-kind.d.ts +20 -0
- package/dist/queries/by-kind.js +10 -0
- package/dist/queries/by-kind.js.map +1 -0
- package/dist/queries/call-graph.d.ts +13 -0
- package/dist/queries/call-graph.js +9 -0
- package/dist/queries/call-graph.js.map +1 -0
- package/dist/queries/change-surface.d.ts +10 -0
- package/dist/queries/change-surface.js +9 -0
- package/dist/queries/change-surface.js.map +1 -0
- package/dist/queries/clean-signature.d.ts +9 -0
- package/dist/queries/clean-signature.js +7 -0
- package/dist/queries/clean-signature.js.map +1 -0
- package/dist/queries/code.d.ts +17 -0
- package/dist/queries/code.js +9 -0
- package/dist/queries/code.js.map +1 -0
- package/dist/queries/complexity-hotspots.d.ts +19 -0
- package/dist/queries/complexity-hotspots.js +9 -0
- package/dist/queries/complexity-hotspots.js.map +1 -0
- package/dist/queries/complexity.d.ts +13 -0
- package/dist/queries/complexity.js +9 -0
- package/dist/queries/complexity.js.map +1 -0
- package/dist/queries/convergence.d.ts +11 -0
- package/dist/queries/convergence.js +9 -0
- package/dist/queries/convergence.js.map +1 -0
- package/dist/queries/coupling.d.ts +17 -0
- package/dist/queries/coupling.js +9 -0
- package/dist/queries/coupling.js.map +1 -0
- package/dist/queries/cycles.d.ts +16 -0
- package/dist/queries/cycles.js +8 -0
- package/dist/queries/cycles.js.map +1 -0
- package/dist/queries/dataflow.d.ts +19 -0
- package/dist/queries/dataflow.js +9 -0
- package/dist/queries/dataflow.js.map +1 -0
- package/dist/queries/dead.d.ts +10 -0
- package/dist/queries/dead.js +9 -0
- package/dist/queries/dead.js.map +1 -0
- package/dist/queries/deep-chains.d.ts +16 -0
- package/dist/queries/deep-chains.js +8 -0
- package/dist/queries/deep-chains.js.map +1 -0
- package/dist/queries/deps.d.ts +9 -0
- package/dist/queries/deps.js +9 -0
- package/dist/queries/deps.js.map +1 -0
- package/dist/queries/diff-impact.d.ts +13 -0
- package/dist/queries/diff-impact.js +9 -0
- package/dist/queries/diff-impact.js.map +1 -0
- package/dist/queries/doc-coverage.d.ts +14 -0
- package/dist/queries/doc-coverage.js +8 -0
- package/dist/queries/doc-coverage.js.map +1 -0
- package/dist/queries/drift.d.ts +25 -0
- package/dist/queries/drift.js +8 -0
- package/dist/queries/drift.js.map +1 -0
- package/dist/queries/extract-candidates.d.ts +25 -0
- package/dist/queries/extract-candidates.js +9 -0
- package/dist/queries/extract-candidates.js.map +1 -0
- package/dist/queries/fan.d.ts +29 -0
- package/dist/queries/fan.js +14 -0
- package/dist/queries/fan.js.map +1 -0
- package/dist/queries/files.d.ts +6 -0
- package/dist/queries/files.js +7 -0
- package/dist/queries/files.js.map +1 -0
- package/dist/queries/health.d.ts +18 -0
- package/dist/queries/health.js +21 -0
- package/dist/queries/health.js.map +1 -0
- package/dist/queries/hierarchy.d.ts +13 -0
- package/dist/queries/hierarchy.js +8 -0
- package/dist/queries/hierarchy.js.map +1 -0
- package/dist/queries/hotspots.d.ts +13 -0
- package/dist/queries/hotspots.js +8 -0
- package/dist/queries/hotspots.js.map +1 -0
- package/dist/queries/imports.d.ts +19 -0
- package/dist/queries/imports.js +12 -0
- package/dist/queries/imports.js.map +1 -0
- package/dist/queries/index.d.ts +47 -0
- package/dist/queries/index.js +207 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/isolated.d.ts +14 -0
- package/dist/queries/isolated.js +9 -0
- package/dist/queries/isolated.js.map +1 -0
- package/dist/queries/members.d.ts +10 -0
- package/dist/queries/members.js +8 -0
- package/dist/queries/members.js.map +1 -0
- package/dist/queries/methods.d.ts +6 -0
- package/dist/queries/methods.js +8 -0
- package/dist/queries/methods.js.map +1 -0
- package/dist/queries/outline.d.ts +10 -0
- package/dist/queries/outline.js +8 -0
- package/dist/queries/outline.js.map +1 -0
- package/dist/queries/passthrough-candidates.d.ts +18 -0
- package/dist/queries/passthrough-candidates.js +9 -0
- package/dist/queries/passthrough-candidates.js.map +1 -0
- package/dist/queries/redundant-reexports.d.ts +22 -0
- package/dist/queries/redundant-reexports.js +8 -0
- package/dist/queries/redundant-reexports.js.map +1 -0
- package/dist/queries/refs.d.ts +6 -0
- package/dist/queries/refs.js +7 -0
- package/dist/queries/refs.js.map +1 -0
- package/dist/queries/similar-chains.d.ts +29 -0
- package/dist/queries/similar-chains.js +8 -0
- package/dist/queries/similar-chains.js.map +1 -0
- package/dist/queries/similar-files.d.ts +19 -0
- package/dist/queries/similar-files.js +8 -0
- package/dist/queries/similar-files.js.map +1 -0
- package/dist/queries/similar-signatures.d.ts +21 -0
- package/dist/queries/similar-signatures.js +8 -0
- package/dist/queries/similar-signatures.js.map +1 -0
- package/dist/queries/similar.d.ts +34 -0
- package/dist/queries/similar.js +11 -0
- package/dist/queries/similar.js.map +1 -0
- package/dist/queries/slice.d.ts +21 -0
- package/dist/queries/slice.js +9 -0
- package/dist/queries/slice.js.map +1 -0
- package/dist/queries/stale-abstractions.d.ts +18 -0
- package/dist/queries/stale-abstractions.js +9 -0
- package/dist/queries/stale-abstractions.js.map +1 -0
- package/dist/queries/stats.d.ts +6 -0
- package/dist/queries/stats.js +7 -0
- package/dist/queries/stats.js.map +1 -0
- package/dist/queries/surface.d.ts +7 -0
- package/dist/queries/surface.js +8 -0
- package/dist/queries/surface.js.map +1 -0
- package/dist/queries/symbols.d.ts +6 -0
- package/dist/queries/symbols.js +9 -0
- package/dist/queries/symbols.js.map +1 -0
- package/dist/queries/system.d.ts +7 -0
- package/dist/queries/system.js +9 -0
- package/dist/queries/system.js.map +1 -0
- package/dist/queries/test-coverage.d.ts +22 -0
- package/dist/queries/test-coverage.js +11 -0
- package/dist/queries/test-coverage.js.map +1 -0
- package/dist/queries/trace.d.ts +6 -0
- package/dist/queries/trace.js +8 -0
- package/dist/queries/trace.js.map +1 -0
- package/dist/queries/wrapper-candidates.d.ts +17 -0
- package/dist/queries/wrapper-candidates.js +9 -0
- package/dist/queries/wrapper-candidates.js.map +1 -0
- package/dist/reindex-worker.js +368 -0
- package/dist/reindex-worker.js.map +1 -0
- package/docs/AGENT_GUIDE.md +359 -0
- package/package.json +70 -0
- package/reports/debloat/2026-04-10-scip-query-self-audit.md +161 -0
- package/skills/concrete-plan/SKILL.md +318 -0
- package/skills/scip-debloat/SKILL.md +413 -0
- package/skills/scip-explore/SKILL.md +235 -0
- package/skills/scip-verify/SKILL.md +323 -0
- package/src/cli.ts +1480 -0
- package/src/config.ts +117 -0
- package/src/db.ts +127 -0
- package/src/gitignore-filter.ts +143 -0
- package/src/index.ts +11 -0
- package/src/postinstall.ts +8 -0
- package/src/queries/affected.ts +86 -0
- package/src/queries/bottlenecks.ts +67 -0
- package/src/queries/by-kind.ts +204 -0
- package/src/queries/call-graph.ts +66 -0
- package/src/queries/change-surface.ts +110 -0
- package/src/queries/clean-signature.ts +22 -0
- package/src/queries/code.ts +101 -0
- package/src/queries/complexity-hotspots.ts +119 -0
- package/src/queries/complexity.ts +152 -0
- package/src/queries/convergence.ts +82 -0
- package/src/queries/coupling.ts +99 -0
- package/src/queries/cycles.ts +78 -0
- package/src/queries/dataflow.ts +128 -0
- package/src/queries/dead.ts +122 -0
- package/src/queries/deep-chains.ts +59 -0
- package/src/queries/deps.ts +46 -0
- package/src/queries/diff-impact.ts +204 -0
- package/src/queries/doc-coverage.ts +86 -0
- package/src/queries/drift.ts +224 -0
- package/src/queries/extract-candidates.ts +167 -0
- package/src/queries/fan.ts +148 -0
- package/src/queries/files.ts +16 -0
- package/src/queries/health.ts +324 -0
- package/src/queries/hierarchy.ts +49 -0
- package/src/queries/hotspots.ts +53 -0
- package/src/queries/imports.ts +95 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/isolated.ts +67 -0
- package/src/queries/members.ts +54 -0
- package/src/queries/methods.ts +27 -0
- package/src/queries/outline.ts +52 -0
- package/src/queries/passthrough-candidates.ts +94 -0
- package/src/queries/redundant-reexports.ts +170 -0
- package/src/queries/refs.ts +27 -0
- package/src/queries/similar-chains.ts +314 -0
- package/src/queries/similar-files.ts +140 -0
- package/src/queries/similar-signatures.ts +151 -0
- package/src/queries/similar.ts +305 -0
- package/src/queries/slice.ts +154 -0
- package/src/queries/stale-abstractions.ts +82 -0
- package/src/queries/stats.ts +22 -0
- package/src/queries/surface.ts +34 -0
- package/src/queries/symbols.ts +39 -0
- package/src/queries/system.ts +86 -0
- package/src/queries/test-coverage.ts +106 -0
- package/src/queries/trace.ts +55 -0
- package/src/queries/wrapper-candidates.ts +112 -0
- package/src/query-support.ts +226 -0
- package/src/reindex/detect.ts +58 -0
- package/src/reindex/index.ts +153 -0
- package/src/reindex/indexers.ts +220 -0
- package/src/reindex/install.ts +125 -0
- package/src/reindex-worker.ts +35 -0
- package/src/setup.ts +202 -0
- package/src/symbol-parser.ts +278 -0
- package/src/types.ts +654 -0
- package/src/watch.ts +274 -0
- package/tests/gitignore-filter.test.ts +48 -0
- package/tests/queries.test.ts +300 -0
- package/tests/symbol-parser.test.ts +157 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +40 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shortenSymbol
|
|
3
|
+
} from "./chunk-QOV2R2WT.js";
|
|
4
|
+
|
|
5
|
+
// src/queries/surface.ts
|
|
6
|
+
function surface(db, modulePattern) {
|
|
7
|
+
const rows = db.all(
|
|
8
|
+
`SELECT DISTINCT d1.relative_path, gs.symbol
|
|
9
|
+
FROM mentions m
|
|
10
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
11
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
12
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
13
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
14
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
15
|
+
WHERE d2.relative_path LIKE ?
|
|
16
|
+
AND d1.relative_path NOT LIKE ?
|
|
17
|
+
AND ${db.localSymbolPredicate}
|
|
18
|
+
AND m.role = 0
|
|
19
|
+
ORDER BY d1.relative_path`,
|
|
20
|
+
`%${modulePattern}%`,
|
|
21
|
+
`%${modulePattern}%`
|
|
22
|
+
);
|
|
23
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
24
|
+
consumer: r.relative_path,
|
|
25
|
+
symbol: r.symbol,
|
|
26
|
+
shortName: shortenSymbol(r.symbol)
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
surface
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=chunk-FFSWWE5O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shortenSymbol
|
|
3
|
+
} from "./chunk-QOV2R2WT.js";
|
|
4
|
+
|
|
5
|
+
// src/queries/doc-coverage.ts
|
|
6
|
+
function docCoverage(db, opts = {}) {
|
|
7
|
+
const { scope, minLoc = 3, limit = 50 } = opts;
|
|
8
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
9
|
+
const totalRow = db.get(
|
|
10
|
+
`SELECT COUNT(*) AS c
|
|
11
|
+
FROM global_symbols gs
|
|
12
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
13
|
+
JOIN documents d ON der.document_id = d.id
|
|
14
|
+
WHERE 1 = 1
|
|
15
|
+
${db.pathExclusionsFor("d")}
|
|
16
|
+
${db.symbolNoiseFor("gs")}
|
|
17
|
+
AND gs.symbol NOT LIKE '%#%'
|
|
18
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
19
|
+
${scopeFilter}`,
|
|
20
|
+
minLoc
|
|
21
|
+
);
|
|
22
|
+
const docRow = db.get(
|
|
23
|
+
`SELECT COUNT(*) AS c
|
|
24
|
+
FROM global_symbols gs
|
|
25
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
26
|
+
JOIN documents d ON der.document_id = d.id
|
|
27
|
+
WHERE 1 = 1
|
|
28
|
+
${db.pathExclusionsFor("d")}
|
|
29
|
+
${db.symbolNoiseFor("gs")}
|
|
30
|
+
AND gs.symbol NOT LIKE '%#%'
|
|
31
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
32
|
+
AND gs.documentation IS NOT NULL
|
|
33
|
+
AND gs.documentation != ''
|
|
34
|
+
${scopeFilter}`,
|
|
35
|
+
minLoc
|
|
36
|
+
);
|
|
37
|
+
const total = totalRow?.c ?? 0;
|
|
38
|
+
const documented = docRow?.c ?? 0;
|
|
39
|
+
const undocRows = db.all(
|
|
40
|
+
`SELECT gs.symbol, d.relative_path, der.start_line
|
|
41
|
+
FROM global_symbols gs
|
|
42
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
43
|
+
JOIN documents d ON der.document_id = d.id
|
|
44
|
+
WHERE 1 = 1
|
|
45
|
+
${db.pathExclusionsFor("d")}
|
|
46
|
+
${db.symbolNoiseFor("gs")}
|
|
47
|
+
AND gs.symbol NOT LIKE '%#%'
|
|
48
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
49
|
+
AND (gs.documentation IS NULL OR gs.documentation = '')
|
|
50
|
+
${scopeFilter}
|
|
51
|
+
ORDER BY d.relative_path, der.start_line
|
|
52
|
+
LIMIT ?`,
|
|
53
|
+
minLoc,
|
|
54
|
+
limit
|
|
55
|
+
);
|
|
56
|
+
return {
|
|
57
|
+
totalSymbols: total,
|
|
58
|
+
documented,
|
|
59
|
+
undocumented: total - documented,
|
|
60
|
+
coveragePercent: total > 0 ? Math.round(documented / total * 100) : 0,
|
|
61
|
+
undocumentedSymbols: undocRows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
62
|
+
symbol: r.symbol,
|
|
63
|
+
shortName: shortenSymbol(r.symbol),
|
|
64
|
+
relativePath: r.relative_path,
|
|
65
|
+
startLine: r.start_line
|
|
66
|
+
}))
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
docCoverage
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=chunk-FGXRVW7G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// src/query-support.ts
|
|
2
|
+
var TEST_FILE_PATTERNS = [
|
|
3
|
+
"%/__tests__/%",
|
|
4
|
+
"%.test.%",
|
|
5
|
+
"%.spec.%",
|
|
6
|
+
"%/test/%",
|
|
7
|
+
"%/tests/%",
|
|
8
|
+
"%_test.%",
|
|
9
|
+
"%_spec.%",
|
|
10
|
+
"%/test_%.%",
|
|
11
|
+
"%/spec_%.%"
|
|
12
|
+
];
|
|
13
|
+
var TEST_SUPPORT_PATH_PATTERNS = [
|
|
14
|
+
"%/test-utils/%"
|
|
15
|
+
];
|
|
16
|
+
function testFileMatchSql(alias, patterns = TEST_FILE_PATTERNS) {
|
|
17
|
+
return `(${patterns.map((pattern) => `${alias}.relative_path LIKE '${pattern}'`).join(" OR ")})`;
|
|
18
|
+
}
|
|
19
|
+
function testFileExclusionSql(alias, extraPatterns = []) {
|
|
20
|
+
const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);
|
|
21
|
+
return patterns.map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`).join("\n AND ");
|
|
22
|
+
}
|
|
23
|
+
function buildFileDepGraph(db, scope) {
|
|
24
|
+
const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : "";
|
|
25
|
+
const edges = db.all(
|
|
26
|
+
`SELECT DISTINCT
|
|
27
|
+
d1.relative_path AS from_file,
|
|
28
|
+
d2.relative_path AS to_file
|
|
29
|
+
FROM mentions m
|
|
30
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
31
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
32
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
33
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
34
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
35
|
+
WHERE d1.id != d2.id
|
|
36
|
+
AND m.role = 0
|
|
37
|
+
${db.pathExclusionsFor("d1", "d2")}
|
|
38
|
+
${scopeFilter}`
|
|
39
|
+
);
|
|
40
|
+
const graph = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const edge of edges) {
|
|
42
|
+
if (db.isIgnored(edge.from_file) || db.isIgnored(edge.to_file)) continue;
|
|
43
|
+
if (!graph.has(edge.from_file)) graph.set(edge.from_file, /* @__PURE__ */ new Set());
|
|
44
|
+
graph.get(edge.from_file).add(edge.to_file);
|
|
45
|
+
}
|
|
46
|
+
return graph;
|
|
47
|
+
}
|
|
48
|
+
function findFirstSymbolMatch(db, symbolPattern) {
|
|
49
|
+
const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
|
|
50
|
+
if (fileLineMatch) {
|
|
51
|
+
const [, filePath, startStr, endStr] = fileLineMatch;
|
|
52
|
+
const row = db.get(
|
|
53
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
54
|
+
FROM global_symbols gs
|
|
55
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
56
|
+
JOIN documents d ON der.document_id = d.id
|
|
57
|
+
WHERE d.relative_path LIKE ?
|
|
58
|
+
AND der.start_line <= ? AND der.end_line >= ?
|
|
59
|
+
${db.pathExclusionsFor("d")}
|
|
60
|
+
ORDER BY (der.end_line - der.start_line) ASC
|
|
61
|
+
LIMIT 1`,
|
|
62
|
+
`%${filePath}%`,
|
|
63
|
+
parseInt(startStr, 10),
|
|
64
|
+
parseInt(endStr, 10)
|
|
65
|
+
);
|
|
66
|
+
if (row && !db.isIgnored(row.relative_path)) {
|
|
67
|
+
return {
|
|
68
|
+
symbolId: row.id,
|
|
69
|
+
symbol: row.symbol,
|
|
70
|
+
documentId: row.document_id,
|
|
71
|
+
startLine: row.start_line,
|
|
72
|
+
endLine: row.end_line,
|
|
73
|
+
relativePath: row.relative_path
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const cleaned = symbolPattern.replace(/\(\)$/, "").replace(/\(.*$/, "");
|
|
78
|
+
for (const useNoiseFilter of [true, false]) {
|
|
79
|
+
const noiseClause = useNoiseFilter ? db.symbolNoiseFor("gs") : "";
|
|
80
|
+
const row = db.get(
|
|
81
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
82
|
+
FROM global_symbols gs
|
|
83
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
84
|
+
JOIN documents d ON der.document_id = d.id
|
|
85
|
+
WHERE gs.symbol LIKE ?
|
|
86
|
+
${db.pathExclusionsFor("d")}
|
|
87
|
+
${noiseClause}
|
|
88
|
+
ORDER BY (der.end_line - der.start_line) DESC
|
|
89
|
+
LIMIT 1`,
|
|
90
|
+
`%${cleaned}%`
|
|
91
|
+
);
|
|
92
|
+
if (row && !db.isIgnored(row.relative_path)) {
|
|
93
|
+
return {
|
|
94
|
+
symbolId: row.id,
|
|
95
|
+
symbol: row.symbol,
|
|
96
|
+
documentId: row.document_id,
|
|
97
|
+
startLine: row.start_line,
|
|
98
|
+
endLine: row.end_line,
|
|
99
|
+
relativePath: row.relative_path
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function getCalleeRowsForSymbol(db, symbol, opts = {}) {
|
|
106
|
+
const rows = db.all(
|
|
107
|
+
`SELECT DISTINCT
|
|
108
|
+
callee_gs.symbol AS symbol,
|
|
109
|
+
callee_d.relative_path AS file,
|
|
110
|
+
c.id AS chunk_id
|
|
111
|
+
FROM mentions m
|
|
112
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
113
|
+
JOIN global_symbols callee_gs ON m.symbol_id = callee_gs.id
|
|
114
|
+
JOIN defn_enclosing_ranges callee_der ON callee_gs.id = callee_der.symbol_id
|
|
115
|
+
JOIN documents callee_d ON callee_der.document_id = callee_d.id
|
|
116
|
+
WHERE c.document_id = ?
|
|
117
|
+
AND c.start_line >= ?
|
|
118
|
+
AND c.end_line <= ?
|
|
119
|
+
AND m.role = 0
|
|
120
|
+
AND callee_gs.id != ?
|
|
121
|
+
${db.symbolNoiseFor("callee_gs")}
|
|
122
|
+
${db.pathExclusionsFor("callee_d")}
|
|
123
|
+
ORDER BY callee_d.relative_path
|
|
124
|
+
${opts.limit ? "LIMIT ?" : ""}`,
|
|
125
|
+
...calleeQueryParams(symbol, opts.limit)
|
|
126
|
+
);
|
|
127
|
+
return rows.filter((row) => !db.isIgnored(row.file)).map((row) => ({
|
|
128
|
+
symbol: row.symbol,
|
|
129
|
+
file: row.file,
|
|
130
|
+
chunkId: row.chunk_id
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
function calleeQueryParams(symbol, limit) {
|
|
134
|
+
const params = [
|
|
135
|
+
symbol.documentId,
|
|
136
|
+
symbol.startLine,
|
|
137
|
+
symbol.endLine,
|
|
138
|
+
symbol.symbolId
|
|
139
|
+
];
|
|
140
|
+
if (typeof limit === "number") {
|
|
141
|
+
params.push(limit);
|
|
142
|
+
}
|
|
143
|
+
return params;
|
|
144
|
+
}
|
|
145
|
+
function uniquePatterns(patterns) {
|
|
146
|
+
return [...new Set(patterns)];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
TEST_FILE_PATTERNS,
|
|
151
|
+
TEST_SUPPORT_PATH_PATTERNS,
|
|
152
|
+
testFileMatchSql,
|
|
153
|
+
testFileExclusionSql,
|
|
154
|
+
buildFileDepGraph,
|
|
155
|
+
findFirstSymbolMatch,
|
|
156
|
+
getCalleeRowsForSymbol
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=chunk-FUHJCHS4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/query-support.ts"],"sourcesContent":["import type { ScipDatabase } from './db.js';\n\nexport interface SymbolLocation {\n documentId: number;\n startLine: number;\n endLine: number;\n symbolId: number;\n}\n\nexport interface SymbolMatch extends SymbolLocation {\n symbol: string;\n relativePath: string;\n}\n\nexport interface CalleeRow {\n symbol: string;\n file: string;\n chunkId: number;\n}\n\nexport const TEST_FILE_PATTERNS = [\n '%/__tests__/%',\n '%.test.%',\n '%.spec.%',\n '%/test/%',\n '%/tests/%',\n '%_test.%',\n '%_spec.%',\n '%/test_%.%',\n '%/spec_%.%',\n] as const;\n\nexport const TEST_SUPPORT_PATH_PATTERNS = [\n '%/test-utils/%',\n] as const;\n\nexport function testFileMatchSql(\n alias: string,\n patterns: readonly string[] = TEST_FILE_PATTERNS,\n): string {\n return `(${patterns.map((pattern) => `${alias}.relative_path LIKE '${pattern}'`).join(' OR ')})`;\n}\n\nexport function testFileExclusionSql(\n alias: string,\n extraPatterns: readonly string[] = [],\n): string {\n const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);\n return patterns\n .map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`)\n .join('\\n AND ');\n}\n\nexport function buildFileDepGraph(\n db: ScipDatabase,\n scope?: string,\n): Map<string, Set<string>> {\n const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : '';\n\n const edges = db.all<{ from_file: string; to_file: string }>(\n `SELECT DISTINCT\n d1.relative_path AS from_file,\n d2.relative_path AS to_file\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.id != d2.id\n AND m.role = 0\n ${db.pathExclusionsFor('d1', 'd2')}\n ${scopeFilter}`,\n );\n\n const graph = new Map<string, Set<string>>();\n for (const edge of edges) {\n if (db.isIgnored(edge.from_file) || db.isIgnored(edge.to_file)) continue;\n if (!graph.has(edge.from_file)) graph.set(edge.from_file, new Set());\n graph.get(edge.from_file)!.add(edge.to_file);\n }\n\n return graph;\n}\n\nexport function findFirstSymbolMatch(\n db: ScipDatabase,\n symbolPattern: string,\n): SymbolMatch | null {\n // Handle file:line-line syntax (e.g., \"src/foo.ts:10-50\")\n const fileLineMatch = symbolPattern.match(/^(.+):(\\d+)-(\\d+)$/);\n if (fileLineMatch) {\n const [, filePath, startStr, endStr] = fileLineMatch;\n const row = db.get<{\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 d.relative_path LIKE ?\n AND der.start_line <= ? AND der.end_line >= ?\n ${db.pathExclusionsFor('d')}\n ORDER BY (der.end_line - der.start_line) ASC\n LIMIT 1`,\n `%${filePath}%`, parseInt(startStr!, 10), parseInt(endStr!, 10),\n );\n if (row && !db.isIgnored(row.relative_path)) {\n return {\n symbolId: row.id,\n symbol: row.symbol,\n documentId: row.document_id,\n startLine: row.start_line,\n endLine: row.end_line,\n relativePath: row.relative_path,\n };\n }\n }\n\n // Strip parentheses from the pattern to avoid shell escaping issues.\n // Agents often pass \"functionName()\" — strip the () for matching.\n const cleaned = symbolPattern.replace(/\\(\\)$/, '').replace(/\\(.*$/, '');\n\n // Try exact-ish match first (with noise filter), then fallback without noise filter.\n // The noise filter excludes %().(% which can incorrectly match some symbols.\n for (const useNoiseFilter of [true, false]) {\n const noiseClause = useNoiseFilter ? db.symbolNoiseFor('gs') : '';\n const row = db.get<{\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 gs.symbol LIKE ?\n ${db.pathExclusionsFor('d')}\n ${noiseClause}\n ORDER BY (der.end_line - der.start_line) DESC\n LIMIT 1`,\n `%${cleaned}%`,\n );\n\n if (row && !db.isIgnored(row.relative_path)) {\n return {\n symbolId: row.id,\n symbol: row.symbol,\n documentId: row.document_id,\n startLine: row.start_line,\n endLine: row.end_line,\n relativePath: row.relative_path,\n };\n }\n }\n\n return null;\n}\n\nexport function getCalleeRowsForSymbol(\n db: ScipDatabase,\n symbol: SymbolLocation,\n opts: { limit?: number } = {},\n): CalleeRow[] {\n const rows = db.all<{\n symbol: string;\n file: string;\n chunk_id: number;\n }>(\n `SELECT DISTINCT\n callee_gs.symbol AS symbol,\n callee_d.relative_path AS file,\n c.id AS chunk_id\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN global_symbols callee_gs ON m.symbol_id = callee_gs.id\n JOIN defn_enclosing_ranges callee_der ON callee_gs.id = callee_der.symbol_id\n JOIN documents callee_d ON callee_der.document_id = callee_d.id\n WHERE c.document_id = ?\n AND c.start_line >= ?\n AND c.end_line <= ?\n AND m.role = 0\n AND callee_gs.id != ?\n ${db.symbolNoiseFor('callee_gs')}\n ${db.pathExclusionsFor('callee_d')}\n ORDER BY callee_d.relative_path\n ${opts.limit ? 'LIMIT ?' : ''}`,\n ...calleeQueryParams(symbol, opts.limit),\n );\n\n return rows.filter((row) => !db.isIgnored(row.file)).map((row) => ({\n symbol: row.symbol,\n file: row.file,\n chunkId: row.chunk_id,\n }));\n}\n\nfunction calleeQueryParams(\n symbol: SymbolLocation,\n limit?: number,\n): number[] {\n const params = [\n symbol.documentId,\n symbol.startLine,\n symbol.endLine,\n symbol.symbolId,\n ];\n\n if (typeof limit === 'number') {\n params.push(limit);\n }\n\n return params;\n}\n\nfunction uniquePatterns(patterns: readonly string[]): string[] {\n return [...new Set(patterns)];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
testFileExclusionSql
|
|
3
|
+
} from "./chunk-FUHJCHS4.js";
|
|
4
|
+
import {
|
|
5
|
+
shortenSymbol
|
|
6
|
+
} from "./chunk-QOV2R2WT.js";
|
|
7
|
+
|
|
8
|
+
// src/queries/stale-abstractions.ts
|
|
9
|
+
function staleAbstractions(db, opts) {
|
|
10
|
+
const { scope, minLoc = 3, limit = 30 } = opts ?? {};
|
|
11
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
12
|
+
const rows = db.all(
|
|
13
|
+
`SELECT * FROM (
|
|
14
|
+
SELECT
|
|
15
|
+
gs.symbol,
|
|
16
|
+
d.relative_path AS file,
|
|
17
|
+
der.start_line,
|
|
18
|
+
der.end_line,
|
|
19
|
+
(der.end_line - der.start_line + 1) AS loc,
|
|
20
|
+
(SELECT COUNT(DISTINCT ref_c.document_id)
|
|
21
|
+
FROM mentions ref_m
|
|
22
|
+
JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
|
|
23
|
+
WHERE ref_m.symbol_id = gs.id
|
|
24
|
+
AND ref_m.role = 0
|
|
25
|
+
AND ref_c.document_id != der.document_id
|
|
26
|
+
) AS consumers
|
|
27
|
+
FROM global_symbols gs
|
|
28
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
29
|
+
JOIN documents d ON der.document_id = d.id
|
|
30
|
+
WHERE 1 = 1
|
|
31
|
+
${db.pathExclusionsFor("d")}
|
|
32
|
+
AND ${testFileExclusionSql("d")}
|
|
33
|
+
${db.symbolNoiseFor("gs")}
|
|
34
|
+
-- Top-level type symbols: ends with # but does not contain nested #
|
|
35
|
+
AND gs.symbol LIKE '%#'
|
|
36
|
+
AND gs.symbol NOT LIKE '%#%#%'
|
|
37
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
38
|
+
${scopeFilter}
|
|
39
|
+
) WHERE consumers <= 1
|
|
40
|
+
ORDER BY loc DESC
|
|
41
|
+
LIMIT ?`,
|
|
42
|
+
minLoc,
|
|
43
|
+
limit
|
|
44
|
+
);
|
|
45
|
+
return rows.filter((r) => !db.isIgnored(r.file)).filter((r) => {
|
|
46
|
+
const basename = r.file.split("/").pop() ?? "";
|
|
47
|
+
const isTypeFile = basename.includes("types") || r.file.includes("/types/");
|
|
48
|
+
if (isTypeFile && r.consumers > 0) return false;
|
|
49
|
+
return true;
|
|
50
|
+
}).map((r) => ({
|
|
51
|
+
symbol: r.symbol,
|
|
52
|
+
shortName: shortenSymbol(r.symbol),
|
|
53
|
+
file: r.file,
|
|
54
|
+
startLine: r.start_line,
|
|
55
|
+
endLine: r.end_line,
|
|
56
|
+
loc: r.loc,
|
|
57
|
+
consumers: r.consumers
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
staleAbstractions
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=chunk-GJFURBEW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/stale-abstractions.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { testFileExclusionSql } from '../query-support.js';\nimport type { StaleAbstraction } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find stale abstractions: type-level symbols (classes, interfaces, type\n * aliases) that have 0 or 1 cross-file consumers.\n *\n * A type that only one file uses is over-abstracted — it was designed\n * for reuse that never materialized. Large single-consumer types are\n * the strongest signal of wasted abstraction.\n */\nexport function staleAbstractions(\n db: ScipDatabase,\n opts?: { scope?: string; minLoc?: number; limit?: number },\n): StaleAbstraction[] {\n const { scope, minLoc = 3, 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 consumers: number;\n }>(\n `SELECT * FROM (\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 (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\n AND ref_m.role = 0\n AND ref_c.document_id != der.document_id\n ) AS consumers\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 -- Top-level type symbols: ends with # but does not contain nested #\n AND gs.symbol LIKE '%#'\n AND gs.symbol NOT LIKE '%#%#%'\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}\n ) WHERE consumers <= 1\n ORDER BY loc DESC\n LIMIT ?`,\n minLoc, limit,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.file))\n // Exclude types defined in dedicated type files (types.ts, types/, etc.)\n // These are intentional public API types, not premature abstractions.\n .filter((r) => {\n const basename = r.file.split('/').pop() ?? '';\n const isTypeFile = basename.includes('types') || r.file.includes('/types/');\n // Types in type files with 1 consumer are normal API types — skip them.\n // Types in type files with 0 consumers are genuinely unused — keep them.\n if (isTypeFile && r.consumers > 0) return false;\n return true;\n })\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 consumers: r.consumers,\n }));\n}\n"],"mappings":";;;;;;;;AAaO,SAAS,kBACd,IACA,MACoB;AACpB,QAAM,EAAE,OAAO,SAAS,GAAG,QAAQ,GAAG,IAAI,QAAQ,CAAC;AACnD,QAAM,cAAc,QAAQ,8BAA8B,KAAK,OAAO;AAEtE,QAAM,OAAO,GAAG;AAAA,IAQd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAkBM,GAAG,kBAAkB,GAAG,CAAC;AAAA,cACrB,qBAAqB,GAAG,CAAC;AAAA,UAC7B,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,UAKvB,WAAW;AAAA;AAAA;AAAA;AAAA,IAIjB;AAAA,IAAQ;AAAA,EACV;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,IAAI,CAAC,EAGnC,OAAO,CAAC,MAAM;AACb,UAAM,WAAW,EAAE,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK;AAC5C,UAAM,aAAa,SAAS,SAAS,OAAO,KAAK,EAAE,KAAK,SAAS,SAAS;AAG1E,QAAI,cAAc,EAAE,YAAY,EAAG,QAAO;AAC1C,WAAO;AAAA,EACT,CAAC,EACA,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,WAAW,EAAE;AAAA,EACf,EAAE;AACN;","names":[]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shortenSymbol
|
|
3
|
+
} from "./chunk-QOV2R2WT.js";
|
|
4
|
+
|
|
5
|
+
// src/queries/fan.ts
|
|
6
|
+
function fanIn(db, symbolPattern) {
|
|
7
|
+
const rows = db.all(
|
|
8
|
+
`SELECT gs.symbol, COUNT(DISTINCT c.document_id) AS file_count
|
|
9
|
+
FROM mentions m
|
|
10
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
11
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
12
|
+
WHERE gs.symbol LIKE ?
|
|
13
|
+
AND m.role = 0
|
|
14
|
+
GROUP BY gs.id
|
|
15
|
+
ORDER BY file_count DESC`,
|
|
16
|
+
`%${symbolPattern}%`
|
|
17
|
+
);
|
|
18
|
+
return rows.map((r) => ({
|
|
19
|
+
name: shortenSymbol(r.symbol),
|
|
20
|
+
count: r.file_count
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
function fanOut(db, filePattern) {
|
|
24
|
+
const rows = db.all(
|
|
25
|
+
`SELECT d.relative_path, COUNT(DISTINCT gs.id) AS symbol_count
|
|
26
|
+
FROM mentions m
|
|
27
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
28
|
+
JOIN documents d ON c.document_id = d.id
|
|
29
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
30
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
31
|
+
JOIN documents def_d ON der.document_id = def_d.id
|
|
32
|
+
WHERE d.relative_path LIKE ?
|
|
33
|
+
AND m.role = 0
|
|
34
|
+
AND def_d.id != d.id
|
|
35
|
+
GROUP BY d.id
|
|
36
|
+
ORDER BY symbol_count DESC`,
|
|
37
|
+
`%${filePattern}%`
|
|
38
|
+
);
|
|
39
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
40
|
+
name: r.relative_path,
|
|
41
|
+
count: r.symbol_count
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
function topFanIn(db, opts = {}) {
|
|
45
|
+
const { limit = 30, scope } = opts;
|
|
46
|
+
const scopeFilter = scope ? `AND def_d.relative_path LIKE '%${scope}%'` : "";
|
|
47
|
+
const rows = db.all(
|
|
48
|
+
`SELECT gs.symbol, COUNT(DISTINCT c.document_id) AS file_count
|
|
49
|
+
FROM mentions m
|
|
50
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
51
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
52
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
53
|
+
JOIN documents def_d ON der.document_id = def_d.id
|
|
54
|
+
WHERE m.role = 0
|
|
55
|
+
${db.pathExclusionsFor("def_d")}
|
|
56
|
+
${db.symbolNoiseFor("gs")}
|
|
57
|
+
${scopeFilter}
|
|
58
|
+
GROUP BY gs.id
|
|
59
|
+
HAVING file_count > 1
|
|
60
|
+
ORDER BY file_count DESC
|
|
61
|
+
LIMIT ?`,
|
|
62
|
+
limit
|
|
63
|
+
);
|
|
64
|
+
return rows.map((r) => ({
|
|
65
|
+
name: shortenSymbol(r.symbol),
|
|
66
|
+
count: r.file_count
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
function topFanOut(db, opts = {}) {
|
|
70
|
+
const { limit = 30, scope } = opts;
|
|
71
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
72
|
+
const rows = db.all(
|
|
73
|
+
`SELECT d.relative_path, COUNT(DISTINCT gs.id) AS symbol_count
|
|
74
|
+
FROM mentions m
|
|
75
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
76
|
+
JOIN documents d ON c.document_id = d.id
|
|
77
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
78
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
79
|
+
JOIN documents def_d ON der.document_id = def_d.id
|
|
80
|
+
WHERE m.role = 0
|
|
81
|
+
AND def_d.id != d.id
|
|
82
|
+
${db.pathExclusionsFor("d")}
|
|
83
|
+
${db.symbolNoiseFor("gs")}
|
|
84
|
+
${scopeFilter}
|
|
85
|
+
GROUP BY d.id
|
|
86
|
+
ORDER BY symbol_count DESC
|
|
87
|
+
LIMIT ?`,
|
|
88
|
+
limit
|
|
89
|
+
);
|
|
90
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
91
|
+
name: r.relative_path,
|
|
92
|
+
count: r.symbol_count
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export {
|
|
97
|
+
fanIn,
|
|
98
|
+
fanOut,
|
|
99
|
+
topFanIn,
|
|
100
|
+
topFanOut
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=chunk-GTILYBH6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queries/fan.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { FanResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Fan-in: how many distinct files reference this symbol.\n * High fan-in = widely depended upon = high blast radius for changes.\n */\nexport function fanIn(\n db: ScipDatabase,\n symbolPattern: string,\n): FanResult[] {\n const rows = db.all<{\n symbol: string;\n file_count: number;\n }>(\n `SELECT gs.symbol, COUNT(DISTINCT c.document_id) AS file_count\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n WHERE gs.symbol LIKE ?\n AND m.role = 0\n GROUP BY gs.id\n ORDER BY file_count DESC`,\n `%${symbolPattern}%`,\n );\n\n return rows.map((r) => ({\n name: shortenSymbol(r.symbol),\n count: r.file_count,\n }));\n}\n\n/**\n * Fan-out: how many external symbols does this file reference.\n * High fan-out = depends on many things = fragile to upstream changes.\n */\nexport function fanOut(\n db: ScipDatabase,\n filePattern: string,\n): FanResult[] {\n const rows = db.all<{\n relative_path: string;\n symbol_count: number;\n }>(\n `SELECT d.relative_path, COUNT(DISTINCT gs.id) AS symbol_count\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 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 d.relative_path LIKE ?\n AND m.role = 0\n AND def_d.id != d.id\n GROUP BY d.id\n ORDER BY symbol_count DESC`,\n `%${filePattern}%`,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.relative_path))\n .map((r) => ({\n name: r.relative_path,\n count: r.symbol_count,\n }));\n}\n\n/**\n * Top fan-in across the whole codebase — the most depended-on symbols.\n */\nexport function topFanIn(\n db: ScipDatabase,\n opts: { limit?: number; scope?: string } = {},\n): FanResult[] {\n const { limit = 30, scope } = opts;\n const scopeFilter = scope\n ? `AND def_d.relative_path LIKE '%${scope}%'`\n : '';\n\n const rows = db.all<{\n symbol: string;\n file_count: number;\n }>(\n `SELECT gs.symbol, COUNT(DISTINCT c.document_id) AS file_count\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.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 HAVING file_count > 1\n ORDER BY file_count DESC\n LIMIT ?`,\n limit,\n );\n\n return rows.map((r) => ({\n name: shortenSymbol(r.symbol),\n count: r.file_count,\n }));\n}\n\n/**\n * Top fan-out across the whole codebase — files that depend on the most external symbols.\n */\nexport function topFanOut(\n db: ScipDatabase,\n opts: { limit?: number; scope?: string } = {},\n): FanResult[] {\n const { limit = 30, scope } = opts;\n const scopeFilter = scope\n ? `AND d.relative_path LIKE '%${scope}%'`\n : '';\n\n const rows = db.all<{\n relative_path: string;\n symbol_count: number;\n }>(\n `SELECT d.relative_path, COUNT(DISTINCT gs.id) AS symbol_count\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 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 AND def_d.id != d.id\n ${db.pathExclusionsFor('d')}\n ${db.symbolNoiseFor('gs')}\n ${scopeFilter}\n GROUP BY d.id\n ORDER BY symbol_count DESC\n LIMIT ?`,\n limit,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.relative_path))\n .map((r) => ({\n name: r.relative_path,\n count: r.symbol_count,\n }));\n}\n"],"mappings":";;;;;AAQO,SAAS,MACd,IACA,eACa;AACb,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,IAAI,aAAa;AAAA,EACnB;AAEA,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,MAAM,cAAc,EAAE,MAAM;AAAA,IAC5B,OAAO,EAAE;AAAA,EACX,EAAE;AACJ;AAMO,SAAS,OACd,IACA,aACa;AACb,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYA,IAAI,WAAW;AAAA,EACjB;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,aAAa,CAAC,EAC5C,IAAI,CAAC,OAAO;AAAA,IACX,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,EACX,EAAE;AACN;AAKO,SAAS,SACd,IACA,OAA2C,CAAC,GAC/B;AACb,QAAM,EAAE,QAAQ,IAAI,MAAM,IAAI;AAC9B,QAAM,cAAc,QAChB,kCAAkC,KAAK,OACvC;AAEJ,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOI,GAAG,kBAAkB,OAAO,CAAC;AAAA,QAC7B,GAAG,eAAe,IAAI,CAAC;AAAA,QACvB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,IAKf;AAAA,EACF;AAEA,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,MAAM,cAAc,EAAE,MAAM;AAAA,IAC5B,OAAO,EAAE;AAAA,EACX,EAAE;AACJ;AAKO,SAAS,UACd,IACA,OAA2C,CAAC,GAC/B;AACb,QAAM,EAAE,QAAQ,IAAI,MAAM,IAAI;AAC9B,QAAM,cAAc,QAChB,8BAA8B,KAAK,OACnC;AAEJ,QAAM,OAAO,GAAG;AAAA,IAId;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASI,GAAG,kBAAkB,GAAG,CAAC;AAAA,QACzB,GAAG,eAAe,IAAI,CAAC;AAAA,QACvB,WAAW;AAAA;AAAA;AAAA;AAAA,IAIf;AAAA,EACF;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,aAAa,CAAC,EAC5C,IAAI,CAAC,OAAO;AAAA,IACX,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,EACX,EAAE;AACN;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-JJP7KQND.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|