entry.psm1

# Entry module for Delta-Code
# Provides entry point functionality as reusable module functions
# Loads satellite, workspace, and workflow modules for extended commands

# Resolve module dependencies from the sibling directory when available (repository
# and flat-install layouts) and fall back to module-name resolution (PSGallery).
foreach ($dependencyName in @('delta-code', 'satellite', 'workspace', 'workflow')) {
    $dependencyManifest = Join-Path $PSScriptRoot "$dependencyName.psd1"
    if (Test-Path -LiteralPath $dependencyManifest) {
        Import-Module $dependencyManifest -ErrorAction Stop -DisableNameChecking
    }
    else {
        Import-Module $dependencyName -ErrorAction Stop -DisableNameChecking
    }
}
Remove-Variable dependencyName, dependencyManifest -ErrorAction SilentlyContinue

function Show-DeltaCodeUsage {
    [CmdletBinding()]
    param(
        [string]$ErrorMessage
    )
    if ($ErrorMessage) {
        Write-Error $ErrorMessage
        Write-Host ""
    }
    Write-Host "Usage: delta-code [-h | --help] [--prj-dir=<path>] <command> [args...]"
    Write-Host ""
    Write-Host "Commands:"
    Write-Host " diff <from> <to> [options] Compare two paths"
    Write-Host " satellite <name> <cmd> [args] Invoke satellite command"
    Write-Host " satellites List registered satellites"
    Write-Host " workspace list List registered workspaces"
    Write-Host " workspace show Show active workspace"
    Write-Host " workspace use <name> Set active workspace"
    Write-Host " workspace register <name> <path> Register a workspace"
    Write-Host " workflow list List registered workflows"
    Write-Host " workflow show <name> Show workflow details"
    Write-Host " workflow run <name> [--dry-run] Execute workflow"
    Write-Host " workflow validate <name> Validate workflow"
    Write-Host " workflow history Show execution history"
    Write-Host " workflow metrics Show workflow metrics"
    Write-Host " workflow register <name> <workspace> <steps.json> Register workflow"
    Write-Host " profile list List automation profiles"
    Write-Host " profile show <name> Show profile details"
    Write-Host " profile run <name> [--dry-run] Run automation profile"
    Write-Host " profile register <name> <workflow> [options] Register profile"
    Write-Host " health [--root=<path>] [--json] Verify the installation"
    Write-Host " <npm script...> Forward to npm"
    Write-Host ""
    Write-Host "Options:"
    Write-Host " -h, --help Show this help message and exit."
    Write-Host " --prj-dir=<path> Project directory to run the command in (default: current directory)."
    Write-Host " --prj-dir <path> Space-separated form; use this for Windows paths."
    Write-Host ""
    Write-Host "Health options:"
    Write-Host " --root=<path> Installation or tree to check (default: this installation)."
    Write-Host " --root <path> Space-separated form; use this for Windows paths."
    Write-Host " --json Emit the report as JSON."
    Write-Host " --skip-module-load Do not import each module during the check."
    Write-Host " --skip-cli Do not run the launcher smoke test."
    Write-Host ""
    Write-Host "Examples:"
    Write-Host " delta-code diff . ../other"
    Write-Host " delta-code diff --from master --to feature/fix"
    Write-Host " delta-code satellite mysat build"
    Write-Host " delta-code satellites"
    Write-Host " delta-code workspace list"
    Write-Host " delta-code workspace use myproj"
    Write-Host " delta-code workspace register myproj C:\src\myproj"
    Write-Host " delta-code workflow list"
    Write-Host " delta-code workflow run build-and-diff"
    Write-Host " delta-code workflow run build-and-diff --dry-run"
    Write-Host " delta-code workflow validate build-and-diff"
    Write-Host " delta-code workflow history"
    Write-Host " delta-code workflow metrics"
    Write-Host " delta-code profile run nightly"
    Write-Host " delta-code profile register nightly build-and-diff --retry=2"
    Write-Host " delta-code --prj-dir=C:\src\project run dev"
}

function Show-DeltaCodeDiffUsage {
    [CmdletBinding()]
    param(
        [string]$ErrorMessage
    )
    if ($ErrorMessage) {
        Write-Error $ErrorMessage
        Write-Host ""
    }
    Write-Host "Usage: delta-code diff <from> <to> [options]"
    Write-Host ""
    Write-Host "Options:"
    Write-Host " --from <path> Source path (base) to compare from."
    Write-Host " --to <path> Target path to compare against."
    Write-Host " --ignore-whitespace Ignore whitespace-only differences (default: true)."
    Write-Host " --format <table|json> Output format (default: table)."
    Write-Host " --recursive Recursively compare directories (default: true)."
    Write-Host " -h, --help Show this help message and exit."
}

