conare 0.6.7 → 0.6.9

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 +474 -299
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -269,16 +269,16 @@ __export(exports_codebase, {
269
269
  detectProjectName: () => detectProjectName
270
270
  });
271
271
  import { createHash as createHash2 } from "node:crypto";
272
- import { readdirSync as readdirSync7, readFileSync as readFileSync7, statSync as statSync2, existsSync as existsSync8 } from "node:fs";
273
- import { join as join8, relative, extname, resolve, basename as basename3 } from "node:path";
272
+ import { readdirSync as readdirSync8, readFileSync as readFileSync8, statSync as statSync2, existsSync as existsSync9 } from "node:fs";
273
+ import { join as join9, relative, extname, resolve, basename as basename4 } from "node:path";
274
274
  import { execSync as execSync2 } from "node:child_process";
275
275
  function parseGitignore(rootPath) {
276
276
  const patterns = new Set;
277
- const gitignorePath = join8(rootPath, ".gitignore");
278
- if (!existsSync8(gitignorePath))
277
+ const gitignorePath = join9(rootPath, ".gitignore");
278
+ if (!existsSync9(gitignorePath))
279
279
  return patterns;
280
280
  try {
281
- const content = readFileSync7(gitignorePath, "utf-8");
281
+ const content = readFileSync8(gitignorePath, "utf-8");
282
282
  for (const line of content.split(`
283
283
  `)) {
284
284
  const trimmed = line.trim();
@@ -314,15 +314,15 @@ ${content}
314
314
  function detectProjectName(rootPath) {
315
315
  const readers = [
316
316
  () => {
317
- const pkg = JSON.parse(readFileSync7(join8(rootPath, "package.json"), "utf-8"));
317
+ const pkg = JSON.parse(readFileSync8(join9(rootPath, "package.json"), "utf-8"));
318
318
  return typeof pkg.name === "string" ? pkg.name : null;
319
319
  },
320
320
  () => {
321
- const content = readFileSync7(join8(rootPath, "Cargo.toml"), "utf-8");
321
+ const content = readFileSync8(join9(rootPath, "Cargo.toml"), "utf-8");
322
322
  return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
323
323
  },
324
324
  () => {
325
- const content = readFileSync7(join8(rootPath, "pyproject.toml"), "utf-8");
325
+ const content = readFileSync8(join9(rootPath, "pyproject.toml"), "utf-8");
326
326
  return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
327
327
  }
328
328
  ];
@@ -333,7 +333,7 @@ function detectProjectName(rootPath) {
333
333
  return name;
334
334
  } catch {}
335
335
  }
336
- return basename3(resolve(rootPath));
336
+ return basename4(resolve(rootPath));
337
337
  }
338
338
  function getChangedFiles(rootPath) {
339
339
  try {
@@ -384,14 +384,14 @@ function indexCodebase(rootPath, options = {}) {
384
384
  function walk(dir) {
385
385
  let entries;
386
386
  try {
387
- entries = readdirSync7(dir, { withFileTypes: true });
387
+ entries = readdirSync8(dir, { withFileTypes: true });
388
388
  } catch {
389
389
  return;
390
390
  }
391
391
  for (const entry of entries) {
392
392
  if (shouldIgnore(entry.name, gitignorePatterns))
393
393
  continue;
394
- const fullPath = join8(dir, entry.name);
394
+ const fullPath = join9(dir, entry.name);
395
395
  if (entry.isDirectory()) {
396
396
  walk(fullPath);
397
397
  continue;
@@ -419,7 +419,7 @@ function indexCodebase(rootPath, options = {}) {
419
419
  }
420
420
  let raw;
421
421
  try {
422
- raw = readFileSync7(fullPath, "utf-8");
422
+ raw = readFileSync8(fullPath, "utf-8");
423
423
  } catch {
424
424
  skipped++;
425
425
  continue;
@@ -677,7 +677,7 @@ async function validateKey(apiKey) {
677
677
  }
678
678
  }
679
679
  async function fetchServerDedupFingerprints(apiKey) {
680
- const bySource = { claude: [], codex: [], cursor: [] };
680
+ const bySource = Object.fromEntries(Object.values(CONTAINER_TO_SOURCE).map((s) => [s, []]));
681
681
  await Promise.all(Object.entries(CONTAINER_TO_SOURCE).map(async ([containerTag, source]) => {
682
682
  const data = await apiRequest(`/api/memories/dedup-keys?containerTag=${encodeURIComponent(containerTag)}`, apiKey);
683
683
  for (const k of data.keys || []) {
@@ -703,7 +703,7 @@ async function getRemoteChatMemoryCount(apiKey) {
703
703
  const data = await apiRequest("/api/containers", apiKey);
704
704
  if (!Array.isArray(data.containers))
705
705
  return 0;
706
- const chatContainers = new Set(["claude-chats", "codex-chats", "cursor-chats"]);
706
+ const chatContainers = new Set(Object.keys(CONTAINER_TO_SOURCE));
707
707
  return data.containers.filter((c) => c.tag && chatContainers.has(c.tag)).reduce((sum, c) => sum + (c.count || 0), 0);
708
708
  } catch {
709
709
  return null;
@@ -716,11 +716,11 @@ async function getBillingStatus(apiKey) {
716
716
  return null;
717
717
  }
718
718
  }
719
- async function recordSyncCheckIn(apiKey, source = "cli", memoryDelta = 0) {
719
+ async function recordSyncCheckIn(apiKey, source = "cli", memoryDelta = 0, choices) {
720
720
  try {
721
721
  const data = await apiRequest("/api/sync/check-in", apiKey, {
722
722
  method: "POST",
723
- body: JSON.stringify({ source, memoryDelta })
723
+ body: JSON.stringify({ source, memoryDelta, ...choices })
724
724
  });
725
725
  return typeof data.lastSyncAt === "number" ? data.lastSyncAt : null;
726
726
  } catch {
@@ -820,7 +820,10 @@ var init_api = __esm(() => {
820
820
  CONTAINER_TO_SOURCE = {
821
821
  "claude-chats": "claude",
822
822
  "codex-chats": "codex",
823
- "cursor-chats": "cursor"
823
+ "cursor-chats": "cursor",
824
+ "opencode-chats": "opencode",
825
+ "grok-chats": "grok",
826
+ "pi-chats": "pi"
824
827
  };
825
828
  });
826
829
 
@@ -1777,13 +1780,14 @@ var init_interactive = __esm(() => {
1777
1780
  });
1778
1781
 
1779
1782
  // src/index.ts
1780
- import { existsSync as existsSync12 } from "node:fs";
1781
- import { join as join12 } from "node:path";
1783
+ import { existsSync as existsSync13 } from "node:fs";
1784
+ import { join as join13 } from "node:path";
1782
1785
 
1783
1786
  // src/detect.ts
1784
- import { existsSync as existsSync7, readdirSync as readdirSync6 } from "node:fs";
1785
- import { join as join7 } from "node:path";
1786
- import { homedir as homedir7, platform as platform3 } from "node:os";
1787
+ import { existsSync as existsSync8, readdirSync as readdirSync7 } from "node:fs";
1788
+ import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
1789
+ import { join as join8 } from "node:path";
1790
+ import { homedir as homedir8, platform as platform3 } from "node:os";
1787
1791
 
1788
1792
  // src/ingest/claude.ts
1789
1793
  init_shared();
@@ -3183,14 +3187,251 @@ async function countImportableGrokSessions(onProgress) {
3183
3187
  return found;
3184
3188
  }
3185
3189
 
3190
+ // src/ingest/pi.ts
3191
+ init_shared();
3192
+ import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as readdirSync6 } from "node:fs";
3193
+ import { join as join7, basename as basename3 } from "node:path";
3194
+ import { homedir as homedir7 } from "node:os";
3195
+ function sessionsDirs() {
3196
+ const override = process.env.PI_CODING_AGENT_SESSION_DIR;
3197
+ if (override)
3198
+ return override.split(",").map((d) => d.trim()).filter(Boolean);
3199
+ return [join7(homedir7(), ".pi", "agent", "sessions")];
3200
+ }
3201
+ function projectFromCwd4(cwd) {
3202
+ const home = homedir7();
3203
+ const normalized = cwd.replace(/\\/g, "/");
3204
+ const normalizedHome = home.replace(/\\/g, "/");
3205
+ if (normalized.startsWith(normalizedHome + "/")) {
3206
+ return normalized.slice(normalizedHome.length + 1);
3207
+ }
3208
+ return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
3209
+ }
3210
+ function textFromContent(content) {
3211
+ if (typeof content === "string")
3212
+ return content;
3213
+ if (!Array.isArray(content))
3214
+ return "";
3215
+ return content.filter((b) => !!b && typeof b === "object").filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
3216
+ `);
3217
+ }
3218
+ function parsePiSession(lines) {
3219
+ let cwd = null;
3220
+ let project = null;
3221
+ let startedAt = null;
3222
+ const byId = new Map;
3223
+ const order = [];
3224
+ const childless = new Set;
3225
+ for (const line of lines) {
3226
+ let obj;
3227
+ try {
3228
+ obj = JSON.parse(line);
3229
+ } catch {
3230
+ continue;
3231
+ }
3232
+ if (obj.type === "session") {
3233
+ if (typeof obj.cwd === "string") {
3234
+ cwd = obj.cwd;
3235
+ project = projectFromCwd4(obj.cwd);
3236
+ }
3237
+ if (typeof obj.timestamp === "string")
3238
+ startedAt = obj.timestamp;
3239
+ continue;
3240
+ }
3241
+ if (obj.type !== "message" || !obj.message || typeof obj.id !== "string")
3242
+ continue;
3243
+ const role = obj.message.role;
3244
+ const text = role === "user" || role === "assistant" ? cleanText(textFromContent(obj.message.content)) : "";
3245
+ const entry = {
3246
+ id: obj.id,
3247
+ parentId: typeof obj.parentId === "string" ? obj.parentId : null,
3248
+ role,
3249
+ text,
3250
+ timestamp: typeof obj.timestamp === "string" ? obj.timestamp : null
3251
+ };
3252
+ byId.set(entry.id, entry);
3253
+ order.push(entry.id);
3254
+ childless.add(entry.id);
3255
+ if (entry.parentId)
3256
+ childless.delete(entry.parentId);
3257
+ }
3258
+ let leafId = null;
3259
+ for (let i = order.length - 1;i >= 0; i--) {
3260
+ if (childless.has(order[i])) {
3261
+ leafId = order[i];
3262
+ break;
3263
+ }
3264
+ }
3265
+ const path = [];
3266
+ const seen = new Set;
3267
+ let cur = leafId;
3268
+ while (cur && byId.has(cur) && !seen.has(cur)) {
3269
+ seen.add(cur);
3270
+ const e = byId.get(cur);
3271
+ path.push(e);
3272
+ cur = e.parentId;
3273
+ }
3274
+ path.reverse();
3275
+ const rounds = [];
3276
+ let currentUser = null;
3277
+ let currentAssistant = [];
3278
+ let updatedAt = null;
3279
+ for (const e of path) {
3280
+ if (e.timestamp)
3281
+ updatedAt = e.timestamp;
3282
+ if (e.role === "user") {
3283
+ if (!e.text || e.text.length < 50)
3284
+ continue;
3285
+ if (currentUser !== null && currentAssistant.length > 0) {
3286
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
3287
+ }
3288
+ currentUser = e.text;
3289
+ currentAssistant = [];
3290
+ } else if (e.role === "assistant") {
3291
+ if (e.text && !isNarration(e.text))
3292
+ currentAssistant.push(e.text);
3293
+ }
3294
+ }
3295
+ if (currentUser !== null && currentAssistant.length > 0) {
3296
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
3297
+ }
3298
+ const date = (updatedAt ?? startedAt)?.slice(0, 10) ?? null;
3299
+ const sourceTimestamp = parseTimestampMs(updatedAt ?? startedAt);
3300
+ return { rounds, date, startedAt, updatedAt, sourceTimestamp, project, cwd };
3301
+ }
3302
+ function ingestPi(projectRoots, opts) {
3303
+ const memories = [];
3304
+ const sessionIds = [];
3305
+ let filtered = 0;
3306
+ let deduped = 0;
3307
+ for (const dir of sessionsDirs()) {
3308
+ if (!existsSync7(dir))
3309
+ continue;
3310
+ try {
3311
+ const stats = { filtered: 0, deduped: 0 };
3312
+ walkPiSessions(dir, memories, sessionIds, stats, projectRoots, opts?.includeIngested);
3313
+ filtered += stats.filtered;
3314
+ deduped += stats.deduped;
3315
+ } catch {}
3316
+ }
3317
+ return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
3318
+ }
3319
+ function walkPiSessionFiles(dir, visit) {
3320
+ try {
3321
+ for (const entry of readdirSync6(dir, { withFileTypes: true })) {
3322
+ const fullPath = join7(dir, entry.name);
3323
+ if (entry.isDirectory()) {
3324
+ walkPiSessionFiles(fullPath, visit);
3325
+ } else if (entry.name.endsWith(".jsonl")) {
3326
+ visit(fullPath);
3327
+ }
3328
+ }
3329
+ } catch {}
3330
+ }
3331
+ async function countImportablePiSessions(onProgress) {
3332
+ const dirs = sessionsDirs().filter(existsSync7);
3333
+ if (dirs.length === 0)
3334
+ return 0;
3335
+ const { readFile } = await import("node:fs/promises");
3336
+ const files = [];
3337
+ for (const dir of dirs)
3338
+ walkPiSessionFiles(dir, (f) => files.push(f));
3339
+ const total = files.length;
3340
+ let count = 0;
3341
+ let checked = 0;
3342
+ const CONCURRENCY = 16;
3343
+ let nextIdx = 0;
3344
+ const worker = async () => {
3345
+ while (true) {
3346
+ const i = nextIdx++;
3347
+ if (i >= files.length)
3348
+ return;
3349
+ try {
3350
+ const lines = (await readFile(files[i], "utf-8")).split(`
3351
+ `).filter(Boolean);
3352
+ const { rounds } = parsePiSession(lines);
3353
+ if (rounds.length > 0)
3354
+ count++;
3355
+ } catch {}
3356
+ checked++;
3357
+ if (checked % 100 === 0)
3358
+ onProgress?.({ checked, found: count, total });
3359
+ }
3360
+ };
3361
+ await Promise.all(Array.from({ length: CONCURRENCY }, () => worker()));
3362
+ onProgress?.({ checked, found: count, total });
3363
+ return count;
3364
+ }
3365
+ function walkPiSessions(dir, memories, sessionIds, stats, projectRoots, includeIngested) {
3366
+ try {
3367
+ for (const entry of readdirSync6(dir, { withFileTypes: true })) {
3368
+ if (entry.isDirectory()) {
3369
+ walkPiSessions(join7(dir, entry.name), memories, sessionIds, stats, projectRoots, includeIngested);
3370
+ continue;
3371
+ }
3372
+ if (!entry.name.endsWith(".jsonl"))
3373
+ continue;
3374
+ const sessionId = basename3(entry.name, ".jsonl");
3375
+ try {
3376
+ const lines = readFileSync7(join7(dir, entry.name), "utf-8").split(`
3377
+ `).filter(Boolean);
3378
+ const { rounds, date, startedAt, updatedAt, sourceTimestamp, project, cwd } = parsePiSession(lines);
3379
+ if (rounds.length === 0) {
3380
+ stats.filtered++;
3381
+ continue;
3382
+ }
3383
+ if (!matchesProjectFilter(cwd, projectRoots)) {
3384
+ stats.filtered++;
3385
+ continue;
3386
+ }
3387
+ const header = `# Pi Session${project ? `: ${project}` : ""} | ${date || "unknown"}`;
3388
+ const turns = rounds.map((r) => ({
3389
+ user: r.user,
3390
+ assistant: r.assistantParts.join(`
3391
+
3392
+ `)
3393
+ }));
3394
+ const content = fitContent(header, turns);
3395
+ const contentHash = createContentHash(content);
3396
+ const dedupKey = `pi:${sessionId}`;
3397
+ const fingerprint = `${dedupKey}:${contentHash}`;
3398
+ if (!includeIngested && isIngested("pi", fingerprint)) {
3399
+ stats.deduped++;
3400
+ continue;
3401
+ }
3402
+ const sourceRemote = cwd ? repoRemote(cwd) : undefined;
3403
+ memories.push({
3404
+ content,
3405
+ containerTag: "pi-chats",
3406
+ metadata: {
3407
+ dedupKey,
3408
+ contentHash,
3409
+ source: "pi-session",
3410
+ sessionId,
3411
+ ...cwd ? { sourceRoot: cwd } : {},
3412
+ ...sourceRemote ? { sourceRemote } : {},
3413
+ date: date || "unknown",
3414
+ ...sourceTimestamp ? { sourceTimestamp } : {},
3415
+ ...startedAt ? { sessionStartedAt: startedAt } : {},
3416
+ ...updatedAt ? { sessionUpdatedAt: updatedAt } : {},
3417
+ ...project ? { project } : {}
3418
+ },
3419
+ ...sourceTimestamp ? { created_at: sourceTimestamp, updated_at: sourceTimestamp } : {}
3420
+ });
3421
+ sessionIds.push(sessionId);
3422
+ } catch {}
3423
+ }
3424
+ } catch {}
3425
+ }
3426
+
3186
3427
  // src/detect.ts
3187
3428
  async function detect(options = {}) {
3188
- const home = homedir7();
3429
+ const home = homedir8();
3189
3430
  const os = platform3();
3190
3431
  const tools = [];
3191
3432
  const onProgress = options.onProgress;
3192
- const claudeDir = join7(home, ".claude", "projects");
3193
- if (existsSync7(claudeDir)) {
3433
+ const claudeDir = join8(home, ".claude", "projects");
3434
+ if (existsSync8(claudeDir)) {
3194
3435
  onProgress?.({ tool: "Claude Code", status: "start" });
3195
3436
  const sessionCount = await countImportableClaudeSessions((progress) => {
3196
3437
  onProgress?.({ tool: "Claude Code", status: "progress", ...progress });
@@ -3201,9 +3442,9 @@ async function detect(options = {}) {
3201
3442
  onProgress?.({ tool: "Claude Code", status: "skip", reason: "not found" });
3202
3443
  tools.push({ name: "Claude Code", id: "claude", available: false, path: claudeDir, sessionCount: 0 });
3203
3444
  }
3204
- const codexConfig = join7(home, ".codex", "config.toml");
3205
- const codexSessions = join7(home, ".codex", "sessions");
3206
- if (existsSync7(codexConfig) || existsSync7(codexSessions)) {
3445
+ const codexConfig = join8(home, ".codex", "config.toml");
3446
+ const codexSessions = join8(home, ".codex", "sessions");
3447
+ if (existsSync8(codexConfig) || existsSync8(codexSessions)) {
3207
3448
  onProgress?.({ tool: "Codex", status: "start" });
3208
3449
  const sessionCount = await countImportableCodexSessions((progress) => {
3209
3450
  onProgress?.({ tool: "Codex", status: "progress", ...progress });
@@ -3216,13 +3457,13 @@ async function detect(options = {}) {
3216
3457
  }
3217
3458
  let cursorDbPath;
3218
3459
  if (os === "darwin") {
3219
- cursorDbPath = join7(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
3460
+ cursorDbPath = join8(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
3220
3461
  } else if (os === "win32") {
3221
- cursorDbPath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
3462
+ cursorDbPath = join8(process.env.APPDATA || join8(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
3222
3463
  } else {
3223
- cursorDbPath = join7(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
3464
+ cursorDbPath = join8(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
3224
3465
  }
3225
- const cursorExists = existsSync7(cursorDbPath);
3466
+ const cursorExists = existsSync8(cursorDbPath);
3226
3467
  if (cursorExists)
3227
3468
  onProgress?.({ tool: "Cursor", status: "start" });
3228
3469
  else
@@ -3240,8 +3481,8 @@ async function detect(options = {}) {
3240
3481
  sessionCount: cursorCount,
3241
3482
  sessionCountApproximate: true
3242
3483
  });
3243
- const opencodeStorage = join7(process.env.OPENCODE_DATA_DIR?.split(",")[0]?.trim() || join7(home, ".local", "share", "opencode"), "storage");
3244
- const opencodeExists = existsSync7(opencodeStorage);
3484
+ const opencodeStorage = join8(process.env.OPENCODE_DATA_DIR?.split(",")[0]?.trim() || join8(home, ".local", "share", "opencode"), "storage");
3485
+ const opencodeExists = existsSync8(opencodeStorage);
3245
3486
  if (opencodeExists)
3246
3487
  onProgress?.({ tool: "OpenCode", status: "start" });
3247
3488
  else
@@ -3255,12 +3496,12 @@ async function detect(options = {}) {
3255
3496
  name: "OpenCode",
3256
3497
  id: "opencode",
3257
3498
  available: opencodeExists,
3258
- path: join7(home, ".config", "opencode", "opencode.json"),
3499
+ path: join8(home, ".config", "opencode", "opencode.json"),
3259
3500
  sessionCount: opencodeCount,
3260
3501
  sessionCountApproximate: true
3261
3502
  });
3262
- const grokSessions = join7(process.env.GROK_DATA_DIR?.split(",")[0]?.trim() || join7(home, ".grok"), "sessions");
3263
- const grokExists = existsSync7(grokSessions);
3503
+ const grokSessions = join8(process.env.GROK_DATA_DIR?.split(",")[0]?.trim() || join8(home, ".grok"), "sessions");
3504
+ const grokExists = existsSync8(grokSessions);
3264
3505
  if (grokExists)
3265
3506
  onProgress?.({ tool: "Grok", status: "start" });
3266
3507
  else
@@ -3274,77 +3515,98 @@ async function detect(options = {}) {
3274
3515
  name: "Grok",
3275
3516
  id: "grok",
3276
3517
  available: grokExists,
3277
- path: join7(home, ".grok", "config.toml"),
3518
+ path: join8(home, ".grok", "config.toml"),
3278
3519
  sessionCount: grokCount,
3279
3520
  sessionCountApproximate: true
3280
3521
  });
3281
- const windsurfDir = join7(home, ".codeium", "windsurf");
3522
+ const piSessions = join8(process.env.PI_CODING_AGENT_SESSION_DIR?.split(",")[0]?.trim() || join8(home, ".pi", "agent", "sessions"));
3523
+ const piSessionsExist = existsSync8(piSessions);
3524
+ const piOnPath = spawnSync("pi", ["--version"], { stdio: "ignore", shell: platform3() === "win32" }).status === 0;
3525
+ const piExists = piSessionsExist || piOnPath;
3526
+ if (piSessionsExist)
3527
+ onProgress?.({ tool: "Pi", status: "start" });
3528
+ else
3529
+ onProgress?.({ tool: "Pi", status: "skip", reason: piOnPath ? "no chats yet" : "not found" });
3530
+ const piCount = piSessionsExist ? await countImportablePiSessions((progress) => {
3531
+ onProgress?.({ tool: "Pi", status: "progress", ...progress });
3532
+ }) : 0;
3533
+ if (piSessionsExist)
3534
+ onProgress?.({ tool: "Pi", status: "done", count: piCount });
3535
+ tools.push({
3536
+ name: "Pi",
3537
+ id: "pi",
3538
+ available: piExists,
3539
+ path: join8(home, ".pi", "agent", "extensions", "conare.ts"),
3540
+ sessionCount: piCount,
3541
+ sessionCountApproximate: true
3542
+ });
3543
+ const windsurfDir = join8(home, ".codeium", "windsurf");
3282
3544
  tools.push({
3283
3545
  name: "Windsurf",
3284
3546
  id: "windsurf",
3285
- available: existsSync7(windsurfDir),
3286
- path: join7(windsurfDir, "mcp_config.json"),
3547
+ available: existsSync8(windsurfDir),
3548
+ path: join8(windsurfDir, "mcp_config.json"),
3287
3549
  sessionCount: 0
3288
3550
  });
3289
3551
  let vscodePath;
3290
3552
  if (os === "darwin") {
3291
- vscodePath = join7(home, "Library", "Application Support", "Code");
3553
+ vscodePath = join8(home, "Library", "Application Support", "Code");
3292
3554
  } else if (os === "win32") {
3293
- vscodePath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Code");
3555
+ vscodePath = join8(process.env.APPDATA || join8(home, "AppData", "Roaming"), "Code");
3294
3556
  } else {
3295
- vscodePath = join7(home, ".config", "Code");
3557
+ vscodePath = join8(home, ".config", "Code");
3296
3558
  }
3297
3559
  tools.push({
3298
3560
  name: "VS Code Copilot",
3299
3561
  id: "vscode",
3300
- available: existsSync7(vscodePath),
3301
- path: join7(vscodePath, "User", "mcp.json"),
3562
+ available: existsSync8(vscodePath),
3563
+ path: join8(vscodePath, "User", "mcp.json"),
3302
3564
  sessionCount: 0
3303
3565
  });
3304
3566
  let clinePath;
3305
3567
  if (os === "darwin") {
3306
- clinePath = join7(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3568
+ clinePath = join8(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3307
3569
  } else if (os === "win32") {
3308
- clinePath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3570
+ clinePath = join8(process.env.APPDATA || join8(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3309
3571
  } else {
3310
- clinePath = join7(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3572
+ clinePath = join8(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
3311
3573
  }
3312
3574
  tools.push({
3313
3575
  name: "Cline",
3314
3576
  id: "cline",
3315
- available: existsSync7(clinePath),
3316
- path: join7(clinePath, "settings", "cline_mcp_settings.json"),
3577
+ available: existsSync8(clinePath),
3578
+ path: join8(clinePath, "settings", "cline_mcp_settings.json"),
3317
3579
  sessionCount: 0
3318
3580
  });
3319
- const zedPath = os === "darwin" ? join7(home, ".zed") : join7(home, ".config", "zed");
3581
+ const zedPath = os === "darwin" ? join8(home, ".zed") : join8(home, ".config", "zed");
3320
3582
  tools.push({
3321
3583
  name: "Zed",
3322
3584
  id: "zed",
3323
- available: existsSync7(zedPath),
3324
- path: join7(zedPath, "settings.json"),
3585
+ available: existsSync8(zedPath),
3586
+ path: join8(zedPath, "settings.json"),
3325
3587
  sessionCount: 0
3326
3588
  });
3327
- const openclawDir = join7(home, ".openclaw");
3589
+ const openclawDir = join8(home, ".openclaw");
3328
3590
  tools.push({
3329
3591
  name: "OpenClaw",
3330
3592
  id: "openclaw",
3331
- available: existsSync7(openclawDir),
3332
- path: join7(openclawDir, "openclaw.json"),
3593
+ available: existsSync8(openclawDir),
3594
+ path: join8(openclawDir, "openclaw.json"),
3333
3595
  sessionCount: 0
3334
3596
  });
3335
- const antigravityDir = join7(home, ".gemini", "antigravity");
3336
- const antigravityConvDir = join7(antigravityDir, "conversations");
3597
+ const antigravityDir = join8(home, ".gemini", "antigravity");
3598
+ const antigravityConvDir = join8(antigravityDir, "conversations");
3337
3599
  let antigravityCount = 0;
3338
- if (existsSync7(antigravityConvDir)) {
3600
+ if (existsSync8(antigravityConvDir)) {
3339
3601
  try {
3340
- antigravityCount = readdirSync6(antigravityConvDir).filter((f) => f.endsWith(".pb")).length;
3602
+ antigravityCount = readdirSync7(antigravityConvDir).filter((f) => f.endsWith(".pb")).length;
3341
3603
  } catch {}
3342
3604
  }
3343
3605
  tools.push({
3344
3606
  name: "Antigravity",
3345
3607
  id: "antigravity",
3346
- available: existsSync7(antigravityDir),
3347
- path: join7(antigravityDir, "mcp_config.json"),
3608
+ available: existsSync8(antigravityDir),
3609
+ path: join8(antigravityDir, "mcp_config.json"),
3348
3610
  sessionCount: antigravityCount
3349
3611
  });
3350
3612
  return tools;
@@ -3438,10 +3700,10 @@ init_shared();
3438
3700
  init_api();
3439
3701
 
3440
3702
  // src/configure.ts
3441
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync2, symlinkSync, readlinkSync, rmSync } from "node:fs";
3442
- import { dirname as dirname2, join as join9 } from "node:path";
3443
- import { homedir as homedir8, platform as platform5 } from "node:os";
3444
- import { spawnSync } from "node:child_process";
3703
+ import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync2, rmSync } from "node:fs";
3704
+ import { dirname as dirname2, join as join10 } from "node:path";
3705
+ import { homedir as homedir9, platform as platform5 } from "node:os";
3706
+ import { spawnSync as spawnSync2 } from "node:child_process";
3445
3707
  var CONARE_URL = "https://conare.ai";
3446
3708
  var SERVER_NAME = "conare";
3447
3709
  var MCP_TARGETS = [
@@ -3455,11 +3717,12 @@ var MCP_TARGETS = [
3455
3717
  { id: "openclaw", label: "OpenClaw", defaultSelected: false },
3456
3718
  { id: "antigravity", label: "Antigravity", defaultSelected: false },
3457
3719
  { id: "opencode", label: "OpenCode", defaultSelected: false },
3458
- { id: "grok", label: "Grok", defaultSelected: false }
3720
+ { id: "grok", label: "Grok", defaultSelected: false },
3721
+ { id: "pi", label: "Pi", defaultSelected: false }
3459
3722
  ];
3460
3723
  function readJsonFile(path) {
3461
3724
  try {
3462
- return JSON.parse(readFileSync8(path, "utf-8"));
3725
+ return JSON.parse(readFileSync9(path, "utf-8"));
3463
3726
  } catch {
3464
3727
  return {};
3465
3728
  }
@@ -3479,13 +3742,13 @@ function getServerConfig(apiKey) {
3479
3742
  };
3480
3743
  }
3481
3744
  function configureClaude(apiKey) {
3482
- if (spawnSync("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
3745
+ if (spawnSync2("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
3483
3746
  stdio: "ignore",
3484
3747
  shell: platform5() === "win32"
3485
3748
  }).status === 0) {
3486
3749
  return "\x1B[32m✓\x1B[0m Claude Code";
3487
3750
  }
3488
- if (spawnSync("claude", [
3751
+ if (spawnSync2("claude", [
3489
3752
  "mcp",
3490
3753
  "add",
3491
3754
  SERVER_NAME,
@@ -3503,7 +3766,7 @@ function configureClaude(apiKey) {
3503
3766
  }).status === 0) {
3504
3767
  return "\x1B[32m✓\x1B[0m Claude Code";
3505
3768
  }
3506
- const claudeConfigPath = join9(homedir8(), ".claude.json");
3769
+ const claudeConfigPath = join10(homedir9(), ".claude.json");
3507
3770
  const config = readJsonFile(claudeConfigPath);
3508
3771
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3509
3772
  config.mcpServers = {};
@@ -3513,8 +3776,8 @@ function configureClaude(apiKey) {
3513
3776
  return "\x1B[32m✓\x1B[0m Claude Code (json fallback)";
3514
3777
  }
3515
3778
  function configureCodex(apiKey) {
3516
- const configPath = join9(homedir8(), ".codex", "config.toml");
3517
- if (spawnSync("codex", ["mcp", "add", SERVER_NAME, "--url", `${CONARE_URL}/mcp`, "--header", `Authorization: Bearer ${apiKey}`], {
3779
+ const configPath = join10(homedir9(), ".codex", "config.toml");
3780
+ if (spawnSync2("codex", ["mcp", "add", SERVER_NAME, "--url", `${CONARE_URL}/mcp`, "--header", `Authorization: Bearer ${apiKey}`], {
3518
3781
  stdio: "ignore",
3519
3782
  shell: platform5() === "win32"
3520
3783
  }).status === 0) {
@@ -3522,7 +3785,7 @@ function configureCodex(apiKey) {
3522
3785
  }
3523
3786
  let toml = "";
3524
3787
  try {
3525
- toml = readFileSync8(configPath, "utf-8");
3788
+ toml = readFileSync9(configPath, "utf-8");
3526
3789
  } catch {}
3527
3790
  const sectionHeader = `[mcp_servers.${SERVER_NAME}]`;
3528
3791
  const newSection = [
@@ -3545,9 +3808,9 @@ ${newSection}
3545
3808
  `;
3546
3809
  mkdirSync2(dirname2(configPath), { recursive: true });
3547
3810
  writeFileSync2(configPath, result);
3548
- const oldMcpJson = join9(homedir8(), ".codex", "mcp.json");
3811
+ const oldMcpJson = join10(homedir9(), ".codex", "mcp.json");
3549
3812
  try {
3550
- if (existsSync9(oldMcpJson)) {
3813
+ if (existsSync10(oldMcpJson)) {
3551
3814
  const old = readJsonFile(oldMcpJson);
3552
3815
  const servers = old.mcpServers;
3553
3816
  if (servers && (("conare" in servers) || ("conare-memory" in servers))) {
@@ -3564,7 +3827,7 @@ ${newSection}
3564
3827
  return "\x1B[32m✓\x1B[0m Codex";
3565
3828
  }
3566
3829
  function configureCursor(apiKey) {
3567
- const configPath = join9(homedir8(), ".cursor", "mcp.json");
3830
+ const configPath = join10(homedir9(), ".cursor", "mcp.json");
3568
3831
  const config = readJsonFile(configPath);
3569
3832
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3570
3833
  config.mcpServers = {};
@@ -3577,7 +3840,7 @@ function configureCursor(apiKey) {
3577
3840
  return "\x1B[32m✓\x1B[0m Cursor";
3578
3841
  }
3579
3842
  function configureWindsurf(apiKey) {
3580
- const configPath = join9(homedir8(), ".codeium", "windsurf", "mcp_config.json");
3843
+ const configPath = join10(homedir9(), ".codeium", "windsurf", "mcp_config.json");
3581
3844
  const config = readJsonFile(configPath);
3582
3845
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3583
3846
  config.mcpServers = {};
@@ -3593,11 +3856,11 @@ function configureVscode(apiKey) {
3593
3856
  const os = platform5();
3594
3857
  let configPath;
3595
3858
  if (os === "darwin") {
3596
- configPath = join9(homedir8(), "Library", "Application Support", "Code", "User", "mcp.json");
3859
+ configPath = join10(homedir9(), "Library", "Application Support", "Code", "User", "mcp.json");
3597
3860
  } else if (os === "win32") {
3598
- configPath = join9(process.env.APPDATA || join9(homedir8(), "AppData", "Roaming"), "Code", "User", "mcp.json");
3861
+ configPath = join10(process.env.APPDATA || join10(homedir9(), "AppData", "Roaming"), "Code", "User", "mcp.json");
3599
3862
  } else {
3600
- configPath = join9(homedir8(), ".config", "Code", "User", "mcp.json");
3863
+ configPath = join10(homedir9(), ".config", "Code", "User", "mcp.json");
3601
3864
  }
3602
3865
  const config = readJsonFile(configPath);
3603
3866
  if (!config.servers || typeof config.servers !== "object") {
@@ -3615,11 +3878,11 @@ function configureCline(apiKey) {
3615
3878
  const os = platform5();
3616
3879
  let configPath;
3617
3880
  if (os === "darwin") {
3618
- configPath = join9(homedir8(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3881
+ configPath = join10(homedir9(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3619
3882
  } else if (os === "win32") {
3620
- configPath = join9(process.env.APPDATA || join9(homedir8(), "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3883
+ configPath = join10(process.env.APPDATA || join10(homedir9(), "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3621
3884
  } else {
3622
- configPath = join9(homedir8(), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3885
+ configPath = join10(homedir9(), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
3623
3886
  }
3624
3887
  const config = readJsonFile(configPath);
3625
3888
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
@@ -3637,9 +3900,9 @@ function configureZed(apiKey) {
3637
3900
  const os = platform5();
3638
3901
  let configPath;
3639
3902
  if (os === "darwin") {
3640
- configPath = join9(homedir8(), ".zed", "settings.json");
3903
+ configPath = join10(homedir9(), ".zed", "settings.json");
3641
3904
  } else {
3642
- configPath = join9(homedir8(), ".config", "zed", "settings.json");
3905
+ configPath = join10(homedir9(), ".config", "zed", "settings.json");
3643
3906
  }
3644
3907
  const config = readJsonFile(configPath);
3645
3908
  if (!config.context_servers || typeof config.context_servers !== "object") {
@@ -3660,7 +3923,7 @@ function configureZed(apiKey) {
3660
3923
  return "\x1B[32m✓\x1B[0m Zed";
3661
3924
  }
3662
3925
  function configureOpenclaw(apiKey) {
3663
- const configPath = join9(homedir8(), ".openclaw", "openclaw.json");
3926
+ const configPath = join10(homedir9(), ".openclaw", "openclaw.json");
3664
3927
  const config = readJsonFile(configPath);
3665
3928
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3666
3929
  config.mcpServers = {};
@@ -3674,7 +3937,7 @@ function configureOpenclaw(apiKey) {
3674
3937
  return "\x1B[32m✓\x1B[0m OpenClaw";
3675
3938
  }
3676
3939
  function configureAntigravity(apiKey) {
3677
- const configPath = join9(homedir8(), ".gemini", "antigravity", "mcp_config.json");
3940
+ const configPath = join10(homedir9(), ".gemini", "antigravity", "mcp_config.json");
3678
3941
  const config = readJsonFile(configPath);
3679
3942
  if (!config.mcpServers || typeof config.mcpServers !== "object") {
3680
3943
  config.mcpServers = {};
@@ -3687,7 +3950,7 @@ function configureAntigravity(apiKey) {
3687
3950
  return "\x1B[32m✓\x1B[0m Antigravity";
3688
3951
  }
3689
3952
  function configureOpencode(apiKey) {
3690
- const configPath = join9(homedir8(), ".config", "opencode", "opencode.json");
3953
+ const configPath = join10(homedir9(), ".config", "opencode", "opencode.json");
3691
3954
  const config = readJsonFile(configPath);
3692
3955
  if (!config.mcp || typeof config.mcp !== "object") {
3693
3956
  config.mcp = {};
@@ -3704,10 +3967,10 @@ function configureOpencode(apiKey) {
3704
3967
  return "\x1B[32m✓\x1B[0m OpenCode";
3705
3968
  }
3706
3969
  function configureGrok(apiKey) {
3707
- const configPath = join9(homedir8(), ".grok", "config.toml");
3970
+ const configPath = join10(homedir9(), ".grok", "config.toml");
3708
3971
  let toml = "";
3709
3972
  try {
3710
- toml = readFileSync8(configPath, "utf-8");
3973
+ toml = readFileSync9(configPath, "utf-8");
3711
3974
  } catch {}
3712
3975
  const newSection = [
3713
3976
  `[mcp_servers.${SERVER_NAME}]`,
@@ -3729,6 +3992,25 @@ ${newSection}
3729
3992
  writeFileSync2(configPath, result);
3730
3993
  return "\x1B[32m✓\x1B[0m Grok";
3731
3994
  }
3995
+ function configurePi(apiKey) {
3996
+ const win = platform5() === "win32";
3997
+ const mcpPath = join10(homedir9(), ".pi", "agent", "mcp.json");
3998
+ const config = readJsonFile(mcpPath);
3999
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
4000
+ config.mcpServers = {};
4001
+ }
4002
+ config.mcpServers[SERVER_NAME] = {
4003
+ url: `${CONARE_URL}/mcp`,
4004
+ headers: { Authorization: `Bearer ${apiKey}` }
4005
+ };
4006
+ writeJsonFile(mcpPath, config);
4007
+ const piOnPath = spawnSync2("pi", ["--version"], { stdio: "ignore", shell: win }).status === 0;
4008
+ if (!piOnPath) {
4009
+ return "\x1B[33m•\x1B[0m Pi — config written; run `pi install npm:@conare/pi` once Pi is installed";
4010
+ }
4011
+ const installed = spawnSync2("pi", ["install", "npm:@conare/pi"], { stdio: "ignore", shell: win }).status === 0;
4012
+ return installed ? "\x1B[32m✓\x1B[0m Pi" : "\x1B[33m•\x1B[0m Pi — run `pi install npm:@conare/pi` (manual; the auto-install didn't complete)";
4013
+ }
3732
4014
  var CLIENT_CONFIGURATORS = {
3733
4015
  claude: configureClaude,
3734
4016
  codex: configureCodex,
@@ -3740,135 +4022,9 @@ var CLIENT_CONFIGURATORS = {
3740
4022
  openclaw: configureOpenclaw,
3741
4023
  antigravity: configureAntigravity,
3742
4024
  opencode: configureOpencode,
3743
- grok: configureGrok
4025
+ grok: configureGrok,
4026
+ pi: configurePi
3744
4027
  };
3745
- var SKILL_MD = `---
3746
- name: conare
3747
- description: Load prior project context, search past sessions, save durable preferences, list stored memories, and forget saved items. Use when the user asks what they worked on before, wants prior context loaded at the start of a task, asks to remember or forget something, needs past conversations, decisions, or code recalled from memory, OR when you encounter a reference you don't understand that might exist in the user's memory.
3748
- compatibility: Requires the Conare MCP server tools (\`recall\`, \`search\`, \`save\`, \`list\`, \`forget\`) to be installed and connected.
3749
- metadata:
3750
- author: Conare
3751
- version: 1.3.0
3752
- mcp-server: conare
3753
- homepage: https://conare.ai
3754
- ---
3755
-
3756
- # Conare
3757
-
3758
- This skill teaches the agent the default workflow, tool-selection rules, and query patterns for working with persistent memory across sessions.
3759
-
3760
- ## Primary Use Cases
3761
-
3762
- 1. Start a new coding task with relevant history already loaded through \`recall\`.
3763
- 2. Answer questions about prior work, decisions, bugs, architecture, or preferences through \`search\`.
3764
- 3. Persist durable information the user wants carried into future sessions through \`save\`.
3765
-
3766
- ## When To Use Each Tool
3767
-
3768
- | Situation | Tool | Example |
3769
- |-----------|------|---------|
3770
- | Start of conversation | \`recall\` | Always call first with conversation context + \`prompt\` |
3771
- | User asks about past work | \`search\` | query + \`prompt\` steering the angle |
3772
- | User says "remember this" | \`save\` | Save preferences, rules, decisions |
3773
- | User says "forget this" | \`forget\` | Remove a specific memory |
3774
- | Browse what's stored | \`list\` | "Show me recent memories" |
3775
- | Exact-string raw lookup | \`search\` with \`deep: false\` | Verbatim memory text (rare) |
3776
-
3777
- ## How recall & search Work
3778
-
3779
- Both return an LLM-synthesized answer by default — a noise-removed, detail-preserving brief distilled from the matched memories. The synthesizer is **not a summarizer**: it strips redundancy and superseded claims while preserving every specific number, file path, CLI command, code block, and the WHY behind each decision.
3780
-
3781
- **Two axes, always pair them:**
3782
-
3783
- - \`query\` / \`context\` → keyword-dense retrieval phrase (finds the right memories)
3784
- - \`prompt\` → synthesis instruction (what to emphasize / how to structure it)
3785
-
3786
- Example:
3787
- \`\`\`
3788
- search({
3789
- query: "auth rewrite middleware compliance",
3790
- prompt: "focus on the final decision and why; preserve all file paths and config values"
3791
- })
3792
- \`\`\`
3793
-
3794
- Pass \`prompt\` on almost every call. Without it the synthesizer picks a sensible default, but with it you get exactly the angle the user cares about.
3795
-
3796
- **Opt out of synthesis** with \`deep: false\` only when you need raw memory text for an exact-string lookup. Prefer leaving it unset.
3797
-
3798
- ## Critical Rules
3799
-
3800
- 1. **Always call \`recall\` at conversation start** — pass a specific description of the conversation topic, not generic text
3801
- 2. **\`search\` is global** — it searches ALL memories across all projects. Use it when the user asks about specific topics or past conversations
3802
- 3. **\`recall\` is scoped** — it returns project-relevant context + recent sessions + preferences. Use the \`project\` param when available
3803
- 4. **Write descriptive queries** — "how does the billing webhook handle refunds" beats "billing"
3804
- 5. **Rephrase and retry** — if first search misses, try different phrasing. Semantic search responds to synonyms and related concepts
3805
-
3806
- ## Workflow
3807
-
3808
- ### Step 1: Load context at the start
3809
-
3810
- - Call \`recall\` at conversation start with a specific description of the current task.
3811
- - Include the \`project\` parameter when the project is known or inferable from the workspace.
3812
- - Use the returned context to avoid re-asking for things the user already told the agent in earlier sessions.
3813
-
3814
- Expected outcome: the agent begins with recent sessions, saved preferences, and project-relevant memory already in context.
3815
-
3816
- ### Step 2: Search when the user asks about prior work
3817
-
3818
- - Use \`search\` for questions about past conversations, earlier implementations, prior bugs, design decisions, or work done in a time range.
3819
- - Start with a descriptive natural-language query.
3820
- - If results are weak, retry with 2-3 rephrasings from different angles.
3821
-
3822
- Expected outcome: the agent can cite or summarize the most relevant prior work without broad manual browsing.
3823
-
3824
- ### Step 3: Save durable facts intentionally
3825
-
3826
- - Use \`save\` proactively for information that should persist across sessions: preferences, standing rules, important decisions, long-lived project facts, and user-specific context that will help in future work.
3827
- - When the user shares durable context or says to remember something, prefer capturing it with \`save\` so future \`recall\` calls can surface it automatically.
3828
- - Avoid cluttering memory with purely transient scratch notes unless the user explicitly wants them remembered.
3829
-
3830
- Expected outcome: future \`recall\` calls surface the information automatically when relevant.
3831
-
3832
- ## Search Tips
3833
-
3834
- - Use \`after\`/\`before\` (Unix ms) for time-scoped searches: "what did we work on this week?"
3835
- - Use \`containerTag\` to filter by source: \`claude-chats\`, \`codex-chats\`, \`cursor-chats\`, \`codebase\`, \`preferences\`
3836
- - Use \`project\` for cross-project filtering (partial names work: "conare" matches "fun/conare")
3837
- - Keep \`limit\` low (3-5) for focused results, higher (10-15) for broad exploration
3838
- - When user asks to "remember" something, save it with \`save\` — it goes to the \`preferences\` container and gets surfaced by \`recall\` in future sessions
3839
-
3840
- ## Setup
3841
-
3842
- Install with a single command:
3843
-
3844
- \`\`\`bash
3845
- bunx conare@latest
3846
- \`\`\`
3847
-
3848
- The wizard handles everything: account creation, API key, MCP configuration, background sync setup.
3849
-
3850
- For manual setup, visit [conare.ai](https://conare.ai).
3851
- `;
3852
- function installSkill() {
3853
- const skillDir = join9(homedir8(), ".agents", "skills", "conare");
3854
- mkdirSync2(skillDir, { recursive: true });
3855
- writeFileSync2(join9(skillDir, "SKILL.md"), SKILL_MD);
3856
- const claudeSkillsDir = join9(homedir8(), ".claude", "skills");
3857
- const claudeSkillDir = join9(claudeSkillsDir, "conare");
3858
- try {
3859
- if (existsSync9(claudeSkillsDir)) {
3860
- if (existsSync9(claudeSkillDir)) {
3861
- try {
3862
- if (readlinkSync(claudeSkillDir) === skillDir)
3863
- return "\x1B[32m✓\x1B[0m Agent Skill";
3864
- } catch {}
3865
- rmSync(claudeSkillDir, { recursive: true, force: true });
3866
- }
3867
- symlinkSync(skillDir, claudeSkillDir);
3868
- }
3869
- } catch {}
3870
- return "\x1B[32m✓\x1B[0m Agent Skill";
3871
- }
3872
4028
  function configureMcp(apiKey, targets = ["claude", "codex"]) {
3873
4029
  const results = [];
3874
4030
  for (const target of targets) {
@@ -3881,22 +4037,19 @@ function configureMcp(apiKey, targets = ["claude", "codex"]) {
3881
4037
  }
3882
4038
  }
3883
4039
  }
3884
- try {
3885
- results.push(installSkill());
3886
- } catch {}
3887
4040
  return results;
3888
4041
  }
3889
4042
 
3890
4043
  // src/config.ts
3891
- import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
3892
- import { join as join10 } from "node:path";
3893
- import { homedir as homedir9 } from "node:os";
4044
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
4045
+ import { join as join11 } from "node:path";
4046
+ import { homedir as homedir10 } from "node:os";
3894
4047
  function readConfig() {
3895
4048
  const configPath = getConfigPath();
3896
4049
  try {
3897
- if (!existsSync10(configPath))
4050
+ if (!existsSync11(configPath))
3898
4051
  return {};
3899
- return JSON.parse(readFileSync9(configPath, "utf-8"));
4052
+ return JSON.parse(readFileSync10(configPath, "utf-8"));
3900
4053
  } catch {
3901
4054
  return {};
3902
4055
  }
@@ -3907,10 +4060,10 @@ function writeConfig(config) {
3907
4060
  `, { mode: 384 });
3908
4061
  }
3909
4062
  function getConfigDir() {
3910
- return join10(homedir9(), ".conare");
4063
+ return join11(homedir10(), ".conare");
3911
4064
  }
3912
4065
  function getConfigPath() {
3913
- return join10(getConfigDir(), "config.json");
4066
+ return join11(getConfigDir(), "config.json");
3914
4067
  }
3915
4068
  function saveApiKey(apiKey) {
3916
4069
  const config = readConfig();
@@ -3930,7 +4083,7 @@ function clearSavedApiKey() {
3930
4083
  delete config.key;
3931
4084
  if (Object.keys(config).length === 0) {
3932
4085
  const configPath = getConfigPath();
3933
- if (existsSync10(configPath))
4086
+ if (existsSync11(configPath))
3934
4087
  unlinkSync(configPath);
3935
4088
  } else {
3936
4089
  writeConfig(config);
@@ -3957,19 +4110,19 @@ function stripLegacyTeamConfig(config) {
3957
4110
  }
3958
4111
 
3959
4112
  // src/sync.ts
3960
- 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";
3961
- import { join as join11, dirname as dirname3 } from "node:path";
3962
- import { homedir as homedir10, platform as platform6 } from "node:os";
4113
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync11, chmodSync, cpSync, rmSync as rmSync2, symlinkSync, readlinkSync, appendFileSync } from "node:fs";
4114
+ import { join as join12, dirname as dirname3 } from "node:path";
4115
+ import { homedir as homedir11, platform as platform6 } from "node:os";
3963
4116
  import { execSync as execSync3 } from "node:child_process";
3964
4117
  import { createRequire as createRequire3 } from "node:module";
3965
- var CONARE_DIR = join11(homedir10(), ".conare");
3966
- var BIN_DIR = join11(CONARE_DIR, "bin");
3967
- var CONFIG_PATH = join11(CONARE_DIR, "config.json");
4118
+ var CONARE_DIR = join12(homedir11(), ".conare");
4119
+ var BIN_DIR = join12(CONARE_DIR, "bin");
4120
+ var CONFIG_PATH = join12(CONARE_DIR, "config.json");
3968
4121
  var PLIST_LABEL = "ai.conare.ingest";
3969
- var PLIST_PATH = join11(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
3970
- var SYSTEMD_DIR = join11(homedir10(), ".config", "systemd", "user");
3971
- var SYSTEMD_SERVICE = join11(SYSTEMD_DIR, "conare-sync.service");
3972
- var SYSTEMD_TIMER = join11(SYSTEMD_DIR, "conare-sync.timer");
4122
+ var PLIST_PATH = join12(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
4123
+ var SYSTEMD_DIR = join12(homedir11(), ".config", "systemd", "user");
4124
+ var SYSTEMD_SERVICE = join12(SYSTEMD_DIR, "conare-sync.service");
4125
+ var SYSTEMD_TIMER = join12(SYSTEMD_DIR, "conare-sync.timer");
3973
4126
  var TASK_NAME = "ConareMemorySync";
3974
4127
  var RUN_VBS = `Set WshShell = CreateObject("WScript.Shell")
3975
4128
  WshShell.Run """" & CreateObject("WScript.Shell").ExpandEnvironmentStrings("%USERPROFILE%") & "\\.conare\\bin\\run.cmd" & """", 0, True
@@ -4114,38 +4267,38 @@ function persistBinary(apiKey) {
4114
4267
  if (!cliEntry) {
4115
4268
  throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
4116
4269
  }
4117
- const dest = join11(BIN_DIR, "conare-ingest.mjs");
4118
- const content = readFileSync10(cliEntry, "utf-8");
4270
+ const dest = join12(BIN_DIR, "conare-ingest.mjs");
4271
+ const content = readFileSync11(cliEntry, "utf-8");
4119
4272
  writeFileSync4(dest, content);
4120
4273
  for (const moduleName of ["sql.js", "better-sqlite3", "bindings", "file-uri-to-path"]) {
4121
4274
  try {
4122
4275
  copyNodeModule(moduleName);
4123
4276
  } catch {}
4124
4277
  }
4125
- const runShPath = join11(BIN_DIR, "run.sh");
4278
+ const runShPath = join12(BIN_DIR, "run.sh");
4126
4279
  writeFileSync4(runShPath, RUN_SH);
4127
4280
  try {
4128
4281
  chmodSync(runShPath, 493);
4129
4282
  } catch {}
4130
- const runCmdPath = join11(BIN_DIR, "run.cmd");
4283
+ const runCmdPath = join12(BIN_DIR, "run.cmd");
4131
4284
  writeFileSync4(runCmdPath, RUN_CMD);
4132
- const runVbsPath = join11(BIN_DIR, "run.vbs");
4285
+ const runVbsPath = join12(BIN_DIR, "run.vbs");
4133
4286
  writeFileSync4(runVbsPath, RUN_VBS);
4134
4287
  let existing = {};
4135
- if (existsSync11(CONFIG_PATH)) {
4288
+ if (existsSync12(CONFIG_PATH)) {
4136
4289
  try {
4137
- existing = JSON.parse(readFileSync10(CONFIG_PATH, "utf-8"));
4290
+ existing = JSON.parse(readFileSync11(CONFIG_PATH, "utf-8"));
4138
4291
  } catch {}
4139
4292
  }
4140
4293
  writeFileSync4(CONFIG_PATH, JSON.stringify(mergeApiKeyIntoConfig(existing, apiKey), null, 2) + `
4141
4294
  `, { mode: 384 });
4142
4295
  }
4143
4296
  function isValidJsBundle(path) {
4144
- if (!existsSync11(path))
4297
+ if (!existsSync12(path))
4145
4298
  return false;
4146
4299
  if (path.endsWith(".ts") || path.endsWith(".tsx"))
4147
4300
  return false;
4148
- const head = readFileSync10(path, "utf-8").slice(0, 2000);
4301
+ const head = readFileSync11(path, "utf-8").slice(0, 2000);
4149
4302
  if (/\btype\s+\{/.test(head) || /,\s*type\s+\w+/.test(head))
4150
4303
  return false;
4151
4304
  return true;
@@ -4153,8 +4306,8 @@ function isValidJsBundle(path) {
4153
4306
  function findCliBundle() {
4154
4307
  const dir = dirname3(new URL(import.meta.url).pathname);
4155
4308
  const distCandidates = [
4156
- join11(dir, "index.js"),
4157
- join11(dir, "..", "dist", "index.js")
4309
+ join12(dir, "index.js"),
4310
+ join12(dir, "..", "dist", "index.js")
4158
4311
  ];
4159
4312
  for (const c of distCandidates) {
4160
4313
  if (isValidJsBundle(c))
@@ -4171,12 +4324,12 @@ function findNodeModule(name) {
4171
4324
  return dirname3(require2.resolve(`${name}/package.json`));
4172
4325
  } catch {}
4173
4326
  const candidates = [
4174
- join11(process.cwd(), "node_modules", name),
4175
- join11(dirname3(new URL(import.meta.url).pathname), "..", "node_modules", name),
4176
- join11(dirname3(new URL(import.meta.url).pathname), "..", "..", "node_modules", name)
4327
+ join12(process.cwd(), "node_modules", name),
4328
+ join12(dirname3(new URL(import.meta.url).pathname), "..", "node_modules", name),
4329
+ join12(dirname3(new URL(import.meta.url).pathname), "..", "..", "node_modules", name)
4177
4330
  ];
4178
4331
  for (const c of candidates) {
4179
- if (existsSync11(c))
4332
+ if (existsSync12(c))
4180
4333
  return c;
4181
4334
  }
4182
4335
  return null;
@@ -4185,7 +4338,7 @@ function copyNodeModule(name) {
4185
4338
  const sourceDir = findNodeModule(name);
4186
4339
  if (!sourceDir)
4187
4340
  return;
4188
- const targetDir = join11(BIN_DIR, "node_modules", name);
4341
+ const targetDir = join12(BIN_DIR, "node_modules", name);
4189
4342
  rmSync2(targetDir, { recursive: true, force: true });
4190
4343
  mkdirSync4(dirname3(targetDir), { recursive: true });
4191
4344
  cpSync(sourceDir, targetDir, { recursive: true });
@@ -4220,7 +4373,7 @@ function makeLaunchdPlist(intervalSeconds) {
4220
4373
  <key>ProgramArguments</key>
4221
4374
  <array>
4222
4375
  <string>/bin/bash</string>
4223
- <string>${join11(BIN_DIR, "run.sh")}</string>
4376
+ <string>${join12(BIN_DIR, "run.sh")}</string>
4224
4377
  </array>
4225
4378
  <key>StartInterval</key>
4226
4379
  <integer>${intervalSeconds}</integer>
@@ -4229,7 +4382,7 @@ function makeLaunchdPlist(intervalSeconds) {
4229
4382
  <key>ProcessType</key>
4230
4383
  <string>Background</string>
4231
4384
  <key>StandardErrorPath</key>
4232
- <string>${join11(CONARE_DIR, "ingest.log")}</string>
4385
+ <string>${join12(CONARE_DIR, "ingest.log")}</string>
4233
4386
  </dict>
4234
4387
  </plist>
4235
4388
  `;
@@ -4282,7 +4435,7 @@ function clampCronInterval(intervalMinutes) {
4282
4435
  }
4283
4436
  function setupCron(intervalMinutes) {
4284
4437
  const clamped = clampCronInterval(intervalMinutes);
4285
- const cronCmd = `"${homedir10()}/.conare/bin/run.sh"`;
4438
+ const cronCmd = `"${homedir11()}/.conare/bin/run.sh"`;
4286
4439
  const cronLine = `*/${clamped} * * * * ${cronCmd} # conare-sync`;
4287
4440
  try {
4288
4441
  const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
@@ -4323,7 +4476,7 @@ function removeCronEntry() {
4323
4476
  function installGlobalCommand() {
4324
4477
  const isWindows = platform6() === "win32";
4325
4478
  if (isWindows) {
4326
- const wrapper2 = join11(BIN_DIR, "conare.cmd");
4479
+ const wrapper2 = join12(BIN_DIR, "conare.cmd");
4327
4480
  const content2 = `@echo off\r
4328
4481
  where node >nul 2>nul\r
4329
4482
  if errorlevel 1 (\r
@@ -4345,7 +4498,7 @@ node "%USERPROFILE%\\.conare\\bin\\conare-ingest.mjs" %*\r
4345
4498
  return `Global command: add ${binDirWin} to your PATH manually`;
4346
4499
  }
4347
4500
  }
4348
- const wrapper = join11(BIN_DIR, "conare");
4501
+ const wrapper = join12(BIN_DIR, "conare");
4349
4502
  const content = `#!/bin/bash
4350
4503
  # Conare global command — runs the persisted CLI bundle
4351
4504
  CONARE_DIR="$HOME/.conare"
@@ -4366,15 +4519,15 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
4366
4519
  chmodSync(wrapper, 493);
4367
4520
  const symlinkTarget = "/usr/local/bin/conare";
4368
4521
  try {
4369
- if (existsSync11(symlinkTarget)) {
4522
+ if (existsSync12(symlinkTarget)) {
4370
4523
  try {
4371
- const existing = readlinkSync2(symlinkTarget);
4524
+ const existing = readlinkSync(symlinkTarget);
4372
4525
  if (existing === wrapper)
4373
4526
  return "Global command: conare (already linked)";
4374
4527
  } catch {}
4375
4528
  unlinkSync2(symlinkTarget);
4376
4529
  }
4377
- symlinkSync2(wrapper, symlinkTarget);
4530
+ symlinkSync(wrapper, symlinkTarget);
4378
4531
  return "Global command: conare (linked to /usr/local/bin)";
4379
4532
  } catch {
4380
4533
  const pathDirs = (process.env.PATH || "").split(":");
@@ -4384,7 +4537,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
4384
4537
  const shellProfile = getShellProfile();
4385
4538
  if (shellProfile) {
4386
4539
  try {
4387
- const profileContent = existsSync11(shellProfile) ? readFileSync10(shellProfile, "utf-8") : "";
4540
+ const profileContent = existsSync12(shellProfile) ? readFileSync11(shellProfile, "utf-8") : "";
4388
4541
  const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
4389
4542
  if (!profileContent.includes(".conare/bin")) {
4390
4543
  appendFileSync(shellProfile, `
@@ -4402,24 +4555,24 @@ ${exportLine}
4402
4555
  }
4403
4556
  }
4404
4557
  function getShellProfile() {
4405
- const home = homedir10();
4558
+ const home = homedir11();
4406
4559
  const shell = process.env.SHELL || "";
4407
4560
  if (shell.includes("zsh"))
4408
- return join11(home, ".zshrc");
4561
+ return join12(home, ".zshrc");
4409
4562
  if (shell.includes("bash")) {
4410
- const profile = join11(home, ".bash_profile");
4411
- if (platform6() === "darwin" && existsSync11(profile))
4563
+ const profile = join12(home, ".bash_profile");
4564
+ if (platform6() === "darwin" && existsSync12(profile))
4412
4565
  return profile;
4413
- return join11(home, ".bashrc");
4566
+ return join12(home, ".bashrc");
4414
4567
  }
4415
- if (existsSync11(join11(home, ".zshrc")))
4416
- return join11(home, ".zshrc");
4417
- if (existsSync11(join11(home, ".bashrc")))
4418
- return join11(home, ".bashrc");
4568
+ if (existsSync12(join12(home, ".zshrc")))
4569
+ return join12(home, ".zshrc");
4570
+ if (existsSync12(join12(home, ".bashrc")))
4571
+ return join12(home, ".bashrc");
4419
4572
  return null;
4420
4573
  }
4421
4574
  function setupWindows(intervalMinutes) {
4422
- const runVbs = join11(BIN_DIR, "run.vbs").replace(/\//g, "\\");
4575
+ const runVbs = join12(BIN_DIR, "run.vbs").replace(/\//g, "\\");
4423
4576
  try {
4424
4577
  execSync3(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
4425
4578
  } catch {}
@@ -4559,18 +4712,18 @@ function detectMechanism() {
4559
4712
  }
4560
4713
  function syncStatus() {
4561
4714
  const mechanism = detectMechanism();
4562
- const logPath = join11(CONARE_DIR, "ingest.log");
4715
+ const logPath = join12(CONARE_DIR, "ingest.log");
4563
4716
  let parsed = { lastSyncIso: null, lastSyncError: null };
4564
- if (existsSync11(logPath)) {
4717
+ if (existsSync12(logPath)) {
4565
4718
  try {
4566
- parsed = parseLastSync(readFileSync10(logPath, "utf-8"));
4719
+ parsed = parseLastSync(readFileSync11(logPath, "utf-8"));
4567
4720
  } catch {}
4568
4721
  }
4569
4722
  return {
4570
4723
  timerInstalled: syncTimerInstalled(),
4571
4724
  mechanism,
4572
- binaryPersisted: existsSync11(join11(BIN_DIR, "conare-ingest.mjs")),
4573
- configPresent: existsSync11(CONFIG_PATH),
4725
+ binaryPersisted: existsSync12(join12(BIN_DIR, "conare-ingest.mjs")),
4726
+ configPresent: existsSync12(CONFIG_PATH),
4574
4727
  lastSyncIso: parsed.lastSyncIso,
4575
4728
  lastSyncError: parsed.lastSyncError
4576
4729
  };
@@ -4580,7 +4733,7 @@ function removeSyncTimer() {
4580
4733
  const os = platform6();
4581
4734
  if (os === "darwin") {
4582
4735
  bootoutLaunchAgent();
4583
- if (existsSync11(PLIST_PATH)) {
4736
+ if (existsSync12(PLIST_PATH)) {
4584
4737
  unlinkSync2(PLIST_PATH);
4585
4738
  messages.push("Removed launchd agent");
4586
4739
  }
@@ -4595,9 +4748,9 @@ function removeSyncTimer() {
4595
4748
  try {
4596
4749
  execSync3("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
4597
4750
  } catch {}
4598
- if (existsSync11(SYSTEMD_SERVICE))
4751
+ if (existsSync12(SYSTEMD_SERVICE))
4599
4752
  unlinkSync2(SYSTEMD_SERVICE);
4600
- if (existsSync11(SYSTEMD_TIMER))
4753
+ if (existsSync12(SYSTEMD_TIMER))
4601
4754
  unlinkSync2(SYSTEMD_TIMER);
4602
4755
  try {
4603
4756
  execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
@@ -4610,19 +4763,19 @@ function removeSyncTimer() {
4610
4763
  return messages;
4611
4764
  }
4612
4765
  function clearSyncLocks() {
4613
- const lockDir = join11(CONARE_DIR, "sync.lock.d");
4614
- if (existsSync11(lockDir))
4766
+ const lockDir = join12(CONARE_DIR, "sync.lock.d");
4767
+ if (existsSync12(lockDir))
4615
4768
  rmSync2(lockDir, { recursive: true, force: true });
4616
- const lockFile = join11(CONARE_DIR, "sync.lock");
4617
- if (existsSync11(lockFile))
4769
+ const lockFile = join12(CONARE_DIR, "sync.lock");
4770
+ if (existsSync12(lockFile))
4618
4771
  unlinkSync2(lockFile);
4619
4772
  }
4620
4773
  function uninstallSync() {
4621
4774
  const messages = removeSyncTimer();
4622
- if (existsSync11(CONFIG_PATH))
4775
+ if (existsSync12(CONFIG_PATH))
4623
4776
  unlinkSync2(CONFIG_PATH);
4624
4777
  clearSyncLocks();
4625
- if (existsSync11(BIN_DIR)) {
4778
+ if (existsSync12(BIN_DIR)) {
4626
4779
  rmSync2(BIN_DIR, { recursive: true, force: true });
4627
4780
  messages.push("Removed ~/.conare/bin/");
4628
4781
  }
@@ -4650,7 +4803,8 @@ var CHAT_CONTAINER_LABELS = {
4650
4803
  "codex-chats": "Codex",
4651
4804
  "cursor-chats": "Cursor",
4652
4805
  "opencode-chats": "OpenCode",
4653
- "grok-chats": "Grok"
4806
+ "grok-chats": "Grok",
4807
+ "pi-chats": "Pi"
4654
4808
  };
4655
4809
  function getManifestFingerprint(memory) {
4656
4810
  const metadata = memory.metadata;
@@ -4807,7 +4961,7 @@ Options:
4807
4961
  --ingest-only Index memories without MCP configuration
4808
4962
  --config-only Configure MCP only, skip indexing
4809
4963
  --interactive Run guided setup prompts
4810
- --source <name> Only index from: claude, codex, cursor, opencode, grok
4964
+ --source <name> Only index from: claude, codex, cursor, opencode, grok, pi
4811
4965
  --wasm-dir <path> Path to sql.js module (for Cursor indexing)
4812
4966
 
4813
4967
  Get your API key at https://conare.ai
@@ -4945,8 +5099,8 @@ async function main() {
4945
5099
  let configFileKey;
4946
5100
  if (opts.configFile) {
4947
5101
  try {
4948
- const { readFileSync: readFileSync11 } = await import("node:fs");
4949
- const raw = JSON.parse(readFileSync11(opts.configFile, "utf-8"));
5102
+ const { readFileSync: readFileSync12 } = await import("node:fs");
5103
+ const raw = JSON.parse(readFileSync12(opts.configFile, "utf-8"));
4950
5104
  configFileKey = raw.apiKey || raw.key;
4951
5105
  if (!configFileKey) {
4952
5106
  console.error(`Error: no apiKey/key found in ${opts.configFile}`);
@@ -4966,7 +5120,7 @@ async function main() {
4966
5120
  let effectiveConfigOnly = opts.configOnly;
4967
5121
  let effectiveIngestOnly = opts.ingestOnly;
4968
5122
  let effectiveIndexPath = opts.indexPath;
4969
- let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex", "opencode", "grok"];
5123
+ let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex", "opencode", "grok", "pi"];
4970
5124
  let apiKey = opts.key || configFileKey || process.env.CONARE_API_KEY || savedApiKey;
4971
5125
  let interactiveTargets = MCP_TARGETS.map((target) => ({
4972
5126
  id: target.id,
@@ -5013,7 +5167,7 @@ async function main() {
5013
5167
  detectedCountApproximate: detected?.sessionCountApproximate
5014
5168
  };
5015
5169
  });
5016
- const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor", "opencode", "grok"]);
5170
+ const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor", "opencode", "grok", "pi"]);
5017
5171
  const ingestibleTargets = interactiveTargets.filter((t) => INGESTIBLE_SOURCES.has(t.id));
5018
5172
  showDetectedApps(ingestibleTargets);
5019
5173
  selectedSources = await selectChatSources(ingestibleTargets);
@@ -5086,8 +5240,8 @@ async function main() {
5086
5240
  }
5087
5241
  }
5088
5242
  }
5089
- if (!opts.wasmDir && existsSync12(join12(process.cwd(), "node_modules", "sql.js"))) {
5090
- opts.wasmDir = join12(process.cwd(), "node_modules");
5243
+ if (!opts.wasmDir && existsSync13(join13(process.cwd(), "node_modules", "sql.js"))) {
5244
+ opts.wasmDir = join13(process.cwd(), "node_modules");
5091
5245
  }
5092
5246
  if (effectiveConfigOnly) {
5093
5247
  if (!opts.dryRun) {
@@ -5287,6 +5441,17 @@ Nothing new to index.`);
5287
5441
  allMemories.push(...ingestGrok().memories);
5288
5442
  }
5289
5443
  }
5444
+ if (shouldIngest("pi") && tools.find((t) => t.name === "Pi")?.available) {
5445
+ if (!opts.quiet) {
5446
+ const s = Y2();
5447
+ s.start("Scanning Pi chats...");
5448
+ const { memories, filtered, deduped } = ingestPi();
5449
+ allMemories.push(...memories);
5450
+ s.stop(`Pi: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
5451
+ } else {
5452
+ allMemories.push(...ingestPi().memories);
5453
+ }
5454
+ }
5290
5455
  allMemories.sort((a, b3) => {
5291
5456
  const ta = a.metadata?.sourceTimestamp || a.updated_at || 0;
5292
5457
  const tb = b3.metadata?.sourceTimestamp || b3.updated_at || 0;
@@ -5355,7 +5520,8 @@ Nothing new to index.`);
5355
5520
  codex: [],
5356
5521
  cursor: [],
5357
5522
  opencode: [],
5358
- grok: []
5523
+ grok: [],
5524
+ pi: []
5359
5525
  };
5360
5526
  for (const result of results) {
5361
5527
  if (!result.success)
@@ -5380,6 +5546,9 @@ Nothing new to index.`);
5380
5546
  case "grok-chats":
5381
5547
  successfulKeysBySource.grok.push(key);
5382
5548
  break;
5549
+ case "pi-chats":
5550
+ successfulKeysBySource.pi.push(key);
5551
+ break;
5383
5552
  }
5384
5553
  }
5385
5554
  for (const [source, ids] of Object.entries(successfulKeysBySource)) {
@@ -5426,6 +5595,12 @@ Nothing new to index.`);
5426
5595
  syncSpinner.stop(`Could not set up background sync: ${e2.message}`);
5427
5596
  }
5428
5597
  }
5598
+ if (interactiveMode) {
5599
+ await recordSyncCheckIn(apiKey, "cli", 0, {
5600
+ saveAmount: getSaveAmount(),
5601
+ syncInterval: shouldSync ? opts.syncInterval : undefined
5602
+ });
5603
+ }
5429
5604
  }
5430
5605
  const targetLabels = new Map(MCP_TARGETS.map((t) => [t.id, t.label]));
5431
5606
  const configuredTools = selectedTargets.filter((t) => t !== "conare-skill").map((t) => targetLabels.get(t) ?? t);