aidevops 2.105.4 → 2.105.6

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 +30 -14
  3. package/package.json +1 -1
  4. package/setup.sh +132 -45
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.105.4
1
+ 2.105.6
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.105.4
6
+ # Version: 2.105.6
7
7
 
8
8
  set -euo pipefail
9
9
 
@@ -25,6 +25,9 @@ REPOS_FILE="$CONFIG_DIR/repos.json"
25
25
  REPO_URL="https://github.com/marcusquinn/aidevops.git"
26
26
  VERSION_FILE="$INSTALL_DIR/VERSION"
27
27
 
28
+ # Portable sed in-place edit (macOS BSD sed vs GNU sed)
29
+ sed_inplace() { if [[ "$(uname)" == "Darwin" ]]; then sed -i '' "$@"; else sed -i "$@"; fi; }
30
+
28
31
  print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
29
32
  print_success() { echo -e "${GREEN}[OK]${NC} $1"; }
30
33
  print_warning() { echo -e "${YELLOW}[WARN]${NC} $1"; }
@@ -588,8 +591,24 @@ cmd_update() {
588
591
  fi
589
592
  else
590
593
  print_warning "Repository not found, performing fresh install..."
591
- # shellcheck disable=SC2312 # curl|bash is intentional for install
592
- bash <(curl -fsSL https://raw.githubusercontent.com/marcusquinn/aidevops/main/setup.sh)
594
+ # Download setup script to temp file first (not piped to shell)
595
+ local tmp_setup
596
+ tmp_setup=$(mktemp "${TMPDIR:-/tmp}/aidevops-setup-XXXXXX.sh") || {
597
+ print_error "Failed to create temp file for setup script"
598
+ return 1
599
+ }
600
+ if curl -fsSL "https://raw.githubusercontent.com/marcusquinn/aidevops/main/setup.sh" -o "$tmp_setup" 2>/dev/null && [[ -s "$tmp_setup" ]]; then
601
+ chmod +x "$tmp_setup"
602
+ bash "$tmp_setup"
603
+ local setup_exit=$?
604
+ rm -f "$tmp_setup"
605
+ [[ $setup_exit -ne 0 ]] && return 1
606
+ else
607
+ rm -f "$tmp_setup"
608
+ print_error "Failed to download setup script"
609
+ print_info "Try: git clone https://github.com/marcusquinn/aidevops.git $INSTALL_DIR && bash $INSTALL_DIR/setup.sh"
610
+ return 1
611
+ fi
593
612
  fi
594
613
 
595
614
  # Check registered repos for updates
@@ -761,8 +780,7 @@ cmd_uninstall() {
761
780
  # Create backup
762
781
  cp "$rc_file" "$rc_file.bak"
763
782
  # Remove our alias block (from comment to empty line)
764
- sed -i.tmp '/# AI Assistant Server Access Framework/,/^$/d' "$rc_file"
765
- rm -f "$rc_file.tmp"
783
+ sed_inplace '/# AI Assistant Server Access Framework/,/^$/d' "$rc_file"
766
784
  print_success "Removed aliases from $rc_file"
767
785
  fi
768
786
  fi
@@ -798,7 +816,8 @@ cmd_uninstall() {
798
816
  echo ""
799
817
  print_success "Uninstall complete!"
800
818
  print_info "To reinstall, run:"
801
- echo " bash <(curl -fsSL https://raw.githubusercontent.com/marcusquinn/aidevops/main/setup.sh)"
819
+ echo " npm install -g aidevops && aidevops update"
820
+ echo " OR: brew install marcusquinn/tap/aidevops && aidevops update"
802
821
  }
803
822
 
804
823
  # Init command - initialize aidevops in a project
@@ -1360,8 +1379,7 @@ cmd_upgrade_planning() {
1360
1379
  fi
1361
1380
 
1362
1381
  # Update date placeholder
1363
- sed -i.tmp "s/{{DATE}}/$(date +%Y-%m-%d)/" "$todo_file" 2>/dev/null || true
1364
- rm -f "${todo_file}.tmp"
1382
+ sed_inplace "s/{{DATE}}/$(date +%Y-%m-%d)/" "$todo_file" 2>/dev/null || true
1365
1383
 
1366
1384
  # Merge existing tasks into Backlog section (after the TOON block closing tag)
1367
1385
  if [[ -n "$existing_tasks" ]]; then
@@ -1426,8 +1444,7 @@ cmd_upgrade_planning() {
1426
1444
  fi
1427
1445
 
1428
1446
  # Update date placeholder
1429
- sed -i.tmp "s/{{DATE}}/$(date +%Y-%m-%d)/" "$plans_file" 2>/dev/null || true
1430
- rm -f "${plans_file}.tmp"
1447
+ sed_inplace "s/{{DATE}}/$(date +%Y-%m-%d)/" "$plans_file" 2>/dev/null || true
1431
1448
 
1432
1449
  # Merge existing plans into Active Plans section (after the TOON block closing tag)
1433
1450
  if [[ -n "$existing_plans" ]]; then
@@ -1483,8 +1500,7 @@ cmd_upgrade_planning() {
1483
1500
  ' "$config_file" > "$temp_json" && mv "$temp_json" "$config_file"
1484
1501
  else
1485
1502
  # Update existing templates_version
1486
- sed -i.tmp "s/\"templates_version\": \"[^\"]*\"/\"templates_version\": \"$aidevops_version\"/" "$config_file" 2>/dev/null || true
1487
- rm -f "${config_file}.tmp"
1503
+ sed_inplace "s/\"templates_version\": \"[^\"]*\"/\"templates_version\": \"$aidevops_version\"/" "$config_file" 2>/dev/null || true
1488
1504
  fi
1489
1505
  fi
1490
1506
 
@@ -2019,9 +2035,9 @@ cmd_help() {
2019
2035
  echo " aidevops skill remove <name> # Remove an imported skill"
2020
2036
  echo ""
2021
2037
  echo "Installation:"
2022
- echo " npm install -g aidevops && aidevops update # via npm"
2038
+ echo " npm install -g aidevops && aidevops update # via npm (recommended)"
2023
2039
  echo " brew install marcusquinn/tap/aidevops && aidevops update # via Homebrew"
2024
- echo " bash <(curl -fsSL https://aidevops.sh) # via curl"
2040
+ echo " curl -fsSL https://aidevops.sh -o /tmp/aidevops-setup.sh && bash /tmp/aidevops-setup.sh # manual"
2025
2041
  echo ""
2026
2042
  echo "Documentation: https://github.com/marcusquinn/aidevops"
2027
2043
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidevops",
3
- "version": "2.105.4",
3
+ "version": "2.105.6",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "bin": {
package/setup.sh CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env bash
2
+ set -euo pipefail
2
3
 
3
4
  # AI Assistant Server Access Framework Setup Script
4
5
  # Helps developers set up the framework for their infrastructure
5
6
  #
6
- # Version: 2.105.4
7
+ # Version: 2.105.6
7
8
  #
8
- # Quick Install (one-liner):
9
- # bash <(curl -fsSL https://aidevops.dev/install)
10
- # OR: bash <(curl -fsSL https://raw.githubusercontent.com/marcusquinn/aidevops/main/setup.sh)
9
+ # Quick Install:
10
+ # npm install -g aidevops && aidevops update (recommended)
11
+ # brew install marcusquinn/tap/aidevops && aidevops update (Homebrew)
12
+ # curl -fsSL https://aidevops.sh -o /tmp/aidevops-setup.sh && bash /tmp/aidevops-setup.sh (manual)
11
13
 
12
14
  # Colors for output
13
15
  GREEN='\033[0;32m'
@@ -64,6 +66,73 @@ run_with_spinner() {
64
66
  return $exit_code
65
67
  }
66
68
 
69
+ # Verified install: download script to temp file, inspect, then execute
70
+ # Replaces unsafe curl|sh patterns with download-verify-execute
71
+ # Usage: verified_install "description" "url" [extra_args...]
72
+ # Options (set before calling):
73
+ # VERIFIED_INSTALL_SUDO="true" - run with sudo
74
+ # VERIFIED_INSTALL_SHELL="sh" - use sh instead of bash (default: bash)
75
+ # Returns: 0 on success, 1 on failure
76
+ verified_install() {
77
+ local description="$1"
78
+ local url="$2"
79
+ shift 2
80
+ local extra_args=("$@")
81
+ local shell="${VERIFIED_INSTALL_SHELL:-bash}"
82
+ local use_sudo="${VERIFIED_INSTALL_SUDO:-false}"
83
+
84
+ # Reset options for next call
85
+ VERIFIED_INSTALL_SUDO="false"
86
+ VERIFIED_INSTALL_SHELL="bash"
87
+
88
+ # Create secure temp file
89
+ local tmp_script
90
+ tmp_script=$(mktemp "${TMPDIR:-/tmp}/aidevops-install-XXXXXX.sh") || {
91
+ print_error "Failed to create temp file for $description"
92
+ return 1
93
+ }
94
+
95
+ # Ensure cleanup on exit from this function
96
+ # shellcheck disable=SC2064
97
+ trap "rm -f '$tmp_script'" RETURN
98
+
99
+ # Download script to file (not piped to shell)
100
+ print_info "Downloading $description install script..."
101
+ if ! curl -fsSL "$url" -o "$tmp_script" 2>/dev/null; then
102
+ print_error "Failed to download $description install script from $url"
103
+ return 1
104
+ fi
105
+
106
+ # Verify download is non-empty and looks like a script
107
+ if [[ ! -s "$tmp_script" ]]; then
108
+ print_error "Downloaded $description script is empty"
109
+ return 1
110
+ fi
111
+
112
+ # Basic content safety check: reject binary content
113
+ if file "$tmp_script" 2>/dev/null | grep -qv 'text'; then
114
+ print_error "Downloaded $description script appears to be binary, not a shell script"
115
+ return 1
116
+ fi
117
+
118
+ # Make executable
119
+ chmod +x "$tmp_script"
120
+
121
+ # Execute from file
122
+ local cmd=("$shell" "$tmp_script" "${extra_args[@]}")
123
+ if [[ "$use_sudo" == "true" ]]; then
124
+ cmd=(sudo "$shell" "$tmp_script" "${extra_args[@]}")
125
+ fi
126
+
127
+ if "${cmd[@]}"; then
128
+ print_success "$description installed"
129
+ return 0
130
+ else
131
+ print_error "$description installation failed"
132
+ return 1
133
+ fi
134
+ }
135
+
67
136
  # Find OpenCode config file (checks multiple possible locations)
68
137
  # Returns: path to config file, or empty string if not found
69
138
  find_opencode_config() {
@@ -310,20 +379,36 @@ migrate_agent_to_agents_folder() {
310
379
  done < <(jq -r '.initialized_repos[].path' "$repos_file" 2>/dev/null)
311
380
  fi
312
381
 
313
- # 2. Also scan ~/Git/ for any .agent symlinks not in repos.json
382
+ # 2. Also scan ~/Git/ for any .agent symlinks or directories not in repos.json
314
383
  if [[ -d "$HOME/Git" ]]; then
315
- while IFS= read -r -d '' agent_link; do
384
+ while IFS= read -r -d '' agent_path; do
316
385
  local repo_dir
317
- repo_dir=$(dirname "$agent_link")
318
- if [[ -L "$agent_link" ]] && [[ ! -e "$repo_dir/.agents" ]]; then
319
- local target
320
- target=$(readlink "$agent_link")
321
- rm -f "$agent_link"
322
- ln -s "$target" "$repo_dir/.agents" 2>/dev/null || true
323
- print_info " Migrated symlink: $agent_link -> .agents"
324
- ((migrated++))
386
+ repo_dir=$(dirname "$agent_path")
387
+
388
+ if [[ -L "$agent_path" ]]; then
389
+ # Symlink: migrate or clean up stale
390
+ if [[ ! -e "$repo_dir/.agents" ]]; then
391
+ local target
392
+ target=$(readlink "$agent_path")
393
+ rm -f "$agent_path"
394
+ ln -s "$target" "$repo_dir/.agents" 2>/dev/null || true
395
+ print_info " Migrated symlink: $agent_path -> .agents"
396
+ ((migrated++))
397
+ else
398
+ # .agents already exists, remove stale .agent symlink
399
+ rm -f "$agent_path"
400
+ print_info " Removed stale symlink: $agent_path (.agents already exists)"
401
+ ((migrated++))
402
+ fi
403
+ elif [[ -d "$agent_path" ]]; then
404
+ # Directory: rename to .agents if .agents doesn't exist
405
+ if [[ ! -e "$repo_dir/.agents" ]]; then
406
+ mv "$agent_path" "$repo_dir/.agents"
407
+ print_info " Renamed directory: $agent_path -> .agents"
408
+ ((migrated++))
409
+ fi
325
410
  fi
326
- done < <(find "$HOME/Git" -maxdepth 3 -name ".agent" -type l -print0 2>/dev/null)
411
+ done < <(find "$HOME/Git" -maxdepth 3 -name ".agent" \( -type l -o -type d \) -print0 2>/dev/null)
327
412
  fi
328
413
 
329
414
  # 3. Update AI assistant config files that reference .agent/
@@ -1211,8 +1296,9 @@ setup_oh_my_zsh() {
1211
1296
 
1212
1297
  if [[ "$install_omz" =~ ^[Yy]$ ]]; then
1213
1298
  print_info "Installing Oh My Zsh..."
1214
- # Use --unattended to avoid changing the shell or starting zsh
1215
- if sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended; then
1299
+ # Use verified download + --unattended to avoid changing the shell or starting zsh
1300
+ VERIFIED_INSTALL_SHELL="sh"
1301
+ if verified_install "Oh My Zsh" "https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh" --unattended; then
1216
1302
  print_success "Oh My Zsh installed"
1217
1303
 
1218
1304
  # Ensure .zshrc exists (Oh My Zsh creates it, but verify)
@@ -1235,11 +1321,11 @@ setup_oh_my_zsh() {
1235
1321
  fi
1236
1322
  else
1237
1323
  print_warning "Oh My Zsh installation failed"
1238
- print_info "Install manually: sh -c \"\$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\""
1324
+ print_info "Install manually: curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -o /tmp/omz-install.sh && sh /tmp/omz-install.sh"
1239
1325
  fi
1240
1326
  else
1241
1327
  print_info "Skipped Oh My Zsh installation"
1242
- print_info "Install later: sh -c \"\$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\""
1328
+ print_info "Install later: curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -o /tmp/omz-install.sh && sh /tmp/omz-install.sh"
1243
1329
  fi
1244
1330
 
1245
1331
  return 0
@@ -1720,15 +1806,17 @@ setup_recommended_tools() {
1720
1806
  pkg_manager=$(detect_package_manager)
1721
1807
  case "$pkg_manager" in
1722
1808
  apt)
1723
- # Add packagecloud repo for Tabby
1724
- print_info "Adding Tabby repository..."
1725
- curl -s https://packagecloud.io/install/repositories/eugeny/tabby/script.deb.sh | sudo bash
1726
- sudo apt-get install -y tabby-terminal
1809
+ # Add packagecloud repo for Tabby (verified download, not piped to sudo)
1810
+ VERIFIED_INSTALL_SUDO="true"
1811
+ if verified_install "Tabby repository (apt)" "https://packagecloud.io/install/repositories/eugeny/tabby/script.deb.sh"; then
1812
+ sudo apt-get install -y tabby-terminal
1813
+ fi
1727
1814
  ;;
1728
1815
  dnf|yum)
1729
- print_info "Adding Tabby repository..."
1730
- curl -s https://packagecloud.io/install/repositories/eugeny/tabby/script.rpm.sh | sudo bash
1731
- sudo "$pkg_manager" install -y tabby-terminal
1816
+ VERIFIED_INSTALL_SUDO="true"
1817
+ if verified_install "Tabby repository (rpm)" "https://packagecloud.io/install/repositories/eugeny/tabby/script.rpm.sh"; then
1818
+ sudo "$pkg_manager" install -y tabby-terminal
1819
+ fi
1732
1820
  ;;
1733
1821
  pacman)
1734
1822
  # AUR package
@@ -1764,10 +1852,9 @@ setup_recommended_tools() {
1764
1852
  echo " Download manually: https://zed.dev/download"
1765
1853
  fi
1766
1854
  elif [[ "$(uname)" == "Linux" ]]; then
1767
- # Zed provides an install script for Linux (interactive, can't use spinner)
1768
- print_info "Running Zed install script..."
1769
- if curl -f https://zed.dev/install.sh | sh; then
1770
- print_success "Zed installed successfully"
1855
+ # Zed provides an install script for Linux (verified download)
1856
+ VERIFIED_INSTALL_SHELL="sh"
1857
+ if verified_install "Zed" "https://zed.dev/install.sh"; then
1771
1858
  zed_installed=true
1772
1859
  else
1773
1860
  print_warning "Failed to install Zed"
@@ -1955,8 +2042,9 @@ add_local_bin_to_path() {
1955
2042
  while IFS= read -r rc_file; do
1956
2043
  [[ -z "$rc_file" ]] && continue
1957
2044
 
1958
- # Create the rc file if it doesn't exist
2045
+ # Create the rc file if it doesn't exist (ensure parent dir exists for fish etc.)
1959
2046
  if [[ ! -f "$rc_file" ]]; then
2047
+ mkdir -p "$(dirname "$rc_file")"
1960
2048
  touch "$rc_file"
1961
2049
  fi
1962
2050
 
@@ -2074,7 +2162,7 @@ alias aws-helper './.agents/scripts/aws-helper.sh'
2074
2162
  ALIASES
2075
2163
  )
2076
2164
 
2077
- # Check if aliases already exist in any rc file
2165
+ # Check if aliases already exist in any rc file (including fish config)
2078
2166
  local any_configured=false
2079
2167
  local rc_file
2080
2168
  while IFS= read -r rc_file; do
@@ -2084,6 +2172,13 @@ ALIASES
2084
2172
  break
2085
2173
  fi
2086
2174
  done < <(get_all_shell_rcs)
2175
+ # Also check fish config (not included in get_all_shell_rcs on macOS)
2176
+ if [[ "$any_configured" == "false" ]]; then
2177
+ local fish_config="$HOME/.config/fish/config.fish"
2178
+ if grep -q "# AI Assistant Server Access" "$fish_config" 2>/dev/null; then
2179
+ any_configured=true
2180
+ fi
2181
+ fi
2087
2182
 
2088
2183
  if [[ "$any_configured" == "true" ]]; then
2089
2184
  print_info "Server Access aliases already configured - Skipping"
@@ -2935,7 +3030,7 @@ install_mcp_packages() {
2935
3030
  install_cmd="npm install -g"
2936
3031
  else
2937
3032
  print_warning "Neither bun nor npm found - cannot install MCP packages"
2938
- print_info "Install bun (recommended): curl -fsSL https://bun.sh/install | bash"
3033
+ print_info "Install bun (recommended): npm install -g bun OR brew install oven-sh/bun/bun"
2939
3034
  return 0
2940
3035
  fi
2941
3036
 
@@ -3366,15 +3461,15 @@ setup_beads_ui() {
3366
3461
  print_warning "Go install failed"
3367
3462
  fi
3368
3463
  else
3369
- # Offer curl install script
3464
+ # Offer verified install script (download-then-execute, not piped)
3370
3465
  read -r -p " Install bv via install script? [Y/n]: " use_script
3371
3466
  if [[ "$use_script" =~ ^[Yy]?$ ]]; then
3372
- if run_with_spinner "Installing bv via script" bash -c 'curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/beads_viewer/main/install.sh" | bash'; then
3467
+ if verified_install "bv (beads viewer)" "https://raw.githubusercontent.com/Dicklesworthstone/beads_viewer/main/install.sh"; then
3373
3468
  print_info "Run: bv (in a beads-enabled project)"
3374
3469
  ((installed_count++))
3375
3470
  else
3376
3471
  print_warning "Install script failed - try manually:"
3377
- print_info " curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/beads_viewer/main/install.sh | bash"
3472
+ print_info " Homebrew: brew tap dicklesworthstone/tap && brew install dicklesworthstone/tap/bv"
3378
3473
  fi
3379
3474
  else
3380
3475
  print_info "Install later:"
@@ -3448,7 +3543,7 @@ setup_browser_tools() {
3448
3543
  # Install Bun if not present (required for dev-browser)
3449
3544
  if [[ "$has_bun" == "false" ]]; then
3450
3545
  print_info "Installing Bun (required for dev-browser)..."
3451
- if curl -fsSL https://bun.sh/install | bash 2>/dev/null; then
3546
+ if verified_install "Bun" "https://bun.sh/install"; then
3452
3547
  # Source the updated PATH
3453
3548
  export BUN_INSTALL="$HOME/.bun"
3454
3549
  export PATH="$BUN_INSTALL/bin:$PATH"
@@ -3701,14 +3796,6 @@ setup_opencode_plugins() {
3701
3796
 
3702
3797
  return 0
3703
3798
  }
3704
-
3705
- # Setup Oh-My-OpenCode Plugin (removed - no longer supported)
3706
- # Kept as stub for backward compatibility with any callers
3707
- setup_oh_my_opencode() {
3708
- # Removed - oh-my-opencode no longer supported
3709
- return 0
3710
- }
3711
-
3712
3799
  setup_seo_mcps() {
3713
3800
  print_info "Setting up SEO integrations..."
3714
3801