ai-git-tools 2.0.46 → 2.0.47
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
|
@@ -108,6 +108,7 @@ program
|
|
|
108
108
|
.option('--skip-pr', '跳過自動 PR 建立')
|
|
109
109
|
.option('--skip-all', '只執行測試並發佈評論')
|
|
110
110
|
.option('--commit-only', '執行測試 + commit,不建立 PR')
|
|
111
|
+
.option('--skip-tests', '無測試可執行時仍繼續 commit/PR(不強制停止)')
|
|
111
112
|
.action(async (issue, options) => {
|
|
112
113
|
try {
|
|
113
114
|
await autodevCommand(issue, options);
|
package/package.json
CHANGED
package/src/commands/autodev.js
CHANGED
|
@@ -40,6 +40,7 @@ export async function autodevCommand(issueInput, cliOptions = {}) {
|
|
|
40
40
|
skipPr: cliOptions.skipPr ?? config.skipPr ?? false,
|
|
41
41
|
skipAll: cliOptions.skipAll ?? false,
|
|
42
42
|
commitOnly: cliOptions.commitOnly ?? false,
|
|
43
|
+
skipTests: cliOptions.skipTests ?? config.skipTests ?? false,
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
// 永遠顯示啟動設定,方便確認
|
|
@@ -49,6 +50,7 @@ export async function autodevCommand(issueInput, cliOptions = {}) {
|
|
|
49
50
|
console.log(` 測試框架 : ${options.framework ?? '自動偵測'}`);
|
|
50
51
|
console.log(` Commit : ${options.skipAll || options.skipCommit ? '跳過' : '自動'}`);
|
|
51
52
|
console.log(` PR : ${options.skipAll || options.skipPr || options.commitOnly ? '跳過' : '自動'}`);
|
|
53
|
+
console.log(` 跳過測試 : ${options.skipTests ? '是(--skip-tests)' : '否(0 個測試時停止)'}`);
|
|
52
54
|
if (options.dryRun) console.log(' ⚠️ 乾運行模式(不實際執行)');
|
|
53
55
|
|
|
54
56
|
const workflow = new AutodevWorkflow(config);
|
|
@@ -28,26 +28,45 @@ export class CodeGenerator {
|
|
|
28
28
|
async generateCodeFiles(issueData, projectRoot = process.cwd()) {
|
|
29
29
|
const prompt = this._buildPrompt(issueData, projectRoot);
|
|
30
30
|
|
|
31
|
-
const VALID_MODELS = [
|
|
31
|
+
const VALID_MODELS = [
|
|
32
|
+
'gpt-4.1',
|
|
33
|
+
'gpt-4o',
|
|
34
|
+
'claude-haiku-4.5',
|
|
35
|
+
'claude-sonnet-4.5',
|
|
36
|
+
'claude-sonnet-4.6',
|
|
37
|
+
'o3',
|
|
38
|
+
'o4-mini',
|
|
39
|
+
];
|
|
32
40
|
if (!VALID_MODELS.includes(this.model)) {
|
|
33
41
|
console.warn(` ⚠️ model「${this.model}」可能無效!支援的模型:${VALID_MODELS.join(', ')}`);
|
|
34
42
|
}
|
|
35
|
-
|
|
43
|
+
|
|
44
|
+
// Claude 模型使用 extended thinking,需要更長的等待時間
|
|
45
|
+
const isClaude = this.model.toLowerCase().includes('claude');
|
|
46
|
+
const timeout = isClaude ? 300_000 : 120_000; // Claude 5 分鐘,GPT 2 分鐘
|
|
47
|
+
const timeoutMinutes = timeout / 60_000;
|
|
48
|
+
|
|
49
|
+
console.log(` 🤖 調用 AI 生成代碼框架(model: ${this.model},最多等待 ${timeoutMinutes} 分鐘)...`);
|
|
50
|
+
if (isClaude) {
|
|
51
|
+
console.log(' ℹ️ Claude 模型使用 extended thinking 模式,回應時間較長,請耐心等候');
|
|
52
|
+
console.log(' 💡 若等待太久,可改用較快的模型:在 .ai-git-config 的 autodev.aiModel 改為 "gpt-4.1"');
|
|
53
|
+
}
|
|
36
54
|
|
|
37
55
|
let response;
|
|
38
56
|
try {
|
|
39
|
-
// 代碼生成需要較長時間,使用 180 秒 timeout(直接傳給 SDK)
|
|
40
57
|
response = await AIClient.sendAndWait(
|
|
41
58
|
prompt,
|
|
42
59
|
this.model,
|
|
43
60
|
this.config.maxRetries || 3,
|
|
44
|
-
|
|
61
|
+
timeout
|
|
45
62
|
);
|
|
46
63
|
} catch (error) {
|
|
47
64
|
console.warn(` ❌ AI 調用最終失敗:${error.message}`);
|
|
48
65
|
console.warn(' 💡 排查建議:');
|
|
49
66
|
console.warn(` 1. 確認 model「${this.model}」已在你的 Copilot 方案中啟用`);
|
|
50
|
-
console.warn(
|
|
67
|
+
console.warn(
|
|
68
|
+
' 2. 嘗試改用 gpt-4.1:在 .ai-git-config 的 autodev.aiModel 改為 "gpt-4.1"'
|
|
69
|
+
);
|
|
51
70
|
console.warn(' 3. 確認 gh auth status 有登入 GitHub');
|
|
52
71
|
return [];
|
|
53
72
|
}
|
|
@@ -68,8 +87,9 @@ export class CodeGenerator {
|
|
|
68
87
|
}
|
|
69
88
|
} else {
|
|
70
89
|
console.log(` ✅ AI 規劃生成 ${files.length} 個檔案:`);
|
|
71
|
-
files.forEach(
|
|
72
|
-
const icon =
|
|
90
|
+
files.forEach(f => {
|
|
91
|
+
const icon =
|
|
92
|
+
f.filePath.includes('.test.') || f.filePath.includes('__tests__') ? '🧪' : '📄';
|
|
73
93
|
console.log(` ${icon} ${f.filePath}`);
|
|
74
94
|
});
|
|
75
95
|
}
|
|
@@ -208,7 +228,7 @@ ${body}
|
|
|
208
228
|
return [];
|
|
209
229
|
}
|
|
210
230
|
|
|
211
|
-
return files.map(
|
|
231
|
+
return files.map(f => ({
|
|
212
232
|
filePath: f.filePath || '',
|
|
213
233
|
content: f.content || '',
|
|
214
234
|
type: f.type || 'new',
|
|
@@ -21,6 +21,7 @@ import { prProgrammatic } from '../../commands/pr.js';
|
|
|
21
21
|
* @property {boolean} [skipPr] - 跳過 PR 建立
|
|
22
22
|
* @property {boolean} [skipAll] - 只測試 + 發佈評論,跳過 commit 和 PR
|
|
23
23
|
* @property {boolean} [commitOnly] - 測試 + commit,不建立 PR
|
|
24
|
+
* @property {boolean} [skipTests] - 無測試可執行時繼續(而非停止)
|
|
24
25
|
*/
|
|
25
26
|
|
|
26
27
|
export class AutodevWorkflow {
|
|
@@ -127,9 +128,20 @@ export class AutodevWorkflow {
|
|
|
127
128
|
return;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
//
|
|
131
|
+
// 若沒有測試:預設停止,加 --skip-tests 才繼續
|
|
131
132
|
if (testResults.total === 0) {
|
|
132
|
-
|
|
133
|
+
if (options.skipTests) {
|
|
134
|
+
console.warn(' ⚠️ 無測試可執行(--skip-tests 已啟用,繼續後續流程)');
|
|
135
|
+
} else {
|
|
136
|
+
console.log('\n⏸️ 無測試可執行,已停止自動 commit / PR');
|
|
137
|
+
console.log(' 可能原因:');
|
|
138
|
+
console.log(' 1. 生成的測試檔案依賴未安裝(node_modules 缺失或模組不存在)');
|
|
139
|
+
console.log(' 2. 生成的測試檔案語法有誤(用 npx jest --listTests 確認)');
|
|
140
|
+
console.log(' 3. testPaths 設定範圍未涵蓋新生成的檔案');
|
|
141
|
+
console.log('\n 若確認要跳過測試直接 commit/PR,可加上 --skip-tests:');
|
|
142
|
+
console.log(' ai autodev <issue> --skip-tests');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
133
145
|
}
|
|
134
146
|
|
|
135
147
|
// ── 9. 自動 commit-all ───────────────────────────────────
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { execSync } from 'child_process';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
7
9
|
import { ExecutorBase } from './executor-base.js';
|
|
8
10
|
|
|
9
11
|
export class JestExecutor extends ExecutorBase {
|
|
@@ -22,6 +24,25 @@ export class JestExecutor extends ExecutorBase {
|
|
|
22
24
|
async run(testPaths = [], options = {}) {
|
|
23
25
|
const pathArgs = testPaths.length > 0 ? testPaths.join(' ') : '';
|
|
24
26
|
const cmd = `npx jest --json --forceExit --passWithNoTests ${pathArgs}`;
|
|
27
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
28
|
+
|
|
29
|
+
// 若 node_modules 不存在,自動執行 npm install 安裝依賴
|
|
30
|
+
const nodeModulesPath = join(projectRoot, 'node_modules');
|
|
31
|
+
if (!existsSync(nodeModulesPath)) {
|
|
32
|
+
console.log(' 📦 偵測到 node_modules 不存在,自動執行 npm install...');
|
|
33
|
+
try {
|
|
34
|
+
execSync('npm install', {
|
|
35
|
+
cwd: projectRoot,
|
|
36
|
+
stdio: 'inherit',
|
|
37
|
+
timeout: 120_000, // 2 分鐘安裝時間上限
|
|
38
|
+
});
|
|
39
|
+
console.log(' ✅ 依賴安裝完成');
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.warn(` ⚠️ npm install 失敗:${err.message}`);
|
|
42
|
+
console.warn(' 跳過測試步驟,代碼已生成但未驗證');
|
|
43
|
+
return this.emptyResults(this.framework);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
25
46
|
|
|
26
47
|
if (options.verbose) {
|
|
27
48
|
console.log(` 執行命令:${cmd}`);
|
|
@@ -33,7 +54,7 @@ export class JestExecutor extends ExecutorBase {
|
|
|
33
54
|
encoding: 'utf-8',
|
|
34
55
|
timeout: this.timeout,
|
|
35
56
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
-
cwd:
|
|
57
|
+
cwd: projectRoot,
|
|
37
58
|
});
|
|
38
59
|
} catch (error) {
|
|
39
60
|
// Jest 測試失敗時 exit code 非 0,但 stdout 仍有 JSON
|