aidevops 2.172.18 → 2.172.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/aidevops.sh +1 -1
- package/package.json +2 -1
- package/scripts/npm-postinstall.cjs +13 -2
- package/setup-modules/agent-deploy.sh +627 -0
- package/setup-modules/config.sh +183 -0
- package/setup-modules/core.sh +572 -0
- package/setup-modules/mcp-setup.sh +766 -0
- package/setup-modules/migrations.sh +961 -0
- package/setup-modules/plugins.sh +588 -0
- package/setup-modules/shell-env.sh +892 -0
- package/setup-modules/tool-install.sh +1373 -0
- package/setup.sh +1 -1
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# MCP setup functions: install_mcp_packages, resolve_mcp_binary, localwp, augment, seo, analytics, quickfile, browser-tools, opencode-plugins
|
|
3
|
+
# Part of aidevops setup.sh modularization (t316.3)
|
|
4
|
+
|
|
5
|
+
# Shell safety baseline
|
|
6
|
+
set -Eeuo pipefail
|
|
7
|
+
IFS=$'\n\t'
|
|
8
|
+
# shellcheck disable=SC2154 # rc is assigned by $? in the trap string
|
|
9
|
+
trap 'rc=$?; echo "[ERROR] ${BASH_SOURCE[0]}:${LINENO} exit $rc" >&2' ERR
|
|
10
|
+
shopt -s inherit_errexit 2>/dev/null || true
|
|
11
|
+
|
|
12
|
+
install_mcp_packages() {
|
|
13
|
+
print_info "Installing MCP server packages globally (eliminates npx startup delay)..."
|
|
14
|
+
|
|
15
|
+
# Security note: MCP servers run as persistent processes with access to conversation
|
|
16
|
+
# context, credentials, and network. The packages below are from known/vetted sources.
|
|
17
|
+
# Before adding new MCP packages to this list, verify the source repository and scan
|
|
18
|
+
# dependencies with: npx @socketsecurity/cli npm info <package>
|
|
19
|
+
# See: .agents/tools/mcp-toolkit/mcporter.md "Security Considerations"
|
|
20
|
+
|
|
21
|
+
# Node.js MCP packages to install globally
|
|
22
|
+
local -a node_mcps=(
|
|
23
|
+
"chrome-devtools-mcp"
|
|
24
|
+
"mcp-server-gsc"
|
|
25
|
+
"playwriter"
|
|
26
|
+
"@steipete/macos-automator-mcp"
|
|
27
|
+
"@steipete/claude-code-mcp"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if ! command -v bun &>/dev/null && ! command -v npm &>/dev/null; then
|
|
31
|
+
print_warning "Neither bun nor npm found - cannot install MCP packages"
|
|
32
|
+
print_info "Install bun (recommended): npm install -g bun OR brew install oven-sh/bun/bun"
|
|
33
|
+
return 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
local installer="npm"
|
|
37
|
+
command -v bun &>/dev/null && installer="bun"
|
|
38
|
+
print_info "Using $installer to install/update Node.js MCP packages..."
|
|
39
|
+
|
|
40
|
+
# Always install latest (bun install -g is fast and idempotent)
|
|
41
|
+
local updated=0
|
|
42
|
+
local failed=0
|
|
43
|
+
local pkg
|
|
44
|
+
for pkg in "${node_mcps[@]}"; do
|
|
45
|
+
local short_name="${pkg##*/}" # Strip @scope/ prefix for display
|
|
46
|
+
if run_with_spinner "Installing $short_name" npm_global_install "${pkg}@latest"; then
|
|
47
|
+
((++updated))
|
|
48
|
+
else
|
|
49
|
+
((++failed))
|
|
50
|
+
print_warning "Failed to install/update $pkg"
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
if [[ $updated -gt 0 ]]; then
|
|
55
|
+
print_success "$updated Node.js MCP packages installed/updated to latest via $installer"
|
|
56
|
+
fi
|
|
57
|
+
if [[ $failed -gt 0 ]]; then
|
|
58
|
+
print_warning "$failed packages failed (check network or package names)"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Python MCP packages (install or upgrade)
|
|
62
|
+
if command -v pipx &>/dev/null; then
|
|
63
|
+
print_info "Installing/updating analytics-mcp via pipx..."
|
|
64
|
+
if command -v analytics-mcp &>/dev/null; then
|
|
65
|
+
pipx upgrade analytics-mcp >/dev/null 2>&1 || true
|
|
66
|
+
else
|
|
67
|
+
pipx install analytics-mcp >/dev/null 2>&1 || print_warning "Failed to install analytics-mcp"
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if command -v uv &>/dev/null; then
|
|
72
|
+
print_info "Installing/updating outscraper-mcp-server via uv..."
|
|
73
|
+
if command -v outscraper-mcp-server &>/dev/null; then
|
|
74
|
+
uv tool upgrade outscraper-mcp-server >/dev/null 2>&1 || true
|
|
75
|
+
else
|
|
76
|
+
uv tool install outscraper-mcp-server >/dev/null 2>&1 || print_warning "Failed to install outscraper-mcp-server"
|
|
77
|
+
fi
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Update opencode.json with resolved full paths for all MCP binaries
|
|
81
|
+
update_mcp_paths_in_opencode
|
|
82
|
+
|
|
83
|
+
print_info "MCP servers will start instantly (no registry lookups on each launch)"
|
|
84
|
+
return 0
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
resolve_mcp_binary_path() {
|
|
88
|
+
local bin_name
|
|
89
|
+
bin_name="$1"
|
|
90
|
+
local resolved=""
|
|
91
|
+
|
|
92
|
+
# Check common locations in priority order
|
|
93
|
+
local search_paths=(
|
|
94
|
+
"$HOME/.bun/bin/$bin_name"
|
|
95
|
+
"/opt/homebrew/bin/$bin_name"
|
|
96
|
+
"/usr/local/bin/$bin_name"
|
|
97
|
+
"$HOME/.local/bin/$bin_name"
|
|
98
|
+
"$HOME/.npm-global/bin/$bin_name"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
for path in "${search_paths[@]}"; do
|
|
102
|
+
if [[ -x "$path" ]]; then
|
|
103
|
+
resolved="$path"
|
|
104
|
+
break
|
|
105
|
+
fi
|
|
106
|
+
done
|
|
107
|
+
|
|
108
|
+
# Fallback: use command -v if in PATH (portable, POSIX-compliant)
|
|
109
|
+
if [[ -z "$resolved" ]]; then
|
|
110
|
+
resolved=$(command -v "$bin_name" 2>/dev/null || true)
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
echo "$resolved"
|
|
114
|
+
return 0
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
update_mcp_paths_in_opencode() {
|
|
118
|
+
local opencode_config
|
|
119
|
+
opencode_config=$(find_opencode_config) || return 0
|
|
120
|
+
|
|
121
|
+
if [[ ! -f "$opencode_config" ]]; then
|
|
122
|
+
return 0
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
if ! command -v jq &>/dev/null; then
|
|
126
|
+
return 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
local tmp_config
|
|
130
|
+
tmp_config=$(mktemp)
|
|
131
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
132
|
+
cp "$opencode_config" "$tmp_config"
|
|
133
|
+
|
|
134
|
+
local updated=0
|
|
135
|
+
|
|
136
|
+
# Get all MCP entries with local commands
|
|
137
|
+
local mcp_keys
|
|
138
|
+
mcp_keys=$(jq -r '.mcp | to_entries[] | select(.value.type == "local") | select(.value.command != null) | .key' "$tmp_config" 2>/dev/null)
|
|
139
|
+
|
|
140
|
+
while IFS= read -r mcp_key; do
|
|
141
|
+
[[ -z "$mcp_key" ]] && continue
|
|
142
|
+
|
|
143
|
+
# Get the first element of the command array (the binary)
|
|
144
|
+
local current_cmd
|
|
145
|
+
current_cmd=$(jq -r --arg k "$mcp_key" '.mcp[$k].command[0]' "$tmp_config" 2>/dev/null)
|
|
146
|
+
|
|
147
|
+
# Skip if already a full path
|
|
148
|
+
if [[ "$current_cmd" == /* ]]; then
|
|
149
|
+
# Verify the path still exists
|
|
150
|
+
if [[ ! -x "$current_cmd" ]]; then
|
|
151
|
+
# Path is stale, try to resolve
|
|
152
|
+
local bin_name
|
|
153
|
+
bin_name=$(basename "$current_cmd")
|
|
154
|
+
local new_path
|
|
155
|
+
new_path=$(resolve_mcp_binary_path "$bin_name")
|
|
156
|
+
if [[ -n "$new_path" && "$new_path" != "$current_cmd" ]]; then
|
|
157
|
+
jq --arg k "$mcp_key" --arg p "$new_path" '.mcp[$k].command[0] = $p' "$tmp_config" >"${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
|
|
158
|
+
((++updated))
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
continue
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Skip docker (container runtime) and node (resolved separately below)
|
|
165
|
+
case "$current_cmd" in
|
|
166
|
+
docker | node) continue ;;
|
|
167
|
+
esac
|
|
168
|
+
|
|
169
|
+
# Resolve the full path
|
|
170
|
+
local full_path
|
|
171
|
+
full_path=$(resolve_mcp_binary_path "$current_cmd")
|
|
172
|
+
|
|
173
|
+
if [[ -n "$full_path" && "$full_path" != "$current_cmd" ]]; then
|
|
174
|
+
jq --arg k "$mcp_key" --arg p "$full_path" '.mcp[$k].command[0] = $p' "$tmp_config" >"${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
|
|
175
|
+
((++updated))
|
|
176
|
+
fi
|
|
177
|
+
done <<<"$mcp_keys"
|
|
178
|
+
|
|
179
|
+
# Also resolve 'node' commands (e.g., quickfile, amazon-order-history)
|
|
180
|
+
# These use ["node", "/path/to/index.js"] - node itself should be resolved
|
|
181
|
+
local node_path
|
|
182
|
+
node_path=$(resolve_mcp_binary_path "node")
|
|
183
|
+
if [[ -n "$node_path" ]]; then
|
|
184
|
+
local node_mcp_keys
|
|
185
|
+
node_mcp_keys=$(jq -r '.mcp | to_entries[] | select(.value.type == "local") | select(.value.command != null) | select(.value.command[0] == "node") | .key' "$tmp_config" 2>/dev/null)
|
|
186
|
+
while IFS= read -r mcp_key; do
|
|
187
|
+
[[ -z "$mcp_key" ]] && continue
|
|
188
|
+
jq --arg k "$mcp_key" --arg p "$node_path" '.mcp[$k].command[0] = $p' "$tmp_config" >"${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
|
|
189
|
+
((++updated))
|
|
190
|
+
done <<<"$node_mcp_keys"
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
if [[ $updated -gt 0 ]]; then
|
|
194
|
+
create_backup_with_rotation "$opencode_config" "opencode"
|
|
195
|
+
mv "$tmp_config" "$opencode_config"
|
|
196
|
+
print_success "Updated $updated MCP commands to use full binary paths in opencode.json"
|
|
197
|
+
else
|
|
198
|
+
rm -f "$tmp_config"
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
return 0
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
setup_localwp_mcp() {
|
|
205
|
+
print_info "Setting up LocalWP MCP server..."
|
|
206
|
+
|
|
207
|
+
# Check if LocalWP is installed
|
|
208
|
+
local localwp_found=false
|
|
209
|
+
if [[ -d "/Applications/Local.app" ]] || [[ -d "$HOME/Applications/Local.app" ]]; then
|
|
210
|
+
localwp_found=true
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
if [[ "$localwp_found" != "true" ]]; then
|
|
214
|
+
print_info "LocalWP not found - skipping MCP server setup"
|
|
215
|
+
print_info "Install LocalWP from: https://localwp.com/"
|
|
216
|
+
return 0
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
print_success "LocalWP found"
|
|
220
|
+
|
|
221
|
+
# Check if npm is available
|
|
222
|
+
if ! command -v npm &>/dev/null; then
|
|
223
|
+
print_warning "npm not found - cannot install LocalWP MCP server"
|
|
224
|
+
print_info "Install Node.js and npm first"
|
|
225
|
+
return 0
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# Check if mcp-local-wp is already installed
|
|
229
|
+
if command -v mcp-local-wp &>/dev/null; then
|
|
230
|
+
print_success "LocalWP MCP server already installed"
|
|
231
|
+
return 0
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Offer to install mcp-local-wp
|
|
235
|
+
print_info "LocalWP MCP server enables AI assistants to query WordPress databases"
|
|
236
|
+
read -r -p "Install LocalWP MCP server (@verygoodplugins/mcp-local-wp)? [Y/n]: " install_mcp
|
|
237
|
+
|
|
238
|
+
if [[ "$install_mcp" =~ ^[Yy]?$ ]]; then
|
|
239
|
+
if run_with_spinner "Installing LocalWP MCP server" npm_global_install "@verygoodplugins/mcp-local-wp"; then
|
|
240
|
+
print_info "Start with: ~/.aidevops/agents/scripts/localhost-helper.sh start-mcp"
|
|
241
|
+
print_info "Or configure in OpenCode MCP settings for auto-start"
|
|
242
|
+
else
|
|
243
|
+
print_info "Try manually: sudo npm install -g @verygoodplugins/mcp-local-wp"
|
|
244
|
+
fi
|
|
245
|
+
else
|
|
246
|
+
print_info "Skipped LocalWP MCP server installation"
|
|
247
|
+
print_info "Install later: npm install -g @verygoodplugins/mcp-local-wp"
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
return 0
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
setup_augment_context_engine() {
|
|
254
|
+
print_info "Setting up Augment Context Engine MCP..."
|
|
255
|
+
|
|
256
|
+
# Check Node.js version (requires 22+)
|
|
257
|
+
if ! command -v node &>/dev/null; then
|
|
258
|
+
print_warning "Node.js not found - Augment Context Engine setup skipped"
|
|
259
|
+
print_info "Install Node.js 22+ to enable Augment Context Engine"
|
|
260
|
+
return
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
local node_version
|
|
264
|
+
node_version=$(node --version 2>/dev/null | cut -d'v' -f2 | cut -d'.' -f1)
|
|
265
|
+
if [[ -z "$node_version" ]] || ! [[ "$node_version" =~ ^[0-9]+$ ]]; then
|
|
266
|
+
print_warning "Could not determine Node.js version - Augment Context Engine setup skipped"
|
|
267
|
+
return
|
|
268
|
+
fi
|
|
269
|
+
if [[ "$node_version" -lt 22 ]]; then
|
|
270
|
+
print_warning "Node.js 22+ required for Augment Context Engine, found v$node_version"
|
|
271
|
+
print_info "Install: brew install node@22 (macOS) or nvm install 22"
|
|
272
|
+
return
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# Check if auggie is installed
|
|
276
|
+
if ! command -v auggie &>/dev/null; then
|
|
277
|
+
print_warning "Auggie CLI not found"
|
|
278
|
+
print_info "Install with: npm install -g @augmentcode/auggie@prerelease"
|
|
279
|
+
print_info "Then run: auggie login"
|
|
280
|
+
return
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
# Check if logged in
|
|
284
|
+
if [[ ! -f "$HOME/.augment/session.json" ]]; then
|
|
285
|
+
print_warning "Auggie not logged in"
|
|
286
|
+
print_info "Run: auggie login"
|
|
287
|
+
return
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
print_success "Auggie CLI found and authenticated"
|
|
291
|
+
|
|
292
|
+
# MCP configuration is handled by generate-opencode-agents.sh for OpenCode
|
|
293
|
+
|
|
294
|
+
print_info "Augment Context Engine available as MCP in OpenCode"
|
|
295
|
+
print_info "Verification: 'What is this project? Please use codebase retrieval tool.'"
|
|
296
|
+
|
|
297
|
+
return 0
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
setup_browser_tools() {
|
|
301
|
+
print_info "Setting up browser automation tools..."
|
|
302
|
+
|
|
303
|
+
local has_bun=false
|
|
304
|
+
local has_node=false
|
|
305
|
+
|
|
306
|
+
# Check Bun
|
|
307
|
+
if command -v bun &>/dev/null; then
|
|
308
|
+
has_bun=true
|
|
309
|
+
print_success "Bun $(bun --version) found"
|
|
310
|
+
fi
|
|
311
|
+
|
|
312
|
+
# Check Node.js (for Playwriter)
|
|
313
|
+
if command -v node &>/dev/null; then
|
|
314
|
+
has_node=true
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
# Install Bun if not present (required for dev-browser)
|
|
318
|
+
if [[ "$has_bun" == "false" ]]; then
|
|
319
|
+
print_info "Installing Bun (required for dev-browser)..."
|
|
320
|
+
if verified_install "Bun" "https://bun.sh/install"; then
|
|
321
|
+
# Source the updated PATH
|
|
322
|
+
export BUN_INSTALL="$HOME/.bun"
|
|
323
|
+
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
324
|
+
if command -v bun &>/dev/null; then
|
|
325
|
+
has_bun=true
|
|
326
|
+
print_success "Bun installed: $(bun --version)"
|
|
327
|
+
|
|
328
|
+
# Bun's installer may only write to the running shell's rc file.
|
|
329
|
+
# Ensure Bun PATH is in all shell rc files for cross-shell compat.
|
|
330
|
+
# shellcheck disable=SC2016 # written to rc files; must expand at shell startup, not now
|
|
331
|
+
local bun_path_line='export BUN_INSTALL="$HOME/.bun"'
|
|
332
|
+
# shellcheck disable=SC2016 # written to rc files; must expand at shell startup, not now
|
|
333
|
+
local bun_export_line='export PATH="$BUN_INSTALL/bin:$PATH"'
|
|
334
|
+
local bun_rc
|
|
335
|
+
while IFS= read -r bun_rc; do
|
|
336
|
+
[[ -z "$bun_rc" ]] && continue
|
|
337
|
+
if [[ ! -f "$bun_rc" ]]; then
|
|
338
|
+
touch "$bun_rc"
|
|
339
|
+
fi
|
|
340
|
+
if ! grep -q '\.bun' "$bun_rc" 2>/dev/null; then
|
|
341
|
+
{
|
|
342
|
+
echo ""
|
|
343
|
+
echo "# Bun (added by aidevops setup)"
|
|
344
|
+
echo "$bun_path_line"
|
|
345
|
+
echo "$bun_export_line"
|
|
346
|
+
} >>"$bun_rc"
|
|
347
|
+
print_info "Added Bun to PATH in $bun_rc"
|
|
348
|
+
fi
|
|
349
|
+
done < <(get_all_shell_rcs)
|
|
350
|
+
fi
|
|
351
|
+
else
|
|
352
|
+
print_warning "Bun installation failed - dev-browser will need manual setup"
|
|
353
|
+
fi
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
# Setup dev-browser if Bun is available
|
|
357
|
+
if [[ "$has_bun" == "true" ]]; then
|
|
358
|
+
local dev_browser_dir="$HOME/.aidevops/dev-browser"
|
|
359
|
+
|
|
360
|
+
if [[ -d "${dev_browser_dir}/skills/dev-browser" ]]; then
|
|
361
|
+
print_success "dev-browser already installed"
|
|
362
|
+
else
|
|
363
|
+
print_info "Installing dev-browser (stateful browser automation)..."
|
|
364
|
+
local dev_browser_output
|
|
365
|
+
if dev_browser_output=$(bash "$HOME/.aidevops/agents/scripts/dev-browser-helper.sh" setup 2>&1); then
|
|
366
|
+
print_success "dev-browser installed"
|
|
367
|
+
print_info "Start server with: bash ~/.aidevops/agents/scripts/dev-browser-helper.sh start"
|
|
368
|
+
else
|
|
369
|
+
print_warning "dev-browser setup failed:"
|
|
370
|
+
# Show last few lines of error output for debugging
|
|
371
|
+
echo "$dev_browser_output" | tail -5 | sed 's/^/ /'
|
|
372
|
+
echo ""
|
|
373
|
+
print_info "Run manually to see full output:"
|
|
374
|
+
print_info " bash ~/.aidevops/agents/scripts/dev-browser-helper.sh setup"
|
|
375
|
+
fi
|
|
376
|
+
fi
|
|
377
|
+
fi
|
|
378
|
+
|
|
379
|
+
# Playwriter MCP (Node.js based, runs via npx)
|
|
380
|
+
if [[ "$has_node" == "true" ]]; then
|
|
381
|
+
print_success "Playwriter MCP available (runs via npx playwriter@latest)"
|
|
382
|
+
print_info "Install Chrome extension: https://chromewebstore.google.com/detail/playwriter-mcp/jfeammnjpkecdekppnclgkkffahnhfhe"
|
|
383
|
+
else
|
|
384
|
+
print_warning "Node.js not found - Playwriter MCP unavailable"
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
# Playwright MCP (cross-browser testing automation)
|
|
388
|
+
if [[ "$has_node" == "true" ]]; then
|
|
389
|
+
print_info "Setting up Playwright MCP..."
|
|
390
|
+
|
|
391
|
+
# Check if Playwright browsers are installed (--no-install prevents auto-download)
|
|
392
|
+
if npx --no-install playwright --version &>/dev/null 2>&1; then
|
|
393
|
+
print_success "Playwright already installed"
|
|
394
|
+
else
|
|
395
|
+
local install_playwright
|
|
396
|
+
read -r -p "Install Playwright MCP with browsers (chromium, firefox, webkit)? [Y/n]: " install_playwright
|
|
397
|
+
|
|
398
|
+
if [[ "$install_playwright" =~ ^[Yy]?$ ]]; then
|
|
399
|
+
print_info "Installing Playwright browsers..."
|
|
400
|
+
# Use -y to auto-confirm npx install, suppress the "install without dependencies" warning
|
|
401
|
+
# Use PIPESTATUS to check npx exit code, not grep's exit code
|
|
402
|
+
npx -y playwright@latest install 2>&1 | grep -v "WARNING: It looks like you are running"
|
|
403
|
+
if [[ ${PIPESTATUS[0]} -eq 0 ]]; then
|
|
404
|
+
print_success "Playwright browsers installed"
|
|
405
|
+
else
|
|
406
|
+
print_warning "Playwright browser installation failed"
|
|
407
|
+
print_info "Run manually: npx -y playwright@latest install"
|
|
408
|
+
fi
|
|
409
|
+
else
|
|
410
|
+
print_info "Skipped Playwright installation"
|
|
411
|
+
print_info "Install later with: npx playwright install"
|
|
412
|
+
fi
|
|
413
|
+
fi
|
|
414
|
+
|
|
415
|
+
print_info "Playwright MCP runs via: npx playwright-mcp@latest"
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
if [[ "$has_node" == "true" ]]; then
|
|
419
|
+
print_info "Browser tools: dev-browser (stateful), Playwriter (extension), Playwright (testing), Stagehand (AI)"
|
|
420
|
+
else
|
|
421
|
+
print_info "Browser tools: dev-browser (stateful), Stagehand (AI)"
|
|
422
|
+
fi
|
|
423
|
+
return 0
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
add_opencode_plugin() {
|
|
427
|
+
local plugin_name plugin_spec opencode_config
|
|
428
|
+
plugin_name="$1"
|
|
429
|
+
plugin_spec="$2"
|
|
430
|
+
opencode_config="$3"
|
|
431
|
+
|
|
432
|
+
# Check if plugin array exists and if plugin is already configured
|
|
433
|
+
local has_plugin_array
|
|
434
|
+
if jq -e '.plugin' "$opencode_config" >/dev/null 2>&1; then
|
|
435
|
+
has_plugin_array="true"
|
|
436
|
+
else
|
|
437
|
+
has_plugin_array="false"
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
if [[ "$has_plugin_array" == "true" ]]; then
|
|
441
|
+
# Check if plugin is already in the array
|
|
442
|
+
local plugin_exists
|
|
443
|
+
if jq -e --arg p "$plugin_name" '.plugin | map(select(startswith($p))) | length > 0' "$opencode_config" >/dev/null 2>&1; then
|
|
444
|
+
plugin_exists="true"
|
|
445
|
+
else
|
|
446
|
+
plugin_exists="false"
|
|
447
|
+
fi
|
|
448
|
+
|
|
449
|
+
if [[ "$plugin_exists" == "true" ]]; then
|
|
450
|
+
# Update existing plugin to latest version
|
|
451
|
+
local temp_file
|
|
452
|
+
temp_file=$(mktemp)
|
|
453
|
+
trap 'rm -f "${temp_file:-}"' RETURN
|
|
454
|
+
jq --arg old "$plugin_name" --arg new "$plugin_spec" \
|
|
455
|
+
'.plugin = [.plugin[] | if startswith($old) then $new else . end]' \
|
|
456
|
+
"$opencode_config" >"$temp_file" && mv "$temp_file" "$opencode_config"
|
|
457
|
+
print_success "Updated $plugin_name to latest version"
|
|
458
|
+
else
|
|
459
|
+
# Add plugin to existing array
|
|
460
|
+
local temp_file
|
|
461
|
+
temp_file=$(mktemp)
|
|
462
|
+
trap 'rm -f "${temp_file:-}"' RETURN
|
|
463
|
+
jq --arg p "$plugin_spec" '.plugin += [$p]' "$opencode_config" >"$temp_file" && mv "$temp_file" "$opencode_config"
|
|
464
|
+
print_success "Added $plugin_name plugin to OpenCode config"
|
|
465
|
+
fi
|
|
466
|
+
else
|
|
467
|
+
# Create plugin array with the plugin
|
|
468
|
+
local temp_file
|
|
469
|
+
temp_file=$(mktemp)
|
|
470
|
+
trap 'rm -f "${temp_file:-}"' RETURN
|
|
471
|
+
jq --arg p "$plugin_spec" '. + {plugin: [$p]}' "$opencode_config" >"$temp_file" && mv "$temp_file" "$opencode_config"
|
|
472
|
+
print_success "Created plugin array with $plugin_name"
|
|
473
|
+
fi
|
|
474
|
+
|
|
475
|
+
return 0
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
setup_opencode_plugins() {
|
|
479
|
+
print_info "Setting up OpenCode plugins..."
|
|
480
|
+
|
|
481
|
+
# Check if OpenCode is installed
|
|
482
|
+
if ! command -v opencode &>/dev/null; then
|
|
483
|
+
print_warning "OpenCode not found - plugin setup skipped"
|
|
484
|
+
print_info "Install OpenCode first: https://opencode.ai"
|
|
485
|
+
return 0
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
# Check if config exists
|
|
489
|
+
local opencode_config
|
|
490
|
+
if ! opencode_config=$(find_opencode_config); then
|
|
491
|
+
print_warning "OpenCode config not found - plugin setup skipped"
|
|
492
|
+
return 0
|
|
493
|
+
fi
|
|
494
|
+
|
|
495
|
+
# Check if jq is available
|
|
496
|
+
if ! command -v jq &>/dev/null; then
|
|
497
|
+
print_warning "jq not found - cannot update OpenCode config"
|
|
498
|
+
return 0
|
|
499
|
+
fi
|
|
500
|
+
|
|
501
|
+
# Setup aidevops compaction plugin (local file plugin)
|
|
502
|
+
local aidevops_plugin_path="$HOME/.aidevops/agents/plugins/opencode-aidevops/index.mjs"
|
|
503
|
+
if [[ -f "$aidevops_plugin_path" ]]; then
|
|
504
|
+
print_info "Setting up aidevops compaction plugin..."
|
|
505
|
+
add_opencode_plugin "file://$HOME/.aidevops" "file://${aidevops_plugin_path}" "$opencode_config"
|
|
506
|
+
print_success "aidevops compaction plugin registered (preserves context across compaction)"
|
|
507
|
+
fi
|
|
508
|
+
|
|
509
|
+
# Note: opencode-anthropic-auth is built into OpenCode v1.1.36+
|
|
510
|
+
# Adding it as an external plugin causes TypeError due to double-loading.
|
|
511
|
+
# Removed in v2.90.0 - see PR #230.
|
|
512
|
+
|
|
513
|
+
print_info "After setup, authenticate with: opencode auth login"
|
|
514
|
+
print_info " • For Claude OAuth: Select 'Anthropic' → 'Claude Pro/Max' (built-in)"
|
|
515
|
+
|
|
516
|
+
return 0
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
setup_seo_mcps() {
|
|
520
|
+
print_info "Setting up SEO integrations..."
|
|
521
|
+
|
|
522
|
+
# SEO services use curl-based subagents (no MCP needed)
|
|
523
|
+
# Subagents: serper.md, dataforseo.md, ahrefs.md, google-search-console.md
|
|
524
|
+
print_info "SEO uses curl-based subagents (zero context cost until invoked)"
|
|
525
|
+
|
|
526
|
+
# Check if credentials are configured
|
|
527
|
+
if [[ -f "$HOME/.config/aidevops/credentials.sh" ]]; then
|
|
528
|
+
# shellcheck source=/dev/null
|
|
529
|
+
source "$HOME/.config/aidevops/credentials.sh"
|
|
530
|
+
|
|
531
|
+
if [[ -n "$DATAFORSEO_USERNAME" ]]; then
|
|
532
|
+
print_success "DataForSEO credentials configured"
|
|
533
|
+
else
|
|
534
|
+
print_info "DataForSEO: set DATAFORSEO_USERNAME and DATAFORSEO_PASSWORD in credentials.sh"
|
|
535
|
+
fi
|
|
536
|
+
|
|
537
|
+
if [[ -n "$SERPER_API_KEY" ]]; then
|
|
538
|
+
print_success "Serper API key configured"
|
|
539
|
+
else
|
|
540
|
+
print_info "Serper: set SERPER_API_KEY in credentials.sh"
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
if [[ -n "$AHREFS_API_KEY" ]]; then
|
|
544
|
+
print_success "Ahrefs API key configured"
|
|
545
|
+
else
|
|
546
|
+
print_info "Ahrefs: set AHREFS_API_KEY in credentials.sh"
|
|
547
|
+
fi
|
|
548
|
+
else
|
|
549
|
+
print_info "Configure SEO API credentials in ~/.config/aidevops/credentials.sh"
|
|
550
|
+
fi
|
|
551
|
+
|
|
552
|
+
# GSC uses MCP (OAuth2 complexity warrants it)
|
|
553
|
+
local gsc_creds="$HOME/.config/aidevops/gsc-credentials.json"
|
|
554
|
+
if [[ -f "$gsc_creds" ]]; then
|
|
555
|
+
print_success "Google Search Console credentials configured"
|
|
556
|
+
else
|
|
557
|
+
print_info "GSC: Create service account JSON at $gsc_creds"
|
|
558
|
+
print_info " See: ~/.aidevops/agents/seo/google-search-console.md"
|
|
559
|
+
fi
|
|
560
|
+
|
|
561
|
+
print_info "SEO documentation: ~/.aidevops/agents/seo/"
|
|
562
|
+
return 0
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
setup_google_analytics_mcp() {
|
|
566
|
+
print_info "Setting up Google Analytics MCP..."
|
|
567
|
+
|
|
568
|
+
local gsc_creds="$HOME/.config/aidevops/gsc-credentials.json"
|
|
569
|
+
|
|
570
|
+
# Check if opencode.json exists
|
|
571
|
+
local opencode_config
|
|
572
|
+
if ! opencode_config=$(find_opencode_config); then
|
|
573
|
+
print_warning "OpenCode config not found - skipping Google Analytics MCP"
|
|
574
|
+
return 0
|
|
575
|
+
fi
|
|
576
|
+
|
|
577
|
+
# Check if jq is available
|
|
578
|
+
if ! command -v jq &>/dev/null; then
|
|
579
|
+
print_warning "jq not found - cannot add Google Analytics MCP to config"
|
|
580
|
+
print_info "Install jq and re-run setup, or manually add the MCP config"
|
|
581
|
+
return 0
|
|
582
|
+
fi
|
|
583
|
+
|
|
584
|
+
# Check if pipx is available
|
|
585
|
+
if ! command -v pipx &>/dev/null; then
|
|
586
|
+
print_warning "pipx not found - Google Analytics MCP requires pipx"
|
|
587
|
+
print_info "Install pipx: brew install pipx (macOS) or pip install pipx"
|
|
588
|
+
print_info "Then re-run setup to add Google Analytics MCP"
|
|
589
|
+
return 0
|
|
590
|
+
fi
|
|
591
|
+
|
|
592
|
+
# Auto-detect credentials from shared GSC service account
|
|
593
|
+
local creds_path=""
|
|
594
|
+
local project_id=""
|
|
595
|
+
local enable_mcp="false"
|
|
596
|
+
|
|
597
|
+
if [[ -f "$gsc_creds" ]]; then
|
|
598
|
+
creds_path="$gsc_creds"
|
|
599
|
+
# Extract project_id from service account JSON
|
|
600
|
+
project_id=$(jq -r '.project_id // empty' "$gsc_creds" 2>/dev/null)
|
|
601
|
+
if [[ -n "$project_id" ]]; then
|
|
602
|
+
enable_mcp="true"
|
|
603
|
+
print_success "Found GSC credentials - sharing with Google Analytics MCP"
|
|
604
|
+
print_info "Project: $project_id"
|
|
605
|
+
fi
|
|
606
|
+
fi
|
|
607
|
+
|
|
608
|
+
# Check if google-analytics-mcp already exists in config
|
|
609
|
+
if jq -e '.mcp["google-analytics-mcp"]' "$opencode_config" >/dev/null 2>&1; then
|
|
610
|
+
# Update existing entry if we have credentials now
|
|
611
|
+
if [[ "$enable_mcp" == "true" ]]; then
|
|
612
|
+
local tmp_config
|
|
613
|
+
tmp_config=$(mktemp)
|
|
614
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
615
|
+
if jq --arg creds "$creds_path" --arg proj "$project_id" \
|
|
616
|
+
'.mcp["google-analytics-mcp"].environment.GOOGLE_APPLICATION_CREDENTIALS = $creds |
|
|
617
|
+
.mcp["google-analytics-mcp"].environment.GOOGLE_PROJECT_ID = $proj |
|
|
618
|
+
.mcp["google-analytics-mcp"].enabled = true' \
|
|
619
|
+
"$opencode_config" >"$tmp_config" 2>/dev/null; then
|
|
620
|
+
mv "$tmp_config" "$opencode_config"
|
|
621
|
+
print_success "Updated Google Analytics MCP with GSC credentials (enabled)"
|
|
622
|
+
else
|
|
623
|
+
rm -f "$tmp_config"
|
|
624
|
+
print_warning "Failed to update Google Analytics MCP config"
|
|
625
|
+
fi
|
|
626
|
+
else
|
|
627
|
+
print_info "Google Analytics MCP already configured in OpenCode"
|
|
628
|
+
fi
|
|
629
|
+
return 0
|
|
630
|
+
fi
|
|
631
|
+
|
|
632
|
+
# Add google-analytics-mcp to opencode.json
|
|
633
|
+
local tmp_config
|
|
634
|
+
tmp_config=$(mktemp)
|
|
635
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
636
|
+
|
|
637
|
+
if jq --arg creds "$creds_path" --arg proj "$project_id" --argjson enabled "$enable_mcp" \
|
|
638
|
+
'.mcp["google-analytics-mcp"] = {
|
|
639
|
+
"type": "local",
|
|
640
|
+
"command": ["analytics-mcp"],
|
|
641
|
+
"environment": {
|
|
642
|
+
"GOOGLE_APPLICATION_CREDENTIALS": $creds,
|
|
643
|
+
"GOOGLE_PROJECT_ID": $proj
|
|
644
|
+
},
|
|
645
|
+
"enabled": $enabled
|
|
646
|
+
}' "$opencode_config" >"$tmp_config" 2>/dev/null; then
|
|
647
|
+
mv "$tmp_config" "$opencode_config"
|
|
648
|
+
if [[ "$enable_mcp" == "true" ]]; then
|
|
649
|
+
print_success "Added Google Analytics MCP to OpenCode (enabled with GSC credentials)"
|
|
650
|
+
else
|
|
651
|
+
print_success "Added Google Analytics MCP to OpenCode (disabled - no credentials found)"
|
|
652
|
+
print_info "To enable: Create service account JSON at $gsc_creds"
|
|
653
|
+
fi
|
|
654
|
+
print_info "Or use the google-analytics subagent which enables it automatically"
|
|
655
|
+
else
|
|
656
|
+
rm -f "$tmp_config"
|
|
657
|
+
print_warning "Failed to add Google Analytics MCP to config"
|
|
658
|
+
fi
|
|
659
|
+
|
|
660
|
+
# Show setup instructions
|
|
661
|
+
print_info "Google Analytics MCP setup:"
|
|
662
|
+
print_info " 1. Enable Google Analytics Admin & Data APIs in Google Cloud Console"
|
|
663
|
+
print_info " 2. Configure ADC: gcloud auth application-default login --scopes https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform"
|
|
664
|
+
print_info " 3. Update GOOGLE_APPLICATION_CREDENTIALS path in opencode.json"
|
|
665
|
+
print_info " 4. Set GOOGLE_PROJECT_ID in opencode.json"
|
|
666
|
+
print_info "Documentation: ~/.aidevops/agents/services/analytics/google-analytics.md"
|
|
667
|
+
|
|
668
|
+
return 0
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
setup_quickfile_mcp() {
|
|
672
|
+
print_info "Setting up QuickFile MCP server..."
|
|
673
|
+
|
|
674
|
+
local quickfile_dir="$HOME/Git/quickfile-mcp"
|
|
675
|
+
local credentials_dir="$HOME/.config/.quickfile-mcp"
|
|
676
|
+
local credentials_file="$credentials_dir/credentials.json"
|
|
677
|
+
|
|
678
|
+
# Check if Node.js is available
|
|
679
|
+
if ! command -v node &>/dev/null; then
|
|
680
|
+
print_warning "Node.js not found - QuickFile MCP setup skipped"
|
|
681
|
+
print_info "Install Node.js 18+ to enable QuickFile MCP"
|
|
682
|
+
return 0
|
|
683
|
+
fi
|
|
684
|
+
|
|
685
|
+
# Check if already cloned and built
|
|
686
|
+
if [[ -f "$quickfile_dir/dist/index.js" ]]; then
|
|
687
|
+
print_success "QuickFile MCP already installed at $quickfile_dir"
|
|
688
|
+
else
|
|
689
|
+
print_info "QuickFile MCP provides AI access to UK accounting (invoices, clients, reports)"
|
|
690
|
+
read -r -p "Clone and build QuickFile MCP server? [Y/n]: " install_qf
|
|
691
|
+
|
|
692
|
+
if [[ "$install_qf" =~ ^[Yy]?$ ]]; then
|
|
693
|
+
if [[ ! -d "$quickfile_dir" ]]; then
|
|
694
|
+
if run_with_spinner "Cloning quickfile-mcp" git clone https://github.com/marcusquinn/quickfile-mcp.git "$quickfile_dir"; then
|
|
695
|
+
print_success "Cloned quickfile-mcp"
|
|
696
|
+
else
|
|
697
|
+
print_warning "Failed to clone quickfile-mcp"
|
|
698
|
+
return 0
|
|
699
|
+
fi
|
|
700
|
+
fi
|
|
701
|
+
|
|
702
|
+
if run_with_spinner "Installing dependencies" npm install --prefix "$quickfile_dir"; then
|
|
703
|
+
if run_with_spinner "Building QuickFile MCP" npm run build --prefix "$quickfile_dir"; then
|
|
704
|
+
print_success "QuickFile MCP built successfully"
|
|
705
|
+
else
|
|
706
|
+
print_warning "Build failed - try manually: cd $quickfile_dir && npm run build"
|
|
707
|
+
return 0
|
|
708
|
+
fi
|
|
709
|
+
else
|
|
710
|
+
print_warning "npm install failed - try manually: cd $quickfile_dir && npm install"
|
|
711
|
+
return 0
|
|
712
|
+
fi
|
|
713
|
+
else
|
|
714
|
+
print_info "Skipped QuickFile MCP installation"
|
|
715
|
+
print_info "Install later: git clone https://github.com/marcusquinn/quickfile-mcp.git ~/Git/quickfile-mcp"
|
|
716
|
+
return 0
|
|
717
|
+
fi
|
|
718
|
+
fi
|
|
719
|
+
|
|
720
|
+
# Check credentials
|
|
721
|
+
if [[ -f "$credentials_file" ]]; then
|
|
722
|
+
print_success "QuickFile credentials configured at $credentials_file"
|
|
723
|
+
else
|
|
724
|
+
print_info "QuickFile credentials not found"
|
|
725
|
+
print_info "Create credentials:"
|
|
726
|
+
print_info " mkdir -p $credentials_dir && chmod 700 $credentials_dir"
|
|
727
|
+
print_info " Create $credentials_file with:"
|
|
728
|
+
print_info " accountNumber: from QuickFile dashboard (top-right)"
|
|
729
|
+
print_info " apiKey: Account Settings > 3rd Party Integrations > API Key"
|
|
730
|
+
print_info " applicationId: Account Settings > Create a QuickFile App"
|
|
731
|
+
fi
|
|
732
|
+
|
|
733
|
+
# Update OpenCode config if available
|
|
734
|
+
local opencode_config
|
|
735
|
+
if opencode_config=$(find_opencode_config); then
|
|
736
|
+
local quickfile_entry
|
|
737
|
+
quickfile_entry=$(jq -r '.mcp.quickfile // empty' "$opencode_config" 2>/dev/null)
|
|
738
|
+
|
|
739
|
+
if [[ -z "$quickfile_entry" ]]; then
|
|
740
|
+
print_info "Adding QuickFile MCP to OpenCode config..."
|
|
741
|
+
local node_path
|
|
742
|
+
node_path=$(resolve_mcp_binary_path "node")
|
|
743
|
+
[[ -z "$node_path" ]] && node_path="node"
|
|
744
|
+
|
|
745
|
+
local tmp_config
|
|
746
|
+
tmp_config=$(mktemp)
|
|
747
|
+
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
748
|
+
|
|
749
|
+
if jq --arg np "$node_path" --arg dp "$quickfile_dir/dist/index.js" \
|
|
750
|
+
'.mcp.quickfile = {"type": "local", "command": [$np, $dp], "enabled": true}' \
|
|
751
|
+
"$opencode_config" >"$tmp_config" 2>/dev/null; then
|
|
752
|
+
create_backup_with_rotation "$opencode_config" "opencode"
|
|
753
|
+
mv "$tmp_config" "$opencode_config"
|
|
754
|
+
print_success "QuickFile MCP added to OpenCode config"
|
|
755
|
+
else
|
|
756
|
+
rm -f "$tmp_config"
|
|
757
|
+
print_warning "Failed to update OpenCode config - add manually"
|
|
758
|
+
fi
|
|
759
|
+
else
|
|
760
|
+
print_success "QuickFile MCP already in OpenCode config"
|
|
761
|
+
fi
|
|
762
|
+
fi
|
|
763
|
+
|
|
764
|
+
print_info "Documentation: ~/.aidevops/agents/services/accounting/quickfile.md"
|
|
765
|
+
return 0
|
|
766
|
+
}
|