#!/usr/bin/env bash set -euo pipefail MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" TOOLS_DIR="$MOSAIC_HOME/tools/excalidraw" MODE="apply" SCOPE="user" err() { echo "[mosaic-excalidraw] ERROR: $*" >&2; } log() { echo "[mosaic-excalidraw] $*"; } while [[ $# -gt 0 ]]; do case "$1" in --check) MODE="check"; shift ;; --scope) if [[ $# -lt 2 ]]; then err "--scope requires a value: user|local" exit 2 fi SCOPE="$2" shift 2 ;; *) err "Unknown argument: $1" exit 2 ;; esac done require_binary() { local name="$1" if ! command -v "$name" >/dev/null 2>&1; then err "Required binary missing: $name" return 1 fi } check_software() { require_binary node require_binary npm } check_tool_dir() { [[ -d "$TOOLS_DIR" ]] || { err "Tool dir not found: $TOOLS_DIR"; return 1; } [[ -f "$TOOLS_DIR/package.json" ]] || { err "package.json not found in $TOOLS_DIR"; return 1; } [[ -f "$TOOLS_DIR/launch.sh" ]] || { err "launch.sh not found in $TOOLS_DIR"; return 1; } } check_npm_deps() { [[ -d "$TOOLS_DIR/node_modules/@modelcontextprotocol" ]] || return 1 [[ -d "$TOOLS_DIR/node_modules/@excalidraw" ]] || return 1 [[ -d "$TOOLS_DIR/node_modules/jsdom" ]] || return 1 } install_npm_deps() { if check_npm_deps; then return 0 fi log "Installing npm deps in $TOOLS_DIR..." (cd "$TOOLS_DIR" && npm install --silent) || { err "npm install failed in $TOOLS_DIR" return 1 } } check_claude_config() { python3 - <<'PY' import json from pathlib import Path p = Path.home() / ".claude.json" if not p.exists(): raise SystemExit(1) try: data = json.loads(p.read_text(encoding="utf-8")) except Exception: raise SystemExit(1) mcp = data.get("mcpServers") if not isinstance(mcp, dict): raise SystemExit(1) entry = mcp.get("excalidraw") if not isinstance(entry, dict): raise SystemExit(1) cmd = entry.get("command", "") if not cmd.endswith("launch.sh"): raise SystemExit(1) PY } apply_claude_config() { require_binary claude local launch_sh="$TOOLS_DIR/launch.sh" claude mcp add --scope user excalidraw -- "$launch_sh" } # ── Check mode ──────────────────────────────────────────────────────────────── if [[ "$MODE" == "check" ]]; then check_software check_tool_dir if ! check_npm_deps; then err "npm deps not installed in $TOOLS_DIR (run without --check to install)" exit 1 fi if ! check_claude_config; then err "excalidraw not registered in ~/.claude.json" exit 1 fi log "excalidraw MCP is configured and available" exit 0 fi # ── Apply mode ──────────────────────────────────────────────────────────────── check_software check_tool_dir install_npm_deps apply_claude_config log "excalidraw MCP configured (scope: $SCOPE)"