[Bug] Ghostty terminal-setup uses naive string search for keybind detection
Bug Description
# Bug Report: Ghostty terminal-setup uses naive string search and doesn't support modern key codes
## Problem 1: Naive String Search for Existing Keybinds
### Description
The /terminal-setup command performs a naive Y.includes("shift+enter") check that:
- Matches comments and documentation, not just actual keybindings
- Fails to detect keybindings Claude Code itself previously added
- Creates false positives when users have default Ghostty documentation
### Evidence
From the Claude binary:
```javascript
if(Y.includes("shift+enter"))return 'Found existing Ghostty Shift+Enter key binding. Remove it to continue.'
How This Failed
- Claude Code successfully added keybind = shift+enter=text:\x1b\r via /terminal-setup
- User later added/moved documentation comments containing "shift + enter"
- Re-running /terminal-setup falsely detected the comment (not the actual keybind)
- User was blocked from proceeding despite having the correct keybind already
Impact
- False positives from documentation/comments
- Cannot detect legitimately conflicting keybinds vs comments
- Catch-22: Can't add keybind because any mention of the string triggers the check
- Breaks unexpectedly when users edit their config files
Workaround
Remove or reword any comments containing "shift+enter" (e.g., change to "shift+return"). However, this is problematic if you legitimately need multiple shift+enter bindings or want to
document what you changed and why.
Suggested Fix for Detection
Parse the config properly and only detect active (uncommented) keybinds:
const lines = Y.split('\n');
const hasActiveBinding = lines.some(line => {
const trimmed = line.trim();
// Skip comments and empty lines
if (!trimmed || trimmed.startsWith('#')) return false;
// Match actual keybind directives
return /keybind\s=\s.*shift\+enter/i.test(trimmed);
});
if (hasActiveBinding) {
// Optionally: warn but allow user to proceed with --force flag
console.warn('Detected existing shift+enter binding. This may conflict.');
if (!flags.force) {
console.log('Re-run with --force to override.');
return;
}
}
Problem 2: Ghostty Uses Modern Terminal Standards
Root Cause
Ghostty sends [27;2;13~ (modifyOtherKeys standard) for Shift+Enter by default, but Claude Code only recognizes the older \x1b\r (ESC + CR) sequence.
Current Solution (Suboptimal)
Requires manually configuring Ghostty to send the legacy sequence via:
keybind = shift+enter=text:\x1b\r
Better Solution: Support Modern Key Codes
Claude Code should recognize both:
- Legacy: \x1b\r (ESC + CR) - for older terminals
- Modern: [27;2;13~ - for terminals following modifyOtherKeys standard
This would make Shift+Enter work out of the box in Ghostty and other modern terminals without requiring any configuration.
Implementation Suggestion
Update the input parser to recognize both sequences:
// Legacy sequence
if (input === '\x1b\r') {
insertNewline();
}
// Modern modifyOtherKeys sequence for Shift+Enter
if (input === '\x1b[27;2;13~') {
insertNewline();
}
Benefits
- Works out of the box in modern terminals (Ghostty, WezTerm, etc.)
- No configuration required
- Backwards compatible with terminals sending legacy sequences
- Aligns with modern terminal standards
Related Issues
- #5757 - Reports the [27;2;13~ sequence not being recognized
- #1282 - Feature request for Ghostty support
- #6344 - Crash during terminal-setup
Reproduction
- Run /terminal-setup successfully (Claude adds the keybind)
- Add a comment like # Note: changed shift + enter to shift + return
- Re-run /terminal-setup
- Get false positive: "Found existing Ghostty Shift+Enter key binding"
Priority
Medium-High: Affects all Ghostty users and creates confusing false positives
You can copy this and use the built-in bug reporter to submit it!
Environment Info
- Platform: darwin
- Terminal: ghostty
- Version: 2.0.27
- Feedback ID: 93791566-b3ee-4234-9474-aa0ea7c1c3a3
Errors
[{"error":"Error: Plugin path not found: /Users/dmmalam/.claude/plugins/marketplaces/plugins/pr-review-toolkit\n at hTI (/$bunfs/root/claude:700:4853)\n at hTI (/$bunfs/root/claude:700:10866)\n at bTI (/$bunfs/root/claude:700:4397)\n at async <anonymous> (/$bunfs/root/claude:700:11813)\n at async CO (/$bunfs/root/claude:724:25941)\n at async <anonymous> (/$bunfs/root/claude:3755:312)\n at async parseAsync (/$bunfs/root/claude:26:4180)\n at async r85 (/$bunfs/root/claude:3800:1619)\n at async l85 (/$bunfs/root/claude:3745:1545)\n at async s85 (/$bunfs/root/claude:3800:9962)","timestamp":"2025-10-27T19:47:
Note: Error logs were truncated.
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