fix(tmux): restore Ctrl+Z suspend/resume inside attached sessions#328
Open
rhukster wants to merge 1 commit intoasheshgoplani:mainfrom
Open
fix(tmux): restore Ctrl+Z suspend/resume inside attached sessions#328rhukster wants to merge 1 commit intoasheshgoplani:mainfrom
rhukster wants to merge 1 commit intoasheshgoplani:mainfrom
Conversation
Ctrl+Z was broken inside agent-deck tmux sessions in two independent
ways, both preventing proper job control (suspend + fg resume):
1. The PTY master connecting agent-deck to `tmux attach-session` had
default terminal settings with ISIG enabled. When Ctrl+Z (byte 0x1A)
was written to the PTY, the kernel line discipline interpreted it as
SUSP and delivered SIGTSTP to the tmux attach process — causing it to
exit and returning the user to the session list (identical to Ctrl+Q).
Fix: set the PTY master to raw mode so all bytes pass through
transparently to tmux.
2. Compound session commands (containing $() subshells) are wrapped in
`bash -c` for fish shell compatibility. This non-interactive bash has
no job control, so when Claude Code sent SIGTSTP to itself, the
bash -c parent couldn't handle the stopped child — leaving the
terminal in an unrecoverable state. Two changes fix this:
- Add `exec` before the final claude invocation in compound commands,
so claude replaces the bash -c process and becomes a direct child
of the interactive shell where job control works.
- Only apply `wrapIgnoreSuspend` (stty susp undef) for sandboxed
sessions where the command runs as the pane's initial process with
no interactive shell. Non-sandbox sessions use send-keys into the
user's shell, so Ctrl+Z works naturally.
Also removes a duplicate stripControlCharsPreserveANSI function
introduced by a merge.
47bd8ac to
a5a945d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ctrl+Z (suspend) has been broken inside agent-deck tmux sessions, leaving users in an unrecoverable frozen terminal state where control characters echo as raw text (
^Z^Z^Z^Z). Only Ctrl+Q (detach) still worked. This PR fixes two independent root causes:Problem 1: PTY line discipline suspending the tmux client
The PTY master fd connecting agent-deck to
tmux attach-sessionhad default terminal settings withISIGenabled. When the user pressed Ctrl+Z, the PTY's line discipline interpreted byte0x1AasSUSPand deliveredSIGTSTPto the tmux attach process — suspending the tmux client. This left the user in a frozen limbo state: no process was actively reading input, so further control characters were simply echoed as text. Only Ctrl+Q escaped because it is intercepted in the stdin-reader goroutine before bytes reach the PTY.Fix: Set the PTY master to raw mode (
term.MakeRaw) immediately after creation, so all bytes pass through transparently to tmux. This is safe because:os.Stdin/os.Stdoutand Bubble Tea's terminal stateProblem 2: Non-interactive
bash -cwrapper breaking job controlCompound session commands (containing
$()subshells for UUID generation) are wrapped inbash -c '...'for fish shell compatibility. This non-interactive bash shell has no job control. When Claude Code handled Ctrl+Z internally by sendingSIGTSTPto itself, thebash -cparent couldn't handle the stopped child — leaving the pane in a broken state.Additionally,
wrapIgnoreSuspendwrapped every command inbash -c 'stty susp undef; ...', which disabled the terminal's ability to generateSIGTSTPfrom Ctrl+Z.Fix (two parts):
execbefore the finalclaudeinvocation in compound commands. This replaces thebash -cprocess with claude, making it a direct child of the interactive shell where job control works naturally (Ctrl+Z →[1]+ Stopped→ shell prompt →fgto resume).wrapIgnoreSuspendfor sandboxed sessions, where the command runs as the pane's initial process with no interactive shell for job control. Non-sandbox sessions launch viasend-keysinto the user's interactive shell, so standard job control works without intervention.Test plan
fg— Claude should resume normallyexecprefix