281 lines
7.7 KiB
Bash
281 lines
7.7 KiB
Bash
#!/usr/bin/env bash
|
|
# Understand-Anything installer (macOS / Linux)
|
|
#
|
|
# Usage:
|
|
# ./install.sh Prompt for platform
|
|
# ./install.sh <platform> Install for <platform>
|
|
# ./install.sh --update Pull latest changes
|
|
# ./install.sh --uninstall <plat> Remove links for <plat>
|
|
# ./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 <<EOF
|
|
gemini|$HOME/.agents/skills|per-skill
|
|
codex|$HOME/.agents/skills|per-skill
|
|
opencode|$HOME/.agents/skills|per-skill
|
|
pi|$HOME/.agents/skills|per-skill
|
|
openclaw|$HOME/.openclaw/skills|folder
|
|
antigravity|$HOME/.gemini/antigravity/skills|folder
|
|
vibe|$HOME/.vibe/skills|per-skill
|
|
vscode|$HOME/.copilot/skills|per-skill
|
|
hermes|$HOME/.hermes/skills|folder
|
|
cline|$HOME/.cline/skills|folder
|
|
kimi|$HOME/.kimi/skills|folder
|
|
trae|$HOME/.trae/skills|per-skill
|
|
EOF
|
|
}
|
|
|
|
platform_ids() { platforms_table | cut -d'|' -f1; }
|
|
|
|
resolve_platform() {
|
|
local id="$1"
|
|
local row
|
|
row="$(platforms_table | awk -F'|' -v id="$id" '$1==id {print; exit}')"
|
|
if [[ -z "$row" ]]; then
|
|
printf 'Unknown platform: %s\n' "$id" >&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/tty; } 2>/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 <<USAGE
|
|
Understand-Anything installer
|
|
|
|
Usage:
|
|
install.sh [<platform>] Install for <platform> (or prompt if omitted)
|
|
install.sh --update Pull latest changes (skills update through symlinks)
|
|
install.sh --uninstall <platform> Remove links for <platform>
|
|
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 "$@"
|