Functions/GenXdev.Coding.PowerShell.Modules/Update-Refactor.ps1

################################################################################
<#
.SYNOPSIS
Updates and manages refactoring sets including file selection and processing.
 
.DESCRIPTION
Provides comprehensive management of refactoring sets by:
- Adding or removing files from processing queues
- Cleaning up deleted files from the set
- Managing state information and progress tracking
- Handling LLM-based file selection and processing
- Supporting both automatic and manual file management
- Maintaining detailed logs of all operations
 
.PARAMETER Name
Names of refactor sets to update, accepts wildcards. Default is "*".
 
.PARAMETER Refactor
Direct input of refactor set objects instead of loading by name.
 
.PARAMETER FilesToAdd
Files to add to the processing queue.
 
.PARAMETER FilesToRemove
Files to remove from the processing queue.
 
.PARAMETER CleanUpDeletedFiles
Remove entries for files that no longer exist on disk.
 
.PARAMETER AskBeforeLLMSelection
Prompt before launching LLM invocations for file selections.
 
.PARAMETER PerformLLMSelections
Enable LLM-based file selection processing.
 
.PARAMETER PerformAllLLMSelections
Process all files in the refactor set with LLM.
 
.PARAMETER RetryFailedLLMSelections
Retry previously failed LLM selections.
 
.PARAMETER Reset
Start processing from beginning of refactor set.
 
.PARAMETER Clear
Remove all files from the refactor set.
 
.PARAMETER ClearLog
Clear the refactor set's operation log.
 
.PARAMETER ResetLMSelections
Restart all LLM selections from beginning.
 
.PARAMETER MarkAllCompleted
Mark all files as successfully refactored.
 
.PARAMETER SelectByModifiedDateFrom
Select files modified on or after this date.
 
.PARAMETER SelectByModifiedDateTo
Select files modified on or before this date.
 
.PARAMETER SelectByCreationDateFrom
Select files created on or after this date.
 
.PARAMETER SelectByCreationDateTo
Select files created on or before this date.
 
.PARAMETER RedoLast
Reprocess the last refactoring operation.
 
.PARAMETER PromptKey
Key identifying which prompt script to use.
 
.PARAMETER Prompt
Direct prompt text to use for processing.
 
.PARAMETER SelectionScript
PowerShell script for file selection logic.
 
.PARAMETER AutoAddModifiedFiles
Automatically add modified files to processing queue.
 
.PARAMETER SelectionPrompt
Content for LLM-based selection prompts.
 
.PARAMETER Model
LLM model identifier for LM-Studio.
 
.PARAMETER ModelLMSGetIdentifier
Model retrieval identifier for LM-Studio.
 
.PARAMETER Temperature
Temperature setting for response randomness (0.0-1.0).
 
.PARAMETER MaxToken
Maximum tokens allowed in responses (-1 for default).
 
.PARAMETER TTLSeconds
Time-to-live in seconds for API model requests.
 
.PARAMETER Gpu
GPU processing control (-2=Auto, -1=LM-Studio default).
 
.PARAMETER Force
Force stop LM-Studio before initialization.
 
.PARAMETER ApiEndpoint
API endpoint URL for LLM service.
 
.PARAMETER ApiKey
Authentication key for API access.
 
.PARAMETER Priority
Processing priority level for this refactor set.
 
.PARAMETER ExposedCmdlets
PowerShell commands available during LLM selection.
 
.PARAMETER Code
Open file in Visual Studio Code.
 
.PARAMETER VisualStudio
Open file in Visual Studio.
 
.PARAMETER KeysToSend
Keystrokes to send after opening file.
 
.PARAMETER Speak
Enable text-to-speech for operation details.
 
