PoshCodex.psm1

### --- PUBLIC FUNCTIONS --- ###
#Region - Enter-CompletionKeybind.ps1
#Requires -Modules PSReadLine

# Asks for user input (reads key combinations), calls internal functions to convert to string and set it as the new keybind
function Enter-CompletionKeybind {
    # Add cmdletBinding to the parameter list
    [CmdletBinding()]
    param()

    Write-Host "Press any key or combination (Ctrl, Alt, Shift + other keys) to set the new keybind for PoshCodex. Press 'Escape' to exit."
    # Read the key press
    $key = [System.Console]::ReadKey($true)
    $new_autocomplete_keybind = Convert-KeyPressToString $key
    if ($null -eq $new_autocomplete_keybind) {
        # exit, no input given (user pressed Escape).
        return
    }

    # get old keybind, call set keybind for new
    $old_autocomplete_keybind = [Environment]::GetEnvironmentVariable('AUTOCOMPLETE_KEYBIND', 'User')
    Set-CompletionKeybind $old_autocomplete_keybind $new_autocomplete_keybind
    Write-Host "New keybind set: $([Environment]::GetEnvironmentVariable('AUTOCOMPLETE_KEYBIND', 'User'))"
}
Export-ModuleMember -Function Enter-CompletionKeybind
#EndRegion - Enter-CompletionKeybind.ps1
### --- PRIVATE FUNCTIONS --- ###
#Region - Convert-KeyPressToString.ps1
#Requires -Modules PSReadLine

# Function to convert a user input key press into a keybind string
function Convert-KeyPressToString {
    # Add cmdletBinding to the parameter list
    [CmdletBinding()]
    param($key)

    # First, check and append Ctrl if it's pressed
    if ($key.Modifiers -band [ConsoleModifiers]::Control) {
        $keybind += 'Ctrl+'
    }

    # Next, check and append Alt if it's pressed
    if ($key.Modifiers -band [ConsoleModifiers]::Alt) {
        $keybind += 'Alt+'
    }

    # Lastly, check and append Shift if it's pressed
    if ($key.Modifiers -band [ConsoleModifiers]::Shift) {
        $keybind += 'Shift+'
    }

    # Append the actual key that was pressed
    $keybind += $key.Key.ToString()

    # Display the keybind string
    # Write-Host "Keybind entered: $keybind"

    # If the Escape key is pressed, exit the loop
    if ($key.Key -eq 'Escape') {
        Write-Host 'Aborted by user, exiting...'
        return
    }

    $keybind
}
#EndRegion - Convert-KeyPressToString.ps1
#Region - Invoke-OllamaApi.ps1
function Invoke-OllamaApi {
    [CmdletBinding()]
    param (
        $BUFFER
    )

    $ollama_model = [Environment]::GetEnvironmentVariable('OLLAMA_MODEL', 'User')
    $ollama_host = [Environment]::GetEnvironmentVariable('OLLAMA_HOST', 'User')

    $data = @{
        model  = "$ollama_model"
        prompt = $BUFFER
        stream = $false
    }

    $splatRestMethod = @{
        Method      = 'POST'
        Uri         = "$ollama_host/api/generate"
        Body        = ConvertTo-Json -InputObject $data
        ContentType = 'application/json; charset=utf-8'
    }
    Invoke-RestMethod @splatRestMethod
}
#EndRegion - Invoke-OllamaApi.ps1
#Region - Set-CompletionKeybind.ps1
#Requires -Modules PSReadLine

# Function to simply handle the setting and removal of keybinds, without worrying about the user input.
function Set-CompletionKeybind {
    # Add cmdletBinding to the parameter list
    [CmdletBinding()]
    param(
        $old_keybind, $new_keybind
    )

    if ($null -ne $old_keybind) {
        # unset current handler for Write-Completion if it exists
        Remove-PSReadLineKeyHandler -Chord $old_keybind
        Write-Host "Previous keybind removed: $old_keybind"
    }

    $splatKeyHandler = @{
        Chord            = $new_keybind
        BriefDescription = 'Write-Completion'
        Description      = 'Autocomplete the command'
        ScriptBlock      = { Write-Completion }
    }
    Set-PSReadLineKeyHandler @splatKeyHandler

    # Update env var with new keybind
    [Environment]::SetEnvironmentVariable('AUTOCOMPLETE_KEYBIND', $new_keybind, [EnvironmentVariableTarget]::User)
}
#EndRegion - Set-CompletionKeybind.ps1
#Region - Write-Completion.ps1
#Requires -Modules PSReadLine

function Write-Completion {
    # Add cmdletBinding to the parameter list
    [CmdletBinding()]
    param()

    $BUFFER = $null
    $cursor = $null

    # read text from current buffer
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$BUFFER, [ref]$cursor)

    # If the buffer text itself contains double quotes, then we need to escape them.
    $BUFFER = $BUFFER.Replace('"', '""')

    $json_output = Invoke-OllamaApi $BUFFER

    # check if json_output is not equal to null
    if ($null -ne $json_output) {
        $completion = $json_output.response

        # Insert the completion on the next line. This will NOT cause the command to be executed.
        [Microsoft.PowerShell.PSConsoleReadLine]::InsertLineBelow();
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert($completion)
    }
    else {
        Write-Host 'Response returned by API is null! It could be an internal error or the model is not installed properly through Ollama. Please fix and try again.' -ForegroundColor Red
    }
}
#EndRegion - Write-Completion.ps1
## Set necessary environment variables:

[Environment]::SetEnvironmentVariable('OLLAMA_HOST', 'http://localhost:11434', [EnvironmentVariableTarget]::User)
[Environment]::SetEnvironmentVariable('OLLAMA_MODEL', 'rishi255/posh_codex_model', [EnvironmentVariableTarget]::User)

$current_keybind = [Environment]::GetEnvironmentVariable('AUTOCOMPLETE_KEYBIND', 'User')

$default_keybind = if ([String]::IsNullOrWhiteSpace($current_keybind)) {
    ## Use default keybind, if none is set
    'Ctrl+Shift+O'
}
else {
    ## Use existing keybind
    $current_keybind
}
Set-CompletionKeybind $null $default_keybind

## Check if Ollama is installed in the user or system PATH
if (Get-Command -Name 'ollama' -CommandType 'Application' -ErrorAction Ignore) {
    ## Check if Ollama is running
    if (-not (Get-Process -Name 'ollama' -ErrorAction Ignore)) {
        ## Start Ollama if it's not running
        Start-Process -FilePath 'ollama' -ArgumentList 'serve' -WindowStyle Hidden
    }
} else {
    Write-Warning 'Ollama is not installed or not in the user or system PATH.'
    Write-Warning 'Please follow the instructions at "https://github.com/rishi255/posh_codex#configuration-of-the-ollama-model" and re-import the module.'
}