#!/usr/bin/env bash # Understand-Anything installer (macOS / Linux) # # Usage: # ./install.sh Prompt for platform # ./install.sh Install for # ./install.sh --update Pull latest changes # ./install.sh --uninstall Remove links for # ./install.sh --help # # Curl-pipe usage: # curl -fsSL https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/install.sh | bash # curl -fsSL https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/install.sh | bash -s codex # # Environment: # UA_REPO_URL Override clone URL (default: official GitHub repo) # UA_DIR Override clone destination (default: $HOME/.understand-anything/repo) set -euo pipefail REPO_URL="${UA_REPO_URL:-https://github.com/Lum1104/Understand-Anything.git}" REPO_DIR="${UA_DIR:-$HOME/.understand-anything/repo}" PLUGIN_LINK="$HOME/.understand-anything-plugin" # Platform table — id|skills-target-dir|style # style "per-skill": one symlink per skill into the target dir # style "folder": one symlink for the whole skills/ dir into the target, # named "understand-anything" platforms_table() { cat <&2 printf 'Supported: %s\n' "$(platform_ids | tr '\n' ' ')" >&2 exit 1 fi printf '%s\n' "$row" } prompt_platform() { local ids=() while IFS= read -r id; do ids+=("$id"); done < <(platform_ids) printf 'Which platform are you installing for?\n' >&2 local i=1 for id in "${ids[@]}"; do printf ' %d) %s\n' "$i" "$id" >&2 i=$((i+1)) done printf 'Choose [1-%d]: ' "${#ids[@]}" >&2 local choice="" if { exec 3/dev/null; then read -r choice <&3 || true exec 3<&- else read -r choice || true fi if [[ -z "$choice" ]]; then printf '\nNo input received. Pass the platform as an argument instead, e.g.:\n' >&2 printf ' install.sh codex\n' >&2 exit 1 fi if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#ids[@]} )); then printf 'Invalid choice: %s\n' "$choice" >&2 exit 1 fi printf '%s\n' "${ids[$((choice-1))]}" } clone_or_update() { if [[ -d "$REPO_DIR/.git" ]]; then printf -- '→ Updating existing checkout at %s\n' "$REPO_DIR" git -C "$REPO_DIR" pull --ff-only else printf -- '→ Cloning %s → %s\n' "$REPO_URL" "$REPO_DIR" mkdir -p "$(dirname "$REPO_DIR")" git clone "$REPO_URL" "$REPO_DIR" fi } skills_root() { printf '%s\n' "$REPO_DIR/understand-anything-plugin/skills"; } list_skills() { local root root="$(skills_root)" if [[ ! -d "$root" ]]; then printf 'Skills directory not found: %s\n' "$root" >&2 exit 1 fi local d for d in "$root"/*/; do [[ -d "$d" ]] || continue basename "$d" done } link_skills() { local target="$1" style="$2" local root root="$(skills_root)" mkdir -p "$target" case "$style" in per-skill) local skill while IFS= read -r skill; do ln -sfn "$root/$skill" "$target/$skill" printf ' ✓ %s → %s\n' "$target/$skill" "$root/$skill" done < <(list_skills) ;; folder) ln -sfn "$root" "$target/understand-anything" printf ' ✓ %s → %s\n' "$target/understand-anything" "$root" ;; *) printf 'Unknown style: %s\n' "$style" >&2 exit 1 ;; esac } unlink_skills() { local target="$1" style="$2" [[ -d "$target" ]] || return 0 case "$style" in per-skill) if [[ -d "$(skills_root)" ]]; then local skill while IFS= read -r skill; do [[ -L "$target/$skill" ]] && rm -f "$target/$skill" done < <(list_skills) else # Checkout is gone — scan the target dir for stale links pointing into # our plugin tree so we can still clean up. local link resolved for link in "$target"/*; do [[ -L "$link" ]] || continue resolved="$(readlink "$link" 2>/dev/null || true)" [[ "$resolved" == *"/understand-anything-plugin/skills/"* ]] || continue rm -f "$link" done fi ;; folder) [[ -L "$target/understand-anything" ]] && rm -f "$target/understand-anything" ;; esac } link_plugin_root() { if [[ -L "$PLUGIN_LINK" || -e "$PLUGIN_LINK" ]]; then printf ' • %s already exists, leaving as-is\n' "$PLUGIN_LINK" else ln -s "$REPO_DIR/understand-anything-plugin" "$PLUGIN_LINK" printf ' ✓ %s → %s\n' "$PLUGIN_LINK" "$REPO_DIR/understand-anything-plugin" fi } cmd_install() { local id="$1" local row target style row="$(resolve_platform "$id")" target="$(printf '%s\n' "$row" | cut -d'|' -f2)" style="$(printf '%s\n' "$row" | cut -d'|' -f3)" clone_or_update printf -- '→ Linking skills for %s (%s → %s)\n' "$id" "$style" "$target" link_skills "$target" "$style" printf -- '→ Linking universal plugin root\n' link_plugin_root printf '\n✓ Installed Understand-Anything for %s\n' "$id" printf ' Restart your CLI or IDE to pick up the skills.\n' if [[ "$id" == "vscode" ]]; then printf '\n Tip: VS Code can also auto-discover the plugin by opening this repo\n' printf ' directly (it reads .copilot-plugin/plugin.json), no symlinks needed.\n' fi } cmd_uninstall() { local id="$1" local row target style row="$(resolve_platform "$id")" target="$(printf '%s\n' "$row" | cut -d'|' -f2)" style="$(printf '%s\n' "$row" | cut -d'|' -f3)" printf -- '→ Removing skill links for %s\n' "$id" unlink_skills "$target" "$style" if [[ -L "$PLUGIN_LINK" ]]; then rm -f "$PLUGIN_LINK" printf ' ✓ removed %s\n' "$PLUGIN_LINK" fi if [[ -d "$REPO_DIR" ]]; then printf '\nThe checkout at %s was kept (other platforms may still use it).\n' "$REPO_DIR" printf 'To remove it: rm -rf "%s"\n' "$REPO_DIR" fi } cmd_update() { if [[ ! -d "$REPO_DIR/.git" ]]; then printf 'No installation found at %s. Run install first.\n' "$REPO_DIR" >&2 exit 1 fi git -C "$REPO_DIR" pull --ff-only printf '✓ Updated.\n' } usage() { cat <] Install for (or prompt if omitted) install.sh --update Pull latest changes (skills update through symlinks) install.sh --uninstall Remove links for install.sh --help Supported platforms: $(platform_ids | sed 's/^/ - /') Environment: UA_REPO_URL Override clone URL (default: official repo) UA_DIR Override clone destination (default: \$HOME/.understand-anything/repo) USAGE } main() { case "${1:-}" in -h|--help) usage ;; --update) cmd_update ;; --uninstall) shift if [[ -z "${1:-}" ]]; then printf '%s\n' '--uninstall requires a platform argument' >&2 usage >&2 exit 1 fi cmd_uninstall "$1" ;; "") local id id="$(prompt_platform)" cmd_install "$id" ;; -*) printf 'Unknown option: %s\n' "$1" >&2 usage >&2 exit 1 ;; *) cmd_install "$1" ;; esac } main "$@"