UNPKG

aios-core

Version:

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

191 lines (166 loc) 5.08 kB
/** * Task Definitions Check * * Verifies task definition files are valid. * * @module aios-core/health-check/checks/project/task-definitions * @version 1.0.0 * @story HCS-2 - Health Check System Implementation */ const fs = require('fs').promises; const path = require('path'); const { BaseCheck, CheckSeverity, CheckDomain } = require('../../base-check'); /** * Task definitions validation check * * @class TaskDefinitionsCheck * @extends BaseCheck */ class TaskDefinitionsCheck extends BaseCheck { constructor() { super({ id: 'project.task-definitions', name: 'Task Definitions', description: 'Verifies task definition files are valid', domain: CheckDomain.PROJECT, severity: CheckSeverity.MEDIUM, timeout: 5000, cacheable: true, healingTier: 0, // Cannot auto-fix task definitions tags: ['aios', 'tasks', 'config'], }); } /** * Execute the check * @param {Object} context - Execution context * @returns {Promise<Object>} Check result */ async execute(context) { const projectRoot = context.projectRoot || process.cwd(); const taskPaths = [ path.join(projectRoot, '.aios-core', 'development', 'tasks'), path.join(projectRoot, '.aios-core', 'infrastructure', 'tasks'), ]; const validTasks = []; const invalidTasks = []; let totalTasks = 0; for (const taskPath of taskPaths) { try { const files = await this.findYamlFiles(taskPath); for (const file of files) { totalTasks++; try { const content = await fs.readFile(file, 'utf8'); const validation = this.validateTaskDefinition(content, file); if (validation.valid) { validTasks.push(path.basename(file)); } else { invalidTasks.push({ file: path.basename(file), reasons: validation.errors, }); } } catch (readError) { invalidTasks.push({ file: path.basename(file), reasons: [`Cannot read: ${readError.message}`], }); } } } catch { // Directory doesn't exist, skip } } if (totalTasks === 0) { return this.pass('No task definitions found (framework may not be fully set up)', { details: { searchPaths: taskPaths }, }); } if (invalidTasks.length > 0) { const issues = invalidTasks.map((t) => `${t.file}: ${t.reasons.join(', ')}`); return this.warning(`${invalidTasks.length} task definition(s) have issues`, { recommendation: 'Review and fix invalid task definition files', details: { valid: validTasks.length, invalid: invalidTasks, issues, }, }); } return this.pass(`All ${validTasks.length} task definitions are valid`, { details: { tasks: validTasks, total: totalTasks, }, }); } /** * Find YAML files recursively * @private * @param {string} dir - Directory to search * @returns {Promise<string[]>} Array of file paths */ async findYamlFiles(dir) { const files = []; try { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { const subFiles = await this.findYamlFiles(fullPath); files.push(...subFiles); } else if (entry.name.endsWith('.yaml') || entry.name.endsWith('.yml')) { files.push(fullPath); } } } catch { // Directory doesn't exist } return files; } /** * Validate task definition content * @private * @param {string} content - YAML content * @param {string} filePath - File path for context * @returns {Object} Validation result */ validateTaskDefinition(content, _filePath) { const errors = []; try { // Try to load js-yaml if available let yaml; try { yaml = require('js-yaml'); } catch { // If js-yaml not available, do basic validation if (content.includes('\t')) { errors.push('Contains tabs (use spaces)'); } return { valid: errors.length === 0, errors }; } const parsed = yaml.load(content); if (!parsed) { errors.push('Empty or invalid YAML'); return { valid: false, errors }; } // Check for required task fields if (!parsed.name && !parsed.id) { errors.push('Missing name or id field'); } // Check for v2 task format if (parsed.version === '2.0' || parsed.version === 2) { if (!parsed.steps && !parsed.tasks) { errors.push('V2 task missing steps or tasks'); } } } catch (parseError) { errors.push(`YAML parse error: ${parseError.message}`); } return { valid: errors.length === 0, errors, }; } } module.exports = TaskDefinitionsCheck;