@prom.codes/memory-mcp 0.11.2 → 0.11.4
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/bin.js +86 -29
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -34,11 +34,43 @@ var LANGUAGE_IDS = [
|
|
|
34
34
|
];
|
|
35
35
|
|
|
36
36
|
// ../shared/dist/update-check.js
|
|
37
|
+
import { exec } from "node:child_process";
|
|
37
38
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
38
39
|
import { homedir } from "node:os";
|
|
39
40
|
import { join } from "node:path";
|
|
40
41
|
import { fileURLToPath } from "node:url";
|
|
41
|
-
var
|
|
42
|
+
var UPGRADE_BASE = "npm install -g @prom.codes/context-mcp @prom.codes/memory-mcp @prom.codes/saver";
|
|
43
|
+
var NATIVE_BUILD_PACKAGES = "better-sqlite3,tree-sitter";
|
|
44
|
+
function upgradeCommandFor(npmMajor) {
|
|
45
|
+
if (npmMajor !== null && npmMajor >= 12) {
|
|
46
|
+
return `${UPGRADE_BASE} --allow-scripts=${NATIVE_BUILD_PACKAGES}`;
|
|
47
|
+
}
|
|
48
|
+
return `${UPGRADE_BASE} --ignore-scripts=false --foreground-scripts`;
|
|
49
|
+
}
|
|
50
|
+
var UPGRADE_COMMAND = upgradeCommandFor(null);
|
|
51
|
+
var npmMajorPromise;
|
|
52
|
+
function detectNpmMajor(execImpl = exec) {
|
|
53
|
+
if (npmMajorPromise === void 0) {
|
|
54
|
+
npmMajorPromise = new Promise((resolvePromise) => {
|
|
55
|
+
try {
|
|
56
|
+
execImpl("npm --version", { timeout: 4e3, windowsHide: true }, (err, stdout) => {
|
|
57
|
+
if (err) {
|
|
58
|
+
resolvePromise(null);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const m = /(\d+)\./.exec(String(stdout).trim());
|
|
62
|
+
resolvePromise(m ? Number(m[1]) : null);
|
|
63
|
+
});
|
|
64
|
+
} catch {
|
|
65
|
+
resolvePromise(null);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return npmMajorPromise;
|
|
70
|
+
}
|
|
71
|
+
async function resolveUpgradeCommand(execImpl = exec) {
|
|
72
|
+
return upgradeCommandFor(await detectNpmMajor(execImpl));
|
|
73
|
+
}
|
|
42
74
|
async function packageIdentity(binImportMetaUrl) {
|
|
43
75
|
try {
|
|
44
76
|
const binPath = fileURLToPath(binImportMetaUrl);
|
|
@@ -108,7 +140,7 @@ async function syncAvailabilityMarker(dir, name, current, latest, updateAvailabl
|
|
|
108
140
|
name,
|
|
109
141
|
current,
|
|
110
142
|
latest,
|
|
111
|
-
command:
|
|
143
|
+
command: await resolveUpgradeCommand(),
|
|
112
144
|
notedAt: Date.now()
|
|
113
145
|
};
|
|
114
146
|
await mkdir(dir, { recursive: true }).catch(() => void 0);
|
|
@@ -231,7 +263,7 @@ function notify(log, name, current, latest) {
|
|
|
231
263
|
|
|
232
264
|
// ../shared/dist/update-info.js
|
|
233
265
|
async function buildUpdateStatus(pkgName, currentVersion, options = {}) {
|
|
234
|
-
const base = { current: currentVersion, command:
|
|
266
|
+
const base = { current: currentVersion, command: await resolveUpgradeCommand() };
|
|
235
267
|
if (options.isDevBuild === true) {
|
|
236
268
|
return {
|
|
237
269
|
...base,
|
|
@@ -261,7 +293,7 @@ async function buildUpdateStatus(pkgName, currentVersion, options = {}) {
|
|
|
261
293
|
|
|
262
294
|
// ../shared/dist/workspace-root.js
|
|
263
295
|
import { homedir as homedir2 } from "node:os";
|
|
264
|
-
import { dirname, resolve } from "node:path";
|
|
296
|
+
import { dirname, join as join2, resolve } from "node:path";
|
|
265
297
|
function isHomeOrFilesystemRoot(root) {
|
|
266
298
|
const abs = resolve(root);
|
|
267
299
|
if (abs === "")
|
|
@@ -276,18 +308,18 @@ function isHomeOrFilesystemRoot(root) {
|
|
|
276
308
|
// ../shared/dist/heartbeat.js
|
|
277
309
|
import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
278
310
|
import { homedir as homedir3 } from "node:os";
|
|
279
|
-
import { join as
|
|
311
|
+
import { join as join3 } from "node:path";
|
|
280
312
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
281
313
|
var STALE_AFTER_MS = 5 * 6e4;
|
|
282
314
|
function defaultStatusDir(env = process.env) {
|
|
283
315
|
const override = (env.PROMETHEUS_STATUS_DIR ?? "").trim();
|
|
284
316
|
if (override !== "")
|
|
285
317
|
return override;
|
|
286
|
-
return
|
|
318
|
+
return join3(homedir3(), ".prometheus", "status");
|
|
287
319
|
}
|
|
288
320
|
function startHeartbeat(options) {
|
|
289
321
|
const dir = options.dir ?? defaultStatusDir(options.env ?? process.env);
|
|
290
|
-
const file =
|
|
322
|
+
const file = join3(dir, `${options.server}-${process.pid}.json`);
|
|
291
323
|
const intervalMs = options.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
292
324
|
let record = {
|
|
293
325
|
server: options.server,
|
|
@@ -397,7 +429,7 @@ function createIdleWatchdog(options) {
|
|
|
397
429
|
// dist/composition.js
|
|
398
430
|
import { createHash } from "node:crypto";
|
|
399
431
|
import { homedir as homedir4 } from "node:os";
|
|
400
|
-
import { basename, join as
|
|
432
|
+
import { basename, join as join4, resolve as resolve2 } from "node:path";
|
|
401
433
|
|
|
402
434
|
// ../embeddings-openai-compat/dist/index.js
|
|
403
435
|
var DEFAULT_BATCH = 96;
|
|
@@ -676,6 +708,7 @@ var DEFAULT_BATCH2 = 128;
|
|
|
676
708
|
var DEFAULT_BATCH_CHARS = 4e5;
|
|
677
709
|
var DEFAULT_RETRIES2 = 4;
|
|
678
710
|
var DEFAULT_BACKOFF2 = 250;
|
|
711
|
+
var DEFAULT_TIMEOUT_MS2 = 15e3;
|
|
679
712
|
function sleep2(ms, signal) {
|
|
680
713
|
return new Promise((resolve3, reject) => {
|
|
681
714
|
if (signal?.aborted === true) {
|
|
@@ -707,6 +740,7 @@ var PrometheusEmbeddingProvider = class {
|
|
|
707
740
|
#maxBatchChars;
|
|
708
741
|
#maxRetries;
|
|
709
742
|
#retryBaseMs;
|
|
743
|
+
#timeoutMs;
|
|
710
744
|
#fetch;
|
|
711
745
|
#identity = null;
|
|
712
746
|
#identityPromise = null;
|
|
@@ -721,8 +755,12 @@ var PrometheusEmbeddingProvider = class {
|
|
|
721
755
|
this.#url = `${(opts.baseUrl ?? DEFAULT_BASE).replace(/\/+$/, "")}/embed`;
|
|
722
756
|
this.#batchSize = opts.batchSize ?? DEFAULT_BATCH2;
|
|
723
757
|
this.#maxBatchChars = opts.maxBatchChars ?? DEFAULT_BATCH_CHARS;
|
|
758
|
+
if (opts.timeoutMs !== void 0 && (!Number.isInteger(opts.timeoutMs) || opts.timeoutMs < 0)) {
|
|
759
|
+
throw new Error(`PrometheusEmbeddingProvider: timeoutMs must be a non-negative integer (0 disables), got ${opts.timeoutMs}`);
|
|
760
|
+
}
|
|
724
761
|
this.#maxRetries = opts.maxRetries ?? DEFAULT_RETRIES2;
|
|
725
762
|
this.#retryBaseMs = opts.retryBaseMs ?? DEFAULT_BACKOFF2;
|
|
763
|
+
this.#timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
726
764
|
this.#fetch = opts.fetch ?? fetch;
|
|
727
765
|
}
|
|
728
766
|
/**
|
|
@@ -792,8 +830,6 @@ var PrometheusEmbeddingProvider = class {
|
|
|
792
830
|
method: "GET",
|
|
793
831
|
headers: { authorization: `Bearer ${this.#apiKey}` }
|
|
794
832
|
};
|
|
795
|
-
if (signal !== void 0)
|
|
796
|
-
init.signal = signal;
|
|
797
833
|
const payload = await this.#requestJson(init, signal);
|
|
798
834
|
if (payload?.ok !== true || typeof payload.fingerprint !== "string" || payload.fingerprint === "" || !Number.isInteger(payload.dimension) || payload.dimension <= 0) {
|
|
799
835
|
throw nonRetryable2("prometheus-embed: malformed identity response");
|
|
@@ -827,8 +863,6 @@ var PrometheusEmbeddingProvider = class {
|
|
|
827
863
|
},
|
|
828
864
|
body: JSON.stringify({ input: batch, inputType })
|
|
829
865
|
};
|
|
830
|
-
if (signal !== void 0)
|
|
831
|
-
init.signal = signal;
|
|
832
866
|
const payload = await this.#requestJson(init, signal);
|
|
833
867
|
if (payload?.ok !== true || !Array.isArray(payload.embeddings)) {
|
|
834
868
|
throw nonRetryable2("prometheus-embed: malformed embed response");
|
|
@@ -864,8 +898,25 @@ var PrometheusEmbeddingProvider = class {
|
|
|
864
898
|
let attempt = 0;
|
|
865
899
|
let lastError = null;
|
|
866
900
|
while (attempt <= this.#maxRetries) {
|
|
901
|
+
const controller = new AbortController();
|
|
902
|
+
let timedOut = false;
|
|
903
|
+
let timer;
|
|
904
|
+
if (this.#timeoutMs > 0) {
|
|
905
|
+
timer = setTimeout(() => {
|
|
906
|
+
timedOut = true;
|
|
907
|
+
controller.abort();
|
|
908
|
+
}, this.#timeoutMs);
|
|
909
|
+
timer.unref?.();
|
|
910
|
+
}
|
|
911
|
+
const onParentAbort = () => controller.abort();
|
|
912
|
+
if (signal !== void 0) {
|
|
913
|
+
if (signal.aborted)
|
|
914
|
+
controller.abort();
|
|
915
|
+
else
|
|
916
|
+
signal.addEventListener("abort", onParentAbort, { once: true });
|
|
917
|
+
}
|
|
867
918
|
try {
|
|
868
|
-
const res = await this.#fetch(this.#url, init);
|
|
919
|
+
const res = await this.#fetch(this.#url, { ...init, signal: controller.signal });
|
|
869
920
|
if (res.status >= 500 && res.status < 600) {
|
|
870
921
|
lastError = new Error(`prometheus-embed: HTTP ${res.status}`);
|
|
871
922
|
attempt += 1;
|
|
@@ -881,15 +932,21 @@ var PrometheusEmbeddingProvider = class {
|
|
|
881
932
|
}
|
|
882
933
|
return await res.json();
|
|
883
934
|
} catch (err) {
|
|
884
|
-
if (err?.name === "AbortError")
|
|
935
|
+
if (err?.name === "AbortError" && !timedOut)
|
|
885
936
|
throw err;
|
|
886
937
|
if (err?.nonRetryable === true)
|
|
887
938
|
throw err;
|
|
939
|
+
const retryErr = timedOut ? new Error(`prometheus-embed: request timed out after ${this.#timeoutMs}ms`) : err;
|
|
888
940
|
if (attempt >= this.#maxRetries)
|
|
889
|
-
throw
|
|
890
|
-
lastError =
|
|
941
|
+
throw retryErr;
|
|
942
|
+
lastError = retryErr;
|
|
891
943
|
attempt += 1;
|
|
892
944
|
await sleep2(this.#retryBaseMs * 2 ** (attempt - 1), signal);
|
|
945
|
+
} finally {
|
|
946
|
+
if (timer !== void 0)
|
|
947
|
+
clearTimeout(timer);
|
|
948
|
+
if (signal !== void 0)
|
|
949
|
+
signal.removeEventListener("abort", onParentAbort);
|
|
893
950
|
}
|
|
894
951
|
}
|
|
895
952
|
throw lastError instanceof Error ? lastError : new Error(`prometheus-embed: exhausted ${this.#maxRetries} retries`);
|
|
@@ -1076,7 +1133,7 @@ var DEFAULT_BATCH4 = 100;
|
|
|
1076
1133
|
var DEFAULT_BATCH_CHARS2 = 4e5;
|
|
1077
1134
|
var DEFAULT_RETRIES4 = 4;
|
|
1078
1135
|
var DEFAULT_BACKOFF4 = 250;
|
|
1079
|
-
var
|
|
1136
|
+
var DEFAULT_TIMEOUT_MS3 = 15e3;
|
|
1080
1137
|
function sleep4(ms, signal) {
|
|
1081
1138
|
return new Promise((resolve3, reject) => {
|
|
1082
1139
|
if (signal?.aborted === true) {
|
|
@@ -1131,7 +1188,7 @@ var PrometheusRerankProvider = class {
|
|
|
1131
1188
|
}
|
|
1132
1189
|
this.#maxRetries = opts.maxRetries ?? DEFAULT_RETRIES4;
|
|
1133
1190
|
this.#retryBaseMs = opts.retryBaseMs ?? DEFAULT_BACKOFF4;
|
|
1134
|
-
this.#timeoutMs = opts.timeoutMs ??
|
|
1191
|
+
this.#timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
|
|
1135
1192
|
this.#fetch = opts.fetch ?? fetch;
|
|
1136
1193
|
}
|
|
1137
1194
|
/** Cumulative credits charged across all rerank calls of this instance. */
|
|
@@ -2453,7 +2510,7 @@ function projectIdFor(workspaceRoot) {
|
|
|
2453
2510
|
return createHash("sha256").update(abs).digest("hex").slice(0, 16);
|
|
2454
2511
|
}
|
|
2455
2512
|
function defaultMemoryDbPath() {
|
|
2456
|
-
return
|
|
2513
|
+
return join4(homedir4(), ".prometheus", "memory.db");
|
|
2457
2514
|
}
|
|
2458
2515
|
function intEnv(env, name, def) {
|
|
2459
2516
|
const raw = env[name];
|
|
@@ -2896,7 +2953,7 @@ function assertNoSecrets(text) {
|
|
|
2896
2953
|
// dist/setup.js
|
|
2897
2954
|
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
2898
2955
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2899
|
-
import { dirname as dirname3, join as
|
|
2956
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
2900
2957
|
var MEMORY_RUNTIMES = [
|
|
2901
2958
|
"claude-code",
|
|
2902
2959
|
"cursor",
|
|
@@ -2940,13 +2997,13 @@ alwaysApply: true
|
|
|
2940
2997
|
var TARGETS = {
|
|
2941
2998
|
"claude-code": { relPath: "CLAUDE.md", mode: "block", detect: "CLAUDE.md" },
|
|
2942
2999
|
cursor: {
|
|
2943
|
-
relPath:
|
|
3000
|
+
relPath: join6(".cursor", "rules", "prometheus-memory.mdc"),
|
|
2944
3001
|
mode: "file",
|
|
2945
3002
|
fileContent: CURSOR_FRONTMATTER + withMarkers(RULE_BLOCK) + "\n",
|
|
2946
3003
|
detect: ".cursor"
|
|
2947
3004
|
},
|
|
2948
3005
|
augment: {
|
|
2949
|
-
relPath:
|
|
3006
|
+
relPath: join6(".augment", "rules", "prometheus-memory.md"),
|
|
2950
3007
|
mode: "file",
|
|
2951
3008
|
fileContent: withMarkers(RULE_BLOCK) + "\n",
|
|
2952
3009
|
detect: ".augment"
|
|
@@ -2954,15 +3011,15 @@ var TARGETS = {
|
|
|
2954
3011
|
agents: { relPath: "AGENTS.md", mode: "block", detect: "AGENTS.md" }
|
|
2955
3012
|
};
|
|
2956
3013
|
function detectRuntimes(workspaceRoot) {
|
|
2957
|
-
const found = MEMORY_RUNTIMES.filter((rt) => existsSync(
|
|
3014
|
+
const found = MEMORY_RUNTIMES.filter((rt) => existsSync(join6(workspaceRoot, TARGETS[rt].detect)));
|
|
2958
3015
|
return found.length > 0 ? found : ["agents"];
|
|
2959
3016
|
}
|
|
2960
3017
|
function existingRuntimes(workspaceRoot) {
|
|
2961
|
-
return MEMORY_RUNTIMES.filter((rt) => existsSync(
|
|
3018
|
+
return MEMORY_RUNTIMES.filter((rt) => existsSync(join6(workspaceRoot, TARGETS[rt].detect)));
|
|
2962
3019
|
}
|
|
2963
3020
|
function installedRuntimes(workspaceRoot) {
|
|
2964
3021
|
return MEMORY_RUNTIMES.filter((rt) => {
|
|
2965
|
-
const p =
|
|
3022
|
+
const p = join6(workspaceRoot, TARGETS[rt].relPath);
|
|
2966
3023
|
if (!existsSync(p))
|
|
2967
3024
|
return false;
|
|
2968
3025
|
try {
|
|
@@ -2991,7 +3048,7 @@ function upsertBlock(existing, block) {
|
|
|
2991
3048
|
}
|
|
2992
3049
|
async function installRuntime(workspaceRoot, runtime) {
|
|
2993
3050
|
const target = TARGETS[runtime];
|
|
2994
|
-
const absPath =
|
|
3051
|
+
const absPath = join6(workspaceRoot, target.relPath);
|
|
2995
3052
|
const exists = existsSync(absPath);
|
|
2996
3053
|
const before = exists ? await readFile3(absPath, "utf-8") : "";
|
|
2997
3054
|
const after = target.mode === "file" ? target.fileContent : upsertBlock(before, RULE_BLOCK);
|
|
@@ -3398,7 +3455,7 @@ ${f.value}`);
|
|
|
3398
3455
|
embeddingsError = err instanceof Error ? err.message : String(err);
|
|
3399
3456
|
}
|
|
3400
3457
|
}
|
|
3401
|
-
const update = await buildUpdateStatus("@prom.codes/memory-mcp", "0.11.
|
|
3458
|
+
const update = await buildUpdateStatus("@prom.codes/memory-mcp", "0.11.4", { isDevBuild: false });
|
|
3402
3459
|
const summary = deps.rootIsHomeOrFsRoot ? `Memory at ${dbPath}: ${stats.total} records, but the workspace resolved to ${workspaceRoot} (home/root) \u2014 open a project folder so memories scope and mirror correctly.` : `Memory at ${dbPath}: ${stats.total} records for project "${projectName}".${update.updateAvailable === true ? ` Update available: ${update.current} \u2192 ${update.latest} (ask me to run update via the context server's update_servers tool).` : ""}`;
|
|
3403
3460
|
return textResult({
|
|
3404
3461
|
installed: true,
|
|
@@ -3434,7 +3491,7 @@ ${f.value}`);
|
|
|
3434
3491
|
// dist/server.js
|
|
3435
3492
|
var SERVER_IDENTITY = {
|
|
3436
3493
|
name: "prometheus-memory-mcp",
|
|
3437
|
-
version: "0.11.
|
|
3494
|
+
version: "0.11.4",
|
|
3438
3495
|
title: "prom.codes Memory"
|
|
3439
3496
|
};
|
|
3440
3497
|
var SERVER_INSTRUCTIONS = "Persistent agent memory for this workspace \u2014 USE IT PROACTIVELY; the user will not tell you to. Protocol:\n1. ONE-TIME: if this workspace has no Prometheus memory rule yet, call memory_setup now (idempotent) so the protocol is installed into the runtime rule files and survives future sessions. (The server also auto-installs it on startup when a project rule file already exists \u2014 memory_setup covers the rest.)\n2. SESSION START: before any non-trivial task, call memory_read to recall facts, decisions and procedures from earlier sessions.\n3. DURING WORK: when the user states a durable preference, decision, correction or project fact, store it with memory_write (semantic for facts, procedural for how-tos) \u2014 without being asked.\n4. LOOK-UP: use memory_search for keyword recall when memory_read is not specific enough.\n5. SESSION END: consolidate what was learned with memory_capture.\nCall memory_status anytime to check what is stored and whether the rule is installed. Never store secrets, API keys or credentials \u2014 such writes are rejected.";
|
|
@@ -3443,7 +3500,7 @@ var SERVER_INSTRUCTIONS = "Persistent agent memory for this workspace \u2014 USE
|
|
|
3443
3500
|
function looksLikeMissingNativeBinding(msg) {
|
|
3444
3501
|
return /bindings file|better_sqlite3\.node|could not locate the bindings|node_module_version|was compiled against a different|invalid elf|\.node['"\s]/i.test(msg);
|
|
3445
3502
|
}
|
|
3446
|
-
var NATIVE_BINDING_HINT = '\nThis looks like the native `better-sqlite3` module failed to load \u2014
|
|
3503
|
+
var NATIVE_BINDING_HINT = '\nThis looks like the native `better-sqlite3` module failed to load \u2014 the\ninstall script that builds it was skipped, so the binary was never produced.\nOn npm v12+ install scripts are opt-in by default; on older npm it\'s usually\n`ignore-scripts=true` hardening. Install globally, allowing the native build,\nthen point Claude Code at the built binary instead of npx:\n npm v12+: npm install -g @prom.codes/memory-mcp --allow-scripts=better-sqlite3\n npm \u2264 11: npm install -g @prom.codes/memory-mcp --ignore-scripts=false --foreground-scripts\n claude mcp add memory -- node "$(npm root -g)/@prom.codes/memory-mcp/dist/bin.js"\nDocs: https://prom.codes/docs/guides/troubleshooting#could-not-locate-the-bindings-file\n';
|
|
3447
3504
|
async function main() {
|
|
3448
3505
|
const env = process.env;
|
|
3449
3506
|
const explicitRoot = (env.PROMETHEUS_WORKSPACE_ROOT ?? "").trim();
|