conare 0.0.6 → 0.0.8

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 +144 -129
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -153,6 +153,7 @@ import { join as join9 } from "node:path";
153
153
 
154
154
  // src/detect.ts
155
155
  import { existsSync, readdirSync } from "node:fs";
156
+ import { spawnSync } from "node:child_process";
156
157
  import { join } from "node:path";
157
158
  import { homedir, platform } from "node:os";
158
159
  function countJsonlFiles(dir) {
@@ -168,6 +169,49 @@ function countJsonlFiles(dir) {
168
169
  } catch {}
169
170
  return count;
170
171
  }
172
+ function countCursorSessions(dbPath) {
173
+ try {
174
+ const result = spawnSync("sqlite3", [
175
+ dbPath,
176
+ `
177
+ WITH composer_rows AS (
178
+ SELECT substr(key, 14) AS composer_id, value AS composer_json
179
+ FROM cursorDiskKV
180
+ WHERE key LIKE 'composerData:%'
181
+ ),
182
+ headers AS (
183
+ SELECT
184
+ composer_id,
185
+ json_extract(j.value, '$.bubbleId') AS bubble_id,
186
+ json_extract(j.value, '$.type') AS type
187
+ FROM composer_rows, json_each(json_extract(composer_json, '$.fullConversationHeadersOnly')) AS j
188
+ ),
189
+ messages AS (
190
+ SELECT h.composer_id, h.type
191
+ FROM headers h
192
+ JOIN cursorDiskKV kv
193
+ ON kv.key = 'bubbleId:' || h.composer_id || ':' || h.bubble_id
194
+ WHERE h.type IN (1, 2)
195
+ AND length(COALESCE(json_extract(kv.value, '$.text'), '')) >= 50
196
+ )
197
+ SELECT count(*)
198
+ FROM (
199
+ SELECT composer_id
200
+ FROM messages
201
+ GROUP BY composer_id
202
+ HAVING SUM(CASE WHEN type = 1 THEN 1 ELSE 0 END) > 0
203
+ AND SUM(CASE WHEN type = 2 THEN 1 ELSE 0 END) > 0
204
+ );
205
+ `.trim()
206
+ ], { encoding: "utf-8" });
207
+ if (result.status !== 0)
208
+ return 0;
209
+ const count = Number.parseInt(result.stdout.trim(), 10);
210
+ return Number.isFinite(count) ? count : 0;
211
+ } catch {
212
+ return 0;
213
+ }
214
+ }
171
215
  function detect() {
172
216
  const home = homedir();
173
217
  const tools = [];
@@ -225,7 +269,7 @@ function detect() {
225
269
  name: "Cursor",
226
270
  available: existsSync(cursorDbPath),
227
271
  path: cursorDbPath,
228
- sessionCount: 0
272
+ sessionCount: existsSync(cursorDbPath) ? countCursorSessions(cursorDbPath) : 0
229
273
  });
230
274
  return tools;
231
275
  }
