@esparkman/pensieve 0.1.2 → 0.1.3

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.
@@ -65,7 +65,13 @@ export interface OpenQuestion {
65
65
  export declare class MemoryDatabase {
66
66
  private db;
67
67
  private projectPath;
68
+ private dbPath;
68
69
  constructor(projectPath?: string);
70
+ private openDatabase;
71
+ /**
72
+ * Check if the database is writable and reconnect if needed
73
+ */
74
+ ensureWritable(): boolean;
69
75
  private getDbPath;
70
76
  /**
71
77
  * Truncate a string to the maximum field length
package/dist/database.js CHANGED
@@ -15,32 +15,82 @@ export const LIMITS = {
15
15
  export class MemoryDatabase {
16
16
  db;
17
17
  projectPath;
18
+ dbPath;
18
19
  constructor(projectPath) {
19
20
  // Use provided path, or detect from current directory, or use home
20
21
  this.projectPath = projectPath || process.cwd();
21
- const dbPath = this.getDbPath();
22
+ this.dbPath = this.getDbPath();
22
23
  // Ensure directory exists
23
- const dbDir = dirname(dbPath);
24
+ const dbDir = dirname(this.dbPath);
24
25
  if (!existsSync(dbDir)) {
25
26
  mkdirSync(dbDir, { recursive: true });
26
27
  }
27
- this.db = new Database(dbPath);
28
+ this.db = this.openDatabase();
28
29
  this.initSchema();
29
30
  }
31
+ openDatabase() {
32
+ // Always try to open in read-write mode
33
+ // If file doesn't exist, it will be created
34
+ try {
35
+ return new Database(this.dbPath, { fileMustExist: false });
36
+ }
37
+ catch (error) {
38
+ console.error(`[Pensieve] Failed to open database: ${error}`);
39
+ throw error;
40
+ }
41
+ }
42
+ /**
43
+ * Check if the database is writable and reconnect if needed
44
+ */
45
+ ensureWritable() {
46
+ try {
47
+ // Test write capability
48
+ this.db.exec('SELECT 1');
49
+ this.db.prepare('CREATE TABLE IF NOT EXISTS _pensieve_health_check (id INTEGER)').run();
50
+ return true;
51
+ }
52
+ catch (error) {
53
+ const errorMessage = error instanceof Error ? error.message : String(error);
54
+ if (errorMessage.includes('readonly')) {
55
+ console.error('[Pensieve] Database is read-only, attempting reconnection...');
56
+ try {
57
+ this.db.close();
58
+ this.db = this.openDatabase();
59
+ console.error('[Pensieve] Reconnected successfully');
60
+ return true;
61
+ }
62
+ catch (reconnectError) {
63
+ console.error(`[Pensieve] Reconnection failed: ${reconnectError}`);
64
+ return false;
65
+ }
66
+ }
67
+ return false;
68
+ }
69
+ }
30
70
  getDbPath() {
31
- // Check for explicit environment variable override first
71
+ // Check for explicit database path override
32
72
  const envPath = process.env.PENSIEVE_DB_PATH;
33
73
  if (envPath) {
34
74
  console.error(`[Pensieve] Using database from PENSIEVE_DB_PATH: ${envPath}`);
35
75
  return envPath;
36
76
  }
37
- // Try project-local first, then fall back to home directory
77
+ // Check for explicit project path (recommended for MCP server usage)
78
+ // This should be set in the MCP server config to ensure deterministic behavior
79
+ const projectDir = process.env.PENSIEVE_PROJECT_DIR;
80
+ if (projectDir) {
81
+ const projectPath = join(projectDir, '.pensieve', 'memory.sqlite');
82
+ console.error(`[Pensieve] Using project database from PENSIEVE_PROJECT_DIR: ${projectPath}`);
83
+ return projectPath;
84
+ }
85
+ // Fallback: Try project-local first, then fall back to home directory
86
+ // WARNING: Using process.cwd() is unreliable in MCP server context
38
87
  const localPath = join(this.projectPath, '.pensieve', 'memory.sqlite');
39
88
  const globalPath = join(homedir(), '.claude-pensieve', 'memory.sqlite');
40
89
  // If local .pensieve directory exists or we're in a git repo, use local
41
90
  if (existsSync(join(this.projectPath, '.pensieve')) ||
42
91
  existsSync(join(this.projectPath, '.git'))) {
43
- console.error(`[Pensieve] Using project-local database: ${localPath}`);
92
+ console.error(`[Pensieve] WARNING: Using cwd-based path (unreliable): ${localPath}`);
93
+ console.error(`[Pensieve] Set PENSIEVE_PROJECT_DIR for deterministic behavior`);
44
94
  return localPath;
45
95
  }
46
96
  console.error(`[Pensieve] Using global database: ${globalPath}`);
@@ -177,6 +227,7 @@ export class MemoryDatabase {
177
227
  }
178
228
  // Decision methods
179
229
  addDecision(decision) {
230
+ this.ensureWritable();
180
231
  this.pruneIfNeeded();
181
232
  const stmt = this.db.prepare(`
182
233
  INSERT INTO decisions (topic, decision, rationale, alternatives, source)
@@ -205,6 +256,7 @@ export class MemoryDatabase {
205
256
  }
206
257
  // Preference methods
207
258
  setPreference(pref) {
259
+ this.ensureWritable();
208
260
  const stmt = this.db.prepare(`
209
261
  INSERT OR REPLACE INTO preferences (category, key, value, notes, updated_at)
210
262
  VALUES (?, ?, ?, ?, datetime('now'))
@@ -231,6 +283,7 @@ export class MemoryDatabase {
231
283
  }
232
284
  // Discovery methods
233
285
  addDiscovery(discovery) {
286
+ this.ensureWritable();
234
287
  this.pruneIfNeeded();
235
288
  const stmt = this.db.prepare(`
236
289
  INSERT INTO discoveries (category, name, location, description, metadata, confidence)
@@ -257,6 +310,7 @@ export class MemoryDatabase {
257
310
  }
258
311
  // Entity methods
259
312
  upsertEntity(entity) {
313
+ this.ensureWritable();
260
314
  const stmt = this.db.prepare(`
261
315
  INSERT OR REPLACE INTO entities (name, description, relationships, attributes, location, updated_at)
262
316
  VALUES (?, ?, ?, ?, ?, datetime('now'))
@@ -273,11 +327,13 @@ export class MemoryDatabase {
273
327
  }
274
328
  // Session methods
275
329
  startSession() {
330
+ this.ensureWritable();
276
331
  const stmt = this.db.prepare(`INSERT INTO sessions (started_at) VALUES (datetime('now'))`);
277
332
  const result = stmt.run();
278
333
  return result.lastInsertRowid;
279
334
  }
280
335
  endSession(sessionId, summary, workInProgress, nextSteps, keyFiles, tags) {
336
+ this.ensureWritable();
281
337
  this.pruneIfNeeded();
282
338
  const stmt = this.db.prepare(`
283
339
  UPDATE sessions
@@ -305,6 +361,7 @@ export class MemoryDatabase {
305
361
  }
306
362
  // Open questions methods
307
363
  addQuestion(question, context) {
364
+ this.ensureWritable();
308
365
  const stmt = this.db.prepare(`
309
366
  INSERT INTO open_questions (question, context) VALUES (?, ?)
310
367
  `);
@@ -312,6 +369,7 @@ export class MemoryDatabase {
312
369
  return result.lastInsertRowid;
313
370
  }
314
371
  resolveQuestion(id, resolution) {
372
+ this.ensureWritable();
315
373
  const stmt = this.db.prepare(`
316
374
  UPDATE open_questions
317
375
  SET status = 'resolved', resolution = ?, resolved_at = datetime('now')
@@ -336,7 +394,7 @@ export class MemoryDatabase {
336
394
  }
337
395
  // Get database path for debugging
338
396
  getPath() {
339
- return this.getDbPath();
397
+ return this.dbPath;
340
398
  }
341
399
  close() {
342
400
  this.db.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esparkman/pensieve",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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",