Kitty keyboard protocol enabled for tmux breaks raw Ctrl-A/Ctrl-E (Cmd+Left/Right) when tmux extended-keys is on

Resolved 💬 2 comments Opened Jun 17, 2026 by amr Closed Jun 20, 2026

Summary

When tmux has extended-keys on, Claude Code enables the kitty keyboard protocol but the handshake doesn't complete properly through tmux. Raw C0 control bytes (0x01 Ctrl-A, 0x05 Ctrl-E) that iTerm2 sends for Cmd+Left/Right are then inserted literally into the prompt instead of acting as line-start/line-end.

Environment

  • macOS (darwin arm64)
  • iTerm2 3.6.11 with tmux integration (-CC mode)
  • tmux with set -s extended-keys on + set -as terminal-features 'xterm*:extkeys:tmux*:extkeys'
  • Claude Code v2.1.179
  • iTerm2 "Natural Text Editing" key mappings: Cmd+Left → 0x01, Cmd+Right → 0x05

Root Cause (identified via binary analysis)

The terminal capability list c_3 includes "tmux":

c_3 = ["iTerm.app", "kitty", "WezTerm", "ghostty", "tmux", "windows-terminal", "WarpTerminal"];

When Claude starts, it sends ESC[>0q (XTVERSION query). tmux responds with its name "tmux", which matches c_3. Claude then enables the kitty keyboard protocol by sending ESC[<u ESC[>1u ESC[>4;2m.

However, tmux's extended-keys is not a full kitty protocol implementation — it's tmux's own modifier-key feature. The kitty protocol handshake is not relayed to the outer terminal (iTerm2). Claude ends up in kitty mode expecting ESC[97;5u for Ctrl-A, but iTerm2 keeps sending raw 0x01. Claude doesn't recognize the raw byte in kitty mode and inserts it literally.

The relevant code path:

function d_3(H) { return c_3.includes(H ?? dH.terminal ?? "") }
function qe()   { return d_3() ? x9H + rY7 + oY7 : "" }
// qe() is called during screen init via Z2H()

dH.terminal is set to "tmux" from the XTVERSION response, d_3() returns true, and qe() sends the kitty enable sequence unconditionally.

Proposed Fix

Skip kitty protocol enablement when running inside tmux (i.e. when process.env.TMUX is set), since tmux's extended-keys is not equivalent to full kitty passthrough:

function qe() { return d_3() && !process.env.TMUX ? x9H + rY7 + oY7 : "" }

Or more precisely, remove "tmux" from c_3 — tmux does not implement the kitty keyboard protocol, it has its own separate extended-keys feature that happens to respond to the same XTVERSION query.

Steps to Reproduce

  1. macOS + iTerm2, open a tmux session (native or -CC integration)
  2. Add to ~/.tmux.conf: set -s extended-keys on + set -as terminal-features 'xterm*:extkeys:tmux*:extkeys'
  3. In iTerm2 key mappings, set Cmd+Left → Send Hex 0x01, Cmd+Right → Send Hex 0x05
  4. Launch Claude Code inside tmux
  5. Type something in the prompt, press Cmd+Left → ^A is inserted literally instead of moving to line start

Expected: cursor moves to start/end of line
Actual: ^A / ^E inserted as literal characters

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