@aiready/context-analyzer 0.21.5 → 0.21.6
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/.aiready/aiready-report-20260314-222254.json +39216 -0
- package/.aiready/aiready-report-20260314-223947.json +3413 -0
- package/.aiready/aiready-report-20260314-224112.json +3413 -0
- package/.aiready/aiready-report-20260314-224302.json +2973 -0
- package/.aiready/aiready-report-20260314-224939.json +3092 -0
- package/.aiready/aiready-report-20260314-225154.json +3092 -0
- package/.turbo/turbo-build.log +26 -24
- package/.turbo/turbo-test.log +41 -119
- package/dist/__tests__/analyzer.test.js +55 -14
- package/dist/__tests__/analyzer.test.js.map +1 -1
- package/dist/__tests__/cluster-detector.test.d.ts +2 -0
- package/dist/__tests__/cluster-detector.test.d.ts.map +1 -0
- package/dist/__tests__/cluster-detector.test.js +121 -0
- package/dist/__tests__/cluster-detector.test.js.map +1 -0
- package/dist/__tests__/contract.test.d.ts +2 -0
- package/dist/__tests__/contract.test.d.ts.map +1 -0
- package/dist/__tests__/contract.test.js +59 -0
- package/dist/__tests__/contract.test.js.map +1 -0
- package/dist/__tests__/enhanced-cohesion.test.js +12 -2
- package/dist/__tests__/enhanced-cohesion.test.js.map +1 -1
- package/dist/__tests__/file-classification.test.d.ts +2 -0
- package/dist/__tests__/file-classification.test.d.ts.map +1 -0
- package/dist/__tests__/file-classification.test.js +749 -0
- package/dist/__tests__/file-classification.test.js.map +1 -0
- package/dist/__tests__/fragmentation-advanced.test.js +2 -8
- package/dist/__tests__/fragmentation-advanced.test.js.map +1 -1
- package/dist/__tests__/fragmentation-coupling.test.js +2 -2
- package/dist/__tests__/fragmentation-coupling.test.js.map +1 -1
- package/dist/__tests__/fragmentation-log.test.js +3 -7
- package/dist/__tests__/fragmentation-log.test.js.map +1 -1
- package/dist/__tests__/provider.test.d.ts +2 -0
- package/dist/__tests__/provider.test.d.ts.map +1 -0
- package/dist/__tests__/provider.test.js +72 -0
- package/dist/__tests__/provider.test.js.map +1 -0
- package/dist/__tests__/remediation.test.d.ts +2 -0
- package/dist/__tests__/remediation.test.d.ts.map +1 -0
- package/dist/__tests__/remediation.test.js +61 -0
- package/dist/__tests__/remediation.test.js.map +1 -0
- package/dist/__tests__/scoring.test.js +196 -16
- package/dist/__tests__/scoring.test.js.map +1 -1
- package/dist/__tests__/structural-cohesion.test.js +8 -2
- package/dist/__tests__/structural-cohesion.test.js.map +1 -1
- package/dist/analyzer.d.ts +31 -94
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +260 -678
- package/dist/analyzer.js.map +1 -1
- package/dist/analyzers/python-context.d.ts.map +1 -1
- package/dist/analyzers/python-context.js +10 -8
- package/dist/analyzers/python-context.js.map +1 -1
- package/dist/ast-utils.d.ts +16 -0
- package/dist/ast-utils.d.ts.map +1 -0
- package/dist/ast-utils.js +81 -0
- package/dist/ast-utils.js.map +1 -0
- package/dist/chunk-64U3PNO3.mjs +94 -0
- package/dist/chunk-CDIVYADN.mjs +2110 -0
- package/dist/chunk-D3SIHB2V.mjs +2118 -0
- package/dist/chunk-FNPSK3CG.mjs +1760 -0
- package/dist/chunk-GXTGOLZT.mjs +92 -0
- package/dist/chunk-LERPI33Y.mjs +2060 -0
- package/dist/chunk-MZP3G7TF.mjs +2118 -0
- package/dist/chunk-NOHK5DLU.mjs +2173 -0
- package/dist/chunk-ORLC5Y4J.mjs +1787 -0
- package/dist/chunk-OTCQL7DY.mjs +2045 -0
- package/dist/chunk-SFK6XTJE.mjs +2110 -0
- package/dist/chunk-U5R2FTCR.mjs +1803 -0
- package/dist/chunk-UU4HZ7ZT.mjs +1849 -0
- package/dist/chunk-WKOZOHOU.mjs +2060 -0
- package/dist/chunk-XIXAWCMS.mjs +1760 -0
- package/dist/classifier.d.ts +114 -0
- package/dist/classifier.d.ts.map +1 -0
- package/dist/classifier.js +439 -0
- package/dist/classifier.js.map +1 -0
- package/dist/cli.js +590 -1071
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +63 -533
- package/dist/cluster-detector.d.ts +8 -0
- package/dist/cluster-detector.d.ts.map +1 -0
- package/dist/cluster-detector.js +70 -0
- package/dist/cluster-detector.js.map +1 -0
- package/dist/defaults.d.ts +7 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +54 -0
- package/dist/defaults.js.map +1 -0
- package/dist/graph-builder.d.ts +33 -0
- package/dist/graph-builder.d.ts.map +1 -0
- package/dist/graph-builder.js +225 -0
- package/dist/graph-builder.js.map +1 -0
- package/dist/index.d.mts +24 -31
- package/dist/index.d.ts +24 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +788 -569
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +265 -8
- package/dist/metrics.d.ts +34 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +170 -0
- package/dist/metrics.js.map +1 -0
- package/dist/provider.d.ts +6 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +48 -0
- package/dist/provider.js.map +1 -0
- package/dist/python-context-3GZKN3LR.mjs +162 -0
- package/dist/python-context-O2EN3M6Z.mjs +162 -0
- package/dist/remediation.d.ts +25 -0
- package/dist/remediation.d.ts.map +1 -0
- package/dist/remediation.js +98 -0
- package/dist/remediation.js.map +1 -0
- package/dist/scoring.d.ts +3 -7
- package/dist/scoring.d.ts.map +1 -1
- package/dist/scoring.js +57 -48
- package/dist/scoring.js.map +1 -1
- package/dist/semantic-analysis.d.ts +12 -23
- package/dist/semantic-analysis.d.ts.map +1 -1
- package/dist/semantic-analysis.js +172 -110
- package/dist/semantic-analysis.js.map +1 -1
- package/dist/summary.d.ts +6 -0
- package/dist/summary.d.ts.map +1 -0
- package/dist/summary.js +92 -0
- package/dist/summary.js.map +1 -0
- package/dist/types.d.ts +9 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/output-formatter.d.ts +14 -0
- package/dist/utils/output-formatter.d.ts.map +1 -0
- package/dist/utils/output-formatter.js +338 -0
- package/dist/utils/output-formatter.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +1 -1
- package/src/__tests__/auto-detection.test.ts +1 -1
- package/src/__tests__/contract.test.ts +1 -1
- package/src/__tests__/enhanced-cohesion.test.ts +1 -1
- package/src/__tests__/file-classification.test.ts +1 -1
- package/src/__tests__/fragmentation-advanced.test.ts +1 -1
- package/src/__tests__/fragmentation-coupling.test.ts +1 -1
- package/src/__tests__/fragmentation-log.test.ts +1 -1
- package/src/__tests__/provider.test.ts +1 -1
- package/src/__tests__/structural-cohesion.test.ts +1 -1
- package/src/analyzer.ts +96 -309
- package/src/analyzers/python-context.ts +7 -76
- package/src/cli-action.ts +103 -0
- package/src/cli.ts +12 -693
- package/src/cluster-detector.ts +1 -1
- package/src/graph-builder.ts +9 -85
- package/src/index.ts +6 -0
- package/src/issue-analyzer.ts +143 -0
- package/src/semantic-analysis.ts +1 -14
- package/src/summary.ts +62 -106
- package/src/utils/dependency-graph-utils.ts +126 -0
- package/src/utils/output-formatter.ts +411 -0
- package/src/utils/string-utils.ts +17 -0
package/dist/index.js
CHANGED
|
@@ -30,6 +30,99 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
+
// src/utils/dependency-graph-utils.ts
|
|
34
|
+
function calculateImportDepthFromEdges(file, edges, visited = /* @__PURE__ */ new Set(), depth = 0) {
|
|
35
|
+
if (visited.has(file)) return depth;
|
|
36
|
+
const dependencies = edges.get(file);
|
|
37
|
+
if (!dependencies || dependencies.size === 0) return depth;
|
|
38
|
+
const nextVisited = new Set(visited);
|
|
39
|
+
nextVisited.add(file);
|
|
40
|
+
let maxDepth = depth;
|
|
41
|
+
for (const dep of dependencies) {
|
|
42
|
+
maxDepth = Math.max(
|
|
43
|
+
maxDepth,
|
|
44
|
+
calculateImportDepthFromEdges(dep, edges, nextVisited, depth + 1)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return maxDepth;
|
|
48
|
+
}
|
|
49
|
+
function getTransitiveDependenciesFromEdges(file, edges, visited = /* @__PURE__ */ new Set()) {
|
|
50
|
+
if (visited.has(file)) return [];
|
|
51
|
+
const nextVisited = new Set(visited);
|
|
52
|
+
nextVisited.add(file);
|
|
53
|
+
const dependencies = edges.get(file);
|
|
54
|
+
if (!dependencies || dependencies.size === 0) return [];
|
|
55
|
+
const allDeps = [];
|
|
56
|
+
for (const dep of dependencies) {
|
|
57
|
+
allDeps.push(dep);
|
|
58
|
+
allDeps.push(
|
|
59
|
+
...getTransitiveDependenciesFromEdges(dep, edges, nextVisited)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return [...new Set(allDeps)];
|
|
63
|
+
}
|
|
64
|
+
function detectGraphCycles(edges) {
|
|
65
|
+
const cycles = [];
|
|
66
|
+
const visited = /* @__PURE__ */ new Set();
|
|
67
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
68
|
+
function dfs(file, path) {
|
|
69
|
+
if (recursionStack.has(file)) {
|
|
70
|
+
const cycleStart = path.indexOf(file);
|
|
71
|
+
if (cycleStart !== -1) {
|
|
72
|
+
cycles.push([...path.slice(cycleStart), file]);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (visited.has(file)) return;
|
|
77
|
+
visited.add(file);
|
|
78
|
+
recursionStack.add(file);
|
|
79
|
+
const dependencies = edges.get(file);
|
|
80
|
+
if (dependencies) {
|
|
81
|
+
for (const dep of dependencies) {
|
|
82
|
+
dfs(dep, [...path, file]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
recursionStack.delete(file);
|
|
86
|
+
}
|
|
87
|
+
for (const file of edges.keys()) {
|
|
88
|
+
if (!visited.has(file)) {
|
|
89
|
+
dfs(file, []);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return cycles;
|
|
93
|
+
}
|
|
94
|
+
function detectGraphCyclesFromFile(file, edges) {
|
|
95
|
+
const cycles = [];
|
|
96
|
+
const visited = /* @__PURE__ */ new Set();
|
|
97
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
98
|
+
function dfs(current, path) {
|
|
99
|
+
if (recursionStack.has(current)) {
|
|
100
|
+
const cycleStart = path.indexOf(current);
|
|
101
|
+
if (cycleStart !== -1) {
|
|
102
|
+
cycles.push([...path.slice(cycleStart), current]);
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (visited.has(current)) return;
|
|
107
|
+
visited.add(current);
|
|
108
|
+
recursionStack.add(current);
|
|
109
|
+
const dependencies = edges.get(current);
|
|
110
|
+
if (dependencies) {
|
|
111
|
+
for (const dep of dependencies) {
|
|
112
|
+
dfs(dep, [...path, current]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
recursionStack.delete(current);
|
|
116
|
+
}
|
|
117
|
+
dfs(file, []);
|
|
118
|
+
return cycles;
|
|
119
|
+
}
|
|
120
|
+
var init_dependency_graph_utils = __esm({
|
|
121
|
+
"src/utils/dependency-graph-utils.ts"() {
|
|
122
|
+
"use strict";
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
33
126
|
// src/analyzers/python-context.ts
|
|
34
127
|
var python_context_exports = {};
|
|
35
128
|
__export(python_context_exports, {
|
|
@@ -64,7 +157,7 @@ async function analyzePythonContext(files, rootDir) {
|
|
|
64
157
|
type: exp.type
|
|
65
158
|
}));
|
|
66
159
|
const linesOfCode = code.split("\n").length;
|
|
67
|
-
const importDepth =
|
|
160
|
+
const importDepth = calculateImportDepthFromEdges(
|
|
68
161
|
file,
|
|
69
162
|
dependencyGraph,
|
|
70
163
|
/* @__PURE__ */ new Set()
|
|
@@ -75,10 +168,10 @@ async function analyzePythonContext(files, rootDir) {
|
|
|
75
168
|
dependencyGraph
|
|
76
169
|
);
|
|
77
170
|
const cohesion = calculatePythonCohesion(exports2, imports);
|
|
78
|
-
const circularDependencies =
|
|
171
|
+
const circularDependencies = detectGraphCyclesFromFile(
|
|
79
172
|
file,
|
|
80
173
|
dependencyGraph
|
|
81
|
-
);
|
|
174
|
+
).map((cycle) => cycle.join(" -> "));
|
|
82
175
|
results.push({
|
|
83
176
|
file,
|
|
84
177
|
importDepth,
|
|
@@ -158,27 +251,6 @@ function resolvePythonImport(fromFile, importPath, rootDir) {
|
|
|
158
251
|
}
|
|
159
252
|
return void 0;
|
|
160
253
|
}
|
|
161
|
-
async function calculatePythonImportDepth(file, dependencyGraph, visited, depth = 0) {
|
|
162
|
-
if (visited.has(file)) {
|
|
163
|
-
return depth;
|
|
164
|
-
}
|
|
165
|
-
visited.add(file);
|
|
166
|
-
const dependencies = dependencyGraph.get(file) || /* @__PURE__ */ new Set();
|
|
167
|
-
if (dependencies.size === 0) {
|
|
168
|
-
return depth;
|
|
169
|
-
}
|
|
170
|
-
let maxDepth = depth;
|
|
171
|
-
for (const dep of dependencies) {
|
|
172
|
-
const depDepth = await calculatePythonImportDepth(
|
|
173
|
-
dep,
|
|
174
|
-
dependencyGraph,
|
|
175
|
-
new Set(visited),
|
|
176
|
-
depth + 1
|
|
177
|
-
);
|
|
178
|
-
maxDepth = Math.max(maxDepth, depDepth);
|
|
179
|
-
}
|
|
180
|
-
return maxDepth;
|
|
181
|
-
}
|
|
182
254
|
function estimateContextBudget(code, imports, dependencyGraph) {
|
|
183
255
|
void dependencyGraph;
|
|
184
256
|
let budget = (0, import_core5.estimateTokens)(code);
|
|
@@ -206,31 +278,6 @@ function calculatePythonCohesion(exports2, imports) {
|
|
|
206
278
|
}
|
|
207
279
|
return Math.min(1, Math.max(0, cohesion));
|
|
208
280
|
}
|
|
209
|
-
function detectCircularDependencies2(file, dependencyGraph) {
|
|
210
|
-
const circular = [];
|
|
211
|
-
const visited = /* @__PURE__ */ new Set();
|
|
212
|
-
const recursionStack = /* @__PURE__ */ new Set();
|
|
213
|
-
function dfs(current, path) {
|
|
214
|
-
if (recursionStack.has(current)) {
|
|
215
|
-
const cycleStart = path.indexOf(current);
|
|
216
|
-
const cycle = path.slice(cycleStart).concat([current]);
|
|
217
|
-
circular.push(cycle.join(" \u2192 "));
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
if (visited.has(current)) {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
visited.add(current);
|
|
224
|
-
recursionStack.add(current);
|
|
225
|
-
const dependencies = dependencyGraph.get(current) || /* @__PURE__ */ new Set();
|
|
226
|
-
for (const dep of dependencies) {
|
|
227
|
-
dfs(dep, [...path, current]);
|
|
228
|
-
}
|
|
229
|
-
recursionStack.delete(current);
|
|
230
|
-
}
|
|
231
|
-
dfs(file, []);
|
|
232
|
-
return [...new Set(circular)];
|
|
233
|
-
}
|
|
234
281
|
var import_core5, import_path2, import_fs;
|
|
235
282
|
var init_python_context = __esm({
|
|
236
283
|
"src/analyzers/python-context.ts"() {
|
|
@@ -238,6 +285,7 @@ var init_python_context = __esm({
|
|
|
238
285
|
import_core5 = require("@aiready/core");
|
|
239
286
|
import_path2 = require("path");
|
|
240
287
|
import_fs = __toESM(require("fs"));
|
|
288
|
+
init_dependency_graph_utils();
|
|
241
289
|
}
|
|
242
290
|
});
|
|
243
291
|
|
|
@@ -249,7 +297,6 @@ __export(index_exports, {
|
|
|
249
297
|
adjustCohesionForClassification: () => adjustCohesionForClassification,
|
|
250
298
|
adjustFragmentationForClassification: () => adjustFragmentationForClassification,
|
|
251
299
|
analyzeContext: () => analyzeContext,
|
|
252
|
-
analyzeIssues: () => analyzeIssues,
|
|
253
300
|
buildCoUsageMatrix: () => buildCoUsageMatrix,
|
|
254
301
|
buildDependencyGraph: () => buildDependencyGraph,
|
|
255
302
|
buildTypeGraph: () => buildTypeGraph,
|
|
@@ -266,10 +313,12 @@ __export(index_exports, {
|
|
|
266
313
|
classifyFile: () => classifyFile,
|
|
267
314
|
detectCircularDependencies: () => detectCircularDependencies,
|
|
268
315
|
detectModuleClusters: () => detectModuleClusters,
|
|
316
|
+
displayConsoleReport: () => displayConsoleReport,
|
|
269
317
|
extractDomainKeywordsFromPaths: () => extractDomainKeywordsFromPaths,
|
|
270
318
|
extractExports: () => extractExports,
|
|
271
319
|
findConsolidationCandidates: () => findConsolidationCandidates,
|
|
272
320
|
findSemanticClusters: () => findSemanticClusters,
|
|
321
|
+
generateHTMLReport: () => generateHTMLReport,
|
|
273
322
|
generateSummary: () => generateSummary,
|
|
274
323
|
getClassificationRecommendations: () => getClassificationRecommendations,
|
|
275
324
|
getCoUsageData: () => getCoUsageData,
|
|
@@ -288,13 +337,14 @@ __export(index_exports, {
|
|
|
288
337
|
isSessionFile: () => isSessionFile,
|
|
289
338
|
isTypeDefinition: () => isTypeDefinition,
|
|
290
339
|
isUtilityModule: () => isUtilityModule,
|
|
291
|
-
mapScoreToRating: () => mapScoreToRating
|
|
340
|
+
mapScoreToRating: () => mapScoreToRating,
|
|
341
|
+
runInteractiveSetup: () => runInteractiveSetup
|
|
292
342
|
});
|
|
293
343
|
module.exports = __toCommonJS(index_exports);
|
|
294
|
-
var
|
|
344
|
+
var import_core11 = require("@aiready/core");
|
|
295
345
|
|
|
296
346
|
// src/provider.ts
|
|
297
|
-
var
|
|
347
|
+
var import_core9 = require("@aiready/core");
|
|
298
348
|
|
|
299
349
|
// src/analyzer.ts
|
|
300
350
|
var import_core6 = require("@aiready/core");
|
|
@@ -305,6 +355,21 @@ var import_core2 = require("@aiready/core");
|
|
|
305
355
|
// src/ast-utils.ts
|
|
306
356
|
var import_core = require("@aiready/core");
|
|
307
357
|
|
|
358
|
+
// src/utils/string-utils.ts
|
|
359
|
+
function singularize(word) {
|
|
360
|
+
const irregulars = {
|
|
361
|
+
people: "person",
|
|
362
|
+
children: "child",
|
|
363
|
+
men: "man",
|
|
364
|
+
women: "woman"
|
|
365
|
+
};
|
|
366
|
+
if (irregulars[word]) return irregulars[word];
|
|
367
|
+
if (word.endsWith("ies")) return word.slice(0, -3) + "y";
|
|
368
|
+
if (word.endsWith("ses")) return word.slice(0, -2);
|
|
369
|
+
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
370
|
+
return word;
|
|
371
|
+
}
|
|
372
|
+
|
|
308
373
|
// src/semantic-analysis.ts
|
|
309
374
|
function buildCoUsageMatrix(graph) {
|
|
310
375
|
const coUsageMatrix = /* @__PURE__ */ new Map();
|
|
@@ -524,19 +589,6 @@ function inferDomain(name, filePath, domainOptions, fileImports) {
|
|
|
524
589
|
}
|
|
525
590
|
return "unknown";
|
|
526
591
|
}
|
|
527
|
-
function singularize(word) {
|
|
528
|
-
const irregulars = {
|
|
529
|
-
people: "person",
|
|
530
|
-
children: "child",
|
|
531
|
-
men: "man",
|
|
532
|
-
women: "woman"
|
|
533
|
-
};
|
|
534
|
-
if (irregulars[word]) return irregulars[word];
|
|
535
|
-
if (word.endsWith("ies")) return word.slice(0, -3) + "y";
|
|
536
|
-
if (word.endsWith("ses")) return word.slice(0, -2);
|
|
537
|
-
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
538
|
-
return word;
|
|
539
|
-
}
|
|
540
592
|
function getCoUsageData(file, coUsageMatrix) {
|
|
541
593
|
return {
|
|
542
594
|
file,
|
|
@@ -729,143 +781,112 @@ function calculateDirectoryDistance(files) {
|
|
|
729
781
|
return comparisons > 0 ? totalNormalized / comparisons : 0;
|
|
730
782
|
}
|
|
731
783
|
|
|
732
|
-
// src/
|
|
784
|
+
// src/issue-analyzer.ts
|
|
733
785
|
var import_core3 = require("@aiready/core");
|
|
734
|
-
function
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
config
|
|
759
|
-
};
|
|
786
|
+
function analyzeIssues(params) {
|
|
787
|
+
const {
|
|
788
|
+
file,
|
|
789
|
+
importDepth,
|
|
790
|
+
contextBudget,
|
|
791
|
+
cohesionScore,
|
|
792
|
+
fragmentationScore,
|
|
793
|
+
maxDepth,
|
|
794
|
+
maxContextBudget,
|
|
795
|
+
minCohesion,
|
|
796
|
+
maxFragmentation,
|
|
797
|
+
circularDeps
|
|
798
|
+
} = params;
|
|
799
|
+
const issues = [];
|
|
800
|
+
const recommendations = [];
|
|
801
|
+
let severity = import_core3.Severity.Info;
|
|
802
|
+
let potentialSavings = 0;
|
|
803
|
+
if (circularDeps.length > 0) {
|
|
804
|
+
severity = import_core3.Severity.Critical;
|
|
805
|
+
issues.push(`Part of ${circularDeps.length} circular dependency chain(s)`);
|
|
806
|
+
recommendations.push(
|
|
807
|
+
"Break circular dependencies by extracting interfaces or using dependency injection"
|
|
808
|
+
);
|
|
809
|
+
potentialSavings += contextBudget * 0.2;
|
|
760
810
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
(
|
|
765
|
-
0
|
|
766
|
-
)
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const moduleMap = /* @__PURE__ */ new Map();
|
|
774
|
-
for (const result of results) {
|
|
775
|
-
for (const domain of result.domains) {
|
|
776
|
-
if (!moduleMap.has(domain)) moduleMap.set(domain, []);
|
|
777
|
-
moduleMap.get(domain).push(result);
|
|
778
|
-
}
|
|
811
|
+
if (importDepth > maxDepth * 1.5) {
|
|
812
|
+
severity = import_core3.Severity.Critical;
|
|
813
|
+
issues.push(`Import depth ${importDepth} exceeds limit by 50%`);
|
|
814
|
+
recommendations.push("Flatten dependency tree or use facade pattern");
|
|
815
|
+
potentialSavings += contextBudget * 0.3;
|
|
816
|
+
} else if (importDepth > maxDepth) {
|
|
817
|
+
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
818
|
+
issues.push(
|
|
819
|
+
`Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`
|
|
820
|
+
);
|
|
821
|
+
recommendations.push("Consider reducing dependency depth");
|
|
822
|
+
potentialSavings += contextBudget * 0.15;
|
|
779
823
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
fragmentationScore
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
824
|
+
if (contextBudget > maxContextBudget * 1.5) {
|
|
825
|
+
severity = import_core3.Severity.Critical;
|
|
826
|
+
issues.push(
|
|
827
|
+
`Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
|
|
828
|
+
);
|
|
829
|
+
recommendations.push(
|
|
830
|
+
"Split into smaller modules or reduce dependency tree"
|
|
831
|
+
);
|
|
832
|
+
potentialSavings += contextBudget * 0.4;
|
|
833
|
+
} else if (contextBudget > maxContextBudget) {
|
|
834
|
+
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
835
|
+
issues.push(
|
|
836
|
+
`Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
|
|
837
|
+
);
|
|
838
|
+
recommendations.push("Reduce file size or dependencies");
|
|
839
|
+
potentialSavings += contextBudget * 0.2;
|
|
840
|
+
}
|
|
841
|
+
if (cohesionScore < minCohesion * 0.5) {
|
|
842
|
+
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
843
|
+
issues.push(
|
|
844
|
+
`Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`
|
|
845
|
+
);
|
|
846
|
+
recommendations.push(
|
|
847
|
+
"Split file by domain - separate unrelated functionality"
|
|
848
|
+
);
|
|
849
|
+
potentialSavings += contextBudget * 0.25;
|
|
850
|
+
} else if (cohesionScore < minCohesion) {
|
|
851
|
+
if (severity === import_core3.Severity.Info) severity = import_core3.Severity.Minor;
|
|
852
|
+
issues.push(`Low cohesion (${(cohesionScore * 100).toFixed(0)}%)`);
|
|
853
|
+
recommendations.push("Consider grouping related exports together");
|
|
854
|
+
potentialSavings += contextBudget * 0.1;
|
|
855
|
+
}
|
|
856
|
+
if (fragmentationScore > maxFragmentation) {
|
|
857
|
+
if (severity === import_core3.Severity.Info || severity === import_core3.Severity.Minor)
|
|
858
|
+
severity = import_core3.Severity.Minor;
|
|
859
|
+
issues.push(
|
|
860
|
+
`High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`
|
|
861
|
+
);
|
|
862
|
+
recommendations.push("Consolidate with related files in same domain");
|
|
863
|
+
potentialSavings += contextBudget * 0.3;
|
|
864
|
+
}
|
|
865
|
+
if (issues.length === 0) {
|
|
866
|
+
issues.push("No significant issues detected");
|
|
867
|
+
recommendations.push("File is well-structured for AI context usage");
|
|
868
|
+
}
|
|
869
|
+
if (isBuildArtifact(file)) {
|
|
870
|
+
issues.push("Detected build artifact (bundled/output file)");
|
|
871
|
+
recommendations.push("Exclude build outputs from analysis");
|
|
872
|
+
severity = import_core3.Severity.Info;
|
|
873
|
+
potentialSavings = 0;
|
|
829
874
|
}
|
|
830
|
-
const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / totalFiles;
|
|
831
|
-
const lowCohesionFiles = results.filter((r) => r.cohesionScore < 0.4).map((r) => ({ file: r.file, score: r.cohesionScore })).sort((a, b) => a.score - b.score).slice(0, 10);
|
|
832
|
-
const criticalIssues = results.filter(
|
|
833
|
-
(r) => r.severity === "critical"
|
|
834
|
-
).length;
|
|
835
|
-
const majorIssues = results.filter((r) => r.severity === "major").length;
|
|
836
|
-
const minorIssues = results.filter((r) => r.severity === "minor").length;
|
|
837
|
-
const totalPotentialSavings = results.reduce(
|
|
838
|
-
(sum, r) => sum + r.potentialSavings,
|
|
839
|
-
0
|
|
840
|
-
);
|
|
841
|
-
const topExpensiveFiles = results.sort((a, b) => b.contextBudget - a.contextBudget).slice(0, 10).map((r) => ({
|
|
842
|
-
file: r.file,
|
|
843
|
-
contextBudget: r.contextBudget,
|
|
844
|
-
severity: r.severity
|
|
845
|
-
}));
|
|
846
875
|
return {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
avgImportDepth,
|
|
852
|
-
maxImportDepth,
|
|
853
|
-
deepFiles,
|
|
854
|
-
avgFragmentation,
|
|
855
|
-
fragmentedModules,
|
|
856
|
-
avgCohesion,
|
|
857
|
-
lowCohesionFiles,
|
|
858
|
-
criticalIssues,
|
|
859
|
-
majorIssues,
|
|
860
|
-
minorIssues,
|
|
861
|
-
totalPotentialSavings,
|
|
862
|
-
topExpensiveFiles,
|
|
863
|
-
config
|
|
876
|
+
severity,
|
|
877
|
+
issues,
|
|
878
|
+
recommendations,
|
|
879
|
+
potentialSavings: Math.floor(potentialSavings)
|
|
864
880
|
};
|
|
865
881
|
}
|
|
882
|
+
function isBuildArtifact(filePath) {
|
|
883
|
+
const lower = filePath.toLowerCase();
|
|
884
|
+
return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/.next/");
|
|
885
|
+
}
|
|
866
886
|
|
|
867
887
|
// src/graph-builder.ts
|
|
868
888
|
var import_core4 = require("@aiready/core");
|
|
889
|
+
init_dependency_graph_utils();
|
|
869
890
|
var import_path = require("path");
|
|
870
891
|
function resolveImport(source, importingFile, allFiles) {
|
|
871
892
|
if (!source.startsWith(".") && !source.startsWith("/")) {
|
|
@@ -913,25 +934,12 @@ function extractDomainKeywordsFromPaths(files) {
|
|
|
913
934
|
for (const segment of segments) {
|
|
914
935
|
const normalized = segment.toLowerCase();
|
|
915
936
|
if (normalized && !skipFolders.has(normalized) && !normalized.includes(".")) {
|
|
916
|
-
folderNames.add(
|
|
937
|
+
folderNames.add(singularize(normalized));
|
|
917
938
|
}
|
|
918
939
|
}
|
|
919
940
|
}
|
|
920
941
|
return Array.from(folderNames);
|
|
921
942
|
}
|
|
922
|
-
function singularize2(word) {
|
|
923
|
-
const irregulars = {
|
|
924
|
-
people: "person",
|
|
925
|
-
children: "child",
|
|
926
|
-
men: "man",
|
|
927
|
-
women: "woman"
|
|
928
|
-
};
|
|
929
|
-
if (irregulars[word]) return irregulars[word];
|
|
930
|
-
if (word.endsWith("ies")) return word.slice(0, -3) + "y";
|
|
931
|
-
if (word.endsWith("ses")) return word.slice(0, -2);
|
|
932
|
-
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
933
|
-
return word;
|
|
934
|
-
}
|
|
935
943
|
function buildDependencyGraph(files, options) {
|
|
936
944
|
const nodes = /* @__PURE__ */ new Map();
|
|
937
945
|
const edges = /* @__PURE__ */ new Map();
|
|
@@ -982,31 +990,10 @@ function buildDependencyGraph(files, options) {
|
|
|
982
990
|
return graph;
|
|
983
991
|
}
|
|
984
992
|
function calculateImportDepth(file, graph, visited = /* @__PURE__ */ new Set(), depth = 0) {
|
|
985
|
-
|
|
986
|
-
const dependencies = graph.edges.get(file);
|
|
987
|
-
if (!dependencies || dependencies.size === 0) return depth;
|
|
988
|
-
visited.add(file);
|
|
989
|
-
let maxDepth = depth;
|
|
990
|
-
for (const dep of dependencies) {
|
|
991
|
-
maxDepth = Math.max(
|
|
992
|
-
maxDepth,
|
|
993
|
-
calculateImportDepth(dep, graph, visited, depth + 1)
|
|
994
|
-
);
|
|
995
|
-
}
|
|
996
|
-
visited.delete(file);
|
|
997
|
-
return maxDepth;
|
|
993
|
+
return calculateImportDepthFromEdges(file, graph.edges, visited, depth);
|
|
998
994
|
}
|
|
999
995
|
function getTransitiveDependencies(file, graph, visited = /* @__PURE__ */ new Set()) {
|
|
1000
|
-
|
|
1001
|
-
visited.add(file);
|
|
1002
|
-
const dependencies = graph.edges.get(file);
|
|
1003
|
-
if (!dependencies || dependencies.size === 0) return [];
|
|
1004
|
-
const allDeps = [];
|
|
1005
|
-
for (const dep of dependencies) {
|
|
1006
|
-
allDeps.push(dep);
|
|
1007
|
-
allDeps.push(...getTransitiveDependencies(dep, graph, visited));
|
|
1008
|
-
}
|
|
1009
|
-
return [...new Set(allDeps)];
|
|
996
|
+
return getTransitiveDependenciesFromEdges(file, graph.edges, visited);
|
|
1010
997
|
}
|
|
1011
998
|
function calculateContextBudget(file, graph) {
|
|
1012
999
|
const node = graph.nodes.get(file);
|
|
@@ -1022,35 +1009,79 @@ function calculateContextBudget(file, graph) {
|
|
|
1022
1009
|
return totalTokens;
|
|
1023
1010
|
}
|
|
1024
1011
|
function detectCircularDependencies(graph) {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1012
|
+
return detectGraphCycles(graph.edges);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/cluster-detector.ts
|
|
1016
|
+
function detectModuleClusters(graph, options) {
|
|
1017
|
+
const domainMap = /* @__PURE__ */ new Map();
|
|
1018
|
+
for (const [file, node] of graph.nodes.entries()) {
|
|
1019
|
+
const primaryDomain = node.exports[0]?.inferredDomain || "unknown";
|
|
1020
|
+
if (!domainMap.has(primaryDomain)) {
|
|
1021
|
+
domainMap.set(primaryDomain, []);
|
|
1035
1022
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1023
|
+
domainMap.get(primaryDomain).push(file);
|
|
1024
|
+
}
|
|
1025
|
+
const clusters = [];
|
|
1026
|
+
for (const [domain, files] of domainMap.entries()) {
|
|
1027
|
+
if (files.length < 2 || domain === "unknown") continue;
|
|
1028
|
+
const totalTokens = files.reduce((sum, file) => {
|
|
1029
|
+
const node = graph.nodes.get(file);
|
|
1030
|
+
return sum + (node?.tokenCost || 0);
|
|
1031
|
+
}, 0);
|
|
1032
|
+
let sharedImportRatio = 0;
|
|
1033
|
+
if (files.length >= 2) {
|
|
1034
|
+
const allImportSets = files.map(
|
|
1035
|
+
(f) => new Set(graph.nodes.get(f)?.imports || [])
|
|
1036
|
+
);
|
|
1037
|
+
let intersection = new Set(allImportSets[0]);
|
|
1038
|
+
const union = new Set(allImportSets[0]);
|
|
1039
|
+
for (let i = 1; i < allImportSets.length; i++) {
|
|
1040
|
+
const nextSet = allImportSets[i];
|
|
1041
|
+
intersection = new Set([...intersection].filter((x) => nextSet.has(x)));
|
|
1042
|
+
for (const x of nextSet) union.add(x);
|
|
1044
1043
|
}
|
|
1044
|
+
sharedImportRatio = union.size > 0 ? intersection.size / union.size : 0;
|
|
1045
1045
|
}
|
|
1046
|
-
|
|
1046
|
+
const fragmentation = calculateFragmentation(files, domain, {
|
|
1047
|
+
...options,
|
|
1048
|
+
sharedImportRatio
|
|
1049
|
+
});
|
|
1050
|
+
let totalCohesion = 0;
|
|
1051
|
+
files.forEach((f) => {
|
|
1052
|
+
const node = graph.nodes.get(f);
|
|
1053
|
+
if (node) totalCohesion += calculateEnhancedCohesion(node.exports);
|
|
1054
|
+
});
|
|
1055
|
+
const avgCohesion = totalCohesion / files.length;
|
|
1056
|
+
clusters.push({
|
|
1057
|
+
domain,
|
|
1058
|
+
files,
|
|
1059
|
+
totalTokens,
|
|
1060
|
+
fragmentationScore: fragmentation,
|
|
1061
|
+
avgCohesion,
|
|
1062
|
+
suggestedStructure: generateSuggestedStructure(
|
|
1063
|
+
files,
|
|
1064
|
+
totalTokens,
|
|
1065
|
+
fragmentation
|
|
1066
|
+
)
|
|
1067
|
+
});
|
|
1047
1068
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1069
|
+
return clusters;
|
|
1070
|
+
}
|
|
1071
|
+
function generateSuggestedStructure(files, tokens, fragmentation) {
|
|
1072
|
+
const targetFiles = Math.max(1, Math.ceil(tokens / 1e4));
|
|
1073
|
+
const plan = [];
|
|
1074
|
+
if (fragmentation > 0.5) {
|
|
1075
|
+
plan.push(
|
|
1076
|
+
`Consolidate ${files.length} files scattered across multiple directories into ${targetFiles} core module(s)`
|
|
1077
|
+
);
|
|
1052
1078
|
}
|
|
1053
|
-
|
|
1079
|
+
if (tokens > 2e4) {
|
|
1080
|
+
plan.push(
|
|
1081
|
+
`Domain logic is very large (${Math.round(tokens / 1e3)}k tokens). Ensure clear sub-domain boundaries.`
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
return { targetFiles, consolidationPlan: plan };
|
|
1054
1085
|
}
|
|
1055
1086
|
|
|
1056
1087
|
// src/classifier.ts
|
|
@@ -1365,78 +1396,6 @@ function adjustFragmentationForClassification(baseFragmentation, classification)
|
|
|
1365
1396
|
}
|
|
1366
1397
|
}
|
|
1367
1398
|
|
|
1368
|
-
// src/cluster-detector.ts
|
|
1369
|
-
function detectModuleClusters(graph, options) {
|
|
1370
|
-
const domainMap = /* @__PURE__ */ new Map();
|
|
1371
|
-
for (const [file, node] of graph.nodes.entries()) {
|
|
1372
|
-
const primaryDomain = node.exports[0]?.inferredDomain || "unknown";
|
|
1373
|
-
if (!domainMap.has(primaryDomain)) {
|
|
1374
|
-
domainMap.set(primaryDomain, []);
|
|
1375
|
-
}
|
|
1376
|
-
domainMap.get(primaryDomain).push(file);
|
|
1377
|
-
}
|
|
1378
|
-
const clusters = [];
|
|
1379
|
-
for (const [domain, files] of domainMap.entries()) {
|
|
1380
|
-
if (files.length < 2 || domain === "unknown") continue;
|
|
1381
|
-
const totalTokens = files.reduce((sum, file) => {
|
|
1382
|
-
const node = graph.nodes.get(file);
|
|
1383
|
-
return sum + (node?.tokenCost || 0);
|
|
1384
|
-
}, 0);
|
|
1385
|
-
let sharedImportRatio = 0;
|
|
1386
|
-
if (files.length >= 2) {
|
|
1387
|
-
const allImportSets = files.map(
|
|
1388
|
-
(f) => new Set(graph.nodes.get(f)?.imports || [])
|
|
1389
|
-
);
|
|
1390
|
-
let intersection = new Set(allImportSets[0]);
|
|
1391
|
-
let union = new Set(allImportSets[0]);
|
|
1392
|
-
for (let i = 1; i < allImportSets.length; i++) {
|
|
1393
|
-
const nextSet = allImportSets[i];
|
|
1394
|
-
intersection = new Set([...intersection].filter((x) => nextSet.has(x)));
|
|
1395
|
-
for (const x of nextSet) union.add(x);
|
|
1396
|
-
}
|
|
1397
|
-
sharedImportRatio = union.size > 0 ? intersection.size / union.size : 0;
|
|
1398
|
-
}
|
|
1399
|
-
const fragmentation = calculateFragmentation(files, domain, {
|
|
1400
|
-
...options,
|
|
1401
|
-
sharedImportRatio
|
|
1402
|
-
});
|
|
1403
|
-
let totalCohesion = 0;
|
|
1404
|
-
files.forEach((f) => {
|
|
1405
|
-
const node = graph.nodes.get(f);
|
|
1406
|
-
if (node) totalCohesion += calculateEnhancedCohesion(node.exports);
|
|
1407
|
-
});
|
|
1408
|
-
const avgCohesion = totalCohesion / files.length;
|
|
1409
|
-
clusters.push({
|
|
1410
|
-
domain,
|
|
1411
|
-
files,
|
|
1412
|
-
totalTokens,
|
|
1413
|
-
fragmentationScore: fragmentation,
|
|
1414
|
-
avgCohesion,
|
|
1415
|
-
suggestedStructure: generateSuggestedStructure(
|
|
1416
|
-
files,
|
|
1417
|
-
totalTokens,
|
|
1418
|
-
fragmentation
|
|
1419
|
-
)
|
|
1420
|
-
});
|
|
1421
|
-
}
|
|
1422
|
-
return clusters;
|
|
1423
|
-
}
|
|
1424
|
-
function generateSuggestedStructure(files, tokens, fragmentation) {
|
|
1425
|
-
const targetFiles = Math.max(1, Math.ceil(tokens / 1e4));
|
|
1426
|
-
const plan = [];
|
|
1427
|
-
if (fragmentation > 0.5) {
|
|
1428
|
-
plan.push(
|
|
1429
|
-
`Consolidate ${files.length} files scattered across multiple directories into ${targetFiles} core module(s)`
|
|
1430
|
-
);
|
|
1431
|
-
}
|
|
1432
|
-
if (tokens > 2e4) {
|
|
1433
|
-
plan.push(
|
|
1434
|
-
`Domain logic is very large (${Math.round(tokens / 1e3)}k tokens). Ensure clear sub-domain boundaries.`
|
|
1435
|
-
);
|
|
1436
|
-
}
|
|
1437
|
-
return { targetFiles, consolidationPlan: plan };
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
1399
|
// src/remediation.ts
|
|
1441
1400
|
function getClassificationRecommendations(classification, file, issues) {
|
|
1442
1401
|
switch (classification) {
|
|
@@ -1543,124 +1502,14 @@ function getGeneralRecommendations(metrics, thresholds) {
|
|
|
1543
1502
|
|
|
1544
1503
|
// src/analyzer.ts
|
|
1545
1504
|
function calculateCohesion(exports2, filePath, options) {
|
|
1546
|
-
if (exports2.length <= 1) return 1;
|
|
1547
|
-
if (filePath && isTestFile(filePath)) return 1;
|
|
1548
|
-
const domains = exports2.map((e) => e.inferredDomain || "unknown");
|
|
1549
|
-
const uniqueDomains = new Set(domains.filter((d) => d !== "unknown"));
|
|
1550
|
-
const hasImports = exports2.some((e) => !!e.imports);
|
|
1551
|
-
if (!hasImports && !options?.weights) {
|
|
1552
|
-
if (uniqueDomains.size <= 1) return 1;
|
|
1553
|
-
return 0.4;
|
|
1554
|
-
}
|
|
1555
1505
|
return calculateEnhancedCohesion(exports2, filePath, options);
|
|
1556
1506
|
}
|
|
1557
|
-
function analyzeIssues(params) {
|
|
1558
|
-
const {
|
|
1559
|
-
file,
|
|
1560
|
-
importDepth,
|
|
1561
|
-
contextBudget,
|
|
1562
|
-
cohesionScore,
|
|
1563
|
-
fragmentationScore,
|
|
1564
|
-
maxDepth,
|
|
1565
|
-
maxContextBudget,
|
|
1566
|
-
minCohesion,
|
|
1567
|
-
maxFragmentation,
|
|
1568
|
-
circularDeps
|
|
1569
|
-
} = params;
|
|
1570
|
-
const issues = [];
|
|
1571
|
-
const recommendations = [];
|
|
1572
|
-
let severity = import_core6.Severity.Info;
|
|
1573
|
-
let potentialSavings = 0;
|
|
1574
|
-
if (circularDeps.length > 0) {
|
|
1575
|
-
severity = import_core6.Severity.Critical;
|
|
1576
|
-
issues.push(`Part of ${circularDeps.length} circular dependency chain(s)`);
|
|
1577
|
-
recommendations.push(
|
|
1578
|
-
"Break circular dependencies by extracting interfaces or using dependency injection"
|
|
1579
|
-
);
|
|
1580
|
-
potentialSavings += contextBudget * 0.2;
|
|
1581
|
-
}
|
|
1582
|
-
if (importDepth > maxDepth * 1.5) {
|
|
1583
|
-
severity = import_core6.Severity.Critical;
|
|
1584
|
-
issues.push(`Import depth ${importDepth} exceeds limit by 50%`);
|
|
1585
|
-
recommendations.push("Flatten dependency tree or use facade pattern");
|
|
1586
|
-
potentialSavings += contextBudget * 0.3;
|
|
1587
|
-
} else if (importDepth > maxDepth) {
|
|
1588
|
-
if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
|
|
1589
|
-
issues.push(
|
|
1590
|
-
`Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`
|
|
1591
|
-
);
|
|
1592
|
-
recommendations.push("Consider reducing dependency depth");
|
|
1593
|
-
potentialSavings += contextBudget * 0.15;
|
|
1594
|
-
}
|
|
1595
|
-
if (contextBudget > maxContextBudget * 1.5) {
|
|
1596
|
-
severity = import_core6.Severity.Critical;
|
|
1597
|
-
issues.push(
|
|
1598
|
-
`Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
|
|
1599
|
-
);
|
|
1600
|
-
recommendations.push(
|
|
1601
|
-
"Split into smaller modules or reduce dependency tree"
|
|
1602
|
-
);
|
|
1603
|
-
potentialSavings += contextBudget * 0.4;
|
|
1604
|
-
} else if (contextBudget > maxContextBudget) {
|
|
1605
|
-
if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
|
|
1606
|
-
issues.push(
|
|
1607
|
-
`Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
|
|
1608
|
-
);
|
|
1609
|
-
recommendations.push("Reduce file size or dependencies");
|
|
1610
|
-
potentialSavings += contextBudget * 0.2;
|
|
1611
|
-
}
|
|
1612
|
-
if (cohesionScore < minCohesion * 0.5) {
|
|
1613
|
-
if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
|
|
1614
|
-
issues.push(
|
|
1615
|
-
`Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`
|
|
1616
|
-
);
|
|
1617
|
-
recommendations.push(
|
|
1618
|
-
"Split file by domain - separate unrelated functionality"
|
|
1619
|
-
);
|
|
1620
|
-
potentialSavings += contextBudget * 0.25;
|
|
1621
|
-
} else if (cohesionScore < minCohesion) {
|
|
1622
|
-
if (severity === import_core6.Severity.Info) severity = import_core6.Severity.Minor;
|
|
1623
|
-
issues.push(`Low cohesion (${(cohesionScore * 100).toFixed(0)}%)`);
|
|
1624
|
-
recommendations.push("Consider grouping related exports together");
|
|
1625
|
-
potentialSavings += contextBudget * 0.1;
|
|
1626
|
-
}
|
|
1627
|
-
if (fragmentationScore > maxFragmentation) {
|
|
1628
|
-
if (severity === import_core6.Severity.Info || severity === import_core6.Severity.Minor)
|
|
1629
|
-
severity = import_core6.Severity.Minor;
|
|
1630
|
-
issues.push(
|
|
1631
|
-
`High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`
|
|
1632
|
-
);
|
|
1633
|
-
recommendations.push("Consolidate with related files in same domain");
|
|
1634
|
-
potentialSavings += contextBudget * 0.3;
|
|
1635
|
-
}
|
|
1636
|
-
if (issues.length === 0) {
|
|
1637
|
-
issues.push("No significant issues detected");
|
|
1638
|
-
recommendations.push("File is well-structured for AI context usage");
|
|
1639
|
-
}
|
|
1640
|
-
if (isBuildArtifact(file)) {
|
|
1641
|
-
issues.push("Detected build artifact (bundled/output file)");
|
|
1642
|
-
recommendations.push("Exclude build outputs from analysis");
|
|
1643
|
-
severity = import_core6.Severity.Info;
|
|
1644
|
-
potentialSavings = 0;
|
|
1645
|
-
}
|
|
1646
|
-
return {
|
|
1647
|
-
severity,
|
|
1648
|
-
issues,
|
|
1649
|
-
recommendations,
|
|
1650
|
-
potentialSavings: Math.floor(potentialSavings)
|
|
1651
|
-
};
|
|
1652
|
-
}
|
|
1653
|
-
function isBuildArtifact(filePath) {
|
|
1654
|
-
const lower = filePath.toLowerCase();
|
|
1655
|
-
return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/.next/");
|
|
1656
|
-
}
|
|
1657
1507
|
async function analyzeContext(options) {
|
|
1658
1508
|
const {
|
|
1659
1509
|
maxDepth = 5,
|
|
1660
1510
|
maxContextBudget = 1e4,
|
|
1661
1511
|
minCohesion = 0.6,
|
|
1662
1512
|
maxFragmentation = 0.5,
|
|
1663
|
-
focus = "all",
|
|
1664
1513
|
includeNodeModules = false,
|
|
1665
1514
|
...scanOptions
|
|
1666
1515
|
} = options;
|
|
@@ -1698,27 +1547,21 @@ async function analyzeContext(options) {
|
|
|
1698
1547
|
maxContextBudget,
|
|
1699
1548
|
minCohesion,
|
|
1700
1549
|
maxFragmentation,
|
|
1701
|
-
circularDeps:
|
|
1702
|
-
(cycle) => cycle.split(" \u2192 ")
|
|
1703
|
-
)
|
|
1550
|
+
circularDeps: []
|
|
1704
1551
|
});
|
|
1705
1552
|
return {
|
|
1706
1553
|
file: metric.file,
|
|
1707
|
-
tokenCost:
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
linesOfCode: metric.metrics.linesOfCode,
|
|
1554
|
+
tokenCost: 0,
|
|
1555
|
+
linesOfCode: 0,
|
|
1556
|
+
// Not provided by python context yet
|
|
1711
1557
|
importDepth: metric.importDepth,
|
|
1712
|
-
dependencyCount:
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
circularDeps: metric.metrics.circularDependencies.map(
|
|
1717
|
-
(cycle) => cycle.split(" \u2192 ")
|
|
1718
|
-
),
|
|
1558
|
+
dependencyCount: 0,
|
|
1559
|
+
// Not provided
|
|
1560
|
+
dependencyList: [],
|
|
1561
|
+
circularDeps: [],
|
|
1719
1562
|
cohesionScore: metric.cohesion,
|
|
1720
|
-
domains: [
|
|
1721
|
-
exportCount:
|
|
1563
|
+
domains: [],
|
|
1564
|
+
exportCount: 0,
|
|
1722
1565
|
contextBudget: metric.contextBudget,
|
|
1723
1566
|
fragmentationScore: 0,
|
|
1724
1567
|
relatedFiles: [],
|
|
@@ -1730,116 +1573,169 @@ async function analyzeContext(options) {
|
|
|
1730
1573
|
};
|
|
1731
1574
|
});
|
|
1732
1575
|
}
|
|
1733
|
-
const
|
|
1734
|
-
const
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1576
|
+
const clusters = detectModuleClusters(graph);
|
|
1577
|
+
const allCircularDeps = detectCircularDependencies(graph);
|
|
1578
|
+
const results = Array.from(graph.nodes.values()).map(
|
|
1579
|
+
(node) => {
|
|
1580
|
+
const file = node.file;
|
|
1581
|
+
const tokenCost = node.tokenCost;
|
|
1582
|
+
const importDepth = calculateImportDepth(file, graph);
|
|
1583
|
+
const transitiveDeps = getTransitiveDependencies(file, graph);
|
|
1584
|
+
const contextBudget = calculateContextBudget(file, graph);
|
|
1585
|
+
const circularDeps = allCircularDeps.filter(
|
|
1586
|
+
(cycle) => cycle.includes(file)
|
|
1587
|
+
);
|
|
1588
|
+
const cluster = clusters.find((c) => c.files.includes(file));
|
|
1589
|
+
const rawFragmentationScore = cluster ? cluster.fragmentationScore : 0;
|
|
1590
|
+
const rawCohesionScore = calculateEnhancedCohesion(
|
|
1591
|
+
node.exports,
|
|
1592
|
+
file,
|
|
1593
|
+
options
|
|
1594
|
+
);
|
|
1595
|
+
const fileClassification = classifyFile(node, rawCohesionScore);
|
|
1596
|
+
const cohesionScore = adjustCohesionForClassification(
|
|
1597
|
+
rawCohesionScore,
|
|
1598
|
+
fileClassification
|
|
1599
|
+
);
|
|
1600
|
+
const fragmentationScore = adjustFragmentationForClassification(
|
|
1601
|
+
rawFragmentationScore,
|
|
1602
|
+
fileClassification
|
|
1603
|
+
);
|
|
1604
|
+
const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
|
|
1605
|
+
file,
|
|
1606
|
+
importDepth,
|
|
1607
|
+
contextBudget,
|
|
1608
|
+
cohesionScore,
|
|
1609
|
+
fragmentationScore,
|
|
1610
|
+
maxDepth,
|
|
1611
|
+
maxContextBudget,
|
|
1612
|
+
minCohesion,
|
|
1613
|
+
maxFragmentation,
|
|
1614
|
+
circularDeps
|
|
1615
|
+
});
|
|
1616
|
+
const classRecs = getClassificationRecommendations(
|
|
1617
|
+
fileClassification,
|
|
1618
|
+
file,
|
|
1619
|
+
issues
|
|
1620
|
+
);
|
|
1621
|
+
const allRecommendations = Array.from(
|
|
1622
|
+
/* @__PURE__ */ new Set([...recommendations, ...classRecs])
|
|
1623
|
+
);
|
|
1624
|
+
return {
|
|
1625
|
+
file,
|
|
1626
|
+
tokenCost,
|
|
1627
|
+
linesOfCode: node.linesOfCode,
|
|
1628
|
+
importDepth,
|
|
1629
|
+
dependencyCount: transitiveDeps.length,
|
|
1630
|
+
dependencyList: transitiveDeps,
|
|
1631
|
+
circularDeps,
|
|
1632
|
+
cohesionScore,
|
|
1633
|
+
domains: Array.from(
|
|
1634
|
+
new Set(
|
|
1635
|
+
node.exports.flatMap((e) => e.domains?.map((d) => d.domain) || [])
|
|
1636
|
+
)
|
|
1637
|
+
),
|
|
1638
|
+
exportCount: node.exports.length,
|
|
1639
|
+
contextBudget,
|
|
1640
|
+
fragmentationScore,
|
|
1641
|
+
relatedFiles: cluster ? cluster.files : [],
|
|
1642
|
+
fileClassification,
|
|
1643
|
+
severity,
|
|
1644
|
+
issues,
|
|
1645
|
+
recommendations: allRecommendations,
|
|
1646
|
+
potentialSavings
|
|
1647
|
+
};
|
|
1740
1648
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1649
|
+
);
|
|
1650
|
+
return [...results, ...pythonResults];
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// src/summary.ts
|
|
1654
|
+
var import_core7 = require("@aiready/core");
|
|
1655
|
+
function generateSummary(results, options = {}) {
|
|
1656
|
+
const config = options ? Object.fromEntries(
|
|
1657
|
+
Object.entries(options).filter(
|
|
1658
|
+
([key]) => !import_core7.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
|
|
1659
|
+
)
|
|
1660
|
+
) : {};
|
|
1661
|
+
const totalFiles = results.length;
|
|
1662
|
+
const totalTokens = results.reduce((sum, r) => sum + r.tokenCost, 0);
|
|
1663
|
+
const avgContextBudget = totalFiles > 0 ? results.reduce((sum, r) => sum + r.contextBudget, 0) / totalFiles : 0;
|
|
1664
|
+
const deepFiles = results.filter((r) => r.importDepth > 5).map((r) => ({ file: r.file, depth: r.importDepth }));
|
|
1665
|
+
const maxImportDepth = Math.max(0, ...results.map((r) => r.importDepth));
|
|
1666
|
+
const moduleMap = /* @__PURE__ */ new Map();
|
|
1667
|
+
results.forEach((r) => {
|
|
1668
|
+
const parts = r.file.split("/");
|
|
1669
|
+
let domain = "root";
|
|
1670
|
+
if (parts.length > 2) {
|
|
1671
|
+
domain = parts.slice(0, 2).join("/");
|
|
1672
|
+
}
|
|
1673
|
+
if (!moduleMap.has(domain)) moduleMap.set(domain, []);
|
|
1674
|
+
moduleMap.get(domain).push(r);
|
|
1675
|
+
});
|
|
1676
|
+
const fragmentedModules = [];
|
|
1677
|
+
moduleMap.forEach((files, domain) => {
|
|
1678
|
+
const clusterTokens = files.reduce((sum, f) => sum + f.tokenCost, 0);
|
|
1679
|
+
const filePaths = files.map((f) => f.file);
|
|
1680
|
+
const avgEntropy = calculatePathEntropy(filePaths);
|
|
1681
|
+
const fragmentationScore = Math.min(1, avgEntropy * (files.length / 10));
|
|
1682
|
+
if (fragmentationScore > 0.4) {
|
|
1683
|
+
fragmentedModules.push({
|
|
1684
|
+
domain,
|
|
1685
|
+
files: filePaths,
|
|
1686
|
+
fragmentationScore,
|
|
1687
|
+
totalTokens: clusterTokens,
|
|
1688
|
+
avgCohesion: files.reduce((sum, f) => sum + f.cohesionScore, 0) / files.length,
|
|
1689
|
+
suggestedStructure: {
|
|
1690
|
+
targetFiles: Math.ceil(files.length / 2),
|
|
1691
|
+
consolidationPlan: [
|
|
1692
|
+
`Consolidate ${files.length} files in ${domain} into fewer modules`
|
|
1693
|
+
]
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1759
1696
|
}
|
|
1760
|
-
const { issues } = analyzeIssues({
|
|
1761
|
-
file,
|
|
1762
|
-
importDepth,
|
|
1763
|
-
contextBudget,
|
|
1764
|
-
cohesionScore,
|
|
1765
|
-
fragmentationScore,
|
|
1766
|
-
maxDepth,
|
|
1767
|
-
maxContextBudget,
|
|
1768
|
-
minCohesion,
|
|
1769
|
-
maxFragmentation,
|
|
1770
|
-
circularDeps
|
|
1771
|
-
});
|
|
1772
|
-
const domains = [
|
|
1773
|
-
...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
|
|
1774
|
-
];
|
|
1775
|
-
const fileClassification = classifyFile(node);
|
|
1776
|
-
const adjustedCohesionScore = adjustCohesionForClassification(
|
|
1777
|
-
cohesionScore,
|
|
1778
|
-
fileClassification,
|
|
1779
|
-
node
|
|
1780
|
-
);
|
|
1781
|
-
const adjustedFragmentationScore = adjustFragmentationForClassification(
|
|
1782
|
-
fragmentationScore,
|
|
1783
|
-
fileClassification
|
|
1784
|
-
);
|
|
1785
|
-
const classificationRecommendations = getClassificationRecommendations(
|
|
1786
|
-
fileClassification,
|
|
1787
|
-
file,
|
|
1788
|
-
issues
|
|
1789
|
-
);
|
|
1790
|
-
const {
|
|
1791
|
-
severity: adjustedSeverity,
|
|
1792
|
-
issues: adjustedIssues,
|
|
1793
|
-
recommendations: finalRecommendations,
|
|
1794
|
-
potentialSavings: adjustedSavings
|
|
1795
|
-
} = analyzeIssues({
|
|
1796
|
-
file,
|
|
1797
|
-
importDepth,
|
|
1798
|
-
contextBudget,
|
|
1799
|
-
cohesionScore: adjustedCohesionScore,
|
|
1800
|
-
fragmentationScore: adjustedFragmentationScore,
|
|
1801
|
-
maxDepth,
|
|
1802
|
-
maxContextBudget,
|
|
1803
|
-
minCohesion,
|
|
1804
|
-
maxFragmentation,
|
|
1805
|
-
circularDeps
|
|
1806
|
-
});
|
|
1807
|
-
results.push({
|
|
1808
|
-
file,
|
|
1809
|
-
tokenCost: node.tokenCost,
|
|
1810
|
-
linesOfCode: node.linesOfCode,
|
|
1811
|
-
importDepth,
|
|
1812
|
-
dependencyCount: dependencyList.length,
|
|
1813
|
-
dependencyList,
|
|
1814
|
-
circularDeps: circularDeps.filter((cycle) => cycle.includes(file)),
|
|
1815
|
-
cohesionScore: adjustedCohesionScore,
|
|
1816
|
-
domains,
|
|
1817
|
-
exportCount: node.exports.length,
|
|
1818
|
-
contextBudget,
|
|
1819
|
-
fragmentationScore: adjustedFragmentationScore,
|
|
1820
|
-
relatedFiles,
|
|
1821
|
-
fileClassification,
|
|
1822
|
-
severity: adjustedSeverity,
|
|
1823
|
-
issues: adjustedIssues,
|
|
1824
|
-
recommendations: [
|
|
1825
|
-
...finalRecommendations,
|
|
1826
|
-
...classificationRecommendations.slice(0, 1)
|
|
1827
|
-
],
|
|
1828
|
-
potentialSavings: adjustedSavings
|
|
1829
|
-
});
|
|
1830
|
-
}
|
|
1831
|
-
const allResults = [...results, ...pythonResults];
|
|
1832
|
-
const finalSummary = generateSummary(allResults, options);
|
|
1833
|
-
return allResults.sort((a, b) => {
|
|
1834
|
-
const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
|
|
1835
|
-
const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
|
|
1836
|
-
if (severityDiff !== 0) return severityDiff;
|
|
1837
|
-
return b.contextBudget - a.contextBudget;
|
|
1838
1697
|
});
|
|
1698
|
+
fragmentedModules.sort((a, b) => b.fragmentationScore - a.fragmentationScore);
|
|
1699
|
+
const avgFragmentation = fragmentedModules.length > 0 ? fragmentedModules.reduce((sum, m) => sum + m.fragmentationScore, 0) / fragmentedModules.length : 0;
|
|
1700
|
+
const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / (totalFiles || 1);
|
|
1701
|
+
const lowCohesionFiles = results.filter((r) => r.cohesionScore < 0.4).map((r) => ({ file: r.file, score: r.cohesionScore }));
|
|
1702
|
+
const criticalIssues = results.filter(
|
|
1703
|
+
(r) => r.severity === "critical"
|
|
1704
|
+
).length;
|
|
1705
|
+
const majorIssues = results.filter((r) => r.severity === "major").length;
|
|
1706
|
+
const minorIssues = results.filter((r) => r.severity === "minor").length;
|
|
1707
|
+
const totalPotentialSavings = results.reduce(
|
|
1708
|
+
(sum, r) => sum + (r.potentialSavings || 0),
|
|
1709
|
+
0
|
|
1710
|
+
);
|
|
1711
|
+
const topExpensiveFiles = [...results].sort((a, b) => b.contextBudget - a.contextBudget).slice(0, 10).map((r) => ({
|
|
1712
|
+
file: r.file,
|
|
1713
|
+
contextBudget: r.contextBudget,
|
|
1714
|
+
severity: r.severity
|
|
1715
|
+
}));
|
|
1716
|
+
return {
|
|
1717
|
+
totalFiles,
|
|
1718
|
+
totalTokens,
|
|
1719
|
+
avgContextBudget,
|
|
1720
|
+
maxContextBudget: Math.max(0, ...results.map((r) => r.contextBudget)),
|
|
1721
|
+
avgImportDepth: results.reduce((sum, r) => sum + r.importDepth, 0) / (totalFiles || 1),
|
|
1722
|
+
maxImportDepth,
|
|
1723
|
+
deepFiles,
|
|
1724
|
+
avgFragmentation,
|
|
1725
|
+
fragmentedModules,
|
|
1726
|
+
avgCohesion,
|
|
1727
|
+
lowCohesionFiles,
|
|
1728
|
+
criticalIssues,
|
|
1729
|
+
majorIssues,
|
|
1730
|
+
minorIssues,
|
|
1731
|
+
totalPotentialSavings,
|
|
1732
|
+
topExpensiveFiles,
|
|
1733
|
+
config
|
|
1734
|
+
};
|
|
1839
1735
|
}
|
|
1840
1736
|
|
|
1841
1737
|
// src/scoring.ts
|
|
1842
|
-
var
|
|
1738
|
+
var import_core8 = require("@aiready/core");
|
|
1843
1739
|
function calculateContextScore(summary, costConfig) {
|
|
1844
1740
|
const {
|
|
1845
1741
|
avgContextBudget,
|
|
@@ -1950,8 +1846,8 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1950
1846
|
priority: "high"
|
|
1951
1847
|
});
|
|
1952
1848
|
}
|
|
1953
|
-
const cfg = { ...
|
|
1954
|
-
const estimatedMonthlyCost = (0,
|
|
1849
|
+
const cfg = { ...import_core8.DEFAULT_COST_CONFIG, ...costConfig };
|
|
1850
|
+
const estimatedMonthlyCost = (0, import_core8.calculateMonthlyCost)(
|
|
1955
1851
|
avgContextBudget * (totalFiles || 1),
|
|
1956
1852
|
cfg
|
|
1957
1853
|
);
|
|
@@ -1959,9 +1855,9 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1959
1855
|
...Array(criticalIssues).fill({ severity: "critical" }),
|
|
1960
1856
|
...Array(majorIssues).fill({ severity: "major" })
|
|
1961
1857
|
];
|
|
1962
|
-
const productivityImpact = (0,
|
|
1858
|
+
const productivityImpact = (0, import_core8.calculateProductivityImpact)(issues);
|
|
1963
1859
|
return {
|
|
1964
|
-
toolName:
|
|
1860
|
+
toolName: import_core8.ToolName.ContextAnalyzer,
|
|
1965
1861
|
score,
|
|
1966
1862
|
rawMetrics: {
|
|
1967
1863
|
avgContextBudget: Math.round(avgContextBudget),
|
|
@@ -1988,7 +1884,7 @@ function mapScoreToRating(score) {
|
|
|
1988
1884
|
|
|
1989
1885
|
// src/provider.ts
|
|
1990
1886
|
var ContextAnalyzerProvider = {
|
|
1991
|
-
id:
|
|
1887
|
+
id: import_core9.ToolName.ContextAnalyzer,
|
|
1992
1888
|
alias: ["context", "fragmentation", "budget"],
|
|
1993
1889
|
async analyze(options) {
|
|
1994
1890
|
const results = await analyzeContext(options);
|
|
@@ -1997,7 +1893,7 @@ var ContextAnalyzerProvider = {
|
|
|
1997
1893
|
(r) => ({
|
|
1998
1894
|
fileName: r.file,
|
|
1999
1895
|
issues: r.issues.map((msg) => ({
|
|
2000
|
-
type:
|
|
1896
|
+
type: import_core9.IssueType.ContextFragmentation,
|
|
2001
1897
|
severity: r.severity,
|
|
2002
1898
|
message: msg,
|
|
2003
1899
|
location: { file: r.file, line: 1 },
|
|
@@ -2010,13 +1906,13 @@ var ContextAnalyzerProvider = {
|
|
|
2010
1906
|
}
|
|
2011
1907
|
})
|
|
2012
1908
|
);
|
|
2013
|
-
return
|
|
1909
|
+
return import_core9.SpokeOutputSchema.parse({
|
|
2014
1910
|
results: normalizedResults,
|
|
2015
1911
|
summary: {
|
|
2016
1912
|
...summary
|
|
2017
1913
|
},
|
|
2018
1914
|
metadata: {
|
|
2019
|
-
toolName:
|
|
1915
|
+
toolName: import_core9.ToolName.ContextAnalyzer,
|
|
2020
1916
|
version: "0.17.5",
|
|
2021
1917
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2022
1918
|
}
|
|
@@ -2030,9 +1926,9 @@ var ContextAnalyzerProvider = {
|
|
|
2030
1926
|
};
|
|
2031
1927
|
|
|
2032
1928
|
// src/defaults.ts
|
|
2033
|
-
var
|
|
1929
|
+
var import_core10 = require("@aiready/core");
|
|
2034
1930
|
async function getSmartDefaults(directory, userOptions) {
|
|
2035
|
-
const files = await (0,
|
|
1931
|
+
const files = await (0, import_core10.scanFiles)({
|
|
2036
1932
|
rootDir: directory,
|
|
2037
1933
|
include: userOptions.include,
|
|
2038
1934
|
exclude: userOptions.exclude
|
|
@@ -2076,8 +1972,329 @@ async function getSmartDefaults(directory, userOptions) {
|
|
|
2076
1972
|
};
|
|
2077
1973
|
}
|
|
2078
1974
|
|
|
1975
|
+
// src/utils/output-formatter.ts
|
|
1976
|
+
var import_chalk = __toESM(require("chalk"));
|
|
1977
|
+
var import_fs2 = require("fs");
|
|
1978
|
+
var import_path3 = require("path");
|
|
1979
|
+
var import_prompts = __toESM(require("prompts"));
|
|
1980
|
+
function displayConsoleReport(summary, results, maxResults = 10) {
|
|
1981
|
+
const divider = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
|
|
1982
|
+
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
1983
|
+
console.log(import_chalk.default.bold("\u{1F4CA} Context Analysis Summary:\n"));
|
|
1984
|
+
console.log(` \u2022 Total Files: ${import_chalk.default.cyan(summary.totalFiles)}`);
|
|
1985
|
+
console.log(
|
|
1986
|
+
` \u2022 Total Tokens: ${import_chalk.default.cyan(summary.totalTokens.toLocaleString())}`
|
|
1987
|
+
);
|
|
1988
|
+
console.log(
|
|
1989
|
+
` \u2022 Avg Budget: ${import_chalk.default.cyan(summary.avgContextBudget.toFixed(0))} tokens`
|
|
1990
|
+
);
|
|
1991
|
+
console.log(
|
|
1992
|
+
` \u2022 Potential Saving: ${import_chalk.default.green(summary.totalPotentialSavings.toLocaleString())} tokens`
|
|
1993
|
+
);
|
|
1994
|
+
console.log();
|
|
1995
|
+
if (totalIssues > 0) {
|
|
1996
|
+
console.log(import_chalk.default.bold("\u26A0\uFE0F Issues Detected:\n"));
|
|
1997
|
+
console.log(` \u2022 ${import_chalk.default.red("\u{1F534} Critical:")} ${summary.criticalIssues}`);
|
|
1998
|
+
console.log(` \u2022 ${import_chalk.default.yellow("\u{1F7E1} Major:")} ${summary.majorIssues}`);
|
|
1999
|
+
console.log(` \u2022 ${import_chalk.default.blue("\u{1F535} Minor:")} ${summary.minorIssues}`);
|
|
2000
|
+
console.log();
|
|
2001
|
+
} else {
|
|
2002
|
+
console.log(import_chalk.default.green("\u2705 No significant context issues detected!\n"));
|
|
2003
|
+
}
|
|
2004
|
+
if (summary.fragmentedModules.length > 0) {
|
|
2005
|
+
console.log(import_chalk.default.bold("\u{1F9E9} Top Fragmented Modules:\n"));
|
|
2006
|
+
summary.fragmentedModules.slice(0, maxResults).forEach((mod) => {
|
|
2007
|
+
const scoreColor = mod.fragmentationScore > 0.7 ? import_chalk.default.red : mod.fragmentationScore > 0.4 ? import_chalk.default.yellow : import_chalk.default.green;
|
|
2008
|
+
console.log(
|
|
2009
|
+
` ${scoreColor("\u25A0")} ${import_chalk.default.white(mod.domain)} ${import_chalk.default.dim(`(${mod.files.length} files, ${(mod.fragmentationScore * 100).toFixed(0)}% frag)`)}`
|
|
2010
|
+
);
|
|
2011
|
+
});
|
|
2012
|
+
console.log();
|
|
2013
|
+
}
|
|
2014
|
+
if (summary.topExpensiveFiles.length > 0) {
|
|
2015
|
+
console.log(import_chalk.default.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
2016
|
+
summary.topExpensiveFiles.slice(0, maxResults).forEach((item) => {
|
|
2017
|
+
const fileName = item.file.split("/").slice(-2).join("/");
|
|
2018
|
+
const severityColor = item.severity === "critical" ? import_chalk.default.red : item.severity === "major" ? import_chalk.default.yellow : import_chalk.default.blue;
|
|
2019
|
+
console.log(
|
|
2020
|
+
` ${severityColor("\u25CF")} ${import_chalk.default.white(fileName)} ${import_chalk.default.dim(`- ${item.contextBudget.toLocaleString()} tokens`)}`
|
|
2021
|
+
);
|
|
2022
|
+
});
|
|
2023
|
+
console.log();
|
|
2024
|
+
}
|
|
2025
|
+
if (totalIssues > 0) {
|
|
2026
|
+
console.log(import_chalk.default.bold("\u{1F4A1} Top Recommendations:\n"));
|
|
2027
|
+
const topFiles = results.filter((r) => r.severity === "critical" || r.severity === "major").slice(0, 3);
|
|
2028
|
+
topFiles.forEach((result, index) => {
|
|
2029
|
+
const fileName = result.file.split("/").slice(-2).join("/");
|
|
2030
|
+
console.log(import_chalk.default.cyan(` ${index + 1}. ${fileName}`));
|
|
2031
|
+
result.recommendations.slice(0, 2).forEach((rec) => {
|
|
2032
|
+
console.log(import_chalk.default.dim(` \u2022 ${rec}`));
|
|
2033
|
+
});
|
|
2034
|
+
});
|
|
2035
|
+
console.log();
|
|
2036
|
+
}
|
|
2037
|
+
console.log(import_chalk.default.cyan(divider));
|
|
2038
|
+
console.log(
|
|
2039
|
+
import_chalk.default.dim(
|
|
2040
|
+
"\n\u2B50 Like aiready? Star us on GitHub: https://github.com/caopengau/aiready-context-analyzer"
|
|
2041
|
+
)
|
|
2042
|
+
);
|
|
2043
|
+
console.log(
|
|
2044
|
+
import_chalk.default.dim(
|
|
2045
|
+
"\u{1F41B} Found a bug? Report it: https://github.com/caopengau/aiready-context-analyzer/issues\n"
|
|
2046
|
+
)
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
function generateHTMLReport(summary, results) {
|
|
2050
|
+
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
2051
|
+
void results;
|
|
2052
|
+
return `<!DOCTYPE html>
|
|
2053
|
+
<html lang="en">
|
|
2054
|
+
<head>
|
|
2055
|
+
<meta charset="UTF-8">
|
|
2056
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2057
|
+
<title>aiready Context Analysis Report</title>
|
|
2058
|
+
<style>
|
|
2059
|
+
body {
|
|
2060
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
2061
|
+
line-height: 1.6;
|
|
2062
|
+
color: #333;
|
|
2063
|
+
max-width: 1200px;
|
|
2064
|
+
margin: 0 auto;
|
|
2065
|
+
padding: 20px;
|
|
2066
|
+
background-color: #f5f5f5;
|
|
2067
|
+
}
|
|
2068
|
+
h1, h2, h3 { color: #2c3e50; }
|
|
2069
|
+
.header {
|
|
2070
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
2071
|
+
color: white;
|
|
2072
|
+
padding: 30px;
|
|
2073
|
+
border-radius: 8px;
|
|
2074
|
+
margin-bottom: 30px;
|
|
2075
|
+
}
|
|
2076
|
+
.summary {
|
|
2077
|
+
display: grid;
|
|
2078
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
2079
|
+
gap: 20px;
|
|
2080
|
+
margin-bottom: 30px;
|
|
2081
|
+
}
|
|
2082
|
+
.card {
|
|
2083
|
+
background: white;
|
|
2084
|
+
padding: 20px;
|
|
2085
|
+
border-radius: 8px;
|
|
2086
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
2087
|
+
}
|
|
2088
|
+
.metric {
|
|
2089
|
+
font-size: 2em;
|
|
2090
|
+
font-weight: bold;
|
|
2091
|
+
color: #667eea;
|
|
2092
|
+
}
|
|
2093
|
+
.label {
|
|
2094
|
+
color: #666;
|
|
2095
|
+
font-size: 0.9em;
|
|
2096
|
+
margin-top: 5px;
|
|
2097
|
+
}
|
|
2098
|
+
.issue-critical { color: #e74c3c; }
|
|
2099
|
+
.issue-major { color: #f39c12; }
|
|
2100
|
+
.issue-minor { color: #3498db; }
|
|
2101
|
+
table {
|
|
2102
|
+
width: 100%;
|
|
2103
|
+
border-collapse: collapse;
|
|
2104
|
+
background: white;
|
|
2105
|
+
border-radius: 8px;
|
|
2106
|
+
overflow: hidden;
|
|
2107
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
2108
|
+
}
|
|
2109
|
+
th, td {
|
|
2110
|
+
padding: 12px;
|
|
2111
|
+
text-align: left;
|
|
2112
|
+
border-bottom: 1px solid #eee;
|
|
2113
|
+
}
|
|
2114
|
+
th {
|
|
2115
|
+
background-color: #667eea;
|
|
2116
|
+
color: white;
|
|
2117
|
+
font-weight: 600;
|
|
2118
|
+
}
|
|
2119
|
+
tr:hover { background-color: #f8f9fa; }
|
|
2120
|
+
.footer {
|
|
2121
|
+
text-align: center;
|
|
2122
|
+
margin-top: 40px;
|
|
2123
|
+
padding: 20px;
|
|
2124
|
+
color: #666;
|
|
2125
|
+
font-size: 0.9em;
|
|
2126
|
+
}
|
|
2127
|
+
</style>
|
|
2128
|
+
</head>
|
|
2129
|
+
<body>
|
|
2130
|
+
<div class="header">
|
|
2131
|
+
<h1>\u{1F50D} AIReady Context Analysis Report</h1>
|
|
2132
|
+
<p>Generated on ${(/* @__PURE__ */ new Date()).toLocaleString()}</p>
|
|
2133
|
+
</div>
|
|
2134
|
+
|
|
2135
|
+
<div class="summary">
|
|
2136
|
+
<div class="card">
|
|
2137
|
+
<div class="metric">${summary.totalFiles}</div>
|
|
2138
|
+
<div class="label">Files Analyzed</div>
|
|
2139
|
+
</div>
|
|
2140
|
+
<div class="card">
|
|
2141
|
+
<div class="metric">${summary.totalTokens.toLocaleString()}</div>
|
|
2142
|
+
<div class="label">Total Tokens</div>
|
|
2143
|
+
</div>
|
|
2144
|
+
<div class="card">
|
|
2145
|
+
<div class="metric">${summary.avgContextBudget.toFixed(0)}</div>
|
|
2146
|
+
<div class="label">Avg Context Budget</div>
|
|
2147
|
+
</div>
|
|
2148
|
+
<div class="card">
|
|
2149
|
+
<div class="metric ${totalIssues > 0 ? "issue-major" : ""}">${totalIssues}</div>
|
|
2150
|
+
<div class="label">Total Issues</div>
|
|
2151
|
+
</div>
|
|
2152
|
+
</div>
|
|
2153
|
+
|
|
2154
|
+
${totalIssues > 0 ? `
|
|
2155
|
+
<div class="card" style="margin-bottom: 30px;">
|
|
2156
|
+
<h2>\u26A0\uFE0F Issues Summary</h2>
|
|
2157
|
+
<p>
|
|
2158
|
+
<span class="issue-critical">\u{1F534} Critical: ${summary.criticalIssues}</span>
|
|
2159
|
+
<span class="issue-major">\u{1F7E1} Major: ${summary.majorIssues}</span>
|
|
2160
|
+
<span class="issue-minor">\u{1F535} Minor: ${summary.minorIssues}</span>
|
|
2161
|
+
</p>
|
|
2162
|
+
<p><strong>Potential Savings:</strong> ${summary.totalPotentialSavings.toLocaleString()} tokens</p>
|
|
2163
|
+
</div>
|
|
2164
|
+
` : ""}
|
|
2165
|
+
|
|
2166
|
+
${summary.fragmentedModules.length > 0 ? `
|
|
2167
|
+
<div class="card" style="margin-bottom: 30px;">
|
|
2168
|
+
<h2>\u{1F9E9} Fragmented Modules</h2>
|
|
2169
|
+
<table>
|
|
2170
|
+
<thead>
|
|
2171
|
+
<tr>
|
|
2172
|
+
<th>Domain</th>
|
|
2173
|
+
<th>Files</th>
|
|
2174
|
+
<th>Fragmentation</th>
|
|
2175
|
+
<th>Token Cost</th>
|
|
2176
|
+
</tr>
|
|
2177
|
+
</thead>
|
|
2178
|
+
<tbody>
|
|
2179
|
+
${summary.fragmentedModules.map(
|
|
2180
|
+
(m) => `
|
|
2181
|
+
<tr>
|
|
2182
|
+
<td>${m.domain}</td>
|
|
2183
|
+
<td>${m.files.length}</td>
|
|
2184
|
+
<td>${(m.fragmentationScore * 100).toFixed(0)}%</td>
|
|
2185
|
+
<td>${m.totalTokens.toLocaleString()}</td>
|
|
2186
|
+
</tr>
|
|
2187
|
+
`
|
|
2188
|
+
).join("")}
|
|
2189
|
+
</tbody>
|
|
2190
|
+
</table>
|
|
2191
|
+
</div>
|
|
2192
|
+
` : ""}
|
|
2193
|
+
|
|
2194
|
+
${summary.topExpensiveFiles.length > 0 ? `
|
|
2195
|
+
<div class="card" style="margin-bottom: 30px;">
|
|
2196
|
+
<h2>\u{1F4B8} Most Expensive Files</h2>
|
|
2197
|
+
<table>
|
|
2198
|
+
<thead>
|
|
2199
|
+
<tr>
|
|
2200
|
+
<th>File</th>
|
|
2201
|
+
<th>Context Budget</th>
|
|
2202
|
+
<th>Severity</th>
|
|
2203
|
+
</tr>
|
|
2204
|
+
</thead>
|
|
2205
|
+
<tbody>
|
|
2206
|
+
${summary.topExpensiveFiles.map(
|
|
2207
|
+
(f) => `
|
|
2208
|
+
<tr>
|
|
2209
|
+
<td>${f.file}</td>
|
|
2210
|
+
<td>${f.contextBudget.toLocaleString()} tokens</td>
|
|
2211
|
+
<td class="issue-${f.severity}">${f.severity.toUpperCase()}</td>
|
|
2212
|
+
</tr>
|
|
2213
|
+
`
|
|
2214
|
+
).join("")}
|
|
2215
|
+
</tbody>
|
|
2216
|
+
</table>
|
|
2217
|
+
</div>
|
|
2218
|
+
` : ""}
|
|
2219
|
+
|
|
2220
|
+
<div class="footer">
|
|
2221
|
+
<p>Generated by <strong>@aiready/context-analyzer</strong></p>
|
|
2222
|
+
<p>Like AIReady? <a href="https://github.com/caopengau/aiready-context-analyzer">Star us on GitHub</a></p>
|
|
2223
|
+
<p>Found a bug? <a href="https://github.com/caopengau/aiready-context-analyzer/issues">Report it here</a></p>
|
|
2224
|
+
</div>
|
|
2225
|
+
</body>
|
|
2226
|
+
</html>`;
|
|
2227
|
+
}
|
|
2228
|
+
async function runInteractiveSetup(directory, current) {
|
|
2229
|
+
console.log(import_chalk.default.yellow("\u{1F9ED} Interactive mode: let\u2019s tailor the analysis."));
|
|
2230
|
+
const pkgPath = (0, import_path3.join)(directory, "package.json");
|
|
2231
|
+
let deps = {};
|
|
2232
|
+
if ((0, import_fs2.existsSync)(pkgPath)) {
|
|
2233
|
+
try {
|
|
2234
|
+
const pkg = JSON.parse((0, import_fs2.readFileSync)(pkgPath, "utf-8"));
|
|
2235
|
+
deps = { ...pkg.dependencies || {}, ...pkg.devDependencies || {} };
|
|
2236
|
+
} catch (e) {
|
|
2237
|
+
void e;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
const hasNextJs = (0, import_fs2.existsSync)((0, import_path3.join)(directory, ".next")) || !!deps["next"];
|
|
2241
|
+
const hasCDK = (0, import_fs2.existsSync)((0, import_path3.join)(directory, "cdk.out")) || !!deps["aws-cdk-lib"] || Object.keys(deps).some((d) => d.startsWith("@aws-cdk/"));
|
|
2242
|
+
const recommendedExcludes = new Set(current.exclude || []);
|
|
2243
|
+
if (hasNextJs && !Array.from(recommendedExcludes).some((p) => p.includes(".next"))) {
|
|
2244
|
+
recommendedExcludes.add("**/.next/**");
|
|
2245
|
+
}
|
|
2246
|
+
if (hasCDK && !Array.from(recommendedExcludes).some((p) => p.includes("cdk.out"))) {
|
|
2247
|
+
recommendedExcludes.add("**/cdk.out/**");
|
|
2248
|
+
}
|
|
2249
|
+
const { applyExcludes } = await (0, import_prompts.default)({
|
|
2250
|
+
type: "toggle",
|
|
2251
|
+
name: "applyExcludes",
|
|
2252
|
+
message: `Detected ${hasNextJs ? "Next.js " : ""}${hasCDK ? "AWS CDK " : ""}frameworks. Apply recommended excludes?`,
|
|
2253
|
+
initial: true,
|
|
2254
|
+
active: "yes",
|
|
2255
|
+
inactive: "no"
|
|
2256
|
+
});
|
|
2257
|
+
const nextOptions = { ...current };
|
|
2258
|
+
if (applyExcludes) {
|
|
2259
|
+
nextOptions.exclude = Array.from(recommendedExcludes);
|
|
2260
|
+
}
|
|
2261
|
+
const { focusArea } = await (0, import_prompts.default)({
|
|
2262
|
+
type: "select",
|
|
2263
|
+
name: "focusArea",
|
|
2264
|
+
message: "Which areas to focus?",
|
|
2265
|
+
choices: [
|
|
2266
|
+
{ title: "Frontend (web app)", value: "frontend" },
|
|
2267
|
+
{ title: "Backend (API/infra)", value: "backend" },
|
|
2268
|
+
{ title: "Both", value: "both" }
|
|
2269
|
+
],
|
|
2270
|
+
initial: 2
|
|
2271
|
+
});
|
|
2272
|
+
if (focusArea === "frontend") {
|
|
2273
|
+
nextOptions.include = ["**/*.{ts,tsx,js,jsx}"];
|
|
2274
|
+
nextOptions.exclude = Array.from(
|
|
2275
|
+
/* @__PURE__ */ new Set([
|
|
2276
|
+
...nextOptions.exclude || [],
|
|
2277
|
+
"**/cdk.out/**",
|
|
2278
|
+
"**/infra/**",
|
|
2279
|
+
"**/server/**",
|
|
2280
|
+
"**/backend/**"
|
|
2281
|
+
])
|
|
2282
|
+
);
|
|
2283
|
+
} else if (focusArea === "backend") {
|
|
2284
|
+
nextOptions.include = [
|
|
2285
|
+
"**/api/**",
|
|
2286
|
+
"**/server/**",
|
|
2287
|
+
"**/backend/**",
|
|
2288
|
+
"**/infra/**",
|
|
2289
|
+
"**/*.{ts,js,py,java}"
|
|
2290
|
+
];
|
|
2291
|
+
}
|
|
2292
|
+
console.log(import_chalk.default.green("\u2713 Interactive configuration applied."));
|
|
2293
|
+
return nextOptions;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2079
2296
|
// src/index.ts
|
|
2080
|
-
|
|
2297
|
+
import_core11.ToolRegistry.register(ContextAnalyzerProvider);
|
|
2081
2298
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2082
2299
|
0 && (module.exports = {
|
|
2083
2300
|
Classification,
|
|
@@ -2085,7 +2302,6 @@ import_core10.ToolRegistry.register(ContextAnalyzerProvider);
|
|
|
2085
2302
|
adjustCohesionForClassification,
|
|
2086
2303
|
adjustFragmentationForClassification,
|
|
2087
2304
|
analyzeContext,
|
|
2088
|
-
analyzeIssues,
|
|
2089
2305
|
buildCoUsageMatrix,
|
|
2090
2306
|
buildDependencyGraph,
|
|
2091
2307
|
buildTypeGraph,
|
|
@@ -2102,10 +2318,12 @@ import_core10.ToolRegistry.register(ContextAnalyzerProvider);
|
|
|
2102
2318
|
classifyFile,
|
|
2103
2319
|
detectCircularDependencies,
|
|
2104
2320
|
detectModuleClusters,
|
|
2321
|
+
displayConsoleReport,
|
|
2105
2322
|
extractDomainKeywordsFromPaths,
|
|
2106
2323
|
extractExports,
|
|
2107
2324
|
findConsolidationCandidates,
|
|
2108
2325
|
findSemanticClusters,
|
|
2326
|
+
generateHTMLReport,
|
|
2109
2327
|
generateSummary,
|
|
2110
2328
|
getClassificationRecommendations,
|
|
2111
2329
|
getCoUsageData,
|
|
@@ -2124,5 +2342,6 @@ import_core10.ToolRegistry.register(ContextAnalyzerProvider);
|
|
|
2124
2342
|
isSessionFile,
|
|
2125
2343
|
isTypeDefinition,
|
|
2126
2344
|
isUtilityModule,
|
|
2127
|
-
mapScoreToRating
|
|
2345
|
+
mapScoreToRating,
|
|
2346
|
+
runInteractiveSetup
|
|
2128
2347
|
});
|