Sessions
Resume, name, branch, find and export Claude Code conversations from the CLI, and understand what a resumed session restores and where transcripts live.
A session is a saved conversation tied to a project directory. Claude Code writes it to disk as you go, which means you can quit, come back tomorrow and carry on, branch off to try a different idea, or juggle several tasks without losing any of them.
This page is about the terminal CLI. The desktop app, Claude Code on the web and the VS Code extension each keep their own session lists (the desktop app can also pick up CLI sessions).
Getting back into a session
| Command | Effect |
|---|---|
claude --continue (or -c) | Reopen the latest conversation in this directory |
claude --resume (or -r) | Open the session picker |
claude --resume <name> | Jump straight to a named session |
claude --resume <session-id> | Resume by ID |
claude --resume /abs/path/to/session.jsonl | Resume from a specific transcript file |
claude --from-pr <number> | Open the picker filtered to sessions linked to that pull request |
/resume | Switch conversation from inside a running session |
My daily pattern: claude -c first thing to pick up where I stopped, and named sessions for anything that will span several days.
Sessions that are hidden by default
Runs started with claude -p or the Agent SDK are kept out of the picker and out of --continue, as are sessions whose first prompt was /loop. You can still resume any of them by ID. claude -p --continue does include -p, SDK and /loop sessions.
Resuming by ID from anywhere
claude --resume <session-id> works from any directory. It looks first in the current project and its git worktrees, then in every other project on the machine. The cross-project search only succeeds when exactly one other project has a transcript with messages for that ID, so a hand-copied duplicate gives you No conversation found with session ID: <id> rather than a guess. (Before v2.1.223, you had to resume from the directory the session last used.)
Background sessions
claude --continue will open a finished background session (v2.1.257 or later) but not one still running. If your latest conversation is running in the background, it exits with Your most recent conversation is running in the background and the session's ID; attach from claude agents or pick another with claude --resume.
Resuming a background session that is still running opens that live session rather than loading a copy (from v2.1.285; earlier versions told you to use claude attach <id> or stop it first). With --bg on the command line, the resume becomes a background dispatch instead.
From the shell, claude --resume <session> attaches this terminal to the running session. A prompt on the command line, as in claude --resume nightly-audit "include the admin routes too", is sent as its next turn first, and you see Sent your prompt to the background session (<id>); opening it…. Typing claude -p --resume <session> "..." at a terminal does the same.
It will not attach, and instead exits with status 1 and prints the claude attach <id> command (or points you to claude agents), if the command line includes:
- piped or redirected input or output,
- session-configuring flags such as
--permission-mode,--modelor--settings, - output-reading flags such as
--output-format jsonor--json-schema, - run-limiting flags such as
--max-turnsor--max-budget-usd,
or if agent view is turned off. Add --fork-session to resume a copy instead, or run claude stop <id> and repeat the command to take the conversation over with your flags. Prompts starting with / or !, and any prompt while the session is waiting on your answer to a question, are not sent; the message explains why with Your prompt was not sent to it.
From inside a session, /resume on a running background session moves your current conversation to the background and attaches you, printing Opening "<title>", running in the background (<id>). Press the left arrow on an empty prompt to return to agent view. If your current conversation cannot move to the background (you are already attached to one, or persistence is off), /resume prints the claude attach command instead.
What resuming brings back
Loading a session from its transcript restores:
- The conversation, including every tool call and result. A tool that was mid-run when the previous process died (say, in a crash) is not re-run. Claude sees it marked as cut off and is told to check whether it took effect before trying again, unless
CLAUDE_CODE_RESUME_INTERRUPTED_TURNis set. (Before v2.1.281, such calls were dropped or shown as interrupted by you.) - The model, with a few exceptions covered in model configuration.
- The agent, if the session started with
--agentor theagentsetting: same tool restrictions and model. Pass--agentto choose another. Claude Code looks for the agent definition in the original directory (if you trusted that workspace) and then in your current one; if neither has it, you get default tools and a warning naming the missing agent. - The permission mode, in many cases (details below).
- An active goal, with its turn count, timer and token baseline reset.
- Scheduled tasks that have not expired. Background Bash and monitor tasks are not restored.
- Notes about unfinished background work. Background subagents, background Bash commands and workflows that died with the old process appear as notes in the transcript. Claude reads them with your next prompt; they do not trigger a turn by themselves.
Not every launch flag is remembered. If the session relied on --mcp-config, --settings, --plugin-dir, --fallback-model or --add-dir, pass them again. Directories added mid-session with /add-dir are not restored either (though the picker still uses them to find the session). Normal settings files are re-read at launch, so anything configured there is fine. For --system-prompt and --append-system-prompt, see the CLI reference.
Permission mode after resuming
It depends how you resume. (Attaching to a running background session keeps whatever mode it is in.)
- Terminal, direct:
claude --continue,claude --resume <id>, orclaude --resume <name>matching exactly one session, without-p. The previous mode is restored subject to the table below.--permission-modeor--dangerously-skip-permissionsoverrides it. - Non-interactive:
claude -p --resumeorclaude -p --continuestart in whatever mode a freshclaude -pwould, except plan mode can be restored under the conditions listed below. - Session picker at launch (plain
claude --resume,--from-pr, or an ambiguous name): starts in the mode a new session would, except a session that ended in plan mode returns to plan mode unless you pass--permission-mode,--dangerously-skip-permissionsor--fork-session. /resumeinside a session: the conversation continues in your current mode, except one that ended in plan mode goes back to plan mode, even if you launched with--permission-modeor--dangerously-skip-permissions. A conversation already opened earlier in this process continues in your current mode.- VS Code: see VS Code.
| Ended in | Resumed via | Starts in |
|---|---|---|
bypassPermissions | Terminal | Whatever a new session would. Re-enable bypass with its launch flag or permissions.defaultMode: "bypassPermissions" in user, --settings or managed settings |
plan | Terminal | Plan mode (or the new-session default with --fork-session) |
auto | Terminal | auto, if your account still qualifies for auto mode |
| Manual | Terminal | Manual when a new session would otherwise start in auto by built-in default; if a settings defaultMode applies, that mode instead |
plan | Non-interactive, conditions met | Plan mode |
| Anything | Non-interactive, otherwise | Whatever a new claude -p run would |
plan | VS Code | Plan mode, with exceptions described on the VS Code page |
Restoring plan mode via -p or VS Code needs v2.1.246 or later. For -p, all of these must hold: you pass --permission-prompt-tool and not --permission-prompts none; you do not pass --permission-mode or --dangerously-skip-permissions; you do not pass --fork-session; and the run was not started through channels. See permission modes.
Resuming from a summary
On Pro and Max, if you resume a session that has been idle for more than about an hour and holds over 100,000 tokens, a dialog appears before your first message. The prompt cache has expired by then, so the next request processes the full history once whichever option you pick.
| Option | What happens | Trade-off |
|---|---|---|
| Resume from summary | Runs /compact straight away: one summarisation request, then history becomes the summary, your latest exchanges and up to five recently read files | Cheaper on every later request; anything the summary omits is gone |
| Resume full session as-is | Loads everything unchanged and re-caches it after your first message | Every detail kept; each request costs in proportion to the full history |
| Don't ask me again | Resumes in full and never shows the dialog again | As above |
Costs explains why per-request usage grows with session length.
The session picker
/resume with no argument, or claude --resume on its own, opens the picker.
What it shows
By default, sessions from the current worktree (including background sessions, marked bg) and sessions from elsewhere that added this directory with /add-dir. Each row shows the name (or generated title, summary or first prompt), time since last activity, git branch and file size.
Sessions whose first prompt was /loop are hidden (running /loop later in a conversation does not hide it; before v2.1.211, an early /loop hid it permanently). A session moved with /cd lives in the new directory's storage and, from v2.1.196, stays out of the old directory's list even after a crash.
Forks from /branch or --fork-session have their own IDs and appear as separate rows. Multiple entries for one session are grouped; press the right arrow to expand.
Shortcuts
| Key | Action |
|---|---|
Up/Down or k/j | Move through the list |
| Right/Left | Expand or collapse a group |
Enter | Resume the highlighted session |
1 to 9 | Resume the session at that position |
Space | Preview (Ctrl+V also works on terminals that do not treat it as paste) |
Ctrl+R | Rename the highlighted session |
/ or any other printable key | Search. Paste a GitHub, GitHub Enterprise, GitLab or Bitbucket pull or merge request URL to find the session that created it |
Ctrl+W | Toggle all worktrees of this repository (only in multi-worktree repos) |
Ctrl+A | Toggle every project on the machine (also shows project paths) |
Ctrl+B | Toggle filtering to the current git branch |
Esc | Leave search, or close the picker |
Picking a session from somewhere else
A session from another worktree of the same repo resumes in place; if that worktree no longer exists, it resumes in your current directory (see worktrees). A session from an unrelated project puts a cd plus resume command on your clipboard, unless that project's directory is gone, in which case it resumes where you are.
If a session selected from the claude --resume picker fails to load, you get Failed to resume the conversation with a retry command and exit code 1. From /resume, the error is reported and your current conversation keeps going.
Resuming by name
Name lookup covers the current repository and all its worktrees:
| Command | Exact match | Ambiguous |
|---|---|---|
claude --resume <name> | Resumes directly | Opens the picker with the name as a search |
/resume <name> | Resumes directly | Reports an error; use /resume alone to pick |
Naming sessions
Names make sessions easy to find and resume. They pay off most when several tasks run in parallel.
| When | How |
|---|---|
| At launch | claude -n stripe-webhooks |
| Mid-session | /rename stripe-webhooks (also shown on the prompt bar) |
| In the picker | Highlight and press Ctrl+R |
| Accepting a plan | Plan mode gives an unnamed session a title based on the plan |
| From claude.ai or the Claude app | Renaming a Remote Control session applies in the CLI too (v2.1.221 or later) |
| In the desktop app | Rename it there; it then resumes in the desktop app |
If you choose a name already used by another live session on the machine, the existing session keeps it and yours gets a two-word suffix, such as stripe-webhooks-quiet-otter, with a notice (from v2.1.232). This check skips AI-generated titles and default display names, the --name of background or -p sessions at startup, and sessions on older versions.
Unnamed sessions still get two automatic labels:
- Default display name (v2.1.196 or later): the directory name plus a two-character suffix, such as
api-7k. It identifies the session in running-session lists like agent view andclaude agents --json, but is not a resume handle. - Generated title: a short summary of your first prompt, written in the background by the small fast model (usually Haiku class). Plain
claude -pruns from a script do not get one. Accepting a plan replaces it with a plan-based title, which also replaces the default display name in running-session lists. Both kinds of title work with--resumeand/resume.
Branching
Branching copies the conversation so far into a new session and switches you into it, leaving the original untouched. It is ideal when you want to try a riskier approach without losing a good line of work.
/branch try-websocket-transport
Without a name, the branch is named after the conversation's first prompt (from v2.1.198 this works after compaction too, rather than falling back to Branched conversation). From the shell:
claude --continue --fork-session
/branch prints both IDs; return to the original with /resume and its name or ID.
| What | After /branch |
|---|---|
| Conversation history | Copied up to the moment you branched |
| "Allow for this session" grants | Kept, because the same process continues. --fork-session starts a new process and you re-approve |
| Running background subagents and Bash commands | Keep running, reporting into the new branch |
| Remote Control | Stays connected and follows you into the branch |
Resuming the same session in two terminals without forking interleaves both into one transcript, which is rarely what you want. For rewinding within one session, see checkpointing.
Managing context without leaving
/clearstarts a fresh, empty conversation. The old one is saved; get it back with/resumeor, in the same process, from the rewind menu's previous-session entry. With no argument, the new conversation keeps a name set with--nameor/rename(but not a generated title). Pass a name, as in/clear invoice-bugfix, to name the conversation you are leaving; the new one starts unnamed./compact [instructions]swaps history for a summary, optionally focused./contextshows what is using space.
See context window and best practices for when to clear versus compact.
Exporting and scripting
/export copies the conversation to your clipboard or saves it as readable plain text; give it a filename to skip the menu.
For scripts, use the structured interfaces rather than parsing transcripts:
| You want to | Use |
|---|---|
| Run once and capture the outcome | claude -p with --output-format json or stream-json (result, session ID, usage, cost) |
| Ask an existing session something | claude -p --resume <id> |
| React to session events | The transcript_path field given to hooks and status line commands, for example archiving in a SessionEnd hook |
| Embed Claude in an app | The Agent SDK |
For example, to get a quick changelog out of yesterday's session:
claude -p --resume 3f9c2a1e-... --output-format json \
"list every file we changed and why, as bullet points" | jq -r '.result'
See headless mode.
Where transcripts live
Transcripts are JSONL files at ~/.claude/projects/<project>/<session-id>.jsonl, where <project> is the working directory path with every non-alphanumeric character replaced by -. If that name would exceed 200 characters it is truncated and a hash of the full path appended.
Each line is a JSON object for a message, tool call or metadata. The format is internal and changes between releases, so do not build scripts on it.
| To | Set |
|---|---|
Store everything outside ~/.claude | CLAUDE_CONFIG_DIR environment variable |
Choose the <project> folder name | CLAUDE_CODE_PROJECT_DIR_NAME environment variable |
| Change the 30-day retention | cleanupPeriodDays in settings.json |
| Limit desktop and Cowork transcript age | desktopSessionCleanupPeriodDays in user, managed or --settings settings |
Cap how large a -p or SDK transcript grows | CLAUDE_CODE_TRANSCRIPT_LOCAL_GC environment variable |
| Stop writing transcripts in every mode | CLAUDE_CODE_SKIP_PROMPT_HISTORY environment variable |
Skip writing for one -p run | --no-session-persistence |
Transcripts are swept automatically after the retention period; claude purge removes a project's data immediately. Both are covered in the .claude directory. Deleting a background session with claude rm <id> leaves its transcript on disk, still resumable.
Naming the project folder yourself
Useful when a host application embeds Claude Code and gives each user or tenant its own config directory. Set CLAUDE_CODE_PROJECT_DIR_NAME together with CLAUDE_CONFIG_DIR (v2.1.234 or later):
CLAUDE_CONFIG_DIR=/var/lib/agents/acme CLAUDE_CODE_PROJECT_DIR_NAME=main claude
Transcripts then go to /var/lib/agents/acme/projects/main/ and auto memory to /var/lib/agents/acme/projects/main/memory/, regardless of working directory.
- It is ignored without
CLAUDE_CONFIG_DIR, because under the default~/.claudeit would merge every project into one folder. - Use 1 to 64 letters, digits, hyphens or underscores, and avoid Windows device names like
con. Anything else is ignored. - Set it in the shell that launches
claude; it is read once at startup, so a settingsenvblock cannot set it.
If you later launch with the same config directory but without the variable, Claude Code goes back to the derived folder. Your named sessions are still there: Ctrl+A in the picker lists both, and claude --resume <id> finds either.