mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-17 17:55:34 +00:00
111 lines
3.3 KiB
JavaScript
111 lines
3.3 KiB
JavaScript
|
|
/**
|
||
|
|
* Agent Schema Validator CLI
|
||
|
|
*
|
||
|
|
* Scans all *.agent.yaml files in src/{core,modules/*}/agents/
|
||
|
|
* and validates them against the Zod schema.
|
||
|
|
*
|
||
|
|
* Usage: node tools/validate-agent-schema.js [project_root]
|
||
|
|
* Exit codes: 0 = success, 1 = validation failures
|
||
|
|
*
|
||
|
|
* Optional argument:
|
||
|
|
* project_root - Directory to scan (defaults to BMAD repo root)
|
||
|
|
*/
|
||
|
|
|
||
|
|
const { glob } = require('glob');
|
||
|
|
const yaml = require('js-yaml');
|
||
|
|
const fs = require('node:fs');
|
||
|
|
const path = require('node:path');
|
||
|
|
const { validateAgentFile } = require('./schema/agent.js');
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Main validation routine
|
||
|
|
* @param {string} [customProjectRoot] - Optional project root to scan (for testing)
|
||
|
|
*/
|
||
|
|
async function main(customProjectRoot) {
|
||
|
|
console.log('🔍 Scanning for agent files...\n');
|
||
|
|
|
||
|
|
// Determine project root: use custom path if provided, otherwise default to repo root
|
||
|
|
const project_root = customProjectRoot || path.join(__dirname, '..');
|
||
|
|
|
||
|
|
// Find all agent files
|
||
|
|
const agentFiles = await glob('src/{core,modules/*}/agents/*.agent.yaml', {
|
||
|
|
cwd: project_root,
|
||
|
|
absolute: true,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (agentFiles.length === 0) {
|
||
|
|
console.log('❌ No agent files found. This likely indicates a configuration error.');
|
||
|
|
console.log(' Expected to find *.agent.yaml files in src/{core,modules/*}/agents/');
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`Found ${agentFiles.length} agent file(s)\n`);
|
||
|
|
|
||
|
|
const errors = [];
|
||
|
|
|
||
|
|
// Validate each file
|
||
|
|
for (const filePath of agentFiles) {
|
||
|
|
const relativePath = path.relative(process.cwd(), filePath);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||
|
|
const agentData = yaml.load(fileContent);
|
||
|
|
|
||
|
|
// Convert absolute path to relative src/ path for module detection
|
||
|
|
const srcRelativePath = relativePath.startsWith('src/') ? relativePath : path.relative(project_root, filePath).replaceAll('\\', '/');
|
||
|
|
|
||
|
|
const result = validateAgentFile(srcRelativePath, agentData);
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
console.log(`✅ ${relativePath}`);
|
||
|
|
} else {
|
||
|
|
errors.push({
|
||
|
|
file: relativePath,
|
||
|
|
issues: result.error.issues,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
errors.push({
|
||
|
|
file: relativePath,
|
||
|
|
issues: [
|
||
|
|
{
|
||
|
|
code: 'parse_error',
|
||
|
|
message: `Failed to parse YAML: ${error.message}`,
|
||
|
|
path: [],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Report errors
|
||
|
|
if (errors.length > 0) {
|
||
|
|
console.log('\n❌ Validation failed for the following files:\n');
|
||
|
|
|
||
|
|
for (const { file, issues } of errors) {
|
||
|
|
console.log(`\n📄 ${file}`);
|
||
|
|
for (const issue of issues) {
|
||
|
|
const pathString = issue.path.length > 0 ? issue.path.join('.') : '(root)';
|
||
|
|
console.log(` Path: ${pathString}`);
|
||
|
|
console.log(` Error: ${issue.message}`);
|
||
|
|
if (issue.code) {
|
||
|
|
console.log(` Code: ${issue.code}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`\n\n💥 ${errors.length} file(s) failed validation`);
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`\n✨ All ${agentFiles.length} agent file(s) passed validation!\n`);
|
||
|
|
process.exit(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Run with optional command-line argument for project root
|
||
|
|
const customProjectRoot = process.argv[2];
|
||
|
|
main(customProjectRoot).catch((error) => {
|
||
|
|
console.error('Fatal error:', error);
|
||
|
|
process.exit(1);
|
||
|
|
});
|