@umsai/ums-code 0.5.0-v1 → 0.6.0-v1

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.
Files changed (194) hide show
  1. package/dist/package.json +4 -4
  2. package/dist/src/acp-integration/acp.d.ts +6 -0
  3. package/dist/src/acp-integration/acp.js +7 -0
  4. package/dist/src/acp-integration/acp.js.map +1 -1
  5. package/dist/src/acp-integration/acpAgent.js +33 -7
  6. package/dist/src/acp-integration/acpAgent.js.map +1 -1
  7. package/dist/src/acp-integration/schema.d.ts +1235 -892
  8. package/dist/src/acp-integration/schema.js +19 -0
  9. package/dist/src/acp-integration/schema.js.map +1 -1
  10. package/dist/src/acp-integration/session/Session.d.ts +0 -5
  11. package/dist/src/acp-integration/session/Session.js +94 -15
  12. package/dist/src/acp-integration/session/Session.js.map +1 -1
  13. package/dist/src/commandMode.d.ts +11 -0
  14. package/dist/src/commandMode.js +238 -0
  15. package/dist/src/commandMode.js.map +1 -0
  16. package/dist/src/config/auth.js +26 -0
  17. package/dist/src/config/auth.js.map +1 -1
  18. package/dist/src/config/config.d.ts +8 -0
  19. package/dist/src/config/config.js +96 -21
  20. package/dist/src/config/config.js.map +1 -1
  21. package/dist/src/config/extensions/update.test.js +9 -0
  22. package/dist/src/config/extensions/update.test.js.map +1 -1
  23. package/dist/src/config/settingsSchema.d.ts +41 -2
  24. package/dist/src/config/settingsSchema.js +36 -2
  25. package/dist/src/config/settingsSchema.js.map +1 -1
  26. package/dist/src/core/initializer.js +3 -0
  27. package/dist/src/core/initializer.js.map +1 -1
  28. package/dist/src/gemini.js +39 -19
  29. package/dist/src/gemini.js.map +1 -1
  30. package/dist/src/gemini.test.js +30 -4
  31. package/dist/src/gemini.test.js.map +1 -1
  32. package/dist/src/generated/git-commit.d.ts +3 -3
  33. package/dist/src/generated/git-commit.js +3 -3
  34. package/dist/src/i18n/index.d.ts +10 -2
  35. package/dist/src/i18n/index.js +22 -1
  36. package/dist/src/i18n/index.js.map +1 -1
  37. package/dist/src/i18n/languages.d.ts +20 -0
  38. package/dist/src/i18n/languages.js +36 -0
  39. package/dist/src/i18n/languages.js.map +1 -0
  40. package/dist/src/i18n/locales/de.js +1073 -0
  41. package/dist/src/i18n/locales/en.js +120 -162
  42. package/dist/src/i18n/locales/ru.js +125 -151
  43. package/dist/src/i18n/locales/zh.js +51 -164
  44. package/dist/src/nonInteractive/control/controllers/systemController.d.ts +1 -1
  45. package/dist/src/nonInteractive/control/controllers/systemController.js +5 -10
  46. package/dist/src/nonInteractive/control/controllers/systemController.js.map +1 -1
  47. package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js +11 -4
  48. package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js.map +1 -1
  49. package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.test.js +40 -0
  50. package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.test.js.map +1 -1
  51. package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.test.js +50 -0
  52. package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.test.js.map +1 -1
  53. package/dist/src/nonInteractiveCli.js +104 -18
  54. package/dist/src/nonInteractiveCli.js.map +1 -1
  55. package/dist/src/nonInteractiveCliCommands.d.ts +48 -9
  56. package/dist/src/nonInteractiveCliCommands.js +180 -64
  57. package/dist/src/nonInteractiveCliCommands.js.map +1 -1
  58. package/dist/src/{ui/hooks/useQuotaAndFallback.test.d.ts → nonInteractiveCliCommands.test.d.ts} +1 -1
  59. package/dist/src/nonInteractiveCliCommands.test.js +157 -0
  60. package/dist/src/nonInteractiveCliCommands.test.js.map +1 -0
  61. package/dist/src/services/BuiltinCommandLoader.js +2 -0
  62. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  63. package/dist/src/services/McpPromptLoader.js +4 -2
  64. package/dist/src/services/McpPromptLoader.js.map +1 -1
  65. package/dist/src/services/McpPromptLoader.test.js +1 -1
  66. package/dist/src/services/McpPromptLoader.test.js.map +1 -1
  67. package/dist/src/ui/AppContainer.js +23 -20
  68. package/dist/src/ui/AppContainer.js.map +1 -1
  69. package/dist/src/ui/AppContainer.test.js +0 -46
  70. package/dist/src/ui/AppContainer.test.js.map +1 -1
  71. package/dist/src/ui/auth/AuthDialog.test.js +4 -4
  72. package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
  73. package/dist/src/ui/auth/useAuth.js +11 -3
  74. package/dist/src/ui/auth/useAuth.js.map +1 -1
  75. package/dist/src/ui/commands/approvalModeCommand.js +53 -4
  76. package/dist/src/ui/commands/approvalModeCommand.js.map +1 -1
  77. package/dist/src/ui/commands/approvalModeCommand.test.js +72 -10
  78. package/dist/src/ui/commands/approvalModeCommand.test.js.map +1 -1
  79. package/dist/src/ui/commands/compressCommand.js +86 -16
  80. package/dist/src/ui/commands/compressCommand.js.map +1 -1
  81. package/dist/src/ui/commands/languageCommand.d.ts +6 -1
  82. package/dist/src/ui/commands/languageCommand.js +162 -216
  83. package/dist/src/ui/commands/languageCommand.js.map +1 -1
  84. package/dist/src/ui/commands/languageCommand.test.js +105 -10
  85. package/dist/src/ui/commands/languageCommand.test.js.map +1 -1
  86. package/dist/src/ui/commands/restoreCommand.js +1 -1
  87. package/dist/src/ui/commands/restoreCommand.js.map +1 -1
  88. package/dist/src/ui/commands/resumeCommand.d.ts +7 -0
  89. package/dist/src/ui/commands/resumeCommand.js +19 -0
  90. package/dist/src/ui/commands/resumeCommand.js.map +1 -0
  91. package/dist/src/ui/{components/ProQuotaDialog.test.d.ts → commands/resumeCommand.test.d.ts} +1 -1
  92. package/dist/src/ui/commands/resumeCommand.test.js +32 -0
  93. package/dist/src/ui/commands/resumeCommand.test.js.map +1 -0
  94. package/dist/src/ui/commands/reviewCommand.js +871 -92
  95. package/dist/src/ui/commands/reviewCommand.js.map +1 -1
  96. package/dist/src/ui/commands/reviewCommand.test.js +55 -0
  97. package/dist/src/ui/commands/reviewCommand.test.js.map +1 -1
  98. package/dist/src/ui/commands/summaryCommand.js +129 -42
  99. package/dist/src/ui/commands/summaryCommand.js.map +1 -1
  100. package/dist/src/ui/commands/types.d.ts +21 -2
  101. package/dist/src/ui/commands/types.js.map +1 -1
  102. package/dist/src/ui/components/DialogManager.js +4 -4
  103. package/dist/src/ui/components/DialogManager.js.map +1 -1
  104. package/dist/src/ui/components/HistoryItemDisplay.test.js +5 -2
  105. package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
  106. package/dist/src/ui/components/PermissionsModifyTrustDialog.js +1 -1
  107. package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -1
  108. package/dist/src/ui/components/SessionPicker.d.ts +18 -0
  109. package/dist/src/ui/components/SessionPicker.js +69 -0
  110. package/dist/src/ui/components/SessionPicker.js.map +1 -0
  111. package/dist/src/ui/components/SessionSummaryDisplay.js +6 -2
  112. package/dist/src/ui/components/SessionSummaryDisplay.js.map +1 -1
  113. package/dist/src/ui/components/{ResumeSessionPicker.d.ts → StandaloneSessionPicker.d.ts} +1 -1
  114. package/dist/src/ui/components/StandaloneSessionPicker.js +78 -0
  115. package/dist/src/ui/components/StandaloneSessionPicker.js.map +1 -0
  116. package/dist/src/ui/components/StandaloneSessionPicker.test.d.ts +6 -0
  117. package/dist/src/ui/components/StandaloneSessionPicker.test.js +410 -0
  118. package/dist/src/ui/components/StandaloneSessionPicker.test.js.map +1 -0
  119. package/dist/src/ui/components/ums/UMSKeyPrompt.js +1 -1
  120. package/dist/src/ui/components/ums/UMSKeyPrompt.js.map +1 -1
  121. package/dist/src/ui/contexts/UIActionsContext.d.ts +3 -1
  122. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  123. package/dist/src/ui/contexts/UIStateContext.d.ts +2 -8
  124. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  125. package/dist/src/ui/hooks/slashCommandProcessor.d.ts +1 -0
  126. package/dist/src/ui/hooks/slashCommandProcessor.js +9 -0
  127. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  128. package/dist/src/ui/hooks/useGeminiStream.js +46 -11
  129. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  130. package/dist/src/ui/hooks/useLoadingIndicator.test.js +11 -6
  131. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  132. package/dist/src/ui/hooks/usePhraseCycler.js +12 -136
  133. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  134. package/dist/src/ui/hooks/useQwenAuth.test.js +1 -1
  135. package/dist/src/ui/hooks/useQwenAuth.test.js.map +1 -1
  136. package/dist/src/ui/hooks/useResumeCommand.d.ts +20 -0
  137. package/dist/src/ui/hooks/useResumeCommand.js +49 -0
  138. package/dist/src/ui/hooks/useResumeCommand.js.map +1 -0
  139. package/dist/src/ui/hooks/useResumeCommand.test.d.ts +6 -0
  140. package/dist/src/ui/hooks/useResumeCommand.test.js +144 -0
  141. package/dist/src/ui/hooks/useResumeCommand.test.js.map +1 -0
  142. package/dist/src/ui/hooks/useSessionPicker.d.ts +36 -0
  143. package/dist/src/ui/hooks/useSessionPicker.js +189 -0
  144. package/dist/src/ui/hooks/useSessionPicker.js.map +1 -0
  145. package/dist/src/ui/hooks/useToolScheduler.test.js +1 -1
  146. package/dist/src/ui/models/availableModels.d.ts +1 -0
  147. package/dist/src/ui/models/availableModels.js +8 -0
  148. package/dist/src/ui/models/availableModels.js.map +1 -1
  149. package/dist/src/ui/utils/resumeHistoryUtils.js +5 -1
  150. package/dist/src/ui/utils/resumeHistoryUtils.js.map +1 -1
  151. package/dist/src/ui/utils/resumeHistoryUtils.test.js +5 -0
  152. package/dist/src/ui/utils/resumeHistoryUtils.test.js.map +1 -1
  153. package/dist/src/ui/utils/sessionPickerUtils.d.ts +30 -0
  154. package/dist/src/ui/utils/sessionPickerUtils.js +41 -0
  155. package/dist/src/ui/utils/sessionPickerUtils.js.map +1 -0
  156. package/dist/src/ui/utils/sessionPickerUtils.test.d.ts +6 -0
  157. package/dist/src/ui/utils/sessionPickerUtils.test.js +38 -0
  158. package/dist/src/ui/utils/sessionPickerUtils.test.js.map +1 -0
  159. package/dist/src/utils/errors.d.ts +1 -1
  160. package/dist/src/utils/errors.js +14 -3
  161. package/dist/src/utils/errors.js.map +1 -1
  162. package/dist/src/utils/errors.test.js +43 -1
  163. package/dist/src/utils/errors.test.js.map +1 -1
  164. package/dist/src/utils/gitUtils.js +22 -5
  165. package/dist/src/utils/gitUtils.js.map +1 -1
  166. package/dist/src/utils/gitUtils.test.js +59 -0
  167. package/dist/src/utils/gitUtils.test.js.map +1 -1
  168. package/dist/src/utils/nonInteractiveHelpers.d.ts +3 -1
  169. package/dist/src/utils/nonInteractiveHelpers.js +13 -14
  170. package/dist/src/utils/nonInteractiveHelpers.js.map +1 -1
  171. package/dist/src/utils/nonInteractiveHelpers.test.js +35 -20
  172. package/dist/src/utils/nonInteractiveHelpers.test.js.map +1 -1
  173. package/dist/src/utils/relaunch.js +2 -2
  174. package/dist/src/utils/relaunch.js.map +1 -1
  175. package/dist/src/utils/relaunch.test.js +5 -5
  176. package/dist/src/utils/relaunch.test.js.map +1 -1
  177. package/dist/src/utils/systemInfo.js +2 -1
  178. package/dist/src/utils/systemInfo.js.map +1 -1
  179. package/dist/src/validateNonInterActiveAuth.js +9 -0
  180. package/dist/src/validateNonInterActiveAuth.js.map +1 -1
  181. package/dist/tsconfig.tsbuildinfo +1 -1
  182. package/package.json +5 -5
  183. package/dist/src/ui/components/ProQuotaDialog.d.ts +0 -13
  184. package/dist/src/ui/components/ProQuotaDialog.js +0 -24
  185. package/dist/src/ui/components/ProQuotaDialog.js.map +0 -1
  186. package/dist/src/ui/components/ProQuotaDialog.test.js +0 -58
  187. package/dist/src/ui/components/ProQuotaDialog.test.js.map +0 -1
  188. package/dist/src/ui/components/ResumeSessionPicker.js +0 -249
  189. package/dist/src/ui/components/ResumeSessionPicker.js.map +0 -1
  190. package/dist/src/ui/hooks/useQuotaAndFallback.d.ts +0 -21
  191. package/dist/src/ui/hooks/useQuotaAndFallback.js +0 -122
  192. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +0 -1
  193. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +0 -269
  194. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +0 -1
