Private/Paths.ps1

# Path resolution for ccs. Every path is derived, never hardcoded at call sites,
# so tests can redirect the whole tree by setting $script:CcsRootOverride.

$script:CcsHomeOverride = $null

function Get-CcsHome {
    <#
    .SYNOPSIS
        The user home directory every other path derives from.
    .DESCRIPTION
        Indirection exists so tests can relocate the whole tree; $HOME itself is
        engine-owned and cannot be reassigned reliably.
    #>

    if ($script:CcsHomeOverride) { return $script:CcsHomeOverride }
    $HOME
}

function Set-CcsHomeOverride {
    <#
    .SYNOPSIS
        Redirect all ccs paths at a sandbox directory (tests only). $null restores.
    #>

    param([string]$Path)
    $script:CcsHomeOverride = $Path
}

function Get-CcsRoot {
    <#
    .SYNOPSIS
        Root directory holding ccs profiles and its own config file.
    #>

    Join-Path (Get-CcsHome) '.claude-profiles'
}

function Get-CcsConfigPath {
    Join-Path (Get-CcsRoot) 'ccs.json'
}

function Get-CcsDefaultClaudeHome {
    <#
    .SYNOPSIS
        Claude Code's built-in config home (~/.claude), ignoring CLAUDE_CONFIG_DIR.
    .DESCRIPTION
        Deliberately ignores the env var: a profile-aware command must be able to
        name the real default profile even while running inside another one.
    #>

    Join-Path (Get-CcsHome) '.claude'
}

function Get-CcsProfileDir {
    param([Parameter(Mandatory)][string]$Name)
    Join-Path (Get-CcsRoot) $Name
}

function Get-CcsSettingsPath {
    param([Parameter(Mandatory)][string]$ProfileDir)
    Join-Path $ProfileDir 'settings.json'
}

function Get-CcsAwsConfigPath {
    Join-Path (Get-CcsHome) '.aws\config'
}

function Get-CcwbHome {
    <#
    .SYNOPSIS
        Where the company CCWB package installs its credential process and helpers.
    #>

    Join-Path (Get-CcsHome) 'claude-code-with-bedrock'
}

function Get-CcsLockDirs {
    <#
    .SYNOPSIS
        Claude Code's own advisory lock directories for a config home.
    .DESCRIPTION
        Claude Code guards OAuth refresh with proper-lockfile, where the lock
        artifact is a *directory*. Ordered as Claude Code takes them: the
        primary refresh lock first, then the legacy one it still acquires for
        compatibility with external tools, then the global-config lock.
    #>

    param([Parameter(Mandatory)][string]$ConfigHome)

    [pscustomobject]@{
        OauthRefresh = Join-Path $ConfigHome '.oauth_refresh.lock'
        Legacy       = "$ConfigHome.lock"
        Config       = Join-Path (Split-Path $ConfigHome -Parent) '.claude.json.lock'
    }
}

function Resolve-CcsConfigDirValue {
    <#
    .SYNOPSIS
        Normalize a directory into the exact string to export as CLAUDE_CONFIG_DIR.
    .DESCRIPTION
        On macOS Claude Code derives its keychain service name from a SHA-256 of
        the raw env var string, so a trailing separator or a './' prefix would
        select a different credential entry. Windows is file-based and immune,
        but normalizing here keeps profiles portable and comparisons stable.
    #>

    param([Parameter(Mandatory)][string]$Path)
    $trimmed = $Path.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar)
    if ([string]::IsNullOrWhiteSpace($trimmed)) { return $Path }
    $trimmed
}