From ecc2e96033c2976464a1fae06d9dd1bb7fd59a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E7=BB=8D=E5=BA=B759912?= <59912@sangfor.com> Date: Sat, 6 Sep 2025 10:54:47 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9costrict?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jetbrains_plugin/README_EXTENSIONS.md | 8 +- .../agent/extensions/common/ExtensionType.kt | 1 + .../config/ExtensionConfiguration.kt | 13 + .../agent/extensions/core/ExtensionManager.kt | 2 + .../costrict/CostrictCodeActionConstants.kt | 252 +++++++++++ .../costrict/CostrictCodeButtonProvider.kt | 213 +++++++++ .../CostrictCodeContextMenuProvider.kt | 407 ++++++++++++++++++ .../costrict/CostrictExtensionProvider.kt | 95 ++++ .../actions/DynamicExtensionActionsGroup.kt | 3 + .../ui/buttons/DynamicButtonManager.kt | 2 + .../contextmenu/DynamicContextMenuManager.kt | 2 + .../extensions/ExtensionDecouplingTest.kt | 2 + 12 files changed, 999 insertions(+), 1 deletion(-) create mode 100644 jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt create mode 100644 jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt create mode 100644 jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt create mode 100644 jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt diff --git a/jetbrains_plugin/README_EXTENSIONS.md b/jetbrains_plugin/README_EXTENSIONS.md index 5ce8980a..59397ed4 100644 --- a/jetbrains_plugin/README_EXTENSIONS.md +++ b/jetbrains_plugin/README_EXTENSIONS.md @@ -24,6 +24,12 @@ RunVSAgent now supports a modular extension system that allows you to use differ - **Publisher**: Kilo-AI - **Directory**: `kilo-code/` +### 4. Costrict +- **ID**: `costrict` +- **Description**: AI-powered code assistant with advanced capabilities +- **Publisher**: zgsm-ai +- **Directory**: `costrict/` + ## Quick Start ### For Users @@ -40,7 +46,7 @@ RunVSAgent now supports a modular extension system that allows you to use differ ```properties extension.type=roo-code ``` - - Supported values: `roo-code`, `cline`, `kilo-code` + - Supported values: `roo-code`, `cline`, `kilo-code`, `costrict` 3. **Extension Directory Structure**: ``` diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt index 0519726f..6f1ed7e8 100644 --- a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt @@ -8,6 +8,7 @@ enum class ExtensionType(val code: String, val displayName: String, val descript ROO_CODE("roo-code", "Roo Code", "AI-powered code assistant"), CLINE("cline", "Cline AI", "AI-powered coding assistant with advanced features"), KILO_CODE("kilo-code", "Kilo Code", "AI-powered code assistant with advanced capabilities"), + COSTRICT("costrict", "Costrict", "AI-powered code assistant with advanced capabilities"), ; companion object { diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/config/ExtensionConfiguration.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/config/ExtensionConfiguration.kt index 93c53ddc..42c83518 100644 --- a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/config/ExtensionConfiguration.kt +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/config/ExtensionConfiguration.kt @@ -194,6 +194,19 @@ data class ExtensionConfig( capabilities = emptyMap(), extensionDependencies = emptyList() ) + ExtensionType.COSTRICT -> ExtensionConfig( + extensionType = extensionType, + codeDir = "costrict", + displayName = "Costrict", + description = "AI-powered code assistant with advanced capabilities", + publisher = "zgsm-ai", + version = "1.6.5", + mainFile = "./dist/extension.js", + activationEvents = listOf("onStartupFinished"), + engines = mapOf("vscode" to "^1.0.0"), + capabilities = emptyMap(), + extensionDependencies = emptyList() + ) } } diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt index f0031d9e..49c7e331 100644 --- a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt @@ -12,6 +12,7 @@ import com.sina.weibo.agent.extensions.config.ExtensionProvider import com.sina.weibo.agent.extensions.plugin.cline.ClineExtensionProvider import com.sina.weibo.agent.extensions.plugin.roo.RooExtensionProvider import com.sina.weibo.agent.extensions.plugin.kilo.KiloCodeExtensionProvider +import com.sina.weibo.agent.extensions.plugin.costrict.CostrictExtensionProvider import com.sina.weibo.agent.extensions.ui.buttons.DynamicButtonManager import java.util.concurrent.CompletableFuture import java.util.concurrent.ConcurrentHashMap @@ -109,6 +110,7 @@ class ExtensionManager(private val project: Project) { add(RooExtensionProvider()) add(ClineExtensionProvider()) add(KiloCodeExtensionProvider()) + add(CostrictExtensionProvider()) } } diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt new file mode 100644 index 00000000..c00ad123 --- /dev/null +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt @@ -0,0 +1,252 @@ +// SPDX-FileCopyrightText: 2025 Weibo, Inc. +// +// SPDX-License-Identifier: Apache-2.0 + +package com.sina.weibo.agent.extensions.plugin.costrict + +/** Type alias for prompt type identifiers */ +typealias CostrictCodeSupportPromptType = String +/** Type alias for prompt parameters map */ +typealias CostrictCodePromptParams = Map + +/** + * Data class representing a prompt configuration with a template string. + * Templates contain placeholders that will be replaced with actual values. + */ +data class CostrictCodeSupportPromptConfig(val template: String) + +/** + * Collection of predefined prompt configurations for different use cases. + * Each configuration contains a template with placeholders for dynamic content. + * + * now organized under the Costrict extension. + */ +object CostrictCodeSupportPromptConfigs { + /** + * Template for enhancing user prompts. + * Instructs the AI to generate an improved version of the user's input. + */ + val ENHANCE = CostrictCodeSupportPromptConfig( + """Generate an enhanced version of this prompt (reply with only the enhanced prompt - no conversation, explanations, lead-in, bullet points, placeholders, or surrounding quotes): + +${'$'}{userInput}""" + ) + + /** + * Template for explaining code. + * Provides structure for code explanation requests with file path and line information. + */ + val EXPLAIN = CostrictCodeSupportPromptConfig( + """Explain the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} +${'$'}{userInput} + +``` +${'$'}{selectedText} +``` + +Please provide a clear and concise explanation of what this code does, including: +1. The purpose and functionality +2. Key components and their interactions +3. Important patterns or techniques used""" + ) + + /** + * Template for fixing code issues. + * Includes diagnostic information and structured format for issue resolution. + */ + val FIX = CostrictCodeSupportPromptConfig( + """Fix any issues in the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} +${'$'}{diagnosticText} +${'$'}{userInput} + +``` +${'$'}{selectedText} +``` + +Please: +1. Address all detected problems listed above (if any) +2. Identify any other potential bugs or issues +3. Provide corrected code +4. Explain what was fixed and why""" + ) + + /** + * Template for improving code quality. + * Focuses on readability, performance, best practices, and error handling. + */ + val IMPROVE = CostrictCodeSupportPromptConfig( + """Improve the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} +${'$'}{userInput} + +``` +${'$'}{selectedText} +``` + +Please suggest improvements for: +1. Code readability and maintainability +2. Performance optimization +3. Best practices and patterns +4. Error handling and edge cases + +Provide the improved code along with explanations for each enhancement.""" + ) + + /** + * Template for adding code to context. + * Simple format that includes file path, line range, and selected code. + */ + val ADD_TO_CONTEXT = CostrictCodeSupportPromptConfig( + """${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} +``` +${'$'}{selectedText} +```""" + ) + + /** + * Template for adding terminal output to context. + * Includes user input and terminal content. + */ + val TERMINAL_ADD_TO_CONTEXT = CostrictCodeSupportPromptConfig( + """${'$'}{userInput} +Terminal output: +``` +${'$'}{terminalContent} +```""" + ) + + /** + * Template for fixing terminal commands. + * Structured format for identifying and resolving command issues. + */ + val TERMINAL_FIX = CostrictCodeSupportPromptConfig( + """${'$'}{userInput} +Fix this terminal command: +``` +${'$'}{terminalContent} +``` + +Please: +1. Identify any issues in the command +2. Provide the corrected command +3. Explain what was fixed and why""" + ) + + /** + * Template for explaining terminal commands. + * Provides structure for command explanation with focus on functionality and behavior. + */ + val TERMINAL_EXPLAIN = CostrictCodeSupportPromptConfig( + """${'$'}{userInput} +Explain this terminal command: +``` +${'$'}{terminalContent} +``` + +Please provide: +1. What the command does +2. Explanation of each part/flag +3. Expected output and behavior""" + ) + + /** + * Template for creating a new task. + * Simple format that passes through user input directly. + */ + val NEW_TASK = CostrictCodeSupportPromptConfig( + """${'$'}{userInput}""" + ) + + /** + * Map of all available prompt configurations indexed by their type identifiers. + * Used for lookup when creating prompts. + */ + val configs = mapOf( + "ENHANCE" to ENHANCE, + "EXPLAIN" to EXPLAIN, + "FIX" to FIX, + "IMPROVE" to IMPROVE, + "ADD_TO_CONTEXT" to ADD_TO_CONTEXT, + "TERMINAL_ADD_TO_CONTEXT" to TERMINAL_ADD_TO_CONTEXT, + "TERMINAL_FIX" to TERMINAL_FIX, + "TERMINAL_EXPLAIN" to TERMINAL_EXPLAIN, + "NEW_TASK" to NEW_TASK + ) +} + +/** + * Utility object for working with Costrict Code support prompts. + * Provides methods for creating and customizing prompts based on templates. + * + * now organized under the Costrict extension. + */ +object CostrictCodeSupportPrompt { + /** + * Generates formatted diagnostic text from a list of diagnostic items. + * + * @param diagnostics List of diagnostic items containing source, message, and code + * @return Formatted string of diagnostic messages or empty string if no diagnostics + */ + private fun generateDiagnosticText(diagnostics: List>?): String { + if (diagnostics.isNullOrEmpty()) return "" + return "\nCurrent problems detected:\n" + diagnostics.joinToString("\n") { d -> + val source = d["source"] as? String ?: "Error" + val message = d["message"] as? String ?: "" + val code = d["code"] as? String + "- [$source] $message${code?.let { " ($it)" } ?: ""}" + } + } + + /** + * Creates a prompt by replacing placeholders in a template with actual values. + * + * @param template The prompt template with placeholders + * @param params Map of parameter values to replace placeholders + * @return The processed prompt with placeholders replaced by actual values + */ + private fun createPrompt(template: String, params: CostrictCodePromptParams): String { + val pattern = Regex("""\$\{(.*?)}""") + return pattern.replace(template) { matchResult -> + val key = matchResult.groupValues[1] + if (key == "diagnosticText") { + generateDiagnosticText(params["diagnostics"] as? List>) + } else if (params.containsKey(key)) { + // Ensure the value is treated as a string for replacement + val value = params[key] + when (value) { + is String -> value + else -> { + // Convert non-string values to string for replacement + value?.toString() ?: "" + } + } + } else { + // If the placeholder key is not in params, replace with empty string + "" + } + } + } + + /** + * Gets the template for a specific prompt type, with optional custom overrides. + * + * @param customSupportPrompts Optional map of custom prompt templates + * @param type The type of prompt to retrieve + * @return The template string for the specified prompt type + */ + fun get(customSupportPrompts: Map?, type: CostrictCodeSupportPromptType): String { + return customSupportPrompts?.get(type) ?: CostrictCodeSupportPromptConfigs.configs[type]?.template ?: "" + } + + /** + * Creates a complete prompt by getting the template and replacing placeholders. + * + * @param type The type of prompt to create + * @param params Parameters to substitute into the template + * @param customSupportPrompts Optional custom prompt templates + * @return The final prompt with all placeholders replaced + */ + fun create(type: CostrictCodeSupportPromptType, params: CostrictCodePromptParams, customSupportPrompts: Map? = null): String { + val template = get(customSupportPrompts, type) + return createPrompt(template, params) + } +} \ No newline at end of file diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt new file mode 100644 index 00000000..a0e74ae1 --- /dev/null +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: 2025 Weibo, Inc. +// +// SPDX-License-Identifier: Apache-2.0 + +package com.sina.weibo.agent.extensions.plugin.costrict + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import com.intellij.icons.AllIcons +import com.sina.weibo.agent.actions.* +import com.sina.weibo.agent.extensions.ui.buttons.ExtensionButtonProvider +import com.sina.weibo.agent.extensions.ui.buttons.ButtonType +import com.sina.weibo.agent.extensions.ui.buttons.ButtonConfiguration + +/** + * Costrict extension button provider. + * Provides button configuration specific to Costrict extension. + */ +class CostrictCodeButtonProvider : ExtensionButtonProvider { + + override fun getExtensionId(): String = "costrict" + + override fun getDisplayName(): String = "Costrict" + + override fun getDescription(): String = "AI-powered code assistant with advanced capabilities" + + override fun isAvailable(project: Project): Boolean { + // Check if costrict extension is available + // This could include checking for extension files, dependencies, etc. + return true + } + + override fun getButtons(project: Project): List { + // Note: project parameter kept for future extensibility + return listOf( + PlusButtonClickAction(), + PromptsButtonClickAction(), + MCPButtonClickAction(), + HistoryButtonClickAction(), + MarketplaceButtonClickAction(), + SettingsButtonClickAction() + ) + } + + override fun getButtonConfiguration(): ButtonConfiguration { + return CostrictCodeButtonConfiguration() + } + + /** + * Costrict button configuration - shows all buttons (full-featured). + */ + private class CostrictCodeButtonConfiguration : ButtonConfiguration { + override fun isButtonVisible(buttonType: ButtonType): Boolean { + return true // All buttons are visible for Costrict + } + + override fun getVisibleButtons(): List { + return ButtonType.values().toList() + } + } + + /** + * Action that handles clicks on the Plus button in the UI. + * Executes the corresponding VSCode command when triggered. + */ + class PlusButtonClickAction : AnAction() { + private val logger: Logger = Logger.getInstance(PlusButtonClickAction::class.java) + private val commandId: String = "zgsm.plusButtonClicked" + + init { + templatePresentation.icon = AllIcons.General.Add + templatePresentation.text = "New Task" + templatePresentation.description = "New task" + } + + /** + * Performs the action when the Plus button is clicked. + * + * @param e The action event containing context information + */ + override fun actionPerformed(e: AnActionEvent) { + logger.info("Plus button clicked") + executeCommand(commandId,e.project) + } + } + + /** + * Action that handles clicks on the Prompts button in the UI. + * Executes the corresponding VSCode command when triggered. + */ + class PromptsButtonClickAction : AnAction() { + private val logger: Logger = Logger.getInstance(PromptsButtonClickAction::class.java) + private val commandId: String = "zgsm.promptsButtonClicked" + + init { + templatePresentation.icon = AllIcons.General.Information + templatePresentation.text = "Prompt" + templatePresentation.description = "Prompts" + } + + /** + * Performs the action when the Prompts button is clicked. + * + * @param e The action event containing context information + */ + override fun actionPerformed(e: AnActionEvent) { + logger.info("Prompts button clicked") + executeCommand(commandId, e.project) + } + } + + /** + * Action that handles clicks on the MCP button in the UI. + * Executes the corresponding VSCode command when triggered. + */ + class MCPButtonClickAction : AnAction() { + private val logger: Logger = Logger.getInstance(MCPButtonClickAction::class.java) + private val commandId: String = "zgsm.mcpButtonClicked" + + init { + templatePresentation.icon = AllIcons.Webreferences.Server + templatePresentation.text = "MCP Server" + templatePresentation.description = "MCP server" + } + + /** + * Performs the action when the MCP button is clicked. + * + * @param e The action event containing context information + */ + override fun actionPerformed(e: AnActionEvent) { + logger.info("MCP button clicked") + executeCommand(commandId, e.project) + } + } + + /** + * Action that handles clicks on the History button in the UI. + * Executes the corresponding VSCode command when triggered. + */ + class HistoryButtonClickAction : AnAction() { + private val logger: Logger = Logger.getInstance(HistoryButtonClickAction::class.java) + private val commandId: String = "zgsm.historyButtonClicked" + + init { + templatePresentation.icon = AllIcons.Vcs.History + templatePresentation.text = "History" + templatePresentation.description = "History" + } + + /** + * Performs the action when the History button is clicked. + * + * @param e The action event containing context information + */ + override fun actionPerformed(e: AnActionEvent) { + logger.info("History button clicked") + executeCommand(commandId, e.project) + } + } + + /** + * Action that handles clicks on the Settings button in the UI. + * Executes the corresponding VSCode command when triggered. + */ + class SettingsButtonClickAction : AnAction() { + private val logger: Logger = Logger.getInstance(SettingsButtonClickAction::class.java) + private val commandId: String = "zgsm.settingsButtonClicked" + + init { + templatePresentation.icon = AllIcons.General.Settings + templatePresentation.text = "Settings" + templatePresentation.description = "Setting" + } + + /** + * Performs the action when the Settings button is clicked. + * + * @param e The action event containing context information + */ + override fun actionPerformed(e: AnActionEvent) { + logger.info("Settings button clicked") + executeCommand(commandId, e.project) + } + } + + /** + * Action that handles clicks on the Marketplace button in the UI. + * Executes the corresponding VSCode command when triggered. + */ + class MarketplaceButtonClickAction : AnAction() { + private val logger: Logger = Logger.getInstance(MarketplaceButtonClickAction::class.java) + private val commandId: String = "zgsm.marketplaceButtonClicked" + + init { + templatePresentation.icon = AllIcons.Actions.Install + templatePresentation.text = "MCP Marketplace" + templatePresentation.description = "Marketplace" + } + + /** + * Performs the action when the Marketplace button is clicked. + * + * @param e The action event containing context information + */ + override fun actionPerformed(e: AnActionEvent) { + logger.info("Marketplace button clicked") + executeCommand(commandId, e.project) + } + } +} \ No newline at end of file diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt new file mode 100644 index 00000000..467fe1ed --- /dev/null +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt @@ -0,0 +1,407 @@ +// SPDX-FileCopyrightText: 2025 Weibo, Inc. +// +// SPDX-License-Identifier: Apache-2.0 + +package com.sina.weibo.agent.extensions.plugin.costrict + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import com.sina.weibo.agent.extensions.ui.contextmenu.ExtensionContextMenuProvider +import com.sina.weibo.agent.extensions.ui.contextmenu.ContextMenuConfiguration +import com.sina.weibo.agent.extensions.ui.contextmenu.ContextMenuActionType +import com.sina.weibo.agent.webview.WebViewManager + +/** + * Costrict extension context menu provider. + * Provides context menu actions specific to Costrict extension. + * This includes all the advanced functionality evolved from roo. + */ +class CostrictCodeContextMenuProvider : ExtensionContextMenuProvider { + + override fun getExtensionId(): String = "costrict" + + override fun getDisplayName(): String = "Costrict" + + override fun getDescription(): String = "AI-powered code assistant with advanced capabilities and full context menu" + + override fun isAvailable(project: Project): Boolean { + // Check if costrict extension is available + return true + } + + override fun getContextMenuActions(project: Project): List { + return listOf( + ExplainCodeAction(), + FixCodeAction(), + FixLogicAction(), + ImproveCodeAction(), + AddToContextAction(), + ) + } + + override fun getContextMenuConfiguration(): ContextMenuConfiguration { + return CostrictCodeContextMenuConfiguration() + } + + /** + * Costrict context menu configuration - shows all actions (full-featured). + */ + private class CostrictCodeContextMenuConfiguration : ContextMenuConfiguration { + override fun isActionVisible(actionType: ContextMenuActionType): Boolean { + return true // All actions are visible for Costrict + } + + override fun getVisibleActions(): List { + return ContextMenuActionType.values().toList() + } + } + + /** + * Action to explain selected code. + * Creates a new task with the explanation request. + */ + class ExplainCodeAction : AnAction("Explain Code") { + private val logger: Logger = Logger.getInstance(ExplainCodeAction::class.java) + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val editor = e.getData(CommonDataKeys.EDITOR) ?: return + val file = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + + val effectiveRange = CostrictCodeContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) return + + val args = mutableMapOf() + args["filePath"] = file.path + args["selectedText"] = effectiveRange.text + args["startLine"] = effectiveRange.startLine + 1 + args["endLine"] = effectiveRange.endLine + 1 + + CostrictCodeContextMenuProvider.handleCodeAction("zgsm.explainCode.InCurrentTask", "EXPLAIN", args, project) + } + } + + /** + * Action to fix code issues. + * Creates a new task with the fix request. + */ + class FixCodeAction : AnAction("Fix Code") { + private val logger: Logger = Logger.getInstance(FixCodeAction::class.java) + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val editor = e.getData(CommonDataKeys.EDITOR) ?: return + val file = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + + val effectiveRange = CostrictCodeContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) return + + val args = mutableMapOf() + args["filePath"] = file.path + args["selectedText"] = effectiveRange.text + args["startLine"] = effectiveRange.startLine + 1 + args["endLine"] = effectiveRange.endLine + 1 + + CostrictCodeContextMenuProvider.handleCodeAction("zgsm.fixCode.InCurrentTask", "FIX", args, project) + } + } + + /** + * Action to fix logical issues in code. + * Creates a new task with the logic fix request. + */ + class FixLogicAction : AnAction("Fix Logic") { + private val logger: Logger = Logger.getInstance(FixLogicAction::class.java) + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val editor = e.getData(CommonDataKeys.EDITOR) ?: return + val file = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + + val effectiveRange = CostrictCodeContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) return + + val args = mutableMapOf() + args["filePath"] = file.path + args["selectedText"] = effectiveRange.text + args["startLine"] = effectiveRange.startLine + 1 + args["endLine"] = effectiveRange.endLine + 1 + + CostrictCodeContextMenuProvider.handleCodeAction("zgsm.fixCode.InCurrentTask", "FIX", args, project) + } + } + + /** + * Action to improve code quality. + * Creates a new task with the improvement request. + */ + class ImproveCodeAction : AnAction("Improve Code") { + private val logger: Logger = Logger.getInstance(ImproveCodeAction::class.java) + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val editor = e.getData(CommonDataKeys.EDITOR) ?: return + val file = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + + val effectiveRange = CostrictCodeContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) return + + val args = mutableMapOf() + args["filePath"] = file.path + args["selectedText"] = effectiveRange.text + args["startLine"] = effectiveRange.startLine + 1 + args["endLine"] = effectiveRange.endLine + 1 + + CostrictCodeContextMenuProvider.handleCodeAction("zgsm.improveCode.InCurrentTask", "IMPROVE", args, project) + } + } + + /** + * Action to add selected code to context. + * Adds the code to the current chat context. + */ + class AddToContextAction : AnAction("Add to Context") { + private val logger: Logger = Logger.getInstance(AddToContextAction::class.java) + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val editor = e.getData(CommonDataKeys.EDITOR) ?: return + val file = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + + val effectiveRange = CostrictCodeContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) return + + val args = mutableMapOf() + args["filePath"] = file.path + args["selectedText"] = effectiveRange.text + args["startLine"] = effectiveRange.startLine + 1 + args["endLine"] = effectiveRange.endLine + 1 + + CostrictCodeContextMenuProvider.handleCodeAction("zgsm.addToContext", "ADD_TO_CONTEXT", args, project) + } + } + + /** + * Action to create a new task. + * Opens a new task with the selected code. + */ + class NewTaskAction : AnAction("New Task") { + private val logger: Logger = Logger.getInstance(NewTaskAction::class.java) + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val editor = e.getData(CommonDataKeys.EDITOR) ?: return + val file = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + + val effectiveRange = CostrictCodeContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) return + + val args = mutableMapOf() + args["filePath"] = file.path + args["selectedText"] = effectiveRange.text + args["startLine"] = effectiveRange.startLine + 1 + args["endLine"] = effectiveRange.endLine + 1 + + CostrictCodeContextMenuProvider.handleCodeAction("zgsm.newTask", "NEW_TASK", args, project) + } + } + + /** + * Data class representing an effective range of selected text. + * Contains the selected text and its start/end line numbers. + * + * @property text The selected text content + * @property startLine The starting line number (0-based) + * @property endLine The ending line number (0-based) + */ + data class EffectiveRange( + val text: String, + val startLine: Int, + val endLine: Int + ) + + companion object { + /** + * Gets the effective range and text from the current editor selection. + * + * @param editor The current editor instance + * @return EffectiveRange object containing selected text and line numbers, or null if no selection + */ + fun getEffectiveRange(editor: com.intellij.openapi.editor.Editor): EffectiveRange? { + val document = editor.document + val selectionModel = editor.selectionModel + + return if (selectionModel.hasSelection()) { + val selectedText = selectionModel.selectedText ?: "" + val startLine = document.getLineNumber(selectionModel.selectionStart) + val endLine = document.getLineNumber(selectionModel.selectionEnd) + EffectiveRange(selectedText, startLine, endLine) + } else { + null + } + } + + /** + * Core logic for handling code actions. + * Processes different types of commands and sends appropriate messages to the webview. + * + * @param command The command identifier + * @param promptType The type of prompt to use + * @param params Parameters for the action (can be Map or List) + * @param project The current project + */ + fun handleCodeAction(command: String, promptType: String, params: Any, project: Project?) { + val latestWebView = project?.getService(WebViewManager::class.java)?.getLatestWebView() + if (latestWebView == null) { + return + } + + // Create message content based on command type + val messageContent = when { + // Add to context command + command.contains("addToContext") -> { + val promptParams = if (params is Map<*, *>) params as Map else emptyMap() + mapOf( + "type" to "invoke", + "invoke" to "setChatBoxMessage", + "text" to CostrictCodeSupportPrompt.create("ADD_TO_CONTEXT", promptParams) + ) + } + // Command executed in current task + command.endsWith("InCurrentTask") -> { + val promptParams = if (params is Map<*, *>) params as Map else emptyMap() + val basePromptType = when { + command.contains("explain") -> "EXPLAIN" + command.contains("fix") -> "FIX" + command.contains("improve") -> "IMPROVE" + else -> promptType + } + mapOf( + "type" to "invoke", + "invoke" to "sendMessage", + "text" to CostrictCodeSupportPrompt.create(basePromptType, promptParams) + ) + } + // Command executed in new task + else -> { + val promptParams = if (params is List<*>) { + // Process parameter list from createAction + val argsList = params as List + if (argsList.size >= 4) { + mapOf( + "filePath" to argsList[0], + "selectedText" to argsList[1], + "startLine" to argsList[2], + "endLine" to argsList[3] + ) + } else { + emptyMap() + } + } else if (params is Map<*, *>) { + params as Map + } else { + emptyMap() + } + + val basePromptType = when { + command.contains("explain") -> "EXPLAIN" + command.contains("fix") -> "FIX" + command.contains("improve") -> "IMPROVE" + else -> promptType + } + + mapOf( + "type" to "invoke", + "invoke" to "initClineWithTask", + "text" to CostrictCodeSupportPrompt.create(basePromptType, promptParams) + ) + } + } + + // Convert to JSON and send + val messageJson = com.google.gson.Gson().toJson(messageContent) + latestWebView.postMessageToWebView(messageJson) + } + + /** + * Creates a prompt by replacing placeholders in a template with actual values. + * + * @param promptType The type of prompt to create + * @param params Parameters to substitute into the template + * @return The final prompt with all placeholders replaced + */ + fun createPrompt(promptType: String, params: Map): String { + val template = getPromptTemplate(promptType) + return replacePlaceholders(template, params) + } + + /** + * Gets the template for a specific prompt type. + * + * @param type The type of prompt to retrieve + * @return The template string for the specified prompt type + */ + fun getPromptTemplate(type: String): String { + return when (type) { + "EXPLAIN" -> """Explain the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} + +``` +${'$'}{selectedText} +``` + +Please provide a clear and concise explanation of what this code does, including: +1. The purpose and functionality +2. Key components and their interactions +3. Important patterns or techniques used""" + "FIX" -> """Fix any issues in the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} + +``` +${'$'}{selectedText} +``` + +Please: +1. Address all detected problems listed above (if any) +2. Identify any other potential bugs or issues +3. Provide corrected code +4. Explain what was fixed and why""" + "IMPROVE" -> """Improve the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} + +``` +${'$'}{selectedText} +``` + +Please suggest improvements for: +1. Code readability and maintainability +2. Performance optimization +3. Best practices and patterns +4. Error handling and edge cases + +Provide the improved code along with explanations for each enhancement.""" + "ADD_TO_CONTEXT" -> """${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} +``` +${'$'}{selectedText} +```""" + "NEW_TASK" -> """${'$'}{selectedText}""" + else -> "" + } + } + + /** + * Replaces placeholders in a template with actual values. + * + * @param template The prompt template with placeholders + * @param params Map of parameter values to replace placeholders + * @return The processed prompt with placeholders replaced by actual values + */ + fun replacePlaceholders(template: String, params: Map): String { + val pattern = Regex("""\$\{(.*?)}""") + return pattern.replace(template) { matchResult -> + val key = matchResult.groupValues[1] + params[key]?.toString() ?: "" + } + } + } +} \ No newline at end of file diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt new file mode 100644 index 00000000..bf164498 --- /dev/null +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2025 Weibo, Inc. +// +// SPDX-License-Identifier: Apache-2.0 + +package com.sina.weibo.agent.extensions.plugin.costrict + +import com.intellij.openapi.project.Project +import com.sina.weibo.agent.extensions.common.ExtensionType +import com.sina.weibo.agent.extensions.config.ExtensionConfiguration +import com.sina.weibo.agent.extensions.core.ExtensionManagerFactory +import com.sina.weibo.agent.extensions.config.ExtensionProvider +import com.sina.weibo.agent.extensions.config.ExtensionMetadata +import com.sina.weibo.agent.util.PluginConstants +import com.sina.weibo.agent.util.PluginConstants.ConfigFiles.getUserConfigDir +import com.sina.weibo.agent.util.PluginResourceUtil +import java.io.File + +/** + * Costrict extension provider implementation + */ +class CostrictExtensionProvider : ExtensionProvider { + + override fun getExtensionId(): String = "costrict" + + override fun getDisplayName(): String = "Costrict" + + override fun getDescription(): String = "AI-powered code assistant with advanced capabilities" + + override fun initialize(project: Project) { + // Initialize costrict extension configuration + val extensionConfig = ExtensionConfiguration.getInstance(project) + extensionConfig.initialize() + + // Initialize extension manager factory if needed + try { + val extensionManagerFactory = ExtensionManagerFactory.getInstance(project) + extensionManagerFactory.initialize() + } catch (e: Exception) { + // If ExtensionManagerFactory is not available, continue without it + // This allows costrict to work independently + } + } + + override fun isAvailable(project: Project): Boolean { + // Check if costrict extension files exist + val extensionConfig = ExtensionConfiguration.getInstance(project) + val config = extensionConfig.getConfig(ExtensionType.COSTRICT) + + // First check project paths + val possiblePaths = listOf( + "${getUserConfigDir()}/plugins/${config.codeDir}" + ) + + if (possiblePaths.any { File(it).exists() }) { + return true + } + + // Then check plugin resources (for built-in extensions) + try { + val pluginResourcePath = PluginResourceUtil.getResourcePath( + PluginConstants.PLUGIN_ID, + config.codeDir + ) + if (pluginResourcePath != null && File(pluginResourcePath).exists()) { + return true + } + } catch (e: Exception) { + // Ignore exceptions when checking plugin resources + } + + // For development/testing, always return true if we can't find the files + // This allows the extension to work even without the actual extension files + return false + } + + override fun getConfiguration(project: Project): ExtensionMetadata { + val extensionConfig = ExtensionConfiguration.getInstance(project) + val config = extensionConfig.getConfig(ExtensionType.COSTRICT); + + return object : ExtensionMetadata { + override fun getCodeDir(): String = config.codeDir + override fun getPublisher(): String = config.publisher + override fun getVersion(): String = config.version + override fun getMainFile(): String = config.mainFile + override fun getActivationEvents(): List = config.activationEvents + override fun getEngines(): Map = config.engines + override fun getCapabilities(): Map = config.capabilities + override fun getExtensionDependencies(): List = config.extensionDependencies + } + } + + override fun dispose() { + // Cleanup resources if needed + } +} \ No newline at end of file diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/DynamicExtensionActionsGroup.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/DynamicExtensionActionsGroup.kt index 0c60be9a..fe694afa 100644 --- a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/DynamicExtensionActionsGroup.kt +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/DynamicExtensionActionsGroup.kt @@ -14,6 +14,7 @@ import com.sina.weibo.agent.extensions.common.ExtensionChangeListener import com.sina.weibo.agent.extensions.plugin.cline.ClineButtonProvider import com.sina.weibo.agent.extensions.plugin.roo.RooCodeButtonProvider import com.sina.weibo.agent.extensions.plugin.kilo.KiloCodeButtonProvider +import com.sina.weibo.agent.extensions.plugin.costrict.CostrictCodeButtonProvider import com.sina.weibo.agent.extensions.ui.buttons.ExtensionButtonProvider /** @@ -80,6 +81,7 @@ class DynamicExtensionActionsGroup : DefaultActionGroup(), DumbAware, ActionUpda "roo-code" -> RooCodeButtonProvider() "cline" -> ClineButtonProvider() "kilo-code" -> KiloCodeButtonProvider() + "costrict" -> CostrictCodeButtonProvider() else -> null } @@ -109,6 +111,7 @@ class DynamicExtensionActionsGroup : DefaultActionGroup(), DumbAware, ActionUpda "roo-code" -> RooCodeButtonProvider() "cline" -> ClineButtonProvider() "kilo-code" -> KiloCodeButtonProvider() + "costrict" -> CostrictCodeButtonProvider() else -> null } // Create actions based on extension type diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt index 9a8ae42c..65266933 100644 --- a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt @@ -14,6 +14,7 @@ import com.sina.weibo.agent.extensions.core.ExtensionManager import com.sina.weibo.agent.extensions.plugin.cline.ClineButtonProvider import com.sina.weibo.agent.extensions.plugin.roo.RooCodeButtonProvider import com.sina.weibo.agent.extensions.plugin.kilo.KiloCodeButtonProvider +import com.sina.weibo.agent.extensions.plugin.costrict.CostrictCodeButtonProvider /** * Dynamic button manager that controls which buttons are visible based on the current extension type. @@ -94,6 +95,7 @@ class DynamicButtonManager(private val project: Project) { "roo-code" -> RooCodeButtonProvider() "cline" -> ClineButtonProvider() "kilo-code" -> KiloCodeButtonProvider() + "costrict" -> CostrictCodeButtonProvider() // TODO: Add other button providers as they are implemented // "copilot" -> CopilotButtonProvider() // "claude" -> ClaudeButtonProvider() diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt index e513452b..2261f928 100644 --- a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt @@ -11,6 +11,7 @@ import com.sina.weibo.agent.extensions.core.ExtensionManager import com.sina.weibo.agent.extensions.plugin.cline.ClineContextMenuProvider import com.sina.weibo.agent.extensions.plugin.roo.RooCodeContextMenuProvider import com.sina.weibo.agent.extensions.plugin.kilo.KiloCodeContextMenuProvider +import com.sina.weibo.agent.extensions.plugin.costrict.CostrictCodeContextMenuProvider /** * Dynamic context menu manager that controls which context menu actions are available @@ -101,6 +102,7 @@ class DynamicContextMenuManager(private val project: Project) { "roo-code" -> RooCodeContextMenuProvider() "cline" -> ClineContextMenuProvider() "kilo-code" -> KiloCodeContextMenuProvider() + "costrict" -> CostrictCodeContextMenuProvider() // TODO: Add other context menu providers as they are implemented // "copilot" -> CopilotContextMenuProvider() // "claude" -> ClaudeContextMenuProvider() diff --git a/jetbrains_plugin/src/test/kotlin/com/sina/weibo/agent/extensions/ExtensionDecouplingTest.kt b/jetbrains_plugin/src/test/kotlin/com/sina/weibo/agent/extensions/ExtensionDecouplingTest.kt index b9968da7..c2b6db6c 100644 --- a/jetbrains_plugin/src/test/kotlin/com/sina/weibo/agent/extensions/ExtensionDecouplingTest.kt +++ b/jetbrains_plugin/src/test/kotlin/com/sina/weibo/agent/extensions/ExtensionDecouplingTest.kt @@ -8,6 +8,7 @@ import com.intellij.testFramework.fixtures.BasePlatformTestCase import com.sina.weibo.agent.extensions.core.ExtensionManager import com.sina.weibo.agent.extensions.plugin.roo.RooExtensionProvider import com.sina.weibo.agent.extensions.plugin.kilo.KiloCodeExtensionProvider +import com.sina.weibo.agent.extensions.plugin.costrict.CostrictExtensionProvider /** * Test class for extension decoupling functionality @@ -81,6 +82,7 @@ class ExtensionDecouplingTest : BasePlatformTestCase() { val providers = listOf( RooExtensionProvider(), KiloCodeExtensionProvider(), + CostrictExtensionProvider(), ) providers.forEach { provider -> From dffd6b6b790a9219497bca96160ae2386afdb23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E7=BB=8D=E5=BA=B759912?= <59912@sangfor.com> Date: Sat, 6 Sep 2025 11:26:47 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9costrict?= =?UTF-8?q?=E4=B8=AD=E7=99=BB=E9=99=86=E6=8C=89=E9=92=AE=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../costrict/CostrictCodeButtonProvider.kt | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt index a0e74ae1..f3b5e685 100644 --- a/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt +++ b/jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt @@ -40,7 +40,8 @@ class CostrictCodeButtonProvider : ExtensionButtonProvider { MCPButtonClickAction(), HistoryButtonClickAction(), MarketplaceButtonClickAction(), - SettingsButtonClickAction() + SettingsButtonClickAction(), + AccountButtonClickAction(), ) } @@ -111,6 +112,27 @@ class CostrictCodeButtonProvider : ExtensionButtonProvider { } } + class AccountButtonClickAction : AnAction() { + private val logger: Logger = Logger.getInstance(AccountButtonClickAction::class.java) + private val commandId: String = "zgsm.cloudButtonClicked" + + init { + templatePresentation.icon = AllIcons.General.User + templatePresentation.text = "Account" + templatePresentation.description = "Account" + } + + /** + * Performs the action when the MCP button is clicked. + * + * @param e The action event containing context information + */ + override fun actionPerformed(e: AnActionEvent) { + logger.info("Account clicked") + executeCommand(commandId, e.project) + } + } + /** * Action that handles clicks on the MCP button in the UI. * Executes the corresponding VSCode command when triggered.