Skip to content

fix: set spawn cwd for workingDirectory to work around codex -C bug#138

Merged
tuannvm merged 4 commits intotuannvm:mainfrom
hampsterx:fix/review-working-directory-cwd
Apr 7, 2026
Merged

fix: set spawn cwd for workingDirectory to work around codex -C bug#138
tuannvm merged 4 commits intotuannvm:mainfrom
hampsterx:fix/review-working-directory-cwd

Conversation

@hampsterx
Copy link
Copy Markdown
Contributor

@hampsterx hampsterx commented Mar 29, 2026

Summary

  • Add cwd to Node's spawn() options when workingDirectory is provided, so the child Codex CLI process starts in the correct directory
  • Works around openai/codex#9084 where the -C flag is ignored by codex review
  • Keep -C in CLI args for forward compatibility when the upstream CLI bug is fixed
  • Use SpawnOptionsWithoutStdio for proper typing (replaces Record<string, unknown> + as never cast)
  • Extract shared CommandOptions interface with optional cwd and envOverride

Problem

The workingDirectory parameter on the review and codex tools was only forwarded as a -C CLI flag. However, Codex CLI's review subcommand ignores -C and resolves the git repo from process.cwd() instead. This makes it impossible to review code in git worktrees via MCP.

Root cause: Two compounding issues:

  1. codex-mcp-server: spawn() did not set cwd, so the child process inherited the MCP server's working directory
  2. Codex CLI: review subcommand uses process.cwd() instead of the -C path (openai/codex#9084)

Solution

By setting cwd on spawn() options, process.cwd() in the child Codex process returns the intended directory, bypassing the CLI bug entirely.

Verified with manual testing:

Test cwd -C flag workdir reported Result
Baseline main-repo none main-repo
-C only main-repo worktree main-repo ❌ Bug
cwd only worktree none worktree ✅ Fix works

Test plan

  • 5 new dedicated tests for cwd propagation (working-directory.test.ts)
  • All 61 existing tests updated and passing
  • Build clean (tsc)
  • Prettier and ESLint clean on all changed files
  • Manual verification that cwd on spawn() makes Codex resolve the correct git repo

Summary by CodeRabbit

  • New Features

    • Commands can run in a specified working directory.
    • Command invocations consistently accept an options object (including working-directory and environment overrides).
  • Tests

    • Tests updated to expect the new three-argument invocation shape.
    • Added a test suite validating working-directory propagation, streaming vs non-streaming, and resume-related flows.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Handlers now always pass a unified options object (cwd and envOverride) into command utilities; command utilities accept a typed CommandOptions (cwd/envOverride) and apply cwd/env to spawn options. Tests were updated to expect the new three-argument executeCommand signature and a new working-directory test was added.

Changes

Cohort / File(s) Summary
Command API
src/utils/command.ts
Added CommandOptions { envOverride?: ProcessEnv; cwd?: string }; executeCommand now accepts options?: CommandOptions; StreamingCommandOptions extends CommandOptions; spawn options now respect options.cwd and merge envOverride into env.
Handler Implementation
src/tools/handlers.ts
Resolve workingDirectory to an absolute path, construct cmdOptions (cwd/envOverride), and always pass it as the third arg to executeCommand and executeCommandStreaming (streaming includes onProgress).
Test signature updates
src/__tests__/default-model.test.ts, src/__tests__/error-scenarios.test.ts, src/__tests__/model-selection.test.ts, src/__tests__/resume-functionality.test.ts
Updated mocked executeCommand expectations to require a third options parameter (usually expect.any(Object)); callback-URI test now asserts envOverride nested shape.
Working directory tests (new)
src/__tests__/working-directory.test.ts
New tests validate workingDirectory flows into command args (-C <dir>) and into options.cwd for ReviewToolHandler and CodexToolHandler, covering streaming, non-streaming, and resume behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Handler as Handler
  participant CmdUtil as CommandUtil
  participant Child as ChildProc

  Client->>Handler: execute(request { workingDirectory?, envOverride?, stream? })
  Handler->>Handler: build cmdArgs and resolve cwd -> resolvedWorkDir
  Handler->>CmdUtil: executeCommand('codex', cmdArgs, cmdOptions { cwd: resolvedWorkDir, envOverride })
  CmdUtil->>Child: spawn(process, args, spawnOptions { cwd: path.resolve(cmdOptions.cwd), env: mergedEnv })
  Child-->>CmdUtil: stdout / stderr / exit
  CmdUtil-->>Handler: return result / stream callbacks
  Handler-->>Client: respond with output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through args and paths so wide,

I tuck cwd and env right by my side.
Spawned children find a tidy home,
Streams and callbacks freely roam —
A rabbit cheers for paths that guide!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the primary change: adding cwd to spawn options as a workaround for the codex -C bug, which is the main technical fix in this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hampsterx hampsterx force-pushed the fix/review-working-directory-cwd branch from 6d0cac0 to ce52945 Compare March 29, 2026 06:27
The workingDirectory parameter was only forwarded as a -C CLI flag,
but Codex CLI review ignores -C and resolves the git repo from
process.cwd() instead (openai/codex#9084). Setting cwd on spawn()
options makes the child process start in the intended directory.

- Add CommandOptions interface with optional cwd to executeCommand
  and executeCommandStreaming
- Use SpawnOptionsWithoutStdio for proper typing (no more as-never)
- Pass workingDirectory as spawn cwd from review and codex handlers
- Keep -C flag in args for forward compat when upstream fixes the bug
- Add 5 dedicated tests for cwd propagation

Signed-off-by: TiM <[email protected]>
@hampsterx hampsterx force-pushed the fix/review-working-directory-cwd branch from ce52945 to daf2f7f Compare March 29, 2026 06:30
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/tools/handlers.ts`:
- Around line 156-168: Normalize the workingDirectory to an absolute path before
building cmdArgs and cmdOptions: in both CodexToolHandler and ReviewToolHandler,
run path.resolve(workingDirectory) (or equivalent) and use that resolved path
for the "-C" entry in cmdArgs and for cwd in cmdOptions passed to executeCommand
and executeCommandStreaming; ensure you update any places that construct
cmdArgs/cmdOptions (references: workingDirectory, cmdArgs, cmdOptions,
executeCommand, executeCommandStreaming) so the CLI sees the same absolute path
the child process uses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ac4984e5-7fde-41aa-b6aa-1fe0ed4981a3

📥 Commits

Reviewing files that changed from the base of the PR and between ce52945 and daf2f7f.

📒 Files selected for processing (7)
  • src/__tests__/default-model.test.ts
  • src/__tests__/error-scenarios.test.ts
  • src/__tests__/model-selection.test.ts
  • src/__tests__/resume-functionality.test.ts
  • src/__tests__/working-directory.test.ts
  • src/tools/handlers.ts
  • src/utils/command.ts
✅ Files skipped from review due to trivial changes (2)
  • src/tests/resume-functionality.test.ts
  • src/tests/working-directory.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/tests/model-selection.test.ts
  • src/tests/error-scenarios.test.ts
  • src/tests/default-model.test.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/tools/handlers.ts`:
- Around line 162-174: The current cmdOptions unconditionally includes cwd:
resolvedWorkDir which violates the resume path; update the cmdOptions
construction in handlers.ts to only include cwd when not useResume (apply the
same guard used for sandbox and fullAuto), so that when useResume is true the
spawn options omit cwd; ensure both executeCommandStreaming('codex', ...) and
executeCommand('codex', ...) use this conditional cmdOptions, and add a
unit/integration test that runs the resume path with a workingDirectory set to
assert that cwd is not applied.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c964809-1ed4-4b97-a17d-f04d2ffa3e4c

📥 Commits

Reviewing files that changed from the base of the PR and between daf2f7f and 394da4d.

📒 Files selected for processing (1)
  • src/tools/handlers.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/tools/handlers.ts`:
- Around line 59-62: Validate the incoming workingDirectory string before
resolving and passing it to the child spawn: after computing resolvedWorkDir
from workingDirectory, check filesystem existence and that it refers to a
directory (use fs.existsSync/stat/isDirectory or async equivalents) and if the
check fails throw a ValidationError with a clear message (e.g.,
"workingDirectory does not exist" or "workingDirectory is not a directory").
Update the code around resolvedWorkDir in src/tools/handlers.ts (where
workingDirectory is processed) to perform this validation and throw
ValidationError instead of letting spawn surface ENOENT/ENOTDIR.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1f88233-3957-4fa4-923b-1640dca9aee0

📥 Commits

Reviewing files that changed from the base of the PR and between 394da4d and 8a1d96b.

📒 Files selected for processing (2)
  • src/__tests__/working-directory.test.ts
  • src/tools/handlers.ts
✅ Files skipped from review due to trivial changes (1)
  • src/tests/working-directory.test.ts

@tuannvm tuannvm merged commit ea14c7a into tuannvm:main Apr 7, 2026
2 of 5 checks passed
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.

2 participants