claude-mem-lite 2.50.0 → 2.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": "2.50.0",
13
+ "version": "2.51.0",
14
14
  "source": "./",
15
15
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall"
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.50.0",
3
+ "version": "2.51.0",
4
4
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall",
5
5
  "author": {
6
6
  "name": "sdsrss"
package/install.mjs CHANGED
@@ -25,10 +25,115 @@ const MARKETPLACE_KEY = 'sdsrss';
25
25
  const PLUGIN_KEY = `claude-mem-lite@${MARKETPLACE_KEY}`;
26
26
  const NPM_INSTALL_CMD = 'npm install --omit=dev --no-audit --no-fund';
27
27
 
28
+ import { createRequire } from 'module';
29
+
28
30
  import { RESOURCE_METADATA } from './install-metadata.mjs';
29
31
  import { scanPluginCacheHookPollution } from './plugin-cache-guard.mjs';
30
32
  import { SOURCE_FILES } from './source-files.mjs';
31
33
 
34
+ /**
35
+ * Hook scripts that non-dev install must copy into ~/.claude-mem-lite/scripts/
36
+ * to keep settings.json hook commands resolvable. Single source of truth so
37
+ * adding a new PreToolUse/PostToolUse hook script can't drift from the install
38
+ * copy block (which previously hand-listed only 3 of these and silently
39
+ * dropped pre-tool-recall.js + pre-skill-bridge.js — every fresh install left
40
+ * settings.json pointing at non-existent files).
41
+ */
42
+ export const HOOK_SCRIPT_FILES = [
43
+ 'post-tool-use.sh',
44
+ 'user-prompt-search.js',
45
+ 'prompt-search-utils.mjs',
46
+ 'pre-tool-recall.js',
47
+ 'pre-skill-bridge.js',
48
+ ];
49
+
50
+ export function copyHookScripts(srcDir, destDir) {
51
+ for (const name of HOOK_SCRIPT_FILES) {
52
+ const src = join(srcDir, name);
53
+ if (existsSync(src)) copyFileSync(src, join(destDir, name));
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Move legacy `~/.claude-mem/claude-mem.db` (+ -wal/-shm sidecars) to
59
+ * timestamped `*.legacy-backup-<ms>` files inside `newDir`. The legacy DB
60
+ * carries v16 schema (schema_versions plural table); the new claude-mem-lite
61
+ * code expects v28 (schema_version singular + memory_session_id column) and
62
+ * MIGRATIONS[] has no v16→v28 bridge — so loading the legacy DB FATALs on
63
+ * first launch. Backing up rather than copying-as-current lets the new
64
+ * install create a fresh v28 DB while preserving legacy bytes for recovery.
65
+ *
66
+ * Returns: {action: 'noop'|'skip'|'backed-up', backupPath?}
67
+ * - noop: no legacy DB found
68
+ * - skip: working `claude-mem-lite.db` already exists in newDir
69
+ * - backed-up: legacy files renamed to `<newDir>/claude-mem-lite.db.legacy-backup-<ts>` etc.
70
+ */
71
+ export function migrateLegacyClaudeMemData(oldDir, newDir, opts = {}) {
72
+ const legacyDb = join(oldDir, 'claude-mem.db');
73
+ const targetDb = join(newDir, 'claude-mem-lite.db');
74
+ if (!existsSync(legacyDb)) return { action: 'noop' };
75
+ if (existsSync(targetDb)) return { action: 'skip' };
76
+
77
+ if (!existsSync(newDir)) mkdirSync(newDir, { recursive: true });
78
+ const ts = opts.now ?? Date.now();
79
+ const backupPath = join(newDir, `claude-mem-lite.db.legacy-backup-${ts}`);
80
+ renameSync(legacyDb, backupPath);
81
+ for (const ext of ['-wal', '-shm']) {
82
+ const src = legacyDb + ext;
83
+ if (existsSync(src)) renameSync(src, join(newDir, `claude-mem-lite.db${ext}.legacy-backup-${ts}`));
84
+ }
85
+ return { action: 'backed-up', backupPath };
86
+ }
87
+
88
+ /**
89
+ * Probe better-sqlite3's native binding by importing it from `installDir`'s
90
+ * node_modules and opening an in-memory DB. Returns {ok, error?}. `npm install`
91
+ * exits 0 even when the prebuilt .node binary mismatches the running Node ABI
92
+ * (e.g. NODE_MODULE_VERSION 137 on Node v24), so install must verify before
93
+ * declaring success — otherwise the next launch FATALs with "Could not locate
94
+ * the bindings file".
95
+ */
96
+ export async function probeBetterSqlite3Binding(installDir) {
97
+ try {
98
+ const localRequire = createRequire(join(installDir, 'package.json'));
99
+ const Database = localRequire('better-sqlite3');
100
+ const db = new Database(':memory:');
101
+ db.close();
102
+ return { ok: true };
103
+ } catch (e) {
104
+ return { ok: false, error: e.message };
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Verify better-sqlite3 binding works in `installDir`; if not, run
110
+ * `npm rebuild better-sqlite3` and re-probe. Returns
111
+ * { ok: true, action: 'verified' | 'rebuilt' } on success or
112
+ * { ok: false, error } if rebuild can't fix it. The `probe` and `rebuild`
113
+ * deps are injectable so this can be unit-tested without a real npm
114
+ * subprocess.
115
+ */
116
+ export async function ensureBetterSqlite3Working(installDir, deps = {}) {
117
+ const probe = deps.probe || (() => probeBetterSqlite3Binding(installDir));
118
+ const rebuild = deps.rebuild || (async () => {
119
+ execSync('npm rebuild better-sqlite3', { cwd: installDir, stdio: 'pipe' });
120
+ });
121
+
122
+ const first = await probe();
123
+ if (first.ok) return { ok: true, action: 'verified' };
124
+
125
+ try {
126
+ await rebuild();
127
+ } catch (e) {
128
+ return { ok: false, error: `rebuild failed: ${e.message}` };
129
+ }
130
+
131
+ const second = await probe();
132
+ if (second.ok) return { ok: true, action: 'rebuilt' };
133
+
134
+ return { ok: false, error: second.error || first.error };
135
+ }
136
+
32
137
  /**
33
138
  * Derive invocation_name from resource name when metadata doesn't provide one.
34
139
  * Rules:
@@ -265,13 +370,9 @@ async function install() {
265
370
  copyFileSync(src, dst);
266
371
  }
267
372
  }
268
- // Copy scripts
269
- const postToolSrc = join(PROJECT_DIR, 'scripts', 'post-tool-use.sh');
270
- if (existsSync(postToolSrc)) copyFileSync(postToolSrc, join(scriptsDir, 'post-tool-use.sh'));
271
- const promptSearchSrc = join(PROJECT_DIR, 'scripts', 'user-prompt-search.js');
272
- if (existsSync(promptSearchSrc)) copyFileSync(promptSearchSrc, join(scriptsDir, 'user-prompt-search.js'));
273
- const promptSearchUtilsSrc = join(PROJECT_DIR, 'scripts', 'prompt-search-utils.mjs');
274
- if (existsSync(promptSearchUtilsSrc)) copyFileSync(promptSearchUtilsSrc, join(scriptsDir, 'prompt-search-utils.mjs'));
373
+ // Copy hook scripts (settings.json hook commands point at these — must
374
+ // stay in sync with HOOK_SCRIPT_FILES manifest)
375
+ copyHookScripts(join(PROJECT_DIR, 'scripts'), scriptsDir);
275
376
  // Ensure bash script is executable
276
377
  try { execFileSync('chmod', ['+x', join(scriptsDir, 'post-tool-use.sh')], { stdio: 'pipe' }); } catch {}
277
378
  // Copy commands directory
@@ -314,6 +415,18 @@ async function install() {
314
415
  fail('npm install failed: ' + e.message);
315
416
  process.exit(1);
316
417
  }
418
+ // npm install exits 0 even when the better-sqlite3 prebuilt .node binary
419
+ // mismatches the running Node ABI (e.g. NODE_MODULE_VERSION 137 on Node v24).
420
+ // Probe and auto-rebuild before declaring success — otherwise the next
421
+ // launch FATALs with "Could not locate the bindings file".
422
+ const verify = await ensureBetterSqlite3Working(INSTALL_DIR);
423
+ if (verify.ok) {
424
+ ok(`better-sqlite3: ${verify.action}`);
425
+ } else {
426
+ fail(`better-sqlite3 binding unusable after rebuild: ${verify.error}`);
427
+ log('Try manually: cd ' + INSTALL_DIR + ' && npm rebuild better-sqlite3 --build-from-source');
428
+ process.exit(1);
429
+ }
317
430
  }
318
431
 
319
432
  // 2b. Create global CLI symlink (claude-mem-lite command)
@@ -542,30 +655,19 @@ async function install() {
542
655
  writeSettings(settings);
543
656
  ok('Hooks configured (PreToolUse, PostToolUse, SessionStart, Stop, UserPromptSubmit)');
544
657
 
545
- // 5. Migrate from old ~/.claude-mem/ if needed
546
- if (existsSync(join(OLD_DATA_DIR, 'claude-mem.db')) && !existsSync(DB_PATH) && !existsSync(join(DATA_DIR, 'claude-mem.db'))) {
547
- log('Detected old ~/.claude-mem/ directory, migrating to ~/.claude-mem-lite/...');
548
- try {
549
- if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
550
- // Migrate database and WAL/SHM files (copy as claude-mem-lite.db)
551
- const srcDb = join(OLD_DATA_DIR, 'claude-mem.db');
552
- if (existsSync(srcDb)) copyFileSync(srcDb, DB_PATH);
553
- for (const ext of ['-wal', '-shm']) {
554
- const src = join(OLD_DATA_DIR, 'claude-mem.db' + ext);
555
- if (existsSync(src)) copyFileSync(src, DB_PATH + ext);
556
- }
557
- // Migrate runtime directory
558
- const oldRuntime = join(OLD_DATA_DIR, 'runtime');
559
- const newRuntime = join(DATA_DIR, 'runtime');
560
- if (existsSync(oldRuntime) && !existsSync(newRuntime)) {
561
- cpSync(oldRuntime, newRuntime, { recursive: true });
562
- }
563
- ok('Data migrated from ~/.claude-mem/ → ~/.claude-mem-lite/');
564
- log('Old ~/.claude-mem/ preserved (remove manually when ready)');
565
- } catch (e) {
566
- warn('Migration failed: ' + e.message);
567
- log('You can copy manually: cp ~/.claude-mem/claude-mem.db ~/.claude-mem-lite/claude-mem-lite.db');
658
+ // 5. Legacy ~/.claude-mem/ ~/.claude-mem-lite/ back up, don't reuse.
659
+ // The legacy DB is schema v16 (schema_versions plural) and there's no
660
+ // bridge in MIGRATIONS[] to v28. Reusing it FATALs on first launch with
661
+ // "no such column: memory_session_id". Rename to a timestamped backup
662
+ // so the new install creates a fresh v28 DB.
663
+ try {
664
+ const r = migrateLegacyClaudeMemData(OLD_DATA_DIR, DATA_DIR);
665
+ if (r.action === 'backed-up') {
666
+ ok(`Legacy ~/.claude-mem/ DB backed up to ${r.backupPath}`);
667
+ log('New v28 DB will be created on first launch (legacy schema is incompatible).');
568
668
  }
669
+ } catch (e) {
670
+ warn('Legacy DB backup failed: ' + e.message);
569
671
  }
570
672
 
571
673
  // 5b. Rename claude-mem.db → claude-mem-lite.db in same directory
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.50.0",
3
+ "version": "2.51.0",
4
4
  "description": "Lightweight persistent memory system for Claude Code",
5
5
  "type": "module",
6
6
  "engines": {