0%

opencode 官方文档学习(2):开发

这篇文章继续学习 OpenCode 的官方文档,学习基于 OpenCode 进行二次开发,从而对 OpenCode 进行二次扩展。

SDK

opencode JS/TS SDK 提供了一个类型安全的客户端,用于与服务器进行交互。 你可以用它来构建集成方案,并以编程方式控制 opencode。

  • 通过如下方式安装 SDK
1
npm install @opencode-ai/sdk
  • 创建一个 opencode 实例:这会同时启动服务器和客户端
1
2
3
import { createOpencode } from "@opencode-ai/sdk"

const { client } = await createOpencode()
  • 你可以传入一个配置对象来自定义行为。实例仍然会读取你的 opencode.json,但你可以通过内联方式覆盖或添加配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
import { createOpencode } from "@opencode-ai/sdk"

const opencode = await createOpencode({
hostname: "127.0.0.1",
port: 4096,
config: {
model: "anthropic/claude-3-5-sonnet-20241022",
},
})

console.log(`Server running at ${opencode.server.url}`)

opencode.server.close()
  • 如果你已经有一个正在运行的 opencode 实例,可以创建一个客户端实例来连接它:
1
2
3
4
5
import { createOpencodeClient } from "@opencode-ai/sdk"

const client = createOpencodeClient({
baseUrl: "http://localhost:4096",
})

SDK 通过类型安全的客户端暴露所有服务器 API,具体参见 SDK

如下是使用opencode SDK 的一个完整例子,它使用了自定义 provider 来提供 model:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { createOpencode } from "@opencode-ai/sdk"

async function main() {
const { client, server } = await createOpencode({
config: {
model: "chaitin/glm-4.7",
provider: {
chaitin: {
name: "chaitin",
options: {
apiKey: "your api key",
baseURL: "your base url",
},
models: {
"glm-4.7": {
name: "GLM-4.7",
},
},
},
},
}
})

console.log("Server started at:", server.url)

const session = await client.session.create({
body: { title: "Test Session" },
})

console.log("Session:", session.data.id)

const result = await client.session.prompt({
path: { id: session.data.id },
body: {
parts: [{ type: "text", text: "中国首都在哪里?" }],
},
})

console.log("\n回复:", JSON.stringify(result, null, 2))

server.close()
}

main().catch(console.error)

服务器

当你运行 opencode 时,它会启动一个 TUI 和一个服务器。TUI 是与服务器通信的客户端。服务器暴露一个 OpenAPI 3.1 规范端点。该端点也用于生成 SDK。这种架构让 opencode 支持多个客户端,并允许你以编程方式与 opencode 交互

你可以运行 opencode serve 来启动一个独立的服务器。如果你已经在运行 opencode TUI,opencode serve 会启动一个新的服务器。服务器暴露的具体 API 可以参考这里

插件

插件允许你通过挂钩各种事件和自定义行为来扩展 OpenCode。你可以创建插件来添加新功能、集成外部服务,或修改 OpenCode 的默认行为。

有两种方式加载插件。

  • 从本地文件加载:将 JavaScript 或 TypeScript 文件放置在插件目录中,这些目录中的文件会在启动时自动加载。

    • .opencode/plugins/ - 项目级插件
    • ~/.config/opencode/plugins/ - 全局插件
  • 从 npm 加载:在配置文件中指定 npm 包,支持常规和带作用域的 npm 包

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

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

插件从所有来源加载,所有钩子按顺序执行。加载顺序为:

  • 全局配置 (~/.config/opencode/opencode.json)
  • 项目配置 (opencode.json)
  • 全局插件目录 (~/.config/opencode/plugins/)
  • 项目插件目录 (.opencode/plugins/)

创建插件

插件是一个 JavaScript/TypeScript 模块,它导出一个或多个插件函数。每个函数接收一个上下文对象,并返回一个钩子对象。

  • 本地插件和自定义工具可以使用外部 npm 包。在配置目录中添加一个 package.json,列出所需的依赖项。
  • OpenCode 会在启动时运行 bun install 来安装这些依赖项。之后你的插件和工具就可以导入它们了

