aidevops 3.5.541 → 3.5.544

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/VERSION CHANGED
@@ -1 +1 @@
1
- 3.5.541
1
+ 3.5.544
package/aidevops.sh CHANGED
@@ -3,7 +3,7 @@
3
3
  # AI DevOps Framework CLI
4
4
  # Usage: aidevops <command> [options]
5
5
  #
6
- # Version: 3.5.541
6
+ # Version: 3.5.544
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": "3.5.541",
3
+ "version": "3.5.544",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -536,51 +536,132 @@ check_requirements() {
536
536
 
537
537
  # Check for quality/linting tools (shellcheck, shfmt, markdownlint)
538
538
  # These are optional but recommended for development
539
- check_quality_tools() {
540
- print_info "Checking quality tools..."
541
-
542
- local missing_tools=()
543
- local missing_npm_tools=()
544
-
545
- # Check for shellcheck
546
- if command -v shellcheck >/dev/null 2>&1; then
547
- print_success "shellcheck: $(shellcheck --version | head -1)"
539
+ _check_quality_tool() {
540
+ local tool_name="$1"
541
+ local version=""
542
+
543
+ if command -v "$tool_name" >/dev/null 2>&1; then
544
+ case "$tool_name" in
545
+ shellcheck)
546
+ version=$(shellcheck --version 2>/dev/null | head -1)
547
+ ;;
548
+ shfmt)
549
+ version=$(shfmt --version 2>/dev/null)
550
+ ;;
551
+ esac
552
+ print_success "$tool_name: ${version:-installed}"
548
553
  else
549
- missing_tools+=("shellcheck")
554
+ missing_tools+=("$tool_name")
550
555
  fi
551
556
 
552
- # Check for shfmt
553
- if command -v shfmt >/dev/null 2>&1; then
554
- print_success "shfmt: $(shfmt --version)"
555
- else
556
- missing_tools+=("shfmt")
557
- fi
557
+ return 0
558
+ }
559
+
560
+ _check_markdownlint_tool() {
561
+ local version=""
558
562
 
559
- # Check for markdownlint (accept either CLI variant)
560
563
  if command -v markdownlint >/dev/null 2>&1; then
561
- print_success "markdownlint: $(markdownlint --version 2>/dev/null | head -1)"
562
- elif command -v markdownlint-cli2 >/dev/null 2>&1; then
563
- print_success "markdownlint-cli2: $(markdownlint-cli2 --version 2>/dev/null | head -1)"
564
- else
565
- missing_npm_tools+=("markdownlint-cli2")
564
+ version=$(markdownlint --version 2>/dev/null | head -1)
565
+ print_success "markdownlint: ${version:-installed}"
566
+ return 0
566
567
  fi
567
568
 
568
- # If all tools present, return early
569
- if [[ ${#missing_tools[@]} -eq 0 && ${#missing_npm_tools[@]} -eq 0 ]]; then
570
- print_success "All quality tools installed"
569
+ if command -v markdownlint-cli2 >/dev/null 2>&1; then
570
+ version=$(markdownlint-cli2 --version 2>/dev/null | head -1)
571
+ print_success "markdownlint-cli2: ${version:-installed}"
571
572
  return 0
572
573
  fi
573
574
 
574
- # Show missing tools
575
+ missing_npm_tools+=("markdownlint-cli2")
576
+ return 0
577
+ }
578
+
579
+ _report_missing_quality_tools() {
575
580
  if [[ ${#missing_tools[@]} -gt 0 ]]; then
576
581
  print_warning "Missing quality tools: ${missing_tools[*]}"
577
582
  fi
583
+
578
584
  if [[ ${#missing_npm_tools[@]} -gt 0 ]]; then
579
585
  print_warning "Missing npm quality tools: ${missing_npm_tools[*]}"
580
586
  fi
587
+
581
588
  print_info "These tools are used by linters-local.sh for code quality checks"
589
+ return 0
590
+ }
591
+
592
+ _install_missing_quality_tools() {
593
+ local pkg_manager="$1"
594
+ local install_quality="Y"
595
+
596
+ if [[ "$pkg_manager" == "unknown" ]]; then
597
+ print_info "Install manually:"
598
+ echo " macOS: brew install ${missing_tools[*]}"
599
+ echo " Ubuntu/Debian: sudo apt-get install ${missing_tools[*]}"
600
+ echo " Fedora: sudo dnf install ${missing_tools[*]}"
601
+ return 0
602
+ fi
603
+
604
+ echo ""
605
+ setup_prompt install_quality "Install quality tools using $pkg_manager? [Y/n]: " "Y"
606
+
607
+ if [[ "$install_quality" =~ ^[Yy]?$ ]]; then
608
+ print_info "Installing ${missing_tools[*]}..."
609
+ if install_packages "$pkg_manager" "${missing_tools[@]}"; then
610
+ print_success "Quality tools installed successfully"
611
+ else
612
+ print_warning "Failed to install some quality tools - continuing anyway"
613
+ fi
614
+ else
615
+ print_info "Skipped quality tools installation"
616
+ print_info "Install later: $pkg_manager install ${missing_tools[*]}"
617
+ fi
618
+
619
+ return 0
620
+ }
621
+
622
+ _install_missing_npm_quality_tools() {
623
+ local install_npm_quality="Y"
624
+
625
+ if ! command -v npm >/dev/null 2>&1; then
626
+ print_warning "npm not found; cannot auto-install ${missing_npm_tools[*]}"
627
+ print_info "Install Node.js/npm, then run: npm install -g ${missing_npm_tools[*]}"
628
+ return 0
629
+ fi
630
+
631
+ echo ""
632
+ setup_prompt install_npm_quality "Install npm quality tools (npm install -g ${missing_npm_tools[*]})? [Y/n]: " "Y"
633
+ if [[ "$install_npm_quality" =~ ^[Yy]?$ ]]; then
634
+ print_info "Installing npm quality tools: ${missing_npm_tools[*]}..."
635
+ if npm install -g "${missing_npm_tools[@]}" >/dev/null 2>&1; then
636
+ print_success "npm quality tools installed successfully"
637
+ else
638
+ print_warning "Failed to install npm quality tools - continuing anyway"
639
+ fi
640
+ else
641
+ print_info "Skipped npm quality tools installation"
642
+ print_info "Install later: npm install -g ${missing_npm_tools[*]}"
643
+ fi
644
+
645
+ return 0
646
+ }
647
+
648
+ check_quality_tools() {
649
+ print_info "Checking quality tools..."
650
+
651
+ local missing_tools=()
652
+ local missing_npm_tools=()
653
+
654
+ _check_quality_tool "shellcheck"
655
+ _check_quality_tool "shfmt"
656
+ _check_markdownlint_tool
657
+
658
+ if [[ ${#missing_tools[@]} -eq 0 && ${#missing_npm_tools[@]} -eq 0 ]]; then
659
+ print_success "All quality tools installed"
660
+ return 0
661
+ fi
662
+
663
+ _report_missing_quality_tools
582
664
 
583
- # In non-interactive mode, just warn and continue
584
665
  if [[ "$NON_INTERACTIVE" == "true" ]]; then
585
666
  if [[ ${#missing_tools[@]} -gt 0 ]]; then
586
667
  print_info "Install later: brew install ${missing_tools[*]}"
@@ -599,49 +680,12 @@ check_quality_tools() {
599
680
  local pkg_manager
600
681
  pkg_manager=$(detect_package_manager)
601
682
 
602
- if [[ "$pkg_manager" == "unknown" && ${#missing_tools[@]} -gt 0 ]]; then
603
- print_info "Install manually:"
604
- echo " macOS: brew install ${missing_tools[*]}"
605
- echo " Ubuntu/Debian: sudo apt-get install ${missing_tools[*]}"
606
- echo " Fedora: sudo dnf install ${missing_tools[*]}"
607
- elif [[ ${#missing_tools[@]} -gt 0 ]]; then
608
- local install_quality="Y"
609
- echo ""
610
- setup_prompt install_quality "Install quality tools using $pkg_manager? [Y/n]: " "Y"
611
-
612
- if [[ "$install_quality" =~ ^[Yy]?$ ]]; then
613
- print_info "Installing ${missing_tools[*]}..."
614
- if install_packages "$pkg_manager" "${missing_tools[@]}"; then
615
- print_success "Quality tools installed successfully"
616
- else
617
- print_warning "Failed to install some quality tools - continuing anyway"
618
- fi
619
- else
620
- print_info "Skipped quality tools installation"
621
- print_info "Install later: $pkg_manager install ${missing_tools[*]}"
622
- fi
683
+ if [[ ${#missing_tools[@]} -gt 0 ]]; then
684
+ _install_missing_quality_tools "$pkg_manager"
623
685
  fi
624
686
 
625
687
  if [[ ${#missing_npm_tools[@]} -gt 0 ]]; then
626
- if ! command -v npm >/dev/null 2>&1; then
627
- print_warning "npm not found; cannot auto-install ${missing_npm_tools[*]}"
628
- print_info "Install Node.js/npm, then run: npm install -g ${missing_npm_tools[*]}"
629
- else
630
- local install_npm_quality="Y"
631
- echo ""
632
- setup_prompt install_npm_quality "Install npm quality tools (npm install -g ${missing_npm_tools[*]})? [Y/n]: " "Y"
633
- if [[ "$install_npm_quality" =~ ^[Yy]?$ ]]; then
634
- print_info "Installing npm quality tools: ${missing_npm_tools[*]}..."
635
- if npm install -g "${missing_npm_tools[@]}" >/dev/null 2>&1; then
636
- print_success "npm quality tools installed successfully"
637
- else
638
- print_warning "Failed to install npm quality tools - continuing anyway"
639
- fi
640
- else
641
- print_info "Skipped npm quality tools installation"
642
- print_info "Install later: npm install -g ${missing_npm_tools[*]}"
643
- fi
644
- fi
688
+ _install_missing_npm_quality_tools
645
689
  fi
646
690
 
647
691
  return 0
@@ -215,13 +215,11 @@ setup_supervisor_pulse() {
215
215
  return 0
216
216
  }
217
217
 
218
- # Install supervisor pulse via launchd (macOS)
219
- _install_pulse_launchd() {
218
+ # Clean up old/legacy pulse launchd plists before reinstalling.
219
+ # Args: $1=pulse_label, $2=pulse_plist path
220
+ _cleanup_old_pulse_plists() {
220
221
  local pulse_label="$1"
221
- local wrapper_script="$2"
222
- local opencode_bin="$3"
223
- local _pulse_installed="$4"
224
- local pulse_plist="$HOME/Library/LaunchAgents/${pulse_label}.plist"
222
+ local pulse_plist="$2"
225
223
 
226
224
  # Unload old plist if upgrading
227
225
  if _launchd_has_agent "$pulse_label"; then
@@ -235,21 +233,18 @@ _install_pulse_launchd() {
235
233
  launchctl unload "$old_plist" || true
236
234
  rm -f "$old_plist"
237
235
  fi
236
+ return 0
237
+ }
238
238
 
239
- # XML-escape paths for safe plist embedding (prevents injection
240
- # if $HOME or paths contain &, <, > characters)
241
- local _xml_wrapper_script _xml_home _xml_opencode_bin _xml_pulse_dir _xml_path
239
+ # Build XML environment variable fragment for headless model overrides.
240
+ # Reads configured overrides and emits XML key/string pairs for plist embedding.
241
+ # Prints the XML fragment to stdout (may be empty if no overrides configured).
242
+ _build_pulse_headless_env_xml() {
242
243
  local _headless_xml_env=""
243
244
  local _configured_headless_models _configured_pulse_model
244
245
  _configured_headless_models=$(_resolve_headless_models_override)
245
246
  _configured_pulse_model=$(_resolve_pulse_model_override)
246
- _xml_wrapper_script=$(_xml_escape "$wrapper_script")
247
- _xml_home=$(_xml_escape "$HOME")
248
- _xml_opencode_bin=$(_xml_escape "$opencode_bin")
249
- # Use neutral workspace path for PULSE_DIR so supervisor sessions
250
- # are not associated with any specific managed repo (GH#5136).
251
- _xml_pulse_dir=$(_xml_escape "${HOME}/.aidevops/.agent-workspace")
252
- _xml_path=$(_xml_escape "$PATH")
247
+
253
248
  if [[ -n "$_configured_headless_models" ]]; then
254
249
  local _xml_headless_models
255
250
  _xml_headless_models=$(_xml_escape "$_configured_headless_models")
@@ -275,8 +270,33 @@ _install_pulse_launchd() {
275
270
  _headless_xml_env+=$'\t\t'"<string>${_xml_headless_allowlist}</string>"
276
271
  fi
277
272
 
278
- # Write the plist (always regenerated to pick up config changes)
279
- cat >"$pulse_plist" <<PLIST
273
+ printf '%s' "$_headless_xml_env"
274
+ return 0
275
+ }
276
+
277
+ # Generate the full pulse launchd plist XML content.
278
+ # Args: $1=pulse_label, $2=wrapper_script, $3=opencode_bin
279
+ # Prints the complete plist XML to stdout.
280
+ _generate_pulse_plist_content() {
281
+ local pulse_label="$1"
282
+ local wrapper_script="$2"
283
+ local opencode_bin="$3"
284
+
285
+ # XML-escape paths for safe plist embedding (prevents injection
286
+ # if $HOME or paths contain &, <, > characters)
287
+ local _xml_wrapper_script _xml_home _xml_opencode_bin _xml_pulse_dir _xml_path
288
+ _xml_wrapper_script=$(_xml_escape "$wrapper_script")
289
+ _xml_home=$(_xml_escape "$HOME")
290
+ _xml_opencode_bin=$(_xml_escape "$opencode_bin")
291
+ # Use neutral workspace path for PULSE_DIR so supervisor sessions
292
+ # are not associated with any specific managed repo (GH#5136).
293
+ _xml_pulse_dir=$(_xml_escape "${HOME}/.aidevops/.agent-workspace")
294
+ _xml_path=$(_xml_escape "$PATH")
295
+
296
+ local _headless_xml_env
297
+ _headless_xml_env=$(_build_pulse_headless_env_xml)
298
+
299
+ cat <<PLIST
280
300
  <?xml version="1.0" encoding="UTF-8"?>
281
301
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
282
302
  <plist version="1.0">
@@ -315,6 +335,21 @@ _install_pulse_launchd() {
315
335
  </dict>
316
336
  </plist>
317
337
  PLIST
338
+ return 0
339
+ }
340
+
341
+ # Install supervisor pulse via launchd (macOS)
342
+ _install_pulse_launchd() {
343
+ local pulse_label="$1"
344
+ local wrapper_script="$2"
345
+ local opencode_bin="$3"
346
+ local _pulse_installed="$4"
347
+ local pulse_plist="$HOME/Library/LaunchAgents/${pulse_label}.plist"
348
+
349
+ _cleanup_old_pulse_plists "$pulse_label" "$pulse_plist"
350
+
351
+ # Write the plist (always regenerated to pick up config changes)
352
+ _generate_pulse_plist_content "$pulse_label" "$wrapper_script" "$opencode_bin" >"$pulse_plist"
318
353
 
319
354
  if launchctl load "$pulse_plist"; then
320
355
  if [[ "$_pulse_installed" == "true" ]]; then
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: 3.5.541
13
+ # Version: 3.5.544
14
14
  #
15
15
  # Quick Install:
16
16
  # npm install -g aidevops && aidevops update (recommended)
@@ -500,32 +500,24 @@ create_backup_with_rotation() {
500
500
  local backup_name="$2"
501
501
  local backup_base="$HOME/.aidevops/${backup_name}-backups"
502
502
  local backup_dir
503
- local backup_target
504
503
  backup_dir="$backup_base/$(date +%Y%m%d_%H%M%S)"
505
- backup_target="$backup_dir/$(basename "$source_path")"
506
504
 
507
505
  # Create backup directory
508
506
  mkdir -p "$backup_dir"
509
507
 
510
- # Copy source to backup
508
+ # Copy source to backup (tolerant of broken symlinks / missing entries)
511
509
  if [[ -d "$source_path" ]]; then
512
- mkdir -p "$backup_target"
513
- if command -v rsync >/dev/null 2>&1; then
514
- local rsync_status=0
515
- if rsync -a "$source_path/" "$backup_target/"; then
516
- rsync_status=0
517
- else
518
- rsync_status=$?
519
- fi
520
-
521
- if [[ "$rsync_status" -eq 24 ]]; then
522
- print_warning "Backup completed with missing source entries skipped: $source_path"
523
- elif [[ "$rsync_status" -ne 0 ]]; then
524
- print_error "Backup failed for $source_path (rsync exit $rsync_status)"
525
- return "$rsync_status"
510
+ if command -v rsync >/dev/null 2>&1 && rsync --help 2>&1 | grep -q -- '--ignore-missing-args'; then
511
+ # rsync >= 3.1.0: --ignore-missing-args skips missing/broken entries gracefully
512
+ if ! rsync -a --ignore-missing-args "$source_path/" "$backup_dir/$(basename "$source_path")/" 2>/dev/null; then
513
+ print_warning "Backup had partial failures (broken symlinks?), continuing"
526
514
  fi
527
515
  else
528
- cp -R "$source_path" "$backup_dir/"
516
+ # Fallback: cp -R may fail on broken symlinks under set -e,
517
+ # so run in a subshell that tolerates errors
518
+ if ! (cp -R "$source_path" "$backup_dir/" 2>/dev/null); then
519
+ print_warning "Backup had partial failures (broken symlinks?), continuing"
520
+ fi
529
521
  fi
530
522
  elif [[ -f "$source_path" ]]; then
531
523
  cp "$source_path" "$backup_dir/"