Metadata-Version: 2.5
Name: agentswitcher
Version: 0.1.1
Summary: Save and switch account logins for AI coding CLIs (Antigravity, Claude Code, OpenCode, Codex).
Author: Rituraj Kumar
License-Expression: MIT
License-File: LICENSE
Keywords: accounts,antigravity,claude-code,cli,codex,opencode,profiles
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: cryptography>=44.0
Requires-Dist: platformdirs>=4.2
Requires-Dist: rich>=13.9
Requires-Dist: typer>=0.12.5
Provides-Extra: tui
Requires-Dist: textual>=8.2; extra == 'tui'
Description-Content-Type: text/markdown

# Agent Switcher (`agentswitcher`)

**Save the login of every account you use with a coding agent, then switch between them with one
command.** Built for developers who share a machine between a work account and a personal one — for
Antigravity (`agy`), Claude Code, OpenCode and Codex — without logging out and back in, and without
touching a browser cookie, a proxy or anyone's traffic.

`av` (or `asw`) stores each login **encrypted on your machine** under a name you choose. One account
is active at a time; switching is a single command with a verification step and a rollback.

> **Status: 0.1.1.** Install with `pip install agentswitcher` (or `pipx install
> agentswitcher`). Everything below is true of this tree.
>
> **Proven:** real `av save`/`av use` on a signed-in Antigravity login (Windows Credential Manager)
> and a signed-in Codex login, with the store byte-identical afterwards; **1428 tests**; a clean-venv
> install of both artifacts; CI green on Windows and Linux, Python 3.11 and 3.13. **Not proven yet:**
> a switch between two *different* accounts (this machine had one account per agent; `av logout` now
> makes that cycle possible without `/logout`).

## Quickstart (measured: 16 s from a clean venv)

