Files
claude-mode/docs/architecture.md
T
smoidoandClaude Opus 5 4091746d4e Split the Windows script into modules; drop the Arkylx Index pieces
claude-mode.ps1 was 2,407 lines. It is now 153: the help block, parameters,
paths, the managed-key list, a loader, and the dispatch. The rest moved,
verbatim, into eleven files under lib/ - providers, output, files, core,
vault, switch, guards, health, catalogue, commands, menu - dot-sourced into
the script's scope in their original order, with the same check as the bash
split that every original line landed in exactly one file. Inside a module
$PSScriptRoot is lib\, so the one path beside the main script
(providers.json) now goes through $script:Here.

install.ps1 ships lib\, clearing old modules first. The Windows suite
parses every module and install.ps1 (36 checks, all green on Windows
PowerShell 5.1); install.ps1 is parsed but never run, since it edits the
real profile and User PATH.

linux/bootstrap.sh and scripts/build-package.ps1 existed only to build and
serve packages for the Arkylx Index. Both installers fetch the repository's
own archive, so a push to master is the release; the two scripts, the dist/
ignore and their mentions in the docs are gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 02:06:45 +03:00

10 KiB

Architecture

How the pieces of claude-mode fit together, what each file on disk is for, and where to go to change something. Why it is shaped this way is in design.md; how to work on it, in development.md.

The pieces