function Parse-DeltaCodeArgs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string[]]$Args
    )
    $result = [PSCustomObject]@{
        PrjDir       = if ($env:DELTA_CODE_PRJ_DIR) { $env:DELTA_CODE_PRJ_DIR } else { "." }
        Command      = $null
        DiffFrom     = $null
        DiffTo       = $null
        IgnoreWhitespace = $true
        Format       = 'table'
        ForwardArgs  = @()
        ShowHelp     = $false
        ShowDiffHelp = $false
        Error        = $null
        ListSatellites = $false
        SatelliteName = $null
        SatelliteCommand = $null
        SatelliteArgs = @()
        WorkspaceSubCommand = $null
        WorkspaceName = $null
        WorkspacePath = $null
        WorkspaceArgs = @()
        WorkflowSubCommand = $null
        WorkflowName = $null
        WorkflowArgs = @()
        WorkflowDryRun = $false
        WorkflowLimit = 50
        ProfileSubCommand = $null
        ProfileName = $null
        ProfileWorkflow = $null
        ProfileArgs = @()
        ProfileDryRun = $false
        ProfileRetryCount = 0
        ProfileContinueOnError = $false
        ProfileInputParams = @{}
        HealthRoot = $null
        HealthJson = $false
        HealthSkipCli = $false
        HealthSkipModuleLoad = $false
        HealthHelp = $false
    }

    $firstArg = $true
    for ($i = 0; $i -lt $Args.Count; $i++) {
        $arg = $Args[$i]
        if (($arg -eq '-h' -or $arg -eq '--help') -and $firstArg) {
            $result.ShowHelp = $true
            continue
        }
        $firstArg = $false
        if ($arg -like '--prj-dir=*') {
            $result.PrjDir = $arg.Substring('--prj-dir='.Length)
            continue
        }
        if ($arg -like '-prj-dir=*') {
            $result.PrjDir = $arg.Substring($arg.IndexOf('=') + 1)
            continue
        }
        # Space-separated form: `--prj-dir C:\path`. Required on Windows because
        # `pwsh -File` splits `--opt=C:\path` at the drive colon.
        if ($arg -eq '--prj-dir' -or $arg -eq '-prj-dir') {
            if ($i + 1 -lt $Args.Count) {
                $i++
                $result.PrjDir = $Args[$i]
            }
            else {
                $result.Error = "--prj-dir requires a path"
            }
            continue
        }
        $result.ForwardArgs += $arg
    }

    if ($result.Error) {
        return $result
    }

    if ($result.ForwardArgs.Count -eq 0) {
        if (-not $result.ShowHelp) {
            $result.Error = "No command specified"
        }
        return $result
    }

    $result.Command = $result.ForwardArgs[0]

    if ($result.Command -eq 'diff') {
        $diffArgs = $result.ForwardArgs[1..($result.ForwardArgs.Count - 1)]
        for ($i = 0; $i -lt $diffArgs.Count; $i++) {
            switch ($diffArgs[$i]) {
                { $_ -eq '-h' -or $_ -eq '--help' } { $result.ShowDiffHelp = $true; break }
                { $_ -eq '--from' } { $result.DiffFrom = $diffArgs[++$i]; continue }
                { $_ -eq '--to' } { $result.DiffTo = $diffArgs[++$i]; continue }
                { $_ -eq '--ignore-whitespace' } { $result.IgnoreWhitespace = $true; continue }
                { $_ -eq '--no-ignore-whitespace' } { $result.IgnoreWhitespace = $false; continue }
                { $_ -eq '--format' } { $result.Format = $diffArgs[++$i]; continue }
                { $_ -eq '--recursive' } { continue }
                default {
                    if (-not $result.DiffFrom) { $result.DiffFrom = $_ }
                    elseif (-not $result.DiffTo) { $result.DiffTo = $_ }
                    else {
                        $result.Error = "Unexpected argument: $_"
                        break
                    }
                }
            }
            if ($result.Error -or $result.ShowDiffHelp) { break }
        }

        if (-not $result.ShowDiffHelp -and (-not $result.DiffFrom -or -not $result.DiffTo)) {
            $result.Error = "Both --from and --to are required for diff"
        }
    }
    elseif ($result.Command -eq 'satellite') {
        $satArgs = $result.ForwardArgs[1..($result.ForwardArgs.Count - 1)]
        if ($satArgs.Count -lt 2) {
            $result.Error = "satellite command requires <name> <cmd> [args...]"
        }
        else {
            $result.SatelliteName = $satArgs[0]
            $result.SatelliteCommand = $satArgs[1]
            # Assign explicitly: routing an empty array through an `if` expression
            # collapses it to $null, which then breaks callers expecting .Count.
            $result.SatelliteArgs = @()
            if ($satArgs.Count -gt 2) {
                $result.SatelliteArgs = @($satArgs[2..($satArgs.Count - 1)])
            }
        }
    }
    elseif ($result.Command -eq 'satellites') {
        $result.ListSatellites = $true
    }
    elseif ($result.Command -eq 'workspace') {
        $wsArgs = $result.ForwardArgs[1..($result.ForwardArgs.Count - 1)]
        if ($wsArgs.Count -eq 0) {
            $result.Error = "workspace command requires <list|show|use|register> [args...]"
        }
        else {
            $result.WorkspaceSubCommand = $wsArgs[0]
            switch ($wsArgs[0]) {
                'list' { }
                'show' { }
                'use' {
                    if ($wsArgs.Count -lt 2) {
                        $result.Error = "workspace use requires <name>"
                    }
                    else {
                        $result.WorkspaceName = $wsArgs[1]
                    }
                }
                'register' {
                    if ($wsArgs.Count -lt 3) {
                        $result.Error = "workspace register requires <name> <path> [satellite]"
                    }
                    else {
                        $result.WorkspaceName = $wsArgs[1]
                        $result.WorkspacePath = $wsArgs[2]
                        $result.WorkspaceArgs = if ($wsArgs.Count -gt 3) { $wsArgs[3..($wsArgs.Count - 1)] } else { @() }
                    }
                }
                default {
                    $result.Error = "Unknown workspace subcommand: $($wsArgs[0]). Valid: list, show, use, register"
                }
            }
        }
    }
    elseif ($result.Command -eq 'workflow') {
        $wfArgs = $result.ForwardArgs[1..($result.ForwardArgs.Count - 1)]
        if ($wfArgs.Count -eq 0) {
            $result.Error = "workflow command requires <list|show|run|register|validate|history|metrics> [args...]"
        }
        else {
            $result.WorkflowSubCommand = $wfArgs[0]
            switch ($wfArgs[0]) {
                'list' { }
                'show' {
                    if ($wfArgs.Count -lt 2) {
                        $result.Error = "workflow show requires <name>"
                    }
                    else {
                        $result.WorkflowName = $wfArgs[1]
                    }
                }
                'run' {
                    if ($wfArgs.Count -lt 2) {
                        $result.Error = "workflow run requires <name>"
                    }
                    else {
                        $result.WorkflowName = $wfArgs[1]
                        $runArgs = if ($wfArgs.Count -gt 2) { $wfArgs[2..($wfArgs.Count - 1)] } else { @() }
                        foreach ($arg in $runArgs) {
                            if ($arg -eq '--dry-run' -or $arg -eq '-n') {
                                $result.WorkflowDryRun = $true
                            }
                            else {
                                $result.WorkflowArgs += $arg
                            }
                        }
                    }
                }
                'register' {
                    if ($wfArgs.Count -lt 3) {
                        $result.Error = "workflow register requires <name> <workspace> <steps.json>"
                    }
                    else {
                        $result.WorkflowName = $wfArgs[1]
                        $result.WorkspaceName = $wfArgs[2]
                        $result.WorkflowArgs = if ($wfArgs.Count -gt 3) { $wfArgs[3..($wfArgs.Count - 1)] } else { @() }
                    }
                }
                'validate' {
                    if ($wfArgs.Count -lt 2) {
                        $result.Error = "workflow validate requires <name>"
                    }
                    else {
                        $result.WorkflowName = $wfArgs[1]
                    }
                }
                'history' {
                    $result.WorkflowLimit = 50
                    $histArgs = if ($wfArgs.Count -gt 1) { $wfArgs[1..($wfArgs.Count - 1)] } else { @() }
                    foreach ($arg in $histArgs) {
                        if ($arg -like '--workflow=*') {
                            $result.WorkflowName = $arg.Substring('--workflow='.Length)
                        }
                        elseif ($arg -like '--workspace=*') {
                            $result.WorkspaceName = $arg.Substring('--workspace='.Length)
                        }
                        elseif ($arg -like '--limit=*') {
                            $result.WorkflowLimit = [int]$arg.Substring('--limit='.Length)
                        }
                    }
                }
                'metrics' {
                    $result.WorkflowLimit = 100
                    $histArgs = if ($wfArgs.Count -gt 1) { $wfArgs[1..($wfArgs.Count - 1)] } else { @() }
                    foreach ($arg in $histArgs) {
                        if ($arg -like '--workflow=*') {
                            $result.WorkflowName = $arg.Substring('--workflow='.Length)
                        }
                        elseif ($arg -like '--workspace=*') {
                            $result.WorkspaceName = $arg.Substring('--workspace='.Length)
                        }
                        elseif ($arg -like '--limit=*') {
                            $result.WorkflowLimit = [int]$arg.Substring('--limit='.Length)
                        }
                    }
                }
                default {
                    $result.Error = "Unknown workflow subcommand: $($wfArgs[0]). Valid: list, show, run, register, validate, history, metrics"
                }
            }
        }
    }
    elseif ($result.Command -eq 'profile') {
        $pfArgs = $result.ForwardArgs[1..($result.ForwardArgs.Count - 1)]
        if ($pfArgs.Count -eq 0) {
            $result.Error = "profile command requires <list|show|run|register> [args...]"
        }
        else {
            $result.ProfileSubCommand = $pfArgs[0]
            switch ($pfArgs[0]) {
                'list' { }
                'show' {
                    if ($pfArgs.Count -lt 2) {
                        $result.Error = "profile show requires <name>"
                    }
                    else {
                        $result.ProfileName = $pfArgs[1]
                    }
                }
                'run' {
                    if ($pfArgs.Count -lt 2) {
                        $result.Error = "profile run requires <name>"
                    }
                    else {
                        $result.ProfileName = $pfArgs[1]
                        $runArgs = if ($pfArgs.Count -gt 2) { $pfArgs[2..($pfArgs.Count - 1)] } else { @() }
                        foreach ($arg in $runArgs) {
                            if ($arg -eq '--dry-run' -or $arg -eq '-n') {
                                $result.ProfileDryRun = $true
                            }
                            else {
                                $result.ProfileArgs += $arg
                            }
                        }
                    }
                }
                'register' {
                    if ($pfArgs.Count -lt 2) {
                        $result.Error = "profile register requires <name> <workflow> [--retry=N] [--continue-on-error] [--dry-run] [--param=key=value]"
                    }
                    else {
                        $result.ProfileName = $pfArgs[1]
                        $result.ProfileWorkflow = $pfArgs[2]
                        $pfOpts = if ($pfArgs.Count -gt 3) { $pfArgs[3..($pfArgs.Count - 1)] } else { @() }
                        foreach ($arg in $pfOpts) {
                            if ($arg -like '--retry=*') {
                                $result.ProfileRetryCount = [int]$arg.Substring('--retry='.Length)
                            }
                            elseif ($arg -eq '--continue-on-error') {
                                $result.ProfileContinueOnError = $true
                            }
                            elseif ($arg -eq '--dry-run') {
                                $result.ProfileDryRun = $true
                            }
                            elseif ($arg -like '--param=*') {
                                $kv = $arg.Substring('--param='.Length)
                                if ($kv -like '*=*') {
                                    $key = $kv.Substring(0, $kv.IndexOf('='))
                                    $value = $kv.Substring($kv.IndexOf('=') + 1)
                                    $result.ProfileInputParams[$key] = $value
                                }
                            }
                        }
                    }
                }
                default {
                    $result.Error = "Unknown profile subcommand: $($pfArgs[0]). Valid: list, show, run, register"
                }
            }
        }
    }
    elseif ($result.Command -eq 'health') {
        $healthArgs = @()
        if ($result.ForwardArgs.Count -gt 1) {
            $healthArgs = @($result.ForwardArgs[1..($result.ForwardArgs.Count - 1)])
        }
        for ($i = 0; $i -lt $healthArgs.Count; $i++) {
            $arg = $healthArgs[$i]
            if ($arg -like '--root=*') {
                $result.HealthRoot = $arg.Substring('--root='.Length)
            }
            elseif ($arg -eq '--root') {
                # Space-separated form: see the `--prj-dir` note above.
                if ($i + 1 -lt $healthArgs.Count) {
                    $i++
                    $result.HealthRoot = $healthArgs[$i]
                }
                else {
                    $result.Error = "--root requires a path"
                }
            }
            elseif ($arg -eq '--json') {
                $result.HealthJson = $true
            }
            elseif ($arg -eq '--skip-cli') {
                $result.HealthSkipCli = $true
            }
            elseif ($arg -eq '--skip-module-load') {
                $result.HealthSkipModuleLoad = $true
            }
            elseif ($arg -eq '-h' -or $arg -eq '--help') {
                $result.HealthHelp = $true
            }
            else {
                $result.Error = "Unknown health option: $arg. Valid: --root=<path>, --json, --skip-cli, --skip-module-load"
            }
        }
    }

    return $result
}

