Skip to content

[codex] Support Codex title sync and native passthrough#168

Merged
leeroybrun merged 7 commits into
happier-dev:devfrom
jiuchuanll:ll-dev-fixAndOptimize-0505
May 11, 2026
Merged

[codex] Support Codex title sync and native passthrough#168
leeroybrun merged 7 commits into
happier-dev:devfrom
jiuchuanll:ll-dev-fixAndOptimize-0505

Conversation

@jiuchuanll
Copy link
Copy Markdown
Contributor

@jiuchuanll jiuchuanll commented May 8, 2026

Summary

  • add Codex app-server runtime support for native thread title sync
  • add generalized provider passthrough handling for native Codex and Claude commands
  • forward provider-native Codex args through initial and resumed local launch paths
  • harden Happier-owned argument parsing and Windows Expo shim test helpers

Validation

  • Passed: yarn workspace @happier-dev/cli vitest run src/cli/providerSessionArgPartition.test.ts src/backends/codex/cli/command.test.ts src/backends/claude/cli/command.help.test.ts
  • Passed: yarn workspace @happier-dev/cli vitest run src/backends/codex/codexLocalLauncher.integration.test.ts --config vitest.integration.config.ts
  • Passed: yarn workspace @happier-dev/cli vitest run src/backends/codex/runCodex.fastStart.integration.test.ts --config vitest.integration.config.ts
  • Passed: node --test apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs
  • Passed: yarn workspace @happier-dev/cli typecheck
  • Passed: git diff --check
  • Broader note: yarn workspace @happier-dev/cli test:integration src/backends/codex/codexLocalLauncher.integration.test.ts runs the sharded integration lane, not just the requested file. The Codex launcher file passed, but that broad shard run surfaced unrelated Claude integration failures in claudeLocalLauncher.integration.test.ts and runClaude.fastStart.integration.test.ts.

Notes

  • Head branch: jiuchuanll:ll-dev-fixAndOptimize-0505
  • Base branch: dev
  • Follow-up commit pushed: ab93fcc866cf4ad6a8c27fbff68a4b2bfb251f6c

View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Summary by CodeRabbit

  • New Features
    • Native thread title sync for Happier "change title" tool calls; CLI argument partitioning and provider-arg forwarding; codex launcher/run paths now accept and forward codexArgs and resume IDs.
  • Bug Fixes
    • Avoids syncing blank/failed titles; improved remote-switch handling for fresh-remote flows; better Windows command/shim selection and PATH handling.
  • Tests
    • Numerous new and extended tests for title sync, launcher behavior, provider CLI flows, and Windows compatibility.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 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

This PR integrates native thread title synchronization with Codex, centralizes provider/session CLI argument parsing across handlers, forwards Codex native arguments through the execution pipeline, and adds Windows .cmd shim support. The changes span runtime behavior, CLI routing, argument partitioning, and platform-specific test utilities.

Changes

Native Thread Title Synchronization

Layer / File(s) Summary
Implementation Foundation
apps/cli/src/backends/codex/appServer/runtime.ts
Imports change-title tool detection; adds helpers to extract title from tool-call input, parse JSON payloads in tool-result, and detect success via recursive inspection; introduces pendingHappierTitleToolNamesByCallId map.
Runtime Integration
apps/cli/src/backends/codex/appServer/runtime.ts
During tool-call, detects change-title MCP tool and stores title by callId; during tool-result, retrieves pending title, clears entry, and issues thread/name/set RPC when success/threadId/not-sidechain; clears map on stream flush.
Test Server & Logging
apps/cli/src/backends/codex/appServer/runtime.test.ts
Fake Codex app-server implements thread/name/set RPC with error-on-fail case; bridge prompt cases extended with change-title completions for normal, envelope, blank, failed, and native-failure scenarios; adds readRequestLog helper.
Test Cases
apps/cli/src/backends/codex/appServer/runtime.test.ts
Five tests: successful title sync triggers RPC, title from MCP envelope syncs, blank title skips sync, failed tool result skips sync, native RPC failure still forwards tool result while recording attempted request.

Centralized Session Argument Partitioning

Layer / File(s) Summary
Partitioning Module
apps/cli/src/cli/providerSessionArgPartition.ts
New module exports ProviderSessionArgPartitionResult/ProviderSessionArgPartitionOptions and partitionProviderSessionArgs() function that parses profile, permission-mode, starting-mode, resume, directory, model/version flags, help/version requests, and appends unrecognized args to providerArgs.
Partition Tests
apps/cli/src/cli/providerSessionArgPartition.test.ts
Comprehensive test suite verifying parsing of shared session flags, directory flag extraction, model flag forwarding, yolo alias handling, version/help detection with provider context preservation.
SessionStartArgs Delegation
apps/cli/src/cli/sessionStartArgs.ts, apps/cli/src/cli/sessionStartArgs.test.ts
parseSessionStartArgs refactored to delegate to partitionProviderSessionArgs and map result into ParsedSessionStartArgs; removes in-file permission validation and chalk error handling; tests updated for acceptEdits/bypassPermissions mapping.
BackendSessionCliCommand Integration
apps/cli/src/cli/runBackendSessionCliCommand.ts, apps/cli/src/cli/runBackendSessionCliCommand.test.ts
Refactored to use partitionProviderSessionArgs with configurable partitioning params; routes provider --help via passthroughProviderCliArgs; resolveExtraOptions now receives parsed partition; tests updated for combined help, subcommand help, and version flag forwarding.
Provider CLI Passthrough Generalization
apps/cli/src/cli/providerCliPassthrough.ts, apps/cli/src/cli/providerCliPassthrough.test.ts
detectProviderCliInfoRequest broadened to return matched flag string; passthroughProviderCliArgs accepts providerArgs array; maybePassthroughProviderCliInfoRequest delegates to passthroughProviderCliArgs with flag wrapped; tests add successfulSpawnResult helper, negative subcommand test, Windows-specific assertions.
Claude CLI Integration
apps/cli/src/backends/claude/cli/command.ts, apps/cli/src/backends/claude/cli/command.help.test.ts, apps/cli/src/backends/claude/cli/command.version.test.ts
Adds extractClaudeWrapperFlags to pre-process --chrome/--js-runtime; updates imports for partitionProviderSessionArgs; delegates to partitionProviderSessionArgs; refactors help/version to use dynamic resolveClaudeCliInfoInvocation; tests cover subcommand help and explicit version passthrough.
Codex CLI Integration
apps/cli/src/backends/codex/cli/command.ts, apps/cli/src/backends/codex/cli/command.test.ts
Configures runBackendSessionCliCommand with directory/model/version flags; resolveExtraOptions derives directory and codexArgs from parsed partition; test verifies session flags are consumed and Codex-native args pass through as codexArgs.
CLI Dispatch Cleanup
apps/cli/src/cli/dispatch.ts, apps/cli/src/cli/dispatch.providerInfoPassthrough.test.ts
Removed early-return passthrough for catalog-agent info requests; dispatch proceeds to default handler when no registry match; test expectation inverted so handler is called instead of short-circuited.

Codex Arguments & Resume ID Forwarding

Layer / File(s) Summary
Launcher Type Updates
apps/cli/src/backends/codex/codexLocalLauncher.ts
CodexLauncherResult switch variant now allows resumeId: string | null; launcher opts now accepts codexArgs?: readonly string[].
Codex TUI Argument Building
apps/cli/src/backends/codex/codexLocalLauncher.ts
buildCodexTuiArgs now accepts codexArgs option; when non-empty, appends codexArgs and returns early, changing how the resume argument is added.
Remote Switch Intent & Fresh Session
apps/cli/src/backends/codex/codexLocalLauncher.ts
Tracks remoteSwitchIntentObserved flag from UI message queue, pending-queue watcher, and switch RPC; after rollout discovery timeout, initiates fresh remote switch (resumeId: null) if flag is true and no local resumeId; centralizes child shutdown via stopChildIfRunning helper used at switch/exit points.
Launcher Test Support
apps/cli/src/backends/codex/codexLocalLauncher.testkit.ts
writeFakeCodexScript accepts optional writeSessionMeta?: boolean flag to control session metadata emission in generated fake Codex script.
Launcher Integration Tests
apps/cli/src/backends/codex/codexLocalLauncher.integration.test.ts
Two tests: codexArgs forwarded after wrapper args to Codex process; fresh local session without metadata resolves to remote switch with resumeId: null.
RunCodex Threading
apps/cli/src/backends/codex/runCodex.ts, apps/cli/src/backends/codex/runCodex.fastStart.integration.test.ts
Accepts optional codexArgs; forwards resumeIdFromArgs to launcher instead of null; passes codexArgs to runCodexLocalModePass; integration test verifies resume ID forwarded after session init.
Local Mode Pass Integration
apps/cli/src/backends/codex/runtime/localModePass.ts, apps/cli/src/backends/codex/runtime/localModePass.test.ts
Accepts optional codexArgs and forwards to launcher; test asserts codexArgs are forwarded to launchLocal.

