aidevops 2.106.0 → 2.108.0
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/README.md +38 -49
- package/VERSION +1 -1
- package/aidevops.sh +571 -8
- package/package.json +1 -1
- package/setup.sh +143 -22
package/README.md
CHANGED
|
@@ -92,10 +92,10 @@ The result: AI agents that work *with* your development process, not around it.
|
|
|
92
92
|
|
|
93
93
|
### Agent Structure
|
|
94
94
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
95
|
+
- 11 primary agents (Build+, SEO, Marketing, etc.) with specialist @subagents on demand
|
|
96
|
+
- 735+ subagent markdown files organized by domain
|
|
97
|
+
- 194 helper scripts in `.agents/scripts/`
|
|
98
|
+
- 53 slash commands for common workflows
|
|
99
99
|
|
|
100
100
|
<!-- AI-CONTEXT-END -->
|
|
101
101
|
|
|
@@ -257,26 +257,6 @@ See `.agents/tools/task-management/beads.md` for complete documentation and inst
|
|
|
257
257
|
|
|
258
258
|
**Your AI assistant now has agentic access to 30+ service integrations.**
|
|
259
259
|
|
|
260
|
-
### OpenCode Antigravity OAuth Plugin
|
|
261
|
-
|
|
262
|
-
The setup automatically installs the [opencode-antigravity-auth](https://github.com/NoeFabris/opencode-antigravity-auth) plugin, enabling Google OAuth authentication for OpenCode. This gives you access to Antigravity rate limits and premium models.
|
|
263
|
-
|
|
264
|
-
**After setup, authenticate:**
|
|
265
|
-
|
|
266
|
-
```bash
|
|
267
|
-
opencode auth login
|
|
268
|
-
# Select: Google → OAuth with Google (Antigravity)
|
|
269
|
-
# Press Enter to skip Project ID prompt
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
**Available models via Antigravity:**
|
|
273
|
-
|
|
274
|
-
- `gemini-3-pro-high` / `gemini-3-pro-low` / `gemini-3-flash`
|
|
275
|
-
- `claude-sonnet-4-5` / `claude-sonnet-4-5-thinking` / `claude-opus-4-5-thinking`
|
|
276
|
-
- `gpt-oss-120b-medium`
|
|
277
|
-
|
|
278
|
-
**Multi-account load balancing:** Add multiple Google accounts for automatic rate limit distribution and failover. See the [plugin documentation](https://github.com/NoeFabris/opencode-antigravity-auth) for model configuration.
|
|
279
|
-
|
|
280
260
|
### OpenCode Anthropic OAuth (Built-in)
|
|
281
261
|
|
|
282
262
|
OpenCode v1.1.36+ includes Anthropic OAuth authentication natively. No external plugin is needed.
|
|
@@ -453,7 +433,7 @@ aidevops implements proven agent design patterns identified by [Lance Martin (La
|
|
|
453
433
|
|
|
454
434
|
| Pattern | Description | aidevops Implementation |
|
|
455
435
|
|---------|-------------|------------------------|
|
|
456
|
-
| **Give Agents a Computer** | Filesystem + shell for persistent context | `~/.aidevops/.agent-workspace/`,
|
|
436
|
+
| **Give Agents a Computer** | Filesystem + shell for persistent context | `~/.aidevops/.agent-workspace/`, 194 helper scripts |
|
|
457
437
|
| **Multi-Layer Action Space** | Few tools, push actions to computer | Per-agent MCP filtering (~12-20 tools each) |
|
|
458
438
|
| **Progressive Disclosure** | Load context on-demand | Subagent routing with content summaries, YAML frontmatter, read-on-demand |
|
|
459
439
|
| **Offload Context** | Write results to filesystem | `.agent-workspace/work/[project]/` for persistence |
|
|
@@ -465,6 +445,8 @@ aidevops implements proven agent design patterns identified by [Lance Martin (La
|
|
|
465
445
|
| **Evolve Context** | Learn from sessions | `/remember`, `/recall` with SQLite FTS5 + opt-in semantic search |
|
|
466
446
|
| **Pattern Tracking** | Learn what works/fails | `pattern-tracker-helper.sh`, `/patterns` command |
|
|
467
447
|
| **Cost-Aware Routing** | Match model to task complexity | `model-routing.md` with 5-tier guidance, `/route` command |
|
|
448
|
+
| **Model Comparison** | Compare models side-by-side | `/compare-models` (live data), `/compare-models-free` (offline) |
|
|
449
|
+
| **Response Scoring** | Evaluate actual model outputs | `/score-responses` with structured criteria |
|
|
468
450
|
|
|
469
451
|
**Key insight**: Context is a finite resource with diminishing returns. aidevops treats every token as precious - loading only what's needed, when it's needed.
|
|
470
452
|
|
|
@@ -886,7 +868,7 @@ See `.agents/tools/ocr/glm-ocr.md` for batch processing, PDF workflows, and Peek
|
|
|
886
868
|
|
|
887
869
|
## **MCP Integrations**
|
|
888
870
|
|
|
889
|
-
**Model Context Protocol servers for real-time AI assistant integration.** The framework configures these MCPs for **[OpenCode](https://opencode.ai/)** (TUI, Desktop, and Extension for Zed/VSCode
|
|
871
|
+
**Model Context Protocol servers for real-time AI assistant integration.** The framework configures these MCPs for **[OpenCode](https://opencode.ai/)** (TUI, Desktop, and Extension for Zed/VSCode).
|
|
890
872
|
|
|
891
873
|
### **All Supported MCPs (19 available)**
|
|
892
874
|
|
|
@@ -1406,7 +1388,7 @@ aidevops is registered as a **Claude Code plugin marketplace**. Install with two
|
|
|
1406
1388
|
/plugin install aidevops@aidevops
|
|
1407
1389
|
```
|
|
1408
1390
|
|
|
1409
|
-
This installs the complete framework:
|
|
1391
|
+
This installs the complete framework: 11 primary agents, 735+ subagents, and 194 helper scripts.
|
|
1410
1392
|
|
|
1411
1393
|
### Importing External Skills
|
|
1412
1394
|
|
|
@@ -1472,29 +1454,27 @@ Call them in your AI assistant conversation with a simple @mention
|
|
|
1472
1454
|
|
|
1473
1455
|
### **Main Agents**
|
|
1474
1456
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
| Name | File | Purpose |
|
|
1478
|
-
|
|
1479
|
-
|
|
|
1480
|
-
|
|
|
1481
|
-
|
|
|
1482
|
-
|
|
|
1483
|
-
|
|
|
1484
|
-
|
|
|
1485
|
-
|
|
|
1486
|
-
|
|
|
1487
|
-
|
|
|
1488
|
-
|
|
|
1489
|
-
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
| Video | `video.md` | AI video generation, prompt engineering, programmatic video | augment |
|
|
1493
|
-
| WordPress | `wordpress.md` | WordPress ecosystem (dev, admin, MainWP, LocalWP) | localwp, context7, augment |
|
|
1457
|
+
Primary agents as registered in `subagent-index.toon` (11 total). MCPs are loaded on-demand per subagent, not per primary agent:
|
|
1458
|
+
|
|
1459
|
+
| Name | File | Purpose | Model Tier |
|
|
1460
|
+
|------|------|---------|------------|
|
|
1461
|
+
| Build+ | `build-plus.md` | Enhanced Build with context tools (default agent) | opus |
|
|
1462
|
+
| Accounts | `accounts.md` | Financial operations | opus |
|
|
1463
|
+
| Content | `content.md` | Content creation workflows | opus |
|
|
1464
|
+
| Health | `health.md` | Health and wellness | opus |
|
|
1465
|
+
| Legal | `legal.md` | Legal compliance | opus |
|
|
1466
|
+
| Marketing | `marketing.md` | Marketing strategy and email campaigns | opus |
|
|
1467
|
+
| Research | `research.md` | Research and analysis tasks | gemini/grok |
|
|
1468
|
+
| Sales | `sales.md` | Sales operations and CRM pipeline | opus |
|
|
1469
|
+
| SEO | `seo.md` | SEO optimization and analysis | opus |
|
|
1470
|
+
| Social-Media | `social-media.md` | Social media management | opus |
|
|
1471
|
+
| Video | `video.md` | AI video generation and prompt engineering | opus |
|
|
1472
|
+
|
|
1473
|
+
**Specialist subagents** (@plan-plus, @aidevops, @wordpress, Build-Agent, Build-MCP, etc.) live under `tools/` or as `mode: subagent` files and are invoked via @mention when domain expertise is needed. See `subagent-index.toon` for the full listing.
|
|
1494
1474
|
|
|
1495
1475
|
### **Example Subagents with MCP Integration**
|
|
1496
1476
|
|
|
1497
|
-
These are examples of subagents that have supporting MCPs enabled. See `.agents/` for the full list of
|
|
1477
|
+
These are examples of subagents that have supporting MCPs enabled. See `.agents/` for the full list of 735+ subagents organized by domain.
|
|
1498
1478
|
|
|
1499
1479
|
| Agent | Purpose | MCPs Enabled |
|
|
1500
1480
|
|-------|---------|--------------|
|
|
@@ -1712,6 +1692,15 @@ Configure time tracking per-repo via `.aidevops.json`.
|
|
|
1712
1692
|
| `/runners` | Batch dispatch tasks to parallel agents (task IDs, PR URLs, or descriptions) |
|
|
1713
1693
|
| `/log-issue-aidevops` | Report issues with aidevops (gathers diagnostics, checks duplicates, creates GitHub issue) |
|
|
1714
1694
|
|
|
1695
|
+
**AI Model Comparison**:
|
|
1696
|
+
|
|
1697
|
+
| Command | Purpose |
|
|
1698
|
+
|---------|---------|
|
|
1699
|
+
| `/compare-models` | Compare AI models by pricing, context, capabilities (with live web data) |
|
|
1700
|
+
| `/compare-models-free` | Compare AI models using offline embedded data only (no web fetches) |
|
|
1701
|
+
| `/score-responses` | Score and compare actual model responses with structured criteria |
|
|
1702
|
+
| `/route` | Suggest optimal model tier for a task description |
|
|
1703
|
+
|
|
1715
1704
|
### Ralph Loop - Iterative AI Development
|
|
1716
1705
|
|
|
1717
1706
|
The **Ralph Loop** (named after Ralph Wiggum's persistent optimism) enables autonomous iterative development. The AI keeps working on a task until it's complete, automatically resolving issues that arise.
|
|
@@ -2128,8 +2117,8 @@ aidevops/
|
|
|
2128
2117
|
├── AGENTS.md # AI agent guidance (dev)
|
|
2129
2118
|
├── .agents/ # Agents and documentation
|
|
2130
2119
|
│ ├── AGENTS.md # User guide (deployed to ~/.aidevops/agents/)
|
|
2131
|
-
│ ├── *.md #
|
|
2132
|
-
│ ├── scripts/ #
|
|
2120
|
+
│ ├── *.md # 11 primary agents
|
|
2121
|
+
│ ├── scripts/ # 194 helper scripts
|
|
2133
2122
|
│ ├── tools/ # Cross-domain utilities (video, browser, git, etc.)
|
|
2134
2123
|
│ ├── services/ # External service integrations
|
|
2135
2124
|
│ └── workflows/ # Development process guides
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.108.0
|
package/aidevops.sh
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# AI DevOps Framework CLI
|
|
4
4
|
# Usage: aidevops <command> [options]
|
|
5
5
|
#
|
|
6
|
-
# Version: 2.
|
|
6
|
+
# Version: 2.108.0
|
|
7
7
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
|
|
@@ -597,6 +597,7 @@ cmd_update() {
|
|
|
597
597
|
print_error "Failed to create temp file for setup script"
|
|
598
598
|
return 1
|
|
599
599
|
}
|
|
600
|
+
trap 'rm -f "${tmp_setup:-}"' RETURN
|
|
600
601
|
if curl -fsSL "https://raw.githubusercontent.com/marcusquinn/aidevops/main/setup.sh" -o "$tmp_setup" 2>/dev/null && [[ -s "$tmp_setup" ]]; then
|
|
601
602
|
chmod +x "$tmp_setup"
|
|
602
603
|
bash "$tmp_setup"
|
|
@@ -851,6 +852,7 @@ cmd_init() {
|
|
|
851
852
|
local enable_time_tracking=false
|
|
852
853
|
local enable_database=false
|
|
853
854
|
local enable_beads=false
|
|
855
|
+
local enable_sops=false
|
|
854
856
|
|
|
855
857
|
case "$features" in
|
|
856
858
|
all)
|
|
@@ -881,6 +883,9 @@ cmd_init() {
|
|
|
881
883
|
enable_beads=true
|
|
882
884
|
enable_planning=true # beads requires planning
|
|
883
885
|
;;
|
|
886
|
+
sops)
|
|
887
|
+
enable_sops=true
|
|
888
|
+
;;
|
|
884
889
|
*)
|
|
885
890
|
# Comma-separated list
|
|
886
891
|
IFS=',' read -ra FEATURE_LIST <<< "$features"
|
|
@@ -898,6 +903,7 @@ cmd_init() {
|
|
|
898
903
|
enable_beads=true
|
|
899
904
|
enable_planning=true
|
|
900
905
|
;;
|
|
906
|
+
sops) enable_sops=true ;;
|
|
901
907
|
esac
|
|
902
908
|
done
|
|
903
909
|
;;
|
|
@@ -933,13 +939,31 @@ cmd_init() {
|
|
|
933
939
|
"seeds_path": "seeds",
|
|
934
940
|
"auto_generate_migration": true
|
|
935
941
|
},
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
942
|
+
"beads": {
|
|
943
|
+
"enabled": $enable_beads,
|
|
944
|
+
"sync_on_commit": false,
|
|
945
|
+
"auto_ready_check": true
|
|
946
|
+
},
|
|
947
|
+
"sops": {
|
|
948
|
+
"enabled": $enable_sops,
|
|
949
|
+
"backend": "age",
|
|
950
|
+
"patterns": ["*.secret.yaml", "*.secret.json", "configs/*.enc.json", "configs/*.enc.yaml"]
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
"plugins": []
|
|
941
954
|
}
|
|
942
955
|
EOF
|
|
956
|
+
# Note: plugins array is always present but empty by default.
|
|
957
|
+
# Users add plugins via: aidevops plugin add <repo-url> [--namespace <name>]
|
|
958
|
+
# Schema per plugin entry:
|
|
959
|
+
# {
|
|
960
|
+
# "name": "pro",
|
|
961
|
+
# "repo": "https://github.com/user/aidevops-pro.git",
|
|
962
|
+
# "branch": "main",
|
|
963
|
+
# "namespace": "pro",
|
|
964
|
+
# "enabled": true
|
|
965
|
+
# }
|
|
966
|
+
# Plugins deploy to ~/.aidevops/agents/<namespace>/ (namespaced, no collisions)
|
|
943
967
|
print_success "Created .aidevops.json"
|
|
944
968
|
|
|
945
969
|
# Create .agents symlink (or migrate from legacy .agent)
|
|
@@ -1109,6 +1133,77 @@ EOF
|
|
|
1109
1133
|
fi
|
|
1110
1134
|
fi
|
|
1111
1135
|
|
|
1136
|
+
# Initialize SOPS if enabled
|
|
1137
|
+
if [[ "$enable_sops" == "true" ]]; then
|
|
1138
|
+
print_info "Setting up SOPS encrypted config support..."
|
|
1139
|
+
|
|
1140
|
+
# Check for sops and age
|
|
1141
|
+
local sops_ready=true
|
|
1142
|
+
if ! command -v sops &>/dev/null; then
|
|
1143
|
+
print_warning "SOPS not installed"
|
|
1144
|
+
echo " Install with: brew install sops"
|
|
1145
|
+
sops_ready=false
|
|
1146
|
+
fi
|
|
1147
|
+
if ! command -v age-keygen &>/dev/null; then
|
|
1148
|
+
print_warning "age not installed (default SOPS backend)"
|
|
1149
|
+
echo " Install with: brew install age"
|
|
1150
|
+
sops_ready=false
|
|
1151
|
+
fi
|
|
1152
|
+
|
|
1153
|
+
# Generate age key if none exists
|
|
1154
|
+
local age_key_file="$HOME/.config/sops/age/keys.txt"
|
|
1155
|
+
if [[ "$sops_ready" == "true" ]] && [[ ! -f "$age_key_file" ]]; then
|
|
1156
|
+
print_info "Generating age key for SOPS..."
|
|
1157
|
+
mkdir -p "$(dirname "$age_key_file")"
|
|
1158
|
+
age-keygen -o "$age_key_file" 2>/dev/null
|
|
1159
|
+
chmod 600 "$age_key_file"
|
|
1160
|
+
print_success "Age key generated at $age_key_file"
|
|
1161
|
+
fi
|
|
1162
|
+
|
|
1163
|
+
# Create .sops.yaml if it doesn't exist
|
|
1164
|
+
if [[ ! -f "$project_root/.sops.yaml" ]]; then
|
|
1165
|
+
local age_pubkey=""
|
|
1166
|
+
if [[ -f "$age_key_file" ]]; then
|
|
1167
|
+
age_pubkey=$(grep -o 'age1[a-z0-9]*' "$age_key_file" | head -1)
|
|
1168
|
+
fi
|
|
1169
|
+
|
|
1170
|
+
if [[ -n "$age_pubkey" ]]; then
|
|
1171
|
+
cat > "$project_root/.sops.yaml" << SOPSEOF
|
|
1172
|
+
# SOPS configuration - encrypts values in config files while keeping keys visible
|
|
1173
|
+
# See: .agents/tools/credentials/sops.md
|
|
1174
|
+
creation_rules:
|
|
1175
|
+
- path_regex: '\.secret\.(yaml|yml|json)$'
|
|
1176
|
+
age: >-
|
|
1177
|
+
$age_pubkey
|
|
1178
|
+
- path_regex: 'configs/.*\.enc\.(yaml|yml|json)$'
|
|
1179
|
+
age: >-
|
|
1180
|
+
$age_pubkey
|
|
1181
|
+
SOPSEOF
|
|
1182
|
+
print_success "Created .sops.yaml with age key"
|
|
1183
|
+
else
|
|
1184
|
+
cat > "$project_root/.sops.yaml" << 'SOPSEOF'
|
|
1185
|
+
# SOPS configuration - encrypts values in config files while keeping keys visible
|
|
1186
|
+
# See: .agents/tools/credentials/sops.md
|
|
1187
|
+
#
|
|
1188
|
+
# Generate an age key first:
|
|
1189
|
+
# age-keygen -o ~/.config/sops/age/keys.txt
|
|
1190
|
+
#
|
|
1191
|
+
# Then replace AGE_PUBLIC_KEY below with your public key:
|
|
1192
|
+
creation_rules:
|
|
1193
|
+
- path_regex: '\.secret\.(yaml|yml|json)$'
|
|
1194
|
+
age: >-
|
|
1195
|
+
AGE_PUBLIC_KEY
|
|
1196
|
+
- path_regex: 'configs/.*\.enc\.(yaml|yml|json)$'
|
|
1197
|
+
age: >-
|
|
1198
|
+
AGE_PUBLIC_KEY
|
|
1199
|
+
SOPSEOF
|
|
1200
|
+
print_warning "Created .sops.yaml template (replace AGE_PUBLIC_KEY with your key)"
|
|
1201
|
+
fi
|
|
1202
|
+
else
|
|
1203
|
+
print_info ".sops.yaml already exists"
|
|
1204
|
+
fi
|
|
1205
|
+
fi
|
|
1206
|
+
|
|
1112
1207
|
# Add to .gitignore if needed
|
|
1113
1208
|
local gitignore="$project_root/.gitignore"
|
|
1114
1209
|
if [[ -f "$gitignore" ]]; then
|
|
@@ -1159,6 +1254,7 @@ EOF
|
|
|
1159
1254
|
[[ "$enable_time_tracking" == "true" ]] && features_list="${features_list}time-tracking,"
|
|
1160
1255
|
[[ "$enable_database" == "true" ]] && features_list="${features_list}database,"
|
|
1161
1256
|
[[ "$enable_beads" == "true" ]] && features_list="${features_list}beads,"
|
|
1257
|
+
[[ "$enable_sops" == "true" ]] && features_list="${features_list}sops,"
|
|
1162
1258
|
features_list="${features_list%,}" # Remove trailing comma
|
|
1163
1259
|
|
|
1164
1260
|
# Register repo in repos.json
|
|
@@ -1174,6 +1270,7 @@ EOF
|
|
|
1174
1270
|
[[ "$enable_time_tracking" == "true" ]] && echo " ✓ Time tracking (estimates, actuals)"
|
|
1175
1271
|
[[ "$enable_database" == "true" ]] && echo " ✓ Database (schemas/, migrations/, seeds/)"
|
|
1176
1272
|
[[ "$enable_beads" == "true" ]] && echo " ✓ Beads (task graph visualization)"
|
|
1273
|
+
[[ "$enable_sops" == "true" ]] && echo " ✓ SOPS (encrypted config files with age backend)"
|
|
1177
1274
|
echo ""
|
|
1178
1275
|
echo "Next steps:"
|
|
1179
1276
|
if [[ "$enable_beads" == "true" ]]; then
|
|
@@ -1388,6 +1485,7 @@ cmd_upgrade_planning() {
|
|
|
1388
1485
|
local temp_file="${todo_file}.merge"
|
|
1389
1486
|
local tasks_file
|
|
1390
1487
|
tasks_file=$(mktemp)
|
|
1488
|
+
trap 'rm -f "${tasks_file:-}"' RETURN
|
|
1391
1489
|
printf '%s\n' "$existing_tasks" > "$tasks_file"
|
|
1392
1490
|
# Use while-read to avoid BSD awk "newline in string" warning with -v
|
|
1393
1491
|
local in_backlog=false
|
|
@@ -1452,6 +1550,7 @@ cmd_upgrade_planning() {
|
|
|
1452
1550
|
local temp_file="${plans_file}.merge"
|
|
1453
1551
|
local plans_content_file
|
|
1454
1552
|
plans_content_file=$(mktemp)
|
|
1553
|
+
trap 'rm -f "${plans_content_file:-}"' RETURN
|
|
1455
1554
|
printf '%s\n' "$existing_plans" > "$plans_content_file"
|
|
1456
1555
|
# Use while-read to avoid BSD awk "newline in string" warning with -v
|
|
1457
1556
|
local in_active=false
|
|
@@ -1558,12 +1657,27 @@ cmd_features() {
|
|
|
1558
1657
|
echo " - Ready task detection (/ready)"
|
|
1559
1658
|
echo " - Bi-directional sync with TODO.md/PLANS.md"
|
|
1560
1659
|
echo ""
|
|
1660
|
+
echo " sops Encrypted config files with SOPS + age"
|
|
1661
|
+
echo " - Value-level encryption (keys visible, values encrypted)"
|
|
1662
|
+
echo " - .sops.yaml with age backend (simpler than GPG)"
|
|
1663
|
+
echo " - Patterns: *.secret.yaml, configs/*.enc.json"
|
|
1664
|
+
echo " - See: .agents/tools/credentials/sops.md"
|
|
1665
|
+
echo ""
|
|
1666
|
+
echo "Extensibility:"
|
|
1667
|
+
echo ""
|
|
1668
|
+
echo " plugins Third-party agent plugins (configured in .aidevops.json)"
|
|
1669
|
+
echo " - Git repos deployed to ~/.aidevops/agents/<namespace>/"
|
|
1670
|
+
echo " - Namespaced to avoid collisions with core agents"
|
|
1671
|
+
echo " - Enable/disable per-plugin without removal"
|
|
1672
|
+
echo " - See: .agents/aidevops/plugins.md"
|
|
1673
|
+
echo ""
|
|
1561
1674
|
echo "Usage:"
|
|
1562
|
-
echo " aidevops init # Enable all features"
|
|
1675
|
+
echo " aidevops init # Enable all features (except sops)"
|
|
1563
1676
|
echo " aidevops init planning # Enable only planning"
|
|
1677
|
+
echo " aidevops init sops # Enable SOPS encryption"
|
|
1564
1678
|
echo " aidevops init beads # Enable beads (includes planning)"
|
|
1565
1679
|
echo " aidevops init database # Enable only database"
|
|
1566
|
-
echo " aidevops init planning,
|
|
1680
|
+
echo " aidevops init planning,sops # Enable multiple"
|
|
1567
1681
|
echo ""
|
|
1568
1682
|
}
|
|
1569
1683
|
|
|
@@ -1980,6 +2094,444 @@ cmd_skill() {
|
|
|
1980
2094
|
esac
|
|
1981
2095
|
}
|
|
1982
2096
|
|
|
2097
|
+
# Plugin management command
|
|
2098
|
+
cmd_plugin() {
|
|
2099
|
+
local action="${1:-help}"
|
|
2100
|
+
shift || true
|
|
2101
|
+
|
|
2102
|
+
local plugins_file="$CONFIG_DIR/plugins.json"
|
|
2103
|
+
local agents_dir="$AGENTS_DIR"
|
|
2104
|
+
|
|
2105
|
+
# Reserved namespaces that plugins cannot use
|
|
2106
|
+
local reserved_namespaces="custom draft scripts tools services workflows templates memory plugins seo wordpress aidevops"
|
|
2107
|
+
|
|
2108
|
+
# Ensure config dir exists
|
|
2109
|
+
mkdir -p "$CONFIG_DIR"
|
|
2110
|
+
|
|
2111
|
+
# Initialize plugins.json if missing
|
|
2112
|
+
if [[ ! -f "$plugins_file" ]]; then
|
|
2113
|
+
echo '{"plugins":[]}' > "$plugins_file"
|
|
2114
|
+
fi
|
|
2115
|
+
|
|
2116
|
+
#######################################
|
|
2117
|
+
# Validate a namespace is safe to use
|
|
2118
|
+
# Arguments: namespace
|
|
2119
|
+
# Returns: 0 if valid, 1 if reserved/invalid
|
|
2120
|
+
#######################################
|
|
2121
|
+
validate_namespace() {
|
|
2122
|
+
local ns="$1"
|
|
2123
|
+
# Must be lowercase alphanumeric with hyphens
|
|
2124
|
+
if [[ ! "$ns" =~ ^[a-z][a-z0-9-]*$ ]]; then
|
|
2125
|
+
print_error "Invalid namespace '$ns': must be lowercase alphanumeric with hyphens, starting with a letter"
|
|
2126
|
+
return 1
|
|
2127
|
+
fi
|
|
2128
|
+
# Must not be reserved
|
|
2129
|
+
local reserved
|
|
2130
|
+
for reserved in $reserved_namespaces; do
|
|
2131
|
+
if [[ "$ns" == "$reserved" ]]; then
|
|
2132
|
+
print_error "Namespace '$ns' is reserved. Choose a different name."
|
|
2133
|
+
return 1
|
|
2134
|
+
fi
|
|
2135
|
+
done
|
|
2136
|
+
return 0
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
#######################################
|
|
2140
|
+
# Get a plugin field from plugins.json
|
|
2141
|
+
# Arguments: plugin_name, field
|
|
2142
|
+
#######################################
|
|
2143
|
+
get_plugin_field() {
|
|
2144
|
+
local name="$1"
|
|
2145
|
+
local field="$2"
|
|
2146
|
+
jq -r --arg n "$name" --arg f "$field" '.plugins[] | select(.name == $n) | .[$f] // empty' "$plugins_file" 2>/dev/null || echo ""
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
case "$action" in
|
|
2150
|
+
add|a)
|
|
2151
|
+
if [[ $# -lt 1 ]]; then
|
|
2152
|
+
print_error "Repository URL required"
|
|
2153
|
+
echo ""
|
|
2154
|
+
echo "Usage: aidevops plugin add <repo-url> [options]"
|
|
2155
|
+
echo ""
|
|
2156
|
+
echo "Options:"
|
|
2157
|
+
echo " --namespace <name> Namespace directory (default: derived from repo name)"
|
|
2158
|
+
echo " --branch <branch> Branch to track (default: main)"
|
|
2159
|
+
echo " --name <name> Human-readable name (default: derived from repo)"
|
|
2160
|
+
echo ""
|
|
2161
|
+
echo "Examples:"
|
|
2162
|
+
echo " aidevops plugin add https://github.com/marcusquinn/aidevops-pro.git --namespace pro"
|
|
2163
|
+
echo " aidevops plugin add https://github.com/marcusquinn/aidevops-anon.git --namespace anon"
|
|
2164
|
+
return 1
|
|
2165
|
+
fi
|
|
2166
|
+
|
|
2167
|
+
local repo_url="$1"
|
|
2168
|
+
shift
|
|
2169
|
+
local namespace="" branch="main" plugin_name=""
|
|
2170
|
+
|
|
2171
|
+
# Parse options
|
|
2172
|
+
while [[ $# -gt 0 ]]; do
|
|
2173
|
+
case "$1" in
|
|
2174
|
+
--namespace|--ns) namespace="$2"; shift 2 ;;
|
|
2175
|
+
--branch|-b) branch="$2"; shift 2 ;;
|
|
2176
|
+
--name|-n) plugin_name="$2"; shift 2 ;;
|
|
2177
|
+
*) print_error "Unknown option: $1"; return 1 ;;
|
|
2178
|
+
esac
|
|
2179
|
+
done
|
|
2180
|
+
|
|
2181
|
+
# Derive namespace from repo URL if not provided
|
|
2182
|
+
if [[ -z "$namespace" ]]; then
|
|
2183
|
+
namespace=$(basename "$repo_url" .git | sed 's/^aidevops-//')
|
|
2184
|
+
namespace=$(echo "$namespace" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
|
|
2185
|
+
fi
|
|
2186
|
+
|
|
2187
|
+
# Derive name from namespace if not provided
|
|
2188
|
+
if [[ -z "$plugin_name" ]]; then
|
|
2189
|
+
plugin_name="$namespace"
|
|
2190
|
+
fi
|
|
2191
|
+
|
|
2192
|
+
# Validate namespace
|
|
2193
|
+
if ! validate_namespace "$namespace"; then
|
|
2194
|
+
return 1
|
|
2195
|
+
fi
|
|
2196
|
+
|
|
2197
|
+
# Check if plugin already exists
|
|
2198
|
+
local existing
|
|
2199
|
+
existing=$(jq -r --arg n "$plugin_name" '.plugins[] | select(.name == $n) | .name' "$plugins_file" 2>/dev/null || echo "")
|
|
2200
|
+
if [[ -n "$existing" ]]; then
|
|
2201
|
+
print_error "Plugin '$plugin_name' already exists. Use 'aidevops plugin update $plugin_name' to update."
|
|
2202
|
+
return 1
|
|
2203
|
+
fi
|
|
2204
|
+
|
|
2205
|
+
# Check if namespace is already in use
|
|
2206
|
+
if [[ -d "$agents_dir/$namespace" ]]; then
|
|
2207
|
+
local ns_owner
|
|
2208
|
+
ns_owner=$(jq -r --arg ns "$namespace" '.plugins[] | select(.namespace == $ns) | .name' "$plugins_file" 2>/dev/null || echo "")
|
|
2209
|
+
if [[ -n "$ns_owner" ]]; then
|
|
2210
|
+
print_error "Namespace '$namespace' is already used by plugin '$ns_owner'"
|
|
2211
|
+
else
|
|
2212
|
+
print_error "Directory '$agents_dir/$namespace/' already exists"
|
|
2213
|
+
echo " Choose a different namespace with --namespace <name>"
|
|
2214
|
+
fi
|
|
2215
|
+
return 1
|
|
2216
|
+
fi
|
|
2217
|
+
|
|
2218
|
+
print_info "Adding plugin '$plugin_name' from $repo_url..."
|
|
2219
|
+
print_info " Namespace: $namespace"
|
|
2220
|
+
print_info " Branch: $branch"
|
|
2221
|
+
|
|
2222
|
+
# Clone the repo
|
|
2223
|
+
local clone_dir="$agents_dir/$namespace"
|
|
2224
|
+
if ! git clone --branch "$branch" --depth 1 "$repo_url" "$clone_dir" 2>&1; then
|
|
2225
|
+
print_error "Failed to clone repository"
|
|
2226
|
+
rm -rf "$clone_dir" 2>/dev/null || true
|
|
2227
|
+
return 1
|
|
2228
|
+
fi
|
|
2229
|
+
|
|
2230
|
+
# Remove .git directory (we track via plugins.json, not nested git)
|
|
2231
|
+
rm -rf "$clone_dir/.git"
|
|
2232
|
+
|
|
2233
|
+
# Add to plugins.json
|
|
2234
|
+
local tmp_file="${plugins_file}.tmp"
|
|
2235
|
+
jq --arg name "$plugin_name" \
|
|
2236
|
+
--arg repo "$repo_url" \
|
|
2237
|
+
--arg branch "$branch" \
|
|
2238
|
+
--arg ns "$namespace" \
|
|
2239
|
+
'.plugins += [{"name": $name, "repo": $repo, "branch": $branch, "namespace": $ns, "enabled": true}]' \
|
|
2240
|
+
"$plugins_file" > "$tmp_file" && mv "$tmp_file" "$plugins_file"
|
|
2241
|
+
|
|
2242
|
+
print_success "Plugin '$plugin_name' installed to $clone_dir"
|
|
2243
|
+
echo ""
|
|
2244
|
+
echo " Agents available at: ~/.aidevops/agents/$namespace/"
|
|
2245
|
+
echo " Update: aidevops plugin update $plugin_name"
|
|
2246
|
+
echo " Remove: aidevops plugin remove $plugin_name"
|
|
2247
|
+
;;
|
|
2248
|
+
|
|
2249
|
+
list|ls|l)
|
|
2250
|
+
local count
|
|
2251
|
+
count=$(jq '.plugins | length' "$plugins_file" 2>/dev/null || echo "0")
|
|
2252
|
+
|
|
2253
|
+
if [[ "$count" == "0" ]]; then
|
|
2254
|
+
echo "No plugins installed."
|
|
2255
|
+
echo ""
|
|
2256
|
+
echo "Add a plugin: aidevops plugin add <repo-url> --namespace <name>"
|
|
2257
|
+
return 0
|
|
2258
|
+
fi
|
|
2259
|
+
|
|
2260
|
+
echo "Installed plugins ($count):"
|
|
2261
|
+
echo ""
|
|
2262
|
+
printf " %-15s %-10s %-8s %s\n" "NAME" "NAMESPACE" "ENABLED" "REPO"
|
|
2263
|
+
printf " %-15s %-10s %-8s %s\n" "----" "---------" "-------" "----"
|
|
2264
|
+
|
|
2265
|
+
jq -r '.plugins[] | " \(.name)\t\(.namespace)\t\(.enabled // true)\t\(.repo)"' "$plugins_file" 2>/dev/null | \
|
|
2266
|
+
while IFS=$'\t' read -r name ns enabled repo; do
|
|
2267
|
+
local status_icon="yes"
|
|
2268
|
+
if [[ "$enabled" == "false" ]]; then
|
|
2269
|
+
status_icon="no"
|
|
2270
|
+
fi
|
|
2271
|
+
printf " %-15s %-10s %-8s %s\n" "$name" "$ns" "$status_icon" "$repo"
|
|
2272
|
+
done
|
|
2273
|
+
;;
|
|
2274
|
+
|
|
2275
|
+
update|u)
|
|
2276
|
+
local target="${1:-}"
|
|
2277
|
+
|
|
2278
|
+
if [[ -n "$target" ]]; then
|
|
2279
|
+
# Update specific plugin
|
|
2280
|
+
local repo ns branch_name
|
|
2281
|
+
repo=$(get_plugin_field "$target" "repo")
|
|
2282
|
+
ns=$(get_plugin_field "$target" "namespace")
|
|
2283
|
+
branch_name=$(get_plugin_field "$target" "branch")
|
|
2284
|
+
branch_name="${branch_name:-main}"
|
|
2285
|
+
|
|
2286
|
+
if [[ -z "$repo" ]]; then
|
|
2287
|
+
print_error "Plugin '$target' not found"
|
|
2288
|
+
return 1
|
|
2289
|
+
fi
|
|
2290
|
+
|
|
2291
|
+
print_info "Updating plugin '$target'..."
|
|
2292
|
+
local clone_dir="$agents_dir/$ns"
|
|
2293
|
+
rm -rf "$clone_dir"
|
|
2294
|
+
if git clone --branch "$branch_name" --depth 1 "$repo" "$clone_dir" 2>&1; then
|
|
2295
|
+
rm -rf "$clone_dir/.git"
|
|
2296
|
+
print_success "Plugin '$target' updated"
|
|
2297
|
+
else
|
|
2298
|
+
print_error "Failed to update plugin '$target'"
|
|
2299
|
+
return 1
|
|
2300
|
+
fi
|
|
2301
|
+
else
|
|
2302
|
+
# Update all enabled plugins
|
|
2303
|
+
local names
|
|
2304
|
+
names=$(jq -r '.plugins[] | select(.enabled != false) | .name' "$plugins_file" 2>/dev/null || echo "")
|
|
2305
|
+
if [[ -z "$names" ]]; then
|
|
2306
|
+
echo "No enabled plugins to update."
|
|
2307
|
+
return 0
|
|
2308
|
+
fi
|
|
2309
|
+
|
|
2310
|
+
local failed=0
|
|
2311
|
+
while IFS= read -r pname; do
|
|
2312
|
+
[[ -z "$pname" ]] && continue
|
|
2313
|
+
local prepo pns pbranch
|
|
2314
|
+
prepo=$(get_plugin_field "$pname" "repo")
|
|
2315
|
+
pns=$(get_plugin_field "$pname" "namespace")
|
|
2316
|
+
pbranch=$(get_plugin_field "$pname" "branch")
|
|
2317
|
+
pbranch="${pbranch:-main}"
|
|
2318
|
+
|
|
2319
|
+
print_info "Updating '$pname'..."
|
|
2320
|
+
local pdir="$agents_dir/$pns"
|
|
2321
|
+
rm -rf "$pdir"
|
|
2322
|
+
if git clone --branch "$pbranch" --depth 1 "$prepo" "$pdir" 2>/dev/null; then
|
|
2323
|
+
rm -rf "$pdir/.git"
|
|
2324
|
+
print_success " '$pname' updated"
|
|
2325
|
+
else
|
|
2326
|
+
print_error " '$pname' failed to update"
|
|
2327
|
+
failed=$((failed + 1))
|
|
2328
|
+
fi
|
|
2329
|
+
done <<< "$names"
|
|
2330
|
+
|
|
2331
|
+
if [[ "$failed" -gt 0 ]]; then
|
|
2332
|
+
print_warning "$failed plugin(s) failed to update"
|
|
2333
|
+
return 1
|
|
2334
|
+
fi
|
|
2335
|
+
print_success "All plugins updated"
|
|
2336
|
+
fi
|
|
2337
|
+
;;
|
|
2338
|
+
|
|
2339
|
+
enable)
|
|
2340
|
+
if [[ $# -lt 1 ]]; then
|
|
2341
|
+
print_error "Plugin name required"
|
|
2342
|
+
echo "Usage: aidevops plugin enable <name>"
|
|
2343
|
+
return 1
|
|
2344
|
+
fi
|
|
2345
|
+
local target_name="$1"
|
|
2346
|
+
local target_repo target_ns target_branch
|
|
2347
|
+
target_repo=$(get_plugin_field "$target_name" "repo")
|
|
2348
|
+
if [[ -z "$target_repo" ]]; then
|
|
2349
|
+
print_error "Plugin '$target_name' not found"
|
|
2350
|
+
return 1
|
|
2351
|
+
fi
|
|
2352
|
+
|
|
2353
|
+
target_ns=$(get_plugin_field "$target_name" "namespace")
|
|
2354
|
+
target_branch=$(get_plugin_field "$target_name" "branch")
|
|
2355
|
+
target_branch="${target_branch:-main}"
|
|
2356
|
+
|
|
2357
|
+
# Update enabled flag
|
|
2358
|
+
local tmp_file="${plugins_file}.tmp"
|
|
2359
|
+
jq --arg n "$target_name" '(.plugins[] | select(.name == $n)).enabled = true' "$plugins_file" > "$tmp_file" && mv "$tmp_file" "$plugins_file"
|
|
2360
|
+
|
|
2361
|
+
# Deploy if not already present
|
|
2362
|
+
if [[ ! -d "$agents_dir/$target_ns" ]]; then
|
|
2363
|
+
print_info "Deploying plugin '$target_name'..."
|
|
2364
|
+
if git clone --branch "$target_branch" --depth 1 "$target_repo" "$agents_dir/$target_ns" 2>/dev/null; then
|
|
2365
|
+
rm -rf "$agents_dir/$target_ns/.git"
|
|
2366
|
+
fi
|
|
2367
|
+
fi
|
|
2368
|
+
|
|
2369
|
+
print_success "Plugin '$target_name' enabled"
|
|
2370
|
+
;;
|
|
2371
|
+
|
|
2372
|
+
disable)
|
|
2373
|
+
if [[ $# -lt 1 ]]; then
|
|
2374
|
+
print_error "Plugin name required"
|
|
2375
|
+
echo "Usage: aidevops plugin disable <name>"
|
|
2376
|
+
return 1
|
|
2377
|
+
fi
|
|
2378
|
+
local target_name="$1"
|
|
2379
|
+
local target_ns
|
|
2380
|
+
target_ns=$(get_plugin_field "$target_name" "namespace")
|
|
2381
|
+
if [[ -z "$target_ns" ]]; then
|
|
2382
|
+
print_error "Plugin '$target_name' not found"
|
|
2383
|
+
return 1
|
|
2384
|
+
fi
|
|
2385
|
+
|
|
2386
|
+
# Update enabled flag
|
|
2387
|
+
local tmp_file="${plugins_file}.tmp"
|
|
2388
|
+
jq --arg n "$target_name" '(.plugins[] | select(.name == $n)).enabled = false' "$plugins_file" > "$tmp_file" && mv "$tmp_file" "$plugins_file"
|
|
2389
|
+
|
|
2390
|
+
# Remove deployed files
|
|
2391
|
+
if [[ -d "$agents_dir/$target_ns" ]]; then
|
|
2392
|
+
rm -rf "$agents_dir/$target_ns"
|
|
2393
|
+
fi
|
|
2394
|
+
|
|
2395
|
+
print_success "Plugin '$target_name' disabled (config preserved)"
|
|
2396
|
+
;;
|
|
2397
|
+
|
|
2398
|
+
remove|rm)
|
|
2399
|
+
if [[ $# -lt 1 ]]; then
|
|
2400
|
+
print_error "Plugin name required"
|
|
2401
|
+
echo "Usage: aidevops plugin remove <name>"
|
|
2402
|
+
return 1
|
|
2403
|
+
fi
|
|
2404
|
+
local target_name="$1"
|
|
2405
|
+
local target_ns
|
|
2406
|
+
target_ns=$(get_plugin_field "$target_name" "namespace")
|
|
2407
|
+
if [[ -z "$target_ns" ]]; then
|
|
2408
|
+
print_error "Plugin '$target_name' not found"
|
|
2409
|
+
return 1
|
|
2410
|
+
fi
|
|
2411
|
+
|
|
2412
|
+
# Remove deployed files
|
|
2413
|
+
if [[ -d "$agents_dir/$target_ns" ]]; then
|
|
2414
|
+
rm -rf "$agents_dir/$target_ns"
|
|
2415
|
+
print_info "Removed $agents_dir/$target_ns/"
|
|
2416
|
+
fi
|
|
2417
|
+
|
|
2418
|
+
# Remove from plugins.json
|
|
2419
|
+
local tmp_file="${plugins_file}.tmp"
|
|
2420
|
+
jq --arg n "$target_name" '.plugins = [.plugins[] | select(.name != $n)]' "$plugins_file" > "$tmp_file" && mv "$tmp_file" "$plugins_file"
|
|
2421
|
+
|
|
2422
|
+
print_success "Plugin '$target_name' removed"
|
|
2423
|
+
;;
|
|
2424
|
+
|
|
2425
|
+
init)
|
|
2426
|
+
local target_dir="${1:-.}"
|
|
2427
|
+
local plugin_name="${2:-my-plugin}"
|
|
2428
|
+
local namespace="${3:-$plugin_name}"
|
|
2429
|
+
|
|
2430
|
+
if [[ "$target_dir" != "." && -d "$target_dir" ]]; then
|
|
2431
|
+
local existing_count
|
|
2432
|
+
existing_count=$(find "$target_dir" -maxdepth 1 -type f | wc -l | tr -d ' ')
|
|
2433
|
+
if [[ "$existing_count" -gt 0 ]]; then
|
|
2434
|
+
print_error "Directory '$target_dir' already has files. Use an empty directory."
|
|
2435
|
+
return 1
|
|
2436
|
+
fi
|
|
2437
|
+
fi
|
|
2438
|
+
|
|
2439
|
+
mkdir -p "$target_dir"
|
|
2440
|
+
|
|
2441
|
+
local template_dir="$agents_dir/templates/plugin-template"
|
|
2442
|
+
if [[ ! -d "$template_dir" ]]; then
|
|
2443
|
+
print_error "Plugin template not found at $template_dir"
|
|
2444
|
+
print_info "Run 'aidevops update' to get the latest templates."
|
|
2445
|
+
return 1
|
|
2446
|
+
fi
|
|
2447
|
+
|
|
2448
|
+
# Copy template files with placeholder substitution
|
|
2449
|
+
local plugin_name_upper
|
|
2450
|
+
plugin_name_upper=$(echo "$plugin_name" | tr '[:lower:]' '[:upper:]' | tr '-' '_')
|
|
2451
|
+
|
|
2452
|
+
# AGENTS.md
|
|
2453
|
+
sed -e "s|{{PLUGIN_NAME}}|$plugin_name|g" \
|
|
2454
|
+
-e "s|{{PLUGIN_NAME_UPPER}}|$plugin_name_upper|g" \
|
|
2455
|
+
-e "s|{{NAMESPACE}}|$namespace|g" \
|
|
2456
|
+
-e "s|{{REPO_URL}}|https://github.com/user/aidevops-$namespace.git|g" \
|
|
2457
|
+
"$template_dir/AGENTS.md" > "$target_dir/AGENTS.md"
|
|
2458
|
+
|
|
2459
|
+
# Main agent file
|
|
2460
|
+
sed -e "s|{{PLUGIN_NAME}}|$plugin_name|g" \
|
|
2461
|
+
-e "s|{{PLUGIN_DESCRIPTION}}|$plugin_name plugin for aidevops|g" \
|
|
2462
|
+
-e "s|{{NAMESPACE}}|$namespace|g" \
|
|
2463
|
+
"$template_dir/main-agent.md" > "$target_dir/$namespace.md"
|
|
2464
|
+
|
|
2465
|
+
# Example subagent directory
|
|
2466
|
+
mkdir -p "$target_dir/$namespace"
|
|
2467
|
+
sed -e "s|{{PLUGIN_NAME}}|$plugin_name|g" \
|
|
2468
|
+
-e "s|{{NAMESPACE}}|$namespace|g" \
|
|
2469
|
+
"$template_dir/example-subagent.md" > "$target_dir/$namespace/example.md"
|
|
2470
|
+
|
|
2471
|
+
# Scripts directory
|
|
2472
|
+
mkdir -p "$target_dir/scripts"
|
|
2473
|
+
|
|
2474
|
+
print_success "Plugin scaffolded in $target_dir/"
|
|
2475
|
+
echo ""
|
|
2476
|
+
echo "Structure:"
|
|
2477
|
+
echo " $target_dir/"
|
|
2478
|
+
echo " ├── AGENTS.md # Plugin documentation"
|
|
2479
|
+
echo " ├── $namespace.md # Main agent"
|
|
2480
|
+
echo " ├── $namespace/"
|
|
2481
|
+
echo " │ └── example.md # Example subagent"
|
|
2482
|
+
echo " └── scripts/ # Helper scripts (empty)"
|
|
2483
|
+
echo ""
|
|
2484
|
+
echo "Next steps:"
|
|
2485
|
+
echo " 1. Edit $namespace.md with your agent instructions"
|
|
2486
|
+
echo " 2. Add subagents to $namespace/"
|
|
2487
|
+
echo " 3. Push to a git repo"
|
|
2488
|
+
echo " 4. Install: aidevops plugin add <repo-url> --namespace $namespace"
|
|
2489
|
+
;;
|
|
2490
|
+
|
|
2491
|
+
help|--help|-h)
|
|
2492
|
+
print_header "Plugin Management"
|
|
2493
|
+
echo ""
|
|
2494
|
+
echo "Manage third-party agent plugins that extend aidevops."
|
|
2495
|
+
echo "Plugins deploy to ~/.aidevops/agents/<namespace>/ (isolated from core)."
|
|
2496
|
+
echo ""
|
|
2497
|
+
echo "Usage: aidevops plugin <command> [options]"
|
|
2498
|
+
echo ""
|
|
2499
|
+
echo "Commands:"
|
|
2500
|
+
echo " add <repo-url> Install a plugin from a git repository"
|
|
2501
|
+
echo " list List installed plugins"
|
|
2502
|
+
echo " update [name] Update specific or all plugins"
|
|
2503
|
+
echo " enable <name> Enable a disabled plugin (redeploys files)"
|
|
2504
|
+
echo " disable <name> Disable a plugin (removes files, keeps config)"
|
|
2505
|
+
echo " remove <name> Remove a plugin entirely"
|
|
2506
|
+
echo " init [dir] [name] Scaffold a new plugin from template"
|
|
2507
|
+
echo ""
|
|
2508
|
+
echo "Options for 'add':"
|
|
2509
|
+
echo " --namespace <name> Directory name under ~/.aidevops/agents/"
|
|
2510
|
+
echo " --branch <branch> Branch to track (default: main)"
|
|
2511
|
+
echo " --name <name> Human-readable plugin name"
|
|
2512
|
+
echo ""
|
|
2513
|
+
echo "Examples:"
|
|
2514
|
+
echo " aidevops plugin add https://github.com/marcusquinn/aidevops-pro.git --namespace pro"
|
|
2515
|
+
echo " aidevops plugin add https://github.com/marcusquinn/aidevops-anon.git --namespace anon"
|
|
2516
|
+
echo " aidevops plugin list"
|
|
2517
|
+
echo " aidevops plugin update"
|
|
2518
|
+
echo " aidevops plugin update pro"
|
|
2519
|
+
echo " aidevops plugin disable pro"
|
|
2520
|
+
echo " aidevops plugin enable pro"
|
|
2521
|
+
echo " aidevops plugin remove pro"
|
|
2522
|
+
echo " aidevops plugin init ./my-plugin my-plugin"
|
|
2523
|
+
echo ""
|
|
2524
|
+
echo "Plugin docs: ~/.aidevops/agents/aidevops/plugins.md"
|
|
2525
|
+
;;
|
|
2526
|
+
*)
|
|
2527
|
+
print_error "Unknown plugin command: $action"
|
|
2528
|
+
echo "Run 'aidevops plugin help' for usage information."
|
|
2529
|
+
return 1
|
|
2530
|
+
;;
|
|
2531
|
+
esac
|
|
2532
|
+
return 0
|
|
2533
|
+
}
|
|
2534
|
+
|
|
1983
2535
|
# Help command
|
|
1984
2536
|
cmd_help() {
|
|
1985
2537
|
local version
|
|
@@ -1994,6 +2546,7 @@ cmd_help() {
|
|
|
1994
2546
|
echo " upgrade-planning Upgrade TODO.md/PLANS.md to latest templates"
|
|
1995
2547
|
echo " features List available features for init"
|
|
1996
2548
|
echo " skill <cmd> Manage agent skills (add/list/check/update/remove)"
|
|
2549
|
+
echo " plugin <cmd> Manage plugins (add/list/update/enable/disable/remove)"
|
|
1997
2550
|
echo " status Check installation status of all components"
|
|
1998
2551
|
echo " update Update aidevops to the latest version (alias: upgrade)"
|
|
1999
2552
|
echo " upgrade Alias for update"
|
|
@@ -2027,6 +2580,12 @@ cmd_help() {
|
|
|
2027
2580
|
echo " aidevops secret import # Import from credentials.sh to gopass"
|
|
2028
2581
|
echo " aidevops secret status # Show backend status"
|
|
2029
2582
|
echo ""
|
|
2583
|
+
echo "Plugins:"
|
|
2584
|
+
echo " aidevops plugin add <url> # Install a plugin from git repo"
|
|
2585
|
+
echo " aidevops plugin list # List installed plugins"
|
|
2586
|
+
echo " aidevops plugin update # Update all plugins"
|
|
2587
|
+
echo " aidevops plugin remove <n> # Remove a plugin"
|
|
2588
|
+
echo ""
|
|
2030
2589
|
echo "Skills:"
|
|
2031
2590
|
echo " aidevops skill add <source> # Import a skill from GitHub"
|
|
2032
2591
|
echo " aidevops skill list # List imported skills"
|
|
@@ -2118,6 +2677,10 @@ main() {
|
|
|
2118
2677
|
shift
|
|
2119
2678
|
cmd_skill "$@"
|
|
2120
2679
|
;;
|
|
2680
|
+
plugin|plugins)
|
|
2681
|
+
shift
|
|
2682
|
+
cmd_plugin "$@"
|
|
2683
|
+
;;
|
|
2121
2684
|
detect|scan)
|
|
2122
2685
|
cmd_detect
|
|
2123
2686
|
;;
|
package/package.json
CHANGED
package/setup.sh
CHANGED
|
@@ -4,7 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
# AI Assistant Server Access Framework Setup Script
|
|
5
5
|
# Helps developers set up the framework for their infrastructure
|
|
6
6
|
#
|
|
7
|
-
# Version: 2.
|
|
7
|
+
# Version: 2.108.0
|
|
8
8
|
#
|
|
9
9
|
# Quick Install:
|
|
10
10
|
# npm install -g aidevops && aidevops update (recommended)
|
|
@@ -21,7 +21,7 @@ NC='\033[0m' # No Color
|
|
|
21
21
|
# Global flags
|
|
22
22
|
CLEAN_MODE=false
|
|
23
23
|
INTERACTIVE_MODE=false
|
|
24
|
-
NON_INTERACTIVE=false
|
|
24
|
+
NON_INTERACTIVE="${AIDEVOPS_NON_INTERACTIVE:-false}"
|
|
25
25
|
UPDATE_TOOLS_MODE=false
|
|
26
26
|
REPO_URL="https://github.com/marcusquinn/aidevops.git"
|
|
27
27
|
INSTALL_DIR="$HOME/Git/aidevops"
|
|
@@ -334,6 +334,7 @@ cleanup_deprecated_paths() {
|
|
|
334
334
|
if jq -e '.plugin | index("oh-my-opencode")' "$opencode_config" >/dev/null 2>&1; then
|
|
335
335
|
local tmp_file
|
|
336
336
|
tmp_file=$(mktemp)
|
|
337
|
+
trap 'rm -f "${tmp_file:-}"' RETURN
|
|
337
338
|
jq '.plugin = [.plugin[] | select(. != "oh-my-opencode")]' "$opencode_config" > "$tmp_file" && mv "$tmp_file" "$opencode_config"
|
|
338
339
|
print_info "Removed oh-my-opencode from OpenCode plugin list"
|
|
339
340
|
fi
|
|
@@ -514,6 +515,7 @@ cleanup_deprecated_mcps() {
|
|
|
514
515
|
local cleaned=0
|
|
515
516
|
local tmp_config
|
|
516
517
|
tmp_config=$(mktemp)
|
|
518
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
517
519
|
|
|
518
520
|
cp "$opencode_config" "$tmp_config"
|
|
519
521
|
|
|
@@ -637,6 +639,7 @@ disable_ondemand_mcps() {
|
|
|
637
639
|
local disabled=0
|
|
638
640
|
local tmp_config
|
|
639
641
|
tmp_config=$(mktemp)
|
|
642
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
640
643
|
|
|
641
644
|
cp "$opencode_config" "$tmp_config"
|
|
642
645
|
|
|
@@ -714,6 +717,7 @@ validate_opencode_config() {
|
|
|
714
717
|
if jq -e ".[\"$key\"]" "$opencode_config" > /dev/null 2>&1; then
|
|
715
718
|
local tmp_fix
|
|
716
719
|
tmp_fix=$(mktemp)
|
|
720
|
+
trap 'rm -f "${tmp_fix:-}"' RETURN
|
|
717
721
|
if jq "del(.[\"$key\"])" "$opencode_config" > "$tmp_fix" 2>/dev/null; then
|
|
718
722
|
create_backup_with_rotation "$opencode_config" "opencode"
|
|
719
723
|
mv "$tmp_fix" "$opencode_config"
|
|
@@ -2542,6 +2546,7 @@ deploy_aidevops_agents() {
|
|
|
2542
2546
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
2543
2547
|
local source_dir="$script_dir/.agents"
|
|
2544
2548
|
local target_dir="$HOME/.aidevops/agents"
|
|
2549
|
+
local plugins_file="$HOME/.config/aidevops/plugins.json"
|
|
2545
2550
|
|
|
2546
2551
|
# Validate source directory exists (catches curl install from wrong directory)
|
|
2547
2552
|
if [[ ! -d "$source_dir" ]]; then
|
|
@@ -2554,6 +2559,14 @@ deploy_aidevops_agents() {
|
|
|
2554
2559
|
return 1
|
|
2555
2560
|
fi
|
|
2556
2561
|
|
|
2562
|
+
# Collect plugin namespace directories to preserve during deployment
|
|
2563
|
+
local -a plugin_namespaces=()
|
|
2564
|
+
if [[ -f "$plugins_file" ]] && command -v jq &>/dev/null; then
|
|
2565
|
+
while IFS= read -r ns; do
|
|
2566
|
+
[[ -n "$ns" ]] && plugin_namespaces+=("$ns")
|
|
2567
|
+
done < <(jq -r '.plugins[].namespace // empty' "$plugins_file" 2>/dev/null)
|
|
2568
|
+
fi
|
|
2569
|
+
|
|
2557
2570
|
# Create backup if target exists (with rotation)
|
|
2558
2571
|
if [[ -d "$target_dir" ]]; then
|
|
2559
2572
|
create_backup_with_rotation "$target_dir" "agents"
|
|
@@ -2562,14 +2575,21 @@ deploy_aidevops_agents() {
|
|
|
2562
2575
|
# Create target directory and copy agents
|
|
2563
2576
|
mkdir -p "$target_dir"
|
|
2564
2577
|
|
|
2565
|
-
# If clean mode, remove stale files first (preserving user directories)
|
|
2578
|
+
# If clean mode, remove stale files first (preserving user and plugin directories)
|
|
2566
2579
|
if [[ "$CLEAN_MODE" == "true" ]]; then
|
|
2567
|
-
|
|
2568
|
-
# Preserve user-managed directories before clean
|
|
2580
|
+
# Build list of directories to preserve: custom, draft, plus plugin namespaces
|
|
2569
2581
|
local -a preserved_dirs=("custom" "draft")
|
|
2570
|
-
|
|
2582
|
+
if [[ ${#plugin_namespaces[@]} -gt 0 ]]; then
|
|
2583
|
+
for pns in "${plugin_namespaces[@]}"; do
|
|
2584
|
+
preserved_dirs+=("$pns")
|
|
2585
|
+
done
|
|
2586
|
+
fi
|
|
2587
|
+
print_info "Clean mode: removing stale files from $target_dir (preserving ${preserved_dirs[*]})"
|
|
2588
|
+
local tmp_preserve
|
|
2589
|
+
tmp_preserve="$(mktemp -d)"
|
|
2590
|
+
trap 'rm -rf "${tmp_preserve:-}"' RETURN
|
|
2571
2591
|
if [[ -z "$tmp_preserve" || ! -d "$tmp_preserve" ]]; then
|
|
2572
|
-
print_error "Failed to create temp dir for preserving
|
|
2592
|
+
print_error "Failed to create temp dir for preserving agents"
|
|
2573
2593
|
return 1
|
|
2574
2594
|
fi
|
|
2575
2595
|
local preserve_failed=false
|
|
@@ -2581,7 +2601,7 @@ deploy_aidevops_agents() {
|
|
|
2581
2601
|
fi
|
|
2582
2602
|
done
|
|
2583
2603
|
if [[ "$preserve_failed" == "true" ]]; then
|
|
2584
|
-
print_error "Failed to preserve
|
|
2604
|
+
print_error "Failed to preserve user/plugin agents; aborting clean"
|
|
2585
2605
|
rm -rf "$tmp_preserve"
|
|
2586
2606
|
return 1
|
|
2587
2607
|
fi
|
|
@@ -2599,15 +2619,28 @@ deploy_aidevops_agents() {
|
|
|
2599
2619
|
# - loop-state/ (local runtime state, not agents)
|
|
2600
2620
|
# - custom/ (user's private agents, never overwritten)
|
|
2601
2621
|
# - draft/ (user's experimental agents, never overwritten)
|
|
2622
|
+
# - plugin namespace directories (managed separately)
|
|
2602
2623
|
# Use rsync for selective exclusion
|
|
2603
2624
|
local deploy_ok=false
|
|
2604
2625
|
if command -v rsync &>/dev/null; then
|
|
2605
|
-
|
|
2626
|
+
local -a rsync_excludes=("--exclude=loop-state/" "--exclude=custom/" "--exclude=draft/")
|
|
2627
|
+
if [[ ${#plugin_namespaces[@]} -gt 0 ]]; then
|
|
2628
|
+
for pns in "${plugin_namespaces[@]}"; do
|
|
2629
|
+
rsync_excludes+=("--exclude=${pns}/")
|
|
2630
|
+
done
|
|
2631
|
+
fi
|
|
2632
|
+
if rsync -a "${rsync_excludes[@]}" "$source_dir/" "$target_dir/"; then
|
|
2606
2633
|
deploy_ok=true
|
|
2607
2634
|
fi
|
|
2608
2635
|
else
|
|
2609
2636
|
# Fallback: use tar with exclusions to match rsync behavior
|
|
2610
|
-
|
|
2637
|
+
local -a tar_excludes=("--exclude=loop-state" "--exclude=custom" "--exclude=draft")
|
|
2638
|
+
if [[ ${#plugin_namespaces[@]} -gt 0 ]]; then
|
|
2639
|
+
for pns in "${plugin_namespaces[@]}"; do
|
|
2640
|
+
tar_excludes+=("--exclude=$pns")
|
|
2641
|
+
done
|
|
2642
|
+
fi
|
|
2643
|
+
if (cd "$source_dir" && tar cf - "${tar_excludes[@]}" .) | (cd "$target_dir" && tar xf -); then
|
|
2611
2644
|
deploy_ok=true
|
|
2612
2645
|
fi
|
|
2613
2646
|
fi
|
|
@@ -2647,6 +2680,7 @@ deploy_aidevops_agents() {
|
|
|
2647
2680
|
# (awk -v doesn't handle multi-line content with special chars well)
|
|
2648
2681
|
local tmp_file
|
|
2649
2682
|
tmp_file=$(mktemp)
|
|
2683
|
+
trap 'rm -f "${tmp_file:-}"' RETURN
|
|
2650
2684
|
local in_placeholder=false
|
|
2651
2685
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
2652
2686
|
if [[ "$line" == *"OPENCODE-PLAN-REMINDER-INJECT-START"* ]]; then
|
|
@@ -2675,6 +2709,9 @@ deploy_aidevops_agents() {
|
|
|
2675
2709
|
print_warning "Mailbox migration had issues (non-critical, old files preserved)"
|
|
2676
2710
|
fi
|
|
2677
2711
|
fi
|
|
2712
|
+
|
|
2713
|
+
# Deploy enabled plugins from plugins.json
|
|
2714
|
+
deploy_plugins "$target_dir" "$plugins_file"
|
|
2678
2715
|
else
|
|
2679
2716
|
print_error "Failed to deploy agents"
|
|
2680
2717
|
return 1
|
|
@@ -2683,6 +2720,93 @@ deploy_aidevops_agents() {
|
|
|
2683
2720
|
return 0
|
|
2684
2721
|
}
|
|
2685
2722
|
|
|
2723
|
+
# Deploy enabled plugins from plugins.json
|
|
2724
|
+
# Arguments: target_dir, plugins_file
|
|
2725
|
+
deploy_plugins() {
|
|
2726
|
+
local target_dir="$1"
|
|
2727
|
+
local plugins_file="$2"
|
|
2728
|
+
|
|
2729
|
+
# Skip if no plugins.json or no jq
|
|
2730
|
+
if [[ ! -f "$plugins_file" ]]; then
|
|
2731
|
+
return 0
|
|
2732
|
+
fi
|
|
2733
|
+
if ! command -v jq &>/dev/null; then
|
|
2734
|
+
print_warning "jq not found; skipping plugin deployment"
|
|
2735
|
+
return 0
|
|
2736
|
+
fi
|
|
2737
|
+
|
|
2738
|
+
local plugin_count
|
|
2739
|
+
plugin_count=$(jq '.plugins | length' "$plugins_file" 2>/dev/null || echo "0")
|
|
2740
|
+
if [[ "$plugin_count" -eq 0 ]]; then
|
|
2741
|
+
return 0
|
|
2742
|
+
fi
|
|
2743
|
+
|
|
2744
|
+
local enabled_count
|
|
2745
|
+
enabled_count=$(jq '[.plugins[] | select(.enabled != false)] | length' "$plugins_file" 2>/dev/null || echo "0")
|
|
2746
|
+
if [[ "$enabled_count" -eq 0 ]]; then
|
|
2747
|
+
print_info "No enabled plugins to deploy ($plugin_count configured, all disabled)"
|
|
2748
|
+
return 0
|
|
2749
|
+
fi
|
|
2750
|
+
|
|
2751
|
+
# Remove directories for disabled plugins (cleanup)
|
|
2752
|
+
local disabled_ns
|
|
2753
|
+
while IFS= read -r disabled_ns; do
|
|
2754
|
+
[[ -z "$disabled_ns" ]] && continue
|
|
2755
|
+
if [[ -d "$target_dir/$disabled_ns" ]]; then
|
|
2756
|
+
rm -rf "$target_dir/$disabled_ns"
|
|
2757
|
+
print_info " Removed disabled plugin directory: $disabled_ns"
|
|
2758
|
+
fi
|
|
2759
|
+
done < <(jq -r '.plugins[] | select(.enabled == false) | .namespace // empty' "$plugins_file" 2>/dev/null)
|
|
2760
|
+
|
|
2761
|
+
print_info "Deploying $enabled_count plugin(s)..."
|
|
2762
|
+
|
|
2763
|
+
local deployed=0
|
|
2764
|
+
local failed=0
|
|
2765
|
+
local skipped=0
|
|
2766
|
+
|
|
2767
|
+
# Process each enabled plugin
|
|
2768
|
+
while IFS=$'\t' read -r pname prepo pns pbranch; do
|
|
2769
|
+
[[ -z "$pname" ]] && continue
|
|
2770
|
+
pbranch="${pbranch:-main}"
|
|
2771
|
+
local clone_dir="$target_dir/$pns"
|
|
2772
|
+
|
|
2773
|
+
if [[ -d "$clone_dir" ]]; then
|
|
2774
|
+
# Plugin directory exists — skip re-clone during setup
|
|
2775
|
+
# Users can force update via: aidevops plugin update [name]
|
|
2776
|
+
skipped=$((skipped + 1))
|
|
2777
|
+
continue
|
|
2778
|
+
fi
|
|
2779
|
+
|
|
2780
|
+
# Clone plugin repo
|
|
2781
|
+
print_info " Installing plugin '$pname' ($prepo)..."
|
|
2782
|
+
if git clone --branch "$pbranch" --depth 1 "$prepo" "$clone_dir" 2>/dev/null; then
|
|
2783
|
+
# Remove .git directory (tracked via plugins.json, not nested git)
|
|
2784
|
+
rm -rf "$clone_dir/.git"
|
|
2785
|
+
# Set permissions on any scripts
|
|
2786
|
+
if [[ -d "$clone_dir/scripts" ]]; then
|
|
2787
|
+
chmod +x "$clone_dir/scripts/"*.sh 2>/dev/null || true
|
|
2788
|
+
fi
|
|
2789
|
+
deployed=$((deployed + 1))
|
|
2790
|
+
else
|
|
2791
|
+
print_warning " Failed to install plugin '$pname' (network or auth issue)"
|
|
2792
|
+
failed=$((failed + 1))
|
|
2793
|
+
fi
|
|
2794
|
+
done < <(jq -r '.plugins[] | select(.enabled != false) | [.name, .repo, .namespace, (.branch // "main")] | @tsv' "$plugins_file" 2>/dev/null)
|
|
2795
|
+
|
|
2796
|
+
# Summary
|
|
2797
|
+
if [[ "$deployed" -gt 0 ]]; then
|
|
2798
|
+
print_success "Deployed $deployed plugin(s)"
|
|
2799
|
+
fi
|
|
2800
|
+
if [[ "$skipped" -gt 0 ]]; then
|
|
2801
|
+
print_info "$skipped plugin(s) already deployed (use 'aidevops plugin update' to refresh)"
|
|
2802
|
+
fi
|
|
2803
|
+
if [[ "$failed" -gt 0 ]]; then
|
|
2804
|
+
print_warning "$failed plugin(s) failed to deploy (non-blocking)"
|
|
2805
|
+
fi
|
|
2806
|
+
|
|
2807
|
+
return 0
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2686
2810
|
# Generate Agent Skills SKILL.md files for cross-tool compatibility
|
|
2687
2811
|
generate_agent_skills() {
|
|
2688
2812
|
print_info "Generating Agent Skills SKILL.md files..."
|
|
@@ -2987,6 +3111,7 @@ inject_agents_reference() {
|
|
|
2987
3111
|
# Prepend reference to existing file
|
|
2988
3112
|
local temp_file
|
|
2989
3113
|
temp_file=$(mktemp)
|
|
3114
|
+
trap 'rm -f "${temp_file:-}"' RETURN
|
|
2990
3115
|
echo "$reference_line" > "$temp_file"
|
|
2991
3116
|
echo "" >> "$temp_file"
|
|
2992
3117
|
cat "$agents_file" >> "$temp_file"
|
|
@@ -3316,6 +3441,7 @@ update_mcp_paths_in_opencode() {
|
|
|
3316
3441
|
|
|
3317
3442
|
local tmp_config
|
|
3318
3443
|
tmp_config=$(mktemp)
|
|
3444
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
3319
3445
|
cp "$opencode_config" "$tmp_config"
|
|
3320
3446
|
|
|
3321
3447
|
local updated=0
|
|
@@ -4139,7 +4265,7 @@ setup_safety_hooks() {
|
|
|
4139
4265
|
return 0
|
|
4140
4266
|
}
|
|
4141
4267
|
|
|
4142
|
-
# Setup OpenCode Plugins
|
|
4268
|
+
# Setup OpenCode Plugins
|
|
4143
4269
|
# Helper function to add/update a single plugin in OpenCode config
|
|
4144
4270
|
add_opencode_plugin() {
|
|
4145
4271
|
local plugin_name="$1"
|
|
@@ -4159,6 +4285,7 @@ add_opencode_plugin() {
|
|
|
4159
4285
|
# Update existing plugin to latest version
|
|
4160
4286
|
local temp_file
|
|
4161
4287
|
temp_file=$(mktemp)
|
|
4288
|
+
trap 'rm -f "${temp_file:-}"' RETURN
|
|
4162
4289
|
jq --arg old "$plugin_name" --arg new "$plugin_spec" \
|
|
4163
4290
|
'.plugin = [.plugin[] | if startswith($old) then $new else . end]' \
|
|
4164
4291
|
"$opencode_config" > "$temp_file" && mv "$temp_file" "$opencode_config"
|
|
@@ -4167,6 +4294,7 @@ add_opencode_plugin() {
|
|
|
4167
4294
|
# Add plugin to existing array
|
|
4168
4295
|
local temp_file
|
|
4169
4296
|
temp_file=$(mktemp)
|
|
4297
|
+
trap 'rm -f "${temp_file:-}"' RETURN
|
|
4170
4298
|
jq --arg p "$plugin_spec" '.plugin += [$p]' "$opencode_config" > "$temp_file" && mv "$temp_file" "$opencode_config"
|
|
4171
4299
|
print_success "Added $plugin_name plugin to OpenCode config"
|
|
4172
4300
|
fi
|
|
@@ -4174,6 +4302,7 @@ add_opencode_plugin() {
|
|
|
4174
4302
|
# Create plugin array with the plugin
|
|
4175
4303
|
local temp_file
|
|
4176
4304
|
temp_file=$(mktemp)
|
|
4305
|
+
trap 'rm -f "${temp_file:-}"' RETURN
|
|
4177
4306
|
jq --arg p "$plugin_spec" '. + {plugin: [$p]}' "$opencode_config" > "$temp_file" && mv "$temp_file" "$opencode_config"
|
|
4178
4307
|
print_success "Created plugin array with $plugin_name"
|
|
4179
4308
|
fi
|
|
@@ -4210,21 +4339,11 @@ setup_opencode_plugins() {
|
|
|
4210
4339
|
print_success "aidevops compaction plugin registered (preserves context across compaction)"
|
|
4211
4340
|
fi
|
|
4212
4341
|
|
|
4213
|
-
# Setup Antigravity OAuth plugin (Google OAuth)
|
|
4214
|
-
print_info "Setting up Antigravity OAuth plugin..."
|
|
4215
|
-
add_opencode_plugin "opencode-antigravity-auth" "opencode-antigravity-auth@latest" "$opencode_config"
|
|
4216
|
-
|
|
4217
|
-
print_info "Antigravity OAuth plugin enables Google OAuth for OpenCode"
|
|
4218
|
-
print_info "Models available: gemini-3-pro-high, claude-opus-4-5-thinking, etc."
|
|
4219
|
-
print_info "See: https://github.com/NoeFabris/opencode-antigravity-auth"
|
|
4220
|
-
echo ""
|
|
4221
|
-
|
|
4222
4342
|
# Note: opencode-anthropic-auth is built into OpenCode v1.1.36+
|
|
4223
4343
|
# Adding it as an external plugin causes TypeError due to double-loading.
|
|
4224
4344
|
# Removed in v2.90.0 - see PR #230.
|
|
4225
4345
|
|
|
4226
4346
|
print_info "After setup, authenticate with: opencode auth login"
|
|
4227
|
-
print_info " • For Google OAuth: Select 'Google' → 'OAuth with Google (Antigravity)'"
|
|
4228
4347
|
print_info " • For Claude OAuth: Select 'Anthropic' → 'Claude Pro/Max' (built-in)"
|
|
4229
4348
|
|
|
4230
4349
|
return 0
|
|
@@ -4316,6 +4435,7 @@ setup_google_analytics_mcp() {
|
|
|
4316
4435
|
if [[ "$enable_mcp" == "true" ]]; then
|
|
4317
4436
|
local tmp_config
|
|
4318
4437
|
tmp_config=$(mktemp)
|
|
4438
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
4319
4439
|
if jq --arg creds "$creds_path" --arg proj "$project_id" \
|
|
4320
4440
|
'.mcp["google-analytics-mcp"].environment.GOOGLE_APPLICATION_CREDENTIALS = $creds |
|
|
4321
4441
|
.mcp["google-analytics-mcp"].environment.GOOGLE_PROJECT_ID = $proj |
|
|
@@ -4336,6 +4456,7 @@ setup_google_analytics_mcp() {
|
|
|
4336
4456
|
# Add google-analytics-mcp to opencode.json
|
|
4337
4457
|
local tmp_config
|
|
4338
4458
|
tmp_config=$(mktemp)
|
|
4459
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
4339
4460
|
|
|
4340
4461
|
if jq --arg creds "$creds_path" --arg proj "$project_id" --argjson enabled "$enable_mcp" \
|
|
4341
4462
|
'.mcp["google-analytics-mcp"] = {
|
|
@@ -4448,7 +4569,7 @@ setup_quickfile_mcp() {
|
|
|
4448
4569
|
|
|
4449
4570
|
local tmp_config
|
|
4450
4571
|
tmp_config=$(mktemp)
|
|
4451
|
-
trap 'rm -f "$tmp_config"' RETURN
|
|
4572
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
4452
4573
|
|
|
4453
4574
|
if jq --arg np "$node_path" --arg dp "$quickfile_dir/dist/index.js" \
|
|
4454
4575
|
'.mcp.quickfile = {"type": "local", "command": [$np, $dp], "enabled": true}' \
|