UNPKG

@notbnull/mcp-cursor-taskmaster

Version:

A project-scoped MCP server requiring explicit --project-dir configuration for task management, memory retrieval, and agentic workflows

415 lines 16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkflowEngine = void 0; const promises_1 = require("timers/promises"); const approval_ui_js_1 = require("./approval-ui.js"); class WorkflowEngine { taskStore; memoryStore; config; constructor(taskStore, memoryStore, config = {}) { this.taskStore = taskStore; this.memoryStore = memoryStore; this.config = { pauseAfterEachTask: false, pauseOnError: true, maxRetries: 3, checkpointFrequency: "error", requireApproval: true, ...config, }; } /** * Parse a user prompt and generate a hierarchical task structure */ async parsePromptToTasks(query, title, description) { // Create the prompt record const prompt = await this.taskStore.createPrompt({ title: title || "Generated from Query", description: description || `Auto-generated prompt from: ${query.substring(0, 100)}...`, originalQuery: query, status: "draft", taskIds: [], }); // Store the original query in memory for future reference await this.memoryStore.set(`prompt_${prompt.id}_query`, query, { promptId: prompt.id, type: "original_query", }); // Log workflow action await this.taskStore.logWorkflowAction("prompt_created", "success", undefined, { promptId: prompt.id, query: query.substring(0, 200) }); // For now, we'll create a basic task structure // In a real implementation, you'd use an LLM to parse the query const mainTask = await this.createTaskFromQuery(query, prompt.id); // Update prompt with task ID await this.taskStore.updatePrompt(prompt.id, { taskIds: [mainTask.id], }); return { promptId: prompt.id, taskIds: [mainTask.id], }; } /** * Create a task from a query (simplified implementation) */ async createTaskFromQuery(query, promptId) { // This is a simplified implementation. In practice, you'd use an LLM to break down the query const task = await this.taskStore.createTask({ title: `Execute: ${query.substring(0, 50)}${query.length > 50 ? "..." : ""}`, description: query, status: "pending", priority: "medium", metadata: { promptId, type: "main_task", needsUserApproval: true, }, }); return task; } /** * Request user approval for a set of tasks */ async requestUserApproval(taskIds) { // Mark tasks as awaiting approval for (const taskId of taskIds) { await this.taskStore.updateTask(taskId, { status: "awaiting_approval", }); } // Log workflow action await this.taskStore.logWorkflowAction("user_approval_requested", "pending", undefined, { taskIds, timestamp: new Date().toISOString() }); // Store approval checkpoint const state = await this.taskStore.getWorkflowState(); await this.taskStore.updateWorkflowState({ ...state, checkpointData: { ...state.checkpointData, pendingApproval: { taskIds, requestedAt: new Date().toISOString(), }, }, }); // If approval is not required, auto-approve if (!this.config.requireApproval) { return { approved: true, userFeedback: "Auto-approved (approval not required)", }; } // Get the approval UI server const approvalUI = (0, approval_ui_js_1.getApprovalUIServer)(); // Request approval for each task const approvalPromises = taskIds.map(async (taskId) => { const task = await this.taskStore.getTask(taskId); if (!task) return null; const prompt = task.metadata?.promptId ? await this.taskStore.getPrompt(task.metadata.promptId) : null; const request = { taskId, task, promptTitle: prompt?.title, promptDescription: prompt?.description, }; return approvalUI.requestApproval(request); }); // Wait for all approvals const responses = await Promise.all(approvalPromises); const validResponses = responses.filter((r) => r !== null); // Process responses const allApproved = validResponses.every((r) => r.approved); const modifiedTasks = []; const feedbacks = []; for (const response of validResponses) { if (response.modifications) { await this.taskStore.updateTask(response.taskId, response.modifications); modifiedTasks.push(response.taskId); } if (response.userFeedback) { feedbacks.push(response.userFeedback); } } return { approved: allApproved, modifiedTasks: modifiedTasks.length > 0 ? modifiedTasks : undefined, userFeedback: feedbacks.length > 0 ? feedbacks.join("; ") : undefined, }; } /** * Execute a workflow from a prompt */ async executeWorkflow(promptId) { const prompt = await this.taskStore.getPrompt(promptId); if (!prompt) { throw new Error(`Prompt ${promptId} not found`); } // Update workflow state await this.taskStore.updateWorkflowState({ currentPromptId: promptId, }); // Update prompt status await this.taskStore.updatePrompt(promptId, { status: "executing" }); const completedTasks = []; const failedTasks = []; try { // Execute tasks in the prompt for (const taskId of prompt.taskIds) { const result = await this.executeTaskHierarchy(taskId); if (result.success) { completedTasks.push(...result.completedTasks); } else { failedTasks.push(...result.failedTasks); if (this.config.pauseOnError) { await this.taskStore.updateWorkflowState({ currentTaskId: taskId, checkpointData: { error: result.error, failedTasks: result.failedTasks, }, }); return { success: false, completedTasks, failedTasks, pausedAt: taskId, needsUserInput: true, }; } } } // Mark prompt as completed await this.taskStore.updatePrompt(promptId, { status: "completed" }); return { success: true, completedTasks, failedTasks, }; } catch (error) { await this.taskStore.updatePrompt(promptId, { status: "failed" }); await this.taskStore.logWorkflowAction("workflow_error", "failed", undefined, { error: error instanceof Error ? error.message : "Unknown error" }); throw error; } } /** * Execute a task and its children hierarchically */ async executeTaskHierarchy(taskId) { const task = await this.taskStore.getTask(taskId); if (!task) { return { success: false, completedTasks: [], failedTasks: [taskId], error: `Task ${taskId} not found`, }; } let completedTasks = []; let failedTasks = []; try { // Check if task needs user approval if (task.metadata?.needsUserApproval && task.status === "awaiting_approval") { return { success: false, completedTasks: [], failedTasks: [], error: "Task awaiting user approval", }; } // Execute the task itself await this.taskStore.updateTask(taskId, { status: "in_progress" }); await this.taskStore.logWorkflowAction("task_started", "in_progress", taskId); // Simulate task execution (in practice, this would invoke actual tools/actions) const executionResult = await this.executeTask(task); if (executionResult.success) { await this.taskStore.updateTask(taskId, { status: "completed", actualTime: executionResult.executionTime, }); completedTasks.push(taskId); await this.taskStore.logWorkflowAction("task_completed", "success", taskId, { executionTime: executionResult.executionTime, }); } else { await this.taskStore.updateTask(taskId, { status: "failed" }); failedTasks.push(taskId); await this.taskStore.logWorkflowAction("task_failed", "failed", taskId, { error: executionResult.error, }); return { success: false, completedTasks, failedTasks, error: executionResult.error, }; } // Execute child tasks if any if (task.children && task.children.length > 0) { for (const childId of task.children) { const childResult = await this.executeTaskHierarchy(childId); completedTasks.push(...childResult.completedTasks); failedTasks.push(...childResult.failedTasks); if (!childResult.success && this.config.pauseOnError) { return { success: false, completedTasks, failedTasks, error: childResult.error, }; } } } return { success: failedTasks.length === 0, completedTasks, failedTasks, }; } catch (error) { await this.taskStore.updateTask(taskId, { status: "failed" }); const errorMessage = error instanceof Error ? error.message : "Unknown error"; await this.taskStore.logWorkflowAction("task_error", "failed", taskId, { error: errorMessage, }); return { success: false, completedTasks, failedTasks: [taskId], error: errorMessage, }; } } /** * Execute a single task (placeholder implementation) */ async executeTask(task) { const startTime = Date.now(); try { // Store task execution context in memory await this.memoryStore.set(`task_execution_${task.id}`, JSON.stringify({ task, startTime: new Date().toISOString(), status: "executing", }), { taskId: task.id, type: "execution_context" }); // Simulate task execution // In a real implementation, this would: // 1. Parse the task description to determine what actions to take // 2. Execute the appropriate tools/commands // 3. Handle results and update task status await (0, promises_1.setTimeout)(100); // Simulate work const executionTime = Date.now() - startTime; // Store execution result in memory await this.memoryStore.set(`task_result_${task.id}`, JSON.stringify({ taskId: task.id, success: true, executionTime, completedAt: new Date().toISOString(), }), { taskId: task.id, type: "execution_result" }); return { success: true, executionTime, }; } catch (error) { const executionTime = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : "Unknown error"; // Store error in memory await this.memoryStore.set(`task_error_${task.id}`, JSON.stringify({ taskId: task.id, error: errorMessage, executionTime, failedAt: new Date().toISOString(), }), { taskId: task.id, type: "execution_error" }); return { success: false, executionTime, error: errorMessage, }; } } /** * Approve a task for execution */ async approveTask(taskId, userFeedback) { const task = await this.taskStore.getTask(taskId); if (!task) return false; await this.taskStore.updateTask(taskId, { status: "pending", metadata: { ...task.metadata, userApproved: true, userFeedback, approvedAt: new Date().toISOString(), }, }); await this.taskStore.logWorkflowAction("task_approved", "success", taskId, { userFeedback, }); return true; } /** * Reject a task and optionally provide modifications */ async rejectTask(taskId, userFeedback, modifications) { const task = await this.taskStore.getTask(taskId); if (!task) return false; if (modifications) { await this.taskStore.updateTask(taskId, { ...modifications, status: "pending", metadata: { ...task.metadata, userRejected: true, userFeedback, rejectedAt: new Date().toISOString(), modified: true, }, }); } else { await this.taskStore.updateTask(taskId, { status: "failed", metadata: { ...task.metadata, userRejected: true, userFeedback, rejectedAt: new Date().toISOString(), }, }); } await this.taskStore.logWorkflowAction("task_rejected", "failed", taskId, { userFeedback, hasModifications: !!modifications, }); return true; } /** * Resume workflow execution from a checkpoint */ async resumeWorkflow() { const state = await this.taskStore.getWorkflowState(); if (!state.currentPromptId) { throw new Error("No workflow to resume"); } const prompt = await this.taskStore.getPrompt(state.currentPromptId); if (!prompt) { throw new Error("Current prompt not found"); } // Resume from current task or start from beginning const resumeFromTaskId = state.currentTaskId; await this.taskStore.logWorkflowAction("workflow_resumed", "in_progress", resumeFromTaskId); return await this.executeWorkflow(state.currentPromptId); } } exports.WorkflowEngine = WorkflowEngine; //# sourceMappingURL=workflow-engine.js.map