claude-mem-lite 3.16.0 → 3.16.1

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.16.0",
13
+ "version": "3.16.1",
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.16.0",
3
+ "version": "3.16.1",
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-optimize.mjs CHANGED
@@ -203,10 +203,22 @@ search_aliases: 2-6 alternative search terms (include CJK if applicable).`;
203
203
  const NORMALIZE_GATE_FILE = join(RUNTIME_DIR, 'last-normalize.json');
204
204
  const NORMALIZE_INTERVAL_MS = 7 * 86400000; // 7 days
205
205
 
206
+ // Pure gate decision (no IO) — exported for testing. Fail-OPEN on a
207
+ // malformed-but-valid-JSON gate: a missing/non-numeric `epoch` makes
208
+ // `now - epoch` NaN, and `NaN >= INTERVAL` is false — which would PERMANENTLY
209
+ // block normalize with no recovery, contradicting the catch-branch's fail-open
210
+ // intent (a corrupt file that fails JSON.parse already returns true). A future
211
+ // epoch (clock skew / NTP correction) is equally suspect → run.
212
+ export function _normalizeGateOpen(last, now) {
213
+ const epoch = last?.epoch;
214
+ if (typeof epoch !== 'number' || !Number.isFinite(epoch) || epoch > now) return true;
215
+ return now - epoch >= NORMALIZE_INTERVAL_MS;
216
+ }
217
+
206
218
  export function shouldRunNormalize() {
207
219
  try {
208
220
  const last = JSON.parse(readFileSync(NORMALIZE_GATE_FILE, 'utf8'));
209
- return Date.now() - last.epoch >= NORMALIZE_INTERVAL_MS;
221
+ return _normalizeGateOpen(last, Date.now());
210
222
  } catch {
211
223
  return true;
212
224
  }
package/mem-cli.mjs CHANGED
@@ -1636,12 +1636,26 @@ function cmdRestore(db, argv) {
1636
1636
  const trimmed = raw.trim();
1637
1637
  if (!trimmed) { out('[mem] Empty file — nothing to restore.'); return; }
1638
1638
  let rows;
1639
- try {
1640
- rows = trimmed[0] === '['
1641
- ? JSON.parse(trimmed)
1642
- : trimmed.split('\n').filter(l => l.trim()).map(l => JSON.parse(l));
1643
- } catch (e) { fail(`[mem] "${file}" is not valid export JSON/JSONL: ${e.message}`); return; }
1644
- if (!Array.isArray(rows) || rows.length === 0) { out('[mem] No observations in file.'); return; }
1639
+ let parseFailures = 0;
1640
+ if (trimmed[0] === '[') {
1641
+ // Whole-array JSON is a single document — partial parse is impossible, so
1642
+ // a syntax error rejects the file (unchanged behavior).
1643
+ try { rows = JSON.parse(trimmed); }
1644
+ catch (e) { fail(`[mem] "${file}" is not valid export JSON/JSONL: ${e.message}`); return; }
1645
+ } else {
1646
+ // JSONL: tolerate per-line syntax errors so one corrupt line in a large
1647
+ // backup doesn't discard every valid row. Parse failures fold into the
1648
+ // malformed tally below (the loop already skips valid-JSON-but-wrong-shape
1649
+ // rows the same way) — a backup tool must recover what it can.
1650
+ rows = [];
1651
+ for (const line of trimmed.split('\n')) {
1652
+ if (!line.trim()) continue;
1653
+ try { rows.push(JSON.parse(line)); }
1654
+ catch { parseFailures++; }
1655
+ }
1656
+ }
1657
+ if (!Array.isArray(rows) || (rows.length === 0 && parseFailures === 0)) { out('[mem] No observations in file.'); return; }
1658
+ if (rows.length === 0) { fail(`[mem] "${file}": all ${parseFailures} line(s) failed to parse as JSONL.`); return; }
1645
1659
 
1646
1660
  const projOverride = flags.project ? resolveProject(db, flags.project) : null;
1647
1661
  const dryRun = flags['dry-run'] === true || flags['dry-run'] === 'true';
@@ -1693,7 +1707,12 @@ function cmdRestore(db, argv) {
1693
1707
  if (process.env.CLAUDE_MEM_DEBUG) process.stderr.write(`[mem] restore row failed: ${e.message}\n`);
1694
1708
  }
1695
1709
  }
1696
- out(`[mem] Restore${dryRun ? ' (dry-run)' : ''}: ${restored} restored, ${skipped} duplicate(s) skipped, ${malformed} malformed/failed from ${rows.length} row(s).`);
1710
+ // Fold JSONL per-line syntax failures into the malformed tally and the
1711
+ // denominator so the report reflects every non-blank input line, not just the
1712
+ // ones that parsed.
1713
+ const totalMalformed = malformed + parseFailures;
1714
+ const totalLines = rows.length + parseFailures;
1715
+ out(`[mem] Restore${dryRun ? ' (dry-run)' : ''}: ${restored} restored, ${skipped} duplicate(s) skipped, ${totalMalformed} malformed/failed from ${totalLines} row(s).`);
1697
1716
  }
1698
1717
 
1699
1718
  // ─── Compress ────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "3.16.0",
3
+ "version": "3.16.1",
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",