@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.
Files changed (138) hide show
  1. package/LICENSE +214 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +11 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/bootstrap.d.ts +144 -0
  7. package/dist/bootstrap.d.ts.map +1 -0
  8. package/dist/bootstrap.js +315 -0
  9. package/dist/bootstrap.js.map +1 -0
  10. package/dist/cli.d.ts +14 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +84 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands/ability.d.ts +17 -0
  15. package/dist/commands/ability.d.ts.map +1 -0
  16. package/dist/commands/ability.js +286 -0
  17. package/dist/commands/ability.js.map +1 -0
  18. package/dist/commands/agent.d.ts +18 -0
  19. package/dist/commands/agent.d.ts.map +1 -0
  20. package/dist/commands/agent.js +361 -0
  21. package/dist/commands/agent.js.map +1 -0
  22. package/dist/commands/call.d.ts +15 -0
  23. package/dist/commands/call.d.ts.map +1 -0
  24. package/dist/commands/call.js +503 -0
  25. package/dist/commands/call.js.map +1 -0
  26. package/dist/commands/cleanup.d.ts +18 -0
  27. package/dist/commands/cleanup.d.ts.map +1 -0
  28. package/dist/commands/cleanup.js +300 -0
  29. package/dist/commands/cleanup.js.map +1 -0
  30. package/dist/commands/config.d.ts +16 -0
  31. package/dist/commands/config.d.ts.map +1 -0
  32. package/dist/commands/config.js +513 -0
  33. package/dist/commands/config.js.map +1 -0
  34. package/dist/commands/discuss.d.ts +16 -0
  35. package/dist/commands/discuss.d.ts.map +1 -0
  36. package/dist/commands/discuss.js +700 -0
  37. package/dist/commands/discuss.js.map +1 -0
  38. package/dist/commands/doctor.d.ts +48 -0
  39. package/dist/commands/doctor.d.ts.map +1 -0
  40. package/dist/commands/doctor.js +356 -0
  41. package/dist/commands/doctor.js.map +1 -0
  42. package/dist/commands/guard.d.ts +12 -0
  43. package/dist/commands/guard.d.ts.map +1 -0
  44. package/dist/commands/guard.js +225 -0
  45. package/dist/commands/guard.js.map +1 -0
  46. package/dist/commands/help.d.ts +11 -0
  47. package/dist/commands/help.d.ts.map +1 -0
  48. package/dist/commands/help.js +180 -0
  49. package/dist/commands/help.js.map +1 -0
  50. package/dist/commands/history.d.ts +19 -0
  51. package/dist/commands/history.d.ts.map +1 -0
  52. package/dist/commands/history.js +200 -0
  53. package/dist/commands/history.js.map +1 -0
  54. package/dist/commands/index.d.ts +23 -0
  55. package/dist/commands/index.d.ts.map +1 -0
  56. package/dist/commands/index.js +26 -0
  57. package/dist/commands/index.js.map +1 -0
  58. package/dist/commands/iterate.d.ts +16 -0
  59. package/dist/commands/iterate.d.ts.map +1 -0
  60. package/dist/commands/iterate.js +72 -0
  61. package/dist/commands/iterate.js.map +1 -0
  62. package/dist/commands/list.d.ts +6 -0
  63. package/dist/commands/list.d.ts.map +1 -0
  64. package/dist/commands/list.js +62 -0
  65. package/dist/commands/list.js.map +1 -0
  66. package/dist/commands/mcp.d.ts +16 -0
  67. package/dist/commands/mcp.d.ts.map +1 -0
  68. package/dist/commands/mcp.js +57 -0
  69. package/dist/commands/mcp.js.map +1 -0
  70. package/dist/commands/resume.d.ts +18 -0
  71. package/dist/commands/resume.d.ts.map +1 -0
  72. package/dist/commands/resume.js +208 -0
  73. package/dist/commands/resume.js.map +1 -0
  74. package/dist/commands/review.d.ts +13 -0
  75. package/dist/commands/review.d.ts.map +1 -0
  76. package/dist/commands/review.js +450 -0
  77. package/dist/commands/review.js.map +1 -0
  78. package/dist/commands/run.d.ts +6 -0
  79. package/dist/commands/run.d.ts.map +1 -0
  80. package/dist/commands/run.js +158 -0
  81. package/dist/commands/run.js.map +1 -0
  82. package/dist/commands/scaffold.d.ts +20 -0
  83. package/dist/commands/scaffold.d.ts.map +1 -0
  84. package/dist/commands/scaffold.js +924 -0
  85. package/dist/commands/scaffold.js.map +1 -0
  86. package/dist/commands/session.d.ts +20 -0
  87. package/dist/commands/session.d.ts.map +1 -0
  88. package/dist/commands/session.js +504 -0
  89. package/dist/commands/session.js.map +1 -0
  90. package/dist/commands/setup.d.ts +14 -0
  91. package/dist/commands/setup.d.ts.map +1 -0
  92. package/dist/commands/setup.js +762 -0
  93. package/dist/commands/setup.js.map +1 -0
  94. package/dist/commands/status.d.ts +17 -0
  95. package/dist/commands/status.d.ts.map +1 -0
  96. package/dist/commands/status.js +227 -0
  97. package/dist/commands/status.js.map +1 -0
  98. package/dist/commands/trace.d.ts +6 -0
  99. package/dist/commands/trace.d.ts.map +1 -0
  100. package/dist/commands/trace.js +204 -0
  101. package/dist/commands/trace.js.map +1 -0
  102. package/dist/commands/update.d.ts +24 -0
  103. package/dist/commands/update.d.ts.map +1 -0
  104. package/dist/commands/update.js +296 -0
  105. package/dist/commands/update.js.map +1 -0
  106. package/dist/index.d.ts +8 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +13 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/parser.d.ts +14 -0
  111. package/dist/parser.d.ts.map +1 -0
  112. package/dist/parser.js +288 -0
  113. package/dist/parser.js.map +1 -0
  114. package/dist/types.d.ts +91 -0
  115. package/dist/types.d.ts.map +1 -0
  116. package/dist/types.js +29 -0
  117. package/dist/types.js.map +1 -0
  118. package/dist/utils/dangerous-op-guard.d.ts +33 -0
  119. package/dist/utils/dangerous-op-guard.d.ts.map +1 -0
  120. package/dist/utils/dangerous-op-guard.js +112 -0
  121. package/dist/utils/dangerous-op-guard.js.map +1 -0
  122. package/dist/utils/database.d.ts +85 -0
  123. package/dist/utils/database.d.ts.map +1 -0
  124. package/dist/utils/database.js +184 -0
  125. package/dist/utils/database.js.map +1 -0
  126. package/dist/utils/index.d.ts +7 -0
  127. package/dist/utils/index.d.ts.map +1 -0
  128. package/dist/utils/index.js +7 -0
  129. package/dist/utils/index.js.map +1 -0
  130. package/dist/utils/provider-factory.d.ts +31 -0
  131. package/dist/utils/provider-factory.d.ts.map +1 -0
  132. package/dist/utils/provider-factory.js +109 -0
  133. package/dist/utils/provider-factory.js.map +1 -0
  134. package/dist/utils/storage-instances.d.ts +19 -0
  135. package/dist/utils/storage-instances.d.ts.map +1 -0
  136. package/dist/utils/storage-instances.js +20 -0
  137. package/dist/utils/storage-instances.js.map +1 -0
  138. 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