Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
VSCODE_STUDIO_EXTENSION_RESEARCH.md50.5 kB
# VSCode Studio Extension Research: Drag-and-Drop Workflow Builder ## Executive Summary This research explores integrating Mimir's Orchestration Studio into a VSCode extension, allowing users to create multi-agent workflows through drag-and-drop directly in their IDE. The existing Studio (React + react-dnd) can be embedded in VSCode using Webview API with message-based communication. **Key Findings:** - ✅ **Feasible**: VSCode Webview API supports complex React applications - ✅ **React DnD Compatible**: HTML5Backend works in webviews with minor adjustments - ✅ **Communication Pattern**: postMessage API for extension ↔ webview coordination - ⚠️ **Challenge**: State persistence and file system integration require careful design - ⚠️ **Performance**: Large workflows may require optimization strategies **Recommendation**: Proceed with phased implementation using existing Studio codebase as foundation. **Architecture Decision**: ✅ **UNIFIED EXTENSION** - Combine chat participant and Studio UI in single extension for optimal user experience. --- ## 1. Unified Extension Architecture ### 1.1 Why One Extension? **User Benefits:** - ✅ **Single Installation**: Install once, get both chat and visual workflow builder - ✅ **Unified Settings**: Configure API URL and credentials once - ✅ **Seamless Workflow**: Chat → Design workflow → Execute → View results - ✅ **Consistent Branding**: One extension name, icon, and marketplace listing - ✅ **Better Discoverability**: Users find both features in one place **Developer Benefits:** - ✅ **Shared Codebase**: Common API client, configuration, and utilities - ✅ **Single Maintenance**: One version, one release cycle, one test suite - ✅ **Simpler Deployment**: One marketplace listing to manage - ✅ **Code Reuse**: Both features use same backend connection logic ### 1.2 Feature Separation **Chat Participant (Existing - No Changes)** - Activated: `@mimir` in chat panel - Purpose: Conversational AI with Graph-RAG memory - UI: Native VSCode chat interface - Code: `src/extension.ts` (handleChatRequest function) **Studio UI (New - Add Alongside)** - Activated: Command palette `Mimir: Open Workflow Studio` or `.mimir-workflow` file - Purpose: Visual drag-and-drop workflow designer - UI: React webview with custom components - Code: `src/studioPanel.ts` + `webview-src/studio/` **Shared Infrastructure** - Configuration: `mimir.*` settings apply to both - API Connection: Both use `config.apiUrl` - Preambles: Studio can reference chat preambles for agent configuration - Backend: Both connect to same Mimir MCP server ### 1.3 User Journey ``` 1. Install "Mimir" extension from marketplace ↓ 2. Configure API URL once: Settings → Mimir → API URL ↓ 3. Use Chat: Open chat panel, type @mimir to start conversation ↓ 4. Use Studio: Command palette → "Mimir: Open Workflow Studio" ↓ 5. Create workflow visually with drag-and-drop ↓ 6. Save as .mimir-workflow file in workspace ↓ 7. Execute workflow from Studio or via command ↓ 8. View results in output channel ``` ### 1.4 Code Organization Strategy **Extension Host (Node.js context)** ``` src/ ├── extension.ts ← Single entry point for BOTH features ├── chat/ ← Chat-specific modules │ ├── chatHandler.ts │ └── preambleManager.ts └── studio/ ← Studio-specific modules ├── studioPanel.ts ├── workflowEditor.ts └── executionManager.ts ``` **Webview (Browser context)** ``` webview-src/studio/ ← Only Studio needs webview ├── main.tsx ← React app entry ├── Studio.tsx ← Root component ├── components/ ← Copied from frontend/ └── store/ ← Copied from frontend/ ``` **Shared** ``` src/shared/ ├── config.ts ← Configuration management ├── apiClient.ts ← Backend API wrapper └── types.ts ← Common type definitions ``` --- ## 2. Current Architecture Analysis ### Existing Studio Implementation **Technology Stack:** - **Frontend**: React 18 + TypeScript - **Drag-and-Drop**: `react-dnd` v16.0.1 + `react-dnd-html5-backend` - **State Management**: Zustand (`usePlanStore`) - **Routing**: React Router v6 - **Styling**: TailwindCSS + Norse theme - **Build**: Vite + esbuild **Key Components:** ``` /Users/c815719/src/Mimir/frontend/src/ ├── pages/ │ └── Studio.tsx (278 lines) - Main orchestration UI ├── components/ │ ├── TaskCanvas.tsx (602 lines) - Drag-and-drop canvas │ ├── TaskEditor.tsx (333 lines) - Task detail editor │ ├── AgentPalette.tsx (281 lines) - Agent selection palette │ ├── TaskCard.tsx (260 lines) - Individual task cards │ ├── ParallelGroupContainer.tsx (142 lines) - Parallel execution groups │ └── AgentDragPreview.tsx (95 lines) - Custom drag preview └── store/ └── planStore.ts - Zustand state management ``` **Studio Features:** - Drag agents from palette to canvas - Create sequential and parallel task flows - Edit task properties (requirements, files, dependencies) - Execute workflows via API - View deliverables from completed executions - Export/download workflow definitions ### Existing VSCode Extension **Current Capabilities:** ```typescript // vscode-extension/src/extension.ts (346 lines) - Chat participant integration (@mimir) - Preamble management - Dynamic model selection - Vector search configuration - Flag-based CLI arguments (-u, -m, -d, -l, -s, -t) - Streaming chat responses ``` **Planned Integration Strategy:** ✅ **UNIFIED EXTENSION** - Both chat and Studio UI in one extension - Chat participant remains at `@mimir` (existing functionality) - Studio UI accessible via command palette or file association - Shared configuration and backend connection - Single marketplace listing and installation - Consistent UX across both features **Benefits of Unified Approach:** - ✅ Single installation for users - ✅ Shared authentication and configuration - ✅ Consistent API client logic - ✅ One extension to maintain - ✅ Better discoverability --- ## 2. VSCode Webview API Research ### 2.1 Webview Architecture **Official Documentation:** [VSCode Webview API Guide](https://code.visualstudio.com/api/extension-guides/webview) **Core Concepts:** ```typescript // Create webview panel const panel = vscode.window.createWebviewPanel( 'mimirStudio', // viewType 'Mimir Studio', // title vscode.ViewColumn.One, // column { enableScripts: true, // REQUIRED for React retainContextWhenHidden: true, // Persist state when hidden localResourceRoots: [ // Security: allowed file paths vscode.Uri.joinPath(context.extensionUri, 'media'), vscode.Uri.joinPath(context.extensionUri, 'dist') ] } ); ``` **Key Features:** - ✅ Full HTML/CSS/JS support (can run React apps) - ✅ Two-way message passing (extension ↔ webview) - ✅ Access to VSCode theme colors - ✅ Persistence across visibility changes - ⚠️ Sandboxed environment (no direct Node.js access) - ⚠️ Limited file system access (must use extension host as proxy) ### 2.2 Communication Pattern **Extension Host → Webview:** ```typescript // Extension host sends command panel.webview.postMessage({ command: 'loadWorkflow', workflow: { /* workflow data */ } }); ``` **Webview → Extension Host:** ```typescript // Webview sends message const vscode = acquireVsCodeApi(); vscode.postMessage({ command: 'saveWorkflow', workflow: { /* workflow data */ } }); // Extension host receives message panel.webview.onDidReceiveMessage(async (message) => { switch (message.command) { case 'saveWorkflow': await saveWorkflowToFile(message.workflow); break; } }); ``` ### 2.3 React Integration **Webview UI Toolkit for React:** - **Package**: `@vscode/webview-ui-toolkit-react` - **Purpose**: Pre-built components matching VSCode's native UI - **Components**: Buttons, inputs, dropdowns, text areas, checkboxes, etc. - **Theming**: Automatically adapts to VSCode light/dark themes **Example:** ```tsx import { VSCodeButton, VSCodeTextField } from '@vscode/webview-ui-toolkit/react'; function StudioPanel() { return ( <div> <VSCodeTextField placeholder="Task name" /> <VSCodeButton onClick={handleSave}>Save Workflow</VSCodeButton> </div> ); } ``` **Compatibility with Existing Studio:** - ✅ Can keep existing React components - ✅ Can incrementally adopt @vscode/webview-ui-toolkit - ✅ TailwindCSS works in webviews - ✅ React DnD works in webviews (verified by community) ### 2.4 Resource Loading **Content Security Policy (CSP):** ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${webview.cspSource} https:; script-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline';" /> ``` **Loading React Build:** ```typescript // Get URIs for bundled assets const scriptUri = panel.webview.asWebviewUri( vscode.Uri.joinPath(context.extensionUri, 'dist', 'studio.js') ); const styleUri = panel.webview.asWebviewUri( vscode.Uri.joinPath(context.extensionUri, 'dist', 'studio.css') ); // Inject into HTML panel.webview.html = ` <!DOCTYPE html> <html> <head> <link href="${styleUri}" rel="stylesheet"> </head> <body> <div id="root"></div> <script src="${scriptUri}"></script> </body> </html> `; ``` --- ## 3. Drag-and-Drop Implementation ### 3.1 React DnD in VSCode Webviews **Current Studio Implementation:** ```tsx import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; export function Studio() { return ( <DndProvider backend={HTML5Backend}> <AgentPalette /> {/* Drag source */} <TaskCanvas /> {/* Drop target */} </DndProvider> ); } ``` **Compatibility Verification:** - ✅ HTML5Backend works in webviews (iframes) - ✅ No special modifications needed - ✅ Drag preview renders correctly - ⚠️ May need to handle cross-origin if loading external resources **Drag Source Example (AgentPalette):** ```tsx const [{ isDragging }, drag] = useDrag(() => ({ type: 'AGENT', item: { agentType: 'worker', role: 'Implementation' }, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), })); ``` **Drop Target Example (TaskCanvas):** ```tsx const [{ isOver }, drop] = useDrop(() => ({ accept: 'AGENT', drop: (item: { agentType: string, role: string }, monitor) => { // Create new task at drop position addTask({ id: generateId(), type: item.agentType, role: item.role, // ... }); }, collect: (monitor) => ({ isOver: monitor.isOver(), }), })); ``` ### 3.2 Custom Drag Preview **Current Implementation:** ```tsx export function AgentDragPreview() { return ( <DragLayer> {({ item, currentOffset }) => ( <div style={{ position: 'fixed', pointerEvents: 'none', left: currentOffset?.x, top: currentOffset?.y }}> {/* Custom preview content */} </div> )} </DragLayer> ); } ``` **VSCode Considerations:** - ✅ Works as-is in webviews - ⚠️ May need z-index adjustments for VSCode UI elements - ⚠️ Preview positioning should be relative to webview, not window --- ## 4. State Management & Persistence ### 4.1 Current State Architecture (Zustand) ```typescript // store/planStore.ts interface PlanStore { tasks: Task[]; parallelGroups: ParallelGroup[]; projectPrompt: string; selectedTaskId: string | null; addTask: (task: Task) => void; updateTask: (id: string, updates: Partial<Task>) => void; deleteTask: (id: string) => void; createParallelGroup: (taskIds: string[]) => void; // ... } export const usePlanStore = create<PlanStore>((set) => ({ tasks: [], parallelGroups: [], // ... })); ``` ### 4.2 VSCode Extension State Strategy **Option 1: Webview State + File Persistence** ```typescript // Extension host manages persistence class WorkflowManager { private workflowPath: string; async saveWorkflow(workflow: Workflow) { const uri = vscode.Uri.file(this.workflowPath); const content = JSON.stringify(workflow, null, 2); await vscode.workspace.fs.writeFile(uri, Buffer.from(content)); } async loadWorkflow(): Promise<Workflow | null> { const uri = vscode.Uri.file(this.workflowPath); const content = await vscode.workspace.fs.readFile(uri); return JSON.parse(content.toString()); } } // Webview requests save vscode.postMessage({ command: 'saveWorkflow', workflow: usePlanStore.getState() }); // Extension host persists to file panel.webview.onDidReceiveMessage(async (message) => { if (message.command === 'saveWorkflow') { await workflowManager.saveWorkflow(message.workflow); vscode.window.showInformationMessage('Workflow saved!'); } }); ``` **Option 2: VSCode Memento (Extension State)** ```typescript // Extension host uses globalState context.globalState.update('mimir.workflow', workflow); // Retrieve const workflow = context.globalState.get<Workflow>('mimir.workflow'); ``` **Option 3: Workspace Settings (JSON Files)** ```json // .vscode/mimir-workflows/my-workflow.json { "name": "Authentication Feature", "tasks": [...], "parallelGroups": [...], "createdAt": "2025-11-19T..." } ``` **Recommended Approach:** - **Primary**: JSON files in `.vscode/mimir-workflows/` (Option 3) - ✅ Version controllable - ✅ Human-readable - ✅ Shareable across team - ✅ Can open/edit manually - **Secondary**: Memento for UI preferences (Option 2) - ✅ Canvas zoom level - ✅ Last opened workflow - ✅ Panel visibility ### 4.3 Auto-Save Strategy ```typescript // Debounced auto-save in webview import { debounce } from 'lodash-es'; const autoSave = debounce(() => { vscode.postMessage({ command: 'saveWorkflow', workflow: usePlanStore.getState(), autoSave: true // Don't show notification }); }, 2000); // Subscribe to store changes usePlanStore.subscribe(autoSave); ``` --- ## 5. File System Integration ### 5.1 Workspace File Picker **Use Case**: User needs to select files to attach to tasks **Current Studio**: Uses HTML `<input type="file">` (browser-based) **VSCode Extension**: Must use VSCode file picker ```typescript // Extension host provides file picker panel.webview.onDidReceiveMessage(async (message) => { if (message.command === 'pickFiles') { const uris = await vscode.window.showOpenDialog({ canSelectMany: true, openLabel: 'Select files for task', filters: { 'All Files': ['*'] } }); if (uris) { // Send selected files back to webview panel.webview.postMessage({ command: 'filesSelected', files: uris.map(uri => ({ path: uri.fsPath, name: path.basename(uri.fsPath) })) }); } } }); ``` ### 5.2 Relative Path Handling ```typescript // Convert absolute paths to workspace-relative function makeRelativePath(absolutePath: string): string { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; if (workspaceFolder) { return path.relative(workspaceFolder.uri.fsPath, absolutePath); } return absolutePath; } // Store relative paths in workflow { "tasks": [ { "id": "task-1", "files": [ "src/auth/login.ts", // Relative to workspace "src/auth/register.ts" ] } ] } ``` ### 5.3 File Watcher Integration ```typescript // Watch for file changes const watcher = vscode.workspace.createFileSystemWatcher( new vscode.RelativePattern(workspaceFolder, '.vscode/mimir-workflows/**/*.json') ); watcher.onDidChange(uri => { // Reload workflow if currently open if (uri.fsPath === currentWorkflowPath) { loadAndUpdateWebview(uri); } }); ``` --- ## 6. Execution Integration ### 6.1 Current Studio Execution Flow ```typescript // Studio.tsx const handleExecute = async () => { const response = await fetch('/api/execute', { method: 'POST', body: JSON.stringify({ tasks: usePlanStore.getState().tasks, parallelGroups: usePlanStore.getState().parallelGroups }) }); const { executionId } = await response.json(); navigate(`/execution/${executionId}`); }; ``` ### 6.2 VSCode Extension Execution Strategy **Option A: API Proxy (Recommended)** ```typescript // Extension host proxies to Mimir backend panel.webview.onDidReceiveMessage(async (message) => { if (message.command === 'executeWorkflow') { const config = getConfig(); const response = await fetch(`${config.apiUrl}/api/execute`, { method: 'POST', body: JSON.stringify(message.workflow) }); const { executionId } = await response.json(); // Open output channel for streaming logs const outputChannel = vscode.window.createOutputChannel('Mimir Execution'); outputChannel.show(); // Stream execution logs streamExecutionLogs(executionId, outputChannel); } }); ``` **Option B: Terminal Execution** ```typescript // Run mimir-execute via integrated terminal const terminal = vscode.window.createTerminal('Mimir'); terminal.sendText(`mimir-execute ${workflowPath}`); terminal.show(); ``` ### 6.3 Progress Reporting ```typescript // Use VSCode progress API await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: "Executing workflow", cancellable: true }, async (progress, token) => { // Update progress progress.report({ increment: 0, message: "Starting PM agent..." }); // Listen for cancellation token.onCancellationRequested(() => { cancelExecution(executionId); }); // Poll execution status await pollExecutionStatus(executionId, (status) => { progress.report({ increment: status.percentComplete, message: status.currentTask }); }); progress.report({ increment: 100, message: "Complete!" }); }); ``` --- ## 7. UI/UX Considerations ### 7.1 Theme Integration **VSCode Theme Variables:** ```css /* Use VSCode CSS variables */ :root { --vscode-editor-background: #1e1e1e; --vscode-editor-foreground: #d4d4d4; --vscode-button-background: #0e639c; --vscode-button-hoverBackground: #1177bb; } ``` **Adapt Norse Theme:** ```css /* Map Norse theme to VSCode variables */ .norse-night { background-color: var(--vscode-editor-background); } .valhalla-gold { color: var(--vscode-textLink-foreground); } .norse-rune { border-color: var(--vscode-panel-border); } ``` ### 7.2 Layout Strategy **Current Studio**: Full-page React app with header/sidebar/canvas **VSCode Extension Options:** **Option 1: Custom Editor (Recommended)** - Opens `.mimir-workflow` files in custom webview - Preserves file association - Appears in editor tabs like normal files ```typescript vscode.window.registerCustomEditorProvider('mimir.workflow', { async openCustomDocument(uri, openContext, token) { const content = await vscode.workspace.fs.readFile(uri); return { uri, content: JSON.parse(content.toString()) }; }, async resolveCustomEditor(document, webviewPanel, token) { // Set up webview with Studio UI webviewPanel.webview.html = getStudioHtml(); } }); ``` **Option 2: Sidebar Webview** - Always visible in sidebar - Persistent across file switches - Less screen real estate ```typescript vscode.window.registerWebviewViewProvider('mimir.studioView', { resolveWebviewView(webviewView, context, token) { webviewView.webview.html = getStudioHtml(); } }); ``` **Option 3: Panel Webview** - Opens as bottom panel (like terminal) - Collapsible - Good for quick access ### 7.3 Canvas Sizing ```tsx // Adapt to webview dimensions const [canvasSize, setCanvasSize] = useState({ width: 800, height: 600 }); useEffect(() => { const handleResize = () => { setCanvasSize({ width: window.innerWidth, height: window.innerHeight - 100 // Account for header }); }; window.addEventListener('resize', handleResize); handleResize(); return () => window.removeEventListener('resize', handleResize); }, []); ``` --- ## 8. Build System Integration ### 8.1 Separate Build for Webview **Project Structure:** ``` vscode-extension/ ├── src/ │ ├── extension.ts # Extension host code │ └── preambleManager.ts ├── webview-src/ │ ├── studio/ │ │ ├── main.tsx # Studio webview entry │ │ ├── components/ # Copy from frontend/src/components │ │ └── store/ # Copy from frontend/src/store │ └── tsconfig.json ├── package.json └── webpack.config.js ``` **Webpack Config for Webview:** ```javascript // webpack.config.js module.exports = [ // Extension host config (existing) { target: 'node', entry: './src/extension.ts', output: { path: path.resolve(__dirname, 'dist'), filename: 'extension.js' } }, // Webview config (new) { target: 'web', entry: './webview-src/studio/main.tsx', output: { path: path.resolve(__dirname, 'dist'), filename: 'studio.js' }, module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader' }, { test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] } ] } } ]; ``` ### 8.2 Shared Code Strategy **Option 1: Copy Components (Simple)** ```bash # Copy Studio components to extension cp -r frontend/src/components/ vscode-extension/webview-src/ cp -r frontend/src/store/ vscode-extension/webview-src/ ``` **Option 2: Symlink (Development)** ```bash # Symlink for development ln -s ../../frontend/src/components vscode-extension/webview-src/ ``` **Option 3: Workspace Packages (Production)** ```json // package.json (root) { "workspaces": [ "frontend", "vscode-extension", "shared-ui" ] } // shared-ui/package.json { "name": "@mimir/shared-ui", "main": "dist/index.js", "exports": { "./components": "./dist/components/index.js", "./store": "./dist/store/index.js" } } ``` --- ## 9. Testing Strategy ### 9.1 Unit Testing ```typescript // Extension tests (existing pattern) import * as assert from 'assert'; import * as vscode from 'vscode'; suite('Studio Extension Tests', () => { test('Opens workflow file in custom editor', async () => { const uri = vscode.Uri.file('/path/to/test.mimir-workflow'); await vscode.commands.executeCommand('vscode.openWith', uri, 'mimir.workflow'); const editor = vscode.window.activeTextEditor; assert.ok(editor); }); }); ``` ### 9.2 Webview Testing ```typescript // Mock VSCode API for React components const mockVSCode = { postMessage: jest.fn(), getState: jest.fn(() => ({})), setState: jest.fn() }; (global as any).acquireVsCodeApi = () => mockVSCode; // Test Studio components import { render, screen } from '@testing-library/react'; import { Studio } from './Studio'; test('renders task canvas', () => { render(<Studio />); expect(screen.getByText(/drag agents here/i)).toBeInTheDocument(); }); ``` ### 9.3 Integration Testing ```typescript // Test message passing suite('Extension-Webview Communication', () => { test('Saves workflow on message', async () => { const panel = await openStudioPanel(); // Simulate webview message await panel.webview.postMessage({ command: 'saveWorkflow', workflow: { tasks: [...] } }); // Verify file was created const uri = vscode.Uri.file('.vscode/mimir-workflows/test.json'); const exists = await fileExists(uri); assert.ok(exists); }); }); ``` --- ## 10. Implementation Roadmap ### Phase 1: Proof of Concept ✅ **COMPLETE** **Goals:** - [x] Research VSCode Webview API - [x] Create basic webview with React - [x] Test React DnD in webview - [x] Implement simple message passing **Tasks:** 1. [x] Set up webview build configuration (webpack dual-config) 2. [x] Create minimal Studio webview (1 component) 3. [x] Test drag-and-drop from palette to canvas 4. [x] Implement save/load workflow messages 5. [x] Document findings (STUDIO_POC.md) **Success Criteria:** - ✅ Can drag-drop agent cards (PM, Worker, QC) - ✅ Can save workflow to `.mimir-workflow` file - ✅ No major blockers identified - ✅ Unified extension architecture working - ✅ Build size: ~205 KiB total **Completed:** November 18, 2025 (~1 hour implementation time) **See:** `vscode-extension/STUDIO_POC.md` for full details ### Phase 2: Core Features (4 weeks) **Goals:** - [ ] Port all Studio components to webview - [ ] Implement file system integration - [ ] Add auto-save functionality - [ ] Create custom editor provider **Tasks:** 1. Copy Studio components to `webview-src/` 2. Adapt styling for VSCode themes 3. Implement workspace file picker 4. Add relative path conversion 5. Create `.mimir-workflow` file association 6. Implement auto-save with debouncing 7. Add file watcher for external changes **Success Criteria:** - Full Studio UI functional in VSCode - Can create complex workflows with parallel groups - Workflows saved to workspace automatically - Can open existing workflow files ### Phase 3: Execution Integration (3 weeks) **Goals:** - [ ] Integrate with Mimir backend - [ ] Stream execution logs to output channel - [ ] Add progress notifications - [ ] Implement cancellation **Tasks:** 1. Create execution API proxy 2. Add output channel for logs 3. Implement progress reporting 4. Add cancellation support 5. Create deliverables viewer 6. Add execution history panel **Success Criteria:** - Can execute workflows from extension - Live logs appear in output channel - Progress shown in notification - Can cancel running workflows - Can download deliverables ### Phase 4: Polish & Release (2 weeks) **Goals:** - [ ] Optimize performance - [ ] Add keyboard shortcuts - [ ] Write user documentation - [ ] Create demo videos **Tasks:** 1. Performance profiling and optimization 2. Add command palette commands 3. Create keyboard shortcut bindings 4. Write README and user guide 5. Record demo videos 6. Test on Windows/macOS/Linux 7. Publish to VSCode Marketplace **Success Criteria:** - Smooth performance with 20+ task workflows - All features keyboard accessible - Comprehensive documentation - Published extension available --- ## 11. Example Implementation ### 11.1 Extension Entry Point (Unified) ```typescript // vscode-extension/src/extension.ts (COMBINED CHAT + STUDIO) import * as vscode from 'vscode'; import { PreambleManager } from './preambleManager'; import { StudioPanel } from './studioPanel'; import { WorkflowEditorProvider } from './workflowEditor'; let preambleManager: PreambleManager; export function activate(context: vscode.ExtensionContext) { console.log('🚀 Mimir Extension activating (Chat + Studio)...'); // Get initial configuration const config = getConfig(); preambleManager = new PreambleManager(config.apiUrl); // ======================================== // FEATURE 1: CHAT PARTICIPANT (EXISTING) // ======================================== // Register chat participant (@mimir) const participant = vscode.chat.createChatParticipant('mimir.chat', async (request, context, response, token) => { try { await handleChatRequest(request, context, response, token); } catch (error: any) { response.markdown(`❌ Error: ${error.message}`); console.error('Chat request error:', error); } }); // Set participant icon const iconPath = vscode.Uri.joinPath(context.extensionUri, 'icon.png'); try { await vscode.workspace.fs.stat(iconPath); participant.iconPath = iconPath; } catch { console.log('ℹ️ No icon.png found, using default icon'); } context.subscriptions.push(participant); // ======================================== // FEATURE 2: STUDIO UI (NEW) // ======================================== // Register command to open Studio context.subscriptions.push( vscode.commands.registerCommand('mimir.openStudio', () => { StudioPanel.createOrShow(context.extensionUri, config.apiUrl); }) ); // Register custom editor for .mimir-workflow files context.subscriptions.push( vscode.window.registerCustomEditorProvider( 'mimir.workflow', new WorkflowEditorProvider(context, config.apiUrl), { webviewOptions: { retainContextWhenHidden: true }, supportsMultipleEditorsPerDocument: false } ) ); // Register webview serializer for persistence vscode.window.registerWebviewPanelSerializer('mimirStudio', { async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) { StudioPanel.revive(webviewPanel, context.extensionUri, state, config.apiUrl); } }); // ======================================== // SHARED CONFIGURATION // ======================================== // Listen for configuration changes (affects both features) context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(e => { if (e.affectsConfiguration('mimir')) { const newConfig = getConfig(); // Update chat preambles preambleManager.updateBaseUrl(newConfig.apiUrl); preambleManager.loadAvailablePreambles().then(preambles => { console.log(`🔄 Configuration updated, reloaded ${preambles.length} preambles`); }); // Notify open Studio panels of config change StudioPanel.updateAllPanels(newConfig); } }) ); console.log('✅ Mimir Extension activated (Chat + Studio)!'); } // Shared config getter (used by both features) function getConfig() { const config = vscode.workspace.getConfiguration('mimir'); return { apiUrl: config.get('apiUrl', 'http://localhost:3000'), defaultPreamble: config.get('defaultPreamble', 'mimir-v2'), model: config.get('model', 'gpt-4.1'), vectorSearchDepth: config.get('vectorSearch.depth', 1), vectorSearchLimit: config.get('vectorSearch.limit', 10), vectorSearchMinSimilarity: config.get('vectorSearch.minSimilarity', 0.8), enableTools: config.get('enableTools', true), maxToolCalls: config.get('maxToolCalls', 3), customPreamble: config.get('customPreamble', '') }; } // Chat handler (existing, unchanged) async function handleChatRequest( request: vscode.ChatRequest, context: vscode.ChatContext, response: vscode.ChatResponseStream, token: vscode.CancellationToken ) { // ... existing chat logic ... } export function deactivate() { console.log('👋 Mimir Extension deactivated (Chat + Studio)'); } ``` **Key Integration Points:** 1. **Shared Configuration**: Both chat and Studio use `getConfig()` 2. **Shared API URL**: Both features connect to same backend 3. **Independent Activation**: Chat and Studio activate separately but in one extension 4. **Unified Settings**: User configures once for both features 5. **Single Package**: One `package.json`, one marketplace listing ### 11.2 Studio Panel Manager ```typescript // vscode-extension/src/studioPanel.ts export class StudioPanel { public static currentPanel: StudioPanel | undefined; private readonly _panel: vscode.WebviewPanel; private _disposables: vscode.Disposable[] = []; public static createOrShow(extensionUri: vscode.Uri) { const column = vscode.window.activeTextEditor?.viewColumn; if (StudioPanel.currentPanel) { StudioPanel.currentPanel._panel.reveal(column); return; } const panel = vscode.window.createWebviewPanel( 'mimirStudio', 'Mimir Studio', column || vscode.ViewColumn.One, { enableScripts: true, retainContextWhenHidden: true, localResourceRoots: [ vscode.Uri.joinPath(extensionUri, 'dist') ] } ); StudioPanel.currentPanel = new StudioPanel(panel, extensionUri); } private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { this._panel = panel; this._panel.webview.html = this._getHtmlForWebview(this._panel.webview, extensionUri); this._panel.onDidDispose(() => this.dispose(), null, this._disposables); // Handle messages from webview this._panel.webview.onDidReceiveMessage( async (message) => { switch (message.command) { case 'saveWorkflow': await this._saveWorkflow(message.workflow); break; case 'loadWorkflow': await this._loadWorkflow(message.path); break; case 'pickFiles': await this._pickFiles(); break; case 'executeWorkflow': await this._executeWorkflow(message.workflow); break; } }, null, this._disposables ); } private _getHtmlForWebview(webview: vscode.Webview, extensionUri: vscode.Uri) { const scriptUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'dist', 'studio.js') ); const styleUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'dist', 'studio.css') ); const nonce = getNonce(); return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}';"> <link href="${styleUri}" rel="stylesheet"> <title>Mimir Studio</title> </head> <body> <div id="root"></div> <script nonce="${nonce}" src="${scriptUri}"></script> </body> </html>`; } private async _saveWorkflow(workflow: any) { const workspaceFolders = vscode.workspace.workspaceFolders; if (!workspaceFolders) { vscode.window.showErrorMessage('No workspace folder open'); return; } const workflowDir = vscode.Uri.joinPath( workspaceFolders[0].uri, '.vscode', 'mimir-workflows' ); // Create directory if it doesn't exist await vscode.workspace.fs.createDirectory(workflowDir); // Save workflow const workflowPath = vscode.Uri.joinPath( workflowDir, `${workflow.name || 'workflow'}.mimir-workflow` ); const content = JSON.stringify(workflow, null, 2); await vscode.workspace.fs.writeFile(workflowPath, Buffer.from(content)); vscode.window.showInformationMessage('Workflow saved!'); } public dispose() { StudioPanel.currentPanel = undefined; this._panel.dispose(); while (this._disposables.length) { const disposable = this._disposables.pop(); if (disposable) { disposable.dispose(); } } } } function getNonce() { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < 32; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } ``` ### 11.3 Webview React Entry ```tsx // vscode-extension/webview-src/studio/main.tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { Studio } from './Studio'; import './index.css'; // VSCode API singleton declare function acquireVsCodeApi(): any; const vscode = acquireVsCodeApi(); // Make vscode API available to components (window as any).vscode = vscode; // Render Studio const root = document.getElementById('root'); if (root) { ReactDOM.createRoot(root).render( <React.StrictMode> <DndProvider backend={HTML5Backend}> <Studio /> </DndProvider> </React.StrictMode> ); } ``` ### 11.4 Studio Component Adapter ```tsx // vscode-extension/webview-src/studio/Studio.tsx import React, { useEffect } from 'react'; import { TaskCanvas } from './components/TaskCanvas'; import { AgentPalette } from './components/AgentPalette'; import { TaskEditor } from './components/TaskEditor'; import { usePlanStore } from './store/planStore'; declare const vscode: any; export function Studio() { const { tasks, parallelGroups } = usePlanStore(); // Auto-save on state changes useEffect(() => { const timer = setTimeout(() => { vscode.postMessage({ command: 'saveWorkflow', workflow: { tasks, parallelGroups, name: 'Current Workflow', timestamp: new Date().toISOString() } }); }, 2000); return () => clearTimeout(timer); }, [tasks, parallelGroups]); // Listen for messages from extension useEffect(() => { const handleMessage = (event: MessageEvent) => { const message = event.data; switch (message.command) { case 'loadWorkflow': // Load workflow into store usePlanStore.setState({ tasks: message.workflow.tasks, parallelGroups: message.workflow.parallelGroups }); break; } }; window.addEventListener('message', handleMessage); return () => window.removeEventListener('message', handleMessage); }, []); const handleExecute = () => { vscode.postMessage({ command: 'executeWorkflow', workflow: { tasks, parallelGroups } }); }; return ( <div className="studio-container"> <div className="studio-header"> <h1>Mimir Studio</h1> <button onClick={handleExecute}>Execute Workflow</button> </div> <div className="studio-body"> <AgentPalette /> <TaskCanvas /> <TaskEditor /> </div> </div> ); } ``` --- ## 12. Comparison with Existing Solutions ### Direktiv VSCode Plugin **Source:** [Medium Article](https://medium.com/nerd-for-tech/direktiv-new-dev-environment-vscode-plugin-ab047b7a8266) **Key Features:** - Workflow linting - Remote execution - Workflow revisioning - Logs streaming **Similarities to Mimir:** - Workflow creation in IDE - Execution integration - Real-time feedback **Differences:** - Direktiv: Text-based YAML editing - Mimir: Visual drag-and-drop interface **Lesson Learned:** - ✅ Tight Git integration valuable - ✅ Real-time validation important - ✅ Remote execution model proven ### Control-M VSCode Extension **Source:** [GitHub Repository](https://github.com/controlm/ctm-vscode-extension) **Key Features:** - Visual job browser - Job creation and management - Code-first approach **Similarities:** - Job orchestration - Visual interface in VSCode **Lessons Learned:** - ✅ Tree view for job hierarchy works well - ✅ Command palette integration improves UX - ⚠️ Need good error handling for API failures --- ## 13. Risks & Mitigations | Risk | Probability | Impact | Mitigation | |------|-------------|---------|------------| | React DnD incompatibility | Low | High | Early POC testing verified compatibility | | Performance issues with large workflows | Medium | Medium | Virtualization, lazy loading, optimize re-renders | | File system race conditions | Medium | Medium | File locking, change detection, conflict resolution | | VSCode API breaking changes | Low | High | Pin VSCode engine version, test updates before adopting | | Webview CSP restrictions | Medium | Low | Use nonce-based CSP, load all resources from extension | | User confusion with two UIs | Low | Medium | Clear documentation, consistent behavior across UIs | --- ## 14. Success Metrics ### Technical Metrics - [ ] POC: Can drag-drop and save workflow (1 week) - [ ] Alpha: Full Studio ported to VSCode (6 weeks) - [ ] Beta: Execution integration working (9 weeks) - [ ] Release: Published to marketplace (11 weeks) ### User Metrics (Post-Launch) - Adoption rate: % of Mimir users installing extension - Engagement: Workflows created per user - Satisfaction: VSCode Marketplace ratings > 4.0 - Performance: Extension host activation time < 2s ### Quality Metrics - Zero P0 bugs in production - Test coverage > 80% - Load time for 50-task workflow < 1s - Auto-save latency < 100ms --- ## 15. Conclusion & Next Steps ### Key Takeaways 1. **Technically Feasible**: VSCode Webview API fully supports React + React DnD 2. **Architecture Proven**: Similar extensions (Direktiv, Control-M) validate approach 3. **Clear Path**: Phased implementation from POC to production 4. **Manageable Risks**: All identified risks have viable mitigations 5. **High Value**: Brings Studio to where developers already work ### Immediate Next Steps 1. **Start POC** (This Week): - Create basic webview with Studio header - Test React DnD in webview environment - Implement single-agent drag-drop - Document any unexpected blockers 2. **Architecture Review** (Next Week): - Review this document with team - Decide on state persistence strategy - Define file format for `.mimir-workflow` - Approve technology choices 3. **Resource Planning** (Next Week): - Assign developer(s) to project - Set up project tracking (Jira/GitHub Issues) - Define sprint goals for Phase 1 4. **Stakeholder Buy-In** (Next 2 Weeks): - Demo POC to stakeholders - Gather feedback on UX approach - Confirm go/no-go for full implementation ### Recommended Decision **PROCEED** with phased implementation: - Low technical risk (verified compatibility) - High user value (IDE-native experience) - Manageable scope (11-week timeline) - Reuses existing codebase (Studio components) --- ## Appendices ### A. Technology Stack Summary | Layer | Technology | Purpose | |-------|-----------|---------| | Extension Host | TypeScript + VSCode API | Extension logic, file I/O | | Webview UI | React 18 + TypeScript | Studio interface | | Drag-and-Drop | react-dnd + HTML5Backend | Workflow creation | | State Management | Zustand | Component state | | Styling | TailwindCSS | UI styling | | Build System | Webpack | Bundle webview assets | | Testing | Vitest + VSCode Test API | Unit + integration tests | ### B. Unified Extension Manifest ```json { "name": "mimir", "displayName": "Mimir - AI Assistant & Workflow Studio", "description": "Graph-RAG AI chat assistant with drag-and-drop multi-agent workflow builder", "version": "1.1.0", "publisher": "mimir", "engines": { "vscode": "^1.85.0" }, "categories": [ "AI", "Chat", "Visualization", "Other" ], "activationEvents": [ "onLanguage:*", "onCommand:mimir.openStudio", "onCustomEditor:mimir.workflow" ], "main": "./dist/extension.js", "contributes": { "chatParticipants": [ { "id": "mimir.chat", "name": "mimir", "description": "AI assistant with Graph-RAG memory and MCP tool access", "isSticky": true } ], "commands": [ { "command": "mimir.openStudio", "title": "Mimir: Open Workflow Studio", "icon": "$(graph)" }, { "command": "mimir.createWorkflow", "title": "Mimir: Create New Workflow", "icon": "$(add)" } ], "customEditors": [ { "viewType": "mimir.workflow", "displayName": "Mimir Workflow Designer", "selector": [ { "filenamePattern": "*.mimir-workflow" } ], "priority": "default" } ], "configuration": { "title": "Mimir", "properties": { "mimir.apiUrl": { "type": "string", "default": "http://localhost:3000", "description": "Mimir backend API URL (used by both chat and Studio)" }, "mimir.defaultPreamble": { "type": "string", "default": "mimir-v2", "description": "Default chat preamble/agent mode" }, "mimir.model": { "type": "string", "default": "gpt-4.1", "description": "Default LLM model for chat" }, "mimir.vectorSearch.enabled": { "type": "boolean", "default": true, "description": "Enable vector search for chat" }, "mimir.vectorSearch.limit": { "type": "number", "default": 10, "description": "Max vector search results" }, "mimir.vectorSearch.minSimilarity": { "type": "number", "default": 0.8, "description": "Minimum similarity threshold (0-1)" }, "mimir.vectorSearch.depth": { "type": "number", "default": 1, "description": "Graph traversal depth (1-3)" }, "mimir.studio.autoSave": { "type": "boolean", "default": true, "description": "Auto-save workflows while editing" }, "mimir.studio.autoSaveDelay": { "type": "number", "default": 2000, "description": "Auto-save delay in milliseconds" } } }, "menus": { "commandPalette": [ { "command": "mimir.openStudio", "when": "true" }, { "command": "mimir.createWorkflow", "when": "true" } ], "editor/title": [ { "command": "mimir.openStudio", "when": "resourceExtname == .mimir-workflow", "group": "navigation" } ] } }, "scripts": { "vscode:prepublish": "npm run build", "build": "webpack --mode production", "watch": "webpack --mode development --watch", "test": "node ./out/test/runTest.js" }, "dependencies": { "@vscode/webview-ui-toolkit": "^1.4.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "zustand": "^4.4.0" }, "devDependencies": { "@types/vscode": "^1.85.0", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "webpack": "^5.89.0", "ts-loader": "^9.5.0", "css-loader": "^6.8.0", "postcss-loader": "^7.3.0", "style-loader": "^3.3.0" } } ``` **Key Points:** 1. **Single Extension**: One `package.json` with both features 2. **Dual Activation**: Activates for chat participant AND custom editor 3. **Shared Configuration**: All settings under `mimir.*` namespace 4. **Command Palette**: Both chat and Studio accessible via commands 5. **File Association**: `.mimir-workflow` files open in custom editor ### C. Unified File Structure ``` vscode-extension/ ├── src/ # Extension host code (BOTH FEATURES) │ ├── extension.ts # ✨ Entry point (chat + studio) │ ├── preambleManager.ts # 💬 Chat: preamble loading │ ├── types.ts # 💬 Chat: type definitions │ ├── studioPanel.ts # 🎨 Studio: webview manager │ ├── workflowEditor.ts # 🎨 Studio: custom editor provider │ ├── workflowManager.ts # 🎨 Studio: file I/O operations │ └── executionManager.ts # 🎨 Studio: workflow execution │ ├── webview-src/ # Webview code (STUDIO ONLY) │ ├── studio/ │ │ ├── main.tsx # React entry point │ │ ├── Studio.tsx # Main Studio component │ │ ├── components/ # UI components (from frontend/) │ │ │ ├── TaskCanvas.tsx │ │ │ ├── AgentPalette.tsx │ │ │ ├── TaskEditor.tsx │ │ │ ├── TaskCard.tsx │ │ │ ├── ParallelGroupContainer.tsx │ │ │ └── AgentDragPreview.tsx │ │ ├── store/ │ │ │ └── planStore.ts # Zustand store (from frontend/) │ │ └── utils/ │ │ └── vscode.ts # VSCode API wrapper │ └── styles/ │ ├── index.css # Global styles │ └── tailwind.css # TailwindCSS (adapted for VSCode) │ ├── dist/ # Built extension │ ├── extension.js # Compiled extension host (chat + studio) │ ├── studio.js # Bundled webview (studio UI) │ └── studio.css # Webview styles │ ├── icon.png # Extension icon ├── package.json # ✨ Unified manifest (both features) ├── webpack.config.js # Build configuration (dual output) ├── tsconfig.json # TypeScript config └── README.md # Documentation (chat + studio) ``` **Key Differences from Separate Extensions:** 1. **Single `extension.ts`**: Activates both chat participant and Studio 2. **Shared Types**: Common types/interfaces used by both features 3. **Shared Config**: Single `getConfig()` function for both 4. **Dual Output**: Webpack builds both extension.js (host) and studio.js (webview) 5. **One Package**: Single npm package, single marketplace listing ### D. References 1. **VSCode Extension API** - [Official Documentation](https://code.visualstudio.com/api) - [Webview API Guide](https://code.visualstudio.com/api/extension-guides/webview) - [Custom Editor Guide](https://code.visualstudio.com/api/extension-guides/custom-editors) 2. **React DnD** - [Official Documentation](https://react-dnd.github.io/react-dnd/) - [HTML5Backend API](https://react-dnd.github.io/react-dnd/docs/backends/html5) 3. **VSCode Webview UI Toolkit** - [GitHub Repository](https://github.com/microsoft/vscode-webview-ui-toolkit) - [Component Storybook](https://microsoft.github.io/vscode-webview-ui-toolkit/) 4. **Example Extensions** - [Direktiv VSCode Plugin](https://medium.com/nerd-for-tech/direktiv-new-dev-environment-vscode-plugin-ab047b7a8266) - [Control-M Extension](https://github.com/controlm/ctm-vscode-extension) - [VSCode Extension Samples](https://github.com/microsoft/vscode-extension-samples) --- **Document Version:** 1.0.0 **Last Updated:** 2025-11-19 **Author:** Mimir Development Team **Status:** ✅ Complete Research - Ready for Implementation

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server