Repository navigation
Fix Claude Code auto-resume under a fish login shell (#6285) - #6866
austinywang wants to merge 8 commits into
Conversation
… login shells
The Claude Code resume binding carries a POSIX `{ cd …; } && …` cwd-guard.
fish (and csh/tcsh) cannot parse `{ …; }` command grouping, so when the
session-restore launcher dispatches the command via `$SHELL -c <command>` to a
fish login shell it errors before the agent launches and the tab drops to a bare
fish prompt.
This test asserts commandThenReturnLines hands fish/csh/tcsh a single external
`/bin/sh -c '<command>'` invocation while zsh/bash keep running the command
natively. It fails until the accompanying fix lands.
Test-only commit (red) per the regression test commit policy.
Refs #6285
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Claude Code resume binding carries a POSIX `{ cd …; } && …` cwd-guard
(emitted whenever cwd is non-null). The session-restore launcher
(TerminalStartupReturnShellScript.commandThenReturnLines) dispatched that
command to the user's login shell via `$SHELL -c <command>`. zsh/bash parse it
natively, but fish rejects `{ …; }` command grouping ("'{ ... }' is not
supported for grouping commands. Please use 'begin; ...; end'") and csh/tcsh
cannot parse `{ …; }`/`2>` either, so the resume errored out before the agent
launched and the tab dropped to a bare login-shell prompt — the #6285 failure,
distinct from the #5796 executable-resolution problem and not fixed by #5678.
Hand the non-POSIX shells (the `csh|tcsh` and `*` case branches) a single
external `/bin/sh -c '<command>'` invocation instead. Every shell parses one
external command identically; the user's shell still sources its own config
(PATH/env) before exec'ing /bin/sh, which inherits it; and the cwd-guard runs
inside /bin/sh exactly as on zsh/bash, so the session still resumes in its
working directory (an improvement over the cwd:null user workaround). zsh/bash
keep running the command natively under -lic.
Verified end-to-end with /bin/tcsh (a non-POSIX proxy for fish, present on every
macOS runner): the unwrapped guard-bearing command fails with
"{: Command not found"; the wrapped form resumes through the cmux wrapper.
Adds a CI-active tcsh end-to-end regression plus faithful test dispatch
modelling; corrects the stale #5639 fish test comment.
Fixes #6285
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe resume-launch script now wraps POSIX-only commands in ChangesResume command dispatch
Sequence Diagram(s)sequenceDiagram
participant Launcher as restore launcher
participant Script as TerminalStartupReturnShellScript
participant Shell as login shell
participant Sh as /bin/sh
Launcher->>Script: commandThenReturnLines(command, workingDirectory)
alt zsh or bash
Script-->>Launcher: resumeCommand
Launcher->>Shell: run native POSIX resume command
else csh, tcsh, fish, other
Script-->>Launcher: /bin/sh -c 'resumeCommand'
Launcher->>Shell: run wrapper command
Shell->>Sh: invoke /bin/sh -c
Sh->>Launcher: execute original resume command
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 2 warnings)
✅ Passed checks (22 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Greptile SummaryFixes Claude Code auto-resume failures under fish, csh, and tcsh login shells by wrapping the resume command in
Confidence Score: 5/5Minimal-footprint fix touching only the non-POSIX shell dispatch branch of commandThenReturnLines; zsh/bash behaviour is entirely unchanged. The two-layer quoting (posixSingleQuoted for the inner /bin/sh payload, TerminalStartupShellQuoting.singleQuoted for the outer zsh-script embedding) is correct and verified by both a unit test that checks the generated script text and an end-to-end test that runs the command through /bin/tcsh. The private helpers are pure functions with no ambient state, the DispatchSemaphore in the test harness is standard bounded-wait scaffolding, and no production actor isolation or logging paths are touched. No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant ZSH as zsh launcher script
participant SHELL as Login Shell ($SHELL)
participant SH as /bin/sh
participant Claude as claude (agent)
ZSH->>ZSH: "_cmux_resume_shell="${SHELL}""
alt zsh or bash
ZSH->>SHELL: "$SHELL -lic '<POSIX resume command>'"
SHELL->>Claude: exec claude --resume id
else fish / csh / tcsh
ZSH->>SHELL: $SHELL -c '/bin/sh -c POSIX-resume-command'
SHELL->>SH: "/bin/sh -c '<POSIX resume command>'"
SH->>Claude: exec claude --resume id
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant ZSH as zsh launcher script
participant SHELL as Login Shell ($SHELL)
participant SH as /bin/sh
participant Claude as claude (agent)
ZSH->>ZSH: "_cmux_resume_shell="${SHELL}""
alt zsh or bash
ZSH->>SHELL: "$SHELL -lic '<POSIX resume command>'"
SHELL->>Claude: exec claude --resume id
else fish / csh / tcsh
ZSH->>SHELL: $SHELL -c '/bin/sh -c POSIX-resume-command'
SHELL->>SH: "/bin/sh -c '<POSIX resume command>'"
SH->>Claude: exec claude --resume id
end
Reviews (7): Last reviewed commit: "Move POSIX resume wrapper helper to file..." | Re-trigger Greptile |
The GitHub Actions check suite was not created for the prior commit (missed pull_request synchronize event under heavy org CI load); this empty commit forces a fresh synchronize so required checks run on the budget-fixed tree. Squash-merge discards this commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ding-cwd-guard-uses-posix # Conflicts: # .github/swift-file-length-budget.tsv
This comment has been minimized.
This comment has been minimized.
…ding-cwd-guard-uses-posix # Conflicts: # .github/swift-file-length-budget.tsv # cmuxTests/SessionPersistenceTests.swift
Fixes #6285
Problem
On 0.64.16 (which already includes the fish shell integration from #5678), Claude Code sessions still fail to auto-resume when the login shell is fish. The executable resolves correctly to
claude --resume <id>(so this is not thebash --resumeproblem of #5796), but the resume binding'scommandcarries a POSIX{ cd …; } && …cwd-guard (emitted whenevercwdis non-null), and the restore launcher dispatches it to the login shell:fish has no
{ …; }command grouping (it usesbegin; …; end), so it errors beforeclaudelaunches and the tab drops to a bare fish prompt. zsh/bash parse the guard fine — this is specific to fish (and csh/tcsh) as login shell. This is a distinct failure mode from #5796 and is not resolved by #5678.Root cause
TerminalStartupReturnShellScript.commandThenReturnLines(the session-restore launcher) dispatches the resume command to the user's login shell via$SHELL -c <command>for itscsh|tcshand*(fish) branches. The #5639 fix already wraps the inner claude command in/bin/sh -c '…'so the POSIX-only wrapper token parses in non-POSIX shells — but it deliberately left the{ cd …; }cwd-guard outside that wrap (so cd-prefix rewriting keeps composing), and that outer guard is itself fish-incompatible.Fix
Hand the non-POSIX login shells a single external
/bin/sh -c '<command>'invocation (thecsh|tcshand*case branches) instead of the raw POSIX command. Every shell parses one external command identically; the user's shell still sources its own config (PATH/env) before exec'ing/bin/sh, which inherits it; and the cwd-guard runs inside/bin/shexactly as it does on zsh/bash — so the session resumes in its working directory (an improvement over thecwd: nulluser workaround, which dropped thecd). zsh/bash keep running the command natively under-lic.This is the issue's proposed fix #2 (wrap the dispatched command), applied at the dispatch seam — so the stored binding, the command generators, and the cd-prefix canonicalizers are all unchanged.
Verification
/bin/tcshis a faithful non-POSIX proxy for fish (same{ …; }limitation) and is present on every macOS runner. Empirically:rc=1,{: Command not found.cdthen reaches the cmux wrapper:rc=0.The same
/bin/sh -c '…'form is what the reporter independently verified resumes correctly under fish.Tests (two-commit red/green)
testResumeLauncherWrapsCommandForNonPOSIXLoginShells(unit, CI-active, no fish needed) — assertscommandThenReturnLineswraps the command in/bin/sh -cfor the fish/csh/tcsh branches while zsh/bash keep running it natively under-lic. Committed first as a failing test (red), then the fix (green).testClaudeResumeCommandWithWorkingDirectoryExecutesThroughWrapperInsideTcshLauncher(end-to-end, CI-active) — runs the guard-bearing resume through/bin/tcshand asserts it reaches the cmux wrapper with--settings.runClaudeResumeCommandtest harness to model the production dispatch (wrap for non-POSIX shells), and corrected the stale Claude resume still drops cmux hooks after #5430: bareclaudedoesn't resolve to the wrapper inside the $SHELL -lic restore launcher #5639 fish-test comment that incorrectly claimed fish parses{ …; }.Scope / localization
No user-facing strings are added or changed (only shell command generation, code comments, and tests), so no
Localizable.xcstrings/ web message-catalog updates are required.🤖 Generated with Claude Code
Summary by cubic
Fixes Claude Code auto-resume under
fishby running the resume command through/bin/shfor non-POSIX login shells, so sessions restore in the correct working directory. Fixes #6285.Bug Fixes
fish,csh, andtcsh, dispatch a single/bin/sh -c '<command>'instead of the raw POSIX command;zsh/bashkeep-lic.Refactors
/bin/tcshend-to-end resume test; updated the harness and file-length budget.Written for commit 50ff976. Summary will update on new commits.
Summary by CodeRabbit
csh,tcsh, andfish.zshandbashwhile avoiding shell parsing errors during resume.