baton-cli 0.3.2 → 0.5.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 +223 -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,143 @@ 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, stat as stat2 } 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({
|
|
239
|
+
name: id,
|
|
240
|
+
localPath,
|
|
241
|
+
remotePath,
|
|
242
|
+
localModified: await getModifiedTime(localPath),
|
|
243
|
+
remoteModified: await getModifiedTime(remotePath)
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const memoryFiles = [];
|
|
248
|
+
for (const file of overlappingMemory) {
|
|
249
|
+
const localPath = join5(ctx.localProjectDir, "memory", file);
|
|
250
|
+
const remotePath = join5(ctx.remoteMemoryDir, file);
|
|
251
|
+
if (await contentDiffers(localPath, remotePath)) {
|
|
252
|
+
memoryFiles.push({
|
|
253
|
+
name: file,
|
|
254
|
+
localPath,
|
|
255
|
+
remotePath,
|
|
256
|
+
localModified: await getModifiedTime(localPath),
|
|
257
|
+
remoteModified: await getModifiedTime(remotePath)
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return { sessions, memoryFiles };
|
|
262
|
+
}
|
|
263
|
+
async function contentDiffers(pathA, pathB) {
|
|
264
|
+
try {
|
|
265
|
+
const [contentA, contentB] = await Promise.all([
|
|
266
|
+
readFile4(pathA, "utf-8"),
|
|
267
|
+
readFile4(pathB, "utf-8")
|
|
268
|
+
]);
|
|
269
|
+
return contentA !== contentB;
|
|
270
|
+
} catch {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async function getModifiedTime(filePath) {
|
|
275
|
+
try {
|
|
276
|
+
const s = await stat2(filePath);
|
|
277
|
+
return formatRelativeTime(s.mtime);
|
|
278
|
+
} catch {
|
|
279
|
+
return "unknown";
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function formatRelativeTime(date) {
|
|
283
|
+
const now = Date.now();
|
|
284
|
+
const diffMs = now - date.getTime();
|
|
285
|
+
const diffMins = Math.floor(diffMs / 6e4);
|
|
286
|
+
const diffHours = Math.floor(diffMs / 36e5);
|
|
287
|
+
const diffDays = Math.floor(diffMs / 864e5);
|
|
288
|
+
if (diffMins < 1) return "just now";
|
|
289
|
+
if (diffMins < 60) return `${diffMins} minute(s) ago`;
|
|
290
|
+
if (diffHours < 24) return `${diffHours} hour(s) ago`;
|
|
291
|
+
return `${diffDays} day(s) ago`;
|
|
292
|
+
}
|
|
293
|
+
function formatConflictMessage(conflicts) {
|
|
294
|
+
const lines = [];
|
|
295
|
+
lines.push("Conflicts detected during baton pull.");
|
|
296
|
+
lines.push("");
|
|
297
|
+
if (conflicts.sessions.length > 0) {
|
|
298
|
+
lines.push(`Conflicting sessions (${conflicts.sessions.length}):`);
|
|
299
|
+
for (const c of conflicts.sessions) {
|
|
300
|
+
lines.push(` - ${c.name}`);
|
|
301
|
+
lines.push(` local: ${c.localPath} (modified ${c.localModified})`);
|
|
302
|
+
lines.push(` remote: ${c.remotePath} (modified ${c.remoteModified})`);
|
|
303
|
+
}
|
|
304
|
+
lines.push("");
|
|
305
|
+
}
|
|
306
|
+
if (conflicts.memoryFiles.length > 0) {
|
|
307
|
+
lines.push(`Conflicting memory files (${conflicts.memoryFiles.length}):`);
|
|
308
|
+
for (const c of conflicts.memoryFiles) {
|
|
309
|
+
lines.push(` - ${c.name}`);
|
|
310
|
+
lines.push(` local: ${c.localPath} (modified ${c.localModified})`);
|
|
311
|
+
lines.push(` remote: ${c.remotePath} (modified ${c.remoteModified})`);
|
|
312
|
+
}
|
|
313
|
+
lines.push("");
|
|
314
|
+
}
|
|
315
|
+
lines.push("Choose how to resolve:");
|
|
316
|
+
lines.push(" 1. Overwrite local with remote (lose local changes)");
|
|
317
|
+
lines.push(
|
|
318
|
+
" 2. Keep local, skip remote (local will overwrite remote on next push)"
|
|
319
|
+
);
|
|
320
|
+
if (conflicts.memoryFiles.length > 0) {
|
|
321
|
+
lines.push(
|
|
322
|
+
" 3. Merge memory files (agent reads both versions and combines them)"
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
lines.push("");
|
|
326
|
+
lines.push("Or use flags directly:");
|
|
327
|
+
lines.push(
|
|
328
|
+
" baton pull --force option 1: overwrite all local with remote"
|
|
329
|
+
);
|
|
330
|
+
lines.push(
|
|
331
|
+
" baton pull --skip option 2: only pull non-conflicting files"
|
|
332
|
+
);
|
|
333
|
+
lines.push("");
|
|
334
|
+
lines.push("<agent-hint>");
|
|
335
|
+
lines.push("Ask the user which option they prefer. Then follow through:");
|
|
336
|
+
lines.push("");
|
|
337
|
+
if (conflicts.sessions.length > 0) {
|
|
338
|
+
lines.push("For sessions (cannot be merged \u2014 pick one version):");
|
|
339
|
+
lines.push(" Option 1: run `baton pull --force`, then `baton push`");
|
|
340
|
+
lines.push(" Option 2: run `baton pull --skip`, then `baton push`");
|
|
341
|
+
lines.push("");
|
|
342
|
+
}
|
|
343
|
+
if (conflicts.memoryFiles.length > 0) {
|
|
344
|
+
lines.push("For memory files (can be merged):");
|
|
345
|
+
lines.push(" Option 1: run `baton pull --force`, then `baton push`");
|
|
346
|
+
lines.push(" Option 2: run `baton pull --skip`, then `baton push`");
|
|
347
|
+
lines.push(" Option 3: read both local and remote files listed above,");
|
|
348
|
+
lines.push(" merge their content (deduplicate, keep most recent info),");
|
|
349
|
+
lines.push(" write merged result to local path,");
|
|
350
|
+
lines.push(" then run `baton pull --skip` and `baton push`");
|
|
351
|
+
lines.push("");
|
|
352
|
+
}
|
|
353
|
+
lines.push("</agent-hint>");
|
|
354
|
+
return lines.join("\n");
|
|
355
|
+
}
|
|
356
|
+
function hasConflicts(conflicts) {
|
|
357
|
+
return conflicts.sessions.length > 0 || conflicts.memoryFiles.length > 0;
|
|
358
|
+
}
|
|
359
|
+
|
|
208
360
|
// src/core/git.ts
|
|
209
361
|
import { execFile } from "child_process";
|
|
210
362
|
import { access } from "fs/promises";
|
|
@@ -429,10 +581,10 @@ async function detectProject(cwd) {
|
|
|
429
581
|
}
|
|
430
582
|
|
|
431
583
|
// src/commands/checkpoint.ts
|
|
432
|
-
import { mkdir as mkdir3, readdir as readdir2, readFile as
|
|
433
|
-
import { join as
|
|
584
|
+
import { mkdir as mkdir3, readdir as readdir2, readFile as readFile5, rm, writeFile as writeFile3 } from "fs/promises";
|
|
585
|
+
import { join as join6 } from "path";
|
|
434
586
|
async function writeCheckpoint(projectDir, project, data) {
|
|
435
|
-
const sessionsDir =
|
|
587
|
+
const sessionsDir = join6(projectDir, "sessions");
|
|
436
588
|
await rm(sessionsDir, { recursive: true, force: true });
|
|
437
589
|
await mkdir3(sessionsDir, { recursive: true });
|
|
438
590
|
const meta = {
|
|
@@ -441,38 +593,38 @@ async function writeCheckpoint(projectDir, project, data) {
|
|
|
441
593
|
pushed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
442
594
|
};
|
|
443
595
|
await writeFile3(
|
|
444
|
-
|
|
596
|
+
join6(projectDir, "meta.json"),
|
|
445
597
|
JSON.stringify(meta, null, 2),
|
|
446
598
|
"utf-8"
|
|
447
599
|
);
|
|
448
600
|
for (const session of data.sessions) {
|
|
449
601
|
await writeFile3(
|
|
450
|
-
|
|
602
|
+
join6(sessionsDir, `${session.sessionId}.jsonl`),
|
|
451
603
|
session.jsonl,
|
|
452
604
|
"utf-8"
|
|
453
605
|
);
|
|
454
606
|
if (session.toolResults.size > 0) {
|
|
455
|
-
const toolResultsDir =
|
|
607
|
+
const toolResultsDir = join6(
|
|
456
608
|
sessionsDir,
|
|
457
609
|
session.sessionId,
|
|
458
610
|
"tool-results"
|
|
459
611
|
);
|
|
460
612
|
await mkdir3(toolResultsDir, { recursive: true });
|
|
461
613
|
for (const [filename, content] of session.toolResults) {
|
|
462
|
-
await writeFile3(
|
|
614
|
+
await writeFile3(join6(toolResultsDir, filename), content, "utf-8");
|
|
463
615
|
}
|
|
464
616
|
}
|
|
465
617
|
}
|
|
466
618
|
if (data.memory.size > 0) {
|
|
467
|
-
const memoryDir =
|
|
619
|
+
const memoryDir = join6(projectDir, "memory");
|
|
468
620
|
await mkdir3(memoryDir, { recursive: true });
|
|
469
621
|
for (const [filename, content] of data.memory) {
|
|
470
|
-
await writeFile3(
|
|
622
|
+
await writeFile3(join6(memoryDir, filename), content, "utf-8");
|
|
471
623
|
}
|
|
472
624
|
}
|
|
473
625
|
}
|
|
474
626
|
async function readCheckpoint(projectDir) {
|
|
475
|
-
const sessionsDir =
|
|
627
|
+
const sessionsDir = join6(projectDir, "sessions");
|
|
476
628
|
let sessionFiles;
|
|
477
629
|
try {
|
|
478
630
|
const entries = await readdir2(sessionsDir);
|
|
@@ -486,7 +638,7 @@ async function readCheckpoint(projectDir) {
|
|
|
486
638
|
const sessions = [];
|
|
487
639
|
for (const file of sessionFiles) {
|
|
488
640
|
const sessionId = file.replace(".jsonl", "");
|
|
489
|
-
const jsonl = await
|
|
641
|
+
const jsonl = await readFile5(join6(sessionsDir, file), "utf-8");
|
|
490
642
|
const toolResults = await readToolResults(sessionsDir, sessionId);
|
|
491
643
|
sessions.push({ sessionId, jsonl, toolResults });
|
|
492
644
|
}
|
|
@@ -499,7 +651,7 @@ async function readCheckpoint(projectDir) {
|
|
|
499
651
|
}
|
|
500
652
|
async function readToolResults(sessionsDir, sessionId) {
|
|
501
653
|
const results = /* @__PURE__ */ new Map();
|
|
502
|
-
const toolResultsDir =
|
|
654
|
+
const toolResultsDir = join6(sessionsDir, sessionId, "tool-results");
|
|
503
655
|
let entries;
|
|
504
656
|
try {
|
|
505
657
|
entries = await readdir2(toolResultsDir);
|
|
@@ -507,14 +659,14 @@ async function readToolResults(sessionsDir, sessionId) {
|
|
|
507
659
|
return results;
|
|
508
660
|
}
|
|
509
661
|
for (const entry of entries) {
|
|
510
|
-
const content = await
|
|
662
|
+
const content = await readFile5(join6(toolResultsDir, entry), "utf-8");
|
|
511
663
|
results.set(entry, content);
|
|
512
664
|
}
|
|
513
665
|
return results;
|
|
514
666
|
}
|
|
515
667
|
async function readMemory(projectDir) {
|
|
516
668
|
const memory = /* @__PURE__ */ new Map();
|
|
517
|
-
const memoryDir =
|
|
669
|
+
const memoryDir = join6(projectDir, "memory");
|
|
518
670
|
let entries;
|
|
519
671
|
try {
|
|
520
672
|
entries = await readdir2(memoryDir);
|
|
@@ -522,7 +674,7 @@ async function readMemory(projectDir) {
|
|
|
522
674
|
return memory;
|
|
523
675
|
}
|
|
524
676
|
for (const entry of entries) {
|
|
525
|
-
const content = await
|
|
677
|
+
const content = await readFile5(join6(memoryDir, entry), "utf-8");
|
|
526
678
|
memory.set(entry, content);
|
|
527
679
|
}
|
|
528
680
|
return memory;
|
|
@@ -621,7 +773,7 @@ async function pull(options) {
|
|
|
621
773
|
console.log("Pulling latest...");
|
|
622
774
|
await pullRepo(repoDir);
|
|
623
775
|
}
|
|
624
|
-
const projectDir =
|
|
776
|
+
const projectDir = join7(repoDir, "projects", project.projectId);
|
|
625
777
|
const data = await readCheckpoint(projectDir);
|
|
626
778
|
if (!data) {
|
|
627
779
|
throw new NoSessionsError(
|
|
@@ -631,26 +783,43 @@ async function pull(options) {
|
|
|
631
783
|
console.log(
|
|
632
784
|
`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`
|
|
633
785
|
);
|
|
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
786
|
const pathCtx = getLocalPathContext(cwd);
|
|
645
787
|
for (const session of data.sessions) {
|
|
646
788
|
session.jsonl = expandPaths(session.jsonl, pathCtx);
|
|
647
789
|
}
|
|
648
|
-
|
|
649
|
-
|
|
790
|
+
const localProjectDir = join7(getClaudeProjectsDir(), encodeProjectDir(cwd));
|
|
791
|
+
const remoteSessionsDir = join7(projectDir, "sessions");
|
|
792
|
+
const remoteMemoryDir = join7(projectDir, "memory");
|
|
793
|
+
const localSessionIds = await listLocalSessionIds(cwd);
|
|
794
|
+
const localMemoryFiles = await listLocalMemoryFiles(cwd);
|
|
795
|
+
const remoteSessionIds = data.sessions.map((s) => s.sessionId);
|
|
796
|
+
const remoteMemoryFiles = [...data.memory.keys()];
|
|
797
|
+
const conflicts = await detectConflicts(
|
|
798
|
+
localSessionIds,
|
|
799
|
+
remoteSessionIds,
|
|
800
|
+
localMemoryFiles,
|
|
801
|
+
remoteMemoryFiles,
|
|
802
|
+
{ localProjectDir, remoteSessionsDir, remoteMemoryDir }
|
|
803
|
+
);
|
|
804
|
+
if (hasConflicts(conflicts) && !options.force && !options.skip) {
|
|
805
|
+
throw new ConflictError(formatConflictMessage(conflicts));
|
|
806
|
+
}
|
|
807
|
+
const skipSessions = options.skip ? new Set(conflicts.sessions.map((c) => c.name)) : void 0;
|
|
808
|
+
const skipMemory = options.skip ? new Set(conflicts.memoryFiles.map((c) => c.name)) : void 0;
|
|
809
|
+
await restoreProjectData(cwd, data, {
|
|
810
|
+
skipSessions,
|
|
811
|
+
skipMemory
|
|
812
|
+
});
|
|
813
|
+
if (options.skip && hasConflicts(conflicts)) {
|
|
814
|
+
const skipped = conflicts.sessions.length + conflicts.memoryFiles.length;
|
|
815
|
+
console.log(`Pulled successfully. Skipped ${skipped} conflicting file(s).`);
|
|
816
|
+
} else {
|
|
817
|
+
console.log("Pulled successfully. Sessions restored to Claude Code.");
|
|
818
|
+
}
|
|
650
819
|
}
|
|
651
820
|
|
|
652
821
|
// src/commands/push.ts
|
|
653
|
-
import { join as
|
|
822
|
+
import { join as join8 } from "path";
|
|
654
823
|
async function push(options) {
|
|
655
824
|
const cwd = process.cwd();
|
|
656
825
|
const project = await detectProject(cwd);
|
|
@@ -677,7 +846,7 @@ async function push(options) {
|
|
|
677
846
|
if (error instanceof ConflictError) throw error;
|
|
678
847
|
}
|
|
679
848
|
}
|
|
680
|
-
const projectDir =
|
|
849
|
+
const projectDir = join8(repoDir, "projects", project.projectId);
|
|
681
850
|
await writeCheckpoint(projectDir, project, data);
|
|
682
851
|
await commitAndPush(
|
|
683
852
|
repoDir,
|
|
@@ -688,15 +857,15 @@ async function push(options) {
|
|
|
688
857
|
}
|
|
689
858
|
|
|
690
859
|
// src/commands/status.ts
|
|
691
|
-
import { readdir as readdir3, readFile as
|
|
692
|
-
import { join as
|
|
860
|
+
import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
|
|
861
|
+
import { join as join9 } from "path";
|
|
693
862
|
async function status() {
|
|
694
863
|
const cwd = process.cwd();
|
|
695
864
|
const project = await detectProject(cwd);
|
|
696
865
|
console.log(`Project: ${project.normalizedRemote}`);
|
|
697
866
|
console.log(`Project ID: ${project.projectId}`);
|
|
698
867
|
const projectDirName = encodeProjectDir(cwd);
|
|
699
|
-
const localProjectDir =
|
|
868
|
+
const localProjectDir = join9(getClaudeProjectsDir(), projectDirName);
|
|
700
869
|
try {
|
|
701
870
|
const entries = await readdir3(localProjectDir);
|
|
702
871
|
const sessionCount = entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
@@ -715,9 +884,9 @@ async function status() {
|
|
|
715
884
|
console.log("Remote checkpoint: not cloned yet");
|
|
716
885
|
return;
|
|
717
886
|
}
|
|
718
|
-
const metaPath =
|
|
887
|
+
const metaPath = join9(repoDir, "projects", project.projectId, "meta.json");
|
|
719
888
|
try {
|
|
720
|
-
const raw = await
|
|
889
|
+
const raw = await readFile6(metaPath, "utf-8");
|
|
721
890
|
const meta = JSON.parse(raw);
|
|
722
891
|
console.log(`Last pushed: ${meta.pushed_at ?? "unknown"}`);
|
|
723
892
|
} catch {
|
|
@@ -727,12 +896,12 @@ async function status() {
|
|
|
727
896
|
|
|
728
897
|
// src/cli.ts
|
|
729
898
|
var program = new Command();
|
|
730
|
-
program.name("baton").description("Git-backed session handoff for Claude Code").version("0.
|
|
899
|
+
program.name("baton").description("Git-backed session handoff for Claude Code").version("0.4.0");
|
|
731
900
|
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
901
|
await push({ force: options.force });
|
|
733
902
|
});
|
|
734
|
-
program.command("pull").description("Restore sessions for the current project from GitHub").option("-f, --force", "Overwrite local
|
|
735
|
-
await pull({ force: options.force });
|
|
903
|
+
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) => {
|
|
904
|
+
await pull({ force: options.force, skip: options.skip });
|
|
736
905
|
});
|
|
737
906
|
program.command("status").description("Show current project and sync state").action(async () => {
|
|
738
907
|
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));\n\t}\n\n\t// 6. Restore to Claude Code's local storage\n\tconst skipSessions = options.skip\n\t\t? new Set(conflicts.sessions.map((c) => c.name))\n\t\t: undefined;\n\tconst skipMemory = options.skip\n\t\t? new Set(conflicts.memoryFiles.map((c) => c.name))\n\t\t: 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, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface FileConflict {\n\tname: string;\n\tlocalPath: string;\n\tremotePath: string;\n\tlocalModified: string;\n\tremoteModified: string;\n}\n\nexport interface ConflictInfo {\n\tsessions: FileConflict[];\n\tmemoryFiles: FileConflict[];\n}\n\nexport interface ConflictContext {\n\tlocalProjectDir: string;\n\tremoteSessionsDir: string;\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 * Includes modification timestamps for each conflicting file.\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: FileConflict[] = [];\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({\n\t\t\t\tname: id,\n\t\t\t\tlocalPath,\n\t\t\t\tremotePath,\n\t\t\t\tlocalModified: await getModifiedTime(localPath),\n\t\t\t\tremoteModified: await getModifiedTime(remotePath),\n\t\t\t});\n\t\t}\n\t}\n\n\tconst memoryFiles: FileConflict[] = [];\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({\n\t\t\t\tname: file,\n\t\t\t\tlocalPath,\n\t\t\t\tremotePath,\n\t\t\t\tlocalModified: await getModifiedTime(localPath),\n\t\t\t\tremoteModified: await getModifiedTime(remotePath),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { sessions, memoryFiles };\n}\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\nasync function getModifiedTime(filePath: string): Promise<string> {\n\ttry {\n\t\tconst s = await stat(filePath);\n\t\treturn formatRelativeTime(s.mtime);\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst now = Date.now();\n\tconst diffMs = now - date.getTime();\n\tconst diffMins = Math.floor(diffMs / 60_000);\n\tconst diffHours = Math.floor(diffMs / 3_600_000);\n\tconst diffDays = Math.floor(diffMs / 86_400_000);\n\n\tif (diffMins < 1) return \"just now\";\n\tif (diffMins < 60) return `${diffMins} minute(s) ago`;\n\tif (diffHours < 24) return `${diffHours} hour(s) ago`;\n\treturn `${diffDays} day(s) ago`;\n}\n\n/**\n * Format conflict info into a message readable by both humans and AI agents.\n */\nexport function formatConflictMessage(conflicts: ConflictInfo): 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 c of conflicts.sessions) {\n\t\t\tlines.push(` - ${c.name}`);\n\t\t\tlines.push(` local: ${c.localPath} (modified ${c.localModified})`);\n\t\t\tlines.push(` remote: ${c.remotePath} (modified ${c.remoteModified})`);\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 c of conflicts.memoryFiles) {\n\t\t\tlines.push(` - ${c.name}`);\n\t\t\tlines.push(` local: ${c.localPath} (modified ${c.localModified})`);\n\t\t\tlines.push(` remote: ${c.remotePath} (modified ${c.remoteModified})`);\n\t\t}\n\t\tlines.push(\"\");\n\t}\n\n\tlines.push(\"Choose how to resolve:\");\n\tlines.push(\" 1. Overwrite local with remote (lose local changes)\");\n\tlines.push(\n\t\t\" 2. Keep local, skip remote (local will overwrite remote on next push)\",\n\t);\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\n\t\t\t\" 3. Merge memory files (agent reads both versions and combines them)\",\n\t\t);\n\t}\n\tlines.push(\"\");\n\n\tlines.push(\"Or use flags directly:\");\n\tlines.push(\n\t\t\" baton pull --force option 1: overwrite all local with remote\",\n\t);\n\tlines.push(\n\t\t\" baton pull --skip option 2: only pull non-conflicting files\",\n\t);\n\n\tlines.push(\"\");\n\tlines.push(\"<agent-hint>\");\n\tlines.push(\"Ask the user which option they prefer. Then follow through:\");\n\tlines.push(\"\");\n\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(\"For sessions (cannot be merged — pick one version):\");\n\t\tlines.push(\" Option 1: run `baton pull --force`, then `baton push`\");\n\t\tlines.push(\" Option 2: run `baton pull --skip`, then `baton push`\");\n\t\tlines.push(\"\");\n\t}\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\"For memory files (can be merged):\");\n\t\tlines.push(\" Option 1: run `baton pull --force`, then `baton push`\");\n\t\tlines.push(\" Option 2: run `baton pull --skip`, then `baton push`\");\n\t\tlines.push(\" Option 3: read both local and remote files listed above,\");\n\t\tlines.push(\" merge their content (deduplicate, keep most recent info),\");\n\t\tlines.push(\" write merged result to local path,\");\n\t\tlines.push(\" then run `baton pull --skip` and `baton push`\");\n\t\tlines.push(\"\");\n\t}\n\tlines.push(\"</agent-hint>\");\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","stat","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"]}
|