@umsai/ums-code 0.3.0-v2 → 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.
- package/dist/package.json +6 -6
- package/dist/src/{zed-integration → acp-integration}/acp.d.ts +13 -0
- package/dist/src/{zed-integration → acp-integration}/acp.js +32 -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 +264 -0
- package/dist/src/acp-integration/acpAgent.js.map +1 -0
- package/dist/src/{zed-integration → acp-integration}/schema.d.ts +2235 -905
- package/dist/src/{zed-integration → acp-integration}/schema.js +100 -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/{ui/hooks/useQuotaAndFallback.test.d.ts → acp-integration/service/filesystem.test.d.ts} +1 -1
- 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 +61 -0
- package/dist/src/acp-integration/session/Session.js +839 -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/commandMode.d.ts +11 -0
- package/dist/src/commandMode.js +238 -0
- package/dist/src/commandMode.js.map +1 -0
- package/dist/src/config/auth.d.ts +1 -0
- package/dist/src/config/auth.js +29 -0
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/config.d.ts +19 -3
- package/dist/src/config/config.js +175 -24
- 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/extensions/update.test.js +9 -0
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +53 -2
- package/dist/src/config/settingsSchema.js +46 -2
- 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/core/initializer.js +3 -0
- package/dist/src/core/initializer.js.map +1 -1
- package/dist/src/gemini.js +75 -35
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +37 -4
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +3 -3
- package/dist/src/generated/git-commit.js +3 -3
- package/dist/src/i18n/index.d.ts +10 -2
- package/dist/src/i18n/index.js +26 -1
- package/dist/src/i18n/index.js.map +1 -1
- package/dist/src/i18n/languages.d.ts +20 -0
- package/dist/src/i18n/languages.js +36 -0
- package/dist/src/i18n/languages.js.map +1 -0
- package/dist/src/i18n/locales/de.js +1073 -0
- package/dist/src/i18n/locales/en.js +121 -175
- package/dist/src/i18n/locales/ru.js +1095 -0
- package/dist/src/i18n/locales/zh.js +52 -174
- 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 +184 -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 +64 -5
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js.map +1 -1
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.test.js +40 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.test.js.map +1 -1
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.test.js +50 -0
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.test.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 +121 -56
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCliCommands.d.ts +48 -9
- package/dist/src/nonInteractiveCliCommands.js +180 -64
- package/dist/src/nonInteractiveCliCommands.js.map +1 -1
- package/dist/src/nonInteractiveCliCommands.test.d.ts +6 -0
- package/dist/src/nonInteractiveCliCommands.test.js +157 -0
- package/dist/src/nonInteractiveCliCommands.test.js.map +1 -0
- package/dist/src/services/BuiltinCommandLoader.js +7 -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/services/McpPromptLoader.js +4 -2
- package/dist/src/services/McpPromptLoader.js.map +1 -1
- package/dist/src/services/McpPromptLoader.test.js +1 -1
- package/dist/src/services/McpPromptLoader.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 +62 -56
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +0 -46
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.test.js +4 -4
- package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
- package/dist/src/ui/auth/useAuth.js +31 -4
- package/dist/src/ui/auth/useAuth.js.map +1 -1
- package/dist/src/ui/commands/approvalModeCommand.js +53 -4
- package/dist/src/ui/commands/approvalModeCommand.js.map +1 -1
- package/dist/src/ui/commands/approvalModeCommand.test.js +72 -10
- package/dist/src/ui/commands/approvalModeCommand.test.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/compressCommand.js +86 -16
- package/dist/src/ui/commands/compressCommand.js.map +1 -1
- package/dist/src/ui/commands/languageCommand.d.ts +6 -1
- package/dist/src/ui/commands/languageCommand.js +162 -183
- 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 +558 -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/restoreCommand.js +1 -1
- package/dist/src/ui/commands/restoreCommand.js.map +1 -1
- package/dist/src/ui/commands/resumeCommand.d.ts +7 -0
- package/dist/src/ui/commands/resumeCommand.js +19 -0
- package/dist/src/ui/commands/resumeCommand.js.map +1 -0
- package/dist/src/ui/commands/resumeCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/resumeCommand.test.js +32 -0
- package/dist/src/ui/commands/resumeCommand.test.js.map +1 -0
- package/dist/src/ui/commands/reviewCommand.d.ts +9 -0
- package/dist/src/ui/commands/reviewCommand.js +2046 -0
- package/dist/src/ui/commands/reviewCommand.js.map +1 -0
- package/dist/src/ui/commands/reviewCommand.test.js +600 -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/summaryCommand.js +129 -42
- package/dist/src/ui/commands/summaryCommand.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +24 -10
- 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 +7 -23
- 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/HistoryItemDisplay.test.js +5 -2
- package/dist/src/ui/components/HistoryItemDisplay.test.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/PermissionsModifyTrustDialog.js +1 -1
- package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -1
- package/dist/src/ui/components/SessionPicker.d.ts +18 -0
- package/dist/src/ui/components/SessionPicker.js +69 -0
- package/dist/src/ui/components/SessionPicker.js.map +1 -0
- package/dist/src/ui/components/SessionSummaryDisplay.js +14 -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/StandaloneSessionPicker.d.ts +10 -0
- package/dist/src/ui/components/StandaloneSessionPicker.js +78 -0
- package/dist/src/ui/components/StandaloneSessionPicker.js.map +1 -0
- package/dist/src/ui/components/StandaloneSessionPicker.test.d.ts +6 -0
- package/dist/src/ui/components/StandaloneSessionPicker.test.js +410 -0
- package/dist/src/ui/components/StandaloneSessionPicker.test.js.map +1 -0
- 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 +24 -4
- 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/UIActionsContext.d.ts +3 -1
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +3 -11
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +3 -8
- package/dist/src/ui/hooks/slashCommandProcessor.js +77 -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 +87 -12
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useLoadingIndicator.test.js +11 -6
- package/dist/src/ui/hooks/useLoadingIndicator.test.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/usePhraseCycler.js +12 -136
- package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
- package/dist/src/ui/hooks/useQwenAuth.test.js +1 -1
- package/dist/src/ui/hooks/useQwenAuth.test.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/useResumeCommand.d.ts +20 -0
- package/dist/src/ui/hooks/useResumeCommand.js +49 -0
- package/dist/src/ui/hooks/useResumeCommand.js.map +1 -0
- package/dist/src/ui/hooks/useResumeCommand.test.d.ts +6 -0
- package/dist/src/ui/hooks/useResumeCommand.test.js +144 -0
- package/dist/src/ui/hooks/useResumeCommand.test.js.map +1 -0
- package/dist/src/ui/hooks/useSessionPicker.d.ts +36 -0
- package/dist/src/ui/hooks/useSessionPicker.js +189 -0
- package/dist/src/ui/hooks/useSessionPicker.js.map +1 -0
- 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 +2 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/models/availableModels.d.ts +9 -2
- package/dist/src/ui/models/availableModels.js +32 -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 +267 -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 +253 -0
- package/dist/src/ui/utils/resumeHistoryUtils.test.js.map +1 -0
- package/dist/src/ui/utils/sessionPickerUtils.d.ts +30 -0
- package/dist/src/ui/utils/sessionPickerUtils.js +41 -0
- package/dist/src/ui/utils/sessionPickerUtils.js.map +1 -0
- package/dist/src/ui/utils/sessionPickerUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/sessionPickerUtils.test.js +38 -0
- package/dist/src/ui/utils/sessionPickerUtils.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/errors.d.ts +1 -1
- package/dist/src/utils/errors.js +14 -3
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/errors.test.js +43 -1
- package/dist/src/utils/errors.test.js.map +1 -1
- package/dist/src/utils/gitUtils.js +25 -8
- package/dist/src/utils/gitUtils.js.map +1 -1
- package/dist/src/utils/gitUtils.test.js +59 -0
- package/dist/src/utils/gitUtils.test.js.map +1 -1
- package/dist/src/utils/nonInteractiveHelpers.d.ts +3 -1
- package/dist/src/utils/nonInteractiveHelpers.js +14 -15
- package/dist/src/utils/nonInteractiveHelpers.js.map +1 -1
- package/dist/src/utils/nonInteractiveHelpers.test.js +36 -21
- package/dist/src/utils/nonInteractiveHelpers.test.js.map +1 -1
- package/dist/src/utils/relaunch.js +2 -2
- package/dist/src/utils/relaunch.js.map +1 -1
- package/dist/src/utils/relaunch.test.js +5 -5
- package/dist/src/utils/relaunch.test.js.map +1 -1
- package/dist/src/utils/systemInfo.js +2 -1
- package/dist/src/utils/systemInfo.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.js +10 -1
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- 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/ProQuotaDialog.d.ts +0 -13
- package/dist/src/ui/components/ProQuotaDialog.js +0 -24
- package/dist/src/ui/components/ProQuotaDialog.js.map +0 -1
- package/dist/src/ui/components/ProQuotaDialog.test.js +0 -58
- package/dist/src/ui/components/ProQuotaDialog.test.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/ui/hooks/useQuotaAndFallback.d.ts +0 -21
- package/dist/src/ui/hooks/useQuotaAndFallback.js +0 -122
- package/dist/src/ui/hooks/useQuotaAndFallback.js.map +0 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js +0 -269
- package/dist/src/ui/hooks/useQuotaAndFallback.test.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
- /package/dist/src/ui/{components/ProQuotaDialog.test.d.ts → commands/reviewCommand.test.d.ts} +0 -0
|
@@ -0,0 +1,2046 @@
|
|
|
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
|
+
case 'commits':
|
|
69
|
+
// commits 模式不使用此方法,每个文件单独构建命令
|
|
70
|
+
throw new ReviewCommandError('Commits mode should not use buildComparisonString');
|
|
71
|
+
default:
|
|
72
|
+
throw new ReviewCommandError(`Unknown review mode: ${mode}`);
|
|
73
|
+
}
|
|
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
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 构建 git diff --stat 命令
|
|
93
|
+
*/
|
|
94
|
+
static buildDiffStatCommand(options) {
|
|
95
|
+
const comparison = this.buildComparisonString(options, 'command');
|
|
96
|
+
return `git diff --stat ${comparison}`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 构建 git diff 命令
|
|
100
|
+
*/
|
|
101
|
+
static buildDiffCommand(options) {
|
|
102
|
+
const comparison = this.buildComparisonString(options, 'command');
|
|
103
|
+
return `git diff ${comparison}`;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 构建 git diff 参数数组
|
|
107
|
+
*/
|
|
108
|
+
static buildDiffArgs(options, command) {
|
|
109
|
+
const baseArgs = command === 'diff-stat'
|
|
110
|
+
? ['diff', '--stat']
|
|
111
|
+
: ['diff', '--name-only'];
|
|
112
|
+
const comparison = this.buildComparisonString(options, 'range');
|
|
113
|
+
return [...baseArgs, comparison];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// 参数解析器
|
|
118
|
+
// ============================================================================
|
|
119
|
+
/**
|
|
120
|
+
* Review 命令参数解析器
|
|
121
|
+
*/
|
|
122
|
+
class ReviewArgumentParser {
|
|
123
|
+
/**
|
|
124
|
+
* 解析 review 命令的参数
|
|
125
|
+
*/
|
|
126
|
+
static parse(args) {
|
|
127
|
+
const trimmedArgs = args.trim();
|
|
128
|
+
if (!trimmedArgs) {
|
|
129
|
+
return { mode: 'default' };
|
|
130
|
+
}
|
|
131
|
+
const parts = trimmedArgs.split(/\s+/);
|
|
132
|
+
// 提取 --prompt-enhance 和 --notice 参数
|
|
133
|
+
const enhanceOptions = this.extractEnhanceOption(parts);
|
|
134
|
+
const noticeOption = this.extractNoticeOption(parts);
|
|
135
|
+
const filteredParts = this.removeOptions(parts);
|
|
136
|
+
if (filteredParts.length === 0) {
|
|
137
|
+
return { mode: 'default', ...enhanceOptions, ...noticeOption };
|
|
138
|
+
}
|
|
139
|
+
const command = filteredParts[0];
|
|
140
|
+
switch (command) {
|
|
141
|
+
case '-commit':
|
|
142
|
+
return { ...this.parseCommitMode(filteredParts), ...enhanceOptions, ...noticeOption };
|
|
143
|
+
case '-branch':
|
|
144
|
+
return { ...this.parseBranchMode(filteredParts), ...enhanceOptions, ...noticeOption };
|
|
145
|
+
case '-commits':
|
|
146
|
+
return { ...this.parseCommitsMode(filteredParts), ...enhanceOptions, ...noticeOption };
|
|
147
|
+
default:
|
|
148
|
+
return { ...this.handleUnknownCommand(filteredParts), ...enhanceOptions, ...noticeOption };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 提取 --prompt-enhance 参数
|
|
153
|
+
*/
|
|
154
|
+
static extractEnhanceOption(parts) {
|
|
155
|
+
for (let i = 0; i < parts.length; i++) {
|
|
156
|
+
if (parts[i] === '--prompt-enhance' && i + 1 < parts.length) {
|
|
157
|
+
return { promptEnhancePath: parts[i + 1] };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 移除所有选项参数(--prompt-enhance 和 --notice)
|
|
164
|
+
*/
|
|
165
|
+
static removeOptions(parts) {
|
|
166
|
+
const filtered = [];
|
|
167
|
+
for (let i = 0; i < parts.length; i++) {
|
|
168
|
+
if (parts[i] === '--prompt-enhance') {
|
|
169
|
+
i++; // 跳过参数值
|
|
170
|
+
}
|
|
171
|
+
else if (parts[i] === '--notice') {
|
|
172
|
+
// 跳过该标志
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
filtered.push(parts[i]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return filtered;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 提取 --notice 参数
|
|
182
|
+
*/
|
|
183
|
+
static extractNoticeOption(parts) {
|
|
184
|
+
return { notice: parts.includes('--notice') };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 解析 commit 模式参数
|
|
188
|
+
*/
|
|
189
|
+
static parseCommitMode(parts) {
|
|
190
|
+
if (parts.length < 2) {
|
|
191
|
+
throw new ReviewCommandError('Commit mode requires at least one commit hash', 'MISSING_COMMIT_HASH');
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
mode: 'commit',
|
|
195
|
+
commit1: parts[1],
|
|
196
|
+
commit2: parts[2],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* 解析 branch 模式参数
|
|
201
|
+
*/
|
|
202
|
+
static parseBranchMode(parts) {
|
|
203
|
+
if (parts.length < 2) {
|
|
204
|
+
throw new ReviewCommandError('Branch mode requires at least one branch name', 'MISSING_BRANCH_NAME');
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
mode: 'branch',
|
|
208
|
+
branch1: parts[1],
|
|
209
|
+
branch2: parts[2],
|
|
210
|
+
};
|
|
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
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 处理未知命令或可能的语法错误
|
|
232
|
+
*/
|
|
233
|
+
static handleUnknownCommand(parts) {
|
|
234
|
+
// 检测是否忘记添加 -commit 或 -branch 参数
|
|
235
|
+
if (parts.length >= 1 && GitUtils.looksLikeCommitHash(parts[0])) {
|
|
236
|
+
throw new ReviewCommandError(this.buildUsageErrorMessage(parts[0], parts[1]), 'INVALID_SYNTAX');
|
|
237
|
+
}
|
|
238
|
+
// 默认模式
|
|
239
|
+
return { mode: 'default' };
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 构建使用错误提示信息
|
|
243
|
+
*/
|
|
244
|
+
static buildUsageErrorMessage(commitHash, secondArg) {
|
|
245
|
+
const example = secondArg
|
|
246
|
+
? `/review -commit ${commitHash} ${secondArg}`
|
|
247
|
+
: `/review -commit ${commitHash}`;
|
|
248
|
+
return `Invalid syntax. Did you forget '-commit'?
|
|
249
|
+
|
|
250
|
+
Usage:
|
|
251
|
+
/review -commit <commit1> [commit2]
|
|
252
|
+
/review -branch <branch1> [branch2]
|
|
253
|
+
/review (for uncommitted changes)
|
|
254
|
+
|
|
255
|
+
Example:
|
|
256
|
+
${example}`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// ============================================================================
|
|
260
|
+
// Git 统计分析器
|
|
261
|
+
// ============================================================================
|
|
262
|
+
/**
|
|
263
|
+
* Git 统计分析器
|
|
264
|
+
*/
|
|
265
|
+
class GitStatsAnalyzer {
|
|
266
|
+
/**
|
|
267
|
+
* 获取 git diff 统计信息
|
|
268
|
+
*/
|
|
269
|
+
static async getDiffStats(projectRoot, options) {
|
|
270
|
+
const git = this.createGitInstance(projectRoot);
|
|
271
|
+
try {
|
|
272
|
+
const diffArgs = GitUtils.buildDiffArgs(options, 'diff-stat');
|
|
273
|
+
const statsOutput = await git.raw(diffArgs);
|
|
274
|
+
return this.parseStatsOutput(statsOutput);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
throw new ReviewCommandError(`Failed to get git diff stats: ${error instanceof Error ? error.message : String(error)}`, 'GIT_STATS_ERROR');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* 创建 Git 实例
|
|
282
|
+
*/
|
|
283
|
+
static createGitInstance(projectRoot) {
|
|
284
|
+
return simpleGit(projectRoot, {
|
|
285
|
+
binary: 'git',
|
|
286
|
+
maxConcurrentProcesses: 1,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* 解析 git diff --stat 输出
|
|
291
|
+
*/
|
|
292
|
+
static parseStatsOutput(statsOutput) {
|
|
293
|
+
const lines = statsOutput.trim().split('\n');
|
|
294
|
+
if (lines.length === 0 || !lines[0]) {
|
|
295
|
+
return this.createEmptyStats();
|
|
296
|
+
}
|
|
297
|
+
const fileCount = this.calculateFileCount(lines);
|
|
298
|
+
const { addedLines, deletedLines } = this.calculateLineChanges(lines);
|
|
299
|
+
return {
|
|
300
|
+
fileCount,
|
|
301
|
+
addedLines,
|
|
302
|
+
deletedLines,
|
|
303
|
+
totalChanges: addedLines + deletedLines,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 创建空的统计对象
|
|
308
|
+
*/
|
|
309
|
+
static createEmptyStats() {
|
|
310
|
+
return {
|
|
311
|
+
fileCount: 0,
|
|
312
|
+
addedLines: 0,
|
|
313
|
+
deletedLines: 0,
|
|
314
|
+
totalChanges: 0,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 计算文件数量
|
|
319
|
+
*/
|
|
320
|
+
static calculateFileCount(lines) {
|
|
321
|
+
return lines.length > 1 ? lines.length - 1 : 0;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 计算行变更统计
|
|
325
|
+
*/
|
|
326
|
+
static calculateLineChanges(lines) {
|
|
327
|
+
let addedLines = 0;
|
|
328
|
+
let deletedLines = 0;
|
|
329
|
+
for (const line of lines) {
|
|
330
|
+
const lineStats = this.parseLineStats(line);
|
|
331
|
+
if (lineStats) {
|
|
332
|
+
addedLines += lineStats.addedLines;
|
|
333
|
+
deletedLines += lineStats.deletedLines;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return { addedLines, deletedLines };
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* 解析单行统计信息
|
|
340
|
+
* 匹配格式:'file.ts | 10 ++++------'
|
|
341
|
+
*/
|
|
342
|
+
static parseLineStats(line) {
|
|
343
|
+
const match = line.match(/\|\s*(\d+)\s*([+-]+)?/);
|
|
344
|
+
if (!match)
|
|
345
|
+
return null;
|
|
346
|
+
const totalChanges = parseInt(match[1], 10);
|
|
347
|
+
const signs = match[2] || '';
|
|
348
|
+
const additions = (signs.match(/\+/g) || []).length;
|
|
349
|
+
const deletions = (signs.match(/-/g) || []).length;
|
|
350
|
+
if (additions === 0 && deletions === 0) {
|
|
351
|
+
// 如果没有符号,估算各占一半
|
|
352
|
+
return {
|
|
353
|
+
addedLines: Math.floor(totalChanges / 2),
|
|
354
|
+
deletedLines: Math.ceil(totalChanges / 2),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
// 按比例计算实际行数
|
|
358
|
+
const total = additions + deletions;
|
|
359
|
+
return {
|
|
360
|
+
addedLines: Math.round((additions / total) * totalChanges),
|
|
361
|
+
deletedLines: Math.round((deletions / total) * totalChanges),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
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
|
+
// ============================================================================
|
|
521
|
+
// Prompt 构建器
|
|
522
|
+
// ============================================================================
|
|
523
|
+
/**
|
|
524
|
+
* 代码评审 Prompt 构建器
|
|
525
|
+
*/
|
|
526
|
+
class ReviewPromptBuilder {
|
|
527
|
+
/**
|
|
528
|
+
* 构建小型变更的评审 prompt
|
|
529
|
+
*/
|
|
530
|
+
static buildSmallChangePrompt(stats, projectRoot, gitDiffCommand, options) {
|
|
531
|
+
return this.buildPromptTemplate([
|
|
532
|
+
this.buildHeader(stats),
|
|
533
|
+
this.buildExecutionFlow(options),
|
|
534
|
+
this.buildFileFilteringPhase(projectRoot, gitDiffCommand),
|
|
535
|
+
this.buildCodeReviewPhase(projectRoot, gitDiffCommand, options),
|
|
536
|
+
this.buildReportGenerationPhase(stats),
|
|
537
|
+
this.buildQualityCheckPhase(projectRoot, options),
|
|
538
|
+
this.buildCallToAction()
|
|
539
|
+
]);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* 构建大型变更的评审 prompt
|
|
543
|
+
*/
|
|
544
|
+
static buildLargeChangePrompt(stats, projectRoot, gitDiffStatCommand, options) {
|
|
545
|
+
return this.buildPromptTemplate([
|
|
546
|
+
this.buildLargeChangeHeader(stats),
|
|
547
|
+
this.buildLargeChangeExecutionFlow(options),
|
|
548
|
+
this.buildAnalysisPhase(projectRoot, gitDiffStatCommand),
|
|
549
|
+
this.buildBatchReviewPhase(projectRoot, options),
|
|
550
|
+
this.buildSummaryPhase(stats, projectRoot, options),
|
|
551
|
+
this.buildLargeChangeCallToAction()
|
|
552
|
+
]);
|
|
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
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* 构建 prompt 模板
|
|
583
|
+
*/
|
|
584
|
+
static buildPromptTemplate(sections) {
|
|
585
|
+
return sections.join('\n\n---\n\n');
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* 构建变更摘要头部
|
|
589
|
+
*/
|
|
590
|
+
static buildHeader(stats) {
|
|
591
|
+
return `# 代码评审任务
|
|
592
|
+
|
|
593
|
+
## 变更摘要
|
|
594
|
+
|
|
595
|
+
- **文件总数**: ${stats.fileCount} 个
|
|
596
|
+
- **新增行数**: ${stats.addedLines} 行
|
|
597
|
+
- **删除行数**: ${stats.deletedLines} 行
|
|
598
|
+
- **总变更量**: ${stats.totalChanges} 行
|
|
599
|
+
|
|
600
|
+
变更规模适中,将使用 code-reviewer 一次性完成评审。`;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* 构建大型变更头部
|
|
604
|
+
*/
|
|
605
|
+
static buildLargeChangeHeader(stats) {
|
|
606
|
+
return `# 代码评审任务 - 大型变更检测
|
|
607
|
+
|
|
608
|
+
## 变更摘要
|
|
609
|
+
|
|
610
|
+
- **文件总数**: ${stats.fileCount} 个
|
|
611
|
+
- **新增行数**: ${stats.addedLines} 行
|
|
612
|
+
- **删除行数**: ${stats.deletedLines} 行
|
|
613
|
+
- **总变更量**: ${stats.totalChanges} 行
|
|
614
|
+
|
|
615
|
+
由于这是一个**大型变更**(超过 ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个文件或 ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行代码),需要分批次进行评审。`;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* 构建执行流程
|
|
619
|
+
*/
|
|
620
|
+
static buildExecutionFlow(options) {
|
|
621
|
+
const tasks = [
|
|
622
|
+
'1. AI 智能文件过滤',
|
|
623
|
+
'2. AI 智能代码评审',
|
|
624
|
+
'3. 生成评审报告',
|
|
625
|
+
'4. 评审报告质量检查'
|
|
626
|
+
];
|
|
627
|
+
if (options?.notice) {
|
|
628
|
+
tasks.push('5. 生成完成标志文件');
|
|
629
|
+
}
|
|
630
|
+
return `## 执行流程
|
|
631
|
+
|
|
632
|
+
请使用 **TodoWrite** 工具创建并跟踪以下任务:
|
|
633
|
+
|
|
634
|
+
\`\`\`
|
|
635
|
+
${tasks.join('\n')}
|
|
636
|
+
\`\`\``;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* 构建大型变更执行流程
|
|
640
|
+
*/
|
|
641
|
+
static buildLargeChangeExecutionFlow(options) {
|
|
642
|
+
const noticeTask = options?.notice ? '\n6. 生成完成标志文件' : '';
|
|
643
|
+
return `## 执行流程(分三个阶段)
|
|
644
|
+
|
|
645
|
+
### 阶段一:智能分析与过滤
|
|
646
|
+
|
|
647
|
+
请使用 **TodoWrite** 创建初始任务列表:
|
|
648
|
+
|
|
649
|
+
\`\`\`
|
|
650
|
+
1. 分析变更文件
|
|
651
|
+
2. AI 智能文件过滤
|
|
652
|
+
3. AI 智能制定分批次评审策略
|
|
653
|
+
4. 复核分批次评审策略
|
|
654
|
+
5. [后续任务待分批完成后添加]${noticeTask}
|
|
655
|
+
\`\`\``;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* 构建文件过滤阶段
|
|
659
|
+
*/
|
|
660
|
+
static buildFileFilteringPhase(projectRoot, gitDiffCommand) {
|
|
661
|
+
return `## 第一阶段:AI 智能文件过滤
|
|
662
|
+
|
|
663
|
+
### 步骤 1:获取变更文件列表并进行智能过滤
|
|
664
|
+
|
|
665
|
+
执行以下 bash 命令获取变更文件统计:
|
|
666
|
+
|
|
667
|
+
\`\`\`bash
|
|
668
|
+
cd ${projectRoot}
|
|
669
|
+
git diff --stat ${gitDiffCommand.replace('git diff', '').trim()}
|
|
670
|
+
\`\`\`
|
|
671
|
+
|
|
672
|
+
**然后对变更文件进行 AI 智能过滤分析**:
|
|
673
|
+
|
|
674
|
+
${this.buildFileFilteringRules()}
|
|
675
|
+
|
|
676
|
+
${this.buildFileFilteringOutputFormat()}
|
|
677
|
+
|
|
678
|
+
### 步骤 2:检查过滤结果
|
|
679
|
+
|
|
680
|
+
**重要**:如果所有文件都被过滤掉(需要评审的文件为 0),则:
|
|
681
|
+
1. 输出消息:"✅ 本次变更无需代码评审(全部为文档、配置或自动生成文件)"
|
|
682
|
+
2. 创建简单的 CODE_REVIEW.md 文件说明情况
|
|
683
|
+
3. 结束评审流程
|
|
684
|
+
|
|
685
|
+
如果还有需要评审的文件,继续下一步。`;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* 构建文件过滤规则
|
|
689
|
+
*/
|
|
690
|
+
static buildFileFilteringRules() {
|
|
691
|
+
return `**过滤原则**:
|
|
692
|
+
1. **需要评审的文件**:
|
|
693
|
+
- 所有源代码文件(.java, .ts, .js, .py, .go, .rs, etc.)
|
|
694
|
+
- 配置文件(特别是涉及权限、安全、路由的配置)
|
|
695
|
+
- SQL/数据库迁移文件
|
|
696
|
+
- API定义文件(OpenAPI, GraphQL schemas)
|
|
697
|
+
- 关键的构建脚本(如果包含复杂逻辑)
|
|
698
|
+
|
|
699
|
+
2. **可以过滤掉的文件**:
|
|
700
|
+
- 纯文档文件(README.md, CHANGELOG.md等,除非是技术文档)
|
|
701
|
+
- 依赖锁文件(package-lock.json, yarn.lock, Cargo.lock等)
|
|
702
|
+
- 二进制文件和媒体文件(图片、字体、音视频等)
|
|
703
|
+
- 自动生成的代码(如果有明确标记)
|
|
704
|
+
- 构建产物和编译输出
|
|
705
|
+
- IDE配置文件(.vscode, .idea等)
|
|
706
|
+
- Git相关配置(.gitignore, .gitattributes)
|
|
707
|
+
|
|
708
|
+
3. **不确定时的策略**:
|
|
709
|
+
- 当无法确定文件类型或用途时,**倾向于包含而不是排除**
|
|
710
|
+
- 对于混合内容(如包含代码示例的markdown),考虑保留
|
|
711
|
+
- 对于小文件(<50行),包含成本低,可以保留`;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* 构建文件过滤输出格式
|
|
715
|
+
*/
|
|
716
|
+
static buildFileFilteringOutputFormat() {
|
|
717
|
+
return `**请使用以下格式输出过滤结果**:
|
|
718
|
+
|
|
719
|
+
\`\`\`markdown
|
|
720
|
+
## 文件过滤分析
|
|
721
|
+
|
|
722
|
+
### 需要评审的文件(共 X 个,约 Y 行变更)
|
|
723
|
+
|
|
724
|
+
1. [文件路径] - [变更行数] 行变更
|
|
725
|
+
2. [文件路径] - [变更行数] 行变更
|
|
726
|
+
...
|
|
727
|
+
|
|
728
|
+
### 已过滤的文件(共 Z 个)
|
|
729
|
+
|
|
730
|
+
**[类别名称](N个)**:
|
|
731
|
+
- [文件路径] - [过滤原因]
|
|
732
|
+
...
|
|
733
|
+
|
|
734
|
+
### 统计
|
|
735
|
+
- 原始变更文件总数:X 个
|
|
736
|
+
- 需要评审:X 个(Y%)
|
|
737
|
+
- 已过滤:Z 个(W%)
|
|
738
|
+
- 预计需要评审的代码行数:约 N 行
|
|
739
|
+
\`\`\``;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* 构建代码评审阶段
|
|
743
|
+
*/
|
|
744
|
+
static buildCodeReviewPhase(projectRoot, gitDiffCommand, options) {
|
|
745
|
+
return `## 第二阶段:执行代码评审
|
|
746
|
+
|
|
747
|
+
### 步骤 3:AI 智能代码评审
|
|
748
|
+
|
|
749
|
+
使用 **Task tool** 调用 code-reviewer subagent,**仅对需要评审的文件进行评审**:
|
|
750
|
+
|
|
751
|
+
\`\`\`typescript
|
|
752
|
+
{
|
|
753
|
+
subagent_type: 'code-reviewer',
|
|
754
|
+
description: '代码评审 - 完整变更',
|
|
755
|
+
prompt: \`${this.buildSubagentPrompt(projectRoot, gitDiffCommand, options)}\`
|
|
756
|
+
}
|
|
757
|
+
\`\`\`
|
|
758
|
+
|
|
759
|
+
### 步骤 4:等待评审完成
|
|
760
|
+
|
|
761
|
+
subagent 会自动:
|
|
762
|
+
1. 执行 git diff 命令获取变更内容
|
|
763
|
+
2. 使用 Read tool 读取相关文件(重点关注需要评审的文件)
|
|
764
|
+
3. 按照六个维度进行深度代码评审
|
|
765
|
+
4. 进行自我检查确保评审质量`;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* 构建附加规则部分
|
|
769
|
+
*/
|
|
770
|
+
static buildEnhancedRulesSection(projectRoot, options) {
|
|
771
|
+
// 构建附加规则文件列表
|
|
772
|
+
const enhancedRules = [];
|
|
773
|
+
enhancedRules.push(`${projectRoot}/UMS.md`);
|
|
774
|
+
if (options?.promptEnhancePath) {
|
|
775
|
+
enhancedRules.push(resolve(projectRoot, options.promptEnhancePath));
|
|
776
|
+
}
|
|
777
|
+
return enhancedRules.length > 0 ? `
|
|
778
|
+
## 附加规则
|
|
779
|
+
|
|
780
|
+
尝试读取以下文件(如果文件不存在则跳过):
|
|
781
|
+
|
|
782
|
+
${enhancedRules.map(file => `- ${file}`).join('\n')}
|
|
783
|
+
|
|
784
|
+
从这些文件中提取开发规范、编码标准和评审规则,作为附加规则。
|
|
785
|
+
|
|
786
|
+
如果代码违反了附加规则,将其报告为问题并给出适当的严重性和维度。
|
|
787
|
+
|
|
788
|
+
` : '';
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* 构建 subagent prompt
|
|
792
|
+
*/
|
|
793
|
+
static buildSubagentPrompt(projectRoot, gitDiffCommand, options) {
|
|
794
|
+
return `请按照你的标准评审流程进行全面代码评审。
|
|
795
|
+
|
|
796
|
+
## 变更信息
|
|
797
|
+
|
|
798
|
+
- 需要评审的文件:X 个
|
|
799
|
+
- 变更行数:约 Y 行
|
|
800
|
+
|
|
801
|
+
## 需要评审的文件列表
|
|
802
|
+
|
|
803
|
+
- file1.ts
|
|
804
|
+
- file2.ts
|
|
805
|
+
- file3.ts
|
|
806
|
+
...
|
|
807
|
+
|
|
808
|
+
## 获取变更内容
|
|
809
|
+
|
|
810
|
+
请执行以下命令获取代码变更:
|
|
811
|
+
|
|
812
|
+
\\\`\\\`\\\`bash
|
|
813
|
+
cd ${projectRoot}
|
|
814
|
+
${gitDiffCommand} -- file1.ts file2.ts file3.ts
|
|
815
|
+
\\\`\\\`\\\`
|
|
816
|
+
|
|
817
|
+
(将 file1.ts file2.ts file3.ts 替换为实际的批次文件列表)
|
|
818
|
+
|
|
819
|
+
${this.buildEnhancedRulesSection(projectRoot, options)}
|
|
820
|
+
|
|
821
|
+
## 结果检查
|
|
822
|
+
|
|
823
|
+
在返回结果前,要确保:
|
|
824
|
+
|
|
825
|
+
1. 已评审所有指定文件
|
|
826
|
+
|
|
827
|
+
如果有遗漏、未评审的文件, 请补充评审
|
|
828
|
+
|
|
829
|
+
2. 每个问题都在 diff 中明确存在
|
|
830
|
+
3. 没有基于"理论上可能"的问题
|
|
831
|
+
4. 没有报告已被处理/修复的问题
|
|
832
|
+
5. 问题是真实存在,不是幻觉、"挑毛病"
|
|
833
|
+
|
|
834
|
+
如果有误报的问题,请剔除
|
|
835
|
+
|
|
836
|
+
6. 每个问题都有 [严重性][维度] 标签
|
|
837
|
+
7. CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
|
|
838
|
+
8. MEDIUM/LOW 有:问题描述、简要说明
|
|
839
|
+
9. 问题严重性符合 SEVERITY_GUIDELINES
|
|
840
|
+
10. 问题维度符合 REVIEW_DIMENSIONS
|
|
841
|
+
|
|
842
|
+
如果有描述不当或者信息缺失的报告问题,请调整修正
|
|
843
|
+
|
|
844
|
+
**质量标准**:
|
|
845
|
+
- ✅ 宁可 5 个准确问题,不要 10 个含 3 个误报
|
|
846
|
+
- ✅ 准确性 > 完整性
|
|
847
|
+
- ✅ 不确定时:不报告或降低严重性`;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* 构建报告生成阶段
|
|
851
|
+
*/
|
|
852
|
+
static buildReportGenerationPhase(stats) {
|
|
853
|
+
return `## 第三阶段:生成最终报告
|
|
854
|
+
|
|
855
|
+
### 步骤 5:整理评审结果并生成报告
|
|
856
|
+
|
|
857
|
+
获取 subagent 的评审结果后,按照以下结构生成最终的评审报告,写入 \`CODE_REVIEW.md\`:
|
|
858
|
+
|
|
859
|
+
${this.buildReportTemplate(stats)}
|
|
860
|
+
|
|
861
|
+
**重要提示**
|
|
862
|
+
- 请**逐条提取**所有评审问题,不要总结或遗漏任何细节
|
|
863
|
+
- **每个问题必须包含文件路径**(从 subagent 输出中提取)
|
|
864
|
+
- 保持原始的代码示例、分析和修复建议的完整性
|
|
865
|
+
- 如果某个严重性级别下某个维度没有问题,则跳过该小节
|
|
866
|
+
- 确保统计表中的数字与实际问题数量一致`;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* 构建报告模板
|
|
870
|
+
*/
|
|
871
|
+
static buildReportTemplate(stats) {
|
|
872
|
+
return `\`\`\`markdown
|
|
873
|
+
# 代码评审报告
|
|
874
|
+
|
|
875
|
+
## 变更概览
|
|
876
|
+
|
|
877
|
+
- **原始文件总数**: ${stats.fileCount} 个
|
|
878
|
+
- **评审文件数**: X 个(Y% 需要评审)
|
|
879
|
+
- **过滤文件数**: Z 个(W% 无需评审)
|
|
880
|
+
- **新增行数**: ${stats.addedLines} 行
|
|
881
|
+
- **删除行数**: ${stats.deletedLines} 行
|
|
882
|
+
- **总变更量**: ${stats.totalChanges} 行
|
|
883
|
+
|
|
884
|
+
本次变更主要涉及 [简要概括主要变更内容]。
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## 📋 问题统计
|
|
889
|
+
|
|
890
|
+
| 严重性 | 正确性 | 安全性 | 性能 | 可维护性 | 架构设计 | 最佳实践 | 合计 |
|
|
891
|
+
|--------|--------|--------|------|----------|----------|----------|------|
|
|
892
|
+
| 🚨 CRITICAL | X | X | X | X | X | X | **X** |
|
|
893
|
+
| ⚠️ HIGH | X | X | X | X | X | X | **X** |
|
|
894
|
+
| 🔶 MEDIUM | X | X | X | X | X | X | **X** |
|
|
895
|
+
| 🔷 LOW | X | X | X | X | X | X | **X** |
|
|
896
|
+
| **合计** | **X** | **X** | **X** | **X** | **X** | **X** | **X** |
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
## 🚨 严重问题 (CRITICAL)
|
|
901
|
+
|
|
902
|
+
[如果有CRITICAL级别问题,按维度组织]
|
|
903
|
+
|
|
904
|
+
### 🐛 正确性
|
|
905
|
+
|
|
906
|
+
#### [问题编号]. [问题标题]
|
|
907
|
+
**文件**: [文件路径]
|
|
908
|
+
|
|
909
|
+
**问题描述**:[简要说明]
|
|
910
|
+
|
|
911
|
+
**影响范围**:[如果影响调用方,说明影响]
|
|
912
|
+
|
|
913
|
+
**详细分析**:
|
|
914
|
+
- [详细分析内容]
|
|
915
|
+
|
|
916
|
+
**当前代码**:
|
|
917
|
+
\\\`\\\`\\\`language
|
|
918
|
+
[有问题的代码]
|
|
919
|
+
\\\`\\\`\\\`
|
|
920
|
+
|
|
921
|
+
**建议修复**:
|
|
922
|
+
\\\`\\\`\\\`language
|
|
923
|
+
[修复后的代码]
|
|
924
|
+
\\\`\\\`\\\`
|
|
925
|
+
|
|
926
|
+
---
|
|
927
|
+
|
|
928
|
+
[其他维度的CRITICAL问题,格式相同]
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
## ⚠️ 高风险问题 (HIGH)
|
|
933
|
+
|
|
934
|
+
[按维度组织HIGH级别问题, 格式与CRITICAL相同]
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
938
|
+
## 🔶 中等风险问题 (MEDIUM)
|
|
939
|
+
|
|
940
|
+
[按维度组织MEDIUM级别问题, 格式与CRITICAL相同]
|
|
941
|
+
|
|
942
|
+
---
|
|
943
|
+
|
|
944
|
+
## 🔷 低风险问题 (LOW)
|
|
945
|
+
|
|
946
|
+
[按维度组织LOW级别问题, 格式与CRITICAL相同]
|
|
947
|
+
|
|
948
|
+
---
|
|
949
|
+
|
|
950
|
+
## 📈 整体代码质量评估
|
|
951
|
+
|
|
952
|
+
### 优点
|
|
953
|
+
[列出代码的优点]
|
|
954
|
+
|
|
955
|
+
### 需要改进的方面
|
|
956
|
+
[列出需要改进的方面]
|
|
957
|
+
\`\`\``;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* 构建质量检查阶段
|
|
961
|
+
*/
|
|
962
|
+
static buildQualityCheckPhase(projectRoot, options) {
|
|
963
|
+
let content = `## 第四阶段:质量检查
|
|
964
|
+
|
|
965
|
+
### 步骤 6:评审结果完整性检查
|
|
966
|
+
|
|
967
|
+
生成报告后,请进行以下检查:
|
|
968
|
+
|
|
969
|
+
#### 1. 数量一致性检查
|
|
970
|
+
- 统计表中的总问题数 = 报告中实际列出的问题数
|
|
971
|
+
- 每个维度的计数 = 该维度下实际列出的问题数
|
|
972
|
+
- 每个严重性级别的计数 = 该级别下实际列出的问题数
|
|
973
|
+
|
|
974
|
+
#### 2. 结构完整性检查
|
|
975
|
+
- 确认报告按 **严重性 > 维度** 两级结构组织
|
|
976
|
+
- 确认每个问题都有完整的字段:
|
|
977
|
+
- 问题描述
|
|
978
|
+
- 影响范围(如适用)
|
|
979
|
+
- 详细分析
|
|
980
|
+
- 当前代码
|
|
981
|
+
- 建议修复
|
|
982
|
+
|
|
983
|
+
#### 3. 内容完整性检查
|
|
984
|
+
- 随机抽查 2-3 个问题,对比原始 subagent 输出
|
|
985
|
+
- 确认代码示例、分析细节没有被简化或遗漏
|
|
986
|
+
- 确认技术术语、文件路径等关键信息准确无误
|
|
987
|
+
|
|
988
|
+
**如果发现问题**:
|
|
989
|
+
- 如果数量不一致:重新检查 subagent 输出,补充遗漏的问题
|
|
990
|
+
- 如果结构不正确:按照模板重新组织
|
|
991
|
+
- 如果内容被简化:恢复完整的原始内容
|
|
992
|
+
|
|
993
|
+
**检查通过后**:
|
|
994
|
+
- 输出消息:"✅ 评审报告已生成并通过质量检查"
|
|
995
|
+
- 提供报告的关键统计信息`;
|
|
996
|
+
if (options?.notice) {
|
|
997
|
+
content += `
|
|
998
|
+
|
|
999
|
+
---
|
|
1000
|
+
|
|
1001
|
+
### 步骤 7:生成完成标志文件
|
|
1002
|
+
|
|
1003
|
+
**重要**:为了方便 CI/CD 流水线集成,请在项目根目录创建标志文件:
|
|
1004
|
+
|
|
1005
|
+
使用 **Write** 工具创建文件 \`${projectRoot}/.review-completed\`,内容如下:
|
|
1006
|
+
|
|
1007
|
+
\`\`\`json
|
|
1008
|
+
{
|
|
1009
|
+
"status": "completed",
|
|
1010
|
+
"timestamp": "<当前时间戳>",
|
|
1011
|
+
"report": "CODE_REVIEW.md"
|
|
1012
|
+
}
|
|
1013
|
+
\`\`\`
|
|
1014
|
+
|
|
1015
|
+
**注意**:
|
|
1016
|
+
- 只有在评审报告 \`CODE_REVIEW.md\` 成功创建且质量检查通过后才创建此标志文件
|
|
1017
|
+
- 时间戳格式:ISO 8601(例如:2025-01-15T10:30:00Z)
|
|
1018
|
+
- 此文件用于 CI/CD 流水线检测评审是否完成`;
|
|
1019
|
+
}
|
|
1020
|
+
return content;
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* 构建调用行动
|
|
1024
|
+
*/
|
|
1025
|
+
static buildCallToAction() {
|
|
1026
|
+
return `## 开始执行
|
|
1027
|
+
|
|
1028
|
+
现在请按照上述流程开始执行,首先进行文件过滤分析。`;
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* 构建分析阶段(大型变更)
|
|
1032
|
+
*/
|
|
1033
|
+
static buildAnalysisPhase(projectRoot, gitDiffStatCommand) {
|
|
1034
|
+
return `#### 步骤 1:执行 git diff --stat
|
|
1035
|
+
|
|
1036
|
+
请执行以下命令获取所有变更文件:
|
|
1037
|
+
|
|
1038
|
+
\`\`\`bash
|
|
1039
|
+
cd ${projectRoot}
|
|
1040
|
+
${gitDiffStatCommand}
|
|
1041
|
+
\`\`\`
|
|
1042
|
+
|
|
1043
|
+
**重要提示:不要获取完整 diff!**
|
|
1044
|
+
- 只需要分析 \`git diff --stat\` 的输出
|
|
1045
|
+
- 这将列出所有变更的文件及其变更行数
|
|
1046
|
+
|
|
1047
|
+
---
|
|
1048
|
+
|
|
1049
|
+
#### 步骤 2:智能过滤文件(重要!)
|
|
1050
|
+
|
|
1051
|
+
**目标**:从变更文件中识别出真正需要代码评审的文件。
|
|
1052
|
+
|
|
1053
|
+
${this.buildFileFilteringRules()}
|
|
1054
|
+
|
|
1055
|
+
${this.buildFileFilteringOutputFormat()}
|
|
1056
|
+
|
|
1057
|
+
**重要**:
|
|
1058
|
+
- 必须列出所有变更文件,要么在"需要评审"列表,要么在"已过滤"列表
|
|
1059
|
+
- 每个被过滤的文件都要简要说明原因
|
|
1060
|
+
- 统计数据要准确
|
|
1061
|
+
|
|
1062
|
+
---
|
|
1063
|
+
|
|
1064
|
+
#### 步骤 3:判断评审规模
|
|
1065
|
+
|
|
1066
|
+
基于**过滤后的文件**(即"需要评审的文件"列表),判断是否需要分批:
|
|
1067
|
+
|
|
1068
|
+
- 如果需要评审的文件 ≤ ${REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD} 个 且 变更量 ≤ ${REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD} 行
|
|
1069
|
+
→ 记录"无需分批,可以一次性评审",跳转到阶段二(单批次评审)
|
|
1070
|
+
|
|
1071
|
+
- 否则 → 继续步骤 4 制定分批策略
|
|
1072
|
+
|
|
1073
|
+
---
|
|
1074
|
+
|
|
1075
|
+
#### 步骤 4:制定分批策略(如需分批)
|
|
1076
|
+
|
|
1077
|
+
基于"需要评审的文件"列表,制定分批方案:
|
|
1078
|
+
|
|
1079
|
+
**分批原则**:
|
|
1080
|
+
- 按功能模块、文件类型、业务关联性分组
|
|
1081
|
+
- 每批不超过 ${REVIEW_THRESHOLDS.MAX_FILES_PER_BATCH} 个文件
|
|
1082
|
+
- 每批不超过 ${REVIEW_THRESHOLDS.MAX_CHANGES_PER_BATCH} 行变更
|
|
1083
|
+
- 相关文件放在同一批(如 Service 和其对应的 Test)
|
|
1084
|
+
|
|
1085
|
+
**输出格式**:
|
|
1086
|
+
|
|
1087
|
+
\`\`\`markdown
|
|
1088
|
+
## 分批方案
|
|
1089
|
+
|
|
1090
|
+
### 批次 1:[批次描述,如 "用户认证模块"]
|
|
1091
|
+
- 文件数:8 个
|
|
1092
|
+
- 预计变更行数:650 行
|
|
1093
|
+
- 文件列表:
|
|
1094
|
+
- src/services/UserService.java
|
|
1095
|
+
- src/services/AuthService.java
|
|
1096
|
+
- src/utils/jwt.ts
|
|
1097
|
+
...
|
|
1098
|
+
|
|
1099
|
+
### 批次 2:[批次描述]
|
|
1100
|
+
...
|
|
1101
|
+
|
|
1102
|
+
### 批次 N:[批次描述]
|
|
1103
|
+
...
|
|
1104
|
+
\`\`\`
|
|
1105
|
+
|
|
1106
|
+
---
|
|
1107
|
+
|
|
1108
|
+
#### 步骤 5:确认完整性
|
|
1109
|
+
|
|
1110
|
+
检查分批方案(如果分批)或单批次方案:
|
|
1111
|
+
- [ ] "需要评审的文件"列表中的每个文件都已分配到某个批次
|
|
1112
|
+
- [ ] 没有文件被遗漏
|
|
1113
|
+
- [ ] 没有文件被重复分配
|
|
1114
|
+
|
|
1115
|
+
如果检查通过,更新 TodoWrite,标记步骤1-5为完成,添加后续评审任务,继续阶段二。
|
|
1116
|
+
|
|
1117
|
+
**重要**:此阶段只做分析和规划,不开始评审代码。`;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* 构建分批评审阶段
|
|
1121
|
+
*/
|
|
1122
|
+
static buildBatchReviewPhase(projectRoot, options) {
|
|
1123
|
+
return `### 阶段二:批次评审(完成分批后执行)
|
|
1124
|
+
|
|
1125
|
+
分批方案确定后,更新 **TodoWrite** 任务列表:
|
|
1126
|
+
|
|
1127
|
+
\`\`\`
|
|
1128
|
+
1. [completed] 分析变更文件
|
|
1129
|
+
2. [completed] AI 智能文件过滤
|
|
1130
|
+
3. [completed] AI 智能制定分批次评审策略
|
|
1131
|
+
4. [completed] 复核分批次评审策略
|
|
1132
|
+
5. 评审批次 1/N
|
|
1133
|
+
6. 评审批次 2/N
|
|
1134
|
+
...
|
|
1135
|
+
N+6. 汇总所有批次评审结果
|
|
1136
|
+
N+7. 生成最终评审报告
|
|
1137
|
+
\`\`\`
|
|
1138
|
+
|
|
1139
|
+
对于每个批次:
|
|
1140
|
+
|
|
1141
|
+
1. 标记任务为 **in_progress**
|
|
1142
|
+
2. 使用 **Task tool** 调用 **code-reviewer** subagent
|
|
1143
|
+
3. **验证返回结果**:
|
|
1144
|
+
- 如果返回"未发现需要关注的问题",记录该批次无问题
|
|
1145
|
+
- 如果返回了问题列表,快速检查格式是否正确
|
|
1146
|
+
4. 标记为 **completed**,继续下一批次
|
|
1147
|
+
|
|
1148
|
+
**Task tool 调用示例**:
|
|
1149
|
+
|
|
1150
|
+
\`\`\`typescript
|
|
1151
|
+
{
|
|
1152
|
+
subagent_type: 'code-reviewer',
|
|
1153
|
+
description: '代码评审 - 批次 1/N: [批次描述]',
|
|
1154
|
+
prompt: \`请按照你的标准评审流程进行全面代码评审(批次 1/N)。
|
|
1155
|
+
|
|
1156
|
+
## 批次信息
|
|
1157
|
+
|
|
1158
|
+
- 批次描述:[描述,如 "用户认证模块"]
|
|
1159
|
+
- 文件数:X 个
|
|
1160
|
+
- 预计变更行数:Y 行
|
|
1161
|
+
|
|
1162
|
+
## 需要评审的文件列表
|
|
1163
|
+
|
|
1164
|
+
- file1.ts
|
|
1165
|
+
- file2.ts
|
|
1166
|
+
- file3.ts
|
|
1167
|
+
...
|
|
1168
|
+
|
|
1169
|
+
## 获取变更内容
|
|
1170
|
+
|
|
1171
|
+
请执行以下命令获取本批次的代码变更:
|
|
1172
|
+
|
|
1173
|
+
\\\`\\\`\\\`bash
|
|
1174
|
+
cd ${projectRoot}
|
|
1175
|
+
git diff ${GitUtils.buildComparisonString(options, 'command')} -- file1.ts file2.ts file3.ts
|
|
1176
|
+
\\\`\\\`\\\`
|
|
1177
|
+
|
|
1178
|
+
(将 file1.ts file2.ts file3.ts 替换为实际的批次文件列表)
|
|
1179
|
+
|
|
1180
|
+
${this.buildEnhancedRulesSection(projectRoot, options)}
|
|
1181
|
+
|
|
1182
|
+
## 结果检查(非常重要)
|
|
1183
|
+
|
|
1184
|
+
在返回结果开头请注明:**批次 1/N - [批次描述]**
|
|
1185
|
+
|
|
1186
|
+
在返回结果前,要确保:
|
|
1187
|
+
|
|
1188
|
+
1. 已评审所有指定文件
|
|
1189
|
+
|
|
1190
|
+
如果有遗漏、未评审的文件, 请补充评审
|
|
1191
|
+
|
|
1192
|
+
2. 每个问题都在 diff 中明确存在
|
|
1193
|
+
3. 没有基于"理论上可能"的问题
|
|
1194
|
+
4. 没有报告已被处理/修复的问题
|
|
1195
|
+
5. 问题是真实存在,不是幻觉、"挑毛病"
|
|
1196
|
+
|
|
1197
|
+
如果有误报的问题,请剔除
|
|
1198
|
+
|
|
1199
|
+
6. 每个问题都有 [严重性][维度] 标签
|
|
1200
|
+
7. CRITICAL/HIGH 有:问题描述、详细分析、当前代码、建议修复
|
|
1201
|
+
8. MEDIUM/LOW 有:问题描述、简要说明
|
|
1202
|
+
9. 问题严重性符合 SEVERITY_GUIDELINES
|
|
1203
|
+
10. 问题维度符合 REVIEW_DIMENSIONS
|
|
1204
|
+
|
|
1205
|
+
如果有描述不当或者信息缺失的报告问题,请调整修正
|
|
1206
|
+
|
|
1207
|
+
**质量标准**:
|
|
1208
|
+
- ✅ 宁可 5 个准确问题,不要 10 个含 3 个误报
|
|
1209
|
+
- ✅ 准确性 > 完整性
|
|
1210
|
+
- ✅ 不确定时:不报告或降低严重性\`
|
|
1211
|
+
}
|
|
1212
|
+
\`\`\`
|
|
1213
|
+
|
|
1214
|
+
**关键点**:
|
|
1215
|
+
- 不要在主 task 中获取完整 diff,只提供 git diff 命令
|
|
1216
|
+
- code-reviewer subagent 会自己执行命令并获取 diff
|
|
1217
|
+
- code-reviewer subagent 会自己读取所有相关文件
|
|
1218
|
+
- code-reviewer subagent 会自己进行深度代码评审并自检
|
|
1219
|
+
- code-reviewer subagent 会返回评审结果`;
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* 构建汇总阶段
|
|
1223
|
+
*/
|
|
1224
|
+
static buildSummaryPhase(stats, projectRoot, options) {
|
|
1225
|
+
let content = `### 阶段三:汇总报告(所有批次完成后)
|
|
1226
|
+
|
|
1227
|
+
所有批次评审完成后,执行以下步骤生成最终报告:
|
|
1228
|
+
|
|
1229
|
+
#### 步骤 1:收集所有评审结果
|
|
1230
|
+
|
|
1231
|
+
1. 标记汇总任务为 **in_progress**
|
|
1232
|
+
2. 从所有 code-reviewer subagent 的返回结果中,**逐条提取**所有问题
|
|
1233
|
+
3. 每个问题保留完整信息(不修改、不删减):
|
|
1234
|
+
- 文件路径
|
|
1235
|
+
- [严重性][维度] 标签
|
|
1236
|
+
- 问题标题
|
|
1237
|
+
- 问题描述
|
|
1238
|
+
- 影响范围
|
|
1239
|
+
- 详细分析
|
|
1240
|
+
- 当前代码(完整上下文)
|
|
1241
|
+
- 建议修复(完整代码)
|
|
1242
|
+
|
|
1243
|
+
**关键要求**:
|
|
1244
|
+
- 必须收集**所有问题**,不得遗漏任何一个
|
|
1245
|
+
- 必须保留**所有细节**,包括代码块、修复建议等
|
|
1246
|
+
- 如果某个subagent返回"未发现需要关注的问题",则该批次无问题
|
|
1247
|
+
- 不要对问题进行总结或简化
|
|
1248
|
+
|
|
1249
|
+
---
|
|
1250
|
+
|
|
1251
|
+
#### 步骤 2:按维度和严重性重新组织
|
|
1252
|
+
|
|
1253
|
+
将收集到的所有问题按照以下结构重新组织:
|
|
1254
|
+
|
|
1255
|
+
\`\`\`
|
|
1256
|
+
CRITICAL 问题列表
|
|
1257
|
+
├── 正确性
|
|
1258
|
+
│ ├── 问题1(完整信息)
|
|
1259
|
+
│ └── 问题2(完整信息)
|
|
1260
|
+
├── 安全性
|
|
1261
|
+
│ └── 问题3(完整信息)
|
|
1262
|
+
└── 性能
|
|
1263
|
+
└── 问题4(完整信息)
|
|
1264
|
+
|
|
1265
|
+
HIGH 问题列表
|
|
1266
|
+
├── 正确性
|
|
1267
|
+
├── 安全性
|
|
1268
|
+
├── 性能
|
|
1269
|
+
├── 可维护性
|
|
1270
|
+
├── 架构设计
|
|
1271
|
+
└── 最佳实践
|
|
1272
|
+
|
|
1273
|
+
MEDIUM 问题列表
|
|
1274
|
+
...
|
|
1275
|
+
|
|
1276
|
+
LOW 问题列表
|
|
1277
|
+
...
|
|
1278
|
+
\`\`\`
|
|
1279
|
+
|
|
1280
|
+
**组织要求**:
|
|
1281
|
+
- 第一层:按严重性分类(CRITICAL → HIGH → MEDIUM → LOW)
|
|
1282
|
+
- 第二层:按维度分类(正确性、安全性、性能、可维护性、架构设计、最佳实践)
|
|
1283
|
+
- 如果某个严重性级别下某个维度没有问题,则不显示该维度
|
|
1284
|
+
- 每个问题保留完整的原始信息,不进行任何删减
|
|
1285
|
+
|
|
1286
|
+
---
|
|
1287
|
+
|
|
1288
|
+
#### 步骤 3:生成最终报告
|
|
1289
|
+
|
|
1290
|
+
按照以下格式生成 \`CODE_REVIEW.md\`:
|
|
1291
|
+
|
|
1292
|
+
${this.buildReportTemplate(stats)}
|
|
1293
|
+
|
|
1294
|
+
**注意**:评审报告中不要体现批次信息,需要整体看上去是一次性完成的评审。
|
|
1295
|
+
|
|
1296
|
+
---
|
|
1297
|
+
|
|
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
|
+
**所有检查项通过后**:
|
|
1323
|
+
1. 将最终报告写入 \`CODE_REVIEW.md\`
|
|
1324
|
+
2. 标记任务为 **completed**
|
|
1325
|
+
3. 输出简要总结:
|
|
1326
|
+
\`\`\`
|
|
1327
|
+
✅ 代码评审完成
|
|
1328
|
+
|
|
1329
|
+
📊 评审统计:
|
|
1330
|
+
- 批次数:X
|
|
1331
|
+
- 原始文件数:Y
|
|
1332
|
+
- 评审文件数:Z
|
|
1333
|
+
- 问题总数:W (CRITICAL: a, HIGH: b, MEDIUM: c, LOW: d)
|
|
1334
|
+
|
|
1335
|
+
📁 报告已生成:CODE_REVIEW.md
|
|
1336
|
+
\`\`\``;
|
|
1337
|
+
if (options?.notice) {
|
|
1338
|
+
content += `
|
|
1339
|
+
|
|
1340
|
+
---
|
|
1341
|
+
|
|
1342
|
+
#### 步骤 5:生成完成标志文件
|
|
1343
|
+
|
|
1344
|
+
**重要**:为了方便 CI/CD 流水线集成,请在项目根目录创建标志文件:
|
|
1345
|
+
|
|
1346
|
+
使用 **Write** 工具创建文件 \`${projectRoot}/.review-completed\`,内容如下:
|
|
1347
|
+
|
|
1348
|
+
\`\`\`json
|
|
1349
|
+
{
|
|
1350
|
+
"status": "completed",
|
|
1351
|
+
"timestamp": "<当前时间戳>",
|
|
1352
|
+
"report": "CODE_REVIEW.md"
|
|
1353
|
+
}
|
|
1354
|
+
\`\`\`
|
|
1355
|
+
|
|
1356
|
+
**注意**:
|
|
1357
|
+
- 只有在评审报告 \`CODE_REVIEW.md\` 成功创建后才创建此标志文件
|
|
1358
|
+
- 时间戳格式:ISO 8601(例如:2025-01-15T10:30:00Z)
|
|
1359
|
+
- 此文件用于 CI/CD 流水线检测评审是否完成`;
|
|
1360
|
+
}
|
|
1361
|
+
return content;
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* 构建大型变更调用行动
|
|
1365
|
+
*/
|
|
1366
|
+
static buildLargeChangeCallToAction() {
|
|
1367
|
+
return `## 立即开始
|
|
1368
|
+
|
|
1369
|
+
**第一步**:使用 TodoWrite 创建任务列表
|
|
1370
|
+
|
|
1371
|
+
**第二步**:执行 git diff --stat 命令获取所有变更文件
|
|
1372
|
+
|
|
1373
|
+
**第三步**:智能过滤文件,识别需要评审的文件
|
|
1374
|
+
|
|
1375
|
+
**第四步**:基于过滤后的文件制定分批策略(如需要)
|
|
1376
|
+
|
|
1377
|
+
**重要**:此阶段只做分析、过滤和规划,不开始评审代码
|
|
1378
|
+
**重要**:此阶段后请继续执行阶段二和阶段三,不要中断,不要询问是否继续
|
|
1379
|
+
|
|
1380
|
+
请开始执行!`;
|
|
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
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
// ============================================================================
|
|
1775
|
+
// 评审策略选择器
|
|
1776
|
+
// ============================================================================
|
|
1777
|
+
/**
|
|
1778
|
+
* 评审策略选择器
|
|
1779
|
+
*/
|
|
1780
|
+
class ReviewStrategySelector {
|
|
1781
|
+
/**
|
|
1782
|
+
* 根据统计信息选择评审策略
|
|
1783
|
+
*/
|
|
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
|
+
// 其他模式
|
|
1790
|
+
const isLargeChange = this.isLargeChange(stats);
|
|
1791
|
+
if (isLargeChange) {
|
|
1792
|
+
const gitDiffStatCommand = GitUtils.buildDiffStatCommand(options);
|
|
1793
|
+
return ReviewPromptBuilder.buildLargeChangePrompt(stats, projectRoot, gitDiffStatCommand, options);
|
|
1794
|
+
}
|
|
1795
|
+
else {
|
|
1796
|
+
const gitDiffCommand = GitUtils.buildDiffCommand(options);
|
|
1797
|
+
return ReviewPromptBuilder.buildSmallChangePrompt(stats, projectRoot, gitDiffCommand, options);
|
|
1798
|
+
}
|
|
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
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* 判断是否为大型变更
|
|
1814
|
+
*/
|
|
1815
|
+
static isLargeChange(stats) {
|
|
1816
|
+
return stats.fileCount > REVIEW_THRESHOLDS.LARGE_FILE_THRESHOLD ||
|
|
1817
|
+
stats.totalChanges > REVIEW_THRESHOLDS.LARGE_CHANGE_THRESHOLD;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
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
|
+
// ============================================================================
|
|
1964
|
+
// 命令导出
|
|
1965
|
+
// ============================================================================
|
|
1966
|
+
export const reviewCommand = {
|
|
1967
|
+
name: 'review',
|
|
1968
|
+
description: 'Performs code review with multiple modes: default (uncommitted changes), commit comparison, branch comparison, or multiple commits comparison.',
|
|
1969
|
+
kind: CommandKind.BUILT_IN,
|
|
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
|
+
}
|
|
1980
|
+
// 验证服务可用性
|
|
1981
|
+
if (!context.services.config) {
|
|
1982
|
+
return {
|
|
1983
|
+
type: 'message',
|
|
1984
|
+
messageType: 'error',
|
|
1985
|
+
content: 'Configuration not available.',
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
if (!context.services.git) {
|
|
1989
|
+
return {
|
|
1990
|
+
type: 'message',
|
|
1991
|
+
messageType: 'error',
|
|
1992
|
+
content: 'Git service not available. This command requires a git repository.',
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1995
|
+
const projectRoot = context.services.config.getProjectRoot();
|
|
1996
|
+
if (!projectRoot) {
|
|
1997
|
+
return {
|
|
1998
|
+
type: 'message',
|
|
1999
|
+
messageType: 'error',
|
|
2000
|
+
content: 'Project root not available.',
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
try {
|
|
2004
|
+
// 解析参数
|
|
2005
|
+
const options = ReviewArgumentParser.parse(args);
|
|
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
|
+
}
|
|
2019
|
+
// 检查是否有变更
|
|
2020
|
+
if (stats.fileCount === 0) {
|
|
2021
|
+
return {
|
|
2022
|
+
type: 'message',
|
|
2023
|
+
messageType: 'info',
|
|
2024
|
+
content: 'No differences found to review.',
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
// 根据变更规模选择评审策略
|
|
2028
|
+
const reviewPrompt = ReviewStrategySelector.selectStrategy(stats, projectRoot, options, changeList);
|
|
2029
|
+
return {
|
|
2030
|
+
type: 'submit_prompt',
|
|
2031
|
+
content: reviewPrompt,
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
catch (error) {
|
|
2035
|
+
const errorMessage = error instanceof ReviewCommandError
|
|
2036
|
+
? error.message
|
|
2037
|
+
: `Error during review: ${error instanceof Error ? error.message : String(error)}`;
|
|
2038
|
+
return {
|
|
2039
|
+
type: 'message',
|
|
2040
|
+
messageType: 'error',
|
|
2041
|
+
content: errorMessage,
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
},
|
|
2045
|
+
};
|
|
2046
|
+
//# sourceMappingURL=reviewCommand.js.map
|