claude-mem-lite 6.29.2 → 6.29.3

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.29.2",
12
+ "version": "6.29.3",
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.29.2",
3
+ "version": "6.29.3",
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
@@ -124,7 +124,7 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
124
124
  <br>Original behaviour and the measurements behind it via explicit keywords or FTS5 term overlap. **The `/clear` and `/compact` arm fires since v5.4.0** (R10-P1-1); before that it had never once written a row — `session_handoffs` on the maintainer's install held 4 `exit` rows and **0** `clear` rows. Two host facts settled it, both measured rather than assumed. (1) `Stop` runs at the end of every assistant *turn*, not once per session, and it deleted the session file that SessionStart reads to learn which session just ended — so the branch was unreachable, and mem sessions were minted per turn (58 prompts over 16 host sessions produced 56 mem sessions and 56 summary rows, 2026-09-07). (2) Claude Code **rotates its session id across `/clear`**: of 21 real transcripts, 12 carry a `/clear` command record, and in 12/12 that record's timestamp precedes its own file's first record by ~0.1s — the command is issued in the old session and replayed into a new file under a new id. So `Stop` no longer deletes the file, SessionStart asks the host's `source` (`startup`/`clear`/`compact`/`resume`) instead of guessing from the file, and the handoff's prompt lookup falls back to the unscoped set when the new session's id matches none. Revert path: `CLAUDE_MEM_LEGACY_STOP_UNLINK=1`
125
125
  - **Git-SHA continuation anchor** (v2.31.0) -- Handoff rows include `git_sha_at_handoff`; any handoff matching the current `HEAD` counts as continuation regardless of TTL. Code state is a stronger continuation signal than wall-clock time
126
126
  - **Startup dashboard** (v2.31.0) -- SessionStart hook aggregates `git status` + `~/.claude/tasks/*.json` + `~/.claude/plans/*.md` + most-recent exit handoff + recent event count into a single structured block injected via `hookSpecificOutput.additionalContext`
127
- - **Activity namespace** (v2.31.0) -- Dedicated `events` table + FTS5 for non-memdir types (`bugfix`, `lesson`, `bug`, `discovery`, `refactor`, `feature`, `observation`, `decision`) that don't compete with `WHAT_NOT_TO_SAVE` semantics on the observations table. CLI: `claude-mem-lite activity save|search|recent|show`. `hook-llm` routes non-memdir summary types through `persistHaikuSummary` so upgrades from observations→events are atomic. (v3.39: the `/lesson` and `/bug` slash commands were redirected from this events table to searchable **observations** — `mem_search` never read the events table, so explicit saves were unfindable; the events table remains the auto-capture activity log.)
127
+ - **Activity namespace** (v2.31.0) -- Dedicated `events` table + FTS5 for non-memdir types (`bugfix`, `lesson`, `bug`, `discovery`, `refactor`, `feature`, `observation`, `decision`) that don't compete with `WHAT_NOT_TO_SAVE` semantics on the observations table. CLI: `claude-mem-lite activity save|search|recent|show`. `hook-llm` routes non-memdir summary types through `persistHaikuSummary` so upgrades from observations→events are atomic. (v3.39: the `/lesson` and `/claude-mem-lite:bug` slash commands were redirected from this events table to searchable **observations** — `mem_search` never read the events table, so explicit saves were unfindable; the events table remains the auto-capture activity log.)
128
128
  - **In-place observation updates** -- `mem_update` tool modifies existing observations atomically (field update + FTS text rebuild in one transaction), preserving original IDs and references
129
129
  - **Bulk export** -- `mem_export` tool exports observations as JSON or JSONL, with project/type/date filtering and 1000-row pagination cap with batch guidance
130
130
  - **FTS integrity management** -- `mem_fts_check` tool verifies FTS5 index health or rebuilds indexes on demand, useful after database recovery or when search results seem wrong
@@ -165,7 +165,7 @@ is still outside it gets a message naming both sides of the mismatch instead of
165
165
 
166
166
  Plugin mode manages its own hooks/runtime. On session start it only **checks and reports** new claude-mem-lite versions; it does **not** self-overwrite plugin files in place. Update plugin-mode installs through Claude's plugin workflow.
167
167
 
168
- > **The plugin install is complete on its own** — hooks, MCP tools, and the bundled slash commands (`/mem`, `/lesson`, `/bug`, `/adopt`) all run from the plugin with no second step. The slash commands invoke the bundled CLI by an absolute path resolved from the plugin directory (`node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" <cmd>`), so they work without anything on your `PATH`. A global `claude-mem-lite` **shell** command (for running queries yourself in a terminal) is **optional** — `npm i -g claude-mem-lite` — and is a *separate* npm install: the plugin's auto-update does **not** refresh it, so re-run `npm i -g claude-mem-lite@latest` if you want that shell command kept in sync. You do **not** need it for the plugin to be fully functional.
168
+ > **The plugin install is complete on its own** — hooks, MCP tools, and the bundled slash commands (`/mem`, `/lesson`, `/claude-mem-lite:bug`, `/adopt`) all run from the plugin with no second step. The slash commands invoke the bundled CLI by an absolute path resolved from the plugin directory (`node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" <cmd>`), so they work without anything on your `PATH`. A global `claude-mem-lite` **shell** command (for running queries yourself in a terminal) is **optional** — `npm i -g claude-mem-lite` — and is a *separate* npm install: the plugin's auto-update does **not** refresh it, so re-run `npm i -g claude-mem-lite@latest` if you want that shell command kept in sync. You do **not** need it for the plugin to be fully functional.
169
169
 
