Terminal configuration
Fix Shift+Enter, Option-key shortcuts, notifications, tmux, themes, pasting and Vim mode so your terminal and Claude Code cooperate.
Claude Code runs happily in almost any terminal with no setup at all. This page is a fault-finding guide for the times it does not: a key that submits when you wanted a new line, a shortcut that does nothing on a Mac, a long task that finishes silently while you are in another window. Find the symptom, apply the fix, move on.
One distinction worth holding on to: this page is about making your terminal send the right signals. If you want to change which key does what inside Claude Code, that lives in keybindings.
| Symptom | Jump to |
|---|---|
| Shift+Enter sends the message instead of adding a line | Multiline prompts |
| Option+P, Option+Enter and friends do nothing on macOS | Option as Meta on macOS |
| No alert when Claude finishes or needs permission | Bells and notifications |
| You live inside tmux | tmux settings |
| Backspace eats a whole word on Windows | Backspace on Windows |
| Screen flickers or scrollback jumps | Flicker and the fullscreen renderer |
| Colours clash with your terminal | Themes |
| You want Vim motions in the prompt | Vim editor mode |
Multiline prompts
Enter submits. Two ways to get a line break work everywhere with zero configuration:
- Ctrl+J
- Type a backslash
\and then press Enter
Shift+Enter is the nicer habit, but whether it works depends on the terminal emulator:
| Terminal | Shift+Enter |
|---|---|
| Ghostty, Kitty, iTerm2, WezTerm, Warp, Apple Terminal, Windows Terminal | Works out of the box |
| Anything else speaking the kitty keyboard protocol (foot, Alacritty 0.16+) | Works out of the box on Claude Code v2.1.269 or later |
| VS Code, Cursor, Devin Desktop, Zed, Alacritty older than 0.16 | Run /terminal-setup once |
| gnome-terminal, JetBrains IDE terminals (PyCharm, Android Studio and so on) | Not possible; stick to Ctrl+J or \ + Enter |
What /terminal-setup changes
Run it in the host terminal itself, not inside tmux or screen, because it edits the host terminal's own config. It adds a Shift+Enter binding and leaves any existing binding alone; if one is already there you get an "already configured" message and nothing changes.
In VS Code, Cursor and Devin Desktop it also touches two editor settings:
terminal.integrated.gpuAccelerationis set to"off"to stop garbled glyphs. Set it back to"auto"and reload the window if you want GPU rendering again.terminal.integrated.mouseWheelScrollSensitivityis tuned so scrolling feels right in fullscreen mode.
In Zed it merges the binding into keymap.json. If that file already has bindings, it first saves a backup alongside it (named like keymap.json.<hash>.bak) and keeps your comments and other bindings. If it cannot parse, back up or verify the file, it leaves it untouched and prints the snippet for you to paste in by hand.
Inside tmux, Shift+Enter needs the tmux settings below even when the outer terminal supports it.
If you would rather flip the behaviour so Enter adds a line and Shift+Enter sends, remap the chat:submit and chat:newline actions in your keybindings file.
Option as Meta on macOS
Several shortcuts use Option, for example Option+Enter for a newline and Option+P to open the model picker. Most Mac terminals swallow Option for typing accented characters, so the keystroke never reaches Claude Code. The fix is a setting usually called "Use Option as Meta key" (Meta being the old Unix name for Alt/Option).
- Apple Terminal: Settings → Profiles → Keyboard → tick "Use Option as Meta Key". If you accepted the first-run terminal setup prompt this is already done; that prompt runs
/terminal-setup, which also turns off the audible bell (except in screen reader mode, where the bell is left on from v2.1.211). If an older run silenced it, re-enable it under Profiles → Advanced → "Audible bell". - iTerm2: Settings → Profiles → Keys → General, set both Left and Right Option key to "Esc+". Running
/terminal-setupin iTerm2 also ticks "Applications in terminal may access clipboard" (Settings → General → Selection) so/copycan reach the system clipboard. It detects iTerm2 even from inside tmux. Restart iTerm2 afterwards. - VS Code: add
"terminal.integrated.macOptionIsMeta": trueto your settings. - Ghostty, Kitty and others: look for an option-as-alt setting in the terminal's config file.
Bells and notifications
When Claude finishes or stops for a permission prompt and you seem to be elsewhere, Claude Code raises a notification event (the timing rules are in the hooks reference). Turning that into something you can hear or see is what lets you walk away from long tasks.
Out of the box, only Ghostty, Kitty and iTerm2 get a desktop notification. Everywhere else, pick one of two routes.
Ring the terminal bell by setting preferredNotifChannel:
{
"preferredNotifChannel": "terminal_bell"
}
Run your own command with a Notification hook. Hooks fire alongside the built-in notification, so this is the answer for Warp and the VS Code integrated terminal too. On a Mac I use a short sound so I know to look up:
{
"hooks": {
"Notification": [
{
"hooks": [
{ "type": "command", "command": "afplay /System/Library/Sounds/Ping.aiff" }
]
}
]
}
}
On Linux swap in something like notify-send "Claude Code" "Waiting for you". The hooks guide has more recipes.
Desktop notifications travel back over SSH, so a remote session can still tap you on the shoulder. Ghostty and Kitty pass them straight to the OS. iTerm2 needs two switches: Settings → Profiles → Terminal, tick "Notification Center Alerts", then under "Filter Alerts" enable "Send escape sequence-generated alerts".
Still silent? Check the terminal app has notification permission in your OS settings, and if you are in tmux, enable passthrough as below.
tmux settings
Under default tmux, Shift+Enter submits, and notifications plus the terminal progress bar never escape to the outer terminal. Add these three lines to ~/.tmux.conf:
set -g allow-passthrough on
set -s extended-keys on
set -as terminal-features 'xterm*:extkeys'
Reload with tmux source-file ~/.tmux.conf. Passthrough lets escape sequences for notifications and progress through; the extended-keys pair lets tmux tell Shift+Enter apart from Enter. For mouse scrolling in fullscreen mode you will also want set -g mouse on, covered on the fullscreen page.
Backspace on Windows
On Windows, a Backspace that arrives as ^H is treated as Ctrl+Backspace (delete previous word), unless TERM_PROGRAM is mintty or TERM is cygwin. On macOS and Linux ^H is treated as plain Backspace.
- If every Backspace deletes a word, set
CLAUDE_CODE_BS_AS_CTRL_BACKSPACE=0. - If on macOS or Linux Ctrl+Backspace only removes one character because your terminal sends
^Hfor it, set the same variable to1.
See environment variables for how to set it persistently.
Flicker and the fullscreen renderer
If the screen flashes or the scroll position jumps while Claude streams output, switch renderer with /tui fullscreen. The conversation relaunches intact and the choice is saved. You can also launch with CLAUDE_CODE_NO_FLICKER=1, or put that variable in the env block of your settings. Scrolling then happens inside Claude Code (mouse wheel, PgUp/PgDn) rather than in the terminal's scrollback; the fullscreen page explains search and copy.
If flicker is the only complaint and your terminal supports synchronised output but is not detected (Emacs eat is the usual example), set CLAUDE_CODE_FORCE_SYNC_OUTPUT=1 and keep the classic renderer.
In screen reader mode none of this applies: Claude Code always renders plain scrolling text, apart from attached background sessions, and /tui fullscreen just explains why it will not switch.
Wide terminals and long prose
On an ultrawide monitor, prose lines stretch edge to edge and become hard to read. Set maxProseWidth in your settings to wrap Claude's prose at a fixed column count.
Themes
/theme (or the theme picker in /config) chooses Claude Code's palette. The auto option reads your terminal's background so the theme flips with your OS appearance, as long as the terminal does. Claude Code cannot change the terminal's own colours.
Custom themes
At the bottom of the /theme list, New custom theme… walks you through naming a theme and overriding individual colour tokens with a live preview. Highlight an existing custom theme and press Ctrl+E to edit it. Themes shipped by installed plugins appear in the same list.
Under the hood each custom theme is a JSON file in ~/.claude/themes/. The filename minus .json is its slug, and choosing it saves custom:<slug> as your theme. All three fields are optional:
| Field | Meaning | Default |
|---|---|---|
name | Label shown in /theme | The slug |
base | Preset to inherit from: dark, light, dark-daltonized, light-daltonized, dark-ansi, light-ansi | dark |
overrides | Token name to colour value | Nothing overridden |
Colours can be #rrggbb, #rgb, rgb(r,g,b), ansi256(n) or ansi:<name> using one of the 16 ANSI names such as ansi:blue or ansi:magentaBright. Unknown tokens and malformed values are silently dropped, so a typo will not break the UI.
Here is a light theme I use for screen-sharing on a projector, where pale diff colours vanish:
{
"name": "Projector",
"base": "light",
"overrides": {
"claude": "#7a1f3d",
"promptBorder": "#333333",
"diffAdded": "#b7f0c0",
"diffRemoved": "#f6b8b8",
"warning": "#a15c00"
}
}
Claude Code watches the folder and reloads on change, so edits apply mid-session. The one exception: if ~/.claude/themes/ did not exist at startup, restart once after creating it.
Colour tokens
| Group | Tokens |
|---|---|
| Text and accents | claude (spinner, assistant label), text, inverseText (text on coloured badges), inactive (hints, timestamps), subtle (faint borders), suggestion (autocomplete and picker highlight), permission (dialog borders), remember (memory and CLAUDE.md markers) |
| Status | success, error, warning (also the auto mode indicator), merged (merged PR status) |
| Input box and modes | promptBorder, planMode, autoAccept (accept-edits accent), bashBorder (border while typing a ! command), ide, fastMode, effortUltra (the ultracode tag; overrides honoured from v2.1.239) |
| Diffs | diffAdded, diffRemoved, diffAddedDimmed, diffRemovedDimmed (shown after you reject an edit), diffAddedWord, diffRemovedWord (word-level highlights) |
| Message backgrounds | userMessageBackground, bashMessageBackgroundColor, memoryBackgroundColor (both renderers); userMessageBackgroundHover, selectionBg (fullscreen only) |
| Usage and labels | rate_limit_fill, rate_limit_empty (the /usage meter), briefLabelYou, briefLabelClaude |
Spinner gradients use paired shimmer tokens: claudeShimmer, warningShimmer, permissionShimmer, promptBorderShimmer, inactiveShimmer and fastModeShimmer. Override the shimmer with its base colour if the animation looks off.
Subagents are drawn in eight named colours through tokens of the form <colour>_FOR_SUBAGENTS_ONLY, where the colour is red, blue, green, yellow, purple, orange, pink or cyan. A subagent declared with color: green uses green_FOR_SUBAGENTS_ONLY. The rainbow on the ultrathink keyword uses rainbow_<colour> and rainbow_<colour>_shimmer for red, orange, yellow, green, blue, indigo and violet.
For what sits under the input box, build a status line.
Pasting large content
Paste more than 800 characters or more than three lines and the input collapses to a placeholder like [Pasted text #2 +64 lines]. The full text is still sent on submit. For really big blobs (whole log files, dumps) save to a file and ask Claude to read it: the transcript stays tidy and Claude can refer back by path. The VS Code integrated terminal can drop characters on huge pastes, which is another reason to use a file there.
A few details worth knowing:
- Invisible characters. Hidden Unicode in a paste is stripped when you press Enter, and the cleaned prompt is put back for you to confirm with a second Enter.
- Pastes are labelled. Claude is told that placeholder content came from elsewhere and may contain instructions you did not write, and to act on those only where your own typed message asks it to. Sessions that do not fetch feature flags skip this labelling.
- Deleting a placeholder. If a word or line delete (
Ctrl+W,Ctrl+K) or a Vimf/tdelete such asdt]reaches into a placeholder, the whole placeholder goes. Bring it back withCtrl+Y(after a word or line delete) orpin NORMAL mode (after a Vim delete). - History recall. Paste contents are stored under
~/.claude/paste-cache/, so a prompt recalled from history resends the full paste, even in a later session. Those files are swept aftercleanupPeriodDays. If one has gone, Claude Code never sends the literal placeholder text: in a plain prompt with other text, it drops the placeholder and sends the rest; in a!shell command, a slash command, or a prompt that would end up empty, it cancels the send and leaves the input for you to fix.
Vim editor mode
Turn on Vim-style editing via /config → Editor mode, or set "editorMode": "vim" in ~/.claude/settings.json. Set it back to normal to switch off.
You get a useful subset of NORMAL and VISUAL mode: hjkl, v and V selection, and d, c, y with text objects. The full key table is in interactive mode. Two differences from real Vim catch people out:
- Enter in INSERT mode still submits. Use
o/Oin NORMAL mode, or Ctrl+J, for a new line. - Vim motions cannot be remapped in the keybindings file. To map an INSERT-mode pair such as
jkto Escape, use thevimInsertModeRemapsuser setting.