Introduce automated validation for agent YAML files using Zod to ensure
schema compliance across all agent definitions. This feature validates
17 agent files across core and module directories, catching structural
errors and maintaining consistency.
Schema Validation (tools/schema/agent.js):
- Zod-based schema validating metadata, persona, menu, prompts, and critical actions
- Module-aware validation: module field required for src/modules/**/agents/,
optional for src/core/agents/
- Enforces kebab-case unique triggers and at least one command target per menu item
- Validates persona.principles as array (not string)
- Comprehensive refinements for data integrity
CLI Validator (tools/validate-agent-schema.js):
- Scans src/{core,modules/*}/agents/*.agent.yaml
- Parses with js-yaml and validates using Zod schema
- Reports detailed errors with file paths and field paths
- Exits 1 on failures, 0 on success
- Accepts optional project_root parameter for testing
Testing (679 lines across 3 test files):
- test/test-cli-integration.sh: CLI behavior and error handling tests
- test/unit-test-schema.js: Direct schema validation unit tests
- test/test-agent-schema.js: Comprehensive fixture-based tests
- 50 test fixtures covering valid and invalid scenarios
- ESLint configured to support CommonJS test files
- Prettier configured to ignore intentionally broken fixtures
CI Integration (.github/workflows/lint.yaml):
- Renamed from format-check.yaml to lint.yaml
- Added schema-validation job running npm run validate:schemas
- Runs in parallel with prettier and eslint jobs
- Validates on all pull requests
Data Cleanup:
- Fixed src/core/agents/bmad-master.agent.yaml: converted persona.principles
from string to array format
Documentation:
- Updated schema-classification.md with validation section
- Documents validator usage, enforcement rules, and CI integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
132 lines
3.7 KiB
JavaScript
132 lines
3.7 KiB
JavaScript
import js from '@eslint/js';
|
|
import eslintConfigPrettier from 'eslint-config-prettier/flat';
|
|
import nodePlugin from 'eslint-plugin-n';
|
|
import unicorn from 'eslint-plugin-unicorn';
|
|
import yml from 'eslint-plugin-yml';
|
|
|
|
export default [
|
|
// Global ignores for files/folders that should not be linted
|
|
{
|
|
ignores: [
|
|
'dist/**',
|
|
'coverage/**',
|
|
'**/*.min.js',
|
|
'test/template-test-generator/**',
|
|
'test/template-test-generator/**/*.js',
|
|
'test/template-test-generator/**/*.md',
|
|
'test/fixtures/**',
|
|
'test/fixtures/**/*.yaml',
|
|
],
|
|
},
|
|
|
|
// Base JavaScript recommended rules
|
|
js.configs.recommended,
|
|
|
|
// Node.js rules
|
|
...nodePlugin.configs['flat/mixed-esm-and-cjs'],
|
|
|
|
// Unicorn rules (modern best practices)
|
|
unicorn.configs.recommended,
|
|
|
|
// YAML linting
|
|
...yml.configs['flat/recommended'],
|
|
|
|
// Place Prettier last to disable conflicting stylistic rules
|
|
eslintConfigPrettier,
|
|
|
|
// Project-specific tweaks
|
|
{
|
|
rules: {
|
|
// Allow console for CLI tools in this repo
|
|
'no-console': 'off',
|
|
// Enforce .yaml file extension for consistency
|
|
'yml/file-extension': [
|
|
'error',
|
|
{
|
|
extension: 'yaml',
|
|
caseSensitive: true,
|
|
},
|
|
],
|
|
// Prefer double quotes in YAML wherever quoting is used, but allow the other to avoid escapes
|
|
'yml/quotes': [
|
|
'error',
|
|
{
|
|
prefer: 'double',
|
|
avoidEscape: true,
|
|
},
|
|
],
|
|
// Relax some Unicorn rules that are too opinionated for this codebase
|
|
'unicorn/prevent-abbreviations': 'off',
|
|
'unicorn/no-null': 'off',
|
|
},
|
|
},
|
|
|
|
// CLI/CommonJS scripts under tools/** and test/**
|
|
{
|
|
files: ['tools/**/*.js', 'test/**/*.js'],
|
|
rules: {
|
|
// Allow CommonJS patterns for Node CLI scripts
|
|
'unicorn/prefer-module': 'off',
|
|
'unicorn/import-style': 'off',
|
|
'unicorn/no-process-exit': 'off',
|
|
'n/no-process-exit': 'off',
|
|
'unicorn/no-await-expression-member': 'off',
|
|
'unicorn/prefer-top-level-await': 'off',
|
|
// Avoid failing CI on incidental unused vars in internal scripts
|
|
'no-unused-vars': 'off',
|
|
// Reduce style-only churn in internal tools
|
|
'unicorn/prefer-ternary': 'off',
|
|
'unicorn/filename-case': 'off',
|
|
'unicorn/no-array-reduce': 'off',
|
|
'unicorn/no-array-callback-reference': 'off',
|
|
'unicorn/consistent-function-scoping': 'off',
|
|
'n/no-extraneous-require': 'off',
|
|
'n/no-extraneous-import': 'off',
|
|
'n/no-unpublished-require': 'off',
|
|
'n/no-unpublished-import': 'off',
|
|
// Some scripts intentionally use globals provided at runtime
|
|
'no-undef': 'off',
|
|
// Additional relaxed rules for legacy/internal scripts
|
|
'no-useless-catch': 'off',
|
|
'unicorn/prefer-number-properties': 'off',
|
|
'no-unreachable': 'off',
|
|
},
|
|
},
|
|
|
|
// Module installer scripts use CommonJS for compatibility
|
|
{
|
|
files: ['**/_module-installer/**/*.js'],
|
|
rules: {
|
|
// Allow CommonJS patterns for installer scripts
|
|
'unicorn/prefer-module': 'off',
|
|
'n/no-missing-require': 'off',
|
|
'n/no-unpublished-require': 'off',
|
|
},
|
|
},
|
|
|
|
// ESLint config file should not be checked for publish-related Node rules
|
|
{
|
|
files: ['eslint.config.mjs'],
|
|
rules: {
|
|
'n/no-unpublished-import': 'off',
|
|
},
|
|
},
|
|
|
|
// GitHub workflow files in this repo may use empty mapping values
|
|
{
|
|
files: ['.github/workflows/**/*.yaml'],
|
|
rules: {
|
|
'yml/no-empty-mapping-value': 'off',
|
|
},
|
|
},
|
|
|
|
// Other GitHub YAML files may intentionally use empty values and reserved filenames
|
|
{
|
|
files: ['.github/**/*.yaml'],
|
|
rules: {
|
|
'yml/no-empty-mapping-value': 'off',
|
|
'unicorn/filename-case': 'off',
|
|
},
|
|
},
|
|
];
|