-
Notifications
You must be signed in to change notification settings - Fork 59
add support for costrict, a branch of roo code. #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a new “Costrict” extension across the JetBrains plugin: enum/type registration, default config, provider, UI button group, context menu actions, prompt utilities, dynamic wiring, docs update, and test inclusion. Introduces prompt templating and action handling that post messages to a webview using generated prompts. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant IDE as IDE Action System
participant Btn as CostrictCodeButtonProvider
participant Cmd as Command Executor
User->>IDE: Clicks Costrict toolbar button
IDE->>Btn: invokeAction()
Btn->>Cmd: executeCommand(commandId, project)
note right of Cmd: Command routed to extension runtime
sequenceDiagram
autonumber
actor User
participant IDE as Editor/Context Menu
participant Ctx as CostrictCodeContextMenuProvider
participant Prompt as CostrictCodeSupportPrompt
participant WebView as WebViewManager
User->>IDE: Selects code and chooses Costrict action
IDE->>Ctx: onActionPerformed(editor, file)
Ctx->>Ctx: getEffectiveRange(editor)
Ctx->>Prompt: create(type, params)
Prompt-->>Ctx: Rendered prompt string
Ctx->>WebView: postMessage({command, payload, prompt})
WebView-->>Ctx: ack (async)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt (1)
80-83
: Add ACCOUNT to ButtonType to enable AccountButtonClickAction
ButtonType enum in DynamicButtonManager.kt (jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt lines 156-161) doesn’t include ACCOUNT, so AccountButtonClickAction will never be rendered. Add ACCOUNT to the enum and update DefaultButtonConfiguration#getVisibleButtons (and any extension-specific configs) accordingly.jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt (1)
141-146
: Log message contradicts selected default provider.Code prefers "cline" but logs “roo-code (preferred)”. Fix the log to match actual selection.
- val rooProvider = availableProviders.find { it.getExtensionId() == "cline" } + val rooProvider = availableProviders.find { it.getExtensionId() == "cline" } if (rooProvider != null) { currentProvider = rooProvider - LOG.info("Set default extension provider: roo-code (preferred)") + LOG.info("Set default extension provider: cline (preferred)")
🧹 Nitpick comments (30)
jetbrains_plugin/README_EXTENSIONS.md (2)
27-31
: Add repository link for Costrict entry.Including the upstream repo helps users discover docs and issues quickly.
### 4. Costrict - **ID**: `costrict` - **Description**: AI-powered code assistant with advanced capabilities - **Publisher**: zgsm-ai - **Directory**: `costrict/` + - **Repository**: https://github.com/zgsm-ai/costrict
49-49
: Show a Costrict config example alongside the supported values.A quick copy-paste sample improves UX.
extension.type=roo-code + +# Example: switch to Costrict +# extension.type=costrictjetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt (5)
189-197
: Preserve non-string diagnostic codes.Diagnostic “code” is often numeric or structured; current cast drops it. Convert to string safely.
- val code = d["code"] as? String + val code = d["code"]?.toString()?.takeIf { it.isNotBlank() }
206-227
: Tighten placeholder regex and avoid re-allocating it.Use a non-greedy, non-braces-crossing class and precompile once.
- val pattern = Regex("""\$\{(.*?)}""") - return pattern.replace(template) { matchResult -> + return PLACEHOLDER_REGEX.replace(template) { matchResult -> val key = matchResult.groupValues[1]Add this file-scope constant (outside the object) to precompile:
private val PLACEHOLDER_REGEX = Regex("""\$\{([^}]+)}""")
248-251
: Graceful fallback for unknown prompt types.Returning an empty prompt can break UX. Fallback to NEW_TASK or log a warning.
- val template = get(customSupportPrompts, type) - return createPrompt(template, params) + val template = get(customSupportPrompts, type) + val safeTemplate = if (template.isBlank()) CostrictCodeSupportPromptConfigs.NEW_TASK.template else template + return createPrompt(safeTemplate, params)Would you prefer we log unknown types via IntelliJ’s Logger instead?
163-173
: Avoid magic strings for prompt types.Define constants (or an enum/sealed interface) for type safety and autocomplete across call sites.
Example constants:
object CostrictPromptTypes { const val ENHANCE = "ENHANCE" const val EXPLAIN = "EXPLAIN" const val FIX = "FIX" const val IMPROVE = "IMPROVE" const val ADD_TO_CONTEXT = "ADD_TO_CONTEXT" const val TERMINAL_ADD_TO_CONTEXT = "TERMINAL_ADD_TO_CONTEXT" const val TERMINAL_FIX = "TERMINAL_FIX" const val TERMINAL_EXPLAIN = "TERMINAL_EXPLAIN" const val NEW_TASK = "NEW_TASK" }Then reference these in configs and callers.
176-182
: Deduplicate prompt-building logic across Costrict components.
CostrictCodeContextMenuProvider
also definescreatePrompt(...)
. Prefer routing all prompt creation through this file to keep behavior consistent.Happy to consolidate and adjust call sites if you want a follow-up patch.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt (1)
91-104
: Consider a registry map instead of a growing when-chain.Keeps this extensible as more providers arrive.
- private fun getButtonProvider(extensionId: String?): ExtensionButtonProvider? { - if (extensionId == null) return null - - return when (extensionId) { - "roo-code" -> RooCodeButtonProvider() - "cline" -> ClineButtonProvider() - "kilo-code" -> KiloCodeButtonProvider() - "costrict" -> CostrictCodeButtonProvider() - else -> null - } - } + private fun getButtonProvider(extensionId: String?): ExtensionButtonProvider? { + if (extensionId == null) return null + val registry = mapOf( + "roo-code" to { RooCodeButtonProvider() }, + "cline" to { ClineButtonProvider() }, + "kilo-code" to { KiloCodeButtonProvider() }, + "costrict" to { CostrictCodeButtonProvider() }, + ) + return registry[extensionId]?.invoke() + }jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt (2)
101-106
: Add Costrict mapping looks good; consider centralizing provider mapping.Duplicated when-branches for extensionId→provider exist across multiple classes. Introduce a small factory (e.g., ExtensionUiProviders.byId(extensionId)) to avoid drift. Also consider logging an info/warn on unknown IDs so failures are diagnosable.
- return when (extensionId) { + return when (extensionId) { "roo-code" -> RooCodeContextMenuProvider() "cline" -> ClineContextMenuProvider() "kilo-code" -> KiloCodeContextMenuProvider() "costrict" -> CostrictCodeContextMenuProvider() else -> null }
28-31
: Remove redundant currentExtensionId or use it consistently.setCurrentExtension() updates currentExtensionId, but getCurrentExtensionId() reads from ExtensionManager, which can cause confusion. Either always read from ExtensionManager (drop the field) or always use the field and keep it in sync.
- @Volatile - private var currentExtensionId: String? = null + // Source of truth is ExtensionManager; avoid local duplication.Also applies to: 61-67, 72-74
jetbrains_plugin/src/test/kotlin/com/sina/weibo/agent/extensions/ExtensionDecouplingTest.kt (2)
31-40
: Also assert Costrict provider registration.You added the provider but the test still only validates Roo. Add a quick presence check to prevent regressions.
val allProviders = extensionManager.getAllProviders() assertTrue("Should have at least 3 extension providers", allProviders.size >= 3) // Check specific providers val rooProvider = extensionManager.getProvider("roo-code") assertNotNull("Roo Code provider should be registered", rooProvider) assertEquals("Roo Code", rooProvider?.getDisplayName()) + val costrictProvider = extensionManager.getProvider("costrict") + assertNotNull("Costrict provider should be registered", costrictProvider) + assertEquals("Costrict", costrictProvider?.getDisplayName())
80-86
: Providers interface test: great coverage; consider asserting metadata fields.Add minimal checks on returned metadata for Costrict (e.g., codeDir="costrict") to catch accidental renames.
val providers = listOf( RooExtensionProvider(), KiloCodeExtensionProvider(), CostrictExtensionProvider(), ) @@ val config = provider.getConfiguration(project) assertNotNull("Configuration should not be null", config) + if (provider.getExtensionId() == "costrict") { + assertEquals("costrict", config.getCodeDir()) + assertEquals("zgsm-ai", config.getPublisher()) + }jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/DynamicExtensionActionsGroup.kt (1)
80-86
: Avoid duplicated mapping logic for button providers.Both updateCachedActions and loadDynamicActions duplicate the when-branch. Extract a small helper to keep mappings in one place.
- val buttonProvider = when (extensionId) { - "roo-code" -> RooCodeButtonProvider() - "cline" -> ClineButtonProvider() - "kilo-code" -> KiloCodeButtonProvider() - "costrict" -> CostrictCodeButtonProvider() - else -> null - } + val buttonProvider = createButtonProvider(extensionId) @@ - val buttonProvider = when (extensionId) { - "roo-code" -> RooCodeButtonProvider() - "cline" -> ClineButtonProvider() - "kilo-code" -> KiloCodeButtonProvider() - "costrict" -> CostrictCodeButtonProvider() - else -> null - } + val buttonProvider = createButtonProvider(extensionId) + } + + private fun createButtonProvider(extensionId: String?): ExtensionButtonProvider? = when (extensionId) { + "roo-code" -> RooCodeButtonProvider() + "cline" -> ClineButtonProvider() + "kilo-code" -> KiloCodeButtonProvider() + "costrict" -> CostrictCodeButtonProvider() + else -> null }Also applies to: 110-116
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt (1)
3-6
: Update header comment to reflect multiple extensions.Minor doc nit: it still says “for Roo Code”.
-/** - * Extension type enum for Roo Code - * Defines different types of extensions that can be supported - */ +/** + * Extension type enum for supported extensions (Roo, Cline, Kilo, Costrict). + */Also applies to: 11-12
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/config/ExtensionConfiguration.kt (1)
197-209
: Confirmed Costrict metadata is up-to-date. Publisher “zgsm-ai” and version “1.6.5” match the upstream VS Marketplace and GitHub release. Consider externalizing these values into a properties/JSON file to avoid shipping stale metadata.jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt (2)
29-42
: Avoid re-initializing global config in provider.initialize().ExtensionConfiguration.initialize() here can re-run loading on each provider init. If ExtensionManager initializes config globally, consider removing or guarding to idempotency.
- val extensionConfig = ExtensionConfiguration.getInstance(project) - extensionConfig.initialize() + // Assume ExtensionConfiguration is initialized by the application/manager. + // If needed, ensure initialize() is idempotent and cheap.
71-74
: Comment vs behavior mismatch: decide dev/test fallback.Comment says “always return true” but code returns false. Either remove the comment or add a guarded fallback (env/system property) to ease local testing without enabling in prod.
- // 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 + // For development/testing, allow opt-in fallback + val dev = System.getProperty("runvsagent.dev") == "true" || System.getenv("RUNVSAGENT_DEV") == "1" + return devjetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt (5)
29-33
: isAvailable() always true.If visibility should follow actual availability, delegate to the extension provider or a shared availability check. Otherwise, a missing extension still shows buttons.
- 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 isAvailable(project: Project): Boolean = true // UI-only; dynamic group decides visibility
98-102
: Unify text/description plurality for Prompts.- templatePresentation.text = "Prompt" - templatePresentation.description = "Prompts" + templatePresentation.text = "Prompts" + templatePresentation.description = "Prompts"
125-133
: Fix misleading docstring (“MCP button”) for Account action.- /** - * Performs the action when the MCP button is clicked. - * - * @param e The action event containing context information - */ + /** Performs the action when the Account button is clicked. */
194-198
: Minor copy: “Setting” → “Settings”.- templatePresentation.description = "Setting" + templatePresentation.description = "Settings"
35-46
: Reduce boilerplate in action classes.Seven near-identical AnAction classes; consider a small factory to create actions from (text, description, icon, commandId) tuples.
+ private fun mkAction(text: String, desc: String, icon: javax.swing.Icon, commandId: String): AnAction = + object : AnAction(text, desc, icon) { + private val logger: Logger = Logger.getInstance("Costrict/$text") + override fun actionPerformed(e: AnActionEvent) { + logger.info("$text button clicked") + executeCommand(commandId, e.project) + } + } @@ - return listOf( - PlusButtonClickAction(), - PromptsButtonClickAction(), - MCPButtonClickAction(), - HistoryButtonClickAction(), - MarketplaceButtonClickAction(), - SettingsButtonClickAction(), - AccountButtonClickAction(), - ) + return listOf( + mkAction("New Task", "New task", AllIcons.General.Add, "zgsm.plusButtonClicked"), + mkAction("Prompts", "Prompts", AllIcons.General.Information, "zgsm.promptsButtonClicked"), + mkAction("MCP Server", "MCP server", AllIcons.Webreferences.Server, "zgsm.mcpButtonClicked"), + mkAction("History", "History", AllIcons.Vcs.History, "zgsm.historyButtonClicked"), + mkAction("MCP Marketplace", "Marketplace", AllIcons.Actions.Install, "zgsm.marketplaceButtonClicked"), + mkAction("Settings", "Settings", AllIcons.General.Settings, "zgsm.settingsButtonClicked"), + mkAction("Account", "Account", AllIcons.General.User, "zgsm.cloudButtonClicked") + )Also applies to: 65-88, 140-158, 165-184, 190-209, 215-234
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt (8)
35-43
: Consider exposing “New Task” in the menu.
NewTaskAction
is implemented but not returned here; add it if you want it visible.Apply:
override fun getContextMenuActions(project: Project): List<AnAction> { return listOf( ExplainCodeAction(), FixCodeAction(), FixLogicAction(), ImproveCodeAction(), - AddToContextAction(), + AddToContextAction(), + NewTaskAction(), ) }
69-85
: Deduplicate action boilerplate.Each action repeats the same selection and args-building logic. Extract a helper to reduce bugs and maintenance.
Add to the companion object:
fun buildArgs(file: com.intellij.openapi.vfs.VirtualFile, range: EffectiveRange) = mapOf( "filePath" to file.path, "selectedText" to range.text, "startLine" to range.startLine + 1, "endLine" to range.endLine + 1 )Then replace per action:
- val args = mutableMapOf<String, Any?>() - args["filePath"] = file.path - args["selectedText"] = effectiveRange.text - args["startLine"] = effectiveRange.startLine + 1 - args["endLine"] = effectiveRange.endLine + 1 + val args = buildArgs(file, effectiveRange)Also applies to: 94-110, 119-135, 144-160, 169-185, 194-210
256-327
: Harden command parsing and visibility.
- String contains/endsWith matching on
command
is brittle; prefer enums/constants.- When no WebView is available, log a warning to aid diagnosis.
Apply minimal logging:
- val latestWebView = project?.getService(WebViewManager::class.java)?.getLatestWebView() - if (latestWebView == null) { - return - } + val latestWebView = project?.getService(WebViewManager::class.java)?.getLatestWebView() + if (latestWebView == null) { + Logger.getInstance(CostrictCodeContextMenuProvider::class.java) + .warn("No active WebView; dropping command=$command") + return + }Optional: replace string parsing with a sealed command type or constants object shared with the webview.
347-390
: Prompt templates: add language fences.Use code fences with language (from file type) to improve model/tooling behavior.
Example:
- "EXPLAIN" -> """Explain the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} - -``` -${'$'}{selectedText} -``` + "EXPLAIN" -> """Explain the following code from file path ${'$'}{filePath}:${'$'}{startLine}-${'$'}{endLine} + +```$${"language"} +${'$'}{selectedText} +```Then include
"language"
inparams
(derive fromfile.fileType.name
and map to markdown identifiers).
336-339
: Remove unused prompt helpers or wire them in.
createPrompt/getPromptTemplate/replacePlaceholders
are unused becauseCostrictCodeSupportPrompt.create(...)
is called. Keep one path.Prefer deleting these functions to avoid drift, or refactor
handleCodeAction
to callcreatePrompt(...)
exclusively.Also applies to: 399-405
66-67
: Unused loggers.Either use the loggers for trace/warn, or drop them.
Example removal:
- private val logger: Logger = Logger.getInstance(ExplainCodeAction::class.java)
Also applies to: 91-93, 116-118, 141-143, 166-168, 191-193
66-85
: Improve UX: disable actions when no selection.Override
update
to enable only when an editor with non-empty selection exists; avoids no-op clicks.Snippet:
override fun update(e: AnActionEvent) { val editor = e.getData(CommonDataKeys.EDITOR) val hasSelection = editor?.selectionModel?.hasSelection() == true e.presentation.isEnabledAndVisible = hasSelection }Replicate for other actions or extract a base class.
324-327
: Consider reusing a singleton JSON adapter.Minor: reuse a single
Gson
instance or IntelliJ’s JSON utils to cut allocations.- val messageJson = com.google.gson.Gson().toJson(messageContent) + val messageJson = GsonHolder.gson.toJson(messageContent)And add a small
object GsonHolder { val gson = com.google.gson.Gson() }
.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
jetbrains_plugin/README_EXTENSIONS.md
(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt
(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/config/ExtensionConfiguration.kt
(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt
(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt
(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt
(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt
(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt
(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/DynamicExtensionActionsGroup.kt
(3 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt
(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt
(2 hunks)jetbrains_plugin/src/test/kotlin/com/sina/weibo/agent/extensions/ExtensionDecouplingTest.kt
(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/util/PluginConstants.kt (1)
getUserConfigDir
(45-47)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt (1)
createPrompt
(336-339)
🔇 Additional comments (10)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt (1)
40-46
: Confirm line-number origin (0- vs 1-based).If upstream reports zero-based lines but UI shows one-based, the prompt header may mismatch what users see.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt (2)
17-17
: Wire Costrict button provider import — LGTM.
94-103
: Map “costrict” to its provider — LGTM.jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt (1)
108-115
: Registration includes Costrict: LGTM.Provider is added to the registry alongside existing extensions.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt (1)
11-12
: COSTRICT entry addition looks consistent.jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt (1)
115-118
: Verify commandId for AccountButtonClickAction
The IDzgsm.cloudButtonClicked
deviates from thezgsm.accountButtonClicked
pattern and doesn’t appear in plugin.xml—confirm this is the intended identifier forAccountButtonClickAction
and that it matches your action registration.jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt (4)
24-33
: Good, clear provider metadata.Extension id/name/description read well;
isAvailable
stub is acceptable as a first pass.
116-134
: Verify command routing for FixLogic.
FixLogicAction
uses"zgsm.fixCode.InCurrentTask"
. If logic-fix has its own route (e.g.,fixLogic
), update it.Apply if appropriate:
- CostrictCodeContextMenuProvider.handleCodeAction("zgsm.fixCode.InCurrentTask", "FIX", args, project) + CostrictCodeContextMenuProvider.handleCodeAction("zgsm.fixLogic.InCurrentTask", "FIX", args, project)Would you like me to scan the repo and confirm the expected command IDs?
45-60
: Visibility config: confirm all actions exist upstream.
getVisibleActions()
exposes allContextMenuActionType
s; ensure each maps to a concrete action or hide unsupported ones.Would you like a quick scan to cross-check the enum vs. provided actions?
256-327
: Verify webview message handlers Ensure the webview’s JS/HTML registers exact handlers for the"setChatBoxMessage"
,"sendMessage"
, and"initClineWithTask"
invoke commands you’re posting here so the contract remains in sync.
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 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Selection end off-by-one; inclusive end line can be wrong.
getLineNumber(selectionEnd)
can point to the next line when the caret is at line start. Compute an inclusive end offset.
Apply:
- return if (selectionModel.hasSelection()) {
- val selectedText = selectionModel.selectedText ?: ""
- val startLine = document.getLineNumber(selectionModel.selectionStart)
- val endLine = document.getLineNumber(selectionModel.selectionEnd)
- EffectiveRange(selectedText, startLine, endLine)
+ return if (selectionModel.hasSelection()) {
+ val selectedText = selectionModel.selectedText?.takeIf { it.isNotBlank() } ?: return null
+ val start = minOf(selectionModel.selectionStart, selectionModel.selectionEnd)
+ val end = (maxOf(selectionModel.selectionStart, selectionModel.selectionEnd) - 1).coerceAtLeast(0)
+ val startLine = document.getLineNumber(start)
+ val endLine = document.getLineNumber(end)
+ EffectiveRange(selectedText, startLine, endLine)
} else {
null
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
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 | |
} | |
} | |
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?.takeIf { it.isNotBlank() } ?: return null | |
val start = minOf(selectionModel.selectionStart, selectionModel.selectionEnd) | |
val end = (maxOf(selectionModel.selectionStart, selectionModel.selectionEnd) - 1).coerceAtLeast(0) | |
val startLine = document.getLineNumber(start) | |
val endLine = document.getLineNumber(end) | |
EffectiveRange(selectedText, startLine, endLine) | |
} else { | |
null | |
} | |
} |
🤖 Prompt for AI Agents
In
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt
around lines 233 to 245, the end line is computed with
document.getLineNumber(selectionEnd) which can point to the next line when the
selection ends at a line start; compute an inclusive end offset (e.g. endOffset
= max(0, selectionModel.selectionEnd - 1) or otherwise adjust to the last
selected character) and use document.getLineNumber(endOffset) to derive the
inclusive endLine before constructing EffectiveRange so the returned end line is
correct.
val pluginResourcePath = PluginResourceUtil.getResourcePath( | ||
PluginConstants.PLUGIN_ID, | ||
config.codeDir | ||
) | ||
if (pluginResourcePath != null && File(pluginResourcePath).exists()) { | ||
return true | ||
} | ||
} catch (e: Exception) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Resource path check may fail inside JARs.
File(...).exists() won’t work for resources packaged in JARs. Prefer ResourceUtil / classLoader resource existence checks or VFS.
- if (pluginResourcePath != null && File(pluginResourcePath).exists()) {
+ if (pluginResourcePath != null) {
+ // Treat non-null as presence; optional: actually attempt to open the resource stream.
+ return true
+ }
- return true
- }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
val pluginResourcePath = PluginResourceUtil.getResourcePath( | |
PluginConstants.PLUGIN_ID, | |
config.codeDir | |
) | |
if (pluginResourcePath != null && File(pluginResourcePath).exists()) { | |
return true | |
} | |
} catch (e: Exception) { | |
val pluginResourcePath = PluginResourceUtil.getResourcePath( | |
PluginConstants.PLUGIN_ID, | |
config.codeDir | |
) | |
if (pluginResourcePath != null) { | |
// Treat non-null as presence; optional: actually attempt to open the resource stream. | |
return true | |
} | |
} catch (e: Exception) { |
@MonkeyCode-AI review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request 概述
- 本次PR为RunVSAgent JetBrains插件添加了一个名为"Costrict"的新扩展。这个扩展集成到现有的模块化扩展系统中,为用户提供额外的AI代码助手功能。
- 实现了Costrict扩展的核心组件,包括ExtensionProvider、ButtonProvider和ContextMenuProvider,提供了代码解释、修复、改进等功能。
- 更新了相关管理器和配置文件,使新扩展能够被正确识别和使用。
Pull Request 变更详情
文件路径 | 变更类型 | 变更内容 |
---|---|---|
jetbrains_plugin/README_EXTENSIONS.md | 修改 | 添加了Costrict扩展的文档说明 |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/common/ExtensionType.kt | 修改 | 在ExtensionType枚举中添加了COSTRICT类型 |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/config/ExtensionConfiguration.kt | 修改 | 添加了Costrict扩展的配置信息 |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt | 修改 | 注册了CostrictExtensionProvider |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeActionConstants.kt | 添加 | 创建了Costrict扩展的代码操作常量定义 |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeButtonProvider.kt | 添加 | 创建了Costrict扩展的按钮提供者实现 |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictCodeContextMenuProvider.kt | 添加 | 创建了Costrict扩展的上下文菜单提供者实现 |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/costrict/CostrictExtensionProvider.kt | 添加 | 创建了Costrict扩展的主要提供者实现 |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/DynamicExtensionActionsGroup.kt | 修改 | 更新了动态扩展操作组以支持Costrict |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt | 修改 | 更新了动态按钮管理器以支持Costrict |
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt | 修改 | 更新了动态上下文菜单管理器以支持Costrict |
jetbrains_plugin/src/test/kotlin/com/sina/weibo/agent/extensions/ExtensionDecouplingTest.kt | 修改 | 更新了扩展解耦测试以包含Costrict |
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"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在ExtensionType.kt文件中,枚举值的命名可能有拼写错误。应该使用COSTRICT还是COCONSTRICT?
COSTRICT("costrict", "Costrict", "AI-powered code assistant with advanced capabilities"), | |
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"), | |
COCONSTRICT("costrict", "Costrict", "AI-powered code assistant with advanced capabilities"), |
capabilities = emptyMap(), | ||
extensionDependencies = emptyList() | ||
) | ||
ExtensionType.COSTRICT -> ExtensionConfig( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在ExtensionConfiguration.kt文件中,应该使用修正后的枚举值COCONSTRICT而不是COSTRICT。
ExtensionType.COSTRICT -> ExtensionConfig( | |
ExtensionType.ROO_CODE -> ExtensionConfig( | |
extensionType = extensionType, | |
codeDir = "roo-code", | |
displayName = "Roo Code", | |
description = "AI-powered code assistant", | |
publisher = "WeCode-AI", | |
version = "1.0.0", | |
mainFile = "./dist/extension.js", | |
activationEvents = listOf("onStartupFinished"), | |
engines = mapOf("vscode" to "^1.0.0"), | |
capabilities = emptyMap(), | |
extensionDependencies = emptyList() | |
) | |
ExtensionType.CLINE -> ExtensionConfig( | |
extensionType = extensionType, | |
codeDir = "cline", | |
displayName = "Cline", | |
description = "AI-powered coding assistant with advanced features", | |
publisher = "Cline-AI", | |
version = "1.0.0", | |
mainFile = "./dist/extension.js", | |
activationEvents = listOf("onStartupFinished"), | |
engines = mapOf("vscode" to "^1.0.0"), | |
capabilities = emptyMap(), | |
extensionDependencies = emptyList() | |
) | |
ExtensionType.KILO_CODE -> ExtensionConfig( | |
extensionType = extensionType, | |
codeDir = "kilo-code", | |
displayName = "Kilo Code", | |
description = "AI-powered code assistant with advanced capabilities", | |
publisher = "Kilo-AI", | |
version = "1.0.0", | |
mainFile = "./dist/extension.js", | |
activationEvents = listOf("onStartupFinished"), | |
engines = mapOf("vscode" to "^1.0.0"), | |
capabilities = emptyMap(), | |
extensionDependencies = emptyList() | |
) | |
ExtensionType.COCONSTRICT -> ExtensionConfig( | |
extensionType = extensionType, | |
codeDir = "costrict", | |
displayName = "Costrict", | |
description = "AI-powered code assistant with advanced capabilities", | |
publisher = "zgsm-ai", | |
version = "1.0.0", | |
mainFile = "./dist/extension.js", | |
activationEvents = listOf("onStartupFinished"), | |
engines = mapOf("vscode" to "^1.0.0"), | |
capabilities = emptyMap(), | |
extensionDependencies = emptyList() | |
) |
${'$'}{userInput}""" | ||
) | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在CostrictCodeActionConstants.kt文件中,有一些TODO注释和未完成的实现,需要进一步完善。
/** | |
// 移除空的TODO注释 | |
// TODO: Add more prompt types as needed | |
// 在注释中明确说明这个文件是Costrict扩展的一部分 | |
/** | |
* Collection of predefined prompt configurations for different use cases. | |
* Each configuration contains a template with placeholders for dynamic content. | |
* | |
* These configurations are now organized under the Costrict extension. | |
*/ |
} | ||
|
||
override fun isAvailable(project: Project): Boolean { | ||
// Check if costrict extension files exist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在CostrictExtensionProvider.kt文件中,初始化逻辑可以进一步优化以提高代码可读性和维护性。
// Check if costrict extension files exist | |
override fun initialize(project: Project) { | |
// Initialize extension configuration | |
try { | |
val configManager = ExtensionConfigurationManager.getInstance(project) | |
configManager.initializeExtension(this) | |
} catch (e: Exception) { | |
LOG.warn("Failed to initialize extension configuration for Costrict", e) | |
} | |
// Initialize extension manager factory | |
try { | |
val factory = ExtensionManagerFactory.getInstance(project) | |
factory.registerExtensionProvider(this) | |
} catch (e: Exception) { | |
LOG.warn("Failed to register Costrict extension provider", e) | |
} | |
} |
} | ||
|
||
override fun getButtons(project: Project): List<AnAction> { | ||
// Note: project parameter kept for future extensibility |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在CostrictCodeButtonProvider.kt文件中,按钮提供者的实现可以进一步优化以提高代码的可维护性。
// Note: project parameter kept for future extensibility | |
class CostrictCodeButtonProvider : ExtensionButtonProvider { | |
override fun getButtonConfiguration(): ButtonConfiguration { | |
return object : ButtonConfiguration { | |
override fun isButtonVisible(buttonType: ButtonType): Boolean { | |
return when (buttonType) { | |
ButtonType.PLUS, | |
ButtonType.PROMPTS, | |
ButtonType.MCP, | |
ButtonType.HISTORY, | |
ButtonType.MARKETPLACE, | |
ButtonType.SETTINGS -> true | |
} | |
} | |
override fun getVisibleButtons(): List<ButtonType> { | |
return listOf( | |
ButtonType.PLUS, | |
ButtonType.PROMPTS, | |
ButtonType.MCP, | |
ButtonType.HISTORY, | |
ButtonType.MARKETPLACE, | |
ButtonType.SETTINGS | |
) | |
} | |
} | |
} | |
} |
} | ||
|
||
/** | ||
* Action to fix code issues. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在CostrictCodeContextMenuProvider.kt文件中,上下文菜单提供者的实现可以进一步优化以提高代码的可维护性。
* Action to fix code issues. | |
class CostrictCodeContextMenuProvider : ExtensionContextMenuProvider { | |
override fun getContextMenuConfiguration(): ContextMenuConfiguration { | |
return object : ContextMenuConfiguration { | |
override fun isActionVisible(actionType: ContextMenuActionType): Boolean { | |
return when (actionType) { | |
ContextMenuActionType.EXPLAIN_CODE, | |
ContextMenuActionType.FIX_CODE, | |
ContextMenuActionType.FIX_LOGIC, | |
ContextMenuActionType.IMPROVE_CODE, | |
ContextMenuActionType.ADD_TO_CONTEXT, | |
ContextMenuActionType.NEW_TASK -> true | |
} | |
} | |
override fun getVisibleActions(): List<ContextMenuActionType> { | |
return listOf( | |
ContextMenuActionType.EXPLAIN_CODE, | |
ContextMenuActionType.FIX_CODE, | |
ContextMenuActionType.FIX_LOGIC, | |
ContextMenuActionType.IMPROVE_CODE, | |
ContextMenuActionType.ADD_TO_CONTEXT, | |
ContextMenuActionType.NEW_TASK | |
) | |
} | |
} | |
} | |
} |
Costrict is a copy of roo code, provides more featrures and api providers.
this pr add support for costrict and add an login button to switch costrict account.
see https://github.com/zgsm-ai/costrict for more detail about costrict.
Summary by CodeRabbit
New Features
Documentation
Tests