Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[env]
# The unit/integration suites still rely on process-global auth override state
# and mock executable setup. Running the Rust test harness single-threaded keeps
# those tests deterministic in CI and local verification.
RUST_TEST_THREADS = "1"
139 changes: 132 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/burakdede/aisw/main/website/public/aisw-logo.png">
<img src="https://raw.githubusercontent.com/burakdede/aisw/main/website/public/aisw-logo.png" alt="aisw" width="160" />
<img src="https://raw.githubusercontent.com/burakdede/aisw/main/website/public/aisw-logo.png" alt="aisw" width="140" />
</picture>
</p>

<p align="center"><strong>Named profile and context manager for Claude Code, Codex CLI, and Gemini CLI.</strong></p>
<p align="center"><strong>AI Switcher (`aisw`) for Claude Code, Codex CLI, and Gemini CLI.</strong></p>

<p align="center">Switch between work, personal, and client accounts without copying auth files, editing hidden config, or logging in again every time.</p>

Expand All @@ -30,9 +30,9 @@

---

## Why people use aisw
## Why people use AI Switcher

`aisw` exists for a very specific kind of mess:
AI Switcher (`aisw`) exists for a very specific kind of mess:

- You use one Claude Code account for work and another for personal projects.
- Codex CLI should use one OpenAI account for client A and a different one for client B, without relying on copied shared ChatGPT session files.
Expand All @@ -48,7 +48,7 @@ The underlying problem is not just "multiple accounts." It is that each upstream
- Switch in one command with rollback if something fails.
- Bind repos to expected contexts so the wrong account does not silently launch in the wrong workspace.

If you have ever searched for "Claude Code account switcher", "multiple Codex CLI accounts", "Gemini CLI work and personal profiles", or "coding agent profile switch per repo", this is the tool that addresses that workflow directly.
If you have ever searched for "AI account switcher for Claude Code", "multiple Codex CLI accounts", "Gemini CLI work and personal profiles", or "coding agent profile switch per repo", AI Switcher addresses that workflow directly.

## Common situations

Expand Down Expand Up @@ -104,6 +104,8 @@ Context workflow:

```sh
# Homebrew (macOS and Linux)
# If Homebrew asks you to trust the tap first
brew trust burakdede/tap
brew tap burakdede/tap
brew install aisw

Expand All @@ -116,6 +118,127 @@ cargo install aisw

## Quick start

The easiest mental model is:

1. `aisw init` captures whatever Claude, Codex, and Gemini accounts are live right now.
2. `aisw add ...` introduces another account directly under `aisw`.
3. `aisw use ...` restores the saved credentials for the selected profile.
4. After switching, start a fresh Claude/Codex/Gemini process.

If you are setting up work and personal accounts, follow this order instead of logging out and back in repeatedly at the upstream CLI first.

The top-level commands most people need are:

- `aisw init`
- `aisw add <tool> <profile>`
- `aisw use <tool> <profile>`
- `aisw use --all --profile <name>`
- `aisw list`
- `aisw status`

### First account: import what is already live

```sh
# Bootstrap ~/.aisw/, install shell integration, and import
# the accounts that are already live right now
aisw init

# Verify what got imported and marked active
aisw list
aisw status
```

If Claude, Codex, or Gemini were already logged in when you ran `aisw init`, you do not need to re-add that same first account with `--from-live`.

### Second account: add it directly

```sh
# Preferred path for a second Claude account
aisw add claude account-2 --label account-2@gmail.com

# Preferred path for a second Codex account
aisw add codex account-2 --label account-2@gmail.com

# If you also use Gemini
aisw add gemini account-2 --label account-2@gmail.com
```

This is the recommended path because AI Switcher drives the login flow itself instead of depending on whatever account happens to be live upstream at that moment.

For Codex specifically, this distinction matters:

- Durable: `aisw add codex <name>` for ChatGPT-managed auth, because login happens inside that profile's isolated `CODEX_HOME`.
- Bootstrap only: `aisw add codex <name> --from-live` when you intentionally want to import the currently live ChatGPT-managed session.

### Switch between saved accounts

```sh
aisw use claude account-1
aisw use claude account-2