Windows Platform Compatibility

Layer / File(s) Summary
Expo Binary Resolution
apps/stack/scripts/utils/expo/command.mjs
resolveExpoBin now prefers workspace/root expo.cmd shim on win32 before falling back to expo binary.
Expo Shim Helper Module
apps/stack/scripts/testkit/core/expo_command_shims.mjs
New module exporting writeExpoShimPairCaptureInvocation that creates POSIX expo and Windows expo.cmd shims recording invocations.
Environment Patching Windows Fix
apps/stack/scripts/testkit/core/env_scope.mjs
withPatchedProcessEnv remaps overrides.PATH to overrides.Path on Windows before applying environment changes.
Expo Heap Limit Tests
apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs
Uses delimiter for cross-platform PATH; yarn and expo stubs include Windows .cmd variants; switched to withPatchedProcessEnv; adds test asserting Windows .cmd shim selection on win32.
Expo Workspace Dependencies Tests
apps/stack/scripts/utils/expo/command_workspace_deps_built.test.mjs
Uses delimiter for cross-platform PATH; yarn and expo stubs include Windows .cmd implementations; assertions accept OS-specific separators and both expo/expo.cmd names.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main changes: adding Codex title sync support and native passthrough handling, which aligns with the file changes and objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description follows the template structure with all major sections present: Summary, Why (inferred from context), How to test (validation steps listed), and Notes. However, the 'Why' section is missing explicit problem context.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@jiuchuanll jiuchuanll force-pushed the ll-dev-fixAndOptimize-0505 branch from 2aa4a8d to d7df8da Compare May 8, 2026 17:48
@jiuchuanll
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

🧹 Nitpick comments (1)
apps/cli/src/backends/codex/appServer/runtime.test.ts (1)

526-528: ⚡ Quick win

Replace any with unknown for better type safety.

The readRequestLog helper uses any for params, result, and error fields without justification. While this is a boundary helper parsing external JSON, using unknown would be more type-safe and force proper type guards at usage sites.

