Public/Ccs.ps1

# The `ccs` front door — a git-style dispatcher over the cmdlets.

# Subcommand names win over profile names in the bare-name shorthand, so a
# profile may not be called one of these.
$script:CcsReservedNames = @(
    'list', 'ls', 'use', 'run', 'status', 'doctor', 'capture', 'remove', 'aws', 'help',
    'adopt', 'backup-personal', 'new', 'rename'
)

function ccs {
    <#
    .SYNOPSIS
        Switch Claude Code between config profiles (subscription / Bedrock).
    .EXAMPLE
        ccs list
    .EXAMPLE
        ccs use work
    .EXAMPLE
        ccs run personal -- --resume
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)][string]$Command = 'status',
        [Parameter(ValueFromRemainingArguments)][string[]]$Rest
    )

    $args1 = @($Rest | Where-Object { $_ -ne '--' })

    switch ($Command.ToLowerInvariant()) {
        'list'    { Get-CcsProfileList; return }
        'ls'      { Get-CcsProfileList; return }

        'use' {
            if (-not $args1) { throw 'Usage: ccs use <profile>' }
            Show-CcsSwitchResult (Use-CcsProfile -Name $args1[0])
            return
        }

        'run' {
            if (-not $args1) { throw 'Usage: ccs run <profile> [-- <claude args>]' }
            $forwarded = @()
            if ($Rest.Count -gt 1) {
                $idx = [Array]::IndexOf($Rest, '--')
                $forwarded = if ($idx -ge 0) { $Rest[($idx + 1)..($Rest.Count - 1)] }
                             else { $Rest[1..($Rest.Count - 1)] }
            }
            Start-CcsClaude -Name $args1[0] -ClaudeArgs $forwarded
            return
        }

        'status' {
            if ($args1) { Get-CcsStatus -Name $args1[0] } else { Get-CcsStatus }
            return
        }

        'doctor' {
            $findings = @(Test-CcsHealth)
            if (-not $findings) {
                Write-Host 'OK — no problems found.' -ForegroundColor Green
                return
            }
            foreach ($f in $findings) {
                $colour = switch ($f.Severity) {
                    'Error'   { 'Red' }
                    'Warning' { 'Yellow' }
                    default   { 'DarkGray' }
                }
                Write-Host ("[{0}] {1}" -f $f.Severity.ToUpper(), $f.Check) -ForegroundColor $colour
                Write-Host (" {0}" -f $f.Message)
                if ($f.Fix) { Write-Host (" fix: {0}" -f $f.Fix) -ForegroundColor DarkGray }
            }
            return
        }

        'capture' {
            if (-not $args1) {
                throw 'Usage: ccs capture <profile> [-From <dir>] [-FromSettingsFile <file>] [-Note <text>] [-SettingsOnly] [-LinkShared] [-Force]'
            }
            $params = @{ Name = $args1[0] }
            foreach ($switchName in @('Force', 'SettingsOnly', 'LinkShared')) {
                if ($Rest | Where-Object { $_ -eq "-$switchName" }) { $params[$switchName] = $true }
            }
            foreach ($valued in @('From', 'FromSettingsFile', 'Note')) {
                $idx = [Array]::FindIndex([string[]]$Rest, [Predicate[string]] { param($x) $x -eq "-$valued" })
                if ($idx -ge 0 -and $idx + 1 -lt $Rest.Count) { $params[$valued] = $Rest[$idx + 1] }
            }
            $p = New-CcsProfileFromCurrent @params
            Write-Host "Captured '$($p.Name)' [$($p.Backend)] → $($p.Dir)" -ForegroundColor Green
            return
        }

        'new' {
            if (-not $args1) { throw 'Usage: ccs new <profile> [-Blank] [-Note <text>] [-Force]' }
            $params = @{ Name = $args1[0] }
            foreach ($switchName in @('Blank', 'Force')) {
                if ($Rest | Where-Object { $_ -eq "-$switchName" }) { $params[$switchName] = $true }
            }
            $idx = [Array]::FindIndex([string[]]$Rest, [Predicate[string]] { param($x) $x -eq '-Note' })
            if ($idx -ge 0 -and $idx + 1 -lt $Rest.Count) { $params['Note'] = $Rest[$idx + 1] }

            $p = New-CcsBlankProfile @params
            Write-Host "Created '$($p.Name)' -> $($p.Dir)" -ForegroundColor Green
            Write-Host " Sign in with: ccs run $($p.Name)" -ForegroundColor DarkGray
            return
        }

        'rename' {
            if ($args1.Count -lt 2) { throw 'Usage: ccs rename <old> <new>' }
            $p = Rename-CcsProfileEntry -Name $args1[0] -NewName $args1[1]
            Write-Host "Renamed '$($args1[0])' -> '$($p.Name)' [$($p.Backend)] $($p.Dir)" -ForegroundColor Green
            return
        }

        'backup-personal' {
            $path = Backup-CcsPersonalSettings -Force:($Rest -contains '-Force')
            Write-Host "Personal settings baseline saved: $path" -ForegroundColor Green
            return
        }

        'adopt' {
            $target = if ($args1) { $args1[0] } else { 'work' }
            $result = Invoke-CcsAdopt -Name $target -KeepLive:($Rest -contains '-KeepLive')
            Write-Host "Adopted company settings into '$($result.Profile)' [$($result.Backend)]" -ForegroundColor Green
            if ($result.PersonalRestored) {
                Write-Host ' ~/.claude restored to your personal settings' -ForegroundColor DarkGray
            }
            $errors = @($result.Findings | Where-Object Severity -eq 'Error')
            if ($errors) {
                Write-Host " $($errors.Count) problem(s) found - run: ccs doctor" -ForegroundColor Yellow
            }
            return
        }

        'remove' {
            if (-not $args1) { throw 'Usage: ccs remove <profile> [-DeleteFiles]' }
            Remove-CcsProfileEntry -Name $args1[0] -DeleteFiles:($Rest -contains '-DeleteFiles')
            return
        }

        'aws' {
            if ($args1) { Get-CcsAwsIdentity -Name $args1[0] } else { Get-CcsAwsIdentity }
            return
        }

        'help'   { Show-CcsHelp; return }
        '--help' { Show-CcsHelp; return }
        '-h'     { Show-CcsHelp; return }

        default {
            # A bare profile name is shorthand for `ccs use <name>` - the shape
            # every other switcher uses, and the one people reach for first.
            $known = (Get-CcsConfig).profiles.PSObject.Properties.Name
            if ($Command -in $known) {
                Show-CcsSwitchResult (Use-CcsProfile -Name $Command)
                return
            }
            throw "Unknown command or profile '$Command'. Profiles: $($known -join ', '). Run 'ccs help'."
        }
    }
}

