#!/usr/bin/env pwsh <# .SYNOPSIS Validates v5 references in the BMAD-METHOD codebase. .DESCRIPTION Searches for all v5 references and categorizes them as: - ALLOWED: Intentional version markers (e.g., CHANGELOG) - DISALLOWED: Documentation that should say v6 - MANUAL_REVIEW: Code/config requiring contextual analysis .PARAMETER Fix If specified, attempts to automatically fix DISALLOWED references. .PARAMETER DryRun If specified with -Fix, shows what would be changed without making changes. .EXAMPLE .\validate-v5-references.ps1 Validates all v5 references and reports violations. .EXAMPLE .\validate-v5-references.ps1 -Fix -DryRun Shows what would be fixed without making changes. .EXAMPLE .\validate-v5-references.ps1 -Fix Automatically fixes all DISALLOWED v5 references. #> param( [switch]$Fix, [switch]$DryRun ) # Configuration $ScriptRoot = $PSScriptRoot if (-not $ScriptRoot) { $ScriptRoot = $PWD.Path } $RootPath = Split-Path -Parent (Split-Path -Parent $ScriptRoot) $FilePatterns = @("*.md", "*.yaml", "*.yml", "*.js", "*.xml") $ExcludeDirs = @("node_modules", ".git", ".patch") # Allowed v5 references (intentional version markers) $AllowedPatterns = @( @{ File = "CHANGELOG.md" Pattern = "## \[v5\.0\.0\]" Reason = "Version history marker" }, @{ File = "CHANGELOG.md" Pattern = "expansion packs from v5 to modules in v5" Reason = "Historical context description" } ) # Files that require manual review (code/config) $ManualReviewExtensions = @(".js", ".json", ".xml") Write-Host "🔍 BMAD v5 Reference Validator" -ForegroundColor Cyan Write-Host "================================" -ForegroundColor Cyan Write-Host "" # Find all matching files Write-Host "📁 Scanning workspace..." -ForegroundColor Yellow $AllFiles = Get-ChildItem -Path $RootPath -Recurse -Include $FilePatterns | Where-Object { $file = $_ -not ($ExcludeDirs | Where-Object { $file.FullName -like "*\$_\*" }) } Write-Host " Found $($AllFiles.Count) files to check" -ForegroundColor Gray Write-Host "" # Search for v5 references Write-Host "🔎 Searching for v5 references..." -ForegroundColor Yellow $V5References = $AllFiles | Select-String -Pattern "\bv5\b" -AllMatches Write-Host " Found $($V5References.Count) v5 references" -ForegroundColor Gray Write-Host "" # Categorize references $Allowed = @() $Disallowed = @() $ManualReview = @() $FixedCount = 0 foreach ($ref in $V5References) { $relativePath = $ref.Path.Replace($RootPath, "").TrimStart("\") $extension = [System.IO.Path]::GetExtension($ref.Path) $fileName = [System.IO.Path]::GetFileName($ref.Path) # Check if this is an allowed reference $isAllowed = $false foreach ($allowed in $AllowedPatterns) { if ($relativePath -like "*$($allowed.File)" -and $ref.Line -match $allowed.Pattern) { $Allowed += [PSCustomObject]@{ Path = $relativePath Line = $ref.LineNumber Content = $ref.Line.Trim() Reason = $allowed.Reason } $isAllowed = $true break } } if ($isAllowed) { continue } # Check if requires manual review if ($ManualReviewExtensions -contains $extension) { $ManualReview += [PSCustomObject]@{ Path = $relativePath Line = $ref.LineNumber Content = $ref.Line.Trim() Extension = $extension } continue } # Otherwise, it's disallowed (should be v6) $Disallowed += [PSCustomObject]@{ Path = $relativePath Line = $ref.LineNumber Content = $ref.Line.Trim() FullPath = $ref.Path } } # Report Results Write-Host "📊 RESULTS" -ForegroundColor Cyan Write-Host "==========" -ForegroundColor Cyan Write-Host "" Write-Host "✅ ALLOWED (Intentional Version Markers): $($Allowed.Count)" -ForegroundColor Green if ($Allowed.Count -gt 0) { foreach ($item in $Allowed) { Write-Host " 📄 $($item.Path):$($item.Line)" -ForegroundColor Gray Write-Host " $($item.Content)" -ForegroundColor DarkGray Write-Host " Reason: $($item.Reason)" -ForegroundColor DarkGray Write-Host "" } } Write-Host "⚠️ MANUAL REVIEW REQUIRED (Code/Config): $($ManualReview.Count)" -ForegroundColor Yellow if ($ManualReview.Count -gt 0) { $ManualReview | Group-Object Extension | ForEach-Object { Write-Host " $($_.Name) files: $($_.Count)" -ForegroundColor Gray foreach ($item in $_.Group | Select-Object -First 5) { Write-Host " $($item.Path):$($item.Line)" -ForegroundColor DarkGray Write-Host " $($item.Content)" -ForegroundColor DarkGray } if ($_.Count -gt 5) { Write-Host " ... and $($_.Count - 5) more" -ForegroundColor DarkGray } } Write-Host "" } Write-Host "❌ DISALLOWED (Should be v6): $($Disallowed.Count)" -ForegroundColor Red if ($Disallowed.Count -gt 0) { # Group by file $DisallowedByFile = $Disallowed | Group-Object Path | Sort-Object Count -Descending Write-Host " Top files with v5 references:" -ForegroundColor Gray foreach ($fileGroup in $DisallowedByFile | Select-Object -First 10) { Write-Host " $($fileGroup.Name): $($fileGroup.Count) references" -ForegroundColor DarkGray } if ($DisallowedByFile.Count -gt 10) { Write-Host " ... and $($DisallowedByFile.Count - 10) more files" -ForegroundColor DarkGray } Write-Host "" } # Fix disallowed references if requested if ($Fix -and $Disallowed.Count -gt 0) { Write-Host "" Write-Host "🔧 FIXING DISALLOWED REFERENCES" -ForegroundColor Cyan Write-Host "================================" -ForegroundColor Cyan Write-Host "" if ($DryRun) { Write-Host " [DRY RUN MODE - No files will be modified]" -ForegroundColor Yellow Write-Host "" } # Group by file for efficient processing $DisallowedByFile = $Disallowed | Group-Object FullPath foreach ($fileGroup in $DisallowedByFile) { $filePath = $fileGroup.Name $relativePath = $filePath.Replace($RootPath, "").TrimStart("\") $refCount = $fileGroup.Count Write-Host " 📝 $relativePath ($refCount references)" -ForegroundColor Yellow if (-not $DryRun) { # Read file content $content = Get-Content -Path $filePath -Raw # Replace v5 with v6 # Use word boundary regex to avoid replacing in unintended contexts $newContent = $content -replace '\bv5\b', 'v6' # Write back Set-Content -Path $filePath -Value $newContent -NoNewline $FixedCount += $refCount Write-Host " ✅ Fixed $refCount references" -ForegroundColor Green } else { Write-Host " [Would fix $refCount references]" -ForegroundColor Gray } } Write-Host "" if (-not $DryRun) { Write-Host "✅ Fixed $FixedCount v5 references across $($DisallowedByFile.Count) files" -ForegroundColor Green } else { Write-Host " [Dry run complete - run without -DryRun to apply changes]" -ForegroundColor Yellow } } # Summary Write-Host "" Write-Host "📋 SUMMARY" -ForegroundColor Cyan Write-Host "==========" -ForegroundColor Cyan Write-Host " Total v5 references found: $($V5References.Count)" -ForegroundColor White Write-Host " ✅ Allowed (intentional): $($Allowed.Count)" -ForegroundColor Green Write-Host " ⚠️ Manual review required: $($ManualReview.Count)" -ForegroundColor Yellow Write-Host " ❌ Disallowed (should be v6): $($Disallowed.Count)" -ForegroundColor Red if ($Fix -and -not $DryRun) { Write-Host " 🔧 Fixed: $FixedCount" -ForegroundColor Green } Write-Host "" # Exit code if ($Disallowed.Count -gt 0 -and -not $Fix) { Write-Host "❌ VALIDATION FAILED - Found $($Disallowed.Count) v5 references that should be v6" -ForegroundColor Red Write-Host " Run with -Fix to automatically correct, or -Fix -DryRun to preview changes" -ForegroundColor Yellow exit 1 } elseif ($ManualReview.Count -gt 0) { Write-Host "⚠️ MANUAL REVIEW NEEDED - Found $($ManualReview.Count) code/config references" -ForegroundColor Yellow Write-Host " Review these files manually to determine if they should be updated" -ForegroundColor Yellow exit 0 } else { Write-Host "✅ VALIDATION PASSED - All v5 references are appropriate" -ForegroundColor Green exit 0 }