@aiready/context-analyzer 0.19.17 → 0.19.21

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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-lint.log +2 -22
  3. package/.turbo/turbo-test.log +23 -23
  4. package/coverage/analyzer.ts.html +1369 -0
  5. package/coverage/ast-utils.ts.html +382 -0
  6. package/coverage/base.css +224 -0
  7. package/coverage/block-navigation.js +87 -0
  8. package/coverage/classifier.ts.html +1771 -0
  9. package/coverage/clover.xml +1245 -0
  10. package/coverage/cluster-detector.ts.html +385 -0
  11. package/coverage/coverage-final.json +15 -0
  12. package/coverage/defaults.ts.html +262 -0
  13. package/coverage/favicon.png +0 -0
  14. package/coverage/graph-builder.ts.html +859 -0
  15. package/coverage/index.html +311 -0
  16. package/coverage/index.ts.html +124 -0
  17. package/coverage/metrics.ts.html +748 -0
  18. package/coverage/prettify.css +1 -0
  19. package/coverage/prettify.js +2 -0
  20. package/coverage/provider.ts.html +283 -0
  21. package/coverage/remediation.ts.html +502 -0
  22. package/coverage/scoring.ts.html +619 -0
  23. package/coverage/semantic-analysis.ts.html +1201 -0
  24. package/coverage/sort-arrow-sprite.png +0 -0
  25. package/coverage/sorter.js +210 -0
  26. package/coverage/summary.ts.html +625 -0
  27. package/coverage/types.ts.html +571 -0
  28. package/dist/chunk-736QSHJP.mjs +1807 -0
  29. package/dist/chunk-CCBNKQYB.mjs +1812 -0
  30. package/dist/chunk-JUHHOSHG.mjs +1808 -0
  31. package/dist/cli.js +393 -379
  32. package/dist/cli.mjs +1 -1
  33. package/dist/index.d.mts +65 -6
  34. package/dist/index.d.ts +65 -6
  35. package/dist/index.js +396 -380
  36. package/dist/index.mjs +3 -1
  37. package/package.json +2 -2
  38. package/src/__tests__/cluster-detector.test.ts +138 -0
  39. package/src/__tests__/provider.test.ts +78 -0
  40. package/src/__tests__/remediation.test.ts +94 -0
  41. package/src/analyzer.ts +244 -1
  42. package/src/classifier.ts +100 -35
  43. package/src/index.ts +1 -242
  44. package/src/provider.ts +2 -1
