@aiready/context-analyzer 0.9.22 → 0.9.23

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/cli.js CHANGED
@@ -890,6 +890,95 @@ function calculateDomainCohesion(exports2) {
890
890
  const maxEntropy = Math.log2(total);
891
891
  return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
892
892
  }
893
+ function classifyFile(node, cohesionScore, domains) {
894
+ const { exports: exports2, imports, linesOfCode } = node;
895
+ if (isBarrelExport(node)) {
896
+ return "barrel-export";
897
+ }
898
+ if (isTypeDefinitionFile(node)) {
899
+ return "type-definition";
900
+ }
901
+ const uniqueDomains = domains.filter((d) => d !== "unknown");
902
+ const hasSingleDomain = uniqueDomains.length <= 1;
903
+ const hasHighCohesion = cohesionScore >= 0.7;
904
+ if (hasSingleDomain && hasHighCohesion) {
905
+ return "cohesive-module";
906
+ }
907
+ const hasMultipleDomains = uniqueDomains.length > 1;
908
+ const hasLowCohesion = cohesionScore < 0.5;
909
+ if (hasMultipleDomains || hasLowCohesion) {
910
+ return "mixed-concerns";
911
+ }
912
+ return "unknown";
913
+ }
914
+ function isBarrelExport(node) {
915
+ const { file, exports: exports2, imports, linesOfCode } = node;
916
+ const fileName = file.split("/").pop()?.toLowerCase();
917
+ const isIndexFile = fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx";
918
+ const hasReExports = exports2.length > 0 && imports.length > 0;
919
+ const highExportToLinesRatio = exports2.length > 3 && linesOfCode < exports2.length * 5;
920
+ const sparseCode = linesOfCode > 0 && linesOfCode < 50 && exports2.length >= 2;
921
+ if (isIndexFile && hasReExports) {
922
+ return true;
923
+ }
924
+ if (highExportToLinesRatio && imports.length >= exports2.length * 0.5) {
925
+ return true;
926
+ }
927
+ if (sparseCode && imports.length > 0) {
928
+ return true;
929
+ }
930
+ return false;
931
+ }
932
+ function isTypeDefinitionFile(node) {
933
+ const { file, exports: exports2 } = node;
934
+ const fileName = file.split("/").pop()?.toLowerCase();
935
+ const isTypesFile = fileName?.includes("types") || fileName?.includes(".d.ts") || fileName === "types.ts" || fileName === "interfaces.ts";
936
+ const typeExports = exports2.filter((e) => e.type === "type" || e.type === "interface");
937
+ const runtimeExports = exports2.filter((e) => e.type === "function" || e.type === "class" || e.type === "const");
938
+ const mostlyTypes = exports2.length > 0 && typeExports.length > runtimeExports.length && typeExports.length / exports2.length > 0.7;
939
+ return isTypesFile || mostlyTypes;
940
+ }
941
+ function adjustFragmentationForClassification(baseFragmentation, classification) {
942
+ switch (classification) {
943
+ case "barrel-export":
944
+ return 0;
945
+ case "type-definition":
946
+ return 0;
947
+ case "cohesive-module":
948
+ return baseFragmentation * 0.3;
949
+ case "mixed-concerns":
950
+ return baseFragmentation;
951
+ default:
952
+ return baseFragmentation * 0.7;
953
+ }
954
+ }
955
+ function getClassificationRecommendations(classification, file, issues) {
956
+ switch (classification) {
957
+ case "barrel-export":
958
+ return [
959
+ "Barrel export file detected - multiple domains are expected here",
960
+ "Consider if this barrel export improves or hinders discoverability"
961
+ ];
962
+ case "type-definition":
963
+ return [
964
+ "Type definition file - centralized types improve consistency",
965
+ "Consider splitting if file becomes too large (>500 lines)"
966
+ ];
967
+ case "cohesive-module":
968
+ return [
969
+ "Module has good cohesion despite its size",
970
+ "Consider documenting the module boundaries for AI assistants"
971
+ ];
972
+ case "mixed-concerns":
973
+ return [
974
+ "Consider splitting this file by domain",
975
+ "Identify independent responsibilities and extract them",
976
+ "Review import dependencies to understand coupling"
977
+ ];
978
+ default:
979
+ return issues;
980
+ }
981
+ }
893
982
 
