aidevops 3.13.76 → 3.13.78

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,665 @@
1
+ #!/usr/bin/env bash
2
+ # SPDX-License-Identifier: MIT
3
+ # SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
4
+ # =============================================================================
5
+ # aidevops Skills and Plugin Library
6
+ # =============================================================================
7
+ # Skill and plugin management functions extracted from aidevops.sh to keep
8
+ # the orchestrator under the 2000-line file-size threshold.
9
+ #
10
+ # Covers:
11
+ # 1. Skills: _skill_help, _skill_add_usage, cmd_skill, cmd_skills
12
+ # 2. Plugins: _plugin_validate_ns, _plugin_field, _plugin_add, _plugin_list,
13
+ # _plugin_update, _plugin_toggle, _plugin_remove, _plugin_scaffold,
14
+ # _plugin_help, cmd_plugin
15
+ #
16
+ # Usage: source "${SCRIPT_DIR}/aidevops-skills-plugin-lib.sh"
17
+ #
18
+ # Dependencies:
19
+ # - INSTALL_DIR, AGENTS_DIR, CONFIG_DIR (set by aidevops.sh)
20
+ # - print_* helpers and utility functions (defined in aidevops.sh before sourcing)
21
+ #
22
+ # Part of aidevops framework: https://aidevops.sh
23
+
24
+ # Apply strict mode only when executed directly (not when sourced)
25
+ [[ "${BASH_SOURCE[0]}" == "${0}" ]] && set -euo pipefail
26
+
27
+ # Include guard
28
+ [[ -n "${_AIDEVOPS_SKILLS_PLUGIN_LIB_LOADED:-}" ]] && return 0
29
+ _AIDEVOPS_SKILLS_PLUGIN_LIB_LOADED=1
30
+
31
+ # Skill help text (extracted for complexity reduction)
32
+ _skill_help() {
33
+ print_header "Agent Skills Management"
34
+ echo ""
35
+ echo "Import and manage reusable AI agent skills from the community."
36
+ echo "Skills are converted to aidevops format with upstream tracking."
37
+ echo "Telemetry is disabled - no data sent to third parties."
38
+ echo ""
39
+ echo "Usage: aidevops skill <command> [options]"
40
+ echo ""
41
+ echo "Commands:"
42
+ echo " add <source> Import a skill from GitHub (saved as *-skill.md)"
43
+ echo " list List all imported skills"
44
+ echo " check Check for upstream updates"
45
+ echo " update [name] Update specific or all skills"
46
+ echo " remove <name> Remove an imported skill"
47
+ echo " scan [name] Security scan imported skills (Cisco Skill Scanner)"
48
+ echo " status Show detailed skill status"
49
+ echo " generate Generate SKILL.md stubs for cross-tool discovery"
50
+ echo " clean Remove generated SKILL.md stubs"
51
+ echo ""
52
+ echo "Source formats:"
53
+ echo " owner/repo GitHub shorthand"
54
+ echo " owner/repo/path/to/skill Specific skill in multi-skill repo"
55
+ echo " https://github.com/owner/repo Full URL"
56
+ echo ""
57
+ echo "Examples:"
58
+ echo " aidevops skill add vercel-labs/agent-skills"
59
+ echo " aidevops skill add anthropics/skills/pdf"
60
+ echo " aidevops skill add expo/skills --name expo-dev"
61
+ echo " aidevops skill check"
62
+ echo " aidevops skill update"
63
+ echo " aidevops skill scan"
64
+ echo " aidevops skill scan cloudflare-platform"
65
+ echo " aidevops skill generate --dry-run"
66
+ echo ""
67
+ echo "Imported skills are saved with a -skill suffix to distinguish"
68
+ echo "from native aidevops subagents (e.g., playwright-skill.md vs playwright.md)."
69
+ echo ""
70
+ echo "Browse community skills: https://skills.sh"
71
+ echo "Agent Skills specification: https://agentskills.io"
72
+ return 0
73
+ }
74
+
75
+ _skill_add_usage() {
76
+ print_error "Source required (owner/repo or URL)"
77
+ echo ""
78
+ echo "Usage: aidevops skill add <source> [options]"
79
+ echo ""
80
+ echo "Examples:"
81
+ echo " aidevops skill add vercel-labs/agent-skills"
82
+ echo " aidevops skill add anthropics/skills/pdf"
83
+ echo " aidevops skill add https://github.com/owner/repo"
84
+ echo ""
85
+ echo "Options:"
86
+ echo " --name <name> Override the skill name"
87
+ echo " --force Overwrite existing skill"
88
+ echo " --dry-run Preview without making changes"
89
+ echo ""
90
+ echo "Browse skills: https://skills.sh"
91
+ return 0
92
+ }
93
+
94
+ # Skill management command
95
+ cmd_skill() {
96
+ local action="${1:-help}"
97
+ shift || true
98
+ export DISABLE_TELEMETRY=1 DO_NOT_TRACK=1 SKILLS_NO_TELEMETRY=1
99
+ local add_skill_script="$AGENTS_DIR/scripts/add-skill-helper.sh"
100
+ local update_skill_script="$AGENTS_DIR/scripts/skill-update-helper.sh"
101
+ case "$action" in
102
+ add | a)
103
+ if [[ $# -lt 1 ]]; then
104
+ _skill_add_usage
105
+ return 1
106
+ fi
107
+ [[ ! -f "$add_skill_script" ]] && {
108
+ print_error "add-skill-helper.sh not found"
109
+ print_info "Run 'aidevops update' to get the latest scripts"
110
+ return 1
111
+ }
112
+ bash "$add_skill_script" add "$@"
113
+ ;;
114
+ list | ls | l)
115
+ [[ ! -f "$add_skill_script" ]] && {
116
+ print_error "add-skill-helper.sh not found"
117
+ return 1
118
+ }
119
+ bash "$add_skill_script" list
120
+ ;;
121
+ check | c)
122
+ [[ ! -f "$update_skill_script" ]] && {
123
+ print_error "skill-update-helper.sh not found"
124
+ return 1
125
+ }
126
+ bash "$update_skill_script" check "$@"
127
+ ;;
128
+ update | u)
129
+ [[ ! -f "$update_skill_script" ]] && {
130
+ print_error "skill-update-helper.sh not found"
131
+ return 1
132
+ }
133
+ bash "$update_skill_script" update "$@"
134
+ ;;
135
+ remove | rm)
136
+ [[ $# -lt 1 ]] && {
137
+ print_error "Skill name required"
138
+ echo "Usage: aidevops skill remove <name>"
139
+ return 1
140
+ }
141
+ [[ ! -f "$add_skill_script" ]] && {
142
+ print_error "add-skill-helper.sh not found"
143
+ return 1
144
+ }
145
+ bash "$add_skill_script" remove "$@"
146
+ ;;
147
+ status | s)
148
+ [[ ! -f "$update_skill_script" ]] && {
149
+ print_error "skill-update-helper.sh not found"
150
+ return 1
151
+ }
152
+ bash "$update_skill_script" status "$@"
153
+ ;;
154
+ generate | gen | g)
155
+ local gs="$AGENTS_DIR/scripts/generate-skills.sh"
156
+ [[ ! -f "$gs" ]] && {
157
+ print_error "generate-skills.sh not found"
158
+ print_info "Run 'aidevops update' to get the latest scripts"
159
+ return 1
160
+ }
161
+ print_info "Generating SKILL.md stubs for cross-tool discovery..."
162
+ bash "$gs" "$@"
163
+ ;;
164
+ scan)
165
+ local ss="$AGENTS_DIR/scripts/security-helper.sh"
166
+ [[ ! -f "$ss" ]] && {
167
+ print_error "security-helper.sh not found"
168
+ print_info "Run 'aidevops update' to get the latest scripts"
169
+ return 1
170
+ }
171
+ bash "$ss" skill-scan "$@"
172
+ ;;
173
+ clean)
174
+ local gs="$AGENTS_DIR/scripts/generate-skills.sh"
175
+ [[ ! -f "$gs" ]] && {
176
+ print_error "generate-skills.sh not found"
177
+ return 1
178
+ }
179
+ bash "$gs" --clean "$@"
180
+ ;;
181
+ help | --help | -h) _skill_help ;;
182
+ *)
183
+ print_error "Unknown skill command: $action"
184
+ echo "Run 'aidevops skill help' for usage information."
185
+ return 1
186
+ ;;
187
+ esac
188
+ }
189
+
190
+ # Plugin management helpers (extracted for complexity reduction)
191
+ _PLUGIN_RESERVED="custom draft scripts tools services workflows templates memory plugins seo wordpress aidevops"
192
+
193
+ _plugin_validate_ns() {
194
+ local ns="$1"
195
+ if [[ ! "$ns" =~ ^[a-z][a-z0-9-]*$ ]]; then
196
+ print_error "Invalid namespace '$ns': must be lowercase alphanumeric with hyphens, starting with a letter"
197
+ return 1
198
+ fi
199
+ local r
200
+ for r in $_PLUGIN_RESERVED; do [[ "$ns" == "$r" ]] && {
201
+ print_error "Namespace '$ns' is reserved."
202
+ return 1
203
+ }; done
204
+ return 0
205
+ }
206
+
207
+ _plugin_field() {
208
+ local pf="$1" n="$2" f="$3"
209
+ jq -r --arg n "$n" --arg f "$f" '.plugins[] | select(.name == $n) | .[$f] // empty' "$pf" 2>/dev/null || echo ""
210
+ }
211
+
212
+ _plugin_add() {
213
+ local pf="$1" ad="$2"
214
+ shift 2
215
+ if [[ $# -lt 1 ]]; then
216
+ print_error "Repository URL required"
217
+ echo ""
218
+ echo "Usage: aidevops plugin add <repo-url> [options]"
219
+ echo ""
220
+ echo "Options:"
221
+ echo " --namespace <name> Namespace directory (default: derived from repo name)"
222
+ echo " --branch <branch> Branch to track (default: main)"
223
+ echo " --name <name> Human-readable name (default: derived from repo)"
224
+ echo ""
225
+ echo "Examples:"
226
+ echo " aidevops plugin add https://github.com/marcusquinn/aidevops-pro.git --namespace pro"
227
+ echo " aidevops plugin add https://github.com/marcusquinn/aidevops-anon.git --namespace anon"
228
+ return 1
229
+ fi
230
+ local repo_url="$1"
231
+ shift
232
+ local namespace="" branch="main" plugin_name=""
233
+ while [[ $# -gt 0 ]]; do
234
+ local _opt="$1" _val="${2:-}"
235
+ case "$_opt" in
236
+ --namespace | --ns)
237
+ namespace="$_val"
238
+ shift 2
239
+ ;;
240
+ --branch | -b)
241
+ branch="$_val"
242
+ shift 2
243
+ ;;
244
+ --name | -n)
245
+ plugin_name="$_val"
246
+ shift 2
247
+ ;;
248
+ *)
249
+ print_error "Unknown option: $_opt"
250
+ return 1
251
+ ;;
252
+ esac
253
+ done
254
+ [[ -z "$namespace" ]] && {
255
+ namespace=$(basename "$repo_url" .git | sed 's/^aidevops-//')
256
+ namespace=$(echo "$namespace" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
257
+ }
258
+ [[ -z "$plugin_name" ]] && plugin_name="$namespace"
259
+ _plugin_validate_ns "$namespace" || return 1
260
+ local existing
261
+ existing=$(jq -r --arg n "$plugin_name" '.plugins[] | select(.name == $n) | .name' "$pf" 2>/dev/null || echo "")
262
+ [[ -n "$existing" ]] && {
263
+ print_error "Plugin '$plugin_name' already exists. Use 'aidevops plugin update $plugin_name' to update."
264
+ return 1
265
+ }
266
+ if [[ -d "$ad/$namespace" ]]; then
267
+ local ns_owner
268
+ ns_owner=$(jq -r --arg ns "$namespace" '.plugins[] | select(.namespace == $ns) | .name' "$pf" 2>/dev/null || echo "")
269
+ [[ -n "$ns_owner" ]] && print_error "Namespace '$namespace' is already used by plugin '$ns_owner'" || {
270
+ print_error "Directory '$ad/$namespace/' already exists"
271
+ echo " Choose a different namespace with --namespace <name>"
272
+ }
273
+ return 1
274
+ fi
275
+ print_info "Adding plugin '$plugin_name' from $repo_url..."
276
+ print_info " Namespace: $namespace"
277
+ print_info " Branch: $branch"
278
+ local clone_dir="$ad/$namespace"
279
+ if ! git clone --branch "$branch" --depth 1 "$repo_url" "$clone_dir" 2>&1; then
280
+ print_error "Failed to clone repository"
281
+ rm -rf "$clone_dir" 2>/dev/null || true
282
+ return 1
283
+ fi
284
+ rm -rf "$clone_dir/.git"
285
+ local tmp="${pf}.tmp"
286
+ jq --arg name "$plugin_name" --arg repo "$repo_url" --arg branch "$branch" --arg ns "$namespace" \
287
+ '.plugins += [{"name": $name, "repo": $repo, "branch": $branch, "namespace": $ns, "enabled": true}]' "$pf" >"$tmp" && mv "$tmp" "$pf"
288
+ local loader="$ad/scripts/plugin-loader-helper.sh"
289
+ [[ -f "$loader" ]] && bash "$loader" hooks "$namespace" init 2>/dev/null || true
290
+ print_success "Plugin '$plugin_name' installed to $clone_dir"
291
+ echo ""
292
+ echo " Agents available at: ~/.aidevops/agents/$namespace/"
293
+ echo " Update: aidevops plugin update $plugin_name"
294
+ echo " Remove: aidevops plugin remove $plugin_name"
295
+ return 0
296
+ }
297
+
298
+ _plugin_list() {
299
+ local pf="$1"
300
+ local count
301
+ count=$(jq '.plugins | length' "$pf" 2>/dev/null || echo "0")
302
+ if [[ "$count" == "0" ]]; then
303
+ echo "No plugins installed."
304
+ echo ""
305
+ echo "Add a plugin: aidevops plugin add <repo-url> --namespace <name>"
306
+ return 0
307
+ fi
308
+ echo "Installed plugins ($count):"
309
+ echo ""
310
+ printf " %-15s %-10s %-8s %s\n" "NAME" "NAMESPACE" "ENABLED" "REPO"
311
+ printf " %-15s %-10s %-8s %s\n" "----" "---------" "-------" "----"
312
+ jq -r '.plugins[] | " \(.name)\t\(.namespace)\t\(.enabled // true)\t\(.repo)"' "$pf" 2>/dev/null |
313
+ while IFS=$'\t' read -r name ns enabled repo; do
314
+ local si="yes"
315
+ [[ "$enabled" == "false" ]] && si="no"
316
+ printf " %-15s %-10s %-8s %s\n" "$name" "$ns" "$si" "$repo"
317
+ done
318
+ return 0
319
+ }
320
+
321
+ _plugin_update() {
322
+ local pf="$1" ad="$2" target="${3:-}"
323
+ if [[ -n "$target" ]]; then
324
+ local repo ns bn
325
+ repo=$(_plugin_field "$pf" "$target" "repo")
326
+ ns=$(_plugin_field "$pf" "$target" "namespace")
327
+ bn=$(_plugin_field "$pf" "$target" "branch")
328
+ bn="${bn:-main}"
329
+ [[ -z "$repo" ]] && {
330
+ print_error "Plugin '$target' not found"
331
+ return 1
332
+ }
333
+ print_info "Updating plugin '$target'..."
334
+ local cd2="$ad/$ns"
335
+ rm -rf "$cd2"
336
+ if git clone --branch "$bn" --depth 1 "$repo" "$cd2" 2>&1; then
337
+ rm -rf "$cd2/.git"
338
+ print_success "Plugin '$target' updated"
339
+ else
340
+ print_error "Failed to update plugin '$target'"
341
+ return 1
342
+ fi
343
+ else
344
+ local names
345
+ names=$(jq -r '.plugins[] | select(.enabled != false) | .name' "$pf" 2>/dev/null || echo "")
346
+ [[ -z "$names" ]] && {
347
+ echo "No enabled plugins to update."
348
+ return 0
349
+ }
350
+ local failed=0
351
+ while IFS= read -r pn; do
352
+ [[ -z "$pn" ]] && continue
353
+ local pr pns pb
354
+ pr=$(_plugin_field "$pf" "$pn" "repo")
355
+ pns=$(_plugin_field "$pf" "$pn" "namespace")
356
+ pb=$(_plugin_field "$pf" "$pn" "branch")
357
+ pb="${pb:-main}"
358
+ print_info "Updating '$pn'..."
359
+ local pd="$ad/$pns"
360
+ rm -rf "$pd"
361
+ if git clone --branch "$pb" --depth 1 "$pr" "$pd" 2>/dev/null; then
362
+ rm -rf "$pd/.git"
363
+ print_success " '$pn' updated"
364
+ else
365
+ print_error " '$pn' failed to update"
366
+ failed=$((failed + 1))
367
+ fi
368
+ done <<<"$names"
369
+ [[ "$failed" -gt 0 ]] && {
370
+ print_warning "$failed plugin(s) failed to update"
371
+ return 1
372
+ }
373
+ print_success "All plugins updated"
374
+ fi
375
+ return 0
376
+ }
377
+
378
+ _plugin_toggle() {
379
+ local pf="$1" ad="$2" tn="$3" action="$4"
380
+ if [[ "$action" == "enable" ]]; then
381
+ local tr
382
+ tr=$(_plugin_field "$pf" "$tn" "repo")
383
+ [[ -z "$tr" ]] && {
384
+ print_error "Plugin '$tn' not found"
385
+ return 1
386
+ }
387
+ local tns
388
+ tns=$(_plugin_field "$pf" "$tn" "namespace")
389
+ local tb
390
+ tb=$(_plugin_field "$pf" "$tn" "branch")
391
+ tb="${tb:-main}"
392
+ local tmp="${pf}.tmp"
393
+ jq --arg n "$tn" '(.plugins[] | select(.name == $n)).enabled = true' "$pf" >"$tmp" && mv "$tmp" "$pf"
394
+ [[ ! -d "$ad/$tns" ]] && {
395
+ print_info "Deploying plugin '$tn'..."
396
+ git clone --branch "$tb" --depth 1 "$tr" "$ad/$tns" 2>/dev/null && rm -rf "$ad/$tns/.git"
397
+ }
398
+ local loader="$ad/scripts/plugin-loader-helper.sh"
399
+ [[ -f "$loader" ]] && bash "$loader" hooks "$tns" init 2>/dev/null || true
400
+ print_success "Plugin '$tn' enabled"
401
+ else
402
+ local tns
403
+ tns=$(_plugin_field "$pf" "$tn" "namespace")
404
+ [[ -z "$tns" ]] && {
405
+ print_error "Plugin '$tn' not found"
406
+ return 1
407
+ }
408
+ local loader="$ad/scripts/plugin-loader-helper.sh"
409
+ [[ -f "$loader" && -d "$ad/$tns" ]] && bash "$loader" hooks "$tns" unload 2>/dev/null || true
410
+ local tmp="${pf}.tmp"
411
+ jq --arg n "$tn" '(.plugins[] | select(.name == $n)).enabled = false' "$pf" >"$tmp" && mv "$tmp" "$pf"
412
+ [[ -d "$ad/${tns:?}" ]] && rm -rf "$ad/${tns:?}"
413
+ print_success "Plugin '$tn' disabled (config preserved)"
414
+ fi
415
+ return 0
416
+ }
417
+
418
+ _plugin_remove() {
419
+ local pf="$1" ad="$2" tn="$3"
420
+ local tns
421
+ tns=$(_plugin_field "$pf" "$tn" "namespace")
422
+ [[ -z "$tns" ]] && {
423
+ print_error "Plugin '$tn' not found"
424
+ return 1
425
+ }
426
+ local loader="$ad/scripts/plugin-loader-helper.sh"
427
+ [[ -f "$loader" && -d "$ad/$tns" ]] && bash "$loader" hooks "$tns" unload 2>/dev/null || true
428
+ [[ -d "$ad/${tns:?}" ]] && {
429
+ rm -rf "$ad/${tns:?}"
430
+ print_info "Removed $ad/$tns/"
431
+ }
432
+ local tmp="${pf}.tmp"
433
+ jq --arg n "$tn" '.plugins = [.plugins[] | select(.name != $n)]' "$pf" >"$tmp" && mv "$tmp" "$pf"
434
+ print_success "Plugin '$tn' removed"
435
+ return 0
436
+ }
437
+
438
+ _plugin_scaffold() {
439
+ local ad="$1" td="${2:-.}" pn="${3:-my-plugin}"
440
+ local ns="${4:-$pn}"
441
+ if [[ "$td" != "." && -d "$td" ]]; then
442
+ local ec
443
+ ec=$(find "$td" -maxdepth 1 -type f | wc -l | tr -d ' ')
444
+ [[ "$ec" -gt 0 ]] && {
445
+ print_error "Directory '$td' already has files. Use an empty directory."
446
+ return 1
447
+ }
448
+ fi
449
+ mkdir -p "$td"
450
+ local tpl="$ad/templates/plugin-template"
451
+ [[ ! -d "$tpl" ]] && {
452
+ print_error "Plugin template not found at $tpl"
453
+ print_info "Run 'aidevops update' to get the latest templates."
454
+ return 1
455
+ }
456
+ local pnu
457
+ pnu=$(echo "$pn" | tr '[:lower:]' '[:upper:]' | tr '-' '_')
458
+ sed -e "s|{{PLUGIN_NAME}}|$pn|g" -e "s|{{PLUGIN_NAME_UPPER}}|$pnu|g" -e "s|{{NAMESPACE}}|$ns|g" -e "s|{{REPO_URL}}|https://github.com/user/aidevops-$ns.git|g" "$tpl/AGENTS.md" >"$td/AGENTS.md"
459
+ sed -e "s|{{PLUGIN_NAME}}|$pn|g" -e "s|{{PLUGIN_DESCRIPTION}}|$pn plugin for aidevops|g" -e "s|{{NAMESPACE}}|$ns|g" "$tpl/main-agent.md" >"$td/$ns.md"
460
+ mkdir -p "$td/$ns"
461
+ sed -e "s|{{PLUGIN_NAME}}|$pn|g" -e "s|{{NAMESPACE}}|$ns|g" "$tpl/example-subagent.md" >"$td/$ns/example.md"
462
+ mkdir -p "$td/scripts"
463
+ if [[ -d "$tpl/scripts" ]]; then
464
+ for hf in "$tpl/scripts"/on-*.sh; do
465
+ [[ -f "$hf" ]] || continue
466
+ local hb
467
+ hb=$(basename "$hf")
468
+ sed -e "s|{{PLUGIN_NAME}}|$pn|g" -e "s|{{NAMESPACE}}|$ns|g" "$hf" >"$td/scripts/$hb"
469
+ chmod +x "$td/scripts/$hb"
470
+ done
471
+ fi
472
+ [[ -f "$tpl/plugin.json" ]] && sed -e "s|{{PLUGIN_NAME}}|$pn|g" -e "s|{{PLUGIN_DESCRIPTION}}|$pn plugin for aidevops|g" -e "s|{{NAMESPACE}}|$ns|g" "$tpl/plugin.json" >"$td/plugin.json"
473
+ print_success "Plugin scaffolded in $td/"
474
+ echo ""
475
+ echo "Structure:"
476
+ echo " $td/"
477
+ echo " ├── AGENTS.md # Plugin documentation"
478
+ echo " ├── plugin.json # Plugin manifest"
479
+ echo " ├── $ns.md # Main agent"
480
+ echo " ├── $ns/"
481
+ echo " │ └── example.md # Example subagent"
482
+ echo " └── scripts/"
483
+ echo " ├── on-init.sh # Init lifecycle hook"
484
+ echo " ├── on-load.sh # Load lifecycle hook"
485
+ echo " └── on-unload.sh # Unload lifecycle hook"
486
+ echo ""
487
+ echo "Next steps:"
488
+ echo " 1. Edit plugin.json with your plugin metadata"
489
+ echo " 2. Edit $ns.md with your agent instructions"
490
+ echo " 3. Add subagents to $ns/"
491
+ echo " 4. Push to a git repo"
492
+ echo " 5. Install: aidevops plugin add <repo-url> --namespace $ns"
493
+ return 0
494
+ }
495
+
496
+ _plugin_help() {
497
+ print_header "Plugin Management"
498
+ echo ""
499
+ echo "Manage third-party agent plugins that extend aidevops."
500
+ echo "Plugins deploy to ~/.aidevops/agents/<namespace>/ (isolated from core)."
501
+ echo ""
502
+ echo "Usage: aidevops plugin <command> [options]"
503
+ echo ""
504
+ echo "Commands:"
505
+ echo " add <repo-url> Install a plugin from a git repository"
506
+ echo " list List installed plugins"
507
+ echo " update [name] Update specific or all plugins"
508
+ echo " enable <name> Enable a disabled plugin (redeploys files)"
509
+ echo " disable <name> Disable a plugin (removes files, keeps config)"
510
+ echo " remove <name> Remove a plugin entirely"
511
+ echo " init [dir] [name] [namespace] Scaffold a new plugin from template"
512
+ echo ""
513
+ echo "Options for 'add':"
514
+ echo " --namespace <name> Directory name under ~/.aidevops/agents/"
515
+ echo " --branch <branch> Branch to track (default: main)"
516
+ echo " --name <name> Human-readable plugin name"
517
+ echo ""
518
+ echo "Examples:"
519
+ echo " aidevops plugin add https://github.com/marcusquinn/aidevops-pro.git --namespace pro"
520
+ echo " aidevops plugin add https://github.com/marcusquinn/aidevops-anon.git --namespace anon"
521
+ echo " aidevops plugin list"
522
+ echo " aidevops plugin update"
523
+ echo " aidevops plugin update pro"
524
+ echo " aidevops plugin disable pro"
525
+ echo " aidevops plugin enable pro"
526
+ echo " aidevops plugin remove pro"
527
+ echo " aidevops plugin init ./my-plugin my-plugin my-plugin"
528
+ echo ""
529
+ echo "Plugin docs: ~/.aidevops/agents/aidevops/plugins.md"
530
+ return 0
531
+ }
532
+
533
+ # Plugin management command
534
+ cmd_plugin() {
535
+ local action="${1:-help}"
536
+ shift || true
537
+ local pf="$CONFIG_DIR/plugins.json" ad="$AGENTS_DIR"
538
+ mkdir -p "$CONFIG_DIR"
539
+ [[ ! -f "$pf" ]] && echo '{"plugins":[]}' >"$pf"
540
+ case "$action" in
541
+ add | a) _plugin_add "$pf" "$ad" "$@" ;;
542
+ list | ls | l) _plugin_list "$pf" ;;
543
+ update | u) _plugin_update "$pf" "$ad" "$@" ;;
544
+ enable)
545
+ [[ $# -lt 1 ]] && {
546
+ print_error "Plugin name required"
547
+ echo "Usage: aidevops plugin enable <name>"
548
+ return 1
549
+ }
550
+ local _enable_name="$1"
551
+ _plugin_toggle "$pf" "$ad" "$_enable_name" enable
552
+ ;;
553
+ disable)
554
+ [[ $# -lt 1 ]] && {
555
+ print_error "Plugin name required"
556
+ echo "Usage: aidevops plugin disable <name>"
557
+ return 1
558
+ }
559
+ local _disable_name="$1"
560
+ _plugin_toggle "$pf" "$ad" "$_disable_name" disable
561
+ ;;
562
+ remove | rm)
563
+ [[ $# -lt 1 ]] && {
564
+ print_error "Plugin name required"
565
+ echo "Usage: aidevops plugin remove <name>"
566
+ return 1
567
+ }
568
+ local _remove_name="$1"
569
+ _plugin_remove "$pf" "$ad" "$_remove_name"
570
+ ;;
571
+ init) _plugin_scaffold "$ad" "$@" ;;
572
+ help | --help | -h) _plugin_help ;;
573
+ *)
574
+ print_error "Unknown plugin command: $action"
575
+ echo "Run 'aidevops plugin help' for usage information."
576
+ return 1
577
+ ;;
578
+ esac
579
+ return 0
580
+ }
581
+
582
+ # Skills discovery command - search, browse, describe installed skills
583
+ cmd_skills() {
584
+ local action="${1:-help}"
585
+ shift || true
586
+
587
+ local skills_helper="$AGENTS_DIR/scripts/skills-helper.sh"
588
+
589
+ if [[ ! -f "$skills_helper" ]]; then
590
+ print_error "skills-helper.sh not found"
591
+ print_info "Run 'aidevops update' to get the latest scripts"
592
+ return 1
593
+ fi
594
+
595
+ case "$action" in
596
+ search | s | find | f)
597
+ bash "$skills_helper" search "$@"
598
+ ;;
599
+ browse | b)
600
+ bash "$skills_helper" browse "$@"
601
+ ;;
602
+ describe | desc | d | show)
603
+ bash "$skills_helper" describe "$@"
604
+ ;;
605
+ info | i | meta)
606
+ bash "$skills_helper" info "$@"
607
+ ;;
608
+ list | ls | l)
609
+ bash "$skills_helper" list "$@"
610
+ ;;
611
+ categories | cats | cat)
612
+ bash "$skills_helper" categories "$@"
613
+ ;;
614
+ recommend | rec | suggest)
615
+ bash "$skills_helper" recommend "$@"
616
+ ;;
617
+ install | add)
618
+ bash "$skills_helper" install "$@"
619
+ ;;
620
+ registry | online)
621
+ bash "$skills_helper" registry "$@"
622
+ ;;
623
+ help | --help | -h)
624
+ print_header "Skill Discovery & Exploration"
625
+ echo ""
626
+ echo "Discover, explore, and get recommendations for installed skills."
627
+ echo "For importing/managing skills, use: aidevops skill <cmd>"
628
+ echo ""
629
+ echo "Usage: aidevops skills <command> [options]"
630
+ echo ""
631
+ echo "Commands:"
632
+ echo " search <query> Search installed skills by keyword"
633
+ echo " search --registry <q> Search the public skills.sh registry (online)"
634
+ echo " browse [category] Browse skills by category"
635
+ echo " describe <name> Show detailed skill description"
636
+ echo " info <name> Show skill metadata (path, source, model tier)"
637
+ echo " list [filter] List skills (--imported, --native, --all)"
638
+ echo " categories List all categories with skill counts"
639
+ echo " recommend <task> Suggest skills for a task description"
640
+ echo " install <owner/repo@s> Install a skill from the public registry"
641
+ echo ""
642
+ echo "Options:"
643
+ echo " --json Output in JSON format (for scripting)"
644
+ echo " --registry, --online Search the public skills.sh registry"
645
+ echo ""
646
+ echo "Examples:"
647
+ echo " aidevops skills search \"browser automation\""
648
+ echo " aidevops skills search --registry \"seo\""
649
+ echo " aidevops skills browse tools"
650
+ echo " aidevops skills browse tools/browser"
651
+ echo " aidevops skills describe playwright"
652
+ echo " aidevops skills info seo-audit-skill"
653
+ echo " aidevops skills list --imported"
654
+ echo " aidevops skills categories"
655
+ echo " aidevops skills recommend \"deploy a Next.js app\""
656
+ echo " aidevops skills install vercel-labs/agent-browser@agent-browser"
657
+ echo ""
658
+ echo "See also: aidevops skill help (import/manage skills)"
659
+ ;;
660
+ *)
661
+ # Treat unknown action as a search query
662
+ bash "$skills_helper" search "$action $*"
663
+ ;;
664
+ esac
665
+ }