🔧 Proposed fix
-    async function readRequestLog(requestLogPath: string): Promise<Array<{ id: unknown; method: string; params: any; result: any; error: any }>> {
+    async function readRequestLog(requestLogPath: string): Promise<Array<{ id: unknown; method: string; params: unknown; result: unknown; error: unknown }>> {
         return (await readFile(requestLogPath, 'utf8')).trim().split('\n').map((line) => JSON.parse(line));
     }

As per coding guidelines: "No untyped escape hatches in production or tests; broad as any casts are forbidden except in boundary fixtures/harnesses with a one-line justification."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/appServer/runtime.test.ts` around lines 526 -
528, Update the readRequestLog helper to remove the use of any for parsed JSON
fields: change the return type so params, result, and error are typed as unknown
instead of any, i.e., Promise<Array<{ id: unknown; method: string; params:
unknown; result: unknown; error: unknown }>>; keep the JSON.parse usage but
ensure callers perform proper type-narrowing or validation when accessing those
fields (refer to readRequestLog function name and its returned object shape).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/cli/src/backends/codex/appServer/runtime.test.ts`:
- Around line 526-528: Update the readRequestLog helper to remove the use of any
for parsed JSON fields: change the return type so params, result, and error are
typed as unknown instead of any, i.e., Promise<Array<{ id: unknown; method:
string; params: unknown; result: unknown; error: unknown }>>; keep the
JSON.parse usage but ensure callers perform proper type-narrowing or validation
when accessing those fields (refer to readRequestLog function name and its
returned object shape).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 70f96be7-304b-4819-ad55-fa1ef38a7de7

📥 Commits

Reviewing files that changed from the base of the PR and between 0cdc50b and d7df8da.

📒 Files selected for processing (6)
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.ts
  • apps/cli/src/cli/providerCliPassthrough.test.ts
  • apps/cli/src/cli/providerCliPassthrough.ts
  • packages/agents/src/manifest.ts
  • packages/agents/src/types.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 8, 2026
@jiuchuanll jiuchuanll force-pushed the ll-dev-fixAndOptimize-0505 branch from d7df8da to 1157093 Compare May 9, 2026 16:45
@jiuchuanll jiuchuanll marked this pull request as ready for review May 9, 2026 16:45
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 9, 2026

Greptile Summary

This PR adds two major capabilities to the Codex backend: native thread title sync (when a Happier change_title MCP tool call completes successfully, the result is forwarded to Codex's thread/name/set RPC), and native CLI passthrough (arbitrary Codex subcommands like resume --all can now be passed through the Happier CLI). It also refactors Claude and Codex command parsing to use a shared partitionProviderSessionArgs utility, removing ~150 lines of duplicated per-command arg-loop code.

  • Title sync (appServer/runtime.ts): Tracks pending change_title calls by callId, validates success including content-envelope format, guards against blank titles, and calls thread/name/set non-fatally.
  • Native passthrough (codexLocalLauncher.ts, runCodex.ts, localModePass.ts): codexArgs flows from the CLI all the way to the TUI invocation; when set, the internal resumeId-based launch args are bypassed.
  • Fresh remote switch (codexLocalLauncher.ts): When a remote-switch intent is observed but no local rollout file appears before the discovery deadline (and no resumeId was set), the launcher now switches to remote with resumeId: null.

Confidence Score: 5/5

Safe to merge; changes are well-scoped and well-tested with no blocking defects found.

The core title-sync and native-passthrough logic is covered end-to-end by integration tests (blank titles, failed results, native-sync failures, arg forwarding). The shared partitionProviderSessionArgs refactor has dedicated unit tests for every flag. The two observations are non-blocking and do not affect any primary code path exercised by the test suite.

The ReferenceError catch block in apps/cli/src/backends/claude/cli/command.ts is worth a quick second look to confirm the error type matches what resolveClaudeCliInfoInvocation actually throws.

Important Files Changed

Filename Overview
apps/cli/src/backends/codex/appServer/runtime.ts Adds native Codex thread title sync: intercepts completed change_title MCP tool-call results, validates success (with content-envelope support and blank-title guard), then calls thread/name/set on the app-server client. Failure is non-fatal.
apps/cli/src/cli/providerSessionArgPartition.ts New shared argument-partitioning utility that consolidates provider/session flag parsing into one place, replacing duplicated per-command loops.
apps/cli/src/backends/claude/cli/command.ts Refactors Claude CLI command handler to use shared partitionProviderSessionArgs. Version display now runs claude --version and always returns early. An unusual ReferenceError catch in the version handler is flagged.
apps/cli/src/backends/codex/codexLocalLauncher.ts Adds codexArgs native passthrough, refactors child-kill logic into stopChildIfRunning(), and adds fresh-remote-switch path when rollout discovery times out.
apps/cli/src/cli/dispatch.ts Removes maybePassthroughProviderCliInfoRequest short-circuit; help/version requests are now delegated to each provider's command handler.
apps/cli/src/cli/runBackendSessionCliCommand.ts Adopts partitionProviderSessionArgs, adds directoryFlags/versionFlags options, integrates combined-help rendering. No logic regressions found.
apps/cli/src/backends/codex/runCodex.ts Adds codexArgs propagation through the pipeline; fixes fast-start local launch to pass resumeIdFromArgs instead of hard-coded null.
apps/cli/src/backends/codex/cli/command.ts Adopts shared partitioning; passes parsed.providerArgs as codexArgs for native Codex subcommand passthrough.

Sequence Diagram

sequenceDiagram
    participant CLI as happier codex CLI
    participant RBC as runBackendSessionCliCommand
    participant RC as runCodex
    participant LMP as runCodexLocalModePass
    participant CLL as codexLocalLauncher
    participant TUI as Codex TUI

    CLI->>RBC: args ['--permission-mode','yolo','resume','--all']
    RBC->>RBC: "partitionProviderSessionArgs()<br/>permissionMode=yolo, providerArgs=['resume','--all']"
    RBC->>RC: "runCodex({permissionMode, codexArgs:['resume','--all']})"
    RC->>LMP: "runCodexLocalModePass({codexArgs:['resume','--all']})"
    LMP->>CLL: "codexLocalLauncher({codexArgs:['resume','--all']})"
    CLL->>CLL: "buildCodexTuiArgs() -> ['--cd',path,'resume','--all']"
    CLL->>TUI: spawn codex --cd path resume --all

    Note over RC,TUI: Title Sync Flow
    TUI-->>RC: item/completed (change_title, success:true)
    RC->>RC: "didHappierTitleToolSucceed() -> true"
    RC->>TUI: "thread/name/set {threadId, name}"
Loading

Reviews (2): Last reviewed commit: "fix(cli): refine provider info passthrou..." | Re-trigger Greptile

Comment thread apps/cli/src/cli/providerCliPassthrough.ts Outdated
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.

🧹 Nitpick comments (1)
apps/cli/src/backends/codex/appServer/runtime.test.ts (1)

591-593: 💤 Low value

Consider consolidating test log parsing or adopting the helper more broadly.

The readRequestLog helper reduces duplication, but it's only used in the 4 new tests while existing tests (lines 612, 666, 706, etc.) still use inline parsing. This creates inconsistency. Consider either:

  1. Refactoring existing tests to use this helper, or
  2. Using inline parsing in the new tests to match the existing pattern

The any types for params, result, and error are acceptable for a test fixture but could be more specific for better type safety.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/appServer/runtime.test.ts` around lines 591 -
593, The new readRequestLog helper function centralizes log parsing but existing
tests still parse logs inline; either refactor those existing inline parsers to
call readRequestLog or revert the new tests to use the same inline parsing style
for consistency. Locate the helper readRequestLog in runtime.test.ts and replace
inline (await readFile(...).trim().split('\n').map(JSON.parse)) occurrences in
other tests with calls to readRequestLog, or update the four new tests to
perform the inline parsing used elsewhere—also consider tightening the helper's
return type by replacing the broad any for params/result/error with more
specific types if desired for stronger type safety.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/cli/src/backends/codex/appServer/runtime.test.ts`:
- Around line 591-593: The new readRequestLog helper function centralizes log
parsing but existing tests still parse logs inline; either refactor those
existing inline parsers to call readRequestLog or revert the new tests to use
the same inline parsing style for consistency. Locate the helper readRequestLog
in runtime.test.ts and replace inline (await
readFile(...).trim().split('\n').map(JSON.parse)) occurrences in other tests
with calls to readRequestLog, or update the four new tests to perform the inline
parsing used elsewhere—also consider tightening the helper's return type by
replacing the broad any for params/result/error with more specific types if
desired for stronger type safety.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fbad7915-e36c-45c5-ae94-aab8cbc32cab

📥 Commits

Reviewing files that changed from the base of the PR and between d7df8da and 1157093.

📒 Files selected for processing (4)
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.ts
  • apps/cli/src/cli/providerCliPassthrough.test.ts
  • apps/cli/src/cli/providerCliPassthrough.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/cli/src/cli/providerCliPassthrough.test.ts
  • apps/cli/src/cli/providerCliPassthrough.ts
  • apps/cli/src/backends/codex/appServer/runtime.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 9, 2026
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.

🧹 Nitpick comments (1)
apps/cli/src/backends/codex/codexLocalLauncher.ts (1)

462-474: ⚡ Quick win

Optional: extract the child-stop logic into a helper.

The child.exitCode === null + Windows/POSIX kill block is now duplicated four times in this file (here, 553-564, 598-609, 651-661). Extracting a small stopChildIfRunning() closure inside codexLocalLauncher would localize the platform branching and make future tweaks (e.g., adjusting graceMs) one-line changes. Deferable — no behavior change required for this PR.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/codexLocalLauncher.ts` around lines 462 - 474,
Extract the duplicated child-stop logic into a single helper (e.g.,
stopChildIfRunning) inside codexLocalLauncher: move the check child &&
child.exitCode === null plus the Windows branch that calls
killProcessTree(child, { graceMs: 250 }) and the POSIX branch that calls
child.kill('SIGTERM') (with the same try/catch) into that helper, replace the
four duplicated blocks with a call to stopChildIfRunning(), and keep existing
semantics (childStopRequested flag and exitReason assignment should remain where
they are but set childStopRequested inside the helper as needed); this
centralizes platform branching and makes future changes (like adjusting graceMs)
one-line edits to stopChildIfRunning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/cli/src/backends/codex/codexLocalLauncher.ts`:
- Around line 462-474: Extract the duplicated child-stop logic into a single
helper (e.g., stopChildIfRunning) inside codexLocalLauncher: move the check
child && child.exitCode === null plus the Windows branch that calls
killProcessTree(child, { graceMs: 250 }) and the POSIX branch that calls
child.kill('SIGTERM') (with the same try/catch) into that helper, replace the
four duplicated blocks with a call to stopChildIfRunning(), and keep existing
semantics (childStopRequested flag and exitReason assignment should remain where
they are but set childStopRequested inside the helper as needed); this
centralizes platform branching and makes future changes (like adjusting graceMs)
one-line edits to stopChildIfRunning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 722d6f74-6544-4385-a6d3-706701cbab8c

📥 Commits

Reviewing files that changed from the base of the PR and between 1157093 and 1a2d785.

📒 Files selected for processing (5)
  • apps/cli/src/backends/codex/codexLocalLauncher.integration.test.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.testkit.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.ts
  • apps/cli/src/backends/codex/runCodex.fastStart.integration.test.ts
  • apps/cli/src/backends/codex/runCodex.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 9, 2026
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs (1)

87-132: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Please switch these new test wrappers to canonical test helpers.

writeExpoShimPairCaptureInvocation and the expanded local env override wrapper add more ad hoc test harness primitives instead of reusing shared stack test helpers.

As per coding guidelines, "Reuse canonical stack runner/discovery helpers from scripts/utils/test/** instead of adding new ad hoc tempdir/env/spawn wrappers in test files or domain testkits." and "Reuse canonical stack-local testkit primitives from scripts/testkit/core/** instead of adding new ad hoc tempdir/env/spawn wrappers."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs` around lines
87 - 132, Remove the ad-hoc test helpers writeExpoShimPairCaptureInvocation and
applyEnvOverrides and replace their usage with the repository's canonical test
helpers: import and use the shared shim-writing/tempdir utility and the shared
environment-override helper from the stack test utilities, then update tests to
call those helpers instead of the local functions; ensure the imported helpers
provide equivalent behavior (creating both posix and cmd shim executables and
restoring env vars after the test) so you can delete
writeExpoShimPairCaptureInvocation and applyEnvOverrides from this file and wire
the tests to the shared helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs`:
- Around line 87-132: Remove the ad-hoc test helpers
writeExpoShimPairCaptureInvocation and applyEnvOverrides and replace their usage
with the repository's canonical test helpers: import and use the shared
shim-writing/tempdir utility and the shared environment-override helper from the
stack test utilities, then update tests to call those helpers instead of the
local functions; ensure the imported helpers provide equivalent behavior
(creating both posix and cmd shim executables and restoring env vars after the
test) so you can delete writeExpoShimPairCaptureInvocation and applyEnvOverrides
from this file and wire the tests to the shared helpers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ffa058ca-f8a0-4f53-8ab9-d507a89a1718

📥 Commits

Reviewing files that changed from the base of the PR and between 1a2d785 and c83a4f9.

📒 Files selected for processing (5)
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.ts
  • apps/stack/scripts/utils/expo/command.mjs
  • apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs
  • apps/stack/scripts/utils/expo/command_workspace_deps_built.test.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 9, 2026
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.

🧹 Nitpick comments (1)
apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs (1)

15-49: ⚡ Quick win

Consolidate duplicated Yarn stub setup into testkit core.

This local writeYarnStub now overlaps with other Expo test files; moving it to a shared helper under scripts/testkit/core/** would reduce cross-platform drift and future maintenance overhead.

As per coding guidelines, “Reuse canonical stack-local testkit primitives from scripts/testkit/core/** instead of adding new ad hoc tempdir/env/spawn wrappers.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs` around lines
15 - 49, The writeYarnStub helper in this test duplicates shared logic and
should be replaced with the canonical testkit helper: remove the local async
function writeYarnStub and import/consume the shared yarn-stub helper from the
testkit core (use the exported helper name from scripts/testkit/core that
provides cross-platform yarn and yarn.cmd stubs), ensuring the test still sets
executable permissions (chmod behavior) and produces the same --version and
no-op install semantics; update the test to call the shared helper and delete
the duplicated implementation in this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs`:
- Around line 15-49: The writeYarnStub helper in this test duplicates shared
logic and should be replaced with the canonical testkit helper: remove the local
async function writeYarnStub and import/consume the shared yarn-stub helper from
the testkit core (use the exported helper name from scripts/testkit/core that
provides cross-platform yarn and yarn.cmd stubs), ensuring the test still sets
executable permissions (chmod behavior) and produces the same --version and
no-op install semantics; update the test to call the shared helper and delete
the duplicated implementation in this file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 66e1aa75-2cd6-44cc-8888-69b85a22f970

📥 Commits

Reviewing files that changed from the base of the PR and between c83a4f9 and c683fca.

📒 Files selected for processing (7)
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.ts
  • apps/cli/src/cli/providerCliPassthrough.ts
  • apps/stack/scripts/testkit/core/env_scope.mjs
  • apps/stack/scripts/testkit/core/expo_command_shims.mjs
  • apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs
  • apps/stack/scripts/utils/expo/command_workspace_deps_built.test.mjs
✅ Files skipped from review due to trivial changes (1)
  • apps/stack/scripts/testkit/core/expo_command_shims.mjs
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/cli/src/cli/providerCliPassthrough.ts
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 10, 2026
leeroybrun and others added 2 commits May 11, 2026 18:53
Co-authored-by: 九川三江 <903682402@qq.com>
Co-authored-by: 九川三江 <903682402@qq.com>
@leeroybrun
Copy link
Copy Markdown
Collaborator

leeroybrun commented May 11, 2026

Thank you very much @jiuchuanll, great ideas !

I've actually refined the commands passthrough logic to extract and generalize the logic we had for Claude instead, which is in fact the inverted logic of your PR proposition: all the commands like happier codex, happier claude, etc should in fact work like the original codex/claude commands with just the Happier arguments handled by Happier, but all of the rest passed through to the provider.

That way, users can use happier codex directly instead of codex and even add it as an alias in their shell if they want to, and we don't have to add passthrough for all providers' commands.

@CodeRabbit @greptile please review deeply

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

@leeroybrun Thank you for the context! The generalized passthrough approach makes a lot of sense — having happier codex / happier claude work as drop-in replacements for the native provider CLIs (with only Happier-specific args intercepted) is a much cleaner design than enumerating per-provider subcommands. Triggering a deep review now with this in mind.

Feedback to incorporate: Perform a deep review focusing on the generalized provider CLI passthrough logic in providerCliPassthrough.ts and providerCliPassthrough.test.ts — the approach inverts the original PR's per-subcommand enumeration so that all non-Happier args are forwarded to the underlying provider CLI (codex, claude, etc.), making happier codex a transparent drop-in alias. Also review the Codex native thread title sync additions in runtime.ts/runtime.test.ts, the nativeCliPassthroughSubcommands field added to AgentCore, and all related integration/unit tests for correctness, edge cases, and consistency with the generalized design.

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

@leeroybrun Thanks for the clear explanation of the design philosophy! The generalized passthrough approach — where happier codex / happier claude act as transparent drop-in aliases forwarding all non-Happier args to the underlying provider CLI — is indeed much more elegant and scalable than per-subcommand enumeration.

A full deep review has been triggered with that context in mind. The review will focus on:

  1. providerCliPassthrough.ts / providerCliPassthrough.test.ts — the generalized passthrough logic, resolveNativeProviderArgs, and the refactored passthroughProviderCli that accepts a providerArgs array.
  2. runtime.ts / runtime.test.ts — the Codex native thread title sync via thread/name/set RPC, the pendingHappierTitleToolNamesByCallId map, and edge cases (blank titles, failed tool results, native sync failures).
  3. AgentCore type & manifest.ts — the new nativeCliPassthroughSubcommands field and whether it remains consistent with the generalized design.
  4. Integration testscodexLocalLauncher fresh-remote-switch path, runCodex resume forwarding, and Windows compatibility fixes in the Expo scripts.
✅ Actions performed

Full review triggered.

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: 2

🧹 Nitpick comments (10)
apps/stack/scripts/testkit/core/expo_command_shims.mjs (1)

20-28: 💤 Low value

Inconsistency: Windows shim uses hardcoded path; POSIX shim uses environment variable.

The POSIX shim (line 13) reads the output path from the OUTPUT_PATH environment variable:

echo "shim=posix bin=$0 args=$*" >> "${OUTPUT_PATH:?}"

The Windows shim (line 24) uses the JavaScript outputPath parameter interpolated directly into the batch file:

`echo shim=cmd bin=%~f0 args=%*>>"${outputPath}"`

This means:

  • POSIX shim: Can be controlled via OUTPUT_PATH environment variable at runtime
  • Windows shim: Output path is baked into the batch file at creation time

For test purposes this likely works fine, but it's architecturally inconsistent. If the Windows shim should also support runtime configuration via environment, update line 24:

-      `echo shim=cmd bin=%~f0 args=%*>>"${outputPath}"`,
+      `echo shim=cmd bin=%~f0 args=%*>>"%OUTPUT_PATH%"`,

And ensure tests set OUTPUT_PATH in the environment before invoking the Windows shim.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/stack/scripts/testkit/core/expo_command_shims.mjs` around lines 20 - 28,
The Windows shim written by writeFile (creating 'expo.cmd') currently hardcodes
the JS outputPath into the batch content; change the batch line inside the array
passed to writeFile (the line creating `echo shim=cmd bin=%~f0
args=%*>>"${outputPath}"`) to instead read the runtime environment variable (use
%OUTPUT_PATH% or the Windows batch parameter syntax) so the batch file appends
to the path provided at runtime, mirroring the POSIX shim's use of OUTPUT_PATH;
update tests to set OUTPUT_PATH in the environment before invoking the produced
expo.cmd shim.
apps/stack/scripts/testkit/core/env_scope.mjs (1)

74-77: ⚡ Quick win

Both PATH and Path are set on Windows, which is inefficient.

When overrides includes PATH on Windows, the current code spreads overrides (keeping PATH) and then adds Path: overrides.PATH, resulting in both keys in effectiveOverrides. While this works correctly (both are set in process.env and both are restored), it's wasteful.

If the intent is to use only the Windows convention (Path), consider using destructuring to exclude PATH:

const effectiveOverrides =
  process.platform === 'win32' && Object.prototype.hasOwnProperty.call(overrides, 'PATH')
    ? { ...Object.fromEntries(Object.entries(overrides).filter(([k]) => k !== 'PATH')), Path: overrides.PATH }
    : overrides;

Or more simply, rely on caller convention (tests already pass PATH; let them handle the Windows mapping if needed).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/stack/scripts/testkit/core/env_scope.mjs` around lines 74 - 77, The
current effectiveOverrides assignment on Windows copies overrides and then adds
Path: overrides.PATH, leaving both PATH and Path set; change it so when
process.platform === 'win32' and overrides has 'PATH' you build
effectiveOverrides by copying overrides but excluding the 'PATH' key and then
add Path: overrides.PATH (i.e., remove 'PATH' before adding 'Path'), referencing
effectiveOverrides, overrides, process.platform, 'PATH' and 'Path' to locate the
code to change.
apps/cli/src/backends/codex/appServer/runtime.test.ts (1)

1596-1760: ⚡ Quick win

Excellent test coverage for title sync! Consider adding edge case tests.

The five new tests comprehensively validate the native title sync behavior:

  1. ✅ Successful sync with standard title
  2. ✅ Successful sync with MCP content envelope
  3. ✅ No sync for blank/whitespace titles
  4. ✅ No sync for failed tool results
  5. ✅ Tool result remains accepted even when native sync fails

The tests properly use try-finally for cleanup and verify both the thread/name/set calls in the request log and the forwarded tool results.

Optional enhancement: Consider adding tests for these edge cases (currently only covered by the runtime implementation logic):

  • Title tool completion in a sidechain context (should not sync to parent thread)
  • Title tool completion before threadId is established (should not attempt sync)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/appServer/runtime.test.ts` around lines 1596 -
1760, Add two new tests in the same suite in runtime.test.ts: one named "does
not sync title tool calls from sidechain context to parent thread" that uses
createRuntimeFixture(...) and createCodexAppServerRuntime(...) then
runtime.sendPrompt('bridge-mcp-title-tool-sidechain') and asserts requestLog has
no 'thread/name/set' entries and that sendCodexMessage tool-call-result was
still sent; and another named "does not sync title tool calls before threadId
established" that triggers a title-complete prompt before the runtime
establishes the parent thread (e.g., sendPrompt immediately after startOrLoad
without creating a thread) and asserts no 'thread/name/set' requests were made.
Reference createRuntimeFixture, createCodexAppServerRuntime, runtime.sendPrompt,
readRequestLog, and the 'thread/name/set' method in your assertions.
apps/cli/src/backends/codex/appServer/runtime.ts (1)

783-811: 💤 Low value

LGTM! Consider adding test coverage for edge cases.

The tool-result handling correctly implements best-effort native title synchronization:

  • Retrieves and clears the pending title immediately
  • Validates all necessary preconditions before syncing
  • Appropriately catches and logs sync failures without propagating them

The conditions at line 801 properly guard against syncing in sidechains or when no native threadId is available. Consider adding test coverage for these edge cases:

  1. Sidechain case (context.sidechainId is set)
  2. Missing threadId case (e.g., before thread/started notification arrives)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/appServer/runtime.ts` around lines 783 - 811, Add
unit/integration tests for the Happier title sync branch around the tool-result
handling: exercise the branch that sends a 'tool-result'/'tool-call-result' and
verify pendingHappierTitleToolNamesByCallId is cleared and no native thread
rename is attempted when context.sidechainId is set, and similarly test the case
where threadId is missing (e.g., before thread/started) so
didHappierTitleToolSucceed(update.output) is true but
ensureClient()/client.request('thread/name/set', ...) is not called; use
mocks/spies for ensureClient and client.request to assert no rename attempts and
for logger.debug to ensure failures are safely caught when client.request would
throw.
apps/cli/src/cli/providerSessionArgPartition.ts (2)

222-227: 💤 Low value

--account-settings-version-hint silently consumes its value.

The flag is recognized and its value is skipped, but nothing about it is returned to the caller and there's no comment explaining why we accept it. If this is intentional backward-compat (e.g., daemon-injected hint kept for forward compatibility), please add a one-line comment; if it's leftover from a prior parser, drop it under the YAGNI rule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/providerSessionArgPartition.ts` around lines 222 - 227, The
branch handling the '--account-settings-version-hint' flag currently consumes an
optional value via readOptionalValue(args, i) and increments i but never returns
or records it; either remove this unused branch (delete the if (arg ===
'--account-settings-version-hint') block) or, if it must be accepted for
backward/forward compatibility, replace it with a one-line explanatory comment
above the block that explains it's intentionally ignored (e.g., "daemon-injected
hint accepted and discarded for forward compatibility") and keep the current
consume-and-skip logic in the same symbols (arg, readOptionalValue, i) so future
readers understand it's intentional.

1-8: ⚡ Quick win

Missing file-level JSDoc explaining responsibilities.

This new module owns the contract for splitting argv into Happier session flags vs. provider-owned args used by Claude/Codex command handlers, runBackendSessionCliCommand, and sessionStartArgs. A top-of-file JSDoc would set expectations for the forwarding rules (forwardModelFlag, forwardResumeFlag, yoloProviderArgs, directoryFlags, versionFlags) so future provider integrations don't drift.

As per coding guidelines: "Include comprehensive JSDoc comments at the top of each file explaining responsibilities".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/providerSessionArgPartition.ts` around lines 1 - 8, Add a
top-of-file JSDoc that documents this module's responsibilities: that it splits
process argv into Happier session flags vs provider-owned args and enumerates
forwarding rules used by runBackendSessionCliCommand and sessionStartArgs
(specifically describe forwardModelFlag, forwardResumeFlag, yoloProviderArgs,
directoryFlags, and versionFlags), include expected semantics for each
flag/group (what is forwarded, when filtered, and who owns parsing), and list
any invariants/contract callers rely on (e.g., order preservation, prefix
handling). Place this comment at the very top of providerSessionArgPartition.ts
so future provider integrations follow the same forwarding contract.
apps/cli/src/cli/providerCliPassthrough.ts (1)

8-17: 💤 Low value

Help-over-version precedence is implicit; consider a short comment.

detectProviderCliInfoRequest returns the first matching --help flag before falling back to version flags, so happier codex --help --version yields the help flag. That precedence is intentional (mirrors prior behavior), but a one-line comment would make it obvious to future readers given the recent return-type widening.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/providerCliPassthrough.ts` around lines 8 - 17, The function
detectProviderCliInfoRequest currently checks HELP_FLAGS before VERSION_FLAGS,
so a combined invocation like "happier codex --help --version" intentionally
returns the help flag; add a one-line comment above the function (or above the
HELP_FLAGS/VERSION_FLAGS definitions) noting that help has precedence over
version to make the behavior explicit for future readers and to justify the
order of checks in detectProviderCliInfoRequest (referencing HELP_FLAGS,
VERSION_FLAGS, and the args parameter).
apps/cli/src/cli/dispatch.providerInfoPassthrough.test.ts (1)

1-53: 💤 Low value

Test file name/scope no longer matches what it verifies.

After the dispatch refactor the only remaining assertion is that maybePassthroughProviderCliInfoRequest is never called and that the provider command handler runs instead. The file/describe still says "provider info passthrough", which now misleads readers. Two small improvements would help:

  • Rename to something like dispatch.providerCommandHandler.test.ts (and update the describe block) or merge this case into an existing dispatch test file.
  • The @/backends/catalog mock at Lines 18-26 isn't exercised by the current test path (the registered gemini handler short-circuits before the default catalog fallback) — either drop it or add a complementary test that actually hits the default-handler path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/dispatch.providerInfoPassthrough.test.ts` around lines 1 -
53, The test name and describe text no longer match what is asserted: update the
file and describe to reflect that this verifies the provider command handler
runs (suggest renaming file to dispatch.providerCommandHandler.test.ts and
change the describe string accordingly), and either remove the unused
'@/backends/catalog' mock (the vi.mock that defines requireCatalogEntry) or add
a complementary test that clears/unsets the registered gemini handler (e.g. make
commandRegistry.gemini undefined) so dispatchCli exercises the
requireCatalogEntry fallback path; ensure the complementary test asserts
maybePassthroughProviderCliInfoRequest (passthroughSpy) is not called and that
requireCatalogEntry/getCliCommandHandler path is invoked instead, referencing
dispatchCli, geminiHandlerSpy, passthroughSpy and requireCatalogEntry to locate
the code to change.
apps/cli/src/backends/codex/codexLocalLauncher.ts (1)

120-152: ⚡ Quick win

Document codexArgs precedence over resumeId in buildCodexTuiArgs.

When codexArgs is non-empty the function pushes it and returns immediately, intentionally dropping opts.resumeId (per the new integration test asserting the session id is not appended when codexArgs are forwarded). Since both options are still accepted in the type, the precedence is invisible at the call site. A short JSDoc on buildCodexTuiArgs (and ideally codexLocalLauncher / resolveCodexTuiInvocation) noting "when codexArgs is provided, native Codex args win and resumeId is ignored" would prevent surprising regressions when wiring new callers.

📝 Suggested JSDoc
+/**
+ * Builds Codex TUI argv.
+ *
+ * Precedence: when `opts.codexArgs` is provided and non-empty, it is forwarded
+ * verbatim and `opts.resumeId` is ignored (the caller is presumed to be in
+ * native Codex passthrough mode and to have authored its own resume args).
+ * Otherwise `opts.resumeId`, if present, is mapped to `resume <id>`.
+ */
 function buildCodexTuiArgs(opts: {
   cwd: string;
   resumeId?: string | null;
   permissionMode: PermissionMode;
   codexArgs?: readonly string[];
 }): string[] {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/codexLocalLauncher.ts` around lines 120 - 152,
Add a short JSDoc to buildCodexTuiArgs explaining that when the codexArgs
parameter is provided and non-empty the function forwards those native Codex
args and returns immediately, therefore any resumeId is ignored; also add
similar brief JSDoc notes to codexLocalLauncher and resolveCodexTuiInvocation to
document the precedence ("codexArgs wins, resumeId ignored") so callers are not
surprised. Ensure the JSDoc references the codexArgs and resumeId parameters by
name and mirrors the existing style in nearby functions.
apps/cli/src/cli/sessionStartArgs.ts (1)

15-29: ⚡ Quick win

Consolidate the partition→ParsedSessionStartArgs picker.

The seven-field mapping here is duplicated verbatim by pickSessionStartArgs in apps/cli/src/cli/runBackendSessionCliCommand.ts (lines 63–73). Any future field additions to ParsedSessionStartArgs will require touching both spots and silently break in one if missed. Consider exporting a single pickSessionStartArgs(parsed) from this module and reusing it from both callers.

♻️ Proposed consolidation
+export function pickSessionStartArgs(
+  parsed: Pick<
+    import('@/cli/providerSessionArgPartition').ProviderSessionArgPartitionResult,
+    | 'startedBy'
+    | 'permissionMode'
+    | 'permissionModeUpdatedAt'
+    | 'agentModeId'
+    | 'agentModeUpdatedAt'
+    | 'modelId'
+    | 'modelUpdatedAt'
+  >,
+): ParsedSessionStartArgs {
+  return {
+    startedBy: parsed.startedBy,
+    permissionMode: parsed.permissionMode,
+    permissionModeUpdatedAt: parsed.permissionModeUpdatedAt,
+    agentModeId: parsed.agentModeId,
+    agentModeUpdatedAt: parsed.agentModeUpdatedAt,
+    modelId: parsed.modelId,
+    modelUpdatedAt: parsed.modelUpdatedAt,
+  };
+}
+
 export function parseSessionStartArgs(args: string[]): ParsedSessionStartArgs {
-  const parsed = partitionProviderSessionArgs({
-    args: args[0] === 'happier' ? args.slice(1) : args,
-  });
-
-  return {
-    startedBy: parsed.startedBy,
-    permissionMode: parsed.permissionMode,
-    permissionModeUpdatedAt: parsed.permissionModeUpdatedAt,
-    agentModeId: parsed.agentModeId,
-    agentModeUpdatedAt: parsed.agentModeUpdatedAt,
-    modelId: parsed.modelId,
-    modelUpdatedAt: parsed.modelUpdatedAt,
-  };
+  const parsed = partitionProviderSessionArgs({
+    args: args[0] === 'happier' ? args.slice(1) : args,
+  });
+  return pickSessionStartArgs(parsed);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/sessionStartArgs.ts` around lines 15 - 29, Introduce a
single picker function (e.g., export function pickSessionStartArgs(parsed:
ReturnType<typeof partitionProviderSessionArgs>)) that maps the
partitionProviderSessionArgs result to the seven ParsedSessionStartArgs fields,
replace the inline mapping in parseSessionStartArgs to call
pickSessionStartArgs(partitioned) (where partitioned is the result of
partitionProviderSessionArgs), and update the other caller (pickSessionStartArgs
usage in runBackendSessionCliCommand.ts) to import and reuse this new exported
pickSessionStartArgs so the mapping logic lives in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/cli/src/backends/codex/codexLocalLauncher.ts`:
- Around line 485-493: The branch that sets exitReason = { type: 'switch',
resumeId: null } when remoteSwitchIntentObserved && !opts.resumeId may
inadvertently drop native codex passthrough args stored in opts.codexArgs (e.g.,
['resume', id]) even when opts.resumeId is null; update the guard to also check
whether native passthrough is present (inspect opts.codexArgs for a resume
intent) and avoid taking the fresh-remote path if native resume args exist, or
explicitly preserve/forward those args into the exitReason/object handed to the
launcher (references: remoteSwitchIntentObserved, opts.resumeId, opts.codexArgs,
exitReason, stopChildIfRunning). Ensure behavior is documented with a short
comment if you intentionally prefer to discard native args.

In `@apps/cli/src/backends/codex/runCodex.ts`:
- Line 142: The initial local-launch branches are not forwarding opts.codexArgs,
which causes provider-specific args to be dropped on first launch; update the
callers inside runCodex (the fast-start spawnVendor path and the direct mode ===
'local' launcher call) to pass codexArgs: opts.codexArgs through to the same
object shape used by runCodexLocalModePass so that the remote→local branch and
all local-launch paths receive the same codexArgs; specifically modify the
spawnVendor invocation and the direct local launcher call to include codexArgs:
opts.codexArgs (matching the parameter name codexArgs and the
runCodexLocalModePass signature).

---

Nitpick comments:
In `@apps/cli/src/backends/codex/appServer/runtime.test.ts`:
- Around line 1596-1760: Add two new tests in the same suite in runtime.test.ts:
one named "does not sync title tool calls from sidechain context to parent
thread" that uses createRuntimeFixture(...) and createCodexAppServerRuntime(...)
then runtime.sendPrompt('bridge-mcp-title-tool-sidechain') and asserts
requestLog has no 'thread/name/set' entries and that sendCodexMessage
tool-call-result was still sent; and another named "does not sync title tool
calls before threadId established" that triggers a title-complete prompt before
the runtime establishes the parent thread (e.g., sendPrompt immediately after
startOrLoad without creating a thread) and asserts no 'thread/name/set' requests
were made. Reference createRuntimeFixture, createCodexAppServerRuntime,
runtime.sendPrompt, readRequestLog, and the 'thread/name/set' method in your
assertions.

In `@apps/cli/src/backends/codex/appServer/runtime.ts`:
- Around line 783-811: Add unit/integration tests for the Happier title sync
branch around the tool-result handling: exercise the branch that sends a
'tool-result'/'tool-call-result' and verify pendingHappierTitleToolNamesByCallId
is cleared and no native thread rename is attempted when context.sidechainId is
set, and similarly test the case where threadId is missing (e.g., before
thread/started) so didHappierTitleToolSucceed(update.output) is true but
ensureClient()/client.request('thread/name/set', ...) is not called; use
mocks/spies for ensureClient and client.request to assert no rename attempts and
for logger.debug to ensure failures are safely caught when client.request would
throw.

In `@apps/cli/src/backends/codex/codexLocalLauncher.ts`:
- Around line 120-152: Add a short JSDoc to buildCodexTuiArgs explaining that
when the codexArgs parameter is provided and non-empty the function forwards
those native Codex args and returns immediately, therefore any resumeId is
ignored; also add similar brief JSDoc notes to codexLocalLauncher and
resolveCodexTuiInvocation to document the precedence ("codexArgs wins, resumeId
ignored") so callers are not surprised. Ensure the JSDoc references the
codexArgs and resumeId parameters by name and mirrors the existing style in
nearby functions.

In `@apps/cli/src/cli/dispatch.providerInfoPassthrough.test.ts`:
- Around line 1-53: The test name and describe text no longer match what is
asserted: update the file and describe to reflect that this verifies the
provider command handler runs (suggest renaming file to
dispatch.providerCommandHandler.test.ts and change the describe string
accordingly), and either remove the unused '@/backends/catalog' mock (the
vi.mock that defines requireCatalogEntry) or add a complementary test that
clears/unsets the registered gemini handler (e.g. make commandRegistry.gemini
undefined) so dispatchCli exercises the requireCatalogEntry fallback path;
ensure the complementary test asserts maybePassthroughProviderCliInfoRequest
(passthroughSpy) is not called and that requireCatalogEntry/getCliCommandHandler
path is invoked instead, referencing dispatchCli, geminiHandlerSpy,
passthroughSpy and requireCatalogEntry to locate the code to change.

In `@apps/cli/src/cli/providerCliPassthrough.ts`:
- Around line 8-17: The function detectProviderCliInfoRequest currently checks
HELP_FLAGS before VERSION_FLAGS, so a combined invocation like "happier codex
--help --version" intentionally returns the help flag; add a one-line comment
above the function (or above the HELP_FLAGS/VERSION_FLAGS definitions) noting
that help has precedence over version to make the behavior explicit for future
readers and to justify the order of checks in detectProviderCliInfoRequest
(referencing HELP_FLAGS, VERSION_FLAGS, and the args parameter).

In `@apps/cli/src/cli/providerSessionArgPartition.ts`:
- Around line 222-227: The branch handling the '--account-settings-version-hint'
flag currently consumes an optional value via readOptionalValue(args, i) and
increments i but never returns or records it; either remove this unused branch
(delete the if (arg === '--account-settings-version-hint') block) or, if it must
be accepted for backward/forward compatibility, replace it with a one-line
explanatory comment above the block that explains it's intentionally ignored
(e.g., "daemon-injected hint accepted and discarded for forward compatibility")
and keep the current consume-and-skip logic in the same symbols (arg,
readOptionalValue, i) so future readers understand it's intentional.
- Around line 1-8: Add a top-of-file JSDoc that documents this module's
responsibilities: that it splits process argv into Happier session flags vs
provider-owned args and enumerates forwarding rules used by
runBackendSessionCliCommand and sessionStartArgs (specifically describe
forwardModelFlag, forwardResumeFlag, yoloProviderArgs, directoryFlags, and
versionFlags), include expected semantics for each flag/group (what is
forwarded, when filtered, and who owns parsing), and list any
invariants/contract callers rely on (e.g., order preservation, prefix handling).
Place this comment at the very top of providerSessionArgPartition.ts so future
provider integrations follow the same forwarding contract.

In `@apps/cli/src/cli/sessionStartArgs.ts`:
- Around line 15-29: Introduce a single picker function (e.g., export function
pickSessionStartArgs(parsed: ReturnType<typeof partitionProviderSessionArgs>))
that maps the partitionProviderSessionArgs result to the seven
ParsedSessionStartArgs fields, replace the inline mapping in
parseSessionStartArgs to call pickSessionStartArgs(partitioned) (where
partitioned is the result of partitionProviderSessionArgs), and update the other
caller (pickSessionStartArgs usage in runBackendSessionCliCommand.ts) to import
and reuse this new exported pickSessionStartArgs so the mapping logic lives in
one place.

In `@apps/stack/scripts/testkit/core/env_scope.mjs`:
- Around line 74-77: The current effectiveOverrides assignment on Windows copies
overrides and then adds Path: overrides.PATH, leaving both PATH and Path set;
change it so when process.platform === 'win32' and overrides has 'PATH' you
build effectiveOverrides by copying overrides but excluding the 'PATH' key and
then add Path: overrides.PATH (i.e., remove 'PATH' before adding 'Path'),
referencing effectiveOverrides, overrides, process.platform, 'PATH' and 'Path'
to locate the code to change.

In `@apps/stack/scripts/testkit/core/expo_command_shims.mjs`:
- Around line 20-28: The Windows shim written by writeFile (creating 'expo.cmd')
currently hardcodes the JS outputPath into the batch content; change the batch
line inside the array passed to writeFile (the line creating `echo shim=cmd
bin=%~f0 args=%*>>"${outputPath}"`) to instead read the runtime environment
variable (use %OUTPUT_PATH% or the Windows batch parameter syntax) so the batch
file appends to the path provided at runtime, mirroring the POSIX shim's use of
OUTPUT_PATH; update tests to set OUTPUT_PATH in the environment before invoking
the produced expo.cmd shim.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20c50072-d826-4b93-ba9e-697fc363d6bd

📥 Commits

Reviewing files that changed from the base of the PR and between 9e47270 and b016866.

📒 Files selected for processing (29)
  • apps/cli/src/backends/claude/cli/command.help.test.ts
  • apps/cli/src/backends/claude/cli/command.ts
  • apps/cli/src/backends/claude/cli/command.version.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.ts
  • apps/cli/src/backends/codex/cli/command.test.ts
  • apps/cli/src/backends/codex/cli/command.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.integration.test.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.testkit.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.ts
  • apps/cli/src/backends/codex/runCodex.fastStart.integration.test.ts
  • apps/cli/src/backends/codex/runCodex.ts
  • apps/cli/src/backends/codex/runtime/localModePass.test.ts
  • apps/cli/src/backends/codex/runtime/localModePass.ts
  • apps/cli/src/cli/dispatch.providerInfoPassthrough.test.ts
  • apps/cli/src/cli/dispatch.ts
  • apps/cli/src/cli/providerCliPassthrough.test.ts
  • apps/cli/src/cli/providerCliPassthrough.ts
  • apps/cli/src/cli/providerSessionArgPartition.test.ts
  • apps/cli/src/cli/providerSessionArgPartition.ts
  • apps/cli/src/cli/runBackendSessionCliCommand.test.ts
  • apps/cli/src/cli/runBackendSessionCliCommand.ts
  • apps/cli/src/cli/sessionStartArgs.test.ts
  • apps/cli/src/cli/sessionStartArgs.ts
  • apps/stack/scripts/testkit/core/env_scope.mjs
  • apps/stack/scripts/testkit/core/expo_command_shims.mjs
  • apps/stack/scripts/utils/expo/command.mjs
  • apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs
  • apps/stack/scripts/utils/expo/command_workspace_deps_built.test.mjs

Comment thread apps/cli/src/backends/codex/codexLocalLauncher.ts Outdated
Comment thread apps/cli/src/backends/codex/runCodex.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: 4

♻️ Duplicate comments (2)
apps/cli/src/backends/codex/runCodex.ts (1)

407-414: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

codexArgs still dropped on initial local launches.

The PR threads opts.codexArgs ?? [] into runCodexLocalModePass at Line 1342, but the two initial local-launch paths still call codexLocalLauncher without codexArgs:

  • Fast-start spawnVendor (Lines 407–414)
  • Direct mode === 'local' launcher call (Lines 663–672)

So provider-native args like resume --all from happier codex … reach the local TUI only after a remote→local switch, not on first launch. The previous review flagged this and was marked addressed, but the gap persists for the initial launch sites.

💡 Suggested patch
@@ spawnVendor
                 artifacts.localResult = await codexLocalLauncher<EnhancedMode>({
                     path: requestedDirectory,
                     api: null,
                     session: artifacts.deferredSession as unknown as ApiSessionClient,
                     messageQueue,
                     permissionMode: initialPermissionMode,
                     resumeId: resumeIdFromArgs,
+                    codexArgs: opts.codexArgs ?? [],
                 });
@@ mode === 'local'
         const localResult = await (localLauncherPromise ??
             codexLocalLauncher<EnhancedMode>({
                 path: workspaceDirFromMetadata ?? requestedDirectory,
                 api,
                 session,
                 messageQueue,
                 permissionMode: initialPermissionMode,
                 resumeId: resumeIdFromArgs,
+                codexArgs: opts.codexArgs ?? [],
             }));

Also applies to: 663-672

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/runCodex.ts` around lines 407 - 414, The initial
local-launch call sites are missing provider-native args: update the two
codexLocalLauncher invocations (the fast-start spawnVendor path where
artifacts.localResult is set and the direct mode === 'local' launcher call) to
pass codexArgs: opts.codexArgs ?? [] (same threading used for
runCodexLocalModePass) so that provider args like "resume --all" are forwarded
on first launch; locate the calls to codexLocalLauncher and add the codexArgs
property to the options object passed in.
apps/cli/src/backends/codex/codexLocalLauncher.ts (1)

485-493: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Native codexArgs may still be discarded on fresh-remote fallback.

This branch was previously flagged: when a user runs happier codex resume <id> (native), the partitioner puts ['resume', <id>] into opts.codexArgs while opts.resumeId stays null. If a remote-switch intent fires before a rollout is discovered (queued UI message, pending-queue watcher, or switch RPC), this fallback will hand the user off to a fresh remote (resumeId: null), silently dropping their native resume target.

Consider gating on opts.codexArgs?.length here as well, or document why discarding is intentional for the remote path.

🛡️ Possible fix
-      if (remoteSwitchIntentObserved && !opts.resumeId && now >= deadline) {
+      const hasNativeCodexArgs = (opts.codexArgs?.length ?? 0) > 0;
+      if (remoteSwitchIntentObserved && !opts.resumeId && !hasNativeCodexArgs && now >= deadline) {
         logger.debug('[codex] switch: starting fresh remote because no local rollout was discovered', {
           elapsedSinceStartMs: now - startedAtMs,
         });
         exitReason = { type: 'switch', resumeId: null };
         stopChildIfRunning();
         childExited = true;
         break;
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/codex/codexLocalLauncher.ts` around lines 485 - 493,
The fallback branch that sets exitReason = { type: 'switch', resumeId: null }
can drop native resume targets stored in opts.codexArgs; update the condition in
the codexLocalLauncher logic so it also checks for opts.codexArgs?.length (or
extracts a resume id from opts.codexArgs when present) before treating this as a
fresh-remote fallback, and if a native resume arg exists preserve it by setting
exitReason.resumeId to that id instead of null; modify the block containing
stopChildIfRunning()/childExited=true to respect opts.codexArgs and
opts.resumeId accordingly.
🧹 Nitpick comments (4)
apps/cli/src/cli/providerSessionArgPartition.ts (2)

9-37: ⚡ Quick win

Use interface for exported object shapes

These exported object-shape declarations should be interfaces to match repository TS conventions.

As per coding guidelines, "Prefer interface over type for defining object shapes in TypeScript".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/providerSessionArgPartition.ts` around lines 9 - 37, Replace
the two exported type aliases with exported interfaces: change
ProviderSessionArgPartitionResult and ProviderSessionArgPartitionOptions from
"export type ... = Readonly<{ ... }>" to "export interface ..." preserving all
property names, optionality, union types and readonly array signatures (e.g.
args: readonly string[]), remove the outer Readonly<> wrapper and make each
property readonly where appropriate so the shape and immutability are preserved;
keep the exported names exactly (ProviderSessionArgPartitionResult,
ProviderSessionArgPartitionOptions) so downstream code continues to work.

1-8: ⚡ Quick win

Add a file-level JSDoc responsibility header

This new CLI parser module should include a top-of-file JSDoc block describing responsibilities and boundary behavior.

As per coding guidelines, "Include comprehensive JSDoc comments at the top of each file explaining responsibilities".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/providerSessionArgPartition.ts` around lines 1 - 8, Add a
top-of-file JSDoc block to providerSessionArgPartition.ts that states this
module's responsibility (parsing provider/session-related CLI arguments and
partitioning permission mode aliases), its boundary behavior (pure parsing only,
no side effects such as I/O or process.exit), expected inputs/outputs (uses
isPermissionMode, PermissionMode, PERMISSION_INTENTS, and
parsePermissionModeAliasShared to validate/translate aliases), and
error/edge-case handling (how invalid modes are reported). Keep the block
concise but explicit about exported behavior, dependencies, and any assumptions
so readers know how to use the parser and what it does not do.
apps/cli/src/backends/claude/cli/command.ts (1)

91-100: 💤 Low value

Misleading error when --js-runtime has no value.

When --js-runtime is the last argument or followed by another flag, args[i + 1] is undefined, and the code prints Invalid --js-runtime value: undefined. Consider distinguishing "missing value" from "invalid value" for a clearer message, and ensure a value that starts with - is not silently treated as a runtime selector.

♻️ Suggested refinement
     if (arg === '--js-runtime') {
       const runtime = args[i + 1];
+      if (typeof runtime !== 'string' || runtime.startsWith('-')) {
+        console.error(chalk.red(`Missing value for --js-runtime. Must be 'node' or 'bun'`));
+        process.exit(1);
+      }
       if (runtime !== 'node' && runtime !== 'bun') {
         console.error(chalk.red(`Invalid --js-runtime value: ${runtime}. Must be 'node' or 'bun'`));
         process.exit(1);
       }
       jsRuntime = runtime;
       i += 1;
       continue;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/backends/claude/cli/command.ts` around lines 91 - 100, In the if
(arg === '--js-runtime') handler, change the validation to first check the next
token (args[i + 1]) for existence and that it doesn't start with '-' (treating
that as a missing value) and emit a clear "missing value for --js-runtime" error
if so; otherwise assign runtime = args[i + 1] and then validate that runtime ===
'node' || runtime === 'bun' and emit the existing "invalid --js-runtime value"
message only for other invalid strings; update jsRuntime assignment, increment
i, and continue as before (refer to the --js-runtime branch, jsRuntime variable,
and the args[i + 1] usage).
apps/cli/src/cli/runBackendSessionCliCommand.test.ts (1)

517-587: 💤 Low value

LGTM with one note on copy-pinning.

Coverage is good: provider help passthrough is verified by spawning the resolved provider binary with --help / exec --help and asserting backend startup is bypassed.

One soft note: expect.stringContaining('codex CLI Options') and expect.stringContaining('codex exec --help') couple the test to the exact banner copy in runBackendSessionCliCommand.ts. If the banner wording is the published contract these are fine; otherwise prefer a more stable marker (e.g., a header sentinel constant exported from runBackendSessionCliCommand or buildRootHelpText).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/cli/runBackendSessionCliCommand.test.ts` around lines 517 - 587,
The test is brittle because it asserts exact banner copy ("codex CLI Options"
and "codex exec --help") from runBackendSessionCliCommand; export a stable
header sentinel (e.g., HELP_HEADER or ROOT_HELP_SENTINEL) from
runBackendSessionCliCommand or buildRootHelpText and update the tests to assert
expect.stringContaining(HELP_HEADER) and
expect.stringContaining(`${providerName} exec --help` or a provider-specific
sentinel) instead of the hard-coded phrases so wording changes don't break
tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/cli/src/backends/codex/cli/command.ts`:
- Line 15: Remove the lowercase '-v' entry from the versionFlags array in
command.ts (the symbol to edit is versionFlags) so the array no longer includes
'-v' (e.g. change ['-v','-V','--version'] to ['-V','--version'] or ['--version']
per project convention); update any nearby comments/tests/docs that reference
'-v' if present to avoid claiming it is a Codex CLI version flag.

In `@apps/cli/src/cli/providerSessionArgPartition.ts`:
- Around line 222-226: The flag handling for '--account-settings-version-hint'
currently calls readOptionalValue which treats '-1' as "missing" and thus leaves
'-1' to be forwarded into providerArgs on the next loop; change the branch in
providerSessionArgPartition so that when arg ===
'--account-settings-version-hint' you always consume the next token if present
(check args[i+1] and increment i) regardless of readOptionalValue's boolean
return, and do not push that consumed value into providerArgs; reference the
variables/function names arg, args, i, readOptionalValue, and providerArgs when
making this change.
- Around line 56-64: The function readRequiredValue currently treats any next
token as a value, so option-like tokens (e.g., "--help" or "-h") can be
misinterpreted as values; update readRequiredValue to detect tokens that look
like options (start with '-') and treat them as missing by calling fail with the
same message. Specifically, in readRequiredValue (and keeping the existing fail
function usage), after obtaining value = args[index + 1], add a check like
value.startsWith('-') and call fail(`Missing value for ${label}`) when true,
preserving the existing string/empty checks.

In `@apps/stack/scripts/testkit/core/expo_command_shims.mjs`:
- Around line 13-14: The posix shim currently writes to the environment variable
OUTPUT_PATH instead of using the function argument outputPath (while the other
shim uses outputPath), so update the posix shim string (the one containing 'echo
"shim=posix bin=$0 args=$*" >> "${OUTPUT_PATH:?}"') to use the passed-in
outputPath value consistently (same style as the other shim), ensuring both shim
variants reference the outputPath parameter rather than the OUTPUT_PATH env var.

---

Duplicate comments:
In `@apps/cli/src/backends/codex/codexLocalLauncher.ts`:
- Around line 485-493: The fallback branch that sets exitReason = { type:
'switch', resumeId: null } can drop native resume targets stored in
opts.codexArgs; update the condition in the codexLocalLauncher logic so it also
checks for opts.codexArgs?.length (or extracts a resume id from opts.codexArgs
when present) before treating this as a fresh-remote fallback, and if a native
resume arg exists preserve it by setting exitReason.resumeId to that id instead
of null; modify the block containing stopChildIfRunning()/childExited=true to
respect opts.codexArgs and opts.resumeId accordingly.

In `@apps/cli/src/backends/codex/runCodex.ts`:
- Around line 407-414: The initial local-launch call sites are missing
provider-native args: update the two codexLocalLauncher invocations (the
fast-start spawnVendor path where artifacts.localResult is set and the direct
mode === 'local' launcher call) to pass codexArgs: opts.codexArgs ?? [] (same
threading used for runCodexLocalModePass) so that provider args like "resume
--all" are forwarded on first launch; locate the calls to codexLocalLauncher and
add the codexArgs property to the options object passed in.

---

Nitpick comments:
In `@apps/cli/src/backends/claude/cli/command.ts`:
- Around line 91-100: In the if (arg === '--js-runtime') handler, change the
validation to first check the next token (args[i + 1]) for existence and that it
doesn't start with '-' (treating that as a missing value) and emit a clear
"missing value for --js-runtime" error if so; otherwise assign runtime = args[i
+ 1] and then validate that runtime === 'node' || runtime === 'bun' and emit the
existing "invalid --js-runtime value" message only for other invalid strings;
update jsRuntime assignment, increment i, and continue as before (refer to the
--js-runtime branch, jsRuntime variable, and the args[i + 1] usage).

In `@apps/cli/src/cli/providerSessionArgPartition.ts`:
- Around line 9-37: Replace the two exported type aliases with exported
interfaces: change ProviderSessionArgPartitionResult and
ProviderSessionArgPartitionOptions from "export type ... = Readonly<{ ... }>" to
"export interface ..." preserving all property names, optionality, union types
and readonly array signatures (e.g. args: readonly string[]), remove the outer
Readonly<> wrapper and make each property readonly where appropriate so the
shape and immutability are preserved; keep the exported names exactly
(ProviderSessionArgPartitionResult, ProviderSessionArgPartitionOptions) so
downstream code continues to work.
- Around line 1-8: Add a top-of-file JSDoc block to
providerSessionArgPartition.ts that states this module's responsibility (parsing
provider/session-related CLI arguments and partitioning permission mode
aliases), its boundary behavior (pure parsing only, no side effects such as I/O
or process.exit), expected inputs/outputs (uses isPermissionMode,
PermissionMode, PERMISSION_INTENTS, and parsePermissionModeAliasShared to
validate/translate aliases), and error/edge-case handling (how invalid modes are
reported). Keep the block concise but explicit about exported behavior,
dependencies, and any assumptions so readers know how to use the parser and what
it does not do.

In `@apps/cli/src/cli/runBackendSessionCliCommand.test.ts`:
- Around line 517-587: The test is brittle because it asserts exact banner copy
("codex CLI Options" and "codex exec --help") from runBackendSessionCliCommand;
export a stable header sentinel (e.g., HELP_HEADER or ROOT_HELP_SENTINEL) from
runBackendSessionCliCommand or buildRootHelpText and update the tests to assert
expect.stringContaining(HELP_HEADER) and
expect.stringContaining(`${providerName} exec --help` or a provider-specific
sentinel) instead of the hard-coded phrases so wording changes don't break
tests.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d77634c-be5c-47c1-a1f2-109bb05cd235

📥 Commits

Reviewing files that changed from the base of the PR and between 9e47270 and b016866.

📒 Files selected for processing (29)
  • apps/cli/src/backends/claude/cli/command.help.test.ts
  • apps/cli/src/backends/claude/cli/command.ts
  • apps/cli/src/backends/claude/cli/command.version.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.test.ts
  • apps/cli/src/backends/codex/appServer/runtime.ts
  • apps/cli/src/backends/codex/cli/command.test.ts
  • apps/cli/src/backends/codex/cli/command.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.integration.test.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.testkit.ts
  • apps/cli/src/backends/codex/codexLocalLauncher.ts
  • apps/cli/src/backends/codex/runCodex.fastStart.integration.test.ts
  • apps/cli/src/backends/codex/runCodex.ts
  • apps/cli/src/backends/codex/runtime/localModePass.test.ts
  • apps/cli/src/backends/codex/runtime/localModePass.ts
  • apps/cli/src/cli/dispatch.providerInfoPassthrough.test.ts
  • apps/cli/src/cli/dispatch.ts
  • apps/cli/src/cli/providerCliPassthrough.test.ts
  • apps/cli/src/cli/providerCliPassthrough.ts
  • apps/cli/src/cli/providerSessionArgPartition.test.ts
  • apps/cli/src/cli/providerSessionArgPartition.ts
  • apps/cli/src/cli/runBackendSessionCliCommand.test.ts
  • apps/cli/src/cli/runBackendSessionCliCommand.ts
  • apps/cli/src/cli/sessionStartArgs.test.ts
  • apps/cli/src/cli/sessionStartArgs.ts
  • apps/stack/scripts/testkit/core/env_scope.mjs
  • apps/stack/scripts/testkit/core/expo_command_shims.mjs
  • apps/stack/scripts/utils/expo/command.mjs
  • apps/stack/scripts/utils/expo/command_heap_limit_env.test.mjs
  • apps/stack/scripts/utils/expo/command_workspace_deps_built.test.mjs

Comment thread apps/cli/src/backends/codex/cli/command.ts Outdated
Comment thread apps/cli/src/cli/providerSessionArgPartition.ts
Comment thread apps/cli/src/cli/providerSessionArgPartition.ts
Comment thread apps/stack/scripts/testkit/core/expo_command_shims.mjs Outdated
Refs happier-dev#168

Co-authored-by: 九川三江 <jiuchuanll@users.noreply.github.com>
@leeroybrun leeroybrun merged commit 5315f80 into happier-dev:dev May 11, 2026
1 check 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