OpenCode 插件系统(Plugins)开发实战指南:为 AI 编程助手定制专属扩展能力

引言

在之前的系列文章中,我们已经掌握了 OpenCode 的配置、规则、自定义工具(Custom Tools)和子代理(Sub-agents)等能力。今天我们要挑战的是 OpenCode 最强大的扩展机制之一——插件系统(Plugins)

如果说 Rules 是给 AI 定规矩、Custom Tools 是给 AI 加工具,那么 Plugins 则是给 OpenCode 本身"装上插件":它可以挂载到会话、工具执行、文件变更、权限询问等各类事件上,拦截、修改甚至增强 OpenCode 的默认行为。无论是给完成的任务发送系统通知、保护敏感的 .env 文件,还是注入自定义环境变量,都能用几十行代码搞定。

本文将带你从"使用插件"到"开发插件"完整走一遍,包括加载方式、基本结构、事件系统,以及 5 个可以直接照抄的实战案例。

一、使用插件:两种加载方式

OpenCode 支持从本地文件和 npm 包两种来源加载插件,非常灵活。

1. 从本地文件加载

.js.ts 文件放进插件目录,OpenCode 启动时就会自动加载:

  • .opencode/plugins/ —— 项目级插件,只对当前项目生效
  • ~/.config/opencode/plugins/ —— 全局插件,对所有项目生效

例如在项目根目录创建一个 .opencode/plugins/notification.js 文件,重启 OpenCode 后插件就生效了,不需要任何额外配置。

2. 从 npm 加载

opencode.json 配置文件的 plugin 字段中列出 npm 包名即可:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-helicone-session", "opencode-wakatime", "@my-org/custom-plugin"]
}

普通包和作用域包(scoped packages)都支持。OpenCode 启动时会用 Bun 自动安装这些包,并缓存到 ~/.cache/opencode/node_modules/,所以不用担心安装速度问题。

3. 加载顺序

了解加载顺序有助于你判断插件之间的覆盖关系:

全局配置(~/.config/opencode/opencode.json

项目配置(opencode.json

全局插件目录(~/.config/opencode/plugins/

项目插件目录(.opencode/plugins/

同名同版本的 npm 包只加载一次,但本地插件和同名 npm 插件会被分别加载。

二、开发插件:基本结构

一个插件本质上就是一个 JavaScript/TypeScript 模块,导出若干个插件函数。每个函数接收一个上下文对象,并返回一个 hooks 对象:

// .opencode/plugins/example.js
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
  console.log("Plugin initialized!")
  return {
    // Hook 实现写在这里
  }
}

上下文对象包含五个重要成员:

  • project:当前项目信息
  • directory:当前工作目录
  • worktree:Git worktree 路径
  • client:用于与 AI 交互的 OpenCode SDK 客户端
  • $:Bun 的 shell API,用于执行命令

如果你用 TypeScript 开发插件,可以引入官方类型获得类型提示:

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

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  return {
    // 类型安全的 hook 实现
  }
}

依赖管理

本地插件想用外部 npm 包?在配置目录放一个 package.json,OpenCode 启动时会自动执行 bun install 安装依赖:

// .opencode/package.json
{
  "dependencies": {
    "shescape": "^2.1.0"
  }
}

然后就能在插件里直接 import 使用了:

// .opencode/plugins/my-plugin.ts
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)
      }
    },
  }
}

三、事件系统:插件能挂载什么

插件之所以强大,是因为它能订阅 OpenCode 运行过程中的几乎所有事件。常用的事件分为几大类:

  • 命令事件command.executed(命令执行)
  • 文件事件file.edited(文件被编辑)、file.watcher.updated
  • 消息事件message.updatedmessage.part.updatedmessage.removed
  • 权限事件permission.askedpermission.replied
  • 会话事件session.createdsession.idlesession.compactedsession.error
  • 工具事件tool.execute.beforetool.execute.after(工具执行前后)
  • Shell 事件shell.env(注入环境变量)
  • TUI 事件tui.toast.showtui.command.execute

在这些事件里,tool.execute.before 是最常用的——它能在 AI 调用某个工具之前拦截、校验甚至直接抛错,是安全防护的核心钩子。

四、实战案例

理论讲完了,下面给出 5 个可以直接放进 .opencode/plugins/ 目录就能用的插件。

案例 1:任务完成发送系统通知

让 AI 会话空闲时(即任务完成)弹出系统通知:

// .opencode/plugins/notification.js
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"'`
      }
    },
  }
}

这里用 osascript 调用了 macOS 的 AppleScript 发送通知。Linux 下可以换成 notify-send,Windows 下可以换成 powershell -Command "..."

案例 2:保护 .env 文件

这个插件非常实用:拦截 read 工具,禁止 AI 读取任何 .env 文件,防止敏感密钥被带进对话上下文:

// .opencode/plugins/env-protection.js
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")
      }
    },
  }
}

配合之前讲过的权限系统(Permissions),可以做到"权限拒绝 + 插件兜底"的双重防护。

案例 3:为所有 Shell 执行注入环境变量

有时候我们希望 AI 执行命令时自动带上某个环境变量,shell.env 钩子可以做到:

// .opencode/plugins/inject-env.js
export const InjectEnvPlugin = async () => {
  return {
    "shell.env": async (input, output) => {
      output.env.MY_API_KEY = "secret"
      output.env.PROJECT_ROOT = input.cwd
    },
  }
}

注意,shell.env 对 AI 工具执行的命令和用户终端都生效。

案例 4:在插件里注册自定义工具

插件还能直接新增自定义工具,供 AI 调用。这比写 custom 配置更内聚,因为逻辑和注册在同一个文件里:

// .opencode/plugins/custom-tools.ts
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, context) {
          const { directory, worktree } = context
          return `Hello ${args.foo} from ${directory} (worktree: ${worktree})`
        },
      }),
    },
  }
}

tool 辅助函数用 Zod schema 定义参数,返回的描述、参数、执行函数三要素与 Custom Tools 完全一致。如果插件工具与内置工具同名,插件工具优先。

案例 5:结构化日志

调试插件时别用 console.log,推荐通过 SDK 客户端输出结构化日志:

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

日志级别支持 debuginfowarnerror 四种,方便后续检索。

五、进阶:会话压缩(Compaction)钩子

这是一个非常有意思的高级能力。当会话变长时,OpenCode 会触发"压缩"(compaction),把历史对话总结成一段摘要来节省上下文。通过 experimental.session.compacting 钩子,你可以注入自定义上下文,甚至完全替换压缩提示词:

// .opencode/plugins/compaction.ts
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
Include any state that should persist across compaction:
- Current task status
- Important decisions made
- Files being actively worked on`)
    },
  }
}

如果你对默认压缩效果不满意,还可以设置 output.prompt 完全替换压缩提示词,这对于需要长期保持领域上下文的多代理会话尤其有用。

总结

OpenCode 的插件系统把"定制 AI 编程助手"的门槛降到了极低:本地文件即插即用、npm 包一键安装、事件系统覆盖面广、开发语言就是 JS/TS。通过本文的 5 个案例,你可以立刻开始打造属于自己的扩展:任务完成通知、敏感文件保护、环境变量注入、自定义工具、会话压缩优化……

建议的开发路径是:先从"使用社区插件"入手(参考官方 Ecosystem 页面),再模仿本文案例写自己的第一个插件,最后结合 @opencode-ai/plugin 的类型系统做一套完整的团队级插件。注意,插件运行在本地环境、能执行命令,请务必只加载可信来源的插件,并在企业敏感项目中谨慎评估第三方插件风险。

下一篇文章,我们可以聊聊 OpenCode 的 SDK 与 Server 模式,如何用代码驱动 OpenCode 构建自动化流水线。