@aiready/context-analyzer 0.9.26 → 0.9.28
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 +11 -11
- package/.turbo/turbo-test.log +21 -20
- package/dist/chunk-FYI56A5M.mjs +1892 -0
- package/dist/chunk-I77HFFZU.mjs +1876 -0
- package/dist/chunk-KYSZF5N6.mjs +1876 -0
- package/dist/chunk-M64RHH4D.mjs +1896 -0
- package/dist/chunk-OP4G6GLN.mjs +1876 -0
- package/dist/chunk-P3T3H27S.mjs +1895 -0
- package/dist/chunk-VBWXHKGD.mjs +1895 -0
- package/dist/cli.js +197 -36
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +202 -32
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/src/analyzer.ts +168 -34
- package/src/index.ts +1 -1
- package/src/scoring.ts +28 -1
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,7 @@ __export(python_context_exports, {
|
|
|
37
37
|
});
|
|
38
38
|
async function analyzePythonContext(files, rootDir) {
|
|
39
39
|
const results = [];
|
|
40
|
-
const parser = (0,
|
|
40
|
+
const parser = (0, import_core3.getParser)("dummy.py");
|
|
41
41
|
if (!parser) {
|
|
42
42
|
console.warn("Python parser not available");
|
|
43
43
|
return results;
|
|
@@ -86,7 +86,7 @@ async function analyzePythonContext(files, rootDir) {
|
|
|
86
86
|
}
|
|
87
87
|
async function buildPythonDependencyGraph(files, rootDir) {
|
|
88
88
|
const graph = /* @__PURE__ */ new Map();
|
|
89
|
-
const parser = (0,
|
|
89
|
+
const parser = (0, import_core3.getParser)("dummy.py");
|
|
90
90
|
if (!parser) return graph;
|
|
91
91
|
for (const file of files) {
|
|
92
92
|
try {
|
|
@@ -167,7 +167,7 @@ async function calculatePythonImportDepth(file, dependencyGraph, visited, depth
|
|
|
167
167
|
return maxDepth;
|
|
168
168
|
}
|
|
169
169
|
function estimateContextBudget(code, imports, dependencyGraph) {
|
|
170
|
-
let budget = (0,
|
|
170
|
+
let budget = (0, import_core3.estimateTokens)(code);
|
|
171
171
|
const avgTokensPerDep = 500;
|
|
172
172
|
budget += imports.length * avgTokensPerDep;
|
|
173
173
|
return budget;
|
|
@@ -217,11 +217,11 @@ function detectCircularDependencies2(file, dependencyGraph) {
|
|
|
217
217
|
dfs(file, []);
|
|
218
218
|
return [...new Set(circular)];
|
|
219
219
|
}
|
|
220
|
-
var
|
|
220
|
+
var import_core3, import_path;
|
|
221
221
|
var init_python_context = __esm({
|
|
222
222
|
"src/analyzers/python-context.ts"() {
|
|
223
223
|
"use strict";
|
|
224
|
-
|
|
224
|
+
import_core3 = require("@aiready/core");
|
|
225
225
|
import_path = require("path");
|
|
226
226
|
}
|
|
227
227
|
});
|
|
@@ -230,7 +230,7 @@ var init_python_context = __esm({
|
|
|
230
230
|
var import_commander = require("commander");
|
|
231
231
|
|
|
232
232
|
// src/index.ts
|
|
233
|
-
var
|
|
233
|
+
var import_core4 = require("@aiready/core");
|
|
234
234
|
|
|
235
235
|
// src/analyzer.ts
|
|
236
236
|
var import_core = require("@aiready/core");
|
|
@@ -570,9 +570,9 @@ function calculatePathEntropy(files) {
|
|
|
570
570
|
if (counts.length <= 1) return 0;
|
|
571
571
|
const total = counts.reduce((s, v) => s + v, 0);
|
|
572
572
|
let entropy = 0;
|
|
573
|
-
for (const
|
|
574
|
-
const
|
|
575
|
-
entropy -=
|
|
573
|
+
for (const count of counts) {
|
|
574
|
+
const prob = count / total;
|
|
575
|
+
entropy -= prob * Math.log2(prob);
|
|
576
576
|
}
|
|
577
577
|
const maxEntropy = Math.log2(counts.length);
|
|
578
578
|
return maxEntropy > 0 ? entropy / maxEntropy : 0;
|
|
@@ -840,8 +840,8 @@ function calculateStructuralCohesionFromCoUsage(file, coUsageMatrix) {
|
|
|
840
840
|
}
|
|
841
841
|
if (probs.length <= 1) return 1;
|
|
842
842
|
let entropy = 0;
|
|
843
|
-
for (const
|
|
844
|
-
entropy -=
|
|
843
|
+
for (const prob of probs) {
|
|
844
|
+
entropy -= prob * Math.log2(prob);
|
|
845
845
|
}
|
|
846
846
|
const maxEntropy = Math.log2(probs.length);
|
|
847
847
|
return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
|
|
@@ -881,10 +881,10 @@ function calculateDomainCohesion(exports2) {
|
|
|
881
881
|
}
|
|
882
882
|
const total = domains.length;
|
|
883
883
|
let entropy = 0;
|
|
884
|
-
for (const
|
|
885
|
-
const
|
|
886
|
-
if (
|
|
887
|
-
entropy -=
|
|
884
|
+
for (const domainCount of domainCounts.values()) {
|
|
885
|
+
const prob = domainCount / total;
|
|
886
|
+
if (prob > 0) {
|
|
887
|
+
entropy -= prob * Math.log2(prob);
|
|
888
888
|
}
|
|
889
889
|
}
|
|
890
890
|
const maxEntropy = Math.log2(total);
|
|
@@ -904,6 +904,9 @@ function classifyFile(node, cohesionScore, domains) {
|
|
|
904
904
|
if (isLambdaHandler(node)) {
|
|
905
905
|
return "lambda-handler";
|
|
906
906
|
}
|
|
907
|
+
if (isDataAccessFile(node)) {
|
|
908
|
+
return "cohesive-module";
|
|
909
|
+
}
|
|
907
910
|
if (isEmailTemplate(node)) {
|
|
908
911
|
return "email-template";
|
|
909
912
|
}
|
|
@@ -922,11 +925,17 @@ function classifyFile(node, cohesionScore, domains) {
|
|
|
922
925
|
if (isUtilityFile(node)) {
|
|
923
926
|
return "utility-module";
|
|
924
927
|
}
|
|
928
|
+
if (file.toLowerCase().includes("/utils/") || file.toLowerCase().includes("/helpers/")) {
|
|
929
|
+
return "utility-module";
|
|
930
|
+
}
|
|
925
931
|
const uniqueDomains = domains.filter((d) => d !== "unknown");
|
|
926
932
|
const hasSingleDomain = uniqueDomains.length <= 1;
|
|
927
933
|
if (hasSingleDomain) {
|
|
928
934
|
return "cohesive-module";
|
|
929
935
|
}
|
|
936
|
+
if (allExportsShareEntityNoun(exports2)) {
|
|
937
|
+
return "cohesive-module";
|
|
938
|
+
}
|
|
930
939
|
const hasMultipleDomains = uniqueDomains.length > 1;
|
|
931
940
|
const hasLowCohesion = cohesionScore < 0.4;
|
|
932
941
|
if (hasMultipleDomains && hasLowCohesion) {
|
|
@@ -1018,6 +1027,107 @@ function isUtilityFile(node) {
|
|
|
1018
1027
|
const hasManySmallExportsInUtilityContext = exports2.length >= 3 && exports2.every((e) => e.type === "function" || e.type === "const") && (isUtilityName || isUtilityPath);
|
|
1019
1028
|
return isUtilityName || isUtilityPath || hasManySmallExportsInUtilityContext;
|
|
1020
1029
|
}
|
|
1030
|
+
function splitCamelCase(name) {
|
|
1031
|
+
return name.replace(/([A-Z])/g, " $1").trim().toLowerCase().split(/[\s_-]+/).filter(Boolean);
|
|
1032
|
+
}
|
|
1033
|
+
var SKIP_WORDS = /* @__PURE__ */ new Set([
|
|
1034
|
+
"get",
|
|
1035
|
+
"set",
|
|
1036
|
+
"create",
|
|
1037
|
+
"update",
|
|
1038
|
+
"delete",
|
|
1039
|
+
"fetch",
|
|
1040
|
+
"save",
|
|
1041
|
+
"load",
|
|
1042
|
+
"parse",
|
|
1043
|
+
"format",
|
|
1044
|
+
"validate",
|
|
1045
|
+
"convert",
|
|
1046
|
+
"transform",
|
|
1047
|
+
"build",
|
|
1048
|
+
"generate",
|
|
1049
|
+
"render",
|
|
1050
|
+
"send",
|
|
1051
|
+
"receive",
|
|
1052
|
+
"find",
|
|
1053
|
+
"list",
|
|
1054
|
+
"add",
|
|
1055
|
+
"remove",
|
|
1056
|
+
"insert",
|
|
1057
|
+
"upsert",
|
|
1058
|
+
"put",
|
|
1059
|
+
"read",
|
|
1060
|
+
"write",
|
|
1061
|
+
"check",
|
|
1062
|
+
"handle",
|
|
1063
|
+
"process",
|
|
1064
|
+
"compute",
|
|
1065
|
+
"calculate",
|
|
1066
|
+
"init",
|
|
1067
|
+
"reset",
|
|
1068
|
+
"clear",
|
|
1069
|
+
"pending",
|
|
1070
|
+
"active",
|
|
1071
|
+
"current",
|
|
1072
|
+
"new",
|
|
1073
|
+
"old",
|
|
1074
|
+
"all",
|
|
1075
|
+
"by",
|
|
1076
|
+
"with",
|
|
1077
|
+
"from",
|
|
1078
|
+
"to",
|
|
1079
|
+
"and",
|
|
1080
|
+
"or",
|
|
1081
|
+
"is",
|
|
1082
|
+
"has",
|
|
1083
|
+
"in",
|
|
1084
|
+
"on",
|
|
1085
|
+
"of",
|
|
1086
|
+
"the"
|
|
1087
|
+
]);
|
|
1088
|
+
function simpleSingularize(word) {
|
|
1089
|
+
if (word.endsWith("ies") && word.length > 3) return word.slice(0, -3) + "y";
|
|
1090
|
+
if (word.endsWith("ses") && word.length > 4) return word.slice(0, -2);
|
|
1091
|
+
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
1092
|
+
return word;
|
|
1093
|
+
}
|
|
1094
|
+
function extractEntityNouns(name) {
|
|
1095
|
+
return splitCamelCase(name).filter((token) => !SKIP_WORDS.has(token) && token.length > 2).map(simpleSingularize);
|
|
1096
|
+
}
|
|
1097
|
+
function allExportsShareEntityNoun(exports2) {
|
|
1098
|
+
if (exports2.length < 2 || exports2.length > 30) return false;
|
|
1099
|
+
const nounSets = exports2.map((e) => new Set(extractEntityNouns(e.name)));
|
|
1100
|
+
if (nounSets.some((s) => s.size === 0)) return false;
|
|
1101
|
+
const [first, ...rest] = nounSets;
|
|
1102
|
+
const commonNouns = Array.from(first).filter(
|
|
1103
|
+
(noun) => rest.every((s) => s.has(noun))
|
|
1104
|
+
);
|
|
1105
|
+
return commonNouns.length > 0;
|
|
1106
|
+
}
|
|
1107
|
+
function isDataAccessFile(node) {
|
|
1108
|
+
const { file, exports: exports2 } = node;
|
|
1109
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1110
|
+
const dalPatterns = [
|
|
1111
|
+
"dynamo",
|
|
1112
|
+
"database",
|
|
1113
|
+
"repository",
|
|
1114
|
+
"repo",
|
|
1115
|
+
"dao",
|
|
1116
|
+
"firestore",
|
|
1117
|
+
"postgres",
|
|
1118
|
+
"mysql",
|
|
1119
|
+
"mongo",
|
|
1120
|
+
"redis",
|
|
1121
|
+
"sqlite",
|
|
1122
|
+
"supabase",
|
|
1123
|
+
"prisma"
|
|
1124
|
+
];
|
|
1125
|
+
const isDalName = dalPatterns.some((p) => fileName?.includes(p));
|
|
1126
|
+
const isDalPath = file.toLowerCase().includes("/repositories/") || file.toLowerCase().includes("/dao/") || file.toLowerCase().includes("/data/");
|
|
1127
|
+
const hasDalExportPattern = exports2.length >= 1 && exports2.length <= 10 && allExportsShareEntityNoun(exports2);
|
|
1128
|
+
const isUtilityPathLocal = file.toLowerCase().includes("/utils/") || file.toLowerCase().includes("/helpers/");
|
|
1129
|
+
return isDalPath || isDalName && hasDalExportPattern && !isUtilityPathLocal;
|
|
1130
|
+
}
|
|
1021
1131
|
function isLambdaHandler(node) {
|
|
1022
1132
|
const { file, exports: exports2 } = node;
|
|
1023
1133
|
const fileName = file.split("/").pop()?.toLowerCase();
|
|
@@ -1032,7 +1142,7 @@ function isLambdaHandler(node) {
|
|
|
1032
1142
|
const isHandlerName = handlerPatterns.some(
|
|
1033
1143
|
(pattern) => fileName?.includes(pattern)
|
|
1034
1144
|
);
|
|
1035
|
-
const isHandlerPath = file.toLowerCase().includes("/handlers/") || file.toLowerCase().includes("/lambdas/") || file.toLowerCase().includes("/functions/");
|
|
1145
|
+
const isHandlerPath = file.toLowerCase().includes("/handlers/") || file.toLowerCase().includes("/lambdas/") || file.toLowerCase().includes("/lambda/") || file.toLowerCase().includes("/functions/");
|
|
1036
1146
|
const hasHandlerExport = exports2.some(
|
|
1037
1147
|
(e) => e.name.toLowerCase() === "handler" || e.name.toLowerCase() === "main" || e.name.toLowerCase() === "lambdahandler" || e.name.toLowerCase().endsWith("handler")
|
|
1038
1148
|
);
|
|
@@ -1172,25 +1282,25 @@ function adjustCohesionForClassification(baseCohesion, classification, node) {
|
|
|
1172
1282
|
const exportNames = node.exports.map((e) => e.name.toLowerCase());
|
|
1173
1283
|
const hasRelatedNames = hasRelatedExportNames(exportNames);
|
|
1174
1284
|
if (hasRelatedNames) {
|
|
1175
|
-
return Math.min(1, baseCohesion + 0.45);
|
|
1285
|
+
return Math.max(0.8, Math.min(1, baseCohesion + 0.45));
|
|
1176
1286
|
}
|
|
1177
1287
|
}
|
|
1178
|
-
return Math.min(1, baseCohesion + 0.35);
|
|
1288
|
+
return Math.max(0.75, Math.min(1, baseCohesion + 0.35));
|
|
1179
1289
|
}
|
|
1180
1290
|
case "service-file": {
|
|
1181
1291
|
if (node?.exports.some((e) => e.type === "class")) {
|
|
1182
|
-
return Math.min(1, baseCohesion + 0.4);
|
|
1292
|
+
return Math.max(0.78, Math.min(1, baseCohesion + 0.4));
|
|
1183
1293
|
}
|
|
1184
|
-
return Math.min(1, baseCohesion + 0.3);
|
|
1294
|
+
return Math.max(0.72, Math.min(1, baseCohesion + 0.3));
|
|
1185
1295
|
}
|
|
1186
1296
|
case "lambda-handler": {
|
|
1187
1297
|
if (node) {
|
|
1188
1298
|
const hasSingleEntry = node.exports.length === 1 || node.exports.some((e) => e.name.toLowerCase() === "handler");
|
|
1189
1299
|
if (hasSingleEntry) {
|
|
1190
|
-
return Math.min(1, baseCohesion + 0.45);
|
|
1300
|
+
return Math.max(0.8, Math.min(1, baseCohesion + 0.45));
|
|
1191
1301
|
}
|
|
1192
1302
|
}
|
|
1193
|
-
return Math.min(1, baseCohesion + 0.35);
|
|
1303
|
+
return Math.max(0.75, Math.min(1, baseCohesion + 0.35));
|
|
1194
1304
|
}
|
|
1195
1305
|
case "email-template": {
|
|
1196
1306
|
if (node) {
|
|
@@ -1198,10 +1308,10 @@ function adjustCohesionForClassification(baseCohesion, classification, node) {
|
|
|
1198
1308
|
(e) => e.name.toLowerCase().includes("render") || e.name.toLowerCase().includes("generate") || e.name.toLowerCase().includes("template")
|
|
1199
1309
|
);
|
|
1200
1310
|
if (hasTemplateFunc) {
|
|
1201
|
-
return Math.min(1, baseCohesion + 0.4);
|
|
1311
|
+
return Math.max(0.75, Math.min(1, baseCohesion + 0.4));
|
|
1202
1312
|
}
|
|
1203
1313
|
}
|
|
1204
|
-
return Math.min(1, baseCohesion + 0.3);
|
|
1314
|
+
return Math.max(0.72, Math.min(1, baseCohesion + 0.3));
|
|
1205
1315
|
}
|
|
1206
1316
|
case "parser-file": {
|
|
1207
1317
|
if (node) {
|
|
@@ -1209,10 +1319,10 @@ function adjustCohesionForClassification(baseCohesion, classification, node) {
|
|
|
1209
1319
|
(e) => e.name.toLowerCase().startsWith("parse") || e.name.toLowerCase().startsWith("transform") || e.name.toLowerCase().startsWith("convert")
|
|
1210
1320
|
);
|
|
1211
1321
|
if (hasParseFunc) {
|
|
1212
|
-
return Math.min(1, baseCohesion + 0.4);
|
|
1322
|
+
return Math.max(0.75, Math.min(1, baseCohesion + 0.4));
|
|
1213
1323
|
}
|
|
1214
1324
|
}
|
|
1215
|
-
return Math.min(1, baseCohesion + 0.3);
|
|
1325
|
+
return Math.max(0.7, Math.min(1, baseCohesion + 0.3));
|
|
1216
1326
|
}
|
|
1217
1327
|
case "nextjs-page":
|
|
1218
1328
|
return 1;
|
|
@@ -1252,6 +1362,54 @@ function hasRelatedExportNames(exportNames) {
|
|
|
1252
1362
|
const uniquePrefixes = new Set(prefixes);
|
|
1253
1363
|
if (uniquePrefixes.size === 1) return true;
|
|
1254
1364
|
}
|
|
1365
|
+
const nounSets = exportNames.map((name) => {
|
|
1366
|
+
const tokens = name.replace(/([A-Z])/g, " $1").trim().toLowerCase().split(/[\s_-]+/).filter(Boolean);
|
|
1367
|
+
const skip = /* @__PURE__ */ new Set([
|
|
1368
|
+
"get",
|
|
1369
|
+
"set",
|
|
1370
|
+
"create",
|
|
1371
|
+
"update",
|
|
1372
|
+
"delete",
|
|
1373
|
+
"fetch",
|
|
1374
|
+
"save",
|
|
1375
|
+
"load",
|
|
1376
|
+
"parse",
|
|
1377
|
+
"format",
|
|
1378
|
+
"validate",
|
|
1379
|
+
"convert",
|
|
1380
|
+
"transform",
|
|
1381
|
+
"build",
|
|
1382
|
+
"generate",
|
|
1383
|
+
"render",
|
|
1384
|
+
"send",
|
|
1385
|
+
"receive",
|
|
1386
|
+
"find",
|
|
1387
|
+
"list",
|
|
1388
|
+
"add",
|
|
1389
|
+
"remove",
|
|
1390
|
+
"insert",
|
|
1391
|
+
"upsert",
|
|
1392
|
+
"put",
|
|
1393
|
+
"read",
|
|
1394
|
+
"write",
|
|
1395
|
+
"check",
|
|
1396
|
+
"handle",
|
|
1397
|
+
"process",
|
|
1398
|
+
"pending",
|
|
1399
|
+
"active",
|
|
1400
|
+
"current",
|
|
1401
|
+
"new",
|
|
1402
|
+
"old",
|
|
1403
|
+
"all"
|
|
1404
|
+
]);
|
|
1405
|
+
const singularize2 = (w) => w.endsWith("s") && w.length > 3 ? w.slice(0, -1) : w;
|
|
1406
|
+
return new Set(tokens.filter((t) => !skip.has(t) && t.length > 2).map(singularize2));
|
|
1407
|
+
});
|
|
1408
|
+
if (nounSets.length >= 2 && nounSets.every((s) => s.size > 0)) {
|
|
1409
|
+
const [first, ...rest] = nounSets;
|
|
1410
|
+
const commonNouns = Array.from(first).filter((n) => rest.every((s) => s.has(n)));
|
|
1411
|
+
if (commonNouns.length > 0) return true;
|
|
1412
|
+
}
|
|
1255
1413
|
return false;
|
|
1256
1414
|
}
|
|
1257
1415
|
function adjustFragmentationForClassification(baseFragmentation, classification) {
|
|
@@ -1333,6 +1491,9 @@ function getClassificationRecommendations(classification, file, issues) {
|
|
|
1333
1491
|
}
|
|
1334
1492
|
}
|
|
1335
1493
|
|
|
1494
|
+
// src/scoring.ts
|
|
1495
|
+
var import_core2 = require("@aiready/core");
|
|
1496
|
+
|
|
1336
1497
|
// src/index.ts
|
|
1337
1498
|
async function analyzeContext(options) {
|
|
1338
1499
|
const {
|
|
@@ -1344,7 +1505,7 @@ async function analyzeContext(options) {
|
|
|
1344
1505
|
includeNodeModules = false,
|
|
1345
1506
|
...scanOptions
|
|
1346
1507
|
} = options;
|
|
1347
|
-
const files = await (0,
|
|
1508
|
+
const files = await (0, import_core4.scanFiles)({
|
|
1348
1509
|
...scanOptions,
|
|
1349
1510
|
// Only add node_modules to exclude if includeNodeModules is false
|
|
1350
1511
|
// The DEFAULT_EXCLUDE already includes node_modules, so this is only needed
|
|
@@ -1356,7 +1517,7 @@ async function analyzeContext(options) {
|
|
|
1356
1517
|
const fileContents = await Promise.all(
|
|
1357
1518
|
files.map(async (file) => ({
|
|
1358
1519
|
file,
|
|
1359
|
-
content: await (0,
|
|
1520
|
+
content: await (0, import_core4.readFileContent)(file)
|
|
1360
1521
|
}))
|
|
1361
1522
|
);
|
|
1362
1523
|
const graph = buildDependencyGraph(fileContents.filter((f) => !f.file.toLowerCase().endsWith(".py")));
|
|
@@ -1419,7 +1580,7 @@ async function analyzeContext(options) {
|
|
|
1419
1580
|
const importDepth = focus === "depth" || focus === "all" ? calculateImportDepth(file, graph) : 0;
|
|
1420
1581
|
const dependencyList = focus === "depth" || focus === "all" ? getTransitiveDependencies(file, graph) : [];
|
|
1421
1582
|
const contextBudget = focus === "all" ? calculateContextBudget(file, graph) : node.tokenCost;
|
|
1422
|
-
const cohesionScore = focus === "cohesion" || focus === "all" ? calculateCohesion(node.exports, file) : 1;
|
|
1583
|
+
const cohesionScore = focus === "cohesion" || focus === "all" ? calculateCohesion(node.exports, file, { coUsageMatrix: graph.coUsageMatrix }) : 1;
|
|
1423
1584
|
const fragmentationScore = fragmentationMap.get(file) || 0;
|
|
1424
1585
|
const relatedFiles = [];
|
|
1425
1586
|
for (const cluster of clusters) {
|
|
@@ -1728,7 +1889,7 @@ function downgradeSeverity(s) {
|
|
|
1728
1889
|
var import_chalk = __toESM(require("chalk"));
|
|
1729
1890
|
var import_fs = require("fs");
|
|
1730
1891
|
var import_path2 = require("path");
|
|
1731
|
-
var
|
|
1892
|
+
var import_core5 = require("@aiready/core");
|
|
1732
1893
|
var import_prompts = __toESM(require("prompts"));
|
|
1733
1894
|
var program = new import_commander.Command();
|
|
1734
1895
|
program.name("aiready-context").description("Analyze AI context window cost and code structure").version("0.1.0").addHelpText("after", "\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings").argument("<directory>", "Directory to analyze").option("--max-depth <number>", "Maximum acceptable import depth").option(
|
|
@@ -1759,7 +1920,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
1759
1920
|
exclude: void 0,
|
|
1760
1921
|
maxResults: 10
|
|
1761
1922
|
};
|
|
1762
|
-
let finalOptions = await (0,
|
|
1923
|
+
let finalOptions = await (0, import_core5.loadMergedConfig)(directory, defaults, {
|
|
1763
1924
|
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
|
|
1764
1925
|
maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
|
|
1765
1926
|
minCohesion: options.minCohesion ? parseFloat(options.minCohesion) : void 0,
|
|
@@ -1774,7 +1935,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
1774
1935
|
finalOptions = await runInteractiveSetup(directory, finalOptions);
|
|
1775
1936
|
}
|
|
1776
1937
|
const results = await analyzeContext(finalOptions);
|
|
1777
|
-
const elapsedTime = (0,
|
|
1938
|
+
const elapsedTime = (0, import_core5.getElapsedTime)(startTime);
|
|
1778
1939
|
const summary = generateSummary(results);
|
|
1779
1940
|
if (options.output === "json") {
|
|
1780
1941
|
const jsonOutput = {
|
|
@@ -1783,18 +1944,18 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
1783
1944
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1784
1945
|
analysisTime: elapsedTime
|
|
1785
1946
|
};
|
|
1786
|
-
const outputPath = (0,
|
|
1947
|
+
const outputPath = (0, import_core5.resolveOutputPath)(
|
|
1787
1948
|
options.outputFile,
|
|
1788
1949
|
`context-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`,
|
|
1789
1950
|
directory
|
|
1790
1951
|
);
|
|
1791
|
-
(0,
|
|
1952
|
+
(0, import_core5.handleJSONOutput)(jsonOutput, outputPath, `
|
|
1792
1953
|
\u2713 JSON report saved to ${outputPath}`);
|
|
1793
1954
|
return;
|
|
1794
1955
|
}
|
|
1795
1956
|
if (options.output === "html") {
|
|
1796
1957
|
const html = generateHTMLReport(summary, results);
|
|
1797
|
-
const outputPath = (0,
|
|
1958
|
+
const outputPath = (0, import_core5.resolveOutputPath)(
|
|
1798
1959
|
options.outputFile,
|
|
1799
1960
|
`context-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.html`,
|
|
1800
1961
|
directory
|
|
@@ -1811,7 +1972,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
1811
1972
|
displayConsoleReport(summary, results, elapsedTime, finalOptions.maxResults);
|
|
1812
1973
|
displayTuningGuidance(results, finalOptions);
|
|
1813
1974
|
} catch (error) {
|
|
1814
|
-
(0,
|
|
1975
|
+
(0, import_core5.handleCLIError)(error, "Analysis");
|
|
1815
1976
|
}
|
|
1816
1977
|
});
|
|
1817
1978
|
program.parse();
|
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScanOptions, ToolScoringOutput } from '@aiready/core';
|
|
1
|
+
import { ScanOptions, CostConfig, ToolScoringOutput } from '@aiready/core';
|
|
2
2
|
|
|
3
3
|
interface ContextAnalyzerOptions extends ScanOptions {
|
|
4
4
|
maxDepth?: number;
|
|
@@ -158,8 +158,12 @@ declare function adjustFragmentationForClassification(baseFragmentation: number,
|
|
|
158
158
|
* - Import depth (dependency chain length)
|
|
159
159
|
* - Fragmentation score (code organization)
|
|
160
160
|
* - Critical/major issues
|
|
161
|
+
*
|
|
162
|
+
* Includes business value metrics:
|
|
163
|
+
* - Estimated monthly cost of context waste
|
|
164
|
+
* - Estimated developer hours to fix
|
|
161
165
|
*/
|
|
162
|
-
declare function calculateContextScore(summary: ContextSummary): ToolScoringOutput;
|
|
166
|
+
declare function calculateContextScore(summary: ContextSummary, costConfig?: Partial<CostConfig>): ToolScoringOutput;
|
|
163
167
|
|
|
164
168
|
/**
|
|
165
169
|
* Build co-usage matrix: track which files are imported together
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScanOptions, ToolScoringOutput } from '@aiready/core';
|
|
1
|
+
import { ScanOptions, CostConfig, ToolScoringOutput } from '@aiready/core';
|
|
2
2
|
|
|
3
3
|
interface ContextAnalyzerOptions extends ScanOptions {
|
|
4
4
|
maxDepth?: number;
|
|
@@ -158,8 +158,12 @@ declare function adjustFragmentationForClassification(baseFragmentation: number,
|
|
|
158
158
|
* - Import depth (dependency chain length)
|
|
159
159
|
* - Fragmentation score (code organization)
|
|
160
160
|
* - Critical/major issues
|
|
161
|
+
*
|
|
162
|
+
* Includes business value metrics:
|
|
163
|
+
* - Estimated monthly cost of context waste
|
|
164
|
+
* - Estimated developer hours to fix
|
|
161
165
|
*/
|
|
162
|
-
declare function calculateContextScore(summary: ContextSummary): ToolScoringOutput;
|
|
166
|
+
declare function calculateContextScore(summary: ContextSummary, costConfig?: Partial<CostConfig>): ToolScoringOutput;
|
|
163
167
|
|
|
164
168
|
/**
|
|
165
169
|
* Build co-usage matrix: track which files are imported together
|