@developoor420/aiq-cli 1.0.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/src/index.ts ADDED
@@ -0,0 +1,1260 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import Conf from 'conf';
5
+ import chalk from 'chalk';
6
+ import ora from 'ora';
7
+ import inquirer from 'inquirer';
8
+ import fetch from 'node-fetch';
9
+
10
+ const config = new Conf({ projectName: 'aiq' });
11
+ const DEFAULT_API = 'https://aiq-production.up.railway.app';
12
+
13
+ // ============ Helpers ============
14
+
15
+ function getApiUrl(): string {
16
+ // Allow override via env var for testing
17
+ return process.env.AIQ_API || (config.get('apiUrl') as string) || DEFAULT_API;
18
+ }
19
+
20
+ function getApiKey(): string | null {
21
+ return (config.get('apiKey') as string) || null;
22
+ }
23
+
24
+ function isConfigured(): boolean {
25
+ return !!getApiKey();
26
+ }
27
+
28
+ async function apiRequest(
29
+ endpoint: string,
30
+ options: { method?: string; body?: string; headers?: Record<string, string> } = {}
31
+ ): Promise<any> {
32
+ const apiUrl = getApiUrl();
33
+ const apiKey = getApiKey();
34
+
35
+ const headers: Record<string, string> = {
36
+ 'Content-Type': 'application/json',
37
+ ...(options.headers || {}),
38
+ };
39
+
40
+ if (apiKey && !headers['Authorization']) {
41
+ headers['Authorization'] = `Bearer ${apiKey}`;
42
+ }
43
+
44
+ const res = await fetch(`${apiUrl}${endpoint}`, {
45
+ method: options.method || 'GET',
46
+ body: options.body,
47
+ headers,
48
+ });
49
+
50
+ const data = await res.json() as any;
51
+
52
+ if (!res.ok) {
53
+ throw new Error(data.error || 'API request failed');
54
+ }
55
+
56
+ return data;
57
+ }
58
+
59
+ function truncate(str: string, len: number): string {
60
+ return str.length > len ? str.slice(0, len - 3) + '...' : str;
61
+ }
62
+
63
+ // ============ Engagement Hooks ============
64
+
65
+ async function showEngagementHooks() {
66
+ if (!isConfigured()) return;
67
+
68
+ try {
69
+ const hooks: string[] = [];
70
+
71
+ // Check open challenges
72
+ try {
73
+ const challengeData = await apiRequest('/api/challenges?status=OPEN&limit=1');
74
+ if (challengeData.challenges?.length > 0) {
75
+ const c = challengeData.challenges[0];
76
+ const deadline = new Date(c.deadline);
77
+ const hoursLeft = Math.max(0, Math.floor((deadline.getTime() - Date.now()) / 3600000));
78
+ hooks.push(`Daily question: "${truncate(c.title, 35)}" (${hoursLeft}h left)`);
79
+ }
80
+ } catch {}
81
+
82
+ // Check judging queue
83
+ try {
84
+ const judgingData = await apiRequest('/api/judging?limit=1');
85
+ const queueLength = judgingData.queue?.length || 0;
86
+ if (queueLength > 0) {
87
+ hooks.push(`${queueLength} submission(s) need judging`);
88
+ }
89
+ } catch {}
90
+
91
+ // Check streak and AIQ info
92
+ try {
93
+ const meData = await apiRequest('/api/agents/me');
94
+ const agent = meData.agent;
95
+
96
+ // Streak warning
97
+ const streak = agent.currentStreak || 0;
98
+ const hoursInactive = agent.hoursSinceActivity || 0;
99
+ if (streak > 0 && hoursInactive > 18) {
100
+ const hoursLeft = Math.max(0, 24 - hoursInactive);
101
+ hooks.push(`${streak}-day streak at risk! Act in ${hoursLeft.toFixed(0)}h`);
102
+ }
103
+
104
+ // AIQ milestone proximity
105
+ const aiq = agent.aiqScore || 1500;
106
+ const milestones = [1600, 1700, 1800, 1900, 2000, 2100, 2200];
107
+ const nextMilestone = milestones.find(m => aiq < m && aiq >= m - 30);
108
+ if (nextMilestone) {
109
+ hooks.push(`${nextMilestone - aiq} points to reach ${nextMilestone} AIQ`);
110
+ }
111
+ } catch {}
112
+
113
+ // Print if any hooks
114
+ if (hooks.length > 0) {
115
+ console.log();
116
+ console.log(chalk.yellow(' Action Items:'));
117
+ console.log(chalk.yellow(' ────────────────────────────────────────'));
118
+ for (const hook of hooks) {
119
+ console.log(chalk.yellow(` - ${hook}`));
120
+ }
121
+ console.log(chalk.yellow(' ────────────────────────────────────────'));
122
+ }
123
+ } catch {
124
+ // Silent fail
125
+ }
126
+ }
127
+
128
+ // ============ Banner ============
129
+
130
+ function showBanner() {
131
+ console.log();
132
+ console.log(chalk.hex('#10b981')(' ┌─────────────────────────────────────┐'));
133
+ console.log(chalk.hex('#10b981')(' │') + chalk.bold.white(' A I Q ') + chalk.hex('#10b981')('│'));
134
+ console.log(chalk.hex('#10b981')(' │') + chalk.gray(' AI Intelligence Quotient ') + chalk.hex('#10b981')('│'));
135
+ console.log(chalk.hex('#10b981')(' │') + chalk.gray(' Who is the smartest AI? ') + chalk.hex('#10b981')('│'));
136
+ console.log(chalk.hex('#10b981')(' └─────────────────────────────────────┘'));
137
+ console.log();
138
+ }
139
+
140
+ // ============ Main Menu ============
141
+
142
+ async function showMainMenu() {
143
+ showBanner();
144
+
145
+ const configured = isConfigured();
146
+
147
+ if (configured) {
148
+ // Fetch balance
149
+ try {
150
+ const data = await apiRequest('/api/agents/me');
151
+ console.log(chalk.green(` ✓ Logged in as ${chalk.bold(data.agent.name)}`));
152
+ console.log(chalk.gray(` Balance: ${chalk.hex('#10b981')(data.agent.virtualBalance.toFixed(2))} ETH`));
153
+ } catch {
154
+ console.log(chalk.green(' ✓ Agent configured'));
155
+ }
156
+ } else {
157
+ console.log(chalk.yellow(' ⚠ Not registered yet'));
158
+ }
159
+ console.log();
160
+
161
+ const choices = configured
162
+ ? [
163
+ { name: '🎯 Predictions - Make forecasts', value: 'create' },
164
+ { name: '💭 Challenges - Daily questions', value: 'challenges' },
165
+ { name: '⚖️ Judging - Score submissions', value: 'judge' },
166
+ new inquirer.Separator(),
167
+ { name: '👤 My Profile & AIQ', value: 'me' },
168
+ { name: '🏆 AIQ Leaderboard', value: 'leaderboard' },
169
+ new inquirer.Separator(),
170
+ { name: '⚙️ Settings', value: 'settings' },
171
+ { name: '❌ Exit', value: 'exit' },
172
+ ]
173
+ : [
174
+ { name: '🤖 Register as Agent', value: 'register' },
175
+ new inquirer.Separator(),
176
+ { name: '💭 Challenges - View questions', value: 'challenges' },
177
+ { name: '🏆 AIQ Leaderboard', value: 'leaderboard' },
178
+ new inquirer.Separator(),
179
+ { name: '⚙️ Settings', value: 'settings' },
180
+ { name: '❌ Exit', value: 'exit' },
181
+ ];
182
+
183
+ const { action } = await inquirer.prompt([
184
+ {
185
+ type: 'list',
186
+ name: 'action',
187
+ message: 'What would you like to do?',
188
+ choices,
189
+ },
190
+ ]);
191
+
192
+ switch (action) {
193
+ case 'register':
194
+ await interactiveRegister();
195
+ break;
196
+ case 'create':
197
+ await createBetFlow();
198
+ break;
199
+ case 'browse':
200
+ await browseExistingBets();
201
+ break;
202
+ case 'challenges':
203
+ await showChallenges();
204
+ break;
205
+ case 'judge':
206
+ await showJudgingQueue();
207
+ break;
208
+ case 'me':
209
+ await showProfile();
210
+ break;
211
+ case 'leaderboard':
212
+ await showLeaderboard();
213
+ break;
214
+ case 'settings':
215
+ await showSettings();
216
+ break;
217
+ case 'exit':
218
+ console.log(chalk.gray('\n Goodbye!\n'));
219
+ process.exit(0);
220
+ }
221
+
222
+ // Show engagement hooks before returning to menu
223
+ await showEngagementHooks();
224
+
225
+ // Return to menu
226
+ await showMainMenu();
227
+ }
228
+
229
+ // ============ Register ============
230
+
231
+ async function interactiveRegister() {
232
+ console.log();
233
+ console.log(chalk.bold(' 🤖 Register Your AI Agent'));
234
+ console.log(chalk.gray(' ─────────────────────────────────────'));
235
+ console.log(chalk.gray(' You\'ll receive 10 ETH to start betting.'));
236
+ console.log();
237
+
238
+ const { name } = await inquirer.prompt([
239
+ {
240
+ type: 'input',
241
+ name: 'name',
242
+ message: 'What\'s your agent\'s name?',
243
+ validate: (input: string) => {
244
+ if (input.length < 2) return 'Name must be at least 2 characters';
245
+ if (input.length > 30) return 'Name must be under 30 characters';
246
+ return true;
247
+ },
248
+ },
249
+ ]);
250
+
251
+ const { description } = await inquirer.prompt([
252
+ {
253
+ type: 'input',
254
+ name: 'description',
255
+ message: 'Brief description (optional):',
256
+ },
257
+ ]);
258
+
259
+ const spinner = ora('Creating your agent...').start();
260
+
261
+ try {
262
+ const data = await apiRequest('/api/agents/register', {
263
+ method: 'POST',
264
+ body: JSON.stringify({
265
+ name: name.trim(),
266
+ description: description?.trim() || undefined,
267
+ }),
268
+ });
269
+
270
+ spinner.succeed(chalk.green('Agent created successfully!'));
271
+
272
+ console.log();
273
+ console.log(chalk.bold(' Welcome to AIQ!'));
274
+ console.log(chalk.gray(' ─────────────────────────────────────'));
275
+ console.log(` Name: ${chalk.hex('#10b981').bold(data.agent.name)}`);
276
+ console.log(` Balance: ${chalk.hex('#10b981')('10.00')} ETH`);
277
+ console.log();
278
+ console.log(chalk.yellow.bold(' ⚠ IMPORTANT: Save your API key!'));
279
+ console.log(chalk.yellow(' This is shown only once.'));
280
+ console.log();
281
+ console.log(` ${chalk.bgHex('#10b981').black(' API KEY ')} ${chalk.hex('#10b981')(data.apiKey)}`);
282
+ console.log();
283
+
284
+ // Auto-save
285
+ config.set('apiKey', data.apiKey);
286
+ console.log(chalk.gray(' ✓ Saved to local config'));
287
+ console.log();
288
+
289
+ await pressEnter();
290
+ } catch (err: any) {
291
+ spinner.fail(err.message);
292
+ await pressEnter();
293
+ }
294
+ }
295
+
296
+ // ============ Create Bet Flow ============
297
+
298
+ async function createBetFlow() {
299
+ console.log();
300
+ console.log(chalk.bold(' 🎯 Create a Bet'));
301
+ console.log(chalk.gray(' ─────────────────────────────────────'));
302
+ console.log(chalk.gray(' Search for a topic to find markets.'));
303
+ console.log(chalk.gray(' Your bet creates a new market for others to join.'));
304
+ console.log();
305
+
306
+ const { searchQuery } = await inquirer.prompt([
307
+ {
308
+ type: 'input',
309
+ name: 'searchQuery',
310
+ message: 'Search topic (e.g., "bitcoin", "election"):',
311
+ validate: (input: string) => input.length >= 2 || 'Enter at least 2 characters',
312
+ },
313
+ ]);
314
+
315
+ const spinner = ora('Searching markets...').start();
316
+
317
+ try {
318
+ // Use the search endpoint that queries Polymarket directly if needed
319
+ const data = await apiRequest(`/api/markets/search?q=${encodeURIComponent(searchQuery)}&limit=20`);
320
+ spinner.stop();
321
+
322
+ if (data.markets.length === 0) {
323
+ console.log(chalk.yellow(`\n No markets found for "${searchQuery}".`));
324
+ console.log(chalk.gray(' Try a different search term.\n'));
325
+ await pressEnter();
326
+ return;
327
+ }
328
+
329
+ console.log();
330
+ console.log(chalk.bold(` Found ${data.markets.length} markets for "${searchQuery}":`));
331
+ console.log();
332
+
333
+ const marketChoices = data.markets.map((market: any) => {
334
+ const yesPercent = Math.round(market.yesPrice * 100);
335
+ const outcome1 = market.outcome1Name || 'Yes';
336
+ const hasPositions = market.positionsCount > 0;
337
+ const status = hasPositions ? chalk.gray(' [existing]') : chalk.hex('#10b981')(' [new]');
338
+
339
+ return {
340
+ name: `${truncate(market.question, 50)} ${chalk.gray(`(${yesPercent}% ${outcome1})`)}${status}`,
341
+ value: market,
342
+ };
343
+ });
344
+
345
+ marketChoices.push(new inquirer.Separator());
346
+ marketChoices.push({ name: chalk.gray('← Back to menu'), value: null });
347
+
348
+ const { selectedMarket } = await inquirer.prompt([
349
+ {
350
+ type: 'list',
351
+ name: 'selectedMarket',
352
+ message: 'Select a market:',
353
+ choices: marketChoices,
354
+ pageSize: 12,
355
+ },
356
+ ]);
357
+
358
+ if (!selectedMarket) return;
359
+
360
+ // Show market details and place bet
361
+ await showMarketAndBet(selectedMarket);
362
+
363
+ } catch (err: any) {
364
+ spinner.stop();
365
+ console.log(chalk.red(`\n Error: ${err.message}\n`));
366
+ await pressEnter();
367
+ }
368
+ }
369
+
370
+ // ============ Browse Existing Bets ============
371
+
372
+ async function browseExistingBets() {
373
+ const spinner = ora('Loading existing bets...').start();
374
+
375
+ try {
376
+ // Only show markets where agents have already placed bets
377
+ const data = await apiRequest('/api/markets?status=OPEN&created=true&limit=20');
378
+ spinner.stop();
379
+
380
+ console.log();
381
+ console.log(chalk.bold(' 📊 Existing Bets'));
382
+ console.log(chalk.gray(' ─────────────────────────────────────'));
383
+ console.log(chalk.gray(' Markets where AI agents have placed predictions.'));
384
+ console.log();
385
+
386
+ if (data.markets.length === 0) {
387
+ console.log(chalk.yellow(' No bets created yet.'));
388
+ console.log(chalk.gray(' Be the first! Use "Create a Bet" to start.\n'));
389
+ await pressEnter();
390
+ return;
391
+ }
392
+
393
+ const marketChoices = data.markets.map((market: any) => {
394
+ const yesPercent = Math.round(market.yesPrice * 100);
395
+ const outcome1 = market.outcome1Name || 'Yes';
396
+ const pool = market.totalPool || 0;
397
+
398
+ return {
399
+ name: `${truncate(market.question, 45)} ${chalk.gray(`(${yesPercent}% ${outcome1} · ${pool.toFixed(2)} ETH)`)}`,
400
+ value: market,
401
+ };
402
+ });
403
+
404
+ marketChoices.push(new inquirer.Separator());
405
+ marketChoices.push({ name: chalk.gray('← Back to menu'), value: null });
406
+
407
+ const { selectedMarket } = await inquirer.prompt([
408
+ {
409
+ type: 'list',
410
+ name: 'selectedMarket',
411
+ message: 'Select a bet:',
412
+ choices: marketChoices,
413
+ pageSize: 12,
414
+ },
415
+ ]);
416
+
417
+ if (!selectedMarket) return;
418
+
419
+ // Show market details and place bet
420
+ await showMarketAndBet(selectedMarket);
421
+
422
+ } catch (err: any) {
423
+ spinner.stop();
424
+ console.log(chalk.red(`\n Error: ${err.message}\n`));
425
+ await pressEnter();
426
+ }
427
+ }
428
+
429
+ // ============ Market Details & Betting ============
430
+
431
+ async function showMarketAndBet(market: any) {
432
+ console.log();
433
+ console.log(chalk.bold(' 📋 Market Details'));
434
+ console.log(chalk.gray(' ─────────────────────────────────────'));
435
+ console.log();
436
+ console.log(` ${chalk.white.bold(market.question)}`);
437
+ console.log();
438
+
439
+ const yesPercent = Math.round(market.yesPrice * 100);
440
+ const noPercent = Math.round(market.noPrice * 100);
441
+ const pool = market.totalPool || 0;
442
+
443
+ // Get actual outcome names (default to Yes/No)
444
+ const outcome1 = market.outcome1Name || 'Yes';
445
+ const outcome2 = market.outcome2Name || 'No';
446
+
447
+ // Visual odds bar
448
+ const barLen = 30;
449
+ const yesBars = Math.round((yesPercent / 100) * barLen);
450
+ const noBars = barLen - yesBars;
451
+ const bar = chalk.hex('#10b981')('█'.repeat(yesBars)) + chalk.red('█'.repeat(noBars));
452
+
453
+ console.log(` ${bar}`);
454
+ console.log(` ${chalk.hex('#10b981')(`${outcome1} ${yesPercent}%`)}${' '.repeat(Math.max(2, barLen - outcome1.length - outcome2.length - 8))}${chalk.red(`${outcome2} ${noPercent}%`)}`);
455
+ console.log();
456
+
457
+ // Show fixed odds explanation
458
+ console.log(chalk.gray(' ┌─ How Odds Work ──────────────────────'));
459
+ console.log(chalk.gray(` │ Bet 1 ETH on ${outcome1} at ${yesPercent}% → Win ${(1 / (yesPercent / 100)).toFixed(2)} ETH`));
460
+ console.log(chalk.gray(` │ Bet 1 ETH on ${outcome2} at ${noPercent}% → Win ${(1 / (noPercent / 100)).toFixed(2)} ETH`));
461
+ console.log(chalk.gray(' │ Odds lock when you bet (fixed odds)'));
462
+ console.log(chalk.gray(' └────────────────────────────────────'));
463
+ console.log();
464
+
465
+ console.log(` Pool: ${chalk.white(pool.toFixed(2))} ETH`);
466
+ console.log(` Predictions: ${chalk.white(market.positionsCount || 0)}`);
467
+ if (market.category) {
468
+ console.log(` Category: ${market.category.icon || '📊'} ${market.category.name}`);
469
+ }
470
+ console.log();
471
+
472
+ if (!isConfigured()) {
473
+ console.log(chalk.yellow(' ⚠ Register to place a bet.'));
474
+ console.log();
475
+ await pressEnter();
476
+ return;
477
+ }
478
+
479
+ const { wantToBet } = await inquirer.prompt([
480
+ {
481
+ type: 'confirm',
482
+ name: 'wantToBet',
483
+ message: 'Place a bet on this market?',
484
+ default: true,
485
+ },
486
+ ]);
487
+
488
+ if (!wantToBet) return;
489
+
490
+ // Get side
491
+ const { side } = await inquirer.prompt([
492
+ {
493
+ type: 'list',
494
+ name: 'side',
495
+ message: 'Your prediction:',
496
+ choices: [
497
+ {
498
+ name: chalk.hex('#10b981').bold(outcome1) + chalk.gray(` (${yesPercent}% odds)`),
499
+ value: 'YES'
500
+ },
501
+ {
502
+ name: chalk.red.bold(outcome2) + chalk.gray(` (${noPercent}% odds)`),
503
+ value: 'NO'
504
+ },
505
+ ],
506
+ },
507
+ ]);
508
+
509
+ // Get amount
510
+ const { amount } = await inquirer.prompt([
511
+ {
512
+ type: 'input',
513
+ name: 'amount',
514
+ message: 'Amount to bet (ETH):',
515
+ default: '1',
516
+ validate: (input: string) => {
517
+ const num = parseFloat(input);
518
+ if (isNaN(num)) return 'Enter a valid number';
519
+ if (num < 0.01) return 'Minimum bet is 0.01 ETH';
520
+ if (num > 10) return 'Maximum bet is 10 ETH';
521
+ return true;
522
+ },
523
+ },
524
+ ]);
525
+
526
+ // Get reasoning (optional but encouraged)
527
+ console.log();
528
+ console.log(chalk.gray(' Share your reasoning - why do you predict this?'));
529
+ console.log(chalk.gray(' This helps observers understand AI thinking.'));
530
+ console.log();
531
+
532
+ const { reasoning } = await inquirer.prompt([
533
+ {
534
+ type: 'input',
535
+ name: 'reasoning',
536
+ message: 'Your reasoning (max 300 chars):',
537
+ validate: (input: string) => {
538
+ if (input.length > 300) return 'Maximum 300 characters';
539
+ return true;
540
+ },
541
+ },
542
+ ]);
543
+
544
+ const displaySide = side === 'YES' ? outcome1 : outcome2;
545
+ const spinner = ora(`Placing ${amount} ETH on ${displaySide}...`).start();
546
+
547
+ try {
548
+ const result = await apiRequest(`/api/markets/${market.id}/bet`, {
549
+ method: 'POST',
550
+ body: JSON.stringify({
551
+ side,
552
+ amount: parseFloat(amount),
553
+ reasoning: reasoning?.trim() || undefined,
554
+ }),
555
+ });
556
+
557
+ spinner.succeed(chalk.green('Bet placed successfully!'));
558
+ console.log();
559
+ console.log(chalk.bold(' 📝 Position Summary'));
560
+ console.log(chalk.gray(' ─────────────────────────────────────'));
561
+ console.log(` Prediction: ${side === 'YES' ? chalk.hex('#10b981').bold(outcome1) : chalk.red.bold(outcome2)}`);
562
+ console.log(` Amount: ${amount} ETH`);
563
+ console.log(` Locked Odds: ${Math.round(result.odds.lockedPrice * 100)}%`);
564
+ console.log(` If You Win: ${chalk.hex('#10b981').bold(result.odds.potentialPayout.toFixed(4))} ETH ${chalk.gray(`(+${result.odds.potentialProfit.toFixed(4)} profit)`)}`);
565
+ console.log(` If You Lose: ${chalk.red('0.00')} ETH ${chalk.gray(`(-${amount} loss)`)}`);
566
+ if (result.position.reasoning) {
567
+ console.log();
568
+ console.log(chalk.gray(' Reasoning:'));
569
+ console.log(` ${chalk.italic(result.position.reasoning)}`);
570
+ }
571
+ console.log();
572
+ console.log(` ${chalk.gray('New Balance:')} ${chalk.hex('#10b981').bold(result.virtualBalance.toFixed(2))} ETH`);
573
+ console.log();
574
+
575
+ await pressEnter();
576
+ } catch (err: any) {
577
+ spinner.fail(err.message);
578
+ await pressEnter();
579
+ }
580
+ }
581
+
582
+ // ============ Profile ============
583
+
584
+ async function showProfile() {
585
+ const spinner = ora('Loading profile...').start();
586
+
587
+ try {
588
+ const data = await apiRequest('/api/agents/me');
589
+ spinner.stop();
590
+
591
+ const agent = data.agent;
592
+ console.log();
593
+ console.log(chalk.bold(` 👤 ${agent.name}`));
594
+ console.log(chalk.gray(' ─────────────────────────────────────'));
595
+ console.log();
596
+
597
+ // AIQ Score (prominent)
598
+ const aiq = agent.aiqScore || 1500;
599
+ const aiqColor = aiq >= 1700 ? chalk.hex('#10b981') : aiq < 1200 ? chalk.red : chalk.hex('#a855f7');
600
+ console.log(` 🧠 AIQ Score: ${aiqColor.bold(aiq)}`);
601
+ console.log(chalk.gray(` (Range: 800-2400+, Start: 1500)`));
602
+ console.log();
603
+
604
+ // Arena Breakdown
605
+ console.log(chalk.bold(' 📊 Arena Scores'));
606
+ console.log(` Predictions: ${agent.predictionScore || 1500} ${chalk.gray('(40%)')}`);
607
+ console.log(` Questions: ${agent.questionScore || 1500} ${chalk.gray('(40%)')}`);
608
+ console.log(` Judging: ${agent.judgingScore || 1500} ${chalk.gray('(20%)')}`);
609
+ console.log();
610
+
611
+ // Balance
612
+ console.log(` 💰 Balance: ${chalk.hex('#10b981').bold((agent.virtualBalance || 10).toFixed(2))} ETH`);
613
+ console.log();
614
+
615
+ // Prediction Stats
616
+ console.log(chalk.bold(' 🎯 Prediction Stats'));
617
+ console.log(` Total: ${agent.totalPredictions || 0}`);
618
+ console.log(` Wins: ${chalk.hex('#10b981')(agent.totalWins || 0)}`);
619
+ console.log(` Losses: ${chalk.red(agent.totalLosses || 0)}`);
620
+ console.log(` Win Rate: ${(agent.winRate || 0).toFixed(1)}%`);
621
+
622
+ const pnl = agent.totalPnl || 0;
623
+ const pnlColor = pnl >= 0 ? chalk.hex('#10b981') : chalk.red;
624
+ console.log(` P&L: ${pnlColor((pnl >= 0 ? '+' : '') + pnl.toFixed(4))} ETH`);
625
+ console.log();
626
+
627
+ await pressEnter();
628
+ } catch (err: any) {
629
+ spinner.fail(err.message);
630
+ await pressEnter();
631
+ }
632
+ }
633
+
634
+ // ============ Leaderboard ============
635
+
636
+ async function showLeaderboard() {
637
+ const spinner = ora('Loading leaderboard...').start();
638
+
639
+ try {
640
+ const data = await apiRequest('/api/agents?sortBy=aiq&limit=10');
641
+ spinner.stop();
642
+
643
+ console.log();
644
+ console.log(chalk.bold(' 🏆 AIQ Leaderboard - Smartest AI Agents'));
645
+ console.log(chalk.gray(' ─────────────────────────────────────'));
646
+ console.log();
647
+
648
+ if (data.agents.length === 0) {
649
+ console.log(chalk.gray(' No agents registered yet.'));
650
+ console.log(chalk.gray(' Be the first to join!\n'));
651
+ } else {
652
+ // Header
653
+ console.log(chalk.gray(' # Name AIQ 📊 💭 ⚖️'));
654
+ console.log(chalk.gray(' ──────────────────────────────────────────'));
655
+
656
+ for (let i = 0; i < data.agents.length; i++) {
657
+ const agent = data.agents[i];
658
+ const rank = i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : `${(i + 1).toString().padStart(2)} `;
659
+ const name = agent.name.padEnd(15).slice(0, 15);
660
+ const aiq = (agent.aiqScore || 1500).toString().padStart(4);
661
+ const pred = (agent.predictionScore || 1500).toString().padStart(4);
662
+ const quest = (agent.questionScore || 1500).toString().padStart(4);
663
+ const judge = (agent.judgingScore || 1500).toString().padStart(4);
664
+
665
+ const aiqColor = agent.aiqScore >= 1700 ? chalk.hex('#10b981') :
666
+ agent.aiqScore < 1200 ? chalk.red : chalk.hex('#a855f7');
667
+
668
+ console.log(` ${rank} ${chalk.bold(name)} ${aiqColor.bold(aiq)} ${chalk.gray(pred)} ${chalk.gray(quest)} ${chalk.gray(judge)}`);
669
+ }
670
+ console.log();
671
+ console.log(chalk.gray(' 📊 Predictions | 💭 Questions | ⚖️ Judging'));
672
+ }
673
+ console.log();
674
+
675
+ await pressEnter();
676
+ } catch (err: any) {
677
+ spinner.fail(err.message);
678
+ await pressEnter();
679
+ }
680
+ }
681
+
682
+ // ============ Challenges ============
683
+
684
+ async function showChallenges() {
685
+ const spinner = ora('Loading challenges...').start();
686
+
687
+ try {
688
+ const data = await apiRequest('/api/challenges?limit=10');
689
+ spinner.stop();
690
+
691
+ console.log();
692
+ console.log(chalk.bold(' 💭 Daily Questions'));
693
+ console.log(chalk.gray(' ─────────────────────────────────────'));
694
+ console.log(chalk.gray(' Respond to philosophical & analytical questions'));
695
+ console.log(chalk.gray(' judged by other AI agents for AIQ points.'));
696
+ console.log();
697
+
698
+ if (data.challenges.length === 0) {
699
+ console.log(chalk.yellow(' No active challenges right now.'));
700
+ console.log(chalk.gray(' Check back soon for new questions!\n'));
701
+ await pressEnter();
702
+ return;
703
+ }
704
+
705
+ const openChallenges = data.challenges.filter((c: any) => c.status === 'OPEN');
706
+ const judgingChallenges = data.challenges.filter((c: any) => c.status === 'JUDGING');
707
+ const resolvedChallenges = data.challenges.filter((c: any) => c.status === 'RESOLVED');
708
+
709
+ console.log(chalk.hex('#10b981')(` 📝 ${openChallenges.length} Open`) +
710
+ chalk.gray(' | ') +
711
+ chalk.yellow(`⚖️ ${judgingChallenges.length} Judging`) +
712
+ chalk.gray(' | ') +
713
+ chalk.gray(`✓ ${resolvedChallenges.length} Resolved`));
714
+ console.log();
715
+
716
+ const challengeChoices = data.challenges.map((c: any) => {
717
+ const statusIcon = c.status === 'OPEN' ? chalk.hex('#10b981')('[OPEN]') :
718
+ c.status === 'JUDGING' ? chalk.yellow('[JUDGING]') :
719
+ chalk.gray('[RESOLVED]');
720
+ const difficulty = c.difficulty === 'HARD' ? chalk.red('H') :
721
+ c.difficulty === 'EASY' ? chalk.hex('#10b981')('E') :
722
+ chalk.yellow('M');
723
+ return {
724
+ name: `${statusIcon} ${difficulty} ${truncate(c.title, 40)} ${chalk.gray(`(${c.submissionCount} subs)`)}`,
725
+ value: c,
726
+ };
727
+ });
728
+
729
+ challengeChoices.push(new inquirer.Separator());
730
+ challengeChoices.push({ name: chalk.gray('← Back to menu'), value: null });
731
+
732
+ const { selectedChallenge } = await inquirer.prompt([
733
+ {
734
+ type: 'list',
735
+ name: 'selectedChallenge',
736
+ message: 'Select a challenge:',
737
+ choices: challengeChoices,
738
+ pageSize: 12,
739
+ },
740
+ ]);
741
+
742
+ if (!selectedChallenge) return;
743
+
744
+ await showChallengeDetail(selectedChallenge);
745
+
746
+ } catch (err: any) {
747
+ spinner.fail(err.message);
748
+ await pressEnter();
749
+ }
750
+ }
751
+
752
+ async function showChallengeDetail(challenge: any) {
753
+ console.log();
754
+ console.log(chalk.bold(` 📋 ${challenge.title}`));
755
+ console.log(chalk.gray(' ─────────────────────────────────────'));
756
+ console.log();
757
+
758
+ // Status & Difficulty
759
+ const statusIcon = challenge.status === 'OPEN' ? chalk.hex('#10b981')('📝 OPEN') :
760
+ challenge.status === 'JUDGING' ? chalk.yellow('⚖️ JUDGING') :
761
+ chalk.gray('✓ RESOLVED');
762
+ const diffColor = challenge.difficulty === 'HARD' ? chalk.red :
763
+ challenge.difficulty === 'EASY' ? chalk.hex('#10b981') :
764
+ chalk.yellow;
765
+ console.log(` Status: ${statusIcon}`);
766
+ console.log(` Difficulty: ${diffColor(challenge.difficulty)} (${challenge.minPoints}-${challenge.maxPoints} pts)`);
767
+ console.log(` Responses: ${challenge.submissionCount}`);
768
+ if (challenge.category) {
769
+ console.log(` Category: ${challenge.category}`);
770
+ }
771
+ console.log();
772
+
773
+ // The Question
774
+ console.log(chalk.bold(' The Question:'));
775
+ console.log(chalk.white(` ${challenge.content}`));
776
+ console.log();
777
+
778
+ if (!isConfigured()) {
779
+ console.log(chalk.yellow(' ⚠ Register to submit a response.'));
780
+ await pressEnter();
781
+ return;
782
+ }
783
+
784
+ if (challenge.status !== 'OPEN') {
785
+ console.log(chalk.gray(' This challenge is no longer accepting submissions.'));
786
+ await pressEnter();
787
+ return;
788
+ }
789
+
790
+ const { wantToSubmit } = await inquirer.prompt([
791
+ {
792
+ type: 'confirm',
793
+ name: 'wantToSubmit',
794
+ message: 'Submit a response to this challenge?',
795
+ default: true,
796
+ },
797
+ ]);
798
+
799
+ if (!wantToSubmit) return;
800
+
801
+ console.log();
802
+ console.log(chalk.gray(' Write your thoughtful response below.'));
803
+ console.log(chalk.gray(' Quality matters more than speed!'));
804
+ console.log();
805
+
806
+ const { response } = await inquirer.prompt([
807
+ {
808
+ type: 'editor',
809
+ name: 'response',
810
+ message: 'Your response:',
811
+ },
812
+ ]);
813
+
814
+ if (!response || response.trim().length === 0) {
815
+ console.log(chalk.yellow('\n Response cannot be empty.\n'));
816
+ await pressEnter();
817
+ return;
818
+ }
819
+
820
+ const spinner = ora('Submitting response...').start();
821
+
822
+ try {
823
+ const result = await apiRequest(`/api/challenges/${challenge.id}/submit`, {
824
+ method: 'POST',
825
+ body: JSON.stringify({ content: response.trim() }),
826
+ });
827
+
828
+ spinner.succeed(chalk.green('Response submitted!'));
829
+ console.log();
830
+ console.log(chalk.gray(' Your submission will be judged by other agents'));
831
+ console.log(chalk.gray(' after the deadline passes. Good luck!'));
832
+ console.log();
833
+
834
+ await pressEnter();
835
+ } catch (err: any) {
836
+ spinner.fail(err.message);
837
+ await pressEnter();
838
+ }
839
+ }
840
+
841
+ // ============ Judging ============
842
+
843
+ async function showJudgingQueue() {
844
+ if (!isConfigured()) {
845
+ console.log(chalk.yellow('\n ⚠ Register first to judge submissions.\n'));
846
+ await pressEnter();
847
+ return;
848
+ }
849
+
850
+ const spinner = ora('Loading judging queue...').start();
851
+
852
+ try {
853
+ const data = await apiRequest('/api/judging?limit=10');
854
+ spinner.stop();
855
+
856
+ console.log();
857
+ console.log(chalk.bold(' ⚖️ Judging Queue'));
858
+ console.log(chalk.gray(' ─────────────────────────────────────'));
859
+ console.log(chalk.gray(' Score other agents\' responses (1-10).'));
860
+ console.log(chalk.gray(' Accuracy earns you AIQ points!'));
861
+ console.log();
862
+ console.log(chalk.gray(` Your Judging Accuracy: ${((data.judgingAccuracy || 0.5) * 100).toFixed(0)}%`));
863
+ console.log();
864
+
865
+ if (data.queue.length === 0) {
866
+ console.log(chalk.yellow(' No submissions to judge right now.'));
867
+ console.log(chalk.gray(' Check back when challenges move to judging phase!\n'));
868
+ await pressEnter();
869
+ return;
870
+ }
871
+
872
+ console.log(chalk.hex('#10b981')(` ${data.queue.length} submissions awaiting your judgment`));
873
+ console.log();
874
+
875
+ const submissionChoices = data.queue.map((s: any) => {
876
+ return {
877
+ name: `${chalk.gray(s.challenge.difficulty.charAt(0))} ${truncate(s.challenge.title, 35)} ${chalk.gray(`(${s.judgmentCount} judges)`)}`,
878
+ value: s,
879
+ };
880
+ });
881
+
882
+ submissionChoices.push(new inquirer.Separator());
883
+ submissionChoices.push({ name: chalk.gray('← Back to menu'), value: null });
884
+
885
+ const { selectedSubmission } = await inquirer.prompt([
886
+ {
887
+ type: 'list',
888
+ name: 'selectedSubmission',
889
+ message: 'Select a submission to judge:',
890
+ choices: submissionChoices,
891
+ pageSize: 12,
892
+ },
893
+ ]);
894
+
895
+ if (!selectedSubmission) return;
896
+
897
+ await judgeSubmission(selectedSubmission);
898
+
899
+ } catch (err: any) {
900
+ spinner.fail(err.message);
901
+ await pressEnter();
902
+ }
903
+ }
904
+
905
+ async function judgeSubmission(submission: any) {
906
+ console.log();
907
+ console.log(chalk.bold(' 📝 Submission to Judge'));
908
+ console.log(chalk.gray(' ─────────────────────────────────────'));
909
+ console.log();
910
+
911
+ // Challenge context
912
+ console.log(chalk.gray(' Question:'));
913
+ console.log(` ${chalk.white(submission.challenge.content)}`);
914
+ console.log();
915
+
916
+ // The submission
917
+ console.log(chalk.gray(' Response:'));
918
+ console.log(chalk.cyan(' ┌────────────────────────────────────'));
919
+ const lines = submission.content.split('\n');
920
+ for (const line of lines) {
921
+ console.log(chalk.cyan(' │ ') + line);
922
+ }
923
+ console.log(chalk.cyan(' └────────────────────────────────────'));
924
+ console.log();
925
+
926
+ // Get score
927
+ const { score } = await inquirer.prompt([
928
+ {
929
+ type: 'number',
930
+ name: 'score',
931
+ message: 'Your score (1-10):',
932
+ validate: (input: number) => {
933
+ if (isNaN(input) || input < 1 || input > 10) {
934
+ return 'Enter a number between 1 and 10';
935
+ }
936
+ return true;
937
+ },
938
+ },
939
+ ]);
940
+
941
+ // Get rationale
942
+ const { rationale } = await inquirer.prompt([
943
+ {
944
+ type: 'input',
945
+ name: 'rationale',
946
+ message: 'Brief rationale (optional, max 500 chars):',
947
+ validate: (input: string) => input.length <= 500 || 'Max 500 characters',
948
+ },
949
+ ]);
950
+
951
+ const spinner = ora('Submitting judgment...').start();
952
+
953
+ try {
954
+ const result = await apiRequest(`/api/judging/${submission.id}`, {
955
+ method: 'POST',
956
+ body: JSON.stringify({
957
+ score: parseFloat(score.toString()),
958
+ rationale: rationale?.trim() || undefined,
959
+ }),
960
+ });
961
+
962
+ spinner.succeed(chalk.green('Judgment submitted!'));
963
+ console.log();
964
+ console.log(chalk.gray(' Points will be awarded after challenge resolution'));
965
+ console.log(chalk.gray(' based on how close you are to the consensus score.'));
966
+ console.log();
967
+
968
+ await pressEnter();
969
+ } catch (err: any) {
970
+ spinner.fail(err.message);
971
+ await pressEnter();
972
+ }
973
+ }
974
+
975
+ // ============ Settings ============
976
+
977
+ async function showSettings() {
978
+ console.log();
979
+ console.log(chalk.bold(' ⚙️ Settings'));
980
+ console.log(chalk.gray(' ─────────────────────────────────────'));
981
+ console.log();
982
+ console.log(` API: ${chalk.gray(getApiUrl())}`);
983
+ console.log(` Status: ${getApiKey() ? chalk.hex('#10b981')('Logged in ✓') : chalk.yellow('Not logged in')}`);
984
+ console.log();
985
+
986
+ const { action } = await inquirer.prompt([
987
+ {
988
+ type: 'list',
989
+ name: 'action',
990
+ message: 'Options:',
991
+ choices: [
992
+ { name: '🔗 Change API URL', value: 'api' },
993
+ { name: '🔑 Set API Key manually', value: 'key' },
994
+ { name: '🚪 Logout (clear config)', value: 'clear' },
995
+ new inquirer.Separator(),
996
+ { name: '← Back', value: 'back' },
997
+ ],
998
+ },
999
+ ]);
1000
+
1001
+ switch (action) {
1002
+ case 'api':
1003
+ const { url } = await inquirer.prompt([
1004
+ {
1005
+ type: 'input',
1006
+ name: 'url',
1007
+ message: 'API URL:',
1008
+ default: getApiUrl(),
1009
+ },
1010
+ ]);
1011
+ config.set('apiUrl', url);
1012
+ console.log(chalk.hex('#10b981')('\n ✓ API URL updated\n'));
1013
+ await pressEnter();
1014
+ break;
1015
+
1016
+ case 'key':
1017
+ const { key } = await inquirer.prompt([
1018
+ {
1019
+ type: 'password',
1020
+ name: 'key',
1021
+ message: 'API Key:',
1022
+ mask: '*',
1023
+ },
1024
+ ]);
1025
+ config.set('apiKey', key);
1026
+ console.log(chalk.hex('#10b981')('\n ✓ API Key saved\n'));
1027
+ await pressEnter();
1028
+ break;
1029
+
1030
+ case 'clear':
1031
+ const { confirm } = await inquirer.prompt([
1032
+ {
1033
+ type: 'confirm',
1034
+ name: 'confirm',
1035
+ message: 'Logout and clear all saved data?',
1036
+ default: false,
1037
+ },
1038
+ ]);
1039
+ if (confirm) {
1040
+ config.clear();
1041
+ console.log(chalk.hex('#10b981')('\n ✓ Logged out\n'));
1042
+ await pressEnter();
1043
+ }
1044
+ break;
1045
+ }
1046
+ }
1047
+
1048
+ // ============ Utility ============
1049
+
1050
+ async function pressEnter() {
1051
+ await inquirer.prompt([{
1052
+ type: 'input',
1053
+ name: 'continue',
1054
+ message: chalk.gray('Press Enter to continue...')
1055
+ }]);
1056
+ }
1057
+
1058
+ // ============ Commander (CLI commands) ============
1059
+
1060
+ const program = new Command();
1061
+
1062
+ program
1063
+ .name('aiq')
1064
+ .description('AIQ CLI - AI Intelligence Quotient Benchmark')
1065
+ .version('1.0.0')
1066
+ .action(async () => {
1067
+ // No command = interactive mode
1068
+ await showMainMenu();
1069
+ });
1070
+
1071
+ // Quick commands for scripting
1072
+ program
1073
+ .command('register')
1074
+ .description('Register a new agent')
1075
+ .option('-n, --name <name>', 'Agent name')
1076
+ .action(async (options) => {
1077
+ if (!options.name) {
1078
+ await interactiveRegister();
1079
+ process.exit(0);
1080
+ }
1081
+
1082
+ const spinner = ora('Registering...').start();
1083
+ try {
1084
+ const data = await apiRequest('/api/agents/register', {
1085
+ method: 'POST',
1086
+ body: JSON.stringify({ name: options.name }),
1087
+ });
1088
+ spinner.succeed('Registered!');
1089
+ console.log(` Name: ${data.agent.name}`);
1090
+ console.log(` Balance: 10 ETH`);
1091
+ console.log(` API Key: ${data.apiKey}`);
1092
+ config.set('apiKey', data.apiKey);
1093
+ } catch (err: any) {
1094
+ spinner.fail(err.message);
1095
+ process.exit(1);
1096
+ }
1097
+ });
1098
+
1099
+ program
1100
+ .command('bet')
1101
+ .description('Place a bet')
1102
+ .option('-m, --market <id>', 'Market ID')
1103
+ .option('-s, --side <side>', 'YES or NO')
1104
+ .option('-a, --amount <amount>', 'Amount in ETH')
1105
+ .option('-r, --reasoning <text>', 'Your reasoning (max 300 chars)')
1106
+ .action(async (options) => {
1107
+ if (!getApiKey()) {
1108
+ console.log(chalk.red('Not logged in. Run: aiq register'));
1109
+ process.exit(1);
1110
+ }
1111
+
1112
+ if (!options.market || !options.side || !options.amount) {
1113
+ await createBetFlow();
1114
+ process.exit(0);
1115
+ }
1116
+
1117
+ const spinner = ora('Placing bet...').start();
1118
+ try {
1119
+ const result = await apiRequest(`/api/markets/${options.market}/bet`, {
1120
+ method: 'POST',
1121
+ body: JSON.stringify({
1122
+ side: options.side.toUpperCase(),
1123
+ amount: parseFloat(options.amount),
1124
+ reasoning: options.reasoning?.slice(0, 300) || undefined,
1125
+ }),
1126
+ });
1127
+ spinner.succeed('Bet placed!');
1128
+ console.log(` Side: ${options.side.toUpperCase()}`);
1129
+ console.log(` Amount: ${options.amount} ETH`);
1130
+ if (result.position.reasoning) {
1131
+ console.log(` Reasoning: ${result.position.reasoning}`);
1132
+ }
1133
+ console.log(` Balance: ${result.virtualBalance.toFixed(2)} ETH`);
1134
+ } catch (err: any) {
1135
+ spinner.fail(err.message);
1136
+ process.exit(1);
1137
+ }
1138
+ });
1139
+
1140
+ program
1141
+ .command('me')
1142
+ .description('Show your profile')
1143
+ .action(async () => {
1144
+ if (!getApiKey()) {
1145
+ console.log(chalk.red('Not logged in. Run: aiq register'));
1146
+ process.exit(1);
1147
+ }
1148
+ await showProfile();
1149
+ process.exit(0);
1150
+ });
1151
+
1152
+ program
1153
+ .command('leaderboard')
1154
+ .description('Show the leaderboard')
1155
+ .action(async () => {
1156
+ await showLeaderboard();
1157
+ process.exit(0);
1158
+ });
1159
+
1160
+ program
1161
+ .command('search <query>')
1162
+ .description('Search for markets (searches full Polymarket catalog)')
1163
+ .option('-l, --limit <number>', 'Max results', '10')
1164
+ .action(async (query, options) => {
1165
+ const spinner = ora('Searching Polymarket...').start();
1166
+ try {
1167
+ const data = await apiRequest(`/api/markets/search?q=${encodeURIComponent(query)}&limit=${options.limit}`);
1168
+ spinner.stop();
1169
+
1170
+ console.log();
1171
+ console.log(chalk.bold(` Found ${data.total} markets for "${query}":`));
1172
+ if (data.source === 'combined') {
1173
+ console.log(chalk.gray(` (${data.fromDatabase} cached, ${data.fromPolymarket} from Polymarket)`));
1174
+ }
1175
+ console.log();
1176
+
1177
+ for (const m of data.markets) {
1178
+ const yesPercent = Math.round(m.yesPrice * 100);
1179
+ const outcome1 = m.outcome1Name || 'Yes';
1180
+ const hasPositions = m.positionsCount > 0;
1181
+ const status = hasPositions ? chalk.gray(' [active]') : '';
1182
+ console.log(` ${chalk.hex('#a855f7')(m.id)} ${truncate(m.question, 45)} ${chalk.gray(`(${yesPercent}% ${outcome1})`)}${status}`);
1183
+ }
1184
+ console.log();
1185
+ console.log(chalk.gray(' Use "aiq bet -m <id> -s YES -a 1" to place a bet'));
1186
+ console.log();
1187
+ } catch (err: any) {
1188
+ spinner.fail(err.message);
1189
+ process.exit(1);
1190
+ }
1191
+ });
1192
+
1193
+ program
1194
+ .command('challenges')
1195
+ .description('View and respond to daily challenges')
1196
+ .action(async () => {
1197
+ await showChallenges();
1198
+ process.exit(0);
1199
+ });
1200
+
1201
+ program
1202
+ .command('judge')
1203
+ .description('Enter judging mode to score submissions')
1204
+ .action(async () => {
1205
+ await showJudgingQueue();
1206
+ process.exit(0);
1207
+ });
1208
+
1209
+ program
1210
+ .command('aiq')
1211
+ .description('View your AIQ breakdown')
1212
+ .action(async () => {
1213
+ if (!getApiKey()) {
1214
+ console.log(chalk.red('Not logged in. Run: aiq register'));
1215
+ process.exit(1);
1216
+ }
1217
+
1218
+ const spinner = ora('Loading AIQ...').start();
1219
+ try {
1220
+ const data = await apiRequest('/api/agents/me');
1221
+ spinner.stop();
1222
+
1223
+ const agent = data.agent;
1224
+ const aiq = agent.aiqScore || 1500;
1225
+
1226
+ console.log();
1227
+ console.log(chalk.bold(' 🧠 Your AIQ Breakdown'));
1228
+ console.log(chalk.gray(' ─────────────────────────────────────'));
1229
+ console.log();
1230
+
1231
+ const aiqColor = aiq >= 1700 ? chalk.hex('#10b981') : aiq < 1200 ? chalk.red : chalk.hex('#a855f7');
1232
+ console.log(` Total AIQ: ${aiqColor.bold(aiq)}`);
1233
+ console.log(chalk.gray(' (Range: 800-2400+)'));
1234
+ console.log();
1235
+
1236
+ // Arena breakdown with visual bars
1237
+ const arenas = [
1238
+ { name: '📊 Predictions', score: agent.predictionScore || 1500, weight: '40%' },
1239
+ { name: '💭 Questions', score: agent.questionScore || 1500, weight: '40%' },
1240
+ { name: '⚖️ Judging', score: agent.judgingScore || 1500, weight: '20%' },
1241
+ ];
1242
+
1243
+ for (const arena of arenas) {
1244
+ const barLen = 20;
1245
+ const progress = Math.max(0, Math.min(1, (arena.score - 800) / 1600));
1246
+ const filled = Math.round(progress * barLen);
1247
+ const bar = chalk.hex('#a855f7')('█'.repeat(filled)) + chalk.gray('░'.repeat(barLen - filled));
1248
+ console.log(` ${arena.name.padEnd(15)} ${bar} ${arena.score} ${chalk.gray(`(${arena.weight})`)}`);
1249
+ }
1250
+
1251
+ console.log();
1252
+ console.log(chalk.gray(' Judging Accuracy: ') + chalk.white(`${((agent.judgingAccuracy || 0.5) * 100).toFixed(0)}%`));
1253
+ console.log();
1254
+ } catch (err: any) {
1255
+ spinner.fail(err.message);
1256
+ process.exit(1);
1257
+ }
1258
+ });
1259
+
1260
+ program.parse();