polydev-ai 1.5.2 → 1.6.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 (2) hide show
  1. package/lib/cliManager.js +122 -35
  2. package/package.json +1 -1
package/lib/cliManager.js CHANGED
@@ -42,6 +42,9 @@ class CLIManager {
42
42
  id: 'claude_code',
43
43
  name: 'Claude Code',
44
44
  command: process.env.CLAUDE_CODE_PATH || 'claude',
45
+ // Model configuration: use env var or default to 'haiku' for speed
46
+ // Options: haiku (fastest), sonnet (balanced), opus (most capable)
47
+ model: process.env.POLYDEV_CLAUDE_MODEL || 'haiku',
45
48
  subcommands: {
46
49
  chat: [],
47
50
  version: ['--version'],
@@ -55,6 +58,9 @@ class CLIManager {
55
58
  id: 'codex_cli',
56
59
  name: 'Codex CLI',
57
60
  command: process.env.CODEX_CLI_PATH || 'codex',
61
+ // Model configuration: use env var or default to 'gpt-4o-mini' for speed
62
+ // Options: gpt-4o-mini (fastest), gpt-4o, o3-mini, o1, etc.
63
+ model: process.env.POLYDEV_CODEX_MODEL || 'gpt-4o-mini',
58
64
  subcommands: {
59
65
  chat: ['chat'],
60
66
  version: ['--version'],
@@ -72,6 +78,8 @@ class CLIManager {
72
78
  id: 'gemini_cli',
73
79
  name: 'Gemini CLI',
74
80
  command: process.env.GEMINI_CLI_PATH || 'gemini',
81
+ // Model configuration for Gemini
82
+ model: process.env.POLYDEV_GEMINI_MODEL || 'gemini-2.0-flash',
75
83
  subcommands: {
76
84
  chat: ['chat'],
77
85
  version: ['--version'],
@@ -437,13 +445,15 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
437
445
  if (providerId === 'codex_cli') {
438
446
  const execArgs = promptVariants.find(args => args.includes('exec')) || promptVariants[0];
439
447
  try {
440
- const content = await this.executeCodexExec(provider.command, execArgs, prompt, timeoutMs);
448
+ // Pass model configuration to Codex
449
+ const content = await this.executeCodexExec(provider.command, execArgs, prompt, timeoutMs, provider.model);
441
450
  return {
442
451
  success: true,
443
452
  content,
444
453
  tokens_used: this.estimateTokens(prompt + content),
445
454
  latency_ms: Date.now() - startTime,
446
455
  provider: providerId,
456
+ model: provider.model,
447
457
  mode: 'args',
448
458
  timestamp: new Date()
449
459
  };
@@ -453,6 +463,7 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
453
463
  error: `CLI execution failed: ${error instanceof Error ? error.message : String(error)}`,
454
464
  latency_ms: Date.now() - startTime,
455
465
  provider: providerId,
466
+ model: provider.model,
456
467
  mode,
457
468
  timestamp: new Date()
458
469
  };
@@ -462,7 +473,9 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
462
473
  let lastErrorMessage = null;
463
474
 
464
475
  for (const promptArgs of promptVariants) {
465
- const args = Array.isArray(promptArgs) ? [...promptArgs, prompt] : [prompt];
476
+ // Add model parameter for Claude Code
477
+ const modelArgs = provider.model ? ['--model', provider.model] : [];
478
+ const args = Array.isArray(promptArgs) ? [...promptArgs, ...modelArgs, prompt] : [...modelArgs, prompt];
466
479
  try {
467
480
  const result = await this.executeCliCommand(
468
481
  provider.command,
@@ -480,6 +493,7 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
480
493
  tokens_used: this.estimateTokens(prompt + content),
481
494
  latency_ms: Date.now() - startTime,
482
495
  provider: providerId,
496
+ model: provider.model,
483
497
  mode: 'args',
484
498
  timestamp: new Date()
485
499
  };
@@ -609,7 +623,7 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
609
623
  });
610
624
  }
611
625
 
612
- async executeCodexExec(executable, commandArgs, prompt, timeoutMs) {
626
+ async executeCodexExec(executable, commandArgs, prompt, timeoutMs, model) {
613
627
  if (!executable) {
614
628
  throw new Error('Missing Codex executable');
615
629
  }
@@ -619,8 +633,12 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
619
633
  }
620
634
 
621
635
  const workingDir = process.cwd();
636
+
637
+ // Build args with model configuration if specified
638
+ const modelArgs = model ? ['-c', `model="${model}"`] : [];
622
639
  const args = [
623
640
  ...commandArgs,
641
+ ...modelArgs,
624
642
  '--sandbox',
625
643
  'workspace-write',
626
644
  '--skip-git-repo-check',
@@ -675,27 +693,73 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
675
693
  const parseCodexOutput = (output) => {
676
694
  if (!output || !output.trim()) return null;
677
695
 
696
+ // First, try to extract the response between "codex" marker and "tokens used"
697
+ // This is the most reliable pattern for Codex CLI output
698
+ const codexMarkerMatch = output.match(/\bcodex\s*\n([\s\S]*?)(?:\n\s*tokens used|\n\s*$)/i);
699
+ if (codexMarkerMatch && codexMarkerMatch[1]) {
700
+ const extracted = codexMarkerMatch[1].trim();
701
+ if (extracted.length > 0 && !extracted.startsWith('ERROR')) {
702
+ return extracted;
703
+ }
704
+ }
705
+
706
+ // Fallback: Try to find bullet point responses
707
+ const bulletMatches = output.match(/•\s*(.+)/g);
708
+ if (bulletMatches && bulletMatches.length > 0) {
709
+ const bulletContent = bulletMatches
710
+ .map(m => m.replace(/^•\s*/, '').trim())
711
+ .filter(s => s.length > 0)
712
+ .join('\n');
713
+ if (bulletContent.length > 0) {
714
+ return bulletContent;
715
+ }
716
+ }
717
+
718
+ // Last resort: Filter out known noise patterns line by line
678
719
  const lines = output.split('\n');
679
720
  const contentLines = [];
680
721
 
681
- // Filter out MCP protocol noise and collect actual content
722
+ // Patterns to skip (Codex-specific noise)
723
+ const noisePatterns = [
724
+ /^\s*$/, // Empty lines
725
+ /^\d{4}-\d{2}-\d{2}T[\d:]+.*?(ERROR|WARN|INFO)/i, // Timestamp logs
726
+ /^OpenAI Codex v[\d.]+/i, // Version banner
727
+ /^-{4,}$/, // Separator lines
728
+ /^workdir:/i, // Header fields
729
+ /^model:/i,
730
+ /^provider:/i,
731
+ /^approval:/i,
732
+ /^sandbox:/i,
733
+ /^reasoning effort:/i,
734
+ /^reasoning summaries:/i,
735
+ /^session id:/i,
736
+ /^user$/i, // "user" marker
737
+ /^thinking$/i, // "thinking" marker
738
+ /^codex$/i, // "codex" marker
739
+ /^tokens used$/i, // Token count header
740
+ /^[\d,]+$/, // Just numbers (token counts)
741
+ /^ERROR:\s*MCP/i, // MCP errors
742
+ /MCP client for .* failed/i, // MCP client failures
743
+ /handshake.*failed/i, // Handshake errors
744
+ /connection closed/i, // Connection errors
745
+ /\[MCP\]/i, // MCP tags
746
+ /^MCP\s/i, // MCP prefix
747
+ /rmcp::transport/i, // Rust MCP transport errors
748
+ /serde error/i, // Serde parsing errors
749
+ /^\*\*.*\*\*$/, // Bold status messages like **Awaiting next steps**
750
+ ];
751
+
682
752
  for (const line of lines) {
683
753
  const trimmedLine = line.trim();
684
- // Skip empty lines and MCP/protocol noise
685
- if (!trimmedLine) continue;
686
- if (trimmedLine.startsWith('MCP ')) continue;
687
- if (trimmedLine.includes('handshake') && trimmedLine.includes('error')) continue;
688
- if (trimmedLine.startsWith('[MCP]')) continue;
689
- if (trimmedLine.startsWith('Error:') && trimmedLine.includes('MCP')) continue;
690
754
 
691
- // Extract bullet content if present, otherwise keep the line
692
- const bulletMatch = trimmedLine.match(/^•\s*(.+)/);
693
- if (bulletMatch) {
694
- contentLines.push(bulletMatch[1].trim());
695
- } else if (!trimmedLine.startsWith('•')) {
696
- // Keep non-bullet content that isn't just whitespace
697
- contentLines.push(trimmedLine);
698
- }
755
+ // Check if line matches any noise pattern
756
+ const isNoise = noisePatterns.some(pattern => pattern.test(trimmedLine));
757
+ if (isNoise) continue;
758
+
759
+ // Skip if line looks like the echoed user prompt (heuristic: contains "?" and is long)
760
+ if (trimmedLine.includes('?') && trimmedLine.length > 20) continue;
761
+
762
+ contentLines.push(trimmedLine);
699
763
  }
700
764
 
701
765
  const result = contentLines.join('\n').trim();
@@ -730,26 +794,49 @@ This is a known issue with @google/gemini-cli@0.3.4 and older Node.js versions.`
730
794
 
731
795
  const trimmedStdout = stdout.trim();
732
796
  const trimmedStderr = stderr.trim();
797
+
798
+ // Combine stdout and stderr for parsing - Codex may output to either
799
+ const combinedOutput = trimmedStdout + '\n' + trimmedStderr;
800
+
801
+ // Always try to parse stdout first, regardless of exit code
802
+ // MCP handshake failures cause non-zero exit but don't prevent valid responses
803
+ const parsedStdout = parseCodexOutput(trimmedStdout);
804
+ if (parsedStdout) {
805
+ resolve(parsedStdout);
806
+ return;
807
+ }
808
+
809
+ // Try parsing combined output (some responses may appear in stderr)
810
+ const parsedCombined = parseCodexOutput(combinedOutput);
811
+ if (parsedCombined) {
812
+ resolve(parsedCombined);
813
+ return;
814
+ }
733
815
 
734
- if (code === 0 && trimmedStdout) {
735
- // Use the robust parser for consistent output handling
736
- const parsed = parseCodexOutput(trimmedStdout);
737
- if (parsed) {
738
- resolve(parsed);
739
- return;
740
- }
741
- // Fallback to raw output if parsing yields nothing
742
- resolve(trimmedStdout);
743
- } else if (code === 0) {
744
- // Code 0 but no stdout - check if stderr has useful info
745
- const parsedStderr = parseCodexOutput(trimmedStderr);
746
- if (parsedStderr) {
747
- resolve(parsedStderr);
748
- return;
816
+ // Only now consider it a failure
817
+ if (code === 0) {
818
+ // Successful exit but no parseable output
819
+ if (trimmedStdout) {
820
+ resolve(trimmedStdout); // Return raw output as fallback
821
+ } else {
822
+ reject(new Error('Codex completed but produced no output'));
749
823
  }
750
- reject(new Error('Codex completed but produced no output'));
751
824
  } else {
752
- reject(new Error(trimmedStderr || trimmedStdout || `Codex exited with code ${code}`));
825
+ // Non-zero exit and no parseable response - this is a real error
826
+ // Filter error message to remove MCP noise
827
+ const errorLines = (trimmedStderr || trimmedStdout).split('\n')
828
+ .filter(line => {
829
+ const l = line.trim().toLowerCase();
830
+ return l.length > 0 &&
831
+ !l.includes('mcp') &&
832
+ !l.includes('handshake') &&
833
+ !l.includes('rmcp::') &&
834
+ !l.includes('serde error');
835
+ })
836
+ .slice(0, 3); // Only first 3 relevant error lines
837
+
838
+ const cleanError = errorLines.join('; ') || `Codex exited with code ${code}`;
839
+ reject(new Error(cleanError));
753
840
  }
754
841
  });
755
842
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.5.2",
3
+ "version": "1.6.0",
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",