src/public/System/Set-AitherDefenderExclusions.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
    Configure Windows Defender exclusions for development performance

.DESCRIPTION
    Adds Windows Defender exclusions to improve development performance by preventing
    real-time scanning of source code, build artifacts, and development tools.
    
    This function configures three types of exclusions:
    - Path Exclusions: Folders excluded from real-time scanning
    - Process Exclusions: Executables excluded from behavior monitoring
    - Extension Exclusions: File types excluded from scanning

    Configuration is read from config.windows.psd1 under Windows.DefenderExclusions
    or uses sensible defaults for common development scenarios.

.PARAMETER Force
    Apply exclusions even if AutoApply is disabled in config. Also skips confirmation.

.PARAMETER Remove
    Removes the configured exclusions instead of adding them.

.PARAMETER ShowOutput
    Display detailed output during execution.

.EXAMPLE
    Set-AitherDefenderExclusions
    
    Apply all configured Defender exclusions with confirmation.

.EXAMPLE
    Set-AitherDefenderExclusions -Force
    
    Apply exclusions without confirmation prompt.

.EXAMPLE
    Set-AitherDefenderExclusions -Remove
    
    Remove the configured exclusions.

.OUTPUTS
    PSCustomObject with summary of changes made

.NOTES
    Requires: Windows 10/11, Administrator privileges
    Impact: Reduces security coverage for excluded paths - use judiciously
    
    This significantly improves IDE responsiveness, build times, and general
    development performance when working with AitherZero projects.

.LINK
    Get-AitherDefenderExclusions
    Enable-AitherWindowsLongPath
#>

