Skip to content

Fix Claude hooks-disabled env passthrough - #4418

Merged
austinywang merged 8 commits into
mainfrom
issue-4151-hooks-disabled-strips-env
May 29, 2026
Merged

austinywang merged 8 commits into
mainfrom
issue-4151-hooks-disabled-strips-env

Conversation

@austinywang

@austinywang austinywang commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • preserve the cmux terminal environment when CMUX_CLAUDE_HOOKS_DISABLED=1 so user-managed Claude hooks can read CMUX_SOCKET_PATH and workspace metadata
  • treat hooks-disabled as an explicit user-managed-hooks passthrough for both interactive and command-like Claude invocations
  • keep Claude auth-selection cleanup and config normalization on the disabled-hooks passthrough path
  • keep CMUX env stripping on command-like passthrough paths when cmux is still managing hook injection
  • add regression probes for disabled-hooks CMUX env passthrough and stale auth cleanup

Closes #4151.

Testing

  • locally reproduced the current wrapper behavior with CMUX_CLAUDE_HOOKS_DISABLED=1: fake real claude saw no CMUX_* variables
  • reran the same narrow wrapper repro after the fix: fake real claude saw CMUX_SOCKET_PATH, CMUX_WORKSPACE_ID, CMUX_TAB_ID, and CMUX_PANEL_ID, while stale ANTHROPIC_API_KEY was cleared
  • did not run local tests, reload.sh, or xcodebuild per task constraints

Demo Video

Not applicable; this is deterministic shell-wrapper environment behavior.

Review Trigger Checklist

  • Regression test added before the fix commit
  • Wrapper repro documented in PR body
  • No local reload.sh or xcodebuild run

Note

Low Risk
Shell-wrapper passthrough behavior only, with targeted regression tests and no changes to hook injection or app auth flows when hooks remain enabled.

Overview
When CMUX_CLAUDE_HOOKS_DISABLED=1, the Claude wrapper now takes a dedicated early passthrough that execs the real binary without stripping CMUX_* terminal metadata (socket, workspace/tab/panel IDs, etc.), so user-managed hooks can read cmux context. That path still unsets CLAUDECODE, runs stale auth-selection cleanup / config-dir normalization, and does not inject session or hook settings—for both interactive and command-like invocations.

Command-like passthrough when cmux still manages hooks (e.g. agents) continues to clear the full cmux terminal fingerprint via exec_real_claude_passthrough.

Regression tests cover hooks-disabled env preservation and stale ANTHROPIC_* scrubbing before passthrough.

Reviewed by Cursor Bugbot for commit 86ac4d6. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Improvements

    • Optimized Claude wrapper to bypass hook injection logic more efficiently when disabled, with improved handling of environment variables during passthrough.
    • Refined wrapper conditions to better detect and handle unavailable cmux sockets.
  • Tests

    • Expanded regression test coverage for wrapper behavior when hook injection is disabled, including environment variable preservation and auth selection workflows.

Review Change Stack

@vercel

vercel Bot commented May 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 26, 2026 7:54am
cmux-staging Building Building Preview, Comment May 26, 2026 7:54am

@coderabbitai

coderabbitai Bot commented May 20, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR fixes a regression where CMUX_CLAUDE_HOOKS_DISABLED=1 stripped CMUX_* environment variables that custom hooks require. The wrapper now early-exits and preserves those variables when hooks are disabled, with test coverage validating the preserved env behavior and isolating agent-specific passthrough logic.

Changes

Preserve CMUX_ env vars for disabled hooks*

Layer / File(s) Summary
Wrapper early-exit for hooks disabled
Resources/bin/claude
When CMUX_CLAUDE_HOOKS_DISABLED=1, the wrapper immediately resolves and execs the real claude binary with original args, bypassing passthrough logic that would strip CMUX_* variables. The subsequent passthrough condition is adjusted so nested-session marker cleanup and auth env clearing apply only to IN_CMUX==0 or missing socket cases, not to the disabled-hooks path.
Test coverage for env preservation
tests/test_claude_wrapper_hooks.py
Test suite isolates agents-subcommand passthrough behavior into its own test case and adds new test_hooks_disabled_preserves_cmux_terminal_env_for_custom_hooks plus a stale-auth-selection regression test. The test helper now accepts hooks_disabled to mark the wrapper environment accordingly and main() runs the new preservation test.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A wrapper's path now splits with care,
When hooks disabled, env's laid bare,
CMUX_SOCKET_PATH and friends survive,
Custom hooks get what they need—thrives!
Tests stand guard, regression's past.

