Claude Code terminal sessions: the window where the work happens

A Claude Code session in the terminal is not a program with a window of its own. It is a process running inside whatever terminal emulator happened to be open, borrowing that emulator's keyboard handling, its colours, its notification behaviour, and its idea of what the working directory is. Most of the friction people attribute to Claude Code in the terminal is actually the emulator underneath it, and the fixes live in the emulator's settings rather than anywhere in the tool.

That is worth stating first because it changes how to read the symptoms. Shift+Enter submitting instead of inserting a line break is not a missing feature. Silence when a long task finishes is not an oversight. Both are the terminal not sending or not forwarding something, and both have a specific fix.

Confirming the session is really in the terminal

The install is one command, and the version it produces is the fastest way to tell which of several possible installations is actually running.

curl -fsSL https://claude.ai/install.sh | bash
claude --version
claude doctor

On macOS and Linux the native installer puts a launcher at ~/.local/bin/claude as a symlink into ~/.local/share/claude/versions/, and native installations update themselves in the background. Homebrew is the alternative for anyone who prefers it, with brew install --cask claude-code for the stable channel and claude-code@latest for the newest builds, and neither Homebrew cask auto-updates. The npm package, npm install -g @anthropic-ai/claude-code, installs the same native binary rather than something that runs on Node, and as of v2.1.198 it asks for Node.js 22 or later even though the installed binary does not use it.

The reason to run claude doctor rather than only claude --version is that it prints read-only diagnostics without starting a session: install health, settings files that fail to parse, and the result of the most recent update attempt. A machine with both an old npm global install and a newer native install will run whichever one PATH finds first, and that is the class of problem claude doctor is for.

The system requirements are modest and worth knowing before blaming the tool for something else: macOS 13 or later, 4 GB of RAM, x64 or ARM64, and an internet connection. The free claude.ai plan does not include Claude Code, so a session that will not authenticate on a free account is behaving correctly.

The newline problem, and the three answers to it

Pressing Enter submits the message. That is the intended behaviour, and it is also the first thing that gets in the way, because a prompt worth writing carefully is a prompt with more than one line in it.

Two ways to insert a line break work in every terminal with no setup at all: Ctrl+J, or typing a backslash and then pressing Enter. Neither needs a configuration file, and Ctrl+J is the one worth committing to muscle memory because it survives moving between machines.

Shift+Enter is the shortcut most people reach for, and whether it works depends entirely on the emulator.

Terminal Shift+Enter for a newline
Ghostty, Kitty, iTerm2, WezTerm, Warp, Apple Terminal, Windows Terminal Works with no setup
Other terminals supporting the kitty keyboard protocol, such as foot and Alacritty 0.16 or later Works with no setup, from v2.1.269
VS Code, Cursor, Devin Desktop, Alacritty before 0.16, Zed Run /terminal-setup once
gnome-terminal, JetBrains IDEs such as PyCharm and Android Studio Not available, so use Ctrl+J

/terminal-setup writes a Shift+Enter binding into the host terminal's own configuration file, which is why it has to be run in the host terminal rather than inside tmux or screen. Existing bindings are left alone. In VS Code, Cursor, and Devin Desktop it also turns terminal.integrated.gpuAcceleration off, because GPU acceleration in the integrated terminal garbles text.

On macOS there is a second keyboard gap that catches people out. Some shortcuts use the Option key, such as Option+P to switch models, and most macOS terminals do not send Option as a modifier by default. The setting is usually labelled "Use Option as Meta Key". In Apple Terminal it is under Settings, Profiles, Keyboard. In iTerm2 it is under Settings, Profiles, Keys, General, where both Option keys are set to "Esc+". Until that is on, those shortcuts do nothing at all, which reads as a broken feature rather than a missing signal.

Knowing when it finished without watching it

A session that runs for several minutes is a session worth walking away from, and the default behaviour on that front is narrower than most people assume. Claude Code sends a desktop notification only in Ghostty, Kitty, and iTerm2. In any other terminal, nothing arrives.

The one-line fix is to ring the terminal bell instead:

{
  "preferredNotifChannel": "terminal_bell"
}

That goes in ~/.claude/settings.json. iTerm2 needs one extra step even though it is on the supported list: under Settings, Profiles, Terminal, check "Notification Center Alerts", then open "Filter Alerts" and enable "Send escape sequence-generated alerts". Without that, the escape sequence arrives and iTerm2 declines to forward it.

For a sound rather than a bell, a Notification hook runs an arbitrary command when Claude needs attention, and hooks run alongside the built-in notification rather than replacing it:

{
  "hooks": {
    "Notification": [
      {
        "hooks": [{ "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" }]
      }
    ]
  }
}

The detail that makes this worth setting up properly rather than approximately: the desktop notification travels over SSH to the local machine, so a session running on a remote server can still alert the laptop in front of you. That is the case where a bell in a forgotten tab is genuinely useless and a real notification is not.

tmux, and why a long session belongs in one

Running inside tmux changes two behaviours, both silently. Shift+Enter goes back to submitting, and desktop notifications and the progress bar never reach the outer terminal because tmux swallows them. Three lines in ~/.tmux.conf restore both:

set -g allow-passthrough on
set -s extended-keys on
set -as terminal-features 'xterm*:extkeys'

Apply them to a running server with tmux source-file ~/.tmux.conf. The passthrough line is what lets notifications and progress updates reach the outer terminal. The extended-keys lines let tmux tell Shift+Enter apart from plain Enter.

