夜雨聆风学习资料网

ARTICLE · 1350972

OpenCode 自定义插件开发与安装指南

OpenCode 自定义插件开发与安装指南

适用版本:OpenCode V2 官方文档:https://opencode.ai/v2/docs/build/plugins 整理日期:2026-11-08


1. 插件是什么

OpenCode 的插件是一个 npm 包(也可以是本地目录或单文件),用于修改 OpenCode 的行为、增加新能力。

一个插件包最多包含四个部分,每个部分都是包的独立导出:

部分
导出路径
运行位置
用途
Server
.
 或 ./server
OpenCode 服务端
工具、钩子、命令、Agent、Provider、MCP 等
TUI
./tui
终端界面
终端命令、路由、插槽、Markdown 渲染器
GUI
./gui-experimental
OpenCode Desktop
面板、命令、设置页、原生集成
RPC
./rpc
类型契约
供其他插件和客户端导入调用

大多数场景只需要 Server 部分。所有部分的 SDK 都来自同一个依赖:@opencode/plugin。

OpenCode 采用客户端-服务端架构:TUI 连接后台服务,插件运行在服务端,因此插件可以拦截会话、工具调用、模型请求等核心流程。


2. 快速开始:最小插件

2.1 方式一:单文件插件(最快)

在项目根目录创建:

.opencode/plugins/hello.ts

内容:

import { Plugin } from "@opencode/plugin"

export default Plugin.define({
id: "hello",
async setup(ctx) {
console.log(`插件已加载,OpenCode 版本:${ctx.app.version}`)

// 注册一个自定义工具
await ctx.tool.transform((editor) => {
      editor.add({
name: "hello",
description: "向指定的人打招呼",
input: {
type: "object",
properties: {
name: { type: "string" },
          },
required: ["name"],
additionalProperties: false,
        },
execute: async (input) => {
const { name } = input as { name: string }
return { content: `Hello ${name}!` }
        },
      })
    })
  },
})

.opencode/plugins/ 目录下的 .ts / .js 文件会被自动发现并加载,无需修改任何配置。

2.2 方式二:完整插件目录(推荐)

.opencode/plugins/my-plugin/
├── package.json
└── src/
    ├── index.ts        # server 部分
    └── tui.tsx         # 可选:TUI 部分

package.json:

{
"name": "opencode-acme-plugin",
"version": "1.0.0",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"files": ["src"],
"dependencies": {
"@opencode/plugin": "latest"
}
}

2.3 目录的加载规则

  • 如果目录中有 package.json 的 name 和 exports,按 exports 加载各部分。
  • 如果没有 exports,OpenCode 会在目录根查找:
    • server(找不到则用 index)
    • tui
    • rpc
    • 扩展名支持 .ts、.tsx、.js
    • GUI 部分为 gui-experimental.cjs(或 .js)

3. 插件核心概念

3.1 Plugin.define

import { Plugin } from "@opencode/plugin"

export default Plugin.define({
id: "example",
async setup(ctx) {
// 初始化逻辑
  },
})
  • id:插件唯一标识。
  • setup(ctx):插件加载时执行,可以返回一个清理函数,在插件卸载时调用。
setup(ctx) {
const timer = setInterval(() => {
// 周期任务
  }, 60_000)

return () => clearInterval(timer)
}

3.2 插件上下文 ctx

ctx 本质上是一个带插件扩展能力的 OpenCode 服务端客户端。常用能力:

能力
说明
ctx.app.version
当前 OpenCode 版本
ctx.location
当前加载位置(directory、workspaceID、project)
ctx.options
配置中传入的插件选项
ctx.storage
插件级持久化 JSON 存储
ctx.event
订阅服务端事件流
ctx.tool
读取/注册/更新工具,注册工具钩子
ctx.session
会话操作与模型请求钩子
ctx.command
注册/读取命令
ctx.agent
读取/修改 Agent
ctx.provider
 / ctx.model
Provider 与模型注册、过滤
ctx.skill
技能注册与修改
ctx.mcp
MCP 服务器管理
ctx.reference
本地/Git 引用管理
ctx.permission
权限判断与审核钩子
ctx.shell
Shell 命令执行前钩子
ctx.vcs
 / ctx.worktree
版本控制与工作树扩展
ctx.websearch
Web 搜索 Provider 注册

3.3 Transforms(转换)

Transforms 是对 OpenCode 领域状态的同步编辑,例如注册工具、命令、Provider、模型等。

