aidevops 2.172.17 → 2.172.19

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.
@@ -0,0 +1,572 @@
1
+ #!/usr/bin/env bash
2
+ # Core setup functions: requirements, permissions, location
3
+ # Part of aidevops setup.sh modularization (t316.3)
4
+
5
+ # Shell safety baseline
6
+ set -Eeuo pipefail
7
+ IFS=$'\n\t'
8
+ # shellcheck disable=SC2154 # rc is assigned by $? in the trap string
9
+ trap 'rc=$?; echo "[ERROR] ${BASH_SOURCE[0]}:${LINENO} exit $rc" >&2' ERR
10
+ shopt -s inherit_errexit 2>/dev/null || true
11
+
12
+ bootstrap_repo() {
13
+ # Detect if running from curl (no script directory context)
14
+ local script_path="${BASH_SOURCE[0]}"
15
+
16
+ # If script_path is empty, stdin, bash, or /dev/fd/* (process substitution), we're running from curl
17
+ # bash <(curl ...) produces paths like /dev/fd/63
18
+ if [[ -z "$script_path" || "$script_path" == "/dev/stdin" || "$script_path" == "bash" || "$script_path" == /dev/fd/* ]]; then
19
+ print_info "Remote install detected - bootstrapping repository..."
20
+
21
+ # On macOS, offer choice: install locally or in an OrbStack VM
22
+ if [[ "$(uname)" == "Darwin" ]]; then
23
+ echo ""
24
+ echo "Where would you like to install aidevops?"
25
+ echo ""
26
+ echo " 1) Install on this Mac (recommended)"
27
+ echo " 2) Install in a Linux VM (via OrbStack)"
28
+ echo ""
29
+ read -r -p "Choose [1/2] (default: 1): " install_target
30
+
31
+ if [[ "$install_target" == "2" ]]; then
32
+ print_info "Setting up OrbStack VM installation..."
33
+
34
+ # Install OrbStack if not present
35
+ if ! command -v orb >/dev/null 2>&1 && [[ ! -d "/Applications/OrbStack.app" ]]; then
36
+ if command -v brew >/dev/null 2>&1; then
37
+ print_info "Installing OrbStack via Homebrew..."
38
+ brew install --cask orbstack
39
+ else
40
+ print_error "Homebrew is required to install OrbStack"
41
+ echo "Install Homebrew first: /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
42
+ echo "Then re-run this installer."
43
+ exit 1
44
+ fi
45
+ fi
46
+
47
+ # Wait for OrbStack to be ready
48
+ if ! command -v orb >/dev/null 2>&1; then
49
+ print_info "Waiting for OrbStack CLI to become available..."
50
+ # OrbStack installs the CLI at /usr/local/bin/orb
51
+ local wait_count=0
52
+ while ! command -v orb >/dev/null 2>&1 && [[ $wait_count -lt 30 ]]; do
53
+ sleep 2
54
+ ((++wait_count))
55
+ done
56
+ if ! command -v orb >/dev/null 2>&1; then
57
+ print_error "OrbStack CLI not found after installation"
58
+ echo "Open OrbStack.app manually, then re-run this installer."
59
+ exit 1
60
+ fi
61
+ fi
62
+
63
+ # Create or use existing Ubuntu VM
64
+ local vm_name="aidevops"
65
+ if orb list 2>/dev/null | grep -qxF "$vm_name"; then
66
+ print_info "Using existing OrbStack VM: $vm_name"
67
+ else
68
+ print_info "Creating Ubuntu VM: $vm_name..."
69
+ orb create ubuntu "$vm_name"
70
+ fi
71
+
72
+ # Run the installer inside the VM
73
+ print_info "Installing aidevops inside the VM..."
74
+ echo ""
75
+ orb run -m "$vm_name" bash -c 'bash <(curl -fsSL https://aidevops.sh/install)'
76
+
77
+ echo ""
78
+ print_success "aidevops installed in OrbStack VM: $vm_name"
79
+ echo ""
80
+ echo "To use aidevops in the VM:"
81
+ echo " orb shell $vm_name # Enter the VM"
82
+ echo " orb run -m $vm_name opencode # Run OpenCode directly"
83
+ echo ""
84
+ exit 0
85
+ fi
86
+ fi
87
+
88
+ # Auto-install git if missing (required for cloning)
89
+ if ! command -v git >/dev/null 2>&1; then
90
+ print_warning "git is required but not installed - attempting auto-install..."
91
+ if [[ "$(uname)" == "Darwin" ]]; then
92
+ # macOS: xcode-select --install triggers git install
93
+ print_info "Installing Xcode Command Line Tools (includes git)..."
94
+ if xcode-select --install 2>/dev/null; then
95
+ # Wait for installation to complete (timeout after 5 minutes)
96
+ print_info "Waiting for Xcode CLT installation to complete (timeout: 5m)..."
97
+ local xcode_wait=0
98
+ local xcode_max_wait=300
99
+ until command -v git >/dev/null 2>&1; do
100
+ sleep 5
101
+ xcode_wait=$((xcode_wait + 5))
102
+ if [[ $xcode_wait -ge $xcode_max_wait ]]; then
103
+ print_error "Timed out waiting for Xcode CLT installation after ${xcode_max_wait}s"
104
+ echo "Complete the installation manually, then re-run this installer."
105
+ exit 1
106
+ fi
107
+ done
108
+ print_success "git installed via Xcode Command Line Tools"
109
+ else
110
+ # Already installed or failed
111
+ if ! command -v git >/dev/null 2>&1; then
112
+ print_error "git installation failed"
113
+ echo "Install git manually: brew install git (macOS)"
114
+ exit 1
115
+ fi
116
+ fi
117
+ elif command -v apt-get >/dev/null 2>&1; then
118
+ print_info "Installing git via apt..."
119
+ sudo apt-get update -qq && sudo apt-get install -y -qq git
120
+ if ! command -v git >/dev/null 2>&1; then
121
+ print_error "git installation failed"
122
+ exit 1
123
+ fi
124
+ print_success "git installed"
125
+ elif command -v dnf >/dev/null 2>&1; then
126
+ print_info "Installing git via dnf..."
127
+ sudo dnf install -y git
128
+ if ! command -v git >/dev/null 2>&1; then
129
+ print_error "git installation failed"
130
+ exit 1
131
+ fi
132
+ print_success "git installed"
133
+ elif command -v yum >/dev/null 2>&1; then
134
+ print_info "Installing git via yum..."
135
+ sudo yum install -y git
136
+ if ! command -v git >/dev/null 2>&1; then
137
+ print_error "git installation failed"
138
+ exit 1
139
+ fi
140
+ print_success "git installed"
141
+ elif command -v pacman >/dev/null 2>&1; then
142
+ print_info "Installing git via pacman..."
143
+ sudo pacman -S --noconfirm git
144
+ if ! command -v git >/dev/null 2>&1; then
145
+ print_error "git installation failed"
146
+ exit 1
147
+ fi
148
+ print_success "git installed"
149
+ elif command -v apk >/dev/null 2>&1; then
150
+ print_info "Installing git via apk..."
151
+ sudo apk add git
152
+ if ! command -v git >/dev/null 2>&1; then
153
+ print_error "git installation failed"
154
+ exit 1
155
+ fi
156
+ print_success "git installed"
157
+ else
158
+ print_error "git is required but not installed and no supported package manager found"
159
+ echo "Install git manually and re-run the installer"
160
+ exit 1
161
+ fi
162
+ fi
163
+
164
+ # Create parent directory
165
+ mkdir -p "$(dirname "$INSTALL_DIR")"
166
+
167
+ if [[ -d "$INSTALL_DIR/.git" ]]; then
168
+ print_info "Existing installation found - updating..."
169
+ cd "$INSTALL_DIR" || exit 1
170
+ if ! git pull --ff-only; then
171
+ print_warning "Git pull failed - trying reset to origin/main"
172
+ git fetch origin
173
+ git reset --hard origin/main
174
+ fi
175
+ else
176
+ print_info "Cloning aidevops to $INSTALL_DIR..."
177
+ if [[ -d "$INSTALL_DIR" ]]; then
178
+ print_warning "Directory exists but is not a git repo - backing up"
179
+ mv "$INSTALL_DIR" "$INSTALL_DIR.backup.$(date +%Y%m%d_%H%M%S)"
180
+ fi
181
+ if ! git clone "$REPO_URL" "$INSTALL_DIR"; then
182
+ print_error "Failed to clone repository"
183
+ exit 1
184
+ fi
185
+ fi
186
+
187
+ print_success "Repository ready at $INSTALL_DIR"
188
+
189
+ # Re-execute the local script
190
+ cd "$INSTALL_DIR" || exit 1
191
+ exec bash "./setup.sh" "$@"
192
+ fi
193
+ }
194
+
195
+ # Detect package manager
196
+ detect_package_manager() {
197
+ if command -v brew >/dev/null 2>&1; then
198
+ echo "brew"
199
+ elif command -v apt-get >/dev/null 2>&1; then
200
+ echo "apt"
201
+ elif command -v dnf >/dev/null 2>&1; then
202
+ echo "dnf"
203
+ elif command -v yum >/dev/null 2>&1; then
204
+ echo "yum"
205
+ elif command -v pacman >/dev/null 2>&1; then
206
+ echo "pacman"
207
+ elif command -v apk >/dev/null 2>&1; then
208
+ echo "apk"
209
+ else
210
+ echo "unknown"
211
+ fi
212
+ }
213
+
214
+ # Install packages using detected package manager
215
+ # For Homebrew: runs 'brew update' with a spinner first (can take 30s+),
216
+ # then installs packages with HOMEBREW_NO_AUTO_UPDATE to avoid a second update.
217
+ install_packages() {
218
+ local pkg_manager="$1"
219
+ shift
220
+ local packages=("$@")
221
+
222
+ case "$pkg_manager" in
223
+ brew)
224
+ # Run brew update with spinner (Homebrew auto-update is slow and silent)
225
+ run_with_spinner "Updating Homebrew" brew update
226
+ # Install with auto-update disabled (we just ran it)
227
+ # Note: run_with_spinner auto-exports HOMEBREW_NO_AUTO_UPDATE for brew commands
228
+ run_with_spinner "Installing ${packages[*]}" brew install "${packages[@]}"
229
+ ;;
230
+ apt)
231
+ run_with_spinner "Updating package lists" sudo apt-get update -qq
232
+ run_with_spinner "Installing ${packages[*]}" sudo apt-get install -y -qq "${packages[@]}"
233
+ ;;
234
+ dnf)
235
+ run_with_spinner "Installing ${packages[*]}" sudo dnf install -y "${packages[@]}"
236
+ ;;
237
+ yum)
238
+ run_with_spinner "Installing ${packages[*]}" sudo yum install -y "${packages[@]}"
239
+ ;;
240
+ pacman)
241
+ run_with_spinner "Installing ${packages[*]}" sudo pacman -S --noconfirm "${packages[@]}"
242
+ ;;
243
+ apk)
244
+ run_with_spinner "Installing ${packages[*]}" sudo apk add "${packages[@]}"
245
+ ;;
246
+ *)
247
+ return 1
248
+ ;;
249
+ esac
250
+ }
251
+
252
+ # Offer to install Homebrew (Linuxbrew) on Linux when brew is not available
253
+ # Many tools in the aidevops ecosystem (Beads, Worktrunk, bv) are distributed
254
+ # via Homebrew taps. On macOS, brew is almost always present. On Linux, this
255
+ # function offers to install it so those tools can be installed automatically.
256
+ # Returns: 0 if brew is now available, 1 if user declined or install failed
257
+ ensure_homebrew() {
258
+ # Already available
259
+ if command -v brew &>/dev/null; then
260
+ return 0
261
+ fi
262
+
263
+ # Only offer on Linux (macOS users should install Homebrew themselves)
264
+ if [[ "$(uname)" == "Darwin" ]]; then
265
+ print_warning "Homebrew not found. Install from https://brew.sh"
266
+ return 1
267
+ fi
268
+
269
+ # Non-interactive mode: skip
270
+ if [[ "$NON_INTERACTIVE" == "true" ]]; then
271
+ return 1
272
+ fi
273
+
274
+ echo ""
275
+ print_info "Homebrew (Linuxbrew) is not installed."
276
+ print_info "Several optional tools (Beads CLI, Worktrunk, bv) install via Homebrew taps."
277
+ echo ""
278
+ read -r -p "Install Homebrew for Linux? [Y/n]: " install_brew
279
+
280
+ if [[ ! "$install_brew" =~ ^[Yy]?$ ]]; then
281
+ print_info "Skipped Homebrew installation"
282
+ return 1
283
+ fi
284
+
285
+ print_info "Installing Homebrew (Linuxbrew)..."
286
+
287
+ # Prerequisites for Linuxbrew
288
+ if command -v apt-get &>/dev/null; then
289
+ sudo apt-get update -qq
290
+ sudo apt-get install -y -qq build-essential procps curl file git
291
+ elif command -v dnf &>/dev/null; then
292
+ sudo dnf groupinstall -y 'Development Tools'
293
+ sudo dnf install -y procps-ng curl file git
294
+ elif command -v yum &>/dev/null; then
295
+ sudo yum groupinstall -y 'Development Tools'
296
+ sudo yum install -y procps-ng curl file git
297
+ fi
298
+
299
+ # Install Homebrew using verified_install pattern
300
+ if verified_install "Homebrew" "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"; then
301
+ # Add Homebrew to PATH for this session
302
+ local brew_prefix="/home/linuxbrew/.linuxbrew"
303
+ if [[ -x "$brew_prefix/bin/brew" ]]; then
304
+ eval "$("$brew_prefix/bin/brew" shellenv)"
305
+ fi
306
+
307
+ # Persist to shell rc files
308
+ local brew_line="eval \"\$($brew_prefix/bin/brew shellenv)\""
309
+ local rc_file
310
+ while IFS= read -r rc_file; do
311
+ [[ -z "$rc_file" ]] && continue
312
+ if ! grep -q 'linuxbrew' "$rc_file" 2>/dev/null; then
313
+ {
314
+ echo ""
315
+ echo "# Homebrew (Linuxbrew) - added by aidevops setup"
316
+ echo "$brew_line"
317
+ } >>"$rc_file"
318
+ fi
319
+ done < <(get_all_shell_rcs)
320
+
321
+ if command -v brew &>/dev/null; then
322
+ print_success "Homebrew installed and added to PATH"
323
+ return 0
324
+ else
325
+ print_warning "Homebrew installed but not yet in PATH. Restart your shell or run:"
326
+ echo " $brew_line"
327
+ return 1
328
+ fi
329
+ else
330
+ print_warning "Homebrew installation failed"
331
+ return 1
332
+ fi
333
+ }
334
+
335
+ # Check system requirements
336
+ check_requirements() {
337
+ print_info "Checking system requirements..."
338
+
339
+ # Ensure Homebrew is in PATH (macOS Apple Silicon)
340
+ if [[ -x "/opt/homebrew/bin/brew" ]] && [[ ":$PATH:" != *":/opt/homebrew/bin:"* ]]; then
341
+ eval "$(/opt/homebrew/bin/brew shellenv)"
342
+ print_warning "Homebrew not in PATH - added for this session"
343
+
344
+ # Only modify rc files during interactive setup (not updates)
345
+ # Users may intentionally remove these lines; re-adding on every update is harmful
346
+ if [[ "$NON_INTERACTIVE" != "true" ]]; then
347
+ # shellcheck disable=SC2016 # brew_line is written to rc files; must expand at shell startup, not now
348
+ local brew_line='eval "$(/opt/homebrew/bin/brew shellenv)"'
349
+ local fixed_rc=false
350
+ local rc_file
351
+ while IFS= read -r rc_file; do
352
+ [[ -z "$rc_file" ]] && continue
353
+ if ! grep -q '/opt/homebrew/bin/brew' "$rc_file" 2>/dev/null; then
354
+ {
355
+ echo ""
356
+ echo "# Homebrew (added by aidevops setup)"
357
+ echo "$brew_line"
358
+ } >>"$rc_file"
359
+ print_success "Added Homebrew to PATH in $rc_file"
360
+ fixed_rc=true
361
+ fi
362
+ done < <(get_all_shell_rcs)
363
+
364
+ if [[ "$fixed_rc" == "false" ]]; then
365
+ echo ""
366
+ echo " To fix permanently, add to your shell rc file:"
367
+ echo " $brew_line"
368
+ echo ""
369
+ fi
370
+ fi
371
+ fi
372
+
373
+ # Also check Intel Mac Homebrew location
374
+ # Skip entirely on Apple Silicon (ARM brew exists) — Intel brew shellenv prepends
375
+ # /usr/local/bin to PATH, causing x86 binaries to shadow ARM ones (GH#1510)
376
+ if [[ -x "/usr/local/bin/brew" ]] && [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then
377
+ # On Apple Silicon with dual brew, do NOT add Intel brew to PATH — it breaks ARM brew
378
+ if [[ -x "/opt/homebrew/bin/brew" ]]; then
379
+ print_info "Intel Homebrew found but skipped (Apple Silicon uses /opt/homebrew)"
380
+ else
381
+ eval "$(/usr/local/bin/brew shellenv)"
382
+ print_warning "Homebrew (/usr/local/bin) not in PATH - added for this session"
383
+
384
+ # Only modify rc files during interactive setup (not updates)
385
+ if [[ "$NON_INTERACTIVE" != "true" ]]; then
386
+ # shellcheck disable=SC2016 # intel_brew_line is written to rc files; must expand at shell startup, not now
387
+ local intel_brew_line='eval "$(/usr/local/bin/brew shellenv)"'
388
+ local intel_fixed_rc=false
389
+ local intel_rc
390
+ while IFS= read -r intel_rc; do
391
+ [[ -z "$intel_rc" ]] && continue
392
+ if ! grep -q '/usr/local/bin/brew' "$intel_rc" 2>/dev/null; then
393
+ {
394
+ echo ""
395
+ echo "# Homebrew Intel Mac (added by aidevops setup)"
396
+ echo "$intel_brew_line"
397
+ } >>"$intel_rc"
398
+ print_success "Added Homebrew to PATH in $intel_rc"
399
+ intel_fixed_rc=true
400
+ fi
401
+ done < <(get_all_shell_rcs)
402
+
403
+ if [[ "$intel_fixed_rc" == "false" ]]; then
404
+ echo ""
405
+ echo " To fix permanently, add to your shell rc file:"
406
+ echo " $intel_brew_line"
407
+ echo ""
408
+ fi
409
+ fi
410
+ fi
411
+ fi
412
+
413
+ local missing_deps=()
414
+
415
+ # Check for required commands
416
+ command -v jq >/dev/null 2>&1 || missing_deps+=("jq")
417
+ command -v curl >/dev/null 2>&1 || missing_deps+=("curl")
418
+ command -v ssh >/dev/null 2>&1 || missing_deps+=("ssh")
419
+
420
+ if [[ ${#missing_deps[@]} -gt 0 ]]; then
421
+ print_warning "Missing required dependencies: ${missing_deps[*]}"
422
+
423
+ local pkg_manager
424
+ pkg_manager=$(detect_package_manager)
425
+
426
+ if [[ "$pkg_manager" == "unknown" ]]; then
427
+ print_error "Could not detect package manager"
428
+ echo ""
429
+ echo "Please install manually:"
430
+ echo " macOS: brew install ${missing_deps[*]}"
431
+ echo " Ubuntu/Debian: sudo apt-get install ${missing_deps[*]}"
432
+ echo " Fedora: sudo dnf install ${missing_deps[*]}"
433
+ echo " CentOS/RHEL: sudo yum install ${missing_deps[*]}"
434
+ echo " Arch: sudo pacman -S ${missing_deps[*]}"
435
+ echo " Alpine: sudo apk add ${missing_deps[*]}"
436
+ exit 1
437
+ fi
438
+
439
+ # In non-interactive mode, fail fast on missing deps
440
+ if [[ "$NON_INTERACTIVE" == "true" ]]; then
441
+ print_error "Cannot continue without required dependencies (non-interactive mode)"
442
+ exit 1
443
+ fi
444
+
445
+ echo ""
446
+ read -r -p "Install missing dependencies using $pkg_manager? [Y/n]: " install_deps
447
+
448
+ if [[ "$install_deps" =~ ^[Yy]?$ ]]; then
449
+ print_info "Installing ${missing_deps[*]}..."
450
+ if install_packages "$pkg_manager" "${missing_deps[@]}"; then
451
+ print_success "Dependencies installed successfully"
452
+ else
453
+ print_error "Failed to install dependencies"
454
+ exit 1
455
+ fi
456
+ else
457
+ print_error "Cannot continue without required dependencies"
458
+ exit 1
459
+ fi
460
+ fi
461
+
462
+ print_success "All required dependencies found"
463
+ }
464
+
465
+ # Check for quality/linting tools (shellcheck, shfmt)
466
+ # These are optional but recommended for development
467
+ check_quality_tools() {
468
+ print_info "Checking quality tools..."
469
+
470
+ local missing_tools=()
471
+
472
+ # Check for shellcheck
473
+ if command -v shellcheck >/dev/null 2>&1; then
474
+ print_success "shellcheck: $(shellcheck --version | head -1)"
475
+ else
476
+ missing_tools+=("shellcheck")
477
+ fi
478
+
479
+ # Check for shfmt
480
+ if command -v shfmt >/dev/null 2>&1; then
481
+ print_success "shfmt: $(shfmt --version)"
482
+ else
483
+ missing_tools+=("shfmt")
484
+ fi
485
+
486
+ # If all tools present, return early
487
+ if [[ ${#missing_tools[@]} -eq 0 ]]; then
488
+ print_success "All quality tools installed"
489
+ return 0
490
+ fi
491
+
492
+ # Show missing tools
493
+ print_warning "Missing quality tools: ${missing_tools[*]}"
494
+ print_info "These tools are used by linters-local.sh for code quality checks"
495
+
496
+ # In non-interactive mode, just warn and continue
497
+ if [[ "$NON_INTERACTIVE" == "true" ]]; then
498
+ print_info "Install later: brew install ${missing_tools[*]}"
499
+ return 0
500
+ fi
501
+
502
+ # Offer to install
503
+ local pkg_manager
504
+ pkg_manager=$(detect_package_manager)
505
+
506
+ if [[ "$pkg_manager" == "unknown" ]]; then
507
+ print_info "Install manually:"
508
+ echo " macOS: brew install ${missing_tools[*]}"
509
+ echo " Ubuntu/Debian: sudo apt-get install ${missing_tools[*]}"
510
+ echo " Fedora: sudo dnf install ${missing_tools[*]}"
511
+ return 0
512
+ fi
513
+
514
+ echo ""
515
+ read -r -p "Install quality tools using $pkg_manager? [Y/n]: " install_quality
516
+
517
+ if [[ "$install_quality" =~ ^[Yy]?$ ]]; then
518
+ print_info "Installing ${missing_tools[*]}..."
519
+ if install_packages "$pkg_manager" "${missing_tools[@]}"; then
520
+ print_success "Quality tools installed successfully"
521
+ else
522
+ print_warning "Failed to install some quality tools - continuing anyway"
523
+ fi
524
+ else
525
+ print_info "Skipped quality tools installation"
526
+ print_info "Install later: $pkg_manager install ${missing_tools[*]}"
527
+ fi
528
+
529
+ return 0
530
+ }
531
+
532
+ verify_location() {
533
+ local current_dir
534
+ current_dir="$(pwd)"
535
+ local expected_location="$HOME/Git/aidevops"
536
+
537
+ if [[ "$current_dir" != "$expected_location" ]]; then
538
+ print_warning "Repository is not in the recommended location"
539
+ print_info "Current location: $current_dir"
540
+ print_info "Recommended location: $expected_location"
541
+ echo ""
542
+ echo "For optimal AI assistant integration, consider moving this repository to:"
543
+ echo " mkdir -p ~/git"
544
+ echo " mv '$current_dir' '$expected_location'"
545
+ echo ""
546
+ else
547
+ print_success "Repository is in the recommended location: $expected_location"
548
+ fi
549
+ return 0
550
+ }
551
+
552
+ set_permissions() {
553
+ print_info "Setting proper file permissions..."
554
+
555
+ local deployed_dir="$HOME/.aidevops/agents"
556
+
557
+ # Set permissions on DEPLOYED agents (not the git repo, to avoid dirtying the working tree)
558
+ # See: https://github.com/marcusquinn/aidevops/issues/2286
559
+ if [[ -d "$deployed_dir/scripts" ]]; then
560
+ chmod +x "$deployed_dir/scripts/"*.sh 2>/dev/null || true
561
+ # Also handle modularised subdirectories (e.g. memory/, supervisor-modules/)
562
+ find "$deployed_dir/scripts" -mindepth 2 -name "*.sh" -exec chmod +x {} + 2>/dev/null || true
563
+ fi
564
+
565
+ # Secure configuration files (these are in the user's config dir, not the repo)
566
+ chmod 600 "$HOME/.config/aidevops/"*.json 2>/dev/null || true
567
+ # Also secure repo-local configs if present (for interactive setup from repo root)
568
+ chmod 600 configs/*.json 2>/dev/null || true
569
+
570
+ print_success "File permissions set"
571
+ return 0
572
+ }