@@ -1029,7 +1073,7 @@ async function uploadBulk(apiKey, memories, onProgress) {
1029
1073
  import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
1030
1074
  import { dirname, join as join7 } from "node:path";
1031
1075
  import { homedir as homedir5 } from "node:os";
1032
- import { spawnSync } from "node:child_process";
1076
+ import { spawnSync as spawnSync2 } from "node:child_process";
1033
1077
  var CONARE_URL = "https://mcp.conare.ai";
1034
1078
  var SERVER_NAME = "conare-memory";
1035
1079
  var MCP_TARGETS = [
@@ -1069,7 +1113,7 @@ function upsertMcpServer(path, apiKey) {
1069
1113
  function configureClaude(apiKey) {
1070
1114
  const claudeConfigPath = join7(homedir5(), ".claude.json");
1071
1115
  const claudeMcpPath = join7(homedir5(), ".claude", "mcp.json");
1072
- if (spawnSync("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
1116
+ if (spawnSync2("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
1073
1117
  stdio: "ignore"
1074
1118
  }).status === 0) {
1075
1119
  return "Claude Code configured via `claude mcp add-json`";
@@ -1538,24 +1582,6 @@ class MD extends x {
1538
1582
  return this.value.replaceAll(/./g, this._mask);
1539
1583
  }
1540
1584
  }
1541
- class RD extends x {
1542
- get valueWithCursor() {
1543
- if (this.state === "submit")
1544
- return this.value;
1545
- if (this.cursor >= this.value.length)
1546
- return `${this.value}█`;
1547
- const u = this.value.slice(0, this.cursor), [t, ...F] = this.value.slice(this.cursor);
1548
- return `${u}${import_picocolors.default.inverse(t)}${F.join("")}`;
1549
- }
1550
- get cursor() {
1551
- return this._cursor;
1552
- }
1553
- constructor(u) {
1554
- super(u), this.on("finalize", () => {
1555
- this.value || (this.value = u.defaultValue);
1556
- });
1557
- }
1558
- }
1559
1585
 
1560
1586
  // node_modules/@clack/prompts/dist/index.mjs
1561
1587
  var import_picocolors2 = __toESM(require_picocolors(), 1);
@@ -1610,27 +1636,6 @@ var G2 = (t) => {
1610
1636
  return j2 || E ? import_picocolors2.default.dim("...") : i(p2, v2 + l2 === n);
1611
1637
  });
1612
1638
  };
1613
- var he = (t) => new RD({ validate: t.validate, placeholder: t.placeholder, defaultValue: t.defaultValue, initialValue: t.initialValue, render() {
1614
- const n = `${import_picocolors2.default.gray(o)}
1615
- ${b2(this.state)} ${t.message}
1616
- `, r2 = t.placeholder ? import_picocolors2.default.inverse(t.placeholder[0]) + import_picocolors2.default.dim(t.placeholder.slice(1)) : import_picocolors2.default.inverse(import_picocolors2.default.hidden("_")), i = this.value ? this.valueWithCursor : r2;
1617
- switch (this.state) {
1618
- case "error":
1619
- return `${n.trim()}
1620
- ${import_picocolors2.default.yellow(o)} ${i}
1621
- ${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(this.error)}
1622
- `;
1623
- case "submit":
1624
- return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(this.value || t.placeholder)}`;
1625
- case "cancel":
1626
- return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(this.value ?? ""))}${this.value?.trim() ? `
1627
- ${import_picocolors2.default.gray(o)}` : ""}`;
1628
- default:
1629
- return `${n}${import_picocolors2.default.cyan(o)} ${i}
1630
- ${import_picocolors2.default.cyan(d2)}
1631
- `;
1632
- }
1633
- } }).prompt();
1634
1639
  var ge = (t) => new MD({ validate: t.validate, mask: t.mask ?? $e, render() {
1635
1640
  const n = `${import_picocolors2.default.gray(o)}
1636
1641
  ${b2(this.state)} ${t.message}
@@ -1746,9 +1751,9 @@ var J = `${import_picocolors2.default.gray(o)} `;
1746
1751
 
1747
1752
  // src/interactive.ts
1748
1753
  function formatDetectedCount(count) {
1749
- if (!count || count <= 0)
1754
+ if (count === undefined)
1750
1755
  return "available";
1751
- return `${count.toLocaleString()} found`;
1756
+ return `${count.toLocaleString()} chats`;
1752
1757
  }
1753
1758
  function ensureValue(value) {
1754
1759
  if (pD(value)) {
@@ -1757,74 +1762,63 @@ function ensureValue(value) {
1757
1762
  }
1758
1763
  return value;
1759
1764
  }
1760
- async function runInteractiveSetup(options) {
1765
+ function startSetup() {
1761
1766
  Ie("Conare setup");
1762
- let apiKey = options.providedApiKey;
1763
- if (!apiKey) {
1764
- const keyPrompt = await ge({
1765
- message: options.savedApiKey ? `API key (press Enter to use saved key ending in ${options.savedApiKey.slice(-6)})` : "API key",
1766
- mask: "*",
1767
- validate(value) {
1768
- const resolved = value.trim() || options.savedApiKey || "";
1769
- if (!resolved)
1770
- return "Enter an API key from https://mcp.conare.ai";
1771
- if (!resolved.startsWith("cmem_"))
1772
- return "API keys start with cmem_";
1773
- return;
1774
- }
1775
- });
1776
- apiKey = ensureValue(keyPrompt).trim() || options.savedApiKey;
1777
- }
1778
- Me(options.detectedTargets.map((target) => `• ${target.label}: ${target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)}`).join(`
1767
+ }
1768
+ function finishSetup() {
1769
+ Se("Starting setup...");
1770
+ }
1771
+ function showDetectedApps(targets) {
1772
+ Me(targets.map((target) => `• ${target.label}: ${target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)}`).join(`
1779
1773
  `), "Detected apps");
1780
- const ingestChats = ensureValue(await ye({
1781
- message: "Import past chats?",
1782
- initialValue: true
1783
- }));
1784
- let ingestTargets = [];
1785
- if (ingestChats) {
1786
- ingestTargets = ensureValue(await fe({
1787
- message: "Select chat sources",
1788
- required: false,
1789
- initialValues: options.detectedTargets.filter((target) => target.recommended).map((target) => target.id),
1790
- options: options.detectedTargets.map((target) => ({
1791
- value: target.id,
1792
- label: target.label,
1793
- hint: target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)
1794
- }))
1795
- }));
1796
- }
1797
- const indexCodebase2 = ensureValue(await ye({
1798
- message: "Index this codebase too?",
1799
- initialValue: false
1774
+ }
1775
+ async function promptApiKey(options) {
1776
+ if (options.providedApiKey) {
1777
+ return options.providedApiKey;
1778
+ }
1779
+ const keyPrompt = await ge({
1780
+ message: options.savedApiKey ? `API key (press Enter to use saved key ending in ${options.savedApiKey.slice(-6)})` : "API key",
1781
+ mask: "*",
1782
+ validate(value) {
1783
+ const resolved = value.trim() || options.savedApiKey || "";
1784
+ if (!resolved)
1785
+ return "Enter an API key from https://mcp.conare.ai";
1786
+ if (!resolved.startsWith("cmem_"))
1787
+ return "API keys start with cmem_";
1788
+ return;
1789
+ }
1790
+ });
1791
+ return ensureValue(keyPrompt).trim() || options.savedApiKey;
1792
+ }
1793
+ async function selectChatSources(targets) {
1794
+ return ensureValue(await fe({
1795
+ message: "Select chat sources",
1796
+ required: false,
1797
+ initialValues: targets.filter((target) => target.recommended).map((target) => target.id),
1798
+ options: targets.map((target) => ({
1799
+ value: target.id,
1800
+ label: target.label,
1801
+ hint: target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)
1802
+ }))
1800
1803
  }));
1801
- let indexPath;
1802
- if (indexCodebase2) {
1803
- const pathPrompt = await he({
1804
- message: "Project path",
1805
- placeholder: "."
1806
- });
1807
- indexPath = ensureValue(pathPrompt).trim() || ".";
1808
- }
1809
- const configureTargets = ensureValue(await fe({
1804
+ }
1805
+ async function selectMcpTargets(targets) {
1806
+ return ensureValue(await fe({
1810
1807
  message: "Select where to install the MCP",
1811
1808
  required: false,
1812
- initialValues: options.detectedTargets.filter((target) => target.recommended).map((target) => target.id),
1813
- options: options.detectedTargets.map((target) => ({
1809
+ initialValues: targets.filter((target) => target.recommended).map((target) => target.id),
1810
+ options: targets.map((target) => ({
1814
1811
  value: target.id,
1815
1812
  label: target.label,
1816
1813
  hint: target.available === false ? "not detected" : "recommended"
1817
1814
  }))
1818
1815
  }));
1819
- Se("Starting setup...");
1820
- return {
1821
- apiKey,
1822
- configureTargets,
1823
- ingestTargets,
1824
- ingestChats: ingestChats && ingestTargets.length > 0,
1825
- indexCodebase: indexCodebase2,
1826
- indexPath
1827
- };
1816
+ }
1817
+ async function confirmIndexCodebase() {
1818
+ return ensureValue(await ye({
1819
+ message: "Index this codebase too?",
1820
+ initialValue: false
1821
+ }));
1828
1822
  }
1829
1823
 
1830
1824
  // src/index.ts
@@ -1936,29 +1930,34 @@ async function main() {
1936
1930
  let postIngestIndexPath;
1937
1931
  let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex"];
1938
1932
  let apiKey = opts.key || process.env.CONARE_API_KEY || savedApiKey;
1933
+ let interactiveTargets = MCP_TARGETS.map((target) => ({
1934
+ id: target.id,
1935
+ label: target.label,
1936
+ available: true,
1937
+ recommended: true,
1938
+ detectedCount: undefined
1939
+ }));
1940
+ let interactiveMode = false;
1939
1941
  if (shouldRunInteractive) {
1940
1942
  const detectedTools = detect();
1941
- const answers = await runInteractiveSetup({
1942
- savedApiKey,
1943
- providedApiKey: opts.key,
1944
- detectedTargets: MCP_TARGETS.map((target) => {
1945
- const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex");
1946
- return {
1947
- id: target.id,
1948
- label: target.label,
1949
- available: detected?.available,
1950
- recommended: detected?.available !== false,
1951
- detectedCount: detected?.sessionCount
1952
- };
1953
- })
1943
+ interactiveTargets = MCP_TARGETS.map((target) => {
1944
+ const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex");
1945
+ return {
1946
+ id: target.id,
1947
+ label: target.label,
1948
+ available: detected?.available,
1949
+ recommended: detected?.available !== false,
1950
+ detectedCount: detected?.sessionCount
1951
+ };
1954
1952
  });
1955
- apiKey = answers.apiKey || apiKey;
1956
- selectedTargets = answers.configureTargets.length > 0 ? answers.configureTargets : [];
1957
- selectedSources = answers.ingestTargets;
1958
- effectiveConfigOnly = !answers.ingestChats && !answers.indexCodebase;
1959
- effectiveIngestOnly = answers.ingestChats && answers.configureTargets.length === 0;
1960
- effectiveIndexPath = !answers.ingestChats && answers.indexCodebase ? answers.indexPath || "." : undefined;
1961
- postIngestIndexPath = answers.ingestChats && answers.indexCodebase ? answers.indexPath || "." : undefined;
1953
+ startSetup();
1954
+ apiKey = await promptApiKey({
1955
+ savedApiKey,
1956
+ providedApiKey: opts.key
1957
+ }) || apiKey;
1958
+ showDetectedApps(interactiveTargets);
1959
+ selectedSources = await selectChatSources(interactiveTargets);
1960
+ interactiveMode = true;
1962
1961
  }
1963
1962
  if (!apiKey) {
1964
1963
  printMissingKeyError();
@@ -2065,15 +2064,17 @@ Nothing new to index.`);
2065
2064
  console.log();
2066
2065
  }
2067
2066
  const tools = detect();
2068
- console.log("Detected AI tools:");
2069
- for (const t of tools) {
2070
- if (t.available) {
2071
- console.log(` + ${t.name}${t.sessionCount ? ` — ${t.sessionCount} sessions` : ""}`);
2072
- } else {
2073
- console.log(` - ${t.name} (not found)`);
2067
+ if (!interactiveMode) {
2068
+ console.log("Detected AI tools:");
2069
+ for (const t of tools) {
2070
+ if (t.available) {
2071
+ console.log(` + ${t.name}${t.sessionCount || t.sessionCount === 0 ? ` — ${t.sessionCount} sessions` : ""}`);
2072
+ } else {
2073
+ console.log(` - ${t.name} (not found)`);
2074
+ }
2074
2075
  }
2076
+ console.log();
2075
2077
  }
2076
- console.log();
2077
2078
  const allMemories = [];
2078
2079
  const shouldIngest = (name) => selectedSources.includes(name);
2079
2080
  if (shouldIngest("claude") && tools.find((t) => t.name === "Claude Code")?.available) {
@@ -2149,6 +2150,20 @@ Nothing new to index.`);
2149
2150
  }
2150
2151
  }
2151
2152
  console.log();
2153
+ if (interactiveMode) {
2154
+ selectedTargets = await selectMcpTargets(interactiveTargets);
2155
+ const shouldIndexCurrentCodebase = await confirmIndexCodebase();
2156
+ if (selectedTargets.length === 0 && selectedSources.length === 0 && !shouldIndexCurrentCodebase) {
2157
+ effectiveConfigOnly = false;
2158
+ effectiveIngestOnly = false;
2159
+ } else {
2160
+ effectiveIngestOnly = selectedTargets.length === 0;
2161
+ }
2162
+ if (shouldIndexCurrentCodebase) {
2163
+ postIngestIndexPath = ".";
2164
+ }
2165
+ finishSetup();
2166
+ }
2152
2167
  if (!opts.dryRun && !effectiveIngestOnly) {
2153
2168
  console.log("─── Configuring MCP ───");
2154
2169
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conare",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Conare CLI for ingesting AI chat history and configuring memory at conare.ai",
5
5
  "type": "module",
6
6
  "bin": {