ccsini 0.1.49 → 0.1.51

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 +44 -23
  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.49";
28023
+ var VERSION = "0.1.51";
28024
28024
 
28025
28025
  // src/commands/init.ts
28026
28026
  init_source();
@@ -29098,7 +29098,7 @@ class CcsiniClient {
29098
29098
  // src/core/sync.ts
29099
29099
  import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir2 } from "fs/promises";
29100
29100
  import { join as join6, dirname } from "path";
29101
- import { homedir as homedir2 } from "os";
29101
+ import { homedir as homedir3 } from "os";
29102
29102
  import { createHash as createHash2 } from "crypto";
29103
29103
 
29104
29104
  // src/core/manifest.ts
@@ -29107,8 +29107,9 @@ import { join as join5 } from "path";
29107
29107
 
29108
29108
  // src/core/scanner.ts
29109
29109
  init_src();
29110
- import { readdir, readFile as readFile4, stat } from "fs/promises";
29110
+ import { readdir, readFile as readFile4, stat, appendFile } from "fs/promises";
29111
29111
  import { join as join4, relative } from "path";
29112
+ import { homedir as homedir2 } from "os";
29112
29113
  import { createHash } from "crypto";
29113
29114
  function categorizeFile(relativePath) {
29114
29115
  for (const [category, patterns] of Object.entries(DEFAULT_CATEGORY_PATTERNS)) {
@@ -29165,17 +29166,23 @@ function filterSessionFiles(files, options) {
29165
29166
  }
29166
29167
  var cacheHits = 0;
29167
29168
  var cacheMisses = 0;
29169
+ async function logDebug(message2) {
29170
+ const logPath = join4(homedir2(), ".ccsini", "cache-debug.log");
29171
+ const timestamp = new Date().toISOString();
29172
+ await appendFile(logPath, `[${timestamp}] ${message2}
29173
+ `).catch(() => {});
29174
+ }
29168
29175
  async function hashFileIfNeeded(filePath, relativePath, currentMtime, cachedManifest) {
29169
29176
  if (cachedManifest) {
29170
29177
  const cached = cachedManifest.files[relativePath];
29171
- if (cached && Math.abs(cached.modified - currentMtime) < 1) {
29178
+ if (cached && Math.abs(cached.modified - currentMtime) < 100) {
29172
29179
  cacheHits++;
29173
29180
  return cached.hash;
29174
29181
  }
29175
29182
  if (cached) {
29176
29183
  const diff = Math.abs(cached.modified - currentMtime);
29177
- if (diff < 1000) {
29178
- console.log(`[DEBUG] mtime diff for ${relativePath}: ${diff}ms (cached: ${cached.modified}, current: ${currentMtime})`);
29184
+ if (diff >= 100) {
29185
+ await logDebug(`Cache MISS: ${relativePath} (\u0394${diff.toFixed(2)}ms)`);
29179
29186
  }
29180
29187
  }
29181
29188
  }
@@ -29236,16 +29243,21 @@ async function scanClaudeDir(claudeDir, onProgress, sessionOptions, cachedManife
29236
29243
  const diff = results.length - filtered.length;
29237
29244
  progress(`Filtered ${diff} session files (${filtered.length} files to sync)`);
29238
29245
  }
29239
- return filtered;
29246
+ const stats = cacheHits + cacheMisses > 0 ? {
29247
+ hits: cacheHits,
29248
+ misses: cacheMisses,
29249
+ hitRate: cacheHits / (cacheHits + cacheMisses) * 100
29250
+ } : undefined;
29251
+ return { files: filtered, cacheStats: stats };
29240
29252
  }
29241
29253
 
29242
29254
  // src/core/manifest.ts
29243
29255
  async function generateManifest(claudeDir, deviceName, onProgress, sessionOptions) {
29244
29256
  const configDir = getConfigDir();
29245
29257
  const cachedManifest = await loadManifest(configDir);
29246
- const files = await scanClaudeDir(claudeDir, onProgress, sessionOptions, cachedManifest ?? undefined);
29258
+ const scanResult = await scanClaudeDir(claudeDir, onProgress, sessionOptions, cachedManifest ?? undefined);
29247
29259
  const entries = {};
29248
- for (const file of files) {
29260
+ for (const file of scanResult.files) {
29249
29261
  entries[file.relativePath] = {
29250
29262
  hash: file.hash,
29251
29263
  size: file.size,
@@ -29254,10 +29266,13 @@ async function generateManifest(claudeDir, deviceName, onProgress, sessionOption
29254
29266
  };
29255
29267
  }
29256
29268
  return {
29257
- version: 1,
29258
- device: deviceName,
29259
- timestamp: Date.now(),
29260
- files: entries
29269
+ manifest: {
29270
+ version: 1,
29271
+ device: deviceName,
29272
+ timestamp: Date.now(),
29273
+ files: entries
29274
+ },
29275
+ cacheStats: scanResult.cacheStats
29261
29276
  };
29262
29277
  }
29263
29278
  async function saveManifest(configDir, manifest) {
@@ -29352,7 +29367,7 @@ function blobKey(filePath, contentHash) {
29352
29367
  return createHash2("sha256").update(`${filePath}:${contentHash}`).digest("hex");
29353
29368
  }
29354
29369
  function getClaudeDir() {
29355
- return join6(homedir2(), ".claude");
29370
+ return join6(homedir3(), ".claude");
29356
29371
  }
29357
29372
  function extractProjects(manifest) {
29358
29373
  const projectMap = new Map;
@@ -29387,7 +29402,7 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
29387
29402
  const conflicts = [];
29388
29403
  let bytesTransferred = 0;
29389
29404
  const progress = onProgress ?? (() => {});
29390
- const localManifest = await generateManifest(claudeDir, deviceName, progress, sessionOptions);
29405
+ const { manifest: localManifest, cacheStats } = await generateManifest(claudeDir, deviceName, progress, sessionOptions);
29391
29406
  const fileCount = Object.keys(localManifest.files).length;
29392
29407
  progress(`Scanned ${fileCount} files`);
29393
29408
  progress("Fetching remote manifest...");
@@ -29481,7 +29496,8 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress, se
29481
29496
  bytesTransferred,
29482
29497
  durationMs,
29483
29498
  errors: errors2,
29484
- conflicts
29499
+ conflicts,
29500
+ cacheStats
29485
29501
  };
29486
29502
  }
29487
29503
  async function pullSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions) {
@@ -29628,7 +29644,7 @@ import { execSync } from "child_process";
29628
29644
  import { platform } from "os";
29629
29645
  import { writeFile as writeFile6, unlink } from "fs/promises";
29630
29646
  import { join as join7 } from "path";
29631
- import { homedir as homedir3 } from "os";
29647
+ import { homedir as homedir4 } from "os";
29632
29648
  var TASK_NAME = "ccsini-heartbeat";
29633
29649
  var INTERVAL_MINUTES = 3;
29634
29650
  async function installHeartbeatScheduler() {
@@ -29663,7 +29679,7 @@ async function uninstallWindows() {
29663
29679
  } catch {}
29664
29680
  }
29665
29681
  function getLaunchAgentPath() {
29666
- return join7(homedir3(), "Library", "LaunchAgents", `com.ccsini.heartbeat.plist`);
29682
+ return join7(homedir4(), "Library", "LaunchAgents", `com.ccsini.heartbeat.plist`);
29667
29683
  }
29668
29684
  async function installMacOS() {
29669
29685
  const plistPath = getLaunchAgentPath();
@@ -30002,7 +30018,11 @@ function registerAutoCommands(program2) {
30002
30018
  };
30003
30019
  const result = await pushSync(client, masterKey, config.deviceName, configDir, undefined, sessionOptions, { skipConflictBackup: true });
30004
30020
  if (result.filesChanged > 0) {
30005
- console.log(`[ccsini] Pushed ${result.filesChanged} files (${result.durationMs}ms)`);
30021
+ let msg = `[ccsini] Pushed ${result.filesChanged} files (${result.durationMs}ms)`;
30022
+ if (result.cacheStats && result.cacheStats.hits > 0) {
30023
+ msg += ` [cache: ${result.cacheStats.hitRate.toFixed(0)}% hits]`;
30024
+ }
30025
+ console.log(msg);
30006
30026
  }
30007
30027
  if (result.conflicts.length > 0) {
30008
30028
  console.log(`[ccsini] \u26A0 ${result.conflicts.length} conflict(s) \u2014 check .conflict files in ~/.claude/`);
@@ -30422,7 +30442,7 @@ function registerSyncCommands(program2) {
30422
30442
  const spinner = ora2("Checking status...").start();
30423
30443
  const localManifest = await loadManifest(configDir);
30424
30444
  const claudeDir = getClaudeDir();
30425
- const currentManifest = await generateManifest(claudeDir, config.deviceName, (msg) => {
30445
+ const { manifest: currentManifest } = await generateManifest(claudeDir, config.deviceName, (msg) => {
30426
30446
  spinner.text = msg;
30427
30447
  });
30428
30448
  let remoteManifest = null;
@@ -30604,14 +30624,15 @@ function registerSessionsCommand(program2) {
30604
30624
  maxPerProject: sessionConfig.maxPerProject,
30605
30625
  maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
30606
30626
  };
30607
- const withSessions = await scanClaudeDir(claudeDir, undefined, enabledOpts);
30608
- const sessionFiles = withSessions.filter((f) => isSessionFile(f.relativePath));
30627
+ const withSessionsResult = await scanClaudeDir(claudeDir, undefined, enabledOpts);
30628
+ const sessionFiles = withSessionsResult.files.filter((f) => isSessionFile(f.relativePath));
30609
30629
  const disabledOpts = {
30610
30630
  enabled: false,
30611
30631
  maxPerProject: sessionConfig.maxPerProject,
30612
30632
  maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
30613
30633
  };
30614
- const withoutSessions = await scanClaudeDir(claudeDir, undefined, disabledOpts);
30634
+ const withoutSessionsResult = await scanClaudeDir(claudeDir, undefined, disabledOpts);
30635
+ const withoutSessions = withoutSessionsResult.files;
30615
30636
  spinner.stop();
30616
30637
  const totalSessionSize = sessionFiles.reduce((sum, f) => sum + f.size, 0);
30617
30638
  const projects = new Set(sessionFiles.map((f) => f.relativePath.split("/").slice(0, 2).join("/")));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsini",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "description": "Claude Code seamless sync across devices",
5
5
  "type": "module",
6
6
  "bin": {