# Pi Switch > A Windows desktop LLM provider config manager that lets you switch between multiple LLM providers and models across the **Kimi Code CLI** and **Pi** agents. **English** | [中文](./README.md) [![Tauri](https://img.shields.io/badge/Tauri-v2-blue?logo=tauri)](https://tauri.app) [![React](https://img.shields.io/badge/React-18-61dafb?logo=react)](https://react.dev) [![TypeScript](https://img.shields.io/badge/TypeScript-5.5-3178c6?logo=typescript)](https://www.typescriptlang.org) [![Rust](https://img.shields.io/badge/Rust-2021-ed764d?logo=rust)](https://www.rust-lang.org) [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE) --- ## Table of Contents - [What Is This](#what-is-this) - [Key Features](#key-features) - [Architecture Overview](#architecture-overview) - [Supported Provider Types](#supported-provider-types) - [Data Storage Locations](#data-storage-locations) - [Getting Started](#getting-started) - [Development Guide](#development-guide) - [Keyboard Shortcuts](#keyboard-shortcuts) - [Internationalization](#internationalization) - [Testing](#testing) - [Build & Release](#build--release) - [FAQ](#faq) - [Security Notes](#security-notes) --- ## What Is This **Pi Switch** is a Windows desktop configuration tool built for LLM CLI users. It solves two pain points: 1. **Tedious multi-provider management**: switching between Kimi / Anthropic / OpenAI / Google GenAI / self-hosted proxies requires repeatedly hand-editing TOML/JSON, which is error-prone. 2. **Fragmented multi-agent configs**: developers using both **Kimi Code CLI** and **Pi** have to maintain two independent config formats, duplicating every change. Pi Switch provides a unified GUI: - One unified interface to manage two separate agent configs (Kimi Code and Pi), switched via a top tab - One-click provider switching that writes the active provider into the target agent's native config - One-click model-list fetching (OpenAI / Anthropic / Google GenAI) - A `/reload` hint after switching so Kimi Code sessions take effect immediately ## Key Features | Category | Feature | | --- | --- | | **Multi-agent** | Manage Kimi Code and Pi configs independently without interference, via a top tab | | **Multi-provider** | Kimi / Anthropic / OpenAI / OpenAI Responses / Google GenAI / Vertex AI | | **Model mapping** | Alias ↔ real model ID, with display name, context size, role (Sonnet/Opus/Fable/Haiku), and 1M-context declaration | | **One-click discovery** | Auto-fetch available model lists from the provider API | | **Managed providers** | Flag OAuth/managed providers to skip credential validation and preserve the `oauth` section in Kimi Code config | | **Env credentials** | Supports both the `api_key` field and env-table keys (e.g. `OPENAI_API_KEY`) | | **Global settings** | Thinking toggle/level, loop retries, background tasks, permission rules, lifecycle hooks (Kimi Code only) | | **Raw JSON editing** | Power users can hand-edit the full config JSON (unknown fields are preserved, never dropped) | | **i18n** | Simplified Chinese / English, switched at runtime | | **Auto backup** | Before writing the Kimi Code / Pi native configs, back up with timestamp-based names and keep the last 7 days (see `src-tauri/src/config_io.rs`) | | **Shortcuts** | Ctrl+S save, Ctrl+R reload, Ctrl+O open config dir | | **Validation (reserved)** | i18n error strings are defined (duplicate names, missing credentials, missing Vertex fields, etc.), but the backend `validators.rs` is a stub and not yet wired | | **Unsaved-changes prompt** | Detects unsaved edits before closing the window and prefixes the title bar with `*` | ## Architecture Overview ``` ┌────────────────────────────────────────────────────────────────────┐ │ Pi Switch (Tauri v2) │ │ │ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │ React Frontend (TS) │ │ Rust Backend (lib.rs) │ │ │ │ │ │ │ │ │ │ src/App.tsx │◄──►│ src-tauri/src/ │ │ │ │ src/components/ │ Tauri ├── commands.rs │ │ │ │ ProviderList │ invoke ├── db.rs │ │ │ │ ProviderEdit │ ├── kimi_code_io │ │ │ │ AgentSettingsPanel │ ├── pi_io.rs │ │ │ │ src/hooks/useConfig │ ├── config_io.rs │ │ │ │ src/i18n/{zh,en}.ts │ ├── validators.rs │ │ │ │ src/types/index.ts │ └── profile_manager│ │ │ └──────────────────────────┘ └──────────────────────────┘ │ │ │ │ │ └──────────────────┼──────────────────────────────┼──────────────────┘ │ │ ▼ ▼ ┌──────────────────────┐ ┌──────────────────────────┐ │ SQLite │ │ Agent native configs │ │ ~/.pi-switch/ │ │ ├─ ~/.kimi-code/ │ │ pi-switch.db │ │ │ └─ config.toml │ │ │ │ └─ ~/.pi/agent/ │ │ (Pi Switch internal)│ │ ├─ models.json │ └──────────────────────┘ │ └─ settings.json │ └──────────────────────────┘ ``` **Key design points**: - Pi Switch keeps its own SQLite database for the **full config** (all providers + all models, including inactive ones) - Clicking "Switch to" writes **only the active provider** into the agent's native config; the others are not written - This way Kimi Code / Pi always see exactly one active provider and are never confused by managed/OAuth defaults - The `raw_other` field passes unknown keys through untouched, so round-trips never lose fields ## Supported Provider Types | Type | Identifier | Default base_url | Model discovery | Credentials | | --- | --- | --- | --- | --- | | Kimi | `kimi` | `https://api.openai.com/v1` | ✅ OpenAI protocol | `KIMI_API_KEY` or `env.KIMI_API_KEY` | | Anthropic | `anthropic` | — | ✅ `/v1/models` | `ANTHROPIC_API_KEY` or `env` | | OpenAI | `openai` | `https://api.openai.com/v1` | ✅ `/models` | `OPENAI_API_KEY` or `env` | | OpenAI Responses | `openai_responses` | `https://api.openai.com/v1` | ✅ `/models` | `OPENAI_API_KEY` or `env` | | Google GenAI | `google-genai` | `https://generativelanguage.googleapis.com` | ✅ `/v1beta/models` | `GOOGLE_API_KEY` or `env` | | Vertex AI | `vertexai` | — | ⚠️ Not yet implemented | `VERTEXAI_API_KEY` + `GOOGLE_CLOUD_PROJECT` + `GOOGLE_CLOUD_LOCATION` | Credential precedence: the `api_key` field wins over the same-named key in the `env` table. ## Data Storage Locations | File | Purpose | Backup | | --- | --- | --- | | `%USERPROFILE%\.pi-switch\pi-switch.db` | Pi Switch's own SQLite database holding the full config | — | | `%USERPROFILE%\.kimi-code\config.toml` | Kimi Code CLI's TOML config (**written on switch**) | `backups/config.toml.bak.{YYYYMMDD_HHMMSS}` next to it, kept 7 days | | `%USERPROFILE%\.pi\agent\models.json` | Pi's provider+model config (**written on switch**) | `backups/models.json.bak.{YYYYMMDD_HHMMSS}` next to it, kept 7 days | | `%USERPROFILE%\.pi\agent\settings.json` | Pi's default provider/model (**written on switch**) | `backups/settings.json.bak.{YYYYMMDD_HHMMSS}` next to it, kept 7 days | | `localStorage[pi-switch-agent]` | Frontend remembers the last selected agent (kimi_code / pi) | — | Environment-variable overrides: - `KIMI_CODE_HOME` overrides the Kimi Code config dir (default `~/.kimi-code`) - `PI_CODING_AGENT_DIR` overrides the Pi agent config dir (default `~/.pi/agent`) ## Getting Started ### Prerequisites | Tool | Version | Purpose | | --- | --- | --- | | Node.js | ≥ 18 | Frontend build | | Rust | Latest stable (edition 2021) | Tauri backend compilation | | WebView2 Runtime | Bundled with Windows 10/11 | Tauri v2 runtime | | Microsoft C++ Build Tools | Latest | Rust compile dependency | | WiX Toolset 3.14 | `src-tauri/wix314-binaries/` | MSI bundling (auto-downloaded on first build) | ### Install ```bash npm install ``` ### Dev mode (hot reload) ```bash npm run tauri-dev ``` Starts both the Vite dev server (port 1420) and the Tauri window. Frontend edits hot-reload; Rust edits trigger a recompile. ### Frontend-only dev (no Tauri window) ```bash npm run dev ``` Good for pure UI debugging. ## Development Guide ### Project structure ``` . ├── src/ # React frontend │ ├── App.tsx # Root component routing ProviderList/ProviderEdit │ ├── main.tsx # React entry + ErrorBoundary + I18nProvider │ ├── components/ │ │ ├── ProviderList.tsx # Provider list + switch button │ │ ├── ProviderEdit.tsx # Edit provider + model mapping + raw JSON │ │ └── AgentSettingsPanel.tsx# Global settings (thinking/loop/permissions/hooks) │ ├── hooks/ │ │ └── useConfig.ts # Config load/save hook │ ├── lib/ │ │ ├── agent-settings.ts # AgentSettings parse/serialize │ │ └── model-defaults.ts # Default model context sizes │ ├── types/index.ts # Provider/Model/Config type definitions │ ├── i18n/ │ │ ├── zh.ts # Chinese translations (143 keys) │ │ ├── en.ts # English translations │ │ └── index.tsx # useTranslation hook + Provider │ └── index.css # Tailwind entry │ ├── src-tauri/ # Rust backend │ ├── src/ │ │ ├── lib.rs # Tauri Builder + invoke_handler registration │ │ ├── main.rs # Binary entry │ │ ├── commands.rs # 7 Tauri commands │ │ ├── db.rs # SQLite persistence │ │ ├── kimi_code_io.rs # ~/.kimi-code/config.toml read/write │ │ ├── pi_io.rs # ~/.pi/agent/*.json read/write │ │ ├── config_io.rs # File backup utilities │ │ ├── models.rs # Config/Provider/Model data structures │ │ ├── profile_manager.rs # Multi-profile management (stub) │ │ └── validators.rs # Config validation │ ├── capabilities/ # Tauri permission declarations │ ├── icons/ # App icons (script-generated) │ └── tauri.conf.json # Tauri config (window/bundle/CSP) │ ├── scripts/generate-icons.py # Generate all icon sizes from SVG ├── public/pi.svg # App icon source (blue-purple gradient π) └── docs/superpowers/ # Design specs & implementation plans ``` ### Tauri commands (frontend ↔ backend) | Command | Description | | --- | --- | | `load_agent_config_command(agent)` | Load config: SQLite first, falling back to the agent's native config on first use | | `save_agent_config_command(agent, config)` | Save the full config to SQLite | | `activate_agent_config_command(agent)` | Write the active provider into the agent's native config | | `open_agent_config_dir(agent)` | Open the agent's config dir in the system file manager | | `get_app_version()` | Return the `Cargo.toml` version | | `list_provider_models(provider)` | Fetch model list from the provider API (async) | | `debug_log(message)` | Forward frontend logs to stderr (dev use) | ### Adding a new provider type 1. Add a new variant to the `ProviderType` enum in `src-tauri/src/models.rs` 2. Add a default in `default_base_url()` 3. Add a dispatch arm in `commands.rs::list_provider_models` 4. Add mappings in `kimi_code_io.rs::provider_type_for_kimi_type` and `pi_io.rs::provider_type_for_pi_api` 5. Add an option to the API-format dropdown in `src/components/ProviderEdit.tsx` 6. Add new i18n keys in `src/i18n/{zh,en}.ts` ### Adding a new Tauri command 1. Add a `#[tauri::command]` in `src-tauri/src/commands.rs` 2. Register it in the `tauri::generate_handler![...]` list in `src-tauri/src/lib.rs` 3. Call it from the frontend with `import { invoke } from "@tauri-apps/api/core"` 4. Add a permission in `src-tauri/capabilities/default.json` (if filesystem access is needed) ## Keyboard Shortcuts | Shortcut | Action | | --- | --- | | `Ctrl + S` | Save current changes to SQLite | | `Ctrl + R` | Reload config (prompts if unsaved) | | `Ctrl + O` | Open the current agent's config dir | ## Internationalization - Translation sources: `src/i18n/zh.ts` (source) and `src/i18n/en.ts` (target) - When adding a key, **add it to `zh.ts` first** — the `Record` type in `en.ts` will flag any missing entry at compile time - Language is switched at runtime via the top-right dropdown and held in component state (not persisted to SQLite) ## Testing ### Rust unit tests ```bash cd src-tauri cargo test ``` Currently covered: - `kimi_code_io::tests` — TOML import/export round-trips - `pi_io::tests` — JSON round-trips, including advanced fields (headers/compat/cost/extra) ### Frontend No automated tests yet. Suggested manual checklist: - [ ] Switching agents doesn't cross-contaminate configs - [ ] Deleting a provider also deletes its models - [ ] The default model restores correctly after switching providers - [ ] Renaming a provider updates models that reference it - [ ] Raw-JSON editing preserves `raw_other` fields ## Build & Release ```bash npm run tauri-build ``` Output: - `src-tauri/target/release/bundle/msi/Pi Switch_0.1.0_x64_en-US.msi` A Windows installer (MSI) with the WebView2 bootstrapper embedded for auto-download. `nsis` is disabled; only MSI is produced. ### First build The first run downloads: - WiX Toolset 3.14 binaries → `src-tauri/wix314-binaries/` - WebView2 bootstrapper → embedded into the MSI ## FAQ **Q: Kimi Code didn't pick up the change after switching providers?** A: Run `/reload` inside the Kimi Code session (the CLI only re-reads `~/.kimi-code/config.toml` on reload). The app shows this hint in the UI. **Q: I edited the config but closed the window without saving?** A: A native `beforeunload` prompt appears before closing, and the title bar shows a `*` prefix. **Q: How do I back up / migrate my config?** A: Back up `%USERPROFILE%\.pi-switch\pi-switch.db`; it holds the full config (including inactive providers). **Q: Why can't Vertex AI fetch the model list?** A: Vertex requires GCP project/location credentials. The current implementation leaves a TODO pending GCP SDK integration. **Q: macOS / Linux support?** A: The code doesn't depend on Windows-only APIs, but `tauri.conf.json` only targets `msi` for bundling. In theory, changing `bundle.targets` to `["app", "dmg"]` etc. would enable cross-platform builds, but this is unverified. ## Security Notes - API keys are stored in plaintext in the local SQLite database and agent native configs — **do not store them on shared computers** - Do not commit `pi-switch.db`, `config.toml`, or `models.json` to Git - The app CSP is tightened (`default-src 'self'`), but WebView2 may still cache form content — log out when finished on public machines --- ## Appendix: Related Projects - [Kimi Code CLI](https://github.com/MoonshotAI/kimi-cli) — one of the compatible agents - [Tauri](https://tauri.app) — the desktop app framework - Design specs in `docs/superpowers/specs/`, implementation plans in `docs/superpowers/plans/` --- License: MIT, see [LICENSE](./LICENSE). Copyright (c) 2026 CodingPlan.site