ai-git-tools 2.0.47 → 2.0.48

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-git-tools",
3
- "version": "2.0.47",
3
+ "version": "2.0.48",
4
4
  "description": "AI-powered Git automation tools for commit messages and PR generation",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -40,6 +40,44 @@ export default {
40
40
  output: {
41
41
  verbose: true, // 詳細輸出
42
42
  },
43
+
44
+ // AutoDev 自動開發流程配置
45
+ // 使用方式:ai autodev <issue編號>
46
+ autodev: {
47
+ // AI 模型(可獨立於 ai.model 單獨設定,建議使用快速模型)
48
+ // 支援:'gpt-4.1'、'gpt-4o'、'claude-haiku-4.5'、'claude-sonnet-4.5'
49
+ // 注意:claude 系列使用 extended thinking,回應較慢(約 5 分鐘)
50
+ aiModel: 'gpt-4.1',
51
+
52
+ // 重試次數上限(AI 呼叫失敗時)
53
+ maxRetries: 3,
54
+
55
+ // 測試框架(null = 自動偵測,或指定 'jest' / 'vitest' / 'mocha')
56
+ framework: null,
57
+
58
+ // 測試檔案搜尋路徑(glob pattern),留空使用框架預設規則
59
+ testPaths: [
60
+ 'tests/**/*.test.js',
61
+ 'tests/**/*.spec.js',
62
+ 'src/**/__tests__/**/*.js',
63
+ ],
64
+
65
+ // 測試執行 timeout(毫秒),預設 60 秒
66
+ testTimeout: 60000,
67
+
68
+ // 目標專案根目錄(null = 當前工作目錄)
69
+ projectRoot: null,
70
+
71
+ // 無測試可執行時,是否仍繼續 commit/PR(預設 false = 停止)
72
+ // 等同 CLI 的 --skip-tests 旗標
73
+ skipTests: false,
74
+
75
+ // 跳過自動 commit(預設 false)
76
+ skipCommit: false,
77
+
78
+ // 跳過自動建立 PR(預設 false)
79
+ skipPr: false,
80
+ },
43
81
  };
