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
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# linux/install.sh, run for real into a sandbox home: every file lands, the
# ~/.local/bin symlink runs (which exercises the CLI finding its lib/ beside
# itself through a symlink), re-running is a safe upgrade.
. "$(dirname "$0")/../lib.sh"
export HOME="$SB/ihome"
export CM_ROOT="$HOME/.claude-mode"
mkdir -p "$HOME"
: > "$HOME/.bashrc"
install_() { OUT="$(bash "$REPO/linux/install.sh" --skip-key-prompt 2>&1)"; RC=$?; OUT="$(printf '%s' "$OUT" | sed "s/${ESC}\[[0-9;]*m//g")"; }
install_
expect_rc 0 'the installer succeeds'
for f in bin/claude-mode bin/cm-json.py bin/cm-vault.sh bin/claude-key-helper.sh \
bin/lib/core.sh bin/lib/repair.sh providers.json VERSION presets/ollama.json health.json; do
expect_file "$CM_ROOT/$f" "installs $f"
done
expect_eq "$(readlink "$HOME/.local/bin/claude-mode")" "$CM_ROOT/bin/claude-mode" 'links the entry point'
expect_eq "$(grep -c '>>> claude-mode >>>' "$HOME/.bashrc")" 1 'adds the shell block'
OUT="$("$HOME/.local/bin/claude-mode" presets 2>&1)"
expect_out 'ollama' 'runs through the symlink, loading its lib/'
python3 "$CM_ROOT/bin/cm-json.py" set-tier "$CM_ROOT/presets/default.json" opus my/model
: > "$CM_ROOT/bin/lib/stale.sh"
install_
expect_rc 0 're-running the installer succeeds'
expect_eq "$(jget "$CM_ROOT/presets/default.json" models.opus)" my/model 'and keeps an edited preset'
expect_no_file "$CM_ROOT/bin/lib/stale.sh" 'and drops a module the source no longer has'
expect_eq "$(grep -c '>>> claude-mode >>>' "$HOME/.bashrc")" 1 'and does not add the block twice'
finish