baton-cli 0.3.2 → 0.4.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/dist/cli.js +159 -54
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,21 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/pull.ts
|
|
7
|
-
import { join as
|
|
7
|
+
import { join as join7 } from "path";
|
|
8
|
+
|
|
9
|
+
// src/adapters/claude-code/paths.ts
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
function encodeProjectDir(projectPath) {
|
|
13
|
+
const normalized = projectPath.replace(/\\/g, "/");
|
|
14
|
+
return normalized.replace(/[/.:]/g, "-");
|
|
15
|
+
}
|
|
16
|
+
function getClaudeProjectsDir() {
|
|
17
|
+
return join(homedir(), ".claude", "projects");
|
|
18
|
+
}
|
|
19
|
+
function getProjectConfigPath() {
|
|
20
|
+
return join(homedir(), ".claude", "project-config.json");
|
|
21
|
+
}
|
|
8
22
|
|
|
9
23
|
// src/adapters/claude-code/reader.ts
|
|
10
24
|
import { readdir, readFile, stat } from "fs/promises";
|
|
@@ -29,20 +43,6 @@ var GitNotFoundError = class extends BatonError {
|
|
|
29
43
|
var GhNotFoundError = class extends BatonError {
|
|
30
44
|
};
|
|
31
45
|
|
|
32
|
-
// src/adapters/claude-code/paths.ts
|
|
33
|
-
import { homedir } from "os";
|
|
34
|
-
import { join } from "path";
|
|
35
|
-
function encodeProjectDir(projectPath) {
|
|
36
|
-
const normalized = projectPath.replace(/\\/g, "/");
|
|
37
|
-
return normalized.replace(/[/.:]/g, "-");
|
|
38
|
-
}
|
|
39
|
-
function getClaudeProjectsDir() {
|
|
40
|
-
return join(homedir(), ".claude", "projects");
|
|
41
|
-
}
|
|
42
|
-
function getProjectConfigPath() {
|
|
43
|
-
return join(homedir(), ".claude", "project-config.json");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
46
|
// src/adapters/claude-code/reader.ts
|
|
47
47
|
async function listLocalSessionIds(projectPath) {
|
|
48
48
|
const projectDirName = encodeProjectDir(projectPath);
|
|
@@ -55,6 +55,17 @@ async function listLocalSessionIds(projectPath) {
|
|
|
55
55
|
}
|
|
56
56
|
return entries.filter((e) => e.endsWith(".jsonl")).map((e) => basename(e, ".jsonl"));
|
|
57
57
|
}
|
|
58
|
+
async function listLocalMemoryFiles(projectPath) {
|
|
59
|
+
const projectDirName = encodeProjectDir(projectPath);
|
|
60
|
+
const memoryDir = join2(getClaudeProjectsDir(), projectDirName, "memory");
|
|
61
|
+
let entries;
|
|
62
|
+
try {
|
|
63
|
+
entries = await readdir(memoryDir);
|
|
64
|
+
} catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return entries;
|
|
68
|
+
}
|
|
58
69
|
async function collectProjectData(projectPath) {
|
|
59
70
|
const projectDirName = encodeProjectDir(projectPath);
|
|
60
71
|
const projectDir = join2(getClaudeProjectsDir(), projectDirName);
|
|
@@ -126,11 +137,14 @@ async function collectMemory(projectDir) {
|
|
|
126
137
|
// src/adapters/claude-code/writer.ts
|
|
127
138
|
import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
128
139
|
import { dirname, join as join3 } from "path";
|
|
129
|
-
async function restoreProjectData(projectPath, data) {
|
|
140
|
+
async function restoreProjectData(projectPath, data, options) {
|
|
130
141
|
const projectDirName = encodeProjectDir(projectPath);
|
|
131
142
|
const projectDir = join3(getClaudeProjectsDir(), projectDirName);
|
|
132
143
|
await mkdir(projectDir, { recursive: true });
|
|
144
|
+
const skipSessions = options?.skipSessions ?? /* @__PURE__ */ new Set();
|
|
145
|
+
const skipMemory = options?.skipMemory ?? /* @__PURE__ */ new Set();
|
|
133
146
|
for (const session of data.sessions) {
|
|
147
|
+
if (skipSessions.has(session.sessionId)) continue;
|
|
134
148
|
await writeFile(
|
|
135
149
|
join3(projectDir, `${session.sessionId}.jsonl`),
|
|
136
150
|
session.jsonl,
|
|
@@ -152,6 +166,7 @@ async function restoreProjectData(projectPath, data) {
|
|
|
152
166
|
const memoryDir = join3(projectDir, "memory");
|
|
153
167
|
await mkdir(memoryDir, { recursive: true });
|
|
154
168
|
for (const [filename, content] of data.memory) {
|
|
169
|
+
if (skipMemory.has(filename)) continue;
|
|
155
170
|
await writeFile(join3(memoryDir, filename), content, "utf-8");
|
|
156
171
|
}
|
|
157
172
|
}
|
|
@@ -205,6 +220,79 @@ async function saveConfig(config) {
|
|
|
205
220
|
await writeFile2(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
206
221
|
}
|
|
207
222
|
|
|
223
|
+
// src/core/conflicts.ts
|
|
224
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
225
|
+
import { join as join5 } from "path";
|
|
226
|
+
async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFiles, remoteMemoryFiles, ctx) {
|
|
227
|
+
const remoteIdSet = new Set(remoteSessionIds);
|
|
228
|
+
const overlappingSessionIds = localSessionIds.filter(
|
|
229
|
+
(id) => remoteIdSet.has(id)
|
|
230
|
+
);
|
|
231
|
+
const remoteMemSet = new Set(remoteMemoryFiles);
|
|
232
|
+
const overlappingMemory = localMemoryFiles.filter((f) => remoteMemSet.has(f));
|
|
233
|
+
const sessions = [];
|
|
234
|
+
for (const id of overlappingSessionIds) {
|
|
235
|
+
const localPath = join5(ctx.localProjectDir, `${id}.jsonl`);
|
|
236
|
+
const remotePath = join5(ctx.remoteSessionsDir, `${id}.jsonl`);
|
|
237
|
+
if (await contentDiffers(localPath, remotePath)) {
|
|
238
|
+
sessions.push(id);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const memoryFiles = [];
|
|
242
|
+
for (const file of overlappingMemory) {
|
|
243
|
+
const localPath = join5(ctx.localProjectDir, "memory", file);
|
|
244
|
+
const remotePath = join5(ctx.remoteMemoryDir, file);
|
|
245
|
+
if (await contentDiffers(localPath, remotePath)) {
|
|
246
|
+
memoryFiles.push(file);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return { sessions, memoryFiles };
|
|
250
|
+
}
|
|
251
|
+
async function contentDiffers(pathA, pathB) {
|
|
252
|
+
try {
|
|
253
|
+
const [contentA, contentB] = await Promise.all([
|
|
254
|
+
readFile4(pathA, "utf-8"),
|
|
255
|
+
readFile4(pathB, "utf-8")
|
|
256
|
+
]);
|
|
257
|
+
return contentA !== contentB;
|
|
258
|
+
} catch {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function formatConflictMessage(conflicts, remoteMemoryDir) {
|
|
263
|
+
const lines = [];
|
|
264
|
+
lines.push("Conflicts detected during baton pull.");
|
|
265
|
+
lines.push("");
|
|
266
|
+
if (conflicts.sessions.length > 0) {
|
|
267
|
+
lines.push(`Conflicting sessions (${conflicts.sessions.length}):`);
|
|
268
|
+
for (const id of conflicts.sessions) {
|
|
269
|
+
lines.push(` - ${id}`);
|
|
270
|
+
}
|
|
271
|
+
lines.push("");
|
|
272
|
+
}
|
|
273
|
+
if (conflicts.memoryFiles.length > 0) {
|
|
274
|
+
lines.push(`Conflicting memory files (${conflicts.memoryFiles.length}):`);
|
|
275
|
+
for (const file of conflicts.memoryFiles) {
|
|
276
|
+
lines.push(` - ${file}`);
|
|
277
|
+
lines.push(` remote: ${join5(remoteMemoryDir, file)}`);
|
|
278
|
+
}
|
|
279
|
+
lines.push("");
|
|
280
|
+
}
|
|
281
|
+
lines.push("To resolve:");
|
|
282
|
+
lines.push(" baton pull --force overwrite all local with remote");
|
|
283
|
+
lines.push(" baton pull --skip only pull non-conflicting files");
|
|
284
|
+
if (conflicts.memoryFiles.length > 0) {
|
|
285
|
+
lines.push("");
|
|
286
|
+
lines.push(
|
|
287
|
+
"Tip: ask Claude Code to merge the conflicting memory files by reading both local and remote versions."
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return lines.join("\n");
|
|
291
|
+
}
|
|
292
|
+
function hasConflicts(conflicts) {
|
|
293
|
+
return conflicts.sessions.length > 0 || conflicts.memoryFiles.length > 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
208
296
|
// src/core/git.ts
|
|
209
297
|
import { execFile } from "child_process";
|
|
210
298
|
import { access } from "fs/promises";
|
|
@@ -429,10 +517,10 @@ async function detectProject(cwd) {
|
|
|
429
517
|
}
|
|
430
518
|
|
|
431
519
|
// src/commands/checkpoint.ts
|
|
432
|
-
import { mkdir as mkdir3, readdir as readdir2, readFile as
|
|
433
|
-
import { join as
|
|
520
|
+
import { mkdir as mkdir3, readdir as readdir2, readFile as readFile5, rm, writeFile as writeFile3 } from "fs/promises";
|
|
521
|
+
import { join as join6 } from "path";
|
|
434
522
|
async function writeCheckpoint(projectDir, project, data) {
|
|
435
|
-
const sessionsDir =
|
|
523
|
+
const sessionsDir = join6(projectDir, "sessions");
|
|
436
524
|
await rm(sessionsDir, { recursive: true, force: true });
|
|
437
525
|
await mkdir3(sessionsDir, { recursive: true });
|
|
438
526
|
const meta = {
|
|
@@ -441,38 +529,38 @@ async function writeCheckpoint(projectDir, project, data) {
|
|
|
441
529
|
pushed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
442
530
|
};
|
|
443
531
|
await writeFile3(
|
|
444
|
-
|
|
532
|
+
join6(projectDir, "meta.json"),
|
|
445
533
|
JSON.stringify(meta, null, 2),
|
|
446
534
|
"utf-8"
|
|
447
535
|
);
|
|
448
536
|
for (const session of data.sessions) {
|
|
449
537
|
await writeFile3(
|
|
450
|
-
|
|
538
|
+
join6(sessionsDir, `${session.sessionId}.jsonl`),
|
|
451
539
|
session.jsonl,
|
|
452
540
|
"utf-8"
|
|
453
541
|
);
|
|
454
542
|
if (session.toolResults.size > 0) {
|
|
455
|
-
const toolResultsDir =
|
|
543
|
+
const toolResultsDir = join6(
|
|
456
544
|
sessionsDir,
|
|
457
545
|
session.sessionId,
|
|
458
546
|
"tool-results"
|
|
459
547
|
);
|
|
460
548
|
await mkdir3(toolResultsDir, { recursive: true });
|
|
461
549
|
for (const [filename, content] of session.toolResults) {
|
|
462
|
-
await writeFile3(
|
|
550
|
+
await writeFile3(join6(toolResultsDir, filename), content, "utf-8");
|
|
463
551
|
}
|
|
464
552
|
}
|
|
465
553
|
}
|
|
466
554
|
if (data.memory.size > 0) {
|
|
467
|
-
const memoryDir =
|
|
555
|
+
const memoryDir = join6(projectDir, "memory");
|
|
468
556
|
await mkdir3(memoryDir, { recursive: true });
|
|
469
557
|
for (const [filename, content] of data.memory) {
|
|
470
|
-
await writeFile3(
|
|
558
|
+
await writeFile3(join6(memoryDir, filename), content, "utf-8");
|
|
471
559
|
}
|
|
472
560
|
}
|
|
473
561
|
}
|
|
474
562
|
async function readCheckpoint(projectDir) {
|
|
475
|
-
const sessionsDir =
|
|
563
|
+
const sessionsDir = join6(projectDir, "sessions");
|
|
476
564
|
let sessionFiles;
|
|
477
565
|
try {
|
|
478
566
|
const entries = await readdir2(sessionsDir);
|
|
@@ -486,7 +574,7 @@ async function readCheckpoint(projectDir) {
|
|
|
486
574
|
const sessions = [];
|
|
487
575
|
for (const file of sessionFiles) {
|
|
488
576
|
const sessionId = file.replace(".jsonl", "");
|
|
489
|
-
const jsonl = await
|
|
577
|
+
const jsonl = await readFile5(join6(sessionsDir, file), "utf-8");
|
|
490
578
|
const toolResults = await readToolResults(sessionsDir, sessionId);
|
|
491
579
|
sessions.push({ sessionId, jsonl, toolResults });
|
|
492
580
|
}
|
|
@@ -499,7 +587,7 @@ async function readCheckpoint(projectDir) {
|
|
|
499
587
|
}
|
|
500
588
|
async function readToolResults(sessionsDir, sessionId) {
|
|
501
589
|
const results = /* @__PURE__ */ new Map();
|
|
502
|
-
const toolResultsDir =
|
|
590
|
+
const toolResultsDir = join6(sessionsDir, sessionId, "tool-results");
|
|
503
591
|
let entries;
|
|
504
592
|
try {
|
|
505
593
|
entries = await readdir2(toolResultsDir);
|
|
@@ -507,14 +595,14 @@ async function readToolResults(sessionsDir, sessionId) {
|
|
|
507
595
|
return results;
|
|
508
596
|
}
|
|
509
597
|
for (const entry of entries) {
|
|
510
|
-
const content = await
|
|
598
|
+
const content = await readFile5(join6(toolResultsDir, entry), "utf-8");
|
|
511
599
|
results.set(entry, content);
|
|
512
600
|
}
|
|
513
601
|
return results;
|
|
514
602
|
}
|
|
515
603
|
async function readMemory(projectDir) {
|
|
516
604
|
const memory = /* @__PURE__ */ new Map();
|
|
517
|
-
const memoryDir =
|
|
605
|
+
const memoryDir = join6(projectDir, "memory");
|
|
518
606
|
let entries;
|
|
519
607
|
try {
|
|
520
608
|
entries = await readdir2(memoryDir);
|
|
@@ -522,7 +610,7 @@ async function readMemory(projectDir) {
|
|
|
522
610
|
return memory;
|
|
523
611
|
}
|
|
524
612
|
for (const entry of entries) {
|
|
525
|
-
const content = await
|
|
613
|
+
const content = await readFile5(join6(memoryDir, entry), "utf-8");
|
|
526
614
|
memory.set(entry, content);
|
|
527
615
|
}
|
|
528
616
|
return memory;
|
|
@@ -621,7 +709,7 @@ async function pull(options) {
|
|
|
621
709
|
console.log("Pulling latest...");
|
|
622
710
|
await pullRepo(repoDir);
|
|
623
711
|
}
|
|
624
|
-
const projectDir =
|
|
712
|
+
const projectDir = join7(repoDir, "projects", project.projectId);
|
|
625
713
|
const data = await readCheckpoint(projectDir);
|
|
626
714
|
if (!data) {
|
|
627
715
|
throw new NoSessionsError(
|
|
@@ -631,26 +719,43 @@ async function pull(options) {
|
|
|
631
719
|
console.log(
|
|
632
720
|
`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`
|
|
633
721
|
);
|
|
634
|
-
if (!options.force) {
|
|
635
|
-
const localIds = await listLocalSessionIds(cwd);
|
|
636
|
-
const remoteIds = new Set(data.sessions.map((s) => s.sessionId));
|
|
637
|
-
const conflicts = localIds.filter((id) => remoteIds.has(id));
|
|
638
|
-
if (conflicts.length > 0) {
|
|
639
|
-
throw new ConflictError(
|
|
640
|
-
`${conflicts.length} local session(s) would be overwritten. Use 'baton pull --force' to proceed.`
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
722
|
const pathCtx = getLocalPathContext(cwd);
|
|
645
723
|
for (const session of data.sessions) {
|
|
646
724
|
session.jsonl = expandPaths(session.jsonl, pathCtx);
|
|
647
725
|
}
|
|
648
|
-
|
|
649
|
-
|
|
726
|
+
const localProjectDir = join7(getClaudeProjectsDir(), encodeProjectDir(cwd));
|
|
727
|
+
const remoteSessionsDir = join7(projectDir, "sessions");
|
|
728
|
+
const remoteMemoryDir = join7(projectDir, "memory");
|
|
729
|
+
const localSessionIds = await listLocalSessionIds(cwd);
|
|
730
|
+
const localMemoryFiles = await listLocalMemoryFiles(cwd);
|
|
731
|
+
const remoteSessionIds = data.sessions.map((s) => s.sessionId);
|
|
732
|
+
const remoteMemoryFiles = [...data.memory.keys()];
|
|
733
|
+
const conflicts = await detectConflicts(
|
|
734
|
+
localSessionIds,
|
|
735
|
+
remoteSessionIds,
|
|
736
|
+
localMemoryFiles,
|
|
737
|
+
remoteMemoryFiles,
|
|
738
|
+
{ localProjectDir, remoteSessionsDir, remoteMemoryDir }
|
|
739
|
+
);
|
|
740
|
+
if (hasConflicts(conflicts) && !options.force && !options.skip) {
|
|
741
|
+
throw new ConflictError(formatConflictMessage(conflicts, remoteMemoryDir));
|
|
742
|
+
}
|
|
743
|
+
const skipSessions = options.skip ? new Set(conflicts.sessions) : void 0;
|
|
744
|
+
const skipMemory = options.skip ? new Set(conflicts.memoryFiles) : void 0;
|
|
745
|
+
await restoreProjectData(cwd, data, {
|
|
746
|
+
skipSessions,
|
|
747
|
+
skipMemory
|
|
748
|
+
});
|
|
749
|
+
if (options.skip && hasConflicts(conflicts)) {
|
|
750
|
+
const skipped = conflicts.sessions.length + conflicts.memoryFiles.length;
|
|
751
|
+
console.log(`Pulled successfully. Skipped ${skipped} conflicting file(s).`);
|
|
752
|
+
} else {
|
|
753
|
+
console.log("Pulled successfully. Sessions restored to Claude Code.");
|
|
754
|
+
}
|
|
650
755
|
}
|
|
651
756
|
|
|
652
757
|
// src/commands/push.ts
|
|
653
|
-
import { join as
|
|
758
|
+
import { join as join8 } from "path";
|
|
654
759
|
async function push(options) {
|
|
655
760
|
const cwd = process.cwd();
|
|
656
761
|
const project = await detectProject(cwd);
|
|
@@ -677,7 +782,7 @@ async function push(options) {
|
|
|
677
782
|
if (error instanceof ConflictError) throw error;
|
|
678
783
|
}
|
|
679
784
|
}
|
|
680
|
-
const projectDir =
|
|
785
|
+
const projectDir = join8(repoDir, "projects", project.projectId);
|
|
681
786
|
await writeCheckpoint(projectDir, project, data);
|
|
682
787
|
await commitAndPush(
|
|
683
788
|
repoDir,
|
|
@@ -688,15 +793,15 @@ async function push(options) {
|
|
|
688
793
|
}
|
|
689
794
|
|
|
690
795
|
// src/commands/status.ts
|
|
691
|
-
import { readdir as readdir3, readFile as
|
|
692
|
-
import { join as
|
|
796
|
+
import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
|
|
797
|
+
import { join as join9 } from "path";
|
|
693
798
|
async function status() {
|
|
694
799
|
const cwd = process.cwd();
|
|
695
800
|
const project = await detectProject(cwd);
|
|
696
801
|
console.log(`Project: ${project.normalizedRemote}`);
|
|
697
802
|
console.log(`Project ID: ${project.projectId}`);
|
|
698
803
|
const projectDirName = encodeProjectDir(cwd);
|
|
699
|
-
const localProjectDir =
|
|
804
|
+
const localProjectDir = join9(getClaudeProjectsDir(), projectDirName);
|
|
700
805
|
try {
|
|
701
806
|
const entries = await readdir3(localProjectDir);
|
|
702
807
|
const sessionCount = entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
@@ -715,9 +820,9 @@ async function status() {
|
|
|
715
820
|
console.log("Remote checkpoint: not cloned yet");
|
|
716
821
|
return;
|
|
717
822
|
}
|
|
718
|
-
const metaPath =
|
|
823
|
+
const metaPath = join9(repoDir, "projects", project.projectId, "meta.json");
|
|
719
824
|
try {
|
|
720
|
-
const raw = await
|
|
825
|
+
const raw = await readFile6(metaPath, "utf-8");
|
|
721
826
|
const meta = JSON.parse(raw);
|
|
722
827
|
console.log(`Last pushed: ${meta.pushed_at ?? "unknown"}`);
|
|
723
828
|
} catch {
|
|
@@ -727,12 +832,12 @@ async function status() {
|
|
|
727
832
|
|
|
728
833
|
// src/cli.ts
|
|
729
834
|
var program = new Command();
|
|
730
|
-
program.name("baton").description("Git-backed session handoff for Claude Code").version("0.3.
|
|
835
|
+
program.name("baton").description("Git-backed session handoff for Claude Code").version("0.3.2");
|
|
731
836
|
program.command("push").description("Push all sessions for the current project to GitHub").option("-f, --force", "Overwrite remote even if ahead").action(async (options) => {
|
|
732
837
|
await push({ force: options.force });
|
|
733
838
|
});
|
|
734
|
-
program.command("pull").description("Restore sessions for the current project from GitHub").option("-f, --force", "Overwrite local
|
|
735
|
-
await pull({ force: options.force });
|
|
839
|
+
program.command("pull").description("Restore sessions for the current project from GitHub").option("-f, --force", "Overwrite all local with remote").option("-s, --skip", "Only pull non-conflicting files").action(async (options) => {
|
|
840
|
+
await pull({ force: options.force, skip: options.skip });
|
|
736
841
|
});
|
|
737
842
|
program.command("status").description("Show current project and sync state").action(async () => {
|
|
738
843
|
await status();
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/pull.ts","../src/adapters/claude-code/reader.ts","../src/errors.ts","../src/adapters/claude-code/paths.ts","../src/adapters/claude-code/writer.ts","../src/core/config.ts","../src/core/git.ts","../src/core/paths.ts","../src/core/project.ts","../src/commands/checkpoint.ts","../src/commands/setup.ts","../src/commands/push.ts","../src/commands/status.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { pull } from \"./commands/pull.js\";\nimport { push } from \"./commands/push.js\";\nimport { status } from \"./commands/status.js\";\nimport { BatonError } from \"./errors.js\";\n\ndeclare const __VERSION__: string;\n\nconst program = new Command();\n\nprogram\n\t.name(\"baton\")\n\t.description(\"Git-backed session handoff for Claude Code\")\n\t.version(__VERSION__);\n\nprogram\n\t.command(\"push\")\n\t.description(\"Push all sessions for the current project to GitHub\")\n\t.option(\"-f, --force\", \"Overwrite remote even if ahead\")\n\t.action(async (options) => {\n\t\tawait push({ force: options.force });\n\t});\n\nprogram\n\t.command(\"pull\")\n\t.description(\"Restore sessions for the current project from GitHub\")\n\t.option(\"-f, --force\", \"Overwrite local sessions without confirmation\")\n\t.action(async (options) => {\n\t\tawait pull({ force: options.force });\n\t});\n\nprogram\n\t.command(\"status\")\n\t.description(\"Show current project and sync state\")\n\t.action(async () => {\n\t\tawait status();\n\t});\n\ntry {\n\tawait program.parseAsync(process.argv);\n} catch (error) {\n\tif (error instanceof BatonError) {\n\t\tconsole.error(`Error: ${error.message}`);\n\t} else {\n\t\tconsole.error(\n\t\t\t`Unexpected error: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n\tprocess.exit(1);\n}\n","import { join } from \"node:path\";\nimport { listLocalSessionIds } from \"../adapters/claude-code/reader.js\";\nimport { restoreProjectData } from \"../adapters/claude-code/writer.js\";\nimport { getRepoDir } from \"../core/config.js\";\nimport { pullRepo, repoExists } from \"../core/git.js\";\nimport { expandPaths, getLocalPathContext } from \"../core/paths.js\";\nimport { detectProject } from \"../core/project.js\";\nimport { ConflictError, NoSessionsError } from \"../errors.js\";\nimport { readCheckpoint } from \"./checkpoint.js\";\nimport { ensureBatonRepo } from \"./setup.js\";\n\nexport async function pull(options: { force?: boolean }): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// 1. Detect project\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote} (${project.projectId})`);\n\n\t// 2. Ensure baton repo is available\n\tawait ensureBatonRepo(\"pull\");\n\n\tconst repoDir = getRepoDir();\n\tif (await repoExists(repoDir)) {\n\t\tconsole.log(\"Pulling latest...\");\n\t\tawait pullRepo(repoDir);\n\t}\n\n\t// 3. Read checkpoint from baton repo\n\tconst projectDir = join(repoDir, \"projects\", project.projectId);\n\tconst data = await readCheckpoint(projectDir);\n\n\tif (!data) {\n\t\tthrow new NoSessionsError(\n\t\t\t\"No checkpoint found for this project. Run 'baton push' on another machine first.\",\n\t\t);\n\t}\n\n\tconsole.log(\n\t\t`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`,\n\t);\n\n\t// 4. Check for local sessions that would be overwritten\n\tif (!options.force) {\n\t\tconst localIds = await listLocalSessionIds(cwd);\n\t\tconst remoteIds = new Set(data.sessions.map((s) => s.sessionId));\n\t\tconst conflicts = localIds.filter((id) => remoteIds.has(id));\n\n\t\tif (conflicts.length > 0) {\n\t\t\tthrow new ConflictError(\n\t\t\t\t`${conflicts.length} local session(s) would be overwritten. Use 'baton pull --force' to proceed.`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// 5. Expand paths\n\tconst pathCtx = getLocalPathContext(cwd);\n\tfor (const session of data.sessions) {\n\t\tsession.jsonl = expandPaths(session.jsonl, pathCtx);\n\t}\n\n\t// 6. Restore to Claude Code's local storage\n\tawait restoreProjectData(cwd, data);\n\n\tconsole.log(\"Pulled successfully. Sessions restored to Claude Code.\");\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport { NoSessionsError } from \"../../errors.js\";\nimport { encodeProjectDir, getClaudeProjectsDir } from \"./paths.js\";\n\nexport interface SessionData {\n\tsessionId: string;\n\tjsonl: string;\n\ttoolResults: Map<string, string>;\n}\n\nexport interface ProjectData {\n\tsessions: SessionData[];\n\tmemory: Map<string, string>;\n\tprojectDirName: string;\n}\n\n/**\n * List local session IDs for a project (lightweight, no content read).\n */\nexport async function listLocalSessionIds(\n\tprojectPath: string,\n): Promise<string[]> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(projectDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn entries\n\t\t.filter((e) => e.endsWith(\".jsonl\"))\n\t\t.map((e) => basename(e, \".jsonl\"));\n}\n\n/**\n * Collect all session data for a project from Claude Code's local storage.\n */\nexport async function collectProjectData(\n\tprojectPath: string,\n): Promise<ProjectData> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tconst sessions = await collectSessions(projectDir);\n\tif (sessions.length === 0) {\n\t\tthrow new NoSessionsError(\n\t\t\t`No Claude Code sessions found for this project. Start a Claude Code session first.`,\n\t\t);\n\t}\n\n\tconst memory = await collectMemory(projectDir);\n\n\treturn { sessions, memory, projectDirName };\n}\n\nasync function collectSessions(projectDir: string): Promise<SessionData[]> {\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(projectDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\tconst jsonlFiles = entries.filter((e) => e.endsWith(\".jsonl\"));\n\tconst sessions: SessionData[] = [];\n\n\tfor (const jsonlFile of jsonlFiles) {\n\t\tconst sessionId = basename(jsonlFile, \".jsonl\");\n\t\tconst jsonl = await readFile(join(projectDir, jsonlFile), \"utf-8\");\n\t\tconst toolResults = await collectToolResults(projectDir, sessionId);\n\t\tsessions.push({ sessionId, jsonl, toolResults });\n\t}\n\n\treturn sessions;\n}\n\nasync function collectToolResults(\n\tprojectDir: string,\n\tsessionId: string,\n): Promise<Map<string, string>> {\n\tconst results = new Map<string, string>();\n\tconst toolResultsDir = join(projectDir, sessionId, \"tool-results\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(toolResultsDir);\n\t} catch {\n\t\treturn results;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst filePath = join(toolResultsDir, entry);\n\t\tconst fileStat = await stat(filePath);\n\t\tif (fileStat.isFile()) {\n\t\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\t\tresults.set(entry, content);\n\t\t}\n\t}\n\n\treturn results;\n}\n\nasync function collectMemory(projectDir: string): Promise<Map<string, string>> {\n\tconst memory = new Map<string, string>();\n\tconst memoryDir = join(projectDir, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn memory;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst filePath = join(memoryDir, entry);\n\t\tconst fileStat = await stat(filePath);\n\t\tif (fileStat.isFile()) {\n\t\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\t\tmemory.set(entry, content);\n\t\t}\n\t}\n\n\treturn memory;\n}\n","export class BatonError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = this.constructor.name;\n\t\tError.captureStackTrace?.(this, this.constructor);\n\t}\n}\n\nexport class ProjectNotFoundError extends BatonError {}\n\nexport class NoSessionsError extends BatonError {}\n\nexport class ConflictError extends BatonError {}\n\nexport class GitNotFoundError extends BatonError {}\n\nexport class GhNotFoundError extends BatonError {}\n\nexport class ConfigError extends BatonError {}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Encode a local project path into a Claude Code project directory name.\n * Claude Code replaces `/`, `.`, and `:` with `-`.\n *\n * Examples:\n * /home/dr_who/baton → -home-dr_who-baton\n * /Users/dr_who/work/baton → -Users-dr_who-work-baton\n * C:\\Users\\dr_who\\baton → -C-Users-dr_who-baton\n */\nexport function encodeProjectDir(projectPath: string): string {\n\t// Normalize backslashes to forward slashes (Windows support)\n\tconst normalized = projectPath.replace(/\\\\/g, \"/\");\n\treturn normalized.replace(/[/.:]/g, \"-\");\n}\n\n/**\n * Get the Claude Code projects base directory.\n */\nexport function getClaudeProjectsDir(): string {\n\treturn join(homedir(), \".claude\", \"projects\");\n}\n\n/**\n * Get the full path to a Claude Code project directory.\n */\nexport function getClaudeProjectPath(projectPath: string): string {\n\treturn join(getClaudeProjectsDir(), encodeProjectDir(projectPath));\n}\n\n/**\n * Get the path to Claude Code's project-config.json.\n */\nexport function getProjectConfigPath(): string {\n\treturn join(homedir(), \".claude\", \"project-config.json\");\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n\tencodeProjectDir,\n\tgetClaudeProjectsDir,\n\tgetProjectConfigPath,\n} from \"./paths.js\";\nimport type { ProjectData } from \"./reader.js\";\n\n/**\n * Restore project data to Claude Code's local storage.\n */\nexport async function restoreProjectData(\n\tprojectPath: string,\n\tdata: ProjectData,\n): Promise<void> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tawait mkdir(projectDir, { recursive: true });\n\n\tfor (const session of data.sessions) {\n\t\t// Write session JSONL\n\t\tawait writeFile(\n\t\t\tjoin(projectDir, `${session.sessionId}.jsonl`),\n\t\t\tsession.jsonl,\n\t\t\t\"utf-8\",\n\t\t);\n\n\t\t// Write tool results\n\t\tif (session.toolResults.size > 0) {\n\t\t\tconst toolResultsDir = join(\n\t\t\t\tprojectDir,\n\t\t\t\tsession.sessionId,\n\t\t\t\t\"tool-results\",\n\t\t\t);\n\t\t\tawait mkdir(toolResultsDir, { recursive: true });\n\t\t\tfor (const [filename, content] of session.toolResults) {\n\t\t\t\tawait writeFile(join(toolResultsDir, filename), content, \"utf-8\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Write memory files\n\tif (data.memory.size > 0) {\n\t\tconst memoryDir = join(projectDir, \"memory\");\n\t\tawait mkdir(memoryDir, { recursive: true });\n\t\tfor (const [filename, content] of data.memory) {\n\t\t\tawait writeFile(join(memoryDir, filename), content, \"utf-8\");\n\t\t}\n\t}\n\n\t// Ensure project-config.json has a mapping\n\tawait ensureProjectConfig(projectPath, projectDirName);\n}\n\nasync function ensureProjectConfig(\n\tprojectPath: string,\n\tprojectDirName: string,\n): Promise<void> {\n\tconst configPath = getProjectConfigPath();\n\n\tlet config: Record<\n\t\tstring,\n\t\t{ manuallyAdded?: boolean; originalPath: string }\n\t> = {};\n\ttry {\n\t\tconst raw = await readFile(configPath, \"utf-8\");\n\t\tconfig = JSON.parse(raw);\n\t} catch {\n\t\t// File doesn't exist or is invalid, start fresh\n\t}\n\n\tif (!config[projectDirName]) {\n\t\tconfig[projectDirName] = {\n\t\t\toriginalPath: projectPath,\n\t\t};\n\t\tawait mkdir(dirname(configPath), { recursive: true });\n\t\tawait writeFile(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n\t}\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport interface BatonConfig {\n\trepo: string;\n}\n\n/**\n * Get the path to the baton config directory.\n */\nexport function getBatonDir(): string {\n\treturn join(homedir(), \".baton\");\n}\n\n/**\n * Get the path to the baton config file.\n */\nexport function getConfigPath(): string {\n\treturn join(getBatonDir(), \"config.json\");\n}\n\n/**\n * Get the path to the local repo clone.\n */\nexport function getRepoDir(): string {\n\treturn join(getBatonDir(), \"repo\");\n}\n\n/**\n * Load baton config. Returns null if not configured.\n */\nexport async function loadConfig(): Promise<BatonConfig | null> {\n\ttry {\n\t\tconst raw = await readFile(getConfigPath(), \"utf-8\");\n\t\tconst config = JSON.parse(raw);\n\t\tif (!config.repo || typeof config.repo !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\t\treturn config as BatonConfig;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Save baton config.\n */\nexport async function saveConfig(config: BatonConfig): Promise<void> {\n\tconst configPath = getConfigPath();\n\tawait mkdir(dirname(configPath), { recursive: true });\n\tawait writeFile(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n","import { execFile } from \"node:child_process\";\nimport { access } from \"node:fs/promises\";\nimport { promisify } from \"node:util\";\nimport { GhNotFoundError, GitNotFoundError } from \"../errors.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_TIMEOUT_MS = 30_000;\nconst GH_TIMEOUT_MS = 60_000;\n\n/**\n * Run a git command in the given directory.\n */\nexport async function git(args: string[], cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\"git\", args, {\n\t\t\tcwd,\n\t\t\ttimeout: GIT_TIMEOUT_MS,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch (error) {\n\t\tif (isNotFound(error)) {\n\t\t\tthrow new GitNotFoundError(\"git is not installed or not found in PATH.\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Run a gh CLI command.\n */\nexport async function gh(args: string[]): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\"gh\", args, {\n\t\t\ttimeout: GH_TIMEOUT_MS,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch (error) {\n\t\tif (isNotFound(error)) {\n\t\t\tthrow new GhNotFoundError(\n\t\t\t\t\"gh CLI is not installed. Install it from https://cli.github.com/\",\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if the local repo clone exists.\n */\nexport async function repoExists(repoDir: string): Promise<boolean> {\n\ttry {\n\t\tawait access(repoDir);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Clone a repo to the local cache directory.\n */\nexport async function cloneRepo(\n\trepoUrl: string,\n\ttargetDir: string,\n): Promise<void> {\n\tawait execFileAsync(\"git\", [\"clone\", repoUrl, targetDir], {\n\t\ttimeout: GH_TIMEOUT_MS,\n\t});\n}\n\n/**\n * Fetch latest from remote without merging.\n */\nexport async function fetchRepo(repoDir: string): Promise<void> {\n\tawait git([\"fetch\", \"origin\"], repoDir);\n}\n\n/**\n * Pull latest from remote (fast-forward).\n */\nexport async function pullRepo(repoDir: string): Promise<void> {\n\tawait git([\"pull\", \"--ff-only\"], repoDir);\n}\n\n/**\n * Check if remote is ahead of local (has commits we haven't pulled).\n */\nexport async function isRemoteAhead(repoDir: string): Promise<boolean> {\n\tawait fetchRepo(repoDir);\n\tconst localHead = await git([\"rev-parse\", \"HEAD\"], repoDir);\n\tconst remoteHead = await git([\"rev-parse\", \"origin/main\"], repoDir);\n\treturn localHead !== remoteHead;\n}\n\n/**\n * Stage, commit, and push changes.\n */\nexport async function commitAndPush(\n\trepoDir: string,\n\tmessage: string,\n\tforce: boolean,\n): Promise<void> {\n\tawait git([\"add\", \"-A\"], repoDir);\n\n\t// Check if there are changes to commit\n\tconst status = await git([\"status\", \"--porcelain\"], repoDir);\n\tif (!status) {\n\t\treturn; // Nothing to commit\n\t}\n\n\tawait git([\"commit\", \"-m\", message], repoDir);\n\n\tconst pushArgs = force\n\t\t? [\"push\", \"--force\", \"origin\", \"main\"]\n\t\t: [\"push\", \"origin\", \"main\"];\n\tawait git(pushArgs, repoDir);\n}\n\n/**\n * Create a private GitHub repo via gh CLI.\n */\nexport async function createGhRepo(repoName: string): Promise<string> {\n\tconst output = await gh([\n\t\t\"repo\",\n\t\t\"create\",\n\t\trepoName,\n\t\t\"--private\",\n\t\t\"--confirm\",\n\t]);\n\t// gh repo create outputs the URL\n\tconst match = output.match(/https:\\/\\/github\\.com\\/[\\w.-]+\\/[\\w.-]+/);\n\tif (match) {\n\t\treturn match[0];\n\t}\n\t// Fallback: construct from gh whoami\n\tconst username = await gh([\"api\", \"user\", \"--jq\", \".login\"]);\n\treturn `https://github.com/${username}/${repoName}`;\n}\n\n/**\n * Initialize a new git repo with an initial commit.\n */\nexport async function initRepo(repoDir: string): Promise<void> {\n\tawait git([\"init\", \"-b\", \"main\"], repoDir);\n\tawait git([\"config\", \"user.name\", \"baton\"], repoDir);\n\tawait git([\"config\", \"user.email\", \"baton@localhost\"], repoDir);\n\tawait git([\"commit\", \"--allow-empty\", \"-m\", \"init baton repo\"], repoDir);\n}\n\nfunction isNotFound(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\treturn (error as NodeJS.ErrnoException).code === \"ENOENT\";\n}\n","import { homedir, tmpdir } from \"node:os\";\nimport { sep } from \"node:path\";\n\nconst PLACEHOLDER_PROJECT_ROOT = \"$\" + \"{PROJECT_ROOT}\";\nconst PLACEHOLDER_HOME = \"$\" + \"{HOME}\";\nconst PLACEHOLDER_TMP = \"$\" + \"{TMP}\";\n\nconst PLACEHOLDERS = {\n\tPROJECT_ROOT: PLACEHOLDER_PROJECT_ROOT,\n\tHOME: PLACEHOLDER_HOME,\n\tTMP: PLACEHOLDER_TMP,\n} as const;\n\ninterface PathContext {\n\tprojectRoot: string;\n\thome: string;\n\ttmp: string;\n}\n\n/**\n * Get the current machine's path context.\n */\nexport function getLocalPathContext(projectRoot: string): PathContext {\n\treturn {\n\t\tprojectRoot,\n\t\thome: homedir(),\n\t\ttmp: tmpdir(),\n\t};\n}\n\n/**\n * Virtualize paths in content: replace machine-local absolute paths\n * with portable placeholders.\n *\n * Replaces longest paths first to prevent partial matches.\n * Normalizes path separators to `/` in stored content.\n */\nexport function virtualizePaths(content: string, ctx: PathContext): string {\n\t// Normalize backslashes to forward slashes for consistent matching\n\tconst normalizedContent =\n\t\tsep === \"\\\\\" ? content.replace(/\\\\/g, \"/\") : content;\n\n\t// Build replacement pairs, sorted by path length (longest first)\n\tconst replacements: [string, string][] = (\n\t\t[\n\t\t\t[normalizeSeparators(ctx.projectRoot), PLACEHOLDERS.PROJECT_ROOT],\n\t\t\t[normalizeSeparators(ctx.home), PLACEHOLDERS.HOME],\n\t\t\t[normalizeSeparators(ctx.tmp), PLACEHOLDERS.TMP],\n\t\t] as [string, string][]\n\t).sort((a, b) => b[0].length - a[0].length);\n\n\tlet result = normalizedContent;\n\tfor (const [path, placeholder] of replacements) {\n\t\tif (path) {\n\t\t\tresult = replacePathWithBoundary(result, path, placeholder);\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Expand placeholders in content: replace portable placeholders\n * with machine-local absolute paths.\n *\n * Expands to OS-native path separators.\n */\nexport function expandPaths(content: string, ctx: PathContext): string {\n\tlet result = content;\n\n\tresult = replaceAll(\n\t\tresult,\n\t\tPLACEHOLDERS.PROJECT_ROOT,\n\t\ttoNativePath(ctx.projectRoot),\n\t);\n\tresult = replaceAll(result, PLACEHOLDERS.HOME, toNativePath(ctx.home));\n\tresult = replaceAll(result, PLACEHOLDERS.TMP, toNativePath(ctx.tmp));\n\n\treturn result;\n}\n\n/**\n * Normalize path separators to forward slashes.\n */\nfunction normalizeSeparators(path: string): string {\n\treturn path.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Convert a path to use the OS-native separator.\n */\nfunction toNativePath(path: string): string {\n\tif (sep === \"\\\\\") {\n\t\treturn path.replace(/\\//g, \"\\\\\");\n\t}\n\treturn path;\n}\n\n/**\n * Replace all occurrences of a path, but only when followed by a path\n * boundary character (/, \\, \", whitespace, end of string, etc.).\n * Prevents matching /home/dr_who inside /home/dr_who_backup.\n */\nfunction replacePathWithBoundary(\n\tstr: string,\n\tpath: string,\n\treplacement: string,\n): string {\n\tconst escaped = escapeRegex(path);\n\tconst regex = new RegExp(`${escaped}(?=[/\\\\\\\\\"\\\\s,}\\\\]]|$)`, \"g\");\n\treturn str.replace(regex, replacement);\n}\n\n/**\n * Replace all occurrences of a substring.\n */\nfunction replaceAll(str: string, search: string, replacement: string): string {\n\treturn str.split(search).join(replacement);\n}\n\n/**\n * Escape special regex characters in a string.\n */\nfunction escapeRegex(str: string): string {\n\treturn str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\nimport { GitNotFoundError, ProjectNotFoundError } from \"../errors.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_TIMEOUT_MS = 10_000;\n\n/**\n * Detect the git remote URL from the current working directory.\n * Uses the \"origin\" remote by default.\n */\nexport async function getGitRemote(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\n\t\t\t\"git\",\n\t\t\t[\"remote\", \"get-url\", \"origin\"],\n\t\t\t{ cwd, timeout: GIT_TIMEOUT_MS },\n\t\t);\n\t\tconst remote = stdout.trim();\n\t\tif (!remote) {\n\t\t\tthrow new ProjectNotFoundError(\n\t\t\t\t\"No git remote URL found. Run this command from a git repository with an 'origin' remote.\",\n\t\t\t);\n\t\t}\n\t\treturn remote;\n\t} catch (error) {\n\t\tif (error instanceof ProjectNotFoundError) throw error;\n\t\tif (isGitNotInstalled(error)) {\n\t\t\tthrow new GitNotFoundError(\n\t\t\t\t\"git is not installed or not found in PATH. Please install git first.\",\n\t\t\t);\n\t\t}\n\t\tthrow new ProjectNotFoundError(\n\t\t\t\"Not a git repository or no 'origin' remote configured. Run this command from a git repository.\",\n\t\t);\n\t}\n}\n\nfunction isGitNotInstalled(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst err = error as NodeJS.ErrnoException;\n\treturn err.code === \"ENOENT\";\n}\n\n/**\n * Normalize a git remote URL to a canonical form.\n *\n * Handles:\n * - git@github.com:user/repo.git → github.com/user/repo\n * - https://github.com/user/repo.git → github.com/user/repo\n * - https://github.com/user/repo → github.com/user/repo\n * - ssh://git@github.com/user/repo.git → github.com/user/repo\n * - git://github.com/user/repo.git → github.com/user/repo\n */\nexport function normalizeGitRemote(remote: string): string {\n\tconst normalized = remote.trim();\n\n\t// SSH shorthand: git@github.com:user/repo.git\n\tconst sshMatch = normalized.match(/^[\\w.-]+@([\\w.-]+):(.*?)(?:\\.git)?$/);\n\tif (sshMatch) {\n\t\treturn `${sshMatch[1]}/${sshMatch[2]}`;\n\t}\n\n\t// ssh:// or git:// protocol: ssh://git@github.com/user/repo.git\n\tconst protoMatch = normalized.match(\n\t\t/^(?:ssh|git):\\/\\/(?:[\\w.-]+@)?([\\w.-]+)\\/(.*?)(?:\\.git)?$/,\n\t);\n\tif (protoMatch) {\n\t\treturn `${protoMatch[1]}/${protoMatch[2]}`;\n\t}\n\n\t// HTTPS: https://github.com/user/repo.git\n\tconst httpsMatch = normalized.match(\n\t\t/^https?:\\/\\/([\\w.-]+)\\/(.*?)(?:\\.git)?$/,\n\t);\n\tif (httpsMatch) {\n\t\treturn `${httpsMatch[1]}/${httpsMatch[2]}`;\n\t}\n\n\t// Fallback: return as-is (stripped)\n\treturn normalized;\n}\n\n/**\n * Hash a normalized git remote URL to produce a stable project ID.\n */\nexport function hashProjectId(normalizedRemote: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(normalizedRemote)\n\t\t.digest(\"hex\")\n\t\t.slice(0, 16);\n}\n\n/**\n * Detect the project from the current working directory.\n * Returns the project hash and normalized git remote.\n */\nexport async function detectProject(\n\tcwd: string,\n): Promise<{ projectId: string; gitRemote: string; normalizedRemote: string }> {\n\tconst gitRemote = await getGitRemote(cwd);\n\tconst normalizedRemote = normalizeGitRemote(gitRemote);\n\tconst projectId = hashProjectId(normalizedRemote);\n\treturn { projectId, gitRemote, normalizedRemote };\n}\n","import { mkdir, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n\tProjectData,\n\tSessionData,\n} from \"../adapters/claude-code/reader.js\";\n\ninterface ProjectMeta {\n\tproject_id: string;\n\tgit_remote: string;\n\tpushed_at: string;\n}\n\n/**\n * Write project data as a checkpoint to the baton repo.\n */\nexport async function writeCheckpoint(\n\tprojectDir: string,\n\tproject: { projectId: string; gitRemote: string },\n\tdata: ProjectData,\n): Promise<void> {\n\t// Clean existing sessions directory to remove stale sessions\n\tconst sessionsDir = join(projectDir, \"sessions\");\n\tawait rm(sessionsDir, { recursive: true, force: true });\n\tawait mkdir(sessionsDir, { recursive: true });\n\n\t// Write meta\n\tconst meta: ProjectMeta = {\n\t\tproject_id: project.projectId,\n\t\tgit_remote: project.gitRemote,\n\t\tpushed_at: new Date().toISOString(),\n\t};\n\tawait writeFile(\n\t\tjoin(projectDir, \"meta.json\"),\n\t\tJSON.stringify(meta, null, 2),\n\t\t\"utf-8\",\n\t);\n\n\t// Write sessions\n\tfor (const session of data.sessions) {\n\t\tawait writeFile(\n\t\t\tjoin(sessionsDir, `${session.sessionId}.jsonl`),\n\t\t\tsession.jsonl,\n\t\t\t\"utf-8\",\n\t\t);\n\n\t\t// Write tool results\n\t\tif (session.toolResults.size > 0) {\n\t\t\tconst toolResultsDir = join(\n\t\t\t\tsessionsDir,\n\t\t\t\tsession.sessionId,\n\t\t\t\t\"tool-results\",\n\t\t\t);\n\t\t\tawait mkdir(toolResultsDir, { recursive: true });\n\t\t\tfor (const [filename, content] of session.toolResults) {\n\t\t\t\tawait writeFile(join(toolResultsDir, filename), content, \"utf-8\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Write memory\n\tif (data.memory.size > 0) {\n\t\tconst memoryDir = join(projectDir, \"memory\");\n\t\tawait mkdir(memoryDir, { recursive: true });\n\t\tfor (const [filename, content] of data.memory) {\n\t\t\tawait writeFile(join(memoryDir, filename), content, \"utf-8\");\n\t\t}\n\t}\n}\n\n/**\n * Read a checkpoint from the baton repo. Returns null if not found.\n */\nexport async function readCheckpoint(\n\tprojectDir: string,\n): Promise<ProjectData | null> {\n\tconst sessionsDir = join(projectDir, \"sessions\");\n\n\tlet sessionFiles: string[];\n\ttry {\n\t\tconst entries = await readdir(sessionsDir);\n\t\tsessionFiles = entries.filter((e) => e.endsWith(\".jsonl\"));\n\t} catch {\n\t\treturn null;\n\t}\n\n\tif (sessionFiles.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst sessions: SessionData[] = [];\n\tfor (const file of sessionFiles) {\n\t\tconst sessionId = file.replace(\".jsonl\", \"\");\n\t\tconst jsonl = await readFile(join(sessionsDir, file), \"utf-8\");\n\t\tconst toolResults = await readToolResults(sessionsDir, sessionId);\n\t\tsessions.push({ sessionId, jsonl, toolResults });\n\t}\n\n\tconst memory = await readMemory(projectDir);\n\n\treturn {\n\t\tsessions,\n\t\tmemory,\n\t\tprojectDirName: \"\",\n\t};\n}\n\nasync function readToolResults(\n\tsessionsDir: string,\n\tsessionId: string,\n): Promise<Map<string, string>> {\n\tconst results = new Map<string, string>();\n\tconst toolResultsDir = join(sessionsDir, sessionId, \"tool-results\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(toolResultsDir);\n\t} catch {\n\t\treturn results;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst content = await readFile(join(toolResultsDir, entry), \"utf-8\");\n\t\tresults.set(entry, content);\n\t}\n\n\treturn results;\n}\n\nasync function readMemory(projectDir: string): Promise<Map<string, string>> {\n\tconst memory = new Map<string, string>();\n\tconst memoryDir = join(projectDir, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn memory;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst content = await readFile(join(memoryDir, entry), \"utf-8\");\n\t\tmemory.set(entry, content);\n\t}\n\n\treturn memory;\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { getRepoDir, loadConfig, saveConfig } from \"../core/config.js\";\nimport { cloneRepo, createGhRepo, gh, repoExists } from \"../core/git.js\";\n\nconst DEFAULT_REPO_NAME = \"baton-sessions\";\n\n/**\n * Ensure the baton repo is configured and cloned locally.\n * On first run:\n * - push: auto-creates a private \"baton-sessions\" repo\n * - pull: tries the current user's \"baton-sessions\" repo, prompts if not found\n */\nexport async function ensureBatonRepo(mode: \"push\" | \"pull\"): Promise<void> {\n\tconst repoDir = getRepoDir();\n\tlet config = await loadConfig();\n\n\tif (!config) {\n\t\tif (mode === \"push\") {\n\t\t\tconfig = await promptCreateRepo();\n\t\t} else {\n\t\t\tconfig = await autoDetectOrPrompt();\n\t\t}\n\t\tawait saveConfig(config);\n\t}\n\n\tif (!(await repoExists(repoDir))) {\n\t\tconsole.log(\"Cloning baton repo...\");\n\t\tawait cloneRepo(config.repo, repoDir);\n\t}\n}\n\nasync function getGitHubUsername(): Promise<string> {\n\treturn await gh([\"api\", \"user\", \"--jq\", \".login\"]);\n}\n\nasync function promptCreateRepo(): Promise<{ repo: string }> {\n\tconst rl = createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stdout,\n\t});\n\ttry {\n\t\tconst rawName = await rl.question(\n\t\t\t`Enter repo name for baton sync (${DEFAULT_REPO_NAME}): `,\n\t\t);\n\t\tconst repoName = rawName.trim() || DEFAULT_REPO_NAME;\n\t\tconsole.log(`Creating private repo '${repoName}'...`);\n\t\tconst repoUrl = await createGhRepo(repoName);\n\t\tconsole.log(`Repo created: ${repoUrl}`);\n\t\treturn { repo: repoUrl };\n\t} finally {\n\t\trl.close();\n\t}\n}\n\nasync function autoDetectOrPrompt(): Promise<{ repo: string }> {\n\t// Try the current user's default repo name first\n\tconst username = await getGitHubUsername();\n\tconst defaultUrl = `https://github.com/${username}/${DEFAULT_REPO_NAME}`;\n\n\ttry {\n\t\t// Check if the repo exists\n\t\tawait gh([\n\t\t\t\"repo\",\n\t\t\t\"view\",\n\t\t\t`${username}/${DEFAULT_REPO_NAME}`,\n\t\t\t\"--json\",\n\t\t\t\"name\",\n\t\t]);\n\t\tconsole.log(`Found baton repo: ${defaultUrl}`);\n\t\treturn { repo: defaultUrl };\n\t} catch (error) {\n\t\t// Re-throw if not a \"repo not found\" error (e.g., auth, network, gh not installed)\n\t\tif (!isRepoNotFoundError(error)) {\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Repo doesn't exist, prompt for URL\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\ttry {\n\t\t\tconst rawUrl = await rl.question(\n\t\t\t\t`No '${DEFAULT_REPO_NAME}' repo found for ${username}. Enter your baton repo URL: `,\n\t\t\t);\n\t\t\tconst repoUrl = rawUrl.trim();\n\t\t\tif (!repoUrl) {\n\t\t\t\tthrow new Error(\"Repo URL cannot be empty.\");\n\t\t\t}\n\t\t\treturn { repo: repoUrl };\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\t}\n}\n\nfunction isRepoNotFoundError(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst stderr = (error as { stderr?: string }).stderr ?? \"\";\n\tconst message = error.message + stderr;\n\treturn (\n\t\tmessage.includes(\"Could not resolve\") ||\n\t\tmessage.includes(\"HTTP 404\") ||\n\t\tmessage.includes(\"Not Found\")\n\t);\n}\n","import { join } from \"node:path\";\nimport { collectProjectData } from \"../adapters/claude-code/reader.js\";\nimport { getRepoDir } from \"../core/config.js\";\nimport { commitAndPush, isRemoteAhead, repoExists } from \"../core/git.js\";\nimport { getLocalPathContext, virtualizePaths } from \"../core/paths.js\";\nimport { detectProject } from \"../core/project.js\";\nimport { ConflictError } from \"../errors.js\";\nimport { writeCheckpoint } from \"./checkpoint.js\";\nimport { ensureBatonRepo } from \"./setup.js\";\n\nexport async function push(options: { force?: boolean }): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// 1. Detect project\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote} (${project.projectId})`);\n\n\t// 2. Collect session data\n\tconst data = await collectProjectData(cwd);\n\tconsole.log(\n\t\t`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`,\n\t);\n\n\t// 3. Virtualize paths\n\tconst pathCtx = getLocalPathContext(cwd);\n\tfor (const session of data.sessions) {\n\t\tsession.jsonl = virtualizePaths(session.jsonl, pathCtx);\n\t}\n\n\t// 4. Ensure baton repo exists\n\tconst repoDir = getRepoDir();\n\tawait ensureBatonRepo(\"push\");\n\n\t// 5. Conflict check\n\tif (!options.force && (await repoExists(repoDir))) {\n\t\ttry {\n\t\t\tconst ahead = await isRemoteAhead(repoDir);\n\t\t\tif (ahead) {\n\t\t\t\tthrow new ConflictError(\n\t\t\t\t\t\"Remote has changes you haven't pulled. Run 'baton pull' first, or use 'baton push --force' to overwrite.\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof ConflictError) throw error;\n\t\t\t// Ignore fetch errors (e.g., empty repo with no commits on remote yet)\n\t\t}\n\t}\n\n\t// 6. Write checkpoint to baton repo\n\tconst projectDir = join(repoDir, \"projects\", project.projectId);\n\tawait writeCheckpoint(projectDir, project, data);\n\n\t// 7. Commit and push\n\tawait commitAndPush(\n\t\trepoDir,\n\t\t`push: ${project.normalizedRemote}`,\n\t\toptions.force ?? false,\n\t);\n\n\tconsole.log(\"Pushed successfully.\");\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n\tencodeProjectDir,\n\tgetClaudeProjectsDir,\n} from \"../adapters/claude-code/paths.js\";\nimport { getRepoDir, loadConfig } from \"../core/config.js\";\nimport { repoExists } from \"../core/git.js\";\nimport { detectProject } from \"../core/project.js\";\n\nexport async function status(): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// Project info\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote}`);\n\tconsole.log(`Project ID: ${project.projectId}`);\n\n\t// Local sessions\n\tconst projectDirName = encodeProjectDir(cwd);\n\tconst localProjectDir = join(getClaudeProjectsDir(), projectDirName);\n\ttry {\n\t\tconst entries = await readdir(localProjectDir);\n\t\tconst sessionCount = entries.filter((e) => e.endsWith(\".jsonl\")).length;\n\t\tconsole.log(`Local sessions: ${sessionCount}`);\n\t} catch {\n\t\tconsole.log(\"Local sessions: 0\");\n\t}\n\n\t// Baton config\n\tconst config = await loadConfig();\n\tif (!config) {\n\t\tconsole.log(\"Baton repo: not configured (run 'baton push' to set up)\");\n\t\treturn;\n\t}\n\tconsole.log(`Baton repo: ${config.repo}`);\n\n\t// Remote checkpoint\n\tconst repoDir = getRepoDir();\n\tif (!(await repoExists(repoDir))) {\n\t\tconsole.log(\"Remote checkpoint: not cloned yet\");\n\t\treturn;\n\t}\n\n\tconst metaPath = join(repoDir, \"projects\", project.projectId, \"meta.json\");\n\ttry {\n\t\tconst raw = await readFile(metaPath, \"utf-8\");\n\t\tconst meta = JSON.parse(raw);\n\t\tconsole.log(`Last pushed: ${meta.pushed_at ?? \"unknown\"}`);\n\t} catch {\n\t\tconsole.log(\"Remote checkpoint: none for this project\");\n\t}\n}\n"],"mappings":"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","names":["join","join","join","readFile","join","join","readFile","mkdir","readFile","writeFile","homedir","dirname","join","status","homedir","execFile","promisify","execFileAsync","promisify","execFile","GIT_TIMEOUT_MS","mkdir","readdir","readFile","writeFile","join","join","join","join","readdir","readFile","join","join","readdir","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/pull.ts","../src/adapters/claude-code/paths.ts","../src/adapters/claude-code/reader.ts","../src/errors.ts","../src/adapters/claude-code/writer.ts","../src/core/config.ts","../src/core/conflicts.ts","../src/core/git.ts","../src/core/paths.ts","../src/core/project.ts","../src/commands/checkpoint.ts","../src/commands/setup.ts","../src/commands/push.ts","../src/commands/status.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { pull } from \"./commands/pull.js\";\nimport { push } from \"./commands/push.js\";\nimport { status } from \"./commands/status.js\";\nimport { BatonError } from \"./errors.js\";\n\ndeclare const __VERSION__: string;\n\nconst program = new Command();\n\nprogram\n\t.name(\"baton\")\n\t.description(\"Git-backed session handoff for Claude Code\")\n\t.version(__VERSION__);\n\nprogram\n\t.command(\"push\")\n\t.description(\"Push all sessions for the current project to GitHub\")\n\t.option(\"-f, --force\", \"Overwrite remote even if ahead\")\n\t.action(async (options) => {\n\t\tawait push({ force: options.force });\n\t});\n\nprogram\n\t.command(\"pull\")\n\t.description(\"Restore sessions for the current project from GitHub\")\n\t.option(\"-f, --force\", \"Overwrite all local with remote\")\n\t.option(\"-s, --skip\", \"Only pull non-conflicting files\")\n\t.action(async (options) => {\n\t\tawait pull({ force: options.force, skip: options.skip });\n\t});\n\nprogram\n\t.command(\"status\")\n\t.description(\"Show current project and sync state\")\n\t.action(async () => {\n\t\tawait status();\n\t});\n\ntry {\n\tawait program.parseAsync(process.argv);\n} catch (error) {\n\tif (error instanceof BatonError) {\n\t\tconsole.error(`Error: ${error.message}`);\n\t} else {\n\t\tconsole.error(\n\t\t\t`Unexpected error: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n\tprocess.exit(1);\n}\n","import { join } from \"node:path\";\nimport {\n\tencodeProjectDir,\n\tgetClaudeProjectsDir,\n} from \"../adapters/claude-code/paths.js\";\nimport {\n\tlistLocalMemoryFiles,\n\tlistLocalSessionIds,\n} from \"../adapters/claude-code/reader.js\";\nimport { restoreProjectData } from \"../adapters/claude-code/writer.js\";\nimport { getRepoDir } from \"../core/config.js\";\nimport {\n\tdetectConflicts,\n\tformatConflictMessage,\n\thasConflicts,\n} from \"../core/conflicts.js\";\nimport { pullRepo, repoExists } from \"../core/git.js\";\nimport { expandPaths, getLocalPathContext } from \"../core/paths.js\";\nimport { detectProject } from \"../core/project.js\";\nimport { ConflictError, NoSessionsError } from \"../errors.js\";\nimport { readCheckpoint } from \"./checkpoint.js\";\nimport { ensureBatonRepo } from \"./setup.js\";\n\nexport async function pull(options: {\n\tforce?: boolean;\n\tskip?: boolean;\n}): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// 1. Detect project\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote} (${project.projectId})`);\n\n\t// 2. Ensure baton repo is available\n\tawait ensureBatonRepo(\"pull\");\n\n\tconst repoDir = getRepoDir();\n\tif (await repoExists(repoDir)) {\n\t\tconsole.log(\"Pulling latest...\");\n\t\tawait pullRepo(repoDir);\n\t}\n\n\t// 3. Read checkpoint from baton repo\n\tconst projectDir = join(repoDir, \"projects\", project.projectId);\n\tconst data = await readCheckpoint(projectDir);\n\n\tif (!data) {\n\t\tthrow new NoSessionsError(\n\t\t\t\"No checkpoint found for this project. Run 'baton push' on another machine first.\",\n\t\t);\n\t}\n\n\tconsole.log(\n\t\t`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`,\n\t);\n\n\t// 4. Expand paths before conflict detection (so content comparison is meaningful)\n\tconst pathCtx = getLocalPathContext(cwd);\n\tfor (const session of data.sessions) {\n\t\tsession.jsonl = expandPaths(session.jsonl, pathCtx);\n\t}\n\n\t// 5. Detect content-based conflicts\n\tconst localProjectDir = join(getClaudeProjectsDir(), encodeProjectDir(cwd));\n\tconst remoteSessionsDir = join(projectDir, \"sessions\");\n\tconst remoteMemoryDir = join(projectDir, \"memory\");\n\n\tconst localSessionIds = await listLocalSessionIds(cwd);\n\tconst localMemoryFiles = await listLocalMemoryFiles(cwd);\n\tconst remoteSessionIds = data.sessions.map((s) => s.sessionId);\n\tconst remoteMemoryFiles = [...data.memory.keys()];\n\n\tconst conflicts = await detectConflicts(\n\t\tlocalSessionIds,\n\t\tremoteSessionIds,\n\t\tlocalMemoryFiles,\n\t\tremoteMemoryFiles,\n\t\t{ localProjectDir, remoteSessionsDir, remoteMemoryDir },\n\t);\n\n\tif (hasConflicts(conflicts) && !options.force && !options.skip) {\n\t\tthrow new ConflictError(formatConflictMessage(conflicts, remoteMemoryDir));\n\t}\n\n\t// 6. Restore to Claude Code's local storage\n\tconst skipSessions = options.skip ? new Set(conflicts.sessions) : undefined;\n\tconst skipMemory = options.skip ? new Set(conflicts.memoryFiles) : undefined;\n\n\tawait restoreProjectData(cwd, data, {\n\t\tskipSessions,\n\t\tskipMemory,\n\t});\n\n\tif (options.skip && hasConflicts(conflicts)) {\n\t\tconst skipped = conflicts.sessions.length + conflicts.memoryFiles.length;\n\t\tconsole.log(`Pulled successfully. Skipped ${skipped} conflicting file(s).`);\n\t} else {\n\t\tconsole.log(\"Pulled successfully. Sessions restored to Claude Code.\");\n\t}\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Encode a local project path into a Claude Code project directory name.\n * Claude Code replaces `/`, `.`, and `:` with `-`.\n *\n * Examples:\n * /home/dr_who/baton → -home-dr_who-baton\n * /Users/dr_who/work/baton → -Users-dr_who-work-baton\n * C:\\Users\\dr_who\\baton → -C-Users-dr_who-baton\n */\nexport function encodeProjectDir(projectPath: string): string {\n\t// Normalize backslashes to forward slashes (Windows support)\n\tconst normalized = projectPath.replace(/\\\\/g, \"/\");\n\treturn normalized.replace(/[/.:]/g, \"-\");\n}\n\n/**\n * Get the Claude Code projects base directory.\n */\nexport function getClaudeProjectsDir(): string {\n\treturn join(homedir(), \".claude\", \"projects\");\n}\n\n/**\n * Get the full path to a Claude Code project directory.\n */\nexport function getClaudeProjectPath(projectPath: string): string {\n\treturn join(getClaudeProjectsDir(), encodeProjectDir(projectPath));\n}\n\n/**\n * Get the path to Claude Code's project-config.json.\n */\nexport function getProjectConfigPath(): string {\n\treturn join(homedir(), \".claude\", \"project-config.json\");\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport { NoSessionsError } from \"../../errors.js\";\nimport { encodeProjectDir, getClaudeProjectsDir } from \"./paths.js\";\n\nexport interface SessionData {\n\tsessionId: string;\n\tjsonl: string;\n\ttoolResults: Map<string, string>;\n}\n\nexport interface ProjectData {\n\tsessions: SessionData[];\n\tmemory: Map<string, string>;\n\tprojectDirName: string;\n}\n\n/**\n * List local session IDs for a project (lightweight, no content read).\n */\nexport async function listLocalSessionIds(\n\tprojectPath: string,\n): Promise<string[]> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(projectDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn entries\n\t\t.filter((e) => e.endsWith(\".jsonl\"))\n\t\t.map((e) => basename(e, \".jsonl\"));\n}\n\n/**\n * List local memory file names for a project (lightweight, no content read).\n */\nexport async function listLocalMemoryFiles(\n\tprojectPath: string,\n): Promise<string[]> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst memoryDir = join(getClaudeProjectsDir(), projectDirName, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\n/**\n * Collect all session data for a project from Claude Code's local storage.\n */\nexport async function collectProjectData(\n\tprojectPath: string,\n): Promise<ProjectData> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tconst sessions = await collectSessions(projectDir);\n\tif (sessions.length === 0) {\n\t\tthrow new NoSessionsError(\n\t\t\t`No Claude Code sessions found for this project. Start a Claude Code session first.`,\n\t\t);\n\t}\n\n\tconst memory = await collectMemory(projectDir);\n\n\treturn { sessions, memory, projectDirName };\n}\n\nasync function collectSessions(projectDir: string): Promise<SessionData[]> {\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(projectDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\tconst jsonlFiles = entries.filter((e) => e.endsWith(\".jsonl\"));\n\tconst sessions: SessionData[] = [];\n\n\tfor (const jsonlFile of jsonlFiles) {\n\t\tconst sessionId = basename(jsonlFile, \".jsonl\");\n\t\tconst jsonl = await readFile(join(projectDir, jsonlFile), \"utf-8\");\n\t\tconst toolResults = await collectToolResults(projectDir, sessionId);\n\t\tsessions.push({ sessionId, jsonl, toolResults });\n\t}\n\n\treturn sessions;\n}\n\nasync function collectToolResults(\n\tprojectDir: string,\n\tsessionId: string,\n): Promise<Map<string, string>> {\n\tconst results = new Map<string, string>();\n\tconst toolResultsDir = join(projectDir, sessionId, \"tool-results\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(toolResultsDir);\n\t} catch {\n\t\treturn results;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst filePath = join(toolResultsDir, entry);\n\t\tconst fileStat = await stat(filePath);\n\t\tif (fileStat.isFile()) {\n\t\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\t\tresults.set(entry, content);\n\t\t}\n\t}\n\n\treturn results;\n}\n\nasync function collectMemory(projectDir: string): Promise<Map<string, string>> {\n\tconst memory = new Map<string, string>();\n\tconst memoryDir = join(projectDir, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn memory;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst filePath = join(memoryDir, entry);\n\t\tconst fileStat = await stat(filePath);\n\t\tif (fileStat.isFile()) {\n\t\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\t\tmemory.set(entry, content);\n\t\t}\n\t}\n\n\treturn memory;\n}\n","export class BatonError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = this.constructor.name;\n\t\tError.captureStackTrace?.(this, this.constructor);\n\t}\n}\n\nexport class ProjectNotFoundError extends BatonError {}\n\nexport class NoSessionsError extends BatonError {}\n\nexport class ConflictError extends BatonError {}\n\nexport class GitNotFoundError extends BatonError {}\n\nexport class GhNotFoundError extends BatonError {}\n\nexport class ConfigError extends BatonError {}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n\tencodeProjectDir,\n\tgetClaudeProjectsDir,\n\tgetProjectConfigPath,\n} from \"./paths.js\";\nimport type { ProjectData } from \"./reader.js\";\n\nexport interface RestoreOptions {\n\t/** Session IDs to skip (don't overwrite) */\n\tskipSessions?: Set<string>;\n\t/** Memory file names to skip (don't overwrite) */\n\tskipMemory?: Set<string>;\n}\n\n/**\n * Restore project data to Claude Code's local storage.\n */\nexport async function restoreProjectData(\n\tprojectPath: string,\n\tdata: ProjectData,\n\toptions?: RestoreOptions,\n): Promise<void> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tawait mkdir(projectDir, { recursive: true });\n\n\tconst skipSessions = options?.skipSessions ?? new Set();\n\tconst skipMemory = options?.skipMemory ?? new Set();\n\n\tfor (const session of data.sessions) {\n\t\tif (skipSessions.has(session.sessionId)) continue;\n\n\t\t// Write session JSONL\n\t\tawait writeFile(\n\t\t\tjoin(projectDir, `${session.sessionId}.jsonl`),\n\t\t\tsession.jsonl,\n\t\t\t\"utf-8\",\n\t\t);\n\n\t\t// Write tool results\n\t\tif (session.toolResults.size > 0) {\n\t\t\tconst toolResultsDir = join(\n\t\t\t\tprojectDir,\n\t\t\t\tsession.sessionId,\n\t\t\t\t\"tool-results\",\n\t\t\t);\n\t\t\tawait mkdir(toolResultsDir, { recursive: true });\n\t\t\tfor (const [filename, content] of session.toolResults) {\n\t\t\t\tawait writeFile(join(toolResultsDir, filename), content, \"utf-8\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Write memory files\n\tif (data.memory.size > 0) {\n\t\tconst memoryDir = join(projectDir, \"memory\");\n\t\tawait mkdir(memoryDir, { recursive: true });\n\t\tfor (const [filename, content] of data.memory) {\n\t\t\tif (skipMemory.has(filename)) continue;\n\t\t\tawait writeFile(join(memoryDir, filename), content, \"utf-8\");\n\t\t}\n\t}\n\n\t// Ensure project-config.json has a mapping\n\tawait ensureProjectConfig(projectPath, projectDirName);\n}\n\nasync function ensureProjectConfig(\n\tprojectPath: string,\n\tprojectDirName: string,\n): Promise<void> {\n\tconst configPath = getProjectConfigPath();\n\n\tlet config: Record<\n\t\tstring,\n\t\t{ manuallyAdded?: boolean; originalPath: string }\n\t> = {};\n\ttry {\n\t\tconst raw = await readFile(configPath, \"utf-8\");\n\t\tconfig = JSON.parse(raw);\n\t} catch {\n\t\t// File doesn't exist or is invalid, start fresh\n\t}\n\n\tif (!config[projectDirName]) {\n\t\tconfig[projectDirName] = {\n\t\t\toriginalPath: projectPath,\n\t\t};\n\t\tawait mkdir(dirname(configPath), { recursive: true });\n\t\tawait writeFile(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n\t}\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport interface BatonConfig {\n\trepo: string;\n}\n\n/**\n * Get the path to the baton config directory.\n */\nexport function getBatonDir(): string {\n\treturn join(homedir(), \".baton\");\n}\n\n/**\n * Get the path to the baton config file.\n */\nexport function getConfigPath(): string {\n\treturn join(getBatonDir(), \"config.json\");\n}\n\n/**\n * Get the path to the local repo clone.\n */\nexport function getRepoDir(): string {\n\treturn join(getBatonDir(), \"repo\");\n}\n\n/**\n * Load baton config. Returns null if not configured.\n */\nexport async function loadConfig(): Promise<BatonConfig | null> {\n\ttry {\n\t\tconst raw = await readFile(getConfigPath(), \"utf-8\");\n\t\tconst config = JSON.parse(raw);\n\t\tif (!config.repo || typeof config.repo !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\t\treturn config as BatonConfig;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Save baton config.\n */\nexport async function saveConfig(config: BatonConfig): Promise<void> {\n\tconst configPath = getConfigPath();\n\tawait mkdir(dirname(configPath), { recursive: true });\n\tawait writeFile(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface ConflictInfo {\n\t/** Sessions that exist both locally and remotely with different content */\n\tsessions: string[];\n\t/** Memory files that exist both locally and remotely with different content */\n\tmemoryFiles: string[];\n}\n\nexport interface ConflictContext {\n\t/** Local Claude Code project directory */\n\tlocalProjectDir: string;\n\t/** Remote checkpoint sessions directory in baton repo */\n\tremoteSessionsDir: string;\n\t/** Remote checkpoint memory directory in baton repo */\n\tremoteMemoryDir: string;\n}\n\n/**\n * Detect content-based conflicts between local and remote data.\n * Only reports files that actually differ in content.\n * Identical files are not conflicts (safe to overwrite with same data).\n */\nexport async function detectConflicts(\n\tlocalSessionIds: string[],\n\tremoteSessionIds: string[],\n\tlocalMemoryFiles: string[],\n\tremoteMemoryFiles: string[],\n\tctx: ConflictContext,\n): Promise<ConflictInfo> {\n\tconst remoteIdSet = new Set(remoteSessionIds);\n\tconst overlappingSessionIds = localSessionIds.filter((id) =>\n\t\tremoteIdSet.has(id),\n\t);\n\n\tconst remoteMemSet = new Set(remoteMemoryFiles);\n\tconst overlappingMemory = localMemoryFiles.filter((f) => remoteMemSet.has(f));\n\n\tconst sessions: string[] = [];\n\tfor (const id of overlappingSessionIds) {\n\t\tconst localPath = join(ctx.localProjectDir, `${id}.jsonl`);\n\t\tconst remotePath = join(ctx.remoteSessionsDir, `${id}.jsonl`);\n\t\tif (await contentDiffers(localPath, remotePath)) {\n\t\t\tsessions.push(id);\n\t\t}\n\t}\n\n\tconst memoryFiles: string[] = [];\n\tfor (const file of overlappingMemory) {\n\t\tconst localPath = join(ctx.localProjectDir, \"memory\", file);\n\t\tconst remotePath = join(ctx.remoteMemoryDir, file);\n\t\tif (await contentDiffers(localPath, remotePath)) {\n\t\t\tmemoryFiles.push(file);\n\t\t}\n\t}\n\n\treturn { sessions, memoryFiles };\n}\n\n/**\n * Check if two files have different content.\n * Returns true if content differs, false if identical.\n * Returns false if either file can't be read (treat as no conflict).\n */\nasync function contentDiffers(pathA: string, pathB: string): Promise<boolean> {\n\ttry {\n\t\tconst [contentA, contentB] = await Promise.all([\n\t\t\treadFile(pathA, \"utf-8\"),\n\t\t\treadFile(pathB, \"utf-8\"),\n\t\t]);\n\t\treturn contentA !== contentB;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Format conflict info into a human-readable (and Claude Code-readable) message.\n */\nexport function formatConflictMessage(\n\tconflicts: ConflictInfo,\n\tremoteMemoryDir: string,\n): string {\n\tconst lines: string[] = [];\n\tlines.push(\"Conflicts detected during baton pull.\");\n\tlines.push(\"\");\n\n\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(`Conflicting sessions (${conflicts.sessions.length}):`);\n\t\tfor (const id of conflicts.sessions) {\n\t\t\tlines.push(` - ${id}`);\n\t\t}\n\t\tlines.push(\"\");\n\t}\n\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(`Conflicting memory files (${conflicts.memoryFiles.length}):`);\n\t\tfor (const file of conflicts.memoryFiles) {\n\t\t\tlines.push(` - ${file}`);\n\t\t\tlines.push(` remote: ${join(remoteMemoryDir, file)}`);\n\t\t}\n\t\tlines.push(\"\");\n\t}\n\n\tlines.push(\"To resolve:\");\n\tlines.push(\" baton pull --force overwrite all local with remote\");\n\tlines.push(\" baton pull --skip only pull non-conflicting files\");\n\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\"\");\n\t\tlines.push(\n\t\t\t\"Tip: ask Claude Code to merge the conflicting memory files by reading both local and remote versions.\",\n\t\t);\n\t}\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Check if there are any conflicts.\n */\nexport function hasConflicts(conflicts: ConflictInfo): boolean {\n\treturn conflicts.sessions.length > 0 || conflicts.memoryFiles.length > 0;\n}\n","import { execFile } from \"node:child_process\";\nimport { access } from \"node:fs/promises\";\nimport { promisify } from \"node:util\";\nimport { GhNotFoundError, GitNotFoundError } from \"../errors.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_TIMEOUT_MS = 30_000;\nconst GH_TIMEOUT_MS = 60_000;\n\n/**\n * Run a git command in the given directory.\n */\nexport async function git(args: string[], cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\"git\", args, {\n\t\t\tcwd,\n\t\t\ttimeout: GIT_TIMEOUT_MS,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch (error) {\n\t\tif (isNotFound(error)) {\n\t\t\tthrow new GitNotFoundError(\"git is not installed or not found in PATH.\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Run a gh CLI command.\n */\nexport async function gh(args: string[]): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\"gh\", args, {\n\t\t\ttimeout: GH_TIMEOUT_MS,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch (error) {\n\t\tif (isNotFound(error)) {\n\t\t\tthrow new GhNotFoundError(\n\t\t\t\t\"gh CLI is not installed. Install it from https://cli.github.com/\",\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if the local repo clone exists.\n */\nexport async function repoExists(repoDir: string): Promise<boolean> {\n\ttry {\n\t\tawait access(repoDir);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Clone a repo to the local cache directory.\n */\nexport async function cloneRepo(\n\trepoUrl: string,\n\ttargetDir: string,\n): Promise<void> {\n\tawait execFileAsync(\"git\", [\"clone\", repoUrl, targetDir], {\n\t\ttimeout: GH_TIMEOUT_MS,\n\t});\n}\n\n/**\n * Fetch latest from remote without merging.\n */\nexport async function fetchRepo(repoDir: string): Promise<void> {\n\tawait git([\"fetch\", \"origin\"], repoDir);\n}\n\n/**\n * Pull latest from remote (fast-forward).\n */\nexport async function pullRepo(repoDir: string): Promise<void> {\n\tawait git([\"pull\", \"--ff-only\"], repoDir);\n}\n\n/**\n * Check if remote is ahead of local (has commits we haven't pulled).\n */\nexport async function isRemoteAhead(repoDir: string): Promise<boolean> {\n\tawait fetchRepo(repoDir);\n\tconst localHead = await git([\"rev-parse\", \"HEAD\"], repoDir);\n\tconst remoteHead = await git([\"rev-parse\", \"origin/main\"], repoDir);\n\treturn localHead !== remoteHead;\n}\n\n/**\n * Stage, commit, and push changes.\n */\nexport async function commitAndPush(\n\trepoDir: string,\n\tmessage: string,\n\tforce: boolean,\n): Promise<void> {\n\tawait git([\"add\", \"-A\"], repoDir);\n\n\t// Check if there are changes to commit\n\tconst status = await git([\"status\", \"--porcelain\"], repoDir);\n\tif (!status) {\n\t\treturn; // Nothing to commit\n\t}\n\n\tawait git([\"commit\", \"-m\", message], repoDir);\n\n\tconst pushArgs = force\n\t\t? [\"push\", \"--force\", \"origin\", \"main\"]\n\t\t: [\"push\", \"origin\", \"main\"];\n\tawait git(pushArgs, repoDir);\n}\n\n/**\n * Create a private GitHub repo via gh CLI.\n */\nexport async function createGhRepo(repoName: string): Promise<string> {\n\tconst output = await gh([\n\t\t\"repo\",\n\t\t\"create\",\n\t\trepoName,\n\t\t\"--private\",\n\t\t\"--confirm\",\n\t]);\n\t// gh repo create outputs the URL\n\tconst match = output.match(/https:\\/\\/github\\.com\\/[\\w.-]+\\/[\\w.-]+/);\n\tif (match) {\n\t\treturn match[0];\n\t}\n\t// Fallback: construct from gh whoami\n\tconst username = await gh([\"api\", \"user\", \"--jq\", \".login\"]);\n\treturn `https://github.com/${username}/${repoName}`;\n}\n\n/**\n * Initialize a new git repo with an initial commit.\n */\nexport async function initRepo(repoDir: string): Promise<void> {\n\tawait git([\"init\", \"-b\", \"main\"], repoDir);\n\tawait git([\"config\", \"user.name\", \"baton\"], repoDir);\n\tawait git([\"config\", \"user.email\", \"baton@localhost\"], repoDir);\n\tawait git([\"commit\", \"--allow-empty\", \"-m\", \"init baton repo\"], repoDir);\n}\n\nfunction isNotFound(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\treturn (error as NodeJS.ErrnoException).code === \"ENOENT\";\n}\n","import { homedir, tmpdir } from \"node:os\";\nimport { sep } from \"node:path\";\n\nconst PLACEHOLDER_PROJECT_ROOT = \"$\" + \"{PROJECT_ROOT}\";\nconst PLACEHOLDER_HOME = \"$\" + \"{HOME}\";\nconst PLACEHOLDER_TMP = \"$\" + \"{TMP}\";\n\nconst PLACEHOLDERS = {\n\tPROJECT_ROOT: PLACEHOLDER_PROJECT_ROOT,\n\tHOME: PLACEHOLDER_HOME,\n\tTMP: PLACEHOLDER_TMP,\n} as const;\n\ninterface PathContext {\n\tprojectRoot: string;\n\thome: string;\n\ttmp: string;\n}\n\n/**\n * Get the current machine's path context.\n */\nexport function getLocalPathContext(projectRoot: string): PathContext {\n\treturn {\n\t\tprojectRoot,\n\t\thome: homedir(),\n\t\ttmp: tmpdir(),\n\t};\n}\n\n/**\n * Virtualize paths in content: replace machine-local absolute paths\n * with portable placeholders.\n *\n * Replaces longest paths first to prevent partial matches.\n * Normalizes path separators to `/` in stored content.\n */\nexport function virtualizePaths(content: string, ctx: PathContext): string {\n\t// Normalize backslashes to forward slashes for consistent matching\n\tconst normalizedContent =\n\t\tsep === \"\\\\\" ? content.replace(/\\\\/g, \"/\") : content;\n\n\t// Build replacement pairs, sorted by path length (longest first)\n\tconst replacements: [string, string][] = (\n\t\t[\n\t\t\t[normalizeSeparators(ctx.projectRoot), PLACEHOLDERS.PROJECT_ROOT],\n\t\t\t[normalizeSeparators(ctx.home), PLACEHOLDERS.HOME],\n\t\t\t[normalizeSeparators(ctx.tmp), PLACEHOLDERS.TMP],\n\t\t] as [string, string][]\n\t).sort((a, b) => b[0].length - a[0].length);\n\n\tlet result = normalizedContent;\n\tfor (const [path, placeholder] of replacements) {\n\t\tif (path) {\n\t\t\tresult = replacePathWithBoundary(result, path, placeholder);\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Expand placeholders in content: replace portable placeholders\n * with machine-local absolute paths.\n *\n * Expands to OS-native path separators.\n */\nexport function expandPaths(content: string, ctx: PathContext): string {\n\tlet result = content;\n\n\tresult = replaceAll(\n\t\tresult,\n\t\tPLACEHOLDERS.PROJECT_ROOT,\n\t\ttoNativePath(ctx.projectRoot),\n\t);\n\tresult = replaceAll(result, PLACEHOLDERS.HOME, toNativePath(ctx.home));\n\tresult = replaceAll(result, PLACEHOLDERS.TMP, toNativePath(ctx.tmp));\n\n\treturn result;\n}\n\n/**\n * Normalize path separators to forward slashes.\n */\nfunction normalizeSeparators(path: string): string {\n\treturn path.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Convert a path to use the OS-native separator.\n */\nfunction toNativePath(path: string): string {\n\tif (sep === \"\\\\\") {\n\t\treturn path.replace(/\\//g, \"\\\\\");\n\t}\n\treturn path;\n}\n\n/**\n * Replace all occurrences of a path, but only when followed by a path\n * boundary character (/, \\, \", whitespace, end of string, etc.).\n * Prevents matching /home/dr_who inside /home/dr_who_backup.\n */\nfunction replacePathWithBoundary(\n\tstr: string,\n\tpath: string,\n\treplacement: string,\n): string {\n\tconst escaped = escapeRegex(path);\n\tconst regex = new RegExp(`${escaped}(?=[/\\\\\\\\\"\\\\s,}\\\\]]|$)`, \"g\");\n\treturn str.replace(regex, replacement);\n}\n\n/**\n * Replace all occurrences of a substring.\n */\nfunction replaceAll(str: string, search: string, replacement: string): string {\n\treturn str.split(search).join(replacement);\n}\n\n/**\n * Escape special regex characters in a string.\n */\nfunction escapeRegex(str: string): string {\n\treturn str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\nimport { GitNotFoundError, ProjectNotFoundError } from \"../errors.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_TIMEOUT_MS = 10_000;\n\n/**\n * Detect the git remote URL from the current working directory.\n * Uses the \"origin\" remote by default.\n */\nexport async function getGitRemote(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\n\t\t\t\"git\",\n\t\t\t[\"remote\", \"get-url\", \"origin\"],\n\t\t\t{ cwd, timeout: GIT_TIMEOUT_MS },\n\t\t);\n\t\tconst remote = stdout.trim();\n\t\tif (!remote) {\n\t\t\tthrow new ProjectNotFoundError(\n\t\t\t\t\"No git remote URL found. Run this command from a git repository with an 'origin' remote.\",\n\t\t\t);\n\t\t}\n\t\treturn remote;\n\t} catch (error) {\n\t\tif (error instanceof ProjectNotFoundError) throw error;\n\t\tif (isGitNotInstalled(error)) {\n\t\t\tthrow new GitNotFoundError(\n\t\t\t\t\"git is not installed or not found in PATH. Please install git first.\",\n\t\t\t);\n\t\t}\n\t\tthrow new ProjectNotFoundError(\n\t\t\t\"Not a git repository or no 'origin' remote configured. Run this command from a git repository.\",\n\t\t);\n\t}\n}\n\nfunction isGitNotInstalled(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst err = error as NodeJS.ErrnoException;\n\treturn err.code === \"ENOENT\";\n}\n\n/**\n * Normalize a git remote URL to a canonical form.\n *\n * Handles:\n * - git@github.com:user/repo.git → github.com/user/repo\n * - https://github.com/user/repo.git → github.com/user/repo\n * - https://github.com/user/repo → github.com/user/repo\n * - ssh://git@github.com/user/repo.git → github.com/user/repo\n * - git://github.com/user/repo.git → github.com/user/repo\n */\nexport function normalizeGitRemote(remote: string): string {\n\tconst normalized = remote.trim();\n\n\t// SSH shorthand: git@github.com:user/repo.git\n\tconst sshMatch = normalized.match(/^[\\w.-]+@([\\w.-]+):(.*?)(?:\\.git)?$/);\n\tif (sshMatch) {\n\t\treturn `${sshMatch[1]}/${sshMatch[2]}`;\n\t}\n\n\t// ssh:// or git:// protocol: ssh://git@github.com/user/repo.git\n\tconst protoMatch = normalized.match(\n\t\t/^(?:ssh|git):\\/\\/(?:[\\w.-]+@)?([\\w.-]+)\\/(.*?)(?:\\.git)?$/,\n\t);\n\tif (protoMatch) {\n\t\treturn `${protoMatch[1]}/${protoMatch[2]}`;\n\t}\n\n\t// HTTPS: https://github.com/user/repo.git\n\tconst httpsMatch = normalized.match(\n\t\t/^https?:\\/\\/([\\w.-]+)\\/(.*?)(?:\\.git)?$/,\n\t);\n\tif (httpsMatch) {\n\t\treturn `${httpsMatch[1]}/${httpsMatch[2]}`;\n\t}\n\n\t// Fallback: return as-is (stripped)\n\treturn normalized;\n}\n\n/**\n * Hash a normalized git remote URL to produce a stable project ID.\n */\nexport function hashProjectId(normalizedRemote: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(normalizedRemote)\n\t\t.digest(\"hex\")\n\t\t.slice(0, 16);\n}\n\n/**\n * Detect the project from the current working directory.\n * Returns the project hash and normalized git remote.\n */\nexport async function detectProject(\n\tcwd: string,\n): Promise<{ projectId: string; gitRemote: string; normalizedRemote: string }> {\n\tconst gitRemote = await getGitRemote(cwd);\n\tconst normalizedRemote = normalizeGitRemote(gitRemote);\n\tconst projectId = hashProjectId(normalizedRemote);\n\treturn { projectId, gitRemote, normalizedRemote };\n}\n","import { mkdir, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n\tProjectData,\n\tSessionData,\n} from \"../adapters/claude-code/reader.js\";\n\ninterface ProjectMeta {\n\tproject_id: string;\n\tgit_remote: string;\n\tpushed_at: string;\n}\n\n/**\n * Write project data as a checkpoint to the baton repo.\n */\nexport async function writeCheckpoint(\n\tprojectDir: string,\n\tproject: { projectId: string; gitRemote: string },\n\tdata: ProjectData,\n): Promise<void> {\n\t// Clean existing sessions directory to remove stale sessions\n\tconst sessionsDir = join(projectDir, \"sessions\");\n\tawait rm(sessionsDir, { recursive: true, force: true });\n\tawait mkdir(sessionsDir, { recursive: true });\n\n\t// Write meta\n\tconst meta: ProjectMeta = {\n\t\tproject_id: project.projectId,\n\t\tgit_remote: project.gitRemote,\n\t\tpushed_at: new Date().toISOString(),\n\t};\n\tawait writeFile(\n\t\tjoin(projectDir, \"meta.json\"),\n\t\tJSON.stringify(meta, null, 2),\n\t\t\"utf-8\",\n\t);\n\n\t// Write sessions\n\tfor (const session of data.sessions) {\n\t\tawait writeFile(\n\t\t\tjoin(sessionsDir, `${session.sessionId}.jsonl`),\n\t\t\tsession.jsonl,\n\t\t\t\"utf-8\",\n\t\t);\n\n\t\t// Write tool results\n\t\tif (session.toolResults.size > 0) {\n\t\t\tconst toolResultsDir = join(\n\t\t\t\tsessionsDir,\n\t\t\t\tsession.sessionId,\n\t\t\t\t\"tool-results\",\n\t\t\t);\n\t\t\tawait mkdir(toolResultsDir, { recursive: true });\n\t\t\tfor (const [filename, content] of session.toolResults) {\n\t\t\t\tawait writeFile(join(toolResultsDir, filename), content, \"utf-8\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Write memory\n\tif (data.memory.size > 0) {\n\t\tconst memoryDir = join(projectDir, \"memory\");\n\t\tawait mkdir(memoryDir, { recursive: true });\n\t\tfor (const [filename, content] of data.memory) {\n\t\t\tawait writeFile(join(memoryDir, filename), content, \"utf-8\");\n\t\t}\n\t}\n}\n\n/**\n * Read a checkpoint from the baton repo. Returns null if not found.\n */\nexport async function readCheckpoint(\n\tprojectDir: string,\n): Promise<ProjectData | null> {\n\tconst sessionsDir = join(projectDir, \"sessions\");\n\n\tlet sessionFiles: string[];\n\ttry {\n\t\tconst entries = await readdir(sessionsDir);\n\t\tsessionFiles = entries.filter((e) => e.endsWith(\".jsonl\"));\n\t} catch {\n\t\treturn null;\n\t}\n\n\tif (sessionFiles.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst sessions: SessionData[] = [];\n\tfor (const file of sessionFiles) {\n\t\tconst sessionId = file.replace(\".jsonl\", \"\");\n\t\tconst jsonl = await readFile(join(sessionsDir, file), \"utf-8\");\n\t\tconst toolResults = await readToolResults(sessionsDir, sessionId);\n\t\tsessions.push({ sessionId, jsonl, toolResults });\n\t}\n\n\tconst memory = await readMemory(projectDir);\n\n\treturn {\n\t\tsessions,\n\t\tmemory,\n\t\tprojectDirName: \"\",\n\t};\n}\n\nasync function readToolResults(\n\tsessionsDir: string,\n\tsessionId: string,\n): Promise<Map<string, string>> {\n\tconst results = new Map<string, string>();\n\tconst toolResultsDir = join(sessionsDir, sessionId, \"tool-results\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(toolResultsDir);\n\t} catch {\n\t\treturn results;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst content = await readFile(join(toolResultsDir, entry), \"utf-8\");\n\t\tresults.set(entry, content);\n\t}\n\n\treturn results;\n}\n\nasync function readMemory(projectDir: string): Promise<Map<string, string>> {\n\tconst memory = new Map<string, string>();\n\tconst memoryDir = join(projectDir, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn memory;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst content = await readFile(join(memoryDir, entry), \"utf-8\");\n\t\tmemory.set(entry, content);\n\t}\n\n\treturn memory;\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { getRepoDir, loadConfig, saveConfig } from \"../core/config.js\";\nimport { cloneRepo, createGhRepo, gh, repoExists } from \"../core/git.js\";\n\nconst DEFAULT_REPO_NAME = \"baton-sessions\";\n\n/**\n * Ensure the baton repo is configured and cloned locally.\n * On first run:\n * - push: auto-creates a private \"baton-sessions\" repo\n * - pull: tries the current user's \"baton-sessions\" repo, prompts if not found\n */\nexport async function ensureBatonRepo(mode: \"push\" | \"pull\"): Promise<void> {\n\tconst repoDir = getRepoDir();\n\tlet config = await loadConfig();\n\n\tif (!config) {\n\t\tif (mode === \"push\") {\n\t\t\tconfig = await promptCreateRepo();\n\t\t} else {\n\t\t\tconfig = await autoDetectOrPrompt();\n\t\t}\n\t\tawait saveConfig(config);\n\t}\n\n\tif (!(await repoExists(repoDir))) {\n\t\tconsole.log(\"Cloning baton repo...\");\n\t\tawait cloneRepo(config.repo, repoDir);\n\t}\n}\n\nasync function getGitHubUsername(): Promise<string> {\n\treturn await gh([\"api\", \"user\", \"--jq\", \".login\"]);\n}\n\nasync function promptCreateRepo(): Promise<{ repo: string }> {\n\tconst rl = createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stdout,\n\t});\n\ttry {\n\t\tconst rawName = await rl.question(\n\t\t\t`Enter repo name for baton sync (${DEFAULT_REPO_NAME}): `,\n\t\t);\n\t\tconst repoName = rawName.trim() || DEFAULT_REPO_NAME;\n\t\tconsole.log(`Creating private repo '${repoName}'...`);\n\t\tconst repoUrl = await createGhRepo(repoName);\n\t\tconsole.log(`Repo created: ${repoUrl}`);\n\t\treturn { repo: repoUrl };\n\t} finally {\n\t\trl.close();\n\t}\n}\n\nasync function autoDetectOrPrompt(): Promise<{ repo: string }> {\n\t// Try the current user's default repo name first\n\tconst username = await getGitHubUsername();\n\tconst defaultUrl = `https://github.com/${username}/${DEFAULT_REPO_NAME}`;\n\n\ttry {\n\t\t// Check if the repo exists\n\t\tawait gh([\n\t\t\t\"repo\",\n\t\t\t\"view\",\n\t\t\t`${username}/${DEFAULT_REPO_NAME}`,\n\t\t\t\"--json\",\n\t\t\t\"name\",\n\t\t]);\n\t\tconsole.log(`Found baton repo: ${defaultUrl}`);\n\t\treturn { repo: defaultUrl };\n\t} catch (error) {\n\t\t// Re-throw if not a \"repo not found\" error (e.g., auth, network, gh not installed)\n\t\tif (!isRepoNotFoundError(error)) {\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Repo doesn't exist, prompt for URL\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\ttry {\n\t\t\tconst rawUrl = await rl.question(\n\t\t\t\t`No '${DEFAULT_REPO_NAME}' repo found for ${username}. Enter your baton repo URL: `,\n\t\t\t);\n\t\t\tconst repoUrl = rawUrl.trim();\n\t\t\tif (!repoUrl) {\n\t\t\t\tthrow new Error(\"Repo URL cannot be empty.\");\n\t\t\t}\n\t\t\treturn { repo: repoUrl };\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\t}\n}\n\nfunction isRepoNotFoundError(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst stderr = (error as { stderr?: string }).stderr ?? \"\";\n\tconst message = error.message + stderr;\n\treturn (\n\t\tmessage.includes(\"Could not resolve\") ||\n\t\tmessage.includes(\"HTTP 404\") ||\n\t\tmessage.includes(\"Not Found\")\n\t);\n}\n","import { join } from \"node:path\";\nimport { collectProjectData } from \"../adapters/claude-code/reader.js\";\nimport { getRepoDir } from \"../core/config.js\";\nimport { commitAndPush, isRemoteAhead, repoExists } from \"../core/git.js\";\nimport { getLocalPathContext, virtualizePaths } from \"../core/paths.js\";\nimport { detectProject } from \"../core/project.js\";\nimport { ConflictError } from \"../errors.js\";\nimport { writeCheckpoint } from \"./checkpoint.js\";\nimport { ensureBatonRepo } from \"./setup.js\";\n\nexport async function push(options: { force?: boolean }): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// 1. Detect project\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote} (${project.projectId})`);\n\n\t// 2. Collect session data\n\tconst data = await collectProjectData(cwd);\n\tconsole.log(\n\t\t`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`,\n\t);\n\n\t// 3. Virtualize paths\n\tconst pathCtx = getLocalPathContext(cwd);\n\tfor (const session of data.sessions) {\n\t\tsession.jsonl = virtualizePaths(session.jsonl, pathCtx);\n\t}\n\n\t// 4. Ensure baton repo exists\n\tconst repoDir = getRepoDir();\n\tawait ensureBatonRepo(\"push\");\n\n\t// 5. Conflict check\n\tif (!options.force && (await repoExists(repoDir))) {\n\t\ttry {\n\t\t\tconst ahead = await isRemoteAhead(repoDir);\n\t\t\tif (ahead) {\n\t\t\t\tthrow new ConflictError(\n\t\t\t\t\t\"Remote has changes you haven't pulled. Run 'baton pull' first, or use 'baton push --force' to overwrite.\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof ConflictError) throw error;\n\t\t\t// Ignore fetch errors (e.g., empty repo with no commits on remote yet)\n\t\t}\n\t}\n\n\t// 6. Write checkpoint to baton repo\n\tconst projectDir = join(repoDir, \"projects\", project.projectId);\n\tawait writeCheckpoint(projectDir, project, data);\n\n\t// 7. Commit and push\n\tawait commitAndPush(\n\t\trepoDir,\n\t\t`push: ${project.normalizedRemote}`,\n\t\toptions.force ?? false,\n\t);\n\n\tconsole.log(\"Pushed successfully.\");\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n\tencodeProjectDir,\n\tgetClaudeProjectsDir,\n} from \"../adapters/claude-code/paths.js\";\nimport { getRepoDir, loadConfig } from \"../core/config.js\";\nimport { repoExists } from \"../core/git.js\";\nimport { detectProject } from \"../core/project.js\";\n\nexport async function status(): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// Project info\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote}`);\n\tconsole.log(`Project ID: ${project.projectId}`);\n\n\t// Local sessions\n\tconst projectDirName = encodeProjectDir(cwd);\n\tconst localProjectDir = join(getClaudeProjectsDir(), projectDirName);\n\ttry {\n\t\tconst entries = await readdir(localProjectDir);\n\t\tconst sessionCount = entries.filter((e) => e.endsWith(\".jsonl\")).length;\n\t\tconsole.log(`Local sessions: ${sessionCount}`);\n\t} catch {\n\t\tconsole.log(\"Local sessions: 0\");\n\t}\n\n\t// Baton config\n\tconst config = await loadConfig();\n\tif (!config) {\n\t\tconsole.log(\"Baton repo: not configured (run 'baton push' to set up)\");\n\t\treturn;\n\t}\n\tconsole.log(`Baton repo: ${config.repo}`);\n\n\t// Remote checkpoint\n\tconst repoDir = getRepoDir();\n\tif (!(await repoExists(repoDir))) {\n\t\tconsole.log(\"Remote checkpoint: not cloned yet\");\n\t\treturn;\n\t}\n\n\tconst metaPath = join(repoDir, \"projects\", project.projectId, \"meta.json\");\n\ttry {\n\t\tconst raw = await readFile(metaPath, \"utf-8\");\n\t\tconst meta = JSON.parse(raw);\n\t\tconsole.log(`Last pushed: ${meta.pushed_at ?? \"unknown\"}`);\n\t} catch {\n\t\tconsole.log(\"Remote checkpoint: none for this project\");\n\t}\n}\n"],"mappings":"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","names":["join","join","join","readFile","join","join","readFile","mkdir","readFile","writeFile","homedir","dirname","join","readFile","join","status","homedir","execFile","promisify","execFileAsync","promisify","execFile","GIT_TIMEOUT_MS","mkdir","readdir","readFile","writeFile","join","join","join","join","readdir","readFile","join","join","readdir","readFile"]}
|