claude-recall 0.8.21 ā 0.8.23
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/hooks/mcp_tool_tracker.py +0 -0
- package/.claude/hooks/user_prompt_reminder.py +69 -0
- package/.claude/settings.json +7 -2
- package/README.md +26 -34
- package/dist/cli/claude-recall-cli.js +22 -9
- package/dist/cli/commands/agent-commands.js +3 -2
- package/package.json +1 -1
- package/scripts/postinstall.js +19 -4
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
User Prompt Reminder Hook for Claude Recall.
|
|
4
|
+
|
|
5
|
+
Injects a visible reminder into Claude's context on every user prompt,
|
|
6
|
+
encouraging memory search before responding.
|
|
7
|
+
|
|
8
|
+
This hook outputs to stdout (exit 0) which Claude sees in its context.
|
|
9
|
+
|
|
10
|
+
Exit codes:
|
|
11
|
+
- 0: Always allow (non-blocking, but reminder is visible)
|
|
12
|
+
"""
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def extract_keywords(prompt: str) -> str:
|
|
18
|
+
"""Extract meaningful keywords from prompt for suggested search."""
|
|
19
|
+
# Common words to filter out
|
|
20
|
+
stop_words = {
|
|
21
|
+
'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
22
|
+
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
|
|
23
|
+
'should', 'may', 'might', 'must', 'can', 'to', 'of', 'in', 'for',
|
|
24
|
+
'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during',
|
|
25
|
+
'before', 'after', 'above', 'below', 'between', 'under', 'again',
|
|
26
|
+
'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why',
|
|
27
|
+
'how', 'all', 'each', 'few', 'more', 'most', 'other', 'some', 'such',
|
|
28
|
+
'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very',
|
|
29
|
+
's', 't', 'just', 'don', 'now', 'i', 'me', 'my', 'we', 'you', 'your',
|
|
30
|
+
'it', 'its', 'this', 'that', 'these', 'those', 'what', 'which', 'who',
|
|
31
|
+
'please', 'help', 'want', 'need', 'like', 'make', 'get', 'let', 'put'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Extract words, filter stop words, take first 4 meaningful words
|
|
35
|
+
words = prompt.lower().split()
|
|
36
|
+
keywords = [w.strip('.,!?;:\'"()[]{}') for w in words if w.lower() not in stop_words]
|
|
37
|
+
keywords = [w for w in keywords if len(w) > 2] # Filter short words
|
|
38
|
+
|
|
39
|
+
return ' '.join(keywords[:4]) if keywords else 'preferences patterns'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main():
|
|
43
|
+
try:
|
|
44
|
+
hook_data = json.load(sys.stdin)
|
|
45
|
+
prompt = hook_data.get('prompt', '') or hook_data.get('content', '') or ''
|
|
46
|
+
|
|
47
|
+
# Skip if prompt is very short (likely a typo or continuation)
|
|
48
|
+
if len(prompt.strip()) < 5:
|
|
49
|
+
sys.exit(0)
|
|
50
|
+
|
|
51
|
+
# Extract keywords for suggested search
|
|
52
|
+
keywords = extract_keywords(prompt)
|
|
53
|
+
|
|
54
|
+
# Output reminder (Claude will see this)
|
|
55
|
+
reminder = f"""<user-prompt-submit-hook>
|
|
56
|
+
š Search memories before responding: mcp__claude-recall__search("{keywords}")
|
|
57
|
+
</user-prompt-submit-hook>"""
|
|
58
|
+
|
|
59
|
+
print(reminder)
|
|
60
|
+
sys.exit(0)
|
|
61
|
+
|
|
62
|
+
except json.JSONDecodeError:
|
|
63
|
+
sys.exit(0)
|
|
64
|
+
except Exception:
|
|
65
|
+
sys.exit(0)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == '__main__':
|
|
69
|
+
main()
|
package/.claude/settings.json
CHANGED
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"UserPromptSubmit": [
|
|
28
28
|
{
|
|
29
29
|
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": "python3 /home/ebiarao/repos-wsl/personal-projects/claude-recall/.claude/hooks/user_prompt_reminder.py"
|
|
33
|
+
},
|
|
30
34
|
{
|
|
31
35
|
"type": "command",
|
|
32
36
|
"command": "python3 /home/ebiarao/repos-wsl/personal-projects/claude-recall/.claude/hooks/pubnub_prompt_hook.py"
|
|
@@ -34,5 +38,6 @@
|
|
|
34
38
|
]
|
|
35
39
|
}
|
|
36
40
|
]
|
|
37
|
-
}
|
|
38
|
-
|
|
41
|
+
},
|
|
42
|
+
"hooksVersion": "0.8.23"
|
|
43
|
+
}
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Your preferences, project structure, workflows, corrections, and coding style ar
|
|
|
9
9
|
|
|
10
10
|
> **TL;DR**
|
|
11
11
|
> Claude Recall stores and searches your past preferences and project knowledge.
|
|
12
|
-
> Install it ā restart Claude Code ā Claude
|
|
12
|
+
> Install it ā restart Claude Code ā Claude is reminded to search memory on every turn.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -151,12 +151,11 @@ Switch projects ā Claude switches memory.
|
|
|
151
151
|
Claude Recall integrates tightly via:
|
|
152
152
|
|
|
153
153
|
* MCP server (search, store, evolve)
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* post-action hooks
|
|
154
|
+
* UserPromptSubmit hooks (reminder on every turn)
|
|
155
|
+
* PreToolUse hooks (enforce search before Write/Edit)
|
|
157
156
|
* PubNub event subscriber (Memory Agent)
|
|
158
157
|
|
|
159
|
-
Claude
|
|
158
|
+
Claude sees a memory search reminder on every conversation turn, with suggested keywords extracted from your prompt.
|
|
160
159
|
|
|
161
160
|
---
|
|
162
161
|
|
|
@@ -186,59 +185,52 @@ npx claude-recall --version
|
|
|
186
185
|
npx claude-recall setup
|
|
187
186
|
```
|
|
188
187
|
|
|
189
|
-
### Upgrade
|
|
190
|
-
|
|
191
|
-
npm doesn't run postinstall on version upgrades, so hooks/skills won't update automatically.
|
|
188
|
+
### Upgrade
|
|
192
189
|
|
|
193
190
|
```bash
|
|
194
|
-
# Option 1: Uninstall and reinstall (recommended)
|
|
195
|
-
npm uninstall claude-recall && npm install claude-recall@latest
|
|
196
|
-
|
|
197
|
-
# Option 2: Manually install hooks/skills after upgrade
|
|
198
191
|
npm install claude-recall@latest
|
|
199
|
-
npx claude-recall
|
|
192
|
+
npx claude-recall repair
|
|
200
193
|
```
|
|
201
194
|
|
|
195
|
+
This updates the package and repairs hooks/skills to the latest version.
|
|
196
|
+
|
|
202
197
|
---
|
|
203
198
|
|
|
204
199
|
### Activate
|
|
205
200
|
|
|
206
201
|
```bash
|
|
207
|
-
#
|
|
202
|
+
# Register MCP server:
|
|
208
203
|
claude mcp add claude-recall -- npx -y claude-recall@latest mcp start
|
|
209
204
|
|
|
210
|
-
#
|
|
211
|
-
claude mcp remove claude-recall
|
|
212
|
-
claude mcp add claude-recall -- npx -y claude-recall@latest mcp start
|
|
213
|
-
|
|
214
|
-
# Then restart your terminal or session
|
|
205
|
+
# Restart your terminal or Claude Code session
|
|
215
206
|
```
|
|
216
207
|
|
|
217
|
-
|
|
208
|
+
Already registered? Remove first: `claude mcp remove claude-recall`
|
|
218
209
|
|
|
219
|
-
|
|
210
|
+
---
|
|
220
211
|
|
|
221
|
-
|
|
222
|
-
npx -y claude-recall@latest mcp stop
|
|
223
|
-
```
|
|
212
|
+
### Automatic Capture (Optional)
|
|
224
213
|
|
|
225
|
-
|
|
214
|
+
For automatic preference/pattern capture from conversations, start the Memory Agent:
|
|
226
215
|
|
|
227
|
-
|
|
216
|
+
```bash
|
|
217
|
+
npx claude-recall agent start
|
|
218
|
+
```
|
|
228
219
|
|
|
229
|
-
|
|
220
|
+
The Memory Agent:
|
|
221
|
+
- Listens to conversation events via PubNub
|
|
222
|
+
- Extracts preferences ("I prefer TypeScript", "always use Jest")
|
|
223
|
+
- Stores learnings automatically
|
|
230
224
|
|
|
231
|
-
|
|
225
|
+
Without the agent, you can still manually store memories via MCP tools.
|
|
232
226
|
|
|
233
|
-
|
|
227
|
+
---
|
|
234
228
|
|
|
235
|
-
|
|
229
|
+
### Verify
|
|
236
230
|
|
|
237
|
-
|
|
238
|
-
mcp__claude-recall__search
|
|
239
|
-
```
|
|
231
|
+
In Claude Code, ask: *"Search my memories"*
|
|
240
232
|
|
|
241
|
-
If
|
|
233
|
+
Claude should call `mcp__claude-recall__search`. If it works ā you're ready.
|
|
242
234
|
|
|
243
235
|
---
|
|
244
236
|
|
|
@@ -567,7 +567,7 @@ async function main() {
|
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
// Install hooks and skills to current project
|
|
570
|
-
function installHooksAndSkills() {
|
|
570
|
+
function installHooksAndSkills(force = false) {
|
|
571
571
|
const cwd = process.cwd();
|
|
572
572
|
const projectName = path.basename(cwd);
|
|
573
573
|
console.log('\nš¦ Installing Claude Recall hooks and skills...\n');
|
|
@@ -589,7 +589,8 @@ async function main() {
|
|
|
589
589
|
'mcp_tool_tracker.py',
|
|
590
590
|
'pre_tool_search_enforcer.py',
|
|
591
591
|
'pubnub_pre_tool_hook.py',
|
|
592
|
-
'pubnub_prompt_hook.py'
|
|
592
|
+
'pubnub_prompt_hook.py',
|
|
593
|
+
'user_prompt_reminder.py'
|
|
593
594
|
];
|
|
594
595
|
let hooksInstalled = 0;
|
|
595
596
|
for (const script of hookScripts) {
|
|
@@ -614,9 +615,13 @@ async function main() {
|
|
|
614
615
|
const settingsContent = fs.readFileSync(settingsPath, 'utf8');
|
|
615
616
|
settings = JSON.parse(settingsContent);
|
|
616
617
|
}
|
|
617
|
-
//
|
|
618
|
-
//
|
|
619
|
-
|
|
618
|
+
// Version-based hook configuration
|
|
619
|
+
// Update hooks if: no hooks, older version, or force flag
|
|
620
|
+
const CURRENT_HOOKS_VERSION = '0.8.23';
|
|
621
|
+
const needsUpdate = force || !settings.hooks || settings.hooksVersion !== CURRENT_HOOKS_VERSION;
|
|
622
|
+
if (needsUpdate) {
|
|
623
|
+
// Use ABSOLUTE paths so hooks work from any subdirectory
|
|
624
|
+
settings.hooksVersion = CURRENT_HOOKS_VERSION;
|
|
620
625
|
settings.hooks = {
|
|
621
626
|
PreToolUse: [
|
|
622
627
|
{
|
|
@@ -647,6 +652,10 @@ async function main() {
|
|
|
647
652
|
UserPromptSubmit: [
|
|
648
653
|
{
|
|
649
654
|
hooks: [
|
|
655
|
+
{
|
|
656
|
+
type: "command",
|
|
657
|
+
command: `python3 ${path.join(hooksDir, 'user_prompt_reminder.py')}`
|
|
658
|
+
},
|
|
650
659
|
{
|
|
651
660
|
type: "command",
|
|
652
661
|
command: `python3 ${path.join(hooksDir, 'pubnub_prompt_hook.py')}`
|
|
@@ -660,9 +669,12 @@ async function main() {
|
|
|
660
669
|
console.log(' ā PreToolUse (mcp__claude-recall__*): Tracks search calls');
|
|
661
670
|
console.log(' ā PreToolUse (Write|Edit): Enforces memory search first');
|
|
662
671
|
console.log(' ā UserPromptSubmit: Captures prompts for preference extraction');
|
|
672
|
+
if (force) {
|
|
673
|
+
console.log(' ā Force flag: overwrote existing configuration');
|
|
674
|
+
}
|
|
663
675
|
}
|
|
664
676
|
else {
|
|
665
|
-
console.log(
|
|
677
|
+
console.log(`ā¹ļø Hooks already at version ${CURRENT_HOOKS_VERSION} (skipped)`);
|
|
666
678
|
}
|
|
667
679
|
// Copy skills directory
|
|
668
680
|
if (fs.existsSync(packageSkillsDir)) {
|
|
@@ -712,9 +724,10 @@ async function main() {
|
|
|
712
724
|
// Repair command - simple alias for setup --install
|
|
713
725
|
program
|
|
714
726
|
.command('repair')
|
|
715
|
-
.description('Repair broken or missing hooks and skills
|
|
716
|
-
.
|
|
717
|
-
|
|
727
|
+
.description('Repair broken or missing hooks and skills')
|
|
728
|
+
.option('--force', 'Force overwrite existing hook configuration')
|
|
729
|
+
.action((options) => {
|
|
730
|
+
installHooksAndSkills(options.force || false);
|
|
718
731
|
process.exit(0);
|
|
719
732
|
});
|
|
720
733
|
// Check hooks function
|
|
@@ -117,8 +117,9 @@ class AgentCommands {
|
|
|
117
117
|
console.log(chalk_1.default.gray(`š” Use ${chalk_1.default.cyan('npx claude-recall agent logs')} to view logs`));
|
|
118
118
|
console.log(chalk_1.default.gray(`š” Use ${chalk_1.default.cyan('npx claude-recall agent status')} to check status`));
|
|
119
119
|
console.log();
|
|
120
|
-
// Wait a bit to ensure startup
|
|
121
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
120
|
+
// Wait a bit to ensure startup, then exit cleanly
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
122
|
+
process.exit(0);
|
|
122
123
|
}
|
|
123
124
|
/**
|
|
124
125
|
* Stop the memory agent
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-recall",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.23",
|
|
4
4
|
"description": "Persistent memory for Claude Code with fire-and-forget PubNub architecture, automatic capture, failure learning, and project scoping via MCP server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
package/scripts/postinstall.js
CHANGED
|
@@ -138,7 +138,8 @@ try {
|
|
|
138
138
|
'mcp_tool_tracker.py',
|
|
139
139
|
'pre_tool_search_enforcer.py',
|
|
140
140
|
'pubnub_pre_tool_hook.py',
|
|
141
|
-
'pubnub_prompt_hook.py'
|
|
141
|
+
'pubnub_prompt_hook.py',
|
|
142
|
+
'user_prompt_reminder.py'
|
|
142
143
|
];
|
|
143
144
|
|
|
144
145
|
for (const script of hookScripts) {
|
|
@@ -163,9 +164,14 @@ try {
|
|
|
163
164
|
settings = JSON.parse(settingsContent);
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
167
|
+
// Version-based hook configuration
|
|
168
|
+
// Update hooks if: no hooks, or older version
|
|
169
|
+
const CURRENT_HOOKS_VERSION = '0.8.23';
|
|
170
|
+
const needsUpdate = !settings.hooks || settings.hooksVersion !== CURRENT_HOOKS_VERSION;
|
|
171
|
+
|
|
172
|
+
if (needsUpdate) {
|
|
173
|
+
// Use ABSOLUTE paths so hooks work from any subdirectory
|
|
174
|
+
settings.hooksVersion = CURRENT_HOOKS_VERSION;
|
|
169
175
|
settings.hooks = {
|
|
170
176
|
PreToolUse: [
|
|
171
177
|
{
|
|
@@ -196,6 +202,10 @@ try {
|
|
|
196
202
|
UserPromptSubmit: [
|
|
197
203
|
{
|
|
198
204
|
hooks: [
|
|
205
|
+
{
|
|
206
|
+
type: "command",
|
|
207
|
+
command: `python3 ${path.join(hooksDir, 'user_prompt_reminder.py')}`
|
|
208
|
+
},
|
|
199
209
|
{
|
|
200
210
|
type: "command",
|
|
201
211
|
command: `python3 ${path.join(hooksDir, 'pubnub_prompt_hook.py')}`
|
|
@@ -210,6 +220,11 @@ try {
|
|
|
210
220
|
console.log(' ā PreToolUse (mcp__claude-recall__*): Tracks search calls');
|
|
211
221
|
console.log(' ā PreToolUse (Write|Edit): Enforces memory search first');
|
|
212
222
|
console.log(' ā UserPromptSubmit: Captures prompts for preference extraction');
|
|
223
|
+
if (settings.hooksVersion) {
|
|
224
|
+
console.log(` ā Updated from previous version to ${CURRENT_HOOKS_VERSION}`);
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
console.log(`ā¹ļø Hooks already at version ${CURRENT_HOOKS_VERSION} (skipped)`);
|
|
213
228
|
}
|
|
214
229
|
|
|
215
230
|
// Copy skills directory (packageSkillsDir defined above)
|