ccsini 0.1.34 → 0.1.35

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 +270 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -27865,8 +27865,18 @@ var init_webapi = __esm(() => {
27865
27865
  init_verify4();
27866
27866
  init_sign4();
27867
27867
  });
27868
+
27869
+ // ../shared/src/types.ts
27870
+ function decodeProjectPath(encoded) {
27871
+ const driveMatch = encoded.match(/^([A-Za-z])--(.*)$/);
27872
+ if (driveMatch) {
27873
+ return `${driveMatch[1].toUpperCase()}:/${driveMatch[2].replace(/-/g, "/")}`;
27874
+ }
27875
+ return "/" + encoded.replace(/-/g, "/");
27876
+ }
27877
+
27868
27878
  // ../shared/src/constants.ts
27869
- var DEFAULT_CATEGORY_PATTERNS, MERGE_STRATEGIES, NEVER_SYNC_PATTERNS, JWT_EXPIRY_SECONDS = 900, MAX_CONCURRENT_TRANSFERS = 50;
27879
+ var DEFAULT_CATEGORY_PATTERNS, MERGE_STRATEGIES, NEVER_SYNC_PATTERNS, SESSION_SYNC_DEFAULTS, JWT_EXPIRY_SECONDS = 900, MAX_CONCURRENT_TRANSFERS = 50;
27870
27880
  var init_constants = __esm(() => {
27871
27881
  DEFAULT_CATEGORY_PATTERNS = {
27872
27882
  session: ["projects/**/*.jsonl"],
@@ -27910,7 +27920,6 @@ var init_constants = __esm(() => {
27910
27920
  "**/node_modules/**",
27911
27921
  "**/cache/**",
27912
27922
  "**/tool-results/**",
27913
- "projects/**/*.jsonl",
27914
27923
  "plugins/**",
27915
27924
  "plans/**",
27916
27925
  "todos/**",
@@ -27923,6 +27932,11 @@ var init_constants = __esm(() => {
27923
27932
  "settings.*.json",
27924
27933
  "history.jsonl"
27925
27934
  ];
27935
+ SESSION_SYNC_DEFAULTS = {
27936
+ enabled: false,
27937
+ maxPerProject: 3,
27938
+ maxFileSize: 50000000
27939
+ };
27926
27940
  });
27927
27941
 
27928
27942
  // ../shared/src/index.ts
@@ -28004,7 +28018,7 @@ var {
28004
28018
  } = import__.default;
28005
28019
 
28006
28020
  // src/version.ts
28007
- var VERSION = "0.1.34";
28021
+ var VERSION = "0.1.35";
28008
28022
 
28009
28023
  // src/commands/init.ts
28010
28024
  init_source();
@@ -28675,6 +28689,7 @@ function decryptFile(masterKey, filePath, ciphertext) {
28675
28689
  }
28676
28690
 
28677
28691
  // src/core/keystore.ts
28692
+ init_src();
28678
28693
  import { readFile, writeFile, mkdir, access } from "fs/promises";
28679
28694
  import { join } from "path";
28680
28695
  import { homedir } from "os";
@@ -28720,6 +28735,31 @@ async function loadKeys(configDir) {
28720
28735
  apiUrl: config.apiUrl
28721
28736
  };
28722
28737
  }
28738
+ async function loadSessionConfig(configDir) {
28739
+ const dir = configDir ?? getConfigDir();
28740
+ try {
28741
+ const configRaw = await readFile(join(dir, "config.json"), "utf-8");
28742
+ const config = JSON.parse(configRaw);
28743
+ return {
28744
+ enabled: config.syncSessions ?? SESSION_SYNC_DEFAULTS.enabled,
28745
+ maxPerProject: config.sessionsPerProject ?? SESSION_SYNC_DEFAULTS.maxPerProject
28746
+ };
28747
+ } catch {
28748
+ return {
28749
+ enabled: SESSION_SYNC_DEFAULTS.enabled,
28750
+ maxPerProject: SESSION_SYNC_DEFAULTS.maxPerProject
28751
+ };
28752
+ }
28753
+ }
28754
+ async function saveSessionConfig(configDir, sessionConfig) {
28755
+ const dir = configDir ?? getConfigDir();
28756
+ const configPath = join(dir, "config.json");
28757
+ const configRaw = await readFile(configPath, "utf-8");
28758
+ const config = JSON.parse(configRaw);
28759
+ config.syncSessions = sessionConfig.enabled;
28760
+ config.sessionsPerProject = sessionConfig.maxPerProject;
28761
+ await writeFile(configPath, JSON.stringify(config, null, 2));
28762
+ }
28723
28763
 
28724
28764
  // src/core/schema.ts
28725
28765
  init_src();
@@ -29018,6 +29058,17 @@ class CcsiniClient {
29018
29058
  if (!res.ok)
29019
29059
  throw new Error("Failed to wipe account data");
29020
29060
  }
29061
+ async putProjects(projects, device) {
29062
+ const res = await fetch(`${this.apiUrl}/api/sync/projects`, {
29063
+ method: "PUT",
29064
+ headers: this.getHeaders(),
29065
+ body: JSON.stringify({ projects, device, timestamp: Date.now() })
29066
+ });
29067
+ if (!res.ok) {
29068
+ const text = await res.text().catch(() => "");
29069
+ throw new Error(`Failed to send projects (${res.status}): ${text}`);
29070
+ }
29071
+ }
29021
29072
  async resetAll() {
29022
29073
  const res = await fetch(`${this.apiUrl}/api/sync/reset`, {
29023
29074
  method: "DELETE",
@@ -29068,6 +29119,42 @@ function matchGlobPattern(path2, pattern) {
29068
29119
  function isExcluded(relativePath) {
29069
29120
  return NEVER_SYNC_PATTERNS.some((pattern) => matchGlobPattern(relativePath, pattern));
29070
29121
  }
29122
+ function isSessionFile(relativePath) {
29123
+ return matchGlobPattern(relativePath, "projects/**/*.jsonl");
29124
+ }
29125
+ function filterSessionFiles(files, options) {
29126
+ const sessions = [];
29127
+ const nonSessions = [];
29128
+ for (const file of files) {
29129
+ if (isSessionFile(file.relativePath)) {
29130
+ sessions.push(file);
29131
+ } else {
29132
+ nonSessions.push(file);
29133
+ }
29134
+ }
29135
+ if (!options.enabled) {
29136
+ return nonSessions;
29137
+ }
29138
+ const byProject = new Map;
29139
+ for (const file of sessions) {
29140
+ if (file.size > options.maxFileSize)
29141
+ continue;
29142
+ const parts = file.relativePath.split("/");
29143
+ const projectKey = parts.slice(0, 2).join("/");
29144
+ let group = byProject.get(projectKey);
29145
+ if (!group) {
29146
+ group = [];
29147
+ byProject.set(projectKey, group);
29148
+ }
29149
+ group.push(file);
29150
+ }
29151
+ const kept = [];
29152
+ for (const group of byProject.values()) {
29153
+ group.sort((a, b) => b.modified - a.modified);
29154
+ kept.push(...group.slice(0, options.maxPerProject));
29155
+ }
29156
+ return [...nonSessions, ...kept];
29157
+ }
29071
29158
  async function hashFile(filePath) {
29072
29159
  const content = await readFile4(filePath);
29073
29160
  return createHash("sha256").update(content).digest("hex");
@@ -29102,18 +29189,28 @@ async function scanDir(baseDir, currentDir, results, onProgress) {
29102
29189
  }
29103
29190
  }
29104
29191
  }
29105
- async function scanClaudeDir(claudeDir, onProgress) {
29192
+ async function scanClaudeDir(claudeDir, onProgress, sessionOptions) {
29106
29193
  const progress = onProgress ?? (() => {});
29107
29194
  const results = [];
29108
29195
  progress("Scanning local files...");
29109
29196
  await scanDir(claudeDir, claudeDir, results, progress);
29110
29197
  progress(`Scan complete: ${results.length} files found`);
29111
- return results;
29198
+ const sessionOpts = sessionOptions ?? {
29199
+ enabled: SESSION_SYNC_DEFAULTS.enabled,
29200
+ maxPerProject: SESSION_SYNC_DEFAULTS.maxPerProject,
29201
+ maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
29202
+ };
29203
+ const filtered = filterSessionFiles(results, sessionOpts);
29204
+ if (filtered.length !== results.length) {
29205
+ const diff = results.length - filtered.length;
29206
+ progress(`Filtered ${diff} session files (${filtered.length} files to sync)`);
29207
+ }
29208
+ return filtered;
29112
29209
  }
29113
29210
 
29114
29211
  // src/core/manifest.ts
29115
- async function generateManifest(claudeDir, deviceName, onProgress) {
29116
- const files = await scanClaudeDir(claudeDir, onProgress);
29212
+ async function generateManifest(claudeDir, deviceName, onProgress, sessionOptions) {
29213
+ const files = await scanClaudeDir(claudeDir, onProgress, sessionOptions);
29117
29214
  const entries = {};
29118
29215
  for (const file of files) {
29119
29216
  entries[file.relativePath] = {
@@ -29217,19 +29314,46 @@ function mergeLastWriteWins(localContent, remoteContent, localModified, remoteMo
29217
29314
 
29218
29315
  // src/core/sync.ts
29219
29316
  init_src();
29317
+ init_src();
29220
29318
  function blobKey(filePath, contentHash) {
29221
29319
  return createHash2("sha256").update(`${filePath}:${contentHash}`).digest("hex");
29222
29320
  }
29223
29321
  function getClaudeDir() {
29224
29322
  return join6(homedir2(), ".claude");
29225
29323
  }
29226
- async function pushSync(client, masterKey, deviceName, configDir, onProgress) {
29324
+ function extractProjects(manifest) {
29325
+ const projectMap = new Map;
29326
+ for (const [path2, entry] of Object.entries(manifest.files)) {
29327
+ const match = path2.match(/^projects\/([^/]+)\//);
29328
+ if (!match)
29329
+ continue;
29330
+ const encoded = match[1];
29331
+ let proj = projectMap.get(encoded);
29332
+ if (!proj) {
29333
+ proj = { fileCount: 0, totalSize: 0, categories: new Set };
29334
+ projectMap.set(encoded, proj);
29335
+ }
29336
+ proj.fileCount++;
29337
+ proj.totalSize += entry.size;
29338
+ proj.categories.add(entry.category);
29339
+ }
29340
+ return Array.from(projectMap.entries()).map(([encoded, data]) => ({
29341
+ encodedPath: encoded,
29342
+ decodedPath: decodeProjectPath(encoded),
29343
+ name: encoded.split("-").pop() || encoded,
29344
+ fileCount: data.fileCount,
29345
+ totalSize: data.totalSize,
29346
+ categories: Array.from(data.categories),
29347
+ lastSyncedAt: manifest.timestamp
29348
+ }));
29349
+ }
29350
+ async function pushSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions) {
29227
29351
  const start = Date.now();
29228
29352
  const claudeDir = getClaudeDir();
29229
29353
  const errors2 = [];
29230
29354
  let bytesTransferred = 0;
29231
29355
  const progress = onProgress ?? (() => {});
29232
- const localManifest = await generateManifest(claudeDir, deviceName, progress);
29356
+ const localManifest = await generateManifest(claudeDir, deviceName, progress, sessionOptions);
29233
29357
  const fileCount = Object.keys(localManifest.files).length;
29234
29358
  progress(`Scanned ${fileCount} files`);
29235
29359
  progress("Fetching remote manifest...");
@@ -29292,6 +29416,12 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress) {
29292
29416
  bytesTransferred,
29293
29417
  durationMs
29294
29418
  });
29419
+ try {
29420
+ const projects = extractProjects(localManifest);
29421
+ if (projects.length > 0) {
29422
+ await client.putProjects(projects, deviceName);
29423
+ }
29424
+ } catch {}
29295
29425
  return {
29296
29426
  action: "push",
29297
29427
  filesChanged: toPush.length,
@@ -29300,7 +29430,7 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress) {
29300
29430
  errors: errors2
29301
29431
  };
29302
29432
  }
29303
- async function pullSync(client, masterKey, deviceName, configDir, onProgress) {
29433
+ async function pullSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions) {
29304
29434
  const start = Date.now();
29305
29435
  const claudeDir = getClaudeDir();
29306
29436
  const errors2 = [];
@@ -29336,7 +29466,8 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress) {
29336
29466
  const localManifest = await loadManifest(configDir);
29337
29467
  progress("Comparing manifests...");
29338
29468
  const diffs = diffManifests(localManifest, remoteManifest);
29339
- const toPull = diffs.filter((d) => (d.action === "pull" || d.action === "merge") && !isExcluded(d.path));
29469
+ const sessionsEnabled = sessionOptions?.enabled ?? SESSION_SYNC_DEFAULTS.enabled;
29470
+ const toPull = diffs.filter((d) => (d.action === "pull" || d.action === "merge") && !isExcluded(d.path) && (sessionsEnabled || !isSessionFile(d.path)));
29340
29471
  progress(`${toPull.length} files to pull`);
29341
29472
  let downloaded = 0;
29342
29473
  const chunks = chunkArray(toPull, MAX_CONCURRENT_TRANSFERS);
@@ -29614,6 +29745,7 @@ function detectPlatform() {
29614
29745
  // src/commands/auto.ts
29615
29746
  init_auth();
29616
29747
  init_auth();
29748
+ init_src();
29617
29749
  import { readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
29618
29750
  import { join as join8 } from "path";
29619
29751
  async function getMasterKey(configDir) {
@@ -29640,7 +29772,13 @@ function registerAutoCommands(program2) {
29640
29772
  const masterKey = await getMasterKey(configDir);
29641
29773
  const client = await getAuthenticatedClient(configDir);
29642
29774
  const config = await loadKeys(configDir);
29643
- const result = await pullSync(client, masterKey, config.deviceName, configDir);
29775
+ const storedSession = await loadSessionConfig(configDir);
29776
+ const sessionOptions = {
29777
+ enabled: storedSession.enabled,
29778
+ maxPerProject: storedSession.maxPerProject,
29779
+ maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
29780
+ };
29781
+ const result = await pullSync(client, masterKey, config.deviceName, configDir, undefined, sessionOptions);
29644
29782
  if (result.filesChanged > 0) {
29645
29783
  console.log(`[ccsini] Pulled ${result.filesChanged} files (${result.durationMs}ms)`);
29646
29784
  }
@@ -29654,7 +29792,13 @@ function registerAutoCommands(program2) {
29654
29792
  const masterKey = await getMasterKey(configDir);
29655
29793
  const client = await getAuthenticatedClient(configDir);
29656
29794
  const config = await loadKeys(configDir);
29657
- const result = await pushSync(client, masterKey, config.deviceName, configDir);
29795
+ const storedSession = await loadSessionConfig(configDir);
29796
+ const sessionOptions = {
29797
+ enabled: storedSession.enabled,
29798
+ maxPerProject: storedSession.maxPerProject,
29799
+ maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
29800
+ };
29801
+ const result = await pushSync(client, masterKey, config.deviceName, configDir, undefined, sessionOptions);
29658
29802
  if (result.filesChanged > 0) {
29659
29803
  console.log(`[ccsini] Pushed ${result.filesChanged} files (${result.durationMs}ms)`);
29660
29804
  }
@@ -29846,6 +29990,7 @@ Uninstall failed. Try manually:`);
29846
29990
 
29847
29991
  // src/commands/sync.ts
29848
29992
  init_auth();
29993
+ init_src();
29849
29994
  import { readFile as readFile8 } from "fs/promises";
29850
29995
  import { join as join10 } from "path";
29851
29996
  async function getMasterKey2(configDir) {
@@ -29885,7 +30030,7 @@ function formatBytes(bytes) {
29885
30030
  }
29886
30031
  function registerSyncCommands(program2) {
29887
30032
  const syncCmd = program2.command("sync").description("Sync management commands");
29888
- syncCmd.command("push").description("Push local changes to cloud").action(async () => {
30033
+ syncCmd.command("push").description("Push local changes to cloud").option("--with-sessions", "Include session files in sync").option("--no-sessions", "Exclude session files from sync").action(async (opts) => {
29889
30034
  const configDir = getConfigDir();
29890
30035
  if (!await configExists(configDir)) {
29891
30036
  console.error("Not initialized. Run 'ccsini init' first.");
@@ -29897,10 +30042,16 @@ function registerSyncCommands(program2) {
29897
30042
  const masterKey = await getMasterKey2(configDir);
29898
30043
  const client = await getAuthenticatedClient2(configDir);
29899
30044
  const config = await loadKeys(configDir);
30045
+ const storedSession = await loadSessionConfig(configDir);
30046
+ const sessionOptions = {
30047
+ enabled: opts.withSessions ? true : opts.sessions === false ? false : storedSession.enabled,
30048
+ maxPerProject: storedSession.maxPerProject,
30049
+ maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
30050
+ };
29900
30051
  const spinner = ora2("Scanning files...").start();
29901
30052
  const result = await pushSync(client, masterKey, config.deviceName, configDir, (msg) => {
29902
30053
  spinner.text = msg;
29903
- });
30054
+ }, sessionOptions);
29904
30055
  if (result.errors.length > 0) {
29905
30056
  spinner.warn("Push completed with errors");
29906
30057
  for (const err of result.errors) {
@@ -29910,12 +30061,15 @@ function registerSyncCommands(program2) {
29910
30061
  spinner.succeed("Push complete");
29911
30062
  }
29912
30063
  console.log(chalk2.dim(` ${result.filesChanged} files changed, ${formatBytes(result.bytesTransferred)} transferred in ${result.durationMs}ms`));
30064
+ if (sessionOptions.enabled) {
30065
+ console.log(chalk2.dim(" Sessions: enabled"));
30066
+ }
29913
30067
  } catch (e) {
29914
30068
  console.error(`Push failed: ${e.message}`);
29915
30069
  process.exit(1);
29916
30070
  }
29917
30071
  });
29918
- syncCmd.command("pull").description("Pull changes from cloud").action(async () => {
30072
+ syncCmd.command("pull").description("Pull changes from cloud").option("--with-sessions", "Include session files in sync").option("--no-sessions", "Exclude session files from sync").action(async (opts) => {
29919
30073
  const configDir = getConfigDir();
29920
30074
  if (!await configExists(configDir)) {
29921
30075
  console.error("Not initialized. Run 'ccsini init' first.");
@@ -29927,10 +30081,16 @@ function registerSyncCommands(program2) {
29927
30081
  const masterKey = await getMasterKey2(configDir);
29928
30082
  const client = await getAuthenticatedClient2(configDir);
29929
30083
  const config = await loadKeys(configDir);
30084
+ const storedSession = await loadSessionConfig(configDir);
30085
+ const sessionOptions = {
30086
+ enabled: opts.withSessions ? true : opts.sessions === false ? false : storedSession.enabled,
30087
+ maxPerProject: storedSession.maxPerProject,
30088
+ maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
30089
+ };
29930
30090
  const spinner = ora2("Pulling from cloud...").start();
29931
30091
  const result = await pullSync(client, masterKey, config.deviceName, configDir, (msg) => {
29932
30092
  spinner.text = msg;
29933
- });
30093
+ }, sessionOptions);
29934
30094
  if (result.errors.length > 0) {
29935
30095
  spinner.warn("Pull completed with errors");
29936
30096
  for (const err of result.errors) {
@@ -29940,6 +30100,9 @@ function registerSyncCommands(program2) {
29940
30100
  spinner.succeed("Pull complete");
29941
30101
  }
29942
30102
  console.log(chalk2.dim(` ${result.filesChanged} files changed, ${formatBytes(result.bytesTransferred)} transferred in ${result.durationMs}ms`));
30103
+ if (sessionOptions.enabled) {
30104
+ console.log(chalk2.dim(" Sessions: enabled"));
30105
+ }
29943
30106
  } catch (e) {
29944
30107
  console.error(`Pull failed: ${e.message}`);
29945
30108
  process.exit(1);
@@ -30157,6 +30320,95 @@ Account fully reset. Run 'ccsini init --token <token>' to start fresh.`));
30157
30320
  });
30158
30321
  }
30159
30322
 
30323
+ // src/commands/sessions.ts
30324
+ init_src();
30325
+ function formatBytes2(bytes) {
30326
+ if (bytes < 1024)
30327
+ return `${bytes} B`;
30328
+ if (bytes < 1024 * 1024)
30329
+ return `${(bytes / 1024).toFixed(1)} KB`;
30330
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
30331
+ }
30332
+ function registerSessionsCommand(program2) {
30333
+ const sessionsCmd = program2.command("sessions").description("Configure session sync (continue conversations across devices)");
30334
+ sessionsCmd.command("enable").description("Enable session sync").option("-n, --per-project <count>", "Max sessions per project", String(SESSION_SYNC_DEFAULTS.maxPerProject)).action(async (opts) => {
30335
+ const configDir = getConfigDir();
30336
+ if (!await configExists(configDir)) {
30337
+ console.error("Not initialized. Run 'ccsini init' first.");
30338
+ process.exit(1);
30339
+ }
30340
+ const maxPerProject = parseInt(opts.perProject, 10);
30341
+ if (isNaN(maxPerProject) || maxPerProject < 1) {
30342
+ console.error("--per-project must be a positive integer");
30343
+ process.exit(1);
30344
+ }
30345
+ await saveSessionConfig(configDir, {
30346
+ enabled: true,
30347
+ maxPerProject
30348
+ });
30349
+ console.log(`Session sync enabled (${maxPerProject} sessions per project)`);
30350
+ });
30351
+ sessionsCmd.command("disable").description("Disable session sync").action(async () => {
30352
+ const configDir = getConfigDir();
30353
+ if (!await configExists(configDir)) {
30354
+ console.error("Not initialized. Run 'ccsini init' first.");
30355
+ process.exit(1);
30356
+ }
30357
+ const current = await loadSessionConfig(configDir);
30358
+ await saveSessionConfig(configDir, {
30359
+ enabled: false,
30360
+ maxPerProject: current.maxPerProject
30361
+ });
30362
+ console.log("Session sync disabled");
30363
+ });
30364
+ sessionsCmd.command("status").description("Show session sync status and preview").action(async () => {
30365
+ const configDir = getConfigDir();
30366
+ if (!await configExists(configDir)) {
30367
+ console.error("Not initialized. Run 'ccsini init' first.");
30368
+ process.exit(1);
30369
+ }
30370
+ const chalk2 = (await Promise.resolve().then(() => (init_source(), exports_source))).default;
30371
+ const ora2 = (await Promise.resolve().then(() => (init_ora(), exports_ora))).default;
30372
+ const sessionConfig = await loadSessionConfig(configDir);
30373
+ console.log(chalk2.bold("Session Sync"));
30374
+ console.log(` Status: ${sessionConfig.enabled ? chalk2.green("enabled") : chalk2.yellow("disabled")}`);
30375
+ console.log(` Per project: ${sessionConfig.maxPerProject}`);
30376
+ console.log(` Max file size: ${formatBytes2(SESSION_SYNC_DEFAULTS.maxFileSize)}`);
30377
+ const spinner = ora2("Scanning session files...").start();
30378
+ try {
30379
+ const claudeDir = getClaudeDir();
30380
+ const enabledOpts = {
30381
+ enabled: true,
30382
+ maxPerProject: sessionConfig.maxPerProject,
30383
+ maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
30384
+ };
30385
+ const withSessions = await scanClaudeDir(claudeDir, undefined, enabledOpts);
30386
+ const sessionFiles = withSessions.filter((f) => isSessionFile(f.relativePath));
30387
+ const disabledOpts = {
30388
+ enabled: false,
30389
+ maxPerProject: sessionConfig.maxPerProject,
30390
+ maxFileSize: SESSION_SYNC_DEFAULTS.maxFileSize
30391
+ };
30392
+ const withoutSessions = await scanClaudeDir(claudeDir, undefined, disabledOpts);
30393
+ spinner.stop();
30394
+ const totalSessionSize = sessionFiles.reduce((sum, f) => sum + f.size, 0);
30395
+ const projects = new Set(sessionFiles.map((f) => f.relativePath.split("/").slice(0, 2).join("/")));
30396
+ console.log(chalk2.bold(`
30397
+ Preview`));
30398
+ console.log(` Projects: ${projects.size}`);
30399
+ console.log(` Sessions: ${sessionFiles.length} files`);
30400
+ console.log(` Session size: ${formatBytes2(totalSessionSize)}`);
30401
+ console.log(` Other files: ${withoutSessions.length}`);
30402
+ if (!sessionConfig.enabled) {
30403
+ console.log(chalk2.dim(`
30404
+ Run 'ccsini sessions enable' to start syncing sessions`));
30405
+ }
30406
+ } catch (e) {
30407
+ spinner.fail(`Scan failed: ${e.message}`);
30408
+ }
30409
+ });
30410
+ }
30411
+
30160
30412
  // src/index.ts
30161
30413
  var program2 = new Command;
30162
30414
  program2.name("ccsini").description("Claude Code seamless sync across devices").version(VERSION);
@@ -30167,6 +30419,7 @@ registerSelfCommands(program2);
30167
30419
  registerSyncCommands(program2);
30168
30420
  registerHooksCommands(program2);
30169
30421
  registerResetCommand(program2);
30422
+ registerSessionsCommand(program2);
30170
30423
  program2.command("version").description("Show current version").action(() => {
30171
30424
  console.log(VERSION);
30172
30425
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsini",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "Claude Code seamless sync across devices",
5
5
  "type": "module",
6
6
  "bin": {