🚥 Pre-merge checks | ✅ 17
✅ Passed checks (17 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: fixing the Claude hooks-disabled environment variable passthrough issue.
Linked Issues check ✅ Passed The PR code changes fully address issue #4151 objectives: preserving CMUX_* env vars when CMUX_CLAUDE_HOOKS_DISABLED=1, maintaining auth cleanup on the disabled-hooks path, and adding regression tests for the fixed behavior.
Out of Scope Changes check ✅ Passed All code changes directly relate to fixing the hooks-disabled environment passthrough issue. The wrapper changes and test additions are scoped to addressing #4151 requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Cmux Swift Actor Isolation ✅ Passed PR contains no Swift code changes - only bash script and Python test modifications. Check is not applicable.
Cmux Swift Blocking Runtime ✅ Passed No Swift files modified. Changes only affect a bash wrapper script and Python test file. Swift blocking runtime check not applicable.
Cmux No Hacky Sleeps ✅ Passed PR introduces no sleeps, polling, or timing workarounds. Changes use conditional env vars and immediate exec(), with deterministic test scaffolding only.
Cmux Swift Concurrency ✅ Passed No Swift files modified in this PR—only bash script (Resources/bin/claude) and Python test file (tests/test_claude_wrapper_hooks.py). Check only applies to cmux-owned Swift code.
Cmux Swift @Concurrent ✅ Passed PR modifies only Bash and Python files (Resources/bin/claude and tests/test_claude_wrapper_hooks.py); no Swift files changed, so Swift @concurrent annotation rules don't apply.
Cmux Swift File And Package Boundaries ✅ Passed PR contains no Swift files—only Bash wrapper script and Python test modifications. Custom check for Swift file/package boundaries is not applicable.
Cmux Swift Logging ✅ Passed No Swift production code files were modified in this PR. The changes are to a bash script (Resources/bin/claude) and Python tests. The swift-logging rule applies only to Swift source files.
Cmux User-Facing Error Privacy ✅ Passed Production code only outputs generic error message. Test code contains env vars in developer-only assertions, allowed per policy. No credentials or sensitive data exposed in user-facing text.
Cmux Full Internationalization ✅ Passed PR modifies shell script wrapper and tests. Tests are excluded by i18n rules. Error message pre-exists; not new user-facing UI text requiring localization.
Cmux Swiftui State Layout ✅ Passed PR modifies only a shell script wrapper (Resources/bin/claude) and Python test file (tests/test_claude_wrapper_hooks.py) with no SwiftUI code changes; SwiftUI state layout check is not applicable.
Cmux Architecture Rethink ✅ Passed PR modifies only Bash and Python files, not Swift code. The swift-architectural-rethink rule applies exclusively to Swift architecture changes.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains only bash script and Python test changes; no Swift window code added or modified. Custom check for Swift auxiliary windows is not applicable.
Description check ✅ Passed PR description is comprehensive and aligns with the template structure, including Summary, Testing, and checklist items.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-4151-hooks-disabled-strips-env

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.

Comment thread Resources/bin/claude

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Resources/bin/claude`:
- Around line 195-199: The early-exit path that checks
CMUX_CLAUDE_HOOKS_DISABLED currently execs the real claude (via
REAL_CLAUDE="$(find_real_claude)" and exec "$REAL_CLAUDE" "$@") but does not
call clear_inherited_claude_auth_selection_env; modify this branch to invoke
clear_inherited_claude_auth_selection_env before exec so inherited auth vars
(ANTHROPIC_API_KEY, ANTHROPIC_MODEL, CLAUDE_CODE_USE_VERTEX, etc.) are cleared
consistently with the other passthrough paths while preserving Vertex/Bedrock
creds per the function's logic.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 0afc4b7c-6594-4ac1-886c-b300d8d29fd0

📥 Commits

Reviewing files that changed from the base of the PR and between d6ec6b2 and a9e5fe8.

📒 Files selected for processing (2)
  • Resources/bin/claude
  • tests/test_claude_wrapper_hooks.py

Comment thread Resources/bin/claude

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

@greptile-apps

greptile-apps Bot commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes CMUX_CLAUDE_HOOKS_DISABLED=1 passthrough by splitting it into its own early-exit path that preserves the full cmux terminal environment instead of routing through exec_real_claude_passthrough (which strips all CMUX_* vars and TERMINFO). CLAUDECODE is still unset to prevent nested-session detection and stale auth-selection env is still cleared via clear_inherited_claude_auth_selection_env.

  • Separates the CMUX_CLAUDE_HOOKS_DISABLED=1 branch from the IN_CMUX==0 || !cmux_socket_available passthrough, making hooks-disabled a dedicated user-managed-hooks path that no longer strips CMUX_* or TERMINFO.
  • Splits test_agents_subcommand_removes_cmux_terminal_fingerprint into two focused tests: one asserting agents still strips CMUX env when hooks are managed by cmux, and a new test_hooks_disabled_preserves_cmux_terminal_env_for_custom_hooks explicitly verifying CMUX env is intact for both interactive and command-like invocations when hooks are disabled.
  • Adds test_hooks_disabled_clears_stale_auth_selection_before_passthrough to regression-cover auth cleanup on the hooks-disabled path.

Confidence Score: 5/5

Safe to merge — the change is a clean behavioural split with explicit regression coverage for the new path.

The hooks-disabled branch is narrow and self-contained: it unsets CLAUDECODE, runs auth cleanup, and execs real claude without touching CMUX_* or TERMINFO — exactly the stated intent. The remaining passthrough and hook-injection paths are untouched. New tests exercise both interactive and command-like invocations under hooks-disabled and verify auth-selection cleanup, covering the core regression surface.

No files require special attention.

Important Files Changed

Filename Overview
Resources/bin/claude Adds a dedicated early-exit block for CMUX_CLAUDE_HOOKS_DISABLED=1 that preserves CMUX_* env and TERMINFO, unsets CLAUDECODE, clears stale auth selection, and execs real claude directly; the remaining passthrough condition and exec_real_claude_passthrough (which strips CMUX_*) are unchanged.
tests/test_claude_wrapper_hooks.py Refactors and expands hook wrapper regression tests: splits the agents fingerprint test into two focused cases, adds a hooks-disabled CMUX env preservation probe (interactive + command-like), and adds an auth-selection cleanup regression for the hooks-disabled path.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[claude wrapper invoked] --> B{CMUX_CLAUDE_HOOKS_DISABLED == 1?}
    B -- Yes --> C[unset CLAUDECODE if IN_CMUX=1]
    C --> D[clear_inherited_claude_auth_selection_env]
    D --> E[exec real claude\nCMUX_* and TERMINFO preserved]
    B -- No --> F{IN_CMUX == 0 OR socket unavailable?}
    F -- Yes --> G[unset CLAUDECODE if IN_CMUX=1]
    G --> H[clear_inherited_claude_auth_selection_env]
    H --> I[exec_real_claude_passthrough\nCMUX_* and TERMINFO stripped]
    F -- No --> J[find real claude]
    J --> K{should_inject_claude_hooks?}
    K -- No command-like --> L[clear auth env]
    L --> I
    K -- Yes interactive --> M[unset CLAUDECODE]
    M --> N[clear auth env]
    N --> O[inject --session-id and --settings hooks JSON]
    O --> P[exec real claude with hooks]
Loading

Reviews (6): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread Resources/bin/claude
Comment thread tests/test_claude_wrapper_hooks.py
@austinywang
austinywang merged commit d7b5b14 into main May 29, 2026
19 checks passed

This branch was successfully deployed

1 active deployment
Preview – cmux — 86ac4d64 Deployed May 26, 2026 by vercel[bot]
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.

CMUX_CLAUDE_HOOKS_DISABLED=1 strips all CMUX_* env vars in v0.64.5, breaking custom hooks

2 participants