ccsini 0.1.33 → 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 +279 -20
  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/**",
@@ -27920,8 +27929,14 @@ var init_constants = __esm(() => {
27920
27929
  "**/dist/**",
27921
27930
  "**/.git/**",
27922
27931
  "**/*.bak",
27923
- "settings.*.json"
27932
+ "settings.*.json",
27933
+ "history.jsonl"
27924
27934
  ];
27935
+ SESSION_SYNC_DEFAULTS = {
27936
+ enabled: false,
27937
+ maxPerProject: 3,
27938
+ maxFileSize: 50000000
27939
+ };
27925
27940
  });
27926
27941
 
27927
27942
  // ../shared/src/index.ts
@@ -28003,7 +28018,7 @@ var {
28003
28018
  } = import__.default;
28004
28019
 
28005
28020
  // src/version.ts
28006
- var VERSION = "0.1.33";
28021
+ var VERSION = "0.1.35";
28007
28022
 
28008
28023
  // src/commands/init.ts
28009
28024
  init_source();
@@ -28674,6 +28689,7 @@ function decryptFile(masterKey, filePath, ciphertext) {
28674
28689
  }
28675
28690
 
28676
28691
  // src/core/keystore.ts
28692
+ init_src();
28677
28693
  import { readFile, writeFile, mkdir, access } from "fs/promises";
28678
28694
  import { join } from "path";
28679
28695
  import { homedir } from "os";
@@ -28719,6 +28735,31 @@ async function loadKeys(configDir) {
28719
28735
  apiUrl: config.apiUrl
28720
28736
  };
28721
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
+ }
28722
28763
 
28723
28764
  // src/core/schema.ts
28724
28765
  init_src();
