@aiready/context-analyzer 0.19.18 → 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 +25 -25
  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/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, 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
  }
@@ -247,8 +247,11 @@ var import_commander = require("commander");
247
247
  // src/index.ts
248
248
  var import_core10 = require("@aiready/core");
249
249
 
250
+ // src/provider.ts
251
+ var import_core8 = require("@aiready/core");
252
+
250
253
  // src/analyzer.ts
251
- var import_core4 = require("@aiready/core");
254
+ var import_core6 = require("@aiready/core");
252
255
 
253
256
  // src/metrics.ts
254
257
  var import_core2 = require("@aiready/core");
@@ -610,8 +613,143 @@ function calculateDirectoryDistance(files) {
610
613
  return comparisons > 0 ? totalNormalized / comparisons : 0;
611
614
  }
612
615
 
613
- // src/graph-builder.ts
616
+ // src/summary.ts
614
617
  var import_core3 = require("@aiready/core");
618
+ function generateSummary(results, options) {
619
+ const config = options ? Object.fromEntries(
620
+ Object.entries(options).filter(
621
+ ([key]) => !import_core3.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
622
+ )
623
+ ) : void 0;
624
+ if (results.length === 0) {
625
+ return {
626
+ totalFiles: 0,
627
+ totalTokens: 0,
628
+ avgContextBudget: 0,
629
+ maxContextBudget: 0,
630
+ avgImportDepth: 0,
631
+ maxImportDepth: 0,
632
+ deepFiles: [],
633
+ avgFragmentation: 0,
634
+ fragmentedModules: [],
635
+ avgCohesion: 0,
636
+ lowCohesionFiles: [],
637
+ criticalIssues: 0,
638
+ majorIssues: 0,
639
+ minorIssues: 0,
640
+ totalPotentialSavings: 0,
641
+ topExpensiveFiles: [],
642
+ config
643
+ };
644
+ }
645
+ const totalFiles = results.length;
646
+ const totalTokens = results.reduce((sum, r) => sum + r.tokenCost, 0);
647
+ const totalContextBudget = results.reduce(
648
+ (sum, r) => sum + r.contextBudget,
649
+ 0
650
+ );
651
+ const avgContextBudget = totalContextBudget / totalFiles;
652
+ const maxContextBudget = Math.max(...results.map((r) => r.contextBudget));
653
+ const avgImportDepth = results.reduce((sum, r) => sum + r.importDepth, 0) / totalFiles;
654
+ const maxImportDepth = Math.max(...results.map((r) => r.importDepth));
655
+ 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);
656
+ const avgFragmentation = results.reduce((sum, r) => sum + r.fragmentationScore, 0) / totalFiles;
657
+ const moduleMap = /* @__PURE__ */ new Map();
658
+ for (const result of results) {
659
+ for (const domain of result.domains) {
660
+ if (!moduleMap.has(domain)) moduleMap.set(domain, []);
661
+ moduleMap.get(domain).push(result);
662
+ }
663
+ }
664
+ const fragmentedModules = [];
665
+ for (const [domain, files] of moduleMap.entries()) {
666
+ let jaccard2 = function(a, b) {
667
+ const s1 = new Set(a || []);
668
+ const s2 = new Set(b || []);
669
+ if (s1.size === 0 && s2.size === 0) return 0;
670
+ const inter = new Set([...s1].filter((x) => s2.has(x)));
671
+ const uni = /* @__PURE__ */ new Set([...s1, ...s2]);
672
+ return uni.size === 0 ? 0 : inter.size / uni.size;
673
+ };
674
+ var jaccard = jaccard2;
675
+ if (files.length < 2) continue;
676
+ const fragmentationScore = files.reduce((sum, f) => sum + f.fragmentationScore, 0) / files.length;
677
+ if (fragmentationScore < 0.3) continue;
678
+ const totalTokens2 = files.reduce((sum, f) => sum + f.tokenCost, 0);
679
+ const avgCohesion2 = files.reduce((sum, f) => sum + f.cohesionScore, 0) / files.length;
680
+ const targetFiles = Math.max(1, Math.ceil(files.length / 3));
681
+ const filePaths = files.map((f) => f.file);
682
+ const pathEntropy = calculatePathEntropy(filePaths);
683
+ const directoryDistance = calculateDirectoryDistance(filePaths);
684
+ let importSimTotal = 0;
685
+ let importPairs = 0;
686
+ for (let i = 0; i < files.length; i++) {
687
+ for (let j = i + 1; j < files.length; j++) {
688
+ importSimTotal += jaccard2(
689
+ files[i].dependencyList || [],
690
+ files[j].dependencyList || []
691
+ );
692
+ importPairs++;
693
+ }
694
+ }
695
+ const importCohesion = importPairs > 0 ? importSimTotal / importPairs : 0;
696
+ fragmentedModules.push({
697
+ domain,
698
+ files: files.map((f) => f.file),
699
+ totalTokens: totalTokens2,
700
+ fragmentationScore,
701
+ avgCohesion: avgCohesion2,
702
+ importCohesion,
703
+ pathEntropy,
704
+ directoryDistance,
705
+ suggestedStructure: {
706
+ targetFiles,
707
+ consolidationPlan: [
708
+ `Consolidate ${files.length} files across ${new Set(files.map((f) => f.file.split("/").slice(0, -1).join("/"))).size} directories`,
709
+ `Target ~${targetFiles} core modules to reduce context switching`
710
+ ]
711
+ }
712
+ });
713
+ }
714
+ const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / totalFiles;
715
+ 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);
716
+ const criticalIssues = results.filter(
717
+ (r) => r.severity === "critical"
718
+ ).length;
719
+ const majorIssues = results.filter((r) => r.severity === "major").length;
720
+ const minorIssues = results.filter((r) => r.severity === "minor").length;
721
+ const totalPotentialSavings = results.reduce(
722
+ (sum, r) => sum + r.potentialSavings,
723
+ 0
724
+ );
725
+ const topExpensiveFiles = results.sort((a, b) => b.contextBudget - a.contextBudget).slice(0, 10).map((r) => ({
726
+ file: r.file,
727
+ contextBudget: r.contextBudget,
728
+ severity: r.severity
729
+ }));
730
+ return {
731
+ totalFiles,
732
+ totalTokens,
733
+ avgContextBudget,
734
+ maxContextBudget,
735
+ avgImportDepth,
736
+ maxImportDepth,
737
+ deepFiles,
738
+ avgFragmentation,
739
+ fragmentedModules,
740
+ avgCohesion,
741
+ lowCohesionFiles,
742
+ criticalIssues,
743
+ majorIssues,
744
+ minorIssues,
745
+ totalPotentialSavings,
746
+ topExpensiveFiles,
747
+ config
748
+ };
749
+ }
750
+
751
+ // src/graph-builder.ts
752
+ var import_core4 = require("@aiready/core");
615
753
  function extractDomainKeywordsFromPaths(files) {
616
754
  const folderNames = /* @__PURE__ */ new Set();
617
755
  for (const { file } of files) {
@@ -663,7 +801,7 @@ function buildDependencyGraph(files, options) {
663
801
  const edges = /* @__PURE__ */ new Map();
664
802
  const autoDetectedKeywords = options?.domainKeywords ?? extractDomainKeywordsFromPaths(files);
665
803
  for (const { file, content } of files) {
666
- const { imports: astImports } = (0, import_core3.parseFileExports)(content, file);
804
+ const { imports: astImports } = (0, import_core4.parseFileExports)(content, file);
667
805
  const importSources = astImports.map((i) => i.source);
668
806
  const exports2 = extractExportsWithAST(
669
807
  content,
@@ -671,7 +809,7 @@ function buildDependencyGraph(files, options) {
671
809
  { domainKeywords: autoDetectedKeywords },
672
810
  importSources
673
811
  );
674
- const tokenCost = (0, import_core3.estimateTokens)(content);
812
+ const tokenCost = (0, import_core4.estimateTokens)(content);
675
813
  const linesOfCode = content.split("\n").length;
676
814
  nodes.set(file, {
677
815
  file,
@@ -778,48 +916,62 @@ function detectCircularDependencies(graph) {
778
916
  }
779
917
 
780
918
  // src/classifier.ts
919
+ var Classification = {
920
+ BARREL: "barrel-export",
921
+ TYPE_DEFINITION: "type-definition",
922
+ NEXTJS_PAGE: "nextjs-page",
923
+ LAMBDA_HANDLER: "lambda-handler",
924
+ SERVICE: "service-file",
925
+ EMAIL_TEMPLATE: "email-template",
926
+ PARSER: "parser-file",
927
+ COHESIVE_MODULE: "cohesive-module",
928
+ UTILITY_MODULE: "utility-module",
929
+ MIXED_CONCERNS: "mixed-concerns",
930
+ UNKNOWN: "unknown"
931
+ };
781
932
  function classifyFile(node, cohesionScore = 1, domains = []) {
782
933
  if (isBarrelExport(node)) {
783
- return "barrel-export";
934
+ return Classification.BARREL;
784
935
  }
785
936
  if (isTypeDefinition(node)) {
786
- return "type-definition";
937
+ return Classification.TYPE_DEFINITION;
787
938
  }
788
939
  if (isNextJsPage(node)) {
789
- return "nextjs-page";
940
+ return Classification.NEXTJS_PAGE;
790
941
  }
791
942
  if (isLambdaHandler(node)) {
792
- return "lambda-handler";
943
+ return Classification.LAMBDA_HANDLER;
793
944
  }
794
945
  if (isServiceFile(node)) {
795
- return "service-file";
946
+ return Classification.SERVICE;
796
947
  }
797
948
  if (isEmailTemplate(node)) {
798
- return "email-template";
949
+ return Classification.EMAIL_TEMPLATE;
799
950
  }
800
951
  if (isParserFile(node)) {
801
- return "parser-file";
952
+ return Classification.PARSER;
802
953
  }
803
954
  if (isSessionFile(node)) {
804
- if (cohesionScore >= 0.25 && domains.length <= 1) return "cohesive-module";
805
- return "utility-module";
955
+ if (cohesionScore >= 0.25 && domains.length <= 1)
956
+ return Classification.COHESIVE_MODULE;
957
+ return Classification.UTILITY_MODULE;
806
958
  }
807
959
  if (isUtilityModule(node)) {
808
- return "utility-module";
960
+ return Classification.UTILITY_MODULE;
809
961
  }
810
962
  if (isConfigFile(node)) {
811
- return "cohesive-module";
963
+ return Classification.COHESIVE_MODULE;
812
964
  }
813
965
  if (domains.length <= 1 && domains[0] !== "unknown") {
814
- return "cohesive-module";
966
+ return Classification.COHESIVE_MODULE;
815
967
  }
816
968
  if (domains.length > 1 && cohesionScore < 0.4) {
817
- return "mixed-concerns";
969
+ return Classification.MIXED_CONCERNS;
818
970
  }
819
971
  if (cohesionScore >= 0.7) {
820
- return "cohesive-module";
972
+ return Classification.COHESIVE_MODULE;
821
973
  }
822
- return "unknown";
974
+ return Classification.UNKNOWN;
823
975
  }
824
976
  function isBarrelExport(node) {
825
977
  const { file, exports: exports2 } = node;
@@ -982,13 +1134,13 @@ function isNextJsPage(node) {
982
1134
  }
983
1135
  function adjustCohesionForClassification(baseCohesion, classification, node) {
984
1136
  switch (classification) {
985
- case "barrel-export":
1137
+ case Classification.BARREL:
986
1138
  return 1;
987
- case "type-definition":
1139
+ case Classification.TYPE_DEFINITION:
988
1140
  return 1;
989
- case "nextjs-page":
1141
+ case Classification.NEXTJS_PAGE:
990
1142
  return 1;
991
- case "utility-module": {
1143
+ case Classification.UTILITY_MODULE: {
992
1144
  if (node && hasRelatedExportNames(
993
1145
  (node.exports || []).map((e) => e.name.toLowerCase())
994
1146
  )) {
@@ -996,17 +1148,17 @@ function adjustCohesionForClassification(baseCohesion, classification, node) {
996
1148
  }
997
1149
  return Math.max(0.75, Math.min(1, baseCohesion + 0.35));
998
1150
  }
999
- case "service-file":
1151
+ case Classification.SERVICE:
1000
1152
  return Math.max(0.72, Math.min(1, baseCohesion + 0.3));
1001
- case "lambda-handler":
1153
+ case Classification.LAMBDA_HANDLER:
1002
1154
  return Math.max(0.75, Math.min(1, baseCohesion + 0.35));
1003
- case "email-template":
1155
+ case Classification.EMAIL_TEMPLATE:
1004
1156
  return Math.max(0.72, Math.min(1, baseCohesion + 0.3));
1005
- case "parser-file":
1157
+ case Classification.PARSER:
1006
1158
  return Math.max(0.7, Math.min(1, baseCohesion + 0.3));
1007
- case "cohesive-module":
1159
+ case Classification.COHESIVE_MODULE:
1008
1160
  return Math.max(baseCohesion, 0.7);
1009
- case "mixed-concerns":
1161
+ case Classification.MIXED_CONCERNS:
1010
1162
  return baseCohesion;
1011
1163
  default:
1012
1164
  return Math.min(1, baseCohesion + 0.1);
@@ -1055,20 +1207,20 @@ function hasRelatedExportNames(exportNames) {
1055
1207
  }
1056
1208
  function adjustFragmentationForClassification(baseFragmentation, classification) {
1057
1209
  switch (classification) {
1058
- case "barrel-export":
1210
+ case Classification.BARREL:
1059
1211
  return 0;
1060
- case "type-definition":
1212
+ case Classification.TYPE_DEFINITION:
1061
1213
  return 0;
1062
- case "utility-module":
1063
- case "service-file":
1064
- case "lambda-handler":
1065
- case "email-template":
1066
- case "parser-file":
1067
- case "nextjs-page":
1214
+ case Classification.UTILITY_MODULE:
1215
+ case Classification.SERVICE:
1216
+ case Classification.LAMBDA_HANDLER:
1217
+ case Classification.EMAIL_TEMPLATE:
1218
+ case Classification.PARSER:
1219
+ case Classification.NEXTJS_PAGE:
1068
1220
  return baseFragmentation * 0.2;
1069
- case "cohesive-module":
1221
+ case Classification.COHESIVE_MODULE:
1070
1222
  return baseFragmentation * 0.3;
1071
- case "mixed-concerns":
1223
+ case Classification.MIXED_CONCERNS:
1072
1224
  return baseFragmentation;
1073
1225
  default:
1074
1226
  return baseFragmentation * 0.7;
@@ -1234,10 +1386,10 @@ function analyzeIssues(params) {
1234
1386
  } = params;
1235
1387
  const issues = [];
1236
1388
  const recommendations = [];
1237
- let severity = import_core4.Severity.Info;
1389
+ let severity = import_core6.Severity.Info;
1238
1390
  let potentialSavings = 0;
1239
1391
  if (circularDeps.length > 0) {
1240
- severity = import_core4.Severity.Critical;
1392
+ severity = import_core6.Severity.Critical;
1241
1393
  issues.push(`Part of ${circularDeps.length} circular dependency chain(s)`);
1242
1394
  recommendations.push(
1243
1395
  "Break circular dependencies by extracting interfaces or using dependency injection"
@@ -1245,12 +1397,12 @@ function analyzeIssues(params) {
1245
1397
  potentialSavings += contextBudget * 0.2;
1246
1398
  }
1247
1399
  if (importDepth > maxDepth * 1.5) {
1248
- severity = import_core4.Severity.Critical;
1400
+ severity = import_core6.Severity.Critical;
1249
1401
  issues.push(`Import depth ${importDepth} exceeds limit by 50%`);
1250
1402
  recommendations.push("Flatten dependency tree or use facade pattern");
1251
1403
  potentialSavings += contextBudget * 0.3;
1252
1404
  } else if (importDepth > maxDepth) {
1253
- if (severity !== import_core4.Severity.Critical) severity = import_core4.Severity.Major;
1405
+ if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
1254
1406
  issues.push(
1255
1407
  `Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`
1256
1408
  );
@@ -1258,7 +1410,7 @@ function analyzeIssues(params) {
1258
1410
  potentialSavings += contextBudget * 0.15;
1259
1411
  }
1260
1412
  if (contextBudget > maxContextBudget * 1.5) {
1261
- severity = import_core4.Severity.Critical;
1413
+ severity = import_core6.Severity.Critical;
1262
1414
  issues.push(
1263
1415
  `Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
1264
1416
  );
@@ -1267,7 +1419,7 @@ function analyzeIssues(params) {
1267
1419
  );
1268
1420
  potentialSavings += contextBudget * 0.4;
1269
1421
  } else if (contextBudget > maxContextBudget) {
1270
- if (severity !== import_core4.Severity.Critical) severity = import_core4.Severity.Major;
1422
+ if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
1271
1423
  issues.push(
1272
1424
  `Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
1273
1425
  );
@@ -1275,7 +1427,7 @@ function analyzeIssues(params) {
1275
1427
  potentialSavings += contextBudget * 0.2;
1276
1428
  }
1277
1429
  if (cohesionScore < minCohesion * 0.5) {
1278
- if (severity !== import_core4.Severity.Critical) severity = import_core4.Severity.Major;
1430
+ if (severity !== import_core6.Severity.Critical) severity = import_core6.Severity.Major;
1279
1431
  issues.push(
1280
1432
  `Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`
1281
1433
  );
@@ -1284,14 +1436,14 @@ function analyzeIssues(params) {
1284
1436
  );
1285
1437
  potentialSavings += contextBudget * 0.25;
1286
1438
  } else if (cohesionScore < minCohesion) {
1287
- if (severity === import_core4.Severity.Info) severity = import_core4.Severity.Minor;
1439
+ if (severity === import_core6.Severity.Info) severity = import_core6.Severity.Minor;
1288
1440
  issues.push(`Low cohesion (${(cohesionScore * 100).toFixed(0)}%)`);
1289
1441
  recommendations.push("Consider grouping related exports together");
1290
1442
  potentialSavings += contextBudget * 0.1;
1291
1443
  }
1292
1444
  if (fragmentationScore > maxFragmentation) {
1293
- if (severity === import_core4.Severity.Info || severity === import_core4.Severity.Minor)
1294
- severity = import_core4.Severity.Minor;
1445
+ if (severity === import_core6.Severity.Info || severity === import_core6.Severity.Minor)
1446
+ severity = import_core6.Severity.Minor;
1295
1447
  issues.push(
1296
1448
  `High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`
1297
1449
  );
@@ -1305,7 +1457,7 @@ function analyzeIssues(params) {
1305
1457
  if (isBuildArtifact(file)) {
1306
1458
  issues.push("Detected build artifact (bundled/output file)");
1307
1459
  recommendations.push("Exclude build outputs from analysis");
1308
- severity = import_core4.Severity.Info;
1460
+ severity = import_core6.Severity.Info;
1309
1461
  potentialSavings = 0;
1310
1462
  }
1311
1463
  return {
@@ -1319,147 +1471,192 @@ function isBuildArtifact(filePath) {
1319
1471
  const lower = filePath.toLowerCase();
1320
1472
  return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/.next/");
1321
1473
  }
1322
-
1323
- // src/summary.ts
1324
- var import_core5 = require("@aiready/core");
1325
- function generateSummary(results, options) {
1326
- const config = options ? Object.fromEntries(
1327
- Object.entries(options).filter(
1328
- ([key]) => !import_core5.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
1329
- )
1330
- ) : void 0;
1331
- if (results.length === 0) {
1332
- return {
1333
- totalFiles: 0,
1334
- totalTokens: 0,
1335
- avgContextBudget: 0,
1336
- maxContextBudget: 0,
1337
- avgImportDepth: 0,
1338
- maxImportDepth: 0,
1339
- deepFiles: [],
1340
- avgFragmentation: 0,
1341
- fragmentedModules: [],
1342
- avgCohesion: 0,
1343
- lowCohesionFiles: [],
1344
- criticalIssues: 0,
1345
- majorIssues: 0,
1346
- minorIssues: 0,
1347
- totalPotentialSavings: 0,
1348
- topExpensiveFiles: [],
1349
- config
1350
- };
1351
- }
1352
- const totalFiles = results.length;
1353
- const totalTokens = results.reduce((sum, r) => sum + r.tokenCost, 0);
1354
- const totalContextBudget = results.reduce(
1355
- (sum, r) => sum + r.contextBudget,
1356
- 0
1474
+ async function analyzeContext(options) {
1475
+ const {
1476
+ maxDepth = 5,
1477
+ maxContextBudget = 1e4,
1478
+ minCohesion = 0.6,
1479
+ maxFragmentation = 0.5,
1480
+ focus = "all",
1481
+ includeNodeModules = false,
1482
+ ...scanOptions
1483
+ } = options;
1484
+ const files = await (0, import_core6.scanFiles)({
1485
+ ...scanOptions,
1486
+ exclude: includeNodeModules && scanOptions.exclude ? scanOptions.exclude.filter(
1487
+ (pattern) => pattern !== "**/node_modules/**"
1488
+ ) : scanOptions.exclude
1489
+ });
1490
+ const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
1491
+ const fileContents = await Promise.all(
1492
+ files.map(async (file) => ({
1493
+ file,
1494
+ content: await (0, import_core6.readFileContent)(file)
1495
+ }))
1357
1496
  );
1358
- const avgContextBudget = totalContextBudget / totalFiles;
1359
- const maxContextBudget = Math.max(...results.map((r) => r.contextBudget));
1360
- const avgImportDepth = results.reduce((sum, r) => sum + r.importDepth, 0) / totalFiles;
1361
- const maxImportDepth = Math.max(...results.map((r) => r.importDepth));
1362
- 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);
1363
- const avgFragmentation = results.reduce((sum, r) => sum + r.fragmentationScore, 0) / totalFiles;
1364
- const moduleMap = /* @__PURE__ */ new Map();
1365
- for (const result of results) {
1366
- for (const domain of result.domains) {
1367
- if (!moduleMap.has(domain)) moduleMap.set(domain, []);
1368
- moduleMap.get(domain).push(result);
1369
- }
1370
- }
1371
- const fragmentedModules = [];
1372
- for (const [domain, files] of moduleMap.entries()) {
1373
- let jaccard2 = function(a, b) {
1374
- const s1 = new Set(a || []);
1375
- const s2 = new Set(b || []);
1376
- if (s1.size === 0 && s2.size === 0) return 0;
1377
- const inter = new Set([...s1].filter((x) => s2.has(x)));
1378
- const uni = /* @__PURE__ */ new Set([...s1, ...s2]);
1379
- return uni.size === 0 ? 0 : inter.size / uni.size;
1380
- };
1381
- var jaccard = jaccard2;
1382
- if (files.length < 2) continue;
1383
- const fragmentationScore = files.reduce((sum, f) => sum + f.fragmentationScore, 0) / files.length;
1384
- if (fragmentationScore < 0.3) continue;
1385
- const totalTokens2 = files.reduce((sum, f) => sum + f.tokenCost, 0);
1386
- const avgCohesion2 = files.reduce((sum, f) => sum + f.cohesionScore, 0) / files.length;
1387
- const targetFiles = Math.max(1, Math.ceil(files.length / 3));
1388
- const filePaths = files.map((f) => f.file);
1389
- const pathEntropy = calculatePathEntropy(filePaths);
1390
- const directoryDistance = calculateDirectoryDistance(filePaths);
1391
- let importSimTotal = 0;
1392
- let importPairs = 0;
1393
- for (let i = 0; i < files.length; i++) {
1394
- for (let j = i + 1; j < files.length; j++) {
1395
- importSimTotal += jaccard2(
1396
- files[i].dependencyList || [],
1397
- files[j].dependencyList || []
1398
- );
1399
- importPairs++;
1497
+ const graph = buildDependencyGraph(
1498
+ fileContents.filter((f) => !f.file.toLowerCase().endsWith(".py"))
1499
+ );
1500
+ let pythonResults = [];
1501
+ if (pythonFiles.length > 0) {
1502
+ const { analyzePythonContext: analyzePythonContext2 } = await Promise.resolve().then(() => (init_python_context(), python_context_exports));
1503
+ const pythonMetrics = await analyzePythonContext2(
1504
+ pythonFiles,
1505
+ scanOptions.rootDir || options.rootDir || "."
1506
+ );
1507
+ pythonResults = pythonMetrics.map((metric) => {
1508
+ const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
1509
+ file: metric.file,
1510
+ importDepth: metric.importDepth,
1511
+ contextBudget: metric.contextBudget,
1512
+ cohesionScore: metric.cohesion,
1513
+ fragmentationScore: 0,
1514
+ maxDepth,
1515
+ maxContextBudget,
1516
+ minCohesion,
1517
+ maxFragmentation,
1518
+ circularDeps: metric.metrics.circularDependencies.map(
1519
+ (cycle) => cycle.split(" \u2192 ")
1520
+ )
1521
+ });
1522
+ return {
1523
+ file: metric.file,
1524
+ tokenCost: Math.floor(
1525
+ metric.contextBudget / (1 + metric.imports.length || 1)
1526
+ ),
1527
+ linesOfCode: metric.metrics.linesOfCode,
1528
+ importDepth: metric.importDepth,
1529
+ dependencyCount: metric.imports.length,
1530
+ dependencyList: metric.imports.map(
1531
+ (imp) => imp.resolvedPath || imp.source
1532
+ ),
1533
+ circularDeps: metric.metrics.circularDependencies.map(
1534
+ (cycle) => cycle.split(" \u2192 ")
1535
+ ),
1536
+ cohesionScore: metric.cohesion,
1537
+ domains: ["python"],
1538
+ exportCount: metric.exports.length,
1539
+ contextBudget: metric.contextBudget,
1540
+ fragmentationScore: 0,
1541
+ relatedFiles: [],
1542
+ fileClassification: "unknown",
1543
+ severity,
1544
+ issues,
1545
+ recommendations,
1546
+ potentialSavings
1547
+ };
1548
+ });
1549
+ }
1550
+ const circularDeps = detectCircularDependencies(graph);
1551
+ const useLogScale = files.length >= 500;
1552
+ const clusters = detectModuleClusters(graph, { useLogScale });
1553
+ const fragmentationMap = /* @__PURE__ */ new Map();
1554
+ for (const cluster of clusters) {
1555
+ for (const file of cluster.files) {
1556
+ fragmentationMap.set(file, cluster.fragmentationScore);
1557
+ }
1558
+ }
1559
+ const results = [];
1560
+ for (const { file } of fileContents) {
1561
+ const node = graph.nodes.get(file);
1562
+ if (!node) continue;
1563
+ const importDepth = focus === "depth" || focus === "all" ? calculateImportDepth(file, graph) : 0;
1564
+ const dependencyList = focus === "depth" || focus === "all" ? getTransitiveDependencies(file, graph) : [];
1565
+ const contextBudget = focus === "all" ? calculateContextBudget(file, graph) : node.tokenCost;
1566
+ const cohesionScore = focus === "cohesion" || focus === "all" ? calculateCohesion(node.exports, file, {
1567
+ coUsageMatrix: graph.coUsageMatrix
1568
+ }) : 1;
1569
+ const fragmentationScore = fragmentationMap.get(file) || 0;
1570
+ const relatedFiles = [];
1571
+ for (const cluster of clusters) {
1572
+ if (cluster.files.includes(file)) {
1573
+ relatedFiles.push(...cluster.files.filter((f) => f !== file));
1574
+ break;
1400
1575
  }
1401
1576
  }
1402
- const importCohesion = importPairs > 0 ? importSimTotal / importPairs : 0;
1403
- fragmentedModules.push({
1404
- domain,
1405
- files: files.map((f) => f.file),
1406
- totalTokens: totalTokens2,
1577
+ const { issues } = analyzeIssues({
1578
+ file,
1579
+ importDepth,
1580
+ contextBudget,
1581
+ cohesionScore,
1407
1582
  fragmentationScore,
1408
- avgCohesion: avgCohesion2,
1409
- importCohesion,
1410
- pathEntropy,
1411
- directoryDistance,
1412
- suggestedStructure: {
1413
- targetFiles,
1414
- consolidationPlan: [
1415
- `Consolidate ${files.length} files across ${new Set(files.map((f) => f.file.split("/").slice(0, -1).join("/"))).size} directories`,
1416
- `Target ~${targetFiles} core modules to reduce context switching`
1417
- ]
1418
- }
1583
+ maxDepth,
1584
+ maxContextBudget,
1585
+ minCohesion,
1586
+ maxFragmentation,
1587
+ circularDeps
1588
+ });
1589
+ const domains = [
1590
+ ...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
1591
+ ];
1592
+ const fileClassification = classifyFile(node);
1593
+ const adjustedCohesionScore = adjustCohesionForClassification(
1594
+ cohesionScore,
1595
+ fileClassification,
1596
+ node
1597
+ );
1598
+ const adjustedFragmentationScore = adjustFragmentationForClassification(
1599
+ fragmentationScore,
1600
+ fileClassification
1601
+ );
1602
+ const classificationRecommendations = getClassificationRecommendations(
1603
+ fileClassification,
1604
+ file,
1605
+ issues
1606
+ );
1607
+ const {
1608
+ severity: adjustedSeverity,
1609
+ issues: adjustedIssues,
1610
+ recommendations: finalRecommendations,
1611
+ potentialSavings: adjustedSavings
1612
+ } = analyzeIssues({
1613
+ file,
1614
+ importDepth,
1615
+ contextBudget,
1616
+ cohesionScore: adjustedCohesionScore,
1617
+ fragmentationScore: adjustedFragmentationScore,
1618
+ maxDepth,
1619
+ maxContextBudget,
1620
+ minCohesion,
1621
+ maxFragmentation,
1622
+ circularDeps
1623
+ });
1624
+ results.push({
1625
+ file,
1626
+ tokenCost: node.tokenCost,
1627
+ linesOfCode: node.linesOfCode,
1628
+ importDepth,
1629
+ dependencyCount: dependencyList.length,
1630
+ dependencyList,
1631
+ circularDeps: circularDeps.filter((cycle) => cycle.includes(file)),
1632
+ cohesionScore: adjustedCohesionScore,
1633
+ domains,
1634
+ exportCount: node.exports.length,
1635
+ contextBudget,
1636
+ fragmentationScore: adjustedFragmentationScore,
1637
+ relatedFiles,
1638
+ fileClassification,
1639
+ severity: adjustedSeverity,
1640
+ issues: adjustedIssues,
1641
+ recommendations: [
1642
+ ...finalRecommendations,
1643
+ ...classificationRecommendations.slice(0, 1)
1644
+ ],
1645
+ potentialSavings: adjustedSavings
1419
1646
  });
1420
1647
  }
1421
- const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / totalFiles;
1422
- 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);
1423
- const criticalIssues = results.filter(
1424
- (r) => r.severity === "critical"
1425
- ).length;
1426
- const majorIssues = results.filter((r) => r.severity === "major").length;
1427
- const minorIssues = results.filter((r) => r.severity === "minor").length;
1428
- const totalPotentialSavings = results.reduce(
1429
- (sum, r) => sum + r.potentialSavings,
1430
- 0
1431
- );
1432
- const topExpensiveFiles = results.sort((a, b) => b.contextBudget - a.contextBudget).slice(0, 10).map((r) => ({
1433
- file: r.file,
1434
- contextBudget: r.contextBudget,
1435
- severity: r.severity
1436
- }));
1437
- return {
1438
- totalFiles,
1439
- totalTokens,
1440
- avgContextBudget,
1441
- maxContextBudget,
1442
- avgImportDepth,
1443
- maxImportDepth,
1444
- deepFiles,
1445
- avgFragmentation,
1446
- fragmentedModules,
1447
- avgCohesion,
1448
- lowCohesionFiles,
1449
- criticalIssues,
1450
- majorIssues,
1451
- minorIssues,
1452
- totalPotentialSavings,
1453
- topExpensiveFiles,
1454
- config
1455
- };
1648
+ const allResults = [...results, ...pythonResults];
1649
+ const finalSummary = generateSummary(allResults, options);
1650
+ return allResults.sort((a, b) => {
1651
+ const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
1652
+ const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
1653
+ if (severityDiff !== 0) return severityDiff;
1654
+ return b.contextBudget - a.contextBudget;
1655
+ });
1456
1656
  }
1457
1657
 
1458
- // src/provider.ts
1459
- var import_core7 = require("@aiready/core");
1460
-
1461
1658
  // src/scoring.ts
1462
- var import_core6 = require("@aiready/core");
1659
+ var import_core7 = require("@aiready/core");
1463
1660
  function calculateContextScore(summary, costConfig) {
1464
1661
  const {
1465
1662
  avgContextBudget,
@@ -1555,8 +1752,8 @@ function calculateContextScore(summary, costConfig) {
1555
1752
  priority: "high"
1556
1753
  });
1557
1754
  }
1558
- const cfg = { ...import_core6.DEFAULT_COST_CONFIG, ...costConfig };
1559
- const estimatedMonthlyCost = (0, import_core6.calculateMonthlyCost)(
1755
+ const cfg = { ...import_core7.DEFAULT_COST_CONFIG, ...costConfig };
1756
+ const estimatedMonthlyCost = (0, import_core7.calculateMonthlyCost)(
1560
1757
  avgContextBudget * (summary.totalFiles || 1),
1561
1758
  cfg
1562
1759
  );
@@ -1564,9 +1761,9 @@ function calculateContextScore(summary, costConfig) {
1564
1761
  ...Array(criticalIssues).fill({ severity: "critical" }),
1565
1762
  ...Array(majorIssues).fill({ severity: "major" })
1566
1763
  ];
1567
- const productivityImpact = (0, import_core6.calculateProductivityImpact)(issues);
1764
+ const productivityImpact = (0, import_core7.calculateProductivityImpact)(issues);
1568
1765
  return {
1569
- toolName: import_core6.ToolName.ContextAnalyzer,
1766
+ toolName: import_core7.ToolName.ContextAnalyzer,
1570
1767
  score,
1571
1768
  rawMetrics: {
1572
1769
  avgContextBudget: Math.round(avgContextBudget),
@@ -1586,7 +1783,7 @@ function calculateContextScore(summary, costConfig) {
1586
1783
 
1587
1784
  // src/provider.ts
1588
1785
  var ContextAnalyzerProvider = {
1589
- id: import_core7.ToolName.ContextAnalyzer,
1786
+ id: import_core8.ToolName.ContextAnalyzer,
1590
1787
  alias: ["context", "fragmentation", "budget"],
1591
1788
  async analyze(options) {
1592
1789
  const results = await analyzeContext(options);
@@ -1595,7 +1792,7 @@ var ContextAnalyzerProvider = {
1595
1792
  (r) => ({
1596
1793
  fileName: r.file,
1597
1794
  issues: r.issues.map((msg) => ({
1598
- type: import_core7.IssueType.ContextFragmentation,
1795
+ type: import_core8.IssueType.ContextFragmentation,
1599
1796
  severity: r.severity,
1600
1797
  message: msg,
1601
1798
  location: { file: r.file, line: 1 },
@@ -1608,13 +1805,13 @@ var ContextAnalyzerProvider = {
1608
1805
  }
1609
1806
  })
1610
1807
  );
1611
- return import_core7.SpokeOutputSchema.parse({
1808
+ return import_core8.SpokeOutputSchema.parse({
1612
1809
  results: normalizedResults,
1613
1810
  summary: {
1614
1811
  ...summary
1615
1812
  },
1616
1813
  metadata: {
1617
- toolName: import_core7.ToolName.ContextAnalyzer,
1814
+ toolName: import_core8.ToolName.ContextAnalyzer,
1618
1815
  version: "0.17.5",
1619
1816
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1620
1817
  }
@@ -1628,193 +1825,10 @@ var ContextAnalyzerProvider = {
1628
1825
  };
1629
1826
 
1630
1827
  // src/defaults.ts
1631
- var import_core8 = require("@aiready/core");
1828
+ var import_core9 = require("@aiready/core");
1632
1829
 
1633
1830
  // src/index.ts
1634
1831
  import_core10.ToolRegistry.register(ContextAnalyzerProvider);
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_core10.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_core10.readFileContent)(file)
1656
- }))
1657
- );
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;
1736
- }
1737
- }
1738
- const { issues } = analyzeIssues({
1739
- file,
1740
- importDepth,
1741
- contextBudget,
1742
- cohesionScore,
1743
- fragmentationScore,
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
1807
- });
1808
- }
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
- });
1817
- }
1818
1832
 
1819
1833
  // src/cli.ts
1820
1834
  var import_chalk = __toESM(require("chalk"));