conare 0.6.1 → 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 +690 -172
  2. package/package.json +1 -1
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;
@@ -3283,9 +3718,9 @@ init_shared();
3283
3718
  init_api();
3284
3719
 
3285
3720
  // src/configure.ts
3286
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2, symlinkSync, readlinkSync, rmSync } from "node:fs";
3287
- import { dirname as dirname2, join as join7 } from "node:path";
3288
- 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";
3289
3724
  import { spawnSync } from "node:child_process";
3290
3725
  var CONARE_URL = "https://conare.ai";
3291
3726
  var SERVER_NAME = "conare";
@@ -3298,11 +3733,13 @@ var MCP_TARGETS = [
3298
3733
  { id: "cline", label: "Cline", defaultSelected: false },
3299
3734
  { id: "zed", label: "Zed", defaultSelected: false },
3300
3735
  { id: "openclaw", label: "OpenClaw", defaultSelected: false },
3301
- { 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 }
3302
3739
  ];
3303
3740
  function readJsonFile(path) {
3304
3741
  try {
3305
- return JSON.parse(readFileSync6(path, "utf-8"));
3742
+ return JSON.parse(readFileSync8(path, "utf-8"));
3306
3743
  } catch {
3307
3744
  return {};
3308
3745
  }
@@ -3346,7 +3783,7 @@ function configureClaude(apiKey) {
3346
3783
  }).status === 0) {
3347
3784
  return "\x1B[32m✓\x1B[0m Claude Code";
3348
3785
  }
3349
- const claudeConfigPath = join7(homedir6(), ".claude.json");
3786
+ const claudeConfigPath = join9(homedir8(), ".claude.json");
3350
3787
  const config = readJsonFile(claudeConfigPath);
3351
3788
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3352
3789
  config.mcpServers = {};
@@ -3356,7 +3793,7 @@ function configureClaude(apiKey) {
3356
3793
  return "\x1B[32m✓\x1B[0m Claude Code (json fallback)";
3357
3794
  }