flowchart LR
    user([you]) --> cli["claude-mode<br/>(bash on Linux/macOS,<br/>PowerShell on Windows)"]
    widget["bar widget<br/>(Omarchy, QML)"] -- runs --> cli
    cli -- all JSON through --> engine["cm-json.py<br/>(POSIX only)"]
    engine --> providers[(providers.json)]
    engine --> presets[(presets/*.json)]
    cli -- a switch writes --> settings[(~/.claude/settings.json)]
    cli --> state[(state.json)]
    cli --> health[(health.json)]
    claude["Claude Code"] -- reads at startup --> settings
    claude -- runs on a timer --> helper["key helper"]
    helper --> state
    helper --> vault[(vault)]
    widget -- watches --> health
    widget -- reads --> cache[(models-cache.json)]
  • The CLI does everything that changes state. The POSIX port is bash for control flow and terminal UI, with every piece of JSON handled by cm-json.py — bash only ever sees flat lines of text. The Windows build is one PowerShell script that does the same with ConvertFrom-Json.
  • A switch rewrites the managed keys in ~/.claude/settings.json (an env block and apiKeyHelper) and records what it wrote in state.json. Claude Code reads settings.json at startup, from every launch context.
  • The key helper is what apiKeyHelper runs. It prints the active preset's key from the vault and nothing else, so no key ever sits in settings.json. Claude Code re-runs it on a timer, in every live session.
  • The bar widget never reads config itself. It watches health.json, which the CLI rewrites on every change, and runs the CLI for anything it does.
  • providers.json describes every gateway provider as data, read by all three (the widget through health.json). Behaviour that differs in kind is chosen by name from it — see providers.md.

Files on disk

Under ~/.claude-mode/:

file written by read by
bin/claude-mode, bin/lib/*.sh, bin/cm-json.py, bin/cm-vault.sh, bin/claude-key-helper.sh the installer — (Windows: claude-mode.ps1 and lib\*.ps1 at the top, bin\claude-key-helper.ps1 + .cmd)
providers.json the installer, every time the CLI, the key helper's engine, the widget (via health.json)
presets/*.json the installer seeds them; preset, setup, the panel edit them everything
state.json a switch; preset rename of the active preset the CLI, the key helper, the widget
defaults.json preset default (and rename/rm keep it right) the CLI; published in health.json
health.json a switch, status, doctor, preset edits, health the widget
models-cache.json every catalogue fetch the panel's model picker
ignored-sessions.json repair-session --ignore / --unignore the session scan
vault/ set-key (file backend; DPAPI on Windows) the key helper
backups/ a switch, before it writes settings.json (last 20 kept) you, if needed
VERSION the installer the CLI, stamped into health.json

Outside it: ~/.claude/settings.json (a switch writes the managed keys and never anything else), ~/.claude/projects/*/*.jsonl (Claude Code's transcripts, which repair-session reads and repairs), and on Omarchy ~/.config/omarchy/plugins/smoido.claude-mode/ (the widget).

The repository

claude-mode.ps1, lib/*.ps1         the Windows build: settings + dispatch, and its modules
install.ps1, profile-snippet.ps1   its installer, and the block it adds to the PowerShell profile
bin/                               the Windows key helper
linux/                             the POSIX port: claude-mode (settings + dispatch),
                                   lib/*.sh (its modules), cm-json.py, cm-vault.sh,
                                   claude-key-helper.sh, install.sh
omarchy/                           the bar widget and its installer
providers.json, presets/           shared by both builds, byte for byte
scripts/                           test.sh, bump-version.sh
tests/                             static, python, cli and windows suites
docs/                              this

The POSIX CLI's modules

linux/claude-mode holds the paths and flags, loads these in order, and dispatches the command line. It finds them — and cm-json.py and cm-vault.sh — beside itself, following the ~/.local/bin symlink, so a checkout runs its own code rather than the installed version's.

linux/lib/ what is in it
output.sh the theme-derived colour palette, ok/warn/err, the banner, usage
core.sh state and preset helpers, the provider table (prov_field, provider_resolve), resolve_preset
preflight.sh cm_preflight, the server probe, claude-mode preflight
sessions.sh finding running sessions, claude-mode sessions, and the question a switch asks about them
switch.sh set_mode, settings backups, stray env vars, health.json, the guardrail check, claude-mode repair
catalogue.sh provider_catalogue and the model cache
commands.sh status, presets, models, set-key
doctor.sh doctor and its per-provider checks
presets.sh claude-mode preset … and re-applying the preset in use
menu.sh the interactive menu and its pickers
setup.sh claude-mode setup
repair.sh claude-mode repair-session and dismissals

The Windows script's modules

claude-mode.ps1 holds the help block, the parameters, the paths and flags and the managed-key list, then dot-sources these from lib\ beside it, in order, and dispatches. Inside a dot-sourced file $PSScriptRoot is lib\, so paths beside the main script go through $script:Here.

lib/ what is in it
providers.ps1 reading providers.json; Get-Provider, Resolve-ProviderId, Test-ProviderDoctor
output.ps1 Write-Ok/Write-Warn2/Write-Err2, mode colours, the banner, Show-Usage
files.ps1 JSON read/write (5.1 has no -AsHashtable), Protect-FileAcl
core.ps1 state.json, presets, Resolve-PresetForProvider
vault.ps1 the DPAPI vault, Get-PresetAuth
switch.ps1 Set-ClaudeMode, settings backups, the apiKeyHelper command line
guards.ps1 the cost guard, the OpenRouter guardrail check, stale cached model ids, repair
health.ps1 health.json; persistent environment variables that would override a switch
catalogue.ps1 Get-ProviderCatalogue, LM Studio's model list and template check, the doctor model and context checks
commands.ps1 status, presets, preset, models, doctor
menu.ps1 the interactive menu, its pickers, interactive preset editing, New-PresetScaffold

Contracts between the pieces

These are the interfaces one piece relies on another to keep. Each is pinned by a test.

  • providers.json — its shape and the values each field may take (providers.md); tests/static.sh enforces them.
  • provider-tsvcm-json.py hands bash one tab-separated row per provider, and bash addresses the columns by number (bash 3.2 has no associative arrays). The column order is PROVIDER_TSV in cm-json.py, mirrored in a comment in linux/claude-mode: append only. Pinned by tests/python.
  • The managed env keys — the keys a switch owns and clears: BASE_MANAGED in cm-json.py and $script:BaseManagedEnvKeys in claude-mode.ps1 must list the same keys. Pinned by tests/python.
  • health.json — what the widget reads: mode, preset, version, models, contextTokens, keyBackend, presets (the catalogue), providers (id, title, blurb, logo, logoScale, serverEditable, perServerCatalogue, defaultKeyRef, defaultBaseUrl, serverHint, keyOptional), defaultPresetFor, defaultPresetChosen. No key material, ever.
  • claude-mode preflight <mode> [preset] — JSON the widget acts on: ok, code (ok, needs-setup, no-models, no-url, missing-key, helper-missing, server-auth, server-wrong, server-unreachable, no-preset, provider-mismatch), title, detail, remedy (a command), and remedyKind, which picks the panel's button (setup, set-key, edit-preset, set-url, needs-key, start-server).
  • claude-mode repair-session --json — the scan: broken[] and ignored[] (each with sessionId, project, path, dropLines, providers, mtime, and reason for the ignored), count, ignoredCount, maxAgeDays.
  • models-cache.jsonproviders.<id>fetchedAt, ok, failedAt, baseUrl (per-server providers), models[] (id, and contextTokens, priceIn, priceOut, state, note where known). No keys.
  • The key helper — prints the key and nothing else on stdout; every failure explains itself on stderr, because Claude Code shows that under /status. It reads the preset in one open and re-reads state.json once on a miss, so a rename of the active preset cannot catch it mid-lookup.

Where to change what

to change POSIX Windows
a provider's endpoint, auth template, setup, checks or look providers.json the same file
a shipped preset presets/*.json the same files
what a switch writes to settings.json cm-json.py cmd_apply, BASE_MANAGED lib/switch.ps1 Set-ClaudeMode; $script:BaseManagedEnvKeys in claude-mode.ps1
a command's arguments the dispatch at the end of linux/claude-mode the dispatch at the end of claude-mode.ps1
what refuses a switch linux/lib/preflight.sh cm_preflight the guards in Set-ClaudeMode
how a model list is fetched linux/lib/catalogue.sh provider_catalogue + a parser in cm-json.py lib/catalogue.ps1 Get-ProviderCatalogue
a doctor check linux/lib/doctor.sh lib/commands.ps1 Invoke-Doctor, lib/catalogue.ps1
setup linux/lib/setup.sh
session detection linux/lib/sessions.sh
session repair linux/lib/repair.sh; cm-json.py cmd_scan_sessions, cmd_repair_session
key storage linux/cm-vault.sh lib/vault.ps1
the bar icon and state omarchy/smoido.claude-mode/BarWidget.qml
the panel omarchy/smoido.claude-mode/Panel.qml