Plugin-inspiration.md 37 KB

OpenCode Plugin Architecture & oh-my-opencode Deep Dive

A comprehensive guide to understanding OpenCode's plugin system and how oh-my-opencode transforms a single-agent assistant into a multi-model orchestration powerhouse.

Table of Contents

  1. OpenCode Plugin System Overview (#1-opencode-plugin-system-overview)
  2. The @opencode-ai/plugin Package (#2-the-opencode-aiplugin-package)
  3. oh-my-opencode Architecture (#3-oh-my-opencode-architecture)
  4. The Agent Orchestration System (#4-the-agent-orchestration-system)
  5. Background Agent System (#5-background-agent-system)
  6. The Hook Pipeline (#6-the-hook-pipeline)
  7. Custom Tools (#7-custom-tools)
  8. MCP Integrations (#8-mcp-integrations)
  9. Configuration System (#9-configuration-system)
  10. Plugin Loading & Updates (#10-plugin-loading--updates)
  11. Creating Your Own Plugin (#11-creating-your-own-plugin)
  12. Key Files Reference (#12-key-files-reference) ---
  13. OpenCode Plugin System Overview What is OpenCode? OpenCode is a terminal-based AI coding assistant (similar to Claude Code) created by SST. It's highly extensible through a plugin system. Repository: https://github.com/sst/opencode How Plugins Work Plugins are JavaScript/TypeScript modules that:
  14. Receive a context object with SDK client, project info, and utilities
  15. Return a hooks object to extend OpenCode's behavior Plugin locations:
  16. .opencode/plugin/ (project-level)
  17. ~/.config/opencode/plugin/ (global)
  18. npm packages via opencode.json The Plugin Interface // From @opencode-ai/plugin export type PluginInput = { client: ReturnType // SDK client for API calls project: Project // Current project info directory: string // Working directory path worktree: string // Git worktree path $: BunShell // Bun's shell API for commands } export type Plugin = (input: PluginInput) => Promise ---
  19. The @opencode-ai/plugin Package Core Types Source: packages/plugin/src/index.ts in sst/opencode export interface Hooks { // Event subscription - react to 40+ event types event?: (input: { event: Event }) => Promise
  20. // Configuration hook - modify agents, MCPs, commands config?: (input: Config) => Promise

    // Custom tools registration tool?: {

    [key: string]: ToolDefinition
    

    }

    // Authentication provider auth?: AuthHook

    // Chat lifecycle hooks "chat.message"?: (input, output: { message: UserMessage; parts: Part[] }) => Promise "chat.params"?: (input, output: { temperature: number; topP: number; topK: number; options: Record }) => Promise

    // Permission hooks "permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise

    // Tool execution hooks "tool.execute.before"?: (input: { tool: string; sessionID: string; callID: string }, output: { args: any }) => Promise "tool.execute.after"?: (input: { tool: string; sessionID: string; callID: string }, output: { title: string; output: string; metadata: any }) => Promise

    // Experimental hooks "experimental.chat.messages.transform"?: (input, output: { messages: { info: Message; parts: Part[] }[] }) => Promise "experimental.chat.system.transform"?: (input, output: { system: string[] }) => Promise "experimental.text.complete"?: (input: { sessionID: string; messageID: string; partID: string }, output: { text: string }) => Promise } Tool Definition export type ToolContext = { sessionID: string messageID: string agent: string abort: AbortSignal } export function tool(input: { description: string args: Args execute(args: z.infer>, context: ToolContext): Promise }) { return input } tool.schema = z // Zod schema builder Available Events (40+) | Category | Events | |----------|--------| | Command | command.executed | | File | file.edited, file.watcher.updated | | Installation | installation.updated | | LSP | lsp.client.diagnostics, lsp.updated | | Message | message.part.removed, message.part.updated, message.removed, message.updated | | Permission | permission.replied, permission.updated | | Server | server.connected | | Session | session.created, session.compacted, session.deleted, session.diff, session.error, session.idle, session.status, session.updated | | Todo | todo.updated | | Tool | tool.execute.after, tool.execute.before |

    | TUI | tui.prompt.append, tui.command.execute, tui.toast.show |

    1. oh-my-opencode Architecture What is oh-my-opencode? Repository: https://github.com/code-yeongyu/oh-my-opencode oh-my-opencode is a comprehensive OpenCode plugin that transforms the single-agent experience into a multi-model, multi-agent orchestration system. It's described as "oh-my-zsh for OpenCode." Project Structure oh-my-opencode/ ├── src/ │ ├── agents/ # AI agents (OmO, oracle, librarian, explore, etc.) │ │ ├── omo.ts │ │ ├── oracle.ts │ │ ├── librarian.ts │ │ ├── explore.ts │ │ ├── frontend-ui-ux-engineer.ts │ │ ├── document-writer.ts │ │ ├── multimodal-looker.ts │ │ ├── index.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── hooks/ # 21 lifecycle hooks │ │ ├── anthropic-auto-compact/ │ │ ├── auto-update-checker/ │ │ ├── background-notification/ │ │ ├── claude-code-hooks/ │ │ ├── comment-checker/ │ │ ├── directory-readme-injector/ │ │ ├── interactive-bash-session/ │ │ ├── keyword-detector/ │ │ ├── non-interactive-env/ │ │ ├── rules-injector/ │ │ ├── session-recovery/ │ │ ├── think-mode/ │ │ ├── context-window-monitor.ts │ │ ├── empty-task-response-detector.ts │ │ ├── grep-output-truncator.ts │ │ ├── session-notification.ts │ │ ├── todo-continuation-enforcer.ts │ │ ├── tool-output-truncator.ts │ │ └── index.ts │ ├── tools/ # Custom tools (LSP, AST-Grep, background tasks) │ │ ├── lsp/ │ │ ├── ast-grep/ │ │ ├── background-task/ │ │ ├── call-omo-agent/ │ │ ├── look-at/ │ │ ├── grep/ │ │ ├── glob/ │ │ ├── interactive-bash/ │ │ ├── skill/ │ │ ├── slashcommand/ │ │ └── index.ts │ ├── mcp/ # MCP server configurations │ │ ├── context7.ts │ │ ├── grep-app.ts │ │ ├── websearch-exa.ts │ │ └── index.ts │ ├── features/ # Core features │ │ ├── background-agent/ │ │ ├── claude-code-*-loader/ │ │ ├── hook-message-injector/ │ │ └── terminal/ │ ├── auth/ # Google Antigravity OAuth │ ├── shared/ # Utilities │ ├── config/ # Zod schema, types │ └── index.ts # Main plugin entry ├── package.json └── tsconfig.json Main Entry Point (src/index.ts) The plugin implements ALL available hooks: const OhMyOpenCodePlugin: Plugin = async (ctx) => { // 1. Load configuration const pluginConfig = loadPluginConfig(ctx.directory);

    // 2. Initialize 20+ hooks with enable/disable support const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer")

    ? createTodoContinuationEnforcer(ctx) : null;
    

    const contextWindowMonitor = isHookEnabled("context-window-monitor")

    ? createContextWindowMonitorHook(ctx) : null;
    

    // ... 18 more hooks

    // 3. Initialize background agent manager const backgroundManager = new BackgroundManager(ctx);

    // 4. Create tools const backgroundTools = createBackgroundTools(backgroundManager, ctx.client); const callOmoAgent = createCallOmoAgent(ctx, backgroundManager); const lookAt = createLookAt(ctx);

    return {

    // Authentication
    auth: googleAuthHooks?.auth,
    
    // Custom tools
    tool: {
      ...builtinTools,        // LSP, AST-grep, grep, glob
      ...backgroundTools,      // background_task, background_output, background_cancel
      call_omo_agent,
      look_at,
      interactive_bash,
    },
    
    // Config modification
    config: async (config) => {
      // Inject custom agents
      config.agent = { OmO: ..., oracle: ..., ... };
      // Inject MCPs
      config.mcp = { context7: ..., websearch_exa: ..., grep_app: ... };
      // Load Claude Code commands, skills, agents
    },
    
    // Event handling
    event: async (input) => {
      // Process through all 20+ hooks
    },
    
    // Tool interception
    "tool.execute.before": async (input, output) => { ... },
    "tool.execute.after": async (input, output) => { ... },
    
    // Chat hooks
    "chat.message": async (input, output) => { ... },
    "experimental.chat.messages.transform": async (input, output) => { ... },
    

    };

    };

    1. The Agent Orchestration System The Agent Team oh-my-opencode creates a team of specialized agents, each using the optimal model for their task: | Agent | Model | Purpose | Mode | |-------|-------|---------|------| | OmO | anthropic/claude-opus-4-5 | Primary orchestrator, delegates work | primary | | oracle | openai/gpt-5.2 | Architecture, debugging, deep reasoning | subagent | | librarian | anthropic/claude-sonnet-4-5 | External docs, GitHub examples | subagent | | explore | opencode/grok-code | Fast codebase search | subagent | | frontend-ui-ux-engineer | google/gemini-3-pro-preview | UI generation | subagent | | document-writer | google/gemini-3-pro-preview | Documentation | subagent | | multimodal-looker | google/gemini-2.5-flash | PDF/image analysis | subagent | OmO Agent System Prompt (Key Excerpts) Source: src/agents/omo.ts The OmO agent has a 400+ line system prompt that teaches it to: const OMO_SYSTEM_PROMPT = ` You are OmO, the orchestrator agent for OpenCode. Identity: Elite software engineer working at SF, Bay Area. You work, delegate, verify, deliver. Operating Mode: You NEVER work alone when specialists are available. Frontend work → delegate. Deep research → parallel background agents. Complex architecture → consult Oracle.

      Phase 0 - Intent Gate (EVERY message)

      Step 1: Classify Request Type

      Type Signal Action
      Trivial Single file, known location Direct tools only, no agents
      Explicit Specific file/line, clear command Execute directly
      Exploratory "How does X work?" Assess scope, then search
      Open-ended "Improve", "Refactor" Assess codebase first

      Phase 2A - Exploration & Research

      Tool Selection:

      Tool Cost When to Use
      grep, glob, lsp_*, ast_grep FREE Always try first
      explore agent CHEAP Multiple search angles
      librarian agent CHEAP External docs, GitHub examples
      oracle agent EXPENSIVE Architecture, review, debugging

      Parallel Execution (DEFAULT behavior)

      // CORRECT: Always background, always parallel background_task(agent="explore", prompt="Find auth implementations...") background_task(agent="librarian", prompt="Find JWT best practices...") // Continue working immediately. Collect with background_output when needed.

      Phase 2B - Implementation

      GATE: Frontend Files (HARD BLOCK)

      Extension Action
      .tsx, .jsx DELEGATE
      .vue, .svelte DELEGATE
      .css, .scss DELEGATE

      ALL frontend = DELEGATE to frontend-ui-ux-engineer. Period.

      export const omoAgent: AgentConfig = { description: "Powerful AI orchestrator for OpenCode...", mode: "primary", model: "anthropic/claude-opus-4-5", thinking: { type: "enabled", budgetTokens: 32000, // Extended thinking enabled }, maxTokens: 64000, prompt: OMO_SYSTEM_PROMPT, color: "#00CED1", } Oracle Agent (Strategic Advisor) Source: src/agents/oracle.ts export const oracleAgent: AgentConfig = { description: "Expert technical advisor with deep reasoning...", mode: "subagent", model: "openai/gpt-5.2", temperature: 0.1, reasoningEffort: "medium", textVerbosity: "high", tools: { write: false, edit: false, task: false, background_task: false }, prompt:You are a strategic technical advisor...

    Decision Framework

    Bias toward simplicity: The right solution is typically the least complex one. Leverage what exists: Favor modifications to current code over new components. One clear path: Present a single primary recommendation. Signal the investment: Tag recommendations with Quick(<1h), Short(1-4h), Medium(1-2d), Large(3d+). `, } How Agents Get Injected In the config hook: config: async (config) => { const builtinAgents = createBuiltinAgents(...);

    // OmO becomes primary, original agents become subagents config.agent = {

    OmO: builtinAgents.OmO,                        // NEW primary
    "OmO-Plan": omoPlanConfig,                      // NEW plan variant
    ...builtinAgents,                               // oracle, librarian, etc.
    ...config.agent,                                // User's original agents
    build: { ...config.agent?.build, mode: "subagent" },  // DEMOTED
    plan: { ...config.agent?.plan, mode: "subagent" },    // DEMOTED
    

    };

    }

    1. Background Agent System The BackgroundManager Class Source: src/features/background-agent/manager.ts This is the key innovation that enables parallel agent execution: export class BackgroundManager { private tasks: Map private notifications: Map private client: OpencodeClient private pollingInterval?: Timer async launch(input: LaunchInput): Promise { // 1. Create a NEW child session const createResult = await this.client.session.create({ body: {

      parentID: input.parentSessionID,
      title: `Background: ${input.description}`
      

      } });

      const sessionID = createResult.data.id;

      // 2. Track the task const task: BackgroundTask = { id: bg_${crypto.randomUUID().slice(0, 8)}, sessionID, parentSessionID: input.parentSessionID, description: input.description, status: "running", startedAt: new Date(), }; this.tasks.set(task.id, task);

      // 3. Fire async prompt (NON-BLOCKING!) this.client.session.promptAsync({ path: { id: sessionID }, body: {

      agent: input.agent,
      tools: { task: false, background_task: false },  // Prevent recursion
      parts: [{ type: "text", text: input.prompt }]
      

      } });

      // 4. Start polling for completion this.startPolling();

      return task; } // Event handler for completion detection handleEvent(event: Event): void { if (event.type === "session.idle") { const task = this.findBySession(sessionID); if (task?.status === "running") {

      task.status = "completed";
      this.markForNotification(task);
      this.notifyParentSession(task);  // Send message back!
      

      } } } // Notify the main session when background task completes private notifyParentSession(task: BackgroundTask): void { // Show toast notification this.client.tui.showToast({ body: {

      title: "Background Task Completed",
      message: `Task "${task.description}" finished.`,
      variant: "success",
      

      } });

      // Send a message to the parent session this.client.session.prompt({ path: { id: task.parentSessionID }, body: {

      parts: [{
        type: "text",
        text: `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished. Use background_output with task_id="${task.id}" to get results.`
      }]
      

      } }); } } Background Task Tools Source: src/tools/background-task/tools.ts // background_task - Launch an agent in background export function createBackgroundTask(manager: BackgroundManager) { return tool({ description: "Launch a background agent task...", args: { description: tool.schema.string(), prompt: tool.schema.string(), agent: tool.schema.string(), }, async execute(args, toolContext) { const task = await manager.launch({

      description: args.description,
      prompt: args.prompt,
      agent: args.agent,
      parentSessionID: toolContext.sessionID,
      parentMessageID: toolContext.messageID,
      

      });

      return Background task launched. Task ID: ${task.id}; }, }); } // background_output - Get results from background task export function createBackgroundOutput(manager: BackgroundManager, client: OpencodeClient) { return tool({ description: "Get output from a background task...", args: { task_id: tool.schema.string(), block: tool.schema.boolean().optional(), // Wait for completion? }, async execute(args) { const task = manager.getTask(args.task_id); if (task.status === "completed") {

      // Fetch messages from the task's session
      const messages = await client.session.messages({ path: { id: task.sessionID } });
      return formatTaskResult(task, messages);
      

      } return formatTaskStatus(task); }, }); } // background_cancel - Cancel running tasks export function createBackgroundCancel(manager: BackgroundManager, client: OpencodeClient) { return tool({ description: "Cancel background tasks...", args: { taskId: tool.schema.string().optional(), all: tool.schema.boolean().optional(), // Cancel all? }, async execute(args, toolContext) { if (args.all) {

      const tasks = manager.getTasksByParentSession(toolContext.sessionID);
      for (const task of tasks.filter(t => t.status === "running")) {
        client.session.abort({ path: { id: task.sessionID } });
        task.status = "cancelled";
      }
      

      } // ... }, }); } The Parallel Execution Flow You: "Add authentication to my app" │ ▼ ┌─────────────────────────────────────────┐ │ OmO (Claude Opus 4.5) │ │ "I'll orchestrate this..." │ └─────────────────────────────────────────┘ │ │ 1. Creates todo list │ 2. Launches background agents: │ ├──▶ background_task(agent="librarian", "Find JWT best practices...") │ └── Creates child session, runs async │ ├──▶ background_task(agent="explore", "Find existing auth patterns...") │ └── Creates child session, runs async │ │ 3. Starts implementing immediately (doesn't wait!) │ ▼ ┌─────────────────────────────────────────┐ │ [MEANWHILE: Background agents working] │ │ librarian → searching docs │ │ explore → scanning codebase │ └─────────────────────────────────────────┘ │ │ 4. BackgroundManager detects completion │ 5. Sends notification to OmO's session │ ▼ ┌─────────────────────────────────────────┐ │ [BACKGROUND TASK COMPLETED] │ │ Task "Find JWT best practices" done │ │ Use background_output to get results │ └─────────────────────────────────────────┘ │ ▼ OmO: background_output(task_id="bg_abc123") │ ▼

      Integrates findings, continues work...

    2. The Hook Pipeline Hook Categories oh-my-opencode implements 21 hooks across several categories: Session Management

    3. session-recovery: Auto-recovers from API errors

    4. session-notification: OS notifications when session goes idle

    5. anthropic-auto-compact: Auto-summarizes when hitting token limits Task Management

    6. todo-continuation-enforcer: Forces agent to complete all todos

    7. empty-task-response-detector: Warns about empty task responses Context Management

    8. context-window-monitor: Implements "Context Window Anxiety Management"

    9. directory-readme-injector: Injects README.md context

    10. directory-agents-injector: Injects AGENTS.md context

    11. rules-injector: Conditional rules from .claude/rules/ Output Processing

    12. tool-output-truncator: Truncates verbose tool output

    13. grep-output-truncator: Specifically handles grep output Quality Control

    14. comment-checker: Prevents AI-style excessive comments

    15. keyword-detector: Detects ultrawork, search, analyze keywords

    16. think-mode: Auto-enables extended thinking for complex tasks Background System

    17. background-notification: Routes events to BackgroundManager Compatibility

    18. claude-code-hooks: Pre/PostToolUse, UserPromptSubmit, Stop hooks

    19. non-interactive-env: Handles non-interactive environments

    20. interactive-bash-session: Tmux integration Event Handler Pipeline event: async (input) => { // Process through ALL hooks in sequence await autoUpdateChecker?.event(input); await claudeCodeHooks.event(input); await backgroundNotificationHook?.event(input); // Background tracking! await sessionNotification?.(input); await todoContinuationEnforcer?.handler(input); await contextWindowMonitor?.event(input); await directoryAgentsInjector?.event(input); await directoryReadmeInjector?.event(input); await rulesInjector?.event(input); await thinkMode?.event(input); await anthropicAutoCompact?.event(input); await keywordDetector?.event(input); await agentUsageReminder?.event(input); await interactiveBashSession?.event(input);

    21. // Handle session lifecycle if (event.type === "session.created") { ... } if (event.type === "session.idle") { ... } if (event.type === "session.error") { ... } } Tool Execution Hooks "tool.execute.before": async (input, output) => { // Claude Code hooks (PreToolUse) await claudeCodeHooks"tool.execute.before"; // Non-interactive environment handling await nonInteractiveEnv?."tool.execute.before"; // Comment checking await commentChecker?."tool.execute.before";

      // Disable dangerous tools for subagents if (input.tool === "task") {

      output.args.tools = {
        ...output.args.tools,
        background_task: false,  // Prevent nested background tasks
      };
      

      } }, "tool.execute.after": async (input, output) => { // Claude Code hooks (PostToolUse) await claudeCodeHooks"tool.execute.after"; // Truncate verbose output await toolOutputTruncator?."tool.execute.after"; // Track context usage await contextWindowMonitor?."tool.execute.after"; // Inject directory context await directoryAgentsInjector?."tool.execute.after"; await directoryReadmeInjector?."tool.execute.after"; // Inject conditional rules await rulesInjector?."tool.execute.after";

      },

      1. Custom Tools LSP Tools (11 tools) Source: src/tools/lsp/ export const builtinTools = { // Information lsp_hover, // Type info, docs at position lsp_goto_definition, // Jump to definition lsp_find_references, // Find all usages lsp_document_symbols, // File symbol outline lsp_workspace_symbols, // Search symbols by name lsp_diagnostics, // Get errors/warnings lsp_servers, // List available LSP servers

      // Refactoring lsp_prepare_rename, // Validate rename lsp_rename, // Rename symbol across workspace lsp_code_actions, // Get quick fixes/refactorings lsp_code_action_resolve, // Apply code action } AST-Grep Tools Source: src/tools/ast-grep/ export const astGrepTools = { ast_grep_search, // AST-aware pattern search (25 languages) ast_grep_replace, // AST-aware code replacement } Search Tools (Improved) export const searchTools = { grep, // With timeout protection (original hangs forever) glob, // With timeout protection } Special Tools // look_at - Multimodal analysis via subagent export function createLookAt(ctx: PluginInput) { return tool({

      description: "Analyze files using multimodal-looker agent",
      args: { file: tool.schema.string() },
      async execute(args, toolContext) {
        // Delegates to multimodal-looker agent internally
        // Saves context in main session
      },
      

      }); } // call_omo_agent - Specialized agent caller export function createCallOmoAgent(ctx: PluginInput, manager: BackgroundManager) { return tool({

      description: "Call explore or librarian agents",
      args: {
        subagent_type: tool.schema.enum(["explore", "librarian"]),
        prompt: tool.schema.string(),
        run_in_background: tool.schema.boolean(),
      },
      async execute(args, toolContext) {
        if (args.run_in_background) {
          return await manager.launch(...);
        }
        return await executeSynchronously(...);
      },
      

      }); } // interactive_bash - Tmux integration export const interactive_bash = tool({ description: "Execute commands in interactive tmux session", // ...

      });

      1. MCP Integrations Built-in MCPs Source: src/mcp/ // context7.ts - Official documentation lookup export const context7 = { type: "remote", url: "https://mcp.context7.io/sse", enabled: true, } // websearch_exa.ts - Real-time web search export const websearch_exa = { type: "remote", url: "https://mcp.exa.ai/sse", enabled: true, } // grep_app.ts - GitHub code search export const grep_app = { type: "remote", url: "https://mcp.grep.app/sse", enabled: true, } MCP Injection config: async (config) => { config.mcp = { ...config.mcp, ...createBuiltinMcps(pluginConfig.disabled_mcps), // context7, websearch_exa, grep_app ...loadMcpConfigs(), // Claude Code .mcp.json files }; } ---
      2. Configuration System Config File Locations
      3. ~/.config/opencode/oh-my-opencode.json (user-level)
      4. .opencode/oh-my-opencode.json (project-level, overrides user) Config Schema Source: src/config/schema.ts export const OhMyOpenCodeConfigSchema = z.object({ // Enable Google OAuth google_auth: z.boolean().optional(),

      // Agent overrides agents: z.record(z.object({

      model: z.string().optional(),
      temperature: z.number().optional(),
      prompt: z.string().optional(),
      tools: z.record(z.boolean()).optional(),
      disable: z.boolean().optional(),
      description: z.string().optional(),
      mode: z.enum(["primary", "subagent"]).optional(),
      color: z.string().optional(),
      permission: z.object({
        edit: z.enum(["ask", "allow", "deny"]).optional(),
        bash: z.union([
          z.enum(["ask", "allow", "deny"]),
          z.record(z.enum(["ask", "allow", "deny"]))
        ]).optional(),
      }).optional(),
      

      })).optional(),

      // Disable specific agents disabled_agents: z.array(z.string()).optional(),

      // Disable specific MCPs disabled_mcps: z.array(z.enum(["context7", "websearch_exa", "grep_app"])).optional(),

      // Disable specific hooks disabled_hooks: z.array(z.string()).optional(),

      // Claude Code compatibility toggles claude_code: z.object({

      mcp: z.boolean().optional(),
      commands: z.boolean().optional(),
      skills: z.boolean().optional(),
      agents: z.boolean().optional(),
      hooks: z.boolean().optional(),
      

      }).optional(),

      // OmO agent settings omo_agent: z.object({

      disabled: z.boolean().optional(),
      

      }).optional(), }); Example Configuration { $schema: https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json,

      google_auth: true,

      agents: {

      OmO: {
        model: anthropic/claude-sonnet-4
      },
      oracle: {
        temperature: 0.3
      },
      frontend-ui-ux-engineer: {
        disable: true
      }
      

      },

      disabled_hooks: [comment-checker], disabled_mcps: [grep_app],

      claude_code: {

      hooks: true,
      commands: true,
      mcp: false
      

      }

      }

      1. Plugin Loading & Updates How OpenCode Loads Plugins Source: packages/opencode/src/plugin/index.ts (in sst/opencode) for (let plugin of plugins) { if (!plugin.startsWith("file://")) { // Parse version: "oh-my-opencode@2.1.6" const lastAtIndex = plugin.lastIndexOf("@") const pkg = lastAtIndex > 0 ? plugin.substring(0, lastAtIndex) : plugin const version = lastAtIndex > 0 ? plugin.substring(lastAtIndex + 1) : "latest"

        // Install via Bun from npm registry plugin = await BunProc.install(pkg, version) }

      const mod = await import(plugin) for (const [_name, fn] of Object.entries(mod)) {

      const init = await fn(input)
      hooks.push(init)
      

      } } Plugin Cache Location ~/.cache/opencode/ ├── node_modules/ │ └── oh-my-opencode/ ← THE ACTUAL PLUGIN CODE │ ├── dist/ │ │ └── index.js │ └── package.json ├── package.json └── bun.lockb Auto-Update Checker Source: src/hooks/auto-update-checker/ export function createAutoUpdateCheckerHook(ctx: PluginInput) { return {

      event: async ({ event }) => {
        if (event.type !== "session.created") return;
      
        // Check npm registry for latest version
        const result = await checkForUpdate(ctx.directory);
      
        if (result.needsUpdate) {
          // Show notification
          await ctx.client.tui.showToast({
            body: {
              title: `OhMyOpenCode ${result.latestVersion}`,
              message: `v${result.latestVersion} available. Restart OpenCode to apply.`,
            }
          });
      
          // Invalidate cache (delete cached package)
          invalidatePackage("oh-my-opencode");
          // Next restart will download new version
        }
      },
      

      }; } async function getLatestVersion(): Promise { const response = await fetch("https://registry.npmjs.org/-/package/oh-my-opencode/dist-tags"); const data = await response.json(); return data.latest; } Version Pinning // Latest (auto-updates on restart) { plugin: [oh-my-opencode] } // Pinned (never auto-updates) { plugin: [oh-my-opencode@2.1.6] } // Local development

      { plugin: [file:///path/to/oh-my-opencode] }

      1. Creating Your Own Plugin Minimal package.json { name: my-opencode-plugin, version: 1.0.0, main: dist/index.js, types: dist/index.d.ts, type: module, files: [dist], exports: { .: { types: ./dist/index.d.ts, import: ./dist/index.js } }, scripts: { build: bun build src/index.ts --outdir dist --target bun --format esm && tsc --emitDeclarationOnly, prepublishOnly: bun run build }, dependencies: { @opencode-ai/plugin: ^1.0.162 }, devDependencies: { bun-types: latest, typescript: ^5.7.3 }, peerDependencies: { bun: >=1.0.0 } } Minimal tsconfig.json { compilerOptions: { target: ESNext, module: ESNext, moduleResolution: bundler, declaration: true, declarationDir: dist, outDir: dist, rootDir: src, strict: true, esModuleInterop: true, skipLibCheck: true, lib: [ESNext], types: [bun-types] }, include: [src/*/], exclude: [node_modules, dist] } Minimal Plugin (src/index.ts) import type { Plugin } from "@opencode-ai/plugin" import { tool } from "@opencode-ai/plugin" const MyPlugin: Plugin = async ({ client, directory }) => { return { // Add custom tools tool: { my_tool: tool({ description: "Does something useful", args: { input: tool.schema.string(), }, async execute(args) { return Processed: ${args.input}; }, }), }, // Modify config config: async (config) => { config.agent = { ...config.agent, "my-agent": { description: "My custom agent", model: "anthropic/claude-sonnet-4", prompt: "You are helpful...", }, }; }, // React to events event: async ({ event }) => { if (event.type === "session.created") { await client.tui.showToast({ body: { title: "Plugin Loaded", message: "My plugin is active!", variant: "success", } }).catch(() => {}); } }, // Intercept tool calls "tool.execute.before": async (input, output) => { console.log(Tool ${input.tool} called); }, }; }; export default MyPlugin; Publish to npm npm login npm publish Users Install { plugin: [my-opencode-plugin] } ---
      2. Key Files Reference Core oh-my-opencode Files | File | Purpose | |------|---------| | src/index.ts | Main plugin entry, orchestrates everything | | src/agents/.ts | Agent definitions (OmO, oracle, etc.) | | src/features/background-agent/manager.ts | Background task management | | src/tools/background-task/tools.ts | background_task, background_output, background_cancel | | src/tools/lsp/.ts | LSP tool implementations | | src/hooks/.ts | All 21 hooks | | src/mcp/.ts | MCP server configurations | | src/config/schema.ts | Zod config schema | OpenCode Core Files (sst/opencode) | File | Purpose | |------|---------| | packages/plugin/src/index.ts | Plugin interface types | | packages/plugin/src/tool.ts | Tool definition helper | | packages/opencode/src/plugin/index.ts | Plugin loading logic | --- Summary: Why oh-my-opencode Works So Well
      3. Multi-Model Orchestration: Uses the right model for each task (GPT-5.2 for reasoning, Gemini for UI, Grok for speed)
      4. True Parallel Execution: BackgroundManager creates separate sessions, allowing multiple agents to work simultaneously
      5. Automatic Notifications: Event system detects task completion and injects messages into the main conversation
      6. Defensive Hooks: 21 hooks ensure tasks complete, errors recover, context is managed
      7. Comprehensive System Prompt: OmO's 400+ line prompt teaches proper delegation and orchestration
      8. IDE-Quality Tools: LSP and AST-grep tools give agents the same capabilities as human developers
      9. Context Injection: Automatic README/AGENTS.md injection and conditional rules
      10. Claude Code Compatibility: Existing Claude Code configs just work The plugin transforms a single-agent assistant into a dev team where you're the manager and AI models are your specialized engineers. --- Session Summary What We Covered
      11. Researched the @opencode-ai/plugin API - Examined the Plugin type, Hooks interface, available events, and tool definitions from the sst/opencode repository
      12. Deep-dived into oh-my-opencode architecture - Read and analyzed:
        • Main entry point (src/index.ts) - 566 lines
        • Agent definitions (src/agents/) - OmO, oracle, librarian, explore, etc.
        • BackgroundManager (src/features/background-agent/manager.ts) - 430 lines
        • Background tools (src/tools/background-task/tools.ts) - 332 lines
        • Auto-update checker (src/hooks/auto-update-checker/)
      13. Explained key concepts:
        • How plugins are loaded from npm (not GitHub)
        • How the config hook injects custom agents
        • How BackgroundManager enables parallel execution
        • How notifications work (toast + message injection)
        • The difference between schema URL (IDE autocomplete) vs actual code (npm)
      14. Created publishing guide - How to create, build, and publish your own OpenCode plugin Key Files Examined
      15. /Users/darrenhinde/Documents/GitHub/test-larvel/oh-my-opencode/src/index.ts
      16. /Users/darrenhinde/Documents/GitHub/test-larvel/oh-my-opencode/src/agents/*.ts
      17. /Users/darrenhinde/Documents/GitHub/test-larvel/oh-my-opencode/src/features/background-agent/manager.ts
      18. /Users/darrenhinde/Documents/GitHub/test-larvel/oh-my-opencode/src/tools/background-task/tools.ts
      19. /Users/darrenhinde/Documents/GitHub/test-larvel/oh-my-opencode/src/hooks/auto-update-checker/*.ts
      20. /Users/darrenhinde/Documents/GitHub/test-larvel/oh-my-opencode/package.json
      21. /tmp/opencode-repo/packages/plugin/src/index.ts (from sst/opencode) ▣ Compaction · claude-opus-4-5 · 3m 0s Thinking: The user is asking me to continue if there are next steps. Let me check what the current state is and if there's anything pending. Looking at the comprehensive summary I provided, it seems like we've completed a thorough documentation of the OpenCode plugin system and oh-my-opencode architecture. Let me check if there's a todo list or any pending tasks by reading the todo list. ⚙ todoread Thinking: The todo list is empty, which means we've completed the documentation work. The user asked "What did we do so far?" and I provided a comprehensive summary. Now they're asking to continue if there are next steps. Since there's no active todo list and the documentation seems complete, I should ask what they'd like to do next. Possible directions:
      22. Create a custom plugin based on what we learned
      23. Modify/extend oh-my-opencode
      24. Work on something else in the codebase
      25. Dive deeper into specific components Let me ask what direction they want to take.