spa/.claude/skills/thread-manager/dist/database/threads-dao.js

161 lines
6.2 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ThreadsDAO = void 0;
const uuid_1 = require("uuid");
class ThreadsDAO {
dbManager;
constructor(dbManager) {
this.dbManager = dbManager;
}
get db() {
return this.dbManager.getDatabase();
}
create(input) {
const id = input.id || (0, uuid_1.v4)();
const now = new Date();
const thread = {
id,
sessionId: input.sessionId || id,
title: input.title || 'New Thread',
description: input.description,
gitBranch: input.gitBranch,
createdAt: input.createdAt || now,
updatedAt: input.updatedAt || now,
messageCount: input.messageCount || 0,
isActive: input.isActive || false,
metadata: {
filesChanged: input.metadata?.filesChanged || 0,
linesAdded: input.metadata?.linesAdded || 0,
linesDeleted: input.metadata?.linesDeleted || 0,
tags: input.metadata?.tags || []
}
};
const stmt = this.db.prepare(`
INSERT INTO threads (
id, session_id, git_branch, title, description, created_at, updated_at,
message_count, is_active, files_changed,
lines_added, lines_deleted, tags
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
`);
stmt.run(thread.id, thread.sessionId, thread.gitBranch || null, thread.title, thread.description || null, thread.createdAt.getTime(), thread.updatedAt.getTime(), thread.messageCount, thread.isActive ? 1 : 0, thread.metadata.filesChanged, thread.metadata.linesAdded, thread.metadata.linesDeleted, JSON.stringify(thread.metadata.tags || []));
return thread;
}
findById(id) {
const stmt = this.db.prepare('SELECT * FROM threads WHERE id = ?');
const row = stmt.get(id);
if (!row)
return null;
return this.mapRowToThread(row);
}
findAll(options) {
const { limit = 50, offset = 0, sortBy = 'updatedAt', order = 'desc', tags } = options || {};
let query = 'SELECT * FROM threads';
let countQuery = 'SELECT COUNT(*) as total FROM threads';
const params = [];
const conditions = [];
if (tags && tags.length > 0) {
// Note: This is a simple implementation. Ideally use FTS or separate tags table.
// For now, using LIKE on JSON string which is suboptimal but works for MVP.
const tagConditions = tags.map(tag => {
params.push(`%"${tag}"%`);
return 'tags LIKE ?';
});
conditions.push(`(${tagConditions.join(' OR ')})`);
}
if (conditions.length > 0) {
const whereClause = ` WHERE ${conditions.join(' AND ')}`;
query += whereClause;
countQuery += whereClause;
}
const sortColumn = sortBy === 'updatedAt' ? 'updated_at' :
sortBy === 'createdAt' ? 'created_at' : 'message_count';
query += ` ORDER BY ${sortColumn} ${order.toUpperCase()}`;
query += ` LIMIT ? OFFSET ?`;
// params for where clause + limit + offset
const queryParams = [...params, limit, offset];
const rows = this.db.prepare(query).all(...queryParams);
const totalRow = this.db.prepare(countQuery).get(...params);
return {
threads: rows.map(this.mapRowToThread),
total: totalRow.total
};
}
update(id, updates) {
const current = this.findById(id);
if (!current)
return null;
// Merge updates
const updated = { ...current, ...updates };
updated.updatedAt = new Date(); // Always update updatedAt
// Ensure metadata is merged correctly if provided
if (updates.metadata) {
updated.metadata = { ...current.metadata, ...updates.metadata };
}
const stmt = this.db.prepare(`
UPDATE threads SET
title = ?,
description = ?,
git_branch = ?,
updated_at = ?,
message_count = ?,
is_active = ?,
files_changed = ?,
lines_added = ?,
lines_deleted = ?,
tags = ?
WHERE id = ?
`);
stmt.run(updated.title, updated.description || null, updated.gitBranch || null, updated.updatedAt.getTime(), updated.messageCount, updated.isActive ? 1 : 0, updated.metadata.filesChanged, updated.metadata.linesAdded, updated.metadata.linesDeleted, JSON.stringify(updated.metadata.tags || []), id);
return updated;
}
delete(id) {
const stmt = this.db.prepare('DELETE FROM threads WHERE id = ?');
const result = stmt.run(id);
return result.changes > 0;
}
setActive(id) {
const trx = this.db.transaction(() => {
// Deactivate all
this.db.prepare('UPDATE threads SET is_active = 0').run();
// Activate target
const result = this.db.prepare('UPDATE threads SET is_active = 1 WHERE id = ?').run(id);
return result.changes > 0;
});
return trx();
}
getActive() {
const stmt = this.db.prepare('SELECT * FROM threads WHERE is_active = 1 LIMIT 1');
const row = stmt.get();
if (!row)
return null;
return this.mapRowToThread(row);
}
findByPrefix(prefix) {
const stmt = this.db.prepare(`SELECT * FROM threads WHERE id LIKE ? || '%'`);
const rows = stmt.all(prefix);
return rows.map(this.mapRowToThread);
}
mapRowToThread(row) {
return {
id: row.id,
sessionId: row.session_id || row.id,
title: row.title,
description: row.description || undefined,
gitBranch: row.git_branch || undefined,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at),
messageCount: row.message_count,
isActive: Boolean(row.is_active),
metadata: {
filesChanged: row.files_changed,
linesAdded: row.lines_added,
linesDeleted: row.lines_deleted,
tags: JSON.parse(row.tags || '[]')
}
};
}
}
exports.ThreadsDAO = ThreadsDAO;