Skip to content

Conversation

@phodal
Copy link
Member

@phodal phodal commented Dec 3, 2025

Summary by CodeRabbit

  • New Features

    • Multi-step planning: create, import (Markdown), track and visualize task/step progress across UIs.
    • Plan management tool and plan-aware rendering with compact summaries shown in IDE, Compose UI, and webview.
    • VS Code extension: chat UI, file-context tools, completion support, diff preview/accept, and IDE server integration.
    • File selection/search UI: file chips, top toolbar, popup search, and workspace indexing for fast lookup.
  • Tests

    • Added unit tests covering plan parsing, plan state, tool invocation, and VS Code bridge components.

✏️ Tip: You can customize this high-level summary in your review settings.

phodal added 30 commits December 2, 2025 23:39
- Create project directory structure
- Add package.json with VSCode extension configuration
- Add tsconfig.json for TypeScript
- Add README.md with TODO list and architecture overview

Closes #31
Phase 2 - Core Services:
- Add mpp-core bridge layer with LLMService, CodingAgent, ToolRegistry,
  CompletionManager, and DevInsCompiler wrappers
- Implement extension.ts entry point with activation/deactivation

Phase 3 - IDE Integration:
- Implement IDE Server with Express HTTP endpoints
- Add DiffManager for file diff operations
- Support MCP protocol communication

Phase 4 - Chat Interface:
- Implement ChatViewProvider with webview
- Add streaming LLM response support
- Create embedded HTML chat UI

Phase 5 - Commands:
- Register autodev.chat, autodev.acceptDiff, autodev.cancelDiff, autodev.runAgent
- Add keyboard shortcut (Cmd+Shift+A)

Testing:
- Add vitest configuration
- Add unit tests for mpp-core bridge
- Add unit tests for DiffManager
- Add unit tests for IDEServer

Refs #31
Phase 5 - Status Bar:
- Add StatusBarManager with idle/thinking/streaming/error states
- Animated status icons during LLM operations
- Click to open chat command

Phase 6 - DevIns Language Support:
- Add TextMate grammar for syntax highlighting
- Support commands (/), agents (@), variables ($)
- Add language configuration (brackets, folding, etc.)
- Implement DevInsCompletionProvider with built-in completions
- Register completion triggers for /, @, $ characters

Testing:
- Add unit tests for StatusBarManager
- Add unit tests for DevInsCompletionProvider
- All 56 tests passing

Refs #31
Phase 6 - React Webview UI:
- Create webview React project with Vite build
- Add MessageList component with Markdown rendering
- Add ChatInput component with auto-resize textarea
- Add useVSCode hook for extension communication
- Integrate VSCode theme variables for consistent styling
- Support streaming response with animated indicators
- Add fallback inline HTML when React bundle not available
- Update ChatViewProvider to load React bundle with CSP

Components:
- App.tsx - Main chat application
- MessageList.tsx - Message display with Markdown support
- ChatInput.tsx - Input with keyboard shortcuts
- useVSCode.ts - VSCode API communication hook

Testing:
- Add ChatViewProvider tests
- All 63 tests passing

Refs #31
Extract MarkdownTextParser to a shared mpp-core module for reuse and improved testability across platforms. Update imports accordingly.
Phase 7 - Sketch Renderer System:
- Add Timeline component mirroring mpp-ui's ComposeRenderer
- Add SketchRenderer with specialized sub-renderers:
  - CodeBlockRenderer: syntax highlighting with Copy/Insert/Apply actions
  - DiffRenderer: diff display with Accept/Reject/View actions
  - ThinkingRenderer: collapsible thinking blocks
  - TerminalRenderer: terminal command and output display
  - MarkdownRenderer: GFM markdown with react-markdown
  - ToolCallRenderer: tool call information display
- Add codeFence parser mirroring mpp-core's CodeFence.parseAll()
- Add timeline types (TimelineItem, AgentState, ToolCallInfo, etc.)

ChatViewProvider Refactoring:
- Replace LLMService with CodingAgent for agent-based interactions
- Add VSCodeRenderer implementing JsCodingAgentRenderer interface
- Forward agent events (toolCall, toolResult, terminalOutput) to webview
- Add action handlers (insert, apply, run-command, accept-diff, etc.)
- Remove API key requirement - use helpful guidance for non-DevIns input

All 63 tests passing.

Refs #31
Phase 8 - Configuration Management Integration

Changes:
- Add ConfigManager service to load config from ~/.autodev/config.yaml
- Refactor ChatViewProvider to use ConfigManager instead of VSCode settings
- Mirror IdeaAgentViewModel's loadConfiguration() pattern
- Add lazy initialization of CodingAgent with proper config validation
- Create renderer that forwards events to webview (like TuiRenderer)
- Add 'Open Config' button in header and config prompt
- Support all LLM providers: openai, anthropic, deepseek, ollama, etc.
- Auto-create config template when file doesn't exist

Architecture:
- ConfigManager.load() → AutoDevConfigWrapper → getActiveConfig()
- isValid() check before agent initialization
- JsKoogLLMService + JsCodingAgent from mpp-core
- Renderer implements all JsCodingAgentRenderer methods

All 63 tests passing.

Refs #31
- Add ModelSelector component similar to IdeaModelSelector.kt
- Update ChatInput with toolbar: ModelSelector on left, Stop/Send on right
- Add config state management in App.tsx
- Add sendConfigUpdate, stopExecution, selectConfig in chat-view.ts
- Add new message types: configUpdate, stopExecution, selectConfig

All 63 tests passing.

Refs #31
- Acquire VSCode API in HTML before React loads to avoid duplicate acquisition error
- Store API on window.vscodeApi for React to use
- Remove debug logging from production code

All 63 tests passing.

Refs #31
User message was being added twice - once in handleSend for immediate
feedback and again when receiving userMessage from extension.

Refs #31
- Fix repository URL from phodal/auto-dev to phodal/auto-dev-sketch
- Remove unused colors variable in status-bar.ts
- Add platform detection for welcome message keybinding (Cmd vs Ctrl)
- Create project directory structure
- Add package.json with VSCode extension configuration
- Add tsconfig.json for TypeScript
- Add README.md with TODO list and architecture overview

Closes #31
Phase 2 - Core Services:
- Add mpp-core bridge layer with LLMService, CodingAgent, ToolRegistry,
  CompletionManager, and DevInsCompiler wrappers
- Implement extension.ts entry point with activation/deactivation

Phase 3 - IDE Integration:
- Implement IDE Server with Express HTTP endpoints
- Add DiffManager for file diff operations
- Support MCP protocol communication

Phase 4 - Chat Interface:
- Implement ChatViewProvider with webview
- Add streaming LLM response support
- Create embedded HTML chat UI

Phase 5 - Commands:
- Register autodev.chat, autodev.acceptDiff, autodev.cancelDiff, autodev.runAgent
- Add keyboard shortcut (Cmd+Shift+A)

Testing:
- Add vitest configuration
- Add unit tests for mpp-core bridge
- Add unit tests for DiffManager
- Add unit tests for IDEServer

Refs #31
Phase 5 - Status Bar:
- Add StatusBarManager with idle/thinking/streaming/error states
- Animated status icons during LLM operations
- Click to open chat command

Phase 6 - DevIns Language Support:
- Add TextMate grammar for syntax highlighting
- Support commands (/), agents (@), variables ($)
- Add language configuration (brackets, folding, etc.)
- Implement DevInsCompletionProvider with built-in completions
- Register completion triggers for /, @, $ characters

Testing:
- Add unit tests for StatusBarManager
- Add unit tests for DevInsCompletionProvider
- All 56 tests passing

Refs #31
Phase 6 - React Webview UI:
- Create webview React project with Vite build
- Add MessageList component with Markdown rendering
- Add ChatInput component with auto-resize textarea
- Add useVSCode hook for extension communication
- Integrate VSCode theme variables for consistent styling
- Support streaming response with animated indicators
- Add fallback inline HTML when React bundle not available
- Update ChatViewProvider to load React bundle with CSP

Components:
- App.tsx - Main chat application
- MessageList.tsx - Message display with Markdown support
- ChatInput.tsx - Input with keyboard shortcuts
- useVSCode.ts - VSCode API communication hook

Testing:
- Add ChatViewProvider tests
- All 63 tests passing

Refs #31
Extract MarkdownTextParser to a shared mpp-core module for reuse and improved testability across platforms. Update imports accordingly.
Phase 7 - Sketch Renderer System:
- Add Timeline component mirroring mpp-ui's ComposeRenderer
- Add SketchRenderer with specialized sub-renderers:
  - CodeBlockRenderer: syntax highlighting with Copy/Insert/Apply actions
  - DiffRenderer: diff display with Accept/Reject/View actions
  - ThinkingRenderer: collapsible thinking blocks
  - TerminalRenderer: terminal command and output display
  - MarkdownRenderer: GFM markdown with react-markdown
  - ToolCallRenderer: tool call information display
- Add codeFence parser mirroring mpp-core's CodeFence.parseAll()
- Add timeline types (TimelineItem, AgentState, ToolCallInfo, etc.)

ChatViewProvider Refactoring:
- Replace LLMService with CodingAgent for agent-based interactions
- Add VSCodeRenderer implementing JsCodingAgentRenderer interface
- Forward agent events (toolCall, toolResult, terminalOutput) to webview
- Add action handlers (insert, apply, run-command, accept-diff, etc.)
- Remove API key requirement - use helpful guidance for non-DevIns input

All 63 tests passing.

Refs #31
Phase 8 - Configuration Management Integration

