Switch model and effort
Claude Code 2.x supports live switching without losing the conversation:
/model opus
/model sonnet
/model haiku
/model <any model ID from cc-dialect models cc-codex>
/effort low
/effort high
/effort xhigh
/effort max
Each dialect maps opus, sonnet, and haiku to its own three configured model IDs. The codex preset maps them to Astra, Sol, and Luna; codex-sol maps opus and sonnet to Sol and haiku to Luna. Use --opus-model, --sonnet-model, and --haiku-model to change that mapping.
On a Copilot dialect, the picker's dashed Claude IDs (claude-opus-5-5, claude-fable-5-1) are registered as aliases of Copilot's dotted names (claude-opus-5.5). The alias is skipped when the dashed ID is already a catalog model. Other bridges are unchanged.
Interactive choices are stored in the dialect's own configuration directory — changing cc-codex does not change regular claude, cc-kimi, or another dialect.
List live models from an authenticated instance:
cc-dialect models cc-codex
Presets and custom dialects
List presets included in your installed version:
cc-dialect presets
Override parameters while creating or updating a dialect:
cc-dialect create cc-my-codex \
--preset codex \
--model gpt-6-astra \
--opus-model gpt-6-astra \
--sonnet-model gpt-6-sol \
--haiku-model gpt-6-luna \
--effort-level auto \
--concurrency 3 \
--context-window 272000 \
--effort=true \
--tool-search=false \
--port 53170
--subagent-model forces every subagent onto one model through CLAUDE_CODE_SUBAGENT_MODEL. Pass --subagent-model none to leave it unset, including a value inherited from the shell, so each agent uses its own haiku, sonnet, opus, or inherit choice through the dialect's tier mapping, as plain Claude Code does. Set all three tiers explicitly when you do; a tier left at auto on a Copilot preset lets Copilot pick any model. none is not a model: it stays out of the proxy catalog and does not drop a preset's context window. A create command printed by cc-dialect doctor restates --subagent-model none.
For an Anthropic-compatible service, route the upstream through the dialect's isolated embedded proxy:
export MY_PROVIDER_TOKEN="..."
cc-dialect create cc-my-model \
--model my-model-id \
--base-url https://provider.example.com/api/anthropic \
--token-env MY_PROVIDER_TOKEN
cc-dialect shim install cc-my-model
Updating cc-dialect does not silently change existing dialects. Re-run cc-dialect create cc-kimi --preset kimi to apply the latest preset while preserving port, credentials, and history. Start a new conversation after changing the underlying model.
A shell alias using the generated command name takes precedence over the installed executable. Zsh and Bash are both checked, starting with your $SHELL. Both cc-dialect shim install and cc-dialect doctor detect these collisions and name the shell that defines the alias, along with that shell's startup file (~/.zshrc for Zsh, ~/.bashrc for Bash) as the place to start looking — the definition may live in a file that startup file sources, in a framework such as oh-my-zsh, or in system-wide configuration, which a shell does not report. Once removed, run unalias <name> in terminals that were already open. The same applies to existing executables: cc-dialect create recommends an unambiguous alternative, and shim installation refuses an ambiguous name and lists the conflicting paths.
Context window and auto-compaction
Claude Code compacts a conversation before it outgrows the model's context window, but it cannot recognize the provider model IDs a dialect routes to, such as gpt-6-sol or composer-2.5. Without a declared capacity a conversation can reach the provider's real limit before compacting, after which further requests fail.
Every dialect therefore carries a context window in tokens, exported for that Claude Code process as both CLAUDE_CODE_AUTO_COMPACT_WINDOW and CLAUDE_CODE_MAX_CONTEXT_TOKENS, with the same value. Claude Code reads the two through separate chains: the first caps when it compacts, while the second is the window it resolves for the model — the denominator it reports against (the ctx N% in the statusline and its own /context view) and, because Claude Code compacts against the smaller of the two, a second cap on when it compacts. It applies only to model IDs Claude Code cannot recognize, so first-party claude-… IDs keep resolving through its own registry. Claude Dialects supplies only the capacity — it never summarizes, truncates, or deletes conversation turns. Claude Code owns compaction and every readout, and applies its own threshold inside the declared capacity, so the value is raw model capacity rather than a pre-discounted trigger point.
Declaring only the first leaves the two indicators counting the same tokens over different denominators: a cc-glm session showed ctx 47% beside 4% until auto-compact and compacted moments later, because the percentage divided by Claude Code's 200,000-token default instead of the 131,072 that dialect declared at the time. Dialects with a window above 200,000 had the opposite fault, sitting at 100% for most of a long session. The two readings still will not be identical — Claude Code holds back an output allowance and a compaction buffer, so the countdown measures against a usable ceiling roughly 33,000 tokens below the window, an offset that exists against first-party models too. And because CLAUDE_CODE_MAX_CONTEXT_TOKENS is skipped for claude-… IDs, that exclusion follows the model in use rather than the dialect: claude and copilot-claude map every tier to a Claude model and always report against Claude Code's own registry value, while mixed-frontier does so only on its main and subagent model (claude-fable-5-1) and reports against the declared window once /model opus, sonnet, or haiku selects GPT-6 Sol, Kimi K3, or Grok 4.7. Compaction can only come out tighter than the declared window, never looser, since the auto-compact variable has no such exclusion and Claude Code takes the smaller of the two — but where the registry resolves below the declared window, these conversations compact earlier than that window implies and report against the registry value.
A preset that maps different models to opus, sonnet, haiku, or subagents uses the smallest window across every model it can select, because Claude Code receives one window per process. That keeps model switching and subagents safe.
| Preset | Context window | Smallest supported model |
|---|---|---|
gemini | 1,048,576 | Gemini Pro Agent, 3.8 Flash High, and 3.5 Flash Lite |
deepseek | 1,000,000 | DeepSeek V4 Pro and deepseek-flash |
glm | 1,000,000 | GLM-5.3 / GLM-5.3-Flash |
minimax | 1,000,000 | MiniMax-M3 |
copilot-gemini | 1,000,000 | Gemini 3.8 Flash on the Copilot catalog |
grok | 500,000 | Grok 4.7 |
codex-sol, codex | 272,000 | GPT-6 Astra, Sol, and Luna |
mixed-frontier | 272,000 | GPT-6 Sol |
kimi | 262,144 | Kimi K2.7 Code Highspeed and K2.6 |
copilot-mai | 256,000 | MAI-Code-1.1-Flash |
claude | 200,000 | Claude Haiku 4.5 (Fable 5.1 and Sonnet 5 are larger) |
cursor-composer, cursor-composer-fast | 200,000 | Cursor Composer 2.5 route |
cursor-grok | 200,000 | Cursor Grok 4.7 route |
cursor-mix | 200,000 | Cursor Composer/Grok/Kimi mixed route |
copilot-codex | 200,000 | conservative fallback; Copilot publishes no window for GPT-6 Sol |
copilot-claude | 200,000 | Claude Haiku 4.5 (Sonnet 5 is larger) |
cursor-auto, copilot-auto | 128,000 | any model the route may select |
OAuth-backed values come from the embedded CLIProxyAPI model registry. Cursor and GitHub publish no per-route context window, so those presets use a conservative fallback rather than borrowing a number from a same-named direct provider model, and the auto routes are set low enough to cover whatever the vendor selects.
A custom model ID has no reviewed value, and Claude Dialects never guesses one from a model name — a window larger than the route really supports is worse than none. Declare it yourself:
cc-dialect create cc-my-model \
--model vendor-model-id \
--context-window 262144 \
--base-url https://provider.example.com/api/anthropic \
--token-env MY_PROVIDER_TOKEN
The value must be a positive integer of at most 20,000,000 tokens. A capacity describes one specific set of models, so an existing stored value is kept only while that set is unchanged: updating a port, concurrency, or effort keeps it, while changing the primary model, a tier, or the upstream discards it. --subagent-model none adds no model, so it keeps the window when the preset's subagent model is already one of the other models on the route. The fully resolved route then adopts a built-in preset's reviewed window when every model, tier, and upstream field matches exactly; otherwise create reports it as uncalibrated unless you pass a new --context-window. Carrying the old number across a model change would be the more damaging default — a window larger than the new route supports is exactly what lets a conversation run past the provider's real limit, while an unset one only returns to uncalibrated behavior and says so. A stored value also survives naming the same preset again: that asks for the preset's models, not for its window to be raised. An explicit --context-window always wins.
Dialects created before this field existed are migrated on first read through the same exact-route rule. The stored preset label supplies the window while it still describes that route. If the label has diverged but the complete route is field-for-field identical to another preset, the dialect adopts that route's reviewed window while keeping its original label; a route that matches no preset exactly remains unset and is reported by cc-dialect doctor. The preset key is itself younger than some dialects, so one that records no label still qualifies for an exact match and the matched name is written alongside its window. An unrecognized label may belong to a newer build, so it receives an exact route's window without being renamed.
This route fallback calibrates only the window; it does not choose the preset a remedy command may restate. A recognized but diverged label still produces a command based on that stored preset plus the route overrides, while an unrecognized label is not rewritten through today's match. Matching is equality rather than resemblance — a single hand-swapped tier that completes no preset route stays uncalibrated, because a guessed window may be larger than that tier's model supports. The migrated value takes effect immediately at launch, and cc-dialect doctor --fix records it in config.json. Presets are compiled into the executable, so a revised preset window arrives with cc-dialect upgrade and applies to newly created dialects. An already recorded value is never raised on your behalf — nothing distinguishes a window you measured from one a preset supplied, and silently increasing it would delay compaction past a limit you set deliberately — so adopt a revised preset window explicitly with cc-dialect create <name> --preset <preset> --context-window <tokens> when you want it.
The same applies to preset revisions that change models: a dialect stores the models it was created with, so it keeps them after the preset moves on. create stamps the exact preset revision behind the dialect (presetFingerprint in config.json), and cc-dialect doctor compares that stamp against today's preset to report the drift — a dialect untouched since creation is reported with every field that changed and the create command that adopts the current preset, restating a window the revision moved with --context-window, because a same-route create keeps the stored window otherwise; one that cannot be told apart from a hand edit is reported as possibly either; and a dialect that is field-for-field identical to some current preset is never reported as drift, whatever its label says — doctor notes only that its label names another preset. A dialect whose route matches another preset exactly but whose window is behind that preset is reported as drift against the preset it actually runs. doctor --fix never rewrites a model or window; adoption is always the printed command, run by you.
cc-dialect doctor reports dialects with a missing or invalid context window, preset drift — a dialect still running the models or window of a preset revision it was created from, or a window behind the preset its route exactly matches, after cc-dialect upgrade shipped a newer one — the fill level of the most recent request per dialect, and a Claude Code build that no longer references either capacity variable — one line per variable, because dropping CLAUDE_CODE_AUTO_COMPACT_WINDOW delays compaction, while dropping CLAUDE_CODE_MAX_CONTEXT_TOKENS falls back to Claude Code's 200,000-token default, which skews the reported percentage and compacts any larger declared window early. Adding --fix records migrated windows, together with the preset name an exact route match resolved for a dialect that stored none. Existing labels are preserved, a custom route or any route that matches no preset exactly stays reported until you set a window yourself, and drift is never applied by --fix. Each dialect's embedded proxy records the latest request's input usage to instances/<name>/context.json and warns once per 80%, 90%, and 95% threshold. Only counters are recorded — never prompts, tool results, request bodies, or credentials.
Claude Code takes a single auto-compact window for the whole process, so a dialect's window covers every model it can select — which is why a preset uses the smallest one and the dynamic auto routes sit at a conservative floor. Selecting a model outside that set is not calibrated for: cc-dialect run <name> --model <other> warns when the model is not one the dialect configures, and /model <arbitrary-id> mid-session cannot be re-calibrated at all. For a model you use regularly, give it its own dialect.
Detect configured dialects
Human-readable detection:
cc-dialect detect
cc-dialect detect codex
cc-dialect detect glm --running
JSON output for tooling:
cc-dialect detect --running --json
cc-dialect detect kimi --running --json
Silent exit-status check:
if cc-dialect detect glm --running --quiet; then
echo "A GLM Claude Code dialect is running"
fi
Exit status 0 means at least one matching dialect was found; 1 means none matched.
Web dashboard
cc-dialect web
cc-dialect web --no-browser
cc-dialect web --listen 127.0.0.1:8765
cc-dialect web --listen '[::1]:8765'
Without flags, the command binds an available ephemeral port on 127.0.0.1, prints the URL, opens the default browser, and remains in the foreground until Ctrl-C. --no-browser suppresses only browser launch. The browser is opened with open on macOS and xdg-open on Linux; headless Linux hosts often lack xdg-open, so install xdg-utils or use --no-browser and open the printed URL yourself. --listen accepts a numeric loopback IP and port; localhost, wildcard and LAN addresses, remote access, and reverse proxies are unsupported.
The dashboard shows safe views of presets, preset-backed and custom dialects, runtime health, tracked native launchers, and Cursor readiness. Dialect cards and detail pages surface read-only preset-drift badges — confirmed drift, uncertain drift, and the differing fields — using the same resolution as cc-dialect doctor; adoption stays a CLI cc-dialect create command. It can create or update dialects — including their context window — start/stop/restart runtimes, manage tracked native launchers, and install or refresh the pinned Cursor runtime. A successful Cursor runtime update stops running Cursor dialects so stale bridges cannot continue; start them again explicitly.
Dialect edits validate first, stop the old runtime, save the replacement, and leave it stopped. Private local API keys, isolated auth and Claude Code state, history, shims, and stable ports are preserved where applicable. Edits replace only public dashboard fields, so use the CLI for custom configurations that rely on unexposed extra environment values, bridge/auth fields, or URL userinfo, query strings, or fragments.
OAuth login, Copilot install/login, live model listing, proxy logs, shim management, doctor diagnostics, remote access, and launching Claude Code sessions are not dashboard features.
Deleting a dialect or native launcher requires an exact typed-name confirmation. API responses omit credential contents and token values, expose only safe metadata, and sanitize upstream URLs.
Proxy lifecycle
Every proxied dialect has an independent lifecycle:
cc-dialect proxy cc-codex start
Start the embedded proxy
cc-dialect proxy cc-codex status
Check proxy and managed bridge state; a bridge that died is reported as crashed, not stopped
cc-dialect proxy cc-codex logs
View proxy logs
cc-dialect proxy cc-codex stop
Stop the proxy
The proxy starts automatically when its generated command runs. OAuth credentials are scoped per dialect:
cc-dialect auth cc-codex codex
cc-dialect auth cc-kimi kimi
cc-dialect auth cc-claude claude
Supported embedded OAuth providers: codex, claude, kimi, antigravity, and xai.
Native Claude shortcuts
Install a lightweight shortcut for the normal Claude Code application without starting the proxy or changing its configuration:
cc-dialect native install cld --dangerous
cld
This launches Claude Code with --dangerously-skip-permissions using the regular ~/.claude settings, authentication, and history — a shortcut for your existing Claude Max setup, not an isolated dialect. Use dangerous mode only in directories you trust.
Installed launchers are tracked with their canonical path and content digest. A tracked launcher cannot be moved in place; remove and reinstall it to choose another path. Missing or externally modified files cannot be updated or removed. Older launchers are not discovered automatically and can be adopted only when their bytes exactly match what the current executable would generate for the current claude path and dangerous-mode setting.
Security
Proxy servers and SDK bridges bind only to 127.0.0.1. The dashboard accepts numeric loopback listeners only and requires the exact bound Host on every request; mutations also require the exact Origin and a per-process CSRF token. It is designed for one trusted local user, not remote or reverse-proxy exposure.
- Per-dialect
CLAUDE_CONFIG_DIRisolation, with an opt-in--claude-config-dir inherit - Per-dialect file I/O performed by the CLI itself confined to that dialect's own directory by the operating system, so a planted symlink cannot redirect a read, write or removal outside the tree or into another dialect — with four stated limits: removal unlinks a tampered directory rather than refusing; credentials written by the embedded proxy during OAuth are verified by file identity after the write rather than confined during it; launched processes (Claude Code, the Cursor and Copilot bridges) receive absolute paths and resolve them as ordinary programs do; and the embedded proxy's long-lived config watcher re-reads
proxy.yamlby path after the initial rooted read - Ownership of a running proxy or bridge decided by the PID record under the dialect's own directory rather than by the health check, so
cc-dialect startrefuses a runtime already serving the dialect's port that the dialect holds no live record of owning, instead of adopting a process a laterstoporremovecould not find - Secret-safe dashboard API responses omit keys, tokens, credential contents, and extra environment values
- Checked port allocation starting at
:43170 - Cursor/Copilot tools forwarded with
cc_tool_schema aliases - Claude Code permission prompts remain in control
CURSOR_API_KEYand tokens never copied intoconfig.json
The dialect's private claude/ directory is supplied via CLAUDE_CONFIG_DIR. Project-level .claude/ files in the repository you are working in continue to work normally.
--claude-config-dir isolated|inherit chooses that directory per dialect. isolated is the default. inherit passes an exported CLAUDE_CONFIG_DIR through for that launch, and stays isolated when nothing is exported:
cc-dialect create cc-copilot --preset copilot-auto --claude-config-dir inherit
CLAUDE_CONFIG_DIR=~/.claude cc-copilot
cc-copilot
Omitting the flag on a later create for the same name keeps the stored mode. A create command printed by cc-dialect doctor restates --claude-config-dir inherit. The generated statusline and attribution opt-out stay in the dialect's own claude/ directory.
In inherit mode, /model and /effort changes are stored in the shared directory and then affect plain claude as well.
File layout
State lives under the platform's user configuration directory — ~/Library/Application Support/claude-dialects on macOS, ${XDG_CONFIG_HOME:-~/.config}/claude-dialects on Linux — or under DIALECT_HOME when that is set. The CLI opens instances/<dialect>/ as a root and resolves every path relative to it, so a symlink inside cannot reach outside the tree or into a sibling dialect. Both instances/ and each dialect directory must be real directories, and an operation is refused before anything is written if either has been replaced by a symlink. Removal is the deliberate exception: cc-dialect remove unlinks a symlinked dialect directory and commits the configuration change, so a tampered dialect can still be cleaned up, and the link's target is left untouched. Dialect names remain restricted to [a-z0-9_-].
config.json # dialects + tracked native-launcher registry
.state.lock # owner-only cross-process mutation lock
instances/
cc-codex/
auth/
claude/
statusline.sh
proxy.yaml
proxy.pid
proxy.log
proxy.version
context.json
cc-kimi/
auth/
claude/
statusline.sh
proxy.yaml
proxy.pid
proxy.log
proxy.version
context.json
cc-cursor/
claude/
statusline.sh
cursor-workspace/
cursor-bridge.pid
cursor-bridge.log
cursor-bridge.version
proxy.yaml
proxy.pid
proxy.log
proxy.version
context.json
cc-copilot-mai/
claude/
statusline.sh
copilot-home/
copilot-bridge.pid
copilot-bridge.log
copilot-bridge.version
proxy.yaml
proxy.pid
proxy.log
proxy.version
context.json
cursor-runtime/
cursor_bridge.mjs
node_modules/@cursor/sdk/
copilot-runtime/
copilot_bridge.mjs
node_modules/@github/copilot-sdk/
node_modules/@github/copilot/
node_modules/.bin/copilot
defaults/
mcp.json # shared MCP server defaults (owner-only)
Unless a run already passes its own --mcp-config, every cc-dialect run launches Claude Code with --mcp-config defaults/mcp.json, which merges these servers into each dialect's own set — keep shared and dialect-defined names disjoint. mcp import copies a dialect's user-scoped servers (claude mcp add --scope user) and never overwrites a shared one without --force. The file is owner-only (0600) because env may carry tokens; a malformed file is skipped on launch rather than breaking every dialect.
Useful commands
cc-dialect list
List configured dialects
cc-dialect show cc-codex
Show dialect details
cc-dialect web
Open the local dashboard
cc-dialect doctor [--fix]
Diagnose configuration issues, crashed bridges, and recent Codex upstream/cooldown failures; apply deterministic repairs
cc-dialect upgrade
Fetch the latest source, rebuild, replace the installed binary, and reconcile runtimes
cc-dialect remove cc-codex
Remove a dialect permanently
cc-dialect mcp import cc-codex
Promote MCP servers to shared defaults (survive remove)
cc-dialect mcp list
List shared MCP servers (env values are never printed)
cc-dialect mcp path
Print the shared defaults file location for editing
cc-dialect presets
List available presets
cc-dialect --version
Show version
For an authenticated Codex dialect, doctor inspects recent auth/logs/error-v1-messages-*.log and proxy.log entries. It distinguishes a real upstream HTTP 502 or 503 from a fast retry during CLIProxyAPI's short credential cooldown, so auth_unavailable is not misreported as missing OAuth. When the failing request model is available, the diagnostic points to a different configured model tier, along with a manual proxy restart and the full proxy logs; --fix remains limited to deterministic repairs and does not attempt to repair an overloaded upstream.
To fully uninstall after removing all dialects:
rm ~/.local/bin/cc-dialect
rm -rf "$HOME/Library/Application Support/claude-dialects" # macOS
rm -rf "${XDG_CONFIG_HOME:-$HOME/.config}/claude-dialects" # Linux
Build local assets
The compiled dashboard in internal/app/dashboard/dist/ is committed and embedded into the one Go binary. Node.js is not required for dashboard runtime or a normal make build/make install; contributors rebuilding the frontend need npm with Node.js 22.22.2+ or 24.15.0+ and must commit the refreshed dist/.
Create a shareable archive for the host platform instead of installing directly:
make assets VERSION=dev
ls artifacts/
(cd artifacts && shasum -a 256 -c SHA256SUMS) # macOS
(cd artifacts && sha256sum -c SHA256SUMS) # Linux
The archive is named for the platform it was built on: artifacts/cc-dialect_dev_darwin_arm64.zip on an Apple silicon Mac, artifacts/cc-dialect_dev_linux_amd64.tar.gz on x86-64 Linux, alongside SHA256SUMS. Override GOOS and GOARCH to cross-compile. Packaging a .tar.gz works from either host; packaging a macOS .zip uses ditto when available and falls back to zip, so building a darwin archive from Linux needs zip installed. These locally produced assets carry no code signature; on macOS they are neither signed nor notarized.