claude-recall 0.7.1 → 0.7.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.
- package/.claude/CLAUDE.md +81 -0
- package/README.md +84 -0
- package/dist/cli/claude-recall-cli.js +69 -7
- package/dist/mcp/memory-capture-middleware.js +7 -6
- package/dist/mcp/tools/memory-tools.js +23 -6
- package/dist/memory/schema.sql +3 -1
- package/dist/memory/storage.js +6 -5
- package/dist/services/config.js +6 -1
- package/dist/services/memory.js +36 -2
- package/package.json +2 -2
package/.claude/CLAUDE.md
CHANGED
|
@@ -233,6 +233,87 @@ Confirm: "✓ Stored preference"
|
|
|
233
233
|
5. **Use broad search terms**: Include task type + language + preferences + success/failure/correction
|
|
234
234
|
6. **Close the loop**: Pre-action search → Execute → Post-action outcome storage
|
|
235
235
|
|
|
236
|
+
## Project Scoping (v0.7.2+)
|
|
237
|
+
|
|
238
|
+
Claude Recall now supports **project-specific memory isolation** to keep universal preferences separate from project-specific information.
|
|
239
|
+
|
|
240
|
+
### Three Memory Scopes
|
|
241
|
+
|
|
242
|
+
1. **Universal** (`scope='universal'`): Available in all projects
|
|
243
|
+
- User says: "Remember everywhere: I prefer TypeScript with strict mode"
|
|
244
|
+
- Stored as universal memory, accessible from any project
|
|
245
|
+
|
|
246
|
+
2. **Project** (`scope='project'`): Only available in current project
|
|
247
|
+
- User says: "For this project, we use PostgreSQL"
|
|
248
|
+
- Stored with project_id, only accessible from this project
|
|
249
|
+
|
|
250
|
+
3. **Unscoped** (`scope=null`, default): Available everywhere (backward compatible)
|
|
251
|
+
- User says: "I prefer Jest for testing"
|
|
252
|
+
- Stored without scope, works like v0.7.1 and earlier
|
|
253
|
+
|
|
254
|
+
### Auto-Detection
|
|
255
|
+
|
|
256
|
+
When storing memories, the system automatically detects scope from user language:
|
|
257
|
+
|
|
258
|
+
**Universal indicators**:
|
|
259
|
+
- "remember everywhere"
|
|
260
|
+
- "for all projects"
|
|
261
|
+
- "globally"
|
|
262
|
+
- "always use"
|
|
263
|
+
|
|
264
|
+
**Project indicators**:
|
|
265
|
+
- "for this project"
|
|
266
|
+
- "project-specific"
|
|
267
|
+
- "only here"
|
|
268
|
+
- "in this project"
|
|
269
|
+
|
|
270
|
+
**Default**: If no indicator, memory is unscoped (available everywhere)
|
|
271
|
+
|
|
272
|
+
### How Search Works
|
|
273
|
+
|
|
274
|
+
**Default behavior** (project + universal):
|
|
275
|
+
```
|
|
276
|
+
mcp__claude-recall__search("database")
|
|
277
|
+
```
|
|
278
|
+
Returns: Current project memories + universal memories + unscoped memories
|
|
279
|
+
|
|
280
|
+
**Global search** (all projects):
|
|
281
|
+
```
|
|
282
|
+
mcp__claude-recall__search("database", filters: { globalSearch: true })
|
|
283
|
+
```
|
|
284
|
+
Returns: All memories from all projects
|
|
285
|
+
|
|
286
|
+
### CLI Usage
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Current project stats
|
|
290
|
+
npx claude-recall stats
|
|
291
|
+
|
|
292
|
+
# Global stats (all projects)
|
|
293
|
+
npx claude-recall stats --global
|
|
294
|
+
|
|
295
|
+
# Search current project
|
|
296
|
+
npx claude-recall search "database"
|
|
297
|
+
|
|
298
|
+
# Search specific project
|
|
299
|
+
npx claude-recall search "database" --project my-app
|
|
300
|
+
|
|
301
|
+
# Search all projects
|
|
302
|
+
npx claude-recall search "database" --global
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Use Cases
|
|
306
|
+
|
|
307
|
+
**Universal memories**: Preferences that apply across all your work
|
|
308
|
+
- Coding style: "Always use TypeScript with strict mode"
|
|
309
|
+
- Tools: "Prefer Jest for testing"
|
|
310
|
+
- Conventions: "Name files with kebab-case"
|
|
311
|
+
|
|
312
|
+
**Project memories**: Project-specific details
|
|
313
|
+
- Database: "This project uses PostgreSQL"
|
|
314
|
+
- API: "Base URL is https://api.example.com"
|
|
315
|
+
- Build: "Run npm run build:prod for production"
|
|
316
|
+
|
|
236
317
|
## Advanced: Optional Context-Manager Agent
|
|
237
318
|
|
|
238
319
|
For complex multi-step research, a `context-manager` agent is available at `.claude/agents/context-manager.md`.
|
package/README.md
CHANGED
|
@@ -451,6 +451,90 @@ Failure Rate: 12.4% ↘
|
|
|
451
451
|
- **L3 Adaptive**: Systematic workflows, devops patterns
|
|
452
452
|
- **L4 Compositional**: Multi-constraint reasoning, complex decision-making
|
|
453
453
|
|
|
454
|
+
## Project Scoping (v0.7.2+)
|
|
455
|
+
|
|
456
|
+
Claude Recall now supports **project-specific memory isolation** while keeping universal preferences available everywhere.
|
|
457
|
+
|
|
458
|
+
### How It Works
|
|
459
|
+
|
|
460
|
+
- **Single global database**: `~/.claude-recall/claude-recall.db`
|
|
461
|
+
- **Three memory scopes**:
|
|
462
|
+
- **Universal**: Available in all projects (coding preferences, tools)
|
|
463
|
+
- **Project**: Only available in the specific project
|
|
464
|
+
- **Unscoped** (default): Available everywhere (backward compatible)
|
|
465
|
+
- **Project ID**: Automatically detected from directory name
|
|
466
|
+
|
|
467
|
+
### Storing Memories
|
|
468
|
+
|
|
469
|
+
**Universal memories** (available everywhere):
|
|
470
|
+
```
|
|
471
|
+
User: "Remember everywhere: I prefer TypeScript with strict mode"
|
|
472
|
+
Claude: [Stores with scope='universal']
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Project-specific memories**:
|
|
476
|
+
```
|
|
477
|
+
User: "For this project, we use SQLite for the database"
|
|
478
|
+
Claude: [Stores with scope='project', project_id='my-app']
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Default** (unscoped, works like before):
|
|
482
|
+
```
|
|
483
|
+
User: "I prefer Jest for testing"
|
|
484
|
+
Claude: [Stores with scope=null, available everywhere]
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Searching with Scopes
|
|
488
|
+
|
|
489
|
+
**Default** (current project + universal):
|
|
490
|
+
```bash
|
|
491
|
+
npx claude-recall search "database"
|
|
492
|
+
# Returns: Current project memories + universal memories + unscoped
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Specific project**:
|
|
496
|
+
```bash
|
|
497
|
+
npx claude-recall search "database" --project my-app
|
|
498
|
+
# Returns: my-app memories + universal memories + unscoped
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**All projects**:
|
|
502
|
+
```bash
|
|
503
|
+
npx claude-recall search "database" --global
|
|
504
|
+
# Returns: All memories from all projects
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Stats with Scopes
|
|
508
|
+
|
|
509
|
+
**Current project**:
|
|
510
|
+
```bash
|
|
511
|
+
npx claude-recall stats
|
|
512
|
+
# Shows: Memories for current project (claude-recall) + universal + unscoped
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**All projects**:
|
|
516
|
+
```bash
|
|
517
|
+
npx claude-recall stats --global
|
|
518
|
+
# Shows: All memories across all projects
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Use Cases
|
|
522
|
+
|
|
523
|
+
**Universal memories** (scope='universal'):
|
|
524
|
+
- Coding style preferences: "Always use TypeScript with strict mode"
|
|
525
|
+
- Tool preferences: "Prefer Jest for testing"
|
|
526
|
+
- File naming conventions: "Name markdown files with lowercase-dash-case"
|
|
527
|
+
|
|
528
|
+
**Project-specific memories** (scope='project'):
|
|
529
|
+
- Database choice: "This project uses PostgreSQL"
|
|
530
|
+
- API endpoints: "API base URL is https://api.example.com"
|
|
531
|
+
- Build commands: "Run npm run build:prod for production"
|
|
532
|
+
|
|
533
|
+
**Unscoped memories** (scope=null, default):
|
|
534
|
+
- Backward compatible
|
|
535
|
+
- Works like v0.7.1 and earlier
|
|
536
|
+
- Available everywhere unless you explicitly scope them
|
|
537
|
+
|
|
454
538
|
## Memory Management
|
|
455
539
|
|
|
456
540
|
Claude Recall automatically manages memory to prevent unlimited database growth, with user notifications:
|
|
@@ -67,13 +67,28 @@ class ClaudeRecallCLI {
|
|
|
67
67
|
/**
|
|
68
68
|
* Show memory statistics
|
|
69
69
|
*/
|
|
70
|
-
showStats() {
|
|
71
|
-
|
|
70
|
+
showStats(options) {
|
|
71
|
+
let stats;
|
|
72
72
|
const configService = config_1.ConfigService.getInstance();
|
|
73
73
|
const config = configService.getConfig();
|
|
74
74
|
const maxMemories = config.database.compaction?.maxMemories || 10000;
|
|
75
|
+
if (options?.global) {
|
|
76
|
+
// Show stats for all memories
|
|
77
|
+
stats = this.memoryService.getStats();
|
|
78
|
+
console.log('\n📊 Claude Recall Statistics (All Projects)\n');
|
|
79
|
+
}
|
|
80
|
+
else if (options?.project) {
|
|
81
|
+
// Show stats for specific project + universal
|
|
82
|
+
stats = this.getProjectStats(options.project);
|
|
83
|
+
console.log(`\n📊 Claude Recall Statistics (Project: ${options.project})\n`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Show stats for current project + universal
|
|
87
|
+
const projectId = configService.getProjectId();
|
|
88
|
+
stats = this.getProjectStats(projectId);
|
|
89
|
+
console.log(`\n📊 Claude Recall Statistics (Project: ${projectId})\n`);
|
|
90
|
+
}
|
|
75
91
|
const usagePercent = (stats.total / maxMemories) * 100;
|
|
76
|
-
console.log('\n📊 Claude Recall Statistics\n');
|
|
77
92
|
console.log(`Total Memories: ${stats.total}/${maxMemories} (${usagePercent.toFixed(1)}%)`);
|
|
78
93
|
// Simple status indicator
|
|
79
94
|
if (usagePercent >= 90) {
|
|
@@ -95,6 +110,24 @@ class ClaudeRecallCLI {
|
|
|
95
110
|
console.log('\n');
|
|
96
111
|
this.logger.info('CLI', 'Stats displayed', stats);
|
|
97
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Get stats for a specific project (includes universal and unscoped memories)
|
|
115
|
+
*/
|
|
116
|
+
getProjectStats(projectId) {
|
|
117
|
+
const allMemories = this.memoryService.search('');
|
|
118
|
+
const projectMemories = allMemories.filter(m => m.project_id === projectId ||
|
|
119
|
+
m.scope === 'universal' ||
|
|
120
|
+
m.project_id === null);
|
|
121
|
+
// Calculate byType breakdown
|
|
122
|
+
const byType = {};
|
|
123
|
+
for (const mem of projectMemories) {
|
|
124
|
+
byType[mem.type] = (byType[mem.type] || 0) + 1;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
total: projectMemories.length,
|
|
128
|
+
byType
|
|
129
|
+
};
|
|
130
|
+
}
|
|
98
131
|
/**
|
|
99
132
|
* Show skills evolution breakdown
|
|
100
133
|
*/
|
|
@@ -254,7 +287,27 @@ class ClaudeRecallCLI {
|
|
|
254
287
|
*/
|
|
255
288
|
search(query, options) {
|
|
256
289
|
const limit = options.limit || 10;
|
|
257
|
-
|
|
290
|
+
// Determine search scope
|
|
291
|
+
let results;
|
|
292
|
+
if (options.global) {
|
|
293
|
+
// Global search: all memories
|
|
294
|
+
results = this.memoryService.search(query);
|
|
295
|
+
}
|
|
296
|
+
else if (options.project) {
|
|
297
|
+
// Project-specific search: project + universal
|
|
298
|
+
results = this.memoryService.findRelevant({
|
|
299
|
+
query,
|
|
300
|
+
projectId: options.project
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// Default: current project + universal
|
|
305
|
+
const config = config_1.ConfigService.getInstance();
|
|
306
|
+
results = this.memoryService.findRelevant({
|
|
307
|
+
query,
|
|
308
|
+
projectId: config.getProjectId()
|
|
309
|
+
});
|
|
310
|
+
}
|
|
258
311
|
const topResults = results.slice(0, limit);
|
|
259
312
|
if (options.json) {
|
|
260
313
|
// Format for hook consumption - include relevance_score field
|
|
@@ -566,11 +619,15 @@ async function main() {
|
|
|
566
619
|
.description('Search memories by query')
|
|
567
620
|
.option('-l, --limit <number>', 'Maximum results to show', '10')
|
|
568
621
|
.option('--json', 'Output as JSON')
|
|
622
|
+
.option('--project <id>', 'Filter by project ID (includes universal memories)')
|
|
623
|
+
.option('--global', 'Search all projects and memories')
|
|
569
624
|
.action((query, options) => {
|
|
570
625
|
const cli = new ClaudeRecallCLI(program.opts());
|
|
571
626
|
cli.search(query, {
|
|
572
627
|
limit: parseInt(options.limit),
|
|
573
|
-
json: options.json
|
|
628
|
+
json: options.json,
|
|
629
|
+
project: options.project,
|
|
630
|
+
global: options.global
|
|
574
631
|
});
|
|
575
632
|
process.exit(0);
|
|
576
633
|
});
|
|
@@ -578,9 +635,14 @@ async function main() {
|
|
|
578
635
|
program
|
|
579
636
|
.command('stats')
|
|
580
637
|
.description('Show memory statistics')
|
|
581
|
-
.
|
|
638
|
+
.option('--project <id>', 'Filter by project ID')
|
|
639
|
+
.option('--global', 'Show all memories across all projects')
|
|
640
|
+
.action((options) => {
|
|
582
641
|
const cli = new ClaudeRecallCLI(program.opts());
|
|
583
|
-
cli.showStats(
|
|
642
|
+
cli.showStats({
|
|
643
|
+
project: options.project,
|
|
644
|
+
global: options.global
|
|
645
|
+
});
|
|
584
646
|
process.exit(0);
|
|
585
647
|
});
|
|
586
648
|
// Evolution command
|
|
@@ -190,10 +190,10 @@ class MemoryCaptureMiddleware {
|
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
-
// PRIORITY 1: Check for explicit "remember" commands
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
for (const match of
|
|
193
|
+
// PRIORITY 1: Check for explicit "remember" or "recall" commands
|
|
194
|
+
const explicitMemoryRegex = /(?:remember|Remember|recall|Recall)\s+(?:that\s+)?(.+?)(?:[.!?]|$)/gi;
|
|
195
|
+
const explicitMemoryMatches = content.matchAll(explicitMemoryRegex);
|
|
196
|
+
for (const match of explicitMemoryMatches) {
|
|
197
197
|
const memoryContent = match[1].trim();
|
|
198
198
|
if (memoryContent) {
|
|
199
199
|
memories.push({
|
|
@@ -201,7 +201,7 @@ class MemoryCaptureMiddleware {
|
|
|
201
201
|
content: memoryContent,
|
|
202
202
|
data: {
|
|
203
203
|
raw: memoryContent,
|
|
204
|
-
source: '
|
|
204
|
+
source: 'explicit_memory_command',
|
|
205
205
|
confidence: 1.0
|
|
206
206
|
},
|
|
207
207
|
confidence: 1.0, // Always highest confidence
|
|
@@ -230,7 +230,8 @@ class MemoryCaptureMiddleware {
|
|
|
230
230
|
const matches = content.matchAll(regex);
|
|
231
231
|
for (const match of matches) {
|
|
232
232
|
// Skip if this was already captured as explicit memory
|
|
233
|
-
|
|
233
|
+
const lower = match[0].toLowerCase();
|
|
234
|
+
if (lower.includes('remember') || lower.includes('recall'))
|
|
234
235
|
continue;
|
|
235
236
|
memories.push({
|
|
236
237
|
type: pattern.type,
|
|
@@ -86,6 +86,11 @@ class MemoryTools {
|
|
|
86
86
|
metadata: {
|
|
87
87
|
type: 'object',
|
|
88
88
|
description: 'Optional metadata for the memory'
|
|
89
|
+
},
|
|
90
|
+
scope: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
enum: ['universal', 'project'],
|
|
93
|
+
description: 'Memory scope: "universal" (available in all projects) or "project" (current project only). Default: unscoped (available everywhere for backward compatibility)'
|
|
89
94
|
}
|
|
90
95
|
},
|
|
91
96
|
required: ['content']
|
|
@@ -139,7 +144,11 @@ class MemoryTools {
|
|
|
139
144
|
},
|
|
140
145
|
projectId: {
|
|
141
146
|
type: 'string',
|
|
142
|
-
description: 'Filter by project ID'
|
|
147
|
+
description: 'Filter by project ID (includes universal memories)'
|
|
148
|
+
},
|
|
149
|
+
globalSearch: {
|
|
150
|
+
type: 'boolean',
|
|
151
|
+
description: 'Search all projects (ignores projectId filter)'
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
},
|
|
@@ -264,7 +273,7 @@ class MemoryTools {
|
|
|
264
273
|
}
|
|
265
274
|
async handleStoreMemory(input, context) {
|
|
266
275
|
try {
|
|
267
|
-
const { content, metadata } = input;
|
|
276
|
+
const { content, metadata, scope } = input;
|
|
268
277
|
if (!content || typeof content !== 'string') {
|
|
269
278
|
throw new Error('Content is required and must be a string');
|
|
270
279
|
}
|
|
@@ -280,8 +289,9 @@ class MemoryTools {
|
|
|
280
289
|
type: 'conversation',
|
|
281
290
|
context: {
|
|
282
291
|
sessionId: context.sessionId,
|
|
283
|
-
projectId: context.projectId,
|
|
284
|
-
timestamp: context.timestamp
|
|
292
|
+
projectId: scope === 'project' ? context.projectId : undefined,
|
|
293
|
+
timestamp: context.timestamp,
|
|
294
|
+
scope: scope || null
|
|
285
295
|
}
|
|
286
296
|
});
|
|
287
297
|
this.logger.info('MemoryTools', 'Memory stored successfully', {
|
|
@@ -379,8 +389,15 @@ class MemoryTools {
|
|
|
379
389
|
if (filters.type && result.type !== filters.type) {
|
|
380
390
|
return false;
|
|
381
391
|
}
|
|
382
|
-
|
|
383
|
-
|
|
392
|
+
// Global search: include all memories
|
|
393
|
+
if (filters.globalSearch) {
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
// Project-scoped search: include project + universal + unscoped memories
|
|
397
|
+
if (filters.projectId) {
|
|
398
|
+
return result.project_id === filters.projectId ||
|
|
399
|
+
result.scope === 'universal' ||
|
|
400
|
+
result.project_id === null;
|
|
384
401
|
}
|
|
385
402
|
return true;
|
|
386
403
|
});
|
package/dist/memory/schema.sql
CHANGED
|
@@ -14,10 +14,12 @@ CREATE TABLE IF NOT EXISTS memories (
|
|
|
14
14
|
superseded_by TEXT,
|
|
15
15
|
superseded_at INTEGER,
|
|
16
16
|
confidence_score REAL,
|
|
17
|
-
sophistication_level INTEGER DEFAULT 1
|
|
17
|
+
sophistication_level INTEGER DEFAULT 1,
|
|
18
|
+
scope TEXT CHECK(scope IN ('universal', 'project', NULL))
|
|
18
19
|
);
|
|
19
20
|
|
|
20
21
|
CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project_id);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_memories_scope_project ON memories(scope, project_id);
|
|
21
23
|
CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
|
|
22
24
|
CREATE INDEX IF NOT EXISTS idx_memories_timestamp ON memories(timestamp);
|
|
23
25
|
CREATE INDEX IF NOT EXISTS idx_memories_preference_key ON memories(preference_key, is_active);
|
package/dist/memory/storage.js
CHANGED
|
@@ -82,10 +82,10 @@ class MemoryStorage {
|
|
|
82
82
|
const stmt = this.db.prepare(`
|
|
83
83
|
INSERT OR REPLACE INTO memories
|
|
84
84
|
(key, value, type, project_id, file_path, timestamp, relevance_score, access_count,
|
|
85
|
-
preference_key, is_active, superseded_by, superseded_at, confidence_score, sophistication_level)
|
|
86
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
85
|
+
preference_key, is_active, superseded_by, superseded_at, confidence_score, sophistication_level, scope)
|
|
86
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
87
87
|
`);
|
|
88
|
-
stmt.run(memory.key, JSON.stringify(memory.value), memory.type, memory.project_id || null, memory.file_path || null, memory.timestamp || Date.now(), memory.relevance_score || 1.0, memory.access_count || 0, memory.preference_key || null, memory.is_active !== undefined ? (memory.is_active ? 1 : 0) : 1, memory.superseded_by || null, memory.superseded_at || null, memory.confidence_score || null, memory.sophistication_level || 1);
|
|
88
|
+
stmt.run(memory.key, JSON.stringify(memory.value), memory.type, memory.project_id || null, memory.file_path || null, memory.timestamp || Date.now(), memory.relevance_score || 1.0, memory.access_count || 0, memory.preference_key || null, memory.is_active !== undefined ? (memory.is_active ? 1 : 0) : 1, memory.superseded_by || null, memory.superseded_at || null, memory.confidence_score || null, memory.sophistication_level || 1, memory.scope || null);
|
|
89
89
|
// Force a WAL checkpoint to ensure the data is written to the main database file
|
|
90
90
|
// This ensures that other processes (like CLI) can see the changes immediately
|
|
91
91
|
this.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
@@ -133,8 +133,9 @@ class MemoryStorage {
|
|
|
133
133
|
let query = 'SELECT * FROM memories WHERE 1=1';
|
|
134
134
|
const params = [];
|
|
135
135
|
if (context.project_id) {
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
// Include project-specific OR universal OR unscoped (NULL) memories
|
|
137
|
+
query += ' AND (project_id = ? OR scope = ? OR project_id IS NULL)';
|
|
138
|
+
params.push(context.project_id, 'universal');
|
|
138
139
|
}
|
|
139
140
|
if (context.file_path) {
|
|
140
141
|
query += ' AND file_path = ?';
|
package/dist/services/config.js
CHANGED
|
@@ -144,7 +144,12 @@ class ConfigService {
|
|
|
144
144
|
return path.join(this.config.logging.directory, logName);
|
|
145
145
|
}
|
|
146
146
|
getProjectId() {
|
|
147
|
-
|
|
147
|
+
if (this.config.project.id) {
|
|
148
|
+
return this.config.project.id;
|
|
149
|
+
}
|
|
150
|
+
// Use directory name (basename) instead of full path
|
|
151
|
+
const rootDir = this.config.project.rootDir;
|
|
152
|
+
return path.basename(rootDir);
|
|
148
153
|
}
|
|
149
154
|
updateConfig(updates) {
|
|
150
155
|
this.config = this.mergeConfig(this.config, updates);
|
package/dist/services/memory.js
CHANGED
|
@@ -35,14 +35,17 @@ class MemoryService {
|
|
|
35
35
|
const percent = ((stats.total / maxMemories) * 100).toFixed(0);
|
|
36
36
|
console.log(`⚠️ Memory usage at ${percent}% (${stats.total}/${maxMemories})`);
|
|
37
37
|
}
|
|
38
|
+
// Detect scope (v0.8.0)
|
|
39
|
+
const scope = this.detectScope(request);
|
|
38
40
|
const memory = {
|
|
39
41
|
key: request.key,
|
|
40
42
|
value: request.value,
|
|
41
43
|
type: request.type,
|
|
42
|
-
project_id: request.context?.projectId || this.config.getProjectId(),
|
|
44
|
+
project_id: scope === 'universal' ? undefined : (request.context?.projectId || this.config.getProjectId()),
|
|
43
45
|
file_path: request.context?.filePath,
|
|
44
46
|
timestamp: request.context?.timestamp || Date.now(),
|
|
45
|
-
relevance_score: request.relevanceScore || 1.0
|
|
47
|
+
relevance_score: request.relevanceScore || 1.0,
|
|
48
|
+
scope: scope
|
|
46
49
|
};
|
|
47
50
|
// Auto-classify sophistication level (v0.7.0)
|
|
48
51
|
memory.sophistication_level = this.evolution.classifySophistication(memory);
|
|
@@ -354,6 +357,37 @@ class MemoryService {
|
|
|
354
357
|
return false;
|
|
355
358
|
}
|
|
356
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Detect memory scope from request (v0.8.0)
|
|
362
|
+
* @private
|
|
363
|
+
*/
|
|
364
|
+
detectScope(request) {
|
|
365
|
+
// Check explicit scope in context
|
|
366
|
+
if (request.context?.scope) {
|
|
367
|
+
return request.context.scope;
|
|
368
|
+
}
|
|
369
|
+
// Extract content for analysis
|
|
370
|
+
const content = typeof request.value === 'string'
|
|
371
|
+
? request.value
|
|
372
|
+
: JSON.stringify(request.value);
|
|
373
|
+
const lowerContent = content.toLowerCase();
|
|
374
|
+
// Explicit user indicators for universal scope
|
|
375
|
+
if (lowerContent.includes('remember everywhere') ||
|
|
376
|
+
lowerContent.includes('for all projects') ||
|
|
377
|
+
lowerContent.includes('globally') ||
|
|
378
|
+
lowerContent.includes('always use')) {
|
|
379
|
+
return 'universal';
|
|
380
|
+
}
|
|
381
|
+
// Explicit user indicators for project scope
|
|
382
|
+
if (lowerContent.includes('for this project') ||
|
|
383
|
+
lowerContent.includes('project-specific') ||
|
|
384
|
+
lowerContent.includes('only here') ||
|
|
385
|
+
lowerContent.includes('in this project')) {
|
|
386
|
+
return 'project';
|
|
387
|
+
}
|
|
388
|
+
// Default: unscoped (null) for backward compatibility
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
357
391
|
/**
|
|
358
392
|
* Close database connection
|
|
359
393
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-recall",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "Persistent memory for Claude Code with automatic capture, failure learning,
|
|
3
|
+
"version": "0.7.2",
|
|
4
|
+
"description": "Persistent memory for Claude Code with automatic capture, failure learning, sophistication tracking, and project scoping via MCP server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"claude-recall": "dist/cli/claude-recall-cli.js"
|