Last active
March 19, 2026 16:56
-
-
Save flavono123/99454d05648e3dadf92ea765d2a849a7 to your computer and use it in GitHub Desktop.
Fix tmux send-keys race condition with Zsh ZLE — Claude Code headless teammate workaround for tmux users (iTerm2 alternative). Keywords: claude-code, tmux, zsh, zle, send-keys, race-condition, AI agent, anthropic, agentic-coding, coding-agent, headless, teammate, swarm
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
| # tmux-zle-replay-for-claude-code-teammate.zsh | |
| # | |
| # Keywords: claude-code, claude-code-teammate, claude-code-headless, tmux, | |
| # zsh, zle, send-keys, race-condition, terminal, CLI, AI-agent, | |
| # anthropic, coding-agent, agentic-coding, pair-programming | |
| # | |
| # Fix for tmux send-keys race condition with Zsh's ZLE (Zsh Line Editor). | |
| # | |
| # WHY YOU NEED THIS: | |
| # If you run Claude Code teammates (headless agents) inside tmux instead of | |
| # iTerm2, commands sent via `tmux send-keys` can silently fail. The command | |
| # text appears on screen but never executes because Zsh's line editor (ZLE) | |
| # wasn't ready yet. This script fixes that. | |
| # | |
| # PROBLEM: | |
| # When an external process (e.g. Claude Code headless teammate) sends a | |
| # command to a tmux pane via `tmux send-keys`, it can arrive before ZLE is | |
| # initialized. The keystrokes get echoed to the terminal screen but are | |
| # never processed by ZLE — the command appears on screen but doesn't execute. | |
| # | |
| # This is a known issue when using Claude Code's "teammate" feature inside | |
| # tmux, where the teammate spawns a new shell pane and immediately sends | |
| # a command. | |
| # | |
| # SOLUTION: | |
| # This script hooks into `zle-line-init` (fired when ZLE becomes ready) and | |
| # captures the current pane content via `tmux capture-pane`. If it finds a | |
| # command containing the marker string (CLAUDECODE=1), it replays the command | |
| # by injecting it into ZLE's BUFFER and calling `accept-line`. | |
| # | |
| # Both hooks are one-shot: they remove themselves after the first prompt, | |
| # so there is zero overhead on subsequent commands. | |
| # | |
| # USAGE: | |
| # Add to your .zshrc: | |
| # | |
| # source /path/to/tmux-zle-replay-for-claude-code-teammate.zsh | |
| # | |
| # It only activates inside a tmux session ($TMUX_PANE is set). | |
| # Outside of tmux, this script is a no-op. | |
| # | |
| # TESTED WITH: | |
| # - Claude Code CLI (claude --headless, teammates/swarm) | |
| # - tmux 3.x + Zsh 5.9+ | |
| # - macOS (iTerm2, Terminal.app, Alacritty, Ghostty, WezTerm) | |
| # - Linux (GNOME Terminal, Kitty, Alacritty) | |
| # | |
| # CUSTOMIZATION: | |
| # Change the grep pattern below if your tool uses a different marker string. | |
| # Claude Code uses `CLAUDECODE=1` as an environment variable prefix. | |
| # | |
| # SEE ALSO: | |
| # - Claude Code docs: https://docs.anthropic.com/en/docs/claude-code | |
| # - tmux send-keys: https://man.openbsd.org/tmux#send-keys | |
| # - Zsh ZLE: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html | |
| # | |
| # LICENSE: MIT | |
| if [[ -n "$TMUX_PANE" ]]; then | |
| __tmux_replay_setup() { | |
| __tmux_zle_replay() { | |
| local content cmd | |
| # Capture the visible content of the current tmux pane | |
| content=$(tmux capture-pane -t "$TMUX_PANE" -p -J 2>/dev/null) | |
| # Look for the marker that indicates a command was sent before ZLE was ready | |
| cmd=$(printf '%s' "$content" | grep -m1 'CLAUDECODE=1') | |
| if [[ -n "$cmd" ]]; then | |
| BUFFER="$cmd" | |
| zle accept-line | |
| fi | |
| # Remove this widget — only needed once | |
| zle -D zle-line-init 2>/dev/null | |
| } | |
| zle -N zle-line-init __tmux_zle_replay | |
| # Remove this precmd hook — only needed once | |
| add-zsh-hook -d precmd __tmux_replay_setup | |
| } | |
| autoload -U add-zsh-hook | |
| add-zsh-hook precmd __tmux_replay_setup | |
| fi |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When do you need this?
If you use Claude Code teammates (headless agents / swarm) inside tmux and notice that commands sent to new panes silently fail — the text appears on screen but never executes — this script fixes it.
The root cause
tmux send-keysdelivers keystrokes immediately, but Zsh's ZLE (line editor) takes a moment to initialize. If the keys arrive before ZLE is ready, they get echoed to the terminal but are never processed as input.How it works
zle-line-inithooktmux capture-paneCLAUDECODE=1marker, replays the command through ZLEQuick install
Tags: #claude-code #tmux #zsh #zle #ai-agent #anthropic #coding-agent #headless #teammate #agentic-coding #terminal #cli #race-condition #send-keys