multiarena 0.1.0 → 0.1.3

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/LICENSE +21 -0
  3. package/README.md +282 -0
  4. package/dist/cli/args.d.ts +11 -0
  5. package/dist/cli/args.js +56 -0
  6. package/dist/config/loader.js +2 -2
  7. package/dist/config/types.d.ts +11 -1
  8. package/dist/core/deliberation.d.ts +53 -0
  9. package/dist/core/deliberation.js +356 -0
  10. package/dist/core/session.d.ts +3 -1
  11. package/dist/core/session.js +20 -17
  12. package/dist/core/turn.d.ts +2 -0
  13. package/dist/core/turn.js +32 -5
  14. package/dist/index.js +3 -49
  15. package/dist/isolation/worktree.d.ts +1 -1
  16. package/dist/isolation/worktree.js +8 -8
  17. package/dist/persistence/session.js +1 -1
  18. package/dist/provider/adapters/openai.d.ts +15 -0
  19. package/dist/provider/adapters/openai.js +67 -8
  20. package/dist/provider/provider.js +4 -0
  21. package/dist/tools/builtin/bash.js +6 -1
  22. package/dist/ui/app.js +426 -46
  23. package/dist/ui/components/BroadcastSummary.d.ts +1 -0
  24. package/dist/ui/components/BroadcastSummary.js +24 -8
  25. package/dist/ui/components/DeliberationView.d.ts +17 -0
  26. package/dist/ui/components/DeliberationView.js +81 -0
  27. package/dist/ui/components/InputBar.d.ts +3 -0
  28. package/dist/ui/components/InputBar.js +18 -8
  29. package/dist/ui/components/ModelDetail.js +16 -4
  30. package/dist/ui/components/OutputArea.d.ts +8 -0
  31. package/dist/ui/components/OutputArea.js +32 -4
  32. package/dist/ui/components/formatTokens.d.ts +1 -0
  33. package/dist/ui/components/formatTokens.js +7 -0
  34. package/dist/ui/modeTransitions.d.ts +80 -0
  35. package/dist/ui/modeTransitions.js +176 -0
  36. package/package.json +13 -8
  37. package/dist/ui/components/StatusBar.d.ts +0 -9
  38. package/dist/ui/components/StatusBar.js +0 -51