package/dist/index.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, import_core9.getParser)("dummy.py");
40
+ const parser = (0, import_core5.getParser)("dummy.py");
41
41
  if (!parser) {
42
42
  console.warn("Python parser not available");
43
43
  return results;
@@ -101,7 +101,7 @@ async function analyzePythonContext(files, rootDir) {
101
101
  }
102
102
  async function buildPythonDependencyGraph(files, rootDir) {
103
103
  const graph = /* @__PURE__ */ new Map();
104
- const parser = (0, import_core9.getParser)("dummy.py");
104
+ const parser = (0, import_core5.getParser)("dummy.py");
105
105
  if (!parser) return graph;
106
106
  for (const file of files) {
107
107
  try {
@@ -181,7 +181,7 @@ async function calculatePythonImportDepth(file, dependencyGraph, visited, depth
181
181
  }
182
182
  function estimateContextBudget(code, imports, dependencyGraph) {
183
183
  void dependencyGraph;
184
- let budget = (0, import_core9.estimateTokens)(code);
184
+ let budget = (0, import_core5.estimateTokens)(code);
185
185
  const avgTokensPerDep = 500;
186
186
  budget += imports.length * avgTokensPerDep;
187
187
  return budget;
@@ -231,11 +231,11 @@ function detectCircularDependencies2(file, dependencyGraph) {
231
231
  dfs(file, []);
232
232
  return [...new Set(circular)];
233
233
  }
234
- var import_core9, import_path, import_fs;
234
+ var import_core5, import_path, import_fs;
235
235
  var init_python_context = __esm({
236
236
  "src/analyzers/python-context.ts"() {
237
237
  "use strict";
238
- import_core9 = require("@aiready/core");
238
+ import_core5 = require("@aiready/core");
239
239
  import_path = require("path");
240
240
  import_fs = __toESM(require("fs"));
241
241
  }
@@ -244,6 +244,7 @@ var init_python_context = __esm({
244
244
  // src/index.ts
245
245
  var index_exports = {};
246
246
  __export(index_exports, {
247
+ Classification: () => Classification,
247
248
  ContextAnalyzerProvider: () => ContextAnalyzerProvider,
248
249
  adjustCohesionForClassification: () => adjustCohesionForClassification,
249
250
  adjustFragmentationForClassification: () => adjustFragmentationForClassification,
@@ -292,8 +293,11 @@ __export(index_exports, {
292
293
  module.exports = __toCommonJS(index_exports);
293
294
  var import_core10 = require("@aiready/core");
294
295
 
296
+ // src/provider.ts
297
+ var import_core8 = require("@aiready/core");
298
+
295
299
  // src/analyzer.ts
296
- var import_core4 = require("@aiready/core");
300
+ var import_core6 = require("@aiready/core");
297
301
 
298
302
  // src/metrics.ts
299
303
  var import_core2 = require("@aiready/core");
@@ -725,8 +729,143 @@ function calculateDirectoryDistance(files) {
725
729
  return comparisons > 0 ? totalNormalized / comparisons : 0;
726
730
  }
727
731
 
728
- // src/graph-builder.ts
732
+ // src/summary.ts
729
733
  var import_core3 = require("@aiready/core");
734
+ function generateSummary(results, options) {
735
+ const config = options ? Object.fromEntries(
736
+ Object.entries(options).filter(
737
+ ([key]) => !import_core3.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
738
+ )
739
+ ) : void 0;
740
+ if (results.length === 0) {
741
+ return {
742
+ totalFiles: 0,
743
+ totalTokens: 0,
744
+ avgContextBudget: 0,
745
+ maxContextBudget: 0,
746
+ avgImportDepth: 0,
747
+ maxImportDepth: 0,
748
+ deepFiles: [],
749
+ avgFragmentation: 0,
750
+ fragmentedModules: [],
751
+ avgCohesion: 0,
752
+ lowCohesionFiles: [],
753
+ criticalIssues: 0,
754
+ majorIssues: 0,
755
+ minorIssues: 0,
756
+ totalPotentialSavings: 0,
757
+ topExpensiveFiles: [],
758
+ config
759
+ };
760
+ }
761
+ const totalFiles = results.length;
762
+ const totalTokens = results.reduce((sum, r) => sum + r.tokenCost, 0);
763
+ const totalContextBudget = results.reduce(
764
+ (sum, r) => sum + r.contextBudget,
765
+ 0
766
+ );
767
+ const avgContextBudget = totalContextBudget / totalFiles;
768
+ const maxContextBudget = Math.max(...results.map((r) => r.contextBudget));
769
+ const avgImportDepth = results.reduce((sum, r) => sum + r.importDepth, 0) / totalFiles;
770
+ const maxImportDepth = Math.max(...results.map((r) => r.importDepth));
771
+ const deepFiles = results.filter((r) => r.importDepth >= 5).map((r) => ({ file: r.file, depth: r.importDepth })).sort((a, b) => b.depth - a.depth).slice(0, 10);
772
+ const avgFragmentation = results.reduce((sum, r) => sum + r.fragmentationScore, 0) / totalFiles;
773
+ const moduleMap = /* @__PURE__ */ new Map();
774
+ for (const result of results) {
775
+ for (const domain of result.domains) {
776
+ if (!moduleMap.has(domain)) moduleMap.set(domain, []);
777
+ moduleMap.get(domain).push(result);
778
+ }
779
+ }
780
+ const fragmentedModules = [];
781
+ for (const [domain, files] of moduleMap.entries()) {
782
+ let jaccard2 = function(a, b) {
783
+ const s1 = new Set(a || []);
784
+ const s2 = new Set(b || []);
785
+ if (s1.size === 0 && s2.size === 0) return 0;
786
+ const inter = new Set([...s1].filter((x) => s2.has(x)));
787
+ const uni = /* @__PURE__ */ new Set([...s1, ...s2]);
788
+ return uni.size === 0 ? 0 : inter.size / uni.size;
789
+ };
790
+ var jaccard = jaccard2;
791
+ if (files.length < 2) continue;
792
+ const fragmentationScore = files.reduce((sum, f) => sum + f.fragmentationScore, 0) / files.length;
793
+ if (fragmentationScore < 0.3) continue;
794
+ const totalTokens2 = files.reduce((sum, f) => sum + f.tokenCost, 0);
795
+ const avgCohesion2 = files.reduce((sum, f) => sum + f.cohesionScore, 0) / files.length;
796
+ const targetFiles = Math.max(1, Math.ceil(files.length / 3));
797
+ const filePaths = files.map((f) => f.file);
798
+ const pathEntropy = calculatePathEntropy(filePaths);
799
+ const directoryDistance = calculateDirectoryDistance(filePaths);
800
+ let importSimTotal = 0;
801
+ let importPairs = 0;
802
+ for (let i = 0; i < files.length; i++) {
803
+ for (let j = i + 1; j < files.length; j++) {
804
+ importSimTotal += jaccard2(
805
+ files[i].dependencyList || [],
806
+ files[j].dependencyList || []
807
+ );
808
+ importPairs++;
809
+ }
810
+ }
811
+ const importCohesion = importPairs > 0 ? importSimTotal / importPairs : 0;
812
+ fragmentedModules.push({
813
+ domain,
814
+ files: files.map((f) => f.file),
815
+ totalTokens: totalTokens2,
816
+ fragmentationScore,
817
+ avgCohesion: avgCohesion2,
818
+ importCohesion,
819
+ pathEntropy,
820
+ directoryDistance,
821
+ suggestedStructure: {
822
+ targetFiles,
823
+ consolidationPlan: [
824
+ `Consolidate ${files.length} files across ${new Set(files.map((f) => f.file.split("/").slice(0, -1).join("/"))).size} directories`,
825
+ `Target ~${targetFiles} core modules to reduce context switching`
826
+ ]
827
+ }
828
+ });
829
+ }
830
+ const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / totalFiles;
831
+ const lowCohesionFiles = results.filter((r) => r.cohesionScore < 0.4).map((r) => ({ file: r.file, score: r.cohesionScore })).sort((a, b) => a.score - b.score).slice(0, 10);
832
+ const criticalIssues = results.filter(
833
+ (r) => r.severity === "critical"
834
+ ).length;
835
+ const majorIssues = results.filter((r) => r.severity === "major").length;
836
+ const minorIssues = results.filter((r) => r.severity === "minor").length;
837
+ const totalPotentialSavings = results.reduce(
838
+ (sum, r) => sum + r.potentialSavings,
839
+ 0
840
+ );
841
+ const topExpensiveFiles = results.sort((a, b) => b.contextBudget - a.contextBudget).slice(0, 10).map((r) => ({
842
+ file: r.file,
843
+ contextBudget: r.contextBudget,
844
+ severity: r.severity
845
+ }));
846
+ return {
847
+ totalFiles,
848
+ totalTokens,
849
+ avgContextBudget,
850
+ maxContextBudget,
851
+ avgImportDepth,
852
+ maxImportDepth,
853
+ deepFiles,
854
+ avgFragmentation,
855
+ fragmentedModules,
856
+ avgCohesion,
857
+ lowCohesionFiles,
858
+ criticalIssues,
859
+ majorIssues,
860
+ minorIssues,
861
+ totalPotentialSavings,
862
+ topExpensiveFiles,
863
+ config
864
+ };
865
+ }
866
+
867
+ // src/graph-builder.ts
868
+ var import_core4 = require("@aiready/core");
730
869
  function extractDomainKeywordsFromPaths(files) {
731
870
  const folderNames = /* @__PURE__ */ new Set();
732
871
  for (const { file } of files) {
@@ -778,7 +917,7 @@ function buildDependencyGraph(files, options) {
778
917
  const edges = /* @__PURE__ */ new Map();
779
918
  const autoDetectedKeywords = options?.domainKeywords ?? extractDomainKeywordsFromPaths(files);
780
919
  for (const { file, content } of files) {
781
- const { imports: astImports } = (0, import_core3.parseFileExports)(content, file);
920
+ const { imports: astImports } = (0, import_core4.parseFileExports)(content, file);
782
921
  const importSources = astImports.map((i) => i.source);
783
922
  const exports2 = extractExportsWithAST(
784
923
  content,
@@ -786,7 +925,7 @@ function buildDependencyGraph(files, options) {
786
925
  { domainKeywords: autoDetectedKeywords },
787
926
  importSources
788
927
  );
789
- const tokenCost = (0, import_core3.estimateTokens)(content);
928
+ const tokenCost = (0, import_core4.estimateTokens)(content);
790
929
  const linesOfCode = content.split("\n").length;
791
930
  nodes.set(file, {
792
931
  file,
@@ -893,48 +1032,62 @@ function detectCircularDependencies(graph) {
893
1032
  }
894
1033
 
895
1034
  // src/classifier.ts
1035
+ var Classification = {
1036
+ BARREL: "barrel-export",
1037
+ TYPE_DEFINITION: "type-definition",
1038
+ NEXTJS_PAGE: "nextjs-page",
1039
+ LAMBDA_HANDLER: "lambda-handler",
1040
+ SERVICE: "service-file",
1041
+ EMAIL_TEMPLATE: "email-template",
1042
+ PARSER: "parser-file",
1043
+ COHESIVE_MODULE: "cohesive-module",
1044
+ UTILITY_MODULE: "utility-module",
1045
+ MIXED_CONCERNS: "mixed-concerns",
1046
+ UNKNOWN: "unknown"
1047
+ };
896
1048
  function classifyFile(node, cohesionScore = 1, domains = []) {
897
1049
  if (isBarrelExport(node)) {
898
- return "barrel-export";
1050
+ return Classification.BARREL;
899
1051
  }
900
1052
  if (isTypeDefinition(node)) {
901
- return "type-definition";
1053
+ return Classification.TYPE_DEFINITION;
902
1054
  }
903
1055
  if (isNextJsPage(node)) {
904
- return "nextjs-page";
1056
+ return Classification.NEXTJS_PAGE;
905
1057
  }
906
1058
  if (isLambdaHandler(node)) {
907
- return "lambda-handler";
1059
+ return Classification.LAMBDA_HANDLER;
908
1060
  }
909
1061
  if (isServiceFile(node)) {
910
- return "service-file";
1062
+ return Classification.SERVICE;
911
1063
  }
912
1064
  if (isEmailTemplate(node)) {
913
- return "email-template";
1065
+ return Classification.EMAIL_TEMPLATE;
914
1066
  }
915
1067
  if (isParserFile(node)) {
916
- return "parser-file";
1068
+ return Classification.PARSER;
917
1069
  }
918
1070
  if (isSessionFile(node)) {
919
- if (cohesionScore >= 0.25 && domains.length <= 1) return "cohesive-module";
920
- return "utility-module";
1071
+ if (cohesionScore >= 0.25 && domains.length <= 1)
1072
+ return Classification.COHESIVE_MODULE;
1073
+ return Classification.UTILITY_MODULE;
921
1074
  }
922
1075
  if (isUtilityModule(node)) {
923
- return "utility-module";
1076
+ return Classification.UTILITY_MODULE;
924
1077
  }
925
1078
  if (isConfigFile(node)) {
926
- return "cohesive-module";
1079
+ return Classification.COHESIVE_MODULE;
927
1080
  }
928
1081
  if (domains.length <= 1 && domains[0] !== "unknown") {
929
- return "cohesive-module";
1082
+ return Classification.COHESIVE_MODULE;
930
1083
  }
931
1084
  if (domains.length > 1 && cohesionScore < 0.4) {
932
- return "mixed-concerns";
1085
+ return Classification.MIXED_CONCERNS;
933
1086
  }
934
1087
  if (cohesionScore >= 0.7) {
935
- return "cohesive-module";
1088
+ return Classification.COHESIVE_MODULE;
936
1089
  }
937
- return "unknown";
1090
+ return Classification.UNKNOWN;
938
1091
  }
939
1092
  function isBarrelExport(node) {
940
1093
  const { file, exports: exports2 } = node;
@@ -1097,13 +1250,13 @@ function isNextJsPage(node) {
1097
1250
  }
1098
1251
  function adjustCohesionForClassification(baseCohesion, classification, node) {
1099
1252
  switch (classification) {
1100
- case "barrel-export":
1253
+ case Classification.BARREL:
1101
1254
  return 1;
1102
- case "type-definition":
1255
+ case Classification.TYPE_DEFINITION:
1103
1256
  return 1;
1104
- case "nextjs-page":
1257
+ case Classification.NEXTJS_PAGE:
1105
1258
  return 1;
1106
- case "utility-module": {
1259
+ case Classification.UTILITY_MODULE: {
1107
1260
  if (node && hasRelatedExportNames(
1108
1261
  (node.exports || []).map((e) => e.name.toLowerCase())
1109
1262
  )) {
@@ -1111,17 +1264,17 @@ function adjustCohesionForClassification(baseCohesion, classification, node) {
1111
1264
  }
1112
1265
  return Math.max(0.75, Math.min(1, baseCohesion + 0.35));
1113
1266
  }
1114
- case "service-file":
1267
+ case Classification.SERVICE:
1115
1268
  return Math.max(0.72, Math.min(1, baseCohesion + 0.3));
1116
- case "lambda-handler":
1269
+ case Classification.LAMBDA_HANDLER:
1117
1270
  return Math.max(0.75, Math.min(1, baseCohesion + 0.35));
1118
- case "email-template":
1271
+ case Classification.EMAIL_TEMPLATE:
1119
1272
  return Math.max(0.72, Math.min(1, baseCohesion + 0.3));
1120
- case "parser-file":
1273
+ case Classification.PARSER:
1121
1274
  return Math.max(0.7, Math.min(1, baseCohesion + 0.3));
1122
- case "cohesive-module":
1275
+ case Classification.COHESIVE_MODULE:
1123
1276
  return Math.max(baseCohesion, 0.7);
1124
- case "mixed-concerns":
1277
+ case Classification.MIXED_CONCERNS:
1125
1278
  return baseCohesion;
1126
1279
  default:
1127
1280
  return Math.min(1, baseCohesion + 0.1);
@@ -1170,20 +1323,20 @@ function hasRelatedExportNames(exportNames) {
1170
1323
  }
1171
1324
  function adjustFragmentationForClassification(baseFragmentation, classification) {
1172
1325
  switch (classification) {
1173
- case "barrel-export":
1326
+ case Classification.BARREL:
1174
1327
  return 0;
1175
- case "type-definition":
1328
+ case Classification.TYPE_DEFINITION:
1176
1329
  return 0;
1177
- case "utility-module":
1178
- case "service-file":
1179
- case "lambda-handler":
1180
- case "email-template":
1181
- case "parser-file":
1182
- case "nextjs-page":
1330
+ case Classification.UTILITY_MODULE:
1331
+ case Classification.SERVICE:
1332
+ case Classification.LAMBDA_HANDLER:
1333
+ case Classification.EMAIL_TEMPLATE:
1334
+ case Classification.PARSER:
1335
+ case Classification.NEXTJS_PAGE:
1183
1336
  return baseFragmentation * 0.2;
1184
- case "cohesive-module":
1337
+ case Classification.COHESIVE_MODULE:
1185
1338
  return baseFragmentation * 0.3;
1186
- case "mixed-concerns":
1339
+ case Classification.MIXED_CONCERNS:
1187
1340
  return baseFragmentation;
1188
1341
  default:
1189
1342
  return baseFragmentation * 0.7;
@@ -1394,10 +1547,10 @@ function analyzeIssues(params) {
1394
1547
  } = params;
1395
1548
  const issues = [];
1396
1549
  const recommendations = [];
1397
- let severity = import_core4.Severity.Info;
1550
+ let severity = import_core6.Severity.Info;
1398
1551
  let potentialSavings = 0;
1399
1552
  if (circularDeps.length > 0) {
1400
- severity = import_core4.Severity.Critical;
1553
+ severity = import_core6.Severity.Critical;
1401
1554
  issues.push(`Part of ${circularDeps.length} circular dependency chain(s)`);
1402
1555
  recommendations.push(
1403
1556
  "Break circular dependencies by extracting interfaces or using dependency injection"
@@ -1405,12 +1558,12 @@ function analyzeIssues(params) {
1405
1558
  potentialSavings += contextBudget * 0.2;
1406
1559
  }
1407
1560
  if (importDepth > maxDepth * 1.5) {
1408
- severity = import_core4.Severity.Critical;
1561
+ severity = import_core6.Severity.Critical;
1409
1562
  issues.push(`Import depth ${importDepth} exceeds limit by 50%`);
1410
1563
  recommendations.push("Flatten dependency tree or use facade pattern");
1411
1564
  potentialSavings += contextBudget * 0.3;
1412
1565
  } else if (importDepth > maxDepth) {
1413
- if (severity !== import_core4.Severity.Critical) severity = import_core4.Severity.Major;
1566
+ if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
1414
1567
  issues.push(
1415
1568
  `Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`
1416
1569
  );
@@ -1418,7 +1571,7 @@ function analyzeIssues(params) {
1418
1571
  potentialSavings += contextBudget * 0.15;
1419
1572
  }
1420
1573
  if (contextBudget > maxContextBudget * 1.5) {
1421
- severity = import_core4.Severity.Critical;
1574
+ severity = import_core6.Severity.Critical;
1422
1575
  issues.push(
1423
1576
  `Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
1424
1577
  );
@@ -1427,7 +1580,7 @@ function analyzeIssues(params) {
1427
1580
  );
1428
1581
  potentialSavings += contextBudget * 0.4;
1429
1582
  } else if (contextBudget > maxContextBudget) {
1430
- if (severity !== import_core4.Severity.Critical) severity = import_core4.Severity.Major;
1583
+ if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
1431
1584
  issues.push(
1432
1585
  `Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
1433
1586
  );
@@ -1435,7 +1588,7 @@ function analyzeIssues(params) {
1435
1588
  potentialSavings += contextBudget * 0.2;
1436
1589
  }
1437
1590
  if (cohesionScore < minCohesion * 0.5) {
1438
- if (severity !== import_core4.Severity.Critical) severity = import_core4.Severity.Major;
1591
+ if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
1439
1592
  issues.push(
1440
1593
  `Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`
1441
1594
  );
@@ -1444,14 +1597,14 @@ function analyzeIssues(params) {
1444
1597
  );
1445
1598
  potentialSavings += contextBudget * 0.25;
1446
1599
  } else if (cohesionScore < minCohesion) {
1447
- if (severity === import_core4.Severity.Info) severity = import_core4.Severity.Minor;
1600
+ if (severity === import_core6.Severity.Info) severity = import_core6.Severity.Minor;
1448
1601
  issues.push(`Low cohesion (${(cohesionScore * 100).toFixed(0)}%)`);
1449
1602
  recommendations.push("Consider grouping related exports together");
1450
1603
  potentialSavings += contextBudget * 0.1;
1451
1604
  }
1452
1605
  if (fragmentationScore > maxFragmentation) {
1453
- if (severity === import_core4.Severity.Info || severity === import_core4.Severity.Minor)
1454
- severity = import_core4.Severity.Minor;
1606
+ if (severity === import_core6.Severity.Info || severity === import_core6.Severity.Minor)
1607
+ severity = import_core6.Severity.Minor;
1455
1608
  issues.push(
1456
1609
  `High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`
1457
1610
  );
@@ -1465,7 +1618,7 @@ function analyzeIssues(params) {
1465
1618
  if (isBuildArtifact(file)) {
1466
1619
  issues.push("Detected build artifact (bundled/output file)");
1467
1620
  recommendations.push("Exclude build outputs from analysis");
1468
- severity = import_core4.Severity.Info;
1621
+ severity = import_core6.Severity.Info;
1469
1622
  potentialSavings = 0;
1470
1623
  }
1471
1624
  return {
@@ -1479,147 +1632,192 @@ function isBuildArtifact(filePath) {
1479
1632
  const lower = filePath.toLowerCase();
1480
1633
  return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/.next/");
1481
1634
  }
1482
-
1483
- // src/summary.ts
1484
- var import_core5 = require("@aiready/core");
1485
- function generateSummary(results, options) {
1486
- const config = options ? Object.fromEntries(
1487
- Object.entries(options).filter(
1488
- ([key]) => !import_core5.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
1489
- )
1490
- ) : void 0;
1491
- if (results.length === 0) {
1492
- return {
1493
- totalFiles: 0,
1494
- totalTokens: 0,
1495
- avgContextBudget: 0,
1496
- maxContextBudget: 0,
1497
- avgImportDepth: 0,
1498
- maxImportDepth: 0,
1499
- deepFiles: [],
1500
- avgFragmentation: 0,
1501
- fragmentedModules: [],
1502
- avgCohesion: 0,
1503
- lowCohesionFiles: [],
1504
- criticalIssues: 0,
1505
- majorIssues: 0,
1506
- minorIssues: 0,
1507
- totalPotentialSavings: 0,
1508
- topExpensiveFiles: [],
1509
- config
1510
- };
1511
- }
1512
- const totalFiles = results.length;
1513
- const totalTokens = results.reduce((sum, r) => sum + r.tokenCost, 0);
1514
- const totalContextBudget = results.reduce(
1515
- (sum, r) => sum + r.contextBudget,
1516
- 0
1635
+ async function analyzeContext(options) {
1636
+ const {
1637
+ maxDepth = 5,
1638
+ maxContextBudget = 1e4,
1639
+ minCohesion = 0.6,
1640
+ maxFragmentation = 0.5,
1641
+ focus = "all",
1642
+ includeNodeModules = false,
1643
+ ...scanOptions
1644
+ } = options;
1645
+ const files = await (0, import_core6.scanFiles)({
1646
+ ...scanOptions,
1647
+ exclude: includeNodeModules && scanOptions.exclude ? scanOptions.exclude.filter(
1648
+ (pattern) => pattern !== "**/node_modules/**"
1649
+ ) : scanOptions.exclude
1650
+ });
1651
+ const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
1652
+ const fileContents = await Promise.all(
1653
+ files.map(async (file) => ({
1654
+ file,
1655
+ content: await (0, import_core6.readFileContent)(file)
1656
+ }))
1517
1657
  );
1518
- const avgContextBudget = totalContextBudget / totalFiles;
1519
- const maxContextBudget = Math.max(...results.map((r) => r.contextBudget));
1520
- const avgImportDepth = results.reduce((sum, r) => sum + r.importDepth, 0) / totalFiles;
1521
- const maxImportDepth = Math.max(...results.map((r) => r.importDepth));
1522
- const deepFiles = results.filter((r) => r.importDepth >= 5).map((r) => ({ file: r.file, depth: r.importDepth })).sort((a, b) => b.depth - a.depth).slice(0, 10);
1523
- const avgFragmentation = results.reduce((sum, r) => sum + r.fragmentationScore, 0) / totalFiles;
1524
- const moduleMap = /* @__PURE__ */ new Map();
1525
- for (const result of results) {
1526
- for (const domain of result.domains) {
1527
- if (!moduleMap.has(domain)) moduleMap.set(domain, []);
1528
- moduleMap.get(domain).push(result);
1529
- }
1530
- }
1531
- const fragmentedModules = [];
1532
- for (const [domain, files] of moduleMap.entries()) {
1533
- let jaccard2 = function(a, b) {
1534
- const s1 = new Set(a || []);
1535
- const s2 = new Set(b || []);
1536
- if (s1.size === 0 && s2.size === 0) return 0;
1537
- const inter = new Set([...s1].filter((x) => s2.has(x)));
1538
- const uni = /* @__PURE__ */ new Set([...s1, ...s2]);
1539
- return uni.size === 0 ? 0 : inter.size / uni.size;
1540
- };
1541
- var jaccard = jaccard2;
1542
- if (files.length < 2) continue;
1543
- const fragmentationScore = files.reduce((sum, f) => sum + f.fragmentationScore, 0) / files.length;
1544
- if (fragmentationScore < 0.3) continue;
1545
- const totalTokens2 = files.reduce((sum, f) => sum + f.tokenCost, 0);
1546
- const avgCohesion2 = files.reduce((sum, f) => sum + f.cohesionScore, 0) / files.length;
1547
- const targetFiles = Math.max(1, Math.ceil(files.length / 3));
1548
- const filePaths = files.map((f) => f.file);
1549
- const pathEntropy = calculatePathEntropy(filePaths);
1550
- const directoryDistance = calculateDirectoryDistance(filePaths);
1551
- let importSimTotal = 0;
1552
- let importPairs = 0;
1553
- for (let i = 0; i < files.length; i++) {
1554
- for (let j = i + 1; j < files.length; j++) {
1555
- importSimTotal += jaccard2(
1556
- files[i].dependencyList || [],
1557
- files[j].dependencyList || []
1558
- );
1559
- importPairs++;
1658
+ const graph = buildDependencyGraph(
1659
+ fileContents.filter((f) => !f.file.toLowerCase().endsWith(".py"))
1660
+ );
1661
+ let pythonResults = [];
1662
+ if (pythonFiles.length > 0) {
1663
+ const { analyzePythonContext: analyzePythonContext2 } = await Promise.resolve().then(() => (init_python_context(), python_context_exports));
1664
+ const pythonMetrics = await analyzePythonContext2(
1665
+ pythonFiles,
1666
+ scanOptions.rootDir || options.rootDir || "."
1667
+ );
1668
+ pythonResults = pythonMetrics.map((metric) => {
1669
+ const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
1670
+ file: metric.file,
1671
+ importDepth: metric.importDepth,
1672
+ contextBudget: metric.contextBudget,
1673
+ cohesionScore: metric.cohesion,
1674
+ fragmentationScore: 0,
1675
+ maxDepth,
1676
+ maxContextBudget,
1677
+ minCohesion,
1678
+ maxFragmentation,
1679
+ circularDeps: metric.metrics.circularDependencies.map(
1680
+ (cycle) => cycle.split(" \u2192 ")
1681
+ )
1682
+ });
1683
+ return {
1684
+ file: metric.file,
1685
+ tokenCost: Math.floor(
1686
+ metric.contextBudget / (1 + metric.imports.length || 1)
1687
+ ),
1688
+ linesOfCode: metric.metrics.linesOfCode,
1689
+ importDepth: metric.importDepth,
1690
+ dependencyCount: metric.imports.length,
1691
+ dependencyList: metric.imports.map(
1692
+ (imp) => imp.resolvedPath || imp.source
1693
+ ),
1694
+ circularDeps: metric.metrics.circularDependencies.map(
1695
+ (cycle) => cycle.split(" \u2192 ")
1696
+ ),
1697
+ cohesionScore: metric.cohesion,
1698
+ domains: ["python"],
1699
+ exportCount: metric.exports.length,
1700
+ contextBudget: metric.contextBudget,
1701
+ fragmentationScore: 0,
1702
+ relatedFiles: [],
1703
+ fileClassification: "unknown",
1704
+ severity,
1705
+ issues,
1706
+ recommendations,
1707
+ potentialSavings
1708
+ };
1709
+ });
1710
+ }
1711
+ const circularDeps = detectCircularDependencies(graph);
1712
+ const useLogScale = files.length >= 500;
1713
+ const clusters = detectModuleClusters(graph, { useLogScale });
1714
+ const fragmentationMap = /* @__PURE__ */ new Map();
1715
+ for (const cluster of clusters) {
1716
+ for (const file of cluster.files) {
1717
+ fragmentationMap.set(file, cluster.fragmentationScore);
1718
+ }
1719
+ }
1720
+ const results = [];
1721
+ for (const { file } of fileContents) {
1722
+ const node = graph.nodes.get(file);
1723
+ if (!node) continue;
1724
+ const importDepth = focus === "depth" || focus === "all" ? calculateImportDepth(file, graph) : 0;
1725
+ const dependencyList = focus === "depth" || focus === "all" ? getTransitiveDependencies(file, graph) : [];
1726
+ const contextBudget = focus === "all" ? calculateContextBudget(file, graph) : node.tokenCost;
1727
+ const cohesionScore = focus === "cohesion" || focus === "all" ? calculateCohesion(node.exports, file, {
1728
+ coUsageMatrix: graph.coUsageMatrix
1729
+ }) : 1;
1730
+ const fragmentationScore = fragmentationMap.get(file) || 0;
1731
+ const relatedFiles = [];
1732
+ for (const cluster of clusters) {
1733
+ if (cluster.files.includes(file)) {
1734
+ relatedFiles.push(...cluster.files.filter((f) => f !== file));
1735
+ break;
1560
1736
  }
1561
1737
  }
1562
- const importCohesion = importPairs > 0 ? importSimTotal / importPairs : 0;
1563
- fragmentedModules.push({
1564
- domain,
1565
- files: files.map((f) => f.file),
1566
- totalTokens: totalTokens2,
1738
+ const { issues } = analyzeIssues({
1739
+ file,
1740
+ importDepth,
1741
+ contextBudget,
1742
+ cohesionScore,
1567
1743
  fragmentationScore,
1568
- avgCohesion: avgCohesion2,
1569
- importCohesion,
1570
- pathEntropy,
1571
- directoryDistance,
1572
- suggestedStructure: {
1573
- targetFiles,
1574
- consolidationPlan: [
1575
- `Consolidate ${files.length} files across ${new Set(files.map((f) => f.file.split("/").slice(0, -1).join("/"))).size} directories`,
1576
- `Target ~${targetFiles} core modules to reduce context switching`
1577
- ]
1578
- }
1744
+ maxDepth,
1745
+ maxContextBudget,
1746
+ minCohesion,
1747
+ maxFragmentation,
1748
+ circularDeps
1749
+ });
1750
+ const domains = [
1751
+ ...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
1752
+ ];
1753
+ const fileClassification = classifyFile(node);
1754
+ const adjustedCohesionScore = adjustCohesionForClassification(
1755
+ cohesionScore,
1756
+ fileClassification,
1757
+ node
1758
+ );
1759
+ const adjustedFragmentationScore = adjustFragmentationForClassification(
1760
+ fragmentationScore,
1761
+ fileClassification
1762
+ );
1763
+ const classificationRecommendations = getClassificationRecommendations(
1764
+ fileClassification,
1765
+ file,
1766
+ issues
1767
+ );
1768
+ const {
1769
+ severity: adjustedSeverity,
1770
+ issues: adjustedIssues,
1771
+ recommendations: finalRecommendations,
1772
+ potentialSavings: adjustedSavings
1773
+ } = analyzeIssues({
1774
+ file,
1775
+ importDepth,
1776
+ contextBudget,
1777
+ cohesionScore: adjustedCohesionScore,
1778
+ fragmentationScore: adjustedFragmentationScore,
1779
+ maxDepth,
1780
+ maxContextBudget,
1781
+ minCohesion,
1782
+ maxFragmentation,
1783
+ circularDeps
1784
+ });
1785
+ results.push({
1786
+ file,
1787
+ tokenCost: node.tokenCost,
1788
+ linesOfCode: node.linesOfCode,
1789
+ importDepth,
1790
+ dependencyCount: dependencyList.length,
1791
+ dependencyList,
1792
+ circularDeps: circularDeps.filter((cycle) => cycle.includes(file)),
1793
+ cohesionScore: adjustedCohesionScore,
1794
+ domains,
1795
+ exportCount: node.exports.length,
1796
+ contextBudget,
1797
+ fragmentationScore: adjustedFragmentationScore,
1798
+ relatedFiles,
1799
+ fileClassification,
1800
+ severity: adjustedSeverity,
1801
+ issues: adjustedIssues,
1802
+ recommendations: [
1803
+ ...finalRecommendations,
1804
+ ...classificationRecommendations.slice(0, 1)
1805
+ ],
1806
+ potentialSavings: adjustedSavings
1579
1807
  });
1580
1808
  }
1581
- const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / totalFiles;
1582
- const lowCohesionFiles = results.filter((r) => r.cohesionScore < 0.4).map((r) => ({ file: r.file, score: r.cohesionScore })).sort((a, b) => a.score - b.score).slice(0, 10);
1583
- const criticalIssues = results.filter(
1584
- (r) => r.severity === "critical"
1585
- ).length;
1586
- const majorIssues = results.filter((r) => r.severity === "major").length;
1587
- const minorIssues = results.filter((r) => r.severity === "minor").length;
1588
- const totalPotentialSavings = results.reduce(
1589
- (sum, r) => sum + r.potentialSavings,
1590
- 0
1591
- );
1592
- const topExpensiveFiles = results.sort((a, b) => b.contextBudget - a.contextBudget).slice(0, 10).map((r) => ({
1593
- file: r.file,
1594
- contextBudget: r.contextBudget,
1595
- severity: r.severity
1596
- }));
1597
- return {
1598
- totalFiles,
1599
- totalTokens,
1600
- avgContextBudget,
1601
- maxContextBudget,
1602
- avgImportDepth,
1603
- maxImportDepth,
1604
- deepFiles,
1605
- avgFragmentation,
1606
- fragmentedModules,
1607
- avgCohesion,
1608
- lowCohesionFiles,
1609
- criticalIssues,
1610
- majorIssues,
1611
- minorIssues,
1612
- totalPotentialSavings,
1613
- topExpensiveFiles,
1614
- config
1615
- };
1809
+ const allResults = [...results, ...pythonResults];
1810
+ const finalSummary = generateSummary(allResults, options);
1811
+ return allResults.sort((a, b) => {
1812
+ const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
1813
+ const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
1814
+ if (severityDiff !== 0) return severityDiff;
1815
+ return b.contextBudget - a.contextBudget;
1816
+ });
1616
1817
  }
1617
1818
 
1618
- // src/provider.ts
1619
- var import_core7 = require("@aiready/core");
1620
-
1621
1819
  // src/scoring.ts
1622
- var import_core6 = require("@aiready/core");
1820
+ var import_core7 = require("@aiready/core");
1623
1821
  function calculateContextScore(summary, costConfig) {
1624
1822
  const {
1625
1823
  avgContextBudget,
@@ -1715,8 +1913,8 @@ function calculateContextScore(summary, costConfig) {
1715
1913
  priority: "high"
1716
1914
  });
1717
1915
  }
1718
- const cfg = { ...import_core6.DEFAULT_COST_CONFIG, ...costConfig };
1719
- const estimatedMonthlyCost = (0, import_core6.calculateMonthlyCost)(
1916
+ const cfg = { ...import_core7.DEFAULT_COST_CONFIG, ...costConfig };
1917
+ const estimatedMonthlyCost = (0, import_core7.calculateMonthlyCost)(
1720
1918
  avgContextBudget * (summary.totalFiles || 1),
1721
1919
  cfg
1722
1920
  );
@@ -1724,9 +1922,9 @@ function calculateContextScore(summary, costConfig) {
1724
1922
  ...Array(criticalIssues).fill({ severity: "critical" }),
1725
1923
  ...Array(majorIssues).fill({ severity: "major" })
1726
1924
  ];
1727
- const productivityImpact = (0, import_core6.calculateProductivityImpact)(issues);
1925
+ const productivityImpact = (0, import_core7.calculateProductivityImpact)(issues);
1728
1926
  return {
1729
- toolName: import_core6.ToolName.ContextAnalyzer,
1927
+ toolName: import_core7.ToolName.ContextAnalyzer,
1730
1928
  score,
1731
1929
  rawMetrics: {
1732
1930
  avgContextBudget: Math.round(avgContextBudget),
@@ -1753,7 +1951,7 @@ function mapScoreToRating(score) {
1753
1951
 
1754
1952
  // src/provider.ts
1755
1953
  var ContextAnalyzerProvider = {
1756
- id: import_core7.ToolName.ContextAnalyzer,
1954
+ id: import_core8.ToolName.ContextAnalyzer,
1757
1955
  alias: ["context", "fragmentation", "budget"],
1758
1956
  async analyze(options) {
1759
1957
  const results = await analyzeContext(options);
@@ -1762,7 +1960,7 @@ var ContextAnalyzerProvider = {
1762
1960
  (r) => ({
1763
1961
  fileName: r.file,
1764
1962
  issues: r.issues.map((msg) => ({
1765
- type: import_core7.IssueType.ContextFragmentation,
1963
+ type: import_core8.IssueType.ContextFragmentation,
1766
1964
  severity: r.severity,
1767
1965
  message: msg,
1768
1966
  location: { file: r.file, line: 1 },
@@ -1775,13 +1973,13 @@ var ContextAnalyzerProvider = {
1775
1973
  }
1776
1974
  })
1777
1975
  );
1778
- return import_core7.SpokeOutputSchema.parse({
1976
+ return import_core8.SpokeOutputSchema.parse({
1779
1977
  results: normalizedResults,
1780
1978
  summary: {
1781
1979
  ...summary
1782
1980
  },
1783
1981
  metadata: {
1784
- toolName: import_core7.ToolName.ContextAnalyzer,
1982
+ toolName: import_core8.ToolName.ContextAnalyzer,
1785
1983
  version: "0.17.5",
1786
1984
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1787
1985
  }
@@ -1795,9 +1993,9 @@ var ContextAnalyzerProvider = {
1795
1993
  };
1796
1994
 
1797
1995
  // src/defaults.ts
1798
- var import_core8 = require("@aiready/core");
1996
+ var import_core9 = require("@aiready/core");
1799
1997
  async function getSmartDefaults(directory, userOptions) {
1800
- const files = await (0, import_core8.scanFiles)({
1998
+ const files = await (0, import_core9.scanFiles)({
1801
1999
  rootDir: directory,
1802
2000
  include: userOptions.include,
1803
2001
  exclude: userOptions.exclude
@@ -1843,191 +2041,9 @@ async function getSmartDefaults(directory, userOptions) {
1843
2041
 
1844
2042
  // src/index.ts
1845
2043
  import_core10.ToolRegistry.register(ContextAnalyzerProvider);
1846
- async function analyzeContext(options) {
1847
- const {
1848
- maxDepth = 5,
1849
- maxContextBudget = 1e4,
1850
- minCohesion = 0.6,
1851
- maxFragmentation = 0.5,
1852
- focus = "all",
1853
- includeNodeModules = false,
1854
- ...scanOptions
1855
- } = options;
1856
- const files = await (0, import_core10.scanFiles)({
1857
- ...scanOptions,
1858
- exclude: includeNodeModules && scanOptions.exclude ? scanOptions.exclude.filter(
1859
- (pattern) => pattern !== "**/node_modules/**"
1860
- ) : scanOptions.exclude
1861
- });
1862
- const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
1863
- const fileContents = await Promise.all(
1864
- files.map(async (file) => ({
1865
- file,
1866
- content: await (0, import_core10.readFileContent)(file)
1867
- }))
1868
- );
1869
- const graph = buildDependencyGraph(
1870
- fileContents.filter((f) => !f.file.toLowerCase().endsWith(".py"))
1871
- );
1872
- let pythonResults = [];
1873
- if (pythonFiles.length > 0) {
1874
- const { analyzePythonContext: analyzePythonContext2 } = await Promise.resolve().then(() => (init_python_context(), python_context_exports));
1875
- const pythonMetrics = await analyzePythonContext2(
1876
- pythonFiles,
1877
- scanOptions.rootDir || options.rootDir || "."
1878
- );
1879
- pythonResults = pythonMetrics.map((metric) => {
1880
- const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
1881
- file: metric.file,
1882
- importDepth: metric.importDepth,
1883
- contextBudget: metric.contextBudget,
1884
- cohesionScore: metric.cohesion,
1885
- fragmentationScore: 0,
1886
- maxDepth,
1887
- maxContextBudget,
1888
- minCohesion,
1889
- maxFragmentation,
1890
- circularDeps: metric.metrics.circularDependencies.map(
1891
- (cycle) => cycle.split(" \u2192 ")
1892
- )
1893
- });
1894
- return {
1895
- file: metric.file,
1896
- tokenCost: Math.floor(
1897
- metric.contextBudget / (1 + metric.imports.length || 1)
1898
- ),
1899
- linesOfCode: metric.metrics.linesOfCode,
1900
- importDepth: metric.importDepth,
1901
- dependencyCount: metric.imports.length,
1902
- dependencyList: metric.imports.map(
1903
- (imp) => imp.resolvedPath || imp.source
1904
- ),
1905
- circularDeps: metric.metrics.circularDependencies.map(
1906
- (cycle) => cycle.split(" \u2192 ")
1907
- ),
1908
- cohesionScore: metric.cohesion,
1909
- domains: ["python"],
1910
- exportCount: metric.exports.length,
1911
- contextBudget: metric.contextBudget,
1912
- fragmentationScore: 0,
1913
- relatedFiles: [],
1914
- fileClassification: "unknown",
1915
- severity,
1916
- issues,
1917
- recommendations,
1918
- potentialSavings
1919
- };
1920
- });
1921
- }
1922
- const circularDeps = detectCircularDependencies(graph);
1923
- const useLogScale = files.length >= 500;
1924
- const clusters = detectModuleClusters(graph, { useLogScale });
1925
- const fragmentationMap = /* @__PURE__ */ new Map();
1926
- for (const cluster of clusters) {
1927
- for (const file of cluster.files) {
1928
- fragmentationMap.set(file, cluster.fragmentationScore);
1929
- }
1930
- }
1931
- const results = [];
1932
- for (const { file } of fileContents) {
1933
- const node = graph.nodes.get(file);
1934
- if (!node) continue;
1935
- const importDepth = focus === "depth" || focus === "all" ? calculateImportDepth(file, graph) : 0;
1936
- const dependencyList = focus === "depth" || focus === "all" ? getTransitiveDependencies(file, graph) : [];
1937
- const contextBudget = focus === "all" ? calculateContextBudget(file, graph) : node.tokenCost;
1938
- const cohesionScore = focus === "cohesion" || focus === "all" ? calculateCohesion(node.exports, file, {
1939
- coUsageMatrix: graph.coUsageMatrix
1940
- }) : 1;
1941
- const fragmentationScore = fragmentationMap.get(file) || 0;
1942
- const relatedFiles = [];
1943
- for (const cluster of clusters) {
1944
- if (cluster.files.includes(file)) {
1945
- relatedFiles.push(...cluster.files.filter((f) => f !== file));
1946
- break;
1947
- }
1948
- }
1949
- const { issues } = analyzeIssues({
1950
- file,
1951
- importDepth,
1952
- contextBudget,
1953
- cohesionScore,
1954
- fragmentationScore,
1955
- maxDepth,
1956
- maxContextBudget,
1957
- minCohesion,
1958
- maxFragmentation,
1959
- circularDeps
1960
- });
1961
- const domains = [
1962
- ...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
1963
- ];
1964
- const fileClassification = classifyFile(node);
1965
- const adjustedCohesionScore = adjustCohesionForClassification(
1966
- cohesionScore,
1967
- fileClassification,
1968
- node
1969
- );
1970
- const adjustedFragmentationScore = adjustFragmentationForClassification(
1971
- fragmentationScore,
1972
- fileClassification
1973
- );
1974
- const classificationRecommendations = getClassificationRecommendations(
1975
- fileClassification,
1976
- file,
1977
- issues
1978
- );
1979
- const {
1980
- severity: adjustedSeverity,
1981
- issues: adjustedIssues,
1982
- recommendations: finalRecommendations,
1983
- potentialSavings: adjustedSavings
1984
- } = analyzeIssues({
1985
- file,
1986
- importDepth,
1987
- contextBudget,
1988
- cohesionScore: adjustedCohesionScore,
1989
- fragmentationScore: adjustedFragmentationScore,
1990
- maxDepth,
1991
- maxContextBudget,
1992
- minCohesion,
1993
- maxFragmentation,
1994
- circularDeps
1995
- });
1996
- results.push({
1997
- file,
1998
- tokenCost: node.tokenCost,
1999
- linesOfCode: node.linesOfCode,
2000
- importDepth,
2001
- dependencyCount: dependencyList.length,
2002
- dependencyList,
2003
- circularDeps: circularDeps.filter((cycle) => cycle.includes(file)),
2004
- cohesionScore: adjustedCohesionScore,
2005
- domains,
2006
- exportCount: node.exports.length,
2007
- contextBudget,
2008
- fragmentationScore: adjustedFragmentationScore,
2009
- relatedFiles,
2010
- fileClassification,
2011
- severity: adjustedSeverity,
2012
- issues: adjustedIssues,
2013
- recommendations: [
2014
- ...finalRecommendations,
2015
- ...classificationRecommendations.slice(0, 1)
2016
- ],
2017
- potentialSavings: adjustedSavings
2018
- });
2019
- }
2020
- const allResults = [...results, ...pythonResults];
2021
- const finalSummary = generateSummary(allResults, options);
2022
- return allResults.sort((a, b) => {
2023
- const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
2024
- const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
2025
- if (severityDiff !== 0) return severityDiff;
2026
- return b.contextBudget - a.contextBudget;
2027
- });
2028
- }
2029
2044
  // Annotate the CommonJS export names for ESM import in node:
2030
2045
  0 && (module.exports = {
2046
+ Classification,
2031
2047
  ContextAnalyzerProvider,
2032
2048
  adjustCohesionForClassification,
2033
2049
  adjustFragmentationForClassification,