@@ -65,10 +65,29 @@ class GitUtils {
65
65
  return `${branch1}${separator}${branch2}`;
66
66
  }
67
67
  return `main${separator}${branch1}`;
68
+ case 'commits':
69
+ // commits 模式不使用此方法,每个文件单独构建命令
70
+ throw new ReviewCommandError('Commits mode should not use buildComparisonString');
68
71
  default:
69
72
  throw new ReviewCommandError(`Unknown review mode: ${mode}`);
70
73
  }
71
74
  }
75
+ /**
76
+ * 构建单个文件的 git diff 命令(用于 commits 模式)
77
+ */
78
+ static buildFileDiffCommand(fileChange) {
79
+ if (fileChange.commit) {
80
+ // 单个 commit
81
+ return `git diff ${fileChange.commit}^ ${fileChange.commit} -- ${fileChange.filePath}`;
82
+ }
83
+ else if (fileChange.commit1 && fileChange.commit2) {
84
+ // 两个 commit
85
+ return `git diff ${fileChange.commit1}^ ${fileChange.commit2} -- ${fileChange.filePath}`;
86
+ }
87
+ else {
88
+ throw new ReviewCommandError('Invalid file change: missing commit information');
89
+ }
90
+ }
72
91
  /**
73
92
  * 构建 git diff --stat 命令
74
93
  */
@@ -123,6 +142,8 @@ class ReviewArgumentParser {
123
142
  return { ...this.parseCommitMode(filteredParts), ...enhanceOptions, ...noticeOption };
124
143
  case '-branch':
125
144
  return { ...this.parseBranchMode(filteredParts), ...enhanceOptions, ...noticeOption };
145
+ case '-commits':
146
+ return { ...this.parseCommitsMode(filteredParts), ...enhanceOptions, ...noticeOption };
126
147
  default:
127
148
  return { ...this.handleUnknownCommand(filteredParts), ...enhanceOptions, ...noticeOption };
128
149
  }
@@ -138,12 +159,6 @@ class ReviewArgumentParser {
138
159
  }
139
160
  return {};
140
161
  }
141
- /**
142
- * 提取 --notice 参数
143
- */
144
- static extractNoticeOption(parts) {
145
- return { notice: parts.includes('--notice') };
146
- }
147
162
  /**
148
163
  * 移除所有选项参数(--prompt-enhance 和 --notice)
149
164
  */
