Private/Invoke-NativeCommandCapture.ps1

function Invoke-NativeCommandCapture {
    param(
        [Parameter(Mandatory)]
        [string]$FilePath,

        [string[]]$ArgumentList = @(),

        [string]$WorkingDirectory = (Get-Location).Path
    )

    # stdout/stderr are read straight off the redirected streams — no spool files.
    $psi = [System.Diagnostics.ProcessStartInfo]::new()
    $psi.FileName = $FilePath
    $psi.WorkingDirectory = $WorkingDirectory
    $psi.UseShellExecute = $false
    $psi.CreateNoWindow = $true
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError = $true
    if ($psi.PSObject.Properties.Name -contains 'StandardOutputEncoding') {
        $psi.StandardOutputEncoding = [System.Text.UTF8Encoding]::new($false)
    }
    if ($psi.PSObject.Properties.Name -contains 'StandardErrorEncoding') {
        $psi.StandardErrorEncoding = [System.Text.UTF8Encoding]::new($false)
    }
    foreach ($arg in $ArgumentList) {
        [void]$psi.ArgumentList.Add([string]$arg)
    }

    $process = [System.Diagnostics.Process]::new()
    $process.StartInfo = $psi

    if (-not $process.Start()) {
        throw "Failed to start native process: $FilePath"
    }

    $stdoutTask = $process.StandardOutput.ReadToEndAsync()
    $stderrTask = $process.StandardError.ReadToEndAsync()
    $process.WaitForExit()
    [System.Threading.Tasks.Task]::WaitAll(@([System.Threading.Tasks.Task]$stdoutTask, [System.Threading.Tasks.Task]$stderrTask))

    $stdoutText = $stdoutTask.Result
    $stderrText = $stderrTask.Result

    [pscustomobject]@{
        ExitCode    = $process.ExitCode
        StdOutLines = if ([string]::IsNullOrWhiteSpace($stdoutText)) { @() } else { @($stdoutText -split "`r?`n") | Where-Object { $_ -ne '' } }
        StdErrLines = if ([string]::IsNullOrWhiteSpace($stderrText)) { @() } else { @($stderrText -split "`r?`n") | Where-Object { $_ -ne '' } }
    }
}