@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
195 lines (194 loc) • 6.13 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { logger } from "../core/monitoring/logger.js";
import Database from "better-sqlite3";
import { join } from "path";
import { existsSync } from "fs";
class ContextService {
// Using singleton logger from monitoring
db = null;
tasks = /* @__PURE__ */ new Map();
constructor() {
this.initializeDatabase();
}
initializeDatabase() {
try {
const dbPath = join(process.cwd(), ".stackmemory", "context.db");
if (existsSync(dbPath)) {
this.db = new Database(dbPath);
this.loadTasksFromDatabase();
}
} catch (error) {
logger.warn(
"Could not connect to database, using in-memory storage",
error
);
}
}
loadTasksFromDatabase() {
if (!this.db) return;
try {
this.db.exec(`
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'todo',
priority TEXT,
tags TEXT,
external_id TEXT,
external_identifier TEXT,
external_url TEXT,
metadata TEXT,
created_at INTEGER,
updated_at INTEGER
)
`);
const stmt = this.db.prepare("SELECT * FROM tasks");
const rows = stmt.all();
for (const row of rows) {
const task = {
id: row.id,
title: row.title,
description: row.description || "",
status: row.status || "todo",
priority: row.priority || void 0,
tags: row.tags ? JSON.parse(row.tags) : [],
externalId: row.external_id || void 0,
externalIdentifier: row.external_identifier || void 0,
externalUrl: row.external_url || void 0,
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at)
};
this.tasks.set(task.id, task);
}
logger.info(`Loaded ${rows.length} tasks from database`);
} catch (error) {
logger.error("Failed to load tasks from database", error);
}
}
async getTask(id) {
return this.tasks.get(id) || null;
}
async getTaskByExternalId(externalId) {
for (const task of this.tasks.values()) {
if (task.externalId === externalId) {
return task;
}
}
return null;
}
async getAllTasks() {
return Array.from(this.tasks.values());
}
async createTask(taskData) {
const task = {
id: this.generateId(),
title: taskData.title || "Untitled Task",
description: taskData.description || "",
status: taskData.status || "todo",
priority: taskData.priority,
tags: taskData.tags || [],
externalId: taskData.externalId,
externalIdentifier: taskData.externalIdentifier,
externalUrl: taskData.externalUrl,
metadata: taskData.metadata,
createdAt: taskData.createdAt || /* @__PURE__ */ new Date(),
updatedAt: taskData.updatedAt || /* @__PURE__ */ new Date()
};
this.tasks.set(task.id, task);
if (this.db) {
try {
const stmt = this.db.prepare(`
INSERT INTO tasks (id, title, description, status, priority, tags,
external_id, external_identifier, external_url,
metadata, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
stmt.run(
task.id,
task.title,
task.description,
task.status,
task.priority || null,
JSON.stringify(task.tags),
task.externalId || null,
task.externalIdentifier || null,
task.externalUrl || null,
task.metadata ? JSON.stringify(task.metadata) : null,
task.createdAt.getTime(),
task.updatedAt.getTime()
);
} catch (error) {
logger.error("Failed to persist task to database", error);
}
}
logger.debug(`Created task: ${task.id} - ${task.title}`);
return task;
}
async updateTask(id, updates) {
const task = this.tasks.get(id);
if (!task) {
return null;
}
const updatedTask = {
...task,
...updates,
updatedAt: /* @__PURE__ */ new Date()
};
this.tasks.set(id, updatedTask);
if (this.db) {
try {
const stmt = this.db.prepare(`
UPDATE tasks SET title = ?, description = ?, status = ?,
priority = ?, tags = ?, external_id = ?,
external_identifier = ?, external_url = ?,
metadata = ?, updated_at = ?
WHERE id = ?
`);
stmt.run(
updatedTask.title,
updatedTask.description,
updatedTask.status,
updatedTask.priority || null,
JSON.stringify(updatedTask.tags),
updatedTask.externalId || null,
updatedTask.externalIdentifier || null,
updatedTask.externalUrl || null,
updatedTask.metadata ? JSON.stringify(updatedTask.metadata) : null,
updatedTask.updatedAt.getTime(),
id
);
} catch (error) {
logger.error("Failed to update task in database", error);
}
}
logger.debug(`Updated task: ${id} - ${updatedTask.title}`);
return updatedTask;
}
async deleteTask(id) {
const deleted = this.tasks.delete(id);
if (deleted) {
if (this.db) {
try {
const stmt = this.db.prepare("DELETE FROM tasks WHERE id = ?");
stmt.run(id);
} catch (error) {
logger.error("Failed to delete task from database", error);
}
}
logger.debug(`Deleted task: ${id}`);
}
return deleted;
}
generateId() {
return "task_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
}
}
export {
ContextService
};
//# sourceMappingURL=context-service.js.map