[BUG] Kitty keyboard protocol gated on terminal-name allow-list instead of CSI ? u capability — capable terminals (Alacritty) denied
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Environment
- Claude Code: 2.1.193 (single-file compiled binary, embedded Bun 1.4.0)
- Terminal: Alacritty 0.17.0
- OS: Linux x86_64
- Shell: fish
Summary
Claude Code decides whether to enable the kitty keyboard protocol by identifying the terminal by name against an allow-list, rather than by the terminal's capability reply to CSI ? u. Terminals that fully implement the kitty keyboard protocol but are not on the allow-list — and cannot be name-identified — are silently left in legacy input mode. Alacritty is the concrete case: Shift+Enter and Ctrl+Backspace do not work, even though Alacritty correctly speaks the protocol.
Mechanism (as documented in the now-mis-closed #27868, observed in v2.1.50, still current in 2.1.193):
The kitty keyboard push (CSI > 1 u) is gated on a terminal-name allow-list:
const kittyTerminals = ["iTerm.app", "kitty", "WezTerm", "ghostty"];
if (kittyTerminals.includes(detectedTerminal)) {
stdout.write(kittyPushSequence); // CSI > 1 u
}
detectedTerminal comes from name/identity signals (TERM_PROGRAM, XTVERSION, KITTY_WINDOW_ID, $TERM). It is not derived from the CSI ? u capability response. So a terminal that proves protocol support via CSI ? u but whose name is not on the list never receives the push.
Why Alacritty specifically can never pass identity detection
Probing Alacritty 0.17.0 directly:
# XTVERSION (CSI > q) — terminal name/version
sent: \x1b[>q
reply: \x1b[?6c # only the primary-DA fence — NO DCS >| name reply
# kitty keyboard flags (CSI ? u) — capability
sent: \x1b[?u
reply: \x1b[?0u\x1b[?6c # CSI ? 0 u => protocol SUPPORTED (flags currently 0)
- Alacritty answers
CSI ? u→ the protocol is fully supported. - Alacritty does not answer XTVERSION, does not set
TERM_PROGRAM, does not setKITTY_WINDOW_ID, and reportsTERM=alacritty. So none of the identity channels can mark it kitty-capable. - Alacritty not answering XTVERSION is deliberate and permanent — the maintainer rejected it as
wontfix(alacritty/alacritty#5273), even after a working PR, on the grounds that terminal-name/version detection is the wrong model for feature detection. So identity-based detection can never work for Alacritty by design; only capability-based detection can.
Proof that the only thing missing is identification, not capability
Launching with a faked kitty marker makes Claude send the push, and Alacritty honors it — Shift+Enter and Ctrl+Backspace immediately work:
env KITTY_WINDOW_ID=1 claude # both keys now native in Alacritty
This confirms Alacritty fully honors the protocol flags once pushed; the push is being withheld purely on identity grounds.
What Should Happen?
When the terminal responds to CSI ? u with a valid CSI ? <flags> u reply, Claude Code should treat it as kitty-keyboard-capable and send the push sequence regardless of terminal name — i.e. detect the capability, not the identity. This matches the expected behavior already requested in #62423 ("When the terminal responds positively to the kitty keyboard protocol query (CSI ? u), Claude Code should use it"), extended to the case where the terminal is not on the name allow-list at all.
Capability-first detection also aligns with where the terminal ecosystem is deliberately heading (Alacritty's wontfix on XTVERSION is an explicit rejection of identity-based feature detection), and would fix not just Alacritty but any current/future spec-compliant terminal that isn't hard-coded into the list.
Suggested change (mirrors #27868's proposal, generalized): gate the push on the CSI ? u response, with the name allow-list and KITTY_WINDOW_ID kept only as fallbacks for terminals that don't answer the query.
Steps to Reproduce
- Open Alacritty (0.13+; tested 0.17.0) — confirm
printf '\e[?u\e[c'returns a\e[?...ureply (protocol supported). - Run
claudenormally (no env tweaks). - Press Shift+Enter → submits instead of inserting a newline. Press Ctrl+Backspace → deletes one character instead of a word.
- Now run
env KITTY_WINDOW_ID=1 claudein the same Alacritty → both keys work natively.
Additional Context
- #27868 reported this exact identity-vs-capability gating and was auto-closed as a duplicate of #18135 — but #18135 is an unrelated JetBrains/JediTerm rendering bug closed as not-planned. The precise defect is therefore currently untracked.
- Related: #62423 (legacy XTMODKEYS sent even when kitty protocol is advertised), #16066 (
/terminal-setupwrites the legacy Shift+Return binding for Alacritty), alacritty/alacritty#5273 (XTVERSIONwontfix).
Is this a regression?
No — long-standing behavior of the identity-based detection path.
Showing cached comments. Read the full discussion on GitHub ↗
5 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Not a duplicate to consolidate into. #27868 is the same root cause — that's intentional — but #27868 was itself auto-closed as a duplicate of #18135, which is an unrelated JetBrains/JediTerm rendering bug closed as
not planned. So #27868 is dead and tracks nothing.Closing #71700 as a duplicate of #27868 would re-bury a real, reproducible defect (capable terminals denied the kitty keyboard protocol because negotiation gates on terminal identity instead of the
CSI ? ucapability reply) with zero open tracking. Requesting it stay open. Happy to consolidate if #27868 is reopened instead — but it shouldn't be closed into a chain that terminates at a not-planned, unrelated issue.Another spec-compliant data point, with an angle this issue doesn't yet cover: over SSH there is no workaround at all.
AbsoluteTelnet/SSH implements the kitty keyboard protocol and answers CSI ? u with CSI ? <flags> u, but like Alacritty it isn't on the TERM_PROGRAM allow-list, so CSI > 1 u is never sent and Shift+Enter can't work.
The env KITTY_WINDOW_ID=1 claude workaround that rescues Alacritty locally is impossible for remote sessions. Claude reads that variable on the remote host, and SSH doesn't carry arbitrary env vars unless the server's AcceptEnv permits it (default is LANG LC_*). The one thing that always propagates — TERM — isn't checked. So for anyone running Claude over SSH in a capable-but-unlisted terminal, there is no client-side or user-side fix. Capability detection isn't just cleaner here; it's the only thing that can work.
Another data point: iTerm2 is on the allow-list and still gets denied — over SSH.
Claude Code 2.1.220 both ends, iTerm2 3.6.11 → SSH → macOS remote.
Detection reads
TERM_PROGRAM, whichsshdoes not forward:LC_TERMINALis never consulted, so over SSH the terminal is unidentifiable and Shift+Enter stops working. It is fully capable over that same connection:Running iTerm2 directly on the remote host — same machine, same binary, same version — works, because
TERM_PROGRAMis set there. One variable: whether the terminal's name survived the hop.@btpence — there is an SSH workaround for iTerm2, since it also exports
LC_TERMINALandsshforwardsLC_*. On the remote, in~/.zshrc:New session required. Alacritty has no equivalent marker, which is the point of this issue.
I appreciate the fact that there are workarounds, as there often are.
But what's wrong with detection via capability probe?