@claude-sync/cli 0.1.17 → 0.1.18

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.
@@ -55,12 +55,12 @@ var require_path_encoding = __commonJS({
55
55
  init_esm_shims();
56
56
  Object.defineProperty(exports, "__esModule", { value: true });
57
57
  exports.ROOT_PATTERN = void 0;
58
- exports.encodePath = encodePath3;
58
+ exports.encodePath = encodePath5;
59
59
  exports.decodeSuffix = decodeSuffix;
60
60
  exports.decodeRoot = decodeRoot;
61
61
  exports.extractCanonicalPath = extractCanonicalPath;
62
62
  exports.ROOT_PATTERN = /^-(home-[^-]+|Users-[^-]+)/;
63
- function encodePath3(p) {
63
+ function encodePath5(p) {
64
64
  return p.replace(/\//g, "-").replace(/\./g, "-");
65
65
  }
66
66
  function decodeSuffix(suffix, projectPath) {
@@ -80,7 +80,7 @@ var require_path_encoding = __commonJS({
80
80
  }
81
81
  function extractCanonicalPath(fullPath, homeDir) {
82
82
  const relative = fullPath.startsWith(homeDir) ? fullPath.slice(homeDir.length) : fullPath;
83
- return encodePath3(relative.startsWith("/") ? relative : "/" + relative);
83
+ return encodePath5(relative.startsWith("/") ? relative : "/" + relative);
84
84
  }
85
85
  }
86
86
  });
@@ -4346,7 +4346,7 @@ var require_validation = __commonJS({
4346
4346
  "use strict";
4347
4347
  init_esm_shims();
4348
4348
  Object.defineProperty(exports, "__esModule", { value: true });
4349
- exports.updateCollaboratorRoleSchema = exports.inviteCollaboratorSchema = exports.editableRoleSchema = exports.projectRoleSchema = exports.createFeatureRequestCommentSchema = exports.updateFeatureRequestStatusSchema = exports.updateFeatureRequestSchema = exports.createFeatureRequestSchema = exports.featureRequestStatusSchema = exports.featureRequestTypeSchema = exports.updateProfileSchema = exports.resetPasswordSchema = exports.forgotPasswordSchema = exports.oauthExchangeSchema = exports.refreshTokenSchema = exports.syncCompleteSchema = exports.downloadUrlSchema = exports.uploadUrlSchema = exports.pullRequestSchema = exports.pushManifestSchema = exports.updateDeviceSchema = exports.createDeviceSchema = exports.registerSchema = exports.loginSchema = void 0;
4349
+ exports.updateCollaboratorRoleSchema = exports.inviteCollaboratorSchema = exports.editableRoleSchema = exports.projectRoleSchema = exports.createFeatureRequestCommentSchema = exports.updateFeatureRequestStatusSchema = exports.updateFeatureRequestSchema = exports.createFeatureRequestSchema = exports.featureRequestStatusSchema = exports.featureRequestTypeSchema = exports.updateProfileSchema = exports.resetPasswordSchema = exports.forgotPasswordSchema = exports.oauthExchangeSchema = exports.refreshTokenSchema = exports.pushBundleCompleteSchema = exports.pushBundlePrepareSchema = exports.manifestEntrySchema = exports.syncCompleteSchema = exports.downloadUrlSchema = exports.uploadUrlSchema = exports.pullRequestSchema = exports.pushManifestSchema = exports.updateDeviceSchema = exports.createDeviceSchema = exports.registerSchema = exports.loginSchema = void 0;
4350
4350
  var zod_1 = require_zod();
4351
4351
  exports.loginSchema = zod_1.z.object({
4352
4352
  email: zod_1.z.string().email(),
@@ -4405,6 +4405,27 @@ var require_validation = __commonJS({
4405
4405
  isCompressed: zod_1.z.boolean()
4406
4406
  }))
4407
4407
  });
4408
+ exports.manifestEntrySchema = zod_1.z.object({
4409
+ path: zod_1.z.string(),
4410
+ hash: zod_1.z.string(),
4411
+ size: zod_1.z.number(),
4412
+ modifiedAt: zod_1.z.string(),
4413
+ isCompressed: zod_1.z.boolean()
4414
+ });
4415
+ exports.pushBundlePrepareSchema = zod_1.z.object({
4416
+ deviceId: zod_1.z.string().uuid(),
4417
+ projectPath: zod_1.z.string().min(1),
4418
+ projectId: zod_1.z.string().uuid().optional(),
4419
+ manifest: zod_1.z.array(exports.manifestEntrySchema),
4420
+ bundleSize: zod_1.z.number().positive(),
4421
+ localVersion: zod_1.z.number().int().min(0),
4422
+ message: zod_1.z.string().max(500).optional()
4423
+ });
4424
+ exports.pushBundleCompleteSchema = zod_1.z.object({
4425
+ syncEventId: zod_1.z.string().uuid(),
4426
+ manifest: zod_1.z.array(exports.manifestEntrySchema),
4427
+ message: zod_1.z.string().min(1).max(500)
4428
+ });
4408
4429
  exports.refreshTokenSchema = zod_1.z.object({
4409
4430
  refreshToken: zod_1.z.string()
4410
4431
  });
@@ -4703,8 +4724,8 @@ var init_auth = __esm({
4703
4724
  // src/lib/progress.ts
4704
4725
  import ora from "ora";
4705
4726
  import chalk from "chalk";
4706
- function createSpinner(text4) {
4707
- return ora({ text: brandColor(text4), spinner: "dots", color: "magenta" });
4727
+ function createSpinner(text5) {
4728
+ return ora({ text: brandColor(text5), spinner: "dots", color: "magenta" });
4708
4729
  }
4709
4730
  function formatBytes(bytes) {
4710
4731
  if (bytes === 0) return "0 B";
@@ -4782,6 +4803,9 @@ function printSuccess(message) {
4782
4803
  function printError(message) {
4783
4804
  console.log(` ${error("\u2716")} ${message}`);
4784
4805
  }
4806
+ function printWarn(message) {
4807
+ console.log(` ${warn("\u25B2")} ${message}`);
4808
+ }
4785
4809
  function printInfo(message) {
4786
4810
  console.log(` ${brand("\u25CF")} ${message}`);
4787
4811
  }
@@ -5280,19 +5304,23 @@ var init_bundle = __esm({
5280
5304
  }
5281
5305
  });
5282
5306
 
5283
- // src/commands/sync.ts
5284
- var sync_exports = {};
5285
- __export(sync_exports, {
5286
- runSync: () => runSync,
5287
- syncCommand: () => syncCommand
5307
+ // src/commands/push.ts
5308
+ var push_exports = {};
5309
+ __export(push_exports, {
5310
+ pushCommand: () => pushCommand
5288
5311
  });
5289
5312
  import { Command as Command4 } from "commander";
5290
- import { select as select2, isCancel as isCancel3 } from "@clack/prompts";
5291
- import { mkdir as mkdir3, lstat as lstat2, symlink, unlink, rename, rm } from "fs/promises";
5313
+ import { text as text3, isCancel as isCancel3 } from "@clack/prompts";
5292
5314
  import { join as join4 } from "path";
5293
5315
  import { homedir as homedir4, tmpdir } from "os";
5294
- async function runSync(options) {
5295
- printIntro("Sync");
5316
+ import { rm } from "fs/promises";
5317
+ function pushCommand() {
5318
+ return new Command4("push").description("Push local changes to cloud").option("-m, --message <message>", "Commit message").option("--dry-run", "Show what would be pushed without pushing").option("--verbose", "Show detailed output").action(async (options) => {
5319
+ await runPush(options);
5320
+ });
5321
+ }
5322
+ async function runPush(options) {
5323
+ printIntro("Push");
5296
5324
  const config = await loadConfig();
5297
5325
  if (!isAuthenticated(config)) {
5298
5326
  printError("Not logged in. Run `claude-sync login` first.");
@@ -5312,6 +5340,582 @@ async function runSync(options) {
5312
5340
  projectDir = join4(projectsDir, ctx.symlinkTarget);
5313
5341
  }
5314
5342
  printInfo(`Project: ${brand(cwd)}`);
5343
+ const manifest = await buildProjectManifest(cwd);
5344
+ if (manifest.length === 0) {
5345
+ printInfo("No files to push.");
5346
+ printOutro("Done");
5347
+ return;
5348
+ }
5349
+ const totalBytes = manifest.reduce((sum, f) => sum + f.size, 0);
5350
+ printInfo(`Files: ${brand(String(manifest.length))} ${dim(`(${formatBytes(totalBytes)})`)}`);
5351
+ if (options.dryRun) {
5352
+ console.log();
5353
+ printInfo("Changes to push:");
5354
+ if (options.verbose) {
5355
+ for (const entry of manifest) {
5356
+ console.log(` ${success("+")} ${entry.path} ${dim(`(${formatBytes(entry.size)})`)}`);
5357
+ }
5358
+ } else {
5359
+ console.log(` ${brand(String(manifest.length))} files would be uploaded`);
5360
+ }
5361
+ printOutro("Dry run complete");
5362
+ return;
5363
+ }
5364
+ if (projectLink?.projectId) {
5365
+ const permission = await checkPushPermission(client, projectLink.projectId);
5366
+ if (!permission.allowed) {
5367
+ printError(`Cannot push: You have ${brand(permission.role)} access to this project.`);
5368
+ printInfo("Only project editors and owners can push changes.");
5369
+ return;
5370
+ }
5371
+ }
5372
+ const localVersion = projectLink?.localVersion ?? 0;
5373
+ let commitMessage = options.message;
5374
+ if (!commitMessage) {
5375
+ const result = await text3({
5376
+ message: brand("Commit message:"),
5377
+ placeholder: "Describe your changes...",
5378
+ validate: (value) => {
5379
+ if (!value || value.trim().length === 0) {
5380
+ return "Please enter a commit message";
5381
+ }
5382
+ if (value.length > 500) {
5383
+ return "Message too long (max 500 characters)";
5384
+ }
5385
+ }
5386
+ });
5387
+ if (isCancel3(result)) {
5388
+ printInfo("Push cancelled.");
5389
+ return;
5390
+ }
5391
+ commitMessage = result;
5392
+ }
5393
+ const tempBundle = join4(tmpdir(), `claude-sync-bundle-${Date.now()}.tar.gz`);
5394
+ try {
5395
+ const spinner = createSpinner("Preparing...").start();
5396
+ const bundleResult = await createBundle(projectDir, manifest, tempBundle, (bytes, total, phase) => {
5397
+ if (phase === "compressing") {
5398
+ const percent = Math.round(bytes / total * 100);
5399
+ spinner.text = `Compressing files... ${dim(`${percent}%`)}`;
5400
+ }
5401
+ });
5402
+ spinner.text = "Checking remote...";
5403
+ let prepareResponse;
5404
+ try {
5405
+ prepareResponse = await client.post("/api/sync/push/bundle/prepare", {
5406
+ deviceId: config.deviceId,
5407
+ projectPath: cwd,
5408
+ projectId: projectLink?.projectId,
5409
+ manifest,
5410
+ bundleSize: bundleResult.size,
5411
+ localVersion,
5412
+ message: commitMessage
5413
+ });
5414
+ } catch (err) {
5415
+ spinner.stop();
5416
+ if (err instanceof AuthExpiredError) throw err;
5417
+ printError(`Push failed: ${err instanceof Error ? err.message : "Unknown error"}`);
5418
+ return;
5419
+ }
5420
+ if (!prepareResponse.canProceed) {
5421
+ spinner.stop();
5422
+ console.log();
5423
+ printError(`Cannot push: You are ${brand(String(prepareResponse.behindBy))} version(s) behind.`);
5424
+ console.log();
5425
+ if (prepareResponse.recentCommits && prepareResponse.recentCommits.length > 0) {
5426
+ printInfo("Recent commits on remote:");
5427
+ for (const commit of prepareResponse.recentCommits) {
5428
+ const deviceName = commit.device?.name || "Unknown";
5429
+ const timeAgo = formatTimeAgo(commit.createdAt);
5430
+ console.log(` v${commit.version} "${commit.message}" ${dim(`(${deviceName}, ${timeAgo})`)}`);
5431
+ }
5432
+ console.log();
5433
+ }
5434
+ printInfo(`Run ${brand("claude-sync pull")} first to get these changes.`);
5435
+ return;
5436
+ }
5437
+ const progress = createTransferProgress("Uploading", bundleResult.size, spinner);
5438
+ await streamUpload(prepareResponse.uploadUrl, tempBundle, (bytes) => {
5439
+ progress.update(bytes);
5440
+ });
5441
+ let completeResponse;
5442
+ try {
5443
+ completeResponse = await client.post("/api/sync/push/bundle/complete", {
5444
+ syncEventId: prepareResponse.syncEventId,
5445
+ manifest,
5446
+ message: commitMessage
5447
+ });
5448
+ } catch (err) {
5449
+ spinner.stop();
5450
+ if (err instanceof AuthExpiredError) throw err;
5451
+ printError(`Push failed: ${err instanceof Error ? err.message : "Unknown error"}`);
5452
+ return;
5453
+ }
5454
+ const newVersion = completeResponse.commit.version;
5455
+ const compressionRatio = Math.round((1 - bundleResult.size / bundleResult.originalSize) * 100);
5456
+ progress.finish(`Pushed ${manifest.length} files ${dim(`(${formatBytes(bundleResult.size)}, ${compressionRatio}% smaller)`)}`);
5457
+ await saveProjectLink(cwd, {
5458
+ projectId: prepareResponse.projectId,
5459
+ foreignEncodedDir: (0, import_utils3.encodePath)(cwd),
5460
+ linkedAt: projectLink?.linkedAt || (/* @__PURE__ */ new Date()).toISOString(),
5461
+ localVersion: newVersion
5462
+ });
5463
+ console.log();
5464
+ printSuccess(`Commit: "${commitMessage}"`);
5465
+ printInfo(`Version: ${localVersion} \u2192 ${newVersion}`);
5466
+ printOutro("Done");
5467
+ } finally {
5468
+ try {
5469
+ await rm(tempBundle, { force: true });
5470
+ } catch {
5471
+ }
5472
+ }
5473
+ }
5474
+ async function checkPushPermission(client, projectId) {
5475
+ try {
5476
+ const roleInfo = await client.get(`/api/projects/${projectId}/my-role`);
5477
+ return { allowed: roleInfo.canPush, role: roleInfo.role };
5478
+ } catch {
5479
+ return { allowed: true, role: "owner" };
5480
+ }
5481
+ }
5482
+ function formatTimeAgo(dateStr) {
5483
+ const date = new Date(dateStr);
5484
+ const now = /* @__PURE__ */ new Date();
5485
+ const diffMs = now.getTime() - date.getTime();
5486
+ const diffMins = Math.floor(diffMs / 6e4);
5487
+ const diffHours = Math.floor(diffMs / 36e5);
5488
+ const diffDays = Math.floor(diffMs / 864e5);
5489
+ if (diffMins < 1) return "just now";
5490
+ if (diffMins < 60) return `${diffMins}m ago`;
5491
+ if (diffHours < 24) return `${diffHours}h ago`;
5492
+ if (diffDays < 7) return `${diffDays}d ago`;
5493
+ return date.toLocaleDateString();
5494
+ }
5495
+ var import_utils3;
5496
+ var init_push = __esm({
5497
+ "src/commands/push.ts"() {
5498
+ "use strict";
5499
+ init_esm_shims();
5500
+ init_config();
5501
+ init_api_client();
5502
+ init_sync_engine();
5503
+ init_progress();
5504
+ init_bundle();
5505
+ import_utils3 = __toESM(require_dist(), 1);
5506
+ init_theme();
5507
+ }
5508
+ });
5509
+
5510
+ // src/commands/pull.ts
5511
+ var pull_exports = {};
5512
+ __export(pull_exports, {
5513
+ pullCommand: () => pullCommand
5514
+ });
5515
+ import { Command as Command5 } from "commander";
5516
+ import { confirm, isCancel as isCancel4 } from "@clack/prompts";
5517
+ import { join as join5 } from "path";
5518
+ import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
5519
+ import { rm as rm2, mkdir as mkdir3 } from "fs/promises";
5520
+ function pullCommand() {
5521
+ return new Command5("pull").description("Pull latest changes from cloud").option("--dry-run", "Show what would be pulled without pulling").option("--verbose", "Show detailed output").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
5522
+ await runPull(options);
5523
+ });
5524
+ }
5525
+ async function runPull(options) {
5526
+ printIntro("Pull");
5527
+ const config = await loadConfig();
5528
+ if (!isAuthenticated(config)) {
5529
+ printError("Not logged in. Run `claude-sync login` first.");
5530
+ return;
5531
+ }
5532
+ if (!config.deviceId) {
5533
+ printError("No device registered. Run `claude-sync device add` first.");
5534
+ return;
5535
+ }
5536
+ const cwd = process.cwd();
5537
+ const ctx = await resolveProjectState(cwd);
5538
+ const client = new ApiClient(config.apiUrl);
5539
+ const projectsDir = join5(homedir5(), import_utils4.CLAUDE_DIR, import_utils4.PROJECTS_DIR);
5540
+ const projectLink = await loadProjectLink(cwd);
5541
+ let projectDir = ctx.projectDir;
5542
+ if (ctx.state === "symlink" && ctx.symlinkTarget) {
5543
+ projectDir = join5(projectsDir, ctx.symlinkTarget);
5544
+ }
5545
+ printInfo(`Project: ${brand(cwd)}`);
5546
+ if (!projectLink?.projectId) {
5547
+ printInfo("No project linked yet. Run `claude-sync push` first to create one.");
5548
+ printOutro("Done");
5549
+ return;
5550
+ }
5551
+ const localVersion = projectLink.localVersion ?? 0;
5552
+ const spinner = createSpinner("Checking for updates...").start();
5553
+ let syncState;
5554
+ try {
5555
+ syncState = await client.get(`/api/projects/${projectLink.projectId}/sync-state?localVersion=${localVersion}`);
5556
+ } catch (err) {
5557
+ spinner.stop();
5558
+ if (err instanceof AuthExpiredError) throw err;
5559
+ printError(`Failed to check sync state: ${err instanceof Error ? err.message : "Unknown error"}`);
5560
+ return;
5561
+ }
5562
+ if (!syncState.isBehind) {
5563
+ spinner.stop();
5564
+ console.log();
5565
+ printSuccess("Already up to date.");
5566
+ printInfo(`Version: ${localVersion} (latest)`);
5567
+ printOutro("Done");
5568
+ return;
5569
+ }
5570
+ spinner.stop();
5571
+ console.log();
5572
+ printInfo(`You are ${brand(String(syncState.behindBy))} version(s) behind.`);
5573
+ printInfo(`Local: v${localVersion} Remote: v${syncState.currentVersion}`);
5574
+ console.log();
5575
+ if (syncState.recentCommits.length > 0) {
5576
+ printInfo("Commits to pull:");
5577
+ for (const commit of syncState.recentCommits) {
5578
+ const deviceName = commit.device?.name || "Unknown";
5579
+ const timeAgo = formatTimeAgo2(commit.createdAt);
5580
+ console.log(` v${commit.version} "${commit.message}" ${dim(`(${deviceName}, ${timeAgo})`)}`);
5581
+ }
5582
+ console.log();
5583
+ }
5584
+ if (options.dryRun) {
5585
+ printOutro("Dry run complete");
5586
+ return;
5587
+ }
5588
+ if (!options.yes) {
5589
+ const proceed = await confirm({
5590
+ message: brand(`Pull ${syncState.behindBy} version(s)?`),
5591
+ initialValue: true
5592
+ });
5593
+ if (isCancel4(proceed) || !proceed) {
5594
+ printInfo("Pull cancelled.");
5595
+ return;
5596
+ }
5597
+ }
5598
+ const pullSpinner = createSpinner("Downloading...").start();
5599
+ let prepareResponse;
5600
+ try {
5601
+ prepareResponse = await client.post("/api/sync/pull/bundle/prepare", {
5602
+ deviceId: config.deviceId,
5603
+ projectPath: cwd,
5604
+ projectId: projectLink.projectId
5605
+ });
5606
+ } catch (err) {
5607
+ pullSpinner.stop();
5608
+ if (err instanceof AuthExpiredError) throw err;
5609
+ printError(`Pull failed: ${err instanceof Error ? err.message : "Unknown error"}`);
5610
+ return;
5611
+ }
5612
+ if (!prepareResponse.downloadUrl || !prepareResponse.bundleSize) {
5613
+ pullSpinner.stop();
5614
+ printInfo("No files to download.");
5615
+ printOutro("Done");
5616
+ return;
5617
+ }
5618
+ const tempBundle = join5(tmpdir2(), `claude-sync-pull-${Date.now()}.tar.gz`);
5619
+ try {
5620
+ const progress = createTransferProgress("Downloading", prepareResponse.bundleSize, pullSpinner);
5621
+ await streamDownload(prepareResponse.downloadUrl, tempBundle, (bytes) => {
5622
+ progress.update(bytes);
5623
+ });
5624
+ progress.finish("Downloaded");
5625
+ const extractSpinner = createSpinner("Extracting...").start();
5626
+ await mkdir3(projectDir, { recursive: true });
5627
+ const extractedCount = await extractBundle(tempBundle, projectDir);
5628
+ extractSpinner.succeed(`Extracted ${extractedCount} files`);
5629
+ try {
5630
+ await client.post("/api/sync/pull/bundle/complete", {
5631
+ syncEventId: prepareResponse.syncEventId,
5632
+ manifest: prepareResponse.manifest
5633
+ });
5634
+ } catch {
5635
+ }
5636
+ await saveProjectLink(cwd, {
5637
+ ...projectLink,
5638
+ localVersion: syncState.currentVersion
5639
+ });
5640
+ console.log();
5641
+ printSuccess(`Pulled ${prepareResponse.manifest.length} files`);
5642
+ printInfo(`Version: ${localVersion} \u2192 ${syncState.currentVersion}`);
5643
+ printOutro("Done");
5644
+ } finally {
5645
+ try {
5646
+ await rm2(tempBundle, { force: true });
5647
+ } catch {
5648
+ }
5649
+ }
5650
+ }
5651
+ function formatTimeAgo2(dateStr) {
5652
+ const date = new Date(dateStr);
5653
+ const now = /* @__PURE__ */ new Date();
5654
+ const diffMs = now.getTime() - date.getTime();
5655
+ const diffMins = Math.floor(diffMs / 6e4);
5656
+ const diffHours = Math.floor(diffMs / 36e5);
5657
+ const diffDays = Math.floor(diffMs / 864e5);
5658
+ if (diffMins < 1) return "just now";
5659
+ if (diffMins < 60) return `${diffMins}m ago`;
5660
+ if (diffHours < 24) return `${diffHours}h ago`;
5661
+ if (diffDays < 7) return `${diffDays}d ago`;
5662
+ return date.toLocaleDateString();
5663
+ }
5664
+ var import_utils4;
5665
+ var init_pull = __esm({
5666
+ "src/commands/pull.ts"() {
5667
+ "use strict";
5668
+ init_esm_shims();
5669
+ init_config();
5670
+ init_api_client();
5671
+ init_sync_engine();
5672
+ init_progress();
5673
+ init_bundle();
5674
+ import_utils4 = __toESM(require_dist(), 1);
5675
+ init_theme();
5676
+ }
5677
+ });
5678
+
5679
+ // src/commands/log.ts
5680
+ var log_exports = {};
5681
+ __export(log_exports, {
5682
+ logCommand: () => logCommand
5683
+ });
5684
+ import { Command as Command6 } from "commander";
5685
+ function logCommand() {
5686
+ return new Command6("log").description("Show commit history for current project").option("-n, --limit <number>", "Number of commits to show", "10").action(async (options) => {
5687
+ await runLog(options);
5688
+ });
5689
+ }
5690
+ async function runLog(options) {
5691
+ printIntro("Log");
5692
+ const config = await loadConfig();
5693
+ if (!isAuthenticated(config)) {
5694
+ printError("Not logged in. Run `claude-sync login` first.");
5695
+ return;
5696
+ }
5697
+ const cwd = process.cwd();
5698
+ const projectLink = await loadProjectLink(cwd);
5699
+ printInfo(`Project: ${brand(cwd)}`);
5700
+ if (!projectLink?.projectId) {
5701
+ printInfo("No project linked yet. Run `claude-sync push` first.");
5702
+ printOutro("Done");
5703
+ return;
5704
+ }
5705
+ const client = new ApiClient(config.apiUrl);
5706
+ const limit = parseInt(options.limit, 10) || 10;
5707
+ let commits;
5708
+ try {
5709
+ commits = await client.get(`/api/projects/${projectLink.projectId}/commits?limit=${limit}`);
5710
+ } catch (err) {
5711
+ if (err instanceof AuthExpiredError) throw err;
5712
+ printError(`Failed to fetch commits: ${err instanceof Error ? err.message : "Unknown error"}`);
5713
+ return;
5714
+ }
5715
+ if (commits.length === 0) {
5716
+ printInfo("No commits yet.");
5717
+ printOutro("Done");
5718
+ return;
5719
+ }
5720
+ const localVersion = projectLink.localVersion ?? 0;
5721
+ console.log();
5722
+ for (const commit of commits) {
5723
+ const deviceName = commit.device?.name || "Unknown device";
5724
+ const timeAgo = formatTimeAgo3(commit.createdAt);
5725
+ const isHead = commit.version === localVersion;
5726
+ const versionStr = isHead ? `${brand(`v${commit.version}`)} ${dim("(HEAD)")}` : `v${commit.version}`;
5727
+ console.log(` ${versionStr} "${commit.message}"`);
5728
+ console.log(` ${dim(`${deviceName} \u2022 ${timeAgo}`)}`);
5729
+ console.log();
5730
+ }
5731
+ printOutro("");
5732
+ }
5733
+ function formatTimeAgo3(dateStr) {
5734
+ const date = new Date(dateStr);
5735
+ const now = /* @__PURE__ */ new Date();
5736
+ const diffMs = now.getTime() - date.getTime();
5737
+ const diffMins = Math.floor(diffMs / 6e4);
5738
+ const diffHours = Math.floor(diffMs / 36e5);
5739
+ const diffDays = Math.floor(diffMs / 864e5);
5740
+ if (diffMins < 1) return "just now";
5741
+ if (diffMins < 60) return `${diffMins}m ago`;
5742
+ if (diffHours < 24) return `${diffHours}h ago`;
5743
+ if (diffDays < 7) return `${diffDays}d ago`;
5744
+ return date.toLocaleDateString();
5745
+ }
5746
+ var init_log = __esm({
5747
+ "src/commands/log.ts"() {
5748
+ "use strict";
5749
+ init_esm_shims();
5750
+ init_config();
5751
+ init_api_client();
5752
+ init_theme();
5753
+ }
5754
+ });
5755
+
5756
+ // src/commands/status.ts
5757
+ var status_exports = {};
5758
+ __export(status_exports, {
5759
+ statusCommand: () => statusCommand
5760
+ });
5761
+ import { Command as Command8 } from "commander";
5762
+ function statusCommand() {
5763
+ return new Command8("status").description("Show sync status for current project").action(async () => {
5764
+ printIntro("Status");
5765
+ const config = await loadConfig();
5766
+ const cwd = process.cwd();
5767
+ printLabel("API", config.apiUrl);
5768
+ printLabel("Authenticated", isAuthenticated(config) ? success("yes") : error("no"));
5769
+ printLabel("Device", config.deviceName || dim("none"));
5770
+ printLabel("Project", brand(cwd));
5771
+ const ctx = await resolveProjectState(cwd);
5772
+ printLabel("State", ctx.state === "none" ? dim("no session data") : ctx.state === "symlink" ? `symlink \u2192 ${dim(ctx.symlinkTarget || "unknown")}` : "local");
5773
+ if (!isAuthenticated(config) || ctx.state === "none") return;
5774
+ const spinner = createSpinner("Scanning files...").start();
5775
+ const savedManifest = await loadProjectManifest(cwd);
5776
+ const currentManifest = await buildProjectManifest(cwd);
5777
+ const diff = computeDiff(currentManifest, savedManifest);
5778
+ spinner.stop();
5779
+ const totalSize = currentManifest.reduce((sum, e) => sum + e.size, 0);
5780
+ printDivider();
5781
+ printLabel("Local files", `${brand(String(currentManifest.length))} ${dim(`(${formatBytes(totalSize)})`)}`);
5782
+ console.log();
5783
+ printInfo("Changes since last sync:");
5784
+ console.log(` ${success(`+${diff.added.length}`)} added ${warn(`~${diff.modified.length}`)} modified ${error(`-${diff.deleted.length}`)} deleted`);
5785
+ console.log();
5786
+ const projectLink = await loadProjectLink(cwd);
5787
+ if (projectLink?.projectId) {
5788
+ const client = new ApiClient(config.apiUrl);
5789
+ const localVersion = projectLink.localVersion ?? 0;
5790
+ try {
5791
+ const syncState = await client.get(`/api/projects/${projectLink.projectId}/sync-state?localVersion=${localVersion}`);
5792
+ if (syncState.isBehind) {
5793
+ printWarn(`You are ${brand(String(syncState.behindBy))} version(s) behind.`);
5794
+ printInfo(`Local: v${localVersion} Remote: v${syncState.currentVersion}`);
5795
+ console.log();
5796
+ if (syncState.recentCommits.length > 0) {
5797
+ printInfo("Recent commits on remote:");
5798
+ for (const commit of syncState.recentCommits) {
5799
+ const deviceName = commit.device?.name || "Unknown";
5800
+ console.log(` v${commit.version} "${commit.message}" ${dim(`(${deviceName})`)}`);
5801
+ }
5802
+ console.log();
5803
+ }
5804
+ printInfo(`Run ${brand("claude-sync pull")} to get latest changes.`);
5805
+ } else {
5806
+ printSuccess("Up to date");
5807
+ printInfo(`Version: ${localVersion}`);
5808
+ }
5809
+ } catch {
5810
+ printInfo(`Local version: ${localVersion}`);
5811
+ }
5812
+ }
5813
+ });
5814
+ }
5815
+ var init_status = __esm({
5816
+ "src/commands/status.ts"() {
5817
+ "use strict";
5818
+ init_esm_shims();
5819
+ init_config();
5820
+ init_sync_engine();
5821
+ init_api_client();
5822
+ init_progress();
5823
+ init_theme();
5824
+ }
5825
+ });
5826
+
5827
+ // src/commands/whoami.ts
5828
+ var whoami_exports = {};
5829
+ __export(whoami_exports, {
5830
+ whoamiCommand: () => whoamiCommand
5831
+ });
5832
+ import { Command as Command9 } from "commander";
5833
+ function whoamiCommand() {
5834
+ return new Command9("whoami").description("Show current user and device info").action(async () => {
5835
+ printIntro("Who Am I");
5836
+ const config = await loadConfig();
5837
+ if (!isAuthenticated(config)) {
5838
+ printError("Not logged in. Run `claude-sync login` first.");
5839
+ return;
5840
+ }
5841
+ const client = new ApiClient(config.apiUrl);
5842
+ const spinner = createSpinner("Fetching user info...").start();
5843
+ try {
5844
+ const user = await client.get("/api/auth/me");
5845
+ spinner.stop();
5846
+ printLabel("User", `${user.name} ${dim(`(${user.email})`)}`);
5847
+ printLabel("Plan", brand(user.plan));
5848
+ printDivider();
5849
+ printLabel("Device", config.deviceName || dim("none"));
5850
+ printLabel("Device ID", config.deviceId || dim("not registered"));
5851
+ } catch (err) {
5852
+ spinner.stop();
5853
+ if (err instanceof AuthExpiredError) throw err;
5854
+ printLabel("Email", config.email || dim("unknown"));
5855
+ printLabel("Device", config.deviceName || dim("none"));
5856
+ }
5857
+ console.log();
5858
+ });
5859
+ }
5860
+ var init_whoami = __esm({
5861
+ "src/commands/whoami.ts"() {
5862
+ "use strict";
5863
+ init_esm_shims();
5864
+ init_config();
5865
+ init_api_client();
5866
+ init_progress();
5867
+ init_theme();
5868
+ }
5869
+ });
5870
+
5871
+ // bin/claude-sync.ts
5872
+ init_esm_shims();
5873
+
5874
+ // src/index.ts
5875
+ init_esm_shims();
5876
+ init_login();
5877
+ init_logout();
5878
+ init_device();
5879
+ init_push();
5880
+ init_pull();
5881
+ init_log();
5882
+ import { Command as Command10 } from "commander";
5883
+
5884
+ // src/commands/sync.ts
5885
+ init_esm_shims();
5886
+ init_config();
5887
+ init_api_client();
5888
+ init_sync_engine();
5889
+ init_progress();
5890
+ init_bundle();
5891
+ var import_utils5 = __toESM(require_dist(), 1);
5892
+ init_theme();
5893
+ import { Command as Command7 } from "commander";
5894
+ import { select as select2, isCancel as isCancel5 } from "@clack/prompts";
5895
+ import { mkdir as mkdir4, lstat as lstat2, symlink, unlink, rename, rm as rm3 } from "fs/promises";
5896
+ import { join as join6 } from "path";
5897
+ import { homedir as homedir6, tmpdir as tmpdir3 } from "os";
5898
+ async function runSync(options) {
5899
+ printIntro("Sync");
5900
+ const config = await loadConfig();
5901
+ if (!isAuthenticated(config)) {
5902
+ printError("Not logged in. Run `claude-sync login` first.");
5903
+ return;
5904
+ }
5905
+ if (!config.deviceId) {
5906
+ printError("No device registered. Run `claude-sync device add` first.");
5907
+ return;
5908
+ }
5909
+ const cwd = process.cwd();
5910
+ const ctx = await resolveProjectState(cwd);
5911
+ const client = new ApiClient(config.apiUrl);
5912
+ const projectsDir = join6(homedir6(), import_utils5.CLAUDE_DIR, import_utils5.PROJECTS_DIR);
5913
+ const projectLink = await loadProjectLink(cwd);
5914
+ let projectDir = ctx.projectDir;
5915
+ if (ctx.state === "symlink" && ctx.symlinkTarget) {
5916
+ projectDir = join6(projectsDir, ctx.symlinkTarget);
5917
+ }
5918
+ printInfo(`Project: ${brand(cwd)}`);
5315
5919
  if (ctx.state === "symlink") {
5316
5920
  printInfo(`Linked to: ${dim(ctx.symlinkTarget || "unknown")}`);
5317
5921
  }
@@ -5413,7 +6017,7 @@ async function handleFirstRun(client, config, cwd, ctx, manifest, projectDir, pr
5413
6017
  message: brand("What would you like to do?"),
5414
6018
  options: menuOptions
5415
6019
  });
5416
- if (isCancel3(choice)) return "cancelled";
6020
+ if (isCancel5(choice)) return "cancelled";
5417
6021
  if (choice === "push") {
5418
6022
  const result2 = await bundlePushPhase(client, config.deviceId, cwd, manifest, projectDir, void 0, options);
5419
6023
  if (result2.failed) {
@@ -5435,7 +6039,7 @@ async function handleFirstRun(client, config, cwd, ctx, manifest, projectDir, pr
5435
6039
  hint: dim(`${p.owner.name} \xB7 ${p.role}`)
5436
6040
  }))
5437
6041
  });
5438
- if (isCancel3(sharedChoice)) return "cancelled";
6042
+ if (isCancel5(sharedChoice)) return "cancelled";
5439
6043
  const selectedShared = sharedProjects.find((p) => p.id === sharedChoice);
5440
6044
  const syncStatusSpinner = createSpinner("Checking sync status...").start();
5441
6045
  let syncStatus = null;
@@ -5454,7 +6058,7 @@ async function handleFirstRun(client, config, cwd, ctx, manifest, projectDir, pr
5454
6058
  { value: "no", label: "Cancel", hint: dim("Wait for them to finish") }
5455
6059
  ]
5456
6060
  });
5457
- if (isCancel3(confirmChoice) || confirmChoice === "no") return "cancelled";
6061
+ if (isCancel5(confirmChoice) || confirmChoice === "no") return "cancelled";
5458
6062
  }
5459
6063
  const existingCheck2 = await handleExistingSessionDir(projectsDir, ctx.encodedPath);
5460
6064
  if (existingCheck2 === "cancelled") {
@@ -5493,7 +6097,7 @@ async function handleFirstRun(client, config, cwd, ctx, manifest, projectDir, pr
5493
6097
  hint: dim(`${d.hostname} \xB7 ${d.platform}`)
5494
6098
  }))
5495
6099
  });
5496
- if (isCancel3(deviceChoice)) return "cancelled";
6100
+ if (isCancel5(deviceChoice)) return "cancelled";
5497
6101
  const spinner = createSpinner("Loading projects...").start();
5498
6102
  let projects;
5499
6103
  try {
@@ -5517,7 +6121,7 @@ async function handleFirstRun(client, config, cwd, ctx, manifest, projectDir, pr
5517
6121
  hint: dim(p.localPath)
5518
6122
  }))
5519
6123
  });
