polydev-ai 1.8.99 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/mcp/stdio-wrapper.js +149 -116
  2. package/package.json +1 -1
@@ -438,16 +438,14 @@ Token will be saved automatically after login.`
438
438
  result: {
439
439
  content: [{
440
440
  type: 'text',
441
- text: `╭─────────────────────────────────────────╮
442
- │ ALREADY AUTHENTICATED ✓ │
443
- ╰─────────────────────────────────────────╯
441
+ text: `ALREADY AUTHENTICATED
442
+ =====================
444
443
 
445
- 🔐 Your token is configured and ready.
444
+ Your token is configured and ready.
446
445
 
447
- 🤖 Available commands:
448
- /polydev:ask - Query multiple AI models
449
- /polydev:auth - Check status & credits
450
- • get_perspectives - Direct tool call
446
+ Available commands:
447
+ /polydev:ask Query multiple AI models
448
+ /polydev:auth Check status & credits
451
449
 
452
450
  To re-login: npx polydev-ai`
453
451
  }]
@@ -507,19 +505,20 @@ To re-login: npx polydev-ai`
507
505
  result: {
508
506
  content: [{
509
507
  type: 'text',
510
- text: `╭─────────────────────────────────────────╮
511
- │ BROWSER OPENED FOR LOGIN 🌐 │
512
- ╰─────────────────────────────────────────╯
508
+ text: `LOGIN SUCCESSFUL
509
+ ================
513
510
 
514
- 📱 Complete authentication in your browser.
511
+ Token saved to:
512
+ ~/.polydev.env
513
+ ~/.zshrc
515
514
 
516
- After login:
517
- • Token will be saved automatically
518
- • You can safely reconnect MCP or restart IDE
519
- • Use /polydev:auth to check status
515
+ IMPORTANT: Restart your IDE to activate.
520
516
 
521
- Polling for authentication completion...
522
- (Session expires in 10 minutes)`
517
+ After restart, you can:
518
+ /polydev:ask Query multiple AI models
519
+ /polydev:auth Check status & credits
520
+
521
+ Dashboard: https://polydev.ai/dashboard`
523
522
  }]
524
523
  }
525
524
  };
@@ -569,7 +568,7 @@ After login:
569
568
  this.isAuthenticated = true;
570
569
  this._freshLogin = true;
571
570
 
572
- console.error('[Polydev] Login successful! Token saved.');
571
+ console.error('[Polydev] Login successful! Token saved.');
573
572
  console.error('[Polydev] You can now use Polydev tools.');
574
573
  console.error('[Polydev] No restart required - token is active immediately.');
575
574
  return; // Stop polling
@@ -1096,7 +1095,7 @@ Error: ${error.message}`
1096
1095
  </a>
1097
1096
  <button onclick="window.close()" class="secondary-btn">Close Window</button>
1098
1097
 
1099
- <p class="note">✓ Token saved. Restart your IDE to activate.</p>
1098
+ <p class="note">Token saved. Restart your IDE to activate.</p>
1100
1099
  <div class="countdown" id="countdown">
1101
1100
  <div class="countdown-spinner"></div>
1102
1101
  <span>Closing in <strong id="seconds">5</strong>s...</span>
@@ -1407,93 +1406,129 @@ Error: ${error.message}`
1407
1406
  const result = await this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout, model);
1408
1407
  localResults = [{ provider_id, ...result }];
