oh-my-opencode-cohub 1.0.17 → 1.0.19

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/README.md CHANGED
@@ -1,246 +1,306 @@
1
- # oh-my-opencode-cohub
2
-
3
- OpenCode 中文智能体编排插件 CoHub——纯调度模式、全中文提示词、规范分析代理、方案制定代理。
4
-
5
- ## 特性
6
-
7
- - 🚀 **独立运行**:纯 OpenCode Plugin,依赖 `@opencode-ai/plugin` SDK,零外部插件依赖
8
- - 🤖 **12 个内置代理**:8 个通用代理(orchestrator/oracle/librarian/explorer/designer/fixer/observer/council)+ 4 个规范/方案代理(rule-user/rule-project/rule-app/planner)
9
- - 🪝 **自动注入**:通过 `system.transform` 钩子在每次 LLM 调用时注入中文语言要求
10
- - 🧠 **纯调度 Orchestrator**:不碰任何文件工具,只负责规划、委派、验证
11
- - 🇨🇳 **全中文提示词**:8 个内置代理 + 4 个插件代理全中文
12
- - 📋 **规范分析代理**:rule-user(用户AGENTS.md)、rule-project(项目AGENTS.md)、rule-app(.opencode/rules/)
13
- - 🎯 **方案制定代理**:planner 综合所有输入输出结构化任务分解
14
- - 🔗 **OpenSpec + Superpowers 死板路由**:收到需求即触发,不判断"大小"
15
- - **六步工作流**:理解→收集→规范分析→制定方案→调度→验证
16
- - 📜 **AGENTS.md 全覆盖**:项目级和用户级 AGENTS.md 对**本插件所有代理**(内置 8 个 + 插件 4 个)均生效,无需额外配置
17
- - 📖 **架构借鉴**:插件架构和代理编排理念借鉴了 oh-my-opencode-slim,但完全独立实现、无运行时依赖
18
-
19
- ## 安装
20
-
21
- ### 前置条件
22
-
23
- - [OpenCode](https://opencode.ai) 已安装
24
- - 推荐安装 [Bun](https://bun.sh)(CLI 一键安装需要)
25
-
26
- ### 方式一:CLI 一键安装(推荐)
27
-
28
- ```bash
29
- bunx oh-my-opencode-cohub install
30
- ```
31
-
32
- CLI 会自动完成以下操作:
33
-
34
- 1. 将 `oh-my-opencode-cohub` 注册到 `~/.config/opencode/opencode.json` 的 `plugin` 数组
35
- 2. 将插件注册到 TUI 配置 `~/.config/opencode/tui.json`
36
- 3. `opencode.json` 的 `agent` 字段中注册全部 12 个 `co-*` 代理
37
- 4. 写入默认配置文件 `~/.config/opencode/oh-my-opencode-cohub.json`
38
-
39
- 重启 OpenCode 即可生效。
40
-
41
- ### 方式二:npm 安装 + 手动配置
42
-
43
- ```bash
44
- npm install oh-my-opencode-cohub
45
- ```
46
-
47
- 然后在 `~/.config/opencode/opencode.json` 中添加:
48
-
49
- ```json
50
- {
51
- "plugin": ["oh-my-opencode-cohub"]
52
- }
53
- ```
54
-
55
- 重启 OpenCode 即可。
56
-
57
- ### 卸载
58
-
59
- ```bash
60
- bunx oh-my-opencode-cohub uninstall
61
- ```
62
-
63
- CLI 会精确清理所有 `co-*` 代理和相关配置,不影响其他插件的配置数据。
64
-
65
- 如果使用 npm 手动安装,则需手动从 `opencode.json` 的 `plugin` 数组中移除 `"oh-my-opencode-cohub"`,然后 `npm uninstall oh-my-opencode-cohub`。
66
-
67
- ### 验证安装
68
-
69
- 安装并重启 OpenCode 后,确认以下效果:
70
-
71
- - 对话中自动出现 **中文语言要求**(如 "你必须始终使用中文进行思考、推理和回复")
72
- - 可用代理列表中出现 12 个 `co-*` 前缀代理(`co-orchestrator`、`co-oracle`、`co-librarian`、`co-explorer`、`co-designer`、`co-fixer`、`co-observer`、`co-council`、`co-rule-user`、`co-rule-project`、`co-rule-app`、`co-planner`)
73
- - Orchestrator 自动进入纯调度模式(只做规划、委派、验证,不直接操作文件)
74
-
75
- ### 从源码安装(本地开发)
76
-
77
- 如果需要修改源码或使用开发版本:
78
-
79
- ```bash
80
- # 1. 克隆项目
81
- git clone <repo-url> && cd oh-my-opencode-cohub
82
-
83
- # 2. 安装依赖
84
- npm install
85
-
86
- # 3. 构建(需要系统已安装 Bun)
87
- npm run build
88
-
89
- # 4. 方式 A:npm link 全局链接(推荐)
90
- npm link
91
- bunx oh-my-opencode-cohub install
92
- # 重启 OpenCode 即可。之后修改源码只需重新 npm run build
93
-
94
- # 4. 方式 B:手动配置绝对路径
95
- # 编辑 ~/.config/opencode/opencode.json,在 plugin 数组中写入项目绝对路径:
96
- # { "plugin": ["/absolute/path/to/oh-my-opencode-cohub"] }
97
- # 然后手动运行 CLI 注册代理:
98
- # node dist/cli/index.js install
99
- ```
100
-
101
- > **构建依赖**:`npm run build` 使用 `bun build` 和 `tsc`。系统需安装 [Bun](https://bun.sh)(构建工具)和 Node.js(类型声明生成)。所有 `@opencode-ai/*`、`@opentui/*`、`zod` 均为 external,不打包进 `dist/`。
102
-
103
- ## 代理一览
104
-
105
- ### 代理(12 个,全中文提示词)
106
-
107
- | 代理 | 默认模型 | 职责 | 读取源 |
108
- |------|----------|------|--------|
109
- | co-orchestrator | `deepseek/deepseek-v4-pro` | 纯调度:规划→委派→验证 | |
110
- | co-oracle | `deepseek/deepseek-v4-pro` | 架构审查/代码审查(含 Superpowers skills) | — |
111
- | co-librarian | `deepseek/deepseek-v4-flash` | 外部文档/API 研究 | — |
112
- | co-explorer | `deepseek/deepseek-v4-flash` | 代码库搜索定位 | — |
113
- | co-designer | `minimax/MiniMax-M3` | UI/UX 设计与实现 | — |
114
- | co-fixer | `deepseek/deepseek-v4-flash` | 代码修改执行(含 TDD skill) | — |
115
- | co-observer | `codermxtest/gpt-5.5` | 图片/PDF 分析 | — |
116
- | co-council | `deepseek/deepseek-v4-pro` | 多模型共识 | — |
117
- | co-rule-user | `deepseek/deepseek-v4-flash` | 用户级规范分析 | `~/.config/opencode/AGENTS.md` |
118
- | co-rule-project | `deepseek/deepseek-v4-flash` | 项目级规范分析 | 项目 `AGENTS.md` |
119
- | co-rule-app | `deepseek/deepseek-v4-flash` | 应用规则分析 | `.opencode/rules/*.md` |
120
- | co-planner | `deepseek/deepseek-v4-pro` | 方案制定 | 综合需求+信息+规则 |
121
-
122
- > 模型可通过下文「配置文件」或「自定义模型」章节覆盖。
123
-
124
- ## 配置文件
125
-
126
- CLI 安装后自动创建 `~/.config/opencode/oh-my-opencode-cohub.json`,这是 CoHub 的专用配置文件,结构如下:
127
-
128
- ```json
129
- {
130
- "$schema": "https://unpkg.com/oh-my-opencode-cohub@latest/oh-my-opencode-cohub.schema.json",
131
- "agents": {
132
- "co-orchestrator": { "model": "deepseek/deepseek-v4-pro", "variant": "max" },
133
- "co-oracle": { "model": "deepseek/deepseek-v4-pro", "variant": "max" },
134
- "co-librarian": { "model": "deepseek/deepseek-v4-flash", "variant": "low" },
135
- "co-explorer": { "model": "deepseek/deepseek-v4-flash", "variant": "low" },
136
- "co-designer": { "model": "minimax/MiniMax-M3", "variant": "medium" },
137
- "co-fixer": { "model": "deepseek/deepseek-v4-flash", "variant": "high" },
138
- "co-observer": { "model": "codermxtest/gpt-5.5", "variant": "low" },
139
- "co-council": { "model": "deepseek/deepseek-v4-pro", "variant": "high" },
140
- "co-rule-user": { "model": "deepseek/deepseek-v4-flash", "variant": "medium" },
141
- "co-rule-project":{ "model": "deepseek/deepseek-v4-flash", "variant": "medium" },
142
- "co-rule-app": { "model": "deepseek/deepseek-v4-flash", "variant": "medium" },
143
- "co-planner": { "model": "deepseek/deepseek-v4-pro", "variant": "high" }
144
- }
145
- }
146
- ```
147
-
148
- 直接编辑此文件即可修改任意代理的模型和变体。`$schema` 提供 IDE 自动补全支持。
149
-
150
- > **注意**:同一目录 `~/.config/opencode/oh-my-opencode-cohub/` 下也可放置 `{agent}.md` 提示词覆盖文件(见「自定义提示词」章节),与 JSON 配置文件共存。
151
-
152
- ## 工作流
153
-
154
- ```
155
- 1. 理解需求
156
- 2. 信息收集(@co-explorer / @co-librarian / @co-observer)
157
- 3. 规范分析(并行 @co-rule-user / @co-rule-project / @co-rule-app)
158
- 4. 制定方案 → @co-planner
159
- 5. 调度执行(@co-fixer / @co-designer 等)
160
- 6. 验证(@co-oracle / @co-designer)
161
- ```
162
-
163
- ## 依赖
164
-
165
- - [OpenCode](https://opencode.ai) ≥ 1.17
166
- - `@opencode-ai/plugin@^1.17`(运行时依赖)
167
- - `@opencode-ai/sdk@^1.3`(运行时依赖)
168
- - 无其他外部插件依赖
169
-
170
- ## 自定义模型
171
-
172
- **推荐**:直接编辑 `~/.config/opencode/oh-my-opencode-cohub.json`(见「配置文件」章节),修改对应代理的 `model` `variant` 即可。
173
-
174
- **备选**:通过 `opencode.json` 的 plugin config 覆盖:
175
-
176
- ```json
177
- {
178
- "plugin": [
179
- {
180
- "name": "oh-my-opencode-cohub",
181
- "config": {
182
- "models": {
183
- "orchestrator": "openai/gpt-5.5",
184
- "fixer": "openai/gpt-5.4-mini"
185
- }
186
- }
187
- }
188
- ]
189
- }
190
- ```
191
-
192
- > 注意:plugin config 中的 key 使用无 `co-` 前缀的短名(如 `orchestrator`);JSON 配置文件使用完整的 `co-` 前缀名(如 `co-orchestrator`)。
193
-
194
- ## 自定义提示词
195
-
196
- ### 方式一:通过 plugin config 覆盖(推荐)
197
-
198
- 在 `opencode.json` 的 plugin config 中传入自定义 prompt:
199
-
200
- ```json
201
- {
202
- "plugin": [
203
- {
204
- "name": "oh-my-opencode-cohub",
205
- "config": {
206
- "overrides": {
207
- "oracle": "你是我的自定义 Oracle 代理——负责代码审查和安全审计……",
208
- "fixer": "你是我的自定义 Fixer 代理——只做 Python 代码修改……"
209
- }
210
- }
211
- }
212
- ]
213
- }
214
- ```
215
-
216
- 支持覆盖的代理名称:`orchestrator`、`oracle`、`librarian`、`explorer`、`designer`、`fixer`、`observer`、`council`、`rule-user`、`rule-project`、`rule-app`、`planner`
217
-
218
- ### 方式二:文件覆盖(类 oh-my-opencode-slim 机制)
219
-
220
- 在项目 `.opencode/oh-my-opencode-cohub/` 或 `~/.config/opencode/oh-my-opencode-cohub/` 目录下创建 `{agent}.md` 文件:
221
-
222
- ```
223
- oh-my-opencode-cohub/
224
- ├── oracle.md ← 完全替换 oracle 提示词
225
- ├── fixer_append.md ← 追加到 fixer 提示词末尾
226
- └── planner.md ← 完全替换 planner 提示词
227
- ```
228
-
229
- 文件名规则:
230
- - `{agent}.md` —— 完全替换该代理的内置提示词
231
- - `{agent}_append.md` —— 追加内容到内置提示词末尾
232
-
233
- 查找优先级:项目 `.opencode/` > `~/.config/opencode/`
234
-
235
- ### 方式三:Fork 并修改源码
236
-
237
- ```bash
238
- # 修改 src/prompts/oracle.ts 并重新编译
239
- npm run build
240
- ```
241
-
242
- 适合深度定制,但需维护自己的分支。
243
-
244
- ## 许可证
245
-
246
- MIT
1
+ # oh-my-opencode-cohub
2
+
3
+ OpenCode 中文智能体编排插件 CoHub——纯调度模式、全中文提示词、规范分析代理、方案制定代理。
4
+
5
+ [![GitHub](https://img.shields.io/badge/GitHub-Mr--cjf%2Foh--my--opencode--cohub-blue?logo=github)](https://github.com/Mr-cjf/oh-my-opencode-cohub)
6
+
7
+ ## 特性
8
+
9
+ - 🚀 **独立运行**:纯 OpenCode Plugin,依赖 `@opencode-ai/plugin` SDK,零外部插件依赖
10
+ - 🤖 **12 个内置代理**:8 个通用代理(orchestrator/oracle/librarian/explorer/designer/fixer/observer/council)+ 4 个规范/方案代理(rule-user/rule-project/rule-app/planner)
11
+ - 🪝 **自动注入**:通过 `system.transform` 钩子在每次 LLM 调用时注入中文语言要求
12
+ - 🧠 **纯调度 Orchestrator**:不碰任何文件工具,只负责规划、委派、验证
13
+ - 🇨🇳 **全中文提示词**:12 个代理全部使用中文提示词
14
+ - 📋 **规范分析代理**:rule-user(用户AGENTS.md)、rule-project(项目AGENTS.md)、rule-app(.opencode/rules/)
15
+ - 🎯 **方案制定代理**:planner 综合所有输入输出结构化任务分解
16
+ - 🔗 **OpenSpec + Superpowers 死板路由**:收到需求即触发,不判断"大小"
17
+ - **六步工作流**:理解→收集→规范分析→制定方案→调度→验证
18
+ - 📜 **AGENTS.md 全覆盖**:项目级和用户级 AGENTS.md 对**本插件所有代理**(内置 8 个 + 插件 4 个)均生效,无需额外配置
19
+ - 📖 **架构借鉴**:插件架构和代理编排理念借鉴了 oh-my-opencode-slim,但完全独立实现、无运行时依赖
20
+
21
+ ## 安装
22
+
23
+ ### 前置条件
24
+
25
+ - [OpenCode](https://opencode.ai) 已安装
26
+ - 推荐安装 [Bun](https://bun.sh)(CLI 一键安装需要)
27
+
28
+ ### 方式一:CLI 一键安装(推荐)
29
+
30
+ ```bash
31
+ bunx oh-my-opencode-cohub install
32
+ ```
33
+
34
+ CLI 会自动完成以下操作:
35
+
36
+ 1. `oh-my-opencode-cohub` 注册到 `~/.config/opencode/opencode.json` 的 `plugin` 数组
37
+ 2. 将插件注册到 TUI 配置 `~/.config/opencode/tui.json`
38
+ 3. 在 `opencode.json` 的 `agent` 字段中注册全部 12 个 `co-*` 代理
39
+ 4. 写入默认配置文件 `~/.config/opencode/oh-my-opencode-cohub.json`
40
+
41
+ 重启 OpenCode 即可生效。
42
+
43
+ ### 方式二:npm 安装 + 手动配置
44
+
45
+ ```bash
46
+ npm install oh-my-opencode-cohub
47
+ ```
48
+
49
+ 然后在 `~/.config/opencode/opencode.json` 中添加:
50
+
51
+ ```json
52
+ {
53
+ "plugin": ["oh-my-opencode-cohub"]
54
+ }
55
+ ```
56
+
57
+ 重启 OpenCode 即可。
58
+
59
+ ### 卸载
60
+
61
+ ```bash
62
+ bunx oh-my-opencode-cohub uninstall
63
+ ```
64
+
65
+ CLI 会精确清理所有 `co-*` 代理和相关配置,不影响其他插件的配置数据。
66
+
67
+ 如果使用 npm 手动安装,则需手动从 `opencode.json` 的 `plugin` 数组中移除 `"oh-my-opencode-cohub"`,然后 `npm uninstall oh-my-opencode-cohub`。
68
+
69
+ ### 验证安装
70
+
71
+ 安装并重启 OpenCode 后,确认以下效果:
72
+
73
+ - 对话中自动出现 **中文语言要求**(如 "你必须始终使用中文进行思考、推理和回复")
74
+ - 可用代理列表中出现 12 个 `co-*` 前缀代理(`co-orchestrator`、`co-oracle`、`co-librarian`、`co-explorer`、`co-designer`、`co-fixer`、`co-observer`、`co-council`、`co-rule-user`、`co-rule-project`、`co-rule-app`、`co-planner`)
75
+ - Orchestrator 自动进入纯调度模式(只做规划、委派、验证,不直接操作文件)
76
+
77
+ ### 从源码安装(本地开发)
78
+
79
+ 如果需要修改源码或使用开发版本:
80
+
81
+ ```bash
82
+ # 1. 克隆项目
83
+ git clone https://github.com/Mr-cjf/oh-my-opencode-cohub.git && cd oh-my-opencode-cohub
84
+
85
+ # 2. 安装依赖
86
+ npm install
87
+
88
+ # 3. 构建(需要系统已安装 Bun)
89
+ npm run build
90
+
91
+ # 4. 方式 A:npm link 全局链接(推荐)
92
+ npm link
93
+ bunx oh-my-opencode-cohub install
94
+ # 重启 OpenCode 即可。之后修改源码只需重新 npm run build
95
+
96
+ # 4. 方式 B:手动配置绝对路径
97
+ # 编辑 ~/.config/opencode/opencode.json,在 plugin 数组中写入项目绝对路径:
98
+ # { "plugin": ["/absolute/path/to/oh-my-opencode-cohub"] }
99
+ # 然后手动运行 CLI 注册代理:
100
+ # node dist/cli/index.js install
101
+ ```
102
+
103
+ > **构建依赖**:`npm run build` 使用 `bun build` 和 `tsc`。系统需安装 [Bun](https://bun.sh)(构建工具)和 Node.js(类型声明生成)。所有 `@opencode-ai/*`、`@opentui/*`、`zod` 均为 external,不打包进 `dist/`。
104
+
105
+ ## 代理一览
106
+
107
+ ### 代理(12 个,全中文提示词)
108
+
109
+ | 代理 | 默认模型 | 职责 | 读取源 |
110
+ |------|----------|------|--------|
111
+ | co-orchestrator | `deepseek/deepseek-v4-pro` | 纯调度:规划→委派→验证 | — |
112
+ | co-oracle | `deepseek/deepseek-v4-pro` | 架构审查/代码审查(含 Superpowers skills) | — |
113
+ | co-librarian | `deepseek/deepseek-v4-flash` | 外部文档/API 研究 | — |
114
+ | co-explorer | `deepseek/deepseek-v4-flash` | 代码库搜索定位 | — |
115
+ | co-designer | `minimax/MiniMax-M3` | UI/UX 设计与实现 | — |
116
+ | co-fixer | `deepseek/deepseek-v4-flash` | 代码修改执行(含 TDD skill) | — |
117
+ | co-observer | `codermxtest/gpt-5.5` | 图片/PDF 分析 | |
118
+ | co-council | `deepseek/deepseek-v4-pro` | 多模型共识 | |
119
+ | co-rule-user | `deepseek/deepseek-v4-flash` | 用户级规范分析 | `~/.config/opencode/AGENTS.md` |
120
+ | co-rule-project | `deepseek/deepseek-v4-flash` | 项目级规范分析 | 项目 `AGENTS.md` |
121
+ | co-rule-app | `deepseek/deepseek-v4-flash` | 应用规则分析 | `.opencode/rules/*.md` |
122
+ | co-planner | `deepseek/deepseek-v4-pro` | 方案制定 | 综合需求+信息+规则 |
123
+
124
+ > 模型可通过下文「配置文件」或「自定义模型」章节覆盖。
125
+
126
+ ## 配置文件
127
+
128
+ CLI 安装后自动创建 `~/.config/opencode/oh-my-opencode-cohub.json`,这是 CoHub 的专用配置文件,结构如下:
129
+
130
+ ```json
131
+ {
132
+ "$schema": "https://unpkg.com/oh-my-opencode-cohub@latest/oh-my-opencode-cohub.schema.json",
133
+ "agents": {
134
+ "co-orchestrator": { "model": "deepseek/deepseek-v4-pro", "variant": "max" },
135
+ "co-oracle": { "model": "deepseek/deepseek-v4-pro", "variant": "max" },
136
+ "co-librarian": { "model": "deepseek/deepseek-v4-flash", "variant": "low" },
137
+ "co-explorer": { "model": "deepseek/deepseek-v4-flash", "variant": "low" },
138
+ "co-designer": { "model": "minimax/MiniMax-M3", "variant": "medium" },
139
+ "co-fixer": { "model": "deepseek/deepseek-v4-flash", "variant": "high" },
140
+ "co-observer": { "model": "codermxtest/gpt-5.5", "variant": "low" },
141
+ "co-council": { "model": "deepseek/deepseek-v4-pro", "variant": "high" },
142
+ "co-rule-user": { "model": "deepseek/deepseek-v4-flash", "variant": "medium" },
143
+ "co-rule-project":{ "model": "deepseek/deepseek-v4-flash", "variant": "medium" },
144
+ "co-rule-app": { "model": "deepseek/deepseek-v4-flash", "variant": "medium" },
145
+ "co-planner": { "model": "deepseek/deepseek-v4-pro", "variant": "high" }
146
+ }
147
+ }
148
+ ```
149
+
150
+ 直接编辑此文件即可修改任意代理的模型和变体。`$schema` 提供 IDE 自动补全支持。
151
+
152
+ > **注意**:同一目录 `~/.config/opencode/oh-my-opencode-cohub/` 下也可放置 `{agent}.md` 提示词覆盖文件(见「自定义提示词」章节),与 JSON 配置文件共存。
153
+
154
+ ### Council 多模型共识配置
155
+
156
+ `co-council` 代理通过 `council_session` 工具并行调用多个模型(councillors),综合各方观点形成共识。councillors 的数量、模型、推理强度完全可配置:
157
+
158
+ ```json
159
+ {
160
+ "council": {
161
+ "default_preset": "default",
162
+ "timeout": 180000,
163
+ "councillor_execution_mode": "parallel",
164
+ "councillor_retries": 3,
165
+ "presets": {
166
+ "default": {
167
+ "alpha": { "model": "deepseek/deepseek-v4-pro", "variant": "max" },
168
+ "beta": { "model": "deepseek/deepseek-v4-flash", "variant": "high" },
169
+ "gamma": { "model": "minimax/MiniMax-M3", "variant": "medium" }
170
+ },
171
+ "quick": {
172
+ "alpha": { "model": "deepseek/deepseek-v4-flash", "variant": "high" },
173
+ "beta": { "model": "deepseek/deepseek-v4-flash", "variant": "low" }
174
+ },
175
+ "deep": {
176
+ "alpha": { "model": "deepseek/deepseek-v4-pro", "variant": "max" },
177
+ "beta": { "model": "deepseek/deepseek-v4-pro", "variant": "max" },
178
+ "gamma": { "model": "minimax/MiniMax-M3", "variant": "medium" },
179
+ "delta": { "model": "codermxtest/gpt-5.5", "variant": "high" }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ | 字段 | 类型 | 默认值 | 说明 |
187
+ |------|------|--------|------|
188
+ | `presets` | object | (必填) | 预设字典,每个预设是一组 councillor |
189
+ | `default_preset` | string | `"default"` | 调用时不指定 preset 时使用的预设 |
190
+ | `timeout` | number | `180000` | 单个 councillor 超时(毫秒),默认 3 分钟 |
191
+ | `councillor_execution_mode` | `"parallel"` \| `"serial"` | `"parallel"` | 并行/串行执⾏ |
192
+ | `councillor_retries` | number | `3` | 空响应重试次数 |
193
+
194
+ - 每个 councillor 的 key(如 `alpha`、`beta`)可任意命名,会出现在输出中
195
+ - `model` 格式为 `"provider/model"`,`variant` 可选 `"max"` / `"high"` / `"medium"` / `"low"`
196
+ - councillor 数量无上限,但建议 2-5 个以平衡成本和质量
197
+ - 调用时可通过 `council_session` 工具的 `preset` 参数切换预设,如 `"quick"` 或 `"deep"`
198
+
199
+ ## 工作流
200
+
201
+ ```
202
+ 1. 理解需求
203
+ 2. 信息收集(@co-explorer / @co-librarian / @co-observer)
204
+ 3. 规范分析(并行 @co-rule-user / @co-rule-project / @co-rule-app)
205
+ 4. 制定方案 → @co-planner
206
+ 5. 调度执行(@co-fixer / @co-designer 等)
207
+ 6. 验证(@co-oracle / @co-designer)
208
+ ```
209
+
210
+ ## 依赖
211
+
212
+ - [OpenCode](https://opencode.ai) ≥ 1.17
213
+ - `@opencode-ai/plugin@^1.17`(运行时依赖)
214
+ - `@opencode-ai/sdk@^1.3`(运行时依赖)
215
+ - 无其他外部插件依赖
216
+
217
+ ## 自定义模型
218
+
219
+ **推荐**:直接编辑 `~/.config/opencode/oh-my-opencode-cohub.json`(见「配置文件」章节),修改对应代理的 `model` 和 `variant` 即可。
220
+
221
+ **备选**:通过 `opencode.json` 的 plugin config 覆盖:
222
+
223
+ ```json
224
+ {
225
+ "plugin": [
226
+ {
227
+ "name": "oh-my-opencode-cohub",
228
+ "config": {
229
+ "models": {
230
+ "orchestrator": "openai/gpt-5.5",
231
+ "fixer": "openai/gpt-5.4-mini"
232
+ }
233
+ }
234
+ }
235
+ ]
236
+ }
237
+ ```
238
+
239
+ > 注意:plugin config 中的 key 使用无 `co-` 前缀的短名(如 `orchestrator`);JSON 配置文件使用完整的 `co-` 前缀名(如 `co-orchestrator`)。
240
+
241
+ ## 自定义提示词
242
+
243
+ ### 方式一:通过 plugin config 覆盖(推荐)
244
+
245
+ 在 `opencode.json` 的 plugin config 中传入自定义 prompt:
246
+
247
+ ```json
248
+ {
249
+ "plugin": [
250
+ {
251
+ "name": "oh-my-opencode-cohub",
252
+ "config": {
253
+ "overrides": {
254
+ "oracle": "你是我的自定义 Oracle 代理——负责代码审查和安全审计……",
255
+ "fixer": "你是我的自定义 Fixer 代理——只做 Python 代码修改……"
256
+ }
257
+ }
258
+ }
259
+ ]
260
+ }
261
+ ```
262
+
263
+ 支持覆盖的代理名称:`orchestrator`、`oracle`、`librarian`、`explorer`、`designer`、`fixer`、`observer`、`council`、`rule-user`、`rule-project`、`rule-app`、`planner`
264
+
265
+ ### 方式二:文件覆盖(类 oh-my-opencode-slim 机制)
266
+
267
+ 在项目 `.opencode/oh-my-opencode-cohub/` 或 `~/.config/opencode/oh-my-opencode-cohub/` 目录下创建 `{agent}.md` 文件:
268
+
269
+ ```
270
+ oh-my-opencode-cohub/
271
+ ├── oracle.md ← 完全替换 oracle 提示词
272
+ ├── fixer_append.md ← 追加到 fixer 提示词末尾
273
+ └── planner.md ← 完全替换 planner 提示词
274
+ ```
275
+
276
+ 文件名规则:
277
+ - `{agent}.md` —— 完全替换该代理的内置提示词
278
+ - `{agent}_append.md` —— 追加内容到内置提示词末尾
279
+
280
+ 查找优先级:项目 `.opencode/` > `~/.config/opencode/`
281
+
282
+ ### 方式三:Fork 并修改源码
283
+
284
+ ```bash
285
+ # 修改 src/prompts/oracle.ts 并重新编译
286
+ npm run build
287
+ ```
288
+
289
+ 适合深度定制,但需维护自己的分支。
290
+
291
+ ## 版本历史
292
+
293
+ 完整变更记录见 [CHANGELOG.md](./CHANGELOG.md)。
294
+
295
+ | 版本 | 日期 | 主要变更 |
296
+ |------|------|---------|
297
+ | [1.0.18](https://github.com/Mr-cjf/oh-my-opencode-cohub/releases/tag/v1.0.18) | 2026-07-15 | `council_session` 多模型并行共识工具 |
298
+ | [1.0.17](https://github.com/Mr-cjf/oh-my-opencode-cohub/releases/tag/v1.0.17) | 2026-07-15 | @council vs @oracle 选择指南 + DEBUG 日志移除 |
299
+ | [1.0.16](https://github.com/Mr-cjf/oh-my-opencode-cohub/releases/tag/v1.0.16) | 2026-07-15 | 背景任务事件驱动跟踪 + agent 双重注册 |
300
+ | [1.0.15](https://github.com/Mr-cjf/oh-my-opencode-cohub/releases/tag/v1.0.15) | 2026-07-14 | co-orchestrator 完整中文提示词 |
301
+ | [1.0.14](https://github.com/Mr-cjf/oh-my-opencode-cohub/releases/tag/v1.0.14) | 2026-07-14 | HTTP 服务器模式兼容修复 |
302
+ | [1.0.13](https://github.com/Mr-cjf/oh-my-opencode-cohub/releases/tag/v1.0.13) | 初始发布 | 12 个 co-* 代理 + 纯调度模式 |
303
+
304
+ ## 许可证
305
+
306
+ MIT
@@ -1 +1 @@
1
- {"version":3,"file":"config-io.d.ts","sourceRoot":"","sources":["../../src/cli/config-io.ts"],"names":[],"mappings":"AA4BA,2CAA2C;AAC3C,wBAAgB,yBAAyB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAajF;AAED,sCAAsC;AACtC,wBAAgB,oBAAoB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAa5E;AAOD,mDAAmD;AACnD,wBAAgB,mBAAmB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAmC3E;AAID,uBAAuB;AACvB,wBAAgB,kBAAkB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA6B1E;AAED,8BAA8B;AAC9B,wBAAgB,cAAc,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA6GzE"}
1
+ {"version":3,"file":"config-io.d.ts","sourceRoot":"","sources":["../../src/cli/config-io.ts"],"names":[],"mappings":"AA4BA,2CAA2C;AAC3C,wBAAgB,yBAAyB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAajF;AAED,sCAAsC;AACtC,wBAAgB,oBAAoB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAa5E;AAOD,mDAAmD;AACnD,wBAAgB,mBAAmB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAmC3E;AAID,uBAAuB;AACvB,wBAAgB,kBAAkB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA0C1E;AAED,8BAA8B;AAC9B,wBAAgB,cAAc,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA6GzE"}
package/dist/cli/index.js CHANGED
@@ -426,7 +426,7 @@ function registerCoHubAgents() {
426
426
  ## Council 总结
427
427
  总结 councillor 在哪些方面共识、哪些方面分歧,为什么选择最终答案,以及剩余的不确定性。包含共识信心评级:一致、多数或分歧。
428
428
 
429
- **语言要求**: 始终使用中文进行思考、综合和回复。Council 响应、总结等自然语言部分必须用中文。Code examples 可用英文。`, model: "deepseek/deepseek-v4-pro", variant: "high" },
429
+ **语言要求**: 始终使用中文进行思考、综合和回复。Council 响应、总结等自然语言部分必须用中文。Code examples 可用英文。`, model: "deepseek/deepseek-v4-pro", variant: "high", permission: { council_session: "allow" } },
430
430
  "co-rule-user": { description: "用户规范分析", mode: "subagent", prompt: `你是规则分析代理——负责用户级规范。
431
431
 
432
432
  **职责**:读取 \`~/.config/opencode/AGENTS.md\`(用户级全局规则),结合 Orchestrator 提供的当前方案,分析是否有遗漏或冲突。返回具体的调整建议(不要笼统)。
@@ -486,6 +486,19 @@ function writeDefaultConfig() {
486
486
  "co-rule-project": { model: "deepseek/deepseek-v4-flash", variant: "medium" },
487
487
  "co-rule-app": { model: "deepseek/deepseek-v4-flash", variant: "medium" },
488
488
  "co-planner": { model: "deepseek/deepseek-v4-pro", variant: "high" }
489
+ },
490
+ council: {
491
+ default_preset: "default",
492
+ timeout: 180000,
493
+ councillor_execution_mode: "parallel",
494
+ councillor_retries: 3,
495
+ presets: {
496
+ default: {
497
+ alpha: { model: "deepseek/deepseek-v4-pro", variant: "max" },
498
+ beta: { model: "deepseek/deepseek-v4-flash", variant: "high" },
499
+ gamma: { model: "minimax/MiniMax-M3", variant: "medium" }
500
+ }
501
+ }
489
502
  }
490
503
  };
491
504
  try {
@@ -1,3 +1,19 @@
1
+ /** 单个 councillor 配置 */
2
+ export interface CouncillorConfig {
3
+ model: string;
4
+ variant?: string;
5
+ prompt?: string;
6
+ }
7
+ export interface CouncilPreset {
8
+ [councillorName: string]: CouncillorConfig;
9
+ }
10
+ export interface CouncilConfig {
11
+ presets: Record<string, CouncilPreset>;
12
+ timeout?: number;
13
+ default_preset?: string;
14
+ councillor_execution_mode?: 'parallel' | 'serial';
15
+ councillor_retries?: number;
16
+ }
1
17
  /** 用户配置中单个代理的覆盖项 */
2
18
  export interface AgentOverride {
3
19
  model?: string;
@@ -6,6 +22,7 @@ export interface AgentOverride {
6
22
  }
7
23
  export interface CoHubConfig {
8
24
  agents?: Record<string, AgentOverride>;
25
+ council?: CouncilConfig;
9
26
  }
10
27
  /** 加载用户配置 */
11
28
  export declare function loadCoHubConfig(): CoHubConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAIA,oBAAoB;AACpB,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACxC;AAKD,aAAa;AACb,wBAAgB,eAAe,IAAI,WAAW,CAQ7C;AAED,aAAa;AACb,eAAO,MAAM,cAAc,EAAE,WAe5B,CAAC"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAIA,uBAAuB;AACvB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yBAAyB,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;IAClD,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,oBAAoB;AACpB,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAKD,aAAa;AACb,wBAAgB,eAAe,IAAI,WAAW,CAQ7C;AAED,aAAa;AACb,eAAO,MAAM,cAAc,EAAE,WAe5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAqClD,wBAAwB;AACxB,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAalD,CAAC;AAwEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,wrBAA+B,CAAC;AAEhE,QAAA,MAAM,WAAW,EAAE,MAkRlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAsClD,wBAAwB;AACxB,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAalD,CAAC;AAwEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,wrBAA+B,CAAC;AAEhE,QAAA,MAAM,WAAW,EAAE,MA6SlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -616,6 +616,347 @@ function loadCoHubConfig() {
616
616
  }
617
617
  }
618
618
 
619
+ // src/tools/council.ts
620
+ import { tool } from "@opencode-ai/plugin";
621
+ var z = tool.schema;
622
+
623
+ class OperationTimeoutError extends Error {
624
+ constructor(message) {
625
+ super(message);
626
+ this.name = "OperationTimeoutError";
627
+ }
628
+ }
629
+ function isTextPart(part) {
630
+ return (part.type === "text" || part.type === "reasoning") && typeof part.text === "string";
631
+ }
632
+ function shortModelLabel(model) {
633
+ return model.split("/").pop() ?? model;
634
+ }
635
+ function parseModelReference(model) {
636
+ const slashIndex = model.indexOf("/");
637
+ if (slashIndex <= 0 || slashIndex >= model.length - 1)
638
+ return null;
639
+ return {
640
+ providerID: model.slice(0, slashIndex),
641
+ modelID: model.slice(slashIndex + 1)
642
+ };
643
+ }
644
+ async function abortSession(client, sessionId) {
645
+ try {
646
+ await client.session.abort({ path: { id: sessionId } });
647
+ } catch {}
648
+ }
649
+ async function promptWithTimeout(client, path2, body, timeoutMs, directory) {
650
+ const sessionId = path2.id;
651
+ let timer;
652
+ try {
653
+ const promptPromise = client.session.prompt({
654
+ path: path2,
655
+ body,
656
+ query: directory ? { directory } : undefined
657
+ });
658
+ promptPromise.catch(() => {});
659
+ const racers = [promptPromise];
660
+ if (timeoutMs > 0) {
661
+ racers.push(new Promise((_, reject) => {
662
+ timer = setTimeout(() => {
663
+ reject(new OperationTimeoutError(`Prompt timed out after ${timeoutMs}ms`));
664
+ }, timeoutMs);
665
+ }));
666
+ }
667
+ await Promise.race(racers);
668
+ } catch (error) {
669
+ if (error instanceof OperationTimeoutError) {
670
+ await abortSession(client, sessionId);
671
+ }
672
+ throw error;
673
+ } finally {
674
+ if (timer !== undefined)
675
+ clearTimeout(timer);
676
+ }
677
+ }
678
+ async function extractSessionResult(client, sessionId, options) {
679
+ const includeReasoning = options?.includeReasoning ?? true;
680
+ const messagesResult = await client.session.messages({
681
+ path: { id: sessionId }
682
+ });
683
+ const messages = messagesResult.data ?? [];
684
+ const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
685
+ const extractedContent = [];
686
+ for (const message of assistantMessages) {
687
+ for (const part of message.parts ?? []) {
688
+ if (!isTextPart(part))
689
+ continue;
690
+ const allowed = includeReasoning || part.type === "text";
691
+ if (allowed && part.text) {
692
+ extractedContent.push(part.text);
693
+ }
694
+ }
695
+ }
696
+ const text = extractedContent.filter((t) => t.length > 0).join(`
697
+
698
+ `);
699
+ return { text, empty: text.length === 0 };
700
+ }
701
+ function formatCouncillorPrompt(userPrompt, councillorPrompt) {
702
+ if (!councillorPrompt)
703
+ return userPrompt;
704
+ return `${councillorPrompt}
705
+
706
+ ---
707
+
708
+ ${userPrompt}`;
709
+ }
710
+ function formatCouncillorResults(originalPrompt, councillorResults) {
711
+ const completedWithResults = councillorResults.filter((cr) => cr.status === "completed" && cr.result);
712
+ const councillorSection = completedWithResults.map((cr) => {
713
+ const shortModel = shortModelLabel(cr.model);
714
+ return `**${cr.name}** (${shortModel}):
715
+ ${cr.result}`;
716
+ }).join(`
717
+
718
+ `);
719
+ const failedEntries = councillorResults.filter((cr) => cr.status !== "completed");
720
+ const failedSection = failedEntries.map((cr) => `**${cr.name}**: ${cr.status} — ${cr.error ?? "Unknown"}`).join(`
721
+ `);
722
+ if (completedWithResults.length === 0) {
723
+ const errorDetails = councillorResults.map((cr) => `**${cr.name}** (${shortModelLabel(cr.model)}): ${cr.status} — ${cr.error ?? "Unknown"}`).join(`
724
+ `);
725
+ return [
726
+ "---",
727
+ "",
728
+ "**Original Prompt**:",
729
+ originalPrompt,
730
+ "",
731
+ "---",
732
+ "",
733
+ "**Councillor Responses**:",
734
+ "All councillors failed to produce output:",
735
+ errorDetails,
736
+ "",
737
+ "Please generate a response based on the original prompt alone."
738
+ ].join(`
739
+ `);
740
+ }
741
+ const parts = [
742
+ "---",
743
+ "",
744
+ "**Original Prompt**:",
745
+ originalPrompt,
746
+ "",
747
+ "---",
748
+ "",
749
+ "**Councillor Responses**:",
750
+ councillorSection
751
+ ];
752
+ if (failedSection) {
753
+ parts.push("", "---", "", "**Failed/Timed-out Councillors**:", failedSection);
754
+ }
755
+ parts.push("", "---", "", "You MUST follow the Synthesis Process steps before producing output: " + "review each councillor response individually, then produce the required output " + "with a synthesized Council Response, per-councillor details using their exact names, " + "and a Council Summary with consensus confidence rating (unanimous, majority, or split).");
756
+ return parts.join(`
757
+ `);
758
+ }
759
+ function formatModelComposition(councillorResults) {
760
+ return councillorResults.map((cr) => `${cr.name}: ${shortModelLabel(cr.model)}`).join(", ");
761
+ }
762
+
763
+ class CouncilManager {
764
+ client;
765
+ directory;
766
+ config;
767
+ constructor(client, directory, config) {
768
+ this.client = client;
769
+ this.directory = directory;
770
+ this.config = config;
771
+ }
772
+ async runCouncil(prompt, presetName, parentSessionId) {
773
+ const resolvedPreset = presetName ?? this.config.default_preset ?? "default";
774
+ const preset = this.config.presets[resolvedPreset];
775
+ if (!preset) {
776
+ const available = Object.keys(this.config.presets).join(", ");
777
+ return {
778
+ success: false,
779
+ error: `Preset "${resolvedPreset}" does not exist. Available presets: ${available}`,
780
+ councillorResults: []
781
+ };
782
+ }
783
+ if (Object.keys(preset).length === 0) {
784
+ return {
785
+ success: false,
786
+ error: `Preset "${resolvedPreset}" has no councillors configured.`,
787
+ councillorResults: []
788
+ };
789
+ }
790
+ const timeout = this.config.timeout ?? 180000;
791
+ const executionMode = this.config.councillor_execution_mode ?? "parallel";
792
+ const maxRetries = this.config.councillor_retries ?? 3;
793
+ const councillorResults = await this.runCouncillors(prompt, preset, parentSessionId, timeout, executionMode, maxRetries);
794
+ const completedCount = councillorResults.filter((r) => r.status === "completed").length;
795
+ if (completedCount === 0) {
796
+ return {
797
+ success: false,
798
+ error: "All councillors failed or timed out",
799
+ councillorResults
800
+ };
801
+ }
802
+ const formatted = formatCouncillorResults(prompt, councillorResults);
803
+ return {
804
+ success: true,
805
+ result: formatted,
806
+ councillorResults
807
+ };
808
+ }
809
+ async runCouncillors(prompt, councillors, parentSessionId, timeout, executionMode, maxRetries) {
810
+ const entries = Object.entries(councillors);
811
+ const results = [];
812
+ if (executionMode === "serial") {
813
+ for (const [name, config] of entries) {
814
+ const r = await this.runCouncillorWithRetry(name, config, prompt, parentSessionId, timeout, maxRetries);
815
+ results.push(r);
816
+ }
817
+ } else {
818
+ const promises = entries.map(([name, config]) => this.runCouncillorWithRetry(name, config, prompt, parentSessionId, timeout, maxRetries));
819
+ const settled = await Promise.allSettled(promises);
820
+ for (let i = 0;i < settled.length; i++) {
821
+ const s = settled[i];
822
+ const [name, cfg] = entries[i];
823
+ if (s.status === "fulfilled") {
824
+ results.push(s.value);
825
+ } else {
826
+ results.push({
827
+ name,
828
+ model: cfg.model,
829
+ status: "failed",
830
+ error: s.reason instanceof Error ? s.reason.message : String(s.reason)
831
+ });
832
+ }
833
+ }
834
+ }
835
+ return results;
836
+ }
837
+ async runCouncillorWithRetry(name, config, prompt, parentSessionId, timeout, maxRetries) {
838
+ const totalAttempts = 1 + maxRetries;
839
+ for (let attempt = 1;attempt <= totalAttempts; attempt++) {
840
+ try {
841
+ const result = await this.runAgentSession({
842
+ parentSessionId,
843
+ title: `Council ${name} (${shortModelLabel(config.model)})`,
844
+ model: config.model,
845
+ promptText: formatCouncillorPrompt(prompt, config.prompt),
846
+ variant: config.variant,
847
+ timeout
848
+ });
849
+ return { name, model: config.model, status: "completed", result };
850
+ } catch (error) {
851
+ const msg = error instanceof Error ? error.message : String(error);
852
+ const isEmptyResponse = msg.includes("Empty response from provider");
853
+ const canRetry = attempt < totalAttempts && isEmptyResponse;
854
+ if (!canRetry) {
855
+ return {
856
+ name,
857
+ model: config.model,
858
+ status: msg.includes("timed out") ? "timed_out" : "failed",
859
+ error: `Councillor "${name}": ${msg}`
860
+ };
861
+ }
862
+ }
863
+ }
864
+ return {
865
+ name,
866
+ model: config.model,
867
+ status: "failed",
868
+ error: `Councillor "${name}": max retries exhausted`
869
+ };
870
+ }
871
+ async runAgentSession(options) {
872
+ const modelRef = parseModelReference(options.model);
873
+ if (!modelRef) {
874
+ throw new Error(`Invalid model format: ${options.model}`);
875
+ }
876
+ let sessionId;
877
+ try {
878
+ const session = await this.client.session.create({
879
+ body: {
880
+ parentID: options.parentSessionId,
881
+ title: options.title
882
+ },
883
+ query: { directory: this.directory }
884
+ });
885
+ if (!session.data?.id) {
886
+ throw new Error("Failed to create session");
887
+ }
888
+ sessionId = session.data.id;
889
+ const promptBody = {
890
+ model: modelRef,
891
+ tools: {
892
+ task: false,
893
+ question: false,
894
+ edit: false,
895
+ write: false,
896
+ apply_patch: false,
897
+ ast_grep_replace: false,
898
+ bash: false
899
+ },
900
+ parts: [{ type: "text", text: options.promptText }]
901
+ };
902
+ if (options.variant) {
903
+ promptBody.variant = options.variant;
904
+ }
905
+ await promptWithTimeout(this.client, { id: sessionId }, promptBody, options.timeout, this.directory);
906
+ const extraction = await extractSessionResult(this.client, sessionId);
907
+ if (extraction.empty) {
908
+ throw new Error("Empty response from provider");
909
+ }
910
+ return extraction.text;
911
+ } finally {
912
+ if (sessionId) {
913
+ abortSession(this.client, sessionId).catch(() => {});
914
+ }
915
+ }
916
+ }
917
+ }
918
+ function createCouncilTool(ctx, councilManager) {
919
+ const council_session = tool({
920
+ description: [
921
+ "Launch a multi-LLM council session for consensus-based analysis.",
922
+ "",
923
+ "Sends the prompt to multiple models (councillors) in parallel and returns",
924
+ "their formatted responses for you to synthesize.",
925
+ "",
926
+ "Returns the councillor responses with a summary footer."
927
+ ].join(`
928
+ `),
929
+ args: {
930
+ prompt: z.string().describe("The prompt to send to all councillors"),
931
+ preset: z.string().optional().describe('Council preset to use (default: "default"). Must match a preset in the council config.')
932
+ },
933
+ async execute(args, toolContext) {
934
+ const allowedAgents = ["co-council"];
935
+ const callingAgent = toolContext.agent;
936
+ if (callingAgent && !allowedAgents.includes(callingAgent)) {
937
+ throw new Error(`Council sessions can only be invoked by the co-council agent. Current agent: ${callingAgent}`);
938
+ }
939
+ const prompt = String(args.prompt);
940
+ const preset = typeof args.preset === "string" ? args.preset : undefined;
941
+ const parentSessionId = toolContext.sessionID;
942
+ const result = await councilManager.runCouncil(prompt, preset, parentSessionId);
943
+ if (!result.success) {
944
+ return `Council session failed: ${result.error}`;
945
+ }
946
+ let output = result.result ?? "(No output)";
947
+ const completed = result.councillorResults.filter((cr) => cr.status === "completed").length;
948
+ const total = result.councillorResults.length;
949
+ const composition = formatModelComposition(result.councillorResults);
950
+ output += `
951
+
952
+ ---
953
+ *Council: ${completed}/${total} councillors responded (${composition})*`;
954
+ return output;
955
+ }
956
+ });
957
+ return { council_session };
958
+ }
959
+
619
960
  // src/index.ts
620
961
  import * as fs2 from "node:fs";
621
962
  import * as path2 from "node:path";
@@ -757,7 +1098,13 @@ var CoHubPlugin = async (input, options) => {
757
1098
  {
758
1099
  name: "co-council",
759
1100
  description: "多模型共识——并行 LLM 综合",
760
- config: { mode: "subagent", model: "deepseek/deepseek-v4-pro", variant: "high", prompt: "你是多模型共识协调者。用中文回复。" }
1101
+ config: {
1102
+ mode: "subagent",
1103
+ model: "deepseek/deepseek-v4-pro",
1104
+ variant: "high",
1105
+ prompt: COUNCIL_PROMPT,
1106
+ permission: { council_session: "allow" }
1107
+ }
761
1108
  },
762
1109
  {
763
1110
  name: "co-rule-user",
@@ -794,6 +1141,22 @@ var CoHubPlugin = async (input, options) => {
794
1141
  }
795
1142
  }
796
1143
  }
1144
+ const DEFAULT_COUNCIL_CONFIG = {
1145
+ default_preset: "default",
1146
+ timeout: 180000,
1147
+ councillor_execution_mode: "parallel",
1148
+ councillor_retries: 3,
1149
+ presets: {
1150
+ default: {
1151
+ alpha: { model: "deepseek/deepseek-v4-pro", variant: "max" },
1152
+ beta: { model: "deepseek/deepseek-v4-flash", variant: "high" },
1153
+ gamma: { model: "minimax/MiniMax-M3", variant: "medium" }
1154
+ }
1155
+ }
1156
+ };
1157
+ const councilConfig = userConfig.council ?? DEFAULT_COUNCIL_CONFIG;
1158
+ const councilManager = new CouncilManager(input.client, input.directory, councilConfig);
1159
+ const councilTools = createCouncilTool(input, councilManager);
797
1160
  syncAgentConfig();
798
1161
  function extractChildSessionId(output) {
799
1162
  if (!output || typeof output !== "object")
@@ -846,6 +1209,7 @@ var CoHubPlugin = async (input, options) => {
846
1209
  return {
847
1210
  name: "oh-my-opencode-cohub",
848
1211
  agent: agentConfigs,
1212
+ tool: councilTools,
849
1213
  config: async (cfg) => {
850
1214
  const c = cfg;
851
1215
  c.agent ??= {};
@@ -0,0 +1,65 @@
1
+ import { tool } from '@opencode-ai/plugin';
2
+ import type { PluginInput } from '@opencode-ai/plugin';
3
+ import type { createOpencodeClient } from '@opencode-ai/sdk';
4
+ export interface CouncillorConfig {
5
+ /** "provider/model" */
6
+ model: string;
7
+ /** "max" | "high" | "medium" | "low" */
8
+ variant?: string;
9
+ /** Optional per-councillor prompt prefix */
10
+ prompt?: string;
11
+ }
12
+ export interface CouncilPreset {
13
+ [councillorName: string]: CouncillorConfig;
14
+ }
15
+ export interface CouncilConfig {
16
+ /** Preset map (e.g. { default: { expert1: {...}, expert2: {...} } }) */
17
+ presets: Record<string, CouncilPreset>;
18
+ /** Per-councillor timeout in ms (default: 180000) */
19
+ timeout?: number;
20
+ /** Default preset name (default: "default") */
21
+ default_preset?: string;
22
+ /** Execution mode (default: "parallel") */
23
+ councillor_execution_mode?: 'parallel' | 'serial';
24
+ /** Retries on empty response (default: 3) */
25
+ councillor_retries?: number;
26
+ }
27
+ /** Internal councillor result shape */
28
+ interface CouncillorResult {
29
+ name: string;
30
+ model: string;
31
+ status: string;
32
+ result?: string;
33
+ error?: string;
34
+ }
35
+ export declare class CouncilManager {
36
+ private client;
37
+ private directory;
38
+ private config;
39
+ constructor(client: ReturnType<typeof createOpencodeClient>, directory: string, config: CouncilConfig);
40
+ /**
41
+ * Run a full council session.
42
+ * Resolves a preset, runs all councillors, and returns formatted results.
43
+ */
44
+ runCouncil(prompt: string, presetName?: string, parentSessionId?: string): Promise<{
45
+ success: boolean;
46
+ result?: string;
47
+ error?: string;
48
+ councillorResults: CouncillorResult[];
49
+ }>;
50
+ private runCouncillors;
51
+ private runCouncillorWithRetry;
52
+ private runAgentSession;
53
+ }
54
+ /**
55
+ * Create the `council_session` tool definition.
56
+ *
57
+ * The tool launches a multi-LLM council: multiple councillors run in parallel
58
+ * against their assigned models, and their responses are assembled into a
59
+ * structured block for the calling (co-council) agent to synthesize.
60
+ *
61
+ * Only the `co-council` agent is allowed to invoke this tool.
62
+ */
63
+ export declare function createCouncilTool(ctx: PluginInput, councilManager: CouncilManager): Record<string, ReturnType<typeof tool>>;
64
+ export {};
65
+ //# sourceMappingURL=council.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"council.d.ts","sourceRoot":"","sources":["../../src/tools/council.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAU7D,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,yBAAyB,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;IAClD,6CAA6C;IAC7C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,uCAAuC;AACvC,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+OD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAgB;gBAG5B,MAAM,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,EAC/C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,aAAa;IAOvB;;;OAGG;IACG,UAAU,CACd,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;KACvC,CAAC;YA2DY,cAAc;YA8Cd,sBAAsB;YAkDtB,eAAe;CA6E9B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,cAAc,EAAE,cAAc,GAC7B,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,CAyDzC"}
package/package.json CHANGED
@@ -1,53 +1,53 @@
1
- {
2
- "name": "oh-my-opencode-cohub",
3
- "version": "1.0.17",
4
- "description": "OpenCode 中文智能体编排插件 CoHub - 纯调度模式 / 全中文提示词 / 规范分析代理 / 方案制定代理 / 独立运行",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "bin": {
8
- "oh-my-opencode-cohub": "./dist/cli/index.js"
9
- },
10
- "types": "dist/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "import": "./dist/index.js",
14
- "types": "./dist/index.d.ts"
15
- },
16
- "./tui": {
17
- "import": "./dist/tui.js",
18
- "types": "./dist/tui.d.ts"
19
- }
20
- },
21
- "files": [
22
- "dist"
23
- ],
24
- "scripts": {
25
- "build": "bun build src/index.ts src/tui.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @opencode-ai/plugin/* --external @opencode-ai/sdk --external @opencode-ai/sdk/* --external @opentui/core --external @opentui/solid --external zod && bun build src/cli/index.ts --outdir dist/cli --target node --format esm && tsc --emitDeclarationOnly",
26
- "prepublishOnly": "npm run build"
27
- },
28
- "keywords": [
29
- "opencode",
30
- "plugin",
31
- "chinese",
32
- "agent",
33
- "orchestra",
34
- "orchestrator"
35
- ],
36
- "author": "",
37
- "license": "MIT",
38
- "dependencies": {
39
- "@opencode-ai/plugin": "^1.17.0",
40
- "@opencode-ai/sdk": "^1.3.17"
41
- },
42
- "optionalDependencies": {
43
- "@opentui/solid": "^0.4.3",
44
- "@opentui/core": "^0.4.3"
45
- },
46
- "devDependencies": {
47
- "@types/node": "^26.1.1",
48
- "typescript": "^5.4.0"
49
- },
50
- "opencode": {
51
- "plugin": true
52
- }
53
- }
1
+ {
2
+ "name": "oh-my-opencode-cohub",
3
+ "version": "1.0.19",
4
+ "description": "OpenCode 中文智能体编排插件 CoHub - 纯调度模式 / 全中文提示词 / 规范分析代理 / 方案制定代理 / 独立运行",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "oh-my-opencode-cohub": "./dist/cli/index.js"
9
+ },
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./tui": {
17
+ "import": "./dist/tui.js",
18
+ "types": "./dist/tui.d.ts"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "bun build src/index.ts src/tui.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @opencode-ai/plugin/* --external @opencode-ai/sdk --external @opencode-ai/sdk/* --external @opentui/core --external @opentui/solid --external zod && bun build src/cli/index.ts --outdir dist/cli --target node --format esm && tsc --emitDeclarationOnly",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "keywords": [
29
+ "opencode",
30
+ "plugin",
31
+ "chinese",
32
+ "agent",
33
+ "orchestra",
34
+ "orchestrator"
35
+ ],
36
+ "author": "",
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "@opencode-ai/plugin": "^1.17.0",
40
+ "@opencode-ai/sdk": "^1.3.17"
41
+ },
42
+ "optionalDependencies": {
43
+ "@opentui/solid": "^0.4.3",
44
+ "@opentui/core": "^0.4.3"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^26.1.1",
48
+ "typescript": "^5.4.0"
49
+ },
50
+ "opencode": {
51
+ "plugin": true
52
+ }
53
+ }