Skip to main content

Lossless structural codec for Claude Code session JSONLs — portable conversation snapshots that move between devices, plus a built-in localhost chat surface that keeps your API key out of the browser.

Project description

claude-snap

Portable, lossless snapshots of Claude Code sessions.

--fork-session is a Claude Code primitive that already works locally: pick up where you left off in a new shell, with full context. claude-snap is the same primitive when the destination is a different device.

Pack a session JSONL on your laptop. Drop the artifact into a Claude chat on your phone. Keep the chain of ideas going. Read-only on a device that can't execute (the phone); read-write when reloaded into Claude Code on a device that has the repo.

This is not a memory layer. Not a search tool. Not a history browser. Not a summarizer. Not a markdown renderer. It's a codec.

What's in the box

claude-snap pack    session.jsonl  →  session.snap.jsonl   # compress
claude-snap unpack  session.snap.jsonl  →  session.jsonl   # restore (byte-identical events)
claude-snap stats   session.snap.jsonl                     # how much did we save

Zero runtime deps. Pure stdlib Python 3.9+. MIT.

Install

pip install claude-snap

As a hosted PWA (no install, works on phones)

claude-snap.app on GitHub Pages — drop a .snap.jsonl into a static, no-backend web app. The session unpacks in your browser, renders as a transcript, and you can ask follow-up questions using your own Anthropic API key. Works on iOS Safari, Android Chrome, desktop browsers. Installable as a PWA via Add to Home Screen. No App Store, no sideloading, no Apple developer subscription, no server backend.

Security: zero third-party dependencies, strict CSP locking outbound network to api.anthropic.com only, your key in localStorage only, your .snap.jsonl never uploaded anywhere. See docs/SECURITY.md for the full threat model.

If you don't trust GitHub Pages, the PWA runs locally with python3 -m http.server in the docs/ directory — same code, your own origin.

As a Claude Code plugin

This repo doubles as a Claude Code plugin. Install it from inside Claude Code to get four slash commands:

/plugin install achiii800/claude-snap
Command What it does
/snap Status: shows version, locates the most recent session JSONL, explains the rest.
/snap-pack [path] Pack a session JSONL into .snap.jsonl. Defaults to most recent.
/snap-stats [path] Compression stats on a packed file.
/snap-share [path] Pack and drop the artifact at $CLAUDE_SNAP_DROP_PATH (or ~/Documents/claude-snaps). Pair with iCloud Drive / Dropbox / Syncthing for hands-off cross-device handoff.

See PLUGIN.md for full plugin docs and the cross-device workflow.

For auto-pack-on-session-end, see examples/hooks/snap_pack_on_stop.py — a robust, opt-in Stop hook (also submitted upstream as a PR to anthropics/claude-code/examples/hooks/).

End-to-end UX

Today (manual, but works on day one):

  1. On your laptop, after a Claude Code session:
    claude-snap pack ~/.claude/projects/<encoded-path>/<uuid>.jsonl
    # → <uuid>.snap.jsonl
    
  2. Move the .snap.jsonl to your phone — AirDrop, iCloud Drive, email-to-self, gist, whatever moves a text file.
  3. On your phone, open Claude (mobile app or claude.ai), upload/paste the file, and tell Claude "this is a packed prior session — use it as context." Phone-Claude now has the full conversational chain, every file state laptop-Claude saw, every edit it made.

Reattaching to laptop:

claude-snap unpack session.snap.jsonl   # → session.jsonl, byte-identical events

Drop the unpacked JSONL back into ~/.claude/projects/<...>/ and Claude Code resumes against it.

Asymmetry: phone vs laptop

When you load a snapshot into a Claude chat on a device that doesn't hold the repo:

  • Can: ideate, discuss, suggest code, draft the next edits, plan, review what was done.
  • Can't: actually Edit / Write / Bash against the laptop's files.

Two reasons, the second is load-bearing:

  1. No executor connected to the laptop's filesystem.
  2. The snapshot itself has no execution surface. It's inert text. There's no protocol where phone-Claude could reach back through it to your laptop.

That's not a discipline imposed by the codec; it's what the medium is. The right architecture for cross-device mobility: by construction, the snapshot moves without dragging side-effect capability with it.

What "lossless" means here

Roundtrip property: unpack(pack(events)) produces the original event payloads byte-for-byte (modulo header/footer metadata).

The codec only ever replaces redundant events with refs. It never:

  • summarizes
  • truncates conversational turns
  • drops reasoning
  • collapses Edit/Write payloads
  • approximates anything

The redundancy it exploits is structural:

  1. Re-reading an unchanged file. If Claude does Read(foo.py) twice and nothing has Edit'd foo.py between them, the second read becomes a ref. Content preserved exactly once.
  2. Repeated identical tool output. If pytest -q returns the same bytes twice, the second result becomes a ref.
  3. Mutation invalidates dedup. If foo.py was Edit'd between two Reads, the second Read is not ref'd — its content has genuinely changed.

