You’re writing a longer prompt in an AI command-line tool such as Claude Code and press Shift+Enter for a new line. In Ghostty or iTerm2 you get one. In another terminal, or after you SSH into a server, the same keys send your half-written message instead. The tool isn’t at fault: terminals differ in how they tell programs which keys you pressed. This article explains why, and what the kitty keyboard protocol has to do with it.
How a terminal tells a program about keys
A program running in a terminal can’t see your keyboard. Each time you press a key, the terminal turns it into one or more characters and sends them to the program; that’s all the program gets. It works the other way round too: programs send special characters to make the terminal change the window title or show a desktop notification, as described in What is OSC.
The rules for turning keys into characters were set decades ago, when terminals were teletypes wired to a mainframe and keyboards had far fewer keys. Under those rules, Enter is one fixed character, and there’s no way to say “Shift was held down”. So Shift+Enter sends exactly what Enter sends. The program receives the same character, has to treat it as Enter, and off goes your message.
Shift+Enter isn’t the only pair that collides. Tab and Ctrl+I send the same character, and so do Esc and Ctrl+[. Vim users know the result: you can’t give Tab and Ctrl+I different jobs.
The kitty keyboard protocol: a new way to describe keys
kitty is a terminal app. Its author, Kovid Goyal, designed a new way of describing key presses: the kitty keyboard protocol, also known as “CSI u”. Each key press now says which key it was and which modifiers (Shift, Ctrl, Alt and so on) were held. Shift+Enter and Enter become two different things, and the program can see that Shift was down.
It caught on quickly thanks to a careful design: it’s off by default. When a program starts, it asks the terminal “do you support this protocol?”; if the terminal says yes, the program asks it to switch on, and switches it off again when it exits. Older programs that don’t know the protocol never ask, so the terminal keeps using the old rules and nothing breaks.
Besides Shift+Enter, it lets programs tell Tab from Ctrl+I, know when a key is released, and react to a lone Esc without a short wait. Despite the name, it’s no longer just kitty’s: terminals such as Ghostty, WezTerm, foot, Alacritty and Windows Terminal support it, and programs such as Neovim, Helix, fish and Claude Code use it.
Why it works in some terminals and not others
For Shift+Enter to add a line, both sides have to play along: the terminal must be able to tell the program that Shift was held, and the program must ask and listen.
| Terminal | Shift+Enter in Claude Code |
|---|---|
| Ghostty, kitty, iTerm2, WezTerm, Warp, macOS Terminal, Windows Terminal | Works out of the box |
| foot, Alacritty 0.16 and later, and other terminals that support the kitty keyboard protocol | Works out of the box with Claude Code 2.1.269 or later |
| VS Code, Cursor, Zed, Alacritty before 0.16 | Run /terminal-setup once in Claude Code |
| GNOME Terminal, the terminal in JetBrains IDEs such as PyCharm | Not available; use one of the alternatives below |
The first row is terminals Claude Code recognizes, so it just works. It may not recognize those in the second row, but since version 2.1.269 it simply asks the terminal whether it supports the kitty keyboard protocol and uses it if so. The terminals in the third row don’t support the protocol; /terminal-setup adds a key binding to the terminal’s settings file so that Shift+Enter sends something different from Enter. The last row neither supports the protocol nor can be set up that way.
That’s the point of the kitty keyboard protocol: a program doesn’t have to know every terminal in the world. It asks once and knows whether keys can be told apart.
Over SSH
Many people SSH into a server and run Claude Code there. The program is then running on the server and usually can’t tell which terminal you have locally. This is where the kitty keyboard protocol helps: the question and the answer are ordinary characters, SSH carries them back and forth unchanged, and nothing needs setting up on the server. As long as your local terminal supports the protocol and Claude Code is 2.1.269 or later, Shift+Enter adds a line on the server too.
tmux in between changes things. tmux is a terminal in its own right, so the program is asking tmux, and tmux (as of 3.8) doesn’t support the kitty keyboard protocol yet. The way around it is to turn on tmux’s own “extended keys” by adding these lines to ~/.tmux.conf:
set -s extended-keys on
set -as terminal-features 'xterm*:extkeys'
Then run tmux source-file ~/.tmux.conf, or restart tmux.
When Shift+Enter doesn’t work
In Claude Code, these two ways of adding a line work in every terminal with no setup:
- press Ctrl+J;
- type
\and then press Enter.
Option+Enter (Alt+Enter on Windows and Linux) adds a line as well, but on macOS most terminals first need Option set to act as Meta in their settings. Other tools use their own keys for a new line; check their documentation.
When the keyboard suddenly acts up
If a program using the protocol crashes or is killed, it may not get the chance to switch the terminal back. Back at the shell, odd things happen: Ctrl+C does nothing, and bits like ;5u appear on screen. Type reset and press Enter, and things are usually back to normal. The protocol deliberately leaves Enter and Backspace as they were, so you can always type that command. If that doesn’t help, close the tab and open a new one.
In ShellRS
ShellRS is a free, open-source SSH client, and it supports the kitty keyboard protocol:
- When Claude Code or a similar tool asks, ShellRS says yes and sends keys the protocol’s way while the tool runs, so Shift+Enter adds a line in it (Claude Code 2.1.269 or later). Option+Enter (Alt+Enter on Windows and Linux) still works too.
- The same goes for tools running on a remote server, with nothing to set up on the server.
- In a plain shell nothing asks for the protocol, so Shift+Enter is still Enter. To write several lines before sending them, use the compose pane (⌘⇧E, or Ctrl+Shift+E on Windows and Linux): there Shift+Enter adds a line and Enter sends.
See the ShellRS manual, or get ShellRS from the download page.