axm-doctor
Env bootstrap + auth-status doctor (detect, propose, orchestrate)
Installation
Quick Start
Python
from axm_doctor import detect_tool, detect_auth
# Is a tool on PATH? (stdlib + pydantic only, no AXM dependency imported)
print(detect_tool("uv")) # ToolStatus(state='present', version='0.5.1', ...)
# Is a third-party CLI logged in? (read-only: exit code / credential-file existence)
print(detect_auth("gh")) # AuthStatus(state='logged_in', login_cmd=None)
print(detect_auth("claude")) # AuthStatus(state='logged_out', login_cmd='claude login')
Python
from axm_doctor import install_command, run_install
# Propose the official install command — runs nothing.
plan = install_command("uv") # InstallPlan(human_command='curl -LsSf https://astral.sh/uv/install.sh | sh', ...)
# Dry-run by default — NEVER installs silently.
run_install(plan) # InstallResult(executed=False, ...): echoes the command it would run
run_install(plan, confirm=True) # opt-in install, then re-detects via detect_tool
Python
from axm_doctor import missing_secrets, provision_missing
# Which credential specs resolve to 'missing'? Reads vault's catalog + value-free provenance.
missing_secrets() # [MissingSecret(group='research.fred', name='api_key', setup_hint='axm-vault set research.fred.api_key'), ...] — never a value
# Dry-run by default — NEVER prompts or stores.
provision_missing() # ProvisionResult(provisioned=False, groups=['research.fred']): the groups it WOULD prompt for
provision_missing(confirm=True) # delegates to vault's run_setup(only=...) — doctor never writes a secret itself
Features
- ✅ Bootstrap layer —
detect_tool/detect_authdepend on stdlib + pydantic only; importing them pulls no AXM package (deferredaxm-configimport + PEP 562 lazy re-exports), so they run before the rest of AXM is installed. - ✅ Config-resolvability checks —
detect_git_identity(a[git].defaultin the axm-config store, elsegit config --get user.emailexit code) anddetect_gh_config(gh config get git_protocolexit code;not_installedwhenghis absent) report whether a git committer identity andghbase config are resolvable — value-free (presence + exit code only, never the value), degrading tounconfiguredon error. Theenv_doctortool surfaces them under aconfigkey. - ✅ Read-only auth — state comes from an exit code or a non-empty credential-file check (a 0-byte file is
logged_out); on macOS,claudeis probed via the login Keychain entryClaude Code-credentials(exit code only). The file is stat'd, not opened, and the Keychain value is never read, so the token value is never read. - ✅ Frozen models — immutable
ToolStatus/AuthStatus/GitIdentityStatus/GhConfigStatus;AuthStatuscarries alogin_cmdto recover fromlogged_out, never a token. - ✅ Install plans, never silent installs —
install_commandproposes the official command for a known tool;run_installis a dry-run by default (confirm=False) and installs only on explicit opt-in (confirm=True), then re-detects the tool. - ✅ Orchestrates, never possesses —
missing_secretslists the vault credential specs that resolve tomissing(value-free, with asetup_hint);provision_missingis a dry-run by default and onconfirm=Truedelegates to vault'srun_setup— the secret never transits axm-doctor.