claude-mem-lite 6.1.0 → 6.2.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 +11 -4
- package/README.zh-CN.md +10 -4
- package/install.mjs +116 -0
- package/lib/platform-gate.mjs +87 -0
- package/npm-shrinkwrap.json +4 -3
- package/package.json +4 -2
- package/plugin-cache-guard.mjs +10 -2
- package/scripts/launch.mjs +36 -1
- package/source-files.mjs +5 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "claude-mem-lite",
|
|
13
|
-
"version": "6.
|
|
13
|
+
"version": "6.2.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": "6.
|
|
3
|
+
"version": "6.2.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
|
@@ -134,16 +134,23 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
|
|
|
134
134
|
|
|
135
135
|
| Platform | Status | Notes |
|
|
136
136
|
|----------|--------|-------|
|
|
137
|
-
| **Linux** | Supported | Primary development and testing platform |
|
|
137
|
+
| **Linux** | Supported | Primary development and testing platform; the whole CI matrix runs here |
|
|
138
138
|
| **macOS** | Supported | Fully compatible (Intel and Apple Silicon) |
|
|
139
|
-
| **Windows** |
|
|
139
|
+
| **Windows** | Installs, not CI-covered | The MCP server, the CLI and the `node` hooks work (`better-sqlite3` ships `win32-x64` and `win32-arm64` prebuilds, so nothing is compiled). **Three hook commands run under `bash`** — `setup.sh`, `post-tool-use.sh`, `pre-agent-inject.sh` — and need Git for Windows or WSL on `PATH`; `claude-mem-lite doctor` reports it when `bash` cannot be found. No GitHub Actions runner exercises Windows, so this rests on user reports ([#28](https://github.com/sdsrss/claude-mem-lite/issues/28)), not on a green pipeline |
|
|
140
|
+
| **WSL2** | Untested | Linux under the hood, so it should behave as the Linux row; nobody has reported either way |
|
|
141
|
+
|
|
142
|
+
From v5.1.0 through v6.1.0, `package.json` declared `os: ["darwin", "linux"]`. That is an npm *install*
|
|
143
|
+
gate, not a runtime check: on Windows it made `npm install` exit `EBADPLATFORM`, which the
|
|
144
|
+
plugin launcher runs on the first MCP start after every plugin update — so the server never
|
|
145
|
+
came up and `/mcp` reported `CONNECTION_CLOSED`. `win32` is now in the list. A platform that
|
|
146
|
+
is still outside it gets a message naming both sides of the mismatch instead of a guess.
|
|
140
147
|
|
|
141
148
|
## Requirements
|
|
142
149
|
|
|
143
150
|
- **Node.js** >= 22
|
|
144
151
|
- **Claude Code** CLI installed and configured (`claude` command available)
|
|
145
|
-
- **SQLite3** support (provided by `better-sqlite3
|
|
146
|
-
- **Platform**: Linux or macOS (see [Platform Support](#platform-support))
|
|
152
|
+
- **SQLite3** support (provided by `better-sqlite3` 13, which ships prebuilt binaries for 8 platforms — no compiler needed on any of them; a platform it has no prebuild for falls back to building from source)
|
|
153
|
+
- **Platform**: Linux or macOS; Windows installs and runs but is not CI-covered and needs Git Bash or WSL for three hooks (see [Platform Support](#platform-support))
|
|
147
154
|
|
|
148
155
|
## Installation
|
|
149
156
|
|
package/README.zh-CN.md
CHANGED
|
@@ -100,16 +100,22 @@
|
|
|
100
100
|
|
|
101
101
|
| 平台 | 状态 | 说明 |
|
|
102
102
|
|------|------|------|
|
|
103
|
-
| **Linux** | 支持 |
|
|
103
|
+
| **Linux** | 支持 | 主要开发和测试平台;整个 CI 矩阵都跑在这里 |
|
|
104
104
|
| **macOS** | 支持 | 完全兼容(Intel 和 Apple Silicon) |
|
|
105
|
-
| **Windows** |
|
|
105
|
+
| **Windows** | 可安装,但无 CI 覆盖 | MCP server、CLI 和 `node` 类 hook 均可用(`better-sqlite3` 自带 `win32-x64` / `win32-arm64` 预编译产物,无需编译)。**有三个 hook 命令走 `bash`** —— `setup.sh`、`post-tool-use.sh`、`pre-agent-inject.sh` —— 需要 PATH 上有 Git for Windows 或 WSL;`bash` 找不到时 `claude-mem-lite doctor` 会报出来。GitHub Actions 没有 Windows runner,所以这一行依据的是用户报告([#28](https://github.com/sdsrss/claude-mem-lite/issues/28))而不是绿色流水线 |
|
|
106
|
+
| **WSL2** | 未测试 | 底层就是 Linux,预期与 Linux 行一致;但无人报告过实际结果 |
|
|
107
|
+
|
|
108
|
+
v5.1.0 到 v6.1.0 之间,`package.json` 声明的是 `os: ["darwin", "linux"]`。那是 npm 的**安装**门禁,不是运行时检查:
|
|
109
|
+
在 Windows 上它让 `npm install` 以 `EBADPLATFORM` 退出,而插件启动器每次插件更新后的首次 MCP 启动都要跑这条
|
|
110
|
+
安装 —— 于是 server 起不来,`/mcp` 报 `CONNECTION_CLOSED`。现在 `win32` 已加入该列表。仍不在列表内的平台会拿到
|
|
111
|
+
一条同时点明“声明了什么”和“当前是什么”的消息,而不是一句猜测。
|
|
106
112
|
|
|
107
113
|
## 环境要求
|
|
108
114
|
|
|
109
115
|
- **Node.js** >= 22(v4.0.0 起:better-sqlite3 13 要求 >=22,Node 20 已于 2026-04 EOL;`package.json` 的 `engines` 是唯一事实来源)
|
|
110
116
|
- **Claude Code** CLI 已安装并配置(`claude` 命令可用)
|
|
111
|
-
- **SQLite3** 支持(由 `better-sqlite3`
|
|
112
|
-
- **平台**:Linux 或 macOS(参见[平台支持](#平台支持))
|
|
117
|
+
- **SQLite3** 支持(由 `better-sqlite3` 13 提供,它自带 8 个平台的预编译产物,这些平台上都不需要编译器;没有对应预编译产物的平台才会回退到源码编译)
|
|
118
|
+
- **平台**:Linux 或 macOS;Windows 可安装运行,但无 CI 覆盖,且三个 hook 需要 Git Bash 或 WSL(参见[平台支持](#平台支持))
|
|
113
119
|
|
|
114
120
|
## 安装
|
|
115
121
|
|
package/install.mjs
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
scanPluginCacheHookPollution,
|
|
62
62
|
hasInstallManagedHooks,
|
|
63
63
|
pluginCacheHookEvents,
|
|
64
|
+
settingsHookCommands,
|
|
64
65
|
} from './plugin-cache-guard.mjs';
|
|
65
66
|
import { SOURCE_FILES, HOOK_SCRIPT_FILES } from './source-files.mjs';
|
|
66
67
|
import {
|
|
@@ -266,6 +267,59 @@ export function buildDoctorSummary(issues, warnings) {
|
|
|
266
267
|
return `${issues} issue(s) found.${warnSuffix}`;
|
|
267
268
|
}
|
|
268
269
|
|
|
270
|
+
/**
|
|
271
|
+
* How many LIVE hook commands invoke `bash`, and which scripts they are.
|
|
272
|
+
*
|
|
273
|
+
* There are two hook registrations and only one is live per install shape, which is what
|
|
274
|
+
* the first cut of doctor's interpreter check got wrong (pre-ship review P1-1). The plugin
|
|
275
|
+
* shape reads `hooks/hooks.json` out of the plugin cache. The npm / npx / `git clone` shape
|
|
276
|
+
* has no such file — `hooks/hooks.json` is in RELEASE_SIGNED_FILES but NOT in SOURCE_FILES,
|
|
277
|
+
* so nothing deploys it to ~/.claude-mem-lite/ — and registers its hooks in settings.json
|
|
278
|
+
* instead. Reading only the manifest therefore answered "zero bash hooks" on the one shape
|
|
279
|
+
* where two of them are live.
|
|
280
|
+
*
|
|
281
|
+
* Returns THREE outcomes, never two. `count: null` means no registration could be read, and
|
|
282
|
+
* that is deliberately distinct from a count of zero: zero is an answer, null is the absence
|
|
283
|
+
* of one, and a diagnostic that reports them identically tells the reader to stop looking.
|
|
284
|
+
*
|
|
285
|
+
* @param {{manifestPath: string, settingsCommands?: string[], installDir: string}} opts
|
|
286
|
+
* @returns {{count: number|null, source: 'manifest'|'settings'|null, scripts: string[]}}
|
|
287
|
+
*/
|
|
288
|
+
export function resolveBashHookCount({ manifestPath, settingsCommands = [], installDir }) {
|
|
289
|
+
const basenames = (commands) =>
|
|
290
|
+
commands
|
|
291
|
+
.map((c) => {
|
|
292
|
+
const m = c.match(/([^/"\s]+\.sh)/);
|
|
293
|
+
return m ? m[1] : c;
|
|
294
|
+
})
|
|
295
|
+
.sort();
|
|
296
|
+
|
|
297
|
+
if (existsSync(manifestPath)) {
|
|
298
|
+
try {
|
|
299
|
+
const parsed = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
300
|
+
const commands = [];
|
|
301
|
+
for (const matchers of Object.values(parsed?.hooks || {})) {
|
|
302
|
+
for (const m of matchers || []) {
|
|
303
|
+
for (const h of m?.hooks || []) commands.push(String(h?.command || ''));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const bash = commands.filter((c) => c.startsWith('bash '));
|
|
307
|
+
return { count: bash.length, source: 'manifest', scripts: basenames(bash) };
|
|
308
|
+
} catch {
|
|
309
|
+
// A torn manifest is not evidence of zero bash hooks. Fall through to settings.json,
|
|
310
|
+
// and if that says nothing about us either, the caller gets null.
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Only OUR entries: settings.json is shared with every other tool the user installs, so a
|
|
314
|
+
// foreign `bash "…"` line is not ours to report on, and — the discriminating half — a
|
|
315
|
+
// settings.json that names nothing of ours is not evidence that no hook needs bash. It is
|
|
316
|
+
// evidence we are reading the wrong registration.
|
|
317
|
+
const ours = settingsCommands.filter((c) => c.includes(installDir));
|
|
318
|
+
if (ours.length === 0) return { count: null, source: null, scripts: [] };
|
|
319
|
+
const bash = ours.filter((c) => c.startsWith('bash '));
|
|
320
|
+
return { count: bash.length, source: 'settings', scripts: basenames(bash) };
|
|
321
|
+
}
|
|
322
|
+
|
|
269
323
|
// Dev installs symlink server.mjs → the project's source file. Used to suppress
|
|
270
324
|
// misleading "first run" messages since hook-update.mjs skips state-writes in
|
|
271
325
|
// this mode (see hook-update.mjs isDevMode).
|
|
@@ -1865,6 +1919,68 @@ async function doctor() {
|
|
|
1865
1919
|
dwarn('Hook scripts: check failed — ' + e.message);
|
|
1866
1920
|
}
|
|
1867
1921
|
|
|
1922
|
+
// Hook interpreter. Some hook commands are `bash "<script>"` (the PostToolUse and
|
|
1923
|
+
// Agent prefilters, plus setup.sh in the plugin manifest) — the rest are `node`. If bash
|
|
1924
|
+
// cannot run, those commands fail and nothing says so; the check above grades whether the
|
|
1925
|
+
// FILES are present, which they are.
|
|
1926
|
+
//
|
|
1927
|
+
// Keyed on whether bash runs, not on process.platform === 'win32'. A Windows user with
|
|
1928
|
+
// Git for Windows on PATH — the normal case, since Claude Code shells out to bash for its
|
|
1929
|
+
// own Bash tool — has a working configuration and must not be warned; a stripped
|
|
1930
|
+
// container with no bash has a broken one and must be, whatever its platform. This is
|
|
1931
|
+
// also what issue #28's P3-19 intent asked for: `os: [darwin, linux]` was added so a
|
|
1932
|
+
// Windows user "should be told rather than handed a string of silent catch blocks", and
|
|
1933
|
+
// blocking the install told them nothing. This is the telling.
|
|
1934
|
+
try {
|
|
1935
|
+
const {
|
|
1936
|
+
count: bashCommands,
|
|
1937
|
+
source: countSource,
|
|
1938
|
+
scripts: bashScripts,
|
|
1939
|
+
} = resolveBashHookCount({
|
|
1940
|
+
manifestPath: join(PROJECT_DIR, 'hooks', 'hooks.json'),
|
|
1941
|
+
settingsCommands: settingsHookCommands(homedir()),
|
|
1942
|
+
installDir: INSTALL_DIR,
|
|
1943
|
+
});
|
|
1944
|
+
if (bashCommands === null) {
|
|
1945
|
+
// NOT `ok`. Pre-ship review (P1-1) found the first cut printing "no hook command needs
|
|
1946
|
+
// bash" here, on a shape where two of them are registered — a green line that ends the
|
|
1947
|
+
// reader's search is worse than the silence this check exists to remove.
|
|
1948
|
+
dwarn(
|
|
1949
|
+
'Hook interpreter: could not read either hook registration — neither ' +
|
|
1950
|
+
`${join(PROJECT_DIR, 'hooks', 'hooks.json')} nor a claude-mem-lite entry in ` +
|
|
1951
|
+
`${join(homedir(), '.claude', 'settings.json')} — so whether any hook needs bash is unknown.`,
|
|
1952
|
+
);
|
|
1953
|
+
} else if (bashCommands === 0) {
|
|
1954
|
+
ok(`Hook interpreter: no hook command needs bash (per the ${countSource})`);
|
|
1955
|
+
} else {
|
|
1956
|
+
let bashOk = false;
|
|
1957
|
+
try {
|
|
1958
|
+
execFileSync('bash', ['-c', 'exit 0'], { stdio: 'ignore', timeout: 5000 });
|
|
1959
|
+
bashOk = true;
|
|
1960
|
+
} catch {
|
|
1961
|
+
/* not resolvable, or not runnable — either way the hooks that need it cannot fire */
|
|
1962
|
+
}
|
|
1963
|
+
if (bashOk) {
|
|
1964
|
+
ok(`Hook interpreter: bash present (${bashCommands} hook command(s) need it)`);
|
|
1965
|
+
} else {
|
|
1966
|
+
// dwarn, not an issue: everything else works. Saying "broken" about an install
|
|
1967
|
+
// whose MCP server and node hooks are fine would be the mirror of the defect that
|
|
1968
|
+
// sent this round's reporter looking at their disk and their network.
|
|
1969
|
+
// The scripts are NAMED from the live registration rather than described from
|
|
1970
|
+
// memory — the first cut wrote "(episode Read-tracking and the subagent prefilter)",
|
|
1971
|
+
// a two-item gloss on a count of three (P3-1).
|
|
1972
|
+
dwarn(
|
|
1973
|
+
`Hook interpreter: bash not found on PATH — the ${bashCommands} hook command(s) that ` +
|
|
1974
|
+
`invoke it cannot fire (${bashScripts.join(', ')}). The MCP server and the node ` +
|
|
1975
|
+
'hooks are unaffected. On Windows, install Git for Windows or use WSL; elsewhere ' +
|
|
1976
|
+
'this means a stripped PATH.',
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
} catch (e) {
|
|
1981
|
+
dwarn('Hook interpreter: check failed — ' + e.message);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1868
1984
|
// Stale temp files
|
|
1869
1985
|
try {
|
|
1870
1986
|
// hook-update + the episode workers write runtime/ + staging under DB_DIR
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// lib/platform-gate.mjs — is THIS platform inside the `os` list package.json declares?
|
|
2
|
+
//
|
|
3
|
+
// Why this is a module and not four lines inside scripts/launch.mjs, which is its only
|
|
4
|
+
// consumer: launch.mjs cannot be imported. Its top level installs dependencies, probes the
|
|
5
|
+
// native binding and then imports the MCP server, so a test that imported it would start a
|
|
6
|
+
// server. The alternative to a real import is a source-text scan of launch.mjs, which this
|
|
7
|
+
// repo has repeatedly found to be walkable — a guard that greps for a string passes for a
|
|
8
|
+
// change that keeps the string and deletes the behaviour. So the predicate lives here where
|
|
9
|
+
// tests can drive it, and launch.mjs keeps the wording and the exit.
|
|
10
|
+
//
|
|
11
|
+
// `os` is an npm INSTALL GATE, evaluated against the ROOT package being installed. npm
|
|
12
|
+
// rejects with EBADPLATFORM before it resolves anything, which is why a platform left off
|
|
13
|
+
// the list does not degrade — it fails the install outright, and in plugin mode that install
|
|
14
|
+
// is on the path of every first MCP launch after an update (issue #28).
|
|
15
|
+
//
|
|
16
|
+
// Node built-ins only: this runs BEFORE `npm install`, so node_modules may not exist yet.
|
|
17
|
+
|
|
18
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* npm's own `checkList` from npm-install-checks, reproduced rather than approximated.
|
|
23
|
+
*
|
|
24
|
+
* The rule is not "is it in the list": an entry may be NEGATED with `!`, and a list of
|
|
25
|
+
* nothing but negations allows every platform it does not name. `list.includes(platform)`
|
|
26
|
+
* agrees with npm on the simple case and disagrees on `["!win32"]` — where it would tell a
|
|
27
|
+
* linux user their platform is unsupported while npm installs happily. A diagnostic that is
|
|
28
|
+
* wrong in that direction is worse than none.
|
|
29
|
+
*
|
|
30
|
+
* @param {string[]|string} list The `os` value as declared.
|
|
31
|
+
* @param {string} platform A `process.platform` value.
|
|
32
|
+
* @returns {boolean} True when npm would allow the install.
|
|
33
|
+
*/
|
|
34
|
+
export function platformAllowed(list, platform) {
|
|
35
|
+
const entries = typeof list === 'string' ? [list] : list;
|
|
36
|
+
if (!Array.isArray(entries)) return true;
|
|
37
|
+
if (entries.length === 1 && entries[0] === 'any') return true;
|
|
38
|
+
let negated = 0;
|
|
39
|
+
let match = false;
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
const negate = typeof entry === 'string' && entry.startsWith('!');
|
|
42
|
+
const test = negate ? entry.slice(1) : entry;
|
|
43
|
+
if (negate) {
|
|
44
|
+
negated++;
|
|
45
|
+
if (platform === test) return false;
|
|
46
|
+
} else if (platform === test) {
|
|
47
|
+
match = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// An all-negation list (and, by the same arithmetic, an empty one) allows anything it
|
|
51
|
+
// has not vetoed above.
|
|
52
|
+
return match || negated === entries.length;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The `os` list `<root>/package.json` declares, or null when there is nothing to enforce.
|
|
57
|
+
*
|
|
58
|
+
* Fails OPEN on every error. A missing or torn package.json is the incomplete-install case
|
|
59
|
+
* that launch-preflight.mjs already diagnoses with a repair command; turning it into a
|
|
60
|
+
* platform complaint here would replace a good message with a wrong one.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} root Directory holding package.json.
|
|
63
|
+
* @returns {string[]|null}
|
|
64
|
+
*/
|
|
65
|
+
export function readDeclaredPlatforms(root) {
|
|
66
|
+
const p = join(root, 'package.json');
|
|
67
|
+
if (!existsSync(p)) return null;
|
|
68
|
+
try {
|
|
69
|
+
const os = JSON.parse(readFileSync(p, 'utf8'))?.os;
|
|
70
|
+
if (typeof os === 'string') return [os];
|
|
71
|
+
return Array.isArray(os) ? os : null;
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Would npm refuse to install this package on this platform?
|
|
79
|
+
*
|
|
80
|
+
* @param {{root: string, platform?: string}} opts
|
|
81
|
+
* @returns {{blocked: boolean, declared: string[]|null, platform: string}}
|
|
82
|
+
*/
|
|
83
|
+
export function platformGate({ root, platform = process.platform }) {
|
|
84
|
+
const declared = readDeclaredPlatforms(root);
|
|
85
|
+
if (declared === null) return { blocked: false, declared: null, platform };
|
|
86
|
+
return { blocked: !platformAllowed(declared, platform), declared, platform };
|
|
87
|
+
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "claude-mem-lite",
|
|
9
|
-
"version": "6.
|
|
9
|
+
"version": "6.2.0",
|
|
10
10
|
"os": [
|
|
11
11
|
"darwin",
|
|
12
|
-
"linux"
|
|
12
|
+
"linux",
|
|
13
|
+
"win32"
|
|
13
14
|
],
|
|
14
15
|
"dependencies": {
|
|
15
16
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.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",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"os": [
|
|
11
11
|
"darwin",
|
|
12
|
-
"linux"
|
|
12
|
+
"linux",
|
|
13
|
+
"win32"
|
|
13
14
|
],
|
|
14
15
|
"bin": {
|
|
15
16
|
"claude-mem-lite": "./cli.mjs"
|
|
@@ -106,6 +107,7 @@
|
|
|
106
107
|
"lib/hook-stdout.mjs",
|
|
107
108
|
"lib/proc-lock.mjs",
|
|
108
109
|
"lib/atomic-write.mjs",
|
|
110
|
+
"lib/platform-gate.mjs",
|
|
109
111
|
"lib/hook-prune.mjs",
|
|
110
112
|
"lib/proxy-fetch.mjs",
|
|
111
113
|
"lib/llm-provider-probe.mjs",
|
package/plugin-cache-guard.mjs
CHANGED
|
@@ -117,8 +117,16 @@ export function hasInstallManagedHooks(opts) {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/**
|
|
121
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Every `command` string under settings.json `hooks`, in registration order.
|
|
122
|
+
*
|
|
123
|
+
* Exported since the issue-#28 round: doctor's hook-interpreter check needs the LIVE
|
|
124
|
+
* registration, and on the npm / npx / `git clone` shape that is settings.json rather than
|
|
125
|
+
* `hooks/hooks.json` (which SOURCE_FILES does not deploy). Reading it here rather than
|
|
126
|
+
* hand-rolling a second walk keeps the two consumers on one parser — the twin-drift class
|
|
127
|
+
* this repo keeps paying for.
|
|
128
|
+
*/
|
|
129
|
+
export function settingsHookCommands(home) {
|
|
122
130
|
const settingsPath = join(home, '.claude', 'settings.json');
|
|
123
131
|
if (!existsSync(settingsPath)) return [];
|
|
124
132
|
let s;
|
package/scripts/launch.mjs
CHANGED
|
@@ -11,6 +11,36 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
11
11
|
const ROOT = process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
12
12
|
|
|
13
13
|
if (!existsSync(join(ROOT, 'node_modules', 'better-sqlite3'))) {
|
|
14
|
+
// Platform gate BEFORE npm, not after it. package.json's `os` field is an npm install
|
|
15
|
+
// gate: npm exits EBADPLATFORM without resolving anything, so the catch below sees only
|
|
16
|
+
// "Command failed" and answers with a fixed cause list that cannot contain this cause.
|
|
17
|
+
// That is issue #28 — a Windows user got CONNECTION_CLOSED in /mcp plus a wrong reason,
|
|
18
|
+
// where the field was added (b6a2579, R10 P3-19) precisely so they would be TOLD.
|
|
19
|
+
// Letting npm fail and then guessing is the shape that failed; asking the manifest first
|
|
20
|
+
// is the shape that names both sides of the mismatch. Guarded import: lib/ can be absent
|
|
21
|
+
// in an incomplete install, which launch-preflight.mjs below diagnoses properly, and a
|
|
22
|
+
// missing diagnostic must never become a new failure mode.
|
|
23
|
+
try {
|
|
24
|
+
const { platformGate } = await import('../lib/platform-gate.mjs');
|
|
25
|
+
const gate = platformGate({ root: ROOT });
|
|
26
|
+
if (gate.blocked) {
|
|
27
|
+
process.stderr.write(
|
|
28
|
+
`[claude-mem-lite] npm install is blocked by this package's own platform list (npm EBADPLATFORM).\n`,
|
|
29
|
+
);
|
|
30
|
+
process.stderr.write(
|
|
31
|
+
`[claude-mem-lite] package.json declares os: ${gate.declared.join(', ')} — this machine is ${gate.platform}\n`,
|
|
32
|
+
);
|
|
33
|
+
process.stderr.write(
|
|
34
|
+
`[claude-mem-lite] Nothing was installed, so the MCP server cannot start. See "Platform Support" in the README.\n`,
|
|
35
|
+
);
|
|
36
|
+
process.stderr.write(
|
|
37
|
+
`[claude-mem-lite] To install anyway: cd "${ROOT}" && npm install --omit=dev --force\n`,
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
process.stderr.write(`[claude-mem-lite] platform check skipped: ${e.message}\n`);
|
|
43
|
+
}
|
|
14
44
|
process.stderr.write('[claude-mem-lite] Installing dependencies...\n');
|
|
15
45
|
try {
|
|
16
46
|
execSync('npm install --omit=dev', {
|
|
@@ -55,8 +85,13 @@ if (!existsSync(join(ROOT, 'node_modules', 'better-sqlite3'))) {
|
|
|
55
85
|
(e?.signal ? `npm killed by ${e.signal}` : '') ||
|
|
56
86
|
'unknown error';
|
|
57
87
|
process.stderr.write(`[claude-mem-lite] npm install failed in ${ROOT} — ${detail}\n`);
|
|
88
|
+
// "Likely cause: …" until issue #28: it asserted three causes, and the one that was
|
|
89
|
+
// actually firing (EBADPLATFORM, gated above) was not among them. stderr is inherited,
|
|
90
|
+
// so npm's own `npm error code <CODE>` line is already on this stream a few lines up —
|
|
91
|
+
// point at that instead of competing with it. A guess presented as a diagnosis costs
|
|
92
|
+
// more than no diagnosis: it sends the reader looking at their disk and their network.
|
|
58
93
|
process.stderr.write(
|
|
59
|
-
`[claude-mem-lite]
|
|
94
|
+
`[claude-mem-lite] npm printed its own error above — read its "npm error code" line first. Common causes: read-only directory, disk full, network blocked.\n`,
|
|
60
95
|
);
|
|
61
96
|
process.stderr.write(`[claude-mem-lite] Repair: cd "${ROOT}" && npm install --omit=dev\n`);
|
|
62
97
|
process.exit(1);
|
package/source-files.mjs
CHANGED
|
@@ -142,6 +142,11 @@ export const SOURCE_FILES = [
|
|
|
142
142
|
// + auto-update lock). Must ship or a partial install/update skips them.
|
|
143
143
|
'lib/proc-lock.mjs',
|
|
144
144
|
'lib/atomic-write.mjs',
|
|
145
|
+
// Dynamically imported by scripts/launch.mjs BEFORE `npm install` runs, to answer
|
|
146
|
+
// EBADPLATFORM with both sides of the mismatch instead of a guessed cause (issue #28).
|
|
147
|
+
// The import is guarded, so omitting this file would not crash the launcher — it would
|
|
148
|
+
// silently restore the wrong diagnosis, which is the failure this shipped to fix.
|
|
149
|
+
'lib/platform-gate.mjs',
|
|
145
150
|
// Shared settings.json hook classification + dangling-entry reconciliation.
|
|
146
151
|
// Statically imported by install.mjs AND dynamically by hook-update.mjs's
|
|
147
152
|
// post-swap step — missing it from the manifest would break auto-update's
|