@context-sync/server
Version:
MCP server for AI context sync with persistent memory, workspace file access, and intelligent code operations
270 lines • 8.64 kB
JavaScript
/**
* Todo management handlers for Context Sync MCP server
*/
import { randomUUID } from 'crypto';
export class TodoManager {
db;
// Prepared statement cache for 2-5x faster queries
preparedStatements = new Map();
constructor(db) {
this.db = db;
}
/**
* Get or create a prepared statement for faster queries (2-5x performance improvement)
*/
getStatement(sql) {
if (this.preparedStatements.has(sql)) {
return this.preparedStatements.get(sql);
}
const statement = this.db.prepare(sql);
this.preparedStatements.set(sql, statement);
return statement;
}
/**
* Create a new todo item
*/
createTodo(input) {
const id = randomUUID();
const now = new Date().toISOString();
const todo = {
id,
title: input.title,
description: input.description,
status: 'pending',
priority: input.priority || 'medium',
tags: input.tags || [],
dueDate: input.dueDate,
createdAt: now,
updatedAt: now,
projectId: input.projectId
};
const stmt = this.getStatement(`
INSERT INTO todos (
id, title, description, status, priority, tags,
due_date, created_at, updated_at, project_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
stmt.run(todo.id, todo.title, todo.description || null, todo.status, todo.priority, JSON.stringify(todo.tags), todo.dueDate || null, todo.createdAt, todo.updatedAt, todo.projectId || null);
return todo;
}
/**
* Get a todo by ID
*/
getTodo(id) {
const stmt = this.getStatement(`
SELECT * FROM todos WHERE id = ?
`);
const row = stmt.get(id);
if (!row)
return null;
return this.rowToTodo(row);
}
/**
* List todos with optional filtering
*/
listTodos(filter) {
let query = 'SELECT * FROM todos WHERE 1=1';
const params = [];
if (filter?.status) {
if (Array.isArray(filter.status)) {
query += ` AND status IN (${filter.status.map(() => '?').join(',')})`;
params.push(...filter.status);
}
else {
query += ' AND status = ?';
params.push(filter.status);
}
}
if (filter?.priority) {
if (Array.isArray(filter.priority)) {
query += ` AND priority IN (${filter.priority.map(() => '?').join(',')})`;
params.push(...filter.priority);
}
else {
query += ' AND priority = ?';
params.push(filter.priority);
}
}
if (filter?.projectId) {
query += ' AND project_id = ?';
params.push(filter.projectId);
}
if (filter?.dueBefore) {
query += ' AND due_date <= ?';
params.push(filter.dueBefore);
}
if (filter?.dueAfter) {
query += ' AND due_date >= ?';
params.push(filter.dueAfter);
}
if (filter?.search) {
query += ' AND (title LIKE ? OR description LIKE ?)';
const searchPattern = `%${filter.search}%`;
params.push(searchPattern, searchPattern);
}
if (filter?.tags && filter.tags.length > 0) {
// Search for todos that have at least one of the specified tags
const tagConditions = filter.tags.map(() => 'tags LIKE ?').join(' OR ');
query += ` AND (${tagConditions})`;
filter.tags.forEach(tag => {
params.push(`%"${tag}"%`);
});
}
query += ' ORDER BY priority DESC, due_date ASC, created_at DESC';
const stmt = this.getStatement(query);
const rows = stmt.all(...params);
return rows.map(row => this.rowToTodo(row));
}
/**
* Update a todo
*/
updateTodo(input) {
const existing = this.getTodo(input.id);
if (!existing)
return null;
const now = new Date().toISOString();
const updates = [];
const params = [];
if (input.title !== undefined) {
updates.push('title = ?');
params.push(input.title);
}
if (input.description !== undefined) {
updates.push('description = ?');
params.push(input.description);
}
if (input.status !== undefined) {
updates.push('status = ?');
params.push(input.status);
// Set completed_at when marking as completed
if (input.status === 'completed') {
updates.push('completed_at = ?');
params.push(now);
}
}
if (input.priority !== undefined) {
updates.push('priority = ?');
params.push(input.priority);
}
if (input.tags !== undefined) {
updates.push('tags = ?');
params.push(JSON.stringify(input.tags));
}
if (input.dueDate !== undefined) {
updates.push('due_date = ?');
params.push(input.dueDate);
}
if (input.projectId !== undefined) {
updates.push('project_id = ?');
params.push(input.projectId);
}
updates.push('updated_at = ?');
params.push(now);
params.push(input.id);
const stmt = this.getStatement(`
UPDATE todos SET ${updates.join(', ')} WHERE id = ?
`);
stmt.run(...params);
return this.getTodo(input.id);
}
/**
* Delete a todo
*/
deleteTodo(id) {
const stmt = this.getStatement('DELETE FROM todos WHERE id = ?');
const result = stmt.run(id);
return result.changes > 0;
}
/**
* Mark todo as completed
*/
completeTodo(id) {
return this.updateTodo({
id,
status: 'completed'
});
}
/**
* Get todo statistics
*/
getStats(projectId) {
let baseQuery = 'SELECT status, priority, due_date FROM todos';
const params = [];
if (projectId) {
baseQuery += ' WHERE project_id = ?';
params.push(projectId);
}
const stmt = this.getStatement(baseQuery);
const rows = stmt.all(...params);
const stats = {
total: rows.length,
byStatus: {
pending: 0,
in_progress: 0,
completed: 0,
cancelled: 0
},
byPriority: {
low: 0,
medium: 0,
high: 0,
urgent: 0
},
overdue: 0,
dueSoon: 0
};
const now = new Date();
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
rows.forEach(row => {
stats.byStatus[row.status]++;
stats.byPriority[row.priority]++;
if (row.due_date && row.status !== 'completed' && row.status !== 'cancelled') {
const dueDate = new Date(row.due_date);
if (dueDate < now) {
stats.overdue++;
}
else if (dueDate < tomorrow) {
stats.dueSoon++;
}
}
});
return stats;
}
/**
* Get all unique tags across todos
*/
getAllTags() {
const stmt = this.getStatement('SELECT tags FROM todos WHERE tags IS NOT NULL');
const rows = stmt.all();
const tagSet = new Set();
rows.forEach(row => {
try {
const tags = JSON.parse(row.tags);
tags.forEach((tag) => tagSet.add(tag));
}
catch (e) {
// Skip invalid JSON
}
});
return Array.from(tagSet).sort();
}
/**
* Convert database row to Todo object
*/
rowToTodo(row) {
return {
id: row.id,
title: row.title,
description: row.description || undefined,
status: row.status,
priority: row.priority,
tags: row.tags ? JSON.parse(row.tags) : [],
dueDate: row.due_date || undefined,
createdAt: row.created_at,
updatedAt: row.updated_at,
completedAt: row.completed_at || undefined,
projectId: row.project_id || undefined
};
}
}
//# sourceMappingURL=todo-manager.js.map