polydev-ai 1.2.5 → 1.2.7

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/mcp/README.md CHANGED
@@ -57,17 +57,12 @@ export POLYDEV_USER_TOKEN="pd_your_token_here"
57
57
  Add to your Claude Code MCP configuration:
58
58
 
59
59
  ```json
60
- {
61
- "mcpServers": {
62
- "polydev": {
63
- "command": "npx",
64
- "args": ["polydev-ai"],
65
- "env": {
66
- "POLYDEV_USER_TOKEN": "pd_your_token_here"
67
- }
68
- }
69
- }
70
- }
60
+ "polydev": {
61
+ "disabled": false,
62
+ "timeout": 120,
63
+ "type": "http",
64
+ "url": "https://www.polydev.ai/api/mcp"
65
+ },
71
66
  ```
72
67
 
73
68
  ## Available Tools
package/mcp/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.0.0",
3
+ "version": "1.2.6",
4
4
  "description": "Get diverse AI perspectives from multiple LLMs via MCP - supports Cline, Claude Code, and other MCP clients",
5
5
  "main": "stdio-wrapper.js",
6
6
  "bin": {
@@ -212,7 +212,7 @@ class StdioMCPWrapper {
212
212
  // Save status locally to file-based cache
213
213
  await this.saveLocalCliStatus(results);
214
214
 
215
- // NEW: Update database with CLI status
215
+ // Update database with CLI status
216
216
  await this.updateCliStatusInDatabase(results);
217
217
 
218
218
  return {
@@ -250,10 +250,10 @@ class StdioMCPWrapper {
250
250
  results = status;
251
251
  } else {
252
252
  // Get all providers status
253
- const providers = this.cliManager.getAvailableProviders();
253
+ const providers = this.cliManager.getProviders();
254
254
  for (const provider of providers) {
255
255
  const status = await this.cliManager.getCliStatus(provider.id);
256
- results[provider.id] = status[provider.id];
256
+ results[provider.id] = status;
257
257
  }
258
258
  }
259
259
 
@@ -280,7 +280,7 @@ class StdioMCPWrapper {
280
280
  }
281
281
 
282
282
  /**
283
- * Local CLI prompt sending with remote perspectives fallback/supplement
283
+ * Local CLI prompt sending with ALL available CLIs + remote perspectives
284
284
  */
285
285
  async localSendCliPrompt(args) {
286
286
  console.error(`[Stdio Wrapper] Local CLI prompt sending with perspectives`);
@@ -290,49 +290,68 @@ class StdioMCPWrapper {
290
290
 
291
291
  // Ensure timeout_ms is valid (not undefined, null, Infinity, or negative)
292
292
  if (!timeout_ms || timeout_ms === Infinity || timeout_ms < 1 || timeout_ms > 300000) {
293
- timeout_ms = 30000 // Default to 30 seconds
293
+ timeout_ms = 30000; // Default to 30 seconds
294
294
  }
295
295
 
296
296
  if (!prompt) {
297
297
  throw new Error('prompt is required');
298
298
  }
299
299
 
300
- // Auto-select best available provider if none specified
301
- if (!provider_id) {
302
- provider_id = await this.selectBestProvider();
303
- console.error(`[Stdio Wrapper] Auto-selected provider: ${provider_id}`);
304
- }
305
-
306
300
  // Use reasonable timeout for CLI responses (15 seconds instead of 5)
307
301
  const gracefulTimeout = Math.min(timeout_ms, 15000);
308
-
309
- // Start both operations concurrently for better performance
310
- const [localResult, perspectivesResult] = await Promise.allSettled([
311
- this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout),
312
- this.callPerspectivesForCli(args, null)
313
- ]);
314
-
315
- // Process results
316
- const localResponse = localResult.status === 'fulfilled' ? localResult.value : {
317
- success: false,
318
- error: `CLI check failed: ${localResult.reason?.message || 'Unknown error'}`,
319
- latency_ms: gracefulTimeout,
320
- timestamp: new Date().toISOString()
321
- };
322
302
 
323
- const perspectivesResponse = perspectivesResult.status === 'fulfilled' ? perspectivesResult.value : {
324
- success: false,
325
- error: `Perspectives failed: ${perspectivesResult.reason?.message || 'Unknown error'}`,
326
- timestamp: new Date().toISOString()
327
- };
303
+ let localResults = [];
328
304
 
329
- // Record usage locally (file-based analytics) - non-blocking
330
- this.recordLocalUsage(provider_id, prompt, localResponse).catch(err => {
331
- console.error('[Stdio Wrapper] Usage recording failed (non-critical):', err.message);
332
- });
305
+ if (provider_id) {
306
+ // Specific provider requested - use only that one
307
+ console.error(`[Stdio Wrapper] Using specific provider: ${provider_id}`);
308
+ const result = await this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout);
309
+ localResults = [{ provider_id, ...result }];
310
+ } else {
311
+ // No specific provider - use ALL available local CLIs
312
+ console.error(`[Stdio Wrapper] Using all available local CLIs`);
313
+ const availableProviders = await this.getAllAvailableProviders();
314
+
315
+ if (availableProviders.length === 0) {
316
+ console.error(`[Stdio Wrapper] No local CLIs available, will use remote perspectives only`);
317
+ localResults = [];
318
+ } else {
319
+ console.error(`[Stdio Wrapper] Found ${availableProviders.length} available CLIs: ${availableProviders.join(', ')}`);
320
+
321
+ // Run all CLI prompts concurrently
322
+ const cliPromises = availableProviders.map(async (providerId) => {
323
+ try {
324
+ const result = await this.cliManager.sendCliPrompt(providerId, prompt, mode, gracefulTimeout);
325
+ return { provider_id: providerId, ...result };
326
+ } catch (error) {
327
+ console.error(`[Stdio Wrapper] CLI ${providerId} failed:`, error.message);
328
+ return {
329
+ provider_id: providerId,
330
+ success: false,
331
+ error: error.message,
332
+ latency_ms: gracefulTimeout
333
+ };
334
+ }
335
+ });
333
336
 
334
- // Combine results
335
- return this.combineCliAndPerspectives(localResponse, perspectivesResponse, args);
337
+ localResults = await Promise.all(cliPromises);
338
+ }
339
+ }
340
+
341
+ // Get remote perspectives (only for models not covered by local CLIs)
342
+ const perspectivesResult = await this.callPerspectivesForCli(args, localResults);
343
+
344
+ // Record usage for all CLI responses
345
+ for (const localResult of localResults) {
346
+ if (localResult.provider_id) {
347
+ this.recordLocalUsage(localResult.provider_id, prompt, localResult).catch(err => {
348
+ console.error(`[Stdio Wrapper] Usage recording failed for ${localResult.provider_id} (non-critical):`, err.message);
349
+ });
350
+ }
351
+ }
352
+
353
+ // Combine all results
354
+ return this.combineAllCliAndPerspectives(localResults, perspectivesResult, args);
336
355
 
337
356
  } catch (error) {
338
357
  console.error('[Stdio Wrapper] Local CLI prompt error:', error);
@@ -346,43 +365,35 @@ class StdioMCPWrapper {
346
365
  }
347
366
 
348
367
  /**
349
- * Select the best available CLI provider automatically
368
+ * Get all available and authenticated CLI providers
350
369
  */
351
- async selectBestProvider() {
370
+ async getAllAvailableProviders() {
352
371
  try {
353
- const allStatus = await this.cliManager.getCliStatus();
372
+ const results = await this.cliManager.forceCliDetection();
373
+ const availableProviders = [];
354
374
 
355
375
  // Priority order: claude_code > codex_cli > gemini_cli
356
376
  const priorityOrder = ['claude_code', 'codex_cli', 'gemini_cli'];
357
377
 
358
378
  for (const providerId of priorityOrder) {
359
- const status = allStatus[providerId];
379
+ const status = results[providerId];
360
380
  if (status && status.available && status.authenticated) {
361
- return providerId;
362
- }
363
- }
364
-
365
- // If no authenticated provider, return the first available one
366
- for (const providerId of priorityOrder) {
367
- const status = allStatus[providerId];
368
- if (status && status.available) {
369
- return providerId;
381
+ availableProviders.push(providerId);
370
382
  }
371
383
  }
372
384
 
373
- // Default fallback to claude_code (will trigger remote perspectives)
374
- return 'claude_code';
385
+ return availableProviders;
375
386
 
376
387
  } catch (error) {
377
- console.error('[Stdio Wrapper] Provider selection failed, defaulting to claude_code:', error);
378
- return 'claude_code';
388
+ console.error('[Stdio Wrapper] Failed to get available providers:', error);
389
+ return [];
379
390
  }
380
391
  }
381
392
 
382
393
  /**
383
394
  * Call remote perspectives for CLI prompts
384
395
  */
385
- async callPerspectivesForCli(args, localResult) {
396
+ async callPerspectivesForCli(args, localResults) {
386
397
  console.error(`[Stdio Wrapper] Calling remote perspectives for CLI prompt`);
387
398
 
388
399
  try {
@@ -440,76 +451,99 @@ class StdioMCPWrapper {
440
451
  }
441
452
 
442
453
  /**
443
- * Combine local CLI and remote perspectives results
454
+ * Combine multiple CLI results and remote perspectives
444
455
  */
445
- combineCliAndPerspectives(localResult, perspectivesResult, args) {
456
+ combineAllCliAndPerspectives(localResults, perspectivesResult, args) {
446
457
  const combinedResult = {
447
458
  success: true,
448
459
  timestamp: new Date().toISOString(),
449
- provider: args.provider_id,
450
460
  mode: args.mode,
461
+ local_cli_count: localResults.length,
451
462
  sections: {
452
- local: localResult,
463
+ local: localResults,
453
464
  remote: perspectivesResult
454
465
  }
455
466
  };
456
467
 
457
- // Determine overall success and fallback status
458
- if (localResult.success && perspectivesResult.success) {
459
- combinedResult.content = this.formatCombinedResponse(localResult, perspectivesResult, false);
460
- combinedResult.tokens_used = localResult.tokens_used || 0;
461
- combinedResult.latency_ms = localResult.latency_ms || 0;
462
- } else if (!localResult.success && perspectivesResult.success) {
463
- // Fallback case
464
- combinedResult.content = this.formatCombinedResponse(localResult, perspectivesResult, true);
468
+ // Check if any local CLIs succeeded
469
+ const successfulClis = localResults.filter(result => result.success);
470
+ const hasSomeLocalSuccess = successfulClis.length > 0;
471
+
472
+ // Determine overall success and content
473
+ if (hasSomeLocalSuccess && perspectivesResult.success) {
474
+ combinedResult.content = this.formatMultipleCliResponse(localResults, perspectivesResult, false);
475
+ combinedResult.tokens_used = successfulClis.reduce((total, cli) => total + (cli.tokens_used || 0), 0);
476
+ combinedResult.latency_ms = Math.max(...successfulClis.map(cli => cli.latency_ms || 0));
477
+ } else if (!hasSomeLocalSuccess && perspectivesResult.success) {
478
+ // Complete fallback case - no local CLIs worked
479
+ combinedResult.content = this.formatMultipleCliResponse(localResults, perspectivesResult, true);
465
480
  combinedResult.fallback_used = true;
466
481
  combinedResult.tokens_used = 0; // No local tokens used
467
- } else if (localResult.success && !perspectivesResult.success) {
468
- // Local succeeded, remote failed
469
- combinedResult.content = this.formatCombinedResponse(localResult, perspectivesResult, false);
470
- combinedResult.tokens_used = localResult.tokens_used || 0;
471
- combinedResult.latency_ms = localResult.latency_ms || 0;
482
+ } else if (hasSomeLocalSuccess && !perspectivesResult.success) {
483
+ // Local CLIs succeeded, remote failed
484
+ combinedResult.content = this.formatMultipleCliResponse(localResults, perspectivesResult, false);
485
+ combinedResult.tokens_used = successfulClis.reduce((total, cli) => total + (cli.tokens_used || 0), 0);
486
+ combinedResult.latency_ms = Math.max(...successfulClis.map(cli => cli.latency_ms || 0));
472
487
  } else {
473
488
  // Both failed
474
489
  combinedResult.success = false;
475
- combinedResult.error = `Local CLI failed: ${localResult.error}; Perspectives also failed: ${perspectivesResult.error}`;
490
+ const cliErrors = localResults.map(cli => `${cli.provider_id}: ${cli.error}`).join('; ');
491
+ combinedResult.error = `All local CLIs failed: ${cliErrors}; Perspectives also failed: ${perspectivesResult.error}`;
476
492
  }
477
493
 
478
494
  return combinedResult;
479
495
  }
480
496
 
481
497
  /**
482
- * Format combined response text
498
+ * Format multiple CLI responses with remote perspectives
483
499
  */
484
- formatCombinedResponse(localResult, perspectivesResult, isFallback) {
500
+ formatMultipleCliResponse(localResults, perspectivesResult, isFallback) {
485
501
  let formatted = '';
486
502
 
487
- if (localResult.success) {
488
- // Local CLI succeeded
489
- formatted += `🟢 **Local CLI Response** (${localResult.provider} - ${localResult.mode} mode)\n\n`;
490
- formatted += `${localResult.content}\n\n`;
491
- formatted += `*Latency: ${localResult.latency_ms || 0}ms | Tokens: ${localResult.tokens_used || 0}*\n\n`;
492
- formatted += `---\n\n`;
493
- } else if (isFallback) {
494
- // Local CLI failed, using fallback
495
- formatted += `⚠️ **Local CLI unavailable**: ${localResult.error}\n`;
503
+ // Show all local CLI responses
504
+ const successfulClis = localResults.filter(result => result.success);
505
+ const failedClis = localResults.filter(result => !result.success);
506
+
507
+ if (successfulClis.length > 0) {
508
+ // Show successful CLI responses
509
+ for (const cliResult of successfulClis) {
510
+ formatted += `🟢 **Local CLI Response** (${cliResult.provider_id} - ${cliResult.mode || 'args'} mode)\n\n`;
511
+ formatted += `${cliResult.content}\n\n`;
512
+ formatted += `*Latency: ${cliResult.latency_ms || 0}ms | Tokens: ${cliResult.tokens_used || 0}*\n\n`;
513
+ formatted += `---\n\n`;
514
+ }
515
+ }
516
+
517
+ if (failedClis.length > 0 && successfulClis.length === 0) {
518
+ // All local CLIs failed
519
+ formatted += `⚠️ **All Local CLIs Unavailable**\n`;
520
+ for (const cliResult of failedClis) {
521
+ formatted += `- ${cliResult.provider_id}: ${cliResult.error}\n`;
522
+ }
496
523
  formatted += `Using perspectives fallback.\n\n`;
497
524
  formatted += `---\n\n`;
525
+ } else if (failedClis.length > 0) {
526
+ // Some CLIs failed, some succeeded
527
+ formatted += `⚠️ **Some CLIs Failed**\n`;
528
+ for (const cliResult of failedClis) {
529
+ formatted += `- ${cliResult.provider_id}: ${cliResult.error}\n`;
530
+ }
531
+ formatted += `\n---\n\n`;
498
532
  }
499
533
 
534
+ // Add remote perspectives
500
535
  if (perspectivesResult.success) {
501
536
  if (perspectivesResult.raw) {
502
- // Raw content is already formatted - use as-is without any additional title
503
- // The remote server already includes proper headers like "Multiple AI Perspectives"
537
+ // Raw content is already formatted - use as-is
504
538
  formatted += `${perspectivesResult.content}\n\n`;
505
539
  } else {
506
- // Legacy formatting for non-raw content (shouldn't be used with current server)
507
- const title = isFallback ? '🧠 **Perspectives Fallback**' : '🧠 **Supplemental Multi-Model Perspectives**';
540
+ // Legacy formatting
541
+ const title = (successfulClis.length === 0) ? '🧠 **Perspectives Fallback**' : '🧠 **Supplemental Multi-Model Perspectives**';
508
542
  formatted += `${title}\n\n`;
509
543
  formatted += `${perspectivesResult.content}\n\n`;
510
544
  }
511
- } else if (!isFallback) {
512
- // Show remote error only if not in fallback mode
545
+ } else if (successfulClis.length > 0) {
546
+ // Show remote error only if we have local success
513
547
  formatted += `❌ **Perspectives request failed**: ${perspectivesResult.error}\n\n`;
514
548
  }
515
549
 
@@ -523,18 +557,20 @@ class StdioMCPWrapper {
523
557
  console.error('[Stdio Wrapper] Updating CLI status in database...');
524
558
 
525
559
  try {
526
- // Get user token and ID from environment variables
560
+ // Get user token from environment variables
527
561
  const userToken = process.env.POLYDEV_MCP_TOKEN || this.userToken;
528
- const userId = process.env.POLYDEV_USER_ID;
529
562
 
530
- console.error(`[Stdio Wrapper] Using user ID: ${userId}`);
563
+ if (!userToken) {
564
+ console.error('[Stdio Wrapper] No user token available for database updates');
565
+ return;
566
+ }
567
+
531
568
  console.error(`[Stdio Wrapper] Using token: ${userToken ? userToken.substring(0, 20) + '...' : 'MISSING'}`);
532
569
 
533
570
  for (const [providerId, result] of Object.entries(results)) {
534
571
  const statusData = {
535
572
  provider: providerId,
536
573
  status: result.available ? 'available' : 'unavailable',
537
- user_id: userId,
538
574
  mcp_token: userToken,
539
575
  cli_version: result.version || null,
540
576
  cli_path: result.cli_path || null,
@@ -552,7 +588,6 @@ class StdioMCPWrapper {
552
588
  console.error(`[Stdio Wrapper] Updating ${providerId} with data:`, JSON.stringify({
553
589
  provider: statusData.provider,
554
590
  status: statusData.status,
555
- user_id: statusData.user_id,
556
591
  authenticated: statusData.authenticated
557
592
  }, null, 2));
558
593
 
@@ -778,14 +813,20 @@ class StdioMCPWrapper {
778
813
  return `❌ **CLI Error**\n\n${result.error}\n\n*Timestamp: ${result.timestamp}*`;
779
814
  }
780
815
 
781
- // Handle combined CLI + perspectives response
816
+ // Handle combined CLI + perspectives response (single or multiple CLIs)
782
817
  if (result.sections) {
783
818
  return result.content;
784
819
  }
785
820
 
786
821
  if (result.content) {
787
822
  // Standard prompt response
788
- 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}*`;
823
+ const cliCount = result.local_cli_count || 1;
824
+
825
+ if (cliCount > 1) {
826
+ 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}*`;
827
+ } else {
828
+ 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}*`;
829
+ }
789
830
  } else {
790
831
  // Status/detection response
791
832
  let formatted = `✅ **CLI Operation Success**\n\n`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
4
4
  "description": "Agentic workflow assistant with CLI integration - get diverse perspectives from multiple LLMs when stuck or need enhanced reasoning",
5
5
  "keywords": [
6
6
  "mcp",