function Show-CcsSwitchResult {
    <# Human-facing confirmation shared by `ccs use` and the bare-name shorthand. #>
    param([Parameter(Mandatory)]$Profile)

    Write-Host "-> $($Profile.Name) [$($Profile.Backend)] $($Profile.Dir)" -ForegroundColor Green
    Write-Host ' This shell only. Other terminals and Claude Desktop are unchanged.' -ForegroundColor DarkGray
}

function Show-CcsHelp {
    @'
ccs — Claude Code profile switcher

  ccs <profile> Shorthand for 'ccs use <profile>'
  ccs list Show profiles, backend and which one this shell uses
  ccs use <profile> Point THIS shell at a profile (CLAUDE_CONFIG_DIR)
  ccs run <profile> [-- args] Launch claude under a profile, shell untouched
  ccs status [profile] Backend, model pins, telemetry, AWS state
  ccs doctor Diagnose conflicts (read-only, never logs in)
  ccs new <profile> Create an empty profile to sign a second account into
  ccs rename <old> <new> Rename a profile (moves its folder when managed)
  ccs capture <profile> Snapshot ~/.claude settings into a new profile
  ccs backup-personal Save ~/.claude/settings.json as your personal baseline
  ccs adopt [profile] After the company install.bat: move its settings into
                               the profile (default 'work') and restore your own
  ccs remove <profile> Unregister a profile (-DeleteFiles to erase it)
  ccs aws [profile] aws sts get-caller-identity (may open a browser)

Profiles live in ~/.claude-profiles; the 'personal' profile is ~/.claude itself.
Claude Desktop / Cowork follow a machine policy and are NOT switched by ccs.
'@
 | Write-Host
}