UNPKG

mirror-magi-meta-agent

Version:

AI-powered development planning and execution system with Supabase integration

531 lines (437 loc) • 18.7 kB
#!/usr/bin/env node const fs = require('fs').promises; const path = require('path'); const readline = require('readline'); /** * Interactive Project Planner * Allows collaborative planning with full user control before finalizing */ class InteractivePlanner { constructor() { this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); this.draftPlan = null; this.config = null; this.projectState = null; } async start() { console.log('šŸŽÆ Interactive Project Planner'); console.log('═'.repeat(50)); console.log('Create and refine your development plan collaboratively\n'); try { // Load configuration await this.loadConfiguration(); // Check for existing plans await this.checkExistingPlans(); // Start interactive planning await this.startPlanningSession(); } catch (error) { console.error('āŒ Error:', error.message); } finally { this.rl.close(); } } async loadConfiguration() { try { this.config = JSON.parse(await fs.readFile('config/project-config.json', 'utf8')); this.projectState = JSON.parse(await fs.readFile('state/project-state.json', 'utf8')); console.log('āœ… Configuration loaded successfully\n'); } catch (error) { throw new Error('Configuration files not found. Run "npm run setup" first.'); } } async checkExistingPlans() { try { const planPath = path.join('state', 'master-plan.json'); const existingPlan = JSON.parse(await fs.readFile(planPath, 'utf8')); console.log('šŸ“‹ Found existing plan:', existingPlan.goal); console.log('Created:', new Date(existingPlan.created).toLocaleString()); const choice = await this.askQuestion('\nWhat would you like to do?\n1. Edit existing plan\n2. Continue existing plan\n3. Create new plan\n4. View plan details\n\nChoose (1-4): '); switch (choice.trim()) { case '1': await this.editExistingPlan(existingPlan); break; case '2': await this.continueExistingPlan(existingPlan); break; case '3': await this.createNewPlan(); break; case '4': await this.viewPlanDetails(existingPlan); await this.checkExistingPlans(); // Return to menu break; default: console.log('Invalid choice. Creating new plan...\n'); await this.createNewPlan(); } } catch (error) { // No existing plan, start fresh await this.createNewPlan(); } } async createNewPlan() { console.log('\nšŸš€ Creating New Development Plan'); console.log('─'.repeat(40)); // Step 1: Get project goal const goal = await this.askQuestion('\nšŸ“ What is your main project goal?\n(Be specific about what you want to build): '); if (!goal.trim()) { console.log('āŒ Goal cannot be empty. Please try again.'); return await this.createNewPlan(); } // Step 2: Gather context const context = await this.gatherProjectContext(); // Step 3: Generate initial plan structure console.log('\nšŸ”„ Generating initial plan structure...'); this.draftPlan = await this.generateInitialPlan(goal, context); // Step 4: Review and refine await this.reviewAndRefinePlan(); } async gatherProjectContext() { console.log('\nšŸ“Š Let\'s gather some context about your project:'); const context = {}; // Required features const featuresInput = await this.askQuestion('\nšŸ”§ What key features do you need? (comma-separated):\nExamples: charts, authentication, real-time updates, file upload\n> '); context.requiredFeatures = featuresInput.split(',').map(f => f.trim()).filter(f => f); // Design/UI approach const designSystem = await this.askQuestion('\nšŸŽØ What design system/approach are you using?\nExamples: Tailwind CSS, Material-UI, Custom CSS, Bootstrap\n> '); context.designSystem = designSystem.trim(); // Data source const dataSource = await this.askQuestion('\nšŸ’¾ What\'s your primary data source?\nExamples: REST API, GraphQL, Database, Firebase, Static files\n> '); context.dataSource = dataSource.trim(); // User requirements const userReqs = await this.askQuestion('\nšŸ‘„ Any specific user requirements? (comma-separated):\nExamples: mobile-friendly, offline support, accessibility\n> '); context.userRequirements = userReqs.split(',').map(r => r.trim()).filter(r => r); // Complexity/priority const priority = await this.askQuestion('\n⭐ Project priority/urgency?\n1. Low (take time, do it right)\n2. Medium (balanced approach)\n3. High (MVP fast, iterate later)\nChoose (1-3): '); context.priority = priority.trim(); return context; } async generateInitialPlan(goal, context) { // Analyze goal type const goalType = this.classifyProjectGoal(goal); console.log(`šŸ“‹ Detected project type: ${goalType}`); // Generate phases based on goal and context const phases = this.generatePhases(goal, goalType, context); const plan = { id: `plan_${Date.now()}`, goal: goal, context: context, goalType: goalType, phases: phases, status: 'draft', created: new Date().toISOString(), estimatedHours: this.calculateEstimate(phases) }; return plan; } async reviewAndRefinePlan() { console.log('\nšŸ“‹ DRAFT PLAN REVIEW'); console.log('═'.repeat(50)); this.displayPlanSummary(); while (true) { const action = await this.askQuestion('\nWhat would you like to do?\n1. Review/edit phases\n2. Add new phase\n3. Remove phase\n4. Reorder phases\n5. Finalize plan\n6. Start over\n\nChoose (1-6): '); switch (action.trim()) { case '1': await this.reviewPhases(); break; case '2': await this.addNewPhase(); break; case '3': await this.removePhase(); break; case '4': await this.reorderPhases(); break; case '5': await this.finalizePlan(); return; case '6': await this.createNewPlan(); return; default: console.log('Invalid choice. Please try again.'); } this.displayPlanSummary(); } } displayPlanSummary() { console.log(`\nšŸŽÆ PROJECT: ${this.draftPlan.goal}`); console.log(`ā±ļø ESTIMATED: ${this.draftPlan.estimatedHours} hours`); console.log(`šŸ“Š PHASES: ${this.draftPlan.phases.length}`); console.log('\nšŸ“‹ PHASE OVERVIEW:'); this.draftPlan.phases.forEach((phase, index) => { const taskCount = phase.tasks ? phase.tasks.length : 'TBD'; console.log(`${index + 1}. ${phase.name} (${taskCount} tasks)`); console.log(` ${phase.description}`); }); } async reviewPhases() { console.log('\nšŸ” REVIEWING PHASES'); console.log('─'.repeat(30)); for (let i = 0; i < this.draftPlan.phases.length; i++) { const phase = this.draftPlan.phases[i]; console.log(`\nšŸ“‹ PHASE ${i + 1}: ${phase.name}`); console.log(`Description: ${phase.description}`); if (phase.tasks) { console.log(`Tasks (${phase.tasks.length}):`); phase.tasks.forEach((task, idx) => { console.log(` ${idx + 1}. ${task.description} (${task.estimatedTime}min)`); }); } const action = await this.askQuestion('\nActions:\n1. Keep as-is\n2. Edit description\n3. Edit tasks\n4. Skip for now\n\nChoose (1-4): '); switch (action.trim()) { case '2': const newDesc = await this.askQuestion('New description: '); if (newDesc.trim()) this.draftPlan.phases[i].description = newDesc.trim(); break; case '3': await this.editPhaseTasks(i); break; case '1': case '4': default: // Continue to next phase break; } } } async editPhaseTasks(phaseIndex) { const phase = this.draftPlan.phases[phaseIndex]; console.log(`\nšŸ”§ EDITING TASKS FOR: ${phase.name}`); if (!phase.tasks) { phase.tasks = []; } while (true) { console.log(`\nCurrent tasks (${phase.tasks.length}):`); phase.tasks.forEach((task, idx) => { console.log(`${idx + 1}. ${task.description} (${task.estimatedTime}min)`); }); const action = await this.askQuestion('\n1. Add task\n2. Edit task\n3. Remove task\n4. Done\n\nChoose (1-4): '); switch (action.trim()) { case '1': await this.addTaskToPhase(phase); break; case '2': const editIdx = await this.askQuestion('Task number to edit: '); const idx = parseInt(editIdx) - 1; if (idx >= 0 && idx < phase.tasks.length) { await this.editTask(phase.tasks[idx]); } break; case '3': const removeIdx = await this.askQuestion('Task number to remove: '); const rIdx = parseInt(removeIdx) - 1; if (rIdx >= 0 && rIdx < phase.tasks.length) { phase.tasks.splice(rIdx, 1); } break; case '4': return; } } } async addTaskToPhase(phase) { const description = await this.askQuestion('Task description: '); const type = await this.askQuestion('Task type (component_creation, api_integration, configuration, etc.): '); const task = { id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`, description: description.trim(), type: type.trim() || 'feature_development', dependencies: [], specifics: {} }; phase.tasks.push(task); console.log('āœ… Task added successfully'); } async finalizePlan() { console.log('\nšŸŽÆ FINALIZING PLAN'); console.log('═'.repeat(30)); this.displayPlanSummary(); // Validate plan structure before finalizing const isValid = await this.validatePlanStructure(); if (!isValid) { console.log('\nāš ļø Plan has structure issues that need to be fixed before finalizing.'); const continueAnyway = await this.askQuestion('Continue anyway? (y/n): '); if (!continueAnyway.toLowerCase().startsWith('y')) { console.log('\nā†©ļø Returning to plan editing...'); await this.reviewAndRefinePlan(); return; } } const confirm = await this.askQuestion('\nāœ… Are you satisfied with this plan? (y/n): '); if (confirm.toLowerCase().startsWith('y')) { // Save the finalized plan this.draftPlan.status = 'finalized'; this.draftPlan.finalizedAt = new Date().toISOString(); await this.savePlan(this.draftPlan); console.log('\nšŸŽ‰ Plan finalized and saved!'); console.log('šŸ“ Saved to: state/master-plan.json'); // Ask if they want to start execution const startNow = await this.askQuestion('\nšŸš€ Start executing the plan now? (y/n): '); if (startNow.toLowerCase().startsWith('y')) { await this.startPlanExecution(); } else { console.log('\nšŸ’” To start execution later, run:'); console.log(' node plan-project.js continue'); console.log(' or: npm run plan:continue'); console.log('\nšŸ’” To view complete plan with all details:'); console.log(' node view-complete-plan.js'); console.log('\nšŸ’” To discuss and refine further:'); console.log(' node discuss-plan.js'); } } else { console.log('\nā†©ļø Returning to plan editing...'); await this.reviewAndRefinePlan(); } } async validatePlanStructure() { const CompletePlanViewer = require('./view-complete-plan'); const viewer = new CompletePlanViewer(); viewer.plan = this.draftPlan; const checks = viewer.validatePlanStructure(); const allPassed = checks.every(check => check.passed); console.log('\nāœ… PLAN STRUCTURE VALIDATION'); console.log('─'.repeat(40)); checks.forEach(check => { const icon = check.passed ? 'āœ…' : 'āŒ'; console.log(`${icon} ${check.description}`); if (!check.passed && check.suggestion) { console.log(` šŸ’” ${check.suggestion}`); } }); console.log(`\n${allPassed ? 'šŸŽ‰' : 'āš ļø'} Plan Structure: ${allPassed ? 'VALID' : 'NEEDS ATTENTION'}`); return allPassed; } async startPlanExecution() { console.log('\nšŸš€ STARTING PLAN EXECUTION'); console.log('═'.repeat(40)); const ProjectPlanner = require('./core/project-planner'); const planner = new ProjectPlanner(this.config, this.projectState); planner.masterPlan = this.draftPlan; const nextCommand = await planner.generateNextCommand(); if (nextCommand.status === 'complete') { console.log('šŸŽ‰ All tasks completed!'); return; } const { task, command, progress } = nextCommand; console.log(`šŸ“Š Progress: ${progress.completed}/${progress.total} tasks (${Math.round(progress.percentage)}%)`); console.log(`\nšŸŽÆ NEXT TASK: ${task.description}`); console.log(`šŸ“‹ Type: ${task.type}`); console.log(`ā±ļø Estimated: ${task.estimatedTime} minutes`); console.log('\nšŸš€ CLAUDE CODE MAX COMMAND:'); console.log('═'.repeat(80)); console.log(command.command); console.log('═'.repeat(80)); console.log('\nšŸ“ EXECUTION WORKFLOW:'); console.log('1. Copy the command above'); console.log('2. Paste it into Claude Code Max'); console.log('3. Let Claude implement the task'); console.log('4. When complete, run: node plan-project.js complete ' + task.id); console.log('5. Then run: node plan-project.js continue'); console.log('\nāœ… VALIDATION STEPS:'); command.validationSteps.forEach((step, index) => { console.log(`${index + 1}. ${step}`); }); } // Helper methods classifyProjectGoal(goal) { const goalLower = goal.toLowerCase(); if (goalLower.includes('dashboard') || goalLower.includes('page')) return 'new_page'; if (goalLower.includes('api') || goalLower.includes('integration')) return 'api_integration'; if (goalLower.includes('refactor') || goalLower.includes('improve')) return 'refactoring'; return 'new_feature'; } generatePhases(goal, goalType, context) { const phases = []; switch (goalType) { case 'new_page': phases.push( { id: 'foundation', name: 'Page Foundation', description: 'Create basic page structure and routing', tasks: [] }, { id: 'data_layer', name: 'Data & State', description: 'Set up data fetching and state management', tasks: [] }, { id: 'ui_components', name: 'UI Components', description: 'Build the main UI components and features', tasks: [] }, { id: 'integration', name: 'Integration & Polish', description: 'Connect everything and add finishing touches', tasks: [] } ); break; case 'api_integration': phases.push( { id: 'api_setup', name: 'API Setup', description: 'Configure API connections and authentication', tasks: [] }, { id: 'data_models', name: 'Data Models', description: 'Create data types and validation', tasks: [] }, { id: 'integration', name: 'Integration', description: 'Connect API to application', tasks: [] }, { id: 'testing', name: 'Testing & Error Handling', description: 'Test integration and handle edge cases', tasks: [] } ); break; default: phases.push( { id: 'planning', name: 'Technical Planning', description: 'Plan the technical approach', tasks: [] }, { id: 'implementation', name: 'Core Implementation', description: 'Build the main functionality', tasks: [] }, { id: 'testing', name: 'Testing & Validation', description: 'Test and validate the implementation', tasks: [] }, { id: 'polish', name: 'Polish & Documentation', description: 'Final touches and documentation', tasks: [] } ); } return phases; } calculateEstimate(phases) { // Simplified estimation without time - just count complexity return phases.length; // Simple count for reference } async savePlan(plan) { const planPath = path.join('state', 'master-plan.json'); await fs.writeFile(planPath, JSON.stringify(plan, null, 2)); } askQuestion(question) { return new Promise((resolve) => { this.rl.question(question, resolve); }); } // Additional methods for editing, continuing plans, etc. async editExistingPlan(existingPlan) { this.draftPlan = { ...existingPlan, status: 'editing' }; console.log('\nāœļø Editing existing plan...'); await this.reviewAndRefinePlan(); } async continueExistingPlan(existingPlan) { console.log('\nā–¶ļø Continuing existing plan...'); const ProjectPlanner = require('./core/project-planner'); const planner = new ProjectPlanner(this.config, this.projectState); planner.masterPlan = existingPlan; const nextCommand = await planner.generateNextCommand(); if (nextCommand.status === 'complete') { console.log('šŸŽ‰ All tasks completed!'); return; } // Show the next command const { task, command } = nextCommand; console.log('\nšŸš€ NEXT CLAUDE CODE MAX COMMAND:'); console.log('═'.repeat(80)); console.log(command.command); console.log('═'.repeat(80)); console.log(`\nšŸ’” After completing: node plan-project.js complete ${task.id}`); } async viewPlanDetails(plan) { console.log('\nšŸ“‹ PLAN DETAILS'); console.log('═'.repeat(30)); console.log(`Goal: ${plan.goal}`); console.log(`Status: ${plan.status}`); console.log(`Created: ${new Date(plan.created).toLocaleString()}`); console.log(`Estimated: ${plan.estimatedHours} hours`); console.log('\nPhases:'); plan.phases.forEach((phase, i) => { console.log(`${i + 1}. ${phase.name}`); console.log(` ${phase.description}`); if (phase.tasks) { console.log(` Tasks: ${phase.tasks.length}`); } }); await this.askQuestion('\nPress Enter to continue...'); } } // Run the interactive planner if (require.main === module) { const planner = new InteractivePlanner(); planner.start().catch(console.error); } module.exports = InteractivePlanner;