ai-switch-cli 0.1.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai-switch-cli
3
+ Version: 0.3.0
4
+ Summary: One-command provider and model switching for Claude Code and Codex, with session-history checks
5
+ Author: ZidongS
6
+ License: MIT
7
+ Keywords: codex,claude-code,api,profile,cli,model-switching
8
+ Classifier: Environment :: Console
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: POSIX
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+
15
+ # ai-switch
16
+
17
+ ## The problem it solves
18
+
19
+ When you use multiple coding agents and multiple model providers, changing providers separately in Claude Code and Codex is repetitive and error-prone. **ai-switch updates both agents in one command.**
20
+
21
+ Choose a provider profile once, then activate it with `ai-switch use NAME`. The built-in GLM and DeepSeek presets already contain the provider-specific files, endpoints, protocols, model catalogs, and Claude Code mappings recommended by their official documentation. You only need to enter your API key. Custom OpenAI-compatible providers are supported too.
22
+
23
+ A profile can offer several models (for example GLM 5.3 and GLM 5.3 Flash, or DeepSeek Flash and DeepSeek V4 Pro). `ai-switch use` asks which one to activate and writes the answer to both agents, so the model pickers *inside* Claude Code and Codex keep working instead of being pinned to a single model.
24
+
25
+ Switching also leaves conversation history alone: sessions, rollouts, `history.jsonl` and the agents' runtime databases are never copied into a profile or overwritten by a switch, and `ai-switch doctor` reports the things that really do make old sessions disappear.
26
+
27
+ This is a secure, headless-friendly command-line tool for servers without a desktop environment or administrator privileges.
28
+
29
+ ## Install
30
+
31
+ Install from PyPI (recommended):
32
+
33
+ ```bash
34
+ python3 -m pip install --user ai-switch-cli
35
+ ```
36
+
37
+ For a system or virtual-environment install:
38
+
39
+ ```bash
40
+ pip install ai-switch-cli
41
+ ```
42
+
43
+ Install the latest development version directly from GitHub:
44
+
45
+ ```bash
46
+ python3 -m pip install --user "git+https://github.com/ZidongS/ai-switch.git"
47
+ ```
48
+
49
+ Or install a local checkout:
50
+
51
+ ```bash
52
+ python3 -m pip install --user .
53
+ ```
54
+
55
+ If `ai-switch` is not found afterwards, add the user script directory to `PATH`:
56
+
57
+ ```bash
58
+ export PATH="$HOME/.local/bin:$PATH"
59
+ ```
60
+
61
+ ## Quick start
62
+
63
+ Save the configuration currently in use as a fallback profile:
64
+
65
+ ```bash
66
+ ai-switch init default --description "Default daily configuration"
67
+ ```
68
+
69
+ Create a ready-to-use mainstream provider profile interactively:
70
+
71
+ ```bash
72
+ ai-switch add
73
+ ```
74
+
75
+ Select `glm` or `deepseek`, enter the API key, and activate the generated configuration:
76
+
77
+ ```bash
78
+ ai-switch use glm
79
+ ```
80
+
81
+ `use` lists the models the profile offers and remembers your choice:
82
+
83
+ ```text
84
+ This profile provides 2 models:
85
+ 1) glm-5.3 GLM-5.3 flagship (1M context) [default]
86
+ 2) glm-5.3-flash GLM-5.3 flash (fast, 1M context)
87
+ Select model [1-2, Enter=glm-5.3, q=cancel]:
88
+ ```
89
+
90
+ For scripts and servers, choose without prompting (name, unique prefix, or index):
91
+
92
+ ```bash
93
+ ai-switch use glm --model glm-5.3-flash
94
+ ai-switch use glm -m 2 -y
95
+ ai-switch use glm --dry-run # show what would change
96
+ ```
97
+
98
+ Edit `~/.codex/config.toml` and `~/.claude/settings.json` for another provider, then save that configuration as a second profile:
99
+
100
+ ```bash
101
+ ai-switch init glm --description "GLM Coding Plan"
102
+ ai-switch list
103
+ ai-switch use glm
104
+ ai-switch current
105
+ ```
106
+
107
+ Create a profile through an interactive prompt (no editor required; API keys are hidden while typing). Choose the built-in `glm` preset to generate the complete ZAI Codex Responses configuration, Codex model catalog and Claude Code model/environment mappings for the three models the endpoint publishes (`glm-5.3`, `glm-5.3-flash`, `glm-5-turbo`) automatically. The `deepseek` preset creates the two model entries the DeepSeek endpoint currently accepts (`deepseek-flash` and `deepseek-v4-pro`, both with image input metadata) plus the recommended Claude Code mappings:
108
+
109
+ ```bash
110
+ ai-switch add
111
+ ```
112
+
113
+ Update an existing profile description:
114
+
115
+ ```bash
116
+ ai-switch describe default "Default daily configuration"
117
+ ```
118
+
119
+ `list` shows the active marker, profile name, configured clients, description, detected models, and endpoint hostnames. API keys are never printed.
120
+
121
+ ## Choosing a model
122
+
123
+ Each profile keeps its model list in `models.json` next to the client files:
124
+
125
+ ```bash
126
+ ai-switch models # list the models of the active profile
127
+ ai-switch models glm --json # machine readable
128
+ ai-switch upgrade glm # derive models.json for a profile from an older release
129
+ ```
130
+
131
+ For every model a profile records the Codex catalogue entry and the Claude Code mapping, so activating one model updates all of these consistently:
132
+
133
+ * Codex: the top-level `model` (and `model_reasoning_effort`), plus a `~/.codex/models.json` catalogue that lists **every** model of the profile, which is what makes Codex's own `/model` picker show them.
134
+ * Claude Code: the default model in `settings.json` plus the Opus/Sonnet/Haiku mappings. The presets map the three categories to *different* provider models where they exist, and they never set `ANTHROPIC_MODEL`, because a pinned environment model overrides your selection and makes `/model` do nothing.
135
+
136
+ The `glm` and `deepseek` presets ship multi-model lists. A custom (OpenAI-compatible) profile is open-ended as well: when you create it you can name **several models at once, separated by spaces or commas** (for example `DeepSeek-V4.1-Flash Kimi-K3 Qwen3.8-Max`), and those become the profile's catalogue — both agents can then pick them. Any other name the endpoint serves still works with `ai-switch use NAME --model <name>`, and it is added to the catalogue when you use it.
137
+
138
+ ### When a provider adds or renames a model
139
+
140
+ Model names change, and an endpoint refuses a name it does not know. Ask the provider what your key may use:
141
+
142
+ ```bash
143
+ curl -s https://api.deepseek.com/models -H "Authorization: Bearer $KEY" # OpenAI-style list
144
+ curl -s https://open.bigmodel.cn/api/v1/models -H "Authorization: Bearer $KEY" # Codex-format catalogue
145
+ ```
146
+
147
+ A rejected name is also reported in the API error itself (*"The supported API model names are ..."*, or `模型不存在,请检查模型代码。`). Some providers publish their Codex catalogue directly (`/api/v1/models` on ZAI returns exactly the entries a profile's `codex-models.json` wants), so it can be copied verbatim. Historically DeepSeek answered to `deepseek-v4-flash` and `deepseek-v4-flash-vision-exp`; today those are aliases that the endpoint resolves to `deepseek-flash`.
148
+
149
+ To add a model, append it to the profile's `models.json` — the `claude` mapping decides what Opus/Sonnet/Haiku resolve to, the `codex` entry controls whether Codex's own picker offers it. Keep the entries of names that older sessions were recorded with in the Codex catalogue so those sessions stay resumable, then activate:
150
+
151
+ ```bash
152
+ ai-switch use deepseek -m deepseek-flash
153
+ ```
154
+
155
+ Model suffixes are proxy-specific: DeepSeek's Claude endpoint accepts and strips a `[1m]` marker (`deepseek-v4-pro[1m]`), while ZAI rejects it as an unknown model — `glm-5.3` is already 1M context and must be used without a suffix. Worth a one-token request before trusting a name in a profile.
156
+
157
+ ## Session history
158
+
159
+ Conversation history belongs to the agents, not to a provider, so ai-switch never manages it. These files are deliberately excluded from profiles and switches:
160
+
161
+ ```text
162
+ ~/.codex/history.jsonl, ~/.codex/session_index.jsonl, ~/.codex/sessions/,
163
+ ~/.codex/*_N.sqlite (runtime state, logs, goals, memories)
164
+ ~/.claude.json, ~/.claude/history.jsonl, ~/.claude/projects/, ~/.claude/sessions/
165
+ ```
166
+
167
+ If sessions do seem to disappear after a switch, run:
168
+
169
+ ```bash
170
+ ai-switch doctor # full report; --json for tooling
171
+ ai-switch doctor --fix # quarantine damaged Codex runtime databases
172
+ ```
173
+
174
+ `doctor` checks, among other things:
175
+
176
+ * **Damaged SQLite runtime databases.** Codex keeps thread metadata in `~/.codex/state_*.sqlite` and `thread_history_1.sqlite`. When one is damaged (typical messages: `database disk image is malformed`, `file is not a database`) Codex silently stops listing recent sessions in `codex resume`. `doctor` detects this — reading a snapshot copy when the live file cannot be opened — and `--fix` moves the damaged files into `~/.config/ai-switch/quarantine/<timestamp>/`. Codex rebuilds the database from the rollout files on the next start, which brings resumable sessions back. Rollouts and history are not touched.
177
+ * **Network filesystems.** If `CODEX_HOME` lives on NFS/CIFS/SMB, SQLite databases are corrupted sooner or later, which is the usual root cause of "history lost after switching". Keep the runtime databases on a local disk instead:
178
+
179
+ ```bash
180
+ export CODEX_SQLITE_HOME=/var/tmp/codex-sqlite-$USER # a local, persistent directory
181
+ ```
182
+
183
+ Codex only honours the **environment variable**: a `sqlite_home` key in `config.toml` is parsed but ignored (checked with `codex doctor`). `ai-switch add` asks for a local directory when it detects a network filesystem, creates it and prints the export line to put in your shell profile; `ai-switch doctor` reports the variable's status.
184
+ * **A stale model catalogue.** A catalogue left behind by the previous provider hides the new provider's models from Codex and makes sessions that used them unresumable. ai-switch removes that leftover file (only if it wrote it itself, and it backs it up first), and `doctor` verifies the configured model exists in the catalogue.
185
+ * **Disabled history persistence** (`[history] persistence = "none"`), a Claude Code model pinned by `ANTHROPIC_MODEL`, and `claude`/`codex` processes that are running while you switch and will rewrite their configuration on exit.
186
+
187
+ `codex resume` only lists sessions of the current directory by default; `codex resume --all` shows every directory. That cwd filter, not a lost session, is a common false alarm.
188
+
189
+ ## Safety and storage
190
+
191
+ Before activation, the current Codex and Claude files are backed up under `~/.config/ai-switch/backups/<timestamp>/` (files that a switch removes are kept under `removed/`). Profiles are stored under `~/.config/ai-switch/profiles/`; directories use mode 700 and files use mode 600. The active profile and the last used model are recorded in `~/.config/ai-switch/state.json`. Restart `claude` or `codex` after switching so the process reloads its configuration.
192
+
193
+ Set `AI_SWITCH_HOME` to use a different profile directory, `CODEX_HOME`/`CLAUDE_CONFIG_DIR` for relocated agent configurations, and `AI_SWITCH_API_KEY` to fill the API key prompt non-interactively.
194
+
195
+ ## Commands
196
+
197
+ ```text
198
+ ai-switch init NAME [-d DESCRIPTION] Save current files as a new profile
199
+ ai-switch list List profiles and configuration summaries
200
+ ai-switch use NAME [-m MODEL] [-y] [-n] Back up and activate a profile (and a model)
201
+ ai-switch models [NAME] [--json] Show the models a profile offers
202
+ ai-switch current Print the active profile
203
+ ai-switch describe NAME TEXT Set a profile description
204
+ ai-switch upgrade NAME Derive models.json for a profile from an older release
205
+ ai-switch add Create a profile interactively (GLM, DeepSeek, or custom)
206
+ ai-switch doctor [--fix] [--json] Check configuration and session-history health
207
+ ai-switch --help Show full usage and examples
208
+ ```
209
+
210
+ ## Upgrading from 0.1
211
+
212
+ Existing profiles keep working. Profiles that only have `codex-models.json` are read as before and get a model picker automatically; run `ai-switch upgrade NAME` (or re-create the profile with `ai-switch add`) to materialise an editable `models.json` with explicit per-model Claude Code mappings.
213
+
214
+ ## Development
215
+
216
+ ```bash
217
+ python3 -m unittest -v
218
+ ```
219
+
220
+ The project uses only the Python standard library.
@@ -0,0 +1,206 @@
1
+ # ai-switch
2
+
3
+ ## The problem it solves
4
+
5
+ When you use multiple coding agents and multiple model providers, changing providers separately in Claude Code and Codex is repetitive and error-prone. **ai-switch updates both agents in one command.**
6
+
7
+ Choose a provider profile once, then activate it with `ai-switch use NAME`. The built-in GLM and DeepSeek presets already contain the provider-specific files, endpoints, protocols, model catalogs, and Claude Code mappings recommended by their official documentation. You only need to enter your API key. Custom OpenAI-compatible providers are supported too.
8
+
9
+ A profile can offer several models (for example GLM 5.3 and GLM 5.3 Flash, or DeepSeek Flash and DeepSeek V4 Pro). `ai-switch use` asks which one to activate and writes the answer to both agents, so the model pickers *inside* Claude Code and Codex keep working instead of being pinned to a single model.
10
+
11
+ Switching also leaves conversation history alone: sessions, rollouts, `history.jsonl` and the agents' runtime databases are never copied into a profile or overwritten by a switch, and `ai-switch doctor` reports the things that really do make old sessions disappear.
12
+
13
+ This is a secure, headless-friendly command-line tool for servers without a desktop environment or administrator privileges.
14
+
15
+ ## Install
16
+
17
+ Install from PyPI (recommended):
18
+
19
+ ```bash
20
+ python3 -m pip install --user ai-switch-cli
21
+ ```
22
+
23
+ For a system or virtual-environment install:
24
+
25
+ ```bash
26
+ pip install ai-switch-cli
27
+ ```
28
+
29
+ Install the latest development version directly from GitHub:
30
+
31
+ ```bash
32
+ python3 -m pip install --user "git+https://github.com/ZidongS/ai-switch.git"
33
+ ```
34
+
35
+ Or install a local checkout:
36
+
37
+ ```bash
38
+ python3 -m pip install --user .
39
+ ```
40
+
41
+ If `ai-switch` is not found afterwards, add the user script directory to `PATH`:
42
+
43
+ ```bash
44
+ export PATH="$HOME/.local/bin:$PATH"
45
+ ```
46
+
47
+ ## Quick start
48
+
49
+ Save the configuration currently in use as a fallback profile:
50
+
51
+ ```bash
52
+ ai-switch init default --description "Default daily configuration"
53
+ ```
54
+
55
+ Create a ready-to-use mainstream provider profile interactively:
56
+
57
+ ```bash
58
+ ai-switch add
59
+ ```
60
+
61
+ Select `glm` or `deepseek`, enter the API key, and activate the generated configuration:
62
+
63
+ ```bash
64
+ ai-switch use glm
65
+ ```
66
+
67
+ `use` lists the models the profile offers and remembers your choice:
68
+
69
+ ```text
70
+ This profile provides 2 models:
71
+ 1) glm-5.3 GLM-5.3 flagship (1M context) [default]
72
+ 2) glm-5.3-flash GLM-5.3 flash (fast, 1M context)
73
+ Select model [1-2, Enter=glm-5.3, q=cancel]:
74
+ ```
75
+
76
+ For scripts and servers, choose without prompting (name, unique prefix, or index):
77
+
78
+ ```bash
79
+ ai-switch use glm --model glm-5.3-flash
80
+ ai-switch use glm -m 2 -y
81
+ ai-switch use glm --dry-run # show what would change
82
+ ```
83
+
84
+ Edit `~/.codex/config.toml` and `~/.claude/settings.json` for another provider, then save that configuration as a second profile:
85
+
86
+ ```bash
87
+ ai-switch init glm --description "GLM Coding Plan"
88
+ ai-switch list
89
+ ai-switch use glm
90
+ ai-switch current
91
+ ```
92
+
93
+ Create a profile through an interactive prompt (no editor required; API keys are hidden while typing). Choose the built-in `glm` preset to generate the complete ZAI Codex Responses configuration, Codex model catalog and Claude Code model/environment mappings for the three models the endpoint publishes (`glm-5.3`, `glm-5.3-flash`, `glm-5-turbo`) automatically. The `deepseek` preset creates the two model entries the DeepSeek endpoint currently accepts (`deepseek-flash` and `deepseek-v4-pro`, both with image input metadata) plus the recommended Claude Code mappings:
94
+
95
+ ```bash
96
+ ai-switch add
97
+ ```
98
+
99
+ Update an existing profile description:
100
+
101
+ ```bash
102
+ ai-switch describe default "Default daily configuration"
103
+ ```
104
+
105
+ `list` shows the active marker, profile name, configured clients, description, detected models, and endpoint hostnames. API keys are never printed.
106
+
107
+ ## Choosing a model
108
+
109
+ Each profile keeps its model list in `models.json` next to the client files:
110
+
111
+ ```bash
112
+ ai-switch models # list the models of the active profile
113
+ ai-switch models glm --json # machine readable
114
+ ai-switch upgrade glm # derive models.json for a profile from an older release
115
+ ```
116
+
117
+ For every model a profile records the Codex catalogue entry and the Claude Code mapping, so activating one model updates all of these consistently:
118
+
119
+ * Codex: the top-level `model` (and `model_reasoning_effort`), plus a `~/.codex/models.json` catalogue that lists **every** model of the profile, which is what makes Codex's own `/model` picker show them.
120
+ * Claude Code: the default model in `settings.json` plus the Opus/Sonnet/Haiku mappings. The presets map the three categories to *different* provider models where they exist, and they never set `ANTHROPIC_MODEL`, because a pinned environment model overrides your selection and makes `/model` do nothing.
121
+
122
+ The `glm` and `deepseek` presets ship multi-model lists. A custom (OpenAI-compatible) profile is open-ended as well: when you create it you can name **several models at once, separated by spaces or commas** (for example `DeepSeek-V4.1-Flash Kimi-K3 Qwen3.8-Max`), and those become the profile's catalogue — both agents can then pick them. Any other name the endpoint serves still works with `ai-switch use NAME --model <name>`, and it is added to the catalogue when you use it.
123
+
124
+ ### When a provider adds or renames a model
125
+
126
+ Model names change, and an endpoint refuses a name it does not know. Ask the provider what your key may use:
127
+
128
+ ```bash
129
+ curl -s https://api.deepseek.com/models -H "Authorization: Bearer $KEY" # OpenAI-style list
130
+ curl -s https://open.bigmodel.cn/api/v1/models -H "Authorization: Bearer $KEY" # Codex-format catalogue
131
+ ```
132
+
133
+ A rejected name is also reported in the API error itself (*"The supported API model names are ..."*, or `模型不存在,请检查模型代码。`). Some providers publish their Codex catalogue directly (`/api/v1/models` on ZAI returns exactly the entries a profile's `codex-models.json` wants), so it can be copied verbatim. Historically DeepSeek answered to `deepseek-v4-flash` and `deepseek-v4-flash-vision-exp`; today those are aliases that the endpoint resolves to `deepseek-flash`.
134
+
135
+ To add a model, append it to the profile's `models.json` — the `claude` mapping decides what Opus/Sonnet/Haiku resolve to, the `codex` entry controls whether Codex's own picker offers it. Keep the entries of names that older sessions were recorded with in the Codex catalogue so those sessions stay resumable, then activate:
136
+
137
+ ```bash
138
+ ai-switch use deepseek -m deepseek-flash
139
+ ```
140
+
141
+ Model suffixes are proxy-specific: DeepSeek's Claude endpoint accepts and strips a `[1m]` marker (`deepseek-v4-pro[1m]`), while ZAI rejects it as an unknown model — `glm-5.3` is already 1M context and must be used without a suffix. Worth a one-token request before trusting a name in a profile.
142
+
143
+ ## Session history
144
+
145
+ Conversation history belongs to the agents, not to a provider, so ai-switch never manages it. These files are deliberately excluded from profiles and switches:
146
+
147
+ ```text
148
+ ~/.codex/history.jsonl, ~/.codex/session_index.jsonl, ~/.codex/sessions/,
149
+ ~/.codex/*_N.sqlite (runtime state, logs, goals, memories)
150
+ ~/.claude.json, ~/.claude/history.jsonl, ~/.claude/projects/, ~/.claude/sessions/
151
+ ```
152
+
153
+ If sessions do seem to disappear after a switch, run:
154
+
155
+ ```bash
156
+ ai-switch doctor # full report; --json for tooling
157
+ ai-switch doctor --fix # quarantine damaged Codex runtime databases
158
+ ```
159
+
160
+ `doctor` checks, among other things:
161
+
162
+ * **Damaged SQLite runtime databases.** Codex keeps thread metadata in `~/.codex/state_*.sqlite` and `thread_history_1.sqlite`. When one is damaged (typical messages: `database disk image is malformed`, `file is not a database`) Codex silently stops listing recent sessions in `codex resume`. `doctor` detects this — reading a snapshot copy when the live file cannot be opened — and `--fix` moves the damaged files into `~/.config/ai-switch/quarantine/<timestamp>/`. Codex rebuilds the database from the rollout files on the next start, which brings resumable sessions back. Rollouts and history are not touched.
163
+ * **Network filesystems.** If `CODEX_HOME` lives on NFS/CIFS/SMB, SQLite databases are corrupted sooner or later, which is the usual root cause of "history lost after switching". Keep the runtime databases on a local disk instead:
164
+
165
+ ```bash
166
+ export CODEX_SQLITE_HOME=/var/tmp/codex-sqlite-$USER # a local, persistent directory
167
+ ```
168
+
169
+ Codex only honours the **environment variable**: a `sqlite_home` key in `config.toml` is parsed but ignored (checked with `codex doctor`). `ai-switch add` asks for a local directory when it detects a network filesystem, creates it and prints the export line to put in your shell profile; `ai-switch doctor` reports the variable's status.
170
+ * **A stale model catalogue.** A catalogue left behind by the previous provider hides the new provider's models from Codex and makes sessions that used them unresumable. ai-switch removes that leftover file (only if it wrote it itself, and it backs it up first), and `doctor` verifies the configured model exists in the catalogue.
171
+ * **Disabled history persistence** (`[history] persistence = "none"`), a Claude Code model pinned by `ANTHROPIC_MODEL`, and `claude`/`codex` processes that are running while you switch and will rewrite their configuration on exit.
172
+
173
+ `codex resume` only lists sessions of the current directory by default; `codex resume --all` shows every directory. That cwd filter, not a lost session, is a common false alarm.
174
+
175
+ ## Safety and storage
176
+
177
+ Before activation, the current Codex and Claude files are backed up under `~/.config/ai-switch/backups/<timestamp>/` (files that a switch removes are kept under `removed/`). Profiles are stored under `~/.config/ai-switch/profiles/`; directories use mode 700 and files use mode 600. The active profile and the last used model are recorded in `~/.config/ai-switch/state.json`. Restart `claude` or `codex` after switching so the process reloads its configuration.
178
+
179
+ Set `AI_SWITCH_HOME` to use a different profile directory, `CODEX_HOME`/`CLAUDE_CONFIG_DIR` for relocated agent configurations, and `AI_SWITCH_API_KEY` to fill the API key prompt non-interactively.
180
+
181
+ ## Commands
182
+
183
+ ```text
184
+ ai-switch init NAME [-d DESCRIPTION] Save current files as a new profile
185
+ ai-switch list List profiles and configuration summaries
186
+ ai-switch use NAME [-m MODEL] [-y] [-n] Back up and activate a profile (and a model)
187
+ ai-switch models [NAME] [--json] Show the models a profile offers
188
+ ai-switch current Print the active profile
189
+ ai-switch describe NAME TEXT Set a profile description
190
+ ai-switch upgrade NAME Derive models.json for a profile from an older release
191
+ ai-switch add Create a profile interactively (GLM, DeepSeek, or custom)
192
+ ai-switch doctor [--fix] [--json] Check configuration and session-history health
193
+ ai-switch --help Show full usage and examples
194
+ ```
195
+
196
+ ## Upgrading from 0.1
197
+
198
+ Existing profiles keep working. Profiles that only have `codex-models.json` are read as before and get a model picker automatically; run `ai-switch upgrade NAME` (or re-create the profile with `ai-switch add`) to materialise an editable `models.json` with explicit per-model Claude Code mappings.
199
+
200
+ ## Development
201
+
202
+ ```bash
203
+ python3 -m unittest -v
204
+ ```
205
+
206
+ The project uses only the Python standard library.