UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

320 lines (287 loc) 9.93 kB
#!/usr/bin/env node /** * Task Format V2.0 Validation Script * * Validates task files against the V2.0 specification with 11 compliance rules. * * Usage: * node validate-task-v2.js <task-file> # Validate single task * node validate-task-v2.js --all # Validate all tasks * * Exit codes: * 0 = All tasks compliant * 1 = Some tasks non-compliant * 2 = Error during validation */ const fs = require('fs'); const path = require('path'); // ANSI color codes const colors = { green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', cyan: '\x1b[36m', reset: '\x1b[0m', }; /** * Validation rules for V2.0 compliance */ const validationRules = [ { id: 1, name: 'Execution Modes section', check: (content) => { return content.includes('## Execution Modes') || content.includes('# Execution Modes'); }, message: 'Missing "Execution Modes" section', }, { id: 2, name: 'Task Definition YAML format', check: (content) => { return content.includes('task:') && content.includes('responsável:') && content.includes('responsavel_type:') && content.includes('atomic_layer:'); }, message: 'Task Definition incomplete (missing task, responsável, responsavel_type, or atomic_layer)', }, { id: 3, name: 'Entrada and Saída defined', check: (content) => { return content.includes('**Entrada:**') && content.includes('**Saída:**'); }, message: 'Missing Entrada or Saída sections', }, { id: 4, name: 'Checklist restructured', check: (content) => { return content.includes('pre-conditions:') && content.includes('post-conditions:') && content.includes('acceptance-criteria:'); }, message: 'Checklist not restructured (missing pre-conditions, post-conditions, or acceptance-criteria)', }, { id: 5, name: 'Checklist items have required fields', check: (content) => { const hasType = content.includes('tipo:'); const hasBlocker = content.includes('blocker:'); const hasValidation = content.includes('validação:') || content.includes('validation:'); return hasType && hasBlocker && hasValidation; }, message: 'Checklist items missing required fields (tipo, blocker, validação)', }, { id: 6, name: 'Tools section present', check: (content) => { return content.includes('## Tools') || content.includes('**Tools:**') || content.includes('- N/A') && content.toLowerCase().includes('tool'); }, message: 'Missing Tools section', }, { id: 7, name: 'Scripts section present', check: (content) => { return content.includes('## Scripts') || content.includes('**Scripts:**') || content.includes('- N/A') && content.toLowerCase().includes('script'); }, message: 'Missing Scripts section', }, { id: 8, name: 'Performance section present', check: (content) => { return content.includes('## Performance') || content.includes('**Performance:**') || (content.includes('duration_expected:') && content.includes('cost_estimated:')); }, message: 'Missing Performance section or required metrics (duration_expected, cost_estimated)', }, { id: 9, name: 'Error Handling section present', check: (content) => { return (content.includes('## Error Handling') || content.includes('**Error Handling:**')) && (content.includes('strategy:') || content.includes('Strategy:')); }, message: 'Missing Error Handling section or strategy not defined', }, { id: 10, name: 'Metadata section present', check: (content) => { return (content.includes('## Metadata') || content.includes('**Metadata:**')) && content.includes('story:') && content.includes('version:'); }, message: 'Missing Metadata section or required fields (story, version)', }, { id: 11, name: 'Standardized output template', check: (content) => { // Check for output template markers (less strict as this might be in separate template file) return content.includes('Duration:') || content.includes('Tokens Used:') || content.includes('Metrics') || content.includes('task-execution-report'); }, message: 'Output template markers not found (Duration, Tokens, Metrics)', }, ]; /** * Validate a single task file * @param {string} filePath - Path to the task file * @returns {Object} Validation result */ function validateTask(filePath) { const fileName = path.basename(filePath); const result = { file: fileName, path: filePath, compliant: true, passed: [], failed: [], }; try { // Read file content const content = fs.readFileSync(filePath, 'utf8'); // Run all validation rules for (const rule of validationRules) { const passed = rule.check(content); if (passed) { result.passed.push({ id: rule.id, name: rule.name, }); } else { result.failed.push({ id: rule.id, name: rule.name, message: rule.message, }); result.compliant = false; } } } catch (error) { result.compliant = false; result.error = error.message; } return result; } /** * Validate all tasks in .aios-core/tasks/ * @returns {Object} Summary of validation results */ function validateAllTasks() { const tasksDir = path.join(process.cwd(), '.aios-core', 'tasks'); if (!fs.existsSync(tasksDir)) { console.error(`${colors.red}✗ Tasks directory not found: ${tasksDir}${colors.reset}`); process.exit(2); } const taskFiles = fs.readdirSync(tasksDir) .filter(file => file.endsWith('.md') && !file.includes('.v1-backup.md')) .map(file => path.join(tasksDir, file)); console.log(`${colors.cyan}Validating ${taskFiles.length} tasks...${colors.reset}\n`); const results = taskFiles.map(validateTask); const compliantCount = results.filter(r => r.compliant).length; const nonCompliantCount = results.length - compliantCount; // Group results by phases (approximate) const phase1 = results.slice(0, 15); const phase2 = results.slice(15, 65); const phase3 = results.slice(65); const phase1Compliant = phase1.filter(r => r.compliant).length; const phase2Compliant = phase2.filter(r => r.compliant).length; const phase3Compliant = phase3.filter(r => r.compliant).length; console.log(`${colors.cyan}Phase Results:${colors.reset}`); console.log(`${phase1Compliant === phase1.length ? colors.green + '✅' : colors.red + '✗'} Phase 1: ${phase1Compliant}/${phase1.length} tasks compliant${colors.reset}`); console.log(`${phase2Compliant === phase2.length ? colors.green + '✅' : colors.red + '✗'} Phase 2: ${phase2Compliant}/${phase2.length} tasks compliant${colors.reset}`); console.log(`${phase3Compliant === phase3.length ? colors.green + '✅' : colors.red + '✗'} Phase 3: ${phase3Compliant}/${phase3.length} tasks compliant${colors.reset}`); console.log(); // Show overall result if (compliantCount === results.length) { console.log(`${colors.green}✅ RESULT: ${compliantCount}/${results.length} tasks V2.0 compliant${colors.reset}\n`); } else { console.log(`${colors.red}✗ RESULT: ${compliantCount}/${results.length} tasks compliant, ${nonCompliantCount} non-compliant${colors.reset}\n`); // Show non-compliant tasks console.log(`${colors.yellow}Non-compliant tasks:${colors.reset}`); results.filter(r => !r.compliant).forEach(result => { console.log(` ${colors.red}${colors.reset} ${result.file}`); result.failed.forEach(failure => { console.log(` - Rule ${failure.id}: ${failure.message}`); }); }); } return { total: results.length, compliant: compliantCount, nonCompliant: nonCompliantCount, results, }; } /** * Print validation result for a single task * @param {Object} result - Validation result */ function printResult(result) { if (result.error) { console.log(`${colors.red}✗ ERROR: ${result.file}${colors.reset}`); console.log(` ${result.error}\n`); return; } if (result.compliant) { console.log(`${colors.green}✅ PASS: ${result.file}${colors.reset}`); result.passed.forEach(rule => { console.log(` ${colors.green}${colors.reset} ${rule.name}`); }); } else { console.log(`${colors.red}✗ FAIL: ${result.file}${colors.reset}`); result.failed.forEach(failure => { console.log(` ${colors.red}${colors.reset} Rule ${failure.id}: ${failure.message}`); }); if (result.passed.length > 0) { console.log(` ${colors.green}Passed: ${result.passed.length}/${validationRules.length} rules${colors.reset}`); } } console.log(); } /** * Main execution */ function main() { const args = process.argv.slice(2); if (args.length === 0) { console.log('Usage:'); console.log(' node validate-task-v2.js <task-file> # Validate single task'); console.log(' node validate-task-v2.js --all # Validate all tasks'); process.exit(0); } if (args[0] === '--all') { const summary = validateAllTasks(); process.exit(summary.nonCompliant > 0 ? 1 : 0); } else { const taskFile = args[0]; if (!fs.existsSync(taskFile)) { console.error(`${colors.red}✗ File not found: ${taskFile}${colors.reset}`); process.exit(2); } const result = validateTask(taskFile); printResult(result); process.exit(result.compliant ? 0 : 1); } } // Run if executed directly if (require.main === module) { main(); } module.exports = { validateTask, validateAllTasks, validationRules };