diff --git a/.agent/scripts/tool-version-check.sh b/.agent/scripts/tool-version-check.sh index 408971c26..5f15462c6 100755 --- a/.agent/scripts/tool-version-check.sh +++ b/.agent/scripts/tool-version-check.sh @@ -1,9 +1,14 @@ #!/bin/bash # Tool Version Check -# Checks versions of key MCP tools and flags outdated ones +# Checks versions of key tools and flags outdated ones # -# Usage: tool-version-check.sh [--update] -# --update Automatically update outdated tools +# Usage: +# tool-version-check.sh # Check all tools +# tool-version-check.sh --update # Check and update outdated tools +# tool-version-check.sh --category npm # Check only npm tools +# tool-version-check.sh --json # Output as JSON +# +# Categories: npm, brew, pip, all (default) set -euo pipefail @@ -12,86 +17,340 @@ readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly RED='\033[0;31m' readonly BLUE='\033[0;34m' +readonly CYAN='\033[0;36m' +readonly BOLD='\033[1m' readonly NC='\033[0m' -AUTO_UPDATE="${1:-}" +# Parse arguments +AUTO_UPDATE=false +CATEGORY="all" +JSON_OUTPUT=false +QUIET=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --update|-u) + AUTO_UPDATE=true + shift + ;; + --category|-c) + if [[ -z "${2:-}" ]]; then + echo "Error: --category requires a value (npm, brew, pip, all)" + exit 1 + fi + CATEGORY="$2" + shift 2 + ;; + --json|-j) + JSON_OUTPUT=true + shift + ;; + --quiet|-q) + QUIET=true + shift + ;; + --help|-h) + echo "Usage: tool-version-check.sh [OPTIONS]" + echo "" + echo "Options:" + echo " --update, -u Automatically update outdated tools" + echo " --category, -c Check only specific category (npm, brew, pip, all)" + echo " --json, -j Output results as JSON" + echo " --quiet, -q Only show outdated tools" + echo " --help, -h Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Tool definitions +# Format: category|display_name|cli_command|version_flag|package_name|update_command -# Tools to check: display_name|cli_command|version_flag|npm_package -# Version flag can be --version or -V or custom -TOOLS=( - "osgrep|osgrep|--version|osgrep" - "augment|auggie|--version|@augmentcode/auggie" - "repomix|repomix|--version|repomix" - "stagehand|stagehand|--version|@anthropic-ai/stagehand" +NPM_TOOLS=( + "npm|osgrep|osgrep|--version|osgrep|npm update -g osgrep" + "npm|Augment CLI|auggie|--version|@augmentcode/auggie@prerelease|npm update -g @augmentcode/auggie@prerelease" + "npm|Repomix|repomix|--version|repomix|npm update -g repomix" + "npm|DSPyGround|dspyground|--version|dspyground|npm update -g dspyground" + "npm|LocalWP MCP|mcp-local-wp|--version|@verygoodplugins/mcp-local-wp|npm update -g @verygoodplugins/mcp-local-wp" + "npm|Beads UI|beads-ui|--version|beads-ui|npm update -g beads-ui" + "npm|BDUI|bdui|--version|bdui|npm update -g bdui" + "npm|OpenCode|opencode|--version|opencode|npm update -g opencode" ) -echo -e "${BLUE}=== Tool Version Check ===${NC}" -echo "" +BREW_TOOLS=( + "brew|GitHub CLI|gh|--version|gh|brew upgrade gh" + "brew|GitLab CLI|glab|--version|glab|brew upgrade glab" + "brew|Beads CLI|bd|version|steveyegge/beads/bd|brew upgrade steveyegge/beads/bd" + "brew|jq|jq|--version|jq|brew upgrade jq" + "brew|ShellCheck|shellcheck|--version|shellcheck|brew upgrade shellcheck" +) +PIP_TOOLS=( + "pip|Beads Viewer|beads_viewer|--version|beads-viewer|pip install --upgrade beads-viewer" + "pip|DSPy|dspy|--version|dspy-ai|pip install --upgrade dspy-ai" + "pip|Crawl4AI|crawl4ai|--version|crawl4ai|pip install --upgrade crawl4ai" +) + +# Counters OUTDATED_COUNT=0 +INSTALLED_COUNT=0 +NOT_INSTALLED_COUNT=0 declare -a OUTDATED_PACKAGES=() +declare -a JSON_RESULTS=() -for tool_spec in "${TOOLS[@]}"; do - IFS='|' read -r name cmd ver_flag npm_pkg <<< "$tool_spec" +# Get installed version +get_installed_version() { + local cmd="$1" + local ver_flag="$2" - # Get installed version if command -v "$cmd" &>/dev/null; then - installed=$("$cmd" "$ver_flag" 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown") + local version + # shellcheck disable=SC2086 + version=$("$cmd" $ver_flag 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "") + if [[ -z "$version" ]]; then + # Try alternative patterns + # shellcheck disable=SC2086 + version=$("$cmd" $ver_flag 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+' | head -1 || echo "unknown") + fi + echo "$version" + else + echo "not installed" + fi +} + +# Get latest npm version +get_npm_latest() { + local pkg="$1" + npm view "$pkg" version 2>/dev/null || echo "unknown" +} + +# Get latest brew version +get_brew_latest() { + local pkg="$1" + if command -v brew &>/dev/null; then + brew info "$pkg" 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown" else - installed="not installed" + echo "unknown" fi +} + +# Get latest pip version +get_pip_latest() { + local pkg="$1" + pip index versions "$pkg" 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown" +} + +# Compare versions (returns 0 if v1 < v2) +version_lt() { + local v1="$1" + local v2="$2" + + if [[ "$v1" == "$v2" ]]; then + return 1 + fi + + # Use sort -V for version comparison + local lowest + lowest=$(printf '%s\n%s' "$v1" "$v2" | sort -V | head -1) + [[ "$lowest" == "$v1" ]] +} + +# Check a single tool +check_tool() { + local category="$1" + local name="$2" + local cmd="$3" + local ver_flag="$4" + local pkg="$5" + local update_cmd="$6" - # Get latest version from npm - latest=$(npm view "$npm_pkg" version 2>/dev/null || echo "unknown") + local installed + installed=$(get_installed_version "$cmd" "$ver_flag") + + local latest="unknown" + case "$category" in + npm) latest=$(get_npm_latest "$pkg") ;; + brew) latest=$(get_brew_latest "$pkg") ;; + pip) latest=$(get_pip_latest "$pkg") ;; + esac + + local status="up_to_date" + local icon="✓" + local color="$GREEN" - # Compare and report if [[ "$installed" == "not installed" ]]; then - echo -e "${YELLOW}⚠️ $name: not installed${NC}" - echo " Latest: $latest" - echo " Install: npm install -g $npm_pkg" - elif [[ "$installed" == "$latest" ]]; then - echo -e "${GREEN}✓ $name: $installed (up to date)${NC}" - elif [[ "$latest" == "unknown" ]]; then - echo -e "${YELLOW}? $name: $installed (could not check latest)${NC}" - else - echo -e "${RED}⬆️ $name: $installed → $latest (UPDATE AVAILABLE)${NC}" + status="not_installed" + icon="○" + color="$YELLOW" + ((NOT_INSTALLED_COUNT++)) || true + elif [[ "$installed" == "unknown" || "$latest" == "unknown" ]]; then + status="unknown" + icon="?" + color="$YELLOW" + ((INSTALLED_COUNT++)) || true + elif [[ "$installed" != "$latest" ]] && version_lt "$installed" "$latest"; then + status="outdated" + icon="⬆" + color="$RED" ((OUTDATED_COUNT++)) || true - OUTDATED_PACKAGES+=("$npm_pkg") + OUTDATED_PACKAGES+=("$update_cmd") + else + ((INSTALLED_COUNT++)) || true fi -done - -echo "" + + # JSON output (escape special characters for valid JSON) + if [[ "$JSON_OUTPUT" == "true" ]]; then + # Escape backslashes and double quotes for JSON safety + local json_name="${name//\\/\\\\}" + json_name="${json_name//\"/\\\"}" + local json_update="${update_cmd//\\/\\\\}" + json_update="${json_update//\"/\\\"}" + JSON_RESULTS+=("{\"name\":\"$json_name\",\"category\":\"$category\",\"installed\":\"$installed\",\"latest\":\"$latest\",\"status\":\"$status\",\"update_cmd\":\"$json_update\"}") + else + # Console output + if [[ "$QUIET" == "true" && "$status" != "outdated" ]]; then + return + fi + + case "$status" in + not_installed) + echo -e "${color}${icon} $name: not installed${NC}" + if [[ "$QUIET" != "true" ]]; then + echo " Latest: $latest" + fi + ;; + outdated) + echo -e "${color}${icon} $name: $installed → $latest (UPDATE AVAILABLE)${NC}" + ;; + unknown) + echo -e "${color}${icon} $name: $installed (could not check latest)${NC}" + ;; + up_to_date) + echo -e "${color}${icon} $name: $installed${NC}" + ;; + esac + fi +} -# Determine package manager (prefer bun) -if command -v bun &> /dev/null; then - PKG_MGR="bun" - PKG_UPDATE="bun update -g" -else - PKG_MGR="npm" - PKG_UPDATE="npm update -g" -fi +# Check tools by category +check_category() { + local cat_name="$1" + shift + local tools=("$@") + + if [[ "$JSON_OUTPUT" != "true" && "$QUIET" != "true" ]]; then + echo "" + echo -e "${BOLD}${CYAN}=== $cat_name Tools ===${NC}" + fi + + for tool_spec in "${tools[@]}"; do + IFS='|' read -r category name cmd ver_flag pkg update_cmd <<< "$tool_spec" + check_tool "$category" "$name" "$cmd" "$ver_flag" "$pkg" "$update_cmd" + done +} -if [[ $OUTDATED_COUNT -gt 0 ]]; then - echo -e "${YELLOW}Found $OUTDATED_COUNT outdated tool(s)${NC}" - echo "" +# Main +main() { + if [[ "$JSON_OUTPUT" != "true" && "$QUIET" != "true" ]]; then + echo -e "${BOLD}${BLUE}Tool Version Check${NC}" + echo "==================" + fi - if [[ "$AUTO_UPDATE" == "--update" ]]; then - echo "Updating outdated tools using $PKG_MGR..." - for pkg in "${OUTDATED_PACKAGES[@]}"; do - echo " Updating $pkg..." - $PKG_UPDATE "$pkg" 2>&1 | tail -1 + # Check requested categories + case "$CATEGORY" in + npm) + check_category "NPM" "${NPM_TOOLS[@]}" + ;; + brew) + check_category "Homebrew" "${BREW_TOOLS[@]}" + ;; + pip) + check_category "Python/Pip" "${PIP_TOOLS[@]}" + ;; + all|*) + if [[ ${#NPM_TOOLS[@]} -gt 0 ]]; then + check_category "NPM" "${NPM_TOOLS[@]}" + fi + if command -v brew &>/dev/null && [[ ${#BREW_TOOLS[@]} -gt 0 ]]; then + check_category "Homebrew" "${BREW_TOOLS[@]}" + fi + if command -v pip &>/dev/null && [[ ${#PIP_TOOLS[@]} -gt 0 ]]; then + check_category "Python/Pip" "${PIP_TOOLS[@]}" + fi + ;; + esac + + # Output results + if [[ "$JSON_OUTPUT" == "true" ]]; then + echo "{" + echo " \"summary\": {" + echo " \"installed\": $INSTALLED_COUNT," + echo " \"outdated\": $OUTDATED_COUNT," + echo " \"not_installed\": $NOT_INSTALLED_COUNT" + echo " }," + echo " \"tools\": [" + local first=true + for result in "${JSON_RESULTS[@]}"; do + if [[ "$first" == "true" ]]; then + first=false + else + echo "," + fi + echo -n " $result" done echo "" - echo -e "${GREEN}Updates complete. Re-run to verify.${NC}" - else - echo "To update all outdated tools, run:" - echo " tool-version-check.sh --update" + echo " ]" + echo "}" + return 0 + fi + + # Summary (skip in quiet mode if nothing outdated) + if [[ "$QUIET" == "true" && $OUTDATED_COUNT -eq 0 ]]; then + return 0 + fi + + if [[ "$QUIET" != "true" ]]; then + echo "" + echo -e "${BOLD}Summary${NC}" + echo " Installed & up to date: $INSTALLED_COUNT" + echo " Outdated: $OUTDATED_COUNT" + echo " Not installed: $NOT_INSTALLED_COUNT" echo "" - echo "Or update individually:" - for pkg in "${OUTDATED_PACKAGES[@]}"; do - echo " $PKG_UPDATE $pkg" - done fi -else - echo -e "${GREEN}All tools are up to date!${NC}" -fi + + # Handle updates + if [[ $OUTDATED_COUNT -gt 0 ]]; then + if [[ "$AUTO_UPDATE" == "true" ]]; then + echo -e "${BLUE}Updating outdated tools...${NC}" + echo "" + for update_cmd in "${OUTDATED_PACKAGES[@]}"; do + echo " Running: $update_cmd" + # Run update command directly (not via eval for security) + # Commands are hardcoded in tool definitions, not user input + if bash -c "$update_cmd" 2>&1 | tail -2; then + echo -e " ${GREEN}✓ Updated${NC}" + else + echo -e " ${RED}✗ Failed${NC}" + fi + echo "" + done + echo -e "${GREEN}Updates complete. Re-run to verify.${NC}" + else + echo "To update all outdated tools, run:" + echo " tool-version-check.sh --update" + echo "" + echo "Or update individually:" + for update_cmd in "${OUTDATED_PACKAGES[@]}"; do + echo " $update_cmd" + done + fi + else + echo -e "${GREEN}All installed tools are up to date!${NC}" + fi +} + +main diff --git a/aidevops.sh b/aidevops.sh index a64811c0b..664ebf192 100755 --- a/aidevops.sh +++ b/aidevops.sh @@ -809,6 +809,24 @@ cmd_features() { echo "" } +# Update tools command - check and update installed tools +# Passes all arguments through to tool-version-check.sh +cmd_update_tools() { + print_header "Tool Version Check" + echo "" + + local tool_check_script="$AGENTS_DIR/scripts/tool-version-check.sh" + + if [[ ! -f "$tool_check_script" ]]; then + print_error "Tool version check script not found" + print_info "Run 'aidevops update' first to get the latest scripts" + return 1 + fi + + # Pass all arguments through to the script + bash "$tool_check_script" "$@" +} + # Help command cmd_help() { local version @@ -822,7 +840,8 @@ cmd_help() { echo " init [features] Initialize aidevops in current project" echo " features List available features for init" echo " status Check installation status of all components" - echo " update Update to the latest version (alias: upgrade)" + echo " update Update aidevops to the latest version" + echo " update-tools Check for outdated tools (--update to auto-update)" echo " uninstall Remove aidevops from your system" echo " version Show version information" echo " help Show this help message" @@ -832,7 +851,9 @@ cmd_help() { echo " aidevops init planning # Initialize with planning only" echo " aidevops features # List available features" echo " aidevops status # Check what's installed" - echo " aidevops update # Update to latest version" + echo " aidevops update # Update aidevops to latest version" + echo " aidevops update-tools # Check for outdated tools" + echo " aidevops update-tools -u # Update all outdated tools" echo " aidevops uninstall # Remove aidevops" echo "" echo "Quick install:" @@ -873,6 +894,10 @@ main() { update|upgrade|u) cmd_update ;; + update-tools|tools) + shift + cmd_update_tools "$@" + ;; uninstall|remove) cmd_uninstall ;; diff --git a/setup.sh b/setup.sh index facddf55e..d95f1c001 100755 --- a/setup.sh +++ b/setup.sh @@ -19,6 +19,7 @@ NC='\033[0m' # No Color # Global flags CLEAN_MODE=false INTERACTIVE_MODE=false +UPDATE_TOOLS_MODE=false REPO_URL="https://github.com/marcusquinn/aidevops.git" INSTALL_DIR="$HOME/Git/aidevops" @@ -2025,6 +2026,62 @@ setup_seo_mcps() { return 0 } +# Check for tool updates after setup +check_tool_updates() { + print_info "Checking for tool updates..." + + local tool_check_script="$HOME/.aidevops/agents/scripts/tool-version-check.sh" + + if [[ ! -f "$tool_check_script" ]]; then + # Try local script if deployed version not available yet + tool_check_script=".agent/scripts/tool-version-check.sh" + fi + + if [[ ! -f "$tool_check_script" ]]; then + print_warning "Tool version check script not found - skipping update check" + return 0 + fi + + # Run the check in quiet mode first to see if there are updates + # Capture both output and exit code + local outdated_output + local check_exit_code + outdated_output=$(bash "$tool_check_script" --quiet 2>&1) || check_exit_code=$? + check_exit_code=${check_exit_code:-0} + + # If the script failed, warn and continue + if [[ $check_exit_code -ne 0 ]]; then + print_warning "Tool version check encountered an error (exit code: $check_exit_code)" + print_info "Run 'aidevops update-tools' manually to check for updates" + return 0 + fi + + if [[ -z "$outdated_output" ]]; then + print_success "All tools are up to date!" + return 0 + fi + + # Show what's outdated + echo "" + print_warning "Some tools have updates available:" + echo "" + bash "$tool_check_script" --quiet + echo "" + + read -r -p "Update all outdated tools now? (y/n): " do_update + + if [[ "$do_update" == "y" || "$do_update" == "Y" ]]; then + print_info "Updating tools..." + bash "$tool_check_script" --update + print_success "Tool updates complete!" + else + print_info "Skipped tool updates" + print_info "Run 'aidevops update-tools' anytime to update tools" + fi + + return 0 +} + # Parse command line arguments parse_args() { while [[ $# -gt 0 ]]; do @@ -2037,6 +2094,10 @@ parse_args() { INTERACTIVE_MODE=true shift ;; + --update|-u) + UPDATE_TOOLS_MODE=true + shift + ;; --help|-h) echo "Usage: ./setup.sh [OPTIONS]" echo "" @@ -2044,11 +2105,14 @@ parse_args() { echo " --clean Remove stale files before deploying (cleans ~/.aidevops/agents/)" echo " --interactive Ask confirmation before each step" echo " -i Short for --interactive" + echo " --update Check for and offer to update outdated tools after setup" + echo " -u Short for --update" echo " --help Show this help message" echo "" echo "Default behavior adds/overwrites files without removing deleted agents." echo "Use --clean after removing or renaming agents to sync deletions." echo "Use --interactive to control each step individually." + echo "Use --update to check for tool updates after setup completes." exit 0 ;; *) @@ -2077,6 +2141,9 @@ main() { echo "" echo "Controls: [Y]es (default) / [n]o skip / [q]uit" fi + if [[ "$UPDATE_TOOLS_MODE" == "true" ]]; then + echo "Mode: Update (will check for tool updates after setup)" + fi echo "" # Required steps (always run) @@ -2117,11 +2184,12 @@ main() { print_success "🎉 Setup complete!" echo "" echo "CLI Command:" -echo " aidevops init - Initialize aidevops in a project" -echo " aidevops features - List available features" -echo " aidevops status - Check installation status" -echo " aidevops update - Update to latest version" -echo " aidevops uninstall - Remove aidevops" +echo " aidevops init - Initialize aidevops in a project" +echo " aidevops features - List available features" +echo " aidevops status - Check installation status" +echo " aidevops update - Update to latest version" +echo " aidevops update-tools - Check for and update installed tools" +echo " aidevops uninstall - Remove aidevops" echo "" echo "Deployed to:" echo " ~/.aidevops/agents/ - Agent files (main agents, subagents, scripts)" @@ -2176,6 +2244,12 @@ echo " aidevops uninstall - Remove aidevops" echo "Happy server managing! 🚀" echo "" + # Check for tool updates if --update flag was passed + if [[ "$UPDATE_TOOLS_MODE" == "true" ]]; then + echo "" + check_tool_updates + fi + # Offer to launch onboarding for new users (only if not running inside OpenCode) if [[ -z "${OPENCODE_SESSION:-}" ]] && command -v opencode &>/dev/null; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"