@prom.codes/memory-mcp 0.17.0 → 0.19.0
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 +120 -27
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -28,9 +28,11 @@ var DOCUMENT_LANGUAGE_IDS = [
|
|
|
28
28
|
"yaml",
|
|
29
29
|
"toml"
|
|
30
30
|
];
|
|
31
|
+
var COMPOSITE_LANGUAGE_IDS = ["vue"];
|
|
31
32
|
var LANGUAGE_IDS = [
|
|
32
33
|
...GRAMMAR_LANGUAGE_IDS,
|
|
33
|
-
...DOCUMENT_LANGUAGE_IDS
|
|
34
|
+
...DOCUMENT_LANGUAGE_IDS,
|
|
35
|
+
...COMPOSITE_LANGUAGE_IDS
|
|
34
36
|
];
|
|
35
37
|
|
|
36
38
|
// ../shared/dist/update-check.js
|
|
@@ -259,15 +261,19 @@ function notify(log, name, current, latest) {
|
|
|
259
261
|
|
|
260
262
|
// ../shared/dist/update-info.js
|
|
261
263
|
async function buildUpdateStatus(pkgName, currentVersion, options = {}) {
|
|
262
|
-
const base = { current: currentVersion, command: await resolveUpgradeCommand() };
|
|
263
264
|
if (options.isDevBuild === true) {
|
|
264
265
|
return {
|
|
265
|
-
|
|
266
|
+
current: currentVersion,
|
|
267
|
+
// A workspace build does not need an npm-version probe: the command is
|
|
268
|
+
// invariant across supported npm releases, and avoiding the subprocess
|
|
269
|
+
// keeps status responsive during deferred-root startup.
|
|
270
|
+
command: UPGRADE_COMMAND,
|
|
266
271
|
latest: null,
|
|
267
272
|
updateAvailable: null,
|
|
268
273
|
note: "dev build (workspace) \u2014 version comparison skipped"
|
|
269
274
|
};
|
|
270
275
|
}
|
|
276
|
+
const base = { current: currentVersion, command: await resolveUpgradeCommand() };
|
|
271
277
|
let latest = null;
|
|
272
278
|
try {
|
|
273
279
|
latest = await getLatestVersion(pkgName, {
|
|
@@ -388,7 +394,12 @@ function startHeartbeat(options) {
|
|
|
388
394
|
// ../shared/dist/idle-watchdog.js
|
|
389
395
|
var DEFAULT_IDLE_EXIT_MS = 30 * 6e4;
|
|
390
396
|
var IDLE_CHECK_INTERVAL_MS = 6e4;
|
|
397
|
+
var DEFAULT_PROBE_TIMEOUT_MS = 5e3;
|
|
398
|
+
var DEFAULT_PROBE_RETRIES = 2;
|
|
399
|
+
var DEFAULT_MAX_PROBE_EXTENSIONS = 8;
|
|
391
400
|
var IDLE_EXIT_ENV = "PROMETHEUS_IDLE_EXIT_MS";
|
|
401
|
+
var IDLE_MAX_PINGS_ENV = "PROMETHEUS_IDLE_MAX_PINGS";
|
|
402
|
+
var IDLE_PROBE_MS_ENV = "PROMETHEUS_IDLE_PROBE_MS";
|
|
392
403
|
function parseIdleExitMs(env) {
|
|
393
404
|
const raw = (env[IDLE_EXIT_ENV] ?? "").trim();
|
|
394
405
|
if (raw === "")
|
|
@@ -396,6 +407,20 @@ function parseIdleExitMs(env) {
|
|
|
396
407
|
const n = Number(raw);
|
|
397
408
|
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
398
409
|
}
|
|
410
|
+
function parseMaxProbeExtensions(env) {
|
|
411
|
+
const raw = (env[IDLE_MAX_PINGS_ENV] ?? "").trim();
|
|
412
|
+
if (raw === "")
|
|
413
|
+
return void 0;
|
|
414
|
+
const n = Number(raw);
|
|
415
|
+
return Number.isInteger(n) && n >= 0 ? n : void 0;
|
|
416
|
+
}
|
|
417
|
+
function parseProbeTimeoutMs(env) {
|
|
418
|
+
const raw = (env[IDLE_PROBE_MS_ENV] ?? "").trim();
|
|
419
|
+
if (raw === "")
|
|
420
|
+
return void 0;
|
|
421
|
+
const n = Number(raw);
|
|
422
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
423
|
+
}
|
|
399
424
|
function createIdleWatchdog(options) {
|
|
400
425
|
const env = options.env ?? process.env;
|
|
401
426
|
const idleMs = options.idleMs ?? parseIdleExitMs(env) ?? DEFAULT_IDLE_EXIT_MS;
|
|
@@ -406,18 +431,72 @@ function createIdleWatchdog(options) {
|
|
|
406
431
|
}, stop() {
|
|
407
432
|
}, idleMs: 0 };
|
|
408
433
|
}
|
|
434
|
+
const probe = options.probe;
|
|
435
|
+
const probeTimeoutMs = options.probeTimeoutMs ?? parseProbeTimeoutMs(env) ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
436
|
+
const probeRetries = Math.max(1, options.probeRetries ?? DEFAULT_PROBE_RETRIES);
|
|
437
|
+
const maxProbeExtensions = options.maxProbeExtensions ?? parseMaxProbeExtensions(env) ?? DEFAULT_MAX_PROBE_EXTENSIONS;
|
|
438
|
+
const yieldToIo = options.yieldToIo ?? ((fn) => {
|
|
439
|
+
const t = setImmediate(fn);
|
|
440
|
+
t.unref?.();
|
|
441
|
+
});
|
|
409
442
|
let lastActivity = now();
|
|
410
443
|
let stopped = false;
|
|
411
444
|
let fired = false;
|
|
445
|
+
let probing = false;
|
|
446
|
+
let failedProbes = 0;
|
|
447
|
+
let probeOnlyExtensions = 0;
|
|
448
|
+
const fire = (idleFor, why) => {
|
|
449
|
+
fired = true;
|
|
450
|
+
clearInterval(timer);
|
|
451
|
+
const detail = why === "unanswered" ? ` and ${probeRetries} liveness pings went unanswered (${Math.round(probeTimeoutMs / 1e3)}s each)` : why === "capped" ? ` \u2014 the client answered ${probeOnlyExtensions} liveness pings in a row but sent no request in any of those windows, so this server is attached to a session nobody is using (raise or disable with ${IDLE_MAX_PINGS_ENV})` : "";
|
|
452
|
+
options.onIdle(`idle for ${Math.round(idleFor / 1e3)}s with no client activity` + detail + ` (set ${IDLE_EXIT_ENV}=0 to disable)`);
|
|
453
|
+
};
|
|
454
|
+
const launchProbe = () => {
|
|
455
|
+
probing = true;
|
|
456
|
+
const startedAt = now();
|
|
457
|
+
let settled = false;
|
|
458
|
+
let timeoutTimer = null;
|
|
459
|
+
const finish = (alive) => {
|
|
460
|
+
if (settled)
|
|
461
|
+
return;
|
|
462
|
+
settled = true;
|
|
463
|
+
if (timeoutTimer !== null)
|
|
464
|
+
clearTimeout(timeoutTimer);
|
|
465
|
+
probing = false;
|
|
466
|
+
if (stopped || fired)
|
|
467
|
+
return;
|
|
468
|
+
if (alive || lastActivity > startedAt) {
|
|
469
|
+
failedProbes = 0;
|
|
470
|
+
lastActivity = now();
|
|
471
|
+
probeOnlyExtensions += 1;
|
|
472
|
+
if (maxProbeExtensions > 0 && probeOnlyExtensions >= maxProbeExtensions) {
|
|
473
|
+
fire(idleMs * probeOnlyExtensions, "capped");
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
failedProbes += 1;
|
|
478
|
+
if (failedProbes < probeRetries)
|
|
479
|
+
return;
|
|
480
|
+
fire(now() - lastActivity, "unanswered");
|
|
481
|
+
};
|
|
482
|
+
timeoutTimer = setTimeout(() => {
|
|
483
|
+
yieldToIo(() => finish(false));
|
|
484
|
+
}, probeTimeoutMs);
|
|
485
|
+
timeoutTimer.unref?.();
|
|
486
|
+
void probe().then(() => finish(true), () => finish(false));
|
|
487
|
+
};
|
|
412
488
|
const timer = setInterval(() => {
|
|
413
489
|
if (stopped || fired)
|
|
414
490
|
return;
|
|
415
491
|
const idleFor = now() - lastActivity;
|
|
416
|
-
if (idleFor
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
492
|
+
if (idleFor < idleMs)
|
|
493
|
+
return;
|
|
494
|
+
if (probe === void 0) {
|
|
495
|
+
fire(idleFor, "silent");
|
|
496
|
+
return;
|
|
420
497
|
}
|
|
498
|
+
if (!probing)
|
|
499
|
+
launchProbe();
|
|
421
500
|
}, checkIntervalMs);
|
|
422
501
|
timer.unref?.();
|
|
423
502
|
return {
|
|
@@ -426,6 +505,10 @@ function createIdleWatchdog(options) {
|
|
|
426
505
|
if (stopped)
|
|
427
506
|
return;
|
|
428
507
|
lastActivity = now();
|
|
508
|
+
if (!probing) {
|
|
509
|
+
failedProbes = 0;
|
|
510
|
+
probeOnlyExtensions = 0;
|
|
511
|
+
}
|
|
429
512
|
},
|
|
430
513
|
stop() {
|
|
431
514
|
if (stopped)
|
|
@@ -2200,7 +2283,7 @@ function applyRecorderHooks(opts = {}) {
|
|
|
2200
2283
|
hookPath,
|
|
2201
2284
|
events: opts.uninstall ? [] : [...RECORDER_EVENTS],
|
|
2202
2285
|
backup,
|
|
2203
|
-
note: opts.uninstall ? "Removed the
|
|
2286
|
+
note: opts.uninstall ? "Removed the prom.codes session recorder hooks. Reload your Claude Code window(s)." : "Installed the session recorder (opt-in). Reload / restart your Claude Code window(s). It records LOCALLY to ~/.prometheus/recorder; nothing is uploaded. Uninstall anytime with recorder_setup { uninstall: true }."
|
|
2204
2287
|
};
|
|
2205
2288
|
}
|
|
2206
2289
|
|
|
@@ -3949,9 +4032,9 @@ var MEMORY_RUNTIMES = [
|
|
|
3949
4032
|
];
|
|
3950
4033
|
var BLOCK_START = "<!-- prometheus-memory:start -->";
|
|
3951
4034
|
var BLOCK_END = "<!-- prometheus-memory:end -->";
|
|
3952
|
-
var RULE_BLOCK = `##
|
|
4035
|
+
var RULE_BLOCK = `## prom.codes Agent Memory
|
|
3953
4036
|
|
|
3954
|
-
This workspace uses the
|
|
4037
|
+
This workspace uses the prom.codes memory MCP server (\`memory_*\` tools).
|
|
3955
4038
|
Follow this protocol:
|
|
3956
4039
|
|
|
3957
4040
|
1. **Session start:** call \`memory_read\` once before non-trivial work to
|
|
@@ -3976,7 +4059,7 @@ ${block}
|
|
|
3976
4059
|
${BLOCK_END}`;
|
|
3977
4060
|
}
|
|
3978
4061
|
var CURSOR_FRONTMATTER = `---
|
|
3979
|
-
description:
|
|
4062
|
+
description: prom.codes agent memory protocol
|
|
3980
4063
|
alwaysApply: true
|
|
3981
4064
|
---
|
|
3982
4065
|
|
|
@@ -4531,7 +4614,7 @@ ${f.value}`);
|
|
|
4531
4614
|
});
|
|
4532
4615
|
reg("setup", {
|
|
4533
4616
|
title: "Install memory rules into runtime configs",
|
|
4534
|
-
description: "Idempotently install the
|
|
4617
|
+
description: "Idempotently install the prom.codes memory-protocol rule block into agent runtime configs in this workspace: CLAUDE.md (claude-code), .cursor/rules/prometheus-memory.mdc (cursor), .augment/rules/prometheus-memory.md (augment), AGENTS.md (agents). Without `runtimes` it auto-detects which runtimes are present (fallback: agents). Only the marked block is written \u2014 existing content is never touched. Re-running updates the block in place.",
|
|
4535
4618
|
inputSchema: setupInput
|
|
4536
4619
|
}, async (args) => {
|
|
4537
4620
|
const deps = await ready();
|
|
@@ -4553,7 +4636,7 @@ ${f.value}`);
|
|
|
4553
4636
|
});
|
|
4554
4637
|
reg("recorder_setup", {
|
|
4555
4638
|
title: "Install the Session Recorder (opt-in)",
|
|
4556
|
-
description: "Install (or, with `uninstall: true`, remove) the
|
|
4639
|
+
description: "Install (or, with `uninstall: true`, remove) the prom.codes Session Recorder \u2014 Claude Code hooks that capture your coding sessions LOCALLY so memory-mcp can recall what you did and distil durable knowledge. STRICTLY OPT-IN and reversible: this writes a hook script + 5 hook entries (SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd) into settings.json (backed up first). It records BEHAVIOUR, not code \u2014 tool names + short previews, with secrets redacted and sensitive-file contents skipped \u2014 as append-only JSONL under ~/.prometheus/recorder/. NOTHING is uploaded; it never leaves your machine. `scope`: 'project-local' (default \u2014 this project only, .claude/settings.local.json), 'project' (committed .claude/settings.json), or 'user' (~/.claude/settings.json, every project). After install/uninstall, RELOAD your Claude Code window(s). Claude-Code-specific (Cursor/VS Code do not run hooks).",
|
|
4557
4640
|
inputSchema: recorderSetupInput
|
|
4558
4641
|
}, async (args) => {
|
|
4559
4642
|
const deps = await ready();
|
|
@@ -4648,7 +4731,7 @@ ${f.value}`);
|
|
|
4648
4731
|
embeddingsError = err instanceof Error ? err.message : String(err);
|
|
4649
4732
|
}
|
|
4650
4733
|
}
|
|
4651
|
-
const update = await buildUpdateStatus("@prom.codes/memory-mcp", "0.
|
|
4734
|
+
const update = await buildUpdateStatus("@prom.codes/memory-mcp", "0.19.0", { isDevBuild: false });
|
|
4652
4735
|
let recorder;
|
|
4653
4736
|
try {
|
|
4654
4737
|
const scopes = ["project-local", "project", "user"];
|
|
@@ -4729,11 +4812,11 @@ ${f.value}`);
|
|
|
4729
4812
|
|
|
4730
4813
|
// dist/server.js
|
|
4731
4814
|
var SERVER_IDENTITY = {
|
|
4732
|
-
name: "
|
|
4733
|
-
version: "0.
|
|
4815
|
+
name: "prom.codes-memory",
|
|
4816
|
+
version: "0.19.0",
|
|
4734
4817
|
title: "prom.codes Memory"
|
|
4735
4818
|
};
|
|
4736
|
-
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
|
|
4819
|
+
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 prom.codes 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.";
|
|
4737
4820
|
|
|
4738
4821
|
// dist/bin.js
|
|
4739
4822
|
function looksLikeMissingNativeBinding(msg) {
|
|
@@ -4784,7 +4867,7 @@ async function main() {
|
|
|
4784
4867
|
if (shuttingDown)
|
|
4785
4868
|
return;
|
|
4786
4869
|
shuttingDown = true;
|
|
4787
|
-
process.stderr.write(`
|
|
4870
|
+
process.stderr.write(`prom.codes-memory: ${reason}, shutting down
|
|
4788
4871
|
`);
|
|
4789
4872
|
watchdog?.stop();
|
|
4790
4873
|
if (recorderTimer !== null)
|
|
@@ -4803,9 +4886,19 @@ async function main() {
|
|
|
4803
4886
|
process.stdin.once("end", () => void shutdown("stdin closed (client exited)"));
|
|
4804
4887
|
process.stdin.once("close", () => void shutdown("stdin closed (client exited)"));
|
|
4805
4888
|
server.server.onclose = () => void shutdown("transport closed (client exited)");
|
|
4806
|
-
watchdog = createIdleWatchdog({
|
|
4889
|
+
watchdog = createIdleWatchdog({
|
|
4890
|
+
onIdle: (reason) => void shutdown(reason),
|
|
4891
|
+
env,
|
|
4892
|
+
// Before reaping, ask the client via MCP ping: a live-but-quiet session
|
|
4893
|
+
// (e.g. Claude Code with deferred tools, no memory call for 30 min —
|
|
4894
|
+
// 2026-08-12) answers and is never dropped; only an abandoned server
|
|
4895
|
+
// whose pong never comes still reaps itself.
|
|
4896
|
+
probe: async () => {
|
|
4897
|
+
await server.server.ping();
|
|
4898
|
+
}
|
|
4899
|
+
});
|
|
4807
4900
|
if (watchdog.idleMs > 0) {
|
|
4808
|
-
process.stderr.write(`
|
|
4901
|
+
process.stderr.write(`prom.codes-memory: idle self-exit armed (${Math.round(watchdog.idleMs / 6e4)} min of no client activity; client is pinged before exit)
|
|
4809
4902
|
`);
|
|
4810
4903
|
}
|
|
4811
4904
|
const armIdleWatch = () => {
|
|
@@ -4820,7 +4913,7 @@ async function main() {
|
|
|
4820
4913
|
env,
|
|
4821
4914
|
...override !== void 0 && override !== "" ? { workspaceRootOverride: override } : {}
|
|
4822
4915
|
});
|
|
4823
|
-
process.stderr.write(`
|
|
4916
|
+
process.stderr.write(`prom.codes-memory: workspace=${composed.workspaceRoot} (via ${via}) project=${composed.projectName} (${composed.projectId}) db=${composed.dbPath} embed=${composed.embedderId}${composed.embeddingsEnabled ? "" : " (keyword-only)"} rerank=${composed.rerankerId} extract=${composed.extractorId} rewrite=${composed.rewriterId} temporal=${composed.temporalEnabled ? "on" : "off"} dedup=${composed.dedupEnabled ? "on" : "off"}
|
|
4824
4917
|
`);
|
|
4825
4918
|
heartbeat.update({
|
|
4826
4919
|
workspaceRoot: composed.workspaceRoot,
|
|
@@ -4832,13 +4925,13 @@ async function main() {
|
|
|
4832
4925
|
}
|
|
4833
4926
|
});
|
|
4834
4927
|
if (composed.rootIsHomeOrFsRoot) {
|
|
4835
|
-
process.stderr.write(`
|
|
4928
|
+
process.stderr.write(`prom.codes-memory: workspace resolved to ${composed.workspaceRoot} (your home directory or a filesystem root) \u2014 project memories will NOT be mirrored to markdown there. Open a project folder (Claude Code passes it via CLAUDE_PROJECT_DIR) or set PROMETHEUS_WORKSPACE_ROOT. Call memory_status for details.
|
|
4836
4929
|
`);
|
|
4837
4930
|
} else if (composed.autoSetup) {
|
|
4838
4931
|
void autoInstallExisting(composed.workspaceRoot).then((results) => {
|
|
4839
4932
|
const wrote = results.filter((r) => r.action !== "unchanged");
|
|
4840
4933
|
if (wrote.length > 0) {
|
|
4841
|
-
process.stderr.write(`
|
|
4934
|
+
process.stderr.write(`prom.codes-memory: auto-installed the memory rule into ${wrote.map((r) => r.runtime).join(", ")} (set PROMETHEUS_MEMORY_AUTO_SETUP=off to disable)
|
|
4842
4935
|
`);
|
|
4843
4936
|
}
|
|
4844
4937
|
}).catch(() => {
|
|
@@ -4849,7 +4942,7 @@ async function main() {
|
|
|
4849
4942
|
try {
|
|
4850
4943
|
const r = await composed.recorder.ingestSpoolDir(composed.projectId, env);
|
|
4851
4944
|
if (r.events > 0) {
|
|
4852
|
-
process.stderr.write(`
|
|
4945
|
+
process.stderr.write(`prom.codes-memory: recorder ingested ${r.events} event(s) from ${r.sessions} session(s); reclaimed ${r.deletedSpools} spool(s)
|
|
4853
4946
|
`);
|
|
4854
4947
|
}
|
|
4855
4948
|
const c = composed;
|
|
@@ -4865,7 +4958,7 @@ async function main() {
|
|
|
4865
4958
|
mirrorToFiles: !c.rootIsHomeOrFsRoot
|
|
4866
4959
|
}, s.sessionId);
|
|
4867
4960
|
if (outcome.curated) {
|
|
4868
|
-
process.stderr.write(`
|
|
4961
|
+
process.stderr.write(`prom.codes-memory: curated session ${s.sessionId} (${outcome.facts ?? 0} facts, ${outcome.procedures ?? 0} procedures)
|
|
4869
4962
|
`);
|
|
4870
4963
|
}
|
|
4871
4964
|
}
|
|
@@ -4894,7 +4987,7 @@ async function main() {
|
|
|
4894
4987
|
boot(fromRoots ?? process.cwd(), fromRoots !== null ? "MCP roots" : "cwd fallback (client advertised no roots)");
|
|
4895
4988
|
} catch (err) {
|
|
4896
4989
|
const message = err instanceof Error ? err.message : String(err);
|
|
4897
|
-
process.stderr.write(`
|
|
4990
|
+
process.stderr.write(`prom.codes-memory: fatal during boot: ${message}
|
|
4898
4991
|
`);
|
|
4899
4992
|
if (looksLikeMissingNativeBinding(message))
|
|
4900
4993
|
process.stderr.write(NATIVE_BINDING_HINT);
|
|
@@ -4911,7 +5004,7 @@ async function main() {
|
|
|
4911
5004
|
}
|
|
4912
5005
|
main().catch((err) => {
|
|
4913
5006
|
const message = err instanceof Error ? err.message : String(err);
|
|
4914
|
-
process.stderr.write(`
|
|
5007
|
+
process.stderr.write(`prom.codes-memory: fatal: ${message}
|
|
4915
5008
|
`);
|
|
4916
5009
|
if (looksLikeMissingNativeBinding(message))
|
|
4917
5010
|
process.stderr.write(NATIVE_BINDING_HINT);
|