spa/.claude/skills/thread-manager/dist/database/db.js

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();