aidevops 2.100.6 → 2.100.8

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.
Files changed (4) hide show
  1. package/VERSION +1 -1
  2. package/aidevops.sh +1 -1
  3. package/package.json +1 -1
  4. package/setup.sh +200 -134
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.100.6
1
+ 2.100.8
package/aidevops.sh CHANGED
@@ -3,7 +3,7 @@
3
3
  # AI DevOps Framework CLI
4
4
  # Usage: aidevops <command> [options]
5
5
  #
6
- # Version: 2.100.6
6
+ # Version: 2.100.8
7
7
 
8
8
  set -euo pipefail
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidevops",
3
- "version": "2.100.6",
3
+ "version": "2.100.8",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/setup.sh CHANGED
@@ -3,7 +3,7 @@
3
3
  # AI Assistant Server Access Framework Setup Script
4
4
  # Helps developers set up the framework for their infrastructure
5
5
  #
6
- # Version: 2.100.6
6
+ # Version: 2.100.8
7
7
  #
8
8
  # Quick Install (one-liner):
9
9
  # bash <(curl -fsSL https://aidevops.dev/install)
@@ -28,6 +28,41 @@ print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
28
28
  print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
29
29
  print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
30
30
 
31
+ # Spinner for long-running operations
32
+ # Usage: run_with_spinner "Installing package..." command arg1 arg2
33
+ run_with_spinner() {
34
+ local message="$1"
35
+ shift
36
+ local pid
37
+ local spin_chars='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
38
+ local i=0
39
+
40
+ # Start command in background
41
+ "$@" &>/dev/null &
42
+ pid=$!
43
+
44
+ # Show spinner while command runs
45
+ printf "${BLUE}[INFO]${NC} %s " "$message"
46
+ while kill -0 "$pid" 2>/dev/null; do
47
+ printf "\r${BLUE}[INFO]${NC} %s %s" "$message" "${spin_chars:i++%${#spin_chars}:1}"
48
+ sleep 0.1
49
+ done
50
+
51
+ # Check exit status
52
+ wait "$pid"
53
+ local exit_code=$?
54
+
55
+ # Clear spinner and show result
56
+ printf "\r"
57
+ if [[ $exit_code -eq 0 ]]; then
58
+ print_success "$message done"
59
+ else
60
+ print_error "$message failed"
61
+ fi
62
+
63
+ return $exit_code
64
+ }
65
+
31
66
  # Find OpenCode config file (checks multiple possible locations)
32
67
  # Returns: path to config file, or empty string if not found
