Skip to main content

Overview

Plugins extend OpenCode by hooking into events and customizing behavior. They can add tools, intercept operations, and react to session lifecycle events.

Plugin Locations

LocationScope
.opencode/plugin/Project-level
~/.config/opencode/plugin/Global
npm packages in opencode.jsoncConfigured externally
{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-helicone-session", "@my-org/custom-plugin"]
}

Adding npm Plugins with OCX

OCX supports adding npm plugins directly using the npm: protocol:
ocx add npm:<package-name>[@version]
This adds the package to the plugin array in opencode.jsonc. OpenCode installs and loads the plugin at runtime.
# Latest version
ocx add npm:opencode-plugin-foo

# Specific version
ocx add npm:opencode-plugin-foo@1.0.0

# Scoped package
ocx add npm:@scope/plugin

# Mix with registry components
ocx add kdco/researcher npm:some-plugin

Basic Plugin Structure

import type { Plugin } from "@opencode-ai/plugin"

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  console.log("Plugin initialized!")

  return {
    // Hook implementations
  }
}

Context Object Properties

PropertyDescription
projectCurrent project information
directoryCurrent working directory
worktreeGit worktree path
clientOpenCode SDK client for AI interaction
$Bun’s shell API for executing commands

Plugin Dependencies

Add external npm packages via package.json in your config directory:
.opencode/package.json
{
  "dependencies": {
    "shescape": "^2.1.0"
  }
}

Available Events

Session Events

  • session.created / session.updated / session.deleted
  • session.idle — Session completed
  • session.compacted — Context was compacted
  • session.error / session.status / session.diff

Tool Events

  • tool.execute.before — Before tool execution (can modify/abort)
  • tool.execute.after — After tool execution

File Events

  • file.edited — File was modified
  • file.watcher.updated — File system change detected

Message Events

  • message.updated / message.removed
  • message.part.updated / message.part.removed

Permission Events

  • permission.updated / permission.replied

TUI Events

  • tui.prompt.append / tui.command.execute / tui.toast.show

Other Events

  • command.executed / installation.updated / server.connected
  • lsp.updated / lsp.client.diagnostics
  • todo.updated

Plugin Examples

.env Protection

export const EnvProtection = async ({ project, client, $, directory, worktree }) => {
  return {
    "tool.execute.before": async (input, output) => {
      if (input.tool === "read" && output.args.filePath.includes(".env")) {
        throw new Error("Do not read .env files")
      }
    },
  }
}

Send Notifications

export const NotificationPlugin = async ({ project, client, $, directory, worktree }) => {
  return {
    event: async ({ event }) => {
      if (event.type === "session.idle") {
        await $`osascript -e 'display notification "Session completed!" with title "opencode"'`
      }
    },
  }
}

Custom Tools via Plugin

import { type Plugin, tool } from "@opencode-ai/plugin"

export const CustomToolsPlugin: Plugin = async (ctx) => {
  return {
    tool: {
      mytool: tool({
        description: "This is a custom tool",
        args: {
          foo: tool.schema.string(),
        },
        async execute(args, ctx) {
          return `Hello ${args.foo}!`
        },
      }),
    },
  }
}

Compaction Hooks

export const CompactionPlugin: Plugin = async (ctx) => {
  return {
    "experimental.session.compacting": async (input, output) => {
      output.context.push(`
## Custom Context
Include any state that should persist across compaction.
`)
    },
  }
}

Load Order

  1. Global config (~/.config/opencode/opencode.jsonc)
  2. Project config (opencode.jsonc)
  3. Global plugin directory (~/.config/opencode/plugin/)
  4. Project plugin directory (.opencode/plugin/)

See Also