170
170
  > **Auto-adopt no longer adds its block to your project's `CLAUDE.md` (next release after 6.19.4).** On every SessionStart the plugin delivers its steering text — the trigger table that boosts Claude's proactive use of `mem_recall` / `mem_save`. In a git repository that does not already carry the block it keeps a managed block in `CLAUDE.local.md` at the repository root (Claude Code loads that file like `CLAUDE.md`, also in subagents that load project instructions; the built-in Explore and Plan agents load none) and adds the file to the repository's `.git/info/exclude` unless your ignore rules already cover it, so git does not list or commit it; you get a **one-time notice** the first time it is written. Claude Code reads instruction files before the plugin's startup hook runs, so the session that creates the file gets the text added to its context instead, once (its subagents do not see it). Where Claude Code reads an `AGENTS.md` as the project's instructions, it stops reading it once a `CLAUDE.md` or `CLAUDE.local.md` exists in the session's directory or above, so there nothing is written and the text is **injected** (subagents do not see it), with a one-time notice that names that `AGENTS.md`. With `CLAUDE_MEM_RULES_STEERING=1` the block goes to `.claude/rules/claude-mem-lite.md` at the repository root instead — excluded from git the same way, loaded by Claude Code as project instructions, also in subagents that load project instructions, and it leaves `AGENTS.md` loading — with its own one-time notice; it is not the default because in a pre-registered A/B it did not beat injection by the bar set in advance (`docs/audits/20261006-d212-ab.md`). Outside git, in a repository rooted at `$HOME`, where `CLAUDE.local.md` is tracked or a symbolic link, or where the repository root is an npm package that `npm publish` would ship it with (no `"private": true`, no `files` list leaving it out, and without one no `.npmignore` naming it), nothing is written and the text is **injected** into each session's context, with a one-time notice suggesting `/adopt` (not at `$HOME`). With the variable set, the same holds where the rules file cannot be written (a symbolic link on its path, a tracked or foreign file of that name, an npm package that would ship it). The notice beside an `AGENTS.md` names it, why there is no file, and the ways to a file that keep it loading, each only where it works: Project instructions = `claude-md-and-agents-md` in `/config`, after which `CLAUDE.local.md` is written (not where `CLAUDE.local.md` is refused too: a package root, or a file you removed), and `/adopt`, whose `CLAUDE.md` imports the `AGENTS.md` beside it (not offered when an `AGENTS.md` above that directory or in a subdirectory would stop loading). The detail file the block points at lives in the plugin's data directory, named with a `~/` path. `.git/info/exclude` is read by git only: docker build contexts, archives and other packagers can pick `CLAUDE.local.md` or the rules file up (a publishable npm package root does not keep `CLAUDE.local.md`: a block written before is taken out at the next session start; in a project whose `AGENTS.md` it would switch off, the block comes out and is injected, or, with `CLAUDE_MEM_RULES_STEERING=1`, moves to the rules file where that can be written). Earlier versions added the block to your project's own `<cwd>/CLAUDE.md` (a file that is normally committed) plus a `<cwd>/.claude/plugin_claude_mem_lite.md` detail file, unasked, in every project you opened; those projects keep the block, and the first session of this version refreshes it because the text changed — to move one to the local file, run `claude-mem-lite unadopt` there and commit the removal. Why not inject everywhere: in our sandbox evaluation (one project, Claude Opus 5.5) the agent made 1.5 proactive memory saves per 8-session run with injected steering against 5.25 with the block in `CLAUDE.local.md` or `CLAUDE.md`, and subagents never saw injected text. It applies regardless of install path (npm, npx, `/plugin`, manual); silence these notices with `MEM_NO_ADOPT_HINT=1`.
171
171
  >
@@ -684,9 +684,9 @@ have not allowed, and routing the same action through Bash would sidestep it.
684
684
  /mem timeline <query> # Browse timeline around a match
685
685
  /mem browse # Tier-grouped memory dashboard
686
686
  /mem <query> # Shorthand for search
687
- /lesson <text> # Save a non-obvious lesson to the events table (v2.31.0)
688
- /bug <text> # Log a known bug + repro steps to the events table (v2.31.0)
689
- /verify # Check memories against the current code; correct stale ones after you approve
687
+ /lesson <text> # Save a non-obvious lesson as a searchable observation
688
+ /claude-mem-lite:bug <text> # Log a known bug + repro steps as a searchable observation
689
+ /claude-mem-lite:verify # Check memories against the current code; correct stale ones after you approve
690
690
  ```
691
691
 
692
692
  ### Efficient Search Workflow
@@ -701,12 +701,10 @@ have not allowed, and routing the same action through Bash would sidestep it.
701
701
 
702
702
  Opt-in mechanism that installs a slug-scoped managed block into the project's
703
703
  own `CLAUDE.md` (plus an on-demand detail doc under `.claude/`) so Claude Code
704
- loads the plugin's MCP-tool triggers as **project instructions** — a higher
705
- instruction-following authority than MCP server instructions (which are framed
706
- as tool metadata). Claude Code loads `CLAUDE.md` and the memdir `MEMORY.md` at
707
- equal weight, so steering lives in `CLAUDE.md` (the canonical home for project
708
- instructions) rather than polluting `MEMORY.md`, which is reserved for the user's
709
- own memories. The pre-v3.13 scheme wrote into `MEMORY.md`; it is migrated away
704
+ loads the plugin's MCP-tool triggers as **project instructions**, delivered in a
705
+ message after the system prompt. Steering lives in `CLAUDE.md` (the canonical home
706
+ for project instructions) rather than in the memdir `MEMORY.md`, which is reserved
707
+ for the user's own memories. The pre-v3.13 scheme wrote into `MEMORY.md`; it is migrated away
710
708
  automatically on the next SessionStart.
711
709
 
712
710
  ```bash
package/README.zh-CN.md CHANGED
@@ -95,7 +95,7 @@
95
95
  - **插件缓存 hook 自愈** -- Claude Code runtime 从 `~/.claude/plugins/cache/<mp>/<plugin>/<ver>/hooks/hooks.json` 读取插件 hook,而非 marketplace 源。当 `install.mjs` 写入 `settings.json` 的 hooks 与残留 cache `hooks.json` 同时存在(例如曾装过 marketplace 版本,或插件被 Claude Code 自动升级重建 cache),runtime 会注册两套 hook → 每次 SessionStart / UserPromptSubmit 都触发两份。`install.mjs` 和 `hook-update.mjs` 现在会清理每个 cache 版本目录下的 `hooks.json`;`hook.mjs session-start` 每次启动自愈(通过 `hasInstallManagedHooks` 门控,不影响纯插件模式用户);`install.mjs status` 会报告 cache 污染状况(自 v2.31.1 / v2.31.2 起)。
96
96
  - **Git-SHA 延续锚点**(v2.31.0)-- handoff 记录包含 `git_sha_at_handoff` 字段,任何匹配当前 `HEAD` 的 handoff 都视为延续会话,不受 TTL 限制。代码状态比时钟时间更能反映上下文延续。
97
97
  - **启动面板**(v2.31.0)-- SessionStart hook 将 `git status` + `~/.claude/tasks/*.json` + `~/.claude/plans/*.md` + 最近 /exit 交接 + 最近事件数聚合为一个结构化块,通过 `hookSpecificOutput.additionalContext` 注入。
98
- - **活动命名空间**(v2.31.0)-- 为非 memdir 类型(`bugfix` / `lesson` / `bug` / `discovery` / `refactor` / `feature` / `observation` / `decision`)启用独立的 `events` 表 + FTS5,与 observations 表的 `WHAT_NOT_TO_SAVE` 语义解耦。CLI:`claude-mem-lite activity save|search|recent|show`。`hook-llm` 通过 `persistHaikuSummary` 路由非 memdir 摘要,observations → events 升级路径是事务原子的。(v3.39:`/lesson`、`/bug` 斜杠命令已从此 events 表重定向到可搜索的 **observations**——`mem_search` 从不读 events 表,显式保存因此搜不到;events 表仍作自动捕获活动日志。)
98
+ - **活动命名空间**(v2.31.0)-- 为非 memdir 类型(`bugfix` / `lesson` / `bug` / `discovery` / `refactor` / `feature` / `observation` / `decision`)启用独立的 `events` 表 + FTS5,与 observations 表的 `WHAT_NOT_TO_SAVE` 语义解耦。CLI:`claude-mem-lite activity save|search|recent|show`。`hook-llm` 通过 `persistHaikuSummary` 路由非 memdir 摘要,observations → events 升级路径是事务原子的。(v3.39:`/lesson`、`/claude-mem-lite:bug` 斜杠命令已从此 events 表重定向到可搜索的 **observations**——`mem_search` 从不读 events 表,显式保存因此搜不到;events 表仍作自动捕获活动日志。)
99
99
 
