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.
- package/lib/cliManager.js +122 -35
- 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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
692
|
-
const
|
|
693
|
-
if (
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
if (
|
|
738
|
-
resolve(
|
|
739
|
-
|
|
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
|
-
|
|
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