Summary
CodeNomad is currently a single-purpose coding assistant UI that runs OpenCode agents. This proposal requests a comprehensive plugin architecture that transforms CodeNomad into an extensible platform where developers can add custom UI panels, sidebars, visualizations, and custom AI agents without modifying CodeNomad's source code.
The core value proposition is simple: anyone should be able to create a plugin that adds new UI panels to CodeNomad - whether it's a research dashboard, data visualization panel, documentation viewer, or any other UI component - and share it with others.
This builds on existing infrastructure (SSE event channels, background processes, tool renderers) and introduces:
- A formal plugin manifest and loading system
- A UI extension API for custom panels, sidebars, and tool renderers
- A server-side API for custom agents, tools, and HTTP routes
- Plugin lifecycle management (install, enable, disable, uninstall)
This will enable use cases beyond coding: research assistants, data analysis tools, documentation writers, domain-specific AI helpers, and a thriving community ecosystem where users don't need to fork CodeNomad for niche needs.
User Goal
Enable the CodeNomad ecosystem to grow beyond coding by allowing developers to create plugins that:
UI Extensions (Primary Focus)
- Add custom panels - New tabbed panels in the right sidebar (like Changes, Files, Git tabs) for displaying plugin-specific content
- Add sidebar widgets - Expandable sections in the left sidebar for quick access to plugin features
- Custom tool renderers - Rich, interactive displays for custom tool outputs beyond plain text
- Custom visualizations - Charts, graphs, data tables, or any interactive UI for displaying agent results
- Menu items and commands - Add new actions to the command palette
- Keyboard shortcuts - Register custom hotkeys for plugin actions
Backend Extensions
- Custom AI agents - Define new agent types with custom system prompts (e.g., research agent, data analyst)
- Custom tools - Expose new capabilities to AI agents (web search, API calls, data processing)
- HTTP routes - Add custom API endpoints per workspace for external integrations
- Background processes - Run long-running tasks spawned by agents
Platform Features
- Plugin storage - Persist plugin-specific configuration and state
- Configuration UI - Built-in settings panel for configuring installed plugins
- Event system - React to CodeNomad events (messages, sessions, agent actions)
This will allow users to avoid forking CodeNomad for niche needs, foster a community-driven ecosystem, and position CodeNomad as a general-purpose AI assistant platform.
Expected Behavior
Users should be able to:
- Browse plugins - See available plugins in a plugin browser UI
- Install plugins - Add plugins to their CodeNomad with one click
- Use custom panels - See new tabs in the right panel (like "Research", "Data", "Notes")
- Interact with sidebar widgets - Access plugin features from the left sidebar
- Configure plugins - Modify plugin settings in a unified settings UI
- Use custom agents - Select plugin-provided agents when creating sessions
- Disable/uninstall plugins - Remove plugins without breaking CodeNomad
Developers should be able to:
- Create UI panels - Add new tabbed panels to CodeNomad without touching source code
- Create sidebar widgets - Add expandable sections to the left sidebar
- Build custom tool renderers - Display tool outputs as rich interactive components
- Define agents - Create new AI agent types with custom prompts
- Register tools - Expose new capabilities to any agent
- Add HTTP routes - Create API endpoints for external integrations
- Persist data - Store plugin configuration and state
Technical Context
Click to expand - Existing Infrastructure
Server-Side Infrastructure (Already Exists)
| Component |
Location |
Status |
| Plugin Event Channel |
packages/server/src/plugins/channel.ts |
✅ Implemented |
| Plugin Event Handlers |
packages/server/src/plugins/handlers.ts |
✅ Implemented |
| Plugin Routes |
packages/server/src/server/routes/plugin.ts |
✅ Implemented |
| Background Processes |
packages/server/src/background-processes/ |
✅ Implemented |
| Built-in Plugin |
packages/opencode-config/plugin/ |
✅ Implemented |
| Event Bus |
packages/server/src/events/bus.ts |
✅ Implemented |
UI-Side Infrastructure (Already Exists)
| Component |
Location |
Status |
| Tool Renderer Registry |
packages/ui/src/components/tool-call/renderers/index.ts |
✅ Implemented |
| Keyboard Registry |
packages/ui/src/lib/keyboard-registry.ts |
✅ Implemented |
| SSE Event Manager |
packages/ui/src/lib/sse-manager.ts |
✅ Implemented |
| Message Store Hooks |
packages/ui/src/stores/message-v2/instance-store.ts |
✅ Implemented |
| Message Store Bus |
packages/ui/src/stores/message-v2/bus.ts |
✅ Implemented |
Critical Gaps Identified
- No plugin manifest format - Plugins lack declarative metadata
- No plugin discovery/loading - Only one hardcoded built-in plugin exists
- No tool registration API - Plugins cannot expose new tools to AI agents
- No plugin lifecycle management - No install/unload/disable/enable functionality
- No plugin storage - No dedicated plugin configuration/state storage
- No UI extension API - No formal way to add custom panels, sidebars, or widgets
- No plugin-to-plugin communication - Isolated plugins cannot interact
Click to expand - Current UI Architecture + Plugin Integration Points
Current CodeNomad UI Architecture
Understanding the current structure is essential to see where custom panels would integrate:
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ CODE NOMAD UI │
│ ┌─────────────────────────────────────────────────────────────────────────────────┐ │
│ │ TOP BAR (Title, Actions) │ │
│ └─────────────────────────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────┬────────────────────────────────────────┬───────────────────────┐ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ LEFT SIDEBAR │ MAIN CONTENT AREA │ RIGHT PANEL │ │
│ │ │ │ │ │
│ │ ┌────────────┐ │ ┌──────────────────────────────────┐ │ ┌─────────────────┐ │ │
│ │ │ Sessions │ │ │ │ │ │ [Changes] │ │ │
│ │ │ List │ │ │ │ │ │ [Git Changes] │ │ │
│ │ │ │ │ │ Agent Messages / Chat │ │ │ [Files] │ │ │
│ │ │ - Session1 │ │ │ │ │ │ [Status] │ │ │
│ │ │ - Session2 │ │ │ (Tool calls, responses, │ │ │ │ │ │
│ │ │ - Session3 │ │ │ user inputs appear here) │ │ │ ┌─────────────┐│ │ │
│ │ │ │ │ │ │ │ │ │ ││ │ │
│ │ └────────────┘ │ │ │ │ │ │ TAB CONTENT ││ │ │
│ │ │ │ │ │ │ │ ││ │ │
│ │ ┌────────────┐ │ │ │ │ │ │ ││ │ │
│ │ │ Controls: │ │ └──────────────────────────────────┘ │ │ │ └─────────────┘│ │ │
│ │ │ - Agent │ │ │ │ │ │ │ │
│ │ │ - Model │ │ ┌──────────────────────────────────┐ │ │ │ │ │ │
│ │ │ - Thinking │ │ │ PROMPT INPUT │ │ │ └─────────────────┘ │ │
│ │ └────────────┘ │ │ ┌────────────────────────────┐ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ └────────────────────────────┘ │ │ │ │ │
│ │ │ │ │ │ │ │ │
│ └──────────────────┴────────────────────────────────────────┴───────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────────────┘
Current Right Panel Structure (Hardcoded Tabs)
The right panel currently has 4 hardcoded tabs - this is the primary integration point for custom panels:
┌─────────────────────────────────────────────────────┐
│ RIGHT PANEL │
│ ┌───────────────────────────────────────────────┐ │
│ │ [Changes] [Git Changes] [Files] [Status] │ │ ◄── Tab strip (hardcoded)
│ ├───────────────────────────────────────────────┤ │
│ │ │ │
│ │ ACTIVE TAB CONTENT │ │ ◄── Only one shows at a time
│ │ │ │
│ │ - ChangesTab │ │
│ │ - FilesTab │ │
│ │ - GitChangesTab │ │
│ │ - StatusTab │ │
│ │ │ │
│ └───────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Current Code Structure (What Needs to Change)
The current implementation in RightPanel.tsx uses hardcoded strings and components:
// Types are hardcoded - this is the key blocker:
type RightPanelTab = "changes" | "git-changes" | "files" | "status"
// Tabs are hardcoded buttons in the JSX:
<button onClick={() => setRightPanelTab("changes")}>Changes</button>
<button onClick={() => setRightPanelTab("files")}>Files</button>
<button onClick={() => setRightPanelTab("git-changes")}>Git</button>
<button onClick={() => setRightPanelTab("status")}>Status</button>
// Tab content is hardcoded with Show components:
<Show when={rightPanelTab() === "changes"}><ChangesTab /></Show>
<Show when={rightPanelTab() === "files"}><FilesTab /></Show>
<Show when={rightPanelTab() === "git-changes"}><GitChangesTab /></Show>
<Show when={rightPanelTab() === "status"}><StatusTab /></Show>
Proposed Plugin Integration Points
After implementing plugin support, the structure would become:
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ CODE NOMAD UI (WITH PLUGINS) │
│ ┌─────────────────────────────────────────────────────────────────────────────────┐ │
│ │ TOP BAR (Title, Actions) │ │
│ └─────────────────────────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────┬────────────────────────────────────────┬───────────────────────┐ │
│ │ │ │ │ │
│ │ LEFT SIDEBAR │ MAIN CONTENT AREA │ RIGHT PANEL │ │
│ │ │ │ │ │
│ │ ┌────────────┐ │ ┌──────────────────────────────────┐ │ ┌─────────────────┐ │ │
│ │ │ Sessions │ │ │ │ │ │ [Changes] │ │ │
│ │ │ List │ │ │ │ │ │ [Git Changes] │ │ │
│ │ │ │ │ │ Agent Messages / Chat │ │ │ [Files] │ │ │
│ │ │ - Session1 │ │ │ │ │ │ [Status] │ │ │
│ │ │ - Session2 │ │ │ (Tool calls, responses, │ │ │ ─────────────── │ │ │
│ │ │ - Session3 │ │ │ user inputs appear here) │ │ │ [🔬 Research] │ │ │
│ │ │ │ │ │ │ │ │ [📊 Data] │ │ │
│ │ └────────────┘ │ │ │ │ │ [📝 Notes] │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ ┌────────────┐ │ └──────────────────────────────────┘ │ │ │ ┌─────────────┐│ │ │
│ │ │ Controls: │ │ │ │ │ │ ││ │ │
│ │ │ - Agent │ │ ┌──────────────────────────────────┐ │ │ │ │ RESEARCH ││ │ │
│ │ │ - Model │ │ │ PROMPT INPUT │ │ │ │ │ PANEL ││ │ │
│ │ │ - Thinking │ │ └──────────────────────────────────┘ │ │ │ │ ││ │ │
│ │ └────────────┘ │ │ │ │ └─────────────┘│ │ │
│ │ │ │ │ │ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │ │ │ │ │
│ │ │ PLUGINS COULD ADD WIDGETS HERE │ │ ◄── NEW: Sidebar widgets │ │
│ │ │ (e.g., Quick Search, Recent Files) │ │ │ │ │ │
│ │ └──────────────────────────────────────────┘ │ │ │ │ │
│ │ │ │ │ │ │ │
│ └──────────────────┴────────────────────────────┴──────────────┴─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────────────┘
Plugin Panel Integration Flow
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ PLUGIN PANEL INTEGRATION FLOW │
└─────────────────────────────────────────────────────────────────────────────────────┘
1. PLUGIN DEFINES PANEL IN MANIFEST
┌────────────────────────────────────────────────────────────────┐
│ research-plugin/plugin.json │
│ { │
│ "name": "research-assistant", │
│ "contributes": { │
│ "panels": [ │
│ { │
│ "id": "research-dashboard", │
│ "title": "Research", │
│ "icon": "🔬" │
│ } │
│ ] │
│ } │
│ } │
└────────────────────────────────────────────────────────────────┘
│
▼
2. PLUGIN REGISTERS PANEL COMPONENT AT RUNTIME
┌────────────────────────────────────────────────────────────────┐
│ research-plugin/src/ui/panel.tsx │
│ │
│ import { UIPanel } from '@codenomad/ui-plugin-sdk' │
│ │
│ export const researchPanel: UIPanel = { │
│ id: 'research-dashboard', │
│ title: 'Research', │
│ icon: '🔬', │
│ render: () => <ResearchDashboard />, │
│ } │
│ │
│ codenomad.ui.panels.register(researchPanel) │
└────────────────────────────────────────────────────────────────┘
│
▼
3. RIGHT PANEL DYNAMICALLY LOADS FROM REGISTRY
┌────────────────────────────────────────────────────────────────┐
│ RightPanel.tsx (MODIFIED) │
│ │
│ // Dynamic tab loading from registry │
│ const tabs = usePanelRegistry() // Returns all registered │
│ │
│ // Tab buttons rendered dynamically │
│ <For each={tabs()}> │
│ {(tab) => <TabButton tab={tab} />} │
│ </For> │
│ │
│ // Active content resolved from registry │
│ {tabs().find(t => t.id === activeTab())?.render()} │
└────────────────────────────────────────────────────────────────┘
│
▼
4. USER SEES NEW TAB IN RIGHT PANEL
┌────────────────────────────────────────────────────────────────┐
│ RIGHT PANEL │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Changes] [Files] [Git] [Status] [🔬 Research] [📊 Data]│ │
│ ├──────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Research Panel Content │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ 🔍 Search: "neural architecture search" │ │ │
│ │ ├────────────────────────────────────────────────────┤ │ │
│ │ │ 📄 Paper 1: "Neural Architecture Search..." │ │ │
│ │ │ Authors: Smith et al. | Year: 2024 │ │ │
│ │ │ ┌────────────────────────────────────────────┐ │ │ │
│ │ │ │ [Save] [Fetch] [Cite] │ │ │ │
│ │ │ └────────────────────────────────────────────┘ │ │ │
│ │ ├────────────────────────────────────────────────────┤ │ │
│ │ │ 📄 Paper 2: "Efficient Neural..." │ │ │
│ │ │ Authors: Chen et al. | Year: 2023 │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
Sidebar Widget Integration (Future Enhancement)
┌─────────────────────────────────────────────────────────────────┐
│ LEFT SIDEBAR WITH WIDGETS │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ CodeNomad [+] [📌] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🔍 Quick Search [▼] │ │ ◄── Plugin widget
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ Search papers... │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ SESSIONS [+ New] [Search] │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 💬 Session 1 │ │
│ │ 💬 Session 2 │ │
│ │ 💬 Session 3 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ACTIVE SESSION │
│ Agent: [Claude 3.5] ▼ │
│ Model: [Sonnet] ▼ │
│ Thinking: [Enabled] ▼ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🔬 Recent Papers [▶] │ │ ◄── Plugin widget
│ │ • Neural Architecture Search (2024) │ │
│ │ • Efficient Transformers (2023) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Technical Changes Required
| Location |
Current (Hardcoded) |
After (Dynamic) |
types.ts |
type RightPanelTab = "changes" | "files" | ... |
type RightPanelTab = string or discriminated union |
RightPanel.tsx |
<Show when={tab() === "changes"}> |
tabRegistry.get(activeTab())?.render() |
| Tab buttons |
Hardcoded <button>Changes</button> |
<For each={tabs()}> loop |
| Tab state |
Single rightPanelTab signal |
Per-panel state managed in registry |
Click to expand - Server-Side API
Server-Side Plugin API
Plugin Manifest (plugin.json)
{
"name": "research-assistant",
"version": "1.0.0",
"description": "AI-powered research assistant for academic literature review",
"author": {
"name": "CodeNomad Community",
"url": "https://github.com/codenomad"
},
"engines": {
"codenomad": ">=1.0.0 <2.0.0"
},
"contributes": {
"agents": [
{
"id": "researcher",
"name": "Research Assistant",
"description": "Helps find, analyze, and synthesize academic literature",
"systemPrompt": "You are a research assistant..."
}
],
"tools": [
{
"name": "web_search",
"description": "Search the web for relevant information",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"numResults": { "type": "number", "default": 10 }
},
"required": ["query"]
}
}
],
"routes": [
{
"method": "GET",
"path": "/research/:paperId",
"handler": "handlers/getPaper"
}
]
},
"permissions": [
"storage:read",
"storage:write",
"network:http",
"process:spawn"
]
}
Plugin Interface
// Core plugin interface
interface CodeNomadPlugin {
// Metadata
name: string
version: string
description?: string
// Contributions
agents?: AgentDefinition[]
tools?: ToolDefinition[]
routes?: RouteDefinition[]
// Lifecycle hooks
onLoad?(context: PluginContext): Promise<void>
onUnload?(): Promise<void>
onEnable?(): Promise<void>
onDisable?(): Promise<void>
}
// Context provided to plugins
interface PluginContext {
// Workspace identification
workspaceId: string
workspacePath: string
// Storage API
storage: PluginStorage
// Event API
events: PluginEventBus
// Background processes
processes: BackgroundProcessManager
// Configuration
config: PluginConfigManager
// Logging
logger: PluginLogger
// HTTP client for external APIs
http: HttpClient
}
// Storage API
interface PluginStorage {
get<T>(key: string): Promise<T | undefined>
set<T>(key: string, value: T): Promise<void>
delete(key: string): Promise<void>
list(prefix?: string): Promise<string[]>
}
// Event API for inter-plugin communication
interface PluginEventBus {
emit(event: string, data: unknown): void
on(event: string, handler: (data: unknown) => void): () => void
off(event: string, handler: (data: unknown) => void): void
}
Agent Definition
interface AgentDefinition {
id: string
name: string
description: string
systemPrompt: string
capabilities?: string[]
defaultModel?: string
temperature?: number
maxTokens?: number
}
Tool Definition
interface ToolDefinition {
name: string
description: string
inputSchema: JSONSchema
handler: ToolHandler
}
type ToolHandler = (input: unknown, context: ToolContext) => Promise<ToolResult>
interface ToolContext {
workspaceId: string
sessionId: string
userId?: string
logger: PluginLogger
}
Route Definition
interface RouteDefinition {
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
path: string
handler: string // Reference to exported handler function
options?: {
auth?: boolean
schema?: FastifySchema
}
}
Click to expand - UI Extension API
UI Extension API
The primary focus of this plugin system is enabling developers to add custom UI to CodeNomad without modifying its source code. This section details exactly what UI extensions plugins can provide.
Custom Panels (Right Sidebar)
The right sidebar in CodeNomad currently has tabs like "Changes", "Files", "Git Changes", "Status". Plugins should be able to add new tabs to this panel.
// Panel definition - appears as new tab in right sidebar
interface UIPanel {
// Unique identifier for the panel
id: string
// Display title shown in tab
title: string
// Icon shown in tab (emoji or icon name)
icon?: string
// Optional badge number (like notification count)
badge?: () => number | undefined
// Render function - SolidJS component
render: () => JSX.Element
// Panel toolbar actions
actions?: UIPanelAction[]
// Called when panel becomes visible
onMount?: () => void
// Called when panel is hidden
onUnmount?: () => void
// Called when panel receives new data from agent
onData?: (data: unknown) => void
}
interface UIPanelAction {
id: string
label: string
icon?: string
onClick: () => void
}
// Registration API
codenomad.ui.panels.register(panel: UIPanel): void
codenomad.ui.panels.unregister(panelId: string): void
// Example: Research panel would register like this
codenomad.ui.panels.register({
id: 'research-dashboard',
title: 'Research',
icon: '🔍',
render: () => <ResearchDashboard />,
actions: [
{ id: 'new-search', label: 'New Search', icon: '🔎', onClick: () => startNewSearch() }
]
})
Example Use Cases:
- Research plugin: Papers list, citation manager, notes panel
- Data analysis plugin: Query results table, visualization charts
- Documentation plugin: Generated docs viewer, API reference
- Notes plugin: Quick notes panel, markdown editor
Sidebar Widgets (Left Sidebar)
The left sidebar (session list) should accept widget plugins that appear as expandable sections.
// Sidebar widget - appears in left sidebar
interface SidebarWidget {
id: string
title: string
icon?: string
// Position in sidebar
position: 'top' | 'bottom'
// Sort order within position
priority?: number
// Initial expanded state
defaultExpanded?: boolean
// Render the widget content
render: () => JSX.Element
// Optional: actions in widget header
headerActions?: WidgetAction[]
}
interface WidgetAction {
id: string
icon: string
tooltip: string
onClick: () => void
}
// Registration
codenomad.ui.sidebar.register(widget: SidebarWidget): void
codenomad.ui.sidebar.unregister(widgetId: string): void
codenomad.ui.sidebar.setExpanded(widgetId: string, expanded: boolean): void
Example Use Cases:
- Quick search widget for research plugin
- Recent files widget
- Active background processes widget
- Plugin status/indicator widget
Custom Tool Renderers
When an AI agent uses a custom tool, plugins can provide rich renderers for the output (beyond plain text).
// Tool renderer - custom display for tool outputs
interface ToolRenderer {
// Tools this renderer handles (e.g., ["web_search", "url_fetch"])
tools: string[]
// Optional: dynamic title based on tool input
getTitle?: (context: ToolRendererContext) => string | undefined
// Optional: action button (e.g., "View Details")
getAction?: (context: ToolRendererContext) => string | undefined
// Render the main tool output body
renderBody: (context: ToolRendererContext) => JSX.Element | null
// Optional: compact summary version
renderSummary?: (context: ToolRendererContext) => JSX.Element | null
// Optional: full-screen or expanded view
renderExpanded?: (context: ToolRendererContext) => JSX.Element | null
}
interface ToolRendererContext {
tool: Tool
toolName: string
input: unknown
output: unknown
message: Message
instanceId: string
sessionId: string
}
// Registration
codenomad.ui.tools.registerRenderer(renderer: ToolRenderer): void
codenomad.ui.tools.unregisterRenderer(toolName: string): void
// Example: Research plugin would render search results
codenomad.ui.tools.registerRenderer({
tools: ['research_web_search'],
getTitle: (ctx) => `🔍 Search: ${(ctx.input as any).query}`,
renderBody: (ctx) => {
const results = (ctx.output as SearchResult[]);
return <SearchResultsList results={results} />
}
})
Example Use Cases:
- Search results as clickable cards
- Data tables with sorting/filtering
- Image galleries for vision tool outputs
- Code syntax highlighting
- Interactive charts/graphs
Command Palette Commands
Plugins can add commands that appear in the command palette.
// Command for command palette
interface PluginCommand {
id: string
label: string
category?: string // e.g., "Research", "Files"
icon?: string
keybinding?: string // e.g., "ctrl+shift+r"
// Enable/disable based on context
enabled?: () => boolean
// Execute the command
execute: () => void | Promise<void>
}
// Registration
codenomad.ui.commands.register(command: PluginCommand): void
codenomad.ui.commands.unregister(commandId: string): void
// Example
codenomad.ui.commands.register({
id: 'research.new-search',
label: 'Research: New Search',
category: 'Research',
keybinding: 'ctrl+shift+r',
execute: () => openSearchPanel()
})
Keyboard Shortcuts
interface KeyboardShortcut {
id: string
key: string
modifiers?: ('ctrl' | 'shift' | 'alt' | 'meta')[]
context?: 'global' | 'input' | 'messages'
description?: string
action: () => void
condition?: () => boolean
}
codenomad.ui.shortcuts.register(shortcut: KeyboardShortcut): void
codenomad.ui.shortcuts.unregister(shortcutId: string): void
Reacting to Agent Events
Plugins should be able to react when agents send messages, request permissions, etc.
// Available events in UI
interface UIEventMap {
// Message events
'message:send': (message: string) => void
'message:receive': (message: Message) => void
'message:part': (part: MessagePart) => void
'tool:call': (tool: ToolCall) => void
'tool:result': (toolName: string, result: unknown) => void
// Session events
'session:create': (session: Session) => void
'session:switch': (sessionId: string) => void
'session:complete': (session: Session) => void
// Agent events
'agent:thinking': (thinking: string) => void
'agent:error': (error: Error) => void
// UI events
'panel:open': (panelId: string) => void
'panel:close': (panelId: string) => void
'panel:data': (panelId: string, data: unknown) => void
// System events
'theme:change': (theme: 'light' | 'dark') => void
'instance:connect': (instanceId: string) => void
'instance:disconnect': (instanceId: string) => void
}
// Subscribe to events
codenomad.ui.events.on<K extends keyof UIEventMap>(
event: K,
handler: UIEventMap[K]
): () => void // Returns unsubscribe function
Sending Data to UI Panels
Plugins should be able to send data to their UI panels.
// Send data from server-side plugin to UI panel
codenomad.ui.panels.sendData(panelId: string, data: unknown): void
// Example: Research agent sends search results to Research panel
// Server-side:
agent.on('search_complete', (results) => {
codenomad.ui.panels.sendData('research-dashboard', results)
})
// UI side:
codenomad.ui.panels.register({
id: 'research-dashboard',
// ...
onData: (data) => {
// Update panel state with new data
setResults(data as SearchResult[])
}
})
Configuration UI
Plugins can define settings that appear in CodeNomad's settings panel.
interface PluginSetting {
key: string
type: 'string' | 'number' | 'boolean' | 'select' | 'array' | 'object'
label: string
description?: string
default?: unknown
// For select type
options?: { label: string; value: unknown }[]
// For object type - nested schema
schema?: JSONSchema
// Validation
validate?: (value: unknown) => string | null // Error message or null if valid
}
codenomad.ui.settings.register(pluginId: string, settings: PluginSetting[]): void
codenomad.ui.settings.get<T>(key: string): T
codenomad.ui.settings.set<T>(key: string, value: T): void
Theme Integration
Plugins should integrate with CodeNomad's theme system.
// Access current theme
codenomad.ui.theme.get(): 'light' | 'dark'
// Subscribe to theme changes
codenomad.ui.theme.onChange((theme) => {
// Update plugin UI accordingly
})
// Use theme colors in plugin UI
// CodeNomad provides CSS custom properties:
// - var(--cn-bg-primary)
// - var(--cn-bg-secondary)
// - var(--cn-text-primary)
// - var(--cn-border-color)
// etc.
Dialogs and Modals
// Show dialog
codenomad.ui.dialogs.show(options: {
title: string
content: JSX.Element | string
confirmLabel?: string
cancelLabel?: string
type?: 'info' | 'warning' | 'error'
}): Promise<boolean>
// Show notification/toast
codenomad.ui.notifications.show(options: {
message: string
type?: 'info' | 'success' | 'warning' | 'error'
duration?: number // ms
})
Communication with Server-Side Plugin
// UI plugin can communicate with its server-side counterpart
codenomad.plugin.invoke<T>(method: string, args?: unknown): Promise<T>
// Example: Research panel calls server-side to fetch paper details
const paper = await codenomad.plugin.invoke<Paper>('getPaper', { id: paperId })
// Server-side handler:
plugin.onInvoke('getPaper', async (args) => {
return await fetchPaperDetails(args.id)
})
Click to expand - Research Use Case
Research Use Case - Concrete Example
To validate the plugin architecture, this section outlines a Research Assistant Plugin - this is the primary use case driving this feature request. The goal is to enable research workflows within CodeNomad without forking the codebase.
What Researchers Need
| Capability |
UI Component |
Description |
| Search the web |
Custom tool |
Agent can search for papers, articles |
| Fetch URLs |
Custom tool |
Extract content from web pages |
| View papers |
Custom panel |
List of saved papers with metadata |
| Take notes |
Custom panel |
Notes attached to each paper |
| Citations |
Custom panel |
Generate citation strings |
| Analysis |
Custom tool |
Summarize, extract key findings |
Plugin UI Components
Research Panel (Right Sidebar)
The main panel researchers would interact with:
// research-plugin/src/ui/ResearchPanel.tsx
import { UIPanel } from '@codenomad/ui-plugin-sdk'
export const researchPanel: UIPanel = {
id: 'research-dashboard',
title: 'Research',
icon: '🔬',
badge: () => unreadPapersCount(),
render: () => (
<div class="research-panel">
<div class="search-bar">
<Input
value={query()}
onInput={setQuery}
placeholder="Search for papers..."
onEnter={() => agent.search(query())}
/>
<Button onClick={() => agent.search(query())}>Search</Button>
</div>
<Tabs
tabs={[
{ id: 'papers', label: 'Papers', content: <PapersList /> },
{ id: 'notes', label: 'Notes', content: <NotesPanel /> },
{ id: 'citations', label: 'Citations', content: <CitationsPanel /> }
]}
/>
</div>
),
actions: [
{ id: 'new-search', label: 'New Search', icon: '🔎', onClick: () => startSearch() },
{ id: 'export', label: 'Export All', icon: '📤', onClick: () => exportAll() }
],
onMount: () => {
// Load saved papers when panel opens
loadPapers()
}
}
Search Results Tool Renderer
When the research agent performs a search, results appear as interactive cards:
// research-plugin/src/ui/SearchResultsRenderer.tsx
import { ToolRenderer } from '@codenomad/ui-plugin-sdk'
export const searchResultsRenderer: ToolRenderer = {
tools: ['research_web_search'],
getTitle: (ctx) => `🔍 Search: "${(ctx.input as SearchInput).query}"`,
getAction: (ctx) => 'View All',
renderBody: (ctx) => {
const results = ctx.output as SearchResult[]
return (
<div class="search-results">
<For each={results}>
{(result) => (
<PaperCard
title={result.title}
authors={result.authors}
year={result.year}
abstract={result.snippet}
onSave={() => savePaper(result)}
onFetch={() => fetchFullPaper(result.url)}
/>
)}
</For>
</div>
)
},
renderSummary: (ctx) => {
const count = (ctx.output as SearchResult[]).length
return <span>{count} results found</span>
}
}
Sidebar Widget
Quick access from the left sidebar:
// research-plugin/src/ui/ResearchWidget.tsx
import { SidebarWidget } from '@codenomad/ui-plugin-sdk'
export const researchWidget: SidebarWidget = {
id: 'research-quick',
title: 'Research',
icon: '🔬',
position: 'top',
priority: 10,
defaultExpanded: false,
render: () => (
<div class="research-widget">
<div class="quick-search">
<Input
placeholder="Quick search..."
onEnter={(q) => quickSearch(q)}
/>
</div>
<div class="recent-papers">
<span>Recent:</span>
<For each={recentPapers()}>
{(paper) => (
<Button
variant="ghost"
onClick={() => selectPaper(paper.id)}
>
{paper.title}
</Button>
)}
</For>
</div>
</div>
)
}
User Flow
- Install: User runs
codenomad plugin install research or clicks "Install" in plugin browser
- Configure: User sets API keys for search services in plugin settings
- Panel appears: "Research" tab appears in right panel alongside "Changes", "Files"
- Research: User asks "Find recent papers on neural architecture search"
- Agent uses tools: Research agent calls
research_web_search, results appear as cards
- Results in UI: Papers appear in Research panel, can save, take notes
- Citations: Generate citation for saved papers
Server-Side Components
// research-plugin/src/index.ts
import { CodeNomadPlugin } from '@codenomad/server-plugin-sdk'
export const researchPlugin: CodeNomadPlugin = {
name: 'research-assistant',
version: '1.0.0',
// Tools exposed to the agent
tools: [
webSearchTool,
urlFetchTool,
analyzePaperTool,
generateCitationTool
],
// Custom research agent
agents: [
{
id: 'researcher',
name: 'Research Assistant',
description: 'Helps find, analyze, and synthesize academic literature',
systemPrompt: `You are a research assistant...`
}
],
// HTTP routes for external integrations
routes: [
{ method: 'GET', path: '/papers', handler: 'handlers/listPapers' },
{ method: 'POST', path: '/papers', handler: 'handlers/savePaper' }
],
async onLoad(ctx) {
// Initialize storage, connect to external APIs
}
}
Click to expand - Implementation Plan
Implementation Plan
The primary focus of this plugin system is UI extensibility - allowing developers to add custom panels without modifying CodeNomad source code. The backend support is secondary but enables powerful agent capabilities.
Phase 1: Foundation (Server + Plugin Infrastructure)
Goal: Establish core plugin infrastructure
| Task |
Description |
Files to Modify |
| Define plugin manifest schema |
Create JSON schema for plugin.json |
New: packages/server/src/plugins/manifest.ts |
| Create plugin loader |
Scan and load plugins from ./plugins folder |
New: packages/server/src/plugins/loader.ts |
| Implement plugin registry |
Track loaded plugins and their capabilities |
New: packages/server/src/plugins/registry.ts |
| Add plugin lifecycle |
Load/unload/enable/disable |
New: packages/server/src/plugins/lifecycle.ts |
| Add plugin storage |
Dedicated key-value storage per plugin |
New: packages/server/src/plugins/storage.ts |
Estimated Effort: 1-2 weeks
Phase 2: UI Extension Infrastructure (PRIMARY FOCUS)
Goal: Enable UI panels, sidebars, and tool renderers
| Task |
Description |
Files to Modify |
| Create UI plugin SDK |
Expose UI APIs to plugins |
New: packages/ui/src/lib/plugin-sdk.ts |
| Implement panel registry |
Allow adding custom panels to right sidebar |
New: packages/ui/src/stores/panels.ts |
| Extend right panel |
Support dynamic tab types (not hardcoded) |
Modify: packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx |
| Implement sidebar widgets |
Allow adding sidebar content |
Modify: packages/ui/src/components/instance/shell/SessionSidebar.tsx |
| Tool renderer registry |
Formalize existing tool renderer system |
Modify: packages/ui/src/components/tool-call/renderers/index.ts |
| Command palette |
Allow plugins to register commands |
Modify: packages/ui/src/stores/commands.ts |
| Plugin settings UI |
Configure installed plugins |
New: Settings panel for plugins |
Estimated Effort: 2-3 weeks
This is the key phase that enables the main value proposition: adding UI panels without source code.
Phase 3: Server-Side Tools & Agents
Goal: Enable custom tools and agents via plugins
| Task |
Description |
Files to Modify |
| Define tool registration API |
Allow plugins to register tools |
Modify: packages/server/src/plugins/api.ts |
| Define agent interface |
Agent definition structure |
New: packages/server/src/plugins/agent.ts |
| Agent spawner |
Create agent instances from plugins |
Modify: Agent creation flow |
| Agent registry |
Track available agents |
New: packages/server/src/plugins/agent-registry.ts |
| Update UI agent picker |
Show plugin agents in UI |
Modify: packages/ui/src/components/session/ |
| Background process integration |
Plugins can spawn long-running tasks |
Modify: packages/server/src/background-processes/manager.ts |
Estimated Effort: 1-2 weeks
Phase 4: Developer Experience
Goal: Make it easy to create plugins
| Task |
Description |
Files to Modify |
| Create plugin template |
codenomad plugin init command |
New: CLI command |
| Plugin CLI tools |
Install, list, enable, disable |
New: CLI commands |
| Documentation |
Plugin development guide |
New: Docs |
| VS Code extension |
Snippets for plugin development |
Future |
Estimated Effort: 1 week
Phase 5: Research Plugin (Reference Implementation)
Goal: Build the first comprehensive plugin as validation
| Task |
Description |
| Implement web search tool |
Using external search API |
| Implement URL fetch tool |
Content extraction |
| Create research agent |
Specialized system prompt |
| Build research panel |
Papers list, notes, citations in right sidebar |
| Build sidebar widget |
Quick search in left sidebar |
| Create search result renderer |
Rich card display for search results |
| Test end-to-end |
Full user flow |
Estimated Effort: 2-3 weeks
Total Estimated Timeline
| Phase |
Duration |
Focus |
| Phase 1: Foundation |
1-2 weeks |
Server infrastructure |
| Phase 2: UI Extensions |
2-3 weeks |
PRIMARY FOCUS |
| Phase 3: Tools & Agents |
1-2 weeks |
Server agents |
| Phase 4: Developer Experience |
1 week |
DX |
| Phase 5: Research Plugin |
2-3 weeks |
Validation |
| Total |
7-11 weeks |
|
Click to expand - Alternatives Considered
Alternatives Considered
1. MCP (Model Context Protocol) Integration
Description: Use Anthropic's MCP as the plugin protocol instead of custom JSON-RPC
Pros:
- Industry standard gaining traction
- Built-in tool definitions and schema
- Ecosystem of existing MCP servers
Cons:
- Designed for Claude Desktop, not general-purpose
- Less flexible for UI extensions
- Would require significant abstraction layer
Decision: Rejected - MCP is too specific to Claude's tool calling model. CodeNomad should define its own protocol that works with any LLM/agent.
2. VS Code Extension-like Architecture
Description: Model plugins after VS Code extensions with extension host process
Pros:
- Proven architecture with large ecosystem
- Mature APIs for UI and backend
- Well-documented patterns
Cons:
- Heavy complexity for initial implementation
- Requires significant infrastructure (extension host, API surface)
- Overkill for CodeNomad's scale
Decision: Deferred - Consider in v2. Start with simpler process-based plugins first.
3. npm-Based Distribution Only
Description: Only support npm packages for plugin distribution
Pros:
- Familiar developer experience
- Built-in versioning and dependency management
- npm registry infrastructure
Cons:
- Barriers to entry (npm account, publishing)
- No local development workflow
- Security concerns with npm packages
Decision: Rejected - Support local folder plugins for development, add npm support later.
4. WebView-Only UI Extensions
Description: Only allow UI extensions via WebViews (no direct component API)
Pros:
- Complete isolation (security)
- Flexibility for plugin developers
- No coupling to CodeNomad internals
Cons:
- Limited integration (can't add to existing panels easily)
- Harder to build cohesive experience
- Communication overhead
Decision: Rejected - Offer both WebView panels AND direct component API for different use cases.
5. No Plugin System - Feature Requests Instead
Description: Handle all requests via GitHub issues and core team implementation
Pros:
- Simpler initial development
- Full control over quality
- No maintenance burden
Cons:
- Slow feature velocity
- Limits ecosystem growth
- Forces forking for niche needs
- Community cannot contribute
Decision: Rejected - Does not align with goal of building an extensible platform.
Click to expand - Open Questions
Open Questions
1. Plugin Distribution
Q: Should plugins be distributed as npm packages, local folders, or both?
Options:
- Local folders only - Simplest, good for development, no registry needed
- npm packages - Better for distribution, requires publishing workflow
- Both - Local for development, npm for distribution
Recommendation: Start with local folders, add npm support in Phase 4.
2. Version Compatibility
Q: Should plugins declare compatible CodeNomad versions?
Options:
- No version checking - Risk of broken plugins
- Semver range in manifest - Like VS Code's
engines.vscode
- Runtime capability checks - More flexible but complex
Recommendation: Use semver ranges like VS Code (engines.codenomad: ">=1.0.0 <2.0.0").
3. Security/Sandboxing
Q: Should plugins run in isolated processes from day 1?
Options:
- Same process - Simpler, faster, but risky
- Separate processes - More secure, more complex
- Tiered - Built-in plugins same process, external isolated
Recommendation: Start simple (same process) with clear documentation, add isolation in v2.
4. Research Agent Specifics
Q: What specific research capabilities should the reference plugin include?
Options:
- Basic - Web search, URL fetch, notes
- Comprehensive - + citation management, PDF parsing, literature synthesis
- Minimal MVP - Just agent + one tool, UI later
Recommendation: Build comprehensive to validate full plugin capabilities.
5. Plugin-to-Plugin Communication
Q: Should plugins be able to communicate with each other?
Options:
- No isolation - Each plugin independent
- Event bus only - Loose coupling via events
- Direct API - Plugins can call each other's exposed APIs
Recommendation: Start with event bus only, add direct API if needed.
6. Marketplace
Q: Should CodeNomad have an official plugin marketplace?
Options:
- No - Just local/folder distribution
- Yes - Official marketplace with review process
- Third-party - Let community build marketplace
Recommendation: Defer to v2. Start with documentation and GitHub-based distribution.
Click to expand - File References
File References
UI Files (PRIMARY FOCUS - Custom Panels)
These files need modification to enable custom panels without source code changes:
| File |
Reason |
packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx |
KEY - Currently hardcoded tabs; needs dynamic tab registry |
packages/ui/src/components/instance/shell/SessionSidebar.tsx |
KEY - Needs widget registry for sidebar |
packages/ui/src/components/tool-call/renderers/index.ts |
Already has renderer registry; formalize as plugin API |
packages/ui/src/stores/commands.ts |
Needs command registry for plugin commands |
packages/ui/src/stores/preferences.tsx |
Add plugin settings section |
packages/ui/src/lib/keyboard-registry.ts |
Already exists; formalize as plugin API |
Server Files (Backend Support)
| File |
Reason |
packages/server/src/plugins/channel.ts |
Already implements plugin event channel |
packages/server/src/plugins/handlers.ts |
Already implements plugin event handlers |
packages/server/src/server/routes/plugin.ts |
Plugin route definitions |
packages/server/src/server/http-server.ts |
Plugin route registration |
packages/server/src/background-processes/manager.ts |
Background process management |
packages/server/src/events/bus.ts |
Event bus for inter-plugin communication |
New Files to Create
| File |
Purpose |
packages/server/src/plugins/manifest.ts |
Plugin manifest schema |
packages/server/src/plugins/loader.ts |
Plugin discovery and loading |
packages/server/src/plugins/registry.ts |
Plugin registry |
packages/server/src/plugins/api.ts |
Plugin API surface |
packages/server/src/plugins/storage.ts |
Plugin storage |
packages/server/src/plugins/agent.ts |
Agent definitions |
packages/ui/src/lib/plugin-sdk.ts |
KEY - UI plugin SDK |
packages/ui/src/stores/panels.ts |
KEY - Panel registry |
packages/opencode-config/plugin/ (new) |
Research plugin |
Key Technical Changes Required
- RightPanel.tsx - Replace hardcoded tab list with dynamic registry:
// BEFORE (hardcoded)
const tabs = ['changes', 'files', 'git', 'status']
// AFTER (dynamic)
const tabs = usePanelRegistry() // Returns registered panels
- SessionSidebar.tsx - Add widget section:
// Add widget area
<div class="sidebar-widgets">
<For each={useWidgetRegistry()}>
{(widget) => <WidgetPanel widget={widget} />}
</For>
</div>
- New panels.ts - Panel registry store:
// New file
const [panels, setPanels] = createSignal<Map<string, UIPanel>>(new Map())
export function registerPanel(panel: UIPanel) { ... }
export function unregisterPanel(id: string) { ... }
export function usePanels() { return panels() }
Suggested Solutions
Near-term (Phase 1-3)
- Start with local folder plugins only, no npm distribution initially
- Use semver ranges for version compatibility
- Run plugins in same process initially (document security implications)
- Build comprehensive research plugin to validate architecture
- Add plugin-to-plugin events via existing EventBus
Future (v2+)
- Add npm package support for plugins
- Implement process isolation for untrusted plugins
- Create official marketplace with review process
- Consider VS Code-like extension host for better isolation
- Add plugin signatures and verification
Appendix A: Example Plugin Structure
my-plugin/
├── plugin.json # Manifest
├── package.json # Node package (optional)
├── src/
│ ├── index.ts # Plugin entry
│ ├── agents/ # Custom agents
│ ├── tools/ # Custom tools
│ ├── routes/ # HTTP handlers
│ └── ui/ # UI components
├── dist/ # Compiled JS
└── README.md # Documentation
Appendix B: SDK Packages
// Server-side plugin SDK
@codenomad/server-plugin-sdk
├── types/manifest.ts // Manifest types
├── types/plugin.ts // Plugin interface
├── types/agent.ts // Agent types
├── types/tool.ts // Tool types
├── api/
│ ├── storage.ts // Storage API
│ ├── events.ts // Event API
│ ├── processes.ts // Process API
│ └── http.ts // HTTP client
// UI-side plugin SDK
@codenomad/ui-plugin-sdk
├── types/panel.ts // Panel types
├── types/widget.ts // Widget types
├── api/
│ ├── ui.ts // UI API
│ ├── events.ts // Event API
│ ├── shortcuts.ts // Shortcut API
│ └── settings.ts // Settings API
Summary
CodeNomad is currently a single-purpose coding assistant UI that runs OpenCode agents. This proposal requests a comprehensive plugin architecture that transforms CodeNomad into an extensible platform where developers can add custom UI panels, sidebars, visualizations, and custom AI agents without modifying CodeNomad's source code.
The core value proposition is simple: anyone should be able to create a plugin that adds new UI panels to CodeNomad - whether it's a research dashboard, data visualization panel, documentation viewer, or any other UI component - and share it with others.
This builds on existing infrastructure (SSE event channels, background processes, tool renderers) and introduces:
This will enable use cases beyond coding: research assistants, data analysis tools, documentation writers, domain-specific AI helpers, and a thriving community ecosystem where users don't need to fork CodeNomad for niche needs.
User Goal
Enable the CodeNomad ecosystem to grow beyond coding by allowing developers to create plugins that:
UI Extensions (Primary Focus)
Backend Extensions
Platform Features
This will allow users to avoid forking CodeNomad for niche needs, foster a community-driven ecosystem, and position CodeNomad as a general-purpose AI assistant platform.
Expected Behavior
Users should be able to:
Developers should be able to:
Technical Context
Click to expand - Existing Infrastructure
Server-Side Infrastructure (Already Exists)
packages/server/src/plugins/channel.tspackages/server/src/plugins/handlers.tspackages/server/src/server/routes/plugin.tspackages/server/src/background-processes/packages/opencode-config/plugin/packages/server/src/events/bus.tsUI-Side Infrastructure (Already Exists)
packages/ui/src/components/tool-call/renderers/index.tspackages/ui/src/lib/keyboard-registry.tspackages/ui/src/lib/sse-manager.tspackages/ui/src/stores/message-v2/instance-store.tspackages/ui/src/stores/message-v2/bus.tsCritical Gaps Identified
Click to expand - Current UI Architecture + Plugin Integration Points
Current CodeNomad UI Architecture
Understanding the current structure is essential to see where custom panels would integrate:
Current Right Panel Structure (Hardcoded Tabs)
The right panel currently has 4 hardcoded tabs - this is the primary integration point for custom panels:
Current Code Structure (What Needs to Change)
The current implementation in
RightPanel.tsxuses hardcoded strings and components:Proposed Plugin Integration Points
After implementing plugin support, the structure would become:
Plugin Panel Integration Flow
Sidebar Widget Integration (Future Enhancement)
Technical Changes Required
types.tstype RightPanelTab = "changes" | "files" | ...type RightPanelTab = stringor discriminated unionRightPanel.tsx<Show when={tab() === "changes"}>tabRegistry.get(activeTab())?.render()<button>Changes</button><For each={tabs()}>looprightPanelTabsignalClick to expand - Server-Side API
Server-Side Plugin API
Plugin Manifest (
plugin.json){ "name": "research-assistant", "version": "1.0.0", "description": "AI-powered research assistant for academic literature review", "author": { "name": "CodeNomad Community", "url": "https://github.com/codenomad" }, "engines": { "codenomad": ">=1.0.0 <2.0.0" }, "contributes": { "agents": [ { "id": "researcher", "name": "Research Assistant", "description": "Helps find, analyze, and synthesize academic literature", "systemPrompt": "You are a research assistant..." } ], "tools": [ { "name": "web_search", "description": "Search the web for relevant information", "inputSchema": { "type": "object", "properties": { "query": { "type": "string" }, "numResults": { "type": "number", "default": 10 } }, "required": ["query"] } } ], "routes": [ { "method": "GET", "path": "/research/:paperId", "handler": "handlers/getPaper" } ] }, "permissions": [ "storage:read", "storage:write", "network:http", "process:spawn" ] }Plugin Interface
Agent Definition
Tool Definition
Route Definition
Click to expand - UI Extension API
UI Extension API
The primary focus of this plugin system is enabling developers to add custom UI to CodeNomad without modifying its source code. This section details exactly what UI extensions plugins can provide.
Custom Panels (Right Sidebar)
The right sidebar in CodeNomad currently has tabs like "Changes", "Files", "Git Changes", "Status". Plugins should be able to add new tabs to this panel.
Example Use Cases:
Sidebar Widgets (Left Sidebar)
The left sidebar (session list) should accept widget plugins that appear as expandable sections.
Example Use Cases:
Custom Tool Renderers
When an AI agent uses a custom tool, plugins can provide rich renderers for the output (beyond plain text).
Example Use Cases:
Command Palette Commands
Plugins can add commands that appear in the command palette.
Keyboard Shortcuts
Reacting to Agent Events
Plugins should be able to react when agents send messages, request permissions, etc.
Sending Data to UI Panels
Plugins should be able to send data to their UI panels.
Configuration UI
Plugins can define settings that appear in CodeNomad's settings panel.
Theme Integration
Plugins should integrate with CodeNomad's theme system.
Dialogs and Modals
Communication with Server-Side Plugin
Click to expand - Research Use Case
Research Use Case - Concrete Example
To validate the plugin architecture, this section outlines a Research Assistant Plugin - this is the primary use case driving this feature request. The goal is to enable research workflows within CodeNomad without forking the codebase.
What Researchers Need
Plugin UI Components
Research Panel (Right Sidebar)
The main panel researchers would interact with:
Search Results Tool Renderer
When the research agent performs a search, results appear as interactive cards:
Sidebar Widget
Quick access from the left sidebar:
User Flow
codenomad plugin install researchor clicks "Install" in plugin browserresearch_web_search, results appear as cardsServer-Side Components
Click to expand - Implementation Plan
Implementation Plan
The primary focus of this plugin system is UI extensibility - allowing developers to add custom panels without modifying CodeNomad source code. The backend support is secondary but enables powerful agent capabilities.
Phase 1: Foundation (Server + Plugin Infrastructure)
Goal: Establish core plugin infrastructure
plugin.jsonpackages/server/src/plugins/manifest.ts./pluginsfolderpackages/server/src/plugins/loader.tspackages/server/src/plugins/registry.tspackages/server/src/plugins/lifecycle.tspackages/server/src/plugins/storage.tsEstimated Effort: 1-2 weeks
Phase 2: UI Extension Infrastructure (PRIMARY FOCUS)
Goal: Enable UI panels, sidebars, and tool renderers
packages/ui/src/lib/plugin-sdk.tspackages/ui/src/stores/panels.tspackages/ui/src/components/instance/shell/right-panel/RightPanel.tsxpackages/ui/src/components/instance/shell/SessionSidebar.tsxpackages/ui/src/components/tool-call/renderers/index.tspackages/ui/src/stores/commands.tsEstimated Effort: 2-3 weeks
This is the key phase that enables the main value proposition: adding UI panels without source code.
Phase 3: Server-Side Tools & Agents
Goal: Enable custom tools and agents via plugins
packages/server/src/plugins/api.tspackages/server/src/plugins/agent.tspackages/server/src/plugins/agent-registry.tspackages/ui/src/components/session/packages/server/src/background-processes/manager.tsEstimated Effort: 1-2 weeks
Phase 4: Developer Experience
Goal: Make it easy to create plugins
codenomad plugin initcommandEstimated Effort: 1 week
Phase 5: Research Plugin (Reference Implementation)
Goal: Build the first comprehensive plugin as validation
Estimated Effort: 2-3 weeks
Total Estimated Timeline
Click to expand - Alternatives Considered
Alternatives Considered
1. MCP (Model Context Protocol) Integration
Description: Use Anthropic's MCP as the plugin protocol instead of custom JSON-RPC
Pros:
Cons:
Decision: Rejected - MCP is too specific to Claude's tool calling model. CodeNomad should define its own protocol that works with any LLM/agent.
2. VS Code Extension-like Architecture
Description: Model plugins after VS Code extensions with extension host process
Pros:
Cons:
Decision: Deferred - Consider in v2. Start with simpler process-based plugins first.
3. npm-Based Distribution Only
Description: Only support npm packages for plugin distribution
Pros:
Cons:
Decision: Rejected - Support local folder plugins for development, add npm support later.
4. WebView-Only UI Extensions
Description: Only allow UI extensions via WebViews (no direct component API)
Pros:
Cons:
Decision: Rejected - Offer both WebView panels AND direct component API for different use cases.
5. No Plugin System - Feature Requests Instead
Description: Handle all requests via GitHub issues and core team implementation
Pros:
Cons:
Decision: Rejected - Does not align with goal of building an extensible platform.
Click to expand - Open Questions
Open Questions
1. Plugin Distribution
Q: Should plugins be distributed as npm packages, local folders, or both?
Options:
Recommendation: Start with local folders, add npm support in Phase 4.
2. Version Compatibility
Q: Should plugins declare compatible CodeNomad versions?
Options:
engines.vscodeRecommendation: Use semver ranges like VS Code (
engines.codenomad: ">=1.0.0 <2.0.0").3. Security/Sandboxing
Q: Should plugins run in isolated processes from day 1?
Options:
Recommendation: Start simple (same process) with clear documentation, add isolation in v2.
4. Research Agent Specifics
Q: What specific research capabilities should the reference plugin include?
Options:
Recommendation: Build comprehensive to validate full plugin capabilities.
5. Plugin-to-Plugin Communication
Q: Should plugins be able to communicate with each other?
Options:
Recommendation: Start with event bus only, add direct API if needed.
6. Marketplace
Q: Should CodeNomad have an official plugin marketplace?
Options:
Recommendation: Defer to v2. Start with documentation and GitHub-based distribution.
Click to expand - File References
File References
UI Files (PRIMARY FOCUS - Custom Panels)
These files need modification to enable custom panels without source code changes:
packages/ui/src/components/instance/shell/right-panel/RightPanel.tsxpackages/ui/src/components/instance/shell/SessionSidebar.tsxpackages/ui/src/components/tool-call/renderers/index.tspackages/ui/src/stores/commands.tspackages/ui/src/stores/preferences.tsxpackages/ui/src/lib/keyboard-registry.tsServer Files (Backend Support)
packages/server/src/plugins/channel.tspackages/server/src/plugins/handlers.tspackages/server/src/server/routes/plugin.tspackages/server/src/server/http-server.tspackages/server/src/background-processes/manager.tspackages/server/src/events/bus.tsNew Files to Create
packages/server/src/plugins/manifest.tspackages/server/src/plugins/loader.tspackages/server/src/plugins/registry.tspackages/server/src/plugins/api.tspackages/server/src/plugins/storage.tspackages/server/src/plugins/agent.tspackages/ui/src/lib/plugin-sdk.tspackages/ui/src/stores/panels.tspackages/opencode-config/plugin/(new)Key Technical Changes Required
Suggested Solutions
Near-term (Phase 1-3)
Future (v2+)
Appendix A: Example Plugin Structure
Appendix B: SDK Packages