repo-nav.psm1
|
#Requires -Version 7.0 # repo-nav.psm1 — Module entry point. # # Un user de PowerShell Gallery hace `Install-Module repo-nav` y queda con esta # carpeta en su $PSModulePath. Al `Import-Module repo-nav` PowerShell ejecuta # este archivo. Cargamos las clases en orden bottom-up (igual que repo-nav.ps1 # para users locales) y exportamos UN cmdlet `Invoke-RepoNav` + alias `rnav`. # OJO: usamos $script:ModuleRoot (no $script:RNRoot) para apuntar al directorio # del módulo. Bootstrap.ps1 reasigna $script:RNRoot al scope donde se dot-sourcea, # y queda apuntando a `src/` (un nivel adentro del módulo) — eso es lo que el # resto del código v3 espera. No queremos que se pise nuestro module root. $script:ModuleRoot = $PSScriptRoot # Versión, leída del manifest. Single source of truth (el manifest manda). # Se asigna antes de cargar las clases para que esté disponible aunque el # Bootstrap haya pisado $script:RNRoot. $script:RNVersion = (Import-PowerShellDataFile -Path (Join-Path $script:ModuleRoot 'repo-nav.psd1')).ModuleVersion # Carga de clases. Bootstrap define $global:RNLoadOrder con el orden bottom-up # (Models → Util → Services → UI → App). Después dot-sourceamos cada uno desde # acá — desde el scope del módulo — para que las classes queden visibles entre # archivos (PS classes solo se ven cuando se cargan en el mismo scope que el # llamador). . (Join-Path $script:ModuleRoot 'src/App/Bootstrap.ps1') foreach ($rel in $global:RNLoadOrder) { . (Join-Path $script:ModuleRoot "src/$rel") } # UTF-8 global. La TUI lo configura adentro de Start-RepoNavV3 también, pero # subcomandos no-TUI (help, doctor) escriben antes de eso. try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 } catch { $null = $_ } function Invoke-RepoNav { <# .SYNOPSIS Interactive multi-repository navigator for the terminal. .DESCRIPTION Opens a TUI listing all git repositories under the current path, with branch operations, favorites, aliases, and bilingual UI (es/en). Subcommands: list, (empty) Open the TUI (default). init Install the alias in your $PROFILE. uninstall Remove the alias from your $PROFILE. config Open the configuration screen directly. doctor Print install/dependencies/settings report. help Show this help. version Print the module version. .PARAMETER Subcommand Action to run. Defaults to 'list' (open the TUI). .PARAMETER Theme Override the active theme by key (midnight, aurora, ocean, forest, solarized, hc, light, default). .EXAMPLE rnav # Opens the TUI in the current directory. .EXAMPLE rnav doctor # Prints a diagnostic report. .EXAMPLE rnav -Theme aurora # Opens the TUI with the Aurora theme. .LINK https://github.com/jobshimo/repo-nav #> [CmdletBinding()] param( [Parameter(Position = 0)] [string] $Subcommand = '', [string] $Theme, [switch] $Help, [switch] $Version, [Parameter(ValueFromRemainingArguments)] $RemainingArgs ) if ($Help) { Show-RepoNavHelp; return } if ($Version) { Show-RepoNavVersion; return } switch ($Subcommand) { '' { } # default → list 'list' { } 'help' { Show-RepoNavHelp; return } 'version' { Show-RepoNavVersion; return } 'init' { Invoke-RepoNavInit -ExtraArgs $RemainingArgs; return } 'uninstall' { Invoke-RepoNavUninstall -ExtraArgs $RemainingArgs; return } 'doctor' { Invoke-RepoNavDoctor; return } 'config' { Invoke-RepoNavConfig -Theme $Theme; return } default { Write-Host "[error] unknown subcommand: '$Subcommand'. Try: rnav help" -ForegroundColor Red return } } # Default = list = TUI principal. if ($Theme) { Start-RepoNavV3 -ThemeKey $Theme } else { Start-RepoNavV3 } } function Show-RepoNavHelp { $v = $script:RNVersion Write-Host @" repo-nav v$v — interactive multi-repository navigator. USAGE rnav [<subcommand>] [-Theme <name>] SUBCOMMANDS list, (empty) Open the TUI with the repository list. init Install the alias in your `$PROFILE. uninstall Remove the alias from your `$PROFILE. config Open the configuration screen directly. doctor Diagnostic report (install, dependencies, settings). help Show this help. version Print the module version. OPTIONS (TUI) -Theme <name> Override theme: midnight, aurora, ocean, forest, solarized, hc, light, default. EXAMPLES rnav # open TUI in current directory rnav -Theme aurora # open TUI with Aurora theme rnav doctor # diagnostic rnav config # configuration only rnav init # add the alias to your `$PROFILE DOCS https://github.com/jobshimo/repo-nav "@ } function Show-RepoNavVersion { $v = $script:RNVersion Write-Host "repo-nav v$v" } function Invoke-RepoNavInit { param([object[]] $ExtraArgs) $installScript = Join-Path $script:ModuleRoot 'Install.ps1' if (-not (Test-Path -LiteralPath $installScript)) { Write-Host "[error] Install.ps1 not found in $script:ModuleRoot" -ForegroundColor Red return } & $installScript @ExtraArgs } function Invoke-RepoNavUninstall { param([object[]] $ExtraArgs) $installScript = Join-Path $script:ModuleRoot 'Install.ps1' if (-not (Test-Path -LiteralPath $installScript)) { Write-Host "[error] Install.ps1 not found in $script:ModuleRoot" -ForegroundColor Red return } & $installScript -Uninstall @ExtraArgs } function Invoke-RepoNavDoctor { $svc = [SetupService]::new() $profileInfo = $svc.GetProfileInfo() $settingsInfo = $svc.GetSettingsInfo() $deps = $svc.CheckDependencies() $aliases = $svc.GetInstalledAliases() $v = $script:RNVersion Write-Host '' Write-Host "repo-nav v$v — doctor" -ForegroundColor Cyan Write-Host ('─' * 60) -ForegroundColor DarkGray Write-Host '' Write-Host 'Installation' -ForegroundColor White if ($aliases.Count -gt 0) { Write-Host ('[ok] Aliases registered: ' + ($aliases -join ', ')) -ForegroundColor Green } else { Write-Host '[warn] No alias registered. The module already exports `rnav` automatically when imported.' -ForegroundColor Yellow } $profileStr = if ($profileInfo.Path) { $profileInfo.Path } else { '(undefined)' } Write-Host "[info] Profile: $profileStr" -ForegroundColor Cyan Write-Host ("[info] Profile exists: " + ($profileInfo.Exists)) -ForegroundColor Cyan Write-Host ("[info] Module root: $script:ModuleRoot") -ForegroundColor Cyan Write-Host '' Write-Host 'Settings' -ForegroundColor White if ($settingsInfo.Exists -and $settingsInfo.Valid) { Write-Host "[ok] Settings file OK ($($settingsInfo.Path))" -ForegroundColor Green } elseif ($settingsInfo.Exists -and -not $settingsInfo.Valid) { Write-Host "[error] Settings file exists but JSON is corrupt: $($settingsInfo.Path)" -ForegroundColor Red } else { Write-Host "[info] Settings file not yet created (will be on first save): $($settingsInfo.Path)" -ForegroundColor Cyan } Write-Host '' Write-Host 'Dependencies' -ForegroundColor White foreach ($name in @('PowerShell', 'Git', 'Node', 'Gh')) { $d = $deps[$name] $label = if ($name -eq 'Gh') { 'gh (GitHub CLI)' } else { $name.ToLower() } if ($d.Available) { Write-Host "[ok] $label $($d.Version)" -ForegroundColor Green } elseif ($d.Required) { Write-Host "[error] $label not available (required). $($d.Notes)" -ForegroundColor Red } else { Write-Host "[info] $label not available (optional). $($d.Notes)" -ForegroundColor Cyan } } Write-Host '' } function Invoke-RepoNavConfig { param([string] $Theme) $settingsSvc = [SettingsService]::new() $settings = $settingsSvc.Load() $themeKey = if ($Theme) { $Theme } elseif ($settings.ThemeKey) { $settings.ThemeKey } else { 'midnight' } if (-not $global:RNThemes.ContainsKey($themeKey)) { $themeKey = 'midnight' } $themeSvc = [ThemeService]::new($themeKey) $renderer = [Renderer]::new($themeSvc) $primitives = [Primitives]::new($themeSvc) $frame = [Frame]::new($themeSvc, $renderer) $header = [AppHeader]::new($themeSvc, $renderer, $primitives) $statusBar = [StatusBar]::new($themeSvc, $renderer, $primitives) $setupSvc = [SetupService]::new() $cfg = [ConfigScreen]::new($themeSvc, $renderer, $primitives, $frame, $header, $statusBar, $settingsSvc, $setupSvc) # Cursor + alt buffer ownership (ADR-0004): el root maneja, sub-screen no. $errOut = [Console]::Error $errOut.Write([AnsiService]::EnterAltBuffer()) $errOut.Write([AnsiService]::HideCursor()) try { $cfg.Open() } finally { $errOut.Write([AnsiService]::ShowCursor()) $errOut.Write([AnsiService]::LeaveAltBuffer()) } } # Alias rnav → Invoke-RepoNav. El user puede ejecutar `rnav` directamente # después de Import-Module — equivalente a Invoke-RepoNav con los mismos args. Set-Alias -Name rnav -Value Invoke-RepoNav -Force Export-ModuleMember -Function 'Invoke-RepoNav' -Alias 'rnav' |