@aiready/context-analyzer 0.21.13 → 0.21.15

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
@@ -923,7 +923,19 @@ var CONFIG_NAME_PATTERNS = [
923
923
  "next.config",
924
924
  "sst.config"
925
925
  ];
926
+ function isBoilerplateBarrel(node) {
927
+ const { exports: exports2, tokenCost } = node;
928
+ if (!exports2 || exports2.length === 0) return false;
929
+ const isPurelyReexports = exports2.every((exp) => !!exp.source);
930
+ if (!isPurelyReexports) return false;
931
+ if (tokenCost > 500) return false;
932
+ const sources = new Set(exports2.map((exp) => exp.source));
933
+ const isSingleSourcePassThrough = sources.size === 1;
934
+ const isMeaninglessAggregation = sources.size > 0 && sources.size < 3;
935
+ return isSingleSourcePassThrough || isMeaninglessAggregation;
936
+ }
926
937
  function isBarrelExport(node) {
938
+ if (isBoilerplateBarrel(node)) return false;
927
939
  const { file, exports: exports2 } = node;
928
940
  const fileName = file.split("/").pop()?.toLowerCase();
929
941
  const isIndexFile = fileName === "index.ts" || fileName === "index.js";
@@ -1054,6 +1066,7 @@ function isHubAndSpokeFile(node) {
1054
1066
  // src/classifier.ts
1055
1067
  var Classification = {
1056
1068
  BARREL: "barrel-export",
1069
+ BOILERPLATE: "boilerplate-barrel",
1057
1070
  TYPE_DEFINITION: "type-definition",
1058
1071
  NEXTJS_PAGE: "nextjs-page",
1059
1072
  LAMBDA_HANDLER: "lambda-handler",
@@ -1067,6 +1080,9 @@ var Classification = {
1067
1080
  UNKNOWN: "unknown"
1068
1081
  };
1069
1082
  function classifyFile(node, cohesionScore = 1, domains = []) {
1083
+ if (isBoilerplateBarrel(node)) {
1084
+ return Classification.BOILERPLATE;
1085
+ }
1070
1086
  if (isBarrelExport(node)) {
1071
1087
  return Classification.BARREL;
1072
1088
  }
@@ -1115,6 +1131,9 @@ function classifyFile(node, cohesionScore = 1, domains = []) {
1115
1131
  }
1116
1132
  function adjustCohesionForClassification(baseCohesion, classification, node) {
1117
1133
  switch (classification) {
1134
+ case Classification.BOILERPLATE:
1135
+ return 0.2;
1136
+ // Redundant indirection is low cohesion (architectural theater)
1118
1137
  case Classification.BARREL:
1119
1138
  return 1;
1120
1139
  case Classification.TYPE_DEFINITION:
@@ -1190,6 +1209,9 @@ function hasRelatedExportNames(exportNames) {
1190
1209
  }
1191
1210
  function adjustFragmentationForClassification(baseFragmentation, classification) {
1192
1211
  switch (classification) {
1212
+ case Classification.BOILERPLATE:
1213
+ return baseFragmentation * 1.5;
1214
+ // Redundant barrels increase fragmentation
1193
1215
  case Classification.BARREL:
1194
1216
  return 0;
1195
1217
  case Classification.TYPE_DEFINITION:
@@ -1298,6 +1320,12 @@ function detectModuleClusters(graph, options) {
1298
1320
  // src/remediation.ts
1299
1321
  function getClassificationRecommendations(classification, file, issues) {
1300
1322
  switch (classification) {
1323
+ case "boilerplate-barrel":
1324
+ return [
1325
+ "Redundant indirection detected (architectural theater)",
1326
+ "Remove this pass-through barrel export to reduce cognitive load",
1327
+ "Consider combining into meaningful domain exports if necessary"
1328
+ ];
1301
1329
  case "barrel-export":
1302
1330
  return [
1303
1331
  "Barrel export file detected - multiple domains are expected here",
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  generateHTMLReport,
6
6
  generateSummary,
7
7
  runInteractiveSetup
8
- } from "./chunk-CAX2MOUZ.mjs";
8
+ } from "./chunk-BEZPBI5C.mjs";
9
9
  import "./chunk-64U3PNO3.mjs";
10
10
 
11
11
  // src/cli.ts
package/dist/index.d.mts CHANGED
@@ -69,7 +69,7 @@ interface ContextAnalysisResult {
69
69
  * Classification of file type for analysis context
70
70
  * Helps distinguish real issues from false positives
71
71
  */
72
- type FileClassification = 'barrel-export' | 'type-definition' | 'cohesive-module' | 'utility-module' | 'service-file' | 'lambda-handler' | 'email-template' | 'parser-file' | 'nextjs-page' | 'spoke-module' | 'mixed-concerns' | 'unknown';
72
+ type FileClassification = 'barrel-export' | 'boilerplate-barrel' | 'type-definition' | 'cohesive-module' | 'utility-module' | 'service-file' | 'lambda-handler' | 'email-template' | 'parser-file' | 'nextjs-page' | 'spoke-module' | 'mixed-concerns' | 'unknown';
73
73
  interface ModuleCluster {
74
74
  domain: string;
75
75
  files: string[];
@@ -292,6 +292,7 @@ declare function calculateDirectoryDistance(files: string[]): number;
292
292
  */
293
293
  declare const Classification: {
294
294
  BARREL: "barrel-export";
295
+ BOILERPLATE: "boilerplate-barrel";
295
296
  TYPE_DEFINITION: "type-definition";
296
297
  NEXTJS_PAGE: "nextjs-page";
297
298
  LAMBDA_HANDLER: "lambda-handler";
package/dist/index.d.ts CHANGED
@@ -69,7 +69,7 @@ interface ContextAnalysisResult {
69
69
  * Classification of file type for analysis context
70
70
  * Helps distinguish real issues from false positives
71
71
  */
72
- type FileClassification = 'barrel-export' | 'type-definition' | 'cohesive-module' | 'utility-module' | 'service-file' | 'lambda-handler' | 'email-template' | 'parser-file' | 'nextjs-page' | 'spoke-module' | 'mixed-concerns' | 'unknown';
72
+ type FileClassification = 'barrel-export' | 'boilerplate-barrel' | 'type-definition' | 'cohesive-module' | 'utility-module' | 'service-file' | 'lambda-handler' | 'email-template' | 'parser-file' | 'nextjs-page' | 'spoke-module' | 'mixed-concerns' | 'unknown';
73
73
  interface ModuleCluster {
74
74
  domain: string;
75
75
  files: string[];
@@ -292,6 +292,7 @@ declare function calculateDirectoryDistance(files: string[]): number;
292
292
  */
293
293
  declare const Classification: {
294
294
  BARREL: "barrel-export";
295
+ BOILERPLATE: "boilerplate-barrel";
295
296
  TYPE_DEFINITION: "type-definition";
296
297
  NEXTJS_PAGE: "nextjs-page";
297
298
  LAMBDA_HANDLER: "lambda-handler";
package/dist/index.js CHANGED
@@ -1057,7 +1057,19 @@ var CONFIG_NAME_PATTERNS = [
1057
1057
  "next.config",
1058
1058
  "sst.config"
1059
1059
  ];
1060
+ function isBoilerplateBarrel(node) {
1061
+ const { exports: exports2, tokenCost } = node;
1062
+ if (!exports2 || exports2.length === 0) return false;
1063
+ const isPurelyReexports = exports2.every((exp) => !!exp.source);
1064
+ if (!isPurelyReexports) return false;
1065
+ if (tokenCost > 500) return false;
1066
+ const sources = new Set(exports2.map((exp) => exp.source));
1067
+ const isSingleSourcePassThrough = sources.size === 1;
1068
+ const isMeaninglessAggregation = sources.size > 0 && sources.size < 3;
1069
+ return isSingleSourcePassThrough || isMeaninglessAggregation;
1070
+ }
1060
1071
  function isBarrelExport(node) {
1072
+ if (isBoilerplateBarrel(node)) return false;
1061
1073
  const { file, exports: exports2 } = node;
1062
1074
  const fileName = file.split("/").pop()?.toLowerCase();
1063
1075
  const isIndexFile = fileName === "index.ts" || fileName === "index.js";
@@ -1188,6 +1200,7 @@ function isHubAndSpokeFile(node) {
1188
1200
  // src/classifier.ts
1189
1201
  var Classification = {
1190
1202
  BARREL: "barrel-export",
1203
+ BOILERPLATE: "boilerplate-barrel",
1191
1204
  TYPE_DEFINITION: "type-definition",
1192
1205
  NEXTJS_PAGE: "nextjs-page",
1193
1206
  LAMBDA_HANDLER: "lambda-handler",
@@ -1201,6 +1214,9 @@ var Classification = {
1201
1214
  UNKNOWN: "unknown"
1202
1215
  };
1203
1216
  function classifyFile(node, cohesionScore = 1, domains = []) {
1217
+ if (isBoilerplateBarrel(node)) {
1218
+ return Classification.BOILERPLATE;
1219
+ }
1204
1220
  if (isBarrelExport(node)) {
1205
1221
  return Classification.BARREL;
1206
1222
  }
@@ -1249,6 +1265,9 @@ function classifyFile(node, cohesionScore = 1, domains = []) {
1249
1265
  }
1250
1266
  function adjustCohesionForClassification(baseCohesion, classification, node) {
1251
1267
  switch (classification) {
1268
+ case Classification.BOILERPLATE:
1269
+ return 0.2;
1270
+ // Redundant indirection is low cohesion (architectural theater)
1252
1271
  case Classification.BARREL:
1253
1272
  return 1;
1254
1273
  case Classification.TYPE_DEFINITION:
@@ -1324,6 +1343,9 @@ function hasRelatedExportNames(exportNames) {
1324
1343
  }
1325
1344
  function adjustFragmentationForClassification(baseFragmentation, classification) {
1326
1345
  switch (classification) {
1346
+ case Classification.BOILERPLATE:
1347
+ return baseFragmentation * 1.5;
1348
+ // Redundant barrels increase fragmentation
1327
1349
  case Classification.BARREL:
1328
1350
  return 0;
1329
1351
  case Classification.TYPE_DEFINITION:
@@ -1432,6 +1454,12 @@ function detectModuleClusters(graph, options) {
1432
1454
  // src/remediation.ts
1433
1455
  function getClassificationRecommendations(classification, file, issues) {
1434
1456
  switch (classification) {
1457
+ case "boilerplate-barrel":
1458
+ return [
1459
+ "Redundant indirection detected (architectural theater)",
1460
+ "Remove this pass-through barrel export to reduce cognitive load",
1461
+ "Consider combining into meaningful domain exports if necessary"
1462
+ ];
1435
1463
  case "barrel-export":
1436
1464
  return [
1437
1465
  "Barrel export file detected - multiple domains are expected here",
package/dist/index.mjs CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  inferDomain,
33
33
  inferDomainFromSemantics,
34
34
  runInteractiveSetup
35
- } from "./chunk-CAX2MOUZ.mjs";
35
+ } from "./chunk-BEZPBI5C.mjs";
36
36
  import "./chunk-64U3PNO3.mjs";
37
37
 
38
38
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/context-analyzer",
3
- "version": "0.21.13",
3
+ "version": "0.21.15",
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",
@@ -49,7 +49,7 @@
49
49
  "commander": "^14.0.0",
50
50
  "chalk": "^5.3.0",
51
51
  "prompts": "^2.4.2",
52
- "@aiready/core": "0.23.10"
52
+ "@aiready/core": "0.23.12"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^24.0.0",
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ classifyFile,
4
+ adjustFragmentationForClassification,
5
+ adjustCohesionForClassification,
6
+ getClassificationRecommendations,
7
+ } from '../index';
8
+ import type { DependencyNode } from '../types';
9
+
10
+ describe('boilerplate-barrel classification', () => {
11
+ const createNode = (overrides: Partial<DependencyNode>): DependencyNode => ({
12
+ file: 'test.ts',
13
+ imports: [],
14
+ exports: [],
15
+ tokenCost: 100,
16
+ linesOfCode: 50,
17
+ ...overrides,
18
+ });
19
+
20
+ it('should classify single source pass-through as boilerplate-barrel', () => {
21
+ const node = createNode({
22
+ file: 'core/lib/types/domains/agent-types.ts',
23
+ tokenCost: 50,
24
+ exports: [{ name: '*', type: 'all', source: '../agent' } as any],
25
+ });
26
+
27
+ const classification = classifyFile(node, 1.0, ['agent']);
28
+ expect(classification).toBe('boilerplate-barrel');
29
+ });
30
+
31
+ it('should classify meaningless aggregation (2 sources) as boilerplate-barrel', () => {
32
+ const node = createNode({
33
+ file: 'src/types/mini-barrel.ts',
34
+ tokenCost: 80,
35
+ exports: [
36
+ { name: 'User', type: 'type', source: './user' } as any,
37
+ { name: 'Profile', type: 'type', source: './profile' } as any,
38
+ ],
39
+ });
40
+
41
+ const classification = classifyFile(node, 1.0, ['user', 'profile']);
42
+ expect(classification).toBe('boilerplate-barrel');
43
+ });
44
+
45
+ it('should classify meaningful aggregation (5+ sources) as regular barrel-export', () => {
46
+ const node = createNode({
47
+ file: 'src/index.ts',
48
+ tokenCost: 150,
49
+ exports: [
50
+ { name: 'A', type: 'const', source: './a' } as any,
51
+ { name: 'B', type: 'const', source: './b' } as any,
52
+ { name: 'C', type: 'const', source: './c' } as any,
53
+ { name: 'D', type: 'const', source: './d' } as any,
54
+ { name: 'E', type: 'const', source: './e' } as any,
55
+ ],
56
+ });
57
+
58
+ const classification = classifyFile(node, 1.0, ['a', 'b', 'c', 'd', 'e']);
59
+ expect(classification).toBe('barrel-export');
60
+ });
61
+
62
+ it('should NOT classify barrel with local logic as boilerplate', () => {
63
+ const node = createNode({
64
+ file: 'src/mixed-barrel.ts',
65
+ tokenCost: 600, // Above limit
66
+ exports: [
67
+ { name: 'A', type: 'const', source: './a' } as any,
68
+ { name: 'localFunc', type: 'function' } as any,
69
+ ],
70
+ });
71
+
72
+ // Should default to something else, or if it matches barrel patterns but has local logic
73
+ const classification = classifyFile(node, 0.5, ['a', 'local']);
74
+ expect(classification).not.toBe('boilerplate-barrel');
75
+ });
76
+
77
+ it('should penalize boilerplate-barrel in cohesion adjustment', () => {
78
+ const result = adjustCohesionForClassification(1.0, 'boilerplate-barrel');
79
+ expect(result).toBe(0.2); // Low score for architectural theater
80
+ });
81
+
82
+ it('should increase fragmentation for boilerplate-barrel', () => {
83
+ const result = adjustFragmentationForClassification(
84
+ 0.4,
85
+ 'boilerplate-barrel'
86
+ );
87
+ expect(result).toBeCloseTo(0.6, 2); // 0.4 * 1.5
88
+ });
89
+
90
+ it('should provide specific recommendations for boilerplate-barrel', () => {
91
+ const recommendations = getClassificationRecommendations(
92
+ 'boilerplate-barrel',
93
+ 'types/domains/agent-types.ts',
94
+ []
95
+ );
96
+ expect(recommendations).toContain(
97
+ 'Redundant indirection detected (architectural theater)'
98
+ );
99
+ expect(recommendations).toContain(
100
+ 'Remove this pass-through barrel export to reduce cognitive load'
101
+ );
102
+ });
103
+ });
package/src/classifier.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { DependencyNode, FileClassification } from './types';
2
2
  import {
3
3
  isBarrelExport,
4
+ isBoilerplateBarrel,
4
5
  isTypeDefinition,
5
6
  isNextJsPage,
6
7
  isLambdaHandler,
@@ -18,6 +19,7 @@ import {
18
19
  */
19
20
  export const Classification = {
20
21
  BARREL: 'barrel-export' as const,
22
+ BOILERPLATE: 'boilerplate-barrel' as const,
21
23
  TYPE_DEFINITION: 'type-definition' as const,
22
24
  NEXTJS_PAGE: 'nextjs-page' as const,
23
25
  LAMBDA_HANDLER: 'lambda-handler' as const,
@@ -44,7 +46,12 @@ export function classifyFile(
44
46
  cohesionScore: number = 1,
45
47
  domains: string[] = []
46
48
  ): FileClassification {
47
- // 1. Detect barrel exports (primarily re-exports)
49
+ // 1. Detect boilerplate barrels (pure indirection/architectural theater)
50
+ if (isBoilerplateBarrel(node)) {
51
+ return Classification.BOILERPLATE;
52
+ }
53
+
54
+ // 2. Detect legitimate barrel exports (primarily re-exports that aggregate)
48
55
  if (isBarrelExport(node)) {
49
56
  return Classification.BARREL;
50
57
  }
@@ -134,6 +141,8 @@ export function adjustCohesionForClassification(
134
141
  node?: DependencyNode
135
142
  ): number {
136
143
  switch (classification) {
144
+ case Classification.BOILERPLATE:
145
+ return 0.2; // Redundant indirection is low cohesion (architectural theater)
137
146
  case Classification.BARREL:
138
147
  return 1;
139
148
  case Classification.TYPE_DEFINITION:
@@ -235,6 +244,8 @@ export function adjustFragmentationForClassification(
235
244
  classification: FileClassification
236
245
  ): number {
237
246
  switch (classification) {
247
+ case Classification.BOILERPLATE:
248
+ return baseFragmentation * 1.5; // Redundant barrels increase fragmentation
238
249
  case Classification.BARREL:
239
250
  return 0;
240
251
  case Classification.TYPE_DEFINITION:
package/src/heuristics.ts CHANGED
@@ -64,14 +64,48 @@ const CONFIG_NAME_PATTERNS = [
64
64
  'sst.config',
65
65
  ];
66
66
 
67
+ /**
68
+ * Detect if a file is a boilerplate barrel (architectural theater).
69
+ * A boilerplate barrel re-exports from other sources without adding value.
70
+ *
71
+ * @param node - The dependency node to analyze.
72
+ * @returns True if the file matches boilerplate barrel patterns.
73
+ */
74
+ export function isBoilerplateBarrel(node: DependencyNode): boolean {
75
+ const { exports, tokenCost } = node;
76
+ if (!exports || exports.length === 0) return false;
77
+
78
+ // 1. Must be purely re-exports
79
+ const isPurelyReexports = exports.every((exp: any) => !!exp.source);
80
+ if (!isPurelyReexports) return false;
81
+
82
+ // 2. Must be low local token cost (no actual logic)
83
+ if (tokenCost > 500) return false;
84
+
85
+ // 3. Detect "Architectural Theater"
86
+ // If it re-exports everything from exactly ONE source, it's a pass-through
87
+ const sources = new Set(exports.map((exp: any) => exp.source));
88
+
89
+ // Pattern: export * from '../actual'
90
+ const isSingleSourcePassThrough = sources.size === 1;
91
+
92
+ // Pattern: multi-file 1-to-1 re-exports that don't aggregate much
93
+ // (e.g., 6 files each re-exporting from exactly one other file)
94
+ const isMeaninglessAggregation = sources.size > 0 && sources.size < 3;
95
+
96
+ return isSingleSourcePassThrough || isMeaninglessAggregation;
97
+ }
98
+
67
99
  /**
68
100
  * Detect if a file is a barrel export (index.ts).
69
101
  *
70
102
  * @param node - The dependency node to analyze.
71
103
  * @returns True if the file matches barrel export patterns.
72
- * @lastUpdated 2026-03-18
104
+ * @lastUpdated 2026-03-20
73
105
  */
74
106
  export function isBarrelExport(node: DependencyNode): boolean {
107
+ if (isBoilerplateBarrel(node)) return false; // Already handled by more specific check
108
+
75
109
  const { file, exports } = node;
76
110
  const fileName = file.split('/').pop()?.toLowerCase();
77
111
 
@@ -14,6 +14,12 @@ export function getClassificationRecommendations(
14
14
  issues: string[]
15
15
  ): string[] {
16
16
  switch (classification) {
17
+ case 'boilerplate-barrel':
18
+ return [
19
+ 'Redundant indirection detected (architectural theater)',
20
+ 'Remove this pass-through barrel export to reduce cognitive load',
21
+ 'Consider combining into meaningful domain exports if necessary',
22
+ ];
17
23
  case 'barrel-export':
18
24
  return [
19
25
  'Barrel export file detected - multiple domains are expected here',
package/src/types.ts CHANGED
@@ -80,6 +80,7 @@ export interface ContextAnalysisResult {
80
80
  */
81
81
  export type FileClassification =
82
82
  | 'barrel-export' // Re-exports from other modules (index.ts files)
83
+ | 'boilerplate-barrel' // Redundant barrel file with no added value (architectural theater)
83
84
  | 'type-definition' // Primarily type/interface definitions
84
85
  | 'cohesive-module' // Single domain, high cohesion (acceptable large files)
85
86
  | 'utility-module' // Utility/helper files with cohesive purpose despite multi-domain