ccsini 0.1.43 → 0.1.45

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.
Files changed (2) hide show
  1. package/dist/index.js +51 -6
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -27930,6 +27930,7 @@ var init_constants = __esm(() => {
27930
27930
  "**/dist/**",
27931
27931
  "**/.git/**",
27932
27932
  "**/*.bak",
27933
+ "**/*.conflict",
27933
27934
  "settings.*.json",
27934
27935
  "history.jsonl"
27935
27936
  ];
@@ -28019,7 +28020,7 @@ var {
28019
28020
  } = import__.default;
28020
28021
 
28021
28022
  // src/version.ts
28022
- var VERSION = "0.1.43";
28023
+ var VERSION = "0.1.45";
28023
28024
 
28024
28025
  // src/commands/init.ts
28025
28026
  init_source();
@@ -29358,6 +29359,7 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
29358
29359
  const start = Date.now();
29359
29360
  const claudeDir = getClaudeDir();
29360
29361
  const errors2 = [];
29362
+ const conflicts = [];
29361
29363
  let bytesTransferred = 0;
29362
29364
  const progress = onProgress ?? (() => {});
29363
29365
  const localManifest = await generateManifest(claudeDir, deviceName, progress, sessionOptions);
@@ -29378,6 +29380,25 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
29378
29380
  const diffs = diffManifests(localManifest, remoteManifest);
29379
29381
  const toPush = diffs.filter((d) => d.action === "push" || d.action === "merge");
29380
29382
  progress(`${toPush.length} files to push`);
29383
+ if (remoteManifest) {
29384
+ const merges = toPush.filter((d) => d.action === "merge" && d.remoteHash);
29385
+ for (const diff of merges) {
29386
+ try {
29387
+ let encrypted;
29388
+ try {
29389
+ encrypted = await client.downloadBlob(blobKey(diff.path, diff.remoteHash));
29390
+ } catch {
29391
+ encrypted = await client.downloadBlob(diff.remoteHash);
29392
+ }
29393
+ const decrypted = decryptFile(masterKey, diff.path, encrypted);
29394
+ const conflictPath = join6(claudeDir, `${diff.path}.conflict`);
29395
+ await mkdir2(dirname(conflictPath), { recursive: true });
29396
+ await writeFile5(conflictPath, decrypted);
29397
+ conflicts.push(diff.path);
29398
+ progress(`Saved conflict backup: ${diff.path}.conflict`);
29399
+ } catch {}
29400
+ }
29401
+ }
29381
29402
  let uploaded = 0;
29382
29403
  const failedPaths = new Set;
29383
29404
  const chunks = chunkArray(toPush, MAX_CONCURRENT_TRANSFERS);
@@ -29434,13 +29455,15 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
29434
29455
  filesChanged: toPush.length,
29435
29456
  bytesTransferred,
29436
29457
  durationMs,
29437
- errors: errors2
29458
+ errors: errors2,
29459
+ conflicts
29438
29460
  };
29439
29461
  }
29440
29462
  async function pullSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions) {
29441
29463
  const start = Date.now();
29442
29464
  const claudeDir = getClaudeDir();
29443
29465
  const errors2 = [];
29466
+ const conflicts = [];
29444
29467
  let bytesTransferred = 0;
29445
29468
  const progress = onProgress ?? (() => {});
29446
29469
  progress("Fetching remote manifest...");
@@ -29451,7 +29474,8 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
29451
29474
  filesChanged: 0,
29452
29475
  bytesTransferred: 0,
29453
29476
  durationMs: Date.now() - start,
29454
- errors: []
29477
+ errors: [],
29478
+ conflicts: []
29455
29479
  };
29456
29480
  }
29457
29481
  let remoteManifest;
@@ -29466,7 +29490,8 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
29466
29490
  durationMs: Date.now() - start,
29467
29491
  errors: [
29468
29492
  "Failed to decrypt remote manifest - wrong encryption password?"
29469
- ]
29493
+ ],
29494
+ conflicts: []
29470
29495
  };
29471
29496
  }
29472
29497
  progress("Loading local manifest...");