package/CHANGELOG.md ADDED
@@ -0,0 +1,131 @@
1
+ # Changelog
2
+
3
+ ## v0.1.3
4
+
5
+ ### 关键升级
6
+
7
+ - **模式导航重构** — 广播/团队两种顶层模式,Tab 在模式内循环(概览 ↔ 模型),Esc 返回概览,Shift+Tab 切换模式
8
+ - **团队私聊** — 审议完成后可 Tab 切换到任一模型继续对话,模型自动获得审议结果上下文
9
+ - **镜像轮次审议** — 多模型从正向到反向接力(ABCBA),最终轮自动清理过程标注产出干净文档
10
+ - **审议流程摘要** — 每轮显示修改处数和代表性修改内容,类似 git 提交记录
11
+ - **审议继续修改** — 审议完成后在团队总览输入新要求即可启动新一轮修改
12
+ - **对比模式退出修复** — 按 `d` 不会错误对比相同模型,支持 wrap-around 选择下一个不同模型
13
+
14
+ ### 关键 Bug 修复
15
+
16
+ - **Esc 键在 Windows Terminal 不生效** — 绕过 Ink 的 `useInput`,使用原始 `process.stdin` 监听 `\x1b` 字节,35ms 超时区分独立 Esc 和转义序列
17
+ - **Shift+Tab 只从概览生效** — 从定向模式按 Shift+Tab 不再意外切换,必须先 Esc 回概览
18
+ - Tab 循环跳过静音模型,不会"卡"在不可见模型上
19
+ - 审议完成后 Tab 发送消息不再错误启动新一轮审议
20
+ - 广播模式 divider 渲染残留修复 — 移除冗余分隔线
21
+ - 审议视图文字过暗修复 — 去除双重灰色叠加,添加滚动支持
22
+ - 审议视图内容无法滚动 — 新增 `deliberationScrollOffset`,滚轮浏览审议历史
23
+ - npm pack 缺少 `dist/` — 创建 `.npmignore` 覆盖 `.gitignore` 的 `dist/` 排除
24
+ - 删除残留的 `StatusBar.js` / `.d.ts` 编译产物,新增 `prebuild` 清理步骤
25
+ - 最终文档不再包含 `[修订:]` / `[补充:]` 过程标注
26
+ - DeepSeek 不再将创意写作误判为编程任务(优化系统提示词)
27
+ - Think-tag 过滤逻辑提取为可测试纯函数
28
+
29
+ ### 测试
30
+
31
+ - 模式转换测试:48 个用例(含 `buildModeState`、error 状态、用户旅程)
32
+ - Session 测试:28 个用例(含静音模型跳过、全静音边界)
33
+ - 新增 CLI 参数解析测试:11 个用例
34
+ - 新增 Provider 工厂测试:12 个用例
35
+ - 新增 DeliberationView 组件测试:13 个用例
36
+ - 新增 Think-tag 过滤器测试:17 个用例
37
+ - 新增跨模块集成测试:19 个用例
38
+ - Ollama adapter 测试从 4 个扩展到 15 个
39
+ - Permission 测试新增 `deny_always`、`.git-credentials`、`grep .env` 覆盖
40
+ - Registry 测试新增 `execute()` 成功路径和异常处理
41
+ - 全部 30 个测试文件、311 个测试用例通过
42
+
43
+ ---
44
+
45
+ ## v0.1.2
46
+
47
+ ### 关键升级
48
+
49
+ - **团队模式** — 多模型接力协作(起草 → 修订 → 润色 → 审查),产出一份经过多重打磨的文档
50
+ - **/merge 合并命令** — 将各模型已有的回答合并为一份综合文档,标注共识和分歧
51
+ - **Shift+Tab 模式切换** — 广播模式与团队模式一键切换,输入 `/team` 也可切换
52
+ - **项目重新定位** — 从 AI 编程助手转向通用内容生成平台
53
+ - **CHANGELOG.md** — 按版本记录关键升级、Bug 修复和测试覆盖
54
+
55
+ ### 关键 Bug 修复
56
+
57
+ - Shift+Tab 在部分 Windows 终端上不生效,增加原始序列监听作为兜底
58
+ - 删除死代码 `StatusBar.tsx`,提取 `formatTokens` 到独立文件
59
+ - 移除 `cycleTargetReverse` 无用方法
60
+ - 移除 `Ctrl+O` / `Ctrl+S` 多余快捷键,保持交互简洁
61
+
62
+ ### 测试
63
+
64
+ - 新增 11 个 deliberation 测试用例(`autoAssignRounds`、`runDeliberation`、事件序列、约束注入、错误处理)
65
+ - 全部 24 个测试文件、164 个测试用例通过
66
+
67
+ ---
68
+
69
+ ## v0.1.1
70
+
71
+ ### 关键升级
72
+
73
+ - **MiniMax、DeepSeek Provider** — 6 家厂商全部接入
74
+ - **Token 用量实时显示** — 广播和定向视图均显示每模型的 token 消耗
75
+ - **上下文水位可配置** — `context_limit` 支持自定义,超出阈值有颜色预警
76
+ - **Provider 超时与重试** — 请求超时自动中断,支持重试策略
77
+ - **输入历史导航** — `↑↓` 键浏览历史输入
78
+ - **滚动偏移** — 定向模式下 `↑↓` 可滚动查看长输出
79
+ - **快捷键提示** — InputBar 底部常驻快捷键说明
80
+ - **会话持久化** — `q` 退出自动保存,`--resume` 恢复历史会话
81
+ - **孤儿 worktree 清理** — 启动时自动扫除上次崩溃遗留的 worktree
82
+ - **CLI 参数** — `--help` / `--version` 支持
83
+ - **配置校验** — 启动时警告缺失的模型或 API key
84
+
85
+ ### 关键 Bug 修复
86
+
87
+ - bash 工具空命令导致 hang,改为优雅拒绝
88
+ - 推理模型(DeepSeek-R1 等)的 `think` 标签被误当作正文输出,增加过滤
89
+ - 工具调用参数为空时导致异常,增加兜底处理
90
+ - MiniMax 中国区端点错误,改为 `minimax.chat`
91
+ - Google Gemini 工具结果角色应为 `function` 而非 `tool`
92
+ - Google Gemini 缺少工具调用循环处理
93
+ - npm bin 路径多余 `./` 前缀导致安装警告
94
+
95
+ ### 测试
96
+
97
+ - 新增 session 持久化测试(保存/加载/列表,5 个用例)
98
+ - 新增 `runTurn` 工具调用循环测试(6 个用例)
99
+ - 新增 UI 组件测试(`InputBar`、`formatTokens`)
100
+ - 新增工具测试(`readFile`、`grep`、`bash`、`writeFile`、`editFile`)
101
+ - 新增 `worktree` 隔离测试(5 个用例)
102
+ - 新增 `permission` 权限测试(5 个用例)
103
+ - 全部 24 个测试文件、164 个测试用例通过
104
+
105
+ ---
106
+
107
+ ## v0.1.0
108
+
109
+ ### 关键升级
110
+
111
+ - **首个可用版本** — 终端原生多模型 AI 助手正式发布
112
+ - **广播模式** — 消息同时发给所有模型,分栏并排查看回答
113
+ - **定向模式** — Tab 切换选择单一模型对话,全宽详情视图
114
+ - **6 家 Provider** — Anthropic、OpenAI、Google、DeepSeek、MiniMax、Ollama
115
+ - **Provider 统一接口** — AsyncGenerator + Adapter 模式,新增厂商只需实现 `chat()` 方法
116
+ - **git worktree 隔离** — 每个模型运行在独立的 worktree 中,文件系统无冲突
117
+ - **6 个内置工具** — bash、read、write、edit、glob、grep
118
+ - **权限管理** — 会话内记忆已授权操作,跨模型共享
119
+ - **TOML 配置** — `.multiarenarc` 文件,支持 `${ENV}` 环境变量和全局/项目级覆盖
120
+ - **对话历史** — 每个模型独立维护完整上下文
121
+
122
+ ### 关键 Bug 修复
123
+
124
+ - Anthropic adapter 的 `AbortController` 在连续调用时会复用已中止的实例,改为每次调用新建
125
+ - TOML 配置合并逻辑不完整,改用 `DEFAULT_CONFIG` 兜底
126
+ - 修复 ESM 模块格式和 TypeScript 类型导入
127
+
128
+ ### 测试
129
+
130
+ - Anthropic Provider 适配器测试(含 abort 场景)
131
+ - TOML 配置加载与默认值合并测试
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tim Gunnar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # multiarena
2
+
3
+ 终端原生多模型 AI 协作工具。一键提问,多个大模型同时回答;多模型接力审议,产出经过多重打磨的文档。
4
+
5
+ ## 模式系统
6
+
7
+ multiarena 有两个顶层模式,各自包含总览和模型私聊两种视图:
8
+
9
+ ```
10
+ 广播模式 (Broadcast) 团队模式 (Team)
11
+ ├── 总览:多模型并排面板 ├── 总览:审议入口 / 审议结果
12
+ └── 定向:单个模型全宽详情 └── 定向:与单个模型私聊
13
+ ```
14
+
15
+ ### 输入栏前缀
16
+
17
+ 前缀告诉你当前处于哪个模式和视图:
18
+
19
+ | 前缀 | 所处位置 |
20
+ |------|---------|
21
+ | `[all]` | 广播总览 — 消息发给所有模型 |
22
+ | `[模型名]` | 广播定向 — 只跟该模型对话 |
23
+ | `[team]` | 团队总览 — 输入任务启动审议 |
24
+ | `[team:模型名]` | 团队定向 — 审议后与该模型私聊 |
25
+
26
+ ### 模式切换规则
27
+
28
+ ```
29
+ Shift+Tab (仅在总览时) Shift+Tab (仅在总览时)
30
+ 广播总览 ←────────────────────→ 团队总览
31
+ ↑ ↓ Tab ↑ ↓ Tab
32
+ 广播定向 团队定向
33
+ ↑ ↑
34
+ └── Esc ───────────────────────┘── Esc
35
+ ```
36
+
37
+ **Tab** — 在当前模式内循环切换目标。从总览进入第一个模型,再按切到下一个,循环回总览。Tab **永远不会**切换模式(不会在广播 ↔ 团队之间跳)。
38
+
39
+ **Shift+Tab** — 在广播和团队之间切换。**只在总览时生效**,且切换后总是落在目标模式的总览。如果你在私聊中,需要先按 Esc 回总览,再按 Shift+Tab。
40
+
41
+ **Esc** — 返回当前模式的总览。无论你在私聊、对比视图、还是审议进行中,Esc 都会带你回到当前模式的总览。Esc **永远不会**切换模式。
42
+
43
+ **d** — 对比模式。输入栏为空时,按 `d` 将当前模型与另一个模型的回答并排展示,再按 `d` 退出。在总览中按 `d` 会将第一个模型作为主视图、第二个作为对比。
44
+
45
+ ---
46
+
47
+ ## 广播模式(启动默认)
48
+
49
+ 消息同时发给所有非静音模型,分栏并排查看回答。
50
+
51
+ ### 广播总览
52
+
53
+ ```
54
+ ┌─ claude ──────────────────────┐ ┌─ gpt ─────────────────────────┐
55
+ │ │ │ │
56
+ │ 可以使用 ls 列出当前目录文件: │ │ 你可以用 ls 或 dir 命令查看目 │
57
+ │ $ ls -la │ │ 录内容: │
58
+ │ total 48 │ │ $ ls │
59
+ │ drwxr-xr-x 12 user staff │ │ src/ test/ package.json │
60
+ │ │ │ │
61
+ │ 3 lines · 1K/200K · done │ │ 3 lines · 1K/128K · done │
62
+ └───────────────────────────────┘ └───────────────────────────────┘
63
+ ────────────────────────────────────────────────────────────────────
64
+ claude ● gpt ● — Tab:model d:compare m:mute r:reset q:quit ↑↓:scroll
65
+ [all] > ▊
66
+ ```
67
+
68
+ 提交消息后,所有非静音模型同时接收并回答。每个面板底部显示行数、token 用量和状态。
69
+
70
+ ### 广播定向(Tab 进入)
71
+
72
+ 按 `Tab` 进入某个模型的私聊视图。消息只发给该模型,输出区变为全宽详情。
73
+
74
+ ```
75
+ ┌─ claude ──────────────────────────────────────────────────────────┐
76
+ │ │
77
+ │ 我来详细分析这个方案的优缺点: │
78
+ │ │
79
+ │ **优点:** │
80
+ │ 1. 四层架构清晰,每层职责单一,易于测试和维护 │
81
+ │ 2. Provider 接口设计简洁,新增模型只需实现 chat() 方法 │
82
+ │ │
83
+ │ 2K/200K · 15 lines · done │
84
+ └────────────────────────────────────────────────────────────────────┘
85
+ ────────────────────────────────────────────────────────────────────
86
+ claude ● gpt — Tab:model d:compare m:mute r:reset q:quit ↑↓:scroll
87
+ [claude] > ▊
88
+ ```
89
+
90
+ 再按 `Tab` 切换到下一个模型(跳过静音模型),循环回到总览。按 `Esc` 返回总览。
91
+
92
+ ### 对比模式(按 `d`)
93
+
94
+ 输入栏为空时按 `d`,将当前模型与下一个模型的回答并排对比。
95
+
96
+ ```
97
+ ┌─ claude ──────────────────────┐ ┌─ gpt ─────────────────────────┐
98
+ │ │ │ │
99
+ │ 推荐使用策略模式,将每种算法封 │ │ 建议用工厂模式配合依赖注入来解 │
100
+ │ 装为独立的策略类。 │ │ 耦。 │
101
+ │ │ │ │
102
+ │ 2K/200K · 12 lines · done │ │ 1K/128K · 14 lines · done │
103
+ └───────────────────────────────┘ └───────────────────────────────┘
104
+ ────────────────────────────────────────────────────────────────────
105
+ claude ● gpt ● — Tab:model d:exit-compare m:mute r:reset q:quit
106
+ [claude] > ▊
107
+ ```
108
+
109
+ 再按 `d` 退出对比,按 `Esc` 也能退出并回到总览。
110
+
111
+ ---
112
+
113
+ ## 团队模式(Shift+Tab 进入)
114
+
115
+ 多个模型接力协作,起草 → 修订 → 终审,产出一份经过多视角打磨的文档。
116
+
117
+ ### 启动审议
118
+
119
+ 在广播总览按 `Shift+Tab` 进入团队总览,输入任务描述后回车:
120
+
121
+ ```
122
+ ────────────────────────────────────────────────────────────────────
123
+ 团队模式
124
+
125
+ 输入任务描述即可启动多模型接力审议。
126
+ Tab 可切换到特定模型私聊。Shift+Tab 返回广播模式。
127
+ ────────────────────────────────────────────────────────────────────
128
+ minimax deepseek ● — Tab:model d:compare m:mute r:reset q:quit
129
+ [team] > 写一篇关于兰州拉面的美食日记▊
130
+ ```
131
+
132
+ ### 审议进行中
133
+
134
+ 模型按顺序接力:起草(模型 A)→ 修订(模型 B)→ 终审(模型 C)。每轮在上一轮的基础上改进。
135
+
136
+ ```
137
+ ┌─ 团队审议 ─────────────────────────────────────────────────────────┐
138
+ │ │
139
+ │ 第 2/3 轮:deepseek (修订) │
140
+ │ │
141
+ │ ✓ 起草 ● 修订 ○ 终审 │
142
+ │ minimax deepseek minimax │
143
+ │ │
144
+ │ 修订中 — deepseek — 正在生成… │
145
+ │ │
146
+ │ ## 今天吃了兰州拉面,我很开心 │
147
+ │ │
148
+ │ 今天中午去楼下那家兰州拉面馆… │
149
+ │ │
150
+ └────────────────────────────────────────────────────────────────────┘
151
+ ────────────────────────────────────────────────────────────────────
152
+ minimax deepseek ● — Tab:model d:compare m:mute r:reset q:quit Esc:cancel
153
+ [team] > ▊
154
+ ```
155
+
156
+ 审议进行中按 `Esc` 中止。
157
+
158
+ ### 审议完成
159
+
160
+ 审议结束后,显示审议过程和最终文档。每轮显示模型名称、角色、修改处数,以及代表性修改内容:
161
+
162
+ ```
163
+ ┌─ 团队审议 ─────────────────────────────────────────────────────────┐
164
+ │ │
165
+ │ 审议完成 · 3 轮 · 4 处修改 │
166
+ │ │
167
+ │ ✓ 起草 ✓ 修订 ✓ 终审 │
168
+ │ minimax deepseek minimax │
169
+ │ │
170
+ │ ── 审议过程 ── │
171
+ │ │
172
+ │ 1. minimax(起草) │
173
+ │ 2. deepseek(修订) — 4 处修改 │
174
+ │ 明天还想去! → 我决定下次再来。 │
175
+ │ 這個味道吧。 → 这个味道吧。 │
176
+ │ 明天还想再来一碗。 → 计划下次再品尝一次。 │
177
+ │ 3. minimax(终审) — 无修改 │
178
+ │ │
179
+ │ ── 最终文档 ── │
180
+ │ │
181
+ │ # 今天吃了兰州拉面,我很开心 │
182
+ │ 今天中午去楼下那家兰州拉面馆… │
183
+ │ │
184
+ └────────────────────────────────────────────────────────────────────┘
185
+ ────────────────────────────────────────────────────────────────────
186
+ minimax ● deepseek ● — Tab:model d:compare m:mute r:reset q:quit
187
+ [team] > ▊
188
+ ```
189
+
190
+ ### 审议后与模型私聊
191
+
192
+ 审议完成后,按 `Tab` 切换到任一模型,可以就该模型在审议中的表现、最终文档的内容进行讨论。每个模型的对话历史中已自动注入审议结果上下文,模型知道自己参与了审议并了解最终成果。
193
+
194
+ ```
195
+ 按 Tab → [team:minimax] > 你觉得终审时deepseek的修改是否合理?
196
+ ```
197
+
198
+ ### 合并已有回答(`/merge`)
199
+
200
+ 先在广播模式下让各模型各自回答一个问题,然后在团队模式下输入 `/merge`,选一个模型把所有回答合并为综合文档,标注共识和分歧。
201
+
202
+ ---
203
+
204
+ ## 安装
205
+
206
+ ```bash
207
+ npm install -g multiarena
208
+ ```
209
+
210
+ ## 快速开始
211
+
212
+ ```bash
213
+ # 创建配置文件
214
+ cat > .multiarenarc << 'EOF'
215
+ [models.claude]
216
+ provider = "anthropic"
217
+ model = "claude-sonnet-4-6"
218
+ api_key = "${ANTHROPIC_API_KEY}"
219
+
220
+ [models.gpt]
221
+ provider = "openai"
222
+ model = "gpt-4o"
223
+ api_key = "${OPENAI_API_KEY}"
224
+
225
+ [defaults]
226
+ active = ["claude", "gpt"]
227
+ EOF
228
+
229
+ # 启动
230
+ multiarena
231
+ ```
232
+
233
+ ## 配置
234
+
235
+ 配置文件 `.multiarenarc`(TOML 格式),放在当前目录或 `~/.multiarenarc`。
236
+
237
+ ```toml
238
+ [models.<名称>]
239
+ provider = "anthropic" # anthropic | openai | google | deepseek | minimax | ollama
240
+ model = "claude-sonnet-4-6" # API 模型名
241
+ api_key = "${ENV_VAR}" # API key,支持环境变量
242
+ endpoint = "..." # 自定义端点(可选)
243
+ context_limit = 200000 # 上下文窗口大小(可选)
244
+
245
+ [defaults]
246
+ active = ["claude", "gpt"] # 启动时加载的模型
247
+
248
+ [deliberation]
249
+ constraint_file = "rules.md" # 审议约束文件(可选)
250
+ ```
251
+
252
+ 会话保存至 `~/.multiarena/sessions/`。使用 `multiarena --resume <id>` 恢复历史会话。
253
+
254
+ ## 支持的模型
255
+
256
+ | 厂商 | 模型 |
257
+ |------|------|
258
+ | Anthropic | Claude Sonnet 4.6、Opus 4.7、Haiku 4.5 |
259
+ | OpenAI | GPT-4o、GPT-4.1 |
260
+ | Google | Gemini 2.5 Flash、Gemini 2.5 Pro |
261
+ | DeepSeek | DeepSeek-V4、DeepSeek-R1 |
262
+ | MiniMax | MiniMax-M2.1 |
263
+ | Ollama | 任意本地模型 |
264
+
265
+ ## 快捷键速查
266
+
267
+ | 按键 | 功能 | 约束 |
268
+ |------|------|------|
269
+ | `Tab` | 当前模式内循环切换目标(总览 → 模型1 → 模型2 → 总览) | 不切换模式,跳过静音模型 |
270
+ | `Shift+Tab` | 广播 ↔ 团队模式切换 | **仅在总览时生效**,切换后落在目标模式总览 |
271
+ | `Esc` | 返回当前模式总览 / 退出对比 / 中止审议 | 不切换模式 |
272
+ | `d` | 对比两个模型的回答(输入栏为空时) | 需要至少 2 个非静音模型 |
273
+ | `m` | 静音/取消静音当前模型 | 定向模式下生效 |
274
+ | `r` | 重置当前模型对话历史 | 定向模式下生效 |
275
+ | `↑ ↓` | 输入为空:浏览历史输入;输入非空:滚动输出 | 团队总览下审议内容也可滚动 |
276
+ | `/team` | 等同于 Shift+Tab | |
277
+ | `/merge` | 合并各模型最近的回答 | 需要至少 2 个模型有回复 |
278
+ | `q` | 退出(自动保存会话) | |
279
+
280
+ ## License
281
+
282
+ MIT
@@ -0,0 +1,11 @@
1
+ export declare function getPkgVersion(): string;
2
+ /** Override for tests. */
3
+ export declare function setPkgVersion(v: string): void;
4
+ export declare const HELP = "multiarena \u2014 Multi-Model AI Coding Assistant\n\nUsage:\n multiarena [options]\n\nOptions:\n --new Start a new session (default)\n --resume <id> Resume a saved session\n --list List saved sessions\n --help Show this help\n --version Show version";
5
+ export interface ParsedArgs {
6
+ sessionId?: string;
7
+ listOnly: boolean;
8
+ showHelp: boolean;
9
+ showVersion: boolean;
10
+ }
11
+ export declare function parseArgs(argv: string[]): ParsedArgs;
@@ -0,0 +1,56 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ // __dirname won't be available in this module; use a static path for PKG_VERSION.
5
+ // We store the pkg version lazy-loaded so tests don't need a real package.json.
6
+ let _pkgVersion = null;
7
+ export function getPkgVersion() {
8
+ if (_pkgVersion !== null)
9
+ return _pkgVersion;
10
+ try {
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const pkgPath = path.join(__dirname, "..", "..", "package.json");
13
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
14
+ _pkgVersion = pkg.version ?? "0.1.0";
15
+ }
16
+ catch {
17
+ _pkgVersion = "0.1.0";
18
+ }
19
+ return _pkgVersion;
20
+ }
21
+ /** Override for tests. */
22
+ export function setPkgVersion(v) {
23
+ _pkgVersion = v;
24
+ }
25
+ export const HELP = `multiarena — Multi-Model AI Coding Assistant
26
+
27
+ Usage:
28
+ multiarena [options]
29
+
30
+ Options:
31
+ --new Start a new session (default)
32
+ --resume <id> Resume a saved session
33
+ --list List saved sessions
34
+ --help Show this help
35
+ --version Show version`;
36
+ export function parseArgs(argv) {
37
+ if (argv.includes("--help") || argv.includes("-h")) {
38
+ return { listOnly: false, showHelp: true, showVersion: false };
39
+ }
40
+ if (argv.includes("--version") || argv.includes("-v")) {
41
+ return { listOnly: false, showHelp: false, showVersion: true };
42
+ }
43
+ const resumeIdx = argv.indexOf("--resume");
44
+ if (resumeIdx >= 0 && argv[resumeIdx + 1]) {
45
+ return {
46
+ sessionId: argv[resumeIdx + 1],
47
+ listOnly: false,
48
+ showHelp: false,
49
+ showVersion: false,
50
+ };
51
+ }
52
+ if (argv.includes("--list") || argv.includes("--list-sessions")) {
53
+ return { listOnly: true, showHelp: false, showVersion: false };
54
+ }
55
+ return { listOnly: false, showHelp: false, showVersion: false };
56
+ }
@@ -48,8 +48,8 @@ export function validateConfig(config) {
48
48
  }
