baton-cli 0.5.1 → 0.5.3

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 CHANGED
@@ -4,6 +4,7 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/pull.ts
7
+ import { readFile as readFile6 } from "fs/promises";
7
8
  import { join as join7 } from "path";
8
9
 
9
10
  // src/adapters/claude-code/paths.ts
@@ -230,6 +231,7 @@ async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFil
230
231
  );
231
232
  const remoteMemSet = new Set(remoteMemoryFiles);
232
233
  const overlappingMemory = localMemoryFiles.filter((f) => remoteMemSet.has(f));
234
+ const remotePushedLabel = ctx.remotePushedAt ? formatRelativeTime(new Date(ctx.remotePushedAt)) : "unknown";
233
235
  const sessions = [];
234
236
  for (const id of overlappingSessionIds) {
235
237
  const localPath = join5(ctx.localProjectDir, `${id}.jsonl`);
@@ -240,7 +242,7 @@ async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFil
240
242
  localPath,
241
243
  remotePath,
242
244
  localModified: await getModifiedTime(localPath),
243
- remoteModified: await getModifiedTime(remotePath)
245
+ remoteModified: remotePushedLabel
244
246
  });
245
247
  }
246
248
  }
@@ -254,7 +256,7 @@ async function detectConflicts(localSessionIds, remoteSessionIds, localMemoryFil
254
256
  localPath,
255
257
  remotePath,
256
258
  localModified: await getModifiedTime(localPath),
257
- remoteModified: await getModifiedTime(remotePath)
259
+ remoteModified: remotePushedLabel
258
260
  });
259
261
  }
260
262
  }
@@ -312,43 +314,48 @@ function formatConflictMessage(conflicts) {
312
314
  }
313
315
  lines.push("");
314
316
  }
315
- lines.push("Choose how to resolve:");
316
- lines.push(" 1. Overwrite local with remote (lose local changes)");
317
- lines.push(
318
- " 2. Keep local, skip remote (local will overwrite remote on next push)"
319
- );
317
+ if (conflicts.sessions.length > 0) {
318
+ lines.push("Choose how to resolve sessions:");
319
+ lines.push(" 1. Overwrite local with remote (lose local changes)");
320
+ lines.push(
321
+ " 2. Keep local, skip remote (local will overwrite remote on next push)"
322
+ );
323
+ lines.push("");
324
+ }
320
325
  if (conflicts.memoryFiles.length > 0) {
321
326
  lines.push(
322
- " 3. Merge memory files (agent reads both versions and combines them)"
327
+ "Warning: options 1 and 2 will lose memory file content from one side."
328
+ );
329
+ lines.push(
330
+ "Use an AI coding agent (e.g. Claude Code) to merge memory files automatically."
323
331
  );
332
+ lines.push("");
324
333
  }
325
- lines.push("");
326
- lines.push("Or use flags directly:");
327
- lines.push(
328
- " baton pull --force option 1: overwrite all local with remote"
329
- );
330
- lines.push(
331
- " baton pull --skip option 2: only pull non-conflicting files"
332
- );
334
+ lines.push("Flags:");
335
+ lines.push(" baton pull --force overwrite all local with remote");
336
+ lines.push(" baton pull --skip only pull non-conflicting files");
333
337
  lines.push("");
334
338
  lines.push("<agent-hint>");
