aidevops 2.172.29 → 2.172.30

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.
package/README.md CHANGED
@@ -502,7 +502,7 @@ aidevops implements proven agent design patterns identified by [Lance Martin (La
502
502
  | **Compaction Resilience** | Preserve context across compaction | OpenCode plugin injects dynamic state at compaction time |
503
503
  | **Ralph Loop** | Iterative execution until complete | `/full-loop`, `full-loop-helper.sh` |
504
504
  | **Evolve Context** | Learn from sessions | `/remember`, `/recall` with SQLite FTS5 + opt-in semantic search |
505
- | **Pattern Tracking** | Learn what works/fails | `/patterns` command, memory system |
505
+ | **Pattern Tracking** | Learn what works/fails | `/patterns` command, `memory-helper.sh` |
506
506
  | **Cost-Aware Routing** | Match model to task complexity | `model-routing.md` with 5-tier guidance, `/route` command |
507
507
  | **Model Comparison** | Compare models side-by-side | `/compare-models` (live data), `/compare-models-free` (offline) |
508
508
  | **Response Scoring** | Evaluate actual model outputs | `/score-responses` with structured criteria |
@@ -756,7 +756,7 @@ Agents that learn from experience and contribute improvements:
756
756
 
757
757
  | Phase | Description |
758
758
  |-------|-------------|
759
- | **Review** | Analyze memory for success/failure patterns (memory system) |
759
+ | **Review** | Analyze memory for success/failure patterns (`memory-helper.sh`) |
760
760
  | **Refine** | Generate and apply improvements to agents |
761
761
  | **Test** | Validate in isolated OpenCode sessions |
762
762
  | **PR** | Contribute to community with privacy filtering |
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.172.29
1
+ 2.172.30
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.172.29
6
+ # Version: 2.172.30
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.172.29",
3
+ "version": "2.172.30",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -219,16 +219,28 @@ install_packages() {
219
219
  shift
220
220
  local packages=("$@")
221
221
 
222
+ # Cache sudo credentials before spinner commands.
223
+ # Backgrounded processes cannot safely prompt for passwords.
224
+ if [[ "$pkg_manager" =~ ^(apt|dnf|yum|pacman|apk)$ ]]; then
225
+ sudo -v
226
+ fi
227
+
222
228
  case "$pkg_manager" in
223
229
  brew)
224
230
  # Run brew update with spinner (Homebrew auto-update is slow and silent)
225
- run_with_spinner "Updating Homebrew" brew update
231
+ if ! run_with_spinner "Updating Homebrew" brew update; then
232
+ print_error "Homebrew update failed"
233
+ return 1
234
+ fi
226
235
  # Install with auto-update disabled (we just ran it)
227
236
  # Note: run_with_spinner auto-exports HOMEBREW_NO_AUTO_UPDATE for brew commands
228
237
  run_with_spinner "Installing ${packages[*]}" brew install "${packages[@]}"
229
238
  ;;
230
239
  apt)
231
- run_with_spinner "Updating package lists" sudo apt-get update -qq
240
+ if ! run_with_spinner "Updating package lists" sudo apt-get update -qq; then
241
+ print_error "apt-get update failed"
242
+ return 1
243
+ fi
232
244
  run_with_spinner "Installing ${packages[*]}" sudo apt-get install -y -qq "${packages[@]}"
233
245
  ;;
234
246
  dnf)
@@ -247,6 +259,8 @@ install_packages() {
247
259
  return 1
248
260
  ;;
249
261
  esac
262
+
263
+ return 0
250
264
  }
251
265
 
252
266
  # Offer to install Homebrew (Linuxbrew) on Linux when brew is not available
