用 Claude Code 这类 AI 命令行工具写长一点的提示,想换行时按 Shift+Enter:在 Ghostty、iTerm2 里是换行;换一个终端,或者 SSH 到服务器上再用,同样的按键却把没写完的话直接发了出去。这不是工具的毛病,而是终端把按键告诉程序的方式不一样。这篇讲讲原因,以及背后的 kitty 键盘协议。
终端怎样把按键告诉程序
在终端里运行的程序看不到你的键盘。你每按一个键,终端把它翻译成一个或几个字符发给程序,程序收到字符,才知道你按了什么。反过来,程序也靠发特殊字符来指挥终端,比如改窗口标题、弹桌面通知,见《什么是 OSC》。
这套翻译规则是几十年前定下的,那时的终端还是接在主机上的电传打字机,键盘上的键远没有现在多。在这套规则里,回车就是一个固定的字符,没有办法表示「同时按着 Shift」。所以你按 Shift+Enter,终端发出去的和单按回车一模一样。程序收到的是同一个字符,只能当作回车处理,消息就发出去了。
这样「撞车」的按键不止一对:Tab 和 Ctrl+I 发出的是同一个字符,Esc 和 Ctrl+[ 也是。用 Vim 的人大概都遇到过:没法给 Tab 和 Ctrl+I 绑定不同的功能。
kitty 键盘协议:给按键一套新说法
kitty 是一款终端软件。它的作者 Kovid Goyal 设计了一套新的按键表示法,就是 kitty 键盘协议,也常被叫作「CSI u」。在这套说法里,每次按键都写明按的是哪个键、同时按着哪些修饰键(Shift、Ctrl、Alt 等),Shift+Enter 和回车就成了两样东西,程序一看就知道你按着 Shift。
它能被很快接受,靠的是一个稳妥的设计:默认不启用。程序启动时先问终端「你支持这个协议吗」,终端回答支持,程序再请它打开;程序退出时关掉,一切恢复原样。不认识这个协议的老程序从来不问,终端就照老规矩发,什么都不受影响。
除了 Shift+Enter,它还让程序分得清 Tab 和 Ctrl+I,能知道按键什么时候松开,单按 Esc 也不必再等一下才生效。虽然名字里带着 kitty,它已经被许多终端和程序采用:终端里有 Ghostty、WezTerm、foot、Alacritty、Windows Terminal 等,程序里有 Neovim、Helix、fish 和 Claude Code。
为什么有的终端行,有的不行
要让 Shift+Enter 换行,终端和程序两边都得配合:终端要能把「按着 Shift」告诉程序,程序要会问、会听。
| 终端 | 在 Claude Code 里按 Shift+Enter |
|---|---|
| Ghostty、kitty、iTerm2、WezTerm、Warp、macOS 自带的「终端」、Windows Terminal | 直接可用 |
| foot、Alacritty 0.16 及以后,以及其他支持 kitty 键盘协议的终端 | 直接可用,需要 Claude Code 2.1.269 及以后 |
| VS Code、Cursor、Zed,Alacritty 0.16 之前的版本 | 在 Claude Code 里运行一次 /terminal-setup |
| GNOME Terminal,PyCharm 等 JetBrains IDE 里的终端 | 不支持,用后面的替代办法 |
第一行是 Claude Code 认得的终端,开箱就能用。第二行的终端它未必认得,但从 2.1.269 起,它会直接问终端支不支持 kitty 键盘协议,支持就用上。第三行的终端不支持这个协议,/terminal-setup 会在终端的设置文件里加一条按键设置,让 Shift+Enter 发出一个和回车不同的字符串。最后一行既不支持协议,也没法这样设置。
这就是 kitty 键盘协议的意义:程序不必一个个去认识世上的终端,问一句就知道能不能区分按键。
通过 SSH 使用时
很多人是 SSH 到服务器上再运行 Claude Code。这时程序跑在服务器上,通常认不出你本地用的是哪个终端。kitty 键盘协议在这里正好派上用场:询问和回答都是普通的字符,SSH 原样转发,服务器上什么都不用设置。只要本地终端支持这个协议,Claude Code 又是 2.1.269 或更新的版本,Shift+Enter 在远程也能换行。
如果中间还隔着 tmux,情况又不一样:tmux 本身也是一个终端,程序问的是 tmux,而 tmux 目前(3.8)还不支持 kitty 键盘协议。办法是打开 tmux 自己的「扩展按键」,在 ~/.tmux.conf 里加上:
set -s extended-keys on
set -as terminal-features 'xterm*:extkeys'
然后运行 tmux source-file ~/.tmux.conf,或者重开 tmux。
Shift+Enter 用不了时
在 Claude Code 里,下面两种换行方式在任何终端里都能用,不需要设置:
- 按 Ctrl+J;
- 先输入
\,再按回车。
Option+Enter(Windows 和 Linux 上是 Alt+Enter)也能换行,但在 macOS 上,多数终端要先在设置里把 Option 键设为 Meta 键。其他工具的换行键各不相同,以它们自己的说明为准。
键盘突然失灵了
用这个协议的程序如果崩溃了,或者被强行关掉,可能来不及让终端恢复原样。回到命令行后会出现怪现象:Ctrl+C 不管用,屏幕上冒出 ;5u 这样的乱码。这时输入 reset 再按回车,一般就能恢复:协议有意让回车、退格这几个基本按键保持原样,所以这条命令总能输进去。还不行,就关掉这个标签页重开一个。
在 ShellRS 里
ShellRS 是一个免费、开源的 SSH 客户端,支持 kitty 键盘协议:
- Claude Code 等工具问起时,ShellRS 照实回答,并在它们运行时按协议发送按键,所以 Shift+Enter 在这些工具里是换行(Claude Code 需要 2.1.269 及以后)。Option+Enter(Windows 和 Linux 上是 Alt+Enter)照旧也能换行。
- 在远程服务器上运行也一样,服务器上不用做任何设置。
- 普通 shell 里没有程序请求这个协议,Shift+Enter 仍等于回车。想先写好多行命令再发,可以用撰写窗格(⌘⇧E,Windows 和 Linux 上是 Ctrl+Shift+E):里面 Shift+Enter 换行,回车发送。
详见 ShellRS 使用手册,ShellRS 可以在下载页获取。