Requires **Python 3.11 or newer** (developed and tested on 3.14.2) — see [Prerequisites](#prerequisites).

```bash
# 1. install (from PyPI; from a checkout use `python -m pip install .`)
python -m pip install agentswitcher

# 2. see what is on this machine
av detect          # which agents are installed, where each login lives, whether it can be switched
av doctor          # the full health check; exit code 1 means something needs fixing

# 3. sign in to an agent as usual, then save that login under a name
av save claude work

# 4. sign out and in as the other account, save it too, then switch freely
av save claude personal
av use claude work
av list            # which profile is active, and when it was captured
```

`asw` is an alias for `av`. Add the full-screen app with `pip install 'agentswitcher[tui]'`, then
run `av tui` (`?` shows every key).

Nothing leaves the machine: no network calls, no telemetry, no account names — the vault key lives in
the OS credential store and the profiles are AES-256-GCM files under your data directory.

## Prerequisites

| Platform | Needs | Notes |
|---|---|---|
| Windows 10 / 11 | Python **3.11+** from [python.org](https://www.python.org/downloads/) or the Store | the vault key lives in Windows Credential Manager; no extra install |
| Linux (Ubuntu 22.04+) | Python **3.11+** from your distro or [deadsnakes](https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa) · `libsecret-tools` (`sudo apt install libsecret-tools`) · a running D-Bus session with a keyring | the vault key is stored through `secret-tool`; on a headless server there is no keyring, so `av save` refuses instead of writing a key somewhere unsafe |

macOS is **not** supported in this release - declared as a non-goal rather than an untested claim.

## What it does

| Command | What it does |
|---|---|
| `av detect` / `av agents` | Explain which agents exist, where each login lives, and whether this build can switch it. |
| `av save <agent> <name>` | Capture the login that is signed in **now** into an encrypted profile. |
| `av current` | Who is signed in **now** and which profile is recorded as active — `antigravity → work (you@example.com)`, or `signed in as you@example.com · not saved`. |
| `av list` / `av profiles` · `av current` | The saved profiles with their **account** (email/account id), dates, status and which one is active. |
| `av use <agent> <name>` | The switch: validate → refuse while the agent is running → lock → re-capture the outgoing account → rollback point → restore → verify → record. Any failure rolls back. |
| `av close <agent>` | Stop the agent that is blocking a switch (`--yes` for scripts). Asks first, names the pids, and says that unsaved work is lost. |
| `av logout <agent>` | Sign the agent out **locally** so another account can sign in - no `/logout`, so the provider never revokes the token the vault holds. Saved profiles stay; asks first, and refuses while the agent is running. |
| `av rename <agent> <old> <new>` · `av remove <agent> <name>` | Rename or delete a profile; `remove` asks first and refuses to delete the active one. |
| `av export <file>` · `av import <file>` | Move every profile to another machine as one Argon2id-sealed `AVEX` file, with a SHA-256 per snapshot. |
| `av doctor` | Health check: vault key, every blob's digest, orphan files, agent capabilities, locks, free space. It reads the machine and, apart from creating the data directory when missing, writes nothing. |
| `av vault status` · `av vault rotate` | The key's target and how many profiles it protects; rotation is journalled and recovers from an interruption. |
| `av tui` | The same actions on one screen (needs the `[tui]` extra). |

Every read-only command accepts `--json` (schema 1, additive-only — see `super-docs/API.md`).
Exit codes: `0` ok · `1` error · `2` usage or a bad option · `3` refused because the agent is running,
another `av` holds the lock, or the login signed in now is a different account from the active profile
(AV023) · `130` interrupted.

## Before you use this with Antigravity

Google's Antigravity FAQ answers *"Can I use Antigravity CLI headless mode in third-party coding
agents or automate account switching?"* with: **"Wrapping `agy` inside third-party coding agents or
applications, or automating account rotation, is not supported under consumer plans."** Its Terms of
Service also call third-party access a breach that "may be grounds for suspension or termination" of
your Antigravity and/or Gemini CLI account — see <https://antigravity.google/terms> and
<https://antigravity.google/docs/faq>.

`av` does it anyway, because you asked for it and the accounts are yours. That is the entire warning:
one command changes which account `agy` uses, and Google has said it does not support that. Codex,
Claude Code and OpenCode carry no such wording — OpenAI in fact documents saved-account switching.

`av` never rotates accounts for capacity, never pools quota, and never runs in the background. One
account is active at a time, always because you typed a command.

## Configuration

`av` never reads a `.env` file. Precedence, highest first: a **flag** (`--data-dir`, `--config-dir`,
`--color`) → an **`AV_*` variable** → **`config.toml`** in the config directory → the platform
default. Every variable is also listed with no values in `.env.example`.

| Variable | Purpose | Default |
|---|---|---|
| `AV_DATA_DIR` | where profiles, locks and logs live (absolute; `~` is expanded) | `%LOCALAPPDATA%\agentswitcher` · `$XDG_DATA_HOME/agentswitcher` |
| `AV_CONFIG_DIR` | where `config.toml` lives (absolute) | `%APPDATA%\agentswitcher` · `$XDG_CONFIG_HOME/agentswitcher` |
| `AV_LOG_LEVEL` | `debug`, `info`, `warning`, `error`, `critical` | `warning` |
| `AV_TIMEOUT_SECONDS` | how long to wait for another `av` process to release a lock | `10` |
| `AV_COLOR` | `auto`, `always`, `never`; `NO_COLOR` (any value) wins | `auto` |
| `AV_REQUIRE_E2E` | test-only: fail instead of skipping when no credential store exists | unset |

There are no secrets in the environment on purpose: the vault key is created in the OS credential
store on first use and never printed.

## Privacy

- **Local only.** No analytics, no telemetry, no crash reporting, no update check. The program makes
  no network call at all; there is no HTTP client in its dependency list.
- **What is stored:** your saved logins, encrypted (AES-256-GCM) under a random key in the OS
  credential store, plus a small index of profile names and timestamps. Profile names are labels you
  choose — no email, no account name.
- **Logs** are off by default (`warning`); when enabled they redact tokens before writing.
- **Deleting everything:** `av remove` each profile, then delete the data directory printed by
  `av doctor`, and finally delete the `agentswitcher/vault` entry in your OS credential store
  (Windows Credential Manager; on Linux `secret-tool clear service agentswitcher target vault`).
  Removing profile files alone leaves ciphertext that no longer has an index entry, which
  `av doctor` reports as an orphan.
- **Export files** are sealed with Argon2id (64 MiB, 3 passes) and need a passphrase of at least 12
  characters; `av export` reads it from the terminal, never from an argument.

## Development

| Task | Command |
|---|---|
| install the dev environment | `uv sync` |
| run the CLI from source | `uv run av --version` |
| tests | `uv run pytest` · `uv run pytest -q tests/unit` |
| lint + format | `uv run nox -s lint` (`ruff check . && ruff format --check .`) |
| types | `uv run nox -s types` (`mypy src`, strict) |
| tests only | `uv run nox -s tests` |
| **verify everything** | `uv run nox -s verify` — lint, types, tests with coverage, bandit, pip-audit |
| build the release artifacts | `uv build` → `dist/*.whl`, `dist/*.tar.gz`; check with `uv run twine check dist/*` |
| **publish a release** | `uv run python deployments/publish.py` — guarded; see [`deployments/README.md`](deployments/README.md) |
| secrets + hooks | `uv run pre-commit run --all-files` |
| project map | `uv run graphify update .` |

Python is pinned in `.python-version` (3.14.2 for development; the package supports 3.11+). The test
suite includes artifact smoke tests that build the wheel and sdist and install them into fresh
virtual environments (`tests/smoke`), so `verify` is slower than a plain unit run and needs `uv` and
network access for the first install.

## Troubleshooting

| Symptom | What it means | What to do |
|---|---|---|
| `AV003 … is running` | the agent's process is open, and `av` will not swap a login under a running program | close the agent. If it is a background daemon (a Codex app-server can outlive the window by hours), `av use … --force` is the documented override |
| `AV020` / `av tui` exits 2 | Textual is an optional extra and is not installed | `pip install 'agentswitcher[tui]'` |
| `AV009` on Linux | `secret-tool` or a keyring session is missing | `sudo apt install libsecret-tools`, and run inside a logged-in session (`dbus-run-session` in CI) |
| `AV011` after 10 s | another `av` holds the lock | wait, or raise `AV_TIMEOUT_SECONDS`; a killed process never wedges it (the lock is a file with an owner) |
| `AV004` vault key unavailable | the OS credential store has no `agentswitcher/vault` entry | `av doctor` says whether profiles exist; if the key is gone, the profiles cannot be decrypted — restore the key from backup or re-save the logins |
| `av list` shows nothing after a restore | the profile index was restored from `metadata.json.bak`, which is one save behind | re-save the missing profile; `av doctor` names its orphan blob |
| `pip-audit` mentions `agentswitcher` as "dependency not found on PyPI" | the package is local (a source checkout), so it cannot audit itself; **this is not a failure** when the run ends `No known vulnerabilities found` | expected only before the first release; a published version resolves normally |
| Codex: "refresh token was revoked" after a switch | the saved copy was rotated away by the provider (another session used that account), so restoring it writes a dead token | sign in again as that account, then `av save codex <name> --force`; switching accounts never needs `/logout` - `av logout codex` clears only the local copy |
| Garbled box characters on an old console | the console is not in UTF-8 | `av` prints ASCII when it detects that; `--json` is always ASCII |

## Licence

MIT — see [LICENSE](LICENSE). The decision and the alternatives considered are in
`super-docs/DECISIONS.md`.