Changes:
- Add ConfigManager service to load config from ~/.autodev/config.yaml
- Refactor ChatViewProvider to use ConfigManager instead of VSCode settings
- Mirror IdeaAgentViewModel's loadConfiguration() pattern
- Add lazy initialization of CodingAgent with proper config validation
- Create renderer that forwards events to webview (like TuiRenderer)
- Add 'Open Config' button in header and config prompt
- Support all LLM providers: openai, anthropic, deepseek, ollama, etc.
- Auto-create config template when file doesn't exist

Architecture:
- ConfigManager.load() → AutoDevConfigWrapper → getActiveConfig()
- isValid() check before agent initialization
- JsKoogLLMService + JsCodingAgent from mpp-core
- Renderer implements all JsCodingAgentRenderer methods

All 63 tests passing.

Refs #31
- Add ModelSelector component similar to IdeaModelSelector.kt
- Update ChatInput with toolbar: ModelSelector on left, Stop/Send on right
- Add config state management in App.tsx
- Add sendConfigUpdate, stopExecution, selectConfig in chat-view.ts
- Add new message types: configUpdate, stopExecution, selectConfig

All 63 tests passing.

Refs #31
- Acquire VSCode API in HTML before React loads to avoid duplicate acquisition error
- Store API on window.vscodeApi for React to use
- Remove debug logging from production code

All 63 tests passing.

Refs #31
User message was being added twice - once in handleSend for immediate
feedback and again when receiving userMessage from extension.

Refs #31
- Fix repository URL from phodal/auto-dev to phodal/auto-dev-sketch
- Remove unused colors variable in status-bar.ts
- Add platform detection for welcome message keybinding (Cmd vs Ctrl)
- Use mpp-core's JsCompletionManager for DevIn command completions
- Add getCompletions and applyCompletion message handlers
- Update CompletionPopup to support mpp-core CompletionItem format
- Fix popup positioning by removing overflow:hidden from container
- Add completion state management in App.tsx
feat(vscode): Enhanced Chat Input with DevIn Language Support
- Add SelectedFileItem, FileChip, TopToolbar, FileSearchPopup components
- Add WorkspaceFileSearchProvider with pre-built file index for fast search
- Add IndexingState enum for tracking indexing progress
- Integrate file context into DevInEditorInput with buildAndSendMessage()
- Add Prompt Enhancement button to BottomToolbar
- Add AutoAwesome and History icons to AutoDevComposeIcons
- Add FileContext to EditorCallbacks for file context submission

Closes #35
* feat(mpp-ui): implement file context management with indexed search

- Add SelectedFileItem, FileChip, TopToolbar, FileSearchPopup components
- Add WorkspaceFileSearchProvider with pre-built file index for fast search
- Add IndexingState enum for tracking indexing progress
- Integrate file context into DevInEditorInput with buildAndSendMessage()
- Add Prompt Enhancement button to BottomToolbar
- Add AutoAwesome and History icons to AutoDevComposeIcons
- Add FileContext to EditorCallbacks for file context submission

Closes #35

* feat(mpp-core): add plan management data models and parser for task management

- Add TaskStatus enum (TODO, IN_PROGRESS, COMPLETED, FAILED, BLOCKED) and PlanPhase enum (PDCA cycle)
- Add CodeFileLink data class for markdown file link extraction
- Add PlanStep, PlanTask, AgentPlan data models with serialization support
- Add MarkdownPlanParser using pure Kotlin regex (no IntelliJ dependencies)
- Add PlanStateService with StateFlow and listener pattern for reactive updates
- Add PlanUpdateListener interface for UI notifications
- Add comprehensive unit tests for parser and state service
- Fix DocQLReturnAllTest missing Pending branch in when expressions

Part of #37

* feat(mpp-core): add PlanManagementTool for AI agent task planning

- Add PlanManagementTool with CREATE, UPDATE, COMPLETE_STEP, FAIL_STEP, VIEW actions
- Add PlanManagementParams, PlanManagementSchema, PlanManagementInvocation
- Add comprehensive unit tests for all tool actions
- Tool integrates with PlanStateService for state management

Part of #37

* feat(mpp-core): register PlanManagementTool in BuiltinToolsProvider

- Add PlanManagementTool to BuiltinToolsProvider.provide()
- Add executePlanManagementTool method in ToolOrchestrator
- Add tests for plan and task-boundary tool registration

Part of #37

* feat(mpp-ui): add PlanPanel UI component and integrate with ComposeRenderer

- Create PlanPanel composable with task and step display
- Add expandable task cards with progress tracking
- Implement status icons and colors for plan visualization
- Integrate plan state tracking in ComposeRenderer
- Handle plan tool calls to update UI state

Part of #37

* feat(mpp-core): add plan management guidance to system prompt and fix parameter parsing

- Add Planning and Task Management section to CodingAgentTemplate (EN and ZH)
- Document when to use planning, plan format, and plan actions
- Update Task Completion Strategy to include planning step
- Fix taskIndex/stepIndex parameter parsing in ToolOrchestrator to handle both Number and String types

Part of #37

* refactor(mpp-core): remove TaskBoundaryTool in favor of PlanManagementTool

- Remove TaskBoundaryTool.kt as PlanManagementTool provides superset functionality
- Remove TaskBoundaryTool from BuiltinToolsProvider and ToolOrchestrator
- Update ToolRegistryTest to remove task-boundary test
- Update comments referencing task-boundary
- Enhance PlanManagementTool with detailed KDoc documentation
- Fix tuiEmoji to use proper emoji character

Part of #37

* fix(executor): reduce shell command max timeout to 2 minutes

Lower the maximum wait time for shell command execution to 2 minutes and pass it as a timeout to the orchestrator context.
- Add PlanSummaryData model in mpp-core for lightweight UI summary
- Add PlanSummaryBar Compose component in mpp-ui
- Add IdeaPlanSummaryBar with Jewel styling for mpp-idea
- Add JewelRenderer plan tracking (currentPlan StateFlow)
- Add PlanSummaryBar React component for mpp-vscode
- Integrate PlanSummaryBar above input box in all platforms

The PlanSummaryBar displays a collapsible summary of the current plan
above the input box, showing progress, current step, and allowing
users to expand for full plan details.
Simplifies schema by eliminating UPDATE action and clarifying usage of COMPLETE_STEP for progress updates.
Refactor rendering logic and update plan step handling across agent components to enhance orchestration and output formatting.
- PlanSummaryBar now expands upward instead of downward to avoid compressing content
- Plan tool calls are no longer rendered in the message timeline (shown only in PlanSummaryBar)
- Added renderToolCallWithParams method to pass parsed parameters directly
- Fixed parameter parsing issues with complex values like planMarkdown containing JSON
Revise agent template to require codebase exploration before plan creation, add concrete examples, and update planning guidelines for both English and Chinese. Also update DevInEditorInput to use transparent text and default keyboard behavior.
Refine minHeight, maxHeight, and padding for iOS to improve input usability and avoid keyboard constraint issues.
Removed the linear progress indicator when content is not complete in SketchRenderer.
Copilot AI review requested due to automatic review settings December 3, 2025 15:38
@coderabbitai
Copy link

coderabbitai bot commented Dec 3, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a multiplatform plan-management system (models, parser, reactive service, UI summaries), replaces TaskBoundaryTool with PlanManagementTool, integrates plan flows into agent executor and renderers, updates prompts to "Explore First, Then Plan", and adds a full VSCode extension and webview UI for plan and editor workflows.

Changes

