claude-mem-lite 5.1.0 → 5.1.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": "5.1.0",
13
+ "version": "5.1.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": "5.1.0",
3
+ "version": "5.1.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/install.mjs CHANGED
@@ -2545,6 +2545,12 @@ async function rebuildBinding() {
2545
2545
  const verify = await ensureBetterSqlite3Working(root);
2546
2546
  if (verify.ok) {
2547
2547
  ok(`better-sqlite3 binding ${verify.action} for Node ${process.version} — ${label} (${root})`);
2548
+ if (verify.quarantined) {
2549
+ // Say it out loud: the heal renamed a file inside the user's node_modules because
2550
+ // the shipped prebuild was present and would not load. A silent move inside a
2551
+ // dependency is the kind of thing that reads as corruption six months later.
2552
+ log(` the shipped prebuild would not load — moved aside to ${verify.quarantined}.unusable`);
2553
+ }
2548
2554
  } else {
2549
2555
  fail(`better-sqlite3 binding still unusable in ${label}: ${verify.error}`);
2550
2556
  log(`Try manually: ${nativeBindingRepairHint(root)}`);
@@ -7,6 +7,7 @@
7
7
  // sufficient — the binding can be present-but-stale after a Node upgrade.
8
8
 
9
9
  import { execSync, spawnSync } from 'node:child_process';
10
+ import { existsSync, renameSync } from 'node:fs';
10
11
  import { createRequire } from 'node:module';
11
12
  import { join } from 'node:path';
12
13
 
@@ -58,7 +59,16 @@ export const NATIVE_BINDING_SOURCE_BUILD_CMD = 'npm run --prefix node_modules/be
58
59
  export function nativeBindingRepairHint(root) {
59
60
  // Quoted: INSTALL_DIR / a plugin-cache root can contain spaces, and an unquoted `cd`
60
61
  // hands the user a command that fails on exactly the machines least able to debug it.
61
- return `cd "${root}" && ${NATIVE_BINDING_REBUILD_CMD} && ${NATIVE_BINDING_SOURCE_BUILD_CMD}`;
62
+ const npmPair = `cd "${root}" && ${NATIVE_BINDING_REBUILD_CMD} && ${NATIVE_BINDING_SOURCE_BUILD_CMD}`;
63
+ // The pair above heals a platform better-sqlite3 ships NO prebuild for. It cannot heal a
64
+ // prebuild that is present and will not load — 13 selects `prebuilds/<target>.node` on
65
+ // existence alone and prefers it over `build/`, so the addon those two commands compile
66
+ // stays shadowed. Only ensureBetterSqlite3Working moves the dead prebuild out of the way
67
+ // first, and `rebuild-binding` is how a human reaches it — so it goes FIRST, because a user
68
+ // runs the first command they are given and stops looking when it reports success.
69
+ const cli = join(root, 'cli.mjs');
70
+ if (!existsSync(cli)) return npmPair;
71
+ return `node "${cli}" rebuild-binding (or, without the CLI: ${npmPair})`;
62
72
  }
63
73
 
64
74
  // Set on a re-exec'd child so one failed heal cannot fork-bomb the CLI.
@@ -193,6 +203,45 @@ export function probeBindingInFreshProcess(installDir, { timeoutMs = 30_000 } =
193
203
  };
194
204
  }
195
205
 
206
+ // Suffix for a prebuilt addon that is present and will not load. Renamed, never deleted:
207
+ // the file is evidence for whoever debugs the machine, and `npm install` puts a fresh
208
+ // prebuild back at the original name regardless.
209
+ const UNUSABLE_PREBUILD_SUFFIX = '.unusable';
210
+
211
+ /**
212
+ * The prebuilt addon better-sqlite3 would choose under `installDir`, or null.
213
+ *
214
+ * ASKED OF THE DEPENDENCY, never computed here. Which file gets loaded depends on
215
+ * platform, arch and a musl probe, and this repo has already paid twice for guessing it:
216
+ * `tests/install-bsqlite-probe.test.mjs` matched a v12 filename that v13 does not produce
217
+ * (its main assertion went vacuously true), and both sandbox phases corrupted
218
+ * `build/Release/better_sqlite3.node` for a year of releases while the resolver loaded a
219
+ * prebuild. `lib/binding.js` exports the selector itself, so use it.
220
+ *
221
+ * Out of process, like every other probe here: this runs on a path that is about to dlopen
222
+ * the result, and `getPrebuildPath()` is existence-only today but is not ours to promise.
223
+ * `lib/binding.js` is absent from the package's `exports` map, hence the file path.
224
+ *
225
+ * @param {string} installDir Directory whose node_modules holds better-sqlite3
226
+ * @returns {string|null}
227
+ */
228
+ function resolvedPrebuildPath(installDir) {
229
+ const bindingJs = join(installDir, 'node_modules', 'better-sqlite3', 'lib', 'binding.js');
230
+ if (!existsSync(bindingJs)) return null;
231
+ const r = spawnSync(
232
+ process.execPath,
233
+ [
234
+ '-e',
235
+ `const b=require(${JSON.stringify(bindingJs)});` +
236
+ `process.stdout.write((b.getPrebuildPath&&b.getPrebuildPath())||'')`,
237
+ ],
238
+ { stdio: 'pipe', encoding: 'utf8', timeout: 10_000 },
239
+ );
240
+ if (r.error || r.status !== 0) return null;
241
+ const p = String(r.stdout || '').trim();
242
+ return p && existsSync(p) ? p : null;
243
+ }
244
+
196
245
  /**
197
246
  * Verify better-sqlite3 binding works in `installDir`; if not, run
198
247
  * `npm rebuild better-sqlite3` and re-probe. Returns
@@ -295,15 +344,52 @@ export async function ensureBetterSqlite3Working(installDir, deps = {}) {
295
344
  : () => exec(NATIVE_BINDING_SOURCE_BUILD_CMD, { cwd: installDir, stdio: 'pipe' }));
296
345
  if (!sourceBuild) return { ok: false, error: second.error || first.error };
297
346
 
347
+ // A prebuild that is present and unloadable SHADOWS everything the source build is about
348
+ // to produce: better-sqlite3 13 picks `prebuilds/<target>.node` on existence alone and
349
+ // prefers it over `build/`. Measured 2026-09-06 with a control — corrupt prebuild +
350
+ // healthy build/Release → `wrong ELF class`; the same tree with the prebuild moved aside →
351
+ // opens; with neither → fails. Until this, `rebuild-binding` (the foreground repair doctor
352
+ // prints, deliberately given no time budget) exited 1 on that shape and the manual command
353
+ // it offered instead could not fix it either. Reproduced end-to-end in
354
+ // tests/sandbox/phaseB-npm.mjs before the fix.
355
+ //
356
+ // Deliberately inside this branch and after the npm step: quarantining without a compile to
357
+ // follow it turns "broken addon" into "no addon", which is strictly worse — so the
358
+ // time-budgeted SessionStart path (sourceBuild: false) never reaches this.
359
+ const shadowingPrebuild = resolvedPrebuildPath(installDir);
360
+ let quarantined = null;
361
+ if (shadowingPrebuild) {
362
+ try {
363
+ renameSync(shadowingPrebuild, shadowingPrebuild + UNUSABLE_PREBUILD_SUFFIX);
364
+ quarantined = shadowingPrebuild;
365
+ } catch {
366
+ // A read-only or otherwise unwritable tree. The compile below is still worth trying:
367
+ // on a platform with no prebuild it is the whole fix, and failing here would turn a
368
+ // recoverable case into a hard stop.
369
+ }
370
+ }
371
+ /** Put the tree back exactly as found — an install we could not repair must not lose a file. */
372
+ const restorePrebuild = () => {
373
+ if (!quarantined) return;
374
+ try {
375
+ renameSync(quarantined + UNUSABLE_PREBUILD_SUFFIX, quarantined);
376
+ } catch {
377
+ // Nothing better to do; the error the caller gets already says the heal failed.
378
+ }
379
+ };
380
+
298
381
  try {
299
382
  await sourceBuild();
300
383
  } catch (e) {
384
+ restorePrebuild();
301
385
  return { ok: false, error: `source build failed: ${e.message}` };
302
386
  }
303
387
 
304
388
  const third = await verify();
305
- if (third.ok) return { ok: true, action: 'compiled' };
389
+ if (third.ok)
390
+ return quarantined ? { ok: true, action: 'compiled', quarantined } : { ok: true, action: 'compiled' };
306
391
 
392
+ restorePrebuild();
307
393
  return { ok: false, error: third.error || second.error || first.error };
308
394
  }
