@planu/cli 0.48.2 → 0.50.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/dist/config/license-plans.json +2 -1
- package/dist/engine/scan-project/cross-module-analyzer.d.ts +3 -0
- package/dist/engine/scan-project/cross-module-analyzer.d.ts.map +1 -0
- package/dist/engine/scan-project/cross-module-analyzer.js +302 -0
- package/dist/engine/scan-project/cross-module-analyzer.js.map +1 -0
- package/dist/engine/scan-project/health-scorer.d.ts +3 -0
- package/dist/engine/scan-project/health-scorer.d.ts.map +1 -0
- package/dist/engine/scan-project/health-scorer.js +142 -0
- package/dist/engine/scan-project/health-scorer.js.map +1 -0
- package/dist/engine/scan-project/index.d.ts +8 -0
- package/dist/engine/scan-project/index.d.ts.map +1 -0
- package/dist/engine/scan-project/index.js +258 -0
- package/dist/engine/scan-project/index.js.map +1 -0
- package/dist/engine/scan-project/module-discoverer.d.ts +3 -0
- package/dist/engine/scan-project/module-discoverer.d.ts.map +1 -0
- package/dist/engine/scan-project/module-discoverer.js +388 -0
- package/dist/engine/scan-project/module-discoverer.js.map +1 -0
- package/dist/engine/scan-project/overview-generator.d.ts +3 -0
- package/dist/engine/scan-project/overview-generator.d.ts.map +1 -0
- package/dist/engine/scan-project/overview-generator.js +218 -0
- package/dist/engine/scan-project/overview-generator.js.map +1 -0
- package/dist/engine/scan-project/team-planner.d.ts +5 -0
- package/dist/engine/scan-project/team-planner.d.ts.map +1 -0
- package/dist/engine/scan-project/team-planner.js +123 -0
- package/dist/engine/scan-project/team-planner.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/register-scan-tools.d.ts +3 -0
- package/dist/tools/register-scan-tools.d.ts.map +1 -0
- package/dist/tools/register-scan-tools.js +38 -0
- package/dist/tools/register-scan-tools.js.map +1 -0
- package/dist/tools/scan-project.d.ts +3 -0
- package/dist/tools/scan-project.d.ts.map +1 -0
- package/dist/tools/scan-project.js +22 -0
- package/dist/tools/scan-project.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/scan-project.d.ts +97 -0
- package/dist/types/scan-project.d.ts.map +1 -0
- package/dist/types/scan-project.js +3 -0
- package/dist/types/scan-project.js.map +1 -0
- package/package.json +1 -1
- package/src/config/license-plans.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-module-analyzer.d.ts","sourceRoot":"","sources":["../../../src/engine/scan-project/cross-module-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EAGpB,MAAM,sBAAsB,CAAC;AAM9B,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,oBAAoB,EAAE,GAC9B,mBAAmB,CAUrB"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Public API
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
export function analyzeCrossModuleDependencies(results) {
|
|
5
|
+
const successful = results.filter((r) => r.status === 'success');
|
|
6
|
+
const dependencies = buildDependencyGraph(successful);
|
|
7
|
+
const circularDeps = detectCircularDeps(dependencies);
|
|
8
|
+
const antipatterns = detectAntipatterns(successful, dependencies, circularDeps);
|
|
9
|
+
const orphanModules = findOrphanModules(successful, dependencies);
|
|
10
|
+
const duplications = findDuplications(successful);
|
|
11
|
+
return { dependencies, circularDeps, antipatterns, duplications, orphanModules };
|
|
12
|
+
}
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Step 1: Build cross-module dependency graph
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
function buildDependencyGraph(results) {
|
|
17
|
+
const modulePathMap = buildModulePathMap(results);
|
|
18
|
+
const deps = [];
|
|
19
|
+
const depIndex = new Map();
|
|
20
|
+
for (const result of results) {
|
|
21
|
+
const fromModule = result.module.name;
|
|
22
|
+
const nodes = result.analysis.dependencyGraph.nodes;
|
|
23
|
+
for (const node of nodes) {
|
|
24
|
+
for (const importPath of node.imports) {
|
|
25
|
+
const toModule = resolveImportToModule(importPath, modulePathMap, fromModule);
|
|
26
|
+
if (toModule === null) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const key = `${fromModule}→${toModule}`;
|
|
30
|
+
const existing = depIndex.get(key);
|
|
31
|
+
if (existing) {
|
|
32
|
+
if (!existing.files.includes(node.file)) {
|
|
33
|
+
existing.files.push(node.file);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const dep = {
|
|
38
|
+
from: fromModule,
|
|
39
|
+
to: toModule,
|
|
40
|
+
type: 'import',
|
|
41
|
+
files: [node.file],
|
|
42
|
+
};
|
|
43
|
+
depIndex.set(key, dep);
|
|
44
|
+
deps.push(dep);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return deps;
|
|
50
|
+
}
|
|
51
|
+
function buildModulePathMap(results) {
|
|
52
|
+
const map = new Map();
|
|
53
|
+
for (const r of results) {
|
|
54
|
+
map.set(r.module.name, normalizePath(r.module.path));
|
|
55
|
+
}
|
|
56
|
+
return map;
|
|
57
|
+
}
|
|
58
|
+
function normalizePath(p) {
|
|
59
|
+
return p.replace(/\\/g, '/').replace(/\/$/, '');
|
|
60
|
+
}
|
|
61
|
+
function resolveImportToModule(importPath, modulePathMap, fromModule) {
|
|
62
|
+
const normalized = normalizePath(importPath);
|
|
63
|
+
for (const [moduleName, modulePath] of modulePathMap) {
|
|
64
|
+
if (moduleName === fromModule) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Check if the import path contains the module's directory path
|
|
68
|
+
if (normalized.includes(modulePath) || normalized.includes(`/${moduleName}/`)) {
|
|
69
|
+
return moduleName;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Step 2: Detect circular dependencies via DFS
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
function detectCircularDeps(deps) {
|
|
78
|
+
const graph = buildAdjacencyMap(deps);
|
|
79
|
+
const cycles = [];
|
|
80
|
+
const visited = new Set();
|
|
81
|
+
const inStack = new Set();
|
|
82
|
+
for (const node of graph.keys()) {
|
|
83
|
+
if (!visited.has(node)) {
|
|
84
|
+
dfsDetectCycles(node, graph, visited, inStack, [], cycles);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return deduplicateCycles(cycles);
|
|
88
|
+
}
|
|
89
|
+
function buildAdjacencyMap(deps) {
|
|
90
|
+
const graph = new Map();
|
|
91
|
+
for (const dep of deps) {
|
|
92
|
+
if (!graph.has(dep.from)) {
|
|
93
|
+
graph.set(dep.from, []);
|
|
94
|
+
}
|
|
95
|
+
if (!graph.has(dep.to)) {
|
|
96
|
+
graph.set(dep.to, []);
|
|
97
|
+
}
|
|
98
|
+
const fromEdges = graph.get(dep.from);
|
|
99
|
+
if (fromEdges) {
|
|
100
|
+
fromEdges.push(dep.to);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return graph;
|
|
104
|
+
}
|
|
105
|
+
function dfsDetectCycles(node, graph, visited, inStack, path, cycles) {
|
|
106
|
+
visited.add(node);
|
|
107
|
+
inStack.add(node);
|
|
108
|
+
path.push(node);
|
|
109
|
+
for (const neighbor of graph.get(node) ?? []) {
|
|
110
|
+
if (!visited.has(neighbor)) {
|
|
111
|
+
dfsDetectCycles(neighbor, graph, visited, inStack, path, cycles);
|
|
112
|
+
}
|
|
113
|
+
else if (inStack.has(neighbor)) {
|
|
114
|
+
const cycleStart = path.indexOf(neighbor);
|
|
115
|
+
cycles.push(path.slice(cycleStart));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
path.pop();
|
|
119
|
+
inStack.delete(node);
|
|
120
|
+
}
|
|
121
|
+
function deduplicateCycles(cycles) {
|
|
122
|
+
const seen = new Set();
|
|
123
|
+
const result = [];
|
|
124
|
+
for (const cycle of cycles) {
|
|
125
|
+
const sorted = [...cycle].sort().join('|');
|
|
126
|
+
if (!seen.has(sorted)) {
|
|
127
|
+
seen.add(sorted);
|
|
128
|
+
result.push(cycle);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Step 3: Detect antipatterns
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
function detectAntipatterns(results, deps, circularDeps) {
|
|
137
|
+
return [
|
|
138
|
+
...detectCircularAntipatterns(circularDeps),
|
|
139
|
+
...detectGodModule(results, deps),
|
|
140
|
+
...detectOrphanAntipatterns(results, deps),
|
|
141
|
+
...detectInconsistentTestPatterns(results),
|
|
142
|
+
...detectDuplicatedLogicAntipatterns(results),
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
function detectCircularAntipatterns(circularDeps) {
|
|
146
|
+
return circularDeps.map((cycle) => ({
|
|
147
|
+
description: `Circular dependency detected: ${cycle.join(' → ')}`,
|
|
148
|
+
severity: 'high',
|
|
149
|
+
modules: cycle,
|
|
150
|
+
suggestion: 'Extract shared interfaces or abstractions into a dedicated module to break the cycle.',
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
function detectGodModule(results, deps) {
|
|
154
|
+
if (results.length === 0 || deps.length === 0) {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
const incomingCount = countIncomingDeps(results, deps);
|
|
158
|
+
const threshold = results.length * 0.5;
|
|
159
|
+
const antipatterns = [];
|
|
160
|
+
for (const [moduleName, count] of incomingCount) {
|
|
161
|
+
if (count > threshold) {
|
|
162
|
+
antipatterns.push({
|
|
163
|
+
description: `God module: "${moduleName}" receives ${count} of ${deps.length} cross-module dependencies (${Math.round((count / results.length) * 100)}% of modules depend on it).`,
|
|
164
|
+
severity: 'medium',
|
|
165
|
+
modules: [moduleName],
|
|
166
|
+
suggestion: 'Split this module into smaller, focused modules with well-defined responsibilities.',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return antipatterns;
|
|
171
|
+
}
|
|
172
|
+
function countIncomingDeps(results, deps) {
|
|
173
|
+
const counts = new Map();
|
|
174
|
+
for (const r of results) {
|
|
175
|
+
counts.set(r.module.name, 0);
|
|
176
|
+
}
|
|
177
|
+
for (const dep of deps) {
|
|
178
|
+
counts.set(dep.to, (counts.get(dep.to) ?? 0) + 1);
|
|
179
|
+
}
|
|
180
|
+
return counts;
|
|
181
|
+
}
|
|
182
|
+
function detectOrphanAntipatterns(results, deps) {
|
|
183
|
+
const connectedModules = new Set();
|
|
184
|
+
for (const dep of deps) {
|
|
185
|
+
connectedModules.add(dep.from);
|
|
186
|
+
connectedModules.add(dep.to);
|
|
187
|
+
}
|
|
188
|
+
const orphans = results.map((r) => r.module.name).filter((name) => !connectedModules.has(name));
|
|
189
|
+
return orphans.map((name) => ({
|
|
190
|
+
description: `Orphan module: "${name}" has no cross-module dependencies (neither imports nor is imported).`,
|
|
191
|
+
severity: 'low',
|
|
192
|
+
modules: [name],
|
|
193
|
+
suggestion: 'Verify this module is intentionally standalone or consider integrating it with the project.',
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
function detectInconsistentTestPatterns(results) {
|
|
197
|
+
const withTests = results.filter((r) => r.analysis.testCoverage.testedModules.length > 0);
|
|
198
|
+
const withoutTests = results.filter((r) => r.analysis.testCoverage.testedModules.length === 0);
|
|
199
|
+
if (withTests.length === 0 || withoutTests.length === 0) {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
return [
|
|
203
|
+
{
|
|
204
|
+
description: `Inconsistent test coverage: ${withTests.length} modules have tests but ${withoutTests.length} module(s) have none.`,
|
|
205
|
+
severity: 'medium',
|
|
206
|
+
modules: withoutTests.map((r) => r.module.name),
|
|
207
|
+
suggestion: 'Establish a uniform testing policy and add test coverage to untested modules.',
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
function detectDuplicatedLogicAntipatterns(results) {
|
|
212
|
+
const duplications = findDuplications(results);
|
|
213
|
+
return duplications.map((dup) => ({
|
|
214
|
+
description: `Duplicated logic detected: function signature "${dup.signature}" appears in ${dup.modules.length} modules.`,
|
|
215
|
+
severity: 'low',
|
|
216
|
+
modules: dup.modules,
|
|
217
|
+
suggestion: 'Extract duplicated logic into a shared utility module to avoid divergence over time.',
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Step 4: Find orphan modules (no cross-module connections)
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
function findOrphanModules(results, deps) {
|
|
224
|
+
const connected = new Set();
|
|
225
|
+
for (const dep of deps) {
|
|
226
|
+
connected.add(dep.from);
|
|
227
|
+
connected.add(dep.to);
|
|
228
|
+
}
|
|
229
|
+
return results.map((r) => r.module.name).filter((name) => !connected.has(name));
|
|
230
|
+
}
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Step 5: Find duplications via function name similarity
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
function findDuplications(results) {
|
|
235
|
+
const functionsByModule = extractFunctionNames(results);
|
|
236
|
+
const allNames = Array.from(new Set(functionsByModule.flatMap(({ names }) => names)));
|
|
237
|
+
const duplications = [];
|
|
238
|
+
const reported = new Set();
|
|
239
|
+
for (let i = 0; i < allNames.length; i++) {
|
|
240
|
+
const nameA = allNames[i] ?? '';
|
|
241
|
+
if (nameA === '' || reported.has(nameA)) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const cluster = [nameA];
|
|
245
|
+
for (let j = i + 1; j < allNames.length; j++) {
|
|
246
|
+
const nameB = allNames[j] ?? '';
|
|
247
|
+
if (!reported.has(nameB) && stringSimilarity(nameA, nameB) >= 0.8) {
|
|
248
|
+
cluster.push(nameB);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (cluster.length < 2) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const modulesWithCluster = functionsByModule
|
|
255
|
+
.filter(({ names }) => cluster.some((c) => names.includes(c)))
|
|
256
|
+
.map(({ module }) => module);
|
|
257
|
+
if (modulesWithCluster.length >= 2) {
|
|
258
|
+
cluster.forEach((n) => reported.add(n));
|
|
259
|
+
duplications.push({ signature: nameA, modules: modulesWithCluster });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return duplications;
|
|
263
|
+
}
|
|
264
|
+
function extractFunctionNames(results) {
|
|
265
|
+
return results.map((r) => ({
|
|
266
|
+
module: r.module.name,
|
|
267
|
+
names: r.analysis.complexityMetrics.flatMap((m) => m.hotspots.map((h) => h.name)),
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Character overlap ratio similarity (0..1).
|
|
272
|
+
* Counts shared characters relative to the longer string.
|
|
273
|
+
*/
|
|
274
|
+
function stringSimilarity(a, b) {
|
|
275
|
+
if (a === b) {
|
|
276
|
+
return 1;
|
|
277
|
+
}
|
|
278
|
+
const longer = a.length >= b.length ? a : b;
|
|
279
|
+
const shorter = a.length < b.length ? a : b;
|
|
280
|
+
if (longer.length === 0) {
|
|
281
|
+
return 1;
|
|
282
|
+
}
|
|
283
|
+
let matches = 0;
|
|
284
|
+
const usedIndices = new Set();
|
|
285
|
+
for (const char of shorter) {
|
|
286
|
+
const idx = findUnusedIndex(longer, char, usedIndices);
|
|
287
|
+
if (idx !== -1) {
|
|
288
|
+
matches++;
|
|
289
|
+
usedIndices.add(idx);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return matches / longer.length;
|
|
293
|
+
}
|
|
294
|
+
function findUnusedIndex(str, char, usedIndices) {
|
|
295
|
+
for (let i = 0; i < str.length; i++) {
|
|
296
|
+
if (str.charAt(i) === char && !usedIndices.has(i)) {
|
|
297
|
+
return i;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return -1;
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=cross-module-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-module-analyzer.js","sourceRoot":"","sources":["../../../src/engine/scan-project/cross-module-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ModuleAnalysisResult, CrossModuleAnalysis, ScanHealthScore } from '../../types/scan-project.js';
|
|
2
|
+
export declare function calculateProjectHealth(moduleResults: ModuleAnalysisResult[], crossModuleAnalysis: CrossModuleAnalysis): ScanHealthScore;
|
|
3
|
+
//# sourceMappingURL=health-scorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-scorer.d.ts","sourceRoot":"","sources":["../../../src/engine/scan-project/health-scorer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EAGhB,MAAM,6BAA6B,CAAC;AA0JrC,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,oBAAoB,EAAE,EACrC,mBAAmB,EAAE,mBAAmB,GACvC,eAAe,CAoBjB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Helpers — one per dimension (keeps cyclomatic complexity ≤ 15)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
function clamp(value) {
|
|
5
|
+
return Math.max(0, Math.min(100, value));
|
|
6
|
+
}
|
|
7
|
+
function calcTestCoverageScore(modules) {
|
|
8
|
+
const successful = modules.filter((m) => m.status === 'success');
|
|
9
|
+
if (successful.length === 0) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
const totalRatio = successful.reduce((sum, m) => sum + m.analysis.testCoverage.testToSourceRatio, 0);
|
|
13
|
+
const avgRatio = totalRatio / successful.length;
|
|
14
|
+
return clamp(avgRatio * 100);
|
|
15
|
+
}
|
|
16
|
+
function calcTechDebtScore(modules) {
|
|
17
|
+
const successful = modules.filter((m) => m.status === 'success');
|
|
18
|
+
if (successful.length === 0) {
|
|
19
|
+
return 100;
|
|
20
|
+
}
|
|
21
|
+
let totalHotspots = 0;
|
|
22
|
+
let totalFiles = 0;
|
|
23
|
+
for (const m of successful) {
|
|
24
|
+
for (const metric of m.analysis.complexityMetrics) {
|
|
25
|
+
totalFiles++;
|
|
26
|
+
if (metric.avgComplexity > 10) {
|
|
27
|
+
totalHotspots++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (totalFiles === 0) {
|
|
32
|
+
return 100;
|
|
33
|
+
}
|
|
34
|
+
const density = totalHotspots / totalFiles;
|
|
35
|
+
return clamp(100 - density * 500);
|
|
36
|
+
}
|
|
37
|
+
function calcArchConsistencyScore(modules, cross) {
|
|
38
|
+
const successful = modules.filter((m) => m.status === 'success');
|
|
39
|
+
let score = 100;
|
|
40
|
+
if (successful.length > 1) {
|
|
41
|
+
// Inconsistency 1: test presence varies across modules
|
|
42
|
+
const withTests = successful.filter((m) => m.analysis.testCoverage.testedModules.length > 0);
|
|
43
|
+
const allHaveTests = withTests.length === successful.length;
|
|
44
|
+
const noneHaveTests = withTests.length === 0;
|
|
45
|
+
if (!allHaveTests && !noneHaveTests) {
|
|
46
|
+
score -= 15;
|
|
47
|
+
}
|
|
48
|
+
// Inconsistency 2: config presence varies across modules
|
|
49
|
+
const withConfig = successful.filter((m) => m.analysis.configAnalysis.configFiles.length > 0);
|
|
50
|
+
const allHaveConfig = withConfig.length === successful.length;
|
|
51
|
+
const noneHaveConfig = withConfig.length === 0;
|
|
52
|
+
if (!allHaveConfig && !noneHaveConfig) {
|
|
53
|
+
score -= 15;
|
|
54
|
+
}
|
|
55
|
+
// Inconsistency 3: high std deviation in avg complexity across modules
|
|
56
|
+
const complexities = successful.map((m) => {
|
|
57
|
+
const metrics = m.analysis.complexityMetrics;
|
|
58
|
+
if (metrics.length === 0) {
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
return metrics.reduce((s, c) => s + c.avgComplexity, 0) / metrics.length;
|
|
62
|
+
});
|
|
63
|
+
const mean = complexities.reduce((s, v) => s + v, 0) / complexities.length;
|
|
64
|
+
const variance = complexities.reduce((s, v) => s + (v - mean) ** 2, 0) / complexities.length;
|
|
65
|
+
const stdDev = Math.sqrt(variance);
|
|
66
|
+
if (stdDev > 5) {
|
|
67
|
+
score -= 15;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Penalise antipatterns from cross-module analysis
|
|
71
|
+
for (const ap of cross.antipatterns) {
|
|
72
|
+
if (ap.severity === 'medium') {
|
|
73
|
+
score -= 10;
|
|
74
|
+
}
|
|
75
|
+
else if (ap.severity === 'high' || ap.severity === 'critical') {
|
|
76
|
+
score -= 20;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return clamp(score);
|
|
80
|
+
}
|
|
81
|
+
function calcDepHealthScore(modules, cross) {
|
|
82
|
+
let score = 100;
|
|
83
|
+
// Circular dependency cycles
|
|
84
|
+
score -= cross.circularDeps.length * 25;
|
|
85
|
+
// Orphan modules
|
|
86
|
+
score -= cross.orphanModules.length * 5;
|
|
87
|
+
// God module detection: any module where >50% of other modules depend on it
|
|
88
|
+
const totalModules = modules.length;
|
|
89
|
+
if (totalModules > 1) {
|
|
90
|
+
const incomingCount = new Map();
|
|
91
|
+
for (const dep of cross.dependencies) {
|
|
92
|
+
incomingCount.set(dep.to, (incomingCount.get(dep.to) ?? 0) + 1);
|
|
93
|
+
}
|
|
94
|
+
for (const count of incomingCount.values()) {
|
|
95
|
+
if (count / totalModules > 0.5) {
|
|
96
|
+
score -= 10;
|
|
97
|
+
break; // penalise once even if multiple god modules exist
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return clamp(score);
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Grade mapping
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
function deriveGrade(score) {
|
|
107
|
+
if (score >= 80) {
|
|
108
|
+
return 'A';
|
|
109
|
+
}
|
|
110
|
+
if (score >= 60) {
|
|
111
|
+
return 'B';
|
|
112
|
+
}
|
|
113
|
+
if (score >= 40) {
|
|
114
|
+
return 'C';
|
|
115
|
+
}
|
|
116
|
+
if (score >= 20) {
|
|
117
|
+
return 'D';
|
|
118
|
+
}
|
|
119
|
+
return 'F';
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Public API
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
export function calculateProjectHealth(moduleResults, crossModuleAnalysis) {
|
|
125
|
+
const testCoverage = calcTestCoverageScore(moduleResults);
|
|
126
|
+
const techDebt = calcTechDebtScore(moduleResults);
|
|
127
|
+
const archConsistency = calcArchConsistencyScore(moduleResults, crossModuleAnalysis);
|
|
128
|
+
const depHealth = calcDepHealthScore(moduleResults, crossModuleAnalysis);
|
|
129
|
+
const breakdown = {
|
|
130
|
+
testCoverage,
|
|
131
|
+
techDebt,
|
|
132
|
+
archConsistency,
|
|
133
|
+
depHealth,
|
|
134
|
+
};
|
|
135
|
+
const score = Math.round((testCoverage + techDebt + archConsistency + depHealth) / 4);
|
|
136
|
+
return {
|
|
137
|
+
score,
|
|
138
|
+
breakdown,
|
|
139
|
+
grade: deriveGrade(score),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=health-scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-scorer.js","sourceRoot":"","sources":["../../../src/engine/scan-project/health-scorer.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,iEAAiE;AACjE,8EAA8E;AAE9E,SAAS,KAAK,CAAC,KAAa;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,qBAAqB,CAAC,OAA+B;IAC5D,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IACjE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAClC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,iBAAiB,EAC3D,CAAC,CACF,CAAC;IACF,MAAM,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;IAEhD,OAAO,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,iBAAiB,CAAC,OAA+B;IACxD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IACjE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,iBAAiB,EAAE,CAAC;YAClD,UAAU,EAAE,CAAC;YACb,IAAI,MAAM,CAAC,aAAa,GAAG,EAAE,EAAE,CAAC;gBAC9B,aAAa,EAAE,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,OAAO,GAAG,aAAa,GAAG,UAAU,CAAC;IAE3C,OAAO,KAAK,CAAC,GAAG,GAAG,OAAO,GAAG,GAAG,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,wBAAwB,CAC/B,OAA+B,EAC/B,KAA0B;IAE1B,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IACjE,IAAI,KAAK,GAAG,GAAG,CAAC;IAEhB,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,uDAAuD;QACvD,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC7F,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,CAAC;QAC5D,MAAM,aAAa,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,IAAI,CAAC,aAAa,EAAE,CAAC;YACpC,KAAK,IAAI,EAAE,CAAC;QACd,CAAC;QAED,yDAAyD;QACzD,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9F,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,CAAC;QAC9D,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,aAAa,IAAI,CAAC,cAAc,EAAE,CAAC;YACtC,KAAK,IAAI,EAAE,CAAC;QACd,CAAC;QAED,uEAAuE;QACvE,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACxC,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;QAC3E,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC;QAC3E,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC;QAC7F,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,KAAK,IAAI,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACpC,IAAI,EAAE,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC7B,KAAK,IAAI,EAAE,CAAC;QACd,CAAC;aAAM,IAAI,EAAE,CAAC,QAAQ,KAAK,MAAM,IAAI,EAAE,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;YAChE,KAAK,IAAI,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,kBAAkB,CAAC,OAA+B,EAAE,KAA0B;IACrF,IAAI,KAAK,GAAG,GAAG,CAAC;IAEhB,6BAA6B;IAC7B,KAAK,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,EAAE,CAAC;IAExC,iBAAiB;IACjB,KAAK,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IAExC,4EAA4E;IAC5E,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IACpC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;QAChD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YACrC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,IAAI,KAAK,GAAG,YAAY,GAAG,GAAG,EAAE,CAAC;gBAC/B,KAAK,IAAI,EAAE,CAAC;gBACZ,MAAM,CAAC,mDAAmD;YAC5D,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;AACtB,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,UAAU,sBAAsB,CACpC,aAAqC,EACrC,mBAAwC;IAExC,MAAM,YAAY,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAC;IAClD,MAAM,eAAe,GAAG,wBAAwB,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;IACrF,MAAM,SAAS,GAAG,kBAAkB,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;IAEzE,MAAM,SAAS,GAAoB;QACjC,YAAY;QACZ,QAAQ;QACR,eAAe;QACf,SAAS;KACV,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,QAAQ,GAAG,eAAe,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAEtF,OAAO;QACL,KAAK;QACL,SAAS;QACT,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;KAC1B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ScanProjectInput, ScanProjectOutput } from '../../types/index.js';
|
|
2
|
+
export { discoverModules } from './module-discoverer.js';
|
|
3
|
+
export { analyzeCrossModuleDependencies } from './cross-module-analyzer.js';
|
|
4
|
+
export { calculateProjectHealth } from './health-scorer.js';
|
|
5
|
+
export { generateProjectOverview } from './overview-generator.js';
|
|
6
|
+
export { generateTeamPlan } from './team-planner.js';
|
|
7
|
+
export declare function scanProject(input: ScanProjectInput): Promise<ScanProjectOutput>;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/scan-project/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAGV,gBAAgB,EAChB,iBAAiB,EAKlB,MAAM,sBAAsB,CAAC;AAW9B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,8BAA8B,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAuPrD,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+CrF"}
|