# lib/core.ps1 - state.json, presets, and which preset a mode resolves to. # # Part of claude-mode.ps1, which dot-sources it into its own script scope after # the settings at its top. Not meant to run on its own. ASCII only: Windows # PowerShell 5.1 reads a .ps1 without a BOM as ANSI. $PSScriptRoot here would be # lib\, so paths beside the main script go through $script:Here. # --------------------------------------------------------------------------- # State / presets # --------------------------------------------------------------------------- function Initialize-Root { foreach ($d in @($script:Root, $script:PresetDir, $script:VaultDir, $script:BackupDir, $script:BinDir)) { if (-not (Test-Path -LiteralPath $d)) { New-Item -ItemType Directory -Path $d -Force | Out-Null } } } function Get-State { $s = Read-JsonFile $script:StatePath if ($null -eq $s) { $s = [ordered]@{} } if (-not $s.Contains('mode')) { $s['mode'] = 'anthropic' } if (-not $s.Contains('preset')) { $s['preset'] = '' } if (-not $s.Contains('writtenEnvKeys')) { $s['writtenEnvKeys'] = @() } return $s } function Set-State { param([string] $Mode, [string] $PresetName, [string[]] $WrittenKeys) $s = Get-State $s['mode'] = $Mode $s['preset'] = $PresetName $s['writtenEnvKeys'] = @($WrittenKeys) $s['updated'] = (Get-Date).ToString('o') # Written by older builds that picked "most recently used" presets; the # per-provider default is fixed now, so nothing maintains this. if ($s.Contains('lastByProvider')) { $s.Remove('lastByProvider') } Write-JsonFile $script:StatePath $s } function Get-PresetPath { param([string] $Name) return (Join-Path $script:PresetDir "$Name.json") } function Get-Preset { param([string] $Name) $p = Read-JsonFile (Get-PresetPath $Name) if ($null -eq $p) { throw "preset '$Name' not found. Run: claude-mode presets" } if (-not $p.Contains('provider')) { $p['provider'] = 'openrouter' } return $p } function Get-PresetNames { if (-not (Test-Path -LiteralPath $script:PresetDir)) { return @() } return @(Get-ChildItem -LiteralPath $script:PresetDir -Filter '*.json' | ForEach-Object { $_.BaseName } | Sort-Object) } function Get-PresetNamesForProvider { param([string] $Provider) $out = @() foreach ($n in Get-PresetNames) { try { if ([string](Get-Preset $n)['provider'] -eq $Provider) { $out += $n } } catch { } } return $out } function Resolve-PresetForProvider { param([string] $Provider, [string] $Requested) if ($Requested) { $p = Get-Preset $Requested if ([string]$p['provider'] -ne $Provider) { throw "preset '$Requested' is a '$($p['provider'])' preset, not '$Provider'" } return $Requested } # Fixed per-provider default, so `claude-mode openrouter` is predictable # rather than depending on what you last used or on alphabetical order. $fallback = $script:ProviderDefaultPreset[$Provider] if ($fallback -and (Test-Path -LiteralPath (Get-PresetPath $fallback))) { if ([string](Get-Preset $fallback)['provider'] -eq $Provider) { return $fallback } } $names = Get-PresetNamesForProvider $Provider if ($names.Count -gt 0) { return $names[0] } throw "no preset found for provider '$Provider'" }