claude-recall 0.15.13 → 0.15.16

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.
@@ -32,7 +32,11 @@ READ_ONLY_BASH = [
32
32
  'ls', 'cat', 'head', 'tail', 'less', 'more', 'file', 'stat', 'wc',
33
33
  'find', 'locate', 'which', 'whereis', 'type', 'pwd', 'whoami',
34
34
  'git status', 'git log', 'git diff', 'git show', 'git branch',
35
- 'git remote', 'git fetch', 'git stash list', 'git tag',
35
+ 'git remote', 'git fetch', 'git stash', 'git tag',
36
+ 'git add', 'git commit', 'git push', 'git pull', 'git merge',
37
+ 'git rebase', 'git checkout', 'git switch', 'git cherry-pick',
38
+ 'npm install', 'npm version', 'npm publish', 'npm pack',
39
+ 'npx',
36
40
  'npm list', 'npm ls', 'npm view', 'npm outdated', 'npm audit',
37
41
  'npm test', 'npm run test', 'npm run build', 'npm run lint',
38
42
  'pip list', 'pip show', 'pip freeze',
@@ -125,7 +129,21 @@ def main():
125
129
  if (now - last_search) <= SEARCH_TTL_MS:
126
130
  sys.exit(0)
127
131
 
128
- # Block or warn
132
+ # TTL expired but rules were loaded earlier — degrade to warn, not block.
133
+ # This prevents deadlock when MCP server disconnects mid-session.
134
+ msg = f"""
135
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
136
+ STALE RULES — consider reloading before {tool_name}
137
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
138
+
139
+ Rules were loaded earlier but TTL expired.
140
+ Run: mcp__claude-recall__load_rules({{}})
141
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
142
+ """
143
+ print(msg.strip(), file=sys.stderr)
144
+ sys.exit(0) # Warn only — allow the action
145
+
146
+ # Never loaded in this session — block
129
147
  msg = f"""
130
148
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
131
149
  LOAD RULES REQUIRED before {tool_name}
@@ -1 +1,48 @@
1
- {"hooks": {}, "hooksVersion": "1.0.0"}
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "mcp__claude-recall__.*|Write|Edit|Bash|Task",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "python3 /home/ebiarao/repos-wsl/personal-projects/claude-recall/.claude/hooks/search_enforcer.py"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "UserPromptSubmit": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "npx claude-recall@latest hook run correction-detector"
20
+ }
21
+ ]
22
+ }
23
+ ],
24
+ "Stop": [
25
+ {
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "npx claude-recall@latest hook run memory-stop",
30
+ "timeout": 30
31
+ }
32
+ ]
33
+ }
34
+ ],
35
+ "PreCompact": [
36
+ {
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "npx claude-recall@latest hook run precompact-preserve",
41
+ "timeout": 60
42
+ }
43
+ ]
44
+ }
45
+ ]
46
+ },
47
+ "hooksVersion": "4.0.0"
48
+ }
@@ -8,7 +8,7 @@ source: claude-recall
8
8
 
9
9
  # Corrections
10
10
 
11
- Auto-generated from 22 memories. Last updated: 2026-02-18.
11
+ Auto-generated from 25 memories. Last updated: 2026-02-26.
12
12
 
13
13
  ## Rules
14
14
 
@@ -33,6 +33,9 @@ Auto-generated from 22 memories. Last updated: 2026-02-18.
33
33
  - CORRECTION: Memory with complex metadata
34
34
  - CORRECTION: Memory with complex metadata
35
35
  - CORRECTION: Memory with complex metadata
36
+ - CORRECTION: Memory with complex metadata
37
+ - CORRECTION: Memory with complex metadata
38
+ - CORRECTION: Memory with complex metadata
36
39
  - CORRECTION: use spaces not tabs for indentation
37
40
 
38
41
  ---
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "topicId": "corrections",
3
- "sourceHash": "a5771c8eabda87fcc3f6e70078899728da3232155bae5b8c230d476bf8d23935",
4
- "memoryCount": 22,
5
- "generatedAt": "2026-02-18T19:13:11.271Z",
3
+ "sourceHash": "9db7378bea9322948eab03d138983fc4e4173aa593ea16e1487f0551b91cecc3",
4
+ "memoryCount": 25,
5
+ "generatedAt": "2026-02-26T10:01:12.122Z",
6
6
  "memoryKeys": [
7
+ "memory_1772100072113_a6za43yqy",
8
+ "memory_1771937053521_wayeeo1gr",
9
+ "memory_1771936515420_zm9348oh9",
7
10
  "memory_1771441991246_26wk2e6q3",
8
11
  "memory_1771432628423_6l1fpuu77",
9
12
  "memory_1771432266123_aevced1ym",
@@ -8,10 +8,25 @@ source: claude-recall
8
8
 
9
9
  # Preferences
10
10
 
11
- Auto-generated from 123 memories. Last updated: 2026-02-18.
11
+ Auto-generated from 140 memories. Last updated: 2026-02-26.
12
12
 
13
13
  ## Rules
14
14
 
15
+ - Session test preference 1772100072188
16
+ - Test preference 1772100072127-2
17
+ - Test preference 1772100072127-1
18
+ - Test preference 1772100072127-0
19
+ - Test memory content
20
+ - Session test preference 1771937053612
21
+ - Test preference 1771937053539-2
22
+ - Test preference 1771937053539-1
23
+ - Test preference 1771937053539-0
24
+ - Test memory content
25
+ - Session test preference 1771936515500
26
+ - Test preference 1771936515435-2
27
+ - Test preference 1771936515435-1
28
+ - Test preference 1771936515435-0
29
+ - Test memory content
15
30
  - Session test preference 1771441991453
16
31
  - Test preference 1771441991283-2
17
32
  - Test preference 1771441991283-1
@@ -132,6 +147,8 @@ Auto-generated from 123 memories. Last updated: 2026-02-18.
132
147
  - Test memory 1770503266492-0
133
148
  - Memory with complex metadata
134
149
  - Test memory content
150
+ - When user says 'jam', it means 'just answer me' — a shorthand directive to provide direct answers without elaboration
151
+ - When user says 'jam', they mean 'just answer me' — respond directly without explanation
135
152
  - always use tabs for indentation
136
153
  - always use tabs
137
154
  - always use tabs
@@ -1,9 +1,24 @@
1
1
  {
2
2
  "topicId": "preferences",
3
- "sourceHash": "8f50f9a19f039ef86bce89ab33afcd2782dc19e536275039f56031ebd22c56a3",
4
- "memoryCount": 123,
5
- "generatedAt": "2026-02-18T19:13:11.483Z",
3
+ "sourceHash": "6abccdcd61e745a5953d71937d9d76bc0e63e4fdea57efd12e82ee5364c5cb7e",
4
+ "memoryCount": 140,
5
+ "generatedAt": "2026-02-26T10:01:12.200Z",
6
6
  "memoryKeys": [
7
+ "memory_1772100072189_1z5ky8vqz",
8
+ "memory_1772100072157_1dhc3az8z",
9
+ "memory_1772100072143_9wwr89ivr",
10
+ "memory_1772100072128_v5em20s0r",
11
+ "memory_1772100072057_gpuspbnd5",
12
+ "memory_1771937053614_p4252awle",
13
+ "memory_1771937053577_7hkwaug2z",
14
+ "memory_1771937053557_2tv5gy1kn",
15
+ "memory_1771937053540_byc4aoei6",
16
+ "memory_1771937053479_5b1aq5bo4",
17
+ "memory_1771936515501_7rxf04yr7",
18
+ "memory_1771936515470_5amfyz2c2",
19
+ "memory_1771936515454_yuee3hady",
20
+ "memory_1771936515436_2zas4g86g",
21
+ "memory_1771936515373_x886pahdp",
7
22
  "memory_1771441991455_q068p8n23",
8
23
  "memory_1771441991411_dv9e76ojs",
9
24
  "memory_1771441991351_8esutde44",
@@ -124,6 +139,8 @@
124
139
  "memory_1770503266494_f3p8e15xv",
125
140
  "memory_1770503266473_23fy8pzky",
126
141
  "memory_1770503266442_289cuenj7",
142
+ "hook_preference_1771934420257_l4pawjgn5",
143
+ "hook_preference_1771934405021_mnhek0x7b",
127
144
  "hook_preference_1771112119815_z02zal3z6",
128
145
  "hook_preference_1771112104493_d3nz1o2ye",
129
146
  "hook_preference_1771112075356_3gh724ucm"
@@ -108,6 +108,8 @@ class ClaudeRecallCLI {
108
108
  this.showSkillsEvolution();
109
109
  // Token Savings Estimate
110
110
  this.showTokenSavings(stats);
111
+ // Rule Compliance
112
+ this.showComplianceStats();
111
113
  console.log('\n');
112
114
  this.logger.info('CLI', 'Stats displayed', stats);
113
115
  }
@@ -190,6 +192,34 @@ class ClaudeRecallCLI {
190
192
  console.log(` Total saved: ~${totalSavings.toLocaleString()} tokens`);
191
193
  console.log(` (vs repeating preferences or loading all reference files)`);
192
194
  }
195
+ /**
196
+ * Show rule compliance statistics
197
+ */
198
+ showComplianceStats() {
199
+ const report = this.memoryService.getComplianceReport();
200
+ if (report.rules.length === 0)
201
+ return;
202
+ console.log('\n📋 Rule Compliance:');
203
+ const loaded = report.rules.filter(r => r.load_count > 0);
204
+ const cited = loaded.filter(r => r.cite_count > 0);
205
+ console.log(` Rules loaded at least once: ${loaded.length}`);
206
+ console.log(` Rules cited at least once: ${cited.length}`);
207
+ const neverCited = loaded.filter(r => r.load_count >= 5 && r.cite_count === 0);
208
+ if (neverCited.length > 0) {
209
+ console.log(` ⚠️ Never cited (loaded 5+ times): ${neverCited.length}`);
210
+ neverCited.slice(0, 3).forEach(r => {
211
+ let val;
212
+ try {
213
+ const parsed = JSON.parse(r.value);
214
+ val = parsed?.content || parsed?.value || r.value;
215
+ }
216
+ catch {
217
+ val = r.value;
218
+ }
219
+ console.log(` - "${String(val).substring(0, 60)}..." (loaded ${r.load_count}x)`);
220
+ });
221
+ }
222
+ }
193
223
  /**
194
224
  * Show failure memories (v0.7.0)
195
225
  */
@@ -529,7 +559,9 @@ async function main() {
529
559
  if (fs.existsSync(hooksDir)) {
530
560
  const oldHooks = [
531
561
  'memory_enforcer.py', // Old v0.8.x hook
532
- 'search_enforcer.py',
562
+ // search_enforcer.py intentionally NOT listed — it's the current hook.
563
+ // copyFileSync overwrites it during install; deleting first risks leaving
564
+ // it missing if the copy source doesn't resolve (e.g. in the source project).
533
565
  'mcp_tool_tracker.py',
534
566
  'pubnub_pre_tool_hook.py',
535
567
  'pubnub_prompt_hook.py',
@@ -9,6 +9,7 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.handleMemoryStop = handleMemoryStop;
11
11
  const shared_1 = require("./shared");
12
+ const memory_1 = require("../services/memory");
12
13
  const MAX_STORE = 3;
13
14
  async function handleMemoryStop(input) {
14
15
  const transcriptPath = input?.transcript_path ?? '';
@@ -60,4 +61,55 @@ async function handleMemoryStop(input) {
60
61
  (0, shared_1.hookLog)('memory-stop', `Captured ${result.type}: ${result.extract.substring(0, 80)}`);
61
62
  }
62
63
  (0, shared_1.hookLog)('memory-stop', `Session end: stored ${stored} memories from ${entries.length} entries`);
64
+ // Scan for citations in assistant messages to track compliance
65
+ scanForCitations(transcriptPath);
66
+ }
67
+ /**
68
+ * Scan transcript for (applied from memory: ...) citations and increment cite_count.
69
+ * Uses a wider window (last 50 entries) since citations appear throughout assistant responses.
70
+ */
71
+ function scanForCitations(transcriptPath) {
72
+ try {
73
+ const entries = (0, shared_1.readTranscriptTail)(transcriptPath, 50);
74
+ if (entries.length === 0)
75
+ return;
76
+ const citationRegex = /\(applied from memory:\s*(.+?)\)/g;
77
+ const citations = [];
78
+ for (const entry of entries) {
79
+ // Only scan assistant entries (opposite of isUserEntry)
80
+ if ((0, shared_1.isUserEntry)(entry))
81
+ continue;
82
+ const text = (0, shared_1.extractTextFromEntry)(entry);
83
+ if (!text)
84
+ continue;
85
+ let match;
86
+ while ((match = citationRegex.exec(text)) !== null) {
87
+ citations.push(match[1].trim());
88
+ }
89
+ }
90
+ if (citations.length === 0)
91
+ return;
92
+ (0, shared_1.hookLog)('memory-stop', `Found ${citations.length} citation(s) in transcript`);
93
+ const memoryService = memory_1.MemoryService.getInstance();
94
+ for (const cite of citations) {
95
+ // Search for rules that match this citation text
96
+ const existing = (0, shared_1.searchExisting)(cite.substring(0, 100));
97
+ if (existing.length === 0)
98
+ continue;
99
+ // Fuzzy-match: citations are often paraphrased, so use a looser threshold
100
+ for (const mem of existing) {
101
+ const memContent = typeof mem.value === 'string'
102
+ ? mem.value
103
+ : (mem.value?.content || JSON.stringify(mem.value));
104
+ if ((0, shared_1.jaccardSimilarity)(cite, memContent) >= 0.5) {
105
+ memoryService.incrementCiteCount(mem.key);
106
+ (0, shared_1.hookLog)('memory-stop', `Citation matched: "${cite.substring(0, 50)}" → rule ${mem.key}`);
107
+ break; // One match per citation
108
+ }
109
+ }
110
+ }
111
+ }
112
+ catch (error) {
113
+ (0, shared_1.hookLog)('memory-stop', `Citation scan error: ${error}`);
114
+ }
63
115
  }
@@ -276,6 +276,16 @@ class MemoryTools {
276
276
  return `- ${val}`;
277
277
  }).join('\n'));
278
278
  }
279
+ // Add compliance section for rules loaded frequently but never cited
280
+ const compliance = this.memoryService.getComplianceReport(projectId || context.projectId);
281
+ const lowCompliance = compliance.rules.filter(r => r.load_count >= 5 && r.cite_count === 0);
282
+ if (lowCompliance.length > 0) {
283
+ sections.push('## Rule Health\nThese rules are loaded frequently but never cited — consider rewording or removing:\n' +
284
+ lowCompliance.map(r => {
285
+ const val = typeof r.value === 'string' ? (JSON.parse(r.value)?.content || r.value) : r.value;
286
+ return `- "${String(val).substring(0, 80)}" (loaded ${r.load_count}x, cited 0x)`;
287
+ }).join('\n'));
288
+ }
279
289
  const totalRules = rules.preferences.length + rules.corrections.length +
280
290
  rules.failures.length + rules.devops.length;
281
291
  const resultTokens = this.estimateTokens([
@@ -103,6 +103,18 @@ class MemoryStorage {
103
103
  this.db.exec('CREATE INDEX IF NOT EXISTS idx_memories_scope_project ON memories(scope, project_id)');
104
104
  console.log('✅ Added scope column');
105
105
  }
106
+ // Add load_count if missing (compliance tracking v0.15.14)
107
+ if (!columnNames.includes('load_count')) {
108
+ console.log('📋 Migrating database schema: Adding load_count column...');
109
+ this.db.exec('ALTER TABLE memories ADD COLUMN load_count INTEGER DEFAULT 0');
110
+ console.log('✅ Added load_count column');
111
+ }
112
+ // Add cite_count if missing (compliance tracking v0.15.14)
113
+ if (!columnNames.includes('cite_count')) {
114
+ console.log('📋 Migrating database schema: Adding cite_count column...');
115
+ this.db.exec('ALTER TABLE memories ADD COLUMN cite_count INTEGER DEFAULT 0');
116
+ console.log('✅ Added cite_count column');
117
+ }
106
118
  // Add content_hash if missing (content dedup)
107
119
  if (!columnNames.includes('content_hash')) {
108
120
  console.log('📋 Migrating database schema: Adding content_hash column...');
@@ -386,6 +398,45 @@ class MemoryStorage {
386
398
  const rows = stmt.all(...params);
387
399
  return rows.map(row => this.rowToMemory(row));
388
400
  }
401
+ /**
402
+ * Bulk-increment load_count for a set of memory IDs.
403
+ * Called when rules are loaded via load_rules.
404
+ */
405
+ incrementLoadCounts(ids) {
406
+ if (ids.length === 0)
407
+ return;
408
+ const placeholders = ids.map(() => '?').join(',');
409
+ this.db.prepare(`UPDATE memories SET load_count = load_count + 1 WHERE id IN (${placeholders})`).run(...ids);
410
+ }
411
+ /**
412
+ * Increment cite_count for a single memory by ID.
413
+ * Called when a citation is detected in the transcript.
414
+ */
415
+ incrementCiteCount(id) {
416
+ this.db.prepare('UPDATE memories SET cite_count = cite_count + 1 WHERE id = ?').run(id);
417
+ }
418
+ /**
419
+ * Get rules with their compliance metrics (load_count, cite_count).
420
+ * Returns rules that have been loaded at least once.
421
+ */
422
+ getRulesWithCompliance(projectId) {
423
+ let query = 'SELECT id, key, type, value, load_count, cite_count FROM memories WHERE load_count > 0';
424
+ const params = [];
425
+ if (projectId) {
426
+ query += ' AND (project_id = ? OR scope = ? OR project_id IS NULL)';
427
+ params.push(projectId, 'universal');
428
+ }
429
+ query += ' ORDER BY load_count DESC';
430
+ const rows = this.db.prepare(query).all(...params);
431
+ return rows.map(row => ({
432
+ id: row.id,
433
+ key: row.key,
434
+ type: row.type,
435
+ value: row.value,
436
+ load_count: row.load_count,
437
+ cite_count: row.cite_count
438
+ }));
439
+ }
389
440
  getDatabase() {
390
441
  return this.db;
391
442
  }
@@ -369,6 +369,13 @@ class MemoryService {
369
369
  ? `Loaded ${counts.join(', ')}`
370
370
  : 'No active rules found';
371
371
  this.logger.info('MemoryService', summary, { projectId: pid });
372
+ // Increment load_count for all returned rules
373
+ const allIds = [
374
+ ...preferences, ...corrections, ...failures, ...devops
375
+ ].map(m => m.id).filter((id) => id !== undefined);
376
+ if (allIds.length > 0) {
377
+ this.storage.incrementLoadCounts(allIds);
378
+ }
372
379
  return { preferences, corrections, failures, devops, summary };
373
380
  }
374
381
  catch (error) {
@@ -376,6 +383,40 @@ class MemoryService {
376
383
  return { preferences: [], corrections: [], failures: [], devops: [], summary: 'Error loading rules' };
377
384
  }
378
385
  }
386
+ /**
387
+ * Increment cite_count for a rule matched by key.
388
+ * Called by the citation scanner in the stop hook.
389
+ */
390
+ incrementCiteCount(key) {
391
+ const memory = this.storage.retrieve(key);
392
+ if (memory?.id) {
393
+ this.storage.incrementCiteCount(memory.id);
394
+ }
395
+ }
396
+ /**
397
+ * Get compliance report showing load vs cite rates for rules.
398
+ */
399
+ getComplianceReport(projectId) {
400
+ const pid = projectId || this.config.getProjectId();
401
+ const rules = this.storage.getRulesWithCompliance(pid);
402
+ const loaded = rules.filter(r => r.load_count > 0);
403
+ const cited = loaded.filter(r => r.cite_count > 0);
404
+ const neverCited = loaded.filter(r => r.load_count >= 5 && r.cite_count === 0);
405
+ return {
406
+ rules: rules.map(r => ({
407
+ key: r.key,
408
+ type: r.type,
409
+ value: r.value,
410
+ load_count: r.load_count,
411
+ cite_count: r.cite_count
412
+ })),
413
+ summary: {
414
+ totalLoaded: loaded.length,
415
+ totalCited: cited.length,
416
+ neverCited: neverCited.length
417
+ }
418
+ };
419
+ }
379
420
  /**
380
421
  * Mark a preference as superseded
381
422
  */
@@ -0,0 +1,68 @@
1
+ # Agentic Reasoning Opportunities for Claude Recall
2
+
3
+ Based on "Agentic Reasoning for Large Language Models" (arXiv:2601.12538) and real-world feedback from consumer projects.
4
+
5
+ ## Core Problem
6
+
7
+ Claude loads rules, cites them, and then doesn't follow through. The directive says "MUST output Applying memories:" — Claude sometimes does, sometimes doesn't. Even when it does, it may cite a rule and still skip the action the rule requires. This is the gap between passive memory retrieval and active reasoning with memory.
8
+
9
+ ## Key Insight
10
+
11
+ We're treating memory as **passive context injection** when it should be an **active constraint in the execution loop**. The paper's "self-evolving agentic reasoning" dimension — feedback, memory across interactions, adaptive strategies — points to closing the loop mechanically rather than relying on model reasoning.
12
+
13
+ ## Opportunities
14
+
15
+ ### 1. Executable Pre-Checks on Rules (Highest leverage)
16
+
17
+ Rules currently: prose text Claude reads and may ignore.
18
+ Rules could be: executable commands the system runs automatically before allowing actions.
19
+
20
+ Example — current rule:
21
+ ```
22
+ BEFORE making any code changes: always check (1) what branch am I on? (2) is everything committed?
23
+ ```
24
+
25
+ Proposed rule with executable pre-check:
26
+ ```json
27
+ {
28
+ "content": "Check git state before code changes",
29
+ "type": "devops",
30
+ "pre_check": "git branch --show-current && git status --short",
31
+ "applies_to": ["Write", "Edit"]
32
+ }
33
+ ```
34
+
35
+ The hook runs `pre_check` before allowing Write/Edit and injects the output into context. Claude doesn't need to "decide" to follow through — it happens mechanically. Removes the model from the compliance path entirely.
36
+
37
+ ### 2. Post-Action Validation Hook
38
+
39
+ Pre-action enforcement exists (load rules before Write/Edit). Post-action checking does not.
40
+
41
+ A PostToolUse hook could compare what Claude did against loaded rules and flag violations: "You wrote a file without running `git branch` first — rule #4 says to check." Claude can't retroactively ignore a visible violation notice.
42
+
43
+ ### 3. Compliance Tracking
44
+
45
+ Track which rules get loaded vs. cited vs. actually followed across sessions. Surface patterns:
46
+ - "This rule was loaded 12 times, followed 3 times"
47
+ - "This rule has low compliance. Rewrite it as a concrete command?"
48
+
49
+ Persistent non-compliance signal means either the rule is too vague or needs reformulation as an executable check.
50
+
51
+ ### 4. Rule Reformulation Feedback
52
+
53
+ Rules that are consistently not followed might be too abstract. The system could suggest:
54
+ - "This rule has low compliance. Rewrite it as a concrete command?"
55
+ - Turning prose rules into executable pre-checks closes the reasoning gap
56
+
57
+ ## Priority Order
58
+
59
+ 1. **Executable pre-checks** — Removes model from compliance path. Highest impact, moderate effort.
60
+ 2. **Post-action validation** — Catches failures after the fact. Medium impact, medium effort.
61
+ 3. **Compliance tracking** — Informs which rules need reformulation. Medium impact, low effort.
62
+ 4. **Rule reformulation** — Improves rule quality over time. Lower impact, depends on #3.
63
+
64
+ ## Reference
65
+
66
+ - Paper: https://arxiv.org/pdf/2601.12538
67
+ - Feedback source: Consumer project testing (v0.15.9–v0.15.13)
68
+ - Core failure mode: "I understood the rule, acknowledged it, and still took the shortcut" — Claude's own assessment
@@ -0,0 +1,30 @@
1
+ # Claude Recall — Expected Behavior (v0.15.13)
2
+
3
+ Use this document to evaluate how well Claude Recall is performing in consumer projects. Paste the assessment prompt below into a conversation and ask Claude to self-evaluate.
4
+
5
+ ## Assessment Prompt
6
+
7
+ Evaluate how well Claude Recall is performing against these expectations:
8
+
9
+ **1. Load rules before acting**
10
+ - The `search_enforcer` hook blocks Write/Edit/Bash until `load_rules` is called (1-minute TTL, so it re-fires per task)
11
+ - Expected: Claude calls `load_rules` before its first code action in each task. This should be automatic due to the hook — not something Claude decides to skip.
12
+
13
+ **2. Visible rule application**
14
+ - The `load_rules` response includes a directive requiring Claude to output an "Applying memories:" section before its first action
15
+ - Expected: Before every Write/Edit/Bash, Claude states which loaded rules apply to the current task. If none apply, it says so. This is visible to the user, not internal reasoning.
16
+
17
+ **3. Actual compliance with loaded rules**
18
+ - Expected: If a rule says "check git branch before code changes," Claude actually runs `git branch`. Not just citing the rule and skipping the action. Loading + citing without following through is compliance theater.
19
+
20
+ **4. Inline citations**
21
+ - Expected: When a rule influences an action, Claude adds `(applied from memory: <rule summary>)` inline so the user sees which memories are driving decisions.
22
+
23
+ **5. Auto-capture quality**
24
+ - Hooks auto-capture corrections, preferences from user messages
25
+ - Expected: Only actionable, reusable rules are stored. Conversational fragments, questions, typo-filled casual messages, and Claude's own responses should NOT be stored. If junk gets through, note the content.
26
+
27
+ **6. Store with permission**
28
+ - Expected: Before calling `store_memory`, Claude tells the user what it plans to store and asks for confirmation. No silent storing.
29
+
30
+ **Please assess**: For each of the 6 points above, is it working, partially working, or not working? Provide specific examples from this session.
@@ -1,7 +1,7 @@
1
1
  # Installation
2
2
 
3
3
  Claude Recall supports macOS, Linux, Windows, and WSL.
4
- Memory remains fully local (SQLite), while PubNub is used for lightweight realtime event metadata.
4
+ Memory remains fully local (SQLite).
5
5
 
6
6
  ---
7
7
 
@@ -11,7 +11,6 @@ Memory remains fully local (SQLite), while PubNub is used for lightweight realti
11
11
  |----------|---------|-------|
12
12
  | Node.js | **20+** | Required (`better-sqlite3`) |
13
13
  | Python | **3.x** | Required for Claude Code hook scripts |
14
- | PubNub | Included via npm | Used only for metadata events |
15
14
  | Claude Code | Latest | Required for MCP integration |
16
15
  | OS | macOS / Linux / Windows | WSL supported |
17
16
 
@@ -101,8 +100,6 @@ If using Ubuntu under WSL2:
101
100
  ```
102
101
  3. Ensure VSCode's terminal is *also* WSL.
103
102
 
104
- PubNub works normally under WSL since it uses outbound-only secure connections.
105
-
106
103
  ---
107
104
 
108
105
  ## Node 20 Requirement
@@ -7,10 +7,9 @@ Claude Recall uses a 5-phase learning process that improves over time.
7
7
  ## 1. Pre-Action Search
8
8
  Before Claude writes or edits a file:
9
9
 
10
- - Hook sends metadata to PubNub
11
- - Agent receives event
12
- - Searches SQLite memory
13
- - Sends relevant memories back
10
+ - Hook enforces `load_rules` call
11
+ - MCP server searches SQLite memory
12
+ - Returns relevant rules to Claude
14
13
 
15
14
  Claude now knows:
16
15
  - your preferences
@@ -8,7 +8,6 @@ Claude Recall maintains a registry of all projects using it.
8
8
 
9
9
  Each project gets:
10
10
  - a unique ID
11
- - a PubNub presence channel
12
11
  - its own memory namespace
13
12
  - its own context directory inside `~/.claude-recall/projects`
14
13
 
@@ -30,21 +29,6 @@ No user configuration needed.
30
29
 
31
30
  ---
32
31
 
33
- ## Presence Channel
34
-
35
- Memory Agent publishes heartbeats:
36
-
37
- ```
38
- claude-presence:<projectId>
39
- ```
40
-
41
- Used to ensure:
42
- - at most one Agent per project
43
- - consistent event processing
44
- - clean agent shutdown
45
-
46
- ---
47
-
48
32
  ## Monorepos
49
33
 
50
34
  Claude Recall:
@@ -57,7 +41,6 @@ Claude Recall:
57
41
  ## Multi-Project Workflows
58
42
 
59
43
  Switching projects inside VSCode triggers:
60
- - PubNub presence update
61
44
  - registry notification
62
45
  - memory namespace change
63
46
 
@@ -41,8 +41,6 @@ Claude Recall will:
41
41
 
42
42
  * capture the preference
43
43
  * store it locally in SQLite
44
- * route metadata via PubNub
45
- * evolve memory asynchronously
46
44
  * apply it the next time Claude generates tests
47
45
 
48
46
  ---
package/docs/security.md CHANGED
@@ -9,7 +9,7 @@ Claude Recall is designed to be **local-first** and **privacy-safe**.
9
9
  All memory is stored in:
10
10
 
11
11
  ```
12
- ~/.claude-recall/memories.db
12
+ ~/.claude-recall/claude-recall.db
13
13
  ```
14
14
 
15
15
  Properties:
@@ -21,42 +21,12 @@ Properties:
21
21
 
22
22
  ---
23
23
 
24
- # PubNub Security Model
25
-
26
- PubNub is used only as a **realtime metadata bus**.
27
-
28
- ### What PubNub does NOT transmit:
29
- - code
30
- - text
31
- - file contents
32
- - prompts
33
- - memory content
34
- - embeddings
35
- - project data
36
-
37
- ### What PubNub DOES transmit:
38
- - tool names
39
- - file paths
40
- - event types
41
- - token counts
42
- - memory suggestion IDs
43
- - agent heartbeat metadata
44
-
45
- ### Why this is safe:
46
- - payloads are small
47
- - no sensitive content ever leaves machine
48
- - events are ephemeral
49
- - keys stored locally only
50
- - no cloud persistence
51
-
52
- ---
53
-
54
24
  # SQLite Security
55
25
 
56
26
  - file-based permissions
57
27
  - no remote access
58
28
  - ACID-compliant
59
- - optional encryption layer (coming soon)
29
+ - WAL mode for concurrency
60
30
 
61
31
  ---
62
32
 
@@ -67,8 +37,8 @@ You can:
67
37
  - inspect memory
68
38
  - delete memory
69
39
  - purge memory
70
- - view event streams
71
- - stop the agent
40
+ - view compliance stats
41
+ - stop the MCP server
72
42
 
73
43
  Claude Recall provides full visibility.
74
44
 
@@ -26,43 +26,23 @@ nvm install 20
26
26
 
27
27
  ---
28
28
 
29
- ## PubNub connection errors
30
-
31
- Symptoms:
32
-
33
- * Agent not receiving events
34
- * No memory suggestions
35
- * watch mode shows nothing
36
-
37
- Solution:
38
-
39
- * restart clamps
40
- * check network firewall
41
- * ensure project-specific keys exist in `~/.claude-recall/keys.json`
42
-
43
- PubNub uses outbound-only connections.
44
-
45
- ---
46
-
47
29
  ## Memory not updating
48
30
 
49
- Ensure the Memory Agent is running:
31
+ Check MCP server status:
50
32
 
51
33
  ```bash
52
- npx claude-recall watch
53
- ```
54
-
55
- Look for:
56
-
57
- ```
58
- [agent] heartbeat ok
34
+ npx claude-recall mcp status
59
35
  ```
60
36
 
61
37
  ---
62
38
 
63
39
  ## Hooks not firing
64
40
 
65
- Check `.claude/hooks/` directory exists.
41
+ Check `.claude/hooks/` directory exists and `search_enforcer.py` is present:
42
+
43
+ ```bash
44
+ npx claude-recall hooks check
45
+ ```
66
46
 
67
47
  ---
68
48
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.15.13",
3
+ "version": "0.15.16",
4
4
  "description": "Persistent memory for Claude Code with native Skills integration, automatic capture, failure learning, and project scoping via MCP server",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -88,6 +88,7 @@
88
88
  "@anthropic-ai/sdk": "^0.39.0",
89
89
  "better-sqlite3": "^12.2.0",
90
90
  "chalk": "^5.5.0",
91
+ "claude-recall": "^0.15.15",
91
92
  "commander": "^12.1.0"
92
93
  }
93
94
  }