Refactor database integration to use bun:sqlite instead of better-sqlite3

- Updated import statements across multiple files to use bun:sqlite.
- Changed database query methods from `prepare` to `query` for consistency.
- Removed dependency on better-sqlite3 from package.json and adjusted package.json to specify bun as the engine.
- Modified hook installation process to eliminate unnecessary dependency installation.
- Updated migration scripts to align with bun:sqlite's API.
This commit is contained in:
Alex Newman
2025-10-15 15:03:43 -04:00
parent b5bfc029c3
commit 2663121d9f
14 changed files with 351 additions and 364 deletions
+2 -3
View File
@@ -136,12 +136,11 @@ Perfect for developers who want their AI assistant to remember project context,
<!-- Prerequisites --> <!-- Prerequisites -->
### :bangbang: Prerequisites ### :bangbang: Prerequisites
This project requires Node.js and works best with Claude Code This project requires Bun runtime and works best with Claude Code
- Node.js >= 18.0.0 - Bun >= 1.0.0 (required for SQLite support)
- Claude Code with MCP support - Claude Code with MCP support
- macOS/Linux (POSIX-compliant system) - macOS/Linux (POSIX-compliant system)
- Bun >= 1.0.0 (optional, for development)
<!-- Installation --> <!-- Installation -->
### :gear: Installation ### :gear: Installation
+9 -5
View File
@@ -43,10 +43,11 @@ async function build() {
const buildCommand = [ const buildCommand = [
'bun build', 'bun build',
'src/bin/cli.ts', 'src/bin/cli.ts',
'--target=node', '--target=bun',
'--outfile=dist/claude-mem.min.js', '--outfile=dist/claude-mem.min.js',
'--minify', '--minify',
'--external @anthropic-ai/claude-agent-sdk', '--external @anthropic-ai/claude-agent-sdk',
'--external bun:sqlite',
`--define __DEFAULT_PACKAGE_VERSION__='"${version}"'` `--define __DEFAULT_PACKAGE_VERSION__='"${version}"'`
].join(' '); ].join(' ');
@@ -58,10 +59,13 @@ async function build() {
// Add shebang to output // Add shebang to output
console.log('\n📝 Adding shebang...'); console.log('\n📝 Adding shebang...');
const distFile = 'dist/claude-mem.min.js'; const distFile = 'dist/claude-mem.min.js';
const content = fs.readFileSync(distFile, 'utf-8'); let content = fs.readFileSync(distFile, 'utf-8');
if (!content.startsWith('#!/usr/bin/env node')) {
fs.writeFileSync(distFile, `#!/usr/bin/env node\n${content}`); // Remove any existing shebangs
} content = content.replace(/^#!.*\n/gm, '');
// Add the bun shebang
fs.writeFileSync(distFile, `#!/usr/bin/env bun\n${content}`);
console.log('✓ Shebang added'); console.log('✓ Shebang added');
// Make executable // Make executable
+193 -194
View File
File diff suppressed because one or more lines are too long
+11 -11
View File
@@ -10,7 +10,7 @@
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import { join, dirname } from 'path'; import { join, dirname } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import Database from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import os from 'os'; import os from 'os';
import fs from 'fs'; import fs from 'fs';
@@ -320,7 +320,7 @@ export function createStreamingSession(db, { claude_session_id, project, user_pr
const timestamp = started_at || new Date().toISOString(); const timestamp = started_at || new Date().toISOString();
const epoch = new Date(timestamp).getTime(); const epoch = new Date(timestamp).getTime();
const stmt = db.prepare(` const stmt = db.query(`
INSERT INTO streaming_sessions ( INSERT INTO streaming_sessions (
claude_session_id, project, user_prompt, started_at, started_at_epoch, status claude_session_id, project, user_prompt, started_at, started_at_epoch, status
) VALUES (?, ?, ?, ?, ?, 'active') ) VALUES (?, ?, ?, ?, ?, 'active')
@@ -328,7 +328,7 @@ export function createStreamingSession(db, { claude_session_id, project, user_pr
const info = stmt.run(claude_session_id, project, user_prompt || null, timestamp, epoch); const info = stmt.run(claude_session_id, project, user_prompt || null, timestamp, epoch);
return db.prepare('SELECT * FROM streaming_sessions WHERE id = ?').get(info.lastInsertRowid); return db.query('SELECT * FROM streaming_sessions WHERE id = ?').get(info.lastInsertRowid);
} }
/** /**
@@ -372,7 +372,7 @@ export function updateStreamingSession(db, id, updates) {
values.push(id); values.push(id);
const stmt = db.prepare(` const stmt = db.query(`
UPDATE streaming_sessions UPDATE streaming_sessions
SET ${parts.join(', ')} SET ${parts.join(', ')}
WHERE id = ? WHERE id = ?
@@ -380,7 +380,7 @@ export function updateStreamingSession(db, id, updates) {
stmt.run(...values); stmt.run(...values);
return db.prepare('SELECT * FROM streaming_sessions WHERE id = ?').get(id); return db.query('SELECT * FROM streaming_sessions WHERE id = ?').get(id);
} }
/** /**
@@ -389,7 +389,7 @@ export function updateStreamingSession(db, id, updates) {
export function getActiveStreamingSessionsForProject(db, project) { export function getActiveStreamingSessionsForProject(db, project) {
ensureStreamingSessionsTable(db); ensureStreamingSessionsTable(db);
const stmt = db.prepare(` const stmt = db.query(`
SELECT * FROM streaming_sessions SELECT * FROM streaming_sessions
WHERE project = ? AND status = 'active' WHERE project = ? AND status = 'active'
ORDER BY started_at_epoch DESC ORDER BY started_at_epoch DESC
@@ -405,7 +405,7 @@ export function markStreamingSessionCompleted(db, id) {
const timestamp = new Date().toISOString(); const timestamp = new Date().toISOString();
const epoch = Date.now(); const epoch = Date.now();
const stmt = db.prepare(` const stmt = db.query(`
UPDATE streaming_sessions UPDATE streaming_sessions
SET status = ?, SET status = ?,
completed_at = ?, completed_at = ?,
@@ -417,7 +417,7 @@ export function markStreamingSessionCompleted(db, id) {
stmt.run('completed', timestamp, epoch, timestamp, epoch, id); stmt.run('completed', timestamp, epoch, timestamp, epoch, id);
return db.prepare('SELECT * FROM streaming_sessions WHERE id = ?').get(id); return db.query('SELECT * FROM streaming_sessions WHERE id = ?').get(id);
} }
/** /**
@@ -459,7 +459,7 @@ export function acquireSessionLock(db, sdkSessionId, lockOwner) {
const timestamp = new Date().toISOString(); const timestamp = new Date().toISOString();
const epoch = Date.now(); const epoch = Date.now();
const stmt = db.prepare(` const stmt = db.query(`
INSERT INTO session_locks (sdk_session_id, locked_by, locked_at, locked_at_epoch) INSERT INTO session_locks (sdk_session_id, locked_by, locked_at, locked_at_epoch)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
`); `);
@@ -478,7 +478,7 @@ export function acquireSessionLock(db, sdkSessionId, lockOwner) {
export function releaseSessionLock(db, sdkSessionId) { export function releaseSessionLock(db, sdkSessionId) {
ensureSessionLocksTable(db); ensureSessionLocksTable(db);
const stmt = db.prepare(` const stmt = db.query(`
DELETE FROM session_locks DELETE FROM session_locks
WHERE sdk_session_id = ? WHERE sdk_session_id = ?
`); `);
@@ -494,7 +494,7 @@ export function cleanupStaleLocks(db) {
const fiveMinutesAgo = Date.now() - (5 * 60 * 1000); const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
const stmt = db.prepare(` const stmt = db.query(`
DELETE FROM session_locks DELETE FROM session_locks
WHERE locked_at_epoch < ? WHERE locked_at_epoch < ?
`); `);
+1 -2
View File
@@ -30,7 +30,7 @@
}, },
"type": "module", "type": "module",
"engines": { "engines": {
"node": ">=18.0.0" "bun": ">=1.0.0"
}, },
"bin": { "bin": {
"claude-mem": "./dist/claude-mem.min.js" "claude-mem": "./dist/claude-mem.min.js"
@@ -44,7 +44,6 @@
"dependencies": { "dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.1.0", "@anthropic-ai/claude-agent-sdk": "^0.1.0",
"@clack/prompts": "^0.11.0", "@clack/prompts": "^0.11.0",
"better-sqlite3": "^11.8.1",
"boxen": "^8.0.1", "boxen": "^8.0.1",
"chalk": "^5.6.0", "chalk": "^5.6.0",
"commander": "^14.0.0", "commander": "^14.0.0",
+2 -15
View File
@@ -262,25 +262,12 @@ function writeHookFiles(timeout: number = 180000): void {
}; };
writeFileSync(join(runtimeHooksDir, 'config.json'), JSON.stringify(hookConfig, null, 2)); writeFileSync(join(runtimeHooksDir, 'config.json'), JSON.stringify(hookConfig, null, 2));
// Create package.json and install dependencies in hooks directory // Create package.json in hooks directory (no dependencies needed with bun:sqlite)
const hookPackageJson = { const hookPackageJson = {
name: "claude-mem-hooks", name: "claude-mem-hooks",
type: "module", type: "module"
dependencies: {
"better-sqlite3": "^11.8.0"
}
}; };
writeFileSync(join(runtimeHooksDir, 'package.json'), JSON.stringify(hookPackageJson, null, 2)); writeFileSync(join(runtimeHooksDir, 'package.json'), JSON.stringify(hookPackageJson, null, 2));
// Install dependencies
try {
execSync('npm install --silent', {
cwd: runtimeHooksDir,
stdio: 'pipe'
});
} catch (error) {
// Silent fail - hooks might still work if better-sqlite3 is globally available
}
} }
+28 -29
View File
@@ -1,22 +1,25 @@
import Database from 'better-sqlite3'; import { Database as BunDatabase } from 'bun:sqlite';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { PathDiscovery } from '../path-discovery.js'; import { PathDiscovery } from '../path-discovery.js';
// Type alias for better-sqlite3 compatibility
type Database = BunDatabase;
export interface Migration { export interface Migration {
version: number; version: number;
up: (db: Database.Database) => void; up: (db: Database) => void;
down?: (db: Database.Database) => void; down?: (db: Database) => void;
} }
let dbInstance: Database.Database | null = null; let dbInstance: Database | null = null;
/** /**
* SQLite Database singleton with migration support and optimized settings * SQLite Database singleton with migration support and optimized settings
*/ */
export class DatabaseManager { export class DatabaseManager {
private static instance: DatabaseManager; private static instance: DatabaseManager;
private db: Database.Database | null = null; private db: Database | null = null;
private migrations: Migration[] = []; private migrations: Migration[] = [];
static getInstance(): DatabaseManager { static getInstance(): DatabaseManager {
@@ -38,7 +41,7 @@ export class DatabaseManager {
/** /**
* Initialize database connection with optimized settings * Initialize database connection with optimized settings
*/ */
async initialize(): Promise<Database.Database> { async initialize(): Promise<Database> {
if (this.db) { if (this.db) {
return this.db; return this.db;
} }
@@ -48,15 +51,15 @@ export class DatabaseManager {
fs.mkdirSync(dataDir, { recursive: true }); fs.mkdirSync(dataDir, { recursive: true });
const dbPath = path.join(dataDir, 'claude-mem.db'); const dbPath = path.join(dataDir, 'claude-mem.db');
this.db = new Database(dbPath); this.db = new BunDatabase(dbPath, { create: true, readwrite: true });
// Apply optimized SQLite settings // Apply optimized SQLite settings
this.db.pragma('journal_mode = WAL'); this.db.run('PRAGMA journal_mode = WAL');
this.db.pragma('synchronous = NORMAL'); this.db.run('PRAGMA synchronous = NORMAL');
this.db.pragma('foreign_keys = ON'); this.db.run('PRAGMA foreign_keys = ON');
this.db.pragma('temp_store = memory'); this.db.run('PRAGMA temp_store = memory');
this.db.pragma('mmap_size = 268435456'); // 256MB this.db.run('PRAGMA mmap_size = 268435456'); // 256MB
this.db.pragma('cache_size = 10000'); this.db.run('PRAGMA cache_size = 10000');
// Initialize schema_versions table // Initialize schema_versions table
this.initializeSchemaVersions(); this.initializeSchemaVersions();
@@ -71,7 +74,7 @@ export class DatabaseManager {
/** /**
* Get the current database connection * Get the current database connection
*/ */
getConnection(): Database.Database { getConnection(): Database {
if (!this.db) { if (!this.db) {
throw new Error('Database not initialized. Call initialize() first.'); throw new Error('Database not initialized. Call initialize() first.');
} }
@@ -81,7 +84,7 @@ export class DatabaseManager {
/** /**
* Execute a function within a transaction * Execute a function within a transaction
*/ */
withTransaction<T>(fn: (db: Database.Database) => T): T { withTransaction<T>(fn: (db: Database) => T): T {
const db = this.getConnection(); const db = this.getConnection();
const transaction = db.transaction(fn); const transaction = db.transaction(fn);
return transaction(db); return transaction(db);
@@ -104,7 +107,7 @@ export class DatabaseManager {
private initializeSchemaVersions(): void { private initializeSchemaVersions(): void {
if (!this.db) return; if (!this.db) return;
this.db.exec(` this.db.run(`
CREATE TABLE IF NOT EXISTS schema_versions ( CREATE TABLE IF NOT EXISTS schema_versions (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
version INTEGER UNIQUE NOT NULL, version INTEGER UNIQUE NOT NULL,
@@ -119,10 +122,8 @@ export class DatabaseManager {
private async runMigrations(): Promise<void> { private async runMigrations(): Promise<void> {
if (!this.db) return; if (!this.db) return;
const appliedVersions = this.db const query = this.db.query('SELECT version FROM schema_versions ORDER BY version');
.prepare('SELECT version FROM schema_versions ORDER BY version') const appliedVersions = query.all().map((row: any) => row.version);
.all()
.map((row: any) => row.version);
const maxApplied = appliedVersions.length > 0 ? Math.max(...appliedVersions) : 0; const maxApplied = appliedVersions.length > 0 ? Math.max(...appliedVersions) : 0;
@@ -133,9 +134,8 @@ export class DatabaseManager {
const transaction = this.db.transaction(() => { const transaction = this.db.transaction(() => {
migration.up(this.db!); migration.up(this.db!);
this.db! const insertQuery = this.db!.query('INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)');
.prepare('INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)') insertQuery.run(migration.version, new Date().toISOString());
.run(migration.version, new Date().toISOString());
}); });
transaction(); transaction();
@@ -150,9 +150,8 @@ export class DatabaseManager {
getCurrentVersion(): number { getCurrentVersion(): number {
if (!this.db) return 0; if (!this.db) return 0;
const result = this.db const query = this.db.query('SELECT MAX(version) as version FROM schema_versions');
.prepare('SELECT MAX(version) as version FROM schema_versions') const result = query.get() as { version: number } | undefined;
.get() as { version: number } | undefined;
return result?.version || 0; return result?.version || 0;
} }
@@ -161,7 +160,7 @@ export class DatabaseManager {
/** /**
* Get the global database instance (for compatibility) * Get the global database instance (for compatibility)
*/ */
export function getDatabase(): Database.Database { export function getDatabase(): Database {
if (!dbInstance) { if (!dbInstance) {
throw new Error('Database not initialized. Call DatabaseManager.getInstance().initialize() first.'); throw new Error('Database not initialized. Call DatabaseManager.getInstance().initialize() first.');
} }
@@ -171,9 +170,9 @@ export function getDatabase(): Database.Database {
/** /**
* Initialize and get database manager * Initialize and get database manager
*/ */
export async function initializeDatabase(): Promise<Database.Database> { export async function initializeDatabase(): Promise<Database> {
const manager = DatabaseManager.getInstance(); const manager = DatabaseManager.getInstance();
return await manager.initialize(); return await manager.initialize();
} }
export { Database }; export { BunDatabase as Database };
+17 -17
View File
@@ -1,4 +1,4 @@
import { Database } from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import { getDatabase } from './Database.js'; import { getDatabase } from './Database.js';
import { DiagnosticRow, DiagnosticInput, normalizeTimestamp } from './types.js'; import { DiagnosticRow, DiagnosticInput, normalizeTimestamp } from './types.js';
@@ -18,7 +18,7 @@ export class DiagnosticsStore {
create(input: DiagnosticInput): DiagnosticRow { create(input: DiagnosticInput): DiagnosticRow {
const { isoString, epoch } = normalizeTimestamp(input.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
INSERT INTO diagnostics ( INSERT INTO diagnostics (
session_id, message, severity, created_at, created_at_epoch, project, origin session_id, message, severity, created_at, created_at_epoch, project, origin
) VALUES (?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?)
@@ -41,7 +41,7 @@ export class DiagnosticsStore {
* Get diagnostic by primary key * Get diagnostic by primary key
*/ */
getById(id: number): DiagnosticRow | null { getById(id: number): DiagnosticRow | null {
const stmt = this.db.prepare('SELECT * FROM diagnostics WHERE id = ?'); const stmt = this.db.query('SELECT * FROM diagnostics WHERE id = ?');
return stmt.get(id) as DiagnosticRow || null; return stmt.get(id) as DiagnosticRow || null;
} }
@@ -49,7 +49,7 @@ export class DiagnosticsStore {
* Get diagnostics for a specific session * Get diagnostics for a specific session
*/ */
getBySessionId(sessionId: string): DiagnosticRow[] { getBySessionId(sessionId: string): DiagnosticRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM diagnostics SELECT * FROM diagnostics
WHERE session_id = ? WHERE session_id = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -61,7 +61,7 @@ export class DiagnosticsStore {
* Get recent diagnostics for a project * Get recent diagnostics for a project
*/ */
getRecentForProject(project: string, limit = 10): DiagnosticRow[] { getRecentForProject(project: string, limit = 10): DiagnosticRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM diagnostics SELECT * FROM diagnostics
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -74,7 +74,7 @@ export class DiagnosticsStore {
* Get recent diagnostics across all projects * Get recent diagnostics across all projects
*/ */
getRecent(limit = 10): DiagnosticRow[] { getRecent(limit = 10): DiagnosticRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM diagnostics SELECT * FROM diagnostics
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
LIMIT ? LIMIT ?
@@ -90,7 +90,7 @@ export class DiagnosticsStore {
? 'SELECT * FROM diagnostics WHERE severity = ? ORDER BY created_at_epoch DESC LIMIT ?' ? 'SELECT * FROM diagnostics WHERE severity = ? ORDER BY created_at_epoch DESC LIMIT ?'
: 'SELECT * FROM diagnostics WHERE severity = ? ORDER BY created_at_epoch DESC'; : 'SELECT * FROM diagnostics WHERE severity = ? ORDER BY created_at_epoch DESC';
const stmt = this.db.prepare(query); const stmt = this.db.query(query);
const params = limit ? [severity, limit] : [severity]; const params = limit ? [severity, limit] : [severity];
return stmt.all(...params) as DiagnosticRow[]; return stmt.all(...params) as DiagnosticRow[];
} }
@@ -103,7 +103,7 @@ export class DiagnosticsStore {
? 'SELECT * FROM diagnostics WHERE origin = ? ORDER BY created_at_epoch DESC LIMIT ?' ? 'SELECT * FROM diagnostics WHERE origin = ? ORDER BY created_at_epoch DESC LIMIT ?'
: 'SELECT * FROM diagnostics WHERE origin = ? ORDER BY created_at_epoch DESC'; : 'SELECT * FROM diagnostics WHERE origin = ? ORDER BY created_at_epoch DESC';
const stmt = this.db.prepare(query); const stmt = this.db.query(query);
const params = limit ? [origin, limit] : [origin]; const params = limit ? [origin, limit] : [origin];
return stmt.all(...params) as DiagnosticRow[]; return stmt.all(...params) as DiagnosticRow[];
} }
@@ -123,7 +123,7 @@ export class DiagnosticsStore {
sql += ' ORDER BY created_at_epoch DESC LIMIT ?'; sql += ' ORDER BY created_at_epoch DESC LIMIT ?';
params.push(limit); params.push(limit);
const stmt = this.db.prepare(sql); const stmt = this.db.query(sql);
return stmt.all(...params) as DiagnosticRow[]; return stmt.all(...params) as DiagnosticRow[];
} }
@@ -131,7 +131,7 @@ export class DiagnosticsStore {
* Count total diagnostics * Count total diagnostics
*/ */
count(): number { count(): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM diagnostics'); const stmt = this.db.query('SELECT COUNT(*) as count FROM diagnostics');
const result = stmt.get() as { count: number }; const result = stmt.get() as { count: number };
return result.count; return result.count;
} }
@@ -140,7 +140,7 @@ export class DiagnosticsStore {
* Count diagnostics by project * Count diagnostics by project
*/ */
countByProject(project: string): number { countByProject(project: string): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM diagnostics WHERE project = ?'); const stmt = this.db.query('SELECT COUNT(*) as count FROM diagnostics WHERE project = ?');
const result = stmt.get(project) as { count: number }; const result = stmt.get(project) as { count: number };
return result.count; return result.count;
} }
@@ -149,7 +149,7 @@ export class DiagnosticsStore {
* Count diagnostics by severity * Count diagnostics by severity
*/ */
countBySeverity(severity: 'info' | 'warn' | 'error'): number { countBySeverity(severity: 'info' | 'warn' | 'error'): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM diagnostics WHERE severity = ?'); const stmt = this.db.query('SELECT COUNT(*) as count FROM diagnostics WHERE severity = ?');
const result = stmt.get(severity) as { count: number }; const result = stmt.get(severity) as { count: number };
return result.count; return result.count;
} }
@@ -165,7 +165,7 @@ export class DiagnosticsStore {
const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
UPDATE diagnostics SET UPDATE diagnostics SET
message = ?, severity = ?, created_at = ?, created_at_epoch = ?, project = ?, origin = ? message = ?, severity = ?, created_at = ?, created_at_epoch = ?, project = ?, origin = ?
WHERE id = ? WHERE id = ?
@@ -188,7 +188,7 @@ export class DiagnosticsStore {
* Delete a diagnostic by ID * Delete a diagnostic by ID
*/ */
deleteById(id: number): boolean { deleteById(id: number): boolean {
const stmt = this.db.prepare('DELETE FROM diagnostics WHERE id = ?'); const stmt = this.db.query('DELETE FROM diagnostics WHERE id = ?');
const info = stmt.run(id); const info = stmt.run(id);
return info.changes > 0; return info.changes > 0;
} }
@@ -197,7 +197,7 @@ export class DiagnosticsStore {
* Delete diagnostics by session_id * Delete diagnostics by session_id
*/ */
deleteBySessionId(sessionId: string): number { deleteBySessionId(sessionId: string): number {
const stmt = this.db.prepare('DELETE FROM diagnostics WHERE session_id = ?'); const stmt = this.db.query('DELETE FROM diagnostics WHERE session_id = ?');
const info = stmt.run(sessionId); const info = stmt.run(sessionId);
return info.changes; return info.changes;
} }
@@ -206,7 +206,7 @@ export class DiagnosticsStore {
* Get unique projects from diagnostics * Get unique projects from diagnostics
*/ */
getUniqueProjects(): string[] { getUniqueProjects(): string[] {
const stmt = this.db.prepare('SELECT DISTINCT project FROM diagnostics ORDER BY project'); const stmt = this.db.query('SELECT DISTINCT project FROM diagnostics ORDER BY project');
const rows = stmt.all() as { project: string }[]; const rows = stmt.all() as { project: string }[];
return rows.map(row => row.project); return rows.map(row => row.project);
} }
@@ -215,7 +215,7 @@ export class DiagnosticsStore {
* Get diagnostic summary stats * Get diagnostic summary stats
*/ */
getStats(): { total: number; info: number; warn: number; error: number } { getStats(): { total: number; info: number; warn: number; error: number } {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT SELECT
COUNT(*) as total, COUNT(*) as total,
COUNT(CASE WHEN severity = 'info' THEN 1 END) as info, COUNT(CASE WHEN severity = 'info' THEN 1 END) as info,
+19 -19
View File
@@ -1,4 +1,4 @@
import { Database } from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import { getDatabase } from './Database.js'; import { getDatabase } from './Database.js';
import { MemoryRow, MemoryInput, normalizeTimestamp } from './types.js'; import { MemoryRow, MemoryInput, normalizeTimestamp } from './types.js';
@@ -18,7 +18,7 @@ export class MemoryStore {
create(input: MemoryInput): MemoryRow { create(input: MemoryInput): MemoryRow {
const { isoString, epoch } = normalizeTimestamp(input.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
INSERT INTO memories ( INSERT INTO memories (
session_id, text, document_id, keywords, created_at, created_at_epoch, session_id, text, document_id, keywords, created_at, created_at_epoch,
project, archive_basename, origin, title, subtitle, facts, concepts, files_touched project, archive_basename, origin, title, subtitle, facts, concepts, files_touched
@@ -64,7 +64,7 @@ export class MemoryStore {
* Get memory by primary key * Get memory by primary key
*/ */
getById(id: number): MemoryRow | null { getById(id: number): MemoryRow | null {
const stmt = this.db.prepare('SELECT * FROM memories WHERE id = ?'); const stmt = this.db.query('SELECT * FROM memories WHERE id = ?');
return stmt.get(id) as MemoryRow || null; return stmt.get(id) as MemoryRow || null;
} }
@@ -72,7 +72,7 @@ export class MemoryStore {
* Get memory by document_id * Get memory by document_id
*/ */
getByDocumentId(documentId: string): MemoryRow | null { getByDocumentId(documentId: string): MemoryRow | null {
const stmt = this.db.prepare('SELECT * FROM memories WHERE document_id = ?'); const stmt = this.db.query('SELECT * FROM memories WHERE document_id = ?');
return stmt.get(documentId) as MemoryRow || null; return stmt.get(documentId) as MemoryRow || null;
} }
@@ -80,7 +80,7 @@ export class MemoryStore {
* Check if a document_id already exists * Check if a document_id already exists
*/ */
hasDocumentId(documentId: string): boolean { hasDocumentId(documentId: string): boolean {
const stmt = this.db.prepare('SELECT 1 FROM memories WHERE document_id = ? LIMIT 1'); const stmt = this.db.query('SELECT 1 FROM memories WHERE document_id = ? LIMIT 1');
return Boolean(stmt.get(documentId)); return Boolean(stmt.get(documentId));
} }
@@ -88,7 +88,7 @@ export class MemoryStore {
* Get memories for a specific session * Get memories for a specific session
*/ */
getBySessionId(sessionId: string): MemoryRow[] { getBySessionId(sessionId: string): MemoryRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM memories SELECT * FROM memories
WHERE session_id = ? WHERE session_id = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -100,7 +100,7 @@ export class MemoryStore {
* Get recent memories for a project * Get recent memories for a project
*/ */
getRecentForProject(project: string, limit = 10): MemoryRow[] { getRecentForProject(project: string, limit = 10): MemoryRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM memories SELECT * FROM memories
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -113,7 +113,7 @@ export class MemoryStore {
* Get recent memories across all projects * Get recent memories across all projects
*/ */
getRecent(limit = 10): MemoryRow[] { getRecent(limit = 10): MemoryRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM memories SELECT * FROM memories
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
LIMIT ? LIMIT ?
@@ -136,7 +136,7 @@ export class MemoryStore {
sql += ' ORDER BY created_at_epoch DESC LIMIT ?'; sql += ' ORDER BY created_at_epoch DESC LIMIT ?';
params.push(limit); params.push(limit);
const stmt = this.db.prepare(sql); const stmt = this.db.query(sql);
return stmt.all(...params) as MemoryRow[]; return stmt.all(...params) as MemoryRow[];
} }
@@ -155,7 +155,7 @@ export class MemoryStore {
sql += ' ORDER BY created_at_epoch DESC LIMIT ?'; sql += ' ORDER BY created_at_epoch DESC LIMIT ?';
params.push(limit); params.push(limit);
const stmt = this.db.prepare(sql); const stmt = this.db.query(sql);
return stmt.all(...params) as MemoryRow[]; return stmt.all(...params) as MemoryRow[];
} }
@@ -167,7 +167,7 @@ export class MemoryStore {
? 'SELECT * FROM memories WHERE origin = ? ORDER BY created_at_epoch DESC LIMIT ?' ? 'SELECT * FROM memories WHERE origin = ? ORDER BY created_at_epoch DESC LIMIT ?'
: 'SELECT * FROM memories WHERE origin = ? ORDER BY created_at_epoch DESC'; : 'SELECT * FROM memories WHERE origin = ? ORDER BY created_at_epoch DESC';
const stmt = this.db.prepare(query); const stmt = this.db.query(query);
const params = limit ? [origin, limit] : [origin]; const params = limit ? [origin, limit] : [origin];
return stmt.all(...params) as MemoryRow[]; return stmt.all(...params) as MemoryRow[];
} }
@@ -176,7 +176,7 @@ export class MemoryStore {
* Get recent memories for a project filtered by origin * Get recent memories for a project filtered by origin
*/ */
getRecentForProjectByOrigin(project: string, origin: string, limit = 10): MemoryRow[] { getRecentForProjectByOrigin(project: string, origin: string, limit = 10): MemoryRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM memories SELECT * FROM memories
WHERE project = ? AND origin = ? WHERE project = ? AND origin = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -189,7 +189,7 @@ export class MemoryStore {
* Get last N memories for a project, sorted oldest to newest * Get last N memories for a project, sorted oldest to newest
*/ */
getLastNForProject(project: string, limit = 10): MemoryRow[] { getLastNForProject(project: string, limit = 10): MemoryRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM ( SELECT * FROM (
SELECT * FROM memories SELECT * FROM memories
WHERE project = ? WHERE project = ?
@@ -205,7 +205,7 @@ export class MemoryStore {
* Count total memories * Count total memories
*/ */
count(): number { count(): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM memories'); const stmt = this.db.query('SELECT COUNT(*) as count FROM memories');
const result = stmt.get() as { count: number }; const result = stmt.get() as { count: number };
return result.count; return result.count;
} }
@@ -214,7 +214,7 @@ export class MemoryStore {
* Count memories by project * Count memories by project
*/ */
countByProject(project: string): number { countByProject(project: string): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM memories WHERE project = ?'); const stmt = this.db.query('SELECT COUNT(*) as count FROM memories WHERE project = ?');
const result = stmt.get(project) as { count: number }; const result = stmt.get(project) as { count: number };
return result.count; return result.count;
} }
@@ -230,7 +230,7 @@ export class MemoryStore {
const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
UPDATE memories SET UPDATE memories SET
text = ?, document_id = ?, keywords = ?, created_at = ?, created_at_epoch = ?, text = ?, document_id = ?, keywords = ?, created_at = ?, created_at_epoch = ?,
project = ?, archive_basename = ?, origin = ?, title = ?, subtitle = ?, facts = ?, project = ?, archive_basename = ?, origin = ?, title = ?, subtitle = ?, facts = ?,
@@ -262,7 +262,7 @@ export class MemoryStore {
* Delete a memory by ID * Delete a memory by ID
*/ */
deleteById(id: number): boolean { deleteById(id: number): boolean {
const stmt = this.db.prepare('DELETE FROM memories WHERE id = ?'); const stmt = this.db.query('DELETE FROM memories WHERE id = ?');
const info = stmt.run(id); const info = stmt.run(id);
return info.changes > 0; return info.changes > 0;
} }
@@ -271,7 +271,7 @@ export class MemoryStore {
* Delete memories by session_id * Delete memories by session_id
*/ */
deleteBySessionId(sessionId: string): number { deleteBySessionId(sessionId: string): number {
const stmt = this.db.prepare('DELETE FROM memories WHERE session_id = ?'); const stmt = this.db.query('DELETE FROM memories WHERE session_id = ?');
const info = stmt.run(sessionId); const info = stmt.run(sessionId);
return info.changes; return info.changes;
} }
@@ -280,7 +280,7 @@ export class MemoryStore {
* Get unique projects from memories * Get unique projects from memories
*/ */
getUniqueProjects(): string[] { getUniqueProjects(): string[] {
const stmt = this.db.prepare('SELECT DISTINCT project FROM memories ORDER BY project'); const stmt = this.db.query('SELECT DISTINCT project FROM memories ORDER BY project');
const rows = stmt.all() as { project: string }[]; const rows = stmt.all() as { project: string }[];
return rows.map(row => row.project); return rows.map(row => row.project);
} }
+17 -17
View File
@@ -1,4 +1,4 @@
import { Database } from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import { getDatabase } from './Database.js'; import { getDatabase } from './Database.js';
import { OverviewRow, OverviewInput, normalizeTimestamp } from './types.js'; import { OverviewRow, OverviewInput, normalizeTimestamp } from './types.js';
@@ -18,7 +18,7 @@ export class OverviewStore {
create(input: OverviewInput): OverviewRow { create(input: OverviewInput): OverviewRow {
const { isoString, epoch } = normalizeTimestamp(input.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
INSERT INTO overviews ( INSERT INTO overviews (
session_id, content, created_at, created_at_epoch, project, origin session_id, content, created_at, created_at_epoch, project, origin
) VALUES (?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?)
@@ -51,7 +51,7 @@ export class OverviewStore {
* Get overview by primary key * Get overview by primary key
*/ */
getById(id: number): OverviewRow | null { getById(id: number): OverviewRow | null {
const stmt = this.db.prepare('SELECT * FROM overviews WHERE id = ?'); const stmt = this.db.query('SELECT * FROM overviews WHERE id = ?');
return stmt.get(id) as OverviewRow || null; return stmt.get(id) as OverviewRow || null;
} }
@@ -59,7 +59,7 @@ export class OverviewStore {
* Get overview by session_id * Get overview by session_id
*/ */
getBySessionId(sessionId: string): OverviewRow | null { getBySessionId(sessionId: string): OverviewRow | null {
const stmt = this.db.prepare('SELECT * FROM overviews WHERE session_id = ?'); const stmt = this.db.query('SELECT * FROM overviews WHERE session_id = ?');
return stmt.get(sessionId) as OverviewRow || null; return stmt.get(sessionId) as OverviewRow || null;
} }
@@ -67,7 +67,7 @@ export class OverviewStore {
* Get recent overviews for a project * Get recent overviews for a project
*/ */
getRecentForProject(project: string, limit = 5): OverviewRow[] { getRecentForProject(project: string, limit = 5): OverviewRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM overviews SELECT * FROM overviews
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -80,7 +80,7 @@ export class OverviewStore {
* Get all overviews for a project (oldest to newest) * Get all overviews for a project (oldest to newest)
*/ */
getAllForProject(project: string): OverviewRow[] { getAllForProject(project: string): OverviewRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM overviews SELECT * FROM overviews
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch ASC ORDER BY created_at_epoch ASC
@@ -92,7 +92,7 @@ export class OverviewStore {
* Get recent overviews across all projects * Get recent overviews across all projects
*/ */
getRecent(limit = 5): OverviewRow[] { getRecent(limit = 5): OverviewRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM overviews SELECT * FROM overviews
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
LIMIT ? LIMIT ?
@@ -115,7 +115,7 @@ export class OverviewStore {
sql += ' ORDER BY created_at_epoch DESC LIMIT ?'; sql += ' ORDER BY created_at_epoch DESC LIMIT ?';
params.push(limit); params.push(limit);
const stmt = this.db.prepare(sql); const stmt = this.db.query(sql);
return stmt.all(...params) as OverviewRow[]; return stmt.all(...params) as OverviewRow[];
} }
@@ -127,7 +127,7 @@ export class OverviewStore {
? 'SELECT * FROM overviews WHERE origin = ? ORDER BY created_at_epoch DESC LIMIT ?' ? 'SELECT * FROM overviews WHERE origin = ? ORDER BY created_at_epoch DESC LIMIT ?'
: 'SELECT * FROM overviews WHERE origin = ? ORDER BY created_at_epoch DESC'; : 'SELECT * FROM overviews WHERE origin = ? ORDER BY created_at_epoch DESC';
const stmt = this.db.prepare(query); const stmt = this.db.query(query);
const params = limit ? [origin, limit] : [origin]; const params = limit ? [origin, limit] : [origin];
return stmt.all(...params) as OverviewRow[]; return stmt.all(...params) as OverviewRow[];
} }
@@ -136,7 +136,7 @@ export class OverviewStore {
* Count total overviews * Count total overviews
*/ */
count(): number { count(): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM overviews'); const stmt = this.db.query('SELECT COUNT(*) as count FROM overviews');
const result = stmt.get() as { count: number }; const result = stmt.get() as { count: number };
return result.count; return result.count;
} }
@@ -145,7 +145,7 @@ export class OverviewStore {
* Count overviews by project * Count overviews by project
*/ */
countByProject(project: string): number { countByProject(project: string): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM overviews WHERE project = ?'); const stmt = this.db.query('SELECT COUNT(*) as count FROM overviews WHERE project = ?');
const result = stmt.get(project) as { count: number }; const result = stmt.get(project) as { count: number };
return result.count; return result.count;
} }
@@ -161,7 +161,7 @@ export class OverviewStore {
const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
UPDATE overviews SET UPDATE overviews SET
content = ?, created_at = ?, created_at_epoch = ?, project = ?, origin = ? content = ?, created_at = ?, created_at_epoch = ?, project = ?, origin = ?
WHERE id = ? WHERE id = ?
@@ -183,7 +183,7 @@ export class OverviewStore {
* Delete an overview by ID * Delete an overview by ID
*/ */
deleteById(id: number): boolean { deleteById(id: number): boolean {
const stmt = this.db.prepare('DELETE FROM overviews WHERE id = ?'); const stmt = this.db.query('DELETE FROM overviews WHERE id = ?');
const info = stmt.run(id); const info = stmt.run(id);
return info.changes > 0; return info.changes > 0;
} }
@@ -192,7 +192,7 @@ export class OverviewStore {
* Delete overview by session_id * Delete overview by session_id
*/ */
deleteBySessionId(sessionId: string): boolean { deleteBySessionId(sessionId: string): boolean {
const stmt = this.db.prepare('DELETE FROM overviews WHERE session_id = ?'); const stmt = this.db.query('DELETE FROM overviews WHERE session_id = ?');
const info = stmt.run(sessionId); const info = stmt.run(sessionId);
return info.changes > 0; return info.changes > 0;
} }
@@ -201,7 +201,7 @@ export class OverviewStore {
* Get unique projects from overviews * Get unique projects from overviews
*/ */
getUniqueProjects(): string[] { getUniqueProjects(): string[] {
const stmt = this.db.prepare('SELECT DISTINCT project FROM overviews ORDER BY project'); const stmt = this.db.query('SELECT DISTINCT project FROM overviews ORDER BY project');
const rows = stmt.all() as { project: string }[]; const rows = stmt.all() as { project: string }[];
return rows.map(row => row.project); return rows.map(row => row.project);
} }
@@ -210,7 +210,7 @@ export class OverviewStore {
* Get most recent overview for a specific project * Get most recent overview for a specific project
*/ */
getByProject(project: string): OverviewRow | null { getByProject(project: string): OverviewRow | null {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM overviews SELECT * FROM overviews
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -234,7 +234,7 @@ export class OverviewStore {
* Delete overview by project name * Delete overview by project name
*/ */
deleteByProject(project: string): boolean { deleteByProject(project: string): boolean {
const stmt = this.db.prepare('DELETE FROM overviews WHERE project = ?'); const stmt = this.db.query('DELETE FROM overviews WHERE project = ?');
const info = stmt.run(project); const info = stmt.run(project);
return info.changes > 0; return info.changes > 0;
} }
+14 -14
View File
@@ -1,4 +1,4 @@
import { Database } from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import { getDatabase } from './Database.js'; import { getDatabase } from './Database.js';
import { SessionRow, SessionInput, normalizeTimestamp } from './types.js'; import { SessionRow, SessionInput, normalizeTimestamp } from './types.js';
@@ -18,7 +18,7 @@ export class SessionStore {
create(input: SessionInput): SessionRow { create(input: SessionInput): SessionRow {
const { isoString, epoch } = normalizeTimestamp(input.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
INSERT INTO sessions ( INSERT INTO sessions (
session_id, project, created_at, created_at_epoch, source, session_id, project, created_at, created_at_epoch, source,
archive_path, archive_bytes, archive_checksum, archived_at, metadata_json archive_path, archive_bytes, archive_checksum, archived_at, metadata_json
@@ -63,7 +63,7 @@ export class SessionStore {
const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at); const { isoString, epoch } = normalizeTimestamp(input.created_at || existing.created_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
UPDATE sessions SET UPDATE sessions SET
project = ?, created_at = ?, created_at_epoch = ?, source = ?, project = ?, created_at = ?, created_at_epoch = ?, source = ?,
archive_path = ?, archive_bytes = ?, archive_checksum = ?, archived_at = ?, metadata_json = ? archive_path = ?, archive_bytes = ?, archive_checksum = ?, archived_at = ?, metadata_json = ?
@@ -90,7 +90,7 @@ export class SessionStore {
* Get session by primary key * Get session by primary key
*/ */
getById(id: number): SessionRow | null { getById(id: number): SessionRow | null {
const stmt = this.db.prepare('SELECT * FROM sessions WHERE id = ?'); const stmt = this.db.query('SELECT * FROM sessions WHERE id = ?');
return stmt.get(id) as SessionRow || null; return stmt.get(id) as SessionRow || null;
} }
@@ -98,7 +98,7 @@ export class SessionStore {
* Get session by session_id * Get session by session_id
*/ */
getBySessionId(sessionId: string): SessionRow | null { getBySessionId(sessionId: string): SessionRow | null {
const stmt = this.db.prepare('SELECT * FROM sessions WHERE session_id = ?'); const stmt = this.db.query('SELECT * FROM sessions WHERE session_id = ?');
return stmt.get(sessionId) as SessionRow || null; return stmt.get(sessionId) as SessionRow || null;
} }
@@ -106,7 +106,7 @@ export class SessionStore {
* Check if a session exists by session_id * Check if a session exists by session_id
*/ */
has(sessionId: string): boolean { has(sessionId: string): boolean {
const stmt = this.db.prepare('SELECT 1 FROM sessions WHERE session_id = ? LIMIT 1'); const stmt = this.db.query('SELECT 1 FROM sessions WHERE session_id = ? LIMIT 1');
return Boolean(stmt.get(sessionId)); return Boolean(stmt.get(sessionId));
} }
@@ -114,7 +114,7 @@ export class SessionStore {
* Get all session_ids as a Set (useful for import-history) * Get all session_ids as a Set (useful for import-history)
*/ */
getAllSessionIds(): Set<string> { getAllSessionIds(): Set<string> {
const stmt = this.db.prepare('SELECT session_id FROM sessions'); const stmt = this.db.query('SELECT session_id FROM sessions');
const rows = stmt.all() as { session_id: string }[]; const rows = stmt.all() as { session_id: string }[];
return new Set(rows.map(row => row.session_id)); return new Set(rows.map(row => row.session_id));
} }
@@ -123,7 +123,7 @@ export class SessionStore {
* Get recent sessions for a project * Get recent sessions for a project
*/ */
getRecentForProject(project: string, limit = 5): SessionRow[] { getRecentForProject(project: string, limit = 5): SessionRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM sessions SELECT * FROM sessions
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -136,7 +136,7 @@ export class SessionStore {
* Get recent sessions across all projects * Get recent sessions across all projects
*/ */
getRecent(limit = 5): SessionRow[] { getRecent(limit = 5): SessionRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM sessions SELECT * FROM sessions
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
LIMIT ? LIMIT ?
@@ -152,7 +152,7 @@ export class SessionStore {
? 'SELECT * FROM sessions WHERE source = ? ORDER BY created_at_epoch DESC LIMIT ?' ? 'SELECT * FROM sessions WHERE source = ? ORDER BY created_at_epoch DESC LIMIT ?'
: 'SELECT * FROM sessions WHERE source = ? ORDER BY created_at_epoch DESC'; : 'SELECT * FROM sessions WHERE source = ? ORDER BY created_at_epoch DESC';
const stmt = this.db.prepare(query); const stmt = this.db.query(query);
const params = limit ? [source, limit] : [source]; const params = limit ? [source, limit] : [source];
return stmt.all(...params) as SessionRow[]; return stmt.all(...params) as SessionRow[];
} }
@@ -161,7 +161,7 @@ export class SessionStore {
* Count total sessions * Count total sessions
*/ */
count(): number { count(): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM sessions'); const stmt = this.db.query('SELECT COUNT(*) as count FROM sessions');
const result = stmt.get() as { count: number }; const result = stmt.get() as { count: number };
return result.count; return result.count;
} }
@@ -170,7 +170,7 @@ export class SessionStore {
* Count sessions by project * Count sessions by project
*/ */
countByProject(project: string): number { countByProject(project: string): number {
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM sessions WHERE project = ?'); const stmt = this.db.query('SELECT COUNT(*) as count FROM sessions WHERE project = ?');
const result = stmt.get(project) as { count: number }; const result = stmt.get(project) as { count: number };
return result.count; return result.count;
} }
@@ -179,7 +179,7 @@ export class SessionStore {
* Delete a session by ID (cascades to related records) * Delete a session by ID (cascades to related records)
*/ */
deleteById(id: number): boolean { deleteById(id: number): boolean {
const stmt = this.db.prepare('DELETE FROM sessions WHERE id = ?'); const stmt = this.db.query('DELETE FROM sessions WHERE id = ?');
const info = stmt.run(id); const info = stmt.run(id);
return info.changes > 0; return info.changes > 0;
} }
@@ -188,7 +188,7 @@ export class SessionStore {
* Delete a session by session_id (cascades to related records) * Delete a session by session_id (cascades to related records)
*/ */
deleteBySessionId(sessionId: string): boolean { deleteBySessionId(sessionId: string): boolean {
const stmt = this.db.prepare('DELETE FROM sessions WHERE session_id = ?'); const stmt = this.db.query('DELETE FROM sessions WHERE session_id = ?');
const info = stmt.run(sessionId); const info = stmt.run(sessionId);
return info.changes > 0; return info.changes > 0;
} }
+13 -13
View File
@@ -1,4 +1,4 @@
import { Database } from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import { getDatabase } from './Database.js'; import { getDatabase } from './Database.js';
import { normalizeTimestamp } from './types.js'; import { normalizeTimestamp } from './types.js';
@@ -61,7 +61,7 @@ export class StreamingSessionStore {
create(input: StreamingSessionInput): StreamingSessionRow { create(input: StreamingSessionInput): StreamingSessionRow {
const { isoString, epoch } = normalizeTimestamp(input.started_at); const { isoString, epoch } = normalizeTimestamp(input.started_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
INSERT INTO streaming_sessions ( INSERT INTO streaming_sessions (
claude_session_id, project, user_prompt, started_at, started_at_epoch, status claude_session_id, project, user_prompt, started_at, started_at_epoch, status
) VALUES (?, ?, ?, ?, ?, 'active') ) VALUES (?, ?, ?, ?, ?, 'active')
@@ -121,7 +121,7 @@ export class StreamingSessionStore {
values.push(id); values.push(id);
const stmt = this.db.prepare(` const stmt = this.db.query(`
UPDATE streaming_sessions UPDATE streaming_sessions
SET ${parts.join(', ')} SET ${parts.join(', ')}
WHERE id = ? WHERE id = ?
@@ -148,7 +148,7 @@ export class StreamingSessionStore {
* Get streaming session by internal ID * Get streaming session by internal ID
*/ */
getById(id: number): StreamingSessionRow | null { getById(id: number): StreamingSessionRow | null {
const stmt = this.db.prepare('SELECT * FROM streaming_sessions WHERE id = ?'); const stmt = this.db.query('SELECT * FROM streaming_sessions WHERE id = ?');
return stmt.get(id) as StreamingSessionRow || null; return stmt.get(id) as StreamingSessionRow || null;
} }
@@ -156,7 +156,7 @@ export class StreamingSessionStore {
* Get streaming session by Claude session ID * Get streaming session by Claude session ID
*/ */
getByClaudeSessionId(claudeSessionId: string): StreamingSessionRow | null { getByClaudeSessionId(claudeSessionId: string): StreamingSessionRow | null {
const stmt = this.db.prepare('SELECT * FROM streaming_sessions WHERE claude_session_id = ?'); const stmt = this.db.query('SELECT * FROM streaming_sessions WHERE claude_session_id = ?');
return stmt.get(claudeSessionId) as StreamingSessionRow || null; return stmt.get(claudeSessionId) as StreamingSessionRow || null;
} }
@@ -164,7 +164,7 @@ export class StreamingSessionStore {
* Get streaming session by SDK session ID * Get streaming session by SDK session ID
*/ */
getBySdkSessionId(sdkSessionId: string): StreamingSessionRow | null { getBySdkSessionId(sdkSessionId: string): StreamingSessionRow | null {
const stmt = this.db.prepare('SELECT * FROM streaming_sessions WHERE sdk_session_id = ?'); const stmt = this.db.query('SELECT * FROM streaming_sessions WHERE sdk_session_id = ?');
return stmt.get(sdkSessionId) as StreamingSessionRow || null; return stmt.get(sdkSessionId) as StreamingSessionRow || null;
} }
@@ -172,7 +172,7 @@ export class StreamingSessionStore {
* Check if a streaming session exists by Claude session ID * Check if a streaming session exists by Claude session ID
*/ */
has(claudeSessionId: string): boolean { has(claudeSessionId: string): boolean {
const stmt = this.db.prepare('SELECT 1 FROM streaming_sessions WHERE claude_session_id = ? LIMIT 1'); const stmt = this.db.query('SELECT 1 FROM streaming_sessions WHERE claude_session_id = ? LIMIT 1');
return Boolean(stmt.get(claudeSessionId)); return Boolean(stmt.get(claudeSessionId));
} }
@@ -180,7 +180,7 @@ export class StreamingSessionStore {
* Get active streaming sessions for a project * Get active streaming sessions for a project
*/ */
getActiveForProject(project: string): StreamingSessionRow[] { getActiveForProject(project: string): StreamingSessionRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM streaming_sessions SELECT * FROM streaming_sessions
WHERE project = ? AND status = 'active' WHERE project = ? AND status = 'active'
ORDER BY started_at_epoch DESC ORDER BY started_at_epoch DESC
@@ -192,7 +192,7 @@ export class StreamingSessionStore {
* Get all active streaming sessions * Get all active streaming sessions
*/ */
getAllActive(): StreamingSessionRow[] { getAllActive(): StreamingSessionRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM streaming_sessions SELECT * FROM streaming_sessions
WHERE status = 'active' WHERE status = 'active'
ORDER BY started_at_epoch DESC ORDER BY started_at_epoch DESC
@@ -204,7 +204,7 @@ export class StreamingSessionStore {
* Get recent streaming sessions (completed or failed) * Get recent streaming sessions (completed or failed)
*/ */
getRecent(limit = 10): StreamingSessionRow[] { getRecent(limit = 10): StreamingSessionRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM streaming_sessions SELECT * FROM streaming_sessions
ORDER BY started_at_epoch DESC ORDER BY started_at_epoch DESC
LIMIT ? LIMIT ?
@@ -236,7 +236,7 @@ export class StreamingSessionStore {
* Delete a streaming session by ID * Delete a streaming session by ID
*/ */
deleteById(id: number): boolean { deleteById(id: number): boolean {
const stmt = this.db.prepare('DELETE FROM streaming_sessions WHERE id = ?'); const stmt = this.db.query('DELETE FROM streaming_sessions WHERE id = ?');
const info = stmt.run(id); const info = stmt.run(id);
return info.changes > 0; return info.changes > 0;
} }
@@ -245,7 +245,7 @@ export class StreamingSessionStore {
* Delete a streaming session by Claude session ID * Delete a streaming session by Claude session ID
*/ */
deleteByClaudeSessionId(claudeSessionId: string): boolean { deleteByClaudeSessionId(claudeSessionId: string): boolean {
const stmt = this.db.prepare('DELETE FROM streaming_sessions WHERE claude_session_id = ?'); const stmt = this.db.query('DELETE FROM streaming_sessions WHERE claude_session_id = ?');
const info = stmt.run(claudeSessionId); const info = stmt.run(claudeSessionId);
return info.changes > 0; return info.changes > 0;
} }
@@ -255,7 +255,7 @@ export class StreamingSessionStore {
*/ */
cleanupOldSessions(daysOld = 30): number { cleanupOldSessions(daysOld = 30): number {
const cutoffEpoch = Date.now() - (daysOld * 24 * 60 * 60 * 1000); const cutoffEpoch = Date.now() - (daysOld * 24 * 60 * 60 * 1000);
const stmt = this.db.prepare(` const stmt = this.db.query(`
DELETE FROM streaming_sessions DELETE FROM streaming_sessions
WHERE status IN ('completed', 'failed') WHERE status IN ('completed', 'failed')
AND completed_at_epoch < ? AND completed_at_epoch < ?
+5 -5
View File
@@ -1,4 +1,4 @@
import { Database } from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import { getDatabase } from './Database.js'; import { getDatabase } from './Database.js';
import { import {
TranscriptEventInput, TranscriptEventInput,
@@ -22,7 +22,7 @@ export class TranscriptEventStore {
upsert(event: TranscriptEventInput): TranscriptEventRow { upsert(event: TranscriptEventInput): TranscriptEventRow {
const { isoString, epoch } = normalizeTimestamp(event.captured_at); const { isoString, epoch } = normalizeTimestamp(event.captured_at);
const stmt = this.db.prepare(` const stmt = this.db.query(`
INSERT INTO transcript_events ( INSERT INTO transcript_events (
session_id, session_id,
project, project,
@@ -72,7 +72,7 @@ export class TranscriptEventStore {
* Get event by session and index * Get event by session and index
*/ */
getBySessionAndIndex(sessionId: string, eventIndex: number): TranscriptEventRow | null { getBySessionAndIndex(sessionId: string, eventIndex: number): TranscriptEventRow | null {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM transcript_events SELECT * FROM transcript_events
WHERE session_id = ? AND event_index = ? WHERE session_id = ? AND event_index = ?
`); `);
@@ -83,7 +83,7 @@ export class TranscriptEventStore {
* Get highest event_index stored for a session * Get highest event_index stored for a session
*/ */
getMaxEventIndex(sessionId: string): number { getMaxEventIndex(sessionId: string): number {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT MAX(event_index) as max_event_index SELECT MAX(event_index) as max_event_index
FROM transcript_events FROM transcript_events
WHERE session_id = ? WHERE session_id = ?
@@ -96,7 +96,7 @@ export class TranscriptEventStore {
* List recent events for a session * List recent events for a session
*/ */
listBySession(sessionId: string, limit = 200, offset = 0): TranscriptEventRow[] { listBySession(sessionId: string, limit = 200, offset = 0): TranscriptEventRow[] {
const stmt = this.db.prepare(` const stmt = this.db.query(`
SELECT * FROM transcript_events SELECT * FROM transcript_events
WHERE session_id = ? WHERE session_id = ?
ORDER BY event_index ASC ORDER BY event_index ASC
+17 -17
View File
@@ -1,4 +1,4 @@
import { Database } from 'better-sqlite3'; import { Database } from 'bun:sqlite';
import { Migration } from './Database.js'; import { Migration } from './Database.js';
/** /**
@@ -6,9 +6,9 @@ import { Migration } from './Database.js';
*/ */
export const migration001: Migration = { export const migration001: Migration = {
version: 1, version: 1,
up: (db: Database.Database) => { up: (db: Database) => {
// Sessions table - core session tracking // Sessions table - core session tracking
db.exec(` db.run(`
CREATE TABLE IF NOT EXISTS sessions ( CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT UNIQUE NOT NULL, session_id TEXT UNIQUE NOT NULL,
@@ -29,7 +29,7 @@ export const migration001: Migration = {
`); `);
// Memories table - compressed memory chunks // Memories table - compressed memory chunks
db.exec(` db.run(`
CREATE TABLE IF NOT EXISTS memories ( CREATE TABLE IF NOT EXISTS memories (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL, session_id TEXT NOT NULL,
@@ -53,7 +53,7 @@ export const migration001: Migration = {
`); `);
// Overviews table - session summaries (one per project) // Overviews table - session summaries (one per project)
db.exec(` db.run(`
CREATE TABLE IF NOT EXISTS overviews ( CREATE TABLE IF NOT EXISTS overviews (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL, session_id TEXT NOT NULL,
@@ -73,7 +73,7 @@ export const migration001: Migration = {
`); `);
// Diagnostics table - system health and debug info // Diagnostics table - system health and debug info
db.exec(` db.run(`
CREATE TABLE IF NOT EXISTS diagnostics ( CREATE TABLE IF NOT EXISTS diagnostics (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT, session_id TEXT,
@@ -93,7 +93,7 @@ export const migration001: Migration = {
`); `);
// Transcript events table - raw conversation events // Transcript events table - raw conversation events
db.exec(` db.run(`
CREATE TABLE IF NOT EXISTS transcript_events ( CREATE TABLE IF NOT EXISTS transcript_events (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL, session_id TEXT NOT NULL,
@@ -116,8 +116,8 @@ export const migration001: Migration = {
console.log('✅ Created all database tables successfully'); console.log('✅ Created all database tables successfully');
}, },
down: (db: Database.Database) => { down: (db: Database) => {
db.exec(` db.run(`
DROP TABLE IF EXISTS transcript_events; DROP TABLE IF EXISTS transcript_events;
DROP TABLE IF EXISTS diagnostics; DROP TABLE IF EXISTS diagnostics;
DROP TABLE IF EXISTS overviews; DROP TABLE IF EXISTS overviews;
@@ -132,9 +132,9 @@ export const migration001: Migration = {
*/ */
export const migration002: Migration = { export const migration002: Migration = {
version: 2, version: 2,
up: (db: Database.Database) => { up: (db: Database) => {
// Add new columns for hierarchical memory structure // Add new columns for hierarchical memory structure
db.exec(` db.run(`
ALTER TABLE memories ADD COLUMN title TEXT; ALTER TABLE memories ADD COLUMN title TEXT;
ALTER TABLE memories ADD COLUMN subtitle TEXT; ALTER TABLE memories ADD COLUMN subtitle TEXT;
ALTER TABLE memories ADD COLUMN facts TEXT; ALTER TABLE memories ADD COLUMN facts TEXT;
@@ -143,7 +143,7 @@ export const migration002: Migration = {
`); `);
// Create indexes for the new fields to improve search performance // Create indexes for the new fields to improve search performance
db.exec(` db.run(`
CREATE INDEX IF NOT EXISTS idx_memories_title ON memories(title); CREATE INDEX IF NOT EXISTS idx_memories_title ON memories(title);
CREATE INDEX IF NOT EXISTS idx_memories_concepts ON memories(concepts); CREATE INDEX IF NOT EXISTS idx_memories_concepts ON memories(concepts);
`); `);
@@ -151,7 +151,7 @@ export const migration002: Migration = {
console.log('✅ Added hierarchical memory fields to memories table'); console.log('✅ Added hierarchical memory fields to memories table');
}, },
down: (db: Database.Database) => { down: (db: Database) => {
// Note: SQLite doesn't support DROP COLUMN in all versions // Note: SQLite doesn't support DROP COLUMN in all versions
// In production, we'd need to recreate the table without these columns // In production, we'd need to recreate the table without these columns
// For now, we'll just log a warning // For now, we'll just log a warning
@@ -165,9 +165,9 @@ export const migration002: Migration = {
*/ */
export const migration003: Migration = { export const migration003: Migration = {
version: 3, version: 3,
up: (db: Database.Database) => { up: (db: Database) => {
// Streaming sessions table - tracks active SDK compression sessions // Streaming sessions table - tracks active SDK compression sessions
db.exec(` db.run(`
CREATE TABLE IF NOT EXISTS streaming_sessions ( CREATE TABLE IF NOT EXISTS streaming_sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
claude_session_id TEXT UNIQUE NOT NULL, claude_session_id TEXT UNIQUE NOT NULL,
@@ -195,8 +195,8 @@ export const migration003: Migration = {
console.log('✅ Created streaming_sessions table for real-time session tracking'); console.log('✅ Created streaming_sessions table for real-time session tracking');
}, },
down: (db: Database.Database) => { down: (db: Database) => {
db.exec(` db.run(`
DROP TABLE IF EXISTS streaming_sessions; DROP TABLE IF EXISTS streaming_sessions;
`); `);
} }