如下是一个插件的基本结构:

1
2
3
4
5
6
7
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
console.log("Plugin initialized!")

return {
// Hook implementations go here
}
}

插件函数接收以下参数:

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

插件可以订阅事件,这里是具体的事件列表

生态系统

这里介绍了基于 OpenCode 构建的社区项目合集。除此之外,您还可以查看 awesome-opencodeopencode.cafe,这是一个聚合生态系统与社区资源的社区。

实际例子

接下来将通过一些实际例子,演示为 opencode 添加自定义 slash 命令skillagent插件 等。

自定义 slash 命令

我们创建一个项目级别生效的 slash 命令,该命令可以获取某个国家的首都:

1
2
3
4
# mkdir -p /root/code/private/ts/test/opencode/opencode_slash_command_demo/.opencode/commands

# cd root/code/private/ts/test/opencode/opencode_slash_command_demo/
# vim .opencode/commands/oc-demo-capital.md

命令的定义如下:

1
2
3
4
5
description: 查询指定国家的首都
model: dashscope/qwen3.5-plus
---

请告诉我 $ARGUMENTS 的首都是哪个城市?请直接回答城市名称。

在当前目录下启动 opencode,就能使用 /oc-demo-capital 该命令了:

1
> /oc-demo-capital 中国

自定义 skill

创建两个项目级别的 skill,一个 skill 用于获取北京天气,另一个 skill 通过 python 脚本分析数据文件:

1
2
# mkdir -p /code/private/ts/test/opencode/opencode_skill_demo/.opencode/skills/
# cd /code/private/ts/test/opencode/opencode_skill_demo/.opencode/skills/
  • beijing-weather 技能:
1
2
# mkdir beijing-weather
# vim SKILL.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
---
name: beijing-weather
description: 查询北京当前天气情况,包括温度、湿度、风力等信息
---

## 功能说明

查询北京当前天气状况,包括:
- 当前温度和体感温度
- 湿度
- 风速和风向
- 空气质量指数(AQI)
- 天气状况(晴、多云、雨等)

## 使用时机

用户询问北京天气时使用此技能。

## 工作流程

1. 使用 webfetch 工具访问天气 API 或天气网站
2. 解析返回的天气数据
3. 以简洁格式向用户报告天气信息

## 输出格式

北京天气 [日期时间]
温度:XX°C(体感 XX°C)
天气:晴/多云/雨
湿度:XX%
风速:XX km/h,风向:XX
空气质量:X
  • data-analyzer 技能:
1
2
# mkdir beijing-weather
# vim SKILL.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
---
name: data-analyzer
description: Use when analyzing CSV or JSON data files to generate statistical reports, including numeric statistics and categorical value dist
ributions
---

## Overview

Analyze data files (CSV/JSON) and generate comprehensive statistical reports with numeric and categorical field analysis.

## When to Use

- User asks to analyze data files
- Need statistical summary of datasets
- Exploring data structure and distributions
- Preparing data quality reports

## Workflow

1. Identify data file path (CSV or JSON)
2. Run Python script: `python .opencode/skills/data-analyzer/analyze_data.py <file>`
3. Use `--json` flag for JSON output if needed
4. Present results to user

## Quick Reference

| Command | Purpose |
|---------|---------|
| `python analyze_data.py data.csv` | Analyze CSV file (text output) |
| `python analyze_data.py data.json` | Analyze JSON file (text output) |
| `python analyze_data.py data.csv --json` | JSON format output |
......
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# more analyze_data.py
#!/usr/bin/env python3
"""
Data Analysis Tool
Analyzes CSV/JSON data files and generates statistical reports.
"""

import json
import sys
import csv
from pathlib import Path
from collections import Counter
from typing import Any, Dict, List, Union

......
  • 之后在项目目录下启动 opencode ,输入 /skills 就能看到这两个技能了。opencode 可以自动触发 SKILL 的调用
1
2
3
4
5
6
7
8
> 北京天气

Thinking: 用户询问北京天气。我看到有一个 "beijing-weather" skill 可以查询北京当前天气情况。根据指令,我应该在处理任务之前检查是否有相关的技能适用。