function Invoke-DeltaCodeDiff {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$From,
        [Parameter(Mandatory = $true)]
        [string]$To,
        [bool]$IgnoreWhitespace = $true,
        [ValidateSet('table', 'json')]
        [string]$Format = 'table'
    )
    # Same sibling-then-name resolution as the import block at the top of this
    # module. This used to import "$PSScriptRoot\..\modules\delta-code.psd1",
    # which only exists in the repository and flat-install layouts; in a
    # PSGallery install the sibling directory does not exist and every call threw
    # "no valid module file was found in any module directory".
    $diffManifest = Join-Path $PSScriptRoot 'delta-code.psd1'
    if (Test-Path -LiteralPath $diffManifest) {
        Import-Module $diffManifest -ErrorAction Stop -DisableNameChecking
    }
    else {
        Import-Module 'delta-code' -ErrorAction Stop -DisableNameChecking
    }
    $diff = Get-DeltaCodeDiff -From $From -To $To -IgnoreWhitespace $IgnoreWhitespace
    if ($Format -eq 'json') {
        $diff | ConvertTo-Json -Depth 5
    } else {
        $diff | Format-List
    }
}

function Invoke-DeltaCodeNpm {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$PrjDir,
        [Parameter(Mandatory = $true)]
        [string[]]$ForwardArgs
    )
    if ($PrjDir -ne ".") {
        Push-Location $PrjDir
    }
    try {
        & npm @ForwardArgs
        exit $LASTEXITCODE
    } finally {
        if ($PrjDir -ne ".") {
            Pop-Location
        }
    }
}

