Sessions¶
Finding past conversations, moving one to another account, and deleting the ones you are done with.
claude-sessions [options]¶
List conversations readably, so you can identify one by what it was about rather than by a 36-character id.
╭───┬────────┬───────┬──────────────────────────────────────┬──────────────────────────────────╮
│ # │ WHEN │ TURNS │ SESSION ID │ SUMMARY │
├───┼────────┼───────┼──────────────────────────────────────┼──────────────────────────────────┤
│ 1 │ 2m ago │ 18 │ eb82f424-4aff-43eb-a0e1-f1e130553fc6 │ refactor the auth middleware… │
│ 2 │ Sep 11 │ 2 │ 2c24a68b-8411-4bb6-9072-99b86b5ae909 │ cant able to take pull │
╰───┴────────┴───────┴──────────────────────────────────────┴──────────────────────────────────╯
The table adapts to your terminal: on a narrow one the SESSION ID column
shortens to an 8-character ID, which is still enough for claude-handoff
since it accepts prefixes.
Defaults to the current directory and the active profile, newest first.
A session that exists in more than one profile - because it was handed off - is marked, so you can see at a glance where to resume it:
1 work 0m ago 145 eb82f424 * refactor the auth middleware…
2 personal 6m ago 139 eb82f424 · refactor the auth middleware…
* is the copy with the most turns, which is the one you continued on. · is
behind. On a tie the more recently used one wins.
| Flag | Effect |
|---|---|
-a, --all |
every directory, not just the current one. Adds a dim ~/path (branch) line under each row |
-A, --all-profiles |
scan every profile. Adds a PROFILE column |
-p NAME, --profile NAME |
one specific profile |
-n N, --limit N |
max rows (default 20). Bare --limit means no limit |
-f, --full |
wrap long summaries instead of truncating them |
-d DIR, --dir DIR |
filter on a directory other than the current one |
--plain |
no borders - easier to pipe into other tools |
-g PATTERN, --grep PATTERN |
only sessions containing PATTERN (case-insensitive regex) |
claude-sessions # this directory, active profile
claude-sessions -a # every directory
claude-sessions -A -a --full # everything, everywhere, untruncated
claude-sessions -p work -n 50 # 50 most recent in the "work" profile
Summaries adapt to terminal width between 40 and 100 characters. TURNS counts
your messages, ignoring tool results and sidechains.
Searching your conversations¶
Searches the full text of every message - yours and Claude's - not just the opening prompt. Sessions that match show the matching passage in place of the summary, with surrounding context:
The pattern is a case-insensitive regular expression, so -g "auth|login" works.
An invalid pattern exits 2 with the regex error rather than silently matching
nothing.
Combine it with -A -a to search every profile and every directory at once -
this is the fastest way to find the conversation where you solved something
months ago.
claude-handoff TARGET [SESSION]¶
Copy a conversation into another profile so that account can resume it. See the guide for what this is for; this section is the mechanics.
claude-handoff work # this directory's most recent conversation
claude-handoff work 0a9014e8 # a specific one - a prefix is enough
claude-handoff work -s client # take it from "client" instead of the active profile
claude-handoff work -d ~/projects/<project-name>/api # conversations belonging to another directory
| Argument | Meaning |
|---|---|
TARGET |
profile to copy into. Must already exist |
SESSION |
optional session id or unique prefix. Omit for the latest |
-s, --source |
profile to copy from (default: the active one) |
-d, --dir |
directory whose conversations to consider (default: current) |
-m, --with-memory |
also merge this project's saved memory into the target |
--force-memory |
replace the target's memory instead of merging |
-n, --dry-run |
show what would happen, change nothing |
-f, --force |
overwrite a target copy that is ahead or has diverged |
--move |
retire the source copy after handing off (the default) |
--copy |
leave the source copy in place |
On success it prints the resume command:
moved session 0a9014e8-8806-43cf-98fd-28fad1353923
from: default to: work
dir : ~/projects/<project-name>/api
side files: 24 copied
default's copy retired as 0a9014e8-8806-43cf-98fd-28fad1353923.jsonl.bak - undo by renaming it back
claude-profile work && claude --resume 0a9014e8-8806-43cf-98fd-28fad1353923
Guards. An ambiguous prefix lists the matches instead of guessing. Handing off to the same profile, or to one that does not exist, is refused with a message rather than silently doing nothing.
Move or copy¶
By default handoff moves the session. The transcript is copied, the copy is
verified byte for byte, and only then is the source renamed to
<session-id>.jsonl.bak. A renamed file is invisible to claude-sessions and
to claude --resume, so there is one live copy and no question of which to
resume - and undoing a move is a rename.
--move (default) |
--copy |
|
|---|---|---|
| Target | gets the session | gets the session |
| Source | transcript renamed to .bak, side files removed |
left in place |
| Where to resume | the target, unambiguously | either - claude-sessions marks the leader |
| If the copy fails to verify | source untouched | source untouched |
Reach for --copy to try something on another account without committing to it.
Set CLAUDE_HANDOFF_COPY=1 to make it your default; --move overrides that for
one command.
Existing installs
Before 2.0.0 handoff only ever copied. Running claude-update changes the
default to move. Pass --copy or set CLAUDE_HANDOFF_COPY=1 to keep the
old behaviour.
When the target already has the session¶
Handing the same session back and forth is normal: you move it to personal
when work runs out, then back to work once the limit resets. So the target
often already has a copy, and overwriting the wrong one would lose turns.
What happens depends on which copy is further along:
| Target copy | Result |
|---|---|
| Not there | copied |
| Identical | nothing to do, says so |
| Fewer turns | updated, reports 3 turns -> 7 |
| More turns | refused - that copy has work the source does not |
| Same turns, different contents | refused - they were continued separately |
A refusal tells you both turn counts, so you can see which copy to resume:
work already has this session and it would be overwritten
work: 7 turns personal: 4 turns - the copy in work is further along
resume it there instead, or pass --force to overwrite (a .bak is kept)
--force overwrites anyway and writes the previous contents to
<session-id>.jsonl.bak first, so nothing is unrecoverable.
The session's own side files¶
A long conversation is not only its transcript. Claude Code spills big tool results, subagent transcripts and workflow journals into a directory named after the session, next to it:
~/.claude-profiles/<profile>/projects/<encoded-dir>/
<session-id>.jsonl the conversation
<session-id>/ its side files - tool results, subagents, workflows
memory/ facts about the project, shared by every session here
Those side files belong to that one session, so handoff takes them along
without being asked. There is no flag. Memory is different - it is shared by
every session in the project, so moving it is your decision and needs
--with-memory.
Merging follows the same no-overwrite rule as memory:
| Situation | What happens |
|---|---|
| File only in the source | copied |
| File only in the target | left alone |
| Same file, identical | nothing to do |
| Same name, different contents | the target's is kept, and a move leaves the source directory in place |
That last row is the safe default: rather than pick a winner, the command keeps both sides and says so.
moved session 0a9014e8-…
side files: 23 copied, 1 kept the target's
default's side files stayed - some of them differ in the target
A --copy handoff copies the side files and leaves the originals alone, same as
it does with the transcript.
Upgrading from 2.0
Handoffs made before 2.1.0 left the side directory behind in the source
profile. A resumed session still works - Claude Code refetches what it
needs - but old tool results and subagent transcripts stay in the old
account. claude-profiles doctor reports any that are stranded.
Carrying the project's memory across¶
Claude Code saves memory per profile and per project:
~/.claude-profiles/<profile>/projects/<encoded-dir>/
<session-id>.jsonl the conversation - moved
<session-id>/ the session's side files - moved
memory/ saved facts about the project - a sibling, left alone
A plain handoff moves the conversation, not the memory. The conversation itself carries everything that was said, so you can keep working - but the distilled facts Claude normally reads at the start of a session stay behind.
That merges them:
| Situation | What happens |
|---|---|
| File only in the source | copied |
| File only in the target | left alone |
| Same file, identical | nothing to do |
| Same name, different contents | both kept - the incoming one saved as <name>.from-<source>.md |
MEMORY.md |
the two indexes are combined, keeping the target's order |
Memory is one file per fact with a unique name, so merging is a set union rather than an edit of prose. Nothing is overwritten and nothing is lost.
moved session a3f81c2e-…
from: work to: personal
memory: 2 copied, 1 already there, 1 kept side by side
Look before you leap:
dry run - nothing will be written
would hand off a3f81c2e-… work -> personal
copy no-em-dashes.md
same show-drafts.md
differs api-conventions.md -> kept as api-conventions.from-work.md
--force-memory replaces the target's memory for that project outright. Rarely
what you want, and it does discard whatever the target had learned.
The better fix for most facts
Anything true about the project regardless of which account you use
belongs in CLAUDE.md in the repository. Every profile reads it, it
survives handoffs, and it is version-controlled. Profile memory then holds
only the account-specific things, and losing those on a handoff is correct
rather than a gap.
claude-clear [PATH]¶
Delete the conversations recorded for one project directory, across every profile. Use it when a project is finished, or when you want a clean slate for it without touching anything else.
Look before you delete
Add -n to any of these. It prints the same list and the exact files it
would remove, and deletes nothing.
It checks the path exists first, then lists what it found and asks which to delete:
3 session(s) for ~/projects/<project-name> 41.2MB across 2 profile(s)
╭───┬─────────┬────────┬───────┬────────┬──────────────────────────────────────┬──────────────────────────╮
│ # │ PROFILE │ WHEN │ TURNS │ SIZE │ SESSION ID │ SUMMARY │
├───┼─────────┼────────┼───────┼────────┼──────────────────────────────────────┼──────────────────────────┤
│ 1 │ work │ 2d ago │ 191 │ 38.4MB │ c9f6d2ae-80e4-456d-a6c9-2b6c562c6313 │ set up the staging box… │
│ 2 │ default │ Oct 03 │ 17 │ 2.6MB │ 6f2c1864-f20c-4eba-b105-dbf8f4814ed9 │ fix the failing test… │
│ 3 │ work │ Oct 03 │ 1 │ 212KB │ 2ac384e3-453d-45bb-9dd8-fa293c0b58fe │ ok │
╰───┴─────────┴────────┴───────┴────────┴──────────────────────────────────────┴──────────────────────────╯
which to delete?
2 one
1,3,5-7 several
all every session listed
enter cancel
>
Pick numbers and it shows exactly which files will go, then asks once more:
about to delete 2 session(s), 38.6MB:
c9f6d2ae-80e4-456d-a6c9-2b6c562c6313 work, 191 turns, 38.4MB
~/.claude-profiles/work/projects/-Users-you-projects-<project-name>/c9f6d2ae-….jsonl
~/.claude-profiles/work/projects/-Users-you-projects-<project-name>/c9f6d2ae-… (side files)
6f2c1864-f20c-4eba-b105-dbf8f4814ed9 default, 17 turns, 2.6MB
~/.claude/projects/-Users-you-projects-<project-name>/6f2c1864-….jsonl
this cannot be undone. type y to delete:
| Flag | Effect |
|---|---|
PATH |
project directory (default: the current one) |
-p NAME, --profile NAME |
just one profile (default: all of them) |
-r, --under |
also sessions started in subdirectories of PATH |
-s ID, --session ID |
delete this id or prefix instead of prompting. Repeatable |
-a, --all |
select every session listed |
-n, --dry-run |
show what would go, delete nothing |
-y, --yes |
skip the confirmation |
--plain |
no borders |
What it looks at, and what it deletes¶
Sessions are matched on the working directory recorded inside each
transcript, not on the folder name, so a session handed between profiles is
still found. Symlinks are resolved, so /tmp and /private/tmp match.
Deleting a session removes everything that belongs to it. The conversation is
only part of that: Claude Code keys several folders beside projects/ by
session id, and some of them hold real content.
| Removed | What it held |
|---|---|
projects/<dir>/<session-id>.jsonl |
the conversation |
projects/<dir>/<session-id>.jsonl.bak |
a copy left by an earlier handoff |
projects/<dir>/<session-id>/ |
its tool results and subagent transcripts |
file-history/<session-id>/ |
copies of every file that session edited, version by version |
jobs/<short-id>/ |
a background job's state and timeline |
session-env/<session-id>/ |
its environment |
tasks/<session-id>/ |
its task state |
anything else named <session-id> or <session-id>.* |
plugin notes and the like |
Only exact names count, so deleting some of a project's sessions never
disturbs the rest. A folder whose name merely contains the id is not matched,
nor is another session's. jobs/ is the one place Claude Code uses a
shortened id, and that eight-character rule applies there alone - and is
skipped altogether when two sessions happen to begin with the same eight
characters, so the survivor keeps its job.
The shared folders themselves stay: projects/<dir>/, file-history/,
jobs/ and the rest are left in place for the sessions still using them.
| Kept | Why |
|---|---|
memory/ |
shared by every session in the project, not owned by one conversation |
| sessions for other directories | out of scope |
| sessions in subdirectories | unless you pass -r |
file-history/ is the one that matters most. It holds readable copies of your
source files as that session changed them, so deleting the transcript alone
leaves the content behind. Delete the memory/ folder yourself if you want
that gone too.
Deleting is not erasing
These files are unlinked, not overwritten, so the blocks survive until the disk reuses them. Your backups keep their own copies regardless. If a session saw a password or a token, rotate it - do not rely on deleting the file.
Sweeping up after older versions¶
Before 2.3.0, deleting a session left the folders beside projects/ behind.
claude-prune --traces clears
them, and claude-doctor reports whatever is still there.
Without a prompt¶
For scripts, choose up front. Without a selection and without a terminal it refuses rather than guessing:
claude-clear ~/projects/<project-name> -s a3f81c2e -y # one session
claude-clear ~/projects/<project-name> --all -y # all of them
claude-clear ~/projects/<project-name> --all -n # just show the plan
Recipes¶
| Goal | Command |
|---|---|
| See what is there, change nothing | claude-clear ~/projects/<project-name> -n --all |
| Clear the project you are standing in | claude-clear |
| Include sessions from its subfolders | claude-clear ~/projects/<project-name> -r |
| Only one account's copies | claude-clear ~/projects/<project-name> -p work |
| Drop one session by its id prefix | claude-clear ~/projects/<project-name> -s a3f81c2e -y |
| Wipe the project, no prompt | claude-clear ~/projects/<project-name> --all -y |
| Clean up after older versions | claude-prune --traces |
| Easier to read in a narrow window | add --plain |
Flags combine, so -r -p work -n previews everything work recorded in that
project and below it.
There is no undo
Unlike a handoff, this deletes. claude --resume cannot reach a cleared
session and no .bak is kept. Run it with -n first, and
hand off anything worth keeping.
claude-prune¶
Housekeeping: delete sessions you no longer need. Dry run by default - it
always lists first and deletes nothing until you pass --yes.
Three selectors, and you can combine them:
claude-prune # older than 90 days (the default)
claude-prune -o 30 # ...older than 30 days
claude-prune --orphaned # the project directory no longer exists
claude-prune --traces # files left behind by sessions already deleted
claude-prune --orphaned --traces # both
claude-prune -o 90 --yes # actually delete
╭────────┬─────────┬──────────────────────────────────────┬────────┬───────────────────────────────╮
│ WHEN │ PROFILE │ SESSION ID │ SIZE │ DIRECTORY │
├────────┼─────────┼──────────────────────────────────────┼────────┼───────────────────────────────┤
│ Aug 19 │ default │ a6c22511-53a7-4a10-8e16-104b79f2dc17 │ 2.3MB │ ~/projects/<project-name>/api │
╰────────┴─────────┴──────────────────────────────────────┴────────┴───────────────────────────────╯
3 session(s), 6.1MB (90 session(s) untouched)
dry run - nothing deleted. pass --yes to delete.
| Flag | Effect |
|---|---|
-o DAYS, --older-than DAYS |
age threshold (default 90, and the default selector) |
--orphaned |
also sessions whose recorded directory is no longer on disk |
--traces |
also files left behind by sessions that are already deleted |
-p NAME, --profile NAME |
just one profile (default: all) |
-n N, --limit N |
rows to list, 0 for all (default 20) |
-y, --yes |
actually delete |
--plain |
no borders |
Passing --orphaned or --traces replaces the age default rather than adding
to it, so claude-prune --orphaned takes nothing merely for being old. Ask for
both by naming both: claude-prune -o 90 --orphaned. A session that is old
and orphaned is listed once, with a WHY column appearing when the rows have
different reasons.
Whatever it takes, it takes whole: the transcript, its side files and every
profile-level file named after it, the
same as claude-clear. The size it reports is the total of all of them.
--orphaned: the project is gone¶
You delete a project folder; its conversations stay in the profile. They cannot
be usefully resumed - the working directory they refer to is not there - but
they still hold everything that was said, and every file the session edited is
still in file-history/.
The directory is shown for each one, so you can check nothing is merely unmounted or on an external disk before confirming. A transcript with no readable working directory is never called an orphan - no evidence, no verdict.
--traces: leftovers of sessions already deleted¶
Anything that removed a transcript without sweeping the folders beside
projects/ left files behind, named after a session that no longer exists.
Liveness is judged across every profile, not one: handing a session over moves
its transcript but not these files, so a session living in another profile is
never treated as deleted. claude-doctor reports whatever is left.
╭─────────┬───────────────┬───────┬─────────┬───────────────────────────────╮
│ PROFILE │ WHERE │ FILES │ SIZE │ WHAT IT HELD │
├─────────┼───────────────┼───────┼─────────┼───────────────────────────────┤
│ default │ file-history │ 4 │ 2.1MB │ copies of the files it edited │
│ default │ jobs │ 2 │ 2.3KB │ a background job's state │
│ work │ session-env │ 29 │ 0B │ its environment │
╰─────────┴───────────────┴───────┴─────────┴───────────────────────────────╯
prune or clear?¶
claude-prune |
claude-clear PATH |
|
|---|---|---|
| Picks by | age, orphaned, leftovers | one project directory |
| Scope | every project, every profile | that directory |
| You choose | no - the selector decides | yes, from a numbered list |
| Safe by default | lists, needs --yes |
you pick, then confirm |
Deleted transcripts are gone
There is no undo, and claude --resume cannot reach a deleted session.
Check the dry run first, and hand off anything worth
keeping. Deleting unlinks rather than erases - rotate any secret a session
saw rather than relying on the delete.