Bug Description
Pressing Enter in the CLI input sometimes inserts a newline instead of submitting the message. The cursor moves down but nothing is sent. The only way to recover is Ctrl+D (which exits the app) or Ctrl+C.
This was observed on Ghostty (Arch Linux, TERM=xterm-ghostty) but affects any terminal that sends \n (0x0a) instead of \r (0x0d) for the Enter key.
Root Cause
The custom c-j key binding in cli.py (line ~8424) overrides prompt_toolkit's built-in safety net for terminals that send \n for Enter.
How prompt_toolkit handles this natively
In prompt_toolkit/key_binding/bindings/basic.py (lines 195-202):
@handle("c-j")
def _newline2(event):
"""
By default, handle \n as if it were a \r (enter).
(It appears that some terminals send \n instead of \r when pressing
enter. - at least the Linux subsystem for Windows.)
"""
event.key_processor.feed(KeyPress(Keys.ControlM, "\r"), first=True)
This built-in handler re-feeds \n as \r so both key codes trigger the Enter action. It exists specifically because terminal behavior varies.
What Hermes does
@kb.add('c-j')
def handle_ctrl_enter(event):
"""Ctrl+Enter (c-j) inserts a newline."""
event.current_buffer.insert_text('\n')
This binding shadows the built-in safety net. Now when any terminal sends \n for Enter, it inserts a newline instead of submitting.
Key mappings at the terminal level
| Byte received |
prompt_toolkit Key |
Built-in behavior |
Hermes override |
\r (0x0d) |
Keys.ControlM / Keys.Enter |
Submit or newline (multiline) |
✅ Submit (works) |
\n (0x0a) |
Keys.ControlJ |
Re-feed as \r → Submit |
❌ Insert newline (broken) |
Affected Terminals
- Ghostty — known to send
\n in certain states (see ghostty-org/ghostty#3320, #11563)
- WSL terminals — prompt_toolkit's own comment documents this
- Any terminal where
stty icrnl gets disrupted or the terminal sends line feed for Enter
Reproduction
- Use a terminal that sends
\n for Enter (Ghostty, or simulate with stty -icrnl before launching)
- Launch
hermes
- Type any text and press Enter
- Expected: message submits
- Actual: cursor moves down, newline inserted in input area
Suggested Fix
Make the c-j handler behave like the enter handler (submit), and use only escape enter (Alt+Enter) for newline insertion. The c-j binding was intended for "Ctrl+Enter" but most terminals send c-j for Ctrl+J, not Ctrl+Enter — and some send it for plain Enter too.
# Option A: Remove the c-j binding entirely — let prompt_toolkit's
# built-in remap c-j -> c-m handle it
# (Just delete the @kb.add('c-j') handler)
# Option B: Make c-j also submit (same as enter)
@kb.add('c-j')
def handle_ctrl_j(event):
"""Ctrl+J / \\n — treat as Enter (submit). Some terminals send \\n for Enter."""
handle_enter(event)
Alt+Enter (escape enter) already works for multiline input, so removing the c-j newline binding loses nothing.
Environment
- Hermes Agent: latest (git)
- Terminal: Ghostty (xterm-ghostty)
- OS: Arch Linux
- prompt_toolkit: 3.0.52
- Python: 3.14
Bug Description
Pressing Enter in the CLI input sometimes inserts a newline instead of submitting the message. The cursor moves down but nothing is sent. The only way to recover is Ctrl+D (which exits the app) or Ctrl+C.
This was observed on Ghostty (Arch Linux,
TERM=xterm-ghostty) but affects any terminal that sends\n(0x0a) instead of\r(0x0d) for the Enter key.Root Cause
The custom
c-jkey binding incli.py(line ~8424) overrides prompt_toolkit's built-in safety net for terminals that send\nfor Enter.How prompt_toolkit handles this natively
In
prompt_toolkit/key_binding/bindings/basic.py(lines 195-202):This built-in handler re-feeds
\nas\rso both key codes trigger the Enter action. It exists specifically because terminal behavior varies.What Hermes does
This binding shadows the built-in safety net. Now when any terminal sends
\nfor Enter, it inserts a newline instead of submitting.Key mappings at the terminal level
\r(0x0d)Keys.ControlM/Keys.Enter\n(0x0a)Keys.ControlJ\r→ SubmitAffected Terminals
\nin certain states (see ghostty-org/ghostty#3320, #11563)stty icrnlgets disrupted or the terminal sends line feed for EnterReproduction
\nfor Enter (Ghostty, or simulate withstty -icrnlbefore launching)hermesSuggested Fix
Make the
c-jhandler behave like theenterhandler (submit), and use onlyescape enter(Alt+Enter) for newline insertion. Thec-jbinding was intended for "Ctrl+Enter" but most terminals sendc-jfor Ctrl+J, not Ctrl+Enter — and some send it for plain Enter too.Alt+Enter (
escape enter) already works for multiline input, so removing thec-jnewline binding loses nothing.Environment