aidevops 3.13.95 → 3.14.1

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.
@@ -1,478 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: MIT
3
- # SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
4
- # Configuration functions: setup_configs, set_permissions, ssh, aidevops-cli, opencode-config, claude-config, validate, extract-prompts, drift-check
5
- # Part of aidevops setup.sh modularization (t316.3)
6
-
7
- # Shell safety baseline
8
- set -Eeuo pipefail
9
- IFS=$'\n\t'
10
- # shellcheck disable=SC2154 # rc is assigned by $? in the trap string
11
- trap 'rc=$?; echo "[ERROR] ${BASH_SOURCE[0]}:${LINENO} exit $rc" >&2' ERR
12
- shopt -s inherit_errexit 2>/dev/null || true
13
-
14
- setup_configs() {
15
- print_info "Setting up configuration files..."
16
-
17
- # Create configs directory if it doesn't exist
18
- mkdir -p configs
19
-
20
- # Copy template configs if they don't exist
21
- for template in configs/*.txt; do
22
- if [[ -f "$template" ]]; then
23
- config_file="${template%.txt}"
24
- if [[ ! -f "$config_file" ]]; then
25
- cp "$template" "$config_file"
26
- print_success "Created $(basename "$config_file")"
27
- print_warning "Please edit $(basename "$config_file") with your actual credentials"
28
- else
29
- print_info "Found existing config: $(basename "$config_file") - Skipping"
30
- fi
31
- fi
32
- done
33
-
34
- return 0
35
- }
36
-
37
- install_aidevops_cli() {
38
- print_info "Installing aidevops CLI command..."
39
-
40
- # Use INSTALL_DIR (repo root, exported by setup.sh) — not BASH_SOURCE[0]
41
- # which resolves to setup-modules/ when sourced from setup.sh.
42
- local cli_source="${INSTALL_DIR:?INSTALL_DIR not set}/aidevops.sh"
43
- local cli_target="/usr/local/bin/aidevops"
44
-
45
- if [[ ! -f "$cli_source" ]]; then
46
- print_warning "aidevops.sh not found at $cli_source - skipping CLI installation"
47
- return 0
48
- fi
49
-
50
- # Check if we can write to /usr/local/bin
51
- if [[ -w "/usr/local/bin" ]]; then
52
- # Direct symlink
53
- ln -sf "$cli_source" "$cli_target"
54
- print_success "Installed aidevops command to $cli_target"
55
- elif [[ -w "$HOME/.local/bin" ]] || mkdir -p "$HOME/.local/bin" 2>/dev/null; then
56
- # Use ~/.local/bin instead
57
- cli_target="$HOME/.local/bin/aidevops"
58
- ln -sf "$cli_source" "$cli_target"
59
- print_success "Installed aidevops command to $cli_target"
60
-
61
- # Check if ~/.local/bin is in PATH and add it if not
62
- if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
63
- add_local_bin_to_path
64
- fi
65
- else
66
- # Need sudo
67
- print_info "Installing aidevops command requires sudo..."
68
- if sudo ln -sf "$cli_source" "$cli_target"; then
69
- print_success "Installed aidevops command to $cli_target"
70
- else
71
- print_warning "Could not install aidevops command globally"
72
- print_info "You can run it directly: $cli_source"
73
- fi
74
- fi
75
-
76
- return 0
77
- }
78
-
79
- # Helper: check for a generator script, run it, report result consistently
80
- _run_generator() {
81
- local script_path="$1"
82
- local info_msg="$2"
83
- local success_msg="$3"
84
- local failure_msg="$4"
85
- shift 4
86
- local script_args=("$@")
87
-
88
- if [[ ! -f "$script_path" ]]; then
89
- print_warning "Generator script not found: $script_path"
90
- return 0
91
- fi
92
-
93
- print_info "$info_msg"
94
- # Use ${arr[@]+"${arr[@]}"} pattern for safe expansion under set -u when array may be empty
95
- if bash "$script_path" ${script_args[@]+"${script_args[@]}"}; then
96
- print_success "$success_msg"
97
- else
98
- print_warning "$failure_msg"
99
- fi
100
-
101
- return 0
102
- }
103
-
104
- update_opencode_config() {
105
- # Respect config (env var or config file)
106
- if ! is_feature_enabled manage_opencode_config 2>/dev/null; then
107
- print_info "OpenCode config management disabled via config (integrations.manage_opencode_config)"
108
- return 0
109
- fi
110
-
111
- print_info "Updating OpenCode configuration..."
112
-
113
- # Use unified generator (t1665.4) if available, fall back to legacy scripts
114
- if [[ -f ".agents/scripts/generate-runtime-config.sh" ]]; then
115
- _run_generator ".agents/scripts/generate-runtime-config.sh" \
116
- "Generating OpenCode configuration (unified)..." \
117
- "OpenCode configuration complete (agents, commands, MCPs, prompts)" \
118
- "OpenCode configuration encountered issues" \
119
- all --runtime opencode
120
- else
121
- # Legacy fallback — remove after one release cycle
122
- _run_generator ".agents/scripts/generate-opencode-commands.sh" \
123
- "Generating OpenCode commands..." \
124
- "OpenCode commands configured" \
125
- "OpenCode command generation encountered issues"
126
-
127
- _run_generator ".agents/scripts/generate-opencode-agents.sh" \
128
- "Generating OpenCode agent configuration..." \
129
- "OpenCode agents configured (11 primary in JSON, subagents as markdown)" \
130
- "OpenCode agent generation encountered issues"
131
-
132
- _run_generator ".agents/scripts/subagent-index-helper.sh" \
133
- "Regenerating subagent index..." \
134
- "Subagent index regenerated" \
135
- "Subagent index generation encountered issues" \
136
- generate
137
- fi
138
-
139
- return 0
140
- }
141
-
142
- update_claude_config() {
143
- # Respect config (env var or config file)
144
- if ! is_feature_enabled manage_claude_config 2>/dev/null; then
145
- print_info "Claude config management disabled via config (integrations.manage_claude_config)"
146
- return 0
147
- fi
148
-
149
- print_info "Updating Claude Code configuration..."
150
-
151
- # Use unified generator (t1665.4) if available, fall back to legacy scripts
152
- if [[ -f ".agents/scripts/generate-runtime-config.sh" ]]; then
153
- _run_generator ".agents/scripts/generate-runtime-config.sh" \
154
- "Generating Claude Code configuration (unified)..." \
155
- "Claude Code configuration complete (agents, commands, MCPs, prompts)" \
156
- "Claude Code configuration encountered issues" \
157
- all --runtime claude-code
158
- else
159
- # Legacy fallback — remove after one release cycle
160
- _run_generator ".agents/scripts/generate-claude-commands.sh" \
161
- "Generating Claude Code commands..." \
162
- "Claude Code commands configured" \
163
- "Claude Code command generation encountered issues"
164
-
165
- _run_generator ".agents/scripts/generate-claude-agents.sh" \
166
- "Generating Claude Code agent configuration..." \
167
- "Claude Code agents configured (MCPs, settings, commands)" \
168
- "Claude Code agent generation encountered issues"
169
-
170
- _run_generator ".agents/scripts/subagent-index-helper.sh" \
171
- "Regenerating subagent index..." \
172
- "Subagent index regenerated" \
173
- "Subagent index generation encountered issues" \
174
- generate
175
- fi
176
-
177
- return 0
178
- }
179
-
180
- # Unified runtime config update (t1665.4)
181
- # Generates config for all installed runtimes in a single pass.
182
- # Called by setup.sh as an alternative to separate update_opencode_config + update_claude_config.
183
- # Respects per-runtime opt-outs (manage_opencode_config, manage_claude_config).
184
- update_runtime_configs() {
185
- print_info "Updating runtime configurations..."
186
-
187
- if [[ ! -f ".agents/scripts/generate-runtime-config.sh" ]]; then
188
- # Legacy fallback — use per-runtime update functions
189
- print_info "Unified generator not found — falling back to per-runtime updates"
190
- update_opencode_config
191
- update_claude_config
192
- return 0
193
- fi
194
-
195
- # Build list of runtimes to generate, respecting opt-outs
196
- local runtimes_to_generate=()
197
-
198
- if is_feature_enabled manage_opencode_config 2>/dev/null; then
199
- runtimes_to_generate+=("opencode")
200
- else
201
- print_info "OpenCode config management disabled via config"
202
- fi
203
-
204
- if is_feature_enabled manage_claude_config 2>/dev/null; then
205
- runtimes_to_generate+=("claude-code")
206
- else
207
- print_info "Claude Code config management disabled via config"
208
- fi
209
-
210
- # Generate for each enabled runtime
211
- local runtime
212
- for runtime in "${runtimes_to_generate[@]}"; do
213
- _run_generator ".agents/scripts/generate-runtime-config.sh" \
214
- "Generating configuration for $runtime..." \
215
- "$runtime configuration updated" \
216
- "$runtime configuration encountered issues" \
217
- all --runtime "$runtime"
218
- done
219
-
220
- return 0
221
- }
222
-
223
- update_codex_config() {
224
- # Only run if Codex is installed or config dir exists
225
- if [[ ! -d "$HOME/.codex" ]] && ! command -v codex >/dev/null 2>&1; then
226
- return 0
227
- fi
228
-
229
- print_info "Updating Codex configuration..."
230
-
231
- # Fix broken MCP_DOCKER entry (P0 — OrbStack/Colima don't support docker mcp)
232
- if type _fix_codex_docker_mcp &>/dev/null; then
233
- _fix_codex_docker_mcp
234
- fi
235
-
236
- # Deploy aidevops MCP servers to Codex config.toml
237
- _deploy_codex_mcps
238
-
239
- print_success "Codex configuration updated"
240
- return 0
241
- }
242
-
243
- # Deploy standard aidevops MCP servers to ~/.codex/config.toml
244
- # Codex uses TOML format: [mcp_servers.NAME] sections
245
- _deploy_codex_mcps() {
246
- local config="$HOME/.codex/config.toml"
247
- mkdir -p "$HOME/.codex"
248
-
249
- # Touch config if it doesn't exist
250
- [[ -f "$config" ]] || touch "$config"
251
-
252
- local mcp_count=0
253
-
254
- # Helper: add a TOML MCP section if not already present
255
- # Args: $1=name, $2=type (stdio|url), $3=command_or_url, $4=args (optional, comma-separated)
256
- _add_codex_mcp() {
257
- local name="$1"
258
- local mcp_type="$2"
259
- local cmd_or_url="$3"
260
- local args="${4:-}"
261
-
262
- if grep -q "\\[mcp_servers\\.${name}\\]" "$config" 2>/dev/null; then
263
- echo -e " ${BLUE:-}=${NC:-} $name (already configured)"
264
- return 0
265
- fi
266
-
267
- {
268
- echo ""
269
- echo "[mcp_servers.${name}]"
270
- if [[ "$mcp_type" == "stdio" ]]; then
271
- echo "command = '${cmd_or_url}'"
272
- if [[ -n "$args" ]]; then
273
- echo "args = [${args}]"
274
- fi
275
- else
276
- echo "type = 'url'"
277
- echo "url = '${cmd_or_url}'"
278
- fi
279
- } >>"$config"
280
- ((++mcp_count))
281
- echo -e " ${GREEN:-}+${NC:-} $name"
282
- return 0
283
- }
284
-
285
- # --- context7 (library docs) ---
286
- _add_codex_mcp "context7" "stdio" "npx" "'-y', '@upstash/context7-mcp@latest'"
287
-
288
- # --- Playwright MCP ---
289
- _add_codex_mcp "playwright" "stdio" "npx" "'-y', '@anthropic-ai/mcp-server-playwright@latest'"
290
-
291
- # --- shadcn UI ---
292
- _add_codex_mcp "shadcn" "stdio" "npx" "'shadcn@latest', 'mcp'"
293
-
294
- # --- OpenAPI Search (remote, zero install) ---
295
- _add_codex_mcp "openapi-search" "url" "https://openapi-mcp.openapisearch.com/mcp"
296
-
297
- # --- Cloudflare API (remote) ---
298
- _add_codex_mcp "cloudflare-api" "url" "https://mcp.cloudflare.com/mcp"
299
-
300
- echo -e " ${GREEN:-}Done${NC:-} -- $mcp_count new MCP servers added to Codex config"
301
- return 0
302
- }
303
-
304
- update_cursor_config() {
305
- # Only run if Cursor is installed or config dir exists
306
- if [[ ! -d "$HOME/.cursor" ]] && ! command -v cursor >/dev/null 2>&1 && ! command -v agent >/dev/null 2>&1; then
307
- return 0
308
- fi
309
-
310
- print_info "Updating Cursor configuration..."
311
-
312
- # Deploy aidevops MCP servers to Cursor mcp.json
313
- _deploy_cursor_mcps
314
-
315
- print_success "Cursor configuration updated"
316
- return 0
317
- }
318
-
319
- # Deploy standard aidevops MCP servers to ~/.cursor/mcp.json
320
- # Cursor uses JSON format: { "mcpServers": { "name": { ... } } }
321
- _deploy_cursor_mcps() {
322
- local config="$HOME/.cursor/mcp.json"
323
- mkdir -p "$HOME/.cursor"
324
-
325
- # Ensure config file exists with valid JSON
326
- if [[ ! -f "$config" ]]; then
327
- echo '{}' >"$config"
328
- fi
329
-
330
- # Use the json_set_nested helper from ai-cli-config.sh if available,
331
- # otherwise use python3 directly
332
- local mcp_count=0
333
-
334
- # Helper: add a JSON MCP entry if not already present
335
- # Increments mcp_count only when a new entry is actually added.
336
- _add_cursor_mcp() {
337
- local name="$1"
338
- local json_value="$2"
339
-
340
- if ! command -v python3 >/dev/null 2>&1; then
341
- print_warning "python3 not found - cannot update Cursor config"
342
- return 0
343
- fi
344
-
345
- local py_output
346
- py_output=$(
347
- python3 - "$config" "$name" "$json_value" <<'PYEOF'
348
- import json, sys
349
-
350
- file_path = sys.argv[1]
351
- name = sys.argv[2]
352
- value_json = sys.argv[3]
353
-
354
- try:
355
- with open(file_path, 'r') as f:
356
- cfg = json.load(f)
357
- except (FileNotFoundError, json.JSONDecodeError):
358
- cfg = {}
359
-
360
- if "mcpServers" not in cfg or not isinstance(cfg["mcpServers"], dict):
361
- cfg["mcpServers"] = {}
362
-
363
- if name in cfg["mcpServers"]:
364
- print(f"SKIP = {name} (already configured)")
365
- else:
366
- cfg["mcpServers"][name] = json.loads(value_json)
367
- with open(file_path, 'w') as f:
368
- json.dump(cfg, f, indent=2)
369
- f.write('\n')
370
- print(f"ADDED + {name}")
371
- PYEOF
372
- ) || true
373
- echo " ${py_output#* }"
374
- if [[ "$py_output" == ADDED* ]]; then
375
- ((++mcp_count))
376
- fi
377
- return 0
378
- }
379
-
380
- # --- context7 (library docs) ---
381
- _add_cursor_mcp "context7" '{"command":"npx","args":["-y","@upstash/context7-mcp@latest"]}'
382
-
383
- # --- Playwright MCP ---
384
- _add_cursor_mcp "playwright" '{"command":"npx","args":["-y","@anthropic-ai/mcp-server-playwright@latest"]}'
385
-
386
- # --- shadcn UI ---
387
- _add_cursor_mcp "shadcn" '{"command":"npx","args":["shadcn@latest","mcp"]}'
388
-
389
- # --- OpenAPI Search (remote, zero install) ---
390
- _add_cursor_mcp "openapi-search" '{"url":"https://openapi-mcp.openapisearch.com/mcp"}'
391
-
392
- # --- Cloudflare API (remote) ---
393
- _add_cursor_mcp "cloudflare-api" '{"url":"https://mcp.cloudflare.com/mcp"}'
394
-
395
- echo " Done -- $mcp_count new MCP servers added to Cursor config"
396
- return 0
397
- }
398
-
399
- # Deploy slash commands to every installed runtime that supports them.
400
- #
401
- # Background: update_opencode_config and update_claude_config already invoke
402
- # the unified generator (.agents/scripts/generate-runtime-config.sh) for
403
- # their runtimes. The other per-client update_*_config functions (Codex,
404
- # Cursor, Droid, etc.) were written before the unified generator existed
405
- # and only handle MCP registration. This function closes that gap by
406
- # invoking the generator for every other installed client.
407
- #
408
- # Gated on rt_feature_commands so users can disable command installation
409
- # per-runtime via AIDEVOPS_FEATURE_COMMANDS_<SUFFIX>=no. Clients with no
410
- # _RT_COMMAND_DIR (windsurf, amp, kilo, aider) are skipped automatically.
411
- #
412
- # Fixes GH#18106 / t15474.
413
- deploy_commands_to_all_runtimes() {
414
- local registry_script="${INSTALL_DIR:-.}/.agents/scripts/runtime-registry.sh"
415
- local generator_script="${INSTALL_DIR:-.}/.agents/scripts/generate-runtime-config.sh"
416
-
417
- if [[ ! -f "$registry_script" ]]; then
418
- print_info "Runtime registry not found — skipping unified command deployment"
419
- return 0
420
- fi
421
- if [[ ! -x "$generator_script" ]]; then
422
- print_info "Runtime config generator not executable — skipping unified command deployment"
423
- return 0
424
- fi
425
-
426
- # Source registry if not already loaded
427
- if [[ -z "${_RUNTIME_REGISTRY_LOADED:-}" ]]; then
428
- # shellcheck source=/dev/null
429
- source "$registry_script"
430
- fi
431
-
432
- local runtime_id cmd_dir feature_flag display_name
433
- local deployed_count=0 skipped_count=0
434
-
435
- while IFS= read -r runtime_id; do
436
- # OpenCode and Claude Code are already handled by their dedicated
437
- # update_*_config functions above — skip to avoid double-deploy
438
- # and keep the log output clean.
439
- case "$runtime_id" in
440
- opencode | claude-code) continue ;;
441
- esac
442
-
443
- # Skip runtimes with no command directory in the registry (repo-only
444
- # clients like Windsurf/Amp, and clients without native slash command
445
- # support like Kilo/Aider).
446
- cmd_dir=$(rt_command_dir "$runtime_id" 2>/dev/null || echo "")
447
- [[ -z "$cmd_dir" ]] && continue
448
-
449
- # Honour the rt_feature_commands flag.
450
- feature_flag=$(rt_feature_commands "$runtime_id" 2>/dev/null || echo "yes")
451
- if [[ "$feature_flag" != "yes" ]]; then
452
- display_name=$(rt_display_name "$runtime_id" 2>/dev/null || echo "$runtime_id")
453
- print_info "Commands installation disabled for $display_name (feature flag)"
454
- skipped_count=$((skipped_count + 1))
455
- continue
456
- fi
457
-
458
- # Invoke the unified generator — it prints its own success/failure.
459
- # Redirect stdin from /dev/null so the generator cannot accidentally
460
- # read from the `while read` loop's process-substitution pipe and
461
- # steal the remaining runtime IDs — a classic bash pitfall.
462
- if "$generator_script" commands --runtime "$runtime_id" </dev/null; then
463
- deployed_count=$((deployed_count + 1))
464
- else
465
- display_name=$(rt_display_name "$runtime_id" 2>/dev/null || echo "$runtime_id")
466
- print_warning "Failed to deploy commands for $display_name"
467
- fi
468
- done < <(rt_detect_installed 2>/dev/null)
469
-
470
- if [[ $deployed_count -gt 0 ]]; then
471
- print_success "Deployed slash commands to $deployed_count additional runtime(s)"
472
- elif [[ $skipped_count -gt 0 ]]; then
473
- print_info "All remaining runtimes had commands installation disabled via feature flags"
474
- else
475
- print_info "No additional runtimes needed command deployment"
476
- fi
477
- return 0
478
- }