ai-git-tools 2.0.41 → 2.0.44
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/package.json
CHANGED
|
@@ -196,6 +196,9 @@ export async function loadAutodevConfig() {
|
|
|
196
196
|
skipPr: false,
|
|
197
197
|
verbose: false,
|
|
198
198
|
projectRoot: process.cwd(),
|
|
199
|
+
// AI 設定(可被 autodev 子區塊或全域 ai 區塊覆蓋)
|
|
200
|
+
aiModel: 'gpt-4.1',
|
|
201
|
+
maxRetries: 3,
|
|
199
202
|
};
|
|
200
203
|
|
|
201
204
|
// 支援 .ai-git-config.js 和 .ai-git-config.mjs
|
|
@@ -205,11 +208,14 @@ export async function loadAutodevConfig() {
|
|
|
205
208
|
];
|
|
206
209
|
|
|
207
210
|
let userAutodev = {};
|
|
211
|
+
let userAi = {};
|
|
208
212
|
for (const configPath of candidates) {
|
|
209
213
|
if (existsSync(configPath)) {
|
|
210
214
|
try {
|
|
211
215
|
const imported = await import(`file://${configPath}`);
|
|
212
|
-
|
|
216
|
+
const root = imported.default || {};
|
|
217
|
+
userAutodev = root.autodev || {};
|
|
218
|
+
userAi = root.ai || {}; // 讀取全域 ai 區塊
|
|
213
219
|
break;
|
|
214
220
|
} catch (error) {
|
|
215
221
|
console.warn(`⚠️ 載入 autodev 配置失敗: ${error.message}`);
|
|
@@ -219,6 +225,10 @@ export async function loadAutodevConfig() {
|
|
|
219
225
|
|
|
220
226
|
return {
|
|
221
227
|
...defaults,
|
|
228
|
+
// 全域 ai 區塊優先度低於 autodev 子區塊
|
|
229
|
+
aiModel: userAutodev.aiModel ?? userAi.model ?? defaults.aiModel,
|
|
230
|
+
maxRetries: userAutodev.maxRetries ?? userAi.maxRetries ?? defaults.maxRetries,
|
|
231
|
+
// 其他 autodev 欄位
|
|
222
232
|
...userAutodev,
|
|
223
233
|
};
|
|
224
234
|
}
|
|
@@ -28,23 +28,45 @@ export class CodeGenerator {
|
|
|
28
28
|
async generateCodeFiles(issueData, projectRoot = process.cwd()) {
|
|
29
29
|
const prompt = this._buildPrompt(issueData, projectRoot);
|
|
30
30
|
|
|
31
|
-
console.log(' 🤖 調用 AI
|
|
31
|
+
console.log(' 🤖 調用 AI 生成代碼框架(最多等待 3 分鐘)...');
|
|
32
32
|
|
|
33
33
|
let response;
|
|
34
34
|
try {
|
|
35
|
+
// 代碼生成需要較長時間,使用 180 秒 timeout
|
|
35
36
|
response = await AIClient.sendAndWait(
|
|
36
37
|
prompt,
|
|
37
38
|
this.model,
|
|
38
|
-
this.config.maxRetries || 3
|
|
39
|
+
this.config.maxRetries || 3,
|
|
40
|
+
180_000
|
|
39
41
|
);
|
|
40
42
|
} catch (error) {
|
|
43
|
+
// 顯示完整錯誤訊息方便除錯
|
|
41
44
|
console.warn(` ⚠️ AI 調用失敗:${error.message}`);
|
|
45
|
+
if (error.cause) console.warn(` 原因:${error.cause}`);
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!response) {
|
|
50
|
+
console.warn(' ⚠️ AI 回傳空回應');
|
|
42
51
|
return [];
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
// 解析 AI 回應
|
|
46
55
|
const files = this._parseResponse(response);
|
|
47
|
-
|
|
56
|
+
if (files.length === 0) {
|
|
57
|
+
console.warn(' ⚠️ AI 未回傳任何檔案(可能 JSON 格式有誤)');
|
|
58
|
+
if (process.env.AUTODEV_DEBUG) {
|
|
59
|
+
console.log(' --- AI 原始回應 ---');
|
|
60
|
+
console.log(response.slice(0, 500));
|
|
61
|
+
console.log(' (設定 AUTODEV_DEBUG=1 可看到完整回應)');
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
console.log(` ✅ AI 規劃生成 ${files.length} 個檔案:`);
|
|
65
|
+
files.forEach((f) => {
|
|
66
|
+
const icon = f.filePath.includes('.test.') || f.filePath.includes('__tests__') ? '🧪' : '📄';
|
|
67
|
+
console.log(` ${icon} ${f.filePath}`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
48
70
|
return files;
|
|
49
71
|
}
|
|
50
72
|
|
|
@@ -63,7 +85,8 @@ export class CodeGenerator {
|
|
|
63
85
|
|
|
64
86
|
for (const file of files) {
|
|
65
87
|
if (file.type === 'skip') {
|
|
66
|
-
|
|
88
|
+
console.log(` ⏭️ 跳過 ${file.filePath}${file.reason ? ` (${file.reason})` : ''}`);
|
|
89
|
+
skipped.push(`${file.filePath}${file.reason ? ` (${file.reason})` : ''}`);
|
|
67
90
|
continue;
|
|
68
91
|
}
|
|
69
92
|
|
|
@@ -76,17 +99,21 @@ export class CodeGenerator {
|
|
|
76
99
|
mkdirSync(dir, { recursive: true });
|
|
77
100
|
}
|
|
78
101
|
} catch (e) {
|
|
79
|
-
console.warn(` ⚠️ 無法建立目錄 ${dir}`);
|
|
102
|
+
console.warn(` ⚠️ 無法建立目錄 ${dir}:${e.message}`);
|
|
80
103
|
skipped.push(file.filePath);
|
|
81
104
|
continue;
|
|
82
105
|
}
|
|
83
106
|
|
|
84
|
-
//
|
|
107
|
+
// 寫入檔案(即時顯示)
|
|
85
108
|
try {
|
|
109
|
+
const isTest = file.filePath.includes('.test.') || file.filePath.includes('__tests__');
|
|
110
|
+
const icon = isTest ? '🧪' : '📄';
|
|
111
|
+
const existed = existsSync(fullPath);
|
|
86
112
|
writeFileSync(fullPath, file.content, 'utf-8');
|
|
87
113
|
written.push(file.filePath);
|
|
114
|
+
console.log(` ${icon} ${existed ? '更新' : '新建'} ${file.filePath}`);
|
|
88
115
|
} catch (e) {
|
|
89
|
-
console.warn(` ⚠️ 無法寫入 ${file.filePath}`);
|
|
116
|
+
console.warn(` ⚠️ 無法寫入 ${file.filePath}:${e.message}`);
|
|
90
117
|
skipped.push(file.filePath);
|
|
91
118
|
}
|
|
92
119
|
}
|
|
@@ -99,7 +126,7 @@ export class CodeGenerator {
|
|
|
99
126
|
_buildPrompt(issueData, projectRoot) {
|
|
100
127
|
const { title, body, labels } = issueData;
|
|
101
128
|
|
|
102
|
-
return `你是一個資深軟體工程師,負責基於 GitHub Issue
|
|
129
|
+
return `你是一個資深軟體工程師,負責基於 GitHub Issue 生成完整的代碼框架和測試用例。
|
|
103
130
|
|
|
104
131
|
## Issue 資訊
|
|
105
132
|
標題:${title}
|
|
@@ -109,21 +136,40 @@ ${body}
|
|
|
109
136
|
|
|
110
137
|
## 任務
|
|
111
138
|
1. 分析上方 Issue 的需求
|
|
112
|
-
2.
|
|
113
|
-
|
|
139
|
+
2. **同時**設計兩套檔案:
|
|
140
|
+
a. 實現檔案(src/ 中的原始碼)
|
|
141
|
+
b. 測試檔案(tests/ 或 __tests__/ 中的測試)
|
|
142
|
+
3. 生成初始實現和初始測試
|
|
143
|
+
|
|
144
|
+
## 重要:測試檔案生成
|
|
145
|
+
- 對每個實現檔案,都生成對應的測試檔案
|
|
146
|
+
- 測試檔案應放在 \`tests/\` 或 \`src/__tests__/\` 目錄
|
|
147
|
+
- 測試應覆蓋主要功能(可使用 TODO 標記待補充的測試用例)
|
|
148
|
+
- 測試檔案命名:實現檔案 \`.js\` → 測試檔案 \`.test.js\`
|
|
149
|
+
- 例:\`src/utils/helper.js\` → \`tests/utils/helper.test.js\`
|
|
114
150
|
|
|
115
151
|
## 輸出格式
|
|
116
|
-
請回傳 JSON
|
|
152
|
+
請回傳 JSON 陣列,包含**源文件和測試文件**:
|
|
117
153
|
\`\`\`json
|
|
118
154
|
[
|
|
119
155
|
{
|
|
120
|
-
"filePath": "src/components/MyComponent.
|
|
121
|
-
"content": "//
|
|
156
|
+
"filePath": "src/components/MyComponent.jsx",
|
|
157
|
+
"content": "// React 元件實現...",
|
|
158
|
+
"type": "new"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"filePath": "tests/components/MyComponent.test.jsx",
|
|
162
|
+
"content": "// Jest 測試...",
|
|
163
|
+
"type": "new"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"filePath": "src/utils/helper.js",
|
|
167
|
+
"content": "// 工具函數...",
|
|
122
168
|
"type": "new"
|
|
123
169
|
},
|
|
124
170
|
{
|
|
125
|
-
"filePath": "
|
|
126
|
-
"content": "//
|
|
171
|
+
"filePath": "tests/utils/helper.test.js",
|
|
172
|
+
"content": "// 單元測試...",
|
|
127
173
|
"type": "new"
|
|
128
174
|
}
|
|
129
175
|
]
|
|
@@ -131,11 +177,12 @@ ${body}
|
|
|
131
177
|
|
|
132
178
|
只回傳 JSON,不要其他文字。
|
|
133
179
|
|
|
134
|
-
##
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
180
|
+
## 測試框架指南(假設 Jest)
|
|
181
|
+
- \`describe('名稱', () => { ... })\` 分組
|
|
182
|
+
- \`test('應該...', () => { ... })\` 單一測試
|
|
183
|
+
- 包含 arrange-act-assert 結構
|
|
184
|
+
- 對 TODO 部分用 \`test.todo('待實現')\`
|
|
185
|
+
- 可包含 \`// TODO: 補充邊界情況\` 註釋
|
|
139
186
|
|
|
140
187
|
專案根目錄:${projectRoot}`;
|
|
141
188
|
}
|
|
@@ -196,15 +196,28 @@ export class AutodevWorkflow {
|
|
|
196
196
|
return [];
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
//
|
|
199
|
+
// 寫入檔案(每個檔案會即時顯示名稱)
|
|
200
200
|
const { written, skipped } = await this.codeGenerator.writeFiles(files, projectRoot);
|
|
201
|
-
|
|
201
|
+
|
|
202
202
|
if (skipped.length > 0) {
|
|
203
203
|
console.warn(` ⚠️ 跳過 ${skipped.length} 個檔案`);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
// 顯示 git diff --stat 讓用戶看到實際變動
|
|
207
|
+
if (written.length > 0) {
|
|
208
|
+
console.log(`\n ✅ 共寫入 ${written.length} 個檔案,git 變動摘要:`);
|
|
209
|
+
try {
|
|
210
|
+
const { execSync } = await import('child_process');
|
|
211
|
+
const stat = execSync('git diff --stat HEAD 2>/dev/null || git status --short', {
|
|
212
|
+
cwd: projectRoot,
|
|
213
|
+
encoding: 'utf-8',
|
|
214
|
+
}).trim();
|
|
215
|
+
if (stat) {
|
|
216
|
+
stat.split('\n').forEach((line) => console.log(` ${line}`));
|
|
217
|
+
}
|
|
218
|
+
} catch (_) {
|
|
219
|
+
// git diff 失敗不影響流程
|
|
220
|
+
}
|
|
208
221
|
}
|
|
209
222
|
|
|
210
223
|
return written;
|