UNPKG

@nicolasmirson/plan-mode-claude

Version:

Structured planning workflow for AI assistants with MCP integration

181 lines 5.62 kB
import { PlanValidator, ValidationError } from './validation.js'; export class PlanManager { constructor() { this.state = { isInPlanMode: false, history: [] }; } /** * Enter plan mode - switches to planning state */ enterPlanMode() { this.state.isInPlanMode = true; } /** * Create a new plan with the given title and description */ createPlan(title, description) { const plan = { id: this.generateId(), title, description, steps: [], created: new Date(), updated: new Date(), status: 'draft' }; try { PlanValidator.validatePlan(plan); } catch (error) { if (error instanceof ValidationError) { throw error; } throw new Error(`Plan validation failed: ${error}`); } this.state.currentPlan = plan; return plan; } /** * Add a step to the current plan */ addStep(content, priority = 'medium', dependencies) { if (!this.state.currentPlan) { throw new Error('No active plan. Create a plan first.'); } const step = { id: this.generateId(), content, status: 'pending', priority, dependencies }; try { PlanValidator.validateStep(step); } catch (error) { if (error instanceof ValidationError) { throw error; } throw new Error(`Step validation failed: ${error}`); } this.state.currentPlan.steps.push(step); this.state.currentPlan.updated = new Date(); // Validate dependencies after adding the step try { PlanValidator.validateDependencies(this.state.currentPlan.steps); } catch (error) { // Remove the step if dependency validation fails this.state.currentPlan.steps.pop(); throw error; } return step; } /** * Convert plan to markdown format */ planToMarkdown(plan) { let markdown = `## ${plan.title}\n\n`; markdown += `${plan.description}\n\n`; const highPrioritySteps = plan.steps.filter(s => s.priority === 'high'); const mediumPrioritySteps = plan.steps.filter(s => s.priority === 'medium'); const lowPrioritySteps = plan.steps.filter(s => s.priority === 'low'); if (highPrioritySteps.length > 0) { markdown += `### High Priority\n`; highPrioritySteps.forEach((step, index) => { markdown += `${index + 1}. ${step.content}\n`; }); markdown += '\n'; } if (mediumPrioritySteps.length > 0) { markdown += `### Medium Priority\n`; mediumPrioritySteps.forEach((step, index) => { markdown += `${index + 1}. ${step.content}\n`; }); markdown += '\n'; } if (lowPrioritySteps.length > 0) { markdown += `### Low Priority\n`; lowPrioritySteps.forEach((step, index) => { markdown += `${index + 1}. ${step.content}\n`; }); markdown += '\n'; } return markdown; } /** * Exit plan mode - present plan for approval */ async exitPlanMode() { if (!this.state.currentPlan) { throw new Error('No active plan to present.'); } if (!this.approvalCallback) { throw new Error('No approval callback set. Use setApprovalCallback() first.'); } const request = { plan: this.state.currentPlan, markdown: this.planToMarkdown(this.state.currentPlan) }; const response = await this.approvalCallback(request); if (response.approved) { this.state.currentPlan.status = 'approved'; this.state.history.push({ ...this.state.currentPlan }); this.state.isInPlanMode = false; } else if (response.modifications) { // Apply modifications Object.assign(this.state.currentPlan, response.modifications); this.state.currentPlan.updated = new Date(); } return response; } /** * Update step status */ updateStepStatus(stepId, status) { if (!this.state.currentPlan) { throw new Error('No active plan.'); } const step = this.state.currentPlan.steps.find(s => s.id === stepId); if (!step) { throw new Error(`Step with id ${stepId} not found.`); } step.status = status; this.state.currentPlan.updated = new Date(); // Check if all steps are completed if (this.state.currentPlan.steps.every(s => s.status === 'completed')) { this.state.currentPlan.status = 'completed'; } } /** * Set the approval callback function */ setApprovalCallback(callback) { this.approvalCallback = callback; } /** * Get current state */ getState() { return { ...this.state }; } /** * Get current plan */ getCurrentPlan() { return this.state.currentPlan; } /** * Check if in plan mode */ isInPlanMode() { return this.state.isInPlanMode; } generateId() { return Math.random().toString(36).substr(2, 9); } } //# sourceMappingURL=PlanManager.js.map