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