ccsini 0.1.44 → 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.
- package/dist/index.js +121 -8
- 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.
|
|
28023
|
+
var VERSION = "0.1.46";
|
|
28024
28024
|
|
|
28025
28025
|
// src/commands/init.ts
|
|
28026
28026
|
init_source();
|
|
@@ -29355,10 +29355,11 @@ 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 = [];
|
|
29362
|
+
const conflicts = [];
|
|
29362
29363
|
let bytesTransferred = 0;
|
|
29363
29364
|
const progress = onProgress ?? (() => {});
|
|
29364
29365
|
const localManifest = await generateManifest(claudeDir, deviceName, progress, sessionOptions);
|
|
@@ -29379,7 +29380,7 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
|
|
|
29379
29380
|
const diffs = diffManifests(localManifest, remoteManifest);
|
|
29380
29381
|
const toPush = diffs.filter((d) => d.action === "push" || d.action === "merge");
|
|
29381
29382
|
progress(`${toPush.length} files to push`);
|
|
29382
|
-
if (remoteManifest) {
|
|
29383
|
+
if (remoteManifest && !pushOptions?.skipConflictBackup) {
|
|
29383
29384
|
const merges = toPush.filter((d) => d.action === "merge" && d.remoteHash);
|
|
29384
29385
|
for (const diff of merges) {
|
|
29385
29386
|
try {
|
|
@@ -29393,6 +29394,7 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
|
|
|
29393
29394
|
const conflictPath = join6(claudeDir, `${diff.path}.conflict`);
|
|
29394
29395
|
await mkdir2(dirname(conflictPath), { recursive: true });
|
|
29395
29396
|
await writeFile5(conflictPath, decrypted);
|
|
29397
|
+
conflicts.push(diff.path);
|
|
29396
29398
|
progress(`Saved conflict backup: ${diff.path}.conflict`);
|
|
29397
29399
|
} catch {}
|
|
29398
29400
|
}
|
|
@@ -29453,13 +29455,15 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
|
|
|
29453
29455
|
filesChanged: toPush.length,
|
|
29454
29456
|
bytesTransferred,
|
|
29455
29457
|
durationMs,
|
|
29456
|
-
errors: errors2
|
|
29458
|
+
errors: errors2,
|
|
29459
|
+
conflicts
|
|
29457
29460
|
};
|
|
29458
29461
|
}
|
|
29459
29462
|
async function pullSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions) {
|
|
29460
29463
|
const start = Date.now();
|
|
29461
29464
|
const claudeDir = getClaudeDir();
|
|
29462
29465
|
const errors2 = [];
|
|
29466
|
+
const conflicts = [];
|
|
29463
29467
|
let bytesTransferred = 0;
|
|
29464
29468
|
const progress = onProgress ?? (() => {});
|
|
29465
29469
|
progress("Fetching remote manifest...");
|
|
@@ -29470,7 +29474,8 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
|
|
|
29470
29474
|
filesChanged: 0,
|
|
29471
29475
|
bytesTransferred: 0,
|
|
29472
29476
|
durationMs: Date.now() - start,
|
|
29473
|
-
errors: []
|
|
29477
|
+
errors: [],
|
|
29478
|
+
conflicts: []
|
|
29474
29479
|
};
|
|
29475
29480
|
}
|
|
29476
29481
|
let remoteManifest;
|
|
@@ -29485,7 +29490,8 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
|
|
|
29485
29490
|
durationMs: Date.now() - start,
|
|
29486
29491
|
errors: [
|
|
29487
29492
|
"Failed to decrypt remote manifest - wrong encryption password?"
|
|
29488
|
-
]
|
|
29493
|
+
],
|
|
29494
|
+
conflicts: []
|
|
29489
29495
|
};
|
|
29490
29496
|
}
|
|
29491
29497
|
progress("Loading local manifest...");
|
|
@@ -29525,12 +29531,14 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
|
|
|
29525
29531
|
const result = mergeLastWriteWins(localContent, remoteContent, localEntry.modified, entry.modified);
|
|
29526
29532
|
await writeFile5(localPath2, result.content);
|
|
29527
29533
|
await writeFile5(`${localPath2}.conflict`, result.backupContent);
|
|
29534
|
+
conflicts.push(diff.path);
|
|
29528
29535
|
downloaded++;
|
|
29529
29536
|
progress(`Downloading ${downloaded}/${toPull.length}: ${diff.path}`);
|
|
29530
29537
|
return;
|
|
29531
29538
|
}
|
|
29532
29539
|
await writeFile5(`${localPath2}.conflict`, localContent);
|
|
29533
29540
|
await writeFile5(localPath2, remoteContent);
|
|
29541
|
+
conflicts.push(diff.path);
|
|
29534
29542
|
downloaded++;
|
|
29535
29543
|
progress(`Downloading ${downloaded}/${toPull.length}: ${diff.path} (conflict saved)`);
|
|
29536
29544
|
return;
|
|
@@ -29560,7 +29568,8 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress, se
|
|
|
29560
29568
|
filesChanged: toPull.length,
|
|
29561
29569
|
bytesTransferred,
|
|
29562
29570
|
durationMs,
|
|
29563
|
-
errors: errors2
|
|
29571
|
+
errors: errors2,
|
|
29572
|
+
conflicts
|
|
29564
29573
|
};
|
|
29565
29574
|
}
|
|
29566
29575
|
function chunkArray(arr, size) {
|
|
@@ -29947,6 +29956,9 @@ function registerAutoCommands(program2) {
|
|
|
29947
29956
|
if (result.filesChanged > 0) {
|
|
29948
29957
|
console.log(`[ccsini] Pulled ${result.filesChanged} files (${result.durationMs}ms)`);
|
|
29949
29958
|
}
|
|
29959
|
+
if (result.conflicts.length > 0) {
|
|
29960
|
+
console.log(`[ccsini] \u26A0 ${result.conflicts.length} conflict(s) \u2014 check .conflict files in ~/.claude/`);
|
|
29961
|
+
}
|
|
29950
29962
|
} catch {}
|
|
29951
29963
|
});
|
|
29952
29964
|
program2.command("auto-push").description("Auto-push changes (called by Claude Code hooks)").action(async () => {
|
|
@@ -29963,10 +29975,13 @@ function registerAutoCommands(program2) {
|
|
|
29963
29975
|
maxPerProject: storedSession.maxPerProject,
|
|
29964
29976
|
maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
|
|
29965
29977
|
};
|
|
29966
|
-
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 });
|
|
29967
29979
|
if (result.filesChanged > 0) {
|
|
29968
29980
|
console.log(`[ccsini] Pushed ${result.filesChanged} files (${result.durationMs}ms)`);
|
|
29969
29981
|
}
|
|
29982
|
+
if (result.conflicts.length > 0) {
|
|
29983
|
+
console.log(`[ccsini] \u26A0 ${result.conflicts.length} conflict(s) \u2014 check .conflict files in ~/.claude/`);
|
|
29984
|
+
}
|
|
29970
29985
|
} catch {}
|
|
29971
29986
|
});
|
|
29972
29987
|
program2.command("unlock").description("Enter encryption password to enable auto-sync").action(async () => {
|
|
@@ -30231,6 +30246,9 @@ function registerSyncCommands(program2) {
|
|
|
30231
30246
|
if (sessionOptions.enabled) {
|
|
30232
30247
|
console.log(chalk2.dim(" Sessions: enabled"));
|
|
30233
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
|
+
}
|
|
30234
30252
|
} catch (e) {
|
|
30235
30253
|
console.error(`Push failed: ${e.message}`);
|
|
30236
30254
|
process.exit(1);
|
|
@@ -30270,6 +30288,9 @@ function registerSyncCommands(program2) {
|
|
|
30270
30288
|
if (sessionOptions.enabled) {
|
|
30271
30289
|
console.log(chalk2.dim(" Sessions: enabled"));
|
|
30272
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
|
+
}
|
|
30273
30294
|
} catch (e) {
|
|
30274
30295
|
console.error(`Pull failed: ${e.message}`);
|
|
30275
30296
|
process.exit(1);
|
|
@@ -30576,6 +30597,97 @@ function registerSessionsCommand(program2) {
|
|
|
30576
30597
|
});
|
|
30577
30598
|
}
|
|
30578
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
|
+
|
|
30579
30691
|
// src/commands/menu.ts
|
|
30580
30692
|
init_source();
|
|
30581
30693
|
init_dist16();
|
|
@@ -30642,6 +30754,7 @@ registerSyncCommands(program2);
|
|
|
30642
30754
|
registerHooksCommands(program2);
|
|
30643
30755
|
registerResetCommand(program2);
|
|
30644
30756
|
registerSessionsCommand(program2);
|
|
30757
|
+
registerConflictsCommand(program2);
|
|
30645
30758
|
program2.command("version").description("Show current version").action(() => {
|
|
30646
30759
|
console.log(VERSION);
|
|
30647
30760
|
});
|