Public/ProfileHook.ps1

# ---------------------------------------------------------------------------
# Profile hook installer
# ---------------------------------------------------------------------------

function Install-TTProfileHook {
    <#
    .SYNOPSIS
        Add the TerminalTracker prompt hook to your PowerShell profile.
    .DESCRIPTION
        Adds a snippet to $PROFILE.CurrentUserAllHosts that wraps the prompt
        function to call Update-TTSession on every prompt render. Also starts
        the background monitor if not already running.
    .PARAMETER Force
        Reinstall the hook even if one is already present.
    .EXAMPLE
        Install-TTProfileHook
        Installs the prompt hook in the PowerShell profile.
    .EXAMPLE
        Install-TTProfileHook -Force
        Reinstalls the prompt hook, replacing any existing one.
    #>

    [CmdletBinding()]
    param(
        [switch]$Force
    )

    $profilePath = $PROFILE.CurrentUserAllHosts
    if (-not (Test-Path (Split-Path $profilePath -Parent))) {
        New-Item -Path (Split-Path $profilePath -Parent) -ItemType Directory -Force | Out-Null
    }

    $startMarker = '# >>> TerminalTracker Hook >>>'
    $endMarker   = '# <<< TerminalTracker Hook <<<'
    $modulePath = Join-Path (Split-Path $PSScriptRoot -Parent) 'TerminalTracker.psd1'

    $hookBlock = @"
 
$startMarker
# Auto-generated by TerminalTracker. Do not edit this block manually.
Import-Module '$modulePath' -Force -DisableNameChecking
`$Global:__TTOriginalPrompt = if (`$Function:prompt) { `$Function:prompt } else { { "PS> " } }
function Global:prompt {
    # Call original prompt
    `$result = & `$Global:__TTOriginalPrompt
    # Update tracker (non-blocking, errors suppressed)
    try { Update-TTSession } catch { }
    return `$result
}
# Start monitor if not already running
if (-not (Get-Job -Name 'TerminalTracker-Monitor' -ErrorAction SilentlyContinue)) {
    Start-TTMonitor -AsJob | Out-Null
}
$endMarker
"@


    if (Test-Path $profilePath) {
        $content = [System.IO.File]::ReadAllText($profilePath)
        $startIdx = $content.IndexOf($startMarker)
        if ($startIdx -ge 0) {
            if (-not $Force) {
                Write-Verbose "Profile hook already installed. Use -Force to reinstall."
                return
            }
            # Backup before modifying
            $backupPath = "$profilePath.bak"
            [System.IO.File]::Copy($profilePath, $backupPath, $true)
            # Remove existing hook by exact index
            $endIdx = $content.IndexOf($endMarker)
            if ($endIdx -gt $startIdx) {
                $before = $content.Substring(0, $startIdx).TrimEnd()
                $after  = $content.Substring($endIdx + $endMarker.Length).TrimStart("`r", "`n")
                $content = if ($after) { "$before`r`n$after" } else { $before }
            }
        }
        $content = $content.TrimEnd() + "`n" + $hookBlock
        [System.IO.File]::WriteAllText($profilePath, $content, [System.Text.Encoding]::UTF8)
    }
    else {
        [System.IO.File]::WriteAllText($profilePath, $hookBlock, [System.Text.Encoding]::UTF8)
    }

    # Update config
    $cfg = Get-TTConfig
    $cfg.ProfileHookInstalled = $true
    Write-JsonFile $script:ConfigFile $cfg

    Write-Verbose "Profile hook installed at: $profilePath"
    Write-Verbose "Restart your PowerShell sessions for it to take effect."
}

