Skip to content

Instantly share code, notes, and snippets.

@flavono123
Last active March 19, 2026 16:56
Show Gist options
  • Select an option

  • Save flavono123/99454d05648e3dadf92ea765d2a849a7 to your computer and use it in GitHub Desktop.

Select an option

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
# 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
@flavono123
Copy link
Copy Markdown
Author

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-keys delivers 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

  1. Registers a one-shot zle-line-init hook
  2. When ZLE becomes ready, captures the pane screen via tmux capture-pane
  3. If it finds the CLAUDECODE=1 marker, replays the command through ZLE
  4. Cleans up after itself — zero overhead on subsequent commands

Quick install

# Download
curl -o ~/.zsh/tmux-zle-replay.zsh https://gist.githubusercontent.com/flavono123/99454d05648e3dadf92ea765d2a849a7/raw/tmux-zle-replay-for-claude-code-teammate.zsh

# Add to .zshrc
echo 'source ~/.zsh/tmux-zle-replay.zsh' >> ~/.zshrc

Tags: #claude-code #tmux #zsh #zle #ai-agent #anthropic #coding-agent #headless #teammate #agentic-coding #terminal #cli #race-condition #send-keys

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment