Private/ProfileStore.ps1
|
# The profile registry: ccs.json. Small on purpose — it stores only what cannot # be derived. Backend (subscription vs bedrock) is always read from the profile's # own settings.json so the registry can never drift out of sync with reality. $script:CcsConfigVersion = 1 function New-CcsConfig { <# .SYNOPSIS A fresh registry pre-seeded with the built-in 'personal' profile (~/.claude). #> [pscustomobject]@{ version = $script:CcsConfigVersion profiles = [pscustomobject]@{ personal = [pscustomobject]@{ dir = Resolve-CcsConfigDirValue (Get-CcsDefaultClaudeHome) note = 'Default Claude Code config home' } } } } function Get-CcsConfig { <# .SYNOPSIS Read the registry, returning a seeded default when it does not exist yet. #> $config = Read-CcsJson -Path (Get-CcsConfigPath) if (-not $config) { return New-CcsConfig } if (-not $config.PSObject.Properties['profiles']) { throw "Registry at $(Get-CcsConfigPath) has no 'profiles' section" } $config } function Save-CcsConfig { param([Parameter(Mandatory)]$Config) Write-CcsJson -Path (Get-CcsConfigPath) -Value $Config } function Add-CcsConfigProfile { <# .SYNOPSIS Return a NEW registry with one profile added or replaced. #> param( [Parameter(Mandatory)]$Config, [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$Dir, [string]$Note = '' ) $profiles = [pscustomobject]@{} foreach ($p in $Config.profiles.PSObject.Properties) { if ($p.Name -eq $Name) { continue } Add-Member -InputObject $profiles -NotePropertyName $p.Name -NotePropertyValue $p.Value } Add-Member -InputObject $profiles -NotePropertyName $Name -NotePropertyValue ([pscustomobject]@{ dir = Resolve-CcsConfigDirValue $Dir note = $Note }) [pscustomobject]@{ version = $script:CcsConfigVersion profiles = $profiles } } function Remove-CcsConfigProfile { <# .SYNOPSIS Return a NEW registry without the named profile. #> param( [Parameter(Mandatory)]$Config, [Parameter(Mandatory)][string]$Name ) $profiles = [pscustomobject]@{} foreach ($p in $Config.profiles.PSObject.Properties) { if ($p.Name -eq $Name) { continue } Add-Member -InputObject $profiles -NotePropertyName $p.Name -NotePropertyValue $p.Value } [pscustomobject]@{ version = $script:CcsConfigVersion; profiles = $profiles } } function Resolve-CcsProfile { <# .SYNOPSIS Look up one profile by name and describe it, reading its live settings. .OUTPUTS Name, Dir, Note, Exists, Backend ('bedrock'|'subscription'|'unknown'), Settings (parsed settings.json or $null), Env (its env block as a hashtable). #> param( [Parameter(Mandatory)][string]$Name, $Config = (Get-CcsConfig) ) $entry = $Config.profiles.PSObject.Properties[$Name] if (-not $entry) { $known = ($Config.profiles.PSObject.Properties.Name | Sort-Object) -join ', ' throw "Unknown profile '$Name'. Known profiles: $known" } $dir = $entry.Value.dir $settingsPath = Get-CcsSettingsPath -ProfileDir $dir $settings = $null try { $settings = Read-CcsJson -Path $settingsPath } catch { Write-Warning $_.Exception.Message } $backend = 'unknown' if ($settings) { $backend = if (Test-CcsBedrockSettings -Settings $settings) { 'bedrock' } else { 'subscription' } } [pscustomobject]@{ Name = $Name Dir = $dir Note = $entry.Value.note Exists = Test-Path -LiteralPath $dir SettingsPath = $settingsPath Settings = $settings Env = Get-CcsSettingsEnv -Settings $settings Backend = $backend } } function Get-CcsActiveProfileName { <# .SYNOPSIS Which profile this shell is currently pointed at, by CLAUDE_CONFIG_DIR. .DESCRIPTION An unset CLAUDE_CONFIG_DIR means Claude Code uses ~/.claude, so it maps to whichever profile registers that directory. Returns $null when the env var points somewhere no profile claims. #> param($Config = (Get-CcsConfig)) $current = $env:CLAUDE_CONFIG_DIR if ([string]::IsNullOrWhiteSpace($current)) { $current = Get-CcsDefaultClaudeHome } $normalized = Resolve-CcsConfigDirValue $current foreach ($p in $Config.profiles.PSObject.Properties) { if ($p.Value.dir -eq $normalized) { return $p.Name } } $null } |