@askalf/claude-sync 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 askalf
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # @askalf/claude-sync
2
+
3
+ > 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
+
5
+ ```bash
6
+ npm install -g @askalf/claude-sync
7
+ ```
8
+
9
+ [![CI](https://img.shields.io/github/actions/workflow/status/askalf/claude-sync/ci.yml?style=flat-square&label=CI&labelColor=020612)](https://github.com/askalf/claude-sync/actions)
10
+ [![npm](https://img.shields.io/npm/v/@askalf/claude-sync?style=flat-square&color=00ff88&label=npm&labelColor=020612)](https://www.npmjs.com/package/@askalf/claude-sync)
11
+ [![License](https://img.shields.io/badge/MIT-00ff88?style=flat-square&label=license&labelColor=020612)](LICENSE)
12
+
13
+ ## Why
14
+
15
+ Claude Code stores sessions locally at `~/.claude/projects/<encoded-cwd>/<session-id>.jsonl`. There's no native cross-machine sync. The naive workaround — rsync `~/.claude/projects/` — has two problems:
16
+
17
+ 1. **Path-hash mismatch.** `<encoded-cwd>` is the absolute path with `[/\\:.\s]+` collapsed to `-`. Same git repo at `/Users/alice/code/myapp` (mac) and `D:\code\myapp` (windows) hashes to different directory names → the synced session is orphaned on arrival.
18
+ 2. **No file locking.** Concurrent writes from two machines into the same JSONL corrupt the transcript.
19
+
20
+ `claude-sync` solves both:
21
+
22
+ - **Canonical project key** = `git:<remote-url>`. Same logical repo → same key, regardless of where it's checked out. (Falls back to `name:<basename>` when no git remote.)
23
+ - **Single-writer model.** You explicitly `push` from machine A, then `pull` on machine B. No background daemon racing files. The transport is a directory you nominate (Dropbox / iCloud / Syncthing / a USB stick); each machine writes its own file under its own machine name, so two machines pushing the same session don't collide.
24
+
25
+ ## Use it
26
+
27
+ ### One-time setup, on each machine
28
+
29
+ ```bash
30
+ # Point claude-sync at a directory that's mirrored across machines.
31
+ # Anything Dropbox / iCloud / Syncthing / OneDrive sync, or a network
32
+ # share. Doesn't matter what — claude-sync just reads + writes here.
33
+ claude-sync init ~/Dropbox/claude-sync --name desktop
34
+
35
+ # On the other machine:
36
+ claude-sync init ~/Dropbox/claude-sync --name laptop
37
+ ```
38
+
39
+ ### Push a session
40
+
41
+ ```bash
42
+ cd ~/code/myapp
43
+ claude # ... do work ...
44
+ # When you're switching machines:
45
+ claude-sync push
46
+ # → Pushed sess-abc123 → ~/Dropbox/claude-sync/git-https---github-com-you-myapp-git/sess-abc123-desktop.ccsync
47
+ ```
48
+
49
+ ### Pull on the other machine
50
+
51
+ ```bash
52
+ cd ~/code/myapp
53
+ claude-sync pull
54
+ # → Imported sess-abc123 from desktop → ~/.claude/projects/-Users-laptop-code-myapp/sess-abc123.jsonl
55
+ claude --resume sess-abc123
56
+ ```
57
+
58
+ That's the whole loop.
59
+
60
+ ## How path resolution works
61
+
62
+ When you `push` from a project with a git remote, claude-sync:
63
+
64
+ 1. Reads `git remote get-url origin` from your cwd.
65
+ 2. Builds a project key: `git:https://github.com/you/myapp.git`.
66
+ 3. Registers the local cwd under that key in `~/.claude-sync/projects.json`.
67
+ 4. Writes the `.ccsync` into `<syncDir>/<encoded-key>/<session>-<machine>.ccsync`.
68
+
69
+ When you `pull`, the receiving machine:
70
+
71
+ 1. Scans `<syncDir>/` for project subdirs.
72
+ 2. For each, looks up the encoded key in its own `projects.json`.
73
+ 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. You either `push` once from the right cwd to register it, or run `claude-sync import <file> --cwd <path>` explicitly.
75
+
76
+ In practice: clone the repo on the new machine, `cd` in, run `claude-sync push` once with no real session (it'll error if there's no session — that's fine), then run `claude-sync pull` to fetch. The first push registers the cwd.
77
+
78
+ A cleaner way: just run any `claude` command in the cwd once, then `claude-sync export` (which registers without pushing).
79
+
80
+ ## Concurrency
81
+
82
+ There's no daemon and no automatic background sync. You decide when to push and when to pull. If you forget and edit the same session on both machines:
83
+
84
+ - Two `.ccsync` files end up in `<syncDir>/<project>/`, named with each machine's name.
85
+ - The receiving `pull` skips files older or shorter than the local copy.
86
+ - Newer/longer files install with `--overwrite`. The losing machine's edits become a `<session-id>-copy.jsonl` (you can manually inspect + reconcile).
87
+
88
+ Future versions may add a watch-mode + lock-file protocol for "real-time" sync; v0.0.1 is deliberate-action only.
89
+
90
+ ## CLI reference
91
+
92
+ ```text
93
+ claude-sync init <syncDir> [--name <machine>]
94
+ claude-sync list
95
+ claude-sync export [session-id] [-o <file>]
96
+ claude-sync import <file> [--cwd <path>] [--overwrite]
97
+ claude-sync push [session-id]
98
+ claude-sync pull
99
+ claude-sync doctor
100
+ claude-sync --help / --version
101
+ ```
102
+
103
+ `doctor` prints config, machine name, registered projects, and warns if any registered cwd no longer exists on disk.
104
+
105
+ ## File format
106
+
107
+ `.ccsync` is a small JSON wrapper around the JSONL session content:
108
+
109
+ ```json
110
+ {
111
+ "_schemaVersion": 1,
112
+ "sessionId": "...",
113
+ "projectKey": "git:https://github.com/you/myapp.git",
114
+ "originalCwd": "C:\\Users\\you\\code\\myapp",
115
+ "machineName": "desktop",
116
+ "exportedAt": 1715380000000,
117
+ "lineCount": 142,
118
+ "jsonl": "<the entire session JSONL>"
119
+ }
120
+ ```
121
+
122
+ Schema-versioned. Version 1 is the only thing v0.0.1 understands; importers refuse unknown versions explicitly rather than silently dropping fields.
123
+
124
+ ## What it isn't
125
+
126
+ - **Not a daemon.** Push and pull are explicit user actions. Run a watcher yourself if you want background sync.
127
+ - **Not a relay service.** v0.0.1 ships only the filesystem transport — point it at any synced folder. SSH / S3 / gist / WebSocket transports would be straightforward additions; not in v0.0.1.
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.
129
+ - **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
+
131
+ ## Library API
132
+
133
+ For embedders (custom transports, watch daemons, etc.):
134
+
135
+ ```ts
136
+ import {
137
+ listSessions, readSession, writeSession,
138
+ buildCcsync, parseCcsync, readCcsyncFile, writeCcsyncFile,
139
+ projectKey, registerProject, lookupCwd,
140
+ pushToTransport, listTransport,
141
+ } from '@askalf/claude-sync';
142
+ ```
143
+
144
+ See `src/index.ts` for the full surface. Zero runtime dependencies.
145
+
146
+ ## License
147
+
148
+ MIT — see [LICENSE](LICENSE).
149
+
150
+ ## Also by askalf
151
+
152
+ | Project | What it does |
153
+ |---------|-------------|
154
+ | [arnie](https://github.com/askalf/arnie) | Portable IT troubleshooting companion. Networking, AD, Windows Update, package managers, log triage, hardware checks. |
155
+ | [brio](https://github.com/askalf/brio) | Capability layer for AI workloads — semantic cache, cost tiering, policy. Sits in front of any Anthropic-compat endpoint. |
156
+ | [browser-bridge](https://github.com/askalf/browser-bridge) | Stealth headless Chromium in a container. CDP on 9222 — Playwright/Puppeteer/MCP-compatible. |
157
+ | [claude-bridge](https://github.com/askalf/claude-bridge) | Bridge Claude Code sessions to Discord. |
158
+ | [dario](https://github.com/askalf/dario) | Local LLM router. Use your Claude Max/Pro subscription as an API. |
159
+ | [deepdive](https://github.com/askalf/deepdive) | Local research agent. Plan → search → fetch → extract → synthesize. Cited answers. |
160
+ | [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. |
161
+ | [hands](https://github.com/askalf/hands) | Cross-platform computer-use agent. Mouse, keyboard, screen. |
162
+ | [install-kit](https://github.com/askalf/install-kit) | curl-pipe-bash template for self-hosted Docker apps. |
163
+ | [pgflex](https://github.com/askalf/pgflex) | One Postgres API. Two modes (real PG ↔ PGlite WASM). |
164
+ | [redisflex](https://github.com/askalf/redisflex) | One Redis API. Two modes (ioredis ↔ in-process). |
package/dist/cli.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `claude-sync` — CLI entry point.
4
+ *
5
+ * Subcommands:
6
+ * init <syncDir> [--name <machine>] scaffold ~/.claude-sync/config.json
7
+ * list list local CC sessions for the cwd
8
+ * export [session-id] [-o <file>] pack a session into a .ccsync file
9
+ * import <file> [--cwd <path>] install a .ccsync file locally
10
+ * push [session-id] export + ship to configured syncDir
11
+ * pull import any newer .ccsync files
12
+ * doctor diagnose config + registry health
13
+ *
14
+ * Common flags: --help, --version. No external arg-parser dep — the
15
+ * surface is small enough that hand-rolled parsing keeps the runtime
16
+ * dep budget at zero.
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG"}
package/dist/cli.js ADDED
@@ -0,0 +1,338 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `claude-sync` — CLI entry point.
4
+ *
5
+ * Subcommands:
6
+ * init <syncDir> [--name <machine>] scaffold ~/.claude-sync/config.json
7
+ * list list local CC sessions for the cwd
8
+ * export [session-id] [-o <file>] pack a session into a .ccsync file
9
+ * import <file> [--cwd <path>] install a .ccsync file locally
10
+ * push [session-id] export + ship to configured syncDir
11
+ * pull import any newer .ccsync files
12
+ * doctor diagnose config + registry health
13
+ *
14
+ * Common flags: --help, --version. No external arg-parser dep — the
15
+ * surface is small enough that hand-rolled parsing keeps the runtime
16
+ * dep budget at zero.
17
+ */
18
+ import { existsSync } from 'node:fs';
19
+ import { resolve, basename } from 'node:path';
20
+ import { VERSION } from './index.js';
21
+ import { loadConfig, saveConfig, configExists, buildDefaultConfig, } from './config.js';
22
+ import { encodeProjectDir, registerProject, loadRegistry, lookupCwd, claudeProjectsRoot, } from './project.js';
23
+ import { listSessions, readSession, writeSession, assignFreshId, } from './session.js';
24
+ import { buildCcsync, readCcsyncFile, writeCcsyncFile, } from './format.js';
25
+ import { pushToTransport, listTransport, listProjectKeys, } from './transport.js';
26
+ const HELP = `claude-sync ${VERSION}
27
+
28
+ Usage: claude-sync <command> [options]
29
+
30
+ Commands:
31
+ init <syncDir> [--name <machine>]
32
+ Scaffold ~/.claude-sync/config.json. <syncDir> is a directory
33
+ that's mirrored across your machines (Dropbox, iCloud, Syncthing,
34
+ a USB drive). --name defaults to your hostname.
35
+
36
+ list
37
+ List Claude Code sessions for the current working directory,
38
+ sorted by recency.
39
+
40
+ export [session-id] [-o <file>]
41
+ Pack a session into a portable .ccsync file. Default session-id
42
+ is the most recent one for the current cwd. Default output path
43
+ is ./<session-id>.ccsync.
44
+
45
+ import <file> [--cwd <path>] [--overwrite]
46
+ Install a .ccsync file into local Claude Code sessions. Uses the
47
+ project registry to find the right local cwd; --cwd overrides.
48
+ --overwrite replaces an existing session with the same id.
49
+
50
+ push [session-id]
51
+ Equivalent to: export → write into the configured syncDir.
52
+
53
+ pull
54
+ Scan syncDir for newer .ccsync files from other machines, import
55
+ each into the matching local cwd. Skips sessions that already
56
+ exist locally with the same line count (idempotent re-runs).
57
+
58
+ doctor
59
+ Print a health report: config, machine name, registered projects,
60
+ Claude Code projects dir, and any obvious mismatches.
61
+
62
+ Options:
63
+ --help, -h Show this help.
64
+ --version, -v Show version.
65
+ `;
66
+ async function main(argv) {
67
+ const [cmd, ...rest] = argv;
68
+ if (!cmd || cmd === '--help' || cmd === '-h') {
69
+ process.stdout.write(HELP);
70
+ return 0;
71
+ }
72
+ if (cmd === '--version' || cmd === '-v') {
73
+ process.stdout.write(`${VERSION}\n`);
74
+ return 0;
75
+ }
76
+ switch (cmd) {
77
+ case 'init': return cmdInit(rest);
78
+ case 'list': return cmdList(rest);
79
+ case 'export': return cmdExport(rest);
80
+ case 'import': return cmdImport(rest);
81
+ case 'push': return cmdPush(rest);
82
+ case 'pull': return cmdPull(rest);
83
+ case 'doctor': return cmdDoctor(rest);
84
+ default:
85
+ process.stderr.write(`Unknown command: ${cmd}\nRun \`claude-sync --help\` for usage.\n`);
86
+ return 1;
87
+ }
88
+ }
89
+ // ── init ───────────────────────────────────────────────────────────
90
+ function cmdInit(args) {
91
+ const syncDir = args[0];
92
+ if (!syncDir || syncDir.startsWith('--')) {
93
+ process.stderr.write('Error: <syncDir> is required.\nUsage: claude-sync init <syncDir> [--name <machine>]\n');
94
+ return 1;
95
+ }
96
+ const nameIdx = args.indexOf('--name');
97
+ const machineName = nameIdx >= 0 ? args[nameIdx + 1] : undefined;
98
+ if (configExists()) {
99
+ process.stderr.write('Config already exists at ~/.claude-sync/config.json. Edit it manually to change settings.\n');
100
+ return 1;
101
+ }
102
+ const cfg = buildDefaultConfig(resolve(syncDir), machineName);
103
+ saveConfig(cfg);
104
+ process.stdout.write(`Created ~/.claude-sync/config.json\n` +
105
+ ` syncDir: ${cfg.syncDir}\n` +
106
+ ` machineName: ${cfg.machineName}\n\n` +
107
+ `Next steps:\n` +
108
+ ` 1. Run \`claude-sync push\` after a session you want to share.\n` +
109
+ ` 2. On your other machine: install claude-sync, run \`claude-sync init <same-syncDir>\` with a different --name.\n` +
110
+ ` 3. Run \`claude-sync pull\` to fetch.\n`);
111
+ return 0;
112
+ }
113
+ // ── list ───────────────────────────────────────────────────────────
114
+ function cmdList(_args) {
115
+ const cwd = process.cwd();
116
+ const sessions = listSessions(cwd);
117
+ if (sessions.length === 0) {
118
+ process.stdout.write(`No Claude Code sessions found for ${cwd}\n`);
119
+ process.stdout.write(` expected at: ${claudeProjectsRoot()}/${encodeProjectDir(cwd)}\n`);
120
+ return 0;
121
+ }
122
+ process.stdout.write(`Sessions for ${cwd}:\n`);
123
+ for (const s of sessions) {
124
+ const when = new Date(s.modifiedAt).toISOString().replace('T', ' ').slice(0, 19);
125
+ process.stdout.write(` ${s.id} (${s.lineCount} lines, ${formatBytes(s.size)}, ${when})\n`);
126
+ }
127
+ return 0;
128
+ }
129
+ // ── export ─────────────────────────────────────────────────────────
130
+ function cmdExport(args) {
131
+ const cwd = process.cwd();
132
+ const oIdx = args.indexOf('-o');
133
+ const outPath = oIdx >= 0 ? args[oIdx + 1] : undefined;
134
+ const sessionId = args[0] && !args[0].startsWith('-') ? args[0] : undefined;
135
+ const session = pickSession(cwd, sessionId);
136
+ if (!session)
137
+ return 1;
138
+ const { key } = registerProject(cwd);
139
+ const cfg = configExists() ? loadConfig() : null;
140
+ const machineName = cfg?.machineName ?? 'unknown';
141
+ const ccsync = buildCcsync({
142
+ sessionId: session.id,
143
+ projectKey: key,
144
+ originalCwd: cwd,
145
+ machineName,
146
+ exportedAt: Date.now(),
147
+ lineCount: session.lineCount,
148
+ jsonl: readSession(session.path),
149
+ });
150
+ const dest = outPath ?? `${session.id}.ccsync`;
151
+ writeCcsyncFile(dest, ccsync);
152
+ process.stdout.write(`Exported ${session.id} → ${dest}\n (${session.lineCount} lines, project: ${key})\n`);
153
+ return 0;
154
+ }
155
+ // ── import ─────────────────────────────────────────────────────────
156
+ function cmdImport(args) {
157
+ const filePath = args[0];
158
+ if (!filePath || filePath.startsWith('--')) {
159
+ process.stderr.write('Error: <file> is required.\nUsage: claude-sync import <file> [--cwd <path>] [--overwrite]\n');
160
+ return 1;
161
+ }
162
+ if (!existsSync(filePath)) {
163
+ process.stderr.write(`Error: file not found: ${filePath}\n`);
164
+ return 1;
165
+ }
166
+ const cwdIdx = args.indexOf('--cwd');
167
+ const explicitCwd = cwdIdx >= 0 ? resolve(args[cwdIdx + 1] ?? '') : null;
168
+ const overwrite = args.includes('--overwrite');
169
+ const ccsync = readCcsyncFile(filePath);
170
+ return installCcsync(ccsync, explicitCwd, overwrite);
171
+ }
172
+ // ── push ───────────────────────────────────────────────────────────
173
+ function cmdPush(args) {
174
+ const cfg = mustLoadConfig();
175
+ if (!cfg)
176
+ return 1;
177
+ const cwd = process.cwd();
178
+ const sessionId = args[0] && !args[0].startsWith('-') ? args[0] : undefined;
179
+ const session = pickSession(cwd, sessionId);
180
+ if (!session)
181
+ return 1;
182
+ const { key } = registerProject(cwd);
183
+ const ccsync = buildCcsync({
184
+ sessionId: session.id,
185
+ projectKey: key,
186
+ originalCwd: cwd,
187
+ machineName: cfg.machineName,
188
+ exportedAt: Date.now(),
189
+ lineCount: session.lineCount,
190
+ jsonl: readSession(session.path),
191
+ });
192
+ const written = pushToTransport(cfg.syncDir, ccsync);
193
+ process.stdout.write(`Pushed ${session.id} → ${written}\n`);
194
+ return 0;
195
+ }
196
+ // ── pull ───────────────────────────────────────────────────────────
197
+ function cmdPull(_args) {
198
+ const cfg = mustLoadConfig();
199
+ if (!cfg)
200
+ return 1;
201
+ const reg = loadRegistry();
202
+ const projectKeys = listProjectKeys(cfg.syncDir);
203
+ if (projectKeys.length === 0) {
204
+ process.stdout.write(`Nothing to pull — ${cfg.syncDir} is empty.\n`);
205
+ return 0;
206
+ }
207
+ let imported = 0;
208
+ let skipped = 0;
209
+ for (const encodedKey of projectKeys) {
210
+ // We have the encoded form; find the matching raw key in the registry.
211
+ const rawKey = Object.keys(reg.projects).find((k) => encodeProjectDir(k) === encodedKey);
212
+ if (!rawKey) {
213
+ process.stdout.write(` skip ${encodedKey} (no local project registered for this key)\n`);
214
+ skipped++;
215
+ continue;
216
+ }
217
+ const cwd = lookupCwd(rawKey, reg);
218
+ if (!cwd) {
219
+ process.stdout.write(` skip ${encodedKey} (registered cwds no longer exist on disk)\n`);
220
+ skipped++;
221
+ continue;
222
+ }
223
+ // Per-session: take the freshest entry that's newer than what we
224
+ // already have locally.
225
+ const entries = listTransport(cfg.syncDir, rawKey, cfg.machineName);
226
+ const localSessions = new Map(listSessions(cwd).map((s) => [s.id, s]));
227
+ for (const entry of entries) {
228
+ const local = localSessions.get(entry.file.sessionId);
229
+ if (local && local.lineCount >= entry.file.lineCount) {
230
+ // Already at this revision (or further) locally. Skip silently
231
+ // so re-running pull is a no-op.
232
+ continue;
233
+ }
234
+ const status = installCcsync(entry.file, cwd, /*overwrite*/ true);
235
+ if (status === 0)
236
+ imported++;
237
+ }
238
+ }
239
+ process.stdout.write(`\nDone — imported ${imported} session(s), skipped ${skipped}.\n`);
240
+ return 0;
241
+ }
242
+ // ── doctor ─────────────────────────────────────────────────────────
243
+ function cmdDoctor(_args) {
244
+ const ok = (m) => { process.stdout.write(` ✓ ${m}\n`); };
245
+ const warn = (m) => { process.stdout.write(` ! ${m}\n`); };
246
+ process.stdout.write(`claude-sync ${VERSION}\n\n`);
247
+ if (configExists()) {
248
+ const cfg = loadConfig();
249
+ ok(`config: ${cfg.machineName} → ${cfg.syncDir}`);
250
+ if (!existsSync(cfg.syncDir))
251
+ warn(`syncDir does not exist: ${cfg.syncDir}`);
252
+ }
253
+ else {
254
+ warn(`no config (run \`claude-sync init <syncDir>\`)`);
255
+ }
256
+ if (!existsSync(claudeProjectsRoot())) {
257
+ warn(`Claude Code projects dir not found: ${claudeProjectsRoot()}`);
258
+ }
259
+ else {
260
+ ok(`Claude Code projects dir: ${claudeProjectsRoot()}`);
261
+ }
262
+ const reg = loadRegistry();
263
+ const total = Object.values(reg.projects).reduce((acc, list) => acc + list.length, 0);
264
+ ok(`registered projects: ${Object.keys(reg.projects).length} keys, ${total} cwds`);
265
+ for (const [k, cwds] of Object.entries(reg.projects)) {
266
+ for (const cwd of cwds) {
267
+ const exists = existsSync(cwd);
268
+ process.stdout.write(` ${exists ? '·' : '✗'} ${k}\n ${cwd}${exists ? '' : ' (missing)'}\n`);
269
+ }
270
+ }
271
+ return 0;
272
+ }
273
+ // ── helpers ────────────────────────────────────────────────────────
274
+ function mustLoadConfig() {
275
+ if (!configExists()) {
276
+ process.stderr.write(`No config found. Run \`claude-sync init <syncDir>\` first.\n`);
277
+ return null;
278
+ }
279
+ return loadConfig();
280
+ }
281
+ function pickSession(cwd, requestedId) {
282
+ const sessions = listSessions(cwd);
283
+ if (sessions.length === 0) {
284
+ process.stderr.write(`No Claude Code sessions found for ${cwd}.\n`);
285
+ return null;
286
+ }
287
+ if (requestedId) {
288
+ const found = sessions.find((s) => s.id === requestedId);
289
+ if (!found) {
290
+ process.stderr.write(`Session ${requestedId} not found in ${cwd}. Run \`claude-sync list\` to see available ids.\n`);
291
+ return null;
292
+ }
293
+ return found;
294
+ }
295
+ // Default to most-recent.
296
+ return sessions[0] ?? null;
297
+ }
298
+ function installCcsync(ccsync, explicitCwd, overwrite) {
299
+ let targetCwd = explicitCwd;
300
+ if (!targetCwd) {
301
+ targetCwd = lookupCwd(ccsync.projectKey);
302
+ }
303
+ if (!targetCwd) {
304
+ process.stderr.write(`Error: no local cwd registered for project key:\n ${ccsync.projectKey}\n` +
305
+ `Either:\n` +
306
+ ` - Pass --cwd <path> to install to a specific directory, OR\n` +
307
+ ` - Run \`claude-sync export\` (or \`push\`) once from the right cwd to register it, then re-import.\n`);
308
+ return 1;
309
+ }
310
+ let sessionId = ccsync.sessionId;
311
+ if (!overwrite) {
312
+ sessionId = assignFreshId(targetCwd, ccsync.sessionId);
313
+ }
314
+ try {
315
+ const writtenTo = writeSession(targetCwd, sessionId, ccsync.jsonl, { overwrite });
316
+ process.stdout.write(`Imported ${sessionId} from ${ccsync.machineName} → ${writtenTo}\n` +
317
+ ` (${ccsync.lineCount} lines, project: ${ccsync.projectKey})\n`);
318
+ return 0;
319
+ }
320
+ catch (err) {
321
+ process.stderr.write(`Failed to import: ${err instanceof Error ? err.message : err}\n`);
322
+ return 1;
323
+ }
324
+ }
325
+ function formatBytes(n) {
326
+ if (n < 1024)
327
+ return `${n} B`;
328
+ if (n < 1024 * 1024)
329
+ return `${(n / 1024).toFixed(1)} KB`;
330
+ return `${(n / 1024 / 1024).toFixed(1)} MB`;
331
+ }
332
+ // We accept basename in some args; suppress unused-import warnings.
333
+ void basename;
334
+ main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
335
+ process.stderr.write(`claude-sync: ${err.message}\n`);
336
+ process.exit(1);
337
+ });
338
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `~/.claude-sync/config.json` — top-level CLI configuration.
3
+ *
4
+ * One required field: `syncDir`. Everything else is optional and
5
+ * defaulted. The user runs `claude-sync init` to scaffold this file
6
+ * pointing at their Dropbox / iCloud Drive / Syncthing folder.
7
+ *
8
+ * {
9
+ * "_schemaVersion": 1,
10
+ * "syncDir": "/Users/thomas/Dropbox/claude-sync",
11
+ * "machineName": "thomas-desktop"
12
+ * }
13
+ *
14
+ * The transport layer (filesystem only in v0.0.1) reads/writes
15
+ * `<syncDir>/<projectKey>/<sessionId>-<machineName>.ccsync`. That
16
+ * naming gives us:
17
+ * - One subdir per project, easy to inspect
18
+ * - Multiple machines can push into the same project without
19
+ * overwriting each other (machineName disambiguates)
20
+ * - `pull` can list project subdirs and import the freshest file
21
+ */
22
+ export interface Config {
23
+ _schemaVersion: 1;
24
+ /** Path to the sync directory. Anything that ends up here gets
25
+ * picked up by `claude-sync pull` on the other side. The user
26
+ * points this at a Dropbox / iCloud / Syncthing-mirrored path. */
27
+ syncDir: string;
28
+ /** Machine name used in .ccsync metadata. Defaults to `os.hostname()`
29
+ * at init time; can be changed manually. */
30
+ machineName: string;
31
+ }
32
+ export declare function configExists(): boolean;
33
+ export declare function loadConfig(): Config;
34
+ export declare function saveConfig(cfg: Config): void;
35
+ export declare function defaultMachineName(): string;
36
+ export declare function buildDefaultConfig(syncDir: string, machineName?: string): Config;
37
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,MAAM,WAAW,MAAM;IACrB,cAAc,EAAE,CAAC,CAAC;IAClB;;uEAEmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB;iDAC6C;IAC7C,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAYnC;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI5C;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhF"}