conare 0.5.14 → 0.5.16

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 +83 -55
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1836,15 +1836,10 @@ function showCountingToolProgress(tool, checked, found, total) {
1836
1836
  countingSpinner?.message(`Scanning ${tool}: ${checkedText} files, ${found.toLocaleString()} importable`);
1837
1837
  }
1838
1838
  function showCountingToolDone(tool, count) {
1839
- toolTotals[tool] = count;
1840
1839
  countingSpinner?.message(`${tool}: ${count.toLocaleString()} chats`);
1841
1840
  }
1842
1841
  function showLocalChatsCounted() {
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}`);
1842
+ countingSpinner?.stop("Scan complete");
1848
1843
  countingSpinner = null;
1849
1844
  }
1850
1845
  async function promptApiKey(options) {
@@ -1936,10 +1931,9 @@ async function confirmBackgroundSync() {
1936
1931
  }));
1937
1932
  return value === "yes";
1938
1933
  }
1939
- var countingSpinner = null, toolTotals;
1934
+ var countingSpinner = null;
1940
1935
  var init_interactive = __esm(() => {
1941
1936
  init_dist2();
1942
- toolTotals = {};
1943
1937
  });
1944
1938
 
1945
1939
  // src/index.ts
@@ -2055,6 +2049,42 @@ function parseSession(lines) {
2055
2049
  })).filter((t) => t.assistant.length >= MIN_TURN_LEN);
2056
2050
  return { turns, date, sourceTimestamp, startedAt, updatedAt };
2057
2051
  }
2052
+ function hasAnyImportableTurn(lines) {
2053
+ let currentUser = null;
2054
+ let currentAssistant = [];
2055
+ const closeRound = () => {
2056
+ if (currentUser === null || currentAssistant.length === 0)
2057
+ return false;
2058
+ const assistant = currentAssistant.filter((p) => !isNarration(p)).join(`
2059
+
2060
+ `);
2061
+ return assistant.length >= MIN_TURN_LEN;
2062
+ };
2063
+ for (const line of lines) {
2064
+ if (!line.trim())
2065
+ continue;
2066
+ let obj;
2067
+ try {
2068
+ obj = JSON.parse(line);
2069
+ } catch {
2070
+ continue;
2071
+ }
2072
+ if (obj.type === "user") {
2073
+ const text = cleanText(extractText(obj.message?.content));
2074
+ if (text.length >= MIN_TURN_LEN) {
2075
+ if (closeRound())
2076
+ return true;
2077
+ currentUser = text;
2078
+ currentAssistant = [];
2079
+ }
2080
+ } else if (obj.type === "assistant") {
2081
+ const text = cleanText(extractText(obj.message?.content));
2082
+ if (text.length >= MIN_TURN_LEN)
2083
+ currentAssistant.push(text);
2084
+ }
2085
+ }
2086
+ return closeRound();
2087
+ }
2058
2088
  function getParentUuid(lines) {
2059
2089
  for (const line of lines) {
2060
2090
  if (!line.trim())
@@ -2072,56 +2102,48 @@ function getParentUuid(lines) {
2072
2102
  return;
2073
2103
  }
2074
2104
  async function countImportableClaudeSessions(onProgress) {
2105
+ const { readFile } = await import("node:fs/promises");
2075
2106
  const projectsDir = join2(homedir2(), ".claude", "projects");
2076
- let count = 0;
2077
- let checked = 0;
2078
- let total = 0;
2079
2107
  let projectDirs;
2080
2108
  try {
2081
2109
  projectDirs = readdirSync(projectsDir);
2082
2110
  } catch {
2083
2111
  return 0;
2084
2112
  }
2085
- const projectFiles = projectDirs.map((projDir) => {
2113
+ const allFiles = [];
2114
+ for (const projDir of projectDirs) {
2086
2115
  const projPath = join2(projectsDir, projDir);
2087
2116
  try {
2088
- const files = readdirSync(projPath).filter((f) => f.endsWith(".jsonl"));
2089
- total += files.length;
2090
- return { projPath, files };
2091
- } catch {
2092
- return { projPath, files: [] };
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) {
2101
- for (const file of files) {
2102
- checked++;
2117
+ for (const f of readdirSync(projPath)) {
2118
+ if (f.endsWith(".jsonl"))
2119
+ allFiles.push(join2(projPath, f));
2120
+ }
2121
+ } catch {}
2122
+ }
2123
+ const total = allFiles.length;
2124
+ let count = 0;
2125
+ let checked = 0;
2126
+ const CONCURRENCY = 16;
2127
+ let nextIdx = 0;
2128
+ const worker = async () => {
2129
+ while (true) {
2130
+ const i = nextIdx++;
2131
+ if (i >= allFiles.length)
2132
+ return;
2103
2133
  try {
2104
- const raw = readFileSync2(join2(projPath, file), "utf-8");
2134
+ const raw = await readFile(allFiles[i], "utf-8");
2105
2135
  const lines = raw.split(`
