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>
The gear on every preset row now opens an editor: the four tiers with the
model each maps to, and for each one field that filters the provider's
catalogue as you type (every word must match; arrows and Enter work), listed
inline with context length and price, and that also takes any id typed by
hand. LM Studio's server form moves one click inside the editor.
The panel card moves from PopupCard to KeyboardPanel. PopupCard is an
xdg-popup, which only receives keys after focus is routed through its parent
surface, so no text field in it could ever be typed into - the existing
server URL form included. KeyboardPanel primes layer-shell keyboard focus on
open, which is why every shell panel with a text field uses it. Esc now
closes the panel.
The picker reads ~/.claude-mode/models-cache.json, which or_catalogue and
lms_catalogue now write as a side effect, so models, doctor, setup and the
menu's picker all keep it fresh and the panel never hits the network itself.
One node per provider with its own fetchedAt/ok; a failed fetch keeps the
old list, an LM Studio list is tied to its server, and no key or key name is
ever stored. `models` gains --preset, --refresh and --json, and the Z.AI list
now lives in one place.
A tier edit to the active preset re-applies without the running-sessions
prompt: that prompt guards against the endpoint or key moving, and a tier
edit moves neither (preset url/auth still ask). A failed re-apply now says
the edit was saved. `preset set` on an unknown name no longer creates it.
Panel edits run through one chain that stops on the first failure and
refreshes health.json at the end.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A broken session that will never be repaired kept the bar's warning dot lit
forever. repair-session now takes --ignore/--unignore/--unignore-all/--ignored,
recorded in ~/.claude-mode/ignored-sessions.json, and the scan hides broken
transcripts older than --max-age days (default 7, 0 disables,
CM_IGNORE_AGE_DAYS sets it). Hidden sessions move to a separate ignored[]
list with a reason, and --all always names how many it held back.
A repair clears the session's dismissal, and entries whose transcript is gone
are pruned, so a session that breaks again is never silently hidden.
The panel gets an Ignore button beside Repair and a collapsed "hidden (N)"
section with Restore. The dot still counts broken sessions only.
Bumps to 1.10.0 and fixes the widget manifest, which still said 1.8.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing tells you a session is unresumable until you try to resume it, and by
then you have usually forgotten which one it was. The widget now scans every
project on a timer and whenever the panel opens, marks its icon when something
needs fixing, and lists the affected sessions with a repair button that says
what it will drop and what it will keep before doing anything.
The scan had to get roughly eighty times cheaper first. Classifying a transcript
needs two facts - what its last message id is, and whether any Anthropic id
exists at all - and the first settles the common case alone. Reading the tail of
each file and only opening the whole thing when the tail already looks wrong
takes the sweep from ~5s to ~60ms across 59 transcripts, which is the difference
between something that can sit on a timer and something that cannot. `--all`
uses the same path, and `--json` exposes it.
The badge is a dot beside the mark rather than a recolouring of it: this widget's
job is to report which provider is active, and tinting it red to mean something
else entirely would be a lie about that.
Verified by planting a genuinely corrupted transcript, watching the scan find it
and the dot appear, then removing it and watching both clear.
A Quickshell plugin for the Omarchy shell (v4's bar is Quickshell, not Waybar).
The icon is the mode; clicking it opens a panel that switches.
State comes from watching health.json rather than polling the CLI, so the widget
costs nothing while idle and a switch made in a terminal shows up in the bar on
its own. state.json is the fallback, because it exists from install onward where
health.json only appears once claude-mode has run.
The marks are the providers' real logos, drawn as vector paths through
QtQuick.Shapes rather than set as font glyphs - three of the four have no Nerd
Font pictograph at all, and paths take the bar's foreground colour and follow
the theme. Claude, OpenRouter and LM Studio from simple-icons, Z.AI from
lobe-icons; trademarks belong to their owners. Zhipu's mark was rejected for
Z.AI: it is a dense lattice that is unreadable below ~40px.
Rendering them at 13px took three fixes. A layer rasterises the Shape at its own
size and then scales the texture, so a 24px buffer minified to 13 resampled two
pixels into one and the Claude burst lost rays; without the layer the scale is a
transform on the geometry and rasterisation happens once, at final resolution.
CurveRenderer replaces the tessellating default. And the size is forced odd,
because a radially symmetric mark puts its vertical and horizontal arms on the
centre line, which is a pixel centre at odd sizes and the seam between two
pixels at even ones, where each arm splits its coverage and greys out. Measured
against a cairo render at the same size, the result is now identical.
Sizing is measured rather than guessed. Every stock glyph in this bar paints
11px of ink; the marks fill their box instead of carrying a font's padding, so
the box is the smaller number. Each mark also carries an optical scale from two
measurements of a 200px render - LM Studio's filled container covers 69% of its
box against ~38% for the others, and Z.AI and OpenRouter are wide-but-short
marks spanning ~84% of the box height.
Choosing a target does not switch immediately. It runs the CLI's own preflight,
and if that refuses, the panel names what is missing and offers the fix - a
terminal for the hidden key prompt, a re-check once a server is up, or the
server form. If sessions are running it lists them by terminal and directory,
marks any mid-request, and asks whether to restart them, close them, or leave
them. The session action is applied strictly after the write, since restarting
first would only bring them back up on the provider just left.
The server form edits an LM Studio preset's base URL and whether it needs an API
key, reachable from the gear on any LM Studio preset row - per row, because two
presets can point at two different machines - and from the failure card. The
shipped local default is untouched unless it is changed.