3358
3795
  function configureCodex(apiKey) {
3359
- const configPath = join7(homedir6(), ".codex", "config.toml");
3796
+ const configPath = join9(homedir8(), ".codex", "config.toml");
3360
3797
  if (spawnSync("codex", ["mcp", "add", SERVER_NAME, "--url", `${CONARE_URL}/mcp`, "--header", `Authorization: Bearer ${apiKey}`], {
3361
3798
  stdio: "ignore",
3362
3799
  shell: platform5() === "win32"
@@ -3365,7 +3802,7 @@ function configureCodex(apiKey) {
3365
3802
  }
3366
3803
  let toml = "";
3367
3804
  try {
3368
- toml = readFileSync6(configPath, "utf-8");
3805
+ toml = readFileSync8(configPath, "utf-8");
3369
3806
  } catch {}
3370
3807
  const sectionHeader = `[mcp_servers.${SERVER_NAME}]`;
3371
3808
  const newSection = [
@@ -3388,9 +3825,9 @@ ${newSection}
3388
3825
  `;
3389
3826
  mkdirSync2(dirname2(configPath), { recursive: true });
3390
3827
  writeFileSync2(configPath, result);
3391
- const oldMcpJson = join7(homedir6(), ".codex", "mcp.json");
3828
+ const oldMcpJson = join9(homedir8(), ".codex", "mcp.json");
3392
3829
  try {
3393
- if (existsSync7(oldMcpJson)) {
3830
+ if (existsSync9(oldMcpJson)) {
3394
3831
  const old = readJsonFile(oldMcpJson);
3395
3832
  const servers = old.mcpServers;
3396
3833
  if (servers && (("conare" in servers) || ("conare-memory" in servers))) {
@@ -3407,7 +3844,7 @@ ${newSection}
3407
3844
  return "\x1B[32m✓\x1B[0m Codex";
3408
3845
  }
3409
3846
  function configureCursor(apiKey) {
3410
- const configPath = join7(homedir6(), ".cursor", "mcp.json");
3847
+ const configPath = join9(homedir8(), ".cursor", "mcp.json");
3411
3848
  const config = readJsonFile(configPath);
3412
3849
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3413
3850
  config.mcpServers = {};
@@ -3420,7 +3857,7 @@ function configureCursor(apiKey) {
3420
3857
  return "\x1B[32m✓\x1B[0m Cursor";
3421
3858
  }
3422
3859
  function configureWindsurf(apiKey) {
3423
- const configPath = join7(homedir6(), ".codeium", "windsurf", "mcp_config.json");
3860
+ const configPath = join9(homedir8(), ".codeium", "windsurf", "mcp_config.json");
3424
3861
  const config = readJsonFile(configPath);
3425
3862
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3426
3863
  config.mcpServers = {};
@@ -3436,11 +3873,11 @@ function configureVscode(apiKey) {
3436
3873
  const os = platform5();
3437
3874
  let configPath;
3438
3875
  if (os === "darwin") {
3439
- configPath = join7(homedir6(), "Library", "Application Support", "Code", "User", "mcp.json");
3876
+ configPath = join9(homedir8(), "Library", "Application Support", "Code", "User", "mcp.json");
3440
3877
  } else if (os === "win32") {
3441
- 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");
3442
3879
  } else {
3443
- configPath = join7(homedir6(), ".config", "Code", "User", "mcp.json");
3880
+ configPath = join9(homedir8(), ".config", "Code", "User", "mcp.json");
3444
3881
  }
3445
3882
  const config = readJsonFile(configPath);
3446
3883
  if (!config.servers || typeof config.servers !== "object") {
@@ -3458,11 +3895,11 @@ function configureCline(apiKey) {
3458
3895
  const os = platform5();
3459
3896
  let configPath;
3460
3897
  if (os === "darwin") {
3461
- 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");
3462
3899
  } else if (os === "win32") {
3463
- 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");
3464
3901
  } else {
3465
- 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");
3466
3903
  }
3467
3904
  const config = readJsonFile(configPath);
3468
3905
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
@@ -3480,9 +3917,9 @@ function configureZed(apiKey) {
3480
3917
  const os = platform5();
3481
3918
  let configPath;
3482
3919
  if (os === "darwin") {
3483
- configPath = join7(homedir6(), ".zed", "settings.json");
3920
+ configPath = join9(homedir8(), ".zed", "settings.json");
3484
3921
  } else {
3485
- configPath = join7(homedir6(), ".config", "zed", "settings.json");
3922
+ configPath = join9(homedir8(), ".config", "zed", "settings.json");
3486
3923
  }
3487
3924
  const config = readJsonFile(configPath);
3488
3925
  if (!config.context_servers || typeof config.context_servers !== "object") {
@@ -3503,7 +3940,7 @@ function configureZed(apiKey) {
3503
3940
  return "\x1B[32m✓\x1B[0m Zed";
3504
3941
  }
3505
3942
  function configureOpenclaw(apiKey) {
3506
- const configPath = join7(homedir6(), ".openclaw", "openclaw.json");
3943
+ const configPath = join9(homedir8(), ".openclaw", "openclaw.json");
3507
3944
  const config = readJsonFile(configPath);
3508
3945
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3509
3946
  config.mcpServers = {};
@@ -3517,7 +3954,7 @@ function configureOpenclaw(apiKey) {
3517
3954
  return "\x1B[32m✓\x1B[0m OpenClaw";
3518
3955
  }
3519
3956
  function configureAntigravity(apiKey) {
3520
- const configPath = join7(homedir6(), ".gemini", "antigravity", "mcp_config.json");
3957
+ const configPath = join9(homedir8(), ".gemini", "antigravity", "mcp_config.json");
3521
3958
  const config = readJsonFile(configPath);
3522
3959
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3523
3960
  config.mcpServers = {};
@@ -3529,6 +3966,49 @@ function configureAntigravity(apiKey) {
3529
3966
  writeJsonFile(configPath, config);
3530
3967
  return "\x1B[32m✓\x1B[0m Antigravity";
3531
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
+ }
3532
4012
  var CLIENT_CONFIGURATORS = {
3533
4013
  claude: configureClaude,
3534
4014
  codex: configureCodex,
@@ -3538,7 +4018,9 @@ var CLIENT_CONFIGURATORS = {
3538
4018
  cline: configureCline,
3539
4019
  zed: configureZed,
3540
4020
  openclaw: configureOpenclaw,
3541
- antigravity: configureAntigravity
4021
+ antigravity: configureAntigravity,
4022
+ opencode: configureOpencode,
4023
+ grok: configureGrok
3542
4024
  };
3543
4025
  var SKILL_MD = `---
3544
4026
  name: conare
@@ -3648,14 +4130,14 @@ The wizard handles everything: account creation, API key, MCP configuration, bac
3648
4130
  For manual setup, visit [conare.ai](https://conare.ai).
3649
4131
  `;
3650
4132
  function installSkill() {
3651
- const skillDir = join7(homedir6(), ".agents", "skills", "conare");
4133
+ const skillDir = join9(homedir8(), ".agents", "skills", "conare");
3652
4134
  mkdirSync2(skillDir, { recursive: true });
3653
- writeFileSync2(join7(skillDir, "SKILL.md"), SKILL_MD);
3654
- const claudeSkillsDir = join7(homedir6(), ".claude", "skills");
3655
- 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");
3656
4138
  try {
3657
- if (existsSync7(claudeSkillsDir)) {
3658
- if (existsSync7(claudeSkillDir)) {
4139
+ if (existsSync9(claudeSkillsDir)) {
4140
+ if (existsSync9(claudeSkillDir)) {
3659
4141
  try {
3660
4142
  if (readlinkSync(claudeSkillDir) === skillDir)
3661
4143
  return "\x1B[32m✓\x1B[0m Agent Skill";
@@ -3686,15 +4168,15 @@ function configureMcp(apiKey, targets = ["claude", "codex"]) {
3686
4168
  }
3687
4169
 
3688
4170
  // src/config.ts
3689
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
3690
- import { join as join8 } from "node:path";
3691
- 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";
3692
4174
  function readConfig() {
3693
4175
  const configPath = getConfigPath();
3694
4176
  try {
3695
- if (!existsSync8(configPath))
4177
+ if (!existsSync10(configPath))
3696
4178
  return {};
3697
- return JSON.parse(readFileSync7(configPath, "utf-8"));
4179
+ return JSON.parse(readFileSync9(configPath, "utf-8"));
3698
4180
  } catch {
3699
4181
  return {};
3700
4182
  }
@@ -3705,10 +4187,10 @@ function writeConfig(config) {
3705
4187
  `, { mode: 384 });
3706
4188
  }
3707
4189
  function getConfigDir() {
3708
- return join8(homedir7(), ".conare");
4190
+ return join10(homedir9(), ".conare");
3709
4191
  }
3710
4192
  function getConfigPath() {
3711
- return join8(getConfigDir(), "config.json");
4193
+ return join10(getConfigDir(), "config.json");
3712
4194
  }
3713
4195
  function saveApiKey(apiKey) {
3714
4196
  const config = readConfig();
@@ -3728,7 +4210,7 @@ function clearSavedApiKey() {
3728
4210
  delete config.key;
3729
4211
  if (Object.keys(config).length === 0) {
3730
4212
  const configPath = getConfigPath();
3731
- if (existsSync8(configPath))
4213
+ if (existsSync10(configPath))
3732
4214
  unlinkSync(configPath);
3733
4215
  } else {
3734
4216
  writeConfig(config);
@@ -3747,19 +4229,19 @@ function stripLegacyTeamConfig(config) {
3747
4229
  }
3748
4230
 
3749
4231
  // src/sync.ts
3750
- 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";
3751
- import { join as join9, dirname as dirname3 } from "node:path";
3752
- 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";
3753
4235
  import { execSync as execSync3 } from "node:child_process";
3754
4236
  import { createRequire as createRequire3 } from "node:module";
3755
- var CONARE_DIR = join9(homedir8(), ".conare");
3756
- var BIN_DIR = join9(CONARE_DIR, "bin");
3757
- 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");
3758
4240
  var PLIST_LABEL = "ai.conare.ingest";
3759
- var PLIST_PATH = join9(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
3760
- var SYSTEMD_DIR = join9(homedir8(), ".config", "systemd", "user");
3761
- var SYSTEMD_SERVICE = join9(SYSTEMD_DIR, "conare-sync.service");
3762
- 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");
3763
4245
  var TASK_NAME = "ConareMemorySync";
3764
4246
  var RUN_VBS = `Set WshShell = CreateObject("WScript.Shell")
3765
4247
  WshShell.Run """" & CreateObject("WScript.Shell").ExpandEnvironmentStrings("%USERPROFILE%") & "\\.conare\\bin\\run.cmd" & """", 0, True
@@ -3904,38 +4386,38 @@ function persistBinary(apiKey) {
3904
4386
  if (!cliEntry) {
3905
4387
  throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
3906
4388
  }
3907
- const dest = join9(BIN_DIR, "conare-ingest.mjs");
3908
- const content = readFileSync8(cliEntry, "utf-8");
4389
+ const dest = join11(BIN_DIR, "conare-ingest.mjs");
4390
+ const content = readFileSync10(cliEntry, "utf-8");
3909
4391
  writeFileSync4(dest, content);
3910
4392
  for (const moduleName of ["sql.js", "better-sqlite3", "bindings", "file-uri-to-path"]) {
3911
4393
  try {
3912
4394
  copyNodeModule(moduleName);
3913
4395
  } catch {}
3914
4396
  }
3915
- const runShPath = join9(BIN_DIR, "run.sh");
4397
+ const runShPath = join11(BIN_DIR, "run.sh");
3916
4398
  writeFileSync4(runShPath, RUN_SH);
3917
4399
  try {
3918
4400
  chmodSync(runShPath, 493);
3919
4401
  } catch {}
3920
- const runCmdPath = join9(BIN_DIR, "run.cmd");
4402
+ const runCmdPath = join11(BIN_DIR, "run.cmd");
3921
4403
  writeFileSync4(runCmdPath, RUN_CMD);
3922
- const runVbsPath = join9(BIN_DIR, "run.vbs");
4404
+ const runVbsPath = join11(BIN_DIR, "run.vbs");
3923
4405
  writeFileSync4(runVbsPath, RUN_VBS);
3924
4406
  let existing = {};
3925
- if (existsSync9(CONFIG_PATH)) {
4407
+ if (existsSync11(CONFIG_PATH)) {
3926
4408
  try {
3927
- existing = JSON.parse(readFileSync8(CONFIG_PATH, "utf-8"));
4409
+ existing = JSON.parse(readFileSync10(CONFIG_PATH, "utf-8"));
3928
4410
  } catch {}
3929
4411
  }
3930
4412
  writeFileSync4(CONFIG_PATH, JSON.stringify(mergeApiKeyIntoConfig(existing, apiKey), null, 2) + `
3931
4413
  `, { mode: 384 });
3932
4414
  }
3933
4415
  function isValidJsBundle(path) {
3934
- if (!existsSync9(path))
4416
+ if (!existsSync11(path))
3935
4417
  return false;
3936
4418
  if (path.endsWith(".ts") || path.endsWith(".tsx"))
3937
4419
  return false;
3938
- const head = readFileSync8(path, "utf-8").slice(0, 2000);
4420
+ const head = readFileSync10(path, "utf-8").slice(0, 2000);
3939
4421
  if (/\btype\s+\{/.test(head) || /,\s*type\s+\w+/.test(head))
3940
4422
  return false;
3941
4423
  return true;
@@ -3943,8 +4425,8 @@ function isValidJsBundle(path) {
3943
4425
  function findCliBundle() {
3944
4426
  const dir = dirname3(new URL(import.meta.url).pathname);
3945
4427
  const distCandidates = [
3946
- join9(dir, "index.js"),
3947
- join9(dir, "..", "dist", "index.js")
4428
+ join11(dir, "index.js"),
4429
+ join11(dir, "..", "dist", "index.js")
3948
4430
  ];
3949
4431
  for (const c of distCandidates) {
3950
4432
  if (isValidJsBundle(c))
@@ -3961,12 +4443,12 @@ function findNodeModule(name) {
3961
4443
  return dirname3(require2.resolve(`${name}/package.json`));
3962
4444
  } catch {}
3963
4445
  const candidates = [
3964
- join9(process.cwd(), "node_modules", name),
3965
- join9(dirname3(new URL(import.meta.url).pathname), "..", "node_modules", name),
3966
- 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)
3967
4449
  ];
3968
4450
  for (const c of candidates) {
3969
- if (existsSync9(c))
4451
+ if (existsSync11(c))
3970
4452
  return c;
3971
4453
  }
3972
4454
  return null;
@@ -3975,7 +4457,7 @@ function copyNodeModule(name) {
3975
4457
  const sourceDir = findNodeModule(name);
3976
4458
  if (!sourceDir)
3977
4459
  return;
3978
- const targetDir = join9(BIN_DIR, "node_modules", name);
4460
+ const targetDir = join11(BIN_DIR, "node_modules", name);
3979
4461
  rmSync2(targetDir, { recursive: true, force: true });
3980
4462
  mkdirSync4(dirname3(targetDir), { recursive: true });
3981
4463
  cpSync(sourceDir, targetDir, { recursive: true });
@@ -4010,7 +4492,7 @@ function makeLaunchdPlist(intervalSeconds) {
4010
4492
  <key>ProgramArguments</key>
4011
4493
  <array>
4012
4494
  <string>/bin/bash</string>
4013
- <string>${join9(BIN_DIR, "run.sh")}</string>
4495
+ <string>${join11(BIN_DIR, "run.sh")}</string>
4014
4496
  </array>
4015
4497
  <key>StartInterval</key>
4016
4498
  <integer>${intervalSeconds}</integer>
@@ -4019,7 +4501,7 @@ function makeLaunchdPlist(intervalSeconds) {
4019
4501
  <key>ProcessType</key>
4020
4502
  <string>Background</string>
4021
4503
  <key>StandardErrorPath</key>
4022
- <string>${join9(CONARE_DIR, "ingest.log")}</string>
4504
+ <string>${join11(CONARE_DIR, "ingest.log")}</string>
4023
4505
  </dict>
4024
4506
  </plist>
4025
4507
  `;
@@ -4072,7 +4554,7 @@ function clampCronInterval(intervalMinutes) {
4072
4554
  }
4073
4555
  function setupCron(intervalMinutes) {
4074
4556
  const clamped = clampCronInterval(intervalMinutes);
4075
- const cronCmd = `"${homedir8()}/.conare/bin/run.sh"`;
4557
+ const cronCmd = `"${homedir10()}/.conare/bin/run.sh"`;
4076
4558
  const cronLine = `*/${clamped} * * * * ${cronCmd} # conare-sync`;
4077
4559
  try {
4078
4560
  const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
@@ -4113,7 +4595,7 @@ function removeCronEntry() {
4113
4595
  function installGlobalCommand() {
4114
4596
  const isWindows = platform6() === "win32";
4115
4597
  if (isWindows) {
4116
- const wrapper2 = join9(BIN_DIR, "conare.cmd");
4598
+ const wrapper2 = join11(BIN_DIR, "conare.cmd");
4117
4599
  const content2 = `@echo off\r
4118
4600
  where node >nul 2>nul\r
4119
4601
  if errorlevel 1 (\r
@@ -4135,7 +4617,7 @@ node "%USERPROFILE%\\.conare\\bin\\conare-ingest.mjs" %*\r
4135
4617
  return `Global command: add ${binDirWin} to your PATH manually`;
4136
4618
  }
4137
4619
  }
4138
- const wrapper = join9(BIN_DIR, "conare");
4620
+ const wrapper = join11(BIN_DIR, "conare");
4139
4621
  const content = `#!/bin/bash
4140
4622
  # Conare global command — runs the persisted CLI bundle
4141
4623
  CONARE_DIR="$HOME/.conare"
@@ -4156,7 +4638,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
4156
4638
  chmodSync(wrapper, 493);
4157
4639
  const symlinkTarget = "/usr/local/bin/conare";
4158
4640
  try {
4159
- if (existsSync9(symlinkTarget)) {
4641
+ if (existsSync11(symlinkTarget)) {
4160
4642
  try {
4161
4643
  const existing = readlinkSync2(symlinkTarget);
4162
4644
  if (existing === wrapper)
@@ -4174,7 +4656,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
4174
4656
  const shellProfile = getShellProfile();
4175
4657
  if (shellProfile) {
4176
4658
  try {
4177
- const profileContent = existsSync9(shellProfile) ? readFileSync8(shellProfile, "utf-8") : "";
4659
+ const profileContent = existsSync11(shellProfile) ? readFileSync10(shellProfile, "utf-8") : "";
4178
4660
  const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
4179
4661
  if (!profileContent.includes(".conare/bin")) {
4180
4662
  appendFileSync(shellProfile, `
@@ -4192,24 +4674,24 @@ ${exportLine}
4192
4674
  }
4193
4675
  }
4194
4676
  function getShellProfile() {
4195
- const home = homedir8();
4677
+ const home = homedir10();
4196
4678
  const shell = process.env.SHELL || "";
4197
4679
  if (shell.includes("zsh"))
4198
- return join9(home, ".zshrc");
4680
+ return join11(home, ".zshrc");
4199
4681
  if (shell.includes("bash")) {
4200
- const profile = join9(home, ".bash_profile");
4201
- if (platform6() === "darwin" && existsSync9(profile))
4682
+ const profile = join11(home, ".bash_profile");
4683
+ if (platform6() === "darwin" && existsSync11(profile))
4202
4684
  return profile;
4203
- return join9(home, ".bashrc");
4685
+ return join11(home, ".bashrc");
4204
4686
  }
4205
- if (existsSync9(join9(home, ".zshrc")))
4206
- return join9(home, ".zshrc");
4207
- if (existsSync9(join9(home, ".bashrc")))
4208
- 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");
4209
4691
  return null;
4210
4692
  }
4211
4693
  function setupWindows(intervalMinutes) {
4212
- const runVbs = join9(BIN_DIR, "run.vbs").replace(/\//g, "\\");
4694
+ const runVbs = join11(BIN_DIR, "run.vbs").replace(/\//g, "\\");
4213
4695
  try {
4214
4696
  execSync3(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
4215
4697
  } catch {}
@@ -4349,18 +4831,18 @@ function detectMechanism() {
4349
4831
  }
4350
4832
  function syncStatus() {
4351
4833
  const mechanism = detectMechanism();
4352
- const logPath = join9(CONARE_DIR, "ingest.log");
4834
+ const logPath = join11(CONARE_DIR, "ingest.log");
4353
4835
  let parsed = { lastSyncIso: null, lastSyncError: null };
4354
- if (existsSync9(logPath)) {
4836
+ if (existsSync11(logPath)) {
4355
4837
  try {
4356
- parsed = parseLastSync(readFileSync8(logPath, "utf-8"));
4838
+ parsed = parseLastSync(readFileSync10(logPath, "utf-8"));
4357
4839
  } catch {}
4358
4840
  }
4359
4841
  return {
4360
4842
  timerInstalled: syncTimerInstalled(),
4361
4843
  mechanism,
4362
- binaryPersisted: existsSync9(join9(BIN_DIR, "conare-ingest.mjs")),
4363
- configPresent: existsSync9(CONFIG_PATH),
4844
+ binaryPersisted: existsSync11(join11(BIN_DIR, "conare-ingest.mjs")),
4845
+ configPresent: existsSync11(CONFIG_PATH),
4364
4846
  lastSyncIso: parsed.lastSyncIso,
4365
4847
  lastSyncError: parsed.lastSyncError
4366
4848
  };
@@ -4370,7 +4852,7 @@ function removeSyncTimer() {
4370
4852
  const os = platform6();
4371
4853
  if (os === "darwin") {
4372
4854
  bootoutLaunchAgent();
4373
- if (existsSync9(PLIST_PATH)) {
4855
+ if (existsSync11(PLIST_PATH)) {
4374
4856
  unlinkSync2(PLIST_PATH);
4375
4857
  messages.push("Removed launchd agent");
4376
4858
  }
@@ -4385,9 +4867,9 @@ function removeSyncTimer() {
4385
4867
  try {
4386
4868
  execSync3("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
4387
4869
  } catch {}
4388
- if (existsSync9(SYSTEMD_SERVICE))
4870
+ if (existsSync11(SYSTEMD_SERVICE))
4389
4871
  unlinkSync2(SYSTEMD_SERVICE);
4390
- if (existsSync9(SYSTEMD_TIMER))
4872
+ if (existsSync11(SYSTEMD_TIMER))
4391
4873
  unlinkSync2(SYSTEMD_TIMER);
4392
4874
  try {
4393
4875
  execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
@@ -4400,19 +4882,19 @@ function removeSyncTimer() {
4400
4882
  return messages;
4401
4883
  }
4402
4884
  function clearSyncLocks() {
4403
- const lockDir = join9(CONARE_DIR, "sync.lock.d");
4404
- if (existsSync9(lockDir))
4885
+ const lockDir = join11(CONARE_DIR, "sync.lock.d");
4886
+ if (existsSync11(lockDir))
4405
4887
  rmSync2(lockDir, { recursive: true, force: true });
4406
- const lockFile = join9(CONARE_DIR, "sync.lock");
4407
- if (existsSync9(lockFile))
4888
+ const lockFile = join11(CONARE_DIR, "sync.lock");
4889
+ if (existsSync11(lockFile))
4408
4890
  unlinkSync2(lockFile);
4409
4891
  }
4410
4892
  function uninstallSync() {
4411
4893
  const messages = removeSyncTimer();
4412
- if (existsSync9(CONFIG_PATH))
4894
+ if (existsSync11(CONFIG_PATH))
4413
4895
  unlinkSync2(CONFIG_PATH);
4414
4896
  clearSyncLocks();
4415
- if (existsSync9(BIN_DIR)) {
4897
+ if (existsSync11(BIN_DIR)) {
4416
4898
  rmSync2(BIN_DIR, { recursive: true, force: true });
4417
4899
  messages.push("Removed ~/.conare/bin/");
4418
4900
  }
@@ -4438,7 +4920,9 @@ var CONARE_URL2 = "https://conare.ai";
4438
4920
  var CHAT_CONTAINER_LABELS = {
4439
4921
  "claude-chats": "Claude Code",
4440
4922
  "codex-chats": "Codex",
4441
- "cursor-chats": "Cursor"
4923
+ "cursor-chats": "Cursor",
4924
+ "opencode-chats": "OpenCode",
4925
+ "grok-chats": "Grok"
4442
4926
  };
4443
4927
  function getManifestFingerprint(memory) {
4444
4928
  const metadata = memory.metadata;
@@ -4595,7 +5079,7 @@ Options:
4595
5079
  --ingest-only Index memories without MCP configuration
4596
5080
  --config-only Configure MCP only, skip indexing
4597
5081
  --interactive Run guided setup prompts
4598
- --source <name> Only index from: claude, codex, cursor
5082
+ --source <name> Only index from: claude, codex, cursor, opencode, grok
4599
5083
  --wasm-dir <path> Path to sql.js module (for Cursor indexing)
4600
5084
 
4601
5085
  Get your API key at https://conare.ai
@@ -4733,8 +5217,8 @@ async function main() {
4733
5217
  let configFileKey;
4734
5218
  if (opts.configFile) {
4735
5219
  try {
4736
- const { readFileSync: readFileSync9 } = await import("node:fs");
4737
- 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"));
4738
5222
  configFileKey = raw.apiKey || raw.key;
4739
5223
  if (!configFileKey) {
4740
5224
  console.error(`Error: no apiKey/key found in ${opts.configFile}`);
@@ -4754,7 +5238,7 @@ async function main() {
4754
5238
  let effectiveConfigOnly = opts.configOnly;
4755
5239
  let effectiveIngestOnly = opts.ingestOnly;
4756
5240
  let effectiveIndexPath = opts.indexPath;
4757
- let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex"];
5241
+ let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex", "opencode", "grok"];
4758
5242
  let apiKey = opts.key || configFileKey || process.env.CONARE_API_KEY || savedApiKey;
4759
5243
  let interactiveTargets = MCP_TARGETS.map((target) => ({
4760
5244
  id: target.id,
@@ -4801,7 +5285,7 @@ async function main() {
4801
5285
  detectedCountApproximate: detected?.sessionCountApproximate
4802
5286
  };
4803
5287
  });
4804
- const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor"]);
5288
+ const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor", "opencode", "grok"]);
4805
5289
  const ingestibleTargets = interactiveTargets.filter((t) => INGESTIBLE_SOURCES.has(t.id));
4806
5290
  showDetectedApps(ingestibleTargets);
4807
5291
  selectedSources = await selectChatSources(ingestibleTargets);
@@ -4849,7 +5333,7 @@ async function main() {
4849
5333
  }
4850
5334
  saveApiKey(apiKey);
4851
5335
  if (auth.id)
4852
- setManifestAccount(auth.id);
5336
+ setManifestScope(auth.id, apiKey);
4853
5337
  if (!opts.force && !opts.dryRun) {
4854
5338
  const remoteMemoryCount = await getRemoteMemoryCount(apiKey);
4855
5339
  const localManifest = getIngested();
@@ -4871,8 +5355,8 @@ async function main() {
4871
5355
  }
4872
5356
  }
4873
5357
  }
4874
- if (!opts.wasmDir && existsSync10(join10(process.cwd(), "node_modules", "sql.js"))) {
4875
- 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");
4876
5360
  }
4877
5361
  if (effectiveConfigOnly) {
4878
5362
  if (!opts.dryRun) {
@@ -5019,11 +5503,11 @@ Nothing new to index.`);
5019
5503
  if (!opts.quiet) {
5020
5504
  const s = Y2();
5021
5505
  s.start("Scanning Claude Code chats...");
5022
- const { memories, filtered, deduped } = ingestClaude(undefined, { dedupSource: "claude" });
5506
+ const { memories, filtered, deduped } = ingestClaude();
5023
5507
  allMemories.push(...memories);
5024
5508
  s.stop(`Claude Code: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
5025
5509
  } else {
5026
- allMemories.push(...ingestClaude(undefined, { dedupSource: "claude" }).memories);
5510
+ allMemories.push(...ingestClaude().memories);
5027
5511
  }
5028
5512
  }
5029
5513
  if (shouldIngest("codex") && tools.find((t) => t.name === "Codex")?.available) {
@@ -5042,12 +5526,34 @@ Nothing new to index.`);
5042
5526
  const s = Y2();
5043
5527
  s.start("Scanning Cursor chats...");
5044
5528
  const cursorTool = tools.find((t) => t.name === "Cursor");
5045
- const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir, undefined, { dedupSource: "cursor" });
5529
+ const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir);
5046
5530
  allMemories.push(...memories);
5047
5531
  s.stop(`Cursor: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
5048
5532
  } else {
5049
5533
  const cursorTool = tools.find((t) => t.name === "Cursor");
5050
- 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);
5051
5557
  }
5052
5558
  }
5053
5559
  allMemories.sort((a, b3) => {
@@ -5133,7 +5639,9 @@ Nothing new to index.`);
5133
5639
  const successfulKeysBySource = {
5134
5640
  claude: [],
5135
5641
  codex: [],
5136
- cursor: []
5642
+ cursor: [],
5643
+ opencode: [],
5644
+ grok: []
5137
5645
  };
5138
5646
  for (const result of results) {
5139
5647
  if (!result.success)
@@ -5152,6 +5660,12 @@ Nothing new to index.`);
5152
5660
  case "cursor-chats":
5153
5661
  successfulKeysBySource.cursor.push(key);
5154
5662
  break;
5663
+ case "opencode-chats":
5664
+ successfulKeysBySource.opencode.push(key);
5665
+ break;
5666
+ case "grok-chats":
5667
+ successfulKeysBySource.grok.push(key);
5668
+ break;
5155
5669
  }
5156
5670
  }
5157
5671
  for (const [source, ids] of Object.entries(successfulKeysBySource)) {
@@ -5207,6 +5721,10 @@ Nothing new to index.`);
5207
5721
  return "Codex";
5208
5722
  if (t === "cursor")
5209
5723
  return "Cursor";
5724
+ if (t === "opencode")
5725
+ return "OpenCode";
5726
+ if (t === "grok")
5727
+ return "Grok";
5210
5728
  return t;
5211
5729
  });
5212
5730
  const toolList = configuredTools.length > 0 ? configuredTools.join(", ") : "your AI tools";