> ## Documentation Index
> Fetch the complete documentation index at: https://ocx.kdco.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugin Development

> Build OpenCode plugins with hooks, tools, and event-driven extensions

## 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

| Location                         | Scope                 |
| -------------------------------- | --------------------- |
| `.opencode/plugin/`              | Project-level         |
| `~/.config/opencode/plugin/`     | Global                |
| npm packages in `opencode.jsonc` | Configured externally |

```jsonc theme={null}
{
  "$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:

```bash theme={null}
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.

```bash theme={null}
# 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

```typescript theme={null}
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

| Property    | Description                            |
| ----------- | -------------------------------------- |
| `project`   | Current project information            |
| `directory` | Current working directory              |
| `worktree`  | Git worktree path                      |
| `client`    | OpenCode 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:

```json title=".opencode/package.json" theme={null}
{
  "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

```javascript theme={null}
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

```javascript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

* [Custom Tools](/docs/reference/tools) — Standalone tool implementations.
* [MCP Servers](/docs/reference/mcp) — External tool integration via MCP.
* [Creating Registries](/docs/registries/create) — Distribute plugins via registries.