@@ -29505,11 +29530,18 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
29505
29530
  if (localEntry && entry) {
29506
29531
  const result = mergeLastWriteWins(localContent, remoteContent, localEntry.modified, entry.modified);
29507
29532
  await writeFile5(localPath2, result.content);
29508
- await writeFile5(`${localPath2}.bak`, result.backupContent);
29533
+ await writeFile5(`${localPath2}.conflict`, result.backupContent);
29534
+ conflicts.push(diff.path);
29509
29535
  downloaded++;
29510
29536
  progress(`Downloading ${downloaded}/${toPull.length}: ${diff.path}`);
29511
29537
  return;
29512
29538
  }
29539
+ await writeFile5(`${localPath2}.conflict`, localContent);
29540
+ await writeFile5(localPath2, remoteContent);
29541
+ conflicts.push(diff.path);
29542
+ downloaded++;
29543
+ progress(`Downloading ${downloaded}/${toPull.length}: ${diff.path} (conflict saved)`);
29544
+ return;
29513
29545
  }
29514
29546
  const localPath = join6(claudeDir, diff.path);
29515
29547
  await mkdir2(dirname(localPath), { recursive: true });
@@ -29536,7 +29568,8 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
29536
29568
  filesChanged: toPull.length,
29537
29569
  bytesTransferred,
29538
29570
  durationMs,
29539
- errors: errors2
29571
+ errors: errors2,
29572
+ conflicts
29540
29573
  };
29541
29574
  }
29542
29575
  function chunkArray(arr, size) {
@@ -29923,6 +29956,9 @@ function registerAutoCommands(program2) {
29923
29956
  if (result.filesChanged > 0) {
29924
29957
  console.log(`[ccsini] Pulled ${result.filesChanged} files (${result.durationMs}ms)`);
29925
29958
  }
29959
+ if (result.conflicts.length > 0) {
29960
+ console.log(`[ccsini] \u26A0 ${result.conflicts.length} conflict(s) \u2014 check .conflict files in ~/.claude/`);
29961
+ }
29926
29962
  } catch {}
29927
29963
  });
29928
29964
  program2.command("auto-push").description("Auto-push changes (called by Claude Code hooks)").action(async () => {
@@ -29943,6 +29979,9 @@ function registerAutoCommands(program2) {
29943
29979
  if (result.filesChanged > 0) {
29944
29980
  console.log(`[ccsini] Pushed ${result.filesChanged} files (${result.durationMs}ms)`);
29945
29981
  }
29982
+ if (result.conflicts.length > 0) {
29983
+ console.log(`[ccsini] \u26A0 ${result.conflicts.length} conflict(s) \u2014 check .conflict files in ~/.claude/`);
29984
+ }
29946
29985
  } catch {}
29947
29986
  });
29948
29987
  program2.command("unlock").description("Enter encryption password to enable auto-sync").action(async () => {
@@ -30207,6 +30246,9 @@ function registerSyncCommands(program2) {
30207
30246
  if (sessionOptions.enabled) {
30208
30247
  console.log(chalk2.dim(" Sessions: enabled"));
30209
30248
  }
30249
+ if (result.conflicts.length > 0) {
30250
+ console.log(chalk2.yellow(` \u26A0 ${result.conflicts.length} conflict(s) \u2014 check .conflict files in ~/.claude/`));
30251
+ }
30210
30252
  } catch (e) {
30211
30253
  console.error(`Push failed: ${e.message}`);
30212
30254
  process.exit(1);
@@ -30246,6 +30288,9 @@ function registerSyncCommands(program2) {
30246
30288
  if (sessionOptions.enabled) {
30247
30289
  console.log(chalk2.dim(" Sessions: enabled"));
30248
30290
  }
30291
+ if (result.conflicts.length > 0) {
30292
+ console.log(chalk2.yellow(` \u26A0 ${result.conflicts.length} conflict(s) \u2014 check .conflict files in ~/.claude/`));
30293
+ }
30249
30294
  } catch (e) {
30250
30295
  console.error(`Pull failed: ${e.message}`);
30251
30296
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsini",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "description": "Claude Code seamless sync across devices",
5
5
  "type": "module",
6
6
  "bin": {