ai-git-tools 1.0.3 → 1.0.5

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
@@ -19,7 +19,7 @@ const program = new Command();
19
19
  program
20
20
  .name('ai-git-tools')
21
21
  .description('AI-powered Git automation tools')
22
- .version('1.0.2');
22
+ .version('1.0.5');
23
23
 
24
24
  // Init 命令
25
25
  program
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-git-tools",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import chalk from 'chalk';
8
+ import { existsSync } from 'fs';
8
9
  import { loadConfig } from '../core/config-loader.js';
9
10
  import { AIClient } from '../core/ai-client.js';
10
11
  import { GitOperations } from '../core/git-operations.js';
@@ -42,6 +43,11 @@ async function analyzeAndGroupChanges(changes, config, logger) {
42
43
 
43
44
  請分析以下的檔案變更,並將它們按照功能/目的分組。
44
45
 
46
+ **重要提醒**:
47
+ - 每個檔案都有一個 [檔案 X] 的標記,X 是索引號碼
48
+ - 你**必須**使用這些索引號碼,**不要**自行推測或修改檔案名稱
49
+ - file_indices 中只能包含上面提供的有效索引 (0 到 ${changes.length - 1})
50
+
45
51
  **分組規則**:
46
52
  1. 將相關功能的變更歸類在同一組(例如:同一個功能開發、同一個 bug 修復、相關的重構等)
47
53
  2. 每組應該要有明確的主題
@@ -51,7 +57,7 @@ async function analyzeAndGroupChanges(changes, config, logger) {
51
57
  - group_name: 群組名稱(簡短描述,繁體中文)
52
58
  - commit_type: commit 類型(feat/fix/docs/style/refactor/test/chore/perf)
53
59
  - commit_scope: commit 影響範圍(如 api、ui、config、auth 等,選填)
54
- - file_indices: 屬於這組的檔案索引陣列(對應上面的 [檔案 X]
60
+ - file_indices: 屬於這組的檔案索引陣列(**只能使用上面 [檔案 X] 的 X 值**)
55
61
  - description: 這組變更的詳細說明(繁體中文)
56
62
 
57
63
  範例輸出:
@@ -149,16 +155,25 @@ async function commitGroup(group, files, config, logger) {
149
155
  GitOperations.resetStaged();
150
156
 
151
157
  // Add 這組的檔案
158
+ const addedFiles = [];
152
159
  for (const file of files) {
153
160
  console.log(` ├─ ${file.filePath}`);
154
161
  try {
155
162
  GitOperations.addFile(file.filePath);
163
+ addedFiles.push(file.filePath);
156
164
  } catch (error) {
157
165
  logger.warn(`無法 add 檔案 ${file.filePath}: ${error.message}`);
158
- throw error;
166
+ // 繼續處理其他檔案,不要中斷
167
+ continue;
159
168
  }
160
169
  }
161
170
 
171
+ // 如果沒有成功 add 任何檔案,跳過這個群組
172
+ if (addedFiles.length === 0) {
173
+ logger.warn('沒有檔案被成功 add,跳過此群組');
174
+ return false;
175
+ }
176
+
162
177
  // 生成 commit message
163
178
  console.log(` └─ 生成 commit message...`);
164
179
  const commitMessage = await generateCommitMessage(group, files, config, logger);
@@ -245,9 +260,35 @@ export async function commitAllCommand(options) {
245
260
  let successCount = 0;
246
261
  for (let i = 0; i < groups.length; i++) {
247
262
  const group = groups[i];
248
- const groupFiles = group.file_indices.map(index => changes[index]);
263
+
264
+ // 驗證並過濾有效的檔案索引
265
+ const validFiles = group.file_indices
266
+ .filter(index => {
267
+ if (typeof index !== 'number' || index < 0 || index >= changes.length) {
268
+ logger.warn(`群組 "${group.group_name}" 包含無效的檔案索引: ${index} (有效範圍: 0-${changes.length - 1})`);
269
+ return false;
270
+ }
271
+ return true;
272
+ })
273
+ .map(index => changes[index])
274
+ .filter(file => {
275
+ if (!file || !file.filePath) {
276
+ logger.warn(`檔案資訊不完整,已跳過`);
277
+ return false;
278
+ }
279
+ if (!existsSync(file.filePath)) {
280
+ logger.warn(`檔案不存在:${file.filePath},已跳過`);
281
+ return false;
282
+ }
283
+ return true;
284
+ });
285
+
286
+ if (validFiles.length === 0) {
287
+ logger.warn(`群組 ${i + 1} 沒有有效的檔案,跳過`);
288
+ continue;
289
+ }
249
290
 
250
- const success = await commitGroup(group, groupFiles, config, logger);
291
+ const success = await commitGroup(group, validFiles, config, logger);
251
292
  if (success) {
252
293
  successCount++;
253
294
  }
@@ -13,7 +13,6 @@ import { GitHubAPI } from '../core/github-api.js';
13
13
  import { Logger } from '../utils/logger.js';
14
14
  import {
15
15
  handleError,
16
- truncateText,
17
16
  getProjectTypePrompt,
18
17
  } from '../utils/helpers.js';
19
18
 
@@ -50,7 +49,8 @@ async function generatePRContent(baseBranch, headBranch, config, logger) {
50
49
  const diff = GitOperations.getDiffBetweenBranches(baseBranch, headBranch);
51
50
  const commits = GitOperations.getCommitsBetweenBranches(baseBranch, headBranch);
52
51
 
53
- const truncatedDiff = truncateText(diff, config.ai.maxDiffLength);
52
+ // 使用智能截斷,保留前後各 50
53
+ const truncatedDiff = GitOperations.truncateDiff(diff, config.ai.maxDiffLength);
54
54
 
55
55
  const aiClient = new AIClient(config);
56
56
 
@@ -16,6 +16,7 @@ export class GitOperations {
16
16
  const result = execSync(command, {
17
17
  encoding: 'utf-8',
18
18
  stdio: options.silent ? 'pipe' : 'inherit',
19
+ maxBuffer: 50 * 1024 * 1024, // 50MB buffer 避免 ENOBUFS 錯誤
19
20
  ...options,
20
21
  });
21
22
  return result ? result.toString().trim() : '';
@@ -154,6 +155,23 @@ export class GitOperations {
154
155
  * Add 檔案
155
156
  */
156
157
  static addFile(filePath) {
158
+ // 檢查檔案是否存在(對於新檔案和已修改檔案)
159
+ if (!existsSync(filePath)) {
160
+ // 可能是 git 追蹤的檔案但在工作目錄中不存在
161
+ // 嘗試使用 git ls-files 確認
162
+ try {
163
+ const tracked = GitOperations.exec(`git ls-files "${filePath}"`, {
164
+ silent: true,
165
+ throwOnError: false
166
+ });
167
+ if (!tracked || !tracked.trim()) {
168
+ throw new Error(`檔案不存在: ${filePath}`);
169
+ }
170
+ } catch (error) {
171
+ throw new Error(`檔案不存在或無法追蹤: ${filePath}`);
172
+ }
173
+ }
174
+
157
175
  GitOperations.exec(`git add "${filePath}"`);
158
176
  }
159
177
 
@@ -215,7 +233,37 @@ export class GitOperations {
215
233
  * 獲取兩個分支之間的 diff
216
234
  */
217
235
  static getDiffBetweenBranches(base, head) {
218
- return GitOperations.exec(`git diff ${base}...${head}`, { silent: true });
236
+ try {
237
+ return GitOperations.exec(`git diff ${base}...${head}`, { silent: true });
238
+ } catch (error) {
239
+ // 嘗試替代方案
240
+ try {
241
+ return GitOperations.exec(`git diff ${base}..${head}`, { silent: true });
242
+ } catch (fallbackError) {
243
+ throw new Error(`無法獲取分支差異: ${error.message}`);
244
+ }
245
+ }
246
+ }
247
+
248
+ /**
249
+ * 智能截斷 diff
250
+ * 保留前後各 50 行,中間用省略標記
251
+ */
252
+ static truncateDiff(diff, maxLength = 8000) {
253
+ if (diff.length <= maxLength) return diff;
254
+
255
+ const lines = diff.split('\n');
256
+ const contextLines = 50;
257
+
258
+ if (lines.length <= contextLines * 2) {
259
+ return diff;
260
+ }
261
+
262
+ const header = lines.slice(0, contextLines).join('\n');
263
+ const footer = lines.slice(-contextLines).join('\n');
264
+ const omittedLines = lines.length - (contextLines * 2);
265
+
266
+ return `${header}\n\n... [已省略 ${omittedLines} 行變更] ...\n\n${footer}`;
219
267
  }
220
268
 
221
269
  /**