claude-mem-lite 6.9.0 → 6.10.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 +3 -1
- package/README.zh-CN.md +1 -0
- package/cli.mjs +40 -0
- package/hook-context.mjs +3 -1
- package/hook-handoff.mjs +339 -42
- package/hook-shared.mjs +1 -0
- package/hook.mjs +12 -8
- package/install.mjs +71 -6
- package/lib/git-state.mjs +4 -1
- package/lib/handoff-constants.mjs +13 -0
- package/lib/paused-reader.mjs +167 -0
- package/lib/scrub-record.mjs +7 -0
- package/lib/startup-dashboard.mjs +2 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
- package/schema.mjs +36 -0
- package/search-scoring.mjs +69 -0
- package/source-files.mjs +1 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"plugins": [
|
|
10
10
|
{
|
|
11
11
|
"name": "claude-mem-lite",
|
|
12
|
-
"version": "6.
|
|
12
|
+
"version": "6.10.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. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark)."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.10.0",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sdsrss"
|
package/README.md
CHANGED
|
@@ -119,7 +119,9 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
|
|
|
119
119
|
- **Schema auto-migration** -- Idempotent `ALTER TABLE` migrations run on every startup, safely adding new columns and indexes without data loss
|
|
120
120
|
- **LLM concurrency control** -- File-based semaphore limits background workers to 2 concurrent LLM calls, preventing resource contention
|
|
121
121
|
- **stdin overflow protection** -- Hook input truncated at 256KB with regex-based action salvage for oversized tool outputs
|
|
122
|
-
- **Cross-session handoff** -- Captures session state
|
|
122
|
+
- **Cross-session handoff** -- Captures session state on `/exit` and `/clear`, then injects context when the next session detects continuation intent
|
|
123
|
+
<br>**Changed in v6.10.0**: the injected block went from two sections to six. Its observation queries were keyed on the hook-minted session id while every `mem_save` writes a `manual-<project>` id, so `completed` and `key_decisions` could not reach a saved lesson at all — 17 of 17 stored rows held 0 bytes in both. The block now also carries `## Tree state` (branch, short sha, uncommitted count) and `## Next steps`, read from the project's newest `tasks/<slug>-paused.md` when it is under a week old. Four additive nullable columns land on `session_handoffs`; the schema version deliberately does not move, so an older build still opens the database (measured: the v6.9.1 tree read and wrote a database this release had migrated). Revert by pinning `claude-mem-lite@6.9.1` — no data-directory work.
|
|
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`
|
|
123
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
|
|
124
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`
|
|
125
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.)
|
package/README.zh-CN.md
CHANGED
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
- **LLM 并发控制** -- 基于文件的信号量将后台 worker 限制为 2 个并发 LLM 调用,防止资源争用
|
|
92
92
|
- **stdin 溢出保护** -- Hook 输入在 256KB 处截断,对超大工具输出使用正则挽救关键信息
|
|
93
93
|
- **跨会话交接** -- 在 `/clear` 或 `/exit` 时捕获会话状态(请求、已完成工作、后续步骤、关键文件),下次会话检测到继续意图时自动注入上下文(支持显式关键词和 FTS5 术语重叠匹配)
|
|
94
|
+
<br>**v6.10.0 变更**:注入块从 2 段变为 6 段。此前它的观测查询按钩子铸造的会话 id 过滤,而每一次 `mem_save` 写的是 `manual-<project>` id,两个命名空间不相交,所以 `completed` 与 `key_decisions` 根本够不到已保存的经验——库里 17 行交接记录这两个字段全是 0 字节。现在还会带 `## Tree state`(分支、短 sha、未提交文件数)和 `## Next steps`(读取项目里最新且未超过 7 天的 `tasks/<slug>-paused.md`)。`session_handoffs` 新增 4 个可空列;schema 版本号**刻意不动**,所以旧版本仍能打开数据库(已实测:v6.9.1 的代码能读写本版本迁移过的库)。回退方式:固定到 `claude-mem-lite@6.9.1`,无需处理数据目录。
|
|
94
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 起)。
|
|
95
96
|
- **Git-SHA 延续锚点**(v2.31.0)-- handoff 记录包含 `git_sha_at_handoff` 字段,任何匹配当前 `HEAD` 的 handoff 都视为延续会话,不受 TTL 限制。代码状态比时钟时间更能反映上下文延续。
|
|
96
97
|
- **启动面板**(v2.31.0)-- SessionStart hook 将 `git status` + `~/.claude/tasks/*.json` + `~/.claude/plans/*.md` + 最近 /exit 交接 + 最近事件数聚合为一个结构化块,通过 `hookSpecificOutput.additionalContext` 注入。
|
package/cli.mjs
CHANGED
|
@@ -136,6 +136,46 @@ const INSTALL_COMMANDS = new Set([
|
|
|
136
136
|
'release',
|
|
137
137
|
]);
|
|
138
138
|
|
|
139
|
+
// A reader that leaves is not an error. `claude-mem-lite search x | head -1`,
|
|
140
|
+
// `| grep -q`, or quitting `less` closes the read end while we are still writing;
|
|
141
|
+
// Node then emits 'error' on the stdout Socket, and with no listener that is an
|
|
142
|
+
// UNHANDLED error event — a ~20-line stack ending in `outVerbatim` where the user
|
|
143
|
+
// expected the shell prompt.
|
|
144
|
+
//
|
|
145
|
+
// WHICH COMMANDS, measured rather than generalised (20 trials each, `| head -1`,
|
|
146
|
+
// pre-fix): `search`, `export`, `recent`, `stats`, `doctor`, `timeline`,
|
|
147
|
+
// `citation-stats` 20/20; `browse` 19/20; `help`, `status`, `context`, `get`,
|
|
148
|
+
// `memdir-audit` 0/20. So NOT "every stdout-bearing command" — what decides it is
|
|
149
|
+
// whether a write is still pending when the reader goes, which depends on how many
|
|
150
|
+
// lines the consumer takes and how the output is batched — NOT on the 64 KB pipe
|
|
151
|
+
// buffer, which an earlier draft of this comment blamed: pre-ship review found
|
|
152
|
+
// `stats` crashing at `head -20` on an output far under it. That output's size is
|
|
153
|
+
// corpus dependent, so no byte count is quoted here. This is also why the crash
|
|
154
|
+
// survived so long — it is invisible to exactly the pipe depths a smoke test picks.
|
|
155
|
+
//
|
|
156
|
+
// Lives HERE, at the published `bin`, and not at `cli/common.mjs`'s `out()`: the
|
|
157
|
+
// crash reproduces on `doctor` too, whose writes are `console.log` inside
|
|
158
|
+
// install.mjs, so a chokepoint fix would cover the CLI half and leave the installer
|
|
159
|
+
// half loud. One process-level listener covers both routes below.
|
|
160
|
+
//
|
|
161
|
+
// SWALLOW, DO NOT EXIT. The first cut called `process.exit(0)` here, on the
|
|
162
|
+
// reasoning that a CLI whose consumer has gone should stop rather than serialise a
|
|
163
|
+
// whole-DB `export` into a dead pipe. Pre-ship review measured what that costs:
|
|
164
|
+
// `doctor | head -1` under `pipefail` exited 0 on 10/10 runs while the same doctor
|
|
165
|
+
// exits 1 unpiped, because `runDoctor` assigns `process.exitCode = 1` AFTER its last
|
|
166
|
+
// print (install.mjs, "Diagnostic-tool exit-code contract") and the forced exit lands
|
|
167
|
+
// first. That silently turns a failing `claude-mem-lite doctor || alert` — the
|
|
168
|
+
// wrapper that contract names — into a passing one. `process.exitCode ?? 0` does not
|
|
169
|
+
// rescue it: the verdict does not exist yet at kill time. Returning instead reads
|
|
170
|
+
// exit 1 on 10/10 and keeps the crash fixed (doctor 0/10, search 0/10 EPIPE stacks).
|
|
171
|
+
// Correctness over the saved work: the process finishes into a pipe nobody reads,
|
|
172
|
+
// which is wasted effort but never a wrong answer. Non-EPIPE is rethrown — this is a
|
|
173
|
+
// classifier, not a blanket swallow, the same charter `explainBrokenInstall` follows.
|
|
174
|
+
process.stdout.on('error', (err) => {
|
|
175
|
+
if (err && err.code === 'EPIPE') return;
|
|
176
|
+
throw err;
|
|
177
|
+
});
|
|
178
|
+
|
|
139
179
|
const cmd = process.argv[2];
|
|
140
180
|
|
|
141
181
|
// `version` and `-V` are aliases, not extra syntax: the bare subcommand is what a user
|
package/hook-context.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
effectiveQuiet,
|
|
28
28
|
isQuietHooks,
|
|
29
29
|
KEY_CONTEXT_LIMIT,
|
|
30
|
+
UNCONSUMED_HANDOFF_SQL,
|
|
30
31
|
} from './hook-shared.mjs';
|
|
31
32
|
import { extractUnfinishedSummary } from './hook-handoff.mjs';
|
|
32
33
|
import { recentInjectableEvents, renderInjectableEvent } from './lib/events-injection.mjs';
|
|
@@ -752,6 +753,7 @@ export function buildSessionContextLines(
|
|
|
752
753
|
SELECT working_on, unfinished, key_files
|
|
753
754
|
FROM session_handoffs
|
|
754
755
|
WHERE project = ? AND type = 'clear' AND session_id = ? AND created_at_epoch > ?
|
|
756
|
+
AND ${UNCONSUMED_HANDOFF_SQL}
|
|
755
757
|
ORDER BY created_at_epoch DESC LIMIT 1
|
|
756
758
|
`,
|
|
757
759
|
)
|
|
@@ -761,7 +763,7 @@ export function buildSessionContextLines(
|
|
|
761
763
|
`
|
|
762
764
|
SELECT working_on, unfinished, key_files
|
|
763
765
|
FROM session_handoffs
|
|
764
|
-
WHERE project = ? AND type = 'clear' AND created_at_epoch > ?
|
|
766
|
+
WHERE project = ? AND type = 'clear' AND created_at_epoch > ? AND ${UNCONSUMED_HANDOFF_SQL}
|
|
765
767
|
ORDER BY created_at_epoch DESC LIMIT 1
|
|
766
768
|
`,
|
|
767
769
|
)
|