scip-query 0.5.0 → 0.6.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 +14 -7
- package/dist/chunk-2DSS2NGF.js +10 -0
- package/dist/chunk-2RLP74AO.js +2 -0
- package/dist/chunk-4QJ7LVW5.js +2 -0
- package/dist/chunk-4TYGGOLO.js +5 -0
- package/dist/chunk-5IADAU5B.js +7 -0
- package/dist/chunk-7754WFFV.js +18 -0
- package/dist/chunk-7VOF4ZG6.js +2 -0
- package/dist/chunk-7Z4COVMC.js +2 -0
- package/dist/chunk-AJ5PWKD4.js +2 -0
- package/dist/chunk-BDBRZPX3.js +7 -0
- package/dist/chunk-BE6EQIWY.js +2 -0
- package/dist/chunk-BQ3INTYT.js +8 -0
- package/dist/chunk-D7KLLMPB.js +2 -0
- package/dist/chunk-D7YBWSON.js +29 -0
- package/dist/chunk-DE5ZBHMK.js +39 -0
- package/dist/chunk-DHYIJHXZ.js +33 -0
- package/dist/chunk-EEF3YEHW.js +2 -0
- package/dist/chunk-F2LLHRRZ.js +2 -0
- package/dist/chunk-FCC3XJTI.js +2 -0
- package/dist/chunk-GNXRLK5G.js +2 -0
- package/dist/chunk-GXVB36TG.js +62 -0
- package/dist/chunk-HMKJTAZD.js +2 -0
- package/dist/chunk-IBGBI3VU.js +2 -0
- package/dist/chunk-IYFZS4PV.js +84 -0
- package/dist/chunk-JH3A7HTU.js +2 -0
- package/dist/chunk-JS2RNIC7.js +2 -0
- package/dist/chunk-K5FQFCSN.js +41 -0
- package/dist/chunk-K6GBKEQE.js +6 -0
- package/dist/chunk-KO7YJRWP.js +12 -0
- package/dist/chunk-KYT47WU2.js +4 -0
- package/dist/chunk-LORWXBOO.js +2 -0
- package/dist/chunk-LX4H4LLG.js +89 -0
- package/dist/chunk-N3Z2SJCR.js +2 -0
- package/dist/chunk-NTDA4A2D.js +25 -0
- package/dist/chunk-NXMYYHDO.js +24 -0
- package/dist/chunk-PZ6ESKRH.js +7 -0
- package/dist/chunk-QXE6EDY2.js +6 -0
- package/dist/chunk-RJ7SPBJ5.js +5 -0
- package/dist/chunk-RWE6FHG3.js +3 -0
- package/dist/chunk-SDX6MDBL.js +2 -0
- package/dist/chunk-SG35Y7J2.js +2 -0
- package/dist/chunk-STOGKRJH.js +4 -0
- package/dist/chunk-TINPMWJK.js +2 -0
- package/dist/chunk-UJB62HV3.js +2 -0
- package/dist/chunk-VEUMRDHW.js +2 -0
- package/dist/chunk-WCDXJGYT.js +65 -0
- package/dist/chunk-WTSTDJZ7.js +6 -0
- package/dist/chunk-XAZTIDST.js +2 -0
- package/dist/chunk-XVDASCN7.js +35 -0
- package/dist/chunk-Y7H6D2EV.js +2 -0
- package/dist/chunk-Y7LOQSWY.js +2 -0
- package/dist/chunk-YIPCV7M7.js +70 -0
- package/dist/chunk-ZSRXMNMK.js +5 -0
- package/dist/chunk-ZXKURFVB.js +56 -0
- package/dist/cli.js +509 -8758
- package/dist/{db-6F9R9e_t.d.ts → db-BSTtBG_H.d.ts} +146 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +13 -1616
- package/dist/postinstall.js +4 -100
- package/dist/queries/affected.d.ts +1 -1
- package/dist/queries/affected.js +1 -8
- package/dist/queries/bottlenecks.d.ts +1 -1
- package/dist/queries/bottlenecks.js +1 -8
- package/dist/queries/by-kind.d.ts +1 -4
- package/dist/queries/by-kind.js +1 -10
- package/dist/queries/call-graph.d.ts +1 -1
- package/dist/queries/call-graph.js +1 -8
- package/dist/queries/change-surface.d.ts +1 -1
- package/dist/queries/change-surface.js +1 -8
- package/dist/queries/code.d.ts +1 -1
- package/dist/queries/code.js +1 -8
- package/dist/queries/complexity-hotspots.d.ts +5 -10
- package/dist/queries/complexity-hotspots.js +1 -8
- package/dist/queries/complexity.d.ts +1 -1
- package/dist/queries/complexity.js +1 -8
- package/dist/queries/convergence.d.ts +1 -1
- package/dist/queries/convergence.js +1 -8
- package/dist/queries/coupling.d.ts +1 -1
- package/dist/queries/coupling.js +1 -10
- package/dist/queries/cycles.d.ts +1 -1
- package/dist/queries/cycles.js +1 -8
- package/dist/queries/dataflow.d.ts +1 -1
- package/dist/queries/dataflow.js +1 -8
- package/dist/queries/dead.d.ts +1 -1
- package/dist/queries/dead.js +1 -9
- package/dist/queries/deep-chains.d.ts +9 -1
- package/dist/queries/deep-chains.js +1 -8
- package/dist/queries/deps.d.ts +1 -1
- package/dist/queries/deps.js +1 -10
- package/dist/queries/diff-impact.d.ts +1 -1
- package/dist/queries/diff-impact.js +1 -7
- package/dist/queries/drift.d.ts +1 -1
- package/dist/queries/drift.js +1 -8
- package/dist/queries/extract-candidates.d.ts +1 -1
- package/dist/queries/extract-candidates.js +1 -8
- package/dist/queries/fan.d.ts +1 -1
- package/dist/queries/fan.js +1 -14
- package/dist/queries/files.d.ts +1 -1
- package/dist/queries/files.js +1 -6
- package/dist/queries/health.d.ts +1 -1
- package/dist/queries/health.js +1 -20
- package/dist/queries/hierarchy.d.ts +1 -1
- package/dist/queries/hierarchy.js +1 -8
- package/dist/queries/hotspots.d.ts +1 -1
- package/dist/queries/hotspots.js +1 -8
- package/dist/queries/imports.d.ts +1 -1
- package/dist/queries/imports.js +1 -12
- package/dist/queries/index.d.ts +1 -1
- package/dist/queries/index.js +1 -197
- package/dist/queries/isolated.d.ts +1 -1
- package/dist/queries/isolated.js +1 -9
- package/dist/queries/members.d.ts +1 -1
- package/dist/queries/members.js +1 -8
- package/dist/queries/methods.d.ts +1 -1
- package/dist/queries/methods.js +1 -8
- package/dist/queries/outline.d.ts +1 -1
- package/dist/queries/outline.js +1 -8
- package/dist/queries/passthrough-candidates.d.ts +1 -1
- package/dist/queries/passthrough-candidates.js +1 -8
- package/dist/queries/redundant-reexports.d.ts +1 -1
- package/dist/queries/redundant-reexports.js +1 -9
- package/dist/queries/refs.d.ts +1 -1
- package/dist/queries/refs.js +1 -8
- package/dist/queries/similar-chains.d.ts +1 -1
- package/dist/queries/similar-chains.js +1 -8
- package/dist/queries/similar-files.d.ts +1 -1
- package/dist/queries/similar-files.js +1 -8
- package/dist/queries/similar-signatures.d.ts +1 -1
- package/dist/queries/similar-signatures.js +1 -8
- package/dist/queries/similar.d.ts +1 -1
- package/dist/queries/similar.js +1 -10
- package/dist/queries/slice.d.ts +1 -1
- package/dist/queries/slice.js +1 -8
- package/dist/queries/stale-abstractions.d.ts +15 -5
- package/dist/queries/stale-abstractions.js +1 -8
- package/dist/queries/stats.d.ts +1 -1
- package/dist/queries/stats.js +1 -6
- package/dist/queries/surface.d.ts +1 -1
- package/dist/queries/surface.js +1 -8
- package/dist/queries/symbols.d.ts +1 -1
- package/dist/queries/symbols.js +1 -9
- package/dist/queries/system.d.ts +1 -1
- package/dist/queries/system.js +1 -9
- package/dist/queries/trace.d.ts +1 -1
- package/dist/queries/trace.js +1 -9
- package/dist/queries/wrapper-candidates.d.ts +1 -1
- package/dist/queries/wrapper-candidates.js +1 -8
- package/dist/reindex-worker.js +7 -672
- package/package.json +20 -2
- package/skills/concrete-plan/SKILL.md +3 -3
- package/skills/scip-debloat/SKILL.md +3 -6
- package/skills/scip-language-playbook/SKILL.md +2 -0
- package/dist/chunk-2MQ5DPY6.js +0 -61
- package/dist/chunk-2QTYIOJ5.js +0 -165
- package/dist/chunk-3VI4YXCL.js +0 -172
- package/dist/chunk-3VV2G6U7.js +0 -34
- package/dist/chunk-44PFXVXG.js +0 -76
- package/dist/chunk-6SLFQR36.js +0 -64
- package/dist/chunk-74RFWB5T.js +0 -24
- package/dist/chunk-7DBPRGMS.js +0 -221
- package/dist/chunk-DTB724R3.js +0 -110
- package/dist/chunk-FLOYI6I4.js +0 -185
- package/dist/chunk-FO2CBB7U.js +0 -23
- package/dist/chunk-GJT3MO2T.js +0 -17
- package/dist/chunk-HAP4LJKX.js +0 -66
- package/dist/chunk-JCOJQ4I6.js +0 -93
- package/dist/chunk-JGQMOS4V.js +0 -59
- package/dist/chunk-JMD4WJ2Q.js +0 -213
- package/dist/chunk-JSQPZOPO.js +0 -64
- package/dist/chunk-JSXGC2EH.js +0 -151
- package/dist/chunk-JZN3DRCT.js +0 -59
- package/dist/chunk-KMWYB3CX.js +0 -71
- package/dist/chunk-MRM755FU.js +0 -37
- package/dist/chunk-N2XO3Z5F.js +0 -69
- package/dist/chunk-OLW5UL36.js +0 -76
- package/dist/chunk-OMCRXXDX.js +0 -2600
- package/dist/chunk-OWJOHUZE.js +0 -44
- package/dist/chunk-P3VCDYMJ.js +0 -269
- package/dist/chunk-PEDH3D4G.js +0 -53
- package/dist/chunk-POAN4SCR.js +0 -46
- package/dist/chunk-PTMGEBU3.js +0 -101
- package/dist/chunk-PU44HK7P.js +0 -87
- package/dist/chunk-QJI7EECA.js +0 -327
- package/dist/chunk-R5HICGMB.js +0 -110
- package/dist/chunk-RJ2D6YWQ.js +0 -49
- package/dist/chunk-RZ5GYPBP.js +0 -79
- package/dist/chunk-SRLQNO6O.js +0 -101
- package/dist/chunk-UGS7HJI4.js +0 -84
- package/dist/chunk-VKUUXOE7.js +0 -105
- package/dist/chunk-VPUJSJCI.js +0 -84
- package/dist/chunk-VRWVV3EP.js +0 -72
- package/dist/chunk-WJWQEU4A.js +0 -162
- package/dist/chunk-WJZHDUSB.js +0 -40
- package/dist/chunk-WWOCQ5W4.js +0 -34
- package/dist/chunk-X3Q2OVRL.js +0 -77
- package/dist/chunk-Y3P7QKKN.js +0 -27
- package/dist/chunk-Y6FAHY4N.js +0 -81
- package/dist/chunk-YMSJCSRG.js +0 -213
- package/dist/chunk-ZDL3U4W2.js +0 -124
- package/dist/chunk-ZXNX5JRE.js +0 -216
- package/dist/queries/clean-signature.d.ts +0 -17
- package/dist/queries/clean-signature.js +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scip-query",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Language-agnostic code intelligence CLI powered by SCIP indexes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -66,9 +66,27 @@
|
|
|
66
66
|
"commander": "^13.1.0",
|
|
67
67
|
"ignore": "^7.0.3"
|
|
68
68
|
},
|
|
69
|
+
"//optionalDependencies": "Native parsers and bundled SCIP indexers go in optionalDependencies so a failed install (missing build tools, restricted registry, incompatible platform) downgrades scip-query gracefully — the affected language falls back to regex parsing or asks the user to install its indexer manually — instead of blocking npm install.",
|
|
70
|
+
"optionalDependencies": {
|
|
71
|
+
"@sourcegraph/scip-typescript": "^0.4.0",
|
|
72
|
+
"scip-python-plus": "^0.7.4",
|
|
73
|
+
"tree-sitter": "^0.22.4",
|
|
74
|
+
"tree-sitter-c": "^0.21.4",
|
|
75
|
+
"tree-sitter-c-sharp": "^0.21.3",
|
|
76
|
+
"tree-sitter-cpp": "^0.22.3",
|
|
77
|
+
"tree-sitter-java": "^0.21.0",
|
|
78
|
+
"tree-sitter-javascript": "^0.23.1",
|
|
79
|
+
"tree-sitter-kotlin": "^0.3.8",
|
|
80
|
+
"tree-sitter-php": "^0.23.11",
|
|
81
|
+
"tree-sitter-python": "^0.23.6",
|
|
82
|
+
"tree-sitter-ruby": "^0.23.1",
|
|
83
|
+
"tree-sitter-rust": "^0.23.3",
|
|
84
|
+
"tree-sitter-scala": "^0.24.0",
|
|
85
|
+
"tree-sitter-typescript": "^0.23.2",
|
|
86
|
+
"tree-sitter-vb-dotnet": "^0.1.9"
|
|
87
|
+
},
|
|
69
88
|
"devDependencies": {
|
|
70
89
|
"@eslint/js": "^10.0.1",
|
|
71
|
-
"@sourcegraph/scip-typescript": "^0.4.0",
|
|
72
90
|
"@types/better-sqlite3": "^7.6.12",
|
|
73
91
|
"@types/node": "^22.10.0",
|
|
74
92
|
"eslint": "^10.2.0",
|
|
@@ -67,7 +67,7 @@ This skill deliberately excludes `Grep` and `Read` from its allowed tools. This
|
|
|
67
67
|
- Using `Read` via a subagent to browse files for discovery. Subagents must also use scip-query.
|
|
68
68
|
- Spawning Explore agents that fall back to grep and file reads. If a subagent's output does not cite scip-query commands, reject its findings and re-run.
|
|
69
69
|
|
|
70
|
-
**Instead, use scip-query (
|
|
70
|
+
**Instead, use scip-query (full reference at `/Users/aydansalois/Documents/GitHub/scip-query/README.md`):**
|
|
71
71
|
|
|
72
72
|
| You want to... | Use this |
|
|
73
73
|
|---|---|
|
|
@@ -107,7 +107,7 @@ If none of these can answer your question, say so explicitly in the plan rather
|
|
|
107
107
|
|
|
108
108
|
### Full Documentation
|
|
109
109
|
|
|
110
|
-
- **
|
|
110
|
+
- **Every command with options and examples:** `/Users/aydansalois/Documents/GitHub/scip-query/README.md`
|
|
111
111
|
- **Goal-oriented agent workflows:** `/Users/aydansalois/Documents/GitHub/scip-query/docs/AGENT_GUIDE.md`
|
|
112
112
|
|
|
113
113
|
---
|
|
@@ -255,7 +255,7 @@ When spawning any subagent for this planning process, **include the following bl
|
|
|
255
255
|
|
|
256
256
|
You have the `scip-query` CLI for compiler-resolved code intelligence. Use it for ALL code references — do not use grep, rg, Read, or cat.
|
|
257
257
|
|
|
258
|
-
### scip-query commands
|
|
258
|
+
### scip-query commands
|
|
259
259
|
|
|
260
260
|
Navigation:
|
|
261
261
|
- `scip-query code <symbol>` — read source code (bounded to definition range)
|
|
@@ -73,7 +73,7 @@ scip-query code 'src/modules/chat/chat.service.ts:100-200'
|
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
|
76
|
-
## The
|
|
76
|
+
## The 12 Angles of Bloat
|
|
77
77
|
|
|
78
78
|
Run every one of these. Each catches a different class of problem. Skip none.
|
|
79
79
|
|
|
@@ -108,7 +108,7 @@ Stricter than dead code — these symbols reference nothing AND are referenced b
|
|
|
108
108
|
scip-query similar --min-similarity 0.5 --min-callees 3
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
Functions that call the same set of symbols.
|
|
111
|
+
Functions that call the same set of symbols. Uses TF-IDF weighted cosine similarity over the callee set, so rare shared callees score higher than ubiquitous infrastructure ones — a high score means the pair shares meaningful work, not just the same `db` and `logger`.
|
|
112
112
|
|
|
113
113
|
For each high-similarity pair, get the consolidation prescription:
|
|
114
114
|
|
|
@@ -263,7 +263,6 @@ scip-query deep-chains --min-depth 5 # Excessively deep dependency chains
|
|
|
263
263
|
scip-query bottlenecks -n 10 # Coupling pressure points
|
|
264
264
|
scip-query complexity-hotspots -n 10 # Riskiest symbols
|
|
265
265
|
scip-query hotspots -n 10 # Most-referenced symbols
|
|
266
|
-
scip-query doc-coverage --min-loc 5 # Documentation coverage
|
|
267
266
|
```
|
|
268
267
|
|
|
269
268
|
---
|
|
@@ -281,7 +280,7 @@ Read the health score, the findings breakdown, and the prioritized action list.
|
|
|
281
280
|
|
|
282
281
|
### Phase 2: Deep Scan (10-15 minutes)
|
|
283
282
|
|
|
284
|
-
Run all
|
|
283
|
+
Run all 12 angles plus the structural assessment. For each:
|
|
285
284
|
1. Run the command
|
|
286
285
|
2. Record the count and top findings
|
|
287
286
|
3. For actionable findings, drill deeper (e.g., `convergence` for similar pairs)
|
|
@@ -349,7 +348,6 @@ The report is a markdown file with:
|
|
|
349
348
|
- Max dependency chain depth: N
|
|
350
349
|
- Coupling bottlenecks: [top 5]
|
|
351
350
|
- Complexity hotspots: [top 5]
|
|
352
|
-
- Doc coverage: N%
|
|
353
351
|
```
|
|
354
352
|
|
|
355
353
|
Every finding includes the scip-query command that produced it.
|
|
@@ -402,7 +400,6 @@ Do NOT use grep, rg, or Read. Use only scip-query commands.
|
|
|
402
400
|
| Coupling pressure | `scip-query bottlenecks -n 10` |
|
|
403
401
|
| Complexity hotspots | `scip-query complexity-hotspots -n 10` |
|
|
404
402
|
| Most-referenced | `scip-query hotspots -n 10` |
|
|
405
|
-
| Doc coverage | `scip-query doc-coverage` |
|
|
406
403
|
| Redundant re-exports | `scip-query redundant-reexports` |
|
|
407
404
|
| Similar signatures | `scip-query similar-signatures --min-loc 5` |
|
|
408
405
|
| Read source | `scip-query code <symbol>` |
|
|
@@ -62,6 +62,8 @@ scip-query redundant-reexports
|
|
|
62
62
|
|
|
63
63
|
Why this row is strong: TypeScript has the broadest verified command surface in the current suite, especially for imports, members, call flow, change risk, and DRY analysis.
|
|
64
64
|
|
|
65
|
+
Vue note: `.vue` single-file components are handled by the same path. scip-query extracts the `<script>` (or `<script setup>`) block and parses it as TS/JS, so the TypeScript commands above also work on the Vue file's `<script>` symbols. Identifiers used only in `<template>`/`<style>` are tracked through a separate identifier scan so they don't show up as unused imports.
|
|
66
|
+
|
|
65
67
|
### Python
|
|
66
68
|
|
|
67
69
|
Use first:
|
package/dist/chunk-2MQ5DPY6.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildFileDepGraph
|
|
3
|
-
} from "./chunk-OMCRXXDX.js";
|
|
4
|
-
|
|
5
|
-
// src/queries/cycles.ts
|
|
6
|
-
function cycles(db, opts = {}) {
|
|
7
|
-
const { scope, maxDepth = 10 } = opts;
|
|
8
|
-
const graph = buildFileDepGraph(db, scope);
|
|
9
|
-
const allCycles = [];
|
|
10
|
-
const visited = /* @__PURE__ */ new Set();
|
|
11
|
-
const inStack = /* @__PURE__ */ new Set();
|
|
12
|
-
const stack = [];
|
|
13
|
-
function dfs(node, depth) {
|
|
14
|
-
if (depth > maxDepth) return;
|
|
15
|
-
if (inStack.has(node)) {
|
|
16
|
-
const cycleStart = stack.indexOf(node);
|
|
17
|
-
if (cycleStart !== -1) {
|
|
18
|
-
const cyclePath = stack.slice(cycleStart).concat(node);
|
|
19
|
-
const minIdx = cyclePath.indexOf(
|
|
20
|
-
cyclePath.reduce((a, b) => a < b ? a : b)
|
|
21
|
-
);
|
|
22
|
-
const normalized = [
|
|
23
|
-
...cyclePath.slice(minIdx, -1),
|
|
24
|
-
...cyclePath.slice(0, minIdx),
|
|
25
|
-
cyclePath[minIdx]
|
|
26
|
-
];
|
|
27
|
-
const key = normalized.join(" -> ");
|
|
28
|
-
if (!seenCycles.has(key)) {
|
|
29
|
-
seenCycles.add(key);
|
|
30
|
-
allCycles.push({ path: normalized });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if (visited.has(node)) return;
|
|
36
|
-
visited.add(node);
|
|
37
|
-
inStack.add(node);
|
|
38
|
-
stack.push(node);
|
|
39
|
-
const neighbors = graph.get(node);
|
|
40
|
-
if (neighbors) {
|
|
41
|
-
for (const neighbor of neighbors) {
|
|
42
|
-
dfs(neighbor, depth + 1);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
stack.pop();
|
|
46
|
-
inStack.delete(node);
|
|
47
|
-
}
|
|
48
|
-
const seenCycles = /* @__PURE__ */ new Set();
|
|
49
|
-
for (const node of graph.keys()) {
|
|
50
|
-
if (!visited.has(node)) {
|
|
51
|
-
dfs(node, 0);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
allCycles.sort((a, b) => a.path.length - b.path.length);
|
|
55
|
-
return allCycles;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export {
|
|
59
|
-
cycles
|
|
60
|
-
};
|
|
61
|
-
//# sourceMappingURL=chunk-2MQ5DPY6.js.map
|
package/dist/chunk-2QTYIOJ5.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildFileDepGraph
|
|
3
|
-
} from "./chunk-OMCRXXDX.js";
|
|
4
|
-
|
|
5
|
-
// src/queries/drift.ts
|
|
6
|
-
import path from "path";
|
|
7
|
-
function drift(db, opts) {
|
|
8
|
-
const { scope } = opts ?? {};
|
|
9
|
-
const depGraph = buildFileDepGraph(db, scope);
|
|
10
|
-
const symbolRefs = buildSymbolRefGraph(db, scope);
|
|
11
|
-
const results = [];
|
|
12
|
-
for (const [file, deps] of depGraph) {
|
|
13
|
-
if (shouldSkipDriftFile(file)) continue;
|
|
14
|
-
const referencedFiles = symbolRefs.get(file) ?? /* @__PURE__ */ new Set();
|
|
15
|
-
for (const dep of deps) {
|
|
16
|
-
if (shouldSkipDriftFile(dep)) continue;
|
|
17
|
-
if (!referencedFiles.has(dep)) {
|
|
18
|
-
if (isLikelyTypeOnlyDep(dep)) continue;
|
|
19
|
-
results.push({
|
|
20
|
-
file,
|
|
21
|
-
kind: "unused-import",
|
|
22
|
-
description: `Depends on ${dep} but references none of its symbols`,
|
|
23
|
-
dep
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
const layerRules = inferLayerRules(depGraph);
|
|
29
|
-
for (const [file, deps] of depGraph) {
|
|
30
|
-
if (shouldSkipDriftFile(file)) continue;
|
|
31
|
-
const fileLayer = getArchitecturalLayer(file);
|
|
32
|
-
for (const dep of deps) {
|
|
33
|
-
if (shouldSkipDriftFile(dep)) continue;
|
|
34
|
-
const depLayer = getArchitecturalLayer(dep);
|
|
35
|
-
if (fileLayer === depLayer) continue;
|
|
36
|
-
const violation = layerRules.get(`${fileLayer}->${depLayer}`);
|
|
37
|
-
if (violation === "violation") {
|
|
38
|
-
results.push({
|
|
39
|
-
file,
|
|
40
|
-
kind: "layer-violation",
|
|
41
|
-
description: `Imports from ${depLayer}/ (${dep}) \u2014 may cross architectural boundary`,
|
|
42
|
-
dep,
|
|
43
|
-
detail: `${fileLayer}/ should not depend on ${depLayer}/`
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const dirToFiles = /* @__PURE__ */ new Map();
|
|
49
|
-
for (const file of depGraph.keys()) {
|
|
50
|
-
const dir = path.dirname(file);
|
|
51
|
-
if (!dirToFiles.has(dir)) dirToFiles.set(dir, []);
|
|
52
|
-
dirToFiles.get(dir).push(file);
|
|
53
|
-
}
|
|
54
|
-
for (const [dir, files] of dirToFiles) {
|
|
55
|
-
if (files.length < 3) continue;
|
|
56
|
-
const depFreq = /* @__PURE__ */ new Map();
|
|
57
|
-
for (const file of files) {
|
|
58
|
-
if (shouldSkipDriftFile(file)) continue;
|
|
59
|
-
for (const dep of depGraph.get(file) ?? []) {
|
|
60
|
-
if (shouldSkipDriftFile(dep)) continue;
|
|
61
|
-
depFreq.set(dep, (depFreq.get(dep) ?? 0) + 1);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
for (const file of files) {
|
|
65
|
-
if (shouldSkipDriftFile(file)) continue;
|
|
66
|
-
for (const dep of depGraph.get(file) ?? []) {
|
|
67
|
-
if (shouldSkipDriftFile(dep)) continue;
|
|
68
|
-
if ((depFreq.get(dep) ?? 0) === 1) {
|
|
69
|
-
if (path.dirname(dep) === dir) continue;
|
|
70
|
-
results.push({
|
|
71
|
-
file,
|
|
72
|
-
kind: "pattern-deviation",
|
|
73
|
-
description: `Only file in ${dir}/ that depends on ${dep}`,
|
|
74
|
-
dep
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
results,
|
|
82
|
-
unusedImports: results.filter((r) => r.kind === "unused-import").length,
|
|
83
|
-
layerViolations: results.filter((r) => r.kind === "layer-violation").length,
|
|
84
|
-
patternDeviations: results.filter((r) => r.kind === "pattern-deviation").length
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
function buildSymbolRefGraph(db, scope) {
|
|
88
|
-
const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : "";
|
|
89
|
-
const rows = db.all(
|
|
90
|
-
`SELECT DISTINCT d1.relative_path AS from_file, d2.relative_path AS to_file
|
|
91
|
-
FROM mentions m
|
|
92
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
93
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
94
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
95
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
96
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
97
|
-
WHERE d1.id != d2.id
|
|
98
|
-
AND m.role != 1
|
|
99
|
-
${db.pathExclusionsFor("d1", "d2")}
|
|
100
|
-
${scopeFilter}`
|
|
101
|
-
);
|
|
102
|
-
const graph = /* @__PURE__ */ new Map();
|
|
103
|
-
for (const r of rows) {
|
|
104
|
-
if (db.isIgnored(r.from_file) || db.isIgnored(r.to_file)) continue;
|
|
105
|
-
if (!graph.has(r.from_file)) graph.set(r.from_file, /* @__PURE__ */ new Set());
|
|
106
|
-
graph.get(r.from_file).add(r.to_file);
|
|
107
|
-
}
|
|
108
|
-
return graph;
|
|
109
|
-
}
|
|
110
|
-
function inferLayerRules(depGraph) {
|
|
111
|
-
const layerEdges = /* @__PURE__ */ new Map();
|
|
112
|
-
const layerSet = /* @__PURE__ */ new Set();
|
|
113
|
-
for (const [file, deps] of depGraph) {
|
|
114
|
-
if (shouldSkipDriftFile(file)) continue;
|
|
115
|
-
const fromLayer = getArchitecturalLayer(file);
|
|
116
|
-
layerSet.add(fromLayer);
|
|
117
|
-
for (const dep of deps) {
|
|
118
|
-
if (shouldSkipDriftFile(dep)) continue;
|
|
119
|
-
const toLayer = getArchitecturalLayer(dep);
|
|
120
|
-
if (fromLayer === toLayer) continue;
|
|
121
|
-
layerSet.add(toLayer);
|
|
122
|
-
const key = `${fromLayer}->${toLayer}`;
|
|
123
|
-
layerEdges.set(key, (layerEdges.get(key) ?? 0) + 1);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
const rules = /* @__PURE__ */ new Map();
|
|
127
|
-
for (const [edge, count] of layerEdges) {
|
|
128
|
-
rules.set(edge, count <= 2 ? "violation" : "ok");
|
|
129
|
-
}
|
|
130
|
-
return rules;
|
|
131
|
-
}
|
|
132
|
-
function getArchitecturalLayer(filePath) {
|
|
133
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
134
|
-
const parts = normalized.split("/").filter(Boolean);
|
|
135
|
-
if (parts.length <= 1) {
|
|
136
|
-
return "(root)";
|
|
137
|
-
}
|
|
138
|
-
if (parts.length >= 3 && ["src", "lib", "app", "server", "client"].includes(parts[0])) {
|
|
139
|
-
return `${parts[0]}/${parts[1]}`;
|
|
140
|
-
}
|
|
141
|
-
return parts[0];
|
|
142
|
-
}
|
|
143
|
-
function isLikelyTypeOnlyDep(dep) {
|
|
144
|
-
return dep.includes("types") || dep.endsWith(".d.ts");
|
|
145
|
-
}
|
|
146
|
-
function shouldSkipDriftFile(filePath) {
|
|
147
|
-
return isStructuralRole(path.basename(filePath)) || isTestLikePath(filePath);
|
|
148
|
-
}
|
|
149
|
-
function isStructuralRole(basename) {
|
|
150
|
-
if (basename === "index.ts" || basename === "index.js") return true;
|
|
151
|
-
if (basename === "cli.ts" || basename === "main.ts" || basename === "main.rs") return true;
|
|
152
|
-
if (basename.includes("worker.") || basename.includes("postinstall.")) return true;
|
|
153
|
-
if (basename === "health.ts" || basename === "health.js") return true;
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
function isTestLikePath(filePath) {
|
|
157
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
158
|
-
const basename = path.basename(normalized);
|
|
159
|
-
return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(basename) || /_(test|spec)\.[A-Za-z0-9]+$/.test(basename) || /^test[_-]/.test(basename) || /^test\./.test(basename);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export {
|
|
163
|
-
drift
|
|
164
|
-
};
|
|
165
|
-
//# sourceMappingURL=chunk-2QTYIOJ5.js.map
|
package/dist/chunk-3VI4YXCL.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
findFirstSymbolMatch,
|
|
3
|
-
getSourceImports,
|
|
4
|
-
resolveIndexedFile
|
|
5
|
-
} from "./chunk-OMCRXXDX.js";
|
|
6
|
-
import {
|
|
7
|
-
isModuleLikeSymbol,
|
|
8
|
-
leafName,
|
|
9
|
-
shortenSymbol
|
|
10
|
-
} from "./chunk-YMSJCSRG.js";
|
|
11
|
-
|
|
12
|
-
// src/queries/imports.ts
|
|
13
|
-
function imports(db, filePattern) {
|
|
14
|
-
const importer = resolveIndexedFile(db, filePattern);
|
|
15
|
-
if (!importer) return [];
|
|
16
|
-
const rows = db.all(
|
|
17
|
-
`SELECT DISTINCT gs.symbol, def_d.relative_path AS from_file, imp_d.relative_path AS importer
|
|
18
|
-
FROM mentions m
|
|
19
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
20
|
-
JOIN documents imp_d ON c.document_id = imp_d.id
|
|
21
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
22
|
-
LEFT JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
23
|
-
LEFT JOIN documents def_d ON der.document_id = def_d.id
|
|
24
|
-
WHERE imp_d.relative_path = ?
|
|
25
|
-
AND m.role = 2
|
|
26
|
-
ORDER BY def_d.relative_path, gs.symbol`,
|
|
27
|
-
importer
|
|
28
|
-
);
|
|
29
|
-
const indexedResults = rows.filter((row) => !db.isIgnored(row.importer)).map((r) => ({
|
|
30
|
-
symbol: r.symbol,
|
|
31
|
-
shortName: shortenSymbol(r.symbol),
|
|
32
|
-
fromFile: r.from_file ?? "(external)"
|
|
33
|
-
}));
|
|
34
|
-
if (indexedResults.length > 0) {
|
|
35
|
-
return indexedResults;
|
|
36
|
-
}
|
|
37
|
-
return getSourceImports(db, importer).map((entry) => ({
|
|
38
|
-
symbol: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
39
|
-
shortName: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
40
|
-
fromFile: entry.sourcePath ?? "(external)"
|
|
41
|
-
}));
|
|
42
|
-
}
|
|
43
|
-
function importedBy(db, symbolPattern) {
|
|
44
|
-
const rows = db.all(
|
|
45
|
-
`SELECT DISTINCT gs.symbol, d.relative_path AS importer
|
|
46
|
-
FROM mentions m
|
|
47
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
48
|
-
JOIN documents d ON c.document_id = d.id
|
|
49
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
50
|
-
WHERE gs.symbol LIKE ?
|
|
51
|
-
AND m.role = 2
|
|
52
|
-
ORDER BY d.relative_path`,
|
|
53
|
-
`%${symbolPattern}%`
|
|
54
|
-
);
|
|
55
|
-
const indexedResults = rows.filter((r) => !db.isIgnored(r.importer)).map((r) => ({
|
|
56
|
-
symbol: r.symbol,
|
|
57
|
-
shortName: shortenSymbol(r.symbol),
|
|
58
|
-
fromFile: r.importer
|
|
59
|
-
}));
|
|
60
|
-
if (indexedResults.length > 0) {
|
|
61
|
-
return indexedResults;
|
|
62
|
-
}
|
|
63
|
-
const target = findFirstSymbolMatch(db, symbolPattern);
|
|
64
|
-
const targetFile = target?.relativePath ?? null;
|
|
65
|
-
const targetLeaf = target ? leafName(target.symbol) : symbolPattern.replace(/\(\)$/, "");
|
|
66
|
-
const targetIsModule = target ? isModuleLikeSymbol(target.symbol) : false;
|
|
67
|
-
const files = db.all(
|
|
68
|
-
`SELECT relative_path
|
|
69
|
-
FROM documents
|
|
70
|
-
WHERE 1 = 1
|
|
71
|
-
${db.pathExclusionsFor("documents")}
|
|
72
|
-
ORDER BY relative_path`
|
|
73
|
-
);
|
|
74
|
-
const importers = /* @__PURE__ */ new Set();
|
|
75
|
-
for (const row of files) {
|
|
76
|
-
if (db.isIgnored(row.relative_path)) continue;
|
|
77
|
-
for (const entry of getSourceImports(db, row.relative_path)) {
|
|
78
|
-
if (!entry.sourcePath) continue;
|
|
79
|
-
if (targetFile && normalizePath(entry.sourcePath) !== normalizePath(targetFile)) {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (entry.kind === "side-effect") {
|
|
83
|
-
importers.add(row.relative_path);
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (targetFile && isCLikeImporter(row.relative_path)) {
|
|
87
|
-
importers.add(row.relative_path);
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (targetIsModule) {
|
|
91
|
-
importers.add(row.relative_path);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (entry.kind === "named" && entry.importedName === targetLeaf) {
|
|
95
|
-
importers.add(row.relative_path);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (entry.kind === "namespace" && entry.usedMembers.includes(targetLeaf)) {
|
|
99
|
-
importers.add(row.relative_path);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return [...importers].sort().map((importer) => ({
|
|
104
|
-
symbol: target?.symbol ?? targetLeaf,
|
|
105
|
-
shortName: target ? shortenSymbol(target.symbol) : targetLeaf,
|
|
106
|
-
fromFile: importer
|
|
107
|
-
}));
|
|
108
|
-
}
|
|
109
|
-
function unusedImports(db, filePattern) {
|
|
110
|
-
const importer = resolveIndexedFile(db, filePattern);
|
|
111
|
-
if (!importer) return [];
|
|
112
|
-
const rows = db.all(
|
|
113
|
-
`SELECT gs.symbol, d.relative_path AS imported_in, d.relative_path AS importer
|
|
114
|
-
FROM mentions m
|
|
115
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
116
|
-
JOIN documents d ON c.document_id = d.id
|
|
117
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
118
|
-
WHERE d.relative_path = ?
|
|
119
|
-
AND m.role = 2
|
|
120
|
-
AND NOT EXISTS (
|
|
121
|
-
SELECT 1
|
|
122
|
-
FROM mentions ref_m
|
|
123
|
-
JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
|
|
124
|
-
WHERE ref_m.symbol_id = gs.id
|
|
125
|
-
AND ref_m.role != 1
|
|
126
|
-
AND ref_c.document_id = d.id
|
|
127
|
-
)
|
|
128
|
-
ORDER BY d.relative_path, gs.symbol`,
|
|
129
|
-
importer
|
|
130
|
-
);
|
|
131
|
-
const indexedResults = rows.filter((row) => !db.isIgnored(row.importer)).map((r) => ({
|
|
132
|
-
symbol: r.symbol,
|
|
133
|
-
shortName: shortenSymbol(r.symbol),
|
|
134
|
-
importedIn: r.imported_in
|
|
135
|
-
}));
|
|
136
|
-
if (indexedResults.length > 0) {
|
|
137
|
-
return indexedResults;
|
|
138
|
-
}
|
|
139
|
-
return getSourceImports(db, importer).filter((entry) => entry.kind !== "side-effect" && !entry.used).map((entry) => ({
|
|
140
|
-
symbol: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
141
|
-
shortName: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
142
|
-
importedIn: importer
|
|
143
|
-
}));
|
|
144
|
-
}
|
|
145
|
-
function renderImportSymbol(importedName, localName, kind) {
|
|
146
|
-
if (kind === "namespace" && importedName === "*" && localName) {
|
|
147
|
-
return `* as ${localName}`;
|
|
148
|
-
}
|
|
149
|
-
if (kind === "default" && localName) {
|
|
150
|
-
return `default as ${localName}`;
|
|
151
|
-
}
|
|
152
|
-
if (kind === "side-effect") {
|
|
153
|
-
return "(side effect import)";
|
|
154
|
-
}
|
|
155
|
-
if (localName && localName !== importedName) {
|
|
156
|
-
return `${importedName} as ${localName}`;
|
|
157
|
-
}
|
|
158
|
-
return importedName;
|
|
159
|
-
}
|
|
160
|
-
function normalizePath(path) {
|
|
161
|
-
return path.replace(/\\/g, "/");
|
|
162
|
-
}
|
|
163
|
-
function isCLikeImporter(relativePath) {
|
|
164
|
-
return /\.(?:c|h|cc|cpp|cxx|hpp|hh|hxx)$/i.test(relativePath);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export {
|
|
168
|
-
imports,
|
|
169
|
-
importedBy,
|
|
170
|
-
unusedImports
|
|
171
|
-
};
|
|
172
|
-
//# sourceMappingURL=chunk-3VI4YXCL.js.map
|
package/dist/chunk-3VV2G6U7.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isEntrySurface
|
|
3
|
-
} from "./chunk-UGS7HJI4.js";
|
|
4
|
-
import {
|
|
5
|
-
getAllDefinitions,
|
|
6
|
-
getCalleeRowsForSymbol,
|
|
7
|
-
getCallerRowsForSymbol
|
|
8
|
-
} from "./chunk-OMCRXXDX.js";
|
|
9
|
-
import {
|
|
10
|
-
shortenSymbol
|
|
11
|
-
} from "./chunk-YMSJCSRG.js";
|
|
12
|
-
|
|
13
|
-
// src/queries/isolated.ts
|
|
14
|
-
function isolated(db, opts = {}) {
|
|
15
|
-
const { scope, minLoc = 3 } = opts;
|
|
16
|
-
return getAllDefinitions(db, { scope }).filter((definition) => !db.isIgnored(definition.relativePath)).filter((definition) => !isEntrySurface(db, definition.relativePath)).filter((definition) => definition.isFunctionLike).map((definition) => ({
|
|
17
|
-
definition,
|
|
18
|
-
loc: definition.endLine - definition.startLine + 1
|
|
19
|
-
})).filter((entry) => entry.loc >= minLoc).filter((entry) => getCallerRowsForSymbol(db, entry.definition, { limit: 1 }).length === 0).filter((entry) => getCalleeRowsForSymbol(db, entry.definition, { limit: 1 }).length === 0).sort(
|
|
20
|
-
(left, right) => right.loc - left.loc || left.definition.relativePath.localeCompare(right.definition.relativePath) || left.definition.startLine - right.definition.startLine
|
|
21
|
-
).map((entry) => ({
|
|
22
|
-
symbol: entry.definition.symbol,
|
|
23
|
-
shortName: shortenSymbol(entry.definition.symbol),
|
|
24
|
-
relativePath: entry.definition.relativePath,
|
|
25
|
-
startLine: entry.definition.startLine,
|
|
26
|
-
endLine: entry.definition.endLine,
|
|
27
|
-
loc: entry.loc
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export {
|
|
32
|
-
isolated
|
|
33
|
-
};
|
|
34
|
-
//# sourceMappingURL=chunk-3VV2G6U7.js.map
|
package/dist/chunk-44PFXVXG.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
findFirstSymbolMatch,
|
|
3
|
-
getCalleeRowsForSymbol
|
|
4
|
-
} from "./chunk-OMCRXXDX.js";
|
|
5
|
-
import {
|
|
6
|
-
shortenSymbol
|
|
7
|
-
} from "./chunk-YMSJCSRG.js";
|
|
8
|
-
|
|
9
|
-
// src/queries/convergence.ts
|
|
10
|
-
function convergence(db, symbolPatternA, symbolPatternB) {
|
|
11
|
-
const matchA = findFirstSymbolMatch(db, symbolPatternA);
|
|
12
|
-
const matchB = findFirstSymbolMatch(db, symbolPatternB);
|
|
13
|
-
if (!matchA || !matchB) return null;
|
|
14
|
-
const calleesA = new Set(
|
|
15
|
-
getCalleeRowsForSymbol(db, matchA).map((r) => r.symbol)
|
|
16
|
-
);
|
|
17
|
-
const calleesB = new Set(
|
|
18
|
-
getCalleeRowsForSymbol(db, matchB).map((r) => r.symbol)
|
|
19
|
-
);
|
|
20
|
-
const shared = [];
|
|
21
|
-
for (const c of calleesA) {
|
|
22
|
-
if (calleesB.has(c)) shared.push(c);
|
|
23
|
-
}
|
|
24
|
-
const uniqueA = [];
|
|
25
|
-
for (const c of calleesA) {
|
|
26
|
-
if (!calleesB.has(c)) uniqueA.push(c);
|
|
27
|
-
}
|
|
28
|
-
const uniqueB = [];
|
|
29
|
-
for (const c of calleesB) {
|
|
30
|
-
if (!calleesA.has(c)) uniqueB.push(c);
|
|
31
|
-
}
|
|
32
|
-
const union = /* @__PURE__ */ new Set([...calleesA, ...calleesB]);
|
|
33
|
-
const similarity = union.size > 0 ? shared.length / union.size : 0;
|
|
34
|
-
let strategy;
|
|
35
|
-
if (union.size === 0) {
|
|
36
|
-
strategy = "Neither function calls other tracked symbols. There is no callee-pattern evidence for consolidation; inspect the source bodies directly.";
|
|
37
|
-
} else if (shared.length === 0) {
|
|
38
|
-
strategy = "These functions do not share any callees. They are not a callee-based consolidation candidate.";
|
|
39
|
-
} else if (uniqueA.length === 0 && uniqueB.length === 0) {
|
|
40
|
-
strategy = "These functions have identical tracked callee sets. They are a strong structural match, but identical callees do not prove interchangeable semantics; inspect signatures, control flow, and return values before consolidating.";
|
|
41
|
-
} else if (uniqueA.length === 0) {
|
|
42
|
-
strategy = `A's tracked callees are a subset of B's. B may subsume part of A's structure, but verify signatures, guards, and non-call logic before replacing A with B.`;
|
|
43
|
-
} else if (uniqueB.length === 0) {
|
|
44
|
-
strategy = `B's tracked callees are a subset of A's. A may subsume part of B's structure, but verify signatures, guards, and non-call logic before replacing B with A.`;
|
|
45
|
-
} else if (uniqueA.length <= 2 && uniqueB.length <= 2) {
|
|
46
|
-
strategy = `Create a shared function with the ${shared.length} common callees. Pass the ${uniqueA.length + uniqueB.length} divergent callees as parameters or strategy callbacks.`;
|
|
47
|
-
} else {
|
|
48
|
-
strategy = `Extract the ${shared.length} shared callees into a common helper. Each function calls the helper plus its own unique logic (${uniqueA.length} callees in A, ${uniqueB.length} in B).`;
|
|
49
|
-
}
|
|
50
|
-
const locA = matchA.endLine - matchA.startLine + 1;
|
|
51
|
-
const locB = matchB.endLine - matchB.startLine + 1;
|
|
52
|
-
return {
|
|
53
|
-
symbolA: {
|
|
54
|
-
symbol: matchA.symbol,
|
|
55
|
-
shortName: shortenSymbol(matchA.symbol),
|
|
56
|
-
file: matchA.relativePath,
|
|
57
|
-
loc: locA
|
|
58
|
-
},
|
|
59
|
-
symbolB: {
|
|
60
|
-
symbol: matchB.symbol,
|
|
61
|
-
shortName: shortenSymbol(matchB.symbol),
|
|
62
|
-
file: matchB.relativePath,
|
|
63
|
-
loc: locB
|
|
64
|
-
},
|
|
65
|
-
similarity,
|
|
66
|
-
sharedCallees: shared.map(shortenSymbol),
|
|
67
|
-
uniqueToA: uniqueA.map(shortenSymbol),
|
|
68
|
-
uniqueToB: uniqueB.map(shortenSymbol),
|
|
69
|
-
consolidationStrategy: strategy
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export {
|
|
74
|
-
convergence
|
|
75
|
-
};
|
|
76
|
-
//# sourceMappingURL=chunk-44PFXVXG.js.map
|