#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: install.sh [--hub URL] [--bin DIR] [--no-pair] Installs the Watch Agent Assistant channel plugin, writes a `claude-watchagent` launcher and pairs your Apple Watch in one go. From a checkout it uses the plugin next to this script. Otherwise it downloads the plugin from the website: curl -fsSL https://watchagentassistant.alasolutions.co.uk/install.sh | bash --hub URL use a self-hosted or local hub (e.g. http://localhost:8787) --bin DIR where to put the launcher (default ~/.local/bin) --no-pair install only; pair later with: claude-watchagent-pair EOF } hub="" bin="$HOME/.local/bin" pair=1 while [ $# -gt 0 ]; do case "$1" in --hub) hub="${2:?--hub needs a URL}"; shift 2 ;; --bin) bin="${2:?--bin needs a directory}"; shift 2 ;; --no-pair) pair=0; shift ;; -h|--help) usage; exit 0 ;; *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; esac done site="${WATCHPILOT_SITE:-https://watchagentassistant.alasolutions.co.uk}" need() { command -v "$1" >/dev/null 2>&1 && return echo "Missing $1. $2" >&2 exit 1 } need bun "Install it with: curl -fsSL https://bun.sh/install | bash" need claude "Install Claude Code first: https://claude.com/claude-code" # Piped through bash there is no script file, so BASH_SOURCE is empty. here="$(dirname "${BASH_SOURCE[0]:-.}")" if [ -f "$here/plugins/watchagent/package.json" ]; then plugin="$(cd "$here/plugins/watchagent" && pwd)" else plugin="${XDG_DATA_HOME:-$HOME/.local/share}/watchagent/plugin" echo "Downloading the Watch Agent Assistant plugin from $site..." tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' EXIT curl -fsSL "$site/download/watchagent-channel.tar.gz" | tar -xzf - -C "$tmp" mkdir -p "$(dirname "$plugin")" rm -rf "$plugin" mv "$tmp/watchagent" "$plugin" echo "Installed the plugin in $plugin" fi echo "Installing plugin dependencies..." (cd "$plugin" && bun install --production --frozen-lockfile --no-summary) env_lines="" if [ -n "$hub" ]; then env_lines="export WATCHPILOT_DEV=1 export WATCHPILOT_HUB_URL=$(printf '%q' "$hub")" fi mkdir -p "$bin" cat >"$bin/claude-watchagent" <"$bin/claude-watchagent-pair" <"$bin/$old" <&2 exec $(printf '%q' "$bin/$new") "\$@" EOF chmod 755 "$bin/$old" echo "Pointed $old at $new" done old_plugin="${XDG_DATA_HOME:-$HOME/.local/share}/watchpilot/plugin" if [ "$old_plugin" != "$plugin" ] && [ -f "$old_plugin/.claude-plugin/plugin.json" ]; then rm -rf "$old_plugin" rmdir "$(dirname "$old_plugin")" 2>/dev/null || true echo "Removed the previous plugin copy in $old_plugin" fi case ":$PATH:" in *":$bin:"*) ;; *) echo "Add $bin to your PATH, e.g.: echo 'export PATH=\"$bin:\$PATH\"' >> ~/.zshrc" ;; esac # Pairing reads codes from the keyboard, which is not stdin under curl | bash. # Read from the terminal's own device rather than /dev/tty: on macOS, Bun # can't poll /dev/tty and readline fails with "EINVAL: invalid argument, kqueue". if [ "$pair" = 1 ]; then echo tty_dev="/dev/$(ps -o tty= -p $$ 2>/dev/null | tr -d ' ')" if [ -t 0 ]; then "$bin/claude-watchagent-pair" elif [ -c "$tty_dev" ] && { : <"$tty_dev"; } 2>/dev/null; then "$bin/claude-watchagent-pair" <"$tty_dev" else echo "No terminal to pair from. Pair later with: $bin/claude-watchagent-pair" fi fi echo echo "Done. Start Claude Code with the channel by running: claude-watchagent" echo "On first launch choose \"I am using this for local development\"."