const registration = await ctx.tool.transform((editor) => {
  editor.add({ /* 工具定义 */ })
})

// 不再需要时注销
await registration.dispose()

重要规则:

  • Transform 回调必须是同步的;外部数据要在回调外先加载好。
  • 捕获的外部数据变化后,调用对应的 reload() 重新应用,例如:
    • ctx.tool.reload()
    • ctx.model.reload()
    • ctx.provider.reload()
    • ctx.command.reload()
    • ctx.agent.reload()
    • ctx.mcp.reload()
    • ctx.skill.reload()
  • 注册顺序有意义,后注册的 transform 可以看到前面注册的结果。
  • 每个模型请求会捕获一份稳定的工具快照,后续的 transform 只影响未来的请求。

3.4 Hooks(钩子)

Hooks 用于拦截正在发生的操作,返回的注册对象可调用 dispose() 注销。

常用钩子:

钩子
作用
ctx.session.hook("prompt", ...)
用户提示词进入会话前,可改写文本/附件/元数据
ctx.session.hook("context", ...)
每次模型调用前,可修改系统提示、消息、工具、请求参数
ctx.session.hook("compaction", ...)
压缩摘要请求,可自定义摘要结果
ctx.session.hook("title", ...)
标题生成,可自定义标题
ctx.session.hook("http.request", ...)
修改原生 HTTP 请求
ctx.session.hook("http.response", ...)
修改原生 HTTP 响应
ctx.session.hook("retry", ...)
自定义重试策略
ctx.tool.hook("execute.before", ...)
工具执行前,可修改输入
ctx.tool.hook("execute.after", ...)
工具执行后,可修改结果或记录错误
ctx.permission.hook("evaluate", ...)
权限规则评估后、执行前,可改成 allow/ask/deny
ctx.shell.hook("create.before", ...)
Shell 命令执行前,可修改命令/目录/超时/环境变量

示例:拦截所有用户提示词中的敏感信息:

await ctx.session.hook("prompt", (event) => {
  event.prompt.text = event.prompt.text.replaceAll("company-secret", "[redacted]")
  event.metadata = { ...event.metadata, source: "company-policy" }
})

示例:给每次模型调用追加系统指令并降低温度:

await ctx.session.hook("context", (event) => {
  event.system.push({ type: "text", text: "回答必须简洁,并给出依据。" })
  event.options.temperature = 0.2
})

3.5 工具定义

await ctx.tool.transform((editor) => {
// 可选:给工具加命名空间
  editor.namespace({
name: "acme",
description: "Acme 客户账户工具",
  })

  editor.add({
name: "greeting",
description: "创建一条问候语",
input: {
type: "object",
properties: {
name: { type: "string" },
      },
required: ["name"],
additionalProperties: false,
    },
options: { namespace: "acme", codemode: true },
execute: async (input, context) => {
// 向用户报告进度
await context.progress({ status: "greeting" })

// context.signal 可用于取消可中止操作
const response = await fetch("https://example.com/api", {
signal: context.signal,
      })

return { content: await response.text() }
    },
  })
})

更新和删除工具(使用带命名空间的最终名称):

await ctx.tool.transform((editor) => {
  editor.update("acme_greeting", (tool) => {
    tool.description = "按名字问候用户"
  })
  editor.remove("acme_obsolete")
})

3.6 命令

await ctx.command.transform((editor) => {
  editor.add({
name: "security-review",
description: "审查当前变更的安全问题",
execute: async ({ sessionID, prompt, delivery }) => {
await ctx.session.prompt({
        ...prompt,
        sessionID,
text: `请从安全角度审查以下变更:\n\n${prompt.text}`,
        delivery,
      })
    },
  })
})

3.7 持久化存储

await ctx.storage.set("settings", { strict: true })

const settings = await ctx.storage.get("settings")

const page = await ctx.storage.scan({ prefix: "cache/", limit: 100 })

await ctx.storage.remove("settings")

3.8 读取插件选项

配置:

{
"$schema": "https://opencode.ai/config.json",
"plugins": [
{
"package": "./plugins/company",
"options": {
"strict": true,
"agent": "reviewer"
}
}
]
}

插件中:

setup(ctx) {
const strict = ctx.options.strict === true
const agent = String(ctx.options.agent ?? "build")
}

3.9 订阅事件

export default Plugin.define({
id: "events-example",
async setup(ctx) {
const controller = new AbortController()

void (async () => {
for await (const event of ctx.event.subscribe({ signal: controller.signal })) {
console.log("事件类型:", event.type)
      }
    })()

return () => controller.abort()
  },
})

