@defai.digital/cli 13.1.0
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/LICENSE +214 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +11 -0
- package/dist/bin.js.map +1 -0
- package/dist/bootstrap.d.ts +144 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +315 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +84 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ability.d.ts +17 -0
- package/dist/commands/ability.d.ts.map +1 -0
- package/dist/commands/ability.js +286 -0
- package/dist/commands/ability.js.map +1 -0
- package/dist/commands/agent.d.ts +18 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +361 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/call.d.ts +15 -0
- package/dist/commands/call.d.ts.map +1 -0
- package/dist/commands/call.js +503 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/cleanup.d.ts +18 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +300 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/config.d.ts +16 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +513 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/discuss.d.ts +16 -0
- package/dist/commands/discuss.d.ts.map +1 -0
- package/dist/commands/discuss.js +700 -0
- package/dist/commands/discuss.js.map +1 -0
- package/dist/commands/doctor.d.ts +48 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +356 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/guard.d.ts +12 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +225 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +180 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/history.d.ts +19 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +200 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/index.d.ts +23 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +26 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/iterate.d.ts +16 -0
- package/dist/commands/iterate.d.ts.map +1 -0
- package/dist/commands/iterate.js +72 -0
- package/dist/commands/iterate.js.map +1 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +62 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.d.ts +16 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +57 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/resume.d.ts +18 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +208 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/review.d.ts +13 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +450 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/run.d.ts +6 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +158 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/scaffold.d.ts +20 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +924 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +504 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/setup.d.ts +14 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +762 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/status.d.ts +17 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +227 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/trace.d.ts +6 -0
- package/dist/commands/trace.d.ts.map +1 -0
- package/dist/commands/trace.js +204 -0
- package/dist/commands/trace.js.map +1 -0
- package/dist/commands/update.d.ts +24 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +296 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +14 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +288 -0
- package/dist/parser.js.map +1 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +29 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/dangerous-op-guard.d.ts +33 -0
- package/dist/utils/dangerous-op-guard.d.ts.map +1 -0
- package/dist/utils/dangerous-op-guard.js +112 -0
- package/dist/utils/dangerous-op-guard.js.map +1 -0
- package/dist/utils/database.d.ts +85 -0
- package/dist/utils/database.d.ts.map +1 -0
- package/dist/utils/database.js +184 -0
- package/dist/utils/database.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/provider-factory.d.ts +31 -0
- package/dist/utils/provider-factory.d.ts.map +1 -0
- package/dist/utils/provider-factory.js +109 -0
- package/dist/utils/provider-factory.js.map +1 -0
- package/dist/utils/storage-instances.d.ts +19 -0
- package/dist/utils/storage-instances.d.ts.map +1 -0
- package/dist/utils/storage-instances.js +20 -0
- package/dist/utils/storage-instances.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discuss Command - Multi-model discussion orchestration
|
|
3
|
+
*
|
|
4
|
+
* Usage: ax discuss <topic>
|
|
5
|
+
* ax discuss --providers claude,glm,qwen "Design a REST API"
|
|
6
|
+
* ax discuss --pattern debate "Is microservices better than monolith?"
|
|
7
|
+
*
|
|
8
|
+
* Enables multiple AI models to discuss a topic from their unique perspectives,
|
|
9
|
+
* building consensus through various patterns and mechanisms.
|
|
10
|
+
*/
|
|
11
|
+
// Bootstrap imports - composition root provides adapter access
|
|
12
|
+
import { createProvider, PROVIDER_CONFIGS, } from '../bootstrap.js';
|
|
13
|
+
// Discussion domain imports
|
|
14
|
+
import { DiscussionExecutor, RecursiveDiscussionExecutor, parseParticipantList, } from '@defai.digital/discussion-domain';
|
|
15
|
+
// Contract types
|
|
16
|
+
import { DEFAULT_PROVIDERS, } from '@defai.digital/contracts';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Constants
|
|
19
|
+
// ============================================================================
|
|
20
|
+
const COLORS = {
|
|
21
|
+
reset: '\x1b[0m',
|
|
22
|
+
green: '\x1b[32m',
|
|
23
|
+
red: '\x1b[31m',
|
|
24
|
+
yellow: '\x1b[33m',
|
|
25
|
+
cyan: '\x1b[36m',
|
|
26
|
+
blue: '\x1b[34m',
|
|
27
|
+
magenta: '\x1b[35m',
|
|
28
|
+
bold: '\x1b[1m',
|
|
29
|
+
dim: '\x1b[2m',
|
|
30
|
+
};
|
|
31
|
+
const ICONS = {
|
|
32
|
+
check: `${COLORS.green}\u2713${COLORS.reset}`,
|
|
33
|
+
cross: `${COLORS.red}\u2717${COLORS.reset}`,
|
|
34
|
+
arrow: `${COLORS.cyan}\u2192${COLORS.reset}`,
|
|
35
|
+
bullet: `${COLORS.dim}\u2022${COLORS.reset}`,
|
|
36
|
+
discuss: `${COLORS.magenta}\u2630${COLORS.reset}`,
|
|
37
|
+
};
|
|
38
|
+
// Pattern display names
|
|
39
|
+
const PATTERN_NAMES = {
|
|
40
|
+
'round-robin': 'Round Robin',
|
|
41
|
+
synthesis: 'Synthesis',
|
|
42
|
+
debate: 'Debate',
|
|
43
|
+
critique: 'Critique',
|
|
44
|
+
voting: 'Voting',
|
|
45
|
+
};
|
|
46
|
+
// Consensus method display names
|
|
47
|
+
const CONSENSUS_NAMES = {
|
|
48
|
+
synthesis: 'Synthesis',
|
|
49
|
+
voting: 'Voting',
|
|
50
|
+
moderator: 'Moderator',
|
|
51
|
+
unanimous: 'Unanimous',
|
|
52
|
+
majority: 'Majority',
|
|
53
|
+
};
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Provider Bridge
|
|
56
|
+
// ============================================================================
|
|
57
|
+
/**
|
|
58
|
+
* Creates a DiscussionProviderExecutor that bridges to CLI providers
|
|
59
|
+
*/
|
|
60
|
+
function createProviderBridge(providerConfigs) {
|
|
61
|
+
// Cache of provider adapters
|
|
62
|
+
const adapterCache = new Map();
|
|
63
|
+
function getAdapter(providerId) {
|
|
64
|
+
if (adapterCache.has(providerId)) {
|
|
65
|
+
return adapterCache.get(providerId);
|
|
66
|
+
}
|
|
67
|
+
const config = providerConfigs[providerId];
|
|
68
|
+
if (config === undefined) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const adapter = createProvider(config);
|
|
72
|
+
adapterCache.set(providerId, adapter);
|
|
73
|
+
return adapter;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
async execute(request) {
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
const adapter = getAdapter(request.providerId);
|
|
79
|
+
if (adapter === undefined) {
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
error: `Provider ${request.providerId} not found`,
|
|
83
|
+
retryable: false,
|
|
84
|
+
durationMs: Date.now() - startTime,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const config = providerConfigs[request.providerId];
|
|
89
|
+
const completionRequest = {
|
|
90
|
+
requestId: crypto.randomUUID(),
|
|
91
|
+
messages: [{ role: 'user', content: request.prompt }],
|
|
92
|
+
model: config?.models.find(m => m.isDefault)?.modelId ?? config?.models[0]?.modelId ?? 'default',
|
|
93
|
+
systemPrompt: request.systemPrompt,
|
|
94
|
+
};
|
|
95
|
+
const response = await adapter.complete(completionRequest);
|
|
96
|
+
if (!response.success) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: response.error?.message ?? 'Unknown error',
|
|
100
|
+
retryable: response.error?.shouldRetry ?? true,
|
|
101
|
+
durationMs: Date.now() - startTime,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
success: true,
|
|
106
|
+
content: response.content,
|
|
107
|
+
durationMs: Date.now() - startTime,
|
|
108
|
+
tokenCount: response.usage?.totalTokens,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
115
|
+
retryable: true,
|
|
116
|
+
durationMs: Date.now() - startTime,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
async isAvailable(providerId) {
|
|
121
|
+
const adapter = getAdapter(providerId);
|
|
122
|
+
if (adapter === undefined) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return await adapter.isAvailable();
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
async getAvailableProviders() {
|
|
133
|
+
const available = [];
|
|
134
|
+
for (const providerId of Object.keys(providerConfigs)) {
|
|
135
|
+
const adapter = getAdapter(providerId);
|
|
136
|
+
if (adapter !== undefined) {
|
|
137
|
+
try {
|
|
138
|
+
const isAvail = await adapter.isAvailable();
|
|
139
|
+
if (isAvail) {
|
|
140
|
+
available.push(providerId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Provider not available
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return available;
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// ============================================================================
|
|
153
|
+
// Argument Parsing
|
|
154
|
+
// ============================================================================
|
|
155
|
+
/**
|
|
156
|
+
* Parses discuss command arguments
|
|
157
|
+
*/
|
|
158
|
+
function parseDiscussArgs(args, _options) {
|
|
159
|
+
let topic;
|
|
160
|
+
let providers = [];
|
|
161
|
+
let pattern = 'synthesis';
|
|
162
|
+
let rounds = 2;
|
|
163
|
+
let consensus = 'synthesis';
|
|
164
|
+
let synthesizer;
|
|
165
|
+
let context;
|
|
166
|
+
let timeout = 60000;
|
|
167
|
+
// Participant options
|
|
168
|
+
let participants;
|
|
169
|
+
let agentWeight = 1.5; // Default agent weight multiplier (INV-DISC-642)
|
|
170
|
+
// Recursive options
|
|
171
|
+
let recursive = false;
|
|
172
|
+
let maxDepth = 2;
|
|
173
|
+
let timeoutStrategy = 'cascade';
|
|
174
|
+
let totalBudget = 180000; // 3 minutes default
|
|
175
|
+
let maxCalls = 20;
|
|
176
|
+
let earlyExit = true;
|
|
177
|
+
let confidenceThreshold = 0.9;
|
|
178
|
+
for (let i = 0; i < args.length; i++) {
|
|
179
|
+
const arg = args[i];
|
|
180
|
+
if (arg === '--providers' && i + 1 < args.length) {
|
|
181
|
+
const providerStr = args[++i];
|
|
182
|
+
providers = providerStr?.split(',').map(p => p.trim()) ?? [];
|
|
183
|
+
}
|
|
184
|
+
else if (arg === '--pattern' && i + 1 < args.length) {
|
|
185
|
+
pattern = args[++i];
|
|
186
|
+
}
|
|
187
|
+
else if (arg === '--rounds' && i + 1 < args.length) {
|
|
188
|
+
rounds = parseInt(args[++i] ?? '2', 10);
|
|
189
|
+
}
|
|
190
|
+
else if (arg === '--consensus' && i + 1 < args.length) {
|
|
191
|
+
consensus = args[++i];
|
|
192
|
+
}
|
|
193
|
+
else if (arg === '--synthesizer' && i + 1 < args.length) {
|
|
194
|
+
synthesizer = args[++i];
|
|
195
|
+
}
|
|
196
|
+
else if (arg === '--context' && i + 1 < args.length) {
|
|
197
|
+
context = args[++i];
|
|
198
|
+
}
|
|
199
|
+
else if (arg === '--timeout' && i + 1 < args.length) {
|
|
200
|
+
timeout = parseInt(args[++i] ?? '60000', 10);
|
|
201
|
+
}
|
|
202
|
+
// Participant options (agents and providers)
|
|
203
|
+
else if (arg === '--participants' && i + 1 < args.length) {
|
|
204
|
+
// Parse participant list: "claude,glm,reviewer:agent,security:agent"
|
|
205
|
+
const participantStr = args[++i] ?? '';
|
|
206
|
+
participants = parseParticipantList(participantStr);
|
|
207
|
+
}
|
|
208
|
+
else if (arg === '--agent-weight' && i + 1 < args.length) {
|
|
209
|
+
// Agent weight multiplier (0.5 - 3.0, default 1.5)
|
|
210
|
+
agentWeight = Math.max(0.5, Math.min(3.0, parseFloat(args[++i] ?? '1.5')));
|
|
211
|
+
}
|
|
212
|
+
// Recursive discussion options
|
|
213
|
+
else if (arg === '--recursive' || arg === '-r') {
|
|
214
|
+
recursive = true;
|
|
215
|
+
}
|
|
216
|
+
else if (arg === '--max-depth' && i + 1 < args.length) {
|
|
217
|
+
maxDepth = parseInt(args[++i] ?? '2', 10);
|
|
218
|
+
recursive = true; // Implies recursive
|
|
219
|
+
}
|
|
220
|
+
else if (arg === '--timeout-strategy' && i + 1 < args.length) {
|
|
221
|
+
timeoutStrategy = args[++i];
|
|
222
|
+
}
|
|
223
|
+
else if (arg === '--budget' && i + 1 < args.length) {
|
|
224
|
+
// Parse budget like "180s" or "180000"
|
|
225
|
+
const budgetStr = args[++i] ?? '180000';
|
|
226
|
+
if (budgetStr.endsWith('s')) {
|
|
227
|
+
totalBudget = parseInt(budgetStr.slice(0, -1), 10) * 1000;
|
|
228
|
+
}
|
|
229
|
+
else if (budgetStr.endsWith('m')) {
|
|
230
|
+
totalBudget = parseInt(budgetStr.slice(0, -1), 10) * 60000;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
totalBudget = parseInt(budgetStr, 10);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else if (arg === '--max-calls' && i + 1 < args.length) {
|
|
237
|
+
maxCalls = parseInt(args[++i] ?? '20', 10);
|
|
238
|
+
}
|
|
239
|
+
else if (arg === '--early-exit') {
|
|
240
|
+
earlyExit = true;
|
|
241
|
+
}
|
|
242
|
+
else if (arg === '--no-early-exit') {
|
|
243
|
+
earlyExit = false;
|
|
244
|
+
}
|
|
245
|
+
else if (arg === '--confidence-threshold' && i + 1 < args.length) {
|
|
246
|
+
confidenceThreshold = parseFloat(args[++i] ?? '0.9');
|
|
247
|
+
}
|
|
248
|
+
else if (arg !== undefined && !arg.startsWith('-')) {
|
|
249
|
+
// Positional argument is the topic
|
|
250
|
+
if (topic === undefined) {
|
|
251
|
+
// Collect remaining non-flag args as topic
|
|
252
|
+
const topicParts = [];
|
|
253
|
+
for (let j = i; j < args.length; j++) {
|
|
254
|
+
const part = args[j];
|
|
255
|
+
if (part?.startsWith('-'))
|
|
256
|
+
break;
|
|
257
|
+
topicParts.push(part ?? '');
|
|
258
|
+
}
|
|
259
|
+
topic = topicParts.join(' ');
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Default providers if none specified
|
|
265
|
+
if (providers.length === 0) {
|
|
266
|
+
providers = [...DEFAULT_PROVIDERS];
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
topic,
|
|
270
|
+
providers,
|
|
271
|
+
pattern,
|
|
272
|
+
rounds,
|
|
273
|
+
consensus,
|
|
274
|
+
synthesizer,
|
|
275
|
+
context,
|
|
276
|
+
timeout,
|
|
277
|
+
participants,
|
|
278
|
+
agentWeight,
|
|
279
|
+
recursive,
|
|
280
|
+
maxDepth,
|
|
281
|
+
timeoutStrategy,
|
|
282
|
+
totalBudget,
|
|
283
|
+
maxCalls,
|
|
284
|
+
earlyExit,
|
|
285
|
+
confidenceThreshold,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
// ============================================================================
|
|
289
|
+
// Progress Display
|
|
290
|
+
// ============================================================================
|
|
291
|
+
/**
|
|
292
|
+
* Creates a progress handler for verbose output
|
|
293
|
+
*/
|
|
294
|
+
function createProgressHandler(verbose) {
|
|
295
|
+
if (!verbose) {
|
|
296
|
+
return () => { }; // No-op
|
|
297
|
+
}
|
|
298
|
+
return (event) => {
|
|
299
|
+
switch (event.type) {
|
|
300
|
+
case 'round_start':
|
|
301
|
+
console.log(`\n${COLORS.bold}Round ${event.round}${COLORS.reset}`);
|
|
302
|
+
break;
|
|
303
|
+
case 'provider_start':
|
|
304
|
+
process.stdout.write(` ${ICONS.bullet} ${event.provider}: `);
|
|
305
|
+
break;
|
|
306
|
+
case 'provider_complete':
|
|
307
|
+
console.log(`${ICONS.check} ${COLORS.dim}(${event.message ?? 'done'})${COLORS.reset}`);
|
|
308
|
+
break;
|
|
309
|
+
case 'round_complete':
|
|
310
|
+
console.log(` ${ICONS.arrow} Round ${event.round} complete`);
|
|
311
|
+
break;
|
|
312
|
+
case 'synthesis_start':
|
|
313
|
+
console.log(`\n${COLORS.bold}Synthesizing...${COLORS.reset}`);
|
|
314
|
+
break;
|
|
315
|
+
case 'synthesis_complete':
|
|
316
|
+
console.log(`${ICONS.check} Synthesis complete`);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Formats the discussion result for text output
|
|
323
|
+
*/
|
|
324
|
+
function formatDiscussionResult(result, verbose) {
|
|
325
|
+
const lines = [];
|
|
326
|
+
// Header
|
|
327
|
+
lines.push('');
|
|
328
|
+
lines.push(`${COLORS.bold}${ICONS.discuss} Discussion Results${COLORS.reset}`);
|
|
329
|
+
lines.push('─'.repeat(50));
|
|
330
|
+
// Pattern and status
|
|
331
|
+
const patternName = PATTERN_NAMES[result.pattern] ?? result.pattern;
|
|
332
|
+
lines.push(`Pattern: ${COLORS.cyan}${patternName}${COLORS.reset}`);
|
|
333
|
+
lines.push(`Status: ${result.success ? `${ICONS.check} Success` : `${ICONS.cross} Failed`}`);
|
|
334
|
+
// Providers
|
|
335
|
+
lines.push(`Providers: ${result.participatingProviders.join(', ')}`);
|
|
336
|
+
if (result.failedProviders.length > 0) {
|
|
337
|
+
lines.push(`Failed: ${COLORS.red}${result.failedProviders.join(', ')}${COLORS.reset}`);
|
|
338
|
+
}
|
|
339
|
+
// Duration
|
|
340
|
+
const durationSec = (result.totalDurationMs / 1000).toFixed(1);
|
|
341
|
+
lines.push(`Duration: ${durationSec}s`);
|
|
342
|
+
// Recursive discussion info
|
|
343
|
+
const recursiveResult = result;
|
|
344
|
+
if (recursiveResult.totalProviderCalls !== undefined) {
|
|
345
|
+
lines.push(`Total Calls: ${recursiveResult.totalProviderCalls}`);
|
|
346
|
+
}
|
|
347
|
+
if (recursiveResult.maxDepthReached !== undefined && recursiveResult.maxDepthReached > 0) {
|
|
348
|
+
lines.push(`Max Depth: ${recursiveResult.maxDepthReached}`);
|
|
349
|
+
}
|
|
350
|
+
if (recursiveResult.subDiscussions && recursiveResult.subDiscussions.length > 0) {
|
|
351
|
+
lines.push(`Sub-discussions: ${recursiveResult.subDiscussions.length}`);
|
|
352
|
+
}
|
|
353
|
+
// Consensus result
|
|
354
|
+
if (result.consensus !== undefined) {
|
|
355
|
+
lines.push('');
|
|
356
|
+
lines.push(`${COLORS.bold}Consensus${COLORS.reset}`);
|
|
357
|
+
const consensusName = CONSENSUS_NAMES[result.consensus.method] ?? result.consensus.method;
|
|
358
|
+
lines.push(` Method: ${consensusName}`);
|
|
359
|
+
if (result.consensus.synthesizer !== undefined) {
|
|
360
|
+
lines.push(` Synthesizer: ${result.consensus.synthesizer}`);
|
|
361
|
+
}
|
|
362
|
+
if (result.consensus.agreementScore !== undefined) {
|
|
363
|
+
const scorePercent = (result.consensus.agreementScore * 100).toFixed(0);
|
|
364
|
+
lines.push(` Agreement: ${scorePercent}%`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Voting results (for voting pattern)
|
|
368
|
+
if (result.votingResults !== undefined) {
|
|
369
|
+
lines.push(` Winner: ${COLORS.green}${result.votingResults.winner}${COLORS.reset}`);
|
|
370
|
+
const marginPercent = (result.votingResults.margin * 100).toFixed(0);
|
|
371
|
+
lines.push(` Margin: ${marginPercent}%`);
|
|
372
|
+
if (result.votingResults.unanimous) {
|
|
373
|
+
lines.push(` ${ICONS.check} Unanimous`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// Sub-discussions (for recursive mode)
|
|
377
|
+
if (verbose && recursiveResult.subDiscussions && recursiveResult.subDiscussions.length > 0) {
|
|
378
|
+
lines.push('');
|
|
379
|
+
lines.push(`${COLORS.bold}Sub-discussions${COLORS.reset}`);
|
|
380
|
+
for (const sub of recursiveResult.subDiscussions) {
|
|
381
|
+
lines.push(` ${COLORS.cyan}Depth ${sub.depth}${COLORS.reset}: ${sub.topic.substring(0, 50)}...`);
|
|
382
|
+
lines.push(` Providers: ${sub.participatingProviders.join(', ')}`);
|
|
383
|
+
lines.push(` Duration: ${(sub.durationMs / 1000).toFixed(1)}s`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// Verbose: show rounds
|
|
387
|
+
if (verbose && result.rounds.length > 0) {
|
|
388
|
+
lines.push('');
|
|
389
|
+
lines.push(`${COLORS.bold}Rounds${COLORS.reset}`);
|
|
390
|
+
for (const round of result.rounds) {
|
|
391
|
+
lines.push(` Round ${round.roundNumber}:`);
|
|
392
|
+
for (const response of round.responses) {
|
|
393
|
+
const preview = response.content.substring(0, 80).replace(/\n/g, ' ');
|
|
394
|
+
lines.push(` ${response.provider}: ${COLORS.dim}${preview}...${COLORS.reset}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Synthesis
|
|
399
|
+
lines.push('');
|
|
400
|
+
lines.push(`${COLORS.bold}Synthesis${COLORS.reset}`);
|
|
401
|
+
lines.push('─'.repeat(50));
|
|
402
|
+
lines.push(result.synthesis);
|
|
403
|
+
return lines.join('\n');
|
|
404
|
+
}
|
|
405
|
+
// ============================================================================
|
|
406
|
+
// Help
|
|
407
|
+
// ============================================================================
|
|
408
|
+
/**
|
|
409
|
+
* Shows discuss command help
|
|
410
|
+
*/
|
|
411
|
+
function showDiscussHelp() {
|
|
412
|
+
const availableProviders = Object.keys(PROVIDER_CONFIGS).join(', ');
|
|
413
|
+
const patterns = Object.keys(PATTERN_NAMES).join(', ');
|
|
414
|
+
const consensusMethods = Object.keys(CONSENSUS_NAMES).join(', ');
|
|
415
|
+
const helpText = `
|
|
416
|
+
${COLORS.bold}Discuss Command${COLORS.reset} - Multi-model discussion orchestration
|
|
417
|
+
|
|
418
|
+
${COLORS.bold}Usage:${COLORS.reset}
|
|
419
|
+
ax discuss <topic>
|
|
420
|
+
ax discuss --providers <providers> <topic>
|
|
421
|
+
ax discuss --pattern <pattern> <topic>
|
|
422
|
+
ax discuss --recursive <topic>
|
|
423
|
+
|
|
424
|
+
${COLORS.bold}Arguments:${COLORS.reset}
|
|
425
|
+
<topic> The topic or question for discussion
|
|
426
|
+
|
|
427
|
+
${COLORS.bold}Basic Options:${COLORS.reset}
|
|
428
|
+
--providers Comma-separated list of providers (default: claude,glm,qwen,gemini)
|
|
429
|
+
--pattern Discussion pattern: ${patterns}
|
|
430
|
+
--rounds Number of discussion rounds (default: 2)
|
|
431
|
+
--consensus Consensus method: ${consensusMethods}
|
|
432
|
+
--synthesizer Provider for synthesis (default: claude)
|
|
433
|
+
--context Additional context for the discussion
|
|
434
|
+
--timeout Per-provider timeout in ms (default: 60000)
|
|
435
|
+
--verbose, -v Show detailed progress
|
|
436
|
+
--format Output format: text (default) or json
|
|
437
|
+
|
|
438
|
+
${COLORS.bold}Recursive Discussion Options:${COLORS.reset}
|
|
439
|
+
--recursive, -r Enable recursive sub-discussions
|
|
440
|
+
--max-depth Maximum discussion depth (default: 2, max: 4)
|
|
441
|
+
--timeout-strategy Timeout strategy: fixed, cascade, budget (default: cascade)
|
|
442
|
+
--budget Total timeout budget (e.g., 180s, 3m, 180000)
|
|
443
|
+
--max-calls Maximum total provider calls (default: 20)
|
|
444
|
+
--early-exit Enable early exit on high confidence (default: true)
|
|
445
|
+
--no-early-exit Disable early exit
|
|
446
|
+
--confidence-threshold Confidence threshold for early exit (default: 0.9)
|
|
447
|
+
|
|
448
|
+
${COLORS.bold}Available Providers:${COLORS.reset}
|
|
449
|
+
${availableProviders}
|
|
450
|
+
|
|
451
|
+
${COLORS.bold}Discussion Patterns:${COLORS.reset}
|
|
452
|
+
synthesis All models discuss, one synthesizes final answer
|
|
453
|
+
debate Structured debate with proponent/opponent roles
|
|
454
|
+
critique One proposes, others critique, then revision
|
|
455
|
+
voting Models vote on options with confidence levels
|
|
456
|
+
round-robin Sequential turns with each building on previous
|
|
457
|
+
|
|
458
|
+
${COLORS.bold}Timeout Strategies:${COLORS.reset}
|
|
459
|
+
fixed Each level gets equal timeout
|
|
460
|
+
cascade Each level gets half of parent (recommended)
|
|
461
|
+
budget Total budget divided across levels
|
|
462
|
+
|
|
463
|
+
${COLORS.bold}Examples:${COLORS.reset}
|
|
464
|
+
ax discuss "What is the best approach for microservices?"
|
|
465
|
+
ax discuss --providers claude,glm,qwen "Design a REST API"
|
|
466
|
+
ax discuss --pattern debate "Is functional programming better than OOP?"
|
|
467
|
+
ax discuss --pattern voting "Which framework: React, Vue, or Angular?"
|
|
468
|
+
ax discuss --verbose --rounds 3 "Optimize database queries"
|
|
469
|
+
|
|
470
|
+
${COLORS.cyan}# Recursive discussions${COLORS.reset}
|
|
471
|
+
ax discuss --recursive "Complex architectural decision"
|
|
472
|
+
ax discuss --recursive --max-depth 3 "Design a distributed system"
|
|
473
|
+
ax discuss --recursive --budget 5m --max-calls 30 "Research topic"
|
|
474
|
+
`.trim();
|
|
475
|
+
return {
|
|
476
|
+
success: true,
|
|
477
|
+
message: helpText,
|
|
478
|
+
data: undefined,
|
|
479
|
+
exitCode: 0,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
// ============================================================================
|
|
483
|
+
// Main Command Handler
|
|
484
|
+
// ============================================================================
|
|
485
|
+
/**
|
|
486
|
+
* Discuss command handler
|
|
487
|
+
*/
|
|
488
|
+
export async function discussCommand(args, options) {
|
|
489
|
+
// Show help if requested
|
|
490
|
+
if (args.length === 0 || args[0] === 'help' || options.help) {
|
|
491
|
+
return showDiscussHelp();
|
|
492
|
+
}
|
|
493
|
+
// Handle 'quick' subcommand - use quick synthesis with 2-3 providers
|
|
494
|
+
if (args[0] === 'quick') {
|
|
495
|
+
const quickArgs = args.slice(1);
|
|
496
|
+
const topic = quickArgs.join(' ');
|
|
497
|
+
if (topic.trim() === '') {
|
|
498
|
+
return {
|
|
499
|
+
success: false,
|
|
500
|
+
message: 'Error: Topic is required.\n\nUsage: ax discuss quick <topic>',
|
|
501
|
+
data: undefined,
|
|
502
|
+
exitCode: 1,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// For quick mode, use 3 providers and 2 rounds with synthesis
|
|
506
|
+
return discussCommand([
|
|
507
|
+
'--providers', 'claude,glm,qwen',
|
|
508
|
+
'--pattern', 'synthesis',
|
|
509
|
+
'--rounds', '2',
|
|
510
|
+
topic,
|
|
511
|
+
], options);
|
|
512
|
+
}
|
|
513
|
+
const parsed = parseDiscussArgs(args, options);
|
|
514
|
+
// Validate topic
|
|
515
|
+
if (parsed.topic === undefined || parsed.topic.trim() === '') {
|
|
516
|
+
return {
|
|
517
|
+
success: false,
|
|
518
|
+
message: 'Error: Topic is required.\n\nRun "ax discuss help" for usage.',
|
|
519
|
+
data: undefined,
|
|
520
|
+
exitCode: 1,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
// Validate providers
|
|
524
|
+
const invalidProviders = parsed.providers.filter(p => PROVIDER_CONFIGS[p] === undefined);
|
|
525
|
+
if (invalidProviders.length > 0) {
|
|
526
|
+
const available = Object.keys(PROVIDER_CONFIGS).join(', ');
|
|
527
|
+
return {
|
|
528
|
+
success: false,
|
|
529
|
+
message: `Error: Unknown provider(s): ${invalidProviders.join(', ')}\nAvailable: ${available}`,
|
|
530
|
+
data: undefined,
|
|
531
|
+
exitCode: 1,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
// Validate minimum providers
|
|
535
|
+
if (parsed.providers.length < 2) {
|
|
536
|
+
return {
|
|
537
|
+
success: false,
|
|
538
|
+
message: 'Error: At least 2 providers are required for discussion.',
|
|
539
|
+
data: undefined,
|
|
540
|
+
exitCode: 1,
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
// Create provider bridge
|
|
544
|
+
const providerBridge = createProviderBridge(PROVIDER_CONFIGS);
|
|
545
|
+
// Check provider availability
|
|
546
|
+
if (options.verbose) {
|
|
547
|
+
console.log(`${COLORS.bold}Checking provider availability...${COLORS.reset}`);
|
|
548
|
+
}
|
|
549
|
+
const availableProviders = [];
|
|
550
|
+
for (const providerId of parsed.providers) {
|
|
551
|
+
const isAvailable = await providerBridge.isAvailable(providerId);
|
|
552
|
+
if (isAvailable) {
|
|
553
|
+
availableProviders.push(providerId);
|
|
554
|
+
if (options.verbose) {
|
|
555
|
+
console.log(` ${ICONS.check} ${providerId}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
if (options.verbose) {
|
|
560
|
+
console.log(` ${ICONS.cross} ${providerId} ${COLORS.dim}(not available)${COLORS.reset}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (availableProviders.length < 2) {
|
|
565
|
+
return {
|
|
566
|
+
success: false,
|
|
567
|
+
message: `Error: Only ${availableProviders.length} providers available. Need at least 2.\nRun "ax doctor" to check provider status.`,
|
|
568
|
+
data: undefined,
|
|
569
|
+
exitCode: 1,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
// Build discussion config
|
|
573
|
+
const config = {
|
|
574
|
+
pattern: parsed.pattern,
|
|
575
|
+
rounds: parsed.rounds,
|
|
576
|
+
providers: availableProviders,
|
|
577
|
+
prompt: parsed.topic,
|
|
578
|
+
context: parsed.context,
|
|
579
|
+
verbose: options.verbose,
|
|
580
|
+
consensus: {
|
|
581
|
+
method: parsed.consensus,
|
|
582
|
+
synthesizer: parsed.synthesizer ?? 'claude',
|
|
583
|
+
threshold: 0.5,
|
|
584
|
+
includeDissent: true,
|
|
585
|
+
},
|
|
586
|
+
providerTimeout: parsed.timeout,
|
|
587
|
+
continueOnProviderFailure: true,
|
|
588
|
+
minProviders: 2,
|
|
589
|
+
temperature: 0.7,
|
|
590
|
+
agentWeightMultiplier: parsed.agentWeight,
|
|
591
|
+
// Include participants if specified via --participants option
|
|
592
|
+
...(parsed.participants !== undefined && { participants: parsed.participants }),
|
|
593
|
+
};
|
|
594
|
+
// Show discussion start
|
|
595
|
+
if (options.verbose) {
|
|
596
|
+
console.log('');
|
|
597
|
+
console.log(`${COLORS.bold}Starting Discussion${COLORS.reset}`);
|
|
598
|
+
console.log(` Topic: ${parsed.topic.substring(0, 60)}${parsed.topic.length > 60 ? '...' : ''}`);
|
|
599
|
+
console.log(` Pattern: ${PATTERN_NAMES[parsed.pattern] ?? parsed.pattern}`);
|
|
600
|
+
console.log(` Providers: ${availableProviders.join(', ')}`);
|
|
601
|
+
console.log(` Rounds: ${parsed.rounds}`);
|
|
602
|
+
if (parsed.participants !== undefined && parsed.participants.length > 0) {
|
|
603
|
+
const agentCount = parsed.participants.filter(p => p.type === 'agent').length;
|
|
604
|
+
const providerCount = parsed.participants.filter(p => p.type === 'provider').length;
|
|
605
|
+
console.log(` Participants: ${providerCount} providers, ${agentCount} agents`);
|
|
606
|
+
console.log(` Agent Weight: ${parsed.agentWeight}x`);
|
|
607
|
+
}
|
|
608
|
+
if (parsed.recursive) {
|
|
609
|
+
console.log(` ${COLORS.cyan}Recursive: enabled${COLORS.reset}`);
|
|
610
|
+
console.log(` Max Depth: ${parsed.maxDepth}`);
|
|
611
|
+
console.log(` Timeout Strategy: ${parsed.timeoutStrategy}`);
|
|
612
|
+
console.log(` Budget: ${(parsed.totalBudget / 1000).toFixed(0)}s`);
|
|
613
|
+
console.log(` Max Calls: ${parsed.maxCalls}`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
// Simple progress indicator for non-verbose mode
|
|
618
|
+
const recursiveLabel = parsed.recursive ? ' (recursive)' : '';
|
|
619
|
+
process.stdout.write(`${ICONS.discuss} Discussing with ${availableProviders.length} providers${recursiveLabel}... `);
|
|
620
|
+
}
|
|
621
|
+
try {
|
|
622
|
+
// Choose executor based on recursive flag
|
|
623
|
+
let result;
|
|
624
|
+
if (parsed.recursive) {
|
|
625
|
+
// Use recursive executor
|
|
626
|
+
const recursiveExecutor = new RecursiveDiscussionExecutor({
|
|
627
|
+
providerExecutor: providerBridge,
|
|
628
|
+
defaultTimeoutMs: parsed.timeout,
|
|
629
|
+
checkProviderHealth: false, // Already checked above
|
|
630
|
+
recursive: {
|
|
631
|
+
enabled: true,
|
|
632
|
+
maxDepth: parsed.maxDepth,
|
|
633
|
+
allowSubDiscussions: true,
|
|
634
|
+
},
|
|
635
|
+
timeout: {
|
|
636
|
+
strategy: parsed.timeoutStrategy,
|
|
637
|
+
totalBudgetMs: parsed.totalBudget,
|
|
638
|
+
minSynthesisMs: 10000,
|
|
639
|
+
},
|
|
640
|
+
cost: {
|
|
641
|
+
maxTotalCalls: parsed.maxCalls,
|
|
642
|
+
cascadingConfidence: {
|
|
643
|
+
enabled: parsed.earlyExit,
|
|
644
|
+
threshold: parsed.confidenceThreshold,
|
|
645
|
+
minProviders: 2,
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
result = await recursiveExecutor.execute(config, {
|
|
650
|
+
onProgress: createProgressHandler(options.verbose),
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
// Use standard executor
|
|
655
|
+
const discussionExecutor = new DiscussionExecutor({
|
|
656
|
+
providerExecutor: providerBridge,
|
|
657
|
+
defaultTimeoutMs: parsed.timeout,
|
|
658
|
+
checkProviderHealth: false, // Already checked above
|
|
659
|
+
});
|
|
660
|
+
result = await discussionExecutor.execute(config, {
|
|
661
|
+
onProgress: createProgressHandler(options.verbose),
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
// Clear simple progress indicator if not verbose
|
|
665
|
+
if (!options.verbose) {
|
|
666
|
+
console.log(result.success ? ICONS.check : ICONS.cross);
|
|
667
|
+
}
|
|
668
|
+
// Handle JSON output
|
|
669
|
+
if (options.format === 'json') {
|
|
670
|
+
return {
|
|
671
|
+
success: result.success,
|
|
672
|
+
message: undefined,
|
|
673
|
+
data: result,
|
|
674
|
+
exitCode: result.success ? 0 : 1,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
// Format text output
|
|
678
|
+
const message = formatDiscussionResult(result, options.verbose);
|
|
679
|
+
return {
|
|
680
|
+
success: result.success,
|
|
681
|
+
message,
|
|
682
|
+
data: result,
|
|
683
|
+
exitCode: result.success ? 0 : 1,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
catch (error) {
|
|
687
|
+
// Clear simple progress indicator if not verbose
|
|
688
|
+
if (!options.verbose) {
|
|
689
|
+
console.log(ICONS.cross);
|
|
690
|
+
}
|
|
691
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
692
|
+
return {
|
|
693
|
+
success: false,
|
|
694
|
+
message: `Error during discussion: ${errorMessage}`,
|
|
695
|
+
data: undefined,
|
|
696
|
+
exitCode: 1,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
//# sourceMappingURL=discuss.js.map
|