UNPKG

prompt-scheduler-mcp

Version:

MCP server for managing Augment Prompt Scheduler tasks with local workspace storage and real-time countdown features

259 lines 10.1 kB
import * as fs from 'fs/promises'; import * as path from 'path'; import * as crypto from 'crypto'; export class PromptSchedulerManager { tasksFilePath; configFilePath; workspaceId; constructor(workspaceId) { // Use workspace-specific storage inside the workspace directory this.workspaceId = workspaceId || this.detectWorkspace(); // Store tasks inside the workspace directory in a .augment-scheduler folder const workspaceDataDir = path.join(this.workspaceId, '.augment-scheduler'); this.tasksFilePath = path.join(workspaceDataDir, 'tasks.json'); this.configFilePath = path.join(workspaceDataDir, 'config.json'); console.error(`MCP Server using workspace: ${this.workspaceId}`); console.error(`Tasks will be stored in: ${this.tasksFilePath}`); this.ensureDataDirectory(); } detectWorkspace() { // Priority order for workspace detection: // 1. Environment variables from MCP client // 2. Current working directory (where MCP client is running) // 3. Look for VS Code workspace indicators const cwd = process.cwd(); // Check for VS Code specific environment variables if (process.env.VSCODE_CWD) { console.error(`Using VSCODE_CWD: ${process.env.VSCODE_CWD}`); return process.env.VSCODE_CWD; } // Check for workspace path from MCP client environment if (process.env.WORKSPACE_PATH) { console.error(`Using WORKSPACE_PATH: ${process.env.WORKSPACE_PATH}`); return process.env.WORKSPACE_PATH; } // Use current working directory (where MCP client is running) // This should match where the user is working console.error(`Using current working directory: ${cwd}`); return cwd; } getWorkspaceHash(workspaceId) { // Use the same hashing algorithm as the VS Code extension return crypto.createHash('md5').update(workspaceId).digest('hex').substring(0, 8); } async ensureDataDirectory() { const dataDir = path.dirname(this.tasksFilePath); try { await fs.access(dataDir); } catch { await fs.mkdir(dataDir, { recursive: true }); } } setWorkspace(workspaceId) { this.workspaceId = workspaceId; // Store tasks inside the workspace directory in a .augment-scheduler folder const workspaceDataDir = path.join(workspaceId, '.augment-scheduler'); this.tasksFilePath = path.join(workspaceDataDir, 'tasks.json'); this.configFilePath = path.join(workspaceDataDir, 'config.json'); console.error(`MCP Server switched to workspace: ${workspaceId}`); console.error(`Tasks will be stored in: ${this.tasksFilePath}`); } getWorkspaceInfo() { return { workspaceId: this.workspaceId, tasksFile: this.tasksFilePath, configFile: this.configFilePath }; } generateTaskId() { return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } async loadTasks() { try { const data = await fs.readFile(this.tasksFilePath, 'utf-8'); return JSON.parse(data); } catch (error) { // File doesn't exist or is invalid, return empty array return []; } } async saveTasks(tasks) { await this.ensureDataDirectory(); await fs.writeFile(this.tasksFilePath, JSON.stringify(tasks, null, 2)); } async createTask(taskData) { try { const tasks = await this.loadTasks(); const newTask = { id: this.generateTaskId(), title: taskData.title, prompt: taskData.prompt, priority: taskData.priority || 'medium', createdAt: Date.now(), tags: taskData.tags || [], isActive: taskData.isActive !== false, executionCount: 0, maxExecutions: taskData.maxExecutions || -1, executionMode: taskData.executionMode || 'infinite', idleThresholdMinutes: taskData.idleThresholdMinutes || 5, idleStartTime: undefined, lastStatusCheck: 'unknown' }; tasks.push(newTask); await this.saveTasks(tasks); return { success: true, task: newTask }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async listTasks(options = {}) { try { let tasks = await this.loadTasks(); if (options.activeOnly) { tasks = tasks.filter(task => task.isActive); } if (options.priority) { tasks = tasks.filter(task => task.priority === options.priority); } return { success: true, tasks }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async getTask(id) { try { const tasks = await this.loadTasks(); const task = tasks.find(t => t.id === id); if (!task) { return { success: false, error: 'Task not found' }; } return { success: true, task }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async updateTask(updateData) { try { const tasks = await this.loadTasks(); const taskIndex = tasks.findIndex(t => t.id === updateData.id); if (taskIndex === -1) { return { success: false, error: 'Task not found' }; } // Update the task with provided data const updatedTask = { ...tasks[taskIndex] }; Object.keys(updateData).forEach(key => { if (key !== 'id' && updateData[key] !== undefined) { updatedTask[key] = updateData[key]; } }); tasks[taskIndex] = updatedTask; await this.saveTasks(tasks); return { success: true, task: updatedTask }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async deleteTask(id) { try { const tasks = await this.loadTasks(); const taskIndex = tasks.findIndex(t => t.id === id); if (taskIndex === -1) { return { success: false, error: 'Task not found' }; } tasks.splice(taskIndex, 1); await this.saveTasks(tasks); return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async executeTask(id) { try { const tasks = await this.loadTasks(); const task = tasks.find(t => t.id === id); if (!task) { return { success: false, error: 'Task not found' }; } if (!task.isActive) { return { success: false, error: 'Task is not active' }; } // Update execution count and timestamp task.executionCount++; task.lastExecuted = Date.now(); // Handle execution limits if (task.executionMode === 'once' && task.executionCount >= 1) { task.isActive = false; } else if (task.executionMode === 'limited' && task.maxExecutions > 0 && task.executionCount >= task.maxExecutions) { task.isActive = false; } await this.saveTasks(tasks); // Create an execution request file that the VS Code extension can monitor const executionRequest = { taskId: id, prompt: task.prompt, timestamp: Date.now(), title: task.title }; const executionDir = path.join(path.dirname(this.tasksFilePath), 'executions'); await fs.mkdir(executionDir, { recursive: true }); const executionFile = path.join(executionDir, `execution_${Date.now()}_${id}.json`); await fs.writeFile(executionFile, JSON.stringify(executionRequest, null, 2)); return { success: true, message: `Task "${task.title}" queued for execution. Execution count: ${task.executionCount}` }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async getSchedulerStatus() { try { const tasks = await this.loadTasks(); const activeTasks = tasks.filter(t => t.isActive).length; const completedTasks = tasks.filter(t => !t.isActive).length; const lastActivity = Math.max(...tasks.map(t => t.lastExecuted || 0)); const status = { totalTasks: tasks.length, activeTasks, completedTasks, lastActivity: lastActivity > 0 ? lastActivity : undefined, isRunning: true // Assume running if we can access the data }; return { success: true, status }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } } //# sourceMappingURL=scheduler-manager.js.map