@aiready/context-analyzer 0.9.35 → 0.9.38

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