pi-brainstorm 0.2.0 → 0.3.1
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 +44 -11
- package/README.zh-CN.md +50 -11
- package/config/default.yaml +58 -0
- package/extensions/brainstorm.ts +688 -222
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -10,13 +10,14 @@ The plugin stores each participant's full contribution in a local meeting blackb
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
- Multi-model brainstorming with GPT, DeepSeek,
|
|
13
|
+
- Multi-model brainstorming with configurable participants (default: GPT, DeepSeek, MiniMax).
|
|
14
14
|
- Debate / battle mode where agents prosecute, stress-test, and challenge positions.
|
|
15
|
+
- Configuration-driven: add, remove, or customize participants via YAML.
|
|
15
16
|
- Round-by-round summaries focused on consensus, disagreement, and next questions.
|
|
16
17
|
- Full participant contributions stored as Markdown files under `.pi-meetings/`.
|
|
17
18
|
- Compact visible cards in the main conversation instead of long pasted transcripts.
|
|
18
19
|
- JSONL index for lightweight cross-round context.
|
|
19
|
-
-
|
|
20
|
+
- Managed agent files generated from config — update one YAML, all agents sync.
|
|
20
21
|
|
|
21
22
|
## Install
|
|
22
23
|
|
|
@@ -29,7 +30,7 @@ pi install npm:pi-brainstorm
|
|
|
29
30
|
From GitHub:
|
|
30
31
|
|
|
31
32
|
```bash
|
|
32
|
-
pi install git:github.com/Jarcis-cy/pi-brainstorm@v0.
|
|
33
|
+
pi install git:github.com/Jarcis-cy/pi-brainstorm@v0.3.1
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
For local development:
|
|
@@ -38,15 +39,47 @@ For local development:
|
|
|
38
39
|
pi install /Users/jarcis/Project/pi-brainstorm
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
##
|
|
42
|
+
## Configuration
|
|
43
|
+
|
|
44
|
+
Participants are defined in YAML. The extension loads config in this order (later overrides earlier):
|
|
45
|
+
|
|
46
|
+
1. Package default: `config/default.yaml` (shipped with the package)
|
|
47
|
+
2. User override: `~/.pi/agent/pi-brainstorm.yaml`
|
|
48
|
+
3. Project override: `.pi-brainstorm.yaml` or `.pi/pi-brainstorm.yaml`
|
|
49
|
+
|
|
50
|
+
Arrays (e.g. `participants`) replace entirely; objects deep-merge.
|
|
51
|
+
|
|
52
|
+
### Adding a new participant
|
|
53
|
+
|
|
54
|
+
Create `~/.pi/agent/pi-brainstorm.yaml` (user-level) or `.pi-brainstorm.yaml` (project-level):
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
participants:
|
|
58
|
+
- displayName: Claude
|
|
59
|
+
agentName: claude-brainstormer
|
|
60
|
+
description: Claude brainstorming consultant. Nuanced analyst for multi-model discussion sessions.
|
|
61
|
+
model: anthropic/claude-sonnet-4-20250514:xhigh
|
|
62
|
+
roleTitle: Nuanced Analyst
|
|
63
|
+
rolePrompt: |
|
|
64
|
+
你是多模型头脑风暴中的分析顾问。擅长细致入微的论证和长篇分析。用中文回答。
|
|
65
|
+
whatYouDo:
|
|
66
|
+
- 提供细致、深入的逐点分析
|
|
67
|
+
- 识别细微差别和边缘情况
|
|
68
|
+
- 撰写结构清晰的长篇论证
|
|
69
|
+
debatePersona:
|
|
70
|
+
label: THE ANALYST
|
|
71
|
+
prompt: |
|
|
72
|
+
DEBATE MODE. You are THE ANALYST. Dissect every argument with precision. Find the weakest link in every chain of reasoning. Use Chinese.
|
|
73
|
+
brainstormRole: Nuanced analyst
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To replace the default participants entirely, define a full `participants` list in your override file.
|
|
42
77
|
|
|
43
|
-
|
|
78
|
+
User-level config can create or update managed agent files in `~/.pi/agent/agents/` after confirmation. Project-level config is used for the current session, but it does not auto-write global agent files; create those agents manually or move the config to `~/.pi/agent/pi-brainstorm.yaml` when you want automatic sync.
|
|
44
79
|
|
|
45
|
-
|
|
46
|
-
- `deepseek-brainstormer`
|
|
47
|
-
- `minimax-brainstormer`
|
|
80
|
+
### Managed agent files
|
|
48
81
|
|
|
49
|
-
|
|
82
|
+
Agent files generated by pi-brainstorm contain `<!-- managed-by: pi-brainstorm -->`. These files are overwritten when the config changes. Existing agent files without this marker are never touched.
|
|
50
83
|
|
|
51
84
|
## Commands
|
|
52
85
|
|
|
@@ -55,7 +88,7 @@ On first use, if any of these user-level agents are missing, the extension asks
|
|
|
55
88
|
/debate <topic>
|
|
56
89
|
```
|
|
57
90
|
|
|
58
|
-
`/brainstorm` starts
|
|
91
|
+
`/brainstorm` starts an interactive three-round multi-model brainstorming session. After Round 1 and Round 2, the facilitator should stop, summarize the round, and wait for your feedback or permission before continuing.
|
|
59
92
|
|
|
60
93
|
`/debate` starts an open-ended multi-agent battle that should continue until convergence or user intervention.
|
|
61
94
|
|
|
@@ -65,7 +98,7 @@ On first use, if any of these user-level agents are missing, the extension asks
|
|
|
65
98
|
participant model -> meeting_append_entry -> .pi-meetings/... files
|
|
66
99
|
participant model -> short WROTE_ENTRY summary -> facilitator context
|
|
67
100
|
file watcher -> compact visible card -> main chat
|
|
68
|
-
facilitator -> consensus / disagreement / final synthesis
|
|
101
|
+
facilitator -> round checkpoint / consensus / disagreement / final synthesis
|
|
69
102
|
```
|
|
70
103
|
|
|
71
104
|
The blackboard files are the source of truth for the session. The facilitator can read the index or specific entries when producing summaries.
|
package/README.zh-CN.md
CHANGED
|
@@ -10,13 +10,14 @@ English README: [README.md](./README.md).
|
|
|
10
10
|
|
|
11
11
|
## 功能
|
|
12
12
|
|
|
13
|
-
-
|
|
13
|
+
- 多模型头脑风暴:参与者可配置(默认:GPT、DeepSeek、MiniMax)。
|
|
14
14
|
- 辩论 / battle 模式:Agent 会攻击、审视、反驳彼此的观点。
|
|
15
|
+
- 配置驱动:通过 YAML 添加、删除或自定义参与者。
|
|
15
16
|
- 每轮输出聚焦共识、分歧、关键问题和下一步方向。
|
|
16
17
|
- 参与者完整发言以 Markdown 文件保存到 `.pi-meetings/`。
|
|
17
18
|
- 主会话中展示紧凑发言卡片,而不是粘贴长篇 transcript。
|
|
18
19
|
- 使用 JSONL 索引作为轻量级跨轮上下文入口。
|
|
19
|
-
-
|
|
20
|
+
- 由配置自动生成托管的 agent 文件——更新一个 YAML,所有 agent 同步。
|
|
20
21
|
|
|
21
22
|
## 安装
|
|
22
23
|
|
|
@@ -29,7 +30,7 @@ pi install npm:pi-brainstorm
|
|
|
29
30
|
通过 GitHub 安装:
|
|
30
31
|
|
|
31
32
|
```bash
|
|
32
|
-
pi install git:github.com/Jarcis-cy/pi-brainstorm@v0.
|
|
33
|
+
pi install git:github.com/Jarcis-cy/pi-brainstorm@v0.3.1
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
本地开发安装:
|
|
@@ -38,15 +39,53 @@ pi install git:github.com/Jarcis-cy/pi-brainstorm@v0.2.0
|
|
|
38
39
|
pi install /Users/jarcis/Project/pi-brainstorm
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
##
|
|
42
|
+
## 配置
|
|
43
|
+
|
|
44
|
+
参与者通过 YAML 定义。插件按以下顺序加载配置(后者覆盖前者):
|
|
45
|
+
|
|
46
|
+
1. 包默认配置:`config/default.yaml`(随包发布)
|
|
47
|
+
2. 用户级覆盖:`~/.pi/agent/pi-brainstorm.yaml`
|
|
48
|
+
3. 项目级覆盖:`.pi-brainstorm.yaml` 或 `.pi/pi-brainstorm.yaml`
|
|
49
|
+
|
|
50
|
+
数组(如 `participants`)整体替换;对象字段深度合并。
|
|
51
|
+
|
|
52
|
+
### 添加新参与者
|
|
53
|
+
|
|
54
|
+
创建 `~/.pi/agent/pi-brainstorm.yaml`(用户级)或 `.pi-brainstorm.yaml`(项目级):
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
participants:
|
|
58
|
+
- displayName: Claude
|
|
59
|
+
agentName: claude-brainstormer
|
|
60
|
+
description: Claude 头脑风暴顾问。细致入微的分析师,用于多模型讨论。
|
|
61
|
+
model: anthropic/claude-sonnet-4-20250514:xhigh
|
|
62
|
+
roleTitle: 细致分析师
|
|
63
|
+
rolePrompt: |
|
|
64
|
+
你是多模型头脑风暴中的分析顾问。擅长细致入微的论证和长篇分析。用中文回答。
|
|
65
|
+
whatYouDo:
|
|
66
|
+
- 提供细致、深入的逐点分析
|
|
67
|
+
- 识别细微差别和边缘情况
|
|
68
|
+
- 撰写结构清晰的长篇论证
|
|
69
|
+
debatePersona:
|
|
70
|
+
label: THE ANALYST
|
|
71
|
+
prompt: |
|
|
72
|
+
DEBATE MODE. You are THE ANALYST. Dissect every argument with precision. Find the weakest link in every chain of reasoning. Use Chinese.
|
|
73
|
+
brainstormRole: 细致分析师
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
如需完全替换默认参与者,在覆盖文件中定义完整的 `participants` 列表即可。
|
|
77
|
+
|
|
78
|
+
用户级配置可以在确认后创建或更新 `~/.pi/agent/agents/` 下的受管理 agent 文件。项目级配置只影响当前会话编排,但不会自动写入全局 agent 文件;如果需要自动同步 agent,请手动创建对应 agent,或把配置移到 `~/.pi/agent/pi-brainstorm.yaml`。
|
|
42
79
|
|
|
43
|
-
|
|
80
|
+
### 托管的 Agent 文件
|
|
81
|
+
|
|
82
|
+
由 pi-brainstorm 生成的 agent 文件包含 `<!-- managed-by: pi-brainstorm -->` 标记。当配置变更时,这些文件会被覆盖。不含此标记的已有 agent 文件永远不会被修改。
|
|
83
|
+
|
|
84
|
+
## 前置条件
|
|
44
85
|
|
|
45
|
-
|
|
46
|
-
- `deepseek-brainstormer`
|
|
47
|
-
- `minimax-brainstormer`
|
|
86
|
+
该扩展依赖 pi 中已有的 `subagent` 工具。命令处理器会先创建本地会议记录,然后让主 Agent 调用配置中定义的参与者(默认:`gpt-brainstormer`、`deepseek-brainstormer`、`minimax-brainstormer`)。
|
|
48
87
|
|
|
49
|
-
第一次使用时,如果这些用户级 agent
|
|
88
|
+
第一次使用时,如果这些用户级 agent 不存在,扩展会询问是否写入内置默认定义。已有同名且不含托管标记的文件不会被覆盖。
|
|
50
89
|
|
|
51
90
|
## 命令
|
|
52
91
|
|
|
@@ -55,7 +94,7 @@ pi install /Users/jarcis/Project/pi-brainstorm
|
|
|
55
94
|
/debate <主题>
|
|
56
95
|
```
|
|
57
96
|
|
|
58
|
-
`/brainstorm`
|
|
97
|
+
`/brainstorm` 启动交互式三轮多模型头脑风暴。Round 1 和 Round 2 结束后,主持人应停止、总结本轮,并等待你的反馈或继续许可,再进入下一轮。
|
|
59
98
|
|
|
60
99
|
`/debate` 启动开放式多 Agent battle,直到收敛或用户介入为止。
|
|
61
100
|
|
|
@@ -65,7 +104,7 @@ pi install /Users/jarcis/Project/pi-brainstorm
|
|
|
65
104
|
参与者模型 -> meeting_append_entry -> .pi-meetings/... 文件
|
|
66
105
|
参与者模型 -> 短 WROTE_ENTRY 摘要 -> 主持人上下文
|
|
67
106
|
文件 watcher -> 短卡片 -> 主会话可见区域
|
|
68
|
-
主持人 -> 共识 / 分歧 / 最终综合
|
|
107
|
+
主持人 -> 每轮 checkpoint / 共识 / 分歧 / 最终综合
|
|
69
108
|
```
|
|
70
109
|
|
|
71
110
|
黑板文件是会话事实源。主持人可以按需读取索引或具体条目,然后生成结构化总结。
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# pi-brainstorm default configuration
|
|
2
|
+
# Each participant defines a subagent that joins brainstorming/debate sessions.
|
|
3
|
+
#
|
|
4
|
+
# To override: create ~/.pi/agent/pi-brainstorm.yaml (user-level)
|
|
5
|
+
# or .pi-brainstorm.yaml / .pi/pi-brainstorm.yaml (project-level)
|
|
6
|
+
# Later files override earlier. Arrays replace entirely; objects deep-merge.
|
|
7
|
+
|
|
8
|
+
participants:
|
|
9
|
+
- displayName: GPT
|
|
10
|
+
agentName: gpt-brainstormer
|
|
11
|
+
description: GPT brainstorming consultant. Visionary strategist for multi-model discussion sessions.
|
|
12
|
+
model: vendor-codex/gpt-5.5:xhigh
|
|
13
|
+
roleTitle: Visionary Strategist
|
|
14
|
+
rolePrompt: |
|
|
15
|
+
你是多模型头脑风暴中的愿景战略家。思考大局,发现别人忽略的机会,将复杂权衡综合为清晰方向。用中文回答。
|
|
16
|
+
whatYouDo:
|
|
17
|
+
- 提出创新的战略方向和解决方案
|
|
18
|
+
- 发现别人忽略的机会和盲点
|
|
19
|
+
- 把零散想法综合成连贯战略
|
|
20
|
+
debatePersona:
|
|
21
|
+
label: THE PROSECUTOR
|
|
22
|
+
prompt: |
|
|
23
|
+
DEBATE MODE. You are THE PROSECUTOR. Attack the other positions ruthlessly. Find every logical flaw, every hidden assumption, every missing edge case. Be relentless. Do not concede easily. Use Chinese.
|
|
24
|
+
brainstormRole: Visionary strategist
|
|
25
|
+
|
|
26
|
+
- displayName: DeepSeek
|
|
27
|
+
agentName: deepseek-brainstormer
|
|
28
|
+
description: DeepSeek brainstorming consultant. Meticulous systems thinker for multi-model discussion sessions.
|
|
29
|
+
model: deepseek/deepseek-v4-pro:xhigh
|
|
30
|
+
roleTitle: Meticulous Systems Thinker
|
|
31
|
+
rolePrompt: |
|
|
32
|
+
你是多模型头脑风暴中的系统思考者。分析结构、依赖、扩展上限和失败模式。用中文回答。
|
|
33
|
+
whatYouDo:
|
|
34
|
+
- 从结构、依赖和风险角度分析提案
|
|
35
|
+
- 识别隐藏耦合、扩展上限和失败模式
|
|
36
|
+
- 提出具体、可实现、可验证的技术优化方案
|
|
37
|
+
debatePersona:
|
|
38
|
+
label: THE SYSTEMS SKEPTIC
|
|
39
|
+
prompt: |
|
|
40
|
+
DEBATE MODE. You are THE SYSTEMS SKEPTIC. Dissect the structural implications of every proposal. What breaks at scale? Where are the hidden costs? What dependencies create risk? Be precise and unforgiving. Use Chinese.
|
|
41
|
+
brainstormRole: Systems thinker
|
|
42
|
+
|
|
43
|
+
- displayName: MiniMax
|
|
44
|
+
agentName: minimax-brainstormer
|
|
45
|
+
description: MiniMax brainstorming consultant. Creative lateral thinker for multi-model discussion sessions.
|
|
46
|
+
model: minimax-cn/MiniMax-M3:xhigh
|
|
47
|
+
roleTitle: Creative Lateral Thinker
|
|
48
|
+
rolePrompt: |
|
|
49
|
+
你是多模型头脑风暴中的创意顾问。跳出框框思考,挑战隐性假设,提出非常规方案。用中文回答。
|
|
50
|
+
whatYouDo:
|
|
51
|
+
- 从意想不到的角度切入问题
|
|
52
|
+
- 提出打破常规的创新方案
|
|
53
|
+
- 挑战团队隐性假设
|
|
54
|
+
debatePersona:
|
|
55
|
+
label: THE CONTRARIAN
|
|
56
|
+
prompt: |
|
|
57
|
+
DEBATE MODE. You are THE CONTRARIAN. Take the opposite position from the dominant view. Expose groupthink. Propose radical alternatives. Challenge the fundamental premise if needed. Use Chinese.
|
|
58
|
+
brainstormRole: Creative lateral thinker
|
package/extensions/brainstorm.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-brainstorm — Multi-model brainstorm/debate extension for Pi
|
|
3
3
|
*
|
|
4
|
-
* Runs brainstorm and
|
|
5
|
-
* Full participant contributions are stored in a local filesystem
|
|
6
|
-
* while the main conversation sees compact cards and facilitator
|
|
4
|
+
* Runs brainstorm and debate sessions across multiple subagents configured
|
|
5
|
+
* via YAML. Full participant contributions are stored in a local filesystem
|
|
6
|
+
* blackboard, while the main conversation sees compact cards and facilitator
|
|
7
|
+
* synthesis.
|
|
7
8
|
*
|
|
8
9
|
* Features:
|
|
10
|
+
* - Configuration-driven participants (YAML)
|
|
9
11
|
* - meeting_append_entry tool — concurrency-safe append to meeting folder
|
|
10
12
|
* - meeting_read_index tool — read meeting index
|
|
11
13
|
* - meeting_read_entry tool — read full entry content
|
|
@@ -13,15 +15,632 @@
|
|
|
13
15
|
* - /debate command — open-ended multi-agent debate
|
|
14
16
|
* - meeting-entry message renderer — compact cards with expandable content
|
|
15
17
|
* - File watcher — auto-posts new entries into the main conversation
|
|
18
|
+
* - Managed agent file generation from config
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
import * as path from "node:path";
|
|
19
22
|
import * as fs from "node:fs";
|
|
20
23
|
import * as fsp from "node:fs/promises";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
21
25
|
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
22
26
|
import { Type } from "typebox";
|
|
23
27
|
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
24
28
|
import { Text, Box } from "@earendil-works/pi-tui";
|
|
29
|
+
import * as YAML from "yaml";
|
|
30
|
+
|
|
31
|
+
// ────────────────────────────────────────────────────────
|
|
32
|
+
// Types
|
|
33
|
+
// ────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
interface DebatePersona {
|
|
36
|
+
label: string;
|
|
37
|
+
prompt: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ParticipantConfig {
|
|
41
|
+
displayName: string;
|
|
42
|
+
agentName: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
model: string;
|
|
45
|
+
roleTitle?: string;
|
|
46
|
+
rolePrompt: string;
|
|
47
|
+
whatYouDo?: string[];
|
|
48
|
+
debatePersona?: DebatePersona;
|
|
49
|
+
brainstormRole?: string;
|
|
50
|
+
tools?: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface BrainstormConfig {
|
|
54
|
+
participants: ParticipantConfig[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ────────────────────────────────────────────────────────
|
|
58
|
+
// Constants
|
|
59
|
+
// ────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
const MANAGED_MARKER = "<!-- managed-by: pi-brainstorm -->";
|
|
62
|
+
const DEFAULT_TOOLS = [
|
|
63
|
+
"read",
|
|
64
|
+
"grep",
|
|
65
|
+
"find",
|
|
66
|
+
"ls",
|
|
67
|
+
"meeting_append_entry",
|
|
68
|
+
"meeting_read_index",
|
|
69
|
+
"meeting_read_entry",
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
// ────────────────────────────────────────────────────────
|
|
73
|
+
// Config helpers
|
|
74
|
+
// ────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Deep-merge two values. Arrays are replaced entirely; objects are
|
|
78
|
+
* shallow-merged recursively; scalars use the overlay value.
|
|
79
|
+
*/
|
|
80
|
+
function deepMerge(base: any, overlay: any): any {
|
|
81
|
+
if (overlay === null || overlay === undefined) return base;
|
|
82
|
+
if (base === null || base === undefined) return overlay;
|
|
83
|
+
|
|
84
|
+
if (Array.isArray(base) && Array.isArray(overlay)) {
|
|
85
|
+
return overlay;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
typeof base === "object" &&
|
|
90
|
+
typeof overlay === "object" &&
|
|
91
|
+
!Array.isArray(base) &&
|
|
92
|
+
!Array.isArray(overlay)
|
|
93
|
+
) {
|
|
94
|
+
const result: Record<string, any> = { ...base };
|
|
95
|
+
for (const key of Object.keys(overlay)) {
|
|
96
|
+
result[key] =
|
|
97
|
+
key in result
|
|
98
|
+
? deepMerge(result[key], overlay[key])
|
|
99
|
+
: overlay[key];
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return overlay;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolve extension directory from import.meta.url.
|
|
109
|
+
*/
|
|
110
|
+
function getExtensionDir(): string {
|
|
111
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Load and merge config from all locations, in priority order (later wins):
|
|
116
|
+
* 1. Package default: config/default.yaml (relative to package root or extension dir)
|
|
117
|
+
* Also try brainstorm.yaml in extension dir (for manual installs)
|
|
118
|
+
* 2. User override: ~/.pi/agent/pi-brainstorm.yaml
|
|
119
|
+
* 3. Project override: {cwd}/.pi-brainstorm.yaml
|
|
120
|
+
* 4. Project override: {cwd}/.pi/pi-brainstorm.yaml
|
|
121
|
+
*/
|
|
122
|
+
function hasProjectConfig(cwd: string): boolean {
|
|
123
|
+
if (!cwd) return false;
|
|
124
|
+
return [
|
|
125
|
+
path.join(cwd, ".pi-brainstorm.yaml"),
|
|
126
|
+
path.join(cwd, ".pi", "pi-brainstorm.yaml"),
|
|
127
|
+
].some((candidate) => fs.existsSync(candidate));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function loadConfig(cwd: string): BrainstormConfig {
|
|
131
|
+
const extensionDir = getExtensionDir();
|
|
132
|
+
const packageRoot = path.dirname(extensionDir);
|
|
133
|
+
|
|
134
|
+
// Step 1: package/extension defaults
|
|
135
|
+
const defaultCandidates = [
|
|
136
|
+
path.join(packageRoot, "config", "default.yaml"),
|
|
137
|
+
path.join(extensionDir, "config", "default.yaml"),
|
|
138
|
+
path.join(extensionDir, "brainstorm.yaml"),
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
let merged: any = {};
|
|
142
|
+
let loadedAny = false;
|
|
143
|
+
|
|
144
|
+
for (const candidate of defaultCandidates) {
|
|
145
|
+
if (fs.existsSync(candidate)) {
|
|
146
|
+
try {
|
|
147
|
+
const raw = fs.readFileSync(candidate, "utf-8");
|
|
148
|
+
const parsed = YAML.parse(raw);
|
|
149
|
+
if (parsed && typeof parsed === "object") {
|
|
150
|
+
merged = deepMerge(merged, parsed);
|
|
151
|
+
loadedAny = true;
|
|
152
|
+
}
|
|
153
|
+
} catch (err: any) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Failed to parse config ${candidate}: ${err.message}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!loadedAny) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
"No pi-brainstorm config found. Expected config/default.yaml in package root or extension directory."
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Step 2: user override
|
|
168
|
+
const userPath = path.join(getAgentDir(), "pi-brainstorm.yaml");
|
|
169
|
+
if (fs.existsSync(userPath)) {
|
|
170
|
+
try {
|
|
171
|
+
const raw = fs.readFileSync(userPath, "utf-8");
|
|
172
|
+
const parsed = YAML.parse(raw);
|
|
173
|
+
if (parsed && typeof parsed === "object") {
|
|
174
|
+
merged = deepMerge(merged, parsed);
|
|
175
|
+
}
|
|
176
|
+
} catch (err: any) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Failed to parse user config ${userPath}: ${err.message}`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Step 3: project overrides
|
|
184
|
+
const projectCandidates = cwd
|
|
185
|
+
? [path.join(cwd, ".pi-brainstorm.yaml"), path.join(cwd, ".pi", "pi-brainstorm.yaml")]
|
|
186
|
+
: [];
|
|
187
|
+
|
|
188
|
+
for (const candidate of projectCandidates) {
|
|
189
|
+
if (fs.existsSync(candidate)) {
|
|
190
|
+
try {
|
|
191
|
+
const raw = fs.readFileSync(candidate, "utf-8");
|
|
192
|
+
const parsed = YAML.parse(raw);
|
|
193
|
+
if (parsed && typeof parsed === "object") {
|
|
194
|
+
merged = deepMerge(merged, parsed);
|
|
195
|
+
}
|
|
196
|
+
} catch (err: any) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Failed to parse project config ${candidate}: ${err.message}`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return merged as BrainstormConfig;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Resolve and validate participants for a command invocation.
|
|
209
|
+
* Returns validated participant array; throws with a clear message on failure.
|
|
210
|
+
*/
|
|
211
|
+
function isSafeAgentName(value: string): boolean {
|
|
212
|
+
return /^[A-Za-z0-9][A-Za-z0-9_.-]{0,80}$/.test(value);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function resolveParticipants(cwd: string): ParticipantConfig[] {
|
|
216
|
+
const config = loadConfig(cwd);
|
|
217
|
+
|
|
218
|
+
if (
|
|
219
|
+
!config.participants ||
|
|
220
|
+
!Array.isArray(config.participants) ||
|
|
221
|
+
config.participants.length === 0
|
|
222
|
+
) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
"pi-brainstorm config must define at least one participant under 'participants'."
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const requiredFields: (keyof ParticipantConfig)[] = [
|
|
229
|
+
"displayName",
|
|
230
|
+
"agentName",
|
|
231
|
+
"model",
|
|
232
|
+
"rolePrompt",
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
for (let i = 0; i < config.participants.length; i++) {
|
|
236
|
+
const p = config.participants[i];
|
|
237
|
+
for (const field of requiredFields) {
|
|
238
|
+
if (!p[field]) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Participant at index ${i} is missing required field "${field}".`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (typeof p.displayName !== "string" || !p.displayName.trim()) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Participant at index ${i} has invalid displayName.`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (typeof p.agentName !== "string" || !p.agentName.trim()) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Participant at index ${i} has invalid agentName.`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
if (!isSafeAgentName(p.agentName)) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Participant "${p.displayName}" has unsafe agentName "${p.agentName}". Use only letters, digits, dot, underscore, and hyphen; it must start with a letter or digit.`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
if (typeof p.model !== "string" || !p.model.trim()) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
`Participant at index ${i} has invalid model.`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
if (typeof p.rolePrompt !== "string" || !p.rolePrompt.trim()) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
`Participant at index ${i} has invalid rolePrompt.`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return config.participants;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ────────────────────────────────────────────────────────
|
|
275
|
+
// Agent file generation
|
|
276
|
+
// ────────────────────────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Generate the content of a managed agent markdown file from a participant config.
|
|
280
|
+
*/
|
|
281
|
+
function yamlScalar(value: string): string {
|
|
282
|
+
return JSON.stringify(value);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function generateAgentFile(participant: ParticipantConfig): string {
|
|
286
|
+
const tools = participant.tools && participant.tools.length > 0
|
|
287
|
+
? participant.tools
|
|
288
|
+
: DEFAULT_TOOLS;
|
|
289
|
+
const toolsStr = tools.join(", ");
|
|
290
|
+
|
|
291
|
+
const description =
|
|
292
|
+
participant.description ||
|
|
293
|
+
`${participant.displayName} brainstorming consultant.`;
|
|
294
|
+
|
|
295
|
+
const roleTitle = participant.roleTitle
|
|
296
|
+
? ` - ${participant.roleTitle}`
|
|
297
|
+
: "";
|
|
298
|
+
|
|
299
|
+
const whatYouDoLines = (participant.whatYouDo && participant.whatYouDo.length > 0)
|
|
300
|
+
? participant.whatYouDo.map((item) => `- ${item}`).join("\n")
|
|
301
|
+
: `- 参与多模型讨论并提供${participant.displayName}视角的分析`;
|
|
302
|
+
|
|
303
|
+
return [
|
|
304
|
+
MANAGED_MARKER,
|
|
305
|
+
"---",
|
|
306
|
+
`name: ${yamlScalar(participant.agentName)}`,
|
|
307
|
+
`description: ${yamlScalar(description)}`,
|
|
308
|
+
`tools: ${toolsStr}`,
|
|
309
|
+
`model: ${yamlScalar(participant.model)}`,
|
|
310
|
+
"---",
|
|
311
|
+
"",
|
|
312
|
+
`# ${participant.displayName} Brainstormer${roleTitle}`,
|
|
313
|
+
"",
|
|
314
|
+
participant.rolePrompt,
|
|
315
|
+
"",
|
|
316
|
+
"## What You Do",
|
|
317
|
+
whatYouDoLines,
|
|
318
|
+
"",
|
|
319
|
+
"## What You Do Not Do",
|
|
320
|
+
"- 写代码或修改项目文件,你只读项目文件",
|
|
321
|
+
"- 委派给其他 Agent",
|
|
322
|
+
"- 在聊天中直接粘贴长篇分析;当明确指示使用 meeting_append_entry 时,必须将完整贡献写入会议黑板,最终回复仅写 WROTE_ENTRY + 一句话摘要",
|
|
323
|
+
"",
|
|
324
|
+
"## Worker Preamble",
|
|
325
|
+
"You are a terminal worker. Work directly with tools. Do NOT spawn sub-agents.",
|
|
326
|
+
"",
|
|
327
|
+
].join("\n");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Ensure agent files exist for all configured participants.
|
|
332
|
+
* - Files with the managed marker are overwritten from current config.
|
|
333
|
+
* - Files without the marker are never touched.
|
|
334
|
+
* - Missing files are created after user confirmation.
|
|
335
|
+
*
|
|
336
|
+
* Returns true if all participants have existing agent files (managed or not).
|
|
337
|
+
*/
|
|
338
|
+
async function safeWriteAgentFile(targetPath: string, content: string, mode: "create" | "update"): Promise<void> {
|
|
339
|
+
const dir = path.dirname(targetPath);
|
|
340
|
+
assertDirectoryNoSymlink(dir, "agents directory");
|
|
341
|
+
assertPathInside(fs.realpathSync(dir), path.resolve(targetPath), "agent file");
|
|
342
|
+
|
|
343
|
+
if (mode === "create") {
|
|
344
|
+
await fsp.writeFile(targetPath, content, { encoding: "utf-8", flag: "wx" });
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
assertExistingFileNoSymlink(targetPath, "agent file");
|
|
349
|
+
const tempPath = path.join(
|
|
350
|
+
dir,
|
|
351
|
+
`.${path.basename(targetPath)}.${process.pid}.${Date.now()}.tmp`
|
|
352
|
+
);
|
|
353
|
+
await fsp.writeFile(tempPath, content, { encoding: "utf-8", flag: "wx" });
|
|
354
|
+
try {
|
|
355
|
+
await fsp.rename(tempPath, targetPath);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
try {
|
|
358
|
+
await fsp.unlink(tempPath);
|
|
359
|
+
} catch {
|
|
360
|
+
// Ignore cleanup errors.
|
|
361
|
+
}
|
|
362
|
+
throw err;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function ensureAgentsFromConfig(
|
|
367
|
+
ctx: any,
|
|
368
|
+
participants: ParticipantConfig[],
|
|
369
|
+
options: { allowGlobalWrites: boolean }
|
|
370
|
+
): Promise<boolean> {
|
|
371
|
+
const agentsDir = path.join(getAgentDir(), "agents");
|
|
372
|
+
|
|
373
|
+
const planned: { filename: string; action: "create" | "update" }[] = [];
|
|
374
|
+
const protectedFiles: string[] = [];
|
|
375
|
+
|
|
376
|
+
for (const p of participants) {
|
|
377
|
+
const filename = `${p.agentName}.md`;
|
|
378
|
+
const targetPath = path.resolve(agentsDir, filename);
|
|
379
|
+
assertPathInside(agentsDir, targetPath, "agent file");
|
|
380
|
+
|
|
381
|
+
if (fs.existsSync(targetPath)) {
|
|
382
|
+
assertExistingFileNoSymlink(targetPath, "agent file");
|
|
383
|
+
const content = fs.readFileSync(targetPath, "utf-8");
|
|
384
|
+
if (content.includes(MANAGED_MARKER)) {
|
|
385
|
+
planned.push({ filename, action: "update" });
|
|
386
|
+
} else {
|
|
387
|
+
protectedFiles.push(filename);
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
planned.push({ filename, action: "create" });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// If nothing to create or update, we're done
|
|
395
|
+
if (planned.length === 0) {
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (!options.allowGlobalWrites) {
|
|
400
|
+
ctx.ui?.notify?.(
|
|
401
|
+
"Project-level pi-brainstorm config is active. For safety, this command will not create or update global agent files. Create the listed agents manually or move the config to ~/.pi/agent/pi-brainstorm.yaml.",
|
|
402
|
+
"warning"
|
|
403
|
+
);
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Non-interactive mode
|
|
408
|
+
if (!ctx.hasUI) {
|
|
409
|
+
// Update managed files silently
|
|
410
|
+
await fsp.mkdir(agentsDir, { recursive: true });
|
|
411
|
+
assertDirectoryNoSymlink(agentsDir, "agents directory");
|
|
412
|
+
for (const plan of planned) {
|
|
413
|
+
if (plan.action === "update") {
|
|
414
|
+
const p = participants.find(
|
|
415
|
+
(p) => `${p.agentName}.md` === plan.filename
|
|
416
|
+
)!;
|
|
417
|
+
const targetPath = path.resolve(agentsDir, plan.filename);
|
|
418
|
+
assertPathInside(agentsDir, targetPath, "agent file");
|
|
419
|
+
await safeWriteAgentFile(
|
|
420
|
+
targetPath,
|
|
421
|
+
generateAgentFile(p),
|
|
422
|
+
"update"
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Report missing
|
|
427
|
+
const missing = planned.filter((p) => p.action === "create");
|
|
428
|
+
if (missing.length > 0) {
|
|
429
|
+
ctx.ui?.notify?.(
|
|
430
|
+
`Missing meeting agents: ${missing.map((m) => m.filename).join(", ")}. Install them under ${agentsDir}.`,
|
|
431
|
+
"warning"
|
|
432
|
+
);
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Interactive mode: ask user
|
|
439
|
+
let message = "";
|
|
440
|
+
if (planned.length > 0) {
|
|
441
|
+
const actionWord = planned.some((p) => p.action === "update")
|
|
442
|
+
? "created/updated"
|
|
443
|
+
: "created";
|
|
444
|
+
message += `The following agent files will be ${actionWord}:\n`;
|
|
445
|
+
for (const p of planned) {
|
|
446
|
+
message += ` - ${p.filename} (${p.action})\n`;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (protectedFiles.length > 0) {
|
|
450
|
+
message += `\nThe following existing files are NOT managed by pi-brainstorm and will be left untouched:\n`;
|
|
451
|
+
for (const f of protectedFiles) {
|
|
452
|
+
message += ` - ${f}\n`;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const title = planned.some((p) => p.action === "update")
|
|
457
|
+
? "Update brainstorm agents?"
|
|
458
|
+
: "Install brainstorm agents?";
|
|
459
|
+
|
|
460
|
+
const ok = await ctx.ui.confirm(
|
|
461
|
+
title,
|
|
462
|
+
message + `\nFiles will be written to ${agentsDir}.`
|
|
463
|
+
);
|
|
464
|
+
if (!ok) return false;
|
|
465
|
+
|
|
466
|
+
await fsp.mkdir(agentsDir, { recursive: true });
|
|
467
|
+
assertDirectoryNoSymlink(agentsDir, "agents directory");
|
|
468
|
+
for (const plan of planned) {
|
|
469
|
+
const p = participants.find(
|
|
470
|
+
(p) => `${p.agentName}.md` === plan.filename
|
|
471
|
+
)!;
|
|
472
|
+
const targetPath = path.resolve(agentsDir, plan.filename);
|
|
473
|
+
assertPathInside(agentsDir, targetPath, "agent file");
|
|
474
|
+
await safeWriteAgentFile(targetPath, generateAgentFile(p), plan.action);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
ctx.ui.notify(`Updated ${planned.length} agent file(s).`, "info");
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// ────────────────────────────────────────────────────────
|
|
482
|
+
// Prompt builders
|
|
483
|
+
// ────────────────────────────────────────────────────────
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Build the facilitator prompt for /brainstorm.
|
|
487
|
+
*/
|
|
488
|
+
function buildBrainstormPrompt(
|
|
489
|
+
topic: string,
|
|
490
|
+
absDir: string,
|
|
491
|
+
participants: ParticipantConfig[]
|
|
492
|
+
): string {
|
|
493
|
+
const consultantLines = participants
|
|
494
|
+
.map(
|
|
495
|
+
(p) =>
|
|
496
|
+
`- **${p.displayName}**: use the ${p.agentName} subagent. ${p.brainstormRole || p.roleTitle || "Consultant"}.`
|
|
497
|
+
)
|
|
498
|
+
.join("\n");
|
|
499
|
+
|
|
500
|
+
const agentTaskLines = participants
|
|
501
|
+
.map(
|
|
502
|
+
(p) =>
|
|
503
|
+
` - speaker: "${p.displayName}"`
|
|
504
|
+
)
|
|
505
|
+
.join("\n");
|
|
506
|
+
|
|
507
|
+
return [
|
|
508
|
+
`BLACKBOARD BRAINSTORMING SESSION: ${topic}`,
|
|
509
|
+
"",
|
|
510
|
+
`Meeting folder: \`${absDir}\``,
|
|
511
|
+
"",
|
|
512
|
+
"You are facilitating a round-robin brainstorming session using the MEETING BLACKBOARD.",
|
|
513
|
+
"Each consultant writes their FULL contribution to disk via meeting_append_entry.",
|
|
514
|
+
"",
|
|
515
|
+
"## Consultants (3 rounds)",
|
|
516
|
+
consultantLines,
|
|
517
|
+
"",
|
|
518
|
+
"## CRITICAL INSTRUCTIONS",
|
|
519
|
+
"",
|
|
520
|
+
"### For subagents (include in EVERY task):",
|
|
521
|
+
"1. Write your FULL contribution using the meeting_append_entry tool with:",
|
|
522
|
+
` - meetingDir: "${absDir}"`,
|
|
523
|
+
" - speaker: your display name, e.g.:",
|
|
524
|
+
agentTaskLines,
|
|
525
|
+
' - phase: "Round 1", "Round 2", or "Round 3"',
|
|
526
|
+
" - summary: a ONE-SENTENCE summary of your contribution",
|
|
527
|
+
" - content: your FULL analysis in Chinese (中文)",
|
|
528
|
+
" - content must contain only the participant's analysis. Do not include wrapper tags, hidden thinking markers, tool-call text, or WROTE_ENTRY text inside content.",
|
|
529
|
+
"2. After writing, your FINAL ANSWER must be ONLY:",
|
|
530
|
+
" `WROTE_ENTRY: <your one-sentence summary>`",
|
|
531
|
+
"3. DO NOT paste your full analysis into the chat. The main agent and user will read it from the blackboard.",
|
|
532
|
+
"",
|
|
533
|
+
"### For you, the facilitator:",
|
|
534
|
+
"- Do NOT paste participant full text into chat. They are on the blackboard.",
|
|
535
|
+
"- After each round, read the index with meeting_read_index and present a structural overview.",
|
|
536
|
+
"- Optionally read full entries with meeting_read_entry when needed.",
|
|
537
|
+
"- Present each consultant's summary + your structural overview (conflict matrix, consensus table).",
|
|
538
|
+
"- When the user gives feedback, relay it VERBATIM to the consultants in the next round.",
|
|
539
|
+
"",
|
|
540
|
+
"## Protocol",
|
|
541
|
+
"Round 1: Each consultant gives initial analysis on the topic. Run all in parallel.",
|
|
542
|
+
"After Round 1: read the index, present summaries plus a structural overview, then STOP. Ask the user for feedback or permission to continue. Do NOT start Round 2 in the same assistant turn.",
|
|
543
|
+
"Round 2: only after the user replies, feed Round 1 plus the user's VERBATIM feedback back to each consultant. Ask each to challenge the others and propose improvements.",
|
|
544
|
+
"After Round 2: read the index, present summaries plus an updated structural overview, then STOP. Ask the user for feedback or permission to continue. Do NOT start Round 3 in the same assistant turn.",
|
|
545
|
+
"Round 3: only after the user replies, feed all prior rounds plus the user's VERBATIM feedback back to each consultant. Each gives FINAL recommendation, synthesizing the best ideas.",
|
|
546
|
+
"",
|
|
547
|
+
"After Round 3, present the complete structural overview and ask whether to write the final conclusion. Only write conclusion.md after the user confirms.",
|
|
548
|
+
"",
|
|
549
|
+
"## IMPORTANT",
|
|
550
|
+
"- All responses in Chinese (中文).",
|
|
551
|
+
"- Save transcript.md and (after user confirms) conclusion.md per the MEETING OUTPUT PROTOCOL.",
|
|
552
|
+
"- The user can intervene at any time to steer the discussion.",
|
|
553
|
+
].join("\n");
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Build the facilitator prompt for /debate.
|
|
558
|
+
*/
|
|
559
|
+
function buildDebatePrompt(
|
|
560
|
+
topic: string,
|
|
561
|
+
absDir: string,
|
|
562
|
+
participants: ParticipantConfig[]
|
|
563
|
+
): string {
|
|
564
|
+
const debaterLines = participants
|
|
565
|
+
.map((p) => {
|
|
566
|
+
const dp = p.debatePersona;
|
|
567
|
+
if (dp) {
|
|
568
|
+
return `- **${p.displayName}** (${p.agentName}): ${dp.label} — Attack other positions, find flaws, expose assumptions.`;
|
|
569
|
+
}
|
|
570
|
+
return `- **${p.displayName}** (${p.agentName})`;
|
|
571
|
+
})
|
|
572
|
+
.join("\n");
|
|
573
|
+
|
|
574
|
+
// Build per-agent debate task prefixes for the facilitator to include
|
|
575
|
+
const taskPrefixLines = participants
|
|
576
|
+
.map((p) => {
|
|
577
|
+
const dp = p.debatePersona;
|
|
578
|
+
if (dp && dp.prompt) {
|
|
579
|
+
return `**${p.displayName}** (${p.agentName}):\n${dp.prompt}`;
|
|
580
|
+
}
|
|
581
|
+
return `**${p.displayName}** (${p.agentName}): Debate participant.`;
|
|
582
|
+
})
|
|
583
|
+
.join("\n\n");
|
|
584
|
+
|
|
585
|
+
const agentTaskLines = participants
|
|
586
|
+
.map(
|
|
587
|
+
(p) =>
|
|
588
|
+
` - speaker: "${p.displayName}"`
|
|
589
|
+
)
|
|
590
|
+
.join("\n");
|
|
591
|
+
|
|
592
|
+
return [
|
|
593
|
+
`⚔️ BLACKBOARD DEBATE: ${topic}`,
|
|
594
|
+
"",
|
|
595
|
+
`Meeting folder: \`${absDir}\``,
|
|
596
|
+
"",
|
|
597
|
+
"You are facilitating an OPEN-ENDED debate using the MEETING BLACKBOARD.",
|
|
598
|
+
"Each debater writes their FULL argument to disk via meeting_append_entry.",
|
|
599
|
+
"Continue until the debate CONVERGES or the user intervenes.",
|
|
600
|
+
"",
|
|
601
|
+
"## Debaters (cycling indefinitely)",
|
|
602
|
+
debaterLines,
|
|
603
|
+
"",
|
|
604
|
+
"## DEBATE PERSONAS (include in each subagent task)",
|
|
605
|
+
taskPrefixLines,
|
|
606
|
+
"",
|
|
607
|
+
"## CRITICAL INSTRUCTIONS",
|
|
608
|
+
"",
|
|
609
|
+
"### For subagents (include in EVERY task):",
|
|
610
|
+
"1. Write your FULL contribution using the meeting_append_entry tool with:",
|
|
611
|
+
` - meetingDir: "${absDir}"`,
|
|
612
|
+
" - speaker: your display name, e.g.:",
|
|
613
|
+
agentTaskLines,
|
|
614
|
+
' - phase: "Cycle 1", "Cycle 2", etc.',
|
|
615
|
+
" - summary: a ONE-SENTENCE summary of your argument",
|
|
616
|
+
" - content: your FULL argument in Chinese (中文)",
|
|
617
|
+
"2. After writing, your FINAL ANSWER must be ONLY:",
|
|
618
|
+
" `WROTE_ENTRY: <your one-sentence summary>`",
|
|
619
|
+
"3. DO NOT paste your full argument into the chat.",
|
|
620
|
+
"",
|
|
621
|
+
"### Include the FULL VERBATIM prior debate record in each subagent task.",
|
|
622
|
+
"Use meeting_read_index and meeting_read_entry to retrieve the complete debate history.",
|
|
623
|
+
"NEVER summarize or truncate the debate record when passing to subagents.",
|
|
624
|
+
"",
|
|
625
|
+
"### For you, the facilitator:",
|
|
626
|
+
"- Do NOT paste participant full text into chat. They are on the blackboard.",
|
|
627
|
+
"- Cycle through debaters in sequence (chain mode) so each sees all prior entries.",
|
|
628
|
+
"- Read the index with meeting_read_index frequently.",
|
|
629
|
+
"- Read full entries with meeting_read_entry when synthesizing.",
|
|
630
|
+
"- After EACH full cycle (all debaters spoke once), check for CONVERGENCE:",
|
|
631
|
+
" * Do 2+ agents agree on a specific conclusion?",
|
|
632
|
+
" * Did the last cycle introduce any NEW arguments?",
|
|
633
|
+
" * Did anyone explicitly concede?",
|
|
634
|
+
"- If NOT converged: run another cycle. Keep going.",
|
|
635
|
+
"- If converged: present synthesis to me.",
|
|
636
|
+
"",
|
|
637
|
+
"## Rules",
|
|
638
|
+
"- NEVER stop at a predetermined count. Only convergence or user intervention ends this debate.",
|
|
639
|
+
"- All responses in Chinese (中文).",
|
|
640
|
+
"- After convergence, save transcript.md immediately and (after user confirms) conclusion.md per the MEETING OUTPUT PROTOCOL.",
|
|
641
|
+
"- Present: (1) the debate arc, (2) who conceded what, (3) final synthesis.",
|
|
642
|
+
].join("\n");
|
|
643
|
+
}
|
|
25
644
|
|
|
26
645
|
// ────────────────────────────────────────────────────────
|
|
27
646
|
// Helpers
|
|
@@ -29,12 +648,14 @@ import { Text, Box } from "@earendil-works/pi-tui";
|
|
|
29
648
|
|
|
30
649
|
/** Sanitize a string for use in filenames (keep letters, digits, hyphens, underscores). */
|
|
31
650
|
function sanitizeFilenamePart(raw: string): string {
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
651
|
+
return (
|
|
652
|
+
raw
|
|
653
|
+
.replace(/[^a-zA-Z0-9\u4e00-\u9fff_-]/g, "_")
|
|
654
|
+
.replace(/_+/g, "_")
|
|
655
|
+
.replace(/^_|_$/g, "")
|
|
656
|
+
.slice(0, 60)
|
|
657
|
+
.toLowerCase() || "unknown"
|
|
658
|
+
);
|
|
38
659
|
}
|
|
39
660
|
|
|
40
661
|
/** Convert a topic string to a filesystem-safe slug. */
|
|
@@ -162,7 +783,11 @@ function startWatching(pi: ExtensionAPI, meetingDir: string): void {
|
|
|
162
783
|
try {
|
|
163
784
|
if (!fs.existsSync(entryPath)) return;
|
|
164
785
|
assertExistingFileNoSymlink(entryPath, "meeting entry");
|
|
165
|
-
assertPathInside(
|
|
786
|
+
assertPathInside(
|
|
787
|
+
fs.realpathSync(entriesDir),
|
|
788
|
+
fs.realpathSync(entryPath),
|
|
789
|
+
"meeting entry real path"
|
|
790
|
+
);
|
|
166
791
|
const parsed = parseEntryFilename(filename);
|
|
167
792
|
if (!parsed) return;
|
|
168
793
|
const summary = readEntrySummary(entryPath);
|
|
@@ -189,7 +814,7 @@ function startWatching(pi: ExtensionAPI, meetingDir: string): void {
|
|
|
189
814
|
);
|
|
190
815
|
});
|
|
191
816
|
|
|
192
|
-
watcher.on("error", () => {
|
|
817
|
+
(watcher as any).on?.("error", () => {
|
|
193
818
|
// Silently handle watcher errors (e.g., directory deleted)
|
|
194
819
|
});
|
|
195
820
|
|
|
@@ -221,116 +846,6 @@ interface MeetingManifest {
|
|
|
221
846
|
entryCount: number;
|
|
222
847
|
}
|
|
223
848
|
|
|
224
|
-
const BRAINSTORM_AGENT_FILES: Record<string, string> = {
|
|
225
|
-
"gpt-brainstormer.md": `---
|
|
226
|
-
name: gpt-brainstormer
|
|
227
|
-
description: GPT brainstorming consultant. Visionary strategist for multi-model discussion sessions.
|
|
228
|
-
tools: read, grep, find, ls, meeting_append_entry, meeting_read_index, meeting_read_entry
|
|
229
|
-
model: vendor-codex/gpt-5.5:xhigh
|
|
230
|
-
---
|
|
231
|
-
|
|
232
|
-
# GPT Brainstormer - Visionary Strategist
|
|
233
|
-
|
|
234
|
-
你是多模型头脑风暴中的愿景战略家。思考大局,发现别人忽略的机会,将复杂权衡综合为清晰方向。用中文回答。
|
|
235
|
-
|
|
236
|
-
## What You Do
|
|
237
|
-
- 提出创新的战略方向和解决方案
|
|
238
|
-
- 发现别人忽略的机会和盲点
|
|
239
|
-
- 把零散想法综合成连贯战略
|
|
240
|
-
|
|
241
|
-
## What You Do Not Do
|
|
242
|
-
- 写代码或修改项目文件,你只读项目文件
|
|
243
|
-
- 委派给其他 Agent
|
|
244
|
-
- 在聊天中直接粘贴长篇分析;当明确指示使用 meeting_append_entry 时,必须将完整贡献写入会议黑板,最终回复仅写 WROTE_ENTRY + 一句话摘要
|
|
245
|
-
|
|
246
|
-
## Worker Preamble
|
|
247
|
-
You are a terminal worker. Work directly with tools. Do NOT spawn sub-agents.
|
|
248
|
-
`,
|
|
249
|
-
"deepseek-brainstormer.md": `---
|
|
250
|
-
name: deepseek-brainstormer
|
|
251
|
-
description: DeepSeek brainstorming consultant. Meticulous systems thinker for multi-model discussion sessions.
|
|
252
|
-
tools: read, grep, find, ls, meeting_append_entry, meeting_read_index, meeting_read_entry
|
|
253
|
-
model: deepseek/deepseek-v4-pro:xhigh
|
|
254
|
-
---
|
|
255
|
-
|
|
256
|
-
# DeepSeek Brainstormer - Meticulous Systems Thinker
|
|
257
|
-
|
|
258
|
-
你是多模型头脑风暴中的系统思考者。分析结构、依赖、扩展上限和失败模式。用中文回答。
|
|
259
|
-
|
|
260
|
-
## What You Do
|
|
261
|
-
- 从结构、依赖和风险角度分析提案
|
|
262
|
-
- 识别隐藏耦合、扩展上限和失败模式
|
|
263
|
-
- 提出具体、可实现、可验证的技术优化方案
|
|
264
|
-
|
|
265
|
-
## What You Do Not Do
|
|
266
|
-
- 写代码或修改项目文件,你只读项目文件
|
|
267
|
-
- 委派给其他 Agent
|
|
268
|
-
- 在聊天中直接粘贴长篇分析;当明确指示使用 meeting_append_entry 时,必须将完整贡献写入会议黑板,最终回复仅写 WROTE_ENTRY + 一句话摘要
|
|
269
|
-
|
|
270
|
-
## Worker Preamble
|
|
271
|
-
You are a terminal worker. Work directly with tools. Do NOT spawn sub-agents.
|
|
272
|
-
`,
|
|
273
|
-
"minimax-brainstormer.md": `---
|
|
274
|
-
name: minimax-brainstormer
|
|
275
|
-
description: MiniMax brainstorming consultant. Creative lateral thinker for multi-model discussion sessions.
|
|
276
|
-
tools: read, grep, find, ls, meeting_append_entry, meeting_read_index, meeting_read_entry
|
|
277
|
-
model: minimax-cn/MiniMax-M3:xhigh
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
# MiniMax Brainstormer - Creative Lateral Thinker
|
|
281
|
-
|
|
282
|
-
你是多模型头脑风暴中的创意顾问。跳出框框思考,挑战隐性假设,提出非常规方案。用中文回答。
|
|
283
|
-
|
|
284
|
-
## What You Do
|
|
285
|
-
- 从意想不到的角度切入问题
|
|
286
|
-
- 提出打破常规的创新方案
|
|
287
|
-
- 挑战团队隐性假设
|
|
288
|
-
|
|
289
|
-
## What You Do Not Do
|
|
290
|
-
- 写代码或修改项目文件,你只读项目文件
|
|
291
|
-
- 委派给其他 Agent
|
|
292
|
-
- 在聊天中直接粘贴长篇分析;当明确指示使用 meeting_append_entry 时,必须将完整贡献写入会议黑板,最终回复仅写 WROTE_ENTRY + 一句话摘要
|
|
293
|
-
|
|
294
|
-
## Worker Preamble
|
|
295
|
-
You are a terminal worker. Work directly with tools. Do NOT spawn sub-agents.
|
|
296
|
-
`,
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
async function ensureBrainstormAgents(ctx: any): Promise<boolean> {
|
|
300
|
-
const agentsDir = path.join(getAgentDir(), "agents");
|
|
301
|
-
const missing = Object.keys(BRAINSTORM_AGENT_FILES).filter(
|
|
302
|
-
(filename) => !fs.existsSync(path.join(agentsDir, filename))
|
|
303
|
-
);
|
|
304
|
-
if (missing.length === 0) return true;
|
|
305
|
-
|
|
306
|
-
if (!ctx.hasUI) {
|
|
307
|
-
ctx.ui?.notify?.(
|
|
308
|
-
`Missing meeting agents: ${missing.join(", ")}. Install them under ${agentsDir}.`,
|
|
309
|
-
"warning"
|
|
310
|
-
);
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const ok = await ctx.ui.confirm(
|
|
315
|
-
"Install meeting brainstorm agents?",
|
|
316
|
-
`The blackboard meeting commands need these user-level agents:\n${missing
|
|
317
|
-
.map((name) => `- ${name}`)
|
|
318
|
-
.join("\n")}\n\nThey will be created under ${agentsDir}. Existing files are not overwritten.`
|
|
319
|
-
);
|
|
320
|
-
if (!ok) return false;
|
|
321
|
-
|
|
322
|
-
await fsp.mkdir(agentsDir, { recursive: true });
|
|
323
|
-
for (const filename of missing) {
|
|
324
|
-
const target = path.join(agentsDir, filename);
|
|
325
|
-
await fsp.writeFile(target, BRAINSTORM_AGENT_FILES[filename], {
|
|
326
|
-
encoding: "utf-8",
|
|
327
|
-
flag: "wx",
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
ctx.ui.notify(`Installed ${missing.length} meeting agent(s).`, "info");
|
|
331
|
-
return true;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
849
|
async function readManifest(absDir: string): Promise<MeetingManifest | null> {
|
|
335
850
|
const manifestPath = path.join(absDir, "manifest.json");
|
|
336
851
|
try {
|
|
@@ -428,7 +943,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
428
943
|
// Read or create manifest
|
|
429
944
|
let manifest = await readManifest(absDir);
|
|
430
945
|
if (!manifest) {
|
|
431
|
-
// Should not normally happen — commands seed the manifest
|
|
432
946
|
manifest = {
|
|
433
947
|
topic: path.basename(absDir),
|
|
434
948
|
created: new Date().toISOString(),
|
|
@@ -620,7 +1134,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
620
1134
|
|
|
621
1135
|
try {
|
|
622
1136
|
assertExistingFileNoSymlink(absEntryPath, "meeting entry");
|
|
623
|
-
assertPathInside(
|
|
1137
|
+
assertPathInside(
|
|
1138
|
+
fs.realpathSync(absDir),
|
|
1139
|
+
fs.realpathSync(absEntryPath),
|
|
1140
|
+
"meeting entry real path"
|
|
1141
|
+
);
|
|
624
1142
|
const content = await fsp.readFile(absEntryPath, "utf-8");
|
|
625
1143
|
return {
|
|
626
1144
|
content: [
|
|
@@ -682,7 +1200,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
682
1200
|
try {
|
|
683
1201
|
assertExistingFileNoSymlink(details.path, "meeting entry");
|
|
684
1202
|
if (details.meetingDir) {
|
|
685
|
-
assertPathInside(
|
|
1203
|
+
assertPathInside(
|
|
1204
|
+
fs.realpathSync(details.meetingDir),
|
|
1205
|
+
fs.realpathSync(details.path),
|
|
1206
|
+
"meeting entry real path"
|
|
1207
|
+
);
|
|
686
1208
|
}
|
|
687
1209
|
const fullContent = fs.readFileSync(details.path, "utf-8");
|
|
688
1210
|
text += `\n\n${theme.fg("dim", fullContent)}`;
|
|
@@ -708,7 +1230,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
708
1230
|
return;
|
|
709
1231
|
}
|
|
710
1232
|
|
|
711
|
-
|
|
1233
|
+
// Resolve participants from config
|
|
1234
|
+
let participants: ParticipantConfig[];
|
|
1235
|
+
try {
|
|
1236
|
+
participants = resolveParticipants(ctx.cwd);
|
|
1237
|
+
} catch (err: any) {
|
|
1238
|
+
ctx.ui.notify(
|
|
1239
|
+
`pi-brainstorm config error: ${err.message}`,
|
|
1240
|
+
"error"
|
|
1241
|
+
);
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
const agentsReady = await ensureAgentsFromConfig(ctx, participants, {
|
|
1246
|
+
allowGlobalWrites: !hasProjectConfig(ctx.cwd),
|
|
1247
|
+
});
|
|
712
1248
|
if (!agentsReady) return;
|
|
713
1249
|
|
|
714
1250
|
const topic = args.trim();
|
|
@@ -723,7 +1259,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
723
1259
|
// Create meeting folder structure
|
|
724
1260
|
await fsp.mkdir(path.join(absDir, "entries"), { recursive: true });
|
|
725
1261
|
assertDirectoryNoSymlink(absDir, "meeting directory");
|
|
726
|
-
assertDirectoryNoSymlink(
|
|
1262
|
+
assertDirectoryNoSymlink(
|
|
1263
|
+
path.join(absDir, "entries"),
|
|
1264
|
+
"entries directory"
|
|
1265
|
+
);
|
|
727
1266
|
|
|
728
1267
|
// Seed manifest
|
|
729
1268
|
const manifest: MeetingManifest = {
|
|
@@ -765,54 +1304,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
765
1304
|
);
|
|
766
1305
|
|
|
767
1306
|
// Send orchestration prompt to main agent
|
|
1307
|
+
const promptText = buildBrainstormPrompt(topic, absDir, participants);
|
|
768
1308
|
pi.sendUserMessage([
|
|
769
1309
|
{
|
|
770
1310
|
type: "text" as const,
|
|
771
|
-
text:
|
|
772
|
-
`BLACKBOARD BRAINSTORMING SESSION: ${topic}`,
|
|
773
|
-
"",
|
|
774
|
-
`Meeting folder: \`${absDir}\``,
|
|
775
|
-
"",
|
|
776
|
-
"You are facilitating a round-robin brainstorming session using the MEETING BLACKBOARD.",
|
|
777
|
-
"Each consultant writes their FULL contribution to disk via meeting_append_entry.",
|
|
778
|
-
"",
|
|
779
|
-
"## Consultants (3 rounds)",
|
|
780
|
-
"- **GPT**: use the gpt-brainstormer subagent. Visionary strategist.",
|
|
781
|
-
"- **DeepSeek**: use the deepseek-brainstormer subagent. Systems thinker.",
|
|
782
|
-
"- **MiniMax**: use the minimax-brainstormer subagent. Creative lateral thinker.",
|
|
783
|
-
"",
|
|
784
|
-
"## CRITICAL INSTRUCTIONS",
|
|
785
|
-
"",
|
|
786
|
-
"### For subagents (include in EVERY task):",
|
|
787
|
-
"1. Write your FULL contribution using the meeting_append_entry tool with:",
|
|
788
|
-
` - meetingDir: "${absDir}"`,
|
|
789
|
-
" - speaker: your name (GPT, DeepSeek, or MiniMax)",
|
|
790
|
-
' - phase: "Round 1", "Round 2", or "Round 3"',
|
|
791
|
-
" - summary: a ONE-SENTENCE summary of your contribution",
|
|
792
|
-
" - content: your FULL analysis in Chinese (中文)",
|
|
793
|
-
"2. After writing, your FINAL ANSWER must be ONLY:",
|
|
794
|
-
" `WROTE_ENTRY: <your one-sentence summary>`",
|
|
795
|
-
"3. DO NOT paste your full analysis into the chat. The main agent and user will read it from the blackboard.",
|
|
796
|
-
"",
|
|
797
|
-
"### For you, the facilitator:",
|
|
798
|
-
"- Do NOT paste participant full text into chat. They are on the blackboard.",
|
|
799
|
-
"- After each round, read the index with meeting_read_index and present a structural overview.",
|
|
800
|
-
"- Optionally read full entries with meeting_read_entry when needed.",
|
|
801
|
-
"- Present each consultant's summary + your structural overview (conflict matrix, consensus table).",
|
|
802
|
-
"- When the user gives feedback, relay it VERBATIM to the consultants in the next round.",
|
|
803
|
-
"",
|
|
804
|
-
"## Protocol",
|
|
805
|
-
"Round 1: Each consultant gives initial analysis on the topic. Run all 3 in parallel.",
|
|
806
|
-
"Round 2: Feed prior discussion back to each. Ask each to challenge the others and propose improvements.",
|
|
807
|
-
"Round 3: Each gives FINAL recommendation, synthesizing the best ideas.",
|
|
808
|
-
"",
|
|
809
|
-
"After Round 3, present the complete structural overview and a synthesized conclusion.",
|
|
810
|
-
"",
|
|
811
|
-
"## IMPORTANT",
|
|
812
|
-
"- All responses in Chinese (中文).",
|
|
813
|
-
"- Save transcript.md and (after user confirms) conclusion.md per the MEETING OUTPUT PROTOCOL.",
|
|
814
|
-
"- The user can intervene at any time to steer the discussion.",
|
|
815
|
-
].join("\n"),
|
|
1311
|
+
text: promptText,
|
|
816
1312
|
},
|
|
817
1313
|
]);
|
|
818
1314
|
},
|
|
@@ -829,7 +1325,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
829
1325
|
return;
|
|
830
1326
|
}
|
|
831
1327
|
|
|
832
|
-
|
|
1328
|
+
// Resolve participants from config
|
|
1329
|
+
let participants: ParticipantConfig[];
|
|
1330
|
+
try {
|
|
1331
|
+
participants = resolveParticipants(ctx.cwd);
|
|
1332
|
+
} catch (err: any) {
|
|
1333
|
+
ctx.ui.notify(
|
|
1334
|
+
`pi-brainstorm config error: ${err.message}`,
|
|
1335
|
+
"error"
|
|
1336
|
+
);
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
const agentsReady = await ensureAgentsFromConfig(ctx, participants, {
|
|
1341
|
+
allowGlobalWrites: !hasProjectConfig(ctx.cwd),
|
|
1342
|
+
});
|
|
833
1343
|
if (!agentsReady) return;
|
|
834
1344
|
|
|
835
1345
|
const topic = args.trim();
|
|
@@ -844,7 +1354,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
844
1354
|
// Create meeting folder structure
|
|
845
1355
|
await fsp.mkdir(path.join(absDir, "entries"), { recursive: true });
|
|
846
1356
|
assertDirectoryNoSymlink(absDir, "meeting directory");
|
|
847
|
-
assertDirectoryNoSymlink(
|
|
1357
|
+
assertDirectoryNoSymlink(
|
|
1358
|
+
path.join(absDir, "entries"),
|
|
1359
|
+
"entries directory"
|
|
1360
|
+
);
|
|
848
1361
|
|
|
849
1362
|
// Seed manifest
|
|
850
1363
|
const manifest: MeetingManifest = {
|
|
@@ -887,58 +1400,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
887
1400
|
);
|
|
888
1401
|
|
|
889
1402
|
// Send orchestration prompt to main agent
|
|
1403
|
+
const promptText = buildDebatePrompt(topic, absDir, participants);
|
|
890
1404
|
pi.sendUserMessage([
|
|
891
1405
|
{
|
|
892
1406
|
type: "text" as const,
|
|
893
|
-
text:
|
|
894
|
-
`⚔️ BLACKBOARD DEBATE: ${topic}`,
|
|
895
|
-
"",
|
|
896
|
-
`Meeting folder: \`${absDir}\``,
|
|
897
|
-
"",
|
|
898
|
-
"You are facilitating an OPEN-ENDED debate using the MEETING BLACKBOARD.",
|
|
899
|
-
"Each debater writes their FULL argument to disk via meeting_append_entry.",
|
|
900
|
-
"Continue until the debate CONVERGES or the user intervenes.",
|
|
901
|
-
"",
|
|
902
|
-
"## Debaters (cycling indefinitely)",
|
|
903
|
-
"- **GPT** (gpt-brainstormer): THE PROSECUTOR — Attack other positions ruthlessly. Find every logical flaw, hidden assumption, and missing edge case.",
|
|
904
|
-
"- **DeepSeek** (deepseek-brainstormer): THE SYSTEMS SKEPTIC — Dissect structural implications. What breaks at scale? Where are the hidden costs?",
|
|
905
|
-
"- **MiniMax** (minimax-brainstormer): THE CONTRARIAN — Take the opposite position. Expose groupthink. Propose radical alternatives.",
|
|
906
|
-
"",
|
|
907
|
-
"## CRITICAL INSTRUCTIONS",
|
|
908
|
-
"",
|
|
909
|
-
"### For subagents (include in EVERY task):",
|
|
910
|
-
"1. Write your FULL contribution using the meeting_append_entry tool with:",
|
|
911
|
-
` - meetingDir: "${absDir}"`,
|
|
912
|
-
" - speaker: your name (GPT, DeepSeek, or MiniMax)",
|
|
913
|
-
' - phase: "Cycle 1", "Cycle 2", etc.',
|
|
914
|
-
" - summary: a ONE-SENTENCE summary of your argument",
|
|
915
|
-
" - content: your FULL argument in Chinese (中文)",
|
|
916
|
-
"2. After writing, your FINAL ANSWER must be ONLY:",
|
|
917
|
-
" `WROTE_ENTRY: <your one-sentence summary>`",
|
|
918
|
-
"3. DO NOT paste your full argument into the chat.",
|
|
919
|
-
"",
|
|
920
|
-
"### Include the FULL VERBATIM prior debate record in each subagent task.",
|
|
921
|
-
"Use meeting_read_index and meeting_read_entry to retrieve the complete debate history.",
|
|
922
|
-
"NEVER summarize or truncate the debate record when passing to subagents.",
|
|
923
|
-
"",
|
|
924
|
-
"### For you, the facilitator:",
|
|
925
|
-
"- Do NOT paste participant full text into chat. They are on the blackboard.",
|
|
926
|
-
"- Run debaters in CHAIN mode (one at a time, each sees all prior entries).",
|
|
927
|
-
"- Read the index with meeting_read_index frequently.",
|
|
928
|
-
"- Read full entries with meeting_read_entry when synthesizing.",
|
|
929
|
-
"- After EACH full cycle (all 3 spoke), check for CONVERGENCE:",
|
|
930
|
-
" * Do 2+ agents agree on a specific conclusion?",
|
|
931
|
-
" * Did the last cycle introduce any NEW arguments?",
|
|
932
|
-
" * Did anyone explicitly concede?",
|
|
933
|
-
"- If NOT converged: run another cycle. Keep going.",
|
|
934
|
-
"- If converged: present synthesis to me.",
|
|
935
|
-
"",
|
|
936
|
-
"## Rules",
|
|
937
|
-
"- NEVER stop at a predetermined count. Only convergence or user intervention ends this debate.",
|
|
938
|
-
"- All responses in Chinese (中文).",
|
|
939
|
-
"- After convergence, save transcript.md immediately and (after user confirms) conclusion.md per the MEETING OUTPUT PROTOCOL.",
|
|
940
|
-
"- Present: (1) the debate arc, (2) who conceded what, (3) final synthesis.",
|
|
941
|
-
].join("\n"),
|
|
1407
|
+
text: promptText,
|
|
942
1408
|
},
|
|
943
1409
|
]);
|
|
944
1410
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-brainstorm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Multi-model brainstorming and debate sessions for pi subagents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -26,10 +26,14 @@
|
|
|
26
26
|
"homepage": "https://github.com/Jarcis-cy/pi-brainstorm#readme",
|
|
27
27
|
"files": [
|
|
28
28
|
"extensions",
|
|
29
|
+
"config",
|
|
29
30
|
"README.md",
|
|
30
31
|
"README.zh-CN.md",
|
|
31
32
|
"LICENSE"
|
|
32
33
|
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"yaml": "^2.7.0"
|
|
36
|
+
},
|
|
33
37
|
"peerDependencies": {
|
|
34
38
|
"@earendil-works/pi-coding-agent": "*",
|
|
35
39
|
"@earendil-works/pi-tui": "*",
|