#!/bin/bash # Create Raycast script commands and Alfred keywords for Mole (clean + uninstall). set -euo pipefail BLUE='\033[0;34m' GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' ICON_STEP="➜" ICON_SUCCESS="✓" ICON_WARN="!" ICON_ERR="✗" LAUNCHER_COMMAND_SPECS=( "clean|Mole Clean|Deep system cleanup with Mole|Run Mole clean" "uninstall|Mole Uninstall|Uninstall applications with Mole|Uninstall apps via Mole" "optimize|Mole Optimize|System health checks and optimization|System health and optimization" "analyze|Mole Analyze|Disk space analysis with Mole|Disk space analysis" "status|Mole Status|Live system status dashboard|Live system dashboard" ) log_step() { echo -e "${BLUE}${ICON_STEP}${NC} $1"; } log_success() { echo -e "${GREEN}${ICON_SUCCESS}${NC} $1"; } log_warn() { echo -e "${YELLOW}${ICON_WARN}${NC} $1"; } log_error() { echo -e "${RED}${ICON_ERR}${NC} $1"; } log_header() { echo -e "\n${BLUE}==== $1 ====${NC}\n"; } is_interactive() { [[ -t 1 && -r /dev/tty ]]; } prompt_enter() { local prompt="$1" if is_interactive; then read -r -p "$prompt" < /dev/tty || true else echo "$prompt" fi } detect_mo() { if command -v mo > /dev/null 2>&1; then command -v mo elif command -v mole > /dev/null 2>&1; then command -v mole else log_error "Mole not found. Install it first via Homebrew or ./install.sh." exit 1 fi } write_raycast_script() { local target="$1" local title="$2" local description="$3" local mo_bin="$4" local subcommand="$5" local cmd_for_applescript="${mo_bin//\\/\\\\}" cmd_for_applescript="${cmd_for_applescript//\"/\\\"}" cat > "$target" << EOF #!/bin/bash # Required parameters: # @raycast.schemaVersion 1 # @raycast.title ${title} # @raycast.mode fullOutput # @raycast.packageName Mole # @raycast.description ${description} # Optional parameters: # @raycast.icon 🐹 # ────────────────────────────────────────────────────────── # Script execution begins below # ────────────────────────────────────────────────────────── set -euo pipefail echo "🐹 Running ${title}..." echo "" MO_BIN="${mo_bin}" MO_SUBCOMMAND="${subcommand}" MO_BIN_ESCAPED="${cmd_for_applescript}" has_app() { local name="\$1" [[ -d "/Applications/\${name}.app" || -d "\$HOME/Applications/\${name}.app" ]] } has_bin() { command -v "\$1" >/dev/null 2>&1 } launcher_available() { local app="\$1" case "\$app" in Terminal) return 0 ;; iTerm|iTerm2) has_app "iTerm" || has_app "iTerm2" ;; Alacritty) has_app "Alacritty" ;; Kitty) has_bin "kitty" || has_app "kitty" ;; WezTerm) has_bin "wezterm" || has_app "WezTerm" ;; Ghostty) has_bin "ghostty" || has_app "Ghostty" ;; Hyper) has_app "Hyper" ;; WindTerm) has_app "WindTerm" ;; Warp) has_app "Warp" ;; *) return 1 ;; esac } detect_launcher_app() { if [[ -n "\${MO_LAUNCHER_APP:-}" ]]; then echo "\${MO_LAUNCHER_APP}" return fi local candidates=(Warp Ghostty Alacritty Kitty WezTerm WindTerm Hyper iTerm2 iTerm Terminal) local app for app in "\${candidates[@]}"; do if launcher_available "\$app"; then echo "\$app" return fi done echo "Terminal" } launch_with_app() { local app="\$1" case "\$app" in Terminal) if command -v osascript >/dev/null 2>&1; then osascript </dev/null 2>&1; then osascript </dev/null 2>&1; then open -na "Alacritty" --args -e /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? fi ;; Kitty) if has_bin "kitty"; then kitty --hold /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? elif [[ -x "/Applications/kitty.app/Contents/MacOS/kitty" ]]; then "/Applications/kitty.app/Contents/MacOS/kitty" --hold /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? fi ;; WezTerm) if has_bin "wezterm"; then wezterm start -- /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? elif [[ -x "/Applications/WezTerm.app/Contents/MacOS/wezterm" ]]; then "/Applications/WezTerm.app/Contents/MacOS/wezterm" start -- /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? fi ;; Ghostty) if launcher_available "Ghostty" && command -v open >/dev/null 2>&1; then open -na "Ghostty" --args -e /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}; exec /bin/zsh -l" return \$? fi ;; Hyper) if launcher_available "Hyper" && command -v open >/dev/null 2>&1; then open -na "Hyper" --args /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? fi ;; WindTerm) if launcher_available "WindTerm" && command -v open >/dev/null 2>&1; then open -na "WindTerm" --args /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? fi ;; Warp) if launcher_available "Warp" && command -v open >/dev/null 2>&1; then open -na "Warp" --args /bin/zsh -lc "\"\${MO_BIN}\" \${MO_SUBCOMMAND}" return \$? fi ;; esac return 1 } if [[ -n "\${TERM:-}" && "\${TERM}" != "dumb" ]]; then "\${MO_BIN}" \${MO_SUBCOMMAND} exit \$? fi TERM_APP="\$(detect_launcher_app)" if launch_with_app "\$TERM_APP"; then exit 0 fi if [[ "\$TERM_APP" != "Terminal" ]]; then echo "Could not control \$TERM_APP, falling back to Terminal..." if launch_with_app "Terminal"; then exit 0 fi fi echo "TERM environment variable not set and no launcher succeeded." echo "Run this manually:" echo " \"\${MO_BIN}\" \${MO_SUBCOMMAND}" exit 1 EOF chmod +x "$target" } create_raycast_commands() { local mo_bin="$1" local default_dir="$HOME/Library/Application Support/Raycast/script-commands" local dir="$default_dir" local entry local subcommand local title local description local alfred_subtitle log_step "Installing Raycast commands..." mkdir -p "$dir" for entry in "${LAUNCHER_COMMAND_SPECS[@]}"; do IFS="|" read -r subcommand title description alfred_subtitle <<< "$entry" write_raycast_script "$dir/mole-${subcommand}.sh" "$title" "$description" "$mo_bin" "$subcommand" done log_success "Scripts ready in: $dir" log_header "Raycast Configuration" log_step "Open Raycast → Settings → Extensions → Script Commands." echo "1. Click \"+\" → Add Script Directory." echo "2. Choose: $dir" echo "3. Click \"Reload Script Directories\"." if is_interactive; then log_header "Finalizing Setup" log_warn "Please complete the Raycast steps above before continuing." prompt_enter "Press [Enter] to continue..." log_success "Raycast setup complete!" else log_warn "Non-interactive mode; skip Raycast reload. Please run 'Reload Script Directories' in Raycast." fi } uuid() { if command -v uuidgen > /dev/null 2>&1; then uuidgen else # Fallback pseudo UUID in format: 8-4-4-4-12 local hex=$(openssl rand -hex 16) echo "${hex:0:8}-${hex:8:4}-${hex:12:4}-${hex:16:4}-${hex:20:12}" fi } create_alfred_workflow() { local mo_bin="$1" local prefs_dir="${ALFRED_PREFS_DIR:-$HOME/Library/Application Support/Alfred/Alfred.alfredpreferences}" local workflows_dir="$prefs_dir/workflows" local entry local subcommand local title local subtitle local bundle local keyword local command if [[ ! -d "$workflows_dir" ]]; then return fi log_step "Installing Alfred workflows..." for entry in "${LAUNCHER_COMMAND_SPECS[@]}"; do IFS="|" read -r subcommand title _ subtitle <<< "$entry" bundle="fun.tw93.mole.${subcommand}" keyword="${subcommand}" command="\"${mo_bin}\" ${subcommand}" local workflow_uid="user.workflow.$(uuid | LC_ALL=C tr '[:upper:]' '[:lower:]')" local input_uid local action_uid input_uid="$(uuid)" action_uid="$(uuid)" local dir="$workflows_dir/$workflow_uid" mkdir -p "$dir" cat > "$dir/info.plist" << EOF bundleid ${bundle} createdby Mole name ${title} objects config argumenttype 2 keyword ${keyword} subtext ${subtitle} text ${title} withspace type alfred.workflow.input.keyword uid ${input_uid} version 1 config concurrently escaping 102 script #!/bin/bash PATH="/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin" ${command} scriptargtype 1 scriptfile type 0 type alfred.workflow.action.script uid ${action_uid} version 2 connections ${input_uid} destinationuid ${action_uid} modifiers 0 modifiersubtext uid ${workflow_uid} version 1 EOF log_success "Workflow ready: ${title}, keyword: ${keyword}" done log_step "Open Alfred preferences → Workflows if you need to adjust keywords." } main() { echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " Mole Quick Launchers" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" local mo_bin mo_bin="$(detect_mo)" log_step "Detected Mole binary at: ${mo_bin}" create_raycast_commands "$mo_bin" create_alfred_workflow "$mo_bin" echo "" log_success "Done! Raycast and Alfred are ready with 5 commands:" local entry local subcommand local title for entry in "${LAUNCHER_COMMAND_SPECS[@]}"; do IFS="|" read -r subcommand title _ _ <<< "$entry" echo " • Raycast: ${title} | Alfred keyword: ${subcommand}" done echo "" } main "$@"