894
983
  // src/index.ts
895
984
  async function analyzeContext(options) {
@@ -952,6 +1041,8 @@ async function analyzeContext(options) {
952
1041
  contextBudget: metric.contextBudget,
953
1042
  fragmentationScore: 0,
954
1043
  relatedFiles: [],
1044
+ fileClassification: "unknown",
1045
+ // Python files not yet classified
955
1046
  severity,
956
1047
  issues,
957
1048
  recommendations,
@@ -999,6 +1090,33 @@ async function analyzeContext(options) {
999
1090
  const domains = [
1000
1091
  ...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
1001
1092
  ];
1093
+ const fileClassification = classifyFile(node, cohesionScore, domains);
1094
+ const adjustedFragmentationScore = adjustFragmentationForClassification(
1095
+ fragmentationScore,
1096
+ fileClassification
1097
+ );
1098
+ const classificationRecommendations = getClassificationRecommendations(
1099
+ fileClassification,
1100
+ file,
1101
+ issues
1102
+ );
1103
+ const {
1104
+ severity: adjustedSeverity,
1105
+ issues: adjustedIssues,
1106
+ recommendations: finalRecommendations,
1107
+ potentialSavings: adjustedSavings
1108
+ } = analyzeIssues({
1109
+ file,
1110
+ importDepth,
1111
+ contextBudget,
1112
+ cohesionScore,
1113
+ fragmentationScore: adjustedFragmentationScore,
1114
+ maxDepth,
1115
+ maxContextBudget,
1116
+ minCohesion,
1117
+ maxFragmentation,
1118
+ circularDeps
1119
+ });
1002
1120
  results.push({
1003
1121
  file,
1004
1122
  tokenCost: node.tokenCost,
@@ -1011,12 +1129,13 @@ async function analyzeContext(options) {
1011
1129
  domains,
1012
1130
  exportCount: node.exports.length,
1013
1131
  contextBudget,
1014
- fragmentationScore,
1132
+ fragmentationScore: adjustedFragmentationScore,
1015
1133
  relatedFiles,
1016
- severity,
1017
- issues,
1018
- recommendations,
1019
- potentialSavings
1134
+ fileClassification,
1135
+ severity: adjustedSeverity,
1136
+ issues: adjustedIssues,
1137
+ recommendations: [...finalRecommendations, ...classificationRecommendations.slice(0, 1)],
1138
+ potentialSavings: adjustedSavings
1020
1139
  });
1021
1140
  }
1022
1141
  const allResults = [...results, ...pythonResults];
package/dist/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  analyzeContext,
4
4
  generateSummary
5
- } from "./chunk-VTALAPQZ.mjs";
5
+ } from "./chunk-MBE4AQP5.mjs";
6
6
  import "./chunk-Y6FXYEAI.mjs";
7
7
 
8
8
  // src/cli.ts
package/dist/index.d.mts CHANGED
@@ -22,11 +22,17 @@ interface ContextAnalysisResult {
22
22
  contextBudget: number;
23
23
  fragmentationScore: number;
24
24
  relatedFiles: string[];
25
+ fileClassification: FileClassification;
25
26
  severity: 'critical' | 'major' | 'minor' | 'info';
26
27
  issues: string[];
27
28
  recommendations: string[];
28
29
  potentialSavings: number;
29
30
  }
31
+ /**
32
+ * Classification of file type for analysis context
33
+ * Helps distinguish real issues from false positives
34
+ */
35
+ type FileClassification = 'barrel-export' | 'type-definition' | 'cohesive-module' | 'mixed-concerns' | 'unknown';
30
36
  interface ModuleCluster {
31
37
  domain: string;
32
38
  files: string[];
@@ -116,6 +122,28 @@ interface TypeDependency {
116
122
  usedBy: string[];
117
123
  }
118
124
 
125
+ /**
126
+ * Classify a file based on its characteristics to help distinguish
127
+ * real issues from false positives.
128
+ *
129
+ * Classification types:
130
+ * - barrel-export: Re-exports from other modules (index.ts files)
131
+ * - type-definition: Primarily type/interface definitions
132
+ * - cohesive-module: Single domain, high cohesion (acceptable large files)
133
+ * - mixed-concerns: Multiple domains, potential refactoring candidate
134
+ * - unknown: Unable to classify
135
+ */
136
+ declare function classifyFile(node: DependencyNode, cohesionScore: number, domains: string[]): FileClassification;
137
+ /**
138
+ * Adjust fragmentation score based on file classification
139
+ *
140
+ * This reduces false positives by:
141
+ * - Ignoring fragmentation for barrel exports (they're meant to aggregate)
142
+ * - Ignoring fragmentation for type definitions (centralized types are good)
143
+ * - Reducing fragmentation for cohesive modules (large but focused is OK)
144
+ */
145
+ declare function adjustFragmentationForClassification(baseFragmentation: number, classification: FileClassification): number;
146
+
119
147
  /**
120
148
  * Calculate AI Readiness Score for context efficiency (0-100)
121
149
  *
@@ -184,4 +212,4 @@ declare function analyzeContext(options: ContextAnalyzerOptions): Promise<Contex
184
212
  */
185
213
  declare function generateSummary(results: ContextAnalysisResult[]): ContextSummary;
186
214
 
187
- export { type CoUsageData, type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type DomainAssignment, type DomainSignals, type ModuleCluster, type TypeDependency, analyzeContext, buildCoUsageMatrix, buildTypeGraph, calculateContextScore, calculateDomainConfidence, findConsolidationCandidates, findSemanticClusters, generateSummary, getCoUsageData, getSmartDefaults, inferDomainFromSemantics };
215
+ export { type CoUsageData, type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type DomainAssignment, type DomainSignals, type FileClassification, type ModuleCluster, type TypeDependency, adjustFragmentationForClassification, analyzeContext, buildCoUsageMatrix, buildTypeGraph, calculateContextScore, calculateDomainConfidence, classifyFile, findConsolidationCandidates, findSemanticClusters, generateSummary, getCoUsageData, getSmartDefaults, inferDomainFromSemantics };
package/dist/index.d.ts CHANGED
@@ -22,11 +22,17 @@ interface ContextAnalysisResult {
22
22
  contextBudget: number;
23
23
  fragmentationScore: number;
24
24
  relatedFiles: string[];
25
+ fileClassification: FileClassification;
25
26
  severity: 'critical' | 'major' | 'minor' | 'info';
26
27
  issues: string[];
27
28
  recommendations: string[];
28
29
  potentialSavings: number;
29
30
  }
31
+ /**
32
+ * Classification of file type for analysis context
33
+ * Helps distinguish real issues from false positives
34
+ */
35
+ type FileClassification = 'barrel-export' | 'type-definition' | 'cohesive-module' | 'mixed-concerns' | 'unknown';
30
36
  interface ModuleCluster {
31
37
  domain: string;
32
38
  files: string[];
@@ -116,6 +122,28 @@ interface TypeDependency {
116
122
  usedBy: string[];
117
123
  }
118
124
 
125
+ /**
126
+ * Classify a file based on its characteristics to help distinguish
127
+ * real issues from false positives.
128
+ *
129
+ * Classification types:
130
+ * - barrel-export: Re-exports from other modules (index.ts files)
131
+ * - type-definition: Primarily type/interface definitions
132
+ * - cohesive-module: Single domain, high cohesion (acceptable large files)
133
+ * - mixed-concerns: Multiple domains, potential refactoring candidate
134
+ * - unknown: Unable to classify
135
+ */
136
+ declare function classifyFile(node: DependencyNode, cohesionScore: number, domains: string[]): FileClassification;
137
+ /**
138
+ * Adjust fragmentation score based on file classification
139
+ *
140
+ * This reduces false positives by:
141
+ * - Ignoring fragmentation for barrel exports (they're meant to aggregate)
142
+ * - Ignoring fragmentation for type definitions (centralized types are good)
143
+ * - Reducing fragmentation for cohesive modules (large but focused is OK)
144
+ */
145
+ declare function adjustFragmentationForClassification(baseFragmentation: number, classification: FileClassification): number;
146
+
119
147
  /**
120
148
  * Calculate AI Readiness Score for context efficiency (0-100)
121
149
  *
@@ -184,4 +212,4 @@ declare function analyzeContext(options: ContextAnalyzerOptions): Promise<Contex
184
212
  */
185
213
  declare function generateSummary(results: ContextAnalysisResult[]): ContextSummary;
186
214
 
187
- export { type CoUsageData, type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type DomainAssignment, type DomainSignals, type ModuleCluster, type TypeDependency, analyzeContext, buildCoUsageMatrix, buildTypeGraph, calculateContextScore, calculateDomainConfidence, findConsolidationCandidates, findSemanticClusters, generateSummary, getCoUsageData, getSmartDefaults, inferDomainFromSemantics };
215
+ export { type CoUsageData, type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type DomainAssignment, type DomainSignals, type FileClassification, type ModuleCluster, type TypeDependency, adjustFragmentationForClassification, analyzeContext, buildCoUsageMatrix, buildTypeGraph, calculateContextScore, calculateDomainConfidence, classifyFile, findConsolidationCandidates, findSemanticClusters, generateSummary, getCoUsageData, getSmartDefaults, inferDomainFromSemantics };
package/dist/index.js CHANGED
@@ -229,11 +229,13 @@ var init_python_context = __esm({
229
229
  // src/index.ts
230
230
  var index_exports = {};
231
231
  __export(index_exports, {
232
+ adjustFragmentationForClassification: () => adjustFragmentationForClassification,
232
233
  analyzeContext: () => analyzeContext,
233
234
  buildCoUsageMatrix: () => buildCoUsageMatrix,
234
235
  buildTypeGraph: () => buildTypeGraph,
235
236
  calculateContextScore: () => calculateContextScore,
236
237
  calculateDomainConfidence: () => calculateDomainConfidence,
238
+ classifyFile: () => classifyFile,
237
239
  findConsolidationCandidates: () => findConsolidationCandidates,
238
240
  findSemanticClusters: () => findSemanticClusters,
239
241
  generateSummary: () => generateSummary,
@@ -963,6 +965,95 @@ function calculateDomainCohesion(exports2) {
963
965
  const maxEntropy = Math.log2(total);
964
966
  return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
965
967
  }
968
+ function classifyFile(node, cohesionScore, domains) {
969
+ const { exports: exports2, imports, linesOfCode } = node;
970
+ if (isBarrelExport(node)) {
971
+ return "barrel-export";
972
+ }
973
+ if (isTypeDefinitionFile(node)) {
974
+ return "type-definition";
975
+ }
976
+ const uniqueDomains = domains.filter((d) => d !== "unknown");
977
+ const hasSingleDomain = uniqueDomains.length <= 1;
978
+ const hasHighCohesion = cohesionScore >= 0.7;
979
+ if (hasSingleDomain && hasHighCohesion) {
980
+ return "cohesive-module";
981
+ }
982
+ const hasMultipleDomains = uniqueDomains.length > 1;
983
+ const hasLowCohesion = cohesionScore < 0.5;
984
+ if (hasMultipleDomains || hasLowCohesion) {
985
+ return "mixed-concerns";
986
+ }
987
+ return "unknown";
988
+ }
989
+ function isBarrelExport(node) {
990
+ const { file, exports: exports2, imports, linesOfCode } = node;
991
+ const fileName = file.split("/").pop()?.toLowerCase();
992
+ const isIndexFile = fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx";
993
+ const hasReExports = exports2.length > 0 && imports.length > 0;
994
+ const highExportToLinesRatio = exports2.length > 3 && linesOfCode < exports2.length * 5;
995
+ const sparseCode = linesOfCode > 0 && linesOfCode < 50 && exports2.length >= 2;
996
+ if (isIndexFile && hasReExports) {
997
+ return true;
998
+ }
999
+ if (highExportToLinesRatio && imports.length >= exports2.length * 0.5) {
1000
+ return true;
1001
+ }
1002
+ if (sparseCode && imports.length > 0) {
1003
+ return true;
1004
+ }
1005
+ return false;
1006
+ }
1007
+ function isTypeDefinitionFile(node) {
1008
+ const { file, exports: exports2 } = node;
1009
+ const fileName = file.split("/").pop()?.toLowerCase();
1010
+ const isTypesFile = fileName?.includes("types") || fileName?.includes(".d.ts") || fileName === "types.ts" || fileName === "interfaces.ts";
1011
+ const typeExports = exports2.filter((e) => e.type === "type" || e.type === "interface");
1012
+ const runtimeExports = exports2.filter((e) => e.type === "function" || e.type === "class" || e.type === "const");
1013
+ const mostlyTypes = exports2.length > 0 && typeExports.length > runtimeExports.length && typeExports.length / exports2.length > 0.7;
1014
+ return isTypesFile || mostlyTypes;
1015
+ }
1016
+ function adjustFragmentationForClassification(baseFragmentation, classification) {
1017
+ switch (classification) {
1018
+ case "barrel-export":
1019
+ return 0;
1020
+ case "type-definition":
1021
+ return 0;
1022
+ case "cohesive-module":
1023
+ return baseFragmentation * 0.3;
1024
+ case "mixed-concerns":
1025
+ return baseFragmentation;
1026
+ default:
1027
+ return baseFragmentation * 0.7;
1028
+ }
1029
+ }
1030
+ function getClassificationRecommendations(classification, file, issues) {
1031
+ switch (classification) {
1032
+ case "barrel-export":
1033
+ return [
1034
+ "Barrel export file detected - multiple domains are expected here",
1035
+ "Consider if this barrel export improves or hinders discoverability"
1036
+ ];
1037
+ case "type-definition":
1038
+ return [
1039
+ "Type definition file - centralized types improve consistency",
1040
+ "Consider splitting if file becomes too large (>500 lines)"
1041
+ ];
1042
+ case "cohesive-module":
1043
+ return [
1044
+ "Module has good cohesion despite its size",
1045
+ "Consider documenting the module boundaries for AI assistants"
1046
+ ];
1047
+ case "mixed-concerns":
1048
+ return [
1049
+ "Consider splitting this file by domain",
1050
+ "Identify independent responsibilities and extract them",
1051
+ "Review import dependencies to understand coupling"
1052
+ ];
1053
+ default:
1054
+ return issues;
1055
+ }
1056
+ }
966
1057
 
967
1058
  // src/scoring.ts
968
1059
  function calculateContextScore(summary) {
@@ -1176,6 +1267,8 @@ async function analyzeContext(options) {
1176
1267
  contextBudget: metric.contextBudget,
1177
1268
  fragmentationScore: 0,
1178
1269
  relatedFiles: [],
1270
+ fileClassification: "unknown",
1271
+ // Python files not yet classified
1179
1272
  severity,
1180
1273
  issues,
1181
1274
  recommendations,
@@ -1223,6 +1316,33 @@ async function analyzeContext(options) {
1223
1316
  const domains = [
1224
1317
  ...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
1225
1318
  ];
1319
+ const fileClassification = classifyFile(node, cohesionScore, domains);
1320
+ const adjustedFragmentationScore = adjustFragmentationForClassification(
1321
+ fragmentationScore,
1322
+ fileClassification
1323
+ );
1324
+ const classificationRecommendations = getClassificationRecommendations(
1325
+ fileClassification,
1326
+ file,
1327
+ issues
1328
+ );
1329
+ const {
1330
+ severity: adjustedSeverity,
1331
+ issues: adjustedIssues,
1332
+ recommendations: finalRecommendations,
1333
+ potentialSavings: adjustedSavings
1334
+ } = analyzeIssues({
1335
+ file,
1336
+ importDepth,
1337
+ contextBudget,
1338
+ cohesionScore,
1339
+ fragmentationScore: adjustedFragmentationScore,
1340
+ maxDepth,
1341
+ maxContextBudget,
1342
+ minCohesion,
1343
+ maxFragmentation,
1344
+ circularDeps
1345
+ });
1226
1346
  results.push({
1227
1347
  file,
1228
1348
  tokenCost: node.tokenCost,
@@ -1235,12 +1355,13 @@ async function analyzeContext(options) {
1235
1355
  domains,
1236
1356
  exportCount: node.exports.length,
1237
1357
  contextBudget,
1238
- fragmentationScore,
1358
+ fragmentationScore: adjustedFragmentationScore,
1239
1359
  relatedFiles,
1240
- severity,
1241
- issues,
1242
- recommendations,
1243
- potentialSavings
1360
+ fileClassification,
1361
+ severity: adjustedSeverity,
1362
+ issues: adjustedIssues,
1363
+ recommendations: [...finalRecommendations, ...classificationRecommendations.slice(0, 1)],
1364
+ potentialSavings: adjustedSavings
1244
1365
  });
1245
1366
  }
1246
1367
  const allResults = [...results, ...pythonResults];
@@ -1470,11 +1591,13 @@ function downgradeSeverity(s) {
1470
1591
  }
1471
1592
  // Annotate the CommonJS export names for ESM import in node:
1472
1593
  0 && (module.exports = {
1594
+ adjustFragmentationForClassification,
1473
1595
  analyzeContext,
1474
1596
  buildCoUsageMatrix,
1475
1597
  buildTypeGraph,
1476
1598
  calculateContextScore,
1477
1599
  calculateDomainConfidence,
1600
+ classifyFile,
1478
1601
  findConsolidationCandidates,
1479
1602
  findSemanticClusters,
1480
1603
  generateSummary,
package/dist/index.mjs CHANGED
@@ -1,23 +1,27 @@
1
1
  import {
2
+ adjustFragmentationForClassification,
2
3
  analyzeContext,
3
4
  buildCoUsageMatrix,
4
5
  buildTypeGraph,
5
6
  calculateContextScore,
6
7
  calculateDomainConfidence,
8
+ classifyFile,
7
9
  findConsolidationCandidates,
8
10
  findSemanticClusters,
9
11
  generateSummary,
10
12
  getCoUsageData,
11
13
  getSmartDefaults,
12
14
  inferDomainFromSemantics
13
- } from "./chunk-VTALAPQZ.mjs";
15
+ } from "./chunk-MBE4AQP5.mjs";
14
16
  import "./chunk-Y6FXYEAI.mjs";
15
17
  export {
18
+ adjustFragmentationForClassification,
16
19
  analyzeContext,
17
20
  buildCoUsageMatrix,
18
21
  buildTypeGraph,
19
22
  calculateContextScore,
20
23
  calculateDomainConfidence,
24
+ classifyFile,
21
25
  findConsolidationCandidates,
22
26
  findSemanticClusters,
23
27
  generateSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/context-analyzer",
3
- "version": "0.9.22",
3
+ "version": "0.9.23",
4
4
  "description": "AI context window cost analysis - detect fragmented code, deep import chains, and expensive context budgets",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",