# If profile names line up across tools
aisw use --all --profile account-1
aisw use --all --profile account-2
```

After `aisw use ...`, start a fresh Claude/Codex/Gemini process. You should not need to log out and log back in manually after every switch, but you should not rely on reusing an already running or resumable session from the previous account.

### When to use `--from-live`

Use `--from-live` when you intentionally want to capture whichever account is currently live in the upstream CLI.

```sh
# Example: import the account that is currently live upstream
aisw add claude account-2 --from-live --label account-2@gmail.com
```

That workflow is:

1. Exit the upstream CLI.
2. Log out upstream.
3. Log back in upstream as the account you want to capture.
4. Run `aisw add ... --from-live`.

That is a valid import path, but it is not the best default recommendation when AI Switcher can run the login flow directly.

<details>
<summary>Common variants</summary>

Use these when your setup is a little different from the normal two-account OAuth flow above.

```sh
# API-key-backed profiles
aisw add claude work --api-key "$ANTHROPIC_API_KEY"
aisw add codex work --api-key "$OPENAI_API_KEY"
aisw add gemini work --api-key "$GEMINI_API_KEY"

# Switch every tool when the profile names line up
aisw use --all --profile work

# Create one mixed-tool context when names do not line up
aisw context create acme \
--claude acme-claude \
--codex acme-codex \
--gemini acme-gemini

aisw context use acme
```
</details>

<details>
<summary>Important tool-specific limits</summary>

- Claude: after switching, start a fresh Claude process instead of relying on a resumed session from the previous account.
- Codex: ChatGPT-managed `aisw add codex <name> --from-live` is bootstrap-only. The durable path is direct per-profile login with `aisw add codex <name>`.
- Codex: shared-mode ChatGPT auth switching is explicitly unsupported.
- Gemini: `aisw init` and `aisw add gemini ...` can capture the currently live Gemini state, but you should still start a fresh process after switching.

</details>

### Generic command examples

Once the two-account flow above makes sense, these are the shorter building blocks:

```sh
# Bootstrap: creates ~/.aisw/, offers shell-hook setup,
# and can import already logged-in accounts
Expand Down Expand Up @@ -187,7 +310,7 @@ What you do not get from a context:

The practical value is simple: `aisw use --all --profile personal` works when names line up, and `aisw context use acme` works when the real world does not.

## Why aisw works better than manual switching
## Why AI Switcher works better than manual switching

- It writes the native upstream credential locations that Claude Code, Codex CLI, and Gemini CLI already use.
- It snapshots live state before switching and rolls back on failure instead of leaving you mid-edit.
Expand Down Expand Up @@ -254,10 +377,12 @@ Credentials never leave the local machine. There is no remote service, no teleme

## Documentation

Most users should be able to get productive from this README. For deeper workflows, edge cases, and tool-specific behavior, use the docs below.

