conare 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2240 -2002
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17,6 +17,16 @@ var __toESM = (mod, isNodeMode, target) => {
17
17
  return to;
18
18
  };
19
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
29
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
20
30
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
21
31
 
22
32
  // node_modules/sisteransi/src/index.js
@@ -147,2092 +157,2294 @@ var require_picocolors = __commonJS((exports, module) => {
147
157
  module.exports.createColors = createColors;
148
158
  });
149
159
 
150
- // src/index.ts
151
- import { existsSync as existsSync8 } from "node:fs";
152
- import { join as join10 } from "node:path";
153
-
154
- // src/detect.ts
155
- import { existsSync, readdirSync } from "node:fs";
156
- import { spawnSync } from "node:child_process";
157
- import { join } from "node:path";
158
- import { homedir, platform } from "node:os";
159
- function countJsonlFiles(dir) {
160
- let count = 0;
161
- try {
162
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
163
- if (entry.isDirectory()) {
164
- count += countJsonlFiles(join(dir, entry.name));
165
- } else if (entry.name.endsWith(".jsonl")) {
166
- count++;
167
- }
168
- }
169
- } catch {}
170
- return count;
160
+ // node_modules/@clack/core/dist/index.mjs
161
+ import { stdin as j, stdout as M } from "node:process";
162
+ import * as g from "node:readline";
163
+ import O from "node:readline";
164
+ import { Writable as X } from "node:stream";
165
+ function DD({ onlyFirst: e = false } = {}) {
166
+ const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
167
+ return new RegExp(t, e ? undefined : "g");
171
168
  }
