param( [Parameter(Mandatory = $true)][string]$PromptFile, [Parameter(Mandatory = $true)][string]$OutputFile, [string]$WorkingDirectory = "", [switch]$SkipVerdictParse ) . (Join-Path $PSScriptRoot "Common.ps1") $codexConfig = Get-CodexConfig if ([string]::IsNullOrWhiteSpace($WorkingDirectory)) { $WorkingDirectory = [string]$codexConfig.working_directory } Ensure-Directory -Path (Split-Path -Parent $OutputFile) $promptText = Get-Content -Raw -Path $PromptFile $preferredCmd = Join-Path $env:APPDATA "npm\codex.cmd" $preferredPs1 = Join-Path $env:APPDATA "npm\codex.ps1" if (Test-Path $preferredCmd) { $codexPath = $preferredCmd } elseif (Test-Path $preferredPs1) { $codexPath = $preferredPs1 } else { $codexCommand = Get-Command codex -ErrorAction SilentlyContinue if ($null -eq $codexCommand) { throw "codex executable not found in PATH." } $codexPath = $codexCommand.Source } Write-Utf8File -Path $PromptFile -Content $promptText $stdoutLog = $OutputFile + ".stdout.log" $stderrLog = $OutputFile + ".stderr.log" $model = [string]$codexConfig.model $sessionId = [string]$codexConfig.session_id if ([string]::IsNullOrWhiteSpace($sessionId)) { throw "codex.local.json is missing session_id" } $arguments = @( "-C", $WorkingDirectory, "exec", "resume", $sessionId, "--dangerously-bypass-approvals-and-sandbox" ) if (-not [string]::IsNullOrWhiteSpace($model)) { $arguments += @("--model", $model) } $arguments += @("--output-last-message", $OutputFile, "-") $stdout = @() $stderr = @() try { $mergedOutput = @($promptText | & $codexPath @arguments 2>&1) $exitCode = $LASTEXITCODE foreach ($entry in $mergedOutput) { if ($entry -is [System.Management.Automation.ErrorRecord]) { $stderr += $entry.ToString() } else { $stdout += [string]$entry } } } catch { $exitCode = 1 $stderr = @($_.Exception.Message) } if ($stdout) { Write-Utf8File -Path $stdoutLog -Content ((@($stdout) -join [Environment]::NewLine) + [Environment]::NewLine) } elseif (-not (Test-Path $stdoutLog)) { Write-Utf8File -Path $stdoutLog -Content "" } if ($stderr) { Write-Utf8File -Path $stderrLog -Content ((@($stderr) -join [Environment]::NewLine) + [Environment]::NewLine) } elseif (-not (Test-Path $stderrLog)) { Write-Utf8File -Path $stderrLog -Content "" } if (-not $SkipVerdictParse) { if (Test-Path $OutputFile) { try { $parsedVerdict = Read-CodexReviewVerdict -Path $OutputFile if ($exitCode -ne 0) { $stderr += "Codex returned exit code $exitCode but produced a valid review output. Accepting the review output." Write-Utf8File -Path $stderrLog -Content ((@($stderr) -join [Environment]::NewLine) + [Environment]::NewLine) } return $parsedVerdict } catch { if ($exitCode -eq 0) { throw } } } } elseif (Test-Path $OutputFile) { $rawOutput = (Get-Content -Raw -Path $OutputFile).Trim() if (-not [string]::IsNullOrWhiteSpace($rawOutput)) { if ($exitCode -ne 0) { $stderr += "Codex returned exit code $exitCode but produced non-empty output. Accepting the generated output." Write-Utf8File -Path $stderrLog -Content ((@($stderr) -join [Environment]::NewLine) + [Environment]::NewLine) } return $rawOutput } } if ($exitCode -ne 0) { $errorExcerpt = "" $combined = ((@($stdout) + @($stderr)) -join " ").Trim() if ($combined -match "usage limit") { $errorExcerpt = " Codex local CLI is currently over its usage limit." } throw "Codex master review failed with exit code $exitCode.$errorExcerpt See $stdoutLog and $stderrLog" }