function Invoke-DeltaCodeSatellite {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [Parameter(Mandatory = $true)]
        [string]$Command,
        [string[]]$Args = @()
    )
    Initialize-DeltaCodeSatellites
    Invoke-DeltaCodeSatellite -Name $Name -Command $Command -Args $Args
}

function Invoke-DeltaCodeListSatellites {
    [CmdletBinding()]
    param()
    Initialize-DeltaCodeSatellites
    $sats = Get-DeltaCodeSatellites
    if (-not $sats) {
        Write-Host "No satellites registered"
        return
    }
    $sats | Format-Table Name, Category, Path, Description
}

function Invoke-DeltaCodeWorkspaceList {
    [CmdletBinding()]
    param()
    Initialize-DeltaCodeWorkspaces
    $ws = Get-DeltaCodeWorkspaces
    if (-not $ws) {
        Write-Host "No workspaces registered"
        return
    }
    $ws | Format-Table Name, Root, Category, Satellite, Active
}

function Invoke-DeltaCodeWorkspaceShow {
    [CmdletBinding()]
    param()
    Initialize-DeltaCodeWorkspaces
    $active = Get-DeltaCodeActiveWorkspace
    if (-not $active) {
        Write-Host "No active workspace"
        return
    }
    $active | Format-List Name, Root, Category, Satellite, Description, Metadata, Active
}