33
68
  find_opencode_config() {
@@ -377,6 +412,85 @@ disable_ondemand_mcps() {
377
412
  return 0
378
413
  }
379
414
 
415
+ # Validate and repair OpenCode config schema
416
+ # Fixes common issues from manual editing or AI-generated configs:
417
+ # - MCP entries missing "type": "local" field
418
+ # - tools entries as objects {} instead of booleans
419
+ # If invalid, backs up and regenerates using the generator script
420
+ validate_opencode_config() {
421
+ local opencode_config
422
+ opencode_config=$(find_opencode_config) || return 0
423
+
424
+ if [[ ! -f "$opencode_config" ]]; then
425
+ return 0
426
+ fi
427
+
428
+ if ! command -v jq &> /dev/null; then
429
+ return 0
430
+ fi
431
+
432
+ local needs_repair=false
433
+ local issues=""
434
+
435
+ # Check 1: MCP entries must have "type" field (usually "local")
436
+ # Invalid: {"mcp": {"foo": {"command": "..."}}}
437
+ # Valid: {"mcp": {"foo": {"type": "local", "command": "..."}}}
438
+ local mcps_without_type
439
+ mcps_without_type=$(jq -r '.mcp // {} | to_entries[] | select(.value.type == null and .value.command != null) | .key' "$opencode_config" 2>/dev/null | head -5)
440
+ if [[ -n "$mcps_without_type" ]]; then
441
+ needs_repair=true
442
+ issues="${issues}\n - MCP entries missing 'type' field: $(echo "$mcps_without_type" | tr '\n' ', ' | sed 's/,$//')"
443
+ fi
444
+
445
+ # Check 2: tools entries must be booleans, not objects
446
+ # Invalid: {"tools": {"gh_grep": {}}}
447
+ # Valid: {"tools": {"gh_grep": true}}
448
+ local tools_as_objects
449
+ tools_as_objects=$(jq -r '.tools // {} | to_entries[] | select(.value | type == "object") | .key' "$opencode_config" 2>/dev/null | head -5)
450
+ if [[ -n "$tools_as_objects" ]]; then
451
+ needs_repair=true
452
+ issues="${issues}\n - tools entries as objects instead of booleans: $(echo "$tools_as_objects" | tr '\n' ', ' | sed 's/,$//')"
453
+ fi
454
+
455
+ # Check 3: Try to parse with opencode (if available) to catch other schema issues
456
+ if command -v opencode &> /dev/null; then
457
+ local validation_output
458
+ if ! validation_output=$(opencode --version 2>&1); then
459
+ # If opencode fails to start, config might be invalid
460
+ if echo "$validation_output" | grep -q "Configuration is invalid"; then
461
+ needs_repair=true
462
+ issues="${issues}\n - OpenCode reports invalid configuration"
463
+ fi
464
+ fi
465
+ fi
466
+
467
+ if [[ "$needs_repair" == "true" ]]; then
468
+ print_warning "OpenCode config has schema issues:$issues"
469
+
470
+ # Backup the invalid config
471
+ create_backup_with_rotation "$opencode_config" "opencode"
472
+ print_info "Backed up invalid config"
473
+
474
+ # Remove the invalid config so generator creates fresh one
475
+ rm -f "$opencode_config"
476
+
477
+ # Regenerate using the generator script
478
+ local generator_script="$HOME/.aidevops/agents/scripts/generate-opencode-agents.sh"
479
+ if [[ -x "$generator_script" ]]; then
480
+ print_info "Regenerating OpenCode config with correct schema..."
481
+ if "$generator_script" > /dev/null 2>&1; then
482
+ print_success "OpenCode config regenerated successfully"
483
+ else
484
+ print_warning "Config regeneration failed - run manually: $generator_script"
485
+ fi
486
+ else
487
+ print_warning "Generator script not found - run setup.sh again after agents are deployed"
488
+ fi
489
+ fi
490
+
491
+ return 0
492
+ }
493
+
380
494
  # Migrate old config-backups to new per-type backup structure
381
495
  # This runs once to clean up the legacy backup directory
382
496
  migrate_old_backups() {
@@ -660,9 +774,9 @@ check_requirements() {
660
774
  fi
661
775
 
662
776
  echo ""
663
- read -r -p "Install missing dependencies using $pkg_manager? (y/n): " install_deps
777
+ read -r -p "Install missing dependencies using $pkg_manager? [Y/n]: " install_deps
664
778
 
665
- if [[ "$install_deps" == "y" ]]; then
779
+ if [[ "$install_deps" =~ ^[Yy]?$ ]]; then
666
780
  print_info "Installing ${missing_deps[*]}..."
667
781
  if install_packages "$pkg_manager" "${missing_deps[@]}"; then
668
782
  print_success "Dependencies installed successfully"
@@ -699,9 +813,9 @@ check_optional_deps() {
699
813
  pkg_manager=$(detect_package_manager)
700
814
 
701
815
  if [[ "$pkg_manager" != "unknown" ]]; then
702
- read -r -p "Install optional dependencies using $pkg_manager? (y/n): " install_optional
816
+ read -r -p "Install optional dependencies using $pkg_manager? [Y/n]: " install_optional
703
817
 
704
- if [[ "$install_optional" == "y" ]]; then
818
+ if [[ "$install_optional" =~ ^[Yy]?$ ]]; then
705
819
  print_info "Installing ${missing_optional[*]}..."
706
820
  if install_packages "$pkg_manager" "${missing_optional[@]}"; then
707
821
  print_success "Optional dependencies installed"
@@ -755,9 +869,9 @@ setup_git_clis() {
755
869
 
756
870
  if [[ "$pkg_manager" != "unknown" ]]; then
757
871
  echo ""
758
- read -r -p "Install Git CLI tools (${missing_packages[*]}) using $pkg_manager? (y/n): " install_git_clis
872
+ read -r -p "Install Git CLI tools (${missing_packages[*]}) using $pkg_manager? [Y/n]: " install_git_clis
759
873
 
760
- if [[ "$install_git_clis" == "y" ]]; then
874
+ if [[ "$install_git_clis" =~ ^[Yy]?$ ]]; then
761
875
  print_info "Installing ${missing_packages[*]}..."
762
876
  if install_packages "$pkg_manager" "${missing_packages[@]}"; then
763
877
  print_success "Git CLI tools installed"
@@ -853,10 +967,10 @@ setup_file_discovery_tools() {
853
967
  if [[ "$pkg_manager" != "unknown" ]]; then
854
968
  local install_fd_tools="y"
855
969
  if [[ "$INTERACTIVE_MODE" == "true" ]]; then
856
- read -r -p "Install file discovery tools (${missing_packages[*]}) using $pkg_manager? (y/n): " install_fd_tools
970
+ read -r -p "Install file discovery tools (${missing_packages[*]}) using $pkg_manager? [Y/n]: " install_fd_tools
857
971
  fi
858
972
 
859
- if [[ "$install_fd_tools" == "y" ]]; then
973
+ if [[ "$install_fd_tools" =~ ^[Yy]?$ ]]; then
860
974
  print_info "Installing ${missing_packages[*]}..."
861
975
 
862
976
  # Handle package name differences across package managers
@@ -959,8 +1073,8 @@ setup_worktrunk() {
959
1073
  if [[ -n "$shell_rc" ]] && [[ -f "$shell_rc" ]]; then
960
1074
  if ! grep -q "worktrunk" "$shell_rc" 2>/dev/null; then
961
1075
  print_info "Shell integration not detected"
962
- read -r -p "Install Worktrunk shell integration (enables 'wt switch' to change directories)? (y/n): " install_shell
963
- if [[ "$install_shell" == "y" ]]; then
1076
+ read -r -p "Install Worktrunk shell integration (enables 'wt switch' to change directories)? [Y/n]: " install_shell
1077
+ if [[ "$install_shell" =~ ^[Yy]?$ ]]; then
964
1078
  if wt config shell install 2>/dev/null; then
965
1079
  print_success "Shell integration installed"
966
1080
  print_info "Restart your terminal or run: source $shell_rc"
@@ -989,25 +1103,15 @@ setup_worktrunk() {
989
1103
  pkg_manager=$(detect_package_manager)
990
1104
 
991
1105
  if [[ "$pkg_manager" == "brew" ]]; then
992
- read -r -p "Install Worktrunk via Homebrew? (y/n): " install_wt
1106
+ read -r -p "Install Worktrunk via Homebrew? [Y/n]: " install_wt
993
1107
 
994
- if [[ "$install_wt" == "y" ]]; then
995
- print_info "Installing Worktrunk..."
996
- if brew install max-sixty/worktrunk/wt 2>/dev/null; then
997
- print_success "Worktrunk installed"
998
-
1108
+ if [[ "$install_wt" =~ ^[Yy]?$ ]]; then
1109
+ if run_with_spinner "Installing Worktrunk via Homebrew" brew install max-sixty/worktrunk/wt; then
999
1110
  # Install shell integration
1000
- print_info "Installing shell integration..."
1001
- local wt_shell_output
1002
- if wt_shell_output=$(wt config shell install 2>&1); then
1003
- print_success "Shell integration installed"
1111
+ if run_with_spinner "Installing shell integration" wt config shell install; then
1004
1112
  print_info "Restart your terminal or source your shell config"
1005
1113
  else
1006
- # Show the actual error for debugging
1007
1114
  print_warning "Shell integration failed - run manually: wt config shell install"
1008
- if [[ -n "$wt_shell_output" ]]; then
1009
- echo "$wt_shell_output" | head -3 | sed 's/^/ /'
1010
- fi
1011
1115
  fi
1012
1116
 
1013
1117
  echo ""
@@ -1027,16 +1131,13 @@ setup_worktrunk() {
1027
1131
  print_info "Fallback available: ~/.aidevops/agents/scripts/worktree-helper.sh"
1028
1132
  fi
1029
1133
  elif command -v cargo >/dev/null 2>&1; then
1030
- read -r -p "Install Worktrunk via Cargo? (y/n): " install_wt
1134
+ read -r -p "Install Worktrunk via Cargo? [Y/n]: " install_wt
1031
1135
 
1032
- if [[ "$install_wt" == "y" ]]; then
1033
- print_info "Installing Worktrunk via Cargo..."
1034
- if cargo install worktrunk 2>/dev/null; then
1035
- print_success "Worktrunk installed"
1036
-
1136
+ if [[ "$install_wt" =~ ^[Yy]?$ ]]; then
1137
+ if run_with_spinner "Installing Worktrunk via Cargo" cargo install worktrunk; then
1037
1138
  # Install shell integration
1038
- if wt config shell install 2>/dev/null; then
1039
- print_success "Shell integration installed"
1139
+ if run_with_spinner "Installing shell integration" wt config shell install; then
1140
+ print_info "Restart your terminal or source your shell config"
1040
1141
  else
1041
1142
  print_warning "Shell integration failed - run manually: wt config shell install"
1042
1143
  fi
@@ -1121,8 +1222,8 @@ setup_recommended_tools() {
1121
1222
 
1122
1223
  if [[ -d "$zed_extensions_dir" ]]; then
1123
1224
  if [[ ! -d "$zed_extensions_dir/opencode" ]]; then
1124
- read -r -p "Install OpenCode extension for Zed? (y/n): " install_opencode_ext
1125
- if [[ "$install_opencode_ext" == "y" ]]; then
1225
+ read -r -p "Install OpenCode extension for Zed? [Y/n]: " install_opencode_ext
1226
+ if [[ "$install_opencode_ext" =~ ^[Yy]?$ ]]; then
1126
1227
  print_info "Installing OpenCode extension..."
1127
1228
  if [[ "$(uname)" == "Darwin" ]]; then
1128
1229
  open "zed://extension/opencode" 2>/dev/null
@@ -1148,15 +1249,13 @@ setup_recommended_tools() {
1148
1249
 
1149
1250
  # Install Tabby if missing
1150
1251
  if [[ " ${missing_tools[*]} " =~ " tabby " ]]; then
1151
- read -r -p "Install Tabby terminal? (y/n): " install_tabby
1252
+ read -r -p "Install Tabby terminal? [Y/n]: " install_tabby
1152
1253
 
1153
- if [[ "$install_tabby" == "y" ]]; then
1154
- print_info "Installing Tabby..."
1254
+ if [[ "$install_tabby" =~ ^[Yy]?$ ]]; then
1155
1255
  if [[ "$(uname)" == "Darwin" ]]; then
1156
1256
  if command -v brew >/dev/null 2>&1; then
1157
- brew install --cask tabby
1158
- if [[ $? -eq 0 ]]; then
1159
- print_success "Tabby installed successfully"
1257
+ if run_with_spinner "Installing Tabby" brew install --cask tabby; then
1258
+ : # Success message handled by spinner
1160
1259
  else
1161
1260
  print_warning "Failed to install Tabby via Homebrew"
1162
1261
  echo " Download manually: https://github.com/Eugeny/tabby/releases/latest"
@@ -1197,16 +1296,13 @@ setup_recommended_tools() {
1197
1296
 
1198
1297
  # Install Zed if missing
1199
1298
  if [[ " ${missing_tools[*]} " =~ " zed " ]]; then
1200
- read -r -p "Install Zed editor? (y/n): " install_zed
1299
+ read -r -p "Install Zed editor? [Y/n]: " install_zed
1201
1300
 
1202
- if [[ "$install_zed" == "y" ]]; then
1203
- print_info "Installing Zed..."
1301
+ if [[ "$install_zed" =~ ^[Yy]?$ ]]; then
1204
1302
  local zed_installed=false
1205
1303
  if [[ "$(uname)" == "Darwin" ]]; then
1206
1304
  if command -v brew >/dev/null 2>&1; then
1207
- brew install --cask zed
1208
- if [[ $? -eq 0 ]]; then
1209
- print_success "Zed installed successfully"
1305
+ if run_with_spinner "Installing Zed" brew install --cask zed; then
1210
1306
  zed_installed=true
1211
1307
  else
1212
1308
  print_warning "Failed to install Zed via Homebrew"
@@ -1217,10 +1313,9 @@ setup_recommended_tools() {
1217
1313
  echo " Download manually: https://zed.dev/download"
1218
1314
  fi
1219
1315
  elif [[ "$(uname)" == "Linux" ]]; then
1220
- # Zed provides an install script for Linux
1316
+ # Zed provides an install script for Linux (interactive, can't use spinner)
1221
1317
  print_info "Running Zed install script..."
1222
- curl -f https://zed.dev/install.sh | sh
1223
- if [[ $? -eq 0 ]]; then
1318
+ if curl -f https://zed.dev/install.sh | sh; then
1224
1319
  print_success "Zed installed successfully"
1225
1320
  zed_installed=true
1226
1321
  else
@@ -1231,8 +1326,8 @@ setup_recommended_tools() {
1231
1326
 
1232
1327
  # Install OpenCode extension for Zed
1233
1328
  if [[ "$zed_installed" == "true" ]]; then
1234
- read -r -p "Install OpenCode extension for Zed? (y/n): " install_opencode_ext
1235
- if [[ "$install_opencode_ext" == "y" ]]; then
1329
+ read -r -p "Install OpenCode extension for Zed? [Y/n]: " install_opencode_ext
1330
+ if [[ "$install_opencode_ext" =~ ^[Yy]?$ ]]; then
1236
1331
  print_info "Installing OpenCode extension..."
1237
1332
  if [[ "$(uname)" == "Darwin" ]]; then
1238
1333
  open "zed://extension/opencode" 2>/dev/null
@@ -1319,12 +1414,10 @@ setup_minisim() {
1319
1414
  fi
1320
1415
 
1321
1416
  local install_minisim
1322
- read -r -p "Install MiniSim? (y/n): " install_minisim
1417
+ read -r -p "Install MiniSim? [Y/n]: " install_minisim
1323
1418
 
1324
- if [[ "$install_minisim" == "y" ]]; then
1325
- print_info "Installing MiniSim..."
1326
- if brew install --cask minisim; then
1327
- print_success "MiniSim installed successfully"
1419
+ if [[ "$install_minisim" =~ ^[Yy]?$ ]]; then
1420
+ if run_with_spinner "Installing MiniSim" brew install --cask minisim; then
1328
1421
  print_info "Global shortcut: Option + Shift + E"
1329
1422
  print_info "Documentation: ~/.aidevops/agents/tools/mobile/minisim.md"
1330
1423
  else
@@ -1345,9 +1438,9 @@ setup_ssh_key() {
1345
1438
 
1346
1439
  if [[ ! -f ~/.ssh/id_ed25519 ]]; then
1347
1440
  print_warning "Ed25519 SSH key not found"
1348
- read -r -p "Generate new Ed25519 SSH key? (y/n): " generate_key
1441
+ read -r -p "Generate new Ed25519 SSH key? [Y/n]: " generate_key
1349
1442
 
1350
- if [[ "$generate_key" == "y" ]]; then
1443
+ if [[ "$generate_key" =~ ^[Yy]?$ ]]; then
1351
1444
  read -r -p "Enter your email address: " email
1352
1445
  ssh-keygen -t ed25519 -C "$email"
1353
1446
  print_success "SSH key generated"
@@ -1563,9 +1656,9 @@ setup_aliases() {
1563
1656
  fi
1564
1657
 
1565
1658
  print_info "Detected shell: $shell_name"
1566
- read -r -p "Add shell aliases to $shell_rc? (y/n): " add_aliases
1659
+ read -r -p "Add shell aliases to $shell_rc? [Y/n]: " add_aliases
1567
1660
 
1568
- if [[ "$add_aliases" == "y" ]]; then
1661
+ if [[ "$add_aliases" =~ ^[Yy]?$ ]]; then
1569
1662
  # Fish shell uses different syntax
1570
1663
  if [[ "$shell_name" == "fish" ]]; then
1571
1664
  mkdir -p "$HOME/.config/fish"
@@ -1652,9 +1745,9 @@ setup_terminal_title() {
1652
1745
  fi
1653
1746
 
1654
1747
  echo ""
1655
- read -r -p "Install terminal title integration? (y/n): " install_title
1748
+ read -r -p "Install terminal title integration? [Y/n]: " install_title
1656
1749
 
1657
- if [[ "$install_title" == "y" ]]; then
1750
+ if [[ "$install_title" =~ ^[Yy]?$ ]]; then
1658
1751
  if bash "$setup_script" install; then
1659
1752
  print_success "Terminal title integration installed"
1660
1753
  else
@@ -2182,14 +2275,9 @@ setup_python_env() {
2182
2275
  source python-env/dspy-env/bin/activate
2183
2276
  pip install --upgrade pip > /dev/null 2>&1
2184
2277
 
2185
- local pip_output
2186
- if pip_output=$(pip install -r requirements.txt 2>&1); then
2187
- print_success "DSPy dependencies installed successfully"
2278
+ if run_with_spinner "Installing DSPy dependencies" pip install -r requirements.txt; then
2279
+ : # Success message handled by spinner
2188
2280
  else
2189
- print_warning "Failed to install DSPy dependencies:"
2190
- # Show last few lines of error for debugging
2191
- echo "$pip_output" | tail -8 | sed 's/^/ /'
2192
- echo ""
2193
2281
  print_info "Check requirements.txt or run manually:"
2194
2282
  print_info " source python-env/dspy-env/bin/activate && pip install -r requirements.txt"
2195
2283
  fi
@@ -2220,13 +2308,10 @@ setup_nodejs_env() {
2220
2308
 
2221
2309
  # Install DSPyGround globally if not already installed
2222
2310
  if ! command -v dspyground &> /dev/null; then
2223
- print_info "Installing DSPyGround globally..."
2224
- npm install -g dspyground > /dev/null 2>&1
2225
-
2226
- if [[ $? -eq 0 ]]; then
2227
- print_success "DSPyGround installed successfully"
2311
+ if run_with_spinner "Installing DSPyGround" npm install -g dspyground; then
2312
+ : # Success message handled by spinner
2228
2313
  else
2229
- print_warning "Failed to install DSPyGround globally"
2314
+ print_warning "Try manually: npm install -g dspyground"
2230
2315
  fi
2231
2316
  else
2232
2317
  print_success "DSPyGround already installed"
@@ -2462,16 +2547,13 @@ setup_localwp_mcp() {
2462
2547
 
2463
2548
  # Offer to install mcp-local-wp
2464
2549
  print_info "LocalWP MCP server enables AI assistants to query WordPress databases"
2465
- read -r -p "Install LocalWP MCP server (@verygoodplugins/mcp-local-wp)? (y/n): " install_mcp
2550
+ read -r -p "Install LocalWP MCP server (@verygoodplugins/mcp-local-wp)? [Y/n]: " install_mcp
2466
2551
 
2467
- if [[ "$install_mcp" == "y" ]]; then
2468
- print_info "Installing LocalWP MCP server..."
2469
- if npm install -g @verygoodplugins/mcp-local-wp > /dev/null 2>&1; then
2470
- print_success "LocalWP MCP server installed successfully"
2552
+ if [[ "$install_mcp" =~ ^[Yy]?$ ]]; then
2553
+ if run_with_spinner "Installing LocalWP MCP server" npm install -g @verygoodplugins/mcp-local-wp; then
2471
2554
  print_info "Start with: ~/.aidevops/agents/scripts/localhost-helper.sh start-mcp"
2472
2555
  print_info "Or configure in OpenCode MCP settings for auto-start"
2473
2556
  else
2474
- print_warning "Failed to install LocalWP MCP server"
2475
2557
  print_info "Try manually: npm install -g @verygoodplugins/mcp-local-wp"
2476
2558
  fi
2477
2559
  else
@@ -2580,16 +2662,13 @@ setup_beads() {
2580
2662
  else
2581
2663
  # Try to install via Homebrew first (macOS/Linux with Homebrew)
2582
2664
  if command -v brew &> /dev/null; then
2583
- print_info "Installing Beads via Homebrew..."
2584
- if brew install steveyegge/beads/bd 2>/dev/null; then
2585
- print_success "Beads CLI installed via Homebrew"
2665
+ if run_with_spinner "Installing Beads via Homebrew" brew install steveyegge/beads/bd; then
2666
+ : # Success message handled by spinner
2586
2667
  else
2587
2668
  print_warning "Homebrew tap installation failed, trying alternative..."
2588
2669
  # Try Go install if Go is available
2589
2670
  if command -v go &> /dev/null; then
2590
- print_info "Installing Beads via Go..."
2591
- if go install github.com/steveyegge/beads/cmd/bd@latest 2>/dev/null; then
2592
- print_success "Beads CLI installed via Go"
2671
+ if run_with_spinner "Installing Beads via Go" go install github.com/steveyegge/beads/cmd/bd@latest; then
2593
2672
  print_info "Ensure \$GOPATH/bin is in your PATH"
2594
2673
  else
2595
2674
  print_warning "Go installation failed"
@@ -2597,9 +2676,7 @@ setup_beads() {
2597
2676
  fi
2598
2677
  fi
2599
2678
  elif command -v go &> /dev/null; then
2600
- print_info "Installing Beads via Go..."
2601
- if go install github.com/steveyegge/beads/cmd/bd@latest 2>/dev/null; then
2602
- print_success "Beads CLI installed via Go"
2679
+ if run_with_spinner "Installing Beads via Go" go install github.com/steveyegge/beads/cmd/bd@latest; then
2603
2680
  print_info "Ensure \$GOPATH/bin is in your PATH"
2604
2681
  else
2605
2682
  print_warning "Go installation failed"
@@ -2635,9 +2712,9 @@ setup_beads_ui() {
2635
2712
  echo " • perles (Rust) - BQL query language TUI"
2636
2713
  echo ""
2637
2714
 
2638
- read -r -p "Install optional Beads UI tools? (y/n): " install_beads_ui
2715
+ read -r -p "Install optional Beads UI tools? [Y/n]: " install_beads_ui
2639
2716
 
2640
- if [[ "$install_beads_ui" != "y" ]]; then
2717
+ if [[ ! "$install_beads_ui" =~ ^[Yy]?$ ]]; then
2641
2718
  print_info "Skipped Beads UI tools (can install later from beads.md docs)"
2642
2719
  return 0
2643
2720
  fi
@@ -2646,24 +2723,21 @@ setup_beads_ui() {
2646
2723
 
2647
2724
  # beads_viewer (Python) - use pipx for isolated install
2648
2725
  if command -v pipx &> /dev/null || command -v pip3 &> /dev/null || command -v pip &> /dev/null; then
2649
- read -r -p " Install beads_viewer (Python TUI with graph analytics)? (y/n): " install_viewer
2650
- if [[ "$install_viewer" == "y" ]]; then
2726
+ read -r -p " Install beads_viewer (Python TUI with graph analytics)? [Y/n]: " install_viewer
2727
+ if [[ "$install_viewer" =~ ^[Yy]?$ ]]; then
2651
2728
  if command -v pipx &> /dev/null; then
2652
- print_info "Installing beads_viewer via pipx..."
2653
- if pipx install beads-viewer 2>/dev/null; then
2654
- print_success "beads_viewer installed (run: beads-viewer)"
2729
+ if run_with_spinner "Installing beads_viewer via pipx" pipx install beads-viewer; then
2730
+ print_info "Run: beads-viewer"
2655
2731
  ((installed_count++))
2656
2732
  else
2657
- print_warning "Failed to install beads_viewer"
2658
2733
  print_info "Try manually: pipx install beads-viewer"
2659
2734
  fi
2660
2735
  else
2661
- print_info "Installing beads_viewer..."
2662
- if pip3 install --user beads-viewer 2>/dev/null || pip install --user beads-viewer 2>/dev/null; then
2663
- print_success "beads_viewer installed"
2736
+ if run_with_spinner "Installing beads_viewer" pip3 install --user beads-viewer; then
2737
+ ((installed_count++))
2738
+ elif run_with_spinner "Installing beads_viewer" pip install --user beads-viewer; then
2664
2739
  ((installed_count++))
2665
2740
  else
2666
- print_warning "Failed to install beads_viewer"
2667
2741
  print_info "On macOS, install pipx first: brew install pipx && pipx ensurepath"
2668
2742
  fi
2669
2743
  fi
@@ -2672,39 +2746,30 @@ setup_beads_ui() {
2672
2746
 
2673
2747
  # beads-ui (Node.js)
2674
2748
  if command -v npm &> /dev/null; then
2675
- read -r -p " Install beads-ui (Web dashboard)? (y/n): " install_web
2676
- if [[ "$install_web" == "y" ]]; then
2677
- print_info "Installing beads-ui..."
2678
- if npm install -g beads-ui 2>/dev/null; then
2679
- print_success "beads-ui installed (run: beads-ui)"
2749
+ read -r -p " Install beads-ui (Web dashboard)? [Y/n]: " install_web
2750
+ if [[ "$install_web" =~ ^[Yy]?$ ]]; then
2751
+ if run_with_spinner "Installing beads-ui" npm install -g beads-ui; then
2752
+ print_info "Run: beads-ui"
2680
2753
  ((installed_count++))
2681
- else
2682
- print_warning "Failed to install beads-ui"
2683
2754
  fi
2684
2755
  fi
2685
2756
 
2686
- read -r -p " Install bdui (React/Ink TUI)? (y/n): " install_bdui
2687
- if [[ "$install_bdui" == "y" ]]; then
2688
- print_info "Installing bdui..."
2689
- if npm install -g bdui 2>/dev/null; then
2690
- print_success "bdui installed (run: bdui)"
2757
+ read -r -p " Install bdui (React/Ink TUI)? [Y/n]: " install_bdui
2758
+ if [[ "$install_bdui" =~ ^[Yy]?$ ]]; then
2759
+ if run_with_spinner "Installing bdui" npm install -g bdui; then
2760
+ print_info "Run: bdui"
2691
2761
  ((installed_count++))
2692
- else
2693
- print_warning "Failed to install bdui"
2694
2762
  fi
2695
2763
  fi
2696
2764
  fi
2697
2765
 
2698
2766
  # perles (Rust)
2699
2767
  if command -v cargo &> /dev/null; then
2700
- read -r -p " Install perles (BQL query language TUI)? (y/n): " install_perles
2701
- if [[ "$install_perles" == "y" ]]; then
2702
- print_info "Installing perles (this may take a few minutes)..."
2703
- if cargo install perles 2>/dev/null; then
2704
- print_success "perles installed (run: perles)"
2768
+ read -r -p " Install perles (BQL query language TUI)? [Y/n]: " install_perles
2769
+ if [[ "$install_perles" =~ ^[Yy]?$ ]]; then
2770
+ if run_with_spinner "Installing perles (Rust compile)" cargo install perles; then
2771
+ print_info "Run: perles"
2705
2772
  ((installed_count++))
2706
- else
2707
- print_warning "Failed to install perles"
2708
2773
  fi
2709
2774
  fi
2710
2775
  fi
@@ -2795,9 +2860,9 @@ setup_browser_tools() {
2795
2860
  print_success "Playwright already installed"
2796
2861
  else
2797
2862
  local install_playwright
2798
- read -r -p "Install Playwright MCP with browsers (chromium, firefox, webkit)? (y/n): " install_playwright
2863
+ read -r -p "Install Playwright MCP with browsers (chromium, firefox, webkit)? [Y/n]: " install_playwright
2799
2864
 
2800
- if [[ "$install_playwright" == "y" ]]; then
2865
+ if [[ "$install_playwright" =~ ^[Yy]?$ ]]; then
2801
2866
  print_info "Installing Playwright browsers..."
2802
2867
  # Use -y to auto-confirm npx install, suppress the "install without dependencies" warning
2803
2868
  # Use PIPESTATUS to check npx exit code, not grep's exit code
@@ -3014,9 +3079,9 @@ setup_oh_my_opencode() {
3014
3079
  echo " They are complementary and work well together."
3015
3080
  echo ""
3016
3081
 
3017
- read -r -p "Install Oh-My-OpenCode plugin? (y/n): " install_omo
3082
+ read -r -p "Install Oh-My-OpenCode plugin? [Y/n]: " install_omo
3018
3083
 
3019
- if [[ "$install_omo" != "y" ]]; then
3084
+ if [[ ! "$install_omo" =~ ^[Yy]?$ ]]; then
3020
3085
  print_info "Skipped Oh-My-OpenCode installation"
3021
3086
  return 0
3022
3087
  fi
@@ -3267,10 +3332,10 @@ setup_multi_tenant_credentials() {
3267
3332
  print_info "Everything continues to work as before - this is non-breaking."
3268
3333
  echo ""
3269
3334
 
3270
- read -r -p "Enable multi-tenant credential storage? (y/n): " enable_mt
3335
+ read -r -p "Enable multi-tenant credential storage? [Y/n]: " enable_mt
3271
3336
  enable_mt=$(echo "$enable_mt" | tr '[:upper:]' '[:lower:]')
3272
3337
 
3273
- if [[ "$enable_mt" == "y" || "$enable_mt" == "yes" ]]; then
3338
+ if [[ "$enable_mt" =~ ^[Yy]?$ || "$enable_mt" == "yes" ]]; then
3274
3339
  bash "$credential_helper" init
3275
3340
  print_success "Multi-tenant credential storage enabled"
3276
3341
  echo ""
@@ -3332,9 +3397,9 @@ check_tool_updates() {
3332
3397
  bash "$tool_check_script" --quiet
3333
3398
  echo ""
3334
3399
 
3335
- read -r -p "Update all outdated tools now? (y/n): " do_update
3400
+ read -r -p "Update all outdated tools now? [Y/n]: " do_update
3336
3401
 
3337
- if [[ "$do_update" == "y" || "$do_update" == "Y" ]]; then
3402
+ if [[ "$do_update" =~ ^[Yy]?$ || "$do_update" == "Y" ]]; then
3338
3403
  print_info "Updating tools..."
3339
3404
  bash "$tool_check_script" --update
3340
3405
  print_success "Tool updates complete!"
@@ -3432,6 +3497,7 @@ main() {
3432
3497
  confirm_step "Migrate loop state from .claude/ to .agent/loop-state/" && migrate_loop_state_directories
3433
3498
  confirm_step "Cleanup deprecated agent paths" && cleanup_deprecated_paths
3434
3499
  confirm_step "Cleanup deprecated MCP entries (hetzner, serper, etc.)" && cleanup_deprecated_mcps
3500
+ confirm_step "Validate and repair OpenCode config schema" && validate_opencode_config
3435
3501
  confirm_step "Extract OpenCode prompts" && extract_opencode_prompts
3436
3502
  confirm_step "Check OpenCode prompt drift" && check_opencode_prompt_drift
3437
3503
  confirm_step "Deploy aidevops agents to ~/.aidevops/agents/" && deploy_aidevops_agents
@@ -3543,8 +3609,8 @@ echo " aidevops uninstall - Remove aidevops"
3543
3609
  echo " • Get personalized recommendations based on your work"
3544
3610
  echo " • Set up API keys and credentials interactively"
3545
3611
  echo ""
3546
- read -r -p "Launch OpenCode with /onboarding now? (y/n): " launch_onboarding
3547
- if [[ "$launch_onboarding" == "y" || "$launch_onboarding" == "Y" ]]; then
3612
+ read -r -p "Launch OpenCode with /onboarding now? [Y/n]: " launch_onboarding
3613
+ if [[ "$launch_onboarding" =~ ^[Yy]?$ || "$launch_onboarding" == "Y" ]]; then
3548
3614
  echo ""
3549
3615
  echo "Starting OpenCode..."
3550
3616
  opencode --prompt "/onboarding"