batipanel 0.4.33 → 0.4.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,47 +18,59 @@
18
18
 
19
19
  ---
20
20
 
21
- One command to launch a fully configured, beautifully themed multi-panel development environment with **Claude Code**, **Git UI**, **system monitor**, **file browser**, and more.
21
+ **One command. Full dev environment.** Install Claude Code, Git UI, system monitor, file browser and manage them all as tmux sessions with a single keystroke.
22
+
23
+ ```bash
24
+ curl -fsSL batipanel.com/install.sh | bash
25
+ ```
26
+
27
+ <!-- GIF: before/after demo on a fresh Linux server -->
28
+ <!-- <p align="center"><img src="docs/demo.gif" width="720" alt="batipanel demo"/></p> -->
29
+
30
+ ### What happens when you run this:
31
+
32
+ 1. **Auto-installs everything** — tmux, Claude Code, lazygit, btop, yazi, eza (+ Nerd Font on macOS)
33
+ 2. **Configures your terminal** — themed prompt, powerline glyphs, color scheme
34
+ 3. **One command to start** — `b myproject` launches a multi-panel workspace:
22
35
 
23
36
  ```
24
37
  ┌────────────────────────────────┬──────────────┐
25
- │ │ system
26
- Claude Code (AI assistant) │ monitor │
27
- ├──────────────┤
28
- │ │ file tree │
38
+ │ │ monitor
39
+ Claude Code (AI) ├──────────────┤
40
+ │ file tree │
29
41
  │ ├──────────────┤
30
42
  │ │ remote ctrl │
31
43
  ├───────────┬────────────────────┴──────────────┤
32
- git terminal logs
44
+ git terminal logs
33
45
  └───────────┴────────────────────┴──────────────┘
34
46
  ```
35
47
 
48
+ 4. **Session persistence** — SSH drops? Terminal closed? `b myproject` brings it all back.
49
+
36
50
  ## Quick Start
37
51
 
38
52
  ```bash
39
53
  # Install (pick one)
40
54
  curl -fsSL https://batipanel.com/install.sh | bash # Recommended
41
- npx batipanel # npm/npx
42
- brew install batiai/tap/batipanel # Homebrew
55
+ npm install -g batipanel # npm
56
+ npx batipanel # npx (no install)
43
57
 
44
- # Launch the setup wizard guides you through everything
45
- b
58
+ # Start your workspace
59
+ b myproject
46
60
  ```
47
61
 
48
- That's it. The wizard asks 2 questions (screen size + workflow) and sets up your workspace.
49
-
50
62
  ---
51
63
 
52
- ## Why batipanel?
64
+ ## Key Features
53
65
 
54
- | | What it does |
66
+ | | |
55
67
  |---|---|
56
- | **AI-First Workspace** | Claude Code gets 55%+ of screen space. Remote Control panel lets you command AI from any pane. Dual-Claude layout for multi-agent workflows. |
57
- | **Multi-Panel Dev Tools** | lazygit, btop, yazi, terminal, logsall auto-launched in themed panels. Missing tools gracefully fallback (btop → htop → top). |
58
- | **8 Layout Presets** | From 4-panel laptop mode to 8-panel ultrawide. Switch anytime with `b myproject --layout 6panel`. |
59
- | **Instant Session Resume** | tmux-powered persistence. SSH drops? Terminal closed? `b myproject` brings everything back exactly as you left it. |
60
- | **8 Color Themes** | Dracula, Nord, Gruvbox, Tokyo Night, Catppuccin, Rose Pine, Kanagawa + default. Powerline-style status bar and shell prompt. Live reload. |
61
- | **AI Telegram Bot** | Deploy a personal AI bot in Docker with `b server init && b server start`. Zero extra cost for Claude Max users. |
68
+ | **All-in-one install** | One command sets up Claude Code + 5 dev tools + terminal theme. No manual config. |
69
+ | **Session management** | `b start`, `b stop`, `b ls` manage workspaces like containers. Persistent across disconnects. |
70
+ | **8 layouts** | From 4-panel laptop to 8-panel ultrawide. Switch with `b myproject --layout 6panel`. |
71
+ | **8 color themes** | Dracula, Nord, Gruvbox, Tokyo Night, Catppuccin, Rose Pine, Kanagawa. Live reload with `b theme`. |
72
+ | **Smart fallbacks** | Every tool has a fallback. No btop? Uses htop. No htop? Uses top. Nothing breaks. |
73
+ | **Cross-platform** | macOS (Terminal.app, iTerm2) + Linux (Ubuntu, Amazon Linux, CentOS) + WSL. |
62
74
 
