@aiready/change-amplification 0.13.19 → 0.13.20
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/.turbo/turbo-build.log +11 -11
- package/.turbo/turbo-test.log +7 -7
- package/dist/chunk-SPXGOPNW.mjs +146 -0
- package/dist/cli.js +24 -30
- package/dist/cli.mjs +24 -30
- package/dist/index.d.mts +2 -6
- package/dist/index.d.ts +2 -6
- package/dist/index.js +14 -20
- package/dist/index.mjs +15 -21
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +10 -5
- package/src/__tests__/scoring.test.ts +1 -1
- package/src/analyzer.ts +1 -1
- package/src/cli.ts +23 -30
- package/src/scoring.ts +14 -31
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/change-amplification@0.13.
|
|
3
|
+
> @aiready/change-amplification@0.13.20 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
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2020
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
11
|
[34mESM[39m Build start
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[32mESM[39m
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
12
|
+
[32mCJS[39m [1mdist/cli.js [22m[32m8.51 KB[39m
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m7.74 KB[39m
|
|
14
|
+
[32mCJS[39m ⚡️ Build success in 331ms
|
|
15
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m1.53 KB[39m
|
|
16
|
+
[32mESM[39m [1mdist/cli.mjs [22m[32m2.41 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/chunk-SPXGOPNW.mjs [22m[32m4.83 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 418ms
|
|
19
19
|
DTS Build start
|
|
20
|
-
DTS ⚡️ Build success in
|
|
20
|
+
DTS ⚡️ Build success in 12130ms
|
|
21
21
|
DTS dist/cli.d.ts 152.00 B
|
|
22
|
-
DTS dist/index.d.ts 1.
|
|
22
|
+
DTS dist/index.d.ts 1.12 KB
|
|
23
23
|
DTS dist/cli.d.mts 152.00 B
|
|
24
|
-
DTS dist/index.d.mts 1.
|
|
24
|
+
DTS dist/index.d.mts 1.12 KB
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/change-amplification@0.13.
|
|
3
|
+
> @aiready/change-amplification@0.13.19 test /Users/pengcao/projects/aiready/packages/change-amplification
|
|
4
4
|
> vitest run
|
|
5
5
|
|
|
6
6
|
[?25l
|
|
7
7
|
[1m[46m RUN [49m[22m [36mv4.0.18 [39m[90m/Users/pengcao/projects/aiready/packages/change-amplification[39m
|
|
8
8
|
|
|
9
|
-
[32m✓[39m src/__tests__/dummy.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m
|
|
9
|
+
[32m✓[39m src/__tests__/dummy.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 9[2mms[22m[39m
|
|
10
|
+
[32m✓[39m src/__tests__/provider.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 23[2mms[22m[39m
|
|
10
11
|
[32m✓[39m src/__tests__/cli.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
12
|
+
[32m✓[39m src/__tests__/scoring.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 241[2mms[22m[39m
|
|
11
13
|
[90mstdout[2m | src/__tests__/analyzer.test.ts[2m > [22m[2manalyzeChangeAmplification reproduction[2m > [22m[2mshould see how it gets to 0
|
|
12
14
|
[22m[39mResulting score for highly coupled: [33m27[39m
|
|
13
15
|
Rating: explosive
|
|
14
16
|
|
|
15
|
-
[32m✓[39m src/__tests__/analyzer.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m
|
|
16
|
-
[32m✓[39m src/__tests__/provider.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 18[2mms[22m[39m
|
|
17
|
-
[32m✓[39m src/__tests__/scoring.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
17
|
+
[32m✓[39m src/__tests__/analyzer.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 184[2mms[22m[39m
|
|
18
18
|
|
|
19
19
|
[2m Test Files [22m [1m[32m5 passed[39m[22m[90m (5)[39m
|
|
20
20
|
[2m Tests [22m [1m[32m9 passed[39m[22m[90m (9)[39m
|
|
21
|
-
[2m Start at [22m
|
|
22
|
-
[2m Duration [22m
|
|
21
|
+
[2m Start at [22m 23:31:08
|
|
22
|
+
[2m Duration [22m 11.56s[2m (transform 5.08s, setup 0ms, import 39.45s, tests 459ms, environment 15ms)[22m
|
|
23
23
|
|
|
24
24
|
[?25h
|
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
if (hotspot.amplificationFactor > 20) {
|
|
104
|
+
issues.push({
|
|
105
|
+
type: IssueType.ChangeAmplification,
|
|
106
|
+
severity: hotspot.amplificationFactor > 40 ? Severity.Critical : Severity.Major,
|
|
107
|
+
message: `High change amplification detected (Factor: ${hotspot.amplificationFactor}). Changes here cascade heavily.`,
|
|
108
|
+
location: { file: hotspot.file, line: 1 },
|
|
109
|
+
suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (hotspot.amplificationFactor > 5) {
|
|
113
|
+
results.push({
|
|
114
|
+
fileName: hotspot.file,
|
|
115
|
+
issues,
|
|
116
|
+
metrics: {
|
|
117
|
+
aiSignalClarityScore: 100 - hotspot.amplificationFactor
|
|
118
|
+
// Just a rough score
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
summary: {
|
|
125
|
+
totalFiles: files.length,
|
|
126
|
+
totalIssues: results.reduce((sum, r) => sum + r.issues.length, 0),
|
|
127
|
+
criticalIssues: results.reduce(
|
|
128
|
+
(sum, r) => sum + r.issues.filter((i) => getLevel(i.severity) === 4).length,
|
|
129
|
+
0
|
|
130
|
+
),
|
|
131
|
+
majorIssues: results.reduce(
|
|
132
|
+
(sum, r) => sum + r.issues.filter((i) => getLevel(i.severity) === 3).length,
|
|
133
|
+
0
|
|
134
|
+
),
|
|
135
|
+
score: finalScore,
|
|
136
|
+
rating: riskResult.rating,
|
|
137
|
+
recommendations: riskResult.recommendations
|
|
138
|
+
},
|
|
139
|
+
results
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export {
|
|
144
|
+
__require,
|
|
145
|
+
analyzeChangeAmplification
|
|
146
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -65,7 +65,7 @@ async function analyzeChangeAmplification(options) {
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
try {
|
|
68
|
-
const parser = (0, import_core.getParser)(file);
|
|
68
|
+
const parser = await (0, import_core.getParser)(file);
|
|
69
69
|
if (!parser) continue;
|
|
70
70
|
const content = fs.readFileSync(file, "utf8");
|
|
71
71
|
const parseResult = parser.parse(content, file);
|
|
@@ -169,6 +169,7 @@ async function analyzeChangeAmplification(options) {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
// src/cli.ts
|
|
172
|
+
var import_core2 = require("@aiready/core");
|
|
172
173
|
var changeAmplificationAction = async (directory, options) => {
|
|
173
174
|
try {
|
|
174
175
|
const resolvedDir = path2.resolve(process.cwd(), directory);
|
|
@@ -183,35 +184,28 @@ var changeAmplificationAction = async (directory, options) => {
|
|
|
183
184
|
fs2.writeFileSync(outputPath, JSON.stringify(report, null, 2));
|
|
184
185
|
return;
|
|
185
186
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
} else {
|
|
209
|
-
console.log(
|
|
210
|
-
import_chalk.default.green(
|
|
211
|
-
"\n\u2728 No change amplification issues found. Architecture is well contained."
|
|
212
|
-
)
|
|
213
|
-
);
|
|
214
|
-
}
|
|
187
|
+
(0, import_core2.displayStandardConsoleReport)({
|
|
188
|
+
title: "\u{1F310} Change Amplification Analysis",
|
|
189
|
+
score: report.summary.score ?? 0,
|
|
190
|
+
rating: report.summary.rating,
|
|
191
|
+
dimensions: [
|
|
192
|
+
{
|
|
193
|
+
name: "Critical Issues",
|
|
194
|
+
value: (report.summary.criticalIssues ?? 0) * 10
|
|
195
|
+
},
|
|
196
|
+
{ name: "Major Issues", value: (report.summary.majorIssues ?? 0) * 5 }
|
|
197
|
+
],
|
|
198
|
+
issues: report.results.flatMap(
|
|
199
|
+
(r) => r.issues.map((i) => ({
|
|
200
|
+
...i,
|
|
201
|
+
message: `[${r.fileName}] ${i.message}`
|
|
202
|
+
}))
|
|
203
|
+
),
|
|
204
|
+
recommendations: report.summary.recommendations,
|
|
205
|
+
elapsedTime: "0",
|
|
206
|
+
// Not tracked in this action yet
|
|
207
|
+
noIssuesMessage: "\u2728 No change amplification issues found. Architecture is well contained."
|
|
208
|
+
});
|
|
215
209
|
} catch (error) {
|
|
216
210
|
console.error(
|
|
217
211
|
import_chalk.default.red("Error during change amplification analysis:"),
|
package/dist/cli.mjs
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
__require,
|
|
4
4
|
analyzeChangeAmplification
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SPXGOPNW.mjs";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
import chalk from "chalk";
|
|
10
10
|
import * as path from "path";
|
|
11
11
|
import * as fs from "fs";
|
|
12
|
+
import { displayStandardConsoleReport } from "@aiready/core";
|
|
12
13
|
var changeAmplificationAction = async (directory, options) => {
|
|
13
14
|
try {
|
|
14
15
|
const resolvedDir = path.resolve(process.cwd(), directory);
|
|
@@ -23,35 +24,28 @@ var changeAmplificationAction = async (directory, options) => {
|
|
|
23
24
|
fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} else {
|
|
49
|
-
console.log(
|
|
50
|
-
chalk.green(
|
|
51
|
-
"\n\u2728 No change amplification issues found. Architecture is well contained."
|
|
52
|
-
)
|
|
53
|
-
);
|
|
54
|
-
}
|
|
27
|
+
displayStandardConsoleReport({
|
|
28
|
+
title: "\u{1F310} Change Amplification Analysis",
|
|
29
|
+
score: report.summary.score ?? 0,
|
|
30
|
+
rating: report.summary.rating,
|
|
31
|
+
dimensions: [
|
|
32
|
+
{
|
|
33
|
+
name: "Critical Issues",
|
|
34
|
+
value: (report.summary.criticalIssues ?? 0) * 10
|
|
35
|
+
},
|
|
36
|
+
{ name: "Major Issues", value: (report.summary.majorIssues ?? 0) * 5 }
|
|
37
|
+
],
|
|
38
|
+
issues: report.results.flatMap(
|
|
39
|
+
(r) => r.issues.map((i) => ({
|
|
40
|
+
...i,
|
|
41
|
+
message: `[${r.fileName}] ${i.message}`
|
|
42
|
+
}))
|
|
43
|
+
),
|
|
44
|
+
recommendations: report.summary.recommendations,
|
|
45
|
+
elapsedTime: "0",
|
|
46
|
+
// Not tracked in this action yet
|
|
47
|
+
noIssuesMessage: "\u2728 No change amplification issues found. Architecture is well contained."
|
|
48
|
+
});
|
|
55
49
|
} catch (error) {
|
|
56
50
|
console.error(
|
|
57
51
|
chalk.red("Error during change amplification analysis:"),
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _aiready_core from '@aiready/core';
|
|
2
|
-
import { Issue, IssueType, ScanOptions, SpokeOutput, AnalysisResult
|
|
2
|
+
import { Issue, IssueType, ScanOptions, SpokeOutput, AnalysisResult } from '@aiready/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Change Amplification Tool Provider
|
|
@@ -21,11 +21,7 @@ declare function analyzeChangeAmplification(options: ChangeAmplificationOptions)
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Convert change amplification report into a standardized ToolScoringOutput.
|
|
24
|
-
*
|
|
25
|
-
* @param report - The detailed change amplification report.
|
|
26
|
-
* @returns Standardized scoring and risk factor breakdown.
|
|
27
|
-
* @lastUpdated 2026-03-18
|
|
28
24
|
*/
|
|
29
|
-
declare function calculateChangeAmplificationScore(report: ChangeAmplificationReport):
|
|
25
|
+
declare function calculateChangeAmplificationScore(report: ChangeAmplificationReport): any;
|
|
30
26
|
|
|
31
27
|
export { type ChangeAmplificationIssue, type ChangeAmplificationOptions, ChangeAmplificationProvider, type ChangeAmplificationReport, type FileChangeAmplificationResult, analyzeChangeAmplification, calculateChangeAmplificationScore };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _aiready_core from '@aiready/core';
|
|
2
|
-
import { Issue, IssueType, ScanOptions, SpokeOutput, AnalysisResult
|
|
2
|
+
import { Issue, IssueType, ScanOptions, SpokeOutput, AnalysisResult } from '@aiready/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Change Amplification Tool Provider
|
|
@@ -21,11 +21,7 @@ declare function analyzeChangeAmplification(options: ChangeAmplificationOptions)
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Convert change amplification report into a standardized ToolScoringOutput.
|
|
24
|
-
*
|
|
25
|
-
* @param report - The detailed change amplification report.
|
|
26
|
-
* @returns Standardized scoring and risk factor breakdown.
|
|
27
|
-
* @lastUpdated 2026-03-18
|
|
28
24
|
*/
|
|
29
|
-
declare function calculateChangeAmplificationScore(report: ChangeAmplificationReport):
|
|
25
|
+
declare function calculateChangeAmplificationScore(report: ChangeAmplificationReport): any;
|
|
30
26
|
|
|
31
27
|
export { type ChangeAmplificationIssue, type ChangeAmplificationOptions, ChangeAmplificationProvider, type ChangeAmplificationReport, type FileChangeAmplificationResult, analyzeChangeAmplification, calculateChangeAmplificationScore };
|
package/dist/index.js
CHANGED
|
@@ -66,7 +66,7 @@ async function analyzeChangeAmplification(options) {
|
|
|
66
66
|
);
|
|
67
67
|
}
|
|
68
68
|
try {
|
|
69
|
-
const parser = (0, import_core.getParser)(file);
|
|
69
|
+
const parser = await (0, import_core.getParser)(file);
|
|
70
70
|
if (!parser) continue;
|
|
71
71
|
const content = fs.readFileSync(file, "utf8");
|
|
72
72
|
const parseResult = parser.parse(content, file);
|
|
@@ -196,29 +196,23 @@ var ChangeAmplificationProvider = (0, import_core2.createProvider)({
|
|
|
196
196
|
var import_core3 = require("@aiready/core");
|
|
197
197
|
function calculateChangeAmplificationScore(report) {
|
|
198
198
|
const { summary } = report;
|
|
199
|
-
|
|
200
|
-
{
|
|
201
|
-
name: "Graph Stability",
|
|
202
|
-
impact: Math.round((summary.score ?? 0) - 50),
|
|
203
|
-
description: (summary.score ?? 0) < 30 ? "High coupling detected in core modules" : "Stable dependency structure"
|
|
204
|
-
}
|
|
205
|
-
];
|
|
206
|
-
const recommendations = summary.recommendations.map((rec) => ({
|
|
207
|
-
action: rec,
|
|
208
|
-
estimatedImpact: 10,
|
|
209
|
-
priority: (summary.score ?? 0) < 50 ? "high" : "medium"
|
|
210
|
-
}));
|
|
211
|
-
return {
|
|
199
|
+
return (0, import_core3.buildStandardToolScore)({
|
|
212
200
|
toolName: import_core3.ToolName.ChangeAmplification,
|
|
213
201
|
score: summary.score ?? 0,
|
|
214
|
-
|
|
202
|
+
rawData: {
|
|
215
203
|
totalFiles: summary.totalFiles,
|
|
216
|
-
totalIssues: summary.totalIssues
|
|
217
|
-
rating: summary.rating
|
|
204
|
+
totalIssues: summary.totalIssues
|
|
218
205
|
},
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
206
|
+
dimensions: {
|
|
207
|
+
graphStability: summary.score ?? 0
|
|
208
|
+
},
|
|
209
|
+
dimensionNames: {
|
|
210
|
+
graphStability: "Graph Stability"
|
|
211
|
+
},
|
|
212
|
+
recommendations: summary.recommendations,
|
|
213
|
+
recommendationImpact: 10,
|
|
214
|
+
rating: summary.rating
|
|
215
|
+
});
|
|
222
216
|
}
|
|
223
217
|
|
|
224
218
|
// src/index.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
analyzeChangeAmplification
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SPXGOPNW.mjs";
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
import { ToolRegistry } from "@aiready/core";
|
|
@@ -34,32 +34,26 @@ var ChangeAmplificationProvider = createProvider({
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
// src/scoring.ts
|
|
37
|
-
import { ToolName as ToolName2 } from "@aiready/core";
|
|
37
|
+
import { ToolName as ToolName2, buildStandardToolScore } from "@aiready/core";
|
|
38
38
|
function calculateChangeAmplificationScore(report) {
|
|
39
39
|
const { summary } = report;
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
name: "Graph Stability",
|
|
43
|
-
impact: Math.round((summary.score ?? 0) - 50),
|
|
44
|
-
description: (summary.score ?? 0) < 30 ? "High coupling detected in core modules" : "Stable dependency structure"
|
|
45
|
-
}
|
|
46
|
-
];
|
|
47
|
-
const recommendations = summary.recommendations.map((rec) => ({
|
|
48
|
-
action: rec,
|
|
49
|
-
estimatedImpact: 10,
|
|
50
|
-
priority: (summary.score ?? 0) < 50 ? "high" : "medium"
|
|
51
|
-
}));
|
|
52
|
-
return {
|
|
40
|
+
return buildStandardToolScore({
|
|
53
41
|
toolName: ToolName2.ChangeAmplification,
|
|
54
42
|
score: summary.score ?? 0,
|
|
55
|
-
|
|
43
|
+
rawData: {
|
|
56
44
|
totalFiles: summary.totalFiles,
|
|
57
|
-
totalIssues: summary.totalIssues
|
|
58
|
-
rating: summary.rating
|
|
45
|
+
totalIssues: summary.totalIssues
|
|
59
46
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
dimensions: {
|
|
48
|
+
graphStability: summary.score ?? 0
|
|
49
|
+
},
|
|
50
|
+
dimensionNames: {
|
|
51
|
+
graphStability: "Graph Stability"
|
|
52
|
+
},
|
|
53
|
+
recommendations: summary.recommendations,
|
|
54
|
+
recommendationImpact: 10,
|
|
55
|
+
rating: summary.rating
|
|
56
|
+
});
|
|
63
57
|
}
|
|
64
58
|
|
|
65
59
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/change-amplification",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.20",
|
|
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.23.
|
|
13
|
+
"@aiready/core": "0.23.21"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^24.0.0",
|
|
@@ -17,14 +17,16 @@ describe('analyzeChangeAmplification reproduction', () => {
|
|
|
17
17
|
it('should not return 0 if there are some dependencies but not crazy', async () => {
|
|
18
18
|
const files = ['src/a.ts', 'src/b.ts', 'src/c.ts'];
|
|
19
19
|
(scanFiles as any).mockResolvedValue(files);
|
|
20
|
-
(getParser as any).
|
|
20
|
+
(getParser as any).mockResolvedValue({
|
|
21
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
21
22
|
parse: (content: string) => {
|
|
22
23
|
if (content.includes('import b'))
|
|
23
|
-
return { imports: [{ source: './b' }] };
|
|
24
|
+
return { imports: [{ source: './b' }], exports: [] };
|
|
24
25
|
if (content.includes('import c'))
|
|
25
|
-
return { imports: [{ source: './c' }] };
|
|
26
|
-
return { imports: [] };
|
|
26
|
+
return { imports: [{ source: './c' }], exports: [] };
|
|
27
|
+
return { imports: [], exports: [] };
|
|
27
28
|
},
|
|
29
|
+
language: 'typescript',
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
(fs.readFileSync as any).mockImplementation((file: string) => {
|
|
@@ -42,10 +44,13 @@ describe('analyzeChangeAmplification reproduction', () => {
|
|
|
42
44
|
// Creating a highly coupled scenario
|
|
43
45
|
const files = Array.from({ length: 20 }, (_, i) => `src/file${i}.ts`);
|
|
44
46
|
(scanFiles as any).mockResolvedValue(files);
|
|
45
|
-
(getParser as any).
|
|
47
|
+
(getParser as any).mockResolvedValue({
|
|
48
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
46
49
|
parse: () => ({
|
|
50
|
+
exports: [],
|
|
47
51
|
imports: files.map((f) => ({ source: f })), // Everyone imports everyone
|
|
48
52
|
}),
|
|
53
|
+
language: 'typescript',
|
|
49
54
|
});
|
|
50
55
|
(fs.readFileSync as any).mockReturnValue('import everything');
|
|
51
56
|
|
|
@@ -37,7 +37,7 @@ describe('Change Amplification Scoring', () => {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
const scoring = calculateChangeAmplificationScore(lowScoreReport);
|
|
40
|
-
expect(scoring.factors[0].description).toContain('
|
|
40
|
+
expect(scoring.factors[0].description).toContain('20/100');
|
|
41
41
|
expect(scoring.recommendations[0].priority).toBe('high');
|
|
42
42
|
});
|
|
43
43
|
});
|
package/src/analyzer.ts
CHANGED
package/src/cli.ts
CHANGED
|
@@ -5,6 +5,7 @@ import * as path from 'path';
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import { analyzeChangeAmplification } from './analyzer';
|
|
7
7
|
import type { ChangeAmplificationOptions } from './types';
|
|
8
|
+
import { displayStandardConsoleReport } from '@aiready/core';
|
|
8
9
|
|
|
9
10
|
export const changeAmplificationAction = async (
|
|
10
11
|
directory: string,
|
|
@@ -27,37 +28,29 @@ export const changeAmplificationAction = async (
|
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
displayStandardConsoleReport({
|
|
32
|
+
title: '🌐 Change Amplification Analysis',
|
|
33
|
+
score: report.summary.score ?? 0,
|
|
34
|
+
rating: report.summary.rating,
|
|
35
|
+
dimensions: [
|
|
36
|
+
{
|
|
37
|
+
name: 'Critical Issues',
|
|
38
|
+
value: (report.summary.criticalIssues ?? 0) * 10,
|
|
39
|
+
},
|
|
40
|
+
{ name: 'Major Issues', value: (report.summary.majorIssues ?? 0) * 5 },
|
|
41
|
+
],
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
for (const issue of result.issues) {
|
|
48
|
-
const color =
|
|
49
|
-
issue.severity === 'critical' ? chalk.red : chalk.yellow;
|
|
50
|
-
console.log(` ${color('■')} ${issue.message}`);
|
|
51
|
-
console.log(` ${chalk.dim('Suggestion: ' + issue.suggestion)}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
console.log(
|
|
56
|
-
chalk.green(
|
|
57
|
-
'\n✨ No change amplification issues found. Architecture is well contained.'
|
|
58
|
-
)
|
|
59
|
-
);
|
|
60
|
-
}
|
|
43
|
+
issues: report.results.flatMap((r: any) =>
|
|
44
|
+
r.issues.map((i: any) => ({
|
|
45
|
+
...i,
|
|
46
|
+
message: `[${r.fileName}] ${i.message}`,
|
|
47
|
+
}))
|
|
48
|
+
),
|
|
49
|
+
recommendations: report.summary.recommendations,
|
|
50
|
+
elapsedTime: '0', // Not tracked in this action yet
|
|
51
|
+
noIssuesMessage:
|
|
52
|
+
'✨ No change amplification issues found. Architecture is well contained.',
|
|
53
|
+
});
|
|
61
54
|
} catch (error) {
|
|
62
55
|
console.error(
|
|
63
56
|
chalk.red('Error during change amplification analysis:'),
|
package/src/scoring.ts
CHANGED
|
@@ -1,46 +1,29 @@
|
|
|
1
|
-
import { ToolName } from '@aiready/core';
|
|
2
|
-
import type { ToolScoringOutput } from '@aiready/core';
|
|
1
|
+
import { ToolName, buildStandardToolScore } from '@aiready/core';
|
|
3
2
|
import type { ChangeAmplificationReport } from './types';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Convert change amplification report into a standardized ToolScoringOutput.
|
|
7
|
-
*
|
|
8
|
-
* @param report - The detailed change amplification report.
|
|
9
|
-
* @returns Standardized scoring and risk factor breakdown.
|
|
10
|
-
* @lastUpdated 2026-03-18
|
|
11
6
|
*/
|
|
12
7
|
export function calculateChangeAmplificationScore(
|
|
13
8
|
report: ChangeAmplificationReport
|
|
14
|
-
):
|
|
9
|
+
): any {
|
|
15
10
|
const { summary } = report;
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
name: 'Graph Stability',
|
|
20
|
-
impact: Math.round((summary.score ?? 0) - 50),
|
|
21
|
-
description:
|
|
22
|
-
(summary.score ?? 0) < 30
|
|
23
|
-
? 'High coupling detected in core modules'
|
|
24
|
-
: 'Stable dependency structure',
|
|
25
|
-
},
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
const recommendations: ToolScoringOutput['recommendations'] =
|
|
29
|
-
summary.recommendations.map((rec: string) => ({
|
|
30
|
-
action: rec,
|
|
31
|
-
estimatedImpact: 10,
|
|
32
|
-
priority: (summary.score ?? 0) < 50 ? 'high' : 'medium',
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
return {
|
|
12
|
+
return buildStandardToolScore({
|
|
36
13
|
toolName: ToolName.ChangeAmplification,
|
|
37
14
|
score: summary.score ?? 0,
|
|
38
|
-
|
|
15
|
+
rawData: {
|
|
39
16
|
totalFiles: summary.totalFiles,
|
|
40
17
|
totalIssues: summary.totalIssues,
|
|
41
|
-
rating: summary.rating,
|
|
42
18
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
19
|
+
dimensions: {
|
|
20
|
+
graphStability: summary.score ?? 0,
|
|
21
|
+
},
|
|
22
|
+
dimensionNames: {
|
|
23
|
+
graphStability: 'Graph Stability',
|
|
24
|
+
},
|
|
25
|
+
recommendations: summary.recommendations,
|
|
26
|
+
recommendationImpact: 10,
|
|
27
|
+
rating: summary.rating,
|
|
28
|
+
});
|
|
46
29
|
}
|