function Invoke-DeltaCodeWorkspaceUse {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    Initialize-DeltaCodeWorkspaces
    $result = Set-DeltaCodeWorkspace -Name $Name
    if ($result) {
        Write-Host "Active workspace set to '$Name'"
    }
    else {
        Write-Error "Failed to set active workspace: $Name"
    }
}

function Invoke-DeltaCodeWorkspaceRegister {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [Parameter(Mandatory = $true)]
        [string]$Path,
        [string]$Satellite = $null,
        [string[]]$Args = @()
    )
    Initialize-DeltaCodeWorkspaces
    $meta = @{}
    foreach ($arg in $Args) {
        if ($arg -like '--*=*') {
            $key = $arg.Substring(2, $arg.IndexOf('=') - 2)
            $value = $arg.Substring($arg.IndexOf('=') + 1)
            $meta[$key] = $value
        }
    }
    $result = Register-DeltaCodeWorkspace -Name $Name -Root $Path -Satellite $Satellite -Metadata $meta
    if ($result) {
        Write-Host "Workspace '$Name' registered at '$Path'"
    }
    else {
        Write-Error "Failed to register workspace: $Name"
    }
}

function Invoke-DeltaCodeWorkflowList {
    [CmdletBinding()]
    param()
    Initialize-DeltaCodeWorkflows
    $wfs = Get-DeltaCodeWorkflows
    if (-not $wfs) {
        Write-Host "No workflows registered"
        return
    }
    $wfs | Format-Table Name, Workspace, Category, Description, Steps
}