function Remove-TTProfileHook {
    <#
    .SYNOPSIS
        Remove the TerminalTracker prompt hook from your PowerShell profile.
    .DESCRIPTION
        Removes the TerminalTracker hook block from $PROFILE.CurrentUserAllHosts.
        Creates a backup of the profile before modification.
    .EXAMPLE
        Remove-TTProfileHook
        Removes the prompt hook from the PowerShell profile.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param()

    if (-not $PSCmdlet.ShouldProcess("PowerShell profile hook", "Remove")) { return }

    $profilePath = $PROFILE.CurrentUserAllHosts
    if (-not (Test-Path $profilePath)) { Write-Verbose "No profile found."; return }

    $startMarker = '# >>> TerminalTracker Hook >>>'
    $endMarker   = '# <<< TerminalTracker Hook <<<'

    $content = [System.IO.File]::ReadAllText($profilePath)
    $startIdx = $content.IndexOf($startMarker)
    $endIdx   = $content.IndexOf($endMarker)

    if ($startIdx -ge 0 -and $endIdx -gt $startIdx) {
        # Backup before modifying
        $backupPath = "$profilePath.bak"
        [System.IO.File]::Copy($profilePath, $backupPath, $true)
        # Remove hook block by exact index
        $before = $content.Substring(0, $startIdx).TrimEnd()
        $after  = $content.Substring($endIdx + $endMarker.Length).TrimStart("`r", "`n")
        $newContent = if ($after) { "$before`r`n$after" } else { $before }
        [System.IO.File]::WriteAllText($profilePath, $newContent, [System.Text.Encoding]::UTF8)
        Write-Verbose "Profile hook removed."

        $cfg = Get-TTConfig
        $cfg.ProfileHookInstalled = $false
        Write-JsonFile $script:ConfigFile $cfg
    }
    else {
        Write-Verbose "No hook found in profile."
    }
}

function Repair-TTProfileHook {
    <#
    .SYNOPSIS
        Detect and repair a broken TerminalTracker profile hook path.
    .DESCRIPTION
        Checks $PROFILE.CurrentUserAllHosts for a hook block that references a bad
        path containing 'Public\TerminalTracker'. If found, removes the old block and
        reinstalls using Install-TTProfileHook to write the correct path.
    .EXAMPLE
        Repair-TTProfileHook
        Detects and repairs a bad profile hook if one exists.
    #>

    [CmdletBinding()]
    param()

    $profilePath = $PROFILE.CurrentUserAllHosts
    if (-not (Test-Path $profilePath)) {
        Write-Host "No profile file found at: $profilePath" -ForegroundColor Yellow
        return
    }

    $content = [System.IO.File]::ReadAllText($profilePath)
    $startMarker = '# >>> TerminalTracker Hook >>>'
    $endMarker   = '# <<< TerminalTracker Hook <<<'

    $startIdx = $content.IndexOf($startMarker)
    if ($startIdx -lt 0) {
        Write-Host "No TerminalTracker hook found in profile." -ForegroundColor Gray
        return
    }

    $endIdx = $content.IndexOf($endMarker)
    if ($endIdx -le $startIdx) {
        Write-Host "Hook markers malformed in profile." -ForegroundColor Red
        return
    }

    $hookBlock = $content.Substring($startIdx, $endIdx - $startIdx + $endMarker.Length)

    # Check for bad path pattern: any path ending with Public\TerminalTracker (with any extension)
    if ($hookBlock -notmatch 'Public[/\\]TerminalTracker') {
        Write-Host "Profile hook path looks correct -- no repair needed." -ForegroundColor Green
        return
    }

    Write-Host "Bad hook path detected (Public\TerminalTracker.*). Repairing..." -ForegroundColor Yellow

    # Backup profile
    $backupPath = "$profilePath.bak"
    try {
        [System.IO.File]::Copy($profilePath, $backupPath, $true)
        Write-Host "Profile backed up to: $backupPath" -ForegroundColor Gray
    }
    catch {
        Write-Host "Could not create backup: $_" -ForegroundColor Red
        return
    }

    # Remove old hook block
    $before = $content.Substring(0, $startIdx).TrimEnd()
    $after  = $content.Substring($endIdx + $endMarker.Length).TrimStart("`r", "`n")
    $stripped = if ($after) { "$before`r`n$after" } else { $before }
    [System.IO.File]::WriteAllText($profilePath, $stripped, [System.Text.Encoding]::UTF8)

    # Reinstall with correct path
    try {
        Install-TTProfileHook -Force
        Write-Host "Profile hook repaired successfully. Restart your terminals to apply." -ForegroundColor Green
    }
    catch {
        Write-Host "Hook removal succeeded but reinstall failed: $_" -ForegroundColor Red
    }
}