@aiready/context-analyzer 0.21.1 → 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 +13 -12
- package/.turbo/turbo-test.log +32 -341
- 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-OUYSZZ7X.mjs +1846 -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-W2KNBN6W.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 +591 -1057
- 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 +822 -588
- 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__/scoring.test.ts +217 -9
- 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/scoring.ts +40 -20
- 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("/");
|
|
1759
1672
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
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;
|
|
1673
|
+
if (!moduleMap.has(domain)) moduleMap.set(domain, []);
|
|
1674
|
+
moduleMap.get(domain).push(r);
|
|
1838
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
|
+
});
|
|
1696
|
+
}
|
|
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,
|
|
@@ -1848,34 +1744,46 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1848
1744
|
maxImportDepth,
|
|
1849
1745
|
avgFragmentation,
|
|
1850
1746
|
criticalIssues,
|
|
1851
|
-
majorIssues
|
|
1747
|
+
majorIssues,
|
|
1748
|
+
totalFiles
|
|
1852
1749
|
} = summary;
|
|
1853
|
-
const budgetScore = avgContextBudget <
|
|
1854
|
-
const depthScore = avgImportDepth <
|
|
1855
|
-
const fragmentationScore = avgFragmentation < 0.
|
|
1856
|
-
const criticalPenalty = criticalIssues *
|
|
1857
|
-
const majorPenalty = majorIssues *
|
|
1750
|
+
const budgetScore = avgContextBudget < 8e3 ? 100 : Math.max(0, 100 - (avgContextBudget - 8e3) / 200);
|
|
1751
|
+
const depthScore = avgImportDepth < 8 ? 100 : Math.max(0, 100 - (avgImportDepth - 8) * 5);
|
|
1752
|
+
const fragmentationScore = avgFragmentation < 0.5 ? 100 : Math.max(0, 100 - (avgFragmentation - 0.5) * 100);
|
|
1753
|
+
const criticalPenalty = Math.min(20, criticalIssues * 3);
|
|
1754
|
+
const majorPenalty = Math.min(15, majorIssues * 1);
|
|
1858
1755
|
const maxBudgetPenalty = maxContextBudget > 15e3 ? Math.min(20, (maxContextBudget - 15e3) / 500) : 0;
|
|
1859
|
-
|
|
1860
|
-
|
|
1756
|
+
let bonus = 0;
|
|
1757
|
+
if (criticalIssues === 0 && majorIssues === 0 && avgFragmentation < 0.2) {
|
|
1758
|
+
bonus = 5;
|
|
1759
|
+
}
|
|
1760
|
+
const rawScore = budgetScore * 0.35 + depthScore * 0.25 + fragmentationScore * 0.25 + bonus;
|
|
1761
|
+
const finalScore = rawScore - Math.min(30, criticalPenalty + majorPenalty) - maxBudgetPenalty;
|
|
1861
1762
|
const score = Math.max(0, Math.min(100, Math.round(finalScore)));
|
|
1862
1763
|
const factors = [
|
|
1863
1764
|
{
|
|
1864
1765
|
name: "Context Budget",
|
|
1865
|
-
impact: Math.round(budgetScore * 0.
|
|
1866
|
-
description: `Avg ${Math.round(avgContextBudget)} tokens per file ${avgContextBudget <
|
|
1766
|
+
impact: Math.round(budgetScore * 0.35 - 35),
|
|
1767
|
+
description: `Avg ${Math.round(avgContextBudget)} tokens per file ${avgContextBudget < 8e3 ? "(excellent)" : avgContextBudget < 12e3 ? "(acceptable)" : "(high)"}`
|
|
1867
1768
|
},
|
|
1868
1769
|
{
|
|
1869
1770
|
name: "Import Depth",
|
|
1870
|
-
impact: Math.round(depthScore * 0.
|
|
1871
|
-
description: `Avg ${avgImportDepth.toFixed(1)} levels ${avgImportDepth <
|
|
1771
|
+
impact: Math.round(depthScore * 0.25 - 25),
|
|
1772
|
+
description: `Avg ${avgImportDepth.toFixed(1)} levels ${avgImportDepth < 8 ? "(excellent)" : avgImportDepth < 12 ? "(acceptable)" : "(deep)"}`
|
|
1872
1773
|
},
|
|
1873
1774
|
{
|
|
1874
1775
|
name: "Fragmentation",
|
|
1875
|
-
impact: Math.round(fragmentationScore * 0.
|
|
1776
|
+
impact: Math.round(fragmentationScore * 0.25 - 25),
|
|
1876
1777
|
description: `${(avgFragmentation * 100).toFixed(0)}% fragmentation ${avgFragmentation < 0.3 ? "(well-organized)" : avgFragmentation < 0.5 ? "(moderate)" : "(high)"}`
|
|
1877
1778
|
}
|
|
1878
1779
|
];
|
|
1780
|
+
if (bonus > 0) {
|
|
1781
|
+
factors.push({
|
|
1782
|
+
name: "Well-Organized Codebase",
|
|
1783
|
+
impact: bonus,
|
|
1784
|
+
description: "No critical/major issues and low fragmentation"
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1879
1787
|
if (criticalIssues > 0) {
|
|
1880
1788
|
factors.push({
|
|
1881
1789
|
name: "Critical Issues",
|
|
@@ -1898,10 +1806,10 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1898
1806
|
});
|
|
1899
1807
|
}
|
|
1900
1808
|
const recommendations = [];
|
|
1901
|
-
if (avgContextBudget >
|
|
1809
|
+
if (avgContextBudget > 12e3) {
|
|
1902
1810
|
const estimatedImpact = Math.min(
|
|
1903
1811
|
15,
|
|
1904
|
-
Math.round((avgContextBudget -
|
|
1812
|
+
Math.round((avgContextBudget - 12e3) / 1e3)
|
|
1905
1813
|
);
|
|
1906
1814
|
recommendations.push({
|
|
1907
1815
|
action: "Reduce file dependencies to lower context requirements",
|
|
@@ -1909,12 +1817,15 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1909
1817
|
priority: "high"
|
|
1910
1818
|
});
|
|
1911
1819
|
}
|
|
1912
|
-
if (avgImportDepth >
|
|
1913
|
-
const estimatedImpact = Math.min(
|
|
1820
|
+
if (avgImportDepth > 10) {
|
|
1821
|
+
const estimatedImpact = Math.min(
|
|
1822
|
+
10,
|
|
1823
|
+
Math.round((avgImportDepth - 10) * 1.5)
|
|
1824
|
+
);
|
|
1914
1825
|
recommendations.push({
|
|
1915
1826
|
action: "Flatten import chains to reduce depth",
|
|
1916
1827
|
estimatedImpact,
|
|
1917
|
-
priority: avgImportDepth >
|
|
1828
|
+
priority: avgImportDepth > 15 ? "high" : "medium"
|
|
1918
1829
|
});
|
|
1919
1830
|
}
|
|
1920
1831
|
if (avgFragmentation > 0.5) {
|
|
@@ -1935,18 +1846,18 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1935
1846
|
priority: "high"
|
|
1936
1847
|
});
|
|
1937
1848
|
}
|
|
1938
|
-
const cfg = { ...
|
|
1939
|
-
const estimatedMonthlyCost = (0,
|
|
1940
|
-
avgContextBudget * (
|
|
1849
|
+
const cfg = { ...import_core8.DEFAULT_COST_CONFIG, ...costConfig };
|
|
1850
|
+
const estimatedMonthlyCost = (0, import_core8.calculateMonthlyCost)(
|
|
1851
|
+
avgContextBudget * (totalFiles || 1),
|
|
1941
1852
|
cfg
|
|
1942
1853
|
);
|
|
1943
1854
|
const issues = [
|
|
1944
1855
|
...Array(criticalIssues).fill({ severity: "critical" }),
|
|
1945
1856
|
...Array(majorIssues).fill({ severity: "major" })
|
|
1946
1857
|
];
|
|
1947
|
-
const productivityImpact = (0,
|
|
1858
|
+
const productivityImpact = (0, import_core8.calculateProductivityImpact)(issues);
|
|
1948
1859
|
return {
|
|
1949
|
-
toolName:
|
|
1860
|
+
toolName: import_core8.ToolName.ContextAnalyzer,
|
|
1950
1861
|
score,
|
|
1951
1862
|
rawMetrics: {
|
|
1952
1863
|
avgContextBudget: Math.round(avgContextBudget),
|
|
@@ -1973,7 +1884,7 @@ function mapScoreToRating(score) {
|
|
|
1973
1884
|
|
|
1974
1885
|
// src/provider.ts
|
|
1975
1886
|
var ContextAnalyzerProvider = {
|
|
1976
|
-
id:
|
|
1887
|
+
id: import_core9.ToolName.ContextAnalyzer,
|
|
1977
1888
|
alias: ["context", "fragmentation", "budget"],
|
|
1978
1889
|
async analyze(options) {
|
|
1979
1890
|
const results = await analyzeContext(options);
|
|
@@ -1982,7 +1893,7 @@ var ContextAnalyzerProvider = {
|
|
|
1982
1893
|
(r) => ({
|
|
1983
1894
|
fileName: r.file,
|
|
1984
1895
|
issues: r.issues.map((msg) => ({
|
|
1985
|
-
type:
|
|
1896
|
+
type: import_core9.IssueType.ContextFragmentation,
|
|
1986
1897
|
severity: r.severity,
|
|
1987
1898
|
message: msg,
|
|
1988
1899
|
location: { file: r.file, line: 1 },
|
|
@@ -1995,13 +1906,13 @@ var ContextAnalyzerProvider = {
|
|
|
1995
1906
|
}
|
|
1996
1907
|
})
|
|
1997
1908
|
);
|
|
1998
|
-
return
|
|
1909
|
+
return import_core9.SpokeOutputSchema.parse({
|
|
1999
1910
|
results: normalizedResults,
|
|
2000
1911
|
summary: {
|
|
2001
1912
|
...summary
|
|
2002
1913
|
},
|
|
2003
1914
|
metadata: {
|
|
2004
|
-
toolName:
|
|
1915
|
+
toolName: import_core9.ToolName.ContextAnalyzer,
|
|
2005
1916
|
version: "0.17.5",
|
|
2006
1917
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2007
1918
|
}
|
|
@@ -2015,9 +1926,9 @@ var ContextAnalyzerProvider = {
|
|
|
2015
1926
|
};
|
|
2016
1927
|
|
|
2017
1928
|
// src/defaults.ts
|
|
2018
|
-
var
|
|
1929
|
+
var import_core10 = require("@aiready/core");
|
|
2019
1930
|
async function getSmartDefaults(directory, userOptions) {
|
|
2020
|
-
const files = await (0,
|
|
1931
|
+
const files = await (0, import_core10.scanFiles)({
|
|
2021
1932
|
rootDir: directory,
|
|
2022
1933
|
include: userOptions.include,
|
|
2023
1934
|
exclude: userOptions.exclude
|
|
@@ -2061,8 +1972,329 @@ async function getSmartDefaults(directory, userOptions) {
|
|
|
2061
1972
|
};
|
|
2062
1973
|
}
|
|
2063
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
|
+
|
|
2064
2296
|
// src/index.ts
|
|
2065
|
-
|
|
2297
|
+
import_core11.ToolRegistry.register(ContextAnalyzerProvider);
|
|
2066
2298
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2067
2299
|
0 && (module.exports = {
|
|
2068
2300
|
Classification,
|
|
@@ -2070,7 +2302,6 @@ import_core10.ToolRegistry.register(ContextAnalyzerProvider);
|
|
|
2070
2302
|
adjustCohesionForClassification,
|
|
2071
2303
|
adjustFragmentationForClassification,
|
|
2072
2304
|
analyzeContext,
|
|
2073
|
-
analyzeIssues,
|
|
2074
2305
|
buildCoUsageMatrix,
|
|
2075
2306
|
buildDependencyGraph,
|
|
2076
2307
|
buildTypeGraph,
|
|
@@ -2087,10 +2318,12 @@ import_core10.ToolRegistry.register(ContextAnalyzerProvider);
|
|
|
2087
2318
|
classifyFile,
|
|
2088
2319
|
detectCircularDependencies,
|
|
2089
2320
|
detectModuleClusters,
|
|
2321
|
+
displayConsoleReport,
|
|
2090
2322
|
extractDomainKeywordsFromPaths,
|
|
2091
2323
|
extractExports,
|
|
2092
2324
|
findConsolidationCandidates,
|
|
2093
2325
|
findSemanticClusters,
|
|
2326
|
+
generateHTMLReport,
|
|
2094
2327
|
generateSummary,
|
|
2095
2328
|
getClassificationRecommendations,
|
|
2096
2329
|
getCoUsageData,
|
|
@@ -2109,5 +2342,6 @@ import_core10.ToolRegistry.register(ContextAnalyzerProvider);
|
|
|
2109
2342
|
isSessionFile,
|
|
2110
2343
|
isTypeDefinition,
|
|
2111
2344
|
isUtilityModule,
|
|
2112
|
-
mapScoreToRating
|
|
2345
|
+
mapScoreToRating,
|
|
2346
|
+
runInteractiveSetup
|
|
2113
2347
|
});
|