The reason to accept that configuration cost is what tmux buys. A Claude Code session in the terminal is a local process, and if the process stops, the session stops with it. Over SSH that means a dropped connection ends the work. Starting the session inside tmux or screen on the remote machine keeps it running after the SSH connection goes away, which is the documented answer for exactly that case.

A related rendering setting belongs here. If the display flickers or the scroll position jumps while Claude is working, /tui fullscreen switches to fullscreen rendering and saves the preference. The trade is that scrolling then happens inside Claude Code with the mouse or PageUp rather than through the terminal's own scrollback.

What the session shares with the shell around it

The terminal session is the most complete of the surfaces Claude Code runs on, and the reason is not a feature list. It is that the session and the shell share a working directory, an environment, and a view of the filesystem. Scripting and the Agent SDK are available only from the CLI. Everything the shell can reach, the session can reach, with no intermediate step.

That sharing has two practical consequences worth planning around.

The first is workspace trust. Running claude in a project directory prompts once to accept trust for that directory, and the startup dialog never saves trust for the home directory. Starting a session from a project folder rather than from ~ is therefore not just tidier, it is the difference between a feature working and not working: Remote Control specifically requires a trusted project directory.

The second is how large content should get into the session. Pasting more than 800 characters or more than three lines collapses the input into a placeholder such as [Pasted text #1 +120 lines], and the full content is still sent on submit. For an entire file or a long log, writing it to a file and asking Claude to read it is the better move: the transcript stays readable and the file can be referred to by path in later turns. In the VS Code integrated terminal there is a harder reason, which is that very large pastes can drop characters before they arrive.

This is also where the shape of a terminal workflow starts to matter more than any single setting. A session spends most of its time reading and writing files in a directory that is open somewhere else on screen, in a separate window. A file manager with a built-in terminal removes that separation, because the pane and the shell point at the same directory and a change made by the session is visible without switching windows. The Features page covers what that arrangement includes, and the Compared with other file managers page sets out how it differs from the alternatives.

Leaving the terminal without ending the session

The limitation of a terminal session is physical: it runs on one machine, in one process, in front of one keyboard. Remote Control is the documented way around that without giving up the local filesystem.

Start it with claude remote-control in a project directory, or /remote-control in a session that is already open. The local session keeps running and keeps doing all execution and file access on the machine it started on. A phone or a browser connects to it through claude.ai and can send messages, answer permission prompts, and stop subagents. The connection is outbound HTTPS only, so no inbound port is opened on the machine.

Three constraints are worth knowing before relying on it. The local process must keep running, so closing the terminal takes the session offline until it is brought back, and a session on a remote machine needs tmux or screen to survive an SSH disconnect. Outside server mode, each Claude Code instance supports one remote session at a time. And commands that only exist in the terminal interface, /plugin and /resume among them, stay local-only regardless of what is connected.

Push notifications to a phone come with Remote Control rather than separately. In the terminal, /config has two toggles, "Push when Claude decides" and "Push when actions required", and notifications are skipped while you are typing in the connected terminal. Working from a phone in general, including which commands behave differently there, is covered on the From iPhone and iPad page.

What to change first

Learn Ctrl+J for a newline before configuring Shift+Enter, because it works everywhere and needs nothing. Then set up one notification route so a finished task reaches you when you are not looking at the tab, using preferredNotifChannel if the terminal is not Ghostty, Kitty, or iTerm2. If the slow part turns out to be switching between the session and the folder it is working in, Atriens puts both in one window.

Frequently asked questions

Why does Shift+Enter submit instead of adding a line break?

The terminal emulator is not sending Shift+Enter as a distinct key. In VS Code, Cursor, Devin Desktop, Zed, and Alacritty before 0.16, running /terminal-setup once writes the binding into that terminal's configuration. In gnome-terminal and JetBrains IDEs it is not available at all. Ctrl+J inserts a newline in every terminal with no setup, and inside tmux the extended-keys settings are required as well.

Does a terminal session keep running if the terminal window closes?

No. A terminal session is a local process, and closing the window or quitting the terminal ends it. To keep one alive across an SSH disconnect, start it inside tmux or screen. To keep steering it from a phone or browser while it runs, use Remote Control, which still requires the local process to stay up.

Why is there no sound or alert when a long task finishes?

By default a desktop notification is sent only in Ghostty, Kitty, and iTerm2. Elsewhere, set preferredNotifChannel to terminal_bell in ~/.claude/settings.json, or configure a Notification hook to play a sound. iTerm2 also needs "Notification Center Alerts" and "Send escape sequence-generated alerts" enabled in its profile before the alert is forwarded.

Is the terminal version missing anything the desktop app has?

The trade runs in both directions. The desktop app adds a diff viewer and app preview. The CLI is the only surface with scripting and the Agent SDK, and it is the one that shares a working directory and environment with the shell. Configuration, project memory, and MCP servers are shared across the local surfaces, so running both on the same project is normal rather than a conflict.

Should a long log be pasted into the session or written to a file?

Write it to a file and ask Claude to read it. Anything over 800 characters or three lines collapses into a placeholder in the input box anyway, and a file can be referred to by path in later turns instead of sitting in the transcript. In the VS Code integrated terminal there is an additional reason, which is that very large pastes can lose characters before they reach the session.

Back to all posts