@aiready/change-amplification 0.14.14 → 0.14.16

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.
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > @aiready/change-amplification@0.14.13 build /Users/pengcao/projects/aiready/packages/change-amplification
3
+ > @aiready/change-amplification@0.14.15 build /Users/pengcao/projects/aiready/packages/change-amplification
4
4
  > tsup src/index.ts src/cli.ts --format cjs,esm --dts
5
5
 
6
6
  CLI Building entry: src/cli.ts, src/index.ts
@@ -9,15 +9,15 @@
9
9
  CLI Target: es2020
10
10
  CJS Build start
11
11
  ESM Build start
12
- ESM dist/chunk-SPXGOPNW.mjs 4.83 KB
13
- ESM dist/index.mjs 916.00 B
12
+ CJS dist/index.js 7.53 KB
13
+ CJS dist/cli.js 8.99 KB
14
+ CJS ⚡️ Build success in 43ms
15
+ ESM dist/chunk-KUIEB4UN.mjs 5.32 KB
14
16
  ESM dist/cli.mjs 2.41 KB
15
- ESM ⚡️ Build success in 105ms
16
- CJS dist/cli.js 8.51 KB
17
- CJS dist/index.js 7.04 KB
18
- CJS ⚡️ Build success in 107ms
17
+ ESM dist/index.mjs 916.00 B
18
+ ESM ⚡️ Build success in 48ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 5067ms
20
+ DTS ⚡️ Build success in 3155ms
21
21
  DTS dist/cli.d.ts 266.00 B
22
22
  DTS dist/index.d.ts 937.00 B
23
23
  DTS dist/cli.d.mts 266.00 B
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > @aiready/change-amplification@0.14.13 format-check /Users/pengcao/projects/aiready/packages/change-amplification
3
+ > @aiready/change-amplification@0.14.15 format-check /Users/pengcao/projects/aiready/packages/change-amplification
4
4
  > prettier --check . --ignore-path ../../.prettierignore
5
5
 
6
6
  Checking formatting...
@@ -1,5 +1,5 @@
1
1
 
2
2
  
3
- > @aiready/change-amplification@0.14.13 lint /Users/pengcao/projects/aiready/packages/change-amplification
3
+ > @aiready/change-amplification@0.14.15 lint /Users/pengcao/projects/aiready/packages/change-amplification
4
4
  > eslint src --ext .ts
5
5
 
@@ -1,24 +1,62 @@
1
1
 
2
2
  
3
- > @aiready/change-amplification@0.14.13 test /Users/pengcao/projects/aiready/packages/change-amplification
3
+ > @aiready/change-amplification@0.14.15 test /Users/pengcao/projects/aiready/packages/change-amplification
4
4
  > vitest run
5
5
 
