Manage a local registry of Claude login snapshots for CLI selection and Python SDK usage.
Project description
claude-select 🔐
claude-select is a local Claude auth registry and selector for people who use multiple Claude accounts on one machine.
It captures the current Claude CLI login state, stores each account as a snapshot in a local SQLite registry, shows expiry status in a table, and lets you:
- select one stored account back into Claude's live auth state for CLI use
- read one stored account from Python and build
envfor Claude Agent SDK use
It does not auto-refresh OAuth tokens. It trusts the captured expiresAt value and asks the user to log in again when an account is near expiry or expired.
Install 🚀
pip install claude-select
How Users Get Started 👇
1. Capture your accounts
Run the guided bootstrap:
claude-select init
By default, claude-select launches claude in the current terminal for each account capture.
For each account:
- choose an alias such as
workorpersonal claude-selectlaunchesclaude- inside the
claudeCLI session, run/loginand finish authorization - exit
claudeand return toclaude-select - press Enter so
claude-selectcan capture the current login snapshot
You can add another account later:
claude-select add work
claude-select add personal
If you do not want claude-select to launch claude for you, use:
claude-select add work --no-launch
In that mode, claude-select will print guidance and wait while you run claude and /login yourself.
2. See what is stored
claude-select list
claude-select watch
Example table:
Alias Email Status Expires In Last Selected
-------- ---------------- -------------- ---------- -------------
personal a@example.com healthy 18h 12m 2h ago
work b@company.com expiring_soon 1h 05m -
team-a c@company.com expired expired 3d ago
3. Select an account for Claude CLI
claude-select select work
This reads the stored snapshot from the local registry and writes it back into Claude's current live auth backend:
- macOS: Keychain + Claude config
- Linux / Windows: Claude credentials file + Claude config
4. Use an account in Python
from claude_select import AuthManager
from claude_code_sdk import ClaudeAgentOptions, query
manager = AuthManager()
env = manager.build_sdk_env("work")
options = ClaudeAgentOptions(env=env)
async for message in query(prompt="analyze this repo", options=options):
print(message)
The Python side reads from the same local registry, but it does not mutate Claude's live auth state.
CLI Commands 🧰
claude-select init
claude-select add <alias>
claude-select relogin <alias>
claude-select list
claude-select watch
claude-select select [alias]
claude-select remove <alias>
claude-select export-env <alias> --json
claude-select current
Command behavior:
init: guided multi-account bootstrapadd: launchclaudein the current terminal by default, then capture the current login into the registryrelogin: launchclaudein the current terminal by default, then overwrite one stored alias after the user logs in againlist: show the current registry tablewatch: keep refreshing the table in the terminalselect: write one stored snapshot back into Claude's live auth stateremove: delete one stored accountexport-env: print SDK env vars for one aliascurrent: show the last alias selected for CLI use
Python API 🐍
Minimal usage:
from claude_select import AuthManager
manager = AuthManager()
accounts = manager.list_accounts()
details = manager.get_account("work")
env = manager.build_sdk_env("work")
auth_payload = manager.export_sdk_auth("work")
Current public surface:
class AuthManager:
def list_accounts(self) -> list[dict]: ...
def get_account(self, alias: str): ...
def capture_current_account(self, alias: str, overwrite: bool = True) -> dict: ...
def relogin_account(self, alias: str) -> dict: ...
def remove_account(self, alias: str) -> None: ...
def select_account(self, alias: str) -> dict: ...
def build_sdk_env(self, alias: str, base_env: dict[str, str] | None = None) -> dict[str, str]: ...
def export_sdk_auth(self, alias: str) -> dict: ...
def current_alias(self) -> str | None: ...
def render_table(self) -> str: ...
Top-level helper:
from claude_select import build_sdk_env
env = build_sdk_env("work")
How Expiry Works ⏳
claude-select does not refresh tokens automatically.
It only reads the stored expiresAt timestamp and derives status from it:
healthy: more than 6 hours remainexpiring_soon: 6 hours or less remainexpired: already expiredunknown: noexpiresAtvalue was captured
When an account is expired:
selectfailsbuild_sdk_env()fails- the user should log in again and run:
claude-select relogin <alias>
Storage Model 🗃️
The registry is stored locally in SQLite:
- macOS / Linux default:
~/.config/claude-select/registry.db
- custom XDG config root:
$XDG_CONFIG_HOME/claude-select/registry.db
Each stored account contains:
- alias
- organization name / id
- account uuid
- captured time
- expiry time
- last selected time
- stored Claude
oauthAccount - stored Claude
claudeAiOauthcredentials payload
Claude's own live state remains separate:
- global Claude config
- Claude credentials file or macOS Keychain
The registry is the source of truth. select copies one stored snapshot back into Claude's live state.
Current Limitations ⚠️
- This project currently centers on captured Claude OAuth snapshots.
- It does not auto-refresh tokens.
- It relies on the structure of Claude's current local auth state.
- Expiry monitoring is local-only; it does not call a remote validation API.
- Selecting an account updates the local machine's active Claude login state.
Development 🛠️
Install dev dependencies:
pip install -e .[dev]
Run checks:
ruff check .
ruff format --check .
mypy
pytest
python -m build
python -m twine check dist/*
License 📄
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_select-0.2.1.tar.gz.
File metadata
- Download URL: claude_select-0.2.1.tar.gz
- Upload date:
- Size: 20.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fb297274435ce283ddb12779277bf4cdef55d8bdda9cb1d2d99dd564c80988a
|
|
| MD5 |
44182cd111c06e5a91da3e04560b062b
|
|
| BLAKE2b-256 |
260ce90f10c52077e369433b58cfc7c12239fde2c94f047039f21897f04b89ea
|
Provenance
The following attestation bundles were made for claude_select-0.2.1.tar.gz:
Publisher:
publish.yml on Nomia/claude-select
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_select-0.2.1.tar.gz -
Subject digest:
9fb297274435ce283ddb12779277bf4cdef55d8bdda9cb1d2d99dd564c80988a - Sigstore transparency entry: 1496407448
- Sigstore integration time:
-
Permalink:
Nomia/claude-select@b628da25e1b3a64a0102cc5e282ccac673c31f36 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/Nomia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b628da25e1b3a64a0102cc5e282ccac673c31f36 -
Trigger Event:
release
-
Statement type:
File details
Details for the file claude_select-0.2.1-py3-none-any.whl.
File metadata
- Download URL: claude_select-0.2.1-py3-none-any.whl
- Upload date:
- Size: 17.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0315c80006682b77d3a7beaa1f6a36291c864cc50c7e9805f6589fd81dd9f2f7
|
|
| MD5 |
cd73fe3b69f531ae8cd8bdeeef9755f1
|
|
| BLAKE2b-256 |
664580d217ab451bd7538bc8c2a511e5c1f80bb81d12b7729e3265026a3cf58a
|
Provenance
The following attestation bundles were made for claude_select-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on Nomia/claude-select
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_select-0.2.1-py3-none-any.whl -
Subject digest:
0315c80006682b77d3a7beaa1f6a36291c864cc50c7e9805f6589fd81dd9f2f7 - Sigstore transparency entry: 1496407549
- Sigstore integration time:
-
Permalink:
Nomia/claude-select@b628da25e1b3a64a0102cc5e282ccac673c31f36 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/Nomia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b628da25e1b3a64a0102cc5e282ccac673c31f36 -
Trigger Event:
release
-
Statement type: