public/Open-WtwCmuxWorkspace.ps1

function Get-WtwCmuxObjectValue {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] $Object,
        [Parameter(Mandatory)][string[]] $Names
    )

    foreach ($name in $Names) {
        $current = $Object
        $found = $true
        foreach ($part in $name.Split('.')) {
            if (-not $current) { $found = $false; break }
            $prop = $current.PSObject.Properties[$part]
            if (-not $prop) { $found = $false; break }
            $current = $prop.Value
        }
        if ($found -and $null -ne $current -and "$current" -ne '') { return $current }
    }

    return $null
}

function Get-WtwCmuxWorkspaceRef {
    [CmdletBinding()]
    param([Parameter(Mandatory)] $Workspace)

    return Get-WtwCmuxObjectValue -Object $Workspace -Names @(
        'ref',
        'workspaceRef',
        'workspace',
        'id',
        'uuid',
        'workspace_id',
        'workspaceId',
        'refs.workspace',
        'refs.workspaceRef'
    )
}

function Get-WtwCmuxWorkspaceName {
    [CmdletBinding()]
    param([Parameter(Mandatory)] $Workspace)

    return Get-WtwCmuxObjectValue -Object $Workspace -Names @('name', 'title', 'displayName')
}

function Get-WtwCmuxWorkspaceCwd {
    [CmdletBinding()]
    param([Parameter(Mandatory)] $Workspace)

    return Get-WtwCmuxObjectValue -Object $Workspace -Names @(
        'cwd',
        'path',
        'workingDirectory',
        'currentWorkingDirectory',
        'sidebar.cwd',
        'sidebarState.cwd'
    )
}

function Get-WtwCmuxLiveWorkspaces {
    [CmdletBinding()]
    param()

    $result = Invoke-WtwCmuxCommand -ArgumentList @('list-workspaces')
    if ($result.ExitCode -ne 0) { return @() }

    $parsed = ConvertFrom-WtwCmuxJsonOutput -Output $result.Output
    if ($parsed) {
        if ($parsed -is [array]) { return @($parsed) }
        if ($parsed.PSObject.Properties.Name -contains 'workspaces') { return @($parsed.workspaces) }
        return @($parsed)
    }

    return ConvertFrom-WtwCmuxWorkspaceListOutput -Output $result.Output
}

function Find-WtwCmuxWorkspace {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $ProjectPath,
        [Parameter(Mandatory)][string] $PrettyName
    )

    $fullPath = [System.IO.Path]::GetFullPath($ProjectPath)
    $workspaces = Get-WtwCmuxLiveWorkspaces
    if ($workspaces.Count -eq 0) { return $null }

    $byCwd = $workspaces | Where-Object {
        $cwd = Get-WtwCmuxWorkspaceCwd -Workspace $_
        $cwd -and [string]::Equals([System.IO.Path]::GetFullPath("$cwd"), $fullPath, [System.StringComparison]::OrdinalIgnoreCase)
    } | Select-Object -First 1
    if ($byCwd) { return $byCwd }

    return $workspaces | Where-Object {
        [string]::Equals((Get-WtwCmuxWorkspaceName -Workspace $_), $PrettyName, [System.StringComparison]::OrdinalIgnoreCase)
    } | Select-Object -First 1
}

function Set-WtwCmuxWorkspaceMetadata {
    [CmdletBinding()]
    param(
        [string] $WorkspaceRef,
        [string] $PrettyName,
        [string] $Color,
        [string] $StatusValue,
        [string] $CurrentName,
        [string] $CurrentColor
    )

    if (-not $WorkspaceRef) { return }

    if ($PrettyName -and -not [string]::Equals($CurrentName, $PrettyName, [System.StringComparison]::Ordinal)) {
        Invoke-WtwCmuxCommand -ArgumentList @('workspace-action', '--workspace', $WorkspaceRef, '--action', 'rename', '--title', $PrettyName) | Out-Null
    }
    if ($Color -and -not [string]::Equals($CurrentColor, $Color, [System.StringComparison]::OrdinalIgnoreCase)) {
        Invoke-WtwCmuxCommand -ArgumentList @('workspace-action', '--workspace', $WorkspaceRef, '--action', 'set-color', '--color', $Color) | Out-Null
    }
    if ($StatusValue) {
        Invoke-WtwCmuxCommand -ArgumentList @('set-status', 'wtw', $StatusValue, '--workspace', $WorkspaceRef, '--icon', 'git-branch', '--color', ($Color ?? '#7A4FD8'), '--priority', '90') | Out-Null
    }
}

