Skip to content

vt-t-session-restore

This skill should be used after a re-login, a session-limit reset, or a switch to a second Anthropic account or to another org of the same login, when Claude Code sessions across several repositories need to be brought back to where they stopped. For desktop-app work it reports which sidebar sessions the new account or org is missing, puts chosen ones — or all of them — back into the sidebar with claude-boot adopt, and hands over a ready opening message for the rest, without opening a terminal; for CLI work it resumes conversations one terminal window per repository. It surveys every conversation on disk and names which repositories carry uncommitted work before anything is started. Trigger phrases include "restore my sessions", "bring my sessions back", "I switched accounts", and "start everything again".

Plugin: vt-toolkit
Category: Other
Command: /vt-t-session-restore


Session Restore

Overview

Claude Code stores every conversation as a JSONL transcript under ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl, keyed by folder. The desktop app's session sidebar is stored separately under claude-code-sessions/<account>/<org>/, keyed by account and org — one login can hold several orgs, and each org has its own sidebar. That asymmetry is the whole point of this skill: after an account or org switch the sidebar looks empty while every conversation is still on disk, untouched and resumable.

This skill restores those conversations and reports what it finds before it acts. Which route applies depends on where the work was: a conversation from the desktop app belongs back in the desktop app (Step 1), one from the CLI belongs in a terminal (Steps 2-4).

When to use

  • After logging out and back in, or after switching to a second account because a session limit hit
  • After switching to another org of the same login (team plan ↔ private plan) — the same situation
  • After killing stale processes (an expired OAuth session leaves sessions alive but unable to work)
  • When the sidebar is empty but yesterday's work should still exist
  • Before a working day, to see which repositories carry uncommitted work

Workflow

Step 1 — The desktop report (start here after an account or org switch)

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot desktop

Compares the sidebars in the desktop store — (account, org) pairs — and prints, per session the current sidebar is missing: the folder, the git state and a ready opening message whose first instruction is the switch into that folder. It opens no window and starts no process.

Read the first lines: current account: names the account and the rule that decided it, and current org: the org, when a rule could name one (this session's own entry, or a scratch path). Without a current org: line only the account is known and the comparison is account-wide — an org switch inside that account is then invisible; say so. If the account is wrong, everything below it is inverted — say so rather than reading on.

An exit code of 1 means drift: a store was there but yielded nothing, so the layout may have changed. Report that as a finding; it is not "nothing is missing".

A section OFFENE RÜCKFRAGEN lists the current account's sessions that wait on something. Relay it before the user quits the desktop app: quitting records every open question as a rejection by the user, and the session then proceeds as if the user had declined (references/mechanics.md). A line Rückfrage: is a question; a line Werkzeug: is an unanswered tool call — often a pending approval, which the transcript alone cannot confirm. Say the second half too: answer the listed questions and then quit right away — a question that appears in between is rejected the same way, and nothing afterwards can tell it apart from an answer the user gave.

How to read the rest of the report:

Section Means Exit code
FEHLT: … a session of another sidebar with no counterpart here — put it back with adopt, or restart it with the message below it 0
Auch in: … under a FEHLT the same conversation sits in further sidebars; the block shows the newest live copy, which adopt takes 0
Hinweis: … --allow-shared-folder the folder already has a session here; adopt needs that flag for a second entry on it 0
Hinweis: … --replace-older this sidebar holds the same entry with another, older conversation; adopt overwrites it only with that flag 0
DOPPELT im aktuellen Konto two entries of this account on one folder or one conversation; nothing is missing, the sidebar is just cluttered 0
no second account found only one sidebar in the store (one account, or with a known org one account/org pair), so only duplicates can be reported 0
HINWEIS: N Eintrag(e) nicht lesbar the report is incomplete by that many entries — everything else still holds 0
BEFUND: … nicht gefunden a store was not there at all 0
BEFUND: … vorhanden, aber … + Keine Aussage über fehlende Sessions möglich. drift: the store exists and yielded nothing, so no count is trustworthy 1

"0 Sessions fehlen" is a statement about what the rules compared, not proof that nothing is gone: archived entries and entries whose conversation a live entry of the current sidebar already has are dropped silently (references/mechanics.md). An entry whose folder the sidebar already claims is reported since BUG-309, with a Hinweis.

Step 1a — Put a missing session back (adopt)

A missing session's entry still exists, in the previous sidebar's folder. adopt copies it into the current one, which is all the sidebar needs: the entry reappears, opens its history and continues the original transcript (measured 2026-09-16, references/mechanics.md). Creating a session that never had an entry is still the user's part — for that there is no interface.

The order is not free, and each step exists because of what the step before it costs:

  1. Pick the ids with the user, in the chat. The report prints one per missing session. This skill never picks for them. After a switch the usual answer is "all of them": then the choice left is the two opt-ins, and each is the user's — --allow-shared-folder (a second entry on a folder that already has one) and --replace-older (overwrite this sidebar's entry of the same name when the other copy is newer). Ask about both; do not add them unasked.
  2. Run it inside the app anyway:
    bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot adopt <id>...
    bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot adopt --all [--allow-shared-folder] [--replace-older]
    
    It refuses — adopt writes only with the app closed — and that refusal is the point: it prints the open questions that quitting would turn into rejections, and the full command for afterwards, with --account, --org, the opt-ins and the ids expanded — --all included, so the run afterwards copies exactly the list the user saw. Relay both verbatim, together with the abgelehnt: lines above them: each names the session and the reason it stays out.

