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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "claude-mem-lite",
13
- "version": "3.50.0",
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.50.0",
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
- if (bashSig?.isError) {
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
  }
@@ -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
- const pTag = r.priority === 3 ? '🔴' : r.priority === 1 ? '⚪' : '🟡';
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.50.0",
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
- const pTag = r.priority === 3 ? '🔴' : r.priority === 1 ? '⚪' : '🟡';
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
  '',