Providers as data; add Ollama and Custom endpoints

Every gateway provider is now an entry in providers.json - endpoint and auth
template, how its model list is read, how it is probed before a switch, what
setup asks, which doctor checks apply, and its title, colour and logo -
installed next to the presets and read by all three consumers: the bash CLI
(through cm-json.py), the Windows script, and the bar widget (through
health.json). anthropic stays built in; it is the native login, not a gateway.

Behaviour that differs in kind stays in code, chosen by name from the entry:
catalogue parsers (openrouter, lmstudio, ollama, openai, static), probe rules
(always, lenient, local), and named doctor checks. A provider that reuses them
is an entry and a default preset, with no code. The widget draws providers
from health.json, so a new one needs no QML change and no shell restart.

The existing three are unchanged in behaviour: their blank presets come out
byte-identical from the file, and setup, doctor, models and the picker run the
same checks through the generic paths.

Ollama: local server on :11434, placeholder token, one model for every tier,
models from /api/tags. doctor reads the context each loaded model actually runs
with (/api/ps) and its maximum (/api/show), because Ollama defaults to 4096
tokens unless OLLAMA_CONTEXT_LENGTH is set and silently truncates past it. A
bare model name matches its :latest tag.

Custom: any Anthropic-compatible endpoint. Ships with no address and is refused
until it has one; key optional; models from /v1/models when the endpoint has a
list, and a lenient probe so a proxy without one is not blocked.

Also: preflight (and Set-ClaudeMode on Windows) refuses a preset with no
server address; the server form, setup and set-auth use each provider's own
default URL, key name and placeholder token instead of LM Studio's; the
Windows build gains the no-models and no-address guards it never had.
Tested on Linux against fake Ollama/Custom servers, and on Windows 5.1 in a
USERPROFILE sandbox on winbox.

