- AGENTS.md: design principles, enforcement hierarchy, deferred loading - agents/: brainstorm, build, orchestrator, research (auto-discovered by MCP server) - skills/: research methodology (auto-discovered by MCP server) - hooks/: pre-tool-use, post-tool-use (BFF block removed), session-start, stop, pre-compact, user-prompt-submit - frameworks/: opencode/plugin.ts (resolves hooks via import.meta.url — works as project-local or global plugin), github/hooks.json - mcp/index.ts: auto-discovers agents/*.md and skills/*.md from frontmatter (replaces hand-maintained registry); server renamed all-agents - docs/: agent-infrastructure.md (generalized), research docs (7 files), ai_architectures.md, llama-server-cuda-wsl2.md - install.sh: idempotent setup — Copilot global hooks, OpenCode global plugin + AGENTS.md + MCP entry, VS Code global MCP config
130 lines
6.1 KiB
Bash
Executable File
130 lines
6.1 KiB
Bash
Executable File
#!/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
|
||
|
||
# ── 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'
|