← Back to PRs

#12308: fix(cli): redirect log output to stderr during completion script generation

by mcaxtr open 2026-02-09 03:44 View on GitHub →
cli size: S trusted-contributor experienced-contributor
## Summary Fixes #12241 When running `source <(openclaw completion --shell zsh)`, ANSI-colored subsystem log messages (e.g. `[plugins] Loaded 3 plugins`) leak onto stdout and corrupt the completion script. Zsh then fails with `bad pattern` errors when it encounters the escape codes. **Root cause:** `enableConsoleCapture()` patches `console.log`, but subsystem loggers write colored messages via `writeConsoleLine()` which uses `rawConsole.log` (the original, unpatched `console.log`). These messages bypass the capture layer and go directly to stdout. **Fix (two parts):** 1. **`run-main.ts`**: Detect the `completion` command early and call `routeLogsToStderr()` right after `enableConsoleCapture()`. This redirects both the patched console and `writeConsoleLine()` output to stderr, keeping stdout clean for the script. 2. **`completion-cli.ts`**: Switch from `console.log(script)` to `process.stdout.write(script)`. Since `routeLogsToStderr()` makes the patched `console.log` redirect to stderr, the completion script itself must use `process.stdout.write()` to stay on stdout. Also adds an async EPIPE/EIO handler for graceful broken-pipe handling (e.g. when piped to `head`), re-throwing non-transient errors. ## Test plan - [x] New test: subsystem logger does not write to stdout when `routeLogsToStderr` is active - [x] New test: `process.stdout.write` bypasses patched console when `forceConsoleToStderr` is active - [x] New test: `console.log` sends to stderr when `routeLogsToStderr` is active (demonstrates need for `process.stdout.write`) - [x] All 3 new tests fail before fix, pass after (TDD) - [x] `pnpm build && pnpm check` passes - [x] Existing tests unaffected (13/13 pass in completion test suite) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR fixes `openclaw completion` output corruption by ensuring all subsystem/console logging is redirected to stderr during completion script generation, while the completion script itself is written directly to stdout. Key changes: - `src/cli/run-main.ts`: detects the `completion` primary command early and calls `routeLogsToStderr()` immediately after `enableConsoleCapture()` so both patched `console.*` and subsystem `writeConsoleLine()` output go to stderr. - `src/cli/completion-cli.ts`: switches from `console.log(script)` to `process.stdout.write()` to keep the actual completion script on stdout even when console output is forced to stderr, and handles broken-pipe errors (EPIPE/EIO) gracefully via the write callback. - `src/cli/completion-cli.stdout.test.ts`: adds tests that assert directly on `process.stdout.write`/`process.stderr.write` and `loggingState.rawConsole.error` to validate no stdout leakage and correct routing when `routeLogsToStderr()` is active. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - Changes are narrowly scoped to completion-script generation, redirecting logs to stderr and writing the script via stdout, with targeted tests validating the intended stdout/stderr behavior and correct handling of broken-pipe errors. - No files require special attention <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs