jinzd-ai-cli 0.3.1 → 0.3.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.
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hub/agent-client.ts
4
+ import WebSocket from "ws";
5
+ import chalk from "chalk";
6
+ import { createInterface } from "readline";
7
+ var PASS_MARKER = "[PASS]";
8
+ async function joinHub(options, configManager, providers) {
9
+ const host = options.host ?? "localhost";
10
+ const url = `ws://${host}:${options.port}`;
11
+ const isHuman = options.human === true;
12
+ let defaultProvider = "";
13
+ let defaultModel = "";
14
+ if (!isHuman) {
15
+ defaultProvider = options.provider ?? configManager.get("defaultProvider");
16
+ const allDefaultModels = configManager.get("defaultModels");
17
+ defaultModel = options.model ?? allDefaultModels[defaultProvider] ?? "";
18
+ if (!defaultModel) {
19
+ try {
20
+ const p = providers.get(defaultProvider);
21
+ defaultModel = p.info.defaultModel;
22
+ } catch {
23
+ console.error(` \u2717 Provider "${defaultProvider}" not configured.`);
24
+ process.exit(1);
25
+ }
26
+ }
27
+ }
28
+ let rl = null;
29
+ if (isHuman) {
30
+ rl = createInterface({ input: process.stdin, output: process.stdout });
31
+ }
32
+ console.log();
33
+ console.log(chalk.bold.cyan(isHuman ? " \u{1F464} AI-CLI Hub \u2014 Human Participant" : " \u{1F916} AI-CLI Agent \u2014 Connecting to Hub"));
34
+ console.log(chalk.dim(` Server: ${url}`));
35
+ if (options.role) console.log(chalk.dim(` Requested role: ${options.role}`));
36
+ if (isHuman) {
37
+ console.log(chalk.dim(" Mode: HUMAN (you type your responses)"));
38
+ console.log(chalk.dim(" Tip: type [PASS] to skip your turn, empty line to send"));
39
+ } else {
40
+ console.log(chalk.dim(` Provider: ${defaultProvider} / ${defaultModel}`));
41
+ }
42
+ console.log();
43
+ const ws = new WebSocket(url);
44
+ let myRoleId = "";
45
+ let myRoleName = "";
46
+ let topic = "";
47
+ let allRoles = [];
48
+ let persona = "";
49
+ ws.on("open", () => {
50
+ console.log(chalk.green(" \u2713 Connected to Hub"));
51
+ const msg = { type: "register", roleId: options.role ?? "" };
52
+ ws.send(JSON.stringify(msg));
53
+ });
54
+ ws.on("close", () => {
55
+ if (rl) rl.close();
56
+ console.log(chalk.dim("\n Disconnected from Hub."));
57
+ process.exit(0);
58
+ });
59
+ ws.on("error", (err) => {
60
+ console.error(chalk.red(`
61
+ \u2717 Connection error: ${err.message}`));
62
+ process.exit(1);
63
+ });
64
+ ws.on("message", async (raw) => {
65
+ try {
66
+ const msg = JSON.parse(raw.toString());
67
+ await handleMessage(msg);
68
+ } catch (err) {
69
+ console.error(chalk.red(` \u2717 Message error: ${err.message}`));
70
+ }
71
+ });
72
+ async function handleMessage(msg) {
73
+ switch (msg.type) {
74
+ case "welcome":
75
+ myRoleId = msg.roleId;
76
+ myRoleName = msg.roleName;
77
+ topic = msg.topic;
78
+ allRoles = msg.roles;
79
+ persona = buildPersonaFromRole(myRoleId, myRoleName);
80
+ console.log(chalk.bold(` \u{1F4CB} Assigned role: ${chalk.cyan(myRoleName)} (${myRoleId})`));
81
+ if (topic) {
82
+ console.log(chalk.dim(` Topic: ${topic}`));
83
+ }
84
+ console.log(chalk.dim(` Participants: ${allRoles.map((r) => r.name).join(", ")}`));
85
+ console.log(chalk.dim(" Waiting for discussion to start...\n"));
86
+ break;
87
+ case "round_start":
88
+ console.log(chalk.dim(` \u2500\u2500 Round ${msg.round}/${msg.maxRounds} ${"\u2500".repeat(40)}
89
+ `));
90
+ break;
91
+ case "your_turn": {
92
+ let response;
93
+ if (isHuman) {
94
+ console.log(chalk.bold.green(`
95
+ \u270F\uFE0F Your turn to speak, ${myRoleName}! (Round ${msg.round}/${msg.maxRounds})`));
96
+ console.log(chalk.dim(" Type your response (multi-line: end with empty line). [PASS] to skip.\n"));
97
+ response = await readMultiLineInput(rl);
98
+ } else {
99
+ console.log(chalk.bold.cyan(` \u{1F4AD} Your turn to speak (Round ${msg.round}/${msg.maxRounds})...`));
100
+ try {
101
+ response = await generateResponse(
102
+ topic,
103
+ msg.history,
104
+ msg.round,
105
+ msg.maxRounds,
106
+ myRoleId,
107
+ myRoleName,
108
+ persona,
109
+ providers,
110
+ defaultProvider,
111
+ defaultModel
112
+ );
113
+ } catch (err) {
114
+ console.error(chalk.red(` \u2717 Response error: ${err.message}`));
115
+ const errReply = { type: "error", message: err.message };
116
+ ws.send(JSON.stringify(errReply));
117
+ break;
118
+ }
119
+ }
120
+ const passed = response.trim().toUpperCase() === PASS_MARKER || response.trim() === "";
121
+ if (passed) {
122
+ console.log(chalk.dim(" [PASS \u2014 nothing to add]\n"));
123
+ } else if (!isHuman) {
124
+ console.log(chalk.cyan(` \u250C\u2500 ${myRoleName} (you)`));
125
+ for (const line of response.split("\n")) {
126
+ console.log(chalk.cyan(" \u2502 ") + line);
127
+ }
128
+ console.log(chalk.cyan(" \u2514" + "\u2500".repeat(50)));
129
+ console.log();
130
+ }
131
+ const reply = {
132
+ type: "response",
133
+ content: passed ? "" : response.trim(),
134
+ passed
135
+ };
136
+ ws.send(JSON.stringify(reply));
137
+ break;
138
+ }
139
+ case "agent_spoke": {
140
+ const m = msg.message;
141
+ if (m.speaker === myRoleId) break;
142
+ if (m.passed) {
143
+ console.log(chalk.dim(` ${m.speakerName}: [PASS]
144
+ `));
145
+ } else {
146
+ console.log(chalk.magenta(` \u250C\u2500 ${m.speakerName} (${m.speaker})`));
147
+ for (const line of m.content.split("\n")) {
148
+ console.log(chalk.magenta(" \u2502 ") + line);
149
+ }
150
+ console.log(chalk.magenta(" \u2514" + "\u2500".repeat(50)));
151
+ console.log();
152
+ }
153
+ break;
154
+ }
155
+ case "discussion_end":
156
+ console.log();
157
+ if (msg.reason === "consensus") {
158
+ console.log(chalk.green.bold(" \u2713 Consensus reached \u2014 discussion ended."));
159
+ } else if (msg.reason === "max_rounds") {
160
+ console.log(chalk.yellow(" \u26A0 Maximum rounds reached \u2014 discussion ended."));
161
+ } else {
162
+ console.log(chalk.yellow(" \u26A0 Discussion interrupted by hub operator."));
163
+ }
164
+ if (msg.summary) {
165
+ console.log();
166
+ console.log(chalk.bold(" \u{1F4CB} Summary:"));
167
+ for (const line of msg.summary.split("\n")) {
168
+ console.log(" " + line);
169
+ }
170
+ }
171
+ console.log();
172
+ break;
173
+ case "waiting":
174
+ console.log(chalk.dim(` ${msg.message}`));
175
+ break;
176
+ case "error":
177
+ console.error(chalk.red(` \u2717 Hub error: ${msg.message}`));
178
+ break;
179
+ }
180
+ }
181
+ await new Promise(() => {
182
+ });
183
+ }
184
+ async function generateResponse(topic, history, round, maxRounds, roleId, roleName, persona, providers, providerId, modelId) {
185
+ const provider = providers.get(providerId);
186
+ if (!provider) throw new Error(`Provider "${providerId}" not available`);
187
+ const systemPrompt = `# Multi-Agent Discussion \u2014 Role: ${roleName}
188
+
189
+ ${persona}
190
+
191
+ ## Discussion Rules
192
+ - You are participating in a multi-agent discussion about the topic below.
193
+ - You can see what other participants have said. Build on their ideas, challenge them, or add your own perspective.
194
+ - Stay in character as ${roleName}. Respond from your role's expertise and viewpoint.
195
+ - Keep responses concise and focused (2-6 paragraphs). Do not repeat what others have already said.
196
+ - If you have nothing meaningful to add (others have covered your points), respond with exactly: [PASS]
197
+ - This is round ${round} of ${maxRounds}. ${round >= maxRounds - 1 ? "This is one of the final rounds \u2014 try to converge on conclusions." : ""}
198
+ - Use the language that the topic is written in (if the topic is in Chinese, respond in Chinese).
199
+
200
+ ## Topic
201
+ ${topic}`;
202
+ const messages = [];
203
+ for (const msg of history) {
204
+ if (msg.passed) continue;
205
+ if (msg.speaker === roleId) {
206
+ messages.push({ role: "assistant", content: msg.content, timestamp: new Date(msg.timestamp) });
207
+ } else {
208
+ messages.push({ role: "user", content: `[${msg.speakerName} (${msg.speaker})]:
209
+ ${msg.content}`, timestamp: new Date(msg.timestamp) });
210
+ }
211
+ }
212
+ if (messages.length === 0) {
213
+ messages.push({ role: "user", content: "Please share your initial thoughts on the topic. You are the first to speak.", timestamp: /* @__PURE__ */ new Date() });
214
+ } else {
215
+ messages.push({ role: "user", content: "It is now your turn. Consider what others have said and share your perspective. If you have nothing to add, respond with [PASS].", timestamp: /* @__PURE__ */ new Date() });
216
+ }
217
+ const response = await provider.chat({
218
+ messages,
219
+ model: modelId,
220
+ systemPrompt,
221
+ stream: false,
222
+ temperature: 0.7,
223
+ maxTokens: 4096
224
+ });
225
+ return response.content.trim();
226
+ }
227
+ function buildPersonaFromRole(roleId, roleName) {
228
+ return `You are ${roleName} (${roleId}). You are an expert in your domain.
229
+ Respond thoughtfully based on your area of expertise.
230
+ Be constructive, collaborative, and open to other perspectives while maintaining your professional viewpoint.`;
231
+ }
232
+ function readMultiLineInput(rl) {
233
+ return new Promise((resolve) => {
234
+ const lines = [];
235
+ let lastLineEmpty = false;
236
+ const prompt = () => {
237
+ rl.question(chalk.green(" > "), (line) => {
238
+ if (line === "" && lines.length > 0) {
239
+ if (lastLineEmpty) {
240
+ resolve(lines.join("\n"));
241
+ return;
242
+ }
243
+ lastLineEmpty = true;
244
+ lines.push("");
245
+ prompt();
246
+ return;
247
+ }
248
+ if (line === "" && lines.length === 0) {
249
+ resolve(PASS_MARKER);
250
+ return;
251
+ }
252
+ if (line.trim().toUpperCase() === PASS_MARKER && lines.length === 0) {
253
+ resolve(PASS_MARKER);
254
+ return;
255
+ }
256
+ lastLineEmpty = false;
257
+ lines.push(line);
258
+ prompt();
259
+ });
260
+ };
261
+ prompt();
262
+ });
263
+ }
264
+ export {
265
+ joinHub
266
+ };
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hub/agent.ts
4
+ var PASS_MARKER = "[PASS]";
5
+ var HubAgent = class {
6
+ role;
7
+ providers;
8
+ defaultProvider;
9
+ defaultModel;
10
+ constructor(role, providers, defaultProvider, defaultModel) {
11
+ this.role = role;
12
+ this.providers = providers;
13
+ this.defaultProvider = defaultProvider;
14
+ this.defaultModel = defaultModel;
15
+ }
16
+ get providerId() {
17
+ return this.role.provider ?? this.defaultProvider;
18
+ }
19
+ get modelId() {
20
+ return this.role.model ?? this.defaultModel;
21
+ }
22
+ /**
23
+ * Generate this agent's response given the full discussion history.
24
+ *
25
+ * Returns a DiscussionMessage, with `passed: true` if the agent outputs [PASS].
26
+ */
27
+ async speak(topic, history, round, maxRounds) {
28
+ const provider = this.providers.get(this.providerId);
29
+ if (!provider) {
30
+ throw new Error(`Provider "${this.providerId}" not available for agent "${this.role.id}"`);
31
+ }
32
+ const systemPrompt = this.buildSystemPrompt(topic, round, maxRounds);
33
+ const messages = this.buildMessages(history);
34
+ const response = await provider.chat({
35
+ messages,
36
+ model: this.modelId,
37
+ systemPrompt,
38
+ stream: false,
39
+ temperature: 0.7,
40
+ maxTokens: 4096
41
+ });
42
+ const content = response.content.trim();
43
+ const passed = content.toUpperCase().startsWith(PASS_MARKER) || content.toUpperCase() === PASS_MARKER;
44
+ return {
45
+ speaker: this.role.id,
46
+ speakerName: this.role.name,
47
+ content: passed ? "" : content,
48
+ timestamp: /* @__PURE__ */ new Date(),
49
+ passed
50
+ };
51
+ }
52
+ /**
53
+ * Generate a summary of the entire discussion from this agent's perspective.
54
+ */
55
+ async summarize(topic, history) {
56
+ const provider = this.providers.get(this.providerId);
57
+ if (!provider) {
58
+ throw new Error(`Provider "${this.providerId}" not available`);
59
+ }
60
+ const messages = [
61
+ {
62
+ role: "user",
63
+ content: this.buildSummaryPrompt(topic, history),
64
+ timestamp: /* @__PURE__ */ new Date()
65
+ }
66
+ ];
67
+ const response = await provider.chat({
68
+ messages,
69
+ model: this.modelId,
70
+ stream: false,
71
+ temperature: 0.3,
72
+ maxTokens: 4096
73
+ });
74
+ return response.content.trim();
75
+ }
76
+ // ── Private ──────────────────────────────────────────────────────
77
+ buildSystemPrompt(topic, round, maxRounds) {
78
+ return `# Multi-Agent Discussion \u2014 Role: ${this.role.name}
79
+
80
+ ${this.role.persona}
81
+
82
+ ## Discussion Rules
83
+ - You are participating in a multi-agent discussion about the topic below.
84
+ - You can see what other participants have said. Build on their ideas, challenge them, or add your own perspective.
85
+ - Stay in character as ${this.role.name}. Respond from your role's expertise and viewpoint.
86
+ - Keep responses concise and focused (2-6 paragraphs). Do not repeat what others have already said.
87
+ - If you have nothing meaningful to add (others have covered your points), respond with exactly: [PASS]
88
+ - This is round ${round} of ${maxRounds}. ${round >= maxRounds - 1 ? "This is one of the final rounds \u2014 try to converge on conclusions." : ""}
89
+ - Use the language that the topic is written in (if the topic is in Chinese, respond in Chinese).
90
+
91
+ ## Topic
92
+ ${topic}`;
93
+ }
94
+ buildMessages(history) {
95
+ const messages = [];
96
+ for (const msg of history) {
97
+ if (msg.passed) continue;
98
+ if (msg.speaker === this.role.id) {
99
+ messages.push({
100
+ role: "assistant",
101
+ content: msg.content,
102
+ timestamp: msg.timestamp
103
+ });
104
+ } else {
105
+ const prefix = `[${msg.speakerName} (${msg.speaker})]:`;
106
+ messages.push({
107
+ role: "user",
108
+ content: `${prefix}
109
+ ${msg.content}`,
110
+ timestamp: msg.timestamp
111
+ });
112
+ }
113
+ }
114
+ if (messages.length === 0) {
115
+ messages.push({
116
+ role: "user",
117
+ content: "Please share your initial thoughts on the topic described in the system prompt. You are the first to speak.",
118
+ timestamp: /* @__PURE__ */ new Date()
119
+ });
120
+ } else {
121
+ messages.push({
122
+ role: "user",
123
+ content: "It is now your turn to respond. Consider what the other participants have said and share your perspective. If you have nothing to add, respond with [PASS].",
124
+ timestamp: /* @__PURE__ */ new Date()
125
+ });
126
+ }
127
+ return messages;
128
+ }
129
+ buildSummaryPrompt(topic, history) {
130
+ const transcript = history.filter((m) => !m.passed).map((m) => `**${m.speakerName}** (${m.speaker}):
131
+ ${m.content}`).join("\n\n---\n\n");
132
+ return `Please synthesize the following multi-agent discussion into a clear, structured summary.
133
+
134
+ ## Discussion Topic
135
+ ${topic}
136
+
137
+ ## Full Transcript
138
+ ${transcript}
139
+
140
+ ## Instructions
141
+ Produce a summary that includes:
142
+ 1. **Key Points of Agreement** \u2014 what all participants agreed on
143
+ 2. **Points of Debate** \u2014 where participants disagreed and the different perspectives
144
+ 3. **Conclusions & Recommendations** \u2014 actionable takeaways
145
+ 4. **Open Questions** \u2014 unresolved issues that need further discussion
146
+
147
+ Keep the summary concise (within 500 words). Use the same language as the discussion.`;
148
+ }
149
+ };
150
+
151
+ // src/hub/renderer.ts
152
+ import chalk from "chalk";
153
+ var ROLE_COLORS = [
154
+ chalk.cyan,
155
+ chalk.magenta,
156
+ chalk.yellow,
157
+ chalk.green,
158
+ chalk.blue,
159
+ chalk.red,
160
+ chalk.rgb(255, 165, 0),
161
+ // orange
162
+ chalk.rgb(148, 103, 189)
163
+ // purple
164
+ ];
165
+ var colorMap = /* @__PURE__ */ new Map();
166
+ function assignRoleColors(roles) {
167
+ colorMap.clear();
168
+ roles.forEach((role, i) => {
169
+ if (role.color && chalk[role.color]) {
170
+ colorMap.set(role.id, chalk[role.color]);
171
+ } else {
172
+ colorMap.set(role.id, ROLE_COLORS[i % ROLE_COLORS.length]);
173
+ }
174
+ });
175
+ }
176
+ function renderHubBanner(topic, roles, maxRounds) {
177
+ console.log();
178
+ console.log(chalk.bold.white(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
179
+ console.log(chalk.bold.white(" \u2551") + chalk.bold.cyan(" \u{1F3DB}\uFE0F AI-CLI Multi-Agent Hub \u2014 Discussion Mode ") + chalk.bold.white("\u2551"));
180
+ console.log(chalk.bold.white(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
181
+ console.log(chalk.bold.white(" \u2551") + chalk.dim(` Topic: ${truncate(topic, 52)}`.padEnd(62)) + chalk.bold.white("\u2551"));
182
+ console.log(chalk.bold.white(" \u2551") + chalk.dim(` Rounds: up to ${maxRounds}`.padEnd(62)) + chalk.bold.white("\u2551"));
183
+ console.log(chalk.bold.white(" \u2551") + chalk.dim(" Participants:".padEnd(62)) + chalk.bold.white("\u2551"));
184
+ for (const role of roles) {
185
+ const color = colorMap.get(role.id) ?? chalk.white;
186
+ const line = ` ${color("\u25CF")} ${color.bold(role.name)} ${chalk.dim(`(${role.id})`)}`;
187
+ console.log(chalk.bold.white(" \u2551") + line + " ".repeat(Math.max(0, 62 - stripAnsi(line).length)) + chalk.bold.white("\u2551"));
188
+ }
189
+ console.log(chalk.bold.white(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
190
+ console.log(chalk.bold.white(" \u2551") + chalk.dim(" Press Ctrl+C to interrupt and generate summary".padEnd(62)) + chalk.bold.white("\u2551"));
191
+ console.log(chalk.bold.white(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
192
+ console.log();
193
+ }
194
+ function renderRoundHeader(round, maxRounds) {
195
+ console.log(chalk.dim(` \u2500\u2500 Round ${round}/${maxRounds} ${"\u2500".repeat(48)}`));
196
+ console.log();
197
+ }
198
+ function renderAgentSpeaking(roleName, roleId) {
199
+ const color = colorMap.get(roleId) ?? chalk.white;
200
+ process.stdout.write(chalk.dim(" \u{1F4AD} ") + color.bold(roleName) + chalk.dim(" is thinking..."));
201
+ }
202
+ function clearSpeakingLine() {
203
+ process.stdout.write("\r\x1B[2K");
204
+ }
205
+ function renderAgentMessage(msg) {
206
+ const color = colorMap.get(msg.speaker) ?? chalk.white;
207
+ if (msg.passed) {
208
+ console.log(chalk.dim(` ${msg.speakerName}: [PASS \u2014 nothing to add]`));
209
+ console.log();
210
+ return;
211
+ }
212
+ console.log(color.bold(` \u250C\u2500 ${msg.speakerName} `) + chalk.dim(`(${msg.speaker})`));
213
+ const lines = msg.content.split("\n");
214
+ for (const line of lines) {
215
+ console.log(color(" \u2502 ") + line);
216
+ }
217
+ console.log(color(" \u2514" + "\u2500".repeat(60)));
218
+ console.log();
219
+ }
220
+ function renderConsensus() {
221
+ console.log();
222
+ console.log(chalk.green.bold(" \u2713 All agents passed \u2014 consensus reached."));
223
+ console.log();
224
+ }
225
+ function renderMaxRounds(maxRounds) {
226
+ console.log();
227
+ console.log(chalk.yellow(` \u26A0 Maximum rounds (${maxRounds}) reached.`));
228
+ console.log();
229
+ }
230
+ function renderUserInterrupt() {
231
+ console.log();
232
+ console.log(chalk.yellow(" \u26A0 Discussion interrupted by user."));
233
+ console.log();
234
+ }
235
+ function renderSummary(summary) {
236
+ console.log(chalk.bold.white(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
237
+ console.log(chalk.bold.white(" \u2551") + chalk.bold.green(" \u{1F4CB} Discussion Summary ") + chalk.bold.white("\u2551"));
238
+ console.log(chalk.bold.white(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
239
+ console.log();
240
+ for (const line of summary.split("\n")) {
241
+ console.log(" " + line);
242
+ }
243
+ console.log();
244
+ }
245
+ function renderHubEvent(event) {
246
+ switch (event.type) {
247
+ case "round_start":
248
+ renderRoundHeader(event.round, event.maxRounds);
249
+ break;
250
+ case "agent_speaking":
251
+ renderAgentSpeaking(event.roleName, event.roleId);
252
+ break;
253
+ case "agent_spoke":
254
+ clearSpeakingLine();
255
+ renderAgentMessage(event.message);
256
+ break;
257
+ case "agent_passed":
258
+ clearSpeakingLine();
259
+ break;
260
+ case "discussion_end":
261
+ if (event.reason === "consensus") renderConsensus();
262
+ else if (event.reason === "max_rounds") renderMaxRounds(0);
263
+ else renderUserInterrupt();
264
+ break;
265
+ case "summary":
266
+ renderSummary(event.content);
267
+ break;
268
+ case "error":
269
+ console.log(chalk.red(` \u2717 Error${event.roleId ? ` (${event.roleId})` : ""}: ${event.message}`));
270
+ break;
271
+ }
272
+ }
273
+ function truncate(s, maxLen) {
274
+ return s.length > maxLen ? s.slice(0, maxLen - 3) + "..." : s;
275
+ }
276
+ function stripAnsi(s) {
277
+ return s.replace(/\x1b\[[0-9;]*m/g, "");
278
+ }
279
+
280
+ export {
281
+ HubAgent,
282
+ assignRoleColors,
283
+ renderHubBanner,
284
+ renderRoundHeader,
285
+ renderAgentSpeaking,
286
+ clearSpeakingLine,
287
+ renderAgentMessage,
288
+ renderConsensus,
289
+ renderMaxRounds,
290
+ renderUserInterrupt,
291
+ renderSummary,
292
+ renderHubEvent
293
+ };
@@ -16,7 +16,7 @@ import {
16
16
  SUBAGENT_MAX_ROUNDS_LIMIT,
17
17
  VERSION,
18
18
  runTestsTool
19
- } from "./chunk-QVUOF3DK.js";
19
+ } from "./chunk-NLSQEHZU.js";
20
20
 
21
21
  // src/config/config-manager.ts
22
22
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -6,7 +6,7 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.3.1";
9
+ var VERSION = "0.3.3";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.3.1";
11
+ var VERSION = "0.3.3";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";