跳转到内容

插件

编写插件扩展 OpenCode。

插件(Plugins)允许你通过订阅事件、修改默认行为等方式扩展 OpenCode。你可以编写插件来添加新功能、集成外部服务,或调整 OpenCode 的默认逻辑。

更多示例可以参考社区维护的 插件生态


使用插件

加载插件有两种方式。


本地文件

在插件目录中放置 JavaScript 或 TypeScript 文件:

  • .opencode/plugin/ —— 项目级插件
  • ~/.config/opencode/plugin/ —— 全局插件

这些目录下的文件会在启动时自动加载。


从 npm 加载

在配置文件中声明 npm 包:

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

支持普通包和带作用域的 npm 包。

可在 生态页面 中浏览可用插件。


插件是如何安装的

  • npm 插件:启动时由 Bun 自动安装,包及其依赖会缓存在 ~/.cache/opencode/node_modules/
  • 本地插件:直接从插件目录加载。如果需要使用外部依赖,需要在配置目录下创建 package.json(详见依赖),或者将插件发布到 npm 并在配置中引用

加载顺序

插件会从所有来源加载,其 Hook 依次执行。加载顺序为:

  1. 全局配置(~/.config/opencode/opencode.json
  2. 项目配置(opencode.json
  3. 全局插件目录(~/.config/opencode/plugin/
  4. 项目插件目录(.opencode/plugin/

同名同版本的 npm 包只会加载一次。但如果本地插件和 npm 插件名称“看起来相似”,它们仍然是两个独立插件,都会被加载。


编写插件

插件本质上是一个 JavaScript/TypeScript 模块,导出一个或多个插件函数。每个函数接收一个上下文对象,并返回包含 Hook 的对象。


依赖

本地插件和自定义工具可以使用外部 npm 包。只需在配置目录中添加一个 package.json

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

OpenCode 会在启动时执行 bun install 安装这些依赖,之后你的插件和工具就可以直接 import:

.opencode/plugin/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/plugin/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 支持

在 TypeScript 插件中,可以从插件包中引入类型:

my-plugin.ts
import type { Plugin } from "@opencode-ai/plugin"
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
// 类型安全的 Hook 实现
}
}

事件

插件可以订阅多种事件(在下面的示例中有具体用法)。可用事件包括:

命令事件

  • command.executed

文件事件

  • file.edited
  • file.watcher.updated

安装事件

  • installation.updated

LSP 事件

  • lsp.client.diagnostics
  • lsp.updated

消息事件

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

权限事件

  • permission.replied
  • permission.updated

服务器事件

  • server.connected

会话事件

  • session.created
  • session.compacted
  • session.deleted
  • session.diff
  • session.error
  • session.idle
  • session.status
  • session.updated

待办事件

  • todo.updated

工具事件

  • tool.execute.after
  • tool.execute.before

TUI 事件

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

示例

下面是一些可以用来扩展 OpenCode 的插件示例。


发送通知

在某些事件发生时发送系统通知:

.opencode/plugin/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 来发送通知。


保护 .env

阻止 OpenCode 读取 .env 文件:

.opencode/plugin/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")
}
},
}
}

自定义工具

插件也可以为 OpenCode 注册自定义工具:

.opencode/plugin/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, ctx) {
return `Hello ${args.foo}!`
},
}),
},
}
}

tool 帮助函数会为 OpenCode 创建一个可调用的自定义工具,包含:

  • description:工具的说明
  • args:参数的 Zod schema 定义
  • execute:工具实际执行逻辑

自定义工具会和内置工具一起提供给 OpenCode 使用。


日志

使用 client.app.log() 替代 console.log,便于结构化日志收集:

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

可用日志级别:debuginfowarnerror。更多信息见 SDK 文档


压缩(Compaction)钩子

在会话压缩时自定义需要保留的上下文:

.opencode/plugin/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
`)
},
}
}

experimental.session.compacting Hook 会在 LLM 生成继续会话所需摘要前触发。你可以在这里注入默认压缩逻辑可能遗漏的领域上下文。

也可以通过设置 output.prompt 完全替换压缩提示词:

.opencode/plugin/custom-compaction.ts
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
Format as a structured prompt that a new agent can use to resume work.
`
},
}
}

当设置了 output.prompt 时,会完全替换默认的压缩提示;此时 output.context 数组会被忽略。