conare 0.5.12 → 0.5.14

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 +118 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1791,6 +1791,9 @@ __export(exports_interactive, {
1791
1791
  spinner: () => Y2,
1792
1792
  showLocalChatsCounted: () => showLocalChatsCounted,
1793
1793
  showDetectedApps: () => showDetectedApps,
1794
+ showCountingToolProgress: () => showCountingToolProgress,
1795
+ showCountingToolDone: () => showCountingToolDone,
1796
+ showCountingToolChats: () => showCountingToolChats,
1794
1797
  showCountingLocalChats: () => showCountingLocalChats,
1795
1798
  selectMcpTargets: () => selectMcpTargets,
1796
1799
  selectChatSources: () => selectChatSources,
@@ -1822,10 +1825,27 @@ function showDetectedApps(targets) {
1822
1825
  `), "Detected apps");
1823
1826
  }
1824
1827
  function showCountingLocalChats() {
1825
- M2.step("Counting local chats...");
1828
+ countingSpinner = Y2();
1829
+ countingSpinner.start("Scanning local chats");
1830
+ }
1831
+ function showCountingToolChats(tool) {
1832
+ countingSpinner?.message(`Scanning ${tool} chats`);
1833
+ }
1834
+ function showCountingToolProgress(tool, checked, found, total) {
1835
+ const checkedText = total ? `${checked.toLocaleString()}/${total.toLocaleString()}` : checked.toLocaleString();
1836
+ countingSpinner?.message(`Scanning ${tool}: ${checkedText} files, ${found.toLocaleString()} importable`);
1837
+ }
1838
+ function showCountingToolDone(tool, count) {
1839
+ toolTotals[tool] = count;
1840
+ countingSpinner?.message(`${tool}: ${count.toLocaleString()} chats`);
1826
1841
  }
1827
1842
  function showLocalChatsCounted() {
1828
- M2.success("Local chats counted");
1843
+ if (!countingSpinner)
1844
+ return;
1845
+ const entries = Object.entries(toolTotals).filter(([, n]) => n > 0);
1846
+ const summary = entries.length ? entries.map(([tool, n]) => `${tool} ${n.toLocaleString()}`).join(", ") : "no chats found";
1847
+ countingSpinner.stop(`Found ${summary}`);
1848
+ countingSpinner = null;
1829
1849
  }
1830
1850
  async function promptApiKey(options) {
1831
1851
  if (options.providedApiKey) {
@@ -1916,8 +1936,10 @@ async function confirmBackgroundSync() {
1916
1936
  }));
1917
1937
  return value === "yes";
1918
1938
  }
1939
+ var countingSpinner = null, toolTotals;
1919
1940
  var init_interactive = __esm(() => {
1920
1941
  init_dist2();
1942
+ toolTotals = {};
1921
1943
  });
1922
1944
 
1923
1945
  // src/index.ts
@@ -2049,39 +2071,58 @@ function getParentUuid(lines) {
2049
2071
  }
2050
2072
  return;
2051
2073
  }
2052
- function countImportableClaudeSessions() {
2074
+ async function countImportableClaudeSessions(onProgress) {
2053
2075
  const projectsDir = join2(homedir2(), ".claude", "projects");
2054
2076
  let count = 0;
2077
+ let checked = 0;
2078
+ let total = 0;
2055
2079
  let projectDirs;
2056
2080
  try {
2057
2081
  projectDirs = readdirSync(projectsDir);
2058
2082
  } catch {
2059
2083
  return 0;
2060
2084
  }
2061
- for (const projDir of projectDirs) {
2085
+ const projectFiles = projectDirs.map((projDir) => {
2062
2086
  const projPath = join2(projectsDir, projDir);
2063
- let files;
2064
2087
  try {
2065
- files = readdirSync(projPath).filter((f) => f.endsWith(".jsonl"));
2088
+ const files = readdirSync(projPath).filter((f) => f.endsWith(".jsonl"));
2089
+ total += files.length;
2090
+ return { projPath, files };
2066
2091
  } catch {
2067
- continue;
2092
+ return { projPath, files: [] };
2068
2093
  }
2094
+ });
2095
+ const yieldToEventLoop = () => new Promise((resolve) => setImmediate(resolve));
2096
+ const checkpoint = async () => {
2097
+ onProgress?.({ checked, found: count, total });
2098
+ await yieldToEventLoop();
2099
+ };
2100
+ for (const { projPath, files } of projectFiles) {
2069
2101
  for (const file of files) {
2102
+ checked++;
2070
2103
  try {
2071
2104
  const raw = readFileSync2(join2(projPath, file), "utf-8");
2072
2105
  const lines = raw.split(`
2073
2106
  `);
2074
2107
  const parentUuid = getParentUuid(lines);
2075
- if (parentUuid != null)
2108
+ if (parentUuid != null) {
2109
+ if (checked % 100 === 0)
2110
+ await checkpoint();
2076
2111
  continue;
2112
+ }
2077
2113
  const { turns } = parseSession(lines);
2078
2114
  if (turns.length > 0)
2079
2115
  count++;
2080
2116
  } catch {
2117
+ if (checked % 100 === 0)
2118
+ await checkpoint();
2081
2119
  continue;
2082
2120
  }
2121
+ if (checked % 100 === 0)
2122
+ await checkpoint();
2083
2123
  }
2084
2124
  }
2125
+ onProgress?.({ checked, found: count, total });
2085
2126
  return count;
2086
2127
  }
2087
2128
  function ingestClaude() {
@@ -2274,12 +2315,20 @@ function walkCodexSessionFiles(dir, visit) {
2274
2315
  }
2275
2316
  } catch {}
2276
2317
  }
2277
- function countImportableCodexSessions() {
2318
+ async function countImportableCodexSessions(onProgress) {
2278
2319
  const sessionsDir = join3(homedir3(), ".codex", "sessions");
2279
2320
  if (!existsSync3(sessionsDir))
2280
2321
  return 0;
2281
- let count = 0;
2322
+ const files = [];
2282
2323
  walkCodexSessionFiles(sessionsDir, (filePath) => {
2324
+ files.push(filePath);
2325
+ });
2326
+ const total = files.length;
2327
+ let count = 0;
2328
+ let checked = 0;
2329
+ const yieldToEventLoop = () => new Promise((resolve) => setImmediate(resolve));
2330
+ for (const filePath of files) {
2331
+ checked++;
2283
2332
  try {
2284
2333
  const lines = readFileSync3(filePath, "utf-8").split(`
2285
2334
  `).filter(Boolean);
@@ -2287,7 +2336,12 @@ function countImportableCodexSessions() {
2287
2336
  if (rounds.length > 0)
2288
2337
  count++;
2289
2338
  } catch {}
2290
- });
2339
+ if (checked % 100 === 0) {
2340
+ onProgress?.({ checked, found: count, total });
2341
+ await yieldToEventLoop();
2342
+ }
2343
+ }
2344
+ onProgress?.({ checked, found: count, total });
2291
2345
  return count;
2292
2346
  }
2293
2347
  function walkCodexSessions(dir, memories, sessionIds, stats) {
@@ -2580,7 +2634,7 @@ async function openCursorKvDb(dbPath, fileSize, wasmDir) {
2580
2634
  function formatMiB(bytes) {
2581
2635
  return (bytes / 1024 / 1024).toFixed(0);
2582
2636
  }
2583
- async function countImportableCursorSessions(dbPath, wasmDir) {
2637
+ async function countImportableCursorSessions(dbPath, wasmDir, onProgress) {
2584
2638
  let fileSize;
2585
2639
  try {
2586
2640
  fileSize = statSync(dbPath).size;
@@ -2594,22 +2648,36 @@ async function countImportableCursorSessions(dbPath, wasmDir) {
2594
2648
  try {
2595
2649
  const rows = db.selectComposerData();
2596
2650
  let count = 0;
2651
+ let checked = 0;
2652
+ const total = rows.length;
2653
+ const report = () => {
2654
+ if (checked % 50 === 0 || checked === total) {
2655
+ onProgress?.({ checked, found: count, total });
2656
+ }
2657
+ };
2597
2658
  for (const { key, value } of rows) {
2659
+ checked++;
2598
2660
  const composerId = key.replace("composerData:", "");
2599
2661
  let parsed;
2600
2662
  try {
2601
2663
  parsed = JSON.parse(value);
2602
- if (!parsed || typeof parsed !== "object")
2664
+ if (!parsed || typeof parsed !== "object") {
2665
+ report();
2603
2666
  continue;
2667
+ }
2604
2668
  } catch {
2669
+ report();
2605
2670
  continue;
2606
2671
  }
2607
2672
  const bubbleHeaders = parsed.fullConversationHeadersOnly;
2608
- if (!Array.isArray(bubbleHeaders) || bubbleHeaders.length === 0)
2673
+ if (!Array.isArray(bubbleHeaders) || bubbleHeaders.length === 0) {
2674
+ report();
2609
2675
  continue;
2676
+ }
2610
2677
  const turns = extractTurns(db, composerId, bubbleHeaders);
2611
2678
  if (turns.length > 0)
2612
2679
  count++;
2680
+ report();
2613
2681
  }
2614
2682
  return count;
2615
2683
  } finally {
@@ -2742,23 +2810,34 @@ async function ingestCursor(dbPath, wasmDir) {
2742
2810
  }
2743
2811
 
2744
2812
  // src/detect.ts
2745
- async function detect() {
2813
+ async function detect(options = {}) {
2746
2814
  const home = homedir5();
2747
2815
  const os = platform3();
2748
2816
  const tools = [];
2817
+ const onProgress = options.onProgress;
2749
2818
  const claudeDir = join5(home, ".claude", "projects");
2750
2819
  if (existsSync5(claudeDir)) {
2751
- const sessionCount = countImportableClaudeSessions();
2820
+ onProgress?.({ tool: "Claude Code", status: "start" });
2821
+ const sessionCount = await countImportableClaudeSessions((progress) => {
2822
+ onProgress?.({ tool: "Claude Code", status: "progress", ...progress });
2823
+ });
2824
+ onProgress?.({ tool: "Claude Code", status: "done", count: sessionCount });
2752
2825
  tools.push({ name: "Claude Code", id: "claude", available: sessionCount > 0, path: claudeDir, sessionCount, sessionCountApproximate: true });
2753
2826
  } else {
2827
+ onProgress?.({ tool: "Claude Code", status: "skip", reason: "not found" });
2754
2828
  tools.push({ name: "Claude Code", id: "claude", available: false, path: claudeDir, sessionCount: 0 });
2755
2829
  }
2756
2830
  const codexConfig = join5(home, ".codex", "config.toml");
2757
2831
  const codexSessions = join5(home, ".codex", "sessions");
2758
2832
  if (existsSync5(codexConfig) || existsSync5(codexSessions)) {
2759
- const sessionCount = countImportableCodexSessions();
2833
+ onProgress?.({ tool: "Codex", status: "start" });
2834
+ const sessionCount = await countImportableCodexSessions((progress) => {
2835
+ onProgress?.({ tool: "Codex", status: "progress", ...progress });
2836
+ });
2837
+ onProgress?.({ tool: "Codex", status: "done", count: sessionCount });
2760
2838
  tools.push({ name: "Codex", id: "codex", available: true, path: codexConfig, sessionCount, sessionCountApproximate: true });
2761
2839
  } else {
2840
+ onProgress?.({ tool: "Codex", status: "skip", reason: "not found" });
2762
2841
  tools.push({ name: "Codex", id: "codex", available: false, path: codexConfig, sessionCount: 0 });
2763
2842
  }
2764
2843
  let cursorDbPath;
@@ -2769,12 +2848,22 @@ async function detect() {
2769
2848
  } else {
2770
2849
  cursorDbPath = join5(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
2771
2850
  }
2851
+ const cursorExists = existsSync5(cursorDbPath);
2852
+ if (cursorExists)
2853
+ onProgress?.({ tool: "Cursor", status: "start" });
2854
+ else
2855
+ onProgress?.({ tool: "Cursor", status: "skip", reason: "not found" });
2856
+ const cursorCount = cursorExists ? await countImportableCursorSessions(cursorDbPath, undefined, (progress) => {
2857
+ onProgress?.({ tool: "Cursor", status: "progress", ...progress });
2858
+ }) : 0;
2859
+ if (cursorExists)
2860
+ onProgress?.({ tool: "Cursor", status: "done", count: cursorCount });
2772
2861
  tools.push({
2773
2862
  name: "Cursor",
2774
2863
  id: "cursor",
2775
- available: existsSync5(cursorDbPath),
2864
+ available: cursorExists,
2776
2865
  path: cursorDbPath,
2777
- sessionCount: existsSync5(cursorDbPath) ? await countImportableCursorSessions(cursorDbPath) : 0,
2866
+ sessionCount: cursorCount,
2778
2867
  sessionCountApproximate: true
2779
2868
  });
2780
2869
  const windsurfDir = join5(home, ".codeium", "windsurf");
@@ -3928,6 +4017,15 @@ function renderSourceBreakdown(memories) {
3928
4017
  function formatSessionCount(count, approximate) {
3929
4018
  return `${approximate ? "~" : ""}${count} sessions`;
3930
4019
  }
4020
+ function renderDetectProgress(event) {
4021
+ if (event.status === "start") {
4022
+ showCountingToolChats(event.tool);
4023
+ } else if (event.status === "progress" && event.checked !== undefined && event.found !== undefined) {
4024
+ showCountingToolProgress(event.tool, event.checked, event.found, event.total);
4025
+ } else if (event.status === "done") {
4026
+ showCountingToolDone(event.tool, event.count ?? 0);
4027
+ }
4028
+ }
3931
4029
  function parseArgs() {
3932
4030
  const args = process.argv.slice(2);
3933
4031
  let key = "";
@@ -4161,7 +4259,7 @@ async function main() {
4161
4259
  }
4162
4260
  }
4163
4261
  showCountingLocalChats();
4164
- const detectedTools = await detect();
4262
+ const detectedTools = await detect({ onProgress: renderDetectProgress });
4165
4263
  showLocalChatsCounted();
4166
4264
  interactiveTargets = MCP_TARGETS.map((target) => {
4167
4265
  const detected = detectedTools.find((tool) => tool.id === target.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conare",
3
- "version": "0.5.12",
3
+ "version": "0.5.14",
4
4
  "description": "Conare CLI for indexing AI chat history and configuring memory at conare.ai",
5
5
  "type": "module",
6
6
  "bin": {