ai-git-tools 2.0.62 → 2.0.63

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 CHANGED
@@ -172,6 +172,7 @@ program
172
172
  await autoDevCommand(options);
173
173
  process.exit(0);
174
174
  } catch (error) {
175
+ console.error(`\n[錯誤] ${error.message}`);
175
176
  process.exit(1);
176
177
  }
177
178
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-git-tools",
3
- "version": "2.0.62",
3
+ "version": "2.0.63",
4
4
  "description": "AI-powered Git automation tools for commit messages and PR generation",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -5,13 +5,11 @@
5
5
  */
6
6
 
7
7
  import { resolve } from 'path';
8
- import { existsSync, readFileSync } from 'fs';
9
- import { execSync } from 'child_process';
10
8
  import inquirer from 'inquirer';
11
9
  import { IssueReader } from '../core/issue-reader.js';
12
10
  import { CodeGenerator } from '../core/code-generator.js';
13
11
  import { AIClient } from '../core/ai-client.js';
14
- import { writeAndTestCommand, TEST_TYPE_CHOICES, normalizeTestTypeSelection, resolveRequestedTestTypes } from './write-and-test.js';
12
+ import { writeAndTestCommand, TEST_TYPE_CHOICES, normalizeTestTypeSelection } from './write-and-test.js';
15
13
  import { Logger } from '../utils/logger.js';
16
14
 
17
15
  export async function autoDevCommand(options = {}) {
@@ -123,11 +121,6 @@ export async function autoDevCommand(options = {}) {
123
121
  requestedTestType: options.testType,
124
122
  });
125
123
 
126
- const resolvedRuntimeTestTypes = resolveRequestedTestTypes(absoluteFilePath, selectedTestType);
127
-
128
- logger.step('正在驗證專案可用的測試腳本...');
129
- ensureJestAvailable(resolvedRuntimeTestTypes);
130
- logger.success('已確認可執行 Jest 測試。');
131
124
  console.log('');
132
125
 
133
126
  // ============================================================
@@ -261,61 +254,3 @@ async function resolveTestTypeForAutoDev({ noConfirm, requestedTestType }) {
261
254
 
262
255
  return testType;
263
256
  }
264
-
265
- function ensureJestAvailable(runtimeTestType = ['unit']) {
266
- const packageJson = readProjectPackageJson();
267
-
268
- try {
269
- execSync('npx jest --version', {
270
- encoding: 'utf-8',
271
- stdio: 'pipe',
272
- cwd: process.cwd(),
273
- });
274
- ensureComponentTestDependencies(packageJson, runtimeTestType);
275
- return;
276
- } catch {
277
- const hasJestDependency = Boolean(
278
- packageJson.dependencies?.jest || packageJson.devDependencies?.jest
279
- );
280
-
281
- if (!hasJestDependency) {
282
- throw new Error('目前專案未安裝 Jest,請先安裝後再執行 auto-dev。');
283
- }
284
-
285
- ensureComponentTestDependencies(packageJson, runtimeTestType);
286
-
287
- throw new Error('偵測到 Jest 依賴,但無法執行 npx jest,請檢查安裝或 lockfile 狀態。');
288
- }
289
- }
290
-
291
- function readProjectPackageJson() {
292
- const packageJsonPath = resolve(process.cwd(), 'package.json');
293
- if (!existsSync(packageJsonPath)) {
294
- throw new Error('目前目錄沒有 package.json,無法執行 Jest 測試。');
295
- }
296
-
297
- try {
298
- return JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
299
- } catch {
300
- throw new Error('無法讀取目前專案的 package.json,請確認 Jest 已安裝。');
301
- }
302
- }
303
-
304
- function ensureComponentTestDependencies(packageJson, runtimeTestType) {
305
- if (!runtimeTestType.includes('component')) {
306
- return;
307
- }
308
-
309
- const hasJsdomDependency = Boolean(
310
- packageJson.devDependencies?.['jest-environment-jsdom']
311
- || packageJson.dependencies?.['jest-environment-jsdom']
312
- );
313
- const hasTestingLibraryDependency = Boolean(
314
- packageJson.devDependencies?.['@testing-library/react']
315
- || packageJson.dependencies?.['@testing-library/react']
316
- );
317
-
318
- if (!hasJsdomDependency || !hasTestingLibraryDependency) {
319
- throw new Error('元件測試需要 jest-environment-jsdom 與 @testing-library/react,請先安裝後再執行 auto-dev。');
320
- }
321
- }
@@ -1,12 +1,46 @@
1
1
  /**
2
2
  * TestRunner 服務
3
- * 執行 Jest 測試、解析結果,並在失敗時觸發自動修復(最多 2 次)
3
+ * 執行 Jest / bun test 測試、解析結果,並在失敗時觸發自動修復(最多 2 次)
4
4
  */
