UNPKG

myaidev-method

Version:

Comprehensive development framework with SPARC methodology for AI-assisted software development, multi-platform publishing (WordPress, PayloadCMS, Astro, Docusaurus, Mintlify), and Coolify deployment

314 lines (270 loc) • 8.6 kB
/** * MyAIDev Method - Task Manager * Simple priority-based task queue with JSON persistence * Version: 1.0.0 */ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, unlinkSync } from 'fs'; import { join } from 'path'; export class TaskManager { constructor(workingDir = process.cwd()) { this.workingDir = workingDir; this.myaidevDir = join(workingDir, '.myaidev-method'); this.tasksDir = join(this.myaidevDir, 'tasks'); this.initializeDirectories(); } /** * Initialize task directories */ initializeDirectories() { if (!existsSync(this.tasksDir)) { mkdirSync(this.tasksDir, { recursive: true }); } } /** * Create a new task * @param {string} description - Task description * @param {Object} options - Task options * @returns {Object} Created task object */ createTask(description, options = {}) { const task = { id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, description, type: options.type || 'development', priority: this.validatePriority(options.priority), status: 'queued', assignTo: options.assignTo || null, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), metadata: options.metadata || {}, dependencies: options.dependencies || [], outputs: [] }; const taskFile = join(this.tasksDir, `${task.id}.json`); writeFileSync(taskFile, JSON.stringify(task, null, 2), 'utf8'); console.log(`āœ… Task created: ${task.id}`); console.log(` Description: ${description}`); console.log(` Priority: ${task.priority}`); console.log(` Type: ${task.type}`); return task; } /** * Validate priority value (1-10) * @param {number} priority - Priority value * @returns {number} Validated priority (default: 5) */ validatePriority(priority) { if (typeof priority !== 'number') return 5; return Math.max(1, Math.min(10, Math.floor(priority))); } /** * Get task by ID * @param {string} taskId - Task identifier * @returns {Object|null} Task object or null if not found */ getTask(taskId) { const taskFile = join(this.tasksDir, `${taskId}.json`); if (!existsSync(taskFile)) { return null; } return JSON.parse(readFileSync(taskFile, 'utf8')); } /** * Update task * @param {string} taskId - Task identifier * @param {Object} updates - Fields to update * @returns {Object} Updated task object */ updateTask(taskId, updates) { const task = this.getTask(taskId); if (!task) { throw new Error(`Task not found: ${taskId}`); } // Merge updates Object.assign(task, updates, { updatedAt: new Date().toISOString() }); // Validate priority if updated if (updates.priority !== undefined) { task.priority = this.validatePriority(updates.priority); } const taskFile = join(this.tasksDir, `${taskId}.json`); writeFileSync(taskFile, JSON.stringify(task, null, 2), 'utf8'); return task; } /** * Update task status * @param {string} taskId - Task identifier * @param {string} status - New status (queued, running, completed, failed) * @returns {Object} Updated task */ updateStatus(taskId, status) { const validStatuses = ['queued', 'running', 'completed', 'failed', 'cancelled']; if (!validStatuses.includes(status)) { throw new Error(`Invalid status: ${status}. Must be one of: ${validStatuses.join(', ')}`); } return this.updateTask(taskId, { status }); } /** * Add output to task * @param {string} taskId - Task identifier * @param {string} output - Output file path or description * @returns {Object} Updated task */ addOutput(taskId, output) { const task = this.getTask(taskId); if (!task) { throw new Error(`Task not found: ${taskId}`); } task.outputs.push({ path: output, timestamp: new Date().toISOString() }); return this.updateTask(taskId, { outputs: task.outputs }); } /** * List tasks with optional filtering * @param {Object} filter - Filter criteria * @returns {Array} Array of tasks */ listTasks(filter = {}) { if (!existsSync(this.tasksDir)) { return []; } const taskFiles = readdirSync(this.tasksDir).filter(f => f.endsWith('.json')); let tasks = taskFiles.map(file => JSON.parse(readFileSync(join(this.tasksDir, file), 'utf8')) ); // Apply filters if (filter.status) { tasks = tasks.filter(t => t.status === filter.status); } if (filter.assignTo) { tasks = tasks.filter(t => t.assignTo === filter.assignTo); } if (filter.type) { tasks = tasks.filter(t => t.type === filter.type); } if (filter.priority) { tasks = tasks.filter(t => t.priority === filter.priority); } // Sort by priority (descending) then by creation time tasks.sort((a, b) => { if (b.priority !== a.priority) { return b.priority - a.priority; } return new Date(b.createdAt) - new Date(a.createdAt); }); return tasks; } /** * Get next task in queue (highest priority, queued status) * @returns {Object|null} Next task or null if queue is empty */ getNextTask() { const queuedTasks = this.listTasks({ status: 'queued' }); return queuedTasks.length > 0 ? queuedTasks[0] : null; } /** * Delete task * @param {string} taskId - Task identifier * @returns {boolean} True if deleted, false if not found */ deleteTask(taskId) { const taskFile = join(this.tasksDir, `${taskId}.json`); if (existsSync(taskFile)) { unlinkSync(taskFile); console.log(`āœ… Task deleted: ${taskId}`); return true; } return false; } /** * Clear all completed tasks * @returns {number} Number of tasks cleared */ clearCompleted() { const completedTasks = this.listTasks({ status: 'completed' }); let count = 0; for (const task of completedTasks) { if (this.deleteTask(task.id)) { count++; } } console.log(`āœ… Cleared ${count} completed tasks`); return count; } /** * Get task queue summary * @returns {Object} Queue statistics */ getQueueSummary() { const allTasks = this.listTasks(); const summary = { total: allTasks.length, queued: allTasks.filter(t => t.status === 'queued').length, running: allTasks.filter(t => t.status === 'running').length, completed: allTasks.filter(t => t.status === 'completed').length, failed: allTasks.filter(t => t.status === 'failed').length, cancelled: allTasks.filter(t => t.status === 'cancelled').length }; return summary; } /** * Print queue summary */ printQueueSummary() { const summary = this.getQueueSummary(); console.log('\nšŸ“Š MyAIDev Method - Task Queue Summary'); console.log('═'.repeat(40)); console.log(`Total Tasks: ${summary.total}`); console.log(` ā³ Queued: ${summary.queued}`); console.log(` šŸ”„ Running: ${summary.running}`); console.log(` āœ… Completed: ${summary.completed}`); console.log(` āŒ Failed: ${summary.failed}`); console.log(` 🚫 Cancelled: ${summary.cancelled}`); console.log(''); } /** * Export tasks to JSON file * @param {string} outputPath - Output file path * @returns {string} Path to exported file */ exportTasks(outputPath) { const tasks = this.listTasks(); const exportData = { exportedAt: new Date().toISOString(), taskCount: tasks.length, tasks }; writeFileSync(outputPath, JSON.stringify(exportData, null, 2), 'utf8'); console.log(`āœ… Exported ${tasks.length} tasks to: ${outputPath}`); return outputPath; } /** * Import tasks from JSON file * @param {string} inputPath - Input file path * @returns {number} Number of tasks imported */ importTasks(inputPath) { if (!existsSync(inputPath)) { throw new Error(`Import file not found: ${inputPath}`); } const importData = JSON.parse(readFileSync(inputPath, 'utf8')); const tasks = importData.tasks || []; let count = 0; for (const task of tasks) { // Create new task with imported data this.createTask(task.description, { type: task.type, priority: task.priority, assignTo: task.assignTo, metadata: task.metadata }); count++; } console.log(`āœ… Imported ${count} tasks`); return count; } } export default TaskManager;