Skip to content

Conversation

@phodal
Copy link
Member

@phodal phodal commented Dec 3, 2025

Summary

Implements Issue #33 - Enhanced Chat Input for VSCode Extension

Changes

Phase 1: Enhanced Input Area with File Context

  • FileChip.tsx/css - File tag component with remove button
  • FileSearchPopup.tsx/css - File search popup with fuzzy search
  • TopToolbar.tsx/css - Top toolbar for file context management
  • Extension API: handleSearchFiles, handleGetRecentFiles, handleReadFileContent

Phase 2: DevIn Language Support

  • DevInInput.tsx/css - Input with syntax highlighting
    • Highlights /command:, @agent, $variable syntax
  • CompletionPopup.tsx/css - Auto-completion popup
    • Uses mpp-core's JsCompletionManager for completions
    • Keyboard navigation support (↑↓ Enter Escape)

Phase 3: Bottom Toolbar Enhancement

  • Token usage display
  • Prompt optimization button (uses LLM to enhance prompts)

Phase 4: Context Manager

  • Auto-add current editor file to context
  • Context indicator showing auto-add status
  • Editor change event monitoring

Phase 5: MCP Configuration

  • MCP config button opens ~/.autodev/mcp.json

Technical Details

  • Integrated mpp-core's JsCompletionManager for DevIn command completions
  • Message-based communication between extension and webview for completions
  • Fixed popup positioning issues by removing overflow: hidden from container

Acceptance Criteria

  • File context can be added via search popup
  • Selected files are displayed as removable chips
  • DevIn commands have syntax highlighting in input
  • Auto-completion works for /, @, $ triggers
  • Enter submits, Shift+Enter adds newline
  • Model selector works with saved configurations
  • Prompt optimization feature available
  • Token usage displayed when available

Closes #33


Pull Request opened by Augment Code with guidance from the PR author

Summary by CodeRabbit

Release Notes

  • New Features
    • Added code completion suggestions with keyboard navigation and visual popup.
    • Introduced file context management toolbar for selecting and managing files.
    • Added prompt optimization/enhancement feature.
    • Added active file tracking and display.
    • Added token usage counter.
    • Added file search and recent files functionality.
    • Added MCP configuration management interface.

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

phodal added 12 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)
- 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
Copilot AI review requested due to automatic review settings December 3, 2025 04:57
@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

This PR implements DevIn language input support, file context management, and code completion features for the VSCode extension. It adds new UI components (DevInInput with syntax highlighting, CompletionPopup, TopToolbar, FileSearchPopup, FileChip), extends the ChatViewProvider with completion, file search, and MCP configuration handlers, and updates the message protocol between extension and webview to support these features.

Changes

Cohort / File(s) Summary
Backend Message Routing & Completion
mpp-vscode/src/providers/chat-view.ts
Adds JsCompletionManager integration with initialization logging; implements sendActiveFileUpdate/sendCurrentActiveFile for tracking editor state; adds handlers for searchFiles, getRecentFiles, readFileContent, requestConfig, getActiveFile, getCompletions, applyCompletion; introduces openMcpConfig and handlePromptOptimize utilities; routes completion operations via completionManager.
Message Protocol Extensions
mpp-vscode/webview/src/hooks/useVSCode.ts
Extends ExtensionMessage type with tokenUpdate, promptOptimized, promptOptimizeFailed, searchFilesResult, recentFilesResult, fileContentResult, activeFileChanged, completionsResult, completionApplied; expands WebviewMessage with searchFiles, getRecentFiles, readFileContent, requestConfig, getActiveFile, getCompletions, applyCompletion.
Core Webview Integration
mpp-vscode/webview/src/App.tsx
Adds state for completionItems, completionResult, totalTokens, and activeFile; extends message handling for tokenUpdate, activeFileChanged, completionsResult, completionApplied; requests initial config on mount; passes new completion and file-context props to ChatInput.
Input Component with Syntax Highlighting
mpp-vscode/webview/src/components/DevInInput.tsx, DevInInput.css
Implements textarea with real-time overlay syntax highlighting for DevIn constructs (/, @, $); auto-resizes textarea, detects completion triggers, syncs scroll between textarea and highlight overlay.
Completion Popup UI
mpp-vscode/webview/src/components/CompletionPopup.tsx, CompletionPopup.css
Exports CompletionItem interface and renders an interactive popup list of completions with keyboard navigation (ArrowUp/Down, Enter/Tab to select, Escape to close); supports click/hover selection and displays icon, label, and description per item.
File Context Management
mpp-vscode/webview/src/components/FileChip.tsx, FileChip.css
mpp-vscode/webview/src/components/FileSearchPopup.tsx, FileSearchPopup.css
mpp-vscode/webview/src/components/TopToolbar.tsx, TopToolbar.css
Introduces FileChip/FileChipExpanded for removable file display, FileSearchPopup for searchable file selection with debounced search and keyboard nav, TopToolbar for compact file row display with add/remove/clear actions and ContextIndicator for auto-add-current-file toggle.
Enhanced Chat Input
mpp-vscode/webview/src/components/ChatInput.tsx, ChatInput.css
Extends ChatInput to integrate TopToolbar, DevInInput, CompletionPopup, and file context management; adds handlers for completion lifecycle (onGetCompletions, onApplyCompletion), prompt optimization (onPromptOptimize), and MCP config (onMcpConfigClick); updates submit flow to pass optional SelectedFile[] when files are selected; replaces simple textarea with DevInInput and adds bottom toolbar with token indicator and enhancement button.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Webview as Webview<br/>(ChatInput)
    participant Extension as Extension<br/>(ChatViewProvider)
    participant CompMgr as Completion<br/>Manager
    
    User->>Webview: Types "/" character
    activate Webview
    Webview->>Webview: Detect trigger, get position
    Webview->>Extension: postMessage({ type: 'getCompletions', text, cursorPosition })
    deactivate Webview
    
    activate Extension
    Extension->>CompMgr: getCompletions(text, position)
    activate CompMgr
    CompMgr-->>Extension: [CompletionItem, ...]
    deactivate CompMgr
    Extension->>Webview: postMessage({ type: 'completionsResult', items })
    deactivate Extension
    
    activate Webview
    Webview->>Webview: Update completionItems state
    Webview->>Webview: Render CompletionPopup
    Webview->>Webview: Show popup with items
    deactivate Webview
    
    User->>Webview: Navigate with ArrowUp/Down + Enter
    activate Webview
    Webview->>Extension: postMessage({ type: 'applyCompletion', text, cursorPosition, completionIndex })
    deactivate Webview
    
    activate Extension
    Extension->>CompMgr: applyCompletion(text, index)
    activate CompMgr
    CompMgr-->>Extension: { newText, newCursorPosition }
    deactivate CompMgr
    Extension->>Webview: postMessage({ type: 'completionApplied', newText, newCursorPosition })
    deactivate Extension
    
    activate Webview
    Webview->>Webview: Update input with newText
    Webview->>Webview: Set cursor position
    Webview->>Webview: Close CompletionPopup
    deactivate Webview