Cohort / File(s) Summary
Plan data models & parsing
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/* (AgentPlan.kt, PlanTask.kt, PlanStep.kt, PlanSummaryData.kt, CodeFileLink.kt, TaskStatus.kt, MarkdownPlanParser.kt)
New serializable plan/task/step models, file-link helper, status/phase enums, markdown parser and formatter, and UI-friendly PlanSummaryData conversions.
Plan state service & listeners
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/PlanStateService.kt, PlanUpdateListener.kt
New reactive PlanStateService exposing StateFlow, mutation APIs (create/update/completeStep/etc.), listener registration, and notification helpers.
Plan management tool & registry
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/impl/PlanManagementTool.kt, mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/registry/BuiltinToolsProvider.kt
New PlanManagementTool (CREATE/UPDATE/COMPLETE_STEP/FAIL_STEP/VIEW), step param parsing, and registration replacing removed TaskBoundaryTool.
Executor & orchestration changes
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/executor/CodingAgentExecutor.kt, mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/orchestrator/ToolOrchestrator.kt
New constructor flag singleToolPerIteration, enforce single tool execution per iteration, renderToolCallWithParams usage, post-plan summary rendering, plan-state access helper, and step parsing for plan ops.
Renderer surfaces
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.kt, RendererModels.kt, RendererUtils.kt, mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt
Added renderToolCallWithParams, renderPlanSummary hooks, live-terminal hooks, improved param parsing (JSON+key=value), and JS adapter (JsPlanSummaryData) for plan summaries.
Agent prompts / template
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/CodingAgentTemplate.kt
Updated prompts to "Explore First, Then Plan" (EN/ZH) and introduced explicit exploration-first workflow in template text.
Removed tool
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/impl/TaskBoundaryTool.kt
Deleted TaskBoundaryTool and its related types and schema.
Tests (core)
mpp-core/src/commonTest/kotlin/cc/unitmesh/agent/plan/*, mpp-core/src/commonTest/kotlin/cc/unitmesh/agent/tool/impl/PlanManagementToolTest.kt, ToolRegistryTest.kt
New tests for MarkdownPlanParser, PlanStateService, PlanManagementTool behaviors and registration.
IDE / UI integrations (Compose, IDEA, UI modules)
mpp-ui/*, mpp-idea/* (ComposeRenderer.kt, PlanPanel.kt, PlanSummaryBar.kt, JewelRenderer.kt, Idea UI files)
Expose currentPlan state, renderToolCallWithParams implementations, PlanPanel/PlanSummaryBar UI components, and plan-aware renderer updates for Compose and IDEA.
File context & search UI
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/editor/context/* (SelectedFileItem.kt, FileChip.kt, FileSearchPopup.kt, TopToolbar.kt, WorkspaceFileSearchProvider.kt)
New file selection models, UI chips, file search popup, top toolbar, and workspace index/search provider with indexing state.
Editor callbacks & input changes
mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/editor/EditorCallbacks.kt, mpp-ui/src/commonMain/kotlin/.../DevInEditorInput.kt, BottomToolbar.kt
Added FileContext type and onSubmit overload, file-context-aware input, file-search integration, and enhance action button.
Markdown utility package move
mpp-core/src/commonMain/kotlin/cc/unitmesh/markdown/MarkdownTextParser.kt
Package updated and imports tidied.
Build/deps
mpp-core/build.gradle.kts
Changed kotlinx-datetime dependency from runtimeOnly to implementation.
JVM FS logging
mpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/filesystem/DefaultFileSystem.jvm.kt
Added detailed debug/timing logs to searchFiles.
IDEA input area signatures
mpp-idea/src/main/kotlin/.../IdeaAgentApp.kt, IdeaDevInInputArea.kt
Threaded currentPlan into UI; updated composable signatures to accept currentPlan.
JS/TS VSCode extension
mpp-vscode/* (major new files: package.json, src/extension.ts, src/providers/*, src/services/*, src/bridge/mpp-core.ts, webview/src/*)
New VSCode extension: IDE server, diff manager, status bar, config manager, TypeScript bridge to Kotlin/JS, chat view provider, DevIns completion, extensive webview React app and components (timeline, input, completions, plan summary, sketch renderers), tests and build configs.
Webview components & styling
mpp-vscode/webview/src/components/**, webview/src/*.tsx, webview/src/components/*/*.css
Large set of React components and CSS for chat, sketch renderers (CodeBlock, Diff, Markdown, Terminal), PlanSummaryBar, completion popup, file search, model selector, timeline, and related UI.
Tests & mocks (VSCode)
mpp-vscode/test/* (test/mocks/vscode.ts, various provider/service tests`)
Mocks and unit tests for extension services, bridge, chat-view, completion provider, diff manager, IDE server, and status bar.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Agent as CodingAgent
    participant Orch as ToolOrchestrator
    participant PlanTool as PlanManagementTool
    participant PlanSvc as PlanStateService
    participant Renderer

    User->>Agent: submit request (explore-first mode)
    Agent->>Orch: call exploration tools (glob/read/grep)
    Orch->>Renderer: render tool results
    Renderer->>User: show exploration output

    User->>Agent: instruct "create plan" (planMarkdown)
    Agent->>Orch: invoke plan tool
    Orch->>PlanTool: execute(action=CREATE, planMarkdown)
    PlanTool->>PlanSvc: createPlanFromMarkdown(markdown)
    PlanSvc->>PlanSvc: parse → AgentPlan, notify listeners
    PlanSvc-->>PlanTool: return plan summary
    PlanTool-->>Orch: ToolResult(with plan summary)
    Orch->>Renderer: renderToolCallWithParams(...) and renderPlanSummary(...)
    Renderer->>User: show plan summary UI

    loop iterate steps
        User->>Agent: execute step
        Agent->>Orch: invoke COMPLETE_STEP
        Orch->>PlanTool: execute(action=COMPLETE_STEP, indices)
        PlanTool->>PlanSvc: completeStep(taskId, stepId)
        PlanSvc->>PlanSvc: update statuses, notify listeners
        PlanSvc-->>PlanTool: updated plan summary
        PlanTool-->>Orch: ToolResult(progress)
        Orch->>Renderer: renderPlanSummary(updated)
        Renderer->>User: show progress update
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Areas requiring extra attention:

  • Plan state lifecycle, concurrency, and StateFlow emissions (PlanStateService.kt, AgentPlan.kt, PlanTask.kt, PlanStep.kt, MarkdownPlanParser.kt).
  • Tool orchestration and replacement of TaskBoundaryTool with PlanManagementTool (ToolOrchestrator.kt, PlanManagementTool.kt), including param parsing and backward-compatibility.
  • Executor single-tool-per-iteration behavior and LLM tool-call handling changes (CodingAgentExecutor.kt).
  • Renderer plumbing and cross-platform hooks for renderPlanSummary/renderToolCallWithParams (CodingAgentRenderer.kt, JewelRenderer.kt, ComposeRenderer.kt, JS renderer adapters).
  • Kotlin/JS ↔ TypeScript bridge correctness and webview message contracts (mpp-vscode/src/bridge/mpp-core.ts, webview components, VSCode provider code).
  • Workspace file indexing and debounce behavior for large repositories (WorkspaceFileSearchProvider.kt, FileSearchPopup.tsx).

Possibly related PRs

Poem

🐰
I hopped through code with tiny paws,
I stitched new plans with careful laws,
From markdown seeds to progress bars,
We track each step beneath the stars.
Now agents plan — hooray for us!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.23% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add mpp-vscode support' accurately describes the primary change—introducing comprehensive VSCode extension support with mpp-vscode modules, configuration, services, UI components, and tooling integration.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot finished reviewing on behalf of phodal December 3, 2025 15:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds comprehensive VSCode extension support for the AutoDev multi-platform project (mpp). It introduces a React-based webview for the chat interface, VSCode extension services for IDE integration, and TypeScript/JavaScript infrastructure to bridge with the Kotlin multiplatform core (mpp-core).

Key changes:

  • React webview application with Timeline-based UI architecture for agent interactions
  • VSCode extension services: DiffManager, IDEServer (MCP protocol), StatusBarManager
  • DevIns language support with syntax highlighting and completion
  • Bridge to mpp-core for LLM services, tool execution, and agent capabilities

Reviewed changes

Copilot reviewed 121 out of 122 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
mpp-vscode/webview/vite.config.ts Vite configuration for React webview build
mpp-vscode/webview/tsconfig.json TypeScript config for webview with React JSX
mpp-vscode/webview/package.json React dependencies and build scripts
mpp-vscode/webview/src/* React components for Timeline, ChatInput, ModelSelector, etc.
mpp-vscode/src/extension.ts Main extension entry point with service initialization
mpp-vscode/src/services/* Core services: DiffManager, IDEServer, StatusBarManager
mpp-vscode/src/providers/* ChatViewProvider and DevIns completion provider
mpp-vscode/test/* Unit tests for services and providers
mpp-vscode/syntaxes/* DevIns language syntax definitions
mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts Added plan summary rendering support

The PR is well-structured with comprehensive test coverage, proper TypeScript typing, and follows VSCode extension best practices. The webview architecture mirrors the existing mpp-ui design for consistency across platforms.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

JsCompletionManager,
JsDevInsCompiler,
JsToolRegistry,
JsCompressionConfig
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable JsCompressionConfig.

Copilot uses AI. Check for mistakes.
return;
}

const basePath = workspaceFolders[0].uri.fsPath;
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable basePath.

Copilot uses AI. Check for mistakes.
const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(webviewPath, 'assets', 'index.css'));

// Use nonce for security
const nonce = this.getNonce();
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable nonce.

Copilot uses AI. Check for mistakes.
- Remove unused JsCompressionConfig import in mpp-core.ts
- Remove unused basePath variable in chat-view.ts
- Remove unused nonce variable and getNonce method in chat-view.ts
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.kt (1)

498-510: clearTimeline() does not reset _currentPlan.

When clearing the timeline state, _currentPlan retains its value. This may cause stale plan data to persist across sessions.

Add plan reset:

     fun clearTimeline() {
         _timeline.value = emptyList()
         _currentStreamingOutput.value = ""
         _isProcessing.value = false
         _currentIteration.value = 0
         _errorMessage.value = null
         _taskCompleted.value = false
         _totalTokenInfo.value = TokenInfo()
         _lastMessageTokenInfo = null
         _tasks.value = emptyList()
+        _currentPlan.value = null
         executionStartTime = 0L
         _currentExecutionTime.value = 0L
     }
♻️ Duplicate comments (1)
mpp-core/src/jvmTest/kotlin/cc/unitmesh/agent/tool/impl/DocQLReturnAllTest.kt (1)

171-176: Consistent Pending handling.

The Pending case is handled identically to the first when expression (lines 125-130), maintaining consistency across both test scenarios.

🟡 Minor comments (17)
mpp-ios/build-ios-app.sh-347-355 (1)

347-355: Fix UUID regex to be case-insensitive.

Line 349's regex pattern [0-9A-F]{8}-... only matches uppercase hex digits. Since xcrun devicectl list devices can return UUIDs in lowercase or mixed case, this will cause silent device detection failures.

Use the grep -i flag to make the pattern case-insensitive:

-            DEVICE_UUID=$(xcrun devicectl list devices 2>&1 | grep "connected" | grep -oE "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}" | head -n 1)
+            DEVICE_UUID=$(xcrun devicectl list devices 2>&1 | grep "connected" | grep -ioE "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}" | head -n 1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererUtils.kt-99-99 (1)

99-99: Update comment to reflect actual parsing capabilities.

The comment states "flat objects with string values" but the implementation also handles numbers (line 107) and unquoted words like booleans (line 108).

Apply this diff to make the comment more accurate:

-                // Simple JSON parsing for flat objects with string values
+                // Simple JSON parsing for flat objects with string, number, or boolean values
mpp-vscode/README.md-106-111 (1)

106-111: Replace hardcoded absolute path with relative path.

Line 109 contains a hardcoded absolute path (/Volumes/source/ai/autocrud) that is specific to a particular development environment and won't work for other users.

Apply this diff to use a relative path:

 ### 构建 mpp-core
 
 ```bash
-cd /Volumes/source/ai/autocrud
+cd ../  # Navigate to repository root
 ./gradlew :mpp-core:assembleJsPackage

Alternatively, clarify that this is an example path and users should adjust accordingly.

</blockquote></details>
<details>
<summary>mpp-vscode/webview/src/components/plan/PlanSummaryBar.css-84-86 (1)</summary><blockquote>

`84-86`: **Expand arrow rotation appears incorrect.**

The `.expand-arrow.expanded` rule sets `transform: rotate(0deg)`, which is the default state and won't produce any visual change. Typically, an expanded arrow rotates (e.g., `rotate(90deg)` or `rotate(180deg)`) to indicate the expanded state.



```diff
 .expand-arrow.expanded {
-  transform: rotate(0deg);
+  transform: rotate(90deg);
 }
mpp-vscode/test/services/diff-manager.test.ts-140-149 (1)

140-149: Test does not verify event emission behavior.

This test only verifies that the events array starts empty but doesn't trigger any action that would emit a DiffEvent. Consider either removing this test as it doesn't validate meaningful behavior, or expanding it to call methods like showDiff, acceptDiff, or cancelDiff that would emit events.

   it('should emit events on diff changes', async () => {
     const { DiffManager, DiffContentProvider } = await import('../../src/services/diff-manager');
     const contentProvider = new DiffContentProvider();
     const manager = new DiffManager(mockLog, contentProvider);
     
     const events: any[] = [];
     manager.onDidChange((event) => events.push(event));
     
-    expect(events).toHaveLength(0);
+    // TODO: Trigger an action that emits events (e.g., showDiff + acceptDiff)
+    // and verify the event payload
+    expect(events).toHaveLength(0); // Initially empty before any action
   });
mpp-vscode/webview/src/components/sketch/SketchRenderer.tsx-108-137 (1)

108-137: Missing onAction prop on placeholder renderers.

The Mermaid, DevIn, and Plan blocks use CodeBlockRenderer but don't pass the onAction callback, making Copy/Insert/Apply buttons non-functional for these block types. This inconsistency could confuse users.

Apply this diff to pass onAction consistently:

     case 'mermaid':
     case 'mmd':
       // TODO: Implement MermaidRenderer
       return (
         <CodeBlockRenderer
           code={block.text}
           language="mermaid"
           isComplete={isComplete}
+          onAction={onAction}
         />
       );

     case 'devin':
       // TODO: Implement DevInRenderer
       return (
         <CodeBlockRenderer
           code={block.text}
           language="devin"
           isComplete={isComplete}
+          onAction={onAction}
         />
       );

     case 'plan':
       // TODO: Implement PlanRenderer
       return (
         <CodeBlockRenderer
           code={block.text}
           language="plan"
           isComplete={isComplete}
+          onAction={onAction}
         />
       );
mpp-vscode/src/services/ide-server.ts-194-203 (1)

194-203: Port file not cleaned up on shutdown.

The port file written to ~/.autodev/ide-server.json is not deleted when the server stops. Stale files with old auth tokens could cause confusion or security issues for external tools.

Apply this diff to clean up the port file:

   async stop(): Promise<void> {
     if (this.server) {
       return new Promise((resolve) => {
         this.server!.close(() => {
           this.log('IDE Server stopped');
+          this.cleanupPortFile();
           resolve();
         });
       });
     }
   }
+
+  private async cleanupPortFile(): Promise<void> {
+    if (this.portFile) {
+      try {
+        await fs.unlink(this.portFile);
+        this.log(`Port file removed: ${this.portFile}`);
+      } catch {
+        // Ignore errors during cleanup
+      }
+    }
+  }

Committable suggestion skipped: line range outside the PR's diff.

mpp-vscode/src/providers/devins-completion.ts-52-61 (1)

52-61: completionManager is instantiated but never used.

The CompletionManager is created in the constructor (line 57) and stored in this.completionManager, but none of the completion methods (getCommandCompletions, getAgentCompletions, getVariableCompletions) actually use it. All methods only use the BUILTIN_* arrays.

This appears to be dead code. Either integrate the CompletionManager for dynamic completions or remove the unused field:

 export class DevInsCompletionProvider implements vscode.CompletionItemProvider {
-  private completionManager: CompletionManager | undefined;
-
-  constructor() {
-    try {
-      this.completionManager = new CompletionManager();
-    } catch (e) {
-      // mpp-core not available, use built-in completions only
-    }
-  }
+  // Future: integrate CompletionManager for dynamic completions from mpp-core

Or implement the integration:

private getCommandCompletions(linePrefix: string): vscode.CompletionItem[] {
  // If CompletionManager is available, use it for dynamic completions
  if (this.completionManager) {
    const dynamicItems = this.completionManager.getCompletions(linePrefix, linePrefix.length);
    // ... convert and return
  }
  // Fallback to built-in completions
  // ...
}
mpp-vscode/src/providers/chat-view.ts-517-526 (1)

517-526: Variable declaration inside switch case can be accessed by other cases.

The editor constant declared in the insert case can be accidentally accessed by subsequent cases. This is flagged by Biome static analysis.

       case 'insert':
-        // Insert code at cursor
-        const editor = vscode.window.activeTextEditor;
-        if (editor && data.code) {
-          await editor.edit(editBuilder => {
-            editBuilder.insert(editor.selection.active, data.code);
-          });
-        }
+        {
+          // Insert code at cursor
+          const editor = vscode.window.activeTextEditor;
+          if (editor && data.code) {
+            await editor.edit(editBuilder => {
+              editBuilder.insert(editor.selection.active, data.code);
+            });
+          }
+        }
         break;
mpp-vscode/webview/src/App.tsx-363-365 (1)

363-365: Operator precedence issue in needsConfig expression.

The expression mixes && and || without parentheses. Due to operator precedence, && binds tighter than ||, so this evaluates as:
(timeline.length === 0 && content.includes('No configuration found')) || content.includes('Configuration Required')

This may not be the intended logic. If both conditions should require an empty timeline, add parentheses.

-  const needsConfig = agentState.timeline.length === 0 &&
-    agentState.currentStreamingContent.includes('No configuration found') ||
-    agentState.currentStreamingContent.includes('Configuration Required');
+  const needsConfig = agentState.timeline.length === 0 && (
+    agentState.currentStreamingContent.includes('No configuration found') ||
+    agentState.currentStreamingContent.includes('Configuration Required')
+  );
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/AgentPlan.kt-88-99 (1)

88-99: touch() is called even when task is not found.

Both updateTaskStatus and completeStep call touch() unconditionally, even when getTask(taskId) returns null. This updates updatedAt without any actual modification.

 fun updateTaskStatus(taskId: String, status: TaskStatus) {
-    getTask(taskId)?.updateStatus(status)
-    touch()
+    getTask(taskId)?.let {
+        it.updateStatus(status)
+        touch()
+    }
 }
 
 fun completeStep(taskId: String, stepId: String) {
-    getTask(taskId)?.completeStep(stepId)
-    touch()
+    getTask(taskId)?.let {
+        it.completeStep(stepId)
+        touch()
+    }
 }
mpp-vscode/webview/src/App.tsx-118-138 (1)

118-138: Mutating timeline item directly instead of creating new object.

The toolResult handler creates a shallow copy of the timeline array but then mutates item.toolCall directly (line 126). This violates React's immutability principle and could cause issues with re-renders or component memoization.

       case 'toolResult':
         if (msg.data) {
           setAgentState(prev => {
-            const timeline = [...prev.timeline];
-            // Find the last tool call and update it
-            for (let i = timeline.length - 1; i >= 0; i--) {
-              if (timeline[i].type === 'tool_call') {
-                const item = timeline[i] as ToolCallTimelineItem;
-                item.toolCall = {
-                  ...item.toolCall,
-                  success: msg.data?.success as boolean | undefined,
-                  output: msg.data?.output as string | undefined,
-                  summary: msg.data?.summary as string | undefined
-                };
-                break;
-              }
-            }
+            const timeline = prev.timeline.map((item, i, arr) => {
+              // Find the last tool call
+              if (item.type === 'tool_call') {
+                const isLast = !arr.slice(i + 1).some(t => t.type === 'tool_call');
+                if (isLast) {
+                  return {
+                    ...item,
+                    toolCall: {
+                      ...(item as ToolCallTimelineItem).toolCall,
+                      success: msg.data?.success as boolean | undefined,
+                      output: msg.data?.output as string | undefined,
+                      summary: msg.data?.summary as string | undefined
+                    }
+                  };
+                }
+              }
+              return item;
+            });
             return { ...prev, timeline };
           });
         }
         break;
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.kt-311-324 (1)

311-324: In-place mutation before StateFlow update may cause UI inconsistencies.

The code mutates step.complete() and task.updateStatusFromSteps() in place (lines 318-319), then creates a new plan via copy() (line 321). Since _currentPlan.value still references the same plan object during mutation, observers may see intermediate states.

Consider creating immutable copies of the step and task before updating:

             "COMPLETE_STEP" -> {
                 if (taskIndex == null || stepIndex == null) return
                 _currentPlan.value?.let { plan ->
                     if (taskIndex in 1..plan.tasks.size) {
                         val task = plan.tasks[taskIndex - 1]
                         if (stepIndex in 1..task.steps.size) {
-                            val step = task.steps[stepIndex - 1]
-                            step.complete()
-                            task.updateStatusFromSteps()
-                            // Trigger recomposition by creating a new plan instance
-                            _currentPlan.value = plan.copy(updatedAt = System.currentTimeMillis())
+                            // Create updated step and task immutably
+                            val updatedStep = task.steps[stepIndex - 1].copy(status = TaskStatus.COMPLETED)
+                            val updatedSteps = task.steps.toMutableList().apply { this[stepIndex - 1] = updatedStep }
+                            // Would require PlanTask.copy with steps parameter
+                            _currentPlan.value = plan.copy(updatedAt = System.currentTimeMillis())
                         }
                     }
                 }
             }

Note: This requires PlanTask and PlanStep to support immutable copy operations with nested updates.

Committable suggestion skipped: line range outside the PR's diff.

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/PlanStep.kt-114-118 (1)

114-118: Thread-safety issue: idCounter is not synchronized.

The idCounter is a shared mutable Long that can be accessed concurrently from multiple threads. In Kotlin Multiplatform, this could lead to duplicate IDs.

For thread-safe ID generation, consider using atomicfu or including more entropy:

-        private var idCounter = 0L
-        
-        private fun generateId(): String {
-            return "step_${++idCounter}_${currentTimeMillis()}"
-        }
+        private val random = kotlin.random.Random
+        
+        private fun generateId(): String {
+            return "step_${currentTimeMillis()}_${random.nextInt(0, Int.MAX_VALUE)}"
+        }

This aligns with the pattern shown in RendererModels.kt (line 203 in snippets).

Committable suggestion skipped: line range outside the PR's diff.

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/MarkdownPlanParser.kt-85-100 (1)

85-100: Potential false negative for malformed checkbox items.

Line 90 skips items where description.startsWith("["), intended to filter malformed checkbox items. However, this also skips legitimate steps like - [link text](url) that start with a markdown link.

Consider a more precise check:

-                // Skip if it looks like a checkbox item that didn't match
-                if (description.startsWith("[")) continue
+                // Skip if it looks like an incomplete checkbox item (e.g., "[ ]" or "[x]")
+                if (description.matches(Regex("^\\[.?\\].*"))) continue
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/PlanTask.kt-149-153 (1)

149-153: Thread-safety issue with idCounter in concurrent environments.

The idCounter is a mutable Long incremented without synchronization. In a multiplatform context (especially JVM), concurrent calls to generateId() may produce duplicate IDs due to race conditions.

Consider using AtomicLong on JVM or leveraging platform-specific atomics, or rely solely on the timestamp for uniqueness:

-        private var idCounter = 0L
-
         fun generateId(): String {
-            return "task_${++idCounter}_${kotlinx.datetime.Clock.System.now().toEpochMilliseconds()}"
+            return "task_${kotlinx.datetime.Clock.System.now().toEpochMilliseconds()}_${(0..999999).random()}"
         }
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/editor/context/FileSearchPopup.kt-94-113 (1)

94-113: Debounce implementation may cause race conditions.

Each keystroke launches a new coroutine with a 150ms delay. If the user types faster than 150ms between keystrokes, multiple search requests can execute concurrently and results may arrive out of order, showing stale data.

Consider canceling the previous search job before starting a new one:

+    var searchJob by remember { mutableStateOf<kotlinx.coroutines.Job?>(null) }
+
     // Debounced search function
     fun performSearch(query: String) {
         if (query.length < 2 || currentWorkspace == null || indexingState != IndexingState.READY) {
             searchResults = emptyList()
             isLoading = false
             return
         }

         isLoading = true
+        searchJob?.cancel()
-        scope.launch {
+        searchJob = scope.launch {
             delay(150) // Debounce
             try {
                 searchResults = searchProvider.searchFiles(query)
             } catch (e: Exception) {
                 searchResults = emptyList()
             } finally {
                 isLoading = false
             }
         }
     }

*/
val currentPlan: StateFlow<AgentPlan?> = _currentPlan.asStateFlow()

