opencode-plugin-dev

📁 octave-commons/gates-of-aker 📅 Jan 24, 2026
2
总安装量
2
周安装量
#65969
全站排名
安装命令
npx skills add https://github.com/octave-commons/gates-of-aker --skill opencode-plugin-dev

Agent 安装分布

opencode 2
claude-code 2
mcpjam 1
kilo 1
windsurf 1
zencoder 1

Skill 文档

OpenCode Plugin Development

Plugin Fundamentals

What is a Plugin?

A plugin is a JavaScript/TypeScript module that extends OpenCode by hooking into events, adding custom tools, and modifying behavior. Plugins can integrate with external services, enforce policies, and automate workflows.

Plugin Structure

A plugin exports one or more functions that receive a context object and return a hooks object:

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

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  return {
    // Hook implementations
  }
}

Context Object

Plugins receive:

  • 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

Event Hooks

Tool Events

  • tool.execute.before: Before any tool executes
  • tool.execute.after: After any tool executes

Session Events

  • session.created: New session created
  • session.updated: Session metadata updated
  • session.deleted: Session deleted
  • session.status: Session status changes
  • session.error: Session errors
  • session.idle: Session becomes idle
  • session.compacted: Session compacted
  • session.diff: Session diff generated

Message Events

  • message.updated: Message content changed
  • message.removed: Message deleted
  • message.part.updated: Message part changed
  • message.part.removed: Message part deleted

File Events

  • file.edited: File modified
  • file.watcher.updated: File watcher detected change

Command Events

  • command.executed: Slash command executed

Server Events

  • server.connected: Client connected to server

Permission Events

  • permission.updated: Permissions changed
  • permission.replied: Permission request responded to

TUI Events

  • tui.prompt.append: Text appended to prompt
  • tui.command.execute: Command executed
  • tui.toast.show: Toast notification shown

Installation Events

  • installation.updated: Installation status changed

LSP Events

  • lsp.client.diagnostics: LSP diagnostics available
  • lsp.updated: LSP status updated

Todo Events

  • todo.updated: Todo list changed

Plugin Installation

Local Installation

Place files in:

  • .opencode/plugins/ – Project-level
  • ~/.config/opencode/plugins/ – Global

NPM Installation

Add to opencode.json:

{
  "plugin": ["my-plugin", "@scope/custom-plugin"]
}

Dependencies

Local plugins can use npm packages. Create .opencode/package.json:

{
  "dependencies": {
    "shescape": "^2.1.0"
  }
}

OpenCode runs bun install at startup to install dependencies.

Custom Tools

Basic Tool Definition

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

export const MyPlugin: Plugin = async (ctx) => {
  return {
    tool: {
      mytool: tool({
        description: "Tool description",
        args: {
          foo: tool.schema.string().describe("Parameter"),
        },
        async execute(args, ctx) {
          return `Hello ${args.foo}!`
        },
      }),
    },
  }
}

Tool Naming Convention

  • Single export: Filename becomes tool name
  • Multiple exports: <filename>_<exportname> (e.g., math_add)

Tool Context

Tools receive:

  • agent: Agent ID
  • sessionID: Current session ID
  • messageID: Current message ID

Multi-Tool File

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

export const add = tool({
  description: "Add two numbers",
  args: {
    a: tool.schema.number().describe("First number"),
    b: tool.schema.number().describe("Second number"),
  },
  async execute(args) {
    return args.a + args.b
  },
})

export const multiply = tool({
  description: "Multiply two numbers",
  args: {
    a: tool.schema.number(),
    b: tool.schema.number(),
  },
  async execute(args) {
    return args.a * args.b
  },
})

Common Patterns

Environment 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")
      }
    },
  }
}

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"'`
      }
    },
  }
}

Command Escaping

import { escape } from "shescape"

export const MyPlugin = async (ctx) => {
  return {
    "tool.execute.before": async (input, output) => {
      if (input.tool === "bash") {
        output.args.command = escape(output.args.command)
      }
    },
  }
}

Structured Logging

export const MyPlugin = async ({ client }) => {
  await client.app.log({
    service: "my-plugin",
    level: "info",
    message: "Plugin initialized",
    extra: { foo: "bar" },
  })
  return {}
}

Log levels: debug, info, warn, error

Compaction Hooks

Add Context to Compaction

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

export const CompactionPlugin: Plugin = async (ctx) => {
  return {
    "experimental.session.compacting": async (input, output) => {
      output.context.push(`
## Custom Context
- Current task status
- Important decisions made
- Files being actively worked on
      `)
    },
  }
}

Replace Compaction Prompt

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

export const CustomCompactionPlugin: Plugin = async (ctx) => {
  return {
    "experimental.session.compacting": async (input, output) => {
      output.prompt = `You are generating a continuation prompt for a multi-agent swarm session.
Summarize:
1. The current task and its status
2. Which files are being modified and by whom
3. Any blockers or dependencies between agents
4. The next steps to complete the work
      `
    },
  }
}

TypeScript Support

Import Types

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

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  return {
    // Type-safe implementations
  }
}

Zod Schema

import { z } from "zod"

export default {
  description: "Tool description",
  args: {
    param: z.string().describe("Parameter description"),
  },
  async execute(args, context) {
    return "result"
  },
}

Load Order

Plugins load in this order:

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

Duplicate npm packages with same name and version load once. Local and npm plugins with similar names load separately.

Publishing Plugins

Package Structure

{
  "name": "opencode-my-plugin",
  "version": "1.0.0",
  "type": "module",
  "exports": {
    ".": "./dist/index.js",
    "./package.json": "./package.json"
  },
  "peerDependencies": {
    "@opencode-ai/plugin": "*"
  }
}

Build Process

# TypeScript
npm install typescript @types/node
npx tsc

# Or use Bun
bun build ./src/index.ts --outdir ./dist --target node

NPM Publishing

npm login
npm publish

Users install via:

{
  "plugin": ["opencode-my-plugin"]
}

Best Practices

Error Handling

Always throw descriptive errors:

"tool.execute.before": async (input, output) => {
  if (someCondition) {
    throw new Error("Descriptive error message")
  }
}

Performance

  • Avoid expensive operations in synchronous hooks
  • Use await for async operations
  • Cache expensive results

Security

  • Validate all input
  • Sanitize commands before execution
  • Never log sensitive data
  • Use environment variables for secrets

Compatibility

  • Test with multiple OpenCode versions
  • Provide fallbacks for missing features
  • Document required permissions

Debugging

Use structured logging:

await client.app.log({
  service: "my-plugin",
  level: "debug",
  message: "Debug information",
  extra: { input, output },
})

Troubleshooting

Plugin Not Loading

  1. Verify file is in correct directory
  2. Check TypeScript syntax
  3. Review load order conflicts
  4. Check permissions in opencode.json

Dependencies Not Found

  1. Create .opencode/package.json
  2. Run bun install manually
  3. Verify npm package name

Hook Not Firing

  1. Verify event name spelling
  2. Check if event is supported
  3. Ensure hook returns correctly

Reference URLs