claude-recall 0.22.0 → 0.22.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.
|
@@ -8,14 +8,14 @@ source: claude-recall
|
|
|
8
8
|
|
|
9
9
|
# Preferences
|
|
10
10
|
|
|
11
|
-
Auto-generated from 5 memories. Last updated: 2026-04-
|
|
11
|
+
Auto-generated from 5 memories. Last updated: 2026-04-14.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
15
|
-
- Session test preference
|
|
16
|
-
- Test preference
|
|
17
|
-
- Test preference
|
|
18
|
-
- Test preference
|
|
15
|
+
- Session test preference 1776172131422
|
|
16
|
+
- Test preference 1776172131359-2
|
|
17
|
+
- Test preference 1776172131359-1
|
|
18
|
+
- Test preference 1776172131359-0
|
|
19
19
|
- Test memory content
|
|
20
20
|
|
|
21
21
|
---
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"topicId": "preferences",
|
|
3
|
-
"sourceHash": "
|
|
3
|
+
"sourceHash": "37e19c67668ff4fdd21019d4837a36dc368138b8e4299bcff235fdaf84b5f028",
|
|
4
4
|
"memoryCount": 5,
|
|
5
|
-
"generatedAt": "2026-04-
|
|
5
|
+
"generatedAt": "2026-04-14T13:08:51.442Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
7
|
+
"memory_1776172131423_zdtbhf2vw",
|
|
8
|
+
"memory_1776172131396_d2s4cuyls",
|
|
9
|
+
"memory_1776172131378_1r9w9r7lq",
|
|
10
|
+
"memory_1776172131363_gqgkc4o6s",
|
|
11
|
+
"memory_1776172131318_dbwsryce8"
|
|
12
12
|
]
|
|
13
13
|
}
|
package/README.md
CHANGED
|
@@ -33,20 +33,33 @@ Your preferences, project structure, workflows, corrections, and coding style ar
|
|
|
33
33
|
|
|
34
34
|
### Install for Claude Code
|
|
35
35
|
|
|
36
|
+
#### First-time install
|
|
37
|
+
|
|
38
|
+
Run this **once** on your machine:
|
|
39
|
+
|
|
36
40
|
```bash
|
|
37
|
-
# Install globally
|
|
38
41
|
npm install -g claude-recall
|
|
42
|
+
```
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
claude-recall setup --install
|
|
44
|
+
Then run these **in the project directory** where you want claude-recall active:
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
```bash
|
|
47
|
+
claude-recall setup --install
|
|
44
48
|
claude mcp add claude-recall -- claude-recall mcp start
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
Restart Claude Code. **Verify**: ask *"Load my rules"* — Claude should call `mcp__claude-recall__load_rules`.
|
|
52
|
+
|
|
53
|
+
#### Adding to another project
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
The global binary is already installed. Just `cd` into the new project and run the per-project commands:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
claude-recall setup --install
|
|
59
|
+
claude mcp add claude-recall -- claude-recall mcp start
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Restart Claude Code in that project.
|
|
50
63
|
|
|
51
64
|
### Install for Pi
|
|
52
65
|
|
|
@@ -64,36 +77,61 @@ Both agents use the same database (`~/.claude-recall/claude-recall.db`). Memorie
|
|
|
64
77
|
|
|
65
78
|
### Upgrading
|
|
66
79
|
|
|
80
|
+
#### If you use Claude Code
|
|
81
|
+
|
|
82
|
+
Run this **once** to update the global binary:
|
|
83
|
+
|
|
67
84
|
```bash
|
|
68
|
-
# Claude Code — update binary + re-install hooks in each project
|
|
69
85
|
npm install -g claude-recall
|
|
70
|
-
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then run this **in each project directory** where you use claude-recall (the binary upgrade alone isn't enough — new releases sometimes add hook events that need to be registered in each project's `.claude/settings.json`):
|
|
71
89
|
|
|
72
|
-
|
|
90
|
+
```bash
|
|
91
|
+
claude-recall setup --install
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Restart Claude Code so the new MCP server starts (or run `claude-recall mcp restart` from the project directory to keep the current session running).
|
|
95
|
+
|
|
96
|
+
**Verify**: `claude-recall --version` shows the new version, and asking *"Load my rules"* in Claude Code triggers `mcp__claude-recall__load_rules`.
|
|
97
|
+
|
|
98
|
+
#### If you use Pi
|
|
99
|
+
|
|
100
|
+
Run this **once** — the `npm:` prefix is required (it matches the original install command):
|
|
101
|
+
|
|
102
|
+
```bash
|
|
73
103
|
pi update npm:claude-recall
|
|
74
104
|
```
|
|
75
105
|
|
|
76
|
-
|
|
106
|
+
Restart Pi to load the updated extension.
|
|
107
|
+
|
|
108
|
+
**Verify**: `pi list` shows the new `claude-recall` version, and asking *"Load my rules"* in Pi triggers `recall_load_rules`.
|
|
109
|
+
|
|
110
|
+
#### If you use both
|
|
111
|
+
|
|
112
|
+
Both upgrades are independent — run the Claude Code section AND the Pi section. Both agents share the same `~/.claude-recall/claude-recall.db`, so memories captured in either are visible to the other.
|
|
77
113
|
|
|
78
114
|
---
|
|
79
115
|
|
|
80
116
|
## What to Expect
|
|
81
117
|
|
|
82
|
-
Once installed, Claude Recall works automatically in the background
|
|
118
|
+
Once installed, Claude Recall works automatically in the background. Each row below is tagged with the runtime it applies to so you can skip what doesn't apply to you.
|
|
83
119
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
| When | What happens | CC | Pi |
|
|
121
|
+
|---|---|:-:|:-:|
|
|
122
|
+
| **Session start** | Active rules are loaded before the first action and injected into the agent's context | ✓ | ✓ |
|
|
123
|
+
| **As you work** | Every prompt is classified for corrections and preferences. Natural statements like *"we use tabs here"* are detected and stored | ✓ | ✓ |
|
|
124
|
+
| **Before each tool call / agent turn** | **Just-in-time rule injection** — relevant rules are surfaced as a `<system-reminder>` block adjacent to the action so the agent sees them at the moment of decision (not 50,000 tokens upstream). Per-tool-call in CC; per-turn in Pi | ✓ | ✓ |
|
|
125
|
+
| **Tool outcomes** | Tool results (Bash, Edit, Write, etc.) are captured. Failures are stored; Bash failures are paired with their successful fixes | ✓ | ✓ |
|
|
126
|
+
| **Reask detection** | Frustration signals (*"still broken"*, *"that didn't work"*) are recorded as outcome events | ✓ | ✓ |
|
|
127
|
+
| **Before context compression** | Aggressive memory sweep captures important context before the window shrinks | ✓ | ✓ |
|
|
128
|
+
| **After context compression** | Rules are automatically re-injected into the new context so they're not lost | ✓ | |
|
|
129
|
+
| **Sub-agent spawned** | Active rules are injected into the sub-agent's context. Sub-agent outcomes (completed/failed/killed) are captured | ✓ | |
|
|
130
|
+
| **Rules sync** | Top 30 rules are exported as typed `.md` files to Claude Code's native memory directory | ✓ | |
|
|
131
|
+
| **Session exit** | **Auto-checkpoint** — the most recent task is extracted into a `{completed, remaining, blockers}` snapshot and saved for the next session. Critical for Pi (no `--resume` flag); safety net for CC users who exit without resuming | ✓ | ✓ |
|
|
132
|
+
| **End of session** | Session episodes are created, candidate lessons are extracted from failures, and validated patterns are promoted into active rules | ✓ | ✓ |
|
|
95
133
|
|
|
96
|
-
Classification
|
|
134
|
+
Classification and checkpoint extraction use Claude Haiku (via `ANTHROPIC_API_KEY`) with silent regex fallback. No configuration needed.
|
|
97
135
|
|
|
98
136
|
**Next session:** `load_rules` returns everything captured previously — the agent applies your preferences without being told twice.
|
|
99
137
|
|
|
@@ -121,24 +159,35 @@ Claude Recall provides four memory tools backed by a local SQLite database with
|
|
|
121
159
|
|
|
122
160
|
### Skills
|
|
123
161
|
|
|
124
|
-
Claude Recall uses skill files to teach agents when and how to use memory tools
|
|
162
|
+
Claude Recall uses skill files to teach agents when and how to use memory tools.
|
|
163
|
+
|
|
164
|
+
**Claude Code** uses Anthropic's [Agent Skills](https://agentskills.io/) open standard:
|
|
125
165
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
166
|
+
- `.claude/skills/memory-management/SKILL.md` — core skill, guides memory behavior
|
|
167
|
+
- `.claude/skills/auto-*/` — auto-generated, crystallized from accumulated memories
|
|
168
|
+
|
|
169
|
+
See Anthropic's [Agent Skills blog post](https://claude.com/blog/equipping-agents-for-the-real-world-with-agent-skills) for the standard.
|
|
170
|
+
|
|
171
|
+
**Pi** ships a single `skills/memory-management.md` loaded via Pi's package manifest. No setup needed.
|
|
128
172
|
|
|
129
173
|
### Outcome-Aware Learning
|
|
130
174
|
|
|
131
|
-
Claude Recall tracks what happens *after* the agent acts — not just what was said. The
|
|
175
|
+
Claude Recall tracks what happens *after* the agent acts — not just what was said. The pipeline:
|
|
132
176
|
|
|
133
177
|
```
|
|
134
178
|
action → outcome event → episode → candidate lesson → promotion → active rule
|
|
179
|
+
↓
|
|
180
|
+
JIT-injected before next action
|
|
181
|
+
↓
|
|
182
|
+
PostToolUse resolves outcome per rule
|
|
135
183
|
```
|
|
136
184
|
|
|
137
185
|
- **Outcome events** capture results from all tool types (Bash, Edit, Write, MCP), test outcomes, user corrections, and reask signals
|
|
138
186
|
- **Episodes** summarize entire sessions with outcome type, severity, and confidence
|
|
139
187
|
- **Candidate lessons** are extracted from failure patterns — deduplicated by Jaccard similarity
|
|
140
|
-
- **Promotion engine** graduates lessons into active rules after 2+ observations (or immediately for high-severity failures)
|
|
141
|
-
- **
|
|
188
|
+
- **Promotion engine** graduates lessons into active rules after 2+ observations (or immediately for high-severity failures)
|
|
189
|
+
- **Just-in-time rule injection (v0.22.0+)** — active rules are surfaced as a `<system-reminder>` block adjacent to each tool call (Claude Code) or each agent turn (Pi). Each injection is recorded in `rule_injection_events` and resolved with the tool's success/failure outcome by the PostToolUse hook. **This is the meter that measures rule effectiveness in practice.** It replaces the older citation-detection regex (which empirically returned 0 citations across thousands of opportunities — agents don't reliably write `(applied from memory: …)` markers, so the meter never had data to work with).
|
|
190
|
+
- **Per-rule effectiveness data** accumulates over time in `rule_injection_events`. Future releases will use it to deboost rules that are repeatedly injected without correlating to successful tool calls, and to auto-promote rules that are repeatedly injected before failures. As of v0.22.0 the data is being collected; ranking is not yet feeding back from it.
|
|
142
191
|
|
|
143
192
|
---
|
|
144
193
|
|
|
@@ -204,17 +253,22 @@ Agents can also save/load checkpoints via MCP tools (`mcp__claude-recall__save_c
|
|
|
204
253
|
|
|
205
254
|
Manual `checkpoint save` is the explicit path. **Auto-checkpoint** is the safety net: when a session ends, the most recent task is extracted into a checkpoint automatically so the next session can resume.
|
|
206
255
|
|
|
207
|
-
|
|
208
|
-
|
|
256
|
+
**When it fires:**
|
|
257
|
+
|
|
258
|
+
- **Pi** — every `session_shutdown` event. **This is the only way to recover context in Pi: there is no `pi --resume` equivalent.**
|
|
259
|
+
- **Claude Code** — voluntary `SessionEnd` reasons (`clear`, `prompt_input_exit`, `logout`). Skips `bypass_permissions_disabled` and `other` (system-driven exits, not user intent). Useful if you exit and start fresh instead of using `claude --resume`.
|
|
260
|
+
|
|
261
|
+
**Behavior (both runtimes):**
|
|
209
262
|
|
|
210
|
-
|
|
263
|
+
- Uses Haiku to extract `{completed, remaining, blockers}` from the most recent task in the transcript
|
|
264
|
+
- **Quality gate**: refuses to save if the LLM detects the task was already complete (e.g., agent said "Done.", user said "thanks"). **Manual checkpoints are never overwritten with garbage** — an empty checkpoint is far better than a fabricated one
|
|
265
|
+
- **Tagged**: auto-saved checkpoints include `[auto-saved on <pi|cc> session exit at <iso-timestamp>]` in their notes field
|
|
266
|
+
- **Requires `ANTHROPIC_API_KEY`**. Without it, no auto-checkpoint is saved and manual `checkpoint save` still works
|
|
211
267
|
|
|
212
|
-
|
|
213
|
-
- **Notes tag**: auto-saved checkpoints include `[auto-saved on <pi|cc> session exit at <iso-timestamp>]` in the notes field, so you can tell auto from manual via `checkpoint load`.
|
|
214
|
-
- **Requires `ANTHROPIC_API_KEY`**. Without it, `extractCheckpointWithLLM` returns `null` (graceful fallback) and no auto-checkpoint is saved. Manual `checkpoint save` still works.
|
|
215
|
-
- **Disable**: remove the `SessionEnd` block from `.claude/settings.json` (Claude Code) or, for Pi, no per-project disable flag exists yet — open an issue if you need one.
|
|
268
|
+
**Disable:**
|
|
216
269
|
|
|
217
|
-
|
|
270
|
+
- **Claude Code**: remove the `SessionEnd` block from `.claude/settings.json`
|
|
271
|
+
- **Pi**: no per-project disable flag yet — [open an issue](https://github.com/raoulbia-ai/claude-recall/issues) if you need one
|
|
218
272
|
|
|
219
273
|
### Troubleshooting
|
|
220
274
|
|
|
@@ -274,6 +274,10 @@ class MemoryTools {
|
|
|
274
274
|
? metadata.type
|
|
275
275
|
: 'preference';
|
|
276
276
|
const key = `memory_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
277
|
+
const preferenceKey = typeof metadata?.preference_key === 'string' && metadata.preference_key.length > 0
|
|
278
|
+
? metadata.preference_key
|
|
279
|
+
: undefined;
|
|
280
|
+
const isOverride = metadata?.isOverride === true;
|
|
277
281
|
this.memoryService.store({
|
|
278
282
|
key,
|
|
279
283
|
value: {
|
|
@@ -288,8 +292,17 @@ class MemoryTools {
|
|
|
288
292
|
projectId: scope === 'project' ? context.projectId : undefined,
|
|
289
293
|
timestamp: context.timestamp,
|
|
290
294
|
scope: scope || null
|
|
291
|
-
}
|
|
295
|
+
},
|
|
296
|
+
preferenceKey,
|
|
297
|
+
isOverride
|
|
292
298
|
});
|
|
299
|
+
// If this store overrides an existing rule, mark previous active rules with
|
|
300
|
+
// the same preference_key as superseded and surface their keys so the agent
|
|
301
|
+
// knows to ignore the stale text sitting higher up in its context.
|
|
302
|
+
let supersededKeys = [];
|
|
303
|
+
if (isOverride && preferenceKey) {
|
|
304
|
+
supersededKeys = this.memoryService.supersedeByPreferenceKey(preferenceKey, key, { sessionId: context.sessionId, projectId: context.projectId, timestamp: context.timestamp });
|
|
305
|
+
}
|
|
293
306
|
this.logger.info('MemoryTools', 'Memory stored successfully', {
|
|
294
307
|
key,
|
|
295
308
|
type: detectedType,
|
|
@@ -320,6 +333,10 @@ class MemoryTools {
|
|
|
320
333
|
activeRule: `Stored as active rule:\n- ${content}`,
|
|
321
334
|
type: detectedType,
|
|
322
335
|
_directive: 'Apply this rule immediately. No need to call load_rules again.',
|
|
336
|
+
...(supersededKeys.length > 0 && {
|
|
337
|
+
supersededKeys,
|
|
338
|
+
_supersessionNotice: `Superseded ${supersededKeys.length} prior rule(s) for preference_key="${preferenceKey}". Ignore any earlier text from these rules still in your context: ${supersededKeys.join(', ')}`
|
|
339
|
+
}),
|
|
323
340
|
...(skillResults.length > 0 && {
|
|
324
341
|
_skillsGenerated: skillResults
|
|
325
342
|
.filter(r => r.action === 'created' || r.action === 'updated')
|
|
@@ -383,11 +400,21 @@ class MemoryTools {
|
|
|
383
400
|
return `- ${val}`;
|
|
384
401
|
}).join('\n'));
|
|
385
402
|
}
|
|
386
|
-
// Add compliance section for rules loaded frequently but never cited
|
|
403
|
+
// Add compliance section for rules loaded frequently but never cited.
|
|
404
|
+
// Cap at top 10 by load_count so load_rules stays slim — this diagnostic
|
|
405
|
+
// list was previously unbounded and could dominate the payload.
|
|
406
|
+
const RULE_HEALTH_CAP = 10;
|
|
387
407
|
const compliance = this.memoryService.getComplianceReport(projectId || context.projectId);
|
|
388
|
-
const
|
|
408
|
+
const allLowCompliance = compliance.rules.filter(r => r.load_count >= 5 && r.cite_count === 0);
|
|
409
|
+
const lowCompliance = [...allLowCompliance]
|
|
410
|
+
.sort((a, b) => (b.load_count || 0) - (a.load_count || 0))
|
|
411
|
+
.slice(0, RULE_HEALTH_CAP);
|
|
412
|
+
const hiddenCount = allLowCompliance.length - lowCompliance.length;
|
|
389
413
|
if (lowCompliance.length > 0) {
|
|
390
|
-
|
|
414
|
+
const heading = hiddenCount > 0
|
|
415
|
+
? `## Rule Health (top ${RULE_HEALTH_CAP} of ${allLowCompliance.length})\nThese rules are loaded frequently but never cited — consider rewording or removing. ${hiddenCount} more hidden; run \`npx claude-recall outcomes\` to see all.`
|
|
416
|
+
: '## Rule Health\nThese rules are loaded frequently but never cited — consider rewording or removing:';
|
|
417
|
+
sections.push(heading + '\n' +
|
|
391
418
|
lowCompliance.map(r => {
|
|
392
419
|
let val;
|
|
393
420
|
if (typeof r.value === 'string') {
|
package/dist/memory/storage.js
CHANGED
|
@@ -562,6 +562,23 @@ class MemoryStorage {
|
|
|
562
562
|
const stmt = this.db.prepare(`UPDATE memories SET ${setClause} WHERE key = ?`);
|
|
563
563
|
stmt.run(...values, key);
|
|
564
564
|
}
|
|
565
|
+
/**
|
|
566
|
+
* Get active memories (any type) that share the given preference_key.
|
|
567
|
+
* Used by store_memory's override path — a user can override a rule of any
|
|
568
|
+
* type (devops, correction, preference) as long as it was stored with a
|
|
569
|
+
* preference_key.
|
|
570
|
+
*/
|
|
571
|
+
getActiveByPreferenceKeyAnyType(preferenceKey, projectId) {
|
|
572
|
+
let query = 'SELECT * FROM memories WHERE preference_key = ? AND is_active = 1';
|
|
573
|
+
const params = [preferenceKey];
|
|
574
|
+
if (projectId) {
|
|
575
|
+
query += ' AND (project_id = ? OR project_id IS NULL)';
|
|
576
|
+
params.push(projectId);
|
|
577
|
+
}
|
|
578
|
+
query += ' ORDER BY timestamp DESC';
|
|
579
|
+
const rows = this.db.prepare(query).all(...params);
|
|
580
|
+
return rows.map(row => this.rowToMemory(row));
|
|
581
|
+
}
|
|
565
582
|
/**
|
|
566
583
|
* Get preferences by preference key
|
|
567
584
|
*/
|
package/dist/services/memory.js
CHANGED
|
@@ -43,7 +43,9 @@ class MemoryService {
|
|
|
43
43
|
file_path: request.context?.filePath,
|
|
44
44
|
timestamp: request.context?.timestamp || Date.now(),
|
|
45
45
|
relevance_score: request.relevanceScore || 1.0,
|
|
46
|
-
scope: scope
|
|
46
|
+
scope: scope,
|
|
47
|
+
preference_key: request.preferenceKey,
|
|
48
|
+
is_active: true
|
|
47
49
|
};
|
|
48
50
|
this.storage.save(memory);
|
|
49
51
|
this.logger.logMemoryOperation('STORE', {
|
|
@@ -303,6 +305,30 @@ class MemoryService {
|
|
|
303
305
|
throw error;
|
|
304
306
|
}
|
|
305
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Mark all currently-active memories with the given preference_key as superseded by newKey.
|
|
310
|
+
* Returns the list of superseded keys so callers can surface them to the agent — this
|
|
311
|
+
* closes the "I stored an override but the old rule is still in my context" gap.
|
|
312
|
+
*/
|
|
313
|
+
supersedeByPreferenceKey(preferenceKey, newKey, context) {
|
|
314
|
+
if (!preferenceKey || !newKey)
|
|
315
|
+
return [];
|
|
316
|
+
const superseded = [];
|
|
317
|
+
try {
|
|
318
|
+
const pid = context.projectId || this.config.getProjectId();
|
|
319
|
+
const existing = this.storage.getActiveByPreferenceKeyAnyType(preferenceKey, pid);
|
|
320
|
+
for (const prev of existing) {
|
|
321
|
+
if (prev.key !== newKey) {
|
|
322
|
+
this.storage.markSuperseded(prev.key, newKey);
|
|
323
|
+
superseded.push(prev.key);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
this.logger.logServiceError('MemoryService', 'supersedeByPreferenceKey', error, { preferenceKey, newKey });
|
|
329
|
+
}
|
|
330
|
+
return superseded;
|
|
331
|
+
}
|
|
306
332
|
/**
|
|
307
333
|
* Mark existing preferences as superseded
|
|
308
334
|
*/
|
package/package.json
CHANGED