ai-git-tools 2.0.17 → 2.0.19

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/README.md CHANGED
@@ -157,25 +157,21 @@ npx ai-git-tools ca --verbose
157
157
  npx ai-git-tools pr [選項]
158
158
 
159
159
  選項:
160
- -b, --base <branch> 目標分支
161
- -h, --head <branch> 來源分支
160
+ -b, --base <branch> 目標分支 (預設: 配置檔的 defaultBase 或自動偵測)
162
161
  -m, --model <model> 指定 AI 模型
163
- --draft 創建草稿 PR
164
162
  --preview 僅預覽,不創建 PR
165
163
  --no-confirm 跳過確認直接創建
166
- --auto-reviewers 自動選擇 reviewers
167
- --auto-labels 自動添加 Labels
168
- --no-labels 不添加 Labels
169
- --org <name> GitHub 組織名稱
164
+ --interactive-reviewers 啟用互動式 reviewer 選擇 (預設啟用)
165
+ --auto-labels 自動添加 Labels (預設啟用)
170
166
  \`\`\`
171
167
 
172
168
  **範例:**
173
169
 
174
170
  \`\`\`bash
175
171
  npx ai-git-tools pr
176
- npx ai-git-tools pr --draft
177
- npx ai-git-tools pr --base main --auto-reviewers
172
+ npx ai-git-tools pr --base release-2025-m12.1
178
173
  npx ai-git-tools pr --preview
174
+ npx ai-git-tools pr --no-confirm
179
175
  \`\`\`
180
176
 
181
177
  ### \`gitai workflow\` (別名: \`wf\`)
@@ -188,17 +184,16 @@ npx ai-git-tools workflow [選項]
188
184
  選項:
189
185
  -m, --model <model> 指定 AI 模型
190
186
  -v, --verbose 顯示詳細輸出
191
- -b, --base <branch> PR 目標分支
192
- --draft 創建草稿 PR
193
- --auto-reviewers 自動選擇 reviewers
194
- --auto-labels 自動添加 Labels
187
+ -b, --base <branch> PR 目標分支 (預設: 配置檔 defaultBase 或自動偵測)
188
+ --preview 僅預覽 PR,不創建
189
+ --auto-labels 自動添加 Labels (預設啟用)
195
190
  \`\`\`
196
191
 
197
192
  **範例:**
198
193
 
199
194
  \`\`\`bash
200
195
  npx ai-git-tools workflow
201
- npx ai-git-tools wf --draft --auto-reviewers
196
+ npx ai-git-tools wf --preview
202
197
  \`\`\`
203
198
 
204
199
  ## ⚙️ 配置
@@ -222,10 +217,10 @@ export default {
222
217
 
223
218
  // Reviewer 設定
224
219
  reviewers: {
225
- interactiveReviewers: false, // 啟用 reviewer 選擇
220
+ interactiveReviewers: true, // true=啟用互動式選擇,false=不添加任何 reviewers
226
221
  maxSuggested: 5, // 最多建議人數
227
222
  gitHistoryDepth: 20, // Git 歷史分析深度
228
- excludeAuthors: [], // 排除特定作者
223
+ excludeAuthors: [], // 排除特定作者(最高優先順序,即使手動選擇也會被過濾)
229
224
  },
230
225
 
231
226
  // 輸出設定
@@ -271,7 +266,7 @@ npx ai-git-tools commit-all
271
266
  ### 場景 3:快速發 PR
272
267
 
273
268
  \`\`\`bash
274
- npx ai-git-tools pr --auto-reviewers --auto-labels
269
+ npx ai-git-tools pr --auto-labels
275
270
  # ✅ 自動生成完整的 PR 標題和描述
276
271
  # ✅ 建議合適的 reviewers
277
272
  # ✅ 添加相關的 labels
package/bin/cli.js CHANGED
@@ -51,13 +51,10 @@ program
51
51
  .command('pr')
52
52
  .description('AI 自動生成 PR 並創建 Pull Request')
53
53
  .option('--base <branch>', '指定目標分支')
54
- .option('--head <branch>', '指定來源分支')
55
54
  .option('--model <model>', '指定 AI 模型')
56
55
  .option('--org <org-name>', '指定 GitHub 組織名稱')
57
- .option('--draft', '創建草稿 PR')
58
56
  .option('--preview', '僅預覽 PR 內容,不實際創建')
59
57
  .option('--no-confirm', '跳過確認直接創建')
60
- .option('--auto-reviewers', '自動選擇 reviewers')
61
58
  .option('--auto-labels', '自動添加 Labels')
62
59
  .option('--no-labels', '不添加 Labels')
63
60
  .action(prCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-git-tools",
3
- "version": "2.0.17",
3
+ "version": "2.0.19",
4
4
  "description": "AI-powered Git automation tools for commit messages and PR generation",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -23,8 +23,8 @@ export default {
23
23
 
24
24
  // GitHub 相關配置
25
25
  github: {
26
- orgName: '', // GitHub 組織名稱(留空則自動從 git remote 取得,或使用 'kingsinfo-project')
27
- defaultBase: 'release', // 預設 base 分支(或使用 'auto' 自動偵測最新 release 分支,如 release-2025-m11.1)
26
+ orgName: '', // GitHub 組織名稱(留空則自動從 git remote 取得)
27
+ defaultBase: 'release', // PR分支(使用 'release' 自動偵測最新 release 分支,如 release-2025-m11.1)
28
28
  autoLabels: true, // 自動新增 Labels
29
29
  },
30
30
 
@@ -22,8 +22,6 @@ export async function prCommand() {
22
22
  // 載入配置(使用 scripts/ 的配置載入邏輯)
23
23
  const config = await loadConfig();
24
24
 
25
- console.log('📋 配置:', config);
26
-
27
25
  if (config.output.verbose) {
28
26
  console.log('📋 使用配置:');
29
27
  console.log(` AI Model: ${config.ai.model}`);
@@ -119,27 +119,18 @@ export function parsePRCliArgs() {
119
119
  case '--base':
120
120
  config.baseBranch = args[++i];
121
121
  break;
122
- case '--head':
123
- config.headBranch = args[++i];
124
- break;
125
122
  case '--model':
126
123
  config.model = args[++i];
127
124
  break;
128
125
  case '--org':
129
126
  config.orgName = args[++i];
130
127
  break;
131
- case '--draft':
132
- config.draft = true;
133
- break;
134
128
  case '--preview':
135
129
  config.preview = true;
136
130
  break;
137
131
  case '--no-confirm':
138
132
  config.noConfirm = true;
139
133
  break;
140
- case '--auto-reviewers':
141
- config.autoReviewers = true;
142
- break;
143
134
  case '--auto-labels':
144
135
  config.autoLabels = true;
145
136
  break;
@@ -10,10 +10,9 @@ export function parseCliArgs() {
10
10
  baseBranch: null,
11
11
  headBranch: null,
12
12
  model: null,
13
- draft: false,
14
13
  preview: false,
15
14
  noConfirm: false,
16
- autoReviewers: false,
15
+ interactiveReviewers: undefined,
17
16
  autoLabels: null,
18
17
  };
19
18
 
@@ -28,9 +27,6 @@ export function parseCliArgs() {
28
27
  case '--model':
29
28
  config.model = args[++i];
30
29
  break;
31
- case '--draft':
32
- config.draft = true;
33
- break;
34
30
  case '--preview':
35
31
  config.preview = true;
36
32
  break;
@@ -82,14 +78,12 @@ export async function loadConfig() {
82
78
 
83
79
  // 合併配置(CLI 參數優先)
84
80
  if (cliConfig.model) config.ai.model = cliConfig.model;
85
- if (cliConfig.orgName) config.github.orgName = cliConfig.orgName;
86
- if (cliConfig.autoReviewers) config.reviewers.autoSelect = cliConfig.autoReviewers;
87
- if (cliConfig.autoLabels) config.github.autoLabels = cliConfig.autoLabels;
81
+ if (cliConfig.interactiveReviewers !== undefined) config.reviewers.interactiveReviewers = cliConfig.interactiveReviewers;
82
+ if (cliConfig.autoLabels !== null) config.github.autoLabels = cliConfig.autoLabels;
88
83
 
89
84
  // 其他 CLI 參數直接加入 config
90
85
  config.baseBranch = cliConfig.baseBranch;
91
86
  config.headBranch = cliConfig.headBranch;
92
- config.draft = cliConfig.draft;
93
87
  config.preview = cliConfig.preview;
94
88
  config.noConfirm = cliConfig.noConfirm;
95
89
 
@@ -105,19 +99,29 @@ function showHelp() {
105
99
  npx ai-git-tools pr [選項]
106
100
 
107
101
  選項:
108
- --base <branch> 指定目標分支 (預設: 自動偵測最新 release 分支)
109
- --head <branch> 指定來源分支 (預設: 當前分支)
102
+ --base <branch> 指定目標分支 (預設: 使用配置檔的 defaultBase 或自動偵測)
110
103
  --model <model> 指定 AI 模型 (預設: gpt-4.1)
111
- --draft 創建草稿 PR
112
104
  --preview 僅預覽 PR 內容,不實際創建
113
105
  --no-confirm 跳過確認直接創建
114
- --interactive-reviewers 啟用互動式 reviewer 選擇(預設啟用)
115
- --auto-labels 自動添加 Labels(預設啟用)
106
+ --interactive-reviewers 啟用互動式 reviewer 選擇 (預設啟用)
107
+ --auto-labels 自動添加 Labels (預設啟用)
116
108
  --help 顯示此說明
117
109
 
118
110
  範例:
119
111
  npx ai-git-tools pr
120
- npx ai-git-tools pr --base main --draft
121
- npx ai-git-tools pr --interactive-reviewers --auto-labels
112
+ npx ai-git-tools pr --base release-2025-m12.1
113
+ npx ai-git-tools pr --preview
114
+ npx ai-git-tools pr --no-confirm
115
+
116
+ 配置檔範例 (.ai-git-config.js):
117
+ export default {
118
+ github: {
119
+ defaultBase: 'release-2025-m12.1', // 指定預設目標分支
120
+ autoLabels: true,
121
+ },
122
+ reviewers: {
123
+ interactiveReviewers: true, // 啟用互動式選擇 reviewers
124
+ },
125
+ };
122
126
  `);
