Launch profile manager for Claude Code and Codex.
pm keeps multiple provider setups — endpoint, API key, model — as named profiles, and launches claude or codex with the right credentials for each one. Switch between a company gateway, a personal Anthropic key, AWS Bedrock, and your claude.ai subscription without editing environment variables or global settings files.
- Interactive TUI — bare
pmopens a two-pane picker (profiles / models) with search and per-profile recent models - Secure key storage — OS keychain, environment variable, shell command, or literal
- Clean auth handoff — the API key is delivered via a per-invocation
apiKeyHelper, so your claude.ai login is untouched and Claude Code prints no auth-conflict warnings - Multiple subscription accounts —
pm login <profile>binds a claude.ai account to a profile via a long-lived OAuth token, so work and personal subscriptions can coexist on one machine - Per-directory model memory —
pmremembers which model you last used for each profile in each directory
Requires Go 1.26+ and claude (and/or codex) on your PATH.
go install github.com/boriswu0212/profile-manager/cmd/pm@latestOr from a checkout:
git clone https://github.com/boriswu0212/profile-manager
cd profile-manager
go install ./cmd/pmPrebuilt binaries for macOS and Linux (amd64/arm64) are attached to GitHub Releases — download the archive for your platform, extract, and put pm somewhere on your PATH. Binaries are unsigned; if macOS quarantines a browser download, clear it with xattr -d com.apple.quarantine pm.
pm add # create a profile interactively
pm # open the TUI picker, Enter launches claude
pm run work # or launch a profile directly
pm use work # set the default profile
pm run # launches the default profilePass arguments through to claude after --:
pm run work -- -p "explain this repo"
pm run work -m claude-opus-4-8 -- --continue| Command | Description |
|---|---|
pm |
Open the TUI profile/model picker |
pm add |
Add a profile interactively |
pm list (ls) |
List profiles (* marks the default) |
pm use <profile> |
Set the default profile |
pm run [profile] [-m model] [-- args...] |
Launch claude/codex with a profile; extra args go to the tool |
pm status |
Show the active profile and recent usage |
pm models [profile] |
List models available from the profile's endpoint |
pm login <profile> [--paste] [--account label] |
Bind a claude.ai subscription account to a profile (creates it if needed) |
pm logout <profile> |
Remove a profile's stored subscription token |
pm remove <profile> (rm) |
Remove a profile (and its pm login keychain entry, if any) |
All commands accept --config <path> to use a config file other than ~/.pm.yaml. pm --version prints the build version.
| Key | Action |
|---|---|
↑/k, ↓/j |
Move (model list wraps around) |
←/h, →/l/m |
Switch between profile and model panes |
Enter |
Launch with the selected profile (and model, if in the model pane) |
/ |
Search models |
s |
Set selected profile as default, or selected model as the profile's default |
d |
Delete the selected profile (profile pane only) |
q, Ctrl+C |
Quit |
While searching: type to filter, ↑/↓ to move, Enter to launch, Ctrl+S to set the filtered model as default, Esc to cancel.
The model pane pins up to three recently used models for the selected profile above the full list.
Profiles live in ~/.pm.yaml. The file can contain literal API keys, so pm keeps it at mode 0600 and warns if the permissions are looser.
default_profile: work
profiles:
- name: work
provider: anthropic
base_url: https://gateway.example.com/v1
api_key: keychain://work-gateway
model: claude-sonnet-4-20250514
- name: personal
provider: subscription # use the claude.ai login, no key needed
- name: bedrock
provider: bedrock
region: us-east-1
aws_profile: dev
model: us.anthropic.claude-sonnet-4-20250514-v1:0
- name: codex
tool: codex
provider: openai
base_url: https://api.openai.com/v1
api_key_cmd: op read op://vault/openai/key
model: gpt-5.4-mini-codex| Field | Description |
|---|---|
name |
Profile identifier |
tool |
claude (default) or codex |
provider |
anthropic, openai, bedrock, or subscription |
base_url |
API endpoint (anthropic/openai). Keep the /v1 suffix; pm strips it where Claude Code requires that |
api_key |
API key — see key sources below |
api_key_cmd |
Shell command whose stdout is the key (used when api_key is empty) |
model |
Default model for this profile |
region, aws_profile |
AWS settings (bedrock only) |
env_key |
Env var name the key is injected into for codex (default PM_CODEX_API_KEY) |
The same file also stores usage state maintained by pm: recent (last 10 profile+model launches, shown in the TUI and pm status) and last_models (per-directory last-used model, up to 50 entries).
api_key is interpreted by its form:
keychain://name— OS keychain (macOS Keychain, Windows Credential Manager, or Linux Secret Service), servicepm, accountname. Seed it on macOS withsecurity add-generic-password -s pm -a name -w${ENV_VAR}— read from the environment at launch time- anything else — used literally
When api_key is empty, api_key_cmd is run via sh -c and its trimmed stdout becomes the key.
bedrock profiles need no key (AWS credentials are used). subscription profiles need none either — but can optionally hold an OAuth token bound by pm login (stored as keychain://<profile> in api_key), which pins the launch to a specific claude.ai account.
Claude Code itself keeps exactly one claude.ai login per machine (a single Keychain slot — logging into a second account overwrites the first). pm works around this with per-profile OAuth tokens:
pm login work # browser signed into the work claude.ai account
pm login personal # switch the browser to the personal account first
pm run work # launches claude as the work account
pm run personal # launches claude as the personal accountpm login runs claude setup-token (a browser OAuth flow that prints a 1-year token), stores the token in the OS keychain under pm's own namespace, and injects it as CLAUDE_CODE_OAUTH_TOKEN at launch. Which account a login binds to is decided by whichever claude.ai account is active in your browser during the flow — use a second browser profile or a private window for the second account.
Notes:
- Requires a Pro, Max, Team, or Enterprise plan (
claude setup-tokenrejects free accounts). - Running
setup-tokenagain for the same account invalidates that account's previous token. To reuse a token minted elsewhere (e.g. on another machine), store it withpm login <profile> --paste. - A subscription profile without a token keeps the old behavior: it uses the machine's shared claude.ai login.
- Rate limits are per account, not per profile.
- Token-authed sessions show a "Claude API" launch banner (and
claude auth statusreportsauthMethod: oauth_token) instead of the plan name. This is cosmetic — ansk-ant-oat…token always bills the subscription it belongs to, never pay-per-token API. Verify with/usageinside the session: plan-style rate-limit windows mean subscription billing. - Claude Code's UI can't tell you which token a session runs on (setup-tokens carry only
user:inferencescope, so even the API refuses to identify them). pm compensates at launch: every run printspm ▸ profile "personal" · claude · subscription (you@example.com · token ab12cd34)above the banner and exportsPM_PROFILEfor in-session checks (!echo $PM_PROFILE). The account label is what you declare atpm login(prompted, or--account you@example.com); the fingerprint is a live sha256 prefix of the token actually being used. - Re-running
pm loginon a profile tells you exactly what happened to the token:Token 5bf470b9 stored — replaces ccf4495b(orunchanged, same token as before).pm statusandpm listshow the fingerprint, account label, and bind date.
The model for a launch is chosen in this order:
-m/--modelflag (or the model picked in the TUI)- The model you last used for this profile in the current directory
- The profile's
modelfield
Every launch records its model per (directory, profile) in ~/.pm.yaml, and that remembered model becomes the effective default the next time you run the same profile from the same directory — it takes precedence over the profile's model field until you override it again with -m or the TUI. Each project directory therefore sticks to its own model per profile.
pm run resolves credentials, prepares the environment, and replaces itself with claude/codex (exec), so there is no wrapper process left running.
- anthropic / openai — sets
ANTHROPIC_BASE_URL(with any trailing/v1stripped, since Claude Code appends its own) and passes the key viaclaude --settings '{"apiKeyHelper": ...}', along withdisableClaudeAiConnectors: true(connectors can't work through a gateway). The helper is the hiddenpm _resolve-key <profile>command.ANTHROPIC_API_KEYandANTHROPIC_AUTH_TOKENare cleared from the environment rather than set, and the settings only apply to that invocation — other sessions keep their claude.ai login. Note: any pre-existingapiKeyHelperentry in~/.claude/settings.jsonis removed before launch. - bedrock — clears
ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKEN, andANTHROPIC_BASE_URL, then setsCLAUDE_CODE_USE_BEDROCK=1,AWS_REGION, andAWS_PROFILE. - subscription — clears
ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKEN,ANTHROPIC_BASE_URL, and Bedrock/Vertex overrides. With a token bound viapm login, setsCLAUDE_CODE_OAUTH_TOKENsoclauderuns as that account; otherwise it also clears that variable andclaudeuses your claude.ai login. - codex (
tool: codex) — writes~/.codex/config.tomlwith apmprovider pointing at the profile'sbase_url, and injects the key via theenv_keyenv var. Note: the previousconfig.tomlis not restored after codex exits; keep a copy if you maintain one by hand.
go test ./...
go vet ./...
go build -o pm ./cmd/pmSee CLAUDE.md for architecture notes.
MIT. pm only manages launch profiles and hands credentials to claude/codex (its sole API use is listing models for the picker) — it is provided as-is, without warranty of any kind.