claude-mem-lite 3.17.0 → 3.18.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": "3.
|
|
13
|
+
"version": "3.18.0",
|
|
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.
|
|
3
|
+
"version": "3.18.0",
|
|
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"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// lib/lesson-bridge.mjs — pure prompt builder + fail-open Haiku bridge for the
|
|
2
|
+
// comprehension-bridge forcing-function (CLAUDE_MEM_SALIENCE=bridge). Loaded by
|
|
3
|
+
// scripts/pre-tool-recall.js via dynamic import ONLY when the flag is on, so the
|
|
4
|
+
// fast hook never pulls the LLM stack by default (lesson #8447).
|
|
5
|
+
import { callLLM } from '../hook-shared.mjs';
|
|
6
|
+
|
|
7
|
+
const LESSON_MAX = 600; // chars — a lesson_learned fits well under this
|
|
8
|
+
const HUNK_MAX = 1200; // chars — the change region; bounds Haiku input cost
|
|
9
|
+
const CHECK_MAX = 200; // chars — bounded injected payload
|
|
10
|
+
|
|
11
|
+
export function buildBridgePrompt(lesson, hunk) {
|
|
12
|
+
const l = String(lesson || '').slice(0, LESSON_MAX);
|
|
13
|
+
const h = String(hunk || '').slice(0, HUNK_MAX);
|
|
14
|
+
return [
|
|
15
|
+
'A past lesson and the code change about to be made are below.',
|
|
16
|
+
'In ONE line, state the single concrete check the lesson forces on THIS change,',
|
|
17
|
+
'naming the actual symbol involved. If the lesson cannot apply, output exactly: N/A',
|
|
18
|
+
'Be specific. No preamble, no markdown.',
|
|
19
|
+
'',
|
|
20
|
+
`LESSON: ${l}`,
|
|
21
|
+
'',
|
|
22
|
+
'CHANGE:',
|
|
23
|
+
h,
|
|
24
|
+
].join('\n');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// { ok:true, check } when the bridge produced a usable, applicable check;
|
|
28
|
+
// { ok:false } on N/A / empty / error / timeout. NEVER throws — the caller
|
|
29
|
+
// falls back to the baseline ACK_DIRECTIVE on { ok:false }.
|
|
30
|
+
export async function bridgeLesson({ lesson, hunk, timeoutMs = 2500, _callLLM = callLLM }) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = await _callLLM(buildBridgePrompt(lesson, hunk), timeoutMs);
|
|
33
|
+
const line = String(raw || '').trim().split('\n')[0].trim();
|
|
34
|
+
if (!line || /^n\s*\/?\s*a$/i.test(line)) return { ok: false };
|
|
35
|
+
return { ok: true, check: line.slice(0, CHECK_MAX) };
|
|
36
|
+
} catch {
|
|
37
|
+
return { ok: false };
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0",
|
|
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",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"lib/reread-guard.mjs",
|
|
72
72
|
"lib/metrics.mjs",
|
|
73
73
|
"lib/lesson-idents.mjs",
|
|
74
|
+
"lib/lesson-bridge.mjs",
|
|
74
75
|
"lib/binding-probe.mjs",
|
|
75
76
|
"lib/proc-lock.mjs",
|
|
76
77
|
"lib/atomic-write.mjs",
|
|
@@ -42,6 +42,7 @@ const COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes (used only for legacy fallback)
|
|
|
42
42
|
const SALIENCE_LEGACY = process.env.CLAUDE_MEM_SALIENCE === 'legacy'
|
|
43
43
|
|| process.env.CLAUDE_MEM_SALIENCE === '0';
|
|
44
44
|
const SALIENCE_BIND = process.env.CLAUDE_MEM_SALIENCE === 'bind';
|
|
45
|
+
const SALIENCE_BRIDGE = process.env.CLAUDE_MEM_SALIENCE === 'bridge';
|
|
45
46
|
const ACK_DIRECTIVE = "apply each lesson to this edit or rule it out — state '#NN applied' or '#NN n/a — <reason>' in your next user-facing message.";
|
|
46
47
|
// v-bind salience forcing-function (#8771 audit: ack ≠ act). Instead of a cheap
|
|
47
48
|
// '#NN applied / n/a' verdict, demand the model bind the lesson to the concrete
|
|
@@ -77,6 +78,30 @@ function cooldownPathFor(sessionId) {
|
|
|
77
78
|
return join(RUNTIME_DIR, `pre-recall-cooldown-${safe}.json`);
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
// Comprehension-bridge (CLAUDE_MEM_SALIENCE=bridge): rewrite the top bound lesson
|
|
82
|
+
// into a check naming a symbol in THIS change. Dynamic import keeps the LLM stack
|
|
83
|
+
// out of the default fast path (#8447). Fail-open: null → caller uses ACK line.
|
|
84
|
+
async function bridgeTopLesson(rows, changeText) {
|
|
85
|
+
if (!SALIENCE_BRIDGE || !changeText) return null;
|
|
86
|
+
const fake = process.env.CLAUDE_MEM_BRIDGE_FAKE;
|
|
87
|
+
let extractIdents, bridgeLesson;
|
|
88
|
+
try {
|
|
89
|
+
({ extractIdents } = await import('../lib/lesson-idents.mjs'));
|
|
90
|
+
if (!fake) ({ bridgeLesson } = await import('../lib/lesson-bridge.mjs'));
|
|
91
|
+
} catch { return null; }
|
|
92
|
+
for (const r of rows) {
|
|
93
|
+
const lesson = r.lesson_learned;
|
|
94
|
+
if (!lesson) continue;
|
|
95
|
+
if (!extractIdents(lesson).some((id) => changeText.includes(id))) continue;
|
|
96
|
+
let res;
|
|
97
|
+
if (fake) res = /^n\s*\/?\s*a$/i.test(fake.trim()) ? { ok: false } : { ok: true, check: fake.trim().slice(0, 200) };
|
|
98
|
+
else res = await bridgeLesson({ lesson, hunk: changeText });
|
|
99
|
+
if (res.ok) return { id: r.id, check: res.check };
|
|
100
|
+
return null; // top bound lesson abstained → fall back to ACK, don't scan further
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
80
105
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
81
106
|
|
|
82
107
|
function inferProject() {
|
|
@@ -181,11 +206,13 @@ try {
|
|
|
181
206
|
let filePath;
|
|
182
207
|
let sessionId;
|
|
183
208
|
let toolName;
|
|
209
|
+
let toolInput;
|
|
184
210
|
// isFullRead: a Read with no offset/limit reads the whole file. The reread
|
|
185
211
|
// guard only flags full-vs-full re-reads, so paging never trips it.
|
|
186
212
|
let isFullRead = true;
|
|
187
213
|
try {
|
|
188
214
|
const event = JSON.parse(input);
|
|
215
|
+
toolInput = event.tool_input;
|
|
189
216
|
filePath = event.tool_input?.file_path;
|
|
190
217
|
sessionId = event.session_id || null;
|
|
191
218
|
toolName = event.tool_name || null;
|
|
@@ -442,7 +469,11 @@ try {
|
|
|
442
469
|
// Read keeps the quiet form; its forcing-function fires at the later Edit
|
|
443
470
|
// via the Read→Edit ack nudge above.
|
|
444
471
|
if (!isRead && !SALIENCE_LEGACY) {
|
|
445
|
-
|
|
472
|
+
const changeText = [toolInput?.old_string, toolInput?.new_string, toolInput?.content]
|
|
473
|
+
.filter(Boolean).join('\n');
|
|
474
|
+
const bridged = await bridgeTopLesson(allRows, changeText);
|
|
475
|
+
if (bridged) lines.push(`[mem] ⚠ #${bridged.id} → this edit must: ${bridged.check}. Confirm your new code satisfies it.`);
|
|
476
|
+
else lines.push(`[mem] ⚠ Before this edit: ${ACTIVE_DIRECTIVE}`);
|
|
446
477
|
}
|
|
447
478
|
} else if (!isRead && process.env.CLAUDE_MEM_PRETOOL_NUDGE === '1') {
|
|
448
479
|
// R-4: Edit/Write empty → short backfill reminder. OPT-IN (default off) as
|
package/source-files.mjs
CHANGED
|
@@ -66,6 +66,11 @@ export const SOURCE_FILES = [
|
|
|
66
66
|
// are present in the pre-edit file (component 2). Imported ONLY by
|
|
67
67
|
// scripts/pre-tool-recall.js; kept here for the same reason as file-intel.mjs.
|
|
68
68
|
'lib/lesson-idents.mjs',
|
|
69
|
+
// comprehension-bridge forcing-function (CLAUDE_MEM_SALIENCE=bridge): rewrites
|
|
70
|
+
// a recalled lesson into a check bound to the change hunk. Dynamic-imported by
|
|
71
|
+
// scripts/pre-tool-recall.js ONLY under the flag, but must still ship so the
|
|
72
|
+
// hook can resolve it at runtime when a user opts in.
|
|
73
|
+
'lib/lesson-bridge.mjs',
|
|
69
74
|
// v2.71.x: better-sqlite3 ABI probe + auto-rebuild. Shared by install.mjs
|
|
70
75
|
// (post-`npm install` verify) and scripts/launch.mjs (pre-server-launch
|
|
71
76
|
// self-heal after Node ABI changes). Missing from manifest → auto-update
|