README: install commands first, sections reorganized
This commit is contained in:
@@ -3,9 +3,10 @@
|
|||||||
Switch Claude Code system-wide between **Anthropic**, **OpenRouter**, **Z.AI**, and a
|
Switch Claude Code system-wide between **Anthropic**, **OpenRouter**, **Z.AI**, and a
|
||||||
local **LM Studio** server — with named per-tier model presets.
|
local **LM Studio** server — with named per-tier model presets.
|
||||||
|
|
||||||
Windows / PowerShell 5.1 with no external dependencies, and a POSIX port for
|
One write to `~/.claude/settings.json` covers the CLI, the VS Code extension, and
|
||||||
Linux and macOS that needs only python3 — including a theme-aware TUI and an
|
the desktop app. Restart Claude Code afterwards — nothing else. Windows
|
||||||
Omarchy bar widget. See [Linux / Omarchy](#linux--omarchy).
|
(PowerShell 5.1, no external dependencies) and a POSIX port for Linux and macOS
|
||||||
|
(python3 only), including a theme-aware TUI and an Omarchy bar widget.
|
||||||
|
|
||||||
```
|
```
|
||||||
claude-mode # interactive menu
|
claude-mode # interactive menu
|
||||||
@@ -15,11 +16,124 @@ claude-mode zai # Z.AI GLM coding plan (preset: zai)
|
|||||||
claude-mode lmstudio # local server (preset: lmstudio)
|
claude-mode lmstudio # local server (preset: lmstudio)
|
||||||
```
|
```
|
||||||
|
|
||||||
Works for the CLI, the VS Code extension, and the desktop app from a single
|
**Contents:** [Install](#install) · [First run](#first-run) · [The interactive
|
||||||
switch. Restart Claude Code afterwards — nothing else.
|
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) ·
|
||||||
|
[Troubleshooting](#troubleshooting) · [Design decisions](#design-decisions) ·
|
||||||
|
[Layout](#layout) · [Linux / Omarchy](#linux--omarchy) · [The bar
|
||||||
|
widget](#the-omarchy-bar-widget) · [Uninstall](#uninstall)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
### Windows (PowerShell 5.1+)
|
||||||
|
|
||||||
|
One line:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
irm https://git.nebulm.com/smoido/claude-mode/raw/branch/master/install.ps1 | iex
|
||||||
|
```
|
||||||
|
|
||||||
|
Piped in like that there is no checkout, so the installer fetches the
|
||||||
|
repository archive to `%TEMP%` and installs from it. To pass switches
|
||||||
|
(`-Force`, `-SkipKeyPrompt`), download first and run the file:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
irm https://git.nebulm.com/smoido/claude-mode/raw/branch/master/install.ps1 -OutFile install.ps1
|
||||||
|
.\install.ps1 -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
Or clone it — the normal path if you want to read the code and pull updates:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git clone https://git.nebulm.com/smoido/claude-mode.git
|
||||||
|
cd claude-mode
|
||||||
|
.\install.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Installs to `~/.claude-mode/` (ACL: you only), drops `claude-mode.cmd` into
|
||||||
|
`~/.local/bin` (already on your User PATH, next to `claude.exe`), and adds a
|
||||||
|
marked block to `~/Documents/WindowsPowerShell/profile.ps1`.
|
||||||
|
|
||||||
|
If the profile doesn't load: `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`.
|
||||||
|
|
||||||
|
### Linux / macOS
|
||||||
|
|
||||||
|
One line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.nebulm.com/smoido/claude-mode/raw/branch/master/linux/install.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Or clone:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.nebulm.com/smoido/claude-mode.git
|
||||||
|
cd claude-mode
|
||||||
|
bash linux/install.sh # --skip-key-prompt to install without storing a key
|
||||||
|
```
|
||||||
|
|
||||||
|
Installs to `~/.claude-mode/`, symlinks `~/.local/bin/claude-mode`, and adds the
|
||||||
|
`claude` wrapper to `~/.bashrc` / `~/.zshrc` between markers.
|
||||||
|
|
||||||
|
Secrets have no DPAPI equivalent here, so the vault picks the best backend
|
||||||
|
available and says which one it chose: macOS Keychain, libsecret, `pass`, or a
|
||||||
|
0600 file that is honestly labelled as unencrypted.
|
||||||
|
|
||||||
|
### Omarchy bar widget
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash omarchy/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Copies the plugin to `~/.config/omarchy/plugins/smoido.claude-mode/` and adds its
|
||||||
|
id to the bar layout in `~/.config/omarchy/shell.json` (backed up first). Needs a
|
||||||
|
checkout — clone first. See [the bar widget](#the-omarchy-bar-widget) for what it
|
||||||
|
does.
|
||||||
|
|
||||||
|
### All three, and upgrades
|
||||||
|
|
||||||
|
The installers never touch `~/.claude/settings.json` — that only happens on an
|
||||||
|
actual mode switch, which backs it up to `~/.claude-mode/backups/` first
|
||||||
|
(last 20 kept).
|
||||||
|
|
||||||
|
Re-running any of them is a safe upgrade: scripts are overwritten, existing
|
||||||
|
presets are kept unless `-Force` / `--force` is passed.
|
||||||
|
|
||||||
|
## First run
|
||||||
|
|
||||||
|
A shipped preset is a starting point, not a working configuration. OpenRouter
|
||||||
|
and Z.AI have no key stored. LM Studio's model ids are whatever happened to be
|
||||||
|
installed on the machine this was packaged on, which is almost certainly not
|
||||||
|
yours.
|
||||||
|
|
||||||
|
So the shipped presets carry `configured: false`, and preflight treats that as a
|
||||||
|
blocker with its own remedy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude-mode setup openrouter # key, then models from OpenRouter's catalogue
|
||||||
|
claude-mode setup zai # key, then per-tier GLM models
|
||||||
|
claude-mode setup lmstudio # server URL, auth, then models from that server
|
||||||
|
claude-mode setup anthropic # nothing to do; it uses your existing login
|
||||||
|
```
|
||||||
|
|
||||||
|
Setup asks only what it cannot work out, shows the current model map before
|
||||||
|
offering to change it, and picks from the provider's own catalogue rather than
|
||||||
|
asking anyone to type a model id from memory. LM Studio maps one model across
|
||||||
|
every tier, since a local server has one loaded at a time and per-tier mapping
|
||||||
|
would just pay the load cost on every tier change; the remote gateways ask per
|
||||||
|
tier, which is the point of them.
|
||||||
|
|
||||||
|
In a terminal, a switch that trips this offers to run setup there and then
|
||||||
|
rather than printing a command to type next. It sets `configured: true` on the
|
||||||
|
way out.
|
||||||
|
|
||||||
|
**Absent means configured.** Presets that predate this, and any built by hand
|
||||||
|
with `preset new`, are nobody's business but yours and will not start demanding
|
||||||
|
a wizard.
|
||||||
|
|
||||||
## The interactive menu
|
## The interactive menu
|
||||||
|
|
||||||
Run `claude-mode` with no arguments. The mode you're already in is omitted —
|
Run `claude-mode` with no arguments. The mode you're already in is omitted —
|
||||||
@@ -45,84 +159,6 @@ edit the preset that's currently live it re-applies immediately.
|
|||||||
When stdin is redirected (scripts, CI) the menu is skipped and `status` prints
|
When stdin is redirected (scripts, CI) the menu is skipped and `status` prints
|
||||||
instead, so `claude-mode` is still safe in a pipeline.
|
instead, so `claude-mode` is still safe in a pipeline.
|
||||||
|
|
||||||
## Why settings.json and not a profile export
|
|
||||||
|
|
||||||
Three ways to make this persist. I picked the second.
|
|
||||||
|
|
||||||
**1. Export the variables from `$PROFILE`.**
|
|
||||||
The obvious move, and the wrong one here. It only covers processes launched from
|
|
||||||
a PowerShell session that loaded the profile — which is exactly *not* how you use
|
|
||||||
Claude Code. The VS Code extension is spawned by VS Code, not by your shell, so
|
|
||||||
it would never see the exports. Same for the desktop app, `cmd.exe`, and any
|
|
||||||
terminal opened before the switch. Worse, the failure is silent: you switch to
|
|
||||||
`anthropic`, a shell opened five minutes ago still has `ANTHROPIC_BASE_URL` set,
|
|
||||||
and that session quietly keeps billing OpenRouter.
|
|
||||||
|
|
||||||
**2. Rewrite the `env` block in `~/.claude/settings.json`.** ← chosen
|
|
||||||
Claude Code reads this file on every startup, from every launch context. One
|
|
||||||
write, and the next `claude` — CLI, extension, desktop — picks it up. A switch is
|
|
||||||
atomic: one file, one source of truth. `claude-mode anthropic` *deletes* the
|
|
||||||
managed keys rather than blanking them, so nothing can linger and break native
|
|
||||||
auth. The cost is that config is global rather than per-terminal.
|
|
||||||
|
|
||||||
**3. Persistent User-scope environment variables (`setx`).**
|
|
||||||
Also global and reboot-proof, but strictly worse: new processes only, values sit
|
|
||||||
in the registry in plaintext, and a stale entry silently outranks whatever
|
|
||||||
`claude-mode` writes. This tool treats them as a fault condition — `status` and
|
|
||||||
`doctor` flag them and offer removal, backing the old value up first.
|
|
||||||
|
|
||||||
The `claude` wrapper in the profile is a **safety net, not the mechanism**. It
|
|
||||||
strips inherited process-level copies of all thirteen managed variables before
|
|
||||||
launching `claude.exe`. Everything still works without it — including in VS Code,
|
|
||||||
which never loads the profile.
|
|
||||||
|
|
||||||
## Why API keys are not in settings.json
|
|
||||||
|
|
||||||
`settings.json` is a config file you'll hand-edit, diff, and possibly paste into
|
|
||||||
a bug report. A `sk-or-` or Z.AI token does not belong there.
|
|
||||||
|
|
||||||
Keys are stored **DPAPI-encrypted** in `~/.claude-mode/vault/*.cred` — encrypted
|
|
||||||
against your Windows account on this machine, so copying the file elsewhere or
|
|
||||||
reading it as another user yields nothing — with the file ACL restricted to you.
|
|
||||||
Claude Code receives the key at runtime through `apiKeyHelper`, which decrypts
|
|
||||||
and prints it. `settings.json` holds only the base URL and model IDs.
|
|
||||||
|
|
||||||
In `anthropic` mode the helper is removed from settings.json *and* returns
|
|
||||||
nothing when state says `anthropic` — belt and braces. LM Studio's `lmstudio`
|
|
||||||
token is a placeholder, not a secret, so it's written inline and the helper stays
|
|
||||||
out of it.
|
|
||||||
|
|
||||||
### When the helper "is failing"
|
|
||||||
|
|
||||||
Claude Code reports a broken helper as *your apiKeyHelper script is failing* and
|
|
||||||
shows its stderr under `/status`. Two causes account for nearly all of it.
|
|
||||||
|
|
||||||
**A space in your home directory.** `apiKeyHelper` is a shell *command line*, not
|
|
||||||
a path, so `C:\Users\Firstname Lastname\.claude-mode\bin\claude-key-helper.cmd`
|
|
||||||
is split at the space and cmd tries to run `C:\Users\Firstname`. The value is now
|
|
||||||
quoted when it needs to be, on both ports — `shlex.quote` on POSIX, where a
|
|
||||||
`/Users/Firstname Lastname` home does the same thing. Paths that need no quoting
|
|
||||||
are written bare exactly as before, so no existing `settings.json` churns.
|
|
||||||
|
|
||||||
`doctor` used to miss this, because it quoted the path itself before running it
|
|
||||||
and so tested something Claude Code never sees. It now reads the string out of
|
|
||||||
`settings.json`, says so when that string is not what a switch would write, and
|
|
||||||
executes *that* string through a shell. Re-running the switch rewrites it:
|
|
||||||
|
|
||||||
```
|
|
||||||
claude-mode openrouter default
|
|
||||||
claude-mode doctor
|
|
||||||
```
|
|
||||||
|
|
||||||
**A key stored by a different Windows account.** The vault is DPAPI `CurrentUser`
|
|
||||||
scope, so a key stored from an elevated or *run as* shell cannot be decrypted by
|
|
||||||
the account Claude Code runs as. Re-run `claude-mode set-key <ref>` unelevated,
|
|
||||||
as yourself.
|
|
||||||
|
|
||||||
Every helper failure path now names itself on stderr rather than exiting 1 in
|
|
||||||
silence, so `/status` distinguishes these from a missing preset or an empty
|
|
||||||
vault. The success paths stay silent — stdout carries the key and nothing else.
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -207,7 +243,7 @@ background tasks. Switching without `contextTokens` prints a warning.
|
|||||||
|
|
||||||
Adjust per preset:
|
Adjust per preset:
|
||||||
|
|
||||||
```powershell
|
```bash
|
||||||
# edit ~/.claude-mode/presets/<name>.json -> "contextTokens": 262144
|
# edit ~/.claude-mode/presets/<name>.json -> "contextTokens": 262144
|
||||||
claude-mode doctor # re-checks declared vs actual
|
claude-mode doctor # re-checks declared vs actual
|
||||||
```
|
```
|
||||||
@@ -229,7 +265,7 @@ Per [Z.AI's Claude Code docs](https://docs.z.ai/devpack/tool/claude):
|
|||||||
|
|
||||||
All three extra variables are confirmed present in CLI 2.1.221. Setup:
|
All three extra variables are confirmed present in CLI 2.1.221. Setup:
|
||||||
|
|
||||||
```powershell
|
```bash
|
||||||
claude-mode set-key zai # paste your key from https://z.ai/manage-apikey/apikey-list
|
claude-mode set-key zai # paste your key from https://z.ai/manage-apikey/apikey-list
|
||||||
claude-mode zai
|
claude-mode zai
|
||||||
claude-mode doctor # sends a 1-token request to prove the key works
|
claude-mode doctor # sends a 1-token request to prove the key works
|
||||||
@@ -286,7 +322,7 @@ end-to-end with a cold JIT load, streaming, and tool calls.
|
|||||||
Honest caveat: KAT-Coder's template *does* contain the assertion, but I could not
|
Honest caveat: KAT-Coder's template *does* contain the assertion, but I could not
|
||||||
reproduce the failure against it here — cold JIT, streaming, tools, system
|
reproduce the failure against it here — cold JIT, streaming, tools, system
|
||||||
blocks, and multi-turn `tool_result` all succeeded. Whether it trips seems to
|
blocks, and multi-turn `tool_result` all succeeded. Whether it trips seems to
|
||||||
depend on which parser strategy the runtime picks. If the model setup lands on
|
depend on which parser strategy the runtime picks. If the model you land on
|
||||||
does spam, run setup again and pick another:
|
does spam, run setup again and pick another:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -298,67 +334,37 @@ the model must be **installed** — JIT loading handles "not loaded" fine.
|
|||||||
|
|
||||||
If you enable authentication in LM Studio, move that preset to a vault key:
|
If you enable authentication in LM Studio, move that preset to a vault key:
|
||||||
|
|
||||||
```powershell
|
```bash
|
||||||
claude-mode set-key lmstudio
|
claude-mode set-key lmstudio
|
||||||
# then in ~/.claude-mode/presets/lmstudio.json:
|
# then in ~/.claude-mode/presets/lmstudio.json:
|
||||||
# "auth": { "mode": "vault", "keyRef": "lmstudio" }
|
# "auth": { "mode": "vault", "keyRef": "lmstudio" }
|
||||||
```
|
```
|
||||||
|
|
||||||
## CLI version
|
### LM Studio somewhere other than this machine
|
||||||
|
|
||||||
Verified against `claude.exe` **2.1.221** by scanning the binary — every variable
|
It ships on loopback, but that is a default, not a constraint. A preset is just
|
||||||
this tool writes is referenced by it:
|
a base URL and an auth block, so all of these are the same two fields:
|
||||||
|
|
||||||
`ANTHROPIC_BASE_URL` · `ANTHROPIC_AUTH_TOKEN` · `ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU,FABLE}_MODEL` ·
|
```bash
|
||||||
`CLAUDE_CODE_SUBAGENT_MODEL` · `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY` ·
|
claude-mode preset url lmstudio http://192.168.1.40:1234 # another box on the LAN
|
||||||
`CLAUDE_CODE_ATTRIBUTION_HEADER` · `CLAUDE_CODE_AUTO_COMPACT_WINDOW` ·
|
claude-mode preset url lmstudio https://lms.example.net # through a tunnel or proxy
|
||||||
`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` · `API_TIMEOUT_MS` · `apiKeyHelper`
|
claude-mode preset auth lmstudio key lmstudio # that server wants an API key
|
||||||
|
claude-mode set-key lmstudio # store it (vault, not settings.json)
|
||||||
(On 2.1.89 the fable and gateway-discovery vars did not exist; the update to
|
claude-mode preset auth lmstudio none # back to the open-server default
|
||||||
2.1.221 added both.)
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
One line, on any machine that can reach the repo (PowerShell 5.1 is fine):
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
irm https://git.nebulm.com/smoido/claude-mode/raw/branch/master/install.ps1 | iex
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Piped in like that there is no checkout, so the installer fetches the
|
`auth none` writes LM Studio's inline placeholder token, which is not a secret
|
||||||
repository archive to `%TEMP%` and installs from it. To pass switches
|
and is what an unauthenticated server expects. `auth key` moves it to the vault
|
||||||
(`-Force`, `-SkipKeyPrompt`), download first and run the file:
|
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.
|
||||||
|
|
||||||
```powershell
|
## Live sessions
|
||||||
irm https://git.nebulm.com/smoido/claude-mode/raw/branch/master/install.ps1 -OutFile install.ps1
|
|
||||||
.\install.ps1 -Force
|
|
||||||
```
|
|
||||||
|
|
||||||
Or clone it — the normal path if you want to read the code and pull updates:
|
### Why a switch breaks a running session
|
||||||
|
|
||||||
```powershell
|
A switch **breaks** running sessions. The two halves of the config behave
|
||||||
git clone https://git.nebulm.com/smoido/claude-mode.git
|
differently, which is why.
|
||||||
cd claude-mode
|
|
||||||
.\install.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
All three install the same way: to `~/.claude-mode/` (ACL: you only), dropping
|
|
||||||
`claude-mode.cmd` into `~/.local/bin` (already on your User PATH, next to
|
|
||||||
`claude.exe`), and adding a marked block to
|
|
||||||
`~/Documents/WindowsPowerShell/profile.ps1`.
|
|
||||||
`~/.claude/settings.json` is **not** touched by the installer — only by an actual
|
|
||||||
mode switch, which backs it up to `~/.claude-mode/backups/` first (last 20 kept).
|
|
||||||
|
|
||||||
Re-running any of them is a safe upgrade: scripts are overwritten, existing
|
|
||||||
presets are kept unless `-Force` is passed.
|
|
||||||
|
|
||||||
If the profile doesn't load: `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`.
|
|
||||||
|
|
||||||
## Restarting sessions
|
|
||||||
|
|
||||||
A switch **breaks** running sessions. This originally said it did not affect
|
|
||||||
them; that was wrong, and the difference matters, because the two halves of the
|
|
||||||
config behave differently.
|
|
||||||
|
|
||||||
The static half — base URL, model ids, the env block — genuinely is read once at
|
The static half — base URL, model ids, the env block — genuinely is read once at
|
||||||
startup, and a running session keeps what it started with.
|
startup, and a running session keeps what it started with.
|
||||||
@@ -375,7 +381,14 @@ switch reaches into a live session through the one thing that was never cached:
|
|||||||
| another preset of the *same* provider, same `keyRef` | same key, same endpoint — this one survives, on the model ids it started with |
|
| another preset of the *same* provider, same `keyRef` | same key, same endpoint — this one survives, on the model ids it started with |
|
||||||
|
|
||||||
Either of the first two starts failing calls whenever the TTL happens to expire:
|
Either of the first two starts failing calls whenever the TTL happens to expire:
|
||||||
mid-turn as easily as between turns.
|
mid-turn as easily as between turns. So restart affected sessions — and
|
||||||
|
`claude-mode sessions --restart` will do it for you:
|
||||||
|
|
||||||
|
- **CLI** — exit and relaunch `claude`
|
||||||
|
- **VS Code** — `Ctrl+Shift+P` → *Developer: Reload Window*
|
||||||
|
- **Desktop app** — quit and reopen
|
||||||
|
|
||||||
|
`claude-mode status` shows what the *next* launch will use.
|
||||||
|
|
||||||
### The part that is not just an inconvenience
|
### The part that is not just an inconvenience
|
||||||
|
|
||||||
@@ -441,6 +454,44 @@ session untouched.
|
|||||||
It refuses to touch a transcript written to in the last 90 seconds, since that
|
It refuses to touch a transcript written to in the last 90 seconds, since that
|
||||||
one belongs to a session still alive.
|
one belongs to a session still alive.
|
||||||
|
|
||||||
|
### The switch asks first
|
||||||
|
|
||||||
|
A switch stops and asks while any sessions are running, offering to restart them
|
||||||
|
(the only answer that ends with everything on the mode the bar now claims), close
|
||||||
|
them, proceed anyway, or abort — and abort is the default. Non-interactively it
|
||||||
|
refuses outright unless given `--yes`.
|
||||||
|
|
||||||
|
```
|
||||||
|
claude-mode sessions
|
||||||
|
running sessions (2)
|
||||||
|
562250 pts/4 /home/smoido/Work working (this session - never touched)
|
||||||
|
631644 pts/1 /home/smoido/Projects/api
|
||||||
|
```
|
||||||
|
|
||||||
|
Sessions are found through `/proc/<pid>/exe`, which on Linux resolves to the
|
||||||
|
real `claude` binary — a process-name match would sweep up every shell that
|
||||||
|
merely mentions claude on its command line, including the one this is running
|
||||||
|
from. Two things are then filtered out:
|
||||||
|
|
||||||
|
- **The calling session.** Killing the session that asked for the kill is not a
|
||||||
|
thing anyone means, so it is listed and never signalled.
|
||||||
|
- **Forks of a session.** A busy session spawns children off its own binary,
|
||||||
|
and they inherit the same `exe`. Without excluding anything whose parent is
|
||||||
|
itself claude, the count climbs and falls with how hard the machine is
|
||||||
|
thinking — it read 2, 5, 11 and 40 on the same two sessions before this. A
|
||||||
|
real session's parent is a terminal.
|
||||||
|
|
||||||
|
`working` is a sampled-CPU heuristic — two reads of `utime + stime` 300ms apart
|
||||||
|
— so it is a good guess about which session is mid-turn, not a promise.
|
||||||
|
|
||||||
|
`--stop` sends SIGTERM (never SIGKILL; Claude Code writes out its transcript on
|
||||||
|
the way down). `--restart` stops each session and reopens it — re-running the
|
||||||
|
parent terminal's own command line where there is one, so the same terminal,
|
||||||
|
flags and directory come back, and falling back to a fresh terminal in the
|
||||||
|
session's directory otherwise. Interactive runs confirm first; `--yes` is for
|
||||||
|
callers that have already asked, and `--dry-run` prints the plan and touches
|
||||||
|
nothing.
|
||||||
|
|
||||||
### The bar notices for you
|
### The bar notices for you
|
||||||
|
|
||||||
Nothing tells you a session is unresumable until you try to resume it, by which
|
Nothing tells you a session is unresumable until you try to resume it, by which
|
||||||
@@ -458,123 +509,92 @@ rest of the file is never opened. Since that is the overwhelmingly common case,
|
|||||||
the whole sweep costs ~60ms for 59 transcripts, against ~5s for the obvious
|
the whole sweep costs ~60ms for 59 transcripts, against ~5s for the obvious
|
||||||
version that reads every byte of every one.
|
version that reads every byte of every one.
|
||||||
|
|
||||||
This is why a switch now asks before it writes rather than reporting afterwards.
|
This is why a switch asks before it writes rather than reporting afterwards.
|
||||||
So restart affected sessions — and `claude-mode sessions --restart` will do it
|
|
||||||
for you:
|
|
||||||
|
|
||||||
- **CLI** — exit and relaunch `claude`
|
## Troubleshooting
|
||||||
- **VS Code** — `Ctrl+Shift+P` → *Developer: Reload Window*
|
|
||||||
- **Desktop app** — quit and reopen
|
|
||||||
|
|
||||||
`claude-mode status` shows what the *next* launch will use.
|
### When the helper "is failing"
|
||||||
|
|
||||||
## Layout
|
Claude Code reports a broken helper as *your apiKeyHelper script is failing* and
|
||||||
|
shows its stderr under `/status`. Two causes account for nearly all of it.
|
||||||
|
|
||||||
|
**A space in your home directory.** `apiKeyHelper` is a shell *command line*, not
|
||||||
|
a path, so `C:\Users\Firstname Lastname\.claude-mode\bin\claude-key-helper.cmd`
|
||||||
|
is split at the space and cmd tries to run `C:\Users\Firstname`. The value is now
|
||||||
|
quoted when it needs to be, on both ports — `shlex.quote` on POSIX, where a
|
||||||
|
`/Users/Firstname Lastname` home does the same thing. Paths that need no quoting
|
||||||
|
are written bare exactly as before, so no existing `settings.json` churns.
|
||||||
|
|
||||||
|
`doctor` used to miss this, because it quoted the path itself before running it
|
||||||
|
and so tested something Claude Code never sees. It now reads the string out of
|
||||||
|
`settings.json`, says so when that string is not what a switch would write, and
|
||||||
|
executes *that* string through a shell. Re-running the switch rewrites it:
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.claude-mode/
|
claude-mode openrouter default
|
||||||
claude-mode.ps1 main script
|
claude-mode doctor
|
||||||
state.json mode, active preset, and the exact env keys last written
|
|
||||||
presets/*.json provider + model maps
|
|
||||||
vault/*.cred DPAPI-encrypted keys (openrouter, zai, ...)
|
|
||||||
backups/ settings.json snapshots + removed env-var values
|
|
||||||
bin/claude-key-helper.cmd apiKeyHelper shim
|
|
||||||
~/.local/bin/claude-mode.cmd PATH entry point (works from any shell)
|
|
||||||
~/Documents/WindowsPowerShell/profile.ps1
|
|
||||||
claude-mode + claude functions, between markers
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`state.json` records which env keys the last switch actually wrote, so a custom
|
**A key stored by a different Windows account.** The vault is DPAPI `CurrentUser`
|
||||||
`extraEnv` key (Z.AI's timeouts, LM Studio's attribution header) is removed when
|
scope, so a key stored from an elevated or *run as* shell cannot be decrypted by
|
||||||
you switch away — even though no other preset knows that key exists.
|
the account Claude Code runs as. Re-run `claude-mode set-key <ref>` unelevated,
|
||||||
|
as yourself.
|
||||||
|
|
||||||
## Linux / Omarchy
|
Other known issues with their own sections: [context windows and early
|
||||||
|
compaction](#context-windows-and-early-auto-compaction), the [LM Studio template
|
||||||
|
parser spam](#the-server-error--unable-to-generate-parser-for-this-template-spam),
|
||||||
|
and [refusing a switch that would not work](#refusing-a-switch-that-would-not-work).
|
||||||
|
|
||||||
The `linux/` tree is a POSIX port of the same design: one write to
|
## Design decisions
|
||||||
`~/.claude/settings.json`, secrets kept out of it, presets shared verbatim with
|
|
||||||
the Windows build.
|
|
||||||
|
|
||||||
```bash
|
### Why settings.json and not a profile export
|
||||||
cd claude-code-switcher
|
|
||||||
bash linux/install.sh # --skip-key-prompt to install without storing a key
|
|
||||||
```
|
|
||||||
|
|
||||||
Installs to `~/.claude-mode/`, symlinks `~/.local/bin/claude-mode`, and adds the
|
Three ways to make this persist. I picked the second.
|
||||||
`claude` wrapper to `~/.bashrc` / `~/.zshrc` between markers.
|
|
||||||
|
|
||||||
Secrets have no DPAPI equivalent here, so the vault picks the best backend
|
**1. Export the variables from `$PROFILE`.**
|
||||||
available and says which one it chose: macOS Keychain, libsecret, `pass`, or a
|
The obvious move, and the wrong one here. It only covers processes launched from
|
||||||
0600 file that is honestly labelled as unencrypted.
|
a PowerShell session that loaded the profile — which is exactly *not* how you use
|
||||||
|
Claude Code. The VS Code extension is spawned by VS Code, not by your shell, so
|
||||||
|
it would never see the exports. Same for the desktop app, `cmd.exe`, and any
|
||||||
|
terminal opened before the switch. Worse, the failure is silent: you switch to
|
||||||
|
`anthropic`, a shell opened five minutes ago still has `ANTHROPIC_BASE_URL` set,
|
||||||
|
and that session quietly keeps billing OpenRouter.
|
||||||
|
|
||||||
### The menu follows your desktop theme
|
**2. Rewrite the `env` block in `~/.claude/settings.json`.** ← chosen
|
||||||
|
Claude Code reads this file on every startup, from every launch context. One
|
||||||
|
write, and the next `claude` — CLI, extension, desktop — picks it up. A switch is
|
||||||
|
atomic: one file, one source of truth. `claude-mode anthropic` *deletes* the
|
||||||
|
managed keys rather than blanking them, so nothing can linger and break native
|
||||||
|
auth. The cost is that config is global rather than per-terminal.
|
||||||
|
|
||||||
The sixteen ANSI colour slots carry no guarantee about relative brightness, and
|
**3. Persistent User-scope environment variables (`setx`).**
|
||||||
monochrome themes exploit that. Under Omarchy's Solitude, slot 36 — headings —
|
Also global and reboot-proof, but strictly worse: new processes only, values sit
|
||||||
resolves to `#707070` and slot 31 — `FAIL` — to `#565d60`. Against `#cacccc`
|
in the registry in plaintext, and a stale entry silently outranks whatever
|
||||||
body text on a `#101315` ground that is 3.8:1 and 2.8:1 where the body text is
|
`claude-mode` writes. This tool treats them as a fault condition — `status` and
|
||||||
11.6:1, so headings render as fine print and an error becomes the quietest
|
`doctor` flag them and offer removal, backing the old value up first.
|
||||||
thing on screen. Exactly backwards.
|
|
||||||
|
|
||||||
So when Omarchy is present, the palette is derived from the theme it publishes
|
The `claude` wrapper in the profile is a **safety net, not the mechanism**. It
|
||||||
at `~/.local/state/omarchy/current/theme/colors.toml` instead. Every role is
|
strips inherited process-level copies of all thirteen managed variables before
|
||||||
measured against the background it will actually be drawn on and lifted toward
|
launching `claude.exe`. Everything still works without it — including in VS Code,
|
||||||
the foreground when it falls short, which keeps hue where the theme has any and
|
which never loads the profile.
|
||||||
falls back to weight where it does not:
|
|
||||||
|
|
||||||
| role | before (Solitude) | after |
|
### Why API keys are not in settings.json
|
||||||
|---|---|---|
|
|
||||||
| heading | 3.8:1 | 9.4:1 |
|
|
||||||
| `FAIL` | 2.8:1 | 5.2:1 |
|
|
||||||
| help text | 2.2:1 | 2.2:1 (recessive on purpose, floored) |
|
|
||||||
|
|
||||||
Light themes are handled by the same arithmetic — `mode` in `colors.toml` is
|
`settings.json` is a config file you'll hand-edit, diff, and possibly paste into
|
||||||
authoritative, so `catppuccin-latte` and `flexoki-light` keep their accents
|
a bug report. A `sk-or-` or Z.AI token does not belong there.
|
||||||
rather than washing out.
|
|
||||||
|
|
||||||
Overrides: `CLAUDE_MODE_THEME=/path/to/colors.toml` points it elsewhere,
|
Keys are stored **DPAPI-encrypted** in `~/.claude-mode/vault/*.cred` — encrypted
|
||||||
`NO_COLOR` turns it off. Without Omarchy, or on a terminal that cannot do
|
against your Windows account on this machine, so copying the file elsewhere or
|
||||||
truecolor, it falls back to the ANSI slots with the two roles the slots get
|
reading it as another user yields nothing — with the file ACL restricted to you.
|
||||||
wrong corrected — bright red for `FAIL`, bold on headings.
|
Claude Code receives the key at runtime through `apiKeyHelper`, which decrypts
|
||||||
|
and prints it. `settings.json` holds only the base URL and model IDs.
|
||||||
|
|
||||||
### `claude-mode health`
|
In `anthropic` mode the helper is removed from settings.json *and* returns
|
||||||
|
nothing when state says `anthropic` — belt and braces. LM Studio's `lmstudio`
|
||||||
|
token is a placeholder, not a secret, so it's written inline and the helper stays
|
||||||
|
out of it.
|
||||||
|
|
||||||
Rewrites `~/.claude-mode/health.json`, the machine-readable mirror of the
|
### Refusing a switch that would not work
|
||||||
active configuration: mode, preset, model map, context window, key backend, and
|
|
||||||
the switchable preset catalogue. No key material. A switch and a `status` both
|
|
||||||
refresh it; the command exists for anything that wants to force it.
|
|
||||||
|
|
||||||
## First-run setup
|
|
||||||
|
|
||||||
A shipped preset is a starting point, not a working configuration. OpenRouter
|
|
||||||
and Z.AI have no key stored. LM Studio's model ids are whatever happened to be
|
|
||||||
installed on the machine this was packaged on, which is almost certainly not
|
|
||||||
yours.
|
|
||||||
|
|
||||||
So the shipped presets carry `configured: false`, and preflight treats that as a
|
|
||||||
blocker with its own remedy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
claude-mode setup openrouter # key, then models from OpenRouter's catalogue
|
|
||||||
claude-mode setup zai # key, then per-tier GLM models
|
|
||||||
claude-mode setup lmstudio # server URL, auth, then models from that server
|
|
||||||
claude-mode setup anthropic # nothing to do; it uses your existing login
|
|
||||||
```
|
|
||||||
|
|
||||||
Setup asks only what it cannot work out, shows the current model map before
|
|
||||||
offering to change it, and picks from the provider's own catalogue rather than
|
|
||||||
asking anyone to type a model id from memory. LM Studio maps one model across
|
|
||||||
every tier, since a local server has one loaded at a time and per-tier mapping
|
|
||||||
would just pay the load cost on every tier change; the remote gateways ask per
|
|
||||||
tier, which is the point of them.
|
|
||||||
|
|
||||||
In a terminal, a switch that trips this offers to run setup there and then
|
|
||||||
rather than printing a command to type next. It sets `configured: true` on the
|
|
||||||
way out.
|
|
||||||
|
|
||||||
**Absent means configured.** Presets that predate this, and any built by hand
|
|
||||||
with `preset new`, are nobody's business but yours and will not start demanding
|
|
||||||
a wizard.
|
|
||||||
|
|
||||||
## Refusing a switch that would not work
|
|
||||||
|
|
||||||
A switch writes settings.json and is picked up by the *next* `claude` launch, so
|
A switch writes settings.json and is picked up by the *next* `claude` launch, so
|
||||||
switching into a mode that cannot serve requests does not fail loudly — it
|
switching into a mode that cannot serve requests does not fail loudly — it
|
||||||
@@ -616,83 +636,94 @@ an instance on a sleeping LAN box is exactly as absent as a loopback port with
|
|||||||
nothing behind it, and produces the identical silent breakage. Off-machine
|
nothing behind it, and produces the identical silent breakage. Off-machine
|
||||||
addresses get a longer timeout, not a pass.
|
addresses get a longer timeout, not a pass.
|
||||||
|
|
||||||
### LM Studio somewhere other than this machine
|
### CLI version
|
||||||
|
|
||||||
It ships on loopback, but that is a default, not a constraint. A preset is just
|
Verified against `claude.exe` **2.1.221** by scanning the binary — every variable
|
||||||
a base URL and an auth block, so all of these are the same two fields:
|
this tool writes is referenced by it:
|
||||||
|
|
||||||
```bash
|
`ANTHROPIC_BASE_URL` · `ANTHROPIC_AUTH_TOKEN` · `ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU,FABLE}_MODEL` ·
|
||||||
claude-mode preset url lmstudio http://192.168.1.40:1234 # another box on the LAN
|
`CLAUDE_CODE_SUBAGENT_MODEL` · `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY` ·
|
||||||
claude-mode preset url lmstudio https://lms.example.net # through a tunnel or proxy
|
`CLAUDE_CODE_ATTRIBUTION_HEADER` · `CLAUDE_CODE_AUTO_COMPACT_WINDOW` ·
|
||||||
claude-mode preset auth lmstudio key lmstudio # that server wants an API key
|
`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` · `API_TIMEOUT_MS` · `apiKeyHelper`
|
||||||
claude-mode set-key lmstudio # store it (vault, not settings.json)
|
|
||||||
claude-mode preset auth lmstudio none # back to the open-server default
|
|
||||||
```
|
|
||||||
|
|
||||||
`auth none` writes LM Studio's inline placeholder token, which is not a secret
|
(On 2.1.89 the fable and gateway-discovery vars did not exist; the update to
|
||||||
and is what an unauthenticated server expects. `auth key` moves it to the vault
|
2.1.221 added both.)
|
||||||
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.
|
|
||||||
|
|
||||||
## Sessions still on the old provider
|
## Layout
|
||||||
|
|
||||||
A switch does not leave running sessions on the old provider — it breaks them.
|
Windows:
|
||||||
See [Restarting sessions](#restarting-sessions) for the mechanism: their
|
|
||||||
endpoint is fixed at startup but their credential is re-fetched on a timer, so
|
|
||||||
it switches under them and the endpoint they are still pointed at refuses it.
|
|
||||||
Which is why this is a decision rather than a notification.
|
|
||||||
|
|
||||||
A switch stops and asks while any are running, offering to restart them (the
|
|
||||||
only answer that ends with everything on the mode the bar now claims), close
|
|
||||||
them, proceed anyway, or abort — and abort is the default. Non-interactively it
|
|
||||||
refuses outright unless given `--yes`.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
claude-mode sessions
|
~/.claude-mode/
|
||||||
running sessions (2)
|
claude-mode.ps1 main script
|
||||||
562250 pts/4 /home/smoido/Work working (this session - never touched)
|
state.json mode, active preset, and the exact env keys last written
|
||||||
631644 pts/1 /home/smoido/Projects/api
|
presets/*.json provider + model maps
|
||||||
|
vault/*.cred DPAPI-encrypted keys (openrouter, zai, ...)
|
||||||
|
backups/ settings.json snapshots + removed env-var values
|
||||||
|
bin/claude-key-helper.cmd apiKeyHelper shim
|
||||||
|
~/.local/bin/claude-mode.cmd PATH entry point (works from any shell)
|
||||||
|
~/Documents/WindowsPowerShell/profile.ps1
|
||||||
|
claude-mode + claude functions, between markers
|
||||||
```
|
```
|
||||||
|
|
||||||
Sessions are found through `/proc/<pid>/exe`, which on Linux resolves to the
|
Linux / macOS: the same tree, with `bin/claude-mode` + `bin/claude-key-helper.sh`,
|
||||||
real `claude` binary — a process-name match would sweep up every shell that
|
`bin/cm-json.py`, `bin/cm-vault.sh`, the symlink `~/.local/bin/claude-mode`, and
|
||||||
merely mentions claude on its command line, including the one this is running
|
the wrapper block in `~/.bashrc` / `~/.zshrc`.
|
||||||
from. Two things are then filtered out:
|
|
||||||
|
|
||||||
- **The calling session.** Killing the session that asked for the kill is not a
|
`state.json` records which env keys the last switch actually wrote, so a custom
|
||||||
thing anyone means, so it is listed and never signalled.
|
`extraEnv` key (Z.AI's timeouts, LM Studio's attribution header) is removed when
|
||||||
- **Forks of a session.** A busy session spawns children off its own binary,
|
you switch away — even though no other preset knows that key exists.
|
||||||
and they inherit the same `exe`. Without excluding anything whose parent is
|
|
||||||
itself claude, the count climbs and falls with how hard the machine is
|
|
||||||
thinking — it read 2, 5, 11 and 40 on the same two sessions before this. A
|
|
||||||
real session's parent is a terminal.
|
|
||||||
|
|
||||||
`working` is a sampled-CPU heuristic — two reads of `utime + stime` 300ms apart
|
## Linux / Omarchy
|
||||||
— so it is a good guess about which session is mid-turn, not a promise.
|
|
||||||
|
|
||||||
`--stop` sends SIGTERM (never SIGKILL; Claude Code writes out its transcript on
|
The `linux/` tree is a POSIX port of the same design: one write to
|
||||||
the way down). `--restart` stops each session and reopens it — re-running the
|
`~/.claude/settings.json`, secrets kept out of it, presets shared verbatim with
|
||||||
parent terminal's own command line where there is one, so the same terminal,
|
the Windows build. Install is [up top](#linux--macos).
|
||||||
flags and directory come back, and falling back to a fresh terminal in the
|
|
||||||
session's directory otherwise. Interactive runs confirm first; `--yes` is for
|
### The menu follows your desktop theme
|
||||||
callers that have already asked, and `--dry-run` prints the plan and touches
|
|
||||||
nothing.
|
The sixteen ANSI colour slots carry no guarantee about relative brightness, and
|
||||||
|
monochrome themes exploit that. Under Omarchy's Solitude, slot 36 — headings —
|
||||||
|
resolves to `#707070` and slot 31 — `FAIL` — to `#565d60`. Against `#cacccc`
|
||||||
|
body text on a `#101315` ground that is 3.8:1 and 2.8:1 where the body text is
|
||||||
|
11.6:1, so headings render as fine print and an error becomes the quietest
|
||||||
|
thing on screen. Exactly backwards.
|
||||||
|
|
||||||
|
So when Omarchy is present, the palette is derived from the theme it publishes
|
||||||
|
at `~/.local/state/omarchy/current/theme/colors.toml` instead. Every role is
|
||||||
|
measured against the background it will actually be drawn on and lifted toward
|
||||||
|
the foreground when it falls short, which keeps hue where the theme has any and
|
||||||
|
falls back to weight where it does not:
|
||||||
|
|
||||||
|
| role | before (Solitude) | after |
|
||||||
|
|---|---|---|
|
||||||
|
| heading | 3.8:1 | 9.4:1 |
|
||||||
|
| `FAIL` | 2.8:1 | 5.2:1 |
|
||||||
|
| help text | 2.2:1 | 2.2:1 (recessive on purpose, floored) |
|
||||||
|
|
||||||
|
Light themes are handled by the same arithmetic — `mode` in `colors.toml` is
|
||||||
|
authoritative, so `catppuccin-latte` and `flexoki-light` keep their accents
|
||||||
|
rather than washing out.
|
||||||
|
|
||||||
|
Overrides: `CLAUDE_MODE_THEME=/path/to/colors.toml` points it elsewhere,
|
||||||
|
`NO_COLOR` turns it off. Without Omarchy, or on a terminal that cannot do
|
||||||
|
truecolor, it falls back to the ANSI slots with the two roles the slots get
|
||||||
|
wrong corrected — bright red for `FAIL`, bold on headings.
|
||||||
|
|
||||||
|
### `claude-mode health`
|
||||||
|
|
||||||
|
Rewrites `~/.claude-mode/health.json`, the machine-readable mirror of the
|
||||||
|
active configuration: mode, preset, model map, context window, key backend, and
|
||||||
|
the switchable preset catalogue. No key material. A switch and a `status` both
|
||||||
|
refresh it; the command exists for anything that wants to force it.
|
||||||
|
|
||||||
## The Omarchy bar widget
|
## The Omarchy bar widget
|
||||||
|
|
||||||
An icon in the Omarchy top bar showing which provider the next `claude` launch
|
An icon in the Omarchy top bar showing which provider the next `claude` launch
|
||||||
will use, and a panel that switches it without a terminal.
|
will use, and a panel that switches it without a terminal. Install is
|
||||||
|
[up top](#omarchy-bar-widget). Both install steps hot-reload, so nothing needs
|
||||||
```bash
|
restarting — except after a change to `Modes.js`, which the QML engine caches
|
||||||
bash omarchy/install.sh
|
for the life of the process as a `.pragma library` (`omarchy restart shell`).
|
||||||
```
|
|
||||||
|
|
||||||
Copies the plugin to `~/.config/omarchy/plugins/smoido.claude-mode/` and adds
|
|
||||||
its id to the bar layout in `~/.config/omarchy/shell.json` (backed up first).
|
|
||||||
Both hot-reload, so nothing needs restarting — except after a change to
|
|
||||||
`Modes.js`, which the QML engine caches for the life of the process as a
|
|
||||||
`.pragma library` (`omarchy restart shell`).
|
|
||||||
|
|
||||||
The icon is the mode, and it is the provider's own logo: the Claude burst, the
|
The icon is the mode, and it is the provider's own logo: the Claude burst, the
|
||||||
OpenRouter arrow, the Z.AI Z, the LM Studio mark. They are drawn as vector paths
|
OpenRouter arrow, the Z.AI Z, the LM Studio mark. They are drawn as vector paths
|
||||||
@@ -737,9 +768,9 @@ CLI does, and both can stop it:
|
|||||||
<mode>…* opens a terminal and runs the whole first-run flow there (a bar popup
|
<mode>…* opens a terminal and runs the whole first-run flow there (a bar popup
|
||||||
can host neither a hidden key prompt nor a filter-select model list), *Store
|
can host neither a hidden key prompt nor a filter-select model list), *Store
|
||||||
the key…* opens a terminal for just the prompt, *Check again* re-runs the
|
the key…* opens a terminal for just the prompt, *Check again* re-runs the
|
||||||
preflight, and *Server settings…* opens the form below. The gear on any LM Studio preset row opens the same form without
|
preflight, and *Server settings…* opens the form below. The gear on any LM
|
||||||
waiting for a failure — per row, because two LM Studio presets can point at
|
Studio preset row opens the same form without waiting for a failure — per
|
||||||
two different machines.
|
row, because two LM Studio presets can point at two different machines.
|
||||||
|
|
||||||
The form holds the base URL, a *Use local default* reset, and a switch for
|
The form holds the base URL, a *Use local default* reset, and a switch for
|
||||||
whether that server needs an API key. Saving rewrites the preset and drops
|
whether that server needs an API key. Saving rewrites the preset and drops
|
||||||
@@ -749,6 +780,7 @@ CLI does, and both can stop it:
|
|||||||
*Switch and close*, *Switch only*, or *Cancel*. The session action is applied
|
*Switch and close*, *Switch only*, or *Cancel*. The session action is applied
|
||||||
strictly **after** the write — restarting first would only bring them back up
|
strictly **after** the write — restarting first would only bring them back up
|
||||||
on the provider you just left.
|
on the provider you just left.
|
||||||
|
|
||||||
- **Right-click** switches straight back to Anthropic.
|
- **Right-click** switches straight back to Anthropic.
|
||||||
- **Middle-click** re-reads state.
|
- **Middle-click** re-reads state.
|
||||||
- **Hover** for mode, preset, and the opus/sonnet mapping.
|
- **Hover** for mode, preset, and the opus/sonnet mapping.
|
||||||
@@ -771,18 +803,28 @@ Placement is `right`, before `omarchy.agents`; override with `CM_BAR_SECTION`
|
|||||||
and `CM_BAR_BEFORE` when installing. Moving it later is a normal
|
and `CM_BAR_BEFORE` when installing. Moving it later is a normal
|
||||||
`omarchy bar move smoido.claude-mode --section <section>`.
|
`omarchy bar move smoido.claude-mode --section <section>`.
|
||||||
|
|
||||||
Uninstall:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rm -rf ~/.config/omarchy/plugins/smoido.claude-mode
|
|
||||||
# then remove the {"id": "smoido.claude-mode"} entry from ~/.config/omarchy/shell.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Uninstall
|
## Uninstall
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
claude-mode anthropic # clean settings.json first
|
claude-mode anthropic # clean settings.json first
|
||||||
Remove-Item ~\.claude-mode -Recurse -Force
|
Remove-Item ~\.claude-mode -Recurse -Force
|
||||||
Remove-Item ~\.local\bin\claude-mode.cmd
|
Remove-Item ~\.local\bin\claude-mode.cmd
|
||||||
# then delete the block between the >>> claude-mode >>> markers in profile.ps1
|
# then delete the block between the >>> claude-mode >>> markers in profile.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Linux / macOS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude-mode anthropic # clean settings.json first
|
||||||
|
rm -rf ~/.claude-mode ~/.local/bin/claude-mode
|
||||||
|
# then delete the block between the >>> claude-mode >>> markers in ~/.bashrc / ~/.zshrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Bar widget:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf ~/.config/omarchy/plugins/smoido.claude-mode
|
||||||
|
# then remove the {"id": "smoido.claude-mode"} entry from ~/.config/omarchy/shell.json
|
||||||
|
```
|
||||||
|
|||||||
+15
-1
@@ -20,7 +20,6 @@ for arg in "$@"; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
ROOT="${CM_ROOT:-$HOME/.claude-mode}"
|
ROOT="${CM_ROOT:-$HOME/.claude-mode}"
|
||||||
BINDIR="$HOME/.local/bin"
|
BINDIR="$HOME/.local/bin"
|
||||||
|
|
||||||
@@ -28,6 +27,21 @@ green() { printf ' \033[32mok \033[0m %s\n' "$*"; }
|
|||||||
warn() { printf ' \033[33mwarn\033[0m %s\n' "$*"; }
|
warn() { printf ' \033[33mwarn\033[0m %s\n' "$*"; }
|
||||||
fail() { printf ' \033[31mFAIL\033[0m %s\n' "$*"; }
|
fail() { printf ' \033[31mFAIL\033[0m %s\n' "$*"; }
|
||||||
|
|
||||||
|
# A checkout next to this script is the normal source. Piped in
|
||||||
|
# (curl ... | bash) there is no script file and no checkout, so fetch the
|
||||||
|
# repository archive and install from that instead.
|
||||||
|
SRC="$(cd "$(dirname "${BASH_SOURCE[0]:-''}")" 2>/dev/null && pwd)" || SRC=""
|
||||||
|
if [ ! -f "$SRC/claude-mode" ] || [ ! -f "$SRC/cm-json.py" ]; then
|
||||||
|
REPO_URL="${CM_REPO_URL:-https://git.nebulm.com/smoido/claude-mode}"
|
||||||
|
command -v curl >/dev/null 2>&1 || { fail 'curl required to fetch the source'; exit 1; }
|
||||||
|
TMP="$(mktemp -d "${TMPDIR:-/tmp}/claude-mode-src.XXXXXX")"
|
||||||
|
printf 'no local checkout found - fetching source from %s\n' "$REPO_URL"
|
||||||
|
curl -fsSL "$REPO_URL/archive/master.tar.gz" | tar -xz -C "$TMP"
|
||||||
|
EXTRACTED="$(find "$TMP" -type f -name claude-mode -print -quit)"
|
||||||
|
[ -n "$EXTRACTED" ] || { fail 'archive did not contain linux/claude-mode - repo layout changed?'; exit 1; }
|
||||||
|
SRC="$(dirname "$EXTRACTED")"
|
||||||
|
fi
|
||||||
|
|
||||||
printf '\ninstalling claude-mode -> %s\n' "$ROOT"
|
printf '\ninstalling claude-mode -> %s\n' "$ROOT"
|
||||||
|
|
||||||
# --- preflight -------------------------------------------------------------
|
# --- preflight -------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user