6
6
  [?25l
7
7
   RUN  v4.0.18 /Users/pengcao/projects/aiready/packages/change-amplification
8
8
 
9
- ✓ src/__tests__/dummy.test.ts (1 test) 2ms
10
- ✓ src/__tests__/cli.test.ts (2 tests) 9ms
9
+ [?2026h
10
+  src/__tests__/scoring.test.ts [queued]
11
+
12
+  Test Files 0 passed (5)
13
+  Tests 0 passed (0)
14
+  Start at 23:35:29
15
+  Duration 370ms
16
+ [?2026l[?2026h
17
+  ❯ src/__tests__/analyzer.test.ts [queued]
18
+  ❯ src/__tests__/cli.test.ts [queued]
19
+  ❯ src/__tests__/dummy.test.ts 0/1
20
+  ❯ src/__tests__/provider.test.ts [queued]
21
+  ❯ src/__tests__/scoring.test.ts [queued]
22
+
23
+  Test Files 0 passed (5)
24
+  Tests 0 passed (1)
25
+  Start at 23:35:29
26
+  Duration 474ms
27
+ [?2026l[?2026h ✓ src/__tests__/dummy.test.ts (1 test) 1ms
28
+
29
+  ❯ src/__tests__/analyzer.test.ts [queued]
30
+  ❯ src/__tests__/cli.test.ts [queued]
31
+  ❯ src/__tests__/provider.test.ts [queued]
32
+  ❯ src/__tests__/scoring.test.ts [queued]
33
+
34
+  Test Files 1 passed (5)
35
+  Tests 1 passed (1)
36
+  Start at 23:35:29
37
+  Duration 908ms
38
+ [?2026l[?2026h
39
+  ❯ src/__tests__/analyzer.test.ts [queued]
40
+  ❯ src/__tests__/cli.test.ts 0/2
41
+  ❯ src/__tests__/provider.test.ts [queued]
42
+  ❯ src/__tests__/scoring.test.ts [queued]
43
+
44
+  Test Files 1 passed (5)
45
+  Tests 1 passed (3)
46
+  Start at 23:35:29
47
+  Duration 1.54s
48
+ [?2026l ✓ src/__tests__/cli.test.ts (2 tests) 4ms
11
49
  stdout | src/__tests__/analyzer.test.ts > analyzeChangeAmplification reproduction > should see how it gets to 0
12
50
  Resulting score for highly coupled: 27
13
51
  Rating: explosive
14
52
 
15
- ✓ src/__tests__/analyzer.test.ts (2 tests) 7ms
16
- ✓ src/__tests__/provider.test.ts (2 tests) 3ms
53
+ ✓ src/__tests__/analyzer.test.ts (2 tests) 5ms
54
+ ✓ src/__tests__/provider.test.ts (2 tests) 4ms
17
55
  ✓ src/__tests__/scoring.test.ts (2 tests) 2ms
18
56
 
19
57
   Test Files  5 passed (5)
20
58
   Tests  9 passed (9)
21
-  Start at  20:52:15
22
-  Duration  6.14s (transform 4.12s, setup 0ms, import 19.73s, tests 22ms, environment 0ms)
59
+  Start at  23:35:29
60
+  Duration  1.65s (transform 1.47s, setup 0ms, import 4.83s, tests 16ms, environment 0ms)
23
61
 
24
62
  [?25h
@@ -1,5 +1,5 @@
1
1
 
2
2
  
3
- > @aiready/change-amplification@0.14.13 type-check /Users/pengcao/projects/aiready/packages/change-amplification
3
+ > @aiready/change-amplification@0.14.15 type-check /Users/pengcao/projects/aiready/packages/change-amplification
4
4
  > tsc --noEmit
5
5
 
@@ -0,0 +1,148 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/analyzer.ts
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ import {
12
+ scanFiles,
13
+ calculateChangeAmplification,
14
+ getParser,
15
+ Severity,
16
+ IssueType
17
+ } from "@aiready/core";
18
+ async function analyzeChangeAmplification(options) {
19
+ const files = await scanFiles({
20
+ ...options,
21
+ include: options.include || ["**/*.{ts,tsx,js,jsx,py,go}"]
22
+ });
23
+ const dependencyGraph = /* @__PURE__ */ new Map();
24
+ const reverseGraph = /* @__PURE__ */ new Map();
25
+ for (const file of files) {
26
+ dependencyGraph.set(file, []);
27
+ reverseGraph.set(file, []);
28
+ }
29
+ let processed = 0;
30
+ for (const file of files) {
31
+ processed++;
32
+ if (processed % 50 === 0 || processed === files.length) {
33
+ options.onProgress?.(
34
+ processed,
35
+ files.length,
36
+ `analyzing dependencies (${processed}/${files.length})`
37
+ );
38
+ }
39
+ try {
40
+ const parser = await getParser(file);
41
+ if (!parser) continue;
42
+ const content = fs.readFileSync(file, "utf8");
43
+ const parseResult = parser.parse(content, file);
44
+ const dependencies = parseResult.imports.map((i) => i.source);
45
+ const extensions = [".ts", ".tsx", ".js", ".jsx"];
46
+ for (const dep of dependencies) {
47
+ const depDir = path.dirname(file);
48
+ let resolvedPath;
49
+ if (dep.startsWith(".")) {
50
+ const absoluteDepBase = path.resolve(depDir, dep);
51
+ for (const ext of extensions) {
52
+ const withExt = absoluteDepBase + ext;
53
+ if (files.includes(withExt)) {
54
+ resolvedPath = withExt;
55
+ break;
56
+ }
57
+ }
58
+ if (!resolvedPath) {
59
+ for (const ext of extensions) {
60
+ const withIndex = path.join(absoluteDepBase, `index${ext}`);
61
+ if (files.includes(withIndex)) {
62
+ resolvedPath = withIndex;
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ } else {
68
+ const depWithoutExt = dep.replace(/\.(ts|tsx|js|jsx)$/, "");
69
+ resolvedPath = files.find((f) => {
70
+ const fWithoutExt = f.replace(/\.(ts|tsx|js|jsx)$/, "");
71
+ return fWithoutExt === depWithoutExt || fWithoutExt.endsWith(`/${depWithoutExt}`);
72
+ });
73
+ }
74
+ if (resolvedPath && resolvedPath !== file) {
75
+ dependencyGraph.get(file)?.push(resolvedPath);
76
+ reverseGraph.get(resolvedPath)?.push(file);
77
+ }
78
+ }
79
+ } catch (err) {
80
+ void err;
81
+ }
82
+ }
83
+ const fileMetrics = files.map((file) => {
84
+ const fanOut = dependencyGraph.get(file)?.length || 0;
85
+ const fanIn = reverseGraph.get(file)?.length || 0;
86
+ return { file, fanOut, fanIn };
87
+ });
88
+ const riskResult = calculateChangeAmplification({ files: fileMetrics });
89
+ let finalScore = riskResult.score;
90
+ if (finalScore === 0 && files.length > 0 && riskResult.rating !== "explosive") {
91
+ finalScore = 10;
92
+ }
93
+ const results = [];
94
+ const getLevel = (s) => {
95
+ if (s === Severity.Critical || s === "critical") return 4;
96
+ if (s === Severity.Major || s === "major") return 3;
97
+ if (s === Severity.Minor || s === "minor") return 2;
98
+ if (s === Severity.Info || s === "info") return 1;
99
+ return 0;
100
+ };
101
+ for (const hotspot of riskResult.hotspots) {
102
+ const issues = [];
103
+ const fileName = path.basename(hotspot.file);
104
+ const isBarrelFile = fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx" || fileName.startsWith("all-") || fileName.endsWith(".meta.ts") || fileName.endsWith(".meta.js");
105
+ if (hotspot.amplificationFactor > 20 && !isBarrelFile) {
106
+ issues.push({
107
+ type: IssueType.ChangeAmplification,
108
+ severity: hotspot.amplificationFactor > 40 ? Severity.Critical : Severity.Major,
109
+ message: `High change amplification detected (Factor: ${hotspot.amplificationFactor}). Changes here cascade heavily.`,
110
+ location: { file: hotspot.file, line: 1 },
111
+ suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
112
+ });
113
+ }
114
+ if (hotspot.amplificationFactor > 5 && !isBarrelFile) {
115
+ results.push({
116
+ fileName: hotspot.file,
117
+ issues,
118
+ metrics: {
119
+ aiSignalClarityScore: 100 - hotspot.amplificationFactor
120
+ // Just a rough score
121
+ }
122
+ });
123
+ }
124
+ }
125
+ return {
126
+ summary: {
127
+ totalFiles: files.length,
128
+ totalIssues: results.reduce((sum, r) => sum + r.issues.length, 0),
129
+ criticalIssues: results.reduce(
130
+ (sum, r) => sum + r.issues.filter((i) => getLevel(i.severity) === 4).length,
131
+ 0
132
+ ),
133
+ majorIssues: results.reduce(
134
+ (sum, r) => sum + r.issues.filter((i) => getLevel(i.severity) === 3).length,
135
+ 0
136
+ ),
137
+ score: finalScore,
138
+ rating: riskResult.rating,
139
+ recommendations: riskResult.recommendations
140
+ },
141
+ results
142
+ };
143
+ }
144
+
145
+ export {
146
+ __require,
147
+ analyzeChangeAmplification
148
+ };
@@ -0,0 +1,148 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/analyzer.ts
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ import {
12
+ scanFiles,
13
+ calculateChangeAmplification,
14
+ getParser,
15
+ Severity,
16
+ IssueType
17
+ } from "@aiready/core";
18
+ async function analyzeChangeAmplification(options) {
19
+ const files = await scanFiles({
20
+ ...options,
21
+ include: options.include || ["**/*.{ts,tsx,js,jsx,py,go}"]
22
+ });
23
+ const dependencyGraph = /* @__PURE__ */ new Map();
24
+ const reverseGraph = /* @__PURE__ */ new Map();
25
+ for (const file of files) {
26
+ dependencyGraph.set(file, []);
27
+ reverseGraph.set(file, []);
28
+ }
29
+ let processed = 0;
30
+ for (const file of files) {
31
+ processed++;
32
+ if (processed % 50 === 0 || processed === files.length) {
33
+ options.onProgress?.(
34
+ processed,
35
+ files.length,
36
+ `analyzing dependencies (${processed}/${files.length})`
37
+ );
38
+ }
39
+ try {
40
+ const parser = await getParser(file);
41
+ if (!parser) continue;
42
+ const content = fs.readFileSync(file, "utf8");
43
+ const parseResult = parser.parse(content, file);
44
+ const dependencies = parseResult.imports.map((i) => i.source);
45
+ const extensions = [".ts", ".tsx", ".js", ".jsx"];
46
+ for (const dep of dependencies) {
47
+ const depDir = path.dirname(file);
48
+ let resolvedPath;
49
+ if (dep.startsWith(".")) {
50
+ const absoluteDepBase = path.resolve(depDir, dep);
51
+ for (const ext of extensions) {
52
+ const withExt = absoluteDepBase + ext;
53
+ if (files.includes(withExt)) {
54
+ resolvedPath = withExt;
55
+ break;
56
+ }
57
+ }
58
+ if (!resolvedPath) {
59
+ for (const ext of extensions) {
60
+ const withIndex = path.join(absoluteDepBase, `index${ext}`);
61
+ if (files.includes(withIndex)) {
62
+ resolvedPath = withIndex;
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ } else {
68
+ const depWithoutExt = dep.replace(/\.(ts|tsx|js|jsx)$/, "");
69
+ resolvedPath = files.find((f) => {
70
+ const fWithoutExt = f.replace(/\.(ts|tsx|js|jsx)$/, "");
71
+ return fWithoutExt === depWithoutExt || fWithoutExt.endsWith(`/${depWithoutExt}`);
72
+ });
73
+ }
74
+ if (resolvedPath && resolvedPath !== file) {
75
+ dependencyGraph.get(file)?.push(resolvedPath);
76
+ reverseGraph.get(resolvedPath)?.push(file);
77
+ }
78
+ }
79
+ } catch (err) {
80
+ void err;
81
+ }
82
+ }
83
+ const fileMetrics = files.map((file) => {
84
+ const fanOut = dependencyGraph.get(file)?.length || 0;
85
+ const fanIn = reverseGraph.get(file)?.length || 0;
86
+ return { file, fanOut, fanIn };
87
+ });
88
+ const riskResult = calculateChangeAmplification({ files: fileMetrics });
89
+ let finalScore = riskResult.score;
90
+ if (finalScore === 0 && files.length > 0 && riskResult.rating !== "explosive") {
91
+ finalScore = 10;
92
+ }
93
+ const results = [];
94
+ const getLevel = (s) => {
95
+ if (s === Severity.Critical || s === "critical") return 4;
96
+ if (s === Severity.Major || s === "major") return 3;
97
+ if (s === Severity.Minor || s === "minor") return 2;
98
+ if (s === Severity.Info || s === "info") return 1;
99
+ return 0;
100
+ };
101
+ for (const hotspot of riskResult.hotspots) {
102
+ const issues = [];
103
+ const fileName = path.basename(hotspot.file).toLowerCase();
104
+ const isSharedUtility = fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx" || fileName.includes("logger") || fileName.includes("log.ts") || fileName.includes("constants") || fileName.includes("types.ts") || fileName.includes("enums.ts") || fileName.startsWith("all-") || fileName.endsWith(".meta.ts") || fileName.endsWith(".meta.js");
105
+ if (hotspot.amplificationFactor > 20 && !isSharedUtility) {
106
+ issues.push({
107
+ type: IssueType.ChangeAmplification,
108
+ severity: hotspot.amplificationFactor > 40 ? Severity.Critical : Severity.Major,
109
+ message: `High change amplification detected (Factor: ${hotspot.amplificationFactor}). Changes here cascade heavily.`,
110
+ location: { file: hotspot.file, line: 1 },
111
+ suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
112
+ });
113
+ }
114
+ if (hotspot.amplificationFactor > 5 && !isSharedUtility) {
115
+ results.push({
116
+ fileName: hotspot.file,
117
+ issues,
118
+ metrics: {
119
+ aiSignalClarityScore: 100 - hotspot.amplificationFactor
120
+ // Just a rough score
121
+ }
122
+ });
123
+ }
124
+ }
125
+ return {
126
+ summary: {
127
+ totalFiles: files.length,
128
+ totalIssues: results.reduce((sum, r) => sum + r.issues.length, 0),
129
+ criticalIssues: results.reduce(
130
+ (sum, r) => sum + r.issues.filter((i) => getLevel(i.severity) === 4).length,
131
+ 0
132
+ ),
133
+ majorIssues: results.reduce(
134
+ (sum, r) => sum + r.issues.filter((i) => getLevel(i.severity) === 3).length,
135
+ 0
136
+ ),
137
+ score: finalScore,
138
+ rating: riskResult.rating,
139
+ recommendations: riskResult.recommendations
140
+ },
141
+ results
142
+ };
143
+ }
144
+
145
+ export {
146
+ __require,
147
+ analyzeChangeAmplification
148
+ };
package/dist/cli.js CHANGED
@@ -128,7 +128,9 @@ async function analyzeChangeAmplification(options) {
128
128
  };
129
129
  for (const hotspot of riskResult.hotspots) {
130
130
  const issues = [];
131
- if (hotspot.amplificationFactor > 20) {
131
+ const fileName = path.basename(hotspot.file).toLowerCase();
132
+ const isSharedUtility = fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx" || fileName.includes("logger") || fileName.includes("log.ts") || fileName.includes("constants") || fileName.includes("types.ts") || fileName.includes("enums.ts") || fileName.startsWith("all-") || fileName.endsWith(".meta.ts") || fileName.endsWith(".meta.js");
133
+ if (hotspot.amplificationFactor > 20 && !isSharedUtility) {
132
134
  issues.push({
133
135
  type: import_core.IssueType.ChangeAmplification,
134
136
  severity: hotspot.amplificationFactor > 40 ? import_core.Severity.Critical : import_core.Severity.Major,
@@ -137,7 +139,7 @@ async function analyzeChangeAmplification(options) {
137
139
  suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
138
140
  });
139
141
  }
140
- if (hotspot.amplificationFactor > 5) {
142
+ if (hotspot.amplificationFactor > 5 && !isSharedUtility) {
141
143
  results.push({
142
144
  fileName: hotspot.file,
143
145
  issues,
package/dist/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  __require,
4
4
  analyzeChangeAmplification
5
- } from "./chunk-SPXGOPNW.mjs";
5
+ } from "./chunk-KUIEB4UN.mjs";
6
6
 
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
package/dist/index.js CHANGED
@@ -128,7 +128,9 @@ async function analyzeChangeAmplification(options) {
128
128
  };
129
129
  for (const hotspot of riskResult.hotspots) {
130
130
  const issues = [];
131
- if (hotspot.amplificationFactor > 20) {
131
+ const fileName = path.basename(hotspot.file).toLowerCase();
132
+ const isSharedUtility = fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx" || fileName.includes("logger") || fileName.includes("log.ts") || fileName.includes("constants") || fileName.includes("types.ts") || fileName.includes("enums.ts") || fileName.startsWith("all-") || fileName.endsWith(".meta.ts") || fileName.endsWith(".meta.js");
133
+ if (hotspot.amplificationFactor > 20 && !isSharedUtility) {
132
134
  issues.push({
133
135
  type: import_core.IssueType.ChangeAmplification,
134
136
  severity: hotspot.amplificationFactor > 40 ? import_core.Severity.Critical : import_core.Severity.Major,
@@ -137,7 +139,7 @@ async function analyzeChangeAmplification(options) {
137
139
  suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
138
140
  });
139
141
  }
140
- if (hotspot.amplificationFactor > 5) {
142
+ if (hotspot.amplificationFactor > 5 && !isSharedUtility) {
141
143
  results.push({
142
144
  fileName: hotspot.file,
143
145
  issues,
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  analyzeChangeAmplification
3
- } from "./chunk-SPXGOPNW.mjs";
3
+ } from "./chunk-KUIEB4UN.mjs";
4
4
 
5
5
  // src/index.ts
6
6
  import { ToolRegistry } from "@aiready/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/change-amplification",
3
- "version": "0.14.14",
3
+ "version": "0.14.16",
4
4
  "description": "AI-Readiness: Change Amplification Detection",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -10,7 +10,7 @@
10
10
  "commander": "^14.0.0",
11
11
  "glob": "^13.0.0",
12
12
  "chalk": "^5.3.0",
13
- "@aiready/core": "0.24.15"
13
+ "@aiready/core": "0.24.19"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^24.0.0",
@@ -42,6 +42,8 @@
42
42
  "test:watch": "vitest",
43
43
  "lint": "eslint src --ext .ts",
44
44
  "type-check": "tsc --noEmit",
45
- "format-check": "prettier --check . --ignore-path ../../.prettierignore"
45
+ "format-check": "prettier --check . --ignore-path ../../.prettierignore",
46
+ "format": "prettier --write . --ignore-path ../../.prettierignore",
47
+ "lint:fix": "eslint . --fix"
46
48
  }
47
49
  }
package/src/analyzer.ts CHANGED
@@ -135,7 +135,25 @@ export async function analyzeChangeAmplification(
135
135
 
136
136
  for (const hotspot of riskResult.hotspots) {
137
137
  const issues: ChangeAmplificationIssue[] = [];
138
- if (hotspot.amplificationFactor > 20) {
138
+
139
+ // Check if this is a barrel/index file or a common utility pattern (intentional re-export or shared use)
140
+ const fileName = path.basename(hotspot.file).toLowerCase();
141
+ const isSharedUtility =
142
+ fileName === 'index.ts' ||
143
+ fileName === 'index.js' ||
144
+ fileName === 'index.tsx' ||
145
+ fileName === 'index.jsx' ||
146
+ fileName.includes('logger') ||
147
+ fileName.includes('log.ts') ||
148
+ fileName.includes('constants') ||
149
+ fileName.includes('types.ts') ||
150
+ fileName.includes('enums.ts') ||
151
+ fileName.startsWith('all-') ||
152
+ fileName.endsWith('.meta.ts') ||
153
+ fileName.endsWith('.meta.js');
154
+
155
+ // Only flag high amplification if it's NOT a barrel file or shared utility
156
+ if (hotspot.amplificationFactor > 20 && !isSharedUtility) {
139
157
  issues.push({
140
158
  type: IssueType.ChangeAmplification,
141
159
  severity:
@@ -147,7 +165,8 @@ export async function analyzeChangeAmplification(
147
165
  }
148
166
 
149
167
  // We only push results for files that have either high fan-in or fan-out
150
- if (hotspot.amplificationFactor > 5) {
168
+ // Also exclude barrel files and shared utilities from the results
169
+ if (hotspot.amplificationFactor > 5 && !isSharedUtility) {
151
170
  results.push({
152
171
  fileName: hotspot.file,
153
172
  issues,