@askalf/claude-sync 0.0.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -38
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +332 -35
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +31 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -2
- package/dist/config.js.map +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +2 -1
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/project.d.ts +39 -8
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +98 -15
- package/dist/project.js.map +1 -1
- package/dist/session.d.ts +17 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +25 -12
- package/dist/session.js.map +1 -1
- package/dist/ssh.d.ts +66 -0
- package/dist/ssh.d.ts.map +1 -0
- package/dist/ssh.js +216 -0
- package/dist/ssh.js.map +1 -0
- package/dist/transport.d.ts +40 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +63 -0
- package/dist/transport.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @askalf/claude-sync
|
|
2
2
|
|
|
3
|
+
> _claude-sync — own your sessions — move Claude Code sessions across machines. Part of **[Own Your Stack](https://github.com/askalf)** — own your AI infrastructure instead of renting it by the token._
|
|
4
|
+
|
|
3
5
|
> Sync Claude Code sessions across machines. Pack a session into a portable `.ccsync` file, ship it via Dropbox / iCloud / Syncthing / a USB stick, unpack on the other side. Path-hash mismatches solved via git-remote-url as the canonical project key.
|
|
4
6
|
|
|
5
7
|
```bash
|
|
@@ -19,8 +21,8 @@ Claude Code stores sessions locally at `~/.claude/projects/<encoded-cwd>/<sessio
|
|
|
19
21
|
|
|
20
22
|
`claude-sync` solves both:
|
|
21
23
|
|
|
22
|
-
- **Canonical project key** = `git:<remote
|
|
23
|
-
- **
|
|
24
|
+
- **Canonical project key** = `git:<host>/<owner>/<repo>`, derived from the git remote. Same logical repo → same key, regardless of where it's checked out or which protocol you cloned with — SSH and HTTPS remotes normalize to the same key. (Falls back to `name:<basename>` when there's no git remote.)
|
|
25
|
+
- **You choose the cadence.** `push` from machine A and `pull` on machine B by hand, or run `claude-sync watch` to push-on-change and pull-on-interval automatically. The transport is either a local directory you nominate (Dropbox / iCloud / Syncthing / a USB stick) **or a directory on a remote host over SSH** (a dev box). Each machine writes its own file under its own machine name, so two machines pushing the same session don't collide.
|
|
24
26
|
|
|
25
27
|
## Use it
|
|
26
28
|
|
|
@@ -57,12 +59,80 @@ claude --resume sess-abc123
|
|
|
57
59
|
|
|
58
60
|
That's the whole loop.
|
|
59
61
|
|
|
62
|
+
### Or let it run: watch mode
|
|
63
|
+
|
|
64
|
+
Tired of remembering to `push` and `pull`? Run a daemon per project:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd ~/code/myapp
|
|
68
|
+
claude-sync watch
|
|
69
|
+
# claude-sync watch — desktop
|
|
70
|
+
# project: git:github.com/you/myapp
|
|
71
|
+
# cwd: /Users/you/code/myapp
|
|
72
|
+
# syncDir: /Users/you/Dropbox/claude-sync
|
|
73
|
+
# every 10s · push most-recent changed session · pull all projects
|
|
74
|
+
# Ctrl-C to stop.
|
|
75
|
+
#
|
|
76
|
+
# ↑ pushed sess-abc123 (142 lines)
|
|
77
|
+
# Imported sess-def456 from laptop → …/sess-def456.jsonl
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Every interval (default 10s, `--interval <seconds>`) it pushes the active
|
|
81
|
+
session whenever it has grown and pulls anything newer from your other
|
|
82
|
+
machines. It watches the **most-recent** session by default; pass a session-id
|
|
83
|
+
to pin one, or `--all` to push every changed session in the project. `--once`
|
|
84
|
+
runs a single cycle and exits — handy from `cron`. Ctrl-C stops cleanly.
|
|
85
|
+
|
|
86
|
+
It's still poll-based, not real-time co-editing: don't run the *same* session
|
|
87
|
+
live on two machines at once (see [Concurrency](#concurrency)). Watch is for
|
|
88
|
+
"pick up where the other machine left off without thinking about it."
|
|
89
|
+
|
|
90
|
+
### Sync over SSH (no Dropbox needed)
|
|
91
|
+
|
|
92
|
+
If you run Claude Code on a **dev server** and on a **laptop**, point the
|
|
93
|
+
transport straight at a directory on the server — no third-party sync folder in
|
|
94
|
+
the middle:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# On the laptop: the syncDir lives on the remote box, reached over SSH.
|
|
98
|
+
claude-sync init --ssh me@devbox:/home/me/claude-sync --name laptop
|
|
99
|
+
claude-sync doctor # live connectivity + remote-dir check
|
|
100
|
+
|
|
101
|
+
# On the server: a plain filesystem transport pointed at the same dir.
|
|
102
|
+
claude-sync init /home/me/claude-sync --name devbox
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Now `push` / `pull` / `watch` on the laptop shuttle sessions to and from the
|
|
106
|
+
server with no manual `scp`. Options: `--port <n>`, `--identity <keyfile>`.
|
|
107
|
+
It uses your system `ssh`, honors your `~/.ssh/config`, runs in `BatchMode`
|
|
108
|
+
(so it fails fast instead of prompting), and multiplexes connections on
|
|
109
|
+
macOS/Linux so a `pull`'s round-trips share one SSH session.
|
|
110
|
+
|
|
111
|
+
> The remote layout is identical to the filesystem transport, so the server's
|
|
112
|
+
> `fs` transport and the laptop's `ssh` transport — both pointed at
|
|
113
|
+
> `/home/me/claude-sync` — interoperate. The remote host needs a POSIX shell
|
|
114
|
+
> with `find`/`tar` (any Linux box).
|
|
115
|
+
|
|
116
|
+
### Starting on a fresh clone
|
|
117
|
+
|
|
118
|
+
`pull` can only deliver a session to a directory it knows about. On a machine
|
|
119
|
+
that has never had a session for this project yet, register the directory once:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
cd ~/code/myapp
|
|
123
|
+
claude-sync register # binds this cwd to the project key — no session needed
|
|
124
|
+
claude-sync pull # now resolves
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
(`watch` registers automatically on start, and `import … --cwd <path>` registers
|
|
128
|
+
the target, so you usually only need `register` for a manual `pull`-first flow.)
|
|
129
|
+
|
|
60
130
|
## How path resolution works
|
|
61
131
|
|
|
62
132
|
When you `push` from a project with a git remote, claude-sync:
|
|
63
133
|
|
|
64
134
|
1. Reads `git remote get-url origin` from your cwd.
|
|
65
|
-
2.
|
|
135
|
+
2. Canonicalizes it into a project key: `git:github.com/you/myapp` (SSH and HTTPS remotes land on the same key).
|
|
66
136
|
3. Registers the local cwd under that key in `~/.claude-sync/projects.json`.
|
|
67
137
|
4. Writes the `.ccsync` into `<syncDir>/<encoded-key>/<session>-<machine>.ccsync`.
|
|
68
138
|
|
|
@@ -71,36 +141,37 @@ When you `pull`, the receiving machine:
|
|
|
71
141
|
1. Scans `<syncDir>/` for project subdirs.
|
|
72
142
|
2. For each, looks up the encoded key in its own `projects.json`.
|
|
73
143
|
3. If a local cwd is registered for that key, installs the session there.
|
|
74
|
-
4. If not — claude-sync hasn't seen this project on this machine yet — it skips with a warning.
|
|
144
|
+
4. If not — claude-sync hasn't seen this project on this machine yet — it skips with a warning. Run `claude-sync register` in the cwd once (binds the key, no session needed), or `claude-sync import <file> --cwd <path>` to install a specific file (which also registers the directory).
|
|
75
145
|
|
|
76
|
-
In practice: clone the repo on the new machine, `cd` in,
|
|
77
|
-
|
|
78
|
-
A cleaner way: just run any `claude` command in the cwd once, then `claude-sync export` (which registers without pushing).
|
|
146
|
+
In practice: clone the repo on the new machine, `cd` in, `claude-sync register`, then `claude-sync pull`. Or just start `claude-sync watch`, which registers on launch.
|
|
79
147
|
|
|
80
148
|
## Concurrency
|
|
81
149
|
|
|
82
|
-
|
|
150
|
+
`watch` automates *when* to push and pull, but it's still poll-based, not a locking protocol. If two machines edit the **same** session concurrently:
|
|
83
151
|
|
|
84
152
|
- Two `.ccsync` files end up in `<syncDir>/<project>/`, named with each machine's name.
|
|
85
153
|
- The receiving `pull` skips files older or shorter than the local copy.
|
|
86
154
|
- Newer/longer files install with `--overwrite`. The losing machine's edits become a `<session-id>-copy.jsonl` (you can manually inspect + reconcile).
|
|
87
155
|
|
|
88
|
-
|
|
156
|
+
So: use `watch` to hand a project *back and forth* between machines hands-free; don't drive the same live session from two machines at once. A lock-file protocol for true real-time co-editing is a possible future addition.
|
|
89
157
|
|
|
90
158
|
## CLI reference
|
|
91
159
|
|
|
92
160
|
```text
|
|
93
161
|
claude-sync init <syncDir> [--name <machine>]
|
|
162
|
+
claude-sync init --ssh <[user@]host:/path> [--port <n>] [--identity <keyfile>] [--name <machine>]
|
|
94
163
|
claude-sync list
|
|
164
|
+
claude-sync register [cwd]
|
|
95
165
|
claude-sync export [session-id] [-o <file>]
|
|
96
166
|
claude-sync import <file> [--cwd <path>] [--overwrite]
|
|
97
167
|
claude-sync push [session-id]
|
|
98
168
|
claude-sync pull
|
|
169
|
+
claude-sync watch [session-id] [--interval <seconds>] [--all] [--once]
|
|
99
170
|
claude-sync doctor
|
|
100
171
|
claude-sync --help / --version
|
|
101
172
|
```
|
|
102
173
|
|
|
103
|
-
`doctor` prints config, machine name, registered projects, and warns if any registered cwd no longer exists on disk.
|
|
174
|
+
`doctor` prints config, machine name, registered projects, and transport peers — and warns if any registered cwd no longer exists on disk, or if every file in the transport carries this machine's own name (which makes `pull` a silent no-op — give each machine a distinct `--name`).
|
|
104
175
|
|
|
105
176
|
## File format
|
|
106
177
|
|
|
@@ -110,7 +181,7 @@ claude-sync --help / --version
|
|
|
110
181
|
{
|
|
111
182
|
"_schemaVersion": 1,
|
|
112
183
|
"sessionId": "...",
|
|
113
|
-
"projectKey": "git:
|
|
184
|
+
"projectKey": "git:github.com/you/myapp",
|
|
114
185
|
"originalCwd": "C:\\Users\\you\\code\\myapp",
|
|
115
186
|
"machineName": "desktop",
|
|
116
187
|
"exportedAt": 1715380000000,
|
|
@@ -119,13 +190,12 @@ claude-sync --help / --version
|
|
|
119
190
|
}
|
|
120
191
|
```
|
|
121
192
|
|
|
122
|
-
Schema-versioned. Version 1 is the only thing
|
|
193
|
+
Schema-versioned. Version 1 is the only thing the current release understands; importers refuse unknown versions explicitly rather than silently dropping fields.
|
|
123
194
|
|
|
124
195
|
## What it isn't
|
|
125
196
|
|
|
126
|
-
- **Not
|
|
127
|
-
- **Not a relay service.**
|
|
128
|
-
- **Not real-time.** If both machines are CC-active simultaneously, the second to `pull` overwrites their in-progress session unless they noticed and stopped first. Use one machine at a time.
|
|
197
|
+
- **Not real-time co-editing.** `watch` polls on an interval; it isn't a locking protocol. If both machines drive the *same* session simultaneously, the second to sync overwrites the other's in-progress copy (the loser is kept as `<id>-copy.jsonl`). Use it to hand a project between machines, not to co-edit one session live.
|
|
198
|
+
- **Not a relay service.** Two transports ship — a local synced folder and a remote directory over SSH. There's no hosted relay; S3 / gist / WebSocket transports would be straightforward additions on the `Transport` interface, but aren't shipped yet.
|
|
129
199
|
- **Not a CC re-implementation.** It just shuffles JSONL. CC's session schema can change without breaking claude-sync — we never parse the events.
|
|
130
200
|
|
|
131
201
|
## Library API
|
|
@@ -134,13 +204,21 @@ For embedders (custom transports, watch daemons, etc.):
|
|
|
134
204
|
|
|
135
205
|
```ts
|
|
136
206
|
import {
|
|
137
|
-
listSessions, readSession, writeSession,
|
|
207
|
+
listSessions, listSessionStats, countSessionLines, readSession, writeSession,
|
|
138
208
|
buildCcsync, parseCcsync, readCcsyncFile, writeCcsyncFile,
|
|
139
|
-
projectKey, registerProject, lookupCwd,
|
|
140
|
-
|
|
209
|
+
projectKey, normalizeGitRemote, registerProject, registerProjectKey, lookupCwd,
|
|
210
|
+
// Transports: the abstraction + both backends.
|
|
211
|
+
createTransport, resolveTransportConfig, FsTransport, SshTransport,
|
|
212
|
+
type Transport, type TransportConfig,
|
|
141
213
|
} from '@askalf/claude-sync';
|
|
142
214
|
```
|
|
143
215
|
|
|
216
|
+
Everything is built on these exports — `listSessionStats` for cheap change
|
|
217
|
+
detection, the `Transport` interface (`push` / `listEntries` / …) for the sync,
|
|
218
|
+
`registerProject` for first-contact. A custom transport is one class
|
|
219
|
+
implementing `Transport`; `SshTransport` takes an injectable `SshExecutor`, so
|
|
220
|
+
you can wrap it or swap the wire entirely.
|
|
221
|
+
|
|
144
222
|
See `src/index.ts` for the full surface. Zero runtime dependencies.
|
|
145
223
|
|
|
146
224
|
## Environment
|
|
@@ -153,27 +231,16 @@ See `src/index.ts` for the full surface. Zero runtime dependencies.
|
|
|
153
231
|
|
|
154
232
|
MIT — see [LICENSE](LICENSE).
|
|
155
233
|
|
|
156
|
-
##
|
|
234
|
+
## Own Your Stack
|
|
157
235
|
|
|
158
|
-
|
|
159
|
-
|---------|-------------|
|
|
160
|
-
| [arnie](https://github.com/askalf/arnie) | Portable IT troubleshooting companion. Networking, AD, Windows Update, package managers, log triage, hardware checks. |
|
|
161
|
-
| [brio](https://github.com/askalf/brio) | Capability layer for AI workloads — semantic cache, cost tiering, policy. Sits in front of any Anthropic-compat endpoint. |
|
|
162
|
-
| [browser-bridge](https://github.com/askalf/browser-bridge) | Stealth headless Chromium in a container. CDP on 9222 — Playwright/Puppeteer/MCP-compatible. |
|
|
163
|
-
| [claude-bridge](https://github.com/askalf/claude-bridge) | Bridge Claude Code sessions to Discord. |
|
|
164
|
-
| [dario](https://github.com/askalf/dario) | Local LLM router. Use your Claude Max/Pro subscription as an API. |
|
|
165
|
-
| [deepdive](https://github.com/askalf/deepdive) | Local research agent. Plan → search → fetch → extract → synthesize. Cited answers. |
|
|
166
|
-
| [git-providers](https://github.com/askalf/git-providers) | Unified GitHub + GitLab + Bitbucket Cloud REST clients behind one GitProvider interface. Plus a 44-entry api-key-provider taxonomy. |
|
|
167
|
-
| [hands](https://github.com/askalf/hands) | Cross-platform computer-use agent. Mouse, keyboard, screen. |
|
|
168
|
-
| [install-kit](https://github.com/askalf/install-kit) | curl-pipe-bash template for self-hosted Docker apps. |
|
|
169
|
-
| [pgflex](https://github.com/askalf/pgflex) | One Postgres API. Two modes (real PG ↔ PGlite WASM). |
|
|
170
|
-
| [redisflex](https://github.com/askalf/redisflex) | One Redis API. Two modes (ioredis ↔ in-process). |
|
|
236
|
+
Part of **[Own Your Stack](https://github.com/askalf)** — open tools for owning your AI infrastructure instead of renting it by the token. One subscription. Your box. Your terms.
|
|
171
237
|
|
|
238
|
+
- **[dario](https://github.com/askalf/dario)** — own your routing
|
|
239
|
+
- **[deepdive](https://github.com/askalf/deepdive)** — own your research
|
|
240
|
+
- **[hands](https://github.com/askalf/hands)** — own your computer-use
|
|
241
|
+
- **[agent](https://github.com/askalf/agent)** — own your fleet
|
|
242
|
+
- **[browser-bridge](https://github.com/askalf/browser-bridge)** — own your browser
|
|
243
|
+
- **claude-sync** — own your sessions _(you are here)_
|
|
172
244
|
|
|
173
245
|
---
|
|
174
|
-
|
|
175
|
-
## Built by Sprayberry Labs
|
|
176
|
-
|
|
177
|
-
This is one of the open-source building blocks from **[Sprayberry Labs](https://sprayberrylabs.com)** — an independent studio (Atlanta, GA) that ships bespoke software and **fixed-price code & security audits**, delivered with the AI workforce these tools are part of.
|
|
178
|
-
|
|
179
|
-
**Got a codebase that needs an expert read?** → **[Scan a repo — free mini-audit](https://sprayberrylabs.com)**, or see the **$1,500 fixed-price Audit** and build Sprints. · [sprayberrylabs.com](https://sprayberrylabs.com) · hello@sprayberrylabs.com
|
|
246
|
+
Part of **[Own Your Stack](https://github.com/askalf)** — own your AI infrastructure instead of renting it. Built by Thomas Sprayberry.
|
package/dist/cli.d.ts
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* Subcommands:
|
|
6
6
|
* init <syncDir> [--name <machine>] scaffold ~/.claude-sync/config.json
|
|
7
7
|
* list list local CC sessions for the cwd
|
|
8
|
+
* register [cwd] register a cwd's project key (no session needed)
|
|
8
9
|
* export [session-id] [-o <file>] pack a session into a .ccsync file
|
|
9
10
|
* import <file> [--cwd <path>] install a .ccsync file locally
|
|
10
11
|
* push [session-id] export + ship to configured syncDir
|
|
11
12
|
* pull import any newer .ccsync files
|
|
13
|
+
* watch [session-id] [--interval <s>] daemon: auto-push changes + auto-pull peers
|
|
12
14
|
* doctor diagnose config + registry health
|
|
13
15
|
*
|
|
14
16
|
* Common flags: --help, --version. No external arg-parser dep — the
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG"}
|