这个技能的描述是:"查询北京当前天气情况,包括温度、湿度、风力等信息"
这明显适用于用户的请求,所以我应该调用这个技能。

→ Skill "beijing-weather"

自定义 agent

创建一个数学运算的子 agent,用来执行数学运算:

1
2
3
4
# mkdir -p /root/code/private/ts/test/opencode/opencode_agent_demo
# cd /root/code/private/ts/test/opencode/opencode_agent_demo
# mkdir -p .opencode/agents/
# vim .opencode/agents/math.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
---
description: 使用 bash 执行数学运算的代理
mode: subagent
tools:
write: false
edit: false
glob: false
grep: false
read: false
---

你是一个数学运算代理。你的任务是使用 bash 命令执行数学计算。

## 可用工具

你只能使用 bash 工具。使用以下方法进行数学运算:

### 基本运算

使用 `$(( ))` 进行整数运算:
- 加法: `echo $((5 + 3))`
- 减法: `echo $((10 - 4))`
- 乘法: `echo $((6 * 7))`
- 除法: `echo $((20 / 4))`
- 取模: `echo $((17 % 5))`
- 幂运算: `echo $((2 ** 10))`

### 浮点数运算

使用 `bc` 或 `awk` 进行浮点运算:
.......

之后就可以通过 @math 这种方式手动调用该这个 agent 了:

1
2
3
4
5
6
7
8
9
> @math 1 + 1

Thinking: The user is asking me to use the math subagent to calculate 1 + 1. Let me call the task tool with the math subagent type to perform this calculation.
│ Task Calculate 1 + 1
└ 1 toolcalls · 8.0s
ctrl+x down view subagents
Thinking: The math subagent has returned the result. The answer is 2.

2

opencode 也可能会自动触发这个 agent(也可能不会):

1
2
3
4
5
6
> 帮我执行数学计算 1 + 1

Thinking: 用户要求执行一个简单的数学计算:1 + 1。这是一个非常简单的任务,不需要使用任何技能。我可以直接回答或使用数学代理。
实际上,我看到有一个 "math" 代理可以使用 bash 执行数学运算。但对于这么简单的问题,我可以直接回答,或者使用 bash 来计算。
让我直接回答吧 - 这是一个极其简单的问题。
2
1
2
3
4
5
6
7
8
9
10
11
12
> 帮我执行数学计算 9999 * 99999

Thinking: 用户请求执行一个数学计算:9999 * 99999。
查看可用的技能,我看到有一个 "math" 代理专门用于使用 bash 执行数学运算:
- math: 使用 bash 执行数学运算的代理
我应该使用 Task 工具调用这个 math 子代理来完成数学计算。
│ Task 计算 9999 * 99999
└ 1 toolcalls · 7.0s
ctrl+x down view subagents
Thinking: 数学计算已完成。结果是 999,890,001。我应该简洁地回答用户。

9999 × 99999 = 999,890,001

如果触发了 subagent 的调用,我们可以通过 ctrl+x down 看到 subagent 的会话过程。

自定义插件

如下则展示了一个极简的 plugin 示例,它会在 opencode 启动时在当前目录下创建一个启动文件,记录启动时间。

1
2
3
# mkdir /root/code/private/ts/test/opencode/opencode_plugin_demo/
# cd /root/code/private/ts/test/opencode/opencode_plugin_demo/
# mkdir .opencode/plugins/startup-logger.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import type { Plugin } from "@opencode-ai/plugin"
import { writeFileSync } from "fs"
import { join } from "path"

export const StartupLoggerPlugin: Plugin = async ({ directory }) => {
return {
config: async () => {
const timestamp = new Date().toISOString()
const logFile = join(directory, "startup.log")
writeFileSync(logFile, `OpenCode started at: ${timestamp}\n`, { flag: "a" })
},
}
}
  • 之后在当前目录下启动 opencode,输入 /status 应该就能看到加载的插件。同时当前目录下存在该启动文件:
1
2
3
$ more startup.log
OpenCode started at: 2026-03-11T07:22:27.917Z
OpenCode started at: 2026-03-11T07:26:41.889Z