100
100
  ## 平台支持
101
101
 
@@ -129,7 +129,7 @@ v5.1.0 到 v6.1.0 之间,`package.json` 声明的是 `os: ["darwin", "linux"]`
129
129
 
130
130
  插件模式会管理自己的运行时与钩子。SessionStart 时它现在只会**检查并提示**新版本,不会直接覆盖插件目录中的文件。插件模式请通过 Claude 的插件更新流程完成升级。
131
131
 
132
- > **插件安装本身即完整** —— hooks、MCP 工具、以及捆绑的 slash 命令(`/mem`、`/lesson`、`/bug`、`/adopt`)全部从插件内运行,无需第二步。slash 命令以从插件目录解析出的绝对路径调用捆绑 CLI(`node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" <cmd>`),因此不依赖 `PATH` 上的任何东西。全局 `claude-mem-lite` **shell** 命令(用于你自己在终端里跑查询)是**可选**的 —— `npm i -g claude-mem-lite` —— 且是**独立**的 npm 安装:插件的自动更新**不会**刷新它,想保持同步就重新跑 `npm i -g claude-mem-lite@latest`。插件要完整工作**并不需要**它。
132
+ > **插件安装本身即完整** —— hooks、MCP 工具、以及捆绑的 slash 命令(`/mem`、`/lesson`、`/claude-mem-lite:bug`、`/adopt`)全部从插件内运行,无需第二步。slash 命令以从插件目录解析出的绝对路径调用捆绑 CLI(`node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" <cmd>`),因此不依赖 `PATH` 上的任何东西。全局 `claude-mem-lite` **shell** 命令(用于你自己在终端里跑查询)是**可选**的 —— `npm i -g claude-mem-lite` —— 且是**独立**的 npm 安装:插件的自动更新**不会**刷新它,想保持同步就重新跑 `npm i -g claude-mem-lite@latest`。插件要完整工作**并不需要**它。
133
133
 
134
134
  ### 方式二:npx(一行命令)
135
135
 
@@ -533,9 +533,9 @@ README 和 `docs/ARCHITECTURE.md` 都钉在它上面。)
533
533
  /mem timeline <query> # 围绕匹配结果浏览时间线
534
534
  /mem browse # 分层记忆仪表盘
535
535
  /mem <query> # search 的简写
536
- /lesson <text> # 保存非显而易见的经验到 events 表(v2.31.0)
537
- /bug <text> # 记录已知 bug + 复现步骤到 events 表(v2.31.0)
538
- /verify # 对照当前代码核查记忆;你确认后才改正过期的记忆
536
+ /lesson <text> # 把非显而易见的经验存为可搜索的 observation
537
+ /claude-mem-lite:bug <text> # 把已知 bug + 复现步骤记为可搜索的 observation
538
+ /claude-mem-lite:verify # 对照当前代码核查记忆;你确认后才改正过期的记忆
539
539
  ```
540
540
 
541
541
  ### 高效搜索工作流
@@ -548,10 +548,11 @@ README 和 `docs/ARCHITECTURE.md` 都钉在它上面。)
548
548
 
549
549
  ### Invited Memory(邀请式记忆,v2.32+)
550
550
 
551
- Opt-in 机制——向项目 memdir(`~/.claude/projects/<encoded>/memory/MEMORY.md`)
552
- 注入单行 sentinel 包围的插件契约,Claude Code 会把它作为 **user-memory** 加载
553
- 到系统提示——比 MCP server instructions(被框定为 tool metadata)具有更高的
554
- instruction-following 权威。
551
+ Opt-in 机制——在项目自己的 `CLAUDE.md` 里写入一个按 slug 限定的托管块(外加 `.claude/` 下
552
+ 按需读取的详情文档),让 Claude Code 把插件的 MCP 工具触发规则作为**项目指令**加载(在系统
553
+ 提示之后的一条消息里送达)。引导放在 `CLAUDE.md`(项目指令的正规位置),不去占用 memdir 里
554
+ 留给用户自己记忆的 `MEMORY.md`。v3.13 之前的方案写入
555
+ `MEMORY.md`;下一次 SessionStart 会自动迁走。
555
556
 
556
557
  ```bash
557
558
  claude-mem-lite adopt # 当前项目注入
@@ -566,16 +567,19 @@ claude-mem-lite unadopt # 移除 CLAUDE.md 托管块和详情文档
566
567
  Slash 命令 `/adopt` 和 `/unadopt` 是上述 CLI 的包装。
567
568
 
568
569
  **Adopt 后会发生什么:**
569
- - `MEMORY.md` 新增一段 `<!-- claude-mem-lite:begin v1 -->…<!-- claude-mem-lite:end -->`
570
- 包裹的 `## 插件契约`,含一行 ≤150 字符、指向 `mem_recall` / `mem_save` 关键参数
571
- 的动作锚条目。
572
- - 生成 `plugin_claude_mem_lite.md` 详情文件(按需读取,不自动加载)。
570
+ - `<cwd>/CLAUDE.md` 新增一段 `<!-- claude-mem-lite:begin v1 -->…<!-- claude-mem-lite:end -->`
571
+ 包裹、带独立 `## claude-mem-lite — persistent memory` 标题的托管块,内含指向
572
+ `mem_recall` / `mem_save` / `mem_defer` 及其关键参数的触发表。只管理这一段,`CLAUDE.md`
573
+ 其余内容原样保留,可与其他插件(如 `code-graph-mcp`)的托管块共存于同一文件。
574
+ - 生成 `<cwd>/.claude/plugin_claude_mem_lite.md` 详情文件(不自动加载;`CLAUDE.md` 托管块
575
+ 指向它时按需读取)。出货内容变化(版本升级或模板变更)时托管块自动刷新,除非设
576
+ `CLAUDE_MEM_NO_TEMPLATE_REFRESH=1`。
573
577
  - 保守 hook 层自动瘦身:MCP server instructions 去掉 `WHEN TO USE` 段,
574
578
  SessionStart 注入去掉 `File Lessons` / `Key Context`。`#ID` 引用与 `Recent`
575
579
  表保留,`mem_get` 仍可随时展开。
576
580
 
577
581
  **什么时候生效?**
