modifyOtherKeys is only enabled for kitty-protocol/allowlisted terminals, so ctrl+enter can't be bound on xterm/VTE

Status Open
Reported on v2.1.281
Maintainer reply None cached
Activity 3 comments · opened Sep 23, 2026

What's wrong

Claude Code only enables xterm's modifyOtherKeys (CSI > 4 ; 2 m) as part of
its "extended keys" mode, and that mode is turned on only when either:

  1. the detected terminal is in a hardcoded allowlist

(iTerm.app, kitty, WezTerm, ghostty, tmux, windows-terminal,
WarpTerminal), or

  1. the terminal replies to the kitty keyboard protocol query CSI ? u.

There is no way for a terminal that implements modifyOtherKeys but not the
kitty keyboard protocol (xterm itself, or a VTE build with modifyOtherKeys
support) to get it enabled. As a result, Ctrl+Enter / Shift+Enter arrive as a
plain CR there, and a binding like

{ "context": "Chat", "bindings": { "ctrl+enter": "chat:newline" } }

never fires: Ctrl+Enter submits the message instead.

The parsing side already works. Launching with TERM_PROGRAM=WezTerm claude
(to get onto the allowlist) makes Claude Code send CSI > 4 ; 2 m, and
Ctrl+Enter then inserts a newline as configured. So only the enablement gating
is missing.

Environment

  • Claude Code 2.1.281, Linux (Fedora)
  • VTE-based terminal (VTE_VERSION=8401) patched to support xterm

modifyOtherKeys; no kitty keyboard protocol support

  • TERM=xterm-256color, TERM_PROGRAM unset (detected as vte-based)

Steps to reproduce

  1. Use a terminal that supports modifyOtherKeys but does not answer CSI ? u

(e.g. xterm).

  1. Add "ctrl+enter": "chat:newline" to the Chat context in

~/.claude/keybindings.json.

  1. Run claude, type some text, press Ctrl+Enter.

Expected: newline is inserted.
Actual: the message is submitted.

With TERM_PROGRAM=WezTerm claude the same steps give the expected result.

Suggested fix

Decouple modifyOtherKeys from the kitty keyboard protocol, for example:

  • probe modifyOtherKeys separately with XTQMODKEYS (CSI ? 4 m) and enable

CSI > 4 ; 2 m when the terminal answers, and/or

  • add an env var / setting to force it on (similar to

CLAUDE_CODE_FORCE_SYNC_OUTPUT), and/or

  • enable it for xterm (XTERM_VERSION set) by default.

Related: #83491 (same request for xterm, auto-closed as stale).

View original on GitHub ↗

3 Comments

kolyshkin · 13 days ago

This is not an enhancement, this is a bug. The terminal supports modifyOtherKeys but it is never queried. As a result I can't use Ctrl-Enter which is PITA.

The fix is trivial.

kolyshkin · 13 days ago

Some more details on where this happens (from the shipped 2.1.286 bundle, so
the identifiers below are minified and only meant as pointers):

  • The terminal-capability table has a single extendedKeys entry. Its initial

value comes from the allowlist
["iTerm.app","kitty","WezTerm","ghostty","tmux","windows-terminal","WarpTerminal"]
matched against the detected terminal name (which is TERM_PROGRAM when set,
otherwise e.g. vte-based / xterm); for anything else it is "likely false".

  • The startup probe then settles extendedKeys to true only if the terminal

answers the kitty keyboard query CSI ? u. There is no modifyOtherKeys probe.

  • When extendedKeys is true, the "keys on" sequence is the kitty push

(CSI > 1 u, or CSI > 5 u) followed by CSI > 4 ; 2 m; on exit it is
CSI > 4 m plus the kitty pop. So modifyOtherKeys is only ever enabled
together with the kitty protocol, and only for terminals that either are
allowlisted or speak kitty.

Possible fix:

  1. Split the capability into two: kitty keyboard and modifyOtherKeys, each

with its own on/off sequences.

  1. In the same startup probe that sends CSI ? u, also send XTQMODKEYS

(CSI ? 4 m). A terminal supporting modifyOtherKeys replies with
CSI > 4 ; Pv m; settle the modifyOtherKeys capability on that reply and
emit CSI > 4 ; 2 m (and CSI > 4 m on exit / suspend).

  1. Optionally, an env var to force it on/off, like

CLAUDE_CODE_FORCE_SYNC_OUTPUT for synchronized output.

The input side already decodes the modifyOtherKeys encoding: as noted in the
description, faking an allowlisted terminal makes Ctrl+Enter work.

Workaround until then: TERM_PROGRAM=WarpTerminal claude seems to be the
allowlisted value with the fewest side effects on a non-Warp terminal (it
still works with WezTerm, but that disables hyperlinks unless
TERM_PROGRAM_VERSION is also set, and turns off OSC 133).

kolyshkin · 3 days ago

@bcherny is there anything I can do to help move this forward?