claude-mem-lite 3.0.1 → 3.1.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 +2 -0
- package/adopt-content.mjs +18 -18
- package/commands/adopt.md +1 -1
- package/commands/bug.md +1 -1
- package/commands/lesson.md +1 -1
- package/commands/mem.md +8 -8
- package/commands/unadopt.md +1 -1
- package/hook.mjs +8 -3
- package/lib/native-binding-hint.mjs +71 -0
- package/package.json +2 -1
- package/scripts/hook-launcher.mjs +44 -10
- package/server-internals.mjs +2 -2
- package/source-files.mjs +4 -0
- package/tool-schemas.mjs +19 -19
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "claude-mem-lite",
|
|
13
|
-
"version": "3.0
|
|
13
|
+
"version": "3.1.0",
|
|
14
14
|
"source": "./",
|
|
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)."
|
|
16
16
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.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
|
@@ -160,6 +160,8 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
|
|
|
160
160
|
|
|
161
161
|
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.
|
|
162
162
|
|
|
163
|
+
> **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 absolute path (`node ~/.claude-mem-lite/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.
|
|
164
|
+
|
|
163
165
|
> **Auto-adopt fires on the first SessionStart per project (v2.82.1+).** The plugin automatically writes the **invited-memory sentinel** (a system-authority pointer that boosts Claude's proactive use of `mem_recall` / `mem_save`) into the project's memdir — **no manual `/adopt` needed**, regardless of install path (npm, npx, `/plugin`, manual). Per-project opt-out: `claude-mem-lite adopt --disable` (`--enable` to re-arm). Global opt-out: `export MEM_NO_AUTO_ADOPT=1`. Manual `/adopt` remains available for re-applying after edits and for the `--all` batch path.
|
|
164
166
|
|
|
165
167
|
### Method 2: npx (one-liner)
|
package/adopt-content.mjs
CHANGED
|
@@ -26,7 +26,7 @@ export function getIndexLine() {
|
|
|
26
26
|
export function getDetailDoc() {
|
|
27
27
|
return `# claude-mem-lite 插件契约
|
|
28
28
|
|
|
29
|
-
> 由 \`claude-mem-lite adopt\` 生成;卸载用 \`claude-mem-lite unadopt\`。
|
|
29
|
+
> 由 \`node ~/.claude-mem-lite/cli.mjs adopt\` 生成;卸载用 \`node ~/.claude-mem-lite/cli.mjs unadopt\`。
|
|
30
30
|
> 设计背景见 docs/plans/2026-04-16-invited-memory-pattern.md。
|
|
31
31
|
|
|
32
32
|
## 何时调用 MCP 工具
|
|
@@ -58,28 +58,28 @@ MCP 层,按名 \`tools/call\` 仍可命中,但对 Claude Code 这类只读 t
|
|
|
58
58
|
|
|
59
59
|
| 场景 | CLI |
|
|
60
60
|
|------|-----|
|
|
61
|
-
| 清理过期记忆 | \`claude-mem-lite maintain --
|
|
62
|
-
| 深度优化(Haiku) | \`claude-mem-lite optimize
|
|
63
|
-
| 压缩旧条目 | \`claude-mem-lite compress
|
|
64
|
-
| FTS5 索引检查 / 重建 | \`claude-mem-lite fts-check [--rebuild]\` |
|
|
65
|
-
| tier 分组浏览 | \`claude-mem-lite browse [--tier active]\` |
|
|
66
|
-
| 导出 JSON/JSONL | \`claude-mem-lite export [--format jsonl]\` |
|
|
67
|
-
| 统计总量 / 健康 | \`claude-mem-lite stats [--days 30]\` |
|
|
68
|
-
| 删除某条 | \`claude-mem-lite delete <id>[,<id>]\` |
|
|
69
|
-
| 更新某条 | \`claude-mem-lite update <id> [--title ...]\` |
|
|
70
|
-
| 列 / 搜索 / 导入 skill-agent registry | \`claude-mem-lite registry <list\\|search\\|import>\` |
|
|
61
|
+
| 清理过期记忆 | \`node ~/.claude-mem-lite/cli.mjs maintain scan --ops purge_stale\` → \`maintain execute --ops purge_stale\` |
|
|
62
|
+
| 深度优化(Haiku) | \`node ~/.claude-mem-lite/cli.mjs optimize\`(默认 preview;\`--run\` 执行,\`--task re-enrich,normalize,cluster-merge,smart-compress\` 选阶段) |
|
|
63
|
+
| 压缩旧条目 | \`node ~/.claude-mem-lite/cli.mjs compress\`(默认 preview;\`--execute\` 执行,\`--age-days N\` 改阈值) |
|
|
64
|
+
| FTS5 索引检查 / 重建 | \`node ~/.claude-mem-lite/cli.mjs fts-check [--rebuild]\` |
|
|
65
|
+
| tier 分组浏览 | \`node ~/.claude-mem-lite/cli.mjs browse [--tier active]\` |
|
|
66
|
+
| 导出 JSON/JSONL | \`node ~/.claude-mem-lite/cli.mjs export [--format jsonl]\` |
|
|
67
|
+
| 统计总量 / 健康 | \`node ~/.claude-mem-lite/cli.mjs stats [--days 30]\` |
|
|
68
|
+
| 删除某条 | \`node ~/.claude-mem-lite/cli.mjs delete <id>[,<id>]\` |
|
|
69
|
+
| 更新某条 | \`node ~/.claude-mem-lite/cli.mjs update <id> [--title ...]\` |
|
|
70
|
+
| 列 / 搜索 / 导入 skill-agent registry | \`node ~/.claude-mem-lite/cli.mjs registry <list\\|search\\|import>\` |
|
|
71
71
|
| 按 registry 名载入 skill/agent | (MCP only:\`mem_use\`;由用户主动请求时才使用) |
|
|
72
72
|
|
|
73
73
|
## CLI 速查(常用检索)
|
|
74
74
|
|
|
75
75
|
| 命令 | 用途 |
|
|
76
76
|
|------|------|
|
|
77
|
-
| \`claude-mem-lite search "query"\` | FTS5 全文搜索 |
|
|
78
|
-
| \`claude-mem-lite search "err" --type bugfix\` | 按类型过滤 |
|
|
79
|
-
| \`claude-mem-lite recall "file.mjs"\` | 文件相关记忆 |
|
|
80
|
-
| \`claude-mem-lite recent 5\` | 最近 5 条 |
|
|
81
|
-
| \`claude-mem-lite get 42,43\` | 按 ID 展开 |
|
|
82
|
-
| \`claude-mem-lite timeline --anchor 42\` | 时间线上下文 |
|
|
77
|
+
| \`node ~/.claude-mem-lite/cli.mjs search "query"\` | FTS5 全文搜索 |
|
|
78
|
+
| \`node ~/.claude-mem-lite/cli.mjs search "err" --type bugfix\` | 按类型过滤 |
|
|
79
|
+
| \`node ~/.claude-mem-lite/cli.mjs recall "file.mjs"\` | 文件相关记忆 |
|
|
80
|
+
| \`node ~/.claude-mem-lite/cli.mjs recent 5\` | 最近 5 条 |
|
|
81
|
+
| \`node ~/.claude-mem-lite/cli.mjs get 42,43\` | 按 ID 展开 |
|
|
82
|
+
| \`node ~/.claude-mem-lite/cli.mjs timeline --anchor 42\` | 时间线上下文 |
|
|
83
83
|
|
|
84
84
|
## 质量门槛
|
|
85
85
|
|
|
@@ -89,6 +89,6 @@ MCP 层,按名 \`tools/call\` 仍可命中,但对 Claude Code 这类只读 t
|
|
|
89
89
|
|
|
90
90
|
## 卸载
|
|
91
91
|
|
|
92
|
-
\`claude-mem-lite unadopt\` 精确移除 sentinel 段 + 本文件;其它 MEMORY.md 内容不动。
|
|
92
|
+
\`node ~/.claude-mem-lite/cli.mjs unadopt\` 精确移除 sentinel 段 + 本文件;其它 MEMORY.md 内容不动。
|
|
93
93
|
`;
|
|
94
94
|
}
|
package/commands/adopt.md
CHANGED
package/commands/bug.md
CHANGED
|
@@ -42,7 +42,7 @@ Build the body as `<description>\n\nRepro:\n<repro-steps>` (or just
|
|
|
42
42
|
|
|
43
43
|
Run via Bash:
|
|
44
44
|
|
|
45
|
-
claude-mem-lite activity save \
|
|
45
|
+
node ~/.claude-mem-lite/cli.mjs activity save \
|
|
46
46
|
--type bug \
|
|
47
47
|
--title "<first 60 chars of description>" \
|
|
48
48
|
--body "<body>" \
|
package/commands/lesson.md
CHANGED
|
@@ -37,7 +37,7 @@ Run via Bash — the CLI takes the lesson text as positional args and stores
|
|
|
37
37
|
the full text as the title (the `activity save` command uses title-as-body
|
|
38
38
|
when `--body` is absent):
|
|
39
39
|
|
|
40
|
-
claude-mem-lite activity save \
|
|
40
|
+
node ~/.claude-mem-lite/cli.mjs activity save \
|
|
41
41
|
--type lesson \
|
|
42
42
|
--title "<first 60 chars of text>" \
|
|
43
43
|
--body "<full text>" \
|
package/commands/mem.md
CHANGED
|
@@ -23,16 +23,16 @@ Search and browse your project memory efficiently.
|
|
|
23
23
|
|
|
24
24
|
When the user invokes `/mem`, parse their intent:
|
|
25
25
|
|
|
26
|
-
- `/mem search <query>` → run `claude-mem-lite search <query>` via Bash
|
|
27
|
-
- `/mem recent` or `/mem recent 20` → run `claude-mem-lite recent [N]` via Bash
|
|
28
|
-
- `/mem recall <file>` → run `claude-mem-lite recall <file>` via Bash
|
|
29
|
-
- `/mem timeline <id>` → run `claude-mem-lite timeline --anchor <id>` via Bash
|
|
26
|
+
- `/mem search <query>` → run `node ~/.claude-mem-lite/cli.mjs search <query>` via Bash
|
|
27
|
+
- `/mem recent` or `/mem recent 20` → run `node ~/.claude-mem-lite/cli.mjs recent [N]` via Bash
|
|
28
|
+
- `/mem recall <file>` → run `node ~/.claude-mem-lite/cli.mjs recall <file>` via Bash
|
|
29
|
+
- `/mem timeline <id>` → run `node ~/.claude-mem-lite/cli.mjs timeline --anchor <id>` via Bash
|
|
30
30
|
- `/mem save <text>` → call `mem_save` MCP tool with the text as content
|
|
31
|
-
- `/mem stats` → run `claude-mem-lite stats` via Bash
|
|
32
|
-
- `/mem get <ids>` → run `claude-mem-lite get <ids>` via Bash
|
|
31
|
+
- `/mem stats` → run `node ~/.claude-mem-lite/cli.mjs stats` via Bash
|
|
32
|
+
- `/mem get <ids>` → run `node ~/.claude-mem-lite/cli.mjs get <ids>` via Bash
|
|
33
33
|
- `/mem cleanup` → run `mem_maintain(action="scan")`, report pending purge count and stale items to user, ask for confirmation, then run `mem_maintain(action="execute", operations=["purge_stale"])` if confirmed
|
|
34
34
|
- `/mem cleanup Nd` (e.g. `60d`) → same as above but use `retain_days=N` to only purge items older than N days
|
|
35
35
|
- `/mem cleanup keep Nd` (e.g. `keep 14d`) → same as above with `retain_days=N`
|
|
36
|
-
- `/mem <query>` (no subcommand) → treat as search, run `claude-mem-lite search <query>` via Bash
|
|
36
|
+
- `/mem <query>` (no subcommand) → treat as search, run `node ~/.claude-mem-lite/cli.mjs search <query>` via Bash
|
|
37
37
|
|
|
38
|
-
Use Bash commands first. For detailed data, use `claude-mem-lite get <id>` via Bash.
|
|
38
|
+
Use Bash commands first. For detailed data, use `node ~/.claude-mem-lite/cli.mjs get <id>` via Bash.
|
package/commands/unadopt.md
CHANGED
|
@@ -27,4 +27,4 @@ Once unadopted, the conservative hook layer (SessionStart `File Lessons` /
|
|
|
27
27
|
`Key Context`, MCP instructions `WHEN TO USE`) goes back to verbose mode —
|
|
28
28
|
Claude Code will see the full injection again on the next session start.
|
|
29
29
|
|
|
30
|
-
!claude-mem-lite unadopt $ARGUMENTS
|
|
30
|
+
!node ~/.claude-mem-lite/cli.mjs unadopt $ARGUMENTS
|
package/hook.mjs
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
} from './hook-shared.mjs';
|
|
46
46
|
import { handleLLMEpisode, handleLLMSummary, saveObservation, buildImmediateObservation } from './hook-llm.mjs';
|
|
47
47
|
import { scrubRecord } from './lib/scrub-record.mjs';
|
|
48
|
+
import { formatHookError } from './lib/native-binding-hint.mjs';
|
|
48
49
|
import { selectCompressionCandidates, groupByProjectWeek, compressGroup } from './lib/compress-core.mjs';
|
|
49
50
|
import { cleanupBroken, decayAndMarkIdle, boostAccessed } from './lib/maintain-core.mjs';
|
|
50
51
|
import {
|
|
@@ -1439,9 +1440,13 @@ try {
|
|
|
1439
1440
|
case 'update-check': await checkForUpdate(); break;
|
|
1440
1441
|
}
|
|
1441
1442
|
} catch (err) {
|
|
1442
|
-
//
|
|
1443
|
-
|
|
1444
|
-
|
|
1443
|
+
// Log fatal errors (ungated) with structured format. ERR_DLOPEN_FAILED (an
|
|
1444
|
+
// unloadable native DB binding, e.g. ABI-stale after a Node upgrade) is
|
|
1445
|
+
// collapsed to one short, rate-limited rebuild hint instead of the raw
|
|
1446
|
+
// multi-line NODE_MODULE_VERSION message on every fire — see
|
|
1447
|
+
// lib/native-binding-hint.mjs.
|
|
1448
|
+
const line = formatHookError(err, event, { runtimeDir: RUNTIME_DIR });
|
|
1449
|
+
if (line) console.error(line);
|
|
1445
1450
|
}
|
|
1446
1451
|
|
|
1447
1452
|
process.exit(0);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// lib/native-binding-hint.mjs — friendly, rate-limited hint for an unloadable
|
|
2
|
+
// native DB binding (better-sqlite3 ERR_DLOPEN_FAILED, e.g. a Node version
|
|
3
|
+
// upgrade leaves the prebuilt .node ABI-stale).
|
|
4
|
+
//
|
|
5
|
+
// This is the SIBLING of the missing-dependency case handled in
|
|
6
|
+
// scripts/hook-launcher.mjs. The two fail on different paths:
|
|
7
|
+
// • MISSING dependency (ERR_MODULE_NOT_FOUND) throws at IMPORT time, before
|
|
8
|
+
// hook.mjs runs — caught by the launcher.
|
|
9
|
+
// • UNLOADABLE binding (ERR_DLOPEN_FAILED) imports fine (better-sqlite3 loads
|
|
10
|
+
// its .node lazily at the first `new Database()`), then throws inside a hook
|
|
11
|
+
// handler — caught by hook.mjs's top-level dispatch try/catch. Pre-this,
|
|
12
|
+
// that catch logged the raw multi-line NODE_MODULE_VERSION message on EVERY
|
|
13
|
+
// hook fire. Here we collapse it to one short, actionable line, rate-limited
|
|
14
|
+
// per cooldown. The actual rebuild is the MCP server launch path's job
|
|
15
|
+
// (lib/binding-probe.mjs::ensureBetterSqlite3Working) — a hook must never
|
|
16
|
+
// run `npm rebuild` itself (2–5s timeout + concurrent-fire races).
|
|
17
|
+
//
|
|
18
|
+
// Pure node: imports + injectable now/runtimeDir so it unit-tests without the
|
|
19
|
+
// hook dependency graph (no schema.mjs / better-sqlite3 import).
|
|
20
|
+
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
export const NATIVE_BINDING_HINT_COOLDOWN_MS = 6 * 60 * 60 * 1000; // 6h
|
|
25
|
+
const MARKER_NAME = 'native-binding-hint-last';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* True at most once per cooldown window. Persists the last-hint timestamp as
|
|
29
|
+
* file CONTENT (not mtime) under runtimeDir so callers can inject `now`
|
|
30
|
+
* deterministically in tests. Best-effort: any fs error returns true — showing
|
|
31
|
+
* the hint beats silently swallowing a real problem.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} runtimeDir Directory for the marker file
|
|
34
|
+
* @param {number} [now] Current epoch ms (injectable)
|
|
35
|
+
* @param {number} [cooldownMs] Suppression window
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
export function nativeBindingHintDue(runtimeDir, now = Date.now(), cooldownMs = NATIVE_BINDING_HINT_COOLDOWN_MS) {
|
|
39
|
+
const marker = join(runtimeDir, MARKER_NAME);
|
|
40
|
+
try {
|
|
41
|
+
const last = Number(readFileSync(marker, 'utf8'));
|
|
42
|
+
if (Number.isFinite(last) && now - last < cooldownMs) return false;
|
|
43
|
+
} catch { /* no/invalid marker → due */ }
|
|
44
|
+
try {
|
|
45
|
+
if (!existsSync(runtimeDir)) mkdirSync(runtimeDir, { recursive: true });
|
|
46
|
+
writeFileSync(marker, String(now));
|
|
47
|
+
} catch { /* best-effort */ }
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Single stderr line hook.mjs should log for a caught dispatch error, or null
|
|
53
|
+
* to stay silent (ERR_DLOPEN_FAILED still within cooldown). ERR_DLOPEN_FAILED →
|
|
54
|
+
* short rate-limited rebuild hint; everything else → the existing ungated
|
|
55
|
+
* structured ERROR line. Pass runtimeDir to enable rate-limiting (omit it to
|
|
56
|
+
* always format, e.g. in tests).
|
|
57
|
+
*
|
|
58
|
+
* @param {Error & {code?: string}} err
|
|
59
|
+
* @param {string} event Hook event name (stop / session-start / …)
|
|
60
|
+
* @param {{now?: number, runtimeDir?: string}} [opts]
|
|
61
|
+
* @returns {string | null}
|
|
62
|
+
*/
|
|
63
|
+
export function formatHookError(err, event, { now = Date.now(), runtimeDir } = {}) {
|
|
64
|
+
const ts = new Date(now).toISOString();
|
|
65
|
+
if (err && err.code === 'ERR_DLOPEN_FAILED') {
|
|
66
|
+
if (runtimeDir && !nativeBindingHintDue(runtimeDir, now)) return null;
|
|
67
|
+
return `[claude-mem-lite] [${ts}] [WARN] ${event}: native DB binding can't load ` +
|
|
68
|
+
`(likely a Node version change) — auto-heals on next MCP server start, or run: claude-mem-lite repair`;
|
|
69
|
+
}
|
|
70
|
+
return `[claude-mem-lite] [${ts}] [ERROR] ${event}: ${err && err.message}`;
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.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
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@10.9.2",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"lib/deferred-work.mjs",
|
|
80
80
|
"lib/upgrade-banner.mjs",
|
|
81
81
|
"lib/scrub-record.mjs",
|
|
82
|
+
"lib/native-binding-hint.mjs",
|
|
82
83
|
"lib/import-jsonl.mjs",
|
|
83
84
|
"cli/common.mjs",
|
|
84
85
|
"cli/fts-check.mjs",
|
|
@@ -9,10 +9,16 @@
|
|
|
9
9
|
// launcher is defense-in-depth for similar future drift (corrupt download,
|
|
10
10
|
// half-applied install, manual file deletion).
|
|
11
11
|
//
|
|
12
|
-
// Behavior: try-import the target entry. On ERR_MODULE_NOT_FOUND
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// Behavior: try-import the target entry. On ERR_MODULE_NOT_FOUND originating
|
|
13
|
+
// from our install — either a missing relative module (e.url under the install
|
|
14
|
+
// dir) or a missing bare dependency like better-sqlite3 (e.url is undefined and
|
|
15
|
+
// the importer named in the message is under the install dir) — run
|
|
16
|
+
// `install.mjs repair` (rate-limited via a 6h marker file under runtime/) and
|
|
17
|
+
// retry the import once. If repair is unavailable or fails, degrade quietly:
|
|
18
|
+
// these are best-effort memory hooks, so a broken/missing dependency emits one
|
|
19
|
+
// clean recovery line and exits 0 rather than dumping a Node stack trace on
|
|
20
|
+
// every fire. On any other (foreign) exception, re-throw so Node's default
|
|
21
|
+
// error surface is preserved.
|
|
16
22
|
//
|
|
17
23
|
// HARD constraint: pure node: imports only. Importing anything from lib/ here
|
|
18
24
|
// would defeat the entire purpose — the launcher must survive a broken
|
|
@@ -59,10 +65,33 @@ async function runEntry({ bustCache = false } = {}) {
|
|
|
59
65
|
await import(url);
|
|
60
66
|
}
|
|
61
67
|
|
|
68
|
+
// Two ERR_MODULE_NOT_FOUND shapes reach here (both verified against Node 22):
|
|
69
|
+
// • missing relative module → e.url = file://<missing-path> (under install)
|
|
70
|
+
// • missing bare dependency (e.g. a half-installed better-sqlite3) → e.url is
|
|
71
|
+
// UNDEFINED and the message is `Cannot find package '<name>' imported from
|
|
72
|
+
// <importer>`. This is the shape that bricked the hooks: the old
|
|
73
|
+
// file://INSTALL_DIR prefix test never matched it in a dev-dir install, so
|
|
74
|
+
// a missing dependency was misread as a foreign error and re-thrown as a
|
|
75
|
+
// Node stack trace on every hook fire.
|
|
76
|
+
// Anchor on any path the error exposes — the missing URL and/or the importer
|
|
77
|
+
// (present in both messages as "imported from <path>"). If it sits inside our
|
|
78
|
+
// install, a self-heal could fix it.
|
|
62
79
|
function isLocalModuleErr(e) {
|
|
63
80
|
if (!e || e.code !== 'ERR_MODULE_NOT_FOUND') return false;
|
|
64
|
-
const
|
|
65
|
-
|
|
81
|
+
const importer = /imported from (.+?)\s*$/.exec(String(e.message || ''))?.[1];
|
|
82
|
+
const paths = [e.url, importer]
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.map((p) => String(p).replace(/^file:\/\//, ''));
|
|
85
|
+
return paths.some((p) => p.startsWith(INSTALL_DIR) || p.includes('.claude-mem-lite'));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Human-readable label for the "Detected broken install (<reason>)" line:
|
|
89
|
+
// prefer the missing dependency/module name over a raw path fragment.
|
|
90
|
+
function describeFailure(e) {
|
|
91
|
+
const pkg = /Cannot find package '([^']+)'/.exec(String(e.message || ''))?.[1];
|
|
92
|
+
if (pkg) return pkg;
|
|
93
|
+
if (e.url) return String(e.url).split('/').pop();
|
|
94
|
+
return String(e.message || 'unknown').split('/').slice(-2).join('/');
|
|
66
95
|
}
|
|
67
96
|
|
|
68
97
|
function recentHealAttempt() {
|
|
@@ -133,9 +162,14 @@ try {
|
|
|
133
162
|
await runEntry();
|
|
134
163
|
} catch (e) {
|
|
135
164
|
if (!isLocalModuleErr(e)) throw e;
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
165
|
+
const healed = await attemptHeal(describeFailure(e));
|
|
166
|
+
if (!healed) {
|
|
167
|
+
// Broken/missing dependency we can't repair right now (repair failed, or
|
|
168
|
+
// was skipped within the 6h cooldown). attemptHeal already wrote actionable
|
|
169
|
+
// guidance — degrade quietly instead of re-throwing the original import
|
|
170
|
+
// error, which would spew a Node stack trace on every hook fire.
|
|
171
|
+
process.exit(0);
|
|
172
|
+
}
|
|
139
173
|
try {
|
|
140
174
|
await runEntry({ bustCache: true });
|
|
141
175
|
} catch (retryErr) {
|
|
@@ -143,6 +177,6 @@ try {
|
|
|
143
177
|
`[claude-mem-lite] Hook still failing after self-heal: ${retryErr.message}\n` +
|
|
144
178
|
`[claude-mem-lite] Manual recovery: ${TARBALL_FALLBACK}\n`,
|
|
145
179
|
);
|
|
146
|
-
process.exit(
|
|
180
|
+
process.exit(0);
|
|
147
181
|
}
|
|
148
182
|
}
|
package/server-internals.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { porterStem } from './tfidf.mjs';
|
|
|
14
14
|
const INSTRUCTIONS_BASE = [
|
|
15
15
|
'Long-term memory across sessions. Hooks auto-inject context; CLI preferred for explicit queries.',
|
|
16
16
|
'',
|
|
17
|
-
'CLI (via Bash)
|
|
17
|
+
'CLI (via Bash) — invoke as `node ~/.claude-mem-lite/cli.mjs <cmd>` (shipped with the plugin), or bare `claude-mem-lite <cmd>` only if you ran the optional global `npm i -g claude-mem-lite`:',
|
|
18
18
|
' claude-mem-lite search "query" — FTS5 full-text search',
|
|
19
19
|
' claude-mem-lite search "err" --type bugfix — filter by type',
|
|
20
20
|
' claude-mem-lite recall "file.mjs" — file-related memories',
|
|
@@ -22,7 +22,7 @@ const INSTRUCTIONS_BASE = [
|
|
|
22
22
|
' claude-mem-lite get 42,43 — full details by ID',
|
|
23
23
|
' claude-mem-lite timeline --anchor 42 — chronological context',
|
|
24
24
|
'',
|
|
25
|
-
'MCP tools: mem_search, mem_recent, mem_save, mem_get, mem_recall, mem_timeline for programmatic access.',
|
|
25
|
+
'MCP tools: mem_search, mem_recent, mem_save, mem_get, mem_recall, mem_timeline for programmatic access (always available — no PATH/CLI install needed).',
|
|
26
26
|
'mem_save: Save non-obvious insights (bugfix lessons, architecture decisions).',
|
|
27
27
|
'Search tips: short keywords (2-3 words), filter with obs_type when relevant.',
|
|
28
28
|
];
|
package/source-files.mjs
CHANGED
|
@@ -121,6 +121,10 @@ export const SOURCE_FILES = [
|
|
|
121
121
|
// Statically imported by hook-llm, hook-handoff, hook-optimize, hook,
|
|
122
122
|
// mem-cli; reached transitively from server.mjs and cli.mjs.
|
|
123
123
|
'lib/scrub-record.mjs',
|
|
124
|
+
// Rate-limited friendly hint for an unloadable native DB binding
|
|
125
|
+
// (ERR_DLOPEN_FAILED). Statically imported by hook.mjs; ship it so the
|
|
126
|
+
// dispatch catch path resolves in installed/tarball runtimes.
|
|
127
|
+
'lib/native-binding-hint.mjs',
|
|
124
128
|
// Cold-start backfill: parses ~/.claude/projects/<encoded>/<uuid>.jsonl
|
|
125
129
|
// transcripts into user_prompts + observations. Dynamic-imported by
|
|
126
130
|
// mem-cli.mjs::cmdImportJsonl; listed here so source-files-sync.test.mjs
|
package/tool-schemas.mjs
CHANGED
|
@@ -349,7 +349,7 @@ export const tools = [
|
|
|
349
349
|
' - Looking for prior art on a module/feature before refactoring\n' +
|
|
350
350
|
' - User asks "have we seen this before" or references something not in visible context\n' +
|
|
351
351
|
'\n' +
|
|
352
|
-
'Equivalent CLI: claude-mem-lite search "<query>" [--type bugfix]',
|
|
352
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs search "<query>" [--type bugfix]',
|
|
353
353
|
inputSchema: memSearchSchema,
|
|
354
354
|
},
|
|
355
355
|
{
|
|
@@ -367,7 +367,7 @@ export const tools = [
|
|
|
367
367
|
' - User asks "what did we do yesterday / last" with no topic keyword\n' +
|
|
368
368
|
' - Verifying that a just-made change was captured as an observation\n' +
|
|
369
369
|
'\n' +
|
|
370
|
-
'Equivalent CLI: claude-mem-lite recent [N]',
|
|
370
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs recent [N]',
|
|
371
371
|
inputSchema: memRecentSchema,
|
|
372
372
|
},
|
|
373
373
|
{
|
|
@@ -387,7 +387,7 @@ export const tools = [
|
|
|
387
387
|
' - A search hit is interesting and you want its chronological neighbours\n' +
|
|
388
388
|
' - Replaying a session narrative around a known observation ID\n' +
|
|
389
389
|
'\n' +
|
|
390
|
-
'Equivalent CLI: claude-mem-lite timeline --anchor <ID> [--before N --after N]',
|
|
390
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs timeline --anchor <ID> [--before N --after N]',
|
|
391
391
|
inputSchema: memTimelineSchema,
|
|
392
392
|
},
|
|
393
393
|
{
|
|
@@ -407,7 +407,7 @@ export const tools = [
|
|
|
407
407
|
'\n' +
|
|
408
408
|
'On miss, response includes "Try: …" hint listing other sources the ID lives in.\n' +
|
|
409
409
|
'\n' +
|
|
410
|
-
'Equivalent CLI: claude-mem-lite get <id>[,<id>,...] — accepts P#/S#/# prefix.',
|
|
410
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs get <id>[,<id>,...] — accepts P#/S#/# prefix.',
|
|
411
411
|
inputSchema: memGetSchema,
|
|
412
412
|
},
|
|
413
413
|
{
|
|
@@ -425,7 +425,7 @@ export const tools = [
|
|
|
425
425
|
' - Cleaning up an observation saved from a test run or incorrect save\n' +
|
|
426
426
|
' - Always run once with confirm=false, then again with confirm=true\n' +
|
|
427
427
|
'\n' +
|
|
428
|
-
'Equivalent CLI: claude-mem-lite delete <id>[,<id>,...] [--confirm]',
|
|
428
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs delete <id>[,<id>,...] [--confirm]',
|
|
429
429
|
inputSchema: memDeleteSchema,
|
|
430
430
|
hidden: true,
|
|
431
431
|
},
|
|
@@ -444,7 +444,7 @@ export const tools = [
|
|
|
444
444
|
' - After a non-obvious architecture/tradeoff decision — set type="decision", lesson_learned="<constraint + why>"\n' +
|
|
445
445
|
' - User explicitly asks "remember this" or "save a note that ..."\n' +
|
|
446
446
|
'\n' +
|
|
447
|
-
'Equivalent CLI: claude-mem-lite save --type bugfix --lesson "..." "<content>"',
|
|
447
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs save --type bugfix --lesson "..." "<content>"',
|
|
448
448
|
inputSchema: memSaveSchema,
|
|
449
449
|
},
|
|
450
450
|
{
|
|
@@ -462,7 +462,7 @@ export const tools = [
|
|
|
462
462
|
' - Diagnosing why search feels sparse or noisy at a macro level\n' +
|
|
463
463
|
' - Auditing a project before major compression/maintenance\n' +
|
|
464
464
|
'\n' +
|
|
465
|
-
'Equivalent CLI: claude-mem-lite stats [--project X] [--days 30]',
|
|
465
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs stats [--project X] [--days 30]',
|
|
466
466
|
inputSchema: memStatsSchema,
|
|
467
467
|
hidden: true,
|
|
468
468
|
},
|
|
@@ -481,7 +481,7 @@ export const tools = [
|
|
|
481
481
|
' - After a major project phase completes and old per-file observations are noise\n' +
|
|
482
482
|
' - Stats show thousands of low-importance rows dragging search quality\n' +
|
|
483
483
|
'\n' +
|
|
484
|
-
'Equivalent CLI: claude-mem-lite compress [--execute] [--age-days 90] (preview is default)',
|
|
484
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs compress [--execute] [--age-days 90] (preview is default)',
|
|
485
485
|
inputSchema: memCompressSchema,
|
|
486
486
|
hidden: true,
|
|
487
487
|
},
|
|
@@ -500,7 +500,7 @@ export const tools = [
|
|
|
500
500
|
' - After bulk imports or a long offline period\n' +
|
|
501
501
|
' - User asks for periodic maintenance / cleanup\n' +
|
|
502
502
|
'\n' +
|
|
503
|
-
'Equivalent CLI: claude-mem-lite maintain scan --ops dedup,decay',
|
|
503
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs maintain scan --ops dedup,decay',
|
|
504
504
|
inputSchema: memMaintainSchema,
|
|
505
505
|
hidden: true,
|
|
506
506
|
},
|
|
@@ -519,7 +519,7 @@ export const tools = [
|
|
|
519
519
|
' - stats show many degraded (title-only, no lesson) observations\n' +
|
|
520
520
|
' - Start with action="preview" to see candidates before spending tokens\n' +
|
|
521
521
|
'\n' +
|
|
522
|
-
'Equivalent CLI: claude-mem-lite optimize [--run|--run-all] [--task re-enrich,normalize,cluster-merge,smart-compress] [--max N] (preview is default)',
|
|
522
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs optimize [--run|--run-all] [--task re-enrich,normalize,cluster-merge,smart-compress] [--max N] (preview is default)',
|
|
523
523
|
inputSchema: memOptimizeSchema,
|
|
524
524
|
hidden: true,
|
|
525
525
|
},
|
|
@@ -538,7 +538,7 @@ export const tools = [
|
|
|
538
538
|
' - Looking for a tool by capability → action="search" with keywords\n' +
|
|
539
539
|
' - User explicitly asks to import a GitHub repo → action="import_url"\n' +
|
|
540
540
|
'\n' +
|
|
541
|
-
'Equivalent CLI: claude-mem-lite registry <list|search|import|...> [args]',
|
|
541
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs registry <list|search|import|...> [args]',
|
|
542
542
|
inputSchema: memRegistrySchema,
|
|
543
543
|
hidden: true,
|
|
544
544
|
},
|
|
@@ -576,7 +576,7 @@ export const tools = [
|
|
|
576
576
|
' - You later discover additional context worth appending to lesson_learned\n' +
|
|
577
577
|
' - Reclassifying an observation after its true type becomes clear\n' +
|
|
578
578
|
'\n' +
|
|
579
|
-
'Equivalent CLI: claude-mem-lite update <id> [--title ...] [--lesson ...]',
|
|
579
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs update <id> [--title ...] [--lesson ...]',
|
|
580
580
|
inputSchema: memUpdateSchema,
|
|
581
581
|
hidden: true,
|
|
582
582
|
},
|
|
@@ -595,7 +595,7 @@ export const tools = [
|
|
|
595
595
|
' - Moving observations between machines or projects\n' +
|
|
596
596
|
' - User asks for a JSON snapshot of a project\'s memories\n' +
|
|
597
597
|
'\n' +
|
|
598
|
-
'Equivalent CLI: claude-mem-lite export [--format jsonl] [--project X] [--limit 500]',
|
|
598
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs export [--format jsonl] [--project X] [--limit 500]',
|
|
599
599
|
inputSchema: memExportSchema,
|
|
600
600
|
hidden: true,
|
|
601
601
|
},
|
|
@@ -614,7 +614,7 @@ export const tools = [
|
|
|
614
614
|
' - User asks "what do we know about <file>"\n' +
|
|
615
615
|
' - Investigating a recurring issue in a file you have not touched recently\n' +
|
|
616
616
|
'\n' +
|
|
617
|
-
'Equivalent CLI: claude-mem-lite recall "<file>" [--limit 10]',
|
|
617
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs recall "<file>" [--limit 10]',
|
|
618
618
|
inputSchema: memRecallSchema,
|
|
619
619
|
},
|
|
620
620
|
{
|
|
@@ -632,7 +632,7 @@ export const tools = [
|
|
|
632
632
|
' - After a crash, power loss, or manual DB edit\n' +
|
|
633
633
|
' - doctor / stats flags FTS integrity problems\n' +
|
|
634
634
|
'\n' +
|
|
635
|
-
'Equivalent CLI: claude-mem-lite fts-check [--rebuild]',
|
|
635
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs fts-check [--rebuild]',
|
|
636
636
|
inputSchema: memFtsCheckSchema,
|
|
637
637
|
hidden: true,
|
|
638
638
|
},
|
|
@@ -651,7 +651,7 @@ export const tools = [
|
|
|
651
651
|
' - Triaging what to compress or clean up before running maintenance\n' +
|
|
652
652
|
' - Scanning for interesting anchors to follow up with mem_timeline\n' +
|
|
653
653
|
'\n' +
|
|
654
|
-
'Equivalent CLI: claude-mem-lite browse [--tier active] [--project X]',
|
|
654
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs browse [--tier active] [--project X]',
|
|
655
655
|
inputSchema: memBrowseSchema,
|
|
656
656
|
hidden: true,
|
|
657
657
|
},
|
|
@@ -670,7 +670,7 @@ export const tools = [
|
|
|
670
670
|
' - Wrap-up phase enumerates follow-up items for the next session\n' +
|
|
671
671
|
' - Bug surfaces but root cause is out of this session\'s scope\n' +
|
|
672
672
|
'\n' +
|
|
673
|
-
'Equivalent CLI: claude-mem-lite defer add "<title>" [--priority 1|2|3] [--detail "..."] [--files a.mjs,b.mjs]',
|
|
673
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs defer add "<title>" [--priority 1|2|3] [--detail "..."] [--files a.mjs,b.mjs]',
|
|
674
674
|
inputSchema: memDeferSchema,
|
|
675
675
|
},
|
|
676
676
|
{
|
|
@@ -687,7 +687,7 @@ export const tools = [
|
|
|
687
687
|
' - About to refer to "item N" and need to confirm what N points to\n' +
|
|
688
688
|
' - Auditing carry-forward state across multiple sessions\n' +
|
|
689
689
|
'\n' +
|
|
690
|
-
'Equivalent CLI: claude-mem-lite defer list [--project X] [--limit 10]',
|
|
690
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs defer list [--project X] [--limit 10]',
|
|
691
691
|
inputSchema: memDeferListSchema,
|
|
692
692
|
},
|
|
693
693
|
{
|
|
@@ -705,7 +705,7 @@ export const tools = [
|
|
|
705
705
|
' - Scope changed and the work is no longer needed\n' +
|
|
706
706
|
' - User explicitly says "drop the deferred X, never mind"\n' +
|
|
707
707
|
'\n' +
|
|
708
|
-
'Equivalent CLI: claude-mem-lite defer drop <D#N|ordinal> --reason "..."',
|
|
708
|
+
'Equivalent CLI: node ~/.claude-mem-lite/cli.mjs defer drop <D#N|ordinal> --reason "..."',
|
|
709
709
|
inputSchema: memDeferDropSchema,
|
|
710
710
|
},
|
|
711
711
|
];
|