UNPKG

mcp-adr-analysis-server

Version:

MCP server for analyzing Architectural Decision Records and project architecture

261 lines (256 loc) • 11.5 kB
/** * Migration Utility for JSON-First TODO System * * Helps users transition from markdown-based TODO.md files to the new * JSON-first system with backup and validation. */ import * as fs from 'fs/promises'; import path from 'path'; import { TodoJsonManager } from './todo-json-manager.js'; import { loadConfig } from './config.js'; export class TodoMigrationUtility { todoManager; projectPath; todoMdPath; backupDir; constructor(projectPath) { const config = loadConfig(); this.projectPath = projectPath || config.projectPath; this.todoManager = new TodoJsonManager(this.projectPath); this.todoMdPath = path.join(this.projectPath, 'TODO.md'); this.backupDir = path.join(this.projectPath, '.mcp-adr-cache', 'migration-backups'); } /** * Migrate existing TODO.md to JSON format */ async migrateToJsonFormat(options = {}) { const result = { success: false, migratedTasks: 0, errors: [], warnings: [], summary: '' }; try { // Check if TODO.md exists const todoMdExists = await this.fileExists(this.todoMdPath); if (!todoMdExists) { result.warnings.push('No TODO.md file found - creating fresh JSON structure'); await this.todoManager.loadTodoData(); // Creates default structure result.success = true; result.summary = 'Created new JSON TODO structure (no existing TODO.md found)'; return result; } // Check if JSON already exists const jsonExists = await this.fileExists(path.join(this.projectPath, '.mcp-adr-cache', 'todo-data.json')); if (jsonExists && !options.dryRun) { result.warnings.push('JSON TODO data already exists - this will merge with existing data'); } // Create backup if requested if (options.backupOriginal && !options.dryRun) { await this.ensureBackupDirectory(); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupPath = path.join(this.backupDir, `TODO-${timestamp}.md`); await fs.copyFile(this.todoMdPath, backupPath); result.backupPath = backupPath; } // Read existing TODO.md const todoMdContent = await fs.readFile(this.todoMdPath, 'utf-8'); // Parse and validate markdown const { parseMarkdownToJson } = await import('./todo-markdown-converter.js'); const parsedData = await parseMarkdownToJson(todoMdContent); result.migratedTasks = Object.keys(parsedData.tasks).length; if (options.dryRun) { result.summary = `Dry run: Would migrate ${result.migratedTasks} tasks to JSON format`; result.success = true; return result; } // Save to JSON backend await this.todoManager.saveTodoData(parsedData, false); // Don't sync back to markdown yet // Validate migration if (options.validateAfterMigration) { const validationResult = await this.validateMigration(parsedData); if (!validationResult.valid) { result.errors.push(...validationResult.errors); result.warnings.push(...validationResult.warnings); } } // Generate new markdown from JSON to ensure consistency if (options.preserveMarkdown) { await this.todoManager.convertToMarkdown(); result.warnings.push('TODO.md has been regenerated from JSON data for consistency'); } result.success = result.errors.length === 0; result.summary = `Successfully migrated ${result.migratedTasks} tasks to JSON format`; } catch (error) { result.errors.push(`Migration failed: ${error instanceof Error ? error.message : String(error)}`); result.summary = 'Migration failed - see errors for details'; } return result; } /** * Validate migration results */ async validateMigration(parsedData) { const validation = { valid: true, errors: [], warnings: [] }; try { // Validate JSON structure const { TodoJsonDataSchema } = await import('../types/todo-json-schemas.js'); TodoJsonDataSchema.parse(parsedData); // Check for orphaned dependencies const taskIds = Object.keys(parsedData.tasks); for (const [taskId, task] of Object.entries(parsedData.tasks)) { if (task.dependencies) { for (const depId of task.dependencies) { if (!taskIds.includes(depId)) { validation.warnings.push(`Task ${taskId} has dependency ${depId} which doesn't exist`); } } } } // Check section integrity const sectionTaskIds = parsedData.sections.flatMap((s) => s.tasks); const orphanedTasks = taskIds.filter(id => !sectionTaskIds.includes(id)); if (orphanedTasks.length > 0) { validation.warnings.push(`${orphanedTasks.length} tasks are not assigned to any section`); } } catch (error) { validation.valid = false; validation.errors.push(`Validation failed: ${error instanceof Error ? error.message : String(error)}`); } return validation; } /** * Create a summary report of existing TODO.md */ async generateMigrationReport() { try { const todoMdExists = await this.fileExists(this.todoMdPath); if (!todoMdExists) { return '# TODO Migration Report\n\nāŒ No TODO.md file found in project root.\n\n*Use `manage_todo_json` with `create_task` to start with the new JSON system.*'; } const todoMdContent = await fs.readFile(this.todoMdPath, 'utf-8'); const lines = todoMdContent.split('\n'); // Basic analysis const taskLines = lines.filter(line => line.match(/^- \[(x| )\]/)); const completedTasks = taskLines.filter(line => line.includes('[x]')).length; const pendingTasks = taskLines.filter(line => line.includes('[ ]')).length; const totalTasks = taskLines.length; // Analyze sections const sectionLines = lines.filter(line => line.startsWith('## ')); const sections = sectionLines.map(line => line.replace('## ', '').trim()); // Check for potential issues const issues = []; if (totalTasks === 0) { issues.push('No tasks found in TODO.md'); } if (sections.length === 0) { issues.push('No sections found - tasks may be unorganized'); } // Check for complex formatting that might be lost const hasNestedTasks = lines.some(line => line.match(/^ - \[(x| )\]/)); const hasMetadata = lines.some(line => line.includes('@') || line.includes('šŸ“…')); if (hasNestedTasks) { issues.push('Nested tasks detected - these will be converted to subtasks'); } if (hasMetadata) { issues.push('Task metadata detected (assignees, dates) - these will be preserved'); } let report = '# TODO Migration Report\n\n'; report += `## Current TODO.md Analysis\n\n`; report += `- **Total Tasks**: ${totalTasks}\n`; report += `- **Completed**: ${completedTasks}\n`; report += `- **Pending**: ${pendingTasks}\n`; report += `- **Sections**: ${sections.length}\n\n`; if (sections.length > 0) { report += `### Sections Found:\n`; sections.forEach(section => { report += `- ${section}\n`; }); report += '\n'; } if (issues.length > 0) { report += `### Migration Notes:\n`; issues.forEach(issue => { report += `- ${issue}\n`; }); report += '\n'; } report += `## Migration Options\n\n`; report += `### Recommended Command:\n`; report += '```\n'; report += `manage_todo_json --operation=import_from_markdown --mergeStrategy=merge --backupExisting=true\n`; report += '```\n\n'; report += `### Alternative: Start Fresh\n`; report += 'If you prefer to start with a clean JSON system and manually recreate important tasks:\n'; report += '```\n'; report += `manage_todo_json --operation=create_task --title="Your first task" --priority=high\n`; report += '```\n\n'; report += `*Run this migration when ready to switch to the new JSON-first TODO system.*`; return report; } catch (error) { return `# TODO Migration Report\n\nāŒ Error analyzing TODO.md: ${error instanceof Error ? error.message : String(error)}`; } } /** * Generate a comparison between old and new systems */ async generateFeatureComparison() { return `# TODO System Comparison ## Old System (Markdown-based) - āœ… Human-readable TODO.md file - āŒ Inconsistent LLM updates - āŒ Manual task status tracking - āŒ No automatic scoring integration - āŒ Limited metadata support - āŒ No dependency tracking - āŒ No automation features ## New System (JSON-first) - āœ… Human-readable TODO.md file (auto-generated) - āœ… Consistent LLM interactions with structured JSON - āœ… Automatic task status updates - āœ… Seamless scoring integration - āœ… Rich metadata (assignees, due dates, priorities, tags) - āœ… Full dependency tracking - āœ… Auto-completion rules - āœ… Knowledge graph integration - āœ… Versioning and changelog - āœ… Velocity tracking and analytics - āœ… Backup and recovery ## Migration Benefits 1. **Consistency**: No more broken markdown from LLM updates 2. **Automation**: Tasks automatically move between sections 3. **Intelligence**: Smart scoring and progress tracking 4. **Memory**: Full integration with knowledge graph 5. **Analytics**: Velocity tracking and recommendations 6. **Reliability**: Versioning and backup built-in ## Migration Process 1. **Backup**: Original TODO.md is preserved 2. **Parse**: Existing tasks are analyzed and converted 3. **Validate**: Migration integrity is checked 4. **Sync**: New TODO.md is generated from JSON 5. **Test**: Verify all features work correctly *The new system maintains backward compatibility while adding powerful new features.*`; } async fileExists(filePath) { try { await fs.access(filePath); return true; } catch { return false; } } async ensureBackupDirectory() { try { await fs.mkdir(this.backupDir, { recursive: true }); } catch (error) { // Directory might already exist } } } //# sourceMappingURL=todo-migration.js.map