claude-mem-lite 2.20.0 → 2.21.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/install.mjs +15 -0
- package/nlp.mjs +38 -6
- package/package.json +1 -1
- package/scripts/launch.mjs +15 -3
- package/server-internals.mjs +1 -1
- package/server.mjs +1 -1
- package/stop-words.mjs +19 -0
- package/synonyms.mjs +16 -1
- package/utils.mjs +1 -1
package/install.mjs
CHANGED
|
@@ -353,6 +353,21 @@ async function install() {
|
|
|
353
353
|
}
|
|
354
354
|
}
|
|
355
355
|
} catch (e) { warn(`Marketplace hooks dedup: ${e.message}`); }
|
|
356
|
+
|
|
357
|
+
// Sync launch.mjs to plugin cache — ensures MCP server loads dev code via symlink detection
|
|
358
|
+
try {
|
|
359
|
+
const cacheBase = join(homedir(), '.claude', 'plugins', 'cache', MARKETPLACE_KEY, 'claude-mem-lite');
|
|
360
|
+
if (existsSync(cacheBase)) {
|
|
361
|
+
const srcLaunch = join(PROJECT_DIR, 'scripts', 'launch.mjs');
|
|
362
|
+
for (const ver of readdirSync(cacheBase)) {
|
|
363
|
+
const dest = join(cacheBase, ver, 'scripts', 'launch.mjs');
|
|
364
|
+
if (existsSync(join(cacheBase, ver, 'scripts'))) {
|
|
365
|
+
copyFileSync(srcLaunch, dest);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
ok('Plugin cache: launch.mjs synced (dev mode MCP routing)');
|
|
369
|
+
}
|
|
370
|
+
} catch (e) { warn(`Plugin cache sync: ${e.message}`); }
|
|
356
371
|
}
|
|
357
372
|
|
|
358
373
|
// 4. Configure hooks (merge: preserve user's existing hooks, replace ours)
|
package/nlp.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// nlp.mjs -- FTS5 query building, synonym expansion, CJK tokenization.
|
|
2
2
|
// Extracted from utils.mjs for focused module boundaries.
|
|
3
3
|
|
|
4
|
-
import { BASE_STOP_WORDS } from './stop-words.mjs';
|
|
4
|
+
import { BASE_STOP_WORDS, CJK_STOP_WORDS } from './stop-words.mjs';
|
|
5
5
|
import { SYNONYM_MAP, CJK_COMPOUNDS } from './synonyms.mjs';
|
|
6
6
|
|
|
7
7
|
// Re-export for backward compatibility (consumers import from nlp.mjs or utils.mjs)
|
|
@@ -51,7 +51,7 @@ export function cjkBigrams(text) {
|
|
|
51
51
|
return [...new Set(tokens)].join(' ');
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
// ─── CJK
|
|
54
|
+
// ─── CJK Keyword Extraction ─────────────────────────────────────────────────
|
|
55
55
|
|
|
56
56
|
// Extract known CJK words (from SYNONYM_MAP) out of unsegmented CJK text.
|
|
57
57
|
// Greedy longest-match: "数据库的全文搜索" → ["数据库", "搜索"] (skips particles/unknown).
|
|
@@ -77,6 +77,37 @@ export function extractCjkSynonymTokens(text) {
|
|
|
77
77
|
return found;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// Merged CJK dictionary: CJK_COMPOUNDS + CJK keys from SYNONYM_MAP — sorted longest first.
|
|
81
|
+
// Gives broadest coverage: "搜索" from SYNONYM_MAP + "函数" from CJK_COMPOUNDS.
|
|
82
|
+
const _cjkMergedKeys = [...new Set([...CJK_COMPOUNDS, ..._cjkSynonymKeys])]
|
|
83
|
+
.sort((a, b) => b.length - a.length);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Extract CJK keywords using merged dictionary (CJK_COMPOUNDS + SYNONYM_MAP keys).
|
|
87
|
+
* Broader than either source alone. Filters CJK stop words.
|
|
88
|
+
* "这个函数是做什么的" → ["函数"] (not noisy bigrams)
|
|
89
|
+
* "修复数据库性能优化" → ["修复", "数据库", "性能", "优化"]
|
|
90
|
+
* "之前修复的FTS搜索排序" → ["修复", "搜索", "排序"]
|
|
91
|
+
*/
|
|
92
|
+
export function extractCjkKeywords(text) {
|
|
93
|
+
const found = [];
|
|
94
|
+
let i = 0;
|
|
95
|
+
while (i < text.length) {
|
|
96
|
+
if (!/[\u4e00-\u9fff\u3400-\u4dbf]/.test(text[i])) { i++; continue; }
|
|
97
|
+
let matched = false;
|
|
98
|
+
for (const word of _cjkMergedKeys) {
|
|
99
|
+
if (text.startsWith(word, i) && !CJK_STOP_WORDS.has(word)) {
|
|
100
|
+
found.push(word);
|
|
101
|
+
i += word.length;
|
|
102
|
+
matched = true;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!matched) i++;
|
|
107
|
+
}
|
|
108
|
+
return found;
|
|
109
|
+
}
|
|
110
|
+
|
|
80
111
|
// ─── FTS5 Token Formatting ──────────────────────────────────────────────────
|
|
81
112
|
|
|
82
113
|
// Format a term for FTS5: quote if it contains spaces, hyphens, or special chars
|
|
@@ -124,13 +155,14 @@ export function sanitizeFtsQuery(query) {
|
|
|
124
155
|
// Filter stop words (but keep all if filtering would empty the query)
|
|
125
156
|
const filtered = tokens.filter(t => !FTS_STOP_WORDS.has(t.toLowerCase()));
|
|
126
157
|
if (filtered.length > 0) tokens = filtered;
|
|
127
|
-
// Split unsegmented CJK tokens into known vocabulary words
|
|
128
|
-
//
|
|
158
|
+
// Split unsegmented CJK tokens into known vocabulary words using CJK_COMPOUNDS dictionary.
|
|
159
|
+
// Uses broader dictionary than synonym-only extraction for better recall.
|
|
160
|
+
// e.g. "这个函数是做什么的" → ["函数"] (not noisy bigrams)
|
|
129
161
|
const expandedTokens = [];
|
|
130
162
|
let cjkExtracted = false;
|
|
131
163
|
for (const t of tokens) {
|
|
132
164
|
if (/[\u4e00-\u9fff\u3400-\u4dbf]/.test(t) && t.length > 2) {
|
|
133
|
-
const cjkWords =
|
|
165
|
+
const cjkWords = extractCjkKeywords(t);
|
|
134
166
|
if (cjkWords.length > 0) {
|
|
135
167
|
expandedTokens.push(...cjkWords);
|
|
136
168
|
cjkExtracted = true;
|
|
@@ -146,7 +178,7 @@ export function sanitizeFtsQuery(query) {
|
|
|
146
178
|
// Skip bigrams when CJK synonym extraction already produced meaningful tokens —
|
|
147
179
|
// bigrams joined with AND would make the query too restrictive.
|
|
148
180
|
const bigrams = cjkExtracted ? null : cjkBigrams(cleaned);
|
|
149
|
-
const bigramSet = new Set(bigrams ? bigrams.split(' ').filter(
|
|
181
|
+
const bigramSet = new Set(bigrams ? bigrams.split(' ').filter(b => b && !CJK_STOP_WORDS.has(b)) : []);
|
|
150
182
|
const hasBigrams = bigramSet.size > 0;
|
|
151
183
|
const finalTokens = [];
|
|
152
184
|
const seen = new Set();
|
package/package.json
CHANGED
package/scripts/launch.mjs
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// launch.mjs — Auto-installs dependencies then starts MCP server
|
|
3
3
|
// Uses only Node built-ins so it works before npm install
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
|
-
import { existsSync } from 'node:fs';
|
|
5
|
+
import { existsSync, lstatSync } from 'node:fs';
|
|
6
6
|
import { dirname, join } from 'node:path';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
8
9
|
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const ROOT = process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
@@ -19,4 +20,15 @@ if (!existsSync(join(ROOT, 'node_modules', 'better-sqlite3'))) {
|
|
|
19
20
|
process.stderr.write('[claude-mem-lite] Dependencies installed\n');
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
// Dev mode: prefer ~/.claude-mem-lite/server.mjs (symlinked to source) over
|
|
24
|
+
// CLAUDE_PLUGIN_ROOT (potentially stale plugin cache). This ensures the MCP
|
|
25
|
+
// server always runs the latest code when installed with `install --dev`.
|
|
26
|
+
const devServer = join(homedir(), '.claude-mem-lite', 'server.mjs');
|
|
27
|
+
let useDevServer = false;
|
|
28
|
+
try { useDevServer = existsSync(devServer) && lstatSync(devServer).isSymbolicLink(); } catch {}
|
|
29
|
+
|
|
30
|
+
if (useDevServer) {
|
|
31
|
+
await import(pathToFileURL(devServer).href);
|
|
32
|
+
} else {
|
|
33
|
+
await import(new URL('../server.mjs', import.meta.url).href);
|
|
34
|
+
}
|
package/server-internals.mjs
CHANGED
|
@@ -74,7 +74,7 @@ export function reRankWithContext(db, results, project) {
|
|
|
74
74
|
* Mutates result objects in-place by adding superseded=true flag.
|
|
75
75
|
* @param {object[]} results Array of search result objects with source, files_modified, date, importance
|
|
76
76
|
*/
|
|
77
|
-
export function markSuperseded(
|
|
77
|
+
export function markSuperseded(results) {
|
|
78
78
|
if (!results || results.length === 0) return;
|
|
79
79
|
// Build map: file → [result objects], only for obs with files
|
|
80
80
|
const fileMap = new Map();
|
package/server.mjs
CHANGED
|
@@ -569,7 +569,7 @@ server.registerTool(
|
|
|
569
569
|
if (ftsQuery && results.some(r => r.source === 'obs')) {
|
|
570
570
|
const obsResults = results.filter(r => r.source === 'obs');
|
|
571
571
|
reRankWithContext(db, obsResults, currentProject);
|
|
572
|
-
markSuperseded(
|
|
572
|
+
markSuperseded(obsResults);
|
|
573
573
|
results.sort((a, b) => (a.score ?? 0) - (b.score ?? 0));
|
|
574
574
|
}
|
|
575
575
|
|
package/stop-words.mjs
CHANGED
|
@@ -14,3 +14,22 @@ export const BASE_STOP_WORDS = new Set([
|
|
|
14
14
|
'all', 'each', 'every', 'both', 'few', 'more', 'most', 'other', 'some',
|
|
15
15
|
'such', 'than', 'too', 'very', 'just', 'also', 'then', 'so', 'if',
|
|
16
16
|
]);
|
|
17
|
+
|
|
18
|
+
/** CJK stop words — particles, function words, and fillers that add noise to FTS queries. */
|
|
19
|
+
export const CJK_STOP_WORDS = new Set([
|
|
20
|
+
// Particles & structural
|
|
21
|
+
'的', '了', '是', '在', '有', '和', '与', '被', '把', '给',
|
|
22
|
+
'个', '这', '那', '也', '就', '都', '而', '及', '或', '但',
|
|
23
|
+
'还', '到', '让', '很', '得', '着', '过', '会', '要',
|
|
24
|
+
'能', '不', '没',
|
|
25
|
+
// Question words (intent detection handles these separately)
|
|
26
|
+
'什么', '怎么', '如何', '为何', '哪个',
|
|
27
|
+
// Quantifiers & demonstratives
|
|
28
|
+
'一下', '一些', '一个', '一种',
|
|
29
|
+
// Directional/auxiliary
|
|
30
|
+
'来', '去', '做', '从', '向', '比', '对',
|
|
31
|
+
// Sentence-final particles
|
|
32
|
+
'吗', '吧', '呢', '呀', '啊', '嗯', '哦',
|
|
33
|
+
// Common filler phrases
|
|
34
|
+
'看看', '帮我', '没有', '可以', '怎么样',
|
|
35
|
+
]);
|
package/synonyms.mjs
CHANGED
|
@@ -120,11 +120,26 @@ export const SYNONYM_PAIRS = [
|
|
|
120
120
|
['打包', 'bundle'], ['类型', 'type'], ['类型', 'typescript'],
|
|
121
121
|
// Errors
|
|
122
122
|
['错误', 'error'], ['异常', 'exception'],
|
|
123
|
+
['报错', 'error'], ['崩溃', 'crash'],
|
|
123
124
|
// Infrastructure
|
|
124
125
|
['容器', 'container'], ['容器', 'docker'],
|
|
125
126
|
['集群', 'cluster'], ['集群', 'kubernetes'],
|
|
126
127
|
['网关', 'gateway'], ['负载', 'load balancing'],
|
|
127
128
|
['队列', 'queue'], ['序列化', 'serialize'],
|
|
129
|
+
// Code structure (missing from earlier CJK pairs)
|
|
130
|
+
['函数', 'function'], ['变量', 'variable'],
|
|
131
|
+
['模块', 'module'], ['框架', 'framework'],
|
|
132
|
+
['编译', 'compile'], ['服务器', 'server'],
|
|
133
|
+
['前端', 'frontend'], ['后端', 'backend'],
|
|
134
|
+
['优化', 'optimize'], ['优化', 'optimization'],
|
|
135
|
+
['架构', 'architecture'], ['设计', 'design'],
|
|
136
|
+
['文档', 'documentation'], ['文档', 'docs'],
|
|
137
|
+
['版本', 'version'], ['分支', 'branch'],
|
|
138
|
+
['提交', 'commit'], ['推送', 'push'],
|
|
139
|
+
['合并', 'merge'], ['升级', 'upgrade'],
|
|
140
|
+
['安装', 'install'], ['导入', 'import'],
|
|
141
|
+
['导出', 'export'], ['状态', 'state'],
|
|
142
|
+
['系统', 'system'], ['算法', 'algorithm'],
|
|
128
143
|
];
|
|
129
144
|
|
|
130
145
|
// ─── Bidirectional SYNONYM_MAP (case-insensitive) ──────────────────────────────
|
|
@@ -160,7 +175,7 @@ export const CJK_COMPOUNDS = new Set([
|
|
|
160
175
|
'报错', '崩溃', '泄露', '溢出', '死锁', '超时', '中断', '异常', '故障',
|
|
161
176
|
// architecture
|
|
162
177
|
'架构', '设计', '方案', '规划', '文档', '注释', '版本', '分支', '依赖',
|
|
163
|
-
'性能', '安全', '漏洞', '补丁',
|
|
178
|
+
'性能', '安全', '漏洞', '补丁', '系统', '算法',
|
|
164
179
|
]);
|
|
165
180
|
|
|
166
181
|
// ─── Dispatch Synonyms (unidirectional, broader groupings) ──────────────────
|
package/utils.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { execSync } from 'child_process';
|
|
|
9
9
|
// Backward compatibility: all consumers import from utils.mjs
|
|
10
10
|
|
|
11
11
|
export { DECAY_HALF_LIFE_BY_TYPE, DEFAULT_DECAY_HALF_LIFE_MS, OBS_BM25, SESS_BM25, TYPE_DECAY_CASE, OBS_FTS_COLUMNS } from './scoring-sql.mjs';
|
|
12
|
-
export { cjkBigrams, extractCjkSynonymTokens, SYNONYM_MAP, expandToken, sanitizeFtsQuery, relaxFtsQueryToOr, FTS_STOP_WORDS, CJK_COMPOUNDS } from './nlp.mjs';
|
|
12
|
+
export { cjkBigrams, extractCjkSynonymTokens, extractCjkKeywords, SYNONYM_MAP, expandToken, sanitizeFtsQuery, relaxFtsQueryToOr, FTS_STOP_WORDS, CJK_COMPOUNDS } from './nlp.mjs';
|
|
13
13
|
export { resolveProject, _resetProjectCache } from './project-utils.mjs';
|
|
14
14
|
export { scrubSecrets, SECRET_PATTERNS } from './secret-scrub.mjs';
|
|
15
15
|
export { truncate, typeIcon, fmtDate, fmtTime, isoWeekKey } from './format-utils.mjs';
|