1.12.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
smoido
2026-09-15 01:20:33 +03:00
co-authored by Claude Opus 5
parent a54f7bcb55
commit b5824c6611
15 changed files with 1482 additions and 480 deletions
+104 -4
View File
@@ -1,7 +1,8 @@
# claude-mode
Switch Claude Code system-wide between **Anthropic**, **OpenRouter**, **Z.AI**, and a
local **LM Studio** server — with named per-tier model presets.
Switch Claude Code system-wide between **Anthropic**, **OpenRouter**, **Z.AI**, a
local **LM Studio** or **Ollama** server, and any **custom** Anthropic-compatible
endpoint — with named per-tier model presets.
One write to `~/.claude/settings.json` covers the CLI, the VS Code extension, and
the desktop app. Restart Claude Code afterwards — nothing else. Windows
@@ -14,12 +15,16 @@ claude-mode anthropic # subscription login
claude-mode openrouter # remote gateway (preset: default)
claude-mode zai # Z.AI GLM coding plan (preset: zai)
claude-mode lmstudio # local server (preset: lmstudio)
claude-mode ollama # local server (preset: ollama)
claude-mode custom # your own endpoint (preset: custom)
```
**Contents:** [Install](#install) · [First run](#first-run) · [The interactive
menu](#the-interactive-menu) · [Commands](#commands) · [Presets](#presets-shipped) ·
[Context windows](#context-windows-and-early-auto-compaction) · [Z.AI](#zai-mode) ·
[LM Studio](#lm-studio-mode) · [Live sessions](#live-sessions) ·
[LM Studio](#lm-studio-mode) · [Ollama](#ollama-mode) · [Custom
endpoints](#custom-endpoints) · [Adding a provider](#adding-a-provider) ·
[Live sessions](#live-sessions) ·
[Troubleshooting](#troubleshooting) · [Design decisions](#design-decisions) ·
[Layout](#layout) · [Linux / Omarchy](#linux--omarchy) · [The bar
widget](#the-omarchy-bar-widget) · [Uninstall](#uninstall)
@@ -177,7 +182,9 @@ claude-mode status active mode, preset, model map
claude-mode anthropic native login (deletes all managed keys)
claude-mode openrouter [preset] default preset: default
claude-mode zai [preset] default preset: zai (alias: z.ai, z-ai)
claude-mode lmstudio [preset] default preset: lmstudio
claude-mode lmstudio [preset] default preset: lmstudio (alias: lm-studio)
claude-mode ollama [preset] default preset: ollama
claude-mode custom [preset] default preset: custom
claude-mode presets list presets (* = active)
claude-mode preset show <name>
@@ -401,6 +408,98 @@ like every other credential — a real key on a public address is a real key.
Nothing here changes the shipped presets unless you ask it to; the local default
stays exactly as it was.
## Ollama mode
Ollama serves Anthropic's Messages API itself, at `/v1/messages`, so nothing sits
in between. `claude-mode setup ollama` asks where the server is (it ships on
`http://127.0.0.1:11434`), whether it needs a key, and picks one model for every
tier from what the server has pulled. One model for all four is the right shape
for a local server: it holds one in memory at a time, and mapping tiers to
different models just means paying the load cost on every tier change. The token
is a placeholder, `ollama`, which Ollama requires but does not check.
### The context window is set on the server
This is the part that bites. Ollama sizes the context on the server, not per
request: **4096 tokens** unless `ollama serve` runs with `OLLAMA_CONTEXT_LENGTH`,
and anything past it is cut off without an error. Claude Code's system prompt
alone is most of that. Ollama recommends 64k or more for Claude Code, and the
shipped preset declares 65,536 — but `contextTokens` only tells Claude Code what
to expect. It cannot change what the server does.
```bash
OLLAMA_CONTEXT_LENGTH=65536 ollama serve
# or, for the systemd service: systemctl edit ollama
# [Service]
# Environment=OLLAMA_CONTEXT_LENGTH=65536
```
`claude-mode doctor` reads what each loaded model is actually running with
(`/api/ps`) and the model's own maximum (`/api/show`), and says so when either is
below the preset. When nothing is loaded it cannot see the server's setting, and
says that instead of guessing.
Also worth knowing:
- A bare name like `qwen3-coder` is `qwen3-coder:latest` to Ollama; both work.
- Ollama does not implement `count_tokens` or prompt caching, so long sessions
redo more work than they would on a hosted provider. Nothing breaks.
- To reach it from another machine, serve it with `OLLAMA_HOST=0.0.0.0` and
point the preset there: `claude-mode preset url ollama http://192.168.1.40:11434`.
## Custom endpoints
For anything else that speaks Anthropic's Messages API: a LiteLLM or Vercel
gateway, vLLM, llama.cpp's server, a company proxy. The `custom` preset ships
with no address — there is no sensible one to guess — and a switch to it is
refused until it has one.
```bash
claude-mode setup custom # asks for all of the below
# or by hand:
claude-mode preset url custom https://llm.example.com
claude-mode preset auth custom key custom # the key lives in the vault as 'custom'
claude-mode set-key custom
claude-mode preset set custom opus <model-id> # ...and each other tier
```
Several endpoints are several presets, each with its own address and key name:
`claude-mode preset new work --provider custom --blank`, then
`claude-mode preset auth work key work`.
The model list comes from `/v1/models` when the endpoint has one (OpenAI's shape
or Anthropic's). Plenty of proxies serve Messages and nothing else. That is fine:
the check before a switch only wants something to answer, and model ids can be
typed by hand. The cost guard still applies. A custom endpoint in front of
Anthropic's own models needs `"allowAnthropicModels": true` in the preset, which
keeps it deliberate.
## Adding a provider
Every gateway provider is an entry in [`providers.json`](providers.json), installed
next to the presets and read by the CLI, the bar widget (through `health.json`)
and the Windows build. Anthropic is not in it: it is the native login, not a
gateway. An entry holds:
| field | what it says |
|---|---|
| `id`, `aliases`, `title`, `label`, `blurb`, `color` | names and how the CLI, menu and panel show it |
| `defaultPreset`, `preset` | the preset `claude-mode <id>` picks, and the template for a blank one (URL, auth, context, extra env) |
| `server` | whether the address is editable, how it is probed before a switch (`always`, `lenient` for proxies, `local` only when it is on this machine), setup hint |
| `catalogue.kind` | how its model list is read: `openrouter`, `lmstudio`, `ollama`, `openai` (`/v1/models`) or `static` (a list kept in the entry) |
| `setup` | whether a key is required or optional, where to get one, and one model for every tier or one per tier |
| `doctor` | which checks apply: `catalogue-models`, `openrouter-key`, `guardrail`, `message-check`, `ollama-context`, `lmstudio-templates` (Windows) |
| `logo`, `logoScale` | a single-path 24×24 SVG mark, and its optical size correction |
A provider that reuses those kinds is an entry plus a default preset in
`presets/`, and no code. A hosted Anthropic-compatible coding plan — Kimi,
MiniMax, DeepSeek — is an entry shaped like `zai`, with its own URL and a
`static` or `openai` catalogue. Only a genuinely new kind of behaviour, such as
a catalogue format none of the parsers read, needs code: a parser in
`cm-json.py`, and a branch in `provider_catalogue` (bash) and
`Get-ProviderCatalogue` (Windows). The bar widget picks new providers up from
`health.json` — no QML change, and no shell restart.
## Live sessions
### Why a switch breaks a running session
@@ -719,6 +818,7 @@ Windows:
```
~/.claude-mode/
claude-mode.ps1 main script
providers.json what each gateway provider is (replaced on every install)
state.json mode, active preset, and the exact env keys last written
presets/*.json provider + model maps
vault/*.cred DPAPI-encrypted keys (openrouter, zai, ...)