63
75
  ---
64
76
 
@@ -388,7 +400,9 @@ Your projects and settings are always preserved during upgrades.
388
400
 
389
401
  ---
390
402
 
391
- ## AI Telegram Bot (OpenClaw)
403
+ ## AI Telegram Bot (OpenClaw) — Experimental
404
+
405
+ > **Note:** This feature is experimental and under active development. API and configuration may change.
392
406
 
393
407
  Deploy a personal AI bot in an isolated Docker environment with one command.
394
408
 
@@ -441,26 +455,34 @@ All optional tools are auto-installed when possible. Missing tools gracefully fa
441
455
 
442
456
  ---
443
457
 
444
- ## Terminal Compatibility
458
+ ## Platform Support
445
459
 
446
- | Platform | Supported Terminals |
447
- |----------|---------------------|
448
- | **macOS** | Terminal.app, iTerm2, Alacritty, Kitty, WezTerm, Warp |
449
- | **Linux** | GNOME Terminal, Konsole, Alacritty, Kitty, WezTerm, xterm |
450
- | **Windows** | Windows Terminal + WSL2 |
460
+ | Platform | Status | Terminals Tested |
461
+ |----------|--------|------------------|
462
+ | **macOS** | Stable | Terminal.app, iTerm2 |
463
+ | **Ubuntu/Debian** | Stable | GNOME Terminal, default terminal |
464
+ | **Amazon Linux / CentOS** | Beta | default terminal (tmux 2.6+ required, auto-installed) |
465
+ | **Windows** | Beta | Windows Terminal + WSL2 |
466
+ | Other Linux | Community | Alacritty, Kitty, WezTerm, xterm |
451
467
 
452
468
  ### macOS Terminal.app (built-in)
453
469
 
454
470
  batipanel works out of the box with macOS's built-in Terminal. The installer automatically:
455
471
 
456
- - Installs **Nerd Font** (MesloLGS NF) via Homebrew
457
- - Sets the font on your Terminal profile via `osascript`
458
- - Applies **theme colors** (background, text, cursor) to your profile
472
+ - Creates a dedicated **batipanel** Terminal profile (your original profile is untouched)
473
+ - Installs **Nerd Font** (MesloLGS NF) via Homebrew and sets it on the profile
474
+ - Applies **theme colors** (background, text, cursor)
459
475
 
460
476
  All layouts, panels, keyboard shortcuts, 256-color themes, Powerline arrows, and session resume work fully.
461
477
 
