conare 0.3.5 → 0.3.6
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 +415 -77
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -114,7 +114,7 @@ __export(exports_codebase, {
|
|
|
114
114
|
import { createHash as createHash2 } from "node:crypto";
|
|
115
115
|
import { readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync2, existsSync as existsSync5 } from "node:fs";
|
|
116
116
|
import { join as join6, relative, extname, resolve, basename as basename3 } from "node:path";
|
|
117
|
-
import { execSync } from "node:child_process";
|
|
117
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
118
118
|
function parseGitignore(rootPath) {
|
|
119
119
|
const patterns = new Set;
|
|
120
120
|
const gitignorePath = join6(rootPath, ".gitignore");
|
|
@@ -180,12 +180,12 @@ function detectProjectName(rootPath) {
|
|
|
180
180
|
}
|
|
181
181
|
function getChangedFiles(rootPath) {
|
|
182
182
|
try {
|
|
183
|
-
const committed =
|
|
183
|
+
const committed = execSync2("git diff --name-only HEAD 2>/dev/null || true", {
|
|
184
184
|
cwd: rootPath,
|
|
185
185
|
encoding: "utf-8",
|
|
186
186
|
stdio: ["pipe", "pipe", "pipe"]
|
|
187
187
|
}).trim();
|
|
188
|
-
const staged =
|
|
188
|
+
const staged = execSync2("git diff --name-only --cached 2>/dev/null || true", {
|
|
189
189
|
cwd: rootPath,
|
|
190
190
|
encoding: "utf-8",
|
|
191
191
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -436,7 +436,7 @@ __export(exports_api, {
|
|
|
436
436
|
ApiError: () => ApiError
|
|
437
437
|
});
|
|
438
438
|
async function apiRequest(path, apiKey, init) {
|
|
439
|
-
const res = await fetch(`${
|
|
439
|
+
const res = await fetch(`${API_URL2}${path}`, {
|
|
440
440
|
...init,
|
|
441
441
|
headers: {
|
|
442
442
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -593,7 +593,7 @@ async function uploadBulk(apiKey, memories, onProgress) {
|
|
|
593
593
|
}
|
|
594
594
|
return { success, failed, results };
|
|
595
595
|
}
|
|
596
|
-
var
|
|
596
|
+
var API_URL2 = "https://mcp.conare.ai", ApiError, PAGE_SIZE = 200, DELETE_CONCURRENCY = 5;
|
|
597
597
|
var init_api = __esm(() => {
|
|
598
598
|
ApiError = class ApiError extends Error {
|
|
599
599
|
statusCode;
|
|
@@ -1425,6 +1425,7 @@ __export(exports_interactive, {
|
|
|
1425
1425
|
showDetectedApps: () => showDetectedApps,
|
|
1426
1426
|
selectMcpTargets: () => selectMcpTargets,
|
|
1427
1427
|
selectChatSources: () => selectChatSources,
|
|
1428
|
+
promptAuth: () => promptAuth,
|
|
1428
1429
|
promptApiKey: () => promptApiKey,
|
|
1429
1430
|
finishSetup: () => finishSetup,
|
|
1430
1431
|
confirmIndexCodebase: () => confirmIndexCodebase,
|
|
@@ -1470,6 +1471,54 @@ async function promptApiKey(options) {
|
|
|
1470
1471
|
});
|
|
1471
1472
|
return ensureValue(keyPrompt).trim() || options.savedApiKey;
|
|
1472
1473
|
}
|
|
1474
|
+
async function promptAuth(options) {
|
|
1475
|
+
if (options.providedApiKey) {
|
|
1476
|
+
return options.providedApiKey;
|
|
1477
|
+
}
|
|
1478
|
+
if (options.savedApiKey) {
|
|
1479
|
+
const useSaved = await ye({
|
|
1480
|
+
message: `Use saved API key ending in ${options.savedApiKey.slice(-6)}?`,
|
|
1481
|
+
initialValue: true
|
|
1482
|
+
});
|
|
1483
|
+
if (pD(useSaved)) {
|
|
1484
|
+
xe("Setup cancelled.");
|
|
1485
|
+
process.exit(0);
|
|
1486
|
+
}
|
|
1487
|
+
if (useSaved)
|
|
1488
|
+
return options.savedApiKey;
|
|
1489
|
+
}
|
|
1490
|
+
const method = await ve({
|
|
1491
|
+
message: "How would you like to authenticate?",
|
|
1492
|
+
options: [
|
|
1493
|
+
{ value: "browser", label: "Sign in with browser", hint: "recommended" },
|
|
1494
|
+
{ value: "manual", label: "Enter API key manually" }
|
|
1495
|
+
]
|
|
1496
|
+
});
|
|
1497
|
+
if (pD(method)) {
|
|
1498
|
+
xe("Setup cancelled.");
|
|
1499
|
+
process.exit(0);
|
|
1500
|
+
}
|
|
1501
|
+
if (method === "browser") {
|
|
1502
|
+
return "__BROWSER_AUTH__";
|
|
1503
|
+
}
|
|
1504
|
+
const keyPrompt = await ge({
|
|
1505
|
+
message: "API key",
|
|
1506
|
+
mask: "*",
|
|
1507
|
+
validate(value) {
|
|
1508
|
+
const resolved = value.trim();
|
|
1509
|
+
if (!resolved)
|
|
1510
|
+
return "Enter an API key from https://mcp.conare.ai";
|
|
1511
|
+
if (!resolved.startsWith("cmem_"))
|
|
1512
|
+
return "API keys start with cmem_";
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
if (pD(keyPrompt)) {
|
|
1517
|
+
xe("Setup cancelled.");
|
|
1518
|
+
process.exit(0);
|
|
1519
|
+
}
|
|
1520
|
+
return keyPrompt.trim();
|
|
1521
|
+
}
|
|
1473
1522
|
async function selectChatSources(targets) {
|
|
1474
1523
|
return ensureValue(await fe({
|
|
1475
1524
|
message: "Select chat sources",
|
|
@@ -1630,26 +1679,98 @@ async function detect() {
|
|
|
1630
1679
|
path: cursorDbPath,
|
|
1631
1680
|
sessionCount: existsSync(cursorDbPath) ? await countCursorSessions(cursorDbPath) : 0
|
|
1632
1681
|
});
|
|
1633
|
-
const openclawDir = join(home, ".openclaw");
|
|
1634
|
-
tools.push({
|
|
1635
|
-
name: "OpenClaw",
|
|
1636
|
-
available: existsSync(openclawDir),
|
|
1637
|
-
path: openclawDir,
|
|
1638
|
-
sessionCount: 0
|
|
1639
|
-
});
|
|
1640
1682
|
return tools;
|
|
1641
1683
|
}
|
|
1642
1684
|
|
|
1685
|
+
// src/auth.ts
|
|
1686
|
+
import { execSync } from "node:child_process";
|
|
1687
|
+
import { platform as platform2 } from "node:os";
|
|
1688
|
+
var API_URL = "https://mcp.conare.ai";
|
|
1689
|
+
async function browserAuth() {
|
|
1690
|
+
const stateBytes = new Uint8Array(16);
|
|
1691
|
+
crypto.getRandomValues(stateBytes);
|
|
1692
|
+
const state = Array.from(stateBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1693
|
+
const sessionRes = await fetch(`${API_URL}/api/auth/cli-session`, {
|
|
1694
|
+
method: "POST",
|
|
1695
|
+
headers: { "Content-Type": "application/json" },
|
|
1696
|
+
body: JSON.stringify({ state })
|
|
1697
|
+
});
|
|
1698
|
+
if (!sessionRes.ok) {
|
|
1699
|
+
throw new Error(`Failed to create auth session: HTTP ${sessionRes.status}`);
|
|
1700
|
+
}
|
|
1701
|
+
const { code, expiresAt } = await sessionRes.json();
|
|
1702
|
+
const authUrl = `${API_URL}/cli-auth?code=${code}&state=${state}`;
|
|
1703
|
+
const opened = openBrowser(authUrl);
|
|
1704
|
+
if (!opened) {
|
|
1705
|
+
console.log("");
|
|
1706
|
+
console.log(" Open this URL in your browser to sign in:");
|
|
1707
|
+
console.log("");
|
|
1708
|
+
console.log(` ${authUrl}`);
|
|
1709
|
+
console.log("");
|
|
1710
|
+
}
|
|
1711
|
+
const timeout = Math.max(expiresAt - Date.now(), 0);
|
|
1712
|
+
const deadline = Date.now() + timeout;
|
|
1713
|
+
while (Date.now() < deadline) {
|
|
1714
|
+
await sleep(2000);
|
|
1715
|
+
const exchangeRes = await fetch(`${API_URL}/api/auth/cli-exchange`, {
|
|
1716
|
+
method: "POST",
|
|
1717
|
+
headers: { "Content-Type": "application/json" },
|
|
1718
|
+
body: JSON.stringify({ code, state })
|
|
1719
|
+
});
|
|
1720
|
+
if (exchangeRes.status === 202) {
|
|
1721
|
+
continue;
|
|
1722
|
+
}
|
|
1723
|
+
if (exchangeRes.ok) {
|
|
1724
|
+
const data = await exchangeRes.json();
|
|
1725
|
+
return data.apiKey;
|
|
1726
|
+
}
|
|
1727
|
+
if (exchangeRes.status === 410) {
|
|
1728
|
+
throw new Error("Authentication code was already used. Please try again.");
|
|
1729
|
+
}
|
|
1730
|
+
if (exchangeRes.status === 404) {
|
|
1731
|
+
throw new Error("Authentication session expired. Please try again.");
|
|
1732
|
+
}
|
|
1733
|
+
throw new Error(`Authentication failed: HTTP ${exchangeRes.status}`);
|
|
1734
|
+
}
|
|
1735
|
+
throw new Error("Authentication timed out. Please try again.");
|
|
1736
|
+
}
|
|
1737
|
+
function openBrowser(url) {
|
|
1738
|
+
try {
|
|
1739
|
+
const os = platform2();
|
|
1740
|
+
if (os === "darwin") {
|
|
1741
|
+
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
1742
|
+
} else if (os === "win32") {
|
|
1743
|
+
execSync(`start "" "${url}"`, { stdio: "ignore" });
|
|
1744
|
+
} else {
|
|
1745
|
+
try {
|
|
1746
|
+
execSync(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
1747
|
+
} catch {
|
|
1748
|
+
try {
|
|
1749
|
+
execSync(`wslview "${url}"`, { stdio: "ignore" });
|
|
1750
|
+
} catch {
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
return true;
|
|
1756
|
+
} catch {
|
|
1757
|
+
return false;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
function sleep(ms) {
|
|
1761
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1643
1764
|
// src/ingest/claude.ts
|
|
1644
1765
|
init_shared();
|
|
1645
1766
|
import { readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
|
|
1646
1767
|
import { join as join3, basename } from "node:path";
|
|
1647
|
-
import { homedir as homedir3, platform as
|
|
1768
|
+
import { homedir as homedir3, platform as platform3 } from "node:os";
|
|
1648
1769
|
var MAX_CONTENT = 48000;
|
|
1649
1770
|
var MIN_TURN_LEN = 50;
|
|
1650
1771
|
function resolveProjectName(dirName) {
|
|
1651
1772
|
const segments = dirName.replace(/^-/, "").split("-");
|
|
1652
|
-
const isWindows =
|
|
1773
|
+
const isWindows = platform3() === "win32";
|
|
1653
1774
|
let resolved;
|
|
1654
1775
|
let startIdx;
|
|
1655
1776
|
if (isWindows && segments.length > 0 && /^[A-Za-z]$/.test(segments[0])) {
|
|
@@ -1735,6 +1856,22 @@ function parseSession(lines) {
|
|
|
1735
1856
|
})).filter((t) => t.assistant.length >= MIN_TURN_LEN);
|
|
1736
1857
|
return { turns, date };
|
|
1737
1858
|
}
|
|
1859
|
+
function getParentUuid(lines) {
|
|
1860
|
+
for (const line of lines) {
|
|
1861
|
+
if (!line.trim())
|
|
1862
|
+
continue;
|
|
1863
|
+
let obj;
|
|
1864
|
+
try {
|
|
1865
|
+
obj = JSON.parse(line);
|
|
1866
|
+
} catch {
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
if (obj && typeof obj === "object" && "parentUuid" in obj) {
|
|
1870
|
+
return obj.parentUuid;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1738
1875
|
function ingestClaude() {
|
|
1739
1876
|
const projectsDir = join3(homedir3(), ".claude", "projects");
|
|
1740
1877
|
const memories = [];
|
|
@@ -1759,8 +1896,14 @@ function ingestClaude() {
|
|
|
1759
1896
|
for (const file of files) {
|
|
1760
1897
|
const sessionId = basename(file, ".jsonl");
|
|
1761
1898
|
const raw = readFileSync3(join3(projPath, file), "utf-8");
|
|
1762
|
-
const
|
|
1763
|
-
`)
|
|
1899
|
+
const lines = raw.split(`
|
|
1900
|
+
`);
|
|
1901
|
+
const parentUuid = getParentUuid(lines);
|
|
1902
|
+
if (parentUuid != null) {
|
|
1903
|
+
filtered++;
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
const { turns, date } = parseSession(lines);
|
|
1764
1907
|
if (turns.length === 0) {
|
|
1765
1908
|
filtered++;
|
|
1766
1909
|
continue;
|
|
@@ -2139,15 +2282,14 @@ init_api();
|
|
|
2139
2282
|
// src/configure.ts
|
|
2140
2283
|
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2, symlinkSync, readlinkSync, rmSync } from "node:fs";
|
|
2141
2284
|
import { dirname, join as join7 } from "node:path";
|
|
2142
|
-
import { homedir as homedir5, platform as
|
|
2285
|
+
import { homedir as homedir5, platform as platform5 } from "node:os";
|
|
2143
2286
|
import { spawnSync } from "node:child_process";
|
|
2144
2287
|
var CONARE_URL = "https://mcp.conare.ai";
|
|
2145
|
-
var SERVER_NAME = "conare
|
|
2288
|
+
var SERVER_NAME = "conare";
|
|
2146
2289
|
var MCP_TARGETS = [
|
|
2147
2290
|
{ id: "claude", label: "Claude Code" },
|
|
2148
2291
|
{ id: "cursor", label: "Cursor" },
|
|
2149
|
-
{ id: "codex", label: "Codex" }
|
|
2150
|
-
{ id: "openclaw", label: "OpenClaw" }
|
|
2292
|
+
{ id: "codex", label: "Codex" }
|
|
2151
2293
|
];
|
|
2152
2294
|
function readJsonFile(path) {
|
|
2153
2295
|
try {
|
|
@@ -2183,7 +2325,7 @@ function configureClaude(apiKey) {
|
|
|
2183
2325
|
const claudeMcpPath = join7(homedir5(), ".claude", "mcp.json");
|
|
2184
2326
|
if (spawnSync("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
|
|
2185
2327
|
stdio: "ignore",
|
|
2186
|
-
shell:
|
|
2328
|
+
shell: platform5() === "win32"
|
|
2187
2329
|
}).status === 0) {
|
|
2188
2330
|
return "Claude Code configured via `claude mcp add-json`";
|
|
2189
2331
|
}
|
|
@@ -2198,41 +2340,166 @@ function configureJsonClient(path, apiKey, label) {
|
|
|
2198
2340
|
return `${label} configured at ${path}`;
|
|
2199
2341
|
}
|
|
2200
2342
|
var SKILL_MD = `---
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2343
|
+
name: conare
|
|
2344
|
+
description: Complements the Conare MCP server by teaching the agent when and how to load prior project context, search past sessions, save durable preferences, list stored memories, and forget saved items. Use when the user asks what they worked on before, wants prior context loaded at the start of a task, asks to remember or forget something, or needs past conversations, decisions, or code recalled from memory.
|
|
2345
|
+
compatibility: Requires the Conare MCP server tools (\`recall\`, \`search\`, \`save\`, \`list\`, \`forget\`) to be installed and connected.
|
|
2346
|
+
metadata:
|
|
2347
|
+
author: Conare
|
|
2348
|
+
version: 1.1.0
|
|
2349
|
+
mcp-server: conare
|
|
2204
2350
|
homepage: https://conare.ai
|
|
2205
2351
|
---
|
|
2206
2352
|
|
|
2207
|
-
# Conare
|
|
2353
|
+
# Conare
|
|
2354
|
+
|
|
2355
|
+
This skill complements the Conare MCP server. The MCP provides memory tools and live access to stored memories; this skill teaches the agent the default workflow, tool-selection rules, and query patterns that make those tools reliable.
|
|
2208
2356
|
|
|
2209
|
-
|
|
2357
|
+
## Primary Use Cases
|
|
2358
|
+
|
|
2359
|
+
1. Start a new coding task with relevant history already loaded through \`recall\`.
|
|
2360
|
+
2. Answer questions about prior work, decisions, bugs, architecture, or preferences through \`search\`.
|
|
2361
|
+
3. Persist durable information the user wants carried into future sessions through \`save\`.
|
|
2210
2362
|
|
|
2211
2363
|
## When To Use Each Tool
|
|
2212
2364
|
|
|
2213
2365
|
| Situation | Tool | Example |
|
|
2214
2366
|
|-----------|------|---------|
|
|
2215
2367
|
| Start of conversation | \`recall\` | Always call first with conversation context |
|
|
2216
|
-
| User asks about past work | \`
|
|
2217
|
-
| User says "remember this" | \`
|
|
2218
|
-
| User says "forget this" | \`
|
|
2219
|
-
| Browse what's stored | \`
|
|
2368
|
+
| User asks about past work | \`search\` | "What did we do last week?" |
|
|
2369
|
+
| User says "remember this" | \`save\` | Save preferences, rules, decisions |
|
|
2370
|
+
| User says "forget this" | \`forget\` | Remove a specific memory |
|
|
2371
|
+
| Browse what's stored | \`list\` | "Show me recent memories" |
|
|
2220
2372
|
|
|
2221
2373
|
## Critical Rules
|
|
2222
2374
|
|
|
2223
2375
|
1. **Always call \`recall\` at conversation start** — pass a specific description of the conversation topic, not generic text
|
|
2224
|
-
2. **\`
|
|
2376
|
+
2. **\`search\` is global** — it searches ALL memories across all projects. Use it when the user asks about specific topics or past conversations
|
|
2225
2377
|
3. **\`recall\` is scoped** — it returns project-relevant context + recent sessions + preferences. Use the \`project\` param when available
|
|
2226
2378
|
4. **Write descriptive queries** — "how does the billing webhook handle refunds" beats "billing"
|
|
2227
2379
|
5. **Rephrase and retry** — if first search misses, try different phrasing. Semantic search responds to synonyms and related concepts
|
|
2228
2380
|
|
|
2381
|
+
## Workflow
|
|
2382
|
+
|
|
2383
|
+
### Step 1: Load context at the start
|
|
2384
|
+
|
|
2385
|
+
- Call \`recall\` at conversation start with a specific description of the current task.
|
|
2386
|
+
- Include the \`project\` parameter when the project is known or inferable from the workspace.
|
|
2387
|
+
- Use the returned context to avoid re-asking for things the user already told the agent in earlier sessions.
|
|
2388
|
+
|
|
2389
|
+
Expected outcome: the agent begins with recent sessions, saved preferences, and project-relevant memory already in context.
|
|
2390
|
+
|
|
2391
|
+
### Step 2: Search when the user asks about prior work
|
|
2392
|
+
|
|
2393
|
+
- Use \`search\` for questions about past conversations, earlier implementations, prior bugs, design decisions, or work done in a time range.
|
|
2394
|
+
- Start with a descriptive natural-language query.
|
|
2395
|
+
- If results are weak, retry with 2-3 rephrasings from different angles.
|
|
2396
|
+
|
|
2397
|
+
Expected outcome: the agent can cite or summarize the most relevant prior work without broad manual browsing.
|
|
2398
|
+
|
|
2399
|
+
### Step 3: Save durable facts intentionally
|
|
2400
|
+
|
|
2401
|
+
- Use \`save\` proactively for information that should persist across sessions: preferences, standing rules, important decisions, long-lived project facts, and user-specific context that will help in future work.
|
|
2402
|
+
- When the user shares durable context or says to remember something, prefer capturing it with \`save\` so future \`recall\` calls can surface it automatically.
|
|
2403
|
+
- Avoid cluttering memory with purely transient scratch notes unless the user explicitly wants them remembered.
|
|
2404
|
+
|
|
2405
|
+
Expected outcome: future \`recall\` calls surface the information automatically when relevant.
|
|
2406
|
+
|
|
2229
2407
|
## Search Tips
|
|
2230
2408
|
|
|
2231
2409
|
- Use \`after\`/\`before\` (Unix ms) for time-scoped searches: "what did we work on this week?"
|
|
2232
2410
|
- Use \`containerTag\` to filter by source: \`claude-chats\`, \`codex-chats\`, \`cursor-chats\`, \`codebase\`, \`preferences\`
|
|
2233
2411
|
- Use \`project\` for cross-project filtering (partial names work: "conare" matches "fun/conare-memory-engine")
|
|
2234
2412
|
- Keep \`limit\` low (3-5) for focused results, higher (10-15) for broad exploration
|
|
2235
|
-
- When user asks to "remember" something, save it with \`
|
|
2413
|
+
- When user asks to "remember" something, save it with \`save\` — it goes to the \`preferences\` container and gets surfaced by \`recall\` in future sessions
|
|
2414
|
+
|
|
2415
|
+
## Examples
|
|
2416
|
+
|
|
2417
|
+
### Example 1: Start with context
|
|
2418
|
+
|
|
2419
|
+
User says: "Help me continue the OAuth migration in this repo."
|
|
2420
|
+
|
|
2421
|
+
Actions:
|
|
2422
|
+
1. Call \`recall\` with context like "continue OAuth migration in the current repository".
|
|
2423
|
+
2. Review returned memories for prior migration decisions, unresolved blockers, and saved preferences.
|
|
2424
|
+
3. Proceed with implementation using that context.
|
|
2425
|
+
|
|
2426
|
+
Result: the agent starts with the relevant project history instead of asking the user to restate it.
|
|
2427
|
+
|
|
2428
|
+
### Example 2: Find prior work
|
|
2429
|
+
|
|
2430
|
+
User says: "What did we decide last week about billing webhooks?"
|
|
2431
|
+
|
|
2432
|
+
Actions:
|
|
2433
|
+
1. Call \`search\` with a descriptive query such as "billing webhook decision refunds retries last week".
|
|
2434
|
+
2. If results are weak, retry with alternatives like "refund webhook handling" or "billing retry policy".
|
|
2435
|
+
3. Summarize the decision and note uncertainty if memories conflict.
|
|
2436
|
+
|
|
2437
|
+
Result: the agent retrieves prior decisions from memory rather than guessing.
|
|
2438
|
+
|
|
2439
|
+
### Example 3: Save a durable preference
|
|
2440
|
+
|
|
2441
|
+
User says: "Remember that I prefer ripgrep over grep."
|
|
2442
|
+
|
|
2443
|
+
Actions:
|
|
2444
|
+
1. Call \`save\` with the preference in durable wording.
|
|
2445
|
+
2. Confirm the preference was saved.
|
|
2446
|
+
|
|
2447
|
+
Result: future \`recall\` results can surface that preference automatically.
|
|
2448
|
+
|
|
2449
|
+
## Troubleshooting
|
|
2450
|
+
|
|
2451
|
+
### Weak or irrelevant search results
|
|
2452
|
+
|
|
2453
|
+
Cause:
|
|
2454
|
+
- Query is too short or too generic.
|
|
2455
|
+
- Search needs a different phrasing or time scope.
|
|
2456
|
+
|
|
2457
|
+
Response:
|
|
2458
|
+
1. Rewrite the query with concrete nouns, entities, and actions.
|
|
2459
|
+
2. Add \`after\`/\`before\` when the user implies a time window.
|
|
2460
|
+
3. Add \`project\` or \`containerTag\` when the scope is known.
|
|
2461
|
+
|
|
2462
|
+
### \`recall\` returns little useful context
|
|
2463
|
+
|
|
2464
|
+
Cause:
|
|
2465
|
+
- Conversation context passed to \`recall\` was too vague.
|
|
2466
|
+
- The project parameter was omitted when it should have been included.
|
|
2467
|
+
|
|
2468
|
+
Response:
|
|
2469
|
+
1. Retry \`recall\` with a more specific task description.
|
|
2470
|
+
2. Include \`project\` if the workspace or repository is known.
|
|
2471
|
+
3. Fall back to \`search\` for the exact topic the user cares about.
|
|
2472
|
+
|
|
2473
|
+
### Unsure whether to save something
|
|
2474
|
+
|
|
2475
|
+
Cause:
|
|
2476
|
+
- The information may be temporary rather than durable.
|
|
2477
|
+
|
|
2478
|
+
Response:
|
|
2479
|
+
1. Save it when it is likely to help in future sessions, especially if it reflects a preference, rule, decision, or durable project fact.
|
|
2480
|
+
2. Skip only clearly transient implementation notes unless the user explicitly asks to retain them.
|
|
2481
|
+
|
|
2482
|
+
## Triggering Tests
|
|
2483
|
+
|
|
2484
|
+
Use these tests to validate whether the skill description is tuned correctly.
|
|
2485
|
+
|
|
2486
|
+
Should trigger:
|
|
2487
|
+
- "What did we work on in this repo last week?"
|
|
2488
|
+
- "Load context for the auth refactor before we continue."
|
|
2489
|
+
- "Remember that I prefer pnpm in JavaScript projects."
|
|
2490
|
+
- "Forget the note about using staging Stripe keys."
|
|
2491
|
+
- "Search my past sessions for the DO migration fix."
|
|
2492
|
+
|
|
2493
|
+
Should not trigger:
|
|
2494
|
+
- "Write a sorting function in TypeScript."
|
|
2495
|
+
- "What is the weather in San Francisco?"
|
|
2496
|
+
- "Explain how PostgreSQL indexes work."
|
|
2497
|
+
- "Create a landing page from this mockup."
|
|
2498
|
+
|
|
2499
|
+
Success criteria:
|
|
2500
|
+
- Triggers on memory-oriented requests and start-of-task context loading.
|
|
2501
|
+
- Does not trigger on generic coding or research tasks with no memory component.
|
|
2502
|
+
- Uses \`recall\` first for new task continuation, and uses \`search\` only when the user asks about specific past work.
|
|
2236
2503
|
|
|
2237
2504
|
## Setup
|
|
2238
2505
|
|
|
@@ -2247,11 +2514,11 @@ The wizard handles everything: account creation, API key, MCP configuration, bac
|
|
|
2247
2514
|
For manual setup, visit [conare.ai](https://conare.ai).
|
|
2248
2515
|
`;
|
|
2249
2516
|
function installSkill() {
|
|
2250
|
-
const skillDir = join7(homedir5(), ".agents", "skills", "conare
|
|
2517
|
+
const skillDir = join7(homedir5(), ".agents", "skills", "conare");
|
|
2251
2518
|
mkdirSync2(skillDir, { recursive: true });
|
|
2252
2519
|
writeFileSync2(join7(skillDir, "SKILL.md"), SKILL_MD);
|
|
2253
2520
|
const claudeSkillsDir = join7(homedir5(), ".claude", "skills");
|
|
2254
|
-
const claudeSkillDir = join7(claudeSkillsDir, "conare
|
|
2521
|
+
const claudeSkillDir = join7(claudeSkillsDir, "conare");
|
|
2255
2522
|
try {
|
|
2256
2523
|
if (existsSync6(claudeSkillsDir)) {
|
|
2257
2524
|
if (existsSync6(claudeSkillDir)) {
|
|
@@ -2277,9 +2544,6 @@ function configureMcp(apiKey, targets = ["claude", "cursor", "codex"]) {
|
|
|
2277
2544
|
if (targets.includes("codex")) {
|
|
2278
2545
|
results.push(configureJsonClient(join7(homedir5(), ".codex", "mcp.json"), apiKey, "Codex"));
|
|
2279
2546
|
}
|
|
2280
|
-
if (targets.includes("openclaw")) {
|
|
2281
|
-
results.push(configureJsonClient(join7(homedir5(), ".openclaw", "mcp.json"), apiKey, "OpenClaw"));
|
|
2282
|
-
}
|
|
2283
2547
|
try {
|
|
2284
2548
|
results.push(installSkill());
|
|
2285
2549
|
} catch {}
|
|
@@ -2301,20 +2565,35 @@ function readConfig() {
|
|
|
2301
2565
|
return {};
|
|
2302
2566
|
}
|
|
2303
2567
|
}
|
|
2568
|
+
function writeConfig(config) {
|
|
2569
|
+
mkdirSync3(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
2570
|
+
writeFileSync3(CONFIG_PATH, JSON.stringify(config, null, 2) + `
|
|
2571
|
+
`, { mode: 384 });
|
|
2572
|
+
}
|
|
2304
2573
|
function saveApiKey(apiKey) {
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2574
|
+
const config = readConfig();
|
|
2575
|
+
config.apiKey = apiKey;
|
|
2576
|
+
writeConfig(config);
|
|
2308
2577
|
}
|
|
2309
2578
|
function getSavedApiKey() {
|
|
2310
2579
|
return readConfig().apiKey;
|
|
2311
2580
|
}
|
|
2581
|
+
function addIndexedPath(absPath) {
|
|
2582
|
+
const config = readConfig();
|
|
2583
|
+
const paths = new Set(config.indexedPaths || []);
|
|
2584
|
+
paths.add(absPath);
|
|
2585
|
+
config.indexedPaths = [...paths];
|
|
2586
|
+
writeConfig(config);
|
|
2587
|
+
}
|
|
2588
|
+
function getIndexedPaths() {
|
|
2589
|
+
return readConfig().indexedPaths || [];
|
|
2590
|
+
}
|
|
2312
2591
|
|
|
2313
2592
|
// src/sync.ts
|
|
2314
2593
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as readFileSync9, chmodSync, cpSync, rmSync as rmSync2, symlinkSync as symlinkSync2, readlinkSync as readlinkSync2, appendFileSync } from "node:fs";
|
|
2315
2594
|
import { join as join9, dirname as dirname2 } from "node:path";
|
|
2316
|
-
import { homedir as homedir7, platform as
|
|
2317
|
-
import { execSync as
|
|
2595
|
+
import { homedir as homedir7, platform as platform6 } from "node:os";
|
|
2596
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
2318
2597
|
var CONARE_DIR = join9(homedir7(), ".conare");
|
|
2319
2598
|
var BIN_DIR = join9(CONARE_DIR, "bin");
|
|
2320
2599
|
var CONFIG_PATH2 = join9(CONARE_DIR, "config.json");
|
|
@@ -2470,7 +2749,7 @@ WantedBy=timers.target
|
|
|
2470
2749
|
}
|
|
2471
2750
|
function hasSystemd() {
|
|
2472
2751
|
try {
|
|
2473
|
-
|
|
2752
|
+
execSync3("systemctl --user status 2>/dev/null", { stdio: "ignore" });
|
|
2474
2753
|
return true;
|
|
2475
2754
|
} catch {
|
|
2476
2755
|
return false;
|
|
@@ -2478,7 +2757,7 @@ function hasSystemd() {
|
|
|
2478
2757
|
}
|
|
2479
2758
|
function uid() {
|
|
2480
2759
|
try {
|
|
2481
|
-
return
|
|
2760
|
+
return execSync3("id -u", { encoding: "utf-8" }).trim();
|
|
2482
2761
|
} catch {
|
|
2483
2762
|
return "501";
|
|
2484
2763
|
}
|
|
@@ -2510,7 +2789,7 @@ function persistBinary(apiKey) {
|
|
|
2510
2789
|
const runVbsPath = join9(BIN_DIR, "run.vbs");
|
|
2511
2790
|
writeFileSync4(runVbsPath, RUN_VBS);
|
|
2512
2791
|
writeFileSync4(CONFIG_PATH2, JSON.stringify({ apiKey }, null, 2) + `
|
|
2513
|
-
|
|
2792
|
+
`, { mode: 384 });
|
|
2514
2793
|
}
|
|
2515
2794
|
function isValidJsBundle(path) {
|
|
2516
2795
|
if (!existsSync8(path))
|
|
@@ -2555,13 +2834,13 @@ function setupMacOS(intervalMinutes) {
|
|
|
2555
2834
|
writeFileSync4(PLIST_PATH, makePlist(intervalMinutes));
|
|
2556
2835
|
const id = uid();
|
|
2557
2836
|
try {
|
|
2558
|
-
|
|
2837
|
+
execSync3(`launchctl bootout gui/${id} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
2559
2838
|
} catch {}
|
|
2560
2839
|
try {
|
|
2561
|
-
|
|
2840
|
+
execSync3(`launchctl bootstrap gui/${id} "${PLIST_PATH}"`, { stdio: "ignore" });
|
|
2562
2841
|
} catch {
|
|
2563
2842
|
try {
|
|
2564
|
-
|
|
2843
|
+
execSync3(`launchctl load "${PLIST_PATH}"`, { stdio: "ignore" });
|
|
2565
2844
|
} catch {
|
|
2566
2845
|
throw new Error("Failed to load launchd agent. Try manually: launchctl load " + PLIST_PATH);
|
|
2567
2846
|
}
|
|
@@ -2571,28 +2850,28 @@ function setupLinuxSystemd(intervalMinutes) {
|
|
|
2571
2850
|
mkdirSync4(SYSTEMD_DIR, { recursive: true });
|
|
2572
2851
|
writeFileSync4(SYSTEMD_SERVICE, SYSTEMD_SERVICE_CONTENT);
|
|
2573
2852
|
writeFileSync4(SYSTEMD_TIMER, makeSystemdTimer(intervalMinutes));
|
|
2574
|
-
|
|
2575
|
-
|
|
2853
|
+
execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
2854
|
+
execSync3("systemctl --user enable --now conare-sync.timer", { stdio: "ignore" });
|
|
2576
2855
|
}
|
|
2577
2856
|
function setupLinuxCron(intervalMinutes) {
|
|
2578
2857
|
const cronCmd = `${homedir7()}/.conare/bin/run.sh`;
|
|
2579
2858
|
const cronLine = `*/${intervalMinutes} * * * * ${cronCmd}`;
|
|
2580
2859
|
try {
|
|
2581
|
-
const existing =
|
|
2860
|
+
const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
2582
2861
|
const filtered = existing.split(`
|
|
2583
2862
|
`).filter((l) => !l.includes("conare")).join(`
|
|
2584
2863
|
`);
|
|
2585
2864
|
const newCrontab = (filtered.trim() ? filtered.trim() + `
|
|
2586
2865
|
` : "") + cronLine + `
|
|
2587
2866
|
`;
|
|
2588
|
-
|
|
2867
|
+
execSync3("crontab -", { input: newCrontab, stdio: ["pipe", "ignore", "ignore"] });
|
|
2589
2868
|
} catch {
|
|
2590
|
-
|
|
2869
|
+
execSync3("crontab -", { input: cronLine + `
|
|
2591
2870
|
`, stdio: ["pipe", "ignore", "ignore"] });
|
|
2592
2871
|
}
|
|
2593
2872
|
}
|
|
2594
2873
|
function installGlobalCommand() {
|
|
2595
|
-
const isWindows =
|
|
2874
|
+
const isWindows = platform6() === "win32";
|
|
2596
2875
|
if (isWindows) {
|
|
2597
2876
|
const wrapper2 = join9(BIN_DIR, "conare.cmd");
|
|
2598
2877
|
const content2 = `@echo off\r
|
|
@@ -2610,7 +2889,7 @@ node "%USERPROFILE%\\.conare\\bin\\conare-ingest.mjs" %*\r
|
|
|
2610
2889
|
}
|
|
2611
2890
|
const binDirWin = BIN_DIR.replace(/\//g, "\\");
|
|
2612
2891
|
try {
|
|
2613
|
-
|
|
2892
|
+
execSync3(`powershell -NoProfile -Command "$p = [Environment]::GetEnvironmentVariable('PATH','User'); if ($p -and $p.ToLower().Contains('${binDirWin.toLowerCase().replace(/\\/g, "\\\\")}')) { exit 0 }; [Environment]::SetEnvironmentVariable('PATH', $(if($p){$p + ';'} else {''}) + '${binDirWin.replace(/\\/g, "\\\\")}', 'User')"`, { stdio: "ignore" });
|
|
2614
2893
|
return `Global command: conare (added .conare\\bin to user PATH — restart terminal)`;
|
|
2615
2894
|
} catch {
|
|
2616
2895
|
return `Global command: add ${binDirWin} to your PATH manually`;
|
|
@@ -2679,7 +2958,7 @@ function getShellProfile() {
|
|
|
2679
2958
|
return join9(home, ".zshrc");
|
|
2680
2959
|
if (shell.includes("bash")) {
|
|
2681
2960
|
const profile = join9(home, ".bash_profile");
|
|
2682
|
-
if (
|
|
2961
|
+
if (platform6() === "darwin" && existsSync8(profile))
|
|
2683
2962
|
return profile;
|
|
2684
2963
|
return join9(home, ".bashrc");
|
|
2685
2964
|
}
|
|
@@ -2692,9 +2971,9 @@ function getShellProfile() {
|
|
|
2692
2971
|
function setupWindows(intervalMinutes) {
|
|
2693
2972
|
const runVbs = join9(BIN_DIR, "run.vbs").replace(/\//g, "\\");
|
|
2694
2973
|
try {
|
|
2695
|
-
|
|
2974
|
+
execSync3(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
|
|
2696
2975
|
} catch {}
|
|
2697
|
-
|
|
2976
|
+
execSync3(`schtasks /Create /TN "${TASK_NAME}" /TR "wscript.exe \\"${runVbs}\\"" /SC MINUTE /MO ${intervalMinutes} /F`, { stdio: "ignore" });
|
|
2698
2977
|
}
|
|
2699
2978
|
function persistAndInstallGlobal(apiKey) {
|
|
2700
2979
|
const messages = [];
|
|
@@ -2708,7 +2987,7 @@ function persistAndInstallGlobal(apiKey) {
|
|
|
2708
2987
|
}
|
|
2709
2988
|
function installSync(apiKey, intervalMinutes = 10) {
|
|
2710
2989
|
const messages = persistAndInstallGlobal(apiKey);
|
|
2711
|
-
const os =
|
|
2990
|
+
const os = platform6();
|
|
2712
2991
|
if (os === "darwin") {
|
|
2713
2992
|
setupMacOS(intervalMinutes);
|
|
2714
2993
|
messages.push(`Installed launchd agent (every ${intervalMinutes} min)`);
|
|
@@ -2731,10 +3010,10 @@ function installSync(apiKey, intervalMinutes = 10) {
|
|
|
2731
3010
|
}
|
|
2732
3011
|
function uninstallSync() {
|
|
2733
3012
|
const messages = [];
|
|
2734
|
-
const os =
|
|
3013
|
+
const os = platform6();
|
|
2735
3014
|
if (os === "darwin") {
|
|
2736
3015
|
try {
|
|
2737
|
-
|
|
3016
|
+
execSync3(`launchctl bootout gui/${uid()} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
2738
3017
|
} catch {}
|
|
2739
3018
|
if (existsSync8(PLIST_PATH)) {
|
|
2740
3019
|
unlinkSync(PLIST_PATH);
|
|
@@ -2742,29 +3021,29 @@ function uninstallSync() {
|
|
|
2742
3021
|
}
|
|
2743
3022
|
} else if (os === "win32") {
|
|
2744
3023
|
try {
|
|
2745
|
-
|
|
3024
|
+
execSync3(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
|
|
2746
3025
|
messages.push("Removed Windows scheduled task");
|
|
2747
3026
|
} catch {}
|
|
2748
3027
|
} else if (os === "linux") {
|
|
2749
3028
|
if (hasSystemd()) {
|
|
2750
3029
|
try {
|
|
2751
|
-
|
|
3030
|
+
execSync3("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
|
|
2752
3031
|
} catch {}
|
|
2753
3032
|
if (existsSync8(SYSTEMD_SERVICE))
|
|
2754
3033
|
unlinkSync(SYSTEMD_SERVICE);
|
|
2755
3034
|
if (existsSync8(SYSTEMD_TIMER))
|
|
2756
3035
|
unlinkSync(SYSTEMD_TIMER);
|
|
2757
3036
|
try {
|
|
2758
|
-
|
|
3037
|
+
execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
2759
3038
|
} catch {}
|
|
2760
3039
|
messages.push("Removed systemd timer");
|
|
2761
3040
|
} else {
|
|
2762
3041
|
try {
|
|
2763
|
-
const existing =
|
|
3042
|
+
const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
2764
3043
|
const filtered = existing.split(`
|
|
2765
3044
|
`).filter((l) => !l.includes("conare")).join(`
|
|
2766
3045
|
`);
|
|
2767
|
-
|
|
3046
|
+
execSync3("crontab -", { input: filtered.trim() + `
|
|
2768
3047
|
`, stdio: ["pipe", "ignore", "ignore"] });
|
|
2769
3048
|
messages.push("Removed cron job");
|
|
2770
3049
|
} catch {}
|
|
@@ -2899,13 +3178,13 @@ function parseArgs() {
|
|
|
2899
3178
|
conare — AI memory for your coding tools
|
|
2900
3179
|
|
|
2901
3180
|
Usage:
|
|
2902
|
-
conare
|
|
2903
|
-
conare install
|
|
2904
|
-
conare --key <api_key> Ingest chat history
|
|
3181
|
+
conare Interactive setup with browser auth
|
|
3182
|
+
conare install Just install the MCP
|
|
3183
|
+
conare --key <api_key> Ingest chat history (key optional with browser auth)
|
|
2905
3184
|
conare --key <api_key> --index [path] Index codebase
|
|
2906
3185
|
|
|
2907
3186
|
Options:
|
|
2908
|
-
--key <key> Your Conare API key (
|
|
3187
|
+
--key <key> Your Conare API key (optional if using browser auth, starts with cmem_)
|
|
2909
3188
|
--config-file <path> Read API key from JSON config file (e.g. ~/.conare/config.json)
|
|
2910
3189
|
--index [path] Index codebase at path (default: current directory)
|
|
2911
3190
|
--project <name> Project name for codebase indexing (auto-detected if omitted)
|
|
@@ -2949,8 +3228,22 @@ async function runInstall() {
|
|
|
2949
3228
|
let apiKey = key || process.env.CONARE_API_KEY || savedApiKey;
|
|
2950
3229
|
const hasTty = !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
2951
3230
|
if (!apiKey && hasTty) {
|
|
2952
|
-
const {
|
|
2953
|
-
|
|
3231
|
+
const { promptAuth: promptAuthFn } = await Promise.resolve().then(() => (init_interactive(), exports_interactive));
|
|
3232
|
+
const { spinner: clackSpinner } = await Promise.resolve().then(() => (init_interactive(), exports_interactive));
|
|
3233
|
+
const authResult = await promptAuthFn({ savedApiKey });
|
|
3234
|
+
if (authResult === "__BROWSER_AUTH__") {
|
|
3235
|
+
const authSpinner = clackSpinner();
|
|
3236
|
+
authSpinner.start("Waiting for browser sign-in...");
|
|
3237
|
+
try {
|
|
3238
|
+
apiKey = await browserAuth();
|
|
3239
|
+
authSpinner.stop("Signed in successfully.");
|
|
3240
|
+
} catch (e2) {
|
|
3241
|
+
authSpinner.stop(`Authentication failed: ${e2.message}`);
|
|
3242
|
+
process.exit(1);
|
|
3243
|
+
}
|
|
3244
|
+
} else {
|
|
3245
|
+
apiKey = authResult || "";
|
|
3246
|
+
}
|
|
2954
3247
|
}
|
|
2955
3248
|
if (!apiKey) {
|
|
2956
3249
|
printMissingKeyError();
|
|
@@ -3013,7 +3306,7 @@ async function main() {
|
|
|
3013
3306
|
if (shouldRunInteractive) {
|
|
3014
3307
|
const detectedTools = await detect();
|
|
3015
3308
|
interactiveTargets = MCP_TARGETS.map((target) => {
|
|
3016
|
-
const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex"
|
|
3309
|
+
const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex");
|
|
3017
3310
|
return {
|
|
3018
3311
|
id: target.id,
|
|
3019
3312
|
label: target.label,
|
|
@@ -3023,10 +3316,23 @@ async function main() {
|
|
|
3023
3316
|
};
|
|
3024
3317
|
});
|
|
3025
3318
|
startSetup();
|
|
3026
|
-
|
|
3319
|
+
const authResult = await promptAuth({
|
|
3027
3320
|
savedApiKey,
|
|
3028
3321
|
providedApiKey: opts.key
|
|
3029
|
-
})
|
|
3322
|
+
});
|
|
3323
|
+
if (authResult === "__BROWSER_AUTH__") {
|
|
3324
|
+
const authSpinner = Y2();
|
|
3325
|
+
authSpinner.start("Waiting for browser sign-in...");
|
|
3326
|
+
try {
|
|
3327
|
+
apiKey = await browserAuth();
|
|
3328
|
+
authSpinner.stop("Signed in successfully.");
|
|
3329
|
+
} catch (e2) {
|
|
3330
|
+
authSpinner.stop(`Authentication failed: ${e2.message}`);
|
|
3331
|
+
process.exit(1);
|
|
3332
|
+
}
|
|
3333
|
+
} else {
|
|
3334
|
+
apiKey = authResult || apiKey;
|
|
3335
|
+
}
|
|
3030
3336
|
showDetectedApps(interactiveTargets);
|
|
3031
3337
|
selectedSources = await selectChatSources(interactiveTargets);
|
|
3032
3338
|
interactiveMode = true;
|
|
@@ -3038,8 +3344,20 @@ async function main() {
|
|
|
3038
3344
|
return;
|
|
3039
3345
|
}
|
|
3040
3346
|
if (!apiKey) {
|
|
3041
|
-
|
|
3042
|
-
|
|
3347
|
+
if (hasTty) {
|
|
3348
|
+
const authSpinner = Y2();
|
|
3349
|
+
authSpinner.start("Waiting for browser sign-in...");
|
|
3350
|
+
try {
|
|
3351
|
+
apiKey = await browserAuth();
|
|
3352
|
+
authSpinner.stop("Signed in successfully.");
|
|
3353
|
+
} catch (e2) {
|
|
3354
|
+
authSpinner.stop(`Authentication failed: ${e2.message}`);
|
|
3355
|
+
process.exit(1);
|
|
3356
|
+
}
|
|
3357
|
+
} else {
|
|
3358
|
+
printMissingKeyError();
|
|
3359
|
+
process.exit(1);
|
|
3360
|
+
}
|
|
3043
3361
|
}
|
|
3044
3362
|
if (opts.installSync) {
|
|
3045
3363
|
const auth2 = await validateKey(apiKey);
|
|
@@ -3162,6 +3480,7 @@ Nothing new to index.`);
|
|
|
3162
3480
|
write(renderProgressSummary(success, failed, "indexed"));
|
|
3163
3481
|
const fileHashes = results.filter((result) => result.success).map((result) => getManifestFingerprint(memories[result.index])).filter((key) => !!key);
|
|
3164
3482
|
markIngested("codebase", fileHashes);
|
|
3483
|
+
addIndexedPath(absPath);
|
|
3165
3484
|
if (!opts.quiet)
|
|
3166
3485
|
printFailureSummary(results, memories);
|
|
3167
3486
|
}
|
|
@@ -3293,6 +3612,24 @@ Nothing new to index.`);
|
|
|
3293
3612
|
}
|
|
3294
3613
|
}
|
|
3295
3614
|
log();
|
|
3615
|
+
if (effectiveIngestOnly && !opts.dryRun) {
|
|
3616
|
+
const savedPaths = getIndexedPaths();
|
|
3617
|
+
for (const savedPath of savedPaths) {
|
|
3618
|
+
if (!existsSync9(savedPath))
|
|
3619
|
+
continue;
|
|
3620
|
+
try {
|
|
3621
|
+
const { memories: codeMemories, project } = indexCodebase(savedPath, { changedOnly: true });
|
|
3622
|
+
if (codeMemories.length === 0)
|
|
3623
|
+
continue;
|
|
3624
|
+
log(` Re-indexing ${project}: ${codeMemories.length} changed files`);
|
|
3625
|
+
const { success, failed, results } = await uploadBulk(apiKey, codeMemories, () => {});
|
|
3626
|
+
if (!opts.quiet)
|
|
3627
|
+
log(` ${project}: ${success} indexed, ${failed} failed`);
|
|
3628
|
+
const fileHashes = results.filter((r2) => r2.success).map((r2) => getManifestFingerprint(codeMemories[r2.index])).filter((k3) => !!k3);
|
|
3629
|
+
markIngested("codebase", fileHashes);
|
|
3630
|
+
} catch {}
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3296
3633
|
if (interactiveMode) {
|
|
3297
3634
|
selectedTargets = await selectMcpTargets(interactiveTargets);
|
|
3298
3635
|
const shouldIndexCurrentCodebase = await confirmIndexCodebase();
|
|
@@ -3336,6 +3673,7 @@ Nothing new to index.`);
|
|
|
3336
3673
|
write(renderProgressSummary(success, failed, "indexed"));
|
|
3337
3674
|
const fileHashes = results.filter((result) => result.success).map((result) => getManifestFingerprint(memories[result.index])).filter((key) => !!key);
|
|
3338
3675
|
markIngested("codebase", fileHashes);
|
|
3676
|
+
addIndexedPath(absPath);
|
|
3339
3677
|
if (!opts.quiet)
|
|
3340
3678
|
printFailureSummary(results, memories);
|
|
3341
3679
|
}
|