@wwkit/harness 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/extract.md +2 -14
- package/agents/lint.md +367 -0
- package/agents/pyit.md +361 -0
- package/agents/pyut.md +347 -0
- package/agents/query.md +2 -14
- package/agents/revise.md +2 -14
- package/agents/work.md +151 -0
- package/commands/git-sync.md +218 -0
- package/commands/net-port.md +364 -0
- package/commands/pyit.md +6 -0
- package/commands/pyut.md +6 -0
- package/commands/resume.md +104 -0
- package/package.json +2 -2
- package/skills/better-skill/SKILL.md +124 -0
- package/skills/blame-skill/SKILL.md +201 -0
- package/skills/lint-ai-fix/SKILL.md +141 -0
- package/skills/lint-config-setup/SKILL.md +106 -0
- package/skills/lint-config-setup/references/languages/js.md +110 -0
- package/skills/lint-config-setup/references/languages/py.md +89 -0
- package/skills/lint-env-ensure/SKILL.md +92 -0
- package/skills/lint-env-ensure/references/config.md +65 -0
- package/skills/lint-language-detect/SKILL.md +79 -0
- package/skills/lint-language-detect/references/detect-language.js +65 -0
- package/skills/lint-rules-analyze/SKILL.md +129 -0
- package/skills/lint-suitability-check/SKILL.md +84 -0
- package/skills/lint-tool-fix/SKILL.md +94 -0
- package/skills/new-skill/SKILL.md +227 -0
- package/skills/new-skill/references/template.md +53 -0
- package/skills/new-skill/references/workflow-patterns.md +104 -0
- package/skills/pytest-case-create/SKILL.md +327 -0
- package/skills/pytest-case-create/references/test-standards.md +244 -0
- package/skills/pytest-case-fix/SKILL.md +274 -0
- package/skills/pytest-coverage-analyze/SKILL.md +226 -0
- package/skills/pytest-coverage-analyze/references/scoring-rules.md +57 -0
- package/skills/pytest-env-ensure/SKILL.md +198 -0
- package/skills/pytest-env-ensure/references/config.md +145 -0
- package/skills/pytest-execute/SKILL.md +155 -0
- package/skills/pytest-sample/SKILL.md +164 -0
- package/skills/pytest-sample/references/src/pytest-sample/Calculator.py +67 -0
- package/skills/pytest-sample/references/src/pytest-sample/ConfigManager.py +68 -0
- package/skills/pytest-sample/references/src/pytest-sample/FileProcessor.py +53 -0
- package/skills/pytest-sample/references/src/pytest-sample/OrderService.py +82 -0
- package/skills/pytest-sample/references/src/pytest-sample/TokenGenerator.py +50 -0
- package/skills/pytest-sample/references/src/pytest-sample/UserService.py +45 -0
- package/skills/pytest-sample/references/src/pytest-sample/__init__.py +0 -0
- package/skills/pytest-suitability-check/SKILL.md +224 -0
- package/skills/read-docs/SKILL.md +134 -0
- package/skills/read-docs/references/opencode/agents/cases.md +206 -0
- package/skills/read-docs/references/opencode/agents/design-pattern.md +47 -0
- package/skills/read-docs/references/opencode/agents/detail.md +191 -0
- package/skills/read-docs/references/opencode/agents/examples.md +100 -0
- package/skills/read-docs/references/opencode/agents/index.md +307 -0
- package/skills/read-docs/references/opencode/agents/workflow.md +161 -0
- package/skills/read-docs/references/opencode/cli/commands/acp.md +32 -0
- package/skills/read-docs/references/opencode/cli/commands/agent.md +16 -0
- package/skills/read-docs/references/opencode/cli/commands/attach.md +20 -0
- package/skills/read-docs/references/opencode/cli/commands/mcp.md +37 -0
- package/skills/read-docs/references/opencode/cli/commands/others.md +49 -0
- package/skills/read-docs/references/opencode/cli/commands/plugin.md +13 -0
- package/skills/read-docs/references/opencode/cli/commands/provider.md +44 -0
- package/skills/read-docs/references/opencode/cli/commands/run.md +81 -0
- package/skills/read-docs/references/opencode/cli/commands/serve.md +84 -0
- package/skills/read-docs/references/opencode/cli/commands/session.md +38 -0
- package/skills/read-docs/references/opencode/cli/commands/web.md +15 -0
- package/skills/read-docs/references/opencode/cli/env.md +39 -0
- package/skills/read-docs/references/opencode/cli/index.md +19 -0
- package/skills/read-docs/references/opencode/cli/tui.md +35 -0
- package/skills/read-docs/references/opencode/commands/examples.md +42 -0
- package/skills/read-docs/references/opencode/commands/index.md +185 -0
- package/skills/read-docs/references/opencode/config/provider.md +152 -0
- package/skills/read-docs/references/opencode/formatter/index.md +71 -0
- package/skills/read-docs/references/opencode/guide/config.md +419 -0
- package/skills/read-docs/references/opencode/guide/formatters.md +70 -0
- package/skills/read-docs/references/opencode/guide/index.md +37 -0
- package/skills/read-docs/references/opencode/guide/providers.md +31 -0
- package/skills/read-docs/references/opencode/guide/rules.md +63 -0
- package/skills/read-docs/references/opencode/plugins/examples.md +75 -0
- package/skills/read-docs/references/opencode/plugins/index.md +188 -0
- package/skills/read-docs/references/opencode/reference/index.md +119 -0
- package/skills/read-docs/references/opencode/rule/index.md +78 -0
- package/skills/read-docs/references/opencode/skills/detail.md +113 -0
- package/skills/read-docs/references/opencode/skills/examples.md +141 -0
- package/skills/read-docs/references/opencode/skills/index.md +126 -0
- package/skills/read-docs/references/opencode/skills/workflow.md +146 -0
- package/skills/read-docs/references/opencode/tests/agent.md +10 -0
- package/skills/read-docs/references/opencode/tests/config.md +60 -0
- package/skills/read-docs/references/opencode/tests/file.md +12 -0
- package/skills/read-docs/references/opencode/tests/serve.md +18 -0
- package/skills/read-docs/references/opencode/tests/session.md +31 -0
- package/skills/read-docs/references/opencode/tests/web.md +17 -0
- package/skills/read-docs/references/opencode/tools/arguments.md +305 -0
- package/skills/read-docs/references/opencode/tools/context.md +18 -0
- package/skills/read-docs/references/opencode/tools/custom.md +111 -0
- package/skills/read-docs/references/opencode/tools/detail.md +104 -0
- package/skills/read-docs/references/opencode/tools/examples.md +71 -0
- package/skills/read-docs/references/opencode/tools/index.md +56 -0
- package/skills/read-docs/references/opencode/tools/lsp.md +26 -0
- package/skills/read-docs/references/opencode/tools/mcp.md +132 -0
- package/skills/read-docs/references/opencode/train/README.md +135 -0
- package/skills/read-docs/references/opencode/train/agent-basic.md +772 -0
- package/skills/read-docs/references/opencode/train/command-basic.md +668 -0
- package/skills/read-docs/references/opencode/train/config-basic.md +509 -0
- package/skills/read-docs/references/opencode/train/index.md +164 -0
- package/skills/read-docs/references/opencode/train/practice.md +873 -0
- package/skills/read-docs/references/opencode/train/skill-basic.md +608 -0
- package/skills/read-docs/references/opencode/tui/commands/config.md +32 -0
- package/skills/read-docs/references/opencode/tui/commands/editor.md +47 -0
- package/skills/read-docs/references/opencode/tui/commands/index.md +125 -0
- package/skills/read-docs/references/opencode/tui/commands/init.md +5 -0
- package/skills/read-docs/references/opencode/tui/index.md +26 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Arguments
|
|
2
|
+
|
|
3
|
+
## Zod
|
|
4
|
+
TypeScript-first schema validation with static type inference: `https://zod.dev/`
|
|
5
|
+
|
|
6
|
+
You can use tool.schema, which is just Zod, to define argument types.
|
|
7
|
+
```ts
|
|
8
|
+
args: {
|
|
9
|
+
query: tool.schema.string().describe("SQL query to execute")
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
You can also import Zod directly and return a plain object:
|
|
14
|
+
```ts
|
|
15
|
+
import { z } from "zod"
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
description: "Tool description",
|
|
19
|
+
args: {
|
|
20
|
+
param: z.string().describe("Parameter description"),
|
|
21
|
+
},
|
|
22
|
+
async execute(args, context) {
|
|
23
|
+
// Tool implementation
|
|
24
|
+
return "result"
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Example
|
|
30
|
+
Write a tool in Python
|
|
31
|
+
You can write your tools in any language you want. Here’s an example that adds two numbers using Python.
|
|
32
|
+
|
|
33
|
+
First, create the tool as a Python script:
|
|
34
|
+
```py
|
|
35
|
+
.opencode/tools/add.py
|
|
36
|
+
|
|
37
|
+
import sys
|
|
38
|
+
|
|
39
|
+
a = int(sys.argv[1])
|
|
40
|
+
b = int(sys.argv[2])
|
|
41
|
+
print(a + b)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then create the tool definition that invokes it:
|
|
45
|
+
```ts
|
|
46
|
+
// .opencode/tools/python-add.ts
|
|
47
|
+
import { tool } from "@opencode-ai/plugin"
|
|
48
|
+
import path from "path"
|
|
49
|
+
|
|
50
|
+
export default tool({
|
|
51
|
+
description: "Add two numbers using Python",
|
|
52
|
+
args: {
|
|
53
|
+
a: tool.schema.number().describe("First number"),
|
|
54
|
+
b: tool.schema.number().describe("Second number"),
|
|
55
|
+
},
|
|
56
|
+
async execute(args, context) {
|
|
57
|
+
const script = path.join(context.worktree, ".opencode/tools/add.py")
|
|
58
|
+
const result = await Bun.$`python3 ${script} ${args.a} ${args.b}`.text()
|
|
59
|
+
return result.trim()
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Bun.$ Shell API
|
|
65
|
+
|
|
66
|
+
OpenCode 内置 Bun 运行时,可在 Tool 中直接使用 `Bun.$` Shell API 执行命令。
|
|
67
|
+
|
|
68
|
+
### 基础用法
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { tool } from "@opencode-ai/plugin"
|
|
72
|
+
|
|
73
|
+
export default tool({
|
|
74
|
+
description: "Execute shell commands",
|
|
75
|
+
args: {
|
|
76
|
+
cmd: tool.schema.string().describe("Command to execute"),
|
|
77
|
+
},
|
|
78
|
+
async execute(args, context) {
|
|
79
|
+
// 获取文本输出
|
|
80
|
+
const text = await Bun.$`echo ${args.cmd}`.text()
|
|
81
|
+
return text.trim()
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 常用方法
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// 文本输出
|
|
90
|
+
const text = await Bun.$`cat file.txt`.text()
|
|
91
|
+
|
|
92
|
+
// JSON 输出
|
|
93
|
+
const data = await Bun.$`echo '{"name":"test"}'`.json()
|
|
94
|
+
|
|
95
|
+
// 二进制数据
|
|
96
|
+
const buffer = await Bun.$`cat image.png`.arrayBuffer()
|
|
97
|
+
|
|
98
|
+
// 只执行不获取输出
|
|
99
|
+
await Bun.$`mkdir -p ${dir}`
|
|
100
|
+
|
|
101
|
+
// 检查退出码
|
|
102
|
+
const { exitCode, stdout } = await Bun.$`some-command`
|
|
103
|
+
if (exitCode !== 0) {
|
|
104
|
+
throw new Error(`Command failed with code ${exitCode}`)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 变量插值(自动转义)
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const file = "test.txt"
|
|
112
|
+
const count = 10
|
|
113
|
+
|
|
114
|
+
// 变量自动转义,防止注入
|
|
115
|
+
await Bun.$`cat ${file}`.text()
|
|
116
|
+
await Bun.$`head -n ${count} ${file}`.text()
|
|
117
|
+
|
|
118
|
+
// 数组自动展开
|
|
119
|
+
const files = ["a.txt", "b.txt", "c.txt"]
|
|
120
|
+
await Bun.$`cat ${files}`.text() // => cat a.txt b.txt c.txt
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 错误处理
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
// 捕获错误
|
|
127
|
+
try {
|
|
128
|
+
await Bun.$`some-command`.text()
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error("Command failed:", error.message)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 忽略错误
|
|
134
|
+
const result = await Bun.$`some-command`.quiet()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 管道操作
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
// 管道
|
|
141
|
+
const result = await Bun.$`cat file.txt | grep pattern`.text()
|
|
142
|
+
|
|
143
|
+
// 流式处理
|
|
144
|
+
const proc = Bun.$`cat large-file.txt`
|
|
145
|
+
const reader = proc.stdout.getReader()
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 实用示例
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// Git 操作
|
|
152
|
+
export default tool({
|
|
153
|
+
description: "Get git status",
|
|
154
|
+
args: {},
|
|
155
|
+
async execute() {
|
|
156
|
+
const status = await Bun.$`git status --short`.text()
|
|
157
|
+
return status || "Working tree clean"
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// 文件操作
|
|
162
|
+
export default tool({
|
|
163
|
+
description: "Count lines in file",
|
|
164
|
+
args: {
|
|
165
|
+
file: tool.schema.string().describe("File path"),
|
|
166
|
+
},
|
|
167
|
+
async execute(args) {
|
|
168
|
+
const count = await Bun.$`wc -l ${args.file}`.text()
|
|
169
|
+
return `Total lines: ${count.trim()}`
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// 系统信息
|
|
174
|
+
export default tool({
|
|
175
|
+
description: "Get system info",
|
|
176
|
+
args: {},
|
|
177
|
+
async execute() {
|
|
178
|
+
const [os, arch, cores] = await Promise.all([
|
|
179
|
+
Bun.$`uname -s`.text(),
|
|
180
|
+
Bun.$`uname -m`.text(),
|
|
181
|
+
Bun.$`nproc`.text(),
|
|
182
|
+
])
|
|
183
|
+
return { os: os.trim(), arch: arch.trim(), cores: cores.trim() }
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// 多行脚本
|
|
188
|
+
export default tool({
|
|
189
|
+
description: "Initialize project structure",
|
|
190
|
+
args: {
|
|
191
|
+
name: tool.schema.string().describe("Project name"),
|
|
192
|
+
},
|
|
193
|
+
async execute(args) {
|
|
194
|
+
const result = await Bun.$`
|
|
195
|
+
mkdir -p ${args.name}/src/components
|
|
196
|
+
mkdir -p ${args.name}/tests
|
|
197
|
+
cd ${args.name}
|
|
198
|
+
touch src/index.ts
|
|
199
|
+
touch tests/index.test.ts
|
|
200
|
+
echo "Project ${args.name} initialized"
|
|
201
|
+
`.text()
|
|
202
|
+
return result.trim()
|
|
203
|
+
},
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 传递复杂对象给脚本
|
|
208
|
+
|
|
209
|
+
命令行参数本质是字符串数组,无法直接传递复杂对象。推荐通过 **stdin** 或 **临时文件** 传递 JSON。
|
|
210
|
+
|
|
211
|
+
#### 方案1:stdin(推荐)
|
|
212
|
+
|
|
213
|
+
Tool 通过 stdin 传递 JSON,Python 脚本从 stdin 读取,双方解耦。
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
// Tool 定义
|
|
217
|
+
export default tool({
|
|
218
|
+
description: "Run Python with config",
|
|
219
|
+
args: {
|
|
220
|
+
config: tool.schema.object({
|
|
221
|
+
name: tool.schema.string(),
|
|
222
|
+
count: tool.schema.number(),
|
|
223
|
+
options: tool.schema.object({
|
|
224
|
+
debug: tool.schema.boolean(),
|
|
225
|
+
}),
|
|
226
|
+
}).describe("Configuration object"),
|
|
227
|
+
},
|
|
228
|
+
async execute(args) {
|
|
229
|
+
const proc = Bun.$`python3 script.py`
|
|
230
|
+
proc.stdin.write(JSON.stringify(args.config))
|
|
231
|
+
const result = await proc.text()
|
|
232
|
+
return result.trim()
|
|
233
|
+
},
|
|
234
|
+
})
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```py
|
|
238
|
+
# script.py
|
|
239
|
+
import sys
|
|
240
|
+
import json
|
|
241
|
+
|
|
242
|
+
# 从 stdin 读取 JSON
|
|
243
|
+
config = json.load(sys.stdin)
|
|
244
|
+
|
|
245
|
+
print(f"Name: {config['name']}")
|
|
246
|
+
print(f"Count: {config['count']}")
|
|
247
|
+
print(f"Debug: {config['options']['debug']}")
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
#### 方案2:临时文件
|
|
251
|
+
|
|
252
|
+
Tool 写入临时文件,Python 从文件读取,适合大对象或需要持久化的场景。
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
export default tool({
|
|
256
|
+
description: "Run Python with config file",
|
|
257
|
+
args: {
|
|
258
|
+
config: tool.schema.object({
|
|
259
|
+
name: tool.schema.string(),
|
|
260
|
+
count: tool.schema.number(),
|
|
261
|
+
}).describe("Configuration object"),
|
|
262
|
+
},
|
|
263
|
+
async execute(args) {
|
|
264
|
+
// 写入临时文件
|
|
265
|
+
const tmpFile = `/tmp/config-${Date.now()}.json`
|
|
266
|
+
await Bun.write(tmpFile, JSON.stringify(args.config))
|
|
267
|
+
|
|
268
|
+
// 执行 Python 脚本
|
|
269
|
+
const result = await Bun.$`python3 script.py --config ${tmpFile}`.text()
|
|
270
|
+
|
|
271
|
+
// 清理临时文件
|
|
272
|
+
await Bun.$`rm ${tmpFile}`
|
|
273
|
+
|
|
274
|
+
return result.trim()
|
|
275
|
+
},
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
```py
|
|
280
|
+
# script.py
|
|
281
|
+
import sys
|
|
282
|
+
import json
|
|
283
|
+
import argparse
|
|
284
|
+
|
|
285
|
+
parser = argparse.ArgumentParser()
|
|
286
|
+
parser.add_argument("--config", help="Config file path")
|
|
287
|
+
args = parser.parse_args()
|
|
288
|
+
|
|
289
|
+
# 从文件读取 JSON
|
|
290
|
+
with open(args.config, 'r') as f:
|
|
291
|
+
config = json.load(f)
|
|
292
|
+
|
|
293
|
+
print(f"Name: {config['name']}")
|
|
294
|
+
print(f"Count: {config['count']}")
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### 方案对比
|
|
298
|
+
|
|
299
|
+
| 方案 | 优点 | 缺点 | 适用场景 |
|
|
300
|
+
|------|------|------|---------|
|
|
301
|
+
| **stdin** | 解耦、简单、无需清理 | 无法持久化 | 小对象、一次性传递 |
|
|
302
|
+
| **临时文件** | 可持久化、支持大对象 | 需要清理、文件管理 | 大对象、需要复用 |
|
|
303
|
+
|
|
304
|
+
**推荐**:优先使用 stdin,简单且解耦。
|
|
305
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Context in a tool
|
|
2
|
+
Tools receive context about the current session:
|
|
3
|
+
```ts
|
|
4
|
+
// .opencode/tools/project.ts
|
|
5
|
+
import { tool } from "@opencode-ai/plugin"
|
|
6
|
+
|
|
7
|
+
export default tool({
|
|
8
|
+
description: "Get project information",
|
|
9
|
+
args: {},
|
|
10
|
+
async execute(args, context) {
|
|
11
|
+
// Access context information
|
|
12
|
+
const { agent, sessionID, messageID, directory, worktree } = context
|
|
13
|
+
return `Agent: ${agent}, Session: ${sessionID}, Message: ${messageID}, Directory: ${directory}, Worktree: ${worktree}`
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
```
|
|
17
|
+
- Use context.directory for the session working directory.
|
|
18
|
+
- Use context.worktree for the git worktree root.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# 自定义工具
|
|
2
|
+
|
|
3
|
+
自定义工具是你创建的函数,LLM 可以在对话中调用。
|
|
4
|
+
|
|
5
|
+
## 创建工具
|
|
6
|
+
|
|
7
|
+
工具定义为 TypeScript 或 JavaScript 文件,但工具定义可以调用任何语言编写的脚本。
|
|
8
|
+
|
|
9
|
+
## 位置
|
|
10
|
+
|
|
11
|
+
- 本地:`.opencode/tools/` 目录
|
|
12
|
+
- 全局:`~/.config/opencode/tools/` 目录
|
|
13
|
+
|
|
14
|
+
## 使用 tool() 辅助函数
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { tool } from "@opencode-ai/plugin"
|
|
18
|
+
|
|
19
|
+
export default tool({
|
|
20
|
+
description: "Query the project database",
|
|
21
|
+
args: {
|
|
22
|
+
query: tool.schema.string().describe("SQL query to execute"),
|
|
23
|
+
},
|
|
24
|
+
async execute(args) {
|
|
25
|
+
return `Executed query: ${args.query}`
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
文件名即为工具名。
|
|
31
|
+
|
|
32
|
+
## 单文件多工具
|
|
33
|
+
|
|
34
|
+
每个导出成为独立工具,命名为 `<filename>_<exportname>`:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import { tool } from "@opencode-ai/plugin"
|
|
38
|
+
|
|
39
|
+
export const add = tool({
|
|
40
|
+
description: "Add two numbers",
|
|
41
|
+
args: {
|
|
42
|
+
a: tool.schema.number().describe("First number"),
|
|
43
|
+
b: tool.schema.number().describe("Second number"),
|
|
44
|
+
},
|
|
45
|
+
async execute(args) {
|
|
46
|
+
return (args.a + args.b).toString()
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
export const multiply = tool({
|
|
51
|
+
description: "Multiply two numbers",
|
|
52
|
+
args: {
|
|
53
|
+
a: tool.schema.number().describe("First number"),
|
|
54
|
+
b: tool.schema.number().describe("Second number"),
|
|
55
|
+
},
|
|
56
|
+
async execute(args) {
|
|
57
|
+
return (args.a * args.b).toString()
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
创建两个工具:`math_add` 和 `math_multiply`。
|
|
63
|
+
|
|
64
|
+
### 参数类型
|
|
65
|
+
|
|
66
|
+
使用 `tool.schema`(基于 Zod)定义参数类型:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
args: {
|
|
70
|
+
query: tool.schema.string().describe("SQL query to execute")
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
也可以直接导入 Zod:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
import { z } from "zod"
|
|
78
|
+
|
|
79
|
+
export default {
|
|
80
|
+
description: "Tool description",
|
|
81
|
+
args: {
|
|
82
|
+
param: z.string().describe("Parameter description"),
|
|
83
|
+
},
|
|
84
|
+
async execute(args, context) {
|
|
85
|
+
return "result"
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 上下文信息
|
|
91
|
+
|
|
92
|
+
工具接收当前会话的上下文:
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
import { tool } from "@opencode-ai/plugin"
|
|
96
|
+
|
|
97
|
+
export default tool({
|
|
98
|
+
description: "Get project information",
|
|
99
|
+
args: {},
|
|
100
|
+
async execute(args, context) {
|
|
101
|
+
const { agent, sessionID, messageID, directory, worktree } = context
|
|
102
|
+
return `Agent: ${agent}, Session: ${sessionID}, Directory: ${directory}`
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 更多内容
|
|
108
|
+
|
|
109
|
+
- [工具详解](/tools/detail) — 常用类型、取消处理、依赖管理、输出限制
|
|
110
|
+
- [工具示例](/tools/examples) — HTTP API 封装等示例
|
|
111
|
+
- [MCP 服务器](/tools/mcp) — 通过 MCP 添加外部工具
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# 工具详解
|
|
2
|
+
|
|
3
|
+
## 常用参数类型
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import { tool } from "@opencode-ai/plugin"
|
|
7
|
+
export default tool({
|
|
8
|
+
description: "Demo of parameter types",
|
|
9
|
+
args: {
|
|
10
|
+
// 字符串
|
|
11
|
+
name: tool.schema.string().describe("User name"),
|
|
12
|
+
// 可选参数
|
|
13
|
+
email: tool.schema.string().email().optional().describe("Optional email"),
|
|
14
|
+
// 带默认值
|
|
15
|
+
limit: tool.schema.number().default(10).describe("Max results"),
|
|
16
|
+
// 枚举
|
|
17
|
+
status: tool.schema.enum(["pending", "done"]).describe("Task status"),
|
|
18
|
+
// 布尔
|
|
19
|
+
verbose: tool.schema.boolean().describe("Enable verbose output"),
|
|
20
|
+
// 数组
|
|
21
|
+
tags: tool.schema.array(tool.schema.string()).describe("List of tags"),
|
|
22
|
+
// 对象
|
|
23
|
+
config: tool.schema.object({
|
|
24
|
+
host: tool.schema.string(),
|
|
25
|
+
port: tool.schema.number(),
|
|
26
|
+
}).describe("Server config"),
|
|
27
|
+
},
|
|
28
|
+
async execute(args) {
|
|
29
|
+
return JSON.stringify(args, null, 2)
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 取消操作
|
|
35
|
+
|
|
36
|
+
当用户取消操作(如按 Ctrl+C)时,abort 信号会被触发。长时间运行的工具应监听此信号:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
// .opencode/tool/long-task.ts
|
|
40
|
+
import { tool } from "@opencode-ai/plugin"
|
|
41
|
+
export default tool({
|
|
42
|
+
description: "A long-running task",
|
|
43
|
+
args: {},
|
|
44
|
+
async execute(args, context) {
|
|
45
|
+
// 检查是否已取消
|
|
46
|
+
if (context.abort.aborted) {
|
|
47
|
+
return "Task cancelled"
|
|
48
|
+
}
|
|
49
|
+
// 传递给支持 AbortSignal 的 API
|
|
50
|
+
const response = await fetch("https://api.example.com/data", {
|
|
51
|
+
signal: context.abort,
|
|
52
|
+
})
|
|
53
|
+
return await response.text()
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 依赖管理
|
|
59
|
+
|
|
60
|
+
自定义工具可以使用外部 npm 包,在配置目录添加 `package.json` 声明依赖:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"node-fetch": "^3.0.0",
|
|
66
|
+
"cheerio": "^1.0.0"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
OpenCode 启动时自动运行 `bun install` 安装依赖:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// .opencode/tool/scraper.ts
|
|
75
|
+
import { tool } from "@opencode-ai/plugin"
|
|
76
|
+
import * as cheerio from "cheerio"
|
|
77
|
+
export default tool({
|
|
78
|
+
description: "Extract text from a webpage",
|
|
79
|
+
args: {
|
|
80
|
+
url: tool.schema.string().url().describe("URL to scrape"),
|
|
81
|
+
},
|
|
82
|
+
async execute(args, context) {
|
|
83
|
+
const response = await fetch(args.url, { signal: context.abort })
|
|
84
|
+
const html = await response.text()
|
|
85
|
+
const $ = cheerio.load(html)
|
|
86
|
+
return $("body").text().trim()
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 输出限制
|
|
92
|
+
|
|
93
|
+
工具返回值会被自动截断以避免上下文溢出:
|
|
94
|
+
|
|
95
|
+
| 限制 | 值 |
|
|
96
|
+
|------|-----|
|
|
97
|
+
| 最大行数 | 2000 行 |
|
|
98
|
+
| 最大字节 | 50 KB |
|
|
99
|
+
|
|
100
|
+
超出限制时,OpenCode 会在末尾添加 `...N lines truncated...` 提示。
|
|
101
|
+
|
|
102
|
+
## 查看工具列表
|
|
103
|
+
|
|
104
|
+
启动 OpenCode 后,使用 `/tools` 命令查看所有可用工具列表,确认自定义工具出现在列表中。
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# 工具示例
|
|
2
|
+
|
|
3
|
+
## HTTP API 封装
|
|
4
|
+
|
|
5
|
+
封装 JIRA API 调用:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
// .opencode/tool/jira.ts
|
|
9
|
+
import { tool } from "@opencode-ai/plugin"
|
|
10
|
+
export const getIssue = tool({
|
|
11
|
+
description: "Get JIRA issue details by key",
|
|
12
|
+
args: {
|
|
13
|
+
key: tool.schema.string().describe("Issue key, e.g. PROJ-123"),
|
|
14
|
+
},
|
|
15
|
+
async execute(args, context) {
|
|
16
|
+
const response = await fetch(
|
|
17
|
+
`https://your-company.atlassian.net/rest/api/3/issue/${args.key}`,
|
|
18
|
+
{
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: `Basic ${btoa("email@example.com:API_TOKEN")}`,
|
|
21
|
+
Accept: "application/json",
|
|
22
|
+
},
|
|
23
|
+
signal: context.abort,
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`Failed to fetch issue: ${response.status}`)
|
|
28
|
+
}
|
|
29
|
+
const issue = await response.json()
|
|
30
|
+
return JSON.stringify(issue, null, 2)
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
::: warning
|
|
36
|
+
生产环境中,API Token 应从环境变量读取而非硬编码。
|
|
37
|
+
:::
|
|
38
|
+
|
|
39
|
+
## Python 脚本集成
|
|
40
|
+
|
|
41
|
+
先用 Python 编写脚本:
|
|
42
|
+
|
|
43
|
+
```py
|
|
44
|
+
import sys
|
|
45
|
+
|
|
46
|
+
a = int(sys.argv[1])
|
|
47
|
+
b = int(sys.argv[2])
|
|
48
|
+
print(a + b)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
然后创建工具定义调用它:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { tool } from "@opencode-ai/plugin"
|
|
55
|
+
import path from "path"
|
|
56
|
+
|
|
57
|
+
export default tool({
|
|
58
|
+
description: "Add two numbers using Python",
|
|
59
|
+
args: {
|
|
60
|
+
a: tool.schema.number().describe("First number"),
|
|
61
|
+
b: tool.schema.number().describe("Second number"),
|
|
62
|
+
},
|
|
63
|
+
async execute(args, context) {
|
|
64
|
+
const script = path.join(context.worktree, ".opencode/tools/add.py")
|
|
65
|
+
const result = await Bun.$`python3 ${script} ${args.a} ${args.b}`.text()
|
|
66
|
+
return result.trim()
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
这里使用 `Bun.$` 工具运行 Python 脚本。
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 工具(Tools)
|
|
2
|
+
|
|
3
|
+
工具允许 LLM 在代码库中执行操作。OpenCode 内置一组工具,也可以通过自定义工具或 MCP 服务器扩展。
|
|
4
|
+
|
|
5
|
+
默认所有工具启用且无需权限。
|
|
6
|
+
|
|
7
|
+
自定义工具按键名匹配,如果自定义工具与内置工具同名,自定义工具优先。
|
|
8
|
+
|
|
9
|
+
## 配置
|
|
10
|
+
|
|
11
|
+
使用 `permission` 字段控制工具行为:
|
|
12
|
+
|
|
13
|
+
```jsonc
|
|
14
|
+
{
|
|
15
|
+
"$schema": "https://opencode.ai/config.json",
|
|
16
|
+
"permission": {
|
|
17
|
+
"edit": "deny",
|
|
18
|
+
"bash": "ask",
|
|
19
|
+
"webfetch": "allow",
|
|
20
|
+
// use wildcards
|
|
21
|
+
"mymcp_*": "ask"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 内置工具
|
|
27
|
+
|
|
28
|
+
| 工具 | 说明 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| bash | 在项目环境中执行 Shell 命令 |
|
|
31
|
+
| edit | 使用精确字符串替换修改现有文件 |
|
|
32
|
+
| write | 创建新文件或覆盖现有文件 |
|
|
33
|
+
| read | 读取代码库中的文件内容 |
|
|
34
|
+
| grep | 使用正则表达式搜索文件内容 |
|
|
35
|
+
| glob | 通过模式匹配查找文件 |
|
|
36
|
+
| lsp | 与 LSP 服务器交互,获取代码智能功能 |
|
|
37
|
+
| apply_patch | 应用补丁到文件 |
|
|
38
|
+
| skill | 加载技能(SKILL.md 文件)并返回其内容 |
|
|
39
|
+
| todowrite | 管理编码会话中的待办列表 |
|
|
40
|
+
| webfetch | 获取网页内容 |
|
|
41
|
+
| websearch | 搜索网络信息(需 OpenCode Provider 或设置 `OPENCODE_ENABLE_EXA` 环境变量) |
|
|
42
|
+
| question | 在执行过程中向用户提问 |
|
|
43
|
+
|
|
44
|
+
### apply_patch
|
|
45
|
+
apply_patch 是 opencode 的补丁应用工具,用于将 patch/diff 内容批量应用到代码库中。
|
|
46
|
+
核心要点:
|
|
47
|
+
- 权限:受 edit 权限控制,与 edit、write 工具共享同一权限
|
|
48
|
+
- 输入:通过 args.patchText 传入补丁文本(而非 filePath)
|
|
49
|
+
- 路径格式:路径嵌入在补丁文本的标记行中,相对于项目根目录
|
|
50
|
+
- 支持操作:
|
|
51
|
+
- Add File: src/new-file.ts — 新增文件
|
|
52
|
+
- Update File: src/existing.ts — 更新文件
|
|
53
|
+
- Move to: src/renamed.ts — 重命名/移动文件
|
|
54
|
+
- Delete File: src/obsolete.ts — 删除文件
|
|
55
|
+
- Hook 识别:在 hook 中用 input.tool === "apply_patch" 判断(不是 "patch")
|
|
56
|
+
简言之:它比 edit 工具更适合批量、多文件的修改场景,可以一次性完成增删改移操作。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# lsp
|
|
2
|
+
Interact with your configured LSP servers to get code intelligence features like definitions, references, hover info, and call hierarchy.
|
|
3
|
+
|
|
4
|
+
> This tool is only available when `OPENCODE_EXPERIMENTAL_LSP_TOOL=true` (or `OPENCODE_EXPERIMENTAL=true`).
|
|
5
|
+
|
|
6
|
+
```json
|
|
7
|
+
{
|
|
8
|
+
"$schema": "https://opencode.ai/config.json",
|
|
9
|
+
"permission": {
|
|
10
|
+
"lsp": "allow"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## LSP Servers
|
|
16
|
+
OpenCode can integrate with Language Server Protocol (LSP) servers to use diagnostics as feedback for the agent
|
|
17
|
+
|
|
18
|
+
### 工作原理
|
|
19
|
+
启用 LSP 后,opencode 打开文件时会:
|
|
20
|
+
- 根据文件扩展名匹配已启用的 LSP 服务器
|
|
21
|
+
- 若服务器未运行则自动启动
|
|
22
|
+
|
|
23
|
+
### 最佳实践
|
|
24
|
+
LSP 可通过语言服务器的诊断信息帮助 agent 发现和修复问题,但并非总是有益——语言服务器可能失同步、占用内存、因版本/项目而异、拖慢工作流。
|
|
25
|
+
|
|
26
|
+
许多项目中,直接让 agent 运行 lint/typecheck 等 CLI 工具更佳,避免上述代价。将这些命令写入 AGENTS.md 或 skill 中,以便 agent 知道该运行什么。仅在项目确实受益时启用 LSP。
|