Skip to main content

Claude Session Teleporter

tests PyPI license: MIT python: 3.10+ dependencies: none

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:

  1. 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.

  2. 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.

  3. WSL is a third place entirely. A distro has its own ~/.claude with 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 be 01812872… in your work org and 4b57c823… 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

  1. Signed in, from ~/.claude.json. Authoritative: the only partition the app will show you.
  2. Last active: most recent session activity on disk.
  3. 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 --force flag.
  • 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: A archived, ! transcript missing, R ssh/remote, W runs in WSL.
  • Sessions flagged R are 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.
  • eject only 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/c checkout 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)

Source distribution for claude-session-teleporter 1.1.0
File Size Uploaded
claude_session_teleporter-1.1.0.tar.gz 53.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for claude-session-teleporter 1.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

1.3.0

2 release files

1.2.0

2 release files

This release

1.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page