vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
254 lines (253 loc) • 10.7 kB
JavaScript
import { z } from 'zod';
import logger from '../../../logger.js';
import { contextPackageSchema } from '../types/context-curator.js';
export class PackageValidator {
static MIN_QUALITY_SCORE = 0.7;
static MIN_FILES_COUNT = 1;
static MAX_TOKEN_COUNT = 100000;
static MIN_RELEVANCE_SCORE = 0.3;
static async validatePackage(contextPackage) {
const errors = [];
const warnings = [];
logger.info({ packageId: contextPackage.id }, 'Starting package validation');
try {
const schemaCompliance = this.validateSchema(contextPackage, errors);
const contentCompleteness = this.validateContent(contextPackage, errors, warnings);
const metaPromptQuality = this.validateMetaPrompt(contextPackage, errors, warnings);
const fileRelevance = this.validateFileRelevance(contextPackage, warnings);
const tokenEfficiency = this.validateTokenEfficiency(contextPackage, warnings);
const taskDecompositionQuality = this.validateTaskDecomposition(contextPackage, warnings);
const qualityMetrics = {
schemaCompliance,
contentCompleteness,
metaPromptQuality,
fileRelevance,
tokenEfficiency,
taskDecompositionQuality
};
const qualityScore = this.calculateOverallQualityScore(qualityMetrics);
const result = {
isValid: errors.length === 0 && qualityScore >= this.MIN_QUALITY_SCORE,
errors,
warnings,
qualityScore,
qualityMetrics
};
logger.info({
packageId: contextPackage.id,
isValid: result.isValid,
qualityScore: result.qualityScore,
errorsCount: errors.length,
warningsCount: warnings.length
}, 'Package validation completed');
return result;
}
catch (error) {
logger.error({
packageId: contextPackage.id,
error: error instanceof Error ? error.message : 'Unknown error'
}, 'Package validation failed');
return {
isValid: false,
errors: [`Validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`],
warnings: [],
qualityScore: 0,
qualityMetrics: {
schemaCompliance: 0,
contentCompleteness: 0,
metaPromptQuality: 0,
fileRelevance: 0,
tokenEfficiency: 0,
taskDecompositionQuality: 0
}
};
}
}
static validateSchema(contextPackage, errors) {
try {
contextPackageSchema.parse(contextPackage);
return 1.0;
}
catch (error) {
if (error instanceof z.ZodError) {
error.errors.forEach(err => {
errors.push(`Schema validation error: ${err.path.join('.')} - ${err.message}`);
});
const errorRatio = Math.min(error.errors.length / 10, 1);
return Math.max(0, 1 - errorRatio);
}
errors.push('Unknown schema validation error');
return 0;
}
}
static validateContent(contextPackage, errors, warnings) {
let score = 1.0;
if (contextPackage.files.length < this.MIN_FILES_COUNT) {
errors.push(`Insufficient files: ${contextPackage.files.length} < ${this.MIN_FILES_COUNT}`);
score -= 0.3;
}
if (!contextPackage.userPrompt || contextPackage.userPrompt.trim().length === 0) {
errors.push('User prompt is empty');
score -= 0.2;
}
if (!contextPackage.projectPath || contextPackage.projectPath.trim().length === 0) {
errors.push('Project path is empty');
score -= 0.2;
}
if (!contextPackage.taskType) {
errors.push('Task type is missing');
score -= 0.1;
}
if (contextPackage.statistics.totalFiles !== contextPackage.files.length) {
warnings.push('Statistics total files mismatch with actual files count');
score -= 0.1;
}
if (contextPackage.statistics.totalTokens > this.MAX_TOKEN_COUNT) {
warnings.push(`High token count: ${contextPackage.statistics.totalTokens} > ${this.MAX_TOKEN_COUNT}`);
score -= 0.1;
}
return Math.max(0, score);
}
static validateMetaPrompt(contextPackage, errors, warnings) {
let score = 1.0;
if (!contextPackage.metaPrompt) {
errors.push('Meta-prompt is missing');
return 0;
}
const metaPrompt = contextPackage.metaPrompt;
if (!metaPrompt.systemPrompt || metaPrompt.systemPrompt.trim().length < 50) {
errors.push('System prompt is missing or too short');
score -= 0.2;
}
if (!metaPrompt.userPrompt || metaPrompt.userPrompt.trim().length < 20) {
errors.push('Meta-prompt user prompt is missing or too short');
score -= 0.2;
}
if (!metaPrompt.contextSummary || metaPrompt.contextSummary.trim().length < 50) {
warnings.push('Context summary is missing or too short');
score -= 0.1;
}
if (!metaPrompt.guidelines || metaPrompt.guidelines.length === 0) {
warnings.push('No guidelines provided in meta-prompt');
score -= 0.1;
}
if (!metaPrompt.estimatedComplexity) {
warnings.push('Estimated complexity is missing');
score -= 0.1;
}
return Math.max(0, score);
}
static validateFileRelevance(contextPackage, warnings) {
if (contextPackage.files.length === 0) {
return 0;
}
let totalRelevance = 0;
let lowRelevanceCount = 0;
for (const file of contextPackage.files) {
const relevance = file.relevanceScore.score;
totalRelevance += relevance;
if (relevance < this.MIN_RELEVANCE_SCORE) {
lowRelevanceCount++;
}
}
const averageRelevance = totalRelevance / contextPackage.files.length;
if (lowRelevanceCount > contextPackage.files.length * 0.3) {
warnings.push(`High number of low-relevance files: ${lowRelevanceCount}/${contextPackage.files.length}`);
}
if (averageRelevance < 0.5) {
warnings.push(`Low average relevance score: ${averageRelevance.toFixed(2)}`);
}
return Math.min(1.0, averageRelevance * 2);
}
static validateTokenEfficiency(contextPackage, warnings) {
const totalTokens = contextPackage.statistics.totalTokens;
const totalFiles = contextPackage.files.length;
if (totalFiles === 0) {
return 0;
}
const averageTokensPerFile = totalTokens / totalFiles;
const largeFiles = contextPackage.files.filter(file => file.file.tokenCount > 5000);
if (largeFiles.length > 0) {
warnings.push(`${largeFiles.length} files have high token counts (>5000 tokens)`);
}
const smallFiles = contextPackage.files.filter(file => file.file.tokenCount < 50);
if (smallFiles.length > totalFiles * 0.3) {
warnings.push(`${smallFiles.length} files have very low token counts (<50 tokens)`);
}
let efficiencyScore = 1.0;
if (averageTokensPerFile > 3000) {
efficiencyScore -= 0.2;
}
if (averageTokensPerFile < 100) {
efficiencyScore -= 0.2;
}
if (totalTokens > this.MAX_TOKEN_COUNT) {
efficiencyScore -= 0.3;
}
return Math.max(0, efficiencyScore);
}
static validateTaskDecomposition(contextPackage, warnings) {
if (!contextPackage.metaPrompt?.taskDecomposition) {
warnings.push('Task decomposition is missing');
return 0;
}
const decomposition = contextPackage.metaPrompt.taskDecomposition;
let score = 1.0;
if (!decomposition.epics || decomposition.epics.length === 0) {
warnings.push('No epics defined in task decomposition');
return 0;
}
if (decomposition.epics.length < 2) {
warnings.push('Very few epics defined (recommended: 3-10)');
score -= 0.2;
}
let totalTasks = 0;
let totalSubtasks = 0;
for (const epic of decomposition.epics) {
if (!epic.tasks || epic.tasks.length === 0) {
warnings.push(`Epic "${epic.title}" has no tasks`);
score -= 0.1;
continue;
}
totalTasks += epic.tasks.length;
for (const task of epic.tasks) {
if (task.subtasks) {
totalSubtasks += task.subtasks.length;
}
}
}
const averageTasksPerEpic = totalTasks / decomposition.epics.length;
if (averageTasksPerEpic < 2) {
warnings.push('Low average tasks per epic (recommended: 3-7)');
score -= 0.1;
}
if (totalSubtasks === 0) {
warnings.push('No subtasks defined (recommended for detailed planning)');
score -= 0.1;
}
return Math.max(0, score);
}
static calculateOverallQualityScore(metrics) {
const weights = {
schemaCompliance: 0.25,
contentCompleteness: 0.20,
metaPromptQuality: 0.20,
fileRelevance: 0.15,
tokenEfficiency: 0.10,
taskDecompositionQuality: 0.10
};
return (metrics.schemaCompliance * weights.schemaCompliance +
metrics.contentCompleteness * weights.contentCompleteness +
metrics.metaPromptQuality * weights.metaPromptQuality +
metrics.fileRelevance * weights.fileRelevance +
metrics.tokenEfficiency * weights.tokenEfficiency +
metrics.taskDecompositionQuality * weights.taskDecompositionQuality);
}
static getValidationSummary(result) {
const { isValid, qualityScore, errors, warnings } = result;
return `Validation ${isValid ? 'PASSED' : 'FAILED'} - ` +
`Quality: ${(qualityScore * 100).toFixed(1)}% - ` +
`Errors: ${errors.length} - ` +
`Warnings: ${warnings.length}`;
}
}