claude-mem-lite 2.9.7 → 2.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/dispatch.mjs +29 -12
- package/hook-update.mjs +15 -1
- package/install.mjs +37 -17
- package/package.json +1 -1
- package/scripts/setup.sh +44 -0
- package/server.mjs +30 -2
- package/tool-schemas.mjs +3 -2
- /package/{.mcp.json → .claude-plugin/.mcp.json} +0 -0
package/dispatch.mjs
CHANGED
|
@@ -899,28 +899,45 @@ function passesConfidenceGate(results, signals) {
|
|
|
899
899
|
|
|
900
900
|
// ─── Auto-loaded Skill Filter ────────────────────────────────────────────────
|
|
901
901
|
|
|
902
|
+
// Plugin-namespaced skills with high adoption rates deserve proactive recommendations
|
|
903
|
+
// even though they're listed in system-reminder. The listing alone doesn't guarantee
|
|
904
|
+
// Claude invokes them at the right moment — a contextual nudge at the right time
|
|
905
|
+
// is more effective than a static list.
|
|
906
|
+
const AUTOLOADED_MIN_ADOPTIONS = 3; // Must have been adopted at least N times total
|
|
907
|
+
const AUTOLOADED_MIN_ADOPT_RATE = 0.08; // Minimum adoption rate to keep recommending
|
|
908
|
+
|
|
902
909
|
/**
|
|
903
|
-
* Filter
|
|
904
|
-
*
|
|
905
|
-
*
|
|
910
|
+
* Filter auto-loaded skills with adoption-aware logic.
|
|
911
|
+
*
|
|
912
|
+
* Plugin-namespaced skills (e.g. "superpowers:systematic-debugging") are listed
|
|
913
|
+
* in system-reminder, so Claude already knows about them. However, blanket filtering
|
|
914
|
+
* removes high-value skills that users actually adopt when recommended contextually.
|
|
915
|
+
*
|
|
916
|
+
* Strategy: filter auto-loaded skills that have poor adoption history (recommend fatigue),
|
|
917
|
+
* but keep those that users actually adopt — the contextual timing adds real value.
|
|
906
918
|
*
|
|
907
|
-
* User-installed standalone skills (non-namespaced
|
|
908
|
-
* are KEPT —
|
|
909
|
-
* recommendations still add value (installed skills have 11.5% adoption vs 6.1% community).
|
|
919
|
+
* User-installed standalone skills (non-namespaced like "build-error-resolver")
|
|
920
|
+
* are always KEPT — contextual recommendations still add value.
|
|
910
921
|
*
|
|
911
922
|
* @param {object[]} results FTS5 results
|
|
912
|
-
* @returns {object[]} Filtered results
|
|
923
|
+
* @returns {object[]} Filtered results
|
|
913
924
|
*/
|
|
914
925
|
function filterAutoLoadedSkills(results) {
|
|
915
926
|
return results.filter(r => {
|
|
916
927
|
if (r.type !== 'skill') return true;
|
|
917
928
|
const inv = (r.invocation_name || '').trim();
|
|
918
929
|
if (inv === '') return true; // Community resource — always recommend
|
|
919
|
-
//
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
//
|
|
923
|
-
|
|
930
|
+
// Standalone installed skills (e.g. "build-error-resolver") — keep
|
|
931
|
+
if (!inv.includes(':')) return true;
|
|
932
|
+
// Plugin-namespaced: adoption-aware filter
|
|
933
|
+
// Cold start: keep if never recommended (no data to judge yet)
|
|
934
|
+
const recs = r.recommend_count || 0;
|
|
935
|
+
if (recs < 5) return true;
|
|
936
|
+
// Keep if adoption rate is healthy
|
|
937
|
+
const adopts = r.adopt_count || 0;
|
|
938
|
+
if (adopts >= AUTOLOADED_MIN_ADOPTIONS && (adopts + 1) / (recs + 2) >= AUTOLOADED_MIN_ADOPT_RATE) return true;
|
|
939
|
+
// Poor adoption history — suppress proactive recommendation
|
|
940
|
+
return false;
|
|
924
941
|
});
|
|
925
942
|
}
|
|
926
943
|
|
package/hook-update.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { execSync, execFileSync } from 'node:child_process';
|
|
6
6
|
import { readFileSync, writeFileSync, copyFileSync, readdirSync, existsSync, lstatSync, mkdirSync, rmSync, renameSync } from 'node:fs';
|
|
7
7
|
import { join, dirname } from 'node:path';
|
|
8
|
-
import { tmpdir } from 'node:os';
|
|
8
|
+
import { tmpdir, homedir } from 'node:os';
|
|
9
9
|
import { DB_DIR } from './schema.mjs';
|
|
10
10
|
import { debugCatch, debugLog } from './utils.mjs';
|
|
11
11
|
|
|
@@ -270,6 +270,20 @@ export function installExtractedRelease(sourceDir, targetDir = INSTALL_DIR) {
|
|
|
270
270
|
|
|
271
271
|
rmSync(stagingDir, { recursive: true, force: true });
|
|
272
272
|
rmSync(backupDir, { recursive: true, force: true });
|
|
273
|
+
|
|
274
|
+
// Post-update migration: clean stale global MCP if plugin handles it
|
|
275
|
+
try {
|
|
276
|
+
if (isPluginMode()) {
|
|
277
|
+
const claudeJsonPath = join(homedir(), '.claude.json');
|
|
278
|
+
const cfg = JSON.parse(readFileSync(claudeJsonPath, 'utf8'));
|
|
279
|
+
if (cfg.mcpServers?.mem) {
|
|
280
|
+
delete cfg.mcpServers.mem;
|
|
281
|
+
writeFileSync(claudeJsonPath, JSON.stringify(cfg, null, 2) + '\n');
|
|
282
|
+
debugLog('DEBUG', 'hook-update', 'Post-update: removed stale global MCP "mem"');
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} catch (e) { debugCatch(e, 'post-update-mcp-dedup'); }
|
|
286
|
+
|
|
273
287
|
debugLog('DEBUG', 'hook-update', `Auto-update: switched ${installed.length} paths`);
|
|
274
288
|
return true;
|
|
275
289
|
} catch (err) {
|
package/install.mjs
CHANGED
|
@@ -236,37 +236,57 @@ async function install() {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
// 3. Register MCP server
|
|
240
|
-
|
|
239
|
+
// 3. Register MCP server (skip if plugin system already handles it)
|
|
240
|
+
// Plugin system registers MCP from .claude-plugin/.mcp.json → mcp__plugin_claude-mem-lite_mem__*
|
|
241
|
+
// Global registration via `claude mcp add` creates a DUPLICATE mcp__mem__* server.
|
|
242
|
+
// Detect plugin mode: installed_plugins.json has our entry → plugin handles MCP.
|
|
243
|
+
const installedPluginsPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
244
|
+
let pluginHandlesMcp = false;
|
|
241
245
|
try {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
const installed = JSON.parse(readFileSync(installedPluginsPath, 'utf8'));
|
|
247
|
+
pluginHandlesMcp = !!installed?.plugins?.[PLUGIN_KEY]?.length;
|
|
248
|
+
} catch { /* not installed via plugin system */ }
|
|
249
|
+
|
|
250
|
+
if (pluginHandlesMcp) {
|
|
251
|
+
log('MCP server: plugin system handles registration (skipping global)');
|
|
252
|
+
// Clean up stale global registration if it exists (from older install.mjs versions)
|
|
253
|
+
try { execFileSync('claude', ['mcp', 'remove', '-s', 'user', 'mem'], { stdio: 'pipe' }); ok('Removed stale global MCP registration'); } catch {}
|
|
254
|
+
} else {
|
|
255
|
+
log('Registering MCP server...');
|
|
256
|
+
try {
|
|
257
|
+
try { execFileSync('claude', ['mcp', 'remove', '-s', 'user', 'mem'], { stdio: 'pipe' }); } catch {}
|
|
258
|
+
execFileSync('claude', ['mcp', 'add', '-s', 'user', '-t', 'stdio', 'mem', '--', 'node', SERVER_PATH], { stdio: 'pipe' });
|
|
259
|
+
try { execFileSync('claude', ['mcp', 'remove', '-s', 'project', 'mem'], { stdio: 'pipe' }); } catch {}
|
|
260
|
+
ok('MCP server registered: mem');
|
|
261
|
+
} catch (e) {
|
|
262
|
+
fail('MCP registration failed: ' + e.message);
|
|
263
|
+
warn('Try manually: claude mcp add -s user -t stdio mem -- node ' + SERVER_PATH);
|
|
264
|
+
}
|
|
251
265
|
}
|
|
252
266
|
|
|
253
267
|
// 3b. Deduplicate: if marketplace plugin also registers MCP + hooks,
|
|
254
268
|
// clear them to prevent double execution. install.mjs hooks (in settings.json)
|
|
255
269
|
// point to ~/.claude-mem-lite/ (latest code in dev mode via symlinks),
|
|
256
270
|
// while plugin hooks use ${CLAUDE_PLUGIN_ROOT} (potentially stale marketplace copy).
|
|
271
|
+
//
|
|
272
|
+
// MCP dedup: Claude Code loads .mcp.json from BOTH marketplace root (generic scan)
|
|
273
|
+
// and cache dir (plugin system). Root-level .mcp.json → duplicate mcp__mem__*.
|
|
274
|
+
// Fix: .mcp.json moved to .claude-plugin/ (plugin installer reads from there,
|
|
275
|
+
// generic scanner only scans root). Clear any stale root copy here.
|
|
257
276
|
const pluginDir = join(homedir(), '.claude', 'plugins', 'marketplaces', MARKETPLACE_KEY);
|
|
258
|
-
const pluginMcpPath = join(pluginDir, '.mcp.json');
|
|
259
277
|
const pluginHooksPath = join(pluginDir, 'hooks', 'hooks.json');
|
|
260
278
|
|
|
261
279
|
if (existsSync(pluginDir)) {
|
|
262
|
-
// Clear
|
|
280
|
+
// Clear root-level .mcp.json if it exists (stale from older git versions).
|
|
281
|
+
// .mcp.json is now in .claude-plugin/ to avoid generic scanner duplicate.
|
|
282
|
+
const rootMcpPath = join(pluginDir, '.mcp.json');
|
|
263
283
|
try {
|
|
264
|
-
if (existsSync(
|
|
265
|
-
const pluginMcp = JSON.parse(readFileSync(
|
|
284
|
+
if (existsSync(rootMcpPath)) {
|
|
285
|
+
const pluginMcp = JSON.parse(readFileSync(rootMcpPath, 'utf8'));
|
|
266
286
|
if (pluginMcp.mcpServers?.mem) {
|
|
267
287
|
delete pluginMcp.mcpServers.mem;
|
|
268
|
-
writeFileSync(
|
|
269
|
-
ok('Marketplace plugin:
|
|
288
|
+
writeFileSync(rootMcpPath, JSON.stringify(pluginMcp, null, 2) + '\n');
|
|
289
|
+
ok('Marketplace plugin: root .mcp.json cleared (moved to .claude-plugin/)');
|
|
270
290
|
}
|
|
271
291
|
}
|
|
272
292
|
} catch (e) { warn(`Marketplace MCP dedup: ${e.message}`); }
|
package/package.json
CHANGED
package/scripts/setup.sh
CHANGED
|
@@ -85,5 +85,49 @@ if [[ ! -d "$ROOT/node_modules/better-sqlite3" ]]; then
|
|
|
85
85
|
fi
|
|
86
86
|
fi
|
|
87
87
|
|
|
88
|
+
# 7. One-time MCP migration: clean stale registrations from pre-2.10 versions.
|
|
89
|
+
# Only runs once per version — skips if marker file exists.
|
|
90
|
+
# Before 2.10: .mcp.json at repo root caused duplicate MCP servers.
|
|
91
|
+
# - Global mcpServers.mem in ~/.claude.json (from old install.mjs)
|
|
92
|
+
# - Marketplace root .mcp.json (from old git clone)
|
|
93
|
+
# Now: .mcp.json in .claude-plugin/ → plugin system handles MCP exclusively.
|
|
94
|
+
MCP_MIGRATION="$DATA_DIR/runtime/.mcp-dedup-v2.10"
|
|
95
|
+
if [[ ! -f "$MCP_MIGRATION" && -n "${CLAUDE_PLUGIN_ROOT:-}" ]]; then
|
|
96
|
+
CLAUDE_JSON="$HOME/.claude.json" ROOT="$ROOT" node -e '
|
|
97
|
+
const fs = require("fs");
|
|
98
|
+
let changed = false;
|
|
99
|
+
// 1. Remove stale global MCP registration
|
|
100
|
+
try {
|
|
101
|
+
const p = process.env.CLAUDE_JSON;
|
|
102
|
+
const d = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
103
|
+
if (d.mcpServers?.mem) {
|
|
104
|
+
delete d.mcpServers.mem;
|
|
105
|
+
fs.writeFileSync(p, JSON.stringify(d, null, 2) + "\n");
|
|
106
|
+
process.stderr.write("✓ Removed stale global MCP \"mem\" (plugin handles it)\n");
|
|
107
|
+
changed = true;
|
|
108
|
+
}
|
|
109
|
+
} catch {}
|
|
110
|
+
// 2. Remove stale marketplace root .mcp.json
|
|
111
|
+
try {
|
|
112
|
+
const root = process.env.ROOT;
|
|
113
|
+
if (root.includes("/plugins/cache/")) {
|
|
114
|
+
const key = root.split("/plugins/cache/")[1].split("/")[0];
|
|
115
|
+
const mktMcp = require("path").join(require("os").homedir(), ".claude/plugins/marketplaces", key, ".mcp.json");
|
|
116
|
+
if (fs.existsSync(mktMcp)) {
|
|
117
|
+
const m = JSON.parse(fs.readFileSync(mktMcp, "utf8"));
|
|
118
|
+
if (m.mcpServers?.mem) {
|
|
119
|
+
delete m.mcpServers.mem;
|
|
120
|
+
fs.writeFileSync(mktMcp, JSON.stringify(m, null, 2) + "\n");
|
|
121
|
+
process.stderr.write("✓ Cleared marketplace root .mcp.json (moved to .claude-plugin/)\n");
|
|
122
|
+
changed = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch {}
|
|
127
|
+
if (!changed) process.stderr.write("✓ MCP migration: already clean\n");
|
|
128
|
+
' 2>&2 || true
|
|
129
|
+
touch "$MCP_MIGRATION"
|
|
130
|
+
fi
|
|
131
|
+
|
|
88
132
|
log_ok "claude-mem-lite ready"
|
|
89
133
|
exit 0
|
package/server.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { ensureDb, DB_PATH, REGISTRY_DB_PATH } from './schema.mjs';
|
|
|
9
9
|
import { reRankWithContext, markSuperseded, extractPRFTerms, expandQueryByConcepts, autoBoostIfNeeded, runIdleCleanup } from './server-internals.mjs';
|
|
10
10
|
import { memSearchSchema, memTimelineSchema, memGetSchema, memDeleteSchema, memSaveSchema, memStatsSchema, memCompressSchema, memMaintainSchema, memRegistrySchema } from './tool-schemas.mjs';
|
|
11
11
|
import { ensureRegistryDb, upsertResource } from './registry.mjs';
|
|
12
|
+
import { searchResources } from './registry-retriever.mjs';
|
|
12
13
|
import { createRequire } from 'module';
|
|
13
14
|
|
|
14
15
|
const require = createRequire(import.meta.url);
|
|
@@ -123,6 +124,13 @@ const server = new McpServer(
|
|
|
123
124
|
'SEARCH WORKFLOW: mem_search → mem_timeline(anchor=ID) for surrounding context → mem_get(ids=[...]) for full details.',
|
|
124
125
|
'Search tips: use short keywords (2-3 words), not full sentences. Filter with obs_type when relevant.',
|
|
125
126
|
'',
|
|
127
|
+
'SKILL/AGENT DISCOVERY (mem_registry):',
|
|
128
|
+
'- When your installed skills/agents don\'t cover the current workflow → mem_registry(action="search", query="<what you need>")',
|
|
129
|
+
'- When the user asks for a capability you lack → search the registry first',
|
|
130
|
+
'- When starting complex multi-step work → check if specialized agents exist: mem_registry(action="search", query="<task domain>", type="agent")',
|
|
131
|
+
'- Filter by type: type="skill" for workflow skills, type="agent" for specialized agents',
|
|
132
|
+
'- Say: "Searching mem for [purpose]..." when using this feature',
|
|
133
|
+
'',
|
|
126
134
|
'MAINTENANCE: mem_compress consolidates old observations. mem_maintain runs dedup/cleanup/reindex.',
|
|
127
135
|
].join('\n'),
|
|
128
136
|
},
|
|
@@ -1201,7 +1209,7 @@ server.registerTool(
|
|
|
1201
1209
|
server.registerTool(
|
|
1202
1210
|
'mem_registry',
|
|
1203
1211
|
{
|
|
1204
|
-
description: 'Manage tool resource registry: list resources, view stats, import/remove tools, reindex FTS5.',
|
|
1212
|
+
description: 'Manage tool resource registry: search for skills/agents by need, list resources, view stats, import/remove tools, reindex FTS5.',
|
|
1205
1213
|
inputSchema: memRegistrySchema,
|
|
1206
1214
|
},
|
|
1207
1215
|
safeHandler(async (args) => {
|
|
@@ -1212,6 +1220,26 @@ server.registerTool(
|
|
|
1212
1220
|
|
|
1213
1221
|
const action = args.action;
|
|
1214
1222
|
|
|
1223
|
+
if (action === 'search') {
|
|
1224
|
+
if (!args.query) {
|
|
1225
|
+
return { content: [{ type: 'text', text: 'search requires a query parameter' }], isError: true };
|
|
1226
|
+
}
|
|
1227
|
+
const results = searchResources(rdb, args.query, {
|
|
1228
|
+
type: args.type || undefined,
|
|
1229
|
+
limit: 5,
|
|
1230
|
+
});
|
|
1231
|
+
if (results.length === 0) {
|
|
1232
|
+
return { content: [{ type: 'text', text: `No matching resources for: "${args.query}"` }] };
|
|
1233
|
+
}
|
|
1234
|
+
const lines = results.map(r => {
|
|
1235
|
+
const howToUse = r.type === 'skill'
|
|
1236
|
+
? (r.invocation_name ? `Skill tool: skill="${r.invocation_name}"` : `Community skill: ${r.name}`)
|
|
1237
|
+
: `Agent tool: subagent_type="${r.name}"`;
|
|
1238
|
+
return `${r.type === 'skill' ? 'S' : 'A'} **${r.name}** — ${truncate(r.capability_summary || '', 80)}\n Use: ${howToUse}`;
|
|
1239
|
+
});
|
|
1240
|
+
return { content: [{ type: 'text', text: `Found ${results.length} resource(s) for "${args.query}":\n\n${lines.join('\n\n')}` }] };
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1215
1243
|
if (action === 'list') {
|
|
1216
1244
|
const typeFilter = args.type;
|
|
1217
1245
|
const where = typeFilter ? 'WHERE type = ? AND status = ?' : 'WHERE status = ?';
|
|
@@ -1287,7 +1315,7 @@ server.registerTool(
|
|
|
1287
1315
|
return { content: [{ type: 'text', text: `FTS5 reindexed. ${count.c} active resources.` }] };
|
|
1288
1316
|
}
|
|
1289
1317
|
|
|
1290
|
-
return { content: [{ type: 'text', text: `Unknown action: ${action}. Valid: list, stats, import, remove, reindex` }], isError: true };
|
|
1318
|
+
return { content: [{ type: 'text', text: `Unknown action: ${action}. Valid: search, list, stats, import, remove, reindex` }], isError: true };
|
|
1291
1319
|
})
|
|
1292
1320
|
);
|
|
1293
1321
|
|
package/tool-schemas.mjs
CHANGED
|
@@ -92,8 +92,9 @@ export const memMaintainSchema = {
|
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
export const memRegistrySchema = {
|
|
95
|
-
action: z.enum(['list', 'stats', 'import', 'remove', 'reindex']).describe('Registry operation'),
|
|
96
|
-
|
|
95
|
+
action: z.enum(['list', 'stats', 'search', 'import', 'remove', 'reindex']).describe('Registry operation'),
|
|
96
|
+
query: z.string().optional().describe('Search query — keywords describing what you need (for search)'),
|
|
97
|
+
type: z.enum(['skill', 'agent']).optional().describe('Filter by resource type (for list/search)'),
|
|
97
98
|
name: z.string().optional().describe('Resource name (for import/remove)'),
|
|
98
99
|
resource_type: z.enum(['skill', 'agent']).optional().describe('Resource type (for import/remove)'),
|
|
99
100
|
source: z.enum(['preinstalled', 'user']).optional().describe('Source (for import, default: user)'),
|
|
File without changes
|