讨论模式:把当前单人对话变成 Claude + codex + gemini 等 CLI agent 的回合制群聊,用户是讨论的参与方而不是审批人。 当用户在讨论一个需求/方案该怎么做、想听听别的模型怎么看、要在两个以上方案之间权衡,或点名某个 agent 时使用。 触发词——拟人说法:「叫上 codex」「问问 gemini 怎么看」「@codex」「@gemini」「大家怎么看」「让他们吵一架」 「听听别的模型的意见」「拉个人进来聊聊」; 技术说法:「讨论模式」「/trundle:discuss」「/discuss」「多模型讨论」「交叉验证这个设计」「让 codex 和 gemini 分头论证 A 和 B」 「这个方案有没有别的做法」。 即使用户只是说「这块我不太确定」「我们是不是想复杂了」而当前话题是设计取舍,也应触发——但只用来**提议**进入,不直接进。 不触发:事实查询、读代码就能确认的问题、已进入写代码/调试的执行阶段、 用户只想要一份规格文档或评审报告(本 skill 产出的是讨论过程,不是文档)。
Scanned 9/5/2026
Install to Claude Code
npx -y skills add PouLJenny/trundle --skill discuss --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Discuss?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/pouljenny-discuss)More formats (shields.io, HTML) on the badges page.
---
name: discuss
description: >-
讨论模式:把当前单人对话变成 Claude + codex + gemini 等 CLI agent 的回合制群聊,用户是讨论的参与方而不是审批人。
当用户在讨论一个需求/方案该怎么做、想听听别的模型怎么看、要在两个以上方案之间权衡,或点名某个 agent 时使用。
触发词——拟人说法:「叫上 codex」「问问 gemini 怎么看」「@codex」「@gemini」「大家怎么看」「让他们吵一架」
「听听别的模型的意见」「拉个人进来聊聊」;
技术说法:「讨论模式」「/trundle:discuss」「/discuss」「多模型讨论」「交叉验证这个设计」「让 codex 和 gemini 分头论证 A 和 B」
「这个方案有没有别的做法」。
即使用户只是说「这块我不太确定」「我们是不是想复杂了」而当前话题是设计取舍,也应触发——但只用来**提议**进入,不直接进。
不触发:事实查询、读代码就能确认的问题、已进入写代码/调试的执行阶段、
用户只想要一份规格文档或评审报告(本 skill 产出的是讨论过程,不是文档)。
version: 1.1.0
license: Apache-2.0
---
# trundle discuss
把当前对话变成 host(正在读本文件的你)+ 其他 CLI agent 的回合制群聊。**用户是参与方,不是审批人。产出物是讨论过程本身,不是最后那份文档。**
本文件是**壳**:只管进入/退出、机械执行和渲染。每轮「谁发言、喂什么、怎么收尾」的裁量由 **moderator**(一个独立的 CLI agent,见 `protocol/moderator.md`)产出计划,我照计划执行。我自己的发言在计划的 `host_say` 划定的边界内展开——那是指引不是讲稿,话由我自己说:我是讨论的当事方,不是传声筒。
**路径约定**:下文 `<SKILL>` 指本文件所在目录(Claude Code 软链安装下是 `${CLAUDE_SKILL_DIR}`,plugin 安装下是 `${CLAUDE_PLUGIN_ROOT}/skills/discuss`;其他 host 没有这些变量,就是你读到本文件的实际目录),`<SCRIPTS>` 指 `<SKILL>/scripts`。**主 host 是 Claude Code(行为已验证);其他 host 先读 `references/hosts.md` 的机制映射与支持状态。**
## 铁律
1. **用户是参与方,不是审批人。** 不向用户「提交」结论等批准,而是向用户**提问**。
2. **默认一轮只有我说话。** 拉人是例外——这条裁量由 moderator 执行,但它同时是我在降级模式下的行为底线。
3. **辅助 agent 全程只读。** API 参与者天然满足(它够不到文件系统),其余四个 CLI: codex 走 `--sandbox read-only`,gemini 走 `--approval-mode plan`,claude 走工具白名单 `--allowedTools Read,Glob,Grep`,dsh 走 `DSH_PERMISSION_MODE=read-only`(它没有只读 flag,只读靠环境变量,而且它的默认值是**可写的**——脚本强制覆盖用户环境,不随环境变量放宽)。永不给写权限。moderator 自己也是子进程,同样只读。
4. **产出物是讨论过程。** 除非用户明确要,结尾不生成规格文档、不写"最终方案"。
5. **子进程无状态。** 每次调用把需要的上下文完整塞进 prompt——它不记得上一轮。moderator 也一样:它每轮从 transcript 重建判断。
## 一个回合(壳的机械循环)
用户发言后:
1. **组装本轮输入**,写进临时文件,五段(格式见 `protocol/moderator.md` 的「输入」):
【名册】本轮实际阵容(「这轮别带 X」在这里生效)/【共识状态头】/【讨论记录】/【主持人立场】我对议题当前的立场或内心状态,如实写,包括没把握的部分/【用户本轮消息】原话
2. **跑 moderator**:`python3 <SCRIPTS>/moderate.py --input <文件>`。**必须允许它跑满 600s**——它的总预算是 590s(含一次重试),要让它先于 host 的命令超时开枪(Claude Code:Bash 工具 `timeout` 显式设 `600000`,默认 120s 会误杀;其他 host 见 `references/hosts.md`)。
3. **照计划执行**:输出里 `===EXEC===` 给出的 invoke.sh 命令**原样执行**(同样 timeout 600000);「本轮无外部调用」则跳过。`===WARN===` 里的 soft 违规如实转告用户,不隐瞒。
4. **渲染**所有发言(见「渲染」),然后我在 `host_say` 的边界内发言。
5. **收尾**按 `closing`:`user_question` → 以那个问题结束回合;`fact_verdict` → **我当轮就去查/去跑**,结果以 `[事实裁定] <结论>(host 实测,第 N 轮)` 追加进 transcript 并告知用户——把事实问题推给用户是失职;`none` → 不追问。
6. **记账**:按 `transcript_delta.append` 追加 transcript(`direction_change` 非空则先插 `── 方向变更:X → Y(用户,第 N 轮)──`),打状态行。
回合内不中途问"要不要继续"。moderate.py 退出码非 0 → 走「降级模式」。
## 降级模式
moderator 缺席时(moderate.py 退出码非 0:没装任何可用 CLI、超预算、两次都产不出合法计划),**明说一行**,例如:`moderator 缺席(codex 未安装)· 本轮由我自行主持`,然后**我自己读 `<SKILL>/protocol/moderator.md`,按同一套裁量规则在脑内产出本轮计划,再照常走机械循环的 3-6 步**。
单一事实源:裁量规则只有协议那一份,壳里不复刻——否则协议改一处、壳漂一处。降级不静默、不中止讨论,只是裁量从子进程挪回我脑内,质量约束(不综合、伪共识警惕、@ 原话原样)一条不减。
## 进入与退出(两端都显式)
这个模式会改变我的行为(调外部 agent、拒绝直接下结论、把分歧端回去),所以不能隐式进出。
**进入**——三条路,都以用户点头收尾:`/trundle:discuss [话题]`(软链安装是 `/discuss`);口头声明(「叫上 codex」);或我识别到设计取舍时**提议**一句「要不要拉 codex 和 gemini 进来讨论?」等用户点头。**第三条是提议,不是自动挡。**
首次进入时若名册不存在(路径见「名册」的解析链),先组建阵容。进入后展示一次控制语法,之后每回合只有状态行:
```
─── 讨论模式 · codex + gemini · 第 4 轮 · 未决 2 ───
```
**退出**——用户明说(「退出讨论」「开始写吧」),或我在这三种信号下主动问一次(不反复骚扰):同一未决问题连续两轮无新论据 / 用户开始说执行性的话 / 未决问题清空。退出给两三行收尾:定了什么、还剩什么。**绝不因为退出就把 transcript 整理成规格文档。**
**未进入本模式时,以下规则全部不生效**——正常回答、正常写代码。
## 控制语法
进入时展示一次:
```
@codex <你的话> 只让 codex 回应,你的原话原样转达
@codex @gemini <你的话> 同时点名多个
@all <你的话> 全员回应
让 codex 和 gemini 分头论证 A 和 B 对赌,各给不同任务
把 cline 加进来 / 让 gemini 退出 中途加人 / 踢人
这轮别带 gemini 仅本轮跳过
/trundle:discuss agents 重新挑参与者
退出讨论 / 开始写吧 结束
```
这些控制语从我口中到 moderator 的输入原样传递:@ 的原话、点名、对赌任务分配都是【用户本轮消息】的一部分,moderator 按协议处理(@ 原话必须一字不差进被点名者的 prompt)。用户的话里若有事实错误,我在**自己那段**指出,转达的那份不动。
## 裁量规则在哪
「谁在什么时候说话」「不综合,把分歧端出来」「伪共识警告」——这三块是这个 skill 的价值核心,**正文在 `protocol/moderator.md`**,由 moderator 每轮执行、由我在降级模式下执行。要点仅提醒(执行以协议为准):
- 默认没人说话;拉人只有三个口子(用户点名 / 主动拉且每轮至多一次 / 显式对赌必须不同任务)。**每轮全员发言 = 这个 skill 已经坏了。**
- 分歧未解决时我不下「综合考虑建议 A」式结论——点破分歧、还原前提差异、把只有用户能答的问题交回去。
- 所有人一致时先怀疑答案是被喂进去的;向用户报告一致时必须带边界(「他们一致,但都没碰过 X」)。
## 名册与阵容
三层:**适配库**(`agents.yaml`,某个 CLI 怎么调)→ **名册**(用户选了谁 + 站位)→ **本轮阵容**(运行时,写进本轮输入的【名册】块)。
**名册路径是 host 无关的**,解析链(moderate.py 的 `resolve_roster()` 同一套,壳读写名册也照它走):`TRUNDLE_ROSTER` 环境变量 → `~/.config/trundle/roster.yaml`(XDG,新名册写这里)→ 旧路径 `~/.claude/trundle-discuss/roster.yaml`(存在则回退,老用户零迁移)。名册是用户的偏好,不是某个 host 的私产——换 host 接着用必须是同一份。
**站位只在名册里,适配库不预设任何站位。** 站位是「讨论里的位置」,不是 CLI 的属性——库作者既不知道用户装了哪些 agent,也不该替所有场次预定立场;何况适配库随 skill 更新被覆盖,而站位是用户偏好,写在一起必然漂。
- **首次**:跑 `<SCRIPTS>/discover.sh` 扫描 → 让用户勾选 → 问要不要分站位 → 写名册。适配库只收实测跑通的 CLI;**未登记的只列出、绝不调用**(猜错只读约束会给它写权限;moderate.py 对未登记名字有同样的闸)。
- **moderator 可配置**:名册顶部可写一行 `moderator: <名>`(可选)。缺省是 `codex`——spike 实测它三次全稳定、13-47s/轮;显式指定的 CLI 没装或没通过前置检查时,moderate.py 会**响亮**降级到回退链,不静默换人。首次组阵容时顺带问一句,不强制。
- **站位是可选的。** 写了 stance 的参与者:整场不变,且两人不能同站位(重复视角既是伪共识燃料,又白付一份延迟)。没写的:不预设视角,判伪共识时把观点重合概率算进去。默认 2 人,同时发言上限 3 人,超过要提示。
- **API 参与者**:名册条目带 `api:` 块的,是直连 LLM API 的参与者。它**没有工具**——看不到文件系统,只知道我写进 prompt 的字。字段:`base_url`(必填)/`model`(必填)/`api_key_env`(可选,只写**变量名**不写值)/`headers`(可选)/`max_tokens`(可选)。
```yaml
participants:
- agent: codex
- agent: deepseek
api:
base_url: https://api.deepseek.com/v1
model: deepseek-chat
api_key_env: DEEPSEEK_API_KEY
```
**每轮我要把本轮阵容里的 `api:` 块转写成一个 sidecar JSON**(`{名字: {字段…}}`,只含变量名不含凭证),写进本轮 outdir,然后用 `--api-config <该文件>` 传给 `moderate.py`;它会原样传给 `invoke.sh`。不传就等于本轮没有 API 参与者——**全 API 阵容时忘了传,moderator 会报「没有任何已登记的参与者」并退出**。
写进 moderator 输入的【名册】块时,API 参与者要带标记:`- deepseek [api · 无工具:看不到文件系统,只能看到你写进 prompt 的字]`。
它们的发言追加进 transcript 时,**署名带 `·api` 后缀**。
它们**不能当 moderator**(v1 明确不做),`moderator:` 字段只认已适配的 CLI。
- **中途加人**:发**入场简报**(共识状态头 + 最近两轮,**不是**全量 transcript——中途加人的价值就是没被前面锚定),末尾必须带「如果你觉得已确立的前提有问题,直接说」。
- **中途踢人**:「这轮别带 X」不写盘;「让 X 退出」写盘。**被踢者说过的话留在 transcript 里**,他提出的前提继续有效。
- **对赌进行中禁止加/踢**——换人毁掉对照。
## 状态:transcript + 共识头
transcript **只增不改**,署名原话是指向性反驳的唯一来源。静默 append,不向用户宣传、不做总结。落盘路径:新讨论写 `<项目>/.trundle/<date>-<slug>.md`;若项目里已有 `.claude/trundle-discuss/`(老项目)则**继续写那里**——transcript 是这个项目的记忆,与哪个 host 记的无关,同一项目的记录绝不因升级或换 host 劈成两份。
共识状态头(已确立前提 / 已废弃方向 / 未决问题)由我维护,每轮写进 moderator 的输入;`fact_verdict` 的裁定结果在下一轮进「已确立的前提」。用户改主意时**不作废**:插一行 `── 方向变更:X → Y(用户,第 N 轮)──`,把废弃前提移进「已废弃方向(不要再论证)」。少了这个,agent 会继续攻击一个已作废的方案。
## 调用与失败
参与者调用用 `===EXEC===` 给出的 `<SCRIPTS>/invoke.sh` 命令,原样执行。
**moderate.py 与 invoke.sh 都必须被允许跑满 600s**——两者内置看门狗(590s/540s),会自己收尾并给出失败分类;host 若先开枪,那一轮**连失败分类都拿不到,整段输出直接丢**。Claude Code 下这意味着 Bash 工具的 `timeout` 显式设 `600000`(默认 120s);其他 host 的对应事项见 `references/hosts.md`。
脚本的 stderr 里以 `···` 开头的行是**给用户看的实时进度**(逐字回显、状态行)。**解析时全部忽略,只认 `===AGENT` / `===PLAN` 分段。** 那些字用户在终端里已经看过了,不要当成 agent 的发言再渲染一遍。
超时是**按吐字间隔**判定的,不是墙钟:只要 agent 还在出事件就一直等,静默超限才算卡死(`DISCUSSION_IDLE`,codex 300s / gemini 与 claude 90s——codex 生成回答期间完全不发事件,不能按同一把尺子量);另有绝对上限防工具循环(`DISCUSSION_MAX_WALL`,默认 540s)。
**dsh 是例外:它没有事件流**,整轮 stdout 零输出,跑完才一次性给出全文。空闲超时对它不生效(脚本整体跳过),只受墙钟约束(540s,与全局相同)——无流意味着卡死没有任何征兆,墙钟是唯一的护栏,而 540 已是上限:Bash 工具自己在 600s 开枪,必须留余量让脚本先收尾。
`timeout` 状态有三种含义,脚本会在正文里说明是哪一种,并附上开枪那一刻它在干什么。**如果那里写着「发言中」,那多半不是它卡死,是被砍在正要说话那一刻**——告诉用户可以调大 `DISCUSSION_IDLE` 重试,不要直接当成它没话说。**如果写着「运行中(无进度事件)」,那是 dsh 这类没有事件流的 agent——它的静默是正常的,能调的只有 `DISCUSSION_MAX_WALL`**,别建议用户去拧另外两个。
**API 参与者的失败与 CLI 不同**:没有 exit code、没有 stderr、没有可给的 probe 命令,失败文案用的是 HTTP 状态码 / `finish_reason` / 已收事件数与字数。三种要认得出来——**「疑似上下文被截断」**表示模型只看到了 prompt 的一部分(被丢的通常是开头的共识状态头),这一轮宁可缺席也不能采信;**「生成未完成」**多半是撞上了 `max_tokens`;**「不是流式」**表示该端点或网关忽略了流式参数。它们**都不重试**,一次网络抖动就等于本轮缺席。
**gemini 必须在已 trust 的目录**——未 trust 时脚本返回 `untrusted` 并给补救指引,**绝不用 `GEMINI_CLI_TRUST_WORKSPACE=true` 绕过**(会把模型路由降级,延迟涨约 10 倍)。
失败**明说一行,不中止整轮**:`**gemini** · 调用失败(未认证)· 本轮缺席`。群聊里少一个人只是少一个人发言。**唯一例外**:对赌时一方缺席 → 对赌无效,问用户重试还是改单人回答。moderator 自己失败 → 降级模式,见上。
## 渲染
```
**codex** · 8.2s
先别争全量还是窗口,这可能解错了问题。你们两个方案都假设……
立场:先确认写放大来自哪一层,再选方案
不同意:Claude 说「窗口足够」—— 前提是写入均匀,日志类负载不是
如果我错了:写入其实是均匀的,那窗口确实够
```
## 参考
| 文件 | 何时读 |
|---|---|
| `protocol/moderator.md` | 每轮裁量的完整规则与 round plan schema;降级模式下我照它主持 |
| `references/hosts.md` | host 不是 Claude Code 时**必读**:机制映射与各 host 支持状态 |
| `references/invocation.md` | 要改调用方式、排查 CLI 失败、看实测数据 |
| `references/prompt-kit.md` | prompt 组装的展开说明(机器执行版以协议为准) |
| `references/adapting-new-cli.md` | 接纳一个未登记的 CLI |
| `references/adapting-api-model.md` | 接纳一个新的 API 端点(验证清单与 CLI 完全不同) |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!