.EXAMPLE
Update-Refactor -Name "CodeCleanup" -FilesToAdd ".\src\*.cs" `
    -CleanUpDeletedFiles -PerformLLMSelections
 
.EXAMPLE
Get-Refactor "MyRefactor" | Update-Refactor -Reset -Clear
#>

function Update-Refactor {

    [CmdletBinding(DefaultParameterSetName = 'Name', SupportsShouldProcess)]
    [Alias("updaterefactor")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidUsingInvokeExpression',
        '',
        Justification = 'Required for dynamic script execution in refactoring context')]
    param (
        ########################################################################
        [Parameter(
            ParameterSetName = 'Name',
            Mandatory = $false,
            Position = 0,
            HelpMessage = "The name of the refactor, accepts wildcards",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string[]] $Name = @("*"),
        ########################################################################
        [Parameter(
            ParameterSetName = 'Refactor',
            Mandatory = $false,
            Position = 0,
            HelpMessage = "The refactor set to update")]
        [ValidateNotNull()]
        [GenXdev.Helpers.RefactorDefinition[]]$Refactor,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            Position = 1,
            HelpMessage = "Filenames to add")]
        [ValidateNotNull()]
        [System.IO.FileInfo[]] $FilesToAdd = @(),
        ########################################################################
        [Parameter(
            Mandatory = $false,
            Position = 2,
            HelpMessage = "Filenames to remove")]
        [ValidateNotNull()]
        [System.IO.FileInfo[]] $FilesToRemove = @(),
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Clean up deleted files")]
        [switch] $CleanUpDeletedFiles,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Switch to suppress user interaction")]
        [switch] $AskBeforeLLMSelection,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Switch to enable LLM-based file selection processing")]
        [switch] $PerformLLMSelections,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Switch to process all files in the refactor set")]
        [switch] $PerformAllLLMSelections,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Switch to retry failed LLM selections")]
        [switch] $RetryFailedLLMSelections,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Clear all files from the refactor set")]
        [switch] $Clear,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Clear the log of the refactor set")]
        [switch] $ClearLog,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Start from the beginning of the refactor set")]
        [switch] $Reset,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Restart all LLMSelections")]
        [switch] $ResetLMSelections,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Mark all files as refactored")]
        [switch] $MarkAllCompleted,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Select files by modified date from")]
        [ValidateNotNullOrEmpty()]
        [datetime] $SelectByModifiedDateFrom,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Select files by modified date to")]
        [ValidateNotNullOrEmpty()]
        [datetime] $SelectByModifiedDateTo,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Select files by creation date from")]
        [ValidateNotNullOrEmpty()]
        [datetime] $SelectByCreationDateFrom,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Select files by creation date to")]
        [ValidateNotNullOrEmpty()]
        [datetime] $SelectByCreationDateTo,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Redo the last refactor")]
        [switch] $RedoLast,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The prompt key indicates which prompt script to use")]
        [ValidateNotNullOrEmpty()]
        [string] $PromptKey,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The prompt key indicates which prompt script to use")]
        [ValidateNotNullOrEmpty()]
        [string] $Prompt = "",
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Powershell script for function to select items to " +
            "refactor")]
        [ValidateNotNullOrEmpty()]
        [string] $SelectionScript,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Will automatically add modified files to the queue")]
        [switch] $AutoAddModifiedFiles,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "If provided, will invoke LLM to do the selection " +
            "based on the content of the script")]
        [ValidateNotNullOrEmpty()]
        [string] $SelectionPrompt,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The LM-Studio model to use")]
        [SupportsWildcards()]
        [string] $Model,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Identifier for getting specific model from LM Studio")]
        [string] $ModelLMSGetIdentifier,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Temperature for response randomness (0.0-1.0)")]
        [ValidateRange(0.0, 1.0)]
        [double] $Temperature = 0.2,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Maximum tokens in response (-1 for default)")]
        [Alias("MaxTokens")]
        [int] $MaxToken = -1,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Set a TTL (in seconds) for models via API requests")]
        [Alias("ttl")]
        [int] $TTLSeconds = -1,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "GPU offloading control (-2=Auto, -1=LMStudio decide)")]
        [int] $Gpu = -1,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Force stop LM Studio before initialization")]
        [switch] $Force,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Api endpoint url for LLM service")]
        [string] $ApiEndpoint,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The API key to use for the request")]
        [string] $ApiKey,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Priority for this refactor set")]
        [ValidateNotNullOrEmpty()]
        [int] $Priority,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Array of PowerShell command definitions to use as tools " +
            "during LLM selection")]
        [GenXdev.Helpers.ExposedCmdletDefinition[]]
        $ExposedCmdLets = @(),
        #######################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The ide to open the file in")]
        [Alias("c")]
        [switch] $Code,
        #######################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in Visual Studio")]
        [Alias("vs")]
        [switch] $VisualStudio,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The keys to invoke as key strokes after opening the file")]
        [Alias("keys")]
        [string[]] $KeysToSend = @(),
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Speak out the details of next refactor")]
        [switch] $Speak
        ########################################################################
    )

    begin {

        $modulesPath = GenXdev.FileSystem\Expand-Path "$PSScriptRoot\..\..\..\..\"

        # log start of operation
        Microsoft.PowerShell.Utility\Write-Verbose "Starting Update-Refactor operation"

        # load refactor set by name if not provided directly
        if ($PSCmdlet.ParameterSetName -eq 'Name') {
            $Refactor = @(
                GenXdev.Coding\Get-Refactor -Name $Name |
                Microsoft.PowerShell.Utility\Sort-Object -Property Priority -Descending
            )
        }

        # exit if no refactor set found
        if ($null -eq $Refactor -or $Refactor.Count -eq 0) {

            Microsoft.PowerShell.Utility\Write-Warning "No refactorset found"
            return
        }

        # initialize tracking variables for file operations
        $now = GenXdev.Console\UtcNow
        $script:filesAdded = 0
        $script:filesRemoved = 0
        $script:onlyFirst = -not $PerformAllLLMSelections
        $script:last = $null;
    }


process {

        # process each refactor definition
        foreach ($refactorDefinition in $Refactor) {

            # check containers
            if ($Clear -or ($null -eq $refactorDefinition.State.Unselected)) {

                $refactorDefinition.State.Unselected = @()
            }

            if ($Clear -or ($null -eq $refactorDefinition.State.Selected)) {

                $refactorDefinition.State.Selected = @()
            }

            if ($Clear -or ($null -eq $refactorDefinition.State.Refactored)) {

                $refactorDefinition.State.Refactored = @()
            }

            if ($null -eq $refactorDefinition.Log) {

                $refactorDefinitionrefactorDefinition.Log = @()
            }

            if ($ClearLog) {

                $null = $refactorDefinition.Log.Clear()
                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "Log cleared"
                    }
                )
            }

            if ($Clear) {

                $refactorDefinition.State.RefactoredIndex = -1
                $refactorDefinition.State.SelectedIndex = -1
                $refactorDefinition.State.UnselectedIndex = -1
                $refactorDefinition.State.PercentageComplete = 0
                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "Refactor set cleared"
                    }
                )
            }

            # update prompt key if specified
            if ($PSBoundParameters.ContainsKey('PromptKey')) {
                if ($refactorDefinition.RefactorSettings.PromptKey -ne $PromptKey) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Prompt key changed from '$($refactorDefinition.RefactorSettings.PromptKey))' to '$PromptKey'"
                        }
                    )

                    $refactorDefinition.RefactorSettings.PromptKey = $PromptKey
                }
            }
            if ($PSBoundParameters.ContainsKey('Prompt')) {

                if ($refactorDefinition.RefactorSettings.Prompt -ne $Prompt) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Prompt changed from '$($refactorDefinition.RefactorSettings.Prompt))' to '$Prompt'"
                        }
                    )
                    $refactorDefinition.RefactorSettings.Prompt = $Prompt
                }
            }
            if ($PSBoundParameters.ContainsKey('SelectionScript')) {

                if ($refactorDefinition.SelectionSettings.Script -ne $SelectionScript) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Selection script changed from '$($refactorDefinition.SelectionSettings.SelectionScript))' to '$SelectionScript'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.Script = $SelectionScript
                }
            }
            if ($PSBoundParameters.ContainsKey('AutoAddModifiedFiles')) {

                if ($refactorDefinition.SelectionSettings.AutoAddModifiedFiles -ne $AutoAddModifiedFiles) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Selection script changed from '$($refactorDefinition.SelectionSettings.AutoAddModifiedFiles))' to '$AutoAddModifiedFiles'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.AutoAddModifiedFiles = $AutoAddModifiedFiles
                }
            }
            if ($PSBoundParameters.ContainsKey('SelectionPrompt')) {

                if ($refactorDefinition.SelectionSettings.LLM.Prompt -ne $SelectionPrompt) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Selection prompt changed from '$($refactorDefinition.SelectionSettings.LLM.Prompt))' to '$SelectionPrompt'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.Prompt = $SelectionPrompt
                }
            }
            if ($PSBoundParameters.ContainsKey('Model')) {

                if ($refactorDefinition.SelectionSettings.LLM.Model -ne $Model) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Model changed from '$($refactorDefinition.SelectionSettings.LLM.Model))' to '$Model'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.Model = $Model
                }
            }
            if ($PSBoundParameters.ContainsKey('ModelLMSGetIdentifier')) {

                if ($refactorDefinition.SelectionSettings.LLM.ModelLMSGetIdentifier -ne $ModelLMSGetIdentifier) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "ModelLMSGetIdentifier changed from '$($refactorDefinition.SelectionSettings.LLM.ModelLMSGetIdentifier))' to '$ModelLMSGetIdentifier'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.ModelLMSGetIdentifier = $ModelLMSGetIdentifier
                }
            }
            if ($PSBoundParameters.ContainsKey('Temperature')) {

                if ($refactorDefinition.SelectionSettings.LLM.Temperature -ne $Temperature) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Temperature changed from '$($refactorDefinition.SelectionSettings.LLM.Temperature))' to '$Temperature'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.Temperature = $Temperature
                }
            }
            if ($PSBoundParameters.ContainsKey('MaxToken')) {

                if ($refactorDefinition.SelectionSettings.LLM.MaxToken -ne $MaxToken) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "MaxToken changed from '$($refactorDefinition.SelectionSettings.LLM.MaxToken))' to '$MaxToken'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.MaxToken = $MaxToken
                }
            }
            if ($PSBoundParameters.ContainsKey('TTLSeconds')) {

                if ($refactorDefinition.SelectionSettings.LLM.TTLSeconds -ne $TTLSeconds) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "TTLSeconds changed from '$($refactorDefinition.SelectionSettings.LLM.TTLSeconds))' to '$TTLSeconds'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.TTLSeconds = $TTLSeconds
                }
            }
            if ($PSBoundParameters.ContainsKey('Gpu')) {

                if ($refactorDefinition.SelectionSettings.LLM.Gpu -ne $Gpu) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Gpu changed from '$($refactorDefinition.SelectionSettings.LLM.Gpu))' to '$Gpu'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.Gpu = $Gpu
                }
            }
            if ($PSBoundParameters.ContainsKey('Force')) {

                if ($refactorDefinition.SelectionSettings.LLM.Force -ne $Force) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Force changed from '$($refactorDefinition.SelectionSettings.LLM.Force))' to '$Force'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.Force = $Force
                }
            }
            if ($PSBoundParameters.ContainsKey('ApiEndpoint')) {

                if ($refactorDefinition.SelectionSettings.LLM.ApiEndpoint -ne $ApiEndpoint) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "ApiEndpoint changed from '$($refactorDefinition.SelectionSettings.LLM.ApiEndpoint))' to '$ApiEndpoint'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.ApiEndpoint = $ApiEndpoint
                }
            }
            if ($PSBoundParameters.ContainsKey('ApiKey')) {

                if ($refactorDefinition.SelectionSettings.LLM.ApiKey -ne $ApiKey) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "ApiKey changed from '$($refactorDefinition.SelectionSettings.LLM.ApiKey))' to '$ApiKey'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.ApiKey = $ApiKey
                }
            }
            if ($PSBoundParameters.ContainsKey('ExposedCmdLets')) {

                if ($refactorDefinition.SelectionSettings.LLM.ExposedCmdLets -ne $ExposedCmdLets) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "ExposedCmdLets changed from '$($refactorDefinition.SelectionSettings.LLM.ExposedCmdLets))' to '$ExposedCmdLets'"
                        }
                    )
                    $refactorDefinition.SelectionSettings.LLM.ExposedCmdLets = $ExposedCmdLets
                }
            }
            if ($PSBoundParameters.ContainsKey("Priority")) {

                if ($refactorDefinition.Priority -ne $Priority) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Priority changed from '$($refactorDefinition.Priority))' to '$Priority'"
                        }
                    )
                    $refactorDefinition.Priority = $Priority
                }
            }
            if ($PSBoundParameters.ContainsKey('KeysToSend')) {

                if ($refactorDefinition.RefactorSettings.KeysToSend -ne $KeysToSend) {

                    $null = $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "KeysToSend changed from '$($refactorDefinition.RefactorSettings.KeysToSend))' to '$KeysToSend'"
                        }
                    )
                    $refactorDefinition.RefactorSettings.KeysToSend = $KeysToSend
                }
            }
            $newCode = $PSBoundParameters.ContainsKey("Code") ? ($Code ? 1 : 0) : -1;
            if ($refactorDefinition.RefactorSettings.Code -ne $newCode) {

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "Code changed from '$($refactorDefinition.RefactorSettings.Code))' to '$newCode'"
                    }
                )
                $refactorDefinition.RefactorSettings.Code = $newCode
            }

            $newVisualStudio = $PSBoundParameters.ContainsKey("VisualStudio") ? ($VisualStudio ? 1 : 0) : -1;
            if ($refactorDefinition.RefactorSettings.VisualStudio -ne $newVisualStudio) {

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "VisualStudio changed from '$($refactorDefinition.RefactorSettings.VisualStudio))' to '$newVisualStudio'"
                    }
                )

                $refactorDefinition.RefactorSettings.VisualStudio = $newVisualStudio
            }

            if ($null -ne $SelectByModifiedDateFrom) {

                # copy all refactored files to FilesToAdd
                $FilesToAdd += @(
                    $refactorDefinition.State.Refactored | Microsoft.PowerShell.Core\ForEach-Object {

                        if ([string]::IsNullOrWhiteSpace($_)) { return }
                        $fi = [System.IO.FileInfo]::new($_);

                        if ($fi.Exists -and $fi.LastWriteTime -ge $SelectByModifiedDateFrom) {

                            $fi
                        }
                    }
                );

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "New file selection made: Redo all files modifief from date '$SelectByModifiedDateFrom'"
                    }
                )
            }

            if ($null -ne $SelectByModifiedDateTo) {

                # copy all refactored files to FilesToAdd
                $FilesToAdd += @(
                    $refactorDefinition.State.Refactored | Microsoft.PowerShell.Core\ForEach-Object {

                        if ([string]::IsNullOrWhiteSpace($_)) { return }

                        $fi = [System.IO.FileInfo]::new($_);

                        if ($fi.Exists -and $fi.LastWriteTime -lt $SelectByModifiedDateFrom) {

                            $fi
                        }
                    }
                );

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "New file selection made: Redo all files modifief to date '$SelectByModifiedDateTo'"
                    }
                )
            }

            if ($null -ne $SelectByCreationDateFrom) {

                # copy all refactored files to FilesToAdd
                $FilesToAdd += @(
                    $refactorDefinition.State.Refactored | Microsoft.PowerShell.Core\ForEach-Object {

                        if ([string]::IsNullOrWhiteSpace($_)) { return }

                        $fi = [System.IO.FileInfo]::new($_);

                        if ($fi.Exists -and $fi.CreationTime -ge $SelectByModifiedDateFrom) {

                            $fi
                        }
                    }
                );

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "New file selection made: Redo all files created from date '$SelectByCreationDateFrom'"
                    }
                )
            }

            if ($null -ne $SelectByCreationDateTo) {

                # copy all refactored files to FilesToAdd
                $FilesToAdd += @(
                    $refactorDefinition.State.Refactored | Microsoft.PowerShell.Core\ForEach-Object {

                        if ([string]::IsNullOrWhiteSpace($_)) { return }

                        $fi = [System.IO.FileInfo]::new($_);

                        if ($fi.Exists -and $fi.CreationTime -lt $SelectByModifiedDateFrom) {

                            $fi
                        }
                    }
                );

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "New file selection made: Redo all files created to date '$SelectByCreationDateTo'"
                    }
                )
            }

            if ($RedoLast -and $refactorDefinition.State.Refactored.Count -gt 0) {

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "Redoing last refactor"
                    }
                )

                $refactorDefinition.State.RefactoredIndex = [Math]::Max(-1,
                    $refactorDefinition.State.RefactoredIndex - 1)
            }
            elseif ($Reset) {

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "Resetting refactor set"
                    }
                )
                $refactorDefinition.State.RefactoredIndex = -1
                $refactorDefinition.State.Selected = @(@(
                        $refactorDefinition.State.Selected +
                        $refactorDefinition.State.Refactored
                    ) | Microsoft.PowerShell.Utility\Select-Object -Unique)
                $null = $refactorDefinition.State.Refactored.Clear();
            }

            if ($MarkAllCompleted) {

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "Marking all files as refactored"
                    }
                )

                $refactorDefinition.State.Refactored = @(@(
                        $refactorDefinition.State.Refactored +
                        $refactorDefinition.State.Selected
                    ) | Microsoft.PowerShell.Utility\Select-Object -Unique)
                $null = $refactorDefinition.State.Selected.Clear();
                $refactorDefinition.State.SelectedIndex = $refactorDefinition.State.Selected.Count - 1;
                $refactorDefinition.State.RefactoredIndex = $refactorDefinition.State.Refactored.Count - 1;
                $refactorDefinition.State.PercentageComplete = 100;
            }

            if ($ResetLMSelections) {

                $null = $refactorDefinition.Log.Add(
                    [GenXdev.Helpers.RefactorLogItem]@{
                        Timestamp = $now
                        Message   = "Resetting LLM selections"
                    }
                )

                $refactorDefinition.State.SelectedIndex = -1;
                $refactorDefinition.State.Unselected = @(@(
                        $refactorDefinition.State.Unselected +
                        $refactorDefinition.State.Selected
                    ) | Microsoft.PowerShell.Utility\Select-Object -Unique)
                $null = $refactorDefinition.State.Selected.Clear();
            }

            Microsoft.PowerShell.Utility\Write-Verbose "Processing refactor definition: $($refactorDefinition.Name)"

            # execute selection script to get automatically selected files
            [System.IO.FileInfo[]] $automaticFiles = ($null = @(
                    if (-not [string]::IsNullOrWhiteSpace(
                            $refactorDefinition.SelectionSettings.Script)) {

                        Microsoft.PowerShell.Utility\Write-Verbose "Executing selection script"
                        if (-not $Clear) {

                            Microsoft.PowerShell.Utility\Invoke-Expression -Command $refactorDefinition.SelectionSettings.Script
                        }
                    }
                ) + @($FilesToAdd | Microsoft.PowerShell.Core\ForEach-Object {

                        if ($null -ne $_) {

                            $item = Microsoft.PowerShell.Management\Get-Item -Path (GenXdev.FileSystem\Expand-Path $_) -ErrorAction SilentlyContinue
                            if ($null -ne $item) {

                                $item
                            }
                        }
                    })) |
            Microsoft.PowerShell.Utility\Sort-Object -Property FullName -Unique |
            Microsoft.PowerShell.Utility\Sort-Object -Property LastWriteTimeUtc;

            if ($null -ne $automaticFiles) {

                # process new files to be added
                @($automaticFiles) | Microsoft.PowerShell.Core\ForEach-Object {

                    if ($null -eq $_ ) { return }

                    # check if file exists in any collection
                    $indexRefactored = $refactorDefinition.State.Refactored.IndexOf($_)
                    $indexSelected = $refactorDefinition.State.Selected.IndexOf($_)
                    $indexUnselected = $refactorDefinition.State.Unselected.IndexOf($_)

                    # add file if not already present
                    if ($indexRefactored -lt 0 -and $indexSelected -lt 0 -and
                        $indexUnselected -lt 0) {

                        # add to selected if no LLM prompt, otherwise to unselected
                        if ([string]::IsNullOrWhiteSpace(
                                $refactorDefinition.SelectionSettings.LLM.Prompt)) {

                            $null = $refactorDefinition.State.Selected.Add($_)
                        }
                        else {

                            $null = $refactorDefinition.State.Unselected.Add($_)
                        }

                        $script:filesAdded++
                        return;
                    }

                    if ((($null -ne $FilesToAdd) -and ($FilesToAdd.IndexOf($_) -ge 0)) -or (
                            $refactorDefinition.SelectionSettings.AutoAddModifiedFiles -and
                        ($refactorDefinition.State.LastUpdated -lt ($_.LastWriteTimeUtc.AddMinutes(-15))
                    )
                        )) {

                        if ($indexRefactored -ge 0) {

                            $null = $refactorDefinition.State.Refactored.RemoveAt($indexRefactored)
                            if ($indexRefactored -le $refactorDefinition.State.RefactoredIndex) {

                                $refactorDefinition.State.RefactoredIndex = [Math]::Max(-1,
                                    $refactorDefinition.State.RefactoredIndex - 1)
                            }
                            $indexRefactored = -1;
                            if ($indexSelected -lt 0) {

                                $null = $refactorDefinition.State.Selected.Add($_)
                            }
                        }
                    }
                }
            }

            # process files marked for removal
            $FilesToRemove | Microsoft.PowerShell.Core\ForEach-Object {

                # locate file in collections
                $indexRefactored = $refactorDefinition.State.Refactored.IndexOf($_)
                $indexSelected = $refactorDefinition.State.Selected.IndexOf($_)
                $indexUnselected = $refactorDefinition.State.Unselected.IndexOf($_)

                # remove from refactored collection and update index
                if ($indexRefactored -ge 0) {

                    $target = "File: $($_)"
                    $action = "Remove from refactored set"

                    if ($PSCmdlet.ShouldProcess($target, $action)) {
                        $null = $refactorDefinition.State.Refactored.RemoveAt($indexRefactored)
                        if ($indexRefactored -le $refactorDefinition.State.RefactoredIndex) {

                            $refactorDefinition.State.RefactoredIndex = [Math]::Max(-1,
                                $refactorDefinition.State.RefactoredIndex - 1)
                        }
                        $indexRefactored = -1
                        $script:filesRemoved++
                    }
                }

                # remove from selected collection and update index
                if ($indexSelected -ge 0) {

                    $null = $refactorDefinition.State.Selected.RemoveAt($indexSelected)
                    if ($indexSelected -le $refactorDefinition.State.SelectedIndex) {

                        $refactorDefinition.State.SelectedIndex = [Math]::Max(-1,
                            $refactorDefinition.State.SelectedIndex - 1)
                    }
                    $indexSelected = -1
                }

                # remove from unselected collection and update index
                if ($indexUnselected -ge 0) {

                    $null = $refactorDefinition.State.Unselected.RemoveAt($indexUnselected)
                    if ($indexUnselected -le $refactorDefinition.State.UnselectedIndex) {

                        $refactorDefinition.State.UnselectedIndex = [Math]::Max(-1,
                            $refactorDefinition.State.UnselectedIndex - 1)
                    }
                    $indexUnselected = -1
                }
            }

            # migrate folder names
            for ($refactoredIndex = $refactorDefinition.State.Refactored.Count - 1;
                $refactoredIndex -ge 0; $refactoredIndex--) {

                if ($null -eq $refactorDefinition.State.Refactored[$refactoredIndex]) {

                    $null = $refactorDefinition.State.Refactored.RemoveAt($refactoredIndex)

                }
                else {
                    $path = (GenXdev.FileSystem\Expand-Path ($refactorDefinition.State.Refactored[$refactoredIndex]))

                    if ($path.StartsWith("$modulesPath\GenXdev")) {

                        $parts = $path.Substring($modulesPath.Length).Split("\", [System.StringSplitOptions]::RemoveEmptyEntries);

                        if ($parts.Length -gt 1) {
                            [Version] $version = $null
                            if ([Version]::tryParse($parts[1], [ref]$version)) {

                                $path = "$modulesPath\$($parts[0])\1.160.2025\$($path.Substring($modulesPath.Length + $parts[0].Length+ $parts[1].Length + 2))"

                                if ($refactorDefinition.State.Refactored.IndexOf($path) -lt 0) {

                                    $refactorDefinition.State.Refactored[$refactoredIndex] = $path
                                }
                                else {
                                    $null = $refactorDefinition.State.Refactored.RemoveAt($refactoredIndex)

                                    if ($refactoredIndex -le $refactorDefinition.State.RefactoredIndex) {

                                        $refactorDefinition.State.RefactoredIndex = [Math]::Max(-1,
                                            $refactorDefinition.State.RefactoredIndex - 1)
                                    }
                                }
                            }
                        }
                    }
                }
            }

           # migrate folder names
            for ($selectedIndex = $refactorDefinition.State.Selected.Count - 1;
                $selectedIndex -ge 0; $selectedIndex--) {
                if ($null -eq $refactorDefinition.State.Selected[$selectedIndex]) {

                    $null = $refactorDefinition.State.Selected.RemoveAt($selectedIndex)

                }
                else {
                    $path = (GenXdev.FileSystem\Expand-Path $refactorDefinition.State.Selected[$selectedIndex])

                        if ($path.StartsWith("$modulesPath\GenXdev")) {

                            $parts = $path.Substring($modulesPath.Length).Split("\", [System.StringSplitOptions]::RemoveEmptyEntries);

                            if ($parts.Length -gt 1) {
                                [Version] $version = $null

                                if ([Version]::tryParse($parts[1], [ref]$version)) {

                                    $path = "$modulesPath\$($parts[0])\1.160.2025\$($path.Substring($modulesPath.Length + $parts[0].Length+ $parts[1].Length + 2))"

                                    if ($refactorDefinition.State.Selected.IndexOf($path) -lt 0) {

                                        $refactorDefinition.State.Selected[$selectedIndex] = $path
                                    }
                                    else {
                                        $null = $refactorDefinition.State.Selected.RemoveAt($selectedIndex)

                                        if ($selectedIndex -le $refactorDefinition.State.SelectedIndex) {

                                            $refactorDefinition.State.SelectedIndex = [Math]::Max(-1,
                                                $refactorDefinition.State.SelectedIndex - 1)
                                        }
                                    }
                                }
                            }
                        }
                    }
            }

            # migrate folder names
            for ($unselectedIndex = $refactorDefinition.State.Unselected.Count - 1;
                $unselectedIndex -ge 0; $unselectedIndex--) {

                if ($null -eq $refactorDefinition.State.Unselected[$unselectedIndex]) {

                    $null = $refactorDefinition.State.Unselected.RemoveAt($unselectedIndex)
                }
                else {
                    $path = (GenXdev.FileSystem\Expand-Path $refactorDefinition.State.Unselected[$unselectedIndex])

                    if ($path.StartsWith("$modulesPath\GenXdev")) {

                        $parts = $path.Substring($modulesPath.Length).Split("\", [System.StringSplitOptions]::RemoveEmptyEntries);

                        if ($parts.Length -gt 1) {
                            [Version] $version = $null
                            if ([Version]::tryParse($parts[1], [ref]$version)) {

                                $path = "$modulesPath\$($parts[0])\1.160.2025\$($path.Substring($modulesPath.Length + $parts[0].Length+ $parts[1].Length + 2))"

                                if ($refactorDefinition.State.Unselected.IndexOf($path) -lt 0) {

                                    $refactorDefinition.State.Unselected[$unselectedIndex] = $path
                                }
                                else {
                                    $null = $refactorDefinition.State.Unselected.RemoveAt($unselectedIndex)

                                    if ($unselectedIndex -le $refactorDefinition.State.UnselectedIndex) {

                                        $refactorDefinition.State.UnselectedIndex = [Math]::Max(-1,
                                            $refactorDefinition.State.UnselectedIndex - 1)
                                    }
                                }
                            }
                        }
                    }
                }
            }

            # clean up deleted files if requested
            if ($CleanUpDeletedFiles) {

                # clean refactored collection
                for ($refactoredIndex = $refactorDefinition.State.Refactored.Count - 1;
                    $refactoredIndex -ge 0; $refactoredIndex--) {

                    if ($null -eq $refactorDefinition.State.Refactored[$refactoredIndex]) {

                        $null = $refactorDefinition.State.Refactored.RemoveAt($refactoredIndex)

                    }
                    else {
                        $path = (GenXdev.FileSystem\Expand-Path ($refactorDefinition.State.Refactored[$refactoredIndex]))

                        if (-not [IO.Path]::Exists($path)) {

                            $null = $refactorDefinition.State.Refactored.RemoveAt($refactoredIndex)
                        }
                    }

                    if ($refactorDefinition.State.RefactoredIndex -lt $refactoredIndex) {

                        $refactorDefinition.State.RefactoredIndex = [Math]::Max(-1,
                            $refactorDefinition.State.RefactoredIndex - 1)
                    }
                }

                # clean selected collection
                for ($selectedIndex = $refactorDefinition.State.Selected.Count - 1;
                    $selectedIndex -ge 0; $selectedIndex--) {

                    $path = (GenXdev.FileSystem\Expand-Path $refactorDefinition.State.Selected[$selectedIndex])

                    if (-not [IO.Path]::Exists($path)) {

                        $null = $refactorDefinition.State.Selected.RemoveAt($selectedIndex)
                    }

                    if ($refactorDefinition.State.SelectedIndex -lt $selectedIndex) {

                        $refactorDefinition.State.SelectedIndex = [Math]::Max(-1,
                            $refactorDefinition.State.SelectedIndex - 1)
                    }
                }

                # clean unselected collection
                for ($unselectedIndex = $refactorDefinition.State.Unselected.Count - 1;
                    $unselectedIndex -ge 0; $unselectedIndex--) {

                    $path = (GenXdev.FileSystem\Expand-Path $refactorDefinition.State.Unselected[$unselectedIndex])

                    if (-not [IO.Path]::Exists($path)) {

                        $null = $refactorDefinition.State.Unselected.RemoveAt($unselectedIndex)
                    }

                    if ($refactorDefinition.State.UnselectedIndex -lt $unselectedIndex) {

                        $refactorDefinition.State.UnselectedIndex = [Math]::Max(-1,
                            $refactorDefinition.State.UnselectedIndex - 1)
                    }
                }
            }

            # handle llm selections if enabled
            if ((-not [string]::IsNullOrWhiteSpace($refactorDefinition.SelectionSettings.LLM.Prompt)) -and
               (!!$PerformLLMSelections -or !!$PerformAllLLMSelections)) {

                if (((-not $AskBeforeLLMSelection) -or ($refactorDefinition.State.Selected.Count -eq 0)) -and
                    $refactorDefinition.State.Unselected.Count -ge 0) {

                    $userAnswer = 1;

                    if ($AskBeforeLLMSelection -and (-not $Script:_AlwaysRunLLMDuringRefactors)) {

                        if ($Speak) {

                            GenXdev.Console\Start-TextToSpeech "What to do next?"
                        }

                        $userAnswer = $host.ui.PromptForChoice(
                            "There are only files left that need an LLM invocation",
                            "What to do next?",
                            @("&Allways run invocations", "&Run all invocation now", "&Run invocation", "&Don't invoke now"),
                            0
                        )
                    }

                    if ($userAnswer -eq 0) {

                        $Script:_AlwaysRunLLMDuringRefactors = $true
                    }
                    if ($userAnswer -eq 1) {

                        $script:onlyFirst = $false
                    }
                    elseif ($userAnswer -eq 3) {

                        continue
                    }

                    if ($RetryFailedLLMSelections) {

                        $refactorDefinition.State.UnselectedIndex = -1;
                    }
                }

                $refactorDefinition.State.UnselectedIndex++;

                $script:nextFile = $refactorDefinition.State.Unselected[$refactorDefinition.State.UnselectedIndex];
                function goNext {

                    $script:nextFile = $null;

                    while ($refactorDefinition.State.UnselectedIndex -lt $refactorDefinition.State.Unselected.Count -and
                            ($null -eq $script:nextFile -or ((-not [IO.File]::Exists($script:nextFile))))) {

                        $refactorDefinition.State.UnselectedIndex = [Math]::Min(
                            $refactorDefinition.State.UnselectedIndex + 1,
                            $refactorDefinition.State.Unselected.Count - 1
                        )

                        if ($refactorDefinition.State.UnselectedIndex -lt $refactorDefinition.State.Unselected.Count) {

                            $script:nextFile = $refactorDefinition.State.Unselected[$refactorDefinition.State.UnselectedIndex];
                        }
                        else {

                            $script:nextFile = $null;
                        }
                    }
                }

                goNext

                while (($null -ne $script:nextFile) -and ($script:last -ne $script:nextFile)) {

                    try {
                        # Create a string builder for verbose output
                        $verboseOutput = [System.Text.StringBuilder]::new()

                        # Create a scriptblock to capture verbose output
                        $verboseScriptBlock = {
                            param($Message)
                            $null = $verboseOutput.AppendLine($Message)
                        }

                        # Register temporary verbose handler
                        $null = Microsoft.PowerShell.Utility\Register-EngineEvent -SourceIdentifier "Verbose" -Action $verboseScriptBlock

                        # Run the test and get the result
                        $result = GenXdev.Coding\Test-RefactorLLMSelection `
                            -RefactorDefinition $refactorDefinition `
                            -Path ($script:nextFile)

                    }
                    catch {
                        $result = $false
                        $now = GenXdev.Console\UtcNow
                        $refactorDefinition.Log.Add(
                            [GenXdev.Helpers.RefactorLogItem]@{
                                Timestamp = $now
                                Message   = "Error during LLM selection of file '$($script:nextFile)' : $($_.Exception.Message)"
                            }
                        )
                    }
                    finally {
                        # Clean up verbose handling
                        $null = Microsoft.PowerShell.Utility\Unregister-Event -SourceIdentifier "Verbose" -ErrorAction SilentlyContinue
                    }

                    if ($result -eq $true) {

                        $now = GenXdev.Console\UtcNow
                        $refactorDefinition.Log.Add(
                            [GenXdev.Helpers.RefactorLogItem]@{
                                Timestamp = $now
                                Message   = "File '$($script:nextFile)' was selected by LLM for refactoring`n$($verboseOutput.ToString())"
                            }
                        )

                        $indexRefactored = $refactorDefinition.State.Refactored.IndexOf(($script:nextFile))
                        $indexSelected = $refactorDefinition.State.Selected.IndexOf(($script:nextFile))
                        $indexUnselected = $refactorDefinition.State.Unselected.IndexOf(($script:nextFile))

                        if ($indexSelected -lt 0) {

                            $null = $refactorDefinition.State.Selected.Add(($script:nextFile))
                        }

                        if ($indexUnselected -ge 0) {

                            $null = $refactorDefinition.State.Unselected.RemoveAt($indexUnselected)

                            if ($indexUnselected -le $refactorDefinition.State.UnselectedIndex) {

                                $refactorDefinition.State.UnselectedIndex = [Math]::Max(-1,
                                    $refactorDefinition.State.UnselectedIndex - 1)
                            }
                        }

                        if ($indexRefactored -ge 0) {

                            $refactorDefinition.State.Refactored.RemoveAt($indexRefactored)
                            if ($indexRefactored -le $refactorDefinition.State.RefactoredIndex) {

                                $refactorDefinition.State.RefactoredIndex = [Math]::Max(-1,
                                    $refactorDefinition.State.RefactoredIndex - 1)
                            }
                        }

                        # persist changes to preferences
                        if ($PSCmdlet.ShouldProcess(
                                "Refactor set: $($refactorDefinition.Name)",
                                "Save changes")) {

                            $json = $refactorDefinition | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress
                            $latestJson = (GenXdev.Data\Get-GenXdevPreference `
                                    -Name "refactor_set_$($refactorDefinition.Name)"
                            )
                            if ($null -eq $latestJson) {

                                Microsoft.PowerShell.Utility\Write-Warning "Refactor set has been deleted"
                                break;
                            }

                            $latest = $latestJson | Microsoft.PowerShell.Utility\ConvertFrom-Json -ErrorAction SilentlyContinue
                            if ($null -ne $latest -and ($latest.State.LastUpdated -gt $refactorDefinition.State.LastUpdated)) {

                                $latest.State = $refactorDefinition.State;
                                $latest.Log = $refactorDefinition.Log;

                                $refactorDefinition = $latest;
                            }

                            $now = GenXdev.Console\UtcNow
                            $refactorDefinition.State.LastUpdated = $now
                            $json = $refactorDefinition | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress
                            GenXdev.Data\Set-GenXdevPreference `
                                -Name "refactor_set_$($refactorDefinition.Name)" `
                                -Value $json
                        }

                        if ($script:onlyFirst) {

                            break
                        }
                    }
                    else {
                        $now = GenXdev.Console\UtcNow
                        $refactorDefinition.Log.Add(
                            [GenXdev.Helpers.RefactorLogItem]@{
                                Timestamp = $now
                                Message   = "File '$($script:nextFile)' was NOT selected by LLM for refactoring`n$($verboseOutput.ToString())"
                            }
                        )
                    }

                    $script:last = $script:nextFile
                    goNext
                }
            }

            # update state and persist changes if modifications were made
            if (($null -ne $refactorDefinition) -and
                ($refactorDefinition -is [GenXdev.Helpers.RefactorDefinition])) {

                Microsoft.PowerShell.Utility\Write-Verbose ("Updating refactor set state with $script:filesAdded added " +
                    "and $script:filesRemoved removed")

                # $totalFilesLeft = (
                # ($refactorDefinition.State.Unselected.Count - 1) -
                # $refactorDefinition.State.UnselectedIndex
                # ) + (
                # ($refactorDefinition.State.Selected.Count - 1) -
                # $refactorDefinition.State.SelectedIndex
                # ) + (
                # ($refactorDefinition.State.Refactored.Count - 1) -
                # $refactorDefinition.State.RefactoredIndex
                # );

                $totalFilesDone = (
                    $refactorDefinition.State.UnselectedIndex + 1
                ) + (
                    $refactorDefinition.State.SelectedIndex + 1
                ) + (
                    $refactorDefinition.State.RefactoredIndex + 1
                );

                $totalFiles = (
                    $refactorDefinition.State.Unselected.Count
                ) + (
                    $refactorDefinition.State.Selected.Count
                ) + (
                    $refactorDefinition.State.Refactored.Count
                )

                # calculate and update completion percentage
                $refactorDefinition.State.PercentageComplete = [System.Math]::Min(100, [Math]::Round(
                    (100 * $totalFilesDone) / [Math]::Max(1, $totalFiles),
                        0
                    ))

                $now = GenXdev.Console\UtcNow

                if ($script:filesAdded -gt 0 -or $script:filesRemoved -gt 0) {

                    # log status update
                    $refactorDefinition.Log.Add(
                        [GenXdev.Helpers.RefactorLogItem]@{
                            Timestamp = $now
                            Message   = "Refactor set updated, $script:filesAdded files added, " +
                            "$script:filesRemoved files removed, " +
                            "($($refactorDefinition.State.PercentageComplete)% " +
                            "complete)"
                        }
                    )
                }

                # persist changes to preferences
                if ($PSCmdlet.ShouldProcess(
                        "Refactor set: $($refactorDefinition.Name)",
                        "Save changes")) {

                    $latestJson = (GenXdev.Data\Get-GenXdevPreference `
                            -Name "refactor_set_$($refactorDefinition.Name)"
                    )
                    if ($null -eq $latestJson) {

                        Microsoft.PowerShell.Utility\Write-Warning "Refactor set has been deleted"
                        break;
                    }

                    $latest = $latestJson | Microsoft.PowerShell.Utility\ConvertFrom-Json -ErrorAction SilentlyContinue
                    if ($null -ne $latest -and ($latest.State.LastUpdated -lt $refactorDefinition.State.LastUpdated)) {

                        $latest.State = $refactorDefinition.State;
                        $latest.Log = $refactorDefinition.Log;

                        $refactorDefinition = $latest;
                    }

                    $now = GenXdev.Console\UtcNow
                    $refactorDefinition.State.LastUpdated = $now
                    $json = $refactorDefinition | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress
                    GenXdev.Data\Set-GenXdevPreference `
                        -Name "refactor_set_$($refactorDefinition.Name)" `
                        -Value $json
                }

                Microsoft.PowerShell.Utility\Write-Verbose "Refactor set updated successfully"
            }
        }
    }

    end {

    }
}
################################################################################