@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.
Files changed (2) hide show
  1. package/dist/bin.js +86 -29
  2. 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 UPGRADE_COMMAND = "npm install -g @prom.codes/context-mcp @prom.codes/memory-mcp @prom.codes/saver --ignore-scripts=false --foreground-scripts";
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: UPGRADE_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: UPGRADE_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 join2 } from "node:path";
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 join2(homedir3(), ".prometheus", "status");
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 = join2(dir, `${options.server}-${process.pid}.json`);
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 join3, resolve as resolve2 } from "node:path";
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 err;
890
- lastError = err;
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 DEFAULT_TIMEOUT_MS2 = 15e3;
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 ?? DEFAULT_TIMEOUT_MS2;
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 join3(homedir4(), ".prometheus", "memory.db");
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 join5 } from "node:path";
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: join5(".cursor", "rules", "prometheus-memory.mdc"),
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: join5(".augment", "rules", "prometheus-memory.md"),
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(join5(workspaceRoot, TARGETS[rt].detect)));
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(join5(workspaceRoot, TARGETS[rt].detect)));
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 = join5(workspaceRoot, TARGETS[rt].relPath);
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 = join5(workspaceRoot, target.relPath);
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.2", { isDevBuild: false });
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.2",
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 usually\nbecause npm `ignore-scripts=true` (corporate hardening) made `npx` skip the\nnative build, so the binary was never produced. Fix it once, keeping your\nhardening intact:\n npm install -g @prom.codes/memory-mcp --ignore-scripts=false --foreground-scripts\nthen point Claude Code at the built binary instead of npx:\n claude mcp add memory -- node "$(npm root -g)/@prom.codes/memory-mcp/dist/bin.js"\nDocs: https://prom.codes/docs/mcp/claude-code\n';
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prom.codes/memory-mcp",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "description": "prom.codes Memory — persistent, local-first agent memory as an MCP server.",
5
5
  "type": "module",
6
6
  "bin": {