ai-git-tools 2.0.48 → 2.0.49
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/bin/cli.js +0 -22
- package/package.json +1 -1
- package/src/commands/commit-all.js +0 -79
- package/src/commands/init.js +0 -38
- package/src/commands/pr.js +0 -63
- package/src/core/ai-client.js +15 -28
- package/src/core/config-loader.js +0 -53
- package/src/pr-modules/core/github-api.js +0 -84
- package/src/commands/autodev.js +0 -62
- package/src/dev-modules/ai/code-generator.js +0 -288
- package/src/dev-modules/core/autodev-workflow.js +0 -305
- package/src/dev-modules/core/issue-parser.js +0 -144
- package/src/dev-modules/test/executor-base.js +0 -74
- package/src/dev-modules/test/executor-factory.js +0 -29
- package/src/dev-modules/test/jest-executor.js +0 -107
- package/src/dev-modules/test/mocha-executor.js +0 -95
- package/src/dev-modules/test/result-formatter.js +0 -90
- package/src/dev-modules/test/test-detector.js +0 -170
- package/src/dev-modules/test/vitest-executor.js +0 -90
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 測試框架檢測模組
|
|
3
|
-
* 自動偵測專案使用的測試框架(Jest / Vitest / Mocha)
|
|
4
|
-
* 並發現所有相關測試檔案
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
|
|
8
|
-
import { resolve, join } from 'path';
|
|
9
|
-
|
|
10
|
-
/** 支援的框架識別字 */
|
|
11
|
-
export const FRAMEWORKS = {
|
|
12
|
-
JEST: 'jest',
|
|
13
|
-
VITEST: 'vitest',
|
|
14
|
-
MOCHA: 'mocha',
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export class TestDetector {
|
|
18
|
-
/**
|
|
19
|
-
* @param {string} projectRoot - 專案根目錄(預設 process.cwd())
|
|
20
|
-
*/
|
|
21
|
-
constructor(projectRoot = process.cwd()) {
|
|
22
|
-
this.projectRoot = projectRoot;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 偵測測試框架
|
|
27
|
-
* 優先順序:設定檔 > package.json scripts > package.json devDependencies
|
|
28
|
-
* @returns {string|null} 框架名稱或 null
|
|
29
|
-
*/
|
|
30
|
-
detectFramework() {
|
|
31
|
-
const root = this.projectRoot;
|
|
32
|
-
|
|
33
|
-
// 1. 專用設定檔
|
|
34
|
-
if (
|
|
35
|
-
existsSync(join(root, 'vitest.config.ts')) ||
|
|
36
|
-
existsSync(join(root, 'vitest.config.js')) ||
|
|
37
|
-
existsSync(join(root, 'vitest.config.mjs'))
|
|
38
|
-
) {
|
|
39
|
-
return FRAMEWORKS.VITEST;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
existsSync(join(root, 'jest.config.js')) ||
|
|
44
|
-
existsSync(join(root, 'jest.config.ts')) ||
|
|
45
|
-
existsSync(join(root, 'jest.config.mjs')) ||
|
|
46
|
-
existsSync(join(root, 'jest.config.json'))
|
|
47
|
-
) {
|
|
48
|
-
return FRAMEWORKS.JEST;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
existsSync(join(root, '.mocharc.js')) ||
|
|
53
|
-
existsSync(join(root, '.mocharc.cjs')) ||
|
|
54
|
-
existsSync(join(root, '.mocharc.json')) ||
|
|
55
|
-
existsSync(join(root, '.mocharc.yml')) ||
|
|
56
|
-
existsSync(join(root, '.mocharc.yaml'))
|
|
57
|
-
) {
|
|
58
|
-
return FRAMEWORKS.MOCHA;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// 2. package.json
|
|
62
|
-
const pkgPath = join(root, 'package.json');
|
|
63
|
-
if (existsSync(pkgPath)) {
|
|
64
|
-
try {
|
|
65
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
66
|
-
|
|
67
|
-
// test script
|
|
68
|
-
const testScript = pkg.scripts?.test || '';
|
|
69
|
-
if (/\bvitest\b/.test(testScript)) return FRAMEWORKS.VITEST;
|
|
70
|
-
if (/\bjest\b/.test(testScript)) return FRAMEWORKS.JEST;
|
|
71
|
-
if (/\bmocha\b/.test(testScript)) return FRAMEWORKS.MOCHA;
|
|
72
|
-
|
|
73
|
-
// devDependencies / dependencies
|
|
74
|
-
const allDeps = {
|
|
75
|
-
...pkg.devDependencies,
|
|
76
|
-
...pkg.dependencies,
|
|
77
|
-
};
|
|
78
|
-
if (allDeps.vitest) return FRAMEWORKS.VITEST;
|
|
79
|
-
if (allDeps.jest || allDeps['@jest/core']) return FRAMEWORKS.JEST;
|
|
80
|
-
if (allDeps.mocha) return FRAMEWORKS.MOCHA;
|
|
81
|
-
} catch (_) {
|
|
82
|
-
// 解析失敗,繼續往下
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 遞迴搜尋符合 patterns 的檔案
|
|
91
|
-
* @param {string} dir
|
|
92
|
-
* @param {RegExp[]} patterns
|
|
93
|
-
* @param {string[]} excludes - 排除目錄名稱
|
|
94
|
-
* @returns {string[]}
|
|
95
|
-
*/
|
|
96
|
-
_glob(dir, patterns, excludes = ['node_modules', 'dist', '.next', 'coverage', '.git']) {
|
|
97
|
-
const results = [];
|
|
98
|
-
|
|
99
|
-
let entries;
|
|
100
|
-
try {
|
|
101
|
-
entries = readdirSync(dir);
|
|
102
|
-
} catch (_) {
|
|
103
|
-
return results;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
for (const entry of entries) {
|
|
107
|
-
if (excludes.includes(entry)) continue;
|
|
108
|
-
const fullPath = join(dir, entry);
|
|
109
|
-
let stat;
|
|
110
|
-
try {
|
|
111
|
-
stat = statSync(fullPath);
|
|
112
|
-
} catch (_) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (stat.isDirectory()) {
|
|
117
|
-
results.push(...this._glob(fullPath, patterns, excludes));
|
|
118
|
-
} else if (patterns.some((p) => p.test(entry))) {
|
|
119
|
-
results.push(fullPath);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return results;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* 依框架發現測試檔案
|
|
128
|
-
* @param {string} framework
|
|
129
|
-
* @param {Object} [config] - 可選:{ testPaths?: string[] }
|
|
130
|
-
* @returns {string[]}
|
|
131
|
-
*/
|
|
132
|
-
discoverTests(framework, config = {}) {
|
|
133
|
-
// 若使用者明確指定路徑,直接回傳(轉為絕對路徑)
|
|
134
|
-
if (config.testPaths && config.testPaths.length > 0) {
|
|
135
|
-
return config.testPaths.map((p) => resolve(this.projectRoot, p));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const commonPatterns = [
|
|
139
|
-
/\.test\.(js|ts|jsx|tsx|mjs|cjs)$/,
|
|
140
|
-
/\.spec\.(js|ts|jsx|tsx|mjs|cjs)$/,
|
|
141
|
-
];
|
|
142
|
-
|
|
143
|
-
switch (framework) {
|
|
144
|
-
case FRAMEWORKS.JEST:
|
|
145
|
-
case FRAMEWORKS.VITEST:
|
|
146
|
-
return this._glob(this.projectRoot, commonPatterns);
|
|
147
|
-
|
|
148
|
-
case FRAMEWORKS.MOCHA: {
|
|
149
|
-
// Mocha 慣例:test/ 或 tests/ 目錄
|
|
150
|
-
const mochaPatterns = [/\.(js|ts|mjs|cjs)$/];
|
|
151
|
-
const dirs = ['test', 'tests', 'spec', 'specs'].map((d) =>
|
|
152
|
-
join(this.projectRoot, d)
|
|
153
|
-
);
|
|
154
|
-
const results = [];
|
|
155
|
-
for (const d of dirs) {
|
|
156
|
-
if (existsSync(d)) {
|
|
157
|
-
results.push(...this._glob(d, mochaPatterns));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// 也搜尋根目錄的 .test. / .spec. 檔案
|
|
161
|
-
results.push(...this._glob(this.projectRoot, commonPatterns));
|
|
162
|
-
return [...new Set(results)];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
default:
|
|
166
|
-
// 未知框架,使用通用 patterns
|
|
167
|
-
return this._glob(this.projectRoot, commonPatterns);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vitest 測試執行器
|
|
3
|
-
* 使用 --reporter=json 取得結構化輸出
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { execSync } from 'child_process';
|
|
7
|
-
import { ExecutorBase } from './executor-base.js';
|
|
8
|
-
|
|
9
|
-
export class VitestExecutor extends ExecutorBase {
|
|
10
|
-
constructor(options = {}) {
|
|
11
|
-
super(options);
|
|
12
|
-
this.framework = 'vitest';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 執行 Vitest,回傳標準化 TestResults
|
|
17
|
-
* @param {string[]} testPaths
|
|
18
|
-
* @param {Object} options
|
|
19
|
-
* @returns {Promise<import('./executor-base.js').TestResults>}
|
|
20
|
-
*/
|
|
21
|
-
async run(testPaths = [], options = {}) {
|
|
22
|
-
const pathArgs = testPaths.length > 0 ? testPaths.join(' ') : '';
|
|
23
|
-
const cmd = `npx vitest run --reporter=json ${pathArgs}`;
|
|
24
|
-
|
|
25
|
-
if (options.verbose) {
|
|
26
|
-
console.log(` 執行命令:${cmd}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let output = '';
|
|
30
|
-
try {
|
|
31
|
-
output = execSync(cmd, {
|
|
32
|
-
encoding: 'utf-8',
|
|
33
|
-
timeout: this.timeout,
|
|
34
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
35
|
-
cwd: options.projectRoot || process.cwd(),
|
|
36
|
-
});
|
|
37
|
-
} catch (error) {
|
|
38
|
-
output = error.stdout || error.output?.[1] || '';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return this.parseResults(output);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 將 Vitest --reporter=json 輸出解析為標準 TestResults
|
|
46
|
-
* @param {string} output
|
|
47
|
-
* @returns {import('./executor-base.js').TestResults}
|
|
48
|
-
*/
|
|
49
|
-
parseResults(output) {
|
|
50
|
-
const result = this.emptyResults(this.framework);
|
|
51
|
-
result.rawOutput = output;
|
|
52
|
-
|
|
53
|
-
let json;
|
|
54
|
-
try {
|
|
55
|
-
const start = output.indexOf('{');
|
|
56
|
-
if (start === -1) return result;
|
|
57
|
-
json = JSON.parse(output.slice(start));
|
|
58
|
-
} catch (_) {
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Vitest JSON 結構
|
|
63
|
-
const sum = (json.numPassedTests ?? 0) + (json.numFailedTests ?? 0) + (json.numPendingTests ?? 0);
|
|
64
|
-
const numTotalTests = json.numTotalTests ?? sum;
|
|
65
|
-
result.total = numTotalTests;
|
|
66
|
-
result.passed = json.numPassedTests ?? 0;
|
|
67
|
-
result.failed = json.numFailedTests ?? 0;
|
|
68
|
-
result.skipped = json.numPendingTests ?? 0;
|
|
69
|
-
result.duration = json.testResults?.reduce(
|
|
70
|
-
(acc, s) => acc + (s.perfStats?.runtime ?? s.duration ?? 0),
|
|
71
|
-
0
|
|
72
|
-
) ?? 0;
|
|
73
|
-
|
|
74
|
-
for (const suite of json.testResults ?? []) {
|
|
75
|
-
const suiteName =
|
|
76
|
-
suite.name?.replace(process.cwd(), '').replace(/^\//, '') ?? '';
|
|
77
|
-
for (const t of suite.assertionResults ?? []) {
|
|
78
|
-
result.tests.push({
|
|
79
|
-
name: t.fullName ?? t.title,
|
|
80
|
-
suite: suiteName,
|
|
81
|
-
passed: t.status === 'passed',
|
|
82
|
-
duration: t.duration ?? 0,
|
|
83
|
-
error: t.failureMessages?.join('\n') || null,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
|
-
}
|