claude-mem-lite 2.0.2 → 2.0.3
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/dispatch-feedback.mjs +57 -10
- package/dispatch-inject.mjs +5 -5
- package/dispatch.mjs +47 -8
- package/hook.mjs +2 -2
- package/install.mjs +894 -0
- package/package.json +1 -1
- package/registry/preinstalled.json +2180 -40
- package/registry-retriever.mjs +73 -5
- package/registry.mjs +16 -4
package/registry-retriever.mjs
CHANGED
|
@@ -247,6 +247,56 @@ export function buildQueryFromText(text) {
|
|
|
247
247
|
return expanded.join(' OR ');
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
// ─── Project Domain Filtering ─────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
// Platform/language tags that indicate a resource is technology-specific.
|
|
253
|
+
// Only resources with these tags AND no overlap with project domains get filtered out.
|
|
254
|
+
// Resources with only functional tags (testing, quality, review) always pass.
|
|
255
|
+
const TECHNOLOGY_TAGS = new Set([
|
|
256
|
+
'javascript', 'typescript', 'node', 'react', 'vue', 'svelte', 'angular',
|
|
257
|
+
'python', 'django', 'flask', 'fastapi',
|
|
258
|
+
'rust', 'go', 'java', 'kotlin', 'ruby', 'php', 'swift', 'dart', 'flutter',
|
|
259
|
+
'ios', 'macos', 'android',
|
|
260
|
+
'cpp', 'c', 'csharp', 'dotnet', 'aspnet',
|
|
261
|
+
'elixir', 'erlang', 'lua', 'zig', 'solidity',
|
|
262
|
+
'html', 'css', 'frontend', 'backend',
|
|
263
|
+
]);
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Post-filter FTS5 results by project domain overlap.
|
|
267
|
+
* - Resources with empty domain_tags (universal) always pass.
|
|
268
|
+
* - Resources with only functional tags (no technology-specific tags) always pass.
|
|
269
|
+
* - Resources with technology tags must overlap with project domains or tech_stack.
|
|
270
|
+
* @param {object[]} results FTS5 results
|
|
271
|
+
* @param {string[]} projectDomains Detected project domains
|
|
272
|
+
* @returns {object[]} Filtered results
|
|
273
|
+
*/
|
|
274
|
+
export function filterByProjectDomain(results, projectDomains) {
|
|
275
|
+
if (!projectDomains || projectDomains.length === 0) return results;
|
|
276
|
+
const domainSet = new Set(projectDomains.map(d => d.toLowerCase()));
|
|
277
|
+
return results.filter(r => {
|
|
278
|
+
// Universal: no domain_tags
|
|
279
|
+
if (!r.domain_tags || r.domain_tags.trim() === '') return true;
|
|
280
|
+
|
|
281
|
+
const tags = r.domain_tags.split(/[\s,]+/).map(t => t.trim().toLowerCase()).filter(Boolean);
|
|
282
|
+
|
|
283
|
+
// Check if any tag is a technology tag
|
|
284
|
+
const hasTechTag = tags.some(t => TECHNOLOGY_TAGS.has(t));
|
|
285
|
+
if (!hasTechTag) return true; // Only functional tags — always pass
|
|
286
|
+
|
|
287
|
+
// Has tech tags — check overlap with project domains
|
|
288
|
+
if (tags.some(t => domainSet.has(t))) return true;
|
|
289
|
+
|
|
290
|
+
// Also check tech_stack column (broader tech info)
|
|
291
|
+
if (r.tech_stack) {
|
|
292
|
+
const techTags = r.tech_stack.split(/[\s,]+/).map(t => t.trim().toLowerCase()).filter(Boolean);
|
|
293
|
+
if (techTags.some(t => domainSet.has(t))) return true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return false;
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
250
300
|
// ─── FTS5 Retrieval ──────────────────────────────────────────────────────────
|
|
251
301
|
|
|
252
302
|
// BM25 weights (8 columns, positional — must match FTS5 column order in registry.mjs):
|
|
@@ -327,30 +377,48 @@ const SEARCH_BY_TYPE_SQL = `
|
|
|
327
377
|
|
|
328
378
|
/**
|
|
329
379
|
* Search for resources using FTS5 with composite scoring.
|
|
380
|
+
* When projectDomains is provided, fetches extra results internally to allow
|
|
381
|
+
* headroom after domain filtering, then slices to requested limit.
|
|
330
382
|
* @param {Database} db Registry database
|
|
331
383
|
* @param {string} query FTS5 query string (already expanded)
|
|
332
384
|
* @param {object} [opts] Options
|
|
333
385
|
* @param {'skill'|'agent'} [opts.type] Filter by type
|
|
334
386
|
* @param {number} [opts.limit=3] Max results
|
|
387
|
+
* @param {string[]} [opts.projectDomains] Project domains for post-filtering
|
|
335
388
|
* @returns {object[]} Array of matching resources with relevance scores
|
|
336
389
|
*/
|
|
337
|
-
export function retrieveResources(db, query, { type, limit = 3 } = {}) {
|
|
390
|
+
export function retrieveResources(db, query, { type, limit = 3, projectDomains } = {}) {
|
|
338
391
|
if (!query) return [];
|
|
339
392
|
|
|
393
|
+
// Fetch extra when domain filtering is active to ensure enough results after filtering
|
|
394
|
+
const fetchLimit = (projectDomains && projectDomains.length > 0) ? Math.max(limit * 3, 10) : limit;
|
|
395
|
+
|
|
340
396
|
try {
|
|
397
|
+
let results;
|
|
341
398
|
if (type) {
|
|
342
|
-
|
|
399
|
+
results = db.prepare(SEARCH_BY_TYPE_SQL).all(query, type, fetchLimit);
|
|
400
|
+
} else {
|
|
401
|
+
results = db.prepare(SEARCH_SQL).all(query, fetchLimit);
|
|
343
402
|
}
|
|
344
|
-
|
|
403
|
+
if (projectDomains && projectDomains.length > 0) {
|
|
404
|
+
results = filterByProjectDomain(results, projectDomains);
|
|
405
|
+
}
|
|
406
|
+
return results.slice(0, limit);
|
|
345
407
|
} catch (e) {
|
|
346
408
|
// FTS5 query syntax error — try simpler query
|
|
347
409
|
debugCatch(e, 'retrieveResources');
|
|
348
410
|
try {
|
|
349
411
|
const simpleQuery = query.replace(/[()]/g, '').split(/\s+OR\s+/).slice(0, 3).join(' OR ');
|
|
412
|
+
let results;
|
|
350
413
|
if (type) {
|
|
351
|
-
|
|
414
|
+
results = db.prepare(SEARCH_BY_TYPE_SQL).all(simpleQuery, type, fetchLimit);
|
|
415
|
+
} else {
|
|
416
|
+
results = db.prepare(SEARCH_SQL).all(simpleQuery, fetchLimit);
|
|
417
|
+
}
|
|
418
|
+
if (projectDomains && projectDomains.length > 0) {
|
|
419
|
+
results = filterByProjectDomain(results, projectDomains);
|
|
352
420
|
}
|
|
353
|
-
return
|
|
421
|
+
return results.slice(0, limit);
|
|
354
422
|
} catch {
|
|
355
423
|
return [];
|
|
356
424
|
}
|
package/registry.mjs
CHANGED
|
@@ -21,6 +21,7 @@ const RESOURCES_SCHEMA = `
|
|
|
21
21
|
local_path TEXT NOT NULL,
|
|
22
22
|
file_hash TEXT,
|
|
23
23
|
parent_plugin TEXT,
|
|
24
|
+
invocation_name TEXT DEFAULT '',
|
|
24
25
|
intent_tags TEXT DEFAULT '',
|
|
25
26
|
domain_tags TEXT DEFAULT '',
|
|
26
27
|
action_type TEXT DEFAULT '',
|
|
@@ -154,6 +155,14 @@ export function ensureRegistryDb(dbPath) {
|
|
|
154
155
|
|
|
155
156
|
db.exec(RESOURCES_SCHEMA);
|
|
156
157
|
|
|
158
|
+
// Migrate: add invocation_name column if missing (safe for existing DBs)
|
|
159
|
+
try {
|
|
160
|
+
const cols = db.prepare("PRAGMA table_info(resources)").all();
|
|
161
|
+
if (!cols.some(c => c.name === 'invocation_name')) {
|
|
162
|
+
db.exec("ALTER TABLE resources ADD COLUMN invocation_name TEXT DEFAULT ''");
|
|
163
|
+
}
|
|
164
|
+
} catch {}
|
|
165
|
+
|
|
157
166
|
// FTS5 + triggers: only create if not exists
|
|
158
167
|
const hasFts = db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='resources_fts'`).get();
|
|
159
168
|
if (!hasFts) {
|
|
@@ -189,13 +198,14 @@ export function ensureRegistryDb(dbPath) {
|
|
|
189
198
|
|
|
190
199
|
const UPSERT_SQL = `
|
|
191
200
|
INSERT INTO resources (name, type, status, source, repo_url, repo_stars, local_path, file_hash,
|
|
192
|
-
intent_tags, domain_tags, action_type, trigger_patterns, capability_summary,
|
|
201
|
+
invocation_name, intent_tags, domain_tags, action_type, trigger_patterns, capability_summary,
|
|
193
202
|
input_type, output_type, prerequisites, keywords, tech_stack, use_cases, complexity,
|
|
194
203
|
indexed_at, updated_at)
|
|
195
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
204
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
196
205
|
ON CONFLICT(type, name) DO UPDATE SET
|
|
197
206
|
status=excluded.status, source=excluded.source, repo_url=excluded.repo_url,
|
|
198
207
|
repo_stars=excluded.repo_stars, local_path=excluded.local_path, file_hash=excluded.file_hash,
|
|
208
|
+
invocation_name=CASE WHEN excluded.invocation_name != '' THEN excluded.invocation_name ELSE invocation_name END,
|
|
199
209
|
intent_tags=excluded.intent_tags, domain_tags=excluded.domain_tags,
|
|
200
210
|
action_type=excluded.action_type, trigger_patterns=excluded.trigger_patterns,
|
|
201
211
|
capability_summary=excluded.capability_summary, input_type=excluded.input_type,
|
|
@@ -216,7 +226,8 @@ export function upsertResource(db, r) {
|
|
|
216
226
|
const result = db.prepare(UPSERT_SQL).run(
|
|
217
227
|
r.name, r.type, r.status || 'active', r.source || 'preinstalled',
|
|
218
228
|
r.repo_url || null, r.repo_stars || 0, r.local_path,
|
|
219
|
-
r.file_hash || null, r.
|
|
229
|
+
r.file_hash || null, r.invocation_name || '',
|
|
230
|
+
r.intent_tags || '', r.domain_tags || '',
|
|
220
231
|
r.action_type || '', r.trigger_patterns || '', r.capability_summary || '',
|
|
221
232
|
r.input_type || '', r.output_type || '', r.prerequisites || '{}',
|
|
222
233
|
r.keywords || '', r.tech_stack || '', r.use_cases || '', r.complexity || 'intermediate',
|
|
@@ -318,7 +329,8 @@ export function getResourceSuccessRates(db, days = 30) {
|
|
|
318
329
|
*/
|
|
319
330
|
export function getSessionInvocations(db, sessionId) {
|
|
320
331
|
return db.prepare(`
|
|
321
|
-
SELECT i.*, r.name as resource_name, r.type as resource_type
|
|
332
|
+
SELECT i.*, r.name as resource_name, r.type as resource_type,
|
|
333
|
+
r.invocation_name as invocation_name
|
|
322
334
|
FROM invocations i
|
|
323
335
|
JOIN resources r ON r.id = i.resource_id
|
|
324
336
|
WHERE i.session_id = ?
|