claude-mem-lite 3.99.0 → 4.0.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/hook-optimize.mjs +3 -3
- package/install.mjs +77 -31
- package/lib/binding-probe.mjs +69 -3
- package/lib/install-shape.mjs +2 -2
- package/npm-shrinkwrap.json +248 -791
- package/package.json +5 -5
- package/scripts/hook-launcher.mjs +9 -3
- package/scripts/launch.mjs +3 -4
- package/scripts/setup.sh +4 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "claude-mem-lite",
|
|
13
|
-
"version": "
|
|
13
|
+
"version": "4.0.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
|
+
"version": "4.0.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
|
@@ -169,7 +169,7 @@ export function findReenrichCandidates(db, limit = 10, { scope = 'narrow', proje
|
|
|
169
169
|
const stmt = db.prepare(`
|
|
170
170
|
SELECT id, title, narrative, type, subtitle, importance, project
|
|
171
171
|
FROM observations
|
|
172
|
-
WHERE
|
|
172
|
+
WHERE ${liveObsFilterSql('')}
|
|
173
173
|
AND (concepts IS NULL OR concepts = '')
|
|
174
174
|
AND (facts IS NULL OR facts = '')
|
|
175
175
|
AND lesson_learned IS NULL
|
|
@@ -468,7 +468,7 @@ export function extractUniqueConcepts(db, limit = 500, { project } = {}) {
|
|
|
468
468
|
const projectClause = project ? 'AND project = ?' : '';
|
|
469
469
|
const stmt = db.prepare(`
|
|
470
470
|
SELECT concepts FROM observations
|
|
471
|
-
WHERE
|
|
471
|
+
WHERE ${liveObsFilterSql('')}
|
|
472
472
|
AND concepts IS NOT NULL AND concepts != ''
|
|
473
473
|
${projectClause}
|
|
474
474
|
ORDER BY created_at_epoch DESC
|
|
@@ -536,7 +536,7 @@ export function applyNormalization(db, groups, { project = null } = {}) {
|
|
|
536
536
|
.prepare(
|
|
537
537
|
`
|
|
538
538
|
SELECT id, title, narrative, concepts, search_aliases, lesson_learned FROM observations
|
|
539
|
-
WHERE
|
|
539
|
+
WHERE ${liveObsFilterSql('')}
|
|
540
540
|
AND concepts IS NOT NULL AND concepts != ''
|
|
541
541
|
AND (? IS NULL OR project = ?)
|
|
542
542
|
`,
|
package/install.mjs
CHANGED
|
@@ -68,7 +68,7 @@ import { SOURCE_FILES, HOOK_SCRIPT_FILES } from './source-files.mjs';
|
|
|
68
68
|
import {
|
|
69
69
|
probeBetterSqlite3Binding,
|
|
70
70
|
ensureBetterSqlite3Working,
|
|
71
|
-
|
|
71
|
+
nativeBindingRepairHint,
|
|
72
72
|
} from './lib/binding-probe.mjs';
|
|
73
73
|
import { detectInstallShape, probeRuntimeRoots } from './lib/install-shape.mjs';
|
|
74
74
|
import { clearNativeBindingBreakage, readNativeBindingBreakage } from './lib/native-binding-hint.mjs';
|
|
@@ -404,6 +404,57 @@ export function patchClaudeMdVersion(text, version) {
|
|
|
404
404
|
// at the same red line. Claude Code also seeds a NEW cache version from that same
|
|
405
405
|
// emptied clone. So check the source before prescribing it, and fall back to a
|
|
406
406
|
// reinstall — which re-clones the manifest from the repo — when it is empty too.
|
|
407
|
+
/**
|
|
408
|
+
* Clear whatever occupies `p` — file, directory, or symlink INCLUDING a dangling one —
|
|
409
|
+
* before a symlink is written over it. Returns true when something was removed.
|
|
410
|
+
*
|
|
411
|
+
* A20260906-R8-P2-3: every call site used to gate on `existsSync(p)`, which FOLLOWS the
|
|
412
|
+
* link, so a dangling symlink reads as absent. Two consequences, and the second is the
|
|
413
|
+
* one that bites: `uninstall` leaves a dead `claude-mem-lite` on PATH, and
|
|
414
|
+
* `createCliSymlink` skips the removal, `symlinkSync` throws EEXIST, the catch falls back
|
|
415
|
+
* to an unwritable /usr/local/bin, and the user is told "CLI symlink failed — run
|
|
416
|
+
* manually". Re-running `install` — the documented repair — cannot repair it. The same
|
|
417
|
+
* file already knew the idiom: isDevInstall() pairs existsSync with lstatSync.
|
|
418
|
+
*
|
|
419
|
+
* rmSync rather than unlinkSync because the dev-mode sites link DIRECTORIES; verified it
|
|
420
|
+
* removes the link and leaves the target intact (a link to a populated dir, target still
|
|
421
|
+
* readable afterwards) — following it would delete the developer's own scripts/.
|
|
422
|
+
*
|
|
423
|
+
* @param {string} p
|
|
424
|
+
* @returns {boolean}
|
|
425
|
+
*/
|
|
426
|
+
export function clearLinkPath(p) {
|
|
427
|
+
try {
|
|
428
|
+
lstatSync(p);
|
|
429
|
+
} catch {
|
|
430
|
+
return false; // genuinely absent — nothing to clear
|
|
431
|
+
}
|
|
432
|
+
try {
|
|
433
|
+
rmSync(p, { recursive: true, force: true });
|
|
434
|
+
return true;
|
|
435
|
+
} catch {
|
|
436
|
+
return false; // permissions; callers fall back or report their own failure
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Minimum supported Node MAJOR, parsed out of a `package.json#engines.node` range.
|
|
442
|
+
*
|
|
443
|
+
* A20260906-R8-P2-1: doctor carried its own `>= 18` literal, so v4.0.0 raised the real
|
|
444
|
+
* floor to 22 (npm refuses to install below it, and better-sqlite3 13 ships no prebuild
|
|
445
|
+
* for those Nodes) while doctor kept printing `✓ Node.js: v20.x` — greenlighting the
|
|
446
|
+
* runtime that WAS the fault, in the one tool a broken user is told to run. One source,
|
|
447
|
+
* so the two cannot drift again; the fallback only covers an unreadable manifest.
|
|
448
|
+
*
|
|
449
|
+
* @param {unknown} enginesNode e.g. '>=22' or '^22.12.0 || ^24.0.0 || >=26.0.0'
|
|
450
|
+
* @param {number} [fallback]
|
|
451
|
+
* @returns {number}
|
|
452
|
+
*/
|
|
453
|
+
export function requiredNodeMajor(enginesNode, fallback = 22) {
|
|
454
|
+
const m = /(\d+)/.exec(String(enginesNode ?? ''));
|
|
455
|
+
return m ? Number(m[1]) : fallback;
|
|
456
|
+
}
|
|
457
|
+
|
|
407
458
|
export function hookManifestRepairHint(cacheRoot, marketplaceRoot) {
|
|
408
459
|
const src = join(marketplaceRoot, 'hooks', 'hooks.json');
|
|
409
460
|
const dst = join(cacheRoot, 'hooks', 'hooks.json');
|
|
@@ -513,34 +564,22 @@ function installSourceFiles(IS_DEV) {
|
|
|
513
564
|
// Ensure parent dir exists for subdir entries (e.g. 'lib/activity.mjs')
|
|
514
565
|
const linkParent = dirname(link);
|
|
515
566
|
if (!existsSync(linkParent)) mkdirSync(linkParent, { recursive: true });
|
|
516
|
-
// Remove existing file/symlink before creating
|
|
517
|
-
|
|
518
|
-
try {
|
|
519
|
-
unlinkSync(link);
|
|
520
|
-
} catch {}
|
|
567
|
+
// Remove existing file/symlink (including a dangling one) before creating.
|
|
568
|
+
clearLinkPath(link);
|
|
521
569
|
symlinkSync(target, link);
|
|
522
570
|
}
|
|
523
571
|
}
|
|
524
572
|
// Symlink scripts/ directory
|
|
525
573
|
const scriptsLink = join(DATA_DIR, 'scripts');
|
|
526
|
-
|
|
527
|
-
try {
|
|
528
|
-
rmSync(scriptsLink, { recursive: true, force: true });
|
|
529
|
-
} catch {}
|
|
574
|
+
clearLinkPath(scriptsLink);
|
|
530
575
|
symlinkSync(join(PROJECT_DIR, 'scripts'), scriptsLink);
|
|
531
576
|
// Symlink node_modules/
|
|
532
577
|
const nmLink = join(DATA_DIR, 'node_modules');
|
|
533
|
-
|
|
534
|
-
try {
|
|
535
|
-
rmSync(nmLink, { recursive: true, force: true });
|
|
536
|
-
} catch {}
|
|
578
|
+
clearLinkPath(nmLink);
|
|
537
579
|
symlinkSync(join(PROJECT_DIR, 'node_modules'), nmLink);
|
|
538
580
|
// Symlink registry/ directory
|
|
539
581
|
const regLink = join(DATA_DIR, 'registry');
|
|
540
|
-
|
|
541
|
-
try {
|
|
542
|
-
rmSync(regLink, { recursive: true, force: true });
|
|
543
|
-
} catch {}
|
|
582
|
+
clearLinkPath(regLink);
|
|
544
583
|
if (existsSync(join(PROJECT_DIR, 'registry'))) {
|
|
545
584
|
symlinkSync(join(PROJECT_DIR, 'registry'), regLink);
|
|
546
585
|
}
|
|
@@ -620,9 +659,7 @@ async function installDependencies(IS_DEV) {
|
|
|
620
659
|
ok(`better-sqlite3: ${verify.action}`);
|
|
621
660
|
} else {
|
|
622
661
|
fail(`better-sqlite3 binding unusable after rebuild: ${verify.error}`);
|
|
623
|
-
log(
|
|
624
|
-
'Try manually: cd ' + INSTALL_DIR + ' && npm rebuild better-sqlite3 --dangerously-allow-all-scripts',
|
|
625
|
-
);
|
|
662
|
+
log(`Try manually: ${nativeBindingRepairHint(INSTALL_DIR)}`);
|
|
626
663
|
process.exit(1);
|
|
627
664
|
}
|
|
628
665
|
|
|
@@ -643,7 +680,7 @@ async function installDependencies(IS_DEV) {
|
|
|
643
680
|
`better-sqlite3 unusable in the package this installer runs from (${PROJECT_DIR}): ${selfVerify.error}`,
|
|
644
681
|
);
|
|
645
682
|
log(
|
|
646
|
-
` The install itself is fine; the \`claude-mem-lite\` shell command will self-heal on first use, or run:
|
|
683
|
+
` The install itself is fine; the \`claude-mem-lite\` shell command will self-heal on first use, or run: ${nativeBindingRepairHint(PROJECT_DIR)}`,
|
|
647
684
|
);
|
|
648
685
|
}
|
|
649
686
|
}
|
|
@@ -662,14 +699,14 @@ function createCliSymlink() {
|
|
|
662
699
|
const cliLink = join(localBin, 'claude-mem-lite');
|
|
663
700
|
try {
|
|
664
701
|
if (!existsSync(localBin)) mkdirSync(localBin, { recursive: true });
|
|
665
|
-
|
|
702
|
+
clearLinkPath(cliLink);
|
|
666
703
|
symlinkSync(cliSource, cliLink);
|
|
667
704
|
ok(`CLI: ${cliLink} → ${cliSource}`);
|
|
668
705
|
} catch {
|
|
669
706
|
// Fallback: try /usr/local/bin (may need sudo)
|
|
670
707
|
try {
|
|
671
708
|
const globalLink = '/usr/local/bin/claude-mem-lite';
|
|
672
|
-
|
|
709
|
+
clearLinkPath(globalLink);
|
|
673
710
|
symlinkSync(cliSource, globalLink);
|
|
674
711
|
ok(`CLI: ${globalLink} → ${cliSource}`);
|
|
675
712
|
} catch {
|
|
@@ -1545,8 +1582,7 @@ async function uninstall() {
|
|
|
1545
1582
|
for (const binDir of [join(homedir(), '.local', 'bin'), '/usr/local/bin']) {
|
|
1546
1583
|
const cliLink = join(binDir, 'claude-mem-lite');
|
|
1547
1584
|
try {
|
|
1548
|
-
if (
|
|
1549
|
-
unlinkSync(cliLink);
|
|
1585
|
+
if (clearLinkPath(cliLink)) {
|
|
1550
1586
|
ok(`CLI symlink removed: ${cliLink}`);
|
|
1551
1587
|
}
|
|
1552
1588
|
} catch {
|
|
@@ -1901,12 +1937,22 @@ async function doctor() {
|
|
|
1901
1937
|
warn(msg);
|
|
1902
1938
|
};
|
|
1903
1939
|
|
|
1904
|
-
// Node version
|
|
1940
|
+
// Node version. The floor is read from package.json#engines rather than restated here —
|
|
1941
|
+
// see requiredNodeMajor. It is PRINTED on the ok line too, so the guard test has something
|
|
1942
|
+
// to compare against the manifest; a floor nobody can observe is a floor nobody notices
|
|
1943
|
+
// has gone stale, which is exactly how the `>= 18` literal outlived the v4.0.0 bump.
|
|
1905
1944
|
const nodeVer = process.version;
|
|
1906
|
-
|
|
1907
|
-
|
|
1945
|
+
let enginesNode = null;
|
|
1946
|
+
try {
|
|
1947
|
+
enginesNode = JSON.parse(readFileSync(join(PROJECT_DIR, 'package.json'), 'utf8')).engines?.node;
|
|
1948
|
+
} catch {
|
|
1949
|
+
/* unreadable manifest → requiredNodeMajor's fallback */
|
|
1950
|
+
}
|
|
1951
|
+
const nodeFloor = requiredNodeMajor(enginesNode);
|
|
1952
|
+
if (parseInt(nodeVer.slice(1)) >= nodeFloor) {
|
|
1953
|
+
ok(`Node.js: ${nodeVer} (>=${nodeFloor} required)`);
|
|
1908
1954
|
} else {
|
|
1909
|
-
fail(`Node.js ${nodeVer} too old (need
|
|
1955
|
+
fail(`Node.js ${nodeVer} too old (need >=${nodeFloor})`);
|
|
1910
1956
|
issues++;
|
|
1911
1957
|
}
|
|
1912
1958
|
|
|
@@ -3094,7 +3140,7 @@ async function rebuildBinding() {
|
|
|
3094
3140
|
ok(`better-sqlite3 binding ${verify.action} for Node ${process.version} — ${label} (${root})`);
|
|
3095
3141
|
} else {
|
|
3096
3142
|
fail(`better-sqlite3 binding still unusable in ${label}: ${verify.error}`);
|
|
3097
|
-
log(`Try manually:
|
|
3143
|
+
log(`Try manually: ${nativeBindingRepairHint(root)}`);
|
|
3098
3144
|
failed++;
|
|
3099
3145
|
}
|
|
3100
3146
|
}
|
package/lib/binding-probe.mjs
CHANGED
|
@@ -11,10 +11,51 @@ import { createRequire } from 'node:module';
|
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
|
|
13
13
|
// npm >= 12 blocks lifecycle scripts by default, so a plain `npm rebuild` exits 0
|
|
14
|
-
// WITHOUT compiling — see the rebuild() comment below.
|
|
15
|
-
//
|
|
14
|
+
// WITHOUT compiling — see the rebuild() comment below. Step 1 of the heal chain, and
|
|
15
|
+
// correct for a dependency that declares an install script. It is NOT sufficient for the
|
|
16
|
+
// better-sqlite3 the project actually ships — see the constant below, and never hand this
|
|
17
|
+
// one to a human on its own (nativeBindingRepairHint is what surfaces get).
|
|
16
18
|
export const NATIVE_BINDING_REBUILD_CMD = 'npm rebuild better-sqlite3 --dangerously-allow-all-scripts';
|
|
17
19
|
|
|
20
|
+
// Last-resort heal, added in v4.0.0 with better-sqlite3 13. THE npm-REBUILD PATH ABOVE
|
|
21
|
+
// CANNOT HEAL v13 AT ALL — measured, not inferred: 12 carried
|
|
22
|
+
// `"install": "prebuild-install || node-gyp rebuild --release"`, and 13 carries NO install
|
|
23
|
+
// script whatsoever (it ships `prebuilds/<platform>.node` instead). `npm rebuild` therefore
|
|
24
|
+
// has nothing to run and exits 0 printing "rebuilt dependencies successfully" while
|
|
25
|
+
// producing no `.node` — the same print-success-compile-nothing trap the constant above was
|
|
26
|
+
// written for, moved up one level and now immune to the --dangerously-allow-all-scripts
|
|
27
|
+
// flag. On the 8 platforms 13 prebuilds (linux/linuxmusl/darwin/win32 × x64/arm64) the heal
|
|
28
|
+
// never runs. On any other platform this is the only remaining recovery: the package still
|
|
29
|
+
// ships `src/`, `deps/` and `binding.gyp`, so its own build-release script compiles from
|
|
30
|
+
// source. Verified in a sandbox: prebuilds+build removed → `npm rebuild …` exits 0 and heals
|
|
31
|
+
// nothing → this command produces build/Release/better_sqlite3.node and the DB opens.
|
|
32
|
+
export const NATIVE_BINDING_SOURCE_BUILD_CMD = 'npm run --prefix node_modules/better-sqlite3 build-release';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The one-liner a HUMAN should be given to repair the binding under `root`.
|
|
36
|
+
*
|
|
37
|
+
* A20260906-R8-P1-1: every user-facing "Repair:" line printed NATIVE_BINDING_REBUILD_CMD
|
|
38
|
+
* alone — including doctor's, via install-shape. On better-sqlite3 13 that command prints
|
|
39
|
+
* "rebuilt dependencies successfully", exits 0, and compiles nothing (re-measured
|
|
40
|
+
* 2026-09-06, npm 12.0.2, both prebuild states), so the copy-paste repair REPORTS SUCCESS
|
|
41
|
+
* on a still-dead binding. That is worse than printing nothing. The command that heals was
|
|
42
|
+
* added in v4.0.0 but lived only inside the automated chain and was shown to nobody.
|
|
43
|
+
*
|
|
44
|
+
* This is the same defect the CHANGELOG records once already: the hints used to say
|
|
45
|
+
* `--build-from-source`, which no-oped the same way, and were changed to the flag above.
|
|
46
|
+
* That fix was right for better-sqlite3 12 and expired when the dependency was bumped.
|
|
47
|
+
*
|
|
48
|
+
* `&&`, never `||`: step 1 exits 0 whether or not it did anything, so an `||` chain would
|
|
49
|
+
* never reach step 2 — the no-op is the whole trap. Sequencing unconditionally costs a
|
|
50
|
+
* recompile in the case step 1 already fixed, which is the right trade for a manual repair.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} root Directory whose node_modules holds better-sqlite3
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
export function nativeBindingRepairHint(root) {
|
|
56
|
+
return `cd ${root} && ${NATIVE_BINDING_REBUILD_CMD} && ${NATIVE_BINDING_SOURCE_BUILD_CMD}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
18
59
|
// Set on a re-exec'd child so one failed heal cannot fork-bomb the CLI.
|
|
19
60
|
export const BINDING_HEAL_GUARD_ENV = 'CLAUDE_MEM_BINDING_HEALED';
|
|
20
61
|
|
|
@@ -218,7 +259,32 @@ export async function ensureBetterSqlite3Working(installDir, deps = {}) {
|
|
|
218
259
|
const second = await verify();
|
|
219
260
|
if (second.ok) return { ok: true, action: 'rebuilt' };
|
|
220
261
|
|
|
221
|
-
|
|
262
|
+
// Source-compile fallback (v4.0.0). Reached only when the npm path ran without throwing
|
|
263
|
+
// and STILL left an unusable binding — which is exactly what better-sqlite3 13 does on a
|
|
264
|
+
// platform it ships no prebuild for, because it has no install script for `npm rebuild`
|
|
265
|
+
// to run. Deliberately gated behind a failed verify rather than replacing the npm path:
|
|
266
|
+
// on 12, and on 13 wherever a prebuild matches, the npm path already worked and this
|
|
267
|
+
// never fires.
|
|
268
|
+
//
|
|
269
|
+
// It is part of OUR rebuild strategy, so a caller that injected its own `rebuild` does not
|
|
270
|
+
// get it appended — otherwise an injected strategy would silently grow a step its owner
|
|
271
|
+
// never asked for, and (as the stub tests caught) a test that stubs `rebuild` without
|
|
272
|
+
// stubbing `exec` would shell out for real.
|
|
273
|
+
const sourceBuild =
|
|
274
|
+
deps.sourceBuild ||
|
|
275
|
+
(deps.rebuild ? null : () => exec(NATIVE_BINDING_SOURCE_BUILD_CMD, { cwd: installDir, stdio: 'pipe' }));
|
|
276
|
+
if (!sourceBuild) return { ok: false, error: second.error || first.error };
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await sourceBuild();
|
|
280
|
+
} catch (e) {
|
|
281
|
+
return { ok: false, error: `source build failed: ${e.message}` };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const third = await verify();
|
|
285
|
+
if (third.ok) return { ok: true, action: 'compiled' };
|
|
286
|
+
|
|
287
|
+
return { ok: false, error: third.error || second.error || first.error };
|
|
222
288
|
}
|
|
223
289
|
|
|
224
290
|
/**
|
package/lib/install-shape.mjs
CHANGED
|
@@ -36,7 +36,7 @@ import { homedir } from 'node:os';
|
|
|
36
36
|
|
|
37
37
|
import {
|
|
38
38
|
probeBindingInFreshProcess,
|
|
39
|
-
|
|
39
|
+
nativeBindingRepairHint,
|
|
40
40
|
flattenBindingError,
|
|
41
41
|
} from './binding-probe.mjs';
|
|
42
42
|
|
|
@@ -245,7 +245,7 @@ export function probeRuntimeRoots(roots, deps = {}) {
|
|
|
245
245
|
// showed a bare path for the one fault family this check exists for.
|
|
246
246
|
const error = flattenBindingError(r.error);
|
|
247
247
|
return ownDeps
|
|
248
|
-
? { label, root, ok: false, error, repair:
|
|
248
|
+
? { label, root, ok: false, error, repair: nativeBindingRepairHint(root) }
|
|
249
249
|
: {
|
|
250
250
|
label,
|
|
251
251
|
root,
|