private val listeners = mutableListOf<PlanUpdateListener>()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Thread safety issue in listener management.

The listeners list is a regular MutableList that can be modified (addListener/removeListener) while being iterated in notify* methods. This could cause ConcurrentModificationException in multi-threaded scenarios.

-    private val listeners = mutableListOf<PlanUpdateListener>()
+    private val listeners = java.util.concurrent.CopyOnWriteArrayList<PlanUpdateListener>()

Or use a copy for iteration:

     private fun notifyPlanCreated(plan: AgentPlan) {
-        listeners.forEach { it.onPlanCreated(plan) }
+        listeners.toList().forEach { it.onPlanCreated(plan) }
     }

Also applies to: 130-139

Comment on lines +62 to +71
fun updatePlan(tasks: List<PlanTask>) {
val plan = _currentPlan.value
if (plan != null) {
plan.tasks.clear()
plan.tasks.addAll(tasks)
notifyPlanUpdated(plan)
} else {
createPlan(tasks)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

StateFlow won't emit on direct mutation of plan.tasks.

The updatePlan method directly mutates plan.tasks (via clear() and addAll()), but this doesn't trigger a new StateFlow emission since the reference to plan hasn't changed. Observers won't be notified of the update.

     fun updatePlan(tasks: List<PlanTask>) {
         val plan = _currentPlan.value
         if (plan != null) {
-            plan.tasks.clear()
-            plan.tasks.addAll(tasks)
-            notifyPlanUpdated(plan)
+            val updatedPlan = plan.copy(
+                tasks = tasks.toMutableList(),
+                updatedAt = kotlinx.datetime.Clock.System.now().toEpochMilliseconds()
+            )
+            _currentPlan.value = updatedPlan
+            notifyPlanUpdated(updatedPlan)
         } else {
             createPlan(tasks)
         }
     }

This ensures the StateFlow emits a new value and observers are properly notified.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/PlanStateService.kt
around lines 62 to 71, the code mutates plan.tasks in place which does not
produce a new StateFlow emission; instead construct a new Plan instance (or a
copy of the existing plan) with the updated tasks list, assign it to
_currentPlan.value, and then call notifyPlanUpdated(newPlan) (or notify with the
newly assigned value) so observers receive the updated Plan reference and
StateFlow emits the change.

Comment on lines +93 to +117
fun updateTaskStatus(taskId: String, status: TaskStatus) {
val plan = _currentPlan.value ?: return
val task = plan.getTask(taskId) ?: return
task.updateStatus(status)
notifyTaskUpdated(task)
}

/**
* Complete a step within a task.
*/
fun completeStep(taskId: String, stepId: String) {
val plan = _currentPlan.value ?: return
plan.completeStep(taskId, stepId)
notifyStepCompleted(taskId, stepId)
}

/**
* Update a step's status.
*/
fun updateStepStatus(taskId: String, stepId: String, status: TaskStatus) {
val plan = _currentPlan.value ?: return
val task = plan.getTask(taskId) ?: return
task.updateStepStatus(stepId, status)
notifyTaskUpdated(task)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mutations in updateTaskStatus, completeStep, and updateStepStatus don't trigger StateFlow emissions.

These methods modify nested task/step state but don't update _currentPlan.value, so observers won't be notified. Apply the same pattern as suggested for updatePlan:

     fun completeStep(taskId: String, stepId: String) {
         val plan = _currentPlan.value ?: return
         plan.completeStep(taskId, stepId)
+        _currentPlan.value = plan.copy(updatedAt = kotlinx.datetime.Clock.System.now().toEpochMilliseconds())
         notifyStepCompleted(taskId, stepId)
     }

     fun updateStepStatus(taskId: String, stepId: String, status: TaskStatus) {
         val plan = _currentPlan.value ?: return
         val task = plan.getTask(taskId) ?: return
         task.updateStepStatus(stepId, status)
+        _currentPlan.value = plan.copy(updatedAt = kotlinx.datetime.Clock.System.now().toEpochMilliseconds())
         notifyTaskUpdated(task)
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/PlanStateService.kt
around lines 93 to 117, the methods updateTaskStatus, completeStep, and
updateStepStatus mutate nested task/step state but do not update
_currentPlan.value so StateFlow observers are not emitted to; after performing
the in-place mutation in each method, reassign _currentPlan.value = plan (or
_currentPlan.value = plan.copy() if Plan is immutable) to force a StateFlow
emission, then proceed to call the existing notification methods.

import cc.unitmesh.agent.tool.schema.ToolCategory
import kotlinx.serialization.Serializable

enum class PlanAction { CREATE, UPDATE, COMPLETE_STEP, FAIL_STEP, VIEW }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

UPDATE action defined but not exposed in schema.

The PlanAction enum includes UPDATE, and updatePlan() is implemented (lines 100-114), but the schema on line 43 only lists ["CREATE", "COMPLETE_STEP", "FAIL_STEP", "VIEW"]. The LLM won't be able to use the UPDATE action since it's not in the schema.

Either add UPDATE to the schema or remove the dead code:

         "action" to string(description = "Action: CREATE (new plan), COMPLETE_STEP (mark steps done), FAIL_STEP (mark steps failed), VIEW (show plan). Use COMPLETE_STEP to update progress - do NOT resend the full plan markdown to update progress.", required = true,
-            enum = listOf("CREATE", "COMPLETE_STEP", "FAIL_STEP", "VIEW")),
+            enum = listOf("CREATE", "UPDATE", "COMPLETE_STEP", "FAIL_STEP", "VIEW")),

Or if UPDATE is intentionally hidden, remove it from the enum and updatePlan() method.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/impl/PlanManagementTool.kt
around line 14 (enum) and lines ~43 (schema) and ~100-114 (updatePlan
implementation): the PlanAction enum contains UPDATE but the schema array on
line 43 omits it, making the UPDATE action unusable; fix by either adding
"UPDATE" to the schema array so the enum and schema match, or remove the UPDATE
enum value and delete (or comment out) the updatePlan() method to eliminate dead
code, keeping both enum and schema consistent.

Comment on lines +62 to +69
val TaskStatus.planColor: Color
get() = when (this) {
TaskStatus.TODO -> Color(0xFF9E9E9E)
TaskStatus.IN_PROGRESS -> Color(0xFF2196F3)
TaskStatus.COMPLETED -> Color(0xFF4CAF50)
TaskStatus.FAILED -> Color(0xFFF44336)
TaskStatus.BLOCKED -> Color(0xFFFF9800)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded colors violate coding guidelines.

The planColor extension uses hardcoded Color(0xFF...) values. As per coding guidelines: "DO NOT hardcode colors... Always use design tokens for consistency across platforms."

Replace with MaterialTheme.colorScheme tokens:

 val TaskStatus.planColor: Color
+    @Composable
     get() = when (this) {
-        TaskStatus.TODO -> Color(0xFF9E9E9E)
-        TaskStatus.IN_PROGRESS -> Color(0xFF2196F3)
-        TaskStatus.COMPLETED -> Color(0xFF4CAF50)
-        TaskStatus.FAILED -> Color(0xFFF44336)
-        TaskStatus.BLOCKED -> Color(0xFFFF9800)
+        TaskStatus.TODO -> MaterialTheme.colorScheme.outline
+        TaskStatus.IN_PROGRESS -> MaterialTheme.colorScheme.primary
+        TaskStatus.COMPLETED -> MaterialTheme.colorScheme.tertiary  // or a success semantic color
+        TaskStatus.FAILED -> MaterialTheme.colorScheme.error
+        TaskStatus.BLOCKED -> MaterialTheme.colorScheme.secondary   // or a warning semantic color
     }

Note: This will require updating usages to be within @Composable context, which they already are.

Comment on lines +90 to +105
isValid(): boolean {
const active = this.getActiveConfig();
if (!active) return false;

// Ollama doesn't require API key
if (active.provider === 'ollama') {
return !!active.model;
}

// Custom OpenAI-compatible providers require baseUrl, apiKey, and model
if (active.provider === 'custom-openai-base') {
return !!active.baseUrl && !!active.apiKey && !!active.model;
}

return !!active.provider && !!active.apiKey && !!active.model;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the LLMProvider type definition
rg "type LLMProvider|interface LLMProvider|enum LLMProvider" --type ts --type tsx

Repository: unit-mesh/auto-dev

Length of output: 89


🏁 Script executed:

# Search for how qwen, kimi, and glm are configured in the codebase
rg "qwen|kimi|glm" -i --type ts --type tsx -A 3 -B 3

Repository: unit-mesh/auto-dev

Length of output: 89


🏁 Script executed:

# Check the config-manager.ts file to understand all provider handling
cat -n mpp-vscode/src/services/config-manager.ts | head -150

Repository: unit-mesh/auto-dev

Length of output: 135


🏁 Script executed:

# Find the config-manager.ts file
fd "config-manager" --type f

Repository: unit-mesh/auto-dev

Length of output: 44


🏁 Script executed:

# Check the repository structure
ls -la | head -20

Repository: unit-mesh/auto-dev

Length of output: 1223


🏁 Script executed:

# Search for LLMProvider type with correct flags
rg "type LLMProvider|interface LLMProvider|enum LLMProvider" -t ts

Repository: unit-mesh/auto-dev

Length of output: 263


🏁 Script executed:

# Find isValid() method in the codebase
rg "isValid\(\)" -A 20 -B 2

Repository: unit-mesh/auto-dev

Length of output: 50374


🏁 Script executed:

# Find all config-manager related files
fd "config-manager|ConfigManager" --type f

Repository: unit-mesh/auto-dev

Length of output: 661


🏁 Script executed:

# Check the ConfigManager.ts file for isValid() and provider handling
rg "qwen|kimi|glm" -A 5 -B 5

Repository: unit-mesh/auto-dev

Length of output: 50374


🌐 Web query:

Qwen LLM API baseUrl custom endpoint requirements

💡 Result:

Summary of requirements for using a custom/baseUrl with Qwen (OpenAI‑compatible endpoints)

  • Base URL must point at the Qwen-compatible API root (OpenAI-compatible path usually ends with /v1 or /compatible-mode/v1). Example regional base URLs: Singapore (international) https://dashscope-intl.aliyuncs.com/compatible-mode/v1 and Beijing (China) https://dashscope.aliyuncs.com/compatible-mode/v1. [1][2]

  • When using an OpenAI SDK set the SDK’s api_base/api_base_url to that base URL so the SDK will call e.g. {BASE_URL}/chat/completions. For raw HTTP calls use the full endpoint (POST {BASE_URL}/chat/completions). [1][2]

  • Authentication: include the Qwen API key (usually sk-... style) in the Authorization header (Bearer) / SDK api_key; keys are region‑tied so use the matching base URL for your key. [2][3]

  • Model parameter: supply the Qwen model name in the model field (e.g., "qwen-plus", "qwen-turbo" or Qwen3 model ids). Some client integrations expect a model_server or api_base config field for custom servers. [0][4]

  • If using a third‑party/custom server that implements an OpenAI‑compatible interface (vLLM, SGLang, etc.), ensure it exposes the same request/response shapes (chat/completions or chat responses) and the /v1 path; some tooling (Qwen-Agent, liteLLM, etc.) expects model_server/model name conventions (e.g., set api_base/model_server and sometimes prefix model with "custom/"). Validate your custom endpoint returns OpenAI‑compatible JSON fields (id, model, choices, usage) or the adapter will fail. [0][3][4]

Sources

  • Alibaba Cloud Model Studio — Qwen OpenAI‑compatible usage and base URLs. [1]
  • Alibaba Cloud Model Studio — Qwen API details (regional base_url and endpoints). [2]
  • Qwen / Qwen‑Agent examples (custom model_server / api_base usage). [0]
  • liteLLM custom-provider docs (example expectations when using custom OpenAI‑compatible endpoints). [3]

If you want, tell me which SDK or client you plan to use (OpenAI SDK, Qwen Agent, liteLLM, or a custom server) and I’ll give exact example code and any adapter settings.


🌐 Web query:

Kimi LLM API baseUrl configuration requirements

💡 Result:

Short checklist for configuring Kimi (Moonshot) LLM API baseUrl

  • Use the Kimi/Moonshot API base endpoint (recommended): https://api.moonshot.ai/v1 or https://api.moonshot.cn/v1 (service-specific). [1][6]
  • Must be an HTTPS URL with a valid TLS certificate (same format as OpenAI-compatible endpoints). [1][2]
  • Point your client’s “base_url” / “baseURL” / “api_base” config to that URL (include the /v1 path if your client expects it). Examples: client.base_url="https://api.moonshot.ai/v1". [1][2][3]
  • Keep OpenAI-compatible request paths (e.g., /chat/completions) — most OpenAI SDKs work by only changing baseUrl + API key. [1][3]
  • Supply API key via Authorization: Bearer or provider-specific config (env var like MOONSHOT_API_KEY) per SDK. [3][2]
  • If calling from a browser, ensure the endpoint supports CORS or route through a backend proxy. [4]
  • If using an SDK option name, check package-specific field: base_url / baseURL / api_base / WithBaseURL etc. (names vary by client). [2][3][5]

Sources: Kimi API docs and examples, plus community SDK docs. [1][2][3][4][5]


🏁 Script executed:

# View the actual isValid() implementation in ConfigManager.ts
cat -n mpp-ui/src/jsMain/typescript/config/ConfigManager.ts | sed -n '90,110p'

Repository: unit-mesh/auto-dev

Length of output: 927


🏁 Script executed:

# Search for isValid() method in ConfigManager.ts
grep -n "isValid" mpp-ui/src/jsMain/typescript/config/ConfigManager.ts

Repository: unit-mesh/auto-dev

Length of output: 88


🏁 Script executed:

# View the isValid() method
cat -n mpp-ui/src/jsMain/typescript/config/ConfigManager.ts | sed -n '279,300p'

Repository: unit-mesh/auto-dev

Length of output: 839


🏁 Script executed:

# Verify the provider configs one more time for clarity
rg "glm|qwen|kimi" mpp-ui/src/jsMain/typescript/ui/ModelConfigForm.tsx -A 1 -B 1 | head -20

Repository: unit-mesh/auto-dev

Length of output: 1005


Add baseUrl validation for glm, qwen, and kimi providers.

The isValid() method only enforces baseUrl validation for custom-openai-base, but glm, qwen, and kimi are OpenAI-compatible providers that also require baseUrl configuration (as confirmed by ModelConfigForm.tsx and config.yaml.example). These providers should have the same validation logic as custom-openai-base to prevent invalid configurations from passing validation.

🤖 Prompt for AI Agents
In mpp-vscode/src/services/config-manager.ts around lines 90 to 105, the
isValid() method currently only requires baseUrl for 'custom-openai-base' but
must also require baseUrl for the 'glm', 'qwen', and 'kimi' providers; update
the validation so that for providers 'custom-openai-base', 'glm', 'qwen', and
'kimi' you check that active.baseUrl, active.apiKey, and active.model are all
truthy, and keep the existing special-case for 'ollama' (only model required)
and the fallback for other providers unchanged.

Comment on lines +140 to +156
const rightDoc = await vscode.workspace.openTextDocument(rightDocUri);
const modifiedContent = rightDoc.getText();

// Write the content to the original file
const fileUri = vscode.Uri.file(diffInfo.originalFilePath);
await vscode.workspace.fs.writeFile(fileUri, Buffer.from(modifiedContent, 'utf8'));

await this.closeDiffEditor(rightDocUri);

this.onDidChangeEmitter.fire({
type: 'accepted',
filePath: diffInfo.originalFilePath,
content: modifiedContent
});

this.log(`Accepted diff for: ${diffInfo.originalFilePath}`);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing error handling for file write operation.

The acceptDiff method writes to the file system (line 145) without try-catch. If the write fails (permissions, disk full, etc.), the error propagates unhandled, and the diff state becomes inconsistent—the editor is closed but the file isn't updated.

   async acceptDiff(rightDocUri: vscode.Uri): Promise<void> {
     const diffInfo = this.diffDocuments.get(rightDocUri.toString());
     if (!diffInfo) {
       return;
     }

     const rightDoc = await vscode.workspace.openTextDocument(rightDocUri);
     const modifiedContent = rightDoc.getText();
     
     // Write the content to the original file
     const fileUri = vscode.Uri.file(diffInfo.originalFilePath);
+    try {
       await vscode.workspace.fs.writeFile(fileUri, Buffer.from(modifiedContent, 'utf8'));
+    } catch (error) {
+      this.log(`Failed to write diff for ${diffInfo.originalFilePath}: ${error}`);
+      throw error; // Re-throw to let caller handle
+    }
     
     await this.closeDiffEditor(rightDocUri);
📝 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.

Suggested change
const rightDoc = await vscode.workspace.openTextDocument(rightDocUri);
const modifiedContent = rightDoc.getText();
// Write the content to the original file
const fileUri = vscode.Uri.file(diffInfo.originalFilePath);
await vscode.workspace.fs.writeFile(fileUri, Buffer.from(modifiedContent, 'utf8'));
await this.closeDiffEditor(rightDocUri);
this.onDidChangeEmitter.fire({
type: 'accepted',
filePath: diffInfo.originalFilePath,
content: modifiedContent
});
this.log(`Accepted diff for: ${diffInfo.originalFilePath}`);
}
const rightDoc = await vscode.workspace.openTextDocument(rightDocUri);
const modifiedContent = rightDoc.getText();
// Write the content to the original file
const fileUri = vscode.Uri.file(diffInfo.originalFilePath);
try {
await vscode.workspace.fs.writeFile(fileUri, Buffer.from(modifiedContent, 'utf8'));
} catch (error) {
this.log(`Failed to write diff for ${diffInfo.originalFilePath}: ${error}`);
throw error; // Re-throw to let caller handle
}
await this.closeDiffEditor(rightDocUri);
this.onDidChangeEmitter.fire({
type: 'accepted',
filePath: diffInfo.originalFilePath,
content: modifiedContent
});
this.log(`Accepted diff for: ${diffInfo.originalFilePath}`);
}
🤖 Prompt for AI Agents
In mpp-vscode/src/services/diff-manager.ts around lines 140 to 156, the code
writes modifiedContent to disk without error handling which can leave the editor
closed while the file wasn't updated; wrap the writeFile (and subsequent
close/editor state changes and event firing) in a try/catch so the sequence is:
attempt vscode.workspace.fs.writeFile(fileUri,
Buffer.from(modifiedContent,'utf8')) inside try, only on success call
closeDiffEditor, fire onDidChangeEmitter and log; in catch log the error, show a
user-facing message (vscode.window.showErrorMessage) explaining the save failed,
and avoid closing or mark the diff as unaccepted so state remains consistent;
ensure the catch rethrows or returns appropriately so callers can handle
failures if needed.

Comment on lines +139 to +153
app.post('/file/read', async (req: Request, res: Response) => {
try {
const { filePath } = req.body;
if (!filePath) {
return res.status(400).json({ error: 'filePath is required' });
}

const uri = vscode.Uri.file(filePath);
const content = await vscode.workspace.fs.readFile(uri);
res.json({ success: true, content: Buffer.from(content).toString('utf8') });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
res.status(500).json({ error: message });
}
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Path traversal vulnerability in file read endpoint.

The /file/read endpoint accepts arbitrary filePath without validation, allowing potential access to files outside the workspace (e.g., /etc/passwd, ~/.ssh/). Even with auth token protection, this is a security risk if the token is compromised.

Apply this diff to validate paths are within workspace bounds:

     app.post('/file/read', async (req: Request, res: Response) => {
       try {
         const { filePath } = req.body;
         if (!filePath) {
           return res.status(400).json({ error: 'filePath is required' });
         }
+        
+        // Validate path is within workspace
+        const workspaceFolders = vscode.workspace.workspaceFolders;
+        if (!workspaceFolders?.some(f => filePath.startsWith(f.uri.fsPath))) {
+          return res.status(403).json({ error: 'Path outside workspace' });
+        }
         
         const uri = vscode.Uri.file(filePath);
         const content = await vscode.workspace.fs.readFile(uri);
🤖 Prompt for AI Agents
In mpp-vscode/src/services/ide-server.ts around lines 139–153, the endpoint
reads an arbitrary filePath which allows path traversal; validate that the
requested file is inside one of the opened workspace folders before reading. Fix
by: obtain workspace folder root(s) (vscode.workspace.workspaceFolders), use
Node's path.resolve/normalize against each workspace folder path to compute the
absolute target path, then compare the resolved target to the resolved workspace
root(s) using a startsWith check (or path.relative and ensure it does not start
with '..'); additionally resolve symlinks (fs.promises.realpath) for both root
and target to prevent symlink escape, and return 400/403 if the file is outside
workspace or workspaceFolders is undefined; only then call vscode.Uri.file and
vscode.workspace.fs.readFile.

Comment on lines +155 to +170
// Write file endpoint
app.post('/file/write', async (req: Request, res: Response) => {
try {
const { filePath, content } = req.body;
if (!filePath || content === undefined) {
return res.status(400).json({ error: 'filePath and content are required' });
}

const uri = vscode.Uri.file(filePath);
await vscode.workspace.fs.writeFile(uri, Buffer.from(content, 'utf8'));
res.json({ success: true });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
res.status(500).json({ error: message });
}
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Path traversal vulnerability in file write endpoint.

Same issue as /file/read — arbitrary file paths allow writing outside the workspace, which is more dangerous as it enables arbitrary file modification.

Apply this diff to validate paths:

     app.post('/file/write', async (req: Request, res: Response) => {
       try {
         const { filePath, content } = req.body;
         if (!filePath || content === undefined) {
           return res.status(400).json({ error: 'filePath and content are required' });
         }
+
+        // Validate path is within workspace
+        const workspaceFolders = vscode.workspace.workspaceFolders;
+        if (!workspaceFolders?.some(f => filePath.startsWith(f.uri.fsPath))) {
+          return res.status(403).json({ error: 'Path outside workspace' });
+        }

         const uri = vscode.Uri.file(filePath);
📝 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.

Suggested change
// Write file endpoint
app.post('/file/write', async (req: Request, res: Response) => {
try {
const { filePath, content } = req.body;
if (!filePath || content === undefined) {
return res.status(400).json({ error: 'filePath and content are required' });
}
const uri = vscode.Uri.file(filePath);
await vscode.workspace.fs.writeFile(uri, Buffer.from(content, 'utf8'));
res.json({ success: true });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
res.status(500).json({ error: message });
}
});
// Write file endpoint
app.post('/file/write', async (req: Request, res: Response) => {
try {
const { filePath, content } = req.body;
if (!filePath || content === undefined) {
return res.status(400).json({ error: 'filePath and content are required' });
}
// Validate path is within workspace
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders?.some(f => filePath.startsWith(f.uri.fsPath))) {
return res.status(403).json({ error: 'Path outside workspace' });
}
const uri = vscode.Uri.file(filePath);
await vscode.workspace.fs.writeFile(uri, Buffer.from(content, 'utf8'));
res.json({ success: true });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
res.status(500).json({ error: message });
}
});

"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^9.0.1",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

react-markdown 9.0.1 stability compatibility 2025

💡 Result:

Short answer: react-markdown@9.0.1 (published Nov 13, 2023) is a stable release but is older and may lack compatibility with newer ecosystems in 2025 (notably React 19 typings and newer major release features). (releasealert.dev)

Details:

  • Release & stability: 9.0.1 is tagged stable (Nov 13, 2023). (releasealert.dev)
  • Node / bundlers: v9 is ESM-only and intended to work on maintained Node.js versions (Node 16+ recommended). (npmjs.com)
  • React 19 / types: upstream issue tracking React 19 compatibility (JSX global / @types/react changes) — react-markdown has active discussion and later 10.x releases add more modern compatibility. If you target React 19, prefer v10 (or check that typings are patched). (github.com)
  • Security/health: No direct vulnerabilities reported for 9.0.1 in Snyk; nevertheless, using the latest maintained release is recommended for fixes and compatibility. (security.snyk.io)

Recommendation: For new projects in 2025 use the latest 10.x release (or at least test 9.0.1 against your React/TypeScript toolchain). If you must stay on 9.0.1, verify TypeScript/@types/react compatibility (React 19 issues) and Node/ESM build pipelines. (github.com)

Citations:


🌐 Web query:

react-markdown remark-gfm compatibility issues 2024 2025

💡 Result:

Summary of known compatibility issues (2024–2025) and how to handle them:

  • remark-gfm v4 (released 2023) introduced breaking changes (ESM-only and AST changes) that caused table rendering errors and incompatibility with older react-markdown setups; many users reverted to remark-gfm@3.x or adjusted rehype/remark options. [1][2][3]

  • remark-gfm is ESM-only (requires Node 16+); that change can break toolchains or CommonJS projects and plugins expecting CJS. Align your bundler/node target or use older remark-gfm if you must stay CJS. [6][2]

  • react-markdown’s types and peer-deps have tripped users upgrading to React 18.3+/React 19 because @types/react/@jsx typing changes removed global JSX; react-markdown maintainers tracked React 19 support in issues (type/defaultProps/peerDep friction). Expect to need updated react-markdown releases or adjusted TS config/@types. [1][4]

  • Many concrete breakages arise from mismatched major versions across react-markdown, remark-gfm, remark-rehype/rehype-raw, and rehype plugins. The usual fix: align compatible majors (either downgrade remark-gfm to 3.x for older react-markdown, or upgrade react-markdown and all remark/rehype plugins to ESM-compatible/latest). Examples and discussion on Stack Overflow and community threads. [2][3]

  • Practical checklist:

    • Check react-markdown and remark-gfm release notes and peerDependencies for your project’s React and Node versions. [4][6]
    • If seeing table/“inTable” errors, try remark-gfm@3.0.1 with react-markdown v8, or upgrade react-markdown + plugins to versions that explicitly support remark-gfm v4/ESM. [2][3]
    • Ensure your build (Next/webpack/rollup) supports ESM dependencies, or keep plugin versions that remain CJS-compatible. [6][2]

Sources:
[1] react-markdown GitHub issue — React 19 support.
[2] Stack Overflow thread on react-markdown + remark-gfm (table errors).
[3] Blog post: “remark-gfm v4.0.0 appears to break table-rendering.”
[4] react-markdown GitHub repo (project/compatibility notes).
[6] remark-gfm GitHub README (ESM-only / install notes).


Consider upgrading to react-markdown 10.x for better long-term compatibility.

You're using react-markdown@9.0.1 (Nov 2023) paired with remark-gfm@4.0.0. While 9.0.1 is stable, it's becoming outdated for new projects. remark-gfm v4 introduced breaking changes and has known table rendering issues with certain react-markdown configurations. For a new webview project in 2025, upgrading to react-markdown 10.x (the latest maintained release) provides better ESM compatibility, fixes, and forward compatibility. If you must stay on 9.0.1, test the markdown rendering thoroughly—particularly tables and complex formatting—to ensure remark-gfm 4.0.0 works as expected in your specific use case.

@phodal phodal merged commit 3d9ec96 into unit-mesh:master Dec 3, 2025
3 of 4 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
mpp-vscode/src/providers/chat-view.ts (2)

32-54: Resource leak: editorChangeDisposable is never disposed.

The editorChangeDisposable created at line 49 is never cleaned up, causing a memory leak. When the webview is disposed, this listener remains active.

Apply this diff to dispose the listener when the webview is disposed:

   async resolveWebviewView(
     webviewView: vscode.WebviewView,
     _context: vscode.WebviewViewResolveContext,
     _token: vscode.CancellationToken
   ): Promise<void> {
     this.webviewView = webviewView;

     webviewView.webview.options = {
       enableScripts: true,
       localResourceRoots: [
         vscode.Uri.joinPath(this.context.extensionUri, 'dist', 'webview')
       ]
     };

     webviewView.webview.html = this.getHtmlContent(webviewView.webview);
+
+    // Dispose editor change listener when webview is disposed
+    webviewView.onDidDispose(() => {
+      this.editorChangeDisposable?.dispose();
+    });

776-790: Security risk: Absolute paths allow reading files outside workspace.

The code accepts absolute paths and converts them to vscode.Uri without validating they're within the workspace. This allows the webview to read arbitrary files on the system.

Apply this diff to restrict file access to workspace paths:

     try {
       // Convert relative path to absolute if needed
       let uri: vscode.Uri;
       if (filePath.startsWith('/') || filePath.match(/^[a-zA-Z]:\\/)) {
-        // Already absolute path
-        uri = vscode.Uri.file(filePath);
+        // Absolute path - validate it's within workspace
+        const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
+        if (!workspaceFolder) {
+          this.postMessage({ type: 'fileContentResult', data: { content: null, error: 'No workspace folder' } });
+          return;
+        }
+        uri = vscode.Uri.file(filePath);
+        // Ensure the path is within the workspace
+        const normalizedUri = uri.fsPath.toLowerCase();
+        const normalizedWorkspace = workspaceFolder.uri.fsPath.toLowerCase();
+        if (!normalizedUri.startsWith(normalizedWorkspace)) {
+          this.postMessage({ type: 'fileContentResult', data: { content: null, error: 'Path outside workspace' } });
+          return;
+        }
       } else {
mpp-vscode/src/bridge/mpp-core.ts (2)

25-33: Incomplete provider type mappings.

The ProviderTypes mapping is missing entries for 'glm', 'qwen', and 'kimi' which are supported in the Kotlin layer but not mapped here. This will cause errors when users try to use these providers.

Apply this diff to add the missing mappings:

 export const ProviderTypes: Record<string, string> = {
   'openai': 'OPENAI',
   'anthropic': 'ANTHROPIC',
   'google': 'GOOGLE',
   'deepseek': 'DEEPSEEK',
   'ollama': 'OLLAMA',
   'openrouter': 'OPENROUTER',
-  'custom-openai-base': 'CUSTOM_OPENAI_BASE'
+  'custom-openai-base': 'CUSTOM_OPENAI_BASE',
+  'glm': 'GLM',
+  'qwen': 'QWEN',
+  'kimi': 'KIMI'
 };

92-107: Error in callback won't reject the Promise.

The error callback at line 100 throws an error, but this executes asynchronously inside the Kotlin callback and won't propagate to the caller. The Promise will resolve successfully even when an error occurs.

Apply this diff to properly capture and propagate errors:

   async streamMessage(
     message: string,
     onChunk: (chunk: string) => void
   ): Promise<string> {
     this.chatHistory.push({ role: 'user', content: message });

     const historyMessages = this.chatHistory.slice(0, -1).map(msg =>
       new JsMessage(msg.role, msg.content)
     );

     let fullResponse = '';
+    let streamError: Error | null = null;

     await this.koogService.streamPrompt(
       message,
       historyMessages,
       (chunk: string) => {
         fullResponse += chunk;
         onChunk(chunk);
       },
       (error: any) => {
-        throw new Error(`LLM Error: ${error.message || error}`);
+        streamError = new Error(`LLM Error: ${error.message || error}`);
       },
       () => { /* complete */ }
     );

+    if (streamError) {
+      throw streamError;
+    }
+
     this.chatHistory.push({ role: 'assistant', content: fullResponse });
     return fullResponse;
   }
🧹 Nitpick comments (1)
mpp-vscode/src/providers/chat-view.ts (1)

69-71: Extract binary extensions to a constant to avoid duplication.

The binary extensions array is duplicated at lines 69 and 679. Extract this to a class-level constant for maintainability.

Apply this diff:

 export class ChatViewProvider implements vscode.WebviewViewProvider {
+  private static readonly BINARY_EXTENSIONS = [
+    'jar', 'class', 'exe', 'dll', 'so', 'dylib', 
+    'png', 'jpg', 'jpeg', 'gif', 'ico', 'pdf', 
+    'zip', 'tar', 'gz', 'rar', '7z'
+  ];
+
   private webviewView: vscode.WebviewView | undefined;

Then use ChatViewProvider.BINARY_EXTENSIONS at both locations.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aceaf34 and 1f09174.

📒 Files selected for processing (2)
  • mpp-vscode/src/bridge/mpp-core.ts (1 hunks)
  • mpp-vscode/src/providers/chat-view.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

DO NOT hardcode colors (e.g., Color(0xFF...) or #hex). Always use design tokens for consistency across platforms

Files:

  • mpp-vscode/src/bridge/mpp-core.ts
  • mpp-vscode/src/providers/chat-view.ts
🧠 Learnings (2)
📚 Learning: 2025-12-01T15:41:32.195Z
Learnt from: CR
Repo: unit-mesh/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-01T15:41:32.195Z
Learning: Applies to mpp-ui/src/jsMain/typescript/**/*.ts : In CLI/TUI (TypeScript): Use `mpp-ui/src/jsMain/typescript/design-system/` and import `semanticInk` / `semanticChalk`

Applied to files:

  • mpp-vscode/src/bridge/mpp-core.ts
📚 Learning: 2025-12-01T15:41:32.195Z
Learnt from: CR
Repo: unit-mesh/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-01T15:41:32.195Z
Learning: Applies to **/*.kt : In Kotlin/JS JsExport: Avoid `Flow`, use `Promise` instead

Applied to files:

  • mpp-vscode/src/bridge/mpp-core.ts
🧬 Code graph analysis (1)
mpp-vscode/src/providers/chat-view.ts (1)
mpp-vscode/src/services/config-manager.ts (3)
  • AutoDevConfigWrapper (56-106)
  • ConfigManager (111-176)
  • LLMConfig (18-26)
🪛 Biome (2.1.2)
mpp-vscode/src/providers/chat-view.ts

[error] 520-521: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test mpp-core (KMP)
  • GitHub Check: Build (223)
  • GitHub Check: Build (241)

Comment on lines +518 to +526
case 'insert':
// Insert code at cursor
const editor = vscode.window.activeTextEditor;
if (editor && data.code) {
await editor.edit(editBuilder => {
editBuilder.insert(editor.selection.active, data.code);
});
}
break;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix variable scoping in switch statement.

The variable editor at line 520 can be accessed by other switch cases, which Biome flags as an error. Wrap the case in a block.

Apply this diff:

     switch (action) {
-      case 'insert':
+      case 'insert': {
         // Insert code at cursor
         const editor = vscode.window.activeTextEditor;
         if (editor && data.code) {
           await editor.edit(editBuilder => {
             editBuilder.insert(editor.selection.active, data.code);
           });
         }
         break;
+      }
📝 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.

Suggested change
case 'insert':
// Insert code at cursor
const editor = vscode.window.activeTextEditor;
if (editor && data.code) {
await editor.edit(editBuilder => {
editBuilder.insert(editor.selection.active, data.code);
});
}
break;
case 'insert': {
// Insert code at cursor
const editor = vscode.window.activeTextEditor;
if (editor && data.code) {
await editor.edit(editBuilder => {
editBuilder.insert(editor.selection.active, data.code);
});
}
break;
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 520-521: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
In mpp-vscode/src/providers/chat-view.ts around lines 518 to 526, the variable
`editor` declared in the 'insert' switch case has function-level scope and is
visible to other cases; wrap the case body in its own block by adding an opening
brace immediately after `case 'insert':` and a closing brace just before
`break;`, then move the `const editor = ...` and the `if (editor && data.code) {
... }` logic inside that block (preserve the await editor.edit call and the
break); adjust indentation accordingly so `editor` is scoped to the block only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant