OpenCode 插件系统(Plugins)完全指南:用自定义扩展增强 AI 编程助手

引言

OpenCode 作为一个开源的 AI 编程助手,其核心设计理念之一就是可扩展性。除了内置的丰富功能外,OpenCode 提供了强大的插件系统(Plugin System),允许开发者通过编写 JavaScript 或 TypeScript 代码来 hook 到 OpenCode 的各种生命周期事件,从而实现自定义行为。无论你想在特定操作前后执行逻辑、添加自定义工具、保护敏感文件,还是集成外部通知服务,插件系统都能帮你实现。

本文将从插件的基础概念讲起,涵盖插件的加载方式、创建方法、事件系统,以及多个实战案例,帮助你全面掌握 OpenCode 插件开发。

什么是 OpenCode 插件

OpenCode 插件本质上是一个 JavaScript 或 TypeScript 模块,它导出一个或多个函数。每个函数接收一个上下文对象,并返回一个包含各种事件钩子(hooks)的对象。当 OpenCode 运行时发生特定事件(如执行工具、创建会话、修改文件等),插件中注册的钩子函数会被自动调用。

插件的核心优势在于:

  • 非侵入式:无需修改 OpenCode 核心代码
  • 事件驱动:订阅感兴趣的事件,按需处理
  • 全能力访问:可访问项目信息、SDK 客户端、Bun 的 Shell API 等

安装和使用插件

从本地加载

将你的插件文件放在以下目录,OpenCode 会自动加载:

  • .opencode/plugins/ — 项目级插件,仅在该项目中生效
  • ~/.config/opencode/plugins/ — 全局插件,所有项目生效

文件可以是 .js.ts 格式,直接放置在目录中即可。

从 npm 安装

你也可以通过配置 opencode.jsonplugin 字段来加载 npm 包:

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

OpenCode 在启动时会自动使用 Bun 安装这些 npm 包并缓存到 ~/.cache/opencode/node_modules/

加载顺序

当存在多个插件来源时,加载顺序如下:

全局配置文件 ~/.config/opencode/opencode.json 中的插件

项目配置文件 opencode.json 中的插件

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

项目插件目录 .opencode/plugins/

所有插件的同类型钩子会按此顺序依次执行。

创建你的第一个插件

让我们从一个基础示例开始。

基本结构

.opencode/plugins/ 下创建一个 example.js 文件:

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

  return {
    // Hook implementations go here
  }
}

插件函数接收以下参数:

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

使用 TypeScript

如果你偏好 TypeScript,可以从 @opencode-ai/plugin 包中导入类型定义:

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

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

事件系统详解

OpenCode 插件可以订阅多种事件,覆盖了从工具执行到会话管理的方方面面。

工具事件

最常用的事件钩子,允许你在工具执行前后插入逻辑:

  • tool.execute.before — 工具执行前触发
  • tool.execute.after — 工具执行后触发

文件事件

  • file.edited — 文件被编辑时
  • file.watcher.updated — 文件监听器更新时

会话事件

  • session.created — 新会话创建时
  • session.compacted — 会话上下文被压缩时
  • session.deleted — 会话被删除时
  • session.error — 会话发生错误时
  • session.idle — 会话空闲时
  • session.status — 会话状态变化时

其他事件

  • permission.asked / permission.replied — 权限请求与响应
  • command.executed — 命令执行完成
  • shell.env — Shell 环境变量注入
  • tui.prompt.append — TUI 提示信息追加
  • tui.toast.show — TUI 通知显示

完整的事件列表可参考官方文档的 Events 章节。

实战案例

1. 系统通知插件

在长时间运行的任务完成时,你可能希望收到系统通知:

// .opencode/plugins/notification.js
export const NotificationPlugin = async ({ $ }) => {
  return {
    event: async ({ event }) => {
      if (event.type === "session.idle") {
        await $`osascript -e 'display notification "OpenCode 会话已完成!" with title "opencode"'`
      }
    },
  }
}

在 macOS 上使用 osascript 发送通知;Linux 用户可以使用 notify-send,Windows 用户可以使用 msg 命令。

2. .env 文件保护

防止 AI 意外读取敏感的环境变量文件:

// .opencode/plugins/env-protection.js
export const EnvProtection = async () => {
  return {
    "tool.execute.before": async (input, output) => {
      if (input.tool === "read" && output.args.filePath.includes(".env")) {
        throw new Error("禁止读取 .env 文件")
      }
    },
  }
}

当 AI 尝试读取包含 .env 的文件路径时,插件会抛出错误,阻止该操作。

3. 注入环境变量

为所有 Shell 执行注入自定义环境变量:

// .opencode/plugins/inject-env.js
export const InjectEnvPlugin = async () => {
  return {
    "shell.env": async (input, output) => {
      output.env.MY_API_KEY = "your-api-key"
      output.env.PROJECT_ENV = "development"
    },
  }
}

这些环境变量会同时注入到 AI 工具执行的命令和用户终端中。

4. 自定义工具插件

插件不仅能监听事件,还可以注册全新的工具供 AI 调用:

// .opencode/plugins/custom-tools.ts
import { type Plugin, tool } from "@opencode-ai/plugin"

export const CustomToolsPlugin: Plugin = async (ctx) => {
  return {
    tool: {
      getWeather: tool({
        description: "获取指定城市的天气信息",
        args: {
          city: tool.schema.string({ description: "城市名称" }),
        },
        async execute(args, context) {
          const { directory } = context
          // 这里可以调用外部天气 API
          return `${args.city} 的天气:晴朗,25°C`
        },
      }),
    },
  }
}

自定义工具使用 tool 辅助函数创建,包含描述、参数定义和执行函数。AI 会像调用内置工具一样调用它们。如果插件工具与内置工具同名,插件工具会优先执行。

5. 日志记录插件

使用结构化日志替代 console.log

// .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

6. 自定义会话压缩上下文

当长会话被压缩时,注入额外的上下文信息:

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

export const CompactionPlugin: Plugin = async (ctx) => {
  return {
    "experimental.session.compacting": async (input, output) => {
      output.context.push(`## 自定义上下文
需要在压缩后保留的状态信息:
- 当前任务状态
- 已做出的重要决策
- 正在处理的文件列表`)
    },
  }
}

你也可以通过设置 output.prompt 完全替换默认的压缩提示词。

依赖管理

如果你的本地插件需要使用外部 npm 包,需要在配置目录中创建 package.json

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

OpenCode 会在启动时自动运行 bun install 安装这些依赖,插件中可以直接 import 使用:

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

最佳实践

单一职责:每个插件只做一件事,保持代码简洁

错误处理:始终用 try-catch 包裹可能出错的逻辑,避免影响 OpenCode 主流程

性能敏感:钩子函数应尽量轻量,避免在 tool.execute.before 等高频事件中执行耗时操作

使用 TypeScript:类型安全能显著降低插件开发中的错误率

版本管理:将项目级插件提交到 Git 仓库,团队共享

总结

OpenCode 的插件系统为用户提供了极大的灵活性和扩展能力。无论是简单的通知提醒、环境变量注入,还是复杂的自定义工具开发,插件都能让你按需定制 AI 编程助手的行为。通过掌握事件机制和插件 API,你可以将 OpenCode 打造成完全符合个人或团队工作流的专属工具。

如果你开发了有用的插件,不妨提交到 OpenCode 的生态系统(Ecosystem)页面,与社区分享你的成果。