claude-mem-lite 3.70.0 → 3.70.2
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/lib/binding-probe.mjs +23 -0
- package/lib/install-shape.mjs +55 -36
- package/lib/native-binding-hint.mjs +2 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/scripts/binding-probe-cli.mjs +12 -4
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "claude-mem-lite",
|
|
13
|
-
"version": "3.70.
|
|
13
|
+
"version": "3.70.2",
|
|
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.70.
|
|
3
|
+
"version": "3.70.2",
|
|
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/lib/binding-probe.mjs
CHANGED
|
@@ -51,6 +51,29 @@ export function isNativeBindingError(err) {
|
|
|
51
51
|
return NATIVE_BINDING_PATTERNS.some((re) => re.test(msg));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Render a binding error as ONE line without losing the diagnosis.
|
|
56
|
+
*
|
|
57
|
+
* Every surface used to do `String(err).split('\n')[0]`, which is exactly wrong for
|
|
58
|
+
* the ABI-mismatch family this subsystem exists to detect. Node's message puts the
|
|
59
|
+
* filename on line 0 and the `NODE_MODULE_VERSION 127 … requires 137` on lines 2-3,
|
|
60
|
+
* so first-line truncation printed a bare path and dropped the only part that says
|
|
61
|
+
* what is wrong. (The comment in probeBindingInFreshProcess below called that string
|
|
62
|
+
* "the highest-value line doctor prints"; for a stale binding it carried no diagnosis
|
|
63
|
+
* at all.) Collapsing whitespace keeps both the path and the numbers while staying
|
|
64
|
+
* safe for a JSON envelope, a JSONL log record and a one-line hook receipt.
|
|
65
|
+
*
|
|
66
|
+
* @param {unknown} err Error, string, or anything thrown.
|
|
67
|
+
* @param {number} [max] Hard cap; a probe must not be able to flood a receipt.
|
|
68
|
+
* @returns {string}
|
|
69
|
+
*/
|
|
70
|
+
export function flattenBindingError(err, max = 240) {
|
|
71
|
+
const raw = err instanceof Error ? err.message : err;
|
|
72
|
+
const s = String(raw ?? '').replace(/\s+/g, ' ').trim();
|
|
73
|
+
if (!s) return 'unknown';
|
|
74
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
75
|
+
}
|
|
76
|
+
|
|
54
77
|
/**
|
|
55
78
|
* Probe better-sqlite3's native binding by importing it from `installDir`'s
|
|
56
79
|
* node_modules and opening an in-memory DB. Returns {ok, error?}.
|
package/lib/install-shape.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import { existsSync, readdirSync, realpathSync } from 'node:fs';
|
|
|
34
34
|
import { join } from 'node:path';
|
|
35
35
|
import { homedir } from 'node:os';
|
|
36
36
|
|
|
37
|
-
import { probeBindingInFreshProcess, NATIVE_BINDING_REBUILD_CMD } from './binding-probe.mjs';
|
|
37
|
+
import { probeBindingInFreshProcess, NATIVE_BINDING_REBUILD_CMD, flattenBindingError } from './binding-probe.mjs';
|
|
38
38
|
|
|
39
39
|
// Module-private: nothing outside needs these, and a new unused export is a
|
|
40
40
|
// review signal against the knip baseline recorded in CLAUDE.md.
|
|
@@ -116,7 +116,7 @@ export function listPluginCacheVersions(opts = {}) {
|
|
|
116
116
|
* twice would double every failure message for a single fault.
|
|
117
117
|
*
|
|
118
118
|
* @param {{home?: string, projectDir?: string, installDir?: string, marketplace?: string, plugin?: string, pluginRoot?: string}} opts
|
|
119
|
-
* @returns {{managed: boolean, pluginVersions: Array<{version: string, root: string}>, activePluginVersion: {version: string, root: string}|null, runtimeRoots: Array<{label: string, root: string,
|
|
119
|
+
* @returns {{managed: boolean, pluginVersions: Array<{version: string, root: string}>, activePluginVersion: {version: string, root: string}|null, runtimeRoots: Array<{label: string, root: string, ownDeps: boolean}>}}
|
|
120
120
|
*/
|
|
121
121
|
export function detectInstallShape({
|
|
122
122
|
home = homedir(), projectDir, installDir, marketplace, plugin,
|
|
@@ -137,31 +137,43 @@ export function detectInstallShape({
|
|
|
137
137
|
|| null;
|
|
138
138
|
|
|
139
139
|
const runtimeRoots = [];
|
|
140
|
-
const
|
|
140
|
+
const byKey = new Map();
|
|
141
141
|
const add = (label, root, { certified = false } = {}) => {
|
|
142
142
|
if (!root) return;
|
|
143
143
|
const bs3 = join(root, 'node_modules', 'better-sqlite3');
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
const ownDeps = existsSync(bs3);
|
|
145
|
+
// A dir that merely lacks deps is not a runtime root — but one this function
|
|
146
|
+
// just CERTIFIED as a code home is, because that is where hooks and the MCP
|
|
147
|
+
// server load from. Dropping it turned a pre-v3.70 exit 1 into exit 0.
|
|
148
|
+
//
|
|
149
|
+
// It is added as a probe TARGET, not pre-judged broken. v3.70.0 pre-judged it,
|
|
150
|
+
// which over-corrected into a false red: Node resolves a specifier up the
|
|
151
|
+
// directory tree, so a code home nested under an ancestor that owns a working
|
|
152
|
+
// better-sqlite3 loads perfectly well. Measured on that build — ground-truth
|
|
153
|
+
// probe {ok:true} against a verdict of "absent, every hook throws". Owning the
|
|
154
|
+
// tree is not the question; being able to LOAD is, and only a probe answers it.
|
|
155
|
+
if (!ownDeps && !(certified && existsSync(root))) return;
|
|
156
|
+
|
|
157
|
+
// Dedup on the binding's realpath when there is one (scripts/setup.sh symlinks a
|
|
158
|
+
// plugin cache's node_modules at the managed dir's, so two homes routinely share
|
|
159
|
+
// one tree and one probe answers for both). With no own tree there is no binding
|
|
160
|
+
// path to key on, so key on the root itself.
|
|
161
|
+
let key = root;
|
|
162
|
+
if (ownDeps) {
|
|
163
|
+
key = bs3;
|
|
164
|
+
try { key = realpathSync(bs3); } catch { /* unresolvable → key on the literal path */ }
|
|
165
|
+
} else {
|
|
166
|
+
try { key = realpathSync(root); } catch { /* ditto */ }
|
|
152
167
|
}
|
|
153
|
-
|
|
154
|
-
try { key = realpathSync(bs3); } catch { /* unresolvable → dedupe on the literal path */ }
|
|
155
|
-
const existing = byBinding.get(key);
|
|
168
|
+
const existing = byKey.get(key);
|
|
156
169
|
if (existing) {
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
// "managed install is broken" has no way to know their plugin shares it.
|
|
170
|
+
// The label has to name both homes — otherwise a plugin user reading
|
|
171
|
+
// "managed install is broken" has no way to know their plugin shares that tree.
|
|
160
172
|
existing.label += `, ${label}`;
|
|
161
173
|
return;
|
|
162
174
|
}
|
|
163
|
-
const entry = { label, root };
|
|
164
|
-
|
|
175
|
+
const entry = { label, root, ownDeps };
|
|
176
|
+
byKey.set(key, entry);
|
|
165
177
|
runtimeRoots.push(entry);
|
|
166
178
|
};
|
|
167
179
|
|
|
@@ -192,27 +204,34 @@ function resolvesSame(a, b) {
|
|
|
192
204
|
*/
|
|
193
205
|
export function probeRuntimeRoots(roots, deps = {}) {
|
|
194
206
|
const probe = deps.probe || ((root) => probeBindingInFreshProcess(root));
|
|
195
|
-
return roots.map(({ label, root,
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
207
|
+
return roots.map(({ label, root, ownDeps = true }) => {
|
|
208
|
+
// EVERY root is probed, including one with no tree of its own: the probe uses
|
|
209
|
+
// Node's real resolution chain, so it is the only thing that knows whether an
|
|
210
|
+
// ancestor node_modules is carrying this install. Pre-judging an unowned tree
|
|
211
|
+
// broken is what made v3.70.0 report "absent — every hook throws" about a root
|
|
212
|
+
// that loaded fine.
|
|
213
|
+
const r = probe(root);
|
|
214
|
+
if (r.ok) return { label, root, ok: true };
|
|
215
|
+
// The repair depends on WHICH failure it is. `npm rebuild` on a package that is
|
|
216
|
+
// not installed exits 0 and heals nothing, so an unowned tree that also cannot
|
|
217
|
+
// resolve from an ancestor needs an install; a present-but-unloadable tree needs
|
|
218
|
+
// the rebuild.
|
|
219
|
+
// NOT `.split('\n')[0]`: Node puts the filename on line 0 and the
|
|
220
|
+
// `NODE_MODULE_VERSION 127 … requires 137` on lines 2-3, so first-line truncation
|
|
221
|
+
// showed a bare path for the one fault family this check exists for.
|
|
222
|
+
const error = flattenBindingError(r.error);
|
|
223
|
+
return ownDeps
|
|
224
|
+
? { label, root, ok: false, error, repair: `cd ${root} && ${NATIVE_BINDING_REBUILD_CMD}` }
|
|
225
|
+
: {
|
|
200
226
|
label,
|
|
201
227
|
root,
|
|
202
228
|
ok: false,
|
|
203
|
-
|
|
204
|
-
|
|
229
|
+
// Say only what is known. The probe may have reached an ANCESTOR
|
|
230
|
+
// node_modules and failed there — an earlier draft of this line asserted
|
|
231
|
+
// "none resolvable from a parent directory" and was caught printing that
|
|
232
|
+
// next to a probe error naming the parent tree it had just loaded from.
|
|
233
|
+
error: `${error} — and this install owns no node_modules/better-sqlite3, so there is nothing here to rebuild`,
|
|
205
234
|
repair: `cd ${root} && npm install --omit=dev`,
|
|
206
235
|
};
|
|
207
|
-
}
|
|
208
|
-
const r = probe(root);
|
|
209
|
-
if (r.ok) return { label, root, ok: true };
|
|
210
|
-
return {
|
|
211
|
-
label,
|
|
212
|
-
root,
|
|
213
|
-
ok: false,
|
|
214
|
-
error: String(r.error || 'unknown').split('\n')[0],
|
|
215
|
-
repair: `cd ${root} && ${NATIVE_BINDING_REBUILD_CMD}`,
|
|
216
|
-
};
|
|
217
236
|
});
|
|
218
237
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import { join } from 'node:path';
|
|
25
25
|
import { readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from 'node:fs';
|
|
26
26
|
import { fileURLToPath } from 'node:url';
|
|
27
|
-
import { isNativeBindingError } from './binding-probe.mjs';
|
|
27
|
+
import { isNativeBindingError, flattenBindingError } from './binding-probe.mjs';
|
|
28
28
|
|
|
29
29
|
export const NATIVE_BINDING_HINT_COOLDOWN_MS = 6 * 60 * 60 * 1000; // 6h
|
|
30
30
|
const MARKER_NAME = 'native-binding-hint-last';
|
|
@@ -136,7 +136,7 @@ export function recordNativeBindingBreakage(runtimeDir, { reason = '', event = '
|
|
|
136
136
|
const tmp = `${marker}.tmp-${process.pid}`;
|
|
137
137
|
// First line only: the ABI error is multi-line and the marker is read by the
|
|
138
138
|
// launcher (pure node:, no parser beyond JSON.parse) and by `doctor`.
|
|
139
|
-
writeFileSync(tmp, JSON.stringify({ reason:
|
|
139
|
+
writeFileSync(tmp, JSON.stringify({ reason: flattenBindingError(reason), event, ts: now }));
|
|
140
140
|
renameSync(tmp, marker);
|
|
141
141
|
} catch { /* best-effort */ }
|
|
142
142
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.70.
|
|
3
|
+
"version": "3.70.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "claude-mem-lite",
|
|
9
|
-
"version": "3.70.
|
|
9
|
+
"version": "3.70.2",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
12
12
|
"better-sqlite3": "^12.6.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.70.
|
|
3
|
+
"version": "3.70.2",
|
|
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",
|
|
@@ -42,8 +42,16 @@ function bareProbe(root) {
|
|
|
42
42
|
const r = spawnSync(process.execPath, ['-e', script], { stdio: 'pipe', timeout: 8000 });
|
|
43
43
|
if (!r.error && r.status === 0) return true;
|
|
44
44
|
// Say WHY. The inline predecessor printed the cause here; dropping it left the
|
|
45
|
-
// user with setup.sh's generic "binding unusable" and nothing to act on.
|
|
46
|
-
|
|
45
|
+
// user with setup.sh's generic "binding unusable" and nothing to act on. Flattened
|
|
46
|
+
// rather than first-lined: Node's ABI message puts the filename on line 0 and the
|
|
47
|
+
// NODE_MODULE_VERSION pair on lines 2-3, so `.split('\n')[0]` said WHERE but never
|
|
48
|
+
// WHY — for the exact fault this probe exists to find.
|
|
49
|
+
//
|
|
50
|
+
// Flattening is inlined, NOT lib/binding-probe.mjs::flattenBindingError, because
|
|
51
|
+
// this function is the fallback for a tree where lib/ failed to import — `helpers`
|
|
52
|
+
// is still null on every path that reaches here. Duplicated deliberately; keep the
|
|
53
|
+
// two in step.
|
|
54
|
+
const why = String(r.stdout || '').replace(/\s+/g, ' ').trim().slice(0, 240)
|
|
47
55
|
|| (r.error && r.error.message)
|
|
48
56
|
|| `probe exited ${r.status ?? `on signal ${r.signal}`}`;
|
|
49
57
|
process.stderr.write(`[claude-mem-lite] binding probe: ${why}\n`);
|
|
@@ -82,9 +90,9 @@ try {
|
|
|
82
90
|
}
|
|
83
91
|
const release = helpers.acquireLock(lockPath);
|
|
84
92
|
if (!release) {
|
|
85
|
-
const firstLine = String(first.error).split('\n')[0];
|
|
86
93
|
process.stderr.write(
|
|
87
|
-
`[claude-mem-lite] binding probe: ${
|
|
94
|
+
`[claude-mem-lite] binding probe: ${helpers.flattenBindingError(first.error)} `
|
|
95
|
+
+ '(another install/repair in flight — deferring heal)\n',
|
|
88
96
|
);
|
|
89
97
|
process.exit(1);
|
|
90
98
|
}
|