diff --git a/.agents/skills/add_installer/SKILL.md b/.agents/skills/add_installer/SKILL.md index 0e2e029..78f99fe 100644 --- a/.agents/skills/add_installer/SKILL.md +++ b/.agents/skills/add_installer/SKILL.md @@ -9,7 +9,7 @@ This skill provides everything needed to add a new installer to the bootstrap pr ## Project Overview -Bootstrap CLI (`b`) is a bash-based tool installer and system bootstrapper. Users run `b ` to install tools (e.g., `b nvim`, `b bat`). The project lives at the workspace root. +Bootstrap CLI (`b`) is a bash-based tool installer and system bootstrapper. Users run `b ` or `b ware ` to install or edit tools (e.g., `b nvim`, `b ware bat`). The project lives at the workspace root. ### Key Directories @@ -54,7 +54,10 @@ Make **two** edits to `routes.sh`: ### Step 3: Verify (optional) -Run `bash routes.sh` or `b all` to confirm the new installer appears in the help output. +Verify that the installer works and appears in the help output: +- Run `b all` to confirm it appears in the help list. +- Run `b ware -y` to test direct installation. +- Run `b ware ` to test the interactive editing flow. --- diff --git a/VERSION b/VERSION index bb83058..524cb55 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.12 +1.1.1 diff --git a/b.sh b/b.sh index 208d6f0..9d9b7d4 100755 --- a/b.sh +++ b/b.sh @@ -77,7 +77,7 @@ _b_completion() { # If completing the first argument after 'b' if [ "$COMP_CWORD" -eq 1 ]; then - opts="all con bye up" + opts="all con bye up ware" local routes_file="$HOME/.config/bootstrap/routes.sh" if [ -f "$routes_file" ]; then @@ -111,6 +111,36 @@ _b_completion() { return 0 fi + # If completing arguments for 'b ware ' + if [ "$COMP_CWORD" -eq 2 ] && [ "$prev" = "ware" ]; then + local routes_file="$HOME/.config/bootstrap/routes.sh" + local installer_keys="" + if [ -f "$routes_file" ]; then + installer_keys=$(grep -E "^INSTALLER_KEYS=" "$routes_file" 2>/dev/null | sed -E 's/INSTALLER_KEYS=\(([^)]+)\)/\1/' 2>/dev/null) + fi + [ -z "$installer_keys" ] && installer_keys="agy bat node nvim pnpm rust starship yay yazi zoxide" + + # Support comma-separated completions (e.g. b ware nvim,ya) + if [[ "$cur" == *,* ]]; then + local prefix="${cur%,*}" + local last="${cur##*,}" + local matches + matches=$(compgen -W "$installer_keys" -- "$last") + if [ -n "$matches" ]; then + COMPREPLY=() + for m in $matches; do + if [[ ",$prefix," != *",$m,"* ]]; then + COMPREPLY+=("${prefix},${m}") + fi + done + fi + return 0 + fi + + COMPREPLY=( $(compgen -W "$installer_keys" -- "$cur") ) + return 0 + fi + # If completing arguments for 'b con ' if [ "$COMP_CWORD" -eq 2 ] && [ "$prev" = "con" ]; then # List of directories in ~/.config/ to choose from diff --git a/commands/help.sh b/commands/help.sh index a51a7d8..31277d4 100644 --- a/commands/help.sh +++ b/commands/help.sh @@ -6,6 +6,7 @@ echo "Available bootstrap commands:" printf " %-6s - %s\n" "all" "List all available commands" printf " %-6s - %s\n" "con" "Edit config (e.g. b con nvim)" printf " %-6s - %s\n" "up" "Check for updates and update Bootstrap CLI" +printf " %-6s - %s\n" "ware" "Edit and run an installer (e.g. b ware nvim)" printf " %-6s - %s\n" "bye" "Uninstall Bootstrap CLI helper" # Installers second diff --git a/installers/install_agy.sh b/installers/install_agy.sh index b4c1034..34dffd4 100644 --- a/installers/install_agy.sh +++ b/installers/install_agy.sh @@ -32,15 +32,6 @@ install_agy() { if [ -f "$BINARY_PATH" ]; then log_info "Notice: 'agy' is already installed at $BINARY_PATH." log_info "The Antigravity CLI automatically self-updates in the background during regular runs." - if ! confirm "Do you want to perform a fresh reinstall/upgrade anyway?"; then - log_info "Skipping Antigravity CLI installation." - return 0 - fi - else - if ! confirm "Install Antigravity CLI (agy)?"; then - log_info "Skipping Antigravity CLI installation." - return 0 - fi fi # Detect Architecture (map uname -m to amd64 / arm64) diff --git a/installers/install_bat.sh b/installers/install_bat.sh index 0f8f831..0108d77 100644 --- a/installers/install_bat.sh +++ b/installers/install_bat.sh @@ -35,51 +35,16 @@ install_bat() { if [ "$distro" = "arch" ]; then log_info "Arch Linux detected" - if has_command bat; then - if ! confirm "Bat is already installed. Reinstall/Upgrade?"; then - log_info "Skipping Bat installation." - return - fi - else - if ! confirm "Install Bat?"; then - log_info "Skipping Bat installation." - return - fi - fi - log_info "Installing Bat..." pkg_install bat elif [ "$distro" = "fedora" ]; then log_info "Fedora detected" - if has_command bat; then - if ! confirm "Bat is already installed. Reinstall/Upgrade?"; then - log_info "Skipping Bat installation." - return - fi - else - if ! confirm "Install Bat?"; then - log_info "Skipping Bat installation." - return - fi - fi - log_info "Installing Bat..." pkg_install bat elif [ "$distro" = "debian" ]; then log_info "Debian/Ubuntu detected" - if has_command bat; then - if ! confirm "Bat is already installed. Reinstall/Upgrade?"; then - log_info "Skipping Bat installation." - return - fi - else - if ! confirm "Install Bat?"; then - log_info "Skipping Bat installation." - return - fi - fi pkg_install curl wget diff --git a/installers/install_node.sh b/installers/install_node.sh index 4a92371..49ee60a 100644 --- a/installers/install_node.sh +++ b/installers/install_node.sh @@ -31,15 +31,7 @@ trap cleanup EXIT install_nvm() { if has_command nvm || [ -s "$HOME/.nvm/nvm.sh" ]; then - if ! confirm "NVM is already installed. Reinstall/Upgrade?"; then - log_info "Skipping NVM installation." - return 0 - fi - else - if ! confirm "Install NVM (Node Version Manager)?"; then - log_info "Skipping NVM installation." - return 0 - fi + log_info "NVM is already installed." fi # Ensure required commands are installed @@ -116,17 +108,6 @@ install_node() { if has_command node; then log_info "Currently installed Node.js version: $(node --version)" - if ! confirm "Install/Update to latest Node.js LTS version?"; then - log_info "Skipping Node.js installation." - set -u - return 0 - fi - else - if ! confirm "Install Node.js LTS version?"; then - log_info "Skipping Node.js installation." - set -u - return 0 - fi fi log_info "Installing Node.js LTS version..." diff --git a/installers/install_nvim.sh b/installers/install_nvim.sh index ec8fafc..2509721 100644 --- a/installers/install_nvim.sh +++ b/installers/install_nvim.sh @@ -35,27 +35,8 @@ cleanup() { trap cleanup EXIT check_config_dir() { - if [[ -d "$NVIM_CONFIG_DIR" ]]; then - if confirm "$NVIM_CONFIG_DIR already exists. Replace it?"; then - log_info "Existing configuration will be removed during setup." - rm -rf "$NVIM_CONFIG_DIR" - else - while true; do - read -r -p "Enter an alternative directory to clone the configuration into: " alt_dir Neovim) + local display_name="$(echo "${tool:0:1}" | tr '[:lower:]' '[:upper:]')${tool:1}" + + # Check for local installer first + local local_installer="$BOOTSTRAP_DIR/installers/install_${tool}.sh" + local temp_script + temp_script=$(mktemp --suffix=".sh" 2>/dev/null || mktemp) + + if [ -f "$local_installer" ]; then + cp "$local_installer" "$temp_script" + else + BOOTSTRAP_BASE_URL="${BOOTSTRAP_BASE_URL:-https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master}" + BOOTSTRAP_FALLBACK_URL="${BOOTSTRAP_FALLBACK_URL:-https://raw.githubusercontent.com/sortedcord/bootstrap/refs/heads/master}" + local installer_path="installers/install_${tool}.sh" + local download_status=0 + + log_info "Downloading ${display_name} installer..." + if has_command curl; then + curl -fsSL "${BOOTSTRAP_BASE_URL}/${installer_path}" -o "$temp_script" + download_status=$? + if [ "$download_status" -ne 0 ]; then + log_warn "Failed to download installer from primary URL, trying fallback..." + curl -fsSL "${BOOTSTRAP_FALLBACK_URL}/${installer_path}" -o "$temp_script" + download_status=$? + fi + elif has_command wget; then + wget -qO "$temp_script" "${BOOTSTRAP_BASE_URL}/${installer_path}" + download_status=$? + if [ "$download_status" -ne 0 ]; then + log_warn "Failed to download installer from primary URL, trying fallback..." + wget -qO "$temp_script" "${BOOTSTRAP_FALLBACK_URL}/${installer_path}" + download_status=$? + fi + else + log_error "Neither curl nor wget is installed to download the installer." + rm -f "$temp_script" + exit 1 + fi + + if [ "$download_status" -ne 0 ]; then + log_error "Failed to download the installer from both primary and fallback URLs." + rm -f "$temp_script" + exit 1 + fi + fi + + # Edit if bypass_edit is false + if [ "$bypass_edit" = "false" ]; then + local editor="${EDITOR:-}" + if [ -z "$editor" ]; then + if has_command nvim; then + editor="nvim" + elif has_command vim; then + editor="vim" + elif has_command nano; then + editor="nano" + else + editor="vi" + fi + fi + log_info "Opening ${display_name} installer for editing using $editor..." + $editor "$temp_script" + fi + + # Run the script (edited or unchanged) + log_info "Running ${display_name} installer..." + bash "$temp_script" "${cmd_args[@]}" + local run_status=$? + + # Cleanup + rm -f "$temp_script" + return "$run_status" +} + SCRIPT_NAMES="${1:-}" if [ -z "$SCRIPT_NAMES" ] || [ "$SCRIPT_NAMES" = "-h" ] || [ "$SCRIPT_NAMES" = "--help" ]; then SCRIPT_NAMES="all" @@ -47,46 +137,7 @@ IFS=',' read -ra SCRIPTS <<< "$SCRIPT_NAMES" for script in "${SCRIPTS[@]}"; do # Check if it is a registered installer if [[ -n "${INSTALLERS[$script]:-}" ]]; then - # Capitalize first letter for display (e.g. nvim -> Neovim) - display_name="$(echo "${script:0:1}" | tr '[:lower:]' '[:upper:]')${script:1}" - log_info "Launching ${display_name} installer..." - - # Check for local installer first, fallback to curl - local_installer="$BOOTSTRAP_DIR/installers/install_${script}.sh" - if [ -f "$local_installer" ]; then - bash "$local_installer" "$@" - else - BOOTSTRAP_BASE_URL="${BOOTSTRAP_BASE_URL:-https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master}" - BOOTSTRAP_FALLBACK_URL="${BOOTSTRAP_FALLBACK_URL:-https://raw.githubusercontent.com/sortedcord/bootstrap/refs/heads/master}" - installer_path="installers/install_${script}.sh" - download_status=0 - - if has_command curl; then - curl -fsSL "${BOOTSTRAP_BASE_URL}/${installer_path}" | bash -s -- "$@" - download_status="${PIPESTATUS[0]}" - if [ "$download_status" -ne 0 ]; then - log_warn "Failed to download installer from primary URL, trying fallback..." - curl -fsSL "${BOOTSTRAP_FALLBACK_URL}/${installer_path}" | bash -s -- "$@" - download_status="${PIPESTATUS[0]}" - fi - elif has_command wget; then - wget -qO- "${BOOTSTRAP_BASE_URL}/${installer_path}" | bash -s -- "$@" - download_status="${PIPESTATUS[0]}" - if [ "$download_status" -ne 0 ]; then - log_warn "Failed to download installer from primary URL, trying fallback..." - wget -qO- "${BOOTSTRAP_FALLBACK_URL}/${installer_path}" | bash -s -- "$@" - download_status="${PIPESTATUS[0]}" - fi - else - log_error "Neither curl nor wget is installed to download the installer." - exit 1 - fi - - if [ "$download_status" -ne 0 ]; then - log_error "Failed to download the installer from both primary and fallback URLs." - exit 1 - fi - fi + run_ware "$script" -y "$@" else # Handle non-installer commands @@ -115,6 +166,24 @@ for script in "${SCRIPTS[@]}"; do exit 1 fi ;; + ware) + tools_arg="${1:-}" + if [ -z "$tools_arg" ]; then + log_error "Usage: b ware [-y]" + exit 1 + fi + shift + IFS=',' read -ra WARE_TOOLS <<< "$tools_arg" + for tool in "${WARE_TOOLS[@]}"; do + if [[ -z "${INSTALLERS[$tool]:-}" ]]; then + log_error "Unknown tool '$tool'." + exit 1 + fi + done + for tool in "${WARE_TOOLS[@]}"; do + run_ware "$tool" "$@" + done + ;; bye) if [ -f "$BOOTSTRAP_DIR/commands/uninstall.sh" ]; then . "$BOOTSTRAP_DIR/commands/uninstall.sh"