( repoRoot: string, )
| 49 | |
| 50 | /** Validates all skills found in the `skills/` directory. */ |
| 51 | export async function validateSkills( |
| 52 | repoRoot: string, |
| 53 | ): Promise<{results: ValidationResult[]; exitCode: number}> { |
| 54 | let errorCount = 0; |
| 55 | const skillFiles = await glob('**/SKILL.md', {cwd: join(repoRoot, 'skills'), absolute: true}); |
| 56 | |
| 57 | if (skillFiles.length === 0) { |
| 58 | Log.info(` ${yellow('⚠')} No skills found in skills/ directory.`); |
| 59 | return {results: [], exitCode: 0}; |
| 60 | } |
| 61 | |
| 62 | Log.info(`Found ${skillFiles.length} skills. Validating...`); |
| 63 | |
| 64 | const validationResults = await Promise.all(skillFiles.map(validateSkill)); |
| 65 | |
| 66 | for (const result of validationResults.sort((a, b) => a.failures.length - b.failures.length)) { |
| 67 | if (result.failures.length > 0) { |
| 68 | Log.info(` ${red('✘')} ${bold(result.name)} (${join('skills', result.name, 'SKILL.md')})`); |
| 69 | result.failures.forEach((failure) => { |
| 70 | Log.info(` - ${failure}`); |
| 71 | errorCount++; |
| 72 | }); |
| 73 | } else { |
| 74 | Log.info(` ${green('✔')} ${bold(result.name)} (${join('skills', result.name, 'SKILL.md')})`); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | Log.info(); |
| 79 | if (errorCount > 0) { |
| 80 | Log.error(` ${red('✘')} Validation failed with ${errorCount} errors.`); |
| 81 | return {results: validationResults, exitCode: 1}; |
| 82 | } else { |
| 83 | Log.info(` ${green('✔')} All skills validated successfully.`); |
| 84 | return {results: validationResults, exitCode: 0}; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | /** Validates a single skill file. */ |
| 89 | export async function validateSkill(filePath: string): Promise<ValidationResult> { |
no test coverage detected