5
5
 
6
6
  import { execSync } from 'child_process';
7
- import { existsSync } from 'fs';
7
+ import { existsSync, readFileSync } from 'fs';
8
8
  import { resolve } from 'path';
9
9
 
10
+ /**
11
+ * 偵測目前專案使用的測試執行器
12
+ * 優先順序:bun test > vitest > jest
13
+ */
14
+ function detectTestRunner() {
15
+ const packageJsonPath = resolve(process.cwd(), 'package.json');
16
+ if (!existsSync(packageJsonPath)) {
17
+ return 'jest';
18
+ }
19
+
20
+ try {
21
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
22
+ const allDeps = {
23
+ ...pkg.dependencies,
24
+ ...pkg.devDependencies,
25
+ };
26
+
27
+ // 偵測 bun:test 腳本含 bun test,或安裝了 bun
28
+ const testScript = pkg.scripts?.test || '';
29
+ if (testScript.includes('bun test')) {
30
+ return 'bun';
31
+ }
32
+
33
+ // 偵測 vitest
34
+ if (allDeps.vitest) {
35
+ return 'vitest';
36
+ }
37
+
38
+ return 'jest';
39
+ } catch {
40
+ return 'jest';
41
+ }
42
+ }
43
+
10
44
  export class TestRunner {
11
45
  /**
12
46
  * 執行指定的測試檔案
@@ -21,22 +55,32 @@ export class TestRunner {
21
55
  return { success: false, errors: [`測試檔案不存在:${missingTestFilePath}`] };
22
56
  }
23
57
 
58
+ const runner = detectTestRunner();
59
+
24
60
  try {
25
- const absoluteTestFilePaths = testFilePaths
26
- .map((filePath) => `"${resolve(filePath)}"`)
27
- .join(' ');
61
+ const absoluteTestFilePaths = testFilePaths.map((filePath) => `"${resolve(filePath)}"`);
62
+
63
+ let cmd;
64
+ if (runner === 'bun') {
65
+ // bun test 接受多個檔案作為參數
66
+ cmd = `bun test ${absoluteTestFilePaths.join(' ')}`;
67
+ } else if (runner === 'vitest') {
68
+ cmd = `npx vitest run ${absoluteTestFilePaths.join(' ')}`;
69
+ } else {
70
+ cmd = `npx jest --runInBand --runTestsByPath ${absoluteTestFilePaths.join(' ')} --no-coverage`;
71
+ }
28
72
 
29
- execSync(`npx jest --runInBand --runTestsByPath ${absoluteTestFilePaths} --no-coverage`, {
73
+ execSync(cmd, {
30
74
  encoding: 'utf-8',
31
75
  stdio: 'pipe',
32
76
  timeout: 120000, // 2 分鐘超時
33
77
  cwd: process.cwd(),
34
78
  });
35
- return { success: true, errors: [] };
79
+ return { success: true, errors: [], runner };
36
80
  } catch (error) {
37
81
  const output = (error.stdout || '') + (error.stderr || '');
38
82
  const errors = TestRunner._parseErrors(output);
39
- return { success: false, errors };
83
+ return { success: false, errors, runner };
40
84
  }
41
85
  }
42
86