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.
- package/dist/index.js +474 -299
- 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
|
|
273
|
-
import { join as
|
|
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 =
|
|
278
|
-
if (!
|
|
277
|
+
const gitignorePath = join9(rootPath, ".gitignore");
|
|
278
|
+
if (!existsSync9(gitignorePath))
|
|
279
279
|
return patterns;
|
|
280
280
|
try {
|
|
281
|
-
const content =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
1781
|
-
import { join as
|
|
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
|
|
1785
|
-
import {
|
|
1786
|
-
import {
|
|
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 =
|
|
3429
|
+
const home = homedir8();
|
|
3189
3430
|
const os = platform3();
|
|
3190
3431
|
const tools = [];
|
|
3191
3432
|
const onProgress = options.onProgress;
|
|
3192
|
-
const claudeDir =
|
|
3193
|
-
if (
|
|
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 =
|
|
3205
|
-
const codexSessions =
|
|
3206
|
-
if (
|
|
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 =
|
|
3460
|
+
cursorDbPath = join8(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
3220
3461
|
} else if (os === "win32") {
|
|
3221
|
-
cursorDbPath =
|
|
3462
|
+
cursorDbPath = join8(process.env.APPDATA || join8(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
3222
3463
|
} else {
|
|
3223
|
-
cursorDbPath =
|
|
3464
|
+
cursorDbPath = join8(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
3224
3465
|
}
|
|
3225
|
-
const cursorExists =
|
|
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 =
|
|
3244
|
-
const opencodeExists =
|
|
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:
|
|
3499
|
+
path: join8(home, ".config", "opencode", "opencode.json"),
|
|
3259
3500
|
sessionCount: opencodeCount,
|
|
3260
3501
|
sessionCountApproximate: true
|
|
3261
3502
|
});
|
|
3262
|
-
const grokSessions =
|
|
3263
|
-
const grokExists =
|
|
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:
|
|
3518
|
+
path: join8(home, ".grok", "config.toml"),
|
|
3278
3519
|
sessionCount: grokCount,
|
|
3279
3520
|
sessionCountApproximate: true
|
|
3280
3521
|
});
|
|
3281
|
-
const
|
|
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:
|
|
3286
|
-
path:
|
|
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 =
|
|
3553
|
+
vscodePath = join8(home, "Library", "Application Support", "Code");
|
|
3292
3554
|
} else if (os === "win32") {
|
|
3293
|
-
vscodePath =
|
|
3555
|
+
vscodePath = join8(process.env.APPDATA || join8(home, "AppData", "Roaming"), "Code");
|
|
3294
3556
|
} else {
|
|
3295
|
-
vscodePath =
|
|
3557
|
+
vscodePath = join8(home, ".config", "Code");
|
|
3296
3558
|
}
|
|
3297
3559
|
tools.push({
|
|
3298
3560
|
name: "VS Code Copilot",
|
|
3299
3561
|
id: "vscode",
|
|
3300
|
-
available:
|
|
3301
|
-
path:
|
|
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 =
|
|
3568
|
+
clinePath = join8(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
3307
3569
|
} else if (os === "win32") {
|
|
3308
|
-
clinePath =
|
|
3570
|
+
clinePath = join8(process.env.APPDATA || join8(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
3309
3571
|
} else {
|
|
3310
|
-
clinePath =
|
|
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:
|
|
3316
|
-
path:
|
|
3577
|
+
available: existsSync8(clinePath),
|
|
3578
|
+
path: join8(clinePath, "settings", "cline_mcp_settings.json"),
|
|
3317
3579
|
sessionCount: 0
|
|
3318
3580
|
});
|
|
3319
|
-
const zedPath = os === "darwin" ?
|
|
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:
|
|
3324
|
-
path:
|
|
3585
|
+
available: existsSync8(zedPath),
|
|
3586
|
+
path: join8(zedPath, "settings.json"),
|
|
3325
3587
|
sessionCount: 0
|
|
3326
3588
|
});
|
|
3327
|
-
const openclawDir =
|
|
3589
|
+
const openclawDir = join8(home, ".openclaw");
|
|
3328
3590
|
tools.push({
|
|
3329
3591
|
name: "OpenClaw",
|
|
3330
3592
|
id: "openclaw",
|
|
3331
|
-
available:
|
|
3332
|
-
path:
|
|
3593
|
+
available: existsSync8(openclawDir),
|
|
3594
|
+
path: join8(openclawDir, "openclaw.json"),
|
|
3333
3595
|
sessionCount: 0
|
|
3334
3596
|
});
|
|
3335
|
-
const antigravityDir =
|
|
3336
|
-
const antigravityConvDir =
|
|
3597
|
+
const antigravityDir = join8(home, ".gemini", "antigravity");
|
|
3598
|
+
const antigravityConvDir = join8(antigravityDir, "conversations");
|
|
3337
3599
|
let antigravityCount = 0;
|
|
3338
|
-
if (
|
|
3600
|
+
if (existsSync8(antigravityConvDir)) {
|
|
3339
3601
|
try {
|
|
3340
|
-
antigravityCount =
|
|
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:
|
|
3347
|
-
path:
|
|
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
|
|
3442
|
-
import { dirname as dirname2, join as
|
|
3443
|
-
import { homedir as
|
|
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(
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 =
|
|
3517
|
-
if (
|
|
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 =
|
|
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 =
|
|
3811
|
+
const oldMcpJson = join10(homedir9(), ".codex", "mcp.json");
|
|
3549
3812
|
try {
|
|
3550
|
-
if (
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3859
|
+
configPath = join10(homedir9(), "Library", "Application Support", "Code", "User", "mcp.json");
|
|
3597
3860
|
} else if (os === "win32") {
|
|
3598
|
-
configPath =
|
|
3861
|
+
configPath = join10(process.env.APPDATA || join10(homedir9(), "AppData", "Roaming"), "Code", "User", "mcp.json");
|
|
3599
3862
|
} else {
|
|
3600
|
-
configPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3903
|
+
configPath = join10(homedir9(), ".zed", "settings.json");
|
|
3641
3904
|
} else {
|
|
3642
|
-
configPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3970
|
+
const configPath = join10(homedir9(), ".grok", "config.toml");
|
|
3708
3971
|
let toml = "";
|
|
3709
3972
|
try {
|
|
3710
|
-
toml =
|
|
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
|
|
3892
|
-
import { join as
|
|
3893
|
-
import { homedir as
|
|
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 (!
|
|
4050
|
+
if (!existsSync11(configPath))
|
|
3898
4051
|
return {};
|
|
3899
|
-
return JSON.parse(
|
|
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
|
|
4063
|
+
return join11(homedir10(), ".conare");
|
|
3911
4064
|
}
|
|
3912
4065
|
function getConfigPath() {
|
|
3913
|
-
return
|
|
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 (
|
|
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
|
|
3961
|
-
import { join as
|
|
3962
|
-
import { homedir as
|
|
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 =
|
|
3966
|
-
var BIN_DIR =
|
|
3967
|
-
var CONFIG_PATH =
|
|
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 =
|
|
3970
|
-
var SYSTEMD_DIR =
|
|
3971
|
-
var SYSTEMD_SERVICE =
|
|
3972
|
-
var SYSTEMD_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 =
|
|
4118
|
-
const content =
|
|
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 =
|
|
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 =
|
|
4283
|
+
const runCmdPath = join12(BIN_DIR, "run.cmd");
|
|
4131
4284
|
writeFileSync4(runCmdPath, RUN_CMD);
|
|
4132
|
-
const runVbsPath =
|
|
4285
|
+
const runVbsPath = join12(BIN_DIR, "run.vbs");
|
|
4133
4286
|
writeFileSync4(runVbsPath, RUN_VBS);
|
|
4134
4287
|
let existing = {};
|
|
4135
|
-
if (
|
|
4288
|
+
if (existsSync12(CONFIG_PATH)) {
|
|
4136
4289
|
try {
|
|
4137
|
-
existing = JSON.parse(
|
|
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 (!
|
|
4297
|
+
if (!existsSync12(path))
|
|
4145
4298
|
return false;
|
|
4146
4299
|
if (path.endsWith(".ts") || path.endsWith(".tsx"))
|
|
4147
4300
|
return false;
|
|
4148
|
-
const head =
|
|
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
|
-
|
|
4157
|
-
|
|
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
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
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 (
|
|
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 =
|
|
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>${
|
|
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>${
|
|
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 = `"${
|
|
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 =
|
|
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 =
|
|
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 (
|
|
4522
|
+
if (existsSync12(symlinkTarget)) {
|
|
4370
4523
|
try {
|
|
4371
|
-
const existing =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
4558
|
+
const home = homedir11();
|
|
4406
4559
|
const shell = process.env.SHELL || "";
|
|
4407
4560
|
if (shell.includes("zsh"))
|
|
4408
|
-
return
|
|
4561
|
+
return join12(home, ".zshrc");
|
|
4409
4562
|
if (shell.includes("bash")) {
|
|
4410
|
-
const profile =
|
|
4411
|
-
if (platform6() === "darwin" &&
|
|
4563
|
+
const profile = join12(home, ".bash_profile");
|
|
4564
|
+
if (platform6() === "darwin" && existsSync12(profile))
|
|
4412
4565
|
return profile;
|
|
4413
|
-
return
|
|
4566
|
+
return join12(home, ".bashrc");
|
|
4414
4567
|
}
|
|
4415
|
-
if (
|
|
4416
|
-
return
|
|
4417
|
-
if (
|
|
4418
|
-
return
|
|
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 =
|
|
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 =
|
|
4715
|
+
const logPath = join12(CONARE_DIR, "ingest.log");
|
|
4563
4716
|
let parsed = { lastSyncIso: null, lastSyncError: null };
|
|
4564
|
-
if (
|
|
4717
|
+
if (existsSync12(logPath)) {
|
|
4565
4718
|
try {
|
|
4566
|
-
parsed = parseLastSync(
|
|
4719
|
+
parsed = parseLastSync(readFileSync11(logPath, "utf-8"));
|
|
4567
4720
|
} catch {}
|
|
4568
4721
|
}
|
|
4569
4722
|
return {
|
|
4570
4723
|
timerInstalled: syncTimerInstalled(),
|
|
4571
4724
|
mechanism,
|
|
4572
|
-
binaryPersisted:
|
|
4573
|
-
configPresent:
|
|
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 (
|
|
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 (
|
|
4751
|
+
if (existsSync12(SYSTEMD_SERVICE))
|
|
4599
4752
|
unlinkSync2(SYSTEMD_SERVICE);
|
|
4600
|
-
if (
|
|
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 =
|
|
4614
|
-
if (
|
|
4766
|
+
const lockDir = join12(CONARE_DIR, "sync.lock.d");
|
|
4767
|
+
if (existsSync12(lockDir))
|
|
4615
4768
|
rmSync2(lockDir, { recursive: true, force: true });
|
|
4616
|
-
const lockFile =
|
|
4617
|
-
if (
|
|
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 (
|
|
4775
|
+
if (existsSync12(CONFIG_PATH))
|
|
4623
4776
|
unlinkSync2(CONFIG_PATH);
|
|
4624
4777
|
clearSyncLocks();
|
|
4625
|
-
if (
|
|
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:
|
|
4949
|
-
const raw = JSON.parse(
|
|
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 &&
|
|
5090
|
-
opts.wasmDir =
|
|
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);
|