A conversation that sits in several other sidebars is taken from the copy with the newest activity; the refusal says which copy that is (Hinweis: … neueste von N Kopien). A live copy beats an archived one whatever their age, so adopt takes the copy the report showed (Hinweis: … archiviert, nicht genommen). Copies with the same file name and the same bytes count as one (Hinweis: … byte-gleich). Only live copies with equal activity that still differ stay mehrdeutig; the line under the refusal names the fields they differ in (unterscheiden sich in: …), so the user can decide which copy to take by hand. An entry the target sidebar holds archived does not make a session "already here": it is brought back, and an archived entry of the same file name is replaced without --replace-older (Hinweis: … ersetzt den archivierten Eintrag hier; the undo restores it).

--account/--org name the target sidebar — the account and org written into, normally the current ones. They are never where the sessions come from; that is already on each id. Passing the source account and org makes adopt refuse every id with "schon im aktuellen Konto", which reads like an entirely different failure. Passing the source account with a different org that account holds is worse: nothing refuses, and the copies land in that sibling org. (An org the account does not have is refused with "Org-Ordner fehlt".) This is why step 4 says to run the printed command unchanged. 3. Answer the listed questions, then quit right away. A question a session asks between the answer and the quit is rejected by the quit as well, and nothing afterwards can tell that rejection from an answer the user gave (EC-16, mechanics.md). 4. Run the printed command in Terminal.app, unchanged. Never reconstruct the flags: they are the --account/--org passed in step 2, or — when none were passed — this session's own entry, and a guessed account writes into someone else's sidebar. A sidebar is an account and an org: one login can hold several orgs, and each has its own sidebar. 5. Keep the undo command the run prints. It is the only line that names the backup directory. 6. Start the app. The adopted sessions are in the sidebar.

If the printed command still says the app is running although it is quit, something else inside Claude.app/Contents/ is alive. The browser extension's chrome-native-host, which Chrome or Comet keep running, no longer counts (BUG-310); any other process there does, on purpose.

To reverse a run:

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot adopt --undo <dir> --account <a> --org <o>

--account/--org name the same target sidebar the run itself was given — the account and org the entries were written into and are now removed from.

The undo removes only what that run created, and puts back from the backup what it replaced (--replace-older) — checked against the store, the flags and the backup, never against its own record. Restoring the whole backup directory by hand is a different act: it also rolls back everything the app itself wrote since, which is the user's decision to make.

For the sessions that are not adopted, hand over the folder and the opening message, one at a time.

Step 2 — Build the inventory (CLI conversations)

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot scan
bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot inventory

scan writes one entry per repository (the most recent conversation) to ~/.claude/session-boot.json. inventory records every conversation, with its topic, its closing state and its branch, to ~/.claude/session-inventory.json.

scan takes cli conversations only and names what it sets aside — a desktop conversation resumed in a terminal is the detour Step 1 exists to avoid; CLAUDE_BOOT_ALL_ENTRYPOINTS=1 includes every surface. inventory records every conversation of the manifest's repositories, whatever its surface, and marks each with its entrypoint. A manifest written before schema v2 is refused with a named reason rather than read: its dates come from file mtimes.

Step 3 — Report before acting

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot list