335
- lines.push("Ask the user which option they prefer. Then follow through:");
336
- lines.push("");
337
- if (conflicts.sessions.length > 0) {
338
- lines.push("For sessions (cannot be merged \u2014 pick one version):");
339
- lines.push(" Option 1: run `baton pull --force`, then `baton push`");
340
- lines.push(" Option 2: run `baton pull --skip`, then `baton push`");
339
+ if (conflicts.memoryFiles.length > 0) {
340
+ lines.push(
341
+ "ALWAYS merge memory files before asking the user about sessions."
342
+ );
343
+ lines.push("Steps:");
344
+ lines.push(" 1. Read both local and remote memory files listed above");
345
+ lines.push(
346
+ " 2. Merge their content (deduplicate entries, keep most recent info)"
347
+ );
348
+ lines.push(" 3. Write the merged result to the local path");
341
349
  lines.push("");
342
350
  }
343
- if (conflicts.memoryFiles.length > 0) {
344
- lines.push("For memory files (can be merged):");
351
+ if (conflicts.sessions.length > 0) {
352
+ lines.push("Then ask the user which option they prefer for sessions:");
345
353
  lines.push(" Option 1: run `baton pull --force`, then `baton push`");
346
354
  lines.push(" Option 2: run `baton pull --skip`, then `baton push`");
347
- lines.push(" Option 3: read both local and remote files listed above,");
348
- lines.push(" merge their content (deduplicate, keep most recent info),");
349
- lines.push(" write merged result to local path,");
350
- lines.push(" then run `baton pull --skip` and `baton push`");
351
- lines.push("");
355
+ } else {
356
+ lines.push(
357
+ "After merging memory, run `baton pull --skip` then `baton push`"
358
+ );
352
359
  }
353
360
  lines.push("</agent-hint>");
354
361
  return lines.join("\n");
@@ -794,12 +801,20 @@ async function pull(options) {
794
801
  const localMemoryFiles = await listLocalMemoryFiles(cwd);
795
802
  const remoteSessionIds = data.sessions.map((s) => s.sessionId);
796
803
  const remoteMemoryFiles = [...data.memory.keys()];
804
+ let remotePushedAt;
805
+ try {
806
+ const meta = JSON.parse(
807
+ await readFile6(join7(projectDir, "meta.json"), "utf-8")
808
+ );
809
+ remotePushedAt = meta.pushed_at;
810
+ } catch {
811
+ }
797
812
  const conflicts = await detectConflicts(
798
813
  localSessionIds,
799
814
  remoteSessionIds,
800
815
  localMemoryFiles,
801
816
  remoteMemoryFiles,
802
- { localProjectDir, remoteSessionsDir, remoteMemoryDir }
817
+ { localProjectDir, remoteSessionsDir, remoteMemoryDir, remotePushedAt }
803
818
  );
804
819
  if (hasConflicts(conflicts) && !options.force && !options.skip) {
805
820
  throw new ConflictError(formatConflictMessage(conflicts));
@@ -868,7 +883,7 @@ async function push(options) {
868
883
  }
869
884
 
870
885
  // src/commands/status.ts
871
- import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
886
+ import { readdir as readdir3, readFile as readFile7 } from "fs/promises";
872
887
  import { join as join9 } from "path";
873
888
  async function status() {
874
889
  const cwd = process.cwd();
@@ -897,7 +912,7 @@ async function status() {
897
912
  }
898
913
  const metaPath = join9(repoDir, "projects", project.projectId, "meta.json");
899
914
  try {
900
- const raw = await readFile6(metaPath, "utf-8");
915
+ const raw = await readFile7(metaPath, "utf-8");
901
916
  const meta = JSON.parse(raw);
902
917
  console.log(`Last pushed: ${meta.pushed_at ?? "unknown"}`);
903
918
  } catch {
@@ -907,7 +922,7 @@ async function status() {
907
922
 
908
923
  // src/cli.ts
909
924
  var program = new Command();
910
- program.name("baton").description("Git-backed session handoff for Claude Code").version("0.5.0");
925
+ program.name("baton").description("Git-backed session handoff for Claude Code").version("0.5.2");
911
926
  program.command("push").description("Push all sessions for the current project to GitHub").option("-f, --force", "Overwrite remote even if ahead").action(async (options) => {
912
927
  await push({ force: options.force });
913
928
  });
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 { join } from \"node:path\";\nimport {\n\tencodeProjectDir,\n\tgetClaudeProjectsDir,\n} from \"../adapters/claude-code/paths.js\";\nimport {\n\tlistLocalMemoryFiles,\n\tlistLocalSessionIds,\n} from \"../adapters/claude-code/reader.js\";\nimport { restoreProjectData } from \"../adapters/claude-code/writer.js\";\nimport { getRepoDir } from \"../core/config.js\";\nimport {\n\tdetectConflicts,\n\tformatConflictMessage,\n\thasConflicts,\n} from \"../core/conflicts.js\";\nimport { pullRepo, repoExists } from \"../core/git.js\";\nimport { expandPaths, getLocalPathContext } from \"../core/paths.js\";\nimport { detectProject } from \"../core/project.js\";\nimport { ConflictError, NoSessionsError } from \"../errors.js\";\nimport { readCheckpoint } from \"./checkpoint.js\";\nimport { ensureBatonRepo } from \"./setup.js\";\n\nexport async function pull(options: {\n\tforce?: boolean;\n\tskip?: boolean;\n}): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// 1. Detect project\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote} (${project.projectId})`);\n\n\t// 2. Ensure baton repo is available\n\tawait ensureBatonRepo(\"pull\");\n\n\tconst repoDir = getRepoDir();\n\tif (await repoExists(repoDir)) {\n\t\tconsole.log(\"Pulling latest...\");\n\t\tawait pullRepo(repoDir);\n\t}\n\n\t// 3. Read checkpoint from baton repo\n\tconst projectDir = join(repoDir, \"projects\", project.projectId);\n\tconst data = await readCheckpoint(projectDir);\n\n\tif (!data) {\n\t\tthrow new NoSessionsError(\n\t\t\t\"No checkpoint found for this project. Run 'baton push' on another machine first.\",\n\t\t);\n\t}\n\n\tconsole.log(\n\t\t`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`,\n\t);\n\n\t// 4. Expand paths before conflict detection (so content comparison is meaningful)\n\tconst pathCtx = getLocalPathContext(cwd);\n\tfor (const session of data.sessions) {\n\t\tsession.jsonl = expandPaths(session.jsonl, pathCtx);\n\t}\n\n\t// 5. Detect content-based conflicts\n\tconst localProjectDir = join(getClaudeProjectsDir(), encodeProjectDir(cwd));\n\tconst remoteSessionsDir = join(projectDir, \"sessions\");\n\tconst remoteMemoryDir = join(projectDir, \"memory\");\n\n\tconst localSessionIds = await listLocalSessionIds(cwd);\n\tconst localMemoryFiles = await listLocalMemoryFiles(cwd);\n\tconst remoteSessionIds = data.sessions.map((s) => s.sessionId);\n\tconst remoteMemoryFiles = [...data.memory.keys()];\n\n\tconst conflicts = await detectConflicts(\n\t\tlocalSessionIds,\n\t\tremoteSessionIds,\n\t\tlocalMemoryFiles,\n\t\tremoteMemoryFiles,\n\t\t{ localProjectDir, remoteSessionsDir, remoteMemoryDir },\n\t);\n\n\tif (hasConflicts(conflicts) && !options.force && !options.skip) {\n\t\tthrow new ConflictError(formatConflictMessage(conflicts));\n\t}\n\n\t// 6. Restore to Claude Code's local storage\n\tconst skipSessions = options.skip\n\t\t? new Set(conflicts.sessions.map((c) => c.name))\n\t\t: undefined;\n\tconst skipMemory = options.skip\n\t\t? new Set(conflicts.memoryFiles.map((c) => c.name))\n\t\t: undefined;\n\n\tawait restoreProjectData(cwd, data, {\n\t\tskipSessions,\n\t\tskipMemory,\n\t});\n\n\tif (options.skip && hasConflicts(conflicts)) {\n\t\tconst skipped = conflicts.sessions.length + conflicts.memoryFiles.length;\n\t\tconsole.log(`Pulled successfully. Skipped ${skipped} conflicting file(s).`);\n\t} else {\n\t\tconsole.log(\"Pulled successfully. Sessions restored to Claude Code.\");\n\t}\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Encode a local project path into a Claude Code project directory name.\n * Claude Code replaces `/`, `.`, and `:` with `-`.\n *\n * Examples:\n * /home/dr_who/baton → -home-dr_who-baton\n * /Users/dr_who/work/baton → -Users-dr_who-work-baton\n * C:\\Users\\dr_who\\baton → -C-Users-dr_who-baton\n */\nexport function encodeProjectDir(projectPath: string): string {\n\t// Normalize backslashes to forward slashes (Windows support)\n\tconst normalized = projectPath.replace(/\\\\/g, \"/\");\n\treturn normalized.replace(/[/.:]/g, \"-\");\n}\n\n/**\n * Get the Claude Code projects base directory.\n */\nexport function getClaudeProjectsDir(): string {\n\treturn join(homedir(), \".claude\", \"projects\");\n}\n\n/**\n * Get the full path to a Claude Code project directory.\n */\nexport function getClaudeProjectPath(projectPath: string): string {\n\treturn join(getClaudeProjectsDir(), encodeProjectDir(projectPath));\n}\n\n/**\n * Get the path to Claude Code's project-config.json.\n */\nexport function getProjectConfigPath(): string {\n\treturn join(homedir(), \".claude\", \"project-config.json\");\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport { NoSessionsError } from \"../../errors.js\";\nimport { encodeProjectDir, getClaudeProjectsDir } from \"./paths.js\";\n\nexport interface SessionData {\n\tsessionId: string;\n\tjsonl: string;\n\ttoolResults: Map<string, string>;\n}\n\nexport interface ProjectData {\n\tsessions: SessionData[];\n\tmemory: Map<string, string>;\n\tprojectDirName: string;\n}\n\n/**\n * List local session IDs for a project (lightweight, no content read).\n */\nexport async function listLocalSessionIds(\n\tprojectPath: string,\n): Promise<string[]> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(projectDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn entries\n\t\t.filter((e) => e.endsWith(\".jsonl\"))\n\t\t.map((e) => basename(e, \".jsonl\"));\n}\n\n/**\n * List local memory file names for a project (lightweight, no content read).\n */\nexport async function listLocalMemoryFiles(\n\tprojectPath: string,\n): Promise<string[]> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst memoryDir = join(getClaudeProjectsDir(), projectDirName, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\n/**\n * Collect all session data for a project from Claude Code's local storage.\n */\nexport async function collectProjectData(\n\tprojectPath: string,\n): Promise<ProjectData> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tconst sessions = await collectSessions(projectDir);\n\tif (sessions.length === 0) {\n\t\tthrow new NoSessionsError(\n\t\t\t`No Claude Code sessions found for this project. Start a Claude Code session first.`,\n\t\t);\n\t}\n\n\tconst memory = await collectMemory(projectDir);\n\n\treturn { sessions, memory, projectDirName };\n}\n\nasync function collectSessions(projectDir: string): Promise<SessionData[]> {\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(projectDir);\n\t} catch {\n\t\treturn [];\n\t}\n\n\tconst jsonlFiles = entries.filter((e) => e.endsWith(\".jsonl\"));\n\tconst sessions: SessionData[] = [];\n\n\tfor (const jsonlFile of jsonlFiles) {\n\t\tconst sessionId = basename(jsonlFile, \".jsonl\");\n\t\tconst jsonl = await readFile(join(projectDir, jsonlFile), \"utf-8\");\n\t\tconst toolResults = await collectToolResults(projectDir, sessionId);\n\t\tsessions.push({ sessionId, jsonl, toolResults });\n\t}\n\n\treturn sessions;\n}\n\nasync function collectToolResults(\n\tprojectDir: string,\n\tsessionId: string,\n): Promise<Map<string, string>> {\n\tconst results = new Map<string, string>();\n\tconst toolResultsDir = join(projectDir, sessionId, \"tool-results\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(toolResultsDir);\n\t} catch {\n\t\treturn results;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst filePath = join(toolResultsDir, entry);\n\t\tconst fileStat = await stat(filePath);\n\t\tif (fileStat.isFile()) {\n\t\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\t\tresults.set(entry, content);\n\t\t}\n\t}\n\n\treturn results;\n}\n\nasync function collectMemory(projectDir: string): Promise<Map<string, string>> {\n\tconst memory = new Map<string, string>();\n\tconst memoryDir = join(projectDir, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn memory;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst filePath = join(memoryDir, entry);\n\t\tconst fileStat = await stat(filePath);\n\t\tif (fileStat.isFile()) {\n\t\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\t\tmemory.set(entry, content);\n\t\t}\n\t}\n\n\treturn memory;\n}\n","export class BatonError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = this.constructor.name;\n\t\tError.captureStackTrace?.(this, this.constructor);\n\t}\n}\n\nexport class ProjectNotFoundError extends BatonError {}\n\nexport class NoSessionsError extends BatonError {}\n\nexport class ConflictError extends BatonError {}\n\nexport class GitNotFoundError extends BatonError {}\n\nexport class GhNotFoundError extends BatonError {}\n\nexport class ConfigError extends BatonError {}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n\tencodeProjectDir,\n\tgetClaudeProjectsDir,\n\tgetProjectConfigPath,\n} from \"./paths.js\";\nimport type { ProjectData } from \"./reader.js\";\n\nexport interface RestoreOptions {\n\t/** Session IDs to skip (don't overwrite) */\n\tskipSessions?: Set<string>;\n\t/** Memory file names to skip (don't overwrite) */\n\tskipMemory?: Set<string>;\n}\n\n/**\n * Restore project data to Claude Code's local storage.\n */\nexport async function restoreProjectData(\n\tprojectPath: string,\n\tdata: ProjectData,\n\toptions?: RestoreOptions,\n): Promise<void> {\n\tconst projectDirName = encodeProjectDir(projectPath);\n\tconst projectDir = join(getClaudeProjectsDir(), projectDirName);\n\n\tawait mkdir(projectDir, { recursive: true });\n\n\tconst skipSessions = options?.skipSessions ?? new Set();\n\tconst skipMemory = options?.skipMemory ?? new Set();\n\n\tfor (const session of data.sessions) {\n\t\tif (skipSessions.has(session.sessionId)) continue;\n\n\t\t// Write session JSONL\n\t\tawait writeFile(\n\t\t\tjoin(projectDir, `${session.sessionId}.jsonl`),\n\t\t\tsession.jsonl,\n\t\t\t\"utf-8\",\n\t\t);\n\n\t\t// Write tool results\n\t\tif (session.toolResults.size > 0) {\n\t\t\tconst toolResultsDir = join(\n\t\t\t\tprojectDir,\n\t\t\t\tsession.sessionId,\n\t\t\t\t\"tool-results\",\n\t\t\t);\n\t\t\tawait mkdir(toolResultsDir, { recursive: true });\n\t\t\tfor (const [filename, content] of session.toolResults) {\n\t\t\t\tawait writeFile(join(toolResultsDir, filename), content, \"utf-8\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Write memory files\n\tif (data.memory.size > 0) {\n\t\tconst memoryDir = join(projectDir, \"memory\");\n\t\tawait mkdir(memoryDir, { recursive: true });\n\t\tfor (const [filename, content] of data.memory) {\n\t\t\tif (skipMemory.has(filename)) continue;\n\t\t\tawait writeFile(join(memoryDir, filename), content, \"utf-8\");\n\t\t}\n\t}\n\n\t// Ensure project-config.json has a mapping\n\tawait ensureProjectConfig(projectPath, projectDirName);\n}\n\nasync function ensureProjectConfig(\n\tprojectPath: string,\n\tprojectDirName: string,\n): Promise<void> {\n\tconst configPath = getProjectConfigPath();\n\n\tlet config: Record<\n\t\tstring,\n\t\t{ manuallyAdded?: boolean; originalPath: string }\n\t> = {};\n\ttry {\n\t\tconst raw = await readFile(configPath, \"utf-8\");\n\t\tconfig = JSON.parse(raw);\n\t} catch {\n\t\t// File doesn't exist or is invalid, start fresh\n\t}\n\n\tif (!config[projectDirName]) {\n\t\tconfig[projectDirName] = {\n\t\t\toriginalPath: projectPath,\n\t\t};\n\t\tawait mkdir(dirname(configPath), { recursive: true });\n\t\tawait writeFile(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n\t}\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport interface BatonConfig {\n\trepo: string;\n}\n\n/**\n * Get the path to the baton config directory.\n */\nexport function getBatonDir(): string {\n\treturn join(homedir(), \".baton\");\n}\n\n/**\n * Get the path to the baton config file.\n */\nexport function getConfigPath(): string {\n\treturn join(getBatonDir(), \"config.json\");\n}\n\n/**\n * Get the path to the local repo clone.\n */\nexport function getRepoDir(): string {\n\treturn join(getBatonDir(), \"repo\");\n}\n\n/**\n * Load baton config. Returns null if not configured.\n */\nexport async function loadConfig(): Promise<BatonConfig | null> {\n\ttry {\n\t\tconst raw = await readFile(getConfigPath(), \"utf-8\");\n\t\tconst config = JSON.parse(raw);\n\t\tif (!config.repo || typeof config.repo !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\t\treturn config as BatonConfig;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Save baton config.\n */\nexport async function saveConfig(config: BatonConfig): Promise<void> {\n\tconst configPath = getConfigPath();\n\tawait mkdir(dirname(configPath), { recursive: true });\n\tawait writeFile(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface FileConflict {\n\tname: string;\n\tlocalPath: string;\n\tremotePath: string;\n\tlocalModified: string;\n\tremoteModified: string;\n}\n\nexport interface ConflictInfo {\n\tsessions: FileConflict[];\n\tmemoryFiles: FileConflict[];\n}\n\nexport interface ConflictContext {\n\tlocalProjectDir: string;\n\tremoteSessionsDir: string;\n\tremoteMemoryDir: string;\n}\n\n/**\n * Detect content-based conflicts between local and remote data.\n * Only reports files that actually differ in content.\n * Includes modification timestamps for each conflicting file.\n */\nexport async function detectConflicts(\n\tlocalSessionIds: string[],\n\tremoteSessionIds: string[],\n\tlocalMemoryFiles: string[],\n\tremoteMemoryFiles: string[],\n\tctx: ConflictContext,\n): Promise<ConflictInfo> {\n\tconst remoteIdSet = new Set(remoteSessionIds);\n\tconst overlappingSessionIds = localSessionIds.filter((id) =>\n\t\tremoteIdSet.has(id),\n\t);\n\n\tconst remoteMemSet = new Set(remoteMemoryFiles);\n\tconst overlappingMemory = localMemoryFiles.filter((f) => remoteMemSet.has(f));\n\n\tconst sessions: FileConflict[] = [];\n\tfor (const id of overlappingSessionIds) {\n\t\tconst localPath = join(ctx.localProjectDir, `${id}.jsonl`);\n\t\tconst remotePath = join(ctx.remoteSessionsDir, `${id}.jsonl`);\n\t\tif (await contentDiffers(localPath, remotePath)) {\n\t\t\tsessions.push({\n\t\t\t\tname: id,\n\t\t\t\tlocalPath,\n\t\t\t\tremotePath,\n\t\t\t\tlocalModified: await getModifiedTime(localPath),\n\t\t\t\tremoteModified: await getModifiedTime(remotePath),\n\t\t\t});\n\t\t}\n\t}\n\n\tconst memoryFiles: FileConflict[] = [];\n\tfor (const file of overlappingMemory) {\n\t\tconst localPath = join(ctx.localProjectDir, \"memory\", file);\n\t\tconst remotePath = join(ctx.remoteMemoryDir, file);\n\t\tif (await contentDiffers(localPath, remotePath)) {\n\t\t\tmemoryFiles.push({\n\t\t\t\tname: file,\n\t\t\t\tlocalPath,\n\t\t\t\tremotePath,\n\t\t\t\tlocalModified: await getModifiedTime(localPath),\n\t\t\t\tremoteModified: await getModifiedTime(remotePath),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { sessions, memoryFiles };\n}\n\nasync function contentDiffers(pathA: string, pathB: string): Promise<boolean> {\n\ttry {\n\t\tconst [contentA, contentB] = await Promise.all([\n\t\t\treadFile(pathA, \"utf-8\"),\n\t\t\treadFile(pathB, \"utf-8\"),\n\t\t]);\n\t\treturn contentA !== contentB;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function getModifiedTime(filePath: string): Promise<string> {\n\ttry {\n\t\tconst s = await stat(filePath);\n\t\treturn formatRelativeTime(s.mtime);\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst now = Date.now();\n\tconst diffMs = now - date.getTime();\n\tconst diffMins = Math.floor(diffMs / 60_000);\n\tconst diffHours = Math.floor(diffMs / 3_600_000);\n\tconst diffDays = Math.floor(diffMs / 86_400_000);\n\n\tif (diffMins < 1) return \"just now\";\n\tif (diffMins < 60) return `${diffMins} minute(s) ago`;\n\tif (diffHours < 24) return `${diffHours} hour(s) ago`;\n\treturn `${diffDays} day(s) ago`;\n}\n\n/**\n * Format conflict info into a message readable by both humans and AI agents.\n */\nexport function formatConflictMessage(conflicts: ConflictInfo): string {\n\tconst lines: string[] = [];\n\tlines.push(\"Conflicts detected during baton pull.\");\n\tlines.push(\"\");\n\n\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(`Conflicting sessions (${conflicts.sessions.length}):`);\n\t\tfor (const c of conflicts.sessions) {\n\t\t\tlines.push(` - ${c.name}`);\n\t\t\tlines.push(` local: ${c.localPath} (modified ${c.localModified})`);\n\t\t\tlines.push(` remote: ${c.remotePath} (modified ${c.remoteModified})`);\n\t\t}\n\t\tlines.push(\"\");\n\t}\n\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(`Conflicting memory files (${conflicts.memoryFiles.length}):`);\n\t\tfor (const c of conflicts.memoryFiles) {\n\t\t\tlines.push(` - ${c.name}`);\n\t\t\tlines.push(` local: ${c.localPath} (modified ${c.localModified})`);\n\t\t\tlines.push(` remote: ${c.remotePath} (modified ${c.remoteModified})`);\n\t\t}\n\t\tlines.push(\"\");\n\t}\n\n\tlines.push(\"Choose how to resolve:\");\n\tlines.push(\" 1. Overwrite local with remote (lose local changes)\");\n\tlines.push(\n\t\t\" 2. Keep local, skip remote (local will overwrite remote on next push)\",\n\t);\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\n\t\t\t\" 3. Merge memory files (agent reads both versions and combines them)\",\n\t\t);\n\t}\n\tlines.push(\"\");\n\n\tlines.push(\"Or use flags directly:\");\n\tlines.push(\n\t\t\" baton pull --force option 1: overwrite all local with remote\",\n\t);\n\tlines.push(\n\t\t\" baton pull --skip option 2: only pull non-conflicting files\",\n\t);\n\n\tlines.push(\"\");\n\tlines.push(\"<agent-hint>\");\n\tlines.push(\"Ask the user which option they prefer. Then follow through:\");\n\tlines.push(\"\");\n\tif (conflicts.sessions.length > 0) {\n\t\tlines.push(\"For sessions (cannot be merged — pick one version):\");\n\t\tlines.push(\" Option 1: run `baton pull --force`, then `baton push`\");\n\t\tlines.push(\" Option 2: run `baton pull --skip`, then `baton push`\");\n\t\tlines.push(\"\");\n\t}\n\tif (conflicts.memoryFiles.length > 0) {\n\t\tlines.push(\"For memory files (can be merged):\");\n\t\tlines.push(\" Option 1: run `baton pull --force`, then `baton push`\");\n\t\tlines.push(\" Option 2: run `baton pull --skip`, then `baton push`\");\n\t\tlines.push(\" Option 3: read both local and remote files listed above,\");\n\t\tlines.push(\" merge their content (deduplicate, keep most recent info),\");\n\t\tlines.push(\" write merged result to local path,\");\n\t\tlines.push(\" then run `baton pull --skip` and `baton push`\");\n\t\tlines.push(\"\");\n\t}\n\tlines.push(\"</agent-hint>\");\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Check if there are any conflicts.\n */\nexport function hasConflicts(conflicts: ConflictInfo): boolean {\n\treturn conflicts.sessions.length > 0 || conflicts.memoryFiles.length > 0;\n}\n","import { execFile } from \"node:child_process\";\nimport { access } from \"node:fs/promises\";\nimport { promisify } from \"node:util\";\nimport { GhNotFoundError, GitNotFoundError } from \"../errors.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_TIMEOUT_MS = 30_000;\nconst GH_TIMEOUT_MS = 60_000;\n\n/**\n * Run a git command in the given directory.\n */\nexport async function git(args: string[], cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\"git\", args, {\n\t\t\tcwd,\n\t\t\ttimeout: GIT_TIMEOUT_MS,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch (error) {\n\t\tif (isNotFound(error)) {\n\t\t\tthrow new GitNotFoundError(\"git is not installed or not found in PATH.\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Run a gh CLI command.\n */\nexport async function gh(args: string[]): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\"gh\", args, {\n\t\t\ttimeout: GH_TIMEOUT_MS,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch (error) {\n\t\tif (isNotFound(error)) {\n\t\t\tthrow new GhNotFoundError(\n\t\t\t\t\"gh CLI is not installed. Install it from https://cli.github.com/\",\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if the local repo clone exists.\n */\nexport async function repoExists(repoDir: string): Promise<boolean> {\n\ttry {\n\t\tawait access(repoDir);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Clone a repo to the local cache directory.\n */\nexport async function cloneRepo(\n\trepoUrl: string,\n\ttargetDir: string,\n): Promise<void> {\n\tawait execFileAsync(\"git\", [\"clone\", repoUrl, targetDir], {\n\t\ttimeout: GH_TIMEOUT_MS,\n\t});\n}\n\n/**\n * Fetch latest from remote without merging.\n */\nexport async function fetchRepo(repoDir: string): Promise<void> {\n\tawait git([\"fetch\", \"origin\"], repoDir);\n}\n\n/**\n * Pull latest from remote (fast-forward).\n */\nexport async function pullRepo(repoDir: string): Promise<void> {\n\tawait git([\"pull\", \"--ff-only\"], repoDir);\n}\n\n/**\n * Check if remote is ahead of local (has commits we haven't pulled).\n */\nexport async function isRemoteAhead(repoDir: string): Promise<boolean> {\n\tawait fetchRepo(repoDir);\n\tconst localHead = await git([\"rev-parse\", \"HEAD\"], repoDir);\n\tconst remoteHead = await git([\"rev-parse\", \"origin/main\"], repoDir);\n\treturn localHead !== remoteHead;\n}\n\n/**\n * Stage, commit, and push changes.\n */\nexport async function commitAndPush(\n\trepoDir: string,\n\tmessage: string,\n\tforce: boolean,\n): Promise<void> {\n\tawait git([\"add\", \"-A\"], repoDir);\n\n\t// Check if there are changes to commit\n\tconst status = await git([\"status\", \"--porcelain\"], repoDir);\n\tif (!status) {\n\t\treturn; // Nothing to commit\n\t}\n\n\tawait git([\"commit\", \"-m\", message], repoDir);\n\n\tconst pushArgs = force\n\t\t? [\"push\", \"--force\", \"origin\", \"main\"]\n\t\t: [\"push\", \"origin\", \"main\"];\n\tawait git(pushArgs, repoDir);\n}\n\n/**\n * Create a private GitHub repo via gh CLI.\n */\nexport async function createGhRepo(repoName: string): Promise<string> {\n\tconst output = await gh([\n\t\t\"repo\",\n\t\t\"create\",\n\t\trepoName,\n\t\t\"--private\",\n\t\t\"--confirm\",\n\t]);\n\t// gh repo create outputs the URL\n\tconst match = output.match(/https:\\/\\/github\\.com\\/[\\w.-]+\\/[\\w.-]+/);\n\tif (match) {\n\t\treturn match[0];\n\t}\n\t// Fallback: construct from gh whoami\n\tconst username = await gh([\"api\", \"user\", \"--jq\", \".login\"]);\n\treturn `https://github.com/${username}/${repoName}`;\n}\n\n/**\n * Initialize a new git repo with an initial commit.\n */\nexport async function initRepo(repoDir: string): Promise<void> {\n\tawait git([\"init\", \"-b\", \"main\"], repoDir);\n\tawait git([\"config\", \"user.name\", \"baton\"], repoDir);\n\tawait git([\"config\", \"user.email\", \"baton@localhost\"], repoDir);\n\tawait git([\"commit\", \"--allow-empty\", \"-m\", \"init baton repo\"], repoDir);\n}\n\nfunction isNotFound(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\treturn (error as NodeJS.ErrnoException).code === \"ENOENT\";\n}\n","import { homedir, tmpdir } from \"node:os\";\nimport { sep } from \"node:path\";\n\nconst PLACEHOLDER_PROJECT_ROOT = \"$\" + \"{PROJECT_ROOT}\";\nconst PLACEHOLDER_HOME = \"$\" + \"{HOME}\";\nconst PLACEHOLDER_TMP = \"$\" + \"{TMP}\";\n\nconst PLACEHOLDERS = {\n\tPROJECT_ROOT: PLACEHOLDER_PROJECT_ROOT,\n\tHOME: PLACEHOLDER_HOME,\n\tTMP: PLACEHOLDER_TMP,\n} as const;\n\ninterface PathContext {\n\tprojectRoot: string;\n\thome: string;\n\ttmp: string;\n}\n\n/**\n * Get the current machine's path context.\n */\nexport function getLocalPathContext(projectRoot: string): PathContext {\n\treturn {\n\t\tprojectRoot,\n\t\thome: homedir(),\n\t\ttmp: tmpdir(),\n\t};\n}\n\n/**\n * Virtualize paths in content: replace machine-local absolute paths\n * with portable placeholders.\n *\n * Replaces longest paths first to prevent partial matches.\n * Normalizes path separators to `/` in stored content.\n */\nexport function virtualizePaths(content: string, ctx: PathContext): string {\n\t// Normalize backslashes to forward slashes for consistent matching\n\tconst normalizedContent =\n\t\tsep === \"\\\\\" ? content.replace(/\\\\/g, \"/\") : content;\n\n\t// Build replacement pairs, sorted by path length (longest first)\n\tconst replacements: [string, string][] = (\n\t\t[\n\t\t\t[normalizeSeparators(ctx.projectRoot), PLACEHOLDERS.PROJECT_ROOT],\n\t\t\t[normalizeSeparators(ctx.home), PLACEHOLDERS.HOME],\n\t\t\t[normalizeSeparators(ctx.tmp), PLACEHOLDERS.TMP],\n\t\t] as [string, string][]\n\t).sort((a, b) => b[0].length - a[0].length);\n\n\tlet result = normalizedContent;\n\tfor (const [path, placeholder] of replacements) {\n\t\tif (path) {\n\t\t\tresult = replacePathWithBoundary(result, path, placeholder);\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Expand placeholders in content: replace portable placeholders\n * with machine-local absolute paths.\n *\n * Expands to OS-native path separators.\n */\nexport function expandPaths(content: string, ctx: PathContext): string {\n\tlet result = content;\n\n\tresult = replaceAll(\n\t\tresult,\n\t\tPLACEHOLDERS.PROJECT_ROOT,\n\t\ttoNativePath(ctx.projectRoot),\n\t);\n\tresult = replaceAll(result, PLACEHOLDERS.HOME, toNativePath(ctx.home));\n\tresult = replaceAll(result, PLACEHOLDERS.TMP, toNativePath(ctx.tmp));\n\n\treturn result;\n}\n\n/**\n * Normalize path separators to forward slashes.\n */\nfunction normalizeSeparators(path: string): string {\n\treturn path.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Convert a path to use the OS-native separator.\n */\nfunction toNativePath(path: string): string {\n\tif (sep === \"\\\\\") {\n\t\treturn path.replace(/\\//g, \"\\\\\");\n\t}\n\treturn path;\n}\n\n/**\n * Replace all occurrences of a path, but only when followed by a path\n * boundary character (/, \\, \", whitespace, end of string, etc.).\n * Prevents matching /home/dr_who inside /home/dr_who_backup.\n */\nfunction replacePathWithBoundary(\n\tstr: string,\n\tpath: string,\n\treplacement: string,\n): string {\n\tconst escaped = escapeRegex(path);\n\tconst regex = new RegExp(`${escaped}(?=[/\\\\\\\\\"\\\\s,}\\\\]]|$)`, \"g\");\n\treturn str.replace(regex, replacement);\n}\n\n/**\n * Replace all occurrences of a substring.\n */\nfunction replaceAll(str: string, search: string, replacement: string): string {\n\treturn str.split(search).join(replacement);\n}\n\n/**\n * Escape special regex characters in a string.\n */\nfunction escapeRegex(str: string): string {\n\treturn str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\nimport { GitNotFoundError, ProjectNotFoundError } from \"../errors.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_TIMEOUT_MS = 10_000;\n\n/**\n * Detect the git remote URL from the current working directory.\n * Uses the \"origin\" remote by default.\n */\nexport async function getGitRemote(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execFileAsync(\n\t\t\t\"git\",\n\t\t\t[\"remote\", \"get-url\", \"origin\"],\n\t\t\t{ cwd, timeout: GIT_TIMEOUT_MS },\n\t\t);\n\t\tconst remote = stdout.trim();\n\t\tif (!remote) {\n\t\t\tthrow new ProjectNotFoundError(\n\t\t\t\t\"No git remote URL found. Run this command from a git repository with an 'origin' remote.\",\n\t\t\t);\n\t\t}\n\t\treturn remote;\n\t} catch (error) {\n\t\tif (error instanceof ProjectNotFoundError) throw error;\n\t\tif (isGitNotInstalled(error)) {\n\t\t\tthrow new GitNotFoundError(\n\t\t\t\t\"git is not installed or not found in PATH. Please install git first.\",\n\t\t\t);\n\t\t}\n\t\tthrow new ProjectNotFoundError(\n\t\t\t\"Not a git repository or no 'origin' remote configured. Run this command from a git repository.\",\n\t\t);\n\t}\n}\n\nfunction isGitNotInstalled(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst err = error as NodeJS.ErrnoException;\n\treturn err.code === \"ENOENT\";\n}\n\n/**\n * Normalize a git remote URL to a canonical form.\n *\n * Handles:\n * - git@github.com:user/repo.git → github.com/user/repo\n * - https://github.com/user/repo.git → github.com/user/repo\n * - https://github.com/user/repo → github.com/user/repo\n * - ssh://git@github.com/user/repo.git → github.com/user/repo\n * - git://github.com/user/repo.git → github.com/user/repo\n */\nexport function normalizeGitRemote(remote: string): string {\n\tconst normalized = remote.trim();\n\n\t// SSH shorthand: git@github.com:user/repo.git\n\tconst sshMatch = normalized.match(/^[\\w.-]+@([\\w.-]+):(.*?)(?:\\.git)?$/);\n\tif (sshMatch) {\n\t\treturn `${sshMatch[1]}/${sshMatch[2]}`;\n\t}\n\n\t// ssh:// or git:// protocol: ssh://git@github.com/user/repo.git\n\tconst protoMatch = normalized.match(\n\t\t/^(?:ssh|git):\\/\\/(?:[\\w.-]+@)?([\\w.-]+)\\/(.*?)(?:\\.git)?$/,\n\t);\n\tif (protoMatch) {\n\t\treturn `${protoMatch[1]}/${protoMatch[2]}`;\n\t}\n\n\t// HTTPS: https://github.com/user/repo.git\n\tconst httpsMatch = normalized.match(\n\t\t/^https?:\\/\\/([\\w.-]+)\\/(.*?)(?:\\.git)?$/,\n\t);\n\tif (httpsMatch) {\n\t\treturn `${httpsMatch[1]}/${httpsMatch[2]}`;\n\t}\n\n\t// Fallback: return as-is (stripped)\n\treturn normalized;\n}\n\n/**\n * Hash a normalized git remote URL to produce a stable project ID.\n */\nexport function hashProjectId(normalizedRemote: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(normalizedRemote)\n\t\t.digest(\"hex\")\n\t\t.slice(0, 16);\n}\n\n/**\n * Detect the project from the current working directory.\n * Returns the project hash and normalized git remote.\n */\nexport async function detectProject(\n\tcwd: string,\n): Promise<{ projectId: string; gitRemote: string; normalizedRemote: string }> {\n\tconst gitRemote = await getGitRemote(cwd);\n\tconst normalizedRemote = normalizeGitRemote(gitRemote);\n\tconst projectId = hashProjectId(normalizedRemote);\n\treturn { projectId, gitRemote, normalizedRemote };\n}\n","import { mkdir, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n\tProjectData,\n\tSessionData,\n} from \"../adapters/claude-code/reader.js\";\n\ninterface ProjectMeta {\n\tproject_id: string;\n\tgit_remote: string;\n\tpushed_at: string;\n}\n\n/**\n * Write project data as a checkpoint to the baton repo.\n */\nexport async function writeCheckpoint(\n\tprojectDir: string,\n\tproject: { projectId: string; gitRemote: string },\n\tdata: ProjectData,\n): Promise<void> {\n\t// Clean existing sessions directory to remove stale sessions\n\tconst sessionsDir = join(projectDir, \"sessions\");\n\tawait rm(sessionsDir, { recursive: true, force: true });\n\tawait mkdir(sessionsDir, { recursive: true });\n\n\t// Write meta\n\tconst meta: ProjectMeta = {\n\t\tproject_id: project.projectId,\n\t\tgit_remote: project.gitRemote,\n\t\tpushed_at: new Date().toISOString(),\n\t};\n\tawait writeFile(\n\t\tjoin(projectDir, \"meta.json\"),\n\t\tJSON.stringify(meta, null, 2),\n\t\t\"utf-8\",\n\t);\n\n\t// Write sessions\n\tfor (const session of data.sessions) {\n\t\tawait writeFile(\n\t\t\tjoin(sessionsDir, `${session.sessionId}.jsonl`),\n\t\t\tsession.jsonl,\n\t\t\t\"utf-8\",\n\t\t);\n\n\t\t// Write tool results\n\t\tif (session.toolResults.size > 0) {\n\t\t\tconst toolResultsDir = join(\n\t\t\t\tsessionsDir,\n\t\t\t\tsession.sessionId,\n\t\t\t\t\"tool-results\",\n\t\t\t);\n\t\t\tawait mkdir(toolResultsDir, { recursive: true });\n\t\t\tfor (const [filename, content] of session.toolResults) {\n\t\t\t\tawait writeFile(join(toolResultsDir, filename), content, \"utf-8\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Write memory\n\tif (data.memory.size > 0) {\n\t\tconst memoryDir = join(projectDir, \"memory\");\n\t\tawait mkdir(memoryDir, { recursive: true });\n\t\tfor (const [filename, content] of data.memory) {\n\t\t\tawait writeFile(join(memoryDir, filename), content, \"utf-8\");\n\t\t}\n\t}\n}\n\n/**\n * Read a checkpoint from the baton repo. Returns null if not found.\n */\nexport async function readCheckpoint(\n\tprojectDir: string,\n): Promise<ProjectData | null> {\n\tconst sessionsDir = join(projectDir, \"sessions\");\n\n\tlet sessionFiles: string[];\n\ttry {\n\t\tconst entries = await readdir(sessionsDir);\n\t\tsessionFiles = entries.filter((e) => e.endsWith(\".jsonl\"));\n\t} catch {\n\t\treturn null;\n\t}\n\n\tif (sessionFiles.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst sessions: SessionData[] = [];\n\tfor (const file of sessionFiles) {\n\t\tconst sessionId = file.replace(\".jsonl\", \"\");\n\t\tconst jsonl = await readFile(join(sessionsDir, file), \"utf-8\");\n\t\tconst toolResults = await readToolResults(sessionsDir, sessionId);\n\t\tsessions.push({ sessionId, jsonl, toolResults });\n\t}\n\n\tconst memory = await readMemory(projectDir);\n\n\treturn {\n\t\tsessions,\n\t\tmemory,\n\t\tprojectDirName: \"\",\n\t};\n}\n\nasync function readToolResults(\n\tsessionsDir: string,\n\tsessionId: string,\n): Promise<Map<string, string>> {\n\tconst results = new Map<string, string>();\n\tconst toolResultsDir = join(sessionsDir, sessionId, \"tool-results\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(toolResultsDir);\n\t} catch {\n\t\treturn results;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst content = await readFile(join(toolResultsDir, entry), \"utf-8\");\n\t\tresults.set(entry, content);\n\t}\n\n\treturn results;\n}\n\nasync function readMemory(projectDir: string): Promise<Map<string, string>> {\n\tconst memory = new Map<string, string>();\n\tconst memoryDir = join(projectDir, \"memory\");\n\n\tlet entries: string[];\n\ttry {\n\t\tentries = await readdir(memoryDir);\n\t} catch {\n\t\treturn memory;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst content = await readFile(join(memoryDir, entry), \"utf-8\");\n\t\tmemory.set(entry, content);\n\t}\n\n\treturn memory;\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { getRepoDir, loadConfig, saveConfig } from \"../core/config.js\";\nimport { cloneRepo, createGhRepo, gh, repoExists } from \"../core/git.js\";\n\nconst DEFAULT_REPO_NAME = \"baton-sessions\";\n\n/**\n * Ensure the baton repo is configured and cloned locally.\n * On first run:\n * - push: auto-creates a private \"baton-sessions\" repo\n * - pull: tries the current user's \"baton-sessions\" repo, prompts if not found\n */\nexport async function ensureBatonRepo(mode: \"push\" | \"pull\"): Promise<void> {\n\tconst repoDir = getRepoDir();\n\tlet config = await loadConfig();\n\n\tif (!config) {\n\t\tif (mode === \"push\") {\n\t\t\tconfig = await promptCreateRepo();\n\t\t} else {\n\t\t\tconfig = await autoDetectOrPrompt();\n\t\t}\n\t\tawait saveConfig(config);\n\t}\n\n\tif (!(await repoExists(repoDir))) {\n\t\tconsole.log(\"Cloning baton repo...\");\n\t\tawait cloneRepo(config.repo, repoDir);\n\t}\n}\n\nasync function getGitHubUsername(): Promise<string> {\n\treturn await gh([\"api\", \"user\", \"--jq\", \".login\"]);\n}\n\nasync function promptCreateRepo(): Promise<{ repo: string }> {\n\tconst rl = createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stdout,\n\t});\n\ttry {\n\t\tconst rawName = await rl.question(\n\t\t\t`Enter repo name for baton sync (${DEFAULT_REPO_NAME}): `,\n\t\t);\n\t\tconst repoName = rawName.trim() || DEFAULT_REPO_NAME;\n\t\tconsole.log(`Creating private repo '${repoName}'...`);\n\t\tconst repoUrl = await createGhRepo(repoName);\n\t\tconsole.log(`Repo created: ${repoUrl}`);\n\t\treturn { repo: repoUrl };\n\t} finally {\n\t\trl.close();\n\t}\n}\n\nasync function autoDetectOrPrompt(): Promise<{ repo: string }> {\n\t// Try the current user's default repo name first\n\tconst username = await getGitHubUsername();\n\tconst defaultUrl = `https://github.com/${username}/${DEFAULT_REPO_NAME}`;\n\n\ttry {\n\t\t// Check if the repo exists\n\t\tawait gh([\n\t\t\t\"repo\",\n\t\t\t\"view\",\n\t\t\t`${username}/${DEFAULT_REPO_NAME}`,\n\t\t\t\"--json\",\n\t\t\t\"name\",\n\t\t]);\n\t\tconsole.log(`Found baton repo: ${defaultUrl}`);\n\t\treturn { repo: defaultUrl };\n\t} catch (error) {\n\t\t// Re-throw if not a \"repo not found\" error (e.g., auth, network, gh not installed)\n\t\tif (!isRepoNotFoundError(error)) {\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Repo doesn't exist, prompt for URL\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\ttry {\n\t\t\tconst rawUrl = await rl.question(\n\t\t\t\t`No '${DEFAULT_REPO_NAME}' repo found for ${username}. Enter your baton repo URL: `,\n\t\t\t);\n\t\t\tconst repoUrl = rawUrl.trim();\n\t\t\tif (!repoUrl) {\n\t\t\t\tthrow new Error(\"Repo URL cannot be empty.\");\n\t\t\t}\n\t\t\treturn { repo: repoUrl };\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\t}\n}\n\nfunction isRepoNotFoundError(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst stderr = (error as { stderr?: string }).stderr ?? \"\";\n\tconst message = error.message + stderr;\n\treturn (\n\t\tmessage.includes(\"Could not resolve\") ||\n\t\tmessage.includes(\"HTTP 404\") ||\n\t\tmessage.includes(\"Not Found\")\n\t);\n}\n","import { join } from \"node:path\";\nimport { collectProjectData } from \"../adapters/claude-code/reader.js\";\nimport { getRepoDir } from \"../core/config.js\";\nimport { commitAndPush, isRemoteAhead, repoExists } from \"../core/git.js\";\nimport { getLocalPathContext, virtualizePaths } from \"../core/paths.js\";\nimport { detectProject } from \"../core/project.js\";\nimport { ConflictError } from \"../errors.js\";\nimport { writeCheckpoint } from \"./checkpoint.js\";\nimport { ensureBatonRepo } from \"./setup.js\";\n\nexport async function push(options: { force?: boolean }): Promise<void> {\n\tconst cwd = process.cwd();\n\n\t// 1. Detect project\n\tconst project = await detectProject(cwd);\n\tconsole.log(`Project: ${project.normalizedRemote} (${project.projectId})`);\n\n\t// 2. Collect session data\n\tconst data = await collectProjectData(cwd);\n\tconsole.log(\n\t\t`Found ${data.sessions.length} session(s), ${data.memory.size} memory file(s)`,\n\t);\n\n\t// 3. Virtualize paths\n\tconst pathCtx = getLocalPathContext(cwd);\n\tfor (const session of data.sessions) {\n\t\tsession.jsonl = virtualizePaths(session.jsonl, pathCtx);\n\t}\n\n\t// 4. Ensure baton repo exists\n\tconst repoDir = getRepoDir();\n\tawait ensureBatonRepo(\"push\");\n\n\t// 5. Conflict check\n\tif (!options.force && (await repoExists(repoDir))) {\n\t\ttry {\n\t\t\tconst ahead = await isRemoteAhead(repoDir);\n\t\t\tif (ahead) {\n\t\t\t\tthrow new ConflictError(\n\t\t\t\t\t[\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":["join","join","join","readFile","join","join","readFile","mkdir","readFile","writeFile","homedir","dirname","join","readFile","stat","join","status","homedir","execFile","promisify","execFileAsync","promisify","execFile","GIT_TIMEOUT_MS","mkdir","readdir","readFile","writeFile","join","join","join","join","readdir","readFile","join","join","readdir","readFile"]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baton-cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Git-backed session handoff for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {