@aiready/change-amplification 0.1.6 → 0.1.10

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.1.6 build /Users/pengcao/projects/aiready/packages/change-amplification
3
+ > @aiready/change-amplification@0.1.10 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
@@ -10,7 +10,7 @@
10
10
  CJS Build start
11
11
  ESM Build start
12
12
 
13
- [10:18:49 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
13
+ [1:59:56 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
14
14
 
15
15
  package.json:33:6:
16
16
   33 │ "types": "./dist/index.d.ts"
@@ -31,7 +31,7 @@
31
31
 
32
32
 
33
33
 
34
- [10:18:49 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
34
+ [1:59:56 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
35
35
 
36
36
  package.json:38:6:
37
37
   38 │ "types": "./dist/cli.d.ts"
@@ -52,7 +52,7 @@
52
52
 
53
53
 
54
54
 
55
- [10:18:49 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
55
+ [1:59:56 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
56
56
 
57
57
  package.json:33:6:
58
58
   33 │ "types": "./dist/index.d.ts"
@@ -73,7 +73,7 @@
73
73
 
74
74
 
75
75
 
76
- [10:18:49 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
76
+ [1:59:56 PM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
77
77
 
78
78
  package.json:38:6:
79
79
   38 │ "types": "./dist/cli.d.ts"
@@ -93,15 +93,15 @@
93
93
 
94
94
 
95
95
 
96
- CJS dist/cli.js 8.08 KB
97
- CJS dist/index.js 5.21 KB
98
- CJS ⚡️ Build success in 145ms
99
96
  ESM dist/cli.mjs 2.78 KB
97
+ ESM dist/chunk-3CM4X7K3.mjs 3.47 KB
100
98
  ESM dist/index.mjs 110.00 B
101
- ESM dist/chunk-A5ACPWWO.mjs 3.92 KB
102
- ESM ⚡️ Build success in 146ms
99
+ ESM ⚡️ Build success in 127ms
100
+ CJS dist/cli.js 7.60 KB
101
+ CJS dist/index.js 4.73 KB
102
+ CJS ⚡️ Build success in 160ms
103
103
  DTS Build start
104
- DTS ⚡️ Build success in 4081ms
104
+ DTS ⚡️ Build success in 3072ms
105
105
  DTS dist/cli.d.ts 152.00 B
106
106
  DTS dist/index.d.ts 999.00 B
107
107
  DTS dist/cli.d.mts 152.00 B
@@ -0,0 +1,5 @@
1
+
2
+ 
3
+ > @aiready/change-amplification@0.1.6 lint /Users/pengcao/projects/aiready/packages/change-amplification
4
+ > eslint src --ext .ts
5
+
@@ -1,16 +1,16 @@
1
1
 
2
2
  
3
- > @aiready/change-amplification@0.1.6 test /Users/pengcao/projects/aiready/packages/change-amplification
3
+ > @aiready/change-amplification@0.1.8 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
9
+ ✓ src/__tests__/dummy.test.ts (1 test) 13ms
10
10
 
11
11
   Test Files  1 passed (1)
12
12
   Tests  1 passed (1)
13
-  Start at  22:19:16
14
-  Duration  285ms (transform 44ms, setup 0ms, import 97ms, tests 2ms, environment 0ms)
13
+  Start at  00:56:24
14
+  Duration  1.16s (transform 306ms, setup 0ms, import 474ms, tests 13ms, environment 0ms)
15
15
 
16
16
  [?25h
@@ -0,0 +1,111 @@
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
+ } from "@aiready/core";
16
+ async function analyzeChangeAmplification(options) {
17
+ const files = await scanFiles({
18
+ ...options,
19
+ include: options.include || ["**/*.{ts,tsx,js,jsx,py,go}"]
20
+ });
21
+ const dependencyGraph = /* @__PURE__ */ new Map();
22
+ const reverseGraph = /* @__PURE__ */ new Map();
23
+ for (const file of files) {
24
+ dependencyGraph.set(file, []);
25
+ reverseGraph.set(file, []);
26
+ }
27
+ let processed = 0;
28
+ for (const file of files) {
29
+ processed++;
30
+ options.onProgress?.(
31
+ processed,
32
+ files.length,
33
+ `change-amplification: analyzing files`
34
+ );
35
+ try {
36
+ const parser = getParser(file);
37
+ if (!parser) continue;
38
+ const content = fs.readFileSync(file, "utf8");
39
+ const parseResult = parser.parse(content, file);
40
+ const dependencies = parseResult.imports.map((i) => i.source);
41
+ for (const dep of dependencies) {
42
+ const depDir = path.dirname(file);
43
+ const resolvedPath = files.find((f) => {
44
+ if (dep.startsWith(".")) {
45
+ return f.startsWith(path.resolve(depDir, dep));
46
+ } else {
47
+ return f.includes(dep);
48
+ }
49
+ });
50
+ if (resolvedPath) {
51
+ dependencyGraph.get(file)?.push(resolvedPath);
52
+ reverseGraph.get(resolvedPath)?.push(file);
53
+ }
54
+ }
55
+ } catch (err) {
56
+ void err;
57
+ }
58
+ }
59
+ const fileMetrics = files.map((file) => {
60
+ const fanOut = dependencyGraph.get(file)?.length || 0;
61
+ const fanIn = reverseGraph.get(file)?.length || 0;
62
+ return { file, fanOut, fanIn };
63
+ });
64
+ const riskResult = calculateChangeAmplification({ files: fileMetrics });
65
+ const results = [];
66
+ for (const hotspot of riskResult.hotspots) {
67
+ const issues = [];
68
+ if (hotspot.amplificationFactor > 20) {
69
+ issues.push({
70
+ type: "change-amplification",
71
+ severity: hotspot.amplificationFactor > 40 ? "critical" : "major",
72
+ message: `High change amplification detected (Factor: ${hotspot.amplificationFactor}). Changes here cascade heavily.`,
73
+ location: { file: hotspot.file, line: 1 },
74
+ suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
75
+ });
76
+ }
77
+ if (hotspot.amplificationFactor > 5) {
78
+ results.push({
79
+ fileName: hotspot.file,
80
+ issues,
81
+ metrics: {
82
+ aiSignalClarityScore: 100 - hotspot.amplificationFactor
83
+ // Just a rough score
84
+ }
85
+ });
86
+ }
87
+ }
88
+ return {
89
+ summary: {
90
+ totalFiles: files.length,
91
+ totalIssues: results.reduce((sum, r) => sum + r.issues.length, 0),
92
+ criticalIssues: results.reduce(
93
+ (sum, r) => sum + r.issues.filter((i) => i.severity === "critical").length,
94
+ 0
95
+ ),
96
+ majorIssues: results.reduce(
97
+ (sum, r) => sum + r.issues.filter((i) => i.severity === "major").length,
98
+ 0
99
+ ),
100
+ score: riskResult.score,
101
+ rating: riskResult.rating,
102
+ recommendations: riskResult.recommendations
103
+ },
104
+ results
105
+ };
106
+ }
107
+
108
+ export {
109
+ __require,
110
+ analyzeChangeAmplification
111
+ };
@@ -0,0 +1,112 @@
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
+ } from "@aiready/core";
16
+ async function analyzeChangeAmplification(options) {
17
+ const rootDir = path.resolve(options.rootDir || ".");
18
+ const files = await scanFiles({
19
+ ...options,
20
+ include: options.include || ["**/*.{ts,tsx,js,jsx,py,go}"]
21
+ });
22
+ const dependencyGraph = /* @__PURE__ */ new Map();
23
+ const reverseGraph = /* @__PURE__ */ new Map();
24
+ for (const file of files) {
25
+ dependencyGraph.set(file, []);
26
+ reverseGraph.set(file, []);
27
+ }
28
+ let processed = 0;
29
+ for (const file of files) {
30
+ processed++;
31
+ options.onProgress?.(
32
+ processed,
33
+ files.length,
34
+ `change-amplification: analyzing files`
35
+ );
36
+ try {
37
+ const parser = getParser(file);
38
+ if (!parser) continue;
39
+ const content = fs.readFileSync(file, "utf8");
40
+ const parseResult = parser.parse(content, file);
41
+ const dependencies = parseResult.imports.map((i) => i.source);
42
+ for (const dep of dependencies) {
43
+ const depDir = path.dirname(file);
44
+ const resolvedPath = files.find((f) => {
45
+ if (dep.startsWith(".")) {
46
+ return f.startsWith(path.resolve(depDir, dep));
47
+ } else {
48
+ return f.includes(dep);
49
+ }
50
+ });
51
+ if (resolvedPath) {
52
+ dependencyGraph.get(file)?.push(resolvedPath);
53
+ reverseGraph.get(resolvedPath)?.push(file);
54
+ }
55
+ }
56
+ } catch (err) {
57
+ void err;
58
+ }
59
+ }
60
+ const fileMetrics = files.map((file) => {
61
+ const fanOut = dependencyGraph.get(file)?.length || 0;
62
+ const fanIn = reverseGraph.get(file)?.length || 0;
63
+ return { file, fanOut, fanIn };
64
+ });
65
+ const riskResult = calculateChangeAmplification({ files: fileMetrics });
66
+ const results = [];
67
+ for (const hotspot of riskResult.hotspots) {
68
+ const issues = [];
69
+ if (hotspot.amplificationFactor > 20) {
70
+ issues.push({
71
+ type: "change-amplification",
72
+ severity: hotspot.amplificationFactor > 40 ? "critical" : "major",
73
+ message: `High change amplification detected (Factor: ${hotspot.amplificationFactor}). Changes here cascade heavily.`,
74
+ location: { file: hotspot.file, line: 1 },
75
+ suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
76
+ });
77
+ }
78
+ if (hotspot.amplificationFactor > 5) {
79
+ results.push({
80
+ fileName: hotspot.file,
81
+ issues,
82
+ metrics: {
83
+ aiSignalClarityScore: 100 - hotspot.amplificationFactor
84
+ // Just a rough score
85
+ }
86
+ });
87
+ }
88
+ }
89
+ return {
90
+ summary: {
91
+ totalFiles: files.length,
92
+ totalIssues: results.reduce((sum, r) => sum + r.issues.length, 0),
93
+ criticalIssues: results.reduce(
94
+ (sum, r) => sum + r.issues.filter((i) => i.severity === "critical").length,
95
+ 0
96
+ ),
97
+ majorIssues: results.reduce(
98
+ (sum, r) => sum + r.issues.filter((i) => i.severity === "major").length,
99
+ 0
100
+ ),
101
+ score: riskResult.score,
102
+ rating: riskResult.rating,
103
+ recommendations: riskResult.recommendations
104
+ },
105
+ results
106
+ };
107
+ }
108
+
109
+ export {
110
+ __require,
111
+ analyzeChangeAmplification
112
+ };
@@ -0,0 +1,117 @@
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 { globSync } from "glob";
12
+ import { calculateChangeAmplification } from "@aiready/core";
13
+ import { getParser } from "@aiready/core";
14
+ function collectFiles(dir, options) {
15
+ const includePatterns = options.include && options.include.length > 0 ? options.include : ["**/*.{ts,tsx,js,jsx,py,go}"];
16
+ const excludePatterns = options.exclude && options.exclude.length > 0 ? options.exclude : ["**/node_modules/**", "**/dist/**", "**/.git/**"];
17
+ let matchedFiles = [];
18
+ for (const pattern of includePatterns) {
19
+ const files = globSync(pattern, {
20
+ cwd: dir,
21
+ ignore: excludePatterns,
22
+ absolute: true
23
+ });
24
+ matchedFiles = matchedFiles.concat(files);
25
+ }
26
+ return [...new Set(matchedFiles)];
27
+ }
28
+ async function analyzeChangeAmplification(options) {
29
+ const rootDir = path.resolve(options.rootDir || ".");
30
+ const files = collectFiles(rootDir, options);
31
+ const dependencyGraph = /* @__PURE__ */ new Map();
32
+ const reverseGraph = /* @__PURE__ */ new Map();
33
+ for (const file of files) {
34
+ dependencyGraph.set(file, []);
35
+ reverseGraph.set(file, []);
36
+ }
37
+ let processed = 0;
38
+ for (const file of files) {
39
+ processed++;
40
+ options.onProgress?.(processed, files.length, `change-amplification: analyzing ${file.substring(rootDir.length + 1)}`);
41
+ try {
42
+ const parser = getParser(file);
43
+ if (!parser) continue;
44
+ const content = fs.readFileSync(file, "utf8");
45
+ const parseResult = parser.parse(content, file);
46
+ const dependencies = parseResult.imports.map((i) => i.source);
47
+ for (const dep of dependencies) {
48
+ const depDir = path.dirname(file);
49
+ const resolvedPath = files.find((f) => {
50
+ if (dep.startsWith(".")) {
51
+ return f.startsWith(path.resolve(depDir, dep));
52
+ } else {
53
+ return f.includes(dep);
54
+ }
55
+ });
56
+ if (resolvedPath) {
57
+ dependencyGraph.get(file)?.push(resolvedPath);
58
+ reverseGraph.get(resolvedPath)?.push(file);
59
+ }
60
+ }
61
+ } catch (err) {
62
+ void err;
63
+ }
64
+ }
65
+ const fileMetrics = files.map((file) => {
66
+ const fanOut = dependencyGraph.get(file)?.length || 0;
67
+ const fanIn = reverseGraph.get(file)?.length || 0;
68
+ return { file, fanOut, fanIn };
69
+ });
70
+ const riskResult = calculateChangeAmplification({ files: fileMetrics });
71
+ const results = [];
72
+ for (const hotspot of riskResult.hotspots) {
73
+ const issues = [];
74
+ if (hotspot.amplificationFactor > 20) {
75
+ issues.push({
76
+ type: "change-amplification",
77
+ severity: hotspot.amplificationFactor > 40 ? "critical" : "major",
78
+ message: `High change amplification detected (Factor: ${hotspot.amplificationFactor}). Changes here cascade heavily.`,
79
+ location: { file: hotspot.file, line: 1 },
80
+ suggestion: `Reduce coupling. Fan-out is ${hotspot.fanOut}, Fan-in is ${hotspot.fanIn}.`
81
+ });
82
+ }
83
+ if (hotspot.amplificationFactor > 5) {
84
+ results.push({
85
+ fileName: hotspot.file,
86
+ issues,
87
+ metrics: {
88
+ aiSignalClarityScore: 100 - hotspot.amplificationFactor
89
+ // Just a rough score
90
+ }
91
+ });
92
+ }
93
+ }
94
+ return {
95
+ summary: {
96
+ totalFiles: files.length,
97
+ totalIssues: results.reduce((sum, r) => sum + r.issues.length, 0),
98
+ criticalIssues: results.reduce(
99
+ (sum, r) => sum + r.issues.filter((i) => i.severity === "critical").length,
100
+ 0
101
+ ),
102
+ majorIssues: results.reduce(
103
+ (sum, r) => sum + r.issues.filter((i) => i.severity === "major").length,
104
+ 0
105
+ ),
106
+ score: riskResult.score,
107
+ rating: riskResult.rating,
108
+ recommendations: riskResult.recommendations
109
+ },
110
+ results
111
+ };
112
+ }
113
+
114
+ export {
115
+ __require,
116
+ analyzeChangeAmplification
117
+ };
package/dist/cli.js CHANGED
@@ -42,35 +42,28 @@ var fs2 = __toESM(require("fs"));
42
42
  // src/analyzer.ts
43
43
  var fs = __toESM(require("fs"));
44
44
  var path = __toESM(require("path"));
45
- var import_glob = require("glob");
46
45
  var import_core = require("@aiready/core");
47
- var import_core2 = require("@aiready/core");
48
- function collectFiles(dir, options) {
49
- const includePatterns = options.include && options.include.length > 0 ? options.include : ["**/*.{ts,tsx,js,jsx,py,go}"];
50
- const excludePatterns = options.exclude && options.exclude.length > 0 ? options.exclude : ["**/node_modules/**", "**/dist/**", "**/.git/**"];
51
- let matchedFiles = [];
52
- for (const pattern of includePatterns) {
53
- const files = (0, import_glob.globSync)(pattern, {
54
- cwd: dir,
55
- ignore: excludePatterns,
56
- absolute: true
57
- });
58
- matchedFiles = matchedFiles.concat(files);
59
- }
60
- return [...new Set(matchedFiles)];
61
- }
62
46
  async function analyzeChangeAmplification(options) {
63
- const rootDir = path.resolve(options.rootDir || ".");
64
- const files = collectFiles(rootDir, options);
47
+ const files = await (0, import_core.scanFiles)({
48
+ ...options,
49
+ include: options.include || ["**/*.{ts,tsx,js,jsx,py,go}"]
50
+ });
65
51
  const dependencyGraph = /* @__PURE__ */ new Map();
66
52
  const reverseGraph = /* @__PURE__ */ new Map();
67
53
  for (const file of files) {
68
54
  dependencyGraph.set(file, []);
69
55
  reverseGraph.set(file, []);
70
56
  }
57
+ let processed = 0;
71
58
  for (const file of files) {
59
+ processed++;
60
+ options.onProgress?.(
61
+ processed,
62
+ files.length,
63
+ `change-amplification: analyzing files`
64
+ );
72
65
  try {
73
- const parser = (0, import_core2.getParser)(file);
66
+ const parser = (0, import_core.getParser)(file);
74
67
  if (!parser) continue;
75
68
  const content = fs.readFileSync(file, "utf8");
76
69
  const parseResult = parser.parse(content, file);
package/dist/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  __require,
4
4
  analyzeChangeAmplification
5
- } from "./chunk-A5ACPWWO.mjs";
5
+ } from "./chunk-3CM4X7K3.mjs";
6
6
 
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
package/dist/index.js CHANGED
@@ -37,35 +37,28 @@ module.exports = __toCommonJS(index_exports);
37
37
  // src/analyzer.ts
38
38
  var fs = __toESM(require("fs"));
39
39
  var path = __toESM(require("path"));
40
- var import_glob = require("glob");
41
40
  var import_core = require("@aiready/core");
42
- var import_core2 = require("@aiready/core");
43
- function collectFiles(dir, options) {
44
- const includePatterns = options.include && options.include.length > 0 ? options.include : ["**/*.{ts,tsx,js,jsx,py,go}"];
45
- const excludePatterns = options.exclude && options.exclude.length > 0 ? options.exclude : ["**/node_modules/**", "**/dist/**", "**/.git/**"];
46
- let matchedFiles = [];
47
- for (const pattern of includePatterns) {
48
- const files = (0, import_glob.globSync)(pattern, {
49
- cwd: dir,
50
- ignore: excludePatterns,
51
- absolute: true
52
- });
53
- matchedFiles = matchedFiles.concat(files);
54
- }
55
- return [...new Set(matchedFiles)];
56
- }
57
41
  async function analyzeChangeAmplification(options) {
58
- const rootDir = path.resolve(options.rootDir || ".");
59
- const files = collectFiles(rootDir, options);
42
+ const files = await (0, import_core.scanFiles)({
43
+ ...options,
44
+ include: options.include || ["**/*.{ts,tsx,js,jsx,py,go}"]
45
+ });
60
46
  const dependencyGraph = /* @__PURE__ */ new Map();
61
47
  const reverseGraph = /* @__PURE__ */ new Map();
62
48
  for (const file of files) {
63
49
  dependencyGraph.set(file, []);
64
50
  reverseGraph.set(file, []);
65
51
  }
52
+ let processed = 0;
66
53
  for (const file of files) {
54
+ processed++;
55
+ options.onProgress?.(
56
+ processed,
57
+ files.length,
58
+ `change-amplification: analyzing files`
59
+ );
67
60
  try {
68
- const parser = (0, import_core2.getParser)(file);
61
+ const parser = (0, import_core.getParser)(file);
69
62
  if (!parser) continue;
70
63
  const content = fs.readFileSync(file, "utf8");
71
64
  const parseResult = parser.parse(content, file);
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  analyzeChangeAmplification
3
- } from "./chunk-A5ACPWWO.mjs";
3
+ } from "./chunk-3CM4X7K3.mjs";
4
4
  export {
5
5
  analyzeChangeAmplification
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/change-amplification",
3
- "version": "0.1.6",
3
+ "version": "0.1.10",
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.9.33"
13
+ "@aiready/core": "0.9.37"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^24.0.0",
package/src/analyzer.ts CHANGED
@@ -1,45 +1,25 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import { globSync } from 'glob';
4
- import { calculateChangeAmplification } from '@aiready/core';
3
+ import {
4
+ scanFiles,
5
+ calculateChangeAmplification,
6
+ getParser,
7
+ } from '@aiready/core';
5
8
  import type {
6
9
  ChangeAmplificationOptions,
7
10
  ChangeAmplificationReport,
8
11
  FileChangeAmplificationResult,
9
12
  ChangeAmplificationIssue,
10
13
  } from './types';
11
- import { getParser } from '@aiready/core';
12
-
13
- function collectFiles(
14
- dir: string,
15
- options: ChangeAmplificationOptions
16
- ): string[] {
17
- const includePatterns =
18
- options.include && options.include.length > 0
19
- ? options.include
20
- : ['**/*.{ts,tsx,js,jsx,py,go}'];
21
- const excludePatterns =
22
- options.exclude && options.exclude.length > 0
23
- ? options.exclude
24
- : ['**/node_modules/**', '**/dist/**', '**/.git/**'];
25
-
26
- let matchedFiles: string[] = [];
27
- for (const pattern of includePatterns) {
28
- const files = globSync(pattern, {
29
- cwd: dir,
30
- ignore: excludePatterns,
31
- absolute: true,
32
- });
33
- matchedFiles = matchedFiles.concat(files);
34
- }
35
- return [...new Set(matchedFiles)];
36
- }
37
14
 
38
15
  export async function analyzeChangeAmplification(
39
16
  options: ChangeAmplificationOptions
40
17
  ): Promise<ChangeAmplificationReport> {
41
- const rootDir = path.resolve(options.rootDir || '.');
42
- const files = collectFiles(rootDir, options);
18
+ // Use core scanFiles which respects .gitignore recursively
19
+ const files = await scanFiles({
20
+ ...options,
21
+ include: options.include || ['**/*.{ts,tsx,js,jsx,py,go}'],
22
+ });
43
23
 
44
24
  // Compute graph metrics: fanIn and fanOut
45
25
  const dependencyGraph = new Map<string, string[]>(); // key: file, value: imported files
@@ -52,7 +32,15 @@ export async function analyzeChangeAmplification(
52
32
  }
53
33
 
54
34
  // Parse files to build dependency graph
35
+ let processed = 0;
55
36
  for (const file of files) {
37
+ processed++;
38
+ options.onProgress?.(
39
+ processed,
40
+ files.length,
41
+ `change-amplification: analyzing files`
42
+ );
43
+
56
44
  try {
57
45
  const parser = getParser(file);
58
46
  if (!parser) continue;