1409
1408
  } else {
1410
- // No specific provider - use allProviders from dashboard in order
1411
- // For each provider: try CLI first if available, otherwise use API
1412
- console.error(`[Stdio Wrapper] Using allProviders from dashboard (max: ${maxPerspectives})`);
1409
+ // No specific provider - SMART ROUTING: CLIs first (free), then API/credits
1410
+ // Priority order: Available CLIs > User's API keys > Credits tier models
1411
+ // If a configured API/credits model can be accessed through an available CLI, use CLI
1412
+ console.error(`[Stdio Wrapper] Smart routing: CLIs first, then API/credits (max: ${maxPerspectives})`);
1413
1413
 
1414
1414
  // Get available CLIs for checking
1415
1415
  const { available: availableClis } = await this.getAllAvailableProviders();
1416
1416
  console.error(`[Stdio Wrapper] Available CLIs: ${availableClis.join(', ') || 'none'}`);
1417
1417
 
1418
- // Use allProviders from API (full list including API-only providers)
1419
- // Falls back to CLI-only providers if allProviders not available
1418
+ // Use allProviders from API (user's configured providers from dashboard)
1420
1419
  const allProviders = this.allProviders || [];
1421
1420
 
1422
- if (allProviders.length === 0) {
1423
- // Fallback: use legacy CLI-only flow
1424
- console.error(`[Stdio Wrapper] No allProviders, using legacy CLI-only flow`);
1425
- // NOTE: Use length check because empty array [] is truthy in JS
1426
- const userOrder = (this.userProviderOrder && this.userProviderOrder.length > 0)
1427
- ? this.userProviderOrder
1428
- : ['claude_code', 'codex_cli', 'gemini_cli'];
1429
- const cliProviders = userOrder.slice(0, maxPerspectives).filter(p => availableClis.includes(p));
1421
+ // CLI to provider name mapping
1422
+ const cliToProviderMap = {
1423
+ 'claude_code': 'anthropic',
1424
+ 'gemini_cli': 'google',
1425
+ 'codex_cli': 'openai'
1426
+ };
1427
+
1428
+ // Provider name normalization (matches model-preferences API)
1429
+ const normalizeProvider = (provider) => {
1430
+ const map = {
1431
+ 'gemini': 'google',
1432
+ 'google-ai': 'google',
1433
+ 'x-ai': 'xai',
1434
+ 'open-ai': 'openai',
1435
+ 'anthropic-ai': 'anthropic'
1436
+ };
1437
+ const lower = provider.toLowerCase();
1438
+ return map[lower] || lower;
1439
+ };
1440
+
1441
+ // CLI priority order: Claude Code first, then Gemini, then Codex
1442
+ const cliPriorityOrder = ['claude_code', 'gemini_cli', 'codex_cli'];
1443
+
1444
+ // Build merged provider list: CLIs first, then API-only
1445
+ const finalProviders = [];
1446
+ const usedProviderNames = new Set();
1447
+
1448
+ // STEP 1: Add ALL available CLIs first (in priority order) - they're FREE
1449
+ for (const cliId of cliPriorityOrder) {
1450
+ if (finalProviders.length >= maxPerspectives) break;
1451
+ if (!availableClis.includes(cliId)) continue;
1452
+
1453
+ const providerName = cliToProviderMap[cliId];
1454
+ usedProviderNames.add(providerName);
1455
+
1456
+ // Check if user has a configured model for this provider (from API keys or credits)
1457
+ // If so, use that model through CLI instead of burning API credits
1458
+ const configuredProvider = allProviders.find(p => {
1459
+ const normalized = normalizeProvider(p.provider);
1460
+ return normalized === providerName;
1461
+ });
1462
+
1463
+ finalProviders.push({
1464
+ provider: providerName,
1465
+ model: configuredProvider?.model || null,
1466
+ cliId: cliId,
1467
+ source: 'cli',
1468
+ tier: configuredProvider?.tier || null
1469
+ });
1470
+
1471
+ console.error(`[Stdio Wrapper] [CLI-FIRST] Added CLI: ${cliId} (${providerName})${configuredProvider ? ` with configured model: ${configuredProvider.model}` : ' with CLI default'}`);
1472
+ }
1473
+
1474
+ // STEP 2: Fill remaining slots with API/credits providers (skip those already covered by CLI)
1475
+ for (const p of allProviders) {
1476
+ if (finalProviders.length >= maxPerspectives) break;
1477
+
1478
+ const normalizedProvider = normalizeProvider(p.provider);
1479
+ if (usedProviderNames.has(normalizedProvider)) {
1480
+ console.error(`[Stdio Wrapper] [CLI-FIRST] Skipping ${normalizedProvider} (already covered by CLI)`);
1481
+ continue;
1482
+ }
1483
+
1484
+ usedProviderNames.add(normalizedProvider);
1485
+ finalProviders.push({
1486
+ ...p,
1487
+ provider: normalizedProvider,
1488
+ source: 'api'
1489
+ });
1430
1490
 
1431
- const cliPromises = cliProviders.map(async (providerId) => {
1491
+ console.error(`[Stdio Wrapper] [CLI-FIRST] Added API/credits: ${normalizedProvider} (${p.model})${p.tier ? ` [${p.tier}]` : ''}`);
1492
+ }
1493
+
1494
+ console.error(`[Stdio Wrapper] Final provider list (${finalProviders.length}/${maxPerspectives}): ${finalProviders.map(p => `${p.provider}[${p.source}]`).join(', ')}`);
1495
+
1496
+ // Separate into CLI entries (local execution) vs API entries (remote execution)
1497
+ const cliProviderEntries = finalProviders.filter(p => p.source === 'cli');
1498
+ const apiOnlyProviders = finalProviders.filter(p => p.source === 'api');
1499
+
1500
+ console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviderEntries.map(p => p.cliId).join(', ') || 'none'}, API-only=${apiOnlyProviders.map(p => p.provider).join(', ') || 'none'}`);
1501
+
1502
+ // Run all CLI prompts concurrently
1503
+ if (cliProviderEntries.length > 0) {
1504
+ const cliPromises = cliProviderEntries.map(async (providerEntry) => {
1432
1505
  try {
1433
- const model = modelPreferences[providerId] || null;
1434
- const result = await this.cliManager.sendCliPrompt(providerId, prompt, mode, gracefulTimeout, model);
1506
+ const model = providerEntry.model || modelPreferences[providerEntry.cliId] || null;
1507
+ if (model) {
1508
+ console.error(`[Stdio Wrapper] Using model for ${providerEntry.cliId}: ${model}`);
1509
+ }
1510
+ const result = await this.cliManager.sendCliPrompt(providerEntry.cliId, prompt, mode, gracefulTimeout, model);
1435
1511
  return {
1436
- provider_id: providerId,
1437
- original_provider: providerId,
1512
+ provider_id: providerEntry.cliId,
1513
+ original_provider: providerEntry.provider,
1438
1514
  ...result
1439
1515
  };
1440
1516
  } catch (error) {
1441
- return { provider_id: providerId, success: false, error: error.message };
1517
+ console.error(`[Stdio Wrapper] CLI ${providerEntry.cliId} failed:`, error.message);
1518
+ return {
1519
+ provider_id: providerEntry.cliId,
1520
+ original_provider: providerEntry.provider,
1521
+ success: false,
1522
+ error: error.message,
1523
+ latency_ms: gracefulTimeout
1524
+ };
1442
1525
  }
1443
1526
  });
1444
1527
  localResults = await Promise.all(cliPromises);
1445
- } else {
1446
- // NEW: Use allProviders list (includes CLI + API-only providers)
1447
- const providersToUse = allProviders.slice(0, maxPerspectives);
1448
- console.error(`[Stdio Wrapper] Using ${providersToUse.length} providers from dashboard`);
1449
-
1450
- // Separate into CLI providers vs API-only providers
1451
- const cliProviderEntries = [];
1452
- const apiOnlyProviders = [];
1453
-
1454
- for (const p of providersToUse) {
1455
- if (p.cliId && availableClis.includes(p.cliId)) {
1456
- // Has CLI and CLI is available
1457
- cliProviderEntries.push(p);
1458
- } else {
1459
- // No CLI or CLI unavailable - needs API
1460
- apiOnlyProviders.push(p);
1461
- }
1462
- }
1463
-
1464
- console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviderEntries.map(p => p.cliId).join(', ') || 'none'}, API-only=${apiOnlyProviders.map(p => p.provider).join(', ') || 'none'}`);
1465
-
1466
- // Run all CLI prompts concurrently
1467
- if (cliProviderEntries.length > 0) {
1468
- const cliPromises = cliProviderEntries.map(async (providerEntry) => {
1469
- try {
1470
- const model = providerEntry.model || modelPreferences[providerEntry.cliId] || null;
1471
- if (model) {
1472
- console.error(`[Stdio Wrapper] Using model for ${providerEntry.cliId}: ${model}`);
1473
- }
1474
- const result = await this.cliManager.sendCliPrompt(providerEntry.cliId, prompt, mode, gracefulTimeout, model);
1475
- return {
1476
- provider_id: providerEntry.cliId,
1477
- original_provider: providerEntry.provider,
1478
- ...result
1479
- };
1480
- } catch (error) {
1481
- console.error(`[Stdio Wrapper] CLI ${providerEntry.cliId} failed:`, error.message);
1482
- return {
1483
- provider_id: providerEntry.cliId,
1484
- original_provider: providerEntry.provider,
1485
- success: false,
1486
- error: error.message,
1487
- latency_ms: gracefulTimeout
1488
- };
1489
- }
1490
- });
1491
- localResults = await Promise.all(cliPromises);
1492
- }
1493
-
1494
- // Store API-only providers info for remote API call
1495
- this._apiOnlyProviders = apiOnlyProviders;
1496
1528
  }
1529
+
1530
+ // Store API-only providers info for remote API call
1531
+ this._apiOnlyProviders = apiOnlyProviders;
1497
1532
  }
