private/Invoke-WtwWorkmuxProject.ps1
|
# workmux integration for wtw. # # workmux (https://workmux.raine.dev) pairs git worktrees with tmux windows and # adds an agent status sidebar/dashboard (`workmux sidebar`, `workmux dashboard`). # It reads `git worktree list`, so wtw's worktrees (and the main checkout) are # already known to it by directory name: # workmux open <dir-name> -> "Opened tmux window for ..." or # "Switched to existing tmux window for ..." # It needs a running tmux server, but not a tmux client: from outside tmux the # window opens in the server's session. With no server, wtw starts # `tmux new-session` in a new Ghostty window first. workmux slugs window names # (emoji become words), so wtw keeps its default `wm-<dir>` names. function Get-WtwWorkmuxExe { [CmdletBinding()] param() if ($env:WTW_WORKMUX -and (Test-Path $env:WTW_WORKMUX)) { return $env:WTW_WORKMUX } $cmd = Get-Command workmux -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1 if ($cmd -and $cmd.Source) { return $cmd.Source } $homeDir = if ($HOME) { $HOME } else { $env:USERPROFILE } if ($homeDir) { foreach ($dir in @('.cargo/bin', '.local/bin')) { $candidate = Join-Path (Join-Path $homeDir $dir) 'workmux' if (Test-Path $candidate) { return $candidate } } } return $null } function Test-WtwTmuxServer { <# .SYNOPSIS True when a tmux server with at least one session is running. #> [CmdletBinding()] param() if (-not (Get-Command tmux -CommandType Application -ErrorAction SilentlyContinue)) { return $false } & tmux has-session 2>$null return $LASTEXITCODE -eq 0 } function Start-WtwTmuxServer { <# .SYNOPSIS Start a tmux session in a new Ghostty window and wait for the server. #> [CmdletBinding()] param( [Parameter(Mandatory)][string] $ProjectPath, [int] $TimeoutSeconds = 10 ) if (-not (Get-Command tmux -CommandType Application -ErrorAction SilentlyContinue)) { return $false } if (-not (Start-WtwGhosttyWindow -WorkingDirectory $ProjectPath -Command @('tmux', 'new-session', '-A', '-s', 'wtw'))) { return $false } $deadline = [DateTime]::UtcNow.AddSeconds($TimeoutSeconds) while ([DateTime]::UtcNow -lt $deadline) { Start-Sleep -Milliseconds 250 if (Test-WtwTmuxServer) { return $true } } return $false } function Invoke-WtwWorkmuxCommand { [CmdletBinding()] param( [Parameter(Mandatory)][string] $WorkingDirectory, [Parameter(Mandatory)][string[]] $ArgumentList ) $exe = Get-WtwWorkmuxExe if (-not $exe) { return [PSCustomObject]@{ ExitCode = 127; Output = 'workmux not found' } } Write-Verbose "workmux command (in $WorkingDirectory): $exe $($ArgumentList -join ' ')" Push-Location -LiteralPath $WorkingDirectory try { $output = & $exe @ArgumentList 2>&1 $exitCode = $LASTEXITCODE } finally { Pop-Location } if ($null -eq $exitCode) { $exitCode = 0 } $text = (@($output) | ForEach-Object { "$_" }) -join [Environment]::NewLine return [PSCustomObject]@{ ExitCode = $exitCode; Output = $text.Trim() } } function Open-WtwWorkmuxProject { <# .SYNOPSIS Open a wtw target as a workmux tmux window: `workmux open <dir-name>` switches to the existing window or creates one. #> [CmdletBinding()] param( [Parameter(Mandatory)][string] $ProjectPath ) if (-not (Get-WtwWorkmuxExe)) { return [PSCustomObject]@{ Success = $false; Created = $false; Started = $false; Reason = 'workmux not found' } } $fullPath = [System.IO.Path]::GetFullPath($ProjectPath).TrimEnd('/', '\') $name = Split-Path -Leaf $fullPath $started = $false if (-not (Test-WtwTmuxServer)) { if (-not (Start-WtwTmuxServer -ProjectPath $fullPath)) { return [PSCustomObject]@{ Success = $false; Created = $false; Started = $false; Path = $fullPath; Name = $name Reason = 'no tmux server is running and Ghostty could not start one. Run `tmux` in a terminal, then retry.' } } $started = $true } $result = Invoke-WtwWorkmuxCommand -WorkingDirectory $fullPath -ArgumentList @('open', $name) if ($result.ExitCode -ne 0) { return [PSCustomObject]@{ Success = $false; Created = $false; Started = $started; Path = $fullPath; Name = $name; Reason = $result.Output } } return [PSCustomObject]@{ Success = $true; Created = ($result.Output -notmatch 'existing'); Started = $started Path = $fullPath; Name = $name; Reason = $null } } |