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.
@@ -9,7 +9,7 @@
9
9
  "plugins": [
10
10
  {
11
11
  "name": "claude-mem-lite",
12
- "version": "6.17.0",
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.0",
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`. Adopted
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 directive only: the adoption row in CLAUDE.md and the detail doc keep the new wording); `bind` / `bridge` = comprehension-bridge arms (`bridge` keeps the pre-6.17 verdict wording as its fallback); `legacy` = no directive. | _(unset)_ |
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`。已接入的项目会在下次 SessionStart 时把 CLAUDE.md 托管行和详细文档
210
- 换成对应的新写法;想保留旧文本设 `CLAUDE_MEM_NO_TEMPLATE_REFRESH=1`。
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((e) => ({ id: e.id, text: e.line })));
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((e) => ({ id: e.id, text: e.line })));
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 hidden = Number(deferredItems[0].open_total) - deferredItems.length;
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
- `+${hidden} more open — mem_defer_list / \`defer list\` with a larger limit lists them`,
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. Both cached faces (the SessionStart
184
- // banner and the throttled checkForUpdate) ask this, so neither can nag alone.
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
- if (state.updateAvailable) parts.push('update pending');
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'}`);
@@ -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
- return `${hidden} more open item${hidden === 1 ? '' : 's'} not shown — ${raise}`;
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}`);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "6.17.0",
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.0",
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.0",
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' || SALIENCE_BRIDGE
172
- ? VERDICT_DIRECTIVE
173
- : ACK_DIRECTIVE;
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);