
Product
Introducing Socket Scanning for VS Code Marketplace Extensions
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.
Pi package: switch providers/models from local cc-switch, with parameter overrides and short display labels
English | 中文
pi-switch is a Pi extension package built on top of cc-switch. It uses cc-switch as the source of provider and model configuration, then exposes a fast provider/model switcher directly inside Pi.
pi-switch does not replace cc-switch and does not modify the cc-switch database. It reads the local cc-switch SQLite database in read-only mode, registers the selected provider in Pi, and stores the active model in Pi settings.
The screenshots below are sample illustrations of the interaction flow. Actual providers, models, and paths depend on your local cc-switch data.
/ps-config (optional alias: /ccs)./ps quick switch: pins + recents on one screen, one Enter for the daily hot path./), manually enter model IDs, refresh remote lists, pin favorites with p, and page through long lists with PgUp/PgDn.originator + X-Codex-Window-ID, Claude Code claude-cli/... (external, cli) + anthropic-version/anthropic-beta, GeminiCLI UA + x-goog-api-client)./ps-override or picker key o) — e.g. 中转兼容 sets reasoning=false when a relay rejects thinking./ps-doctor (PASS/WARN/FAIL + fix hints)./ps-probe (basic / reasoning / tool contracts, structured evidence, JSON in headless/CI)./ps-repair (interactive only): re-probe → whitelist Recipe → confirm → in-memory candidate verify → CAS commit, without switching the Session Model.diagnose-upstream skill as supplemental knowledge for upstream / relay troubleshooting.See SPEC.md for the full product contract.
cc-switch is the upstream configuration manager. pi-switch depends on the local cc-switch data model and treats cc-switch as the source of truth for providers.
pi-switch is intentionally scoped as a Pi-side bridge:
~/.cc-switch/cc-switch.db.This means you should configure providers in cc-switch first, then use pi-switch to select and activate them inside Pi.
| This project (Bandersnatch0x/pi-switch) | Not this project |
|---|---|
| cc-switch → Pi bridge | Local HTTP gateway / reverse proxy |
Read-only consumer of cc-switch.db | All-in-One provider CRUD manager |
In-process Pi extension (/ps-config) | Standalone daemon with WebUI |
| Local pin / recent shortcuts only | Multi-tool expose / config center |
If you need a local gateway that terminates requests and manages providers itself, look at projects such as @cokefenta/pi-switch / CallmeLins/pi-switch. This repo intentionally stays a thin Pi-side bridge on top of cc-switch.
┌──────────────────────┐
│ cc-switch │
│ provider management │
└──────────┬───────────┘
│ read-only SQLite
▼
┌──────────────────────┐
│ pi-switch │
│ DB read + normalize │
└──────────┬───────────┘
│ parsed providers
▼
┌──────────────────────┐
│ interactive picker │
│ type → name → model │
│ (+ override dialog)│
└──────────┬───────────┘
│ selected provider/model
▼
┌──────────────────────┐
│ Pi │
│ register + setModel │
└──────────────────────┘
Main modules:
pi-switch/
├─ extensions/
│ └─ index.ts # Pi entry: /ps-config, /ps-doctor, /ps-override
├─ src/
│ ├─ db.ts # Read the cc-switch SQLite database
│ ├─ register.ts # Build and register Pi providers
│ ├─ settings.ts # Pi settings, selection, pins/recent, overrides
│ ├─ model-meta.ts # modelMeta presets + resolution
│ ├─ doctor.ts # /ps-doctor pure checks
│ ├─ sqlite-path.ts # sqlite3 executable resolution
│ ├─ models-fetch.ts # Remote model discovery and merging
│ ├─ headers/ # Header rule loading, merge, and vars
│ ├─ parse/ # cc-switch provider config parsers
│ └─ ui/
│ ├─ three-level-pick.ts # Progressive type → name → model picker
│ ├─ model-meta-dialog.ts # Non-interactive dialog (fallback / tests)
│ ├─ model-meta-form.ts # TUI SettingsList form for modelMeta overrides
│ ├─ labels.ts # Display labels and status text
│ └─ tabs.ts # Tab helpers
├─ skills/
│ └─ diagnose-upstream/ # Upstream / relay diagnostics skill
├─ defaults/
│ └─ headers.json # Default header rules
├─ docs/
│ └─ images/ # README sample screenshots
├─ tests/ # Bun tests
├─ SPEC.md # Product contract (maintainers)
└─ package.json
pi install npm:pi-ccs
After the package is published publicly on npm with the pi-package keyword, it can also appear in the Pi package catalog. There is no separate submission form — catalog discovery is based on public npm metadata (keywords includes pi-package, plus a valid package.json pi manifest).
Direct catalog page after listing:
https://pi.dev/packages/pi-ccs
pi install git:github.com/Bandersnatch0x/pi-switch
Git installs work even before npm / catalog listing.
pi update npm:pi-ccs
pi config
Pi packages usually land under ~/.pi/agent/npm/. With project-local installation, they are placed under .pi/npm/ in the current project.
In a Pi session, run:
/ps-config
Aliases:
/ccs
Quick switch for the hot path (pins + recents, one screen):
/ps
To edit model parameter overrides (for example disable reasoning for a Claude-protocol → GLM relay):
/ps-override
In the provider picker, after the Name column is revealed, press o to open the same override dialog for the focused provider. The footer shows o override.
Switching a provider/model means “the model is listed” ≠ “requests actually work”. Verify and repair out-of-band:
/ps-probe
Read-only probe against the current/selected Target. Sends isolated synthetic requests (never your conversation history):
basic — plain-text contractreasoning — controlled thinking contract (only when the target claims reasoning support)tool — side-effect-free probe_echo tool contractOutputs structured evidence grouped into wide failure categories (auth / model / protocol / streaming / tool / client-gate); ambiguous evidence yields unknown (no guessing). Hard budget: max 9 requests, 15s each, ≤32 output tokens; 401/429/5xx stop immediately. headless/CI emits JSON.
/ps-repair
Evidence-driven repair, interactive only (headless is rejected — a persistent config change needs consent). Re-probes fresh each run → matches a whitelist Repair Recipe → one plan-level confirmation (target, recipe order, each patch, affected models) → candidate verified on an in-memory probe target first (the same contract must pass twice consecutively before commit) → CAS commit of one recipe. Session Model stays unchanged; an explicit “switch to repaired target” is offered on success.
First-version whitelist recipes:
reasoning/thinking → exact-model reasoning=false.fingerprint (+ optional claudeCodeCompat); non-unique → unknown.geminiToolCompat (report-only when already enabled and still failing).Every write re-checks the config version (CAS); a concurrent external edit aborts the repair and preserves external content. Each probe/repair is recorded as a Repair Case (redacted summary in context, detailed redacted evidence out of context).
Typical flow:
After selection, Pi uses the selected provider baseUrl, apiKey, protocol type, and model ID for subsequent requests.
In a terminal (TUI) Pi runs a single-screen SettingsList overlay form (Pi's own settings-list primitive): one row per field, Enter/Space cycles enum values, count/预设/作用域 rows open a SelectList submenu, custom counts accept a 200k / 1M input. In non-interactive modes (RPC / headless / tests) it falls back to the chained select / input / confirm popup in model-meta-dialog.ts. Both paths return the same result shape.
Parameter override · elysiver-claude · model glm-4.6 ✱
scope model glm-g4 ▸ § submenu switch layer
preset select… ▸ § relay-safe / full-reasoning
reasoning inherit true ∘ inline: § true / false / inherit
contextWindow override 200k ▸ § 200k 256k 500k 1M / custom
maxTokens default 64k ▸ § 4k 8k 16k 32k 64k 128k / custom
thinkingFormat override deepseek ∘ inline-cycle enum
— clear this layer ▸
— clear all for provider ▸
save ✱ save (Title shows ✱ when dirty)
cancel
Enter/Space switch or open submenu · Esc back · s save
Each row reads one of four states: override (set in this scope), inherit (a lower user-config layer), built-in (built-in compat profile), default (protocol tier). Count fields offer common presets (200k, 256k, 500k, 1M) plus custom input (k/M suffix). Saving writes providerOverrides keyed by the cc-switch dbId; model-scope edits go under modelOverrides[modelId] (default scope is the preselected model when opened from the picker's o key; the § submenu switches to provider-scope or another model/glob). If that provider is currently active, pi-switch re-registers it immediately.
Default database path:
~/.cc-switch/cc-switch.db
sqlite3 resolution order:
SQLITE3_PATH → ~/.pi/agent/pi-switch.json sqlitePath → sqlite3 from PATH
Windows users should explicitly configure SQLITE3_PATH if sqlite3.exe is not globally available.
Optional configuration file:
~/.pi/agent/pi-switch.json
Example:
{
"sqlitePath": "C:/tools/sqlite3.exe",
"tabs": ["claude", "codex", "gemini", "opencode"],
"vars": {
"codexVersion": "0.144.5",
"claudeCodeVersion": "2.1.190"
},
"debug": false
}
| Field | Description |
|---|---|
sqlitePath | Overrides the sqlite3 executable path (null disables lookup) |
tabs | Preferred provider-type order in the picker |
vars | Optional overrides for UA template versions (otherwise auto-detected) |
providerOverrides | Per-provider label, fingerprint, headers, modelMeta, and per-model modelOverrides (keyed by dbId) |
aliasCcs | Register /ccs alias (default true) |
debug | Enables debug output |
Database path is not in this file — use env CC_SWITCH_DB or the default ~/.cc-switch/cc-switch.db.
providerOverrides)Some gateways reject Anthropic-style fields. A common case is Claude-protocol → GLM relays returning:
Unsupported parameter(s): `reasoning`
Use the popup dialog (/ps-override or picker key o) to set modelMeta.reasoning to false, and optionally set a short label. The dialog is scope-aware: edit 全部模型 (provider level) or pick one model id. Values are persisted under the provider's cc-switch dbId in ~/.pi/agent/pi-switch.json:
{
"providerOverrides": {
"dooongai-1775180253543": {
"label": "elysiver-claude",
"modelMeta": {
"reasoning": false
},
"modelOverrides": {
"glm-4.6": { "reasoning": false, "maxTokens": 8192 },
"gpt-5*": { "reasoning": true }
}
}
}
}
Layering (later wins per field, unset fields never clobber a lower layer):
defaultModelMeta ⊕ providerOverrides[dbId].modelMeta ⊕ providerOverrides[dbId].modelOverrides[modelId]
modelOverrides keys may be exact ids or globs (gpt-5* / *sonnet*). Match order: exact → case-insensitive → most specific glob.
Optional fingerprint field forces a CLI disguise preset regardless of protocol:
| Value | Effect |
|---|---|
claude-code | claude-cli/<ver> (external, cli) + anthropic version/beta |
codex | codex_cli_rs/<ver> (...) + originator + per-process X-Codex-Window-ID |
gemini | GeminiCLI/<ver> + x-goog-api-client |
none | Skip default/api-matched rule injection; only explicit headers (if any) remain |
Explicit headers always win over the preset on conflicts.
Supported modelMeta fields (stored flat in pi-switch.json; registration reshapes into Pi's modern layout):
| Field | Description |
|---|---|
reasoning | Whether Pi may send reasoning/thinking parameters |
thinkingFormat | One of: openai / openrouter / together / deepseek / zai / qwen / chat-template / qwen-chat-template / string-thinking / ant-ling → registered as compat.thinkingFormat |
contextWindow | Context window size (drives Pi compact: contextTokens > contextWindow - reserveTokens) |
maxTokens | Max output tokens |
thinkingLevelMap | Optional map of Pi levels (off / minimal / low / medium / high / xhigh / max) → provider effort strings, or null for unsupported → registered top-level |
supportsDeveloperRole | OpenAI-compatible upstream accepts role: "developer"; set true to preserve it, otherwise pi-switch conservatively rewrites it to system |
requiresReasoningContentOnAssistantMessages | OpenAI-compat: require empty reasoning_content on assistant turns → registered under compat |
useBuiltInCompat | pi-switch only (not sent to Pi): false disables the whole built-in compat profile; unset/true keeps the default (apply when id matches) |
The UI edits the common scalar fields (reasoning / thinkingFormat / contextWindow / maxTokens) plus the 内置compat toggle. Object fields such as thinkingLevelMap are config-only (edit pi-switch.json or call the write APIs). Each form row shows override / inherit / built-in / default (built-in = matched profile and not opted out).
Advanced fields such as supportsDeveloperRole (exact-model tuple / flat meta) are config-only (edit pi-switch.json or call the write APIs).
models.dev covers capability scalars (contextWindow / maxTokens / reasoning) only. Some models also need compat fields such as thinkingFormat to register correctly. pi-switch ships a small in-code profile table for known families:
| Model id match | Built-in fields |
|---|---|
deepseek* | thinkingFormat=deepseek, requiresReasoningContentOnAssistantMessages=true, DeepSeek-style thinkingLevelMap |
qwen* | thinkingFormat=qwen |
Precedence: user override > built-in profile. Profiles never set contextWindow / maxTokens / reasoning (those still flow models.dev → protocol default).
Disable the whole profile: in /ps-override set 内置compat → 关闭, or write:
{
"modelOverrides": {
"deepseek-v4-flash": { "useBuiltInCompat": false }
}
}
Provider-scope modelMeta.useBuiltInCompat: false turns it off for all matching models under that provider; a model-scope true re-enables one id. /ps-doctor and the post-switch notify share the same effective meta; doctor sources look like 用户: …;内置: deepseek* (omitted when disabled).
Compact is executed by Pi itself; pi-switch does not compress sessions. Per-switch registration means the same model id can use different contextWindow / compat under different providers.
Thinking compat for deepseek* is already supplied by the built-in profile. To raise the window / enable reasoning when models.dev misses, override only the capability fields:
{
"providerOverrides": {
"<dbId>": {
"modelOverrides": {
"deepseek-v4-flash": {
"reasoning": true,
"contextWindow": 1000000,
"maxTokens": 384000
}
}
}
}
}
At register time this becomes Pi model config with top-level thinkingLevelMap and nested compat (no top-level thinkingFormat). The [1M] model-id tag only sets contextWindow=1000000; it does not replace the DeepSeek thinking map (that comes from the built-in profile or a user override).
After save, if the provider is currently active, pi-switch re-registers it so the override applies immediately.
The latest selection is stored as piSwitchSelection in Pi settings, so it can be highlighted the next time the switcher opens.
Note: remote model list fetching currently returns model IDs only. Per-model parameters are not imported from
/models; use protocol defaults plus built-in compat profiles plusproviderOverrides.modelMeta/modelOverridesinstead.
Default header rules are stored at:
defaults/headers.json
Optional user override file:
~/.pi/agent/provider-headers.json
pi-switch only merges allowlisted headers to avoid injecting arbitrary sensitive fields into provider configuration. Allowlist:
| Header | Default rules inject? | Notes |
|---|---|---|
User-Agent | yes | Version/os auto-detected; overridable per provider / fingerprint |
anthropic-version | yes (claude) | Protocol-required for Anthropic Messages |
anthropic-beta | yes (claude) | Claude Code beta flags (template via vars.anthropicBeta) |
originator | yes (codex) | Codex CLI private header (template via vars.codexOriginator) |
X-Codex-Window-ID | yes (codex) | Per-process UUID required by official-client relay gates |
x-goog-api-client | yes (gemini) | Gemini CLI client id (gemini-cli/<ver>) |
Authorization / x-api-key / Host / etc. are never injectable via rules or overrides.
Rule precedence: defaults/headers.json < ~/.pi/agent/provider-headers.json < providerOverrides[dbId].headers.
Branch and release-tag protection is documented in .github/branch-protection.md.
Install dependencies:
bun install
Run tests:
bun test
Typecheck:
bun run typecheck
Pre-publish check:
bun run prepublishOnly
Run the isolated TUI smoke (requires pi and sqlite3 on PATH):
bun run smoke:tui
This drives the interactive slash commands through a Pi RPC subprocess under a temporary HOME with a faux OpenAI relay, asserting on state outcomes rather than visual rendering. Real settings.json, pi-switch.json, the cc-switch DB, and its SQLite sidecars are snapshotted and verified unchanged even when a flow fails. It covers the five main flows:
/ps-override — provider-scope modelMeta write round-trip./ps-config — 3-level pick, provider registration, and selection persistence./ps-info — effective-config summary./ps-doctor — diagnostics (offline models.dev/routing items degrade to warn, not fail)./ps — quick switch off a pinned/recent entry.Use --flow=<name> to run one flow, or KEEP_SMOKE_TEMP=1 to retain the temp HOME for inspection.
Run the isolated end-to-end /ps-repair smoke (requires pi and sqlite3 on PATH):
bun run smoke:probe-repair
This starts a local faux OpenAI relay and a Pi RPC subprocess under a temporary HOME. It runs 3 repair scenarios against the same faux target:
reasoning-false — The faux target passes basic/tool requests but rejects reasoning, matching reasoning-false. Writes modelOverrides[model].reasoning=false.client-fingerprint — Sets fingerprint="codex"; the relay validates real originator: codex_cli_rs headers and User-Agent.gemini-tool-compat — Sets geminiToolCompat=true; the relay validates Gemini-style payload (toolConfig.functionCallingConfig.mode=AUTO, parameters instead of parametersJsonSchema).Each scenario: verifies the candidate twice, declines the post-repair Session Model switch, asserts real Pi settings/config and cc-switch DB state remain unchanged, and deletes temporary state after success. Use --recipe=<id> to run a single scenario, or --keep / KEEP_SMOKE_TEMP=1 to retain temp files.
Publishing is modeled after a release-gate flow (similar to vibe-designing-playbook):
tree / version / test / pack / tag)vX.Y.Z tag after gates passOne-time setup on GitHub:
NPM_TOKEN, value: the tokenRelease steps:
# 1) bump version in package.json (keep semver)
# 2) commit all release changes
bun run release # dry-run gates (no tag)
bun run release:apply # create tag vX.Y.Z after gates pass
git push origin main
git push origin v0.1.0 # triggers Actions publish
Manual re-publish is also available from Actions → CI → Run workflow with publish=true (the matching vX.Y.Z tag must already point at that commit).
The workflow:
v* tags (or manual dispatch)package.json versionnpm publish --access public --provenancepi-switch parses provider configuration from the cc-switch providers table and normalizes it into Pi-registerable providers where possible.
If a provider protocol cannot be mapped to a Pi-supported API type, it is shown as non-switchable in the UI instead of being force-registered.
/models responses (IDs only).FAQs
UNMAINTAINED (final release 0.3.6): use CC Switch v3.20.0+ for native Pi support. Switch providers/models from local cc-switch, mirror them into models.json for subagents, and set per-subagent models.
The npm package pi-ccs receives a total of 97 weekly downloads. As such, pi-ccs popularity was classified as not popular.
We found that pi-ccs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.

Research
/Security News
Socket uncovered two malicious VS Code themes in a GlassWorm-linked cluster with thousands of installs across VS Code Marketplace and Open VSX.

Security News
/Company News
Capital One is partnering with Socket to proactively secure its open source supply chain.