baton-cli 0.5.3 → 0.6.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 +107 -60
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/pull.ts
|
|
7
|
-
import { readFile as
|
|
8
|
-
import { join as
|
|
7
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
8
|
+
import { join as join8 } from "path";
|
|
9
9
|
|
|
10
10
|
// src/adapters/claude-code/paths.ts
|
|
11
11
|
import { homedir } from "os";
|
|
@@ -135,40 +135,74 @@ async function collectMemory(projectDir) {
|
|
|
135
135
|
return memory;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// src/adapters/claude-code/session.ts
|
|
139
|
+
import { readdir as readdir2, readFile as readFile2 } from "fs/promises";
|
|
140
|
+
import { homedir as homedir2 } from "os";
|
|
141
|
+
import { join as join3 } from "path";
|
|
142
|
+
async function findActiveSessionId(projectCwd) {
|
|
143
|
+
const sessionsDir = join3(homedir2(), ".claude", "sessions");
|
|
144
|
+
let entries;
|
|
145
|
+
try {
|
|
146
|
+
entries = await readdir2(sessionsDir);
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (!entry.endsWith(".json")) continue;
|
|
152
|
+
try {
|
|
153
|
+
const raw = await readFile2(join3(sessionsDir, entry), "utf-8");
|
|
154
|
+
const session = JSON.parse(raw);
|
|
155
|
+
if (session.cwd === projectCwd && isProcessRunning(session.pid)) {
|
|
156
|
+
return session.sessionId;
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
function isProcessRunning(pid) {
|
|
164
|
+
try {
|
|
165
|
+
process.kill(pid, 0);
|
|
166
|
+
return true;
|
|
167
|
+
} catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
138
172
|
// src/adapters/claude-code/writer.ts
|
|
139
|
-
import { mkdir, readFile as
|
|
140
|
-
import { dirname, join as
|
|
173
|
+
import { mkdir, readFile as readFile3, writeFile } from "fs/promises";
|
|
174
|
+
import { dirname, join as join4 } from "path";
|
|
141
175
|
async function restoreProjectData(projectPath, data, options) {
|
|
142
176
|
const projectDirName = encodeProjectDir(projectPath);
|
|
143
|
-
const projectDir =
|
|
177
|
+
const projectDir = join4(getClaudeProjectsDir(), projectDirName);
|
|
144
178
|
await mkdir(projectDir, { recursive: true });
|
|
145
179
|
const skipSessions = options?.skipSessions ?? /* @__PURE__ */ new Set();
|
|
146
180
|
const skipMemory = options?.skipMemory ?? /* @__PURE__ */ new Set();
|
|
147
181
|
for (const session of data.sessions) {
|
|
148
182
|
if (skipSessions.has(session.sessionId)) continue;
|
|
149
183
|
await writeFile(
|
|
150
|
-
|
|
184
|
+
join4(projectDir, `${session.sessionId}.jsonl`),
|
|
151
185
|
session.jsonl,
|
|
152
186
|
"utf-8"
|
|
153
187
|
);
|
|
154
188
|
if (session.toolResults.size > 0) {
|
|
155
|
-
const toolResultsDir =
|
|
189
|
+
const toolResultsDir = join4(
|
|
156
190
|
projectDir,
|
|
157
191
|
session.sessionId,
|
|
158
192
|
"tool-results"
|
|
159
193
|
);
|
|
160
194
|
await mkdir(toolResultsDir, { recursive: true });
|
|
161
195
|
for (const [filename, content] of session.toolResults) {
|
|
162
|
-
await writeFile(
|
|
196
|
+
await writeFile(join4(toolResultsDir, filename), content, "utf-8");
|
|
163
197
|
}
|
|
164
198
|
}
|
|
165
199
|
}
|
|
166
200
|
if (data.memory.size > 0) {
|
|
167
|
-
const memoryDir =
|
|
201
|
+
const memoryDir = join4(projectDir, "memory");
|
|
168
202
|
await mkdir(memoryDir, { recursive: true });
|
|
169
203
|
for (const [filename, content] of data.memory) {
|
|
170
204
|
if (skipMemory.has(filename)) continue;
|
|
171
|
-
await writeFile(
|
|
205
|
+
await writeFile(join4(memoryDir, filename), content, "utf-8");
|
|
172
206
|
}
|
|
173
207
|
}
|
|
174
208
|
await ensureProjectConfig(projectPath, projectDirName);
|
|
@@ -177,7 +211,7 @@ async function ensureProjectConfig(projectPath, projectDirName) {
|
|
|
177
211
|
const configPath = getProjectConfigPath();
|
|
178
212
|
let config = {};
|
|
179
213
|
try {
|
|
180
|
-
const raw = await
|
|
214
|
+
const raw = await readFile3(configPath, "utf-8");
|
|
181
215
|
config = JSON.parse(raw);
|
|
182
216
|
} catch {
|
|
183
217
|
}
|
|
@@ -191,21 +225,21 @@ async function ensureProjectConfig(projectPath, projectDirName) {
|
|
|
191
225
|
}
|
|
192
226
|
|
|
193
227
|
// src/core/config.ts
|
|
194
|
-
import { mkdir as mkdir2, readFile as
|
|
195
|
-
import { homedir as
|
|
196
|
-
import { dirname as dirname2, join as
|
|
228
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
229
|
+
import { homedir as homedir3 } from "os";
|
|
230
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
197
231
|
function getBatonDir() {
|
|
198
|
-
return
|
|
232
|
+
return join5(homedir3(), ".baton");
|
|
199
233
|
}
|
|
200
234
|
function getConfigPath() {
|
|
201
|
-
return
|
|
235
|
+
return join5(getBatonDir(), "config.json");
|
|
202
236
|
}
|
|
203
237
|
function getRepoDir() {
|
|
204
|
-
return
|
|
238
|
+
return join5(getBatonDir(), "repo");
|
|
205
239
|
}
|
|
206
240
|
async function loadConfig() {
|
|
207
241
|
try {
|
|
208
|
-
const raw = await
|
|
242
|
+
const raw = await readFile4(getConfigPath(), "utf-8");
|
|
209
243
|
const config = JSON.parse(raw);
|
|
210
244
|
if (!config.repo || typeof config.repo !== "string") {
|
|
211
245
|
return null;
|
|
@@ -222,8 +256,8 @@ async function saveConfig(config) {
|
|
|
222
256
|
}
|
|
223
257
|
|
|
224
258
|
// src/core/conflicts.ts
|
|
225
|
-
import { readFile as
|
|
226
|
-
import { join as
|
|
259
|
+
import { readFile as readFile5, stat as stat2 } from "fs/promises";
|
|
260
|
+
import { join as join6 } from "path";
|
|
227
261
|
async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFiles, remoteMemoryFiles, ctx) {
|
|
228
262
|
const remoteIdSet = new Set(remoteSessionIds);
|
|
229
263
|
const overlappingSessionIds = localSessionIds.filter(
|
|
@@ -234,8 +268,8 @@ async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFil
|
|
|
234
268
|
const remotePushedLabel = ctx.remotePushedAt ? formatRelativeTime(new Date(ctx.remotePushedAt)) : "unknown";
|
|
235
269
|
const sessions = [];
|
|
236
270
|
for (const id of overlappingSessionIds) {
|
|
237
|
-
const localPath =
|
|
238
|
-
const remotePath =
|
|
271
|
+
const localPath = join6(ctx.localProjectDir, `${id}.jsonl`);
|
|
272
|
+
const remotePath = join6(ctx.remoteSessionsDir, `${id}.jsonl`);
|
|
239
273
|
if (await contentDiffers(localPath, remotePath)) {
|
|
240
274
|
sessions.push({
|
|
241
275
|
name: id,
|
|
@@ -248,8 +282,8 @@ async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFil
|
|
|
248
282
|
}
|
|
249
283
|
const memoryFiles = [];
|
|
250
284
|
for (const file of overlappingMemory) {
|
|
251
|
-
const localPath =
|
|
252
|
-
const remotePath =
|
|
285
|
+
const localPath = join6(ctx.localProjectDir, "memory", file);
|
|
286
|
+
const remotePath = join6(ctx.remoteMemoryDir, file);
|
|
253
287
|
if (await contentDiffers(localPath, remotePath)) {
|
|
254
288
|
memoryFiles.push({
|
|
255
289
|
name: file,
|
|
@@ -265,8 +299,8 @@ async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFil
|
|
|
265
299
|
async function contentDiffers(pathA, pathB) {
|
|
266
300
|
try {
|
|
267
301
|
const [contentA, contentB] = await Promise.all([
|
|
268
|
-
|
|
269
|
-
|
|
302
|
+
readFile5(pathA, "utf-8"),
|
|
303
|
+
readFile5(pathB, "utf-8")
|
|
270
304
|
]);
|
|
271
305
|
return contentA !== contentB;
|
|
272
306
|
} catch {
|
|
@@ -456,7 +490,7 @@ function isNotFound(error) {
|
|
|
456
490
|
}
|
|
457
491
|
|
|
458
492
|
// src/core/paths.ts
|
|
459
|
-
import { homedir as
|
|
493
|
+
import { homedir as homedir4, tmpdir } from "os";
|
|
460
494
|
import { sep } from "path";
|
|
461
495
|
var PLACEHOLDER_PROJECT_ROOT = "${PROJECT_ROOT}";
|
|
462
496
|
var PLACEHOLDER_HOME = "${HOME}";
|
|
@@ -469,7 +503,7 @@ var PLACEHOLDERS = {
|
|
|
469
503
|
function getLocalPathContext(projectRoot) {
|
|
470
504
|
return {
|
|
471
505
|
projectRoot,
|
|
472
|
-
home:
|
|
506
|
+
home: homedir4(),
|
|
473
507
|
tmp: tmpdir()
|
|
474
508
|
};
|
|
475
509
|
}
|
|
@@ -588,10 +622,10 @@ async function detectProject(cwd) {
|
|
|
588
622
|
}
|
|
589
623
|
|
|
590
624
|
// src/commands/checkpoint.ts
|
|
591
|
-
import { mkdir as mkdir3, readdir as
|
|
592
|
-
import { join as
|
|
625
|
+
import { mkdir as mkdir3, readdir as readdir3, readFile as readFile6, rm, writeFile as writeFile3 } from "fs/promises";
|
|
626
|
+
import { join as join7 } from "path";
|
|
593
627
|
async function writeCheckpoint(projectDir, project, data) {
|
|
594
|
-
const sessionsDir =
|
|
628
|
+
const sessionsDir = join7(projectDir, "sessions");
|
|
595
629
|
await rm(sessionsDir, { recursive: true, force: true });
|
|
596
630
|
await mkdir3(sessionsDir, { recursive: true });
|
|
597
631
|
const meta = {
|
|
@@ -600,41 +634,41 @@ async function writeCheckpoint(projectDir, project, data) {
|
|
|
600
634
|
pushed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
601
635
|
};
|
|
602
636
|
await writeFile3(
|
|
603
|
-
|
|
637
|
+
join7(projectDir, "meta.json"),
|
|
604
638
|
JSON.stringify(meta, null, 2),
|
|
605
639
|
"utf-8"
|
|
606
640
|
);
|
|
607
641
|
for (const session of data.sessions) {
|
|
608
642
|
await writeFile3(
|
|
609
|
-
|
|
643
|
+
join7(sessionsDir, `${session.sessionId}.jsonl`),
|
|
610
644
|
session.jsonl,
|
|
611
645
|
"utf-8"
|
|
612
646
|
);
|
|
613
647
|
if (session.toolResults.size > 0) {
|
|
614
|
-
const toolResultsDir =
|
|
648
|
+
const toolResultsDir = join7(
|
|
615
649
|
sessionsDir,
|
|
616
650
|
session.sessionId,
|
|
617
651
|
"tool-results"
|
|
618
652
|
);
|
|
619
653
|
await mkdir3(toolResultsDir, { recursive: true });
|
|
620
654
|
for (const [filename, content] of session.toolResults) {
|
|
621
|
-
await writeFile3(
|
|
655
|
+
await writeFile3(join7(toolResultsDir, filename), content, "utf-8");
|
|
622
656
|
}
|
|
623
657
|
}
|
|
624
658
|
}
|
|
625
659
|
if (data.memory.size > 0) {
|
|
626
|
-
const memoryDir =
|
|
660
|
+
const memoryDir = join7(projectDir, "memory");
|
|
627
661
|
await mkdir3(memoryDir, { recursive: true });
|
|
628
662
|
for (const [filename, content] of data.memory) {
|
|
629
|
-
await writeFile3(
|
|
663
|
+
await writeFile3(join7(memoryDir, filename), content, "utf-8");
|
|
630
664
|
}
|
|
631
665
|
}
|
|
632
666
|
}
|
|
633
667
|
async function readCheckpoint(projectDir) {
|
|
634
|
-
const sessionsDir =
|
|
668
|
+
const sessionsDir = join7(projectDir, "sessions");
|
|
635
669
|
let sessionFiles;
|
|
636
670
|
try {
|
|
637
|
-
const entries = await
|
|
671
|
+
const entries = await readdir3(sessionsDir);
|
|
638
672
|
sessionFiles = entries.filter((e) => e.endsWith(".jsonl"));
|
|
639
673
|
} catch {
|
|
640
674
|
return null;
|
|
@@ -645,7 +679,7 @@ async function readCheckpoint(projectDir) {
|
|
|
645
679
|
const sessions = [];
|
|
646
680
|
for (const file of sessionFiles) {
|
|
647
681
|
const sessionId = file.replace(".jsonl", "");
|
|
648
|
-
const jsonl = await
|
|
682
|
+
const jsonl = await readFile6(join7(sessionsDir, file), "utf-8");
|
|
649
683
|
const toolResults = await readToolResults(sessionsDir, sessionId);
|
|
650
684
|
sessions.push({ sessionId, jsonl, toolResults });
|
|
651
685
|
}
|
|
@@ -658,30 +692,30 @@ async function readCheckpoint(projectDir) {
|
|
|
658
692
|
}
|
|
659
693
|
async function readToolResults(sessionsDir, sessionId) {
|
|
660
694
|
const results = /* @__PURE__ */ new Map();
|
|
661
|
-
const toolResultsDir =
|
|
695
|
+
const toolResultsDir = join7(sessionsDir, sessionId, "tool-results");
|
|
662
696
|
let entries;
|
|
663
697
|
try {
|
|
664
|
-
entries = await
|
|
698
|
+
entries = await readdir3(toolResultsDir);
|
|
665
699
|
} catch {
|
|
666
700
|
return results;
|
|
667
701
|
}
|
|
668
702
|
for (const entry of entries) {
|
|
669
|
-
const content = await
|
|
703
|
+
const content = await readFile6(join7(toolResultsDir, entry), "utf-8");
|
|
670
704
|
results.set(entry, content);
|
|
671
705
|
}
|
|
672
706
|
return results;
|
|
673
707
|
}
|
|
674
708
|
async function readMemory(projectDir) {
|
|
675
709
|
const memory = /* @__PURE__ */ new Map();
|
|
676
|
-
const memoryDir =
|
|
710
|
+
const memoryDir = join7(projectDir, "memory");
|
|
677
711
|
let entries;
|
|
678
712
|
try {
|
|
679
|
-
entries = await
|
|
713
|
+
entries = await readdir3(memoryDir);
|
|
680
714
|
} catch {
|
|
681
715
|
return memory;
|
|
682
716
|
}
|
|
683
717
|
for (const entry of entries) {
|
|
684
|
-
const content = await
|
|
718
|
+
const content = await readFile6(join7(memoryDir, entry), "utf-8");
|
|
685
719
|
memory.set(entry, content);
|
|
686
720
|
}
|
|
687
721
|
return memory;
|
|
@@ -780,7 +814,7 @@ async function pull(options) {
|
|
|
780
814
|
console.log("Pulling latest...");
|
|
781
815
|
await pullRepo(repoDir);
|
|
782
816
|
}
|
|
783
|
-
const projectDir =
|
|
817
|
+
const projectDir = join8(repoDir, "projects", project.projectId);
|
|
784
818
|
const data = await readCheckpoint(projectDir);
|
|
785
819
|
if (!data) {
|
|
786
820
|
throw new NoSessionsError(
|
|
@@ -794,9 +828,9 @@ async function pull(options) {
|
|
|
794
828
|
for (const session of data.sessions) {
|
|
795
829
|
session.jsonl = expandPaths(session.jsonl, pathCtx);
|
|
796
830
|
}
|
|
797
|
-
const localProjectDir =
|
|
798
|
-
const remoteSessionsDir =
|
|
799
|
-
const remoteMemoryDir =
|
|
831
|
+
const localProjectDir = join8(getClaudeProjectsDir(), encodeProjectDir(cwd));
|
|
832
|
+
const remoteSessionsDir = join8(projectDir, "sessions");
|
|
833
|
+
const remoteMemoryDir = join8(projectDir, "memory");
|
|
800
834
|
const localSessionIds = await listLocalSessionIds(cwd);
|
|
801
835
|
const localMemoryFiles = await listLocalMemoryFiles(cwd);
|
|
802
836
|
const remoteSessionIds = data.sessions.map((s) => s.sessionId);
|
|
@@ -804,7 +838,7 @@ async function pull(options) {
|
|
|
804
838
|
let remotePushedAt;
|
|
805
839
|
try {
|
|
806
840
|
const meta = JSON.parse(
|
|
807
|
-
await
|
|
841
|
+
await readFile7(join8(projectDir, "meta.json"), "utf-8")
|
|
808
842
|
);
|
|
809
843
|
remotePushedAt = meta.pushed_at;
|
|
810
844
|
} catch {
|
|
@@ -831,10 +865,23 @@ async function pull(options) {
|
|
|
831
865
|
} else {
|
|
832
866
|
console.log("Pulled successfully. Sessions restored to Claude Code.");
|
|
833
867
|
}
|
|
868
|
+
const activeSessionId = await findActiveSessionId(cwd);
|
|
869
|
+
if (activeSessionId) {
|
|
870
|
+
const pulledIds = new Set(data.sessions.map((s) => s.sessionId));
|
|
871
|
+
const wasSkipped = skipSessions?.has(activeSessionId);
|
|
872
|
+
if (pulledIds.has(activeSessionId) && !wasSkipped) {
|
|
873
|
+
console.log("");
|
|
874
|
+
console.log("<agent-hint>");
|
|
875
|
+
console.log("The active Claude Code session was updated by this pull.");
|
|
876
|
+
console.log("The in-memory conversation context is now stale.");
|
|
877
|
+
console.log("Run /resume to reload the session with the pulled context.");
|
|
878
|
+
console.log("</agent-hint>");
|
|
879
|
+
}
|
|
880
|
+
}
|
|
834
881
|
}
|
|
835
882
|
|
|
836
883
|
// src/commands/push.ts
|
|
837
|
-
import { join as
|
|
884
|
+
import { join as join9 } from "path";
|
|
838
885
|
async function push(options) {
|
|
839
886
|
const cwd = process.cwd();
|
|
840
887
|
const project = await detectProject(cwd);
|
|
@@ -872,7 +919,7 @@ async function push(options) {
|
|
|
872
919
|
if (error instanceof ConflictError) throw error;
|
|
873
920
|
}
|
|
874
921
|
}
|
|
875
|
-
const projectDir =
|
|
922
|
+
const projectDir = join9(repoDir, "projects", project.projectId);
|
|
876
923
|
await writeCheckpoint(projectDir, project, data);
|
|
877
924
|
await commitAndPush(
|
|
878
925
|
repoDir,
|
|
@@ -883,17 +930,17 @@ async function push(options) {
|
|
|
883
930
|
}
|
|
884
931
|
|
|
885
932
|
// src/commands/status.ts
|
|
886
|
-
import { readdir as
|
|
887
|
-
import { join as
|
|
933
|
+
import { readdir as readdir4, readFile as readFile8 } from "fs/promises";
|
|
934
|
+
import { join as join10 } from "path";
|
|
888
935
|
async function status() {
|
|
889
936
|
const cwd = process.cwd();
|
|
890
937
|
const project = await detectProject(cwd);
|
|
891
938
|
console.log(`Project: ${project.normalizedRemote}`);
|
|
892
939
|
console.log(`Project ID: ${project.projectId}`);
|
|
893
940
|
const projectDirName = encodeProjectDir(cwd);
|
|
894
|
-
const localProjectDir =
|
|
941
|
+
const localProjectDir = join10(getClaudeProjectsDir(), projectDirName);
|
|
895
942
|
try {
|
|
896
|
-
const entries = await
|
|
943
|
+
const entries = await readdir4(localProjectDir);
|
|
897
944
|
const sessionCount = entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
898
945
|
console.log(`Local sessions: ${sessionCount}`);
|
|
899
946
|
} catch {
|
|
@@ -910,9 +957,9 @@ async function status() {
|
|
|
910
957
|
console.log("Remote checkpoint: not cloned yet");
|
|
911
958
|
return;
|
|
912
959
|
}
|
|
913
|
-
const metaPath =
|
|
960
|
+
const metaPath = join10(repoDir, "projects", project.projectId, "meta.json");
|
|
914
961
|
try {
|
|
915
|
-
const raw = await
|
|
962
|
+
const raw = await readFile8(metaPath, "utf-8");
|
|
916
963
|
const meta = JSON.parse(raw);
|
|
917
964
|
console.log(`Last pushed: ${meta.pushed_at ?? "unknown"}`);
|
|
918
965
|
} catch {
|
|
@@ -922,7 +969,7 @@ async function status() {
|
|
|
922
969
|
|
|
923
970
|
// src/cli.ts
|
|
924
971
|
var program = new Command();
|
|
925
|
-
program.name("baton").description("Git-backed session handoff for Claude Code").version("0.5.
|
|
972
|
+
program.name("baton").description("Git-backed session handoff for Claude Code").version("0.5.3");
|
|
926
973
|
program.command("push").description("Push all sessions for the current project to GitHub").option("-f, --force", "Overwrite remote even if ahead").action(async (options) => {
|
|
927
974
|
await push({ force: options.force });
|
|
928
975
|
});
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 { readFile } from \"node:fs/promises\";\nimport { 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\t// Read pushed_at from meta.json for accurate remote timestamps\n\tlet remotePushedAt: string | undefined;\n\ttry {\n\t\tconst meta = JSON.parse(\n\t\t\tawait readFile(join(projectDir, \"meta.json\"), \"utf-8\"),\n\t\t);\n\t\tremotePushedAt = meta.pushed_at;\n\t} catch {\n\t\t// meta.json missing or invalid, timestamps will show \"unknown\"\n\t}\n\n\tconst conflicts = await detectConflicts(\n\t\tlocalSessionIds,\n\t\tremoteSessionIds,\n\t\tlocalMemoryFiles,\n\t\tremoteMemoryFiles,\n\t\t{ localProjectDir, remoteSessionsDir, remoteMemoryDir, remotePushedAt },\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\t/** ISO timestamp from meta.json pushed_at, used for remote file timestamps */\n\tremotePushedAt?: 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 remotePushedLabel = ctx.remotePushedAt\n\t\t? formatRelativeTime(new Date(ctx.remotePushedAt))\n\t\t: \"unknown\";\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: remotePushedLabel,\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: remotePushedLabel,\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\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(\"Choose how to resolve sessions:\");\n\t\tlines.push(\" 1. Overwrite local with remote (lose local changes)\");\n\t\tlines.push(\n\t\t\t\" 2. Keep local, skip remote (local will overwrite remote on next push)\",\n\t\t);\n\t\tlines.push(\"\");\n\t}\n\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\n\t\t\t\"Warning: options 1 and 2 will lose memory file content from one side.\",\n\t\t);\n\t\tlines.push(\n\t\t\t\"Use an AI coding agent (e.g. Claude Code) to merge memory files automatically.\",\n\t\t);\n\t\tlines.push(\"\");\n\t}\n\n\tlines.push(\"Flags:\");\n\tlines.push(\" baton pull --force overwrite all local with remote\");\n\tlines.push(\" baton pull --skip only pull non-conflicting files\");\n\n\tlines.push(\"\");\n\tlines.push(\"<agent-hint>\");\n\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\n\t\t\t\"ALWAYS merge memory files before asking the user about sessions.\",\n\t\t);\n\t\tlines.push(\"Steps:\");\n\t\tlines.push(\" 1. Read both local and remote memory files listed above\");\n\t\tlines.push(\n\t\t\t\" 2. Merge their content (deduplicate entries, keep most recent info)\",\n\t\t);\n\t\tlines.push(\" 3. Write the merged result to the local path\");\n\t\tlines.push(\"\");\n\t}\n\n\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(\"Then ask the user which option they prefer for sessions:\");\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} else {\n\t\tlines.push(\n\t\t\t\"After merging memory, run `baton pull --skip` then `baton push`\",\n\t\t);\n\t}\n\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[\n\t\t\t\t\t\t\"Remote has changes you haven't pulled.\",\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\"To resolve:\",\n\t\t\t\t\t\t\" baton pull pull remote changes first, then push again\",\n\t\t\t\t\t\t\" baton push --force overwrite remote with local (discard remote changes)\",\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\"<agent-hint>\",\n\t\t\t\t\t\t\"Run `baton pull` first. If pull reports conflicts, ask the user to choose.\",\n\t\t\t\t\t\t\"After resolving, run `baton push` to sync.\",\n\t\t\t\t\t\t\"</agent-hint>\",\n\t\t\t\t\t].join(\"\\n\"),\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":["readFile","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","readFile","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/session.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 { readFile } from \"node:fs/promises\";\nimport { 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 { findActiveSessionId } from \"../adapters/claude-code/session.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\t// Read pushed_at from meta.json for accurate remote timestamps\n\tlet remotePushedAt: string | undefined;\n\ttry {\n\t\tconst meta = JSON.parse(\n\t\t\tawait readFile(join(projectDir, \"meta.json\"), \"utf-8\"),\n\t\t);\n\t\tremotePushedAt = meta.pushed_at;\n\t} catch {\n\t\t// meta.json missing or invalid, timestamps will show \"unknown\"\n\t}\n\n\tconst conflicts = await detectConflicts(\n\t\tlocalSessionIds,\n\t\tremoteSessionIds,\n\t\tlocalMemoryFiles,\n\t\tremoteMemoryFiles,\n\t\t{ localProjectDir, remoteSessionsDir, remoteMemoryDir, remotePushedAt },\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\t// 7. Check if the active Claude Code session was modified\n\tconst activeSessionId = await findActiveSessionId(cwd);\n\tif (activeSessionId) {\n\t\tconst pulledIds = new Set(data.sessions.map((s) => s.sessionId));\n\t\tconst wasSkipped = skipSessions?.has(activeSessionId);\n\t\tif (pulledIds.has(activeSessionId) && !wasSkipped) {\n\t\t\tconsole.log(\"\");\n\t\t\tconsole.log(\"<agent-hint>\");\n\t\t\tconsole.log(\"The active Claude Code session was updated by this pull.\");\n\t\t\tconsole.log(\"The in-memory conversation context is now stale.\");\n\t\t\tconsole.log(\"Run /resume to reload the session with the pulled context.\");\n\t\t\tconsole.log(\"</agent-hint>\");\n\t\t}\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 { readdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\ninterface ActiveSession {\n\tpid: number;\n\tsessionId: string;\n\tcwd: string;\n}\n\n/**\n * Find the active Claude Code session for a given project directory.\n * Returns the session ID if found and the process is still running, null otherwise.\n */\nexport async function findActiveSessionId(\n\tprojectCwd: string,\n): Promise<string | null> {\n\tconst sessionsDir = join(homedir(), \".claude\", \"sessions\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(sessionsDir);\n\t} catch {\n\t\treturn null;\n\t}\n\n\tfor (const entry of entries) {\n\t\tif (!entry.endsWith(\".json\")) continue;\n\t\ttry {\n\t\t\tconst raw = await readFile(join(sessionsDir, entry), \"utf-8\");\n\t\t\tconst session: ActiveSession = JSON.parse(raw);\n\t\t\tif (session.cwd === projectCwd && isProcessRunning(session.pid)) {\n\t\t\t\treturn session.sessionId;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Skip invalid session files\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction isProcessRunning(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\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\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\t/** ISO timestamp from meta.json pushed_at, used for remote file timestamps */\n\tremotePushedAt?: 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 remotePushedLabel = ctx.remotePushedAt\n\t\t? formatRelativeTime(new Date(ctx.remotePushedAt))\n\t\t: \"unknown\";\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: remotePushedLabel,\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: remotePushedLabel,\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\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(\"Choose how to resolve sessions:\");\n\t\tlines.push(\" 1. Overwrite local with remote (lose local changes)\");\n\t\tlines.push(\n\t\t\t\" 2. Keep local, skip remote (local will overwrite remote on next push)\",\n\t\t);\n\t\tlines.push(\"\");\n\t}\n\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\n\t\t\t\"Warning: options 1 and 2 will lose memory file content from one side.\",\n\t\t);\n\t\tlines.push(\n\t\t\t\"Use an AI coding agent (e.g. Claude Code) to merge memory files automatically.\",\n\t\t);\n\t\tlines.push(\"\");\n\t}\n\n\tlines.push(\"Flags:\");\n\tlines.push(\" baton pull --force overwrite all local with remote\");\n\tlines.push(\" baton pull --skip only pull non-conflicting files\");\n\n\tlines.push(\"\");\n\tlines.push(\"<agent-hint>\");\n\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\n\t\t\t\"ALWAYS merge memory files before asking the user about sessions.\",\n\t\t);\n\t\tlines.push(\"Steps:\");\n\t\tlines.push(\" 1. Read both local and remote memory files listed above\");\n\t\tlines.push(\n\t\t\t\" 2. Merge their content (deduplicate entries, keep most recent info)\",\n\t\t);\n\t\tlines.push(\" 3. Write the merged result to the local path\");\n\t\tlines.push(\"\");\n\t}\n\n\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(\"Then ask the user which option they prefer for sessions:\");\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} else {\n\t\tlines.push(\n\t\t\t\"After merging memory, run `baton pull --skip` then `baton push`\",\n\t\t);\n\t}\n\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[\n\t\t\t\t\t\t\"Remote has changes you haven't pulled.\",\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\"To resolve:\",\n\t\t\t\t\t\t\" baton pull pull remote changes first, then push again\",\n\t\t\t\t\t\t\" baton push --force overwrite remote with local (discard remote changes)\",\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\"<agent-hint>\",\n\t\t\t\t\t\t\"Run `baton pull` first. If pull reports conflicts, ask the user to choose.\",\n\t\t\t\t\t\t\"After resolving, run `baton push` to sync.\",\n\t\t\t\t\t\t\"</agent-hint>\",\n\t\t\t\t\t].join(\"\\n\"),\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":["readFile","join","join","join","readdir","readFile","homedir","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","readFile","join","join","readdir","readFile","join","join","readdir","readFile"]}
|