fix: Auto-detect JSON Lines output format for Codex CLI

Problem: Codex CLI uses --json flag to output JSONL events, but executor was using plain text parser. This prevented proper parsing of structured events, breaking session creation.

Root cause: buildCommand() added --json flag for Codex but never communicated this to the output parser. Result: JSONL events treated as raw text → session markers lost.

Solution:
- Extend buildCommand() to return outputFormat
- Auto-detect 'json-lines' when tool is 'codex'
- Use auto-detected format in executeCliTool()
- Properly parse structured events and extract session data

Files modified:
- ccw/src/tools/cli-executor-utils.ts: Add output format auto-detection
- ccw/src/tools/cli-executor-core.ts: Use auto-detected format for parser
- ccw/src/commands/cli.ts: Add debug instrumentation

Verified:
- Codex outputs valid JSONL (confirmed via direct test)
- CLI_EXECUTION_STARTED events broadcast correctly
- Issue was downstream in output parsing, not event transmission
This commit is contained in:
catlog22
2026-01-29 16:38:30 +08:00
parent 0b791c03cf
commit 113dce55c5
3 changed files with 32 additions and 6 deletions

View File

@@ -795,7 +795,7 @@ async function executeCliTool(
const effectiveModel = model || getPrimaryModel(workingDir, tool);
// Build command
const { command, args, useStdin } = buildCommand({
const { command, args, useStdin, outputFormat: autoDetectedFormat } = buildCommand({
tool,
prompt: finalPrompt,
mode,
@@ -806,8 +806,11 @@ async function executeCliTool(
reviewOptions: mode === 'review' ? { uncommitted, base, commit, title } : undefined
});
// Use auto-detected format (from buildCommand) if available, otherwise use passed outputFormat
const finalOutputFormat = autoDetectedFormat || outputFormat;
// Create output parser and IR storage
const parser = createOutputParser(outputFormat);
const parser = createOutputParser(finalOutputFormat);
const allOutputUnits: CliOutputUnit[] = [];
const startTime = Date.now();
@@ -820,7 +823,7 @@ async function executeCliTool(
promptLength: finalPrompt.length,
hasResume: !!resume,
hasCustomId: !!customId,
outputFormat
outputFormat: finalOutputFormat
});
return new Promise((resolve, reject) => {