309
395
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "5.1.0",
3
+ "version": "5.1.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "claude-mem-lite",
9
- "version": "5.1.0",
9
+ "version": "5.1.1",
10
10
  "os": [
11
11
  "darwin",
12
12
  "linux"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "5.1.0",
3
+ "version": "5.1.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",
package/scripts/setup.sh CHANGED
@@ -190,7 +190,20 @@ if [[ -d "$ROOT/node_modules/better-sqlite3" ]]; then
190
190
  # Both commands, `&&` not `||`: `npm rebuild` exits 0 without compiling on
191
191
  # better-sqlite3 13 (no install script to run), so it never signals failure and
192
192
  # an `||` chain would never reach the source build. A20260906-R8-P1-1.
193
- mark_deps_broken "better-sqlite3 binding probe/rebuild failed (npm >= 12 blocks compile scripts by default)" "npm rebuild better-sqlite3 --dangerously-allow-all-scripts && npm run --prefix node_modules/better-sqlite3 build-release"
193
+ #
194
+ # …and the pair alone is still not enough. It heals a platform 13 ships no prebuild
195
+ # for; it cannot heal a prebuild that is PRESENT and will not load, because 13 prefers
196
+ # `prebuilds/<target>.node` over anything the source build produces. This string is what
197
+ # the SessionStart dashboard prints as `Repair:`, so it leads with the CLI, which is the
198
+ # only path that moves the dead prebuild aside first (lib/binding-probe.mjs). Mirrors
199
+ # nativeBindingRepairHint(); this file may not import lib/, hence the duplication —
200
+ # tests/audit-r8-binding-repair-hint.test.mjs pins the set of files allowed to do that.
201
+ # mark_deps_broken already prefixes `cd <root> && `, so these are root-relative.
202
+ NB_REPAIR="npm rebuild better-sqlite3 --dangerously-allow-all-scripts && npm run --prefix node_modules/better-sqlite3 build-release"
203
+ if [[ -f "$ROOT/cli.mjs" ]]; then
204
+ NB_REPAIR="node cli.mjs rebuild-binding (or, without the CLI: $NB_REPAIR)"
205
+ fi
206
+ mark_deps_broken "better-sqlite3 binding probe/rebuild failed (npm >= 12 blocks compile scripts by default)" "$NB_REPAIR"
194
207
  fi
195
208
  fi
196
209