From e4c2f6dd53e9704eadcfb13d6e98964554396aea Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Nov 2025 09:01:39 +0000 Subject: [PATCH] Add frontmatter schema folder structure with validation tools - Create new schemas/frontmatter/ folder for better organization - Add comprehensive validation script (validate.js) using AJV - Add validation tests and example frontmatter files - Add GitHub Actions workflow for CI/CD validation - Add detailed README and migration plan documentation This prepares for moving frontmatter.schema.json to its own subfolder. The schema file itself will be moved in a subsequent commit after review. Includes: - Validation CLI tool with schema and frontmatter validation - Example files for agents, instructions, and prompts - Automated testing framework - CI/CD integration for pull request validation - Comprehensive migration plan documenting all 45+ affected files --- .github/workflows/.gitignore | 3 + .github/workflows/frontmatter-validation.yml | 130 ++++++ schemas/frontmatter/MIGRATION.md | 375 ++++++++++++++++++ schemas/frontmatter/README.md | 243 ++++++++++++ schemas/frontmatter/examples/agent.example.md | 70 ++++ .../examples/instruction.example.md | 98 +++++ .../frontmatter/examples/prompt.example.md | 140 +++++++ schemas/frontmatter/package.json | 32 ++ schemas/frontmatter/tests/schema.test.js | 212 ++++++++++ schemas/frontmatter/validate.js | 276 +++++++++++++ 10 files changed, 1579 insertions(+) create mode 100644 .github/workflows/.gitignore create mode 100644 .github/workflows/frontmatter-validation.yml create mode 100644 schemas/frontmatter/MIGRATION.md create mode 100644 schemas/frontmatter/README.md create mode 100644 schemas/frontmatter/examples/agent.example.md create mode 100644 schemas/frontmatter/examples/instruction.example.md create mode 100644 schemas/frontmatter/examples/prompt.example.md create mode 100644 schemas/frontmatter/package.json create mode 100644 schemas/frontmatter/tests/schema.test.js create mode 100644 schemas/frontmatter/validate.js diff --git a/.github/workflows/.gitignore b/.github/workflows/.gitignore new file mode 100644 index 0000000000..603ec14871 --- /dev/null +++ b/.github/workflows/.gitignore @@ -0,0 +1,3 @@ +# Workflow artifacts and logs +validation-errors.log +*.log diff --git a/.github/workflows/frontmatter-validation.yml b/.github/workflows/frontmatter-validation.yml new file mode 100644 index 0000000000..cbe2a97233 --- /dev/null +++ b/.github/workflows/frontmatter-validation.yml @@ -0,0 +1,130 @@ +--- +name: Frontmatter Validation + +on: + push: + branches: + - main + - develop + - 'claude/**' + paths: + - '**.md' + - 'schemas/frontmatter/**' + - '.github/workflows/frontmatter-validation.yml' + pull_request: + paths: + - '**.md' + - 'schemas/frontmatter/**' + - '.github/workflows/frontmatter-validation.yml' + workflow_dispatch: + +jobs: + validate-schema: + name: Validate Frontmatter Schema + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: schemas/frontmatter/package-lock.json + + - name: Install dependencies + working-directory: schemas/frontmatter + run: npm ci + + - name: Validate schema structure + working-directory: schemas/frontmatter + run: npm run validate:schema + + - name: Run schema tests + working-directory: schemas/frontmatter + run: npm test + + validate-frontmatter: + name: Validate All Frontmatter + runs-on: ubuntu-latest + needs: validate-schema + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: schemas/frontmatter/package-lock.json + + - name: Install dependencies + working-directory: schemas/frontmatter + run: npm ci + + - name: Validate all frontmatter files + working-directory: schemas/frontmatter + run: npm run validate + + - name: Upload validation report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: validation-errors + path: schemas/frontmatter/validation-errors.log + retention-days: 7 + + frontmatter-changed-files: + name: Validate Changed Files Only + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: schemas/frontmatter/package-lock.json + + - name: Install dependencies + working-directory: schemas/frontmatter + run: npm ci + + - name: Get changed markdown files + id: changed-files + uses: tj-actions/changed-files@v44 + with: + files: | + **.md + + - name: Validate changed files + if: steps.changed-files.outputs.any_changed == 'true' + working-directory: schemas/frontmatter + run: | + echo "Validating changed files:" + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + echo " - $file" + node validate.js "../../$file" || exit 1 + done + + - name: Comment on PR + if: failure() && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '⚠️ **Frontmatter validation failed**\n\nPlease check the workflow logs for details and ensure all frontmatter follows the schema at `schemas/frontmatter/frontmatter.schema.json`.\n\nSee [Frontmatter Documentation](https://github.com/lightspeedwp/.github/blob/develop/schemas/frontmatter/README.md) for guidance.' + }) diff --git a/schemas/frontmatter/MIGRATION.md b/schemas/frontmatter/MIGRATION.md new file mode 100644 index 0000000000..a13e36af6c --- /dev/null +++ b/schemas/frontmatter/MIGRATION.md @@ -0,0 +1,375 @@ +--- +title: 'Frontmatter Schema Migration Plan' +description: 'Step-by-step plan for moving frontmatter.schema.json to its own subfolder with validation' +version: 'v1.0' +last_updated: '2025-11-12' +file_type: 'documentation' +tags: ['migration', 'schema', 'refactoring'] +references: + - path: './README.md' + description: 'Frontmatter schema documentation' + - path: '../../docs/DOCUMENTATION_AUDIT.md' + description: 'Documentation audit that recommended this change' +--- + +# Frontmatter Schema Migration Plan + +This document outlines the complete migration plan for moving `schemas/frontmatter.schema.json` to `schemas/frontmatter/frontmatter.schema.json`. + +## Overview + +**Old Location**: `/schemas/frontmatter.schema.json` +**New Location**: `/schemas/frontmatter/frontmatter.schema.json` + +**Why?** +- Better organization for related files (docs, examples, tests) +- More scalable structure for future schema versions +- Dedicated space for validation tools and utilities +- Aligns with best practices for schema management + +## Impact Analysis + +### Files Affected: 45+ + +#### Critical (Code Files) - Must Update First +- `metrics/frontmatter-metrics.js` (line 63) +- `scripts/validation/validate-frontmatter.js` (line 24) +- `scripts/validation/__tests__/validate-frontmatter.test.js` (lines 311, 358) + +#### High Priority ($schema References in Frontmatter) +- `.github/agents/wp-security-review.agent.md` +- `.github/agents/wp-performance-audit.agent.md` +- `.github/agents/wp-accessibility-review.agent.md` +- `schemas/frontmatter/frontmatter.schema.json` (self-references) + +#### Medium Priority (Documentation Links) +- All test README files (5 files) +- `schemas/README.md` +- `scripts/README.md` and `scripts/validation/README.md` +- Instruction files in `.github/instructions/` (7 files) +- Chatmode files (2 files) +- Documentation files in `docs/` (multiple) + +#### Broken Links to Fix +- `.github/instructions/issue-creation.instructions.md` +- `.github/instructions/issues.instructions.md` +- `.github/instructions/pr-creation.instructions.md` +- `docs/README_DOCS_ARCHITECTURE.md` + +These use incorrect path `schema/` instead of `schemas/` - must be fixed! + +## Migration Steps + +### Phase 1: Preparation ✅ COMPLETE + +- [x] Audit all references to frontmatter.schema.json +- [x] Create new folder structure +- [x] Create validation script +- [x] Create tests +- [x] Create documentation +- [x] Create examples +- [x] Create GitHub Actions workflow + +### Phase 2: Schema Setup (Current) + +1. **Move the schema file** + ```bash + # Create backup + cp schemas/frontmatter.schema.json schemas/frontmatter.schema.json.backup + + # Move to new location + mv schemas/frontmatter.schema.json schemas/frontmatter/frontmatter.schema.json + ``` + +2. **Install validation dependencies** + ```bash + cd schemas/frontmatter + npm install + ``` + +3. **Test the validation** + ```bash + # Validate schema itself + npm run validate:schema + + # Run tests + npm test + ``` + +### Phase 3: Update Code Files (Critical) + +**These MUST be updated before deployment to avoid runtime errors.** + +1. **Update `metrics/frontmatter-metrics.js`** + ```diff + - const schemaPath = path.join(process.cwd(), "schemas/frontmatter.schema.json"); + + const schemaPath = path.join(process.cwd(), "schemas/frontmatter/frontmatter.schema.json"); + ``` + +2. **Update `scripts/validation/validate-frontmatter.js`** + ```diff + - schemaPath: path.join(__dirname, '../../schemas/frontmatter.schema.json') + + schemaPath: path.join(__dirname, '../../schemas/frontmatter/frontmatter.schema.json') + ``` + +3. **Update `scripts/validation/__tests__/validate-frontmatter.test.js`** + ```diff + - const schemaPath = path.resolve(__dirname, '../../../schemas/frontmatter.schema.json'); + + const schemaPath = path.resolve(__dirname, '../../../schemas/frontmatter/frontmatter.schema.json'); + ``` + +### Phase 4: Update Frontmatter $schema References + +Update all files that reference the schema in their frontmatter: + +**Pattern to find:** +```bash +grep -r '$schema.*frontmatter.schema.json' .github/ +``` + +**Agent files:** +```diff +- $schema: "../frontmatter.schema.json" ++ $schema: "../schemas/frontmatter/frontmatter.schema.json" +``` + +**Schema self-references:** +```diff +- "path": "schemas/frontmatter.schema.json" ++ "path": "schemas/frontmatter/frontmatter.schema.json" +``` + +### Phase 5: Update Documentation Links + +Update all markdown links: + +**Pattern to find:** +```bash +grep -r 'frontmatter\.schema\.json' --include="*.md" +``` + +**Replace pattern:** +```diff +- [schema](../../schemas/frontmatter.schema.json) ++ [schema](../../schemas/frontmatter/frontmatter.schema.json) +``` + +**Files to update:** +- `schemas/README.md` +- `scripts/README.md` +- `scripts/validation/README.md` +- All test README files +- `docs/CHATMODE-FRONTMATTER.md` +- `.github/instructions/*.instructions.md` + +### Phase 6: Fix Broken Links + +These files incorrectly use `schema/` instead of `schemas/`: + +1. `.github/instructions/issue-creation.instructions.md` +2. `.github/instructions/issues.instructions.md` +3. `.github/instructions/pr-creation.instructions.md` +4. `docs/README_DOCS_ARCHITECTURE.md` + +**Fix:** +```diff +- [frontmatter schema](../../schema/frontmatter.schema.json) ++ [frontmatter schema](../../schemas/frontmatter/frontmatter.schema.json) +``` + +### Phase 7: Update Workflow Files + +Update `.github/workflows/frontmatter-metrics.yml` and any other workflows that reference the schema: + +```diff +- schemas/frontmatter.schema.json ++ schemas/frontmatter/frontmatter.schema.json +``` + +### Phase 8: Testing & Validation + +1. **Run validation script** + ```bash + cd schemas/frontmatter + npm run validate + ``` + +2. **Check for broken references** + ```bash + # Search for old references + grep -r "schemas/frontmatter\.schema\.json" . --exclude-dir=node_modules + + # Should only find in this migration doc and backup file + ``` + +3. **Test code functionality** + ```bash + # Run any dependent scripts + node scripts/validation/validate-frontmatter.js + node metrics/frontmatter-metrics.js + ``` + +4. **Test GitHub Actions** + - Create a test PR with a markdown file + - Verify the frontmatter-validation workflow runs + - Ensure it uses the new path + +### Phase 9: Cleanup & Documentation + +1. **Remove backup** + ```bash + rm schemas/frontmatter.schema.json.backup + ``` + +2. **Update VSCode settings** + + In `.vscode/settings.json`: + ```diff + { + "yaml.schemas": { + - "./schemas/frontmatter.schema.json": [ + + "./schemas/frontmatter/frontmatter.schema.json": [ + ".github/agents/*.md", + ".github/instructions/*.md", + ".github/prompts/*.md", + ".github/chatmodes/*.md", + "docs/*.md" + ] + } + } + ``` + +3. **Update CHANGELOG** + ```markdown + ## [Unreleased] + + ### Changed + - Moved frontmatter schema to dedicated subfolder for better organization + - Schema now at `schemas/frontmatter/frontmatter.schema.json` + - Added validation tools, tests, and examples in schema folder + + ### Added + - Frontmatter validation script with CLI tool + - Automated schema validation via GitHub Actions + - Example frontmatter files for each file type + - Comprehensive schema documentation + ``` + +4. **Announce the change** + - Update relevant documentation index files + - Post in GitHub Discussions if necessary + - Note in next team meeting + +## Automated Migration Script + +For bulk updates, you can use this script: + +```bash +#!/bin/bash +# migrate-schema-references.sh + +OLD_PATH="schemas/frontmatter.schema.json" +NEW_PATH="schemas/frontmatter/frontmatter.schema.json" + +# Update code files (JavaScript) +find . -name "*.js" -type f -not -path "*/node_modules/*" -exec sed -i "s|$OLD_PATH|$NEW_PATH|g" {} + + +# Update markdown files +find . -name "*.md" -type f -not -path "*/node_modules/*" -exec sed -i "s|$OLD_PATH|$NEW_PATH|g" {} + + +# Update YAML files +find . -name "*.yml" -type f -not -path "*/node_modules/*" -exec sed -i "s|$OLD_PATH|$NEW_PATH|g" {} + +find . -name "*.yaml" -type f -not -path "*/node_modules/*" -exec sed -i "s|$OLD_PATH|$NEW_PATH|g" {} + + +# Fix broken links (schema/ -> schemas/frontmatter/) +find . -name "*.md" -type f -not -path "*/node_modules/*" -exec sed -i "s|schema/frontmatter\.schema\.json|schemas/frontmatter/frontmatter.schema.json|g" {} + + +echo "Migration complete! Review changes with: git diff" +``` + +**Usage:** +```bash +chmod +x migrate-schema-references.sh +./migrate-schema-references.sh +git diff # Review changes +``` + +## Rollback Plan + +If issues arise: + +1. **Quick rollback** + ```bash + # Restore from backup + cp schemas/frontmatter.schema.json.backup schemas/frontmatter.schema.json + + # Revert git changes + git checkout HEAD -- . + ``` + +2. **Partial rollback** + - Keep the new structure + - Create symlink for backward compatibility: + ```bash + ln -s frontmatter/frontmatter.schema.json schemas/frontmatter.schema.json + ``` + +## Verification Checklist + +After migration, verify: + +- [ ] Schema file exists at new location +- [ ] Old location removed (or symlinked) +- [ ] All code files updated and working +- [ ] All frontmatter $schema references updated +- [ ] All documentation links updated +- [ ] Broken links fixed +- [ ] VSCode settings updated +- [ ] GitHub Actions workflow passing +- [ ] Validation script works: `npm run validate` +- [ ] Tests pass: `npm test` +- [ ] No grep results for old path (except backups/docs) +- [ ] CHANGELOG updated +- [ ] No broken references in production + +## Success Criteria + +Migration is considered successful when: + +1. ✅ All frontmatter validates without errors +2. ✅ CI/CD pipeline passes +3. ✅ No broken links in documentation +4. ✅ All code files use new path +5. ✅ VSCode intellisense works with new path +6. ✅ Examples and tests run successfully +7. ✅ No complaints from team members about broken tools + +## Timeline + +**Estimated Duration**: 2-3 hours + +- Phase 1 (Preparation): ✅ Complete +- Phase 2 (Setup): 15 minutes +- Phase 3 (Code): 15 minutes +- Phase 4 (Frontmatter): 30 minutes +- Phase 5 (Documentation): 45 minutes +- Phase 6 (Broken Links): 15 minutes +- Phase 7 (Workflows): 10 minutes +- Phase 8 (Testing): 30 minutes +- Phase 9 (Cleanup): 15 minutes + +## Support + +Questions or issues? + +1. Check [README.md](./README.md) for schema documentation +2. Review [validation examples](./examples/) +3. Test with `npm run validate` +4. Open an issue in GitHub + +--- + +**Status**: Ready for execution +**Approved by**: Pending +**Executed by**: Pending +**Completion date**: Pending diff --git a/schemas/frontmatter/README.md b/schemas/frontmatter/README.md new file mode 100644 index 0000000000..c6f2634215 --- /dev/null +++ b/schemas/frontmatter/README.md @@ -0,0 +1,243 @@ +--- +title: 'Frontmatter Schema Documentation' +description: 'Unified frontmatter schema for LightSpeed .github files, validation tools, and usage guidelines' +version: 'v1.0' +last_updated: '2025-11-12' +file_type: 'documentation' +tags: ['schema', 'frontmatter', 'validation', 'yaml'] +references: + - path: '../../docs/CHATMODE-FRONTMATTER.md' + description: 'Frontmatter conventions guide' + - path: '../../.github/instructions/frontmatter.instructions.md' + description: 'Frontmatter instructions for AI agents' + - path: '../../.github/instructions/tagging-and-frontmatter-conventions.instructions.md' + description: 'Tagging conventions' +--- + +# Frontmatter Schema + +This folder contains the unified frontmatter schema used across all LightSpeed `.github` configuration files, along with validation tools, examples, and documentation. + +## Overview + +The `frontmatter.schema.json` file is a JSON Schema (Draft 07) that validates YAML frontmatter in: + +- Agent specifications (`.github/agents/*.agent.md`) +- Instructions (`.github/instructions/*.instructions.md`) +- Prompts (`.github/prompts/*.prompt.md`) +- Chatmodes (`.github/chatmodes/*.chatmode.md`) +- Documentation (`docs/*.md`) +- GitHub templates (issue, PR, discussion templates) +- Root configuration files (`CLAUDE.md`, `GEMINI.md`, `AGENTS.md`) + +## Files + +``` +frontmatter/ +├── frontmatter.schema.json # The main JSON Schema +├── validate.js # Validation CLI tool +├── package.json # Dependencies for validation +├── README.md # This file +├── examples/ # Example frontmatter +│ ├── agent.example.md +│ ├── instruction.example.md +│ └── prompt.example.md +└── tests/ # Validation tests + └── schema.test.js +``` + +## Quick Start + +### Installation + +```bash +cd schemas/frontmatter +npm install +``` + +### Validate All Files + +```bash +npm run validate +``` + +### Validate Specific File + +```bash +node validate.js path/to/file.md +``` + +### Validate Schema Only + +```bash +npm run validate:schema +``` + +## Schema Structure + +The schema uses JSON Schema's `oneOf` discriminator pattern with `file_type` as the discriminator property. Each file type has specific requirements: + +### Common Fields + +Available across all file types (via `$ref: "#/definitions/commonFields"`): + +| Field | Type | Description | +|-------|------|-------------| +| `title` | string | Human-readable title | +| `description` | string | Brief description of purpose (required for most types) | +| `version` | string | Version string (e.g., v1.1) | +| `created_date` | date | ISO date when file was created | +| `last_updated` | date | ISO date of last update | +| `author` | string | Main author or responsible party | +| `maintainer` | string | Current maintainer or team | +| `owners` | array | List of owners/maintainers | +| `tags` | array | Keywords for discovery (max 8) | +| `status` | enum | `active`, `deprecated`, `draft`, `experimental` | +| `stability` | enum | `stable`, `experimental`, `incubating` | +| `deprecated` | boolean | Whether this file is deprecated | +| `replacement` | string | Path to replacement file if deprecated | +| `domain` | enum | Primary classification domain | +| `references` | array | AI-focused references to related files | + +### File Types + +Each `file_type` has specific required and optional fields: + +- **`agent`**: Agent specifications (`.github/agents/*.agent.md`) +- **`instructions`**: Instructions files (`.github/instructions/*.instructions.md`) +- **`prompt`**: Prompt specifications (`.github/prompts/*.prompt.md`) +- **`chatmode`**: Chatmode configurations (`.github/chatmodes/*.chatmode.md`) +- **`documentation`**: General docs (`docs/*.md`) +- **`issue-template`**: GitHub issue templates +- **`pr-template`**: GitHub PR templates +- And more... + +See the schema file for complete definitions. + +## Usage in Files + +### Reference the Schema + +Add a `$schema` property in your YAML frontmatter: + +```yaml +--- +$schema: "schemas/frontmatter/frontmatter.schema.json" +file_type: "agent" +name: "example-agent" +description: "An example agent specification" +--- +``` + +### VSCode Integration + +VSCode will automatically validate YAML frontmatter if you have the YAML extension installed and the schema properly referenced. + +Add to `.vscode/settings.json`: + +```json +{ + "yaml.schemas": { + "./schemas/frontmatter/frontmatter.schema.json": [ + ".github/agents/*.md", + ".github/instructions/*.md", + ".github/prompts/*.md", + ".github/chatmodes/*.md", + "docs/*.md" + ] + } +} +``` + +## Validation + +### Manual Validation + +```bash +# Validate all files +npm run validate + +# Validate specific file +node validate.js .github/agents/example.agent.md + +# Only check if schema is valid +npm run validate:schema +``` + +### CI/CD Validation + +The validation script is integrated into GitHub Actions. See `.github/workflows/frontmatter-validation.yml`. + +### Pre-commit Hook + +To validate frontmatter before committing: + +```bash +# .git/hooks/pre-commit +#!/bin/bash +cd schemas/frontmatter +npm run validate +``` + +## Examples + +See the `examples/` directory for complete examples of each file type: + +- `agent.example.md` - Agent specification +- `instruction.example.md` - Instructions file +- `prompt.example.md` - Prompt specification + +## Testing + +Run the test suite: + +```bash +npm test +``` + +## Updating the Schema + +When updating `frontmatter.schema.json`: + +1. **Edit the schema** - Make your changes following JSON Schema Draft 07 spec +2. **Validate the schema** - Run `npm run validate:schema` +3. **Update examples** - Ensure examples in `examples/` reflect changes +4. **Update documentation** - Update this README and related docs +5. **Run full validation** - Run `npm run validate` to check all files +6. **Update version** - Increment version in schema's `title` or add a `version` field +7. **Commit changes** - Include rationale in commit message +8. **Update references** - If path changed, update all referencing files + +## Migration + +This schema was moved from `schemas/frontmatter.schema.json` to `schemas/frontmatter/frontmatter.schema.json` in November 2025 for better organization. + +If you encounter broken references, update them to the new path: + +```diff +- $schema: "schemas/frontmatter.schema.json" ++ $schema: "schemas/frontmatter/frontmatter.schema.json" +``` + +## Resources + +- [JSON Schema Specification](https://json-schema.org/specification.html) +- [AJV Documentation](https://ajv.js.org/) +- [YAML Specification](https://yaml.org/spec/) +- [LightSpeed Frontmatter Conventions](../../docs/CHATMODE-FRONTMATTER.md) +- [Frontmatter Instructions](../../.github/instructions/frontmatter.instructions.md) + +## Support + +For questions or issues: + +1. Check [GitHub Discussions](https://github.com/orgs/lightspeedwp/discussions) +2. Review [CHATMODE-FRONTMATTER.md](../../docs/CHATMODE-FRONTMATTER.md) +3. Reference [frontmatter.instructions.md](../../.github/instructions/frontmatter.instructions.md) +4. Open an issue following the [issue template](../../.github/ISSUE_TEMPLATE/) + +--- + +**Maintainer**: LightSpeed Team +**Last Updated**: 2025-11-12 +**Version**: 1.0 diff --git a/schemas/frontmatter/examples/agent.example.md b/schemas/frontmatter/examples/agent.example.md new file mode 100644 index 0000000000..242a6e99a6 --- /dev/null +++ b/schemas/frontmatter/examples/agent.example.md @@ -0,0 +1,70 @@ +--- +$schema: "../frontmatter.schema.json" +file_type: "agent" +name: "example-security-agent" +description: "Example agent that performs WordPress security audits and recommends fixes" +version: "v1.0.0" +last_updated: "2025-11-12" +owners: ["lightspeedwp/security-team"] +status: "active" +category: "security" +domain: "security" +stability: "stable" +tags: ["security", "audit", "wordpress", "owasp"] +labels: ["security", "wordpress", "automated"] +references: + - path: "../../docs/SECURITY.md" + description: "Security guidelines and policies" + - path: "../../.github/instructions/coding-standards.instructions.md" + description: "Coding standards including security requirements" + - path: "../frontmatter.schema.json" + description: "Frontmatter schema definition" +--- + +# Example Security Agent + +This is an example agent specification showing proper frontmatter structure. + +## Purpose + +This agent performs comprehensive security audits on WordPress codebases, checking for: + +- SQL injection vulnerabilities +- XSS vulnerabilities +- CSRF protection +- Input validation and sanitization +- Output escaping +- Authentication and authorization +- Nonce verification +- Capability checks + +## Usage + +```bash +# Trigger via GitHub Copilot +@agent example-security-agent audit this file for security issues +``` + +## Expected Behavior + +1. Scans the specified files or codebase +2. Identifies potential security vulnerabilities +3. Provides specific recommendations for fixes +4. References WordPress Coding Standards and OWASP guidelines +5. Generates a security report + +## Output Format + +The agent produces a markdown report with: + +- Executive summary +- Detailed findings by severity (Critical, High, Medium, Low) +- Code snippets showing vulnerabilities +- Recommended fixes with code examples +- References to security best practices + +## Related + +- [WordPress Security Guidelines](https://developer.wordpress.org/apis/security/) +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) +- [Security Instructions](../../.github/instructions/security.instructions.md) diff --git a/schemas/frontmatter/examples/instruction.example.md b/schemas/frontmatter/examples/instruction.example.md new file mode 100644 index 0000000000..40c5d4d7b3 --- /dev/null +++ b/schemas/frontmatter/examples/instruction.example.md @@ -0,0 +1,98 @@ +--- +$schema: "../frontmatter.schema.json" +file_type: "instructions" +title: "WordPress REST API Security Instructions" +description: "Security guidelines for WordPress REST API development including authentication, authorization, input validation, and output sanitization" +version: "v1.0.0" +last_updated: "2025-11-12" +author: "LightSpeed Security Team" +maintainer: "Ash Shaw" +applyTo: + - "includes/api/**/*.php" + - "includes/rest/**/*.php" +mode: "agent" +domain: "security" +stability: "stable" +tags: ["security", "rest-api", "validation", "wordpress"] +references: + - path: "../../docs/SECURITY.md" + description: "Main security documentation" + - path: "./coding-standards.instructions.md" + description: "WordPress coding standards" + - path: "../frontmatter.schema.json" + description: "Frontmatter schema definition" +--- + +# WordPress REST API Security Instructions + +This is an example instructions file showing proper frontmatter structure for applying security guidelines to specific file patterns. + +## Purpose + +Ensure all WordPress REST API endpoints follow security best practices. + +## Required Security Measures + +### 1. Authentication + +All custom REST API endpoints MUST implement proper authentication: + +```php +register_rest_route('myplugin/v1', '/secure-endpoint', [ + 'methods' => 'POST', + 'callback' => 'my_secure_callback', + 'permission_callback' => 'my_permission_check', // Required! +]); +``` + +### 2. Authorization + +Check user capabilities before processing: + +```php +function my_permission_check() { + return current_user_can('edit_posts'); +} +``` + +### 3. Input Validation + +Validate all input parameters: + +```php +'args' => [ + 'id' => [ + 'required' => true, + 'validate_callback' => function($param) { + return is_numeric($param); + }, + 'sanitize_callback' => 'absint', + ], +], +``` + +### 4. Nonce Verification + +For sensitive operations, verify nonces: + +```php +if (!wp_verify_nonce($_REQUEST['_wpnonce'], 'my_action')) { + return new WP_Error('invalid_nonce', 'Security check failed', ['status' => 403]); +} +``` + +## Checklist + +- [ ] Permission callback defined (not `__return_true`) +- [ ] User capabilities checked +- [ ] Input validated and sanitized +- [ ] Output escaped if returning HTML +- [ ] Nonces verified for state-changing operations +- [ ] Rate limiting considered for public endpoints +- [ ] Error messages don't leak sensitive information + +## References + +- [REST API Handbook](https://developer.wordpress.org/rest-api/) +- [WordPress Security](https://developer.wordpress.org/apis/security/) +- [OWASP API Security](https://owasp.org/www-project-api-security/) diff --git a/schemas/frontmatter/examples/prompt.example.md b/schemas/frontmatter/examples/prompt.example.md new file mode 100644 index 0000000000..ebb6c258db --- /dev/null +++ b/schemas/frontmatter/examples/prompt.example.md @@ -0,0 +1,140 @@ +--- +$schema: "../frontmatter.schema.json" +file_type: "prompt" +title: "Generate WordPress Block Pattern" +description: "Prompt for generating WordPress block patterns from design specifications or descriptions" +version: "v1.0.0" +last_updated: "2025-11-12" +author: "LightSpeed Team" +mode: "edit" +model: "claude-sonnet-4.0" +domain: "wp-core" +stability: "stable" +tags: ["blocks", "patterns", "wordpress", "generation"] +tools: ["edit", "write", "read"] +references: + - path: "../../docs/BLOCK-PATTERNS.md" + description: "Block patterns documentation" + - path: "../../.github/instructions/pattern-development.instructions.md" + description: "Pattern development guidelines" + - path: "../frontmatter.schema.json" + description: "Frontmatter schema definition" +--- + +# Generate WordPress Block Pattern + +This is an example prompt specification showing proper frontmatter structure. + +## Prompt + +You are a WordPress block pattern generator. Given a design specification or description, create a WordPress block pattern following WordPress coding standards and best practices. + +## Requirements + +1. **Pattern Structure**: Follow WordPress block pattern registration format +2. **Accessibility**: Include proper ARIA labels and semantic HTML +3. **Responsiveness**: Use WordPress responsive utilities +4. **Internationalization**: Wrap user-facing strings in `__()` or `_e()` +5. **Naming**: Use kebab-case for pattern slugs +6. **Categories**: Assign to appropriate pattern categories + +## Expected Input + +User provides one of: + +- Design mockup or screenshot +- Text description of desired layout +- Example website or pattern to recreate +- Specific blocks and arrangement + +## Expected Output + +Generate: + +1. Pattern registration code (PHP) +2. Pattern metadata (title, description, categories, keywords) +3. Block markup with proper structure +4. Inline documentation + +## Example + +**User Input:** +> Create a hero section with a heading, paragraph, and button in two columns + +**Assistant Output:** + +```php + + +
+ +
+ +
+ +