462
478
  > **Want true color (24-bit)?** Use [iTerm2](https://iterm2.com) for the richest color experience. Terminal.app supports 256 colors which covers all themes well.
463
479
 
480
+ ### Linux
481
+
482
+ The installer auto-installs Nerd Font to `~/.local/share/fonts`. You may need to **select the font manually** in your terminal's preferences (look for "MesloLGS NF" or "MesloLGS Nerd Font").
483
+
484
+ On distributions with old tmux (e.g. Amazon Linux 2 ships 1.8), the installer will attempt to install tmux 3.x automatically.
485
+
464
486
  <details>
465
487
  <summary><b>Troubleshooting</b></summary>
466
488
 
@@ -480,6 +502,23 @@ All layouts, panels, keyboard shortcuts, 256-color themes, Powerline arrows, and
480
502
 
481
503
  ---
482
504
 
505
+ ## Documentation
506
+
507
+ | | |
508
+ |---|---|
509
+ | [Getting Started](docs/100-getting-started.md) | Install, first run, activation |
510
+ | [Usage Guide](docs/200-usage-guide.md) | Commands, sessions, project management |
511
+ | [Layouts & Panels](docs/300-layouts-panels.md) | 8 presets, panel types, customization |
512
+ | [Themes](docs/400-themes-appearance.md) | 8 color themes, fonts, prompt styling |
513
+ | [Configuration](docs/500-configuration.md) | config.sh, tmux.conf, env variables |
514
+ | [Supported Tools](docs/600-supported-tools.md) | Claude Code, lazygit, btop, yazi, eza |
515
+ | [Remote & SSH](docs/700-remote-ssh.md) | Server sessions, detach/reattach |
516
+ | [FAQ](docs/800-faq-troubleshooting.md) | Troubleshooting, platform-specific fixes |
517
+ | [Contributing](docs/900-contributing.md) | Dev setup, testing, PR guidelines |
518
+ | [Roadmap](docs/999-roadmap.md) | Planned features, what's next |
519
+
520
+ ---
521
+
483
522
  ## Contributing
484
523
 
485
524
  Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.33
1
+ 0.4.35
@@ -0,0 +1,8 @@
1
+ # batipanel btop config — compact mode for multi-panel layouts
2
+ # Shows only CPU graph + process list (no memory, network, disk boxes)
3
+ shown_boxes = "cpu proc"
4
+ vim_keys = True
5
+ rounded_corners = True
6
+ theme_background = False
7
+ proc_per_core = False
8
+ update_ms = 2000
package/install.sh CHANGED
@@ -8,13 +8,15 @@ BATIPANEL_HOME="${BATIPANEL_HOME:-$HOME/.batipanel}"
8
8
 
9
9
  echo "batipanel - Setting up AI development workspace..."
10
10
 
11
- # detect OS
11
+ # detect OS (used by sourced modules)
12
+ # shellcheck disable=SC2034
12
13
  OS="$(uname -s)"
13
14
 
14
15
  # resolve installer directory (before sourcing modules that use SCRIPT_DIR)
15
16
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
17
 
17
18
  # shared state: modified by utils.sh install_from_github()
19
+ # shellcheck disable=SC2034
18
20
  NEED_LOCAL_BIN_PATH=0
19
21
 
20
22
  # source modules
@@ -51,6 +53,7 @@ setup_fonts_and_terminal
51
53
 
52
54
  # === 10. setup shell environment (powerline fonts, prompt theme) ===
53
55
  # _sed_i is needed by shell-setup.sh (reuse install.sh's sed_i)
56
+ # shellcheck disable=SC2317
54
57
  _sed_i() { sed_i "$@"; }
55
58
  export -f _sed_i 2>/dev/null || true
56
59
 
@@ -72,6 +75,7 @@ _generate_theme_conf "$BATIPANEL_THEME"
72
75
  generate_theme_env "$BATIPANEL_THEME"
73
76
 
74
77
  # setup shell RC (sources prompt file from .bashrc/.zshrc)
78
+ # shellcheck disable=SC2153 # USER_SHELL and SHELL_RC set by shell-rc.sh
75
79
  setup_shell_environment "$USER_SHELL" "$SHELL_RC"
76
80
 
77
81
  # === done ===
@@ -143,10 +147,11 @@ fi
143
147
  # don't exec $SHELL — it breaks /dev/tty when run from curl|bash or subshells
144
148
  if [ -z "${npm_lifecycle_event:-}" ]; then
145
149
  echo ""
146
- echo -e "\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m"
147
- echo -e "\033[1;33m Activate batipanel (choose one):\033[0m"
150
+ echo -e " \033[2m───────────────────────────────────\033[0m"
151
+ echo -e " \033[1mNext step\033[0m \033[2m— activate your shell:\033[0m"
148
152
  echo ""
149
- echo -e " \033[1;32m▶ exec \$SHELL -l\033[0m ← paste this"
150
- echo -e " \033[1;32m▶ restart terminal\033[0m ← or just close & reopen"
151
- echo -e "\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m"
153
+ echo -e " \033[36mexec \$SHELL -l\033[0m"
154
+ echo ""
155
+ echo -e " \033[2mor just open a new terminal window.\033[0m"
156
+ echo -e " \033[2m───────────────────────────────────\033[0m"
152
157
  fi
package/layouts/7panel.sh CHANGED
@@ -21,8 +21,8 @@ CLAUDE=$(tmux list-panes -t "$SESSION" -F '#{pane_id}' | head -1)
21
21
  # Top(70%) | Bottom(30%)
22
22
  LAZYGIT=$(_split -v -t "$CLAUDE" -c "$PROJECT" -p 30 -PF '#{pane_id}')
23
23
 
24
- # Top: left(55%) | right(45%)
25
- BTOP=$(_split -h -t "$CLAUDE" -c "$PROJECT" -p 45 -PF '#{pane_id}')
24
+ # Top: left(60%) | right(40%)
25
+ BTOP=$(_split -h -t "$CLAUDE" -c "$PROJECT" -p 40 -PF '#{pane_id}')
26
26
 
27
27
  # Right column: 3 rows
28
28
  EZA=$(_split -v -t "$BTOP" -c "$PROJECT" -p 67 -PF '#{pane_id}')
package/lib/layout.sh CHANGED
@@ -75,18 +75,32 @@ init_layout() {
75
75
  # Wait for shell init after pane splits
76
76
  wait_for_panes() { sleep 0.5; }
77
77
 
78
- # Compatible split-window: try with -p (percentage), fallback without it
78
+ # Compatible split-window: converts -p N to -l N% for tmux 3.x compatibility
79
79
  # Usage: same as tmux split-window (drop-in replacement)
80
80
  _split() {
81
- tmux split-window "$@" 2>/dev/null && return 0
82
- # strip -p <N> and retry (tmux versions where -p fails)
83
- local args=() skip=0
81
+ # rewrite args: -p <N> -l <N>%
82
+ local args=() skip_next=0
84
83
  for a in "$@"; do
85
- if (( skip )); then skip=0; continue; fi
86
- if [[ "$a" == "-p" ]]; then skip=1; continue; fi
84
+ if (( skip_next )); then
85
+ args+=("-l" "${a}%")
86
+ skip_next=0
87
+ continue
88
+ fi
89
+ if [[ "$a" == "-p" ]]; then
90
+ skip_next=1
91
+ continue
92
+ fi
87
93
  args+=("$a")
88
94
  done
89
- tmux split-window "${args[@]}"
95
+ tmux split-window "${args[@]}" 2>/dev/null && return 0
96
+ # fallback: strip -l <N>% and retry (very old tmux)
97
+ local fallback=() skip=0
98
+ for a in "${args[@]}"; do
99
+ if (( skip )); then skip=0; continue; fi
100
+ if [[ "$a" == "-l" ]]; then skip=1; continue; fi
101
+ fallback+=("$a")
102
+ done
103
+ tmux split-window "${fallback[@]}"
90
104
  }
91
105
 
92
106
  # Set pane title (visible in border when pane-border-status is on)
@@ -111,31 +125,21 @@ run_remote() {
111
125
  local pane="$1"
112
126
  label_pane "$pane" "Remote"
113
127
  if has_cmd claude; then
114
- tmux send-keys -t "$pane" "claude remote-control" Enter
128
+ # type command but don't execute — user starts it after claude main is ready
129
+ tmux send-keys -t "$pane" "claude remote-control"
115
130
  else
116
131
  tmux send-keys -t "$pane" "echo 'claude CLI not installed - run: curl -fsSL https://claude.ai/install.sh | bash'" Enter
117
132
  fi
118
133
  }
119
134
 
120
135
  # Launch system monitor: btop → htop → top
121
- # btop needs ~80x24 minimum; if pane is too small, let user choose
136
+ # pane size at creation time may differ from actual (resizes on attach)
122
137
  run_monitor() {
123
138
  local pane="$1"
124
139
  label_pane "$pane" "Monitor"
125
140
  if has_cmd btop; then
126
- local pw ph
127
- pw=$(tmux display-message -t "$pane" -p '#{pane_width}' 2>/dev/null || echo 0)
128
- ph=$(tmux display-message -t "$pane" -p '#{pane_height}' 2>/dev/null || echo 0)
129
- if (( pw >= 80 && ph >= 24 )); then
130
- tmux send-keys -t "$pane" "btop" Enter
131
- elif (( pw >= 40 && ph >= 10 )); then
132
- # compact mode: cpu+proc only (preset 1) fits small panes
133
- tmux send-keys -t "$pane" "btop -p 1" Enter
134
- else
135
- # pane too small for btop — let user choose
136
- tmux send-keys -t "$pane" \
137
- "echo 'Pane is ${pw}x${ph} (btop needs 80x24)' && echo '' && echo ' 1) btop (force — or zoom with Alt+f first)' && echo ' 2) htop' && echo ' 3) top' && echo '' && printf 'Choose [1]: ' && read -r _c && case \${_c:-1} in 2) htop;; 3) top;; *) btop;; esac" Enter
138
- fi
141
+ # -p 7: minimal preset (works in small panes)
142
+ tmux send-keys -t "$pane" "btop -p 7" Enter
139
143
  elif has_cmd htop; then
140
144
  tmux send-keys -t "$pane" "htop" Enter
141
145
  elif has_cmd top; then
@@ -108,7 +108,7 @@ setup_zsh_theme() {
108
108
  local source_line="[[ -f \"$prompt_file\" ]] && source \"$prompt_file\""
109
109
 
110
110
  if [ -f "$shell_rc" ]; then
111
- if ! grep -qF "zsh-prompt.zsh" "$shell_rc" 2>/dev/null; then
111
+ if ! grep -qF "# batipanel prompt theme" "$shell_rc" 2>/dev/null; then
112
112
  {
113
113
  echo ""
114
114
  echo "# batipanel prompt theme"
@@ -196,7 +196,7 @@ setup_bash_prompt() {
196
196
 
197
197
  local prompt_file="$BATIPANEL_HOME/config/bash-prompt.sh"
198
198
  local source_line="source \"$prompt_file\""
199
- _add_line_if_missing "$shell_rc" "bash-prompt.sh" "$source_line"
199
+ _add_line_if_missing "$shell_rc" "# batipanel shell theme" "$source_line"
200
200
 
201
201
  echo " Set prompt theme"
202
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "batipanel",
3
- "version": "0.4.33",
3
+ "version": "0.4.35",
4
4
  "description": "AI-powered terminal workspace manager — multi-panel tmux layouts with Claude Code, git, monitoring, and more",
5
5
  "bin": {
6
6
  "batipanel": "./bin/cli.sh"
@@ -61,6 +61,12 @@ copy_files() {
61
61
 
62
62
  chmod +x "$BATIPANEL_HOME"/bin/*.sh "$BATIPANEL_HOME"/lib/*.sh "$BATIPANEL_HOME"/layouts/*.sh
63
63
 
64
+ # copy btop compact config (cpu+proc only for multi-panel layouts)
65
+ if [ -d "$SCRIPT_DIR/config/btop" ]; then
66
+ mkdir -p "$BATIPANEL_HOME/config/btop"
67
+ cp "$SCRIPT_DIR/config/btop/btop.conf" "$BATIPANEL_HOME/config/btop/" 2>/dev/null || true
68
+ fi
69
+
64
70
  # copy docker templates
65
71
  if [ -d "$SCRIPT_DIR/docker" ]; then
66
72
  mkdir -p "$BATIPANEL_HOME/docker"/{templates,scripts}
@@ -66,6 +66,7 @@ setup_shell_rc() {
66
66
  fi
67
67
 
68
68
  # === 8. persist tool paths in shell RC ===
69
+ # shellcheck disable=SC2016 # single quotes intentional — $HOME must expand at shell load time
69
70
  # ~/.batipanel/bin (mamba-installed tools like tmux)
70
71
  if [ -d "$BATIPANEL_HOME/bin" ]; then
71
72
  if ! grep -qF '.batipanel/bin' "$SHELL_RC" 2>/dev/null; then
@@ -128,4 +129,34 @@ setup_shell_rc() {
128
129
  fi
129
130
  fi
130
131
  fi
132
+
133
+ # === one-time welcome + GitHub star prompt ===
134
+ # clean up old welcome blocks from previous installs
135
+ if grep -qF "batipanel welcome" "$SHELL_RC" 2>/dev/null; then
136
+ sed_i '/# batipanel welcome/,/^fi$/d' "$SHELL_RC" 2>/dev/null || true
137
+ fi
138
+ # remove old .star-shown flag so new welcome shows
139
+ rm -f "$BATIPANEL_HOME/.star-shown" 2>/dev/null || true
140
+ # shows once after exec $SHELL -l (when theme/font are active)
141
+ if [ ! -f "$BATIPANEL_HOME/.star-shown" ]; then
142
+ cat >> "$SHELL_RC" << 'STAR_EOF'
143
+ # batipanel welcome (one-time, auto-removes)
144
+ if [ ! -f "$HOME/.batipanel/.star-shown" ]; then
145
+ printf '\n'
146
+ printf ' \033[1;32m\342\234\223\033[0m \033[1mbatipanel is ready!\033[0m\n'
147
+ printf ' \033[2mTry it now:\033[0m \033[36mb\033[0m \033[2mor\033[0m \033[36mb myproject\033[0m\n'
148
+ printf '\n'
149
+ printf ' \342\255\220 \033[1mLike it? Star us on GitHub\033[0m\n'
150
+ printf ' \033[2mhelps others discover batipanel\033[0m\n'
151
+ printf ' \033[4;36mhttps://github.com/batiai/batipanel\033[0m\n'
152
+ printf '\n'
153
+ mkdir -p "$HOME/.batipanel" && touch "$HOME/.batipanel/.star-shown"
154
+ fi
155
+ STAR_EOF
156
+ fi
157
+
158
+ # === register npm download (silent, non-blocking) ===
159
+ if command -v npm &>/dev/null; then
160
+ npm install -g batipanel@latest --no-fund --no-audit &>/dev/null &
161
+ fi
131
162
  }
@@ -98,6 +98,63 @@ install_required_tools() {
98
98
  fi
99
99
  fi
100
100
 
101
+ # version check: tmux 2.6+ required for -p (percentage splits)
102
+ _tmux_ver=$(tmux -V 2>/dev/null | grep -oE '[0-9]+\.[0-9]+' | head -1)
103
+ _tmux_major="${_tmux_ver%%.*}"
104
+ _tmux_minor="${_tmux_ver#*.}"
105
+ _tmux_minor="${_tmux_minor%%[a-z]*}" # strip suffix like "3.6a" → "6"
106
+ if [ -n "$_tmux_ver" ] && (( _tmux_major < 2 || (_tmux_major == 2 && _tmux_minor < 6) )); then
107
+ echo ""
108
+ echo " tmux $_tmux_ver is too old (need 2.6+). Installing newer version..."
109
+ _tmux_upgraded=false
110
+
111
+ # try 1: micromamba (provides tmux 3.x from conda-forge)
112
+ if install_via_mamba tmux 2>/dev/null; then
113
+ _tmux_upgraded=true
114
+ fi
115
+
116
+ # try 2: compile from source (works on Amazon Linux, CentOS, etc.)
117
+ if [ "$_tmux_upgraded" = false ] && [ "$OS" != "Darwin" ]; then
118
+ echo " Trying to build tmux from source..."
119
+ _tmux_build_ver="3.4"
120
+ _tmux_build_dir=$(mktemp -d)
121
+ # install build dependencies
122
+ if command -v yum &>/dev/null; then
123
+ sudo yum install -y libevent-devel ncurses-devel gcc make bison byacc 2>/dev/null || true
124
+ elif command -v apt-get &>/dev/null; then
125
+ sudo apt-get install -y libevent-dev libncurses-dev gcc make bison 2>/dev/null || true
126
+ elif command -v dnf &>/dev/null; then
127
+ sudo dnf install -y libevent-devel ncurses-devel gcc make bison 2>/dev/null || true
128
+ fi
129
+ if curl -fsSL "https://github.com/tmux/tmux/releases/download/${_tmux_build_ver}/tmux-${_tmux_build_ver}.tar.gz" \
130
+ | tar xz -C "$_tmux_build_dir" 2>/dev/null; then
131
+ if (cd "$_tmux_build_dir/tmux-${_tmux_build_ver}" && ./configure --prefix="$HOME/.local" 2>/dev/null && make -j"$(nproc 2>/dev/null || echo 2)" 2>/dev/null && make install 2>/dev/null); then
132
+ export PATH="$HOME/.local/bin:$PATH"
133
+ _tmux_upgraded=true
134
+ echo " Built tmux ${_tmux_build_ver} → ~/.local/bin/tmux"
135
+ fi
136
+ fi
137
+ rm -rf "$_tmux_build_dir"
138
+ fi
139
+
140
+ if [ "$_tmux_upgraded" = true ]; then
141
+ echo " Upgraded tmux to $(tmux -V 2>/dev/null || echo 'unknown')"
142
+ else
143
+ echo ""
144
+ echo "WARNING: tmux $_tmux_ver is too old and auto-upgrade failed."
145
+ echo " batipanel requires tmux 2.6+."
146
+ echo ""
147
+ echo " Install manually:"
148
+ if [ "$OS" = "Darwin" ]; then
149
+ echo " brew install tmux"
150
+ else
151
+ echo " sudo snap install tmux --classic"
152
+ echo " # or: https://github.com/tmux/tmux/wiki/Installing"
153
+ fi
154
+ exit 1
155
+ fi
156
+ fi
157
+
101
158
  # ensure git >= 2.32 (required by lazygit)
102
159
  if has_cmd git; then
103
160
  GIT_VER=$(git --version | grep -oE '[0-9]+\.[0-9]+' | head -1)
@@ -134,25 +191,103 @@ install_optional_tools() {
134
191
  # claude code (official standalone installer — no Node.js required)
135
192
  if ! has_cmd claude; then
136
193
  echo " Installing Claude Code..."
137
- if curl -fsSL https://claude.ai/install.sh | bash 2>/dev/null; then
138
- # installer adds to PATH but current shell may not have it yet
139
- export PATH="$HOME/.claude/bin:$PATH"
140
- if has_cmd claude; then
141
- echo " Claude Code installed"
194
+
195
+ # check available memory (RAM + swap) Claude installer needs ~2GB
196
+ local _mem_total_kb=0 _swap_total_kb=0
197
+ if [ -f /proc/meminfo ]; then
198
+ _mem_total_kb=$(grep -i 'MemTotal' /proc/meminfo | awk '{print $2}')
199
+ _swap_total_kb=$(grep -i 'SwapTotal' /proc/meminfo | awk '{print $2}')
200
+ elif command -v sysctl &>/dev/null; then
201
+ _mem_total_kb=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1024 ))
202
+ fi
203
+ local _total_kb=$(( _mem_total_kb + _swap_total_kb ))
204
+
205
+ # if total memory < 2GB and no swap, offer to create swap
206
+ if [ "$_total_kb" -gt 0 ] && [ "$_total_kb" -lt 2000000 ] && [ "$_swap_total_kb" -eq 0 ]; then
207
+ echo " Low memory detected ($(( _mem_total_kb / 1024 ))MB RAM, no swap)"
208
+ echo " Claude Code installer may fail without enough memory."
209
+ echo ""
210
+ printf " Add 1GB swap to prevent OOM? [Y/n] "
211
+ local _swap_answer=""
212
+ if [ -t 0 ]; then
213
+ read -r _swap_answer
142
214
  else
143
- echo " Claude Code installer ran but 'claude' not found in PATH"
215
+ read -r _swap_answer < /dev/tty 2>/dev/null || _swap_answer="y"
216
+ fi
217
+ case "$_swap_answer" in
218
+ [nN]*)
219
+ echo " Skipping swap setup"
220
+ ;;
221
+ *)
222
+ if sudo dd if=/dev/zero of=/swapfile bs=1M count=1024 2>/dev/null \
223
+ && sudo chmod 600 /swapfile \
224
+ && sudo mkswap /swapfile 2>/dev/null \
225
+ && sudo swapon /swapfile 2>/dev/null; then
226
+ echo " Swap enabled (1GB)"
227
+ # offer to persist swap across reboots
228
+ printf " Make swap permanent (survives reboot)? [Y/n] "
229
+ local _perm_answer=""
230
+ if [ -t 0 ]; then
231
+ read -r _perm_answer
232
+ else
233
+ read -r _perm_answer < /dev/tty 2>/dev/null || _perm_answer="y"
234
+ fi
235
+ case "$_perm_answer" in
236
+ [nN]*) ;;
237
+ *)
238
+ if ! grep -qF '/swapfile' /etc/fstab 2>/dev/null; then
239
+ echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab >/dev/null 2>/dev/null \
240
+ && echo " Swap registered in /etc/fstab (permanent)" \
241
+ || echo " Could not write to /etc/fstab (swap is temporary)"
242
+ fi
243
+ ;;
244
+ esac
245
+ else
246
+ echo " Failed to create swap (may need root)"
247
+ fi
248
+ ;;
249
+ esac
250
+ fi
251
+
252
+ # download and run installer
253
+ local _claude_installer
254
+ _claude_installer=$(mktemp)
255
+ if curl -fsSL https://claude.ai/install.sh -o "$_claude_installer" 2>/dev/null; then
256
+ local _claude_rc=0 _claude_out=""
257
+ _claude_out=$(bash "$_claude_installer" 2>&1) || _claude_rc=$?
258
+ if echo "$_claude_out" | grep -qi "ENOSPC\|no space left"; then
259
+ echo " Claude Code install failed: no disk space left"
260
+ echo " Free up space: df -h / && sudo du -sh /tmp/* /var/cache/* 2>/dev/null | sort -rh | head"
261
+ echo " Then retry: curl -fsSL https://claude.ai/install.sh | bash"
262
+ elif [ "$_claude_rc" -eq 137 ] || [ "$_claude_rc" -eq 9 ]; then
263
+ echo " Claude Code install killed (not enough memory)"
264
+ echo " Try: add swap and retry manually"
265
+ echo " sudo fallocate -l 1G /swapfile && sudo chmod 600 /swapfile"
266
+ echo " sudo mkswap /swapfile && sudo swapon /swapfile"
267
+ echo " curl -fsSL https://claude.ai/install.sh | bash"
268
+ elif [ "$_claude_rc" -eq 0 ]; then
269
+ export PATH="$HOME/.claude/bin:$PATH"
270
+ if has_cmd claude; then
271
+ echo " Claude Code installed"
272
+ else
273
+ echo " Claude Code installer ran but 'claude' not found in PATH"
274
+ fi
275
+ else
276
+ echo " Claude Code auto-install failed (exit code: $_claude_rc)"
277
+ echo " Install manually: curl -fsSL https://claude.ai/install.sh | bash"
144
278
  fi
145
279
  else
146
- echo " Claude Code auto-install failed"
280
+ echo " Could not download Claude Code installer"
147
281
  echo " Install manually: curl -fsSL https://claude.ai/install.sh | bash"
148
282
  fi
283
+ rm -f "$_claude_installer"
149
284
  fi
150
285
 
151
286
  # btop
152
287
  if ! has_cmd btop; then
153
288
  install_packages btop 2>/dev/null || true
154
289
  fi
155
- # btop GitHub releases only provide Linux binaries
290
+ # btop: direct binary install for Linux (GitHub releases use .tbz = bzip2 tar)
156
291
  if ! has_cmd btop && [ "$OS_LOWER" = "linux" ]; then
157
292
  tag=$(latest_github_tag "aristocratos/btop")
158
293
  if [ -n "$tag" ]; then
@@ -160,8 +295,26 @@ install_optional_tools() {
160
295
  case "$ARCH" in
161
296
  aarch64|arm64) _btop_arch="aarch64" ;;
162
297
  esac
163
- install_from_github btop \
164
- "https://github.com/aristocratos/btop/releases/download/${tag}/btop-${_btop_arch}-unknown-linux-musl.tbz" 2>/dev/null || true
298
+ local _btop_tmp
299
+ _btop_tmp=$(mktemp -d)
300
+ echo " Downloading btop from GitHub..."
301
+ if curl -fsSL "https://github.com/aristocratos/btop/releases/download/${tag}/btop-${_btop_arch}-unknown-linux-musl.tbz" -o "$_btop_tmp/btop.tbz" 2>/dev/null; then
302
+ if tar xjf "$_btop_tmp/btop.tbz" -C "$_btop_tmp" 2>/dev/null; then
303
+ local _btop_bin
304
+ _btop_bin=$(find "$_btop_tmp" -name "btop" -type f 2>/dev/null | head -1)
305
+ if [ -n "$_btop_bin" ] && [ -f "$_btop_bin" ]; then
306
+ chmod +x "$_btop_bin"
307
+ if install "$_btop_bin" "$HOME/.local/bin/" 2>/dev/null; then
308
+ echo " Installed btop to ~/.local/bin/"
309
+ # shellcheck disable=SC2034
310
+ NEED_LOCAL_BIN_PATH=1
311
+ elif sudo install "$_btop_bin" /usr/local/bin/ 2>/dev/null; then
312
+ echo " Installed btop to /usr/local/bin/"
313
+ fi
314
+ fi
315
+ fi
316
+ fi
317
+ rm -rf "$_btop_tmp"
165
318
  fi
166
319
  fi
167
320
 
@@ -29,6 +29,8 @@ install_packages() {
29
29
  sudo apt-get install -y -qq "${packages[@]}" 2>/dev/null || true
30
30
  elif command -v dnf &>/dev/null; then
31
31
  sudo dnf install -y "${packages[@]}" 2>/dev/null || true
32
+ elif command -v yum &>/dev/null; then
33
+ sudo yum install -y "${packages[@]}" 2>/dev/null || true
32
34
  elif command -v pacman &>/dev/null; then
33
35
  sudo pacman -S --noconfirm "${packages[@]}" 2>/dev/null || true
34
36
  elif command -v apk &>/dev/null; then