@aiready/context-analyzer 0.21.26 → 0.21.27
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/.turbo/turbo-build.log +32 -25
- package/.turbo/turbo-test.log +51 -87
- package/coverage/clover.xml +392 -1878
- package/coverage/coverage-final.json +15 -19
- package/coverage/index.html +48 -63
- package/coverage/src/analyzers/index.html +21 -21
- package/coverage/src/analyzers/python-context.ts.html +96 -96
- package/coverage/src/ast-utils.ts.html +34 -109
- package/coverage/src/classifier.ts.html +104 -104
- package/coverage/src/classify/classification-patterns.ts.html +1 -1
- package/coverage/src/classify/file-classifiers.ts.html +1 -1
- package/coverage/src/classify/index.html +1 -1
- package/coverage/src/cluster-detector.ts.html +72 -72
- package/coverage/src/defaults.ts.html +1 -1
- package/coverage/src/graph-builder.ts.html +131 -131
- package/coverage/src/index.html +101 -116
- package/coverage/src/index.ts.html +2 -2
- package/coverage/src/issue-analyzer.ts.html +32 -83
- package/coverage/src/mapper.ts.html +20 -2
- package/coverage/src/metrics.ts.html +127 -130
- package/coverage/src/orchestrator.ts.html +13 -13
- package/coverage/src/provider.ts.html +19 -19
- package/coverage/src/remediation.ts.html +59 -59
- package/coverage/src/report/console-report.ts.html +2 -2
- package/coverage/src/report/html-report.ts.html +60 -84
- package/coverage/src/report/index.html +7 -7
- package/coverage/src/report/interactive-setup.ts.html +1 -1
- package/coverage/src/scoring.ts.html +62 -62
- package/coverage/src/semantic/co-usage.ts.html +1 -1
- package/coverage/src/semantic/consolidation.ts.html +1 -1
- package/coverage/src/semantic/domain-inference.ts.html +1 -1
- package/coverage/src/semantic/index.html +1 -1
- package/coverage/src/semantic/type-graph.ts.html +1 -1
- package/coverage/src/summary.ts.html +67 -67
- package/coverage/src/types.ts.html +1 -1
- package/coverage/src/utils/dependency-graph-utils.ts.html +41 -41
- package/coverage/src/utils/index.html +21 -21
- package/coverage/src/utils/string-utils.ts.html +1 -1
- package/dist/chunk-22ZO4EKZ.mjs +1297 -0
- package/dist/chunk-4U4LDWGF.mjs +360 -0
- package/dist/chunk-BA7QGUHN.mjs +1722 -0
- package/dist/chunk-BCEZGRXI.mjs +1297 -0
- package/dist/chunk-BQCISA2F.mjs +91 -0
- package/dist/chunk-EMYD7NS6.mjs +137 -0
- package/dist/chunk-EWFR366Y.mjs +1740 -0
- package/dist/chunk-FO6YT6RG.mjs +1751 -0
- package/dist/chunk-J3SZQZNU.mjs +221 -0
- package/dist/chunk-OZE3FVZT.mjs +1089 -0
- package/dist/chunk-WHB7QI7N.mjs +91 -0
- package/dist/cli-action-CXIHOVAC.mjs +95 -0
- package/dist/cli-action-SA7SCYNV.mjs +95 -0
- package/dist/cli-action-YAJOJCXJ.mjs +95 -0
- package/dist/cli.js +688 -566
- package/dist/cli.mjs +4 -88
- package/dist/index.js +889 -773
- package/dist/index.mjs +21 -14
- package/dist/orchestrator-3L3NAZYP.mjs +10 -0
- package/dist/orchestrator-MONOZHVW.mjs +10 -0
- package/dist/orchestrator-ZR7JSKWI.mjs +10 -0
- package/dist/summary-7PZVW72O.mjs +7 -0
- package/dist/summary-LKUCJAIS.mjs +7 -0
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +1 -1
- package/src/__tests__/enhanced-cohesion.test.ts +4 -1
- package/src/__tests__/orchestrator.test.ts +19 -4
- package/src/__tests__/python-context.test.ts +6 -0
- package/src/__tests__/report/html-report.test.ts +8 -2
- package/src/ast-utils.ts +1 -26
- package/src/cli-definition.ts +4 -2
- package/src/issue-analyzer.ts +4 -19
- package/src/metrics.ts +1 -2
- package/src/provider.ts +4 -4
- package/src/report/html-report.ts +43 -59
- package/coverage/dist/chunk-64U3PNO3.mjs.html +0 -367
- package/coverage/dist/chunk-J3MUOWHC.mjs.html +0 -5326
- package/coverage/dist/index.html +0 -146
- package/coverage/dist/index.mjs.html +0 -1396
- package/coverage/src/analyzer.ts.html +0 -88
package/dist/index.js
CHANGED
|
@@ -30,6 +30,263 @@ 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/metrics.ts
|
|
34
|
+
function calculateEnhancedCohesion(exports2, filePath, options) {
|
|
35
|
+
if (exports2.length <= 1) return 1;
|
|
36
|
+
if (filePath && (0, import_core2.isTestFile)(filePath)) return 1;
|
|
37
|
+
const domains = exports2.map((e) => e.inferredDomain || "unknown");
|
|
38
|
+
const domainCounts = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const domain of domains)
|
|
40
|
+
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
|
|
41
|
+
if (domainCounts.size === 1 && domains[0] !== "unknown") {
|
|
42
|
+
if (!options?.weights) return 1;
|
|
43
|
+
}
|
|
44
|
+
const probs = Array.from(domainCounts.values()).map(
|
|
45
|
+
(count) => count / exports2.length
|
|
46
|
+
);
|
|
47
|
+
let domainEntropy = 0;
|
|
48
|
+
for (const prob of probs) {
|
|
49
|
+
if (prob > 0) domainEntropy -= prob * Math.log2(prob);
|
|
50
|
+
}
|
|
51
|
+
const maxEntropy = Math.log2(Math.max(2, domainCounts.size));
|
|
52
|
+
const domainScore = 1 - domainEntropy / maxEntropy;
|
|
53
|
+
let importScoreTotal = 0;
|
|
54
|
+
let pairsWithData = 0;
|
|
55
|
+
let anyImportData = false;
|
|
56
|
+
for (let i = 0; i < exports2.length; i++) {
|
|
57
|
+
for (let j = i + 1; j < exports2.length; j++) {
|
|
58
|
+
const exp1Imports = exports2[i].imports;
|
|
59
|
+
const exp2Imports = exports2[j].imports;
|
|
60
|
+
if (exp1Imports || exp2Imports) {
|
|
61
|
+
anyImportData = true;
|
|
62
|
+
const sim = (0, import_core2.calculateImportSimilarity)(
|
|
63
|
+
{ ...exports2[i], imports: exp1Imports || [] },
|
|
64
|
+
{ ...exports2[j], imports: exp2Imports || [] }
|
|
65
|
+
);
|
|
66
|
+
importScoreTotal += sim;
|
|
67
|
+
pairsWithData++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const avgImportScore = pairsWithData > 0 ? importScoreTotal / pairsWithData : 0;
|
|
72
|
+
let score = anyImportData ? domainScore * 0.4 + avgImportScore * 0.6 : domainScore;
|
|
73
|
+
if (anyImportData && score === 0 && domainScore === 0) {
|
|
74
|
+
score = 0.1;
|
|
75
|
+
}
|
|
76
|
+
let structuralScore = 0;
|
|
77
|
+
for (const exp of exports2) {
|
|
78
|
+
if (exp.dependencies && exp.dependencies.length > 0) {
|
|
79
|
+
structuralScore += 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (structuralScore > 0) {
|
|
83
|
+
score = Math.min(1, score + 0.1);
|
|
84
|
+
}
|
|
85
|
+
if (!options?.weights && !anyImportData && domainCounts.size === 1) return 1;
|
|
86
|
+
return score;
|
|
87
|
+
}
|
|
88
|
+
function calculateStructuralCohesionFromCoUsage(file, coUsageMatrix) {
|
|
89
|
+
if (!coUsageMatrix) return 1;
|
|
90
|
+
const coUsages = coUsageMatrix.get(file);
|
|
91
|
+
if (!coUsages || coUsages.size === 0) return 1;
|
|
92
|
+
let total = 0;
|
|
93
|
+
for (const count of coUsages.values()) total += count;
|
|
94
|
+
if (total === 0) return 1;
|
|
95
|
+
const probs = [];
|
|
96
|
+
for (const count of coUsages.values()) {
|
|
97
|
+
if (count > 0) probs.push(count / total);
|
|
98
|
+
}
|
|
99
|
+
if (probs.length <= 1) return 1;
|
|
100
|
+
let entropy = 0;
|
|
101
|
+
for (const prob of probs) {
|
|
102
|
+
entropy -= prob * Math.log2(prob);
|
|
103
|
+
}
|
|
104
|
+
const maxEntropy = Math.log2(probs.length);
|
|
105
|
+
return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
|
|
106
|
+
}
|
|
107
|
+
function calculateFragmentation(files, domain, options) {
|
|
108
|
+
if (files.length <= 1) return 0;
|
|
109
|
+
const directories = new Set(
|
|
110
|
+
files.map((file) => file.split("/").slice(0, -1).join("/"))
|
|
111
|
+
);
|
|
112
|
+
const uniqueDirs = directories.size;
|
|
113
|
+
let score = options?.useLogScale ? uniqueDirs <= 1 ? 0 : Math.log(uniqueDirs) / Math.log(options.logBase || Math.E) / (Math.log(files.length) / Math.log(options.logBase || Math.E)) : (uniqueDirs - 1) / (files.length - 1);
|
|
114
|
+
if (options?.sharedImportRatio && options.sharedImportRatio > 0.5) {
|
|
115
|
+
const discount = (options.sharedImportRatio - 0.5) * 0.4;
|
|
116
|
+
score = score * (1 - discount);
|
|
117
|
+
}
|
|
118
|
+
return score;
|
|
119
|
+
}
|
|
120
|
+
function calculatePathEntropy(files) {
|
|
121
|
+
if (!files || files.length === 0) return 0;
|
|
122
|
+
const dirCounts = /* @__PURE__ */ new Map();
|
|
123
|
+
for (const file of files) {
|
|
124
|
+
const dir = file.split("/").slice(0, -1).join("/") || ".";
|
|
125
|
+
dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1);
|
|
126
|
+
}
|
|
127
|
+
const counts = Array.from(dirCounts.values());
|
|
128
|
+
if (counts.length <= 1) return 0;
|
|
129
|
+
const total = counts.reduce((sum, value) => sum + value, 0);
|
|
130
|
+
let entropy = 0;
|
|
131
|
+
for (const count of counts) {
|
|
132
|
+
const prob = count / total;
|
|
133
|
+
entropy -= prob * Math.log2(prob);
|
|
134
|
+
}
|
|
135
|
+
const maxEntropy = Math.log2(counts.length);
|
|
136
|
+
return maxEntropy > 0 ? entropy / maxEntropy : 0;
|
|
137
|
+
}
|
|
138
|
+
function calculateDirectoryDistance(files) {
|
|
139
|
+
if (!files || files.length <= 1) return 0;
|
|
140
|
+
const pathSegments = (pathStr) => pathStr.split("/").filter(Boolean);
|
|
141
|
+
const commonAncestorDepth = (pathA, pathB) => {
|
|
142
|
+
const minLen = Math.min(pathA.length, pathB.length);
|
|
143
|
+
let i = 0;
|
|
144
|
+
while (i < minLen && pathA[i] === pathB[i]) i++;
|
|
145
|
+
return i;
|
|
146
|
+
};
|
|
147
|
+
let totalNormalized = 0;
|
|
148
|
+
let comparisons = 0;
|
|
149
|
+
for (let i = 0; i < files.length; i++) {
|
|
150
|
+
for (let j = i + 1; j < files.length; j++) {
|
|
151
|
+
const segA = pathSegments(files[i]);
|
|
152
|
+
const segB = pathSegments(files[j]);
|
|
153
|
+
const shared = commonAncestorDepth(segA, segB);
|
|
154
|
+
const maxDepth = Math.max(segA.length, segB.length);
|
|
155
|
+
totalNormalized += 1 - (maxDepth > 0 ? shared / maxDepth : 0);
|
|
156
|
+
comparisons++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return comparisons > 0 ? totalNormalized / comparisons : 0;
|
|
160
|
+
}
|
|
161
|
+
var import_core2;
|
|
162
|
+
var init_metrics = __esm({
|
|
163
|
+
"src/metrics.ts"() {
|
|
164
|
+
"use strict";
|
|
165
|
+
import_core2 = require("@aiready/core");
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// src/issue-analyzer.ts
|
|
170
|
+
function analyzeIssues(params) {
|
|
171
|
+
const {
|
|
172
|
+
file,
|
|
173
|
+
importDepth,
|
|
174
|
+
contextBudget,
|
|
175
|
+
cohesionScore,
|
|
176
|
+
fragmentationScore,
|
|
177
|
+
maxDepth,
|
|
178
|
+
maxContextBudget,
|
|
179
|
+
minCohesion,
|
|
180
|
+
maxFragmentation,
|
|
181
|
+
circularDeps
|
|
182
|
+
} = params;
|
|
183
|
+
const issues = [];
|
|
184
|
+
const recommendations = [];
|
|
185
|
+
let severity = import_core3.Severity.Info;
|
|
186
|
+
let potentialSavings = 0;
|
|
187
|
+
if (circularDeps.length > 0) {
|
|
188
|
+
severity = import_core3.Severity.Critical;
|
|
189
|
+
issues.push(`Part of ${circularDeps.length} circular dependency chain(s)`);
|
|
190
|
+
recommendations.push(
|
|
191
|
+
"Break circular dependencies by extracting interfaces or using dependency injection"
|
|
192
|
+
);
|
|
193
|
+
potentialSavings += contextBudget * 0.2;
|
|
194
|
+
}
|
|
195
|
+
if (importDepth > maxDepth * 1.5) {
|
|
196
|
+
severity = import_core3.Severity.Critical;
|
|
197
|
+
issues.push(`Import depth ${importDepth} exceeds limit by 50%`);
|
|
198
|
+
recommendations.push("Flatten dependency tree or use facade pattern");
|
|
199
|
+
potentialSavings += contextBudget * 0.3;
|
|
200
|
+
} else if (importDepth > maxDepth) {
|
|
201
|
+
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
202
|
+
issues.push(
|
|
203
|
+
`Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`
|
|
204
|
+
);
|
|
205
|
+
recommendations.push("Consider reducing dependency depth");
|
|
206
|
+
potentialSavings += contextBudget * 0.15;
|
|
207
|
+
}
|
|
208
|
+
if (contextBudget > maxContextBudget * 1.5) {
|
|
209
|
+
severity = import_core3.Severity.Critical;
|
|
210
|
+
issues.push(
|
|
211
|
+
`Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
|
|
212
|
+
);
|
|
213
|
+
recommendations.push(
|
|
214
|
+
"Split into smaller modules or reduce dependency tree"
|
|
215
|
+
);
|
|
216
|
+
potentialSavings += contextBudget * 0.4;
|
|
217
|
+
} else if (contextBudget > maxContextBudget) {
|
|
218
|
+
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
219
|
+
issues.push(
|
|
220
|
+
`Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
|
|
221
|
+
);
|
|
222
|
+
recommendations.push("Reduce file size or dependencies");
|
|
223
|
+
potentialSavings += contextBudget * 0.2;
|
|
224
|
+
}
|
|
225
|
+
if (cohesionScore < minCohesion * 0.5) {
|
|
226
|
+
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
227
|
+
issues.push(
|
|
228
|
+
`Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`
|
|
229
|
+
);
|
|
230
|
+
recommendations.push(
|
|
231
|
+
"Split file by domain - separate unrelated functionality"
|
|
232
|
+
);
|
|
233
|
+
potentialSavings += contextBudget * 0.25;
|
|
234
|
+
} else if (cohesionScore < minCohesion) {
|
|
235
|
+
if (severity === import_core3.Severity.Info) severity = import_core3.Severity.Minor;
|
|
236
|
+
issues.push(`Low cohesion (${(cohesionScore * 100).toFixed(0)}%)`);
|
|
237
|
+
recommendations.push("Consider grouping related exports together");
|
|
238
|
+
potentialSavings += contextBudget * 0.1;
|
|
239
|
+
}
|
|
240
|
+
if (fragmentationScore > maxFragmentation) {
|
|
241
|
+
if (severity === import_core3.Severity.Info || severity === import_core3.Severity.Minor)
|
|
242
|
+
severity = import_core3.Severity.Minor;
|
|
243
|
+
issues.push(
|
|
244
|
+
`High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`
|
|
245
|
+
);
|
|
246
|
+
recommendations.push("Consolidate with related files in same domain");
|
|
247
|
+
potentialSavings += contextBudget * 0.3;
|
|
248
|
+
}
|
|
249
|
+
if ((0, import_core3.isBuildArtifact)(file)) {
|
|
250
|
+
issues.push("Detected build artifact (bundled/output file)");
|
|
251
|
+
recommendations.push("Exclude build outputs from analysis");
|
|
252
|
+
severity = import_core3.Severity.Info;
|
|
253
|
+
potentialSavings = 0;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
severity,
|
|
257
|
+
issues,
|
|
258
|
+
recommendations,
|
|
259
|
+
potentialSavings: Math.floor(potentialSavings)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
var import_core3;
|
|
263
|
+
var init_issue_analyzer = __esm({
|
|
264
|
+
"src/issue-analyzer.ts"() {
|
|
265
|
+
"use strict";
|
|
266
|
+
import_core3 = require("@aiready/core");
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// src/utils/string-utils.ts
|
|
271
|
+
function singularize(word) {
|
|
272
|
+
const irregulars = {
|
|
273
|
+
people: "person",
|
|
274
|
+
children: "child",
|
|
275
|
+
men: "man",
|
|
276
|
+
women: "woman"
|
|
277
|
+
};
|
|
278
|
+
if (irregulars[word]) return irregulars[word];
|
|
279
|
+
if (word.endsWith("ies")) return word.slice(0, -3) + "y";
|
|
280
|
+
if (word.endsWith("ses")) return word.slice(0, -2);
|
|
281
|
+
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
282
|
+
return word;
|
|
283
|
+
}
|
|
284
|
+
var init_string_utils = __esm({
|
|
285
|
+
"src/utils/string-utils.ts"() {
|
|
286
|
+
"use strict";
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
33
290
|
// src/utils/dependency-graph-utils.ts
|
|
34
291
|
function calculateImportDepthFromEdges(file, edges, visited = /* @__PURE__ */ new Set(), depth = 0) {
|
|
35
292
|
if (visited.has(file)) return depth;
|
|
@@ -123,264 +380,77 @@ var init_dependency_graph_utils = __esm({
|
|
|
123
380
|
}
|
|
124
381
|
});
|
|
125
382
|
|
|
126
|
-
// src/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const dependencyGraph = await buildPythonDependencyGraph(
|
|
142
|
-
pythonFiles,
|
|
143
|
-
rootDir
|
|
144
|
-
);
|
|
145
|
-
for (const file of pythonFiles) {
|
|
146
|
-
try {
|
|
147
|
-
const code = await import_fs.default.promises.readFile(file, "utf-8");
|
|
148
|
-
const result = parser.parse(code, file);
|
|
149
|
-
const imports = result.imports.map((imp) => ({
|
|
150
|
-
source: imp.source,
|
|
151
|
-
specifiers: imp.specifiers,
|
|
152
|
-
isRelative: imp.source.startsWith("."),
|
|
153
|
-
resolvedPath: resolvePythonImport(file, imp.source, rootDir)
|
|
154
|
-
}));
|
|
155
|
-
const exports2 = result.exports.map((exp) => ({
|
|
156
|
-
name: exp.name,
|
|
157
|
-
type: exp.type
|
|
158
|
-
}));
|
|
159
|
-
const linesOfCode = code.split("\n").length;
|
|
160
|
-
const importDepth = calculateImportDepthFromEdges(
|
|
161
|
-
file,
|
|
162
|
-
dependencyGraph,
|
|
163
|
-
/* @__PURE__ */ new Set()
|
|
164
|
-
);
|
|
165
|
-
const contextBudget = estimateContextBudget(
|
|
166
|
-
code,
|
|
167
|
-
imports,
|
|
168
|
-
dependencyGraph
|
|
169
|
-
);
|
|
170
|
-
const cohesion = calculatePythonCohesion(exports2, imports);
|
|
171
|
-
const circularDependencies = detectGraphCyclesFromFile(
|
|
172
|
-
file,
|
|
173
|
-
dependencyGraph
|
|
174
|
-
).map((cycle) => cycle.join(" -> "));
|
|
175
|
-
results.push({
|
|
176
|
-
file,
|
|
177
|
-
importDepth,
|
|
178
|
-
contextBudget,
|
|
179
|
-
cohesion,
|
|
180
|
-
imports,
|
|
181
|
-
exports: exports2,
|
|
182
|
-
metrics: {
|
|
183
|
-
linesOfCode,
|
|
184
|
-
importCount: imports.length,
|
|
185
|
-
exportCount: exports2.length,
|
|
186
|
-
circularDependencies
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.warn(`Failed to analyze ${file}:`, error);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return results;
|
|
194
|
-
}
|
|
195
|
-
async function buildPythonDependencyGraph(files, rootDir) {
|
|
196
|
-
const graph = /* @__PURE__ */ new Map();
|
|
197
|
-
const parser = await (0, import_core5.getParser)("dummy.py");
|
|
198
|
-
if (!parser) return graph;
|
|
199
|
-
for (const file of files) {
|
|
200
|
-
try {
|
|
201
|
-
const code = await import_fs.default.promises.readFile(file, "utf-8");
|
|
202
|
-
const result = parser.parse(code, file);
|
|
203
|
-
const dependencies = /* @__PURE__ */ new Set();
|
|
204
|
-
for (const imp of result.imports) {
|
|
205
|
-
const resolved = resolvePythonImport(file, imp.source, rootDir);
|
|
206
|
-
if (resolved && files.includes(resolved)) {
|
|
207
|
-
dependencies.add(resolved);
|
|
208
|
-
}
|
|
383
|
+
// src/semantic/co-usage.ts
|
|
384
|
+
function buildCoUsageMatrix(graph) {
|
|
385
|
+
const coUsageMatrix = /* @__PURE__ */ new Map();
|
|
386
|
+
for (const [, node] of graph.nodes) {
|
|
387
|
+
const imports = node.imports;
|
|
388
|
+
for (let i = 0; i < imports.length; i++) {
|
|
389
|
+
const fileA = imports[i];
|
|
390
|
+
if (!coUsageMatrix.has(fileA)) coUsageMatrix.set(fileA, /* @__PURE__ */ new Map());
|
|
391
|
+
for (let j = i + 1; j < imports.length; j++) {
|
|
392
|
+
const fileB = imports[j];
|
|
393
|
+
const fileAUsage = coUsageMatrix.get(fileA);
|
|
394
|
+
fileAUsage.set(fileB, (fileAUsage.get(fileB) || 0) + 1);
|
|
395
|
+
if (!coUsageMatrix.has(fileB)) coUsageMatrix.set(fileB, /* @__PURE__ */ new Map());
|
|
396
|
+
const fileBUsage = coUsageMatrix.get(fileB);
|
|
397
|
+
fileBUsage.set(fileA, (fileBUsage.get(fileA) || 0) + 1);
|
|
209
398
|
}
|
|
210
|
-
graph.set(file, dependencies);
|
|
211
|
-
} catch (error) {
|
|
212
|
-
void error;
|
|
213
399
|
}
|
|
214
400
|
}
|
|
215
|
-
return
|
|
401
|
+
return coUsageMatrix;
|
|
216
402
|
}
|
|
217
|
-
function
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
targetDir = (0, import_path2.dirname)(targetDir);
|
|
229
|
-
}
|
|
230
|
-
const modulePath = parts.join("/");
|
|
231
|
-
const possiblePaths = [
|
|
232
|
-
(0, import_path2.resolve)(targetDir, `${modulePath}.py`),
|
|
233
|
-
(0, import_path2.resolve)(targetDir, modulePath, "__init__.py")
|
|
234
|
-
];
|
|
235
|
-
for (const path of possiblePaths) {
|
|
236
|
-
if (import_fs.default.existsSync(path)) {
|
|
237
|
-
return path;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
} else {
|
|
241
|
-
const modulePath = importPath.replace(/\./g, "/");
|
|
242
|
-
const possiblePaths = [
|
|
243
|
-
(0, import_path2.resolve)(rootDir, `${modulePath}.py`),
|
|
244
|
-
(0, import_path2.resolve)(rootDir, modulePath, "__init__.py")
|
|
245
|
-
];
|
|
246
|
-
for (const path of possiblePaths) {
|
|
247
|
-
if (import_fs.default.existsSync(path)) {
|
|
248
|
-
return path;
|
|
403
|
+
function findSemanticClusters(coUsageMatrix, minCoUsage = 3) {
|
|
404
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
405
|
+
const visited = /* @__PURE__ */ new Set();
|
|
406
|
+
for (const [file, coUsages] of coUsageMatrix) {
|
|
407
|
+
if (visited.has(file)) continue;
|
|
408
|
+
const cluster = [file];
|
|
409
|
+
visited.add(file);
|
|
410
|
+
for (const [relatedFile, count] of coUsages) {
|
|
411
|
+
if (count >= minCoUsage && !visited.has(relatedFile)) {
|
|
412
|
+
cluster.push(relatedFile);
|
|
413
|
+
visited.add(relatedFile);
|
|
249
414
|
}
|
|
250
415
|
}
|
|
416
|
+
if (cluster.length > 1) clusters.set(file, cluster);
|
|
251
417
|
}
|
|
252
|
-
return
|
|
418
|
+
return clusters;
|
|
253
419
|
}
|
|
254
|
-
function
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
420
|
+
function getCoUsageData(file, coUsageMatrix) {
|
|
421
|
+
return {
|
|
422
|
+
file,
|
|
423
|
+
coImportedWith: coUsageMatrix.get(file) || /* @__PURE__ */ new Map(),
|
|
424
|
+
sharedImporters: []
|
|
425
|
+
};
|
|
260
426
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const importCount = imports.length;
|
|
265
|
-
let cohesion = 1;
|
|
266
|
-
if (exportCount > 10) {
|
|
267
|
-
cohesion *= 0.6;
|
|
268
|
-
} else if (exportCount > 5) {
|
|
269
|
-
cohesion *= 0.8;
|
|
427
|
+
var init_co_usage = __esm({
|
|
428
|
+
"src/semantic/co-usage.ts"() {
|
|
429
|
+
"use strict";
|
|
270
430
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// src/semantic/type-graph.ts
|
|
434
|
+
function buildTypeGraph(graph) {
|
|
435
|
+
const typeGraph = /* @__PURE__ */ new Map();
|
|
436
|
+
for (const [file, node] of graph.nodes) {
|
|
437
|
+
for (const exp of node.exports) {
|
|
438
|
+
if (exp.typeReferences) {
|
|
439
|
+
for (const typeRef of exp.typeReferences) {
|
|
440
|
+
if (!typeGraph.has(typeRef)) typeGraph.set(typeRef, /* @__PURE__ */ new Set());
|
|
441
|
+
typeGraph.get(typeRef).add(file);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
277
444
|
}
|
|
278
445
|
}
|
|
279
|
-
return
|
|
446
|
+
return typeGraph;
|
|
280
447
|
}
|
|
281
|
-
var
|
|
282
|
-
|
|
283
|
-
"src/analyzers/python-context.ts"() {
|
|
448
|
+
var init_type_graph = __esm({
|
|
449
|
+
"src/semantic/type-graph.ts"() {
|
|
284
450
|
"use strict";
|
|
285
|
-
import_core5 = require("@aiready/core");
|
|
286
|
-
import_path2 = require("path");
|
|
287
|
-
import_fs = __toESM(require("fs"));
|
|
288
|
-
init_dependency_graph_utils();
|
|
289
451
|
}
|
|
290
452
|
});
|
|
291
453
|
|
|
292
|
-
// src/index.ts
|
|
293
|
-
var index_exports = {};
|
|
294
|
-
__export(index_exports, {
|
|
295
|
-
BARREL_EXPORT_MIN_EXPORTS: () => BARREL_EXPORT_MIN_EXPORTS,
|
|
296
|
-
BARREL_EXPORT_TOKEN_LIMIT: () => BARREL_EXPORT_TOKEN_LIMIT,
|
|
297
|
-
CONFIG_NAME_PATTERNS: () => CONFIG_NAME_PATTERNS,
|
|
298
|
-
Classification: () => Classification,
|
|
299
|
-
ContextAnalyzerProvider: () => ContextAnalyzerProvider,
|
|
300
|
-
EMAIL_NAME_PATTERNS: () => EMAIL_NAME_PATTERNS,
|
|
301
|
-
HANDLER_NAME_PATTERNS: () => HANDLER_NAME_PATTERNS,
|
|
302
|
-
NEXTJS_METADATA_EXPORTS: () => NEXTJS_METADATA_EXPORTS,
|
|
303
|
-
PARSER_NAME_PATTERNS: () => PARSER_NAME_PATTERNS,
|
|
304
|
-
SERVICE_NAME_PATTERNS: () => SERVICE_NAME_PATTERNS,
|
|
305
|
-
SESSION_NAME_PATTERNS: () => SESSION_NAME_PATTERNS,
|
|
306
|
-
adjustCohesionForClassification: () => adjustCohesionForClassification,
|
|
307
|
-
adjustFragmentationForClassification: () => adjustFragmentationForClassification,
|
|
308
|
-
analyzeContext: () => analyzeContext,
|
|
309
|
-
buildCoUsageMatrix: () => buildCoUsageMatrix,
|
|
310
|
-
buildDependencyGraph: () => buildDependencyGraph,
|
|
311
|
-
buildTypeGraph: () => buildTypeGraph,
|
|
312
|
-
calculateCohesion: () => calculateCohesion,
|
|
313
|
-
calculateContextBudget: () => calculateContextBudget,
|
|
314
|
-
calculateContextScore: () => calculateContextScore,
|
|
315
|
-
calculateDirectoryDistance: () => calculateDirectoryDistance,
|
|
316
|
-
calculateDomainConfidence: () => calculateDomainConfidence,
|
|
317
|
-
calculateEnhancedCohesion: () => calculateEnhancedCohesion,
|
|
318
|
-
calculateFragmentation: () => calculateFragmentation,
|
|
319
|
-
calculateImportDepth: () => calculateImportDepth,
|
|
320
|
-
calculatePathEntropy: () => calculatePathEntropy,
|
|
321
|
-
calculateStructuralCohesionFromCoUsage: () => calculateStructuralCohesionFromCoUsage,
|
|
322
|
-
classifyFile: () => classifyFile,
|
|
323
|
-
detectCircularDependencies: () => detectCircularDependencies,
|
|
324
|
-
detectModuleClusters: () => detectModuleClusters,
|
|
325
|
-
displayConsoleReport: () => displayConsoleReport,
|
|
326
|
-
extractDomainKeywordsFromPaths: () => extractDomainKeywordsFromPaths,
|
|
327
|
-
extractExports: () => extractExports,
|
|
328
|
-
findConsolidationCandidates: () => findConsolidationCandidates,
|
|
329
|
-
findSemanticClusters: () => findSemanticClusters,
|
|
330
|
-
generateHTMLReport: () => generateHTMLReport,
|
|
331
|
-
generateSummary: () => generateSummary,
|
|
332
|
-
getClassificationRecommendations: () => getClassificationRecommendations,
|
|
333
|
-
getCoUsageData: () => getCoUsageData,
|
|
334
|
-
getGeneralRecommendations: () => getGeneralRecommendations,
|
|
335
|
-
getSmartDefaults: () => getSmartDefaults,
|
|
336
|
-
getTransitiveDependencies: () => getTransitiveDependencies,
|
|
337
|
-
inferDomain: () => inferDomain,
|
|
338
|
-
inferDomainFromSemantics: () => inferDomainFromSemantics,
|
|
339
|
-
isBarrelExport: () => isBarrelExport,
|
|
340
|
-
isBoilerplateBarrel: () => isBoilerplateBarrel,
|
|
341
|
-
isConfigFile: () => isConfigFile,
|
|
342
|
-
isEmailTemplate: () => isEmailTemplate,
|
|
343
|
-
isHubAndSpokeFile: () => isHubAndSpokeFile,
|
|
344
|
-
isLambdaHandler: () => isLambdaHandler,
|
|
345
|
-
isNextJsPage: () => isNextJsPage,
|
|
346
|
-
isParserFile: () => isParserFile,
|
|
347
|
-
isServiceFile: () => isServiceFile,
|
|
348
|
-
isSessionFile: () => isSessionFile,
|
|
349
|
-
isTypeDefinition: () => isTypeDefinition,
|
|
350
|
-
isUtilityModule: () => isUtilityModule,
|
|
351
|
-
mapScoreToRating: () => mapScoreToRating,
|
|
352
|
-
runInteractiveSetup: () => runInteractiveSetup
|
|
353
|
-
});
|
|
354
|
-
module.exports = __toCommonJS(index_exports);
|
|
355
|
-
var import_core12 = require("@aiready/core");
|
|
356
|
-
|
|
357
|
-
// src/provider.ts
|
|
358
|
-
var import_core9 = require("@aiready/core");
|
|
359
|
-
|
|
360
|
-
// src/orchestrator.ts
|
|
361
|
-
var import_core6 = require("@aiready/core");
|
|
362
|
-
|
|
363
|
-
// src/metrics.ts
|
|
364
|
-
var import_core2 = require("@aiready/core");
|
|
365
|
-
|
|
366
|
-
// src/ast-utils.ts
|
|
367
|
-
var import_core = require("@aiready/core");
|
|
368
|
-
|
|
369
|
-
// src/utils/string-utils.ts
|
|
370
|
-
function singularize(word) {
|
|
371
|
-
const irregulars = {
|
|
372
|
-
people: "person",
|
|
373
|
-
children: "child",
|
|
374
|
-
men: "man",
|
|
375
|
-
women: "woman"
|
|
376
|
-
};
|
|
377
|
-
if (irregulars[word]) return irregulars[word];
|
|
378
|
-
if (word.endsWith("ies")) return word.slice(0, -3) + "y";
|
|
379
|
-
if (word.endsWith("ses")) return word.slice(0, -2);
|
|
380
|
-
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
381
|
-
return word;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
454
|
// src/semantic/domain-inference.ts
|
|
385
455
|
function calculateDomainConfidence(signals) {
|
|
386
456
|
const weights = {
|
|
@@ -544,337 +614,53 @@ function inferDomain(name, filePath, domainOptions, fileImports) {
|
|
|
544
614
|
const segLower = segment.toLowerCase();
|
|
545
615
|
const singularSegment = singularize(segLower);
|
|
546
616
|
for (const keyword of domainKeywords) {
|
|
547
|
-
if (singularSegment === keyword || segLower === keyword) return keyword;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return "unknown";
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// src/ast-utils.ts
|
|
555
|
-
async function extractExportsWithAST(content, filePath, domainOptions, fileImports) {
|
|
556
|
-
try {
|
|
557
|
-
const { exports: astExports } = await (0, import_core.parseFileExports)(content, filePath);
|
|
558
|
-
if (astExports.length === 0 && !isTestFile(filePath)) {
|
|
559
|
-
return extractExports(content, filePath, domainOptions, fileImports);
|
|
560
|
-
}
|
|
561
|
-
return astExports.map((exp) => ({
|
|
562
|
-
name: exp.name,
|
|
563
|
-
type: exp.type,
|
|
564
|
-
inferredDomain: inferDomain(
|
|
565
|
-
exp.name,
|
|
566
|
-
filePath,
|
|
567
|
-
domainOptions,
|
|
568
|
-
fileImports
|
|
569
|
-
),
|
|
570
|
-
imports: exp.imports,
|
|
571
|
-
dependencies: exp.dependencies,
|
|
572
|
-
typeReferences: exp.typeReferences
|
|
573
|
-
}));
|
|
574
|
-
} catch {
|
|
575
|
-
return extractExports(content, filePath, domainOptions, fileImports);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
function isTestFile(filePath) {
|
|
579
|
-
const lower = filePath.toLowerCase();
|
|
580
|
-
return lower.includes(".test.") || lower.includes(".spec.") || lower.includes("/__tests__/") || lower.includes("/tests/") || lower.includes("/test/") || lower.includes("test-") || lower.includes("-test") || lower.includes("/__mocks__/") || lower.includes("/mocks/") || lower.includes("/fixtures/") || lower.includes(".mock.") || lower.includes(".fixture.") || lower.includes("/test-utils/");
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// src/metrics.ts
|
|
584
|
-
function calculateEnhancedCohesion(exports2, filePath, options) {
|
|
585
|
-
if (exports2.length <= 1) return 1;
|
|
586
|
-
if (filePath && isTestFile(filePath)) return 1;
|
|
587
|
-
const domains = exports2.map((e) => e.inferredDomain || "unknown");
|
|
588
|
-
const domainCounts = /* @__PURE__ */ new Map();
|
|
589
|
-
for (const domain of domains)
|
|
590
|
-
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
|
|
591
|
-
if (domainCounts.size === 1 && domains[0] !== "unknown") {
|
|
592
|
-
if (!options?.weights) return 1;
|
|
593
|
-
}
|
|
594
|
-
const probs = Array.from(domainCounts.values()).map(
|
|
595
|
-
(count) => count / exports2.length
|
|
596
|
-
);
|
|
597
|
-
let domainEntropy = 0;
|
|
598
|
-
for (const prob of probs) {
|
|
599
|
-
if (prob > 0) domainEntropy -= prob * Math.log2(prob);
|
|
600
|
-
}
|
|
601
|
-
const maxEntropy = Math.log2(Math.max(2, domainCounts.size));
|
|
602
|
-
const domainScore = 1 - domainEntropy / maxEntropy;
|
|
603
|
-
let importScoreTotal = 0;
|
|
604
|
-
let pairsWithData = 0;
|
|
605
|
-
let anyImportData = false;
|
|
606
|
-
for (let i = 0; i < exports2.length; i++) {
|
|
607
|
-
for (let j = i + 1; j < exports2.length; j++) {
|
|
608
|
-
const exp1Imports = exports2[i].imports;
|
|
609
|
-
const exp2Imports = exports2[j].imports;
|
|
610
|
-
if (exp1Imports || exp2Imports) {
|
|
611
|
-
anyImportData = true;
|
|
612
|
-
const sim = (0, import_core2.calculateImportSimilarity)(
|
|
613
|
-
{ ...exports2[i], imports: exp1Imports || [] },
|
|
614
|
-
{ ...exports2[j], imports: exp2Imports || [] }
|
|
615
|
-
);
|
|
616
|
-
importScoreTotal += sim;
|
|
617
|
-
pairsWithData++;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
const avgImportScore = pairsWithData > 0 ? importScoreTotal / pairsWithData : 0;
|
|
622
|
-
let score = anyImportData ? domainScore * 0.4 + avgImportScore * 0.6 : domainScore;
|
|
623
|
-
if (anyImportData && score === 0 && domainScore === 0) {
|
|
624
|
-
score = 0.1;
|
|
625
|
-
}
|
|
626
|
-
let structuralScore = 0;
|
|
627
|
-
for (const exp of exports2) {
|
|
628
|
-
if (exp.dependencies && exp.dependencies.length > 0) {
|
|
629
|
-
structuralScore += 1;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
if (structuralScore > 0) {
|
|
633
|
-
score = Math.min(1, score + 0.1);
|
|
634
|
-
}
|
|
635
|
-
if (!options?.weights && !anyImportData && domainCounts.size === 1) return 1;
|
|
636
|
-
return score;
|
|
637
|
-
}
|
|
638
|
-
function calculateStructuralCohesionFromCoUsage(file, coUsageMatrix) {
|
|
639
|
-
if (!coUsageMatrix) return 1;
|
|
640
|
-
const coUsages = coUsageMatrix.get(file);
|
|
641
|
-
if (!coUsages || coUsages.size === 0) return 1;
|
|
642
|
-
let total = 0;
|
|
643
|
-
for (const count of coUsages.values()) total += count;
|
|
644
|
-
if (total === 0) return 1;
|
|
645
|
-
const probs = [];
|
|
646
|
-
for (const count of coUsages.values()) {
|
|
647
|
-
if (count > 0) probs.push(count / total);
|
|
648
|
-
}
|
|
649
|
-
if (probs.length <= 1) return 1;
|
|
650
|
-
let entropy = 0;
|
|
651
|
-
for (const prob of probs) {
|
|
652
|
-
entropy -= prob * Math.log2(prob);
|
|
653
|
-
}
|
|
654
|
-
const maxEntropy = Math.log2(probs.length);
|
|
655
|
-
return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
|
|
656
|
-
}
|
|
657
|
-
function calculateFragmentation(files, domain, options) {
|
|
658
|
-
if (files.length <= 1) return 0;
|
|
659
|
-
const directories = new Set(
|
|
660
|
-
files.map((file) => file.split("/").slice(0, -1).join("/"))
|
|
661
|
-
);
|
|
662
|
-
const uniqueDirs = directories.size;
|
|
663
|
-
let score = options?.useLogScale ? uniqueDirs <= 1 ? 0 : Math.log(uniqueDirs) / Math.log(options.logBase || Math.E) / (Math.log(files.length) / Math.log(options.logBase || Math.E)) : (uniqueDirs - 1) / (files.length - 1);
|
|
664
|
-
if (options?.sharedImportRatio && options.sharedImportRatio > 0.5) {
|
|
665
|
-
const discount = (options.sharedImportRatio - 0.5) * 0.4;
|
|
666
|
-
score = score * (1 - discount);
|
|
667
|
-
}
|
|
668
|
-
return score;
|
|
669
|
-
}
|
|
670
|
-
function calculatePathEntropy(files) {
|
|
671
|
-
if (!files || files.length === 0) return 0;
|
|
672
|
-
const dirCounts = /* @__PURE__ */ new Map();
|
|
673
|
-
for (const file of files) {
|
|
674
|
-
const dir = file.split("/").slice(0, -1).join("/") || ".";
|
|
675
|
-
dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1);
|
|
676
|
-
}
|
|
677
|
-
const counts = Array.from(dirCounts.values());
|
|
678
|
-
if (counts.length <= 1) return 0;
|
|
679
|
-
const total = counts.reduce((sum, value) => sum + value, 0);
|
|
680
|
-
let entropy = 0;
|
|
681
|
-
for (const count of counts) {
|
|
682
|
-
const prob = count / total;
|
|
683
|
-
entropy -= prob * Math.log2(prob);
|
|
684
|
-
}
|
|
685
|
-
const maxEntropy = Math.log2(counts.length);
|
|
686
|
-
return maxEntropy > 0 ? entropy / maxEntropy : 0;
|
|
687
|
-
}
|
|
688
|
-
function calculateDirectoryDistance(files) {
|
|
689
|
-
if (!files || files.length <= 1) return 0;
|
|
690
|
-
const pathSegments = (pathStr) => pathStr.split("/").filter(Boolean);
|
|
691
|
-
const commonAncestorDepth = (pathA, pathB) => {
|
|
692
|
-
const minLen = Math.min(pathA.length, pathB.length);
|
|
693
|
-
let i = 0;
|
|
694
|
-
while (i < minLen && pathA[i] === pathB[i]) i++;
|
|
695
|
-
return i;
|
|
696
|
-
};
|
|
697
|
-
let totalNormalized = 0;
|
|
698
|
-
let comparisons = 0;
|
|
699
|
-
for (let i = 0; i < files.length; i++) {
|
|
700
|
-
for (let j = i + 1; j < files.length; j++) {
|
|
701
|
-
const segA = pathSegments(files[i]);
|
|
702
|
-
const segB = pathSegments(files[j]);
|
|
703
|
-
const shared = commonAncestorDepth(segA, segB);
|
|
704
|
-
const maxDepth = Math.max(segA.length, segB.length);
|
|
705
|
-
totalNormalized += 1 - (maxDepth > 0 ? shared / maxDepth : 0);
|
|
706
|
-
comparisons++;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
return comparisons > 0 ? totalNormalized / comparisons : 0;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// src/issue-analyzer.ts
|
|
713
|
-
var import_core3 = require("@aiready/core");
|
|
714
|
-
function analyzeIssues(params) {
|
|
715
|
-
const {
|
|
716
|
-
file,
|
|
717
|
-
importDepth,
|
|
718
|
-
contextBudget,
|
|
719
|
-
cohesionScore,
|
|
720
|
-
fragmentationScore,
|
|
721
|
-
maxDepth,
|
|
722
|
-
maxContextBudget,
|
|
723
|
-
minCohesion,
|
|
724
|
-
maxFragmentation,
|
|
725
|
-
circularDeps
|
|
726
|
-
} = params;
|
|
727
|
-
const issues = [];
|
|
728
|
-
const recommendations = [];
|
|
729
|
-
let severity = import_core3.Severity.Info;
|
|
730
|
-
let potentialSavings = 0;
|
|
731
|
-
if (circularDeps.length > 0) {
|
|
732
|
-
severity = import_core3.Severity.Critical;
|
|
733
|
-
issues.push(`Part of ${circularDeps.length} circular dependency chain(s)`);
|
|
734
|
-
recommendations.push(
|
|
735
|
-
"Break circular dependencies by extracting interfaces or using dependency injection"
|
|
736
|
-
);
|
|
737
|
-
potentialSavings += contextBudget * 0.2;
|
|
738
|
-
}
|
|
739
|
-
if (importDepth > maxDepth * 1.5) {
|
|
740
|
-
severity = import_core3.Severity.Critical;
|
|
741
|
-
issues.push(`Import depth ${importDepth} exceeds limit by 50%`);
|
|
742
|
-
recommendations.push("Flatten dependency tree or use facade pattern");
|
|
743
|
-
potentialSavings += contextBudget * 0.3;
|
|
744
|
-
} else if (importDepth > maxDepth) {
|
|
745
|
-
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
746
|
-
issues.push(
|
|
747
|
-
`Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`
|
|
748
|
-
);
|
|
749
|
-
recommendations.push("Consider reducing dependency depth");
|
|
750
|
-
potentialSavings += contextBudget * 0.15;
|
|
751
|
-
}
|
|
752
|
-
if (contextBudget > maxContextBudget * 1.5) {
|
|
753
|
-
severity = import_core3.Severity.Critical;
|
|
754
|
-
issues.push(
|
|
755
|
-
`Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
|
|
756
|
-
);
|
|
757
|
-
recommendations.push(
|
|
758
|
-
"Split into smaller modules or reduce dependency tree"
|
|
759
|
-
);
|
|
760
|
-
potentialSavings += contextBudget * 0.4;
|
|
761
|
-
} else if (contextBudget > maxContextBudget) {
|
|
762
|
-
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
763
|
-
issues.push(
|
|
764
|
-
`Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
|
|
765
|
-
);
|
|
766
|
-
recommendations.push("Reduce file size or dependencies");
|
|
767
|
-
potentialSavings += contextBudget * 0.2;
|
|
768
|
-
}
|
|
769
|
-
if (cohesionScore < minCohesion * 0.5) {
|
|
770
|
-
if (severity !== import_core3.Severity.Critical) severity = import_core3.Severity.Major;
|
|
771
|
-
issues.push(
|
|
772
|
-
`Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`
|
|
773
|
-
);
|
|
774
|
-
recommendations.push(
|
|
775
|
-
"Split file by domain - separate unrelated functionality"
|
|
776
|
-
);
|
|
777
|
-
potentialSavings += contextBudget * 0.25;
|
|
778
|
-
} else if (cohesionScore < minCohesion) {
|
|
779
|
-
if (severity === import_core3.Severity.Info) severity = import_core3.Severity.Minor;
|
|
780
|
-
issues.push(`Low cohesion (${(cohesionScore * 100).toFixed(0)}%)`);
|
|
781
|
-
recommendations.push("Consider grouping related exports together");
|
|
782
|
-
potentialSavings += contextBudget * 0.1;
|
|
783
|
-
}
|
|
784
|
-
if (fragmentationScore > maxFragmentation) {
|
|
785
|
-
if (severity === import_core3.Severity.Info || severity === import_core3.Severity.Minor)
|
|
786
|
-
severity = import_core3.Severity.Minor;
|
|
787
|
-
issues.push(
|
|
788
|
-
`High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`
|
|
789
|
-
);
|
|
790
|
-
recommendations.push("Consolidate with related files in same domain");
|
|
791
|
-
potentialSavings += contextBudget * 0.3;
|
|
792
|
-
}
|
|
793
|
-
if (isBuildArtifact(file)) {
|
|
794
|
-
issues.push("Detected build artifact (bundled/output file)");
|
|
795
|
-
recommendations.push("Exclude build outputs from analysis");
|
|
796
|
-
severity = import_core3.Severity.Info;
|
|
797
|
-
potentialSavings = 0;
|
|
798
|
-
}
|
|
799
|
-
return {
|
|
800
|
-
severity,
|
|
801
|
-
issues,
|
|
802
|
-
recommendations,
|
|
803
|
-
potentialSavings: Math.floor(potentialSavings)
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
function isBuildArtifact(filePath) {
|
|
807
|
-
const lower = filePath.toLowerCase();
|
|
808
|
-
return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/.next/");
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
// src/graph-builder.ts
|
|
812
|
-
var import_core4 = require("@aiready/core");
|
|
813
|
-
init_dependency_graph_utils();
|
|
814
|
-
|
|
815
|
-
// src/semantic/co-usage.ts
|
|
816
|
-
function buildCoUsageMatrix(graph) {
|
|
817
|
-
const coUsageMatrix = /* @__PURE__ */ new Map();
|
|
818
|
-
for (const [, node] of graph.nodes) {
|
|
819
|
-
const imports = node.imports;
|
|
820
|
-
for (let i = 0; i < imports.length; i++) {
|
|
821
|
-
const fileA = imports[i];
|
|
822
|
-
if (!coUsageMatrix.has(fileA)) coUsageMatrix.set(fileA, /* @__PURE__ */ new Map());
|
|
823
|
-
for (let j = i + 1; j < imports.length; j++) {
|
|
824
|
-
const fileB = imports[j];
|
|
825
|
-
const fileAUsage = coUsageMatrix.get(fileA);
|
|
826
|
-
fileAUsage.set(fileB, (fileAUsage.get(fileB) || 0) + 1);
|
|
827
|
-
if (!coUsageMatrix.has(fileB)) coUsageMatrix.set(fileB, /* @__PURE__ */ new Map());
|
|
828
|
-
const fileBUsage = coUsageMatrix.get(fileB);
|
|
829
|
-
fileBUsage.set(fileA, (fileBUsage.get(fileA) || 0) + 1);
|
|
617
|
+
if (singularSegment === keyword || segLower === keyword) return keyword;
|
|
830
618
|
}
|
|
831
619
|
}
|
|
832
620
|
}
|
|
833
|
-
return
|
|
621
|
+
return "unknown";
|
|
834
622
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
if (visited.has(file)) continue;
|
|
840
|
-
const cluster = [file];
|
|
841
|
-
visited.add(file);
|
|
842
|
-
for (const [relatedFile, count] of coUsages) {
|
|
843
|
-
if (count >= minCoUsage && !visited.has(relatedFile)) {
|
|
844
|
-
cluster.push(relatedFile);
|
|
845
|
-
visited.add(relatedFile);
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
if (cluster.length > 1) clusters.set(file, cluster);
|
|
623
|
+
var init_domain_inference = __esm({
|
|
624
|
+
"src/semantic/domain-inference.ts"() {
|
|
625
|
+
"use strict";
|
|
626
|
+
init_string_utils();
|
|
849
627
|
}
|
|
850
|
-
|
|
851
|
-
}
|
|
852
|
-
function getCoUsageData(file, coUsageMatrix) {
|
|
853
|
-
return {
|
|
854
|
-
file,
|
|
855
|
-
coImportedWith: coUsageMatrix.get(file) || /* @__PURE__ */ new Map(),
|
|
856
|
-
sharedImporters: []
|
|
857
|
-
};
|
|
858
|
-
}
|
|
628
|
+
});
|
|
859
629
|
|
|
860
|
-
// src/
|
|
861
|
-
function
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
for (const typeRef of exp.typeReferences) {
|
|
867
|
-
if (!typeGraph.has(typeRef)) typeGraph.set(typeRef, /* @__PURE__ */ new Set());
|
|
868
|
-
typeGraph.get(typeRef).add(file);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
630
|
+
// src/ast-utils.ts
|
|
631
|
+
async function extractExportsWithAST(content, filePath, domainOptions, fileImports) {
|
|
632
|
+
try {
|
|
633
|
+
const { exports: astExports } = await (0, import_core4.parseFileExports)(content, filePath);
|
|
634
|
+
if (astExports.length === 0 && !(0, import_core4.isTestFile)(filePath)) {
|
|
635
|
+
return extractExports(content, filePath, domainOptions, fileImports);
|
|
871
636
|
}
|
|
637
|
+
return astExports.map((exp) => ({
|
|
638
|
+
name: exp.name,
|
|
639
|
+
type: exp.type,
|
|
640
|
+
inferredDomain: inferDomain(
|
|
641
|
+
exp.name,
|
|
642
|
+
filePath,
|
|
643
|
+
domainOptions,
|
|
644
|
+
fileImports
|
|
645
|
+
),
|
|
646
|
+
imports: exp.imports,
|
|
647
|
+
dependencies: exp.dependencies,
|
|
648
|
+
typeReferences: exp.typeReferences
|
|
649
|
+
}));
|
|
650
|
+
} catch {
|
|
651
|
+
return extractExports(content, filePath, domainOptions, fileImports);
|
|
872
652
|
}
|
|
873
|
-
return typeGraph;
|
|
874
653
|
}
|
|
654
|
+
var import_core4;
|
|
655
|
+
var init_ast_utils = __esm({
|
|
656
|
+
"src/ast-utils.ts"() {
|
|
657
|
+
"use strict";
|
|
658
|
+
import_core4 = require("@aiready/core");
|
|
659
|
+
init_domain_inference();
|
|
660
|
+
}
|
|
661
|
+
});
|
|
875
662
|
|
|
876
663
|
// src/graph-builder.ts
|
|
877
|
-
var import_path = require("path");
|
|
878
664
|
function resolveImport(source, importingFile, allFiles) {
|
|
879
665
|
if (!source.startsWith(".") && !source.startsWith("/")) {
|
|
880
666
|
if (source.startsWith("@aiready/")) {
|
|
@@ -954,7 +740,7 @@ async function buildDependencyGraph(files, options) {
|
|
|
954
740
|
const autoDetectedKeywords = options?.domainKeywords ?? extractDomainKeywordsFromPaths(files);
|
|
955
741
|
const allFilePaths = new Set(files.map((f) => f.file));
|
|
956
742
|
for (const { file, content } of files) {
|
|
957
|
-
const { imports: astImports } = await (0,
|
|
743
|
+
const { imports: astImports } = await (0, import_core5.parseFileExports)(content, file);
|
|
958
744
|
const resolvedImports = astImports.map((i) => resolveImport(i.source, file, allFilePaths)).filter((path) => path !== null);
|
|
959
745
|
const importSources = astImports.map((i) => i.source);
|
|
960
746
|
const exports2 = await extractExportsWithAST(
|
|
@@ -963,7 +749,7 @@ async function buildDependencyGraph(files, options) {
|
|
|
963
749
|
{ domainKeywords: autoDetectedKeywords },
|
|
964
750
|
importSources
|
|
965
751
|
);
|
|
966
|
-
const tokenCost = (0,
|
|
752
|
+
const tokenCost = (0, import_core5.estimateTokens)(content);
|
|
967
753
|
const linesOfCode = content.split("\n").length;
|
|
968
754
|
nodes.set(file, {
|
|
969
755
|
file,
|
|
@@ -1019,61 +805,81 @@ function calculateContextBudget(file, graph) {
|
|
|
1019
805
|
function detectCircularDependencies(graph) {
|
|
1020
806
|
return detectGraphCycles(graph.edges);
|
|
1021
807
|
}
|
|
808
|
+
var import_core5, import_path;
|
|
809
|
+
var init_graph_builder = __esm({
|
|
810
|
+
"src/graph-builder.ts"() {
|
|
811
|
+
"use strict";
|
|
812
|
+
import_core5 = require("@aiready/core");
|
|
813
|
+
init_string_utils();
|
|
814
|
+
init_dependency_graph_utils();
|
|
815
|
+
init_co_usage();
|
|
816
|
+
init_type_graph();
|
|
817
|
+
init_domain_inference();
|
|
818
|
+
init_ast_utils();
|
|
819
|
+
import_path = require("path");
|
|
820
|
+
}
|
|
821
|
+
});
|
|
1022
822
|
|
|
1023
823
|
// src/classify/classification-patterns.ts
|
|
1024
|
-
var BARREL_EXPORT_MIN_EXPORTS
|
|
1025
|
-
var
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
824
|
+
var BARREL_EXPORT_MIN_EXPORTS, BARREL_EXPORT_TOKEN_LIMIT, HANDLER_NAME_PATTERNS, SERVICE_NAME_PATTERNS, EMAIL_NAME_PATTERNS, PARSER_NAME_PATTERNS, SESSION_NAME_PATTERNS, NEXTJS_METADATA_EXPORTS, CONFIG_NAME_PATTERNS;
|
|
825
|
+
var init_classification_patterns = __esm({
|
|
826
|
+
"src/classify/classification-patterns.ts"() {
|
|
827
|
+
"use strict";
|
|
828
|
+
BARREL_EXPORT_MIN_EXPORTS = 5;
|
|
829
|
+
BARREL_EXPORT_TOKEN_LIMIT = 1e3;
|
|
830
|
+
HANDLER_NAME_PATTERNS = [
|
|
831
|
+
"handler",
|
|
832
|
+
".handler.",
|
|
833
|
+
"-handler.",
|
|
834
|
+
"lambda",
|
|
835
|
+
".lambda.",
|
|
836
|
+
"-lambda."
|
|
837
|
+
];
|
|
838
|
+
SERVICE_NAME_PATTERNS = [
|
|
839
|
+
"service",
|
|
840
|
+
".service.",
|
|
841
|
+
"-service.",
|
|
842
|
+
"_service."
|
|
843
|
+
];
|
|
844
|
+
EMAIL_NAME_PATTERNS = [
|
|
845
|
+
"-email-",
|
|
846
|
+
".email.",
|
|
847
|
+
"_email_",
|
|
848
|
+
"-template",
|
|
849
|
+
".template.",
|
|
850
|
+
"_template",
|
|
851
|
+
"-mail.",
|
|
852
|
+
".mail."
|
|
853
|
+
];
|
|
854
|
+
PARSER_NAME_PATTERNS = [
|
|
855
|
+
"parser",
|
|
856
|
+
".parser.",
|
|
857
|
+
"-parser.",
|
|
858
|
+
"_parser.",
|
|
859
|
+
"transform",
|
|
860
|
+
"converter",
|
|
861
|
+
"mapper",
|
|
862
|
+
"serializer"
|
|
863
|
+
];
|
|
864
|
+
SESSION_NAME_PATTERNS = ["session", "state", "context", "store"];
|
|
865
|
+
NEXTJS_METADATA_EXPORTS = [
|
|
866
|
+
"metadata",
|
|
867
|
+
"generatemetadata",
|
|
868
|
+
"faqjsonld",
|
|
869
|
+
"jsonld",
|
|
870
|
+
"icon"
|
|
871
|
+
];
|
|
872
|
+
CONFIG_NAME_PATTERNS = [
|
|
873
|
+
".config.",
|
|
874
|
+
"tsconfig",
|
|
875
|
+
"jest.config",
|
|
876
|
+
"package.json",
|
|
877
|
+
"aiready.json",
|
|
878
|
+
"next.config",
|
|
879
|
+
"sst.config"
|
|
880
|
+
];
|
|
881
|
+
}
|
|
882
|
+
});
|
|
1077
883
|
|
|
1078
884
|
// src/classify/file-classifiers.ts
|
|
1079
885
|
function isBoilerplateBarrel(node) {
|
|
@@ -1215,23 +1021,14 @@ function isHubAndSpokeFile(node) {
|
|
|
1215
1021
|
const { file } = node;
|
|
1216
1022
|
return /\/packages\/[a-zA-Z0-9-]+\/src\//.test(file);
|
|
1217
1023
|
}
|
|
1024
|
+
var init_file_classifiers = __esm({
|
|
1025
|
+
"src/classify/file-classifiers.ts"() {
|
|
1026
|
+
"use strict";
|
|
1027
|
+
init_classification_patterns();
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1218
1030
|
|
|
1219
1031
|
// src/classifier.ts
|
|
1220
|
-
var Classification = {
|
|
1221
|
-
BARREL: "barrel-export",
|
|
1222
|
-
BOILERPLATE: "boilerplate-barrel",
|
|
1223
|
-
TYPE_DEFINITION: "type-definition",
|
|
1224
|
-
NEXTJS_PAGE: "nextjs-page",
|
|
1225
|
-
LAMBDA_HANDLER: "lambda-handler",
|
|
1226
|
-
SERVICE: "service-file",
|
|
1227
|
-
EMAIL_TEMPLATE: "email-template",
|
|
1228
|
-
PARSER: "parser-file",
|
|
1229
|
-
COHESIVE_MODULE: "cohesive-module",
|
|
1230
|
-
UTILITY_MODULE: "utility-module",
|
|
1231
|
-
SPOKE_MODULE: "spoke-module",
|
|
1232
|
-
MIXED_CONCERNS: "mixed-concerns",
|
|
1233
|
-
UNKNOWN: "unknown"
|
|
1234
|
-
};
|
|
1235
1032
|
function classifyFile(node, cohesionScore = 1, domains = []) {
|
|
1236
1033
|
if (isBoilerplateBarrel(node)) {
|
|
1237
1034
|
return Classification.BOILERPLATE;
|
|
@@ -1387,6 +1184,28 @@ function adjustFragmentationForClassification(baseFragmentation, classification)
|
|
|
1387
1184
|
return baseFragmentation * 0.7;
|
|
1388
1185
|
}
|
|
1389
1186
|
}
|
|
1187
|
+
var Classification;
|
|
1188
|
+
var init_classifier = __esm({
|
|
1189
|
+
"src/classifier.ts"() {
|
|
1190
|
+
"use strict";
|
|
1191
|
+
init_file_classifiers();
|
|
1192
|
+
Classification = {
|
|
1193
|
+
BARREL: "barrel-export",
|
|
1194
|
+
BOILERPLATE: "boilerplate-barrel",
|
|
1195
|
+
TYPE_DEFINITION: "type-definition",
|
|
1196
|
+
NEXTJS_PAGE: "nextjs-page",
|
|
1197
|
+
LAMBDA_HANDLER: "lambda-handler",
|
|
1198
|
+
SERVICE: "service-file",
|
|
1199
|
+
EMAIL_TEMPLATE: "email-template",
|
|
1200
|
+
PARSER: "parser-file",
|
|
1201
|
+
COHESIVE_MODULE: "cohesive-module",
|
|
1202
|
+
UTILITY_MODULE: "utility-module",
|
|
1203
|
+
SPOKE_MODULE: "spoke-module",
|
|
1204
|
+
MIXED_CONCERNS: "mixed-concerns",
|
|
1205
|
+
UNKNOWN: "unknown"
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1390
1209
|
|
|
1391
1210
|
// src/cluster-detector.ts
|
|
1392
1211
|
function detectModuleClusters(graph, options) {
|
|
@@ -1469,6 +1288,13 @@ function detectModuleClusters(graph, options) {
|
|
|
1469
1288
|
}
|
|
1470
1289
|
return clusters;
|
|
1471
1290
|
}
|
|
1291
|
+
var init_cluster_detector = __esm({
|
|
1292
|
+
"src/cluster-detector.ts"() {
|
|
1293
|
+
"use strict";
|
|
1294
|
+
init_metrics();
|
|
1295
|
+
init_classifier();
|
|
1296
|
+
}
|
|
1297
|
+
});
|
|
1472
1298
|
|
|
1473
1299
|
// src/remediation.ts
|
|
1474
1300
|
function getClassificationRecommendations(classification, file, issues) {
|
|
@@ -1573,91 +1399,277 @@ function getGeneralRecommendations(metrics, thresholds) {
|
|
|
1573
1399
|
);
|
|
1574
1400
|
if (severity === "info") severity = "minor";
|
|
1575
1401
|
}
|
|
1576
|
-
if (metrics.fragmentationScore > thresholds.maxFragmentation) {
|
|
1577
|
-
issues.push(
|
|
1578
|
-
`High domain fragmentation: ${metrics.fragmentationScore.toFixed(2)}`
|
|
1579
|
-
);
|
|
1580
|
-
recommendations.push(
|
|
1581
|
-
"Consolidate domain-related files into fewer directories"
|
|
1582
|
-
);
|
|
1583
|
-
if (severity === "info") severity = "minor";
|
|
1402
|
+
if (metrics.fragmentationScore > thresholds.maxFragmentation) {
|
|
1403
|
+
issues.push(
|
|
1404
|
+
`High domain fragmentation: ${metrics.fragmentationScore.toFixed(2)}`
|
|
1405
|
+
);
|
|
1406
|
+
recommendations.push(
|
|
1407
|
+
"Consolidate domain-related files into fewer directories"
|
|
1408
|
+
);
|
|
1409
|
+
if (severity === "info") severity = "minor";
|
|
1410
|
+
}
|
|
1411
|
+
return { recommendations, issues, severity };
|
|
1412
|
+
}
|
|
1413
|
+
var init_remediation = __esm({
|
|
1414
|
+
"src/remediation.ts"() {
|
|
1415
|
+
"use strict";
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
// src/mapper.ts
|
|
1420
|
+
function mapNodeToResult(node, graph, clusters, allCircularDeps, options) {
|
|
1421
|
+
const file = node.file;
|
|
1422
|
+
const tokenCost = node.tokenCost;
|
|
1423
|
+
const importDepth = calculateImportDepth(file, graph);
|
|
1424
|
+
const transitiveDeps = getTransitiveDependencies(file, graph);
|
|
1425
|
+
const contextBudget = calculateContextBudget(file, graph);
|
|
1426
|
+
const circularDeps = allCircularDeps.filter((cycle) => cycle.includes(file));
|
|
1427
|
+
const cluster = clusters.find((c) => c.files.includes(file));
|
|
1428
|
+
const rawFragmentationScore = cluster ? cluster.fragmentationScore : 0;
|
|
1429
|
+
const rawCohesionScore = calculateEnhancedCohesion(
|
|
1430
|
+
node.exports,
|
|
1431
|
+
file,
|
|
1432
|
+
options
|
|
1433
|
+
);
|
|
1434
|
+
const fileClassification = classifyFile(node, rawCohesionScore);
|
|
1435
|
+
const cohesionScore = adjustCohesionForClassification(
|
|
1436
|
+
rawCohesionScore,
|
|
1437
|
+
fileClassification
|
|
1438
|
+
);
|
|
1439
|
+
const fragmentationScore = adjustFragmentationForClassification(
|
|
1440
|
+
rawFragmentationScore,
|
|
1441
|
+
fileClassification
|
|
1442
|
+
);
|
|
1443
|
+
const { severity, issues, recommendations, potentialSavings } = analyzeIssues(
|
|
1444
|
+
{
|
|
1445
|
+
file,
|
|
1446
|
+
importDepth,
|
|
1447
|
+
contextBudget,
|
|
1448
|
+
cohesionScore,
|
|
1449
|
+
fragmentationScore,
|
|
1450
|
+
maxDepth: options.maxDepth,
|
|
1451
|
+
maxContextBudget: options.maxContextBudget,
|
|
1452
|
+
minCohesion: options.minCohesion,
|
|
1453
|
+
maxFragmentation: options.maxFragmentation,
|
|
1454
|
+
circularDeps
|
|
1455
|
+
}
|
|
1456
|
+
);
|
|
1457
|
+
const classRecs = getClassificationRecommendations(
|
|
1458
|
+
fileClassification,
|
|
1459
|
+
file,
|
|
1460
|
+
issues
|
|
1461
|
+
);
|
|
1462
|
+
const allRecommendations = Array.from(
|
|
1463
|
+
/* @__PURE__ */ new Set([...recommendations, ...classRecs])
|
|
1464
|
+
);
|
|
1465
|
+
return {
|
|
1466
|
+
file,
|
|
1467
|
+
tokenCost,
|
|
1468
|
+
linesOfCode: node.linesOfCode,
|
|
1469
|
+
importDepth,
|
|
1470
|
+
dependencyCount: transitiveDeps.length,
|
|
1471
|
+
dependencyList: transitiveDeps,
|
|
1472
|
+
circularDeps,
|
|
1473
|
+
cohesionScore,
|
|
1474
|
+
domains: Array.from(
|
|
1475
|
+
new Set(
|
|
1476
|
+
node.exports.flatMap((e) => e.domains?.map((d) => d.domain) || [])
|
|
1477
|
+
)
|
|
1478
|
+
),
|
|
1479
|
+
exportCount: node.exports.length,
|
|
1480
|
+
contextBudget,
|
|
1481
|
+
fragmentationScore,
|
|
1482
|
+
relatedFiles: cluster ? cluster.files : [],
|
|
1483
|
+
fileClassification,
|
|
1484
|
+
severity,
|
|
1485
|
+
issues,
|
|
1486
|
+
recommendations: allRecommendations,
|
|
1487
|
+
potentialSavings
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
var init_mapper = __esm({
|
|
1491
|
+
"src/mapper.ts"() {
|
|
1492
|
+
"use strict";
|
|
1493
|
+
init_metrics();
|
|
1494
|
+
init_issue_analyzer();
|
|
1495
|
+
init_graph_builder();
|
|
1496
|
+
init_classifier();
|
|
1497
|
+
init_remediation();
|
|
1498
|
+
}
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
// src/analyzers/python-context.ts
|
|
1502
|
+
var python_context_exports = {};
|
|
1503
|
+
__export(python_context_exports, {
|
|
1504
|
+
analyzePythonContext: () => analyzePythonContext
|
|
1505
|
+
});
|
|
1506
|
+
async function analyzePythonContext(files, rootDir) {
|
|
1507
|
+
const results = [];
|
|
1508
|
+
const parser = await (0, import_core6.getParser)("dummy.py");
|
|
1509
|
+
if (!parser) {
|
|
1510
|
+
console.warn("Python parser not available");
|
|
1511
|
+
return results;
|
|
1512
|
+
}
|
|
1513
|
+
const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
|
|
1514
|
+
void import_path2.relative;
|
|
1515
|
+
void import_path2.join;
|
|
1516
|
+
const dependencyGraph = await buildPythonDependencyGraph(
|
|
1517
|
+
pythonFiles,
|
|
1518
|
+
rootDir
|
|
1519
|
+
);
|
|
1520
|
+
for (const file of pythonFiles) {
|
|
1521
|
+
try {
|
|
1522
|
+
const code = await import_fs.default.promises.readFile(file, "utf-8");
|
|
1523
|
+
const result = parser.parse(code, file);
|
|
1524
|
+
const imports = result.imports.map((imp) => ({
|
|
1525
|
+
source: imp.source,
|
|
1526
|
+
specifiers: imp.specifiers,
|
|
1527
|
+
isRelative: imp.source.startsWith("."),
|
|
1528
|
+
resolvedPath: resolvePythonImport(file, imp.source, rootDir)
|
|
1529
|
+
}));
|
|
1530
|
+
const exports2 = result.exports.map((exp) => ({
|
|
1531
|
+
name: exp.name,
|
|
1532
|
+
type: exp.type
|
|
1533
|
+
}));
|
|
1534
|
+
const linesOfCode = code.split("\n").length;
|
|
1535
|
+
const importDepth = calculateImportDepthFromEdges(
|
|
1536
|
+
file,
|
|
1537
|
+
dependencyGraph,
|
|
1538
|
+
/* @__PURE__ */ new Set()
|
|
1539
|
+
);
|
|
1540
|
+
const contextBudget = estimateContextBudget(
|
|
1541
|
+
code,
|
|
1542
|
+
imports,
|
|
1543
|
+
dependencyGraph
|
|
1544
|
+
);
|
|
1545
|
+
const cohesion = calculatePythonCohesion(exports2, imports);
|
|
1546
|
+
const circularDependencies = detectGraphCyclesFromFile(
|
|
1547
|
+
file,
|
|
1548
|
+
dependencyGraph
|
|
1549
|
+
).map((cycle) => cycle.join(" -> "));
|
|
1550
|
+
results.push({
|
|
1551
|
+
file,
|
|
1552
|
+
importDepth,
|
|
1553
|
+
contextBudget,
|
|
1554
|
+
cohesion,
|
|
1555
|
+
imports,
|
|
1556
|
+
exports: exports2,
|
|
1557
|
+
metrics: {
|
|
1558
|
+
linesOfCode,
|
|
1559
|
+
importCount: imports.length,
|
|
1560
|
+
exportCount: exports2.length,
|
|
1561
|
+
circularDependencies
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
console.warn(`Failed to analyze ${file}:`, error);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
return results;
|
|
1569
|
+
}
|
|
1570
|
+
async function buildPythonDependencyGraph(files, rootDir) {
|
|
1571
|
+
const graph = /* @__PURE__ */ new Map();
|
|
1572
|
+
const parser = await (0, import_core6.getParser)("dummy.py");
|
|
1573
|
+
if (!parser) return graph;
|
|
1574
|
+
for (const file of files) {
|
|
1575
|
+
try {
|
|
1576
|
+
const code = await import_fs.default.promises.readFile(file, "utf-8");
|
|
1577
|
+
const result = parser.parse(code, file);
|
|
1578
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
1579
|
+
for (const imp of result.imports) {
|
|
1580
|
+
const resolved = resolvePythonImport(file, imp.source, rootDir);
|
|
1581
|
+
if (resolved && files.includes(resolved)) {
|
|
1582
|
+
dependencies.add(resolved);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
graph.set(file, dependencies);
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
void error;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
return graph;
|
|
1591
|
+
}
|
|
1592
|
+
function resolvePythonImport(fromFile, importPath, rootDir) {
|
|
1593
|
+
const dir = (0, import_path2.dirname)(fromFile);
|
|
1594
|
+
if (importPath.startsWith(".")) {
|
|
1595
|
+
const parts = importPath.split(".");
|
|
1596
|
+
let upCount = 0;
|
|
1597
|
+
while (parts[0] === "") {
|
|
1598
|
+
upCount++;
|
|
1599
|
+
parts.shift();
|
|
1600
|
+
}
|
|
1601
|
+
let targetDir = dir;
|
|
1602
|
+
for (let i = 0; i < upCount - 1; i++) {
|
|
1603
|
+
targetDir = (0, import_path2.dirname)(targetDir);
|
|
1604
|
+
}
|
|
1605
|
+
const modulePath = parts.join("/");
|
|
1606
|
+
const possiblePaths = [
|
|
1607
|
+
(0, import_path2.resolve)(targetDir, `${modulePath}.py`),
|
|
1608
|
+
(0, import_path2.resolve)(targetDir, modulePath, "__init__.py")
|
|
1609
|
+
];
|
|
1610
|
+
for (const path of possiblePaths) {
|
|
1611
|
+
if (import_fs.default.existsSync(path)) {
|
|
1612
|
+
return path;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
} else {
|
|
1616
|
+
const modulePath = importPath.replace(/\./g, "/");
|
|
1617
|
+
const possiblePaths = [
|
|
1618
|
+
(0, import_path2.resolve)(rootDir, `${modulePath}.py`),
|
|
1619
|
+
(0, import_path2.resolve)(rootDir, modulePath, "__init__.py")
|
|
1620
|
+
];
|
|
1621
|
+
for (const path of possiblePaths) {
|
|
1622
|
+
if (import_fs.default.existsSync(path)) {
|
|
1623
|
+
return path;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1584
1626
|
}
|
|
1585
|
-
return
|
|
1627
|
+
return void 0;
|
|
1586
1628
|
}
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
const
|
|
1597
|
-
const
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
fileClassification
|
|
1611
|
-
);
|
|
1612
|
-
const { severity, issues, recommendations, potentialSavings } = analyzeIssues(
|
|
1613
|
-
{
|
|
1614
|
-
file,
|
|
1615
|
-
importDepth,
|
|
1616
|
-
contextBudget,
|
|
1617
|
-
cohesionScore,
|
|
1618
|
-
fragmentationScore,
|
|
1619
|
-
maxDepth: options.maxDepth,
|
|
1620
|
-
maxContextBudget: options.maxContextBudget,
|
|
1621
|
-
minCohesion: options.minCohesion,
|
|
1622
|
-
maxFragmentation: options.maxFragmentation,
|
|
1623
|
-
circularDeps
|
|
1629
|
+
function estimateContextBudget(code, imports, dependencyGraph) {
|
|
1630
|
+
void dependencyGraph;
|
|
1631
|
+
let budget = (0, import_core6.estimateTokens)(code);
|
|
1632
|
+
const avgTokensPerDep = 500;
|
|
1633
|
+
budget += imports.length * avgTokensPerDep;
|
|
1634
|
+
return budget;
|
|
1635
|
+
}
|
|
1636
|
+
function calculatePythonCohesion(exports2, imports) {
|
|
1637
|
+
if (exports2.length === 0) return 1;
|
|
1638
|
+
const exportCount = exports2.length;
|
|
1639
|
+
const importCount = imports.length;
|
|
1640
|
+
let cohesion = 1;
|
|
1641
|
+
if (exportCount > 10) {
|
|
1642
|
+
cohesion *= 0.6;
|
|
1643
|
+
} else if (exportCount > 5) {
|
|
1644
|
+
cohesion *= 0.8;
|
|
1645
|
+
}
|
|
1646
|
+
if (exportCount > 0) {
|
|
1647
|
+
const ratio = importCount / exportCount;
|
|
1648
|
+
if (ratio > 2) {
|
|
1649
|
+
cohesion *= 1.1;
|
|
1650
|
+
} else if (ratio < 0.5) {
|
|
1651
|
+
cohesion *= 0.9;
|
|
1624
1652
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
fileClassification,
|
|
1628
|
-
file,
|
|
1629
|
-
issues
|
|
1630
|
-
);
|
|
1631
|
-
const allRecommendations = Array.from(
|
|
1632
|
-
/* @__PURE__ */ new Set([...recommendations, ...classRecs])
|
|
1633
|
-
);
|
|
1634
|
-
return {
|
|
1635
|
-
file,
|
|
1636
|
-
tokenCost,
|
|
1637
|
-
linesOfCode: node.linesOfCode,
|
|
1638
|
-
importDepth,
|
|
1639
|
-
dependencyCount: transitiveDeps.length,
|
|
1640
|
-
dependencyList: transitiveDeps,
|
|
1641
|
-
circularDeps,
|
|
1642
|
-
cohesionScore,
|
|
1643
|
-
domains: Array.from(
|
|
1644
|
-
new Set(
|
|
1645
|
-
node.exports.flatMap((e) => e.domains?.map((d) => d.domain) || [])
|
|
1646
|
-
)
|
|
1647
|
-
),
|
|
1648
|
-
exportCount: node.exports.length,
|
|
1649
|
-
contextBudget,
|
|
1650
|
-
fragmentationScore,
|
|
1651
|
-
relatedFiles: cluster ? cluster.files : [],
|
|
1652
|
-
fileClassification,
|
|
1653
|
-
severity,
|
|
1654
|
-
issues,
|
|
1655
|
-
recommendations: allRecommendations,
|
|
1656
|
-
potentialSavings
|
|
1657
|
-
};
|
|
1653
|
+
}
|
|
1654
|
+
return Math.min(1, Math.max(0, cohesion));
|
|
1658
1655
|
}
|
|
1656
|
+
var import_core6, import_path2, import_fs;
|
|
1657
|
+
var init_python_context = __esm({
|
|
1658
|
+
"src/analyzers/python-context.ts"() {
|
|
1659
|
+
"use strict";
|
|
1660
|
+
import_core6 = require("@aiready/core");
|
|
1661
|
+
import_path2 = require("path");
|
|
1662
|
+
import_fs = __toESM(require("fs"));
|
|
1663
|
+
init_dependency_graph_utils();
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1659
1666
|
|
|
1660
1667
|
// src/orchestrator.ts
|
|
1668
|
+
var orchestrator_exports = {};
|
|
1669
|
+
__export(orchestrator_exports, {
|
|
1670
|
+
analyzeContext: () => analyzeContext,
|
|
1671
|
+
calculateCohesion: () => calculateCohesion
|
|
1672
|
+
});
|
|
1661
1673
|
function calculateCohesion(exports2, filePath, options) {
|
|
1662
1674
|
return calculateEnhancedCohesion(exports2, filePath, options);
|
|
1663
1675
|
}
|
|
@@ -1670,7 +1682,7 @@ async function analyzeContext(options) {
|
|
|
1670
1682
|
includeNodeModules = false,
|
|
1671
1683
|
...scanOptions
|
|
1672
1684
|
} = options;
|
|
1673
|
-
const files = await (0,
|
|
1685
|
+
const files = await (0, import_core7.scanFiles)({
|
|
1674
1686
|
...scanOptions,
|
|
1675
1687
|
exclude: includeNodeModules && scanOptions.exclude ? scanOptions.exclude.filter(
|
|
1676
1688
|
(pattern) => pattern !== "**/node_modules/**"
|
|
@@ -1680,7 +1692,7 @@ async function analyzeContext(options) {
|
|
|
1680
1692
|
const fileContents = await Promise.all(
|
|
1681
1693
|
files.map(async (file) => ({
|
|
1682
1694
|
file,
|
|
1683
|
-
content: await (0,
|
|
1695
|
+
content: await (0, import_core7.readFileContent)(file)
|
|
1684
1696
|
}))
|
|
1685
1697
|
);
|
|
1686
1698
|
const graph = await buildDependencyGraph(
|
|
@@ -1740,13 +1752,28 @@ async function analyzeContext(options) {
|
|
|
1740
1752
|
);
|
|
1741
1753
|
return [...results, ...pythonResults];
|
|
1742
1754
|
}
|
|
1755
|
+
var import_core7;
|
|
1756
|
+
var init_orchestrator = __esm({
|
|
1757
|
+
"src/orchestrator.ts"() {
|
|
1758
|
+
"use strict";
|
|
1759
|
+
import_core7 = require("@aiready/core");
|
|
1760
|
+
init_metrics();
|
|
1761
|
+
init_issue_analyzer();
|
|
1762
|
+
init_graph_builder();
|
|
1763
|
+
init_cluster_detector();
|
|
1764
|
+
init_mapper();
|
|
1765
|
+
}
|
|
1766
|
+
});
|
|
1743
1767
|
|
|
1744
1768
|
// src/summary.ts
|
|
1745
|
-
var
|
|
1769
|
+
var summary_exports = {};
|
|
1770
|
+
__export(summary_exports, {
|
|
1771
|
+
generateSummary: () => generateSummary
|
|
1772
|
+
});
|
|
1746
1773
|
function generateSummary(results, options = {}) {
|
|
1747
1774
|
const config = options ? Object.fromEntries(
|
|
1748
1775
|
Object.entries(options).filter(
|
|
1749
|
-
([key]) => !
|
|
1776
|
+
([key]) => !import_core8.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
|
|
1750
1777
|
)
|
|
1751
1778
|
) : {};
|
|
1752
1779
|
const totalFiles = results.length;
|
|
@@ -1824,9 +1851,85 @@ function generateSummary(results, options = {}) {
|
|
|
1824
1851
|
config
|
|
1825
1852
|
};
|
|
1826
1853
|
}
|
|
1854
|
+
var import_core8;
|
|
1855
|
+
var init_summary = __esm({
|
|
1856
|
+
"src/summary.ts"() {
|
|
1857
|
+
"use strict";
|
|
1858
|
+
import_core8 = require("@aiready/core");
|
|
1859
|
+
init_metrics();
|
|
1860
|
+
}
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
// src/index.ts
|
|
1864
|
+
var index_exports = {};
|
|
1865
|
+
__export(index_exports, {
|
|
1866
|
+
BARREL_EXPORT_MIN_EXPORTS: () => BARREL_EXPORT_MIN_EXPORTS,
|
|
1867
|
+
BARREL_EXPORT_TOKEN_LIMIT: () => BARREL_EXPORT_TOKEN_LIMIT,
|
|
1868
|
+
CONFIG_NAME_PATTERNS: () => CONFIG_NAME_PATTERNS,
|
|
1869
|
+
Classification: () => Classification,
|
|
1870
|
+
ContextAnalyzerProvider: () => ContextAnalyzerProvider,
|
|
1871
|
+
EMAIL_NAME_PATTERNS: () => EMAIL_NAME_PATTERNS,
|
|
1872
|
+
HANDLER_NAME_PATTERNS: () => HANDLER_NAME_PATTERNS,
|
|
1873
|
+
NEXTJS_METADATA_EXPORTS: () => NEXTJS_METADATA_EXPORTS,
|
|
1874
|
+
PARSER_NAME_PATTERNS: () => PARSER_NAME_PATTERNS,
|
|
1875
|
+
SERVICE_NAME_PATTERNS: () => SERVICE_NAME_PATTERNS,
|
|
1876
|
+
SESSION_NAME_PATTERNS: () => SESSION_NAME_PATTERNS,
|
|
1877
|
+
adjustCohesionForClassification: () => adjustCohesionForClassification,
|
|
1878
|
+
adjustFragmentationForClassification: () => adjustFragmentationForClassification,
|
|
1879
|
+
analyzeContext: () => analyzeContext,
|
|
1880
|
+
buildCoUsageMatrix: () => buildCoUsageMatrix,
|
|
1881
|
+
buildDependencyGraph: () => buildDependencyGraph,
|
|
1882
|
+
buildTypeGraph: () => buildTypeGraph,
|
|
1883
|
+
calculateCohesion: () => calculateCohesion,
|
|
1884
|
+
calculateContextBudget: () => calculateContextBudget,
|
|
1885
|
+
calculateContextScore: () => calculateContextScore,
|
|
1886
|
+
calculateDirectoryDistance: () => calculateDirectoryDistance,
|
|
1887
|
+
calculateDomainConfidence: () => calculateDomainConfidence,
|
|
1888
|
+
calculateEnhancedCohesion: () => calculateEnhancedCohesion,
|
|
1889
|
+
calculateFragmentation: () => calculateFragmentation,
|
|
1890
|
+
calculateImportDepth: () => calculateImportDepth,
|
|
1891
|
+
calculatePathEntropy: () => calculatePathEntropy,
|
|
1892
|
+
calculateStructuralCohesionFromCoUsage: () => calculateStructuralCohesionFromCoUsage,
|
|
1893
|
+
classifyFile: () => classifyFile,
|
|
1894
|
+
detectCircularDependencies: () => detectCircularDependencies,
|
|
1895
|
+
detectModuleClusters: () => detectModuleClusters,
|
|
1896
|
+
displayConsoleReport: () => displayConsoleReport,
|
|
1897
|
+
extractDomainKeywordsFromPaths: () => extractDomainKeywordsFromPaths,
|
|
1898
|
+
extractExports: () => extractExports,
|
|
1899
|
+
findConsolidationCandidates: () => findConsolidationCandidates,
|
|
1900
|
+
findSemanticClusters: () => findSemanticClusters,
|
|
1901
|
+
generateHTMLReport: () => generateHTMLReport,
|
|
1902
|
+
generateSummary: () => generateSummary,
|
|
1903
|
+
getClassificationRecommendations: () => getClassificationRecommendations,
|
|
1904
|
+
getCoUsageData: () => getCoUsageData,
|
|
1905
|
+
getGeneralRecommendations: () => getGeneralRecommendations,
|
|
1906
|
+
getSmartDefaults: () => getSmartDefaults,
|
|
1907
|
+
getTransitiveDependencies: () => getTransitiveDependencies,
|
|
1908
|
+
inferDomain: () => inferDomain,
|
|
1909
|
+
inferDomainFromSemantics: () => inferDomainFromSemantics,
|
|
1910
|
+
isBarrelExport: () => isBarrelExport,
|
|
1911
|
+
isBoilerplateBarrel: () => isBoilerplateBarrel,
|
|
1912
|
+
isConfigFile: () => isConfigFile,
|
|
1913
|
+
isEmailTemplate: () => isEmailTemplate,
|
|
1914
|
+
isHubAndSpokeFile: () => isHubAndSpokeFile,
|
|
1915
|
+
isLambdaHandler: () => isLambdaHandler,
|
|
1916
|
+
isNextJsPage: () => isNextJsPage,
|
|
1917
|
+
isParserFile: () => isParserFile,
|
|
1918
|
+
isServiceFile: () => isServiceFile,
|
|
1919
|
+
isSessionFile: () => isSessionFile,
|
|
1920
|
+
isTypeDefinition: () => isTypeDefinition,
|
|
1921
|
+
isUtilityModule: () => isUtilityModule,
|
|
1922
|
+
mapScoreToRating: () => mapScoreToRating,
|
|
1923
|
+
runInteractiveSetup: () => runInteractiveSetup
|
|
1924
|
+
});
|
|
1925
|
+
module.exports = __toCommonJS(index_exports);
|
|
1926
|
+
var import_core12 = require("@aiready/core");
|
|
1927
|
+
|
|
1928
|
+
// src/provider.ts
|
|
1929
|
+
var import_core9 = require("@aiready/core");
|
|
1827
1930
|
|
|
1828
1931
|
// src/scoring.ts
|
|
1829
|
-
var
|
|
1932
|
+
var import_core = require("@aiready/core");
|
|
1830
1933
|
var BUDGET_EXCELLENT_THRESHOLD = 8e3;
|
|
1831
1934
|
var BUDGET_PENALTY_RATE = 200;
|
|
1832
1935
|
var DEPTH_EXCELLENT_THRESHOLD = 8;
|
|
@@ -1976,8 +2079,8 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1976
2079
|
priority: "high"
|
|
1977
2080
|
});
|
|
1978
2081
|
}
|
|
1979
|
-
const cfg = { ...
|
|
1980
|
-
const estimatedMonthlyCost = (0,
|
|
2082
|
+
const cfg = { ...import_core.DEFAULT_COST_CONFIG, ...costConfig };
|
|
2083
|
+
const estimatedMonthlyCost = (0, import_core.calculateMonthlyCost)(
|
|
1981
2084
|
avgContextBudget * (totalFiles || 1),
|
|
1982
2085
|
cfg
|
|
1983
2086
|
);
|
|
@@ -1985,9 +2088,9 @@ function calculateContextScore(summary, costConfig) {
|
|
|
1985
2088
|
...Array(criticalIssues).fill({ severity: "critical" }),
|
|
1986
2089
|
...Array(majorIssues).fill({ severity: "major" })
|
|
1987
2090
|
];
|
|
1988
|
-
const productivityImpact = (0,
|
|
2091
|
+
const productivityImpact = (0, import_core.calculateProductivityImpact)(issues);
|
|
1989
2092
|
return {
|
|
1990
|
-
toolName:
|
|
2093
|
+
toolName: import_core.ToolName.ContextAnalyzer,
|
|
1991
2094
|
score,
|
|
1992
2095
|
rawMetrics: {
|
|
1993
2096
|
avgContextBudget: Math.round(avgContextBudget),
|
|
@@ -2005,7 +2108,7 @@ function calculateContextScore(summary, costConfig) {
|
|
|
2005
2108
|
};
|
|
2006
2109
|
}
|
|
2007
2110
|
function mapScoreToRating(score) {
|
|
2008
|
-
return (0,
|
|
2111
|
+
return (0, import_core.getRatingSlug)(score).replace("-", " ");
|
|
2009
2112
|
}
|
|
2010
2113
|
|
|
2011
2114
|
// src/provider.ts
|
|
@@ -2013,8 +2116,10 @@ var ContextAnalyzerProvider = {
|
|
|
2013
2116
|
id: import_core9.ToolName.ContextAnalyzer,
|
|
2014
2117
|
alias: ["context", "fragmentation", "budget"],
|
|
2015
2118
|
async analyze(options) {
|
|
2016
|
-
const
|
|
2017
|
-
const
|
|
2119
|
+
const { analyzeContext: analyzeContext2 } = await Promise.resolve().then(() => (init_orchestrator(), orchestrator_exports));
|
|
2120
|
+
const { generateSummary: generateSummary2 } = await Promise.resolve().then(() => (init_summary(), summary_exports));
|
|
2121
|
+
const results = await analyzeContext2(options);
|
|
2122
|
+
const summary = generateSummary2(results, options);
|
|
2018
2123
|
const normalizedResults = results.map(
|
|
2019
2124
|
(r) => ({
|
|
2020
2125
|
fileName: r.file,
|
|
@@ -2028,7 +2133,6 @@ var ContextAnalyzerProvider = {
|
|
|
2028
2133
|
metrics: {
|
|
2029
2134
|
tokenCost: r.tokenCost,
|
|
2030
2135
|
complexityScore: r.importDepth
|
|
2031
|
-
// Map other context-specific metrics if needed
|
|
2032
2136
|
}
|
|
2033
2137
|
})
|
|
2034
2138
|
);
|
|
@@ -2051,6 +2155,14 @@ var ContextAnalyzerProvider = {
|
|
|
2051
2155
|
defaultWeight: 19
|
|
2052
2156
|
};
|
|
2053
2157
|
|
|
2158
|
+
// src/index.ts
|
|
2159
|
+
init_orchestrator();
|
|
2160
|
+
init_graph_builder();
|
|
2161
|
+
init_metrics();
|
|
2162
|
+
init_classifier();
|
|
2163
|
+
init_cluster_detector();
|
|
2164
|
+
init_remediation();
|
|
2165
|
+
|
|
2054
2166
|
// src/defaults.ts
|
|
2055
2167
|
var import_core10 = require("@aiready/core");
|
|
2056
2168
|
async function getSmartDefaults(directory, userOptions) {
|
|
@@ -2098,6 +2210,12 @@ async function getSmartDefaults(directory, userOptions) {
|
|
|
2098
2210
|
};
|
|
2099
2211
|
}
|
|
2100
2212
|
|
|
2213
|
+
// src/index.ts
|
|
2214
|
+
init_summary();
|
|
2215
|
+
init_co_usage();
|
|
2216
|
+
init_type_graph();
|
|
2217
|
+
init_domain_inference();
|
|
2218
|
+
|
|
2101
2219
|
// src/semantic/consolidation.ts
|
|
2102
2220
|
function findConsolidationCandidates(graph, coUsageMatrix, typeGraph, minCoUsage = 5, minSharedTypes = 2) {
|
|
2103
2221
|
const candidates = [];
|
|
@@ -2127,6 +2245,10 @@ function findConsolidationCandidates(graph, coUsageMatrix, typeGraph, minCoUsage
|
|
|
2127
2245
|
return candidates.sort((a, b) => b.strength - a.strength);
|
|
2128
2246
|
}
|
|
2129
2247
|
|
|
2248
|
+
// src/index.ts
|
|
2249
|
+
init_classification_patterns();
|
|
2250
|
+
init_file_classifiers();
|
|
2251
|
+
|
|
2130
2252
|
// src/report/console-report.ts
|
|
2131
2253
|
var import_chalk = __toESM(require("chalk"));
|
|
2132
2254
|
function displayConsoleReport(summary, results, maxResults = 10) {
|
|
@@ -2204,8 +2326,7 @@ var import_core11 = require("@aiready/core");
|
|
|
2204
2326
|
function generateHTMLReport(summary, results) {
|
|
2205
2327
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
2206
2328
|
void results;
|
|
2207
|
-
const
|
|
2208
|
-
const stats = (0, import_core11.generateStatCards)([
|
|
2329
|
+
const stats = [
|
|
2209
2330
|
{ value: summary.totalFiles, label: "Files Analyzed" },
|
|
2210
2331
|
{ value: summary.totalTokens.toLocaleString(), label: "Total Tokens" },
|
|
2211
2332
|
{ value: summary.avgContextBudget.toFixed(0), label: "Avg Context Budget" },
|
|
@@ -2214,62 +2335,57 @@ function generateHTMLReport(summary, results) {
|
|
|
2214
2335
|
label: "Total Issues",
|
|
2215
2336
|
color: totalIssues > 0 ? "#f39c12" : void 0
|
|
2216
2337
|
}
|
|
2217
|
-
]
|
|
2218
|
-
const
|
|
2219
|
-
"\u{1F50D} AIReady Context Analysis Report",
|
|
2220
|
-
`Generated on ${(/* @__PURE__ */ new Date()).toLocaleString()}`
|
|
2221
|
-
);
|
|
2222
|
-
let body = `${hero}
|
|
2223
|
-
${stats}`;
|
|
2338
|
+
];
|
|
2339
|
+
const sections = [];
|
|
2224
2340
|
if (totalIssues > 0) {
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2341
|
+
sections.push({
|
|
2342
|
+
title: "\u26A0\uFE0F Issues Summary",
|
|
2343
|
+
content: (0, import_core11.generateIssueSummary)(
|
|
2344
|
+
summary.criticalIssues,
|
|
2345
|
+
summary.majorIssues,
|
|
2346
|
+
summary.minorIssues,
|
|
2347
|
+
summary.totalPotentialSavings
|
|
2348
|
+
)
|
|
2349
|
+
});
|
|
2231
2350
|
}
|
|
2232
2351
|
if (summary.fragmentedModules.length > 0) {
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
`${(m.fragmentationScore * 100).toFixed(0)}%`,
|
|
2237
|
-
m.totalTokens.toLocaleString()
|
|
2238
|
-
]);
|
|
2239
|
-
body += (0, import_core11.wrapInCard)(
|
|
2240
|
-
(0, import_core11.generateTable)({
|
|
2352
|
+
sections.push({
|
|
2353
|
+
title: "\u{1F9E9} Fragmented Modules",
|
|
2354
|
+
content: (0, import_core11.generateTable)({
|
|
2241
2355
|
headers: ["Domain", "Files", "Fragmentation", "Token Cost"],
|
|
2242
|
-
rows:
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2356
|
+
rows: summary.fragmentedModules.map((m) => [
|
|
2357
|
+
m.domain,
|
|
2358
|
+
String(m.files.length),
|
|
2359
|
+
`${(m.fragmentationScore * 100).toFixed(0)}%`,
|
|
2360
|
+
m.totalTokens.toLocaleString()
|
|
2361
|
+
])
|
|
2362
|
+
})
|
|
2363
|
+
});
|
|
2246
2364
|
}
|
|
2247
2365
|
if (summary.topExpensiveFiles.length > 0) {
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
`<span class="issue-${f.severity}">${f.severity.toUpperCase()}</span>`
|
|
2252
|
-
]);
|
|
2253
|
-
body += (0, import_core11.wrapInCard)(
|
|
2254
|
-
(0, import_core11.generateTable)({
|
|
2366
|
+
sections.push({
|
|
2367
|
+
title: "\u{1F4B8} Most Expensive Files",
|
|
2368
|
+
content: (0, import_core11.generateTable)({
|
|
2255
2369
|
headers: ["File", "Context Budget", "Severity"],
|
|
2256
|
-
rows:
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2370
|
+
rows: summary.topExpensiveFiles.map((f) => [
|
|
2371
|
+
f.file,
|
|
2372
|
+
`${f.contextBudget.toLocaleString()} tokens`,
|
|
2373
|
+
`<span class="issue-${f.severity}">${f.severity.toUpperCase()}</span>`
|
|
2374
|
+
])
|
|
2375
|
+
})
|
|
2376
|
+
});
|
|
2260
2377
|
}
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
</html>`;
|
|
2378
|
+
return (0, import_core11.generateStandardHtmlReport)(
|
|
2379
|
+
{
|
|
2380
|
+
title: "Context Analysis Report",
|
|
2381
|
+
packageName: "context-analyzer",
|
|
2382
|
+
packageUrl: "https://github.com/caopengau/aiready-context-analyzer",
|
|
2383
|
+
bugUrl: "https://github.com/caopengau/aiready-context-analyzer/issues",
|
|
2384
|
+
emoji: "\u{1F9E0}"
|
|
2385
|
+
},
|
|
2386
|
+
stats,
|
|
2387
|
+
sections
|
|
2388
|
+
);
|
|
2273
2389
|
}
|
|
2274
2390
|
|
|
2275
2391
|
// src/report/interactive-setup.ts
|