1498
1533
 
1499
1534
  // Calculate how many successful local perspectives we got
@@ -1957,16 +1992,16 @@ Error: ${error.message}`
1957
1992
  const modelDisplay = cliResult.model_used && cliResult.model_used !== 'cli_default' && cliResult.model_used !== 'cli_default_fallback'
1958
1993
  ? cliResult.model_used
1959
1994
  : cliResult.provider_id;
1960
- formatted += `🟢 **Local CLI Response** (${modelDisplay})\n\n`;
1995
+ formatted += `Local CLI Response (${modelDisplay})\n\n`;
1961
1996
  formatted += `${cleanCliResponse(cliResult.content)}\n\n`;
1962
- formatted += `*Latency: ${cliResult.latency_ms || 0}ms | Tokens: ${cliResult.tokens_used || 0}*\n\n`;
1997
+ formatted += `Latency: ${cliResult.latency_ms || 0}ms | Tokens: ${cliResult.tokens_used || 0}\n\n`;
1963
1998
  formatted += `---\n\n`;
1964
1999
  }
1965
2000
  }
1966
2001
 
1967
2002
  if (failedClis.length > 0 && successfulClis.length === 0) {
1968
2003
  // All local CLIs failed
1969
- formatted += `⚠️ **All Local CLIs Unavailable**\n`;
2004
+ formatted += `[Warning] All Local CLIs Unavailable\n`;
1970
2005
  for (const cliResult of failedClis) {
1971
2006
  formatted += `- ${cliResult.provider_id}: ${cliResult.error}\n`;
1972
2007
  }
@@ -1974,7 +2009,7 @@ Error: ${error.message}`
1974
2009
  formatted += `---\n\n`;