- [Common switching situations](https://burakdede.github.io/aisw/common-situations/)
- [Quickstart](https://burakdede.github.io/aisw/quickstart/)
- [Commands](https://burakdede.github.io/aisw/commands/)
- [Why aisw](https://burakdede.github.io/aisw/why-aisw/)
- [Why AI Switcher](https://burakdede.github.io/aisw/why-aisw/)
- [Workspace guardrails](https://burakdede.github.io/aisw/workspace/)
- [How it works](https://burakdede.github.io/aisw/how-it-works/)
- [Security](https://burakdede.github.io/aisw/security/)
Expand Down
1 change: 1 addition & 0 deletions docs/acceptance-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This matrix records the current end-to-end acceptance status for supported `aisw
| Codex CLI | Local state configured for keyring without importable `auth.json` | Supported diagnostic | Not applicable | `init` reports that Codex appears keyring-backed but no importable credential file is available | `tests/init_cmd.rs`, `src/auth/codex.rs` unit coverage, full `cargo test` |
| Gemini CLI | File-managed auth and local state | Supported | Supported | Imports managed Gemini files, preserves required local state files, reapplies live state under `~/.gemini` on switch | `tests/init_cmd.rs`, `tests/use_cmd.rs`, full `cargo test` |
| Gemini CLI | System keyring | Not supported | Not supported | Gemini remains file-managed in `aisw` because upstream behavior is file-centric | Product policy; see [supported-tools.md](./supported-tools.md) |
| Antigravity CLI | Shared live OAuth keyring auth plus documented `~/.gemini` config roots | Supported via `add` / `--from-live` | Supported | Restores the shared live OS keyring session plus `~/.gemini/antigravity-cli/` and `~/.gemini/config/`; upstream does not currently document an isolated per-profile auth root | `src/auth/antigravity.rs`, `src/commands/add.rs`, `src/commands/use_.rs`, full `cargo test` |

## Notes

Expand Down
5 changes: 5 additions & 0 deletions docs/adding-profiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ aisw add gemini personal
- Claude: spawns `claude auth login`. When the installed Claude build supports profile-scoped auth, `aisw` runs login inside the profile-owned `CLAUDE_CONFIG_DIR`; otherwise it monitors the live credential file and Keychain for changes and captures the result there.
- Codex: sets `CODEX_HOME` to the profile directory and spawns `codex`. The device-auth flow writes credentials directly into that profile-owned isolated state. This is the durable ChatGPT-managed Codex path.
- Gemini: sets `GEMINI_CLI_HOME` to a scratch directory, spawns `gemini`, then copies the resulting OAuth cache files into the profile. The scratch directory is removed after the flow regardless of outcome.
- Antigravity: spawns `agy`, captures the resulting live keyring-backed OAuth session plus the documented `~/.gemini/antigravity-cli/` and `~/.gemini/config/` state, then restores the prior live state unless `--set-active` is requested.

Claude OAuth support depends on how the installed Claude build scopes auth:
- File-backed or profile-scoped keychain auth: the interactive login is a durable isolated profile path.
Expand All @@ -65,6 +66,7 @@ Import what the tool is currently using, without launching a browser:
aisw add claude work --from-live
aisw add codex work --from-live
aisw add gemini work --from-live
aisw add antigravity work --from-live
```

This is the fastest path if you are already logged in. The captured profile is automatically set as active because those credentials are already live.
Expand All @@ -73,6 +75,8 @@ For Codex ChatGPT-managed auth, `--from-live` is compatibility/bootstrap only. I

For Claude OAuth, `--from-live` captures whatever Claude is currently using, but it does not upgrade a shared live session into an independently isolated auth owner. If the install still uses Claude's legacy shared Keychain credential, treat the imported profile as a captured shared-live session rather than as a durable isolated OAuth bundle.

For Antigravity OAuth, both interactive add and `--from-live` operate on the same shared live upstream model: `aisw` stores the current keyring-backed session and documented Antigravity config roots, then restores them on switch. Upstream does not currently document an isolated per-profile auth root or profile selector.

If a profile with that name already exists, use `--yes` to overwrite it:

```sh
Expand Down Expand Up @@ -106,6 +110,7 @@ All credential files are written with `0600` permissions. The profile name is re
- `file`: portable and backup-friendly
- `system-keyring`: stronger local secret storage for Claude and Codex where the OS keyring is usable. Stored config and status output use `system_keyring`.
- Gemini remains file-managed because its auth is coupled to broader `~/.gemini/` state
- Antigravity supports `file` and `system-keyring` for the managed profile, but live auth is always restored into Antigravity's shared OS keyring entry.

## Duplicate account detection

Expand Down
6 changes: 4 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,16 @@ Notes:
- `--from-live` always activates the profile because those credentials are already live.
- `--from-live --yes` overwrites an existing profile in place; the existing entry is not removed until capture succeeds.
- For Codex ChatGPT-managed auth, `--from-live` is a bootstrap import, not a durable interchangeable account bundle.
- For Antigravity, `--from-live` captures the current shared live keyring-backed session plus the documented Antigravity config roots.
- When OAuth identity can be resolved, `add` blocks creating a duplicate profile for an already-stored account.
- `--credential-backend` affects the managed `aisw` profile only. It does not force the upstream CLI's live auth backend.
- Gemini supports only `file`. Claude and Codex support `file` and `system-keyring`. Stored config and status output use `system_keyring`.
- Gemini supports only `file`. Claude, Codex, and Antigravity support `file` and `system-keyring`. Stored config and status output use `system_keyring`.

Live credential locations by tool:
- Claude: `~/.claude/.credentials.json` or the macOS Keychain
- Codex: `~/.codex/auth.json` or the OS keyring
- Gemini: `~/.gemini/.env` (API key) or OAuth files in `~/.gemini/`
- Antigravity: live OS keyring auth plus config/state under `~/.gemini/antigravity-cli/` and `~/.gemini/config/`

```sh
aisw add claude work --api-key "$ANTHROPIC_API_KEY"
Expand Down Expand Up @@ -162,7 +164,7 @@ Activate a stored profile as the live account.
| `--emit-env` | Print shell export/unset lines to stdout instead of writing them to the session |

Notes:
- `--state-mode` applies to Claude Code and Codex CLI only. Gemini does not support it.
- `--state-mode` applies to Claude Code and Codex CLI only. Gemini and Antigravity do not support it.
- Switching is atomic: the previous live state is snapshotted before any write. A failed write triggers a full rollback.
- With shell hook active, `aisw use` also emits the environment variable exports into the current shell session.
- `--emit-env` is used internally by the shell hook. You can use it directly to apply exports in a subshell: `eval "$(aisw use claude work --emit-env)"`.
Expand Down
14 changes: 12 additions & 2 deletions docs/how-it-works.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
title: How aisw works
description: Architecture, design decisions, credential storage model, OS keyring integration, and per-tool implementation details for Claude Code, Codex CLI, and Gemini CLI.
description: Architecture, design decisions, credential storage model, OS keyring integration, and per-tool implementation details for Claude Code, Codex CLI, Gemini CLI, and Antigravity CLI.
---

# How aisw works

This page explains the design decisions behind `aisw`, how credentials are stored and applied, and the per-tool implementation details for Claude Code, Codex CLI, and Gemini CLI.
This page explains the design decisions behind `aisw`, how credentials are stored and applied, and the per-tool implementation details for Claude Code, Codex CLI, Gemini CLI, and Antigravity CLI.

## Profile and context model

Expand Down Expand Up @@ -113,6 +113,16 @@ On Linux, if the Secret Service daemon is not available at runtime (e.g. headles
**How `aisw` captures credentials:**
- `--api-key` / `--from-env`: stores the key in a profile `.env` file.
- `--from-live`: copies everything under `~/.gemini/` into the profile directory.

### Antigravity CLI

- Live auth: shared OS-native keyring entry documented by upstream behavior
- Live state: `~/.gemini/antigravity-cli/` and `~/.gemini/config/`
- `--from-live`: captures the current live keyring-backed session plus both documented config roots.
- Interactive OAuth: launches `agy`, captures the resulting live keyring/config state, and restores the prior live state unless `--set-active` is requested.
- `use`: restores the managed keyring secret into Antigravity's live keyring entry, then transactionally syncs the documented config roots.

**Important Antigravity limitation:** upstream does not currently document an isolated per-profile auth/data root or profile selector. `aisw` therefore supports Antigravity through shared live switching rather than profile-owned isolated auth. This is a product limitation upstream, not `aisw` corruption.
- Interactive OAuth: sets `GEMINI_CLI_HOME` to a temporary scratch directory, spawns `gemini` so it writes its OAuth cache there, then copies all resulting files from `<scratch>/.gemini/` into the profile directory. The scratch directory is always cleaned up, regardless of success or failure.

`GEMINI_CLI_HOME` was introduced in Gemini CLI to override the home directory used for config storage. It is cleaner than overriding `HOME` because it does not affect other processes or macOS Keychain lookups that depend on the real home directory.
Expand Down
7 changes: 5 additions & 2 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Quickstart
description: Install aisw, store your first profiles, and switch between Claude Code, Codex CLI, and Gemini CLI accounts in under five minutes.
description: Install aisw, store your first profiles, and switch between Claude Code, Codex CLI, Gemini CLI, and Antigravity CLI accounts in under five minutes.
---

# Quickstart
Expand Down Expand Up @@ -33,7 +33,7 @@ aisw --version
aisw init
```

This creates `~/.aisw/`, offers to install the optional shell hook (recommended), and detects any accounts you are already logged into. If you are already signed into Claude Code, Codex, or Gemini, `init` will offer to import those credentials as named profiles so you start without re-authenticating.
This creates `~/.aisw/`, offers to install the optional shell hook (recommended), and detects any accounts you are already logged into. If you are already signed into Claude Code, Codex, or Gemini, `init` will offer to import those credentials as named profiles so you start without re-authenticating. Antigravity support currently starts with explicit `add` flows rather than `init` auto-import.

For GUI or other machine-driven onboarding, use the non-prompting bootstrap path instead:

Expand Down Expand Up @@ -69,10 +69,13 @@ aisw add codex ci --from-env
aisw add claude personal
aisw add codex personal
aisw add gemini personal
aisw add antigravity work --from-live
```

For Codex ChatGPT-managed auth, this interactive path is the durable setup because login happens inside the profile-owned isolated `CODEX_HOME`.

For Antigravity, `aisw add antigravity <name>` captures the shared live keyring-backed OAuth session that `agy` creates and stores the documented Antigravity config roots alongside it. Upstream does not currently document an isolated per-profile auth root.

If you want machine-readable OAuth progress for a GUI:

```sh
Expand Down
Loading
Loading