@@ -162,6 +177,12 @@ class ReviewArgumentParser {
162
177
  }
163
178
  return filtered;
164
179
  }
180
+ /**
181
+ * 提取 --notice 参数
182
+ */
183
+ static extractNoticeOption(parts) {
184
+ return { notice: parts.includes('--notice') };
185
+ }
165
186
  /**
166
187
  * 解析 commit 模式参数
167
188
  */
@@ -188,6 +209,24 @@ class ReviewArgumentParser {
188
209
  branch2: parts[2],
189
210
  };
190
211
  }
212
+ /**
213
+ * 解析 commits 模式参数
214
+ */
215
+ static parseCommitsMode(parts) {
216
+ if (parts.length < 2) {
217
+ throw new ReviewCommandError('Commits mode requires commit hashes', 'MISSING_COMMITS');
218
+ }
219
+ // 解析逗号分隔的 commit 列表
220
+ const commitsString = parts[1];
221
+ const commits = commitsString.split(',').map(c => c.trim()).filter(c => c.length > 0);
222
+ if (commits.length < 2) {
223
+ throw new ReviewCommandError('Commits mode requires at least 2 commit hashes (comma-separated)', 'INSUFFICIENT_COMMITS');
224
+ }
225
+ return {
226
+ mode: 'commits',
227
+ commits,
228
+ };
229
+ }
191
230
  /**
192
231
  * 处理未知命令或可能的语法错误
193
232
  */
@@ -324,6 +363,161 @@ class GitStatsAnalyzer {
324
363
  }
325
364
  }
326
365
  // ============================================================================
366
+ // 多 Commit 分析器
367
+ // ============================================================================
368
+ /**
369
+ * 多 Commit 文件变更分析器
370
+ */
371
+ class MultiCommitAnalyzer {
372
+ /**
373
+ * 分析多个 commit 的文件变更
374
+ */
375
+ static async analyzeCommits(projectRoot, commits) {
376
+ const git = this.createGitInstance(projectRoot);
377
+ // 步骤1:收集所有commit的变更文件
378
+ const fileCommitMap = new Map();
379
+ for (const commit of commits) {
380
+ const files = await this.getChangedFiles(git, commit);
381
+ for (const file of files) {
382
+ if (!fileCommitMap.has(file)) {
383
+ fileCommitMap.set(file, []);
384
+ }
385
+ fileCommitMap.get(file).push(commit);
386
+ }
387
+ }
388
+ // 步骤2:构建 changeList
389
+ const changeList = [];
390
+ fileCommitMap.forEach((commitList, filePath) => {
391
+ if (commitList.length === 1) {
392
+ // 只在一个 commit 中修改
393
+ changeList.push({
394
+ filePath,
395
+ commit: commitList[0],
396
+ });
397
+ }
398
+ else {
399
+ // 在多个 commit 中修改,保留第一个和最后一个
400
+ changeList.push({
401
+ filePath,
402
+ commit1: commitList[0],
403
+ commit2: commitList[commitList.length - 1],
404
+ });
405
+ }
406
+ });
407
+ // 步骤3:统计每个文件的变更行数
408
+ let totalAddedLines = 0;
409
+ let totalDeletedLines = 0;
410
+ for (const fileChange of changeList) {
411
+ const { addedLines, deletedLines } = await this.getFileStats(git, fileChange);
412
+ fileChange.modifyLines = addedLines + deletedLines;
413
+ totalAddedLines += addedLines;
414
+ totalDeletedLines += deletedLines;
415
+ }
416
+ // 构建统计信息
417
+ const stats = {
418
+ fileCount: changeList.length,
419
+ addedLines: totalAddedLines,
420
+ deletedLines: totalDeletedLines,
421
+ totalChanges: totalAddedLines + totalDeletedLines,
422
+ };
423
+ return { changeList, stats };
424
+ }
425
+ /**
426
+ * 获取单个 commit 的变更文件列表
427
+ */
428
+ static async getChangedFiles(git, commit) {
429
+ try {
430
+ const output = await git.raw(['diff', '--name-only', `${commit}^`, commit]);
431
+ return output
432
+ .trim()
433
+ .split('\n')
434
+ .filter(line => line.length > 0);
435
+ }
436
+ catch (error) {
437
+ throw new ReviewCommandError(`Failed to get changed files for commit ${commit}: ${error instanceof Error ? error.message : String(error)}`, 'GIT_DIFF_ERROR');
438
+ }
439
+ }
440
+ /**
441
+ * 获取单个文件的变更统计
442
+ */
443
+ static async getFileStats(git, fileChange) {
444
+ try {
445
+ let output;
446
+ if (fileChange.commit) {
447
+ // 单个 commit
448
+ output = await git.raw([
449
+ 'diff',
450
+ '--stat',
451
+ `${fileChange.commit}^`,
452
+ fileChange.commit,
453
+ '--',
454
+ fileChange.filePath,
455
+ ]);
456
+ }
457
+ else if (fileChange.commit1 && fileChange.commit2) {
458
+ // 两个 commit
459
+ output = await git.raw([
460
+ 'diff',
461
+ '--stat',
462
+ `${fileChange.commit1}^`,
463
+ fileChange.commit2,
464
+ '--',
465
+ fileChange.filePath,
466
+ ]);
467
+ }
468
+ else {
469
+ return { addedLines: 0, deletedLines: 0 };
470
+ }
471
+ // 解析统计输出
472
+ return this.parseFileStatOutput(output);
473
+ }
474
+ catch (_error) {
475
+ // 如果获取失败,返回0
476
+ return { addedLines: 0, deletedLines: 0 };
477
+ }
478
+ }
479
+ /**
480
+ * 解析文件的统计输出
481
+ */
482
+ static parseFileStatOutput(output) {
483
+ const lines = output.trim().split('\n');
484
+ if (lines.length === 0) {
485
+ return { addedLines: 0, deletedLines: 0 };
486
+ }
487
+ // 查找包含统计信息的行
488
+ for (const line of lines) {
489
+ const match = line.match(/\|\s*(\d+)\s*([+-]+)?/);
490
+ if (match) {
491
+ const totalChanges = parseInt(match[1], 10);
492
+ const signs = match[2] || '';
493
+ const additions = (signs.match(/\+/g) || []).length;
494
+ const deletions = (signs.match(/-/g) || []).length;
495
+ if (additions === 0 && deletions === 0) {
496
+ return {
497
+ addedLines: Math.floor(totalChanges / 2),
498
+ deletedLines: Math.ceil(totalChanges / 2),
499
+ };
500
+ }
501
+ const total = additions + deletions;
502
+ return {
503
+ addedLines: Math.round((additions / total) * totalChanges),
504
+ deletedLines: Math.round((deletions / total) * totalChanges),
505
+ };
506
+ }
507
+ }
508
+ return { addedLines: 0, deletedLines: 0 };
509
+ }
510
+ /**
511
+ * 创建 Git 实例
512
+ */
513
+ static createGitInstance(projectRoot) {
514
+ return simpleGit(projectRoot, {
515
+ binary: 'git',
516
+ maxConcurrentProcesses: 1,
517
+ });
518
+ }
519
+ }
520
+ // ============================================================================
327
521
  // Prompt 构建器
328
522
  // ============================================================================
329
523
  /**
@@ -357,6 +551,33 @@ class ReviewPromptBuilder {
357
551
  this.buildLargeChangeCallToAction()
358
552
  ]);
359
553
  }
554
+ /**
555
+ * 构建 commits 模式小型变更的评审 prompt
556
+ */
557
+ static buildCommitsSmallChangePrompt(stats, projectRoot, changeList, options) {
558
+ return this.buildPromptTemplate([
559
+ this.buildCommitsHeader(stats, changeList),
560
+ this.buildExecutionFlow(options),
561
+ this.buildCommitsFileFilteringPhase(projectRoot, changeList),
562
+ this.buildCommitsCodeReviewPhase(projectRoot, changeList, options),
563
+ this.buildReportGenerationPhase(stats),
564
+ this.buildQualityCheckPhase(projectRoot, options),
565
+ this.buildCallToAction()
566
+ ]);
567
+ }
568
+ /**
569
+ * 构建 commits 模式大型变更的评审 prompt
570
+ */
571
+ static buildCommitsLargeChangePrompt(stats, projectRoot, changeList, options) {
572
+ return this.buildPromptTemplate([
573
+ this.buildCommitsLargeChangeHeader(stats, changeList),
574
+ this.buildLargeChangeExecutionFlow(options),
575
+ this.buildCommitsAnalysisPhase(projectRoot, changeList),
576
+ this.buildCommitsBatchReviewPhase(projectRoot, changeList, options),
577
+ this.buildSummaryPhase(stats, projectRoot, options),
578
+ this.buildLargeChangeCallToAction()
579
+ ]);
580
+ }
360
581
  /**
361
582
  * 构建 prompt 模板
362
583
  */
