UNPKG

@nicolasmirson/plan-mode-claude

Version:

Structured planning workflow for AI assistants with MCP integration

99 lines 4.03 kB
export class ValidationError extends Error { constructor(message, field) { super(message); this.field = field; this.name = 'ValidationError'; } } export class PlanValidator { static validatePlan(plan) { if (!plan.title || plan.title.trim().length === 0) { throw new ValidationError('Plan title is required', 'title'); } if (plan.title.length > 100) { throw new ValidationError('Plan title must be less than 100 characters', 'title'); } if (!plan.description || plan.description.trim().length === 0) { throw new ValidationError('Plan description is required', 'description'); } if (plan.description.length > 1000) { throw new ValidationError('Plan description must be less than 1000 characters', 'description'); } if (plan.steps) { plan.steps.forEach((step, index) => { this.validateStep(step, index); }); // Check for circular dependencies this.validateDependencies(plan.steps); } } static validateStep(step, index) { const prefix = index !== undefined ? `Step ${index + 1}: ` : ''; if (!step.content || step.content.trim().length === 0) { throw new ValidationError(`${prefix}Step content is required`, 'content'); } if (step.content.length > 500) { throw new ValidationError(`${prefix}Step content must be less than 500 characters`, 'content'); } if (step.priority && !['high', 'medium', 'low'].includes(step.priority)) { throw new ValidationError(`${prefix}Step priority must be 'high', 'medium', or 'low'`, 'priority'); } if (step.status && !['pending', 'in_progress', 'completed'].includes(step.status)) { throw new ValidationError(`${prefix}Step status must be 'pending', 'in_progress', or 'completed'`, 'status'); } } static validateDependencies(steps) { const stepIds = new Set(steps.map(s => s.id)); // Check that all dependency IDs exist for (const step of steps) { if (step.dependencies) { for (const depId of step.dependencies) { if (!stepIds.has(depId)) { throw new ValidationError(`Step "${step.content}" has invalid dependency: ${depId}`, 'dependencies'); } } } } // Check for circular dependencies using DFS const visited = new Set(); const recursionStack = new Set(); const hasCycle = (stepId) => { if (recursionStack.has(stepId)) { return true; } if (visited.has(stepId)) { return false; } visited.add(stepId); recursionStack.add(stepId); const step = steps.find(s => s.id === stepId); if (step?.dependencies) { for (const depId of step.dependencies) { if (hasCycle(depId)) { return true; } } } recursionStack.delete(stepId); return false; }; for (const step of steps) { if (hasCycle(step.id)) { throw new ValidationError('Circular dependency detected in plan steps', 'dependencies'); } } } static validateMarkdown(markdown) { if (!markdown || markdown.trim().length === 0) { throw new ValidationError('Markdown content is required'); } if (markdown.length > 10000) { throw new ValidationError('Markdown content is too long (max 10000 characters)'); } // Basic markdown structure validation if (!markdown.includes('#')) { throw new ValidationError('Markdown should include at least one header'); } } } //# sourceMappingURL=validation.js.map