function Open-WtwCmuxWorkspace {
    <#
    .SYNOPSIS
        Open a wtw target as a cmux workspace.
    .DESCRIPTION
        Selects an existing live cmux workspace for the target path/name when
        possible. Otherwise creates a new cmux workspace with the target cwd,
        name, and color. Falls back to `cmux <path>` when socket-driven creation
        is unavailable, which also launches cmux when needed.
    .PARAMETER Target
        Resolved wtw target object (output of Resolve-WtwTarget).
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [psobject] $Target
    )

    if (-not (Test-WtwCmuxPresent)) {
        Write-Error "cmux is not installed or not on PATH. Install cmux or symlink '/Applications/cmux.app/Contents/Resources/bin/cmux'."
        return
    }

    $dir = if ($Target.WorktreeEntry) { $Target.WorktreeEntry.path } else { $Target.RepoEntry.mainPath }
    if (-not ($dir -and (Test-Path $dir))) {
        Write-Error 'No directory found for cmux target.'
        return
    }

    $fullDir = [System.IO.Path]::GetFullPath($dir)
    $prettyName = if ($Target.WorktreeEntry -and $Target.WorktreeEntry.PSObject.Properties.Name -contains 'prettyName' -and $Target.WorktreeEntry.prettyName) {
        $Target.WorktreeEntry.prettyName
    } elseif ($Target.TaskName) {
        $Target.TaskName
    } else {
        Split-Path $fullDir -Leaf
    }
    $color = if ($Target.WorktreeEntry -and $Target.WorktreeEntry.PSObject.Properties.Name -contains 'color') { $Target.WorktreeEntry.color } else { $null }
    $statusValue = if ($Target.TaskName) { "$($Target.RepoName)/$($Target.TaskName)" } else { $Target.RepoName }

    Register-WtwCmuxProject `
        -ProjectPath $fullDir `
        -PrettyName $prettyName `
        -Color $color `
        -RepoName $Target.RepoName `
        -TaskName $Target.TaskName | Out-Null

    $existing = Find-WtwCmuxWorkspace -ProjectPath $fullDir -PrettyName $prettyName
    if ($existing) {
        $workspaceRef = Get-WtwCmuxWorkspaceRef -Workspace $existing
        if ($workspaceRef) {
            $selectResult = Invoke-WtwCmuxCommand -ArgumentList @('select-workspace', '--workspace', "$workspaceRef")
            if ($selectResult.ExitCode -eq 0) {
                Set-WtwCmuxWorkspaceMetadata `
                    -WorkspaceRef "$workspaceRef" `
                    -PrettyName $prettyName `
                    -Color $color `
                    -StatusValue $statusValue `
                    -CurrentName (Get-WtwCmuxWorkspaceName -Workspace $existing) `
                    -CurrentColor (Get-WtwCmuxObjectValue -Object $existing -Names @('color', 'workspace.color', 'sidebar.color', 'sidebarState.color'))
                Write-Host " cmux: selected workspace '$prettyName'" -ForegroundColor Green
                return
            }
        }
    }

    $cmuxArgs = @('new-workspace', '--name', $prettyName, '--cwd', $fullDir, '--focus', 'true')
    if ($statusValue) {
        $cmuxArgs += @('--description', "wtw: $statusValue")
    }
    $createResult = Invoke-WtwCmuxCommand -ArgumentList $cmuxArgs
    if ($createResult.ExitCode -ne 0) {
        if (Open-WtwCmuxAppPath -ProjectPath $fullDir) {
            Write-Host " Opening in cmux: $fullDir" -ForegroundColor Green
            return
        }

        Write-Error "cmux workspace create failed: $($createResult.Output)"
        return
    }

    $currentResult = Invoke-WtwCmuxCommand -ArgumentList @('current-workspace')
    $workspaceRef = $null
    if ($currentResult.ExitCode -eq 0) {
        $currentWorkspace = ConvertFrom-WtwCmuxCurrentWorkspaceOutput -Output $currentResult.Output
        if ($currentWorkspace) {
            $workspaceRef = Get-WtwCmuxWorkspaceRef -Workspace $currentWorkspace
        }
    }
    if (-not $workspaceRef) {
        $created = Find-WtwCmuxWorkspace -ProjectPath $fullDir -PrettyName $prettyName
        if ($created) {
            $workspaceRef = Get-WtwCmuxWorkspaceRef -Workspace $created
        }
    }

    Set-WtwCmuxWorkspaceMetadata -WorkspaceRef "$workspaceRef" -PrettyName $prettyName -Color $color -StatusValue $statusValue -CurrentName $prettyName -CurrentColor $null
    Write-Host " Opening in cmux: $fullDir" -ForegroundColor Green
}