Command reference¶
Four shell commands wrap one cross-platform Python core
(bin/claude-profiles.py). The core can also be called directly - useful in
scripts, or on a shell with no wrapper installed.
| Shell command | Core equivalent |
|---|---|
claude-profile NAME |
(shell only - it must export into your session) |
claude-profiles |
claude-profiles.py status |
claude-sessions |
claude-profiles.py sessions |
claude-handoff |
claude-profiles.py handoff |
claude-profile-remove |
claude-profiles.py remove |
claude-profile-clone |
claude-profiles.py clone |
claude-profile-exec |
(shell only) |
claude-doctor |
claude-profiles.py doctor |
claude-prune |
claude-profiles.py prune |
claude-best |
claude-profiles.py best |
claude-update |
claude-profiles.py update |
claude-auto |
(shell only) |
| - | claude-profiles.py path NAME |
PowerShell users get the same names as aliases, plus verb-noun forms
(Set-ClaudeProfile, Get-ClaudeProfiles, Get-ClaudeSessions,
Move-ClaudeSession, Remove-ClaudeProfile, Test-ClaudeProfiles,
Copy-ClaudeProfile, Invoke-ClaudeProfile).
claude-profile [NAME]¶
Switch the current shell to a profile. With no argument, reports the active one.
claude-profile # which profile am I on?
claude-profile work # switch to "work" (created if it does not exist)
claude-profile default # back to the original ~/.claude
Sets CLAUDE_CONFIG_DIR and CLAUDE_PROFILE_NAME in the current shell. It is a
shell function rather than a script because only a function can modify the
environment of the shell you are sitting in.
Scope is per-shell. Two terminal tabs can be on different accounts at the
same time. An already-running claude is unaffected - the variable is read at
launch, so restart it after switching.
default is a reserved name meaning "no CLAUDE_CONFIG_DIR set", i.e. the
stock ~/.claude. It is not a directory under ~/.claude-profiles.
claude-profiles¶
List every profile, which account is signed into it, whether credentials are present, and how much of each account's usage limit is consumed.
╭───────────┬─────────────────────┬──────┬────────┬───────────┬───────┬───────────┬────────────╮
│ PROFILE │ ACCOUNT │ AUTH │ 5-HOUR │ RESETS │ 7-DAY │ RESETS │ AS OF │
├───────────┼─────────────────────┼──────┼────────┼───────────┼───────┼───────────┼────────────┤
│ * default │ you@example.com │ ok │ 59% │ in 53m │ 40% │ in 4d 23h │ 1h ago │
│ work │ you@company.com │ ok │ 3% │ in 1h 13m │ 87% │ in 2d 18h │ 2h ago │
│ client │ (not logged in) │ none │ - │ - │ - │ - │ never used │
╰───────────┴─────────────────────┴──────┴────────┴───────────┴───────┴───────────┴────────────╯
| Column | Meaning |
|---|---|
* |
active in this shell |
ACCOUNT |
email read from that profile's .claude.json |
AUTH |
ok if credentials exist, none if /login is still needed |
5-HOUR |
percent of the rolling 5-hour limit used, and when it resets |
7-DAY |
percent of the weekly limit used, and when it resets |
AS OF |
how old the usage figures are |
Percentages are colour-coded: green below 75%, amber from 75%, red from 90%.
A locked cell means that limit is currently exhausted.
There are two independent limits with separate clocks, which is why both get
their own reset column. An account can be fine on one and nearly out on the
other - the example above shows work at 3% for the next five hours but 87%
through its week.
| Flag | Effect |
|---|---|
--dirs |
also show each profile's config directory |
--no-usage |
hide the usage columns |
--plain |
no borders - easier to pipe into awk, grep or a script |
--live |
fetch current figures from the API instead of using the cache |
Live figures¶
Fetches each signed-in profile's current usage directly, in parallel, and shows
live in the AS OF column. This costs no model tokens - it reads a usage
endpoint, it does not run a prompt.
Any profile whose fetch fails silently falls back to its cached snapshot, marked
(cached), so the table always renders. A note below the table says whether any
profile fell back.
What this sends, and where
--live reads that profile's OAuth token from your Keychain (macOS) or its
.credentials.json (elsewhere) and sends it to api.anthropic.com - the
same host Claude Code already authenticates against. Nothing is sent
anywhere else, and nothing is written to disk.
Unofficial endpoint
The usage endpoint is internal to Claude Code and is not a documented public
API. It may change or disappear in any release, which is why the cache
remains the default and --live degrades to it rather than failing.
Why the cache is stale¶
Read the AS OF column
Without --live, figures come from a cache Claude Code writes only when
that profile runs, and it refuses to refetch more than once every five
minutes. A profile you have not used today shows figures from whenever you
last used it; one that has never run shows never used.
The AS OF value turns amber once the snapshot is more than a day old.
The cache stays the default because it is instant and needs no network. Reach
for --live when the numbers actually matter - deciding which account to start
a long session on, for instance.
Two accounts showing ok at once is normal and expected - that is the whole
point. See How it works for where credentials live per OS.
claude-sessions [options]¶
List conversations readably, so you can identify one by what it was about rather than by a 36-character id.
╭───┬────────┬───────┬──────────────────────────────────────┬──────────────────────────────────╮
│ # │ WHEN │ TURNS │ SESSION ID │ SUMMARY │
├───┼────────┼───────┼──────────────────────────────────────┼──────────────────────────────────┤
│ 1 │ 2m ago │ 18 │ eb82f424-4aff-43eb-a0e1-f1e130553fc6 │ refactor the auth middleware… │
│ 2 │ Sep 11 │ 2 │ 2c24a68b-8411-4bb6-9072-99b86b5ae909 │ cant able to take pull │
╰───┴────────┴───────┴──────────────────────────────────────┴──────────────────────────────────╯
The table adapts to your terminal: on a narrow one the SESSION ID column
shortens to an 8-character ID, which is still enough for claude-handoff
since it accepts prefixes.
Defaults to the current directory and the active profile, newest first.
| Flag | Effect |
|---|---|
-a, --all |
every directory, not just the current one. Adds a dim ~/path (branch) line under each row |
-A, --all-profiles |
scan every profile. Adds a PROFILE column |
-p NAME, --profile NAME |
one specific profile |
-n N, --limit N |
max rows (default 20). Bare --limit means no limit |
-f, --full |
wrap long summaries instead of truncating them |
-d DIR, --dir DIR |
filter on a directory other than the current one |
--plain |
no borders - easier to pipe into other tools |
-g PATTERN, --grep PATTERN |
only sessions containing PATTERN (case-insensitive regex) |
claude-sessions # this directory, active profile
claude-sessions -a # every directory
claude-sessions -A -a --full # everything, everywhere, untruncated
claude-sessions -p work -n 50 # 50 most recent in the "work" profile
Summaries adapt to terminal width between 40 and 100 characters. TURNS counts
your messages, ignoring tool results and sidechains.
Searching your conversations¶
Searches the full text of every message - yours and Claude's - not just the opening prompt. Sessions that match show the matching passage in place of the summary, with surrounding context:
The pattern is a case-insensitive regular expression, so -g "auth|login" works.
An invalid pattern exits 2 with the regex error rather than silently matching
nothing.
Combine it with -A -a to search every profile and every directory at once -
this is the fastest way to find the conversation where you solved something
months ago.
claude-handoff TARGET [SESSION]¶
Copy a conversation into another profile so that account can resume it. See the guide for what this is for; this section is the mechanics.
claude-handoff work # this directory's most recent conversation
claude-handoff work 0a9014e8 # a specific one - a prefix is enough
claude-handoff work -s client # take it from "client" instead of the active profile
claude-handoff work -d ~/code/api # conversations belonging to another directory
| Argument | Meaning |
|---|---|
TARGET |
profile to copy into. Must already exist |
SESSION |
optional session id or unique prefix. Omit for the latest |
-s, --source |
profile to copy from (default: the active one) |
-d, --dir |
directory whose conversations to consider (default: current) |
On success it prints the resume command:
handed off session 0a9014e8-8806-43cf-98fd-28fad1353923
from: default to: work
dir : ~/Projects/Acme/api
claude-profile work && claude --resume 0a9014e8-8806-43cf-98fd-28fad1353923
Guards. An ambiguous prefix lists the matches instead of guessing. Copying a profile onto itself, or into a profile that does not exist, is refused with a message rather than silently doing nothing.
It is a copy - the source profile keeps its version, so you can switch back.
claude-profiles doctor¶
Checks the installation and reports anything wrong. Also available as
claude-doctor.
environment
ok python3 found at /usr/bin/python3
ok claude found at /usr/local/bin/claude
ok CLAUDE_CONFIG_DIR unset (profile 'default')
ok account data at ~/.claude-profiles (not a git repo)
shell wiring
ok .zshrc -> claude-profiles.zsh
profiles
ok default: you@example.com
warn work: not logged in - run 'claude-profile work' then /login
keychain
ok Claude Code-credentials
FAIL Claude Code-credentials-1a2b3c4d is orphaned - no profile maps to it
project markers
ok ~/Projects/Acme/.claude-profile -> work
1 problem(s), 1 warning(s)
What it checks:
| Area | Looks for |
|---|---|
| environment | python3 and claude on PATH; CLAUDE_CONFIG_DIR pointing somewhere real; account data not accidentally turned into a git repo |
| shell wiring | exactly one source line per rc file, pointing at a file that exists |
| profiles | which are signed in; .credentials.json not readable by other users |
| keychain | macOS entries that no longer map to any profile - the residue of deleting a profile with rm -rf |
| project markers | .claude-profile files that are empty or name a profile that does not exist |
Exits non-zero if it finds problems, so it works in a script. Warnings alone exit zero - "not logged in" is a normal state, not a fault.
claude-profile-remove NAME¶
Delete a profile: its directory, its conversations, and its stored credentials.
It shows what will be destroyed and asks you to type the profile name to confirm:
about to permanently delete profile client
directory : ~/.claude-profiles/client (12.4MB)
account : you@company.com
sessions : 37 conversation(s) - deleted with it
keychain : Claude Code-credentials-fd54d734
type the profile name to confirm:
| Flag | Effect |
|---|---|
-y, --yes |
skip the confirmation prompt (for scripts) |
Why not just rm -rf? On macOS, credentials live in the Keychain rather
than in the profile directory. Deleting the directory by hand leaves an
orphaned Keychain entry behind. This command removes both.
It also resets your current shell to default if you delete the profile you are
standing in, so you are not left pointing at a directory that no longer exists.
Guards. It refuses to delete default - your original ~/.claude - and
refuses unknown names, listing what is available instead.
This is not recoverable
Every conversation in that profile is deleted with it. If you want to keep
one, hand it off to another profile first:
claude-handoff default <session-id>.
claude-best¶
Names the signed-in account with the most headroom.
A profile is judged by its tightest limit, not its average - an account at 1% for the next five hours but 95% through its week is nearly out, and is ranked accordingly. Accounts that are locked out or not signed in are skipped.
| Flag | Effect |
|---|---|
-q, --quiet |
print just the name, for scripting |
--cached |
skip the live fetch and use cached figures |
Fetches live by default, so it reflects reality rather than a stale snapshot.
claude-auto¶
Launch Claude on whichever account has the most headroom, without choosing yourself.
It resolves claude-best --quiet, reports the choice on stderr, then runs
claude under that profile via claude-profile-exec - so your shell's own
profile is unchanged.
When to use which
Use claude-profile when the account matters (client work, a specific
subscription). Use claude-auto when it does not and you just want
capacity.
claude-prune¶
Delete old conversation transcripts. Dry run by default.
claude-prune # show what is older than 90 days
claude-prune -o 30 # ...older than 30 days
claude-prune -o 90 --yes # actually delete
╭────────┬─────────┬──────────────────────────────────────┬─────────────────────────────────╮
│ WHEN │ PROFILE │ SESSION ID │ DIRECTORY │
├────────┼─────────┼──────────────────────────────────────┼─────────────────────────────────┤
│ Aug 19 │ default │ a6c22511-53a7-4a10-8e16-104b79f2dc17 │ ~/Projects/Acme/api │
╰────────┴─────────┴──────────────────────────────────────┴─────────────────────────────────╯
3 session(s) older than 90 days, 2.3MB (90 newer session(s) untouched)
dry run - nothing deleted. pass --yes to delete.
| Flag | Effect |
|---|---|
-o DAYS, --older-than DAYS |
age threshold (default 90) |
-p NAME, --profile NAME |
just one profile (default: all) |
-n N, --limit N |
rows to list, 0 for all (default 20) |
-y, --yes |
actually delete |
--plain |
no borders |
It reports how much space would be freed and how many newer sessions it is leaving alone, so you can see the blast radius before committing.
Deleted transcripts are gone
There is no undo, and claude --resume cannot reach a deleted session.
Check the dry run first, and hand off anything worth
keeping.
claude-profile-exec PROFILE COMMAND [ARGS...]¶
Run a single command under a profile without switching your shell.
The profile applies only to that command. Your shell's own profile is unchanged, so this is the right tool for scripts, cron jobs, and one-off checks against another account.
Exits 2 with a usage message if you omit the command, and 1 if the profile does not exist.
claude-profile-clone SOURCE TARGET¶
Copy configuration from one profile into another, so a new profile does not start from nothing.
claude-profile-clone default work
claude-profile-clone default work --force # overwrite what is already there
| Copied | Never copied |
|---|---|
settings.json, CLAUDE.md |
.credentials.json - belongs to one account |
plugins/, skills/, agents/, commands/ |
.claude.json - account identity and per-project state |
projects/, sessions/, history.jsonl - conversation history |
Anything already present in the target is left alone unless you pass --force,
and the command tells you what it skipped.
Global MCP servers are not cloned
They live in .claude.json alongside account identity, so copying that file
would carry the wrong account with it. Re-add them under the new profile:
claude-profile work && claude mcp add ...
claude-profiles.py path NAME¶
Print a profile's config directory. Useful in scripts.
That form runs a single command under a profile without switching your shell.