只订阅指定事件:

for await (const event of ctx.event.subscribe({ types: ["config.updated"] })) {
// ...
}

4. 完整实战:一个可用的插件

目标:创建一个插件,提供 project_info 工具,并在每次模型请求前注入团队规范。

4.1 目录结构

project-root/
└── .opencode/
    └── plugins/
        └── team-kit/
            ├── package.json
            └── src/
                └── index.ts

4.2 package.json

{
"name": "opencode-team-kit",
"version": "0.1.0",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"files": ["src"],
"dependencies": {
"@opencode/plugin": "latest"
}
}

4.3 src/index.ts

import { Plugin } from "@opencode/plugin"
import { readFile, stat } from "node:fs/promises"
import path from "node:path"

export default Plugin.define({
id: "team-kit",
async setup(ctx) {
const strict = ctx.options.strict === true

// 1. 注册工具
await ctx.tool.transform((editor) => {
      editor.add({
name: "project_info",
description: "读取当前项目的 package.json 基本信息",
input: {
type: "object",
properties: {},
additionalProperties: false,
        },
execute: async () => {
const file = path.join(ctx.location.directory, "package.json")
const info = JSON.parse(await readFile(file, "utf8"))
return {
content: JSON.stringify(
              {
name: info.name,
version: info.version,
scripts: Object.keys(info.scripts ?? {}),
              },
null,
2,
            ),
          }
        },
      })
    })

// 2. 注册命令
await ctx.command.transform((editor) => {
      editor.add({
name: "team-check",
description: "按团队规范检查当前项目",
execute: async ({ sessionID, prompt, delivery }) => {
await ctx.session.prompt({
            ...prompt,
            sessionID,
text: "请按团队规范检查当前项目,并列出问题清单。",
            delivery,
          })
        },
      })
    })

// 3. 注入团队规范
let rules = ""
try {
      rules = await readFile(
        path.join(ctx.location.directory, ".opencode/team-rules.md"),
"utf8",
      )
    } catch {
      rules = "未找到团队规范文件。"
    }

await ctx.session.hook("context", (event) => {
      event.system.push({
type: "text",
text: `## 团队规范\n\n${rules}`,
      })
if (strict) {
        event.options.temperature = 0.2
      }
    })
  },
})

4.4 验证

  1. 保存文件后 OpenCode 会自动重载(也可以执行 /reload)。
  2. 在 TUI 中让 Agent 使用 project_info 工具。
  3. 使用 /team-check 命令触发自定义命令。
  4. 观察模型请求的上下文是否包含团队规范。

5. 安装插件

5.1 自动发现(本地开发最方便)

以下位置会被自动加载:

<项目>/.opencode/plugins/                # 项目级
~/.config/opencode/plugins/              # 全局级

Windows 上全局目录通常是:

C:\Users\<用户名>\.config\opencode\plugins\

支持直接放 .ts / .js 文件,或放带 package.json 的插件目录。

5.2 配置 plugins 数组

在 opencode.jsonc 中添加:

{
"$schema": "https://opencode.ai/config.json",
"plugins": [
"opencode-acme-plugin",
"opencode-acme-plugin@1.2.0",
"@acme/opencode-plugin",
"./plugins/local",
"../shared/plugin",
"/absolute/path/plugin",
"file:///home/me/plugins/local",
{
"package": "@acme/opencode-plugin",
"options": {
"agent": "reviewer",
"strict": true
}
}
]
}

注意事项:

  • 相对路径从包含该条目的配置文件所在目录解析。
  • 通过配置指定的本地路径必须是目录,指向单个文件会被跳过。
  • 插件数组按配置优先级从低到高合并。
  • 支持禁用语法:
    • - 前缀禁用某个插件
    • * 匹配所有插件
    • .* 匹配某个 ID 前缀
    • 后面的 ID 可以重新启用
{
"plugins": ["*", "-opencode.provider.*", "opencode.provider.openai", "-acme.reviewer"]
}

5.3 CLI 管理(npm/Git 包)

安装全局包插件:

opencode plugin add opencode-acme-plugin@1.2.0
opencode plugin add @acme/opencode-plugin@latest
opencode plugin add github:acme/opencode-plugin
opencode plugin add git+ssh://git@github.com/acme/opencode-plugin.git#main
opencode plugin add 'github:acme/plugins#main::path:packages/opencode-plugin'

查看与管理:

