Files
claude-mode/docs/architecture.md
T
smoidoandClaude Opus 5 b514e00745 README down to what a user needs; the rest into docs/
The README had grown to 1,010 lines of user docs and design notes in one file,
and had gone stale: it still showed the old numbered menu, set up only three
providers, and listed preflight checks and a file layout that predate the last
three releases. It now carries install, first run, the full command
reference, the providers at a glance, troubleshooting and a docs index.

docs/:
- providers.md    presets, defaults, the model cache, context windows, each
                  provider (OpenRouter's cost guard and guardrail check are
                  written up for the first time), adding a provider
- live-sessions.md  what a switch does to running sessions, and repair
- design.md       why settings.json, why keys stay out of it (and the vault
                  per platform), the preflight checks as they are now
- bar-widget.md   the widget as it is now: providers from health.json, every
                  server provider's settings, the restart after an upgrade
- architecture.md the pieces, every file on disk and who writes it, the
                  contracts between them, where to change what
- development.md  running the tests, the conventions the code follows,
                  working on the widget, releasing
CONTRIBUTING.md points at it.

Also:
- The per-project session listing used awk, which the CLI avoids because it
  is missing from minimal images; it uses the script's own TSV helpers now,
  and tests/static.sh fails on any awk in the CLI.
- tests/static.sh checks every relative Markdown link and #anchor.
- A unit test pins the managed env keys between cm-json.py and
  claude-mode.ps1, which only a comment kept in step before.
- test_sessions covers the per-project listing, which nothing ran.

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

7.5 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/cm-json.py, bin/cm-vault.sh, bin/claude-key-helper.sh the installer — (Windows: claude-mode.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, install.ps1       the Windows build and its installer
bin/                               the Windows key helper
linux/                             the POSIX port: claude-mode, cm-json.py, cm-vault.sh,
                                   claude-key-helper.sh, install.sh, bootstrap.sh
omarchy/                           the bar widget and its installer
providers.json, presets/           shared by both builds, byte for byte
scripts/                           test.sh, bump-version.sh, build-package.ps1
tests/                             static, python, cli and windows suites
docs/                              this

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 Set-ClaudeMode, $script:BaseManagedEnvKeys
what refuses a switch linux/claude-mode cm_preflight Set-ClaudeMode guards
how a model list is fetched provider_catalogue + a parser in cm-json.py Get-ProviderCatalogue
a doctor check cmd_doctor, doctor_* Invoke-Doctor, Test-PresetCatalogue, Test-OllamaContext
setup cmd_setup, setup_server, setup_models
session detection and repair cmd_sessions, cmd_repair_session; cm-json.py cmd_scan_sessions, cmd_repair_session
key storage cm-vault.sh the DPAPI functions in claude-mode.ps1
the bar icon and state omarchy/smoido.claude-mode/BarWidget.qml
the panel omarchy/smoido.claude-mode/Panel.qml