function Set-AitherDefenderExclusions {
    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter()]
        [switch]$Force,

        [Parameter()]
        [switch]$Remove,

        [Parameter(HelpMessage = "Show command output in console.")]
        [switch]$ShowOutput
    )

    begin {
        # Manage logging targets for this execution
        $originalLogTargets = $script:AitherLogTargets
        if ($ShowOutput) {
            if ($script:AitherLogTargets -notcontains 'Console') {
                $script:AitherLogTargets += 'Console'
            }
        }
    }

    process {
        try {
            # During module validation, skip execution
            if ($PSCmdlet.MyInvocation.InvocationName -eq '.') {
                return $null
            }

            # Check platform
            if (-not ($IsWindows -or $PSVersionTable.Platform -eq 'Win32NT')) {
                Write-AitherLog -Message "Defender exclusions are only applicable to Windows" -Level Warning -Source 'Set-AitherDefenderExclusions'
                return $null
            }

            # Check for admin rights
            $isAdmin = if (Get-Command Test-AitherAdmin -ErrorAction SilentlyContinue) {
                Test-AitherAdmin
            } else {
                $currentPrincipal = [Security.Principal.WindowsPrincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent())
                $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
            }

            if (-not $isAdmin) {
                Write-AitherLog -Message "Administrator privileges required to configure Defender exclusions" -Level Error -Source 'Set-AitherDefenderExclusions'
                Write-AitherLog -Message "To run as Administrator: Start-Process pwsh -Verb RunAs -ArgumentList '-Command', 'Set-AitherDefenderExclusions'" -Level Information -Source 'Set-AitherDefenderExclusions'
                return $null
            }

            # Check if Defender is available
            try {
                $defenderStatus = Get-MpComputerStatus -ErrorAction Stop
                if (-not $defenderStatus.AntivirusEnabled) {
                    Write-AitherLog -Message "Windows Defender is not enabled" -Level Warning -Source 'Set-AitherDefenderExclusions'
                    return $null
                }
            } catch {
                Write-AitherLog -Message "Windows Defender not available: $_" -Level Warning -Source 'Set-AitherDefenderExclusions'
                return $null
            }

            # Find and run the automation script
            $scriptPath = $null
            $moduleRoot = if (Get-Command Get-AitherModuleRoot -ErrorAction SilentlyContinue) {
                Get-AitherModuleRoot
            } else {
                $PSScriptRoot
            }

            # Look for the script
            $possiblePaths = @(
                (Join-Path $moduleRoot 'library\automation-scripts\0101_Configure-DefenderExclusions.ps1')
                (Join-Path (Split-Path -Parent $moduleRoot) 'library\automation-scripts\0101_Configure-DefenderExclusions.ps1')
            )

            foreach ($path in $possiblePaths) {
                if (Test-Path $path) {
                    $scriptPath = $path
                    break
                }
            }

            if ($scriptPath -and (Test-Path $scriptPath)) {
                # Use the automation script
                $params = @{}
                if ($Force) { $params['Force'] = $true }
                if ($Remove) { $params['Remove'] = $true }
                
                & $scriptPath @params
            } else {
                # Inline implementation for when script is not found
                Write-AitherLog -Message "Running inline Defender exclusion configuration" -Level Debug -Source 'Set-AitherDefenderExclusions'
                
                $workspaceRoot = $env:AITHERZERO_ROOT
                if (-not $workspaceRoot) {
                    $workspaceRoot = if (Get-Command Get-AitherProjectRoot -ErrorAction SilentlyContinue) {
                        Get-AitherProjectRoot
                    } else {
                        Split-Path -Parent $moduleRoot
                    }
                }

                # Get current exclusions
                $prefs = Get-MpPreference
                $currentPaths = @($prefs.ExclusionPath | Where-Object { $_ })
                $currentProcesses = @($prefs.ExclusionProcess | Where-Object { $_ })
                $currentExtensions = @($prefs.ExclusionExtension | Where-Object { $_ })

                # Default exclusions
                $pathsToAdd = @($workspaceRoot)
                $processesToAdd = @(
                    'python.exe', 'pythonw.exe', 'node.exe', 'git.exe',
                    'pwsh.exe', 'code.exe', 'cursor.exe', 'docker.exe',
                    'cargo.exe', 'go.exe', 'ollama.exe'
                )
                $extensionsToAdd = @('pyc', 'pyo', 'obj', 'o', 'lock', 'lockb')

                # Add project-relative paths
                $relativeExclusions = @('.venv', 'venv', 'node_modules', '.git', '__pycache__', 'build', 'dist')
                foreach ($rel in $relativeExclusions) {
                    $fullPath = Join-Path $workspaceRoot $rel
                    if (Test-Path $fullPath) {
                        $pathsToAdd += $fullPath
                    }
                }

                $stats = @{
                    PathsAdded = 0
                    ProcessesAdded = 0
                    ExtensionsAdded = 0
                }

                $action = if ($Remove) { "Remove" } else { "Add" }

                if (-not $Force -and -not $Remove) {
                    Write-AitherLog -Level Information -Message "═══════════════════════════════════════════════════════════════" -Source 'Set-AitherDefenderExclusions'
                    Write-AitherLog -Level Information -Message " WINDOWS DEFENDER EXCLUSIONS" -Source 'Set-AitherDefenderExclusions'
                    Write-AitherLog -Level Information -Message "═══════════════════════════════════════════════════════════════" -Source 'Set-AitherDefenderExclusions'
                    Write-AitherLog -Level Information -Message " This will add exclusions to improve development performance." -Source 'Set-AitherDefenderExclusions'
                    Write-AitherLog -Level Information -Message " Paths to exclude:" -Source 'Set-AitherDefenderExclusions'
                    $pathsToAdd | ForEach-Object { Write-AitherLog -Level Information -Message " • $_" -Source 'Set-AitherDefenderExclusions' }
                    
                    $confirm = Read-Host "Apply these exclusions? (y/N)"
                    if ($confirm -ne 'y') {
                        Write-AitherLog -Level Information -Message "Operation cancelled." -Source 'Set-AitherDefenderExclusions'
                        return $null
                    }
                }

                # Apply exclusions
                foreach ($path in $pathsToAdd) {
                    if ($currentPaths -notcontains $path) {
                        try {
                            if ($Remove) {
                                Remove-MpPreference -ExclusionPath $path -ErrorAction Stop
                            } else {
                                Add-MpPreference -ExclusionPath $path -ErrorAction Stop
                            }
                            $stats.PathsAdded++
                            Write-AitherLog -Level Information -Message " ✓ ${action}ed path: $path" -Source 'Set-AitherDefenderExclusions'
                        } catch {
                            Write-AitherLog -Level Error -Message " ✗ Failed to $action path: $path - $_" -Source 'Set-AitherDefenderExclusions' -Exception $_
                        }
                    }
                }

                foreach ($proc in $processesToAdd) {
                    if ($currentProcesses -notcontains $proc) {
                        try {
                            if ($Remove) {
                                Remove-MpPreference -ExclusionProcess $proc -ErrorAction Stop
                            } else {
                                Add-MpPreference -ExclusionProcess $proc -ErrorAction Stop
                            }
                            $stats.ProcessesAdded++
                        } catch {
                            # Silently continue for processes
                        }
                    }
                }

                foreach ($ext in $extensionsToAdd) {
                    if ($currentExtensions -notcontains $ext) {
                        try {
                            if ($Remove) {
                                Remove-MpPreference -ExclusionExtension $ext -ErrorAction Stop
                            } else {
                                Add-MpPreference -ExclusionExtension $ext -ErrorAction Stop
                            }
                            $stats.ExtensionsAdded++
                        } catch {
                            # Silently continue for extensions
                        }
                    }
                }

                Write-AitherLog -Level Information -Message " Summary: $($stats.PathsAdded) paths, $($stats.ProcessesAdded) processes, $($stats.ExtensionsAdded) extensions ${action}ed" -Source 'Set-AitherDefenderExclusions'
                Write-AitherLog -Level Information -Message "═══════════════════════════════════════════════════════════════" -Source 'Set-AitherDefenderExclusions'

                return [PSCustomObject]@{
                    PSTypeName = 'AitherZero.DefenderExclusionResult'
                    Success = $true
                    PathsChanged = $stats.PathsAdded
                    ProcessesChanged = $stats.ProcessesAdded
                    ExtensionsChanged = $stats.ExtensionsAdded
                    Action = $action
                }
            }
        }
        catch {
            Write-AitherLog -Message "Failed to configure Defender exclusions: $_" -Level Error -Source 'Set-AitherDefenderExclusions' -Exception $_
            throw
        }
        finally {
            # Restore original log targets
            $script:AitherLogTargets = $originalLogTargets
        }
    }
}