Skip to content

fix(setup): make codex install opt-in on the auto target - #156

Merged
vanvasten merged 2 commits into
mainfrom
fix/codex-opt-in-prompt
Aug 26, 2026
Merged

fix(setup): make codex install opt-in on the auto target#156
vanvasten merged 2 commits into
mainfrom
fix/codex-opt-in-prompt

Conversation

@vanvasten

Copy link
Copy Markdown
Contributor

What this does

Codex is optional. ./setup.sh used to silently install cc-settings for the Codex CLI whenever anything named codex was on PATH — surprising users who only wanted Claude, and hard-crashing on machines with a cmux proxy shim that fakes the binary.

Now the auto-detect path asks. If Codex is detected, the prompt defaults to yes. If not, it defaults to no. Non-interactive callers (CI, piped input) get the default silently. Explicit --target=claude|codex|both bypasses the prompt.

Follow-up to #155 — that PR fixed the "codex looks installed but fails on invoke" case for the plugin marketplace probe. This one extends the same reasoning to the --target=auto decision and adds the prompt users actually want.

Summary

  • resolveInstallTarget("auto") now prompts and is async; only caller updated with await.
  • codexCliAvailable() probe now requires the --version stdout to look like a real codex banner. The cmux shim prints Error: codex not found in PATH and exits 0 — exit code alone tripped positive. New pure helper looksLikeCodexVersion is exported so it can be unit-tested (the surrounding spawn+Bun.which path reads PATH from a boot-time snapshot the tests cannot override).
  • main() error catch now unwraps AggregateError.errors so restoreCombinedAfterClaudeFailure no longer swallows the real cause.

Test Plan

  • bun run typecheck
  • bun test tests/codex-install.test.ts -t "shim detection" (4/4 pass, including the two new cases)
  • Real ./setup.sh on a machine with the cmux shim: probe now reports codex unavailable; non-interactive install picks claude, no combined-install path
  • Manual interactive: verify prompt defaults to y when codex is genuinely installed, n when not

Setup crashed on fresh installs where a codex proxy shim lives on PATH
(e.g. cmux CLI shims at $TMPDIR/cmux-cli-shims/.../codex). Bun.which
resolved the shim, so codexCliAvailable() returned true; then
readCodexPluginState invoked `codex plugin list --json`, the shim exited
non-zero with "Error: codex not found in PATH", and the raw error surfaced
as a stack trace instead of falling through to the friendly
"Codex CLI is required for a full Codex install" message.

- codexCliAvailable now probes `codex --version` (3s timeout) after the
  hasCommand check, memoized per process to avoid re-probing across the
  ~15 call sites. Non-zero exit or spawn failure -> unavailable.
- resetCodexCliAvailabilityMemoForTests exported so tests that mutate
  PATH can force a re-probe.
- Two new tests: POSIX shim that exits non-zero (mimics cmux) and empty
  PATH fast path. Windows-gated via describe.skipIf since the shim uses
  #!/bin/sh.

bash setup.sh on the reproducing machine now prints the friendly
"Codex CLI is required for a full Codex install" and exits 1 cleanly,
with no stack trace.
The `auto` target used to install cc-settings for Codex whenever a `codex`
binary was on PATH. Two problems:

1. Not everyone uses codex. Surprise-installing it during `./setup.sh` is
   the wrong default when the user only asked for Claude support.
2. On machines with a cmux CLI shim under $TMPDIR/cmux-cli-shims/*/codex,
   the existing check tripped positive and the install then crashed
   downstream trying to actually run the fake `codex`.

Change: `resolveInstallTarget("auto")` now prompts (Y/n) when codex is
detected and (y/N) when it is not. Non-interactive callers (CI, piped
input) fall through to the default silently. Explicit `--target=...`
still bypasses the prompt.

Also tighten `codexCliAvailable()` so a probe that exits 0 with a bogus
stdout (real cmux behavior: prints "Error: codex not found in PATH" and
exits 0) is treated as unavailable. Exit code alone is not enough; the
output has to look like an actual codex version banner. The banner-shape
check is factored out as an exported pure function so it can be
unit-tested — the surrounding spawn+Bun.which path reads PATH from a
boot-time snapshot the tests cannot override.

Errors that go through `restoreCombinedAfterClaudeFailure` now unwrap
`AggregateError.errors` in the main catch, so the real cause surfaces
instead of the "compensation was incomplete" wrapper.
@vanvasten
vanvasten merged commit 4e91297 into main Aug 26, 2026
15 checks passed
@vanvasten
vanvasten deleted the fix/codex-opt-in-prompt branch August 26, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant