Claude Code Sessions: Resume, Branch and Name Your Work

Use the continue flag for the most recent session in the current directory, the resume flag with no argument to open the session picker, or the resume flag with a name or session ID to go straight there. Inside a session, the resume command switches to another conversation. Resuming by session ID works from any directory on the machine.
Launch flags are not restored, so pass MCP config, settings, plugin directories, fallback model and any added directories again. Plan mode and bypass-permissions mode are never restored. Background Bash commands and monitor tasks are not restored either, though unexpired scheduled tasks are.
Both copy the conversation so far. The branch command switches the running process to the copy, so permission grants you approved for the session still apply. Forking from the shell starts a separate process, which begins without those grants, so you approve again there. The original session is untouched either way.
As JSONL files under your config directory, one per session, in a folder named after the working directory path with non-alphanumeric characters replaced by hyphens. The entry format is internal and changes between releases, so use the export command or the scripting interfaces rather than parsing the files directly.
Only if it is a name you set or a generated title. The default display name, which combines the directory name with a two-character suffix, identifies a session in listings of running sessions but is not a resume handle — passing it reports that no session was found. Naming the session replaces the default everywhere.

Key Takeaway
A Claude Code session is a conversation saved continuously to a local JSONL transcript and tied to a project directory. Resuming restores the history, the model, the agent, the permission mode and any active goal, but not the flags you launched with. Branching copies the conversation in place; forking starts a separate process without your session permission grants.
I lost forty minutes once because I resumed a session in a different terminal, in a different directory, without the extra directory I had added at launch — and spent the first ten of those minutes convinced the model had forgotten the project rather than that I had failed to pass a flag. Sessions restore a great deal. The list of what they do not restore is short, specific, and worth memorising.
This post covers the five ways back into a conversation and what each one skips, exactly what a resume restores, the summary dialog that appears on a long-idle session, the three labels a session carries and which of them you can actually resume by, branching against forking, and where the transcripts live when you need to move or expire them.
Sessions are saved as you work, so exiting or clearing does not lose one. Which route you take matters because they filter differently: non-interactive runs and Agent SDK sessions are left out of the picker and out of continue, background sessions are skipped by continue, and a session whose first prompt was a loop command is hidden from both.
claude --continue # most recent in THIS directory
claude --resume # the picker
claude --resume auth-refactor # by name
claude --resume 5fbea6f3-… # by session ID, from ANY directory
claude --from-pr 4821 # picker, filtered to that PR
/resume # switch from inside a session
# Name it, so it is findable later:
claude -n auth-refactor
/rename auth-refactor
# Ctrl+R on a row in the picker renames it too.
# Two names collide on one machine? The existing session keeps
# the name and yours becomes auth-refactor-graceful-unicorn.Resuming by session ID is the escape hatch when a session has moved. Claude Code looks in the current project and its worktrees first, then every other project on the machine — resolving only when exactly one other project holds a transcript for that ID, so a hand-copied duplicate reports not-found rather than resuming an arbitrary copy.
The conversation comes back in full, tool calls and results included. So do several pieces of session state, each with a caveat that is easier to learn now than during an incident.
State on resume:
| What | How it comes back |
|---|---|
| Model | The one the session was using — unless it has been retired, is disallowed, or a model flag or environment variable picks one at launch |
| Agent | A session started as an agent continues as that agent. Claude Code looks in the session's original directory and then the one you resume from, and warns by name if it finds neither |
| Permission mode | Restored, except that plan and bypass-permissions never are, and auto only when your account still qualifies |
| Active goal | Carried over, with its turn count, timer and token baseline reset. An achieved or cleared goal is not restored |
| Launch flags | Not restored. Pass MCP config, settings, plugin directories, fallback model and added directories again — settings files are re-read on their own |
One more thing that catches people: resuming the same session in two terminals without forking interleaves both conversations into one transcript. There is no warning and no conflict, just a history that reads like two people talking over each other. If you want two lines of work from the same starting point, fork one of them.
On Pro or Max, resuming a session that has been quiet for about an hour and is over 100,000 tokens opens a dialog before your first message. The prompt cache has expired by then, so the next request reprocesses the whole history whichever option you pick — the choice is about every request after that one:
A session can carry a name you set, a generated title, and a default display name — and the distinction matters the moment you try to resume by typing what you saw. The default display name, something like my-app-3f, identifies a session in listings of running sessions but is not a resume handle: pass it and Claude Code will not find the session. The generated title, a short summary of your first prompt, is one.
Naming replaces both. Set it at launch, with the rename command mid-session, or with Ctrl+R on a row in the picker; accepting a plan also generates a title from the plan. If a live session on the same machine already holds the name, yours becomes a variant with a two-word suffix and you are told — which is friendlier than the older behaviour of letting two sessions share one name.
Branching copies the conversation so far and switches you into the copy, leaving the original untouched in the picker. Forking from the shell does the same copy but in a new process. That one difference decides what comes with you, and the permission grants are the part people notice.
# Branch: copy the conversation, keep working in the copy.
/branch try-streaming-approach
# The confirmation prints TWO session IDs — the branch you are
# now in, and the original, which is untouched on disk.
# From the shell instead, a separate process:
claude --continue --fork-session
# What the branch inherits — and this is where the two differ:
#
# conversation history copied up to the /branch point
# "allow for this session" KEPT by /branch (same process),
# LOST by --fork-session (new one)
# background subagents keep running; output lands in the
# and background shells branch, not the original
# Remote Control follows you into the branchThe picker is a search tool, not just a list. Any printable character starts a filter, Ctrl+W widens to every worktree of the repository, Ctrl+A widens to every project on the machine, and Ctrl+B filters to the current git branch. Best of all, pasting a pull or merge request URL from GitHub, GitLab or Bitbucket finds the session that created it.
Transcripts are JSONL under your config directory, one file per session, in a folder derived from the working directory path. Read them with the export command or the script interfaces rather than parsing them: the entry format is internal and changes between releases, so a script that reads them directly is a script that breaks on an upgrade.
# Transcripts are JSONL, one object per line:
~/.claude/projects/<project>/<session-id>.jsonl
# <project> = the working directory path with every
# non-alphanumeric character replaced by a hyphen
# The entry format is INTERNAL and changes between releases.
# Parse /export output or the script interfaces instead:
/export # menu, or a filename
claude -p --resume <id> --output-format json "summarize what we
changed" | jq -r '.result'
# Where it lives and how long it stays:
CLAUDE_CONFIG_DIR=/srv/tenant-a # move it off ~/.claude
CLAUDE_CODE_PROJECT_DIR_NAME=work # name <project> yourself
CLAUDE_CODE_SKIP_PROMPT_HISTORY=1 # write nothing, ever
{ "cleanupPeriodDays": 30 } # settings.json, default 30
claude -p --no-session-persistence "…" # one run, no traceThe CLI, the desktop app, the web and the VS Code extension each keep their own session history, so a session is not automatically everywhere. Two bridges exist and are easy to forget: the teleport flag resumes a web session in your local terminal, and renaming a Remote Control session from claude.ai applies the same name in the CLI.
Name sessions early and by task, not by intent — auth-refactor rather than trying-something. A week later the picker is a list of forty rows and the only ones you can find are the ones whose names describe work you remember doing. It costs one command at the start and saves the small archaeology of previewing rows with the spacebar to work out which is which.
Learn the three things that are not restored — launch flags, plan and bypass modes, and background tasks — and resuming stops producing surprises. Fork before you explore, so the path you were on is still there when the experiment does not work. And if you run Claude Code inside something that embeds it, set the config directory and the project directory name deliberately, because the default derives a folder from the working directory path and that is rarely what a host wants.
Sources & further reading