Conversational turns (you and Claude talking) are never dedup'd. That's the chain of ideas; you keep all of it.

What compression to expect

The codec only removes structural redundancy: repeat reads of unchanged files, repeat tool calls with identical output. If your session re-reads or re-runs a lot, expect a meaningful ratio. If every Read and every Bash is unique (common in modern Claude Code sessions, where Claude tends to retain what it has already seen), expect close to 1.0× — and that's correct, not a bug. The artifact is still portable and lossless, which is the point. Compression is incidental.

Where this sits in the ecosystem

The space around moving Claude Code sessions between machines has more tools than you'd think. They each solve a different shape of the problem:

Tool Approach Lossless? Byte-identical roundtrip? Codec? Single artifact? Network setup?
claude-mem AI summarization via agent-sdk no — lossy by design no no (summarizer) yes none
cctrace Markdown / XML render + verbatim JSONL copy partial — md/xml lossy, JSONL copy lossless the JSONL copy yes; the renders no no (transcriber) bundle dir none
claude-conversation-extractor Markdown export no — markdown loses JSON structure no no (extractor) yes none
session-roam Syncthing P2P sync of ~/.claude/projects/ yes (it's the same file) trivially no — file sync, structure-blind no — the live directory Syncthing peering, both nodes online
claude-handoff Git-based bundle with absolute-path scrubbing & secret redaction structurally yes no — paths intentionally rewritten partial — structural transformer yes (.claude-shared/ dir) shared git repo
claude-snap (this repo) sha256 content-hash refs in JSONL stream, mutation-aware structural dedup, per-event metadata patched on restore yes yes — regression-tested yes — true codec yes (single .snap.jsonl) none

Where claude-snap is unique:

  • It's the only one of these that's a true codec — encoding/decoding between two valid representations of the same data, not a render, not a summary, not a sync, not a transformer.
  • It produces a single portable artifact that roundtrips byte-for-byte. No peer setup, no shared repo, no online dependencies, no path rewriting.
  • It composes with everything else. cctrace bundles include a JSONL copy that ships unchanged through claude-snap. claude-handoff's normalized bundles can be packed before commit. session-roam's synced directory is the input.

If your problem is "I want both my machines online and the directory mirrored", use session-roam. If it's "I want to share sessions through a git repo, with secrets and absolute paths scrubbed", use claude-handoff. If it's "I want a single small file I can drop into another Claude chat or commit to a repo, byte-identical on roundtrip", use claude-snap.

Roadmap

  • v0.1.0 (you are here): the codec.
  • A Claude Code skill / configurable script that finds the active session, packs it, and drops the artifact at a configurable location (iCloud Drive folder, S3 bucket, etc.). Removes step 1 of the manual flow.
  • Schema adapters for other agentic CLIs (Cursor, Aider, Codex, Gemini CLI). The Event format is intentionally tool-agnostic.

The phone-side step (upload into a chat) remains an unsolvable workaround until Claude clients natively understand fork-session payloads. See the linked anthropics/claude-code issue.

Status

v0.1.0. Codec is correct on the roundtrip property and on the dedup heuristics tested in tests/test_codec.py. Schema normalization handles the dominant Claude Code JSONL shape; edge cases in unusual tool invocations (custom MCP tools, Task subagents) currently fall through to the generic META bucket and pass through unchanged — safe but uncompressed.

Contributing

Issues and PRs welcome. Particularly interested in:

  • Dedup heuristics for tools we don't currently special-case
  • Schema adapters for other agentic CLIs
  • A laptop-side packing skill / hook for Claude Code

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_snap-0.3.0.tar.gz (46.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_snap-0.3.0-py3-none-any.whl (42.1 kB view details)

Uploaded Python 3

File details

Details for the file claude_snap-0.3.0.tar.gz.

File metadata

  • Download URL: claude_snap-0.3.0.tar.gz
  • Upload date:
  • Size: 46.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for claude_snap-0.3.0.tar.gz
Algorithm Hash digest
SHA256 bb72fd8a476eaa04326f1896e7072926d518b6cd07d61c4874310cc65a3e8064
MD5 d8512a74b33faef73e86cb4f4e6df554
BLAKE2b-256 37ae41755302ecc7aadd117eeb8f0f8a05965f6cd53275ad990c34d23b8968ac

See more details on using hashes here.

File details

Details for the file claude_snap-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: claude_snap-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 42.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for claude_snap-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dcc26b42d2d6949bd8c3effad476df076bcce86501f2967d3dedc89a69459884
MD5 c460c90dedbb33802d88dbe331e41189
BLAKE2b-256 c8ad0d8d7e0c08b9225c89c91fe40be30ecfa1e973fc7791b35e582b14cfe285

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page