@@ -29017,6 +29058,17 @@ class CcsiniClient {
29017
29058
  if (!res.ok)
29018
29059
  throw new Error("Failed to wipe account data");
29019
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
+ }
29020
29072
  async resetAll() {
29021
29073
  const res = await fetch(`${this.apiUrl}/api/sync/reset`, {
29022
29074
  method: "DELETE",
@@ -29061,12 +29113,48 @@ function categorizeFile(relativePath) {
29061
29113
  return "unknown";
29062
29114
  }
29063
29115
  function matchGlobPattern(path2, pattern) {
29064
- const regex2 = pattern.replace(/\./g, "\\.").replace(/\*\*\//g, "(.+/)?").replace(/\*\*/g, ".*").replace(/(?<!\.)(\*)/g, "[^/]*");
29116
+ const regex2 = pattern.replace(/\*\*\//g, "\x00DIR\x00").replace(/\*\*/g, "\x00STAR\x00").replace(/\./g, "\\.").replace(/\*/g, "[^/]*").replace(/\0DIR\0/g, "(.+/)?").replace(/\0STAR\0/g, ".*");
29065
29117
  return new RegExp(`^${regex2}$`).test(path2);
29066
29118
  }
29067
29119
  function isExcluded(relativePath) {
29068
29120
  return NEVER_SYNC_PATTERNS.some((pattern) => matchGlobPattern(relativePath, pattern));
29069
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
+ }
29070
29158
  async function hashFile(filePath) {
29071
29159
  const content = await readFile4(filePath);
29072
29160
  return createHash("sha256").update(content).digest("hex");
@@ -29101,18 +29189,28 @@ async function scanDir(baseDir, currentDir, results, onProgress) {
29101
29189
  }
29102
29190
  }
29103
29191
  }
29104
- async function scanClaudeDir(claudeDir, onProgress) {
29192
+ async function scanClaudeDir(claudeDir, onProgress, sessionOptions) {
29105
29193
  const progress = onProgress ?? (() => {});
29106
29194
  const results = [];
29107
29195
  progress("Scanning local files...");
29108
29196
  await scanDir(claudeDir, claudeDir, results, progress);
29109
29197
  progress(`Scan complete: ${results.length} files found`);
29110
- 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;
29111
29209
  }
29112
29210
 
29113
29211
  // src/core/manifest.ts
29114
- async function generateManifest(claudeDir, deviceName, onProgress) {
29115
- const files = await scanClaudeDir(claudeDir, onProgress);
29212
+ async function generateManifest(claudeDir, deviceName, onProgress, sessionOptions) {
29213
+ const files = await scanClaudeDir(claudeDir, onProgress, sessionOptions);
29116
29214
  const entries = {};
29117
29215
  for (const file of files) {
29118
29216
  entries[file.relativePath] = {
@@ -29216,19 +29314,46 @@ function mergeLastWriteWins(localContent, remoteContent, localModified, remoteMo
29216
29314
 
29217
29315
  // src/core/sync.ts
29218
29316
  init_src();
29317
+ init_src();
29219
29318
  function blobKey(filePath, contentHash) {
29220
29319
  return createHash2("sha256").update(`${filePath}:${contentHash}`).digest("hex");
29221
29320
  }
29222
29321
  function getClaudeDir() {
29223
29322
  return join6(homedir2(), ".claude");
29224
29323
  }
29225
- 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) {
29226
29351
  const start = Date.now();
29227
29352
  const claudeDir = getClaudeDir();
29228
29353
  const errors2 = [];
29229
29354
  let bytesTransferred = 0;
29230
29355
  const progress = onProgress ?? (() => {});
29231
- const localManifest = await generateManifest(claudeDir, deviceName, progress);
29356
+ const localManifest = await generateManifest(claudeDir, deviceName, progress, sessionOptions);
29232
29357
  const fileCount = Object.keys(localManifest.files).length;
29233
29358
  progress(`Scanned ${fileCount} files`);
29234
29359
  progress("Fetching remote manifest...");
@@ -29291,6 +29416,12 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress) {
29291
29416
  bytesTransferred,
29292
29417
  durationMs
29293
29418
  });
29419
+ try {
29420
+ const projects = extractProjects(localManifest);
29421
+ if (projects.length > 0) {
29422
+ await client.putProjects(projects, deviceName);
29423
+ }
29424
+ } catch {}
29294
29425
  return {
29295
29426
  action: "push",
29296
29427
  filesChanged: toPush.length,
@@ -29299,7 +29430,7 @@ async function pushSync(client, masterKey, deviceName, configDir, onProgress) {
29299
29430
  errors: errors2
29300
29431
  };
29301
29432
  }
29302
- async function pullSync(client, masterKey, deviceName, configDir, onProgress) {
29433
+ async function pullSync(client, masterKey, deviceName, configDir, onProgress, sessionOptions) {
29303
29434
  const start = Date.now();
29304
29435
  const claudeDir = getClaudeDir();
29305
29436
  const errors2 = [];
@@ -29335,14 +29466,20 @@ async function pullSync(client, masterKey, deviceName, configDir, onProgress) {
29335
29466
  const localManifest = await loadManifest(configDir);
29336
29467
  progress("Comparing manifests...");
29337
29468
  const diffs = diffManifests(localManifest, remoteManifest);
29338
- 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)));
29339
29471
  progress(`${toPull.length} files to pull`);
29340
29472
  let downloaded = 0;
29341
29473
  const chunks = chunkArray(toPull, MAX_CONCURRENT_TRANSFERS);
29342
29474
  for (const chunk of chunks) {
29343
29475
  await Promise.all(chunk.map(async (diff) => {
29344
29476
  try {
29345
- const encrypted = await client.downloadBlob(blobKey(diff.path, diff.remoteHash));
29477
+ let encrypted;
29478
+ try {
29479
+ encrypted = await client.downloadBlob(blobKey(diff.path, diff.remoteHash));
29480
+ } catch {
29481
+ encrypted = await client.downloadBlob(diff.remoteHash);
29482
+ }
29346
29483
  bytesTransferred += encrypted.length;
29347
29484
  const decrypted = decryptFile(masterKey, diff.path, encrypted);
29348
29485
  if (diff.action === "merge" && diff.localHash) {
@@ -29608,6 +29745,7 @@ function detectPlatform() {
29608
29745
  // src/commands/auto.ts
29609
29746
  init_auth();
29610
29747
  init_auth();
29748
+ init_src();
29611
29749
  import { readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
29612
29750
  import { join as join8 } from "path";
29613
29751
  async function getMasterKey(configDir) {
@@ -29634,7 +29772,13 @@ function registerAutoCommands(program2) {
29634
29772
  const masterKey = await getMasterKey(configDir);
29635
29773
  const client = await getAuthenticatedClient(configDir);
29636
29774
  const config = await loadKeys(configDir);
29637
- 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);
29638
29782
  if (result.filesChanged > 0) {
29639
29783
  console.log(`[ccsini] Pulled ${result.filesChanged} files (${result.durationMs}ms)`);
29640
29784
  }
@@ -29648,7 +29792,13 @@ function registerAutoCommands(program2) {
29648
29792
  const masterKey = await getMasterKey(configDir);
29649
29793
  const client = await getAuthenticatedClient(configDir);
29650
29794
  const config = await loadKeys(configDir);
29651
- 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);
29652
29802
  if (result.filesChanged > 0) {
29653
29803
  console.log(`[ccsini] Pushed ${result.filesChanged} files (${result.durationMs}ms)`);
29654
29804
  }
@@ -29840,6 +29990,7 @@ Uninstall failed. Try manually:`);
29840
29990
 
29841
29991
  // src/commands/sync.ts
29842
29992
  init_auth();
29993
+ init_src();
29843
29994
  import { readFile as readFile8 } from "fs/promises";
29844
29995
  import { join as join10 } from "path";
29845
29996
  async function getMasterKey2(configDir) {
@@ -29879,7 +30030,7 @@ function formatBytes(bytes) {
29879
30030
  }
29880
30031
  function registerSyncCommands(program2) {
29881
30032
  const syncCmd = program2.command("sync").description("Sync management commands");
29882
- 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) => {
29883
30034
  const configDir = getConfigDir();
29884
30035
  if (!await configExists(configDir)) {
29885
30036
  console.error("Not initialized. Run 'ccsini init' first.");
@@ -29891,10 +30042,16 @@ function registerSyncCommands(program2) {
29891
30042
  const masterKey = await getMasterKey2(configDir);
29892
30043
  const client = await getAuthenticatedClient2(configDir);
29893
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
+ };
29894
30051
  const spinner = ora2("Scanning files...").start();
29895
30052
  const result = await pushSync(client, masterKey, config.deviceName, configDir, (msg) => {
29896
30053
  spinner.text = msg;
29897
- });
30054
+ }, sessionOptions);
29898
30055
  if (result.errors.length > 0) {
29899
30056
  spinner.warn("Push completed with errors");
29900
30057
  for (const err of result.errors) {
@@ -29904,12 +30061,15 @@ function registerSyncCommands(program2) {
29904
30061
  spinner.succeed("Push complete");
29905
30062
  }
29906
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
+ }
29907
30067
  } catch (e) {
29908
30068
  console.error(`Push failed: ${e.message}`);
29909
30069
  process.exit(1);
29910
30070
  }
29911
30071
  });
29912
- 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) => {
29913
30073
  const configDir = getConfigDir();
29914
30074
  if (!await configExists(configDir)) {
29915
30075
  console.error("Not initialized. Run 'ccsini init' first.");
@@ -29921,10 +30081,16 @@ function registerSyncCommands(program2) {
29921
30081
  const masterKey = await getMasterKey2(configDir);
29922
30082
  const client = await getAuthenticatedClient2(configDir);
29923
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
+ };
29924
30090
  const spinner = ora2("Pulling from cloud...").start();
29925
30091
  const result = await pullSync(client, masterKey, config.deviceName, configDir, (msg) => {
29926
30092
  spinner.text = msg;
29927
- });
30093
+ }, sessionOptions);
29928
30094
  if (result.errors.length > 0) {
29929
30095
  spinner.warn("Pull completed with errors");
29930
30096
  for (const err of result.errors) {
@@ -29934,6 +30100,9 @@ function registerSyncCommands(program2) {
29934
30100
  spinner.succeed("Pull complete");
29935
30101
  }
29936
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
+ }
29937
30106
  } catch (e) {
29938
30107
  console.error(`Pull failed: ${e.message}`);
29939
30108
  process.exit(1);
@@ -30151,6 +30320,95 @@ Account fully reset. Run 'ccsini init --token <token>' to start fresh.`));
30151
30320
  });
30152
30321
  }
30153
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
+
30154
30412
  // src/index.ts
30155
30413
  var program2 = new Command;
30156
30414
  program2.name("ccsini").description("Claude Code seamless sync across devices").version(VERSION);
@@ -30161,6 +30419,7 @@ registerSelfCommands(program2);
30161
30419
  registerSyncCommands(program2);
30162
30420
  registerHooksCommands(program2);
30163
30421
  registerResetCommand(program2);
30422
+ registerSessionsCommand(program2);
30164
30423
  program2.command("version").description("Show current version").action(() => {
30165
30424
  console.log(VERSION);
30166
30425
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsini",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "Claude Code seamless sync across devices",
5
5
  "type": "module",
6
6
  "bin": {