Private/Remove-Win32ToolkitPathWithRetry.ps1

function Remove-Win32ToolkitPathWithRetry {
    <#
    .SYNOPSIS
        Recursively removes a path, retrying with back-off to ride out transient file locks.
    .DESCRIPTION
        Right after a bulk copy, Windows Defender (real-time scan) or the search indexer briefly hold open
        freshly-written files — notably PSADT .psm1 modules — so an immediate Remove-Item -Recurse fails
        with 'being used by another process' / 'The directory is not empty'. Retrying with a short back-off
        (400 ms doubling to a 3 s cap) almost always clears it.
 
        Returns $true when the path is gone (including when it never existed), $false if it still could not
        be removed after every retry. Progress is silenced for the duration so a large recursive delete
        never paints a progress bar over an interactive (Spectre) TUI.
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [ValidateRange(1, 20)]
        [int]$Retries = 5,

        [ValidateRange(0, 60000)]
        [int]$InitialDelayMs = 400
    )

    if (-not (Test-Path -LiteralPath $Path)) { return $true }

    $prevProgress = $ProgressPreference
    $ProgressPreference = 'SilentlyContinue'
    try {
        $delay = $InitialDelayMs
        for ($attempt = 1; $attempt -le $Retries; $attempt++) {
            try {
                Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop
                return $true
            }
            catch {
                # A partial delete may have already removed it (the error is about a now-gone child).
                if (-not (Test-Path -LiteralPath $Path)) { return $true }
                if ($attempt -eq $Retries) {
                    Write-Warning "Could not remove '$Path' after $Retries attempts (a file is locked — antivirus scan or an open handle): $($_.Exception.Message)"
                    return $false
                }
                Start-Sleep -Milliseconds $delay
                $delay = [Math]::Min($delay * 2, 3000)
            }
        }
        return $false
    }
    finally { $ProgressPreference = $prevProgress }
}