claude-mem-lite 2.12.0 → 2.12.2
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-feedback.mjs +2 -2
- package/dispatch-workflow.mjs +8 -8
- package/dispatch.mjs +4 -4
- package/hook-llm.mjs +18 -2
- package/hook-shared.mjs +0 -18
- package/hook.mjs +3 -1
- package/package.json +1 -1
- package/registry-indexer.mjs +1 -1
- package/registry-retriever.mjs +3 -1
- package/registry-scanner.mjs +5 -2
- package/server.mjs +8 -1
package/dispatch-feedback.mjs
CHANGED
|
@@ -157,7 +157,7 @@ function detectAdoption(invocation, sessionEvents) {
|
|
|
157
157
|
|
|
158
158
|
// Tier 2: Behavioral adoption — methodology patterns (10 min window)
|
|
159
159
|
const resourceLower = resource_name.toLowerCase();
|
|
160
|
-
const recTime = invocation.created_at ? new Date(invocation.created_at).getTime() :
|
|
160
|
+
const recTime = invocation.created_at ? new Date(invocation.created_at).getTime() : null;
|
|
161
161
|
|
|
162
162
|
// TDD pattern: Bash(test fail) → Edit → Bash(test pass)
|
|
163
163
|
if (resourceLower.includes('tdd') || resourceLower.includes('test-driven')) {
|
|
@@ -365,7 +365,7 @@ function autodemoteZombies(db) {
|
|
|
365
365
|
try {
|
|
366
366
|
const demoted = db.prepare(`
|
|
367
367
|
UPDATE resources SET recommendation_mode = 'on_request', updated_at = datetime('now')
|
|
368
|
-
WHERE COALESCE(recommend_count, 0) >
|
|
368
|
+
WHERE COALESCE(recommend_count, 0) > 8
|
|
369
369
|
AND (COALESCE(adopt_count, 0) + 1.0) / (COALESCE(recommend_count, 0) + 2.0) < 0.1
|
|
370
370
|
AND COALESCE(recommendation_mode, 'proactive') = 'proactive'
|
|
371
371
|
AND status = 'active'
|
package/dispatch-workflow.mjs
CHANGED
|
@@ -138,14 +138,14 @@ export function inferCurrentStage(primaryIntent, activeSuite, suppressedIntents
|
|
|
138
138
|
// ─── Explicit Request Detection ──────────────────────────────────────────────
|
|
139
139
|
|
|
140
140
|
const EXPLICIT_REQUEST_PATTERNS = [
|
|
141
|
-
// EN: "use the playwright skill", "try the
|
|
142
|
-
/(?:use|try|invoke|run|activate|load)\s+(?:the\s+)?(
|
|
143
|
-
// CN: "用ppt的技能", "帮我用
|
|
144
|
-
/(?:用|使用|帮我用|试试|启用)\s*(
|
|
145
|
-
// "有没有
|
|
146
|
-
/(?:有没有|有无|是否有|do you have|is there)\s*(?:一个|a|an)?\s*(
|
|
147
|
-
// "推荐一个
|
|
148
|
-
/(?:推荐|suggest|recommend)\s*(?:一个|a|an)?\s*(
|
|
141
|
+
// EN: "use the playwright skill", "try the code review skill" (multi-word support)
|
|
142
|
+
/(?:use|try|invoke|run|activate|load)\s+(?:the\s+)?(.+?)\s+(?:skill|agent|tool|plugin)\b/i,
|
|
143
|
+
// CN: "用ppt的技能", "帮我用code review的skill" (multi-word support)
|
|
144
|
+
/(?:用|使用|帮我用|试试|启用)\s*(.+?)\s*(?:的技能|的skill|的agent|的工具|的插件|技能|skill|agent|工具|插件)/,
|
|
145
|
+
// "有没有code review的skill", "is there a code review agent"
|
|
146
|
+
/(?:有没有|有无|是否有|do you have|is there)\s*(?:一个|a|an)?\s*(.+?)\s*(?:的|skill|agent|技能|工具)/i,
|
|
147
|
+
// "推荐一个code review的", "recommend a testing agent"
|
|
148
|
+
/(?:推荐|suggest|recommend)\s*(?:一个|a|an)?\s*(.+?)\s*(?:的|skill|agent|技能|工具)/i,
|
|
149
149
|
];
|
|
150
150
|
|
|
151
151
|
/**
|
package/dispatch.mjs
CHANGED
|
@@ -1114,16 +1114,16 @@ export async function dispatchOnUserPrompt(db, userPrompt, sessionId) {
|
|
|
1114
1114
|
const textQuery = buildQueryFromText(explicit.searchTerm);
|
|
1115
1115
|
if (!textQuery) return null;
|
|
1116
1116
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1117
|
+
// Explicit requests bypass project domain filtering — if the user asks
|
|
1118
|
+
// "use the playwright skill", don't filter by project tech stack
|
|
1119
|
+
let results = retrieveResources(db, textQuery, { limit: 3 });
|
|
1119
1120
|
results = filterGarbageMetadata(results);
|
|
1120
1121
|
// Community resources without invocation_name can't be easily invoked
|
|
1121
1122
|
results = results.filter(r => r.quality_tier !== 'community' || r.invocation_name);
|
|
1122
|
-
|
|
1123
|
+
// Explicit requests skip adoption decay and cooldown — user asked for this
|
|
1123
1124
|
if (results.length === 0) return null;
|
|
1124
1125
|
|
|
1125
1126
|
const best = results[0];
|
|
1126
|
-
if (sessionId && isRecentlyRecommended(db, best.id, sessionId)) return null;
|
|
1127
1127
|
|
|
1128
1128
|
recordInvocation(db, { resource_id: best.id, session_id: sessionId, trigger: 'user_prompt', tier: 1, recommended: 1 });
|
|
1129
1129
|
updateResourceStats(db, best.id, 'recommend_count');
|
package/hook-llm.mjs
CHANGED
|
@@ -275,6 +275,22 @@ export function buildImmediateObservation(episode) {
|
|
|
275
275
|
importance = ruleImportance;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
// Separate files_modified (from Edit/Write tools) from files_read (everything else)
|
|
279
|
+
const modifiedFiles = new Set();
|
|
280
|
+
const searchedFiles = new Set();
|
|
281
|
+
for (const entry of episode.entries) {
|
|
282
|
+
if (!entry.files) continue;
|
|
283
|
+
if (EDIT_TOOLS.has(entry.tool)) {
|
|
284
|
+
for (const f of entry.files) modifiedFiles.add(f);
|
|
285
|
+
} else {
|
|
286
|
+
for (const f of entry.files) searchedFiles.add(f);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Merge bash-tracked reads and search tool files into filesRead
|
|
290
|
+
const allReads = new Set([...(episode.filesRead || []), ...searchedFiles]);
|
|
291
|
+
// Remove files that were both searched AND modified — they're modified
|
|
292
|
+
for (const f of modifiedFiles) allReads.delete(f);
|
|
293
|
+
|
|
278
294
|
return {
|
|
279
295
|
type: inferredType,
|
|
280
296
|
title,
|
|
@@ -282,8 +298,8 @@ export function buildImmediateObservation(episode) {
|
|
|
282
298
|
narrative: episode.entries.map(e => e.desc).join('; '),
|
|
283
299
|
concepts: [],
|
|
284
300
|
facts: [],
|
|
285
|
-
files:
|
|
286
|
-
filesRead:
|
|
301
|
+
files: [...modifiedFiles],
|
|
302
|
+
filesRead: [...allReads],
|
|
287
303
|
importance,
|
|
288
304
|
};
|
|
289
305
|
}
|
package/hook-shared.mjs
CHANGED
|
@@ -143,24 +143,6 @@ export function hasInjectionBudget() { return _injectionCount < MAX_INJECTIONS_P
|
|
|
143
143
|
// ─── Previous Session Context (for user-prompt dispatch enrichment) ──────────
|
|
144
144
|
// Session-start caches next_steps; first user-prompt reads+clears for richer dispatch.
|
|
145
145
|
|
|
146
|
-
export function prevContextFile() {
|
|
147
|
-
return join(RUNTIME_DIR, `prev-context-${inferProject()}`);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function cachePrevContext(nextSteps) {
|
|
151
|
-
try { writeFileSync(prevContextFile(), JSON.stringify({ nextSteps, ts: Date.now() })); } catch {}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function readAndClearPrevContext() {
|
|
155
|
-
const file = prevContextFile();
|
|
156
|
-
try {
|
|
157
|
-
const data = JSON.parse(readFileSync(file, 'utf8'));
|
|
158
|
-
try { unlinkSync(file); } catch {}
|
|
159
|
-
if (Date.now() - data.ts > 12 * 3600000) return null; // 12h expiry
|
|
160
|
-
return data.nextSteps || null;
|
|
161
|
-
} catch { return null; }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
146
|
// ─── Tool Event Tracking (for dispatch feedback) ────────────────────────────
|
|
165
147
|
// PostToolUse appends feedback-relevant tool events (Skill, Task, Edit, Write, Bash errors).
|
|
166
148
|
// Stop handler reads them and passes to collectFeedback for adoption/outcome detection.
|
package/hook.mjs
CHANGED
|
@@ -759,7 +759,6 @@ async function handleSessionStart() {
|
|
|
759
759
|
// CLAUDE.md: slim (summary + handoff state — observations already in stdout)
|
|
760
760
|
updateClaudeMd([...summaryLines, ...handoffLines].join('\n'));
|
|
761
761
|
|
|
762
|
-
// Cache previous session context for user-prompt dispatch enrichment.
|
|
763
762
|
// Background rescan: detect changed/new managed resources since last scan.
|
|
764
763
|
// TTL-based (1h) — avoids redundant filesystem scans on every session.
|
|
765
764
|
// Non-blocking: spawns detached worker, results available before first user prompt.
|
|
@@ -842,6 +841,9 @@ async function handleUserPrompt() {
|
|
|
842
841
|
const promptText = hookData.prompt || hookData.user_prompt;
|
|
843
842
|
if (!promptText || typeof promptText !== 'string') return;
|
|
844
843
|
|
|
844
|
+
// Skip internal Claude Code protocol messages — not real user input
|
|
845
|
+
if (promptText.startsWith('<task-notification>')) return;
|
|
846
|
+
|
|
845
847
|
const sessionId = getSessionId();
|
|
846
848
|
const db = openDb();
|
|
847
849
|
if (!db) return;
|
package/package.json
CHANGED
package/registry-indexer.mjs
CHANGED
|
@@ -83,7 +83,7 @@ function fallbackExtract(resource) {
|
|
|
83
83
|
|
|
84
84
|
return {
|
|
85
85
|
intent_tags: intentTags || resource.type,
|
|
86
|
-
domain_tags: domainTags || '
|
|
86
|
+
domain_tags: domainTags || '',
|
|
87
87
|
action_type: resource.type === 'agent' ? 'analyze' : 'generate',
|
|
88
88
|
trigger_patterns: `when user needs ${name.replace(/-/g, ' ')} functionality`,
|
|
89
89
|
capability_summary: truncate(`${resource.type}: ${name.replace(/-/g, ' ')}`, 100),
|
package/registry-retriever.mjs
CHANGED
|
@@ -10,6 +10,8 @@ export const DISPATCH_SYNONYMS = {
|
|
|
10
10
|
'clean': ['refactor', 'lint', 'format', 'organize', 'tidy', 'simplify', 'restructure', 'rewrite', 'smell', 'debt'],
|
|
11
11
|
'test': ['testing', 'unittest', 'e2e', 'coverage', 'tdd', 'qa', 'spec', 'jest', 'vitest', 'pytest', 'mocha', 'cypress', 'playwright'],
|
|
12
12
|
'fix': ['debug', 'bugfix', 'troubleshoot', 'diagnose', 'repair', 'error', 'crash', 'broken', 'issue', 'problem'],
|
|
13
|
+
'debug': ['debugging', 'fix', 'bugfix', 'troubleshoot', 'diagnose', 'error', 'crash', 'bug', 'breakpoint'],
|
|
14
|
+
'debugging':['debug', 'fix', 'bugfix', 'troubleshoot', 'diagnose', 'error', 'crash', 'bug', 'systematic'],
|
|
13
15
|
'fast': ['performance', 'optimize', 'profile', 'benchmark', 'speed', 'latency', 'bottleneck', 'slow', 'cache'],
|
|
14
16
|
'deploy': ['release', 'publish', 'ci', 'cd', 'ship', 'rollout', 'staging', 'production'],
|
|
15
17
|
'commit': ['git', 'push', 'merge', 'pr', 'branch', 'version', 'rebase', 'stash', 'tag'],
|
|
@@ -244,7 +246,7 @@ const TEXT_QUERY_STOP_WORDS = new Set([
|
|
|
244
246
|
export function buildQueryFromText(text) {
|
|
245
247
|
if (!text || typeof text !== 'string') return null;
|
|
246
248
|
|
|
247
|
-
const cleaned = text.replace(/[{}()[\]^~*:@#$%&]/g, ' ').trim();
|
|
249
|
+
const cleaned = text.replace(/[{}()[\]^~*:@#$%&"\\]/g, ' ').trim();
|
|
248
250
|
|
|
249
251
|
// Extract CJK compound words before whitespace split (Chinese has no spaces)
|
|
250
252
|
const cjkTokens = extractCJKTokens(cleaned);
|
package/registry-scanner.mjs
CHANGED
|
@@ -220,7 +220,7 @@ export function scanAllResources(config = {}) {
|
|
|
220
220
|
*/
|
|
221
221
|
export function diffResources(db, scanned) {
|
|
222
222
|
const existing = new Map();
|
|
223
|
-
const rows = db.prepare('SELECT id, type, name, file_hash, status FROM resources').all();
|
|
223
|
+
const rows = db.prepare('SELECT id, type, name, file_hash, local_path, status FROM resources').all();
|
|
224
224
|
for (const r of rows) existing.set(`${r.type}:${r.name}`, r);
|
|
225
225
|
|
|
226
226
|
const toIndex = [];
|
|
@@ -242,9 +242,12 @@ export function diffResources(db, scanned) {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
// Resources in DB but not on filesystem → disable
|
|
245
|
+
// Only disable resources that have a local_path (filesystem-backed).
|
|
246
|
+
// Resources without local_path were imported via metadata/registry and
|
|
247
|
+
// cannot be validated by filesystem scan.
|
|
245
248
|
const toDisable = [];
|
|
246
249
|
for (const [key, row] of existing) {
|
|
247
|
-
if (!scannedKeys.has(key) && row.status === 'active') {
|
|
250
|
+
if (!scannedKeys.has(key) && row.status === 'active' && row.local_path) {
|
|
248
251
|
toDisable.push(row);
|
|
249
252
|
}
|
|
250
253
|
}
|
package/server.mjs
CHANGED
|
@@ -1272,11 +1272,18 @@ server.registerTool(
|
|
|
1272
1272
|
}
|
|
1273
1273
|
let results = searchResources(rdb, args.query, {
|
|
1274
1274
|
type: args.type || undefined,
|
|
1275
|
-
limit: args.category || args.quality ? 20 :
|
|
1275
|
+
limit: args.category || args.quality ? 20 : 10, // fetch more for post-filtering
|
|
1276
1276
|
});
|
|
1277
1277
|
// Apply category/quality filters if provided
|
|
1278
1278
|
if (args.category) results = results.filter(r => r.category === args.category);
|
|
1279
1279
|
if (args.quality) results = results.filter(r => r.quality_tier === args.quality);
|
|
1280
|
+
// Prioritize directly invocable resources (with invocation_name) over community resources
|
|
1281
|
+
results.sort((a, b) => {
|
|
1282
|
+
const aInvocable = a.invocation_name ? 1 : 0;
|
|
1283
|
+
const bInvocable = b.invocation_name ? 1 : 0;
|
|
1284
|
+
if (aInvocable !== bInvocable) return bInvocable - aInvocable;
|
|
1285
|
+
return 0; // preserve FTS5 ranking within same tier
|
|
1286
|
+
});
|
|
1280
1287
|
results = results.slice(0, 5);
|
|
1281
1288
|
if (results.length === 0) {
|
|
1282
1289
|
return { content: [{ type: 'text', text: `No matching resources for: "${args.query}"` }] };
|