130 lines
5.2 KiB
JavaScript
130 lines
5.2 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.dbManager = exports.DatabaseManager = void 0;
|
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
const path_1 = __importDefault(require("path"));
|
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
class DatabaseManager {
|
|
db = null;
|
|
dbPath;
|
|
constructor(dbPath) {
|
|
if (dbPath) {
|
|
this.dbPath = dbPath;
|
|
}
|
|
else {
|
|
// Default to user's home directory .claude/threads/threads.db
|
|
const homeDir = process.env.HOME || process.env.USERPROFILE || '.';
|
|
this.dbPath = process.env.THREADS_DB_PATH || path_1.default.join(homeDir, '.claude', 'threads', 'threads.db');
|
|
}
|
|
}
|
|
getDatabase() {
|
|
if (!this.db) {
|
|
this.init();
|
|
}
|
|
return this.db;
|
|
}
|
|
init() {
|
|
if (this.db)
|
|
return;
|
|
// Ensure directory exists
|
|
const dir = path_1.default.dirname(this.dbPath);
|
|
fs_extra_1.default.ensureDirSync(dir);
|
|
this.db = new better_sqlite3_1.default(this.dbPath);
|
|
this.db.pragma('journal_mode = WAL'); // Better concurrency
|
|
this.createTables();
|
|
}
|
|
close() {
|
|
if (this.db) {
|
|
this.db.close();
|
|
this.db = null;
|
|
}
|
|
}
|
|
createTables() {
|
|
if (!this.db)
|
|
return;
|
|
// Threads Table
|
|
this.db.exec(`
|
|
CREATE TABLE IF NOT EXISTS threads (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
description TEXT,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL,
|
|
message_count INTEGER DEFAULT 0,
|
|
is_active INTEGER DEFAULT 0,
|
|
files_changed INTEGER DEFAULT 0,
|
|
lines_added INTEGER DEFAULT 0,
|
|
lines_deleted INTEGER DEFAULT 0,
|
|
tags TEXT
|
|
);
|
|
`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_threads_is_active ON threads(is_active);`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_threads_updated_at ON threads(updated_at DESC);`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_threads_created_at ON threads(created_at DESC);`);
|
|
// Migration: Add session_id if not exists
|
|
try {
|
|
const info = this.db.pragma('table_info(threads)');
|
|
if (!info.some(col => col.name === 'session_id')) {
|
|
this.db.exec(`ALTER TABLE threads ADD COLUMN session_id TEXT`);
|
|
this.db.exec(`UPDATE threads SET session_id = id WHERE session_id IS NULL`);
|
|
}
|
|
if (!info.some(col => col.name === 'git_branch')) {
|
|
this.db.exec(`ALTER TABLE threads ADD COLUMN git_branch TEXT`);
|
|
}
|
|
}
|
|
catch (e) {
|
|
console.error('Error migrating database:', e);
|
|
}
|
|
// Messages Table
|
|
this.db.exec(`
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
id TEXT PRIMARY KEY,
|
|
thread_id TEXT NOT NULL,
|
|
role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system')),
|
|
content TEXT NOT NULL,
|
|
timestamp INTEGER NOT NULL,
|
|
metadata TEXT,
|
|
FOREIGN KEY (thread_id) REFERENCES threads(id) ON DELETE CASCADE
|
|
);
|
|
`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_messages_thread_id ON messages(thread_id);`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp DESC);`);
|
|
// Migration: Add embedding columns if not exists
|
|
try {
|
|
const info = this.db.pragma('table_info(messages)');
|
|
if (!info.some(col => col.name === 'embedding_blob')) {
|
|
this.db.exec(`ALTER TABLE messages ADD COLUMN embedding_blob BLOB`);
|
|
this.db.exec(`ALTER TABLE messages ADD COLUMN embedding_model TEXT`);
|
|
this.db.exec(`ALTER TABLE messages ADD COLUMN embedding_generated_at INTEGER`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_messages_embedding_exists ON messages(embedding_generated_at) WHERE embedding_blob IS NOT NULL`);
|
|
}
|
|
}
|
|
catch (e) {
|
|
console.error('Error migrating messages table:', e);
|
|
}
|
|
// File Changes Table
|
|
this.db.exec(`
|
|
CREATE TABLE IF NOT EXISTS file_changes (
|
|
id TEXT PRIMARY KEY,
|
|
thread_id TEXT NOT NULL,
|
|
file_path TEXT NOT NULL,
|
|
change_type TEXT NOT NULL CHECK(change_type IN ('added', 'modified', 'deleted')),
|
|
lines_added INTEGER DEFAULT 0,
|
|
lines_deleted INTEGER DEFAULT 0,
|
|
timestamp INTEGER NOT NULL,
|
|
git_commit TEXT,
|
|
FOREIGN KEY (thread_id) REFERENCES threads(id) ON DELETE CASCADE
|
|
);
|
|
`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_file_changes_thread_id ON file_changes(thread_id);`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_file_changes_timestamp ON file_changes(timestamp DESC);`);
|
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_file_changes_file_path ON file_changes(file_path);`);
|
|
}
|
|
}
|
|
exports.DatabaseManager = DatabaseManager;
|
|
// Singleton instance for default usage
|
|
exports.dbManager = new DatabaseManager();
|