名称: opencode-acp-control
描述: 通过 Agent Client Protocol (ACP) 直接控制 OpenCode。启动会话、发送提示、恢复对话并管理 OpenCode 更新。
元数据: {"version": "1.0.2", "author": "Benjamin Jesuiter bjesuiter@gmail.com", "license": "MIT", "github_url": "https://github.com/bjesuiter/opencode-acp-skill"}
通过 Agent Client Protocol (ACP) 直接控制 OpenCode。
| 操作 | 方法 |
|---|---|
| 启动 OpenCode | bash(command: "opencode acp", background: true) |
| 发送消息 | process.write(sessionId, data: "<json-rpc>\n") |
| 读取响应 | process.poll(sessionId) - 每 2 秒重复一次 |
| 停止 OpenCode | process.kill(sessionId) |
| 列出会话 | bash(command: "opencode session list", workdir: "...") |
| 恢复会话 | 列出会话 → 询问用户 → session/load |
| 检查版本 | bash(command: "opencode --version") |
bash(
command: "opencode acp",
background: true,
workdir: "/path/to/your/project"
)
保存返回的 sessionId,后续所有命令都需要它。
\n 结尾)启动 OpenCode 后立即发送:
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{"fs":{"readTextFile":true,"writeTextFile":true},"terminal":true},"clientInfo":{"name":"clawdbot","title":"Clawdbot","version":"1.0.0"}}}
轮询响应。预期收到 result.protocolVersion: 1。
{"jsonrpc":"2.0","id":1,"method":"session/new","params":{"cwd":"/path/to/project","mcpServers":[]}}
轮询响应。保存 result.sessionId(例如 "sess_abc123")。
{"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{"sessionId":"sess_abc123","prompt":[{"type":"text","text":"您的问题在此"}]}}
每 2 秒轮询一次。您将收到:
- session/update 通知(流式内容)
- 包含 result.stopReason 的最终响应
每次轮询可能返回多行。将每行解析为 JSON:
method: "session/update" - 收集这些内容作为响应id - 当出现 stopReason 时停止轮询{"jsonrpc":"2.0","method":"session/cancel","params":{"sessionId":"sess_abc123"}}
无需等待响应 - 这是一个通知。
对于每个 OpenCode 实例,跟踪:
- processSessionId - 来自 bash 工具(clawdbot 的进程 ID)
- opencodeSessionId - 来自 session/new 响应(OpenCode 的会话 ID)
- messageId - 为发送的每个请求递增
stopReason 的响应| stopReason | 含义 |
|---|---|
end_turn |
Agent 已完成响应 |
cancelled |
您取消了提示 |
max_tokens |
达到令牌限制 |
| 问题 | 解决方案 |
|---|---|
| 轮询响应为空 | 继续轮询 - Agent 正在思考 |
| 解析错误 | 跳过格式错误行,继续 |
| 进程已退出 | 重启 OpenCode |
| 5 分钟后无响应 | 终止进程,重新开始 |
1. bash(command: "opencode acp", background: true, workdir: "/home/user/myproject")
-> processSessionId: "bg_42"
2. process.write(sessionId: "bg_42", data: '{"jsonrpc":"2.0","id":0,"method":"initialize",...}\n')
process.poll(sessionId: "bg_42") -> 初始化响应
3. process.write(sessionId: "bg_42", data: '{"jsonrpc":"2.0","id":1,"method":"session/new","params":{"cwd":"/home/user/myproject","mcpServers":[]}}\n')
process.poll(sessionId: "bg_42") -> opencodeSessionId: "sess_xyz789"
4. process.write(sessionId: "bg_42", data: '{"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{"sessionId":"sess_xyz789","prompt":[{"type":"text","text":"列出所有 TypeScript 文件"}]}}\n')
5. process.poll(sessionId: "bg_42") 每 2 秒一次,直到 stopReason
-> 收集所有 session/update 内容
-> 最终响应:stopReason: "end_turn"
6. 完成后:process.kill(sessionId: "bg_42")
通过让用户从可用会话中选择,恢复之前的 OpenCode 会话。
bash(command: "opencode session list", workdir: "/path/to/project")
示例输出:
ID 更新于 消息数
ses_451cd8ae0ffegNQsh59nuM3VVy 2026-01-11 15:30 12
ses_451a89e63ffea2TQIpnDGtJBkS 2026-01-10 09:15 5
ses_4518e90d0ffeJIpOFI3t3Jd23Q 2026-01-09 14:22 8
向用户展示列表并询问要恢复哪个会话:
"您希望恢复哪个会话?
1. ses_451cd8ae... (12 条消息,更新于 2026-01-11)
2. ses_451a89e6... (5 条消息,更新于 2026-01-10)
3. ses_4518e90d... (8 条消息,更新于 2026-01-09)
请输入会话编号或 ID:"
用户响应后(例如 "1"、"第一个" 或 "ses_451cd8ae..."):
启动 OpenCode ACP:
bash(command: "opencode acp", background: true, workdir: "/path/to/project")
初始化:
json
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{...}}
加载会话:
json
{"jsonrpc":"2.0","id":1,"method":"session/load","params":{"sessionId":"ses_451cd8ae0ffegNQsh59nuM3VVy","cwd":"/path/to/project","mcpServers":[]}}
注意:session/load 需要 cwd 和 mcpServers 参数。
加载时,OpenCode 会将完整的对话历史流式传输给您。
function resumeSession(workdir):
# 列出可用会话
output = bash("opencode session list", workdir: workdir)
sessions = parseSessionList(output)
if sessions.empty:
notify("未找到之前的会话。开始新会话。")
return createNewSession(workdir)
# 请用户选择
choice = askUser("要恢复哪个会话?", sessions)
selectedId = matchUserChoice(choice, sessions)
# 启动 OpenCode 并加载会话
process = bash("opencode acp", background: true, workdir: workdir)
initialize(process)
session_load(process, selectedId, workdir, mcpServers: [])
notify("会话已恢复。对话历史已加载。")
return process
OpenCode 在重启时会自动更新。使用此工作流检查和触发更新。
bash(command: "opencode --version")
返回类似:opencode version 1.1.13
提取版本号(例如 1.1.13)。
webfetch(url: "https://github.com/anomalyco/opencode/releases/latest", format: "text")
重定向 URL 包含最新的版本标签:
- 重定向至:https://github.com/anomalyco/opencode/releases/tag/v1.2.0
- 从 URL 路径提取版本(例如 1.2.0)
如果最新版本 > 当前版本:
停止所有正在运行的 OpenCode 进程:
process.list() # 查找所有 "opencode acp" 进程
process.kill(sessionId) # 对每个运行实例执行
重启实例(OpenCode 在启动时自动下载新二进制文件):
bash(command: "opencode acp", background: true, workdir: "/path/to/project")
重新初始化 每个实例(对现有会话执行 initialize + session/load)
bash(command: "opencode --version")
如果版本仍与最新版不匹配:
- 通知用户:"OpenCode 自动更新可能失败。当前:X.X.X,最新:Y.Y.Y"
- 建议手动更新:curl -fsSL https://opencode.dev/install | bash
function updateOpenCode():
current = bash("opencode --version") # 例如 "1.1.13"
latestPage = webfetch("https://github.com/anomalyco/opencode/releases/latest")
latest = extractVersionFromRedirectUrl(latestPage) # 例如 "1.2.0"
if semverCompare(latest, current) > 0:
# 停止所有实例
for process in process.list():
if process.command.includes("opencode"):
process.kill(process.sessionId)
# 短暂等待进程终止
sleep(2 seconds)
# 重启触发自动更新
bash("opencode acp", background: true)
# 验证
newVersion = bash("opencode --version")
if newVersion != latest:
notify("自动更新可能失败。建议手动更新。")
else:
notify("OpenCode 已是最新版本:" + current)
opencodeSessionId 在重启后依然存在 — 使用 session/load 恢复