param( [Parameter(Mandatory = $true)] [string]$Title, [Parameter(Mandatory = $true)] [string]$Area, [Parameter(Mandatory = $true)] [string]$Expected, [Parameter(Mandatory = $true)] [string]$Actual, [Parameter(Mandatory = $false)] [ValidateSet("low", "medium", "high", "critical")] [string]$Severity = "medium", [Parameter(Mandatory = $false)] [string]$TriggerContext = "Not specified", [Parameter(Mandatory = $false)] [string[]]$ReproSteps = @(), [Parameter(Mandatory = $false)] [string]$Impact = "", [Parameter(Mandatory = $false)] [string]$Workaround = "", [Parameter(Mandatory = $false)] [string]$DepersonalizationNotes = "No client names, copied client documents, Figma content, credentials, or private identifiers are included.", [Parameter(Mandatory = $false)] [switch]$PrivacyConfirmed, [Parameter(Mandatory = $false)] [string]$WorkspaceDir = "workspace", [Parameter(Mandatory = $false)] [string]$SystemManifestPath = "wireframe-system.manifest.json" ) $ErrorActionPreference = "Stop" function Get-RepoRoot { return (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) } function Resolve-RepoPath { param( [string]$Root, [string]$Path ) if ([System.IO.Path]::IsPathRooted($Path)) { return $Path } return Join-Path $Root $Path } function New-Slug { param([string]$Value) $slug = $Value.ToLowerInvariant() -replace "[^a-z0-9]+", "-" $slug = $slug.Trim("-") if ([string]::IsNullOrWhiteSpace($slug)) { return "bug-report" } if ($slug.Length -gt 48) { return $slug.Substring(0, 48).Trim("-") } return $slug } function New-Directory { param([string]$Path) if (-not (Test-Path -LiteralPath $Path)) { New-Item -ItemType Directory -Force -Path $Path | Out-Null } } $root = Get-RepoRoot $workspacePath = Resolve-RepoPath -Root $root -Path $WorkspaceDir $reportRoot = Join-Path $workspacePath "system-feedback/bug-reports" New-Directory -Path $reportRoot $systemVersion = "unknown" $manifestPath = Resolve-RepoPath -Root $root -Path $SystemManifestPath if (Test-Path -LiteralPath $manifestPath) { $manifest = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json if ($manifest.PSObject.Properties.Name -contains "version") { $systemVersion = [string]$manifest.version } } $createdAt = (Get-Date).ToUniversalTime() $id = "BUG-" + $createdAt.ToString("yyyyMMdd-HHmmss-fff") $reportDir = Join-Path $reportRoot ($id + "-" + (New-Slug -Value $Title)) New-Directory -Path $reportDir New-Directory -Path (Join-Path $reportDir "sanitized-snippets") $report = [ordered]@{ id = $id title = $Title created_at = $createdAt.ToString("o") system_version = $systemVersion area = $Area severity = $Severity trigger_context = $TriggerContext actual_behavior = $Actual expected_behavior = $Expected repro_steps = @($ReproSteps) impact = $Impact workaround = $Workaround depersonalization_notes = $DepersonalizationNotes privacy_confirmed = [bool]$PrivacyConfirmed } $jsonPath = Join-Path $reportDir "report.json" $report | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $jsonPath -Encoding UTF8 $stepsText = if (@($ReproSteps).Count -gt 0) { (@($ReproSteps) | ForEach-Object { "- $_" }) -join "`n" } else { "- Not provided" } $markdown = @" # $id - $Title - Created at: $($report.created_at) - System version: $systemVersion - Area: $Area - Severity: $Severity - Privacy confirmed: $([bool]$PrivacyConfirmed) ## Trigger Context $TriggerContext ## Actual Behavior $Actual ## Expected Behavior $Expected ## Reproduction Steps $stepsText ## Impact $Impact ## Workaround $Workaround ## Depersonalization Notes $DepersonalizationNotes ## Sanitized Attachments Place only manually cleaned snippets in sanitized-snippets/. Do not attach source client documents, copied Figma content, credentials, private names, or identifiers. "@ $markdownPath = Join-Path $reportDir "report.md" Set-Content -LiteralPath $markdownPath -Value $markdown -Encoding UTF8 Set-Content -LiteralPath (Join-Path $reportDir "sanitized-snippets/.gitkeep") -Value "" -Encoding UTF8 Write-Output "Bug report created: $reportDir" Write-Output $jsonPath Write-Output $markdownPath