@@ -599,42 +820,31 @@ ${this.buildEnhancedRulesSection(projectRoot, options)}
599
820
 
600
821
  ## 结果检查
601
822
 
602
- 返回结果前必须自检,剔除误报的问题,修正不准确的问题:
603
-
604
- **检查 1:文件覆盖完整性**
605
- - [ ] 已评审所有指定文件
606
- - [ ] 无法评审的文件已说明原因
823
+ 在返回结果前,要确保:
607
824
 
608
- **检查 2:输出格式完整性**
609
- - [ ] 每个问题都有 [严重性][维度] 标签
610
- - [ ] CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
611
- - [ ] MEDIUM/LOW 有:问题描述、简要说明
825
+ 1. 已评审所有指定文件
612
826
 
613
- **检查 3:分类准确性**
614
- - [ ] 严重性符合 SEVERITY_GUIDELINES
615
- - [ ] 维度符合 REVIEW_DIMENSIONS
827
+ 如果有遗漏、未评审的文件, 请补充评审
616
828
 
617
- **检查 4:严重性合理性**
618
- 对每个 CRITICAL/HIGH 问题:
619
- - [ ] 有明确代码证据证明是 bug?
620
- - [ ] "会出错"还是"可能出错"?
621
- - [ ] 代码是否已有防御/错误处理?
622
- - [ ] bug 修复还是改进建议?
623
- - [ ] 能写测试用例重现?
829
+ 2. 每个问题都在 diff 中明确存在
830
+ 3. 没有基于"理论上可能"的问题
831
+ 4. 没有报告已被处理/修复的问题
832
+ 5. 问题是真实存在,不是幻觉、"挑毛病"
624
833
 
625
- **不确定或"否"→ 必须降低严重性!**
834
+ 如果有误报的问题,请剔除
626
835
 
627
- **检查 5:Diff 覆盖验证(重要)**
628
- 对每个报告的问题:
629
- - [ ] 问题所在代码行确实是在 diff 中
630
- - [ ] 问题确实是本次变更引入的,而非之前就存在
836
+ 6. 每个问题都有 [严重性][维度] 标签
837
+ 7. CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
838
+ 8. MEDIUM/LOW 有:问题描述、简要说明
839
+ 9. 问题严重性符合 SEVERITY_GUIDELINES
840
+ 10. 问题维度符合 REVIEW_DIMENSIONS
631
841
 
632
- **如果是未变更的代码,必须删除该问题!**
842
+ 如果有描述不当或者信息缺失的报告问题,请调整修正
633
843
 
634
- **检查 6:误报风险(重要)**
635
- - [ ] 问题是真实存在,而非基于假设
636
-
637
- **如果是误报的问题,必须剔除!**`;
844
+ **质量标准**:
845
+ - 宁可 5 个准确问题,不要 10 个含 3 个误报
846
+ - ✅ 准确性 > 完整性
847
+ - ✅ 不确定时:不报告或降低严重性`;
638
848
  }
