conare 0.6.0 → 0.6.2

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 +704 -173
  2. package/package.json +5 -2
package/dist/index.js CHANGED
@@ -60,18 +60,18 @@ function conareDir() {
60
60
  function globalManifestPath() {
61
61
  return join(conareDir(), "ingested.json");
62
62
  }
63
- function scopedManifestPath(userId) {
64
- const slug = createHash("sha256").update(userId).digest("hex").slice(0, 16);
63
+ function scopedManifestPath(scope) {
64
+ const slug = createHash("sha256").update(scope).digest("hex").slice(0, 16);
65
65
  return join(conareDir(), `ingested.${slug}.json`);
66
66
  }
67
67
  function manifestPath() {
68
- return activeUserId ? scopedManifestPath(activeUserId) : globalManifestPath();
68
+ return activeScope ? scopedManifestPath(activeScope) : globalManifestPath();
69
69
  }
70
- function setManifestAccount(userId) {
71
- activeUserId = userId || null;
72
- if (!userId)
70
+ function setManifestScope(userId, apiKey) {
71
+ activeScope = userId ? `${userId}:${apiKey}` : null;
72
+ if (!activeScope)
73
73
  return;
74
- const scoped = scopedManifestPath(userId);
74
+ const scoped = scopedManifestPath(activeScope);
75
75
  if (!existsSync(scoped) && existsSync(globalManifestPath())) {
76
76
  try {
77
77
  if (!existsSync(conareDir()))
@@ -240,7 +240,7 @@ function clearIngested(source) {
240
240
  }
241
241
  writeManifest(manifest);
242
242
  }
243
- var activeUserId = null, MAX_MEMORY_CONTENT = 200000, TRUNCATED_USER_MSG = 3000, TRUNCATED_MARKER = `
243
+ var activeScope = null, MAX_MEMORY_CONTENT = 200000, TRUNCATED_USER_MSG = 3000, TRUNCATED_MARKER = `
244
244
 
245
245
  ...[truncated to fit Conare upload limit]`, MIN_SUBSTANTIVE = 200, NARRATION_RE, remoteCache, CASE_INSENSITIVE_FS;
246
246
  var init_shared = __esm(() => {
@@ -258,16 +258,16 @@ __export(exports_codebase, {
258
258
  detectProjectName: () => detectProjectName
259
259
  });
260
260
  import { createHash as createHash2 } from "node:crypto";
261
- import { readdirSync as readdirSync5, readFileSync as readFileSync5, statSync as statSync2, existsSync as existsSync6 } from "node:fs";
262
- import { join as join6, relative, extname, resolve, basename as basename3 } from "node:path";
261
+ import { readdirSync as readdirSync7, readFileSync as readFileSync7, statSync as statSync2, existsSync as existsSync8 } from "node:fs";
262
+ import { join as join8, relative, extname, resolve, basename as basename3 } from "node:path";
263
263
  import { execSync as execSync2 } from "node:child_process";
264
264
  function parseGitignore(rootPath) {
265
265
  const patterns = new Set;
266
- const gitignorePath = join6(rootPath, ".gitignore");
267
- if (!existsSync6(gitignorePath))
266
+ const gitignorePath = join8(rootPath, ".gitignore");
267
+ if (!existsSync8(gitignorePath))
268
268
  return patterns;
269
269
  try {
270
- const content = readFileSync5(gitignorePath, "utf-8");
270
+ const content = readFileSync7(gitignorePath, "utf-8");
271
271
  for (const line of content.split(`
272
272
  `)) {
273
273
  const trimmed = line.trim();
@@ -303,15 +303,15 @@ ${content}
303
303
  function detectProjectName(rootPath) {
304
304
  const readers = [
305
305
  () => {
306
- const pkg = JSON.parse(readFileSync5(join6(rootPath, "package.json"), "utf-8"));
306
+ const pkg = JSON.parse(readFileSync7(join8(rootPath, "package.json"), "utf-8"));
307
307
  return typeof pkg.name === "string" ? pkg.name : null;
308
308
  },
309
309
  () => {
310
- const content = readFileSync5(join6(rootPath, "Cargo.toml"), "utf-8");
310
+ const content = readFileSync7(join8(rootPath, "Cargo.toml"), "utf-8");
311
311
  return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
312
312
  },
313
313
  () => {
314
- const content = readFileSync5(join6(rootPath, "pyproject.toml"), "utf-8");
314
+ const content = readFileSync7(join8(rootPath, "pyproject.toml"), "utf-8");
315
315
  return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
316
316
  }
317
317
  ];
@@ -373,14 +373,14 @@ function indexCodebase(rootPath, options = {}) {
373
373
  function walk(dir) {
374
374
  let entries;
375
375
  try {
376
- entries = readdirSync5(dir, { withFileTypes: true });
376
+ entries = readdirSync7(dir, { withFileTypes: true });
377
377
  } catch {
378
378
  return;
379
379
  }
380
380
  for (const entry of entries) {
381
381
  if (shouldIgnore(entry.name, gitignorePatterns))
382
382
  continue;
383
- const fullPath = join6(dir, entry.name);
383
+ const fullPath = join8(dir, entry.name);
384
384
  if (entry.isDirectory()) {
385
385
  walk(fullPath);
386
386
  continue;
@@ -408,7 +408,7 @@ function indexCodebase(rootPath, options = {}) {
408
408
  }
409
409
  let raw;
410
410
  try {
411
- raw = readFileSync5(fullPath, "utf-8");
411
+ raw = readFileSync7(fullPath, "utf-8");
412
412
  } catch {
413
413
  skipped++;
414
414
  continue;
@@ -2057,13 +2057,13 @@ var init_interactive = __esm(() => {
2057
2057
  });
2058
2058
 
2059
2059
  // src/index.ts
2060
- import { existsSync as existsSync10 } from "node:fs";
2061
- import { join as join10 } from "node:path";
2060
+ import { existsSync as existsSync12 } from "node:fs";
2061
+ import { join as join12 } from "node:path";
2062
2062
 
2063
2063
  // src/detect.ts
2064
- import { existsSync as existsSync5, readdirSync as readdirSync4 } from "node:fs";
2065
- import { join as join5 } from "node:path";
2066
- import { homedir as homedir5, platform as platform3 } from "node:os";
2064
+ import { existsSync as existsSync7, readdirSync as readdirSync6 } from "node:fs";
2065
+ import { join as join7 } from "node:path";
2066
+ import { homedir as homedir7, platform as platform3 } from "node:os";
2067
2067
 
2068
2068
  // src/ingest/claude.ts
2069
2069
  init_shared();
@@ -2271,7 +2271,6 @@ async function countImportableClaudeSessions(onProgress) {
2271
2271
  return count;
2272
2272
  }
2273
2273
  function ingestClaude(projectRoots, opts) {
2274
- const dedupKeySource = opts?.dedupSource ?? "claude";
2275
2274
  const projectsDir = join2(homedir2(), ".claude", "projects");
2276
2275
  const memories = [];
2277
2276
  const sessionIds = [];
@@ -2316,7 +2315,7 @@ function ingestClaude(projectRoots, opts) {
2316
2315
  const contentHash = createContentHash(content);
2317
2316
  const dedupKey = `claude:${sessionId}`;
2318
2317
  const fingerprint = `${dedupKey}:${contentHash}`;
2319
- if (!opts?.includeIngested && isIngested(dedupKeySource, fingerprint)) {
2318
+ if (!opts?.includeIngested && isIngested("claude", fingerprint)) {
2320
2319
  deduped++;
2321
2320
  continue;
2322
2321
  }
@@ -2365,7 +2364,7 @@ function projectFromCwd(cwd) {
2365
2364
  }
2366
2365
  return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
2367
2366
  }
2368
- function ingestCodex(projectRoots) {
2367
+ function ingestCodex(projectRoots, opts) {
2369
2368
  const memories = [];
2370
2369
  const sessionIds = [];
2371
2370
  let filtered = 0;
@@ -2374,7 +2373,7 @@ function ingestCodex(projectRoots) {
2374
2373
  if (existsSync3(sessionsDir)) {
2375
2374
  try {
2376
2375
  const stats = { filtered: 0, deduped: 0 };
2377
- walkCodexSessions(sessionsDir, memories, sessionIds, stats, projectRoots);
2376
+ walkCodexSessions(sessionsDir, memories, sessionIds, stats, projectRoots, opts?.includeIngested);
2378
2377
  filtered += stats.filtered;
2379
2378
  deduped += stats.deduped;
2380
2379
  } catch {}
@@ -2540,6 +2539,7 @@ function walkCodexSessions(dir, memories, sessionIds, stats, projectRoots, inclu
2540
2539
  stats.deduped++;
2541
2540
  continue;
2542
2541
  }
2542
+ const sourceRemote = cwd ? repoRemote(cwd) : undefined;
2543
2543
  memories.push({
2544
2544
  content,
2545
2545
  containerTag: "codex-chats",
@@ -2549,6 +2549,7 @@ function walkCodexSessions(dir, memories, sessionIds, stats, projectRoots, inclu
2549
2549
  source: "codex-session",
2550
2550
  sessionId,
2551
2551
  ...cwd ? { sourceRoot: cwd } : {},
2552
+ ...sourceRemote ? { sourceRemote } : {},
2552
2553
  date: date || "unknown",
2553
2554
  ...sourceTimestamp ? { sourceTimestamp } : {},
2554
2555
  ...startedAt ? { sessionStartedAt: startedAt } : {},
@@ -3032,7 +3033,7 @@ async function ingestCursor(dbPath, wasmDir, projectRoots, opts) {
3032
3033
  const contentHash = createContentHash(content);
3033
3034
  const dedupKey = `cursor:${composerId}`;
3034
3035
  const fingerprint = `${dedupKey}:${contentHash}`;
3035
- if (!opts?.includeIngested && isIngested(opts?.dedupSource ?? "cursor", fingerprint)) {
3036
+ if (!opts?.includeIngested && isIngested("cursor", fingerprint)) {
3036
3037
  deduped++;
3037
3038
  continue;
3038
3039
  }
@@ -3066,14 +3067,410 @@ async function ingestCursor(dbPath, wasmDir, projectRoots, opts) {
3066
3067
  return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
3067
3068
  }
3068
3069
 
3070
+ // src/ingest/opencode.ts
3071
+ init_shared();
3072
+ import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
3073
+ import { join as join5 } from "node:path";
3074
+ import { homedir as homedir5 } from "node:os";
3075
+ function dataDirs() {
3076
+ const override = process.env.OPENCODE_DATA_DIR;
3077
+ if (override)
3078
+ return override.split(",").map((d) => d.trim()).filter(Boolean);
3079
+ return [join5(homedir5(), ".local", "share", "opencode")];
3080
+ }
3081
+ function storageDirs() {
3082
+ return dataDirs().map((d) => join5(d, "storage")).filter(existsSync5);
3083
+ }
3084
+ function readSessions(storage) {
3085
+ const out = [];
3086
+ const sessionRoot = join5(storage, "session");
3087
+ if (!existsSync5(sessionRoot))
3088
+ return out;
3089
+ const walk = (dir) => {
3090
+ for (const entry of readdirSync4(dir, { withFileTypes: true })) {
3091
+ const full = join5(dir, entry.name);
3092
+ if (entry.isDirectory()) {
3093
+ walk(full);
3094
+ continue;
3095
+ }
3096
+ if (!entry.name.endsWith(".json"))
3097
+ continue;
3098
+ try {
3099
+ const s = JSON.parse(readFileSync5(full, "utf-8"));
3100
+ if (typeof s.id !== "string")
3101
+ continue;
3102
+ out.push({
3103
+ id: s.id,
3104
+ directory: typeof s.directory === "string" ? s.directory : null,
3105
+ createdMs: typeof s.time?.created === "number" ? s.time.created : null,
3106
+ updatedMs: typeof s.time?.updated === "number" ? s.time.updated : null
3107
+ });
3108
+ } catch {}
3109
+ }
3110
+ };
3111
+ try {
3112
+ walk(sessionRoot);
3113
+ } catch {}
3114
+ return out;
3115
+ }
3116
+ function messageText(storage, messageId) {
3117
+ const partDir = join5(storage, "part", messageId);
3118
+ if (!existsSync5(partDir))
3119
+ return "";
3120
+ const texts = [];
3121
+ let files;
3122
+ try {
3123
+ files = readdirSync4(partDir).filter((f) => f.endsWith(".json")).sort();
3124
+ } catch {
3125
+ return "";
3126
+ }
3127
+ for (const f of files) {
3128
+ try {
3129
+ const p = JSON.parse(readFileSync5(join5(partDir, f), "utf-8"));
3130
+ if (p.type === "text" && typeof p.text === "string")
3131
+ texts.push(p.text);
3132
+ } catch {}
3133
+ }
3134
+ return texts.join(`
3135
+
3136
+ `);
3137
+ }
3138
+ function buildRounds(storage, sessionId) {
3139
+ const msgDir = join5(storage, "message", sessionId);
3140
+ if (!existsSync5(msgDir))
3141
+ return [];
3142
+ let entries = [];
3143
+ try {
3144
+ for (const f of readdirSync4(msgDir)) {
3145
+ if (!f.endsWith(".json"))
3146
+ continue;
3147
+ const m = JSON.parse(readFileSync5(join5(msgDir, f), "utf-8"));
3148
+ if (typeof m.id !== "string" || typeof m.role !== "string")
3149
+ continue;
3150
+ entries.push({ id: m.id, role: m.role, createdMs: typeof m.time?.created === "number" ? m.time.created : 0 });
3151
+ }
3152
+ } catch {
3153
+ return [];
3154
+ }
3155
+ entries.sort((a, b) => a.createdMs - b.createdMs);
3156
+ const rounds = [];
3157
+ let currentUser = null;
3158
+ let currentAssistant = [];
3159
+ for (const e of entries) {
3160
+ const text = cleanText(messageText(storage, e.id));
3161
+ if (!text)
3162
+ continue;
3163
+ if (e.role === "user") {
3164
+ if (currentUser !== null && currentAssistant.length > 0) {
3165
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
3166
+ }
3167
+ currentUser = text;
3168
+ currentAssistant = [];
3169
+ } else if (e.role === "assistant") {
3170
+ if (!isNarration(text))
3171
+ currentAssistant.push(text);
3172
+ }
3173
+ }
3174
+ if (currentUser !== null && currentAssistant.length > 0) {
3175
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
3176
+ }
3177
+ return rounds;
3178
+ }
3179
+ function ingestOpenCode(projectRoots, opts) {
3180
+ const memories = [];
3181
+ const sessionIds = [];
3182
+ let filtered = 0;
3183
+ let deduped = 0;
3184
+ for (const storage of storageDirs()) {
3185
+ for (const session of readSessions(storage)) {
3186
+ const cwd = session.directory;
3187
+ if (!matchesProjectFilter(cwd, projectRoots)) {
3188
+ filtered++;
3189
+ continue;
3190
+ }
3191
+ const rounds = buildRounds(storage, session.id);
3192
+ if (rounds.length === 0) {
3193
+ filtered++;
3194
+ continue;
3195
+ }
3196
+ const date = session.createdMs ? new Date(session.createdMs).toISOString().slice(0, 10) : null;
3197
+ const project = cwd ? projectFromCwd2(cwd) : null;
3198
+ const header = `# OpenCode Session${project ? `: ${project}` : ""} | ${date || "unknown"}`;
3199
+ const turns = rounds.map((r) => ({ user: r.user, assistant: r.assistantParts.join(`
3200
+
3201
+ `) }));
3202
+ const content = fitContent(header, turns);
3203
+ const contentHash = createContentHash(content);
3204
+ const dedupKey = `opencode:${session.id}`;
3205
+ const fingerprint = `${dedupKey}:${contentHash}`;
3206
+ if (!opts?.includeIngested && isIngested("opencode", fingerprint)) {
3207
+ deduped++;
3208
+ continue;
3209
+ }
3210
+ const sourceRemote = cwd ? repoRemote(cwd) : undefined;
3211
+ const sourceTimestamp = session.updatedMs ?? session.createdMs ?? null;
3212
+ memories.push({
3213
+ content,
3214
+ containerTag: "opencode-chats",
3215
+ metadata: {
3216
+ dedupKey,
3217
+ contentHash,
3218
+ source: "opencode-session",
3219
+ sessionId: session.id,
3220
+ ...cwd ? { sourceRoot: cwd } : {},
3221
+ ...sourceRemote ? { sourceRemote } : {},
3222
+ date: date || "unknown",
3223
+ ...sourceTimestamp ? { sourceTimestamp } : {},
3224
+ ...session.createdMs ? { sessionStartedAt: new Date(session.createdMs).toISOString() } : {},
3225
+ ...session.updatedMs ? { sessionUpdatedAt: new Date(session.updatedMs).toISOString() } : {},
3226
+ ...project ? { project } : {}
3227
+ },
3228
+ ...sourceTimestamp ? { created_at: sourceTimestamp, updated_at: sourceTimestamp } : {}
3229
+ });
3230
+ sessionIds.push(session.id);
3231
+ }
3232
+ }
3233
+ return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
3234
+ }
3235
+ function projectFromCwd2(cwd) {
3236
+ const home = homedir5().replace(/\\/g, "/");
3237
+ const normalized = cwd.replace(/\\/g, "/");
3238
+ if (normalized.startsWith(home + "/"))
3239
+ return normalized.slice(home.length + 1);
3240
+ return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
3241
+ }
3242
+ async function countImportableOpenCodeSessions(onProgress) {
3243
+ const storages = storageDirs();
3244
+ if (storages.length === 0)
3245
+ return 0;
3246
+ let found = 0;
3247
+ let checked = 0;
3248
+ let total = 0;
3249
+ const work = [];
3250
+ for (const storage of storages) {
3251
+ for (const s of readSessions(storage))
3252
+ work.push({ storage, sessionId: s.id });
3253
+ }
3254
+ total = work.length;
3255
+ for (const { storage, sessionId } of work) {
3256
+ if (buildRounds(storage, sessionId).length > 0)
3257
+ found++;
3258
+ checked++;
3259
+ if (checked % 50 === 0)
3260
+ onProgress?.({ checked, found, total });
3261
+ }
3262
+ onProgress?.({ checked, found, total });
3263
+ return found;
3264
+ }
3265
+
3266
+ // src/ingest/grok.ts
3267
+ init_shared();
3268
+ import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync5 } from "node:fs";
3269
+ import { join as join6 } from "node:path";
3270
+ import { homedir as homedir6 } from "node:os";
3271
+ var MIN_TURN_LEN3 = 50;
3272
+ function dataDirs2() {
3273
+ const override = process.env.GROK_DATA_DIR;
3274
+ if (override)
3275
+ return override.split(",").map((d) => d.trim()).filter(Boolean);
3276
+ return [join6(homedir6(), ".grok")];
3277
+ }
3278
+ function sessionRoots() {
3279
+ return dataDirs2().map((d) => join6(d, "sessions")).filter(existsSync6);
3280
+ }
3281
+ function readSessions2(sessionsRoot) {
3282
+ const out = [];
3283
+ let cwdDirs;
3284
+ try {
3285
+ cwdDirs = readdirSync5(sessionsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
3286
+ } catch {
3287
+ return out;
3288
+ }
3289
+ for (const cwdDir of cwdDirs) {
3290
+ const cwdPath = join6(sessionsRoot, cwdDir);
3291
+ let sessionDirs;
3292
+ try {
3293
+ sessionDirs = readdirSync5(cwdPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
3294
+ } catch {
3295
+ continue;
3296
+ }
3297
+ for (const sid of sessionDirs) {
3298
+ const dir = join6(cwdPath, sid);
3299
+ if (!existsSync6(join6(dir, "chat_history.jsonl")))
3300
+ continue;
3301
+ let summary = {};
3302
+ try {
3303
+ summary = JSON.parse(readFileSync6(join6(dir, "summary.json"), "utf-8"));
3304
+ } catch {}
3305
+ const cwd = typeof summary.info?.cwd === "string" ? summary.info.cwd : safeDecode(cwdDir);
3306
+ const remotes = Array.isArray(summary.git_remotes) ? summary.git_remotes.filter((r) => typeof r === "string") : [];
3307
+ out.push({
3308
+ id: sid,
3309
+ dir,
3310
+ cwd,
3311
+ gitRoot: typeof summary.git_root_dir === "string" ? summary.git_root_dir : null,
3312
+ gitRemote: remotes[0] ?? null,
3313
+ title: typeof summary.generated_title === "string" ? summary.generated_title : typeof summary.session_summary === "string" ? summary.session_summary : null,
3314
+ createdAt: typeof summary.created_at === "string" ? summary.created_at : null,
3315
+ updatedAt: typeof summary.updated_at === "string" ? summary.updated_at : null
3316
+ });
3317
+ }
3318
+ }
3319
+ return out;
3320
+ }
3321
+ function safeDecode(name) {
3322
+ try {
3323
+ return decodeURIComponent(name);
3324
+ } catch {
3325
+ return name || null;
3326
+ }
3327
+ }
3328
+ function extractText2(content) {
3329
+ if (typeof content === "string")
3330
+ return content;
3331
+ if (!Array.isArray(content))
3332
+ return "";
3333
+ return content.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
3334
+ `);
3335
+ }
3336
+ function userQuery(text) {
3337
+ const m = text.match(/<user_query>([\s\S]*?)<\/user_query>/);
3338
+ return m ? cleanText(m[1]) : "";
3339
+ }
3340
+ function parseRounds(lines) {
3341
+ const rounds = [];
3342
+ let currentUser = null;
3343
+ let currentAssistant = [];
3344
+ for (const line of lines) {
3345
+ if (!line.trim())
3346
+ continue;
3347
+ let obj;
3348
+ try {
3349
+ obj = JSON.parse(line);
3350
+ } catch {
3351
+ continue;
3352
+ }
3353
+ if (obj.type === "user") {
3354
+ const query = userQuery(extractText2(obj.content));
3355
+ if (query.length >= MIN_TURN_LEN3) {
3356
+ if (currentUser !== null && currentAssistant.length > 0) {
3357
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
3358
+ }
3359
+ currentUser = query;
3360
+ currentAssistant = [];
3361
+ }
3362
+ } else if (obj.type === "assistant") {
3363
+ const text = cleanText(extractText2(obj.content));
3364
+ if (text.length >= MIN_TURN_LEN3 && !isNarration(text))
3365
+ currentAssistant.push(text);
3366
+ }
3367
+ }
3368
+ if (currentUser !== null && currentAssistant.length > 0) {
3369
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
3370
+ }
3371
+ return rounds.map((r) => ({ user: r.user, assistant: r.assistantParts.join(`
3372
+
3373
+ `) })).filter((t) => t.assistant.length >= MIN_TURN_LEN3);
3374
+ }
3375
+ function readRounds(sessionDir) {
3376
+ let raw;
3377
+ try {
3378
+ raw = readFileSync6(join6(sessionDir, "chat_history.jsonl"), "utf-8");
3379
+ } catch {
3380
+ return [];
3381
+ }
3382
+ return parseRounds(raw.split(`
3383
+ `));
3384
+ }
3385
+ function ingestGrok(projectRoots, opts) {
3386
+ const memories = [];
3387
+ const sessionIds = [];
3388
+ let filtered = 0;
3389
+ let deduped = 0;
3390
+ for (const root of sessionRoots()) {
3391
+ for (const session of readSessions2(root)) {
3392
+ const cwd = session.gitRoot ?? session.cwd;
3393
+ if (!matchesProjectFilter(cwd, projectRoots)) {
3394
+ filtered++;
3395
+ continue;
3396
+ }
3397
+ const turns = readRounds(session.dir);
3398
+ if (turns.length === 0) {
3399
+ filtered++;
3400
+ continue;
3401
+ }
3402
+ const project = cwd ? projectFromCwd3(cwd) : null;
3403
+ const date = session.createdAt ? session.createdAt.slice(0, 10) : null;
3404
+ const header = `# Grok Session${project ? `: ${project}` : ""}${session.title ? ` — ${session.title}` : ""} | ${date || "unknown"}`;
3405
+ const content = fitContent(header, turns);
3406
+ const contentHash = createContentHash(content);
3407
+ const dedupKey = `grok:${session.id}`;
3408
+ const fingerprint = `${dedupKey}:${contentHash}`;
3409
+ if (!opts?.includeIngested && isIngested("grok", fingerprint)) {
3410
+ deduped++;
3411
+ continue;
3412
+ }
3413
+ const sourceRemote = session.gitRemote ?? (cwd ? repoRemote(cwd) : undefined);
3414
+ const sourceTimestamp = parseTimestampMs(session.updatedAt ?? session.createdAt ?? "");
3415
+ memories.push({
3416
+ content,
3417
+ containerTag: "grok-chats",
3418
+ metadata: {
3419
+ dedupKey,
3420
+ contentHash,
3421
+ source: "grok-session",
3422
+ sessionId: session.id,
3423
+ ...cwd ? { sourceRoot: cwd } : {},
3424
+ ...sourceRemote ? { sourceRemote } : {},
3425
+ date: date || "unknown",
3426
+ ...sourceTimestamp ? { sourceTimestamp } : {},
3427
+ ...session.createdAt ? { sessionStartedAt: session.createdAt } : {},
3428
+ ...session.updatedAt ? { sessionUpdatedAt: session.updatedAt } : {},
3429
+ ...project ? { project } : {}
3430
+ },
3431
+ ...sourceTimestamp ? { created_at: sourceTimestamp, updated_at: sourceTimestamp } : {}
3432
+ });
3433
+ sessionIds.push(session.id);
3434
+ }
3435
+ }
3436
+ return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
3437
+ }
3438
+ function projectFromCwd3(cwd) {
3439
+ const home = homedir6().replace(/\\/g, "/");
3440
+ const normalized = cwd.replace(/\\/g, "/").replace(/\/$/, "");
3441
+ if (normalized.startsWith(home + "/"))
3442
+ return normalized.slice(home.length + 1);
3443
+ return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
3444
+ }
3445
+ async function countImportableGrokSessions(onProgress) {
3446
+ const roots = sessionRoots();
3447
+ if (roots.length === 0)
3448
+ return 0;
3449
+ const sessions = [];
3450
+ for (const root of roots)
3451
+ sessions.push(...readSessions2(root));
3452
+ let found = 0;
3453
+ let checked = 0;
3454
+ const total = sessions.length;
3455
+ for (const session of sessions) {
3456
+ if (readRounds(session.dir).length > 0)
3457
+ found++;
3458
+ checked++;
3459
+ if (checked % 25 === 0)
3460
+ onProgress?.({ checked, found, total });
3461
+ }
3462
+ onProgress?.({ checked, found, total });
3463
+ return found;
3464
+ }
3465
+
3069
3466
  // src/detect.ts
3070
3467
  async function detect(options = {}) {
3071
- const home = homedir5();
3468
+ const home = homedir7();
3072
3469
  const os = platform3();
3073
3470
  const tools = [];
3074
3471
  const onProgress = options.onProgress;
3075
- const claudeDir = join5(home, ".claude", "projects");
3076
- if (existsSync5(claudeDir)) {
3472
+ const claudeDir = join7(home, ".claude", "projects");
3473
+ if (existsSync7(claudeDir)) {
3077
3474
  onProgress?.({ tool: "Claude Code", status: "start" });
3078
3475
  const sessionCount = await countImportableClaudeSessions((progress) => {
3079
3476
  onProgress?.({ tool: "Claude Code", status: "progress", ...progress });
@@ -3084,9 +3481,9 @@ async function detect(options = {}) {
3084
3481
  onProgress?.({ tool: "Claude Code", status: "skip", reason: "not found" });
3085
3482
  tools.push({ name: "Claude Code", id: "claude", available: false, path: claudeDir, sessionCount: 0 });
3086
3483
  }
3087
- const codexConfig = join5(home, ".codex", "config.toml");
3088
- const codexSessions = join5(home, ".codex", "sessions");
3089
- if (existsSync5(codexConfig) || existsSync5(codexSessions)) {
3484
+ const codexConfig = join7(home, ".codex", "config.toml");
3485
+ const codexSessions = join7(home, ".codex", "sessions");
3486
+ if (existsSync7(codexConfig) || existsSync7(codexSessions)) {
3090
3487
  onProgress?.({ tool: "Codex", status: "start" });
3091
3488
  const sessionCount = await countImportableCodexSessions((progress) => {
3092
3489
  onProgress?.({ tool: "Codex", status: "progress", ...progress });
@@ -3099,13 +3496,13 @@ async function detect(options = {}) {
3099
3496
  }
3100
3497
  let cursorDbPath;
3101
3498
  if (os === "darwin") {
3102
- cursorDbPath = join5(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
3499
+ cursorDbPath = join7(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
3103
3500
  } else if (os === "win32") {
3104
- cursorDbPath = join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
3501
+ cursorDbPath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
3105
3502
  } else {
3106
- cursorDbPath = join5(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
3503
+ cursorDbPath = join7(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
3107
3504
  }
3108
- const cursorExists = existsSync5(cursorDbPath);
3505
+ const cursorExists = existsSync7(cursorDbPath);
3109
3506
  if (cursorExists)
3110
3507
  onProgress?.({ tool: "Cursor", status: "start" });
3111
3508
  else
@@ -3123,73 +3520,111 @@ async function detect(options = {}) {
3123
3520
  sessionCount: cursorCount,
3124
3521
  sessionCountApproximate: true
3125
3522
  });
3126
- const windsurfDir = join5(home, ".codeium", "windsurf");
3523
+ const opencodeStorage = join7(process.env.OPENCODE_DATA_DIR?.split(",")[0]?.trim() || join7(home, ".local", "share", "opencode"), "storage");
3524
+ const opencodeExists = existsSync7(opencodeStorage);
3525
+ if (opencodeExists)
3526
+ onProgress?.({ tool: "OpenCode", status: "start" });
3527
+ else
3528
+ onProgress?.({ tool: "OpenCode", status: "skip", reason: "not found" });
3529
+ const opencodeCount = opencodeExists ? await countImportableOpenCodeSessions((progress) => {
3530
+ onProgress?.({ tool: "OpenCode", status: "progress", ...progress });
3531
+ }) : 0;
3532
+ if (opencodeExists)
3533
+ onProgress?.({ tool: "OpenCode", status: "done", count: opencodeCount });
3534
+ tools.push({
3535
+ name: "OpenCode",
3536
+ id: "opencode",
3537
+ available: opencodeExists,
3538
+ path: join7(home, ".config", "opencode", "opencode.json"),
3539
+ sessionCount: opencodeCount,
3540
+ sessionCountApproximate: true
3541
+ });
3542
+ const grokSessions = join7(process.env.GROK_DATA_DIR?.split(",")[0]?.trim() || join7(home, ".grok"), "sessions");
3543
+ const grokExists = existsSync7(grokSessions);
3544
+ if (grokExists)
3545
+ onProgress?.({ tool: "Grok", status: "start" });
3546
+ else
3547
+ onProgress?.({ tool: "Grok", status: "skip", reason: "not found" });
3548
+ const grokCount = grokExists ? await countImportableGrokSessions((progress) => {
3549
+ onProgress?.({ tool: "Grok", status: "progress", ...progress });
3550
+ }) : 0;
3551
+ if (grokExists)
3552
+ onProgress?.({ tool: "Grok", status: "done", count: grokCount });
3553
+ tools.push({
3554
+ name: "Grok",
3555
+ id: "grok",
3556
+ available: grokExists,
3557
+ path: join7(home, ".grok", "config.toml"),
3558
+ sessionCount: grokCount,
3559
+ sessionCountApproximate: true
3560
+ });
3561
+ const windsurfDir = join7(home, ".codeium", "windsurf");
3127
3562
  tools.push({
3128
3563
  name: "Windsurf",
3129
3564
  id: "windsurf",
3130
- available: existsSync5(windsurfDir),
3131
- path: join5(windsurfDir, "mcp_config.json"),
3565
+ available: existsSync7(windsurfDir),
3566
+ path: join7(windsurfDir, "mcp_config.json"),
3132
3567
  sessionCount: 0
3133
3568
  });
3134
3569
  let vscodePath;
3135
3570
  if (os === "darwin") {
3136
- vscodePath = join5(home, "Library", "Application Support", "Code");
3571
+ vscodePath = join7(home, "Library", "Application Support", "Code");
3137
3572
  } else if (os === "win32") {
3138
- vscodePath = join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "Code");
3573
+ vscodePath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Code");
3139
3574
  } else {
3140
- vscodePath = join5(home, ".config", "Code");
3575
+ vscodePath = join7(home, ".config", "Code");
3141
3576
  }
3142
3577
  tools.push({
3143
3578
  name: "VS Code Copilot",
3144
3579
  id: "vscode",
3145
- available: existsSync5(vscodePath),
3146
- path: join5(vscodePath, "User", "mcp.json"),
3580
+ available: existsSync7(vscodePath),
3581
+ path: join7(vscodePath, "User", "mcp.json"),
3147
3582
  sessionCount: 0
3148
3583
  });
3149
3584
  let clinePath;
3150
3585
  if (os === "darwin") {
3151
- clinePath = join5(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3586
+ clinePath = join7(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3152
3587
  } else if (os === "win32") {
3153
- clinePath = join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3588
+ clinePath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3154
3589
  } else {
3155
- clinePath = join5(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3590
+ clinePath = join7(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3156
3591
  }
3157
3592
  tools.push({
3158
3593
  name: "Cline",
3159
3594
  id: "cline",
3160
- available: existsSync5(clinePath),
3161
- path: join5(clinePath, "settings", "cline_mcp_settings.json"),
3595
+ available: existsSync7(clinePath),
3596
+ path: join7(clinePath, "settings", "cline_mcp_settings.json"),
3162
3597
  sessionCount: 0
3163
3598
  });
3164
- const zedPath = os === "darwin" ? join5(home, ".zed") : join5(home, ".config", "zed");
3599
+ const zedPath = os === "darwin" ? join7(home, ".zed") : join7(home, ".config", "zed");
3165
3600
  tools.push({
3166
3601
  name: "Zed",
3167
3602
  id: "zed",
3168
- available: existsSync5(zedPath),
3169
- path: join5(zedPath, "settings.json"),
3603
+ available: existsSync7(zedPath),
3604
+ path: join7(zedPath, "settings.json"),
3170
3605
  sessionCount: 0
3171
3606
  });
3172
- const openclawDir = join5(home, ".openclaw");
3607
+ const openclawDir = join7(home, ".openclaw");
3173
3608
  tools.push({
3174
3609
  name: "OpenClaw",
3175
3610
  id: "openclaw",
3176
- available: existsSync5(openclawDir),
3177
- path: join5(openclawDir, "openclaw.json"),
3611
+ available: existsSync7(openclawDir),
3612
+ path: join7(openclawDir, "openclaw.json"),
3178
3613
  sessionCount: 0
3179
3614
  });
3180
- const antigravityDir = join5(home, ".gemini", "antigravity");
3181
- const antigravityConvDir = join5(antigravityDir, "conversations");
3615
+ const antigravityDir = join7(home, ".gemini", "antigravity");
3616
+ const antigravityConvDir = join7(antigravityDir, "conversations");
3182
3617
  let antigravityCount = 0;
3183
- if (existsSync5(antigravityConvDir)) {
3618
+ if (existsSync7(antigravityConvDir)) {
3184
3619
  try {
3185
- antigravityCount = readdirSync4(antigravityConvDir).filter((f) => f.endsWith(".pb")).length;
3620
+ antigravityCount = readdirSync6(antigravityConvDir).filter((f) => f.endsWith(".pb")).length;
3186
3621
  } catch {}
3187
3622
  }
3188
3623
  tools.push({
3189
3624
  name: "Antigravity",
3190
3625
  id: "antigravity",
3191
- available: existsSync5(antigravityDir),
3192
- path: join5(antigravityDir, "mcp_config.json"),
3626
+ available: existsSync7(antigravityDir),
3627
+ path: join7(antigravityDir, "mcp_config.json"),
3193
3628
  sessionCount: antigravityCount
3194
3629
  });
3195
3630
  return tools;
@@ -3205,7 +3640,10 @@ async function browserAuth() {
3205
3640
  const state = Array.from(stateBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
3206
3641
  const sessionRes = await fetch(`${API_URL}/api/auth/cli-session`, {
3207
3642
  method: "POST",
3208
- headers: { "Content-Type": "application/json" },
3643
+ headers: {
3644
+ "Content-Type": "application/json",
3645
+ "x-conare-platform": `${platform4()} node/${process.versions.node}`
3646
+ },
3209
3647
  body: JSON.stringify({ state })
3210
3648
  });
3211
3649
  if (!sessionRes.ok) {
@@ -3280,9 +3718,9 @@ init_shared();
3280
3718
  init_api();
3281
3719
 
3282
3720
  // src/configure.ts
3283
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2, symlinkSync, readlinkSync, rmSync } from "node:fs";
3284
- import { dirname as dirname2, join as join7 } from "node:path";
3285
- import { homedir as homedir6, platform as platform5 } from "node:os";
3721
+ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync2, symlinkSync, readlinkSync, rmSync } from "node:fs";
3722
+ import { dirname as dirname2, join as join9 } from "node:path";
3723
+ import { homedir as homedir8, platform as platform5 } from "node:os";
3286
3724
  import { spawnSync } from "node:child_process";
3287
3725
  var CONARE_URL = "https://conare.ai";
3288
3726
  var SERVER_NAME = "conare";
@@ -3295,11 +3733,13 @@ var MCP_TARGETS = [
3295
3733
  { id: "cline", label: "Cline", defaultSelected: false },
3296
3734
  { id: "zed", label: "Zed", defaultSelected: false },
3297
3735
  { id: "openclaw", label: "OpenClaw", defaultSelected: false },
3298
- { id: "antigravity", label: "Antigravity", defaultSelected: false }
3736
+ { id: "antigravity", label: "Antigravity", defaultSelected: false },
3737
+ { id: "opencode", label: "OpenCode", defaultSelected: false },
3738
+ { id: "grok", label: "Grok", defaultSelected: false }
3299
3739
  ];
3300
3740
  function readJsonFile(path) {
3301
3741
  try {
3302
- return JSON.parse(readFileSync6(path, "utf-8"));
3742
+ return JSON.parse(readFileSync8(path, "utf-8"));
3303
3743
  } catch {
3304
3744
  return {};
3305
3745
  }
@@ -3343,7 +3783,7 @@ function configureClaude(apiKey) {
3343
3783
  }).status === 0) {
3344
3784
  return "\x1B[32m✓\x1B[0m Claude Code";
3345
3785
  }
3346
- const claudeConfigPath = join7(homedir6(), ".claude.json");
3786
+ const claudeConfigPath = join9(homedir8(), ".claude.json");
3347
3787
  const config = readJsonFile(claudeConfigPath);
3348
3788
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3349
3789
  config.mcpServers = {};
@@ -3353,7 +3793,7 @@ function configureClaude(apiKey) {
3353
3793
  return "\x1B[32m✓\x1B[0m Claude Code (json fallback)";
3354
3794
  }
3355
3795
  function configureCodex(apiKey) {
3356
- const configPath = join7(homedir6(), ".codex", "config.toml");
3796
+ const configPath = join9(homedir8(), ".codex", "config.toml");
3357
3797
  if (spawnSync("codex", ["mcp", "add", SERVER_NAME, "--url", `${CONARE_URL}/mcp`, "--header", `Authorization: Bearer ${apiKey}`], {
3358
3798
  stdio: "ignore",
3359
3799
  shell: platform5() === "win32"
@@ -3362,7 +3802,7 @@ function configureCodex(apiKey) {
3362
3802
  }
3363
3803
  let toml = "";
3364
3804
  try {
3365
- toml = readFileSync6(configPath, "utf-8");
3805
+ toml = readFileSync8(configPath, "utf-8");
3366
3806
  } catch {}
3367
3807
  const sectionHeader = `[mcp_servers.${SERVER_NAME}]`;
3368
3808
  const newSection = [
@@ -3385,9 +3825,9 @@ ${newSection}
3385
3825
  `;
3386
3826
  mkdirSync2(dirname2(configPath), { recursive: true });
3387
3827
  writeFileSync2(configPath, result);
3388
- const oldMcpJson = join7(homedir6(), ".codex", "mcp.json");
3828
+ const oldMcpJson = join9(homedir8(), ".codex", "mcp.json");
3389
3829
  try {
3390
- if (existsSync7(oldMcpJson)) {
3830
+ if (existsSync9(oldMcpJson)) {
3391
3831
  const old = readJsonFile(oldMcpJson);
3392
3832
  const servers = old.mcpServers;
3393
3833
  if (servers && (("conare" in servers) || ("conare-memory" in servers))) {
@@ -3404,7 +3844,7 @@ ${newSection}
3404
3844
  return "\x1B[32m✓\x1B[0m Codex";
3405
3845
  }
3406
3846
  function configureCursor(apiKey) {
3407
- const configPath = join7(homedir6(), ".cursor", "mcp.json");
3847
+ const configPath = join9(homedir8(), ".cursor", "mcp.json");
3408
3848
  const config = readJsonFile(configPath);
3409
3849
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3410
3850
  config.mcpServers = {};
@@ -3417,7 +3857,7 @@ function configureCursor(apiKey) {
3417
3857
  return "\x1B[32m✓\x1B[0m Cursor";
3418
3858
  }
3419
3859
  function configureWindsurf(apiKey) {
3420
- const configPath = join7(homedir6(), ".codeium", "windsurf", "mcp_config.json");
3860
+ const configPath = join9(homedir8(), ".codeium", "windsurf", "mcp_config.json");
3421
3861
  const config = readJsonFile(configPath);
3422
3862
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3423
3863
  config.mcpServers = {};
@@ -3433,11 +3873,11 @@ function configureVscode(apiKey) {
3433
3873
  const os = platform5();
3434
3874
  let configPath;
3435
3875
  if (os === "darwin") {
3436
- configPath = join7(homedir6(), "Library", "Application Support", "Code", "User", "mcp.json");
3876
+ configPath = join9(homedir8(), "Library", "Application Support", "Code", "User", "mcp.json");
3437
3877
  } else if (os === "win32") {
3438
- configPath = join7(process.env.APPDATA || join7(homedir6(), "AppData", "Roaming"), "Code", "User", "mcp.json");
3878
+ configPath = join9(process.env.APPDATA || join9(homedir8(), "AppData", "Roaming"), "Code", "User", "mcp.json");
3439
3879
  } else {
3440
- configPath = join7(homedir6(), ".config", "Code", "User", "mcp.json");
3880
+ configPath = join9(homedir8(), ".config", "Code", "User", "mcp.json");
3441
3881
  }
3442
3882
  const config = readJsonFile(configPath);
3443
3883
  if (!config.servers || typeof config.servers !== "object") {
@@ -3455,11 +3895,11 @@ function configureCline(apiKey) {
3455
3895
  const os = platform5();
3456
3896
  let configPath;
3457
3897
  if (os === "darwin") {
3458
- configPath = join7(homedir6(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3898
+ configPath = join9(homedir8(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3459
3899
  } else if (os === "win32") {
3460
- configPath = join7(process.env.APPDATA || join7(homedir6(), "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3900
+ configPath = join9(process.env.APPDATA || join9(homedir8(), "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3461
3901
  } else {
3462
- configPath = join7(homedir6(), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3902
+ configPath = join9(homedir8(), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3463
3903
  }
3464
3904
  const config = readJsonFile(configPath);
3465
3905
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
@@ -3477,9 +3917,9 @@ function configureZed(apiKey) {
3477
3917
  const os = platform5();
3478
3918
  let configPath;
3479
3919
  if (os === "darwin") {
3480
- configPath = join7(homedir6(), ".zed", "settings.json");
3920
+ configPath = join9(homedir8(), ".zed", "settings.json");
3481
3921
  } else {
3482
- configPath = join7(homedir6(), ".config", "zed", "settings.json");
3922
+ configPath = join9(homedir8(), ".config", "zed", "settings.json");
3483
3923
  }
3484
3924
  const config = readJsonFile(configPath);
3485
3925
  if (!config.context_servers || typeof config.context_servers !== "object") {
@@ -3500,7 +3940,7 @@ function configureZed(apiKey) {
3500
3940
  return "\x1B[32m✓\x1B[0m Zed";
3501
3941
  }
3502
3942
  function configureOpenclaw(apiKey) {
3503
- const configPath = join7(homedir6(), ".openclaw", "openclaw.json");
3943
+ const configPath = join9(homedir8(), ".openclaw", "openclaw.json");
3504
3944
  const config = readJsonFile(configPath);
3505
3945
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3506
3946
  config.mcpServers = {};
@@ -3514,7 +3954,7 @@ function configureOpenclaw(apiKey) {
3514
3954
  return "\x1B[32m✓\x1B[0m OpenClaw";
3515
3955
  }
3516
3956
  function configureAntigravity(apiKey) {
3517
- const configPath = join7(homedir6(), ".gemini", "antigravity", "mcp_config.json");
3957
+ const configPath = join9(homedir8(), ".gemini", "antigravity", "mcp_config.json");
3518
3958
  const config = readJsonFile(configPath);
3519
3959
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3520
3960
  config.mcpServers = {};
@@ -3526,6 +3966,49 @@ function configureAntigravity(apiKey) {
3526
3966
  writeJsonFile(configPath, config);
3527
3967
  return "\x1B[32m✓\x1B[0m Antigravity";
3528
3968
  }
3969
+ function configureOpencode(apiKey) {
3970
+ const configPath = join9(homedir8(), ".config", "opencode", "opencode.json");
3971
+ const config = readJsonFile(configPath);
3972
+ if (!config.mcp || typeof config.mcp !== "object") {
3973
+ config.mcp = {};
3974
+ }
3975
+ config.mcp[SERVER_NAME] = {
3976
+ type: "remote",
3977
+ url: `${CONARE_URL}/mcp`,
3978
+ enabled: true,
3979
+ headers: { Authorization: `Bearer ${apiKey}` }
3980
+ };
3981
+ if (!config["$schema"])
3982
+ config["$schema"] = "https://opencode.ai/config.json";
3983
+ writeJsonFile(configPath, config);
3984
+ return "\x1B[32m✓\x1B[0m OpenCode";
3985
+ }
3986
+ function configureGrok(apiKey) {
3987
+ const configPath = join9(homedir8(), ".grok", "config.toml");
3988
+ let toml = "";
3989
+ try {
3990
+ toml = readFileSync8(configPath, "utf-8");
3991
+ } catch {}
3992
+ const newSection = [
3993
+ `[mcp_servers.${SERVER_NAME}]`,
3994
+ `url = "${CONARE_URL}/mcp"`,
3995
+ `enabled = true`,
3996
+ `headers = { "Authorization" = "Bearer ${apiKey}" }`
3997
+ ].join(`
3998
+ `);
3999
+ const sectionRegex = new RegExp(`\\[mcp_servers\\.${SERVER_NAME}(?:\\.[^\\]]*)?\\][^\\[]*`, "g");
4000
+ const cleaned = toml.replace(sectionRegex, "").replace(/\n{3,}/g, `
4001
+
4002
+ `).trim();
4003
+ const result = cleaned ? `${cleaned}
4004
+
4005
+ ${newSection}
4006
+ ` : `${newSection}
4007
+ `;
4008
+ mkdirSync2(dirname2(configPath), { recursive: true });
4009
+ writeFileSync2(configPath, result);
4010
+ return "\x1B[32m✓\x1B[0m Grok";
4011
+ }
3529
4012
  var CLIENT_CONFIGURATORS = {
3530
4013
  claude: configureClaude,
3531
4014
  codex: configureCodex,
@@ -3535,7 +4018,9 @@ var CLIENT_CONFIGURATORS = {
3535
4018
  cline: configureCline,
3536
4019
  zed: configureZed,
3537
4020
  openclaw: configureOpenclaw,
3538
- antigravity: configureAntigravity
4021
+ antigravity: configureAntigravity,
4022
+ opencode: configureOpencode,
4023
+ grok: configureGrok
3539
4024
  };
3540
4025
  var SKILL_MD = `---
3541
4026
  name: conare
@@ -3645,14 +4130,14 @@ The wizard handles everything: account creation, API key, MCP configuration, bac
3645
4130
  For manual setup, visit [conare.ai](https://conare.ai).
3646
4131
  `;
3647
4132
  function installSkill() {
3648
- const skillDir = join7(homedir6(), ".agents", "skills", "conare");
4133
+ const skillDir = join9(homedir8(), ".agents", "skills", "conare");
3649
4134
  mkdirSync2(skillDir, { recursive: true });
3650
- writeFileSync2(join7(skillDir, "SKILL.md"), SKILL_MD);
3651
- const claudeSkillsDir = join7(homedir6(), ".claude", "skills");
3652
- const claudeSkillDir = join7(claudeSkillsDir, "conare");
4135
+ writeFileSync2(join9(skillDir, "SKILL.md"), SKILL_MD);
4136
+ const claudeSkillsDir = join9(homedir8(), ".claude", "skills");
4137
+ const claudeSkillDir = join9(claudeSkillsDir, "conare");
3653
4138
  try {
3654
- if (existsSync7(claudeSkillsDir)) {
3655
- if (existsSync7(claudeSkillDir)) {
4139
+ if (existsSync9(claudeSkillsDir)) {
4140
+ if (existsSync9(claudeSkillDir)) {
3656
4141
  try {
3657
4142
  if (readlinkSync(claudeSkillDir) === skillDir)
3658
4143
  return "\x1B[32m✓\x1B[0m Agent Skill";
@@ -3683,15 +4168,15 @@ function configureMcp(apiKey, targets = ["claude", "codex"]) {
3683
4168
  }
3684
4169
 
3685
4170
  // src/config.ts
3686
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
3687
- import { join as join8 } from "node:path";
3688
- import { homedir as homedir7 } from "node:os";
4171
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
4172
+ import { join as join10 } from "node:path";
4173
+ import { homedir as homedir9 } from "node:os";
3689
4174
  function readConfig() {
3690
4175
  const configPath = getConfigPath();
3691
4176
  try {
3692
- if (!existsSync8(configPath))
4177
+ if (!existsSync10(configPath))
3693
4178
  return {};
3694
- return JSON.parse(readFileSync7(configPath, "utf-8"));
4179
+ return JSON.parse(readFileSync9(configPath, "utf-8"));
3695
4180
  } catch {
3696
4181
  return {};
3697
4182
  }
@@ -3702,10 +4187,10 @@ function writeConfig(config) {
3702
4187
  `, { mode: 384 });
3703
4188
  }
3704
4189
  function getConfigDir() {
3705
- return join8(homedir7(), ".conare");
4190
+ return join10(homedir9(), ".conare");
3706
4191
  }
3707
4192
  function getConfigPath() {
3708
- return join8(getConfigDir(), "config.json");
4193
+ return join10(getConfigDir(), "config.json");
3709
4194
  }
3710
4195
  function saveApiKey(apiKey) {
3711
4196
  const config = readConfig();
@@ -3725,7 +4210,7 @@ function clearSavedApiKey() {
3725
4210
  delete config.key;
3726
4211
  if (Object.keys(config).length === 0) {
3727
4212
  const configPath = getConfigPath();
3728
- if (existsSync8(configPath))
4213
+ if (existsSync10(configPath))
3729
4214
  unlinkSync(configPath);
3730
4215
  } else {
3731
4216
  writeConfig(config);
@@ -3744,19 +4229,19 @@ function stripLegacyTeamConfig(config) {
3744
4229
  }
3745
4230
 
3746
4231
  // src/sync.ts
3747
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync8, chmodSync, cpSync, rmSync as rmSync2, symlinkSync as symlinkSync2, readlinkSync as readlinkSync2, appendFileSync } from "node:fs";
3748
- import { join as join9, dirname as dirname3 } from "node:path";
3749
- import { homedir as homedir8, platform as platform6 } from "node:os";
4232
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync10, chmodSync, cpSync, rmSync as rmSync2, symlinkSync as symlinkSync2, readlinkSync as readlinkSync2, appendFileSync } from "node:fs";
4233
+ import { join as join11, dirname as dirname3 } from "node:path";
4234
+ import { homedir as homedir10, platform as platform6 } from "node:os";
3750
4235
  import { execSync as execSync3 } from "node:child_process";
3751
4236
  import { createRequire as createRequire3 } from "node:module";
3752
- var CONARE_DIR = join9(homedir8(), ".conare");
3753
- var BIN_DIR = join9(CONARE_DIR, "bin");
3754
- var CONFIG_PATH = join9(CONARE_DIR, "config.json");
4237
+ var CONARE_DIR = join11(homedir10(), ".conare");
4238
+ var BIN_DIR = join11(CONARE_DIR, "bin");
4239
+ var CONFIG_PATH = join11(CONARE_DIR, "config.json");
3755
4240
  var PLIST_LABEL = "ai.conare.ingest";
3756
- var PLIST_PATH = join9(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
3757
- var SYSTEMD_DIR = join9(homedir8(), ".config", "systemd", "user");
3758
- var SYSTEMD_SERVICE = join9(SYSTEMD_DIR, "conare-sync.service");
3759
- var SYSTEMD_TIMER = join9(SYSTEMD_DIR, "conare-sync.timer");
4241
+ var PLIST_PATH = join11(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4242
+ var SYSTEMD_DIR = join11(homedir10(), ".config", "systemd", "user");
4243
+ var SYSTEMD_SERVICE = join11(SYSTEMD_DIR, "conare-sync.service");
4244
+ var SYSTEMD_TIMER = join11(SYSTEMD_DIR, "conare-sync.timer");
3760
4245
  var TASK_NAME = "ConareMemorySync";
3761
4246
  var RUN_VBS = `Set WshShell = CreateObject("WScript.Shell")
3762
4247
  WshShell.Run """" & CreateObject("WScript.Shell").ExpandEnvironmentStrings("%USERPROFILE%") & "\\.conare\\bin\\run.cmd" & """", 0, True
@@ -3901,38 +4386,38 @@ function persistBinary(apiKey) {
3901
4386
  if (!cliEntry) {
3902
4387
  throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
3903
4388
  }
3904
- const dest = join9(BIN_DIR, "conare-ingest.mjs");
3905
- const content = readFileSync8(cliEntry, "utf-8");
4389
+ const dest = join11(BIN_DIR, "conare-ingest.mjs");
4390
+ const content = readFileSync10(cliEntry, "utf-8");
3906
4391
  writeFileSync4(dest, content);
3907
4392
  for (const moduleName of ["sql.js", "better-sqlite3", "bindings", "file-uri-to-path"]) {
3908
4393
  try {
3909
4394
  copyNodeModule(moduleName);
3910
4395
  } catch {}
3911
4396
  }
3912
- const runShPath = join9(BIN_DIR, "run.sh");
4397
+ const runShPath = join11(BIN_DIR, "run.sh");
3913
4398
  writeFileSync4(runShPath, RUN_SH);
3914
4399
  try {
3915
4400
  chmodSync(runShPath, 493);
3916
4401
  } catch {}
3917
- const runCmdPath = join9(BIN_DIR, "run.cmd");
4402
+ const runCmdPath = join11(BIN_DIR, "run.cmd");
3918
4403
  writeFileSync4(runCmdPath, RUN_CMD);
3919
- const runVbsPath = join9(BIN_DIR, "run.vbs");
4404
+ const runVbsPath = join11(BIN_DIR, "run.vbs");
3920
4405
  writeFileSync4(runVbsPath, RUN_VBS);
3921
4406
  let existing = {};
3922
- if (existsSync9(CONFIG_PATH)) {
4407
+ if (existsSync11(CONFIG_PATH)) {
3923
4408
  try {
3924
- existing = JSON.parse(readFileSync8(CONFIG_PATH, "utf-8"));
4409
+ existing = JSON.parse(readFileSync10(CONFIG_PATH, "utf-8"));
3925
4410
  } catch {}
3926
4411
  }
3927
4412
  writeFileSync4(CONFIG_PATH, JSON.stringify(mergeApiKeyIntoConfig(existing, apiKey), null, 2) + `
3928
4413
  `, { mode: 384 });
3929
4414
  }
3930
4415
  function isValidJsBundle(path) {
3931
- if (!existsSync9(path))
4416
+ if (!existsSync11(path))
3932
4417
  return false;
3933
4418
  if (path.endsWith(".ts") || path.endsWith(".tsx"))
3934
4419
  return false;
3935
- const head = readFileSync8(path, "utf-8").slice(0, 2000);
4420
+ const head = readFileSync10(path, "utf-8").slice(0, 2000);
3936
4421
  if (/\btype\s+\{/.test(head) || /,\s*type\s+\w+/.test(head))
3937
4422
  return false;
3938
4423
  return true;
@@ -3940,8 +4425,8 @@ function isValidJsBundle(path) {
3940
4425
  function findCliBundle() {
3941
4426
  const dir = dirname3(new URL(import.meta.url).pathname);
3942
4427
  const distCandidates = [
3943
- join9(dir, "index.js"),
3944
- join9(dir, "..", "dist", "index.js")
4428
+ join11(dir, "index.js"),
4429
+ join11(dir, "..", "dist", "index.js")
3945
4430
  ];
3946
4431
  for (const c of distCandidates) {
3947
4432
  if (isValidJsBundle(c))
@@ -3958,12 +4443,12 @@ function findNodeModule(name) {
3958
4443
  return dirname3(require2.resolve(`${name}/package.json`));
3959
4444
  } catch {}
3960
4445
  const candidates = [
3961
- join9(process.cwd(), "node_modules", name),
3962
- join9(dirname3(new URL(import.meta.url).pathname), "..", "node_modules", name),
3963
- join9(dirname3(new URL(import.meta.url).pathname), "..", "..", "node_modules", name)
4446
+ join11(process.cwd(), "node_modules", name),
4447
+ join11(dirname3(new URL(import.meta.url).pathname), "..", "node_modules", name),
4448
+ join11(dirname3(new URL(import.meta.url).pathname), "..", "..", "node_modules", name)
3964
4449
  ];
3965
4450
  for (const c of candidates) {
3966
- if (existsSync9(c))
4451
+ if (existsSync11(c))
3967
4452
  return c;
3968
4453
  }
3969
4454
  return null;
@@ -3972,7 +4457,7 @@ function copyNodeModule(name) {
3972
4457
  const sourceDir = findNodeModule(name);
3973
4458
  if (!sourceDir)
3974
4459
  return;
3975
- const targetDir = join9(BIN_DIR, "node_modules", name);
4460
+ const targetDir = join11(BIN_DIR, "node_modules", name);
3976
4461
  rmSync2(targetDir, { recursive: true, force: true });
3977
4462
  mkdirSync4(dirname3(targetDir), { recursive: true });
3978
4463
  cpSync(sourceDir, targetDir, { recursive: true });
@@ -4007,7 +4492,7 @@ function makeLaunchdPlist(intervalSeconds) {
4007
4492
  <key>ProgramArguments</key>
4008
4493
  <array>
4009
4494
  <string>/bin/bash</string>
4010
- <string>${join9(BIN_DIR, "run.sh")}</string>
4495
+ <string>${join11(BIN_DIR, "run.sh")}</string>
4011
4496
  </array>
4012
4497
  <key>StartInterval</key>
4013
4498
  <integer>${intervalSeconds}</integer>
@@ -4016,7 +4501,7 @@ function makeLaunchdPlist(intervalSeconds) {
4016
4501
  <key>ProcessType</key>
4017
4502
  <string>Background</string>
4018
4503
  <key>StandardErrorPath</key>
4019
- <string>${join9(CONARE_DIR, "ingest.log")}</string>
4504
+ <string>${join11(CONARE_DIR, "ingest.log")}</string>
4020
4505
  </dict>
4021
4506
  </plist>
4022
4507
  `;
@@ -4069,7 +4554,7 @@ function clampCronInterval(intervalMinutes) {
4069
4554
  }
4070
4555
  function setupCron(intervalMinutes) {
4071
4556
  const clamped = clampCronInterval(intervalMinutes);
4072
- const cronCmd = `"${homedir8()}/.conare/bin/run.sh"`;
4557
+ const cronCmd = `"${homedir10()}/.conare/bin/run.sh"`;
4073
4558
  const cronLine = `*/${clamped} * * * * ${cronCmd} # conare-sync`;
4074
4559
  try {
4075
4560
  const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
@@ -4110,7 +4595,7 @@ function removeCronEntry() {
4110
4595
  function installGlobalCommand() {
4111
4596
  const isWindows = platform6() === "win32";
4112
4597
  if (isWindows) {
4113
- const wrapper2 = join9(BIN_DIR, "conare.cmd");
4598
+ const wrapper2 = join11(BIN_DIR, "conare.cmd");
4114
4599
  const content2 = `@echo off\r
4115
4600
  where node >nul 2>nul\r
4116
4601
  if errorlevel 1 (\r
@@ -4132,7 +4617,7 @@ node "%USERPROFILE%\\.conare\\bin\\conare-ingest.mjs" %*\r
4132
4617
  return `Global command: add ${binDirWin} to your PATH manually`;
4133
4618
  }
4134
4619
  }
4135
- const wrapper = join9(BIN_DIR, "conare");
4620
+ const wrapper = join11(BIN_DIR, "conare");
4136
4621
  const content = `#!/bin/bash
4137
4622
  # Conare global command — runs the persisted CLI bundle
4138
4623
  CONARE_DIR="$HOME/.conare"
@@ -4153,7 +4638,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
4153
4638
  chmodSync(wrapper, 493);
4154
4639
  const symlinkTarget = "/usr/local/bin/conare";
4155
4640
  try {
4156
- if (existsSync9(symlinkTarget)) {
4641
+ if (existsSync11(symlinkTarget)) {
4157
4642
  try {
4158
4643
  const existing = readlinkSync2(symlinkTarget);
4159
4644
  if (existing === wrapper)
@@ -4171,7 +4656,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
4171
4656
  const shellProfile = getShellProfile();
4172
4657
  if (shellProfile) {
4173
4658
  try {
4174
- const profileContent = existsSync9(shellProfile) ? readFileSync8(shellProfile, "utf-8") : "";
4659
+ const profileContent = existsSync11(shellProfile) ? readFileSync10(shellProfile, "utf-8") : "";
4175
4660
  const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
4176
4661
  if (!profileContent.includes(".conare/bin")) {
4177
4662
  appendFileSync(shellProfile, `
@@ -4189,24 +4674,24 @@ ${exportLine}
4189
4674
  }
4190
4675
  }
4191
4676
  function getShellProfile() {
4192
- const home = homedir8();
4677
+ const home = homedir10();
4193
4678
  const shell = process.env.SHELL || "";
4194
4679
  if (shell.includes("zsh"))
4195
- return join9(home, ".zshrc");
4680
+ return join11(home, ".zshrc");
4196
4681
  if (shell.includes("bash")) {
4197
- const profile = join9(home, ".bash_profile");
4198
- if (platform6() === "darwin" && existsSync9(profile))
4682
+ const profile = join11(home, ".bash_profile");
4683
+ if (platform6() === "darwin" && existsSync11(profile))
4199
4684
  return profile;
4200
- return join9(home, ".bashrc");
4685
+ return join11(home, ".bashrc");
4201
4686
  }
4202
- if (existsSync9(join9(home, ".zshrc")))
4203
- return join9(home, ".zshrc");
4204
- if (existsSync9(join9(home, ".bashrc")))
4205
- return join9(home, ".bashrc");
4687
+ if (existsSync11(join11(home, ".zshrc")))
4688
+ return join11(home, ".zshrc");
4689
+ if (existsSync11(join11(home, ".bashrc")))
4690
+ return join11(home, ".bashrc");
4206
4691
  return null;
4207
4692
  }
4208
4693
  function setupWindows(intervalMinutes) {
4209
- const runVbs = join9(BIN_DIR, "run.vbs").replace(/\//g, "\\");
4694
+ const runVbs = join11(BIN_DIR, "run.vbs").replace(/\//g, "\\");
4210
4695
  try {
4211
4696
  execSync3(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
4212
4697
  } catch {}
@@ -4346,18 +4831,18 @@ function detectMechanism() {
4346
4831
  }
4347
4832
  function syncStatus() {
4348
4833
  const mechanism = detectMechanism();
4349
- const logPath = join9(CONARE_DIR, "ingest.log");
4834
+ const logPath = join11(CONARE_DIR, "ingest.log");
4350
4835
  let parsed = { lastSyncIso: null, lastSyncError: null };
4351
- if (existsSync9(logPath)) {
4836
+ if (existsSync11(logPath)) {
4352
4837
  try {
4353
- parsed = parseLastSync(readFileSync8(logPath, "utf-8"));
4838
+ parsed = parseLastSync(readFileSync10(logPath, "utf-8"));
4354
4839
  } catch {}
4355
4840
  }
4356
4841
  return {
4357
4842
  timerInstalled: syncTimerInstalled(),
4358
4843
  mechanism,
4359
- binaryPersisted: existsSync9(join9(BIN_DIR, "conare-ingest.mjs")),
4360
- configPresent: existsSync9(CONFIG_PATH),
4844
+ binaryPersisted: existsSync11(join11(BIN_DIR, "conare-ingest.mjs")),
4845
+ configPresent: existsSync11(CONFIG_PATH),
4361
4846
  lastSyncIso: parsed.lastSyncIso,
4362
4847
  lastSyncError: parsed.lastSyncError
4363
4848
  };
@@ -4367,7 +4852,7 @@ function removeSyncTimer() {
4367
4852
  const os = platform6();
4368
4853
  if (os === "darwin") {
4369
4854
  bootoutLaunchAgent();
4370
- if (existsSync9(PLIST_PATH)) {
4855
+ if (existsSync11(PLIST_PATH)) {
4371
4856
  unlinkSync2(PLIST_PATH);
4372
4857
  messages.push("Removed launchd agent");
4373
4858
  }
@@ -4382,9 +4867,9 @@ function removeSyncTimer() {
4382
4867
  try {
4383
4868
  execSync3("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
4384
4869
  } catch {}
4385
- if (existsSync9(SYSTEMD_SERVICE))
4870
+ if (existsSync11(SYSTEMD_SERVICE))
4386
4871
  unlinkSync2(SYSTEMD_SERVICE);
4387
- if (existsSync9(SYSTEMD_TIMER))
4872
+ if (existsSync11(SYSTEMD_TIMER))
4388
4873
  unlinkSync2(SYSTEMD_TIMER);
4389
4874
  try {
4390
4875
  execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
@@ -4397,19 +4882,19 @@ function removeSyncTimer() {
4397
4882
  return messages;
4398
4883
  }
4399
4884
  function clearSyncLocks() {
4400
- const lockDir = join9(CONARE_DIR, "sync.lock.d");
4401
- if (existsSync9(lockDir))
4885
+ const lockDir = join11(CONARE_DIR, "sync.lock.d");
4886
+ if (existsSync11(lockDir))
4402
4887
  rmSync2(lockDir, { recursive: true, force: true });
4403
- const lockFile = join9(CONARE_DIR, "sync.lock");
4404
- if (existsSync9(lockFile))
4888
+ const lockFile = join11(CONARE_DIR, "sync.lock");
4889
+ if (existsSync11(lockFile))
4405
4890
  unlinkSync2(lockFile);
4406
4891
  }
4407
4892
  function uninstallSync() {
4408
4893
  const messages = removeSyncTimer();
4409
- if (existsSync9(CONFIG_PATH))
4894
+ if (existsSync11(CONFIG_PATH))
4410
4895
  unlinkSync2(CONFIG_PATH);
4411
4896
  clearSyncLocks();
4412
- if (existsSync9(BIN_DIR)) {
4897
+ if (existsSync11(BIN_DIR)) {
4413
4898
  rmSync2(BIN_DIR, { recursive: true, force: true });
4414
4899
  messages.push("Removed ~/.conare/bin/");
4415
4900
  }
@@ -4421,11 +4906,23 @@ function uninstallSync() {
4421
4906
 
4422
4907
  // src/index.ts
4423
4908
  init_interactive();
4909
+ var nodeMajor = Number(process.versions.node.split(".")[0]);
4910
+ if (nodeMajor < 20 || typeof fetch !== "function" || typeof crypto === "undefined") {
4911
+ console.error("");
4912
+ console.error(` Conare needs Node 20 or newer — you're running ${process.version}.`);
4913
+ console.error(" Install the current LTS from https://nodejs.org and re-run:");
4914
+ console.error("");
4915
+ console.error(" npx conare@latest");
4916
+ console.error("");
4917
+ process.exit(1);
4918
+ }
4424
4919
  var CONARE_URL2 = "https://conare.ai";
4425
4920
  var CHAT_CONTAINER_LABELS = {
4426
4921
  "claude-chats": "Claude Code",
4427
4922
  "codex-chats": "Codex",
4428
- "cursor-chats": "Cursor"
4923
+ "cursor-chats": "Cursor",
4924
+ "opencode-chats": "OpenCode",
4925
+ "grok-chats": "Grok"
4429
4926
  };
4430
4927
  function getManifestFingerprint(memory) {
4431
4928
  const metadata = memory.metadata;
@@ -4582,7 +5079,7 @@ Options:
4582
5079
  --ingest-only Index memories without MCP configuration
4583
5080
  --config-only Configure MCP only, skip indexing
4584
5081
  --interactive Run guided setup prompts
4585
- --source <name> Only index from: claude, codex, cursor
5082
+ --source <name> Only index from: claude, codex, cursor, opencode, grok
4586
5083
  --wasm-dir <path> Path to sql.js module (for Cursor indexing)
4587
5084
 
4588
5085
  Get your API key at https://conare.ai
@@ -4720,8 +5217,8 @@ async function main() {
4720
5217
  let configFileKey;
4721
5218
  if (opts.configFile) {
4722
5219
  try {
4723
- const { readFileSync: readFileSync9 } = await import("node:fs");
4724
- const raw = JSON.parse(readFileSync9(opts.configFile, "utf-8"));
5220
+ const { readFileSync: readFileSync11 } = await import("node:fs");
5221
+ const raw = JSON.parse(readFileSync11(opts.configFile, "utf-8"));
4725
5222
  configFileKey = raw.apiKey || raw.key;
4726
5223
  if (!configFileKey) {
4727
5224
  console.error(`Error: no apiKey/key found in ${opts.configFile}`);
@@ -4741,7 +5238,7 @@ async function main() {
4741
5238
  let effectiveConfigOnly = opts.configOnly;
4742
5239
  let effectiveIngestOnly = opts.ingestOnly;
4743
5240
  let effectiveIndexPath = opts.indexPath;
4744
- let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex"];
5241
+ let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex", "opencode", "grok"];
4745
5242
  let apiKey = opts.key || configFileKey || process.env.CONARE_API_KEY || savedApiKey;
4746
5243
  let interactiveTargets = MCP_TARGETS.map((target) => ({
4747
5244
  id: target.id,
@@ -4788,7 +5285,7 @@ async function main() {
4788
5285
  detectedCountApproximate: detected?.sessionCountApproximate
4789
5286
  };
4790
5287
  });
4791
- const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor"]);
5288
+ const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor", "opencode", "grok"]);
4792
5289
  const ingestibleTargets = interactiveTargets.filter((t) => INGESTIBLE_SOURCES.has(t.id));
4793
5290
  showDetectedApps(ingestibleTargets);
4794
5291
  selectedSources = await selectChatSources(ingestibleTargets);
@@ -4836,7 +5333,7 @@ async function main() {
4836
5333
  }
4837
5334
  saveApiKey(apiKey);
4838
5335
  if (auth.id)
4839
- setManifestAccount(auth.id);
5336
+ setManifestScope(auth.id, apiKey);
4840
5337
  if (!opts.force && !opts.dryRun) {
4841
5338
  const remoteMemoryCount = await getRemoteMemoryCount(apiKey);
4842
5339
  const localManifest = getIngested();
@@ -4858,8 +5355,8 @@ async function main() {
4858
5355
  }
4859
5356
  }
4860
5357
  }
4861
- if (!opts.wasmDir && existsSync10(join10(process.cwd(), "node_modules", "sql.js"))) {
4862
- opts.wasmDir = join10(process.cwd(), "node_modules");
5358
+ if (!opts.wasmDir && existsSync12(join12(process.cwd(), "node_modules", "sql.js"))) {
5359
+ opts.wasmDir = join12(process.cwd(), "node_modules");
4863
5360
  }
4864
5361
  if (effectiveConfigOnly) {
4865
5362
  if (!opts.dryRun) {
@@ -5006,11 +5503,11 @@ Nothing new to index.`);
5006
5503
  if (!opts.quiet) {
5007
5504
  const s = Y2();
5008
5505
  s.start("Scanning Claude Code chats...");
5009
- const { memories, filtered, deduped } = ingestClaude(undefined, { dedupSource: "claude" });
5506
+ const { memories, filtered, deduped } = ingestClaude();
5010
5507
  allMemories.push(...memories);
5011
5508
  s.stop(`Claude Code: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
5012
5509
  } else {
5013
- allMemories.push(...ingestClaude(undefined, { dedupSource: "claude" }).memories);
5510
+ allMemories.push(...ingestClaude().memories);
5014
5511
  }
5015
5512
  }
5016
5513
  if (shouldIngest("codex") && tools.find((t) => t.name === "Codex")?.available) {
@@ -5029,12 +5526,34 @@ Nothing new to index.`);
5029
5526
  const s = Y2();
5030
5527
  s.start("Scanning Cursor chats...");
5031
5528
  const cursorTool = tools.find((t) => t.name === "Cursor");
5032
- const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir, undefined, { dedupSource: "cursor" });
5529
+ const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir);
5033
5530
  allMemories.push(...memories);
5034
5531
  s.stop(`Cursor: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
5035
5532
  } else {
5036
5533
  const cursorTool = tools.find((t) => t.name === "Cursor");
5037
- allMemories.push(...(await ingestCursor(cursorTool.path, opts.wasmDir, undefined, { dedupSource: "cursor" })).memories);
5534
+ allMemories.push(...(await ingestCursor(cursorTool.path, opts.wasmDir)).memories);
5535
+ }
5536
+ }
5537
+ if (shouldIngest("opencode") && tools.find((t) => t.name === "OpenCode")?.available) {
5538
+ if (!opts.quiet) {
5539
+ const s = Y2();
5540
+ s.start("Scanning OpenCode chats...");
5541
+ const { memories, filtered, deduped } = ingestOpenCode();
5542
+ allMemories.push(...memories);
5543
+ s.stop(`OpenCode: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
5544
+ } else {
5545
+ allMemories.push(...ingestOpenCode().memories);
5546
+ }
5547
+ }
5548
+ if (shouldIngest("grok") && tools.find((t) => t.name === "Grok")?.available) {
5549
+ if (!opts.quiet) {
5550
+ const s = Y2();
5551
+ s.start("Scanning Grok chats...");
5552
+ const { memories, filtered, deduped } = ingestGrok();
5553
+ allMemories.push(...memories);
5554
+ s.stop(`Grok: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
5555
+ } else {
5556
+ allMemories.push(...ingestGrok().memories);
5038
5557
  }
5039
5558
  }
5040
5559
  allMemories.sort((a, b3) => {
@@ -5120,7 +5639,9 @@ Nothing new to index.`);
5120
5639
  const successfulKeysBySource = {
5121
5640
  claude: [],
5122
5641
  codex: [],
5123
- cursor: []
5642
+ cursor: [],
5643
+ opencode: [],
5644
+ grok: []
5124
5645
  };
5125
5646
  for (const result of results) {
5126
5647
  if (!result.success)
@@ -5139,6 +5660,12 @@ Nothing new to index.`);
5139
5660
  case "cursor-chats":
5140
5661
  successfulKeysBySource.cursor.push(key);
5141
5662
  break;
5663
+ case "opencode-chats":
5664
+ successfulKeysBySource.opencode.push(key);
5665
+ break;
5666
+ case "grok-chats":
5667
+ successfulKeysBySource.grok.push(key);
5668
+ break;
5142
5669
  }
5143
5670
  }
5144
5671
  for (const [source, ids] of Object.entries(successfulKeysBySource)) {
@@ -5194,6 +5721,10 @@ Nothing new to index.`);
5194
5721
  return "Codex";
5195
5722
  if (t === "cursor")
5196
5723
  return "Cursor";
5724
+ if (t === "opencode")
5725
+ return "OpenCode";
5726
+ if (t === "grok")
5727
+ return "Grok";
5197
5728
  return t;
5198
5729
  });
5199
5730
  const toolList = configuredTools.length > 0 ? configuredTools.join(", ") : "your AI tools";