This commit is contained in:
2026-06-28 08:51:25 +05:30
parent 671cf7f818
commit 190f337f12
34 changed files with 650 additions and 648 deletions

View File

@@ -2,21 +2,16 @@
# Tool: agy
# DisplayName: Antigravity
# Description: Install Antigravity CLI
# Strategy: binary
#
# Antigravity CLI Installer Script (Linux Only)
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
# Constants
DOWNLOAD_BASE_URL="https://antigravity-cli-auto-updater-974169037036.us-central1.run.app"
TARGET_DIR="$HOME/.local/bin"
TARGET_DIR="$BOOTSTRAP_BIN"
BINARY_PATH="$TARGET_DIR/agy"
install_agy() {
@@ -55,19 +50,12 @@ install_agy() {
exit 1
fi
# POSIX-compliant JSON parser (no jq dependencies)
parse_json_key() {
local payload="$1"
local key="$2"
echo "$payload" | sed -n 's/.*"'"$key"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p'
}
local version
local url
local sha512
version=$(parse_json_key "$manifest_json" "version")
url=$(parse_json_key "$manifest_json" "url")
sha512=$(parse_json_key "$manifest_json" "sha512")
version=$(echo "$manifest_json" | jq -r '.version // empty')
url=$(echo "$manifest_json" | jq -r '.url // empty')
sha512=$(echo "$manifest_json" | jq -r '.sha512 // empty')
if [ -z "$url" ] || [ -z "$sha512" ]; then
log_error "Failed to parse release manifest."
@@ -134,16 +122,11 @@ install_agy() {
track_file "$BINARY_PATH"
log_success "Antigravity CLI successfully installed to $BINARY_PATH."
register_tool "agy" "binary" "" "github:sortedcord/agy"
}
configure_shell() {
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "local-bin path"
done
write_env_snippet "local-bin" 'export PATH="$HOME/.local/bin:$PATH"'
}
run_handoff() {

View File

@@ -2,16 +2,11 @@
# Tool: asciicinema
# DisplayName: asciicinema
# Description: Install asciinema terminal recorder
# Strategy: binary
#
# asciinema Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -21,12 +16,9 @@ cleanup() {
trap cleanup EXIT
install_asciicinema() {
local latest_tag=""
if has_command curl; then
log_info "Fetching latest asciinema version from GitHub..."
latest_tag=$(curl -sL https://api.github.com/repos/asciinema/asciinema/releases/latest \
| grep '"tag_name":' | head -n1 \
| sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
latest_tag=$(github_get_latest_release "asciinema/asciinema")
fi
if [ -z "$latest_tag" ]; then
@@ -73,22 +65,21 @@ install_asciicinema() {
*) log_error "Unsupported architecture: $arch"; exit 1 ;;
esac
local download_url="https://github.com/asciinema/asciinema/releases/download/${latest_tag}/asciinema-${asciinema_arch}"
log_info "Downloading asciinema ${latest_tag} for ${arch}..."
download_file "$download_url" "$TMP_DIR/asciinema"
github_download_asset "asciinema/asciinema" "$latest_tag" "asciinema-${asciinema_arch}" "$TMP_DIR/asciinema"
log_info "Installing asciinema to /usr/local/bin..."
sudo cp "$TMP_DIR/asciinema" /usr/local/bin/asciinema
sudo chmod +x /usr/local/bin/asciinema
track_file "/usr/local/bin/asciinema"
log_info "Installing asciinema to $BOOTSTRAP_BIN..."
cp "$TMP_DIR/asciinema" "$BOOTSTRAP_BIN/asciinema"
chmod +x "$BOOTSTRAP_BIN/asciinema"
track_file "$BOOTSTRAP_BIN/asciinema"
# Create compatibility symlink matching the installer name spelling
log_info "Creating compatibility symlink for asciicinema..."
sudo ln -sf /usr/local/bin/asciinema /usr/local/bin/asciicinema
ln -sf "$BOOTSTRAP_BIN/asciinema" /usr/local/bin/asciicinema
track_file "/usr/local/bin/asciicinema"
log_success "asciinema ${latest_tag} installed."
register_tool "asciicinema" "binary" "$latest_tag" "github:asciinema/asciinema"
}
main() {

View File

@@ -2,16 +2,11 @@
# Tool: bat
# DisplayName: Bat
# Description: Install Bat (alternative to cat) and configure alias
# Strategy: binary
#
# Bat Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -21,69 +16,54 @@ cleanup() {
trap cleanup EXIT
install_bat() {
local distro
distro=$(detect_distro)
if [ "$distro" = "arch" ]; then
log_info "Arch Linux detected"
log_info "Installing Bat..."
pkg_install bat
elif [ "$distro" = "fedora" ]; then
log_info "Fedora detected"
log_info "Installing Bat..."
pkg_install bat
elif [ "$distro" = "debian" ]; then
log_info "Debian/Ubuntu detected"
pkg_install curl
log_info "Fetching latest Bat version from GitHub..."
local latest_tag=""
latest_tag=$(curl -sL https://api.github.com/repos/sharkdp/bat/releases/latest | grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
if [ -z "$latest_tag" ]; then
latest_tag="v0.26.1"
log_warn "Failed to fetch latest version from GitHub. Falling back to: $latest_tag"
else
log_info "Latest Bat version found: $latest_tag"
if has_command bat; then
if ! confirm "Bat is already installed. Reinstall/Upgrade?"; then
log_info "Skipping Bat installation."
return
fi
# Remove leading 'v' for file name version
local version="${latest_tag#v}"
# Detect architecture mapping
local arch
arch=$(detect_arch)
local deb_arch="amd64"
if [ "$arch" = "arm64" ]; then
deb_arch="arm64"
fi
local deb_url="https://github.com/sharkdp/bat/releases/download/${latest_tag}/bat_${version}_${deb_arch}.deb"
log_info "Downloading Bat from ${deb_url}..."
download_file "$deb_url" "$TMP_DIR/bat.deb"
log_info "Installing Bat package..."
sudo apt install -y "$TMP_DIR/bat.deb"
add_rollback_cmd "sudo apt remove -y bat"
else
log_error "Unsupported distribution."
exit 1
fi
local arch
arch=$(detect_arch)
local target=""
case "$arch" in
x86_64) target="x86_64-unknown-linux-gnu" ;;
arm64) target="aarch64-unknown-linux-gnu" ;;
*) log_error "Unsupported architecture: $arch"; exit 1 ;;
esac
log_info "Fetching latest Bat version from GitHub..."
local latest_tag=""
latest_tag=$(github_get_latest_release "sharkdp/bat")
if [ -z "$latest_tag" ]; then
latest_tag="v0.26.1"
log_warn "Failed to fetch latest version from GitHub. Falling back to: $latest_tag"
else
log_info "Latest Bat version found: $latest_tag"
fi
log_info "Downloading Bat ${latest_tag}..."
local archive="$TMP_DIR/bat.tar.gz"
github_download_asset "sharkdp/bat" "$latest_tag" "bat-${latest_tag}-${target}\.tar\.gz" "$archive"
log_info "Extracting Bat binary..."
tar -xzf "$archive" -C "$TMP_DIR"
local extract_dir="$TMP_DIR/bat-${latest_tag}-${target}"
local target_dir="$BOOTSTRAP_BIN"
mkdir -p "$target_dir"
log_info "Installing Bat to $target_dir/bat..."
cp "$extract_dir/bat" "$target_dir/bat"
chmod +x "$target_dir/bat"
track_file "$target_dir/bat"
register_tool "bat" "binary" "$latest_tag" "github:sharkdp/bat"
}
configure_shell() {
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "bat alias"
done
if [ -f "$HOME/.bash_aliases" ]; then
remove_block "$HOME/.bash_aliases" "bat alias"
fi
write_alias_snippet "bat" "alias cat='bat --paging=never -p'"
}

View File

@@ -2,16 +2,11 @@
# Tool: docker
# DisplayName: Docker
# Description: Container runtime and orchestration platform
# Strategy: system
#
# Docker Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
# ─── Installation Logic ──────────────────────────────────────────────
@@ -29,8 +24,9 @@ install_docker() {
fi
fi
# Use pkg_install for distro packages (it automatically handles rollback hooks for the packages!)
# Use pkg_install for distro packages and explicitly register them for reference-counted rollback
pkg_install "arch:docker|debian:docker.io|fedora:docker"
registry_add_sys_deps "docker" "arch:docker|debian:docker.io|fedora:docker"
# Ensure docker group exists (some distros might not create it immediately)
if ! getent group docker >/dev/null 2>&1; then
@@ -58,6 +54,7 @@ install_docker() {
sudo systemctl enable --now docker.service || true
sudo systemctl enable --now containerd.service || true
fi
register_tool "docker" "system" "" "os-package-manager"
}
# ─── Main ─────────────────────────────────────────────────────────────

View File

@@ -2,15 +2,8 @@
# Tool: hyperfine
# DisplayName: Hyperfine
# Description: Command-line benchmarking tool
# Strategy: binary
#
# Hyperfine Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
@@ -21,7 +14,7 @@ cleanup() {
trap cleanup EXIT
install_hyperfine() {
if has_command hyperfine || [ -f "$HOME/.local/bin/hyperfine" ]; then
if has_command hyperfine; then
if ! confirm "Hyperfine is already installed. Reinstall/Upgrade?"; then
log_info "Skipping Hyperfine installation."
return
@@ -33,12 +26,6 @@ install_hyperfine() {
fi
fi
# Ensure curl is installed
if ! has_command curl; then
log_info "curl not found. Installing curl..."
pkg_install curl
fi
# Detect architecture
local raw_arch
raw_arch=$(detect_arch)
@@ -51,21 +38,17 @@ install_hyperfine() {
log_info "Fetching latest Hyperfine version from GitHub..."
local latest_tag=""
latest_tag=$(curl -sL https://api.github.com/repos/sharkdp/hyperfine/releases/latest | grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
latest_tag=$(github_get_latest_release "sharkdp/hyperfine")
local download_url
if [ -n "$latest_tag" ]; then
log_info "Latest Hyperfine version found: $latest_tag"
download_url="https://github.com/sharkdp/hyperfine/releases/download/${latest_tag}/hyperfine-${latest_tag}-${arch}-unknown-linux-gnu.tar.gz"
else
if [ -z "$latest_tag" ]; then
latest_tag="v1.20.0"
log_warn "Failed to fetch latest version from GitHub. Falling back to: $latest_tag"
download_url="https://github.com/sharkdp/hyperfine/releases/download/${latest_tag}/hyperfine-${latest_tag}-${arch}-unknown-linux-gnu.tar.gz"
else
log_info "Latest Hyperfine version found: $latest_tag"
fi
log_info "Downloading Hyperfine from ${download_url}..."
local archive="$TMP_DIR/hyperfine.tar.gz"
download_file "$download_url" "$archive"
github_download_asset "sharkdp/hyperfine" "$latest_tag" "hyperfine-${latest_tag}-${arch}-unknown-linux-gnu.tar.gz" "$archive"
# Extract the archive
log_info "Extracting Hyperfine archive..."
@@ -73,7 +56,7 @@ install_hyperfine() {
local extract_dir="$TMP_DIR/hyperfine-${latest_tag}-${arch}-unknown-linux-gnu"
if [ ! -d "$extract_dir" ]; then
# Handle case where directory name might differ (e.g. without leading v in directory name or tag)
# Handle case where directory name might differ
extract_dir=$(find "$TMP_DIR" -maxdepth 1 -type d -name "hyperfine-*" | head -n1)
fi
@@ -82,8 +65,8 @@ install_hyperfine() {
exit 1
fi
# Install binary to ~/.local/bin
local target_dir="$HOME/.local/bin"
# Install binary to $BOOTSTRAP_BIN
local target_dir="$BOOTSTRAP_BIN"
mkdir -p "$target_dir"
log_info "Installing Hyperfine to $target_dir/hyperfine..."
cp "$extract_dir/hyperfine" "$target_dir/hyperfine"
@@ -106,27 +89,15 @@ install_hyperfine() {
comp_content=$(cat "$extract_dir/autocomplete/hyperfine.bash")
write_completion_snippet "hyperfine" "$comp_content"
fi
}
configure_shell() {
# Add ~/.local/bin to PATH for the current process
export PATH="$HOME/.local/bin:$PATH"
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "local-bin path"
done
write_env_snippet "local-bin" 'export PATH="$HOME/.local/bin:$PATH"'
register_tool "hyperfine" "binary" "$latest_tag" "github:sharkdp/hyperfine"
}
main() {
install_hyperfine
configure_shell
echo
log_success "Hyperfine installation and configuration complete."
log_success "Hyperfine installation complete."
}
main "$@"

View File

@@ -2,16 +2,11 @@
# Tool: lazygit
# DisplayName: lazygit
# Description: Simple terminal UI for git commands
# Strategy: binary
#
# lazygit Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
# ─── Installation Logic ──────────────────────────────────────────────
@@ -30,11 +25,7 @@ install_lazygit() {
fi
local latest_tag=""
if has_command curl; then
latest_tag=$(curl -sL https://api.github.com/repos/jesseduffield/lazygit/releases/latest \
| grep '"tag_name":' | head -n1 \
| sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
fi
latest_tag=$(github_get_latest_release "jesseduffield/lazygit")
if [ -z "$latest_tag" ]; then
latest_tag="v0.62.2" # fallback
@@ -43,14 +34,13 @@ install_lazygit() {
local version="${latest_tag#v}"
local arch=$(detect_arch)
local arch
arch=$(detect_arch)
local arch_str="x86_64"
if [ "$arch" = "arm64" ]; then
arch_str="arm64"
fi
local url="https://github.com/jesseduffield/lazygit/releases/download/${latest_tag}/lazygit_${version}_linux_${arch_str}.tar.gz"
TMP_DIR="$(make_temp_dir)"
cleanup() { rm -rf "$TMP_DIR"; }
trap cleanup EXIT
@@ -58,15 +48,16 @@ install_lazygit() {
local dest="$TMP_DIR/lazygit.tar.gz"
log_info "Downloading lazygit ${latest_tag}..."
download_file "$url" "$dest"
github_download_asset "jesseduffield/lazygit" "$latest_tag" "lazygit_${version}_linux_${arch_str}\.tar\.gz" "$dest"
log_info "Extracting..."
tar -xzf "$dest" -C "$TMP_DIR"
mkdir -p "$HOME/.local/bin"
mkdir -p "$BOOTSTRAP_BIN"
cp "$TMP_DIR/lazygit" "$HOME/.local/bin/lazygit"
chmod +x "$HOME/.local/bin/lazygit"
track_file "$HOME/.local/bin/lazygit"
register_tool "lazygit" "binary" "$latest_tag" "github:jesseduffield/lazygit"
}
# ─── Main ─────────────────────────────────────────────────────────────

View File

@@ -2,16 +2,11 @@
# Tool: node
# DisplayName: Node
# Description: Install Node.js (LTS) and NVM
# Strategy: managed
#
# Node.js and NVM Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -21,7 +16,7 @@ cleanup() {
trap cleanup EXIT
install_nvm() {
if has_command nvm || [ -s "$HOME/.nvm/nvm.sh" ]; then
if has_command nvm || [ -s "$BOOTSTRAP_RUNTIMES/nvm/nvm.sh" ]; then
log_info "NVM is already installed."
fi
@@ -29,12 +24,13 @@ install_nvm() {
if ! has_command tar; then
log_info "tar not found. Installing tar..."
pkg_install tar
registry_add_sys_deps "node" "tar"
fi
# Try to fetch the latest version of NVM from GitHub API
log_info "Fetching the latest NVM version..."
local latest_tag=""
latest_tag=$(curl -sL https://api.github.com/repos/nvm-sh/nvm/releases/latest | grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
latest_tag=$(github_get_latest_release "nvm-sh/nvm")
if [ -z "$latest_tag" ]; then
latest_tag="v0.40.5" # Fallback version if API request fails
@@ -47,25 +43,20 @@ install_nvm() {
log_info "Downloading NVM from $nvm_url..."
download_file "$nvm_url" "$TMP_DIR/nvm.tar.gz"
log_info "Extracting NVM archive directly to $HOME/.nvm (stripping versioned subfolder to keep config generic)..."
mkdir -p "$HOME/.nvm"
tar -xzf "$TMP_DIR/nvm.tar.gz" -C "$HOME/.nvm" --strip-components=1
log_info "Extracting NVM archive directly to $BOOTSTRAP_RUNTIMES/nvm (stripping versioned subfolder to keep config generic)..."
mkdir -p "$BOOTSTRAP_RUNTIMES/nvm"
tar -xzf "$TMP_DIR/nvm.tar.gz" -C "$BOOTSTRAP_RUNTIMES/nvm" --strip-components=1
track_dir "$HOME/.nvm"
track_dir "$BOOTSTRAP_RUNTIMES/nvm"
log_success "NVM source files successfully extracted to $HOME/.nvm."
log_success "NVM source files successfully extracted to $BOOTSTRAP_RUNTIMES/nvm."
}
configure_shell() {
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "nvm setup"
done
local content
content=$(cat << 'EOF'
export NVM_DIR="$HOME/.nvm"
export NVM_DIR="$BOOTSTRAP_RUNTIMES/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # Load NVM
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # Load NVM bash completion
EOF
@@ -76,10 +67,11 @@ EOF
install_node() {
# Ensure NVM is loaded in this script context
if [ -s "$HOME/.nvm/nvm.sh" ]; then
if [ -s "$BOOTSTRAP_RUNTIMES/nvm/nvm.sh" ]; then
# Temporarily disable nounset as nvm.sh does not support set -u
set +u
. "$HOME/.nvm/nvm.sh"
# shellcheck source=/dev/null
. "$BOOTSTRAP_RUNTIMES/nvm/nvm.sh"
else
log_error "Could not load NVM to install Node.js."
return 1
@@ -95,6 +87,7 @@ install_node() {
nvm alias default 'lts/*'
log_success "Node.js installed successfully!"
set -u
register_tool "node" "managed" "$latest_tag" "github:nvm-sh/nvm"
}
main() {
@@ -106,7 +99,7 @@ main() {
if has_command node; then
log_success "Node.js (via NVM) installation and configuration complete."
log_info "Installed Node version: $(node --version)"
log_info "Installed NVM version: $(nvm --version 2>/dev/null || cat "$HOME/.nvm/package.json" | grep '"version":' | head -n1 | sed -E 's/.*"version": "([^"]+)".*/\1/' || echo "unknown")"
log_info "Installed NVM version: $(nvm --version 2>/dev/null || grep '"version":' "$BOOTSTRAP_RUNTIMES/nvm/package.json" | head -n1 | sed -E 's/.*"version": "([^"]+)".*/\1/' || echo "unknown")"
else
log_success "Installation complete."
fi

View File

@@ -1,21 +1,18 @@
#!/usr/bin/env bash
# shellcheck disable=SC2016
# Tool: nvim
# DisplayName: Neovim
# Description: Install Neovim 0.12.0 and configuration
# Strategy: binary
#
# Neovim Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
NVIM_VERSION="0.12.0"
NVIM_INSTALL_DIR="/opt/nvim"
NVIM_INSTALL_DIR="$BOOTSTRAP_OPT/nvim"
NVIM_BIN_DIR="$BOOTSTRAP_BIN"
NVIM_CONFIG_REPO="https://git.adityagupta.dev/sortedcord/editor.git"
NVIM_CONFIG_DIR="$HOME/.config/nvim"
@@ -33,7 +30,18 @@ check_config_dir() {
install_packages() {
log_info "Detecting distribution and installing dependencies..."
pkg_install \
git tar curl unzip ripgrep fzf nodejs npm xclip wl-clipboard \
git tar unzip ripgrep fzf nodejs npm xclip wl-clipboard \
"arch:fd|debian:fd-find|fedora:fd-find" \
"arch:cmake|debian:cmake|fedora:cmake" \
"arch:make|debian:build-essential|fedora:make" \
"arch:gcc|debian:build-essential|fedora:gcc" \
"arch:python|debian:python3|fedora:python3" \
"debian:python3-pip|fedora:python3-pip" \
"debian:python3-venv" \
"fedora:gcc-c++"
registry_add_sys_deps "nvim" \
git tar unzip ripgrep fzf nodejs npm xclip wl-clipboard \
"arch:fd|debian:fd-find|fedora:fd-find" \
"arch:cmake|debian:cmake|fedora:cmake" \
"arch:make|debian:build-essential|fedora:make" \
@@ -76,23 +84,23 @@ install_nvim() {
*) log_error "Unsupported architecture: $arch"; exit 1 ;;
esac
local nvim_url="https://github.com/neovim/neovim/releases/download/v${NVIM_VERSION}/nvim-${nvim_arch}.tar.gz"
log_info "Downloading Neovim v${NVIM_VERSION} for ${arch}..."
download_file "$nvim_url" "$TMP_DIR/nvim.tar.gz"
github_download_asset "neovim/neovim" "v${NVIM_VERSION}" "nvim-${nvim_arch}\.tar\.gz" "$TMP_DIR/nvim.tar.gz"
tar -xzf "$TMP_DIR/nvim.tar.gz" -C "$TMP_DIR"
sudo rm -rf "$NVIM_INSTALL_DIR"
sudo mv "$TMP_DIR/nvim-${nvim_arch}" "$NVIM_INSTALL_DIR"
rm -rf "$NVIM_INSTALL_DIR"
mkdir -p "$(dirname "$NVIM_INSTALL_DIR")"
mv "$TMP_DIR/nvim-${nvim_arch}" "$NVIM_INSTALL_DIR"
sudo ln -sf "$NVIM_INSTALL_DIR/bin/nvim" /usr/local/bin/nvim
ln -sf "$NVIM_INSTALL_DIR/bin/nvim" "$NVIM_BIN_DIR/nvim"
track_dir "$NVIM_INSTALL_DIR"
track_file "/usr/local/bin/nvim"
track_file "$NVIM_BIN_DIR/nvim"
log_success "Installed:"
nvim --version | head -n1
register_tool "nvim" "binary" "$NVIM_VERSION" "github:neovim/neovim"
}
install_config() {
@@ -111,24 +119,6 @@ install_config() {
}
configure_shell() {
# Clean up legacy inline edits from bashrc and bash_aliases
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
if [ -f "$config_file" ]; then
local tmp_file
tmp_file=$(mktemp)
sed '/^export EDITOR="nvim"/d' "$config_file" > "$tmp_file"
cat "$tmp_file" > "$config_file"
rm -f "$tmp_file"
fi
done
if [ -f "$HOME/.bash_aliases" ]; then
local tmp_file
tmp_file=$(mktemp)
sed '/^alias vim="nvim"/d' "$HOME/.bash_aliases" > "$tmp_file"
cat "$tmp_file" > "$HOME/.bash_aliases"
rm -f "$tmp_file"
fi
write_alias_snippet "nvim" 'alias vim="nvim"'
write_env_snippet "nvim" 'export EDITOR="nvim"'

View File

@@ -2,6 +2,7 @@
# Tool: pnpm
# DisplayName: Pnpm
# Description: Install pnpm package manager
# Strategy: binary
#
# pnpm Installer Script
#
@@ -17,12 +18,6 @@
# curl -fsSL https://get.pnpm.io/install.sh | ENV="$HOME/.bashrc" SHELL="$(which bash)" bash -
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -127,14 +122,17 @@ install_pnpm() {
}
libc_suffix="$(detect_libc_suffix)"
# Fetch the latest version from the npm registry, or use PNPM_VERSION if set
# Fetch the latest version from GitHub, or use PNPM_VERSION if set
if [ -z "${PNPM_VERSION:-}" ]; then
log_info "Fetching latest pnpm version from npm registry..."
version_json="$(download "https://registry.npmjs.org/@pnpm/exe")" || {
log_error "Failed to fetch pnpm version info from npm registry."
log_info "Fetching latest pnpm version from GitHub..."
local tag
tag=$(github_get_latest_release "pnpm/pnpm")
if [ -n "$tag" ]; then
version="${tag#v}"
else
log_error "Failed to fetch pnpm version info from GitHub."
return 1
}
version="$(echo "$version_json" | grep -o '"latest":[[:space:]]*"[0-9.]*"' | grep -o '[0-9.]*')"
fi
else
version="${PNPM_VERSION}"
fi
@@ -151,7 +149,7 @@ install_pnpm() {
if [ "$major_version" -ge 11 ]; then
# v11+: distributed as tarballs containing the binary and dist/ directory
download "https://github.com/pnpm/pnpm/releases/download/v${version}/${asset_base}.tar.gz" "$TMP_DIR/pnpm.tar.gz" || {
github_download_asset "pnpm/pnpm" "v${version}" "${asset_base}\.tar\.gz" "$TMP_DIR/pnpm.tar.gz" || {
log_error "Failed to download pnpm tarball."
return 1
}
@@ -166,7 +164,7 @@ install_pnpm() {
}
else
# Older versions: distributed as a single executable binary
download "https://github.com/pnpm/pnpm/releases/download/v${version}/${asset_base}" "$TMP_DIR/pnpm" || {
github_download_asset "pnpm/pnpm" "v${version}" "${asset_base}" "$TMP_DIR/pnpm" || {
log_error "Failed to download pnpm binary."
return 1
}
@@ -179,23 +177,19 @@ install_pnpm() {
track_dir "$HOME/.local/share/pnpm"
log_success "pnpm v${version} installed successfully!"
register_tool "pnpm" "binary" "$version" "github:pnpm/pnpm"
}
# ─── Shell Configuration ─────────────────────────────────────────────
configure_shell() {
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "pnpm setup"
done
# pnpm's `setup --force` configures PNPM_HOME and PATH automatically,
# but we also add an env block to ensure PNPM_HOME is set consistently.
local content
content=$(cat << 'EOF'
# pnpm
export PNPM_HOME="$HOME/.local/share/pnpm"
export PNPM_HOME="$BOOTSTRAP_RUNTIMES/pnpm"
case ":$PATH:" in
*":$PNPM_HOME:"*) ;;
*) export PATH="$PNPM_HOME:$PATH" ;;

View File

@@ -1,17 +1,13 @@
#!/usr/bin/env bash
# shellcheck disable=SC2016
# Tool: rust
# DisplayName: Rust
# Description: Install Rustup and Rust compiler/toolchain
# Strategy: managed
#
# Rust Installer Script (Simplified Local Rustup Init)
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -20,14 +16,6 @@ cleanup() {
}
trap cleanup EXIT
# Ensure we have curl
install_downloader() {
if ! has_command curl; then
log_info "curl not found. Installing curl..."
pkg_install curl
fi
}
detect_target_triple() {
local ostype
ostype="$(uname -s)"
@@ -61,11 +49,14 @@ detect_target_triple() {
}
install_rust() {
if has_command rustup || [ -f "$HOME/.cargo/bin/rustup" ]; then
export CARGO_HOME="$BOOTSTRAP_RUNTIMES/cargo"
export RUSTUP_HOME="$BOOTSTRAP_RUNTIMES/rustup"
if has_command rustup || [ -f "$BOOTSTRAP_RUNTIMES/cargo/bin/rustup" ]; then
log_info "Rust (rustup) is already installed."
fi
install_downloader
local target
target=$(detect_target_triple)
@@ -87,19 +78,19 @@ install_rust() {
"$dest" -y --no-modify-path
add_rollback_cmd "rustup self uninstall -y"
register_tool "rust" "managed" "" "rustup"
}
configure_shell() {
# Add ~/.cargo/bin to PATH for the current process
export PATH="$HOME/.cargo/bin:$PATH"
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "rust init"
done
write_env_snippet "rust" '. "$HOME/.cargo/env"'
local snippet_content=$(cat << 'EOF'
export CARGO_HOME="$BOOTSTRAP_RUNTIMES/cargo"
export RUSTUP_HOME="$BOOTSTRAP_RUNTIMES/rustup"
. "$CARGO_HOME/env"
EOF
)
write_env_snippet "rust" "$snippet_content"
}
main() {

View File

@@ -1,17 +1,13 @@
#!/usr/bin/env bash
# shellcheck disable=SC2016
# Tool: starship
# DisplayName: Starship
# Description: Install Starship shell prompt
# Strategy: binary
#
# Starship Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -25,12 +21,6 @@ install_starship() {
log_info "Starship is already installed."
fi
# Ensure curl is installed
if ! has_command curl; then
log_info "curl not found. Installing curl..."
pkg_install curl
fi
# Detect architecture
local raw_arch
raw_arch=$(detect_arch)
@@ -45,47 +35,33 @@ install_starship() {
log_info "Fetching latest Starship version from GitHub..."
local latest_tag=""
latest_tag=$(curl -sL https://api.github.com/repos/starship/starship/releases/latest | grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
local download_url
if [ -n "$latest_tag" ]; then
log_info "Latest Starship version found: $latest_tag"
download_url="https://github.com/starship/starship/releases/download/${latest_tag}/starship-${target}.tar.gz"
else
latest_tag=$(github_get_latest_release "starship/starship")
if [ -z "$latest_tag" ]; then
latest_tag="latest"
log_warn "Failed to fetch latest version from GitHub. Falling back to downloading latest release directly."
download_url="https://github.com/starship/starship/releases/latest/download/starship-${target}.tar.gz"
fi
log_info "Downloading Starship from ${download_url}..."
log_info "Downloading Starship ${latest_tag}..."
local archive="$TMP_DIR/starship.tar.gz"
download_file "$download_url" "$archive"
github_download_asset "starship/starship" "$latest_tag" "starship-${target}\.tar\.gz" "$archive"
# Extract the binary
log_info "Extracting Starship binary..."
tar -xzf "$archive" -C "$TMP_DIR"
# Install to ~/.local/bin
local target_dir="$HOME/.local/bin"
local target_dir="$BOOTSTRAP_BIN"
mkdir -p "$target_dir"
log_info "Installing Starship to $target_dir/starship..."
cp "$TMP_DIR/starship" "$target_dir/starship"
chmod +x "$target_dir/starship"
track_file "$target_dir/starship"
register_tool "starship" "binary" "$latest_tag" "github:starship/starship"
}
configure_shell() {
# Add ~/.local/bin to PATH for the current process
export PATH="$HOME/.local/bin:$PATH"
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "local-bin path"
remove_block "$config_file" "starship init"
done
write_env_snippet "local-bin" 'export PATH="$HOME/.local/bin:$PATH"'
write_env_snippet "starship" 'eval "$(starship init bash)"'
}

View File

@@ -1,17 +1,13 @@
#!/usr/bin/env bash
# shellcheck disable=SC2016
# Tool: uv
# DisplayName: uv
# Description: Fast Python package installer and resolver
# Strategy: binary
#
# uv Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -28,12 +24,6 @@ install_uv() {
fi
fi
# Ensure curl is installed
if ! has_command curl; then
log_info "curl not found. Installing curl..."
pkg_install curl
fi
# Detect architecture
local raw_arch
raw_arch=$(detect_arch)
@@ -54,28 +44,22 @@ install_uv() {
log_info "Fetching latest uv version from GitHub..."
local latest_tag=""
latest_tag=$(curl -sL https://api.github.com/repos/astral-sh/uv/releases/latest | grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
latest_tag=$(github_get_latest_release "astral-sh/uv")
local download_url
if [ -n "$latest_tag" ]; then
log_info "Latest uv version found: $latest_tag"
download_url="https://github.com/astral-sh/uv/releases/download/${latest_tag}/uv-${target}.tar.gz"
else
if [ -z "$latest_tag" ]; then
latest_tag="latest"
log_warn "Failed to fetch latest version from GitHub. Falling back to downloading latest release directly."
download_url="https://github.com/astral-sh/uv/releases/latest/download/uv-${target}.tar.gz"
fi
log_info "Downloading uv from ${download_url}..."
log_info "Downloading uv ${latest_tag}..."
local archive="$TMP_DIR/uv.tar.gz"
download_file "$download_url" "$archive"
github_download_asset "astral-sh/uv" "$latest_tag" "uv-${target}\.tar\.gz" "$archive"
# Extract the binaries
log_info "Extracting uv binaries..."
tar -xzf "$archive" --strip-components 1 -C "$TMP_DIR"
# Install to ~/.local/bin
local target_dir="$HOME/.local/bin"
local target_dir="$BOOTSTRAP_BIN"
mkdir -p "$target_dir"
log_info "Installing uv and uvx to $target_dir..."
cp "$TMP_DIR/uv" "$target_dir/uv"
@@ -83,20 +67,12 @@ install_uv() {
chmod +x "$target_dir/uv" "$target_dir/uvx"
track_file "$target_dir/uv"
track_file "$target_dir/uvx"
register_tool "uv" "binary" "$latest_tag" "github:astral-sh/uv"
}
configure_shell() {
# Add ~/.local/bin to PATH for the current process
export PATH="$HOME/.local/bin:$PATH"
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "local-bin path"
remove_block "$config_file" "uv completion"
done
write_env_snippet "local-bin" 'export PATH="$HOME/.local/bin:$PATH"'
write_env_snippet "uv" 'eval "$(uv generate-shell-completion bash)"'
}

View File

@@ -2,16 +2,11 @@
# Tool: yay
# DisplayName: Yay
# Description: Install Yay AUR helper
# Strategy: system
#
# Yay Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
# ─── Installation Logic ──────────────────────────────────────────────
@@ -43,6 +38,7 @@ install_yay() {
else
log_info "Dependencies (git and base-devel) are already present. Skipping package installation."
fi
registry_add_sys_deps "yay" "git" "base-devel"
log_info "Cloning yay-bin repository..."
local clone_dir
@@ -66,6 +62,7 @@ install_yay() {
cd "$orig_dir"
log_info "Cleaning up installer directory..."
rm -rf "$clone_dir"
register_tool "yay" "system" "" "aur:yay-bin"
}
# ─── Main ─────────────────────────────────────────────────────────────

View File

@@ -2,16 +2,11 @@
# Tool: yazi
# DisplayName: Yazi
# Description: Install Yazi terminal file manager and dependencies
# Strategy: binary
#
# Yazi Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
TMP_DIR="$(make_temp_dir)"
@@ -21,11 +16,6 @@ cleanup() {
trap cleanup EXIT
add_y_wrapper() {
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "yazi wrapper"
done
local wrapper_content
wrapper_content=$(cat << 'EOF'
@@ -40,77 +30,69 @@ y() {
}
EOF
)
write_alias_snippet "yazi" "$wrapper_content"
}
install_yazi() {
local distro
distro=$(detect_distro)
if [ "$distro" = "arch" ]; then
log_info "Arch Linux detected"
if has_command yazi; then
log_info "Yazi is already installed."
if has_command yazi; then
if ! confirm "Yazi is already installed. Reinstall/Upgrade?"; then
log_info "Skipping Yazi installation."
return
fi
log_info "Installing Yazi..."
pkg_install yazi
log_info "Installing dependencies subsequently..."
pkg_install ffmpeg 7zip jq poppler fd ripgrep fzf zoxide resvg imagemagick
elif [ "$distro" = "debian" ]; then
log_info "Debian/Ubuntu detected"
if has_command yazi; then
log_info "Yazi is already installed."
fi
pkg_install curl git
log_info "Fetching latest Yazi version from GitHub..."
local latest_tag
latest_tag=$(curl -sL https://api.github.com/repos/sxyazi/yazi/releases/latest | grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/' || true)
if [ -z "$latest_tag" ]; then
latest_tag="v26.5.6"
fi
local deb_url="https://github.com/sxyazi/yazi/releases/download/${latest_tag}/yazi-x86_64-unknown-linux-gnu.deb"
log_info "Downloading Yazi ${latest_tag} from ${deb_url}..."
download_file "$deb_url" "$TMP_DIR/yazi.deb"
log_info "Installing Yazi package..."
sudo apt install -y "$TMP_DIR/yazi.deb"
add_rollback_cmd "sudo apt remove -y yazi"
log_info "Installing dependencies subsequently..."
pkg_install ffmpeg jq poppler-utils fd-find ripgrep fzf zoxide resvg imagemagick 7zip || \
pkg_install ffmpeg jq poppler-utils fd-find ripgrep fzf zoxide resvg imagemagick p7zip-full
create_fd_symlink
elif [ "$distro" = "fedora" ]; then
log_info "Fedora detected"
if has_command yazi; then
log_info "Yazi is already installed."
fi
log_info "Installing dnf-plugins-core..."
pkg_install dnf-plugins-core
log_info "Enabling lihaohong/yazi copr repo..."
sudo dnf copr enable -y lihaohong/yazi
log_info "Installing Yazi (without weak dependencies first)..."
sudo dnf install -y yazi --setopt=install_weak_deps=False
add_rollback_cmd "sudo dnf remove -y yazi"
log_info "Installing weak dependencies subsequently..."
pkg_install yazi
else
log_error "Unsupported distribution."
exit 1
fi
# Ensure required extraction tools are installed
if ! has_command unzip; then
log_info "unzip not found. Installing unzip..."
pkg_install unzip
registry_add_sys_deps "yazi" "unzip"
fi
local arch
arch=$(detect_arch)
local target=""
case "$arch" in
x86_64) target="x86_64-unknown-linux-gnu" ;;
arm64) target="aarch64-unknown-linux-gnu" ;;
*) log_error "Unsupported architecture: $arch"; exit 1 ;;
esac
log_info "Fetching latest Yazi version from GitHub..."
local latest_tag=""
latest_tag=$(github_get_latest_release "sxyazi/yazi")
if [ -z "$latest_tag" ]; then
latest_tag="v0.3.3"
log_warn "Failed to fetch latest version from GitHub. Falling back to: $latest_tag"
fi
log_info "Downloading Yazi ${latest_tag}..."
local archive="$TMP_DIR/yazi.zip"
github_download_asset "sxyazi/yazi" "$latest_tag" "yazi-${target}\.zip" "$archive"
log_info "Extracting Yazi binaries..."
unzip -q "$archive" -d "$TMP_DIR"
local extract_dir="$TMP_DIR/yazi-${target}"
local target_dir="$BOOTSTRAP_BIN"
mkdir -p "$target_dir"
log_info "Installing Yazi to $target_dir..."
cp "$extract_dir/yazi" "$target_dir/yazi"
cp "$extract_dir/ya" "$target_dir/ya"
chmod +x "$target_dir/yazi" "$target_dir/ya"
track_file "$target_dir/yazi"
track_file "$target_dir/ya"
log_info "Installing system dependencies for Yazi..."
pkg_install ffmpeg jq ripgrep fzf zoxide resvg imagemagick "arch:7zip|debian:7zip|fedora:p7zip" "arch:poppler|debian:poppler-utils|fedora:poppler-utils" "arch:fd|debian:fd-find|fedora:fd-find"
create_fd_symlink
register_tool "yazi" "binary" "$latest_tag" "github:sxyazi/yazi"
# Add the system dependencies to the registry for uninstallation tracking
registry_add_sys_deps "yazi" ffmpeg jq ripgrep fzf zoxide resvg imagemagick "arch:7zip|debian:7zip|fedora:p7zip" "arch:poppler|debian:poppler-utils|fedora:poppler-utils" "arch:fd|debian:fd-find|fedora:fd-find"
}
main() {

View File

@@ -1,25 +1,15 @@
#!/usr/bin/env bash
# shellcheck disable=SC2016
# Tool: zoxide
# DisplayName: Zoxide
# Description: Install Zoxide directory jumper
# Strategy: managed
#
# Zoxide Installer Script
#
# Prevent standalone execution
if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then
echo "Error: This script must be run through the 'b' CLI." >&2
exit 1
fi
set -euo pipefail
install_curl() {
if ! has_command curl; then
log_info "curl not found. Installing curl..."
pkg_install curl
fi
}
install_fzf() {
if has_command fzf; then
@@ -29,6 +19,7 @@ install_fzf() {
log_info "fzf not found. Installing fzf..."
pkg_install fzf
registry_add_sys_deps "zoxide" "fzf"
}
install_zoxide() {
@@ -36,24 +27,17 @@ install_zoxide() {
log_info "Zoxide is already installed."
fi
install_curl
log_info "Downloading and running the official zoxide installer..."
curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh
track_file "$HOME/.local/bin/zoxide"
register_tool "zoxide" "managed" "" "github:ajeetdsouza/zoxide"
}
configure_shell() {
# Add ~/.local/bin to PATH for the current process
export PATH="$HOME/.local/bin:$PATH"
# Clean up legacy in-place configuration blocks
IFS=' ' read -ra target_files <<< "$(get_shell_configs)"
for config_file in "${target_files[@]}"; do
remove_block "$config_file" "zoxide init"
done
write_env_snippet "local-bin" 'export PATH="$HOME/.local/bin:$PATH"'
write_env_snippet "zoxide" 'eval "$(zoxide init --cmd cd bash)"'
}