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.
claude-hop upgrade Upgrade claude-hop itself to the latest release (works for uv, pipx, and pip installs).

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

Syncing a single project

push, pull, and diff take optional project selectors — by default they sync everything:

claude-hop push .                          # just the project you're standing in
claude-hop pull ~/work/webshop             # one project, by path
claude-hop push -home-alice-work-webshop   # by encoded name, as shown in `status`
claude-hop diff . ~/work/blog              # several at once

When a selection is given, only those projects sync and the optional [sync] extras (history/agents/skills) are skipped.

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.2.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.2.0-py3-none-any.whl (23.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: claude_hop-0.2.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.2.0.tar.gz
Algorithm Hash digest
SHA256 1b4bd233efc0a14aa4d07ea91cbd2543d452a1328ef9d098f0338d14d575a234
MD5 ede6d3b8803628d583ef38f4cdf5d848
BLAKE2b-256 f12e20917e150bb892013c03d004a6f2cc10caf0ddc154cd698181c88af5fbe7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: claude_hop-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 23.7 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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e9b89c6008e11e2b6faf2af5983e531973c4cd1ca78925a31437ac48c6dd3355
MD5 95d9237c0a2df970998759b405e12b6b
BLAKE2b-256 355a74f4b537618ceef7a08c1c4d67a52fec977df87e6b3ee4a268d337deeacc

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