123
127
  }
@@ -7,9 +7,9 @@ import { colors } from '../utils/constants.js';
7
7
  * GitHub API 操作封裝
8
8
  */
9
9
  export class GitHubAPI {
10
- constructor(config = {}) {
11
- // 如果沒有提供 orgName 或為空字串,則自動從 git remote 偵測
12
- this.orgName = config.orgName || this.detectOrgFromRemote();
10
+ constructor() {
11
+ // 自動從 git remote 偵測組織名稱
12
+ this.orgName = this.detectOrgFromRemote();
13
13
  }
14
14
 
15
15
  /**
@@ -211,7 +211,7 @@ export class GitHubAPI {
211
211
  * 創建或更新 PR
212
212
  */
213
213
  async createOrUpdatePR(params) {
214
- const { title, body, baseBranch, headBranch, reviewers, config } = params;
214
+ const { title, body, baseBranch, headBranch, reviewers } = params;
215
215
  const bodyFile = '/tmp/pr-body.md';
216
216
  writeFileSync(bodyFile, body);
217
217
 
@@ -227,7 +227,6 @@ export class GitHubAPI {
227
227
  // PR 不存在
228
228
  }
229
229
 
230
- const draftFlag = config.draft ? '--draft' : '';
231
230
  const escapedTitle = title.replace(/"/g, '\\"');
232
231
 
233
232
  if (existingPRUrl) {
@@ -259,18 +258,14 @@ export class GitHubAPI {
259
258
  } else if (this.orgName) {
260
259
  headRef = `${this.orgName}:${headBranch}`;
261
260
  }
262
- let createCmd = `gh pr create --base "${baseBranch}" --head "${headRef}" --title "${escapedTitle}" --body-file "${bodyFile}"`;
263
-
264
- if (draftFlag) {
265
- createCmd += ` ${draftFlag}`;
266
- }
261
+ const createCmd = `gh pr create --base "${baseBranch}" --head "${headRef}" --title "${escapedTitle}" --body-file "${bodyFile}"`;
267
262
 
268
263
  try {
269
264
  const result = execSync(createCmd, {
270
265
  encoding: 'utf-8',
271
266
  stdio: ['pipe', 'pipe', 'pipe'],
272
267
  });
273
- log.success(`Pull Request 創建成功!${config.draft ? ' (草稿模式)' : ''}`);
268
+ log.success('Pull Request 創建成功!');
274
269
 
275
270
  // 提取 PR URL
276
271
  const prUrl = result
@@ -99,20 +99,28 @@ export class PRWorkflow {
99
99
 
100
100
  // 自動偵測 base branch
101
101
  if (!baseBranch) {
102
- log.step('正在偵測 release 分支...\n');
103
- const allBranches = this.git.detectReleaseBranches();
104
- const latestRelease = this.git.findLatestReleaseBranch();
105
-
106
- if (latestRelease) {
107
- baseBranch = latestRelease;
108
- this.displayDetectedBranches(allBranches, latestRelease);
109
- log.success(`自動選擇最新分支: ${baseBranch}`);
110
- log.info(`提示: 使用 --base <分支名> 指定其他分支\n`);
102
+ // 優先使用配置檔中的 defaultBase
103
+ if (this.config.github?.defaultBase) {
104
+ baseBranch = this.config.github.defaultBase;
105
+ log.success(`使用配置檔指定的分支: ${baseBranch}\n`);
111
106
  } else {
112
- throw new PRError('未偵測到任何 release 分支', 'NO_RELEASE_BRANCH', [
113
- '使用 --base 參數指定目標分支',
114
- '確認遠端分支存在: git branch -r | grep release',
115
- ]);
107
+ // 如果沒有配置,則自動偵測最新的 release 分支
108
+ log.step('正在偵測 release 分支...\n');
109
+ const allBranches = this.git.detectReleaseBranches();
110
+ const latestRelease = this.git.findLatestReleaseBranch();
111
+
112
+ if (latestRelease) {
113
+ baseBranch = latestRelease;
114
+ this.displayDetectedBranches(allBranches, latestRelease);
115
+ log.success(`自動選擇最新分支: ${baseBranch}`);
116
+ log.info(`提示: 使用 --base <分支名> 指定其他分支\n`);
117
+ } else {
118
+ throw new PRError('未偵測到任何 release 分支', 'NO_RELEASE_BRANCH', [
119
+ '使用 --base 參數指定目標分支',
120
+ '或在 .ai-git-config.js 中設置 github.defaultBase',
121
+ '確認遠端分支存在: git branch -r | grep release',
122
+ ]);
123
+ }
116
124
  }
117
125
  }
118
126
 
@@ -316,6 +324,13 @@ export class PRWorkflow {
316
324
  return null;
317
325
  }
318
326
 
327
+ // 如果禁用互動式選擇,不添加任何 reviewers
328
+ if (!this.config.reviewers.interactiveReviewers) {
329
+ console.log('ℹ️ 已禁用 reviewer 選擇(interactiveReviewers: false)\n');
330
+ log.info('提示: 你可以在創建 PR 後手動添加 reviewers\n');
331
+ return null;
332
+ }
333
+
319
334
  // 獲取當前用戶
320
335
  const currentUser = this.git.getCurrentUser();
321
336
 
@@ -324,12 +339,6 @@ export class PRWorkflow {
324
339
  changeData.changedFiles
325
340
  );
326
341
 
327
- // 如果禁用互動式選擇,直接自動選擇
328
- if (!this.config.reviewers.interactiveReviewers) {
329
- console.log('🤖 自動選擇 reviewers(基於 Git 歷史)\n');
330
- return this.reviewerSelector.autoSelectReviewers(suggestedReviewers, currentUser);
331
- }
332
-
333
342
  // 抓取 GitHub 團隊資訊(只在互動模式需要)
334
343
  const teams = await this.github.fetchTeams();
335
344
 
@@ -178,16 +178,30 @@ export class ReviewerSelector {
178
178
  }
179
179
  });
180
180
 
181
+ // 應用 excludeAuthors 過濾(優先順序最高)
182
+ const filteredIndividuals = selectedIndividuals.filter((login) => {
183
+ const shouldExclude = this.excludeAuthors.some((excluded) => {
184
+ const normalizedExcluded = excluded.toLowerCase();
185
+ return login.toLowerCase().includes(normalizedExcluded);
186
+ });
187
+
188
+ if (shouldExclude) {
189
+ log.warning(`已排除 @${login}(在 excludeAuthors 列表中)`);
190
+ }
191
+
192
+ return !shouldExclude;
193
+ });
194
+
181
195
  console.log('');
182
196
  if (selectedTeams.length > 0) {
183
197
  log.success(`已選擇團隊: ${selectedTeams.join(', ')}`);
184
198
  }
185
- if (selectedIndividuals.length > 0) {
186
- log.success(`已選擇個人: ${selectedIndividuals.map((u) => `@${u}`).join(', ')}`);
199
+ if (filteredIndividuals.length > 0) {
200
+ log.success(`已選擇個人: ${filteredIndividuals.map((u) => `@${u}`).join(', ')}`);
187
201
  }
188
202
  console.log('');
189
203
 
190
- return { teams: selectedTeams, individuals: selectedIndividuals };
204
+ return { teams: selectedTeams, individuals: filteredIndividuals };
191
205
  }
192
206
 
193
207
  /**
@@ -217,16 +231,10 @@ export class ReviewerSelector {
217
231
  }
218
232
 
219
233
  /**
220
- * 選擇 Reviewers(根據模式)
234
+ * 選擇 Reviewers(互動模式)
221
235
  */
222
236
  async select(teamsData, suggestedReviewers, currentUser) {
223
- if (this.interactiveReviewers) {
224
- // interactiveReviewers: true 表示啟用互動式選擇(手動選擇)
225
- return this.selectInteractive(teamsData, suggestedReviewers, currentUser);
226
- } else {
227
- // interactiveReviewers: false 表示自動選擇(基於 Git 歷史)
228
- console.log('🤖 自動選擇 reviewers(基於 Git 歷史)\n');
229
- return this.autoSelectReviewers(suggestedReviewers, currentUser);
230
- }
237
+ // 此方法現在只在 interactiveReviewers: true 時被調用
238
+ return this.selectInteractive(teamsData, suggestedReviewers, currentUser);
231
239
  }
232
240
  }