claude-mem-lite 2.32.6 → 2.32.8
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/.mcp.json +0 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/README.zh-CN.md +0 -0
- package/adopt-cli.mjs +0 -0
- package/adopt-content.mjs +0 -0
- package/bash-utils.mjs +0 -0
- package/commands/adopt.md +0 -0
- package/commands/bug.md +0 -0
- package/commands/lesson.md +0 -0
- package/commands/mem.md +0 -0
- package/commands/memory.md +0 -0
- package/commands/tools.md +0 -0
- package/commands/unadopt.md +0 -0
- package/commands/update.md +0 -0
- package/format-utils.mjs +0 -0
- package/haiku-client.mjs +0 -0
- package/hash-utils.mjs +0 -0
- package/hook-context.mjs +0 -0
- package/hook-episode.mjs +0 -0
- package/hook-handoff.mjs +30 -18
- package/hook-llm.mjs +0 -0
- package/hook-memory.mjs +0 -0
- package/hook-optimize.mjs +0 -0
- package/hook-semaphore.mjs +0 -0
- package/hook-shared.mjs +1 -0
- package/hook-update.mjs +0 -0
- package/hook.mjs +6 -4
- package/hooks/hooks.json +0 -0
- package/install-metadata.mjs +0 -0
- package/install.mjs +0 -0
- package/lib/activity.mjs +0 -0
- package/lib/doctor-benchmark.mjs +0 -0
- package/lib/git-state.mjs +0 -0
- package/lib/plan-reader.mjs +0 -0
- package/lib/startup-dashboard.mjs +0 -0
- package/lib/task-reader.mjs +0 -0
- package/mem-cli.mjs +0 -0
- package/memdir.mjs +0 -0
- package/nlp.mjs +0 -0
- package/package.json +1 -1
- package/plugin-cache-guard.mjs +0 -0
- package/project-utils.mjs +0 -0
- package/registry/preinstalled.json +0 -0
- package/registry-enricher.mjs +0 -0
- package/registry-github.mjs +0 -0
- package/registry-importer.mjs +0 -0
- package/registry-indexer.mjs +0 -0
- package/registry-retriever.mjs +0 -0
- package/registry-scanner.mjs +0 -0
- package/registry.mjs +0 -0
- package/resource-discovery.mjs +0 -0
- package/schema.mjs +0 -0
- package/scoring-sql.mjs +0 -0
- package/scripts/launch.mjs +0 -0
- package/scripts/pre-skill-bridge.js +0 -0
- package/scripts/pre-tool-recall.js +0 -0
- package/scripts/prompt-search-utils.mjs +40 -2
- package/scripts/user-prompt-search.js +19 -1
- package/secret-scrub.mjs +0 -0
- package/server-internals.mjs +0 -0
- package/server.mjs +0 -0
- package/skill.md +0 -0
- package/skip-tools.mjs +0 -0
- package/source-files.mjs +0 -0
- package/stop-words.mjs +0 -0
- package/synonyms.mjs +0 -0
- package/tfidf.mjs +0 -0
- package/tier.mjs +0 -0
- package/tool-schemas.mjs +0 -0
- package/utils.mjs +0 -0
package/.mcp.json
CHANGED
|
File without changes
|
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
package/README.zh-CN.md
CHANGED
|
File without changes
|
package/adopt-cli.mjs
CHANGED
|
File without changes
|
package/adopt-content.mjs
CHANGED
|
File without changes
|
package/bash-utils.mjs
CHANGED
|
File without changes
|
package/commands/adopt.md
CHANGED
|
File without changes
|
package/commands/bug.md
CHANGED
|
File without changes
|
package/commands/lesson.md
CHANGED
|
File without changes
|
package/commands/mem.md
CHANGED
|
File without changes
|
package/commands/memory.md
CHANGED
|
File without changes
|
package/commands/tools.md
CHANGED
|
File without changes
|
package/commands/unadopt.md
CHANGED
|
File without changes
|
package/commands/update.md
CHANGED
|
File without changes
|
package/format-utils.mjs
CHANGED
|
File without changes
|
package/haiku-client.mjs
CHANGED
|
File without changes
|
package/hash-utils.mjs
CHANGED
|
File without changes
|
package/hook-context.mjs
CHANGED
|
File without changes
|
package/hook-episode.mjs
CHANGED
|
File without changes
|
package/hook-handoff.mjs
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import { basename } from 'path';
|
|
5
5
|
import { truncate, extractMatchKeywords, tokenizeHandoff, isSpecificTerm, LOW_SIGNAL_TITLE } from './utils.mjs';
|
|
6
6
|
import {
|
|
7
|
-
HANDOFF_EXPIRY_CLEAR, HANDOFF_EXPIRY_EXIT,
|
|
7
|
+
HANDOFF_EXPIRY_CLEAR, HANDOFF_EXPIRY_EXIT, HANDOFF_ANCHOR_MAX_AGE,
|
|
8
|
+
HANDOFF_MATCH_THRESHOLD, CONTINUE_KEYWORDS,
|
|
8
9
|
} from './hook-shared.mjs';
|
|
9
10
|
// T10d: import the whole module (not a named export) so tests can spy on
|
|
10
11
|
// gitStateModule.readGitState via vi.spyOn. Named-import bindings are
|
|
@@ -169,23 +170,23 @@ export function detectContinuationIntent(db, promptText, project, currentCcSessi
|
|
|
169
170
|
if (!promptText || typeof promptText !== 'string') return false;
|
|
170
171
|
if (promptText.trim().length < 2) return false;
|
|
171
172
|
|
|
172
|
-
// T10d Stage -1: Git-commit anchor —
|
|
173
|
-
// git_sha_at_handoff
|
|
174
|
-
// since that handoff, so assume continuation regardless of time / prompt.
|
|
173
|
+
// T10d Stage -1: Git-commit anchor — current HEAD == a stored
|
|
174
|
+
// git_sha_at_handoff ⇒ working tree hasn't moved since the handoff.
|
|
175
175
|
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
// does not apply here). This is an MVP choice — see plan 10d concern.
|
|
176
|
+
// Age cap (HANDOFF_ANCHOR_MAX_AGE = 72h) prevents stale HEAD from
|
|
177
|
+
// auto-continuing weeks-old context. For older anchors, the rest of the
|
|
178
|
+
// pipeline (Stage 0/1/2) still evaluates normally.
|
|
180
179
|
try {
|
|
181
180
|
const currentSha = gitStateModule.readGitState({ cwd: process.cwd() }).headSha;
|
|
182
181
|
if (currentSha) {
|
|
183
182
|
const anchor = db.prepare(`
|
|
184
|
-
SELECT
|
|
183
|
+
SELECT created_at_epoch FROM session_handoffs
|
|
185
184
|
WHERE project = ? AND git_sha_at_handoff = ?
|
|
186
185
|
ORDER BY created_at_epoch DESC LIMIT 1
|
|
187
186
|
`).get(project, currentSha);
|
|
188
|
-
if (anchor)
|
|
187
|
+
if (anchor && (Date.now() - anchor.created_at_epoch <= HANDOFF_ANCHOR_MAX_AGE)) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
189
190
|
}
|
|
190
191
|
} catch { /* git/DB failure must not break the rest of the pipeline */ }
|
|
191
192
|
|
|
@@ -204,14 +205,25 @@ export function detectContinuationIntent(db, promptText, project, currentCcSessi
|
|
|
204
205
|
`).get(project);
|
|
205
206
|
|
|
206
207
|
if (clearHandoff && (Date.now() - clearHandoff.created_at_epoch <= HANDOFF_EXPIRY_CLEAR)) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
const pTokens = tokenizeHandoff(promptText);
|
|
209
|
+
const hTokens = clearHandoff.match_keywords
|
|
210
|
+
? new Set(tokenizeHandoff(clearHandoff.match_keywords))
|
|
211
|
+
: null;
|
|
212
|
+
const hasOverlap = hTokens && pTokens.some(t => hTokens.has(t));
|
|
213
|
+
if (promptText.length < 40) {
|
|
214
|
+
// Short prompts: session-scoped clear = same user/context, auto-continue.
|
|
215
|
+
// Unscoped (legacy / no session_id in hook input) requires an explicit
|
|
216
|
+
// continuation keyword or keyword overlap to avoid cross-session noise.
|
|
217
|
+
if (currentCcSessionId) return true;
|
|
218
|
+
if (CONTINUE_KEYWORDS.test(promptText)) return true;
|
|
219
|
+
if (hasOverlap) return true;
|
|
220
|
+
// Fall through
|
|
221
|
+
} else {
|
|
222
|
+
// Long prompts: check keyword overlap to confirm same-task intent
|
|
223
|
+
if (!clearHandoff.match_keywords) return true; // no keywords stored, can't verify
|
|
224
|
+
if (hasOverlap) return true;
|
|
225
|
+
// Long prompt with zero keyword overlap → likely new task, fall through
|
|
226
|
+
}
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
// Stage 1: Explicit keyword match — always works, even without handoff
|
package/hook-llm.mjs
CHANGED
|
File without changes
|
package/hook-memory.mjs
CHANGED
|
File without changes
|
package/hook-optimize.mjs
CHANGED
|
File without changes
|
package/hook-semaphore.mjs
CHANGED
|
File without changes
|
package/hook-shared.mjs
CHANGED
|
@@ -58,6 +58,7 @@ export function effectiveQuiet(cwd) {
|
|
|
58
58
|
// Handoff system constants
|
|
59
59
|
export const HANDOFF_EXPIRY_CLEAR = 6 * 3600000; // 6 hours (covers lunch/meeting breaks)
|
|
60
60
|
export const HANDOFF_EXPIRY_EXIT = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
61
|
+
export const HANDOFF_ANCHOR_MAX_AGE = 72 * 3600000; // 72h cap on git_sha anchor — avoids stale-HEAD false positives
|
|
61
62
|
export const HANDOFF_MATCH_THRESHOLD = 3; // min weighted score
|
|
62
63
|
export const CONTINUE_KEYWORDS = /继续|接着|上次|之前的|前面的|刚才|\bcontinue\b|\bresume\b|\bwhere[\s-]+we[\s-]+left\b|\bpick[\s-]+up\b|\bcarry[\s-]+on\b/i;
|
|
63
64
|
|
package/hook-update.mjs
CHANGED
|
File without changes
|
package/hook.mjs
CHANGED
|
@@ -857,13 +857,15 @@ async function handleUserPrompt() {
|
|
|
857
857
|
const injection = renderHandoffInjection(db, project, ccSessionId);
|
|
858
858
|
if (injection) {
|
|
859
859
|
process.stdout.write(injection + '\n');
|
|
860
|
-
// Consume
|
|
861
|
-
//
|
|
860
|
+
// Consume handoff after injection to prevent re-injection on later prompts
|
|
861
|
+
// (within-session on prompts 2-3, or across new sessions for exit handoffs).
|
|
862
|
+
// clear: scoped to THIS session (parallel sessions keep their own rows).
|
|
863
|
+
// exit: unscoped — any exit handoff in this project is fair game once resumed.
|
|
862
864
|
try {
|
|
863
865
|
if (ccSessionId) {
|
|
864
|
-
db.prepare("DELETE FROM session_handoffs WHERE project = ? AND type = 'clear' AND session_id = ?").run(project, ccSessionId);
|
|
866
|
+
db.prepare("DELETE FROM session_handoffs WHERE project = ? AND ((type = 'clear' AND session_id = ?) OR type = 'exit')").run(project, ccSessionId);
|
|
865
867
|
} else {
|
|
866
|
-
db.prepare("DELETE FROM session_handoffs WHERE project = ? AND type
|
|
868
|
+
db.prepare("DELETE FROM session_handoffs WHERE project = ? AND type IN ('clear','exit')").run(project);
|
|
867
869
|
}
|
|
868
870
|
} catch {}
|
|
869
871
|
}
|
package/hooks/hooks.json
CHANGED
|
File without changes
|
package/install-metadata.mjs
CHANGED
|
File without changes
|
package/install.mjs
CHANGED
|
File without changes
|
package/lib/activity.mjs
CHANGED
|
File without changes
|
package/lib/doctor-benchmark.mjs
CHANGED
|
File without changes
|
package/lib/git-state.mjs
CHANGED
|
File without changes
|
package/lib/plan-reader.mjs
CHANGED
|
File without changes
|
|
File without changes
|
package/lib/task-reader.mjs
CHANGED
|
File without changes
|
package/mem-cli.mjs
CHANGED
|
File without changes
|
package/memdir.mjs
CHANGED
|
File without changes
|
package/nlp.mjs
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/plugin-cache-guard.mjs
CHANGED
|
File without changes
|
package/project-utils.mjs
CHANGED
|
File without changes
|
|
File without changes
|
package/registry-enricher.mjs
CHANGED
|
File without changes
|
package/registry-github.mjs
CHANGED
|
File without changes
|
package/registry-importer.mjs
CHANGED
|
File without changes
|
package/registry-indexer.mjs
CHANGED
|
File without changes
|
package/registry-retriever.mjs
CHANGED
|
File without changes
|
package/registry-scanner.mjs
CHANGED
|
File without changes
|
package/registry.mjs
CHANGED
|
File without changes
|
package/resource-discovery.mjs
CHANGED
|
File without changes
|
package/schema.mjs
CHANGED
|
File without changes
|
package/scoring-sql.mjs
CHANGED
|
File without changes
|
package/scripts/launch.mjs
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -49,8 +49,8 @@ export const INTENTS = [
|
|
|
49
49
|
// CJK: 开发/编写/创建/构建/做一个/写一个 from real prompts
|
|
50
50
|
{ pattern: /implement|feature\b|add\s+(?:a\s+)?new|实现|添加|新功能|新增|开发|编写|创建|构建|做一个|加一个|写一个/i, type: null, limit: 3 },
|
|
51
51
|
// Recall/history intent (catch-all temporal, lowest priority)
|
|
52
|
-
// CJK: 刚才/历史/回顾 from real prompts
|
|
53
|
-
{ pattern: /before|previously|last time|remember
|
|
52
|
+
// CJK: 刚才/历史/回顾 from real prompts; 碰到过|遇到过|见过|同样的问题 from spoken CN
|
|
53
|
+
{ pattern: /before|previously|last time|remember|seen this|same\s+issue|之前|上次|以前|记得|刚才|历史|回顾|碰到过|遇到过|见过|同样的问题|类似的问题/i, type: null, limit: 5, useRecent: true },
|
|
54
54
|
];
|
|
55
55
|
|
|
56
56
|
export function detectIntent(text) {
|
|
@@ -75,6 +75,44 @@ export function detectIntent(text) {
|
|
|
75
75
|
return first;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// ─── Error Signature Extraction ─────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extract a canonical error signature from prompt text.
|
|
82
|
+
*
|
|
83
|
+
* Matches named exception/error classes like:
|
|
84
|
+
* - "TypeError: Cannot read properties of undefined (reading 'foo')"
|
|
85
|
+
* - "Error [ERR_MODULE_NOT_FOUND]: module X not found"
|
|
86
|
+
* - "AssertionError: expected 'a' to equal 'b'"
|
|
87
|
+
* - "ValueError: invalid literal for int()"
|
|
88
|
+
* - "thread 'main' panicked at ..." (Rust) → captured via Panic class
|
|
89
|
+
*
|
|
90
|
+
* Intentionally skips bare "Error: ..." without a typed class, and skips
|
|
91
|
+
* lowercase matches — those carry too little signal vs. the intent-based
|
|
92
|
+
* FTS path which already catches them.
|
|
93
|
+
*
|
|
94
|
+
* Returns { className, errorCode, message, signature } or null.
|
|
95
|
+
* `signature` is suitable for direct FTS5 search (sanitizeFtsQuery applies).
|
|
96
|
+
*/
|
|
97
|
+
export function extractErrorSignature(text) {
|
|
98
|
+
if (!text || typeof text !== 'string') return null;
|
|
99
|
+
// Pass 1: typed class — "<CapCase>(Error|Exception|Panic)" with optional [ERR_CODE]
|
|
100
|
+
const TYPED_RE = /\b([A-Z][A-Za-z0-9]+(?:Error|Exception|Panic))(?:\s*\[([A-Z_][A-Z0-9_]*)\])?\s*:\s*([^\n]{3,200})/;
|
|
101
|
+
// Pass 2: bare "Error|Exception|Panic" followed by required [ERR_CODE] (Node idiom).
|
|
102
|
+
// Bare class without a code is skipped — too noisy; intent-based path catches those.
|
|
103
|
+
const BARE_CODED_RE = /\b(Error|Exception|Panic)\s*\[([A-Z_][A-Z0-9_]*)\]\s*:\s*([^\n]{3,200})/;
|
|
104
|
+
const m = text.match(TYPED_RE) || text.match(BARE_CODED_RE);
|
|
105
|
+
if (!m) return null;
|
|
106
|
+
const className = m[1];
|
|
107
|
+
const errorCode = m[2] || null;
|
|
108
|
+
const message = m[3].trim().replace(/\s+/g, ' ').replace(/[`'"]+$/, '');
|
|
109
|
+
const sigMsg = message.slice(0, 80);
|
|
110
|
+
const signature = errorCode
|
|
111
|
+
? `${className} ${errorCode} ${sigMsg}`
|
|
112
|
+
: `${className} ${sigMsg}`;
|
|
113
|
+
return { className, errorCode, message, signature };
|
|
114
|
+
}
|
|
115
|
+
|
|
78
116
|
// ─── Result Dedup ───────────────────────────────────────────────────────────
|
|
79
117
|
|
|
80
118
|
export const MAX_SESSION_INJECTIONS = 15;
|
|
@@ -8,7 +8,7 @@ import { sanitizeFtsQuery, relaxFtsQueryToOr, truncate, typeIcon, inferProject,
|
|
|
8
8
|
import { writeFileSync, readFileSync, existsSync, renameSync } from 'fs';
|
|
9
9
|
import { join } from 'path';
|
|
10
10
|
import Database from 'better-sqlite3';
|
|
11
|
-
import { shouldSkip, detectIntent, shouldSkipByDedup, extractFiles, DEDUP_STALE_MS, matchRegistrySkillName } from './prompt-search-utils.mjs';
|
|
11
|
+
import { shouldSkip, detectIntent, shouldSkipByDedup, extractFiles, extractErrorSignature, DEDUP_STALE_MS, matchRegistrySkillName } from './prompt-search-utils.mjs';
|
|
12
12
|
|
|
13
13
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
14
14
|
|
|
@@ -267,6 +267,18 @@ async function main() {
|
|
|
267
267
|
const intent = detectIntent(promptText);
|
|
268
268
|
let rows = [];
|
|
269
269
|
|
|
270
|
+
// A (v2.32.8): precision pass for named errors. When the prompt contains
|
|
271
|
+
// a typed exception signature (TypeError/ValueError/ReferenceError/...),
|
|
272
|
+
// seed results with exact-match bugfix observations before the intent-
|
|
273
|
+
// based FTS flow runs. These hits are the most directly relevant and
|
|
274
|
+
// take priority slots in the merged output.
|
|
275
|
+
const errSig = extractErrorSignature(promptText);
|
|
276
|
+
const sigRows = errSig
|
|
277
|
+
? searchByFts(db, errSig.signature, project, 2, 'bugfix').filter(r =>
|
|
278
|
+
typeof r.relevance === 'number' && Math.abs(r.relevance) >= BM25_MIN_SCORE
|
|
279
|
+
)
|
|
280
|
+
: [];
|
|
281
|
+
|
|
270
282
|
if (intent?.useRecent) {
|
|
271
283
|
// Recall intent: show recent observations
|
|
272
284
|
rows = searchRecent(db, project, intent.limit);
|
|
@@ -302,6 +314,12 @@ async function main() {
|
|
|
302
314
|
rows = rows.slice(0, MAX_RESULTS);
|
|
303
315
|
}
|
|
304
316
|
|
|
317
|
+
// A (v2.32.8): prepend error-signature hits (higher precision), dedup, cap.
|
|
318
|
+
if (sigRows.length > 0) {
|
|
319
|
+
const sigIds = new Set(sigRows.map(r => r.id));
|
|
320
|
+
rows = [...sigRows, ...rows.filter(r => !sigIds.has(r.id))].slice(0, MAX_RESULTS);
|
|
321
|
+
}
|
|
322
|
+
|
|
305
323
|
const candidateIds = rows.map(r => r.id);
|
|
306
324
|
const dedupSkip = shouldSkipByDedup(candidateIds, INJECTED_IDS_FILE);
|
|
307
325
|
|
package/secret-scrub.mjs
CHANGED
|
File without changes
|
package/server-internals.mjs
CHANGED
|
File without changes
|
package/server.mjs
CHANGED
|
File without changes
|
package/skill.md
CHANGED
|
File without changes
|
package/skip-tools.mjs
CHANGED
|
File without changes
|
package/source-files.mjs
CHANGED
|
File without changes
|
package/stop-words.mjs
CHANGED
|
File without changes
|
package/synonyms.mjs
CHANGED
|
File without changes
|
package/tfidf.mjs
CHANGED
|
File without changes
|
package/tier.mjs
CHANGED
|
File without changes
|
package/tool-schemas.mjs
CHANGED
|
File without changes
|
package/utils.mjs
CHANGED
|
File without changes
|