+ + + +

+ + + +
+ +
+ + + +
+ +
+ +
+ + + +
+ +
+ <?php esc_attr_e('Hero image', 'mytheme'); ?> +
+ +
+ +
+ +
+ +``` + +## Validation + +After generation, verify: + +- [ ] Valid WordPress block markup +- [ ] Proper escaping and internationalization +- [ ] Accessibility attributes present +- [ ] Pattern metadata complete +- [ ] Responsive design considerations + +## Usage + +```bash +# Via GitHub Copilot +Create a block pattern for a testimonial section with three columns +``` + +## Related + +- [Block Pattern Directory](https://wordpress.org/patterns/) +- [Pattern Registration](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-patterns/) +- [Block Markup Reference](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/) diff --git a/schemas/frontmatter/package.json b/schemas/frontmatter/package.json new file mode 100644 index 0000000000..d6e97b6aa5 --- /dev/null +++ b/schemas/frontmatter/package.json @@ -0,0 +1,32 @@ +{ + "name": "@lightspeedwp/frontmatter-validator", + "version": "1.0.0", + "description": "Validation tools for LightSpeed frontmatter schema", + "main": "validate.js", + "scripts": { + "validate": "node validate.js", + "validate:schema": "node validate.js --schema-only", + "test": "node tests/schema.test.js" + }, + "keywords": [ + "frontmatter", + "yaml", + "validation", + "schema", + "lightspeed" + ], + "author": "LightSpeed", + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", + "yaml": "^2.3.4", + "glob": "^10.3.10" + }, + "devDependencies": { + "jest": "^29.7.0" + }, + "engines": { + "node": ">=16.0.0" + } +} diff --git a/schemas/frontmatter/tests/schema.test.js b/schemas/frontmatter/tests/schema.test.js new file mode 100644 index 0000000000..c7513d368b --- /dev/null +++ b/schemas/frontmatter/tests/schema.test.js @@ -0,0 +1,212 @@ +#!/usr/bin/env node +/** + * Frontmatter Schema Tests + * + * Unit tests for the frontmatter schema validator + */ + +const { validateSchemaFile, extractFrontmatter, validateFile } = require('../validate'); +const fs = require('fs'); +const path = require('path'); + +// Test utilities +const colors = { + reset: '\x1b[0m', + red: '\x1b[31m', + green: '\x1b[32m', + blue: '\x1b[34m' +}; + +let testsPassed = 0; +let testsFailed = 0; + +function assert(condition, message) { + if (condition) { + console.log(`${colors.green}✓${colors.reset} ${message}`); + testsPassed++; + } else { + console.log(`${colors.red}✗${colors.reset} ${message}`); + testsFailed++; + } +} + +function describe(description, tests) { + console.log(`\n${colors.blue}${description}${colors.reset}`); + tests(); +} + +// Tests +describe('Schema Validation', () => { + const result = validateSchemaFile(); + assert(result.valid, 'Schema file should be valid JSON Schema Draft 07'); + assert(result.schema !== undefined, 'Schema should be loaded'); + assert(result.schema.$schema === 'http://json-schema.org/draft-07/schema#', 'Schema should declare Draft 07'); +}); + +describe('Frontmatter Extraction', () => { + // Create temporary test file + const testFile = path.join(__dirname, 'test-temp.md'); + const validFrontmatter = `--- +file_type: "agent" +name: "test-agent" +description: "Test agent description" +--- + +# Test Content +`; + + const noFrontmatter = `# Just a heading + +No frontmatter here. +`; + + const invalidYaml = `--- +file_type: "agent" +name: this is not quoted properly: and has colons +--- + +# Content +`; + + // Test valid frontmatter + fs.writeFileSync(testFile, validFrontmatter); + let fm = extractFrontmatter(testFile); + assert(fm !== null, 'Should extract valid frontmatter'); + assert(fm.file_type === 'agent', 'Should parse file_type correctly'); + assert(fm.name === 'test-agent', 'Should parse name correctly'); + + // Test no frontmatter + fs.writeFileSync(testFile, noFrontmatter); + fm = extractFrontmatter(testFile); + assert(fm === null, 'Should return null when no frontmatter present'); + + // Test invalid YAML + fs.writeFileSync(testFile, invalidYaml); + try { + fm = extractFrontmatter(testFile); + assert(false, 'Should throw error for invalid YAML'); + } catch (error) { + assert(true, 'Should throw error for invalid YAML'); + } + + // Cleanup + fs.unlinkSync(testFile); +}); + +describe('File Validation', () => { + const { schema } = validateSchemaFile(); + const testFile = path.join(__dirname, 'test-validation.md'); + + // Valid agent frontmatter + const validAgent = `--- +file_type: "agent" +name: "test-agent" +description: "Test agent for validation" +version: "v1.0" +owners: ["lightspeedwp/team"] +status: "active" +--- + +# Test Agent +`; + + fs.writeFileSync(testFile, validAgent); + let result = validateFile(testFile, schema); + assert(result.status === 'valid', 'Valid agent frontmatter should pass validation'); + + // Missing required field + const missingRequired = `--- +file_type: "agent" +name: "test-agent" +--- + +# Missing description +`; + + fs.writeFileSync(testFile, missingRequired); + result = validateFile(testFile, schema); + assert(result.status === 'invalid', 'Missing required field should fail validation'); + + // Invalid file_type + const invalidType = `--- +file_type: "not-a-real-type" +name: "test" +description: "Test" +--- + +# Invalid type +`; + + fs.writeFileSync(testFile, invalidType); + result = validateFile(testFile, schema); + assert(result.status === 'invalid', 'Invalid file_type should fail validation'); + + // Valid instruction frontmatter with applyTo + const validInstruction = `--- +file_type: "instructions" +description: "Test instructions" +applyTo: "**/*.php" +domain: "security" +stability: "stable" +--- + +# Test Instructions +`; + + fs.writeFileSync(testFile, validInstruction); + result = validateFile(testFile, schema); + assert(result.status === 'valid', 'Valid instruction frontmatter should pass validation'); + + // Cleanup + fs.unlinkSync(testFile); +}); + +describe('Schema Structure', () => { + const { schema } = validateSchemaFile(); + + assert(schema.definitions !== undefined, 'Schema should have definitions'); + assert(schema.definitions.commonFields !== undefined, 'Schema should define commonFields'); + assert(schema.oneOf !== undefined, 'Schema should use oneOf discriminator'); + assert(Array.isArray(schema.oneOf), 'oneOf should be an array'); + assert(schema.oneOf.length > 0, 'oneOf should have file type definitions'); + + // Check that each oneOf item has required properties + schema.oneOf.forEach((fileType, index) => { + assert(fileType.properties !== undefined, `oneOf[${index}] should have properties`); + assert(fileType.properties.file_type !== undefined, `oneOf[${index}] should define file_type`); + }); +}); + +describe('Common Fields Definition', () => { + const { schema } = validateSchemaFile(); + const commonFields = schema.definitions.commonFields.properties; + + assert(commonFields.title !== undefined, 'commonFields should include title'); + assert(commonFields.description !== undefined, 'commonFields should include description'); + assert(commonFields.tags !== undefined, 'commonFields should include tags'); + assert(commonFields.tags.maxItems === 8, 'tags should have maxItems of 8'); + assert(commonFields.status !== undefined, 'commonFields should include status'); + assert(commonFields.domain !== undefined, 'commonFields should include domain'); + assert(commonFields.references !== undefined, 'commonFields should include references'); +}); + +describe('References Format', () => { + const { schema } = validateSchemaFile(); + const references = schema.definitions.commonFields.properties.references; + + assert(references.type === 'array', 'references should be an array'); + assert(references.items.type === 'object', 'reference items should be objects'); + assert(references.items.properties.path !== undefined, 'reference items should have path'); + assert(references.items.properties.description !== undefined, 'reference items should have description'); + assert(Array.isArray(references.items.required), 'reference items should have required fields'); + assert(references.items.required.includes('path'), 'path should be required in references'); + assert(references.items.required.includes('description'), 'description should be required in references'); +}); + +// Summary +console.log(`\n${'═'.repeat(40)}`); +console.log(`Tests passed: ${colors.green}${testsPassed}${colors.reset}`); +console.log(`Tests failed: ${colors.red}${testsFailed}${colors.reset}`); +console.log(`${'═'.repeat(40)}\n`); + +process.exit(testsFailed > 0 ? 1 : 0); diff --git a/schemas/frontmatter/validate.js b/schemas/frontmatter/validate.js new file mode 100644 index 0000000000..7cde546cb8 --- /dev/null +++ b/schemas/frontmatter/validate.js @@ -0,0 +1,276 @@ +#!/usr/bin/env node +/** + * Frontmatter Schema Validator + * + * Validates: + * 1. The frontmatter schema itself is valid JSON Schema (Draft 07) + * 2. All markdown files with frontmatter validate against the schema + * + * Usage: + * node validate.js # Validate all files in repo + * node validate.js path/to/file.md # Validate specific file + * node validate.js --schema-only # Only validate the schema itself + */ + +const fs = require('fs'); +const path = require('path'); +const yaml = require('yaml'); +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); +const glob = require('glob'); + +// Initialize AJV with strict mode and formats +const ajv = new Ajv({ + strict: true, + allErrors: true, + verbose: true, + discriminator: true +}); +addFormats(ajv); + +// Paths +const SCHEMA_PATH = path.join(__dirname, 'frontmatter.schema.json'); +const REPO_ROOT = path.resolve(__dirname, '../..'); + +// ANSI color codes for output +const colors = { + reset: '\x1b[0m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + cyan: '\x1b[36m' +}; + +/** + * Load and validate the schema file itself + */ +function validateSchemaFile() { + console.log(`${colors.blue}📋 Validating schema file...${colors.reset}\n`); + + try { + const schemaContent = fs.readFileSync(SCHEMA_PATH, 'utf8'); + const schema = JSON.parse(schemaContent); + + // Check if it's a valid JSON Schema Draft 07 + const metaSchema = ajv.getSchema('http://json-schema.org/draft-07/schema#'); + if (!metaSchema) { + throw new Error('JSON Schema Draft 07 meta-schema not found'); + } + + const valid = metaSchema(schema); + + if (valid) { + console.log(`${colors.green}✓ Schema is valid JSON Schema Draft 07${colors.reset}`); + return { valid: true, schema }; + } else { + console.error(`${colors.red}✗ Schema validation failed:${colors.reset}`); + console.error(metaSchema.errors); + return { valid: false, errors: metaSchema.errors }; + } + } catch (error) { + console.error(`${colors.red}✗ Failed to load schema:${colors.reset}`, error.message); + return { valid: false, error }; + } +} + +/** + * Extract YAML frontmatter from markdown file + */ +function extractFrontmatter(filePath) { + const content = fs.readFileSync(filePath, 'utf8'); + + // Match YAML frontmatter (--- ... ---) + const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---/; + const match = content.match(frontmatterRegex); + + if (!match) { + return null; + } + + try { + return yaml.parse(match[1]); + } catch (error) { + throw new Error(`Failed to parse YAML frontmatter: ${error.message}`); + } +} + +/** + * Find all markdown files that should have frontmatter + */ +function findMarkdownFiles() { + const patterns = [ + '.github/agents/**/*.md', + '.github/chatmodes/**/*.md', + '.github/instructions/**/*.md', + '.github/prompts/**/*.md', + '.github/ISSUE_TEMPLATE/**/*.md', + '.github/PULL_REQUEST_TEMPLATE/**/*.md', + '.github/SAVED_REPLIES/**/*.md', + 'docs/**/*.md', + 'AGENTS.md', + 'CLAUDE.md', + 'GEMINI.md' + ]; + + const files = []; + patterns.forEach(pattern => { + const matches = glob.sync(pattern, { cwd: REPO_ROOT }); + files.push(...matches.map(f => path.join(REPO_ROOT, f))); + }); + + return [...new Set(files)]; // Remove duplicates +} + +/** + * Validate a single file's frontmatter against the schema + */ +function validateFile(filePath, schema) { + const relativePath = path.relative(REPO_ROOT, filePath); + + try { + const frontmatter = extractFrontmatter(filePath); + + if (!frontmatter) { + return { + file: relativePath, + status: 'no-frontmatter', + message: 'No frontmatter found' + }; + } + + // Compile schema validator + const validate = ajv.compile(schema); + const valid = validate(frontmatter); + + if (valid) { + return { + file: relativePath, + status: 'valid' + }; + } else { + return { + file: relativePath, + status: 'invalid', + errors: validate.errors + }; + } + } catch (error) { + return { + file: relativePath, + status: 'error', + message: error.message + }; + } +} + +/** + * Format validation errors for display + */ +function formatErrors(errors) { + return errors.map(err => { + const path = err.instancePath || '/'; + const message = err.message || 'Unknown error'; + const params = err.params ? JSON.stringify(err.params) : ''; + return ` ${colors.yellow}→${colors.reset} ${path}: ${message} ${params}`; + }).join('\n'); +} + +/** + * Main validation function + */ +function main() { + const args = process.argv.slice(2); + const schemaOnly = args.includes('--schema-only'); + const specificFile = args.find(arg => !arg.startsWith('--')); + + console.log(`${colors.cyan}╔════════════════════════════════════════╗${colors.reset}`); + console.log(`${colors.cyan}║ Frontmatter Schema Validator ║${colors.reset}`); + console.log(`${colors.cyan}╚════════════════════════════════════════╝${colors.reset}\n`); + + // Step 1: Validate the schema itself + const { valid: schemaValid, schema, errors: schemaErrors } = validateSchemaFile(); + + if (!schemaValid) { + console.error(`\n${colors.red}✗ Schema validation failed. Cannot proceed.${colors.reset}`); + process.exit(1); + } + + if (schemaOnly) { + console.log(`\n${colors.green}✓ Schema-only validation complete!${colors.reset}`); + process.exit(0); + } + + // Step 2: Validate frontmatter files + console.log(`\n${colors.blue}📄 Validating frontmatter files...${colors.reset}\n`); + + const filesToValidate = specificFile + ? [path.resolve(specificFile)] + : findMarkdownFiles(); + + console.log(`Found ${filesToValidate.length} markdown files to check\n`); + + const results = { + valid: [], + invalid: [], + noFrontmatter: [], + errors: [] + }; + + filesToValidate.forEach(file => { + const result = validateFile(file, schema); + + switch (result.status) { + case 'valid': + results.valid.push(result); + console.log(`${colors.green}✓${colors.reset} ${result.file}`); + break; + case 'invalid': + results.invalid.push(result); + console.log(`${colors.red}✗${colors.reset} ${result.file}`); + console.log(formatErrors(result.errors)); + console.log(''); + break; + case 'no-frontmatter': + results.noFrontmatter.push(result); + console.log(`${colors.yellow}○${colors.reset} ${result.file} ${colors.yellow}(no frontmatter)${colors.reset}`); + break; + case 'error': + results.errors.push(result); + console.log(`${colors.red}⚠${colors.reset} ${result.file}`); + console.log(` ${colors.red}Error: ${result.message}${colors.reset}`); + console.log(''); + break; + } + }); + + // Summary + console.log(`\n${colors.cyan}═══════════════════════════════════════${colors.reset}`); + console.log(`${colors.cyan}Summary${colors.reset}\n`); + console.log(`${colors.green}Valid:${colors.reset} ${results.valid.length}`); + console.log(`${colors.red}Invalid:${colors.reset} ${results.invalid.length}`); + console.log(`${colors.yellow}No frontmatter:${colors.reset} ${results.noFrontmatter.length}`); + console.log(`${colors.red}Errors:${colors.reset} ${results.errors.length}`); + console.log(`${colors.cyan}═══════════════════════════════════════${colors.reset}\n`); + + // Exit with appropriate code + if (results.invalid.length > 0 || results.errors.length > 0) { + console.error(`${colors.red}✗ Validation failed!${colors.reset}`); + process.exit(1); + } else { + console.log(`${colors.green}✓ All validations passed!${colors.reset}`); + process.exit(0); + } +} + +// Run if called directly +if (require.main === module) { + main(); +} + +module.exports = { + validateSchemaFile, + extractFrontmatter, + validateFile, + findMarkdownFiles +};