ccsini 0.1.45 → 0.1.46

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 +96 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28020,7 +28020,7 @@ var {
28020
28020
  } = import__.default;
28021
28021
 
28022
28022
  // src/version.ts
28023
- var VERSION = "0.1.45";
28023
+ var VERSION = "0.1.46";
28024
28024
 
28025
28025
  // src/commands/init.ts
28026
28026
  init_source();
@@ -29355,7 +29355,7 @@ function extractProjects(manifest) {
29355
29355
  lastSyncedAt: manifest.timestamp
29356
29356
  }));
29357
29357
  }
29358
- async function pushSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions) {
29358
+ async function pushSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions, pushOptions) {
29359
29359
  const start = Date.now();
29360
29360
  const claudeDir = getClaudeDir();
29361
29361
  const errors2 = [];
@@ -29380,7 +29380,7 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
29380
29380
  const diffs = diffManifests(localManifest, remoteManifest);
29381
29381
  const toPush = diffs.filter((d) => d.action === "push" || d.action === "merge");
29382
29382
  progress(`${toPush.length} files to push`);
29383
- if (remoteManifest) {
29383
+ if (remoteManifest && !pushOptions?.skipConflictBackup) {
29384
29384
  const merges = toPush.filter((d) => d.action === "merge" && d.remoteHash);
29385
29385
  for (const diff of merges) {
29386
29386
  try {
@@ -29975,7 +29975,7 @@ function registerAutoCommands(program2) {
29975
29975
  maxPerProject: storedSession.maxPerProject,
29976
29976
  maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
29977
29977
  };
29978
- const result = await pushSync(client, masterKey, config.deviceName, configDir, undefined, sessionOptions);
29978
+ const result = await pushSync(client, masterKey, config.deviceName, configDir, undefined, sessionOptions, { skipConflictBackup: true });
29979
29979
  if (result.filesChanged > 0) {
29980
29980
  console.log(`[ccsini] Pushed ${result.filesChanged} files (${result.durationMs}ms)`);
29981
29981
  }
@@ -30597,6 +30597,97 @@ function registerSessionsCommand(program2) {
30597
30597
  });
30598
30598
  }
30599
30599
 
30600
+ // src/commands/conflicts.ts
30601
+ import { readdir as readdir2, readFile as readFile9, unlink as unlink2, writeFile as writeFile9 } from "fs/promises";
30602
+ import { join as join12, relative as relative2 } from "path";
30603
+ async function findConflictFiles(dir, base) {
30604
+ base = base ?? dir;
30605
+ const results = [];
30606
+ const entries = await readdir2(dir, { withFileTypes: true });
30607
+ for (const entry of entries) {
30608
+ const full = join12(dir, entry.name);
30609
+ if (entry.isDirectory()) {
30610
+ results.push(...await findConflictFiles(full, base));
30611
+ } else if (entry.name.endsWith(".conflict")) {
30612
+ results.push(relative2(base, full).split("\\").join("/"));
30613
+ }
30614
+ }
30615
+ return results;
30616
+ }
30617
+ function registerConflictsCommand(program2) {
30618
+ program2.command("conflicts").description("List and resolve sync conflicts").option("--keep <side>", "Resolve all: 'local' keeps .conflict, 'remote' discards .conflict").action(async (opts) => {
30619
+ const chalk2 = (await Promise.resolve().then(() => (init_source(), exports_source))).default;
30620
+ const claudeDir = getClaudeDir();
30621
+ const conflictFiles = await findConflictFiles(claudeDir);
30622
+ if (conflictFiles.length === 0) {
30623
+ console.log(chalk2.green("No conflicts found."));
30624
+ return;
30625
+ }
30626
+ console.log(chalk2.bold(`
30627
+ ${conflictFiles.length} conflict(s) found:
30628
+ `));
30629
+ for (const file of conflictFiles) {
30630
+ const original = file.replace(/\.conflict$/, "");
30631
+ console.log(` ${chalk2.yellow("\u26A0")} ${original}`);
30632
+ console.log(chalk2.dim(` conflict: ~/.claude/${file}`));
30633
+ }
30634
+ console.log();
30635
+ if (opts.keep === "remote") {
30636
+ for (const file of conflictFiles) {
30637
+ await unlink2(join12(claudeDir, file));
30638
+ }
30639
+ console.log(chalk2.green(`Resolved ${conflictFiles.length} conflict(s) \u2014 kept remote versions.`));
30640
+ return;
30641
+ }
30642
+ if (opts.keep === "local") {
30643
+ for (const file of conflictFiles) {
30644
+ const conflictPath = join12(claudeDir, file);
30645
+ const originalPath = conflictPath.replace(/\.conflict$/, "");
30646
+ const conflictContent = await readFile9(conflictPath);
30647
+ await writeFile9(originalPath, conflictContent);
30648
+ await unlink2(conflictPath);
30649
+ }
30650
+ console.log(chalk2.green(`Resolved ${conflictFiles.length} conflict(s) \u2014 kept local versions.`));
30651
+ return;
30652
+ }
30653
+ const inquirer2 = await Promise.resolve().then(() => (init_dist16(), exports_dist));
30654
+ for (const file of conflictFiles) {
30655
+ const original = file.replace(/\.conflict$/, "");
30656
+ const conflictPath = join12(claudeDir, file);
30657
+ const originalPath = join12(claudeDir, original);
30658
+ const { action } = await inquirer2.default.prompt([
30659
+ {
30660
+ type: "list",
30661
+ name: "action",
30662
+ message: `${original}:`,
30663
+ choices: [
30664
+ { name: "Keep remote (current file)", value: "remote" },
30665
+ { name: "Keep local (restore from .conflict)", value: "local" },
30666
+ { name: "Skip", value: "skip" }
30667
+ ]
30668
+ }
30669
+ ]);
30670
+ if (action === "remote") {
30671
+ await unlink2(conflictPath);
30672
+ console.log(chalk2.dim(` Kept remote \u2014 deleted ${file}`));
30673
+ } else if (action === "local") {
30674
+ const conflictContent = await readFile9(conflictPath);
30675
+ await writeFile9(originalPath, conflictContent);
30676
+ await unlink2(conflictPath);
30677
+ console.log(chalk2.dim(` Restored local version`));
30678
+ }
30679
+ }
30680
+ const remaining = await findConflictFiles(claudeDir);
30681
+ if (remaining.length === 0) {
30682
+ console.log(chalk2.green(`
30683
+ All conflicts resolved.`));
30684
+ } else {
30685
+ console.log(chalk2.yellow(`
30686
+ ${remaining.length} conflict(s) remaining.`));
30687
+ }
30688
+ });
30689
+ }
30690
+
30600
30691
  // src/commands/menu.ts
30601
30692
  init_source();
30602
30693
  init_dist16();
@@ -30663,6 +30754,7 @@ registerSyncCommands(program2);
30663
30754
  registerHooksCommands(program2);
30664
30755
  registerResetCommand(program2);
30665
30756
  registerSessionsCommand(program2);
30757
+ registerConflictsCommand(program2);
30666
30758
  program2.command("version").description("Show current version").action(() => {
30667
30759
  console.log(VERSION);
30668
30760
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsini",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "Claude Code seamless sync across devices",
5
5
  "type": "module",
6
6
  "bin": {