@esparkman/pensieve 0.2.1 → 0.3.1
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/README.md +61 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +187 -0
- package/dist/database.d.ts +2 -0
- package/dist/database.js +10 -0
- package/dist/index.js +31 -1
- package/hooks/README.md +146 -0
- package/hooks/settings.json +44 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -224,6 +224,67 @@ Summaries of work sessions for continuity.
|
|
|
224
224
|
### Open Questions
|
|
225
225
|
Unresolved blockers or questions to address.
|
|
226
226
|
|
|
227
|
+
## Hooks Integration
|
|
228
|
+
|
|
229
|
+
Pensieve includes a CLI that integrates with Claude Code's hooks system for automatic context preservation.
|
|
230
|
+
|
|
231
|
+
### Quick Setup
|
|
232
|
+
|
|
233
|
+
Copy the example hooks configuration:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
cp node_modules/@esparkman/pensieve/hooks/settings.json ~/.claude/settings.json
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Or add to your existing settings:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"hooks": {
|
|
244
|
+
"PreCompact": [
|
|
245
|
+
{
|
|
246
|
+
"matcher": "auto",
|
|
247
|
+
"hooks": [{ "type": "command", "command": "npx @esparkman/pensieve auto-save" }]
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"matcher": "manual",
|
|
251
|
+
"hooks": [{ "type": "command", "command": "npx @esparkman/pensieve auto-save" }]
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"SessionStart": [
|
|
255
|
+
{
|
|
256
|
+
"matcher": "compact",
|
|
257
|
+
"hooks": [{ "type": "command", "command": "npx @esparkman/pensieve load-context" }]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"matcher": "resume",
|
|
261
|
+
"hooks": [{ "type": "command", "command": "npx @esparkman/pensieve load-context" }]
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### CLI Commands
|
|
269
|
+
|
|
270
|
+
| Command | Purpose |
|
|
271
|
+
|---------|---------|
|
|
272
|
+
| `pensieve auto-save` | Save session snapshot (for PreCompact hooks) |
|
|
273
|
+
| `pensieve load-context` | Output last session context to stdout |
|
|
274
|
+
| `pensieve status` | Show database location and counts |
|
|
275
|
+
|
|
276
|
+
### CLI Options
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# Auto-save with custom summary
|
|
280
|
+
pensieve auto-save --summary "Completed auth feature" --wip "Testing in progress"
|
|
281
|
+
|
|
282
|
+
# Load context as JSON
|
|
283
|
+
pensieve load-context --format json
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
See [hooks/README.md](hooks/README.md) for detailed configuration options.
|
|
287
|
+
|
|
227
288
|
## Development
|
|
228
289
|
|
|
229
290
|
```bash
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { MemoryDatabase } from './database.js';
|
|
4
|
+
// Check if we should run in MCP server mode (no CLI arguments)
|
|
5
|
+
// This maintains backward compatibility with existing MCP registrations
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
if (args.length === 0) {
|
|
8
|
+
// No arguments - run MCP server
|
|
9
|
+
import('./index.js');
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
// Arguments provided - run CLI
|
|
13
|
+
runCLI();
|
|
14
|
+
}
|
|
15
|
+
async function runCLI() {
|
|
16
|
+
const program = new Command();
|
|
17
|
+
program
|
|
18
|
+
.name('pensieve')
|
|
19
|
+
.description('Persistent memory CLI for Claude Code')
|
|
20
|
+
.version('0.2.1');
|
|
21
|
+
program
|
|
22
|
+
.command('auto-save')
|
|
23
|
+
.description('Save a minimal session snapshot (for PreCompact hooks)')
|
|
24
|
+
.option('-s, --summary <text>', 'Session summary')
|
|
25
|
+
.option('-w, --wip <text>', 'Work in progress description')
|
|
26
|
+
.option('-n, --next <text>', 'Next steps')
|
|
27
|
+
.action(async (options) => {
|
|
28
|
+
try {
|
|
29
|
+
const db = await MemoryDatabase.create();
|
|
30
|
+
// Get or create current session
|
|
31
|
+
const session = db.getCurrentSession();
|
|
32
|
+
let sessionId;
|
|
33
|
+
if (!session) {
|
|
34
|
+
sessionId = db.startSession();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
sessionId = session.id;
|
|
38
|
+
}
|
|
39
|
+
// Build summary - use provided or generate auto-save message
|
|
40
|
+
const timestamp = new Date().toISOString();
|
|
41
|
+
const summary = options.summary || `Auto-save before compaction at ${timestamp}`;
|
|
42
|
+
const wip = options.wip || undefined;
|
|
43
|
+
const nextSteps = options.next || undefined;
|
|
44
|
+
// End the session with the summary
|
|
45
|
+
db.endSession(sessionId, summary, wip, nextSteps);
|
|
46
|
+
// Output confirmation to stderr (stdout reserved for data)
|
|
47
|
+
console.error(`[Pensieve] Session saved: ${summary}`);
|
|
48
|
+
db.close();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error(`[Pensieve] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
program
|
|
57
|
+
.command('load-context')
|
|
58
|
+
.description('Output last session context to stdout (for SessionStart hooks)')
|
|
59
|
+
.option('-f, --format <type>', 'Output format: text or json', 'text')
|
|
60
|
+
.action(async (options) => {
|
|
61
|
+
try {
|
|
62
|
+
const db = await MemoryDatabase.create();
|
|
63
|
+
const lastSession = db.getLastSession();
|
|
64
|
+
const decisions = db.getRecentDecisions(10);
|
|
65
|
+
const prefs = db.getAllPreferences();
|
|
66
|
+
const questions = db.getOpenQuestions();
|
|
67
|
+
if (options.format === 'json') {
|
|
68
|
+
const context = {
|
|
69
|
+
lastSession: lastSession ? {
|
|
70
|
+
started_at: lastSession.started_at,
|
|
71
|
+
ended_at: lastSession.ended_at,
|
|
72
|
+
summary: lastSession.summary,
|
|
73
|
+
work_in_progress: lastSession.work_in_progress,
|
|
74
|
+
next_steps: lastSession.next_steps,
|
|
75
|
+
key_files: lastSession.key_files,
|
|
76
|
+
} : null,
|
|
77
|
+
decisions: decisions.map(d => ({
|
|
78
|
+
topic: d.topic,
|
|
79
|
+
decision: d.decision,
|
|
80
|
+
rationale: d.rationale,
|
|
81
|
+
})),
|
|
82
|
+
preferences: prefs.map(p => ({
|
|
83
|
+
category: p.category,
|
|
84
|
+
key: p.key,
|
|
85
|
+
value: p.value,
|
|
86
|
+
})),
|
|
87
|
+
openQuestions: questions.map(q => ({
|
|
88
|
+
id: q.id,
|
|
89
|
+
question: q.question,
|
|
90
|
+
context: q.context,
|
|
91
|
+
})),
|
|
92
|
+
};
|
|
93
|
+
console.log(JSON.stringify(context, null, 2));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Text format for hook injection
|
|
97
|
+
let output = '';
|
|
98
|
+
if (lastSession?.ended_at) {
|
|
99
|
+
output += '## Previous Session Context\n\n';
|
|
100
|
+
if (lastSession.summary) {
|
|
101
|
+
output += `**Last Session:** ${lastSession.summary}\n\n`;
|
|
102
|
+
}
|
|
103
|
+
if (lastSession.work_in_progress) {
|
|
104
|
+
output += `**Work in Progress:** ${lastSession.work_in_progress}\n\n`;
|
|
105
|
+
}
|
|
106
|
+
if (lastSession.next_steps) {
|
|
107
|
+
output += `**Next Steps:** ${lastSession.next_steps}\n\n`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (decisions.length > 0) {
|
|
111
|
+
output += '## Key Decisions\n\n';
|
|
112
|
+
decisions.forEach(d => {
|
|
113
|
+
output += `- **${d.topic}:** ${d.decision}\n`;
|
|
114
|
+
});
|
|
115
|
+
output += '\n';
|
|
116
|
+
}
|
|
117
|
+
if (prefs.length > 0) {
|
|
118
|
+
output += '## Preferences\n\n';
|
|
119
|
+
prefs.forEach(p => {
|
|
120
|
+
output += `- **${p.category}/${p.key}:** ${p.value}\n`;
|
|
121
|
+
});
|
|
122
|
+
output += '\n';
|
|
123
|
+
}
|
|
124
|
+
if (questions.length > 0) {
|
|
125
|
+
output += '## Open Questions\n\n';
|
|
126
|
+
questions.forEach(q => {
|
|
127
|
+
output += `- [#${q.id}] ${q.question}\n`;
|
|
128
|
+
});
|
|
129
|
+
output += '\n';
|
|
130
|
+
}
|
|
131
|
+
if (output) {
|
|
132
|
+
console.log(output.trim());
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
console.log('No previous context found.');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
db.close();
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.error(`[Pensieve] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
program
|
|
147
|
+
.command('status')
|
|
148
|
+
.description('Show database status and counts')
|
|
149
|
+
.action(async () => {
|
|
150
|
+
try {
|
|
151
|
+
const db = await MemoryDatabase.create();
|
|
152
|
+
const decisions = db.getRecentDecisions(1000);
|
|
153
|
+
const prefs = db.getAllPreferences();
|
|
154
|
+
const entities = db.getAllEntities();
|
|
155
|
+
const questions = db.getOpenQuestions();
|
|
156
|
+
const lastSession = db.getLastSession();
|
|
157
|
+
console.log('Pensieve Status');
|
|
158
|
+
console.log('===============');
|
|
159
|
+
console.log(`Database: ${db.getPath()}`);
|
|
160
|
+
console.log('');
|
|
161
|
+
console.log('Counts:');
|
|
162
|
+
console.log(` Decisions: ${decisions.length}`);
|
|
163
|
+
console.log(` Preferences: ${prefs.length}`);
|
|
164
|
+
console.log(` Entities: ${entities.length}`);
|
|
165
|
+
console.log(` Open Questions: ${questions.length}`);
|
|
166
|
+
console.log('');
|
|
167
|
+
if (lastSession) {
|
|
168
|
+
console.log('Last Session:');
|
|
169
|
+
console.log(` Started: ${lastSession.started_at}`);
|
|
170
|
+
console.log(` Ended: ${lastSession.ended_at || 'In progress'}`);
|
|
171
|
+
if (lastSession.summary) {
|
|
172
|
+
console.log(` Summary: ${lastSession.summary.substring(0, 80)}${lastSession.summary.length > 80 ? '...' : ''}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
console.log('No sessions recorded yet.');
|
|
177
|
+
}
|
|
178
|
+
db.close();
|
|
179
|
+
process.exit(0);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.error(`[Pensieve] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
program.parse();
|
|
187
|
+
}
|
package/dist/database.d.ts
CHANGED
|
@@ -106,6 +106,8 @@ export declare class MemoryDatabase {
|
|
|
106
106
|
addDiscovery(discovery: Discovery): number;
|
|
107
107
|
searchDiscoveries(query: string): Discovery[];
|
|
108
108
|
getDiscoveriesByCategory(category: string): Discovery[];
|
|
109
|
+
getAllDiscoveries(): Discovery[];
|
|
110
|
+
getRecentDiscoveries(limit?: number): Discovery[];
|
|
109
111
|
upsertEntity(entity: Entity): void;
|
|
110
112
|
getEntity(name: string): Entity | undefined;
|
|
111
113
|
getAllEntities(): Entity[];
|
package/dist/database.js
CHANGED
|
@@ -343,6 +343,16 @@ export class MemoryDatabase {
|
|
|
343
343
|
SELECT * FROM discoveries WHERE category = ? ORDER BY name
|
|
344
344
|
`, [category]);
|
|
345
345
|
}
|
|
346
|
+
getAllDiscoveries() {
|
|
347
|
+
return this.queryAll(`
|
|
348
|
+
SELECT * FROM discoveries ORDER BY discovered_at DESC
|
|
349
|
+
`);
|
|
350
|
+
}
|
|
351
|
+
getRecentDiscoveries(limit = 10) {
|
|
352
|
+
return this.queryAll(`
|
|
353
|
+
SELECT * FROM discoveries ORDER BY discovered_at DESC LIMIT ?
|
|
354
|
+
`, [limit]);
|
|
355
|
+
}
|
|
346
356
|
// Entity methods
|
|
347
357
|
upsertEntity(entity) {
|
|
348
358
|
this.db.run(`
|
package/dist/index.js
CHANGED
|
@@ -322,6 +322,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
322
322
|
result = 'No preferences found.';
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
else if (type === 'discoveries') {
|
|
326
|
+
const discoveries = category ? db.getDiscoveriesByCategory(category) : db.getAllDiscoveries();
|
|
327
|
+
if (discoveries.length > 0) {
|
|
328
|
+
result = `## Discoveries${category ? ` (${category})` : ''}\n\n`;
|
|
329
|
+
discoveries.forEach(d => {
|
|
330
|
+
result += `- **${d.name}** [${d.category}]: ${d.description || 'No description'}${d.location ? ` (${d.location})` : ''}\n`;
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
result = 'No discoveries found.';
|
|
335
|
+
}
|
|
336
|
+
}
|
|
325
337
|
else if (type === 'questions') {
|
|
326
338
|
const questions = db.getOpenQuestions();
|
|
327
339
|
if (questions.length > 0) {
|
|
@@ -441,6 +453,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
441
453
|
});
|
|
442
454
|
result += '\n';
|
|
443
455
|
}
|
|
456
|
+
const discoveries = db.getRecentDiscoveries(5);
|
|
457
|
+
if (discoveries.length > 0) {
|
|
458
|
+
result += `### Recent Discoveries\n`;
|
|
459
|
+
discoveries.forEach(d => {
|
|
460
|
+
result += `- **${d.name}** [${d.category}]: ${d.description || 'No description'}${d.location ? ` (${d.location})` : ''}\n`;
|
|
461
|
+
});
|
|
462
|
+
result += '\n';
|
|
463
|
+
}
|
|
444
464
|
const questions = db.getOpenQuestions();
|
|
445
465
|
if (questions.length > 0) {
|
|
446
466
|
result += `### Open Questions\n`;
|
|
@@ -491,6 +511,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
491
511
|
case 'pensieve_status': {
|
|
492
512
|
const decisions = db.getRecentDecisions(100);
|
|
493
513
|
const prefs = db.getAllPreferences();
|
|
514
|
+
const discoveries = db.getAllDiscoveries();
|
|
494
515
|
const entities = db.getAllEntities();
|
|
495
516
|
const questions = db.getOpenQuestions();
|
|
496
517
|
const lastSession = db.getLastSession();
|
|
@@ -499,6 +520,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
499
520
|
result += `**Counts:**\n`;
|
|
500
521
|
result += `- Decisions: ${decisions.length}\n`;
|
|
501
522
|
result += `- Preferences: ${prefs.length}\n`;
|
|
523
|
+
result += `- Discoveries: ${discoveries.length}\n`;
|
|
502
524
|
result += `- Entities: ${entities.length}\n`;
|
|
503
525
|
result += `- Open Questions: ${questions.length}\n`;
|
|
504
526
|
result += `- Last Session: ${lastSession ? lastSession.started_at : 'None'}\n`;
|
|
@@ -575,8 +597,9 @@ function outputPriorContext() {
|
|
|
575
597
|
const lastSession = db.getLastSession();
|
|
576
598
|
const decisions = db.getRecentDecisions(5);
|
|
577
599
|
const prefs = db.getAllPreferences();
|
|
600
|
+
const discoveries = db.getRecentDiscoveries(5);
|
|
578
601
|
const questions = db.getOpenQuestions();
|
|
579
|
-
const hasContent = lastSession?.summary || decisions.length > 0 || prefs.length > 0 || questions.length > 0;
|
|
602
|
+
const hasContent = lastSession?.summary || decisions.length > 0 || prefs.length > 0 || discoveries.length > 0 || questions.length > 0;
|
|
580
603
|
if (!hasContent) {
|
|
581
604
|
console.error('🧙 Pensieve ready (no prior context yet)');
|
|
582
605
|
return;
|
|
@@ -615,6 +638,13 @@ function outputPriorContext() {
|
|
|
615
638
|
console.error(` • ${p.category}/${p.key}: ${p.value}`);
|
|
616
639
|
});
|
|
617
640
|
}
|
|
641
|
+
if (discoveries.length > 0) {
|
|
642
|
+
console.error('');
|
|
643
|
+
console.error('🔍 DISCOVERIES:');
|
|
644
|
+
discoveries.forEach(d => {
|
|
645
|
+
console.error(` • [${d.category}] ${d.name}${d.location ? ` at ${d.location}` : ''}`);
|
|
646
|
+
});
|
|
647
|
+
}
|
|
618
648
|
if (questions.length > 0) {
|
|
619
649
|
console.error('');
|
|
620
650
|
console.error('❓ OPEN QUESTIONS:');
|
package/hooks/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Pensieve Hooks Integration
|
|
2
|
+
|
|
3
|
+
Pensieve can integrate with Claude Code's hooks system to automatically save and restore session context during compaction and session resumption.
|
|
4
|
+
|
|
5
|
+
## Quick Setup
|
|
6
|
+
|
|
7
|
+
1. Copy the example `settings.json` to your Claude Code settings directory:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# macOS/Linux
|
|
11
|
+
cp hooks/settings.json ~/.claude/settings.json
|
|
12
|
+
|
|
13
|
+
# Or merge with existing settings if you have them
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
2. The hooks will automatically:
|
|
17
|
+
- **PreCompact**: Save a session snapshot before context compaction
|
|
18
|
+
- **SessionStart**: Load previous context when resuming a session
|
|
19
|
+
|
|
20
|
+
## CLI Commands
|
|
21
|
+
|
|
22
|
+
Pensieve provides these CLI commands for hook integration:
|
|
23
|
+
|
|
24
|
+
### `pensieve auto-save`
|
|
25
|
+
|
|
26
|
+
Saves a minimal session snapshot. Used by PreCompact hooks.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Basic auto-save (timestamps the save automatically)
|
|
30
|
+
pensieve auto-save
|
|
31
|
+
|
|
32
|
+
# With custom summary
|
|
33
|
+
pensieve auto-save --summary "Implemented user auth"
|
|
34
|
+
|
|
35
|
+
# With work-in-progress and next steps
|
|
36
|
+
pensieve auto-save \
|
|
37
|
+
--summary "Working on auth" \
|
|
38
|
+
--wip "OAuth flow partially complete" \
|
|
39
|
+
--next "Add refresh token handling"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `pensieve load-context`
|
|
43
|
+
|
|
44
|
+
Outputs the last session context to stdout. Used by SessionStart hooks.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Text format (default) - for injection into prompts
|
|
48
|
+
pensieve load-context
|
|
49
|
+
|
|
50
|
+
# JSON format - for programmatic use
|
|
51
|
+
pensieve load-context --format json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `pensieve status`
|
|
55
|
+
|
|
56
|
+
Shows database location and counts.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pensieve status
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Hook Configuration
|
|
63
|
+
|
|
64
|
+
The hooks system uses matchers to determine when to run:
|
|
65
|
+
|
|
66
|
+
### PreCompact Hooks
|
|
67
|
+
|
|
68
|
+
Run before Claude Code compacts the conversation context:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"hooks": {
|
|
73
|
+
"PreCompact": [
|
|
74
|
+
{
|
|
75
|
+
"matcher": "auto",
|
|
76
|
+
"hooks": [{ "type": "command", "command": "pensieve auto-save" }]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"matcher": "manual",
|
|
80
|
+
"hooks": [{ "type": "command", "command": "pensieve auto-save" }]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### SessionStart Hooks
|
|
88
|
+
|
|
89
|
+
Run when starting or resuming a session:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"hooks": {
|
|
94
|
+
"SessionStart": [
|
|
95
|
+
{
|
|
96
|
+
"matcher": "compact",
|
|
97
|
+
"hooks": [{ "type": "command", "command": "pensieve load-context" }]
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"matcher": "resume",
|
|
101
|
+
"hooks": [{ "type": "command", "command": "pensieve load-context" }]
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Environment Variables
|
|
109
|
+
|
|
110
|
+
You can control which database Pensieve uses:
|
|
111
|
+
|
|
112
|
+
- `PENSIEVE_DB_PATH` - Explicit path to the database file
|
|
113
|
+
- `PENSIEVE_PROJECT_DIR` - Project directory (uses `$dir/.pensieve/memory.sqlite`)
|
|
114
|
+
|
|
115
|
+
Example hook with project-specific database:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"type": "command",
|
|
120
|
+
"command": "PENSIEVE_PROJECT_DIR=$(pwd) pensieve auto-save"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Troubleshooting
|
|
125
|
+
|
|
126
|
+
### Check if Pensieve is working
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
pensieve status
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Test the hooks manually
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Test auto-save
|
|
136
|
+
pensieve auto-save --summary "Test save"
|
|
137
|
+
|
|
138
|
+
# Test load-context
|
|
139
|
+
pensieve load-context
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Database location
|
|
143
|
+
|
|
144
|
+
By default, Pensieve stores data in:
|
|
145
|
+
- Project-local: `./.pensieve/memory.sqlite` (if `.git` or `.pensieve` exists)
|
|
146
|
+
- Global fallback: `~/.claude-pensieve/memory.sqlite`
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreCompact": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "auto",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "npx @esparkman/pensieve auto-save"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"matcher": "manual",
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "npx @esparkman/pensieve auto-save"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"SessionStart": [
|
|
24
|
+
{
|
|
25
|
+
"matcher": "compact",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "npx @esparkman/pensieve load-context"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"matcher": "resume",
|
|
35
|
+
"hooks": [
|
|
36
|
+
{
|
|
37
|
+
"type": "command",
|
|
38
|
+
"command": "npx @esparkman/pensieve load-context"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@esparkman/pensieve",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Pensieve - persistent memory for Claude Code. Remember decisions, preferences, and context across sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"pensieve": "./dist/
|
|
8
|
+
"pensieve": "./dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
30
|
+
"commander": "^13.0.0",
|
|
30
31
|
"sql.js": "^1.12.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
},
|
|
42
43
|
"files": [
|
|
43
44
|
"dist",
|
|
44
|
-
".claude"
|
|
45
|
+
".claude",
|
|
46
|
+
"hooks"
|
|
45
47
|
]
|
|
46
48
|
}
|