OpenCode 插件系统完全指南:用 JavaScript/TypeScript 扩展 AI 编程助手的能力边界

OpenCode 插件系统完全指南:用 JavaScript/TypeScript 扩展 AI 编程助手的能力边界

OpenCode 的插件系统允许开发者通过 JavaScript 或 TypeScript 编写插件,挂钩到 OpenCode 的各种事件中,实现功能扩展、外部服务集成和默认行为修改。无论是发送系统通知、保护敏感文件,还是添加自定义工具,插件都能帮你轻松实现。

插件概述

OpenCode 插件本质上是一个 JS/TS 模块,导出一个异步函数,该函数接收上下文对象并返回一个 hooks 对象。插件可以响应 OpenCode 生命周期中的各种事件,从会话创建到文件编辑,从工具执行到会话压缩。

插件加载方式有两种:本地文件npm 包

加载插件

本地插件

将 JS/TS 文件放入以下目录,OpenCode 启动时会自动加载:

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

npm 插件

opencode.json 中声明 npm 包:

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

npm 插件在启动时由 Bun 自动安装,缓存到 ~/.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 客户端 |
| $ | Bun 的 Shell API |

TypeScript 支持

使用 TypeScript 可以获得类型提示和编译检查:

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

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  return {
    // Type-safe hook implementations
  }
}

事件系统

插件可以订阅 OpenCode 生命周期中的各类事件。

会话事件

  • session.created —— 会话创建
  • session.compacted —— 会话压缩
  • session.deleted —— 会话删除
  • session.idle —— 会话空闲
  • session.error —— 会话出错

工具事件

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

文件事件

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

权限事件

  • permission.asked —— 权限被询问
  • permission.replied —— 权限被响应

完整的事件列表还包括命令事件、消息事件、Shell 事件、LSP 事件、TUI 事件等,涵盖 OpenCode 运行的方方面面。

实战示例

系统通知

当会话完成时发送系统通知:

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

保护 .env 文件

防止 OpenCode 读取敏感文件:

// .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("Do not read .env files")
      }
    },
  }
}

注入环境变量

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

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

添加自定义工具

插件可以注册全新的工具供 OpenCode 调用:

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

export const CustomToolsPlugin: Plugin = async (ctx) => {
  return {
    tool: {
      mytool: tool({
        description: "A custom tool example",
        args: {
          name: tool.schema.string(),
        },
        async execute(args, context) {
          const { directory } = context
          return `Hello ${args.name} from ${directory}`
        },
      }),
    },
  }
}

tool 辅助函数接收 descriptionargs(Zod schema)和 execute 函数,返回的工具定义会与内置工具一同呈现给 AI。如果插件工具与内置工具同名,插件工具将优先生效。

自定义会话压缩

会话压缩时注入额外上下文:

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

也可以完全替换压缩提示词:

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

结构化日志

使用 client.app.log() 替代 console.log

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

支持 debuginfowarnerror 四个级别。

依赖管理

本地插件需要外部 npm 包时,在配置目录创建 package.json

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

OpenCode 启动时会自动运行 bun install 安装依赖。之后插件中即可导入:

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

最佳实践

事件选择 —— 只在需要的时机挂钩,避免不必要的性能开销。例如文件保护用 tool.execute.before 而不是全局 event

错误处理 —— 插件中的异常不会影响 OpenCode 核心功能,但建议用 try/catch 妥善处理。

日志记录 —— 使用 client.app.log() 而非 console.log,便于集中查看和管理。

TypeScript —— 使用 TypeScript 编写插件可以获得编译期类型检查,减少运行时错误。

测试 —— 在独立项目中进行插件测试,确认事件触发和行为符合预期后再部署到生产项目。

总结

OpenCode 插件系统为你提供了强大的扩展能力。通过几十个事件钩子、自定义工具注册和灵活的生命周期管理,你可以将 OpenCode 无缝集成到自己的工作流中。无论是简单的通知提醒,还是复杂的自定义工具链,插件系统都能满足你的需求。

浏览 OpenCode 插件生态 可以发现社区贡献的更多插件,也可以将自己的插件发布到 npm 分享给他人。