claude-mem-lite 3.50.0 → 3.51.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/hook.mjs +7 -2
- package/lib/deferred-work.mjs +43 -0
- package/mem-cli.mjs +7 -2
- package/package.json +1 -1
- package/server.mjs +6 -2
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "claude-mem-lite",
|
|
13
|
-
"version": "3.
|
|
13
|
+
"version": "3.51.0",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark)."
|
|
16
16
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.51.0",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sdsrss"
|
package/hook.mjs
CHANGED
|
@@ -344,8 +344,13 @@ async function handlePostToolUse() {
|
|
|
344
344
|
let db = null;
|
|
345
345
|
const getDb = () => { if (!db) db = openDb(); return db; };
|
|
346
346
|
|
|
347
|
-
// Tier 2 G: Error-triggered recall
|
|
348
|
-
|
|
347
|
+
// Tier 2 G: Error-triggered recall. Gated on isHardError (genuine failure
|
|
348
|
+
// fingerprint), NOT isError — the loose gate fired "Related memories found for
|
|
349
|
+
// this error" on exit-0 commands whose output merely contained the word "error"
|
|
350
|
+
// (G8, roadmap 2026-07-18), and was self-recursive: the hint string itself
|
|
351
|
+
// contains 'error', so a later command echoing it re-triggered recall.
|
|
352
|
+
// entry.isError above keeps the loose semantics on purpose (episode narrative).
|
|
353
|
+
if (bashSig?.isHardError) {
|
|
349
354
|
const d = getDb();
|
|
350
355
|
if (d) triggerErrorRecall(d, toolInput, resp);
|
|
351
356
|
}
|
package/lib/deferred-work.mjs
CHANGED
|
@@ -58,6 +58,49 @@ export function listOpenWithOrdinal(db, project, limit = 10) {
|
|
|
58
58
|
`).all(project, limit);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// ─── G11: list age + stale refresh hint (roadmap 2026-07-18) ─────────────────
|
|
62
|
+
|
|
63
|
+
const DAY_MS = 86_400_000;
|
|
64
|
+
export const DEFER_STALE_DAYS = 30;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Render one `defer list` row (shared by CLI cmdDeferList and MCP
|
|
68
|
+
* mem_defer_list — no hand-synced twins). Age rides inside the id tag:
|
|
69
|
+
* `1. 🟡 [P2] title (D#5, 12d)`.
|
|
70
|
+
* @param {{ordinal, priority, title, id, created_at_epoch}} r
|
|
71
|
+
* @param {number} [now]
|
|
72
|
+
*/
|
|
73
|
+
export function formatDeferListRow(r, now = Date.now()) {
|
|
74
|
+
const pTag = r.priority === 3 ? '🔴' : r.priority === 1 ? '⚪' : '🟡';
|
|
75
|
+
const days = Math.max(0, Math.floor((now - r.created_at_epoch) / DAY_MS));
|
|
76
|
+
return `${r.ordinal}. ${pTag} [P${r.priority}] ${r.title} (D#${r.id}, ${days}d)`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Count ALL open rows older than DEFER_STALE_DAYS in the project — deliberately
|
|
81
|
+
* not derived from the displayed list: (priority DESC, created_at ASC) ordering
|
|
82
|
+
* sinks old P1 rows past the LIMIT, and those are exactly the ones the hint
|
|
83
|
+
* exists for.
|
|
84
|
+
*/
|
|
85
|
+
export function countStaleOpen(db, project, now = Date.now()) {
|
|
86
|
+
return db.prepare(`
|
|
87
|
+
SELECT COUNT(*) AS n FROM deferred_work
|
|
88
|
+
WHERE project = ? AND status = 'open' AND created_at_epoch < ?
|
|
89
|
+
`).get(project, now - DEFER_STALE_DAYS * DAY_MS).n;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Tail hint for stale open items. Null when none — callers print nothing.
|
|
94
|
+
* Nudge-only by design: no auto-demote / auto-drop (defer is a commitment
|
|
95
|
+
* surface; silent cleanup would break it).
|
|
96
|
+
* @param {number} staleCount
|
|
97
|
+
* @returns {string|null}
|
|
98
|
+
*/
|
|
99
|
+
export function formatDeferStaleHint(staleCount) {
|
|
100
|
+
if (!staleCount || staleCount <= 0) return null;
|
|
101
|
+
return `⚠ ${staleCount} item(s) open >${DEFER_STALE_DAYS} days — refresh (still relevant?) or drop with a reason.`;
|
|
102
|
+
}
|
|
103
|
+
|
|
61
104
|
/**
|
|
62
105
|
* Set status='dropped' with a non-empty reason. No-op when status is not 'open'.
|
|
63
106
|
* @returns {{changed: number}} 1 if updated, 0 if not found or not open.
|
package/mem-cli.mjs
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
resolveDeferredIds, closeDeferredItems,
|
|
57
57
|
getDeferredByIds, formatDeferredDetail,
|
|
58
58
|
searchDeferredWork, formatDeferredSearchTrailer,
|
|
59
|
+
formatDeferListRow, countStaleOpen, formatDeferStaleHint,
|
|
59
60
|
} from './lib/deferred-work.mjs';
|
|
60
61
|
|
|
61
62
|
// ─── Commands ────────────────────────────────────────────────────────────────
|
|
@@ -1037,9 +1038,10 @@ function cmdDeferList(db, args) {
|
|
|
1037
1038
|
}
|
|
1038
1039
|
out(`[mem] Open deferred items (project "${project}"):`);
|
|
1039
1040
|
for (const r of list) {
|
|
1040
|
-
|
|
1041
|
-
out(` ${r.ordinal}. ${pTag} [P${r.priority}] ${r.title} (D#${r.id})`);
|
|
1041
|
+
out(` ${formatDeferListRow(r)}`);
|
|
1042
1042
|
}
|
|
1043
|
+
const staleHint = formatDeferStaleHint(countStaleOpen(db, project));
|
|
1044
|
+
if (staleHint) out(` ${staleHint}`);
|
|
1043
1045
|
// Affordance for the detail field — list stays title-only by design (it is
|
|
1044
1046
|
// mirrored into the SessionStart dashboard, where detail would be noise).
|
|
1045
1047
|
out(` Full detail: claude-mem-lite get D#<id>`);
|
|
@@ -1212,6 +1214,9 @@ async function cmdStats(db, args) {
|
|
|
1212
1214
|
}
|
|
1213
1215
|
|
|
1214
1216
|
out(`[mem] Stats${project ? ` (${project})` : ''}:`);
|
|
1217
|
+
// Env-aware data dir (CLAUDE_MEM_DIR || ~/.claude-mem-lite) — stated so any
|
|
1218
|
+
// raw-db fallback can't guess a co-located-with-the-CLI path (D#92 chain).
|
|
1219
|
+
out(`Data dir: ${DB_DIR}`);
|
|
1215
1220
|
out(`Total: ${obsTotal.c.toLocaleString()} observations | ${sessTotal.c} sessions | ${promptTotal.c} prompts`);
|
|
1216
1221
|
out(`Last ${days}d: ${obsRecent.c} observations | ${sessRecent.c} sessions`);
|
|
1217
1222
|
out('');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.51.0",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@10.9.2",
|
package/server.mjs
CHANGED
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
resolveDeferredIds, closeDeferredItems,
|
|
55
55
|
getDeferredByIds, formatDeferredDetail,
|
|
56
56
|
searchDeferredWork, formatDeferredSearchTrailer,
|
|
57
|
+
formatDeferListRow, countStaleOpen, formatDeferStaleHint,
|
|
57
58
|
} from './lib/deferred-work.mjs';
|
|
58
59
|
import { _resetVocabCache } from './tfidf.mjs';
|
|
59
60
|
import { createRequire } from 'module';
|
|
@@ -846,9 +847,10 @@ server.registerTool(
|
|
|
846
847
|
}
|
|
847
848
|
const lines = [`Open deferred items (project "${project}"):`];
|
|
848
849
|
for (const r of list) {
|
|
849
|
-
|
|
850
|
-
lines.push(`${r.ordinal}. ${pTag} [P${r.priority}] ${r.title} (D#${r.id})`);
|
|
850
|
+
lines.push(formatDeferListRow(r));
|
|
851
851
|
}
|
|
852
|
+
const staleHint = formatDeferStaleHint(countStaleOpen(db, project));
|
|
853
|
+
if (staleHint) lines.push(staleHint);
|
|
852
854
|
// Affordance for the detail field — list stays title-only by design.
|
|
853
855
|
lines.push(`Full detail: mem_get ids=["D#<id>"]`);
|
|
854
856
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
@@ -907,6 +909,8 @@ server.registerTool(
|
|
|
907
909
|
const lines = [
|
|
908
910
|
`Memory Statistics${args.project ? ` (project: ${args.project})` : ''}:`,
|
|
909
911
|
'',
|
|
912
|
+
// Env-aware data dir — parity with CLI cmdStats (D#92 wrong-path chain).
|
|
913
|
+
`Data dir: ${DB_DIR}`,
|
|
910
914
|
`Total: ${obsTotal.c} observations | ${sessTotal.c} sessions | ${promptTotal.c} prompts`,
|
|
911
915
|
`Last ${days}d: ${obsRecent.c} observations | ${sessRecent.c} sessions`,
|
|
912
916
|
'',
|