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