1975
2010
  } else if (failedClis.length > 0) {
1976
2011
  // Some CLIs failed, some succeeded
1977
- formatted += `⚠️ **Some CLIs Failed**\n`;
2012
+ formatted += `[Warning] Some CLIs Failed\n`;
1978
2013
  for (const cliResult of failedClis) {
1979
2014
  formatted += `- ${cliResult.provider_id}: ${cliResult.error}\n`;
1980
2015
  }
@@ -1988,13 +2023,13 @@ Error: ${error.message}`
1988
2023
  formatted += `${safePersp.content}\n\n`;
1989
2024
  } else {
1990
2025
  // Legacy formatting
1991
- const title = (successfulClis.length === 0) ? '🧠 **Perspectives Fallback**' : '🧠 **Supplemental Multi-Model Perspectives**';
2026
+ const title = (successfulClis.length === 0) ? 'Perspectives Fallback' : 'Supplemental Multi-Model Perspectives';
1992
2027
  formatted += `${title}\n\n`;
1993
2028
  formatted += `${safePersp.content}\n\n`;
1994
2029
  }
1995
2030
  } else if (successfulClis.length > 0) {
1996
2031
  // Show remote error only if we have local success
1997
- formatted += `❌ **Perspectives request failed**: ${safePersp.error || 'Unknown error'}\n\n`;
2032
+ formatted += `❌ Perspectives request failed: ${safePersp.error || 'Unknown error'}\n\n`;
1998
2033
  }
1999
2034
 
2000
2035
  return formatted.trim();
@@ -2257,28 +2292,28 @@ Error: ${error.message}`
2257
2292
  const cliCount = result.local_cli_count || 1;
2258
2293
 
2259
2294
  if (cliCount > 1) {
2260
- return `✅ **Multi-CLI Response** (${cliCount} local CLIs + perspectives)\n\n${result.content}\n\n*Total Latency: ${result.latency_ms || 0}ms | Total Tokens: ${result.tokens_used || 0} | ${result.timestamp}*`;
2295
+ return `Multi-CLI Response (${cliCount} local CLIs + perspectives)\n\n${result.content}\n\nTotal Latency: ${result.latency_ms || 0}ms | Total Tokens: ${result.tokens_used || 0} | ${result.timestamp}`;
2261
2296
  } else {
2262
- return `✅ **CLI Response** (${result.provider || 'Unknown'} - ${result.mode || 'unknown'} mode)\n\n${result.content}\n\n*Latency: ${result.latency_ms || 0}ms | Tokens: ${result.tokens_used || 0} | ${result.timestamp}*`;
2297
+ return `CLI Response (${result.provider || 'Unknown'} - ${result.mode || 'unknown'} mode)\n\n${result.content}\n\nLatency: ${result.latency_ms || 0}ms | Tokens: ${result.tokens_used || 0} | ${result.timestamp}`;
2263
2298
  }
2264
2299
  } else {
2265
2300
  // Status/detection response
2266
- let formatted = `✅ **CLI Operation Success**\n\n`;
2301
+ let formatted = `CLI Operation Success\n\n`;
2267
2302
  formatted += `${result.message}\n\n`;
2268
2303
 
2269
2304
  if (result.results) {
2270
- formatted += `**Results:**\n`;
2305
+ formatted += `Results:\n`;
2271
2306
  for (const [providerId, status] of Object.entries(result.results)) {
2272
- const icon = status.available ? '🟢' : '🔴';
2273
- const authIcon = status.authenticated ? '🔐' : '🔓';
2274
- formatted += `- ${icon} ${authIcon} **${providerId}**: ${status.available ? 'Available' : 'Not Available'}`;
2307
+ const icon = status.available ? '[+]' : '[-]';
2308
+ const authStatus = status.authenticated ? '[auth]' : '[no-auth]';
2309
+ formatted += `- ${icon} ${authStatus} ${providerId}: ${status.available ? 'Available' : 'Not Available'}`;
2275
2310
  if (status.version) formatted += ` (${status.version})`;
2276
2311
  if (status.error) formatted += ` - ${status.error}`;
2277
2312
  formatted += `\n`;
2278
2313
  }
2279
2314
  }
2280
2315
 
2281
- formatted += `\n*${result.local_only ? 'Local execution' : 'Remote execution'} | ${result.timestamp}*`;
2316
+ formatted += `\n${result.local_only ? 'Local execution' : 'Remote execution'} | ${result.timestamp}`;
2282
2317
  return formatted;
2283
2318
  }
