On this page
Profile lifecycle operations (delete, etc.) for claudewheel.
#claudewheel.profile_ops
#claudewheel.profile_ops
Delete profiles and clean up their dirs, tokens, and options.
#FixAuthResult
Outcome of fix_auth_shadow(): success or a reason for no-op/failure.
ok: True when the shadow was removed, False otherwise. reason: None on success; "no-token" / "no-shadow" / "unreadable-creds" on failure. tier_saved: rateLimitTier value preserved into tokens.json, or None. subscription_saved: subscriptionType value preserved into tokens.json, or None.
#fix_auth_shadow
def fix_auth_shadow(name: str) -> FixAuthResultRemove session credentials (claudeAiOauth) that shadow a long-lived token.
Reads the profile's .credentials.json, strips the claudeAiOauth key, and preserves any tier/subscription metadata into tokens.json. Zero printing, zero sys.exit -- returns a FixAuthResult describing what happened.
#DeleteResult
Outcome of delete_profile_core(): success data or a refusal reason.
refusal_reason is None on success, else one of:
- "not-found": not registered in options.json and no dir on disk
- "default-profile": the built-in ~/.claude, never deletable
- "running": active sessions detected (and running check not skipped)
- "data-destruction": real data at shared-dir names (see at_risk_dirs)
#_is_profile_running
def _is_profile_running(name: str) -> boolCheck if a profile has active sessions by scanning its sessions/ dir for PID files.
#_remove_profile_dir
def _remove_profile_dir(name: str) -> tuple[int, int]Remove ~/.claudewheel/profiles/
Returns (removed_symlinks, removed_real) counts.
#_remove_from_options
def _remove_from_options(name: str) -> boolRemove a profile from options.json values list and metadata.
Returns True if the profile was found and removed.
#_remove_from_tokens
def _remove_from_tokens(name: str) -> boolRemove a profile entry from tokens.json. Returns True if found.
#_purge_last_config_profile
def _purge_last_config_profile(name: str) -> boolRemove last_config["profile"] from state.json on disk if it names name.
Returns True when a purge happened. Read-modify-write via the state helpers so all other state.json keys are preserved.
#delete_profile_core
def delete_profile_core(name: str, *, skip_running_check: bool=False, allow_data_destruction: bool=False) -> DeleteResultDelete a profile and all associated data. Never prints.
Refuses (in order): the built-in "default" profile; profiles neither registered in options.json nor present on disk under PROFILES_DIR; profiles with active sessions (unless skip_running_check); profiles holding REAL data at shared-dir names (unless allow_data_destruction).
#do_delete_profile
def do_delete_profile(name: str, force: bool=False, force_data: bool=False) -> intCLI wrapper: run delete_profile_core and print the outcome.
Returns a process exit code (0 = success).
#_update_options_rename
def _update_options_rename(old: str, new: str) -> NoneSwap old->new in options.json values list, pinned list, and metadata.
#_update_tokens_rename
def _update_tokens_rename(old: str, new: str) -> boolMove old key to new in tokens.json. Returns True if token existed.
#_update_state_rename
def _update_state_rename(old: str, new: str) -> NoneIf last_config.profile == old, change to new.
#rename_profile
def rename_profile(old: str, new: str) -> NoneRename a profile directory and update all JSON stores.
Raises ValueError on validation failures, OSError on filesystem errors. The function is crash-safe: a .rename_pending breadcrumb file is written before the rename and removed after all stores are updated. If a crash occurs mid-rename, recover_incomplete_renames() can finish the job.
#recover_incomplete_renames
def recover_incomplete_renames() -> list[str]Scan PROFILES_DIR for .rename_pending breadcrumbs and finish the rename.
Returns a list of recovered profile names (the new names). Called at startup to auto-repair after a crash mid-rename.