opencode plugin list
opencode plugin list --builtin
opencode plugin check
opencode plugin update
opencode plugin update opencode-acme-plugin
opencode plugin remove opencode-acme-plugin@1.2.0

说明:

  • 支持 npm 名称、版本、tag、范围,以及 npm 兼容的 Git 规格。
  • 支持分支、tag、完整 commit hash,以及 ::path: 子目录选择器。
  • 不支持 tarball 和 npm alias。
  • 精确版本和完整 commit hash 不会被自动更新。

5.4 TUI-only 插件

只包含 TUI 部分的插件配置在 cli.json:

{
"plugins": ["opencode-acme-cli"]
}

全局 cli.json 通常位于:

~/.config/opencode/cli.json

5.5 桌面端插件

导出 ./gui-experimental 的插件会出现在 OpenCode Desktop 中。桌面端只从全局opencode.json(c) 加载:

{
"plugins": ["opencode-acme-plugin"]
}

可以在 Settings > Extensions > Plugins 中查看和重新加载。


6. 重载与调试

  • 配置文件被监听,保存后自动重载插件,无需重启。
  • 可用 touch 触发本地插件重载:
touch .opencode/plugins/concise/index.ts
  • 未监听的本地依赖变化,可能需要重启后台服务:
opencode service restart
opencode service status
  • TUI 中也可以执行 /reload 强制完整重载。
  • 查看日志:
~/.local/share/opencode/log/opencode.log
  • 需要更多日志时:
OPENCODE_LOG_LEVEL=DEBUG

7. 发布插件

发布就是一个普通 npm 包:

npm publish

建议:

  • 在 package.json 中导出 ./rpc,让其他插件和客户端可以只导入类型契约。
  • GUI 部分先构建产物,并通过 files 包含进去。
  • 使用与目标 OpenCode 版本兼容的 @opencode/plugin 版本。
  • 测试真实安装后的包,而不仅是 workspace 链接的副本。

8. 兼容 V1 与 V2

如果同一个包需要同时支持 V1 和 V2,可以同时导出 setup() 和 server():

import { Plugin } from "@opencode/plugin"

export default {
  ...Plugin.define({
id: "example",
async setup(ctx) {
await ctx.tool.hook("execute.before", () => {
console.log("工具即将执行")
      })
    },
  }),
async server() {
return {
"tool.execute.before": async () => {
console.log("工具即将执行")
      },
    }
  },
}
  • V1 调用 server() 并使用其返回的钩子。
  • V2 读取 id 和 setup()(Effect 插件用 effect()),忽略 server()。
  • 两套实现彼此独立,V1 钩子不会自动转换为 V2 钩子。

9. 常见问题

Q1:插件放了但没加载?

  • 确认文件在 .opencode/plugins/ 下,或在 opencode.jsonc 的 plugins 中配置。
  • 通过配置指定的本地路径必须是目录,不能是单个文件。
  • 检查 opencode plugin list 是否列出。
  • 查看日志 ~/.local/share/opencode/log/opencode.log。

Q2:修改代码后没有生效?

  • 试着 touch 文件或执行 /reload。
  • 如果依赖了未监听的外部文件/包,执行 opencode service restart。

Q3:Transform 里的外部数据不更新?

  • Transform 回调是同步的,外部数据在加载时就已捕获。
  • 数据变化后调用对应域的 reload(),例如 ctx.tool.reload()。

Q4:工具名和预期不一致?

  • 命名空间中的点号和不支持字符会被替换为 _。
  • 更新/删除工具时使用带命名空间的最终名称,例如 acme_greeting。

Q5:本地插件 vs npm 插件怎么选?

  • 开发和团队内部使用:本地目录,放 .opencode/plugins/ 或配置相对路径。
  • 需要跨项目分发、版本管理:发布 npm 包,用 opencode plugin add 安装。

10. 参考文档

  • 插件总览:https://opencode.ai/v2/docs/build/plugins
  • Server 插件:https://opencode.ai/v2/docs/build/plugins/server
  • 插件安装与配置:https://opencode.ai/v2/docs/plugins
  • TUI 插件:https://opencode.ai/v2/docs/build/plugins/cli
  • GUI 插件:https://opencode.ai/v2/docs/build/plugins/gui
  • RPC 契约:https://opencode.ai/v2/docs/build/plugins/rpc
  • V1 插件迁移:https://opencode.ai/v2/docs/build/plugins/migrate-v1
  • 配置参考:https://opencode.ai/v2/docs/config
  • 故障排查:https://opencode.ai/v2/docs/troubleshooting

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料