src/Engine/State.ps1

# State.ps1 — refresh entry state from the live session and the disk.

# Cheap: in-session loaded modules only (no disk scan).
# NB: $Entries params here allow null/empty - an empty entry set is a normal
# state (fresh machine, zero configs), and PowerShell returns empty arrays
# from functions as $null.
function Update-PSMMLoaded {
    [CmdletBinding()]
    param([AllowNull()][AllowEmptyCollection()] $Entries)
    if (-not $Entries) { return }
    $loaded = @{}
    Get-Module -ErrorAction SilentlyContinue | ForEach-Object { $loaded[$_.Name] = $_.Version }
    foreach ($e in $Entries) {
        $e.Loaded = $loaded.ContainsKey($e.Name)
        $e.LoadedVersion = $loaded[$e.Name]
    }
}

# Availability, versions and scope from disk.
# With -Name: cheap, name-filtered refresh for just those modules.
# Without: ONE full -ListAvailable scan (call sparingly: open + reload only).
function Update-PSMMAvailable {
    [CmdletBinding()]
    param(
        [AllowNull()][AllowEmptyCollection()] $Entries,
        [string[]]$Name
    )
    if (-not $Entries) { return }
    $apply = {
        param($entry, $mods)   # $mods: Get-Module -ListAvailable results, one per version
        $sorted = @($mods | Sort-Object Version -Descending)
        $entry.Installed         = [bool]$sorted
        $entry.InstalledVersion  = if ($sorted) { $sorted[0].Version } else { $null }
        $entry.InstalledVersions = @($sorted | ForEach-Object {
            [pscustomobject]@{
                Version = $_.Version
                Path    = $_.ModuleBase
                Scope   = Get-PSMMScopeForPath -Path $_.ModuleBase
            }
        })
        $scopes = @($entry.InstalledVersions.Scope | Select-Object -Unique)
        $entry.InstallScope = if ($scopes.Count -gt 1) { 'mixed' } elseif ($scopes.Count -eq 1) { $scopes[0] } else { $null }
    }

    if ($Name) {
        foreach ($nm in $Name) {
            $mods = @(Get-Module -ListAvailable -Name $nm -ErrorAction SilentlyContinue)
            foreach ($e in @($Entries | Where-Object Name -eq $nm)) { & $apply $e $mods }
        }
        return
    }
    $avail = @{}
    Get-Module -ListAvailable -ErrorAction SilentlyContinue | Group-Object Name | ForEach-Object {
        $avail[$_.Name] = $_.Group
    }
    foreach ($e in $Entries) {
        & $apply $e @(if ($e.Name -and $avail.ContainsKey($e.Name)) { $avail[$e.Name] } else { @() })
    }
}

# Modules installed with MORE than one version on disk (cleanup feature):
# every version except the newest is a pruning candidate.
function Get-PSMMDuplicateVersion {
    [CmdletBinding()] param()
    Get-Module -ListAvailable -ErrorAction SilentlyContinue |
        Group-Object Name |
        Where-Object { @($_.Group.Version | Select-Object -Unique).Count -gt 1 } |
        ForEach-Object {
            $sorted = @($_.Group | Sort-Object Version -Descending)
            [pscustomobject]@{
                Name     = $_.Name
                Latest   = $sorted[0].Version
                Obsolete = @($sorted | Select-Object -Skip 1 | ForEach-Object {
                    [pscustomobject]@{
                        Version = $_.Version
                        Path    = $_.ModuleBase
                        Scope   = Get-PSMMScopeForPath -Path $_.ModuleBase
                    }
                })
            }
        }
}

# Installed modules NOT named in any config file (unmanaged-module feature).
# Returns one object per module name, newest version, with scope.
function Get-PSMMUnmanagedModule {
    [CmdletBinding()]
    param([Parameter(Mandatory)][string[]]$ManagedNames)
    $managed = [System.Collections.Generic.HashSet[string]]::new(
        [string[]]$ManagedNames, [System.StringComparer]::OrdinalIgnoreCase)
    Get-Module -ListAvailable -ErrorAction SilentlyContinue |
        Group-Object Name |
        Where-Object { -not $managed.Contains($_.Name) } |
        ForEach-Object {
            $newest = @($_.Group | Sort-Object Version -Descending)[0]
            [pscustomobject]@{
                Name        = $_.Name
                Version     = $newest.Version
                Scope       = Get-PSMMScopeForPath -Path $newest.ModuleBase
                Description = $newest.Description
            }
        }
}