Loading
sequenceDiagram
    participant User
    participant Webview as Webview<br/>(TopToolbar)
    participant Extension as Extension<br/>(ChatViewProvider)
    
    User->>Webview: Click "Add context" button
    activate Webview
    Webview->>Webview: Open FileSearchPopup
    Webview->>Extension: postMessage({ type: 'getRecentFiles' })
    deactivate Webview
    
    activate Extension
    Extension->>Extension: Get recent files list
    Extension->>Webview: postMessage({ type: 'recentFilesResult', files })
    deactivate Extension
    
    activate Webview
    Webview->>Webview: Display recent files in popup
    User->>Webview: Type search query (e.g., "component")
    deactivate Webview
    
    activate Webview
    Webview->>Webview: Debounce 150ms
    Webview->>Extension: postMessage({ type: 'searchFiles', query: 'component' })
    deactivate Webview
    
    activate Extension
    Extension->>Extension: Search workspace, filter binaries
    Extension->>Webview: postMessage({ type: 'searchFilesResult', files, folders })
    deactivate Extension
    
    activate Webview
    Webview->>Webview: Update popup results
    User->>Webview: Select file (click or Enter)
    Webview->>Webview: Add FileChip to TopToolbar
    Webview->>Webview: Close FileSearchPopup
    deactivate Webview
Loading
sequenceDiagram
    participant User
    participant Webview as Webview<br/>(ChatInput)
    participant Extension as Extension<br/>(ChatViewProvider)
    participant LLM as LLM Service
    
    User->>Webview: Click "Enhance" button with prompt
    activate Webview
    Webview->>Webview: Set isEnhancing = true
    Webview->>Extension: postMessage({ type: 'promptOptimize', prompt })
    deactivate Webview
    
    activate Extension
    Extension->>Extension: Create/get LLM service
    Extension->>LLM: Optimize prompt via AI
    activate LLM
    LLM-->>Extension: optimized_prompt
    deactivate LLM
    Extension->>Webview: postMessage({ type: 'promptOptimized', result })
    deactivate Extension
    
    activate Webview
    Webview->>Webview: Update input with optimized text
    Webview->>Webview: Set isEnhancing = false
    deactivate Webview
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

  • Message routing complexity: The backend routes 8+ new message types with distinct handlers (searchFiles, getCompletions, applyCompletion, etc.) requiring careful validation of each flow.
  • State management: ChatInput and App now manage multiple interdependent states (completionItems, completionResult, activeFile, isEnhancing, selectedFiles), requiring review of state transitions and side effects.
  • Component integration density: Five new UI components (DevInInput, CompletionPopup, FileSearchPopup, TopToolbar, FileChip) with cross-component coordination (completion triggers, keyboard nav, file selection callbacks) need careful review.
  • User interaction flows: Three major user journeys (completion, file search, prompt optimization) introduce complex keyboard navigation, debouncing, and timing logic that needs validation.

Areas requiring extra attention:

  • Debounced search logic in FileSearchPopup and completion trigger detection in DevInInput
  • Cursor position tracking and text manipulation in completion apply flow
  • State cleanup and edge cases when toggling file context, completions, or enhancement states
  • Binary file filtering and path normalization across file search and context management

Possibly related PRs

  • PR #32: Introduces the foundational JsCompletionManager integration and initial message routing between extension and webview—this PR extends that infrastructure with file context, active file tracking, and additional completion/search handlers.

Poem

🐰 A rabbit's delight in code so fine,
DevIn syntax highlights, completion's design,
Files in context, searches take flight,
From prompt to polish, enhancing the light,
Hoppy new features hop into sight! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Enhanced Chat Input with DevIn Language Support' directly and accurately summarizes the main objective of the PR.
Linked Issues check ✅ Passed All five phases and bug fixes from issue #33 are implemented: file context (Phase 1), DevIn language support (Phase 2), bottom toolbar (Phase 3), context manager (Phase 4), and MCP config (Phase 5).
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #33 objectives; no out-of-scope modifications detected in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/mpp-vscode

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

Copilot finished reviewing on behalf of phodal December 3, 2025 04:58
Copy link

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 PR implements Issue #33, adding an enhanced chat input interface for the VSCode extension with DevIn language support. The implementation introduces a React-based webview UI with syntax highlighting, auto-completion, file context management, and integration with mpp-core's completion system. The PR also refactors the MarkdownTextParser from mpp-idea to mpp-core for better code reuse across platforms.

Key Changes:

  • React webview UI with DevIn syntax highlighting and auto-completion
  • File context management with search and selection capabilities
  • Integration with mpp-core's JsCompletionManager for intelligent completions
  • MCP server configuration support and prompt optimization features

Reviewed changes

Copilot reviewed 71 out of 72 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
mpp-vscode/webview/* New React-based webview UI with TypeScript components for chat interface, syntax highlighting, and file management
mpp-vscode/src/extension.ts Extension entry point with service initialization and command registration
mpp-vscode/src/bridge/mpp-core.ts TypeScript bridge to Kotlin/JS compiled mpp-core module
mpp-vscode/src/services/* Service implementations for IDE server, diff management, and status bar
mpp-vscode/src/providers/* Chat view provider and DevIns completion provider
mpp-vscode/syntaxes/* TextMate grammar for DevIns language syntax highlighting
mpp-vscode/test/* Unit tests for services, providers, and mpp-core bridge
mpp-core/src/commonMain/kotlin/cc/unitmesh/markdown/MarkdownTextParser.kt Refactored from mpp-idea to mpp-core with proper package namespace

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

Comment on lines 19 to 20
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.

Suggested change
JsToolRegistry,
JsCompressionConfig
JsToolRegistry

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.

Suggested change
const basePath = workspaceFolders[0].uri.fsPath;

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.

Suggested change
const nonce = this.getNonce();

Copilot uses AI. Check for mistakes.
Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. 2 suggestions posted.

Comment augment review to trigger a new review at any time.

User's original prompt:`;

const response = await this.llmService.chat([
Copy link

Choose a reason for hiding this comment

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

this.llmService.chat([...]) is likely not a function on KotlinCC.llm.JsKoogLLMService; the exposed API provides methods like sendPrompt/streamPrompt, so this will throw at runtime when optimizing prompts. Consider using the available LLM service method (or the TS bridge wrapper) here.

🤖 Was this useful? React with 👍 or 👎

});

// Write file endpoint
app.post('/file/write', async (req: Request, res: Response) => {
Copy link

Choose a reason for hiding this comment

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

The /file/write endpoint writes to any absolute path without validating it’s within the current workspace, enabling arbitrary local file writes if the token is exposed (also applies to /file/read). Consider enforcing a workspace-root restriction using safe path resolution to prevent traversal/prefix-bypass cases.

🤖 Was this useful? React with 👍 or 👎

@phodal phodal merged commit 40c4593 into master Dec 3, 2025
2 of 3 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: 9

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

648-719: Remove unused basePath variable; optional: share binary-extension list

Inside handleSearchFiles:

const workspaceFolders = vscode.workspace.workspaceFolders;

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

basePath is never used, so it should be removed to keep the function clean.

You might also consider centralizing the binary‑extension list (here vs. sendActiveFileUpdate) into a shared helper to avoid them drifting apart over time, but that’s optional.

-      const workspaceFolders = vscode.workspace.workspaceFolders;
-      if (!workspaceFolders) {
+      const workspaceFolders = vscode.workspace.workspaceFolders;
+      if (!workspaceFolders) {
         this.postMessage({ type: 'searchFilesResult', data: { files: [], folders: [] } });
         return;
       }
-
-      const basePath = workspaceFolders[0].uri.fsPath;

609-642: llmService.chat is likely not a valid API on JsKoogLLMService (runtime bug)

This block assumes this.llmService exposes a chat(messages) method and returns a plain string:

const response = await this.llmService.chat([
  { role: 'system', content: systemPrompt },
  { role: 'user', content: prompt }
]);

this.postMessage({ type: 'promptOptimized', data: { optimizedPrompt: response.trim() } });

Per prior feedback and the Kotlin/JS export, KotlinCC.llm.JsKoogLLMService typically exposes methods like sendPrompt / streamPrompt, not chat, so this is very likely to throw at runtime when optimizePrompt is used. The response shape (string vs. object with message/content) may also differ.

Align this call with however you already use JsKoogLLMService elsewhere in the project (e.g., the AgentMode.ts analogue), and adapt the extraction of the optimized text accordingly.

What methods does `JsKoogLLMService` from `@autodev/mpp-core` (namespace `cc.unitmesh.llm`) expose in its JS API, and does it provide a `chat(messages)` method returning a string, or only methods like `sendPrompt` / `streamPrompt`?
🧹 Nitpick comments (12)
mpp-vscode/webview/src/components/TopToolbar.css (2)

126-127: Remove redundant flex-wrap style.

Line 126 sets flex-wrap: nowrap which is already the default behavior for flex containers and therefore unnecessary.

-/* Collapsed state - horizontal scroll */
-.top-toolbar.collapsed .files-row {
-  flex-wrap: nowrap;
-}

