conare 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +320 -126
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -285,6 +285,16 @@ import { createHash } from "node:crypto";
|
|
|
285
285
|
import { join as join2 } from "node:path";
|
|
286
286
|
import { homedir as homedir2 } from "node:os";
|
|
287
287
|
var MANIFEST_PATH = join2(homedir2(), ".conare", "ingested.json");
|
|
288
|
+
var MIN_SUBSTANTIVE = 200;
|
|
289
|
+
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))/;
|
|
290
|
+
function isNarration(text) {
|
|
291
|
+
const stripped = text.trim();
|
|
292
|
+
if (stripped.length < MIN_SUBSTANTIVE)
|
|
293
|
+
return true;
|
|
294
|
+
if (stripped.length <= 300 && NARRATION_RE.test(stripped))
|
|
295
|
+
return true;
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
288
298
|
function cleanText(raw) {
|
|
289
299
|
let text = raw;
|
|
290
300
|
text = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "");
|
|
@@ -344,8 +354,10 @@ function extractText(content) {
|
|
|
344
354
|
`);
|
|
345
355
|
}
|
|
346
356
|
function parseSession(lines) {
|
|
347
|
-
const
|
|
357
|
+
const rounds = [];
|
|
348
358
|
let date = null;
|
|
359
|
+
let currentUser = null;
|
|
360
|
+
let currentAssistant = [];
|
|
349
361
|
for (const line of lines) {
|
|
350
362
|
if (!line.trim())
|
|
351
363
|
continue;
|
|
@@ -357,20 +369,31 @@ function parseSession(lines) {
|
|
|
357
369
|
}
|
|
358
370
|
if (!date && obj.timestamp)
|
|
359
371
|
date = obj.timestamp.slice(0, 10);
|
|
360
|
-
if (obj.type === "user"
|
|
372
|
+
if (obj.type === "user") {
|
|
361
373
|
const text = cleanText(extractText(obj.message?.content));
|
|
362
374
|
if (text.length >= MIN_TURN_LEN) {
|
|
363
|
-
|
|
375
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
376
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
377
|
+
}
|
|
378
|
+
currentUser = text;
|
|
379
|
+
currentAssistant = [];
|
|
380
|
+
}
|
|
381
|
+
} else if (obj.type === "assistant") {
|
|
382
|
+
const text = cleanText(extractText(obj.message?.content));
|
|
383
|
+
if (text.length >= MIN_TURN_LEN) {
|
|
384
|
+
currentAssistant.push(text);
|
|
364
385
|
}
|
|
365
386
|
}
|
|
366
387
|
}
|
|
367
|
-
|
|
368
|
-
|
|
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++;
|
|
372
|
-
}
|
|
388
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
389
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
373
390
|
}
|
|
391
|
+
const turns = rounds.map((r) => ({
|
|
392
|
+
user: r.user,
|
|
393
|
+
assistant: r.assistantParts.filter((p) => !isNarration(p)).join(`
|
|
394
|
+
|
|
395
|
+
`)
|
|
396
|
+
})).filter((t) => t.assistant.length >= MIN_TURN_LEN);
|
|
374
397
|
return { turns, date };
|
|
375
398
|
}
|
|
376
399
|
function ingestClaude() {
|
|
@@ -567,9 +590,11 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
567
590
|
try {
|
|
568
591
|
const lines = readFileSync3(join4(dir, entry.name), "utf-8").split(`
|
|
569
592
|
`).filter(Boolean);
|
|
570
|
-
const turns = [];
|
|
571
593
|
let date = null;
|
|
572
594
|
let project = null;
|
|
595
|
+
const rounds = [];
|
|
596
|
+
let currentUser = null;
|
|
597
|
+
let currentAssistant = [];
|
|
573
598
|
for (const line of lines) {
|
|
574
599
|
try {
|
|
575
600
|
const obj = JSON.parse(line);
|
|
@@ -578,16 +603,33 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
578
603
|
if (obj.type === "session_meta" && obj.payload?.cwd) {
|
|
579
604
|
project = projectFromCwd(obj.payload.cwd);
|
|
580
605
|
}
|
|
581
|
-
if (obj.type
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
606
|
+
if (obj.type !== "response_item" || obj.payload?.type !== "message")
|
|
607
|
+
continue;
|
|
608
|
+
const role = obj.payload.role;
|
|
609
|
+
const msgContent = obj.payload.content;
|
|
610
|
+
if (!Array.isArray(msgContent))
|
|
611
|
+
continue;
|
|
612
|
+
if (role === "user") {
|
|
613
|
+
for (const block of msgContent) {
|
|
614
|
+
if (block.type === "input_text" && block.text) {
|
|
615
|
+
const text = cleanText(block.text);
|
|
616
|
+
if (isCodexBoilerplate(text))
|
|
617
|
+
continue;
|
|
618
|
+
if (text.length >= 50) {
|
|
619
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
620
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
621
|
+
}
|
|
622
|
+
currentUser = text;
|
|
623
|
+
currentAssistant = [];
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
} else if (role === "assistant") {
|
|
628
|
+
for (const block of msgContent) {
|
|
629
|
+
if (block.type === "output_text" && block.text) {
|
|
630
|
+
const text = cleanText(block.text);
|
|
631
|
+
if (!isNarration(text)) {
|
|
632
|
+
currentAssistant.push(text);
|
|
591
633
|
}
|
|
592
634
|
}
|
|
593
635
|
}
|
|
@@ -596,16 +638,27 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
596
638
|
continue;
|
|
597
639
|
}
|
|
598
640
|
}
|
|
599
|
-
if (
|
|
641
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
642
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
643
|
+
}
|
|
644
|
+
if (rounds.length === 0) {
|
|
600
645
|
stats.filtered++;
|
|
601
646
|
continue;
|
|
602
647
|
}
|
|
603
|
-
const body =
|
|
648
|
+
const body = rounds.map((r) => {
|
|
649
|
+
const q = r.user.length > 300 ? r.user.slice(0, 300) + "..." : r.user;
|
|
650
|
+
const assistant = r.assistantParts.join(`
|
|
651
|
+
|
|
652
|
+
`);
|
|
653
|
+
return `## Q: ${q}
|
|
654
|
+
|
|
655
|
+
${assistant}`;
|
|
656
|
+
}).join(`
|
|
604
657
|
|
|
605
658
|
---
|
|
606
659
|
|
|
607
660
|
`);
|
|
608
|
-
let content = `# Codex Session | ${date || "unknown"}
|
|
661
|
+
let content = `# Codex Session${project ? `: ${project}` : ""} | ${date || "unknown"}
|
|
609
662
|
|
|
610
663
|
${body}`;
|
|
611
664
|
if (content.length > MAX_CONTENT2)
|
|
@@ -1276,7 +1329,7 @@ function getSavedApiKey() {
|
|
|
1276
1329
|
}
|
|
1277
1330
|
|
|
1278
1331
|
// src/sync.ts
|
|
1279
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as readFileSync8, chmodSync, cpSync, rmSync } from "node:fs";
|
|
1332
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as readFileSync8, chmodSync, cpSync, rmSync, symlinkSync, readlinkSync, appendFileSync } from "node:fs";
|
|
1280
1333
|
import { join as join9, dirname as dirname2 } from "node:path";
|
|
1281
1334
|
import { homedir as homedir7, platform as platform2 } from "node:os";
|
|
1282
1335
|
import { execSync } from "node:child_process";
|
|
@@ -1351,7 +1404,7 @@ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) START sync" >> "$LOG"
|
|
|
1351
1404
|
2>> "$LOG"
|
|
1352
1405
|
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) DONE sync (exit $?)" >> "$LOG"
|
|
1353
1406
|
`;
|
|
1354
|
-
function makePlist() {
|
|
1407
|
+
function makePlist(intervalMinutes) {
|
|
1355
1408
|
const home = homedir7();
|
|
1356
1409
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1357
1410
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
@@ -1366,7 +1419,7 @@ function makePlist() {
|
|
|
1366
1419
|
<string>${home}/.conare/bin/run.sh</string>
|
|
1367
1420
|
</array>
|
|
1368
1421
|
<key>StartInterval</key>
|
|
1369
|
-
<integer
|
|
1422
|
+
<integer>${intervalMinutes * 60}</integer>
|
|
1370
1423
|
<key>StandardOutPath</key>
|
|
1371
1424
|
<string>${home}/.conare/ingest.log</string>
|
|
1372
1425
|
<key>StandardErrorPath</key>
|
|
@@ -1383,17 +1436,19 @@ Description=Conare Memory — background sync
|
|
|
1383
1436
|
Type=oneshot
|
|
1384
1437
|
ExecStart=%h/.conare/bin/run.sh
|
|
1385
1438
|
`;
|
|
1386
|
-
|
|
1387
|
-
|
|
1439
|
+
function makeSystemdTimer(intervalMinutes) {
|
|
1440
|
+
return `[Unit]
|
|
1441
|
+
Description=Conare Memory — sync every ${intervalMinutes} minutes
|
|
1388
1442
|
|
|
1389
1443
|
[Timer]
|
|
1390
1444
|
OnBootSec=2min
|
|
1391
|
-
OnUnitActiveSec
|
|
1445
|
+
OnUnitActiveSec=${intervalMinutes}min
|
|
1392
1446
|
Persistent=true
|
|
1393
1447
|
|
|
1394
1448
|
[Install]
|
|
1395
1449
|
WantedBy=timers.target
|
|
1396
1450
|
`;
|
|
1451
|
+
}
|
|
1397
1452
|
function hasSystemd() {
|
|
1398
1453
|
try {
|
|
1399
1454
|
execSync("systemctl --user status 2>/dev/null", { stdio: "ignore" });
|
|
@@ -1458,10 +1513,10 @@ function findSqlJs() {
|
|
|
1458
1513
|
}
|
|
1459
1514
|
return null;
|
|
1460
1515
|
}
|
|
1461
|
-
function setupMacOS() {
|
|
1516
|
+
function setupMacOS(intervalMinutes) {
|
|
1462
1517
|
const plistDir = dirname2(PLIST_PATH);
|
|
1463
1518
|
mkdirSync4(plistDir, { recursive: true });
|
|
1464
|
-
writeFileSync4(PLIST_PATH, makePlist());
|
|
1519
|
+
writeFileSync4(PLIST_PATH, makePlist(intervalMinutes));
|
|
1465
1520
|
const id = uid();
|
|
1466
1521
|
try {
|
|
1467
1522
|
execSync(`launchctl bootout gui/${id} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
@@ -1476,16 +1531,16 @@ function setupMacOS() {
|
|
|
1476
1531
|
}
|
|
1477
1532
|
}
|
|
1478
1533
|
}
|
|
1479
|
-
function setupLinuxSystemd() {
|
|
1534
|
+
function setupLinuxSystemd(intervalMinutes) {
|
|
1480
1535
|
mkdirSync4(SYSTEMD_DIR, { recursive: true });
|
|
1481
1536
|
writeFileSync4(SYSTEMD_SERVICE, SYSTEMD_SERVICE_CONTENT);
|
|
1482
|
-
writeFileSync4(SYSTEMD_TIMER,
|
|
1537
|
+
writeFileSync4(SYSTEMD_TIMER, makeSystemdTimer(intervalMinutes));
|
|
1483
1538
|
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
1484
1539
|
execSync("systemctl --user enable --now conare-sync.timer", { stdio: "ignore" });
|
|
1485
1540
|
}
|
|
1486
|
-
function setupLinuxCron() {
|
|
1541
|
+
function setupLinuxCron(intervalMinutes) {
|
|
1487
1542
|
const cronCmd = `${homedir7()}/.conare/bin/run.sh`;
|
|
1488
|
-
const cronLine =
|
|
1543
|
+
const cronLine = `*/${intervalMinutes} * * * * ${cronCmd}`;
|
|
1489
1544
|
try {
|
|
1490
1545
|
const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
1491
1546
|
const filtered = existing.split(`
|
|
@@ -1500,23 +1555,100 @@ function setupLinuxCron() {
|
|
|
1500
1555
|
`, stdio: ["pipe", "ignore", "ignore"] });
|
|
1501
1556
|
}
|
|
1502
1557
|
}
|
|
1503
|
-
function
|
|
1558
|
+
function installGlobalCommand() {
|
|
1559
|
+
const wrapper = join9(BIN_DIR, "conare");
|
|
1560
|
+
const content = `#!/bin/bash
|
|
1561
|
+
# Conare global command — runs the persisted CLI bundle
|
|
1562
|
+
CONARE_DIR="$HOME/.conare"
|
|
1563
|
+
NODE=$(command -v node || echo "")
|
|
1564
|
+
if [ -z "$NODE" ]; then
|
|
1565
|
+
if [ -s "$HOME/.nvm/nvm.sh" ]; then
|
|
1566
|
+
. "$HOME/.nvm/nvm.sh" >/dev/null 2>&1
|
|
1567
|
+
NODE=$(command -v node || echo "")
|
|
1568
|
+
fi
|
|
1569
|
+
fi
|
|
1570
|
+
if [ -z "$NODE" ]; then
|
|
1571
|
+
echo "Error: node not found. Install Node.js first." >&2
|
|
1572
|
+
exit 1
|
|
1573
|
+
fi
|
|
1574
|
+
exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
|
|
1575
|
+
`;
|
|
1576
|
+
writeFileSync4(wrapper, content);
|
|
1577
|
+
chmodSync(wrapper, 493);
|
|
1578
|
+
const symlinkTarget = "/usr/local/bin/conare";
|
|
1579
|
+
try {
|
|
1580
|
+
if (existsSync7(symlinkTarget)) {
|
|
1581
|
+
try {
|
|
1582
|
+
const existing = readlinkSync(symlinkTarget);
|
|
1583
|
+
if (existing === wrapper)
|
|
1584
|
+
return "Global command: conare (already linked)";
|
|
1585
|
+
} catch {}
|
|
1586
|
+
unlinkSync(symlinkTarget);
|
|
1587
|
+
}
|
|
1588
|
+
symlinkSync(wrapper, symlinkTarget);
|
|
1589
|
+
return "Global command: conare (linked to /usr/local/bin)";
|
|
1590
|
+
} catch {
|
|
1591
|
+
const pathDirs = (process.env.PATH || "").split(":");
|
|
1592
|
+
if (pathDirs.includes(BIN_DIR)) {
|
|
1593
|
+
return "Global command: conare (via ~/.conare/bin in PATH)";
|
|
1594
|
+
}
|
|
1595
|
+
const shellProfile = getShellProfile();
|
|
1596
|
+
if (shellProfile) {
|
|
1597
|
+
try {
|
|
1598
|
+
const profileContent = existsSync7(shellProfile) ? readFileSync8(shellProfile, "utf-8") : "";
|
|
1599
|
+
const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
|
|
1600
|
+
if (!profileContent.includes(".conare/bin")) {
|
|
1601
|
+
appendFileSync(shellProfile, `
|
|
1602
|
+
# Conare CLI
|
|
1603
|
+
${exportLine}
|
|
1604
|
+
`);
|
|
1605
|
+
return `Global command: conare (added ~/.conare/bin to ${shellProfile} — restart shell)`;
|
|
1606
|
+
}
|
|
1607
|
+
return "Global command: conare (via ~/.conare/bin in PATH)";
|
|
1608
|
+
} catch {
|
|
1609
|
+
return `Global command: add ~/.conare/bin to your PATH manually`;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
return `Global command: add ~/.conare/bin to your PATH manually`;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function getShellProfile() {
|
|
1616
|
+
const home = homedir7();
|
|
1617
|
+
const shell = process.env.SHELL || "";
|
|
1618
|
+
if (shell.includes("zsh"))
|
|
1619
|
+
return join9(home, ".zshrc");
|
|
1620
|
+
if (shell.includes("bash")) {
|
|
1621
|
+
const profile = join9(home, ".bash_profile");
|
|
1622
|
+
if (platform2() === "darwin" && existsSync7(profile))
|
|
1623
|
+
return profile;
|
|
1624
|
+
return join9(home, ".bashrc");
|
|
1625
|
+
}
|
|
1626
|
+
if (existsSync7(join9(home, ".zshrc")))
|
|
1627
|
+
return join9(home, ".zshrc");
|
|
1628
|
+
if (existsSync7(join9(home, ".bashrc")))
|
|
1629
|
+
return join9(home, ".bashrc");
|
|
1630
|
+
return null;
|
|
1631
|
+
}
|
|
1632
|
+
function installSync(apiKey, intervalMinutes = 10) {
|
|
1504
1633
|
const messages = [];
|
|
1505
1634
|
const os = platform2();
|
|
1506
1635
|
persistBinary(apiKey);
|
|
1507
1636
|
messages.push("Persisted CLI to ~/.conare/bin/");
|
|
1508
1637
|
messages.push("Saved config to ~/.conare/config.json");
|
|
1638
|
+
const globalMsg = installGlobalCommand();
|
|
1639
|
+
if (globalMsg)
|
|
1640
|
+
messages.push(globalMsg);
|
|
1509
1641
|
if (os === "darwin") {
|
|
1510
|
-
setupMacOS();
|
|
1511
|
-
messages.push(
|
|
1642
|
+
setupMacOS(intervalMinutes);
|
|
1643
|
+
messages.push(`Installed launchd agent (every ${intervalMinutes} min)`);
|
|
1512
1644
|
messages.push(`Plist: ${PLIST_PATH}`);
|
|
1513
1645
|
} else if (os === "linux") {
|
|
1514
1646
|
if (hasSystemd()) {
|
|
1515
|
-
setupLinuxSystemd();
|
|
1516
|
-
messages.push(
|
|
1647
|
+
setupLinuxSystemd(intervalMinutes);
|
|
1648
|
+
messages.push(`Installed systemd timer (every ${intervalMinutes} min)`);
|
|
1517
1649
|
} else {
|
|
1518
|
-
setupLinuxCron();
|
|
1519
|
-
messages.push(
|
|
1650
|
+
setupLinuxCron(intervalMinutes);
|
|
1651
|
+
messages.push(`Installed cron job (every ${intervalMinutes} min)`);
|
|
1520
1652
|
}
|
|
1521
1653
|
} else {
|
|
1522
1654
|
messages.push(`Unsupported platform: ${os}. Run manually: ~/.conare/bin/run.sh`);
|
|
@@ -1582,8 +1714,9 @@ import { stripVTControlCharacters as S2 } from "node:util";
|
|
|
1582
1714
|
|
|
1583
1715
|
// node_modules/@clack/core/dist/index.mjs
|
|
1584
1716
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
1585
|
-
import { stdin as j, stdout as M } from "node:process";
|
|
1586
1717
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
1718
|
+
import { stdin as j, stdout as M } from "node:process";
|
|
1719
|
+
import * as g from "node:readline";
|
|
1587
1720
|
import O from "node:readline";
|
|
1588
1721
|
import { Writable as X } from "node:stream";
|
|
1589
1722
|
function DD({ onlyFirst: e = false } = {}) {
|
|
@@ -1816,6 +1949,28 @@ function m(e, u) {
|
|
|
1816
1949
|
const t = e;
|
|
1817
1950
|
t.isTTY && t.setRawMode(u);
|
|
1818
1951
|
}
|
|
1952
|
+
function fD({ input: e = j, output: u = M, overwrite: t = true, hideCursor: F = true } = {}) {
|
|
1953
|
+
const s = g.createInterface({ input: e, output: u, prompt: "", tabSize: 1 });
|
|
1954
|
+
g.emitKeypressEvents(e, s), e.isTTY && e.setRawMode(true);
|
|
1955
|
+
const i = (D, { name: C, sequence: n }) => {
|
|
1956
|
+
const E = String(D);
|
|
1957
|
+
if ($([E, C, n], "cancel")) {
|
|
1958
|
+
F && u.write(import_sisteransi.cursor.show), process.exit(0);
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
if (!t)
|
|
1962
|
+
return;
|
|
1963
|
+
const a = C === "return" ? 0 : -1, o = C === "return" ? -1 : 0;
|
|
1964
|
+
g.moveCursor(u, a, o, () => {
|
|
1965
|
+
g.clearLine(u, 1, () => {
|
|
1966
|
+
e.once("keypress", i);
|
|
1967
|
+
});
|
|
1968
|
+
});
|
|
1969
|
+
};
|
|
1970
|
+
return F && u.write(import_sisteransi.cursor.hide), e.once("keypress", i), () => {
|
|
1971
|
+
e.off("keypress", i), F && u.write(import_sisteransi.cursor.show), e.isTTY && !AD && e.setRawMode(false), s.terminal = false, s.close();
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1819
1974
|
var gD = Object.defineProperty;
|
|
1820
1975
|
var vD = (e, u, t) => (u in e) ? gD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t;
|
|
1821
1976
|
var h = (e, u, t) => (vD(e, typeof u != "symbol" ? u + "" : u, t), t);
|
|
@@ -2041,9 +2196,9 @@ var G2 = (t) => {
|
|
|
2041
2196
|
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
2197
|
let l2 = 0;
|
|
2043
2198
|
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,
|
|
2199
|
+
const $2 = a < r2.length && l2 > 0, g2 = a < r2.length && l2 + a < r2.length;
|
|
2045
2200
|
return r2.slice(l2, l2 + a).map((p2, v2, f) => {
|
|
2046
|
-
const j2 = v2 === 0 && $2, E = v2 === f.length - 1 &&
|
|
2201
|
+
const j2 = v2 === 0 && $2, E = v2 === f.length - 1 && g2;
|
|
2047
2202
|
return j2 || E ? import_picocolors2.default.dim("...") : i(p2, v2 + l2 === n);
|
|
2048
2203
|
});
|
|
2049
2204
|
};
|
|
@@ -2159,6 +2314,57 @@ ${import_picocolors2.default.gray(d2)} ${t}
|
|
|
2159
2314
|
`);
|
|
2160
2315
|
};
|
|
2161
2316
|
var J = `${import_picocolors2.default.gray(o)} `;
|
|
2317
|
+
var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
2318
|
+
const n = V2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], r2 = V2 ? 80 : 120, i = process.env.CI === "true";
|
|
2319
|
+
let s, c, a = false, l2 = "", $2, g2 = performance.now();
|
|
2320
|
+
const p2 = (m2) => {
|
|
2321
|
+
const h2 = m2 > 1 ? "Something went wrong" : "Canceled";
|
|
2322
|
+
a && N2(h2, m2);
|
|
2323
|
+
}, v2 = () => p2(2), f = () => p2(1), j2 = () => {
|
|
2324
|
+
process.on("uncaughtExceptionMonitor", v2), process.on("unhandledRejection", v2), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p2);
|
|
2325
|
+
}, E = () => {
|
|
2326
|
+
process.removeListener("uncaughtExceptionMonitor", v2), process.removeListener("unhandledRejection", v2), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p2);
|
|
2327
|
+
}, B2 = () => {
|
|
2328
|
+
if ($2 === undefined)
|
|
2329
|
+
return;
|
|
2330
|
+
i && process.stdout.write(`
|
|
2331
|
+
`);
|
|
2332
|
+
const m2 = $2.split(`
|
|
2333
|
+
`);
|
|
2334
|
+
process.stdout.write(import_sisteransi2.cursor.move(-999, m2.length - 1)), process.stdout.write(import_sisteransi2.erase.down(m2.length));
|
|
2335
|
+
}, R2 = (m2) => m2.replace(/\.+$/, ""), O2 = (m2) => {
|
|
2336
|
+
const h2 = (performance.now() - m2) / 1000, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
|
|
2337
|
+
return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
|
|
2338
|
+
}, H2 = (m2 = "") => {
|
|
2339
|
+
a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
|
|
2340
|
+
`);
|
|
2341
|
+
let h2 = 0, w2 = 0;
|
|
2342
|
+
j2(), c = setInterval(() => {
|
|
2343
|
+
if (i && l2 === $2)
|
|
2344
|
+
return;
|
|
2345
|
+
B2(), $2 = l2;
|
|
2346
|
+
const I2 = import_picocolors2.default.magenta(n[h2]);
|
|
2347
|
+
if (i)
|
|
2348
|
+
process.stdout.write(`${I2} ${l2}...`);
|
|
2349
|
+
else if (t === "timer")
|
|
2350
|
+
process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
|
|
2351
|
+
else {
|
|
2352
|
+
const z2 = ".".repeat(Math.floor(w2)).slice(0, 3);
|
|
2353
|
+
process.stdout.write(`${I2} ${l2}${z2}`);
|
|
2354
|
+
}
|
|
2355
|
+
h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
|
|
2356
|
+
}, r2);
|
|
2357
|
+
}, N2 = (m2 = "", h2 = 0) => {
|
|
2358
|
+
a = false, clearInterval(c), B2();
|
|
2359
|
+
const w2 = h2 === 0 ? import_picocolors2.default.green(C) : h2 === 1 ? import_picocolors2.default.red(L2) : import_picocolors2.default.red(W2);
|
|
2360
|
+
l2 = R2(m2 ?? l2), t === "timer" ? process.stdout.write(`${w2} ${l2} ${O2(g2)}
|
|
2361
|
+
`) : process.stdout.write(`${w2} ${l2}
|
|
2362
|
+
`), E(), s();
|
|
2363
|
+
};
|
|
2364
|
+
return { start: H2, stop: N2, message: (m2 = "") => {
|
|
2365
|
+
l2 = R2(m2 ?? l2);
|
|
2366
|
+
} };
|
|
2367
|
+
};
|
|
2162
2368
|
|
|
2163
2369
|
// src/interactive.ts
|
|
2164
2370
|
function formatDetectedCount(count) {
|
|
@@ -2298,6 +2504,7 @@ function parseArgs() {
|
|
|
2298
2504
|
let quiet = false;
|
|
2299
2505
|
let installSyncFlag = false;
|
|
2300
2506
|
let uninstallSyncFlag = false;
|
|
2507
|
+
let syncInterval = 10;
|
|
2301
2508
|
let source;
|
|
2302
2509
|
let wasmDir;
|
|
2303
2510
|
let indexPath;
|
|
@@ -2316,6 +2523,8 @@ function parseArgs() {
|
|
|
2316
2523
|
installSyncFlag = true;
|
|
2317
2524
|
} else if (args[i] === "--uninstall-sync") {
|
|
2318
2525
|
uninstallSyncFlag = true;
|
|
2526
|
+
} else if (args[i] === "--sync-interval" && args[i + 1]) {
|
|
2527
|
+
syncInterval = parseInt(args[++i], 10);
|
|
2319
2528
|
} else if (args[i] === "--source" && args[i + 1]) {
|
|
2320
2529
|
source = args[++i];
|
|
2321
2530
|
} else if (args[i] === "--wasm-dir" && args[i + 1]) {
|
|
@@ -2345,6 +2554,7 @@ Options:
|
|
|
2345
2554
|
--quiet Suppress all stdout output (for background timer runs)
|
|
2346
2555
|
--install-sync Set up automatic background sync (every 10 min)
|
|
2347
2556
|
--uninstall-sync Remove background sync timer and persisted files
|
|
2557
|
+
--sync-interval <n> Sync interval in minutes (default: 10)
|
|
2348
2558
|
--ingest-only Ingest memories without MCP configuration
|
|
2349
2559
|
--config-only Configure MCP only, skip ingestion
|
|
2350
2560
|
--interactive Run guided setup prompts
|
|
@@ -2364,7 +2574,7 @@ Get your API key at https://mcp.conare.ai
|
|
|
2364
2574
|
console.error("Error: --ingest-only and --config-only cannot be used together");
|
|
2365
2575
|
process.exit(1);
|
|
2366
2576
|
}
|
|
2367
|
-
return { key, configFile, dryRun, force, ingestOnly, configOnly, interactive, quiet, installSync: installSyncFlag, uninstallSync: uninstallSyncFlag, source, wasmDir, indexPath };
|
|
2577
|
+
return { key, configFile, dryRun, force, ingestOnly, configOnly, interactive, quiet, installSync: installSyncFlag, uninstallSync: uninstallSyncFlag, syncInterval, source, wasmDir, indexPath };
|
|
2368
2578
|
}
|
|
2369
2579
|
async function main() {
|
|
2370
2580
|
const opts = parseArgs();
|
|
@@ -2435,38 +2645,25 @@ async function main() {
|
|
|
2435
2645
|
process.exit(1);
|
|
2436
2646
|
}
|
|
2437
2647
|
if (opts.installSync) {
|
|
2438
|
-
console.log("");
|
|
2439
|
-
console.log("Setting up background sync...");
|
|
2440
|
-
console.log("");
|
|
2441
2648
|
const auth2 = await validateKey(apiKey);
|
|
2442
2649
|
if (!auth2.valid) {
|
|
2443
|
-
console.error("
|
|
2650
|
+
console.error("Invalid API key. Check your key at https://mcp.conare.ai");
|
|
2444
2651
|
process.exit(1);
|
|
2445
2652
|
}
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
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");
|
|
2653
|
+
const syncSpinner = Y2();
|
|
2654
|
+
syncSpinner.start("Setting up background sync...");
|
|
2655
|
+
installSync(apiKey, opts.syncInterval);
|
|
2656
|
+
syncSpinner.stop(`Background sync active (every ${opts.syncInterval}min)`);
|
|
2453
2657
|
console.log("");
|
|
2454
2658
|
return;
|
|
2455
2659
|
}
|
|
2456
2660
|
log("");
|
|
2457
|
-
log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
2458
|
-
log(" Conare Installer");
|
|
2459
|
-
log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
2460
|
-
log("");
|
|
2461
|
-
write("Validating API key... ");
|
|
2462
2661
|
const auth = await validateKey(apiKey);
|
|
2463
2662
|
if (!auth.valid) {
|
|
2464
|
-
console.error("
|
|
2663
|
+
console.error("Invalid API key. Check your key at https://mcp.conare.ai");
|
|
2465
2664
|
process.exit(1);
|
|
2466
2665
|
}
|
|
2467
|
-
log(auth.email ? `OK (${auth.email})` : "OK");
|
|
2468
2666
|
saveApiKey(apiKey);
|
|
2469
|
-
log();
|
|
2470
2667
|
if (!opts.force && !opts.dryRun) {
|
|
2471
2668
|
const remoteMemoryCount = await getRemoteMemoryCount(apiKey);
|
|
2472
2669
|
const localManifest = getIngested();
|
|
@@ -2482,39 +2679,32 @@ async function main() {
|
|
|
2482
2679
|
}
|
|
2483
2680
|
if (effectiveConfigOnly) {
|
|
2484
2681
|
if (!opts.dryRun) {
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
}
|
|
2490
|
-
log("");
|
|
2682
|
+
const mcpSpinner = Y2();
|
|
2683
|
+
mcpSpinner.start("Configuring MCP...");
|
|
2684
|
+
const lines = configureMcp(apiKey, selectedTargets);
|
|
2685
|
+
mcpSpinner.stop(`MCP configured: ${lines.join(", ")}`);
|
|
2491
2686
|
}
|
|
2492
|
-
log("
|
|
2493
|
-
log("
|
|
2494
|
-
log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
2687
|
+
log("");
|
|
2688
|
+
log("Done! Conare MCP is configured.");
|
|
2495
2689
|
log("");
|
|
2496
2690
|
return;
|
|
2497
2691
|
}
|
|
2498
2692
|
if (effectiveIndexPath !== undefined) {
|
|
2499
2693
|
const { resolve: resolve2 } = await import("node:path");
|
|
2500
2694
|
const absPath = resolve2(effectiveIndexPath);
|
|
2501
|
-
log(`Indexing codebase: ${absPath}`);
|
|
2502
2695
|
if (opts.force) {
|
|
2503
2696
|
clearIngested("codebase");
|
|
2504
|
-
write("Clearing existing codebase index... ");
|
|
2505
2697
|
try {
|
|
2506
2698
|
await fetch("https://mcp.conare.ai/api/containers/codebase", {
|
|
2507
2699
|
method: "DELETE",
|
|
2508
2700
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
2509
2701
|
});
|
|
2510
|
-
|
|
2511
|
-
} catch {
|
|
2512
|
-
log("skipped (no existing index)");
|
|
2513
|
-
}
|
|
2702
|
+
} catch {}
|
|
2514
2703
|
}
|
|
2515
|
-
|
|
2704
|
+
const idxSpinner = Y2();
|
|
2705
|
+
idxSpinner.start("Scanning codebase...");
|
|
2516
2706
|
const { memories, fileCount, skipped } = indexCodebase(absPath);
|
|
2517
|
-
|
|
2707
|
+
idxSpinner.stop(`Codebase: ${fileCount} files found, ${skipped} skipped`);
|
|
2518
2708
|
if (memories.length === 0) {
|
|
2519
2709
|
log(`
|
|
2520
2710
|
Nothing new to index.`);
|
|
@@ -2541,14 +2731,12 @@ Nothing new to index.`);
|
|
|
2541
2731
|
if (!opts.quiet)
|
|
2542
2732
|
printFailureSummary(results, memories);
|
|
2543
2733
|
}
|
|
2544
|
-
log();
|
|
2734
|
+
log("");
|
|
2545
2735
|
if (!opts.dryRun && !effectiveIngestOnly) {
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
}
|
|
2551
|
-
log("");
|
|
2736
|
+
const mcpSpinner = Y2();
|
|
2737
|
+
mcpSpinner.start("Configuring MCP...");
|
|
2738
|
+
const mcpLines = configureMcp(apiKey, selectedTargets);
|
|
2739
|
+
mcpSpinner.stop(`MCP: ${mcpLines.join(", ")}`);
|
|
2552
2740
|
}
|
|
2553
2741
|
log("Done! Your codebase is now searchable via recall.");
|
|
2554
2742
|
return;
|
|
@@ -2578,23 +2766,39 @@ Nothing new to index.`);
|
|
|
2578
2766
|
const allMemories = [];
|
|
2579
2767
|
const shouldIngest = (name) => selectedSources.includes(name);
|
|
2580
2768
|
if (shouldIngest("claude") && tools.find((t) => t.name === "Claude Code")?.available) {
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2769
|
+
if (!opts.quiet) {
|
|
2770
|
+
const s = Y2();
|
|
2771
|
+
s.start("Scanning Claude Code chats...");
|
|
2772
|
+
const { memories, filtered, deduped } = ingestClaude();
|
|
2773
|
+
allMemories.push(...memories);
|
|
2774
|
+
s.stop(`Claude Code: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
|
|
2775
|
+
} else {
|
|
2776
|
+
allMemories.push(...ingestClaude().memories);
|
|
2777
|
+
}
|
|
2585
2778
|
}
|
|
2586
2779
|
if (shouldIngest("codex") && tools.find((t) => t.name === "Codex")?.available) {
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2780
|
+
if (!opts.quiet) {
|
|
2781
|
+
const s = Y2();
|
|
2782
|
+
s.start("Scanning Codex chats...");
|
|
2783
|
+
const { memories, filtered, deduped } = ingestCodex();
|
|
2784
|
+
allMemories.push(...memories);
|
|
2785
|
+
s.stop(`Codex: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
|
|
2786
|
+
} else {
|
|
2787
|
+
allMemories.push(...ingestCodex().memories);
|
|
2788
|
+
}
|
|
2591
2789
|
}
|
|
2592
2790
|
if (shouldIngest("cursor") && tools.find((t) => t.name === "Cursor")?.available) {
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2791
|
+
if (!opts.quiet) {
|
|
2792
|
+
const s = Y2();
|
|
2793
|
+
s.start("Scanning Cursor chats...");
|
|
2794
|
+
const cursorTool = tools.find((t) => t.name === "Cursor");
|
|
2795
|
+
const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir);
|
|
2796
|
+
allMemories.push(...memories);
|
|
2797
|
+
s.stop(`Cursor: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
|
|
2798
|
+
} else {
|
|
2799
|
+
const cursorTool = tools.find((t) => t.name === "Cursor");
|
|
2800
|
+
allMemories.push(...(await ingestCursor(cursorTool.path, opts.wasmDir)).memories);
|
|
2801
|
+
}
|
|
2598
2802
|
}
|
|
2599
2803
|
allMemories.sort((a, b3) => {
|
|
2600
2804
|
const da = a.metadata?.date || "0000";
|
|
@@ -2670,21 +2874,18 @@ Nothing new to index.`);
|
|
|
2670
2874
|
finishSetup();
|
|
2671
2875
|
}
|
|
2672
2876
|
if (!opts.dryRun && !effectiveIngestOnly) {
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
}
|
|
2678
|
-
log("");
|
|
2877
|
+
const mcpSpinner = Y2();
|
|
2878
|
+
mcpSpinner.start("Configuring MCP...");
|
|
2879
|
+
const mcpLines = configureMcp(apiKey, selectedTargets);
|
|
2880
|
+
mcpSpinner.stop(`MCP: ${mcpLines.join(", ")}`);
|
|
2679
2881
|
}
|
|
2680
2882
|
if (postIngestIndexPath) {
|
|
2681
2883
|
const { resolve: resolve2 } = await import("node:path");
|
|
2682
2884
|
const absPath = resolve2(postIngestIndexPath);
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
write("Scanning files... ");
|
|
2885
|
+
const idxSpinner = Y2();
|
|
2886
|
+
idxSpinner.start("Indexing codebase...");
|
|
2686
2887
|
const { memories, fileCount, skipped } = indexCodebase(absPath);
|
|
2687
|
-
|
|
2888
|
+
idxSpinner.stop(`Codebase: ${fileCount} files found, ${skipped} skipped`);
|
|
2688
2889
|
if (memories.length === 0) {
|
|
2689
2890
|
log(`
|
|
2690
2891
|
Nothing new to index.`);
|
|
@@ -2705,24 +2906,17 @@ Nothing new to index.`);
|
|
|
2705
2906
|
log("");
|
|
2706
2907
|
}
|
|
2707
2908
|
if (!opts.dryRun && !opts.quiet) {
|
|
2708
|
-
|
|
2709
|
-
|
|
2909
|
+
const syncSpinner = Y2();
|
|
2910
|
+
syncSpinner.start("Setting up background sync...");
|
|
2710
2911
|
try {
|
|
2711
|
-
|
|
2712
|
-
|
|
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");
|
|
2912
|
+
installSync(apiKey, opts.syncInterval);
|
|
2913
|
+
syncSpinner.stop(`Background sync active (every ${opts.syncInterval}min)`);
|
|
2717
2914
|
} catch (e2) {
|
|
2718
|
-
|
|
2719
|
-
log(" Run manually later: conare --install-sync");
|
|
2915
|
+
syncSpinner.stop(`Could not set up background sync: ${e2.message}`);
|
|
2720
2916
|
}
|
|
2721
|
-
log("");
|
|
2722
2917
|
}
|
|
2723
|
-
log("
|
|
2724
|
-
log("
|
|
2725
|
-
log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
2918
|
+
log("");
|
|
2919
|
+
log(" \x1B[32m✓\x1B[0m Done! Every new conversation now starts with context.");
|
|
2726
2920
|
log("");
|
|
2727
2921
|
}
|
|
2728
2922
|
main().catch((e2) => {
|