172
- function countCursorSessions(dbPath) {
173
- try {
174
- const result = spawnSync("sqlite3", [
175
- dbPath,
176
- `
177
- WITH composer_rows AS (
178
- SELECT substr(key, 14) AS composer_id, value AS composer_json
179
- FROM cursorDiskKV
180
- WHERE key LIKE 'composerData:%'
181
- ),
182
- headers AS (
183
- SELECT
184
- composer_id,
185
- json_extract(j.value, '$.bubbleId') AS bubble_id,
186
- json_extract(j.value, '$.type') AS type
187
- FROM composer_rows, json_each(json_extract(composer_json, '$.fullConversationHeadersOnly')) AS j
188
- ),
189
- messages AS (
190
- SELECT h.composer_id, h.type
191
- FROM headers h
192
- JOIN cursorDiskKV kv
193
- ON kv.key = 'bubbleId:' || h.composer_id || ':' || h.bubble_id
194
- WHERE h.type IN (1, 2)
195
- AND length(COALESCE(json_extract(kv.value, '$.text'), '')) >= 50
196
- )
197
- SELECT count(*)
198
- FROM (
199
- SELECT composer_id
200
- FROM messages
201
- GROUP BY composer_id
202
- HAVING SUM(CASE WHEN type = 1 THEN 1 ELSE 0 END) > 0
203
- AND SUM(CASE WHEN type = 2 THEN 1 ELSE 0 END) > 0
204
- );
205
- `.trim()
206
- ], { encoding: "utf-8" });
207
- if (result.status !== 0)
208
- return 0;
209
- const count = Number.parseInt(result.stdout.trim(), 10);
210
- return Number.isFinite(count) ? count : 0;
211
- } catch {
169
+ function P(e) {
170
+ if (typeof e != "string")
171
+ throw new TypeError(`Expected a \`string\`, got \`${typeof e}\``);
172
+ return e.replace(uD, "");
173
+ }
174
+ function L(e) {
175
+ return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
176
+ }
177
+ function p(e, u = {}) {
178
+ if (typeof e != "string" || e.length === 0 || (u = { ambiguousIsNarrow: true, ...u }, e = P(e), e.length === 0))
212
179
  return 0;
180
+ e = e.replace(sD(), " ");
181
+ const t = u.ambiguousIsNarrow ? 1 : 2;
182
+ let F = 0;
183
+ for (const s of e) {
184
+ const i = s.codePointAt(0);
185
+ if (i <= 31 || i >= 127 && i <= 159 || i >= 768 && i <= 879)
186
+ continue;
187
+ switch (eD.eastAsianWidth(s)) {
188
+ case "F":
189
+ case "W":
190
+ F += 2;
191
+ break;
192
+ case "A":
193
+ F += t;
194
+ break;
195
+ default:
196
+ F += 1;
197
+ }
213
198
  }
199
+ return F;
214
200
  }
215
- function detect() {
216
- const home = homedir();
217
- const tools = [];
218
- const claudeDir = join(home, ".claude", "projects");
219
- if (existsSync(claudeDir)) {
220
- const sessionCount = countJsonlFiles(claudeDir);
221
- tools.push({
222
- name: "Claude Code",
223
- available: sessionCount > 0,
224
- path: claudeDir,
225
- sessionCount
226
- });
227
- } else {
228
- tools.push({ name: "Claude Code", available: false, path: claudeDir, sessionCount: 0 });
201
+ function rD() {
202
+ const e = new Map;
203
+ for (const [u, t] of Object.entries(r)) {
204
+ for (const [F, s] of Object.entries(t))
205
+ r[F] = { open: `\x1B[${s[0]}m`, close: `\x1B[${s[1]}m` }, t[F] = r[F], e.set(s[0], s[1]);
206
+ Object.defineProperty(r, u, { value: t, enumerable: false });
229
207
  }
230
- const codexHistory = join(home, ".codex", "history.jsonl");
231
- const codexSessions = join(home, ".codex", "sessions");
232
- if (existsSync(codexHistory) || existsSync(codexSessions)) {
233
- let sessionCount = 0;
234
- if (existsSync(codexHistory)) {
235
- try {
236
- const { readFileSync } = __require("node:fs");
237
- const lines = readFileSync(codexHistory, "utf-8").split(`
238
- `).filter(Boolean);
239
- const sessions = new Set(lines.map((l) => {
240
- try {
241
- return JSON.parse(l).session_id;
242
- } catch {
243
- return null;
244
- }
245
- }));
246
- sessions.delete(null);
247
- sessionCount = sessions.size;
248
- } catch {}
208
+ return Object.defineProperty(r, "codes", { value: e, enumerable: false }), r.color.close = "\x1B[39m", r.bgColor.close = "\x1B[49m", r.color.ansi = N(), r.color.ansi256 = I(), r.color.ansi16m = R(), r.bgColor.ansi = N(w), r.bgColor.ansi256 = I(w), r.bgColor.ansi16m = R(w), Object.defineProperties(r, { rgbToAnsi256: { value: (u, t, F) => u === t && t === F ? u < 8 ? 16 : u > 248 ? 231 : Math.round((u - 8) / 247 * 24) + 232 : 16 + 36 * Math.round(u / 255 * 5) + 6 * Math.round(t / 255 * 5) + Math.round(F / 255 * 5), enumerable: false }, hexToRgb: { value: (u) => {
209
+ const t = /[a-f\d]{6}|[a-f\d]{3}/i.exec(u.toString(16));
210
+ if (!t)
211
+ return [0, 0, 0];
212
+ let [F] = t;
213
+ F.length === 3 && (F = [...F].map((i) => i + i).join(""));
214
+ const s = Number.parseInt(F, 16);
215
+ return [s >> 16 & 255, s >> 8 & 255, s & 255];
216
+ }, enumerable: false }, hexToAnsi256: { value: (u) => r.rgbToAnsi256(...r.hexToRgb(u)), enumerable: false }, ansi256ToAnsi: { value: (u) => {
217
+ if (u < 8)
218
+ return 30 + u;
219
+ if (u < 16)
220
+ return 90 + (u - 8);
221
+ let t, F, s;
222
+ if (u >= 232)
223
+ t = ((u - 232) * 10 + 8) / 255, F = t, s = t;
224
+ else {
225
+ u -= 16;
226
+ const C = u % 36;
227
+ t = Math.floor(u / 36) / 5, F = Math.floor(C / 6) / 5, s = C % 6 / 5;
249
228
  }
250
- tools.push({
251
- name: "Codex",
252
- available: true,
253
- path: existsSync(codexHistory) ? codexHistory : codexSessions,
254
- sessionCount
255
- });
256
- } else {
257
- tools.push({ name: "Codex", available: false, path: codexHistory, sessionCount: 0 });
258
- }
259
- const os = platform();
260
- let cursorDbPath;
261
- if (os === "darwin") {
262
- cursorDbPath = join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
263
- } else if (os === "win32") {
264
- cursorDbPath = join(process.env.APPDATA || join(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
265
- } else {
266
- cursorDbPath = join(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
267
- }
268
- tools.push({
269
- name: "Cursor",
270
- available: existsSync(cursorDbPath),
271
- path: cursorDbPath,
272
- sessionCount: existsSync(cursorDbPath) ? countCursorSessions(cursorDbPath) : 0
273
- });
274
- return tools;
229
+ const i = Math.max(t, F, s) * 2;
230
+ if (i === 0)
231
+ return 30;
232
+ let D = 30 + (Math.round(s) << 2 | Math.round(F) << 1 | Math.round(t));
233
+ return i === 2 && (D += 60), D;
234
+ }, enumerable: false }, rgbToAnsi: { value: (u, t, F) => r.ansi256ToAnsi(r.rgbToAnsi256(u, t, F)), enumerable: false }, hexToAnsi: { value: (u) => r.ansi256ToAnsi(r.hexToAnsi256(u)), enumerable: false } }), r;
275
235
  }
276
-
277
- // src/ingest/claude.ts
278
- import { readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
279
- import { join as join3, basename } from "node:path";
280
- import { homedir as homedir3 } from "node:os";
281
-
282
- // src/ingest/shared.ts
283
- import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "node:fs";
284
- import { createHash } from "node:crypto";
285
- import { join as join2 } from "node:path";
286
- import { homedir as homedir2 } from "node:os";
287
- var MANIFEST_PATH = join2(homedir2(), ".conare", "ingested.json");
288
- function cleanText(raw) {
289
- let text = raw;
290
- text = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "");
291
- text = text.replace(/<attached-context[\s\S]*?<\/attached-context>/g, "");
292
- text = text.replace(/<environment_context>[\s\S]*?<\/environment_context>/g, "");
293
- return text.trim();
236
+ function Y(e, u, t) {
237
+ return String(e).normalize().replace(/\r\n/g, `
238
+ `).split(`
239
+ `).map((F) => lD(F, u, t)).join(`
240
+ `);
294
241
  }
295
- function createContentHash(content) {
296
- return createHash("sha256").update(content).digest("hex").slice(0, 16);
242
+ function $(e, u) {
243
+ if (typeof e == "string")
244
+ return B.aliases.get(e) === u;
245
+ for (const t of e)
246
+ if (t !== undefined && $(t, u))
247
+ return true;
248
+ return false;
297
249
  }
298
- function getIngested() {
299
- try {
300
- if (existsSync2(MANIFEST_PATH)) {
301
- return JSON.parse(readFileSync(MANIFEST_PATH, "utf-8"));
302
- }
303
- } catch {}
304
- return {};
250
+ function BD(e, u) {
251
+ if (e === u)
252
+ return;
253
+ const t = e.split(`
254
+ `), F = u.split(`
255
+ `), s = [];
256
+ for (let i = 0;i < Math.max(t.length, F.length); i++)
257
+ t[i] !== F[i] && s.push(i);
258
+ return s;
305
259
  }
306
- function markIngested(source, sessionIds) {
307
- const manifest = getIngested();
308
- const existing = new Set(manifest[source] || []);
309
- for (const id of sessionIds)
310
- existing.add(id);
311
- manifest[source] = [...existing];
312
- const dir = join2(homedir2(), ".conare");
313
- if (!existsSync2(dir))
314
- mkdirSync(dir, { recursive: true });
315
- writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
260
+ function pD(e) {
261
+ return e === S;
316
262
  }
317
- function isIngested(source, sessionId) {
318
- const manifest = getIngested();
319
- return (manifest[source] || []).includes(sessionId);
263
+ function m(e, u) {
264
+ const t = e;
265
+ t.isTTY && t.setRawMode(u);
320
266
  }
321
- function clearIngested(source) {
322
- const manifest = getIngested();
323
- if (source) {
324
- delete manifest[source];
325
- } else {
326
- for (const key of Object.keys(manifest))
327
- delete manifest[key];
328
- }
329
- const dir = join2(homedir2(), ".conare");
330
- if (!existsSync2(dir))
331
- mkdirSync(dir, { recursive: true });
332
- writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
267
+ function fD({ input: e = j, output: u = M, overwrite: t = true, hideCursor: F = true } = {}) {
268
+ const s = g.createInterface({ input: e, output: u, prompt: "", tabSize: 1 });
269
+ g.emitKeypressEvents(e, s), e.isTTY && e.setRawMode(true);
270
+ const i = (D, { name: C, sequence: n }) => {
271
+ const E = String(D);
272
+ if ($([E, C, n], "cancel")) {
273
+ F && u.write(import_sisteransi.cursor.show), process.exit(0);
274
+ return;
275
+ }
276
+ if (!t)
277
+ return;
278
+ const a = C === "return" ? 0 : -1, o = C === "return" ? -1 : 0;
279
+ g.moveCursor(u, a, o, () => {
280
+ g.clearLine(u, 1, () => {
281
+ e.once("keypress", i);
282
+ });
283
+ });
284
+ };
285
+ return F && u.write(import_sisteransi.cursor.hide), e.once("keypress", i), () => {
286
+ e.off("keypress", i), F && u.write(import_sisteransi.cursor.show), e.isTTY && !AD && e.setRawMode(false), s.terminal = false, s.close();
287
+ };
333
288
  }
334
289
 
335
- // src/ingest/claude.ts
336
- var MAX_CONTENT = 48000;
337
- var MIN_TURN_LEN = 50;
338
- function extractText(content) {
339
- if (typeof content === "string")
340
- return content;
341
- if (!Array.isArray(content))
342
- return "";
343
- return content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join(`
344
- `);
345
- }
346
- function parseSession(lines) {
347
- const messages = [];
348
- let date = null;
349
- for (const line of lines) {
350
- if (!line.trim())
351
- continue;
352
- let obj;
353
- try {
354
- obj = JSON.parse(line);
355
- } catch {
356
- continue;
357
- }
358
- if (!date && obj.timestamp)
359
- date = obj.timestamp.slice(0, 10);
360
- if (obj.type === "user" || obj.type === "assistant") {
361
- const text = cleanText(extractText(obj.message?.content));
362
- if (text.length >= MIN_TURN_LEN) {
363
- messages.push({ role: obj.type, text });
290
+ class x {
291
+ constructor(u, t = true) {
292
+ h(this, "input"), h(this, "output"), h(this, "_abortSignal"), h(this, "rl"), h(this, "opts"), h(this, "_render"), h(this, "_track", false), h(this, "_prevFrame", ""), h(this, "_subscribers", new Map), h(this, "_cursor", 0), h(this, "state", "initial"), h(this, "error", ""), h(this, "value");
293
+ const { input: F = j, output: s = M, render: i, signal: D, ...C } = u;
294
+ this.opts = C, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = i.bind(this), this._track = t, this._abortSignal = D, this.input = F, this.output = s;
295
+ }
296
+ unsubscribe() {
297
+ this._subscribers.clear();
298
+ }
299
+ setSubscriber(u, t) {
300
+ const F = this._subscribers.get(u) ?? [];
301
+ F.push(t), this._subscribers.set(u, F);
302
+ }
303
+ on(u, t) {
304
+ this.setSubscriber(u, { cb: t });
305
+ }
306
+ once(u, t) {
307
+ this.setSubscriber(u, { cb: t, once: true });
308
+ }
309
+ emit(u, ...t) {
310
+ const F = this._subscribers.get(u) ?? [], s = [];
311
+ for (const i of F)
312
+ i.cb(...t), i.once && s.push(() => F.splice(F.indexOf(i), 1));
313
+ for (const i of s)
314
+ i();
315
+ }
316
+ prompt() {
317
+ return new Promise((u, t) => {
318
+ if (this._abortSignal) {
319
+ if (this._abortSignal.aborted)
320
+ return this.state = "cancel", this.close(), u(S);
321
+ this._abortSignal.addEventListener("abort", () => {
322
+ this.state = "cancel", this.close();
323
+ }, { once: true });
324
+ }
325
+ const F = new X;
326
+ F._write = (s, i, D) => {
327
+ this._track && (this.value = this.rl?.line.replace(/\t/g, ""), this._cursor = this.rl?.cursor ?? 0, this.emit("value", this.value)), D();
328
+ }, this.input.pipe(F), this.rl = O.createInterface({ input: this.input, output: F, tabSize: 2, prompt: "", escapeCodeTimeout: 50, terminal: true }), O.emitKeypressEvents(this.input, this.rl), this.rl.prompt(), this.opts.initialValue !== undefined && this._track && this.rl.write(this.opts.initialValue), this.input.on("keypress", this.onKeypress), m(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
329
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), m(this.input, false), u(this.value);
330
+ }), this.once("cancel", () => {
331
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), m(this.input, false), u(S);
332
+ });
333
+ });
334
+ }
335
+ onKeypress(u, t) {
336
+ if (this.state === "error" && (this.state = "active"), t?.name && (!this._track && B.aliases.has(t.name) && this.emit("cursor", B.aliases.get(t.name)), B.actions.has(t.name) && this.emit("cursor", t.name)), u && (u.toLowerCase() === "y" || u.toLowerCase() === "n") && this.emit("confirm", u.toLowerCase() === "y"), u === "\t" && this.opts.placeholder && (this.value || (this.rl?.write(this.opts.placeholder), this.emit("value", this.opts.placeholder))), u && this.emit("key", u.toLowerCase()), t?.name === "return") {
337
+ if (this.opts.validate) {
338
+ const F = this.opts.validate(this.value);
339
+ F && (this.error = F instanceof Error ? F.message : F, this.state = "error", this.rl?.write(this.value));
364
340
  }
341
+ this.state !== "error" && (this.state = "submit");
365
342
  }
343
+ $([u, t?.name, t?.sequence], "cancel") && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
366
344
  }
367
- const turns = [];
368
- for (let i = 0;i < messages.length - 1; i++) {
369
- if (messages[i].role === "user" && messages[i + 1].role === "assistant") {
370
- turns.push({ user: messages[i].text, assistant: messages[i + 1].text });
371
- i++;
345
+ close() {
346
+ this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
347
+ `), m(this.input, false), this.rl?.close(), this.rl = undefined, this.emit(`${this.state}`, this.value), this.unsubscribe();
348
+ }
349
+ restoreCursor() {
350
+ const u = Y(this._prevFrame, process.stdout.columns, { hard: true }).split(`
351
+ `).length - 1;
352
+ this.output.write(import_sisteransi.cursor.move(-999, u * -1));
353
+ }
354
+ render() {
355
+ const u = Y(this._render(this) ?? "", process.stdout.columns, { hard: true });
356
+ if (u !== this._prevFrame) {
357
+ if (this.state === "initial")
358
+ this.output.write(import_sisteransi.cursor.hide);
359
+ else {
360
+ const t = BD(this._prevFrame, u);
361
+ if (this.restoreCursor(), t && t?.length === 1) {
362
+ const F = t[0];
363
+ this.output.write(import_sisteransi.cursor.move(0, F)), this.output.write(import_sisteransi.erase.lines(1));
364
+ const s = u.split(`
365
+ `);
366
+ this.output.write(s[F]), this._prevFrame = u, this.output.write(import_sisteransi.cursor.move(0, s.length - F - 1));
367
+ return;
368
+ }
369
+ if (t && t?.length > 1) {
370
+ const F = t[0];
371
+ this.output.write(import_sisteransi.cursor.move(0, F)), this.output.write(import_sisteransi.erase.down());
372
+ const s = u.split(`
373
+ `).slice(F);
374
+ this.output.write(s.join(`
375
+ `)), this._prevFrame = u;
376
+ return;
377
+ }
378
+ this.output.write(import_sisteransi.erase.down());
379
+ }
380
+ this.output.write(u), this.state === "initial" && (this.state = "active"), this._prevFrame = u;
372
381
  }
373
382
  }
374
- return { turns, date };
375
383
  }
376
- function ingestClaude() {
377
- const projectsDir = join3(homedir3(), ".claude", "projects");
378
- const memories = [];
379
- const sessionIds = [];
380
- let filtered = 0;
381
- let deduped = 0;
382
- let projectDirs;
383
- try {
384
- projectDirs = readdirSync2(projectsDir);
385
- } catch {
386
- return { memories, sessionIds, skipped: 0, filtered, deduped };
384
+ var import_sisteransi, import_picocolors, uD, W, tD, eD, FD = function() {
385
+ return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67)\uDB40\uDC7F|(?:\uD83E\uDDD1\uD83C\uDFFF\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFC-\uDFFF])|\uD83D\uDC68(?:\uD83C\uDFFB(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC)?|(?:\uD83D\uDC69(?:\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC69(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83E\uDDD1(?:\u200D(?:\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F\u200D\u26A7|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\uD83C\uDFF4\u200D\u2620|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299]|\uD83C[\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3])\uFE0F|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDE35\u200D\uD83D\uDCAB|\uD83D\uDE2E\u200D\uD83D\uDCA8|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83E\uDDD1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC69(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC08\u200D\u2B1B|\u2764\uFE0F\u200D(?:\uD83D\uDD25|\uD83E\uDE79)|\uD83D\uDC41\uFE0F|\uD83C\uDFF3\uFE0F|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|[#\*0-9]\uFE0F\u20E3|\u2764\uFE0F|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF4|(?:[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270C\u270D]|\uD83D[\uDD74\uDD90])(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC08\uDC15\uDC3B\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE2E\uDE35\uDE36\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5]|\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD]|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0D\uDD0E\uDD10-\uDD17\uDD1D\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78\uDD7A-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCB\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6]|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDD77\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g;
386
+ }, sD, w = 10, N = (e = 0) => (u) => `\x1B[${u + e}m`, I = (e = 0) => (u) => `\x1B[${38 + e};5;${u}m`, R = (e = 0) => (u, t, F) => `\x1B[${38 + e};2;${u};${t};${F}m`, r, iD, CD, ED, d, oD = 39, y = "\x07", V = "[", nD = "]", G = "m", _, z = (e) => `${d.values().next().value}${V}${e}${G}`, K = (e) => `${d.values().next().value}${_}${e}${y}`, aD = (e) => e.split(" ").map((u) => p(u)), k = (e, u, t) => {
387
+ const F = [...u];
388
+ let s = false, i = false, D = p(P(e[e.length - 1]));
389
+ for (const [C, n] of F.entries()) {
390
+ const E = p(n);
391
+ if (D + E <= t ? e[e.length - 1] += n : (e.push(n), D = 0), d.has(n) && (s = true, i = F.slice(C + 1).join("").startsWith(_)), s) {
392
+ i ? n === y && (s = false, i = false) : n === G && (s = false);
393
+ continue;
394
+ }
395
+ D += E, D === t && C < F.length - 1 && (e.push(""), D = 0);
387
396
  }
388
- for (const projDir of projectDirs) {
389
- const projPath = join3(projectsDir, projDir);
390
- const project = projDir.replace(/^-Users-[^-]+-/, "").replace(/-/g, "/") || projDir;
391
- let files;
392
- try {
393
- files = readdirSync2(projPath).filter((f) => f.endsWith(".jsonl"));
394
- } catch {
397
+ !D && e[e.length - 1].length > 0 && e.length > 1 && (e[e.length - 2] += e.pop());
398
+ }, hD = (e) => {
399
+ const u = e.split(" ");
400
+ let t = u.length;
401
+ for (;t > 0 && !(p(u[t - 1]) > 0); )
402
+ t--;
403
+ return t === u.length ? e : u.slice(0, t).join(" ") + u.slice(t).join("");
404
+ }, lD = (e, u, t = {}) => {
405
+ if (t.trim !== false && e.trim() === "")
406
+ return "";
407
+ let F = "", s, i;
408
+ const D = aD(e);
409
+ let C = [""];
410
+ for (const [E, a] of e.split(" ").entries()) {
411
+ t.trim !== false && (C[C.length - 1] = C[C.length - 1].trimStart());
412
+ let o = p(C[C.length - 1]);
413
+ if (E !== 0 && (o >= u && (t.wordWrap === false || t.trim === false) && (C.push(""), o = 0), (o > 0 || t.trim === false) && (C[C.length - 1] += " ", o++)), t.hard && D[E] > u) {
414
+ const c = u - o, f = 1 + Math.floor((D[E] - c - 1) / u);
415
+ Math.floor((D[E] - 1) / u) < f && C.push(""), k(C, a, u);
395
416
  continue;
396
417
  }
397
- for (const file of files) {
398
- const sessionId = basename(file, ".jsonl");
399
- const raw = readFileSync2(join3(projPath, file), "utf-8");
400
- const { turns, date } = parseSession(raw.split(`
401
- `));
402
- if (turns.length === 0) {
403
- filtered++;
404
- continue;
405
- }
406
- const header = `# Chat: ${project}${date ? ` | ${date}` : ""}`;
407
- const body = turns.map((t) => {
408
- const q = t.user.length > 300 ? t.user.slice(0, 300) + "..." : t.user;
409
- return `## Q: ${q}
410
-
411
- ${t.assistant}`;
412
- }).join(`
413
-
414
- ---
415
-
416
- `);
417
- let content = `${header}
418
-
419
- ${body}`;
420
- if (content.length > MAX_CONTENT)
421
- content = content.slice(0, MAX_CONTENT) + `
422
-
423
- [truncated]`;
424
- const contentHash = createContentHash(content);
425
- const dedupKey = `claude:${sessionId}`;
426
- const fingerprint = `${dedupKey}:${contentHash}`;
427
- if (isIngested("claude", fingerprint)) {
428
- deduped++;
418
+ if (o + D[E] > u && o > 0 && D[E] > 0) {
419
+ if (t.wordWrap === false && o < u) {
420
+ k(C, a, u);
429
421
  continue;
430
422
  }
431
- memories.push({
432
- content,
433
- containerTag: "claude-chats",
434
- metadata: {
435
- dedupKey,
436
- contentHash,
437
- source: "claude-code",
438
- sessionId,
439
- project,
440
- date: date || "unknown"
441
- }
442
- });
443
- sessionIds.push(sessionId);
423
+ C.push("");
424
+ }
425
+ if (o + D[E] > u && t.wordWrap === false) {
426
+ k(C, a, u);
427
+ continue;
444
428
  }
429
+ C[C.length - 1] += a;
445
430
  }
446
- return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
447
- }
448
-
449
- // src/ingest/codex.ts
450
- import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3 } from "node:fs";
451
- import { join as join4, basename as basename2 } from "node:path";
452
- import { homedir as homedir4 } from "node:os";
453
- var MAX_CONTENT2 = 48000;
454
- function isCodexBoilerplate(text) {
455
- return text.startsWith("# AGENTS.md instructions for") || text.startsWith("<INSTRUCTIONS>") || text.startsWith("<user_instructions>") || text.startsWith("<user_action>");
456
- }
457
- function stripEnvironmentContext(text) {
458
- let cwd = null;
459
- const cwdMatch = text.match(/<cwd>([^<]+)<\/cwd>/);
460
- if (cwdMatch)
461
- cwd = cwdMatch[1];
462
- const cleaned = text.replace(/<environment_context>[\s\S]*?<\/environment_context>/g, "").trim();
463
- return { text: cleaned, cwd };
464
- }
465
- function projectFromCwd(cwd) {
466
- return cwd.replace(/^\/Users\/[^/]+\//, "");
467
- }
468
- function ingestCodex() {
469
- const memories = [];
470
- const sessionIds = [];
471
- let filtered = 0;
472
- let deduped = 0;
473
- const historyPath = join4(homedir4(), ".codex", "history.jsonl");
474
- if (existsSync3(historyPath)) {
475
- try {
476
- const lines = readFileSync3(historyPath, "utf-8").split(`
477
- `).filter(Boolean);
478
- const sessions = new Map;
479
- for (const line of lines) {
480
- try {
481
- const obj = JSON.parse(line);
482
- if (!obj.session_id || !obj.text)
483
- continue;
484
- if (!sessions.has(obj.session_id))
485
- sessions.set(obj.session_id, []);
486
- sessions.get(obj.session_id).push({ ts: obj.ts, text: obj.text });
487
- } catch {
488
- continue;
489
- }
431
+ t.trim !== false && (C = C.map((E) => hD(E)));
432
+ const n = [...C.join(`
433
+ `)];
434
+ for (const [E, a] of n.entries()) {
435
+ if (F += a, d.has(a)) {
436
+ const { groups: c } = new RegExp(`(?:\\${V}(?<code>\\d+)m|\\${_}(?<uri>.*)${y})`).exec(n.slice(E).join("")) || { groups: {} };
437
+ if (c.code !== undefined) {
438
+ const f = Number.parseFloat(c.code);
439
+ s = f === oD ? undefined : f;
440
+ } else
441
+ c.uri !== undefined && (i = c.uri.length === 0 ? undefined : c.uri);
442
+ }
443
+ const o = ED.codes.get(Number(s));
444
+ n[E + 1] === `
445
+ ` ? (i && (F += K("")), s && o && (F += z(o))) : a === `
446
+ ` && (s && o && (F += z(s)), i && (F += K(i)));
447
+ }
448
+ return F;
449
+ }, xD, B, AD, S, gD, vD = (e, u, t) => (u in e) ? gD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, h = (e, u, t) => (vD(e, typeof u != "symbol" ? u + "" : u, t), t), dD, A, kD, $D = (e, u, t) => (u in e) ? kD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, H = (e, u, t) => ($D(e, typeof u != "symbol" ? u + "" : u, t), t), SD, TD, jD = (e, u, t) => (u in e) ? TD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, U = (e, u, t) => (jD(e, typeof u != "symbol" ? u + "" : u, t), t), MD;
450
+ var init_dist = __esm(() => {
451
+ import_sisteransi = __toESM(require_src(), 1);
452
+ import_picocolors = __toESM(require_picocolors(), 1);
453
+ uD = DD();
454
+ W = { exports: {} };
455
+ (function(e) {
456
+ var u = {};
457
+ e.exports = u, u.eastAsianWidth = function(F) {
458
+ var s = F.charCodeAt(0), i = F.length == 2 ? F.charCodeAt(1) : 0, D = s;
459
+ return 55296 <= s && s <= 56319 && 56320 <= i && i <= 57343 && (s &= 1023, i &= 1023, D = s << 10 | i, D += 65536), D == 12288 || 65281 <= D && D <= 65376 || 65504 <= D && D <= 65510 ? "F" : D == 8361 || 65377 <= D && D <= 65470 || 65474 <= D && D <= 65479 || 65482 <= D && D <= 65487 || 65490 <= D && D <= 65495 || 65498 <= D && D <= 65500 || 65512 <= D && D <= 65518 ? "H" : 4352 <= D && D <= 4447 || 4515 <= D && D <= 4519 || 4602 <= D && D <= 4607 || 9001 <= D && D <= 9002 || 11904 <= D && D <= 11929 || 11931 <= D && D <= 12019 || 12032 <= D && D <= 12245 || 12272 <= D && D <= 12283 || 12289 <= D && D <= 12350 || 12353 <= D && D <= 12438 || 12441 <= D && D <= 12543 || 12549 <= D && D <= 12589 || 12593 <= D && D <= 12686 || 12688 <= D && D <= 12730 || 12736 <= D && D <= 12771 || 12784 <= D && D <= 12830 || 12832 <= D && D <= 12871 || 12880 <= D && D <= 13054 || 13056 <= D && D <= 19903 || 19968 <= D && D <= 42124 || 42128 <= D && D <= 42182 || 43360 <= D && D <= 43388 || 44032 <= D && D <= 55203 || 55216 <= D && D <= 55238 || 55243 <= D && D <= 55291 || 63744 <= D && D <= 64255 || 65040 <= D && D <= 65049 || 65072 <= D && D <= 65106 || 65108 <= D && D <= 65126 || 65128 <= D && D <= 65131 || 110592 <= D && D <= 110593 || 127488 <= D && D <= 127490 || 127504 <= D && D <= 127546 || 127552 <= D && D <= 127560 || 127568 <= D && D <= 127569 || 131072 <= D && D <= 194367 || 177984 <= D && D <= 196605 || 196608 <= D && D <= 262141 ? "W" : 32 <= D && D <= 126 || 162 <= D && D <= 163 || 165 <= D && D <= 166 || D == 172 || D == 175 || 10214 <= D && D <= 10221 || 10629 <= D && D <= 10630 ? "Na" : D == 161 || D == 164 || 167 <= D && D <= 168 || D == 170 || 173 <= D && D <= 174 || 176 <= D && D <= 180 || 182 <= D && D <= 186 || 188 <= D && D <= 191 || D == 198 || D == 208 || 215 <= D && D <= 216 || 222 <= D && D <= 225 || D == 230 || 232 <= D && D <= 234 || 236 <= D && D <= 237 || D == 240 || 242 <= D && D <= 243 || 247 <= D && D <= 250 || D == 252 || D == 254 || D == 257 || D == 273 || D == 275 || D == 283 || 294 <= D && D <= 295 || D == 299 || 305 <= D && D <= 307 || D == 312 || 319 <= D && D <= 322 || D == 324 || 328 <= D && D <= 331 || D == 333 || 338 <= D && D <= 339 || 358 <= D && D <= 359 || D == 363 || D == 462 || D == 464 || D == 466 || D == 468 || D == 470 || D == 472 || D == 474 || D == 476 || D == 593 || D == 609 || D == 708 || D == 711 || 713 <= D && D <= 715 || D == 717 || D == 720 || 728 <= D && D <= 731 || D == 733 || D == 735 || 768 <= D && D <= 879 || 913 <= D && D <= 929 || 931 <= D && D <= 937 || 945 <= D && D <= 961 || 963 <= D && D <= 969 || D == 1025 || 1040 <= D && D <= 1103 || D == 1105 || D == 8208 || 8211 <= D && D <= 8214 || 8216 <= D && D <= 8217 || 8220 <= D && D <= 8221 || 8224 <= D && D <= 8226 || 8228 <= D && D <= 8231 || D == 8240 || 8242 <= D && D <= 8243 || D == 8245 || D == 8251 || D == 8254 || D == 8308 || D == 8319 || 8321 <= D && D <= 8324 || D == 8364 || D == 8451 || D == 8453 || D == 8457 || D == 8467 || D == 8470 || 8481 <= D && D <= 8482 || D == 8486 || D == 8491 || 8531 <= D && D <= 8532 || 8539 <= D && D <= 8542 || 8544 <= D && D <= 8555 || 8560 <= D && D <= 8569 || D == 8585 || 8592 <= D && D <= 8601 || 8632 <= D && D <= 8633 || D == 8658 || D == 8660 || D == 8679 || D == 8704 || 8706 <= D && D <= 8707 || 8711 <= D && D <= 8712 || D == 8715 || D == 8719 || D == 8721 || D == 8725 || D == 8730 || 8733 <= D && D <= 8736 || D == 8739 || D == 8741 || 8743 <= D && D <= 8748 || D == 8750 || 8756 <= D && D <= 8759 || 8764 <= D && D <= 8765 || D == 8776 || D == 8780 || D == 8786 || 8800 <= D && D <= 8801 || 8804 <= D && D <= 8807 || 8810 <= D && D <= 8811 || 8814 <= D && D <= 8815 || 8834 <= D && D <= 8835 || 8838 <= D && D <= 8839 || D == 8853 || D == 8857 || D == 8869 || D == 8895 || D == 8978 || 9312 <= D && D <= 9449 || 9451 <= D && D <= 9547 || 9552 <= D && D <= 9587 || 9600 <= D && D <= 9615 || 9618 <= D && D <= 9621 || 9632 <= D && D <= 9633 || 9635 <= D && D <= 9641 || 9650 <= D && D <= 9651 || 9654 <= D && D <= 9655 || 9660 <= D && D <= 9661 || 9664 <= D && D <= 9665 || 9670 <= D && D <= 9672 || D == 9675 || 9678 <= D && D <= 9681 || 9698 <= D && D <= 9701 || D == 9711 || 9733 <= D && D <= 9734 || D == 9737 || 9742 <= D && D <= 9743 || 9748 <= D && D <= 9749 || D == 9756 || D == 9758 || D == 9792 || D == 9794 || 9824 <= D && D <= 9825 || 9827 <= D && D <= 9829 || 9831 <= D && D <= 9834 || 9836 <= D && D <= 9837 || D == 9839 || 9886 <= D && D <= 9887 || 9918 <= D && D <= 9919 || 9924 <= D && D <= 9933 || 9935 <= D && D <= 9953 || D == 9955 || 9960 <= D && D <= 9983 || D == 10045 || D == 10071 || 10102 <= D && D <= 10111 || 11093 <= D && D <= 11097 || 12872 <= D && D <= 12879 || 57344 <= D && D <= 63743 || 65024 <= D && D <= 65039 || D == 65533 || 127232 <= D && D <= 127242 || 127248 <= D && D <= 127277 || 127280 <= D && D <= 127337 || 127344 <= D && D <= 127386 || 917760 <= D && D <= 917999 || 983040 <= D && D <= 1048573 || 1048576 <= D && D <= 1114109 ? "A" : "N";
460
+ }, u.characterLength = function(F) {
461
+ var s = this.eastAsianWidth(F);
462
+ return s == "F" || s == "W" || s == "A" ? 2 : 1;
463
+ };
464
+ function t(F) {
465
+ return F.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
466
+ }
467
+ u.length = function(F) {
468
+ for (var s = t(F), i = 0, D = 0;D < s.length; D++)
469
+ i = i + this.characterLength(s[D]);
470
+ return i;
471
+ }, u.slice = function(F, s, i) {
472
+ textLen = u.length(F), s = s || 0, i = i || 1, s < 0 && (s = textLen + s), i < 0 && (i = textLen + i);
473
+ for (var D = "", C = 0, n = t(F), E = 0;E < n.length; E++) {
474
+ var a = n[E], o = u.length(a);
475
+ if (C >= s - (o == 2 ? 1 : 0))
476
+ if (C + o <= i)
477
+ D += a;
478
+ else
479
+ break;
480
+ C += o;
490
481
  }
491
- for (const [sessionId, entries] of sessions) {
492
- entries.sort((a, b) => a.ts - b.ts);
493
- const date = new Date(entries[0].ts * 1000).toISOString().slice(0, 10);
494
- let project = null;
495
- const cleanEntries = [];
496
- for (const e of entries) {
497
- let text = cleanText(e.text);
498
- if (isCodexBoilerplate(text))
499
- continue;
500
- const env = stripEnvironmentContext(text);
501
- text = env.text;
502
- if (!project && env.cwd)
503
- project = projectFromCwd(env.cwd);
504
- if (text.length === 0)
505
- continue;
506
- cleanEntries.push(text.length > 300 ? text.slice(0, 300) + "..." : text);
507
- }
508
- const body = cleanEntries.filter(Boolean).join(`
509
-
510
- ---
511
-
512
- `);
513
- let content = `# Codex Chat | ${date}
514
-
515
- ${body}`;
516
- if (content.length > MAX_CONTENT2)
517
- content = content.slice(0, MAX_CONTENT2) + `
518
-
519
- [truncated]`;
520
- if (content.length < 100) {
521
- filtered++;
522
- continue;
482
+ return D;
483
+ };
484
+ })(W);
485
+ tD = W.exports;
486
+ eD = L(tD);
487
+ sD = L(FD);
488
+ r = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } };
489
+ Object.keys(r.modifier);
490
+ iD = Object.keys(r.color);
491
+ CD = Object.keys(r.bgColor);
492
+ [...iD, ...CD];
493
+ ED = rD();
494
+ d = new Set(["\x1B", "›"]);
495
+ _ = `${nD}8;;`;
496
+ xD = ["up", "down", "left", "right", "space", "enter", "cancel"];
497
+ B = { actions: new Set(xD), aliases: new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["\x03", "cancel"], ["escape", "cancel"]]) };
498
+ AD = globalThis.process.platform.startsWith("win");
499
+ S = Symbol("clack:cancel");
500
+ gD = Object.defineProperty;
501
+ dD = class dD extends x {
502
+ get cursor() {
503
+ return this.value ? 0 : 1;
504
+ }
505
+ get _value() {
506
+ return this.cursor === 0;
507
+ }
508
+ constructor(u) {
509
+ super(u, false), this.value = !!u.initialValue, this.on("value", () => {
510
+ this.value = this._value;
511
+ }), this.on("confirm", (t) => {
512
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = t, this.state = "submit", this.close();
513
+ }), this.on("cursor", () => {
514
+ this.value = !this.value;
515
+ });
516
+ }
517
+ };
518
+ A = new WeakMap;
519
+ kD = Object.defineProperty;
520
+ SD = class extends x {
521
+ constructor(u) {
522
+ super(u, false), H(this, "options"), H(this, "cursor", 0), this.options = u.options, this.value = [...u.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: t }) => t === u.cursorAt), 0), this.on("key", (t) => {
523
+ t === "a" && this.toggleAll();
524
+ }), this.on("cursor", (t) => {
525
+ switch (t) {
526
+ case "left":
527
+ case "up":
528
+ this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
529
+ break;
530
+ case "down":
531
+ case "right":
532
+ this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
533
+ break;
534
+ case "space":
535
+ this.toggleValue();
536
+ break;
523
537
  }
524
- const contentHash = createContentHash(content);
525
- const dedupKey = `codex:${sessionId}`;
526
- const fingerprint = `${dedupKey}:${contentHash}`;
527
- if (isIngested("codex", fingerprint)) {
528
- deduped++;
529
- continue;
538
+ });
539
+ }
540
+ get _value() {
541
+ return this.options[this.cursor].value;
542
+ }
543
+ toggleAll() {
544
+ const u = this.value.length === this.options.length;
545
+ this.value = u ? [] : this.options.map((t) => t.value);
546
+ }
547
+ toggleValue() {
548
+ const u = this.value.includes(this._value);
549
+ this.value = u ? this.value.filter((t) => t !== this._value) : [...this.value, this._value];
550
+ }
551
+ };
552
+ TD = Object.defineProperty;
553
+ MD = class MD extends x {
554
+ constructor({ mask: u, ...t }) {
555
+ super(t), U(this, "valueWithCursor", ""), U(this, "_mask", "•"), this._mask = u ?? "•", this.on("finalize", () => {
556
+ this.valueWithCursor = this.masked;
557
+ }), this.on("value", () => {
558
+ if (this.cursor >= this.value.length)
559
+ this.valueWithCursor = `${this.masked}${import_picocolors.default.inverse(import_picocolors.default.hidden("_"))}`;
560
+ else {
561
+ const F = this.masked.slice(0, this.cursor), s = this.masked.slice(this.cursor);
562
+ this.valueWithCursor = `${F}${import_picocolors.default.inverse(s[0])}${s.slice(1)}`;
530
563
  }
531
- memories.push({
532
- content,
533
- containerTag: "codex-chats",
534
- metadata: {
535
- dedupKey,
536
- contentHash,
537
- source: "codex",
538
- sessionId,
539
- date,
540
- ...project ? { project } : {}
541
- }
542
- });
543
- sessionIds.push(sessionId);
544
- }
545
- } catch {}
546
- }
547
- const sessionsDir = join4(homedir4(), ".codex", "sessions");
548
- if (existsSync3(sessionsDir)) {
549
- try {
550
- const stats = { filtered: 0, deduped: 0 };
551
- walkCodexSessions(sessionsDir, memories, sessionIds, stats);
552
- filtered += stats.filtered;
553
- deduped += stats.deduped;
554
- } catch {}
555
- }
556
- return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
557
- }
558
- function walkCodexSessions(dir, memories, sessionIds, stats) {
559
- try {
560
- for (const entry of readdirSync3(dir, { withFileTypes: true })) {
561
- if (entry.isDirectory()) {
562
- walkCodexSessions(join4(dir, entry.name), memories, sessionIds, stats);
563
- } else if (entry.name.endsWith(".jsonl")) {
564
- const sessionId = basename2(entry.name, ".jsonl");
565
- if (sessionIds.includes(sessionId))
566
- continue;
567
- try {
568
- const lines = readFileSync3(join4(dir, entry.name), "utf-8").split(`
569
- `).filter(Boolean);
570
- const turns = [];
571
- let date = null;
572
- let project = null;
573
- for (const line of lines) {
574
- try {
575
- const obj = JSON.parse(line);
576
- if (!date && obj.timestamp)
577
- date = obj.timestamp.slice(0, 10);
578
- if (obj.type === "session_meta" && obj.payload?.cwd) {
579
- project = projectFromCwd(obj.payload.cwd);
580
- }
581
- if (obj.type === "response_item" && obj.payload?.role === "user") {
582
- const content2 = obj.payload.content;
583
- if (Array.isArray(content2)) {
584
- for (const block of content2) {
585
- if (block.type === "input_text" && block.text) {
586
- const text = cleanText(block.text);
587
- if (isCodexBoilerplate(text))
588
- continue;
589
- if (text.length > 50)
590
- turns.push(text);
591
- }
592
- }
593
- }
594
- }
595
- } catch {
596
- continue;
597
- }
598
- }
599
- if (turns.length === 0) {
600
- stats.filtered++;
601
- continue;
602
- }
603
- const body = turns.map((t) => t.length > 500 ? t.slice(0, 500) + "..." : t).join(`
604
-
605
- ---
606
-
607
- `);
608
- let content = `# Codex Session | ${date || "unknown"}
609
-
610
- ${body}`;
611
- if (content.length > MAX_CONTENT2)
612
- content = content.slice(0, MAX_CONTENT2) + `
613
-
614
- [truncated]`;
615
- const contentHash = createContentHash(content);
616
- const dedupKey = `codex:${sessionId}`;
617
- const fingerprint = `${dedupKey}:${contentHash}`;
618
- if (isIngested("codex", fingerprint)) {
619
- stats.deduped++;
620
- continue;
621
- }
622
- memories.push({
623
- content,
624
- containerTag: "codex-chats",
625
- metadata: {
626
- dedupKey,
627
- contentHash,
628
- source: "codex-session",
629
- sessionId,
630
- date: date || "unknown",
631
- ...project ? { project } : {}
632
- }
633
- });
634
- sessionIds.push(sessionId);
635
- } catch {}
636
- }
564
+ });
637
565
  }
638
- } catch {}
639
- }
640
-
641
- // src/ingest/cursor.ts
642
- import { readFileSync as readFileSync4, statSync } from "node:fs";
643
- import { join as join5 } from "node:path";
644
- import { createRequire as createRequire2 } from "node:module";
645
- var MAX_CONTENT3 = 48000;
646
- var MAX_DB_SIZE = 2 * 1024 * 1024 * 1024;
647
- var WARN_DB_SIZE = 500 * 1024 * 1024;
648
- var MIN_TURN_LEN2 = 50;
649
- function loadSqlJs(wasmDir) {
650
- try {
651
- if (wasmDir) {
652
- const require2 = createRequire2(join5(wasmDir, "sql.js", "package.json"));
653
- return require2("sql.js");
654
- } else {
655
- const require2 = createRequire2(import.meta.url);
656
- return require2("sql.js");
566
+ get cursor() {
567
+ return this._cursor;
657
568
  }
658
- } catch {
659
- return null;
660
- }
569
+ get masked() {
570
+ return this.value.replaceAll(/./g, this._mask);
571
+ }
572
+ };
573
+ });
574
+
575
+ // node_modules/@clack/prompts/dist/index.mjs
576
+ import { stripVTControlCharacters as S2 } from "node:util";
577
+ import y2 from "node:process";
578
+ function ce() {
579
+ return y2.platform !== "win32" ? y2.env.TERM !== "linux" : !!y2.env.CI || !!y2.env.WT_SESSION || !!y2.env.TERMINUS_SUBLIME || y2.env.ConEmuTask === "{cmd::Cmder}" || y2.env.TERM_PROGRAM === "Terminus-Sublime" || y2.env.TERM_PROGRAM === "vscode" || y2.env.TERM === "xterm-256color" || y2.env.TERM === "alacritty" || y2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
661
580
  }
662
- function openDb(initSqlJs, dbPath, wasmDir) {
663
- const locateOpts = {};
664
- if (wasmDir) {
665
- locateOpts.locateFile = (file) => join5(wasmDir, "sql.js", "dist", file);
581
+ var import_picocolors2, import_sisteransi2, V2, u = (t, n) => V2 ? t : n, le, L2, W2, C, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U2, K2, b2 = (t) => {
582
+ switch (t) {
583
+ case "initial":
584
+ case "active":
585
+ return import_picocolors2.default.cyan(le);
586
+ case "cancel":
587
+ return import_picocolors2.default.red(L2);
588
+ case "error":
589
+ return import_picocolors2.default.yellow(W2);
590
+ case "submit":
591
+ return import_picocolors2.default.green(C);
666
592
  }
667
- return initSqlJs(locateOpts).then((SQL) => {
668
- const buffer = readFileSync4(dbPath);
669
- return new SQL.Database(buffer);
593
+ }, G2 = (t) => {
594
+ const { cursor: n, options: r2, style: i } = t, s = t.maxItems ?? Number.POSITIVE_INFINITY, c = Math.max(process.stdout.rows - 4, 0), a = Math.min(c, Math.max(s, 5));
595
+ let l2 = 0;
596
+ n >= l2 + a - 3 ? l2 = Math.max(Math.min(n - a + 3, r2.length - a), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
597
+ const $2 = a < r2.length && l2 > 0, g2 = a < r2.length && l2 + a < r2.length;
598
+ return r2.slice(l2, l2 + a).map((p2, v2, f) => {
599
+ const j2 = v2 === 0 && $2, E = v2 === f.length - 1 && g2;
600
+ return j2 || E ? import_picocolors2.default.dim("...") : i(p2, v2 + l2 === n);
670
601
  });
671
- }
672
- function extractTurns(db, composerId, bubbleHeaders) {
673
- const turns = [];
674
- let pendingUser = null;
675
- for (const header of bubbleHeaders) {
676
- let text = "";
677
- try {
678
- const result = db.exec(`SELECT value FROM cursorDiskKV WHERE key = 'bubbleId:${composerId}:${header.bubbleId}'`);
679
- if (result.length > 0) {
680
- const bubble = JSON.parse(result[0].values[0][0]);
681
- text = cleanText(bubble.text || "");
682
- }
683
- } catch {
684
- continue;
685
- }
686
- if (text.length < MIN_TURN_LEN2)
687
- continue;
688
- if (header.type === 1) {
689
- pendingUser = text;
690
- } else if (header.type === 2 && pendingUser) {
691
- turns.push({ user: pendingUser, assistant: text });
692
- pendingUser = null;
693
- }
694
- }
695
- return turns;
696
- }
697
- async function ingestCursor(dbPath, wasmDir) {
698
- const memories = [];
699
- const sessionIds = [];
700
- let filtered = 0;
701
- let deduped = 0;
702
- let fileSize;
703
- try {
704
- fileSize = statSync(dbPath).size;
705
- } catch {
706
- console.log(" Skipping Cursor: database not accessible");
707
- return { memories, sessionIds, skipped: 0, filtered, deduped };
708
- }
709
- if (fileSize > MAX_DB_SIZE) {
710
- console.log(` Skipping Cursor: database too large (${(fileSize / 1024 / 1024 / 1024).toFixed(1)}GB)`);
711
- return { memories, sessionIds, skipped: 0, filtered, deduped };
712
- }
713
- if (fileSize > WARN_DB_SIZE) {
714
- console.log(` Warning: large database (${(fileSize / 1024 / 1024).toFixed(0)}MB), loading into memory...`);
715
- }
716
- const initSqlJs = loadSqlJs(wasmDir);
717
- if (!initSqlJs) {
718
- console.log(" Skipping Cursor: sql.js not available");
719
- return { memories, sessionIds, skipped: 0, filtered, deduped };
720
- }
721
- let db;
722
- try {
723
- db = await openDb(initSqlJs, dbPath, wasmDir);
724
- } catch (e) {
725
- console.log(` Skipping Cursor: cannot open database (${e.message})`);
726
- return { memories, sessionIds, skipped: 0, filtered, deduped };
602
+ }, ge = (t) => new MD({ validate: t.validate, mask: t.mask ?? $e, render() {
603
+ const n = `${import_picocolors2.default.gray(o)}
604
+ ${b2(this.state)} ${t.message}
605
+ `, r2 = this.valueWithCursor, i = this.masked;
606
+ switch (this.state) {
607
+ case "error":
608
+ return `${n.trim()}
609
+ ${import_picocolors2.default.yellow(o)} ${i}
610
+ ${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(this.error)}
611
+ `;
612
+ case "submit":
613
+ return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(i)}`;
614
+ case "cancel":
615
+ return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(i ?? ""))}${i ? `
616
+ ${import_picocolors2.default.gray(o)}` : ""}`;
617
+ default:
618
+ return `${n}${import_picocolors2.default.cyan(o)} ${r2}
619
+ ${import_picocolors2.default.cyan(d2)}
620
+ `;
727
621
  }
728
- try {
729
- let rows = [];
730
- try {
731
- const results = db.exec("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'");
732
- if (results.length > 0)
733
- rows = results[0].values;
734
- } catch {}
735
- for (const [key, value] of rows) {
736
- const composerId = key.replace("composerData:", "");
737
- let parsed;
738
- try {
739
- parsed = JSON.parse(value);
740
- if (!parsed || typeof parsed !== "object") {
741
- filtered++;
742
- continue;
743
- }
744
- } catch {
745
- filtered++;
746
- continue;
747
- }
748
- const bubbleHeaders = parsed.fullConversationHeadersOnly;
749
- if (!Array.isArray(bubbleHeaders) || bubbleHeaders.length === 0) {
750
- filtered++;
751
- continue;
622
+ } }).prompt(), ye = (t) => {
623
+ const n = t.active ?? "Yes", r2 = t.inactive ?? "No";
624
+ return new dD({ active: n, inactive: r2, initialValue: t.initialValue ?? true, render() {
625
+ const i = `${import_picocolors2.default.gray(o)}
626
+ ${b2(this.state)} ${t.message}
627
+ `, s = this.value ? n : r2;
628
+ switch (this.state) {
629
+ case "submit":
630
+ return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(s)}`;
631
+ case "cancel":
632
+ return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}
633
+ ${import_picocolors2.default.gray(o)}`;
634
+ default:
635
+ return `${i}${import_picocolors2.default.cyan(o)} ${this.value ? `${import_picocolors2.default.green(k2)} ${n}` : `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(r2)}` : `${import_picocolors2.default.green(k2)} ${r2}`}
636
+ ${import_picocolors2.default.cyan(d2)}
637
+ `;
638
+ }
639
+ } }).prompt();
640
+ }, fe = (t) => {
641
+ const n = (r2, i) => {
642
+ const s = r2.label ?? String(r2.value);
643
+ return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(T)} ${import_picocolors2.default.dim(s)} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}` : i === "active-selected" ? `${import_picocolors2.default.green(T)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.dim(F)} ${import_picocolors2.default.dim(s)}`;
644
+ };
645
+ return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? true, cursorAt: t.cursorAt, validate(r2) {
646
+ if (this.required && r2.length === 0)
647
+ return `Please select at least one option.
648
+ ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
649
+ }, render() {
650
+ const r2 = `${import_picocolors2.default.gray(o)}
651
+ ${b2(this.state)} ${t.message}
652
+ `, i = (s, c) => {
653
+ const a = this.value.includes(s.value);
654
+ return c && a ? n(s, "active-selected") : a ? n(s, "selected") : n(s, c ? "active" : "inactive");
655
+ };
656
+ switch (this.state) {
657
+ case "submit":
658
+ return `${r2}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => n(s, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
659
+ case "cancel": {
660
+ const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => n(c, "cancelled")).join(import_picocolors2.default.dim(", "));
661
+ return `${r2}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
662
+ ${import_picocolors2.default.gray(o)}` : ""}`;
752
663
  }
753
- const turns = extractTurns(db, composerId, bubbleHeaders);
754
- if (turns.length === 0) {
755
- filtered++;
756
- continue;
664
+ case "error": {
665
+ const s = this.error.split(`
666
+ `).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
667
+ `);
668
+ return `${r2 + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
669
+ ${import_picocolors2.default.yellow(o)} `)}
670
+ ${s}
671
+ `;
757
672
  }
758
- const sessionName = parsed.name || "Cursor Chat";
759
- const date = parsed.createdAt ? new Date(parsed.createdAt).toISOString().slice(0, 10) : "unknown";
760
- const header = `# ${sessionName} | ${date}`;
761
- const body = turns.map((t) => {
762
- const q = t.user.length > 300 ? t.user.slice(0, 300) + "..." : t.user;
763
- return `## Q: ${q}
764
-
765
- ${t.assistant}`;
766
- }).join(`
673
+ default:
674
+ return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
675
+ ${import_picocolors2.default.cyan(o)} `)}
676
+ ${import_picocolors2.default.cyan(d2)}
677
+ `;
678
+ }
679
+ } }).prompt();
680
+ }, Me = (t = "", n = "") => {
681
+ const r2 = `
682
+ ${t}
683
+ `.split(`
684
+ `), i = S2(n).length, s = Math.max(r2.reduce((a, l2) => {
685
+ const $2 = S2(l2);
686
+ return $2.length > a ? $2.length : a;
687
+ }, 0), i) + 2, c = r2.map((a) => `${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(a)}${" ".repeat(s - S2(a).length)}${import_picocolors2.default.gray(o)}`).join(`
688
+ `);
689
+ process.stdout.write(`${import_picocolors2.default.gray(o)}
690
+ ${import_picocolors2.default.green(C)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(_2.repeat(Math.max(s - i - 1, 1)) + me)}
691
+ ${c}
692
+ ${import_picocolors2.default.gray(de + _2.repeat(s + 2) + pe)}
693
+ `);
694
+ }, xe = (t = "") => {
695
+ process.stdout.write(`${import_picocolors2.default.gray(d2)} ${import_picocolors2.default.red(t)}
767
696
 
768
- ---
697
+ `);
698
+ }, Ie = (t = "") => {
699
+ process.stdout.write(`${import_picocolors2.default.gray(ue)} ${t}
700
+ `);
701
+ }, Se = (t = "") => {
702
+ process.stdout.write(`${import_picocolors2.default.gray(o)}
703
+ ${import_picocolors2.default.gray(d2)} ${t}
769
704
 
770
705
  `);
771
- let content = `${header}
706
+ }, J, Y2 = ({ indicator: t = "dots" } = {}) => {
707
+ const n = V2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], r2 = V2 ? 80 : 120, i = process.env.CI === "true";
708
+ let s, c, a = false, l2 = "", $2, g2 = performance.now();
709
+ const p2 = (m2) => {
710
+ const h2 = m2 > 1 ? "Something went wrong" : "Canceled";
711
+ a && N2(h2, m2);
712
+ }, v2 = () => p2(2), f = () => p2(1), j2 = () => {
713
+ process.on("uncaughtExceptionMonitor", v2), process.on("unhandledRejection", v2), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p2);
714
+ }, E = () => {
715
+ process.removeListener("uncaughtExceptionMonitor", v2), process.removeListener("unhandledRejection", v2), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p2);
716
+ }, B2 = () => {
717
+ if ($2 === undefined)
718
+ return;
719
+ i && process.stdout.write(`
720
+ `);
721
+ const m2 = $2.split(`
722
+ `);
723
+ process.stdout.write(import_sisteransi2.cursor.move(-999, m2.length - 1)), process.stdout.write(import_sisteransi2.erase.down(m2.length));
724
+ }, R2 = (m2) => m2.replace(/\.+$/, ""), O2 = (m2) => {
725
+ const h2 = (performance.now() - m2) / 1000, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
726
+ return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
727
+ }, H2 = (m2 = "") => {
728
+ a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
729
+ `);
730
+ let h2 = 0, w2 = 0;
731
+ j2(), c = setInterval(() => {
732
+ if (i && l2 === $2)
733
+ return;
734
+ B2(), $2 = l2;
735
+ const I2 = import_picocolors2.default.magenta(n[h2]);
736
+ if (i)
737
+ process.stdout.write(`${I2} ${l2}...`);
738
+ else if (t === "timer")
739
+ process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
740
+ else {
741
+ const z2 = ".".repeat(Math.floor(w2)).slice(0, 3);
742
+ process.stdout.write(`${I2} ${l2}${z2}`);
743
+ }
744
+ h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
745
+ }, r2);
746
+ }, N2 = (m2 = "", h2 = 0) => {
747
+ a = false, clearInterval(c), B2();
748
+ const w2 = h2 === 0 ? import_picocolors2.default.green(C) : h2 === 1 ? import_picocolors2.default.red(L2) : import_picocolors2.default.red(W2);
749
+ l2 = R2(m2 ?? l2), t === "timer" ? process.stdout.write(`${w2} ${l2} ${O2(g2)}
750
+ `) : process.stdout.write(`${w2} ${l2}
751
+ `), E(), s();
752
+ };
753
+ return { start: H2, stop: N2, message: (m2 = "") => {
754
+ l2 = R2(m2 ?? l2);
755
+ } };
756
+ };
757
+ var init_dist2 = __esm(() => {
758
+ init_dist();
759
+ init_dist();
760
+ import_picocolors2 = __toESM(require_picocolors(), 1);
761
+ import_sisteransi2 = __toESM(require_src(), 1);
762
+ V2 = ce();
763
+ le = u("◆", "*");
764
+ L2 = u("■", "x");
765
+ W2 = u("▲", "x");
766
+ C = u("◇", "o");
767
+ ue = u("┌", "T");
768
+ o = u("│", "|");
769
+ d2 = u("└", "—");
770
+ k2 = u("●", ">");
771
+ P2 = u("○", " ");
772
+ A2 = u("◻", "[•]");
773
+ T = u("◼", "[+]");
774
+ F = u("◻", "[ ]");
775
+ $e = u("▪", "•");
776
+ _2 = u("─", "-");
777
+ me = u("╮", "+");
778
+ de = u("├", "+");
779
+ pe = u("╯", "+");
780
+ q = u("●", "•");
781
+ D = u("◆", "*");
782
+ U2 = u("▲", "!");
783
+ K2 = u("■", "x");
784
+ J = `${import_picocolors2.default.gray(o)} `;
785
+ });
772
786
 
773
- ${body}`;
774
- if (content.length > MAX_CONTENT3)
775
- content = content.slice(0, MAX_CONTENT3) + `
787
+ // src/interactive.ts
788
+ var exports_interactive = {};
789
+ __export(exports_interactive, {
790
+ startSetup: () => startSetup,
791
+ spinner: () => Y2,
792
+ showDetectedApps: () => showDetectedApps,
793
+ selectMcpTargets: () => selectMcpTargets,
794
+ selectChatSources: () => selectChatSources,
795
+ promptApiKey: () => promptApiKey,
796
+ finishSetup: () => finishSetup,
797
+ confirmIndexCodebase: () => confirmIndexCodebase
798
+ });
799
+ function formatDetectedCount(count) {
800
+ if (count === undefined)
801
+ return "available";
802
+ return `${count.toLocaleString()} chats`;
803
+ }
804
+ function ensureValue(value) {
805
+ if (pD(value)) {
806
+ xe("Setup cancelled.");
807
+ process.exit(0);
808
+ }
809
+ return value;
810
+ }
811
+ function startSetup() {
812
+ Ie("Conare setup");
813
+ }
814
+ function finishSetup() {
815
+ Se("Starting setup...");
816
+ }
817
+ function showDetectedApps(targets) {
818
+ Me(targets.map((target) => `• ${target.label}: ${target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)}`).join(`
819
+ `), "Detected apps");
820
+ }
821
+ async function promptApiKey(options) {
822
+ if (options.providedApiKey) {
823
+ return options.providedApiKey;
824
+ }
825
+ const keyPrompt = await ge({
826
+ message: options.savedApiKey ? `API key (press Enter to use saved key ending in ${options.savedApiKey.slice(-6)})` : "API key",
827
+ mask: "*",
828
+ validate(value) {
829
+ const resolved = value.trim() || options.savedApiKey || "";
830
+ if (!resolved)
831
+ return "Enter an API key from https://mcp.conare.ai";
832
+ if (!resolved.startsWith("cmem_"))
833
+ return "API keys start with cmem_";
834
+ return;
835
+ }
836
+ });
837
+ return ensureValue(keyPrompt).trim() || options.savedApiKey;
838
+ }
839
+ async function selectChatSources(targets) {
840
+ return ensureValue(await fe({
841
+ message: "Select chat sources",
842
+ required: false,
843
+ initialValues: targets.filter((target) => target.recommended).map((target) => target.id),
844
+ options: targets.map((target) => ({
845
+ value: target.id,
846
+ label: target.label,
847
+ hint: target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)
848
+ }))
849
+ }));
850
+ }
851
+ async function selectMcpTargets(targets) {
852
+ return ensureValue(await fe({
853
+ message: "Select where to install the MCP",
854
+ required: false,
855
+ initialValues: targets.filter((target) => target.recommended).map((target) => target.id),
856
+ options: targets.map((target) => ({
857
+ value: target.id,
858
+ label: target.label,
859
+ hint: target.available === false ? "not detected" : "recommended"
860
+ }))
861
+ }));
862
+ }
863
+ async function confirmIndexCodebase() {
864
+ return ensureValue(await ye({
865
+ message: "Index this codebase too?",
866
+ initialValue: false
867
+ }));
868
+ }
869
+ var init_interactive = __esm(() => {
870
+ init_dist2();
871
+ });
776
872
 
777
- [truncated]`;
778
- const contentHash = createContentHash(content);
779
- const dedupKey = `cursor:${composerId}`;
780
- const fingerprint = `${dedupKey}:${contentHash}`;
781
- if (isIngested("cursor", fingerprint)) {
782
- deduped++;
783
- continue;
873
+ // src/index.ts
874
+ import { existsSync as existsSync8 } from "node:fs";
875
+ import { join as join10 } from "node:path";
876
+
877
+ // src/detect.ts
878
+ import { existsSync, readdirSync } from "node:fs";
879
+ import { spawnSync } from "node:child_process";
880
+ import { join } from "node:path";
881
+ import { homedir, platform } from "node:os";
882
+ function countJsonlFiles(dir) {
883
+ let count = 0;
884
+ try {
885
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
886
+ if (entry.isDirectory()) {
887
+ count += countJsonlFiles(join(dir, entry.name));
888
+ } else if (entry.name.endsWith(".jsonl")) {
889
+ count++;
784
890
  }
785
- memories.push({
786
- content,
787
- containerTag: "cursor-chats",
788
- metadata: {
789
- dedupKey,
790
- contentHash,
791
- source: "cursor",
792
- sessionId: composerId,
793
- name: sessionName,
794
- date
795
- }
796
- });
797
- sessionIds.push(composerId);
798
891
  }
799
- } catch (e) {
800
- console.log(` Cursor ingestion error: ${e.message}`);
801
- } finally {
802
- db.close();
803
- }
804
- return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
892
+ } catch {}
893
+ return count;
805
894
  }
806
-
807
- // src/ingest/codebase.ts
808
- import { createHash as createHash2 } from "node:crypto";
809
- import { readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync2, existsSync as existsSync4 } from "node:fs";
810
- import { join as join6, relative, extname, resolve } from "node:path";
811
- var DEFAULT_IGNORE = new Set([
812
- "node_modules",
813
- ".git",
814
- ".next",
815
- ".nuxt",
816
- ".output",
817
- "dist",
818
- "build",
819
- ".cache",
820
- "__pycache__",
821
- ".venv",
822
- "venv",
823
- "vendor",
824
- "target",
825
- ".turbo",
826
- ".DS_Store",
827
- ".claude",
828
- ".conare"
829
- ]);
830
- var IGNORE_FILES = new Set([
831
- "package-lock.json",
832
- "bun.lockb",
833
- "yarn.lock",
834
- "pnpm-lock.yaml"
835
- ]);
836
- var CODE_EXTENSIONS = new Set([
837
- ".ts",
838
- ".tsx",
839
- ".js",
840
- ".jsx",
841
- ".mjs",
842
- ".cjs",
843
- ".vue",
844
- ".svelte",
845
- ".astro",
846
- ".py",
847
- ".rb",
848
- ".go",
849
- ".rs",
850
- ".java",
851
- ".kt",
852
- ".swift",
853
- ".c",
854
- ".cpp",
855
- ".h",
856
- ".css",
857
- ".scss",
858
- ".less",
859
- ".html",
860
- ".json",
861
- ".yaml",
862
- ".yml",
863
- ".toml",
864
- ".md",
865
- ".mdx",
866
- ".txt",
867
- ".sql",
868
- ".graphql",
869
- ".prisma",
870
- ".sh",
871
- ".bash",
872
- ".zsh",
873
- ".lua",
874
- ".zig",
875
- ".ex",
876
- ".exs"
877
- ]);
878
- var SPECIAL_FILES = new Set([
879
- "Dockerfile",
880
- "Makefile",
881
- "Procfile",
882
- "Justfile",
883
- ".gitignore",
884
- ".dockerignore",
885
- "CLAUDE.md",
886
- "AGENTS.md",
887
- "README.md",
888
- "ARCHITECTURE.md",
889
- "wrangler.toml",
890
- "wrangler.json"
891
- ]);
892
- var MAX_FILE_SIZE = 1e5;
893
- function parseGitignore(rootPath) {
894
- const patterns = new Set;
895
- const gitignorePath = join6(rootPath, ".gitignore");
896
- if (!existsSync4(gitignorePath))
897
- return patterns;
895
+ function countCursorSessions(dbPath) {
898
896
  try {
899
- const content = readFileSync5(gitignorePath, "utf-8");
900
- for (const line of content.split(`
901
- `)) {
902
- const trimmed = line.trim();
903
- if (!trimmed || trimmed.startsWith("#"))
904
- continue;
905
- patterns.add(trimmed.replace(/\/$/, ""));
897
+ const result = spawnSync("sqlite3", [
898
+ dbPath,
899
+ `
900
+ WITH composer_rows AS (
901
+ SELECT substr(key, 14) AS composer_id, value AS composer_json
902
+ FROM cursorDiskKV
903
+ WHERE key LIKE 'composerData:%'
904
+ ),
905
+ headers AS (
906
+ SELECT
907
+ composer_id,
908
+ json_extract(j.value, '$.bubbleId') AS bubble_id,
909
+ json_extract(j.value, '$.type') AS type
910
+ FROM composer_rows, json_each(json_extract(composer_json, '$.fullConversationHeadersOnly')) AS j
911
+ ),
912
+ messages AS (
913
+ SELECT h.composer_id, h.type
914
+ FROM headers h
915
+ JOIN cursorDiskKV kv
916
+ ON kv.key = 'bubbleId:' || h.composer_id || ':' || h.bubble_id
917
+ WHERE h.type IN (1, 2)
918
+ AND length(COALESCE(json_extract(kv.value, '$.text'), '')) >= 50
919
+ )
920
+ SELECT count(*)
921
+ FROM (
922
+ SELECT composer_id
923
+ FROM messages
924
+ GROUP BY composer_id
925
+ HAVING SUM(CASE WHEN type = 1 THEN 1 ELSE 0 END) > 0
926
+ AND SUM(CASE WHEN type = 2 THEN 1 ELSE 0 END) > 0
927
+ );
928
+ `.trim()
929
+ ], { encoding: "utf-8" });
930
+ if (result.status !== 0)
931
+ return 0;
932
+ const count = Number.parseInt(result.stdout.trim(), 10);
933
+ return Number.isFinite(count) ? count : 0;
934
+ } catch {
935
+ return 0;
936
+ }
937
+ }
938
+ function detect() {
939
+ const home = homedir();
940
+ const tools = [];
941
+ const claudeDir = join(home, ".claude", "projects");
942
+ if (existsSync(claudeDir)) {
943
+ const sessionCount = countJsonlFiles(claudeDir);
944
+ tools.push({
945
+ name: "Claude Code",
946
+ available: sessionCount > 0,
947
+ path: claudeDir,
948
+ sessionCount
949
+ });
950
+ } else {
951
+ tools.push({ name: "Claude Code", available: false, path: claudeDir, sessionCount: 0 });
952
+ }
953
+ const codexHistory = join(home, ".codex", "history.jsonl");
954
+ const codexSessions = join(home, ".codex", "sessions");
955
+ if (existsSync(codexHistory) || existsSync(codexSessions)) {
956
+ let sessionCount = 0;
957
+ if (existsSync(codexHistory)) {
958
+ try {
959
+ const { readFileSync } = __require("node:fs");
960
+ const lines = readFileSync(codexHistory, "utf-8").split(`
961
+ `).filter(Boolean);
962
+ const sessions = new Set(lines.map((l) => {
963
+ try {
964
+ return JSON.parse(l).session_id;
965
+ } catch {
966
+ return null;
967
+ }
968
+ }));
969
+ sessions.delete(null);
970
+ sessionCount = sessions.size;
971
+ } catch {}
906
972
  }
907
- } catch {}
908
- return patterns;
973
+ tools.push({
974
+ name: "Codex",
975
+ available: true,
976
+ path: existsSync(codexHistory) ? codexHistory : codexSessions,
977
+ sessionCount
978
+ });
979
+ } else {
980
+ tools.push({ name: "Codex", available: false, path: codexHistory, sessionCount: 0 });
981
+ }
982
+ const os = platform();
983
+ let cursorDbPath;
984
+ if (os === "darwin") {
985
+ cursorDbPath = join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
986
+ } else if (os === "win32") {
987
+ cursorDbPath = join(process.env.APPDATA || join(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
988
+ } else {
989
+ cursorDbPath = join(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
990
+ }
991
+ tools.push({
992
+ name: "Cursor",
993
+ available: existsSync(cursorDbPath),
994
+ path: cursorDbPath,
995
+ sessionCount: existsSync(cursorDbPath) ? countCursorSessions(cursorDbPath) : 0
996
+ });
997
+ const openclawDir = join(home, ".openclaw");
998
+ tools.push({
999
+ name: "OpenClaw",
1000
+ available: existsSync(openclawDir),
1001
+ path: openclawDir,
1002
+ sessionCount: 0
1003
+ });
1004
+ return tools;
909
1005
  }
910
- function shouldIgnore(name, ignorePatterns) {
911
- if (DEFAULT_IGNORE.has(name))
912
- return true;
913
- if (IGNORE_FILES.has(name))
914
- return true;
915
- if (name.startsWith(".env"))
1006
+
1007
+ // src/ingest/claude.ts
1008
+ import { readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
1009
+ import { join as join3, basename } from "node:path";
1010
+ import { homedir as homedir3 } from "node:os";
1011
+
1012
+ // src/ingest/shared.ts
1013
+ import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "node:fs";
1014
+ import { createHash } from "node:crypto";
1015
+ import { join as join2 } from "node:path";
1016
+ import { homedir as homedir2 } from "node:os";
1017
+ var MANIFEST_PATH = join2(homedir2(), ".conare", "ingested.json");
1018
+ var MIN_SUBSTANTIVE = 200;
1019
+ var NARRATION_RE = /^[\s\n]*(Let me |Now let me |Now I['\u2019]|Now add |Now fix |Now replace |Now integrate |Now update |Now pass |Now clean |Now build|Update the |Builds clean|Deployed\.|Wait, I |Let['\u2019]s test |Good —|Great\.|Perfect\.|Alright|OK,? let me|I[''\u2019]ll |Starting |I need to |Need |I found |I read |I[''\u2019]ve (loaded|confirmed|verified)|Context loaded|Next (I[''\u2019]|step)|Deps confirm|Diff check|Still missing|I[''\u2019]ll (do|check|inspect|trace|run|grab|pull|read|verify))/;
1020
+ function isNarration(text) {
1021
+ const stripped = text.trim();
1022
+ if (stripped.length < MIN_SUBSTANTIVE)
916
1023
  return true;
917
- if (ignorePatterns.has(name))
1024
+ if (stripped.length <= 300 && NARRATION_RE.test(stripped))
918
1025
  return true;
919
1026
  return false;
920
1027
  }
921
- function langFromExt(ext) {
922
- const map = {
923
- ".ts": "typescript",
924
- ".tsx": "tsx",
925
- ".js": "javascript",
926
- ".jsx": "jsx",
927
- ".mjs": "javascript",
928
- ".cjs": "javascript",
929
- ".vue": "vue",
930
- ".svelte": "svelte",
931
- ".astro": "astro",
932
- ".py": "python",
933
- ".rb": "ruby",
934
- ".go": "go",
935
- ".rs": "rust",
936
- ".java": "java",
937
- ".kt": "kotlin",
938
- ".swift": "swift",
939
- ".c": "c",
940
- ".cpp": "cpp",
941
- ".h": "c",
942
- ".css": "css",
943
- ".scss": "scss",
944
- ".less": "less",
945
- ".html": "html",
946
- ".json": "json",
947
- ".yaml": "yaml",
948
- ".yml": "yaml",
949
- ".toml": "toml",
950
- ".md": "markdown",
951
- ".mdx": "mdx",
952
- ".sql": "sql",
953
- ".graphql": "graphql",
954
- ".prisma": "prisma",
955
- ".sh": "bash",
956
- ".bash": "bash",
957
- ".zsh": "zsh",
958
- ".lua": "lua",
959
- ".zig": "zig",
960
- ".ex": "elixir",
961
- ".exs": "elixir"
962
- };
963
- return map[ext] || ext.slice(1);
1028
+ function cleanText(raw) {
1029
+ let text = raw;
1030
+ text = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "");
1031
+ text = text.replace(/<attached-context[\s\S]*?<\/attached-context>/g, "");
1032
+ text = text.replace(/<environment_context>[\s\S]*?<\/environment_context>/g, "");
1033
+ return text.trim();
1034
+ }
1035
+ function createContentHash(content) {
1036
+ return createHash("sha256").update(content).digest("hex").slice(0, 16);
1037
+ }
1038
+ function getIngested() {
1039
+ try {
1040
+ if (existsSync2(MANIFEST_PATH)) {
1041
+ return JSON.parse(readFileSync(MANIFEST_PATH, "utf-8"));
1042
+ }
1043
+ } catch {}
1044
+ return {};
1045
+ }
1046
+ function markIngested(source, sessionIds) {
1047
+ const manifest = getIngested();
1048
+ const existing = new Set(manifest[source] || []);
1049
+ for (const id of sessionIds)
1050
+ existing.add(id);
1051
+ manifest[source] = [...existing];
1052
+ const dir = join2(homedir2(), ".conare");
1053
+ if (!existsSync2(dir))
1054
+ mkdirSync(dir, { recursive: true });
1055
+ writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
1056
+ }
1057
+ function isIngested(source, sessionId) {
1058
+ const manifest = getIngested();
1059
+ return (manifest[source] || []).includes(sessionId);
1060
+ }
1061
+ function clearIngested(source) {
1062
+ const manifest = getIngested();
1063
+ if (source) {
1064
+ delete manifest[source];
1065
+ } else {
1066
+ for (const key of Object.keys(manifest))
1067
+ delete manifest[key];
1068
+ }
1069
+ const dir = join2(homedir2(), ".conare");
1070
+ if (!existsSync2(dir))
1071
+ mkdirSync(dir, { recursive: true });
1072
+ writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
964
1073
  }
965
- function formatFile(relPath, content, ext) {
966
- const lang = langFromExt(ext);
967
- return `# File: ${relPath}
968
1074
 
969
- \`\`\`${lang}
970
- ${content}
971
- \`\`\``;
1075
+ // src/ingest/claude.ts
1076
+ var MAX_CONTENT = 48000;
1077
+ var MIN_TURN_LEN = 50;
1078
+ function extractText(content) {
1079
+ if (typeof content === "string")
1080
+ return content;
1081
+ if (!Array.isArray(content))
1082
+ return "";
1083
+ return content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join(`
1084
+ `);
972
1085
  }
973
- function indexCodebase(rootPath) {
974
- const absRoot = resolve(rootPath);
975
- const repoHash = createHash2("sha256").update(absRoot).digest("hex").slice(0, 12);
976
- const gitignorePatterns = parseGitignore(absRoot);
977
- const memories = [];
978
- let fileCount = 0;
979
- let skipped = 0;
980
- function walk(dir) {
981
- let entries;
1086
+ function parseSession(lines) {
1087
+ const rounds = [];
1088
+ let date = null;
1089
+ let currentUser = null;
1090
+ let currentAssistant = [];
1091
+ for (const line of lines) {
1092
+ if (!line.trim())
1093
+ continue;
1094
+ let obj;
982
1095
  try {
983
- entries = readdirSync4(dir, { withFileTypes: true });
1096
+ obj = JSON.parse(line);
984
1097
  } catch {
985
- return;
1098
+ continue;
986
1099
  }
987
- for (const entry of entries) {
988
- if (shouldIgnore(entry.name, gitignorePatterns))
989
- continue;
990
- const fullPath = join6(dir, entry.name);
991
- if (entry.isDirectory()) {
992
- walk(fullPath);
993
- continue;
994
- }
995
- if (!entry.isFile())
996
- continue;
997
- const ext = extname(entry.name).toLowerCase();
998
- if (!CODE_EXTENSIONS.has(ext) && !SPECIAL_FILES.has(entry.name))
999
- continue;
1000
- let stat;
1001
- try {
1002
- stat = statSync2(fullPath);
1003
- } catch {
1004
- continue;
1005
- }
1006
- if (stat.size > MAX_FILE_SIZE || stat.size === 0) {
1007
- skipped++;
1008
- continue;
1009
- }
1010
- let raw;
1011
- try {
1012
- raw = readFileSync5(fullPath, "utf-8");
1013
- } catch {
1014
- skipped++;
1015
- continue;
1016
- }
1017
- if (raw.includes("\x00")) {
1018
- skipped++;
1019
- continue;
1020
- }
1021
- const relPath = relative(absRoot, fullPath);
1022
- const contentHash = createContentHash(raw);
1023
- const dedupKey = `codebase:${repoHash}:${relPath}`;
1024
- const fingerprint = `${dedupKey}:${contentHash}`;
1025
- if (isIngested("codebase", fingerprint)) {
1026
- skipped++;
1027
- continue;
1028
- }
1029
- const content = formatFile(relPath, raw, ext);
1030
- memories.push({
1031
- content,
1032
- containerTag: "codebase",
1033
- metadata: {
1034
- dedupKey,
1035
- contentHash,
1036
- source: "codebase-index",
1037
- repoHash,
1038
- filePath: relPath,
1039
- fileHash: `${relPath}:${contentHash}`,
1040
- language: langFromExt(ext)
1100
+ if (!date && obj.timestamp)
1101
+ date = obj.timestamp.slice(0, 10);
1102
+ if (obj.type === "user") {
1103
+ const text = cleanText(extractText(obj.message?.content));
1104
+ if (text.length >= MIN_TURN_LEN) {
1105
+ if (currentUser !== null && currentAssistant.length > 0) {
1106
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
1041
1107
  }
1042
- });
1043
- fileCount++;
1044
- }
1045
- }
1046
- walk(absRoot);
1047
- return { memories, fileCount, skipped };
1048
- }
1049
-
1050
- // src/api.ts
1051
- var API_URL = "https://mcp.conare.ai";
1052
- function createUploadBatches(memories, maxItems = 50, maxChars = 1500000) {
1053
- const batches = [];
1054
- let current = [];
1055
- let currentChars = 0;
1056
- let startIndex = 0;
1057
- for (let i = 0;i < memories.length; i++) {
1058
- const item = memories[i];
1059
- const itemChars = item.content.length;
1060
- const exceedsBatch = current.length > 0 && (current.length >= maxItems || currentChars + itemChars > maxChars);
1061
- if (exceedsBatch) {
1062
- batches.push({ startIndex, items: current });
1063
- current = [];
1064
- currentChars = 0;
1065
- startIndex = i;
1108
+ currentUser = text;
1109
+ currentAssistant = [];
1110
+ }
1111
+ } else if (obj.type === "assistant") {
1112
+ const text = cleanText(extractText(obj.message?.content));
1113
+ if (text.length >= MIN_TURN_LEN) {
1114
+ currentAssistant.push(text);
1115
+ }
1066
1116
  }
1067
- current.push(item);
1068
- currentChars += itemChars;
1069
- }
1070
- if (current.length > 0) {
1071
- batches.push({ startIndex, items: current });
1072
1117
  }
1073
- return batches;
1074
- }
1075
- async function validateKey(apiKey) {
1076
- try {
1077
- const res = await fetch(`${API_URL}/api/auth/me`, {
1078
- headers: { Authorization: `Bearer ${apiKey}` }
1079
- });
1080
- if (!res.ok)
1081
- return { valid: false };
1082
- const data = await res.json();
1083
- return { valid: true, email: data.user.email, name: data.user.name };
1084
- } catch {
1085
- return { valid: false };
1118
+ if (currentUser !== null && currentAssistant.length > 0) {
1119
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
1086
1120
  }
1121
+ const turns = rounds.map((r) => ({
1122
+ user: r.user,
1123
+ assistant: r.assistantParts.filter((p) => !isNarration(p)).join(`
1124
+
1125
+ `)
1126
+ })).filter((t) => t.assistant.length >= MIN_TURN_LEN);
1127
+ return { turns, date };
1087
1128
  }
1088
- async function getRemoteMemoryCount(apiKey) {
1129
+ function ingestClaude() {
1130
+ const projectsDir = join3(homedir3(), ".claude", "projects");
1131
+ const memories = [];
1132
+ const sessionIds = [];
1133
+ let filtered = 0;
1134
+ let deduped = 0;
1135
+ let projectDirs;
1089
1136
  try {
1090
- const res = await fetch(`${API_URL}/api/containers`, {
1091
- headers: { Authorization: `Bearer ${apiKey}` }
1092
- });
1093
- if (!res.ok)
1094
- return null;
1095
- const data = await res.json();
1096
- if (!Array.isArray(data.containers))
1097
- return 0;
1098
- return data.containers.reduce((sum, container) => sum + (container.count || 0), 0);
1137
+ projectDirs = readdirSync2(projectsDir);
1099
1138
  } catch {
1100
- return null;
1139
+ return { memories, sessionIds, skipped: 0, filtered, deduped };
1101
1140
  }
1102
- }
1103
- async function uploadItems(apiKey, items, asyncEmbed = false) {
1104
- let retries = 4;
1105
- while (retries > 0) {
1141
+ for (const projDir of projectDirs) {
1142
+ const projPath = join3(projectsDir, projDir);
1143
+ const project = projDir.replace(/^-Users-[^-]+-/, "").replace(/-/g, "/") || projDir;
1144
+ let files;
1106
1145
  try {
1107
- const res = await fetch(`${API_URL}/api/memories/bulk`, {
1108
- method: "POST",
1109
- headers: {
1110
- "Content-Type": "application/json",
1111
- Authorization: `Bearer ${apiKey}`
1112
- },
1113
- body: JSON.stringify(asyncEmbed ? { items, async: true } : items)
1114
- });
1115
- if (res.status === 429) {
1116
- retries--;
1117
- await new Promise((r) => setTimeout(r, 5000));
1146
+ files = readdirSync2(projPath).filter((f) => f.endsWith(".jsonl"));
1147
+ } catch {
1148
+ continue;
1149
+ }
1150
+ for (const file of files) {
1151
+ const sessionId = basename(file, ".jsonl");
1152
+ const raw = readFileSync2(join3(projPath, file), "utf-8");
1153
+ const { turns, date } = parseSession(raw.split(`
1154
+ `));
1155
+ if (turns.length === 0) {
1156
+ filtered++;
1118
1157
  continue;
1119
1158
  }
1120
- if (!res.ok) {
1121
- const body = await res.text().catch(() => "");
1122
- throw new Error(`HTTP ${res.status}: ${body.slice(0, 120)}`);
1123
- }
1124
- const data = await res.json();
1125
- if (!Array.isArray(data.results) || data.results.length !== items.length) {
1126
- throw new Error("Unexpected bulk upload response");
1127
- }
1128
- return data.results;
1129
- } catch (error) {
1130
- retries--;
1131
- if (retries === 0) {
1132
- return items.map(() => ({
1133
- success: false,
1134
- error: error instanceof Error ? error.message : String(error)
1135
- }));
1159
+ const header = `# Chat: ${project}${date ? ` | ${date}` : ""}`;
1160
+ const body = turns.map((t) => {
1161
+ const q = t.user.length > 300 ? t.user.slice(0, 300) + "..." : t.user;
1162
+ return `## Q: ${q}
1163
+
1164
+ ${t.assistant}`;
1165
+ }).join(`
1166
+
1167
+ ---
1168
+
1169
+ `);
1170
+ let content = `${header}
1171
+
1172
+ ${body}`;
1173
+ if (content.length > MAX_CONTENT)
1174
+ content = content.slice(0, MAX_CONTENT) + `
1175
+
1176
+ [truncated]`;
1177
+ const contentHash = createContentHash(content);
1178
+ const dedupKey = `claude:${sessionId}`;
1179
+ const fingerprint = `${dedupKey}:${contentHash}`;
1180
+ if (isIngested("claude", fingerprint)) {
1181
+ deduped++;
1182
+ continue;
1136
1183
  }
1137
- await new Promise((r) => setTimeout(r, 2000));
1138
- }
1139
- }
1140
- return items.map(() => ({ success: false, error: "Upload failed" }));
1141
- }
1142
- async function uploadBulk(apiKey, memories, onProgress) {
1143
- let success = 0;
1144
- let failed = 0;
1145
- const total = memories.length;
1146
- const results = [];
1147
- const batches = createUploadBatches(memories);
1148
- for (const batch of batches) {
1149
- let batchResults = await uploadItems(apiKey, batch.items, true);
1150
- if (batch.items.length > 1 && batchResults.some((result) => !result.success)) {
1151
- const retriedResults = [];
1152
- for (let i = 0;i < batch.items.length; i++) {
1153
- const result = batchResults[i];
1154
- if (result.success) {
1155
- retriedResults.push(result);
1156
- continue;
1184
+ memories.push({
1185
+ content,
1186
+ containerTag: "claude-chats",
1187
+ metadata: {
1188
+ dedupKey,
1189
+ contentHash,
1190
+ source: "claude-code",
1191
+ sessionId,
1192
+ project,
1193
+ date: date || "unknown"
1157
1194
  }
1158
- const [singleResult] = await uploadItems(apiKey, [batch.items[i]], true);
1159
- retriedResults.push(singleResult);
1160
- }
1161
- batchResults = retriedResults;
1162
- }
1163
- for (let i = 0;i < batchResults.length; i++) {
1164
- const result = batchResults[i];
1165
- results.push({
1166
- index: batch.startIndex + i,
1167
- success: result.success,
1168
- deduped: result.deduped,
1169
- error: result.error
1170
1195
  });
1171
- if (result.success)
1172
- success++;
1173
- else
1174
- failed++;
1196
+ sessionIds.push(sessionId);
1175
1197
  }
1176
- onProgress?.(success + failed, total, failed);
1177
1198
  }
1178
- return { success, failed, results };
1199
+ return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
1179
1200
  }
1180
1201
 
1181
- // src/configure.ts
1182
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
1183
- import { dirname, join as join7 } from "node:path";
1184
- import { homedir as homedir5 } from "node:os";
1185
- import { spawnSync as spawnSync2 } from "node:child_process";
1186
- var CONARE_URL = "https://mcp.conare.ai";
1187
- var SERVER_NAME = "conare-memory";
1188
- var MCP_TARGETS = [
1189
- { id: "claude", label: "Claude Code" },
1190
- { id: "cursor", label: "Cursor" },
1191
- { id: "codex", label: "Codex" }
1192
- ];
1193
- function readJsonFile(path) {
1194
- try {
1195
- return JSON.parse(readFileSync6(path, "utf-8"));
1196
- } catch {
1197
- return {};
1198
- }
1199
- }
1200
- function writeJsonFile(path, data) {
1201
- mkdirSync2(dirname(path), { recursive: true });
1202
- writeFileSync2(path, JSON.stringify(data, null, 2) + `
1203
- `);
1204
- }
1205
- function getServerConfig(apiKey) {
1206
- return {
1207
- type: "http",
1208
- url: `${CONARE_URL}/mcp`,
1209
- headers: {
1210
- Authorization: `Bearer ${apiKey}`
1211
- }
1212
- };
1213
- }
1214
- function upsertMcpServer(path, apiKey) {
1215
- const config = readJsonFile(path);
1216
- if (!config.mcpServers || typeof config.mcpServers !== "object") {
1217
- config.mcpServers = {};
1218
- }
1219
- config.mcpServers[SERVER_NAME] = getServerConfig(apiKey);
1220
- writeJsonFile(path, config);
1202
+ // src/ingest/codex.ts
1203
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3 } from "node:fs";
1204
+ import { join as join4, basename as basename2 } from "node:path";
1205
+ import { homedir as homedir4 } from "node:os";
1206
+ var MAX_CONTENT2 = 48000;
1207
+ function isCodexBoilerplate(text) {
1208
+ return text.startsWith("# AGENTS.md instructions for") || text.startsWith("<INSTRUCTIONS>") || text.startsWith("<user_instructions>") || text.startsWith("<user_action>");
1221
1209
  }
1222
- function configureClaude(apiKey) {
1223
- const claudeConfigPath = join7(homedir5(), ".claude.json");
1224
- const claudeMcpPath = join7(homedir5(), ".claude", "mcp.json");
1225
- if (spawnSync2("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
1226
- stdio: "ignore"
1227
- }).status === 0) {
1228
- return "Claude Code configured via `claude mcp add-json`";
1229
- }
1230
- upsertMcpServer(claudeConfigPath, apiKey);
1231
- if (existsSync5(join7(homedir5(), ".claude"))) {
1232
- upsertMcpServer(claudeMcpPath, apiKey);
1233
- }
1234
- return `Claude Code configured at ${claudeConfigPath}`;
1210
+ function stripEnvironmentContext(text) {
1211
+ let cwd = null;
1212
+ const cwdMatch = text.match(/<cwd>([^<]+)<\/cwd>/);
1213
+ if (cwdMatch)
1214
+ cwd = cwdMatch[1];
1215
+ const cleaned = text.replace(/<environment_context>[\s\S]*?<\/environment_context>/g, "").trim();
1216
+ return { text: cleaned, cwd };
1235
1217
  }
1236
- function configureJsonClient(path, apiKey, label) {
1237
- upsertMcpServer(path, apiKey);
1238
- return `${label} configured at ${path}`;
1218
+ function projectFromCwd(cwd) {
1219
+ return cwd.replace(/^\/Users\/[^/]+\//, "");
1239
1220
  }
1240
- function configureMcp(apiKey, targets = ["claude", "cursor", "codex"]) {
1241
- const results = [];
1242
- if (targets.includes("claude")) {
1243
- results.push(configureClaude(apiKey));
1244
- }
1245
- if (targets.includes("cursor")) {
1246
- results.push(configureJsonClient(join7(homedir5(), ".cursor", "mcp.json"), apiKey, "Cursor"));
1221
+ function ingestCodex() {
1222
+ const memories = [];
1223
+ const sessionIds = [];
1224
+ let filtered = 0;
1225
+ let deduped = 0;
1226
+ const historyPath = join4(homedir4(), ".codex", "history.jsonl");
1227
+ if (existsSync3(historyPath)) {
1228
+ try {
1229
+ const lines = readFileSync3(historyPath, "utf-8").split(`
1230
+ `).filter(Boolean);
1231
+ const sessions = new Map;
1232
+ for (const line of lines) {
1233
+ try {
1234
+ const obj = JSON.parse(line);
1235
+ if (!obj.session_id || !obj.text)
1236
+ continue;
1237
+ if (!sessions.has(obj.session_id))
1238
+ sessions.set(obj.session_id, []);
1239
+ sessions.get(obj.session_id).push({ ts: obj.ts, text: obj.text });
1240
+ } catch {
1241
+ continue;
1242
+ }
1243
+ }
1244
+ for (const [sessionId, entries] of sessions) {
1245
+ entries.sort((a, b) => a.ts - b.ts);
1246
+ const date = new Date(entries[0].ts * 1000).toISOString().slice(0, 10);
1247
+ let project = null;
1248
+ const cleanEntries = [];
1249
+ for (const e of entries) {
1250
+ let text = cleanText(e.text);
1251
+ if (isCodexBoilerplate(text))
1252
+ continue;
1253
+ const env = stripEnvironmentContext(text);
1254
+ text = env.text;
1255
+ if (!project && env.cwd)
1256
+ project = projectFromCwd(env.cwd);
1257
+ if (text.length === 0)
1258
+ continue;
1259
+ cleanEntries.push(text.length > 300 ? text.slice(0, 300) + "..." : text);
1260
+ }
1261
+ const body = cleanEntries.filter(Boolean).join(`
1262
+
1263
+ ---
1264
+
1265
+ `);
1266
+ let content = `# Codex Chat | ${date}
1267
+
1268
+ ${body}`;
1269
+ if (content.length > MAX_CONTENT2)
1270
+ content = content.slice(0, MAX_CONTENT2) + `
1271
+
1272
+ [truncated]`;
1273
+ if (content.length < 100) {
1274
+ filtered++;
1275
+ continue;
1276
+ }
1277
+ const contentHash = createContentHash(content);
1278
+ const dedupKey = `codex:${sessionId}`;
1279
+ const fingerprint = `${dedupKey}:${contentHash}`;
1280
+ if (isIngested("codex", fingerprint)) {
1281
+ deduped++;
1282
+ continue;
1283
+ }
1284
+ memories.push({
1285
+ content,
1286
+ containerTag: "codex-chats",
1287
+ metadata: {
1288
+ dedupKey,
1289
+ contentHash,
1290
+ source: "codex",
1291
+ sessionId,
1292
+ date,
1293
+ ...project ? { project } : {}
1294
+ }
1295
+ });
1296
+ sessionIds.push(sessionId);
1297
+ }
1298
+ } catch {}
1247
1299
  }
1248
- if (targets.includes("codex")) {
1249
- results.push(configureJsonClient(join7(homedir5(), ".codex", "mcp.json"), apiKey, "Codex"));
1300
+ const sessionsDir = join4(homedir4(), ".codex", "sessions");
1301
+ if (existsSync3(sessionsDir)) {
1302
+ try {
1303
+ const stats = { filtered: 0, deduped: 0 };
1304
+ walkCodexSessions(sessionsDir, memories, sessionIds, stats);
1305
+ filtered += stats.filtered;
1306
+ deduped += stats.deduped;
1307
+ } catch {}
1250
1308
  }
1251
- return results;
1309
+ return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
1252
1310
  }
1253
-
1254
- // src/config.ts
1255
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
1256
- import { join as join8 } from "node:path";
1257
- import { homedir as homedir6 } from "node:os";
1258
- var CONFIG_DIR = join8(homedir6(), ".conare");
1259
- var CONFIG_PATH = join8(CONFIG_DIR, "config.json");
1260
- function readConfig() {
1311
+ function walkCodexSessions(dir, memories, sessionIds, stats) {
1261
1312
  try {
1262
- if (!existsSync6(CONFIG_PATH))
1263
- return {};
1264
- return JSON.parse(readFileSync7(CONFIG_PATH, "utf-8"));
1265
- } catch {
1266
- return {};
1267
- }
1268
- }
1269
- function saveApiKey(apiKey) {
1270
- mkdirSync3(CONFIG_DIR, { recursive: true });
1271
- writeFileSync3(CONFIG_PATH, JSON.stringify({ apiKey }, null, 2) + `
1272
- `);
1273
- }
1274
- function getSavedApiKey() {
1275
- return readConfig().apiKey;
1276
- }
1277
-
1278
- // src/sync.ts
1279
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as readFileSync8, chmodSync, cpSync, rmSync } from "node:fs";
1280
- import { join as join9, dirname as dirname2 } from "node:path";
1281
- import { homedir as homedir7, platform as platform2 } from "node:os";
1282
- import { execSync } from "node:child_process";
1283
- var CONARE_DIR = join9(homedir7(), ".conare");
1284
- var BIN_DIR = join9(CONARE_DIR, "bin");
1285
- var CONFIG_PATH2 = join9(CONARE_DIR, "config.json");
1286
- var PLIST_LABEL = "ai.conare.ingest";
1287
- var PLIST_PATH = join9(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
1288
- var SYSTEMD_DIR = join9(homedir7(), ".config", "systemd", "user");
1289
- var SYSTEMD_SERVICE = join9(SYSTEMD_DIR, "conare-sync.service");
1290
- var SYSTEMD_TIMER = join9(SYSTEMD_DIR, "conare-sync.timer");
1291
- var RUN_SH = `#!/bin/bash
1292
- # Conare Memory background sync wrapper
1293
- # Resolves node at runtime to survive nvm/fnm upgrades
1313
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
1314
+ if (entry.isDirectory()) {
1315
+ walkCodexSessions(join4(dir, entry.name), memories, sessionIds, stats);
1316
+ } else if (entry.name.endsWith(".jsonl")) {
1317
+ const sessionId = basename2(entry.name, ".jsonl");
1318
+ if (sessionIds.includes(sessionId))
1319
+ continue;
1320
+ try {
1321
+ const lines = readFileSync3(join4(dir, entry.name), "utf-8").split(`
1322
+ `).filter(Boolean);
1323
+ let date = null;
1324
+ let project = null;
1325
+ const rounds = [];
1326
+ let currentUser = null;
1327
+ let currentAssistant = [];
1328
+ for (const line of lines) {
1329
+ try {
1330
+ const obj = JSON.parse(line);
1331
+ if (!date && obj.timestamp)
1332
+ date = obj.timestamp.slice(0, 10);
1333
+ if (obj.type === "session_meta" && obj.payload?.cwd) {
1334
+ project = projectFromCwd(obj.payload.cwd);
1335
+ }
1336
+ if (obj.type !== "response_item" || obj.payload?.type !== "message")
1337
+ continue;
1338
+ const role = obj.payload.role;
1339
+ const msgContent = obj.payload.content;
1340
+ if (!Array.isArray(msgContent))
1341
+ continue;
1342
+ if (role === "user") {
1343
+ for (const block of msgContent) {
1344
+ if (block.type === "input_text" && block.text) {
1345
+ const text = cleanText(block.text);
1346
+ if (isCodexBoilerplate(text))
1347
+ continue;
1348
+ if (text.length >= 50) {
1349
+ if (currentUser !== null && currentAssistant.length > 0) {
1350
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
1351
+ }
1352
+ currentUser = text;
1353
+ currentAssistant = [];
1354
+ }
1355
+ }
1356
+ }
1357
+ } else if (role === "assistant") {
1358
+ for (const block of msgContent) {
1359
+ if (block.type === "output_text" && block.text) {
1360
+ const text = cleanText(block.text);
1361
+ if (!isNarration(text)) {
1362
+ currentAssistant.push(text);
1363
+ }
1364
+ }
1365
+ }
1366
+ }
1367
+ } catch {
1368
+ continue;
1369
+ }
1370
+ }
1371
+ if (currentUser !== null && currentAssistant.length > 0) {
1372
+ rounds.push({ user: currentUser, assistantParts: currentAssistant });
1373
+ }
1374
+ if (rounds.length === 0) {
1375
+ stats.filtered++;
1376
+ continue;
1377
+ }
1378
+ const body = rounds.map((r) => {
1379
+ const q = r.user.length > 300 ? r.user.slice(0, 300) + "..." : r.user;
1380
+ const assistant = r.assistantParts.join(`
1294
1381
 
1295
- CONARE_DIR="$HOME/.conare"
1296
- LOCKFILE="$CONARE_DIR/sync.lock"
1297
- LOG="$CONARE_DIR/ingest.log"
1382
+ `);
1383
+ return `## Q: ${q}
1298
1384
 
1299
- # Log rotation: truncate if > 1MB
1300
- if [ -f "$LOG" ] && [ "$(wc -c < "$LOG" 2>/dev/null)" -gt 1048576 ]; then
1301
- tail -100 "$LOG" > "$LOG.tmp" && mv "$LOG.tmp" "$LOG"
1302
- fi
1385
+ ${assistant}`;
1386
+ }).join(`
1303
1387
 
1304
- # File lock: prevent concurrent runs
1305
- # macOS doesn't have flock — use mkdir as atomic lock
1306
- if ! mkdir "$LOCKFILE.d" 2>/dev/null; then
1307
- # Check if stale (older than 30 min)
1308
- if [ "$(uname)" = "Darwin" ]; then
1309
- LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$LOCKFILE.d" 2>/dev/null || echo 0) ))
1310
- else
1311
- LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$LOCKFILE.d" 2>/dev/null || echo 0) ))
1312
- fi
1313
- if [ "$LOCK_AGE" -gt 1800 ]; then
1314
- rm -rf "$LOCKFILE.d"
1315
- mkdir "$LOCKFILE.d" 2>/dev/null || true
1316
- else
1317
- echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) SKIP: another instance running" >> "$LOG"
1318
- exit 0
1319
- fi
1320
- fi
1321
- trap 'rm -rf "$LOCKFILE.d"' EXIT
1388
+ ---
1322
1389
 
1323
- # Resolve node binary
1324
- resolve_node() {
1325
- command -v node >/dev/null 2>&1 && { command -v node; return; }
1326
- if [ -s "$HOME/.nvm/nvm.sh" ]; then
1327
- . "$HOME/.nvm/nvm.sh" >/dev/null 2>&1
1328
- command -v node >/dev/null 2>&1 && { command -v node; return; }
1329
- fi
1330
- if command -v fnm >/dev/null 2>&1; then
1331
- eval "$(fnm env)" >/dev/null 2>&1
1332
- command -v node >/dev/null 2>&1 && { command -v node; return; }
1333
- fi
1334
- for p in /opt/homebrew/bin/node /usr/local/bin/node /usr/bin/node; do
1335
- [ -x "$p" ] && { echo "$p"; return; }
1336
- done
1337
- return 1
1338
- }
1390
+ `);
1391
+ let content = `# Codex Session${project ? `: ${project}` : ""} | ${date || "unknown"}
1339
1392
 
1340
- NODE=$(resolve_node)
1341
- if [ -z "$NODE" ]; then
1342
- echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) ERROR: node not found" >> "$LOG"
1343
- exit 1
1344
- fi
1393
+ ${body}`;
1394
+ if (content.length > MAX_CONTENT2)
1395
+ content = content.slice(0, MAX_CONTENT2) + `
1345
1396
 
1346
- echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) START sync" >> "$LOG"
1347
- "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" \\
1348
- --config-file "$CONARE_DIR/config.json" \\
1349
- --ingest-only \\
1350
- --quiet \\
1351
- 2>> "$LOG"
1352
- echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) DONE sync (exit $?)" >> "$LOG"
1353
- `;
1354
- function makePlist() {
1355
- const home = homedir7();
1356
- return `<?xml version="1.0" encoding="UTF-8"?>
1357
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
1358
- "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1359
- <plist version="1.0">
1360
- <dict>
1361
- <key>Label</key>
1362
- <string>${PLIST_LABEL}</string>
1363
- <key>ProgramArguments</key>
1364
- <array>
1365
- <string>/bin/bash</string>
1366
- <string>${home}/.conare/bin/run.sh</string>
1367
- </array>
1368
- <key>StartInterval</key>
1369
- <integer>600</integer>
1370
- <key>StandardOutPath</key>
1371
- <string>${home}/.conare/ingest.log</string>
1372
- <key>StandardErrorPath</key>
1373
- <string>${home}/.conare/ingest.log</string>
1374
- <key>RunAtLoad</key>
1375
- <true/>
1376
- </dict>
1377
- </plist>`;
1397
+ [truncated]`;
1398
+ const contentHash = createContentHash(content);
1399
+ const dedupKey = `codex:${sessionId}`;
1400
+ const fingerprint = `${dedupKey}:${contentHash}`;
1401
+ if (isIngested("codex", fingerprint)) {
1402
+ stats.deduped++;
1403
+ continue;
1404
+ }
1405
+ memories.push({
1406
+ content,
1407
+ containerTag: "codex-chats",
1408
+ metadata: {
1409
+ dedupKey,
1410
+ contentHash,
1411
+ source: "codex-session",
1412
+ sessionId,
1413
+ date: date || "unknown",
1414
+ ...project ? { project } : {}
1415
+ }
1416
+ });
1417
+ sessionIds.push(sessionId);
1418
+ } catch {}
1419
+ }
1420
+ }
1421
+ } catch {}
1378
1422
  }
1379
- var SYSTEMD_SERVICE_CONTENT = `[Unit]
1380
- Description=Conare Memory — background sync
1381
-
1382
- [Service]
1383
- Type=oneshot
1384
- ExecStart=%h/.conare/bin/run.sh
1385
- `;
1386
- var SYSTEMD_TIMER_CONTENT = `[Unit]
1387
- Description=Conare Memory — sync every 10 minutes
1388
-
1389
- [Timer]
1390
- OnBootSec=2min
1391
- OnUnitActiveSec=10min
1392
- Persistent=true
1393
1423
 
1394
- [Install]
1395
- WantedBy=timers.target
1396
- `;
1397
- function hasSystemd() {
1424
+ // src/ingest/cursor.ts
1425
+ import { readFileSync as readFileSync4, statSync } from "node:fs";
1426
+ import { join as join5 } from "node:path";
1427
+ import { createRequire as createRequire2 } from "node:module";
1428
+ var MAX_CONTENT3 = 48000;
1429
+ var MAX_DB_SIZE = 2 * 1024 * 1024 * 1024;
1430
+ var WARN_DB_SIZE = 500 * 1024 * 1024;
1431
+ var MIN_TURN_LEN2 = 50;
1432
+ function loadSqlJs(wasmDir) {
1398
1433
  try {
1399
- execSync("systemctl --user status 2>/dev/null", { stdio: "ignore" });
1400
- return true;
1434
+ if (wasmDir) {
1435
+ const require2 = createRequire2(join5(wasmDir, "sql.js", "package.json"));
1436
+ return require2("sql.js");
1437
+ } else {
1438
+ const require2 = createRequire2(import.meta.url);
1439
+ return require2("sql.js");
1440
+ }
1401
1441
  } catch {
1402
- return false;
1442
+ return null;
1403
1443
  }
1404
1444
  }
1405
- function uid() {
1445
+ function openDb(initSqlJs, dbPath, wasmDir) {
1446
+ const locateOpts = {};
1447
+ if (wasmDir) {
1448
+ locateOpts.locateFile = (file) => join5(wasmDir, "sql.js", "dist", file);
1449
+ }
1450
+ return initSqlJs(locateOpts).then((SQL) => {
1451
+ const buffer = readFileSync4(dbPath);
1452
+ return new SQL.Database(buffer);
1453
+ });
1454
+ }
1455
+ function extractTurns(db, composerId, bubbleHeaders) {
1456
+ const turns = [];
1457
+ let pendingUser = null;
1458
+ for (const header of bubbleHeaders) {
1459
+ let text = "";
1460
+ try {
1461
+ const result = db.exec(`SELECT value FROM cursorDiskKV WHERE key = 'bubbleId:${composerId}:${header.bubbleId}'`);
1462
+ if (result.length > 0) {
1463
+ const bubble = JSON.parse(result[0].values[0][0]);
1464
+ text = cleanText(bubble.text || "");
1465
+ }
1466
+ } catch {
1467
+ continue;
1468
+ }
1469
+ if (text.length < MIN_TURN_LEN2)
1470
+ continue;
1471
+ if (header.type === 1) {
1472
+ pendingUser = text;
1473
+ } else if (header.type === 2 && pendingUser) {
1474
+ turns.push({ user: pendingUser, assistant: text });
1475
+ pendingUser = null;
1476
+ }
1477
+ }
1478
+ return turns;
1479
+ }
1480
+ async function ingestCursor(dbPath, wasmDir) {
1481
+ const memories = [];
1482
+ const sessionIds = [];
1483
+ let filtered = 0;
1484
+ let deduped = 0;
1485
+ let fileSize;
1406
1486
  try {
1407
- return execSync("id -u", { encoding: "utf-8" }).trim();
1487
+ fileSize = statSync(dbPath).size;
1408
1488
  } catch {
1409
- return "501";
1489
+ console.log(" Skipping Cursor: database not accessible");
1490
+ return { memories, sessionIds, skipped: 0, filtered, deduped };
1410
1491
  }
1411
- }
1412
- function persistBinary(apiKey) {
1413
- mkdirSync4(BIN_DIR, { recursive: true });
1414
- const cliEntry = findCliBundle();
1415
- if (!cliEntry) {
1416
- throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
1492
+ if (fileSize > MAX_DB_SIZE) {
1493
+ console.log(` Skipping Cursor: database too large (${(fileSize / 1024 / 1024 / 1024).toFixed(1)}GB)`);
1494
+ return { memories, sessionIds, skipped: 0, filtered, deduped };
1417
1495
  }
1418
- const dest = join9(BIN_DIR, "conare-ingest.mjs");
1419
- const content = readFileSync8(cliEntry, "utf-8");
1420
- writeFileSync4(dest, content);
1421
- const sqlJsDir = findSqlJs();
1422
- if (sqlJsDir) {
1423
- const targetDir = join9(BIN_DIR, "node_modules", "sql.js");
1424
- mkdirSync4(targetDir, { recursive: true });
1496
+ if (fileSize > WARN_DB_SIZE) {
1497
+ console.log(` Warning: large database (${(fileSize / 1024 / 1024).toFixed(0)}MB), loading into memory...`);
1498
+ }
1499
+ const initSqlJs = loadSqlJs(wasmDir);
1500
+ if (!initSqlJs) {
1501
+ console.log(" Skipping Cursor: sql.js not available");
1502
+ return { memories, sessionIds, skipped: 0, filtered, deduped };
1503
+ }
1504
+ let db;
1505
+ try {
1506
+ db = await openDb(initSqlJs, dbPath, wasmDir);
1507
+ } catch (e) {
1508
+ console.log(` Skipping Cursor: cannot open database (${e.message})`);
1509
+ return { memories, sessionIds, skipped: 0, filtered, deduped };
1510
+ }
1511
+ try {
1512
+ let rows = [];
1425
1513
  try {
1426
- cpSync(sqlJsDir, targetDir, { recursive: true });
1514
+ const results = db.exec("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'");
1515
+ if (results.length > 0)
1516
+ rows = results[0].values;
1427
1517
  } catch {}
1428
- }
1429
- const runShPath = join9(BIN_DIR, "run.sh");
1430
- writeFileSync4(runShPath, RUN_SH);
1431
- chmodSync(runShPath, 493);
1432
- writeFileSync4(CONFIG_PATH2, JSON.stringify({ apiKey }, null, 2) + `
1518
+ for (const [key, value] of rows) {
1519
+ const composerId = key.replace("composerData:", "");
1520
+ let parsed;
1521
+ try {
1522
+ parsed = JSON.parse(value);
1523
+ if (!parsed || typeof parsed !== "object") {
1524
+ filtered++;
1525
+ continue;
1526
+ }
1527
+ } catch {
1528
+ filtered++;
1529
+ continue;
1530
+ }
1531
+ const bubbleHeaders = parsed.fullConversationHeadersOnly;
1532
+ if (!Array.isArray(bubbleHeaders) || bubbleHeaders.length === 0) {
1533
+ filtered++;
1534
+ continue;
1535
+ }
1536
+ const turns = extractTurns(db, composerId, bubbleHeaders);
1537
+ if (turns.length === 0) {
1538
+ filtered++;
1539
+ continue;
1540
+ }
1541
+ const sessionName = parsed.name || "Cursor Chat";
1542
+ const date = parsed.createdAt ? new Date(parsed.createdAt).toISOString().slice(0, 10) : "unknown";
1543
+ const header = `# ${sessionName} | ${date}`;
1544
+ const body = turns.map((t) => {
1545
+ const q = t.user.length > 300 ? t.user.slice(0, 300) + "..." : t.user;
1546
+ return `## Q: ${q}
1547
+
1548
+ ${t.assistant}`;
1549
+ }).join(`
1550
+
1551
+ ---
1552
+
1433
1553
  `);
1434
- }
1435
- function findCliBundle() {
1436
- const entry = process.argv[1];
1437
- if (entry && existsSync7(entry))
1438
- return entry;
1439
- const candidates = [
1440
- join9(dirname2(new URL(import.meta.url).pathname), "index.js"),
1441
- join9(dirname2(new URL(import.meta.url).pathname), "..", "dist", "index.js")
1442
- ];
1443
- for (const c of candidates) {
1444
- if (existsSync7(c))
1445
- return c;
1446
- }
1447
- return null;
1448
- }
1449
- function findSqlJs() {
1450
- const candidates = [
1451
- join9(process.cwd(), "node_modules", "sql.js"),
1452
- join9(dirname2(new URL(import.meta.url).pathname), "..", "node_modules", "sql.js"),
1453
- join9(dirname2(new URL(import.meta.url).pathname), "..", "..", "node_modules", "sql.js")
1454
- ];
1455
- for (const c of candidates) {
1456
- if (existsSync7(c))
1457
- return c;
1554
+ let content = `${header}
1555
+
1556
+ ${body}`;
1557
+ if (content.length > MAX_CONTENT3)
1558
+ content = content.slice(0, MAX_CONTENT3) + `
1559
+
1560
+ [truncated]`;
1561
+ const contentHash = createContentHash(content);
1562
+ const dedupKey = `cursor:${composerId}`;
1563
+ const fingerprint = `${dedupKey}:${contentHash}`;
1564
+ if (isIngested("cursor", fingerprint)) {
1565
+ deduped++;
1566
+ continue;
1567
+ }
1568
+ memories.push({
1569
+ content,
1570
+ containerTag: "cursor-chats",
1571
+ metadata: {
1572
+ dedupKey,
1573
+ contentHash,
1574
+ source: "cursor",
1575
+ sessionId: composerId,
1576
+ name: sessionName,
1577
+ date
1578
+ }
1579
+ });
1580
+ sessionIds.push(composerId);
1581
+ }
1582
+ } catch (e) {
1583
+ console.log(` Cursor ingestion error: ${e.message}`);
1584
+ } finally {
1585
+ db.close();
1458
1586
  }
1459
- return null;
1587
+ return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
1460
1588
  }
1461
- function setupMacOS() {
1462
- const plistDir = dirname2(PLIST_PATH);
1463
- mkdirSync4(plistDir, { recursive: true });
1464
- writeFileSync4(PLIST_PATH, makePlist());
1465
- const id = uid();
1466
- try {
1467
- execSync(`launchctl bootout gui/${id} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
1468
- } catch {}
1589
+
1590
+ // src/ingest/codebase.ts
1591
+ import { createHash as createHash2 } from "node:crypto";
1592
+ import { readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync2, existsSync as existsSync4 } from "node:fs";
1593
+ import { join as join6, relative, extname, resolve } from "node:path";
1594
+ var DEFAULT_IGNORE = new Set([
1595
+ "node_modules",
1596
+ ".git",
1597
+ ".next",
1598
+ ".nuxt",
1599
+ ".output",
1600
+ "dist",
1601
+ "build",
1602
+ ".cache",
1603
+ "__pycache__",
1604
+ ".venv",
1605
+ "venv",
1606
+ "vendor",
1607
+ "target",
1608
+ ".turbo",
1609
+ ".DS_Store",
1610
+ ".claude",
1611
+ ".conare"
1612
+ ]);
1613
+ var IGNORE_FILES = new Set([
1614
+ "package-lock.json",
1615
+ "bun.lockb",
1616
+ "yarn.lock",
1617
+ "pnpm-lock.yaml"
1618
+ ]);
1619
+ var CODE_EXTENSIONS = new Set([
1620
+ ".ts",
1621
+ ".tsx",
1622
+ ".js",
1623
+ ".jsx",
1624
+ ".mjs",
1625
+ ".cjs",
1626
+ ".vue",
1627
+ ".svelte",
1628
+ ".astro",
1629
+ ".py",
1630
+ ".rb",
1631
+ ".go",
1632
+ ".rs",
1633
+ ".java",
1634
+ ".kt",
1635
+ ".swift",
1636
+ ".c",
1637
+ ".cpp",
1638
+ ".h",
1639
+ ".css",
1640
+ ".scss",
1641
+ ".less",
1642
+ ".html",
1643
+ ".json",
1644
+ ".yaml",
1645
+ ".yml",
1646
+ ".toml",
1647
+ ".md",
1648
+ ".mdx",
1649
+ ".txt",
1650
+ ".sql",
1651
+ ".graphql",
1652
+ ".prisma",
1653
+ ".sh",
1654
+ ".bash",
1655
+ ".zsh",
1656
+ ".lua",
1657
+ ".zig",
1658
+ ".ex",
1659
+ ".exs"
1660
+ ]);
1661
+ var SPECIAL_FILES = new Set([
1662
+ "Dockerfile",
1663
+ "Makefile",
1664
+ "Procfile",
1665
+ "Justfile",
1666
+ ".gitignore",
1667
+ ".dockerignore",
1668
+ "CLAUDE.md",
1669
+ "AGENTS.md",
1670
+ "README.md",
1671
+ "ARCHITECTURE.md",
1672
+ "wrangler.toml",
1673
+ "wrangler.json"
1674
+ ]);
1675
+ var MAX_FILE_SIZE = 1e5;
1676
+ function parseGitignore(rootPath) {
1677
+ const patterns = new Set;
1678
+ const gitignorePath = join6(rootPath, ".gitignore");
1679
+ if (!existsSync4(gitignorePath))
1680
+ return patterns;
1469
1681
  try {
1470
- execSync(`launchctl bootstrap gui/${id} "${PLIST_PATH}"`, { stdio: "ignore" });
1471
- } catch {
1472
- try {
1473
- execSync(`launchctl load "${PLIST_PATH}"`, { stdio: "ignore" });
1474
- } catch {
1475
- throw new Error("Failed to load launchd agent. Try manually: launchctl load " + PLIST_PATH);
1682
+ const content = readFileSync5(gitignorePath, "utf-8");
1683
+ for (const line of content.split(`
1684
+ `)) {
1685
+ const trimmed = line.trim();
1686
+ if (!trimmed || trimmed.startsWith("#"))
1687
+ continue;
1688
+ patterns.add(trimmed.replace(/\/$/, ""));
1476
1689
  }
1477
- }
1690
+ } catch {}
1691
+ return patterns;
1478
1692
  }
1479
- function setupLinuxSystemd() {
1480
- mkdirSync4(SYSTEMD_DIR, { recursive: true });
1481
- writeFileSync4(SYSTEMD_SERVICE, SYSTEMD_SERVICE_CONTENT);
1482
- writeFileSync4(SYSTEMD_TIMER, SYSTEMD_TIMER_CONTENT);
1483
- execSync("systemctl --user daemon-reload", { stdio: "ignore" });
1484
- execSync("systemctl --user enable --now conare-sync.timer", { stdio: "ignore" });
1693
+ function shouldIgnore(name, ignorePatterns) {
1694
+ if (DEFAULT_IGNORE.has(name))
1695
+ return true;
1696
+ if (IGNORE_FILES.has(name))
1697
+ return true;
1698
+ if (name.startsWith(".env"))
1699
+ return true;
1700
+ if (ignorePatterns.has(name))
1701
+ return true;
1702
+ return false;
1485
1703
  }
1486
- function setupLinuxCron() {
1487
- const cronCmd = `${homedir7()}/.conare/bin/run.sh`;
1488
- const cronLine = `*/10 * * * * ${cronCmd}`;
1489
- try {
1490
- const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
1491
- const filtered = existing.split(`
1492
- `).filter((l) => !l.includes("conare")).join(`
1493
- `);
1494
- const newCrontab = (filtered.trim() ? filtered.trim() + `
1495
- ` : "") + cronLine + `
1496
- `;
1497
- execSync("crontab -", { input: newCrontab, stdio: ["pipe", "ignore", "ignore"] });
1498
- } catch {
1499
- execSync("crontab -", { input: cronLine + `
1500
- `, stdio: ["pipe", "ignore", "ignore"] });
1501
- }
1704
+ function langFromExt(ext) {
1705
+ const map = {
1706
+ ".ts": "typescript",
1707
+ ".tsx": "tsx",
1708
+ ".js": "javascript",
1709
+ ".jsx": "jsx",
1710
+ ".mjs": "javascript",
1711
+ ".cjs": "javascript",
1712
+ ".vue": "vue",
1713
+ ".svelte": "svelte",
1714
+ ".astro": "astro",
1715
+ ".py": "python",
1716
+ ".rb": "ruby",
1717
+ ".go": "go",
1718
+ ".rs": "rust",
1719
+ ".java": "java",
1720
+ ".kt": "kotlin",
1721
+ ".swift": "swift",
1722
+ ".c": "c",
1723
+ ".cpp": "cpp",
1724
+ ".h": "c",
1725
+ ".css": "css",
1726
+ ".scss": "scss",
1727
+ ".less": "less",
1728
+ ".html": "html",
1729
+ ".json": "json",
1730
+ ".yaml": "yaml",
1731
+ ".yml": "yaml",
1732
+ ".toml": "toml",
1733
+ ".md": "markdown",
1734
+ ".mdx": "mdx",
1735
+ ".sql": "sql",
1736
+ ".graphql": "graphql",
1737
+ ".prisma": "prisma",
1738
+ ".sh": "bash",
1739
+ ".bash": "bash",
1740
+ ".zsh": "zsh",
1741
+ ".lua": "lua",
1742
+ ".zig": "zig",
1743
+ ".ex": "elixir",
1744
+ ".exs": "elixir"
1745
+ };
1746
+ return map[ext] || ext.slice(1);
1502
1747
  }
1503
- function installSync(apiKey) {
1504
- const messages = [];
1505
- const os = platform2();
1506
- persistBinary(apiKey);
1507
- messages.push("Persisted CLI to ~/.conare/bin/");
1508
- messages.push("Saved config to ~/.conare/config.json");
1509
- if (os === "darwin") {
1510
- setupMacOS();
1511
- messages.push("Installed launchd agent (every 10 min)");
1512
- messages.push(`Plist: ${PLIST_PATH}`);
1513
- } else if (os === "linux") {
1514
- if (hasSystemd()) {
1515
- setupLinuxSystemd();
1516
- messages.push("Installed systemd timer (every 10 min)");
1517
- } else {
1518
- setupLinuxCron();
1519
- messages.push("Installed cron job (every 10 min)");
1520
- }
1521
- } else {
1522
- messages.push(`Unsupported platform: ${os}. Run manually: ~/.conare/bin/run.sh`);
1523
- }
1524
- return messages;
1748
+ function formatFile(relPath, content, ext) {
1749
+ const lang = langFromExt(ext);
1750
+ return `# File: ${relPath}
1751
+
1752
+ \`\`\`${lang}
1753
+ ${content}
1754
+ \`\`\``;
1525
1755
  }
1526
- function uninstallSync() {
1527
- const messages = [];
1528
- const os = platform2();
1529
- if (os === "darwin") {
1756
+ function indexCodebase(rootPath) {
1757
+ const absRoot = resolve(rootPath);
1758
+ const repoHash = createHash2("sha256").update(absRoot).digest("hex").slice(0, 12);
1759
+ const gitignorePatterns = parseGitignore(absRoot);
1760
+ const memories = [];
1761
+ let fileCount = 0;
1762
+ let skipped = 0;
1763
+ function walk(dir) {
1764
+ let entries;
1530
1765
  try {
1531
- execSync(`launchctl bootout gui/${uid()} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
1532
- } catch {}
1533
- if (existsSync7(PLIST_PATH)) {
1534
- unlinkSync(PLIST_PATH);
1535
- messages.push("Removed launchd agent");
1766
+ entries = readdirSync4(dir, { withFileTypes: true });
1767
+ } catch {
1768
+ return;
1536
1769
  }
1537
- } else if (os === "linux") {
1538
- if (hasSystemd()) {
1539
- try {
1540
- execSync("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
1541
- } catch {}
1542
- if (existsSync7(SYSTEMD_SERVICE))
1543
- unlinkSync(SYSTEMD_SERVICE);
1544
- if (existsSync7(SYSTEMD_TIMER))
1545
- unlinkSync(SYSTEMD_TIMER);
1770
+ for (const entry of entries) {
1771
+ if (shouldIgnore(entry.name, gitignorePatterns))
1772
+ continue;
1773
+ const fullPath = join6(dir, entry.name);
1774
+ if (entry.isDirectory()) {
1775
+ walk(fullPath);
1776
+ continue;
1777
+ }
1778
+ if (!entry.isFile())
1779
+ continue;
1780
+ const ext = extname(entry.name).toLowerCase();
1781
+ if (!CODE_EXTENSIONS.has(ext) && !SPECIAL_FILES.has(entry.name))
1782
+ continue;
1783
+ let stat;
1546
1784
  try {
1547
- execSync("systemctl --user daemon-reload", { stdio: "ignore" });
1548
- } catch {}
1549
- messages.push("Removed systemd timer");
1550
- } else {
1785
+ stat = statSync2(fullPath);
1786
+ } catch {
1787
+ continue;
1788
+ }
1789
+ if (stat.size > MAX_FILE_SIZE || stat.size === 0) {
1790
+ skipped++;
1791
+ continue;
1792
+ }
1793
+ let raw;
1551
1794
  try {
1552
- const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
1553
- const filtered = existing.split(`
1554
- `).filter((l) => !l.includes("conare")).join(`
1555
- `);
1556
- execSync("crontab -", { input: filtered.trim() + `
1557
- `, stdio: ["pipe", "ignore", "ignore"] });
1558
- messages.push("Removed cron job");
1559
- } catch {}
1795
+ raw = readFileSync5(fullPath, "utf-8");
1796
+ } catch {
1797
+ skipped++;
1798
+ continue;
1799
+ }
1800
+ if (raw.includes("\x00")) {
1801
+ skipped++;
1802
+ continue;
1803
+ }
1804
+ const relPath = relative(absRoot, fullPath);
1805
+ const contentHash = createContentHash(raw);
1806
+ const dedupKey = `codebase:${repoHash}:${relPath}`;
1807
+ const fingerprint = `${dedupKey}:${contentHash}`;
1808
+ if (isIngested("codebase", fingerprint)) {
1809
+ skipped++;
1810
+ continue;
1811
+ }
1812
+ const content = formatFile(relPath, raw, ext);
1813
+ memories.push({
1814
+ content,
1815
+ containerTag: "codebase",
1816
+ metadata: {
1817
+ dedupKey,
1818
+ contentHash,
1819
+ source: "codebase-index",
1820
+ repoHash,
1821
+ filePath: relPath,
1822
+ fileHash: `${relPath}:${contentHash}`,
1823
+ language: langFromExt(ext)
1824
+ }
1825
+ });
1826
+ fileCount++;
1560
1827
  }
1561
1828
  }
1562
- const filesToRemove = [
1563
- join9(CONARE_DIR, "config.json"),
1564
- join9(CONARE_DIR, "sync.lock")
1565
- ];
1566
- for (const f of filesToRemove) {
1567
- if (existsSync7(f))
1568
- unlinkSync(f);
1569
- }
1570
- if (existsSync7(BIN_DIR)) {
1571
- rmSync(BIN_DIR, { recursive: true, force: true });
1572
- messages.push("Removed ~/.conare/bin/");
1573
- }
1574
- if (messages.length === 0) {
1575
- messages.push("Nothing to uninstall (no sync timer found)");
1576
- }
1577
- return messages;
1829
+ walk(absRoot);
1830
+ return { memories, fileCount, skipped };
1578
1831
  }
1579
1832
 
1580
- // node_modules/@clack/prompts/dist/index.mjs
1581
- import { stripVTControlCharacters as S2 } from "node:util";
1582
-
1583
- // node_modules/@clack/core/dist/index.mjs
1584
- var import_sisteransi = __toESM(require_src(), 1);
1585
- import { stdin as j, stdout as M } from "node:process";
1586
- var import_picocolors = __toESM(require_picocolors(), 1);
1587
- import O from "node:readline";
1588
- import { Writable as X } from "node:stream";
1589
- function DD({ onlyFirst: e = false } = {}) {
1590
- const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
1591
- return new RegExp(t, e ? undefined : "g");
1592
- }
1593
- var uD = DD();
1594
- function P(e) {
1595
- if (typeof e != "string")
1596
- throw new TypeError(`Expected a \`string\`, got \`${typeof e}\``);
1597
- return e.replace(uD, "");
1598
- }
1599
- function L(e) {
1600
- return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
1601
- }
1602
- var W = { exports: {} };
1603
- (function(e) {
1604
- var u = {};
1605
- e.exports = u, u.eastAsianWidth = function(F) {
1606
- var s = F.charCodeAt(0), i = F.length == 2 ? F.charCodeAt(1) : 0, D = s;
1607
- return 55296 <= s && s <= 56319 && 56320 <= i && i <= 57343 && (s &= 1023, i &= 1023, D = s << 10 | i, D += 65536), D == 12288 || 65281 <= D && D <= 65376 || 65504 <= D && D <= 65510 ? "F" : D == 8361 || 65377 <= D && D <= 65470 || 65474 <= D && D <= 65479 || 65482 <= D && D <= 65487 || 65490 <= D && D <= 65495 || 65498 <= D && D <= 65500 || 65512 <= D && D <= 65518 ? "H" : 4352 <= D && D <= 4447 || 4515 <= D && D <= 4519 || 4602 <= D && D <= 4607 || 9001 <= D && D <= 9002 || 11904 <= D && D <= 11929 || 11931 <= D && D <= 12019 || 12032 <= D && D <= 12245 || 12272 <= D && D <= 12283 || 12289 <= D && D <= 12350 || 12353 <= D && D <= 12438 || 12441 <= D && D <= 12543 || 12549 <= D && D <= 12589 || 12593 <= D && D <= 12686 || 12688 <= D && D <= 12730 || 12736 <= D && D <= 12771 || 12784 <= D && D <= 12830 || 12832 <= D && D <= 12871 || 12880 <= D && D <= 13054 || 13056 <= D && D <= 19903 || 19968 <= D && D <= 42124 || 42128 <= D && D <= 42182 || 43360 <= D && D <= 43388 || 44032 <= D && D <= 55203 || 55216 <= D && D <= 55238 || 55243 <= D && D <= 55291 || 63744 <= D && D <= 64255 || 65040 <= D && D <= 65049 || 65072 <= D && D <= 65106 || 65108 <= D && D <= 65126 || 65128 <= D && D <= 65131 || 110592 <= D && D <= 110593 || 127488 <= D && D <= 127490 || 127504 <= D && D <= 127546 || 127552 <= D && D <= 127560 || 127568 <= D && D <= 127569 || 131072 <= D && D <= 194367 || 177984 <= D && D <= 196605 || 196608 <= D && D <= 262141 ? "W" : 32 <= D && D <= 126 || 162 <= D && D <= 163 || 165 <= D && D <= 166 || D == 172 || D == 175 || 10214 <= D && D <= 10221 || 10629 <= D && D <= 10630 ? "Na" : D == 161 || D == 164 || 167 <= D && D <= 168 || D == 170 || 173 <= D && D <= 174 || 176 <= D && D <= 180 || 182 <= D && D <= 186 || 188 <= D && D <= 191 || D == 198 || D == 208 || 215 <= D && D <= 216 || 222 <= D && D <= 225 || D == 230 || 232 <= D && D <= 234 || 236 <= D && D <= 237 || D == 240 || 242 <= D && D <= 243 || 247 <= D && D <= 250 || D == 252 || D == 254 || D == 257 || D == 273 || D == 275 || D == 283 || 294 <= D && D <= 295 || D == 299 || 305 <= D && D <= 307 || D == 312 || 319 <= D && D <= 322 || D == 324 || 328 <= D && D <= 331 || D == 333 || 338 <= D && D <= 339 || 358 <= D && D <= 359 || D == 363 || D == 462 || D == 464 || D == 466 || D == 468 || D == 470 || D == 472 || D == 474 || D == 476 || D == 593 || D == 609 || D == 708 || D == 711 || 713 <= D && D <= 715 || D == 717 || D == 720 || 728 <= D && D <= 731 || D == 733 || D == 735 || 768 <= D && D <= 879 || 913 <= D && D <= 929 || 931 <= D && D <= 937 || 945 <= D && D <= 961 || 963 <= D && D <= 969 || D == 1025 || 1040 <= D && D <= 1103 || D == 1105 || D == 8208 || 8211 <= D && D <= 8214 || 8216 <= D && D <= 8217 || 8220 <= D && D <= 8221 || 8224 <= D && D <= 8226 || 8228 <= D && D <= 8231 || D == 8240 || 8242 <= D && D <= 8243 || D == 8245 || D == 8251 || D == 8254 || D == 8308 || D == 8319 || 8321 <= D && D <= 8324 || D == 8364 || D == 8451 || D == 8453 || D == 8457 || D == 8467 || D == 8470 || 8481 <= D && D <= 8482 || D == 8486 || D == 8491 || 8531 <= D && D <= 8532 || 8539 <= D && D <= 8542 || 8544 <= D && D <= 8555 || 8560 <= D && D <= 8569 || D == 8585 || 8592 <= D && D <= 8601 || 8632 <= D && D <= 8633 || D == 8658 || D == 8660 || D == 8679 || D == 8704 || 8706 <= D && D <= 8707 || 8711 <= D && D <= 8712 || D == 8715 || D == 8719 || D == 8721 || D == 8725 || D == 8730 || 8733 <= D && D <= 8736 || D == 8739 || D == 8741 || 8743 <= D && D <= 8748 || D == 8750 || 8756 <= D && D <= 8759 || 8764 <= D && D <= 8765 || D == 8776 || D == 8780 || D == 8786 || 8800 <= D && D <= 8801 || 8804 <= D && D <= 8807 || 8810 <= D && D <= 8811 || 8814 <= D && D <= 8815 || 8834 <= D && D <= 8835 || 8838 <= D && D <= 8839 || D == 8853 || D == 8857 || D == 8869 || D == 8895 || D == 8978 || 9312 <= D && D <= 9449 || 9451 <= D && D <= 9547 || 9552 <= D && D <= 9587 || 9600 <= D && D <= 9615 || 9618 <= D && D <= 9621 || 9632 <= D && D <= 9633 || 9635 <= D && D <= 9641 || 9650 <= D && D <= 9651 || 9654 <= D && D <= 9655 || 9660 <= D && D <= 9661 || 9664 <= D && D <= 9665 || 9670 <= D && D <= 9672 || D == 9675 || 9678 <= D && D <= 9681 || 9698 <= D && D <= 9701 || D == 9711 || 9733 <= D && D <= 9734 || D == 9737 || 9742 <= D && D <= 9743 || 9748 <= D && D <= 9749 || D == 9756 || D == 9758 || D == 9792 || D == 9794 || 9824 <= D && D <= 9825 || 9827 <= D && D <= 9829 || 9831 <= D && D <= 9834 || 9836 <= D && D <= 9837 || D == 9839 || 9886 <= D && D <= 9887 || 9918 <= D && D <= 9919 || 9924 <= D && D <= 9933 || 9935 <= D && D <= 9953 || D == 9955 || 9960 <= D && D <= 9983 || D == 10045 || D == 10071 || 10102 <= D && D <= 10111 || 11093 <= D && D <= 11097 || 12872 <= D && D <= 12879 || 57344 <= D && D <= 63743 || 65024 <= D && D <= 65039 || D == 65533 || 127232 <= D && D <= 127242 || 127248 <= D && D <= 127277 || 127280 <= D && D <= 127337 || 127344 <= D && D <= 127386 || 917760 <= D && D <= 917999 || 983040 <= D && D <= 1048573 || 1048576 <= D && D <= 1114109 ? "A" : "N";
1608
- }, u.characterLength = function(F) {
1609
- var s = this.eastAsianWidth(F);
1610
- return s == "F" || s == "W" || s == "A" ? 2 : 1;
1611
- };
1612
- function t(F) {
1613
- return F.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
1614
- }
1615
- u.length = function(F) {
1616
- for (var s = t(F), i = 0, D = 0;D < s.length; D++)
1617
- i = i + this.characterLength(s[D]);
1618
- return i;
1619
- }, u.slice = function(F, s, i) {
1620
- textLen = u.length(F), s = s || 0, i = i || 1, s < 0 && (s = textLen + s), i < 0 && (i = textLen + i);
1621
- for (var D = "", C = 0, n = t(F), E = 0;E < n.length; E++) {
1622
- var a = n[E], o = u.length(a);
1623
- if (C >= s - (o == 2 ? 1 : 0))
1624
- if (C + o <= i)
1625
- D += a;
1626
- else
1627
- break;
1628
- C += o;
1629
- }
1630
- return D;
1631
- };
1632
- })(W);
1633
- var tD = W.exports;
1634
- var eD = L(tD);
1635
- var FD = function() {
1636
- return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67)\uDB40\uDC7F|(?:\uD83E\uDDD1\uD83C\uDFFF\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFC-\uDFFF])|\uD83D\uDC68(?:\uD83C\uDFFB(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC)?|(?:\uD83D\uDC69(?:\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC69(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83E\uDDD1(?:\u200D(?:\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F\u200D\u26A7|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\uD83C\uDFF4\u200D\u2620|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299]|\uD83C[\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3])\uFE0F|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDE35\u200D\uD83D\uDCAB|\uD83D\uDE2E\u200D\uD83D\uDCA8|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83E\uDDD1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC69(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC08\u200D\u2B1B|\u2764\uFE0F\u200D(?:\uD83D\uDD25|\uD83E\uDE79)|\uD83D\uDC41\uFE0F|\uD83C\uDFF3\uFE0F|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|[#\*0-9]\uFE0F\u20E3|\u2764\uFE0F|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF4|(?:[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270C\u270D]|\uD83D[\uDD74\uDD90])(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC08\uDC15\uDC3B\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE2E\uDE35\uDE36\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5]|\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD]|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0D\uDD0E\uDD10-\uDD17\uDD1D\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78\uDD7A-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCB\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6]|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDD77\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g;
1637
- };
1638
- var sD = L(FD);
1639
- function p(e, u = {}) {
1640
- if (typeof e != "string" || e.length === 0 || (u = { ambiguousIsNarrow: true, ...u }, e = P(e), e.length === 0))
1641
- return 0;
1642
- e = e.replace(sD(), " ");
1643
- const t = u.ambiguousIsNarrow ? 1 : 2;
1644
- let F = 0;
1645
- for (const s of e) {
1646
- const i = s.codePointAt(0);
1647
- if (i <= 31 || i >= 127 && i <= 159 || i >= 768 && i <= 879)
1648
- continue;
1649
- switch (eD.eastAsianWidth(s)) {
1650
- case "F":
1651
- case "W":
1652
- F += 2;
1653
- break;
1654
- case "A":
1655
- F += t;
1656
- break;
1657
- default:
1658
- F += 1;
1833
+ // src/api.ts
1834
+ var API_URL = "https://mcp.conare.ai";
1835
+ function createUploadBatches(memories, maxItems = 50, maxChars = 1500000) {
1836
+ const batches = [];
1837
+ let current = [];
1838
+ let currentChars = 0;
1839
+ let startIndex = 0;
1840
+ for (let i = 0;i < memories.length; i++) {
1841
+ const item = memories[i];
1842
+ const itemChars = item.content.length;
1843
+ const exceedsBatch = current.length > 0 && (current.length >= maxItems || currentChars + itemChars > maxChars);
1844
+ if (exceedsBatch) {
1845
+ batches.push({ startIndex, items: current });
1846
+ current = [];
1847
+ currentChars = 0;
1848
+ startIndex = i;
1659
1849
  }
1850
+ current.push(item);
1851
+ currentChars += itemChars;
1660
1852
  }
1661
- return F;
1853
+ if (current.length > 0) {
1854
+ batches.push({ startIndex, items: current });
1855
+ }
1856
+ return batches;
1662
1857
  }
1663
- var w = 10;
1664
- var N = (e = 0) => (u) => `\x1B[${u + e}m`;
1665
- var I = (e = 0) => (u) => `\x1B[${38 + e};5;${u}m`;
1666
- var R = (e = 0) => (u, t, F) => `\x1B[${38 + e};2;${u};${t};${F}m`;
1667
- var r = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } };
1668
- Object.keys(r.modifier);
1669
- var iD = Object.keys(r.color);
1670
- var CD = Object.keys(r.bgColor);
1671
- [...iD, ...CD];
1672
- function rD() {
1673
- const e = new Map;
1674
- for (const [u, t] of Object.entries(r)) {
1675
- for (const [F, s] of Object.entries(t))
1676
- r[F] = { open: `\x1B[${s[0]}m`, close: `\x1B[${s[1]}m` }, t[F] = r[F], e.set(s[0], s[1]);
1677
- Object.defineProperty(r, u, { value: t, enumerable: false });
1858
+ async function validateKey(apiKey) {
1859
+ try {
1860
+ const res = await fetch(`${API_URL}/api/auth/me`, {
1861
+ headers: { Authorization: `Bearer ${apiKey}` }
1862
+ });
1863
+ if (!res.ok)
1864
+ return { valid: false };
1865
+ const data = await res.json();
1866
+ return { valid: true, email: data.user.email, name: data.user.name };
1867
+ } catch {
1868
+ return { valid: false };
1678
1869
  }
1679
- return Object.defineProperty(r, "codes", { value: e, enumerable: false }), r.color.close = "\x1B[39m", r.bgColor.close = "\x1B[49m", r.color.ansi = N(), r.color.ansi256 = I(), r.color.ansi16m = R(), r.bgColor.ansi = N(w), r.bgColor.ansi256 = I(w), r.bgColor.ansi16m = R(w), Object.defineProperties(r, { rgbToAnsi256: { value: (u, t, F) => u === t && t === F ? u < 8 ? 16 : u > 248 ? 231 : Math.round((u - 8) / 247 * 24) + 232 : 16 + 36 * Math.round(u / 255 * 5) + 6 * Math.round(t / 255 * 5) + Math.round(F / 255 * 5), enumerable: false }, hexToRgb: { value: (u) => {
1680
- const t = /[a-f\d]{6}|[a-f\d]{3}/i.exec(u.toString(16));
1681
- if (!t)
1682
- return [0, 0, 0];
1683
- let [F] = t;
1684
- F.length === 3 && (F = [...F].map((i) => i + i).join(""));
1685
- const s = Number.parseInt(F, 16);
1686
- return [s >> 16 & 255, s >> 8 & 255, s & 255];
1687
- }, enumerable: false }, hexToAnsi256: { value: (u) => r.rgbToAnsi256(...r.hexToRgb(u)), enumerable: false }, ansi256ToAnsi: { value: (u) => {
1688
- if (u < 8)
1689
- return 30 + u;
1690
- if (u < 16)
1691
- return 90 + (u - 8);
1692
- let t, F, s;
1693
- if (u >= 232)
1694
- t = ((u - 232) * 10 + 8) / 255, F = t, s = t;
1695
- else {
1696
- u -= 16;
1697
- const C = u % 36;
1698
- t = Math.floor(u / 36) / 5, F = Math.floor(C / 6) / 5, s = C % 6 / 5;
1699
- }
1700
- const i = Math.max(t, F, s) * 2;
1701
- if (i === 0)
1702
- return 30;
1703
- let D = 30 + (Math.round(s) << 2 | Math.round(F) << 1 | Math.round(t));
1704
- return i === 2 && (D += 60), D;
1705
- }, enumerable: false }, rgbToAnsi: { value: (u, t, F) => r.ansi256ToAnsi(r.rgbToAnsi256(u, t, F)), enumerable: false }, hexToAnsi: { value: (u) => r.ansi256ToAnsi(r.hexToAnsi256(u)), enumerable: false } }), r;
1706
1870
  }
1707
- var ED = rD();
1708
- var d = new Set(["\x1B", "›"]);
1709
- var oD = 39;
1710
- var y = "\x07";
1711
- var V = "[";
1712
- var nD = "]";
1713
- var G = "m";
1714
- var _ = `${nD}8;;`;
1715
- var z = (e) => `${d.values().next().value}${V}${e}${G}`;
1716
- var K = (e) => `${d.values().next().value}${_}${e}${y}`;
1717
- var aD = (e) => e.split(" ").map((u) => p(u));
1718
- var k = (e, u, t) => {
1719
- const F = [...u];
1720
- let s = false, i = false, D = p(P(e[e.length - 1]));
1721
- for (const [C, n] of F.entries()) {
1722
- const E = p(n);
1723
- if (D + E <= t ? e[e.length - 1] += n : (e.push(n), D = 0), d.has(n) && (s = true, i = F.slice(C + 1).join("").startsWith(_)), s) {
1724
- i ? n === y && (s = false, i = false) : n === G && (s = false);
1725
- continue;
1726
- }
1727
- D += E, D === t && C < F.length - 1 && (e.push(""), D = 0);
1871
+ async function getRemoteMemoryCount(apiKey) {
1872
+ try {
1873
+ const res = await fetch(`${API_URL}/api/containers`, {
1874
+ headers: { Authorization: `Bearer ${apiKey}` }
1875
+ });
1876
+ if (!res.ok)
1877
+ return null;
1878
+ const data = await res.json();
1879
+ if (!Array.isArray(data.containers))
1880
+ return 0;
1881
+ return data.containers.reduce((sum, container) => sum + (container.count || 0), 0);
1882
+ } catch {
1883
+ return null;
1728
1884
  }
1729
- !D && e[e.length - 1].length > 0 && e.length > 1 && (e[e.length - 2] += e.pop());
1730
- };
1731
- var hD = (e) => {
1732
- const u = e.split(" ");
1733
- let t = u.length;
1734
- for (;t > 0 && !(p(u[t - 1]) > 0); )
1735
- t--;
1736
- return t === u.length ? e : u.slice(0, t).join(" ") + u.slice(t).join("");
1737
- };
1738
- var lD = (e, u, t = {}) => {
1739
- if (t.trim !== false && e.trim() === "")
1740
- return "";
1741
- let F = "", s, i;
1742
- const D = aD(e);
1743
- let C = [""];
1744
- for (const [E, a] of e.split(" ").entries()) {
1745
- t.trim !== false && (C[C.length - 1] = C[C.length - 1].trimStart());
1746
- let o = p(C[C.length - 1]);
1747
- if (E !== 0 && (o >= u && (t.wordWrap === false || t.trim === false) && (C.push(""), o = 0), (o > 0 || t.trim === false) && (C[C.length - 1] += " ", o++)), t.hard && D[E] > u) {
1748
- const c = u - o, f = 1 + Math.floor((D[E] - c - 1) / u);
1749
- Math.floor((D[E] - 1) / u) < f && C.push(""), k(C, a, u);
1750
- continue;
1751
- }
1752
- if (o + D[E] > u && o > 0 && D[E] > 0) {
1753
- if (t.wordWrap === false && o < u) {
1754
- k(C, a, u);
1885
+ }
1886
+ async function uploadItems(apiKey, items, asyncEmbed = false) {
1887
+ let retries = 4;
1888
+ while (retries > 0) {
1889
+ try {
1890
+ const res = await fetch(`${API_URL}/api/memories/bulk`, {
1891
+ method: "POST",
1892
+ headers: {
1893
+ "Content-Type": "application/json",
1894
+ Authorization: `Bearer ${apiKey}`
1895
+ },
1896
+ body: JSON.stringify(asyncEmbed ? { items, async: true } : items)
1897
+ });
1898
+ if (res.status === 429) {
1899
+ retries--;
1900
+ await new Promise((r) => setTimeout(r, 5000));
1755
1901
  continue;
1756
1902
  }
1757
- C.push("");
1758
- }
1759
- if (o + D[E] > u && t.wordWrap === false) {
1760
- k(C, a, u);
1761
- continue;
1903
+ if (!res.ok) {
1904
+ const body = await res.text().catch(() => "");
1905
+ throw new Error(`HTTP ${res.status}: ${body.slice(0, 120)}`);
1906
+ }
1907
+ const data = await res.json();
1908
+ if (!Array.isArray(data.results) || data.results.length !== items.length) {
1909
+ throw new Error("Unexpected bulk upload response");
1910
+ }
1911
+ return data.results;
1912
+ } catch (error) {
1913
+ retries--;
1914
+ if (retries === 0) {
1915
+ return items.map(() => ({
1916
+ success: false,
1917
+ error: error instanceof Error ? error.message : String(error)
1918
+ }));
1919
+ }
1920
+ await new Promise((r) => setTimeout(r, 2000));
1762
1921
  }
1763
- C[C.length - 1] += a;
1764
1922
  }
1765
- t.trim !== false && (C = C.map((E) => hD(E)));
1766
- const n = [...C.join(`
1767
- `)];
1768
- for (const [E, a] of n.entries()) {
1769
- if (F += a, d.has(a)) {
1770
- const { groups: c } = new RegExp(`(?:\\${V}(?<code>\\d+)m|\\${_}(?<uri>.*)${y})`).exec(n.slice(E).join("")) || { groups: {} };
1771
- if (c.code !== undefined) {
1772
- const f = Number.parseFloat(c.code);
1773
- s = f === oD ? undefined : f;
1774
- } else
1775
- c.uri !== undefined && (i = c.uri.length === 0 ? undefined : c.uri);
1923
+ return items.map(() => ({ success: false, error: "Upload failed" }));
1924
+ }
1925
+ async function uploadBulk(apiKey, memories, onProgress) {
1926
+ let success = 0;
1927
+ let failed = 0;
1928
+ const total = memories.length;
1929
+ const results = [];
1930
+ const batches = createUploadBatches(memories);
1931
+ for (const batch of batches) {
1932
+ let batchResults = await uploadItems(apiKey, batch.items, true);
1933
+ if (batch.items.length > 1 && batchResults.some((result) => !result.success)) {
1934
+ const retriedResults = [];
1935
+ for (let i = 0;i < batch.items.length; i++) {
1936
+ const result = batchResults[i];
1937
+ if (result.success) {
1938
+ retriedResults.push(result);
1939
+ continue;
1940
+ }
1941
+ const [singleResult] = await uploadItems(apiKey, [batch.items[i]], true);
1942
+ retriedResults.push(singleResult);
1943
+ }
1944
+ batchResults = retriedResults;
1776
1945
  }
1777
- const o = ED.codes.get(Number(s));
1778
- n[E + 1] === `
1779
- ` ? (i && (F += K("")), s && o && (F += z(o))) : a === `
1780
- ` && (s && o && (F += z(s)), i && (F += K(i)));
1946
+ for (let i = 0;i < batchResults.length; i++) {
1947
+ const result = batchResults[i];
1948
+ results.push({
1949
+ index: batch.startIndex + i,
1950
+ success: result.success,
1951
+ deduped: result.deduped,
1952
+ error: result.error
1953
+ });
1954
+ if (result.success)
1955
+ success++;
1956
+ else
1957
+ failed++;
1958
+ }
1959
+ onProgress?.(success + failed, total, failed);
1781
1960
  }
1782
- return F;
1783
- };
1784
- function Y(e, u, t) {
1785
- return String(e).normalize().replace(/\r\n/g, `
1786
- `).split(`
1787
- `).map((F) => lD(F, u, t)).join(`
1788
- `);
1789
- }
1790
- var xD = ["up", "down", "left", "right", "space", "enter", "cancel"];
1791
- var B = { actions: new Set(xD), aliases: new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["\x03", "cancel"], ["escape", "cancel"]]) };
1792
- function $(e, u) {
1793
- if (typeof e == "string")
1794
- return B.aliases.get(e) === u;
1795
- for (const t of e)
1796
- if (t !== undefined && $(t, u))
1797
- return true;
1798
- return false;
1961
+ return { success, failed, results };
1799
1962
  }
1800
- function BD(e, u) {
1801
- if (e === u)
1802
- return;
1803
- const t = e.split(`
1804
- `), F = u.split(`
1805
- `), s = [];
1806
- for (let i = 0;i < Math.max(t.length, F.length); i++)
1807
- t[i] !== F[i] && s.push(i);
1808
- return s;
1963
+
1964
+ // src/configure.ts
1965
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
1966
+ import { dirname, join as join7 } from "node:path";
1967
+ import { homedir as homedir5 } from "node:os";
1968
+ import { spawnSync as spawnSync2 } from "node:child_process";
1969
+ var CONARE_URL = "https://mcp.conare.ai";
1970
+ var SERVER_NAME = "conare-memory";
1971
+ var MCP_TARGETS = [
1972
+ { id: "claude", label: "Claude Code" },
1973
+ { id: "cursor", label: "Cursor" },
1974
+ { id: "codex", label: "Codex" },
1975
+ { id: "openclaw", label: "OpenClaw" }
1976
+ ];
1977
+ function readJsonFile(path) {
1978
+ try {
1979
+ return JSON.parse(readFileSync6(path, "utf-8"));
1980
+ } catch {
1981
+ return {};
1982
+ }
1809
1983
  }
1810
- var AD = globalThis.process.platform.startsWith("win");
1811
- var S = Symbol("clack:cancel");
1812
- function pD(e) {
1813
- return e === S;
1984
+ function writeJsonFile(path, data) {
1985
+ mkdirSync2(dirname(path), { recursive: true });
1986
+ writeFileSync2(path, JSON.stringify(data, null, 2) + `
1987
+ `);
1814
1988
  }
1815
- function m(e, u) {
1816
- const t = e;
1817
- t.isTTY && t.setRawMode(u);
1989
+ function getServerConfig(apiKey) {
1990
+ return {
1991
+ type: "http",
1992
+ url: `${CONARE_URL}/mcp`,
1993
+ headers: {
1994
+ Authorization: `Bearer ${apiKey}`
1995
+ }
1996
+ };
1818
1997
  }
1819
- var gD = Object.defineProperty;
1820
- var vD = (e, u, t) => (u in e) ? gD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t;
1821
- var h = (e, u, t) => (vD(e, typeof u != "symbol" ? u + "" : u, t), t);
1822
-
1823
- class x {
1824
- constructor(u, t = true) {
1825
- h(this, "input"), h(this, "output"), h(this, "_abortSignal"), h(this, "rl"), h(this, "opts"), h(this, "_render"), h(this, "_track", false), h(this, "_prevFrame", ""), h(this, "_subscribers", new Map), h(this, "_cursor", 0), h(this, "state", "initial"), h(this, "error", ""), h(this, "value");
1826
- const { input: F = j, output: s = M, render: i, signal: D, ...C } = u;
1827
- this.opts = C, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = i.bind(this), this._track = t, this._abortSignal = D, this.input = F, this.output = s;
1828
- }
1829
- unsubscribe() {
1830
- this._subscribers.clear();
1831
- }
1832
- setSubscriber(u, t) {
1833
- const F = this._subscribers.get(u) ?? [];
1834
- F.push(t), this._subscribers.set(u, F);
1998
+ function upsertMcpServer(path, apiKey) {
1999
+ const config = readJsonFile(path);
2000
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
2001
+ config.mcpServers = {};
1835
2002
  }
1836
- on(u, t) {
1837
- this.setSubscriber(u, { cb: t });
2003
+ config.mcpServers[SERVER_NAME] = getServerConfig(apiKey);
2004
+ writeJsonFile(path, config);
2005
+ }
2006
+ function configureClaude(apiKey) {
2007
+ const claudeConfigPath = join7(homedir5(), ".claude.json");
2008
+ const claudeMcpPath = join7(homedir5(), ".claude", "mcp.json");
2009
+ if (spawnSync2("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
2010
+ stdio: "ignore"
2011
+ }).status === 0) {
2012
+ return "Claude Code configured via `claude mcp add-json`";
1838
2013
  }
1839
- once(u, t) {
1840
- this.setSubscriber(u, { cb: t, once: true });
2014
+ upsertMcpServer(claudeConfigPath, apiKey);
2015
+ if (existsSync5(join7(homedir5(), ".claude"))) {
2016
+ upsertMcpServer(claudeMcpPath, apiKey);
1841
2017
  }
1842
- emit(u, ...t) {
1843
- const F = this._subscribers.get(u) ?? [], s = [];
1844
- for (const i of F)
1845
- i.cb(...t), i.once && s.push(() => F.splice(F.indexOf(i), 1));
1846
- for (const i of s)
1847
- i();
2018
+ return `Claude Code configured at ${claudeConfigPath}`;
2019
+ }
2020
+ function configureJsonClient(path, apiKey, label) {
2021
+ upsertMcpServer(path, apiKey);
2022
+ return `${label} configured at ${path}`;
2023
+ }
2024
+ function configureMcp(apiKey, targets = ["claude", "cursor", "codex"]) {
2025
+ const results = [];
2026
+ if (targets.includes("claude")) {
2027
+ results.push(configureClaude(apiKey));
1848
2028
  }
1849
- prompt() {
1850
- return new Promise((u, t) => {
1851
- if (this._abortSignal) {
1852
- if (this._abortSignal.aborted)
1853
- return this.state = "cancel", this.close(), u(S);
1854
- this._abortSignal.addEventListener("abort", () => {
1855
- this.state = "cancel", this.close();
1856
- }, { once: true });
1857
- }
1858
- const F = new X;
1859
- F._write = (s, i, D) => {
1860
- this._track && (this.value = this.rl?.line.replace(/\t/g, ""), this._cursor = this.rl?.cursor ?? 0, this.emit("value", this.value)), D();
1861
- }, this.input.pipe(F), this.rl = O.createInterface({ input: this.input, output: F, tabSize: 2, prompt: "", escapeCodeTimeout: 50, terminal: true }), O.emitKeypressEvents(this.input, this.rl), this.rl.prompt(), this.opts.initialValue !== undefined && this._track && this.rl.write(this.opts.initialValue), this.input.on("keypress", this.onKeypress), m(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
1862
- this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), m(this.input, false), u(this.value);
1863
- }), this.once("cancel", () => {
1864
- this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), m(this.input, false), u(S);
1865
- });
1866
- });
2029
+ if (targets.includes("cursor")) {
2030
+ results.push(configureJsonClient(join7(homedir5(), ".cursor", "mcp.json"), apiKey, "Cursor"));
1867
2031
  }
1868
- onKeypress(u, t) {
1869
- if (this.state === "error" && (this.state = "active"), t?.name && (!this._track && B.aliases.has(t.name) && this.emit("cursor", B.aliases.get(t.name)), B.actions.has(t.name) && this.emit("cursor", t.name)), u && (u.toLowerCase() === "y" || u.toLowerCase() === "n") && this.emit("confirm", u.toLowerCase() === "y"), u === "\t" && this.opts.placeholder && (this.value || (this.rl?.write(this.opts.placeholder), this.emit("value", this.opts.placeholder))), u && this.emit("key", u.toLowerCase()), t?.name === "return") {
1870
- if (this.opts.validate) {
1871
- const F = this.opts.validate(this.value);
1872
- F && (this.error = F instanceof Error ? F.message : F, this.state = "error", this.rl?.write(this.value));
1873
- }
1874
- this.state !== "error" && (this.state = "submit");
1875
- }
1876
- $([u, t?.name, t?.sequence], "cancel") && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
2032
+ if (targets.includes("codex")) {
2033
+ results.push(configureJsonClient(join7(homedir5(), ".codex", "mcp.json"), apiKey, "Codex"));
1877
2034
  }
1878
- close() {
1879
- this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
1880
- `), m(this.input, false), this.rl?.close(), this.rl = undefined, this.emit(`${this.state}`, this.value), this.unsubscribe();
2035
+ if (targets.includes("openclaw")) {
2036
+ results.push(configureJsonClient(join7(homedir5(), ".openclaw", "mcp.json"), apiKey, "OpenClaw"));
1881
2037
  }
1882
- restoreCursor() {
1883
- const u = Y(this._prevFrame, process.stdout.columns, { hard: true }).split(`
1884
- `).length - 1;
1885
- this.output.write(import_sisteransi.cursor.move(-999, u * -1));
2038
+ return results;
2039
+ }
2040
+
2041
+ // src/config.ts
2042
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
2043
+ import { join as join8 } from "node:path";
2044
+ import { homedir as homedir6 } from "node:os";
2045
+ var CONFIG_DIR = join8(homedir6(), ".conare");
2046
+ var CONFIG_PATH = join8(CONFIG_DIR, "config.json");
2047
+ function readConfig() {
2048
+ try {
2049
+ if (!existsSync6(CONFIG_PATH))
2050
+ return {};
2051
+ return JSON.parse(readFileSync7(CONFIG_PATH, "utf-8"));
2052
+ } catch {
2053
+ return {};
1886
2054
  }
1887
- render() {
1888
- const u = Y(this._render(this) ?? "", process.stdout.columns, { hard: true });
1889
- if (u !== this._prevFrame) {
1890
- if (this.state === "initial")
1891
- this.output.write(import_sisteransi.cursor.hide);
1892
- else {
1893
- const t = BD(this._prevFrame, u);
1894
- if (this.restoreCursor(), t && t?.length === 1) {
1895
- const F = t[0];
1896
- this.output.write(import_sisteransi.cursor.move(0, F)), this.output.write(import_sisteransi.erase.lines(1));
1897
- const s = u.split(`
2055
+ }
2056
+ function saveApiKey(apiKey) {
2057
+ mkdirSync3(CONFIG_DIR, { recursive: true });
2058
+ writeFileSync3(CONFIG_PATH, JSON.stringify({ apiKey }, null, 2) + `
1898
2059
  `);
1899
- this.output.write(s[F]), this._prevFrame = u, this.output.write(import_sisteransi.cursor.move(0, s.length - F - 1));
1900
- return;
1901
- }
1902
- if (t && t?.length > 1) {
1903
- const F = t[0];
1904
- this.output.write(import_sisteransi.cursor.move(0, F)), this.output.write(import_sisteransi.erase.down());
1905
- const s = u.split(`
1906
- `).slice(F);
1907
- this.output.write(s.join(`
1908
- `)), this._prevFrame = u;
1909
- return;
1910
- }
1911
- this.output.write(import_sisteransi.erase.down());
1912
- }
1913
- this.output.write(u), this.state === "initial" && (this.state = "active"), this._prevFrame = u;
1914
- }
1915
- }
2060
+ }
2061
+ function getSavedApiKey() {
2062
+ return readConfig().apiKey;
2063
+ }
2064
+
2065
+ // src/sync.ts
2066
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as readFileSync8, chmodSync, cpSync, rmSync, symlinkSync, readlinkSync, appendFileSync } from "node:fs";
2067
+ import { join as join9, dirname as dirname2 } from "node:path";
2068
+ import { homedir as homedir7, platform as platform2 } from "node:os";
2069
+ import { execSync } from "node:child_process";
2070
+ var CONARE_DIR = join9(homedir7(), ".conare");
2071
+ var BIN_DIR = join9(CONARE_DIR, "bin");
2072
+ var CONFIG_PATH2 = join9(CONARE_DIR, "config.json");
2073
+ var PLIST_LABEL = "ai.conare.ingest";
2074
+ var PLIST_PATH = join9(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
2075
+ var SYSTEMD_DIR = join9(homedir7(), ".config", "systemd", "user");
2076
+ var SYSTEMD_SERVICE = join9(SYSTEMD_DIR, "conare-sync.service");
2077
+ var SYSTEMD_TIMER = join9(SYSTEMD_DIR, "conare-sync.timer");
2078
+ var RUN_SH = `#!/bin/bash
2079
+ # Conare Memory — background sync wrapper
2080
+ # Resolves node at runtime to survive nvm/fnm upgrades
2081
+
2082
+ CONARE_DIR="$HOME/.conare"
2083
+ LOCKFILE="$CONARE_DIR/sync.lock"
2084
+ LOG="$CONARE_DIR/ingest.log"
2085
+
2086
+ # Log rotation: truncate if > 1MB
2087
+ if [ -f "$LOG" ] && [ "$(wc -c < "$LOG" 2>/dev/null)" -gt 1048576 ]; then
2088
+ tail -100 "$LOG" > "$LOG.tmp" && mv "$LOG.tmp" "$LOG"
2089
+ fi
2090
+
2091
+ # File lock: prevent concurrent runs
2092
+ # macOS doesn't have flock — use mkdir as atomic lock
2093
+ if ! mkdir "$LOCKFILE.d" 2>/dev/null; then
2094
+ # Check if stale (older than 30 min)
2095
+ if [ "$(uname)" = "Darwin" ]; then
2096
+ LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$LOCKFILE.d" 2>/dev/null || echo 0) ))
2097
+ else
2098
+ LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$LOCKFILE.d" 2>/dev/null || echo 0) ))
2099
+ fi
2100
+ if [ "$LOCK_AGE" -gt 1800 ]; then
2101
+ rm -rf "$LOCKFILE.d"
2102
+ mkdir "$LOCKFILE.d" 2>/dev/null || true
2103
+ else
2104
+ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) SKIP: another instance running" >> "$LOG"
2105
+ exit 0
2106
+ fi
2107
+ fi
2108
+ trap 'rm -rf "$LOCKFILE.d"' EXIT
2109
+
2110
+ # Resolve node binary
2111
+ resolve_node() {
2112
+ command -v node >/dev/null 2>&1 && { command -v node; return; }
2113
+ if [ -s "$HOME/.nvm/nvm.sh" ]; then
2114
+ . "$HOME/.nvm/nvm.sh" >/dev/null 2>&1
2115
+ command -v node >/dev/null 2>&1 && { command -v node; return; }
2116
+ fi
2117
+ if command -v fnm >/dev/null 2>&1; then
2118
+ eval "$(fnm env)" >/dev/null 2>&1
2119
+ command -v node >/dev/null 2>&1 && { command -v node; return; }
2120
+ fi
2121
+ for p in /opt/homebrew/bin/node /usr/local/bin/node /usr/bin/node; do
2122
+ [ -x "$p" ] && { echo "$p"; return; }
2123
+ done
2124
+ return 1
1916
2125
  }
1917
2126
 
1918
- class dD extends x {
1919
- get cursor() {
1920
- return this.value ? 0 : 1;
1921
- }
1922
- get _value() {
1923
- return this.cursor === 0;
1924
- }
1925
- constructor(u) {
1926
- super(u, false), this.value = !!u.initialValue, this.on("value", () => {
1927
- this.value = this._value;
1928
- }), this.on("confirm", (t) => {
1929
- this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = t, this.state = "submit", this.close();
1930
- }), this.on("cursor", () => {
1931
- this.value = !this.value;
1932
- });
1933
- }
2127
+ NODE=$(resolve_node)
2128
+ if [ -z "$NODE" ]; then
2129
+ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) ERROR: node not found" >> "$LOG"
2130
+ exit 1
2131
+ fi
2132
+
2133
+ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) START sync" >> "$LOG"
2134
+ "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" \\
2135
+ --config-file "$CONARE_DIR/config.json" \\
2136
+ --ingest-only \\
2137
+ --quiet \\
2138
+ 2>> "$LOG"
2139
+ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) DONE sync (exit $?)" >> "$LOG"
2140
+ `;
2141
+ function makePlist(intervalMinutes) {
2142
+ const home = homedir7();
2143
+ return `<?xml version="1.0" encoding="UTF-8"?>
2144
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
2145
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2146
+ <plist version="1.0">
2147
+ <dict>
2148
+ <key>Label</key>
2149
+ <string>${PLIST_LABEL}</string>
2150
+ <key>ProgramArguments</key>
2151
+ <array>
2152
+ <string>/bin/bash</string>
2153
+ <string>${home}/.conare/bin/run.sh</string>
2154
+ </array>
2155
+ <key>StartInterval</key>
2156
+ <integer>${intervalMinutes * 60}</integer>
2157
+ <key>StandardOutPath</key>
2158
+ <string>${home}/.conare/ingest.log</string>
2159
+ <key>StandardErrorPath</key>
2160
+ <string>${home}/.conare/ingest.log</string>
2161
+ <key>RunAtLoad</key>
2162
+ <true/>
2163
+ </dict>
2164
+ </plist>`;
1934
2165
  }
1935
- var A;
1936
- A = new WeakMap;
1937
- var kD = Object.defineProperty;
1938
- var $D = (e, u, t) => (u in e) ? kD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t;
1939
- var H = (e, u, t) => ($D(e, typeof u != "symbol" ? u + "" : u, t), t);
1940
- var SD = class extends x {
1941
- constructor(u) {
1942
- super(u, false), H(this, "options"), H(this, "cursor", 0), this.options = u.options, this.value = [...u.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: t }) => t === u.cursorAt), 0), this.on("key", (t) => {
1943
- t === "a" && this.toggleAll();
1944
- }), this.on("cursor", (t) => {
1945
- switch (t) {
1946
- case "left":
1947
- case "up":
1948
- this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
1949
- break;
1950
- case "down":
1951
- case "right":
1952
- this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
1953
- break;
1954
- case "space":
1955
- this.toggleValue();
1956
- break;
1957
- }
1958
- });
1959
- }
1960
- get _value() {
1961
- return this.options[this.cursor].value;
1962
- }
1963
- toggleAll() {
1964
- const u = this.value.length === this.options.length;
1965
- this.value = u ? [] : this.options.map((t) => t.value);
1966
- }
1967
- toggleValue() {
1968
- const u = this.value.includes(this._value);
1969
- this.value = u ? this.value.filter((t) => t !== this._value) : [...this.value, this._value];
1970
- }
1971
- };
1972
- var TD = Object.defineProperty;
1973
- var jD = (e, u, t) => (u in e) ? TD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t;
1974
- var U = (e, u, t) => (jD(e, typeof u != "symbol" ? u + "" : u, t), t);
2166
+ var SYSTEMD_SERVICE_CONTENT = `[Unit]
2167
+ Description=Conare Memory — background sync
1975
2168
 
1976
- class MD extends x {
1977
- constructor({ mask: u, ...t }) {
1978
- super(t), U(this, "valueWithCursor", ""), U(this, "_mask", "•"), this._mask = u ?? "•", this.on("finalize", () => {
1979
- this.valueWithCursor = this.masked;
1980
- }), this.on("value", () => {
1981
- if (this.cursor >= this.value.length)
1982
- this.valueWithCursor = `${this.masked}${import_picocolors.default.inverse(import_picocolors.default.hidden("_"))}`;
1983
- else {
1984
- const F = this.masked.slice(0, this.cursor), s = this.masked.slice(this.cursor);
1985
- this.valueWithCursor = `${F}${import_picocolors.default.inverse(s[0])}${s.slice(1)}`;
1986
- }
1987
- });
1988
- }
1989
- get cursor() {
1990
- return this._cursor;
1991
- }
1992
- get masked() {
1993
- return this.value.replaceAll(/./g, this._mask);
2169
+ [Service]
2170
+ Type=oneshot
2171
+ ExecStart=%h/.conare/bin/run.sh
2172
+ `;
2173
+ function makeSystemdTimer(intervalMinutes) {
2174
+ return `[Unit]
2175
+ Description=Conare Memory — sync every ${intervalMinutes} minutes
2176
+
2177
+ [Timer]
2178
+ OnBootSec=2min
2179
+ OnUnitActiveSec=${intervalMinutes}min
2180
+ Persistent=true
2181
+
2182
+ [Install]
2183
+ WantedBy=timers.target
2184
+ `;
2185
+ }
2186
+ function hasSystemd() {
2187
+ try {
2188
+ execSync("systemctl --user status 2>/dev/null", { stdio: "ignore" });
2189
+ return true;
2190
+ } catch {
2191
+ return false;
1994
2192
  }
1995
2193
  }
1996
-
1997
- // node_modules/@clack/prompts/dist/index.mjs
1998
- var import_picocolors2 = __toESM(require_picocolors(), 1);
1999
- var import_sisteransi2 = __toESM(require_src(), 1);
2000
- import y2 from "node:process";
2001
- function ce() {
2002
- return y2.platform !== "win32" ? y2.env.TERM !== "linux" : !!y2.env.CI || !!y2.env.WT_SESSION || !!y2.env.TERMINUS_SUBLIME || y2.env.ConEmuTask === "{cmd::Cmder}" || y2.env.TERM_PROGRAM === "Terminus-Sublime" || y2.env.TERM_PROGRAM === "vscode" || y2.env.TERM === "xterm-256color" || y2.env.TERM === "alacritty" || y2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
2194
+ function uid() {
2195
+ try {
2196
+ return execSync("id -u", { encoding: "utf-8" }).trim();
2197
+ } catch {
2198
+ return "501";
2199
+ }
2003
2200
  }
2004
- var V2 = ce();
2005
- var u = (t, n) => V2 ? t : n;
2006
- var le = u("◆", "*");
2007
- var L2 = u("■", "x");
2008
- var W2 = u("▲", "x");
2009
- var C = u("◇", "o");
2010
- var ue = u("┌", "T");
2011
- var o = u("│", "|");
2012
- var d2 = u("└", "—");
2013
- var k2 = u("●", ">");
2014
- var P2 = u("○", " ");
2015
- var A2 = u("◻", "[•]");
2016
- var T = u("◼", "[+]");
2017
- var F = u("◻", "[ ]");
2018
- var $e = u("▪", "•");
2019
- var _2 = u("─", "-");
2020
- var me = u("╮", "+");
2021
- var de = u("├", "+");
2022
- var pe = u("╯", "+");
2023
- var q = u("●", "•");
2024
- var D = u("◆", "*");
2025
- var U2 = u("▲", "!");
2026
- var K2 = u("■", "x");
2027
- var b2 = (t) => {
2028
- switch (t) {
2029
- case "initial":
2030
- case "active":
2031
- return import_picocolors2.default.cyan(le);
2032
- case "cancel":
2033
- return import_picocolors2.default.red(L2);
2034
- case "error":
2035
- return import_picocolors2.default.yellow(W2);
2036
- case "submit":
2037
- return import_picocolors2.default.green(C);
2201
+ function persistBinary(apiKey) {
2202
+ mkdirSync4(BIN_DIR, { recursive: true });
2203
+ const cliEntry = findCliBundle();
2204
+ if (!cliEntry) {
2205
+ throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
2038
2206
  }
2039
- };
2040
- var G2 = (t) => {
2041
- const { cursor: n, options: r2, style: i } = t, s = t.maxItems ?? Number.POSITIVE_INFINITY, c = Math.max(process.stdout.rows - 4, 0), a = Math.min(c, Math.max(s, 5));
2042
- let l2 = 0;
2043
- n >= l2 + a - 3 ? l2 = Math.max(Math.min(n - a + 3, r2.length - a), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
2044
- const $2 = a < r2.length && l2 > 0, g = a < r2.length && l2 + a < r2.length;
2045
- return r2.slice(l2, l2 + a).map((p2, v2, f) => {
2046
- const j2 = v2 === 0 && $2, E = v2 === f.length - 1 && g;
2047
- return j2 || E ? import_picocolors2.default.dim("...") : i(p2, v2 + l2 === n);
2048
- });
2049
- };
2050
- var ge = (t) => new MD({ validate: t.validate, mask: t.mask ?? $e, render() {
2051
- const n = `${import_picocolors2.default.gray(o)}
2052
- ${b2(this.state)} ${t.message}
2053
- `, r2 = this.valueWithCursor, i = this.masked;
2054
- switch (this.state) {
2055
- case "error":
2056
- return `${n.trim()}
2057
- ${import_picocolors2.default.yellow(o)} ${i}
2058
- ${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(this.error)}
2059
- `;
2060
- case "submit":
2061
- return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(i)}`;
2062
- case "cancel":
2063
- return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(i ?? ""))}${i ? `
2064
- ${import_picocolors2.default.gray(o)}` : ""}`;
2065
- default:
2066
- return `${n}${import_picocolors2.default.cyan(o)} ${r2}
2067
- ${import_picocolors2.default.cyan(d2)}
2068
- `;
2207
+ const dest = join9(BIN_DIR, "conare-ingest.mjs");
2208
+ const content = readFileSync8(cliEntry, "utf-8");
2209
+ writeFileSync4(dest, content);
2210
+ const sqlJsDir = findSqlJs();
2211
+ if (sqlJsDir) {
2212
+ const targetDir = join9(BIN_DIR, "node_modules", "sql.js");
2213
+ mkdirSync4(targetDir, { recursive: true });
2214
+ try {
2215
+ cpSync(sqlJsDir, targetDir, { recursive: true });
2216
+ } catch {}
2069
2217
  }
2070
- } }).prompt();
2071
- var ye = (t) => {
2072
- const n = t.active ?? "Yes", r2 = t.inactive ?? "No";
2073
- return new dD({ active: n, inactive: r2, initialValue: t.initialValue ?? true, render() {
2074
- const i = `${import_picocolors2.default.gray(o)}
2075
- ${b2(this.state)} ${t.message}
2076
- `, s = this.value ? n : r2;
2077
- switch (this.state) {
2078
- case "submit":
2079
- return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(s)}`;
2080
- case "cancel":
2081
- return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}
2082
- ${import_picocolors2.default.gray(o)}`;
2083
- default:
2084
- return `${i}${import_picocolors2.default.cyan(o)} ${this.value ? `${import_picocolors2.default.green(k2)} ${n}` : `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(r2)}` : `${import_picocolors2.default.green(k2)} ${r2}`}
2085
- ${import_picocolors2.default.cyan(d2)}
2086
- `;
2087
- }
2088
- } }).prompt();
2089
- };
2090
- var fe = (t) => {
2091
- const n = (r2, i) => {
2092
- const s = r2.label ?? String(r2.value);
2093
- return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(T)} ${import_picocolors2.default.dim(s)} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}` : i === "active-selected" ? `${import_picocolors2.default.green(T)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.dim(F)} ${import_picocolors2.default.dim(s)}`;
2094
- };
2095
- return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? true, cursorAt: t.cursorAt, validate(r2) {
2096
- if (this.required && r2.length === 0)
2097
- return `Please select at least one option.
2098
- ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
2099
- }, render() {
2100
- const r2 = `${import_picocolors2.default.gray(o)}
2101
- ${b2(this.state)} ${t.message}
2102
- `, i = (s, c) => {
2103
- const a = this.value.includes(s.value);
2104
- return c && a ? n(s, "active-selected") : a ? n(s, "selected") : n(s, c ? "active" : "inactive");
2105
- };
2106
- switch (this.state) {
2107
- case "submit":
2108
- return `${r2}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => n(s, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
2109
- case "cancel": {
2110
- const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => n(c, "cancelled")).join(import_picocolors2.default.dim(", "));
2111
- return `${r2}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
2112
- ${import_picocolors2.default.gray(o)}` : ""}`;
2113
- }
2114
- case "error": {
2115
- const s = this.error.split(`
2116
- `).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
2117
- `);
2118
- return `${r2 + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
2119
- ${import_picocolors2.default.yellow(o)} `)}
2120
- ${s}
2121
- `;
2122
- }
2123
- default:
2124
- return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
2125
- ${import_picocolors2.default.cyan(o)} `)}
2126
- ${import_picocolors2.default.cyan(d2)}
2127
- `;
2128
- }
2129
- } }).prompt();
2130
- };
2131
- var Me = (t = "", n = "") => {
2132
- const r2 = `
2133
- ${t}
2134
- `.split(`
2135
- `), i = S2(n).length, s = Math.max(r2.reduce((a, l2) => {
2136
- const $2 = S2(l2);
2137
- return $2.length > a ? $2.length : a;
2138
- }, 0), i) + 2, c = r2.map((a) => `${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(a)}${" ".repeat(s - S2(a).length)}${import_picocolors2.default.gray(o)}`).join(`
2139
- `);
2140
- process.stdout.write(`${import_picocolors2.default.gray(o)}
2141
- ${import_picocolors2.default.green(C)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(_2.repeat(Math.max(s - i - 1, 1)) + me)}
2142
- ${c}
2143
- ${import_picocolors2.default.gray(de + _2.repeat(s + 2) + pe)}
2144
- `);
2145
- };
2146
- var xe = (t = "") => {
2147
- process.stdout.write(`${import_picocolors2.default.gray(d2)} ${import_picocolors2.default.red(t)}
2148
-
2149
- `);
2150
- };
2151
- var Ie = (t = "") => {
2152
- process.stdout.write(`${import_picocolors2.default.gray(ue)} ${t}
2153
- `);
2154
- };
2155
- var Se = (t = "") => {
2156
- process.stdout.write(`${import_picocolors2.default.gray(o)}
2157
- ${import_picocolors2.default.gray(d2)} ${t}
2158
-
2159
- `);
2160
- };
2161
- var J = `${import_picocolors2.default.gray(o)} `;
2162
-
2163
- // src/interactive.ts
2164
- function formatDetectedCount(count) {
2165
- if (count === undefined)
2166
- return "available";
2167
- return `${count.toLocaleString()} chats`;
2218
+ const runShPath = join9(BIN_DIR, "run.sh");
2219
+ writeFileSync4(runShPath, RUN_SH);
2220
+ chmodSync(runShPath, 493);
2221
+ writeFileSync4(CONFIG_PATH2, JSON.stringify({ apiKey }, null, 2) + `
2222
+ `);
2168
2223
  }
2169
- function ensureValue(value) {
2170
- if (pD(value)) {
2171
- xe("Setup cancelled.");
2172
- process.exit(0);
2224
+ function findCliBundle() {
2225
+ const entry = process.argv[1];
2226
+ if (entry && existsSync7(entry))
2227
+ return entry;
2228
+ const candidates = [
2229
+ join9(dirname2(new URL(import.meta.url).pathname), "index.js"),
2230
+ join9(dirname2(new URL(import.meta.url).pathname), "..", "dist", "index.js")
2231
+ ];
2232
+ for (const c of candidates) {
2233
+ if (existsSync7(c))
2234
+ return c;
2173
2235
  }
2174
- return value;
2236
+ return null;
2175
2237
  }
2176
- function startSetup() {
2177
- Ie("Conare setup");
2238
+ function findSqlJs() {
2239
+ const candidates = [
2240
+ join9(process.cwd(), "node_modules", "sql.js"),
2241
+ join9(dirname2(new URL(import.meta.url).pathname), "..", "node_modules", "sql.js"),
2242
+ join9(dirname2(new URL(import.meta.url).pathname), "..", "..", "node_modules", "sql.js")
2243
+ ];
2244
+ for (const c of candidates) {
2245
+ if (existsSync7(c))
2246
+ return c;
2247
+ }
2248
+ return null;
2178
2249
  }
2179
- function finishSetup() {
2180
- Se("Starting setup...");
2250
+ function setupMacOS(intervalMinutes) {
2251
+ const plistDir = dirname2(PLIST_PATH);
2252
+ mkdirSync4(plistDir, { recursive: true });
2253
+ writeFileSync4(PLIST_PATH, makePlist(intervalMinutes));
2254
+ const id = uid();
2255
+ try {
2256
+ execSync(`launchctl bootout gui/${id} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
2257
+ } catch {}
2258
+ try {
2259
+ execSync(`launchctl bootstrap gui/${id} "${PLIST_PATH}"`, { stdio: "ignore" });
2260
+ } catch {
2261
+ try {
2262
+ execSync(`launchctl load "${PLIST_PATH}"`, { stdio: "ignore" });
2263
+ } catch {
2264
+ throw new Error("Failed to load launchd agent. Try manually: launchctl load " + PLIST_PATH);
2265
+ }
2266
+ }
2181
2267
  }
2182
- function showDetectedApps(targets) {
2183
- Me(targets.map((target) => `• ${target.label}: ${target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)}`).join(`
2184
- `), "Detected apps");
2268
+ function setupLinuxSystemd(intervalMinutes) {
2269
+ mkdirSync4(SYSTEMD_DIR, { recursive: true });
2270
+ writeFileSync4(SYSTEMD_SERVICE, SYSTEMD_SERVICE_CONTENT);
2271
+ writeFileSync4(SYSTEMD_TIMER, makeSystemdTimer(intervalMinutes));
2272
+ execSync("systemctl --user daemon-reload", { stdio: "ignore" });
2273
+ execSync("systemctl --user enable --now conare-sync.timer", { stdio: "ignore" });
2185
2274
  }
2186
- async function promptApiKey(options) {
2187
- if (options.providedApiKey) {
2188
- return options.providedApiKey;
2275
+ function setupLinuxCron(intervalMinutes) {
2276
+ const cronCmd = `${homedir7()}/.conare/bin/run.sh`;
2277
+ const cronLine = `*/${intervalMinutes} * * * * ${cronCmd}`;
2278
+ try {
2279
+ const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
2280
+ const filtered = existing.split(`
2281
+ `).filter((l) => !l.includes("conare")).join(`
2282
+ `);
2283
+ const newCrontab = (filtered.trim() ? filtered.trim() + `
2284
+ ` : "") + cronLine + `
2285
+ `;
2286
+ execSync("crontab -", { input: newCrontab, stdio: ["pipe", "ignore", "ignore"] });
2287
+ } catch {
2288
+ execSync("crontab -", { input: cronLine + `
2289
+ `, stdio: ["pipe", "ignore", "ignore"] });
2189
2290
  }
2190
- const keyPrompt = await ge({
2191
- message: options.savedApiKey ? `API key (press Enter to use saved key ending in ${options.savedApiKey.slice(-6)})` : "API key",
2192
- mask: "*",
2193
- validate(value) {
2194
- const resolved = value.trim() || options.savedApiKey || "";
2195
- if (!resolved)
2196
- return "Enter an API key from https://mcp.conare.ai";
2197
- if (!resolved.startsWith("cmem_"))
2198
- return "API keys start with cmem_";
2199
- return;
2291
+ }
2292
+ function installGlobalCommand() {
2293
+ const wrapper = join9(BIN_DIR, "conare");
2294
+ const content = `#!/bin/bash
2295
+ # Conare global command runs the persisted CLI bundle
2296
+ CONARE_DIR="$HOME/.conare"
2297
+ NODE=$(command -v node || echo "")
2298
+ if [ -z "$NODE" ]; then
2299
+ if [ -s "$HOME/.nvm/nvm.sh" ]; then
2300
+ . "$HOME/.nvm/nvm.sh" >/dev/null 2>&1
2301
+ NODE=$(command -v node || echo "")
2302
+ fi
2303
+ fi
2304
+ if [ -z "$NODE" ]; then
2305
+ echo "Error: node not found. Install Node.js first." >&2
2306
+ exit 1
2307
+ fi
2308
+ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
2309
+ `;
2310
+ writeFileSync4(wrapper, content);
2311
+ chmodSync(wrapper, 493);
2312
+ const symlinkTarget = "/usr/local/bin/conare";
2313
+ try {
2314
+ if (existsSync7(symlinkTarget)) {
2315
+ try {
2316
+ const existing = readlinkSync(symlinkTarget);
2317
+ if (existing === wrapper)
2318
+ return "Global command: conare (already linked)";
2319
+ } catch {}
2320
+ unlinkSync(symlinkTarget);
2200
2321
  }
2201
- });
2202
- return ensureValue(keyPrompt).trim() || options.savedApiKey;
2322
+ symlinkSync(wrapper, symlinkTarget);
2323
+ return "Global command: conare (linked to /usr/local/bin)";
2324
+ } catch {
2325
+ const pathDirs = (process.env.PATH || "").split(":");
2326
+ if (pathDirs.includes(BIN_DIR)) {
2327
+ return "Global command: conare (via ~/.conare/bin in PATH)";
2328
+ }
2329
+ const shellProfile = getShellProfile();
2330
+ if (shellProfile) {
2331
+ try {
2332
+ const profileContent = existsSync7(shellProfile) ? readFileSync8(shellProfile, "utf-8") : "";
2333
+ const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
2334
+ if (!profileContent.includes(".conare/bin")) {
2335
+ appendFileSync(shellProfile, `
2336
+ # Conare CLI
2337
+ ${exportLine}
2338
+ `);
2339
+ return `Global command: conare (added ~/.conare/bin to ${shellProfile} — restart shell)`;
2340
+ }
2341
+ return "Global command: conare (via ~/.conare/bin in PATH)";
2342
+ } catch {
2343
+ return `Global command: add ~/.conare/bin to your PATH manually`;
2344
+ }
2345
+ }
2346
+ return `Global command: add ~/.conare/bin to your PATH manually`;
2347
+ }
2203
2348
  }
2204
- async function selectChatSources(targets) {
2205
- return ensureValue(await fe({
2206
- message: "Select chat sources",
2207
- required: false,
2208
- initialValues: targets.filter((target) => target.recommended).map((target) => target.id),
2209
- options: targets.map((target) => ({
2210
- value: target.id,
2211
- label: target.label,
2212
- hint: target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)
2213
- }))
2214
- }));
2349
+ function getShellProfile() {
2350
+ const home = homedir7();
2351
+ const shell = process.env.SHELL || "";
2352
+ if (shell.includes("zsh"))
2353
+ return join9(home, ".zshrc");
2354
+ if (shell.includes("bash")) {
2355
+ const profile = join9(home, ".bash_profile");
2356
+ if (platform2() === "darwin" && existsSync7(profile))
2357
+ return profile;
2358
+ return join9(home, ".bashrc");
2359
+ }
2360
+ if (existsSync7(join9(home, ".zshrc")))
2361
+ return join9(home, ".zshrc");
2362
+ if (existsSync7(join9(home, ".bashrc")))
2363
+ return join9(home, ".bashrc");
2364
+ return null;
2215
2365
  }
2216
- async function selectMcpTargets(targets) {
2217
- return ensureValue(await fe({
2218
- message: "Select where to install the MCP",
2219
- required: false,
2220
- initialValues: targets.filter((target) => target.recommended).map((target) => target.id),
2221
- options: targets.map((target) => ({
2222
- value: target.id,
2223
- label: target.label,
2224
- hint: target.available === false ? "not detected" : "recommended"
2225
- }))
2226
- }));
2366
+ function installSync(apiKey, intervalMinutes = 10) {
2367
+ const messages = [];
2368
+ const os = platform2();
2369
+ persistBinary(apiKey);
2370
+ messages.push("Persisted CLI to ~/.conare/bin/");
2371
+ messages.push("Saved config to ~/.conare/config.json");
2372
+ const globalMsg = installGlobalCommand();
2373
+ if (globalMsg)
2374
+ messages.push(globalMsg);
2375
+ if (os === "darwin") {
2376
+ setupMacOS(intervalMinutes);
2377
+ messages.push(`Installed launchd agent (every ${intervalMinutes} min)`);
2378
+ messages.push(`Plist: ${PLIST_PATH}`);
2379
+ } else if (os === "linux") {
2380
+ if (hasSystemd()) {
2381
+ setupLinuxSystemd(intervalMinutes);
2382
+ messages.push(`Installed systemd timer (every ${intervalMinutes} min)`);
2383
+ } else {
2384
+ setupLinuxCron(intervalMinutes);
2385
+ messages.push(`Installed cron job (every ${intervalMinutes} min)`);
2386
+ }
2387
+ } else {
2388
+ messages.push(`Unsupported platform: ${os}. Run manually: ~/.conare/bin/run.sh`);
2389
+ }
2390
+ return messages;
2227
2391
  }
2228
- async function confirmIndexCodebase() {
2229
- return ensureValue(await ye({
2230
- message: "Index this codebase too?",
2231
- initialValue: false
2232
- }));
2392
+ function uninstallSync() {
2393
+ const messages = [];
2394
+ const os = platform2();
2395
+ if (os === "darwin") {
2396
+ try {
2397
+ execSync(`launchctl bootout gui/${uid()} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
2398
+ } catch {}
2399
+ if (existsSync7(PLIST_PATH)) {
2400
+ unlinkSync(PLIST_PATH);
2401
+ messages.push("Removed launchd agent");
2402
+ }
2403
+ } else if (os === "linux") {
2404
+ if (hasSystemd()) {
2405
+ try {
2406
+ execSync("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
2407
+ } catch {}
2408
+ if (existsSync7(SYSTEMD_SERVICE))
2409
+ unlinkSync(SYSTEMD_SERVICE);
2410
+ if (existsSync7(SYSTEMD_TIMER))
2411
+ unlinkSync(SYSTEMD_TIMER);
2412
+ try {
2413
+ execSync("systemctl --user daemon-reload", { stdio: "ignore" });
2414
+ } catch {}
2415
+ messages.push("Removed systemd timer");
2416
+ } else {
2417
+ try {
2418
+ const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
2419
+ const filtered = existing.split(`
2420
+ `).filter((l) => !l.includes("conare")).join(`
2421
+ `);
2422
+ execSync("crontab -", { input: filtered.trim() + `
2423
+ `, stdio: ["pipe", "ignore", "ignore"] });
2424
+ messages.push("Removed cron job");
2425
+ } catch {}
2426
+ }
2427
+ }
2428
+ const filesToRemove = [
2429
+ join9(CONARE_DIR, "config.json"),
2430
+ join9(CONARE_DIR, "sync.lock")
2431
+ ];
2432
+ for (const f of filesToRemove) {
2433
+ if (existsSync7(f))
2434
+ unlinkSync(f);
2435
+ }
2436
+ if (existsSync7(BIN_DIR)) {
2437
+ rmSync(BIN_DIR, { recursive: true, force: true });
2438
+ messages.push("Removed ~/.conare/bin/");
2439
+ }
2440
+ if (messages.length === 0) {
2441
+ messages.push("Nothing to uninstall (no sync timer found)");
2442
+ }
2443
+ return messages;
2233
2444
  }
2234
2445
 
2235
2446
  // src/index.ts
2447
+ init_interactive();
2236
2448
  function getManifestFingerprint(memory) {
2237
2449
  const metadata = memory.metadata;
2238
2450
  if (metadata?.dedupKey && metadata?.contentHash) {
@@ -2298,6 +2510,7 @@ function parseArgs() {
2298
2510
  let quiet = false;
2299
2511
  let installSyncFlag = false;
2300
2512
  let uninstallSyncFlag = false;
2513
+ let syncInterval = 10;
2301
2514
  let source;
2302
2515
  let wasmDir;
2303
2516
  let indexPath;
@@ -2316,6 +2529,8 @@ function parseArgs() {
2316
2529
  installSyncFlag = true;
2317
2530
  } else if (args[i] === "--uninstall-sync") {
2318
2531
  uninstallSyncFlag = true;
2532
+ } else if (args[i] === "--sync-interval" && args[i + 1]) {
2533
+ syncInterval = parseInt(args[++i], 10);
2319
2534
  } else if (args[i] === "--source" && args[i + 1]) {
2320
2535
  source = args[++i];
2321
2536
  } else if (args[i] === "--wasm-dir" && args[i + 1]) {
@@ -2330,9 +2545,11 @@ function parseArgs() {
2330
2545
  interactive = true;
2331
2546
  } else if (args[i] === "--help" || args[i] === "-h") {
2332
2547
  console.log(`
2333
- conare — Ingest AI chat history into Conare
2548
+ conare — AI memory for your coding tools
2334
2549
 
2335
2550
  Usage:
2551
+ conare install Just install the MCP (simplest)
2552
+ conare install --key <api_key> Install MCP with key
2336
2553
  conare --key <api_key> Ingest chat history
2337
2554
  conare --key <api_key> --index [path] Index codebase
2338
2555
 
@@ -2345,6 +2562,7 @@ Options:
2345
2562
  --quiet Suppress all stdout output (for background timer runs)
2346
2563
  --install-sync Set up automatic background sync (every 10 min)
2347
2564
  --uninstall-sync Remove background sync timer and persisted files
2565
+ --sync-interval <n> Sync interval in minutes (default: 10)
2348
2566
  --ingest-only Ingest memories without MCP configuration
2349
2567
  --config-only Configure MCP only, skip ingestion
2350
2568
  --interactive Run guided setup prompts
@@ -2364,9 +2582,45 @@ Get your API key at https://mcp.conare.ai
2364
2582
  console.error("Error: --ingest-only and --config-only cannot be used together");
2365
2583
  process.exit(1);
2366
2584
  }
2367
- return { key, configFile, dryRun, force, ingestOnly, configOnly, interactive, quiet, installSync: installSyncFlag, uninstallSync: uninstallSyncFlag, source, wasmDir, indexPath };
2585
+ return { key, configFile, dryRun, force, ingestOnly, configOnly, interactive, quiet, installSync: installSyncFlag, uninstallSync: uninstallSyncFlag, syncInterval, source, wasmDir, indexPath };
2586
+ }
2587
+ async function runInstall() {
2588
+ const args = process.argv.slice(3);
2589
+ let key = "";
2590
+ for (let i = 0;i < args.length; i++) {
2591
+ if (args[i] === "--key" && args[i + 1]) {
2592
+ key = args[++i];
2593
+ }
2594
+ }
2595
+ const savedApiKey = getSavedApiKey();
2596
+ let apiKey = key || process.env.CONARE_API_KEY || savedApiKey;
2597
+ const hasTty = !!process.stdin.isTTY && !!process.stdout.isTTY;
2598
+ if (!apiKey && hasTty) {
2599
+ const { promptApiKey: promptKey } = await Promise.resolve().then(() => (init_interactive(), exports_interactive));
2600
+ apiKey = await promptKey({ savedApiKey }) || "";
2601
+ }
2602
+ if (!apiKey) {
2603
+ printMissingKeyError();
2604
+ process.exit(1);
2605
+ }
2606
+ const auth = await validateKey(apiKey);
2607
+ if (!auth.valid) {
2608
+ console.error("Invalid API key. Check your key at https://mcp.conare.ai");
2609
+ process.exit(1);
2610
+ }
2611
+ saveApiKey(apiKey);
2612
+ const allTargets = MCP_TARGETS.map((t) => t.id);
2613
+ const lines = configureMcp(apiKey, allTargets);
2614
+ for (const line of lines)
2615
+ console.log(` ${line}`);
2616
+ console.log("");
2617
+ console.log(" \x1B[32m✓\x1B[0m MCP installed. Restart your AI tool to connect.");
2618
+ console.log("");
2368
2619
  }
2369
2620
  async function main() {
2621
+ if (process.argv[2] === "install") {
2622
+ return runInstall();
2623
+ }
2370
2624
  const opts = parseArgs();
2371
2625
  let configFileKey;
2372
2626
  if (opts.configFile) {
@@ -2406,7 +2660,7 @@ async function main() {
2406
2660
  if (shouldRunInteractive) {
2407
2661
  const detectedTools = detect();
2408
2662
  interactiveTargets = MCP_TARGETS.map((target) => {
2409
- const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex");
2663
+ const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex" || target.id === "openclaw" && tool.name === "OpenClaw");
2410
2664
  return {
2411
2665
  id: target.id,
2412
2666
  label: target.label,
@@ -2435,38 +2689,25 @@ async function main() {
2435
2689
  process.exit(1);
2436
2690
  }
2437
2691
  if (opts.installSync) {
2438
- console.log("");
2439
- console.log("Setting up background sync...");
2440
- console.log("");
2441
2692
  const auth2 = await validateKey(apiKey);
2442
2693
  if (!auth2.valid) {
2443
- console.error("INVALID key. Check your key at https://mcp.conare.ai");
2694
+ console.error("Invalid API key. Check your key at https://mcp.conare.ai");
2444
2695
  process.exit(1);
2445
2696
  }
2446
- const messages = installSync(apiKey);
2447
- for (const msg of messages)
2448
- console.log(` ✓ ${msg}`);
2449
- console.log("");
2450
- console.log("Background sync active. New chats will be ingested every 10 minutes.");
2451
- console.log("Logs: ~/.conare/ingest.log");
2452
- console.log("Remove with: conare --uninstall-sync");
2697
+ const syncSpinner = Y2();
2698
+ syncSpinner.start("Setting up background sync...");
2699
+ installSync(apiKey, opts.syncInterval);
2700
+ syncSpinner.stop(`Background sync active (every ${opts.syncInterval}min)`);
2453
2701
  console.log("");
2454
2702
  return;
2455
2703
  }
2456
2704
  log("");
2457
- log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
2458
- log(" Conare Installer");
2459
- log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
2460
- log("");
2461
- write("Validating API key... ");
2462
2705
  const auth = await validateKey(apiKey);
2463
2706
  if (!auth.valid) {
2464
- console.error("INVALID. Check your key at https://mcp.conare.ai");
2707
+ console.error("Invalid API key. Check your key at https://mcp.conare.ai");
2465
2708
  process.exit(1);
2466
2709
  }
2467
- log(auth.email ? `OK (${auth.email})` : "OK");
2468
2710
  saveApiKey(apiKey);
2469
- log();
2470
2711
  if (!opts.force && !opts.dryRun) {
2471
2712
  const remoteMemoryCount = await getRemoteMemoryCount(apiKey);
2472
2713
  const localManifest = getIngested();
@@ -2482,39 +2723,32 @@ async function main() {
2482
2723
  }
2483
2724
  if (effectiveConfigOnly) {
2484
2725
  if (!opts.dryRun) {
2485
- log("─── Configuring MCP ───");
2486
- log("");
2487
- for (const line of configureMcp(apiKey, selectedTargets)) {
2488
- log(` ${line}`);
2489
- }
2490
- log("");
2726
+ const mcpSpinner = Y2();
2727
+ mcpSpinner.start("Configuring MCP...");
2728
+ const lines = configureMcp(apiKey, selectedTargets);
2729
+ mcpSpinner.stop(`MCP configured: ${lines.join(", ")}`);
2491
2730
  }
2492
- log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
2493
- log("Done! Conare MCP is configured.");
2494
- log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
2731
+ log("");
2732
+ log("Done! Conare MCP is configured.");
2495
2733
  log("");
2496
2734
  return;
2497
2735
  }
2498
2736
  if (effectiveIndexPath !== undefined) {
2499
2737
  const { resolve: resolve2 } = await import("node:path");
2500
2738
  const absPath = resolve2(effectiveIndexPath);
2501
- log(`Indexing codebase: ${absPath}`);
2502
2739
  if (opts.force) {
2503
2740
  clearIngested("codebase");
2504
- write("Clearing existing codebase index... ");
2505
2741
  try {
2506
2742
  await fetch("https://mcp.conare.ai/api/containers/codebase", {
2507
2743
  method: "DELETE",
2508
2744
  headers: { Authorization: `Bearer ${apiKey}` }
2509
2745
  });
2510
- log("done");
2511
- } catch {
2512
- log("skipped (no existing index)");
2513
- }
2746
+ } catch {}
2514
2747
  }
2515
- write("Scanning files... ");
2748
+ const idxSpinner = Y2();
2749
+ idxSpinner.start("Scanning codebase...");
2516
2750
  const { memories, fileCount, skipped } = indexCodebase(absPath);
2517
- log(`${fileCount} files found, ${skipped} skipped`);
2751
+ idxSpinner.stop(`Codebase: ${fileCount} files found, ${skipped} skipped`);
2518
2752
  if (memories.length === 0) {
2519
2753
  log(`
2520
2754
  Nothing new to index.`);
@@ -2541,14 +2775,12 @@ Nothing new to index.`);
2541
2775
  if (!opts.quiet)
2542
2776
  printFailureSummary(results, memories);
2543
2777
  }
2544
- log();
2778
+ log("");
2545
2779
  if (!opts.dryRun && !effectiveIngestOnly) {
2546
- log("─── Configuring MCP ───");
2547
- log("");
2548
- for (const line of configureMcp(apiKey, selectedTargets)) {
2549
- log(` ${line}`);
2550
- }
2551
- log("");
2780
+ const mcpSpinner = Y2();
2781
+ mcpSpinner.start("Configuring MCP...");
2782
+ const mcpLines = configureMcp(apiKey, selectedTargets);
2783
+ mcpSpinner.stop(`MCP: ${mcpLines.join(", ")}`);
2552
2784
  }
2553
2785
  log("Done! Your codebase is now searchable via recall.");
2554
2786
  return;
@@ -2578,23 +2810,39 @@ Nothing new to index.`);
2578
2810
  const allMemories = [];
2579
2811
  const shouldIngest = (name) => selectedSources.includes(name);
2580
2812
  if (shouldIngest("claude") && tools.find((t) => t.name === "Claude Code")?.available) {
2581
- write("Ingesting Claude Code... ");
2582
- const { memories, filtered, deduped } = ingestClaude();
2583
- allMemories.push(...memories);
2584
- log(renderDiscoverySummary(memories.length, filtered, deduped));
2813
+ if (!opts.quiet) {
2814
+ const s = Y2();
2815
+ s.start("Scanning Claude Code chats...");
2816
+ const { memories, filtered, deduped } = ingestClaude();
2817
+ allMemories.push(...memories);
2818
+ s.stop(`Claude Code: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
2819
+ } else {
2820
+ allMemories.push(...ingestClaude().memories);
2821
+ }
2585
2822
  }
2586
2823
  if (shouldIngest("codex") && tools.find((t) => t.name === "Codex")?.available) {
2587
- write("Ingesting Codex... ");
2588
- const { memories, filtered, deduped } = ingestCodex();
2589
- allMemories.push(...memories);
2590
- log(renderDiscoverySummary(memories.length, filtered, deduped));
2824
+ if (!opts.quiet) {
2825
+ const s = Y2();
2826
+ s.start("Scanning Codex chats...");
2827
+ const { memories, filtered, deduped } = ingestCodex();
2828
+ allMemories.push(...memories);
2829
+ s.stop(`Codex: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
2830
+ } else {
2831
+ allMemories.push(...ingestCodex().memories);
2832
+ }
2591
2833
  }
2592
2834
  if (shouldIngest("cursor") && tools.find((t) => t.name === "Cursor")?.available) {
2593
- write("Ingesting Cursor... ");
2594
- const cursorTool = tools.find((t) => t.name === "Cursor");
2595
- const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir);
2596
- allMemories.push(...memories);
2597
- log(renderDiscoverySummary(memories.length, filtered, deduped));
2835
+ if (!opts.quiet) {
2836
+ const s = Y2();
2837
+ s.start("Scanning Cursor chats...");
2838
+ const cursorTool = tools.find((t) => t.name === "Cursor");
2839
+ const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir);
2840
+ allMemories.push(...memories);
2841
+ s.stop(`Cursor: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
2842
+ } else {
2843
+ const cursorTool = tools.find((t) => t.name === "Cursor");
2844
+ allMemories.push(...(await ingestCursor(cursorTool.path, opts.wasmDir)).memories);
2845
+ }
2598
2846
  }
2599
2847
  allMemories.sort((a, b3) => {
2600
2848
  const da = a.metadata?.date || "0000";
@@ -2670,21 +2918,18 @@ Nothing new to index.`);
2670
2918
  finishSetup();
2671
2919
  }
2672
2920
  if (!opts.dryRun && !effectiveIngestOnly) {
2673
- log("─── Configuring MCP ───");
2674
- log("");
2675
- for (const line of configureMcp(apiKey, selectedTargets)) {
2676
- log(` ${line}`);
2677
- }
2678
- log("");
2921
+ const mcpSpinner = Y2();
2922
+ mcpSpinner.start("Configuring MCP...");
2923
+ const mcpLines = configureMcp(apiKey, selectedTargets);
2924
+ mcpSpinner.stop(`MCP: ${mcpLines.join(", ")}`);
2679
2925
  }
2680
2926
  if (postIngestIndexPath) {
2681
2927
  const { resolve: resolve2 } = await import("node:path");
2682
2928
  const absPath = resolve2(postIngestIndexPath);
2683
- log("─── Indexing codebase ───");
2684
- log("");
2685
- write("Scanning files... ");
2929
+ const idxSpinner = Y2();
2930
+ idxSpinner.start("Indexing codebase...");
2686
2931
  const { memories, fileCount, skipped } = indexCodebase(absPath);
2687
- log(`${fileCount} files found, ${skipped} skipped`);
2932
+ idxSpinner.stop(`Codebase: ${fileCount} files found, ${skipped} skipped`);
2688
2933
  if (memories.length === 0) {
2689
2934
  log(`
2690
2935
  Nothing new to index.`);
@@ -2705,24 +2950,17 @@ Nothing new to index.`);
2705
2950
  log("");
2706
2951
  }
2707
2952
  if (!opts.dryRun && !opts.quiet) {
2708
- log("─── Background Sync ───");
2709
- log("");
2953
+ const syncSpinner = Y2();
2954
+ syncSpinner.start("Setting up background sync...");
2710
2955
  try {
2711
- const syncMessages = installSync(apiKey);
2712
- for (const msg of syncMessages)
2713
- log(` ✓ ${msg}`);
2714
- log("");
2715
- log(" New chats will be auto-ingested every 10 minutes.");
2716
- log(" Logs: ~/.conare/ingest.log | Remove: conare --uninstall-sync");
2956
+ installSync(apiKey, opts.syncInterval);
2957
+ syncSpinner.stop(`Background sync active (every ${opts.syncInterval}min)`);
2717
2958
  } catch (e2) {
2718
- log(`Could not set up background sync: ${e2.message}`);
2719
- log(" Run manually later: conare --install-sync");
2959
+ syncSpinner.stop(`Could not set up background sync: ${e2.message}`);
2720
2960
  }
2721
- log("");
2722
2961
  }
2723
- log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
2724
- log(" Done! Every new conversation now starts with context.");
2725
- log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
2962
+ log("");
2963
+ log(" \x1B[32m✓\x1B[0m Done! Every new conversation now starts with context.");
2726
2964
  log("");
2727
2965
  }
2728
2966
  main().catch((e2) => {