scripts/internal/skill-catalog-state.ps1

# Shared skill-catalog state helpers for start/init repair paths.

Set-StrictMode -Version Latest

$script:SkillCatalogDetectHostsPath = Join-Path $PSScriptRoot 'detect-hosts.ps1'
if (-not (Get-Command Get-SpecrewSupportedHostKinds -ErrorAction SilentlyContinue)) {
    if (-not (Test-Path -LiteralPath $script:SkillCatalogDetectHostsPath -PathType Leaf)) {
        throw "Missing detect-hosts helper '$script:SkillCatalogDetectHostsPath'."
    }

    . $script:SkillCatalogDetectHostsPath
}

function Assert-SpecrewSkillCatalogHostHelpers {
    if (-not (Get-Command Get-SpecrewSupportedHostKinds -ErrorAction SilentlyContinue) -or
        -not (Get-Command Get-SpecrewHostSkillRoot -ErrorAction SilentlyContinue)) {
        throw "Skill-catalog host helpers were not loaded from '$script:SkillCatalogDetectHostsPath'."
    }
}

function Resolve-SpecrewSkillCatalogDeploymentScriptPath {
    $repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
    $deploymentScriptPath = Join-Path $repoRoot 'extensions\specrew-speckit\scripts\deploy-squad-runtime.ps1'
    if (-not (Test-Path -LiteralPath $deploymentScriptPath -PathType Leaf)) {
        throw "Missing skill-catalog deployment script '$deploymentScriptPath'."
    }

    return $deploymentScriptPath
}

function Get-SpecrewSkillCatalogRoot {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProjectPath
    )

    Assert-SpecrewSkillCatalogHostHelpers

    $rootsByPath = [ordered]@{}
    foreach ($hostKind in Get-SpecrewSupportedHostKinds) {
        $rootPath = Get-SpecrewHostSkillRoot -HostKind $hostKind -ProjectPath $ProjectPath
        $normalizedPath = [System.IO.Path]::GetFullPath($rootPath)
        if (-not $rootsByPath.Contains($normalizedPath)) {
            $rootsByPath[$normalizedPath] = [pscustomobject]@{
                Path      = $normalizedPath
                HostKinds = New-Object System.Collections.Generic.List[string]
                Exists    = $false
                SkillFileCount = 0
            }
        }

        $rootsByPath[$normalizedPath].HostKinds.Add([string]$hostKind) | Out-Null
    }

    foreach ($entry in $rootsByPath.GetEnumerator()) {
        $entry.Value.Exists = Test-Path -LiteralPath $entry.Value.Path -PathType Container
        $entry.Value.SkillFileCount = if ($entry.Value.Exists) {
            @(Get-ChildItem -LiteralPath $entry.Value.Path -Recurse -Filter 'SKILL.md' -File -ErrorAction SilentlyContinue).Count
        }
        else {
            0
        }
        [pscustomobject]@{
            Path           = $entry.Value.Path
            HostKinds      = @($entry.Value.HostKinds.ToArray())
            Exists         = [bool]$entry.Value.Exists
            SkillFileCount = [int]$entry.Value.SkillFileCount
            HasSkillFiles  = ([int]$entry.Value.SkillFileCount -gt 0)
        }
    }
}

function Get-SpecrewSkillCatalogState {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProjectPath
    )

    $roots = @(Get-SpecrewSkillCatalogRoot -ProjectPath $ProjectPath)
    $missingRoots = @($roots | Where-Object { -not $_.Exists -or -not $_.HasSkillFiles })

    return [pscustomobject]@{
        ProjectPath     = $ProjectPath
        RequiredRoots   = @($roots)
        MissingRoots    = @($missingRoots)
        HasMissingRoots = ($missingRoots.Count -gt 0)
    }
}

function Format-SpecrewSkillCatalogRoots {
    param(
        [AllowEmptyCollection()]
        [object[]]$Roots
    )

    return (@($Roots) | ForEach-Object {
            $hostList = (@($_.HostKinds) -join ',')
            if ([string]::IsNullOrWhiteSpace($hostList)) {
                [string]$_.Path
            }
            else {
                '{0} ({1})' -f $_.Path, $hostList
            }
        }) -join '; '
}

function Invoke-SpecrewSkillCatalogRepair {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProjectPath,

        [string]$DeploymentScriptPath = '',

        [switch]$DryRun
    )

    $beforeState = Get-SpecrewSkillCatalogState -ProjectPath $ProjectPath
    if (-not $beforeState.HasMissingRoots) {
        return [pscustomobject]@{
            Repaired        = $false
            BeforeState     = $beforeState
            AfterState      = $beforeState
            DeploymentScript = $DeploymentScriptPath
            Actions         = @()
        }
    }

    if ([string]::IsNullOrWhiteSpace($DeploymentScriptPath)) {
        $DeploymentScriptPath = Resolve-SpecrewSkillCatalogDeploymentScriptPath
    }

    $deploymentActions = @(
        & $DeploymentScriptPath `
            -ProjectPath $ProjectPath `
            -DryRun:$DryRun `
            -PassThru
    )

    $afterState = Get-SpecrewSkillCatalogState -ProjectPath $ProjectPath
    return [pscustomobject]@{
        Repaired        = (-not $afterState.HasMissingRoots)
        BeforeState     = $beforeState
        AfterState      = $afterState
        DeploymentScript = $DeploymentScriptPath
        Actions         = @($deploymentActions)
    }
}