function Invoke-DeltaCodeWorkflowShow {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    Initialize-DeltaCodeWorkflows
    $wf = Get-DeltaCodeWorkflow -Name $Name
    if (-not $wf) {
        Write-Error "Workflow '$Name' not found"
        return
    }
    $wf | Format-List Name, Workspace, Category, Description, Steps, Metadata
}

function Invoke-DeltaCodeWorkflowRun {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [switch]$DryRun,
        [hashtable]$InputParams = @{}
    )
    Initialize-DeltaCodeWorkflows
    $result = Invoke-DeltaCodeWorkflow -Name $Name -InputParams $InputParams -DryRun:$DryRun
    return $result
}

function Invoke-DeltaCodeWorkflowValidate {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    Initialize-DeltaCodeWorkflows
    $valid = Test-DeltaCodeWorkflow -Name $Name
    if ($valid) {
        Write-Host "Workflow '$Name' is valid" -ForegroundColor Green
        return $true
    }
    else {
        Write-Error "Workflow '$Name' validation failed"
        return $false
    }
}

function Invoke-DeltaCodeWorkflowHistory {
    [CmdletBinding()]
    param(
        [string]$Workflow,
        [string]$Workspace,
        [int]$Limit = 50
    )
    Initialize-DeltaCodeWorkflows
    $history = Get-DeltaCodeWorkflowHistory -Workflow $Workflow -Workspace $Workspace -Limit $Limit
    if (-not $history -or $history.Count -eq 0) {
        Write-Host "No execution history found"
        return
    }
    $history | Format-Table Started, Status, DurationMs, @{Name='Steps';Expression={($_.Steps | Measure-Object).Count}}
}

function Invoke-DeltaCodeWorkflowMetrics {
    [CmdletBinding()]
    param(
        [string]$Workflow,
        [string]$Workspace,
        [int]$Limit = 100
    )
    Initialize-DeltaCodeWorkflows
    $metrics = Get-DeltaCodeWorkflowMetrics -Workflow $Workflow -Workspace $Workspace -Limit $Limit
    $metrics | Format-List
}

function Invoke-DeltaCodeWorkflowRegister {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [Parameter(Mandatory = $true)]
        [string]$Workspace,
        [string[]]$Args = @()
    )
    Initialize-DeltaCodeWorkflows
    Initialize-DeltaCodeWorkspaces

    if ($Args.Count -lt 1) {
        Write-Error "workflow register requires <steps.json> file path"
        return
    }

    $stepsPath = $Args[0]
    if (-not (Test-Path $stepsPath)) {
        Write-Error "Steps file not found: $stepsPath"
        return
    }

    $steps = Get-Content $stepsPath -Raw | ConvertFrom-Json
    $meta = @{}
    foreach ($arg in $Args[1..($Args.Count - 1)]) {
        if ($arg -like '--*=*') {
            $key = $arg.Substring(2, $arg.IndexOf('=') - 2)
            $value = $arg.Substring($arg.IndexOf('=') + 1)
            $meta[$key] = $value
        }
    }

    $result = Register-DeltaCodeWorkflow -Name $Name -Workspace $Workspace -Steps $steps -Metadata $meta
    if ($result) {
        Write-Host "Workflow '$Name' registered for workspace '$Workspace' with $($steps.Count) steps"
    }
    else {
        Write-Error "Failed to register workflow: $Name"
    }
}

function Invoke-DeltaCodeProfileList {
    [CmdletBinding()]
    param()
    Initialize-DeltaCodeWorkflows
    $profiles = Get-AutomationProfiles
    if (-not $profiles -or $profiles.Count -eq 0) {
        Write-Host "No automation profiles registered"
        return
    }
    $profiles | Format-Table Name, Workflow, DryRun, RetryCount, ContinueOnError
}

function Invoke-DeltaCodeProfileShow {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    Initialize-DeltaCodeWorkflows
    $profile = Get-AutomationProfile -Name $Name
    if (-not $profile) {
        Write-Error "Automation profile '$Name' not found"
        return
    }
    $profile | Format-List
}

function Invoke-DeltaCodeProfileRun {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [switch]$DryRun
    )
    Initialize-DeltaCodeWorkflows
    $result = Invoke-DeltaCodeProfile -Name $Name -DryRun:$DryRun
    return $result
}

