refactor: Introduce specific context menus and operations for extension#77
Conversation
WalkthroughAdds a forceRestart-aware extension switching flow, introduces a dynamic, extension-provided context menu framework, implements RooCode and Cline context menu providers, refactors Roo prompt constants/APIs, updates UI refresh logic for dynamic buttons, adjusts extension installation lookup paths, and removes legacy code-action and status action files. Updates plugin.xml to wire the dynamic context menu group. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as UI (Switcher)
participant EM as ExtensionManager
participant Btn as DynamicButtonManager
participant Ctx as DynamicContextMenuManager
Note over UI,EM: Extension switch with forceRestart flag
User->>UI: Choose extension (id, forceRestart)
UI->>EM: setCurrentProvider(id, forceRestart)
EM-->>UI: Boolean result
alt forceRestart == true
Note over EM: Initialize provider + save config<br/>(no currentProvider/UI swap)
UI-->>Btn: (no-op)
UI-->>Ctx: (no-op)
else forceRestart == false
Note over EM: Initialize provider + set currentProvider<br/>+ save config
EM->>Btn: Schedule button refresh
EM->>Ctx: Schedule context menu refresh
end
sequenceDiagram
autonumber
participant IDE as IDE Editor
participant Group as DynamicExtensionContextMenuGroup
participant Mgr as DynamicContextMenuManager
participant Prov as ExtensionContextMenuProvider
Note over Group,Mgr: Context menu population on selection
IDE->>Group: update(event)
Group->>IDE: Check editor & selection
alt has selection
Group->>Mgr: getInstance(project).initialize()
Mgr->>Mgr: Resolve current extension id
Mgr->>Prov: getContextMenuActions(project)
Prov-->>Mgr: List<AnAction>
Mgr-->>Group: Actions
Group->>Group: Replace group children with actions
else no selection
Group->>Group: Clear actions
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt (1)
137-138: Suspicious default provider assignmentThe code has a commented-out line preferring "roo-code" as default, but the active line prefers "cline" instead. The log message on line 141 still says "roo-code (preferred)" which is misleading.
Fix the inconsistency between the actual default provider and the log message:
-// val rooProvider = availableProviders.find { it.getExtensionId() == "roo-code" } 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 (23)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RightClickChatActionGroup.kt (2)
13-16: Good deprecation notice; add cross-reference for discoverabilityConsider adding a KDoc @see to explicitly point readers to DynamicExtensionContextMenuGroup for faster navigation.
/** * Right-click menu code action group, similar to VSCode's code action provider. * This class manages the dynamic actions that appear in the context menu when text is selected. * - * @deprecated This class is deprecated in favor of DynamicExtensionContextMenuGroup. - * Use the new extension-based context menu system for better extensibility. + * @deprecated This class is deprecated in favor of DynamicExtensionContextMenuGroup. + * Use the new extension-based context menu system for better extensibility. + * @see com.sina.weibo.agent.extensions.ui.contextmenu.DynamicExtensionContextMenuGroup * * Implements DumbAware to ensure the action works during indexing, and ActionUpdateThreadAware * to specify which thread should handle action updates. */
20-21: Add ReplaceWith and explicit level to the deprecation for better migration UXProviding ReplaceWith helps IDE quick-fix users jump to the new API; an explicit level documents intended severity.
-@Deprecated("Use DynamicExtensionContextMenuGroup instead for extension-based context menus") +@Deprecated( + message = "Use DynamicExtensionContextMenuGroup instead for extension-based context menus", + replaceWith = ReplaceWith( + "com.sina.weibo.agent.extensions.ui.contextmenu.DynamicExtensionContextMenuGroup" + ), + level = DeprecationLevel.WARNING +) class RightClickChatActionGroup : DefaultActionGroup(), DumbAware, ActionUpdateThreadAware {jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ContextMenuConfiguration.kt (2)
7-21: Provide a default implementation to reduce duplication across providersMost providers will implement isActionVisible and then mechanically build getVisibleActions. Offer a default implementation that derives the list from isActionVisible.
interface ContextMenuConfiguration { - - /** * Check if a specific context menu action should be visible. * @param actionType The type of context menu action * @return true if the action should be visible, false otherwise */ fun isActionVisible(actionType: ContextMenuActionType): Boolean - + /** * Get all visible context menu actions. * @return List of visible action types */ - fun getVisibleActions(): List<ContextMenuActionType> + fun getVisibleActions(): List<ContextMenuActionType> = + enumValues<ContextMenuActionType>().filter(::isActionVisible) }
27-34: Consider marking this API surface as experimental while it settlesIf you anticipate churn, annotate with ApiStatus.Experimental to set expectations for ext authors.
+@org.jetbrains.annotations.ApiStatus.Experimental enum class ContextMenuActionType { EXPLAIN_CODE, FIX_CODE, FIX_LOGIC, IMPROVE_CODE, ADD_TO_CONTEXT, NEW_TASK }jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ExtensionContextMenuProvider.kt (2)
11-12: Stabilize the SPI: mark experimental now; you can lift it laterFlagging the extension SPI as experimental communicates potential breaking changes to extension implementers.
-interface ExtensionContextMenuProvider { +@org.jetbrains.annotations.ApiStatus.Experimental +interface ExtensionContextMenuProvider {
38-50: Add an overload that accepts DataContext to enable richer, selection-aware actionsThis preserves the current signature while allowing providers that need the DataContext (e.g., caret, PSI) to use it without reconstructing an AnActionEvent.
-import com.intellij.openapi.project.Project +import com.intellij.openapi.project.Project +import com.intellij.openapi.actionSystem.DataContext @@ - fun getContextMenuActions(project: Project): List<AnAction> + fun getContextMenuActions(project: Project): List<AnAction> + /** + * Optional: Provide actions with access to DataContext. + * Default implementation falls back to the simpler overload. + */ + fun getContextMenuActions(project: Project, dataContext: DataContext): List<AnAction> = + getContextMenuActions(project)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/ActionConstants.kt (4)
11-16: Deprecation is good; add ReplaceWith targets for smoother migrationsProviding ReplaceWith helps IDE users auto-migrate to extension-specific constants.
-@Deprecated("Use extension-specific action names instead") +@Deprecated( + message = "Use extension-specific action names instead", + replaceWith = ReplaceWith( + "com.sina.weibo.agent.extensions.plugin.roo.RooCodeActionNames" + ), + level = DeprecationLevel.WARNING +) object ActionNames {
18-35: Add ReplaceWith to each constant; apply similar to FIX, FIX_LOGIC, IMPROVE, ADD_TO_CONTEXT, NEW_TASKConcrete replacements improve quick-fix accuracy.
- @Deprecated("Use RooCodeActionNames.EXPLAIN or Cline-specific constants") - const val EXPLAIN = "roo-cline: Explain Code" + @Deprecated( + message = "Use RooCodeActionNames.EXPLAIN or Cline-specific constants", + replaceWith = ReplaceWith( + "RooCodeActionNames.EXPLAIN", + "com.sina.weibo.agent.extensions.plugin.roo.RooCodeActionNames" + ) + ) + const val EXPLAIN = "roo-cline: Explain Code"
44-61: Mirror ReplaceWith on CommandIds to aid migration of action wiringSame rationale as ActionNames; include ReplaceWith on each ID constant.
-@Deprecated("Use extension-specific command IDs instead") +@Deprecated( + message = "Use extension-specific command IDs instead", + replaceWith = ReplaceWith( + "com.sina.weibo.agent.extensions.plugin.roo.RooCodeCommandIds" + ), + level = DeprecationLevel.WARNING +) object CommandIds { @@ - @Deprecated("Use RooCodeCommandIds.EXPLAIN or Cline-specific command IDs") + @Deprecated( + message = "Use RooCodeCommandIds.EXPLAIN or Cline-specific command IDs", + replaceWith = ReplaceWith( + "RooCodeCommandIds.EXPLAIN", + "com.sina.weibo.agent.extensions.plugin.roo.RooCodeCommandIds" + ) + ) const val EXPLAIN = "roo-cline.explainCode"
247-318: Minor perf/readability: precompile the placeholder regexAvoid recompiling the same Regex for every createPrompt call.
object SupportPrompt { + // Precompiled for reuse across prompt creations + private val PLACEHOLDER_PATTERN = Regex("""\$\{(.*?)}""") @@ - private fun createPrompt(template: String, params: PromptParams): String { - val pattern = Regex("""\$\{(.*?)}""") - return pattern.replace(template) { matchResult -> + private fun createPrompt(template: String, params: PromptParams): String { + return PLACEHOLDER_PATTERN.replace(template) { matchResult -> val key = matchResult.groupValues[1] if (key == "diagnosticText") { generateDiagnosticText(params["diagnostics"] as? List<Map<String, Any?>>) } 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 "" } } }jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicExtensionContextMenuGroup.kt (1)
55-63: Consider caching the context menu managerThe initialization of
contextMenuManagerhappens on every menu update when it's null, but if initialization fails once, it will be retried on every subsequent menu update. This could impact performance if the initialization is expensive or if it consistently fails.Consider adding a flag to track initialization attempts:
private var contextMenuManager: DynamicContextMenuManager? = null +private var initializationAttempted = false private fun loadDynamicContextMenuActions(e: AnActionEvent) { val project = e.project ?: return // Get or initialize the context menu manager - if (contextMenuManager == null) { + if (contextMenuManager == null && !initializationAttempted) { try { + initializationAttempted = true contextMenuManager = DynamicContextMenuManager.getInstance(project) contextMenuManager?.initialize() } catch (e: Exception) { // If the manager is not available, fall back to default actions return } }jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeActionConstants.kt (1)
249-270: Potential regex injection vulnerability in template processingThe
createPromptfunction uses regex replacement with user-controlled input. While the current implementation appears safe because it only replaces specific placeholder patterns, care should be taken if this logic is extended.Consider using a more robust template engine or escaping mechanism if this functionality expands:
private fun createPrompt(template: String, params: RooCodePromptParams): String { val pattern = Regex("""\$\{(.*?)}""") return pattern.replace(template) { matchResult -> val key = matchResult.groupValues[1] + // Validate key to prevent potential issues + if (!key.matches(Regex("[a-zA-Z0-9_]+"))) { + return@replace matchResult.value // Return original if invalid + } if (key == "diagnosticText") {jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt (4)
117-135: “Fix Logic” action is routed to the generic “fixCode” command — confirm intent and consider discrete command/promptUI shows “Fix Logic”, but the command string and prompt type are the same as “Fix Code”. If the backend differentiates logic-fix vs. general fix, wire the right command ID and/or prompt. Also, prefer using the constants introduced in RooCodeActionConstants.kt to avoid string drift.
Would you like me to patch this to use RooCodeActionConstants (command IDs and names) once you confirm the canonical identifiers?
339-416: Unused prompt helpers (createPrompt/getPromptTemplate/replacePlaceholders)These helpers aren’t used (you rely on SupportPrompt.create). Keeping them increases maintenance burden.
Apply this diff to remove the unused block:
- /** - * Creates a prompt by replacing placeholders in a template with actual values. - */ - fun createPrompt(promptType: String, params: Map<String, Any?>): String { ... } - - /** - * Gets the template for a specific prompt type. - */ - fun getPromptTemplate(type: String): String { ... } - - /** - * Replaces placeholders in a template with actual values. - */ - fun replacePlaceholders(template: String, params: Map<String, Any?>): String { ... }If you intend to keep custom templates, please move them to a shared location to keep parity with other providers.
70-86: Return-on-null silently drops user actions; add lightweight diagnosticsWhen editor/selection/webview are null the action simply returns. Add a debug log (you already instantiate a logger in each action) so we can trace why an action didn’t fire. Optionally show a balloon notification to guide the user.
Example for one action (replicate similarly):
- val effectiveRange = RooCodeContextMenuProvider.getEffectiveRange(editor) - if (effectiveRange == null) return + val effectiveRange = RooCodeContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) { + logger.debug("ExplainCodeAction skipped: no selection in editor ${editor.document}") + return + }Also applies to: 95-111, 120-136, 145-161, 170-186, 195-211
31-35: isAvailable() always returns true — consider delegating to extension configurationIf availability should reflect actual configuration (e.g., active extension id), delegate this check to ExtensionManager/DynamicContextMenuManager to avoid showing actions when the extension isn’t selected.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt (3)
47-51: Prefer idiomatic Kotlin call: ExtensionManager.getInstance(project)Accessing Companion explicitly is non-idiomatic and unnecessary.
- val extensionManager = ExtensionManager.Companion.getInstance(project) + val extensionManager = ExtensionManager.getInstance(project)
96-107: Avoid re-instantiating providers; cache per extension to preserve state and reduce churngetContextMenuProvider() creates a new provider each call. Caching avoids unnecessary allocations and lets providers maintain internal state if needed.
Apply this refactor:
+import java.util.concurrent.ConcurrentHashMap ... class DynamicContextMenuManager(private val project: Project) { @@ - private fun getContextMenuProvider(extensionId: String?): ExtensionContextMenuProvider? { - if (extensionId == null) return null - - return when (extensionId) { - "roo-code" -> RooCodeContextMenuProvider() - "cline" -> ClineContextMenuProvider() - else -> null - } - } + private val providerCache = ConcurrentHashMap<String, ExtensionContextMenuProvider>() + + private fun getContextMenuProvider(extensionId: String?): ExtensionContextMenuProvider? { + if (extensionId == null) return null + return providerCache.computeIfAbsent(extensionId) { + when (it) { + "roo-code" -> RooCodeContextMenuProvider() + "cline" -> ClineContextMenuProvider() + else -> return null + } + } + }
152-170: Default config exposes only EXPLAIN and ADD_TO_CONTEXT — confirm UX alignmentIf the goal is parity across extensions when no provider is active, consider also exposing IMPROVE_CODE (harmless, helpful) unless product requirements say otherwise.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt (4)
281-288: “New Task” currently uses sendMessage; verify whether it should open a new task/sessionRoo uses “initClineWithTask” for new tasks. If Cline also expects a new session, align the invoke channel to initClineWithTask; otherwise, confirm sendMessage is correct.
Proposed change if a new session is desired:
- "invoke" to "sendMessage", + "invoke" to "initClineWithTask",
167-173: Remove unused local variable ‘range’range is created and never used; keep only the map passed to executeCommand.
- val range = ClineContextMenuProvider.Range( - startLine = effectiveRange.startLine, - startCharacter = effectiveRange.startCharacter, - endLine = effectiveRange.endLine, - endCharacter = effectiveRange.endCharacter - )
82-101: Add minimal diagnostics on early returnsLike Roo, these actions silently no-op on missing editor/selection/webview. Add debug logs or user notifications to aid troubleshooting.
Example:
- val effectiveRange = ClineContextMenuProvider.getEffectiveRange(editor) - if (effectiveRange == null) return + val effectiveRange = ClineContextMenuProvider.getEffectiveRange(editor) + if (effectiveRange == null) { + logger.debug("ClineExplainCodeAction skipped: no selection") + return + }Also applies to: 107-125, 131-150, 156-182, 188-207
296-372: Consolidate prompt/placeholder utilities across providersBoth Roo and Cline define near-identical placeholder utilities. Consider centralizing in a shared utility (or rely solely on SupportPrompt) to avoid divergence.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/ActionConstants.kt(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RightClickChatActionGroup.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/core/ExtensionSwitcher.kt(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeActionConstants.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ContextMenuConfiguration.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicExtensionContextMenuGroup.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ExtensionContextMenuProvider.kt(1 hunks)jetbrains_plugin/src/main/resources/META-INF/plugin.xml(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (10)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RightClickChatActionGroup.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (2)
createAction(19-132)text(142-146)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicExtensionContextMenuGroup.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (1)
createAction(19-132)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt (3)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt (1)
getContextMenuConfiguration(48-50)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt (1)
getContextMenuConfiguration(46-48)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ExtensionContextMenuProvider.kt (1)
getContextMenuConfiguration(49-49)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actors/MainThreadMessageServiceShape.kt (1)
logger(21-114)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeActionConstants.kt (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/ActionConstants.kt (3)
generateDiagnosticText(255-263)get(302-304)createPrompt(272-293)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (7)
createAction(19-132)handleCodeAction(228-299)registerCodeAction(157-201)provideCodeActions(95-131)text(142-146)createAction(28-51)actionPerformed(164-199)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionSwitcher.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/core/ExtensionManager.kt (5)
id(262-281)LOG(25-231)parseExtensionDescriptionFromNewConfig(85-120)vscode(253-256)activateExtension(171-222)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/plugin/WecoderPlugin.kt (1)
LOG(42-220)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ExtensionContextMenuProvider.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (2)
createAction(19-132)provideCodeActions(95-131)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (4)
createAction(19-132)text(142-146)handleCodeAction(228-299)actionPerformed(33-49)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/ActionConstants.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (6)
createAction(19-132)handleCodeAction(228-299)text(142-146)actionPerformed(164-199)createAction(28-51)provideCodeActions(95-131)
🔇 Additional comments (8)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt (1)
123-124: Action ID Alignment ConfirmedThe action ID
"RunVSAgent.DynamicExtensionActions"inDynamicButtonManager.ktexactly matches the<group id="RunVSAgent.DynamicExtensionActions">declaration inMETA-INF/plugin.xml(line 117). No changes are required.jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ExtensionContextMenuProvider.kt (1)
36-37: Ensure returned actions are safe during indexingEither return DumbAware actions or ensure heavy work is deferred to actionPerformed. This avoids UI stalls during indexing.
Would you like me to scan providers under extensions/plugin/** to flag any actions not implementing DumbAware?
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionManager.kt (1)
184-200: Inconsistent UI update behaviorThe UI components (buttons and context menu) are only updated when
forceRestart == false, but the configuration is always saved. This could lead to a state where the configuration is updated but the UI doesn't reflect it until restart, which might confuse users.Based on the logic, when
forceRestartis true, the provider is initialized and configuration is saved, butcurrentProviderand UI remain unchanged. This seems counterintuitive - why would forcing a restart skip updating the current provider?Consider clarifying the intent or adjusting the logic to be more predictable.
jetbrains_plugin/src/main/resources/META-INF/plugin.xml (1)
130-131: LGTM! Dynamic context menu integration looks goodThe migration from the static
RightClickChatActionGroupto the dynamicDynamicExtensionContextMenuGroupis well-implemented, enabling extension-specific context menu actions as intended.jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionSwitcher.kt (1)
155-161: Logic inversion concernWhen
forceRestartistrue, the code only updates button configuration and notifies changes, but based on the ExtensionManager implementation, this is when thecurrentProvideris NOT updated. This seems backwards - typically a "force restart" would imply more aggressive changes, not fewer.The current logic appears to be:
forceRestart = false: Updates currentProvider, buttons, context menuforceRestart = true: Only updates buttons and notifies changesThis seems inverted from typical expectations. Please verify if this is the intended behavior or if there's a logic error.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicExtensionContextMenuGroup.kt (1)
18-81: LGTM! Clean implementation of dynamic context menu groupThe implementation correctly handles the dynamic loading of extension-specific context menu actions with proper null safety and exception handling. The use of EDT for UI updates is appropriate.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeActionConstants.kt (1)
1-296: Well-structured constants and prompt management for Roo Code extensionThe file provides a clean separation of concerns with clear organization of action names, command IDs, and prompt templates. The documentation is comprehensive and the code follows Kotlin best practices.
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt (1)
232-246: Good: VS Code-compatible selection semanticsgetEffectiveRange returns start-inclusive/end-exclusive line/character positions, mirroring VS Code/LSP behavior, which is important for downstream tools.
| * | ||
| * @deprecated These constants are deprecated in favor of extension-specific constants. | ||
| * Use RooCodeActionNames for Roo Code extension or Cline-specific constants for Cline extension. | ||
| */ | ||
| @Deprecated("Use extension-specific action names instead") | ||
| object ActionNames { | ||
| /** Action to explain selected code */ | ||
| @Deprecated("Use RooCodeActionNames.EXPLAIN or Cline-specific constants") | ||
| const val EXPLAIN = "roo-cline: Explain Code" | ||
| /** Action to fix issues in selected code */ | ||
| @Deprecated("Use RooCodeActionNames.FIX or Cline-specific constants") | ||
| const val FIX = "roo-cline: Fix Code" | ||
| /** Action to fix logical issues in selected code */ | ||
| @Deprecated("Use RooCodeActionNames.FIX_LOGIC or Cline-specific constants") | ||
| const val FIX_LOGIC = "roo-cline: Fix Logic" | ||
| /** Action to improve selected code */ | ||
| @Deprecated("Use RooCodeActionNames.IMPROVE or Cline-specific constants") | ||
| const val IMPROVE = "roo-cline: Improve Code" | ||
| /** Action to add selected code to context */ | ||
| @Deprecated("Use RooCodeActionNames.ADD_TO_CONTEXT or Cline-specific constants") | ||
| const val ADD_TO_CONTEXT = "roo-cline: Add to Context" | ||
| /** Action to create a new task */ | ||
| @Deprecated("Use RooCodeActionNames.NEW_TASK or Cline-specific constants") | ||
| const val NEW_TASK = "roo-cline: New Task" | ||
| } | ||
|
|
||
| /** | ||
| * Command identifiers used for internal command registration and execution. | ||
| * These IDs are used to register commands with the IDE. | ||
| * | ||
| * @deprecated These command IDs are deprecated in favor of extension-specific command IDs. | ||
| * Use RooCodeCommandIds for Roo Code extension or Cline-specific command IDs for Cline extension. | ||
| */ | ||
| @Deprecated("Use extension-specific command IDs instead") | ||
| object CommandIds { | ||
| /** Command ID for explaining code */ | ||
| @Deprecated("Use RooCodeCommandIds.EXPLAIN or Cline-specific command IDs") | ||
| const val EXPLAIN = "roo-cline.explainCode" | ||
| /** Command ID for fixing code */ | ||
| @Deprecated("Use RooCodeCommandIds.FIX or Cline-specific command IDs") | ||
| const val FIX = "roo-cline.fixCode" | ||
| /** Command ID for improving code */ | ||
| @Deprecated("Use RooCodeCommandIds.IMPROVE or Cline-specific command IDs") | ||
| const val IMPROVE = "roo-cline.improveCode" | ||
| /** Command ID for adding to context */ | ||
| @Deprecated("Use RooCodeCommandIds.ADD_TO_CONTEXT or Cline-specific command IDs") | ||
| const val ADD_TO_CONTEXT = "roo-cline.addToContext" | ||
| /** Command ID for creating a new task */ | ||
| @Deprecated("Use RooCodeCommandIds.NEW_TASK or Cline-specific command IDs") | ||
| const val NEW_TASK = "roo-cline.newTask" | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Plan the removal window and find usages to migrate
Given these broad deprecations, it will help to (1) define a target release for removal and (2) locate remaining usages to schedule fixes.
Also applies to: 64-79, 87-239, 247-318
🏁 Script executed:
#!/bin/bash
# 1) Find usages of deprecated constants/prompt utilities across the codebase (excluding build dirs)
rg -nP '\b(ActionNames|CommandIds|SupportPrompt(Configs)?|SupportPromptType|PromptParams)\b' -g '!**/build/**'
# 2) Confirm no plugin.xml entries still reference deprecated command IDs or action groups
rg -nP 'roo-cline\.(explainCode|fixCode|improveCode|addToContext|newTask)|RightClick\.Chat' jetbrains_plugin/src/main/resources/META-INF/plugin.xmlLength of output: 4430
Plan removal window & migrate remaining deprecated constants
Please define a target release by which we will drop ActionNames and CommandIds, and in advance of that release migrate all remaining usages to the new extension-specific constants (e.g. RooCodeActionNames, Cline equivalents) and prompt configurations.
Remaining references to update:
- jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt
• Lines ~280, 295, 329: calls toSupportPrompt.create("ADD_TO_CONTEXT",…)and uses of deprecated prompt types - jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt
• Line ~286: call toSupportPrompt.create(basePromptType,…) - jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt
• Lines ~101–102, 109–110, 117–118, 125–126: uses ofActionNames.*andCommandIds.*
• Lines ~242, 257, 291: calls toSupportPrompt.create(…)with deprecated type strings - jetbrains_plugin/src/main/resources/META-INF/plugin.xml
• Entry for<group id="RunVSAgent.RightClick.Chat" …>still references the old action group
By the chosen removal release, replace all of the above with the corresponding constants in RooCodeActionNames, RooCodeCommandIds, or your Cline-specific analogs (and updated prompt configs). This will ensure a smooth deprecation cycle and avoid last-minute breakage.
🤖 Prompt for AI Agents
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/ActionConstants.kt
lines 11-61: define a target release (e.g., vX.Y.Z) to remove ActionNames and
CommandIds, then migrate all remaining usages to extension-specific constants:
replace ActionNames.* and CommandIds.* in RegisterCodeActions.kt (lines ~101–126
and ~242,257,291) with RooCodeActionNames/RooCodeCommandIds or the Cline
equivalents; update RooCodeContextMenuProvider.kt (≈lines 280,295,329) and
ClineContextMenuProvider.kt (≈line 286) to call SupportPrompt.create with the
new prompt-type constants instead of string literals like "ADD_TO_CONTEXT" or
deprecated types; update plugin.xml entry for the RunVSAgent.RightClick.Chat
group to reference the new action IDs; run a build and tests to ensure no
unresolved references and adjust imports accordingly before merging.
| fun setCurrentProvider(extensionId: String, forceRestart: Boolean? = false): Boolean { | ||
| val provider = extensionProviders[extensionId] | ||
| if (provider != null && provider.isAvailable(project)) { | ||
| val oldProvider = currentProvider | ||
| currentProvider = provider | ||
|
|
||
| if (forceRestart == false) { | ||
| currentProvider = provider | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Potentially confusing parameter handling
The forceRestart parameter is declared as Boolean? but you're using direct equality comparisons with false. This could lead to unexpected behavior when null is passed. The nullable type suggests three states (true/false/null), but the implementation only handles true vs. non-true.
Consider simplifying the parameter type and logic:
- fun setCurrentProvider(extensionId: String, forceRestart: Boolean? = false): Boolean {
+ fun setCurrentProvider(extensionId: String, forceRestart: Boolean = false): Boolean {
val provider = extensionProviders[extensionId]
if (provider != null && provider.isAvailable(project)) {
val oldProvider = currentProvider
- if (forceRestart == false) {
+ if (!forceRestart) {
currentProvider = provider
}📝 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 setCurrentProvider(extensionId: String, forceRestart: Boolean? = false): Boolean { | |
| val provider = extensionProviders[extensionId] | |
| if (provider != null && provider.isAvailable(project)) { | |
| val oldProvider = currentProvider | |
| currentProvider = provider | |
| if (forceRestart == false) { | |
| currentProvider = provider | |
| } | |
| fun setCurrentProvider(extensionId: String, forceRestart: Boolean = false): Boolean { | |
| val provider = extensionProviders[extensionId] | |
| if (provider != null && provider.isAvailable(project)) { | |
| val oldProvider = currentProvider | |
| if (!forceRestart) { | |
| currentProvider = provider | |
| } | |
| // …rest of implementation… |
| try { | ||
| // Step 1: Update extension manager (this will save configuration) | ||
| updateExtensionManager(extensionId) | ||
| updateExtensionManager(extensionId, forceRestart) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Parameter type consistency issue with ExtensionManager
The forceRestart parameter is passed to extensionManager.setCurrentProvider(), but there's a type mismatch. ExtensionManager.setCurrentProvider() expects Boolean? while this method passes Boolean.
Either update ExtensionManager to use non-nullable Boolean (as suggested in the previous review), or ensure consistent types:
- private suspend fun updateExtensionManager(extensionId: String, forceRestart: Boolean) {
+ private suspend fun updateExtensionManager(extensionId: String, forceRestart: Boolean?) {Also applies to: 177-177, 182-182
🤖 Prompt for AI Agents
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/ExtensionSwitcher.kt
around lines 153, 177, 182: the non-nullable Boolean forceRestart is passed into
ExtensionManager.setCurrentProvider which expects a nullable Boolean, causing a
type mismatch; fix by making types consistent—preferably update
ExtensionManager.setCurrentProvider signature to accept a non-nullable Boolean
(Boolean) so callers can pass forceRestart directly, or if you cannot change the
manager, convert the argument to nullable at call sites (e.g., pass forceRestart
as Boolean? by using forceRestart as Boolean?) ensuring all three call sites
(153, 177, 182) use the same approach.
| override fun getContextMenuActions(project: Project): List<AnAction> { | ||
| return listOf( | ||
| // ClineExplainCodeAction(), | ||
| // ClineFixCodeAction(), | ||
| // ClineImproveCodeAction(), | ||
| // ClineAddToContextAction(), | ||
| // ClineNewTaskAction() | ||
| ) | ||
| } |
There was a problem hiding this comment.
Context menu returns no actions — users won’t see any Cline items
All actions are commented out in getContextMenuActions(). Wire them up so the dynamic menu can display them.
override fun getContextMenuActions(project: Project): List<AnAction> {
- return listOf(
-// ClineExplainCodeAction(),
-//
-// ClineFixCodeAction(),
-//
-// ClineImproveCodeAction(),
-//
-// ClineAddToContextAction(),
-//
-// ClineNewTaskAction()
- )
+ return listOf(
+ ClineExplainCodeAction(),
+ ClineFixCodeAction(),
+ ClineImproveCodeAction(),
+ ClineAddToContextAction(),
+ ClineNewTaskAction()
+ )
}📝 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.
| override fun getContextMenuActions(project: Project): List<AnAction> { | |
| return listOf( | |
| // ClineExplainCodeAction(), | |
| // ClineFixCodeAction(), | |
| // ClineImproveCodeAction(), | |
| // ClineAddToContextAction(), | |
| // ClineNewTaskAction() | |
| ) | |
| } | |
| override fun getContextMenuActions(project: Project): List<AnAction> { | |
| return listOf( | |
| ClineExplainCodeAction(), | |
| ClineFixCodeAction(), | |
| ClineImproveCodeAction(), | |
| ClineAddToContextAction(), | |
| ClineNewTaskAction() | |
| ) | |
| } |
| 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.
🛠️ Refactor suggestion
Off-by-one risk for endLine when selection ends at a line boundary
Using selectionEnd directly to compute endLine can overshoot by one line when the caret is at the start of the next line. For user-facing prompts that display inclusive line ranges, adjust the end offset.
Apply this diff (and add the import):
+import kotlin.math.max
...
- val selectedText = selectionModel.selectedText ?: ""
- val startLine = document.getLineNumber(selectionModel.selectionStart)
- val endLine = document.getLineNumber(selectionModel.selectionEnd)
- EffectiveRange(selectedText, startLine, endLine)
+ val selectedText = selectionModel.selectedText ?: ""
+ val selectionStart = selectionModel.selectionStart
+ val selectionEnd = selectionModel.selectionEnd
+ val startLine = document.getLineNumber(selectionStart)
+ val adjustedEndOffset = max(selectionEnd - 1, selectionStart)
+ val endLine = document.getLineNumber(adjustedEndOffset)
+ EffectiveRange(selectedText, startLine, endLine)Note: Cline’s provider intentionally matches VS Code’s start-inclusive/end-exclusive semantics with characters; here we’re only printing line numbers for humans, so inclusive line display is preferable.
📝 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.
| 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 | |
| } | |
| } | |
| // At the top of the file, among other imports: | |
| import kotlin.math.max | |
| // … | |
| return if (selectionModel.hasSelection()) { | |
| val selectedText = selectionModel.selectedText ?: "" | |
| val selectionStart = selectionModel.selectionStart | |
| val selectionEnd = selectionModel.selectionEnd | |
| val startLine = document.getLineNumber(selectionStart) | |
| // Subtract 1 so that if selectionEnd lands exactly at the start of the next line, | |
| // we still report the last character’s line. Clamp to selectionStart to avoid | |
| // passing a negative offset. | |
| val adjustedEndOffset = max(selectionEnd - 1, selectionStart) | |
| val endLine = document.getLineNumber(adjustedEndOffset) | |
| EffectiveRange(selectedText, startLine, endLine) | |
| } else { | |
| null | |
| } | |
| } |
🤖 Prompt for AI Agents
In
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt
around lines 238 to 246, the endLine calculation can overshoot when selectionEnd
is at the start of the next line; change the logic to compute an inclusive end
offset (use selectionEnd - 1 but not below selectionStart), then call
document.getLineNumber on that inclusive offset to get an inclusive endLine, and
add the import kotlin.math.max for the clamp operation.
| /** | ||
| * 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 | ||
| * @param project The current project | ||
| */ | ||
| /** | ||
| * 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<String, Any?> else emptyMap() | ||
| mapOf( | ||
| "type" to "invoke", | ||
| "invoke" to "setChatBoxMessage", | ||
| "text" to SupportPrompt.create("ADD_TO_CONTEXT", promptParams) | ||
| ) | ||
| } | ||
| // Command executed in current task | ||
| command.endsWith("InCurrentTask") -> { | ||
| val promptParams = if (params is Map<*, *>) params as Map<String, Any?> 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 SupportPrompt.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<Any> | ||
| 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<String, Any?> | ||
| } 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 SupportPrompt.create(basePromptType, promptParams) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| // Convert to JSON and send | ||
| val messageJson = com.google.gson.Gson().toJson(messageContent) | ||
| latestWebView.postMessageToWebView(messageJson) | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
De-duplicate handleCodeAction logic and reuse the shared implementation
This file reimplements a handleCodeAction identical in purpose to com.sina.weibo.agent.actions.handleCodeAction (see RegisterCodeActions.kt). Keeping two versions will drift and complicate fixes. Reuse the shared function and remove the local one. Also swap call sites accordingly.
Apply these diffs:
- Import and replace call sites:
+import com.sina.weibo.agent.actions.handleCodeAction
...
- RooCodeContextMenuProvider.handleCodeAction("roo-cline.explainCode.InCurrentTask", "EXPLAIN", args, project)
+ handleCodeAction("roo-cline.explainCode.InCurrentTask", "EXPLAIN", args, project)
...
- RooCodeContextMenuProvider.handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project)
+ handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project)
...
- RooCodeContextMenuProvider.handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project)
+ handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project)
...
- RooCodeContextMenuProvider.handleCodeAction("roo-cline.improveCode.InCurrentTask", "IMPROVE", args, project)
+ handleCodeAction("roo-cline.improveCode.InCurrentTask", "IMPROVE", args, project)
...
- RooCodeContextMenuProvider.handleCodeAction("roo-cline.addToContext", "ADD_TO_CONTEXT", args, project)
+ handleCodeAction("roo-cline.addToContext", "ADD_TO_CONTEXT", args, project)
...
- RooCodeContextMenuProvider.handleCodeAction("roo-cline.newTask", "NEW_TASK", args, project)
+ handleCodeAction("roo-cline.newTask", "NEW_TASK", args, project)- Remove the local duplicate (entire block shown here):
- /**
- * 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
- }
- ...
- latestWebView.postMessageToWebView(messageJson)
- }Also applies to: 84-85, 109-110, 134-135, 159-160, 184-185, 209-210
🤖 Prompt for AI Agents
In
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt
around lines 248 to 337, this file contains a duplicate handleCodeAction
implementation that should be removed and callers updated to use the shared
implementation in com.sina.weibo.agent.actions.handleCodeAction; remove the
entire local handleCodeAction block, add an import for
com.sina.weibo.agent.actions.handleCodeAction at the top of the file, and
replace every local call site that referenced the removed method (also at the
other occurrences around lines 84-85, 109-110, 134-135, 159-160, 184-185,
209-210) to call the shared handleCodeAction instead, preserving the same
arguments and nullability handling for Project when forwarding calls.
| // Use IntelliJ Platform's proper mechanism to refresh UI on EDT thread | ||
| // This avoids calling @ApiStatus.OverrideOnly methods directly | ||
| com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater { | ||
| try { | ||
| // Get the action manager | ||
| val actionManager = ActionManager.getInstance() | ||
|
|
||
| // Get the dynamic actions group | ||
| val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionActions") | ||
| dynamicGroup?.let { group -> | ||
| // Trigger UI refresh by notifying the platform | ||
| // The platform will automatically call the appropriate update methods | ||
| logger.debug("Triggering UI refresh for dynamic actions group") | ||
| } | ||
|
|
||
| logger.debug("Action toolbars refresh scheduled for extension: $currentExtensionId") | ||
| } catch (e: Exception) { | ||
| logger.warn("Failed to schedule action toolbar refresh", e) | ||
| } |
There was a problem hiding this comment.
EDT scheduling alone doesn’t refresh actions; call invalidateActions() and guard modality/disposal
The current block retrieves the group and logs but does not actually trigger a refresh. Use ActionManager.invalidateActions() and add a modality/disposal guard. Optional: warn if the action ID is missing.
- com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater {
- try {
- // Get the action manager
- val actionManager = ActionManager.getInstance()
-
- // Get the dynamic actions group
- val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionActions")
- dynamicGroup?.let { group ->
- // Trigger UI refresh by notifying the platform
- // The platform will automatically call the appropriate update methods
- logger.debug("Triggering UI refresh for dynamic actions group")
- }
-
- logger.debug("Action toolbars refresh scheduled for extension: $currentExtensionId")
- } catch (e: Exception) {
- logger.warn("Failed to schedule action toolbar refresh", e)
- }
- }
+ com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater({
+ try {
+ if (project.isDisposed) return@invokeLater
+ val actionManager = ActionManager.getInstance()
+
+ // Invalidate action system caches so toolbars/menus re-query presentation
+ actionManager.invalidateActions()
+
+ val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionActions")
+ if (dynamicGroup == null) {
+ logger.warn("Dynamic actions group 'RunVSAgent.DynamicExtensionActions' not found")
+ } else {
+ logger.debug("Triggered action system invalidation for dynamic actions group")
+ }
+
+ logger.debug("Action toolbars refresh scheduled for extension: $currentExtensionId")
+ } catch (e: Exception) {
+ logger.warn("Failed to schedule action toolbar refresh", e)
+ }
+ }, com.intellij.openapi.application.ModalityState.any())📝 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.
| // Use IntelliJ Platform's proper mechanism to refresh UI on EDT thread | |
| // This avoids calling @ApiStatus.OverrideOnly methods directly | |
| com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater { | |
| try { | |
| // Get the action manager | |
| val actionManager = ActionManager.getInstance() | |
| // Get the dynamic actions group | |
| val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionActions") | |
| dynamicGroup?.let { group -> | |
| // Trigger UI refresh by notifying the platform | |
| // The platform will automatically call the appropriate update methods | |
| logger.debug("Triggering UI refresh for dynamic actions group") | |
| } | |
| logger.debug("Action toolbars refresh scheduled for extension: $currentExtensionId") | |
| } catch (e: Exception) { | |
| logger.warn("Failed to schedule action toolbar refresh", e) | |
| } | |
| com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater({ | |
| try { | |
| if (project.isDisposed) return@invokeLater | |
| val actionManager = ActionManager.getInstance() | |
| // Invalidate action system caches so toolbars/menus re-query presentation | |
| actionManager.invalidateActions() | |
| val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionActions") | |
| if (dynamicGroup == null) { | |
| logger.warn("Dynamic actions group 'RunVSAgent.DynamicExtensionActions' not found") | |
| } else { | |
| logger.debug("Triggered action system invalidation for dynamic actions group") | |
| } | |
| logger.debug("Action toolbars refresh scheduled for extension: $currentExtensionId") | |
| } catch (e: Exception) { | |
| logger.warn("Failed to schedule action toolbar refresh", e) | |
| } | |
| }, com.intellij.openapi.application.ModalityState.any()) |
| fun initialize() { | ||
| logger.info("Initializing dynamic context menu manager") | ||
|
|
||
| // Get current extension from extension manager | ||
| try { | ||
| val extensionManager = ExtensionManager.Companion.getInstance(project) | ||
| val currentProvider = extensionManager.getCurrentProvider() | ||
| currentExtensionId = currentProvider?.getExtensionId() | ||
| logger.info("Dynamic context menu manager initialized with extension: $currentExtensionId") | ||
| } catch (e: Exception) { | ||
| logger.warn("Failed to initialize dynamic context menu manager", e) | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Ensure initialize() is invoked during startup
The manager won’t detect the current extension unless initialize() is called. I don’t see a direct call in WecoderPlugin.kt (provided snippet). Please confirm DynamicExtensionContextMenuGroup or another component calls it on project open.
🏁 Script executed:
#!/bin/bash
# Verify that DynamicContextMenuManager.initialize() is invoked somewhere.
rg -nP '\bDynamicContextMenuManager\b' -C2
rg -nP '\bDynamicContextMenuManager\b.*?initialize\s*\(' -C2
# Verify that plugin.xml declares the dynamic group id used here
rg -n 'RunVSAgent.DynamicExtensionContextMenu' jetbrains_plugin/src/main/resources/META-INF/plugin.xmlLength of output: 4648
🏁 Script executed:
#!/bin/bash
# Search for context menu group definitions in plugin.xml
rg -n '<group' jetbrains_plugin/src/main/resources/META-INF/plugin.xml -C3Length of output: 2574
DynamicContextMenuManager.initialize() runs only on first right-click
The initialize() method is invoked lazily inside DynamicExtensionContextMenuGroup when the context menu is first shown (see lines 57–58). It is not called at IDE startup or project open, so the manager won’t detect the current extension until the user opens the context menu.
To ensure the dynamic menu is set up on project startup, explicitly invoke initialize() during project open. For example, in WecoderPlugin.kt’s projectOpened():
override fun projectOpened() {
// existing setup…
DynamicContextMenuManager
.getInstance(project)
.initialize()
}– Point of action:
• In jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/WecoderPlugin.kt, add the above call in projectOpened().
🤖 Prompt for AI Agents
In jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/WecoderPlugin.kt around
the projectOpened() method, the project-open lifecycle doesn't call
DynamicContextMenuManager.initialize(), so the dynamic context menu only detects
the extension on the first right-click; modify projectOpened() to call
DynamicContextMenuManager.getInstance(project).initialize() after existing setup
so the manager is initialized on project open and will have the current
extension available immediately.
| private fun refreshContextMenus() { | ||
| try { | ||
| // Get the action manager | ||
| val actionManager = com.intellij.openapi.actionSystem.ActionManager.getInstance() | ||
|
|
||
| // Refresh the dynamic context menu actions group by invalidating the action | ||
| // This will trigger the UI to refresh without directly calling @ApiStatus.OverrideOnly methods | ||
| val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionContextMenu") | ||
| dynamicGroup?.let { group -> | ||
| // Use the proper IntelliJ Platform mechanism to refresh the action | ||
| // Instead of calling update() directly, we invalidate the action | ||
| actionManager.invalidateAction(group.id) | ||
| } | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Validate ActionManager.invalidateAction availability for target IDE builds
invalidateAction(...) is relatively new and not present in all supported platforms. Confirm it exists for your minimum since using an unavailable API will break refresh in older IDEs.
🌐 Web query:
Is ActionManager.invalidateAction(String actionId) available in IntelliJ Platform 2023.1–2024.3? What is the recommended way to force an action group to refresh if not?
💡 Result:
ActionManager.invalidateAction(String actionId) is not available in IntelliJ Platform versions 2023.1 through 2024.3. There is no mention of this method in the IntelliJ Platform Plugin SDK documentation or community discussions for these or later versions, and current best practices for "refreshing" actions or action groups do not reference such a method[1][3][10].
Why is invalidateAction not available?
- The ActionManager API in IntelliJ Platform provides methods to add, remove, and retrieve actions, but does not include an
invalidateAction(String actionId)method, either in its public interface or in SDK practices since at least 2022.3 and up to 2025.2[1][3][8][10]. API change logs and plugin migration guides do not reference such a utility.
Recommended way to force an Action or Action Group to refresh
The recommended approach to force an action or action group to refresh in the current IntelliJ Platform is:
- Trigger update() logic on the actions/groups by causing a relevant context change or firing a UI event.
- The IDE’s action system depends on the state provided by the overridden
AnAction.update(AnActionEvent e)method, which is called automatically by the platform when it decides the action context has changed (e.g., toolbar/menu opens, project context changes).
- The IDE’s action system depends on the state provided by the overridden
- To ensure dynamic content in groups, implement a custom
ActionGroup(typically extendDefaultActionGroup), and override itsupdate()and/orgetChildren()method to provide contextually appropriate actions at each requested update[1][3][6].- For example, if your group depends on specific IDE state or data, ensure changes to that state are followed by firing appropriate notifications or events that cause the platform to trigger
update()calls[1][6].
- For example, if your group depends on specific IDE state or data, ensure changes to that state are followed by firing appropriate notifications or events that cause the platform to trigger
Common mechanisms to trigger updates:
- Call
ActionToolbar.updateActionsImmediately()(on a relevant component, e.g., after a file/document change or toolwindow visibility change) to force toolbar to reevaluate action states. - Send a DataContext or selection change event in the IDE (such as programmatically changing toolwindow focus, updating project view, or updating application data keys) that your action's
update()responds to. - Refactor your group to always compute its children on each request, if your logic is truly dynamic, by overriding
ActionGroup.getChildren(AnActionEvent e).
Key SDK references:
- Overriding
AnAction.update()and leveraging context[1][3][10]. - Implementing dynamic child actions in a group by extending
DefaultActionGroupand customizinggetChildren()[6]. - Forcing UI components (like toolbars) to update their actions via
ActionToolbar.updateActionsImmediately().
Summary Table: Refreshing Actions or Groups
| Need | Supported Method | Example/Reference |
|---|---|---|
| Invalidate a single action by ID | Not supported (no such method) | — |
| Force toolbar or group UI to refresh | ActionToolbar.updateActionsImmediately() |
[1][6] |
| Ensure dynamic group content | Override getChildren() in custom ActionGroup |
[6] |
| Update action state for context change | Override update(), respond to data/context change |
[1][3][10] |
Conclusion:
Use dynamic group implementations and UI trigger mechanisms documented in the SDK rather than a non-existent invalidateAction method[1][6][10]. For reliable dynamic updates, your update() and getChildren() implementations should respond to changes in the IDE state that matter to your group or action.
If further details on a specific type of refresh or integration with custom UI are needed, please clarify your scenario.
Citations:
- 1: https://plugins.jetbrains.com/docs/intellij/action-system.html
- 2: https://intellij-support.jetbrains.com/hc/en-us/community/posts/20766304678034-Error-Failed-to-save-settings-Please-restart-IntelliJ-IDEA
- 3: https://docs.namichong.com/intellij-platform-sdk/basic-action-system.html
- 4: https://intellij-support.jetbrains.com/hc/en-us/community/posts/10493334466834-Add-action-to-toolbar-for-all-IntelliJ-IDEs
- 5: https://youtrack.jetbrains.com/issue/IJPL-34719/com.intellij.diagnostic.PluginException-ActionUpdateThread.OLDEDT-is-deprecated-and-going-to-be-removed-soon-after-restoring
- 6: https://plugins.jetbrains.com/docs/intellij/grouping-actions-tutorial.html
- 7: https://intellij-support.jetbrains.com/hc/en-us/community/posts/6898947564562-IntelliJ-2022-2-Ultimate-Android-Device-Manager-can-t-start-simulators-anymore
- 8: https://platform.jetbrains.com/t/intellij-idea-ultimate-2025-2-eap-252-23892-248-plugin-is-invalid-the-plugin-archive-file-cannot-be-extracted/2307
- 9: https://intellij-sdk-docs-cn.github.io/intellij/sdk/docs/tutorials/action_system/working_with_custom_actions.html
- 10: https://plugins.jetbrains.com/docs/intellij/creating-actions-tutorial.html
Remove unsupported invalidateAction call
The method ActionManager.invalidateAction(String) does not exist in IntelliJ Platform Plugin SDK versions 2023.1 through 2024.3 and will lead to a NoSuchMethodError at runtime (plugins.jetbrains.com).
To implement dynamic context‐menu refresh, consider one of the following supported approaches:
-
Override your action group's
getChildren(AnActionEvent)
ExtendActionGroup(orDefaultActionGroup) and overridegetChildren(AnActionEvent)to return the current set of child actions. The IDE calls this method each time the menu is shown, ensuring your menu is always up to date (plugins.jetbrains.com). -
Trigger a global action‐system update
If you must programmatically force the UI to re‐evaluate action states (e.g., after external state changes), call:
ActivityTracker.getInstance().inc()
This notifies the action subsystem to update all toolbar and menu actions (intellij-sdk-docs-cn.github.io). -
Leverage UI‐component updates
For toolbars built viaActionToolbar, you can callupdateActionsImmediately()on the toolbar instance after changing your plugin’s data model.
Attention is required in:
• jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt lines 120–133, specifically the block invoking actionManager.invalidateAction(group.id).
Please remove or replace the invalidateAction(...) call and adopt one of the supported mechanisms above.
🤖 Prompt for AI Agents
In
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt
around lines 120-133, remove the unsupported call to
actionManager.invalidateAction(group.id) (it does not exist in supported
IntelliJ SDKs) and replace it with a supported approach: either make the group a
subclass of ActionGroup/DefaultActionGroup and override
getChildren(AnActionEvent) to compute children dynamically, or if you need to
force a programmatic refresh call ActivityTracker.getInstance().inc() to trigger
global action-system updates, or, when updating a specific ActionToolbar
instance, call its updateActionsImmediately() after changing your plugin state;
update imports and comments accordingly.
…the extension manager and UI refresh logic.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt (1)
43-54: Ensure initialize() is invoked on project open (repeat)initialize() is only effective if called early. Please confirm a startup lifecycle (e.g., in WecoderPlugin.projectOpened) invokes it; otherwise menus won’t reflect the current extension until the first context-menu open.
Run:
#!/bin/bash # Verify initialize() is called during project lifecycle and group id exists rg -nP '\bDynamicContextMenuManager\b' -C2 rg -nP '\bDynamicContextMenuManager\b.*\binitialize\s*\(' -C3 rg -n 'RunVSAgent\.DynamicExtensionContextMenu' jetbrains_plugin/src/main/resources/META-INF/plugin.xml -n -C2
🧹 Nitpick comments (3)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt (3)
96-107: Avoid hard-coded provider branching; reuse provider instances via a registry mapThe when-based string matching will sprawl as providers grow and creates new instances per call. Prefer a small registry map and reuse instances.
Inline refactor within this function:
- private fun getContextMenuProvider(extensionId: String?): ExtensionContextMenuProvider? { - if (extensionId == null) return null - - return when (extensionId) { - "roo-code" -> RooCodeContextMenuProvider() - "cline" -> ClineContextMenuProvider() - // TODO: Add other context menu providers as they are implemented - // "copilot" -> CopilotContextMenuProvider() - // "claude" -> ClaudeContextMenuProvider() - else -> null - } - } + private val providers by lazy { + mapOf( + "roo-code" to RooCodeContextMenuProvider(), + "cline" to ClineContextMenuProvider(), + ) + } + + private fun getContextMenuProvider(extensionId: String?): ExtensionContextMenuProvider? { + if (extensionId.isNullOrEmpty()) return null + return providers[extensionId] + }
20-21: Service lifecycle: implement Disposable so dispose() is called automaticallydispose() won’t be called unless the service implements Disposable. Either remove the method or wire it into the platform lifecycle.
Suggested change:
@@ -import com.intellij.openapi.components.Service +import com.intellij.openapi.components.Service +import com.intellij.openapi.Disposable @@ -@Service(Service.Level.PROJECT) -class DynamicContextMenuManager(private val project: Project) { +@Service(Service.Level.PROJECT) +class DynamicContextMenuManager(private val project: Project) : Disposable { @@ - fun dispose() { + override fun dispose() { logger.info("Disposing dynamic context menu manager") currentExtensionId = null }Also applies to: 150-153
129-131: Minor: extract group id into a constant to avoid typosCentralize the action id string.
@@ companion object { /** * Get dynamic context menu manager instance */ fun getInstance(project: Project): DynamicContextMenuManager { return project.getService(DynamicContextMenuManager::class.java) ?: error("DynamicContextMenuManager not found") } + private const val DYNAMIC_GROUP_ID = "RunVSAgent.DynamicExtensionContextMenu" } @@ - val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionContextMenu") + val dynamicGroup = actionManager.getAction(DYNAMIC_GROUP_ID)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt (5)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/buttons/DynamicButtonManager.kt (1)
getCurrentExtensionId(71-73)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/ExtensionContextMenuProvider.kt (1)
getContextMenuConfiguration(49-49)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt (1)
getContextMenuConfiguration(46-48)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt (1)
getContextMenuConfiguration(48-50)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/core/ExtensionManager.kt (2)
LOG(25-231)id(262-281)
🔇 Additional comments (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt (1)
159-177: Default configuration looks sensibleThe fallback shows a minimal, safe action set (EXPLAIN_CODE, ADD_TO_CONTEXT). No issues spotted.
| // Current extension ID | ||
| @Volatile | ||
| private var currentExtensionId: String? = null | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Extension state inconsistency: setCurrentExtension() doesn’t affect lookups
setCurrentExtension() mutates the local currentExtensionId, but getCurrentExtensionId() ignores it and consults ExtensionManager instead. As a result, calling setCurrentExtension() will not change which provider/config the manager returns. Either rely on ExtensionManager as source-of-truth and remove the field and setter, or make this manager’s state authoritative for menu resolution.
Suggested minimal fix to make the setter take effect and avoid redundant refreshes:
@@
- fun setCurrentExtension(extensionId: String) {
- logger.info("Setting current extension to: $extensionId")
- currentExtensionId = extensionId
-
- // Refresh all context menus to reflect the change
- refreshContextMenus()
- }
+ fun setCurrentExtension(extensionId: String) {
+ logger.info("Setting current extension to: $extensionId")
+ if (extensionId == currentExtensionId) {
+ logger.debug("Current extension unchanged; skipping refresh")
+ return
+ }
+ currentExtensionId = extensionId
+ // Refresh all context menus to reflect the change
+ refreshContextMenus()
+ }
@@
- fun getCurrentExtensionId(): String? {
- return extensionManager.getCurrentProvider()?.getExtensionId()
- }
+ fun getCurrentExtensionId(): String? {
+ return currentExtensionId
+ }Also applies to: 59-65, 70-72
🤖 Prompt for AI Agents
In
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt
around lines 26-29 (also apply changes near 59-65 and 70-72): the field
currentExtensionId and setCurrentExtension() are mutated but
getCurrentExtensionId() ignores it and always queries ExtensionManager, so the
setter has no effect; to fix, make the manager’s state authoritative by changing
getCurrentExtensionId() to return currentExtensionId if non-null (falling back
to ExtensionManager only when null), update setCurrentExtension(id) to set the
field only when the id actually changes and trigger refresh/update exactly once
when it changes (avoid redundant refresh calls), and remove any redundant use of
ExtensionManager as the sole source-of-truth or delete the field and setter if
you prefer to rely on ExtensionManager instead—implement the first approach for
minimal change.
| private fun refreshContextMenus() { | ||
| try { | ||
| // Use IntelliJ Platform's proper mechanism to refresh UI on EDT thread | ||
| // This avoids calling @ApiStatus.OverrideOnly methods directly | ||
| com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater { | ||
| try { | ||
| // Get the action manager | ||
| val actionManager = com.intellij.openapi.actionSystem.ActionManager.getInstance() | ||
|
|
||
| // Get the dynamic context menu actions group | ||
| val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionContextMenu") | ||
| dynamicGroup?.let { group -> | ||
| // Trigger UI refresh by notifying the platform | ||
| // The platform will automatically call the appropriate update methods | ||
| logger.debug("Triggering UI refresh for dynamic context menu group") | ||
| } | ||
|
|
||
| logger.debug("Context menus refresh scheduled for extension: $currentExtensionId") | ||
| } catch (e: Exception) { | ||
| logger.warn("Failed to schedule context menu refresh", e) | ||
| } | ||
| } | ||
| } catch (e: Exception) { | ||
| logger.warn("Failed to refresh context menus", e) | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
refreshContextMenus() is effectively a no-op—explicitly trigger action-system updates
Good call removing the unsupported ActionManager.invalidateAction(). However, the current EDT task only fetches the group and logs; it doesn’t cause the IDE to re-run update() or recompute group children. Use ActivityTracker to nudge the action system.
Apply:
@@
- com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater {
+ com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater {
try {
// Get the action manager
val actionManager = com.intellij.openapi.actionSystem.ActionManager.getInstance()
// Get the dynamic context menu actions group
val dynamicGroup = actionManager.getAction("RunVSAgent.DynamicExtensionContextMenu")
- dynamicGroup?.let { group ->
- // Trigger UI refresh by notifying the platform
- // The platform will automatically call the appropriate update methods
- logger.debug("Triggering UI refresh for dynamic context menu group")
- }
+ // Trigger UI refresh by notifying the platform; this causes update() to be re-evaluated
+ com.intellij.ide.ActivityTracker.getInstance().inc()
+ if (dynamicGroup == null) {
+ logger.debug("Dynamic context menu group not found: RunVSAgent.DynamicExtensionContextMenu")
+ } else {
+ logger.debug("Triggered UI refresh for dynamic context menu group")
+ }Additionally, add the missing import at the top of the file:
import com.intellij.ide.ActivityTracker🤖 Prompt for AI Agents
In
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/contextmenu/DynamicContextMenuManager.kt
around lines 120 to 145, refreshContextMenus() currently only fetches the action
group and logs, so it never forces the action system to re-run
update()/recompute children; replace the no-op logging with a call that nudges
the action system via ActivityTracker (e.g., call
ActivityTracker.getInstance().inc(); or notify activity on EDT) to trigger
recomputation of actions, and add the missing import at the top: import
com.intellij.ide.ActivityTracker.
There was a problem hiding this comment.
Actionable comments posted: 0
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/core/VsixManager.kt (2)
153-172: Zip Slip risk when extracting entries — sanitize paths before writingentry.name may contain “../” and escape tempDir. Normalize and enforce that the resolved path stays within tempDir.
- ZipInputStream(BufferedInputStream(FileInputStream(vsixFile))).use { zis -> + ZipInputStream(BufferedInputStream(FileInputStream(vsixFile))).use { zis -> var entry = zis.nextEntry var extractedCount = 0 while (entry != null) { if (!entry.isDirectory) { - val entryPath = tempDir!!.toPath().resolve(entry.name) + val base = tempDir!!.toPath() + val entryPath = base.resolve(entry.name).normalize() + // Prevent Zip Slip (path traversal) + if (!entryPath.startsWith(base)) { + LOG.warn("Skipping suspicious zip entry outside extraction dir: ${entry.name}") + entry = zis.nextEntry + continue + } // Create parent directories if needed val parentDir = entryPath.parent if (parentDir != null) { Files.createDirectories(parentDir) } // Extract file - Files.copy(zis, entryPath) + Files.copy(zis, entryPath) extractedCount++ } entry = zis.nextEntry }
199-211: Potential failures moving across filesystems (/tmp → home): prefer copy + deleteFiles.move can fail across mounts; copy then delete is more robust. Also allows partial resilience.
- var movedCount = 0 + var movedCount = 0 for (file in extensionFiles) { val targetFile = targetPath.resolve(file.name).toFile() if (file.isDirectory) { - // Move directory recursively - moveDirectory(file, targetFile) + // Copy directory recursively + copyDirectory(file, targetFile) } else { - // Move file - Files.move(file.toPath(), targetFile.toPath()) + // Copy file + Files.copy(file.toPath(), targetFile.toPath()) } movedCount++ }And replace moveDirectory with a safe copyDirectory:
- private fun moveDirectory(source: File, target: File) { + private fun copyDirectory(source: File, target: File) { if (!target.exists()) { target.mkdirs() } val sourceFiles = source.listFiles() if (sourceFiles != null) { for (file in sourceFiles) { val targetFile = File(target, file.name) if (file.isDirectory) { - moveDirectory(file, targetFile) + copyDirectory(file, targetFile) } else { - Files.move(file.toPath(), targetFile.toPath()) + Files.copy(file.toPath(), targetFile.toPath()) } } } - - // Remove empty source directory - source.delete() + // Best-effort: remove copied source directory + deleteDirectory(source) }Also applies to: 245-265
♻️ Duplicate comments (3)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt (3)
35-43: Wire “New Task” into the menu listThe action is defined but not added, so it won’t appear.
return listOf( ExplainCodeAction(), FixCodeAction(), FixLogicAction(), ImproveCodeAction(), - AddToContextAction(), + AddToContextAction(), + NewTaskAction(), )
233-245: Inclusive endLine for human-readable ranges (avoid off-by-one at line boundaries)selectionEnd at start of next line yields an extra line. Clamp to (selectionEnd - 1).
- 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 - } + return if (selectionModel.hasSelection()) { + val selectedText = selectionModel.selectedText ?: "" + val selectionStart = selectionModel.selectionStart + val selectionEnd = selectionModel.selectionEnd + val startLine = document.getLineNumber(selectionStart) + val adjustedEndOffset = kotlin.math.max(selectionEnd - 1, selectionStart) + val endLine = document.getLineNumber(adjustedEndOffset) + EffectiveRange(selectedText, startLine, endLine) + } else nullIf you prefer an import:
+import kotlin.math.max ... - val adjustedEndOffset = kotlin.math.max(selectionEnd - 1, selectionStart) + val adjustedEndOffset = max(selectionEnd - 1, selectionStart)
83-84: De-duplicate handleCodeAction: reuse shared implementation from actions/RegisterCodeActions.ktThis file re-implements the same logic. Import and call the shared handleCodeAction, remove the local duplicate to keep one source of truth.
+import com.sina.weibo.agent.actions.handleCodeAction ... - RooCodeContextMenuProvider.handleCodeAction("roo-cline.explainCode.InCurrentTask", "EXPLAIN", args, project) + handleCodeAction("roo-cline.explainCode.InCurrentTask", "EXPLAIN", args, project) ... - RooCodeContextMenuProvider.handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project) + handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project) ... - RooCodeContextMenuProvider.handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project) + handleCodeAction("roo-cline.fixCode.InCurrentTask", "FIX", args, project) ... - RooCodeContextMenuProvider.handleCodeAction("roo-cline.improveCode.InCurrentTask", "IMPROVE", args, project) + handleCodeAction("roo-cline.improveCode.InCurrentTask", "IMPROVE", args, project) ... - RooCodeContextMenuProvider.handleCodeAction("roo-cline.addToContext", "ADD_TO_CONTEXT", args, project) + handleCodeAction("roo-cline.addToContext", "ADD_TO_CONTEXT", args, project) ... - RooCodeContextMenuProvider.handleCodeAction("roo-cline.newTask", "NEW_TASK", args, project) + handleCodeAction("roo-cline.newTask", "NEW_TASK", args, project)Remove the local duplicate method:
- fun handleCodeAction(command: String, promptType: String, params: Any, project: Project?) { - val latestWebView = project?.getService(WebViewManager::class.java)?.getLatestWebView() - if (latestWebView == null) { - return - } - ... - latestWebView.postMessageToWebView(messageJson) - }Also applies to: 108-109, 133-134, 158-159, 183-184, 208-209, 256-336
🧹 Nitpick comments (20)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooExtensionProvider.kt (3)
14-16: Use VsixManager.getBaseDirectory() instead of hardcoding user-config pathCentralize base path logic and avoid drift with other providers by using VsixManager.Companion.getBaseDirectory().
-import com.sina.weibo.agent.util.PluginConstants.ConfigFiles.getUserConfigDir +import com.sina.weibo.agent.extensions.core.VsixManager.Companion.getBaseDirectory ... - val possiblePaths = listOf( - "${getUserConfigDir()}/plugins/${config.codeDir}" - ) + val possiblePaths = listOf( + "${getBaseDirectory()}/${config.codeDir}" + )Also applies to: 44-47
44-47: Comment says “First check project paths” but code does not check project pathsEither adjust the comment or add project-relative candidates (like projectBase/../codeDir).
Example if you want to actually check project paths:
- // First check project paths - val possiblePaths = listOf( - "${getBaseDirectory()}/${config.codeDir}" - ) + // First check installed/base dir (user config), optionally project paths if desired + val possiblePaths = buildList { + add("${getBaseDirectory()}/${config.codeDir}") + project.basePath?.let { base -> + add("$base/${config.codeDir}") + add("$base/../${config.codeDir}") + add("$base/../../${config.codeDir}") + } + }
66-69: Mismatch between behavior and comment for dev/test fallbackThe comment says “always return true if we can't find the files” but the code returns false. Pick one and keep it consistent. If store-review requires strict existence, update the comment; otherwise return true.
- // 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 production/store review, return false if files are missing + // (Enable a dev toggle if you want to bypass in development builds) + return falseAlternatively (dev mode):
- return false + return java.lang.Boolean.getBoolean("runvsagent.dev.allowMissingExtensions")jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/VsixManager.kt (2)
286-301: Safe re-install: allow overwriting theme filesIf re-installing and the themes dir already exists, Files.copy will throw. Use REPLACE_EXISTING.
- Files.copy(themeFile.toPath(), targetFile.toPath()) + Files.copy( + themeFile.toPath(), + targetFile.toPath(), + java.nio.file.StandardCopyOption.REPLACE_EXISTING + )
36-38: Ensure base directory exists before useCreate the base directory up front to simplify downstream checks.
fun getBaseDirectory(): String { - return "${getUserConfigDir()}/plugins" + val base = "${getUserConfigDir()}/plugins" + try { + Files.createDirectories(Path.of(base)) + } catch (e: Exception) { + LOG.warn("Failed to ensure base directory exists: $base", e) + } + return base }Also applies to: 129-139
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineExtensionProvider.kt (4)
45-49: Nit: copy-paste in comment (“roo-code”)This provider is for Cline; update the comment to avoid confusion.
- // Check if roo-code extension files exist + // Check if cline extension files exist
51-53: Comment vs. code mismatch: “First check project paths” but only base dir is checkedEither adjust the comment or include project-relative candidates (like projectBase/../codeDir).
Example to check both:
- // First check project paths - val possiblePaths = listOf( - "${getBaseDirectory()}/${config.codeDir}" - ) + // First check installed/base dir, optionally project-relative locations + val possiblePaths = buildList { + add("${getBaseDirectory()}/${config.codeDir}") + project.basePath?.let { base -> + add("$base/${config.codeDir}") + add("$base/../${config.codeDir}") + add("$base/../../${config.codeDir}") + } + }
72-75: Comment says “always return true” but code returns falseAlign the comment with behavior or add a dev toggle as needed (same suggestion as Roo).
- // 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 production/store review, return false if files are missing + return false
50-68: Avoid duplicating discovery logic — consider VsixManager.getExtensionResourcePath(...)You already depend on VsixManager; reusing getExtensionResourcePath(extensionId, codeDir, project.basePath) would consolidate search order (project > vsix install > plugin resources).
- val possiblePaths = buildList { - add("${getBaseDirectory()}/${config.codeDir}") - project.basePath?.let { base -> - add("$base/${config.codeDir}") - add("$base/../${config.codeDir}") - add("$base/../../${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 - } + val found = VsixManager().getExtensionResourcePath( + getExtensionId(), + config.codeDir, + project.basePath + ) + if (found != null) return truejetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/core/ExtensionHostManager.kt (2)
382-395: Don’t gate base-directory lookup on projectPath; and remove unused ‘homeDir’The base directory resolution doesn’t depend on projectPath; removing the null gate avoids false negatives in non-project contexts. Also, ‘homeDir’ is now unused.
- val projectPath = project.basePath - val homeDir = System.getProperty("user.home") - if (projectPath != null) { - val possiblePaths = listOf( - "${getBaseDirectory()}/${extensionConfig.getCodeDir()}" - ) - - val foundPath = possiblePaths.find { File(it).exists() } - if (foundPath != null) { - return foundPath - } - } + val possiblePaths = listOf( + "${getBaseDirectory()}/${extensionConfig.getCodeDir()}" + ) + val foundPath = possiblePaths.find { File(it).exists() } + if (foundPath != null) { + return foundPath + }
397-415: Reuse VsixManager.getExtensionResourcePath to avoid duplication and driftVsixManager already encapsulates search order (project > vsix install > plugin resources). Using it here reduces maintenance.
- // Then check plugin resources (for built-in extensions) - try { - val pluginResourcePath = com.sina.weibo.agent.util.PluginResourceUtil.getResourcePath( - com.sina.weibo.agent.util.PluginConstants.PLUGIN_ID, - extensionConfig.getCodeDir() - ) - if (pluginResourcePath != null && File(pluginResourcePath).exists()) { - return pluginResourcePath - } - } catch (e: Exception) { - LOG.warn("Failed to get plugin resource path for extension: ${extensionConfig.getCodeDir()}", e) - } + VsixManager().getExtensionResourcePath( + extensionIdentifier = currentExtensionProvider?.getExtensionId() ?: "", + codeDir = extensionConfig.getCodeDir(), + projectPath = project.basePath + )?.let { return it }jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt (1)
338-415: Dead code: prompt templating helpers are unusedcreatePrompt/getPromptTemplate/replacePlaceholders aren’t referenced after switching to RooCodeSupportPrompt. Remove to reduce noise.
- fun createPrompt(promptType: String, params: Map<String, Any?>): String { ... } - fun getPromptTemplate(type: String): String { ... } - fun replacePlaceholders(template: String, params: Map<String, Any?>): String { ... }jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeActionConstants.kt (8)
8-10: Stringly-typed prompt types and params increase typo risk.Type aliases to String/Map keep things flexible for extensions but are brittle. Consider providing constants for built‑in prompt types and well-known param keys to reduce typing mistakes, while still allowing custom types.
You can add constants and use them across the codebase:
// New file or top of this file object RooCodePromptTypes { 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" } object RooCodePromptKeys { const val FILE_PATH = "filePath" const val START_LINE = "startLine" const val END_LINE = "endLine" const val USER_INPUT = "userInput" const val SELECTED_TEXT = "selectedText" const val DIAGNOSTICS = "diagnostics" const val TERMINAL_CONTENT = "terminalContent" }Then replace string literals in this file with these constants for stronger cohesion.
57-71: FIX template: small robustness note on diagnostics.Template expects ${diagnosticText}; your generator composes it from params["diagnostics"]. Good. See diagnostic handling comment below to better support non-string codes.
163-174: Reduce key typos by using constants for map keys.Hard-coded string keys can drift from call sites. Tie them to constants.
Apply:
- 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 - ) + val configs = mapOf( + RooCodePromptTypes.ENHANCE to ENHANCE, + RooCodePromptTypes.EXPLAIN to EXPLAIN, + RooCodePromptTypes.FIX to FIX, + RooCodePromptTypes.IMPROVE to IMPROVE, + RooCodePromptTypes.ADD_TO_CONTEXT to ADD_TO_CONTEXT, + RooCodePromptTypes.TERMINAL_ADD_TO_CONTEXT to TERMINAL_ADD_TO_CONTEXT, + RooCodePromptTypes.TERMINAL_FIX to TERMINAL_FIX, + RooCodePromptTypes.TERMINAL_EXPLAIN to TERMINAL_EXPLAIN, + RooCodePromptTypes.NEW_TASK to NEW_TASK + )(Include the RooCodePromptTypes object from the earlier comment.)
189-197: Handle numeric or non-string diagnostic codes.Casting the code to String drops numeric codes; also default message/source handling can be slightly stricter.
Apply:
- 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)" } ?: ""}" - } + return "\nCurrent problems detected:\n" + diagnostics.joinToString("\n") { d -> + val source = d["source"]?.toString()?.ifBlank { "Error" } ?: "Error" + val message = d["message"]?.toString() ?: "" + val code = d["code"]?.toString()?.takeIf { it.isNotBlank() } + "- [$source] $message${code?.let { " ($it)" } ?: ""}" + }
206-227: Placeholder regex can be safer and cheaper.
- Current pattern
\$\{(.*?)}matches any content, including braces; restrict to identifier-like keys.- Consider precompiling the regex at object scope.
Minimal change:
- val pattern = Regex("""\$\{(.*?)}""") + val pattern = Regex("""\$\{([A-Za-z0-9_]+)}""")Optional precompile outside the method:
// at object scope private val PLACEHOLDER_RE = Regex("""\$\{([A-Za-z0-9_]+)}""")and then use PLACEHOLDER_RE in createPrompt.
236-238: Unknown prompt types currently return an empty string — consider signaling the issue.Silent fallback to "" can produce blank messages posted to the webview. Prefer logging and/or a safe fallback (e.g., NEW_TASK or selectedText/userInput) to keep UX predictable.
Example (no external imports needed):
val template = customSupportPrompts?.get(type) ?: RooCodeSupportPromptConfigs.configs[type]?.template return template ?: ""Alternatively, throw IllegalArgumentException with a clear message if you want to fail fast during development.
248-251: Provide a non-empty fallback when template not found.Prevents sending blank messages if a dynamic prompt type is unknown.
Apply:
- val template = get(customSupportPrompts, type) - return createPrompt(template, params) + val template = get(customSupportPrompts, type) + val effectiveTemplate = template.ifBlank { + // Fallback: reuse user-provided content to avoid empty messages + (params["selectedText"] as? String) + ?: (params["userInput"] as? String) + ?: "" + } + return createPrompt(effectiveTemplate, params)
24-174: Add unit tests for prompt creation and placeholder substitution.Given the templating logic, tests will prevent regressions (e.g., placeholder typos, missing fallbacks).
I can add tests verifying:
- Each built-in type renders expected text when all params are provided.
- Missing optional params don’t produce awkward artifacts (e.g., “:-”).
- Diagnostics are correctly formatted for empty/non-empty lists.
Want me to open a follow-up PR with tests?Also applies to: 189-251
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt(0 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/core/ExtensionHostManager.kt(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/VsixManager.kt(3 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineExtensionProvider.kt(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeActionConstants.kt(13 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt(1 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooExtensionProvider.kt(2 hunks)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/ExtensionResourceStatusAction.kt(0 hunks)
💤 Files with no reviewable changes (2)
- jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/ui/actions/ExtensionResourceStatusAction.kt
- jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineContextMenuProvider.kt
🧰 Additional context used
🧬 Code graph analysis (6)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooExtensionProvider.kt (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/util/PluginConstants.kt (4)
getUserConfigDir(45-47)MAIN_CONFIG_FILE(16-78)PLUGIN_ID(7-79)getMainConfigPath(52-54)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/plugin/WecoderPlugin.kt (2)
LOG(42-220)project(186-214)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/VsixManager.kt (1)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/util/PluginConstants.kt (5)
getUserConfigDir(45-47)MAIN_CONFIG_FILE(16-78)getMainConfigPath(52-54)getExtensionConfigPath(59-61)PLUGIN_ID(7-79)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/cline/ClineExtensionProvider.kt (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/VsixManager.kt (1)
getBaseDirectory(36-38)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/roo/RooCodeActionConstants.kt (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeContextMenuProvider.kt (1)
createPrompt(345-348)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (7)
handleCodeAction(228-299)createAction(19-132)registerCodeAction(157-201)text(142-146)actionPerformed(164-199)createAction(28-51)provideCodeActions(95-131)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/core/ExtensionHostManager.kt (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/core/VsixManager.kt (1)
getBaseDirectory(36-38)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/roo/RooCodeContextMenuProvider.kt (2)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/actions/RegisterCodeActions.kt (9)
createAction(19-132)handleCodeAction(228-299)provideCodeActions(95-131)text(142-146)createAction(28-51)registerCodeAction(157-201)actionPerformed(33-49)actionPerformed(164-199)getEffectiveRange(75-87)jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/plugin/WecoderPlugin.kt (1)
LOG(42-220)
🔇 Additional comments (5)
jetbrains_plugin/src/main/kotlin/com/sina/weibo/agent/extensions/plugin/roo/RooCodeActionConstants.kt (5)
24-34: ENHANCE template looks good.Clear instruction and minimal surface; safe to ship.
98-103: ADD_TO_CONTEXT: concise and correct.Straight pass-through of location + code is appropriate here.
176-182: No remaining usages of SupportPrompt, ActionNames, or CommandIds detected
All call sites in*.ktnow referenceRooCodeSupportPromptand the new constants; there are no leftover references to the old identifiers. The compatibility risk noted in the previous comment has been addressed.
5-5: Package relocation verified—no legacy references foundI searched the entire Kotlin/Java codebase and all
plugin.xmldefinitions for the oldcom.sina.weibo.agent.extensions.roopackage and symbols (SupportPrompt,ActionNames,CommandIds,RooCodeSupportPrompt) and found nothing remaining. All call sites and plugin descriptors now reference the relocatedcom.sina.weibo.agent.extensions.plugin.roonamespace correctly. No further updates are required.
39-51: Verify all EXPLAIN invocations include line numbersPlease manually confirm that every code path which triggers the EXPLAIN prompt always sets both
startLineandendLinebefore rendering the template. Our automated search didn’t conclusively cover every flow:
- In
RooCodeContextMenuProvider.ktaround line 83 (the “InCurrentTask” action), ensure you’re setting bothbefore callingargs["startLine"] = effectiveRange.startLine args["endLine"] = effectiveRange.endLine + 1handleCodeAction("roo-cline.explainCode.InCurrentTask", "EXPLAIN", args, project)- In the other code-action branches (e.g. the “sendMessage” flows at lines 292–296 and 317–321), verify that the
promptParamsmap passed intoalso contains bothRooCodeSupportPrompt.create("EXPLAIN", promptParams)startLineandendLine.Optional refactor to make the template resilient when no lines are provided:
- Explain the following code from file path ${filePath}:${startLine}-${endLine} + Explain the following code from file path ${filePath}${lineRange}Compute
lineRangeahead of time as either":<start>-<end>"or the empty string.
Summary by CodeRabbit