@aiready/context-analyzer 0.9.34 → 0.9.36

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/dist/index.js CHANGED
@@ -43,7 +43,12 @@ async function analyzePythonContext(files, rootDir) {
43
43
  return results;
44
44
  }
45
45
  const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
46
- const dependencyGraph = await buildPythonDependencyGraph(pythonFiles, rootDir);
46
+ void import_path.relative;
47
+ void import_path.join;
48
+ const dependencyGraph = await buildPythonDependencyGraph(
49
+ pythonFiles,
50
+ rootDir
51
+ );
47
52
  for (const file of pythonFiles) {
48
53
  try {
49
54
  const code = await import_fs.default.promises.readFile(file, "utf-8");
@@ -59,10 +64,21 @@ async function analyzePythonContext(files, rootDir) {
59
64
  type: exp.type
60
65
  }));
61
66
  const linesOfCode = code.split("\n").length;
62
- const importDepth = await calculatePythonImportDepth(file, dependencyGraph, /* @__PURE__ */ new Set());
63
- const contextBudget = estimateContextBudget(code, imports, dependencyGraph);
67
+ const importDepth = await calculatePythonImportDepth(
68
+ file,
69
+ dependencyGraph,
70
+ /* @__PURE__ */ new Set()
71
+ );
72
+ const contextBudget = estimateContextBudget(
73
+ code,
74
+ imports,
75
+ dependencyGraph
76
+ );
64
77
  const cohesion = calculatePythonCohesion(exports2, imports);
65
- const circularDependencies = detectCircularDependencies2(file, dependencyGraph);
78
+ const circularDependencies = detectCircularDependencies2(
79
+ file,
80
+ dependencyGraph
81
+ );
66
82
  results.push({
67
83
  file,
68
84
  importDepth,
@@ -100,6 +116,7 @@ async function buildPythonDependencyGraph(files, rootDir) {
100
116
  }
101
117
  graph.set(file, dependencies);
102
118
  } catch (error) {
119
+ void error;
103
120
  }
104
121
  }
105
122
  return graph;