Present this to the user, and call out explicitly:

  • which repositories already show ● (a session is running — open will skip them)
  • which carry uncommitted files, since a resumed session may commit or push on its own
  • which repositories hold several recent conversations, because open restores only one per folder and may not pick the intended one

For the third point, show the alternatives rather than guessing:

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot sessions <repo-fragment>

Step 4 — Restore

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot open

One terminal window per repository, running claude --continue --remote-control <slug>. Repositories with a running session are skipped; CLAUDE_BOOT_FORCE=1 overrides that.

To restore a specific conversation instead of the newest one:

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot resume <id-prefix>

Step 5 — Handle a blocked terminal

open and resume drive Terminal.app through AppleScript. In a session without automation permission this fails. Do not retry it — fall back to printing the commands for the user to paste:

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot print

Step 6 — Suppress the trust prompt (optional, only on request)

A first CLI start in a folder asks whether the folder is trusted, and that prompt blocks the session before it registers with Remote Control — so it is invisible from another device.

bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot-trust
bash ${CLAUDE_PLUGIN_ROOT}/skills/session-restore/scripts/claude-boot-trust --apply

Without --apply it only reports. Never run --apply unprompted: it disables a security question for every repository in the manifest. Ask first, and say what it does.

Step 7 — Write a handoff (optional, for a session that will not be resumed as-is)

The report says which session is missing and where. When the work is complex enough that the next session should not have to reconstruct it — mid-review, uncommitted fix sets, decisions already taken — write a handoff instead of relying on the opening message alone.

  • Input: the conversations of that folder (all entrypoints), plus its git state.
  • Output: ~/.claude/handoffs/<project>-<YYYY-MM-DD>.md — outside any repository, because a handoff quotes conversation text verbatim and that must not land in a commit.
  • Shape: specs/203-session-restore-desktop-report/examples/handoff.md in the toolkit repo.
  • Optional by design: for a session that simply continues, the opening message is enough.

Every claim in a handoff carries its evidence or is marked unchecked, and owner decisions are recorded as decisions — the next session will act on what it reads.

What to warn about

  • Resumed sessions act immediately. They continue mid-task, up to and including a push. After open, advise a look at the windows before anything else.
  • --continue takes the newest conversation in a folder, which is not always the wanted one.
  • File modification times lie. A process writes on shutdown, so transcripts appear freshly touched. Every date this skill prints therefore comes from the last real message; list and the manifest carry it explicitly, and a pre-v2 manifest is refused rather than misread.
  • A folder that is not a repository reports "kein Repo", never the enclosing checkout's branch. Git walks up the tree, so the naive question answers for the parent — the report does not ask it.
  • Occupancy is resolved by working directory, not by the session slug. When it cannot be resolved (no lsof, unreadable process list) the folder counts as occupied, and open skips it. A needless skip costs a line; a second process on one transcript costs the conversation.
  • An adopted session sits in two sidebars. adopt copies, it never moves: the entry stays in the other account or org too. Opening the same session in both at once puts two processes on one transcript — work in one of them.
  • A start that produced no process is reported as FEHLSTART, separately from the started ones. osascript returns before a process exists, so a silent no-start used to count as success.

Reference

references/mechanics.md — why folder-keyed transcripts survive an account switch, what the manifest and inventory contain, and how the pieces fit together.

Next actions

  1. Run claude-boot desktop first and show the user its first line: the current account and the rule that chose it. If that account is wrong, stop — everything below it is inverted.
  2. Relay the OFFENE RÜCKFRAGEN section, if any, before the user quits or switches anything: quitting the app records each open question as a rejection.
  3. Agree which missing sessions to put back — or all, with the user's choice on --allow-shared-folder and --replace-older — then run claude-boot adopt <ids> or claude-boot adopt --all … inside the app and relay its refusal in full: the open questions it lists are lost to the quit, and the command it prints is the one to run afterwards, unchanged.
  4. After the user's Terminal run, keep the printed undo command in the conversation — it names the backup directory, and nothing else does.
  5. For each session that is not adopted, hand over its folder and its opening message, one at a time; the user creates the desktop session and pastes the message.
  6. Only for conversations that really are CLI work: show claude-boot list, let the user pick with claude-boot sessions <repo> where a repository has several, then claude-boot open or claude-boot resume <id> — or hand over claude-boot print when terminal automation is blocked.