aidevops 2.172.18 → 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.
- package/VERSION +1 -1
- package/aidevops.sh +1 -1
- package/package.json +2 -1
- package/scripts/npm-postinstall.cjs +13 -2
- package/setup-modules/agent-deploy.sh +627 -0
- package/setup-modules/config.sh +183 -0
- package/setup-modules/core.sh +572 -0
- package/setup-modules/mcp-setup.sh +766 -0
- package/setup-modules/migrations.sh +961 -0
- package/setup-modules/plugins.sh +588 -0
- package/setup-modules/shell-env.sh +892 -0
- package/setup-modules/tool-install.sh +1373 -0
- package/setup.sh +1 -1
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shell environment setup functions: oh-my-zsh, shell-compat, aliases, terminal-title
|
|
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
|
+
# Detect the shell currently executing this script (zsh, bash, or fallback)
|
|
13
|
+
detect_running_shell() {
|
|
14
|
+
if [[ -n "${ZSH_VERSION:-}" ]]; then
|
|
15
|
+
echo "zsh"
|
|
16
|
+
elif [[ -n "${BASH_VERSION:-}" ]]; then
|
|
17
|
+
echo "bash"
|
|
18
|
+
else
|
|
19
|
+
basename "${SHELL:-/bin/bash}"
|
|
20
|
+
fi
|
|
21
|
+
return 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Detect the user's default login shell from $SHELL
|
|
25
|
+
detect_default_shell() {
|
|
26
|
+
basename "${SHELL:-/bin/bash}"
|
|
27
|
+
return 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Usage: get_shell_rc "zsh" or get_shell_rc "bash"
|
|
31
|
+
get_shell_rc() {
|
|
32
|
+
local shell_name="$1"
|
|
33
|
+
case "$shell_name" in
|
|
34
|
+
zsh)
|
|
35
|
+
echo "$HOME/.zshrc"
|
|
36
|
+
;;
|
|
37
|
+
bash)
|
|
38
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
39
|
+
echo "$HOME/.bash_profile"
|
|
40
|
+
else
|
|
41
|
+
echo "$HOME/.bashrc"
|
|
42
|
+
fi
|
|
43
|
+
;;
|
|
44
|
+
fish)
|
|
45
|
+
echo "$HOME/.config/fish/config.fish"
|
|
46
|
+
;;
|
|
47
|
+
ksh)
|
|
48
|
+
echo "$HOME/.kshrc"
|
|
49
|
+
;;
|
|
50
|
+
*)
|
|
51
|
+
# Fallback: check common rc files
|
|
52
|
+
if [[ -f "$HOME/.zshrc" ]]; then
|
|
53
|
+
echo "$HOME/.zshrc"
|
|
54
|
+
elif [[ -f "$HOME/.bashrc" ]]; then
|
|
55
|
+
echo "$HOME/.bashrc"
|
|
56
|
+
elif [[ -f "$HOME/.bash_profile" ]]; then
|
|
57
|
+
echo "$HOME/.bash_profile"
|
|
58
|
+
else
|
|
59
|
+
echo ""
|
|
60
|
+
fi
|
|
61
|
+
;;
|
|
62
|
+
esac
|
|
63
|
+
return 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Return all relevant shell rc file paths for the current platform
|
|
67
|
+
get_all_shell_rcs() {
|
|
68
|
+
local rcs=()
|
|
69
|
+
|
|
70
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
71
|
+
# macOS: always include zsh (default since Catalina) and bash_profile
|
|
72
|
+
[[ -f "$HOME/.zshrc" ]] && rcs+=("$HOME/.zshrc")
|
|
73
|
+
[[ -f "$HOME/.bash_profile" ]] && rcs+=("$HOME/.bash_profile")
|
|
74
|
+
# If neither exists, create .zshrc (macOS default)
|
|
75
|
+
if [[ ${#rcs[@]} -eq 0 ]]; then
|
|
76
|
+
touch "$HOME/.zshrc"
|
|
77
|
+
rcs+=("$HOME/.zshrc")
|
|
78
|
+
fi
|
|
79
|
+
else
|
|
80
|
+
# Linux: use the default shell's rc file
|
|
81
|
+
local default_shell
|
|
82
|
+
default_shell=$(detect_default_shell)
|
|
83
|
+
local rc
|
|
84
|
+
rc=$(get_shell_rc "$default_shell")
|
|
85
|
+
if [[ -n "$rc" ]]; then
|
|
86
|
+
rcs+=("$rc")
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
printf '%s\n' "${rcs[@]}"
|
|
91
|
+
return 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Offer to install Oh My Zsh if zsh is the default shell and OMZ is not present
|
|
95
|
+
setup_oh_my_zsh() {
|
|
96
|
+
# Only relevant if zsh is available
|
|
97
|
+
if ! command -v zsh >/dev/null 2>&1; then
|
|
98
|
+
print_info "zsh not found - skipping Oh My Zsh setup"
|
|
99
|
+
return 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Check if Oh My Zsh is already installed
|
|
103
|
+
if [[ -d "$HOME/.oh-my-zsh" ]]; then
|
|
104
|
+
print_success "Oh My Zsh already installed"
|
|
105
|
+
return 0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
local default_shell
|
|
109
|
+
default_shell=$(detect_default_shell)
|
|
110
|
+
|
|
111
|
+
# Only offer if zsh is the default shell (or on macOS where it's the system default)
|
|
112
|
+
if [[ "$default_shell" != "zsh" && "$(uname)" != "Darwin" ]]; then
|
|
113
|
+
print_info "Default shell is $default_shell (not zsh) - skipping Oh My Zsh"
|
|
114
|
+
return 0
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
print_info "Oh My Zsh enhances zsh with themes, plugins, and completions"
|
|
118
|
+
echo " Many tools installed later (git, fd, brew) benefit from Oh My Zsh plugins."
|
|
119
|
+
echo " This is optional - plain zsh works fine without it."
|
|
120
|
+
echo ""
|
|
121
|
+
|
|
122
|
+
read -r -p "Install Oh My Zsh? [y/N]: " install_omz
|
|
123
|
+
|
|
124
|
+
if [[ "$install_omz" =~ ^[Yy]$ ]]; then
|
|
125
|
+
print_info "Installing Oh My Zsh..."
|
|
126
|
+
# Use verified download + --unattended to avoid changing the shell or starting zsh
|
|
127
|
+
# shellcheck disable=SC2034 # Read by verified_install() in setup.sh
|
|
128
|
+
VERIFIED_INSTALL_SHELL="sh"
|
|
129
|
+
if verified_install "Oh My Zsh" "https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh" --unattended; then
|
|
130
|
+
print_success "Oh My Zsh installed"
|
|
131
|
+
|
|
132
|
+
# Ensure .zshrc exists (Oh My Zsh creates it, but verify)
|
|
133
|
+
if [[ ! -f "$HOME/.zshrc" ]]; then
|
|
134
|
+
print_warning ".zshrc not created - Oh My Zsh may not have installed correctly"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# If the user's default shell isn't zsh, offer to change it
|
|
138
|
+
if [[ "$default_shell" != "zsh" ]]; then
|
|
139
|
+
echo ""
|
|
140
|
+
read -r -p "Change default shell to zsh? [y/N]: " change_shell
|
|
141
|
+
if [[ "$change_shell" =~ ^[Yy]$ ]]; then
|
|
142
|
+
if chsh -s "$(command -v zsh)"; then
|
|
143
|
+
print_success "Default shell changed to zsh"
|
|
144
|
+
print_info "Restart your terminal for the change to take effect"
|
|
145
|
+
else
|
|
146
|
+
print_warning "Failed to change shell - run manually: chsh -s $(command -v zsh)"
|
|
147
|
+
fi
|
|
148
|
+
fi
|
|
149
|
+
fi
|
|
150
|
+
else
|
|
151
|
+
print_warning "Oh My Zsh installation failed"
|
|
152
|
+
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"
|
|
153
|
+
fi
|
|
154
|
+
else
|
|
155
|
+
print_info "Skipped Oh My Zsh installation"
|
|
156
|
+
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"
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
return 0
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Extract portable customizations from bash configs into a shared profile for cross-shell use
|
|
163
|
+
setup_shell_compatibility() {
|
|
164
|
+
print_info "Setting up cross-shell compatibility..."
|
|
165
|
+
|
|
166
|
+
local shared_profile="$HOME/.shell_common"
|
|
167
|
+
local zsh_rc="$HOME/.zshrc"
|
|
168
|
+
|
|
169
|
+
# If shared profile already exists, we've already set this up
|
|
170
|
+
if [[ -f "$shared_profile" ]]; then
|
|
171
|
+
print_success "Cross-shell compatibility already configured ($shared_profile)"
|
|
172
|
+
return 0
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Need both bash and zsh to be relevant
|
|
176
|
+
if ! command -v zsh >/dev/null 2>&1; then
|
|
177
|
+
print_info "zsh not installed - cross-shell setup not needed"
|
|
178
|
+
return 0
|
|
179
|
+
fi
|
|
180
|
+
if ! command -v bash >/dev/null 2>&1; then
|
|
181
|
+
print_info "bash not installed - cross-shell setup not needed"
|
|
182
|
+
return 0
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# Collect all bash config files that exist
|
|
186
|
+
# macOS: .bash_profile (login) + .bashrc (interactive, often sourced by .bash_profile)
|
|
187
|
+
# Linux: .bashrc (primary) + .bash_profile (login, often sources .bashrc)
|
|
188
|
+
# We check all of them on both platforms since tools write to either
|
|
189
|
+
local -a bash_files=()
|
|
190
|
+
[[ -f "$HOME/.bash_profile" ]] && bash_files+=("$HOME/.bash_profile")
|
|
191
|
+
[[ -f "$HOME/.bashrc" ]] && bash_files+=("$HOME/.bashrc")
|
|
192
|
+
[[ -f "$HOME/.profile" ]] && bash_files+=("$HOME/.profile")
|
|
193
|
+
|
|
194
|
+
if [[ ${#bash_files[@]} -eq 0 ]]; then
|
|
195
|
+
print_info "No bash config files found - skipping cross-shell setup"
|
|
196
|
+
return 0
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
if [[ ! -f "$zsh_rc" ]]; then
|
|
200
|
+
print_info "No .zshrc found - skipping cross-shell setup"
|
|
201
|
+
return 0
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
# Count customizations across all bash config files
|
|
205
|
+
local total_exports=0
|
|
206
|
+
local total_aliases=0
|
|
207
|
+
local total_paths=0
|
|
208
|
+
|
|
209
|
+
for src_file in "${bash_files[@]}"; do
|
|
210
|
+
local n
|
|
211
|
+
# grep -c exits 1 on no match; || : prevents ERR trap noise
|
|
212
|
+
# File existence already verified when building bash_files array
|
|
213
|
+
n=$(grep -cE '^\s*export\s+[A-Z]' "$src_file" || :)
|
|
214
|
+
total_exports=$((total_exports + ${n:-0}))
|
|
215
|
+
n=$(grep -cE '^\s*alias\s+' "$src_file" || :)
|
|
216
|
+
total_aliases=$((total_aliases + ${n:-0}))
|
|
217
|
+
n=$(grep -cE 'PATH.*=' "$src_file" || :)
|
|
218
|
+
total_paths=$((total_paths + ${n:-0}))
|
|
219
|
+
done
|
|
220
|
+
|
|
221
|
+
if [[ $total_exports -eq 0 && $total_aliases -eq 0 && $total_paths -eq 0 ]]; then
|
|
222
|
+
print_info "No bash customizations detected - skipping cross-shell setup"
|
|
223
|
+
return 0
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
print_info "Detected bash customizations across ${#bash_files[@]} file(s):"
|
|
227
|
+
echo " Exports: $total_exports, Aliases: $total_aliases, PATH entries: $total_paths"
|
|
228
|
+
echo ""
|
|
229
|
+
print_info "Best practice: create a shared profile (~/.shell_common) sourced by"
|
|
230
|
+
print_info "both bash and zsh, so your customizations work in either shell."
|
|
231
|
+
echo ""
|
|
232
|
+
|
|
233
|
+
local setup_compat="Y"
|
|
234
|
+
if [[ "$NON_INTERACTIVE" != "true" ]]; then
|
|
235
|
+
read -r -p "Create shared shell profile for cross-shell compatibility? [Y/n]: " setup_compat
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
if [[ ! "$setup_compat" =~ ^[Yy]?$ ]]; then
|
|
239
|
+
print_info "Skipped cross-shell compatibility setup"
|
|
240
|
+
print_info "Set up later by creating ~/.shell_common and sourcing it from both shells"
|
|
241
|
+
return 0
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# Extract portable customizations from bash config into shared profile
|
|
245
|
+
# We extract: exports, PATH modifications, aliases, eval statements, source commands
|
|
246
|
+
# We skip: bash-specific syntax (shopt, PROMPT_COMMAND, PS1, completion, bind, etc.)
|
|
247
|
+
# We deduplicate lines that appear in multiple files (e.g. .bash_profile sources .bashrc)
|
|
248
|
+
print_info "Creating shared profile: $shared_profile"
|
|
249
|
+
|
|
250
|
+
{
|
|
251
|
+
echo "# Shared shell profile - sourced by both bash and zsh"
|
|
252
|
+
echo "# Created by aidevops setup to preserve customizations across shell switches"
|
|
253
|
+
echo "# Edit this file for settings you want in BOTH bash and zsh"
|
|
254
|
+
echo "# Shell-specific settings go in ~/.bashrc or ~/.zshrc"
|
|
255
|
+
echo ""
|
|
256
|
+
} >"$shared_profile"
|
|
257
|
+
|
|
258
|
+
# Track lines we've already written to avoid duplicates
|
|
259
|
+
# (common on Linux where .bash_profile sources .bashrc)
|
|
260
|
+
local -a seen_lines=()
|
|
261
|
+
local extracted=0
|
|
262
|
+
|
|
263
|
+
for src_file in "${bash_files[@]}"; do
|
|
264
|
+
local src_basename
|
|
265
|
+
src_basename=$(basename "$src_file")
|
|
266
|
+
local added_header=false
|
|
267
|
+
|
|
268
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
269
|
+
# Skip empty lines
|
|
270
|
+
[[ -z "$line" ]] && continue
|
|
271
|
+
# Skip pure comment lines
|
|
272
|
+
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
273
|
+
|
|
274
|
+
# Skip bash-specific settings that don't work in zsh
|
|
275
|
+
case "$line" in
|
|
276
|
+
*shopt*) continue ;;
|
|
277
|
+
*PROMPT_COMMAND*) continue ;;
|
|
278
|
+
*PS1=*) continue ;;
|
|
279
|
+
*PS2=*) continue ;;
|
|
280
|
+
*bash_completion*) continue ;;
|
|
281
|
+
*"complete "*) continue ;;
|
|
282
|
+
*"bind "*) continue ;;
|
|
283
|
+
*HISTCONTROL*) continue ;;
|
|
284
|
+
*HISTFILESIZE*) continue ;;
|
|
285
|
+
*HISTSIZE*) continue ;;
|
|
286
|
+
*"source /etc/bash"*) continue ;;
|
|
287
|
+
*". /etc/bash"*) continue ;;
|
|
288
|
+
*"source /etc/profile"*) continue ;;
|
|
289
|
+
*". /etc/profile"*) continue ;;
|
|
290
|
+
# Skip lines that source .bashrc from .bash_profile (circular)
|
|
291
|
+
*".bashrc"*) continue ;;
|
|
292
|
+
# Skip lines that source .shell_common (we'll add this ourselves)
|
|
293
|
+
*"shell_common"*) continue ;;
|
|
294
|
+
esac
|
|
295
|
+
|
|
296
|
+
# Match portable lines: exports, aliases, PATH, eval, source/dot-source
|
|
297
|
+
local is_portable=false
|
|
298
|
+
case "$line" in
|
|
299
|
+
export\ [A-Z]* | export\ PATH*) is_portable=true ;;
|
|
300
|
+
alias\ *) is_portable=true ;;
|
|
301
|
+
eval\ *) is_portable=true ;;
|
|
302
|
+
*PATH=*) is_portable=true ;;
|
|
303
|
+
esac
|
|
304
|
+
# Also match 'source' and '. ' commands (tool integrations like nvm, rvm, pyenv)
|
|
305
|
+
if [[ "$is_portable" == "false" ]]; then
|
|
306
|
+
case "$line" in
|
|
307
|
+
source\ * | .\ /* | .\ \$* | .\ \~*) is_portable=true ;;
|
|
308
|
+
esac
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
if [[ "$is_portable" == "true" ]]; then
|
|
312
|
+
# Deduplicate: skip if we've already seen this exact line
|
|
313
|
+
local is_dup=false
|
|
314
|
+
local seen
|
|
315
|
+
for seen in "${seen_lines[@]}"; do
|
|
316
|
+
if [[ "$seen" == "$line" ]]; then
|
|
317
|
+
is_dup=true
|
|
318
|
+
break
|
|
319
|
+
fi
|
|
320
|
+
done
|
|
321
|
+
if [[ "$is_dup" == "true" ]]; then
|
|
322
|
+
continue
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
if [[ "$added_header" == "false" ]]; then
|
|
326
|
+
echo "" >>"$shared_profile"
|
|
327
|
+
echo "# From $src_basename" >>"$shared_profile"
|
|
328
|
+
added_header=true
|
|
329
|
+
fi
|
|
330
|
+
echo "$line" >>"$shared_profile"
|
|
331
|
+
seen_lines+=("$line")
|
|
332
|
+
((++extracted))
|
|
333
|
+
fi
|
|
334
|
+
done <"$src_file"
|
|
335
|
+
done
|
|
336
|
+
|
|
337
|
+
if [[ $extracted -eq 0 ]]; then
|
|
338
|
+
rm -f "$shared_profile"
|
|
339
|
+
print_info "No portable customizations found to extract"
|
|
340
|
+
return 0
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
chmod 644 "$shared_profile"
|
|
344
|
+
print_success "Extracted $extracted unique customization(s) to $shared_profile"
|
|
345
|
+
|
|
346
|
+
# Add sourcing to .zshrc if not already present (existence verified above)
|
|
347
|
+
if ! grep -q 'shell_common' "$zsh_rc"; then
|
|
348
|
+
{
|
|
349
|
+
echo ""
|
|
350
|
+
echo "# Cross-shell compatibility (added by aidevops setup)"
|
|
351
|
+
echo "# Sources shared profile so bash customizations work in zsh too"
|
|
352
|
+
# shellcheck disable=SC2016
|
|
353
|
+
echo '[ -f "$HOME/.shell_common" ] && . "$HOME/.shell_common"'
|
|
354
|
+
} >>"$zsh_rc"
|
|
355
|
+
print_success "Added shared profile sourcing to .zshrc"
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
# Add sourcing to bash config files if not already present
|
|
359
|
+
# File existence already verified when building bash_files array
|
|
360
|
+
for src_file in "${bash_files[@]}"; do
|
|
361
|
+
if ! grep -q 'shell_common' "$src_file"; then
|
|
362
|
+
{
|
|
363
|
+
echo ""
|
|
364
|
+
echo "# Cross-shell compatibility (added by aidevops setup)"
|
|
365
|
+
echo "# Shared profile - edit ~/.shell_common for settings in both shells"
|
|
366
|
+
# shellcheck disable=SC2016
|
|
367
|
+
echo '[ -f "$HOME/.shell_common" ] && . "$HOME/.shell_common"'
|
|
368
|
+
} >>"$src_file"
|
|
369
|
+
print_success "Added shared profile sourcing to $(basename "$src_file")"
|
|
370
|
+
fi
|
|
371
|
+
done
|
|
372
|
+
|
|
373
|
+
echo ""
|
|
374
|
+
print_success "Cross-shell compatibility configured"
|
|
375
|
+
print_info "Your customizations are now in: $shared_profile"
|
|
376
|
+
print_info "Both bash and zsh will source this file automatically."
|
|
377
|
+
print_info "Edit ~/.shell_common for settings you want in both shells."
|
|
378
|
+
print_info "Use ~/.bashrc or ~/.zshrc for shell-specific settings only."
|
|
379
|
+
|
|
380
|
+
return 0
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# Check for optional dependencies (sshpass) and offer to install them
|
|
384
|
+
check_optional_deps() {
|
|
385
|
+
print_info "Checking optional dependencies..."
|
|
386
|
+
|
|
387
|
+
local missing_optional=()
|
|
388
|
+
|
|
389
|
+
if ! command -v sshpass >/dev/null 2>&1; then
|
|
390
|
+
missing_optional+=("sshpass")
|
|
391
|
+
else
|
|
392
|
+
print_success "sshpass found"
|
|
393
|
+
fi
|
|
394
|
+
|
|
395
|
+
if [[ ${#missing_optional[@]} -gt 0 ]]; then
|
|
396
|
+
print_warning "Missing optional dependencies: ${missing_optional[*]}"
|
|
397
|
+
echo " sshpass - needed for password-based SSH (like Hostinger)"
|
|
398
|
+
|
|
399
|
+
local pkg_manager
|
|
400
|
+
pkg_manager=$(detect_package_manager)
|
|
401
|
+
|
|
402
|
+
if [[ "$pkg_manager" != "unknown" ]]; then
|
|
403
|
+
read -r -p "Install optional dependencies using $pkg_manager? [Y/n]: " install_optional
|
|
404
|
+
|
|
405
|
+
if [[ "$install_optional" =~ ^[Yy]?$ ]]; then
|
|
406
|
+
print_info "Installing ${missing_optional[*]}..."
|
|
407
|
+
if install_packages "$pkg_manager" "${missing_optional[@]}"; then
|
|
408
|
+
print_success "Optional dependencies installed"
|
|
409
|
+
else
|
|
410
|
+
print_warning "Failed to install optional dependencies (non-critical)"
|
|
411
|
+
fi
|
|
412
|
+
else
|
|
413
|
+
print_info "Skipped optional dependencies"
|
|
414
|
+
fi
|
|
415
|
+
fi
|
|
416
|
+
fi
|
|
417
|
+
return 0
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
# Add ~/.local/bin to PATH in all shell rc files for the aidevops CLI
|
|
421
|
+
add_local_bin_to_path() {
|
|
422
|
+
# shellcheck disable=SC2016 # path_line is written to rc files; must expand at shell startup, not now
|
|
423
|
+
local path_line='export PATH="$HOME/.local/bin:$PATH"'
|
|
424
|
+
local added_to=""
|
|
425
|
+
local already_in=""
|
|
426
|
+
|
|
427
|
+
local rc_file
|
|
428
|
+
while IFS= read -r rc_file; do
|
|
429
|
+
[[ -z "$rc_file" ]] && continue
|
|
430
|
+
|
|
431
|
+
# Create the rc file if it doesn't exist (ensure parent dir exists for fish etc.)
|
|
432
|
+
if [[ ! -f "$rc_file" ]]; then
|
|
433
|
+
mkdir -p "$(dirname "$rc_file")"
|
|
434
|
+
touch "$rc_file"
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
# Check if already added (file created above if it didn't exist)
|
|
438
|
+
if grep -q '\.local/bin' "$rc_file"; then
|
|
439
|
+
already_in="${already_in:+$already_in, }$rc_file"
|
|
440
|
+
continue
|
|
441
|
+
fi
|
|
442
|
+
|
|
443
|
+
# Add to shell config
|
|
444
|
+
{
|
|
445
|
+
echo ""
|
|
446
|
+
echo "# Added by aidevops setup"
|
|
447
|
+
echo "$path_line"
|
|
448
|
+
} >>"$rc_file"
|
|
449
|
+
added_to="${added_to:+$added_to, }$rc_file"
|
|
450
|
+
done < <(get_all_shell_rcs)
|
|
451
|
+
|
|
452
|
+
if [[ -n "$added_to" ]]; then
|
|
453
|
+
print_success "Added $HOME/.local/bin to PATH in: $added_to"
|
|
454
|
+
print_info "Restart your terminal to use 'aidevops' command"
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
if [[ -n "$already_in" ]]; then
|
|
458
|
+
print_info "$HOME/.local/bin already in PATH in: $already_in"
|
|
459
|
+
fi
|
|
460
|
+
|
|
461
|
+
if [[ -z "$added_to" && -z "$already_in" ]]; then
|
|
462
|
+
print_warning "Could not detect shell config file"
|
|
463
|
+
print_info "Add this to your shell config: $path_line"
|
|
464
|
+
fi
|
|
465
|
+
|
|
466
|
+
# Also export for current session
|
|
467
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
468
|
+
|
|
469
|
+
return 0
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
# GH#2915, GH#2993: Ensure all processes use the safe ShellCheck wrapper.
|
|
473
|
+
#
|
|
474
|
+
# The bash language server hardcodes --external-sources in every ShellCheck
|
|
475
|
+
# invocation, causing exponential memory growth (9+ GB) when source chains
|
|
476
|
+
# span 463+ scripts. The wrapper strips --external-sources and enforces a
|
|
477
|
+
# background RSS watchdog (ulimit -v is broken on macOS ARM — EINVAL).
|
|
478
|
+
#
|
|
479
|
+
# GH#2915 set SHELLCHECK_PATH env var, but bash-language-server ignores it —
|
|
480
|
+
# it resolves `shellcheck` via PATH lookup, finding /opt/homebrew/bin/shellcheck
|
|
481
|
+
# directly. GH#2993 fixes this by placing a shim on PATH ahead of the real binary.
|
|
482
|
+
#
|
|
483
|
+
# Four layers ensure all processes use the wrapper:
|
|
484
|
+
# 0. PATH shim — ~/.aidevops/bin/shellcheck symlink + PATH prepend (GH#2993)
|
|
485
|
+
# 1. launchctl setenv (macOS) — GUI-launched apps (current boot only)
|
|
486
|
+
# 2. .zshenv — ALL zsh processes including non-interactive (persists)
|
|
487
|
+
# 3. Shell rc files (.zshrc, .bash_profile) — interactive terminals
|
|
488
|
+
#
|
|
489
|
+
# Layer 0 is the primary fix: bash-language-server does a PATH lookup for
|
|
490
|
+
# "shellcheck". By placing ~/.aidevops/bin first on PATH with a symlink to
|
|
491
|
+
# the wrapper, the language server finds the wrapper instead of the real binary.
|
|
492
|
+
# Layers 1-3 are retained for tools that honour SHELLCHECK_PATH.
|
|
493
|
+
#
|
|
494
|
+
# CRITICAL: ~/.aidevops/bin MUST be at the START of PATH, not the end.
|
|
495
|
+
# If it appears after /opt/homebrew/bin, the real shellcheck is found first
|
|
496
|
+
# and the wrapper is bypassed entirely. The launchctl setenv always prepends,
|
|
497
|
+
# and the case-guard in shell rc files ensures it stays first.
|
|
498
|
+
setup_shellcheck_wrapper() {
|
|
499
|
+
local wrapper_path="$HOME/.aidevops/agents/scripts/shellcheck-wrapper.sh"
|
|
500
|
+
|
|
501
|
+
# Verify the wrapper exists and is executable
|
|
502
|
+
if [[ ! -x "$wrapper_path" ]]; then
|
|
503
|
+
if [[ -f "$wrapper_path" ]]; then
|
|
504
|
+
chmod +x "$wrapper_path"
|
|
505
|
+
else
|
|
506
|
+
print_warning "ShellCheck wrapper not found at $wrapper_path (will be available after deploy)"
|
|
507
|
+
return 0
|
|
508
|
+
fi
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
# Verify the wrapper actually works (can find real shellcheck)
|
|
512
|
+
if ! "$wrapper_path" --version >/dev/null 2>&1; then
|
|
513
|
+
print_warning "ShellCheck wrapper cannot find real shellcheck binary — skipping"
|
|
514
|
+
return 0
|
|
515
|
+
fi
|
|
516
|
+
|
|
517
|
+
local env_line
|
|
518
|
+
# shellcheck disable=SC2016 # env_line is written to rc files; must expand at shell startup
|
|
519
|
+
env_line='export SHELLCHECK_PATH="$HOME/.aidevops/agents/scripts/shellcheck-wrapper.sh"'
|
|
520
|
+
# shellcheck disable=SC2016 # path_line is written to rc files; must expand at shell startup
|
|
521
|
+
# Sanitize-and-prepend: strip any existing occurrence of the shim dir from PATH
|
|
522
|
+
# (it may be at the END from a previous setup run), then prepend it. This ensures
|
|
523
|
+
# the shim is always first, even on machines upgrading from the old append form.
|
|
524
|
+
# The ${PATH:+:$PATH} guard handles the empty-PATH edge case without a trailing colon.
|
|
525
|
+
local path_line='_aidevops_shim="$HOME/.aidevops/bin"; PATH="$(printf '\''%s'\'' "$PATH" | tr '\'':'\'' '\''\n'\'' | grep -Fxv -- "$_aidevops_shim" | paste -sd: -)"; export PATH="$_aidevops_shim${PATH:+:$PATH}"; unset _aidevops_shim'
|
|
526
|
+
# Fish shell uses different syntax (set -gx instead of export)
|
|
527
|
+
# shellcheck disable=SC2016 # fish lines are written to config.fish; must expand at shell startup
|
|
528
|
+
local env_line_fish='set -gx SHELLCHECK_PATH "$HOME/.aidevops/agents/scripts/shellcheck-wrapper.sh"'
|
|
529
|
+
# shellcheck disable=SC2016 # fish path line: strip existing, then prepend
|
|
530
|
+
local path_line_fish='set -l _aidevops_shim "$HOME/.aidevops/bin"; set -l _aidevops_rest (string match -v -- "$_aidevops_shim" $PATH); set -gx PATH $_aidevops_shim $_aidevops_rest'
|
|
531
|
+
local added_to=""
|
|
532
|
+
local already_in=""
|
|
533
|
+
|
|
534
|
+
# Layer 0: PATH shim (GH#2993)
|
|
535
|
+
# Create ~/.aidevops/bin/shellcheck as a symlink to the wrapper.
|
|
536
|
+
# This is the primary fix: bash-language-server resolves `shellcheck` via
|
|
537
|
+
# PATH, so the symlink must appear on PATH before /opt/homebrew/bin.
|
|
538
|
+
local shim_dir="$HOME/.aidevops/bin"
|
|
539
|
+
local shim_path="$shim_dir/shellcheck"
|
|
540
|
+
mkdir -p "$shim_dir"
|
|
541
|
+
|
|
542
|
+
# Create or update the symlink
|
|
543
|
+
local wrapper_realpath
|
|
544
|
+
wrapper_realpath="$(realpath "$wrapper_path" 2>/dev/null || readlink -f "$wrapper_path" 2>/dev/null || echo "$wrapper_path")"
|
|
545
|
+
if [[ -L "$shim_path" ]]; then
|
|
546
|
+
local current_target
|
|
547
|
+
current_target="$(realpath "$shim_path" 2>/dev/null || readlink -f "$shim_path" 2>/dev/null || echo "")"
|
|
548
|
+
if [[ "$current_target" != "$wrapper_realpath" ]]; then
|
|
549
|
+
ln -sf "$wrapper_path" "$shim_path"
|
|
550
|
+
print_info "Updated shellcheck shim symlink: $shim_path → $wrapper_path"
|
|
551
|
+
fi
|
|
552
|
+
elif [[ -e "$shim_path" ]]; then
|
|
553
|
+
# Regular file exists — back up and replace with symlink
|
|
554
|
+
mv "$shim_path" "${shim_path}.bak.$(date +%Y%m%d_%H%M%S)"
|
|
555
|
+
ln -sf "$wrapper_path" "$shim_path"
|
|
556
|
+
print_info "Replaced shellcheck shim with symlink: $shim_path → $wrapper_path"
|
|
557
|
+
else
|
|
558
|
+
ln -sf "$wrapper_path" "$shim_path"
|
|
559
|
+
print_success "Created shellcheck shim: $shim_path → $wrapper_path"
|
|
560
|
+
fi
|
|
561
|
+
|
|
562
|
+
# Layer 1: launchctl setenv (macOS) — affects all GUI-launched processes
|
|
563
|
+
# Set both SHELLCHECK_PATH (for tools that honour it) and PATH (for tools
|
|
564
|
+
# that resolve shellcheck via PATH lookup, like bash-language-server).
|
|
565
|
+
# Note: 2>/dev/null on launchctl is intentional — launchctl may not be
|
|
566
|
+
# available in non-GUI contexts (SSH, containers). Unlike grep where we
|
|
567
|
+
# want errors visible, launchctl failure is a non-fatal fallback.
|
|
568
|
+
#
|
|
569
|
+
# CRITICAL: Always prepend shim_dir even if it's already in PATH — it may
|
|
570
|
+
# be at the END (e.g., appended by a previous setup run), which means the
|
|
571
|
+
# real shellcheck at /opt/homebrew/bin is found first. We strip any existing
|
|
572
|
+
# occurrence and prepend to guarantee first position.
|
|
573
|
+
if [[ "$PLATFORM_MACOS" == "true" ]]; then
|
|
574
|
+
if launchctl setenv SHELLCHECK_PATH "$wrapper_path" 2>/dev/null; then
|
|
575
|
+
print_info "Set SHELLCHECK_PATH via launchctl (GUI processes)"
|
|
576
|
+
fi
|
|
577
|
+
# Build a clean PATH with shim_dir at the front, removing any existing
|
|
578
|
+
# occurrence to prevent duplicates while ensuring first position.
|
|
579
|
+
# Handle the empty-PATH edge case to avoid a trailing colon (which
|
|
580
|
+
# resolves to "." and is a PATH injection vector).
|
|
581
|
+
local clean_path
|
|
582
|
+
clean_path=$(printf '%s' "$PATH" | tr ':' '\n' | grep -Fxv "$shim_dir" | tr '\n' ':' | sed 's/:$//')
|
|
583
|
+
local new_path
|
|
584
|
+
if [[ -n "$clean_path" ]]; then
|
|
585
|
+
new_path="${shim_dir}:${clean_path}"
|
|
586
|
+
else
|
|
587
|
+
new_path="${shim_dir}"
|
|
588
|
+
fi
|
|
589
|
+
if launchctl setenv PATH "$new_path" 2>/dev/null; then
|
|
590
|
+
print_info "Prepended $shim_dir to PATH via launchctl (GUI processes)"
|
|
591
|
+
fi
|
|
592
|
+
fi
|
|
593
|
+
|
|
594
|
+
# Layer 2: .zshenv — affects ALL zsh processes (interactive AND non-interactive)
|
|
595
|
+
# This is critical because opencode spawns bash-language-server as a
|
|
596
|
+
# non-interactive child process. .zshrc is NOT sourced for non-interactive
|
|
597
|
+
# shells, so SHELLCHECK_PATH set only in .zshrc is invisible to the LSP.
|
|
598
|
+
# GH#2993: Also prepend ~/.aidevops/bin to PATH here so the shim is found.
|
|
599
|
+
local zshenv="$HOME/.zshenv"
|
|
600
|
+
if [[ -f "$zshenv" ]] || command -v zsh >/dev/null 2>&1; then
|
|
601
|
+
touch "$zshenv"
|
|
602
|
+
|
|
603
|
+
# SHELLCHECK_PATH env var (for tools that honour it)
|
|
604
|
+
if grep -q 'SHELLCHECK_PATH' "$zshenv"; then
|
|
605
|
+
already_in="${already_in:+$already_in, }$zshenv"
|
|
606
|
+
else
|
|
607
|
+
{
|
|
608
|
+
echo ""
|
|
609
|
+
echo "# Added by aidevops setup (GH#2915: prevent ShellCheck memory explosion)"
|
|
610
|
+
echo "$env_line"
|
|
611
|
+
} >>"$zshenv"
|
|
612
|
+
added_to="${added_to:+$added_to, }$zshenv"
|
|
613
|
+
fi
|
|
614
|
+
|
|
615
|
+
# PATH prepend for ~/.aidevops/bin (GH#2993: shim must be on PATH)
|
|
616
|
+
# Remove stale old-form entries (case guard that only checked presence,
|
|
617
|
+
# not position — left the shim at the end of PATH on upgrades)
|
|
618
|
+
# shellcheck disable=SC2016 # Matching literal $PATH text in rc files, not expanding
|
|
619
|
+
if grep -q 'case ":$PATH:" in.*\.aidevops/bin' "$zshenv"; then
|
|
620
|
+
# Remove the old case-guard line (sed is appropriate here — targeted single-line removal)
|
|
621
|
+
# shellcheck disable=SC2016
|
|
622
|
+
sed -i.bak '/case ":$PATH:" in.*\.aidevops\/bin/d' "$zshenv"
|
|
623
|
+
rm -f "${zshenv}.bak"
|
|
624
|
+
fi
|
|
625
|
+
# Use exact-line match for the new sanitize-and-prepend form
|
|
626
|
+
if ! grep -Fq '_aidevops_shim' "$zshenv"; then
|
|
627
|
+
{
|
|
628
|
+
echo ""
|
|
629
|
+
echo "# Added by aidevops setup (GH#2993: shellcheck shim on PATH)"
|
|
630
|
+
echo "$path_line"
|
|
631
|
+
} >>"$zshenv"
|
|
632
|
+
print_success "Prepended $shim_dir to PATH in .zshenv"
|
|
633
|
+
else
|
|
634
|
+
print_info "$shim_dir already on PATH in .zshenv"
|
|
635
|
+
fi
|
|
636
|
+
fi
|
|
637
|
+
|
|
638
|
+
# Layer 3: Shell rc files — affects interactive terminal sessions
|
|
639
|
+
local rc_file
|
|
640
|
+
while IFS= read -r rc_file; do
|
|
641
|
+
[[ -z "$rc_file" ]] && continue
|
|
642
|
+
|
|
643
|
+
if [[ ! -f "$rc_file" ]]; then
|
|
644
|
+
mkdir -p "$(dirname "$rc_file")"
|
|
645
|
+
touch "$rc_file"
|
|
646
|
+
fi
|
|
647
|
+
|
|
648
|
+
# Detect fish config — uses set -gx syntax, not export
|
|
649
|
+
local is_fish_rc=false
|
|
650
|
+
if [[ "$rc_file" == *"/fish/config.fish" ]]; then
|
|
651
|
+
is_fish_rc=true
|
|
652
|
+
fi
|
|
653
|
+
|
|
654
|
+
# Select the correct syntax for this shell
|
|
655
|
+
local rc_env_line="$env_line"
|
|
656
|
+
local rc_path_line="$path_line"
|
|
657
|
+
if [[ "$is_fish_rc" == "true" ]]; then
|
|
658
|
+
rc_env_line="$env_line_fish"
|
|
659
|
+
rc_path_line="$path_line_fish"
|
|
660
|
+
fi
|
|
661
|
+
|
|
662
|
+
# SHELLCHECK_PATH env var
|
|
663
|
+
if grep -q 'SHELLCHECK_PATH' "$rc_file"; then
|
|
664
|
+
already_in="${already_in:+$already_in, }$rc_file"
|
|
665
|
+
else
|
|
666
|
+
{
|
|
667
|
+
echo ""
|
|
668
|
+
echo "# Added by aidevops setup (GH#2915: prevent ShellCheck memory explosion)"
|
|
669
|
+
echo "$rc_env_line"
|
|
670
|
+
} >>"$rc_file"
|
|
671
|
+
added_to="${added_to:+$added_to, }$rc_file"
|
|
672
|
+
fi
|
|
673
|
+
|
|
674
|
+
# PATH prepend for ~/.aidevops/bin (GH#2993)
|
|
675
|
+
# Remove stale old-form entries (case guard that only checked presence,
|
|
676
|
+
# not position — left the shim at the end of PATH on upgrades)
|
|
677
|
+
# shellcheck disable=SC2016 # Matching literal $PATH text in rc files, not expanding
|
|
678
|
+
if grep -q 'case ":$PATH:" in.*\.aidevops/bin' "$rc_file"; then
|
|
679
|
+
# shellcheck disable=SC2016
|
|
680
|
+
sed -i.bak '/case ":$PATH:" in.*\.aidevops\/bin/d' "$rc_file"
|
|
681
|
+
rm -f "${rc_file}.bak"
|
|
682
|
+
fi
|
|
683
|
+
# For fish: also remove old 'contains' form that only checked presence
|
|
684
|
+
if [[ "$is_fish_rc" == "true" ]] && grep -q 'contains.*\.aidevops/bin' "$rc_file"; then
|
|
685
|
+
sed -i.bak '/contains.*\.aidevops\/bin/d' "$rc_file"
|
|
686
|
+
rm -f "${rc_file}.bak"
|
|
687
|
+
fi
|
|
688
|
+
# Check for the new sanitize-and-prepend form (uses _aidevops_shim variable)
|
|
689
|
+
if ! grep -Fq '_aidevops_shim' "$rc_file"; then
|
|
690
|
+
{
|
|
691
|
+
echo ""
|
|
692
|
+
echo "# Added by aidevops setup (GH#2993: shellcheck shim on PATH)"
|
|
693
|
+
echo "$rc_path_line"
|
|
694
|
+
} >>"$rc_file"
|
|
695
|
+
fi
|
|
696
|
+
done < <(get_all_shell_rcs)
|
|
697
|
+
|
|
698
|
+
if [[ -n "$added_to" ]]; then
|
|
699
|
+
print_success "Configured SHELLCHECK_PATH wrapper in: $added_to"
|
|
700
|
+
fi
|
|
701
|
+
|
|
702
|
+
if [[ -n "$already_in" ]]; then
|
|
703
|
+
print_info "SHELLCHECK_PATH already configured in: $already_in"
|
|
704
|
+
fi
|
|
705
|
+
|
|
706
|
+
if [[ -z "$added_to" && -z "$already_in" && "$PLATFORM_MACOS" != "true" ]]; then
|
|
707
|
+
print_warning "Could not configure SHELLCHECK_PATH automatically"
|
|
708
|
+
print_info "Add this to your shell config: $env_line"
|
|
709
|
+
fi
|
|
710
|
+
|
|
711
|
+
# Also export for current session
|
|
712
|
+
export SHELLCHECK_PATH="$wrapper_path"
|
|
713
|
+
export PATH="$HOME/.aidevops/bin:$PATH"
|
|
714
|
+
|
|
715
|
+
return 0
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
# Add server access aliases to shell rc files (bash/zsh/fish)
|
|
719
|
+
setup_aliases() {
|
|
720
|
+
print_info "Setting up shell aliases..."
|
|
721
|
+
|
|
722
|
+
local default_shell
|
|
723
|
+
default_shell=$(detect_default_shell)
|
|
724
|
+
|
|
725
|
+
# Fish shell uses different alias syntax
|
|
726
|
+
local is_fish=false
|
|
727
|
+
if [[ "$default_shell" == "fish" ]]; then
|
|
728
|
+
is_fish=true
|
|
729
|
+
fi
|
|
730
|
+
|
|
731
|
+
local alias_block_bash
|
|
732
|
+
alias_block_bash=$(
|
|
733
|
+
cat <<'ALIASES'
|
|
734
|
+
|
|
735
|
+
# AI Assistant Server Access Framework
|
|
736
|
+
alias servers='./.agents/scripts/servers-helper.sh'
|
|
737
|
+
alias servers-list='./.agents/scripts/servers-helper.sh list'
|
|
738
|
+
alias hostinger='./.agents/scripts/hostinger-helper.sh'
|
|
739
|
+
alias hetzner='./.agents/scripts/hetzner-helper.sh'
|
|
740
|
+
alias aws-helper='./.agents/scripts/aws-helper.sh'
|
|
741
|
+
ALIASES
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
local alias_block_fish
|
|
745
|
+
alias_block_fish=$(
|
|
746
|
+
cat <<'ALIASES'
|
|
747
|
+
|
|
748
|
+
# AI Assistant Server Access Framework
|
|
749
|
+
alias servers './.agents/scripts/servers-helper.sh'
|
|
750
|
+
alias servers-list './.agents/scripts/servers-helper.sh list'
|
|
751
|
+
alias hostinger './.agents/scripts/hostinger-helper.sh'
|
|
752
|
+
alias hetzner './.agents/scripts/hetzner-helper.sh'
|
|
753
|
+
alias aws-helper './.agents/scripts/aws-helper.sh'
|
|
754
|
+
ALIASES
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
# Check if aliases already exist in any rc file (including fish config)
|
|
758
|
+
local any_configured=false
|
|
759
|
+
local rc_file
|
|
760
|
+
while IFS= read -r rc_file; do
|
|
761
|
+
[[ -z "$rc_file" ]] && continue
|
|
762
|
+
if [[ -f "$rc_file" ]] && grep -q "# AI Assistant Server Access" "$rc_file"; then
|
|
763
|
+
any_configured=true
|
|
764
|
+
break
|
|
765
|
+
fi
|
|
766
|
+
done < <(get_all_shell_rcs)
|
|
767
|
+
# Also check fish config (not included in get_all_shell_rcs on macOS)
|
|
768
|
+
if [[ "$any_configured" == "false" ]]; then
|
|
769
|
+
local fish_config="$HOME/.config/fish/config.fish"
|
|
770
|
+
if [[ -f "$fish_config" ]] && grep -q "# AI Assistant Server Access" "$fish_config"; then
|
|
771
|
+
any_configured=true
|
|
772
|
+
fi
|
|
773
|
+
fi
|
|
774
|
+
|
|
775
|
+
if [[ "$any_configured" == "true" ]]; then
|
|
776
|
+
print_info "Server Access aliases already configured - Skipping"
|
|
777
|
+
return 0
|
|
778
|
+
fi
|
|
779
|
+
|
|
780
|
+
print_info "Detected default shell: $default_shell"
|
|
781
|
+
read -r -p "Add shell aliases? [Y/n]: " add_aliases
|
|
782
|
+
|
|
783
|
+
if [[ "$add_aliases" =~ ^[Yy]?$ ]]; then
|
|
784
|
+
local added_to=""
|
|
785
|
+
|
|
786
|
+
# Handle fish separately
|
|
787
|
+
if [[ "$is_fish" == "true" ]]; then
|
|
788
|
+
local fish_rc="$HOME/.config/fish/config.fish"
|
|
789
|
+
mkdir -p "$HOME/.config/fish"
|
|
790
|
+
echo "$alias_block_fish" >>"$fish_rc"
|
|
791
|
+
added_to="$fish_rc"
|
|
792
|
+
else
|
|
793
|
+
# Add to all bash/zsh rc files
|
|
794
|
+
while IFS= read -r rc_file; do
|
|
795
|
+
[[ -z "$rc_file" ]] && continue
|
|
796
|
+
|
|
797
|
+
# Create if it doesn't exist
|
|
798
|
+
if [[ ! -f "$rc_file" ]]; then
|
|
799
|
+
touch "$rc_file"
|
|
800
|
+
fi
|
|
801
|
+
|
|
802
|
+
# Skip if already has aliases (file created above if it didn't exist)
|
|
803
|
+
if grep -q "# AI Assistant Server Access" "$rc_file"; then
|
|
804
|
+
continue
|
|
805
|
+
fi
|
|
806
|
+
|
|
807
|
+
echo "$alias_block_bash" >>"$rc_file"
|
|
808
|
+
added_to="${added_to:+$added_to, }$rc_file"
|
|
809
|
+
done < <(get_all_shell_rcs)
|
|
810
|
+
fi
|
|
811
|
+
|
|
812
|
+
if [[ -n "$added_to" ]]; then
|
|
813
|
+
print_success "Aliases added to: $added_to"
|
|
814
|
+
print_info "Restart your terminal to use aliases"
|
|
815
|
+
fi
|
|
816
|
+
else
|
|
817
|
+
print_info "Skipped alias setup by user request"
|
|
818
|
+
fi
|
|
819
|
+
return 0
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
# Install terminal title integration that syncs tab titles with git repo/branch
|
|
823
|
+
setup_terminal_title() {
|
|
824
|
+
print_info "Setting up terminal title integration..."
|
|
825
|
+
|
|
826
|
+
local setup_script=".agents/scripts/terminal-title-setup.sh"
|
|
827
|
+
|
|
828
|
+
if [[ ! -f "$setup_script" ]]; then
|
|
829
|
+
print_warning "Terminal title setup script not found - skipping"
|
|
830
|
+
return 0
|
|
831
|
+
fi
|
|
832
|
+
|
|
833
|
+
# Check if already installed (check all rc files)
|
|
834
|
+
local title_configured=false
|
|
835
|
+
local rc_file
|
|
836
|
+
while IFS= read -r rc_file; do
|
|
837
|
+
[[ -z "$rc_file" ]] && continue
|
|
838
|
+
if [[ -f "$rc_file" ]] && grep -q "aidevops terminal-title" "$rc_file"; then
|
|
839
|
+
title_configured=true
|
|
840
|
+
break
|
|
841
|
+
fi
|
|
842
|
+
done < <(get_all_shell_rcs)
|
|
843
|
+
|
|
844
|
+
if [[ "$title_configured" == "true" ]]; then
|
|
845
|
+
print_info "Terminal title integration already configured - Skipping"
|
|
846
|
+
return 0
|
|
847
|
+
fi
|
|
848
|
+
|
|
849
|
+
# Show current status before asking
|
|
850
|
+
echo ""
|
|
851
|
+
print_info "Terminal title integration syncs your terminal tab with git repo/branch"
|
|
852
|
+
print_info "Example: Tab shows 'aidevops/feature/xyz' when in that branch"
|
|
853
|
+
echo ""
|
|
854
|
+
echo "Current status:"
|
|
855
|
+
|
|
856
|
+
# Shell info
|
|
857
|
+
local shell_name
|
|
858
|
+
shell_name=$(detect_default_shell)
|
|
859
|
+
local shell_info="$shell_name"
|
|
860
|
+
if [[ "$shell_name" == "zsh" ]] && [[ -d "$HOME/.oh-my-zsh" ]]; then
|
|
861
|
+
shell_info="$shell_name (Oh-My-Zsh)"
|
|
862
|
+
fi
|
|
863
|
+
echo " Shell: $shell_info"
|
|
864
|
+
|
|
865
|
+
# Tabby info
|
|
866
|
+
local tabby_config="$HOME/Library/Application Support/tabby/config.yaml"
|
|
867
|
+
if [[ -f "$tabby_config" ]]; then
|
|
868
|
+
local disabled_count
|
|
869
|
+
disabled_count=$(grep -c "disableDynamicTitle: true" "$tabby_config" || echo "0")
|
|
870
|
+
if [[ "$disabled_count" -gt 0 ]]; then
|
|
871
|
+
echo " Tabby: detected, dynamic titles disabled in $disabled_count profile(s) (will fix)"
|
|
872
|
+
else
|
|
873
|
+
echo " Tabby: detected, dynamic titles enabled"
|
|
874
|
+
fi
|
|
875
|
+
fi
|
|
876
|
+
|
|
877
|
+
echo ""
|
|
878
|
+
read -r -p "Install terminal title integration? [Y/n]: " install_title
|
|
879
|
+
|
|
880
|
+
if [[ "$install_title" =~ ^[Yy]?$ ]]; then
|
|
881
|
+
if bash "$setup_script" install; then
|
|
882
|
+
print_success "Terminal title integration installed"
|
|
883
|
+
else
|
|
884
|
+
print_warning "Terminal title setup encountered issues (non-critical)"
|
|
885
|
+
fi
|
|
886
|
+
else
|
|
887
|
+
print_info "Skipped terminal title setup by user request"
|
|
888
|
+
print_info "You can install later with: ~/.aidevops/agents/scripts/terminal-title-setup.sh install"
|
|
889
|
+
fi
|
|
890
|
+
|
|
891
|
+
return 0
|
|
892
|
+
}
|