@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.
- package/dist/database.d.ts +6 -0
- package/dist/database.js +65 -7
- package/package.json +1 -1
package/dist/database.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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.
|
|
397
|
+
return this.dbPath;
|
|
340
398
|
}
|
|
341
399
|
close() {
|
|
342
400
|
this.db.close();
|
package/package.json
CHANGED