Claude Session Teleporter
Oh, you can take it with you. Out of quota, not out of context.
Your Claude Code sessions aren't gone. They're under the other org.
Switch orgs or accounts in the Claude desktop app and your sessions vanish from
the list. Run claude inside WSL and the Windows app never shows those sessions
at all. Either way, every session is still on your disk. This tool finds them
and puts them back in the app, so you can pick up where you left off, even when
the org you were using has run out of quota.
Quick start
With uv:
# every org on this machine, its sessions, and how much quota it has left (read-only)
uvx --from claude-session-teleporter claude-sessions partitions
# install it, then copy the other org's sessions into the one you are signed into
uv tool install claude-session-teleporter
claude-sessions copy # dry run: prints the plan, writes nothing
claude-sessions copy --apply # do it
With more than two orgs, copy asks you to pick one with --from, using a name
or UUID prefix from the partitions list. After --apply, switch accounts in
the app (or restart it). The app caches its session list and only re-reads
the disk when you do.
No uv? The tool is one standard-library file you can download and run.
What it looks like
This is real output from a demo store: one login with two orgs, work and
personal. You are signed into personal, so the app shows only its one
session. work holds two more, and it has 2% of its quota left.
$ claude-sessions partitions
PARTITION ORG UUID ACCOUNT ALL UNARCH DEL LAST ACTIVITY QUOTA LEFT CONNECTORS
────────── ──────────────────────────────────── ──────── ─── ────── ─── ──────────────── ──────────────── ───────────────────────
work 3c426532-1eaa-4e6f-93c1-4d30abca7b89 1eb44d48 3 2 0 2026-09-27 17:31 ░░░░░░░░░░░░ 2% Datadog, Linear, Sentry
● personal 762f7f2a-1cab-4c8a-98d1-d53bf5e8872c 1eb44d48 1 1 0 2026-09-27 16:31 ███████████░ 88% Linear, Sentry
A dry run shows exactly what would happen, including the connector fixes a plain file copy would miss (see below):
$ claude-sessions copy --from work
┌────────────────────────────────────┐ ┌────────────────────────────────────┐
│ SOURCE │ │ TARGET ● signed in │
│ ────────────────────────────────── │ │ ────────────────────────────────── │
│ work │ │ personal │
│ org 3c426532-1eaa-4e6f-93c1-4d30… │ │ org 762f7f2a-1cab-4c8a-98d1-d53b… │
│ acct 1eb44d48… │ │ acct 1eb44d48… │
│ │ ═══ 2 ═══▶ │ │
│ 3 sessions · 2 unarchived │ │ 1 sessions · 1 unarchived │
│ last active 2026-09-27 17:31 │ │ last active 2026-09-27 16:31 │
│ connectors Datadog, Linear, Sent… │ │ connectors Linear, Sentry │
│ │ │ │
│ quota left ░░░░░░░░░░░░░░░░ 2% │ │ quota left ██████████████░░ 88% │
└────────────────────────────────────┘ └────────────────────────────────────┘
ACTION ID LAST ACTIVITY TITLE BRANCH/DIR FIX
─ ────────────── ──────── ──────────────── ──────────────────────── ────────── ───
✓ COPY 5651c527 2026-09-27 17:31 Fix flaky auth test api 7
✓ COPY 7c0ffee0 2026-09-27 13:31 Migrate billing webhooks api 6
✗ skip: archived 0ddba11a 2026-09-24 00:31 Old spike api
port fixes applied (FIX column counts these per session):
2x remapped Linear tool keys: 01812872 ▶ 4b57c823
2x remapped Sentry tool keys: e5c4f439 ▶ be036bca
2x dropped stale tool keys for Datadog (9d1a0b7e)
2x remapped Linear: 01812872 ▶ 4b57c823
2x remapped Sentry: e5c4f439 ▶ be036bca
2x dropped Datadog (9d1a0b7e) - not present in personal
1x cleared previous crash state
2 COPY 1 skip: archived
transcripts are shared on disk - none are copied or duplicated.
DRY RUN. Nothing written. Re-run with --apply to copy 2 session(s).
Apply it, and the signed-in org has all three:
$ claude-sessions copy --from work --apply
...
✓ 5651c527 Fix flaky auth test
✓ 7c0ffee0 Migrate billing webhooks
Copied 2 session(s) into personal (762f7f2a-1cab-4c8a-98d1-d53bf5e8872c).
The app caches its session list in memory. Switch accounts in the app to
force a reload - faster than restarting it, and it works just as well.
$ claude-sessions sessions -p personal
personal (762f7f2a-1cab-4c8a-98d1-d53bf5e8872c) ● signed in
─────────────────────────────────────────────────────────────
FLG ID LAST ACTIVITY TITLE BRANCH/DIR WSL
─── ──────── ──────────────── ──────────────────────── ────────── ───
5651c527 2026-09-27 17:31 Fix flaky auth test api
b16b00b5 2026-09-27 15:31 Blog post draft blog
7c0ffee0 2026-09-27 13:31 Migrate billing webhooks api
(3 unarchived of 3) flags: A=archived !=transcript missing R=ssh/remote W=runs in WSL
Two reasons a session goes missing
If you use Claude Code under more than one organization, say a work org and a
personal Max plan, the desktop app shows you only the partition you are
currently signed into. Sessions from your other org are still on disk. They
are just not shown. copy puts them into the partition you are signed into.
Sessions you started by running claude inside WSL are invisible for a
different reason: they have no desktop metadata at all, so no org you sign
into will ever show them. That is a separate axis, with a separate fix.
| axis | what differs | command |
|---|---|---|
| partition | account/org: same machine, same transcripts | copy |
| host | WSL vs Windows: different filesystem, different install | adopt / eject |
How Claude Code stores sessions
Everything is local. Nothing about a session lives in the cloud, and nothing syncs between machines.
| What | Where |
|---|---|
| Session metadata | %APPDATA%/Claude/claude-code-sessions/<accountUuid>/<orgUuid>/local_<id>.json |
| Transcript | ~/.claude/projects/<encoded-cwd>/<cliSessionId>.jsonl |
| Deletion tombstone | .../<orgUuid>/deleted_<id> |
Three consequences drive this whole tool:
-
Metadata is partitioned by account and org; transcripts are not. Two orgs under the same login get separate metadata folders but share one transcript pool. So moving a session between partitions means copying a small JSON file. The conversation itself never moves and is never duplicated.
-
The app caches its session index in memory. Files written while it is running are not noticed; there is no filesystem watcher and no reload hook. Make it re-read disk afterwards. Switching accounts in the app is enough, and is faster than restarting it. Restarting works too.
-
WSL is a third place entirely. A distro has its own
~/.claudewith its own transcripts and no metadata directory, so sessions started there are invisible to the app in every org. See WSL.
The part that makes a naive cp wrong
Session metadata is not org-portable as-is. Two fields are org-scoped:
remoteMcpServersConfig: the same connector has a different UUID in each org. Linear might be01812872…in your work org and4b57c823…in your personal one.enabledMcpTools: keyed"<serverUuid>:<toolName>", so every one of those keys inherits the stale UUID.
Copy the file as-is and the session lands pointing at connectors that do not exist in the destination org. This tool remaps them by connector name, builds the destination's name→UUID map from its own native sessions, and drops connectors the destination org does not have. It also clears the source's stale runtime state. From the dry run above:
2x remapped Linear: 01812872 ▶ 4b57c823
2x remapped Sentry: e5c4f439 ▶ be036bca
2x dropped Datadog (9d1a0b7e) - not present in personal
1x cleared previous crash state
Install
Single file, standard library only, Python 3.10+. No dependencies, ever.
That is a design constraint, not an accident, and it is
enforced by tests:
the tool may not import anything outside the standard library, the wheel
carries no Requires-Dist, and CI runs the bare file on an interpreter with
nothing installed.
With uv (puts claude-sessions on your PATH):
uv tool install claude-session-teleporter # or: pip install claude-session-teleporter
claude-sessions --help
Or run it once without installing:
uvx --from claude-session-teleporter claude-sessions --help
For the latest main, install from GitHub instead:
uv tool install git+https://github.com/aviadr1/claude-session-teleporter.
As a single file. In bash (WSL, macOS, Linux, Git Bash):
mkdir -p ~/.claude/tools
curl -o ~/.claude/tools/claude_sessions.py \
https://raw.githubusercontent.com/aviadr1/claude-session-teleporter/main/claude_sessions.py
python3 ~/.claude/tools/claude_sessions.py --help
In PowerShell:
New-Item -ItemType Directory -Force "$HOME\.claude\tools" | Out-Null
curl.exe -o "$HOME\.claude\tools\claude_sessions.py" `
https://raw.githubusercontent.com/aviadr1/claude-session-teleporter/main/claude_sessions.py
python "$HOME\.claude\tools\claude_sessions.py" --help
The PyPI package is claude-session-teleporter, and the command it installs
is claude-sessions. Do not install claude-sessions from PyPI: that name
belongs to an unrelated project.
Windows is the primary target (that is where the paths were verified). macOS and
Linux paths are implemented but untested. Set CLAUDE_SESSIONS_ROOT to
override if detection is wrong.
Usage
The examples use claude-sessions, the installed command. With the single
file, run python claude_sessions.py instead.
Start here. A full walkthrough, printed to your terminal:
claude-sessions guide # the whole story, start to finish
claude-sessions --help # traditional help, with examples
claude-sessions copy -h # per-command help, incl. safety and port fixes
Then the commands themselves:
# what partitions exist, and how much plan quota each has left
claude-sessions partitions
# name one so you stop reading UUIDs
claude-sessions label 3c426532 work
# unarchived sessions (flags: A=archived !=transcript missing R=ssh/remote W=WSL)
claude-sessions sessions -p work
claude-sessions sessions -p work --all
# which partition is "active"? three defensible answers
claude-sessions active
# dry run: copy everything unarchived from work into the signed-in partition
claude-sessions copy --from work
# just one session, then actually do it
claude-sessions copy --from work -s 5651c527 --apply
copy prints a direction diagram, the per-session plan, and the port fixes it
would apply, then stops. Nothing is written without --apply.
WSL
# this machine, plus every WSL distro with a Claude Code install
claude-sessions hosts
# what is in there. ORIGIN separates two things that share a directory:
# cli you ran `claude` at a terminal inside the distro
# desktop the Windows app started it, using the distro as its environment
claude-sessions sessions -H wsl:Ubuntu
claude-sessions sessions -H wsl:Ubuntu --cli -n 10
# make WSL CLI sessions visible in the app (dry run, then for real)
claude-sessions adopt --from wsl:Ubuntu
claude-sessions adopt --from wsl:Ubuntu -s 3f8137a7 --apply
# the other direction: hand a Windows session to the CLI inside WSL
claude-sessions eject 8aef0655 --to wsl:Ubuntu --apply
adopt writes only metadata. The transcript stays inside the distro and is
never copied or rewritten. The app runs claude in WSL against the file that
is already there, so the terminal and the app are the same session rather
than two forks of it. Three fields do the work:
"wslConfig": {"distro": "Ubuntu"},
"cwd": "/home/you/projects/repo",
"sshRemoteTranscriptPath": "/home/you/.claude/projects/-home-you-projects-repo/<id>.jsonl"
eject is the one command that forks. A Windows session's working directory
has to be reachable from the distro (C:\you\repo is visible there as
/mnt/c/you/repo, the same files over drvfs), so it writes a second transcript
with the cwd rewritten, and prints the wsl … claude --resume line. The
Windows session keeps its own. Resume in one place or the other, never both.
Let Claude drive it
claude-sessions skill # print the SKILL.md
claude-sessions skill --install # write it to ~/.claude/skills/
Installs a Claude Code skill that teaches Claude when this applies (sessions
"missing" after an org switch), the storage model, the dry-run-first workflow,
and the cache-reload caveat, so it stops guessing and stops reaching for cp.
The skill records the command you ran it with, so install it from the copy you
will keep (uv tool install or the downloaded file), not from a one-off uvx.
Restart Claude Code afterwards to pick it up.
"Active" is ambiguous, so active gives you all three
- Signed in, from
~/.claude.json. Authoritative: the only partition the app will show you. - Last active: most recent session activity on disk.
- Most quota, parsed from
plan-usage-history.json, which records five-hour (fh) and seven-day (sd) usage percentages per org. Often the real reason you switched orgs in the first place.
Safety
Every rule below is stated formally in
INVARIANTS.md
and enforced by a named test in tests/. The invariant doc lists the test that
proves each one.
copy is built so that a mistake cannot cost you a session:
- Never overwrites. Uses exclusive file creation; there is deliberately no
--forceflag. - Never resurrects a deletion. Skips any session with a
deleted_<id>tombstone in the destination. - Never touches the source. Copy only; the source partition is opened read-only.
- Never duplicates transcripts. They are shared by design.
- Refuses sessions with no transcript on disk. There would be nothing to continue.
- Refuses cross-account copies unless you pass
--allow-cross-account. - Dry-run by default.
adopt inherits all of that, never writes into the distro, derives its desktop
UUID from the WSL session id so re-running is idempotent, and resets
permissionMode to auto: a session this tool created must not arrive
pre-authorised to skip tool approval.
eject is the exception, and says so loudly: it writes a second transcript.
Imports are recorded in ~/.claude/session-copy-ledger.json so that copied
sessions are excluded when building the destination's connector map. That
ledger cannot know about a copy made by hand or made before it existed, so the
map is additionally decided by majority: one stray import cannot redefine an
org's connectors, and a disagreement is reported rather than silently resolved.
Development
uv handles the dev environment. The tool itself
still has no dependencies. pytest lives in a dependency-group, so it is never
installed for users.
uv run pytest # the suite; uv creates the env on first run
uv run pytest -m drift # only the checks against your real session store
uv build # wheel + sdist
uv run claude-sessions --help
tests/test_safety.py, tests/test_formats.py and tests/test_packaging.py
pin every invariant in
INVARIANTS.md
against fixtures.
tests/test_format_drift.py re-checks the reverse-engineered formats against
whatever real Claude Code store is on the machine, and skips cleanly when there
is none. CI stays green on a bare runner while your own machine acts as the
canary. If Anthropic changes a format, that file fails first. It is not
theoretical: it is how the connector-map bug in copy was found.
The suite is checked by mutation, not coverage: deliberately break an invariant and a named test must go red. If you add one, break it first.
Caveats
- Flags in
sessions:Aarchived,!transcript missing,Rssh/remote,Wruns in WSL. - Sessions flagged
Rare ssh/remote; they resume only if that host is reachable. - The destination org needs its own native session before the connector map can be built. With none, MCP config is stripped and the app repopulates it.
ejectonly works for a working directory WSL can reach, meaning a drive path. A UNC path has no spelling inside the distro and is refused rather than guessed at.- A
/mnt/ccheckout is the same files as the Windows one, reached over drvfs: slower, with Windows line endings and file modes. - Reverse-engineered from on-disk formats, which Anthropic can change without notice. Verified against Claude Code 2.1.233 on Windows 11 with WSL2.
- Not affiliated with Anthropic.
Coda
They said that what you leave behind is lost, that each account must keep its own domain, that signing out is simply what it cost, and two-and-twenty threads went down the drain.
But nothing left. It never touched a cloud. It sat in JSON, filed beneath a name; no window showed it, nothing spoke aloud — invisible, and present all the same.
The transcript never moves; the pointer flies. But copy plain, and half of you stays back: a ghost that calls its tools, and none replies, and names you knew fall silent through the crack.
So teleport, and let the app restart — oh, you can take it with you. Every part.
License
MIT
Metadata
Release files for claude-session-teleporter 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| claude_session_teleporter-1.1.0.tar.gz | 53.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claude_session_teleporter-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 90.6 kB
Release files / claude_session_teleporter-1.1.0.tar.gz
| Download URL | claude_session_teleporter-1.1.0.tar.gz |
|---|---|
| Size | 53.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1f7688054427a62c5e7eef92438c6cb521e7a5241abdf5b45ecc3d61e4a06876
|
|
BLAKE2b-256 checksum How to use checksums |
b354a18650ca6f43abbae7609c8a2e017294b8420cf43abe6ced46cabe469099
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency logRelease files / claude_session_teleporter-1.1.0-py3-none-any.whl
| Download URL | claude_session_teleporter-1.1.0-py3-none-any.whl |
|---|---|
| Size | 36.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3db3c20cd9c1c37cf4c4606b86607f20feaf7b8406445a9116a9fb8b58c33edb
|
|
BLAKE2b-256 checksum How to use checksums |
e606646350672002a831ab43130dd6235181869ebd17ab6c3cd3397f764adb63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency log