claude-mem-lite 6.17.0 → 6.17.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/README.md +3 -2
- package/README.zh-CN.md +2 -2
- package/hook-context.mjs +19 -4
- package/hook-update.mjs +3 -3
- package/install.mjs +13 -1
- package/lib/deferred-work.mjs +9 -2
- package/mem-cli.mjs +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/scripts/pre-tool-recall.js +12 -3
- package/server.mjs +1 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"plugins": [
|
|
10
10
|
{
|
|
11
11
|
"name": "claude-mem-lite",
|
|
12
|
-
"version": "6.17.
|
|
12
|
+
"version": "6.17.1",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"homepage": "https://github.com/sdsrss/claude-mem-lite",
|
|
15
15
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. FTS5 BM25 keyword 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)."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.17.
|
|
3
|
+
"version": "6.17.1",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. FTS5 BM25 keyword 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/README.md
CHANGED
|
@@ -244,7 +244,8 @@ everything is pinning `claude-mem-lite@6.16.0`.
|
|
|
244
244
|
|
|
245
245
|
- **The pre-edit lesson line asks for a lesson's `#NN` only where it changed the edit.** It no
|
|
246
246
|
longer asks for an applied / not-applicable verdict on every lesson in your next reply, which
|
|
247
|
-
put lesson-id lists into replies. Old directive: `CLAUDE_MEM_SALIENCE=verdict
|
|
247
|
+
put lesson-id lists into replies. Old directive: `CLAUDE_MEM_SALIENCE=verdict` (since 6.17.1
|
|
248
|
+
it also says it overrides the managed row and detail doc, which keep the new wording). Adopted
|
|
248
249
|
projects get the matching CLAUDE.md managed row and detail doc on the next SessionStart;
|
|
249
250
|
`CLAUDE_MEM_NO_TEMPLATE_REFRESH=1` keeps the old text.
|
|
250
251
|
- **SessionStart's "Deferred Work" list ends with "+N more open"** when more than its 5 rows
|
|
@@ -1086,7 +1087,7 @@ and names can change between releases.
|
|
|
1086
1087
|
| `CLAUDE_MEM_TASK_IMPERATIVE` | `on`/`1` injects the single most relevant lesson at prompt position under an imperative template. | _(off)_ |
|
|
1087
1088
|
| `CLAUDE_MEM_SUBAGENT_INJECT` | Dispatch-time memory injection for subagents. | _(off)_ |
|
|
1088
1089
|
| `CLAUDE_MEM_RECALL_FRAMING` | First line of a PreToolUse / PostToolUse recall block. `ab` gives each session one of two wordings, the older "system-injected context, continue your planned action" or a plain statement of source, so their cite-rates can be compared in one run (`benchmark/citation-live-replay.mjs --by-framing`); `legacy` / `factual` pin one. | `ab` |
|
|
1089
|
-
| `CLAUDE_MEM_SALIENCE` | Selects how the pre-edit lesson line asks for a response: unset = name a lesson's `#NN` only where it changed the edit; `verdict` = the pre-6.17 per-lesson `applied` / `n/a` verdict (the
|
|
1090
|
+
| `CLAUDE_MEM_SALIENCE` | Selects how the pre-edit lesson line asks for a response: unset = name a lesson's `#NN` only where it changed the edit; `verdict` = the pre-6.17 per-lesson `applied` / `n/a` verdict (the adoption row in CLAUDE.md and the detail doc keep the new wording, and since 6.17.1 the directive says it overrides them); `bind` / `bridge` = comprehension-bridge arms (`bridge` keeps the pre-6.17 verdict wording as its fallback; neither says it overrides the adopted text, so their measured wording stays fixed); `legacy` = no directive. | _(unset)_ |
|
|
1090
1091
|
| `CLAUDE_MEM_EDGE_DECAY` | Enables decay of file↔observation edges. | _(off)_ |
|
|
1091
1092
|
| `CLAUDE_MEM_EDGE_DECAY_K` | Edge-decay threshold when the flag above is on (clamped to ≥1). | `3` |
|
|
1092
1093
|
|
package/README.zh-CN.md
CHANGED
|
@@ -206,8 +206,8 @@ rm -rf ~/claude-mem-lite/ # v0.5 前的非隐藏目录(如未自动迁移)
|
|
|
206
206
|
|
|
207
207
|
- **编辑前的 lesson 提示只在某条 lesson 改变了这次改动时,才请模型提一次它的 `#NN`。** 不再要求在
|
|
208
208
|
下一条回复里对每条 lesson 逐一表态“采纳 / 不适用”——那会让回复里出现成串的 lesson 编号。恢复旧提示:
|
|
209
|
-
`CLAUDE_MEM_SALIENCE=verdict
|
|
210
|
-
|
|
209
|
+
`CLAUDE_MEM_SALIENCE=verdict`(6.17.1 起它会注明自己优先于托管行和详细文档,那两处保持新写法)。
|
|
210
|
+
已接入的项目会在下次 SessionStart 时把 CLAUDE.md 托管行和详细文档换成对应的新写法;想保留旧文本设 `CLAUDE_MEM_NO_TEMPLATE_REFRESH=1`。
|
|
211
211
|
- **SessionStart 的 "Deferred Work" 列表(5 行)在还有更多未完成事项时,末尾加一行 "+N more open"。**
|
|
212
212
|
没有开关,回退请固定 6.16.0。
|
|
213
213
|
|
package/hook-context.mjs
CHANGED
|
@@ -710,6 +710,16 @@ export function buildSessionContextLines(
|
|
|
710
710
|
// asserted in tests/hook-context.test.mjs rather than left as a claim, because "it
|
|
711
711
|
// only adds" is exactly the kind of sentence this repo keeps finding to be false.
|
|
712
712
|
const { fileLessonQuota, keyContextQuota } = sectionQuotas(fileLessons.length, keyContext.length);
|
|
713
|
+
// The collector records the line as the caller will SEE it: this function's return runs
|
|
714
|
+
// neutralizeContextDelimiters over the whole body, and idsShownWhole matches exact lines,
|
|
715
|
+
// so a row carrying a delimiter tag was shown yet never booked (D#115 P3-1). Matches the
|
|
716
|
+
// block-level pass for any tag closed on its own line. Two cases still differ, and leave a
|
|
717
|
+
// shown row unbooked as before this fix: an UNclosed tag whose attribute tail
|
|
718
|
+
// (CONTEXT_DELIMITER_RE's `[^>]*` does not stop at a newline) runs to a later row's `>` —
|
|
719
|
+
// the rows at the two ends of that span; and the block pass failing closed (32+ nested
|
|
720
|
+
// forged tags, defangToFixpoint strips every `<` / `>`) — any shown row holding `<` or
|
|
721
|
+
// `>`. 0 of 178 live importance>=2 rows carry a tag (2026-09-27).
|
|
722
|
+
const bookedLine = (e) => ({ id: e.id, text: neutralizeContextDelimiters(e.line) });
|
|
713
723
|
|
|
714
724
|
if (fileLessons.length > 0 && !quiet) {
|
|
715
725
|
const shown = fileLessons.slice(0, fileLessonQuota);
|
|
@@ -718,7 +728,7 @@ export function buildSessionContextLines(
|
|
|
718
728
|
summaryLines.push('');
|
|
719
729
|
if (collector) {
|
|
720
730
|
collector.keyContextIds.push(...shown.map((e) => e.id));
|
|
721
|
-
collector.keyContextLines.push(...shown.map(
|
|
731
|
+
collector.keyContextLines.push(...shown.map(bookedLine));
|
|
722
732
|
}
|
|
723
733
|
}
|
|
724
734
|
if (keyContext.length > 0 && !quiet) {
|
|
@@ -728,7 +738,7 @@ export function buildSessionContextLines(
|
|
|
728
738
|
summaryLines.push('');
|
|
729
739
|
if (collector) {
|
|
730
740
|
collector.keyContextIds.push(...shown.map((e) => e.id));
|
|
731
|
-
collector.keyContextLines.push(...shown.map(
|
|
741
|
+
collector.keyContextLines.push(...shown.map(bookedLine));
|
|
732
742
|
}
|
|
733
743
|
}
|
|
734
744
|
} else if (!latestSummary && !effectiveQuiet()) {
|
|
@@ -881,11 +891,16 @@ export function buildSessionContextLines(
|
|
|
881
891
|
}
|
|
882
892
|
// The list is capped at 5 and used to stop there silently, so 10 open items read as a
|
|
883
893
|
// 5-item backlog. The total is the same statement's COUNT(*) OVER (), taken before LIMIT.
|
|
884
|
-
const
|
|
894
|
+
const openTotal = Number(deferredItems[0].open_total);
|
|
895
|
+
const hidden = openTotal - deferredItems.length;
|
|
885
896
|
// Both listing surfaces page at 10, so the line names the knob rather than promising "all".
|
|
897
|
+
// Their maxima differ (mem_defer_list 50, `defer list` 100): past 50 only the CLI can list
|
|
898
|
+
// them, past 100 nothing lists them all (v6.17.1 pre-tag review).
|
|
886
899
|
if (hidden > 0)
|
|
887
900
|
deferredLines.push(
|
|
888
|
-
|
|
901
|
+
openTotal <= 50
|
|
902
|
+
? `+${hidden} more open — mem_defer_list / \`defer list\` with a larger limit lists them`
|
|
903
|
+
: `+${hidden} more open — \`defer list --limit 100\` lists ${openTotal <= 100 ? 'them' : 'the first 100'}`,
|
|
889
904
|
);
|
|
890
905
|
deferredLines.push('');
|
|
891
906
|
}
|
package/hook-update.mjs
CHANGED
|
@@ -180,9 +180,9 @@ export function getCachedUpdateBanner() {
|
|
|
180
180
|
// running when the check ran, and in plugin mode the update itself is applied by
|
|
181
181
|
// Claude Code — downloadAndInstall never runs, so nothing clears the flag. Judge
|
|
182
182
|
// the cache against the version running NOW: returns that version when the cached
|
|
183
|
-
// latest is still ahead of it, else null.
|
|
184
|
-
// banner
|
|
185
|
-
function pendingCachedUpdate(state) {
|
|
183
|
+
// latest is still ahead of it, else null. Every cached face (the SessionStart
|
|
184
|
+
// banner, the throttled checkForUpdate and install.mjs `doctor`) asks this, so none can nag alone.
|
|
185
|
+
export function pendingCachedUpdate(state) {
|
|
186
186
|
if (!state.updateAvailable || !state.latestVersion) return null;
|
|
187
187
|
const running = getCurrentVersion();
|
|
188
188
|
return compareVersions(state.latestVersion, running) > 0 ? running : null;
|
package/install.mjs
CHANGED
|
@@ -2369,7 +2369,19 @@ async function doctor() {
|
|
|
2369
2369
|
if (state.lastCheck) parts.push(`last check: ${state.lastCheck}`);
|
|
2370
2370
|
if (state.latestVersion) parts.push(`latest: v${state.latestVersion}`);
|
|
2371
2371
|
if (state.lastUpdate) parts.push(`last update: ${state.lastUpdate}`);
|
|
2372
|
-
|
|
2372
|
+
// Judged against the version running now, like the banner (#35): in plugin mode
|
|
2373
|
+
// nothing clears the cached flag once Claude Code has applied the update. Dynamic, as
|
|
2374
|
+
// elsewhere in doctor, so a hook-update that cannot load costs only this judgement.
|
|
2375
|
+
if (state.updateAvailable) {
|
|
2376
|
+
let pending = true;
|
|
2377
|
+
try {
|
|
2378
|
+
const { pendingCachedUpdate } = await import('./hook-update.mjs');
|
|
2379
|
+
pending = pendingCachedUpdate(state) !== null;
|
|
2380
|
+
} catch {
|
|
2381
|
+
/* cannot judge — report the cached flag as it stands */
|
|
2382
|
+
}
|
|
2383
|
+
if (pending) parts.push('update pending');
|
|
2384
|
+
}
|
|
2373
2385
|
if (state.rateLimited) parts.push('rate-limited');
|
|
2374
2386
|
if (state.lastError) parts.push(`last error: ${state.lastError}`);
|
|
2375
2387
|
ok(`Update state: ${parts.join(', ') || 'empty'}`);
|
package/lib/deferred-work.mjs
CHANGED
|
@@ -92,14 +92,21 @@ export function listOpenWithOrdinal(db, project, limit = 10) {
|
|
|
92
92
|
* (11 open, default page 10) reads as the whole list. Shared by both surfaces; `raise`
|
|
93
93
|
* names the knob in each surface's own spelling. The total comes from the page's own
|
|
94
94
|
* statement (`open_total`), so a concurrent `defer add` cannot make the two disagree.
|
|
95
|
+
* A page already at the surface's maximum gets no `raise` advice: the reader cannot act on
|
|
96
|
+
* it (D#115 P3-7), so the line says which rows are missing instead.
|
|
95
97
|
* @param {Array<{open_total?: number}>} page rows from listOpenWithOrdinal
|
|
96
98
|
* @param {string} raise e.g. `raise --limit (max 100)`
|
|
99
|
+
* @param {number} [max] the surface's largest page
|
|
97
100
|
* @returns {string}
|
|
98
101
|
*/
|
|
99
|
-
export function formatDeferMoreHint(page, raise) {
|
|
102
|
+
export function formatDeferMoreHint(page, raise, max = Infinity) {
|
|
100
103
|
const hidden = page.length > 0 ? Number(page[0].open_total) - page.length : 0;
|
|
101
104
|
if (!(hidden > 0)) return '';
|
|
102
|
-
|
|
105
|
+
const head = `${hidden} more open item${hidden === 1 ? '' : 's'} not shown`;
|
|
106
|
+
if (page.length >= max) {
|
|
107
|
+
return `${head} — this is already the largest page (${max}); the rest sort last (lowest priority, then newest)`;
|
|
108
|
+
}
|
|
109
|
+
return `${head} — ${raise}`;
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
// ─── G11: list age + stale refresh hint (roadmap 2026-07-18) ─────────────────
|
package/mem-cli.mjs
CHANGED
|
@@ -1464,7 +1464,7 @@ function cmdDeferList(db, args) {
|
|
|
1464
1464
|
for (const r of list) {
|
|
1465
1465
|
out(` ${formatDeferListRow(r)}`);
|
|
1466
1466
|
}
|
|
1467
|
-
const moreHint = formatDeferMoreHint(list, 'raise --limit (max 100)');
|
|
1467
|
+
const moreHint = formatDeferMoreHint(list, 'raise --limit (max 100)', 100);
|
|
1468
1468
|
if (moreHint) out(` ${moreHint}`);
|
|
1469
1469
|
const staleHint = formatDeferStaleHint(countStaleOpen(db, project));
|
|
1470
1470
|
if (staleHint) out(` ${staleHint}`);
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.17.
|
|
3
|
+
"version": "6.17.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "claude-mem-lite",
|
|
9
|
-
"version": "6.17.
|
|
9
|
+
"version": "6.17.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": "6.17.
|
|
3
|
+
"version": "6.17.1",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. FTS5 BM25 keyword 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",
|
|
@@ -166,11 +166,20 @@ const ACK_DIRECTIVE =
|
|
|
166
166
|
// CLAUDE_MEM_SALIENCE=bind; default stays ACK_DIRECTIVE.
|
|
167
167
|
const BIND_DIRECTIVE =
|
|
168
168
|
"For each lesson: state the one concrete check it forces on the line(s) you're editing, quote the edit line that satisfies it, then report '#NN: <check> — pass' or '#NN: n/a — <why this edit can't reach it>'.";
|
|
169
|
+
// Under `verdict` the adopted CLAUDE.md row ("skip ones that did not apply") and the detail
|
|
170
|
+
// doc ("`#NN n/a` … 所以不必写") still carry the v6.17 wording, so the agent would hold two
|
|
171
|
+
// contradicting instructions; this says which wins (tasks/specs/verdict-precedence.md). Not
|
|
172
|
+
// on `bind` or `bridge`, whose per-lesson asks carry the same contradiction: both are arms
|
|
173
|
+
// benchmark/efficacy-harness.mjs measures, and their text must stay the one their readings
|
|
174
|
+
// were taken with.
|
|
175
|
+
const VERDICT_OPT_OUT_DIRECTIVE = `${VERDICT_DIRECTIVE} CLAUDE_MEM_SALIENCE=verdict is set: this overrides the memory guidance in CLAUDE.md and .claude/plugin_claude_mem_lite.md that a lesson which did not apply needs no mention.`;
|
|
169
176
|
const ACTIVE_DIRECTIVE = SALIENCE_BIND
|
|
170
177
|
? BIND_DIRECTIVE
|
|
171
|
-
: process.env.CLAUDE_MEM_SALIENCE === 'verdict'
|
|
172
|
-
?
|
|
173
|
-
:
|
|
178
|
+
: process.env.CLAUDE_MEM_SALIENCE === 'verdict'
|
|
179
|
+
? VERDICT_OPT_OUT_DIRECTIVE
|
|
180
|
+
: SALIENCE_BRIDGE
|
|
181
|
+
? VERDICT_DIRECTIVE
|
|
182
|
+
: ACK_DIRECTIVE;
|
|
174
183
|
const STALE_MS = 10 * 60 * 1000; // 10 minutes cleanup threshold for legacy file
|
|
175
184
|
// Feature ① (file intelligence): on the first Read of a file each session, inject
|
|
176
185
|
// its approximate token size + a one-line summary so the agent can decide to read
|
package/server.mjs
CHANGED
|
@@ -1252,7 +1252,7 @@ server.registerTool(
|
|
|
1252
1252
|
for (const r of list) {
|
|
1253
1253
|
lines.push(formatDeferListRow(r));
|
|
1254
1254
|
}
|
|
1255
|
-
const moreHint = formatDeferMoreHint(list, 'pass a larger limit (max 50)');
|
|
1255
|
+
const moreHint = formatDeferMoreHint(list, 'pass a larger limit (max 50)', 50);
|
|
1256
1256
|
if (moreHint) lines.push(moreHint);
|
|
1257
1257
|
const staleHint = formatDeferStaleHint(countStaleOpen(db, project));
|
|
1258
1258
|
if (staleHint) lines.push(staleHint);
|