Skip to content

fix: wait for interactive shell before sending pane commands - #193

Open
mvanhorn wants to merge 1 commit into
raine:mainfrom
mvanhorn:fix/155-send-keys-lost-shell-init
Open

fix: wait for interactive shell before sending pane commands#193
mvanhorn wants to merge 1 commit into
raine:mainfrom
mvanhorn:fix/155-send-keys-lost-shell-init

Conversation

@mvanhorn

Copy link
Copy Markdown

Summary

When workmux add opens a new workbox, pane commands (e.g. nvim or a long-running CLI) flash briefly and never execute for users running an async prompt theme like Pure. The handshake wrapper (TmuxHandshake in src/multiplexer/handshake.rs) signals readiness via tmux wait-for -U <channel> before exec <shell> -l, so setup_panes writes the command into the PTY while the shell is still initializing. Plain zsh eventually reads the buffered characters, but Pure's zpty-based async workers reset ZLE state during init and flush the terminal input buffer, silently discarding the keystrokes.

This moves readiness detection to after the shell is interactive. After the existing wait() unblocks, a bounded post-exec wait (~5s, matching HANDSHAKE_TIMEOUT_SECS) polls tmux capture-pane -p until the pane content is non-empty and stable across two consecutive polls (prompt rendered and init settled), then falls back to the current behavior on timeout so minimal-rc shells are neither slowed nor broken. It is wired into the shared setup_panes between handshake.wait() and send_keys(...), so all pane commands benefit.

Why this matters

The issue includes a self-contained repro that loses the keystrokes with Pure and succeeds without it. The root cause is a race between shell init flushing the input buffer and workmux writing the command; the pre-exec wait-for only proves the wrapper reached the exec, not that the shell is ready to read. The fix keeps the change tmux-focused per the issue - the wrapper script is unchanged (echo suppression is still needed) and other multiplexer backends keep their existing handshake semantics through the trait default.

Testing

Added tests/test_workmux_add/test_shell_init.py, which uses a ZDOTDIR with a Pure-style async init that flushes PTY input during startup and asserts the configured pane command still executes. The timeout fallback path preserves current behavior for minimal shells.

Fixes #155

@raine

raine commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Not sure I want to change the handshake process to poll for all users because one specific prompt implementation has issues (this doesn't appear to be a widespread issue). Maybe it could be an opt-in configuration option.

@mvanhorn

Copy link
Copy Markdown
Author

Fair point — I agree it shouldn't change the handshake for everyone when it's specific to one prompt implementation. Happy to rework this as an opt-in config option (default off) so only users who hit it enable the wait. Want me to go that route?

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.

send-keys commands silently lost during shell initialization

2 participants