2106
2136
  `);
2107
2137
  const parentUuid = getParentUuid(lines);
2108
- if (parentUuid != null) {
2109
- if (checked % 100 === 0)
2110
- await checkpoint();
2111
- continue;
2112
- }
2113
- const { turns } = parseSession(lines);
2114
- if (turns.length > 0)
2138
+ if (parentUuid == null && hasAnyImportableTurn(lines))
2115
2139
  count++;
2116
- } catch {
2117
- if (checked % 100 === 0)
2118
- await checkpoint();
2119
- continue;
2120
- }
2140
+ } catch {}
2141
+ checked++;
2121
2142
  if (checked % 100 === 0)
2122
- await checkpoint();
2143
+ onProgress?.({ checked, found: count, total });
2123
2144
  }
2124
- }
2145
+ };
2146
+ await Promise.all(Array.from({ length: CONCURRENCY }, () => worker()));
2125
2147
  onProgress?.({ checked, found: count, total });
2126
2148
  return count;
2127
2149
  }
@@ -2319,6 +2341,7 @@ async function countImportableCodexSessions(onProgress) {
2319
2341
  const sessionsDir = join3(homedir3(), ".codex", "sessions");
2320
2342
  if (!existsSync3(sessionsDir))
2321
2343
  return 0;
2344
+ const { readFile } = await import("node:fs/promises");
2322
2345
  const files = [];
2323
2346
  walkCodexSessionFiles(sessionsDir, (filePath) => {
2324
2347
  files.push(filePath);
@@ -2326,21 +2349,26 @@ async function countImportableCodexSessions(onProgress) {
2326
2349
  const total = files.length;
2327
2350
  let count = 0;
2328
2351
  let checked = 0;
2329
- const yieldToEventLoop = () => new Promise((resolve) => setImmediate(resolve));
2330
- for (const filePath of files) {
2331
- checked++;
2332
- try {
2333
- const lines = readFileSync3(filePath, "utf-8").split(`
2352
+ const CONCURRENCY = 16;
2353
+ let nextIdx = 0;
2354
+ const worker = async () => {
2355
+ while (true) {
2356
+ const i = nextIdx++;
2357
+ if (i >= files.length)
2358
+ return;
2359
+ try {
2360
+ const lines = (await readFile(files[i], "utf-8")).split(`
2334
2361
  `).filter(Boolean);
2335
- const { rounds } = parseCodexSession(lines);
2336
- if (rounds.length > 0)
2337
- count++;
2338
- } catch {}
2339
- if (checked % 100 === 0) {
2340
- onProgress?.({ checked, found: count, total });
2341
- await yieldToEventLoop();
2362
+ const { rounds } = parseCodexSession(lines);
2363
+ if (rounds.length > 0)
2364
+ count++;
2365
+ } catch {}
2366
+ checked++;
2367
+ if (checked % 100 === 0)
2368
+ onProgress?.({ checked, found: count, total });
2342
2369
  }
2343
- }
2370
+ };
2371
+ await Promise.all(Array.from({ length: CONCURRENCY }, () => worker()));
2344
2372
  onProgress?.({ checked, found: count, total });
2345
2373
  return count;
2346
2374
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conare",
3
- "version": "0.5.14",
3
+ "version": "0.5.16",
4
4
  "description": "Conare CLI for indexing AI chat history and configuring memory at conare.ai",
5
5
  "type": "module",
6
6
  "bin": {