5520
- if (isCancel3(projectChoice)) return "cancelled";
6124
+ if (isCancel5(projectChoice)) return "cancelled";
5521
6125
  const selectedProject = projects.find((p) => p.id === projectChoice);
5522
6126
  const existingCheck = await handleExistingSessionDir(projectsDir, ctx.encodedPath);
5523
6127
  if (existingCheck === "cancelled") {
@@ -5549,7 +6153,7 @@ async function handleFirstRun(client, config, cwd, ctx, manifest, projectDir, pr
5549
6153
  return "pulled";
5550
6154
  }
5551
6155
  async function handleExistingSessionDir(projectsDir, localEncoded) {
5552
- const symlinkPath = join4(projectsDir, localEncoded);
6156
+ const symlinkPath = join6(projectsDir, localEncoded);
5553
6157
  try {
5554
6158
  const stats = await lstat2(symlinkPath);
5555
6159
  if (stats.isSymbolicLink()) {
@@ -5573,7 +6177,7 @@ async function handleExistingSessionDir(projectsDir, localEncoded) {
5573
6177
  }
5574
6178
  ]
5575
6179
  });
5576
- if (isCancel3(choice) || choice === "cancel") {
6180
+ if (isCancel5(choice) || choice === "cancel") {
5577
6181
  return "cancelled";
5578
6182
  }
5579
6183
  const backupPath = `${symlinkPath}.bak.${Date.now()}`;
@@ -5586,8 +6190,8 @@ async function handleExistingSessionDir(projectsDir, localEncoded) {
5586
6190
  return "continue";
5587
6191
  }
5588
6192
  async function createProjectSymlink(projectsDir, localEncoded, foreignEncoded) {
5589
- const symlinkPath = join4(projectsDir, localEncoded);
5590
- await mkdir3(projectsDir, { recursive: true });
6193
+ const symlinkPath = join6(projectsDir, localEncoded);
6194
+ await mkdir4(projectsDir, { recursive: true });
5591
6195
  try {
5592
6196
  const stats = await lstat2(symlinkPath);
5593
6197
  if (stats.isSymbolicLink()) {
@@ -5602,7 +6206,7 @@ async function bundlePushPhase(client, deviceId, projectPath, manifest, projectD
5602
6206
  return { fileCount: 0, projectId: projectId || "", failed: false };
5603
6207
  }
5604
6208
  if (projectId) {
5605
- const permission = await checkPushPermission(client, projectId);
6209
+ const permission = await checkPushPermission2(client, projectId);
5606
6210
  if (!permission.allowed) {
5607
6211
  printError(`Cannot push: You have ${brand(permission.role)} access to this project.`);
5608
6212
  printInfo("Only project editors and owners can push changes.");
@@ -5619,7 +6223,7 @@ async function bundlePushPhase(client, deviceId, projectPath, manifest, projectD
5619
6223
  }
5620
6224
  return { fileCount: manifest.length, projectId: projectId || "", failed: false };
5621
6225
  }
5622
- const tempBundle = join4(tmpdir(), `claude-sync-bundle-${Date.now()}.tar.gz`);
6226
+ const tempBundle = join6(tmpdir3(), `claude-sync-bundle-${Date.now()}.tar.gz`);
5623
6227
  try {
5624
6228
  const spinner = createSpinner("Syncing...").start();
5625
6229
  const bundleResult = await createBundle(projectDir, manifest, tempBundle, (bytes, total, phase) => {
@@ -5659,7 +6263,7 @@ async function bundlePushPhase(client, deviceId, projectPath, manifest, projectD
5659
6263
  return { fileCount: manifest.length, projectId: prepareResponse.projectId, failed: false };
5660
6264
  } finally {
5661
6265
  try {
5662
- await rm(tempBundle, { force: true });
6266
+ await rm3(tempBundle, { force: true });
5663
6267
  } catch {
5664
6268
  }
5665
6269
  }
@@ -5694,7 +6298,7 @@ async function bundlePullPhase(client, deviceId, projectPath, projectDir, projec
5694
6298
  }
5695
6299
  return { fileCount, failed: false };
5696
6300
  }
5697
- const tempBundle = join4(tmpdir(), `claude-sync-bundle-${Date.now()}.tar.gz`);
6301
+ const tempBundle = join6(tmpdir3(), `claude-sync-bundle-${Date.now()}.tar.gz`);
5698
6302
  try {
5699
6303
  const progress = createTransferProgress("Downloading", prepareResponse.bundleSize, spinner);
5700
6304
  await streamDownload(prepareResponse.downloadUrl, tempBundle, prepareResponse.bundleSize, (bytes) => {
@@ -5714,7 +6318,7 @@ async function bundlePullPhase(client, deviceId, projectPath, projectDir, projec
5714
6318
  return { fileCount: extractedCount, failed: false };
5715
6319
  } finally {
5716
6320
  try {
5717
- await rm(tempBundle, { force: true });
6321
+ await rm3(tempBundle, { force: true });
5718
6322
  } catch {
5719
6323
  }
5720
6324
  }
@@ -5750,15 +6354,15 @@ async function crossMachineBundlePull(client, deviceId, projectPath, fromDeviceI
5750
6354
  }
5751
6355
  return { fileCount, failed: false };
5752
6356
  }
5753
- const targetDir = join4(projectsDir, foreignEncodedDir);
5754
- const tempBundle = join4(tmpdir(), `claude-sync-bundle-${Date.now()}.tar.gz`);
6357
+ const targetDir = join6(projectsDir, foreignEncodedDir);
6358
+ const tempBundle = join6(tmpdir3(), `claude-sync-bundle-${Date.now()}.tar.gz`);
5755
6359
  try {
5756
6360
  const progress = createTransferProgress("Downloading", prepareResponse.bundleSize, spinner);
5757
6361
  await streamDownload(prepareResponse.downloadUrl, tempBundle, prepareResponse.bundleSize, (bytes) => {
5758
6362
  progress.update(bytes);
5759
6363
  });
5760
6364
  spinner.text = "Extracting files...";
5761
- await mkdir3(targetDir, { recursive: true });
6365
+ await mkdir4(targetDir, { recursive: true });
5762
6366
  const extractedCount = await extractBundle(tempBundle, targetDir);
5763
6367
  try {
5764
6368
  const completeManifest = await walkDirectory(targetDir);
@@ -5772,7 +6376,7 @@ async function crossMachineBundlePull(client, deviceId, projectPath, fromDeviceI
5772
6376
  return { fileCount: extractedCount, failed: false };
5773
6377
  } finally {
5774
6378
  try {
5775
- await rm(tempBundle, { force: true });
6379
+ await rm3(tempBundle, { force: true });
5776
6380
  } catch {
5777
6381
  }
5778
6382
  }
@@ -5808,15 +6412,15 @@ async function collaboratorBundlePull(client, deviceId, projectPath, projectId,
5808
6412
  return { fileCount, failed: false };
5809
6413
  }
5810
6414
  const foreignEncoded = canonicalPath.replace(/\//g, "%2F");
5811
- const targetDir = join4(projectsDir, foreignEncoded);
5812
- const tempBundle = join4(tmpdir(), `claude-sync-bundle-${Date.now()}.tar.gz`);
6415
+ const targetDir = join6(projectsDir, foreignEncoded);
6416
+ const tempBundle = join6(tmpdir3(), `claude-sync-bundle-${Date.now()}.tar.gz`);
5813
6417
  try {
5814
6418
  const progress = createTransferProgress("Downloading", prepareResponse.bundleSize, spinner);
5815
6419
  await streamDownload(prepareResponse.downloadUrl, tempBundle, prepareResponse.bundleSize, (bytes) => {
5816
6420
  progress.update(bytes);
5817
6421
  });
5818
6422
  spinner.text = "Extracting files...";
5819
- await mkdir3(targetDir, { recursive: true });
6423
+ await mkdir4(targetDir, { recursive: true });
5820
6424
  const extractedCount = await extractBundle(tempBundle, targetDir);
5821
6425
  try {
5822
6426
  const completeManifest = await walkDirectory(targetDir);
@@ -5830,12 +6434,12 @@ async function collaboratorBundlePull(client, deviceId, projectPath, projectId,
5830
6434
  return { fileCount: extractedCount, failed: false };
5831
6435
  } finally {
5832
6436
  try {
5833
- await rm(tempBundle, { force: true });
6437
+ await rm3(tempBundle, { force: true });
5834
6438
  } catch {
5835
6439
  }
5836
6440
  }
5837
6441
  }
5838
- async function checkPushPermission(client, projectId) {
6442
+ async function checkPushPermission2(client, projectId) {
5839
6443
  try {
5840
6444
  const myRole = await client.get(`/api/projects/${projectId}/my-role`);
5841
6445
  return { allowed: myRole.canPush, role: myRole.role };
@@ -5844,131 +6448,21 @@ async function checkPushPermission(client, projectId) {
5844
6448
  }
5845
6449
  }
5846
6450
  function syncCommand() {
5847
- return new Command4("sync").description("Sync current project sessions with cloud").option("--dry-run", "Show what would be synced without syncing").option("--verbose", "Show detailed output").action(async (options) => {
6451
+ return new Command7("sync").description("Sync current project sessions with cloud").option("--dry-run", "Show what would be synced without syncing").option("--verbose", "Show detailed output").action(async (options) => {
5848
6452
  await runSync(options);
5849
6453
  });
5850
6454
  }
5851
- var import_utils3;
5852
- var init_sync = __esm({
5853
- "src/commands/sync.ts"() {
5854
- "use strict";
5855
- init_esm_shims();
5856
- init_config();
5857
- init_api_client();
5858
- init_sync_engine();
5859
- init_progress();
5860
- init_bundle();
5861
- import_utils3 = __toESM(require_dist(), 1);
5862
- init_theme();
5863
- }
5864
- });
5865
-
5866
- // src/commands/status.ts
5867
- var status_exports = {};
5868
- __export(status_exports, {
5869
- statusCommand: () => statusCommand
5870
- });
5871
- import { Command as Command5 } from "commander";
5872
- function statusCommand() {
5873
- return new Command5("status").description("Show sync status for current project").action(async () => {
5874
- printIntro("Status");
5875
- const config = await loadConfig();
5876
- const cwd = process.cwd();
5877
- printLabel("API", config.apiUrl);
5878
- printLabel("Authenticated", isAuthenticated(config) ? success("yes") : error("no"));
5879
- printLabel("Device", config.deviceName || dim("none"));
5880
- printLabel("Project", brand(cwd));
5881
- const ctx = await resolveProjectState(cwd);
5882
- printLabel("State", ctx.state === "none" ? dim("no session data") : ctx.state === "symlink" ? `symlink \u2192 ${dim(ctx.symlinkTarget || "unknown")}` : "local");
5883
- if (!isAuthenticated(config) || ctx.state === "none") return;
5884
- const spinner = createSpinner("Scanning files...").start();
5885
- const savedManifest = await loadProjectManifest(cwd);
5886
- const currentManifest = await buildProjectManifest(cwd);
5887
- const diff = computeDiff(currentManifest, savedManifest);
5888
- spinner.stop();
5889
- const totalSize = currentManifest.reduce((sum, e) => sum + e.size, 0);
5890
- printDivider();
5891
- printLabel("Local files", `${brand(String(currentManifest.length))} ${dim(`(${formatBytes(totalSize)})`)}`);
5892
- console.log();
5893
- printInfo("Changes since last sync:");
5894
- console.log(` ${success(`+${diff.added.length}`)} added ${warn(`~${diff.modified.length}`)} modified ${error(`-${diff.deleted.length}`)} deleted`);
5895
- console.log();
5896
- });
5897
- }
5898
- var init_status = __esm({
5899
- "src/commands/status.ts"() {
5900
- "use strict";
5901
- init_esm_shims();
5902
- init_config();
5903
- init_sync_engine();
5904
- init_progress();
5905
- init_theme();
5906
- }
5907
- });
5908
-
5909
- // src/commands/whoami.ts
5910
- var whoami_exports = {};
5911
- __export(whoami_exports, {
5912
- whoamiCommand: () => whoamiCommand
5913
- });
5914
- import { Command as Command6 } from "commander";
5915
- function whoamiCommand() {
5916
- return new Command6("whoami").description("Show current user and device info").action(async () => {
5917
- printIntro("Who Am I");
5918
- const config = await loadConfig();
5919
- if (!isAuthenticated(config)) {
5920
- printError("Not logged in. Run `claude-sync login` first.");
5921
- return;
5922
- }
5923
- const client = new ApiClient(config.apiUrl);
5924
- const spinner = createSpinner("Fetching user info...").start();
5925
- try {
5926
- const user = await client.get("/api/auth/me");
5927
- spinner.stop();
5928
- printLabel("User", `${user.name} ${dim(`(${user.email})`)}`);
5929
- printLabel("Plan", brand(user.plan));
5930
- printDivider();
5931
- printLabel("Device", config.deviceName || dim("none"));
5932
- printLabel("Device ID", config.deviceId || dim("not registered"));
5933
- } catch (err) {
5934
- spinner.stop();
5935
- if (err instanceof AuthExpiredError) throw err;
5936
- printLabel("Email", config.email || dim("unknown"));
5937
- printLabel("Device", config.deviceName || dim("none"));
5938
- }
5939
- console.log();
5940
- });
5941
- }
5942
- var init_whoami = __esm({
5943
- "src/commands/whoami.ts"() {
5944
- "use strict";
5945
- init_esm_shims();
5946
- init_config();
5947
- init_api_client();
5948
- init_progress();
5949
- init_theme();
5950
- }
5951
- });
5952
-
5953
- // bin/claude-sync.ts
5954
- init_esm_shims();
5955
6455
 
5956
6456
  // src/index.ts
5957
- init_esm_shims();
5958
- init_login();
5959
- init_logout();
5960
- init_device();
5961
- init_sync();
5962
6457
  init_status();
5963
6458
  init_whoami();
5964
- import { Command as Command7 } from "commander";
5965
6459
 
5966
6460
  // src/commands/tui.ts
5967
6461
  init_esm_shims();
5968
6462
  init_theme();
5969
6463
  init_config();
5970
6464
  init_api_client();
5971
- import { select as select3, text as text3, isCancel as isCancel4 } from "@clack/prompts";
6465
+ import { select as select3, text as text4, isCancel as isCancel6 } from "@clack/prompts";
5972
6466
  import chalk3 from "chalk";
5973
6467
  function buildMenu(loggedIn) {
5974
6468
  if (!loggedIn) {
@@ -5977,7 +6471,10 @@ function buildMenu(loggedIn) {
5977
6471
  ];
5978
6472
  }
5979
6473
  return [
5980
- { value: "sync", label: "Sync", hint: "Sync current project" },
6474
+ { value: "quick-push", label: "Quick Push", hint: "Push without message" },
6475
+ { value: "push-message", label: "Push with Message", hint: "Add a commit message" },
6476
+ { value: "pull", label: "Pull", hint: "Get latest changes" },
6477
+ { value: "log", label: "View History", hint: "Show commit log" },
5981
6478
  { value: "status", label: "Status", hint: "Show sync state" },
5982
6479
  { value: "device:add", label: "Add Device", hint: "Register this machine" },
5983
6480
  { value: "device:list", label: "List Devices", hint: "Show registered devices" },
@@ -6019,7 +6516,7 @@ async function runTui() {
6019
6516
  message: brand("What would you like to do?"),
6020
6517
  options
6021
6518
  });
6022
- if (isCancel4(choice) || choice === "quit") {
6519
+ if (isCancel6(choice) || choice === "quit") {
6023
6520
  printOutro("Goodbye!");
6024
6521
  return;
6025
6522
  }
@@ -6063,21 +6560,33 @@ async function promptDeviceSelection(config) {
6063
6560
  hint: d.id === config.deviceId ? dim("current") : void 0
6064
6561
  }))
6065
6562
  });
6066
- if (isCancel4(choice)) return null;
6563
+ if (isCancel6(choice)) return null;
6067
6564
  return choice;
6068
6565
  }
6069
6566
  async function executeCommand(command) {
6070
6567
  const { loginCommand: loginCommand2 } = await Promise.resolve().then(() => (init_login(), login_exports));
6071
6568
  const { logoutCommand: logoutCommand2 } = await Promise.resolve().then(() => (init_logout(), logout_exports));
6072
6569
  const { deviceCommand: deviceCommand2 } = await Promise.resolve().then(() => (init_device(), device_exports));
6073
- const { syncCommand: syncCommand2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
6570
+ const { pushCommand: pushCommand2 } = await Promise.resolve().then(() => (init_push(), push_exports));
6571
+ const { pullCommand: pullCommand2 } = await Promise.resolve().then(() => (init_pull(), pull_exports));
6572
+ const { logCommand: logCommand2 } = await Promise.resolve().then(() => (init_log(), log_exports));
6074
6573
  const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
6075
6574
  const { whoamiCommand: whoamiCommand2 } = await Promise.resolve().then(() => (init_whoami(), whoami_exports));
6076
6575
  const config = await loadConfig();
6077
6576
  const handlers = {
6078
6577
  login: () => loginCommand2().parseAsync(["", ""]),
6079
6578
  logout: () => logoutCommand2().parseAsync(["", ""]),
6080
- sync: () => syncCommand2().parseAsync(["", ""]),
6579
+ "quick-push": () => pushCommand2().parseAsync(["", "", "-m", "Quick sync"]),
6580
+ "push-message": async () => {
6581
+ const message = await text4({
6582
+ message: brand("Commit message:"),
6583
+ placeholder: "Describe your changes..."
6584
+ });
6585
+ if (isCancel6(message)) return;
6586
+ await pushCommand2().parseAsync(["", "", "-m", message]);
6587
+ },
6588
+ pull: () => pullCommand2().parseAsync(["", "", "-y"]),
6589
+ log: () => logCommand2().parseAsync(["", ""]),
6081
6590
  status: () => statusCommand2().parseAsync(["", ""]),
6082
6591
  whoami: () => whoamiCommand2().parseAsync(["", ""]),
6083
6592
  "device:add": () => deviceCommand2().parseAsync(["", "", "add"]),
@@ -6088,8 +6597,8 @@ async function executeCommand(command) {
6088
6597
  await deviceCommand2().parseAsync(["", "", "remove", deviceId]);
6089
6598
  },
6090
6599
  "device:rename": async () => {
6091
- const name = await text3({ message: `${brand("New device name")}:` });
6092
- if (isCancel4(name)) return;
6600
+ const name = await text4({ message: `${brand("New device name")}:` });
6601
+ if (isCancel6(name)) return;
6093
6602
  await deviceCommand2().parseAsync(["", "", "rename", name]);
6094
6603
  }
6095
6604
  };
@@ -6102,7 +6611,7 @@ async function executeCommand(command) {
6102
6611
  // src/index.ts
6103
6612
  init_config();
6104
6613
  function run() {
6105
- const program = new Command7();
6614
+ const program = new Command10();
6106
6615
  program.name("claude-sync").description("Sync Claude Code sessions across machines \u2014 claude-sync.com").version("0.1.0").option("--menu", "Open interactive menu").action(async (options) => {
6107
6616
  if (options.menu) {
6108
6617
  await runTui();
@@ -6118,6 +6627,9 @@ function run() {
6118
6627
  program.addCommand(loginCommand());
6119
6628
  program.addCommand(logoutCommand());
6120
6629
  program.addCommand(deviceCommand());
6630
+ program.addCommand(pushCommand());
6631
+ program.addCommand(pullCommand());
6632
+ program.addCommand(logCommand());
6121
6633
  program.addCommand(syncCommand());
6122
6634
  program.addCommand(statusCommand());
6123
6635
  program.addCommand(whoamiCommand());