@umsai/ums-code 0.3.0-v2 → 0.5.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.
- package/dist/package.json +4 -4
- package/dist/src/{zed-integration → acp-integration}/acp.d.ts +7 -0
- package/dist/src/{zed-integration → acp-integration}/acp.js +25 -0
- package/dist/src/acp-integration/acp.js.map +1 -0
- package/dist/src/acp-integration/acpAgent.d.ts +10 -0
- package/dist/src/acp-integration/acpAgent.js +238 -0
- package/dist/src/acp-integration/acpAgent.js.map +1 -0
- package/dist/src/{zed-integration → acp-integration}/schema.d.ts +1030 -43
- package/dist/src/{zed-integration → acp-integration}/schema.js +81 -2
- package/dist/src/acp-integration/schema.js.map +1 -0
- package/dist/src/{zed-integration/fileSystemService.d.ts → acp-integration/service/filesystem.d.ts} +1 -1
- package/dist/src/{zed-integration/fileSystemService.js → acp-integration/service/filesystem.js} +14 -1
- package/dist/src/acp-integration/service/filesystem.js.map +1 -0
- package/dist/src/acp-integration/service/filesystem.test.d.ts +6 -0
- package/dist/src/acp-integration/service/filesystem.test.js +39 -0
- package/dist/src/acp-integration/service/filesystem.test.js.map +1 -0
- package/dist/src/acp-integration/session/HistoryReplayer.d.ts +51 -0
- package/dist/src/acp-integration/session/HistoryReplayer.js +164 -0
- package/dist/src/acp-integration/session/HistoryReplayer.js.map +1 -0
- package/dist/src/acp-integration/session/HistoryReplayer.test.d.ts +6 -0
- package/dist/src/acp-integration/session/HistoryReplayer.test.js +374 -0
- package/dist/src/acp-integration/session/HistoryReplayer.test.js.map +1 -0
- package/dist/src/acp-integration/session/Session.d.ts +66 -0
- package/dist/src/acp-integration/session/Session.js +760 -0
- package/dist/src/acp-integration/session/Session.js.map +1 -0
- package/dist/src/acp-integration/session/SubAgentTracker.d.ts +51 -0
- package/dist/src/acp-integration/session/SubAgentTracker.js +257 -0
- package/dist/src/acp-integration/session/SubAgentTracker.js.map +1 -0
- package/dist/src/acp-integration/session/SubAgentTracker.test.d.ts +6 -0
- package/dist/src/acp-integration/session/SubAgentTracker.test.js +369 -0
- package/dist/src/acp-integration/session/SubAgentTracker.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/BaseEmitter.d.ts +27 -0
- package/dist/src/acp-integration/session/emitters/BaseEmitter.js +34 -0
- package/dist/src/acp-integration/session/emitters/BaseEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.d.ts +41 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.js +77 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.test.d.ts +6 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.test.js +174 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.d.ts +39 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.js +83 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.test.d.ts +6 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.test.js +176 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.d.ts +80 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.js +248 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.test.d.ts +6 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.test.js +561 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/index.d.ts +9 -0
- package/dist/src/acp-integration/session/emitters/index.js +10 -0
- package/dist/src/acp-integration/session/emitters/index.js.map +1 -0
- package/dist/src/acp-integration/session/index.d.ts +24 -0
- package/dist/src/acp-integration/session/index.js +16 -0
- package/dist/src/acp-integration/session/index.js.map +1 -0
- package/dist/src/acp-integration/session/types.d.ts +71 -0
- package/dist/src/acp-integration/session/types.js +7 -0
- package/dist/src/acp-integration/session/types.js.map +1 -0
- package/dist/src/config/auth.d.ts +1 -0
- package/dist/src/config/auth.js +3 -0
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/config.d.ts +11 -3
- package/dist/src/config/config.js +84 -8
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/extension.js +0 -2
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +12 -0
- package/dist/src/config/settingsSchema.js +10 -0
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/core/auth.d.ts +1 -1
- package/dist/src/core/auth.js +3 -2
- package/dist/src/core/auth.js.map +1 -1
- package/dist/src/gemini.js +38 -18
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +7 -0
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/i18n/index.d.ts +1 -1
- package/dist/src/i18n/index.js +4 -0
- package/dist/src/i18n/index.js.map +1 -1
- package/dist/src/i18n/locales/en.js +1 -13
- package/dist/src/i18n/locales/ru.js +1121 -0
- package/dist/src/i18n/locales/zh.js +1 -10
- package/dist/src/nonInteractive/control/ControlDispatcher.d.ts +24 -1
- package/dist/src/nonInteractive/control/ControlDispatcher.js +46 -17
- package/dist/src/nonInteractive/control/ControlDispatcher.js.map +1 -1
- package/dist/src/nonInteractive/control/ControlService.d.ts +2 -1
- package/dist/src/nonInteractive/control/ControlService.js +22 -37
- package/dist/src/nonInteractive/control/ControlService.js.map +1 -1
- package/dist/src/nonInteractive/control/controllers/baseController.d.ts +2 -1
- package/dist/src/nonInteractive/control/controllers/baseController.js +38 -5
- package/dist/src/nonInteractive/control/controllers/baseController.js.map +1 -1
- package/dist/src/nonInteractive/control/controllers/permissionController.d.ts +1 -22
- package/dist/src/nonInteractive/control/controllers/permissionController.js +38 -38
- package/dist/src/nonInteractive/control/controllers/permissionController.js.map +1 -1
- package/dist/src/nonInteractive/control/controllers/sdkMcpController.d.ts +54 -0
- package/dist/src/nonInteractive/control/controllers/sdkMcpController.js +84 -0
- package/dist/src/nonInteractive/control/controllers/sdkMcpController.js.map +1 -0
- package/dist/src/nonInteractive/control/controllers/systemController.d.ts +16 -6
- package/dist/src/nonInteractive/control/controllers/systemController.js +189 -44
- package/dist/src/nonInteractive/control/controllers/systemController.js.map +1 -1
- package/dist/src/nonInteractive/control/types/serviceAPIs.d.ts +1 -16
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.d.ts +11 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js +54 -2
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js.map +1 -1
- package/dist/src/nonInteractive/session.d.ts +0 -16
- package/dist/src/nonInteractive/session.js +317 -321
- package/dist/src/nonInteractive/session.js.map +1 -1
- package/dist/src/nonInteractive/session.test.js +6 -0
- package/dist/src/nonInteractive/session.test.js.map +1 -1
- package/dist/src/nonInteractive/types.d.ts +57 -3
- package/dist/src/nonInteractive/types.js +0 -1
- package/dist/src/nonInteractive/types.js.map +1 -1
- package/dist/src/nonInteractiveCli.js +25 -46
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +5 -6
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +0 -3
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/test-utils/mockCommandContext.js +11 -2
- package/dist/src/test-utils/mockCommandContext.js.map +1 -1
- package/dist/src/ui/AppContainer.js +39 -36
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/auth/useAuth.js +20 -1
- package/dist/src/ui/auth/useAuth.js.map +1 -1
- package/dist/src/ui/commands/clearCommand.js +22 -10
- package/dist/src/ui/commands/clearCommand.js.map +1 -1
- package/dist/src/ui/commands/languageCommand.js +41 -8
- package/dist/src/ui/commands/languageCommand.js.map +1 -1
- package/dist/src/ui/commands/languageCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/languageCommand.test.js +463 -0
- package/dist/src/ui/commands/languageCommand.test.js.map +1 -0
- package/dist/src/ui/commands/modelCommand.js +4 -2
- package/dist/src/ui/commands/modelCommand.js.map +1 -1
- package/dist/src/ui/commands/quitCommand.d.ts +0 -1
- package/dist/src/ui/commands/quitCommand.js +0 -27
- package/dist/src/ui/commands/quitCommand.js.map +1 -1
- package/dist/src/ui/commands/reviewCommand.d.ts +9 -0
- package/dist/src/ui/commands/reviewCommand.js +1267 -0
- package/dist/src/ui/commands/reviewCommand.js.map +1 -0
- package/dist/src/ui/commands/reviewCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/reviewCommand.test.js +545 -0
- package/dist/src/ui/commands/reviewCommand.test.js.map +1 -0
- package/dist/src/ui/commands/setupGithubCommand.js +11 -10
- package/dist/src/ui/commands/setupGithubCommand.js.map +1 -1
- package/dist/src/ui/commands/setupGithubCommand.test.js +14 -14
- package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +4 -9
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/commands/ums/unittestCommand.d.ts +9 -0
- package/dist/src/ui/commands/ums/unittestCommand.js +387 -0
- package/dist/src/ui/commands/ums/unittestCommand.js.map +1 -0
- package/dist/src/ui/components/Composer.test.js +1 -2
- package/dist/src/ui/components/Composer.test.js.map +1 -1
- package/dist/src/ui/components/ContextUsageDisplay.d.ts +3 -2
- package/dist/src/ui/components/ContextUsageDisplay.js +8 -2
- package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
- package/dist/src/ui/components/DialogManager.js +3 -19
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/Footer.js +2 -3
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Help.js +13 -2
- package/dist/src/ui/components/Help.js.map +1 -1
- package/dist/src/ui/components/Help.test.js +6 -0
- package/dist/src/ui/components/Help.test.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +3 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +6 -4
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.d.ts +3 -1
- package/dist/src/ui/components/ModelDialog.js +25 -5
- package/dist/src/ui/components/ModelDialog.js.map +1 -1
- package/dist/src/ui/components/OpenAIKeyPrompt.d.ts +3 -0
- package/dist/src/ui/components/OpenAIKeyPrompt.js +1 -0
- package/dist/src/ui/components/OpenAIKeyPrompt.js.map +1 -1
- package/dist/src/ui/components/ResumeSessionPicker.d.ts +10 -0
- package/dist/src/ui/components/ResumeSessionPicker.js +249 -0
- package/dist/src/ui/components/ResumeSessionPicker.js.map +1 -0
- package/dist/src/ui/components/SessionSummaryDisplay.js +10 -2
- package/dist/src/ui/components/SessionSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +2 -2
- package/dist/src/ui/components/SuggestionsDisplay.js +3 -2
- package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
- package/dist/src/ui/components/messages/GeminiThoughtMessage.d.ts +18 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessage.js +14 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessage.js.map +1 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessageContent.d.ts +18 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessageContent.js +14 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessageContent.js.map +1 -0
- package/dist/src/ui/components/subagents/manage/AgentEditStep.js +4 -1
- package/dist/src/ui/components/subagents/manage/AgentEditStep.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentSelectionStep.js +2 -2
- package/dist/src/ui/components/subagents/manage/AgentSelectionStep.js.map +1 -1
- package/dist/src/ui/components/ums/UMSKeyPrompt.d.ts +1 -1
- package/dist/src/ui/components/ums/UMSKeyPrompt.js +23 -3
- package/dist/src/ui/components/ums/UMSKeyPrompt.js.map +1 -1
- package/dist/src/ui/components/ums/umsStartupWarnings.d.ts +6 -0
- package/dist/src/ui/components/ums/umsStartupWarnings.js +47 -0
- package/dist/src/ui/components/ums/umsStartupWarnings.js.map +1 -0
- package/dist/src/ui/contexts/SessionContext.d.ts +2 -0
- package/dist/src/ui/contexts/SessionContext.js +18 -10
- package/dist/src/ui/contexts/SessionContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +1 -3
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +2 -8
- package/dist/src/ui/hooks/slashCommandProcessor.js +68 -101
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAttentionNotifications.d.ts +3 -1
- package/dist/src/ui/hooks/useAttentionNotifications.js +10 -5
- package/dist/src/ui/hooks/useAttentionNotifications.js.map +1 -1
- package/dist/src/ui/hooks/useAttentionNotifications.test.js +39 -2
- package/dist/src/ui/hooks/useAttentionNotifications.test.js.map +1 -1
- package/dist/src/ui/hooks/useDialogClose.d.ts +0 -3
- package/dist/src/ui/hooks/useDialogClose.js +0 -2
- package/dist/src/ui/hooks/useDialogClose.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +49 -9
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useLogger.d.ts +1 -1
- package/dist/src/ui/hooks/useLogger.js +6 -3
- package/dist/src/ui/hooks/useLogger.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +2 -2
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.js +9 -1
- package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +36 -31
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +1 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/models/availableModels.d.ts +8 -2
- package/dist/src/ui/models/availableModels.js +24 -35
- package/dist/src/ui/models/availableModels.js.map +1 -1
- package/dist/src/ui/noninteractive/nonInteractiveUi.js +0 -1
- package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -1
- package/dist/src/ui/types.d.ts +9 -14
- package/dist/src/ui/types.js +0 -1
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/InlineMarkdownRenderer.d.ts +1 -0
- package/dist/src/ui/utils/InlineMarkdownRenderer.js +2 -2
- package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.d.ts +1 -0
- package/dist/src/ui/utils/MarkdownDisplay.js +13 -13
- package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
- package/dist/src/ui/utils/formatters.d.ts +6 -0
- package/dist/src/ui/utils/formatters.js +31 -0
- package/dist/src/ui/utils/formatters.js.map +1 -1
- package/dist/src/ui/utils/formatters.test.js +51 -2
- package/dist/src/ui/utils/formatters.test.js.map +1 -1
- package/dist/src/ui/utils/resumeHistoryUtils.d.ts +19 -0
- package/dist/src/ui/utils/resumeHistoryUtils.js +263 -0
- package/dist/src/ui/utils/resumeHistoryUtils.js.map +1 -0
- package/dist/src/ui/utils/resumeHistoryUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/resumeHistoryUtils.test.js +248 -0
- package/dist/src/ui/utils/resumeHistoryUtils.test.js.map +1 -0
- package/dist/src/utils/attentionNotification.d.ts +1 -0
- package/dist/src/utils/attentionNotification.js +4 -0
- package/dist/src/utils/attentionNotification.js.map +1 -1
- package/dist/src/utils/gitUtils.js +3 -3
- package/dist/src/utils/gitUtils.js.map +1 -1
- package/dist/src/utils/nonInteractiveHelpers.js +1 -1
- package/dist/src/utils/nonInteractiveHelpers.js.map +1 -1
- package/dist/src/utils/nonInteractiveHelpers.test.js +1 -1
- package/dist/src/utils/nonInteractiveHelpers.test.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.js +1 -1
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/dist/src/nonInteractive/control/controllers/mcpController.d.ts +0 -42
- package/dist/src/nonInteractive/control/controllers/mcpController.js +0 -205
- package/dist/src/nonInteractive/control/controllers/mcpController.js.map +0 -1
- package/dist/src/ui/commands/chatCommand.d.ts +0 -9
- package/dist/src/ui/commands/chatCommand.js +0 -351
- package/dist/src/ui/commands/chatCommand.js.map +0 -1
- package/dist/src/ui/commands/corgiCommand.d.ts +0 -7
- package/dist/src/ui/commands/corgiCommand.js +0 -16
- package/dist/src/ui/commands/corgiCommand.js.map +0 -1
- package/dist/src/ui/components/QuitConfirmationDialog.d.ts +0 -17
- package/dist/src/ui/components/QuitConfirmationDialog.js +0 -49
- package/dist/src/ui/components/QuitConfirmationDialog.js.map +0 -1
- package/dist/src/ui/hooks/usePromptCompletion.d.ts +0 -23
- package/dist/src/ui/hooks/usePromptCompletion.js +0 -177
- package/dist/src/ui/hooks/usePromptCompletion.js.map +0 -1
- package/dist/src/ui/hooks/useQuitConfirmation.d.ts +0 -14
- package/dist/src/ui/hooks/useQuitConfirmation.js +0 -36
- package/dist/src/ui/hooks/useQuitConfirmation.js.map +0 -1
- package/dist/src/zed-integration/acp.js.map +0 -1
- package/dist/src/zed-integration/fileSystemService.js.map +0 -1
- package/dist/src/zed-integration/schema.js.map +0 -1
- package/dist/src/zed-integration/zedIntegration.d.ts +0 -17
- package/dist/src/zed-integration/zedIntegration.js +0 -1135
- package/dist/src/zed-integration/zedIntegration.js.map +0 -1
|
@@ -0,0 +1,1267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review Command - 智能代码评审命令
|
|
3
|
+
*
|
|
4
|
+
* 提供多模式代码评审功能,支持智能分批处理和文件过滤
|
|
5
|
+
*
|
|
6
|
+
* @module reviewCommand
|
|
7
|
+
*/
|
|
8
|
+
import { CommandKind } from './types.js';
|
|
9
|
+
import { simpleGit } from 'simple-git';
|
|
10
|
+
import { resolve } from 'path';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// 常量配置
|
|
13
|
+
// ============================================================================
|
|
14
|
+
const REVIEW_THRESHOLDS = {
|
|
15
|
+
/** 超过此数量的文件被认为是大型变更 */
|
|
16
|
+
LARGE_FILE_THRESHOLD: 50,
|
|
17
|
+
/** 超过此数量的行变更被认为是大型变更 */
|
|
18
|
+
LARGE_CHANGE_THRESHOLD: 3000,
|
|
19
|
+
/** 每批最多处理的文件数 */
|
|
20
|
+
MAX_FILES_PER_BATCH: 20,
|
|
21
|
+
/** 每批最多处理的行变更数 */
|
|
22
|
+
MAX_CHANGES_PER_BATCH: 2000,
|
|
23
|
+
};
|
|
24
|
+
const COMMIT_HASH_PATTERN = /^[0-9a-f]{7,}$/i;
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// 错误类型
|
|
27
|
+
// ============================================================================
|
|
28
|
+
class ReviewCommandError extends Error {
|
|
29
|
+
code;
|
|
30
|
+
constructor(message, code) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = 'ReviewCommandError';
|
|
33
|
+
this.code = code;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// 工具类
|
|
38
|
+
// ============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Git 操作工具类
|
|
41
|
+
*/
|
|
42
|
+
class GitUtils {
|
|
43
|
+
/**
|
|
44
|
+
* 检测字符串是否像 commit hash(至少7位十六进制字符)
|
|
45
|
+
*/
|
|
46
|
+
static looksLikeCommitHash(str) {
|
|
47
|
+
return COMMIT_HASH_PATTERN.test(str);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 根据 review 模式构建 git comparison 字符串
|
|
51
|
+
*/
|
|
52
|
+
static buildComparisonString(options, format = 'command') {
|
|
53
|
+
const { mode, commit1, commit2, branch1, branch2 } = options;
|
|
54
|
+
const separator = format === 'range' ? '..' : ' ';
|
|
55
|
+
switch (mode) {
|
|
56
|
+
case 'default':
|
|
57
|
+
return 'HEAD';
|
|
58
|
+
case 'commit':
|
|
59
|
+
if (commit2) {
|
|
60
|
+
return `${commit1}${separator}${commit2}`;
|
|
61
|
+
}
|
|
62
|
+
return `${commit1}^${separator}${commit1}`;
|
|
63
|
+
case 'branch':
|
|
64
|
+
if (branch2) {
|
|
65
|
+
return `${branch1}${separator}${branch2}`;
|
|
66
|
+
}
|
|
67
|
+
return `main${separator}${branch1}`;
|
|
68
|
+
default:
|
|
69
|
+
throw new ReviewCommandError(`Unknown review mode: ${mode}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 构建 git diff --stat 命令
|
|
74
|
+
*/
|
|
75
|
+
static buildDiffStatCommand(options) {
|
|
76
|
+
const comparison = this.buildComparisonString(options, 'command');
|
|
77
|
+
return `git diff --stat ${comparison}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 构建 git diff 命令
|
|
81
|
+
*/
|
|
82
|
+
static buildDiffCommand(options) {
|
|
83
|
+
const comparison = this.buildComparisonString(options, 'command');
|
|
84
|
+
return `git diff ${comparison}`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 构建 git diff 参数数组
|
|
88
|
+
*/
|
|
89
|
+
static buildDiffArgs(options, command) {
|
|
90
|
+
const baseArgs = command === 'diff-stat'
|
|
91
|
+
? ['diff', '--stat']
|
|
92
|
+
: ['diff', '--name-only'];
|
|
93
|
+
const comparison = this.buildComparisonString(options, 'range');
|
|
94
|
+
return [...baseArgs, comparison];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// 参数解析器
|
|
99
|
+
// ============================================================================
|
|
100
|
+
/**
|
|
101
|
+
* Review 命令参数解析器
|
|
102
|
+
*/
|
|
103
|
+
class ReviewArgumentParser {
|
|
104
|
+
/**
|
|
105
|
+
* 解析 review 命令的参数
|
|
106
|
+
*/
|
|
107
|
+
static parse(args) {
|
|
108
|
+
const trimmedArgs = args.trim();
|
|
109
|
+
if (!trimmedArgs) {
|
|
110
|
+
return { mode: 'default' };
|
|
111
|
+
}
|
|
112
|
+
const parts = trimmedArgs.split(/\s+/);
|
|
113
|
+
// 提取 --prompt-enhance 和 --notice 参数
|
|
114
|
+
const enhanceOptions = this.extractEnhanceOption(parts);
|
|
115
|
+
const noticeOption = this.extractNoticeOption(parts);
|
|
116
|
+
const filteredParts = this.removeOptions(parts);
|
|
117
|
+
if (filteredParts.length === 0) {
|
|
118
|
+
return { mode: 'default', ...enhanceOptions, ...noticeOption };
|
|
119
|
+
}
|
|
120
|
+
const command = filteredParts[0];
|
|
121
|
+
switch (command) {
|
|
122
|
+
case '-commit':
|
|
123
|
+
return { ...this.parseCommitMode(filteredParts), ...enhanceOptions, ...noticeOption };
|
|
124
|
+
case '-branch':
|
|
125
|
+
return { ...this.parseBranchMode(filteredParts), ...enhanceOptions, ...noticeOption };
|
|
126
|
+
default:
|
|
127
|
+
return { ...this.handleUnknownCommand(filteredParts), ...enhanceOptions, ...noticeOption };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 提取 --prompt-enhance 参数
|
|
132
|
+
*/
|
|
133
|
+
static extractEnhanceOption(parts) {
|
|
134
|
+
for (let i = 0; i < parts.length; i++) {
|
|
135
|
+
if (parts[i] === '--prompt-enhance' && i + 1 < parts.length) {
|
|
136
|
+
return { promptEnhancePath: parts[i + 1] };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* 提取 --notice 参数
|
|
143
|
+
*/
|
|
144
|
+
static extractNoticeOption(parts) {
|
|
145
|
+
return { notice: parts.includes('--notice') };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 移除所有选项参数(--prompt-enhance 和 --notice)
|
|
149
|
+
*/
|
|
150
|
+
static removeOptions(parts) {
|
|
151
|
+
const filtered = [];
|
|
152
|
+
for (let i = 0; i < parts.length; i++) {
|
|
153
|
+
if (parts[i] === '--prompt-enhance') {
|
|
154
|
+
i++; // 跳过参数值
|
|
155
|
+
}
|
|
156
|
+
else if (parts[i] === '--notice') {
|
|
157
|
+
// 跳过该标志
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
filtered.push(parts[i]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return filtered;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 解析 commit 模式参数
|
|
167
|
+
*/
|
|
168
|
+
static parseCommitMode(parts) {
|
|
169
|
+
if (parts.length < 2) {
|
|
170
|
+
throw new ReviewCommandError('Commit mode requires at least one commit hash', 'MISSING_COMMIT_HASH');
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
mode: 'commit',
|
|
174
|
+
commit1: parts[1],
|
|
175
|
+
commit2: parts[2],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 解析 branch 模式参数
|
|
180
|
+
*/
|
|
181
|
+
static parseBranchMode(parts) {
|
|
182
|
+
if (parts.length < 2) {
|
|
183
|
+
throw new ReviewCommandError('Branch mode requires at least one branch name', 'MISSING_BRANCH_NAME');
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
mode: 'branch',
|
|
187
|
+
branch1: parts[1],
|
|
188
|
+
branch2: parts[2],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 处理未知命令或可能的语法错误
|
|
193
|
+
*/
|
|
194
|
+
static handleUnknownCommand(parts) {
|
|
195
|
+
// 检测是否忘记添加 -commit 或 -branch 参数
|
|
196
|
+
if (parts.length >= 1 && GitUtils.looksLikeCommitHash(parts[0])) {
|
|
197
|
+
throw new ReviewCommandError(this.buildUsageErrorMessage(parts[0], parts[1]), 'INVALID_SYNTAX');
|
|
198
|
+
}
|
|
199
|
+
// 默认模式
|
|
200
|
+
return { mode: 'default' };
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 构建使用错误提示信息
|
|
204
|
+
*/
|
|
205
|
+
static buildUsageErrorMessage(commitHash, secondArg) {
|
|
206
|
+
const example = secondArg
|
|
207
|
+
? `/review -commit ${commitHash} ${secondArg}`
|
|
208
|
+
: `/review -commit ${commitHash}`;
|
|
209
|
+
return `Invalid syntax. Did you forget '-commit'?
|
|
210
|
+
|
|
211
|
+
Usage:
|
|
212
|
+
/review -commit <commit1> [commit2]
|
|
213
|
+
/review -branch <branch1> [branch2]
|
|
214
|
+
/review (for uncommitted changes)
|
|
215
|
+
|
|
216
|
+
Example:
|
|
217
|
+
${example}`;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// ============================================================================
|
|
221
|
+
// Git 统计分析器
|
|
222
|
+
// ============================================================================
|
|
223
|
+
/**
|
|
224
|
+
* Git 统计分析器
|
|
225
|
+
*/
|
|
226
|
+
class GitStatsAnalyzer {
|
|
227
|
+
/**
|
|
228
|
+
* 获取 git diff 统计信息
|
|
229
|
+
*/
|
|
230
|
+
static async getDiffStats(projectRoot, options) {
|
|
231
|
+
const git = this.createGitInstance(projectRoot);
|
|
232
|
+
try {
|
|
233
|
+
const diffArgs = GitUtils.buildDiffArgs(options, 'diff-stat');
|
|
234
|
+
const statsOutput = await git.raw(diffArgs);
|
|
235
|
+
return this.parseStatsOutput(statsOutput);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
throw new ReviewCommandError(`Failed to get git diff stats: ${error instanceof Error ? error.message : String(error)}`, 'GIT_STATS_ERROR');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 创建 Git 实例
|
|
243
|
+
*/
|
|
244
|
+
static createGitInstance(projectRoot) {
|
|
245
|
+
return simpleGit(projectRoot, {
|
|
246
|
+
binary: 'git',
|
|
247
|
+
maxConcurrentProcesses: 1,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* 解析 git diff --stat 输出
|
|
252
|
+
*/
|
|
253
|
+
static parseStatsOutput(statsOutput) {
|
|
254
|
+
const lines = statsOutput.trim().split('\n');
|
|
255
|
+
if (lines.length === 0 || !lines[0]) {
|
|
256
|
+
return this.createEmptyStats();
|
|
257
|
+
}
|
|
258
|
+
const fileCount = this.calculateFileCount(lines);
|
|
259
|
+
const { addedLines, deletedLines } = this.calculateLineChanges(lines);
|
|
260
|
+
return {
|
|
261
|
+
fileCount,
|
|
262
|
+
addedLines,
|
|
263
|
+
deletedLines,
|
|
264
|
+
totalChanges: addedLines + deletedLines,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 创建空的统计对象
|
|
269
|
+
*/
|
|
270
|
+
static createEmptyStats() {
|
|
271
|
+
return {
|
|
272
|
+
fileCount: 0,
|
|
273
|
+
addedLines: 0,
|
|
274
|
+
deletedLines: 0,
|
|
275
|
+
totalChanges: 0,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* 计算文件数量
|
|
280
|
+
*/
|
|
281
|
+
static calculateFileCount(lines) {
|
|
282
|
+
return lines.length > 1 ? lines.length - 1 : 0;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* 计算行变更统计
|
|
286
|
+
*/
|
|
287
|
+
static calculateLineChanges(lines) {
|
|
288
|
+
let addedLines = 0;
|
|
289
|
+
let deletedLines = 0;
|
|
290
|
+
for (const line of lines) {
|
|
291
|
+
const lineStats = this.parseLineStats(line);
|
|
292
|
+
if (lineStats) {
|
|
293
|
+
addedLines += lineStats.addedLines;
|
|
294
|
+
deletedLines += lineStats.deletedLines;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return { addedLines, deletedLines };
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 解析单行统计信息
|
|
301
|
+
* 匹配格式:'file.ts | 10 ++++------'
|
|
302
|
+
*/
|
|
303
|
+
static parseLineStats(line) {
|
|
304
|
+
const match = line.match(/\|\s*(\d+)\s*([+-]+)?/);
|
|
305
|
+
if (!match)
|
|
306
|
+
return null;
|
|
307
|
+
const totalChanges = parseInt(match[1], 10);
|
|
308
|
+
const signs = match[2] || '';
|
|
309
|
+
const additions = (signs.match(/\+/g) || []).length;
|
|
310
|
+
const deletions = (signs.match(/-/g) || []).length;
|
|
311
|
+
if (additions === 0 && deletions === 0) {
|
|
312
|
+
// 如果没有符号,估算各占一半
|
|
313
|
+
return {
|
|
314
|
+
addedLines: Math.floor(totalChanges / 2),
|
|
315
|
+
deletedLines: Math.ceil(totalChanges / 2),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// 按比例计算实际行数
|
|
319
|
+
const total = additions + deletions;
|
|
320
|
+
return {
|
|
321
|
+
addedLines: Math.round((additions / total) * totalChanges),
|
|
322
|
+
deletedLines: Math.round((deletions / total) * totalChanges),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// ============================================================================
|
|
327
|
+
// Prompt 构建器
|
|
328
|
+
// ============================================================================
|
|
329
|
+
/**
|
|
330
|
+
* 代码评审 Prompt 构建器
|
|
331
|
+
*/
|
|
332
|
+
class ReviewPromptBuilder {
|
|
333
|
+
/**
|
|
334
|
+
* 构建小型变更的评审 prompt
|
|
335
|
+
*/
|
|
336
|
+
static buildSmallChangePrompt(stats, projectRoot, gitDiffCommand, options) {
|
|
337
|
+
return this.buildPromptTemplate([
|
|
338
|
+
this.buildHeader(stats),
|
|
339
|
+
this.buildExecutionFlow(options),
|
|
340
|
+
this.buildFileFilteringPhase(projectRoot, gitDiffCommand),
|
|
341
|
+
this.buildCodeReviewPhase(projectRoot, gitDiffCommand, options),
|
|
342
|
+
this.buildReportGenerationPhase(stats),
|
|
343
|
+
this.buildQualityCheckPhase(projectRoot, options),
|
|
344
|
+
this.buildCallToAction()
|
|
345
|
+
]);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* 构建大型变更的评审 prompt
|
|
349
|
+
*/
|
|
350
|
+
static buildLargeChangePrompt(stats, projectRoot, gitDiffStatCommand, options) {
|
|
351
|
+
return this.buildPromptTemplate([
|
|
352
|
+
this.buildLargeChangeHeader(stats),
|
|
353
|
+
this.buildLargeChangeExecutionFlow(options),
|
|
354
|
+
this.buildAnalysisPhase(projectRoot, gitDiffStatCommand),
|
|
355
|
+
this.buildBatchReviewPhase(projectRoot, options),
|
|
356
|
+
this.buildSummaryPhase(stats, projectRoot, options),
|
|
357
|
+
this.buildLargeChangeCallToAction()
|
|
358
|
+
]);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* 构建 prompt 模板
|
|
362
|
+
*/
|
|
363
|
+
static buildPromptTemplate(sections) {
|
|
364
|
+
return sections.join('\n\n---\n\n');
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* 构建变更摘要头部
|
|
368
|
+
*/
|
|
369
|
+
static buildHeader(stats) {
|
|
370
|
+
return `# 代码评审任务
|
|
371
|
+
|
|
372
|
+
## 变更摘要
|
|
373
|
+
|
|
374
|
+
- **文件总数**: ${stats.fileCount} 个
|
|
375
|
+
- **新增行数**: ${stats.addedLines} 行
|
|
376
|
+
- **删除行数**: ${stats.deletedLines} 行
|
|
377
|
+
- **总变更量**: ${stats.totalChanges} 行
|
|
378
|
+
|
|
379
|
+
变更规模适中,将使用 code-reviewer 一次性完成评审。`;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* 构建大型变更头部
|
|
383
|
+
*/
|
|
384
|
+
static buildLargeChangeHeader(stats) {
|
|
385
|
+
return `# 代码评审任务 - 大型变更检测
|
|
386
|
+
|
|
387
|
+
## 变更摘要
|
|
388
|
+
|
|
389
|
+
- **文件总数**: ${stats.fileCount} 个
|
|
390
|
+
- **新增行数**: ${stats.addedLines} 行
|
|
391
|
+
- **删除行数**: ${stats.deletedLines} 行
|
|
392
|
+
- **总变更量**: ${stats.totalChanges} 行
|
|
393
|
+
|
|
394
|
+
由于这是一个**大型变更**(超过 ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个文件或 ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行代码),需要分批次进行评审。`;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* 构建执行流程
|
|
398
|
+
*/
|
|
399
|
+
static buildExecutionFlow(options) {
|
|
400
|
+
const tasks = [
|
|
401
|
+
'1. AI 智能文件过滤',
|
|
402
|
+
'2. AI 智能代码评审',
|
|
403
|
+
'3. 生成评审报告',
|
|
404
|
+
'4. 评审报告质量检查'
|
|
405
|
+
];
|
|
406
|
+
if (options?.notice) {
|
|
407
|
+
tasks.push('5. 生成完成标志文件');
|
|
408
|
+
}
|
|
409
|
+
return `## 执行流程
|
|
410
|
+
|
|
411
|
+
请使用 **TodoWrite** 工具创建并跟踪以下任务:
|
|
412
|
+
|
|
413
|
+
\`\`\`
|
|
414
|
+
${tasks.join('\n')}
|
|
415
|
+
\`\`\``;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* 构建大型变更执行流程
|
|
419
|
+
*/
|
|
420
|
+
static buildLargeChangeExecutionFlow(options) {
|
|
421
|
+
const noticeTask = options?.notice ? '\n6. 生成完成标志文件' : '';
|
|
422
|
+
return `## 执行流程(分三个阶段)
|
|
423
|
+
|
|
424
|
+
### 阶段一:智能分析与过滤
|
|
425
|
+
|
|
426
|
+
请使用 **TodoWrite** 创建初始任务列表:
|
|
427
|
+
|
|
428
|
+
\`\`\`
|
|
429
|
+
1. 分析变更文件
|
|
430
|
+
2. AI 智能文件过滤
|
|
431
|
+
3. AI 智能制定分批次评审策略
|
|
432
|
+
4. 复核分批次评审策略
|
|
433
|
+
5. [后续任务待分批完成后添加]${noticeTask}
|
|
434
|
+
\`\`\``;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* 构建文件过滤阶段
|
|
438
|
+
*/
|
|
439
|
+
static buildFileFilteringPhase(projectRoot, gitDiffCommand) {
|
|
440
|
+
return `## 第一阶段:AI 智能文件过滤
|
|
441
|
+
|
|
442
|
+
### 步骤 1:获取变更文件列表并进行智能过滤
|
|
443
|
+
|
|
444
|
+
执行以下 bash 命令获取变更文件统计:
|
|
445
|
+
|
|
446
|
+
\`\`\`bash
|
|
447
|
+
cd ${projectRoot}
|
|
448
|
+
git diff --stat ${gitDiffCommand.replace('git diff', '').trim()}
|
|
449
|
+
\`\`\`
|
|
450
|
+
|
|
451
|
+
**然后对变更文件进行 AI 智能过滤分析**:
|
|
452
|
+
|
|
453
|
+
${this.buildFileFilteringRules()}
|
|
454
|
+
|
|
455
|
+
${this.buildFileFilteringOutputFormat()}
|
|
456
|
+
|
|
457
|
+
### 步骤 2:检查过滤结果
|
|
458
|
+
|
|
459
|
+
**重要**:如果所有文件都被过滤掉(需要评审的文件为 0),则:
|
|
460
|
+
1. 输出消息:"✅ 本次变更无需代码评审(全部为文档、配置或自动生成文件)"
|
|
461
|
+
2. 创建简单的 CODE_REVIEW.md 文件说明情况
|
|
462
|
+
3. 结束评审流程
|
|
463
|
+
|
|
464
|
+
如果还有需要评审的文件,继续下一步。`;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* 构建文件过滤规则
|
|
468
|
+
*/
|
|
469
|
+
static buildFileFilteringRules() {
|
|
470
|
+
return `**过滤原则**:
|
|
471
|
+
1. **需要评审的文件**:
|
|
472
|
+
- 所有源代码文件(.java, .ts, .js, .py, .go, .rs, etc.)
|
|
473
|
+
- 配置文件(特别是涉及权限、安全、路由的配置)
|
|
474
|
+
- SQL/数据库迁移文件
|
|
475
|
+
- API定义文件(OpenAPI, GraphQL schemas)
|
|
476
|
+
- 关键的构建脚本(如果包含复杂逻辑)
|
|
477
|
+
|
|
478
|
+
2. **可以过滤掉的文件**:
|
|
479
|
+
- 纯文档文件(README.md, CHANGELOG.md等,除非是技术文档)
|
|
480
|
+
- 依赖锁文件(package-lock.json, yarn.lock, Cargo.lock等)
|
|
481
|
+
- 二进制文件和媒体文件(图片、字体、音视频等)
|
|
482
|
+
- 自动生成的代码(如果有明确标记)
|
|
483
|
+
- 构建产物和编译输出
|
|
484
|
+
- IDE配置文件(.vscode, .idea等)
|
|
485
|
+
- Git相关配置(.gitignore, .gitattributes)
|
|
486
|
+
|
|
487
|
+
3. **不确定时的策略**:
|
|
488
|
+
- 当无法确定文件类型或用途时,**倾向于包含而不是排除**
|
|
489
|
+
- 对于混合内容(如包含代码示例的markdown),考虑保留
|
|
490
|
+
- 对于小文件(<50行),包含成本低,可以保留`;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* 构建文件过滤输出格式
|
|
494
|
+
*/
|
|
495
|
+
static buildFileFilteringOutputFormat() {
|
|
496
|
+
return `**请使用以下格式输出过滤结果**:
|
|
497
|
+
|
|
498
|
+
\`\`\`markdown
|
|
499
|
+
## 文件过滤分析
|
|
500
|
+
|
|
501
|
+
### 需要评审的文件(共 X 个,约 Y 行变更)
|
|
502
|
+
|
|
503
|
+
1. [文件路径] - [变更行数] 行变更
|
|
504
|
+
2. [文件路径] - [变更行数] 行变更
|
|
505
|
+
...
|
|
506
|
+
|
|
507
|
+
### 已过滤的文件(共 Z 个)
|
|
508
|
+
|
|
509
|
+
**[类别名称](N个)**:
|
|
510
|
+
- [文件路径] - [过滤原因]
|
|
511
|
+
...
|
|
512
|
+
|
|
513
|
+
### 统计
|
|
514
|
+
- 原始变更文件总数:X 个
|
|
515
|
+
- 需要评审:X 个(Y%)
|
|
516
|
+
- 已过滤:Z 个(W%)
|
|
517
|
+
- 预计需要评审的代码行数:约 N 行
|
|
518
|
+
\`\`\``;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* 构建代码评审阶段
|
|
522
|
+
*/
|
|
523
|
+
static buildCodeReviewPhase(projectRoot, gitDiffCommand, options) {
|
|
524
|
+
return `## 第二阶段:执行代码评审
|
|
525
|
+
|
|
526
|
+
### 步骤 3:AI 智能代码评审
|
|
527
|
+
|
|
528
|
+
使用 **Task tool** 调用 code-reviewer subagent,**仅对需要评审的文件进行评审**:
|
|
529
|
+
|
|
530
|
+
\`\`\`typescript
|
|
531
|
+
{
|
|
532
|
+
subagent_type: 'code-reviewer',
|
|
533
|
+
description: '代码评审 - 完整变更',
|
|
534
|
+
prompt: \`${this.buildSubagentPrompt(projectRoot, gitDiffCommand, options)}\`
|
|
535
|
+
}
|
|
536
|
+
\`\`\`
|
|
537
|
+
|
|
538
|
+
### 步骤 4:等待评审完成
|
|
539
|
+
|
|
540
|
+
subagent 会自动:
|
|
541
|
+
1. 执行 git diff 命令获取变更内容
|
|
542
|
+
2. 使用 Read tool 读取相关文件(重点关注需要评审的文件)
|
|
543
|
+
3. 按照六个维度进行深度代码评审
|
|
544
|
+
4. 进行自我检查确保评审质量`;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* 构建附加规则部分
|
|
548
|
+
*/
|
|
549
|
+
static buildEnhancedRulesSection(projectRoot, options) {
|
|
550
|
+
// 构建附加规则文件列表
|
|
551
|
+
const enhancedRules = [];
|
|
552
|
+
enhancedRules.push(`${projectRoot}/UMS.md`);
|
|
553
|
+
if (options?.promptEnhancePath) {
|
|
554
|
+
enhancedRules.push(resolve(projectRoot, options.promptEnhancePath));
|
|
555
|
+
}
|
|
556
|
+
return enhancedRules.length > 0 ? `
|
|
557
|
+
## 附加规则
|
|
558
|
+
|
|
559
|
+
尝试读取以下文件(如果文件不存在则跳过):
|
|
560
|
+
|
|
561
|
+
${enhancedRules.map(file => `- ${file}`).join('\n')}
|
|
562
|
+
|
|
563
|
+
从这些文件中提取开发规范、编码标准和评审规则,作为附加规则。
|
|
564
|
+
|
|
565
|
+
如果代码违反了附加规则,将其报告为问题并给出适当的严重性和维度。
|
|
566
|
+
|
|
567
|
+
` : '';
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* 构建 subagent prompt
|
|
571
|
+
*/
|
|
572
|
+
static buildSubagentPrompt(projectRoot, gitDiffCommand, options) {
|
|
573
|
+
return `请按照你的标准评审流程进行全面代码评审。
|
|
574
|
+
|
|
575
|
+
## 变更信息
|
|
576
|
+
|
|
577
|
+
- 需要评审的文件:X 个
|
|
578
|
+
- 变更行数:约 Y 行
|
|
579
|
+
|
|
580
|
+
## 需要评审的文件列表
|
|
581
|
+
|
|
582
|
+
- file1.ts
|
|
583
|
+
- file2.ts
|
|
584
|
+
- file3.ts
|
|
585
|
+
...
|
|
586
|
+
|
|
587
|
+
## 获取变更内容
|
|
588
|
+
|
|
589
|
+
请执行以下命令获取代码变更:
|
|
590
|
+
|
|
591
|
+
\\\`\\\`\\\`bash
|
|
592
|
+
cd ${projectRoot}
|
|
593
|
+
${gitDiffCommand} -- file1.ts file2.ts file3.ts
|
|
594
|
+
\\\`\\\`\\\`
|
|
595
|
+
|
|
596
|
+
(将 file1.ts file2.ts file3.ts 替换为实际的批次文件列表)
|
|
597
|
+
|
|
598
|
+
${this.buildEnhancedRulesSection(projectRoot, options)}
|
|
599
|
+
|
|
600
|
+
## 结果检查
|
|
601
|
+
|
|
602
|
+
返回结果前必须自检,剔除误报的问题,修正不准确的问题:
|
|
603
|
+
|
|
604
|
+
**检查 1:文件覆盖完整性**
|
|
605
|
+
- [ ] 已评审所有指定文件
|
|
606
|
+
- [ ] 无法评审的文件已说明原因
|
|
607
|
+
|
|
608
|
+
**检查 2:输出格式完整性**
|
|
609
|
+
- [ ] 每个问题都有 [严重性][维度] 标签
|
|
610
|
+
- [ ] CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
|
|
611
|
+
- [ ] MEDIUM/LOW 有:问题描述、简要说明
|
|
612
|
+
|
|
613
|
+
**检查 3:分类准确性**
|
|
614
|
+
- [ ] 严重性符合 SEVERITY_GUIDELINES
|
|
615
|
+
- [ ] 维度符合 REVIEW_DIMENSIONS
|
|
616
|
+
|
|
617
|
+
**检查 4:严重性合理性**
|
|
618
|
+
对每个 CRITICAL/HIGH 问题:
|
|
619
|
+
- [ ] 有明确代码证据证明是 bug?
|
|
620
|
+
- [ ] "会出错"还是"可能出错"?
|
|
621
|
+
- [ ] 代码是否已有防御/错误处理?
|
|
622
|
+
- [ ] bug 修复还是改进建议?
|
|
623
|
+
- [ ] 能写测试用例重现?
|
|
624
|
+
|
|
625
|
+
**不确定或"否"→ 必须降低严重性!**
|
|
626
|
+
|
|
627
|
+
**检查 5:Diff 覆盖验证(重要)**
|
|
628
|
+
对每个报告的问题:
|
|
629
|
+
- [ ] 问题所在代码行确实是在 diff 中
|
|
630
|
+
- [ ] 问题确实是本次变更引入的,而非之前就存在
|
|
631
|
+
|
|
632
|
+
**如果是未变更的代码,必须删除该问题!**
|
|
633
|
+
|
|
634
|
+
**检查 6:误报风险(重要)**
|
|
635
|
+
- [ ] 问题是真实存在,而非基于假设
|
|
636
|
+
|
|
637
|
+
**如果是误报的问题,必须剔除!**`;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* 构建报告生成阶段
|
|
641
|
+
*/
|
|
642
|
+
static buildReportGenerationPhase(stats) {
|
|
643
|
+
return `## 第三阶段:生成最终报告
|
|
644
|
+
|
|
645
|
+
### 步骤 5:整理评审结果并生成报告
|
|
646
|
+
|
|
647
|
+
获取 subagent 的评审结果后,按照以下结构生成最终的评审报告,写入 \`CODE_REVIEW.md\`:
|
|
648
|
+
|
|
649
|
+
${this.buildReportTemplate(stats)}
|
|
650
|
+
|
|
651
|
+
**重要提示**
|
|
652
|
+
- 请**逐条提取**所有评审问题,不要总结或遗漏任何细节
|
|
653
|
+
- **每个问题必须包含文件路径**(从 subagent 输出中提取)
|
|
654
|
+
- 保持原始的代码示例、分析和修复建议的完整性
|
|
655
|
+
- 如果某个严重性级别下某个维度没有问题,则跳过该小节
|
|
656
|
+
- 确保统计表中的数字与实际问题数量一致`;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* 构建报告模板
|
|
660
|
+
*/
|
|
661
|
+
static buildReportTemplate(stats) {
|
|
662
|
+
return `\`\`\`markdown
|
|
663
|
+
# 代码评审报告
|
|
664
|
+
|
|
665
|
+
## 变更概览
|
|
666
|
+
|
|
667
|
+
- **原始文件总数**: ${stats.fileCount} 个
|
|
668
|
+
- **评审文件数**: X 个(Y% 需要评审)
|
|
669
|
+
- **过滤文件数**: Z 个(W% 无需评审)
|
|
670
|
+
- **新增行数**: ${stats.addedLines} 行
|
|
671
|
+
- **删除行数**: ${stats.deletedLines} 行
|
|
672
|
+
- **总变更量**: ${stats.totalChanges} 行
|
|
673
|
+
|
|
674
|
+
本次变更主要涉及 [简要概括主要变更内容]。
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## 📋 问题统计
|
|
679
|
+
|
|
680
|
+
| 严重性 | 正确性 | 安全性 | 性能 | 可维护性 | 架构设计 | 最佳实践 | 合计 |
|
|
681
|
+
|--------|--------|--------|------|----------|----------|----------|------|
|
|
682
|
+
| 🚨 CRITICAL | X | X | X | X | X | X | **X** |
|
|
683
|
+
| ⚠️ HIGH | X | X | X | X | X | X | **X** |
|
|
684
|
+
| 🔶 MEDIUM | X | X | X | X | X | X | **X** |
|
|
685
|
+
| 🔷 LOW | X | X | X | X | X | X | **X** |
|
|
686
|
+
| **合计** | **X** | **X** | **X** | **X** | **X** | **X** | **X** |
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## 🚨 严重问题 (CRITICAL)
|
|
691
|
+
|
|
692
|
+
[如果有CRITICAL级别问题,按维度组织]
|
|
693
|
+
|
|
694
|
+
### 🐛 正确性
|
|
695
|
+
|
|
696
|
+
#### [问题编号]. [问题标题]
|
|
697
|
+
**文件**: [文件路径]
|
|
698
|
+
|
|
699
|
+
**问题描述**:[简要说明]
|
|
700
|
+
|
|
701
|
+
**影响范围**:[如果影响调用方,说明影响]
|
|
702
|
+
|
|
703
|
+
**详细分析**:
|
|
704
|
+
- [详细分析内容]
|
|
705
|
+
|
|
706
|
+
**当前代码**:
|
|
707
|
+
\\\`\\\`\\\`language
|
|
708
|
+
[有问题的代码]
|
|
709
|
+
\\\`\\\`\\\`
|
|
710
|
+
|
|
711
|
+
**建议修复**:
|
|
712
|
+
\\\`\\\`\\\`language
|
|
713
|
+
[修复后的代码]
|
|
714
|
+
\\\`\\\`\\\`
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
[其他维度的CRITICAL问题,格式相同]
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## ⚠️ 高风险问题 (HIGH)
|
|
723
|
+
|
|
724
|
+
[按维度组织HIGH级别问题, 格式与CRITICAL相同]
|
|
725
|
+
|
|
726
|
+
---
|
|
727
|
+
|
|
728
|
+
## 🔶 中等风险问题 (MEDIUM)
|
|
729
|
+
|
|
730
|
+
[按维度组织MEDIUM级别问题, 格式与CRITICAL相同]
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## 🔷 低风险问题 (LOW)
|
|
735
|
+
|
|
736
|
+
[按维度组织LOW级别问题, 格式与CRITICAL相同]
|
|
737
|
+
|
|
738
|
+
---
|
|
739
|
+
|
|
740
|
+
## 📈 整体代码质量评估
|
|
741
|
+
|
|
742
|
+
### 优点
|
|
743
|
+
[列出代码的优点]
|
|
744
|
+
|
|
745
|
+
### 需要改进的方面
|
|
746
|
+
[列出需要改进的方面]
|
|
747
|
+
\`\`\``;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* 构建质量检查阶段
|
|
751
|
+
*/
|
|
752
|
+
static buildQualityCheckPhase(projectRoot, options) {
|
|
753
|
+
let content = `## 第四阶段:质量检查
|
|
754
|
+
|
|
755
|
+
### 步骤 6:评审结果完整性检查
|
|
756
|
+
|
|
757
|
+
生成报告后,请进行以下检查:
|
|
758
|
+
|
|
759
|
+
#### 1. 数量一致性检查
|
|
760
|
+
- 统计表中的总问题数 = 报告中实际列出的问题数
|
|
761
|
+
- 每个维度的计数 = 该维度下实际列出的问题数
|
|
762
|
+
- 每个严重性级别的计数 = 该级别下实际列出的问题数
|
|
763
|
+
|
|
764
|
+
#### 2. 结构完整性检查
|
|
765
|
+
- 确认报告按 **严重性 > 维度** 两级结构组织
|
|
766
|
+
- 确认每个问题都有完整的字段:
|
|
767
|
+
- 问题描述
|
|
768
|
+
- 影响范围(如适用)
|
|
769
|
+
- 详细分析
|
|
770
|
+
- 当前代码
|
|
771
|
+
- 建议修复
|
|
772
|
+
|
|
773
|
+
#### 3. 内容完整性检查
|
|
774
|
+
- 随机抽查 2-3 个问题,对比原始 subagent 输出
|
|
775
|
+
- 确认代码示例、分析细节没有被简化或遗漏
|
|
776
|
+
- 确认技术术语、文件路径等关键信息准确无误
|
|
777
|
+
|
|
778
|
+
**如果发现问题**:
|
|
779
|
+
- 如果数量不一致:重新检查 subagent 输出,补充遗漏的问题
|
|
780
|
+
- 如果结构不正确:按照模板重新组织
|
|
781
|
+
- 如果内容被简化:恢复完整的原始内容
|
|
782
|
+
|
|
783
|
+
**检查通过后**:
|
|
784
|
+
- 输出消息:"✅ 评审报告已生成并通过质量检查"
|
|
785
|
+
- 提供报告的关键统计信息`;
|
|
786
|
+
if (options?.notice) {
|
|
787
|
+
content += `
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
### 步骤 7:生成完成标志文件
|
|
792
|
+
|
|
793
|
+
**重要**:为了方便 CI/CD 流水线集成,请在项目根目录创建标志文件:
|
|
794
|
+
|
|
795
|
+
使用 **Write** 工具创建文件 \`${projectRoot}/.review-completed\`,内容如下:
|
|
796
|
+
|
|
797
|
+
\`\`\`json
|
|
798
|
+
{
|
|
799
|
+
"status": "completed",
|
|
800
|
+
"timestamp": "<当前时间戳>",
|
|
801
|
+
"report": "CODE_REVIEW.md"
|
|
802
|
+
}
|
|
803
|
+
\`\`\`
|
|
804
|
+
|
|
805
|
+
**注意**:
|
|
806
|
+
- 只有在评审报告 \`CODE_REVIEW.md\` 成功创建且质量检查通过后才创建此标志文件
|
|
807
|
+
- 时间戳格式:ISO 8601(例如:2025-01-15T10:30:00Z)
|
|
808
|
+
- 此文件用于 CI/CD 流水线检测评审是否完成`;
|
|
809
|
+
}
|
|
810
|
+
return content;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* 构建调用行动
|
|
814
|
+
*/
|
|
815
|
+
static buildCallToAction() {
|
|
816
|
+
return `## 开始执行
|
|
817
|
+
|
|
818
|
+
现在请按照上述流程开始执行,首先进行文件过滤分析。`;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* 构建分析阶段(大型变更)
|
|
822
|
+
*/
|
|
823
|
+
static buildAnalysisPhase(projectRoot, gitDiffStatCommand) {
|
|
824
|
+
return `#### 步骤 1:执行 git diff --stat
|
|
825
|
+
|
|
826
|
+
请执行以下命令获取所有变更文件:
|
|
827
|
+
|
|
828
|
+
\`\`\`bash
|
|
829
|
+
cd ${projectRoot}
|
|
830
|
+
${gitDiffStatCommand}
|
|
831
|
+
\`\`\`
|
|
832
|
+
|
|
833
|
+
**重要提示:不要获取完整 diff!**
|
|
834
|
+
- 只需要分析 \`git diff --stat\` 的输出
|
|
835
|
+
- 这将列出所有变更的文件及其变更行数
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
#### 步骤 2:智能过滤文件
|
|
840
|
+
|
|
841
|
+
**目标**:从变更文件中识别出真正需要代码评审的文件。
|
|
842
|
+
|
|
843
|
+
${this.buildFileFilteringRules()}
|
|
844
|
+
|
|
845
|
+
${this.buildFileFilteringOutputFormat()}
|
|
846
|
+
|
|
847
|
+
**重要**:
|
|
848
|
+
- 必须列出所有变更文件,要么在"需要评审"列表,要么在"已过滤"列表
|
|
849
|
+
- 每个被过滤的文件都要简要说明原因
|
|
850
|
+
- 统计数据要准确
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
#### 步骤 3:判断评审规模
|
|
855
|
+
|
|
856
|
+
基于**过滤后的文件**(即"需要评审的文件"列表),判断是否需要分批:
|
|
857
|
+
|
|
858
|
+
- 如果需要评审的文件 ≤ ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个 且 变更量 ≤ ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行
|
|
859
|
+
→ 记录"无需分批,可以一次性评审",跳转到阶段二(单批次评审)
|
|
860
|
+
- 如果需要评审的文件≤ ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个 且 变更量 ≤ ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行, 但是存在较大的单文件变更(比如超过500行),可视情况分批次
|
|
861
|
+
|
|
862
|
+
- 否则 → 继续步骤 4 制定分批策略
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
#### 步骤 4:制定分批策略(如需分批)
|
|
867
|
+
|
|
868
|
+
基于"需要评审的文件"列表,制定分批方案:
|
|
869
|
+
|
|
870
|
+
**分批原则**:
|
|
871
|
+
- 按功能模块、文件类型、业务关联性分组
|
|
872
|
+
- 每批不超过 ${REVIEW_THRESHOLDS.MAX_FILES_PER_BATCH} 个文件
|
|
873
|
+
- 每批不超过 ${REVIEW_THRESHOLDS.MAX_CHANGES_PER_BATCH} 行变更
|
|
874
|
+
- 相关文件放在同一批(如 Service 和其对应的 Test)
|
|
875
|
+
- 在每批次文件数和变更函数不超过限制的前提下,尽量减少批次
|
|
876
|
+
|
|
877
|
+
**输出格式**:
|
|
878
|
+
|
|
879
|
+
\`\`\`markdown
|
|
880
|
+
## 分批方案
|
|
881
|
+
|
|
882
|
+
### 批次 1:[批次描述,如 "用户认证模块"]
|
|
883
|
+
- 文件数:8 个
|
|
884
|
+
- 预计变更行数:650 行
|
|
885
|
+
- 文件列表:
|
|
886
|
+
- src/services/UserService.java
|
|
887
|
+
- src/services/AuthService.java
|
|
888
|
+
- src/utils/jwt.ts
|
|
889
|
+
...
|
|
890
|
+
|
|
891
|
+
### 批次 2:[批次描述]
|
|
892
|
+
...
|
|
893
|
+
|
|
894
|
+
### 批次 N:[批次描述]
|
|
895
|
+
...
|
|
896
|
+
\`\`\`
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
#### 步骤 5:确认完整性
|
|
901
|
+
|
|
902
|
+
检查分批方案(如果分批)或单批次方案:
|
|
903
|
+
- [ ] "需要评审的文件"列表中的每个文件都已分配到某个批次
|
|
904
|
+
- [ ] 没有文件被遗漏
|
|
905
|
+
- [ ] 没有文件被重复分配
|
|
906
|
+
|
|
907
|
+
如果检查通过,更新 TodoWrite,标记步骤1-5为完成,添加后续评审任务,继续阶段二。
|
|
908
|
+
|
|
909
|
+
**重要**:此阶段只做分析和规划,不开始评审代码。`;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* 构建分批评审阶段
|
|
913
|
+
*/
|
|
914
|
+
static buildBatchReviewPhase(projectRoot, options) {
|
|
915
|
+
return `### 阶段二:批次评审(完成分批后执行)
|
|
916
|
+
|
|
917
|
+
分批方案确定后,更新 **TodoWrite** 任务列表:
|
|
918
|
+
|
|
919
|
+
\`\`\`
|
|
920
|
+
1. [completed] 分析变更文件
|
|
921
|
+
2. [completed] AI 智能文件过滤
|
|
922
|
+
3. [completed] AI 智能制定分批次评审策略
|
|
923
|
+
4. [completed] 复核分批次评审策略
|
|
924
|
+
5. 评审批次 1/N
|
|
925
|
+
6. 评审批次 2/N
|
|
926
|
+
...
|
|
927
|
+
N+6. 汇总所有批次评审结果
|
|
928
|
+
N+7. 生成最终评审报告
|
|
929
|
+
\`\`\`
|
|
930
|
+
|
|
931
|
+
对于每个批次:
|
|
932
|
+
|
|
933
|
+
1. 标记任务为 **in_progress**
|
|
934
|
+
2. 使用 **Task tool** 调用 **code-reviewer** subagent
|
|
935
|
+
3. **验证返回结果**:
|
|
936
|
+
- 如果返回"未发现需要关注的问题",记录该批次无问题
|
|
937
|
+
- 如果返回了问题列表,快速检查格式是否正确
|
|
938
|
+
4. 标记为 **completed**,继续下一批次
|
|
939
|
+
|
|
940
|
+
**Task tool 调用示例**:
|
|
941
|
+
|
|
942
|
+
\`\`\`typescript
|
|
943
|
+
{
|
|
944
|
+
subagent_type: 'code-reviewer',
|
|
945
|
+
description: '代码评审 - 批次 1/N: [批次描述]',
|
|
946
|
+
prompt: \`请按照你的标准评审流程进行全面代码评审(批次 1/N)。
|
|
947
|
+
|
|
948
|
+
## 批次信息
|
|
949
|
+
|
|
950
|
+
- 批次描述:[描述,如 "用户认证模块"]
|
|
951
|
+
- 文件数:X 个
|
|
952
|
+
- 预计变更行数:Y 行
|
|
953
|
+
|
|
954
|
+
## 需要评审的文件列表
|
|
955
|
+
|
|
956
|
+
- file1.ts
|
|
957
|
+
- file2.ts
|
|
958
|
+
- file3.ts
|
|
959
|
+
...
|
|
960
|
+
|
|
961
|
+
## 获取变更内容
|
|
962
|
+
|
|
963
|
+
请执行以下命令获取本批次的代码变更:
|
|
964
|
+
|
|
965
|
+
\\\`\\\`\\\`bash
|
|
966
|
+
cd ${projectRoot}
|
|
967
|
+
git diff ${GitUtils.buildComparisonString(options, 'command')} -- file1.ts file2.ts file3.ts
|
|
968
|
+
\\\`\\\`\\\`
|
|
969
|
+
|
|
970
|
+
(将 file1.ts file2.ts file3.ts 替换为实际的批次文件列表)
|
|
971
|
+
|
|
972
|
+
${this.buildEnhancedRulesSection(projectRoot, options)}
|
|
973
|
+
|
|
974
|
+
## 结果检查
|
|
975
|
+
|
|
976
|
+
在返回结果开头请注明:**批次 1/N - [批次描述]**
|
|
977
|
+
|
|
978
|
+
返回结果前必须自检,剔除误报的问题,修正不准确的问题:
|
|
979
|
+
|
|
980
|
+
**检查 1:文件覆盖完整性**
|
|
981
|
+
- [ ] 已评审所有指定文件
|
|
982
|
+
- [ ] 无法评审的文件已说明原因
|
|
983
|
+
|
|
984
|
+
**检查 2:输出格式完整性**
|
|
985
|
+
- [ ] 每个问题都有 [严重性][维度] 标签
|
|
986
|
+
- [ ] CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
|
|
987
|
+
- [ ] MEDIUM/LOW 有:问题描述、简要说明
|
|
988
|
+
|
|
989
|
+
**检查 3:分类准确性**
|
|
990
|
+
- [ ] 严重性符合 SEVERITY_GUIDELINES
|
|
991
|
+
- [ ] 维度符合 REVIEW_DIMENSIONS
|
|
992
|
+
|
|
993
|
+
**检查 4:严重性合理性**
|
|
994
|
+
对每个 CRITICAL/HIGH 问题:
|
|
995
|
+
- [ ] 有明确代码证据证明是 bug?
|
|
996
|
+
- [ ] "会出错"还是"可能出错"?
|
|
997
|
+
- [ ] 代码是否已有防御/错误处理?
|
|
998
|
+
- [ ] bug 修复还是改进建议?
|
|
999
|
+
- [ ] 能写测试用例重现?
|
|
1000
|
+
|
|
1001
|
+
**不确定或"否"→ 必须降低严重性!**
|
|
1002
|
+
|
|
1003
|
+
**检查 5:Diff 覆盖验证(重要)**
|
|
1004
|
+
对每个报告的问题:
|
|
1005
|
+
- [ ] 问题所在代码行确实是在 diff 中
|
|
1006
|
+
- [ ] 问题确实是本次变更引入的,而非之前就存在
|
|
1007
|
+
|
|
1008
|
+
**如果是未变更的代码,必须删除该问题!**
|
|
1009
|
+
|
|
1010
|
+
**检查 6:误报风险(重要)**
|
|
1011
|
+
- [ ] 问题是真实存在,而非基于假设
|
|
1012
|
+
|
|
1013
|
+
**如果是误报的问题,必须剔除!**\`
|
|
1014
|
+
}
|
|
1015
|
+
\`\`\`
|
|
1016
|
+
|
|
1017
|
+
**关键点**:
|
|
1018
|
+
- 不要在主 task 中获取完整 diff,只提供 git diff 命令
|
|
1019
|
+
- code-reviewer subagent 会自己执行命令并获取 diff
|
|
1020
|
+
- code-reviewer subagent 会自己读取所有相关文件
|
|
1021
|
+
- code-reviewer subagent 会自己进行深度代码评审并自检
|
|
1022
|
+
- code-reviewer subagent 会返回评审结果`;
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* 构建汇总阶段
|
|
1026
|
+
*/
|
|
1027
|
+
static buildSummaryPhase(stats, projectRoot, options) {
|
|
1028
|
+
let content = `### 阶段三:汇总报告(所有批次完成后)
|
|
1029
|
+
|
|
1030
|
+
所有批次评审完成后,执行以下步骤生成最终报告:
|
|
1031
|
+
|
|
1032
|
+
#### 步骤 1:收集所有评审结果
|
|
1033
|
+
|
|
1034
|
+
1. 标记汇总任务为 **in_progress**
|
|
1035
|
+
2. 从所有 code-reviewer subagent 的返回结果中,**逐条提取**所有问题
|
|
1036
|
+
3. 每个问题保留完整信息(不修改、不删减):
|
|
1037
|
+
- 文件路径
|
|
1038
|
+
- [严重性][维度] 标签
|
|
1039
|
+
- 问题标题
|
|
1040
|
+
- 问题描述
|
|
1041
|
+
- 影响范围
|
|
1042
|
+
- 详细分析
|
|
1043
|
+
- 当前代码(完整上下文)
|
|
1044
|
+
- 建议修复(完整代码)
|
|
1045
|
+
|
|
1046
|
+
**关键要求**:
|
|
1047
|
+
- 必须收集**所有问题**,不得遗漏任何一个
|
|
1048
|
+
- 必须保留**所有细节**,包括代码块、修复建议等
|
|
1049
|
+
- 如果某个subagent返回"未发现需要关注的问题",则该批次无问题
|
|
1050
|
+
- 不要对问题进行总结或简化
|
|
1051
|
+
|
|
1052
|
+
---
|
|
1053
|
+
|
|
1054
|
+
#### 步骤 2:复核评审问题
|
|
1055
|
+
|
|
1056
|
+
**要求**:逐一复核评审问题,核对检查项
|
|
1057
|
+
**目标**:移除误报的问题,降低误报率
|
|
1058
|
+
|
|
1059
|
+
**检查 1:Diff 覆盖验证**
|
|
1060
|
+
对每个报告的问题:
|
|
1061
|
+
- 问题所在代码行确实是在 diff 中
|
|
1062
|
+
- 问题确实是本次变更引入的,而非之前就存在
|
|
1063
|
+
|
|
1064
|
+
**检查 2:误报风险**
|
|
1065
|
+
对每个报告的问题:
|
|
1066
|
+
- [ ] 问题是真实存在,而非基于假设
|
|
1067
|
+
|
|
1068
|
+
#### 步骤 3:按维度和严重性重新组织
|
|
1069
|
+
|
|
1070
|
+
将收集到的所有问题按照以下结构重新组织:
|
|
1071
|
+
|
|
1072
|
+
\`\`\`
|
|
1073
|
+
CRITICAL 问题列表
|
|
1074
|
+
├── 正确性
|
|
1075
|
+
│ ├── 问题1(完整信息)
|
|
1076
|
+
│ └── 问题2(完整信息)
|
|
1077
|
+
├── 安全性
|
|
1078
|
+
│ └── 问题3(完整信息)
|
|
1079
|
+
└── 性能
|
|
1080
|
+
└── 问题4(完整信息)
|
|
1081
|
+
|
|
1082
|
+
HIGH 问题列表
|
|
1083
|
+
├── 正确性
|
|
1084
|
+
├── 安全性
|
|
1085
|
+
├── 性能
|
|
1086
|
+
├── 可维护性
|
|
1087
|
+
├── 架构设计
|
|
1088
|
+
└── 最佳实践
|
|
1089
|
+
|
|
1090
|
+
MEDIUM 问题列表
|
|
1091
|
+
...
|
|
1092
|
+
|
|
1093
|
+
LOW 问题列表
|
|
1094
|
+
...
|
|
1095
|
+
\`\`\`
|
|
1096
|
+
|
|
1097
|
+
**组织要求**:
|
|
1098
|
+
- 第一层:按严重性分类(CRITICAL → HIGH → MEDIUM → LOW)
|
|
1099
|
+
- 第二层:按维度分类(正确性、安全性、性能、可维护性、架构设计、最佳实践)
|
|
1100
|
+
- 如果某个严重性级别下某个维度没有问题,则不显示该维度
|
|
1101
|
+
- 每个问题保留完整的原始信息,不进行任何删减
|
|
1102
|
+
|
|
1103
|
+
---
|
|
1104
|
+
|
|
1105
|
+
#### 步骤 4:生成最终报告
|
|
1106
|
+
|
|
1107
|
+
按照以下格式生成 \`CODE_REVIEW.md\`:
|
|
1108
|
+
|
|
1109
|
+
${this.buildReportTemplate(stats)}
|
|
1110
|
+
|
|
1111
|
+
**注意**:评审报告中不要体现批次信息,需要整体看上去是一次性完成的评审。
|
|
1112
|
+
|
|
1113
|
+
---
|
|
1114
|
+
|
|
1115
|
+
**所有步骤完成后**:
|
|
1116
|
+
1. 将最终报告写入 \`CODE_REVIEW.md\`
|
|
1117
|
+
2. 标记任务为 **completed**
|
|
1118
|
+
3. 输出简要总结:
|
|
1119
|
+
\`\`\`
|
|
1120
|
+
✅ 代码评审完成
|
|
1121
|
+
|
|
1122
|
+
📊 评审统计:
|
|
1123
|
+
- 批次数:X
|
|
1124
|
+
- 原始文件数:Y
|
|
1125
|
+
- 评审文件数:Z
|
|
1126
|
+
- 问题总数:W (CRITICAL: a, HIGH: b, MEDIUM: c, LOW: d)
|
|
1127
|
+
|
|
1128
|
+
📁 报告已生成:CODE_REVIEW.md
|
|
1129
|
+
\`\`\``;
|
|
1130
|
+
if (options?.notice) {
|
|
1131
|
+
content += `
|
|
1132
|
+
|
|
1133
|
+
---
|
|
1134
|
+
|
|
1135
|
+
#### 步骤 5:生成完成标志文件
|
|
1136
|
+
|
|
1137
|
+
**重要**:为了方便 CI/CD 流水线集成,请在项目根目录创建标志文件:
|
|
1138
|
+
|
|
1139
|
+
使用 **Write** 工具创建文件 \`${projectRoot}/.review-completed\`,内容如下:
|
|
1140
|
+
|
|
1141
|
+
\`\`\`json
|
|
1142
|
+
{
|
|
1143
|
+
"status": "completed",
|
|
1144
|
+
"timestamp": "<当前时间戳>",
|
|
1145
|
+
"report": "CODE_REVIEW.md"
|
|
1146
|
+
}
|
|
1147
|
+
\`\`\`
|
|
1148
|
+
|
|
1149
|
+
**注意**:
|
|
1150
|
+
- 只有在评审报告 \`CODE_REVIEW.md\` 成功创建后才创建此标志文件
|
|
1151
|
+
- 时间戳格式:ISO 8601(例如:2025-01-15T10:30:00Z)
|
|
1152
|
+
- 此文件用于 CI/CD 流水线检测评审是否完成`;
|
|
1153
|
+
}
|
|
1154
|
+
return content;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* 构建大型变更调用行动
|
|
1158
|
+
*/
|
|
1159
|
+
static buildLargeChangeCallToAction() {
|
|
1160
|
+
return `## 立即开始
|
|
1161
|
+
|
|
1162
|
+
**第一步**:使用 TodoWrite 创建任务列表
|
|
1163
|
+
|
|
1164
|
+
**第二步**:执行 git diff --stat 命令获取所有变更文件
|
|
1165
|
+
|
|
1166
|
+
**第三步**:智能过滤文件,识别需要评审的文件
|
|
1167
|
+
|
|
1168
|
+
**第四步**:基于过滤后的文件制定分批策略(如需要)
|
|
1169
|
+
|
|
1170
|
+
**重要**:此阶段只做分析、过滤和规划,不开始评审代码!
|
|
1171
|
+
**重要**:此阶段后请继续执行阶段二和阶段三,不要中断,不要询问是否继续!
|
|
1172
|
+
|
|
1173
|
+
请开始执行!`;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
// ============================================================================
|
|
1177
|
+
// 评审策略选择器
|
|
1178
|
+
// ============================================================================
|
|
1179
|
+
/**
|
|
1180
|
+
* 评审策略选择器
|
|
1181
|
+
*/
|
|
1182
|
+
class ReviewStrategySelector {
|
|
1183
|
+
/**
|
|
1184
|
+
* 根据统计信息选择评审策略
|
|
1185
|
+
*/
|
|
1186
|
+
static selectStrategy(stats, projectRoot, options) {
|
|
1187
|
+
const isLargeChange = this.isLargeChange(stats);
|
|
1188
|
+
if (isLargeChange) {
|
|
1189
|
+
const gitDiffStatCommand = GitUtils.buildDiffStatCommand(options);
|
|
1190
|
+
return ReviewPromptBuilder.buildLargeChangePrompt(stats, projectRoot, gitDiffStatCommand, options);
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
const gitDiffCommand = GitUtils.buildDiffCommand(options);
|
|
1194
|
+
return ReviewPromptBuilder.buildSmallChangePrompt(stats, projectRoot, gitDiffCommand, options);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* 判断是否为大型变更
|
|
1199
|
+
*/
|
|
1200
|
+
static isLargeChange(stats) {
|
|
1201
|
+
return stats.fileCount > REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD ||
|
|
1202
|
+
stats.totalChanges > REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
// ============================================================================
|
|
1206
|
+
// 命令导出
|
|
1207
|
+
// ============================================================================
|
|
1208
|
+
export const reviewCommand = {
|
|
1209
|
+
name: 'review',
|
|
1210
|
+
description: 'Performs code review with multiple modes: default (uncommitted changes), commit comparison, or branch comparison.',
|
|
1211
|
+
kind: CommandKind.BUILT_IN,
|
|
1212
|
+
action: async (context, args) => {
|
|
1213
|
+
// 验证服务可用性
|
|
1214
|
+
if (!context.services.config) {
|
|
1215
|
+
return {
|
|
1216
|
+
type: 'message',
|
|
1217
|
+
messageType: 'error',
|
|
1218
|
+
content: 'Configuration not available.',
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
if (!context.services.git) {
|
|
1222
|
+
return {
|
|
1223
|
+
type: 'message',
|
|
1224
|
+
messageType: 'error',
|
|
1225
|
+
content: 'Git service not available. This command requires a git repository.',
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
const projectRoot = context.services.config.getProjectRoot();
|
|
1229
|
+
if (!projectRoot) {
|
|
1230
|
+
return {
|
|
1231
|
+
type: 'message',
|
|
1232
|
+
messageType: 'error',
|
|
1233
|
+
content: 'Project root not available.',
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
try {
|
|
1237
|
+
// 解析参数并获取统计信息
|
|
1238
|
+
const options = ReviewArgumentParser.parse(args);
|
|
1239
|
+
const stats = await GitStatsAnalyzer.getDiffStats(projectRoot, options);
|
|
1240
|
+
// 检查是否有变更
|
|
1241
|
+
if (stats.fileCount === 0) {
|
|
1242
|
+
return {
|
|
1243
|
+
type: 'message',
|
|
1244
|
+
messageType: 'info',
|
|
1245
|
+
content: 'No differences found to review.',
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
// 根据变更规模选择评审策略
|
|
1249
|
+
const reviewPrompt = ReviewStrategySelector.selectStrategy(stats, projectRoot, options);
|
|
1250
|
+
return {
|
|
1251
|
+
type: 'submit_prompt',
|
|
1252
|
+
content: reviewPrompt,
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
catch (error) {
|
|
1256
|
+
const errorMessage = error instanceof ReviewCommandError
|
|
1257
|
+
? error.message
|
|
1258
|
+
: `Error during review: ${error instanceof Error ? error.message : String(error)}`;
|
|
1259
|
+
return {
|
|
1260
|
+
type: 'message',
|
|
1261
|
+
messageType: 'error',
|
|
1262
|
+
content: errorMessage,
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
};
|
|
1267
|
+
//# sourceMappingURL=reviewCommand.js.map
|