49
49
  export function loadConfig() {
50
50
  const candidates = [
51
- path.join(process.cwd(), ".arenarc"),
52
- path.join(os.homedir(), ".arenarc"),
51
+ path.join(process.cwd(), ".multiarenarc"),
52
+ path.join(os.homedir(), ".multiarenarc"),
53
53
  ];
54
54
  let resolved = {};
55
55
  for (const p of candidates) {
@@ -1,15 +1,25 @@
1
1
  export interface ModelConfig {
2
- provider: "anthropic" | "openai" | "google" | "ollama";
2
+ provider: "anthropic" | "openai" | "google" | "ollama" | "deepseek" | "minimax";
3
3
  model: string;
4
4
  api_key?: string;
5
5
  endpoint?: string;
6
6
  context_limit?: number;
7
7
  }
8
+ export interface DeliberationConfig {
9
+ /** Models and their round roles in order. E.g. [{ model: "claude", role: "draft" }, ...] */
10
+ rounds: Array<{
11
+ model: string;
12
+ role: "draft" | "revise" | "polish" | "review";
13
+ }>;
14
+ /** Path to a constraint document (markdown), relative to project root. */
15
+ constraint_file?: string;
16
+ }
8
17
  export interface ArenaConfig {
9
18
  models: Record<string, ModelConfig>;
10
19
  defaults: {
11
20
  active: string[];
12
21
  broadcast: boolean;
13
22
  };
23
+ deliberation?: DeliberationConfig;
14
24
  }
15
25
  export declare const DEFAULT_CONFIG: Partial<ArenaConfig>;
@@ -0,0 +1,53 @@
1
+ import type { ModelConfig } from "../config/types.js";
2
+ export type RoundRole = "draft" | "revise" | "polish" | "review";
3
+ export interface DeliberationRoundConfig {
4
+ modelName: string;
5
+ role: RoundRole;
6
+ config: ModelConfig;
7
+ }
8
+ export interface DeliberationProgress {
9
+ type: "round_start" | "text" | "round_end" | "done" | "error";
10
+ round: number;
11
+ totalRounds: number;
12
+ modelName?: string;
13
+ role?: RoundRole;
14
+ content?: string;
15
+ document?: string;
16
+ error?: string;
17
+ /** Number of revision annotations found in this round's output. */
18
+ changeCount?: number;
19
+ /** A few representative revision snippets from this round. */
20
+ changeSamples?: string[];
21
+ }
22
+ export interface DeliberationResult {
23
+ finalDocument: string;
24
+ rounds: Array<{
25
+ modelName: string;
26
+ role: RoundRole;
27
+ document: string;
28
+ }>;
29
+ }
30
+ /**
31
+ * Run the R2D2 (Round-Robin Deliberative Drafting) pipeline.
32
+ *
33
+ * Models take turns in sequence: the first drafts, the second revises,
34
+ * the third polishes, and optionally a fourth reviews. Each round's
35
+ * system prompt injects the task, constraint document, and previous
36
+ * round output for context. No central synthesizer — the document
37
+ * emerges through sequential refinement.
38
+ */
39
+ export declare function runDeliberation(task: string, roundConfigs: DeliberationRoundConfig[], constraint?: string, worktreePath?: string): AsyncGenerator<DeliberationProgress>;
40
+ /** Build round configs with mirror pattern: A→B→C→B→A. */
41
+ export declare function autoAssignRounds(modelNames: string[], models: Record<string, ModelConfig>): DeliberationRoundConfig[];
42
+ /** Human-readable label for a round role. */
43
+ export declare function roundLabel(role: RoundRole): string;
44
+ export interface MergeInput {
45
+ modelName: string;
46
+ content: string;
47
+ }
48
+ /**
49
+ * Synthesize existing model outputs into one final document.
50
+ * Single-turn: one model acts as the merger, combining all outputs
51
+ * into a coherent document with source annotations and conflict notes.
52
+ */
53
+ export declare function runMerge(task: string, outputs: MergeInput[], mergerConfig: ModelConfig, mergerName: string): AsyncGenerator<DeliberationProgress>;