claude-mem-lite 2.79.1 → 2.80.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "claude-mem-lite",
13
- "version": "2.79.1",
13
+ "version": "2.80.1",
14
14
  "source": "./",
15
15
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall"
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.79.1",
3
+ "version": "2.80.1",
4
4
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall",
5
5
  "author": {
6
6
  "name": "sdsrss"
package/README.md CHANGED
@@ -161,7 +161,7 @@ Source files stay in the cloned repo. Update via `git pull && node install.mjs i
161
161
  ### What happens during installation
162
162
 
163
163
  1. **Install dependencies** -- `npm install --omit=dev` (compiles native `better-sqlite3`)
164
- 2. **Register MCP server** -- `mem-lite` server with 16 tools (search, recent, recall, timeline, get, save, update, stats, delete, compress, maintain, export, fts_check, browse, registry, use). The pre-v2.78 generic server name `mem` is renamed to `mem-lite` for namespace hygiene; the tool names themselves (`mem_search`, `mem_recall`, ...) are unchanged.
164
+ 2. **Register MCP server** -- `mem-lite` server with 20 tools (9 core exposed via `tools/list` + 11 hidden-but-callable; see the Usage section for the full table). The pre-v2.78 generic server name `mem` is renamed to `mem-lite` for namespace hygiene; the tool names themselves (`mem_search`, `mem_recall`, ...) are unchanged.
165
165
  3. **Configure hooks** -- `PostToolUse`, `SessionStart`, `Stop`, `UserPromptSubmit` lifecycle hooks
166
166
  4. **Create data directory** -- `~/.claude-mem-lite/` (hidden) for database, runtime, and managed resource files
167
167
  5. **Auto-migrate** -- If `~/.claude-mem/` (original claude-mem) or `~/claude-mem-lite/` (pre-v0.5 unhidden) exists, migrates database and runtime files to `~/.claude-mem-lite/`, preserving the original untouched
package/README.zh-CN.md CHANGED
@@ -146,7 +146,7 @@ node install.mjs install
146
146
  ### 安装过程
147
147
 
148
148
  1. **安装依赖** -- `npm install --omit=dev`(编译原生 `better-sqlite3`)
149
- 2. **注册 MCP 服务器** -- `mem-lite` 服务器,包含 15 个工具(search、recent、recall、timeline、get、save、update、stats、delete、compress、maintain、export、fts_check、browse、registry)。v2.78 前服务器名为通用的 `mem`,现已改名为 `mem-lite` 避免与用户其它 `.mcp.json` 冲突;工具名(`mem_search`/`mem_recall` 等)保持不变。
149
+ 2. **注册 MCP 服务器** -- `mem-lite` 服务器,包含 20 个工具(9 个核心通过 `tools/list` 暴露 + 11 个隐藏但可调;完整表见 Usage 段)。v2.78 前服务器名为通用的 `mem`,现已改名为 `mem-lite` 避免与用户其它 `.mcp.json` 冲突;工具名(`mem_search`/`mem_recall` 等)保持不变。
150
150
 
151
151
  > **每个项目第一次使用,跑一次 `/adopt`。** Plugin 安装给你 MCP server + hooks + slash commands,但**邀请式 memory 哨兵**(一条提升 Claude 主动调用 `mem_recall` / `mem_save` 的 system-authority 指针)是按项目 opt-in 的。不跑 `/adopt` 时 hooks 仍记录观察、注入上下文,但 Claude 不太会主动调 MCP 工具。一次性、按项目:`/adopt`;撤销 `/unadopt`。
152
152
  3. **配置钩子** -- `PostToolUse`、`PreToolUse`、`SessionStart`、`Stop`、`UserPromptSubmit` 生命周期钩子
package/install.mjs CHANGED
@@ -1517,11 +1517,28 @@ function hasMemHooksConfigured(settings) {
1517
1517
  * node "/home/sds/.claude-mem-lite/hook.mjs" session-start
1518
1518
  * bash "/home/sds/.claude-mem-lite/scripts/post-tool-use.sh"
1519
1519
  * node "/home/sds/.claude-mem-lite/scripts/pre-tool-recall.js"
1520
- * We pick the first quoted absolute path; if there is no quoted token we fall
1521
- * back to the first whitespace-delimited absolute-looking token after the
1522
- * interpreter. ${CLAUDE_PLUGIN_ROOT}-templated commands are ignored those
1523
- * are plugin-owned hooks resolved by Claude Code at runtime, not by us.
1520
+ *
1521
+ * Scan order (v2.80+): walk EVERY quoted token via matchAll, prefer ones that
1522
+ * look like a hook path (absolute + ends in a known hook-runtime extension).
1523
+ * If no quoted token qualifies, fall back to the first path-shaped token from
1524
+ * a whitespace-split of the command. If both miss, skip the entry entirely —
1525
+ * deliberate bias toward **under-reporting over false-flagging**: a wrapper
1526
+ * like `bash -c "inline" "/real/path.sh"` should report the real path, not
1527
+ * the inline string. ${CLAUDE_PLUGIN_ROOT}-templated commands are ignored —
1528
+ * those are plugin-owned hooks resolved by Claude Code at runtime, not by us.
1529
+ *
1530
+ * Extension list (HOOK_PATH_EXTS) is hardcoded for the runtimes this plugin
1531
+ * actually registers (node/bash). Extend if Claude Code ever supports new
1532
+ * hook runtimes (e.g. python/.py). Currently safe because isMemHook() filters
1533
+ * to claude-mem-lite-owned hooks only — foreign runtimes can't reach here.
1524
1534
  */
1535
+ const HOOK_PATH_EXTS = ['.mjs', '.js', '.cjs', '.sh'];
1536
+
1537
+ function looksLikeHookPath(p) {
1538
+ if (!p || !p.startsWith('/')) return false;
1539
+ return HOOK_PATH_EXTS.some(ext => p.endsWith(ext));
1540
+ }
1541
+
1525
1542
  export function collectOrphanHookPaths(settings) {
1526
1543
  if (!settings?.hooks) return [];
1527
1544
  const out = [];
@@ -1532,12 +1549,19 @@ export function collectOrphanHookPaths(settings) {
1532
1549
  for (const h of cfg.hooks || []) {
1533
1550
  const cmd = h.command || '';
1534
1551
  if (cmd.includes('${CLAUDE_PLUGIN_ROOT}')) continue;
1535
- const quoted = cmd.match(/"([^"]+)"/);
1536
- let path = quoted ? quoted[1] : null;
1552
+ // v2.80: scan ALL quoted tokens (was: only the first), prefer ones
1553
+ // that look like a hook path. Fixes a footgun where a wrapper command
1554
+ // like `bash -c "some inline" "/real/path.sh"` would pick "some inline"
1555
+ // and flag a false orphan. If no quoted token looks like a path, fall
1556
+ // through to the unquoted scanner; if that also misses, skip the
1557
+ // entry — we'd rather under-report than false-flag.
1558
+ let path = null;
1559
+ for (const m of cmd.matchAll(/"([^"]+)"/g)) {
1560
+ if (looksLikeHookPath(m[1])) { path = m[1]; break; }
1561
+ }
1537
1562
  if (!path) {
1538
- // unquoted: split on whitespace, take the first arg that looks like an absolute path
1539
1563
  const parts = cmd.split(/\s+/);
1540
- path = parts.find(p => p.startsWith('/') && (p.endsWith('.mjs') || p.endsWith('.js') || p.endsWith('.sh'))) || null;
1564
+ path = parts.find(p => looksLikeHookPath(p)) || null;
1541
1565
  }
1542
1566
  if (!path) continue;
1543
1567
  if (!existsSync(path) && !out.includes(path)) out.push(path);
@@ -283,8 +283,17 @@ export function hasMainThreadAssistantText(transcriptPath) {
283
283
  if (!transcriptPath || !existsSync(transcriptPath)) return false;
284
284
  let raw;
285
285
  try { raw = readFileSync(transcriptPath, 'utf8'); } catch { return false; }
286
- for (const line of raw.split('\n')) {
287
- if (!line.trim()) continue;
286
+ // Reverse-iterate so a turn that just produced text returns true on the
287
+ // FIRST line examined instead of walking the entire transcript. Common case
288
+ // (model wrote a paragraph → Stop fires) short-circuits in O(1) line parses;
289
+ // pathological case (no text anywhere) still walks all entries but that's
290
+ // the degenerate state we want false for anyway. v2.80 perf polish — the
291
+ // pre-v2.80 forward scan held the full transcript in memory and walked
292
+ // every line on the common case too.
293
+ const lines = raw.split('\n');
294
+ for (let i = lines.length - 1; i >= 0; i--) {
295
+ const line = lines[i];
296
+ if (!line || !line.trim()) continue;
288
297
  let entry;
289
298
  try { entry = JSON.parse(line); } catch { continue; }
290
299
  if (entry.type !== 'assistant' || !entry.message) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.79.1",
3
+ "version": "2.80.1",
4
4
  "description": "Lightweight persistent memory system for Claude Code",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
package/scripts/setup.sh CHANGED
@@ -85,7 +85,7 @@ mark_deps_broken() {
85
85
  # Embed reason + repair command so hook.mjs renders a complete error without
86
86
  # having to re-derive them. Delegate JSON serialization to node so embedded
87
87
  # quotes / shell metachars in $ROOT or $reason can't produce an invalid file
88
- # (bash `printf '"..%s.."'` cannot escape arbitrary strings safely; v2.79 fix).
88
+ # (bash `printf '"..%s.."'` cannot escape arbitrary strings safely; v2.79.1 fix).
89
89
  MARK_REASON="$reason" MARK_ROOT="$ROOT" MARK_FLAG="$DEPS_FLAG" node -e '
90
90
  const fs = require("fs");
91
91
  const reason = process.env.MARK_REASON || "unknown";