Files
Zoo Queen 5b90aa7b3e fix(install): update CLI tools, login command, dev install method
- Change `hanzo cloud login` → `hanzo login`
- Fix dev install from `cargo install` to `npm i -g @hanzo/dev`
- Add AI CLI tool detection: aider, vibe, windsurf, amazon-q
- Remove hanzod from CLI tools list (it's a daemon, not CLI)
- Update quick start section with current commands
2026-02-21 11:51:24 -08:00

556 lines
21 KiB
Bash

#!/bin/bash
# hanzo installer
# usage: curl -fsSL hanzo.sh | bash
#
# copyright (c) 2024-2026 hanzo ai inc.
# https://hanzo.ai
set -euo pipefail
# config
HANZO_DIR="${HANZO_INSTALL_DIR:-$HOME/.local/bin}"
HANZO_BUNDLE="${HANZO_BUNDLE:-default}"
HANZO_FORCE="${HANZO_FORCE:-0}"
HANZO_UPGRADE="${HANZO_UPGRADE:-0}"
HANZO_QUIET="${HANZO_QUIET:-0}"
# colors
if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]]; then
R='\033[0;31m' G='\033[0;32m' Y='\033[0;33m' B='\033[0;34m' C='\033[0;36m'
N='\033[0m' BD='\033[1m' DM='\033[2m' HR='\033[38;5;196m'
else
R='' G='' Y='' B='' C='' N='' BD='' DM='' HR=''
fi
# tracking
declare -a INSTALLED=() SKIPPED=() UPGRADED=() FAILED=()
banner() {
[[ "$HANZO_QUIET" == "1" ]] && return
local W=$'\033[38;5;255m' D=$'\033[38;5;238m' L=$'\033[38;5;250m' M=$'\033[38;5;246m' K=$'\033[38;5;242m'
echo -e "${L} ${W}__${N} "
echo -e "${M} / /${W}_${M} ${W}____${M} ${W}_____${M} ${W}____${M} ${W}____${M} "
echo -e "${K} / ${W}__${K} \\/ ${W}__${K} \`/ ${W}__${K} \\/${W}_${K} / / ${W}__${K} \\"
echo -e "${D} / / / / /${W}_${D}/ / / / / / /${W}_${D}/ /${W}_${D}/ /"
echo -e "${D}/${W}_${D}/ /${W}_${D}/\\${W}__${D},${W}_${D}/${W}_${D}/ /${W}_${D}/ /${W}___${D}/\\${W}____${D}/${N}"
echo ""
}
log() { [[ "$HANZO_QUIET" != "1" ]] && echo -e " $1"; }
ok() { echo -e " ${G}✓${N} $1"; }
skip() { echo -e " ${DM}○ $1${N}"; }
warn() { echo -e " ${Y}! $1${N}"; }
fail() { echo -e " ${R}✗ $1${N}"; }
die() { fail "$1"; exit 1; }
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
--bundle|-b) HANZO_BUNDLE="$2"; shift 2 ;;
--dir|-d) HANZO_DIR="$2"; shift 2 ;;
--force|-f) HANZO_FORCE=1; shift ;;
--upgrade|-u) HANZO_UPGRADE=1; shift ;;
--quiet|-q) HANZO_QUIET=1; shift ;;
--help|-h)
cat << 'EOF'
hanzo installer
usage: curl -fsSL hanzo.sh | bash
curl -fsSL hanzo.sh | bash -s -- [options]
options:
-b, --bundle NAME installation bundle (default: default)
default - cli + mcp
minimal - cli only
full - cli + mcp + agents + node
-d, --dir PATH install directory (default: ~/.local/bin)
-u, --upgrade upgrade existing installations
-f, --force force reinstall
-q, --quiet minimal output (skip extras suggestions)
-h, --help show this help
shortcuts:
curl hanzo.sh | bash # default (cli + mcp)
curl hanzo.sh/cli | bash # cli only
curl hanzo.sh/mcp | bash # mcp only
curl hanzo.sh/agents | bash # agents
curl hanzo.sh/full | bash # everything
extras (shown after install):
- VS Code / Cursor extensions
- Browser extensions (Chrome, Firefox)
- Shell completions (zsh, bash, fish)
- Claude Desktop MCP integration
- Other AI tools (claude-code, aider)
examples:
curl -fsSL hanzo.sh | bash
curl -fsSL hanzo.sh | bash -s -- --upgrade
curl -fsSL hanzo.sh | bash -s -- --bundle full
EOF
exit 0 ;;
-*) warn "unknown option: $1"; shift ;;
*) HANZO_BUNDLE="$1"; shift ;;
esac
done
}
detect_platform() {
OS="$(uname -s)"
ARCH="$(uname -m)"
case "$OS" in
Darwin) PLATFORM="macos" ;;
Linux) PLATFORM="linux" ;;
MINGW*|MSYS*|CYGWIN*) PLATFORM="windows" ;;
*) die "unsupported os: $OS" ;;
esac
case "$ARCH" in
x86_64|amd64) ARCH="x64" ;;
arm64|aarch64) ARCH="arm64" ;;
armv7*) ARCH="arm" ;;
*) die "unsupported arch: $ARCH" ;;
esac
log "${PLATFORM}-${ARCH}"
}
has_cmd() { command -v "$1" >/dev/null 2>&1; }
has_uv_tool() { uv tool list 2>/dev/null | grep -q "^${1} " 2>/dev/null; }
get_uv_version() { uv tool list 2>/dev/null | grep "^${1} " | awk '{print $2}' | tr -d 'v'; }
install_uv() {
if has_cmd uv; then
ok "uv $(uv --version 2>/dev/null | awk '{print $2}')"
return 0
fi
log "installing uv..."
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
has_cmd uv && ok "uv installed" || die "failed to install uv"
}
install_tool() {
local tool="$1" name="${2:-$1}"
if has_uv_tool "$tool"; then
local ver=$(get_uv_version "$tool")
local path=$(command -v "$name" 2>/dev/null || echo "~/.local/bin/$name")
if [[ "$HANZO_FORCE" == "1" ]]; then
log "reinstalling $name..."
uv tool install "$tool" --force 2>/dev/null && { INSTALLED+=("$name $ver"); ok "$name $ver"; } || { FAILED+=("$name"); fail "$name"; }
elif [[ "$HANZO_UPGRADE" == "1" ]]; then
log "upgrading $name..."
uv tool upgrade "$tool" 2>/dev/null
local new_ver=$(get_uv_version "$tool")
[[ "$ver" != "$new_ver" ]] && { UPGRADED+=("$name $ver → $new_ver"); ok "$name $ver → $new_ver"; } || { SKIPPED+=("$name $ver @ $path"); skip "$name $ver"; }
else
SKIPPED+=("$name $ver @ $path"); skip "$name $ver"
fi
return 0
fi
log "installing $name..."
uv tool install "$tool" 2>/dev/null && {
local ver=$(get_uv_version "$tool")
INSTALLED+=("$name $ver"); ok "$name $ver";
} || { FAILED+=("$name"); fail "$name"; return 1; }
}
has_bin() { [[ -x "$HANZO_DIR/$1" ]] || has_cmd "$1"; }
get_bin_version() {
local p="$HANZO_DIR/$1"
[[ -x "$p" ]] || p="$(command -v "$1" 2>/dev/null)"
[[ -n "$p" ]] && "$p" --version 2>/dev/null | head -1 || echo "?"
}
install_release() {
local repo="$1" bin="$2" name="${3:-$2}"
if has_bin "$bin"; then
local ver=$(get_bin_version "$bin")
if [[ "$HANZO_FORCE" != "1" ]]; then
SKIPPED+=("$name"); skip "$name ($ver)"
return 0
fi
log "reinstalling $name..."
else
log "installing $name..."
fi
local api="https://api.github.com/repos/${repo}/releases/latest"
local pat=""
case "$PLATFORM-$ARCH" in
macos-arm64) pat="darwin.*arm64\|macos.*arm64\|apple.*arm64" ;;
macos-x64) pat="darwin.*x64\|darwin.*amd64\|macos.*x64" ;;
linux-x64) pat="linux.*x64\|linux.*amd64" ;;
linux-arm64) pat="linux.*arm64\|linux.*aarch64" ;;
*) warn "no binary for $PLATFORM-$ARCH"; return 1 ;;
esac
local info url
info=$(curl -sL "$api" 2>/dev/null) || { FAILED+=("$name"); fail "$name"; return 1; }
url=$(echo "$info" | grep "browser_download_url" | grep -iE "$pat" | head -1 | cut -d '"' -f 4)
[[ -z "$url" ]] && { warn "no release for $repo"; FAILED+=("$name"); return 1; }
mkdir -p "$HANZO_DIR"
local tmp="/tmp/${bin}-$$"
curl -sL "$url" -o "$tmp" || { FAILED+=("$name"); rm -f "$tmp"; return 1; }
if [[ "$url" == *.tar.gz ]] || [[ "$url" == *.tgz ]]; then
tar -xzf "$tmp" -C "$HANZO_DIR" 2>/dev/null || {
local d="/tmp/${bin}-ext-$$"; mkdir -p "$d"
tar -xzf "$tmp" -C "$d"
find "$d" -name "$bin" -type f -exec mv {} "$HANZO_DIR/$bin" \;
rm -rf "$d"
}
elif [[ "$url" == *.zip ]]; then
unzip -o "$tmp" -d "$HANZO_DIR" 2>/dev/null || {
local d="/tmp/${bin}-ext-$$"; mkdir -p "$d"
unzip -o "$tmp" -d "$d"
find "$d" -name "$bin" -type f -exec mv {} "$HANZO_DIR/$bin" \;
rm -rf "$d"
}
else
mv "$tmp" "$HANZO_DIR/$bin"
fi
chmod +x "$HANZO_DIR/$bin" 2>/dev/null || true
rm -f "$tmp"
INSTALLED+=("$name"); ok "$name"
}
doctor() {
# Naming strategy:
# Python (uv): hanzo, hanzo-mcp, hanzo-dev, hanzo-node, hanzo-agents (full names)
# Rust (cargo): hanzo, mcp, dev, hanzod (short names)
# Node (npm): hanzo, mcp, dev, hanzod, agents (short names)
# Go: hanzo, hanzod, dev (short names)
# Homebrew: hanzo, hanzo-mcp, hanzo-dev, hanzo-node (formula names)
# python (uv tool) - keeps full hanzo-* names
echo -e " ${BD}python (uv):${N}"
local py_found=0
if has_cmd uv; then
local uv_list=$(uv tool list 2>/dev/null || true)
for pkg in hanzo hanzo-mcp hanzo-dev hanzo-node hanzo-agents; do
local info=$(echo "$uv_list" | grep -E "^${pkg} " || true)
if [[ -n "$info" ]]; then
local ver=$(echo "$info" | awk '{print $2}')
local path=$(command -v "$pkg" 2>/dev/null || echo "~/.local/bin/$pkg")
printf " ${G}✓${N} %-16s %-10s %s\n" "$pkg" "$ver" "$path"
py_found=1
fi
done
fi
[[ $py_found -eq 0 ]] && echo -e " ${DM}(none)${N}"
# rust (cargo) - short binary names
echo -e " ${BD}rust (cargo):${N}"
local rs_found=0
if has_cmd cargo; then
local cargo_list=$(cargo install --list 2>/dev/null || true)
for pair in "hanzo-cli:hanzo" "hanzo-mcp:mcp" "hanzo-dev:dev" "hanzo-node:hanzod"; do
local crate="${pair%%:*}" bin="${pair##*:}"
local info=$(echo "$cargo_list" | grep -E "^${crate} " || true)
if [[ -n "$info" ]]; then
local ver=$(echo "$info" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "?")
local path=$(command -v "$bin" 2>/dev/null || echo "~/.cargo/bin/$bin")
printf " ${G}✓${N} %-16s %-10s %s\n" "$crate" "$ver" "$path"
rs_found=1
fi
done
fi
[[ $rs_found -eq 0 ]] && echo -e " ${DM}(none)${N}"
# node (npm/pnpm) - @hanzo/* packages with short binary names
echo -e " ${BD}node (npm/pnpm):${N}"
local ts_found=0
for pair in "@hanzo/cli:hanzo" "@hanzo/mcp:mcp" "@hanzo/dev:dev" "@hanzo/node:hanzod" "@hanzo/agents:agents"; do
local pkg="${pair%%:*}" bin="${pair##*:}"
local path=$(command -v "$bin" 2>/dev/null || true)
[[ -z "$path" ]] && continue
# skip if python version (full names like hanzo-mcp)
[[ "$path" == *".local/bin"* ]] && continue
# skip if cargo version
[[ "$path" == *".cargo/bin"* ]] && continue
if [[ "$path" == *"node_modules"* || "$path" == *"npm"* || "$path" == *"pnpm"* || "$path" == *"fnm"* || "$path" == *"nvm"* ]]; then
local ver=$("$bin" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "?")
printf " ${G}✓${N} %-16s %-10s %s\n" "$pkg" "$ver" "$path"
ts_found=1
fi
done
[[ $ts_found -eq 0 ]] && echo -e " ${DM}(none)${N}"
# go (go install) - short binary names
echo -e " ${BD}go (go install):${N}"
local go_found=0
local gobin="${GOBIN:-${GOPATH:-$HOME/go}/bin}"
for pair in "hanzo-cli:hanzo" "hanzo-node:hanzod" "hanzo-dev:dev"; do
local pkg="${pair%%:*}" bin="${pair##*:}"
local path="$gobin/$bin"
if [[ -x "$path" ]]; then
local ver=$("$path" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "?")
printf " ${G}✓${N} %-16s %-10s %s\n" "$pkg" "$ver" "$path"
go_found=1
fi
done
[[ $go_found -eq 0 ]] && echo -e " ${DM}(none)${N}"
# homebrew - formula:binary mappings
echo -e " ${BD}homebrew:${N}"
local brew_found=0
if has_cmd brew; then
local brew_list=$(brew list --formula 2>/dev/null || true)
for pair in "hanzo:hanzo" "hanzo-mcp:hanzo-mcp" "hanzo-dev:hanzo-dev" "hanzo-node:hanzo-node" "hanzo-agents:hanzo-agents"; do
local formula="${pair%%:*}" bin="${pair##*:}"
if echo "$brew_list" | grep -q "^${formula}$"; then
local path=$(brew --prefix "$formula" 2>/dev/null)/bin/$bin
[[ ! -x "$path" ]] && path=$(command -v "$bin" 2>/dev/null || echo "?")
local ver=$("$bin" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "?")
printf " ${G}✓${N} %-16s %-10s %s\n" "$formula" "$ver" "$path"
brew_found=1
fi
done
fi
[[ $brew_found -eq 0 ]] && echo -e " ${DM}(none)${N}"
# other binaries (manual installs, /usr/local/bin, etc)
echo -e " ${BD}other:${N}"
local other_found=0
for bin in hanzo hanzo-mcp hanzo-dev hanzo-node hanzo-agents hanzod mcp dev agents; do
local path=$(command -v "$bin" 2>/dev/null || true)
[[ -z "$path" ]] && continue
# skip if found in known ecosystems
[[ "$path" == *".local/bin"* ]] && continue
[[ "$path" == *".cargo/bin"* ]] && continue
[[ "$path" == *"node_modules"* || "$path" == *"npm"* || "$path" == *"pnpm"* ]] && continue
[[ "$path" == *"/go/bin"* ]] && continue
[[ "$path" == *"homebrew"* || "$path" == *"Cellar"* ]] && continue
local ver=$("$bin" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "?")
printf " ${G}✓${N} %-16s %-10s %s\n" "$bin" "$ver" "$path"
other_found=1
done
[[ $other_found -eq 0 ]] && echo -e " ${DM}(none)${N}"
echo ""
}
install_bundle() {
local bundle="$1"
echo ""
case "$bundle" in
default)
# default: cli + mcp
install_tool "hanzo" "hanzo"
install_tool "hanzo-mcp" "hanzo-mcp"
;;
minimal|cli)
install_tool "hanzo" "hanzo"
;;
mcp)
install_tool "hanzo-mcp" "hanzo-mcp"
;;
agents)
install_tool "hanzo-agents" "hanzo-agents"
;;
node)
install_tool "hanzo-node" "hanzo-node"
# hanzo-node install handled by the package
;;
dev)
install_tool "hanzo" "hanzo"
install_tool "hanzo-mcp" "hanzo-mcp"
;;
full|all)
install_tool "hanzo" "hanzo"
install_tool "hanzo-mcp" "hanzo-mcp"
install_tool "hanzo-agents" "hanzo-agents"
install_tool "hanzo-node" "hanzo-node"
;;
*)
die "unknown bundle: $bundle"
;;
esac
}
summary() {
echo ""
if [[ ${#INSTALLED[@]} -gt 0 ]]; then
echo -e " ${G}${BD}installed:${N}"
for i in "${INSTALLED[@]}"; do echo -e " ${G}✓${N} $i"; done
fi
if [[ ${#UPGRADED[@]} -gt 0 ]]; then
echo -e " ${C}${BD}upgraded:${N}"
for i in "${UPGRADED[@]}"; do echo -e " ${C}↑${N} $i"; done
fi
if [[ ${#SKIPPED[@]} -gt 0 ]] && [[ "$HANZO_QUIET" != "1" ]]; then
echo -e " ${DM}skipped:${N}"
for i in "${SKIPPED[@]}"; do echo -e " ${DM}○ $i${N}"; done
fi
if [[ ${#FAILED[@]} -gt 0 ]]; then
echo -e " ${R}${BD}failed:${N}"
for i in "${FAILED[@]}"; do echo -e " ${R}✗${N} $i"; done
fi
}
finish() {
echo ""
if [[ ${#INSTALLED[@]} -gt 0 ]] || [[ ${#UPGRADED[@]} -gt 0 ]]; then
echo -e " ${G}ready!${N}"
echo ""
echo " quick start:"
echo -e " ${C}hanzo login${N} # authenticate with hanzo"
echo -e " ${C}hanzo --help${N} # see all commands"
echo -e " ${C}hanzo dev${N} # start dev cli (auto-installs)"
echo -e " ${C}hanzo net${N} # start compute node (auto-installs)"
has_uv_tool "hanzo-mcp" && echo -e " ${C}hanzo mcp serve${N} # start mcp server"
echo ""
echo " docs: https://docs.hanzo.ai"
echo ""
elif [[ ${#SKIPPED[@]} -gt 0 ]]; then
echo -e " ${G}already up to date${N}"
echo ""
doctor
echo " to upgrade: curl -fsSL hanzo.sh | bash -s -- -u"
echo ""
fi
if [[ ":$PATH:" != *":$HANZO_DIR:"* ]] && [[ ${#INSTALLED[@]} -gt 0 ]]; then
echo -e " ${Y}add to path:${N}"
echo " export PATH=\"$HANZO_DIR:\$PATH\""
echo ""
fi
}
extras() {
[[ "$HANZO_QUIET" == "1" ]] && return
echo -e " ${BD}environment:${N}"
echo ""
# Detect API keys
local api_keys_found=0
echo -e " ${BD}api keys:${N}"
[[ -n "${ANTHROPIC_API_KEY:-}" ]] && echo -e " ${G}✓${N} ANTHROPIC_API_KEY" && ((api_keys_found++))
[[ -n "${OPENAI_API_KEY:-}" ]] && echo -e " ${G}✓${N} OPENAI_API_KEY" && ((api_keys_found++))
[[ -n "${GOOGLE_API_KEY:-}" ]] && echo -e " ${G}✓${N} GOOGLE_API_KEY" && ((api_keys_found++))
[[ -n "${GEMINI_API_KEY:-}" ]] && echo -e " ${G}✓${N} GEMINI_API_KEY" && ((api_keys_found++))
[[ -n "${XAI_API_KEY:-}" ]] && echo -e " ${G}✓${N} XAI_API_KEY" && ((api_keys_found++))
[[ -n "${GROQ_API_KEY:-}" ]] && echo -e " ${G}✓${N} GROQ_API_KEY" && ((api_keys_found++))
[[ -n "${TOGETHER_API_KEY:-}" ]] && echo -e " ${G}✓${N} TOGETHER_API_KEY" && ((api_keys_found++))
[[ -n "${MISTRAL_API_KEY:-}" ]] && echo -e " ${G}✓${N} MISTRAL_API_KEY" && ((api_keys_found++))
[[ -n "${COHERE_API_KEY:-}" ]] && echo -e " ${G}✓${N} COHERE_API_KEY" && ((api_keys_found++))
[[ -n "${REPLICATE_API_TOKEN:-}" ]] && echo -e " ${G}✓${N} REPLICATE_API_TOKEN" && ((api_keys_found++))
[[ -n "${HUGGING_FACE_HUB_TOKEN:-}" ]] && echo -e " ${G}✓${N} HUGGING_FACE_HUB_TOKEN" && ((api_keys_found++))
[[ -n "${HF_TOKEN:-}" ]] && echo -e " ${G}✓${N} HF_TOKEN" && ((api_keys_found++))
[[ -n "${GITHUB_TOKEN:-}" ]] && echo -e " ${G}✓${N} GITHUB_TOKEN" && ((api_keys_found++))
[[ -n "${CLOUDFLARE_API_TOKEN:-}" ]] && echo -e " ${G}✓${N} CLOUDFLARE_API_TOKEN" && ((api_keys_found++))
[[ -n "${AWS_ACCESS_KEY_ID:-}" ]] && echo -e " ${G}✓${N} AWS_ACCESS_KEY_ID" && ((api_keys_found++))
[[ $api_keys_found -eq 0 ]] && echo -e " ${DM}(none detected)${N}"
echo ""
# Detect AI CLI tools
echo -e " ${BD}ai cli tools:${N}"
local cli_found=0
has_cmd claude && echo -e " ${G}✓${N} claude $(claude --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd dev && echo -e " ${G}✓${N} dev $(dev --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd gemini && echo -e " ${G}✓${N} gemini $(gemini --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd codex && echo -e " ${G}✓${N} codex $(codex --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd aider && echo -e " ${G}✓${N} aider $(aider --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd grok && echo -e " ${G}✓${N} grok $(grok --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd vibe && echo -e " ${G}✓${N} vibe $(vibe --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd cursor && echo -e " ${G}✓${N} cursor $(cursor --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd windsurf && echo -e " ${G}✓${N} windsurf $(windsurf --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
has_cmd q && echo -e " ${G}✓${N} amazon-q $(q --version 2>/dev/null | head -1 || echo '')" && ((cli_found++))
[[ $cli_found -eq 0 ]] && echo -e " ${DM}(none detected)${N}"
echo ""
echo -e " ${BD}optional extras:${N}"
echo ""
# Editor extensions (coming soon)
if has_cmd code || has_cmd cursor; then
echo -e " ${BD}editor extensions:${N} ${DM}(coming soon)${N}"
echo -e " ${DM}code --install-extension hanzo-ai.hanzo${N}"
echo ""
fi
# Shell completions
echo -e " ${BD}shell completions:${N}"
if [[ -n "${ZSH_VERSION:-}" ]] || [[ "$SHELL" == *"zsh"* ]]; then
echo -e " ${C}hanzo completion zsh > ~/.zsh/completions/_hanzo${N}"
fi
if [[ -n "${BASH_VERSION:-}" ]] || [[ "$SHELL" == *"bash"* ]]; then
echo -e " ${C}hanzo completion bash > ~/.bash_completion.d/hanzo${N}"
fi
if [[ "$SHELL" == *"fish"* ]]; then
echo -e " ${C}hanzo completion fish > ~/.config/fish/completions/hanzo.fish${N}"
fi
# Claude Desktop MCP
if [[ -d "$HOME/Library/Application Support/Claude" ]] || [[ -d "$HOME/.config/claude" ]]; then
echo ""
echo -e " ${BD}claude desktop mcp:${N}"
echo -e " ${C}hanzo-mcp install claude${N}"
echo -e " └─ Add Hanzo MCP to Claude Desktop"
fi
# AI CLI tools - only show missing ones
local missing=0
has_cmd claude || ((missing++))
has_cmd gemini || ((missing++))
has_cmd codex || ((missing++))
has_cmd dev || ((missing++))
has_cmd aider || ((missing++))
has_cmd grok || ((missing++))
if [[ $missing -gt 0 ]]; then
echo ""
echo -e " ${BD}install ai coding tools:${N}"
has_cmd dev || echo -e " ${C}npm i -g @hanzo/dev${N} # Hanzo Dev"
has_cmd claude || echo -e " ${C}npm i -g @anthropic-ai/claude-code${N} # Claude Code (free)"
has_cmd gemini || echo -e " ${C}npm i -g @google/gemini-cli${N} # Gemini CLI (free)"
has_cmd codex || echo -e " ${C}npm i -g @openai/codex${N} # OpenAI Codex (free)"
has_cmd grok || echo -e " ${C}npm i -g grok-cli${N} # Grok CLI (free)"
has_cmd aider || echo -e " ${C}pip install aider-chat${N} # Aider (free)"
fi
echo ""
}
main() {
parse_args "$@"
banner
log "bundle: $HANZO_BUNDLE"
[[ "$HANZO_UPGRADE" == "1" ]] && log "mode: upgrade"
[[ "$HANZO_FORCE" == "1" ]] && log "mode: force"
echo ""
detect_platform
install_uv
install_bundle "$HANZO_BUNDLE"
summary
finish
extras
[[ ${#FAILED[@]} -gt 0 ]] && exit 1
exit 0
}
main "$@"