639
849
  /**
640
850
  * 构建报告生成阶段
@@ -836,7 +1046,7 @@ ${gitDiffStatCommand}
836
1046
 
837
1047
  ---
838
1048
 
839
- #### 步骤 2:智能过滤文件
1049
+ #### 步骤 2:智能过滤文件(重要!)
840
1050
 
841
1051
  **目标**:从变更文件中识别出真正需要代码评审的文件。
842
1052
 
@@ -857,7 +1067,6 @@ ${this.buildFileFilteringOutputFormat()}
857
1067
 
858
1068
  - 如果需要评审的文件 ≤ ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个 且 变更量 ≤ ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行
859
1069
  → 记录"无需分批,可以一次性评审",跳转到阶段二(单批次评审)
860
- - 如果需要评审的文件≤ ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个 且 变更量 ≤ ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行, 但是存在较大的单文件变更(比如超过500行),可视情况分批次
861
1070
 
862
1071
  - 否则 → 继续步骤 4 制定分批策略
863
1072
 
@@ -872,7 +1081,6 @@ ${this.buildFileFilteringOutputFormat()}
872
1081
  - 每批不超过 ${REVIEW_THRESHOLDS.MAX_FILES_PER_BATCH} 个文件
873
1082
  - 每批不超过 ${REVIEW_THRESHOLDS.MAX_CHANGES_PER_BATCH} 行变更
874
1083
  - 相关文件放在同一批(如 Service 和其对应的 Test)
875
- - 在每批次文件数和变更函数不超过限制的前提下,尽量减少批次
876
1084
 
877
1085
  **输出格式**:
878
1086
 
@@ -971,46 +1179,35 @@ git diff ${GitUtils.buildComparisonString(options, 'command')} -- file1.ts file2
971
1179
 
972
1180
  ${this.buildEnhancedRulesSection(projectRoot, options)}
973
1181
 
974
- ## 结果检查
1182
+ ## 结果检查(非常重要)
975
1183
 
976
1184
  在返回结果开头请注明:**批次 1/N - [批次描述]**
977
1185
 
978
- 返回结果前必须自检,剔除误报的问题,修正不准确的问题:
979
-
980
- **检查 1:文件覆盖完整性**
981
- - [ ] 已评审所有指定文件
982
- - [ ] 无法评审的文件已说明原因
1186
+ 在返回结果前,要确保:
983
1187
 
984
- **检查 2:输出格式完整性**
985
- - [ ] 每个问题都有 [严重性][维度] 标签
986
- - [ ] CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
987
- - [ ] MEDIUM/LOW 有:问题描述、简要说明
1188
+ 1. 已评审所有指定文件
988
1189
 
989
- **检查 3:分类准确性**
990
- - [ ] 严重性符合 SEVERITY_GUIDELINES
991
- - [ ] 维度符合 REVIEW_DIMENSIONS
1190
+ 如果有遗漏、未评审的文件, 请补充评审
992
1191
 
993
- **检查 4:严重性合理性**
994
- 对每个 CRITICAL/HIGH 问题:
995
- - [ ] 有明确代码证据证明是 bug?
996
- - [ ] "会出错"还是"可能出错"?
997
- - [ ] 代码是否已有防御/错误处理?
998
- - [ ] bug 修复还是改进建议?
999
- - [ ] 能写测试用例重现?
1192
+ 2. 每个问题都在 diff 中明确存在
1193
+ 3. 没有基于"理论上可能"的问题
1194
+ 4. 没有报告已被处理/修复的问题
1195
+ 5. 问题是真实存在,不是幻觉、"挑毛病"
1000
1196
 
1001
- **不确定或"否"→ 必须降低严重性!**
1197
+ 如果有误报的问题,请剔除
1002
1198
 
1003
- **检查 5:Diff 覆盖验证(重要)**
1004
- 对每个报告的问题:
1005
- - [ ] 问题所在代码行确实是在 diff 中
1006
- - [ ] 问题确实是本次变更引入的,而非之前就存在
1199
+ 6. 每个问题都有 [严重性][维度] 标签
1200
+ 7. CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
1201
+ 8. MEDIUM/LOW 有:问题描述、简要说明
1202
+ 9. 问题严重性符合 SEVERITY_GUIDELINES
1203
+ 10. 问题维度符合 REVIEW_DIMENSIONS
1007
1204
 
1008
- **如果是未变更的代码,必须删除该问题!**
1205
+ 如果有描述不当或者信息缺失的报告问题,请调整修正
1009
1206
 
1010
- **检查 6:误报风险(重要)**
1011
- - [ ] 问题是真实存在,而非基于假设
1012
-
1013
- **如果是误报的问题,必须剔除!**\`
1207
+ **质量标准**:
1208
+ - 宁可 5 个准确问题,不要 10 个含 3 个误报
1209
+ - ✅ 准确性 > 完整性
1210
+ - ✅ 不确定时:不报告或降低严重性\`
1014
1211
  }
1015
1212
  \`\`\`
1016
1213
 
@@ -1051,21 +1248,7 @@ ${this.buildEnhancedRulesSection(projectRoot, options)}
1051
1248
 
1052
1249
  ---
1053
1250
 
1054
- #### 步骤 2:复核评审问题
1055
-
1056
- **要求**:逐一复核评审问题,核对检查项
1057
- **目标**:移除误报的问题,降低误报率
1058
-
1059
- **检查 1:Diff 覆盖验证**
1060
- 对每个报告的问题:
1061
- - 问题所在代码行确实是在 diff 中
1062
- - 问题确实是本次变更引入的,而非之前就存在
1063
-
1064
- **检查 2:误报风险**
1065
- 对每个报告的问题:
1066
- - [ ] 问题是真实存在,而非基于假设
1067
-
1068
- #### 步骤 3:按维度和严重性重新组织
1251
+ #### 步骤 2:按维度和严重性重新组织
1069
1252
 
1070
1253
  将收集到的所有问题按照以下结构重新组织:
1071
1254
 
@@ -1102,7 +1285,7 @@ LOW 问题列表
1102
1285
 
1103
1286
  ---
1104
1287
 
1105
- #### 步骤 4:生成最终报告
1288
+ #### 步骤 3:生成最终报告
1106
1289
 
1107
1290
  按照以下格式生成 \`CODE_REVIEW.md\`:
1108
1291
 
@@ -1112,7 +1295,31 @@ ${this.buildReportTemplate(stats)}
1112
1295
 
1113
1296
  ---
1114
1297
 
1115
- **所有步骤完成后**:
1298
+ #### 步骤 4:汇总完整性检查(必须执行!)
1299
+
1300
+ **目的**:确保汇总过程没有遗漏 subagent 的评审结果
1301
+
1302
+ **检查项**:
1303
+
1304
+ 1. **数量完整性**
1305
+ - 回顾所有批次的 subagent 返回结果
1306
+ - 统计各批次报告的问题总数:批次1(X个) + 批次2(Y个) + ... = 总计N个
1307
+ - 统计最终报告中的问题总数:M个
1308
+ - 验证:N = M
1309
+ - ❌ 如果不相等,返回步骤1重新逐条收集
1310
+
1311
+ 2. **结构正确性**
1312
+ - [ ] 按"严重性 > 维度"组织
1313
+ - [ ] 评审摘要表格数据与实际问题数量一致
1314
+ - [ ] 没有批次信息残留("批次1"、"第一批"等)
1315
+ - ❌ 如果有误,重新组织结构或重新计算
1316
+
1317
+ 3. **内容完整性**(抽查)
1318
+ - 随机抽查2-3个问题,确认汇总时没有删减内容
1319
+ - 对比原始 subagent 结果与最终报告中的对应问题
1320
+ - ❌ 如果发现删减,从原始结果中恢复完整内容
1321
+
1322
+ **所有检查项通过后**:
1116
1323
  1. 将最终报告写入 \`CODE_REVIEW.md\`
1117
1324
  2. 标记任务为 **completed**
1118
1325
  3. 输出简要总结:
@@ -1167,11 +1374,402 @@ ${this.buildReportTemplate(stats)}
1167
1374
 
1168
1375
  **第四步**:基于过滤后的文件制定分批策略(如需要)
1169
1376
 
1170
- **重要**:此阶段只做分析、过滤和规划,不开始评审代码!
1171
- **重要**:此阶段后请继续执行阶段二和阶段三,不要中断,不要询问是否继续!
1377
+ **重要**:此阶段只做分析、过滤和规划,不开始评审代码
1378
+ **重要**:此阶段后请继续执行阶段二和阶段三,不要中断,不要询问是否继续
1172
1379
 
1173
1380
  请开始执行!`;
1174
1381
  }
1382
+ // ============================================================================
1383
+ // Commits 模式专用方法
1384
+ // ============================================================================
1385
+ /**
1386
+ * 构建 commits 模式变更摘要头部
1387
+ */
1388
+ static buildCommitsHeader(stats, changeList) {
1389
+ const commitsCount = this.getUniqueCommitsCount(changeList);
1390
+ return `# 代码评审任务 - Commits 模式
1391
+
1392
+ ## 变更摘要
1393
+
1394
+ - **Commits 数量**: ${commitsCount} 个
1395
+ - **文件总数**: ${stats.fileCount} 个
1396
+ - **新增行数**: ${stats.addedLines} 行
1397
+ - **删除行数**: ${stats.deletedLines} 行
1398
+ - **总变更量**: ${stats.totalChanges} 行
1399
+
1400
+ 变更规模适中,将使用 code-reviewer 一次性完成评审。`;
1401
+ }
1402
+ /**
1403
+ * 构建 commits 模式大型变更头部
1404
+ */
1405
+ static buildCommitsLargeChangeHeader(stats, changeList) {
1406
+ const commitsCount = this.getUniqueCommitsCount(changeList);
1407
+ return `# 代码评审任务 - Commits 模式(大型变更)
1408
+
1409
+ ## 变更摘要
1410
+
1411
+ - **Commits 数量**: ${commitsCount} 个
1412
+ - **文件总数**: ${stats.fileCount} 个
1413
+ - **新增行数**: ${stats.addedLines} 行
1414
+ - **删除行数**: ${stats.deletedLines} 行
1415
+ - **总变更量**: ${stats.totalChanges} 行
1416
+
1417
+ 由于这是一个**大型变更**(超过 ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个文件或 ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行代码),需要分批次进行评审。`;
1418
+ }
1419
+ /**
1420
+ * 构建 commits 模式文件过滤阶段
1421
+ */
1422
+ static buildCommitsFileFilteringPhase(projectRoot, changeList) {
1423
+ const changeListJson = JSON.stringify({ changeList }, null, 2);
1424
+ return `## 第一阶段:AI 智能文件过滤
1425
+
1426
+ ### 步骤 1:查看变更文件列表并进行智能过滤
1427
+
1428
+ 以下是多个 commit 合并后的变更文件列表(changeList):
1429
+
1430
+ \`\`\`json
1431
+ ${changeListJson}
1432
+ \`\`\`
1433
+
1434
+ **说明**:
1435
+ - 如果文件只在一个 commit 中修改,则只有 \`commit\` 字段
1436
+ - 如果文件在多个 commit 中修改,则有 \`commit1\` 和 \`commit2\` 字段(分别表示第一个和最后一个修改它的 commit)
1437
+ - \`modifyLines\` 表示该文件的总变更行数
1438
+
1439
+ **然后对变更文件进行 AI 智能过滤分析**:
1440
+
1441
+ ${this.buildFileFilteringRules()}
1442
+
1443
+ ${this.buildFileFilteringOutputFormat()}
1444
+
1445
+ ### 步骤 2:检查过滤结果
1446
+
1447
+ **重要**:如果所有文件都被过滤掉(需要评审的文件为 0),则:
1448
+ 1. 输出消息:"✅ 本次变更无需代码评审(全部为文档、配置或自动生成文件)"
1449
+ 2. 创建简单的 CODE_REVIEW.md 文件说明情况
1450
+ 3. 结束评审流程
1451
+
1452
+ 如果还有需要评审的文件,继续下一步。`;
1453
+ }
1454
+ /**
1455
+ * 构建 commits 模式代码评审阶段
1456
+ */
1457
+ static buildCommitsCodeReviewPhase(projectRoot, changeList, options) {
1458
+ return `## 第二阶段:执行代码评审
1459
+
1460
+ ### 步骤 3:AI 智能代码评审
1461
+
1462
+ 使用 **Task tool** 调用 code-reviewer subagent,**仅对需要评审的文件进行评审**:
1463
+
1464
+ \`\`\`typescript
1465
+ {
1466
+ subagent_type: 'code-reviewer',
1467
+ description: '代码评审 - Commits 模式',
1468
+ prompt: \`${this.buildCommitsSubagentPrompt(projectRoot, changeList, options)}\`
1469
+ }
1470
+ \`\`\`
1471
+
1472
+ ### 步骤 4:等待评审完成
1473
+
1474
+ subagent 会自动:
1475
+ 1. 根据 changeList 为每个文件执行相应的 git diff 命令
1476
+ 2. 使用 Read tool 读取相关文件(重点关注需要评审的文件)
1477
+ 3. 按照六个维度进行深度代码评审
1478
+ 4. 进行自我检查确保评审质量`;
1479
+ }
1480
+ /**
1481
+ * 构建 commits 模式分析阶段
1482
+ */
1483
+ static buildCommitsAnalysisPhase(projectRoot, changeList) {
1484
+ const changeListJson = JSON.stringify({ changeList }, null, 2);
1485
+ return `#### 步骤 1:查看 changeList
1486
+
1487
+ 以下是多个 commit 合并后的变更文件列表(changeList):
1488
+
1489
+ \`\`\`json
1490
+ ${changeListJson}
1491
+ \`\`\`
1492
+
1493
+ **说明**:
1494
+ - 如果文件只在一个 commit 中修改,则只有 \`commit\` 字段
1495
+ - 如果文件在多个 commit 中修改,则有 \`commit1\` 和 \`commit2\` 字段(分别表示第一个和最后一个修改它的 commit)
1496
+ - \`modifyLines\` 表示该文件的总变更行数
1497
+
1498
+ **重要提示:不要获取完整 diff!**
1499
+ - 只需要分析 changeList 中的文件列表和变更行数
1500
+ - 这将列出所有变更的文件及其变更行数
1501
+
1502
+ ---
1503
+
1504
+ #### 步骤 2:智能过滤文件
1505
+
1506
+ **目标**:从变更文件中识别出真正需要代码评审的文件。
1507
+
1508
+ ${this.buildFileFilteringRules()}
1509
+
1510
+ ${this.buildFileFilteringOutputFormat()}
1511
+
1512
+ **重要**:
1513
+ - 必须列出所有变更文件,要么在"需要评审"列表,要么在"已过滤"列表
1514
+ - 每个被过滤的文件都要简要说明原因
1515
+ - 统计数据要准确
1516
+
1517
+ ---
1518
+
1519
+ #### 步骤 3:判断评审规模
1520
+
1521
+ 基于**过滤后的文件**(即"需要评审的文件"列表),判断是否需要分批:
1522
+
1523
+ - 如果需要评审的文件 ≤ ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个 且 变更量 ≤ ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行
1524
+ → 记录"无需分批,可以一次性评审",跳转到阶段二(单批次评审)
1525
+
1526
+ - 否则 → 继续步骤 4 制定分批策略
1527
+
1528
+ ---
1529
+
1530
+ #### 步骤 4:制定分批策略(如需分批)
1531
+
1532
+ 基于"需要评审的文件"列表,制定分批方案:
1533
+
1534
+ **分批原则**:
1535
+ - 按功能模块、文件类型、业务关联性分组
1536
+ - 每批不超过 ${REVIEW_THRESHOLDS.MAX_FILES_PER_BATCH} 个文件
1537
+ - 每批不超过 ${REVIEW_THRESHOLDS.MAX_CHANGES_PER_BATCH} 行变更
1538
+ - 相关文件放在同一批(如 Service 和其对应的 Test)
1539
+
1540
+ **输出格式**:
1541
+
1542
+ \`\`\`markdown
1543
+ ## 分批方案
1544
+
1545
+ ### 批次 1:[批次描述,如 "用户认证模块"]
1546
+ - 文件数:8 个
1547
+ - 预计变更行数:650 行
1548
+ - 文件列表:
1549
+ - src/services/UserService.java
1550
+ - src/services/AuthService.java
1551
+ - src/utils/jwt.ts
1552
+ ...
1553
+
1554
+ ### 批次 2:[批次描述]
1555
+ ...
1556
+
1557
+ ### 批次 N:[批次描述]
1558
+ ...
1559
+ \`\`\`
1560
+
1561
+ ---
1562
+
1563
+ #### 步骤 5:确认完整性
1564
+
1565
+ 检查分批方案(如果分批)或单批次方案:
1566
+ - "需要评审的文件"列表中的每个文件都已分配到某个批次
1567
+ - 没有文件被遗漏
1568
+ - 没有文件被重复分配
1569
+
1570
+ 如果检查通过,更新 TodoWrite,标记步骤1-5为完成,添加后续评审任务,继续阶段二。
1571
+
1572
+ **重要**:此阶段只做分析和规划,不开始评审代码。`;
1573
+ }
1574
+ /**
1575
+ * 构建 commits 模式分批评审阶段
1576
+ */
1577
+ static buildCommitsBatchReviewPhase(projectRoot, changeList, options) {
1578
+ return `### 阶段二:批次评审(完成分批后执行)
1579
+
1580
+ 分批方案确定后,更新 **TodoWrite** 任务列表:
1581
+
1582
+ \`\`\`
1583
+ 1. [completed] 分析变更文件
1584
+ 2. [completed] AI 智能文件过滤
1585
+ 3. [completed] AI 智能制定分批次评审策略
1586
+ 4. [completed] 复核分批次评审策略
1587
+ 5. 评审批次 1/N
1588
+ 6. 评审批次 2/N
1589
+ ...
1590
+ N+6. 汇总所有批次评审结果
1591
+ N+7. 生成最终评审报告
1592
+ \`\`\`
1593
+
1594
+ 对于每个批次:
1595
+
1596
+ 1. 标记任务为 **in_progress**
1597
+ 2. 使用 **Task tool** 调用 **code-reviewer** subagent
1598
+ 3. **验证返回结果**:
1599
+ - 如果返回"未发现需要关注的问题",记录该批次无问题
1600
+ - 如果返回了问题列表,快速检查格式是否正确
1601
+ 4. 标记为 **completed**,继续下一批次
1602
+
1603
+ **Task tool 调用示例**:
1604
+
1605
+ \`\`\`typescript
1606
+ {
1607
+ subagent_type: 'code-reviewer',
1608
+ description: '代码评审 - 批次 1/N: [批次描述]',
1609
+ prompt: \`请按照你的标准评审流程进行全面代码评审(批次 1/N - Commits 模式)。
1610
+
1611
+ ## 批次信息
1612
+
1613
+ - 批次描述:[描述,如 "用户认证模块"]
1614
+ - 文件数:X 个
1615
+ - 预计变更行数:Y 行
1616
+
1617
+ ## 需要评审的文件 changeList
1618
+
1619
+ 提供该批次文件的 changeList JSON:
1620
+
1621
+ \\\`\\\`\\\`json
1622
+ {
1623
+ "changeList": [
1624
+ { "filePath": "file1.ts", "commit": "abc123", "modifyLines": 50 },
1625
+ { "filePath": "file2.ts", "commit1": "abc123", "commit2": "def456", "modifyLines": 120 },
1626
+ ...
1627
+ ]
1628
+ }
1629
+ \\\`\\\`\\\`
1630
+
1631
+ ## 获取变更内容
1632
+
1633
+ 对于 changeList 中的每个文件,根据 commit 字段执行相应的 git diff 命令:
1634
+
1635
+ - 如果文件只有 \\\`commit\\\` 字段,执行:
1636
+ \\\`\\\`\\\`bash
1637
+ git diff <commit>^ <commit> -- <filePath>
1638
+ \\\`\\\`\\\`
1639
+
1640
+ - 如果文件有 \\\`commit1\\\` 和 \\\`commit2\\\` 字段,执行:
1641
+ \\\`\\\`\\\`bash
1642
+ git diff <commit1>^ <commit2> -- <filePath>
1643
+ \\\`\\\`\\\`
1644
+
1645
+ ${this.buildEnhancedRulesSection(projectRoot, options)}
1646
+
1647
+ 在返回结果开头请注明:**批次 1/N - [批次描述] (Commits 模式)**
1648
+
1649
+ ## 结果检查(非常重要)
1650
+
1651
+ 在返回结果前,要确保:
1652
+
1653
+ 1. 已评审所有指定文件
1654
+
1655
+ 如果有遗漏、未评审的文件, 请补充评审
1656
+
1657
+ 2. 每个问题都在 diff 中明确存在
1658
+ 3. 没有基于"理论上可能"的问题
1659
+ 4. 没有报告已被处理/修复的问题
1660
+ 5. 问题是真实存在,不是幻觉、"挑毛病"
1661
+
1662
+ 如果有误报的问题,请剔除
1663
+
1664
+ 6. 每个问题都有 [严重性][维度] 标签
1665
+ 7. CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
1666
+ 8. MEDIUM/LOW 有:问题描述、简要说明
1667
+ 9. 问题严重性符合 SEVERITY_GUIDELINES
1668
+ 10. 问题维度符合 REVIEW_DIMENSIONS
1669
+
1670
+ 如果有描述不当或者信息缺失的报告问题,请调整修正
1671
+
1672
+ **质量标准**:
1673
+ - 宁可 5 个准确问题,不要 10 个含 3 个误报
1674
+ - 准确性 > 完整性
1675
+ - 不确定时:不报告\`
1676
+ }
1677
+ \`\`\`
1678
+
1679
+ **关键点**:
1680
+ - 不要在主 task 中获取完整 diff,只提供 changeList 和 git diff 命令说明
1681
+ - code-reviewer subagent 会自己根据 changeList 执行相应的 git diff 命令
1682
+ - code-reviewer subagent 会自己读取所有相关文件
1683
+ - code-reviewer subagent 会自己进行深度代码评审并自检
1684
+ - code-reviewer subagent 会返回评审结果`;
1685
+ }
1686
+ /**
1687
+ * 构建 commits 模式 subagent prompt
1688
+ */
1689
+ static buildCommitsSubagentPrompt(projectRoot, changeList, options) {
1690
+ const changeListJson = JSON.stringify({ changeList }, null, 2);
1691
+ return `请按照你的标准评审流程进行全面代码评审(Commits 模式)。
1692
+
1693
+ ## 变更信息
1694
+
1695
+ - 需要评审的文件:X 个
1696
+ - 变更行数:约 Y 行
1697
+
1698
+ ## 需要评审的文件 changeList
1699
+
1700
+ \\\`\\\`\\\`json
1701
+ ${changeListJson}
1702
+ \\\`\\\`\\\`
1703
+
1704
+ **说明**:
1705
+ - 如果文件只有 \\\`commit\\\` 字段,表示只在一个 commit 中修改
1706
+ - 如果文件有 \\\`commit1\\\` 和 \\\`commit2\\\` 字段,表示在多个 commit 中修改(第一个和最后一个)
1707
+ - \\\`modifyLines\\\` 表示该文件的总变更行数
1708
+
1709
+ ## 获取变更内容
1710
+
1711
+ 对于 changeList 中的每个文件,根据 commit 字段执行相应的 git diff 命令:
1712
+
1713
+ - 如果文件只有 \\\`commit\\\` 字段,执行:
1714
+ \\\`\\\`\\\`bash
1715
+ git diff <commit>^ <commit> -- <filePath>
1716
+ \\\`\\\`\\\`
1717
+
1718
+ - 如果文件有 \\\`commit1\\\` 和 \\\`commit2\\\` 字段,执行:
1719
+ \\\`\\\`\\\`bash
1720
+ git diff <commit1>^ <commit2> -- <filePath>
1721
+ \\\`\\\`\\\`
1722
+
1723
+ (将 <commit>、<commit1>、<commit2>、<filePath> 替换为 changeList 中的实际值)
1724
+
1725
+ ${this.buildEnhancedRulesSection(projectRoot, options)}
1726
+
1727
+ ## 结果检查(非常重要)
1728
+
1729
+ 在返回结果前,要确保:
1730
+
1731
+ 1. 已评审所有指定文件
1732
+
1733
+ 如果有遗漏、未评审的文件, 请补充评审
1734
+
1735
+ 2. 每个问题都在 diff 中明确存在
1736
+ 3. 没有基于"理论上可能"的问题
1737
+ 4. 没有报告已被处理/修复的问题
1738
+ 5. 问题是真实存在,不是幻觉、"挑毛病"
1739
+
1740
+ 如果有误报的问题,请剔除
1741
+
1742
+ 6. 每个问题都有 [严重性][维度] 标签
1743
+ 7. CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
1744
+ 8. MEDIUM/LOW 有:问题描述、简要说明
1745
+ 9. 问题严重性符合 SEVERITY_GUIDELINES
1746
+ 10. 问题维度符合 REVIEW_DIMENSIONS
1747
+
1748
+ 如果有描述不当或者信息缺失的报告问题,请调整修正
1749
+
1750
+ **质量标准**:
1751
+ - 宁可 5 个准确问题,不要 10 个含 3 个误报
1752
+ - 准确性 > 完整性
1753
+ - 不确定时:不报告或降低严重性`;
1754
+ }
1755
+ /**
1756
+ * 获取 changeList 中的唯一 commit 数量
1757
+ */
1758
+ static getUniqueCommitsCount(changeList) {
1759
+ const commits = new Set();
1760
+ for (const file of changeList) {
1761
+ if (file.commit) {
1762
+ commits.add(file.commit);
1763
+ }
1764
+ if (file.commit1) {
1765
+ commits.add(file.commit1);
1766
+ }
1767
+ if (file.commit2) {
1768
+ commits.add(file.commit2);
1769
+ }
1770
+ }
1771
+ return commits.size;
1772
+ }
1175
1773
  }
