Split the POSIX CLI into modules

linux/claude-mode was 3,035 lines. It is now 177: the paths and flags, a
loader, and the command dispatch. Everything else moved, verbatim, into
twelve files under linux/lib/, one per concern - output, core, preflight,
sessions, switch, catalogue, commands, doctor, presets, menu, setup, repair.
The move was done by line range with a check that every original line landed
in exactly one file; the only thing that changed place is the switch's
running-sessions question, which now sits with session detection.

The CLI finds lib/, 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 (it used to mix the two). The key helper path written
into settings.json is still the installed one.

linux/install.sh ships bin/lib/, clearing old modules first so a removed one
cannot linger. The package build copies linux/ recursively, which a flat copy
would not.

tests/cli/test_install.sh runs the real installer into a sandbox home: every
file lands, the symlink runs, a reinstall keeps edited presets and drops a
stale module. docs/architecture.md lists the modules.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
smoido
2026-09-15 02:00:10 +03:00
co-authored by Claude Opus 5
parent b514e00745
commit 441166d003
17 changed files with 3063 additions and 2905 deletions
+70
View File
@@ -0,0 +1,70 @@
# shellcheck shell=bash
# linux/lib/catalogue.sh - fetching a provider's model list, and the cache every fetch leaves.
#
# Part of the claude-mode CLI: sourced by linux/claude-mode, which sets the
# CM_* paths, PY and JSON used here. Not meant to run on its own.
# ---------------------------------------------------------------------------
# Provider catalogues
# ---------------------------------------------------------------------------
# Every fetch leaves a copy behind for a reader that cannot afford the round
# trip - the bar panel's model picker. It is written here, in the only places a
# catalogue is ever fetched, so every command that already pays for the network
# (models, doctor, setup, the menu's picker) keeps it fresh at no extra cost.
cm_cache_catalogue() {
"$PY" "$JSON" cache-models "$CM_MODELS_CACHE" "$1" "$2" "${3:-}" >/dev/null 2>&1 || true
}
# One catalogue fetch, for any provider: fetched according to its
# catalogue.kind, parsed to TSV, and left in the cache for the panel. Prints
# the TSV; the status says whether the fetch itself worked. TSV columns by kind:
#
# openrouter id ctx $in $out lmstudio id state ctx
# ollama id params quant family openai id
# static id note (a list kept in providers.json)
provider_catalogue() {
local pid="$1" base="${2%/}" token="${3:-}" out rc
case "$(prov_field "$pid" 10)" in
openrouter)
out="$(curl -fsS --max-time 30 https://openrouter.ai/api/v1/models 2>/dev/null \
| "$PY" "$JSON" or-models 2>/dev/null)" ;;
lmstudio)
out="$(curl -fsS --max-time 10 ${token:+-H "Authorization: Bearer $token"} \
"$base/api/v0/models" 2>/dev/null | "$PY" "$JSON" lms-models 2>/dev/null)" ;;
ollama)
out="$(curl -fsS --max-time 10 ${token:+-H "Authorization: Bearer $token"} \
"$base/api/tags" 2>/dev/null | "$PY" "$JSON" ollama-models 2>/dev/null)" ;;
openai)
# Both header styles: a proxy in front of Anthropic wants
# x-api-key, one in front of anything else wants Bearer.
out="$(curl -fsS --max-time 15 ${token:+-H "Authorization: Bearer $token"} \
${token:+-H "x-api-key: $token"} -H 'anthropic-version: 2023-06-01' \
"$base/v1/models" 2>/dev/null | "$PY" "$JSON" openai-models 2>/dev/null)" ;;
static)
out="$("$PY" "$JSON" provider-static "$pid" 2>/dev/null)" ;;
*) return 1 ;;
esac
rc=$?
cm_cache_catalogue "$pid" "$([ "$rc" -eq 0 ] && echo 1 || echo 0)" "$base" <<<"$out"
[ -n "$out" ] && printf '%s\n' "$out"
return "$rc"
}
# The credential a preset would send. Empty when there is nothing to send.
cm_preset_token() {
local pf="$1" am ref
am="$(jget "$pf" auth.mode)"; [ -z "$am" ] && am=vault
if [ "$am" = "vault" ]; then
ref="$(jget "$pf" auth.keyRef)"; [ -z "$ref" ] && ref=openrouter
cm_vault_get "$ref" 2>/dev/null || true
else
jget "$pf" auth.token
fi
}
# The token passed to provider_catalogue is not optional decoration. A local
# server with authentication switched on answers its model list with 401 like
# anything else, so without it the catalogue comes back empty and every caller
# silently believes the server has no models - on exactly the setups that need
# the list most.