578
- - MEMORY.md sentinel 和 hook 层瘦身(`File Lessons` / `Key Context` / lesson
582
+ - `CLAUDE.md` 托管块和 hook 层瘦身(`File Lessons` / `Key Context` / lesson
579
583
  后缀)**下一次 SessionStart** 生效(adopt 的项目下任一新会话)。
580
584
  - MCP server instructions 是**服务启动时构建一次**,MCP 协议无 "push" 机制,
581
585
  所以 `WHEN TO USE` / `Decision rules` 两段的瘦身**只在 Claude Code 重启后**
package/adopt-content.mjs CHANGED
@@ -159,7 +159,7 @@ PreToolUse hook 在你 Read / Edit / Write 文件前已自动 \`mem_recall\` 该
159
159
 
160
160
  | 场景 | CLI |
161
161
  |------|-----|
162
- | 清理过期记忆 | \`${CLI} maintain scan --ops purge_stale\` → \`maintain execute --ops purge_stale --confirm\`(删行必须 \`--confirm\`) |
162
+ | 清理过期记忆 | \`${CLI} maintain scan\` 预览;清理本身交给用户启动的 \`/claude-mem-lite:update\` |
163
163
  | 深度优化(Haiku) | \`${CLI} optimize\`(默认 preview;\`--run\` 执行,\`--task re-enrich,normalize,cluster-merge,smart-compress\`) |
164
164
  | 压缩旧条目 | \`${CLI} compress\`(默认 preview;\`--execute\` 执行,\`--age-days N\`) |
165
165
  | FTS5 索引检查 / 重建 | \`${CLI} fts-check <check\\|rebuild>\` |
package/claudemd.mjs CHANGED
@@ -2,9 +2,8 @@
2
2
  // project-tree managed block at <cwd>/CLAUDE.md plus an on-demand detail doc
3
3
  // at <cwd>/.claude/plugin_<slug>.md.
4
4
  //
5
- // Why here and not memdir.mjs: Claude Code loads the project CLAUDE.md and the
6
- // memory-dir MEMORY.md at EQUAL weight, so steering belongs in CLAUDE.md (the
7
- // canonical home for project instructions) — seeding MEMORY.md just pollutes an
5
+ // Why here and not memdir.mjs: steering belongs in CLAUDE.md (the canonical home
6
+ // for project instructions) — seeding the memory-dir MEMORY.md just pollutes an
8
7
  // index meant for the user's own memories. This module mirrors the design
9
8
  // shipped by the sibling code-graph-mcp plugin (claude-plugin/scripts/adopt.js)
10
9
  // and the oh-my-claudecode versioned `<!-- :begin vN -->` managed-block pattern.
package/cli/common.mjs CHANGED
@@ -596,11 +596,16 @@ export function obsFieldLabel(field) {
596
596
  * 2026-08-14 A4). Say what the rows are and what deletes them — and say nothing about
597
597
  * compression, which is a different sentinel with a different lifecycle.
598
598
  *
599
+ * purge_stale deletes only the queued rows older than its retain window (D#450), so the line
600
+ * also gives the number a purge at the default window deletes now.
601
+ *
599
602
  * @param {number} n stats.pendingPurge
603
+ * @param {number} due stats.pendingPurgeDue
604
+ * @param {number} retainDays the default retain window `due` was counted at
600
605
  * @returns {string} the full indented line, identical on both surfaces.
601
606
  */
602
- export function formatPendingPurgeLine(n) {
603
- return ` Pending purge (idle-marked): ${n} (rows maintenance queued: hidden and still idle 7 days later, or queued by an earlier version — purge_stale deletes them)`;
607
+ export function formatPendingPurgeLine(n, due, retainDays) {
608
+ return ` Pending purge (idle-marked): ${n} (rows maintenance queued: hidden and still idle 7 days later, or queued by an earlier version — purge_stale deletes those older than its retain window: ${due} at the default ${retainDays} days)`;
604
609
  }
605
610
 
606
611
  /**
package/commands/adopt.md CHANGED
@@ -6,10 +6,10 @@ description: "Use when: user asks to put the claude-mem-lite steering block into
6
6
  # /adopt
7
7
 
8
8
  Install the claude-mem-lite **steering block** into the current project's
9
- `CLAUDE.md` (the canonical home for project instructions — loaded by Claude Code
10
- at system-prompt authority). It does not go in the project's memory-dir
11
- `MEMORY.md`: that index belongs to the user's own memories, and it carries no
12
- more weight than `CLAUDE.md`.
9
+ `CLAUDE.md` (the canonical home for project instructions — Claude Code loads it
10
+ into every session, in a message after the system prompt). It does not go
11
+ in the project's memory-dir `MEMORY.md`: that index belongs to the user's own
12
+ memories.
13
13
 
14
14
  Auto-adopt does not add the block to `CLAUDE.md`: in a git repository it keeps the same block
15
15
  in `CLAUDE.local.md` at the repository root, excluded from git through `.git/info/exclude`.
@@ -74,7 +74,7 @@ doc). Other plugins' blocks (e.g. `code-graph-mcp:*`) and your own prose survive
74
74
  Adoption does NOT remove the hook-based injection (SessionStart context,
75
75
  UserPromptSubmit related-memory). Those remain as fallback. Post-adopt the MCP
76
76
  `WHEN TO USE` section + the `File Lessons` / `Key Context` sections auto-trim,
77
- since the CLAUDE.md block already carries the triggers at higher authority.
77
+ since the CLAUDE.md block already carries the triggers.
78
78
 
79
79
  ## Restart caveat for the MCP-instructions trim
80
80
 
package/commands/bug.md CHANGED
@@ -3,7 +3,7 @@ name: bug
3
3
  description: "Use when: logging a known bug + repro steps you can't fix right now. Writes a searchable observation (findable via mem_search + surfaced by recall hooks; still NOT memdir). Skip for bugs you're actively fixing in the current turn — just fix them."
4
4
  ---
5
5
 
6
- # /bug
6
+ # /claude-mem-lite:bug
7
7
 
8
8
  Record a known bug + reproduction steps. Writes a searchable **observation**
9
9
  (`type=bugfix`, description in `lesson_learned`) so future sessions can find it
@@ -59,5 +59,5 @@ Confirm to user with: `Bug logged: #<id>`.
59
59
 
60
60
  ## Examples
61
61
 
62
- - `/bug intermittent test flake when DB is under load --repro "run pnpm test -- --retry=0; fails ~1 in 20 runs when CPU is busy"`.
63
- - `/bug --file src/auth.mjs --severity high "session refresh drops the X-Forwarded-For header"` — no repro steps, just a high-severity note.
62
+ - `/claude-mem-lite:bug intermittent test flake when DB is under load --repro "run pnpm test -- --retry=0; fails ~1 in 20 runs when CPU is busy"`.
63
+ - `/claude-mem-lite:bug --file src/auth.mjs --severity high "session refresh drops the X-Forwarded-For header"` — no repro steps, just a high-severity note.
@@ -8,9 +8,9 @@ description: "Use when: capturing a non-obvious lesson/gotcha/workaround after a
8
8
  Record a lesson / gotcha / workaround. Writes a searchable **observation**
9
9
  (`type=discovery` with the text as `lesson_learned`) so future sessions can
10
10
  find it via `mem_search` and the PreToolUse recall hooks surface it on the
11
- relevant files. Does NOT touch memdir — so lessons never end up in the L1
12
- system-prompt memory section and do not conflict with Claude Code's auto-memory
13
- rules on what not to save there.
11
+ relevant files. Does NOT touch memdir — so lessons never end up in the
12
+ auto-memory index Claude Code loads into every session, and do not conflict with
13
+ Claude Code's auto-memory rules on what not to save there.
14
14
 
15
15
  ## When to use
16
16
 
package/commands/mem.md CHANGED
@@ -30,8 +30,8 @@ When the user invokes `/mem`, parse their intent:
30
30
  - `/mem save <text>` → call `mem_save` MCP tool with the text as content
31
31
  - `/mem stats` → run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" stats` via Bash
32
32
  - `/mem get <ids>` → run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" get <ids>` via Bash
33
- - `/mem cleanup` → run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain scan` via Bash, report pending purge count and stale items to user, ask for confirmation, then run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain execute --ops purge_stale --confirm` via Bash if confirmed. **`--confirm` is required and is not optional politeness:** without it the command prints a dry-run PREVIEW and deletes nothing, while still succeeding — so you would report a cleanup that never happened.
34
- - `/mem cleanup Nd` (e.g. `60d`) → same as above but add `--retain-days N` to only purge items older than N days. **`--retain-days` must be between 7 and 365**; the CLI rejects anything outside that range, so `/mem cleanup 3d` cannot be honoured — say so rather than silently substituting the default.
33
+ - `/mem cleanup` → run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain scan` via Bash and report the pending purge and stale counts to the user. Then run the purge command below without `--confirm` first: it prints how many rows it would delete and deletes nothing. If that count is 0, tell the user nothing is old enough to purge yet and stop. Otherwise report that count, not the scan's pending count, which includes rows too recent to purge. Ask for confirmation, and if confirmed run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain execute --ops purge_stale --confirm` via Bash. **`--confirm` is required and is not optional politeness:** without it the command prints a dry-run PREVIEW and deletes nothing, while still succeeding — so you would report a cleanup that never happened.
34
+ - `/mem cleanup Nd` (e.g. `60d`) → same as above but add `--retain-days N` to both purge runs to only purge items older than N days. **`--retain-days` must be between 7 and 365**; the CLI rejects anything outside that range, so `/mem cleanup 3d` cannot be honoured — say so rather than silently substituting the default.
35
35
  - `/mem cleanup keep Nd` (e.g. `keep 14d`) → same as above with `--retain-days N`, same 7–365 range.
36
36
  - `/mem <query>` (no subcommand) → treat as search, run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" search <query>` via Bash
37
37
 
@@ -22,9 +22,9 @@ Run each command below via Bash; the CLI reaches the maintenance operations whet
22
22
 
23
23
  1. Run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain scan` to analyze maintenance candidates
24
24
  2. Report scan results to the user (duplicates, stale items, broken items, boostable items, **pending purge** items)
25
- 3. **If pending purge items > 0**: first run the purge command below without `--confirm`: it prints how many rows it would delete and deletes nothing. Report that count to the user and ask for confirmation. If confirmed, run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain execute --ops purge_stale --confirm`. **`--confirm` is required:** without it the command prints a dry-run preview and deletes nothing while still succeeding, so you would report a purge that never happened. User may optionally add `--retain-days N` (default 30, allowed range 7–365) to both runs to control how many days of data to keep; the CLI rejects a value outside that range, so tell the user instead of substituting the default. Do NOT purge without explicit user confirmation. The purge comes before the next step on purpose: decay there queues more rows for purge, and those wait for a later run, so what gets deleted is what the user approved.
25
+ 3. **If pending purge items > 0**: first run the purge command below without `--confirm`: it prints how many rows it would delete and deletes nothing. If that count is 0, tell the user nothing is old enough to purge yet and go on to the next step. Otherwise report that count to the user and ask for confirmation. If confirmed, run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain execute --ops purge_stale --confirm`. **`--confirm` is required:** without it the command prints a dry-run preview and deletes nothing while still succeeding, so you would report a purge that never happened. User may optionally add `--retain-days N` (default 30, allowed range 7–365) to both runs to control how many days of data to keep; the CLI rejects a value outside that range, so tell the user instead of substituting the default. Do NOT purge without explicit user confirmation. The purge comes before the next step on purpose: decay there queues more rows for purge, and those wait for a later run instead of joining the purge the user approved.
26
26
  4. Run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain execute` to apply safe automatic changes — with no `--ops` it runs the default set: cleanup, decay, boost, then demote_pinned (left out when `CLAUDE_MEM_SKIP_DEMOTE_PINNED=1`)
27
- 5. If duplicates were found in scan, review them and run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain execute --ops dedup --merge-ids keepId:removeId,...` — keep the more important/recent observation in each pair. Groups are comma-separated and the first id in each is kept (`10:11,20:21:22`). For pairs similar enough to merge without review the scan prints a ready-made list after `--merge-ids`; pass it to the command above, since the command the scan prints uses a bare `claude-mem-lite`, which may not be installed
27
+ 5. If duplicates were found in scan, review them and run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" maintain execute --ops dedup --merge-ids keepId:removeId,...` — keep the more important/recent observation in each pair. Groups are comma-separated and the first id in each is kept (`10:11,20:21:22`). For pairs similar enough to merge without review the scan prints a ready-to-run command after `Ready-to-use:`; run it as printed
28
28
  6. Run `node "${CLAUDE_PLUGIN_ROOT}/cli.mjs" compress --execute` for old low-value observations (without `--execute` it only previews)
29
29
 
30
30
  ### Phase 2: Summary
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: verify
3
- description: "Use when: the user explicitly asks to verify, audit or correct their stored memories against the current code (e.g. \"check my memories for stale ones\", /verify). You check each memory with read-only repo tools and propose corrections; nothing is written until the user approves the exact plan. Not for routine recall or saving."
3
+ description: "Use when: the user explicitly asks to verify, audit or correct their stored memories against the current code (e.g. \"check my memories for stale ones\", /claude-mem-lite:verify). You check each memory with read-only repo tools and propose corrections; nothing is written until the user approves the exact plan. Not for routine recall or saving."
4
4
  ---
5
5
 
6
- # /verify — check memories against the code, then correct the stale ones
6
+ # /claude-mem-lite:verify — check memories against the code, then correct the stale ones
7
7
 
8
8
  Memories go stale: a bug recorded as open gets fixed, a measurement gets retracted, a
9
9
  mechanism is replaced. Measured on 118 live memories across 7 repos, ~10% were STALE and
@@ -12,8 +12,6 @@ this — cheap single-shot models misjudged it (precision 0.36) and model-writte
12
12
  were false in 28 of 72 cases. What works is YOU reading the code: you propose, the user
13
13
  approves the exact plan, and `verify-apply` is the only thing that writes.
14
14
 
15
- (If another plugin also defines `/verify`, this one is `/claude-mem-lite:verify`.)
16
-
17
15
  ## Arguments
18
16
 
19
17
  - `--from <YYYY-MM-DD>`: only memories saved on or after this date (default: all live).
package/hook-context.mjs CHANGED
@@ -690,8 +690,8 @@ export function buildSessionContextLines(
690
690
 
691
691
  // Phase A (QUIET_HOOKS) + Phase D (adopted sentinel): drop descriptive
692
692
  // File Lessons / Key Context sections when the user has opted into low-noise
693
- // hooks OR adopted invited-memory (MEMORY.md sentinel carries the triggers
694
- // at higher system-prompt authority). The Recent table still fires so #IDs
693
+ // hooks OR adopted invited-memory (the steering block carries the triggers
694
+ // in the project's instructions). The Recent table still fires so #IDs
695
695
  // remain reachable via mem_get. The collector sees only rows that survive
696
696
  // BOTH the quiet gate and the per-section slice — rendered rows, nothing else.
697
697
  const quiet = effectiveQuiet();
package/lib/activity.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // Pure functions over the events table. No I/O beyond the passed-in db handle.
3
3
  //
4
4
  // Activity events are NOT memdir-compatible types; they live here precisely
5
- // so they don't pollute the L1 system-prompt memory section.
5
+ // so they don't pollute the auto-memory index Claude Code loads into every session.
6
6
 
7
7
  import { sanitizeFtsQuery } from '../utils.mjs';
8
8
  import { eventSearchTerms } from '../nlp.mjs';
package/lib/get-core.mjs CHANGED
@@ -177,10 +177,10 @@ export function supersededNotice(row) {
177
177
  // String markers written by /verify (lib/verify-apply-core.mjs). Named, because "auto-dedup
178
178
  // or merge" would send a reader looking for a duplicate that does not exist.
179
179
  if (row.superseded_by === 'verify-retired') {
180
- return '⚠ RETRACTED — retired by /verify as out of date, with no replacement. The fields below are the withdrawn version.';
180
+ return '⚠ RETRACTED — retired by /claude-mem-lite:verify as out of date, with no replacement. The fields below are the withdrawn version.';
181
181
  }
182
182
  if (row.superseded_by === 'verify-undone') {
183
- return '⚠ RETRACTED — a /verify correction withdrawn by its undo; the original it replaced is live again. The fields below are the withdrawn version.';
183
+ return '⚠ RETRACTED — a /claude-mem-lite:verify correction withdrawn by its undo; the original it replaced is live again. The fields below are the withdrawn version.';
184
184
  }
185
185
  return '⚠ RETRACTED — superseded (auto-dedup or merge). The fields below are the withdrawn version.';
186
186
  }
@@ -34,6 +34,8 @@ import { DAY_MS } from './time-constants.mjs';
34
34
  import { snapshotDb } from './db-backup.mjs';
35
35
  import { isTransientPath } from './bash-file-targets.mjs';
36
36
  export const STALE_AGE_MS = 30 * DAY_MS;
37
+ /** Days of pending-purge rows purge_stale keeps when the caller names no retain window. */
38
+ export const DEFAULT_RETAIN_DAYS = 30;
37
39
  export const OP_CAP = 1000;
38
40
  export const SCAN_LIMIT = 500;
39
41
  export const DUPLICATE_LIMIT = 50;
@@ -896,6 +898,13 @@ export function maintenanceStats(db, { projectFilter, baseParams, staleAge }) {
896
898
  WHERE compressed_into = ${COMPRESSED_PENDING_PURGE} AND ${NOT_EXPLICIT_SAVE_SQL} ${projectFilter}`,
897
899
  )
898
900
  .get(...baseParams);
901
+ // D#450: purge_stale deletes only the queued rows older than its retain window, so the count
902
+ // above can exceed what a purge removes. Say how many a purge at the default window would.
903
+ const pendingPurgeDue = purgeStalePreview(
904
+ db,
905
+ { projectFilter, baseParams },
906
+ Date.now() - DEFAULT_RETAIN_DAYS * DAY_MS,
907
+ ).candidates;
899
908
  // D12: rows maintenance has hidden and will queue once they stay idle through the grace.
900
909
  // Without this line a decay run that hid 3 rows read "Total active: 0 … Pending purge: 0".
901
910
  const hidden = db
@@ -916,6 +925,7 @@ export function maintenanceStats(db, { projectFilter, baseParams, staleAge }) {
916
925
  return {
917
926
  ...stats,
918
927
  pendingPurge: pendingPurge.count,
928
+ pendingPurgeDue,
919
929
  hidden: hidden.count,
920
930
  explicitToShowAgain: explicitToShowAgain.count,
921
931
  };
@@ -987,7 +997,7 @@ export function runMaintainOps(
987
997
  ctx,
988
998
  ops,
989
999
  {
990
- retainDays = 30,
1000
+ retainDays = DEFAULT_RETAIN_DAYS,
991
1001
  retainCutoff,
992
1002
  confirmed = false,
993
1003
  mergeGroups = null,
@@ -25,7 +25,7 @@ export function isQuietHooks() {
25
25
  }
26
26
 
27
27
  // Phase D (v2.32.1+) → v3.13: if the current project has adopted our steering,
28
- // the contract is already loaded at system-prompt authority — so hook +
28
+ // the contract already loads with the project's instructions — so hook +
29
29
  // MCP-instruction output can also go quiet. v3.13 moved that contract from the
30
30
  // memory-dir MEMORY.md sentinel to the project CLAUDE.md managed block, so check
31
31
  // the new scheme first and keep the legacy memdir sentinel as a fallback (an
@@ -7,7 +7,10 @@
7
7
  // (optimizeRun wide/aliases re-enrich) stays the safety net for saves that ignore it.
8
8
  //
9
9
  // Shared by server.mjs (mem_save) and mem-cli.mjs (cmdSave) — one gate, two phrasings,
10
- // no twin drift.
10
+ // no twin drift. Both name the CLI by its path (D#448): mem_update is left out of tools/list,
11
+ // so the model cannot call it, and a plugin install has no `claude-mem-lite` on PATH.
12
+ import { CLI_INVOKE } from '../cli-path.mjs';
13
+
11
14
  const NUDGE_TYPES = new Set(['bugfix', 'decision']);
12
15
 
13
16
  /**
@@ -18,6 +21,6 @@ export function buildLessonNudge({ type, id, lessonCaptured, surface }) {
18
21
  if (lessonCaptured || !NUDGE_TYPES.has(type)) return '';
19
22
  const what = type === 'decision' ? 'constraint + tradeoff' : 'root cause + fix';
20
23
  return surface === 'cli'
21
- ? `\n[mem] ⚠ ${type} #${id} saved without a lesson — capture the ${what} while it's fresh: claude-mem-lite update ${id} --lesson "<${what}>"`
22
- : ` ⚠ Saved without lesson_learned — a ${type} is worth keeping only with its lesson. Capture it now: mem_update(id=${id}, lesson_learned="<${what}>").`;
24
+ ? `\n[mem] ⚠ ${type} #${id} saved without a lesson — capture the ${what} while it's fresh: ${CLI_INVOKE} update ${id} --lesson "<${what}>"`
25
+ : ` ⚠ Saved without lesson_learned — a ${type} is worth keeping only with its lesson. Capture it now: ${CLI_INVOKE} update ${id} --lesson "<${what}>"`;
23
26
  }
@@ -9,6 +9,7 @@ import { truncate } from '../format-utils.mjs';
9
9
  import { COMPRESSED_PENDING_PURGE } from '../utils.mjs';
10
10
 
11
11
  import { DAY_MS } from './time-constants.mjs';
12
+ import { CLI_INVOKE } from '../cli-path.mjs';
12
13
  // v3.42 F7: noise/low-signal ratios must divide by the LIVE (non-compressed) observation
13
14
  // count, NOT the all-rows total. Both numerators (lowVal, lowSignalTitle) already exclude
14
15
  // compressed rows (`compressed_into = 0`); dividing by an all-inclusive denominator
@@ -222,7 +223,7 @@ export function formatQualityReport(data) {
222
223
  const purgeGap = (purgeNum - 10).toFixed(1);
223
224
  const purgeStatus = purgeNum <= 10 ? '✅' : purgeNum <= 30 ? '🟡' : '🔴';
224
225
  lines.push(
225
- ` ${purgeStatus} Pending purge ≤ 10% → currently ${purgePct}% (${purgeRow.pending_purge}/${purgeRow.compressed}) (gap ${purgeGap >= 0 ? '+' : ''}${purgeGap}pp)${purgeNum > 10 ? ' — run: claude-mem-lite maintain execute --ops purge_stale --confirm' : ''}`,
226
+ ` ${purgeStatus} Pending purge ≤ 10% → currently ${purgePct}% (${purgeRow.pending_purge}/${purgeRow.compressed}) (gap ${purgeGap >= 0 ? '+' : ''}${purgeGap}pp)${purgeNum > 10 ? ` — preview: ${CLI_INVOKE} maintain execute --ops purge_stale (add --confirm to delete the rows it counts)` : ''}`,
226
227
  );
227
228
  }
228
229
 
package/mem-cli.mjs CHANGED
@@ -63,6 +63,7 @@ import {
63
63
  PINNED_INJ_THRESHOLD,
64
64
  resolveDefaultMaintainOps,
65
65
  ALL_MAINTAIN_OPS,
66
+ DEFAULT_RETAIN_DAYS,
66
67
  } from './lib/maintain-core.mjs';
67
68
  // snapshotDb left with maintain-core: the pre-maintain snapshot is part of the op ORDER
68
69
  // (it must see the pre-existing pending rows), so it moved into runMaintainOps (P1-5).
@@ -2773,7 +2774,7 @@ function cmdMaintain(db, args) {
2773
2774
  ` Pinned-but-uncited (inj>=${PINNED_INJ_THRESHOLD}, cited=0, above floor): ${stats.pinned} — floored by the default maintain set since v3.76.0, no lesson → 1, lesson → 2 (opt out: CLAUDE_MEM_SKIP_DEMOTE_PINNED=1)`,
2774
2775
  );
2775
2776
  out(formatHiddenLine(stats.hidden));
2776
- out(formatPendingPurgeLine(stats.pendingPurge));
2777
+ out(formatPendingPurgeLine(stats.pendingPurge, stats.pendingPurgeDue, DEFAULT_RETAIN_DAYS));
2777
2778
  if (stats.explicitToShowAgain > 0)
2778
2779
  out(
2779
2780
  ` Explicit saves an earlier release hid (the next execute that runs decay — the default set does — shows them again): ${stats.explicitToShowAgain}`,
@@ -2796,7 +2797,7 @@ function cmdMaintain(db, args) {
2796
2797
  const remove = keep === d.a ? d.b : d.a;
2797
2798
  return `${keep.id}:${remove.id}`;
2798
2799
  });
2799
- out(` Ready-to-use: claude-mem-lite maintain execute --ops dedup --merge-ids ${mergeIds.join(',')}`);
2800
+ out(` Ready-to-use: ${CLI_INVOKE} maintain execute --ops dedup --merge-ids ${mergeIds.join(',')}`);
2800
2801
  }
2801
2802
 
2802
2803
  if (manualReview.length > 0) {
@@ -2832,7 +2833,7 @@ function cmdMaintain(db, args) {
2832
2833
  // whole command atomically. The old code validated inside db.transaction() with a bare
2833
2834
  // `return`, so an earlier op (cleanup hard-delete / decay / boost) had already mutated and
2834
2835
  // the transaction COMMITTED despite the exit-1 error (audit MED-2 atomicity).
2835
- let retainDays = 30;
2836
+ let retainDays = DEFAULT_RETAIN_DAYS;
2836
2837
  if (ops.includes('purge_stale') && flags['retain-days'] !== undefined) {
2837
2838
  // Number + isInteger (not parseInt) so "7.5"/"30x" are rejected rather than silently
2838
2839
  // truncated — parity with the mem_maintain zod .int().min(7).max(365) bound.
@@ -3383,7 +3384,7 @@ Commands:
3383
3384
  --project P Override the restored project for every row
3384
3385
  --dry-run Preview what would be restored without writing
3385
3386
 
3386
- verify-apply <file> Apply agent-verified memory corrections (/verify writes these).
3387
+ verify-apply <file> Apply agent-verified memory corrections (/claude-mem-lite:verify writes these).
3387
3388
  Dry run by default: prints the changes and a plan digest.
3388
3389
  --apply Back up the target rows, apply in one transaction, read back
3389
3390
  --digest D Required with --apply: the digest the approved dry run printed
@@ -3476,7 +3477,7 @@ Commands:
3476
3477
 
3477
3478
  Valid types: bugfix, lesson, bug, discovery, refactor, feature, observation, decision
3478
3479
  --files (plural, comma-split) preferred; --file (singular) kept for back-compat.
3479
- Use /lesson or /bug slash commands for faster capture (T8).
3480
+ Use /lesson or /claude-mem-lite:bug slash commands for faster capture (T8).
3480
3481
 
3481
3482
  adopt Put the claude-mem-lite managed block into this project's
3482
3483
  CLAUDE.md (normally committed, so the team gets it) + a
package/memdir.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // Claude Code memdir at ~/.claude/projects/<encoded>/memory/.
3
3
  //
4
4
  // The public API lets a plugin install a single sentinel-wrapped line into
5
- // MEMORY.md (auto-loaded by Claude Code into the system prompt) plus an
5
+ // MEMORY.md (loaded by Claude Code at the start of every session) plus an
6
6
  // on-demand `plugin_<slug>.md` detail file. Writes are idempotent, hash-guarded
7
7
  // against manual user edits, and capped by a 180-line budget so the injection
8
8
  // block never gets truncated by Claude Code's 200-line MEMORY.md cap.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "6.29.2",
3
+ "version": "6.29.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "claude-mem-lite",
9
- "version": "6.29.2",
9
+ "version": "6.29.3",
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.29.2",
3
+ "version": "6.29.3",
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",
@@ -33,8 +33,8 @@ import { PINNED_INJ_THRESHOLD } from './lib/maintain-core.mjs';
33
33
  // ─── MCP Server Instructions Builder ───────────────────────────────────────
34
34
  // Phase A (v2.31.3+): when quiet=true, drops WHEN-TO-USE proactive-trigger and
35
35
  // Decision-rules sections; keeps the irreducible CLI/MCP tool list. Intended
36
- // for users who adopted invited-memory (MEMORY.md sentinel carries the same
37
- // triggers at higher authority). Default false preserves v2.31.2 behavior.
36
+ // for users who adopted invited-memory (the steering block carries the same
37
+ // triggers). Default false preserves v2.31.2 behavior.
38
38
  // The CLI-vs-MCP round-trip routing rule lives in BASE (not VERBOSE) on purpose:
39
39
  // it must reach adopted (quiet) projects too, where tool-heavy sessions defer the
40
40
  // mem_* tools behind ToolSearch (CLI via Bash = 1 model round-trip; ToolSearch +
@@ -73,10 +73,10 @@ const INSTRUCTIONS_VERBOSE = [
73
73
  ' • "what happened recently?" → mem_recent (NOT search with empty query)',
74
74
  ' • "what do we know about file.mjs?" → mem_recall (NOT grep + manual search)',
75
75
  ' • "show me around observation #42" → mem_timeline (NOT mem_get + manual navigation)',
76
- ' • "clean up old/duplicate memories" → mem_maintain (NOT manual mem_delete loop)',
77
- ' • "is the search index healthy?" → mem_fts_check (NOT manual COUNT queries)',
78
- ' • "overview of memory tiers" → mem_browse (NOT mem_search + manual grouping)',
79
- ' • "export for backup" → mem_export (NOT manual SELECT queries)',
76
+ ' • "clean up old/duplicate memories" → CLI `maintain scan` to preview; the cleanup itself is /claude-mem-lite:update, which the user starts (NOT deleting rows one by one)',
77
+ ' • "is the search index healthy?" → CLI `fts-check check` (NOT manual COUNT queries)',
78
+ ' • "overview of memory tiers" → CLI `browse` (NOT mem_search + manual grouping)',
79
+ ' • "export for backup" → CLI `export` (NOT manual SELECT queries)',
80
80
  ];
81
81
 
82
82
  export function buildServerInstructions(quiet = false) {
package/server.mjs CHANGED
@@ -55,6 +55,7 @@ import {
55
55
  STALE_AGE_MS,
56
56
  resolveDefaultMaintainOps,
57
57
  DEFAULT_MAINTAIN_OPS,
58
+ DEFAULT_RETAIN_DAYS,
58
59
  } from './lib/maintain-core.mjs';
59
60
  // snapshotDb left with maintain-core: the pre-maintain snapshot is part of the op ORDER
60
61
  // (it must see the pre-existing pending rows), so it moved into runMaintainOps (P1-5).
@@ -534,7 +535,7 @@ function formatSearchOutput(
534
535
  // skipped the branch on the reasoning that it "never renders the label" — true of
535
536
  // the label, false of the echo.
536
537
  if (expanded !== args.query) hint.push(`Searched as: ${queryLabel(expanded)}`);
537
- hint.push('Tip: check spelling, try broader terms, or use mem_stats to see available data.');
538
+ hint.push('Tip: check spelling, try broader terms, or use mem_recent to see what is stored.');
538
539
  }
539
540
  }
540
541
  return { content: [{ type: 'text', text: hint.join('\n') }] };
@@ -1742,7 +1743,7 @@ server.registerTool(
1742
1743
  ` Decayable (imp>1, never accessed or injected, past the stale age): ${stats.decayable}`,
1743
1744
  ` Boostable (accessed>3, imp<3): ${stats.boostable}`,
1744
1745
  formatHiddenLine(stats.hidden),
1745
- formatPendingPurgeLine(stats.pendingPurge),
1746
+ formatPendingPurgeLine(stats.pendingPurge, stats.pendingPurgeDue, DEFAULT_RETAIN_DAYS),
1746
1747
  ...(stats.explicitToShowAgain > 0
1747
1748
  ? [
1748
1749
  ` Explicit saves an earlier release hid (the next execute that runs decay — the default set does — shows them again): ${stats.explicitToShowAgain}`,
@@ -1825,8 +1826,8 @@ server.registerTool(
1825
1826
  // preview tells the caller to type next. Before the extraction this block rendered
1826
1827
  // `inj>=8` as a literal while the CLI twin interpolated PINNED_INJ_THRESHOLD.
1827
1828
  const results = runMaintainOps(db, mctx, ops, {
1828
- retainDays: args.retain_days ?? 30,
1829
- retainCutoff: Date.now() - (args.retain_days ?? 30) * DAY_MS,
1829
+ retainDays: args.retain_days ?? DEFAULT_RETAIN_DAYS,
1830
+ retainCutoff: Date.now() - (args.retain_days ?? DEFAULT_RETAIN_DAYS) * DAY_MS,
1830
1831
  confirmed: purgeConfirmed,
1831
1832
  mergeGroups: args.merge_ids || null,
1832
1833
  mergeIdsProvided: Boolean(args.merge_ids),
package/tool-schemas.mjs CHANGED
@@ -780,7 +780,7 @@ export const tools = [
780
780
  '\n' +
781
781
  'DO NOT use when:\n' +
782
782
  ' - You only want one record (use mem_get)\n' +
783
- ' - You have no anchor in mind and are just browsing (use mem_recent or mem_browse)\n' +
783
+ ' - You have no anchor in mind and are just browsing (use mem_recent)\n' +
784
784
  ' - The sequence is obvious from commit history (use git log)\n' +
785
785
  ' - You want "recent activity around X" (use mem_recent or mem_search sort="time")\n' +
786
786
  '\n' +
@@ -802,7 +802,7 @@ export const tools = [
802
802
  'DO NOT use when:\n' +
803
803
  ' - You have not seen the ID referenced anywhere (use mem_search first)\n' +
804
804
  ' - You only need title/type — that is already in the search result line\n' +
805
- ' - You are speculatively paging through IDs (use mem_recent / mem_browse)\n' +
805
+ ' - You are speculatively paging through IDs (use mem_recent)\n' +
806
806
  '\n' +
807
807
  'USE when:\n' +
808
808
  ' - Hook-injected context shows #NN and you need the full narrative/lesson\n' +