44
82
  `;
45
83
 
@@ -191,9 +191,10 @@ export async function loadAutodevConfig() {
191
191
  const defaults = {
192
192
  framework: null, // null = 自動偵測
193
193
  testPaths: [], // 空 = 自動發現
194
- testTimeout: 300_000, // 5 分鐘
194
+ testTimeout: 60_000, // 1 分鐘
195
195
  skipCommit: false,
196
196
  skipPr: false,
197
+ skipTests: false,
197
198
  verbose: false,
198
199
  projectRoot: process.cwd(),
199
200
  // AI 設定(可被 autodev 子區塊或全域 ai 區塊覆蓋)
@@ -225,10 +226,9 @@ export async function loadAutodevConfig() {
225
226
 
226
227
  return {
227
228
  ...defaults,
228
- // 全域 ai 區塊優先度低於 autodev 子區塊
229
+ ...userAutodev,
230
+ // 這兩個需要跨區塊計算,放在展開之後才能保有正確優先序
229
231
  aiModel: userAutodev.aiModel ?? userAi.model ?? defaults.aiModel,
230
232
  maxRetries: userAutodev.maxRetries ?? userAi.maxRetries ?? defaults.maxRetries,
231
- // 其他 autodev 欄位
232
- ...userAutodev,
233
233
  };
234
234
  }
@@ -80,11 +80,6 @@ export class CodeGenerator {
80
80
  const files = this._parseResponse(response);
81
81
  if (files.length === 0) {
82
82
  console.warn(' ⚠️ AI 未回傳任何檔案(可能 JSON 格式有誤)');
83
- if (process.env.AUTODEV_DEBUG) {
84
- console.log(' --- AI 原始回應 ---');
85
- console.log(response.slice(0, 500));
86
- console.log(' (設定 AUTODEV_DEBUG=1 可看到完整回應)');
87
- }
88
83
  } else {
89
84
  console.log(` ✅ AI 規劃生成 ${files.length} 個檔案:`);
90
85
  files.forEach(f => {
@@ -215,28 +210,79 @@ ${body}
215
210
 
216
211
  _parseResponse(response) {
217
212
  try {
218
- // JSON 陣列
219
- const match = response.match(/\[[\s\S]*\]/);
220
- if (!match) {
213
+ // 1. 先去掉 markdown code fence(```json ... ``` 或 ``` ... ```)
214
+ const stripped = response
215
+ .replace(/```json\s*/gi, '')
216
+ .replace(/```\s*/g, '')
217
+ .trim();
218
+
219
+ // 2. 嘗試直接 parse 整個回應(AI 可能真的只回傳 JSON)
220
+ try {
221
+ const direct = JSON.parse(stripped);
222
+ if (Array.isArray(direct)) return this._normalizeFiles(direct);
223
+ } catch (_) {
224
+ // 繼續下一步
225
+ }
226
+
227
+ // 3. 用括號計數找出最外層完整 JSON 陣列(比貪婪 regex 更可靠)
228
+ const start = stripped.indexOf('[');
229
+ if (start === -1) {
221
230
  console.warn(' ⚠️ 無法從 AI 回應找到 JSON 陣列');
231
+ if (process.env.AUTODEV_DEBUG) {
232
+ console.log(' --- AI 原始回應(前 500 字)---');
233
+ console.log(response.slice(0, 500));
234
+ }
235
+ return [];
236
+ }
237
+
238
+ let depth = 0;
239
+ let inString = false;
240
+ let escape = false;
241
+ let end = -1;
242
+
243
+ for (let i = start; i < stripped.length; i++) {
244
+ const ch = stripped[i];
245
+ if (escape) { escape = false; continue; }
246
+ if (ch === '\\' && inString) { escape = true; continue; }
247
+ if (ch === '"') { inString = !inString; continue; }
248
+ if (inString) continue;
249
+ if (ch === '[') depth++;
250
+ else if (ch === ']') {
251
+ depth--;
252
+ if (depth === 0) { end = i; break; }
253
+ }
254
+ }
255
+
256
+ if (end === -1) {
257
+ console.warn(' ⚠️ AI 回應中的 JSON 陣列括號不匹配');
222
258
  return [];
223
259
  }
224
260
 
225
- const files = JSON.parse(match[0]);
261
+ const jsonStr = stripped.slice(start, end + 1);
262
+ const files = JSON.parse(jsonStr);
263
+
226
264
  if (!Array.isArray(files)) {
227
265
  console.warn(' ⚠️ AI 回應不是陣列格式');
228
266
  return [];
229
267
  }
230
268
 
231
- return files.map(f => ({
232
- filePath: f.filePath || '',
233
- content: f.content || '',
234
- type: f.type || 'new',
235
- reason: f.reason || null,
236
- }));
269
+ return this._normalizeFiles(files);
237
270
  } catch (error) {
238
271
  console.warn(` ⚠️ 解析 AI 回應失敗:${error.message}`);
272
+ if (process.env.AUTODEV_DEBUG) {
273
+ console.log(' --- AI 原始回應(前 800 字)---');
274
+ console.log(response.slice(0, 800));
275
+ }
276
+ console.warn(' 💡 設定環境變數 AUTODEV_DEBUG=1 可查看 AI 完整回應內容');
239
277
  return [];
240
278
  }
241
279
  }
242
- }
280
+
281
+ _normalizeFiles(files) {
282
+ return files.map(f => ({
283
+ filePath: f.filePath || '',
284
+ content: f.content || '',
285
+ type: f.type || 'new',
286
+ reason: f.reason || null,
287
+ }));
288
+ }}
@@ -133,12 +133,19 @@ export class AutodevWorkflow {
133
133
  if (options.skipTests) {
134
134
  console.warn(' ⚠️ 無測試可執行(--skip-tests 已啟用,繼續後續流程)');
135
135
  } else {
136
+ const projectRoot = this.config.projectRoot || process.cwd();
136
137
  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:');
138
+ console.log('\n 最常見原因:測試檔案引用的套件尚未安裝');
139
+ console.log(` 👉 請先在目標專案目錄安裝依賴:`);
140
+ console.log(` cd ${projectRoot}`);
141
+ console.log(` npm install`);
142
+ console.log(` npx jest --listTests # 確認測試檔案是否被偵測到`);
143
+ console.log('\n 其他可能原因:');
144
+ console.log(' 2. 生成的測試語法有誤(Jest 無法解析)');
145
+ console.log(' 3. testPaths 設定未涵蓋新生成的測試路徑');
146
+ console.log('\n 安裝依賴並確認後,重新執行:');
147
+ console.log(' ai autodev <issue>');
148
+ console.log('\n 若確定要跳過測試直接 commit/PR:');
142
149
  console.log(' ai autodev <issue> --skip-tests');
143
150
  return;
144
151
  }