1176
1774
  // ============================================================================
1177
1775
  // 评审策略选择器
@@ -1183,7 +1781,12 @@ class ReviewStrategySelector {
1183
1781
  /**
1184
1782
  * 根据统计信息选择评审策略
1185
1783
  */
1186
- static selectStrategy(stats, projectRoot, options) {
1784
+ static selectStrategy(stats, projectRoot, options, changeList) {
1785
+ // commits 模式单独处理
1786
+ if (options.mode === 'commits' && changeList) {
1787
+ return this.selectCommitsStrategy(stats, projectRoot, changeList, options);
1788
+ }
1789
+ // 其他模式
1187
1790
  const isLargeChange = this.isLargeChange(stats);
1188
1791
  if (isLargeChange) {
1189
1792
  const gitDiffStatCommand = GitUtils.buildDiffStatCommand(options);
@@ -1194,6 +1797,18 @@ class ReviewStrategySelector {
1194
1797
  return ReviewPromptBuilder.buildSmallChangePrompt(stats, projectRoot, gitDiffCommand, options);
1195
1798
  }
1196
1799
  }
1800
+ /**
1801
+ * 选择 commits 模式的评审策略
1802
+ */
1803
+ static selectCommitsStrategy(stats, projectRoot, changeList, options) {
1804
+ const isLargeChange = this.isLargeChange(stats);
1805
+ if (isLargeChange) {
1806
+ return ReviewPromptBuilder.buildCommitsLargeChangePrompt(stats, projectRoot, changeList, options);
1807
+ }
1808
+ else {
1809
+ return ReviewPromptBuilder.buildCommitsSmallChangePrompt(stats, projectRoot, changeList, options);
1810
+ }
1811
+ }
1197
1812
  /**
1198
1813
  * 判断是否为大型变更
1199
1814
  */
@@ -1203,13 +1818,165 @@ class ReviewStrategySelector {
1203
1818
  }
1204
1819
  }
1205
1820
  // ============================================================================
1821
+ // 帮助信息构建器
1822
+ // ============================================================================
1823
+ /**
1824
+ * 构建 review 命令的帮助信息
1825
+ */
1826
+ function buildHelpMessage() {
1827
+ return `# /review - 智能代码评审命令
1828
+
1829
+ ## 📖 功能说明
1830
+
1831
+ 执行 AI 驱动的代码评审,支持多种评审模式,自动识别变更规模并智能分批处理。
1832
+
1833
+ ## 🚀 使用方法
1834
+
1835
+ ### 基本用法
1836
+
1837
+ \`\`\`bash
1838
+ /review # 评审未提交的变更(默认模式)
1839
+ /review --help # 显示帮助信息
1840
+ /review -h # 显示帮助信息(简写)
1841
+ \`\`\`
1842
+
1843
+ ### Commit 模式
1844
+
1845
+ \`\`\`bash
1846
+ /review -commit <commit1> # 评审单个 commit 的变更
1847
+ /review -commit <commit1> <commit2> # 评审两个 commit 之间的差异
1848
+ \`\`\`
1849
+
1850
+ **示例:**
1851
+ \`\`\`bash
1852
+ /review -commit abc1234 # 评审 commit abc1234 的变更
1853
+ /review -commit abc1234 def5678 # 评审 abc1234 到 def5678 之间的变更
1854
+ \`\`\`
1855
+
1856
+ ### Branch 模式
1857
+
1858
+ \`\`\`bash
1859
+ /review -branch <branch1> # 评审分支与 main 的差异
1860
+ /review -branch <branch1> <branch2> # 评审两个分支之间的差异
1861
+ \`\`\`
1862
+
1863
+ **示例:**
1864
+ \`\`\`bash
1865
+ /review -branch feature-login # 评审 feature-login 分支与 main 的差异
1866
+ /review -branch dev staging # 评审 dev 和 staging 分支之间的差异
1867
+ \`\`\`
1868
+
1869
+ ## ⚙️ 可选参数
1870
+
1871
+ ### --prompt-enhance <文件路径>
1872
+
1873
+ 指定额外的评审规则文件,补充默认的评审标准。
1874
+
1875
+ **示例:**
1876
+ \`\`\`bash
1877
+ /review --prompt-enhance ./custom-rules.md
1878
+ /review -commit abc1234 --prompt-enhance ./team-standards.md
1879
+ \`\`\`
1880
+
1881
+ ### --notice
1882
+
1883
+ 为 CI/CD 流水线集成生成完成标志文件(\`.review-completed\`)。
1884
+
1885
+ **示例:**
1886
+ \`\`\`bash
1887
+ /review --notice
1888
+ /review -commit abc1234 --notice
1889
+ \`\`\`
1890
+
1891
+ **标志文件格式:**
1892
+ \`\`\`json
1893
+ {
1894
+ "status": "completed",
1895
+ "timestamp": "2025-01-15T10:30:00Z",
1896
+ "report": "CODE_REVIEW.md"
1897
+ }
1898
+ \`\`\`
1899
+
1900
+ ## 📊 评审特性
1901
+
1902
+ ### 智能分批处理
1903
+ - **小型变更**(≤50 文件或 ≤3000 行):一次性评审
1904
+ - **大型变更**(>50 文件或 >3000 行):自动分批评审
1905
+
1906
+ ### 智能文件过滤
1907
+ 自动过滤不需要评审的文件:
1908
+ - 文档文件(README.md, CHANGELOG.md)
1909
+ - 依赖锁文件(package-lock.json, yarn.lock)
1910
+ - 二进制文件和媒体文件
1911
+ - 自动生成的代码
1912
+ - IDE 配置文件
1913
+
1914
+ ### 六维度评审
1915
+ - 🐛 **正确性**:逻辑错误、边界条件、异常处理
1916
+ - 🔒 **安全性**:注入漏洞、权限控制、数据验证
1917
+ - ⚡ **性能**:算法效率、资源使用、查询优化
1918
+ - 🔧 **可维护性**:代码复杂度、命名规范、文档完整性
1919
+ - 🏗️ **架构设计**:设计模式、模块解耦、接口设计
1920
+ - ✨ **最佳实践**:代码风格、惯用法、工程规范
1921
+
1922
+ ### 四级严重性
1923
+ - 🚨 **CRITICAL**:必须立即修复的致命问题
1924
+ - ⚠️ **HIGH**:重要问题,应尽快修复
1925
+ - 🔶 **MEDIUM**:中等问题,建议修复
1926
+ - 🔷 **LOW**:轻微问题,可选择性修复
1927
+
1928
+ ## 📝 输出结果
1929
+
1930
+ 评审完成后会生成 \`CODE_REVIEW.md\` 报告,包含:
1931
+ - 变更概览和统计信息
1932
+ - 按严重性和维度分类的问题列表
1933
+ - 详细的问题分析和修复建议
1934
+ - 代码质量整体评估
1935
+
1936
+ ## 💡 使用技巧
1937
+
1938
+ 1. **首次使用**:建议先对小范围变更进行评审,熟悉评审流程
1939
+ 2. **自定义规则**:使用 \`--prompt-enhance\` 添加团队特定的编码规范
1940
+ 3. **CI/CD 集成**:使用 \`--notice\` 参数在流水线中检测评审完成
1941
+ 4. **大型变更**:系统会自动分批处理,无需手动干预
1942
+
1943
+ ## 🔗 相关命令
1944
+
1945
+ - \`/help\` - 查看所有可用命令
1946
+ - \`/docs\` - 查看完整文档
1947
+
1948
+ ## ❓ 常见问题
1949
+
1950
+ **Q: 如何只评审特定文件?**
1951
+ A: 目前评审基于 git diff 结果,可以先 stage 特定文件再评审。
1952
+
1953
+ **Q: 评审需要多长时间?**
1954
+ A: 取决于变更规模,小型变更通常 1-3 分钟,大型变更可能需要 5-15 分钟。
1955
+
1956
+ **Q: 如何自定义评审规则?**
1957
+ A: 创建 Markdown 文件描述你的规则,然后使用 \`--prompt-enhance\` 参数指定。
1958
+
1959
+ ---
1960
+
1961
+ 💡 提示:使用 \`/review --help\` 随时查看本帮助信息`;
1962
+ }
1963
+ // ============================================================================
1206
1964
  // 命令导出
1207
1965
  // ============================================================================
1208
1966
  export const reviewCommand = {
1209
1967
  name: 'review',
1210
- description: 'Performs code review with multiple modes: default (uncommitted changes), commit comparison, or branch comparison.',
1968
+ description: 'Performs code review with multiple modes: default (uncommitted changes), commit comparison, branch comparison, or multiple commits comparison.',
1211
1969
  kind: CommandKind.BUILT_IN,
1212
1970
  action: async (context, args) => {
1971
+ // 检查是否请求帮助信息
1972
+ const trimmedArgs = args.trim();
1973
+ if (trimmedArgs === '--help' || trimmedArgs === '-h' || trimmedArgs === 'help') {
1974
+ return {
1975
+ type: 'message',
1976
+ messageType: 'info',
1977
+ content: buildHelpMessage(),
1978
+ };
1979
+ }
1213
1980
  // 验证服务可用性
1214
1981
  if (!context.services.config) {
1215
1982
  return {
@@ -1234,9 +2001,21 @@ export const reviewCommand = {
1234
2001
  };
1235
2002
  }
1236
2003
  try {
1237
- // 解析参数并获取统计信息
2004
+ // 解析参数
1238
2005
  const options = ReviewArgumentParser.parse(args);
1239
- const stats = await GitStatsAnalyzer.getDiffStats(projectRoot, options);
2006
+ let stats;
2007
+ let changeList;
2008
+ // 根据模式获取统计信息
2009
+ if (options.mode === 'commits' && options.commits) {
2010
+ // commits 模式:使用 MultiCommitAnalyzer
2011
+ const result = await MultiCommitAnalyzer.analyzeCommits(projectRoot, options.commits);
2012
+ stats = result.stats;
2013
+ changeList = result.changeList;
2014
+ }
2015
+ else {
2016
+ // 其他模式:使用 GitStatsAnalyzer
2017
+ stats = await GitStatsAnalyzer.getDiffStats(projectRoot, options);
2018
+ }
1240
2019
  // 检查是否有变更
1241
2020
  if (stats.fileCount === 0) {
1242
2021
  return {
@@ -1246,7 +2025,7 @@ export const reviewCommand = {
1246
2025
  };
1247
2026
  }
1248
2027
  // 根据变更规模选择评审策略
1249
- const reviewPrompt = ReviewStrategySelector.selectStrategy(stats, projectRoot, options);
2028
+ const reviewPrompt = ReviewStrategySelector.selectStrategy(stats, projectRoot, options, changeList);
1250
2029
  return {
1251
2030
  type: 'submit_prompt',
1252
2031
  content: reviewPrompt,