UNPKG

@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.

240 lines (237 loc) 7.3 kB
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"; class TaskHandlers { constructor(deps) { this.deps = deps; } /** * Create a new task */ async handleCreateTask(args) { try { const { title, description, priority = "medium", tags = [], parent_id } = args; if (!title) { throw new Error("Task title is required"); } const taskPriority = this.validatePriority(priority); const taskId = await this.deps.taskStore.createTask({ title, description: description || "", priority: taskPriority, tags: Array.isArray(tags) ? tags : [tags].filter(Boolean), parentId: parent_id, frameId: "default-frame" }); logger.info("Created task", { taskId, title, priority }); return { content: [ { type: "text", text: `Created task: ${title} (${taskId})` } ], metadata: { taskId, title, priority: taskPriority } }; } catch (error) { logger.error("Error creating task", error instanceof Error ? error : new Error(String(error))); throw error; } } /** * Update task status */ async handleUpdateTaskStatus(args) { try { const { task_id, status, progress } = args; if (!task_id) { throw new Error("Task ID is required"); } if (!status) { throw new Error("Status is required"); } const validStatus = this.validateStatus(status); await this.deps.taskStore.updateTaskStatus(task_id, validStatus, progress); const task = await this.deps.taskStore.getTask(task_id); if (!task) { throw new Error(`Task not found: ${task_id}`); } logger.info("Updated task status", { taskId: task_id, status: validStatus, progress }); return { content: [ { type: "text", text: `Updated task ${task.title} to ${validStatus}${progress ? ` (${progress}% complete)` : ""}` } ], metadata: { taskId: task_id, status: validStatus, progress } }; } catch (error) { logger.error("Error updating task status", error instanceof Error ? error : new Error(String(error))); throw error; } } /** * Get active tasks with filtering */ async handleGetActiveTasks(args) { try { const { status, priority, limit = 20, include_completed = false, tags = [], search } = args; const filters = {}; if (status) { filters.status = this.validateStatus(status); } if (priority) { filters.priority = this.validatePriority(priority); } if (!include_completed) { filters.excludeCompleted = true; } if (Array.isArray(tags) && tags.length > 0) { filters.tags = tags; } if (search) { filters.search = search; } const tasks = await this.deps.taskStore.getActiveTasks(); const taskSummary = tasks.map((task) => ({ id: task.id, title: task.title, status: task.status, priority: task.priority, tags: task.tags || [], created: new Date(task.created_at * 1e3).toLocaleDateString(), progress: 0 })); const summaryText = taskSummary.length > 0 ? taskSummary.map( (t) => `${t.id}: ${t.title} [${t.status}] (${t.priority})` ).join("\n") : "No tasks found matching criteria"; return { content: [ { type: "text", text: `Active Tasks (${tasks.length}): ${summaryText}` } ], metadata: { tasks: taskSummary, totalCount: tasks.length, filters } }; } catch (error) { logger.error("Error getting active tasks", error instanceof Error ? error : new Error(String(error))); throw error; } } /** * Get task metrics and analytics */ async handleGetTaskMetrics(args) { try { const metrics = await this.deps.taskStore.getMetrics(); const metricsText = ` Task Metrics: - Total: ${metrics.total_tasks} - Blocked: ${metrics.blocked_tasks} - Overdue: ${metrics.overdue_tasks} - Completion Rate: ${(metrics.completion_rate * 100).toFixed(1)}% - Effort Accuracy: ${(metrics.avg_effort_accuracy * 100).toFixed(1)}% By Priority: ${Object.entries(metrics.by_priority || {}).map(([priority, count]) => `- ${priority}: ${count}`).join("\n")} By Status: ${Object.entries(metrics.by_status || {}).map(([status, count]) => `- ${status}: ${count}`).join("\n")} `.trim(); return { content: [ { type: "text", text: metricsText } ], metadata: metrics }; } catch (error) { logger.error("Error getting task metrics", error instanceof Error ? error : new Error(String(error))); throw error; } } /** * Add task dependency */ async handleAddTaskDependency(args) { try { const { task_id, depends_on, dependency_type = "blocks" } = args; if (!task_id || !depends_on) { throw new Error("Both task_id and depends_on are required"); } await this.deps.taskStore.addDependency(task_id, depends_on); const task = await this.deps.taskStore.getTask(task_id); const dependencyTask = await this.deps.taskStore.getTask(depends_on); if (!task || !dependencyTask) { throw new Error("One or both tasks not found"); } logger.info("Added task dependency", { taskId: task_id, dependsOn: depends_on, type: dependency_type }); return { content: [ { type: "text", text: `Added dependency: ${task.title} depends on ${dependencyTask.title} (${dependency_type})` } ], metadata: { taskId: task_id, dependsOn: depends_on, dependencyType: dependency_type } }; } catch (error) { logger.error("Error adding task dependency", error instanceof Error ? error : new Error(String(error))); throw error; } } /** * Validate task priority */ validatePriority(priority) { const validPriorities = ["low", "medium", "high", "urgent"]; const normalizedPriority = priority.toLowerCase(); if (!validPriorities.includes(normalizedPriority)) { throw new Error(`Invalid priority: ${priority}. Must be one of: ${validPriorities.join(", ")}`); } return normalizedPriority; } /** * Validate task status */ validateStatus(status) { const validStatuses = ["pending", "in_progress", "blocked", "completed", "cancelled"]; const normalizedStatus = status.toLowerCase().replace("_", "-"); if (!validStatuses.includes(normalizedStatus)) { throw new Error(`Invalid status: ${status}. Must be one of: ${validStatuses.join(", ")}`); } return normalizedStatus; } } export { TaskHandlers }; //# sourceMappingURL=task-handlers.js.map