Skip to main content
Glama
validate-legends.js4.92 kB
#!/usr/bin/env node // Validate legend YAML files for required fields and consistency import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import yaml from 'yaml'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const LEGENDS_DIR = path.join(__dirname, '..', 'legends'); // Required fields for each legend const REQUIRED_FIELDS = ['id', 'name', 'description', 'category']; const RECOMMENDED_FIELDS = ['tags', 'owns', 'principles', 'voice', 'identity']; function validateLegends() { console.log('Validating legend YAML files...\n'); if (!fs.existsSync(LEGENDS_DIR)) { console.error(`Error: Legends directory not found at ${LEGENDS_DIR}`); process.exit(1); } const entries = fs.readdirSync(LEGENDS_DIR, { withFileTypes: true }); const legends = entries.filter(e => e.isDirectory()); let totalValid = 0; let totalWarnings = 0; let totalErrors = 0; const issues = []; for (const legend of legends) { const skillPath = path.join(LEGENDS_DIR, legend.name, 'skill.yaml'); if (!fs.existsSync(skillPath)) { issues.push({ legend: legend.name, type: 'error', message: 'Missing skill.yaml' }); totalErrors++; continue; } try { const content = fs.readFileSync(skillPath, 'utf-8'); let data; try { data = yaml.parse(content); } catch (parseError) { // YAML parse errors are warnings since the runtime loader is more lenient issues.push({ legend: legend.name, type: 'warning', message: `YAML parse warning: ${parseError.message.split('\n')[0]}` }); totalWarnings++; totalValid++; continue; } // Check required fields for (const field of REQUIRED_FIELDS) { if (!data[field]) { issues.push({ legend: legend.name, type: 'error', message: `Missing required field: ${field}` }); totalErrors++; } } // Check recommended fields for (const field of RECOMMENDED_FIELDS) { if (!data[field] || (Array.isArray(data[field]) && data[field].length === 0)) { issues.push({ legend: legend.name, type: 'warning', message: `Missing recommended field: ${field}` }); totalWarnings++; } } // Check tags exist and have values if (!data.tags || data.tags.length === 0) { issues.push({ legend: legend.name, type: 'warning', message: 'No tags defined' }); totalWarnings++; } // Check owns/expertise exists if (!data.owns || data.owns.length === 0) { issues.push({ legend: legend.name, type: 'warning', message: 'No expertise (owns) defined' }); totalWarnings++; } // Check disclaimer exists if (!data.disclaimer) { issues.push({ legend: legend.name, type: 'info', message: 'No custom disclaimer (will use default)' }); } // Check never_say guardrails if (!data.never_say || data.never_say.length === 0) { issues.push({ legend: legend.name, type: 'info', message: 'No never_say guardrails defined' }); } // ID should match directory name if (data.id !== legend.name) { issues.push({ legend: legend.name, type: 'warning', message: `ID mismatch: ${data.id} vs directory ${legend.name}` }); totalWarnings++; } totalValid++; } catch (error) { issues.push({ legend: legend.name, type: 'error', message: `Validation error: ${error.message}` }); totalErrors++; } } // Print summary console.log('='.repeat(60)); console.log(`VALIDATION SUMMARY`); console.log('='.repeat(60)); console.log(`Total legends: ${legends.length}`); console.log(` ✓ Valid: ${totalValid}`); console.log(` ⚠ Warnings: ${totalWarnings}`); console.log(` ✗ Errors: ${totalErrors}`); console.log(''); // Print issues by type const errors = issues.filter(i => i.type === 'error'); const warnings = issues.filter(i => i.type === 'warning'); const infos = issues.filter(i => i.type === 'info'); if (errors.length > 0) { console.log('\n❌ ERRORS:'); errors.forEach(i => console.log(` ${i.legend}: ${i.message}`)); } if (warnings.length > 0) { console.log('\n⚠️ WARNINGS:'); warnings.forEach(i => console.log(` ${i.legend}: ${i.message}`)); } if (process.argv.includes('--verbose') && infos.length > 0) { console.log('\nℹ️ INFO:'); infos.forEach(i => console.log(` ${i.legend}: ${i.message}`)); } console.log(''); // Exit with error if there are errors if (totalErrors > 0) { console.log('❌ Validation failed with errors'); process.exit(1); } else if (totalWarnings > 0) { console.log('⚠️ Validation passed with warnings'); process.exit(0); } else { console.log('✅ All legends validated successfully'); process.exit(0); } } validateLegends();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cryptosquanch/legends-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server