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