Skip to main content

Sync Claude Code sessions between two machines over SSH — no cloud.

Project description

claude-hop — sync Claude Code sessions between machines

Sync Claude Code sessions between two machines over SSH — no cloud storage, just rsync between your own boxes. Start a session on your Linux desktop, claude --resume it on your Mac (or the other way round).

Quickstart (30 seconds)

uv tool install claude-hop        # or: pipx install claude-hop
claude-hop init                   # asks for the SSH host, detects the rest
claude-hop push                   # then `claude --resume` on the other machine

No uv? One-liner that installs it first:

curl -sSL https://raw.githubusercontent.com/SeiduTakizawa/Claude-hop/main/install.sh | bash

Requirements: Python ≥ 3.11 on this machine; sshd and rsync on the other one (key-based SSH auth recommended).

Supported platforms

Any pair of Unix-shaped machines, in either direction: Linux ↔ macOS, Linux ↔ Linux, macOS ↔ macOS. The remap isn't tied to /home vs /Users — it maps your home here to your home there, whatever they are (identical homes on both sides work too; the remap just becomes a no-op and you keep the merge/safety machinery).

Windows is supported via WSL: inside WSL you have a real Linux home, rsync, and sshd, so claude-hop works there like on any Linux box — it syncs the Claude Code you run inside WSL. Native (non-WSL) Windows is not supported: Claude Code encodes C:\ project paths differently and there's no native rsync. If you need it, open an issue.

The problem it solves

Claude Code stores sessions in ~/.claude/projects/, indexed by the absolute path of the project with every non-alphanumeric character replaced by a dash:

/home/alice/work/webshop   →   ~/.claude/projects/-home-alice-work-webshop/

The absolute paths also appear inside the session JSONL files — cwd fields, file paths in tool calls. So copying the files verbatim from a Linux box (/home/alice) to a Mac (/Users/alice) produces sessions that claude --resume can never find: the directory name encodes the wrong path and the transcript still references the wrong filesystem.

claude-hop rewrites both during sync — directory names and JSONL contents — mapping your local home to the remote home (auto-detected at init), in whichever direction you sync. The rewrite is boundary-aware (/home/al never matches inside /home/alice) and round-trip safe: push then pull reproduces your files byte for byte.

Commands

Command What it does
claude-hop init Interactive setup: SSH host, connection test, remote home auto-detection, rsync checks.
claude-hop status Table of local projects, session counts, and the remapped name each gets on the remote; flags ambiguous mappings.
claude-hop diff What would sync, in both directions, without writing anything.
claude-hop push Send local sessions to the remote machine.
claude-hop pull Fetch remote sessions and merge them in.
claude-hop doctor Environment checks: SSH, rsync versions, config, running Claude Code, stale staging dirs.

push and pull accept --dry-run (-n), --yes (-y), and --force.

Safety model

  • Merge, never clobber. rsync runs with -u (newer file wins) and never with --delete — a sync can only add or update sessions, never remove.
  • Staging, not in-place. Remapping happens in a temp dir; ~/.claude/projects/ is only ever touched by the merge itself.
  • Refuses to run alongside Claude Code. Sessions are flushed to disk on exit, so syncing mid-session would miss your latest work. Override with --force if you know what you're doing.
  • First-pull backup. The first pull that would merge into existing local sessions offers a tarball backup (~/.claude/backups/) before touching anything.

Configuration

~/.config/claude-hop/config.toml (written by init, $XDG_CONFIG_HOME honoured):

[remote]
host = "mac.local"          # SSH alias or hostname; "" = a local directory
home = "/Users/alice"       # auto-detected at init

[sync]
include_history = false     # also sync ~/.claude/history.jsonl (remapped)
include_agents = false      # also sync ~/.claude/agents/ (verbatim)
include_skills = false      # also sync ~/.claude/skills/ (verbatim)

[mappings]
# For projects that live at *different relative paths* on the two machines.
# Specific mappings win over the generic home remap.
"/home/alice/work/webshop" = "/Users/alice/projects/webshop"

claude-hop status shows exactly how every project will map; if one looks wrong, add it under [mappings].

Limitations worth knowing

  • Sessions whose conversation text literally mentions the other machine's home path (e.g. a transcript discussing /Users/alice/... while on the Linux box) can't be rewritten losslessly — path rewriting is text-level by design, so such mentions get remapped too.
  • Session files that aren't valid UTF-8 are copied verbatim without remapping rather than risk corrupting them.
  • On macOS 15+, the bundled rsync is openrsync; it works, but if you hit option errors, brew install rsync.

Development

uv sync           # install with dev dependencies
uv run pytest     # unit + integration tests (integration needs rsync)
uv run ruff check .
make e2e          # full two-container SSH round trip (needs Docker)

The e2e rig (e2e/) spins up two containers — one with a Linux-style home, one macOS-style — wires SSH key auth between them, and drives the real CLI through push, modify-on-the-other-side, and pull.

License

MIT — see LICENSE.

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_hop-0.1.0.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

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

claude_hop-0.1.0-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file claude_hop-0.1.0.tar.gz.

File metadata

  • Download URL: claude_hop-0.1.0.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for claude_hop-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7ffc3dda2788196dadef92b7692bc54f836de9eb978854b0dbc2038704362af1
MD5 4307b2e1b73979a8fa4a173470559453
BLAKE2b-256 b41bbe3ef6265781dbd766fc34ec4ced5f56808bf16fe4897c00ed79539c9934

See more details on using hashes here.

File details

Details for the file claude_hop-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: claude_hop-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for claude_hop-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f422fb7fa2a0d452efa3ed94cde11d8834e90a8d8e0a8ccfd1c2b69a262ec970
MD5 53acca96d8744b3e2103ae0f0f563c7c
BLAKE2b-256 79269e13c1a17f7be46b8a006f14749114fbf882cfb6ce02d1c01dfd123e12f8

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