function Invoke-DeltaCodeProfileRegister {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [Parameter(Mandatory = $true)]
        [string]$Workflow,
        [string[]]$Args = @()
    )
    Initialize-DeltaCodeWorkflows
    $retry = 0
    $continueOnError = $false
    $dryRun = $false
    $inputParams = @{}

    foreach ($arg in $Args) {
        if ($arg -like '--retry=*') {
            $retry = [int]$arg.Substring('--retry='.Length)
        }
        elseif ($arg -eq '--continue-on-error') {
            $continueOnError = $true
        }
        elseif ($arg -eq '--dry-run') {
            $dryRun = $true
        }
        elseif ($arg -like '--param=*') {
            $kv = $arg.Substring('--param='.Length)
            if ($kv -like '*=*') {
                $key = $kv.Substring(0, $kv.IndexOf('='))
                $value = $kv.Substring($kv.IndexOf('=') + 1)
                $inputParams[$key] = $value
            }
        }
    }

    $result = Register-AutomationProfile -Name $Name -Workflow $Workflow -DryRun:$dryRun -RetryCount $retry -ContinueOnError:$continueOnError -InputParams $inputParams
    if ($result) {
        Write-Host "Automation profile '$Name' registered for workflow '$Workflow'"
    }
    else {
        Write-Error "Failed to register automation profile: $Name"
    }
}

function Show-DeltaCodeHealthUsage {
    [CmdletBinding()]
    param()
    Write-Host "Usage: delta-code health [options]"
    Write-Host ""
    Write-Host "Options:"
    Write-Host " --root=<path> Installation or tree to check (default: this installation)."
    Write-Host " --root <path> Space-separated form; use this for Windows paths."
    Write-Host " --json Emit the report as JSON."
    Write-Host " --skip-module-load Do not import each module during the check."
    Write-Host " --skip-cli Do not run the launcher smoke test."
    Write-Host ""
    Write-Host "Exits 0 when every check passes, 1 otherwise."
    Write-Host ""
}

function Invoke-DeltaCodeHealth {
    <#
    .SYNOPSIS
        Verifies a Delta-Code installation and returns the result object.
    .DESCRIPTION
        Delegates to the shared health module so the installer and the CLI can
        never validate differently. Returns the result object without printing;
        callers decide whether to render a table or JSON.
    #>

    [CmdletBinding()]
    param(
        [string]$Root,
        [switch]$SkipCli,
        [switch]$SkipModuleLoad
    )

    # The health module is the single authoritative verification implementation.
    # Import it lazily so that other commands do not pay for it at startup.
    $healthManifest = Join-Path $PSScriptRoot 'health.psd1'
    if (Test-Path -LiteralPath $healthManifest) {
        Import-Module $healthManifest -Force -DisableNameChecking
    }
    else {
        Import-Module 'health' -ErrorAction Stop -DisableNameChecking
    }

    # Default to the root that contains this module: <root>\modules\entry.psm1
    $targetRoot = Split-Path -Parent $PSScriptRoot
    if ($Root) {
        $targetRoot = $Root
    }

    return Test-DeltaCodeInstallation -RootPath $targetRoot -SkipCli:$SkipCli -SkipModuleLoad:$SkipModuleLoad
}

