UNPKG

@andrebuzeli/advanced-memory-markdown-mcp

Version:

Advanced Memory Bank MCP v3.1.5 - Sistema avançado de gerenciamento de memória com isolamento de projetos por IDE, sincronização sob demanda, backup a cada 30min, apenas arquivos .md principais sincronizados, pasta reasoning temporária com limpeza automát

218 lines 9.48 kB
/** * Planning Manager - Manages tasks in a Markdown file. * Inspired by the spec-driven approach of kiro.dev. */ import fs from 'fs/promises'; import { existsSync, mkdirSync } from 'fs'; import path from 'path'; import matter from 'gray-matter'; export class PlanningManager { topicManager; constructor(topicManager) { this.topicManager = topicManager; } getPlanningFilePath(projectName) { const projectPath = this.topicManager.getMemoryRootPath(); return path.join(projectPath, projectName, 'planning.md'); } async loadPlanningFile(projectName) { const filePath = this.getPlanningFilePath(projectName); if (!existsSync(filePath)) { return this.createEmptyPlanningFile(projectName); } const fileContent = await fs.readFile(filePath, 'utf-8'); const { data, content } = matter(fileContent); const objectives = this.parseObjectives(content); const categories = this.parseTasks(content); return { version: data.version || '1.0', status: data.status || 'New', last_updated: data.last_updated || new Date().toISOString(), objectives, categories, }; } async savePlanningFile(projectName, planning) { const filePath = this.getPlanningFilePath(projectName); const projectDir = path.dirname(filePath); if (!existsSync(projectDir)) { mkdirSync(projectDir, { recursive: true }); } planning.last_updated = new Date().toISOString(); const frontMatter = { version: planning.version, status: planning.status, last_updated: planning.last_updated, }; let markdownContent = `## 🎯 Objetivos Principais\n\n`; planning.objectives.forEach(obj => { markdownContent += `- ${obj}\n`; }); markdownContent += `\n## Detalhamento das Tarefas\n\n`; planning.categories.forEach(category => { markdownContent += `### ${category.name}\n\n`; category.tasks.forEach(task => { const statusIcon = task.status === 'Completed' ? '[DONE]' : task.status === 'In Progress' ? '[DOING]' : '[TODO]'; markdownContent += `- **ID: ${task.id}**\n`; markdownContent += ` - **Descrição**: ${task.description}\n`; markdownContent += ` - **Status**: ${task.status} ${statusIcon}\n`; markdownContent += ` - **Prioridade**: ${task.priority}\n`; if (task.assignee) markdownContent += ` - **Responsável**: ${task.assignee}\n`; if (task.deadline) markdownContent += ` - **Prazo**: ${task.deadline}\n`; markdownContent += ` - **Criado**: ${new Date(task.created).toISOString()}\n`; markdownContent += ` - **Modificado**: ${new Date(task.modified).toISOString()}\n`; if (task.subtasks.length > 0) { markdownContent += ` - **Sub-tarefas**:\n`; task.subtasks.forEach(sub => { markdownContent += ` - [${sub.completed ? 'x' : ' '}] ${sub.description}\n`; }); } markdownContent += `\n`; }); }); const fileContent = matter.stringify(markdownContent, frontMatter); await fs.writeFile(filePath, fileContent, 'utf-8'); } createEmptyPlanningFile(projectName) { return { version: '1.0', status: 'New', last_updated: new Date().toISOString(), objectives: [], categories: [], }; } parseObjectives(content) { const objectivesSection = content.match(/## 🎯 Objetivos Principais\n\n([\s\S]*?)\n\n##/); if (!objectivesSection || !objectivesSection[1]) return []; const objectivesContent = objectivesSection[1]; return objectivesContent.split('\n').filter(line => line.startsWith('- ')).map(line => line.substring(2)); } parseTasks(content) { const categories = []; const categorySections = content.split('### ').slice(1); categorySections.forEach(section => { const lines = section.split('\n'); if (lines.length === 0 || !lines[0]) return; const categoryName = lines[0].trim(); const category = { name: categoryName, tasks: [] }; const taskChunks = section.split('- **ID: ').slice(1); taskChunks.forEach(chunk => { const taskLines = chunk.split('\n'); if (taskLines.length === 0 || !taskLines[0]) return; const id = taskLines[0].trim().replace(/\*\*/g, ""); const task = { id, description: '', status: 'Pending', priority: 'Medium', created: 0, modified: 0, subtasks: [] }; taskLines.forEach(line => { const trimmedLine = line.trim(); if (trimmedLine.startsWith('- **Descrição**:')) { task.description = trimmedLine.substring(16).trim(); } else if (trimmedLine.startsWith('- **Status**:')) { const statusMatch = trimmedLine.match(/- \*\*Status\*\*: (Pending|In Progress|Completed|Blocked)/); if (statusMatch) { task.status = statusMatch[1]; } } else if (trimmedLine.startsWith('- **Prioridade**:')) { task.priority = trimmedLine.substring(17).trim(); } else if (trimmedLine.startsWith('- **Responsável**:')) { task.assignee = trimmedLine.substring(18).trim(); } else if (trimmedLine.startsWith('- **Prazo**:')) { task.deadline = trimmedLine.substring(12).trim(); } else if (trimmedLine.startsWith('- **Criado**:')) { task.created = new Date(trimmedLine.substring(13).trim()).getTime(); } else if (trimmedLine.startsWith('- **Modificado**:')) { task.modified = new Date(trimmedLine.substring(17).trim()).getTime(); } else if (trimmedLine.startsWith('- [')) { const subtaskMatch = trimmedLine.match(/- \[(x| )\].*/); if (subtaskMatch && subtaskMatch[2]) { task.subtasks.push({ completed: subtaskMatch[1] === 'x', description: subtaskMatch[2].trim(), }); } } }); category.tasks.push(task); }); categories.push(category); }); return categories; } async addTask(projectName, categoryName, task) { const planning = await this.loadPlanningFile(projectName); let category = planning.categories.find(c => c.name === categoryName); if (!category) { category = { name: categoryName, tasks: [] }; planning.categories.push(category); } const now = Date.now(); const newTaskId = `T-${(planning.categories.flatMap(c => c.tasks).length + 1).toString().padStart(2, '0')}`; const newTask = { ...task, id: newTaskId, created: now, modified: now, subtasks: (task.subtasks || []).map(desc => ({ description: desc, completed: false })), }; category.tasks.push(newTask); await this.savePlanningFile(projectName, planning); return newTask; } async updateTask(projectName, taskId, updates) { const planning = await this.loadPlanningFile(projectName); let task; let category; for (const cat of planning.categories) { task = cat.tasks.find(t => t.id === taskId); if (task) { category = cat; break; } } if (task && category) { Object.assign(task, updates); task.modified = Date.now(); await this.savePlanningFile(projectName, planning); return task; } return null; } async listTasks(projectName, categoryName) { const planning = await this.loadPlanningFile(projectName); if (categoryName) { const category = planning.categories.find(c => c.name === categoryName); return category ? category.tasks : []; } return planning.categories.flatMap(c => c.tasks); } async getTask(projectName, taskId) { const planning = await this.loadPlanningFile(projectName); for (const cat of planning.categories) { const task = cat.tasks.find(t => t.id === taskId); if (task) { return task; } } return null; } } //# sourceMappingURL=planning-manager.js.map