Metadata-Version: 2.3
Name: cc-switch
Version: 0.9.11
Summary: Generate ~/.claude/settings.json from saved provider/model configs
Project-URL: Homepage, https://github.com/juzcn/cc-switch
Project-URL: Repository, https://github.com/juzcn/cc-switch
Project-URL: Documentation, https://github.com/juzcn/cc-switch/blob/main/README.md
Project-URL: Changelog, https://github.com/juzcn/cc-switch/releases
Author-email: juzcn <zhangjun@cueb.edu.cn>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Terminals
Requires-Python: >=3.13
Requires-Dist: credstore>=0.10.4
Description-Content-Type: text/markdown

# cc-switch

Generate `~/.claude/settings.json` from saved provider/model configs —
a small CLI that mirrors the [credstore](https://github.com/juzcn/credstore) pattern.

Non-secret provider *shape* lives in `~/.claude/cc-switch.json`.  API
keys are **never** stored there — the config keeps the key's *name*,
and the value is read from credstore at activate time and injected into
the system environment as `ANTHROPIC_AUTH_TOKEN`.  The generated
`settings.json` contains no credential line.

## Install

cc-switch is a standalone PyPI package, installed independently of
[Slife](https://github.com/juzcn/slife) — it was originally extracted from that
repository, but neither package pulls in the other.  It depends on
[credstore](https://github.com/juzcn/credstore), which is pulled in
automatically.

```bash
uv tool install --force --python '>=3.13' cc-switch
# or, in this repo (dev):
uv sync
```

> `--python '>=3.13'` is not optional. Without it `uv` builds the tool
> environment on whichever interpreter it prefers; cc-switch needs ≥ 3.13, so
> an older environment has no compatible release to install at all. With the
> constraint, uv fetches a suitable interpreter or fails loudly.

One-click installers (install `uv` if needed, then `uv tool install --force
--python '>=3.13' cc-switch`):

```bash
# macOS / Linux / WSL
curl -fsSL https://raw.githubusercontent.com/juzcn/cc-switch/main/install.sh | bash

# Windows PowerShell
powershell -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/juzcn/cc-switch/main/install.ps1 | iex"
```

The installers and the `--force` form are **idempotent** — the first run
installs, re-running **updates** to the latest PyPI release.  A plain
`uv tool install cc-switch` no-ops when the tool is already installed.
Uninstall with `uninstall.sh` / `uninstall.ps1` (config under
`~/.claude/cc-switch.json` is left in place).

## Commands

### `cc-switch set <provider-name>`

Create or edit a provider.  Prompts for:

- **Base URL** (required)
- **API key name** — the credstore key holding the secret (required)
- **Supported models** (optional; comma, space, or semicolon separated)

If the provider already exists this *edits* it; otherwise it *adds* it.
When editing, a blank answer keeps the current value.  The models prompt
**toggles** against the provider's current list: a model you type that is
already listed is removed, one that isn't is added (symmetric
difference).  A blank answer leaves the list unchanged, and typing the
same list again undoes the change.

```bash
cc-switch set deepseek
# Base URL [..]: https://api.deepseek.com/anthropic
# API key name (credstore key): DEEPSEEK_API_KEY
# Supported models (comma separated; toggles against the current list): deepseek-chat,deepseek-reasoner
```

No secret value is ever asked for or written — store it first with:

```bash
credstore set DEEPSEEK_API_KEY
```

### `cc-switch activate <provider-name/model-name>`

Writes `~/.claude/settings.json` with stock Claude Code defaults, and
injects the API key from credstore into the system environment as
`ANTHROPIC_AUTH_TOKEN` (mirroring `credstore inject` — registry on
Windows, shell profile on Unix), so a freshly launched Claude Code
session inherits it.  The settings file contains **no** credential.

`settings.json` is written first, then the secret is resolved from
credstore.  If the key is missing — or credstore itself is not
installed — `activate` fails loudly with the appropriate hint, leaving
the generated settings file on disk but **not** injecting a token.

On success, the activated `provider/model` is recorded in the top-level
`active` field of `~/.claude/cc-switch.json`, so the list view can show
which model is currently active.

```bash
cc-switch activate deepseek/deepseek-chat
```

If the model is omitted and the provider has exactly one model, it is
used; if several, you are prompted to pick one.

### `cc-switch activate <provider-name/model-name> --custom`

Like `activate`, but lets you override each model slot first —
`ANTHROPIC_DEFAULT_HAIKU_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`,
`ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_CLAUDE_CODE_SUBAGENT_MODEL`,
and `ANTHROPIC_CLAUDE_CODE_EFFORT_LEVEL` — one at a time.  Enter a value
to override, or Enter to keep the default.  The main model slot
(`ANTHROPIC_MODEL`) is chosen on the command line, not here.  Overrides
are one-shot: they never touch the stored provider config.

### `cc-switch`

With no command, lists every configured provider/model pair, one per
line.  The currently active model is marked `(active)`:

```
deepseek/deepseek-chat
deepseek/deepseek-reasoner
scnet/scnet-1m  (active)
```

### `cc-switch list`

Shows provider metadata (base URL, API key name, models) — one block
per provider.

### `cc-switch remove <provider-name>`

Deletes a provider config.  Does not touch credstore or settings.json.

## Files

| Path | Purpose |
|------|---------|
| `~/.claude/cc-switch.json` | Provider/model shapes + the `active` spec (no secrets) |
| `~/.claude/settings.json` | Generated by `activate` |
| credstore (`DEEPSEEK_API_KEY`, …) | The actual API key values |

`CC_SWITCH_FILE` overrides the config path; the settings path can be
overridden for tests.

## Security notes

- The config file holds only metadata — the API key is referenced by name.
- `activate` reads the secret from credstore and injects it into the
  system environment as `ANTHROPIC_AUTH_TOKEN` (registry on Windows /
  shell profile on Unix), mirroring `credstore inject`; it is never
  written to settings.json.
- A provider's `extra_env` (settable via the Python API) may not contain
  `ANTHROPIC_MODEL`, `ANTHROPIC_BASE_URL`, or `ANTHROPIC_AUTH_TOKEN` —
  cc-switch owns those slots, and a secret must never reach the plaintext
  config.
- On a TTY, `activate` prints an activation hint without echoing the
  secret; when stdout is piped it emits the shell export line for `eval`.
- Restart your shell (or start a new terminal) after activating for the
  env change to take effect in the current session.
- Secrets are immutable Python `str` — cc-switch follows credstore's
  practice of `del`-ing the reference immediately after use.

## Development

Requires [uv](https://docs.astral.sh/uv/).

```bash
uv sync --locked     # create .venv with runtime + dev dependencies
uv run pytest        # run the test suite
uv run ruff check    # lint
uv run ruff format   # format
uv run pyright       # type-check
```

CI runs the suite on Windows, macOS, and Linux (`.github/workflows/ci.yml`), plus lint and type-check jobs.

### Releasing

Bump `version` in `pyproject.toml`, commit, then push a tag with the same
version:

```bash
git tag -a v0.9.12 -m "cc-switch 0.9.12" && git push origin v0.9.12
```

`.github/workflows/publish.yml` fails unless the tag matches the package
version exactly, then uploads to PyPI via trusted publishing.

## License

MIT