@@ -408,8 +425,12 @@ function findConsolidationCandidates(graph, coUsageMatrix, typeGraph, minCoUsage
408
425
  if (coUsageCount < minCoUsage) continue;
409
426
  const nodeB = graph.nodes.get(fileB);
410
427
  if (!nodeB) continue;
411
- const typesA = new Set(nodeA.exports.flatMap((e) => e.typeReferences || []));
412
- const typesB = new Set(nodeB.exports.flatMap((e) => e.typeReferences || []));
428
+ const typesA = new Set(
429
+ nodeA.exports.flatMap((e) => e.typeReferences || [])
430
+ );
431
+ const typesB = new Set(
432
+ nodeB.exports.flatMap((e) => e.typeReferences || [])
433
+ );
413
434
  const sharedTypes = Array.from(typesA).filter((t) => typesB.has(t));
414
435
  if (sharedTypes.length >= minSharedTypes) {
415
436
  const strength = coUsageCount / 10 + sharedTypes.length / 5;
@@ -437,7 +458,26 @@ function extractDomainKeywordsFromPaths(files) {
437
458
  const folderNames = /* @__PURE__ */ new Set();
438
459
  for (const { file } of files) {
439
460
  const segments = file.split("/");
440
- const skipFolders = /* @__PURE__ */ new Set(["src", "lib", "dist", "build", "node_modules", "test", "tests", "__tests__", "spec", "e2e", "scripts", "components", "utils", "helpers", "util", "helper", "api", "apis"]);
461
+ const skipFolders = /* @__PURE__ */ new Set([
462
+ "src",
463
+ "lib",
464
+ "dist",
465
+ "build",
466
+ "node_modules",
467
+ "test",
468
+ "tests",
469
+ "__tests__",
470
+ "spec",
471
+ "e2e",
472
+ "scripts",
473
+ "components",
474
+ "utils",
475
+ "helpers",
476
+ "util",
477
+ "helper",
478
+ "api",
479
+ "apis"
480
+ ]);
441
481
  for (const segment of segments) {
442
482
  const normalized = segment.toLowerCase();
443
483
  if (normalized && !skipFolders.has(normalized) && !normalized.includes(".")) {
@@ -473,9 +513,15 @@ function buildDependencyGraph(files) {
473
513
  const nodes = /* @__PURE__ */ new Map();
474
514
  const edges = /* @__PURE__ */ new Map();
475
515
  const autoDetectedKeywords = extractDomainKeywordsFromPaths(files);
516
+ void import_core.calculateImportSimilarity;
476
517
  for (const { file, content } of files) {
477
518
  const imports = extractImportsFromContent(content);
478
- const exports2 = extractExportsWithAST(content, file, { domainKeywords: autoDetectedKeywords }, imports);
519
+ const exports2 = extractExportsWithAST(
520
+ content,
521
+ file,
522
+ { domainKeywords: autoDetectedKeywords },
523
+ imports
524
+ );
479
525
  const tokenCost = (0, import_core.estimateTokens)(content);
480
526
  const linesOfCode = content.split("\n").length;
481
527
  nodes.set(file, {
@@ -619,7 +665,9 @@ function isTestFile(filePath) {
619
665
  }
620
666
  function calculateFragmentation(files, domain, options) {
621
667
  if (files.length <= 1) return 0;
622
- const directories = new Set(files.map((f) => f.split("/").slice(0, -1).join("/")));
668
+ const directories = new Set(
669
+ files.map((f) => f.split("/").slice(0, -1).join("/"))
670
+ );
623
671
  const uniqueDirs = directories.size;
624
672
  if (options?.useLogScale) {
625
673
  if (uniqueDirs <= 1) return 0;
@@ -692,7 +740,9 @@ function detectModuleClusters(graph, options) {
692
740
  const node = graph.nodes.get(file);
693
741
  return sum + (node?.tokenCost || 0);
694
742
  }, 0);
695
- const baseFragmentation = calculateFragmentation(files, domain, { useLogScale: !!options?.useLogScale });
743
+ const baseFragmentation = calculateFragmentation(files, domain, {
744
+ useLogScale: !!options?.useLogScale
745
+ });
696
746
  let importSimilarityTotal = 0;
697
747
  let importComparisons = 0;
698
748
  for (let i = 0; i < files.length; i++) {
@@ -713,7 +763,9 @@ function detectModuleClusters(graph, options) {
713
763
  const directoryDistance = calculateDirectoryDistance(files);
714
764
  const avgCohesion = files.reduce((sum, file) => {
715
765
  const node = graph.nodes.get(file);
716
- return sum + (node ? calculateCohesion(node.exports, file, { coUsageMatrix: graph.coUsageMatrix }) : 0);
766
+ return sum + (node ? calculateCohesion(node.exports, file, {
767
+ coUsageMatrix: graph.coUsageMatrix
768
+ }) : 0);
717
769
  }, 0) / files.length;
718
770
  const targetFiles = Math.max(1, Math.ceil(files.length / 3));
719
771
  const consolidationPlan = generateConsolidationPlan(
@@ -761,7 +813,12 @@ function extractExports(content, filePath, domainOptions, fileImports) {
761
813
  while ((match = pattern.exec(content)) !== null) {
762
814
  const name = match[1] || "default";
763
815
  const type = types[index];
764
- const inferredDomain = inferDomain(name, filePath, domainOptions, fileImports);
816
+ const inferredDomain = inferDomain(
817
+ name,
818
+ filePath,
819
+ domainOptions,
820
+ fileImports
821
+ );
765
822
  exports2.push({ name, type, inferredDomain });
766
823
  }
767
824
  });
@@ -869,11 +926,17 @@ function extractExportsWithAST(content, filePath, domainOptions, fileImports) {
869
926
  return astExports.map((exp) => ({
870
927
  name: exp.name,
871
928
  type: exp.type,
872
- inferredDomain: inferDomain(exp.name, filePath, domainOptions, fileImports),
929
+ inferredDomain: inferDomain(
930
+ exp.name,
931
+ filePath,
932
+ domainOptions,
933
+ fileImports
934
+ ),
873
935
  imports: exp.imports,
874
936
  dependencies: exp.dependencies
875
937
  }));
876
938
  } catch (error) {
939
+ void error;
877
940
  return extractExports(content, filePath, domainOptions, fileImports);
878
941
  }
879
942
  }
@@ -888,15 +951,24 @@ function calculateEnhancedCohesion(exports2, filePath, options) {
888
951
  const importCohesion = hasImportData ? calculateImportBasedCohesion(exports2) : void 0;
889
952
  const coUsageMatrix = options?.coUsageMatrix;
890
953
  const structuralCohesion = filePath && coUsageMatrix ? calculateStructuralCohesionFromCoUsage(filePath, coUsageMatrix) : void 0;
891
- const defaultWeights = { importBased: 0.5, structural: 0.3, domainBased: 0.2 };
954
+ const defaultWeights = {
955
+ importBased: 0.5,
956
+ structural: 0.3,
957
+ domainBased: 0.2
958
+ };
892
959
  const weights = { ...defaultWeights, ...options?.weights || {} };
893
960
  const signals = [];
894
- if (importCohesion !== void 0) signals.push({ score: importCohesion, weight: weights.importBased });
895
- if (structuralCohesion !== void 0) signals.push({ score: structuralCohesion, weight: weights.structural });
961
+ if (importCohesion !== void 0)
962
+ signals.push({ score: importCohesion, weight: weights.importBased });
963
+ if (structuralCohesion !== void 0)
964
+ signals.push({ score: structuralCohesion, weight: weights.structural });
896
965
  signals.push({ score: domainCohesion, weight: weights.domainBased });
897
966
  const totalWeight = signals.reduce((s, el) => s + el.weight, 0);
898
967
  if (totalWeight === 0) return domainCohesion;
899
- const combined = signals.reduce((sum, el) => sum + el.score * (el.weight / totalWeight), 0);
968
+ const combined = signals.reduce(
969
+ (sum, el) => sum + el.score * (el.weight / totalWeight),
970
+ 0
971
+ );
900
972
  return combined;
901
973
  }
902
974
  function calculateStructuralCohesionFromCoUsage(file, coUsageMatrix) {
@@ -919,7 +991,9 @@ function calculateStructuralCohesionFromCoUsage(file, coUsageMatrix) {
919
991
  return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
920
992
  }
921
993
  function calculateImportBasedCohesion(exports2) {
922
- const exportsWithImports = exports2.filter((e) => e.imports && e.imports.length > 0);
994
+ const exportsWithImports = exports2.filter(
995
+ (e) => e.imports && e.imports.length > 0
996
+ );
923
997
  if (exportsWithImports.length < 2) {
924
998
  return 1;
925
999
  }
@@ -964,6 +1038,8 @@ function calculateDomainCohesion(exports2) {
964
1038
  }
965
1039
  function classifyFile(node, cohesionScore, domains) {
966
1040
  const { exports: exports2, imports, linesOfCode, file } = node;
1041
+ void imports;
1042
+ void linesOfCode;
967
1043
  if (isBarrelExport(node)) {
968
1044
  return "barrel-export";
969
1045
  }
@@ -1042,8 +1118,12 @@ function isTypeDefinitionFile(node) {
1042
1118
  const isTypesFile = fileName?.includes("types") || fileName?.includes(".d.ts") || fileName === "types.ts" || fileName === "interfaces.ts";
1043
1119
  const lowerPath = file.toLowerCase();
1044
1120
  const isTypesPath = lowerPath.includes("/types/") || lowerPath.includes("/typings/") || lowerPath.includes("/@types/") || lowerPath.startsWith("types/") || lowerPath.startsWith("typings/");
1045
- const typeExports = exports2.filter((e) => e.type === "type" || e.type === "interface");
1046
- const runtimeExports = exports2.filter((e) => e.type === "function" || e.type === "class" || e.type === "const");
1121
+ const typeExports = exports2.filter(
1122
+ (e) => e.type === "type" || e.type === "interface"
1123
+ );
1124
+ const runtimeExports = exports2.filter(
1125
+ (e) => e.type === "function" || e.type === "class" || e.type === "const"
1126
+ );
1047
1127
  const mostlyTypes = exports2.length > 0 && typeExports.length > runtimeExports.length && typeExports.length / exports2.length > 0.7;
1048
1128
  const pureTypeFile = exports2.length > 0 && typeExports.length === exports2.length;
1049
1129
  const emptyOrReExportInTypesDir = isTypesPath && exports2.length === 0;
@@ -1224,12 +1304,7 @@ function isLambdaHandler(node) {
1224
1304
  function isServiceFile(node) {
1225
1305
  const { file, exports: exports2 } = node;
1226
1306
  const fileName = file.split("/").pop()?.toLowerCase();
1227
- const servicePatterns = [
1228
- "service",
1229
- ".service.",
1230
- "-service.",
1231
- "_service."
1232
- ];
1307
+ const servicePatterns = ["service", ".service.", "-service.", "_service."];
1233
1308
  const isServiceName = servicePatterns.some(
1234
1309
  (pattern) => fileName?.includes(pattern)
1235
1310
  );
@@ -1337,7 +1412,15 @@ function isNextJsPage(node) {
1337
1412
  }
1338
1413
  const exportNames = exports2.map((e) => e.name.toLowerCase());
1339
1414
  const hasDefaultExport = exports2.some((e) => e.type === "default");
1340
- const nextJsExports = ["metadata", "generatemetadata", "faqjsonld", "jsonld", "icon", "viewport", "dynamic"];
1415
+ const nextJsExports = [
1416
+ "metadata",
1417
+ "generatemetadata",
1418
+ "faqjsonld",
1419
+ "jsonld",
1420
+ "icon",
1421
+ "viewport",
1422
+ "dynamic"
1423
+ ];
1341
1424
  const hasNextJsExports = exportNames.some(
1342
1425
  (name) => nextJsExports.includes(name) || name.includes("jsonld")
1343
1426
  );
@@ -1411,13 +1494,44 @@ function hasRelatedExportNames(exportNames) {
1411
1494
  const stems = /* @__PURE__ */ new Set();
1412
1495
  const domains = /* @__PURE__ */ new Set();
1413
1496
  for (const name of exportNames) {
1414
- const verbs = ["get", "set", "create", "update", "delete", "fetch", "save", "load", "parse", "format", "validate", "convert", "transform", "build", "generate", "render", "send", "receive"];
1497
+ const verbs = [
1498
+ "get",
1499
+ "set",
1500
+ "create",
1501
+ "update",
1502
+ "delete",
1503
+ "fetch",
1504
+ "save",
1505
+ "load",
1506
+ "parse",
1507
+ "format",
1508
+ "validate",
1509
+ "convert",
1510
+ "transform",
1511
+ "build",
1512
+ "generate",
1513
+ "render",
1514
+ "send",
1515
+ "receive"
1516
+ ];
1415
1517
  for (const verb of verbs) {
1416
1518
  if (name.startsWith(verb) && name.length > verb.length) {
1417
1519
  stems.add(name.slice(verb.length).toLowerCase());
1418
1520
  }
1419
1521
  }
1420
- const domainPatterns = ["user", "order", "product", "session", "email", "file", "db", "s3", "dynamo", "api", "config"];
1522
+ const domainPatterns = [
1523
+ "user",
1524
+ "order",
1525
+ "product",
1526
+ "session",
1527
+ "email",
1528
+ "file",
1529
+ "db",
1530
+ "s3",
1531
+ "dynamo",
1532
+ "api",
1533
+ "config"
1534
+ ];
1421
1535
  for (const domain of domainPatterns) {
1422
1536
  if (name.includes(domain)) {
1423
1537
  domains.add(domain);
@@ -1475,11 +1589,15 @@ function hasRelatedExportNames(exportNames) {
1475
1589
  "all"
1476
1590
  ]);
1477
1591
  const singularize2 = (w) => w.endsWith("s") && w.length > 3 ? w.slice(0, -1) : w;
1478
- return new Set(tokens.filter((t) => !skip.has(t) && t.length > 2).map(singularize2));
1592
+ return new Set(
1593
+ tokens.filter((t) => !skip.has(t) && t.length > 2).map(singularize2)
1594
+ );
1479
1595
  });
1480
1596
  if (nounSets.length >= 2 && nounSets.every((s) => s.size > 0)) {
1481
1597
  const [first, ...rest] = nounSets;
1482
- const commonNouns = Array.from(first).filter((n) => rest.every((s) => s.has(n)));
1598
+ const commonNouns = Array.from(first).filter(
1599
+ (n) => rest.every((s) => s.has(n))
1600
+ );
1483
1601
  if (commonNouns.length > 0) return true;
1484
1602
  }
1485
1603
  return false;
@@ -1624,7 +1742,10 @@ function calculateContextScore(summary, costConfig) {
1624
1742
  }
1625
1743
  const recommendations = [];
1626
1744
  if (avgContextBudget > 1e4) {
1627
- const estimatedImpact = Math.min(15, Math.round((avgContextBudget - 1e4) / 1e3));
1745
+ const estimatedImpact = Math.min(
1746
+ 15,
1747
+ Math.round((avgContextBudget - 1e4) / 1e3)
1748
+ );
1628
1749
  recommendations.push({
1629
1750
  action: "Reduce file dependencies to lower context requirements",
1630
1751
  estimatedImpact,
@@ -1640,7 +1761,10 @@ function calculateContextScore(summary, costConfig) {
1640
1761
  });
1641
1762
  }
1642
1763
  if (avgFragmentation > 0.5) {
1643
- const estimatedImpact = Math.min(12, Math.round((avgFragmentation - 0.5) * 40));
1764
+ const estimatedImpact = Math.min(
1765
+ 12,
1766
+ Math.round((avgFragmentation - 0.5) * 40)
1767
+ );
1644
1768
  recommendations.push({
1645
1769
  action: "Consolidate related code into cohesive modules",
1646
1770
  estimatedImpact,
@@ -1742,21 +1866,29 @@ async function analyzeContext(options) {
1742
1866
  // Only add node_modules to exclude if includeNodeModules is false
1743
1867
  // The DEFAULT_EXCLUDE already includes node_modules, so this is only needed
1744
1868
  // if user overrides the default exclude list
1745
- exclude: includeNodeModules && scanOptions.exclude ? scanOptions.exclude.filter((pattern) => pattern !== "**/node_modules/**") : scanOptions.exclude
1869
+ exclude: includeNodeModules && scanOptions.exclude ? scanOptions.exclude.filter(
1870
+ (pattern) => pattern !== "**/node_modules/**"
1871
+ ) : scanOptions.exclude
1746
1872
  });
1747
1873
  const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
1748
1874
  const tsJsFiles = files.filter((f) => !f.toLowerCase().endsWith(".py"));
1875
+ void tsJsFiles;
1749
1876
  const fileContents = await Promise.all(
1750
1877
  files.map(async (file) => ({
1751
1878
  file,
1752
1879
  content: await (0, import_core4.readFileContent)(file)
1753
1880
  }))
1754
1881
  );
1755
- const graph = buildDependencyGraph(fileContents.filter((f) => !f.file.toLowerCase().endsWith(".py")));
1882
+ const graph = buildDependencyGraph(
1883
+ fileContents.filter((f) => !f.file.toLowerCase().endsWith(".py"))
1884
+ );
1756
1885
  let pythonResults = [];
1757
1886
  if (pythonFiles.length > 0) {
1758
1887
  const { analyzePythonContext: analyzePythonContext2 } = await Promise.resolve().then(() => (init_python_context(), python_context_exports));
1759
- const pythonMetrics = await analyzePythonContext2(pythonFiles, scanOptions.rootDir || options.rootDir || ".");
1888
+ const pythonMetrics = await analyzePythonContext2(
1889
+ pythonFiles,
1890
+ scanOptions.rootDir || options.rootDir || "."
1891
+ );
1760
1892
  pythonResults = pythonMetrics.map((metric) => {
1761
1893
  const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
1762
1894
  file: metric.file,
@@ -1769,17 +1901,25 @@ async function analyzeContext(options) {
1769
1901
  maxContextBudget,
1770
1902
  minCohesion,
1771
1903
  maxFragmentation,
1772
- circularDeps: metric.metrics.circularDependencies.map((cycle) => cycle.split(" \u2192 "))
1904
+ circularDeps: metric.metrics.circularDependencies.map(
1905
+ (cycle) => cycle.split(" \u2192 ")
1906
+ )
1773
1907
  });
1774
1908
  return {
1775
1909
  file: metric.file,
1776
- tokenCost: Math.floor(metric.contextBudget / (1 + metric.imports.length || 1)),
1910
+ tokenCost: Math.floor(
1911
+ metric.contextBudget / (1 + metric.imports.length || 1)
1912
+ ),
1777
1913
  // Estimate
1778
1914
  linesOfCode: metric.metrics.linesOfCode,
1779
1915
  importDepth: metric.importDepth,
1780
1916
  dependencyCount: metric.imports.length,
1781
- dependencyList: metric.imports.map((imp) => imp.resolvedPath || imp.source),
1782
- circularDeps: metric.metrics.circularDependencies.map((cycle) => cycle.split(" \u2192 ")),
1917
+ dependencyList: metric.imports.map(
1918
+ (imp) => imp.resolvedPath || imp.source
1919
+ ),
1920
+ circularDeps: metric.metrics.circularDependencies.map(
1921
+ (cycle) => cycle.split(" \u2192 ")
1922
+ ),
1783
1923
  cohesionScore: metric.cohesion,
1784
1924
  domains: ["python"],
1785
1925
  // Generic for now
@@ -1812,7 +1952,9 @@ async function analyzeContext(options) {
1812
1952
  const importDepth = focus === "depth" || focus === "all" ? calculateImportDepth(file, graph) : 0;
1813
1953
  const dependencyList = focus === "depth" || focus === "all" ? getTransitiveDependencies(file, graph) : [];
1814
1954
  const contextBudget = focus === "all" ? calculateContextBudget(file, graph) : node.tokenCost;
1815
- const cohesionScore = focus === "cohesion" || focus === "all" ? calculateCohesion(node.exports, file, { coUsageMatrix: graph.coUsageMatrix }) : 1;
1955
+ const cohesionScore = focus === "cohesion" || focus === "all" ? calculateCohesion(node.exports, file, {
1956
+ coUsageMatrix: graph.coUsageMatrix
1957
+ }) : 1;
1816
1958
  const fragmentationScore = fragmentationMap.get(file) || 0;
1817
1959
  const relatedFiles = [];
1818
1960
  for (const cluster of clusters) {
@@ -1833,6 +1975,10 @@ async function analyzeContext(options) {
1833
1975
  maxFragmentation,
1834
1976
  circularDeps
1835
1977
  });
1978
+ void severity;
1979
+ void issues;
1980
+ void recommendations;
1981
+ void potentialSavings;
1836
1982
  const domains = [
1837
1983
  ...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
1838
1984
  ];
@@ -1887,7 +2033,10 @@ async function analyzeContext(options) {
1887
2033
  fileClassification,
1888
2034
  severity: adjustedSeverity,
1889
2035
  issues: adjustedIssues,
1890
- recommendations: [...finalRecommendations, ...classificationRecommendations.slice(0, 1)],
2036
+ recommendations: [
2037
+ ...finalRecommendations,
2038
+ ...classificationRecommendations.slice(0, 1)
2039
+ ],
1891
2040
  potentialSavings: adjustedSavings
1892
2041
  });
1893
2042
  }
@@ -1966,7 +2115,10 @@ function generateSummary(results) {
1966
2115
  let importPairs = 0;
1967
2116
  for (let i = 0; i < files.length; i++) {
1968
2117
  for (let j = i + 1; j < files.length; j++) {
1969
- importSimTotal += jaccard2(files[i].dependencyList || [], files[j].dependencyList || []);
2118
+ importSimTotal += jaccard2(
2119
+ files[i].dependencyList || [],
2120
+ files[j].dependencyList || []
2121
+ );
1970
2122
  importPairs++;
1971
2123
  }
1972
2124
  }
@@ -1993,7 +2145,9 @@ function generateSummary(results) {
1993
2145
  fragmentedModules.sort((a, b) => b.fragmentationScore - a.fragmentationScore);
1994
2146
  const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / totalFiles;
1995
2147
  const lowCohesionFiles = results.filter((r) => r.cohesionScore < 0.6).map((r) => ({ file: r.file, score: r.cohesionScore })).sort((a, b) => a.score - b.score).slice(0, 10);
1996
- const criticalIssues = results.filter((r) => r.severity === "critical").length;
2148
+ const criticalIssues = results.filter(
2149
+ (r) => r.severity === "critical"
2150
+ ).length;
1997
2151
  const majorIssues = results.filter((r) => r.severity === "major").length;
1998
2152
  const minorIssues = results.filter((r) => r.severity === "minor").length;
1999
2153
  const totalPotentialSavings = results.reduce(
@@ -2043,10 +2197,10 @@ function analyzeIssues(params) {
2043
2197
  let potentialSavings = 0;
2044
2198
  if (circularDeps.length > 0) {
2045
2199
  severity = "critical";
2046
- issues.push(
2047
- `Part of ${circularDeps.length} circular dependency chain(s)`
2200
+ issues.push(`Part of ${circularDeps.length} circular dependency chain(s)`);
2201
+ recommendations.push(
2202
+ "Break circular dependencies by extracting interfaces or using dependency injection"
2048
2203
  );
2049
- recommendations.push("Break circular dependencies by extracting interfaces or using dependency injection");
2050
2204
  potentialSavings += contextBudget * 0.2;
2051
2205
  }
2052
2206
  if (importDepth > maxDepth * 1.5) {
@@ -2056,25 +2210,37 @@ function analyzeIssues(params) {
2056
2210
  potentialSavings += contextBudget * 0.3;
2057
2211
  } else if (importDepth > maxDepth) {
2058
2212
  severity = severity === "critical" ? "critical" : "major";
2059
- issues.push(`Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`);
2213
+ issues.push(
2214
+ `Import depth ${importDepth} exceeds recommended maximum ${maxDepth}`
2215
+ );
2060
2216
  recommendations.push("Consider reducing dependency depth");
2061
2217
  potentialSavings += contextBudget * 0.15;
2062
2218
  }
2063
2219
  if (contextBudget > maxContextBudget * 1.5) {
2064
2220
  severity = severity === "critical" ? "critical" : "critical";
2065
- issues.push(`Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`);
2066
- recommendations.push("Split into smaller modules or reduce dependency tree");
2221
+ issues.push(
2222
+ `Context budget ${contextBudget.toLocaleString()} tokens is 50% over limit`
2223
+ );
2224
+ recommendations.push(
2225
+ "Split into smaller modules or reduce dependency tree"
2226
+ );
2067
2227
  potentialSavings += contextBudget * 0.4;
2068
2228
  } else if (contextBudget > maxContextBudget) {
2069
2229
  severity = severity === "critical" || severity === "major" ? severity : "major";
2070
- issues.push(`Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`);
2230
+ issues.push(
2231
+ `Context budget ${contextBudget.toLocaleString()} exceeds ${maxContextBudget.toLocaleString()}`
2232
+ );
2071
2233
  recommendations.push("Reduce file size or dependencies");
2072
2234
  potentialSavings += contextBudget * 0.2;
2073
2235
  }
2074
2236
  if (cohesionScore < minCohesion * 0.5) {
2075
2237
  severity = severity === "critical" ? "critical" : "major";
2076
- issues.push(`Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`);
2077
- recommendations.push("Split file by domain - separate unrelated functionality");
2238
+ issues.push(
2239
+ `Very low cohesion (${(cohesionScore * 100).toFixed(0)}%) - mixed concerns`
2240
+ );
2241
+ recommendations.push(
2242
+ "Split file by domain - separate unrelated functionality"
2243
+ );
2078
2244
  potentialSavings += contextBudget * 0.25;
2079
2245
  } else if (cohesionScore < minCohesion) {
2080
2246
  severity = severity === "critical" || severity === "major" ? severity : "minor";
@@ -2084,7 +2250,9 @@ function analyzeIssues(params) {
2084
2250
  }
2085
2251
  if (fragmentationScore > maxFragmentation) {
2086
2252
  severity = severity === "critical" || severity === "major" ? severity : "minor";
2087
- issues.push(`High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`);
2253
+ issues.push(
2254
+ `High fragmentation (${(fragmentationScore * 100).toFixed(0)}%) - scattered implementation`
2255
+ );
2088
2256
  recommendations.push("Consolidate with related files in same domain");
2089
2257
  potentialSavings += contextBudget * 0.3;
2090
2258
  }
@@ -2094,11 +2262,18 @@ function analyzeIssues(params) {
2094
2262
  }
2095
2263
  if (isBuildArtifact(file)) {
2096
2264
  issues.push("Detected build artifact (bundled/output file)");
2097
- recommendations.push("Exclude build outputs (e.g., cdk.out, dist, build, .next) from analysis");
2265
+ recommendations.push(
2266
+ "Exclude build outputs (e.g., cdk.out, dist, build, .next) from analysis"
2267
+ );
2098
2268
  severity = downgradeSeverity(severity);
2099
2269
  potentialSavings = 0;
2100
2270
  }
2101
- return { severity, issues, recommendations, potentialSavings: Math.floor(potentialSavings) };
2271
+ return {
2272
+ severity,
2273
+ issues,
2274
+ recommendations,
2275
+ potentialSavings: Math.floor(potentialSavings)
2276
+ };
2102
2277
  }
2103
2278
  function isBuildArtifact(filePath) {
2104
2279
  const lower = filePath.toLowerCase();
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  getCoUsageData,
13
13
  getSmartDefaults,
14
14
  inferDomainFromSemantics
15
- } from "./chunk-EVX2W2BK.mjs";
15
+ } from "./chunk-7LUSCLGR.mjs";
16
16
  export {
17
17
  adjustFragmentationForClassification,
18
18
  analyzeContext,