在之前的系列文章中,我们已经掌握了 OpenCode 的配置、规则、自定义工具(Custom Tools)和子代理(Sub-agents)等能力。今天我们要挑战的是 OpenCode 最强大的扩展机制之一——插件系统(Plugins)。
如果说 Rules 是给 AI 定规矩、Custom Tools 是给 AI 加工具,那么 Plugins 则是给 OpenCode 本身"装上插件":它可以挂载到会话、工具执行、文件变更、权限询问等各类事件上,拦截、修改甚至增强 OpenCode 的默认行为。无论是给完成的任务发送系统通知、保护敏感的 .env 文件,还是注入自定义环境变量,都能用几十行代码搞定。
本文将带你从"使用插件"到"开发插件"完整走一遍,包括加载方式、基本结构、事件系统,以及 5 个可以直接照抄的实战案例。
OpenCode 支持从本地文件和 npm 包两种来源加载插件,非常灵活。
把 .js 或 .ts 文件放进插件目录,OpenCode 启动时就会自动加载:
.opencode/plugins/ —— 项目级插件,只对当前项目生效~/.config/opencode/plugins/ —— 全局插件,对所有项目生效例如在项目根目录创建一个 .opencode/plugins/notification.js 文件,重启 OpenCode 后插件就生效了,不需要任何额外配置。
在 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/,所以不用担心安装速度问题。
了解加载顺序有助于你判断插件之间的覆盖关系:
全局配置(~/.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.updatedmessage.updated、message.part.updated、message.removedpermission.asked、permission.repliedsession.created、session.idle、session.compacted、session.errortool.execute.before、tool.execute.after(工具执行前后)shell.env(注入环境变量)tui.toast.show、tui.command.execute在这些事件里,tool.execute.before 是最常用的——它能在 AI 调用某个工具之前拦截、校验甚至直接抛错,是安全防护的核心钩子。
理论讲完了,下面给出 5 个可以直接放进 .opencode/plugins/ 目录就能用的插件。
让 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 "..."。
.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),可以做到"权限拒绝 + 插件兜底"的双重防护。
有时候我们希望 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 工具执行的命令和用户终端都生效。
插件还能直接新增自定义工具,供 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 完全一致。如果插件工具与内置工具同名,插件工具优先。
调试插件时别用 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" },
},
})
}
日志级别支持 debug、info、warn、error 四种,方便后续检索。
这是一个非常有意思的高级能力。当会话变长时,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 构建自动化流水线。