30-30: Inconsistent CSS variable fallbacks; add fallbacks consistently across all color variables.

Some color variables have VSCode-specific fallbacks (lines 111, 161, 175) while others rely solely on the CSS variable (lines 30, 68, 98). For robustness and consistency, provide fallbacks for all color variables.

Apply fallbacks to ensure all color definitions have a sensible default:

 .add-file-button {
   display: flex;
   align-items: center;
   gap: 4px;
   padding: 4px 8px;
   background: transparent;
   border: 1px dashed var(--panel-border);
   border-radius: 4px;
-  color: var(--foreground);
+  color: var(--foreground, #e0e0e0);
   font-size: 12px;
   cursor: pointer;
   transition: background 0.15s, border-color 0.15s;
   flex-shrink: 0;
 }
 .more-files {
   font-size: 11px;
-  color: var(--foreground);
+  color: var(--foreground, #e0e0e0);
   opacity: 0.6;
   cursor: pointer;
   white-space: nowrap;
   padding: 2px 6px;
   border-radius: 4px;
   transition: background 0.15s;
 }
 .expand-button,
 .clear-button {
   display: flex;
   align-items: center;
   justify-content: center;
   padding: 4px;
   background: transparent;
   border: none;
   border-radius: 4px;
-  color: var(--foreground);
+  color: var(--foreground, #e0e0e0);
   opacity: 0.6;
   cursor: pointer;
   transition: opacity 0.15s, background 0.15s;
 }
 .context-indicator {
   display: flex;
   align-items: center;
   justify-content: center;
   position: relative;
   padding: 4px;
   background: transparent;
   border: none;
   border-radius: 4px;
-  color: var(--foreground);
+  color: var(--foreground, #e0e0e0);
   opacity: 0.5;
   cursor: pointer;
   transition: opacity 0.15s, background 0.15s;
 }

Also applies to: 68-68, 98-98, 111-111, 161-161, 175-175

mpp-vscode/webview/src/hooks/useVSCode.ts (1)

60-60: Consider formatting the long union type for readability.

The WebviewMessage type union now contains 13 different message types in a single line, which reduces readability.

Apply this diff to improve readability:

-  type: 'sendMessage' | 'clearHistory' | 'action' | 'openConfig' | 'stopExecution' | 'selectConfig' | 'searchFiles' | 'getRecentFiles' | 'readFileContent' | 'requestConfig' | 'getActiveFile' | 'getCompletions' | 'applyCompletion';
+  type:
+    | 'sendMessage'
+    | 'clearHistory'
+    | 'action'
+    | 'openConfig'
+    | 'stopExecution'
+    | 'selectConfig'
+    | 'searchFiles'
+    | 'getRecentFiles'
+    | 'readFileContent'
+    | 'requestConfig'
+    | 'getActiveFile'
+    | 'getCompletions'
+    | 'applyCompletion';
mpp-vscode/webview/src/components/FileChip.tsx (1)

27-37: Consider adding ARIA labels to decorative SVG icons.

For better accessibility, decorative icons should have aria-hidden="true" or role="img" with aria-label.

Apply this diff to improve accessibility:

       <span className="file-chip-icon">
         {file.isDirectory ? (
-          <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
+          <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
             <path d="M1.5 2A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5V5a1.5 1.5 0 0 0-1.5-1.5H7.707l-.853-.854A.5.5 0 0 0 6.5 2.5H1.5z"/>
           </svg>
         ) : (
-          <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
+          <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
             <path d="M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.5L9.5 0H4zm5.5 0v3A1.5 1.5 0 0 0 11 4.5h3V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
           </svg>
         )}

Apply similar changes to the remove button SVGs and FileChipExpanded icons.

Also applies to: 73-83

mpp-vscode/webview/src/components/DevInInput.tsx (1)

56-66: Consider broader whitespace detection for trigger characters.

The trigger detection only checks for space (' ') and newline ('\n'), but tabs and other whitespace characters are ignored. If users might type triggers after tabs, consider using a regex test like /\s/.test(charBeforeThat) instead.

       // Trigger completion if typing /, @, or $ after whitespace or at start
       if ((charBefore === '/' || charBefore === '@' || charBefore === '$') &&
-          (charBeforeThat === ' ' || charBeforeThat === '\n' || cursorPos === 1)) {
+          (/\s/.test(charBeforeThat) || cursorPos === 1)) {
         onTriggerCompletion(charBefore as '/' | '@' | '$', cursorPos);
       }
mpp-vscode/webview/src/components/FileSearchPopup.tsx (1)

20-25: FileItem duplicates SelectedFile interface.

FileItem has the same shape as SelectedFile imported from FileChip. Consider reusing SelectedFile directly to avoid maintaining duplicate type definitions.

-interface FileItem {
-  name: string;
-  path: string;
-  relativePath: string;
-  isDirectory: boolean;
-}
+// Reuse SelectedFile as the item type
+type FileItem = SelectedFile;
mpp-vscode/webview/src/components/TopToolbar.tsx (1)

29-41: Extract ContextIndicator outside TopToolbar to avoid re-creation on each render.

Defining ContextIndicator inside TopToolbar causes a new function component instance on every render, which can lead to unnecessary DOM updates. Extract it as a separate component or use useMemo/useCallback patterns.

+interface ContextIndicatorProps {
+  isActive: boolean;
+  onToggle?: () => void;
+}
+
+const ContextIndicator: React.FC<ContextIndicatorProps> = ({ isActive, onToggle }) => (
+  <button
+    className={`context-indicator ${isActive ? 'active' : ''}`}
+    onClick={onToggle}
+    title={isActive ? 'Auto-add current file: ON' : 'Auto-add current file: OFF'}
+  >
+    <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
+      <path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
+    </svg>
+    <span className="indicator-dot" />
+  </button>
+);
+
 export const TopToolbar: React.FC<TopToolbarProps> = ({
   // ...
-  // Context indicator component
-  const ContextIndicator = () => ( ... );

Then use <ContextIndicator isActive={autoAddCurrentFile} onToggle={onToggleAutoAdd} />.

mpp-vscode/webview/src/components/ChatInput.tsx (1)

141-155: Consider providing user feedback on prompt optimization failure.

Currently, errors are only logged to console.error. Users won't know if optimization failed. Consider showing a brief notification or status indicator.

mpp-vscode/src/providers/chat-view.ts (3)

29-54: Hook editorChangeDisposable into context.subscriptions

editorChangeDisposable is created but never disposed; in an extension this should be registered with context.subscriptions so VS Code can clean it up.

You can keep the field if you want to reference it later, but also push it into subscriptions:

   this.editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor((editor) => {
     if (editor && this.webviewView) {
       this.sendActiveFileUpdate(editor.document);
     }
   });
+  this.context.subscriptions.push(this.editorChangeDisposable);

This prevents leaks if the view/provider lifecycle changes in the future.


574-603: Harden MCP config path handling and home directory detection

openMcpConfig relies on process.env.HOME || process.env.USERPROFILE and then string‑concatenates paths. Two minor robustness issues:

  • If both env vars are unset (unlikely but possible), homeDir becomes '', so you end up creating /.autodev/mcp.json (or a relative path), which is surprising.
  • Manual string concatenation (${homeDir}/.autodev/...) can produce mixed‑separator paths on Windows.

Consider:

  • Failing fast with a user‑friendly error if homeDir is falsy.
  • Using path.join for ~/.autodev and mcp.json to keep paths OS‑correct.

Example:

+import * as path from 'path';-      const homeDir = process.env.HOME || process.env.USERPROFILE || '';
-      const mcpConfigPath = `${homeDir}/.autodev/mcp.json`;
+      const homeDir = process.env.HOME || process.env.USERPROFILE;
+      if (!homeDir) {
+        throw new Error('HOME/USERPROFILE is not set; cannot locate user home directory.');
+      }
+      const configDir = path.join(homeDir, '.autodev');
+      const mcpConfigPath = path.join(configDir, 'mcp.json');-        await fs.mkdir(`${homeDir}/.autodev`, { recursive: true });
+        await fs.mkdir(configDir, { recursive: true });

725-765: Recent files implementation is fine; minor deduplication nit

handleGetRecentFiles sensibly prefers visibleTextEditors and then adds other textDocuments, skipping duplicates in the second pass.

One small nit: you can also guard against duplicates within visibleTextEditors themselves (e.g., split editors for the same file) by checking recentFiles.some before each push, but this is purely cosmetic given the low cardinality here.

mpp-vscode/webview/src/App.tsx (1)

270-293: Consider extracting file command formatting logic.

The DevIns file command formatting (lines 274-277) works correctly but could be extracted into a helper function for reusability and testability.

Example:

const formatFileCommands = (files: SelectedFile[]): string => {
  return files.map(f =>
    f.isDirectory ? `/dir:${f.relativePath}` : `/file:${f.relativePath}`
  ).join('\n');
};

// In handleSend:
if (files && files.length > 0) {
  fullContent = `${formatFileCommands(files)}\n\n${content}`;
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd0fd8 and 31fb79f.

📒 Files selected for processing (15)
  • mpp-vscode/src/providers/chat-view.ts (3 hunks)
  • mpp-vscode/webview/src/App.tsx (7 hunks)
  • mpp-vscode/webview/src/components/ChatInput.css (3 hunks)
  • mpp-vscode/webview/src/components/ChatInput.tsx (3 hunks)
  • mpp-vscode/webview/src/components/CompletionPopup.css (1 hunks)
  • mpp-vscode/webview/src/components/CompletionPopup.tsx (1 hunks)
  • mpp-vscode/webview/src/components/DevInInput.css (1 hunks)
  • mpp-vscode/webview/src/components/DevInInput.tsx (1 hunks)
  • mpp-vscode/webview/src/components/FileChip.css (1 hunks)
  • mpp-vscode/webview/src/components/FileChip.tsx (1 hunks)
  • mpp-vscode/webview/src/components/FileSearchPopup.css (1 hunks)
  • mpp-vscode/webview/src/components/FileSearchPopup.tsx (1 hunks)
  • mpp-vscode/webview/src/components/TopToolbar.css (1 hunks)
  • mpp-vscode/webview/src/components/TopToolbar.tsx (1 hunks)
  • mpp-vscode/webview/src/hooks/useVSCode.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to mpp-ui/src/jsMain/typescript/**/*.{ts,tsx} : In CLI/TUI (TypeScript), use `mpp-ui/src/jsMain/typescript/design-system/` and import `semanticInk` / `semanticChalk`

Applied to files:

  • mpp-vscode/webview/src/components/FileChip.tsx
  • mpp-vscode/webview/src/App.tsx
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/*.kt : In Kotlin/JS JsExport: Avoid `Flow`, use `Promise` instead

Applied to files:

  • mpp-vscode/src/providers/chat-view.ts
🧬 Code graph analysis (5)
mpp-vscode/webview/src/components/DevInInput.tsx (1)
core/src/main/kotlin/cc/unitmesh/devti/settings/locale/LanguageChangedCallback.kt (1)
  • placeholder (50-55)
mpp-vscode/webview/src/components/FileSearchPopup.tsx (3)
mpp-vscode/webview/src/components/FileChip.tsx (1)
  • SelectedFile (11-16)
mpp-vscode/webview/src/hooks/useVSCode.ts (2)
  • useVSCode (95-114)
  • ExtensionMessage (20-56)
mpp-vscode/src/providers/chat-view.ts (1)
  • postMessage (222-224)
mpp-vscode/webview/src/components/ChatInput.tsx (5)
mpp-vscode/webview/src/components/FileChip.tsx (1)
  • SelectedFile (11-16)
mpp-vscode/webview/src/components/ModelSelector.tsx (2)
  • ModelConfig (11-15)
  • ModelSelector (24-112)
mpp-vscode/webview/src/components/CompletionPopup.tsx (2)
  • CompletionItem (12-24)
  • CompletionPopup (58-117)
mpp-vscode/webview/src/components/TopToolbar.tsx (1)
  • TopToolbar (22-116)
mpp-vscode/webview/src/components/DevInInput.tsx (1)
  • DevInInput (27-111)
mpp-vscode/src/providers/chat-view.ts (3)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/impl/WebFetchTool.kt (1)
  • prompt (16-23)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/model/AgentDefinition.kt (1)
  • systemPrompt (27-32)
mpp-vscode/test/mocks/vscode.ts (1)
  • Uri (5-18)
mpp-vscode/webview/src/App.tsx (3)
mpp-vscode/webview/src/components/FileChip.tsx (1)
  • SelectedFile (11-16)
mpp-vscode/webview/src/components/CompletionPopup.tsx (1)
  • CompletionItem (12-24)
mpp-vscode/src/bridge/mpp-core.ts (1)
  • CompletionItem (177-184)
🪛 ast-grep (0.40.0)
mpp-vscode/webview/src/components/DevInInput.tsx

[warning] 93-93: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
mpp-vscode/webview/src/components/DevInInput.tsx

[error] 94-94: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ 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). (2)
  • GitHub Check: Build (223)
  • GitHub Check: Build (241)
🔇 Additional comments (26)
mpp-vscode/webview/src/components/ChatInput.css (1)

2-6: LGTM! UI styling updates align with new features.

The CSS changes support the new token usage display, enhance button, and completion workflows. The styling is consistent with the VSCode design system and uses appropriate CSS variables.

Also applies to: 14-16, 62-69, 162-205

mpp-vscode/webview/src/components/CompletionPopup.css (1)

1-77: LGTM! Well-structured completion popup styling.

The styling is clean and consistent, with appropriate hover/selected states and kind-specific icon colors. The z-index (line 15) should coordinate with other popups in the application to prevent stacking conflicts.

mpp-vscode/webview/src/components/FileChip.css (1)

1-122: LGTM! Clean file chip styling with good UX patterns.

The progressive disclosure pattern for the remove button (opacity: 0 by default, visible on hover) provides a clean interface. Both inline and expanded variants are well-implemented.

mpp-vscode/webview/src/components/FileSearchPopup.css (1)

1-134: LGTM! Solid modal overlay implementation.

The overlay pattern with fixed positioning and backdrop is well-implemented. The scrollable list area and search input styling follow VSCode conventions.

mpp-vscode/webview/src/hooks/useVSCode.ts (1)

37-50: LGTM! Message protocol extensions align with new features.

The new event types (tokenUpdate, promptOptimized, searchFilesResult, activeFileChanged, completionsResult, etc.) properly support the file context, completion, and token tracking features.

mpp-vscode/webview/src/components/FileChip.tsx (1)

11-22: LGTM! Clean type definitions.

The SelectedFile interface and FileChipProps are well-structured. Note that FileChipProps is not exported, which is appropriate as it's an internal implementation detail.

mpp-vscode/webview/src/components/DevInInput.css (1)

1-78: LGTM! Clean overlay-based syntax highlighting implementation.

The layered approach (highlight overlay behind transparent textarea) is a solid pattern for syntax highlighting. The styling is well-organized with appropriate use of CSS variables for theming. The actual text displayed in the textarea uses var(--foreground) which maintains proper contrast ratio (~8.6:1 against the dark background), meeting WCAG AA accessibility standards. The colored token backgrounds in the overlay provide visual enhancement without compromising readability.

Likely an incorrect or invalid review comment.

mpp-vscode/webview/src/components/DevInInput.tsx (1)

69-87: XSS mitigation appears adequate, but add a clarifying comment.

The static analysis tool flagged dangerouslySetInnerHTML, but the implementation correctly escapes user input via escapeHtml() before applying regex-based highlighting. Since $& only inserts already-escaped matched substrings, the current approach is safe.

Consider adding a brief comment above getHighlightedHtml to document that escapeHtml must always be called first, preventing future maintainers from accidentally reordering or bypassing the escape step.

mpp-vscode/webview/src/components/CompletionPopup.tsx (2)

62-90: Global keydown handler may intercept events from other components.

The window.addEventListener('keydown', ...) approach captures all keyboard events when the popup is open. This could interfere with other input handlers (e.g., the parent textarea) if not carefully coordinated. Currently, handleKeyDown calls e.preventDefault() on navigation keys, which might block expected behavior in the textarea.

Verify that parent components (like ChatInput) guard against submission when the popup is open—this appears to be handled at line 159 in ChatInput.tsx.


103-114: LGTM!

The component correctly handles keyboard navigation with proper bounds checking, and the event listener cleanup in the useEffect is properly implemented.

mpp-vscode/webview/src/components/FileSearchPopup.tsx (1)

106-122: LGTM!

The useMemo for allItems, the filtering logic, and the keyboard navigation are implemented correctly. The scroll-into-view effect properly synchronizes selection visibility.

mpp-vscode/webview/src/components/TopToolbar.tsx (1)

59-115: LGTM!

The toolbar correctly manages expanded/collapsed states, handles file chip rendering with unique keys, and integrates the FileSearchPopup for adding files. The conditional rendering logic is clean.

mpp-vscode/webview/src/components/ChatInput.tsx (2)

157-165: LGTM!

The early return when completionOpen is true correctly prevents message submission while the user is navigating completions, coordinating well with CompletionPopup's global keyboard handler.


167-200: LGTM!

The component structure cleanly integrates TopToolbar, DevInInput, and CompletionPopup. The file context management and completion UI are well-organized within the layout.

mpp-vscode/src/providers/chat-view.ts (5)

56-91: Active file updates look correct and safely filtered

sendActiveFileUpdate / sendCurrentActiveFile correctly:

  • Require a workspace,
  • Skip non‑file URIs,
  • Filter out common binary extensions, and
  • Use asRelativePath + file name for stable IDs.

This should give the webview consistent, workspace‑relative paths without spamming it for non‑text files.


93-170: New webview message types and config init wiring look sound

The added cases for searchFiles, getRecentFiles, readFileContent, requestConfig, getActiveFile, getCompletions, and applyCompletion are routed to dedicated handlers and wrapped in await where needed.

Also, awaiting initializeFromConfig() at the end of resolveWebviewView while keeping requestConfig as a cheap re‑sync entry point makes the startup/config flow robust even if the webview sends messages early.


562-571: Action wiring for prompt optimization and MCP config is straightforward

The new optimizePrompt and openMcpConfig branches delegate cleanly to handlePromptOptimize and openMcpConfig, and they reuse the existing action dispatch pattern without side effects on other cases.


813-835: Completion retrieval: defensive fallbacks look good

handleGetCompletions:

  • Cleanly short‑circuits when completionManager is missing.
  • Wraps the call in try/catch and degrades to an empty list on failure.
  • Normalizes completion items into a JSON‑friendly array for the webview.

This is a good, defensive boundary for the completion manager.


841-868: Completion apply handler mirrors retrieval nicely

handleApplyCompletion mirrors the get‑completions flow:

  • Safely handles missing completionManager.
  • Wraps applyCompletion in try/catch.
  • Normalizes the result into { newText, newCursorPosition, shouldTriggerNextCompletion } or null.

That shape should play well with the webview and keeps the extension side simple.

mpp-vscode/webview/src/App.tsx (7)

23-27: LGTM!

The CompletionResult interface is well-defined with clear, required fields for managing completion application results.


46-54: LGTM!

The new state hooks are appropriately typed with correct initial values.


264-267: LGTM!

The effect correctly requests configuration on mount with appropriate dependencies.


347-360: LGTM!

The MCP configuration and completion handlers are well-structured, properly memoized, and follow the established messaging pattern.


424-436: LGTM!

The new props passed to ChatInput correctly wire up the token usage, active file, completion, MCP config, and prompt optimization features.


228-237: Confirm whether relativePath should source from msg.data.relativePath or msg.data.path.

Line 233 sets relativePath to the same value as path. If the extension sends both properties, verify that relativePath uses the correct source. Check the extension code that constructs activeFileChanged messages to understand the intended semantics of these two fields.


12-13: No action needed—CompletionItem interfaces are appropriately designed for their respective layers.

The two CompletionItem interfaces serve different purposes and are not interchangeable by design. mpp-core.ts defines a strict interface for the completion manager (lines 177–184 with required triggerType and index), while CompletionPopup.tsx exports a more permissive interface (lines 12–24) with optional fields and legacy fallbacks for backward compatibility. The data flows safely from mpp-core (which provides required fields) to the webview (which accepts them as optional). CompletionPopup uses triggerType for icon display (line 39–41) and relies on the array index parameter rather than the index field, making the optional designation intentional and correct. Consolidating these definitions would conflate architectural layers unnecessarily; the current separation is appropriate.

Comment on lines +771 to +807
private async handleReadFileContent(filePath: string): Promise<void> {
if (!filePath) {
this.postMessage({ type: 'fileContentResult', data: { content: null, error: 'No path provided' } });
return;
}

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);
} else {
// Relative path - resolve against workspace
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
if (!workspaceFolder) {
this.postMessage({ type: 'fileContentResult', data: { content: null, error: 'No workspace folder' } });
return;
}
uri = vscode.Uri.joinPath(workspaceFolder.uri, filePath);
}

const content = await vscode.workspace.fs.readFile(uri);
const text = new TextDecoder().decode(content);

this.postMessage({
type: 'fileContentResult',
data: { path: filePath, content: text }
});
} catch (error) {
this.log(`Error reading file: ${error}`);
this.postMessage({
type: 'fileContentResult',
data: { path: filePath, content: null, error: String(error) }
});
}
}
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

Consider restricting readFileContent to workspace files

handleReadFileContent accepts either relative or absolute paths and will happily read any local file if the webview asks for it:

  • Relative paths are resolved against the first workspace folder.
  • Absolute paths (/... or C:\...) bypass the workspace restriction entirely.

Given this is driven by your own webview UI, it’s not an immediate security hole, but it does expand what the webview can cause the extension to read (e.g. system or home‑directory files) if any XSS or compromised code ever appears in the webview.

Consider tightening this so that:

  • Only files under the current workspace root(s) are allowed, or
  • Absolute paths are rejected (or gated behind an explicit user confirmation), and
  • You validate that the resolved uri is within some allowed root.

That will better align this helper with the “project context” use case.

Comment on lines +220 to +255
// Token usage update
case 'tokenUpdate':
if (msg.data?.totalTokens != null) {
setTotalTokens(msg.data.totalTokens as number);
}
break;

// Active file changed (for auto-add current file)
case 'activeFileChanged':
if (msg.data) {
setActiveFile({
path: msg.data.path as string,
name: msg.data.name as string,
relativePath: msg.data.path as string,
isDirectory: msg.data.isDirectory as boolean || false
});
}
break;

// Completion results from mpp-core
case 'completionsResult':
if (msg.data?.items) {
setCompletionItems(msg.data.items as CompletionItem[]);
}
break;

// Completion applied result
case 'completionApplied':
if (msg.data) {
setCompletionResult({
newText: msg.data.newText as string,
newCursorPosition: msg.data.newCursorPosition as number,
shouldTriggerNextCompletion: msg.data.shouldTriggerNextCompletion as boolean
});
}
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 | 🟠 Major

Strengthen type safety in message handling.

The message handlers use unchecked type assertions (as string, as number, as CompletionItem[]) without validating the data structure. If msg.data doesn't match the expected shape, runtime errors could occur.

Consider adding runtime validation or using type guards to verify the data before casting, especially for complex types like CompletionItem[].

Example validation pattern:

 case 'completionsResult':
   if (msg.data?.items) {
-    setCompletionItems(msg.data.items as CompletionItem[]);
+    const items = msg.data.items;
+    if (Array.isArray(items)) {
+      setCompletionItems(items as CompletionItem[]);
+    }
   }
   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
// Token usage update
case 'tokenUpdate':
if (msg.data?.totalTokens != null) {
setTotalTokens(msg.data.totalTokens as number);
}
break;
// Active file changed (for auto-add current file)
case 'activeFileChanged':
if (msg.data) {
setActiveFile({
path: msg.data.path as string,
name: msg.data.name as string,
relativePath: msg.data.path as string,
isDirectory: msg.data.isDirectory as boolean || false
});
}
break;
// Completion results from mpp-core
case 'completionsResult':
if (msg.data?.items) {
setCompletionItems(msg.data.items as CompletionItem[]);
}
break;
// Completion applied result
case 'completionApplied':
if (msg.data) {
setCompletionResult({
newText: msg.data.newText as string,
newCursorPosition: msg.data.newCursorPosition as number,
shouldTriggerNextCompletion: msg.data.shouldTriggerNextCompletion as boolean
});
}
break;
// Token usage update
case 'tokenUpdate':
if (msg.data?.totalTokens != null) {
setTotalTokens(msg.data.totalTokens as number);
}
break;
// Active file changed (for auto-add current file)
case 'activeFileChanged':
if (msg.data) {
setActiveFile({
path: msg.data.path as string,
name: msg.data.name as string,
relativePath: msg.data.path as string,
isDirectory: msg.data.isDirectory as boolean || false
});
}
break;
// Completion results from mpp-core
case 'completionsResult':
if (msg.data?.items) {
const items = msg.data.items;
if (Array.isArray(items)) {
setCompletionItems(items as CompletionItem[]);
}
}
break;
// Completion applied result
case 'completionApplied':
if (msg.data) {
setCompletionResult({
newText: msg.data.newText as string,
newCursorPosition: msg.data.newCursorPosition as number,
shouldTriggerNextCompletion: msg.data.shouldTriggerNextCompletion as boolean
});
}
break;
🤖 Prompt for AI Agents
In mpp-vscode/webview/src/App.tsx around lines 220 to 255, the message handlers
perform unchecked casts (e.g., "as string", "as number", "as CompletionItem[]")
which can cause runtime errors if msg.data is malformed; replace these casts
with runtime validation/type-guards: check msg.data exists, validate primitive
fields with typeof (e.g., typeof msg.data.totalTokens === 'number'), validate
objects have required keys, validate arrays with Array.isArray and per-item
type-guards for CompletionItem, and only call state setters when validation
passes; on validation failure, log an error and avoid updating state (or use
safe defaults), and factor repeated checks into small helper validators to keep
the handler concise.

Comment on lines +320 to +345
// Handle prompt optimization
const handlePromptOptimize = useCallback(async (prompt: string): Promise<string> => {
return new Promise((resolve) => {
// Send optimization request to extension
postMessage({ type: 'action', action: 'optimizePrompt', data: { prompt } });

// Listen for response
const handler = (event: MessageEvent) => {
const msg = event.data;
if (msg.type === 'promptOptimized' && msg.data?.optimizedPrompt) {
window.removeEventListener('message', handler);
resolve(msg.data.optimizedPrompt as string);
} else if (msg.type === 'promptOptimizeFailed') {
window.removeEventListener('message', handler);
resolve(prompt); // Return original on failure
}
};
window.addEventListener('message', handler);

// Timeout after 30 seconds
setTimeout(() => {
window.removeEventListener('message', handler);
resolve(prompt);
}, 30000);
});
}, [postMessage]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix memory leak and use established message handling pattern.

The handlePromptOptimize function adds a window event listener that isn't cleaned up if the component unmounts, creating a memory leak. Additionally, it bypasses the useVSCode hook's onMessage mechanism, which is inconsistent with the rest of the codebase.

Refactor to use the established pattern or manage cleanup properly:

 const handlePromptOptimize = useCallback(async (prompt: string): Promise<string> => {
   return new Promise((resolve) => {
+    let resolved = false;
+    
     postMessage({ type: 'action', action: 'optimizePrompt', data: { prompt } });

     const handler = (event: MessageEvent) => {
       const msg = event.data;
       if (msg.type === 'promptOptimized' && msg.data?.optimizedPrompt) {
+        if (resolved) return;
+        resolved = true;
         window.removeEventListener('message', handler);
         resolve(msg.data.optimizedPrompt as string);
       } else if (msg.type === 'promptOptimizeFailed') {
+        if (resolved) return;
+        resolved = true;
         window.removeEventListener('message', handler);
         resolve(prompt);
       }
     };
     window.addEventListener('message', handler);

     setTimeout(() => {
+      if (resolved) return;
+      resolved = true;
       window.removeEventListener('message', handler);
       resolve(prompt);
     }, 30000);
   });
 }, [postMessage]);

Better yet, consider using a request ID system with the onMessage hook to avoid manual event listener management entirely.

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

🤖 Prompt for AI Agents
mpp-vscode/webview/src/App.tsx around lines 320 to 345: the current
handlePromptOptimize creates a window message listener that can leak if the
component unmounts and it also bypasses the project's useVSCode onMessage
pattern; refactor to use the established useVSCode/onMessage mechanism (or pass
postMessage into a request/response helper) and implement a request ID
correlation so responses are matched to the Promise, and ensure any temporary
handlers are registered via the hook and removed on unmount; alternatively, keep
the Promise-based API but register the listener through the hook (not
window.addEventListener), store the timeout ID and request ID, and clear both
the listener and timeout when a response arrives or the component unmounts.

Comment on lines +72 to +84
// Handle completion result from mpp-core
useEffect(() => {
textareaRef.current?.focus();
if (completionResult) {
setInput(completionResult.newText);
cursorPositionRef.current = completionResult.newCursorPosition;
if (completionResult.shouldTriggerNextCompletion && onGetCompletions) {
// Trigger next completion
onGetCompletions(completionResult.newText, completionResult.newCursorPosition);
} else {
setCompletionOpen(false);
}
}
}, [completionResult, onGetCompletions]);
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

Cursor position is stored but not applied to the textarea.

cursorPositionRef.current is updated, but the actual textarea cursor is not programmatically set to newCursorPosition. After setInput(completionResult.newText), the cursor will likely jump to the end, causing subsequent completions to use the wrong context.

If cursor positioning matters for chained completions, you'll need to set textarea.selectionStart and selectionEnd after the state update.

Do you want me to generate a solution that properly positions the cursor after applying a completion result?

🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/ChatInput.tsx around lines 72-84, the code
updates cursorPositionRef.current but never applies it to the textarea DOM node,
so after setInput(...) the cursor jumps to the end. After calling
setInput(completionResult.newText) ensure you apply the new cursor position to
the textarea: get the textarea ref (ensure it exists), then after the state/DOM
update (use requestAnimationFrame or setTimeout(...,0) or useLayoutEffect) set
textarea.selectionStart = textarea.selectionEnd =
completionResult.newCursorPosition (or set selectionEnd appropriately if you
need a range). Keep the existing flow for
shouldTriggerNextCompletion/onGetCompletions and closing completionOpen, but
only set the DOM selection when the textarea ref is present and the completion
does not immediately trigger another completion that will replace the text.

Comment on lines +111 to +118
// Handle completion trigger - request completions from mpp-core
const handleTriggerCompletion = useCallback((trigger: '/' | '@' | '$', position: number) => {
cursorPositionRef.current = position;
if (onGetCompletions) {
// Use mpp-core for completions
onGetCompletions(input.substring(0, position) + trigger, position + 1);
}
}, [input, onGetCompletions]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "getCompletions|onGetCompletions" --type=ts --type=tsx -B 2 -A 5

Repository: phodal/auto-dev-sketch

Length of output: 93


🏁 Script executed:

rg -n "handleTriggerCompletion" --type=ts --type=tsx -B 3 -A 3

Repository: phodal/auto-dev-sketch

Length of output: 93


🏁 Script executed:

rg -n "onGetCompletions" webview/src/components/ChatInput.tsx -B 5 -A 10

Repository: phodal/auto-dev-sketch

Length of output: 142


🏁 Script executed:

git ls-files | head -30

Repository: phodal/auto-dev-sketch

Length of output: 1070


🏁 Script executed:

find . -name "ChatInput.tsx" -o -name "*ChatInput*" 2>/dev/null

Repository: phodal/auto-dev-sketch

Length of output: 165


🏁 Script executed:

find . -type f -name "*.ts" -o -name "*.tsx" | grep -i chat | head -20

Repository: phodal/auto-dev-sketch

Length of output: 360


🏁 Script executed:

wc -l ./mpp-vscode/webview/src/components/ChatInput.tsx

Repository: phodal/auto-dev-sketch

Length of output: 119


🏁 Script executed:

cat -n ./mpp-vscode/webview/src/components/ChatInput.tsx | sed -n '100,130p'

Repository: phodal/auto-dev-sketch

Length of output: 1401


🏁 Script executed:

rg -n "getCompletions|onGetCompletions|handleTriggerCompletion" ./mpp-vscode/webview/src/components/ChatInput.tsx -B 2 -A 5

Repository: phodal/auto-dev-sketch

Length of output: 2405


🏁 Script executed:

rg -n "getCompletions" --type=ts --type=js -B 3 -A 8

Repository: phodal/auto-dev-sketch

Length of output: 7941


🏁 Script executed:

rg -n "DevInInput|onTriggerCompletion" ./mpp-vscode/webview/src/components/ -B 3 -A 5

Repository: phodal/auto-dev-sketch

Length of output: 6533


🏁 Script executed:

find ./mpp-vscode/webview/src -name "DevInInput*" -o -name "*DevInInput*"

Repository: phodal/auto-dev-sketch

Length of output: 167


🏁 Script executed:

cat -n ./mpp-vscode/webview/src/components/ChatInput.tsx | sed -n '1,50p'

Repository: phodal/auto-dev-sketch

Length of output: 2119


🏁 Script executed:

cat -n ./mpp-vscode/webview/src/components/DevInInput.tsx | sed -n '50,70p'

Repository: phodal/auto-dev-sketch

Length of output: 1066


Remove duplicate trigger character and incorrect position offset in handleTriggerCompletion.

In handleTriggerCompletion (line 116), the trigger character is already present in input at the cursor position when DevInInput calls this handler. Line 64 in DevInInput.tsx shows that onTriggerCompletion is called with cursorPos after the trigger has been typed, meaning input[position - 1] is already the trigger character.

The current code concatenates the trigger again (+ trigger) and increments the position (position + 1), which duplicates the trigger and provides an incorrect cursor position to getCompletions.

Fix: Use onGetCompletions(input.substring(0, position), position) instead of onGetCompletions(input.substring(0, position) + trigger, position + 1).

🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/ChatInput.tsx around lines 111 to 118,
handleTriggerCompletion is wrongly re-inserting the trigger and advancing the
cursor: input already contains the trigger at position-1, so replace the call to
onGetCompletions with one that passes the current prefix and position unchanged
by using onGetCompletions(input.substring(0, position), position) instead of
concatenating the trigger and using position + 1.

Comment on lines +42 to +46
<button className="file-chip-remove" onClick={onRemove} title="Remove from context">
<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg>
</button>
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

Add explicit button type to prevent form submission.

The remove buttons should have type="button" to prevent unintended form submissions if these chips are used within a form context.

Apply this diff:

-      <button className="file-chip-remove" onClick={onRemove} title="Remove from context">
+      <button type="button" className="file-chip-remove" onClick={onRemove} title="Remove from context">

And similarly for FileChipExpanded (line 88):

-      <button className="file-chip-remove" onClick={onRemove} title="Remove from context">
+      <button type="button" className="file-chip-remove" onClick={onRemove} title="Remove from context">

Also applies to: 88-92

🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/FileChip.tsx around lines 42-46 and also
around lines 88-92, the remove <button> elements lack an explicit type causing
them to act as submit buttons when inside a form; update each remove button to
include type="button" (i.e., add the attribute to the existing <button> tags in
both FileChip and FileChipExpanded) to prevent unintended form submissions while
preserving existing className, onClick, title, and SVG content.

Comment on lines +54 to +65
function getTruncatedPath(path: string): string {
const parentPath = path.substring(0, path.lastIndexOf('/'));
if (!parentPath) return '';

if (parentPath.length <= 30) return parentPath;

const parts = parentPath.split('/');
if (parts.length <= 2) return `...${parentPath}`;

const keepParts = parts.slice(-3);
return `.../${keepParts.join('/')}`;
}
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

Path truncation logic may not handle all edge cases.

The getTruncatedPath function has a few potential issues:

  1. Line 55: Uses / as path separator, which won't work correctly on Windows (should use platform-specific separator or normalize paths)
  2. Line 58: The 30-character threshold is arbitrary and may not work well for all contexts
  3. Line 61: If there are ≤2 parts, it prepends ... but might still exceed the length limit

Consider this more robust implementation:

 function getTruncatedPath(path: string): string {
-  const parentPath = path.substring(0, path.lastIndexOf('/'));
+  // Normalize path separators
+  const normalizedPath = path.replace(/\\/g, '/');
+  const parentPath = normalizedPath.substring(0, normalizedPath.lastIndexOf('/'));
   if (!parentPath) return '';
   
   if (parentPath.length <= 30) return parentPath;
   
   const parts = parentPath.split('/');
-  if (parts.length <= 2) return `...${parentPath}`;
+  if (parts.length <= 2) {
+    // For short paths, truncate from the start
+    return `...${parentPath.slice(-27)}`;
+  }
   
   const keepParts = parts.slice(-3);
   return `.../${keepParts.join('/')}`;
 }
📝 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
function getTruncatedPath(path: string): string {
const parentPath = path.substring(0, path.lastIndexOf('/'));
if (!parentPath) return '';
if (parentPath.length <= 30) return parentPath;
const parts = parentPath.split('/');
if (parts.length <= 2) return `...${parentPath}`;
const keepParts = parts.slice(-3);
return `.../${keepParts.join('/')}`;
}
function getTruncatedPath(path: string): string {
// Normalize path separators
const normalizedPath = path.replace(/\\/g, '/');
const parentPath = normalizedPath.substring(0, normalizedPath.lastIndexOf('/'));
if (!parentPath) return '';
if (parentPath.length <= 30) return parentPath;
const parts = parentPath.split('/');
if (parts.length <= 2) {
// For short paths, truncate from the start
return `...${parentPath.slice(-27)}`;
}
const keepParts = parts.slice(-3);
return `.../${keepParts.join('/')}`;
}

Comment on lines +92 to +104
useEffect(() => {
if (!isOpen) return;
if (searchQuery.length >= 2) {
setIsLoading(true);
const timer = setTimeout(() => {
postMessage({ type: 'searchFiles', data: { query: searchQuery } });
}, 150);
return () => clearTimeout(timer);
} else {
setFiles([]);
setFolders([]);
}
}, [searchQuery, isOpen, postMessage]);
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

isLoading may remain true if query drops below 2 characters.

When searchQuery.length >= 2, isLoading is set to true. If the user then deletes characters so the query becomes shorter than 2, the else branch clears the results but never resets isLoading to false, leaving the UI in a stale loading state.

     } else {
       setFiles([]);
       setFolders([]);
+      setIsLoading(false);
     }
🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/FileSearchPopup.tsx around lines 92 to 104,
the effect sets isLoading(true) when searchQuery.length >= 2 but never resets
isLoading when the query drops below 2 (else branch only clears files/folders),
causing a stale loading state; update the effect so the else branch also calls
setIsLoading(false) and ensure when isOpen becomes false you likewise reset
isLoading(false) (or add cleanup) so the loading flag is always cleared when
searches are cancelled or the popup closes.

Comment on lines +89 to +102
.expand-button,
.clear-button {
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
background: transparent;
border: none;
border-radius: 4px;
color: var(--foreground);
opacity: 0.6;
cursor: pointer;
transition: opacity 0.15s, background 0.15s;
}
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

Add focus states for keyboard navigation accessibility.

The buttons and context indicator lack :focus-visible states, which breaks keyboard navigation in the IDE and violates WCAG accessibility requirements. Users navigating via Tab key will not see a focus indicator.

Apply this diff to add focus states:

 .expand-button,
 .clear-button {
   display: flex;
   align-items: center;
   justify-content: center;
   padding: 4px;
   background: transparent;
   border: none;
   border-radius: 4px;
   color: var(--foreground);
   opacity: 0.6;
   cursor: pointer;
   transition: opacity 0.15s, background 0.15s;
 }

 .expand-button:hover,
 .clear-button:hover {
   opacity: 1;
   background: var(--list-hover-background);
 }

+.expand-button:focus-visible,
+.clear-button:focus-visible {
+  opacity: 1;
+  outline: 1px solid var(--focus-border);
+  outline-offset: 2px;
+}

Also add focus state to context indicator:

 .context-indicator:hover {
   opacity: 0.8;
   background: var(--list-hover-background);
 }

 .context-indicator.active {
   opacity: 1;
   color: var(--vscode-textLink-foreground, #3794ff);
 }

+.context-indicator:focus-visible {
+  opacity: 1;
+  outline: 1px solid var(--focus-border);
+  outline-offset: 2px;
+}

And for the add-file button:

 .add-file-button:hover {
   background: var(--list-hover-background);
   border-color: var(--focus-border);
 }

+.add-file-button:focus-visible {
+  border-color: var(--focus-border);
+  outline: 1px solid var(--focus-border);
+  outline-offset: 2px;
+}

Also applies to: 139-152

🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/TopToolbar.css around lines 89-102 (also
apply same changes to 139-152), the interactive controls lack :focus-visible
styles which breaks keyboard navigation; add :focus-visible rules for
.expand-button and .clear-button, the context indicator element, and the
add-file button that provide a visible focus ring (for example a 2px outline or
a subtle box-shadow using a theme variable like --focus), preserve
border-radius, and optionally set a slightly different background/opacity on
focus; ensure these selectors use :focus-visible (not :focus) so mouse
interactions aren’t affected and keep the existing transition so the focus
indicator appears smoothly.

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.

[VSCode] Migrate IdeaDevInInputArea and IdeaDevInInput to VSCode

2 participants