function Invoke-DeltaCodeEntry {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromRemainingArguments = $true)]
        [string[]]$Args
    )
    $parsed = Parse-DeltaCodeArgs -Args $Args

    # Help must win over "no command specified" so that `-h` exits 0.
    if ($parsed.ShowHelp) {
        Show-DeltaCodeUsage
        exit 0
    }
    if ($parsed.ShowDiffHelp) {
        Show-DeltaCodeDiffUsage
        exit 0
    }
    if ($parsed.Error) {
        Show-DeltaCodeUsage -ErrorMessage $parsed.Error
        exit 1
    }

    if ($parsed.Command -eq 'diff') {
        Invoke-DeltaCodeDiff -From $parsed.DiffFrom -To $parsed.DiffTo -IgnoreWhitespace $parsed.IgnoreWhitespace -Format $parsed.Format
        exit 0
    }
    elseif ($parsed.Command -eq 'satellite') {
        Invoke-DeltaCodeSatellite -Name $parsed.SatelliteName -Command $parsed.SatelliteCommand -Args $parsed.SatelliteArgs
        exit 0
    }
    elseif ($parsed.ListSatellites) {
        Invoke-DeltaCodeListSatellites
        exit 0
    }
    elseif ($parsed.Command -eq 'workspace') {
        switch ($parsed.WorkspaceSubCommand) {
            'list' { Invoke-DeltaCodeWorkspaceList; exit 0 }
            'show' { Invoke-DeltaCodeWorkspaceShow; exit 0 }
            'use' { Invoke-DeltaCodeWorkspaceUse -Name $parsed.WorkspaceName; exit 0 }
            'register' {
                $sat = if ($parsed.WorkspaceArgs.Count -gt 0) { $parsed.WorkspaceArgs[0] } else { $null }
                $metaArgs = if ($parsed.WorkspaceArgs.Count -gt 1) { $parsed.WorkspaceArgs[1..($parsed.WorkspaceArgs.Count - 1)] } else { @() }
                Invoke-DeltaCodeWorkspaceRegister -Name $parsed.WorkspaceName -Path $parsed.WorkspacePath -Satellite $sat -Args $metaArgs
                exit 0
            }
        }
    }
    elseif ($parsed.Command -eq 'workflow') {
        switch ($parsed.WorkflowSubCommand) {
            'list' { Invoke-DeltaCodeWorkflowList; exit 0 }
            'show' { Invoke-DeltaCodeWorkflowShow -Name $parsed.WorkflowName; exit 0 }
            'run' { Invoke-DeltaCodeWorkflowRun -Name $parsed.WorkflowName -DryRun:$parsed.WorkflowDryRun; exit 0 }
            'register' {
                Invoke-DeltaCodeWorkflowRegister -Name $parsed.WorkflowName -Workspace $parsed.WorkspaceName -Args $parsed.WorkflowArgs
                exit 0
            }
            'validate' { Invoke-DeltaCodeWorkflowValidate -Name $parsed.WorkflowName; exit 0 }
            'history' { Invoke-DeltaCodeWorkflowHistory -Workflow $parsed.WorkflowName -Workspace $parsed.WorkspaceName -Limit $parsed.WorkflowLimit; exit 0 }
            'metrics' { Invoke-DeltaCodeWorkflowMetrics -Workflow $parsed.WorkflowName -Workspace $parsed.WorkspaceName -Limit $parsed.WorkflowLimit; exit 0 }
        }
    }
    elseif ($parsed.Command -eq 'profile') {
        switch ($parsed.ProfileSubCommand) {
            'list' { Invoke-DeltaCodeProfileList; exit 0 }
            'show' { Invoke-DeltaCodeProfileShow -Name $parsed.ProfileName; exit 0 }
            'run' { Invoke-DeltaCodeProfileRun -Name $parsed.ProfileName -DryRun:$parsed.ProfileDryRun; exit 0 }
            'register' {
                Invoke-DeltaCodeProfileRegister -Name $parsed.ProfileName -Workflow $parsed.ProfileWorkflow -Args $parsed.ProfileArgs
                exit 0
            }
        }
    }

    elseif ($parsed.Command -eq 'health') {
        if ($parsed.HealthHelp) {
            Show-DeltaCodeHealthUsage
            exit 0
        }

        $result = Invoke-DeltaCodeHealth -Root $parsed.HealthRoot -SkipCli:$parsed.HealthSkipCli -SkipModuleLoad:$parsed.HealthSkipModuleLoad

        if ($parsed.HealthJson) {
            $result | ConvertTo-Json -Depth 6
        }
        else {
            Format-DeltaCodeHealthReport -Result $result
        }

        if ($result.Success) {
            exit 0
        }
        exit 1
    }

    # Forward to npm
    Invoke-DeltaCodeNpm -PrjDir $parsed.PrjDir -ForwardArgs $parsed.ForwardArgs
}

Export-ModuleMember -Function Show-DeltaCodeUsage, Show-DeltaCodeDiffUsage, Show-DeltaCodeHealthUsage, Parse-DeltaCodeArgs, Invoke-DeltaCodeDiff, Invoke-DeltaCodeNpm, Invoke-DeltaCodeHealth, Invoke-DeltaCodeEntry, Invoke-DeltaCodeSatellite, Invoke-DeltaCodeListSatellites, Invoke-DeltaCodeWorkspaceList, Invoke-DeltaCodeWorkspaceShow, Invoke-DeltaCodeWorkspaceUse, Invoke-DeltaCodeWorkspaceRegister, Invoke-DeltaCodeWorkflowList, Invoke-DeltaCodeWorkflowShow, Invoke-DeltaCodeWorkflowRun, Invoke-DeltaCodeWorkflowValidate, Invoke-DeltaCodeWorkflowHistory, Invoke-DeltaCodeWorkflowMetrics, Invoke-DeltaCodeWorkflowRegister, Invoke-DeltaCodeProfileList, Invoke-DeltaCodeProfileShow, Invoke-DeltaCodeProfileRun, Invoke-DeltaCodeProfileRegister