claude-mem-lite 6.19.3 → 6.20.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +22 -17
- package/README.zh-CN.md +17 -15
- package/adopt-cli.mjs +156 -31
- package/adopt-content.mjs +20 -7
- package/bash-utils.mjs +45 -7
- package/claudemd.mjs +132 -55
- package/commands/adopt.md +16 -6
- package/commands/unadopt.md +21 -11
- package/haiku-client.mjs +115 -27
- package/hook-handoff.mjs +72 -7
- package/hook-llm.mjs +11 -20
- package/hook-shared.mjs +4 -0
- package/hook.mjs +144 -12
- package/lib/bash-file-targets.mjs +16 -1
- package/lib/browse-core.mjs +7 -1
- package/lib/cite-back-hint.mjs +52 -4
- package/lib/data-paths.mjs +23 -1
- package/lib/git-state.mjs +10 -1
- package/lib/local-steering.mjs +386 -0
- package/lib/maintain-core.mjs +97 -0
- package/lib/plan-reader.mjs +2 -4
- package/lib/quiet-scope.mjs +45 -3
- package/lib/task-reader.mjs +3 -5
- package/mem-cli.mjs +3 -2
- package/memdir.mjs +45 -1
- package/npm-shrinkwrap.json +5 -5
- package/package.json +3 -2
- package/scripts/pre-tool-recall.js +1 -1
- package/secret-scrub.mjs +5 -2
- package/source-files.mjs +1 -0
- package/utils.mjs +30 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"plugins": [
|
|
10
10
|
{
|
|
11
11
|
"name": "claude-mem-lite",
|
|
12
|
-
"version": "6.
|
|
12
|
+
"version": "6.20.0",
|
|
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.
|
|
3
|
+
"version": "6.20.0",
|
|
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
|
@@ -167,9 +167,9 @@ Plugin mode manages its own hooks/runtime. On session start it only **checks and
|
|
|
167
167
|
|
|
168
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.
|
|
169
169
|
|
|
170
|
-
> **Auto-adopt
|
|
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`, subagents included) 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). 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`). 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` up (a publishable npm package root does not keep it: a block written before the directory became a package is taken out at the next session start). 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 both notices with `MEM_NO_ADOPT_HINT=1`.
|
|
171
171
|
>
|
|
172
|
-
>
|
|
172
|
+
> **Want the block in `CLAUDE.md` instead** (for example to share it with your team)? Run `claude-mem-lite adopt`: it writes the slug-scoped managed block into `<cwd>/CLAUDE.md` plus the detail file, preserving everything outside the block, and removes the `CLAUDE.local.md` copy. A project that carries the block is kept in sync on **every SessionStart** — refreshed when the shipped template changes — and gets no local or injected copy on top of it, also in sessions started from a subdirectory. A `CLAUDE.local.md` block the plugin created and you deleted (or `claude-mem-lite unadopt` removed) is not written back — the text is injected instead — until `claude-mem-lite adopt --enable`. Turn steering off per project with `claude-mem-lite adopt --disable` (it also removes the `CLAUDE.local.md` block; a `CLAUDE.md` block stays until `unadopt`; `--enable` re-arms) or globally with `export MEM_NO_AUTO_ADOPT=1` (blocks already written stay and keep loading until `unadopt`); freeze the blocks against template refreshes with `CLAUDE_MEM_NO_TEMPLATE_REFRESH=1`. `claude-mem-lite unadopt` removes the `CLAUDE.md` block, the detail file, and the `CLAUDE.local.md` block with its exclude entry.
|
|
173
173
|
|
|
174
174
|
### Method 2: npx (one-liner)
|
|
175
175
|
|
|
@@ -543,12 +543,12 @@ automatically on the next SessionStart.
|
|
|
543
543
|
|
|
544
544
|
```bash
|
|
545
545
|
claude-mem-lite adopt # install for current project
|
|
546
|
-
claude-mem-lite adopt --all #
|
|
546
|
+
claude-mem-lite adopt --all # legacy clean-up: strip the old memory-dir sentinel in every project
|
|
547
547
|
claude-mem-lite adopt --status # list adopted/disabled projects + current gating snapshot
|
|
548
548
|
claude-mem-lite adopt --dry-run # preview without writing
|
|
549
549
|
claude-mem-lite adopt --disable # opt out of auto-adopt for current project (writes .mem-no-auto-adopt sentinel)
|
|
550
550
|
claude-mem-lite adopt --enable # re-arm auto-adopt for current project (deletes the sentinel)
|
|
551
|
-
claude-mem-lite unadopt # remove
|
|
551
|
+
claude-mem-lite unadopt # remove the CLAUDE.md block + doc, and the CLAUDE.local.md block (not written again)
|
|
552
552
|
```
|
|
553
553
|
|
|
554
554
|
Slash commands `/adopt` and `/unadopt` wrap the same CLI.
|
|
@@ -586,18 +586,23 @@ Slash commands `/adopt` and `/unadopt` wrap the same CLI.
|
|
|
586
586
|
ever rewritten, and duplicate / CRLF-orphaned copies are collapsed to one.
|
|
587
587
|
Unlike the legacy `MEMORY.md` scheme there is no line-budget gate — `CLAUDE.md`
|
|
588
588
|
has no truncation cap.
|
|
589
|
-
- **Auto-adopt runs on EVERY SessionStart, for any install path
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
`
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
589
|
+
- **Auto-adopt runs on EVERY SessionStart, for any install path, and no longer adds
|
|
590
|
+
the block to `CLAUDE.md` (next release after 6.19.4).** A git project without the
|
|
591
|
+
block gets it in `CLAUDE.local.md` at the git root (kept out of git via
|
|
592
|
+
`.git/info/exclude`), or in SessionStart context outside git; a project that
|
|
593
|
+
carries the `CLAUDE.md` block (explicit `adopt`, or an older version's auto-adopt)
|
|
594
|
+
has it kept in sync — refreshed when the shipped template changes (freeze with
|
|
595
|
+
`CLAUDE_MEM_NO_TEMPLATE_REFRESH=1`) — and gets no local or injected copy, also in
|
|
596
|
+
sessions started from a subdirectory. A `CLAUDE.local.md` block the plugin created
|
|
597
|
+
and you (or `unadopt`) removed is not written back; `adopt --enable` re-arms it.
|
|
598
|
+
Per-project opt-out: `claude-mem-lite adopt --disable` (writes a durable
|
|
599
|
+
`<memdir>/.mem-no-auto-adopt` sentinel that survives marker deletion / plugin
|
|
600
|
+
reinstalls; run at the repository root it also covers sessions started in its
|
|
601
|
+
subdirectories; it removes the local block).
|
|
602
|
+
Global opt-out: `MEM_NO_AUTO_ADOPT=1` (blocks already written stay until `unadopt`).
|
|
598
603
|
- The fallback hook layer is never removed from source — conditional trim is
|
|
599
|
-
runtime-gated on
|
|
600
|
-
full verbose output.
|
|
604
|
+
runtime-gated on steering being delivered (block present, or injection on), so
|
|
605
|
+
projects that opted out get the full verbose output.
|
|
601
606
|
|
|
602
607
|
See [the invited-memory design][invited-memory] for the full design (including the
|
|
603
608
|
reusable template other plugins can follow). It is a development-time document and
|
|
@@ -1020,8 +1025,8 @@ claude-mem-lite.
|
|
|
1020
1025
|
| `CLAUDE_MEM_BASH_RECALL` | File recall before a Bash command that views (`cat`, `sed -n`, `head`…) or writes (`sed -i`, `cat > f`, a python patch…) a file, like the Read / Edit recall. A bash prefilter keeps Node from starting for other commands. `off` disables this leg only. | _(on)_ |
|
|
1021
1026
|
| `CLAUDE_MEM_LESSON_GROUNDING` | An auto-captured event keeps its lesson only when the lesson quotes the window's own diagnosis (a failing output line, a comment the edit added, or the commit message); otherwise the row is kept without it at importance 1, below every injection face. `off` keeps unquoted lessons. | _(on)_ |
|
|
1022
1027
|
| `CLAUDE_MEM_LESSON_OUTPUT_CAP` | An auto-captured lesson that shares four consecutive words with TOOL OUTPUT (a command's printed text or a tool's response, which whoever controls that output can write) is kept off the injection faces: an event keeps its row and lesson, searchable, at importance 1; a `change` observation, whose importance later reads can raise, loses the lesson, and the lesson-less row is then dropped like any other (kept only with `CLAUDE_MEM_KEEP_LOW_SIGNAL=1`). Four shared words of filler ("is not in the") count too, so a lesson quoting your own comment or commit message is demoted when it also happens to share such a run with output in the same window. The row's title is not checked. `off` restores the model's importance and the lesson. | _(on)_ |
|
|
1023
|
-
| `MEM_NO_AUTO_ADOPT` | Global opt-out for auto-adopt (v2.82.0+). `1`
|
|
1024
|
-
| `MEM_NO_ADOPT_HINT` | Silences the one-line "Invited-memory 未启用:`claude-mem-lite adopt`…" hint that SessionStart appends when the current project hasn't been adopted. Since v2.82.1 auto-adopt runs on every SessionStart for any install path, so this hint typically surfaces only when you've explicitly opted out (`MEM_NO_AUTO_ADOPT=1` or `claude-mem-lite adopt --disable`). | _(disabled)_ |
|
|
1028
|
+
| `MEM_NO_AUTO_ADOPT` | Global opt-out for auto-adopt (v2.82.0+). `1` stops auto-adopt across **all** projects — no injected steering, no new `CLAUDE.local.md`, and no sync of an existing `CLAUDE.md` or `CLAUDE.local.md` block (those stay, and keep loading, until `claude-mem-lite unadopt`). For per-project opt-out use `claude-mem-lite adopt --disable` instead (writes a durable `<memdir>/.mem-no-auto-adopt` sentinel that survives marker deletion). | _(disabled)_ |
|
|
1029
|
+
| `MEM_NO_ADOPT_HINT` | Silences the one-time notices shown to you the first time a project gets `CLAUDE.local.md` or is steered by injection (the latter suggests `/adopt`, except at `$HOME`), and the one-line "Invited-memory 未启用:`claude-mem-lite adopt`…" hint that SessionStart appends when the current project hasn't been adopted. Since v2.82.1 auto-adopt runs on every SessionStart for any install path, so this hint typically surfaces only when you've explicitly opted out (`MEM_NO_AUTO_ADOPT=1` or `claude-mem-lite adopt --disable`). | _(disabled)_ |
|
|
1025
1030
|
|
|
1026
1031
|
### What gets injected into your context
|
|
1027
1032
|
|
package/README.zh-CN.md
CHANGED
|
@@ -154,9 +154,9 @@ node install.mjs install
|
|
|
154
154
|
1. **安装依赖** -- `npm install --omit=dev`(编译原生 `better-sqlite3`)
|
|
155
155
|
2. **注册 MCP 服务器** -- `mem-lite` 服务器,包含 18 个工具(9 个核心通过 `tools/list` 暴露 + 9 个隐藏但可调;完整表见 Usage 段)。v2.78 前服务器名为通用的 `mem`,现已改名为 `mem-lite` 避免与用户其它 `.mcp.json` 冲突;工具名(`mem_search`/`mem_recall` 等)保持不变。
|
|
156
156
|
|
|
157
|
-
> **自动 adopt
|
|
157
|
+
> **自动 adopt 不再把托管块加进你项目的 `CLAUDE.md`(6.19.4 之后的下一个版本起)。** 每次 SessionStart,插件都会送达引导文本(提升 Claude 主动调用 `mem_recall` / `mem_save` 的触发表)。在还没有托管块的 git 仓库里,它把托管块写进仓库根目录的 `CLAUDE.local.md`(Claude Code 像加载 `CLAUDE.md` 一样加载它,子代理也能看到),并把这个文件加进仓库的 `.git/info/exclude`(你的 ignore 规则已覆盖时不加),所以 git 不列出、也不提交它;第一次写入时你会看到一条**一次性提示**。Claude Code 在插件的启动钩子运行之前就读取了指令文件,所以创建这个文件的那个会话改为在上下文里注入一次引导(该会话的子代理看不到)。不在 git 仓库里、仓库根是 `$HOME`、`CLAUDE.local.md` 已被 git 跟踪或是符号链接,或者仓库根是一个 `npm publish` 会把它带上的 npm 包(没有 `"private": true`,没有把它排除在外的 `files` 列表,没有 `files` 列表时也没有列出它的 `.npmignore`)时,什么都不写,改为每个会话把引导**注入**上下文,并一次性提示可以运行 `/adopt`(在 `$HOME` 下不提示)。托管块指向的详情文件放在插件自己的数据目录,以 `~/` 路径书写。`.git/info/exclude` 只对 git 生效:docker 构建上下文、归档和其他打包工具都可能把 `CLAUDE.local.md` 打进去(可发布的 npm 包根目录不会保留这个文件:目录变成 npm 包之前写入的托管块,会在下一次会话启动时移除)。旧版本会在你打开的每个项目里,未经询问就向项目自己的 `<cwd>/CLAUDE.md`(通常会进 git)写入托管块,外加 `<cwd>/.claude/plugin_claude_mem_lite.md` 详情文件;这些项目保留托管块,本版本的第一个会话会因为文本变化刷新它——想把某个项目迁到本地文件,在那里运行 `claude-mem-lite unadopt` 并提交这次删除。为什么不全部改成注入:沙箱实测中(一个项目,Claude Opus 5.5),8 个会话里模型主动记录的次数,注入时平均 1.5 次,写在 `CLAUDE.local.md` 或 `CLAUDE.md` 时 5.25 次;而且子代理完全看不到注入的文本。**任何安装路径都生效**(npm、npx、`/plugin`、手动);两条提示都可以用 `MEM_NO_ADOPT_HINT=1` 关闭。
|
|
158
158
|
>
|
|
159
|
-
>
|
|
159
|
+
> **想把托管块改写进 `CLAUDE.md`**(例如与团队共享)?运行 `claude-mem-lite adopt`:它向 `<cwd>/CLAUDE.md` 写入 slug 限定的托管块和详情文件,块以外的内容逐字保留,并删除 `CLAUDE.local.md` 里的那份。带托管块的项目在**每次 SessionStart** 都会同步,出货模板变了会刷新,且不会再叠加本地副本或注入,从子目录启动的会话也一样。插件创建过、又被你删掉(或被 `claude-mem-lite unadopt` 删掉)的 `CLAUDE.local.md` 托管块不会被写回,改为注入,直到运行 `claude-mem-lite adopt --enable`。关闭方式:项目级 `claude-mem-lite adopt --disable`(同时删除 `CLAUDE.local.md` 托管块;`CLAUDE.md` 托管块保留到 `unadopt`;重新启用用 `--enable`);全局 `export MEM_NO_AUTO_ADOPT=1`(已写入的托管块保留并继续加载,直到 `unadopt`);只冻结模板刷新用 `CLAUDE_MEM_NO_TEMPLATE_REFRESH=1`。`claude-mem-lite unadopt` 会移除 `CLAUDE.md` 托管块、详情文件,以及 `CLAUDE.local.md` 托管块和它的 exclude 条目。
|
|
160
160
|
3. **配置钩子** -- 全部七个生命周期事件:`SessionStart`、`PreCompact`、`PreToolUse`、`PostToolUse`、`PostToolUseFailure`、`Stop`、`UserPromptSubmit`
|
|
161
161
|
4. **创建数据目录** -- `~/.claude-mem-lite/`(隐藏目录),存放数据库与运行时文件
|
|
162
162
|
5. **自动迁移** -- 自动检测 `~/.claude-mem/`(原版 claude-mem)或 `~/claude-mem-lite/`(v0.5 前的非隐藏目录),将数据库和运行时文件迁移到 `~/.claude-mem-lite/`,原目录保持不变
|
|
@@ -451,12 +451,12 @@ instruction-following 权威。
|
|
|
451
451
|
|
|
452
452
|
```bash
|
|
453
453
|
claude-mem-lite adopt # 当前项目注入
|
|
454
|
-
claude-mem-lite adopt --all #
|
|
454
|
+
claude-mem-lite adopt --all # 旧方案清理:在所有项目里删除旧的 memory 目录哨兵
|
|
455
455
|
claude-mem-lite adopt --status # 列出已 adopt / 已禁用项目 + 当前 gate 快照
|
|
456
456
|
claude-mem-lite adopt --dry-run # 只打印不写入
|
|
457
457
|
claude-mem-lite adopt --disable # 当前项目关闭 auto-adopt(写 .mem-no-auto-adopt 哨兵)
|
|
458
458
|
claude-mem-lite adopt --enable # 当前项目重新启用 auto-adopt(删哨兵)
|
|
459
|
-
claude-mem-lite unadopt #
|
|
459
|
+
claude-mem-lite unadopt # 移除 CLAUDE.md 托管块和详情文档,以及 CLAUDE.local.md 托管块(之后不再写回)
|
|
460
460
|
```
|
|
461
461
|
|
|
462
462
|
Slash 命令 `/adopt` 和 `/unadopt` 是上述 CLI 的包装。
|
|
@@ -482,15 +482,17 @@ Slash 命令 `/adopt` 和 `/unadopt` 是上述 CLI 的包装。
|
|
|
482
482
|
- Hash 守护:你手动改了 sentinel 段 → 下一次 adopt 报 `UserEditedError`,
|
|
483
483
|
除非显式 `--force`。
|
|
484
484
|
- 预算门:MEMORY.md 已 >180 行时拒绝新增(避开 Claude Code 200 行截断)。
|
|
485
|
-
- **任何安装路径每次 SessionStart 都自动 adopt
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
`
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
-
|
|
493
|
-
|
|
485
|
+
- **任何安装路径每次 SessionStart 都自动 adopt,且不再把托管块加进 `CLAUDE.md`(6.19.4
|
|
486
|
+
之后的下一个版本起)。** 没有托管块的 git 项目,引导写进 git 根目录的 `CLAUDE.local.md`
|
|
487
|
+
(经 `.git/info/exclude` 排除在 git 之外),不在 git 里时注入 SessionStart 上下文;带
|
|
488
|
+
`CLAUDE.md` 托管块的项目(显式 adopt 或旧版本写入)保持同步,出货模板变化会刷新(用
|
|
489
|
+
`CLAUDE_MEM_NO_TEMPLATE_REFRESH=1` 冻结),且不会再叠加本地副本或注入,从子目录启动的会话
|
|
490
|
+
也一样。插件创建过、又被你(或 `unadopt`)删掉的 `CLAUDE.local.md` 托管块不会被写回;
|
|
491
|
+
`adopt --enable` 可重新启用。项目级关闭:`claude-mem-lite adopt --disable`(写
|
|
492
|
+
`<memdir>/.mem-no-auto-adopt` 哨兵,存活于 marker 删除 / 插件重装;在仓库根目录运行时,对从子目录启动的
|
|
493
|
+
会话同样生效;并删除本地托管块)。全局关闭:`export MEM_NO_AUTO_ADOPT=1`(已写入的托管块保留,直到 `unadopt`)。
|
|
494
|
+
- 保守 hook 层源码永不删——条件瘦身按"引导已送达"(有托管块或注入开启)做 runtime
|
|
495
|
+
判断,关闭了引导的项目仍看完整 verbose 输出。
|
|
494
496
|
|
|
495
497
|
完整设计见 `docs/plans/2026-04-16-invited-memory-pattern.md`(含其它插件
|
|
496
498
|
可复用的模板)。
|
|
@@ -786,8 +788,8 @@ npm run benchmark:gate # CI 门控:指标回退超过 5% 容差时失败
|
|
|
786
788
|
| `CLAUDE_MEM_BASH_RECALL` | 在查看(`cat`、`sed -n`、`head`…)或写入(`sed -i`、`cat > f`、python 补丁…)文件的 Bash 命令执行前做文件召回,与 Read / Edit 召回相同。bash 预过滤让其他命令不启动 Node。设为 `off` 只关闭这一路。 | _(开启)_ |
|
|
787
789
|
| `CLAUDE_MEM_LESSON_GROUNDING` | 自动捕获的 event 只有在教训引用了本窗口自己的诊断文字(失败输出行、编辑新增的注释或提交信息)时才保留教训;否则保留这一行但去掉教训,importance 降为 1,低于所有注入面的门槛。设为 `off` 保留未引用原文的教训。 | _(开启)_ |
|
|
788
790
|
| `CLAUDE_MEM_LESSON_OUTPUT_CAP` | 自动捕获的教训如果和**工具输出**(命令打印的文字或工具返回的内容,能控制这段输出的人就能写它)有连续 4 个词相同,就不会进入注入面:event 保留这一行和教训、仍可搜索,importance 降为 1;`change` 类 observation 的 importance 之后会被读取次数抬高,所以改为去掉教训,没有教训的这一行随后会像其他同类行一样被丢弃(只有设了 `CLAUDE_MEM_KEEP_LOW_SIGNAL=1` 才保留)。连续 4 个虚词(例如 "is not in the")也算,所以引用你自己的注释或提交信息的教训,只要碰巧和同一窗口的输出共有这样一串词,也会被降级。这一行的标题不在检查范围内。设为 `off` 恢复模型给出的 importance 和教训。 | _(开启)_ |
|
|
789
|
-
| `MEM_NO_AUTO_ADOPT` | auto-adopt 全局关闭开关(v2.82.0+)。设为 `1`
|
|
790
|
-
| `MEM_NO_ADOPT_HINT` |
|
|
791
|
+
| `MEM_NO_AUTO_ADOPT` | auto-adopt 全局关闭开关(v2.82.0+)。设为 `1` 在**所有**项目停止自动 adopt——不注入引导文本、不新建 `CLAUDE.local.md`,也不同步已有的 `CLAUDE.md` 或 `CLAUDE.local.md` 托管块(这些托管块会保留并继续加载,直到 `claude-mem-lite unadopt`)。项目级关闭走 `claude-mem-lite adopt --disable`(写 `<memdir>/.mem-no-auto-adopt` 哨兵,存活于 marker 删除)。 | _(禁用)_ |
|
|
792
|
+
| `MEM_NO_ADOPT_HINT` | 静音项目第一次得到 `CLAUDE.local.md`、或第一次以注入方式被引导时给你的一次性提示(后者建议运行 `/adopt`,在 `$HOME` 下除外),以及当前项目未 adopt 时 SessionStart 追加的那一行 "Invited-memory 未启用…" 提示。v2.82.1 起任何安装路径每次 SessionStart 都自动 adopt,所以该提示一般只在你显式 opt out(`MEM_NO_AUTO_ADOPT=1` 或 `claude-mem-lite adopt --disable`)的项目才会出现。 | _(禁用)_ |
|
|
791
793
|
|
|
792
794
|
## 许可证
|
|
793
795
|
|
package/adopt-cli.mjs
CHANGED
|
@@ -14,10 +14,14 @@
|
|
|
14
14
|
// every memdir). New-scheme adoption happens per-project on SessionStart (cwd known).
|
|
15
15
|
|
|
16
16
|
import { existsSync, readdirSync, statSync, mkdirSync, writeFileSync, unlinkSync, readFileSync } from 'fs';
|
|
17
|
-
import {
|
|
18
|
-
import { join, isAbsolute } from 'path';
|
|
17
|
+
import { claudeConfigDir, claudeStatePath } from './lib/data-paths.mjs';
|
|
18
|
+
import { join, isAbsolute, resolve } from 'path';
|
|
19
19
|
import {
|
|
20
20
|
memdirPath,
|
|
21
|
+
disableSentinelPath,
|
|
22
|
+
isAutoAdoptDisabled,
|
|
23
|
+
isAutoAdoptDisabledFor,
|
|
24
|
+
legacyMemdirPath,
|
|
21
25
|
removePluginSection,
|
|
22
26
|
removePluginDoc,
|
|
23
27
|
isAdopted as memdirIsAdopted,
|
|
@@ -29,12 +33,36 @@ import {
|
|
|
29
33
|
isAdopted as claudeMdIsAdopted,
|
|
30
34
|
hasResidue as claudeMdHasResidue,
|
|
31
35
|
needsRefresh,
|
|
36
|
+
readBlock,
|
|
32
37
|
migrateLegacyMemoryDir,
|
|
33
38
|
hasLegacyMemdirSentinel,
|
|
34
39
|
claudeMdPath,
|
|
35
40
|
detailDocPath,
|
|
36
41
|
} from './claudemd.mjs';
|
|
37
42
|
import { PLUGIN_SLUG, CURRENT_SENTINEL_VERSION, buildClaudeMdBlock, getDetailDoc } from './adopt-content.mjs';
|
|
43
|
+
import {
|
|
44
|
+
localSteeringRoot,
|
|
45
|
+
readLocalSteering,
|
|
46
|
+
writeLocalSteering,
|
|
47
|
+
removeLocalSteering,
|
|
48
|
+
ensureSteeringDetailDoc,
|
|
49
|
+
localMdPath,
|
|
50
|
+
forgetLocalSteering,
|
|
51
|
+
tildePath,
|
|
52
|
+
} from './lib/local-steering.mjs';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Remove the auto-written CLAUDE.local.md block for the project at `cwd`, if there is one.
|
|
56
|
+
* @returns {{action: 'removed'|'partial'|'absent', residue?: string, path?: string}}
|
|
57
|
+
*/
|
|
58
|
+
function dropLocalSteering(cwd) {
|
|
59
|
+
const root = localSteeringRoot(cwd);
|
|
60
|
+
if (!root) return { action: 'absent' };
|
|
61
|
+
// Runs even when the block is already gone (deleted by hand): removeLocalSteering then drops
|
|
62
|
+
// the exclude lines it added (pre-tag defect review, mutation M7).
|
|
63
|
+
const r = removeLocalSteering(root, PLUGIN_SLUG);
|
|
64
|
+
return r.action === 'absent' ? { action: 'absent' } : { ...r, path: localMdPath(root) };
|
|
65
|
+
}
|
|
38
66
|
|
|
39
67
|
function log(msg) {
|
|
40
68
|
console.log(msg);
|
|
@@ -45,7 +73,7 @@ function detectCwd() {
|
|
|
45
73
|
}
|
|
46
74
|
|
|
47
75
|
function projectsRoot() {
|
|
48
|
-
return join(
|
|
76
|
+
return join(claudeConfigDir(), 'projects');
|
|
49
77
|
}
|
|
50
78
|
|
|
51
79
|
function listAllMemdirs() {
|
|
@@ -66,7 +94,7 @@ function listAllMemdirs() {
|
|
|
66
94
|
}
|
|
67
95
|
|
|
68
96
|
function claudeConfigPath() {
|
|
69
|
-
return
|
|
97
|
+
return claudeStatePath();
|
|
70
98
|
}
|
|
71
99
|
|
|
72
100
|
// Real adopted-project paths come from Claude Code's own ~/.claude.json `projects`
|
|
@@ -93,23 +121,9 @@ function hasFlag(args, flag) {
|
|
|
93
121
|
}
|
|
94
122
|
|
|
95
123
|
// ─── Per-project auto-adopt opt-out sentinel ─────────────────────────────────
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
// `rm ~/.claude-mem-lite/runtime/.auto-adopt-*`. Managed via
|
|
100
|
-
// `claude-mem-lite adopt --disable` / `--enable`. silentAutoAdopt checks it
|
|
101
|
-
// at entry and skips WITHOUT writing the runtime marker, so toggling
|
|
102
|
-
// `--enable` re-arms auto-adopt on the next SessionStart. Kept in the memdir
|
|
103
|
-
// (not the project tree) so it survives `unadopt` cleaning out .claude/.
|
|
104
|
-
const DISABLE_SENTINEL_BASENAME = '.mem-no-auto-adopt';
|
|
105
|
-
|
|
106
|
-
export function disableSentinelPath(memdir) {
|
|
107
|
-
return join(memdir, DISABLE_SENTINEL_BASENAME);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function isAutoAdoptDisabled(memdir) {
|
|
111
|
-
return existsSync(disableSentinelPath(memdir));
|
|
112
|
-
}
|
|
124
|
+
// The `.mem-no-auto-adopt` escape hatch lives in memdir.mjs since report §9-A: lib/quiet-scope.mjs
|
|
125
|
+
// has to ask it too (injected steering counts as adopted), and lib/ may not import this face.
|
|
126
|
+
export { disableSentinelPath, isAutoAdoptDisabled };
|
|
113
127
|
|
|
114
128
|
/**
|
|
115
129
|
* cmdAdopt — write the CLAUDE.md managed block + detail doc for the current
|
|
@@ -151,7 +165,15 @@ function adoptOne(cwd, { force, dryRun }) {
|
|
|
151
165
|
const mig = migrateLegacyMemoryDir(cwd, PLUGIN_SLUG, { force });
|
|
152
166
|
const r = writeManaged(cwd, { slug: PLUGIN_SLUG, version, block, doc });
|
|
153
167
|
const migNote = mig.action === 'removed' ? ' (+migrated legacy memdir)' : '';
|
|
154
|
-
|
|
168
|
+
// CLAUDE.md now carries the block; a CLAUDE.local.md copy would load it twice.
|
|
169
|
+
const local = dropLocalSteering(cwd);
|
|
170
|
+
const localNote =
|
|
171
|
+
local.action === 'absent'
|
|
172
|
+
? ''
|
|
173
|
+
: local.action === 'skipped-symlink'
|
|
174
|
+
? ` (left ${local.path} alone: it is a symlink)`
|
|
175
|
+
: ` (+removed the block from ${local.path})`;
|
|
176
|
+
log(`[adopt] ${cwd} → ${r.action}${migNote}${localNote}`);
|
|
155
177
|
return r;
|
|
156
178
|
} catch (e) {
|
|
157
179
|
log(`[adopt] ${cwd} → error: ${e.message}`);
|
|
@@ -215,14 +237,19 @@ function migrateAll(args) {
|
|
|
215
237
|
* existing users whose marker predates v3.13 still migrate). Order:
|
|
216
238
|
* 1. respect per-project `.mem-no-auto-adopt` opt-out → skip.
|
|
217
239
|
* 2. migrate legacy memory-dir sentinel away (idempotent; no-op once gone).
|
|
218
|
-
* 3.
|
|
219
|
-
*
|
|
240
|
+
* 3. a managed block in CLAUDE.md → keep it in sync, refreshing if shipped content drifted
|
|
241
|
+
* (unless CLAUDE_MEM_NO_TEMPLATE_REFRESH=1), and drop a local copy (no double steering).
|
|
242
|
+
* 4. otherwise, inside a git work tree → the block in <top-level>/CLAUDE.local.md, kept
|
|
243
|
+
* out of commits via info/exclude; return 'local' (`written` says what changed). In a
|
|
244
|
+
* subdirectory, a root CLAUDE.md block → 'already-adopted', a root opt-out → 'disabled'.
|
|
245
|
+
* 5. otherwise (no git, $HOME, a tracked or symlinked CLAUDE.local.md, an npm-publishable
|
|
246
|
+
* root, any git failure) → write nothing, return 'inject' (the caller puts the steering
|
|
247
|
+
* into SessionStart context) — or 'already-adopted' when that file carries the block.
|
|
220
248
|
* Silent: never logs, never throws. Returns { ok, action, reason } for debugLog.
|
|
221
249
|
*/
|
|
222
250
|
export function silentAutoAdopt({ cwd, markerDir, markerKey }) {
|
|
223
|
-
const memdir = memdirPath(cwd);
|
|
224
251
|
try {
|
|
225
|
-
if (
|
|
252
|
+
if (isAutoAdoptDisabledFor(cwd)) {
|
|
226
253
|
return { ok: true, action: 'disabled', reason: 'disabled-by-sentinel' };
|
|
227
254
|
}
|
|
228
255
|
migrateLegacyMemoryDir(cwd, PLUGIN_SLUG);
|
|
@@ -231,6 +258,50 @@ export function silentAutoAdopt({ cwd, markerDir, markerKey }) {
|
|
|
231
258
|
const doc = getDetailDoc();
|
|
232
259
|
const version = CURRENT_SENTINEL_VERSION;
|
|
233
260
|
|
|
261
|
+
// Report §9-A (docs/audits/20260929-sandbox-usage-eval.md): a project with NO managed
|
|
262
|
+
// block is no longer written into. The first SessionStart used to add CLAUDE.md and
|
|
263
|
+
// .claude/plugin_claude_mem_lite.md to every repository the user opened — 4 of 4 sandbox
|
|
264
|
+
// repos, swept into the next `git add -A` — and the startup dashboard then reported them
|
|
265
|
+
// as the user's uncommitted work. The same text now rides SessionStart context
|
|
266
|
+
// ('inject'); only an explicit `adopt` writes files. A project that already carries the
|
|
267
|
+
// block (adopted explicitly, or by an older version) is kept in sync exactly as before,
|
|
268
|
+
// including a half state whose detail doc went missing.
|
|
269
|
+
//
|
|
270
|
+
// r3 (tasks/specs/sandbox-eval-l3.md, report §8.5): injection kept the repository clean but
|
|
271
|
+
// cost most of the proactive memory writes (1.5 vs 5.25 per trajectory) and never reached
|
|
272
|
+
// subagents (0/12). Inside a git work tree the block now goes to CLAUDE.local.md, which the
|
|
273
|
+
// host loads like CLAUDE.md and info/exclude keeps out of commits (5.25 writes, 12/12).
|
|
274
|
+
const hasBlock = readBlock(cwd, PLUGIN_SLUG).body !== null;
|
|
275
|
+
if (!hasBlock) {
|
|
276
|
+
if (markerDir && markerKey) writeMarker(markerDir, markerKey);
|
|
277
|
+
const root = localSteeringRoot(cwd);
|
|
278
|
+
if (root) {
|
|
279
|
+
// A session started below the top-level (pre-tag claims review P2-3, P1-4): the host
|
|
280
|
+
// loads the root's CLAUDE.md as an ancestor, so a block there already steers this
|
|
281
|
+
// session; and an opt-out recorded for the root covers the file that lives there.
|
|
282
|
+
if (resolve(root) !== resolve(cwd)) {
|
|
283
|
+
if (readBlock(root, PLUGIN_SLUG).body !== null)
|
|
284
|
+
return { ok: true, action: 'already-adopted', reason: 'root-claude-md' };
|
|
285
|
+
// Off for the project means off here too: no file, no injected copy, no /adopt offer
|
|
286
|
+
// (delta review P2-2; lib/quiet-scope.mjs mirrors it).
|
|
287
|
+
if (isAutoAdoptDisabledFor(root)) return { ok: true, action: 'disabled', reason: 'root-disabled' };
|
|
288
|
+
}
|
|
289
|
+
const localBlock = buildClaudeMdBlock({ detailDocRef: tildePath(ensureSteeringDetailDoc()) });
|
|
290
|
+
const r = writeLocalSteering(root, {
|
|
291
|
+
slug: PLUGIN_SLUG,
|
|
292
|
+
version,
|
|
293
|
+
block: localBlock,
|
|
294
|
+
frozen: process.env.CLAUDE_MEM_NO_TEMPLATE_REFRESH === '1',
|
|
295
|
+
});
|
|
296
|
+
if (r.action !== 'refused') return { ok: true, action: 'local', written: r.action };
|
|
297
|
+
// A refused file that carries the block anyway (tracked, or behind a link) is loaded by
|
|
298
|
+
// the host: injecting it too would load it twice.
|
|
299
|
+
if (r.present) return { ok: true, action: 'already-adopted', reason: `local-${r.reason}` };
|
|
300
|
+
return { ok: true, action: 'inject', reason: `local-${r.reason}` };
|
|
301
|
+
}
|
|
302
|
+
return { ok: true, action: 'inject' };
|
|
303
|
+
}
|
|
304
|
+
dropLocalSteering(cwd);
|
|
234
305
|
let action = 'already-adopted';
|
|
235
306
|
if (!claudeMdIsAdopted(cwd, PLUGIN_SLUG)) {
|
|
236
307
|
writeManaged(cwd, { slug: PLUGIN_SLUG, version, block, doc });
|
|
@@ -267,11 +338,29 @@ export function hasAutoAdoptMarker(markerDir, markerKey) {
|
|
|
267
338
|
/**
|
|
268
339
|
* cmdDisable — `claude-mem-lite adopt --disable [--all]`.
|
|
269
340
|
* Writes `<memdir>/.mem-no-auto-adopt` so SessionStart auto-adopt skips this
|
|
270
|
-
* project permanently. Does NOT remove
|
|
341
|
+
* project permanently. Does NOT remove a CLAUDE.md block (the user asked for that one, by
|
|
342
|
+
* running adopt) — pair with `unadopt`. DOES remove the CLAUDE.local.md block, which
|
|
343
|
+
* auto-adopt wrote on its own: "turn the guidance off here" has to mean it stops loading.
|
|
271
344
|
*/
|
|
272
345
|
function cmdDisable(args) {
|
|
273
346
|
const all = hasFlag(args, '--all');
|
|
274
|
-
const
|
|
347
|
+
const localTargets = all ? listKnownProjectDirs() : [detectCwd()];
|
|
348
|
+
for (const dir of localTargets) {
|
|
349
|
+
const r = dropLocalSteering(dir);
|
|
350
|
+
if (r.action !== 'absent') log(`[adopt --disable] ${r.path} → ${r.action}`);
|
|
351
|
+
if (r.residue) log(` ⚠ ${r.residue}`);
|
|
352
|
+
}
|
|
353
|
+
// Known projects too, not only memdirs that already exist: Claude Code creates `memory/`
|
|
354
|
+
// only when its auto-memory is used, and a project without one was left armed (pre-tag
|
|
355
|
+
// defect review P2-4).
|
|
356
|
+
const targets = all
|
|
357
|
+
? [
|
|
358
|
+
...new Set([
|
|
359
|
+
...listAllMemdirs().map((m) => m.memdir),
|
|
360
|
+
...listKnownProjectDirs().map((d) => memdirPath(d)),
|
|
361
|
+
]),
|
|
362
|
+
]
|
|
363
|
+
: [memdirPath(detectCwd())];
|
|
275
364
|
|
|
276
365
|
if (targets.length === 0) {
|
|
277
366
|
log('[adopt --disable] no memdirs found');
|
|
@@ -304,7 +393,19 @@ function cmdDisable(args) {
|
|
|
304
393
|
*/
|
|
305
394
|
function cmdEnable(args) {
|
|
306
395
|
const all = hasFlag(args, '--all');
|
|
307
|
-
|
|
396
|
+
// Re-arm the CLAUDE.local.md block too: a block the user or `unadopt` removed is not written
|
|
397
|
+
// back until this forgets that it was (lib/local-steering.mjs).
|
|
398
|
+
for (const dir of all ? listKnownProjectDirs() : [detectCwd()]) {
|
|
399
|
+
const root = localSteeringRoot(dir);
|
|
400
|
+
if (root && forgetLocalSteering(root))
|
|
401
|
+
log(`[adopt --enable] ${localMdPath(root)} → will be written again`);
|
|
402
|
+
}
|
|
403
|
+
// The legacy ~/.claude memdir too: isAutoAdoptDisabledFor still honours a sentinel an earlier
|
|
404
|
+
// version left there, so --enable must be able to remove it.
|
|
405
|
+
const cwdNow = detectCwd();
|
|
406
|
+
const targets = all
|
|
407
|
+
? listAllMemdirs().map((m) => m.memdir)
|
|
408
|
+
: [memdirPath(cwdNow), legacyMemdirPath(cwdNow)].filter(Boolean);
|
|
308
409
|
|
|
309
410
|
if (targets.length === 0) {
|
|
310
411
|
log('[adopt --enable] no memdirs found');
|
|
@@ -342,6 +443,11 @@ function statusAll() {
|
|
|
342
443
|
log('[adopt --status] current project:');
|
|
343
444
|
log(` cwd: ${cwd}`);
|
|
344
445
|
log(` CLAUDE.md: ${adoptedHere ? `✓ adopted (${CURRENT_SENTINEL_VERSION})` : '✗ not adopted'}`);
|
|
446
|
+
const localRoot = localSteeringRoot(cwd);
|
|
447
|
+
const localHere = localRoot && readLocalSteering(localRoot, PLUGIN_SLUG).body !== null;
|
|
448
|
+
log(
|
|
449
|
+
` local: ${localHere ? `✓ ${localMdPath(localRoot)} (auto-written, excluded from git)` : localRoot ? '✗ none' : '— none here: not a git work tree, or its root is $HOME or / (steering is injected at session start)'}`,
|
|
450
|
+
);
|
|
345
451
|
if (hasLegacyMemdirSentinel(cwd, PLUGIN_SLUG)) {
|
|
346
452
|
log(' legacy: ⚠ memory-dir sentinel still present (migrates on next SessionStart, or run `adopt`)');
|
|
347
453
|
}
|
|
@@ -404,8 +510,21 @@ function unadoptAll(args) {
|
|
|
404
510
|
// 1. New scheme: scrub CLAUDE.md managed blocks across known project paths.
|
|
405
511
|
const projectDirs = listKnownProjectDirs();
|
|
406
512
|
let blocks = 0,
|
|
407
|
-
partial = 0
|
|
513
|
+
partial = 0,
|
|
514
|
+
locals = 0;
|
|
408
515
|
for (const dir of projectDirs) {
|
|
516
|
+
// The CLAUDE.local.md block auto-adopt writes (r3) is swept first and independently: a
|
|
517
|
+
// project carries one or the other, and either way nothing of ours should survive.
|
|
518
|
+
const root = localSteeringRoot(dir);
|
|
519
|
+
if (root && readLocalSteering(root, PLUGIN_SLUG).body !== null) {
|
|
520
|
+
if (dryRun) log(`[unadopt --all --dry-run] ${localMdPath(root)} → would-remove`);
|
|
521
|
+
else {
|
|
522
|
+
const lr = removeLocalSteering(root, PLUGIN_SLUG);
|
|
523
|
+
log(`[unadopt --all] ${localMdPath(root)} → ${lr.action}`);
|
|
524
|
+
if (lr.residue) log(` ⚠ ${lr.residue}`);
|
|
525
|
+
}
|
|
526
|
+
locals++;
|
|
527
|
+
}
|
|
409
528
|
// hasResidue, not isAdopted: the sweep must also catch PARTIAL residue
|
|
410
529
|
// (block without detail doc, or an orphaned doc/state sidecar) —
|
|
411
530
|
// isAdopted's block-AND-doc gate skipped those projects forever.
|
|
@@ -452,7 +571,7 @@ function unadoptAll(args) {
|
|
|
452
571
|
log('');
|
|
453
572
|
const partialNote = partial > 0 ? ` (+${partial} partial-residue cleanup(s))` : '';
|
|
454
573
|
log(
|
|
455
|
-
`[unadopt --all] ${dryRun ? 'would remove' : 'removed'} ${blocks} CLAUDE.md block(s)${partialNote} across ${projectDirs.length} known project(s); ${legacy} legacy memory-dir sentinel(s) ${dryRun ? 'pending' : 'cleaned'}.`,
|
|
574
|
+
`[unadopt --all] ${dryRun ? 'would remove' : 'removed'} ${blocks} CLAUDE.md block(s)${partialNote} and ${locals} CLAUDE.local.md block(s) across ${projectDirs.length} known project(s); ${legacy} legacy memory-dir sentinel(s) ${dryRun ? 'pending' : 'cleaned'}.`,
|
|
456
575
|
);
|
|
457
576
|
if (projectDirs.length === 0) {
|
|
458
577
|
log(
|
|
@@ -472,6 +591,9 @@ export function cmdUnadopt(args = []) {
|
|
|
472
591
|
|
|
473
592
|
const cwd = detectCwd();
|
|
474
593
|
if (dryRun) {
|
|
594
|
+
const root = localSteeringRoot(cwd);
|
|
595
|
+
if (root && readLocalSteering(root, PLUGIN_SLUG).body !== null)
|
|
596
|
+
log(`[unadopt --dry-run] would-remove the block in ${localMdPath(root)}`);
|
|
475
597
|
const blockState = claudeMdHasResidue(cwd, PLUGIN_SLUG)
|
|
476
598
|
? 'would-remove CLAUDE.md block + detail doc'
|
|
477
599
|
: 'no CLAUDE.md block';
|
|
@@ -488,6 +610,9 @@ export function cmdUnadopt(args = []) {
|
|
|
488
610
|
const mig = migrateLegacyMemoryDir(cwd, PLUGIN_SLUG, { force });
|
|
489
611
|
const migNote = mig.action === 'removed' ? ' (+cleaned legacy memdir)' : '';
|
|
490
612
|
log(`[unadopt] ${cwd} → ${r.action}${migNote}`);
|
|
613
|
+
const local = dropLocalSteering(cwd);
|
|
614
|
+
if (local.action !== 'absent') log(`[unadopt] ${local.path} → ${local.action}`);
|
|
615
|
+
if (local.residue) log(` ⚠ ${local.residue}`);
|
|
491
616
|
// 'partial' is the outcome that used to print as 'absent': the sidecar files are gone but
|
|
492
617
|
// an unpaired sentinel still holds steering text in the user's CLAUDE.md, and only they can
|
|
493
618
|
// decide where that text ends. Silence here is what let it survive every sweep.
|
package/adopt-content.mjs
CHANGED
|
@@ -39,7 +39,7 @@ const CLI = 'claude-mem-lite';
|
|
|
39
39
|
* per-project-type variation. Keep it tight (cheap always-loaded context); the
|
|
40
40
|
* full tables + rules live in the detail doc this block points to.
|
|
41
41
|
*/
|
|
42
|
-
export function buildClaudeMdBlock() {
|
|
42
|
+
export function buildClaudeMdBlock({ detailDocRef = '.claude/plugin_claude_mem_lite.md' } = {}) {
|
|
43
43
|
// Intentionally machine-stable: MCP tool names only, NO CLI_INVOKE (that
|
|
44
44
|
// resolves to an absolute path that differs per install — it would make this
|
|
45
45
|
// committed/refreshed block churn across machines). The detail doc holds the
|
|
@@ -50,15 +50,16 @@ PreToolUse hooks already run \`mem_recall\` for past lessons before Read/Edit/Wr
|
|
|
50
50
|
|
|
51
51
|
| When | Call |
|
|
52
52
|
|------|------|
|
|
53
|
-
| Before Edit/Write | hook already recalled; if an injected \`#NN\` lesson changed what you did,
|
|
54
|
-
|
|
|
53
|
+
| Before Edit/Write | hook already recalled; if an injected \`#NN\` lesson changed what you did, add the bare tag \`(#NN)\` once at the end of the sentence describing that change (citing = adopting; uncited lessons decay; skip ones that did not apply). No other mention of memory ids, saves or the memory store in replies to the user |
|
|
54
|
+
| A recalled memory drives an answer or a design choice | check its claim in the code or \`git log\` first: \`#NN\` and \`E#NN\` rows are notes from past sessions, many written automatically, so they can be wrong, and they describe the code as it was. If the code disagrees, trust the code and replace the note: \`mem_save(..., supersedes=[NN])\`, or \`supersedes=["E#NN"]\` for an event |
|
|
55
|
+
| After fixing a non-trivial bug | \`mem_save(type="bugfix", lesson_learned="<root cause + fix, only what this change's diff shows>", importance=2)\` |
|
|
55
56
|
| After a non-obvious architecture decision | \`mem_save(type="decision", lesson_learned="<constraint + tradeoff>")\` |
|
|
56
57
|
| Deferring to a future session | \`mem_defer({title, priority:1|2|3, detail})\`; when fixed, add \`closes_deferred=[N]\` to \`mem_save\` |
|
|
57
58
|
| Looking up past work / history | \`mem_search "keywords"\` · \`mem_recent\` · \`mem_timeline\` |
|
|
58
59
|
|
|
59
60
|
Path cost is round-trips, not milliseconds: the PreToolUse hook above already recalls (0 calls) — prefer it. For an explicit query, if these \`mem_*\` tools are deferred behind ToolSearch this session, the Bash CLI \`${CLI}\` is one call vs two (ToolSearch + call); the MCP server instructions carry the absolute path to use when it is not on PATH.
|
|
60
61
|
|
|
61
|
-
Full tool + CLI tables, citation/decay rules, and save discipline →
|
|
62
|
+
Full tool + CLI tables, citation/decay rules, and save discipline → \`${detailDocRef}\``;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
@@ -71,7 +72,7 @@ export function getDetailDoc() {
|
|
|
71
72
|
return `# claude-mem-lite 插件契约(完整)
|
|
72
73
|
|
|
73
74
|
> 由 \`${CLI} adopt\` 生成、随版本自动刷新;卸载用 \`${CLI} unadopt\`。
|
|
74
|
-
>
|
|
75
|
+
> 精炼触发表由 SessionStart 注入会话上下文(显式 \`${CLI} adopt\` 过的项目则写在 \`CLAUDE.md\` 的 \`claude-mem-lite\` 托管块里);本文件是其展开。
|
|
75
76
|
> 设计背景见 docs/CLAUDE-MD-STEERING-PLAN.md。
|
|
76
77
|
|
|
77
78
|
> **本文下方所有命令写作 \`${CLI} <cmd>\`。** 该名字只在全局装过
|
|
@@ -88,13 +89,25 @@ PreToolUse hook 在你 Read / Edit / Write 文件前已自动 \`mem_recall\` 该
|
|
|
88
89
|
lesson 也注入。
|
|
89
90
|
- Read→Edit 同文件共享 cooldown(不重复注入正文),但 Read 注入后的首个 Edit 会把 lesson **ID**
|
|
90
91
|
以一行 ack 指令重新浮出。看到 \`#NN [bugfix] …\` 这类行时:**某条 lesson 改变了你的做法,就在描述
|
|
91
|
-
|
|
92
|
+
那处改动的句子末尾加一个裸标签 \`(#NN)\`**;没用上的 lesson 不必提,也不要逐条列出。纯工具回合不算;
|
|
92
93
|
把 ID 记在工作记忆里,写回时引用。
|
|
94
|
+
- 给用户的回复里,除了这个 \`(#NN)\` 标签,不要再提记忆编号:不要报告保存、延期得到的编号
|
|
95
|
+
(如"已记进项目记忆,编号 #1"),也不要讨论记忆库本身(如"和记忆库里 #1 的记录一致")。
|
|
93
96
|
- 系统按会话追踪引用:被引用的 lesson 在召回排序里上浮,被注入却未引用的下沉(有界的排序乘数);
|
|
94
97
|
反复注入却从未被引用的,后台维护会把它的 importance 降到 2(无 lesson 的降到 1)。
|
|
95
98
|
写成 \`#NN n/a\` 的驳回不算采纳:排序上与未引用相同,同样下沉——所以不必写。
|
|
96
99
|
引用是给系统的反馈,不是合规仪式——注入池据此自调。
|
|
97
100
|
|
|
101
|
+
## 记忆是旧笔记,不是现在的代码
|
|
102
|
+
|
|
103
|
+
- \`E#NN\` 是后台根据会话自动写的事件摘要,可能写错:2026-09 的沙箱实测里 43 条中有 6 条事实错误、13 条部分错误。
|
|
104
|
+
\`#NN\` 可能是 agent 主动保存的笔记,\`#NN\` 也可能是后台自动整理的会话摘要;主动保存的也可能说得超出当时那次改动的实际范围。两者描述的都是保存那一刻的代码。
|
|
105
|
+
- 用一条记忆回答"之前做了什么、为什么",或据此做设计决定之前,先在代码或 \`git log -S\` / \`git show\` 里核对它的具体说法。
|
|
106
|
+
- 代码与记忆矛盾时,以代码为准,回复里按代码说;然后用
|
|
107
|
+
\`mem_save(type=<原类型>, title=..., lesson_learned="<按代码更正后的说法>", supersedes=[NN])\`
|
|
108
|
+
替换那条记忆(事件写 \`supersedes=["E#NN"]\`),被替换的记录不再被召回。只更正你在代码里亲眼核实过的那一点;拿不准就不写。
|
|
109
|
+
- 保存教训时只写这次 diff 能证明的内容:修了什么、为什么这样修;之后才做的或打算做的,不写进去。
|
|
110
|
+
|
|
98
111
|
## 何时主动调用 MCP 工具
|
|
99
112
|
|
|
100
113
|
\`tools/list\` 默认暴露 6 个核心工具 + 3 个 defer 工具:
|
|
@@ -172,7 +185,7 @@ PreToolUse hook 在你 Read / Edit / Write 文件前已自动 \`mem_recall\` 该
|
|
|
172
185
|
|
|
173
186
|
| 命令 | 签名(含硬约束) |
|
|
174
187
|
|------|------------------|
|
|
175
|
-
| 存观测 | \`${CLI} save "<text>" --type bugfix\\|decision --lesson "<≤500 字符>" [--importance 1-3] [--closes-deferred N]\` — \`<text>\` **必填定位参数**;\`--lesson\` 超 500 直接 fail |
|
|
188
|
+
| 存观测 | \`${CLI} save "<text>" --type bugfix\\|decision --lesson "<≤500 字符>" [--importance 1-3] [--closes-deferred N] [--supersedes 12,E#34]\` — \`<text>\` **必填定位参数**;\`--lesson\` 超 500 直接 fail;\`--supersedes\` 替换代码已推翻的旧记忆 |
|
|
176
189
|
| 推迟工作 | \`${CLI} defer add "<title ≤200>" [--priority 1\\|2\\|3] [--detail "<约束+为何推迟>"]\` — 标题 >200 挪到 \`--detail\` |
|
|
177
190
|
| 改某条 | \`${CLI} update <id> [--lesson "<≤500>"] [--title T] [--type T] [--importance 1-3] [--narrative T] [--concepts "a b c"]\` |
|
|
178
191
|
| 事件日志 | \`${CLI} activity save --type <bugfix\\|lesson\\|bug\\|discovery\\|refactor\\|feature\\|observation\\|decision> "<title>" [--body T] [--files f1,f2]\` |
|