conare 0.0.1 → 0.0.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 +318 -14
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
|
+
// src/index.ts
|
|
6
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
7
|
+
import { join as join9 } from "node:path";
|
|
8
|
+
|
|
5
9
|
// src/detect.ts
|
|
6
10
|
import { existsSync, readdirSync } from "node:fs";
|
|
7
11
|
import { join } from "node:path";
|
|
@@ -876,16 +880,201 @@ async function uploadBulk(apiKey, memories, onProgress) {
|
|
|
876
880
|
return { success, failed, results };
|
|
877
881
|
}
|
|
878
882
|
|
|
883
|
+
// src/configure.ts
|
|
884
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
|
|
885
|
+
import { dirname, join as join7 } from "node:path";
|
|
886
|
+
import { homedir as homedir5 } from "node:os";
|
|
887
|
+
import { spawnSync } from "node:child_process";
|
|
888
|
+
var CONARE_URL = "https://mcp.conare.ai";
|
|
889
|
+
var SERVER_NAME = "conare-memory";
|
|
890
|
+
var MCP_TARGETS = [
|
|
891
|
+
{ id: "claude", label: "Claude Code" },
|
|
892
|
+
{ id: "cursor", label: "Cursor" },
|
|
893
|
+
{ id: "codex", label: "Codex" }
|
|
894
|
+
];
|
|
895
|
+
function readJsonFile(path) {
|
|
896
|
+
try {
|
|
897
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
898
|
+
} catch {
|
|
899
|
+
return {};
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
function writeJsonFile(path, data) {
|
|
903
|
+
mkdirSync2(dirname(path), { recursive: true });
|
|
904
|
+
writeFileSync2(path, JSON.stringify(data, null, 2) + `
|
|
905
|
+
`);
|
|
906
|
+
}
|
|
907
|
+
function getServerConfig(apiKey) {
|
|
908
|
+
return {
|
|
909
|
+
type: "http",
|
|
910
|
+
url: `${CONARE_URL}/mcp`,
|
|
911
|
+
headers: {
|
|
912
|
+
Authorization: `Bearer ${apiKey}`
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function upsertMcpServer(path, apiKey) {
|
|
917
|
+
const config = readJsonFile(path);
|
|
918
|
+
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
919
|
+
config.mcpServers = {};
|
|
920
|
+
}
|
|
921
|
+
config.mcpServers[SERVER_NAME] = getServerConfig(apiKey);
|
|
922
|
+
writeJsonFile(path, config);
|
|
923
|
+
}
|
|
924
|
+
function configureClaude(apiKey) {
|
|
925
|
+
const claudeConfigPath = join7(homedir5(), ".claude.json");
|
|
926
|
+
const claudeMcpPath = join7(homedir5(), ".claude", "mcp.json");
|
|
927
|
+
if (spawnSync("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
|
|
928
|
+
stdio: "ignore"
|
|
929
|
+
}).status === 0) {
|
|
930
|
+
return "Claude Code configured via `claude mcp add-json`";
|
|
931
|
+
}
|
|
932
|
+
upsertMcpServer(claudeConfigPath, apiKey);
|
|
933
|
+
if (existsSync5(join7(homedir5(), ".claude"))) {
|
|
934
|
+
upsertMcpServer(claudeMcpPath, apiKey);
|
|
935
|
+
}
|
|
936
|
+
return `Claude Code configured at ${claudeConfigPath}`;
|
|
937
|
+
}
|
|
938
|
+
function configureJsonClient(path, apiKey, label) {
|
|
939
|
+
upsertMcpServer(path, apiKey);
|
|
940
|
+
return `${label} configured at ${path}`;
|
|
941
|
+
}
|
|
942
|
+
function configureMcp(apiKey, targets = ["claude", "cursor", "codex"]) {
|
|
943
|
+
const results = [];
|
|
944
|
+
if (targets.includes("claude")) {
|
|
945
|
+
results.push(configureClaude(apiKey));
|
|
946
|
+
}
|
|
947
|
+
if (targets.includes("cursor")) {
|
|
948
|
+
results.push(configureJsonClient(join7(homedir5(), ".cursor", "mcp.json"), apiKey, "Cursor"));
|
|
949
|
+
}
|
|
950
|
+
if (targets.includes("codex")) {
|
|
951
|
+
results.push(configureJsonClient(join7(homedir5(), ".codex", "mcp.json"), apiKey, "Codex"));
|
|
952
|
+
}
|
|
953
|
+
return results;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// src/config.ts
|
|
957
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
|
|
958
|
+
import { join as join8 } from "node:path";
|
|
959
|
+
import { homedir as homedir6 } from "node:os";
|
|
960
|
+
var CONFIG_DIR = join8(homedir6(), ".conare");
|
|
961
|
+
var CONFIG_PATH = join8(CONFIG_DIR, "config.json");
|
|
962
|
+
function readConfig() {
|
|
963
|
+
try {
|
|
964
|
+
if (!existsSync6(CONFIG_PATH))
|
|
965
|
+
return {};
|
|
966
|
+
return JSON.parse(readFileSync7(CONFIG_PATH, "utf-8"));
|
|
967
|
+
} catch {
|
|
968
|
+
return {};
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function saveApiKey(apiKey) {
|
|
972
|
+
mkdirSync3(CONFIG_DIR, { recursive: true });
|
|
973
|
+
writeFileSync3(CONFIG_PATH, JSON.stringify({ apiKey }, null, 2) + `
|
|
974
|
+
`);
|
|
975
|
+
}
|
|
976
|
+
function getSavedApiKey() {
|
|
977
|
+
return readConfig().apiKey;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// src/interactive.ts
|
|
981
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
982
|
+
import { createInterface } from "node:readline/promises";
|
|
983
|
+
function normalizeYesNo(value, defaultValue) {
|
|
984
|
+
const normalized = value.trim().toLowerCase();
|
|
985
|
+
if (!normalized)
|
|
986
|
+
return defaultValue;
|
|
987
|
+
if (["y", "yes"].includes(normalized))
|
|
988
|
+
return true;
|
|
989
|
+
if (["n", "no"].includes(normalized))
|
|
990
|
+
return false;
|
|
991
|
+
return defaultValue;
|
|
992
|
+
}
|
|
993
|
+
async function runInteractiveSetup(options) {
|
|
994
|
+
const rl = createInterface({ input, output });
|
|
995
|
+
try {
|
|
996
|
+
output.write(`
|
|
997
|
+
Conare setup
|
|
998
|
+
|
|
999
|
+
`);
|
|
1000
|
+
const apiKeyPrompt = options.savedApiKey ? `API key [press enter to use saved key ending in ${options.savedApiKey.slice(-6)}]: ` : "API key: ";
|
|
1001
|
+
const apiKeyAnswer = await rl.question(apiKeyPrompt);
|
|
1002
|
+
const apiKey = apiKeyAnswer.trim() || options.savedApiKey;
|
|
1003
|
+
output.write(`
|
|
1004
|
+
Clients to configure:
|
|
1005
|
+
`);
|
|
1006
|
+
options.detectedTargets.forEach((target, index) => {
|
|
1007
|
+
const availability = target.available === false ? " (not detected locally)" : "";
|
|
1008
|
+
const recommendation = target.recommended ? " [default]" : "";
|
|
1009
|
+
output.write(` ${index + 1}. ${target.label}${recommendation}${availability}
|
|
1010
|
+
`);
|
|
1011
|
+
});
|
|
1012
|
+
const defaultSelection = options.detectedTargets.map((target, index) => target.recommended ? String(index + 1) : null).filter((value) => !!value).join(",");
|
|
1013
|
+
const targetAnswer = await rl.question(`Select clients [${defaultSelection || "none"}]: `);
|
|
1014
|
+
const selectedIndexes = (targetAnswer.trim() || defaultSelection).split(",").map((part) => parseInt(part.trim(), 10)).filter((value) => Number.isFinite(value) && value >= 1 && value <= options.detectedTargets.length);
|
|
1015
|
+
const configureTargets = [...new Set(selectedIndexes)].map((index) => options.detectedTargets[index - 1]?.id).filter((value) => !!value);
|
|
1016
|
+
const ingestChats = normalizeYesNo(await rl.question("Ingest chat history? [Y/n]: "), true);
|
|
1017
|
+
const indexCodebase2 = normalizeYesNo(await rl.question("Index current codebase too? [y/N]: "), false);
|
|
1018
|
+
let indexPath;
|
|
1019
|
+
if (indexCodebase2) {
|
|
1020
|
+
const pathAnswer = await rl.question("Codebase path [.]: ");
|
|
1021
|
+
indexPath = pathAnswer.trim() || ".";
|
|
1022
|
+
}
|
|
1023
|
+
output.write(`
|
|
1024
|
+
`);
|
|
1025
|
+
return {
|
|
1026
|
+
apiKey,
|
|
1027
|
+
configureTargets,
|
|
1028
|
+
ingestChats,
|
|
1029
|
+
indexCodebase: indexCodebase2,
|
|
1030
|
+
indexPath
|
|
1031
|
+
};
|
|
1032
|
+
} finally {
|
|
1033
|
+
rl.close();
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
879
1037
|
// src/index.ts
|
|
880
1038
|
function getDedupKey(memory) {
|
|
881
1039
|
const metadata = memory.metadata;
|
|
882
1040
|
return metadata?.sessionId || metadata?.fileHash || null;
|
|
883
1041
|
}
|
|
1042
|
+
function printMissingKeyError() {
|
|
1043
|
+
console.error("Error: no API key configured.");
|
|
1044
|
+
console.error("");
|
|
1045
|
+
console.error("Run one of these:");
|
|
1046
|
+
console.error(" bunx conare@latest --key YOUR_API_KEY");
|
|
1047
|
+
console.error(" npx conare@latest --key YOUR_API_KEY");
|
|
1048
|
+
console.error("");
|
|
1049
|
+
console.error("For a built-in command, install globally:");
|
|
1050
|
+
console.error(" bun add -g conare");
|
|
1051
|
+
console.error(" npm i -g conare");
|
|
1052
|
+
console.error("");
|
|
1053
|
+
console.error("Get your API key at https://mcp.conare.ai");
|
|
1054
|
+
}
|
|
1055
|
+
function printFailureSummary(results, memories) {
|
|
1056
|
+
const failures = results.map((result, index) => ({ ...result, memory: memories[index] })).filter((result) => !result.success);
|
|
1057
|
+
if (failures.length === 0)
|
|
1058
|
+
return;
|
|
1059
|
+
console.log("");
|
|
1060
|
+
console.log(" Failure summary:");
|
|
1061
|
+
for (const failure of failures.slice(0, 3)) {
|
|
1062
|
+
const metadata = failure.memory.metadata;
|
|
1063
|
+
const label = metadata?.filePath || metadata?.sessionId || failure.memory.containerTag;
|
|
1064
|
+
console.log(` - ${label}: ${failure.error || "Upload failed"}`);
|
|
1065
|
+
}
|
|
1066
|
+
if (failures.length > 3) {
|
|
1067
|
+
console.log(` - ... and ${failures.length - 3} more`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
884
1070
|
function parseArgs() {
|
|
885
1071
|
const args = process.argv.slice(2);
|
|
886
1072
|
let key = "";
|
|
887
1073
|
let dryRun = false;
|
|
888
1074
|
let force = false;
|
|
1075
|
+
let ingestOnly = false;
|
|
1076
|
+
let configOnly = false;
|
|
1077
|
+
let interactive = false;
|
|
889
1078
|
let source;
|
|
890
1079
|
let wasmDir;
|
|
891
1080
|
let indexPath;
|
|
@@ -902,7 +1091,13 @@ function parseArgs() {
|
|
|
902
1091
|
wasmDir = args[++i];
|
|
903
1092
|
} else if (args[i] === "--index") {
|
|
904
1093
|
indexPath = args[i + 1] && !args[i + 1].startsWith("--") ? args[++i] : ".";
|
|
905
|
-
} else if (args[i] === "--ingest-only") {
|
|
1094
|
+
} else if (args[i] === "--ingest-only") {
|
|
1095
|
+
ingestOnly = true;
|
|
1096
|
+
} else if (args[i] === "--config-only") {
|
|
1097
|
+
configOnly = true;
|
|
1098
|
+
} else if (args[i] === "--interactive") {
|
|
1099
|
+
interactive = true;
|
|
1100
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
906
1101
|
console.log(`
|
|
907
1102
|
conare — Ingest AI chat history into Conare
|
|
908
1103
|
|
|
@@ -915,6 +1110,9 @@ Options:
|
|
|
915
1110
|
--index [path] Index codebase at path (default: current directory)
|
|
916
1111
|
--dry-run Preview what would be uploaded
|
|
917
1112
|
--force Re-ingest all / re-index all (bypass dedup)
|
|
1113
|
+
--ingest-only Ingest memories without MCP configuration
|
|
1114
|
+
--config-only Configure MCP only, skip ingestion
|
|
1115
|
+
--interactive Run guided setup prompts
|
|
918
1116
|
--source <name> Only ingest from: claude, codex, cursor
|
|
919
1117
|
--wasm-dir <path> Path to sql.js module (for Cursor ingestion)
|
|
920
1118
|
|
|
@@ -923,29 +1121,86 @@ Get your API key at https://mcp.conare.ai
|
|
|
923
1121
|
process.exit(0);
|
|
924
1122
|
}
|
|
925
1123
|
}
|
|
926
|
-
if (!key) {
|
|
927
|
-
console.error("Error:
|
|
1124
|
+
if (key && !key.startsWith("cmem_")) {
|
|
1125
|
+
console.error("Error: API key must start with cmem_");
|
|
928
1126
|
process.exit(1);
|
|
929
1127
|
}
|
|
930
|
-
if (
|
|
931
|
-
console.error("Error:
|
|
1128
|
+
if (ingestOnly && configOnly) {
|
|
1129
|
+
console.error("Error: --ingest-only and --config-only cannot be used together");
|
|
932
1130
|
process.exit(1);
|
|
933
1131
|
}
|
|
934
|
-
return { key, dryRun, force, source, wasmDir, indexPath };
|
|
1132
|
+
return { key, dryRun, force, ingestOnly, configOnly, interactive, source, wasmDir, indexPath };
|
|
935
1133
|
}
|
|
936
1134
|
async function main() {
|
|
937
1135
|
const opts = parseArgs();
|
|
1136
|
+
const savedApiKey = getSavedApiKey();
|
|
1137
|
+
const shouldRunInteractive = (opts.interactive || process.argv.slice(2).length === 0 || process.argv.slice(2).length <= 2 && !!opts.key) && !opts.dryRun && !opts.force && !opts.source && !opts.indexPath && !opts.configOnly && !opts.ingestOnly && !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
1138
|
+
let selectedTargets = MCP_TARGETS.map((target) => target.id);
|
|
1139
|
+
let effectiveConfigOnly = opts.configOnly;
|
|
1140
|
+
let effectiveIngestOnly = opts.ingestOnly;
|
|
1141
|
+
let effectiveIndexPath = opts.indexPath;
|
|
1142
|
+
let postIngestIndexPath;
|
|
1143
|
+
let apiKey = opts.key || process.env.CONARE_API_KEY || savedApiKey;
|
|
1144
|
+
if (shouldRunInteractive) {
|
|
1145
|
+
const detectedTools = detect();
|
|
1146
|
+
const answers = await runInteractiveSetup({
|
|
1147
|
+
savedApiKey,
|
|
1148
|
+
detectedTargets: MCP_TARGETS.map((target) => {
|
|
1149
|
+
const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex");
|
|
1150
|
+
return {
|
|
1151
|
+
id: target.id,
|
|
1152
|
+
label: target.label,
|
|
1153
|
+
available: detected?.available,
|
|
1154
|
+
recommended: detected?.available !== false
|
|
1155
|
+
};
|
|
1156
|
+
})
|
|
1157
|
+
});
|
|
1158
|
+
apiKey = answers.apiKey || apiKey;
|
|
1159
|
+
selectedTargets = answers.configureTargets.length > 0 ? answers.configureTargets : [];
|
|
1160
|
+
effectiveConfigOnly = !answers.ingestChats && !answers.indexCodebase;
|
|
1161
|
+
effectiveIngestOnly = answers.ingestChats && answers.configureTargets.length === 0;
|
|
1162
|
+
effectiveIndexPath = !answers.ingestChats && answers.indexCodebase ? answers.indexPath || "." : undefined;
|
|
1163
|
+
postIngestIndexPath = answers.ingestChats && answers.indexCodebase ? answers.indexPath || "." : undefined;
|
|
1164
|
+
}
|
|
1165
|
+
if (!apiKey) {
|
|
1166
|
+
printMissingKeyError();
|
|
1167
|
+
process.exit(1);
|
|
1168
|
+
}
|
|
1169
|
+
console.log("");
|
|
1170
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
1171
|
+
console.log(" Conare Installer");
|
|
1172
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
1173
|
+
console.log("");
|
|
938
1174
|
process.stdout.write("Validating API key... ");
|
|
939
|
-
const auth = await validateKey(
|
|
1175
|
+
const auth = await validateKey(apiKey);
|
|
940
1176
|
if (!auth.valid) {
|
|
941
1177
|
console.error("INVALID. Check your key at https://mcp.conare.ai");
|
|
942
1178
|
process.exit(1);
|
|
943
1179
|
}
|
|
944
|
-
console.log(`OK (${auth.email})`);
|
|
1180
|
+
console.log(auth.email ? `OK (${auth.email})` : "OK");
|
|
1181
|
+
saveApiKey(apiKey);
|
|
945
1182
|
console.log();
|
|
946
|
-
if (opts.
|
|
1183
|
+
if (!opts.wasmDir && existsSync7(join9(process.cwd(), "node_modules", "sql.js"))) {
|
|
1184
|
+
opts.wasmDir = join9(process.cwd(), "node_modules");
|
|
1185
|
+
}
|
|
1186
|
+
if (effectiveConfigOnly) {
|
|
1187
|
+
if (!opts.dryRun) {
|
|
1188
|
+
console.log("─── Configuring MCP ───");
|
|
1189
|
+
console.log("");
|
|
1190
|
+
for (const line of configureMcp(apiKey, selectedTargets)) {
|
|
1191
|
+
console.log(` ✓ ${line}`);
|
|
1192
|
+
}
|
|
1193
|
+
console.log("");
|
|
1194
|
+
}
|
|
1195
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
1196
|
+
console.log(" ✓ Done! Conare MCP is configured.");
|
|
1197
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
1198
|
+
console.log("");
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
if (effectiveIndexPath !== undefined) {
|
|
947
1202
|
const { resolve: resolve2 } = await import("node:path");
|
|
948
|
-
const absPath = resolve2(
|
|
1203
|
+
const absPath = resolve2(effectiveIndexPath);
|
|
949
1204
|
console.log(`Indexing codebase: ${absPath}`);
|
|
950
1205
|
if (opts.force) {
|
|
951
1206
|
clearIngested("codebase");
|
|
@@ -953,7 +1208,7 @@ async function main() {
|
|
|
953
1208
|
try {
|
|
954
1209
|
await fetch("https://mcp.conare.ai/api/containers/codebase", {
|
|
955
1210
|
method: "DELETE",
|
|
956
|
-
headers: { Authorization: `Bearer ${
|
|
1211
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
957
1212
|
});
|
|
958
1213
|
console.log("done");
|
|
959
1214
|
} catch {
|
|
@@ -977,7 +1232,7 @@ Nothing new to index.`);
|
|
|
977
1232
|
console.log(` ... and ${memories.length - 10} more`);
|
|
978
1233
|
} else {
|
|
979
1234
|
const barWidth = 20;
|
|
980
|
-
const { success, failed, results } = await uploadBulk(
|
|
1235
|
+
const { success, failed, results } = await uploadBulk(apiKey, memories, (uploaded, total, _failed) => {
|
|
981
1236
|
const pct = (uploaded / total * 100).toFixed(1);
|
|
982
1237
|
const filled = Math.round(uploaded / total * barWidth);
|
|
983
1238
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
@@ -987,8 +1242,17 @@ Nothing new to index.`);
|
|
|
987
1242
|
`);
|
|
988
1243
|
const fileHashes = results.filter((result) => result.success).map((result) => getDedupKey(memories[result.index])).filter((key) => !!key);
|
|
989
1244
|
markIngested("codebase", fileHashes);
|
|
1245
|
+
printFailureSummary(results, memories);
|
|
990
1246
|
}
|
|
991
1247
|
console.log();
|
|
1248
|
+
if (!opts.dryRun && !effectiveIngestOnly) {
|
|
1249
|
+
console.log("─── Configuring MCP ───");
|
|
1250
|
+
console.log("");
|
|
1251
|
+
for (const line of configureMcp(apiKey, selectedTargets)) {
|
|
1252
|
+
console.log(` ✓ ${line}`);
|
|
1253
|
+
}
|
|
1254
|
+
console.log("");
|
|
1255
|
+
}
|
|
992
1256
|
console.log("Done! Your codebase is now searchable via recall.");
|
|
993
1257
|
return;
|
|
994
1258
|
}
|
|
@@ -1045,7 +1309,7 @@ Nothing new to index.`);
|
|
|
1045
1309
|
console.log(` ... and ${allMemories.length - 5} more`);
|
|
1046
1310
|
} else {
|
|
1047
1311
|
const barWidth = 20;
|
|
1048
|
-
const { success, failed, results } = await uploadBulk(
|
|
1312
|
+
const { success, failed, results } = await uploadBulk(apiKey, allMemories, (uploaded, total, _failed) => {
|
|
1049
1313
|
const pct = (uploaded / total * 100).toFixed(1);
|
|
1050
1314
|
const filled = Math.round(uploaded / total * barWidth);
|
|
1051
1315
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
@@ -1056,6 +1320,7 @@ Nothing new to index.`);
|
|
|
1056
1320
|
if (failed > 0) {
|
|
1057
1321
|
console.log(` Re-run with --force to retry failed memories.`);
|
|
1058
1322
|
}
|
|
1323
|
+
printFailureSummary(results, allMemories);
|
|
1059
1324
|
const successfulKeysBySource = {
|
|
1060
1325
|
claude: [],
|
|
1061
1326
|
codex: [],
|
|
@@ -1086,7 +1351,46 @@ Nothing new to index.`);
|
|
|
1086
1351
|
}
|
|
1087
1352
|
}
|
|
1088
1353
|
console.log();
|
|
1089
|
-
|
|
1354
|
+
if (!opts.dryRun && !effectiveIngestOnly) {
|
|
1355
|
+
console.log("─── Configuring MCP ───");
|
|
1356
|
+
console.log("");
|
|
1357
|
+
for (const line of configureMcp(apiKey, selectedTargets)) {
|
|
1358
|
+
console.log(` ✓ ${line}`);
|
|
1359
|
+
}
|
|
1360
|
+
console.log("");
|
|
1361
|
+
}
|
|
1362
|
+
if (postIngestIndexPath) {
|
|
1363
|
+
const { resolve: resolve2 } = await import("node:path");
|
|
1364
|
+
const absPath = resolve2(postIngestIndexPath);
|
|
1365
|
+
console.log("─── Indexing codebase ───");
|
|
1366
|
+
console.log("");
|
|
1367
|
+
process.stdout.write("Scanning files... ");
|
|
1368
|
+
const { memories, fileCount, skipped } = indexCodebase(absPath);
|
|
1369
|
+
console.log(`${fileCount} files found, ${skipped} skipped`);
|
|
1370
|
+
if (memories.length === 0) {
|
|
1371
|
+
console.log(`
|
|
1372
|
+
Nothing new to index.`);
|
|
1373
|
+
} else {
|
|
1374
|
+
const barWidth = 20;
|
|
1375
|
+
const { success, failed, results } = await uploadBulk(apiKey, memories, (uploaded, total, _failed) => {
|
|
1376
|
+
const pct = (uploaded / total * 100).toFixed(1);
|
|
1377
|
+
const filled = Math.round(uploaded / total * barWidth);
|
|
1378
|
+
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
1379
|
+
process.stdout.write(`\r Uploading [\x1B[36m${bar}\x1B[0m] ${uploaded}/${total} (${pct}%)`);
|
|
1380
|
+
});
|
|
1381
|
+
process.stdout.write(`\r \x1B[32m✓\x1B[0m ${success} files indexed, ${failed} failed${" ".repeat(20)}
|
|
1382
|
+
`);
|
|
1383
|
+
const fileHashes = results.filter((result) => result.success).map((result) => getDedupKey(memories[result.index])).filter((key) => !!key);
|
|
1384
|
+
markIngested("codebase", fileHashes);
|
|
1385
|
+
printFailureSummary(results, memories);
|
|
1386
|
+
}
|
|
1387
|
+
console.log("");
|
|
1388
|
+
}
|
|
1389
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
1390
|
+
console.log(" ✓ Done! Every new conversation now starts with context.");
|
|
1391
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
1392
|
+
console.log(" Next time you can run `conare` after a global install.");
|
|
1393
|
+
console.log("");
|
|
1090
1394
|
}
|
|
1091
1395
|
main().catch((e) => {
|
|
1092
1396
|
console.error("Error:", e.message || e);
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conare",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Conare CLI for ingesting AI chat history and configuring memory at conare.ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"conare": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "bun build src/index.ts --outdir dist --target node --format esm --external sql.js
|
|
10
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm --external sql.js",
|
|
11
11
|
"prepublishOnly": "bun run build"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|