claude-recall 0.20.5 → 0.20.6
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/skills/auto-corrections/SKILL.md +3 -1
- package/.claude/skills/auto-corrections/manifest.json +5 -3
- package/.claude/skills/auto-preferences/SKILL.md +16 -1
- package/.claude/skills/auto-preferences/manifest.json +18 -3
- package/.claude/skills/memory-management/SKILL.md +5 -4
- package/dist/hooks/llm-classifier.js +59 -0
- package/dist/hooks/memory-stop-hook.js +62 -0
- package/dist/pi/extension.js +18 -1
- package/dist/shared/event-processors.js +89 -0
- package/package.json +1 -1
- package/skills/memory-management.md +4 -0
|
@@ -8,7 +8,7 @@ source: claude-recall
|
|
|
8
8
|
|
|
9
9
|
# Corrections
|
|
10
10
|
|
|
11
|
-
Auto-generated from
|
|
11
|
+
Auto-generated from 14 memories. Last updated: 2026-04-06.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
@@ -22,6 +22,8 @@ Auto-generated from 12 memories. Last updated: 2026-04-02.
|
|
|
22
22
|
- CORRECTION: Memory with complex metadata
|
|
23
23
|
- CORRECTION: Memory with complex metadata
|
|
24
24
|
- CORRECTION: Memory with complex metadata
|
|
25
|
+
- CORRECTION: Memory with complex metadata
|
|
26
|
+
- CORRECTION: Memory with complex metadata
|
|
25
27
|
- CORRECTION: License copyright should include user's name instead of 'Claude Recall Contributors'
|
|
26
28
|
- CORRECTION: License copyright should list your name instead of 'Claude Recall Contributors'
|
|
27
29
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"topicId": "corrections",
|
|
3
|
-
"sourceHash": "
|
|
4
|
-
"memoryCount":
|
|
5
|
-
"generatedAt": "2026-04-
|
|
3
|
+
"sourceHash": "c77b49a2f99b869465aeb4705a4c3de1f922381d5b49ed8d78c661477f8668fa",
|
|
4
|
+
"memoryCount": 14,
|
|
5
|
+
"generatedAt": "2026-04-06T16:14:29.343Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
|
+
"memory_1775492069326_vksvzmt3f",
|
|
8
|
+
"memory_1775491767369_sepsjmg8y",
|
|
7
9
|
"memory_1775169786543_43p8to1hu",
|
|
8
10
|
"memory_1775169704632_wzwczltzu",
|
|
9
11
|
"memory_1775169639101_rmxkftqtk",
|
|
@@ -8,10 +8,25 @@ source: claude-recall
|
|
|
8
8
|
|
|
9
9
|
# Preferences
|
|
10
10
|
|
|
11
|
-
Auto-generated from
|
|
11
|
+
Auto-generated from 69 memories. Last updated: 2026-04-06.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
15
|
+
- Session test preference 1775492069465
|
|
16
|
+
- Test preference 1775492069353-2
|
|
17
|
+
- Test preference 1775492069353-1
|
|
18
|
+
- Test preference 1775492069353-0
|
|
19
|
+
- Test memory content
|
|
20
|
+
- Session test preference 1775491767519
|
|
21
|
+
- Test preference 1775491767395-2
|
|
22
|
+
- Test preference 1775491767395-1
|
|
23
|
+
- Test preference 1775491767395-0
|
|
24
|
+
- Test memory content
|
|
25
|
+
- When planning implementation work, always divide into phases/stages. Each phase must have its own verification tests with concrete commands and expected outputs. Only proceed to the next phase when the current phase's tests pass. Never combine untested changes into a single deployment. This prevents hours of debugging cascading failures.
|
|
26
|
+
- Solving issues and testing that they are solved takes priority over committing and pushing. Don't suggest committing until the fix is verified end-to-end.
|
|
27
|
+
- When the user says "jam" it means "just answer me" — give a direct, concise answer without extra exploration, tool calls, or elaboration. Skip the research and just respond.
|
|
28
|
+
- After each refactoring, document the changes made. Don't batch documentation to the end — write it as you go.
|
|
29
|
+
- Any major refactoring requires exhaustive search to make sure nothing is missed. Always grep/search comprehensively before and after changes to verify no stale references, broken imports, or missed files remain.
|
|
15
30
|
- Session test preference 1775169786712
|
|
16
31
|
- Test preference 1775169786565-2
|
|
17
32
|
- Test preference 1775169786565-1
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"topicId": "preferences",
|
|
3
|
-
"sourceHash": "
|
|
4
|
-
"memoryCount":
|
|
5
|
-
"generatedAt": "2026-04-
|
|
3
|
+
"sourceHash": "84b3e678b896e5e6761dcfc6d49ee47c0027d4c7808f6ef6f8f3e00aa39ee76c",
|
|
4
|
+
"memoryCount": 69,
|
|
5
|
+
"generatedAt": "2026-04-06T16:14:29.485Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
|
+
"memory_1775492069467_5cturlg0a",
|
|
8
|
+
"memory_1775492069400_icg4tjivf",
|
|
9
|
+
"memory_1775492069377_goix7nu9v",
|
|
10
|
+
"memory_1775492069354_wma3zh5i7",
|
|
11
|
+
"memory_1775492069258_q9d2k28wt",
|
|
12
|
+
"memory_1775491767523_p2xtn4uak",
|
|
13
|
+
"memory_1775491767447_q1dwsdfk3",
|
|
14
|
+
"memory_1775491767421_vgntf4jt8",
|
|
15
|
+
"memory_1775491767397_f181w5lqd",
|
|
16
|
+
"memory_1775491767290_s7ntmkwpg",
|
|
17
|
+
"memory_1775491073130_p8b493ay9",
|
|
18
|
+
"memory_1775236195716_i3pb5nls7",
|
|
19
|
+
"memory_1775210227089_j433ldlva",
|
|
20
|
+
"memory_1775208934902_2kovciriy",
|
|
21
|
+
"memory_1775208477621_fqa3w21j1",
|
|
7
22
|
"memory_1775169786717_1zmwoe6ai",
|
|
8
23
|
"memory_1775169786630_rdudb8hbc",
|
|
9
24
|
"memory_1775169786589_zurej1v51",
|
|
@@ -34,10 +34,11 @@ Persistent memory system that ensures Claude never repeats mistakes and always a
|
|
|
34
34
|
|
|
35
35
|
1. **ALWAYS load rules before acting** — Call `load_rules` as your very first action in a session, before even reading files. Rules inform how you explore, not just how you edit.
|
|
36
36
|
2. **ACT on loaded rules** — After loading, state which rules apply to your current task before proceeding. If a rule conflicts with your plan, follow the rule. If none apply, say so. Loading without applying is the same as not loading.
|
|
37
|
-
3. **Cite applied rules inline** — When a rule influences your work: (applied from memory: <rule>)
|
|
38
|
-
4. **
|
|
39
|
-
5. **
|
|
40
|
-
6. **
|
|
37
|
+
3. **Cite applied rules inline** — When a rule influences your work: (applied from memory: <rule>). Place the citation next to the action it influenced, not at the end of unrelated text.
|
|
38
|
+
4. **User says "recall" / "remember" / "store this" → use Claude Recall** — When the user says any of these keywords, ALWAYS use `mcp__claude-recall__store_memory`. Do NOT write to the native memory directory (`~/.claude/projects/*/memory/`) for these requests. Claude Recall is the user's preferred memory system.
|
|
39
|
+
5. **Ask before storing** — Before calling `store_memory`, tell the user what you plan to store and ask for confirmation
|
|
40
|
+
6. **Capture corrections immediately** — User fixes are highest priority (still ask first)
|
|
41
|
+
7. **Never store secrets** — No API keys, passwords, tokens, or PII
|
|
41
42
|
|
|
42
43
|
## Quick Reference
|
|
43
44
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.classifyWithLLM = classifyWithLLM;
|
|
10
10
|
exports.extractHindsightHint = extractHindsightHint;
|
|
11
|
+
exports.extractSessionLearningsWithLLM = extractSessionLearningsWithLLM;
|
|
11
12
|
exports.classifyBatchWithLLM = classifyBatchWithLLM;
|
|
12
13
|
// Lazy singleton — avoid import cost when API key is absent
|
|
13
14
|
let clientInstance; // undefined = not yet checked
|
|
@@ -148,6 +149,64 @@ async function extractHindsightHint(failureDescription, context) {
|
|
|
148
149
|
return null;
|
|
149
150
|
}
|
|
150
151
|
}
|
|
152
|
+
const SESSION_EXTRACTION_PROMPT = `You are analyzing a coding session transcript to extract durable project knowledge.
|
|
153
|
+
|
|
154
|
+
The transcript shows tool calls (Bash, Edit, Read, Grep, etc.) and their results, plus user and assistant messages.
|
|
155
|
+
|
|
156
|
+
Extract ONLY facts useful in FUTURE sessions:
|
|
157
|
+
- Project conventions discovered (file structure, naming patterns, build tools, test frameworks)
|
|
158
|
+
- Workflow patterns that worked or failed (e.g. "tests must be run from project root")
|
|
159
|
+
- Technical constraints or gotchas encountered (e.g. "this project uses ESM, not CJS")
|
|
160
|
+
- Environment requirements discovered (e.g. "needs Node 20+", "uses pnpm not npm")
|
|
161
|
+
|
|
162
|
+
Do NOT extract:
|
|
163
|
+
- Task-specific details (what was built, which files changed this session)
|
|
164
|
+
- Debugging steps unlikely to recur
|
|
165
|
+
- Code patterns visible by reading the codebase
|
|
166
|
+
- Anything in the EXISTING MEMORIES list below
|
|
167
|
+
|
|
168
|
+
Respond with ONLY valid JSON (no markdown fences):
|
|
169
|
+
[{"type":"project-knowledge|preference|devops|failure","content":"<imperative statement>","confidence":0.0-1.0}]
|
|
170
|
+
|
|
171
|
+
Return [] if nothing durable was learned. Max 10 items. Each content should be a concise imperative statement (e.g. "Run tests with pnpm test, not npm test").`;
|
|
172
|
+
/**
|
|
173
|
+
* Extract durable session learnings from a conversation summary using Haiku.
|
|
174
|
+
* Returns null if no API key or on any failure.
|
|
175
|
+
*/
|
|
176
|
+
async function extractSessionLearningsWithLLM(summary, existingMemories) {
|
|
177
|
+
const client = getClient();
|
|
178
|
+
if (!client)
|
|
179
|
+
return null;
|
|
180
|
+
try {
|
|
181
|
+
const memList = existingMemories.length > 0
|
|
182
|
+
? existingMemories.map(m => `- ${m}`).join('\n')
|
|
183
|
+
: '(none)';
|
|
184
|
+
const systemPrompt = SESSION_EXTRACTION_PROMPT + `\n\nEXISTING MEMORIES (do not duplicate):\n${memList}`;
|
|
185
|
+
const response = await client.messages.create({
|
|
186
|
+
model: MODEL,
|
|
187
|
+
max_tokens: 1000,
|
|
188
|
+
system: systemPrompt,
|
|
189
|
+
messages: [{ role: 'user', content: summary }],
|
|
190
|
+
});
|
|
191
|
+
const content = response.content?.[0];
|
|
192
|
+
if (content?.type !== 'text')
|
|
193
|
+
return null;
|
|
194
|
+
const results = parseJSON(content.text);
|
|
195
|
+
if (!Array.isArray(results))
|
|
196
|
+
return null;
|
|
197
|
+
const validTypes = ['project-knowledge', 'preference', 'devops', 'failure'];
|
|
198
|
+
return results
|
|
199
|
+
.filter((r) => r && validTypes.includes(r.type) && typeof r.content === 'string' && r.content.length > 5)
|
|
200
|
+
.map((r) => ({
|
|
201
|
+
type: r.type,
|
|
202
|
+
content: r.content,
|
|
203
|
+
confidence: typeof r.confidence === 'number' ? r.confidence : 0.7,
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
151
210
|
async function classifyBatchWithLLM(texts) {
|
|
152
211
|
if (texts.length === 0)
|
|
153
212
|
return [];
|
|
@@ -46,6 +46,7 @@ const memory_1 = require("../services/memory");
|
|
|
46
46
|
const config_1 = require("../services/config");
|
|
47
47
|
const failure_detectors_1 = require("./failure-detectors");
|
|
48
48
|
const outcome_storage_1 = require("../services/outcome-storage");
|
|
49
|
+
const event_processors_1 = require("../shared/event-processors");
|
|
49
50
|
const MAX_STORE = 3;
|
|
50
51
|
async function handleMemoryStop(input) {
|
|
51
52
|
const transcriptPath = input?.transcript_path ?? '';
|
|
@@ -107,6 +108,22 @@ async function handleMemoryStop(input) {
|
|
|
107
108
|
(0, shared_1.hookLog)('memory-stop', `Captured ${result.type}: ${result.extract.substring(0, 80)}`);
|
|
108
109
|
}
|
|
109
110
|
(0, shared_1.hookLog)('memory-stop', `Session end: stored ${stored} memories from ${entries.length} entries`);
|
|
111
|
+
// Session extraction: learn from long coding sessions (reads wider window)
|
|
112
|
+
try {
|
|
113
|
+
(0, event_processors_1.setLogFunction)(shared_1.hookLog);
|
|
114
|
+
const sessionEntries = (0, shared_1.readTranscriptTail)(transcriptPath, 50);
|
|
115
|
+
if (sessionEntries.length >= 10) {
|
|
116
|
+
const conversationEntries = buildConversationEntries(sessionEntries);
|
|
117
|
+
const extracted = await (0, event_processors_1.extractSessionLearnings)(conversationEntries, input?.session_id ?? '', projectId, 5);
|
|
118
|
+
if (extracted > 0) {
|
|
119
|
+
(0, shared_1.hookLog)('memory-stop', `Session extraction: stored ${extracted} learnings`);
|
|
120
|
+
stored += extracted;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
(0, shared_1.hookLog)('memory-stop', `Session extraction error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
126
|
+
}
|
|
110
127
|
// Scan for citations in assistant messages to track compliance
|
|
111
128
|
scanForCitations(transcriptPath);
|
|
112
129
|
// Scan transcript for failure signals (non-zero exits, test cycles, backtracking, etc.)
|
|
@@ -385,3 +402,48 @@ function extractTagsFromContext(context) {
|
|
|
385
402
|
}
|
|
386
403
|
return tags;
|
|
387
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* Convert raw JSONL transcript entries to ConversationEntry[] for session extraction.
|
|
407
|
+
*/
|
|
408
|
+
function buildConversationEntries(entries) {
|
|
409
|
+
const result = [];
|
|
410
|
+
for (const entry of entries) {
|
|
411
|
+
if ((0, shared_1.isUserEntry)(entry)) {
|
|
412
|
+
const text = (0, shared_1.extractTextFromEntry)(entry);
|
|
413
|
+
if (text && text.length > 5) {
|
|
414
|
+
result.push({ role: 'user', text: text.substring(0, 300) });
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
const text = (0, shared_1.extractTextFromEntry)(entry);
|
|
419
|
+
if (text && text.length > 5) {
|
|
420
|
+
result.push({ role: 'assistant', text: text.substring(0, 300) });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// Extract paired tool interactions across all entries
|
|
425
|
+
try {
|
|
426
|
+
const interactions = (0, shared_1.extractToolInteractions)(entries);
|
|
427
|
+
for (const ti of interactions) {
|
|
428
|
+
if (ti.call) {
|
|
429
|
+
result.push({
|
|
430
|
+
role: 'assistant',
|
|
431
|
+
text: JSON.stringify(ti.call.input || {}).substring(0, 150),
|
|
432
|
+
toolName: ti.call.name,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (ti.result) {
|
|
436
|
+
result.push({
|
|
437
|
+
role: 'tool_result',
|
|
438
|
+
text: ti.result.content.substring(0, 200),
|
|
439
|
+
toolName: ti.call.name,
|
|
440
|
+
isError: ti.result.isError,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
// Skip if parsing fails
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
}
|
package/dist/pi/extension.js
CHANGED
|
@@ -64,6 +64,7 @@ function formatRules(rules) {
|
|
|
64
64
|
function default_1(pi) {
|
|
65
65
|
let projectId = '';
|
|
66
66
|
let sessionId = `pi_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
|
67
|
+
const collectedToolResults = [];
|
|
67
68
|
let rulesLoaded = false;
|
|
68
69
|
const collectedUserTexts = [];
|
|
69
70
|
// Route logs through Pi's UI when available
|
|
@@ -78,6 +79,7 @@ function default_1(pi) {
|
|
|
78
79
|
projectId = ctx.cwd.split('/').pop() || 'unknown';
|
|
79
80
|
rulesLoaded = false;
|
|
80
81
|
collectedUserTexts.length = 0;
|
|
82
|
+
collectedToolResults.length = 0;
|
|
81
83
|
(0, event_processors_1.resetPendingFailures)();
|
|
82
84
|
try {
|
|
83
85
|
config_1.ConfigService.getInstance().updateConfig({
|
|
@@ -112,6 +114,13 @@ function default_1(pi) {
|
|
|
112
114
|
.map(c => c.text)
|
|
113
115
|
.join('\n');
|
|
114
116
|
const result = (0, event_processors_1.processToolOutcome)(event.toolName, event.input, output, event.isError, sessionId);
|
|
117
|
+
// Collect for session extraction
|
|
118
|
+
collectedToolResults.push({
|
|
119
|
+
role: 'tool_result',
|
|
120
|
+
text: output.substring(0, 300),
|
|
121
|
+
toolName: event.toolName,
|
|
122
|
+
isError: event.isError,
|
|
123
|
+
});
|
|
115
124
|
if (ctx.hasUI) {
|
|
116
125
|
const label = event.input?.command
|
|
117
126
|
? truncateStr(event.input.command, 40)
|
|
@@ -133,9 +142,17 @@ function default_1(pi) {
|
|
|
133
142
|
(0, event_processors_1.processUserInput)(event.text, sessionId).catch(() => { });
|
|
134
143
|
return { action: 'continue' };
|
|
135
144
|
});
|
|
136
|
-
// --- Event: session end — episode + promotion ---
|
|
145
|
+
// --- Event: session end — episode + promotion + session extraction ---
|
|
137
146
|
pi.on('session_shutdown', (_event, _ctx) => {
|
|
138
147
|
(0, event_processors_1.processSessionEnd)(collectedUserTexts, sessionId, projectId).catch(() => { });
|
|
148
|
+
// Session extraction: learn from long coding sessions
|
|
149
|
+
const allEntries = [
|
|
150
|
+
...collectedUserTexts.map(t => ({ role: 'user', text: t })),
|
|
151
|
+
...collectedToolResults,
|
|
152
|
+
];
|
|
153
|
+
if (allEntries.length >= 10) {
|
|
154
|
+
(0, event_processors_1.extractSessionLearnings)(allEntries, sessionId, projectId, 5).catch(() => { });
|
|
155
|
+
}
|
|
139
156
|
});
|
|
140
157
|
// --- Event: pre-compaction — aggressive capture ---
|
|
141
158
|
pi.on('session_before_compact', (event, _ctx) => {
|
|
@@ -46,7 +46,10 @@ exports.processToolOutcome = processToolOutcome;
|
|
|
46
46
|
exports.processUserInput = processUserInput;
|
|
47
47
|
exports.processSessionEnd = processSessionEnd;
|
|
48
48
|
exports.processPreCompact = processPreCompact;
|
|
49
|
+
exports.buildSummary = buildSummary;
|
|
50
|
+
exports.extractSessionLearnings = extractSessionLearnings;
|
|
49
51
|
const shared_1 = require("../hooks/shared");
|
|
52
|
+
const llm_classifier_1 = require("../hooks/llm-classifier");
|
|
50
53
|
const memory_1 = require("../services/memory");
|
|
51
54
|
const outcome_storage_1 = require("../services/outcome-storage");
|
|
52
55
|
let logFn = () => { }; // silent by default
|
|
@@ -401,3 +404,89 @@ async function processPreCompact(userTexts, sessionId, maxStore = 5) {
|
|
|
401
404
|
}
|
|
402
405
|
return stored;
|
|
403
406
|
}
|
|
407
|
+
const SUMMARY_MAX_CHARS = 4000;
|
|
408
|
+
/**
|
|
409
|
+
* Build a condensed conversation summary from entries for LLM extraction.
|
|
410
|
+
*/
|
|
411
|
+
function buildSummary(entries) {
|
|
412
|
+
const lines = [];
|
|
413
|
+
let totalChars = 0;
|
|
414
|
+
for (const entry of entries) {
|
|
415
|
+
let line;
|
|
416
|
+
if (entry.role === 'tool_result') {
|
|
417
|
+
const status = entry.isError ? ' [ERROR]' : '';
|
|
418
|
+
const tool = entry.toolName ? `${entry.toolName}` : 'tool';
|
|
419
|
+
line = `[${tool}${status}] ${truncate(entry.text, 200)}`;
|
|
420
|
+
}
|
|
421
|
+
else if (entry.role === 'assistant' && entry.toolName) {
|
|
422
|
+
line = `[assistant → ${entry.toolName}] ${truncate(entry.text, 150)}`;
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
line = `[${entry.role}] ${truncate(entry.text, 200)}`;
|
|
426
|
+
}
|
|
427
|
+
if (totalChars + line.length > SUMMARY_MAX_CHARS)
|
|
428
|
+
break;
|
|
429
|
+
lines.push(line);
|
|
430
|
+
totalChars += line.length + 1;
|
|
431
|
+
}
|
|
432
|
+
return lines.join('\n');
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Extract durable learnings from a coding session using LLM analysis.
|
|
436
|
+
*
|
|
437
|
+
* Sends a condensed session summary to Haiku and stores extracted
|
|
438
|
+
* project knowledge, preferences, and workflow patterns.
|
|
439
|
+
*
|
|
440
|
+
* Requires ANTHROPIC_API_KEY — logs a message if unavailable.
|
|
441
|
+
*
|
|
442
|
+
* @param entries Conversation entries (user + assistant + tool results)
|
|
443
|
+
* @param sessionId Current session ID
|
|
444
|
+
* @param projectId Current project ID
|
|
445
|
+
* @param maxStore Max learnings to store (default 5)
|
|
446
|
+
* @returns Number of learnings stored
|
|
447
|
+
*/
|
|
448
|
+
async function extractSessionLearnings(entries, sessionId, projectId, maxStore = 5) {
|
|
449
|
+
if (entries.length < 10)
|
|
450
|
+
return 0;
|
|
451
|
+
try {
|
|
452
|
+
const summary = buildSummary(entries);
|
|
453
|
+
// Fetch existing memories for dedup context
|
|
454
|
+
const existingMemories = [];
|
|
455
|
+
try {
|
|
456
|
+
const ms = memory_1.MemoryService.getInstance();
|
|
457
|
+
const rules = ms.loadActiveRules(projectId);
|
|
458
|
+
const all = [...rules.preferences, ...rules.corrections, ...rules.failures, ...rules.devops];
|
|
459
|
+
for (const m of all.slice(0, 20)) {
|
|
460
|
+
const val = typeof m.value === 'object' ? (m.value?.content || JSON.stringify(m.value)) : String(m.value);
|
|
461
|
+
existingMemories.push(truncate(val, 80));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
// Non-critical — extraction can proceed without dedup context
|
|
466
|
+
}
|
|
467
|
+
const learnings = await (0, llm_classifier_1.extractSessionLearningsWithLLM)(summary, existingMemories);
|
|
468
|
+
if (learnings === null) {
|
|
469
|
+
logFn('event-processor', 'Session extraction requires ANTHROPIC_API_KEY. Set it to enable learning from long sessions.');
|
|
470
|
+
return 0;
|
|
471
|
+
}
|
|
472
|
+
if (learnings.length === 0)
|
|
473
|
+
return 0;
|
|
474
|
+
let stored = 0;
|
|
475
|
+
for (const learning of learnings) {
|
|
476
|
+
if (stored >= maxStore)
|
|
477
|
+
break;
|
|
478
|
+
// Dedup against existing memories
|
|
479
|
+
const existing = (0, shared_1.searchExisting)(learning.content.substring(0, 100));
|
|
480
|
+
if ((0, shared_1.isDuplicate)(learning.content, existing))
|
|
481
|
+
continue;
|
|
482
|
+
(0, shared_1.storeMemory)(learning.content, learning.type, projectId, learning.confidence);
|
|
483
|
+
stored++;
|
|
484
|
+
logFn('event-processor', `Session extraction: ${learning.type} — ${truncate(learning.content, 60)}`);
|
|
485
|
+
}
|
|
486
|
+
return stored;
|
|
487
|
+
}
|
|
488
|
+
catch (err) {
|
|
489
|
+
logFn('event-processor', `extractSessionLearnings error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
490
|
+
return 0;
|
|
491
|
+
}
|
|
492
|
+
}
|
package/package.json
CHANGED
|
@@ -27,6 +27,10 @@ If the user states a preference ("I prefer tabs", "use functional style"), call
|
|
|
27
27
|
|
|
28
28
|
If a command fails or you need to backtrack, the failure is captured automatically. You don't need to store it manually.
|
|
29
29
|
|
|
30
|
+
## When the user says "recall", "remember", or "store this"
|
|
31
|
+
|
|
32
|
+
ALWAYS use `recall_store_memory`. These keywords mean the user wants Claude Recall specifically — not any other memory system.
|
|
33
|
+
|
|
30
34
|
## Before making decisions
|
|
31
35
|
|
|
32
36
|
Call `recall_search_memory` with relevant keywords to check for existing project knowledge before choosing approaches, tools, or conventions.
|