dotfiles/.agents/install.sh
Brydon DeWitt 690178da96 fix: add mcp package.json and dep auto-install in install.sh
The MCP server requires @modelcontextprotocol/sdk and zod which are not
globally installed. Added .agents/mcp/package.json and updated install.sh
to run 'npm install --prefix' on first setup.
2026-05-22 16:19:16 -04:00

140 lines
6.6 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# install.sh — Wire ~/dotfiles/.agents/ into global tool configs.
# Idempotent: safe to re-run. Creates dirs, symlinks, and config entries.
# Run once per machine after cloning dotfiles.
set -euo pipefail
DOTFILES_AGENTS="$(cd "$(dirname "$0")" && pwd)"
log() { printf '\033[0;32m✓\033[0m %s\n' "$1"; }
warn() { printf '\033[0;33m⚠\033[0m %s\n' "$1"; }
skip() { printf '\033[0;34m\033[0m %s\n' "$1"; }
# ── 1. Copilot global hooks ──────────────────────────────────────────────────
COPILOT_HOOKS_DIR="$HOME/.copilot/hooks"
COPILOT_HOOK_TARGET="$DOTFILES_AGENTS/frameworks/github/hooks.json"
COPILOT_HOOK_LINK="$COPILOT_HOOKS_DIR/agent-support.json"
mkdir -p "$COPILOT_HOOKS_DIR"
if [[ -L "$COPILOT_HOOK_LINK" && "$(readlink "$COPILOT_HOOK_LINK")" == "$COPILOT_HOOK_TARGET" ]]; then
skip "Copilot hook symlink already set: $COPILOT_HOOK_LINK"
else
ln -sf "$COPILOT_HOOK_TARGET" "$COPILOT_HOOK_LINK"
log "Copilot hook symlink: $COPILOT_HOOK_LINK$COPILOT_HOOK_TARGET"
fi
# ── 2. OpenCode global plugin ────────────────────────────────────────────────
OC_PLUGINS_DIR="$HOME/.config/opencode/plugins"
OC_PLUGIN_TARGET="$DOTFILES_AGENTS/frameworks/opencode/plugin.ts"
OC_PLUGIN_LINK="$OC_PLUGINS_DIR/agent-support.ts"
mkdir -p "$OC_PLUGINS_DIR"
if [[ -L "$OC_PLUGIN_LINK" && "$(readlink "$OC_PLUGIN_LINK")" == "$OC_PLUGIN_TARGET" ]]; then
skip "OpenCode plugin symlink already set: $OC_PLUGIN_LINK"
else
ln -sf "$OC_PLUGIN_TARGET" "$OC_PLUGIN_LINK"
log "OpenCode plugin symlink: $OC_PLUGIN_LINK$OC_PLUGIN_TARGET"
fi
# ── 3. OpenCode global AGENTS.md ────────────────────────────────────────────
OC_AGENTS_TARGET="$DOTFILES_AGENTS/AGENTS.md"
OC_AGENTS_LINK="$HOME/.config/opencode/AGENTS.md"
if [[ -L "$OC_AGENTS_LINK" && "$(readlink "$OC_AGENTS_LINK")" == "$OC_AGENTS_TARGET" ]]; then
skip "OpenCode AGENTS.md symlink already set: $OC_AGENTS_LINK"
else
ln -sf "$OC_AGENTS_TARGET" "$OC_AGENTS_LINK"
log "OpenCode AGENTS.md symlink: $OC_AGENTS_LINK$OC_AGENTS_TARGET"
fi
# ── 4. OpenCode global MCP entry ────────────────────────────────────────────
OC_CONFIG="$HOME/.config/opencode/opencode.json"
MCP_KEY="all-agents"
MCP_CMD="[\"node\", \"--experimental-strip-types\", \"$DOTFILES_AGENTS/mcp/index.ts\"]"
if [[ ! -f "$OC_CONFIG" ]]; then
warn "No OpenCode config at $OC_CONFIG — creating minimal config with MCP entry."
printf '{\n "$schema": "https://opencode.ai/config.json",\n "mcp": {\n "%s": {\n "type": "local",\n "command": %s\n }\n }\n}\n' "$MCP_KEY" "$MCP_CMD" > "$OC_CONFIG"
log "Created $OC_CONFIG with all-agents MCP entry"
elif node -e "const c=JSON.parse(require('fs').readFileSync('$OC_CONFIG','utf8')); process.exit(c.mcp && c.mcp['$MCP_KEY'] ? 0 : 1)" 2>/dev/null; then
skip "OpenCode MCP entry '$MCP_KEY' already present in $OC_CONFIG"
else
# Merge the MCP entry using node — jq may not be available everywhere
node -e "
const fs = require('fs');
const path = '$OC_CONFIG';
const config = JSON.parse(fs.readFileSync(path, 'utf8'));
config.mcp = config.mcp || {};
config.mcp['$MCP_KEY'] = { type: 'local', command: $MCP_CMD };
fs.writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
console.log('Merged all-agents MCP entry into ' + path);
"
log "OpenCode MCP entry merged: $OC_CONFIG"
fi
# ── 5. VS Code global MCP ────────────────────────────────────────────────────
# Primary remote/server path; falls back to local if running VS Code locally.
VSCODE_MCP_PATHS=(
"$HOME/.vscode-server/data/User/mcp.json"
"$HOME/.vscode/data/User/mcp.json"
"$HOME/Library/Application Support/Code/User/mcp.json"
)
for VSCODE_MCP in "${VSCODE_MCP_PATHS[@]}"; do
if [[ -d "$(dirname "$VSCODE_MCP")" ]]; then
MCP_SERVER_CMD="node"
MCP_SERVER_ARGS="[\"--experimental-strip-types\", \"$DOTFILES_AGENTS/mcp/index.ts\"]"
if [[ ! -f "$VSCODE_MCP" ]]; then
printf '{\n "servers": {\n "%s": {\n "type": "stdio",\n "command": "%s",\n "args": %s\n }\n }\n}\n' \
"$MCP_KEY" "$MCP_SERVER_CMD" "$MCP_SERVER_ARGS" > "$VSCODE_MCP"
log "Created VS Code global MCP config: $VSCODE_MCP"
elif node -e "const c=JSON.parse(require('fs').readFileSync('$VSCODE_MCP','utf8')); process.exit(c.servers && c.servers['$MCP_KEY'] ? 0 : 1)" 2>/dev/null; then
skip "VS Code MCP entry '$MCP_KEY' already present in $VSCODE_MCP"
else
node -e "
const fs = require('fs');
const path = '$VSCODE_MCP';
const config = JSON.parse(fs.readFileSync(path, 'utf8'));
config.servers = config.servers || {};
config.servers['$MCP_KEY'] = {
type: 'stdio',
command: '$MCP_SERVER_CMD',
args: $MCP_SERVER_ARGS
};
fs.writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
"
log "VS Code MCP entry merged: $VSCODE_MCP"
fi
break
fi
done
# ── 6. VS Code global prompts dir ───────────────────────────────────────────
for VSCODE_PROMPTS_DIR in \
"$HOME/.vscode-server/data/User/prompts" \
"$HOME/.vscode/data/User/prompts"; do
if [[ -d "$(dirname "$(dirname "$VSCODE_PROMPTS_DIR")")" ]]; then
mkdir -p "$VSCODE_PROMPTS_DIR"
log "VS Code prompts dir ensured: $VSCODE_PROMPTS_DIR"
break
fi
done
# ── 7. MCP server dependencies ───────────────────────────────────────────────
MCP_DIR="$DOTFILES_AGENTS/mcp"
if [[ ! -d "$MCP_DIR/node_modules/@modelcontextprotocol" ]]; then
log "Installing MCP server dependencies (npm install in $MCP_DIR)..."
npm install --prefix "$MCP_DIR" --silent
log "MCP server dependencies installed"
else
skip "MCP server node_modules already present"
fi
# ── Done ─────────────────────────────────────────────────────────────────────
printf '\n\033[0;32minstall.sh complete.\033[0m\n'
printf 'Next steps:\n'
printf ' 1. Restart OpenCode to pick up the new global plugin.\n'
printf ' 2. Reload VS Code / reconnect to reload MCP servers.\n'
printf ' 3. Smoke test: /research slash prompt fires; a denied terminal command is blocked.\n'