tools/smoke-install.ps1

# End-to-end installer smoke test. Complements tools/validate-bundle.ps1:
# validate checks the source bundle; this proves the INSTALLED result.
# PowerShell 5.1-compatible.
param(
    [string]$Root,
    [switch]$Keep
)

$ErrorActionPreference = "Stop"
if (-not $Root) { $Root = Split-Path -Parent $PSScriptRoot }

$script:failures = 0
function Fail([string]$msg) { $script:failures++; Write-Host (" FAIL " + $msg) -ForegroundColor Red }
function Pass([string]$msg) { Write-Host (" ok " + $msg) -ForegroundColor DarkGray }

Write-Host "== Maestro install smoke test =="
Write-Host ("root: " + $Root)

$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("maestro-smoke-" + [guid]::NewGuid().ToString("N").Substring(0, 8))
New-Item -ItemType Directory -Path $tmp -Force | Out-Null

try {
    # --- install to two locations via the CLI form (comma string binding) ---
    $pwshExe = "powershell"
    if (Get-Command pwsh -ErrorAction SilentlyContinue) { $pwshExe = "pwsh" }
    & $pwshExe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "Install-Maestro.ps1") `
        -Target $tmp -Locations ".omp,.claude" -Force *> (Join-Path $tmp "install-log.txt")
    if ($LASTEXITCODE -ne 0) {
        Fail ("installer exited nonzero (see " + (Join-Path $tmp "install-log.txt") + ")")
    } else { Pass "installer ran clean for .omp,.claude via comma-string Locations" }

    # --- skill count matches source ---
    $srcSkills = @(Get-ChildItem -LiteralPath (Join-Path $Root "skills") -Directory).Count
    $dstSkills = @(Get-ChildItem -LiteralPath (Join-Path $tmp ".omp/skills") -Directory).Count
    if ($dstSkills -ne $srcSkills) { Fail ("installed skills: $dstSkills, expected $srcSkills") } else { Pass "skills: $dstSkills installed" }

    # --- placeholder substitution ---
    $compose = Join-Path $tmp ".omp/skills/compose/SKILL.md"
    if (-not (Test-Path $compose)) { Fail "missing .omp/skills/compose/SKILL.md" }
    else {
        $t = [System.IO.File]::ReadAllText($compose)
        if ($t -notlike "*.omp/references/conventions.md*") { Fail "compose: {{MAESTRO_CONFIG}} not substituted to .omp path" } else { Pass "substitution: config path baked in" }
    }

    # --- no unresolved placeholders anywhere ---
    $stray = @(Get-ChildItem -LiteralPath $tmp -Filter "*.md" -Recurse -File | Where-Object {
        $_.Name -ne "MAESTRO_CHANGELOG.md" -and
        ([System.IO.File]::ReadAllText($_.FullName)) -match "\{\{MAESTRO_CONFIG\}\}"
    })
    if ($stray.Count -gt 0) { Fail ("unresolved {{MAESTRO_CONFIG}} in: " + (($stray | ForEach-Object { $_.Name }) -join ", ")) } else { Pass "placeholders: zero unresolved" }

    # --- agents land ---
    foreach ($a in @("play.md", "tune.md")) {
        if (-not (Test-Path (Join-Path $tmp ".claude/agents/$a"))) { Fail "missing .claude/agents/$a" }
    }

    # --- version stamp ---
    $want = ([System.IO.File]::ReadAllText((Join-Path $Root "VERSION"))).Trim()
    $got = ([System.IO.File]::ReadAllText((Join-Path $tmp ".omp/MAESTRO_VERSION"))).Trim()
    if ($got -ne $want) { Fail "MAESTRO_VERSION '$got' != VERSION '$want'" } else { Pass "version stamp: $got" }

    # --- changelog shipped ---
    if (-not (Test-Path (Join-Path $tmp ".omp/MAESTRO_CHANGELOG.md"))) { Fail "missing MAESTRO_CHANGELOG.md" } else { Pass "changelog: MAESTRO_CHANGELOG.md shipped" }

    # --- user-scope pass (isolated fake profile via explicit Target override) ---
    $fakeHome = Join-Path $tmp "fakehome"
    New-Item -ItemType Directory -Path $fakeHome | Out-Null
    & $pwshExe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "Install-Maestro.ps1") -Target $fakeHome -Locations ".omp,.claude" -Force -Scope User *> (Join-Path $tmp "user-scope-log.txt")
    if ($LASTEXITCODE -ne 0) {
        Fail ("user-scope install exited nonzero (see user-scope-log.txt)")
    }
    else {
        $uCount = @(Get-ChildItem -LiteralPath (Join-Path $fakeHome ".omp/skills") -Directory).Count
        $uVersion = ([System.IO.File]::ReadAllText((Join-Path $fakeHome ".omp/MAESTRO_VERSION"))).Trim()
        if ($uCount -ne $srcSkills) { Fail ("user-scope skills: $uCount, expected $srcSkills") }
        elseif ($uVersion -ne $want) { Fail ("user-scope MAESTRO_VERSION '$uVersion' != '$want'") }
        else { Pass "user-scope: skills installed under isolated fake profile" }
    $composeAbs = ([System.IO.File]::ReadAllText((Join-Path $fakeHome ".omp/skills/compose/SKILL.md"))).Contains((($fakeHome -replace '\\','/')) + "/.omp/references/conventions.md")
    if ($composeAbs) { Pass "user-scope: spec paths baked absolute" } else { Fail "user-scope: absolute spec path missing in installed skills" }
    }
}
finally {
    if ($Keep) { Write-Host ("temp kept: " + $tmp) -ForegroundColor Yellow }
    else { Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue }
}

Write-Host ""
if ($script:failures -gt 0) {
    Write-Host ("INSTALL SMOKE FAILED: " + $script:failures + " problem(s)") -ForegroundColor Red
    exit 1
}
Write-Host "INSTALL SMOKE PASSED" -ForegroundColor Green
exit 0