@notbnull/mcp-cursor-taskmaster
Version:
A project-scoped MCP server requiring explicit --project-dir configuration for task management, memory retrieval, and agentic workflows
230 lines • 8.62 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskStore = void 0;
const path_1 = __importDefault(require("path"));
const promises_1 = __importDefault(require("fs/promises"));
const crypto_1 = __importDefault(require("crypto"));
class TaskStore {
dataDir;
tasksPath;
promptsPath;
listsPath;
workflowPath;
constructor(projectDir) {
// Use the provided project directory directly - it's validated upstream
this.dataDir = path_1.default.join(path_1.default.resolve(projectDir), ".taskmaster");
this.tasksPath = path_1.default.join(this.dataDir, "tasks.json");
this.promptsPath = path_1.default.join(this.dataDir, "prompts.json");
this.listsPath = path_1.default.join(this.dataDir, "lists.json");
this.workflowPath = path_1.default.join(this.dataDir, "workflow.json");
}
async initialize() {
try {
// Ensure .taskmaster directory exists
console.error(`TaskStore: Creating directory ${this.dataDir}`);
await promises_1.default.mkdir(this.dataDir, { recursive: true });
// Initialize JSON files if they don't exist
await this.ensureFileExists(this.tasksPath, {});
await this.ensureFileExists(this.promptsPath, {});
await this.ensureFileExists(this.listsPath, {});
await this.ensureFileExists(this.workflowPath, {
executionLog: [],
});
console.error(`TaskStore: Successfully initialized in ${this.dataDir}`);
}
catch (error) {
console.error(`TaskStore: Failed to initialize in ${this.dataDir}:`, error);
throw new Error(`Failed to initialize TaskStore: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
async ensureFileExists(filePath, defaultContent) {
try {
await promises_1.default.access(filePath);
}
catch {
await promises_1.default.writeFile(filePath, JSON.stringify(defaultContent, null, 2));
}
}
async readJsonFile(filePath) {
const content = await promises_1.default.readFile(filePath, "utf-8");
return JSON.parse(content);
}
async writeJsonFile(filePath, data) {
await promises_1.default.writeFile(filePath, JSON.stringify(data, null, 2));
}
// Task operations
async createTask(taskData) {
const tasks = await this.readJsonFile(this.tasksPath);
const task = {
id: crypto_1.default.randomUUID(),
...taskData,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
children: [],
};
tasks[task.id] = task;
// If this task has a parent, add it to parent's children
if (task.parentId && tasks[task.parentId]) {
const parent = tasks[task.parentId];
parent.children = parent.children || [];
parent.children.push(task.id);
parent.updatedAt = new Date().toISOString();
}
await this.writeJsonFile(this.tasksPath, tasks);
return task;
}
async getTask(id) {
const tasks = await this.readJsonFile(this.tasksPath);
return tasks[id] || null;
}
async getAllTasks() {
const tasks = await this.readJsonFile(this.tasksPath);
return Object.values(tasks);
}
async updateTask(id, updates) {
const tasks = await this.readJsonFile(this.tasksPath);
if (!tasks[id])
return null;
tasks[id] = {
...tasks[id],
...updates,
updatedAt: new Date().toISOString(),
};
await this.writeJsonFile(this.tasksPath, tasks);
return tasks[id];
}
async deleteTask(id) {
const tasks = await this.readJsonFile(this.tasksPath);
if (!tasks[id])
return false;
const task = tasks[id];
// Remove from parent's children if it has a parent
if (task.parentId && tasks[task.parentId]) {
tasks[task.parentId].children =
tasks[task.parentId].children?.filter((childId) => childId !== id) ||
[];
tasks[task.parentId].updatedAt = new Date().toISOString();
}
// Delete all child tasks recursively
if (task.children) {
for (const childId of task.children) {
await this.deleteTask(childId);
}
}
delete tasks[id];
await this.writeJsonFile(this.tasksPath, tasks);
return true;
}
// Prompt operations
async createPrompt(promptData) {
const prompts = await this.readJsonFile(this.promptsPath);
const prompt = {
id: crypto_1.default.randomUUID(),
...promptData,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
prompts[prompt.id] = prompt;
await this.writeJsonFile(this.promptsPath, prompts);
return prompt;
}
async getPrompt(id) {
const prompts = await this.readJsonFile(this.promptsPath);
return prompts[id] || null;
}
async getAllPrompts() {
const prompts = await this.readJsonFile(this.promptsPath);
return Object.values(prompts);
}
async updatePrompt(id, updates) {
const prompts = await this.readJsonFile(this.promptsPath);
if (!prompts[id])
return null;
prompts[id] = {
...prompts[id],
...updates,
updatedAt: new Date().toISOString(),
};
await this.writeJsonFile(this.promptsPath, prompts);
return prompts[id];
}
// TaskList operations
async createList(listData) {
const lists = await this.readJsonFile(this.listsPath);
const list = {
id: crypto_1.default.randomUUID(),
...listData,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
lists[list.id] = list;
await this.writeJsonFile(this.listsPath, lists);
return list;
}
async getList(id) {
const lists = await this.readJsonFile(this.listsPath);
return lists[id] || null;
}
async getAllLists() {
const lists = await this.readJsonFile(this.listsPath);
return Object.values(lists);
}
// Workflow operations
async getWorkflowState() {
return await this.readJsonFile(this.workflowPath);
}
async updateWorkflowState(updates) {
const currentState = await this.getWorkflowState();
const newState = { ...currentState, ...updates };
await this.writeJsonFile(this.workflowPath, newState);
return newState;
}
async logWorkflowAction(action, status, taskId, details) {
const state = await this.getWorkflowState();
state.executionLog.push({
timestamp: new Date().toISOString(),
action,
taskId,
status,
details,
});
await this.writeJsonFile(this.workflowPath, state);
}
// Utility methods
async getTaskHierarchy(rootTaskId) {
const tasks = await this.readJsonFile(this.tasksPath);
const allTasks = Object.values(tasks);
if (rootTaskId) {
return this.buildHierarchy(allTasks, rootTaskId);
}
// Return all root tasks (no parent) with their hierarchies
const rootTasks = allTasks.filter((task) => !task.parentId);
const result = [];
for (const rootTask of rootTasks) {
result.push(...this.buildHierarchy(allTasks, rootTask.id));
}
return result;
}
buildHierarchy(allTasks, rootId) {
const taskMap = new Map(allTasks.map((task) => [task.id, task]));
const result = [];
const addTaskWithChildren = (taskId, depth = 0) => {
const task = taskMap.get(taskId);
if (!task)
return;
result.push({ ...task, metadata: { ...task.metadata, depth } });
if (task.children) {
for (const childId of task.children) {
addTaskWithChildren(childId, depth + 1);
}
}
};
addTaskWithChildren(rootId);
return result;
}
}
exports.TaskStore = TaskStore;
//# sourceMappingURL=task-store.js.map