2284
2319
  }
@@ -2544,22 +2579,20 @@ Error: ${error.message}`
2544
2579
  result: {
2545
2580
  content: [{
2546
2581
  type: 'text',
2547
- text: `╭─────────────────────────────────────────╮
2548
- │ LOGIN SUCCESSFUL! ✓ │
2549
- ╰─────────────────────────────────────────╯
2582
+ text: `LOGIN SUCCESSFUL
2583
+ ================
2550
2584
 
2551
- 🔐 Token saved to:
2552
- ~/.polydev.env
2553
- ~/.zshrc
2585
+ Token saved to:
2586
+ ~/.polydev.env
2587
+ ~/.zshrc
2554
2588
 
2555
- ⚠️ IMPORTANT: Restart your IDE to activate.
2589
+ IMPORTANT: Restart your IDE to activate.
2556
2590
 
2557
- 🤖 After restart, you can:
2558
- • Use /polydev:ask to query multiple AI models
2559
- • Use /polydev:auth to check status & credits
2560
- • Use get_perspectives tool directly
2591
+ After restart, you can:
2592
+ /polydev:ask Query multiple AI models
2593
+ /polydev:auth Check status & credits
2561
2594
 
2562
- 📊 Dashboard: https://polydev.ai/dashboard`
2595
+ Dashboard: https://polydev.ai/dashboard`
2563
2596
  }]
2564
2597
  }
2565
2598
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.8.99",
3
+ "version": "1.9.1",
4
4
  "engines": {
5
5
  "node": ">=20.x <=22.x"
6
6
  },