@@ -68,13 +68,16 @@ install_mcp_packages() {
68
68
  fi
69
69
  fi
70
70
 
71
- if command -v uv &>/dev/null; then
71
+ if command -v uv &>/dev/null && uv tool --help &>/dev/null; then
72
72
  print_info "Installing/updating outscraper-mcp-server via uv..."
73
73
  if command -v outscraper-mcp-server &>/dev/null; then
74
74
  uv tool upgrade outscraper-mcp-server >/dev/null 2>&1 || true
75
75
  else
76
76
  uv tool install outscraper-mcp-server >/dev/null 2>&1 || print_warning "Failed to install outscraper-mcp-server"
77
77
  fi
78
+ elif command -v uv &>/dev/null; then
79
+ print_warning "uv is installed but too old to support 'tool' subcommand — skipping outscraper-mcp-server"
80
+ print_info "Update uv with: curl -LsSf https://astral.sh/uv/install.sh | sh"
78
81
  fi
79
82
 
80
83
  # Update opencode.json with resolved full paths for all MCP binaries
@@ -92,13 +92,13 @@ cleanup_osgrep() {
92
92
  # 0. Kill running osgrep processes first (MCP servers, indexers)
93
93
  # These are Node.js processes already loaded in memory — removing the
94
94
  # binary and data won't stop them, and they may try to rebuild indexes.
95
- if pgrep -f 'osgrep' >/dev/null 2>&1; then
95
+ if pgrep -f 'osgrep' >/dev/null; then
96
96
  print_info "Killing running osgrep processes..."
97
- pkill -f 'osgrep' 2>/dev/null || true
97
+ pkill -f 'osgrep' || true
98
98
  # Give processes a moment to exit gracefully
99
99
  sleep 1
100
100
  # Force-kill any stragglers
101
- pkill -9 -f 'osgrep' 2>/dev/null || true
101
+ pkill -9 -f 'osgrep' || true
102
102
  cleaned=true
103
103
  fi
104
104
 
@@ -424,7 +424,7 @@ scan_imported_skills() {
424
424
  local installed=false
425
425
 
426
426
  # 1. uv tool install (preferred - fast, isolated, manages its own Python)
427
- if [[ "$installed" == "false" ]] && command -v uv &>/dev/null; then
427
+ if [[ "$installed" == "false" ]] && command -v uv &>/dev/null && uv tool --help &>/dev/null; then
428
428
  print_info "Installing Cisco Skill Scanner via uv..."
429
429
  if run_with_spinner "Installing cisco-ai-skill-scanner" uv tool install cisco-ai-skill-scanner; then
430
430
  print_success "Cisco Skill Scanner installed via uv"
@@ -512,7 +512,7 @@ setup_multi_tenant_credentials() {
512
512
  # Check if there are existing credentials to migrate
513
513
  if [[ -f "$HOME/.config/aidevops/credentials.sh" ]]; then
514
514
  local key_count
515
- key_count=$(grep -c "^export " "$HOME/.config/aidevops/credentials.sh" 2>/dev/null || echo "0")
515
+ key_count=$(grep -c "^export " "$HOME/.config/aidevops/credentials.sh" 2>/dev/null) || true
516
516
  print_info "Found $key_count existing API keys in credentials.sh"
517
517
  print_info "Multi-tenant enables managing separate credential sets for:"
518
518
  echo " - Multiple clients (agency/freelance work)"
@@ -29,7 +29,8 @@ detect_default_shell() {
29
29
 
30
30
  # Usage: get_shell_rc "zsh" or get_shell_rc "bash"
31
31
  get_shell_rc() {
32
- local shell_name="$1"
32
+ local shell_name
33
+ shell_name="$1"
33
34
  case "$shell_name" in
34
35
  zsh)
35
36
  echo "$HOME/.zshrc"
@@ -866,8 +867,9 @@ setup_terminal_title() {
866
867
  local tabby_config="$HOME/Library/Application Support/tabby/config.yaml"
867
868
  if [[ -f "$tabby_config" ]]; then
868
869
  local disabled_count
869
- disabled_count=$(grep -c "disableDynamicTitle: true" "$tabby_config" || echo "0")
870
- if [[ "$disabled_count" -gt 0 ]]; then
870
+ # grep -c exits 1 on no match; || : inside subshell prevents ERR trap noise
871
+ disabled_count=$(grep -c "disableDynamicTitle: true" "$tabby_config" || :)
872
+ if [[ "${disabled_count:-0}" -gt 0 ]]; then
871
873
  echo " Tabby: detected, dynamic titles disabled in $disabled_count profile(s) (will fix)"
872
874
  else
873
875
  echo " Tabby: detected, dynamic titles enabled"
package/setup.sh CHANGED
@@ -10,7 +10,7 @@ shopt -s inherit_errexit 2>/dev/null || true
10
10
  # AI Assistant Server Access Framework Setup Script
11
11
  # Helps developers set up the framework for their infrastructure
12
12
  #
13
- # Version: 2.172.29
13
+ # Version: 2.172.30
14
14
  #
15
15
  # Quick Install:
16
16
  # npm install -g aidevops && aidevops update (recommended)
@@ -141,12 +141,13 @@ _ensure_cron_path() {
141
141
  current_crontab=$(crontab -l 2>/dev/null) || current_crontab=""
142
142
 
143
143
  # Deduplicate PATH entries (preserving order)
144
+ # Bash 3.2 compat: no associative arrays — use string-based seen list
144
145
  local deduped_path=""
145
- local -A seen_dirs=()
146
+ local seen_dirs=" "
146
147
  local IFS=':'
147
148
  for dir in $PATH; do
148
- if [[ -n "$dir" && -z "${seen_dirs[$dir]:-}" ]]; then
149
- seen_dirs[$dir]=1
149
+ if [[ -n "$dir" && "$seen_dirs" != *" ${dir} "* ]]; then
150
+ seen_dirs="${seen_dirs}${dir} "
150
151
  deduped_path="${deduped_path:+${deduped_path}:}${dir}"
151
152
  fi
152
153
  done
@@ -186,6 +187,46 @@ _launchd_has_agent() {
186
187
  return $?
187
188
  }
188
189
 
190
+ # Detect whether a scheduler is already installed via launchd or cron.
191
+ # Optionally migrates legacy launchd labels / cron entries to launchd on macOS.
192
+ _scheduler_detect_installed() {
193
+ local scheduler_name="$1"
194
+ local launchd_label="$2"
195
+ local legacy_launchd_label="$3"
196
+ local cron_marker="$4"
197
+ local migrate_script="$5"
198
+ local migrate_arg="$6"
199
+ local migrate_hint="$7"
200
+ local installed=false
201
+
202
+ if _launchd_has_agent "$launchd_label"; then
203
+ installed=true
204
+ elif [[ -n "$legacy_launchd_label" ]] && _launchd_has_agent "$legacy_launchd_label"; then
205
+ if [[ -n "$migrate_script" ]] && [[ -x "$migrate_script" ]]; then
206
+ if bash "$migrate_script" "$migrate_arg" >/dev/null 2>&1; then
207
+ print_info "$scheduler_name LaunchAgent migrated to new label"
208
+ else
209
+ print_warning "$scheduler_name label migration failed. Run: $migrate_hint"
210
+ fi
211
+ fi
212
+ installed=true
213
+ elif crontab -l 2>/dev/null | grep -qF "$cron_marker"; then
214
+ if [[ "$PLATFORM_MACOS" == "true" ]] && [[ -n "$migrate_script" ]] && [[ -x "$migrate_script" ]]; then
215
+ if bash "$migrate_script" "$migrate_arg" >/dev/null 2>&1; then
216
+ print_info "$scheduler_name migrated from cron to launchd"
217
+ else
218
+ print_warning "$scheduler_name cron->launchd migration failed. Run: $migrate_hint"
219
+ fi
220
+ fi
221
+ installed=true
222
+ fi
223
+
224
+ if [[ "$installed" == "true" ]]; then
225
+ return 0
226
+ fi
227
+
228
+ return 1
229
+ }
189
230
  # Spinner for long-running operations
190
231
  # Usage: run_with_spinner "Installing package..." command arg1 arg2
191
232
  run_with_spinner() {
@@ -631,6 +672,8 @@ main() {
631
672
  bootstrap_repo "$@"
632
673
 
633
674
  parse_args "$@"
675
+ local _os
676
+ _os="$(uname -s)"
634
677
 
635
678
  # Auto-detect non-interactive terminals (CI/CD, agent shells, piped stdin)
636
679
  # Must run after parse_args so explicit --interactive flag takes precedence
@@ -798,25 +841,14 @@ main() {
798
841
  local auto_update_script="$HOME/.aidevops/agents/scripts/auto-update-helper.sh"
799
842
  if [[ -x "$auto_update_script" ]] && is_feature_enabled auto_update 2>/dev/null; then
800
843
  local _auto_update_installed=false
801
- if _launchd_has_agent "com.aidevops.aidevops-auto-update"; then
802
- _auto_update_installed=true
803
- elif _launchd_has_agent "com.aidevops.auto-update"; then
804
- # Old label — re-running enable will migrate to new label
805
- if bash "$auto_update_script" enable >/dev/null 2>&1; then
806
- print_info "Auto-update LaunchAgent migrated to new label"
807
- else
808
- print_warning "Auto-update label migration failed. Run: aidevops auto-update enable"
809
- fi
810
- _auto_update_installed=true
811
- elif crontab -l 2>/dev/null | grep -qF "aidevops-auto-update"; then
812
- if [[ "$(uname -s)" == "Darwin" ]]; then
813
- # macOS: cron entry exists but no launchd plist — migrate
814
- if bash "$auto_update_script" enable >/dev/null 2>&1; then
815
- print_info "Auto-update migrated from cron to launchd"
816
- else
817
- print_warning "Auto-update cron→launchd migration failed. Run: aidevops auto-update enable"
818
- fi
819
- fi
844
+ if _scheduler_detect_installed \
845
+ "Auto-update" \
846
+ "com.aidevops.aidevops-auto-update" \
847
+ "com.aidevops.auto-update" \
848
+ "aidevops-auto-update" \
849
+ "$auto_update_script" \
850
+ "enable" \
851
+ "aidevops auto-update enable"; then
820
852
  _auto_update_installed=true
821
853
  fi
822
854
  if [[ "$_auto_update_installed" == "false" ]]; then
@@ -847,7 +879,7 @@ main() {
847
879
  #
848
880
  # Ensure crontab has a global PATH= line (Linux only; macOS uses launchd env).
849
881
  # Must run before any cron entries are installed so they inherit the PATH.
850
- if [[ "$(uname -s)" != "Darwin" ]]; then
882
+ if [[ "$_os" != "Darwin" ]]; then
851
883
  _ensure_cron_path
852
884
  fi
853
885
 
@@ -937,14 +969,16 @@ main() {
937
969
  fi
938
970
 
939
971
  # Detect if pulse is already installed (for upgrade messaging)
972
+ # Uses shared helper to check both launchd and cron consistently
940
973
  local _pulse_installed=false
941
- if [[ "$(uname -s)" == "Darwin" ]]; then
942
- local pulse_plist="$HOME/Library/LaunchAgents/${pulse_label}.plist"
943
- if _launchd_has_agent "$pulse_label"; then
944
- _pulse_installed=true
945
- fi
946
- fi
947
- if [[ "$_pulse_installed" == "false" ]] && crontab -l 2>/dev/null | grep -qF "pulse-wrapper"; then
974
+ if _scheduler_detect_installed \
975
+ "Supervisor pulse" \
976
+ "$pulse_label" \
977
+ "" \
978
+ "pulse-wrapper" \
979
+ "" \
980
+ "" \
981
+ ""; then
948
982
  _pulse_installed=true
949
983
  fi
950
984
 
@@ -955,7 +989,7 @@ main() {
955
989
  if [[ "$_do_install" == "true" ]]; then
956
990
  mkdir -p "$HOME/.aidevops/logs"
957
991
 
958
- if [[ "$(uname -s)" == "Darwin" ]]; then
992
+ if [[ "$_os" == "Darwin" ]]; then
959
993
  # macOS: use launchd plist with wrapper
960
994
  local pulse_plist="$HOME/Library/LaunchAgents/${pulse_label}.plist"
961
995
 
@@ -1082,7 +1116,7 @@ PLIST
1082
1116
  fi
1083
1117
  elif [[ "$_pulse_lower" == "false" && "$_pulse_installed" == "true" ]]; then
1084
1118
  # User explicitly disabled but pulse is still installed — clean up
1085
- if [[ "$(uname -s)" == "Darwin" ]]; then
1119
+ if [[ "$_os" == "Darwin" ]]; then
1086
1120
  local pulse_plist="$HOME/Library/LaunchAgents/${pulse_label}.plist"
1087
1121
  if _launchd_has_agent "$pulse_label"; then
1088
1122
  launchctl unload "$pulse_plist" || true