#!/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="โœ—" 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"; } 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 mo_bin="$3" local subcommand="$4" local raw_cmd="\"${mo_bin}\" ${subcommand}" local cmd_escaped="${raw_cmd//\\/\\\\}" cmd_escaped="${cmd_escaped//\"/\\\"}" cat > "$target" << EOF #!/bin/bash # Required parameters: # @raycast.schemaVersion 1 # @raycast.title ${title} # @raycast.mode fullOutput # @raycast.packageName Mole # Optional parameters: # @raycast.icon ๐Ÿน set -euo pipefail echo "๐Ÿน Running ${title}..." echo "" CMD="${raw_cmd}" CMD_ESCAPED="${cmd_escaped}" 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 <<'APPLESCRIPT' set targetCommand to "${cmd_escaped}" tell application "Terminal" activate do script targetCommand end tell APPLESCRIPT return 0 fi ;; iTerm|iTerm2) if command -v osascript >/dev/null 2>&1; then osascript <<'APPLESCRIPT' set targetCommand to "${cmd_escaped}" tell application "iTerm2" activate try tell current window tell current session write text targetCommand end tell end tell on error create window with default profile tell current window tell current session write text targetCommand end tell end tell end try end tell APPLESCRIPT return 0 fi ;; Alacritty) if launcher_available "Alacritty" && command -v open >/dev/null 2>&1; then open -na "Alacritty" --args -e /bin/zsh -lc "${raw_cmd}" return \$? fi ;; Kitty) if has_bin "kitty"; then kitty --hold /bin/zsh -lc "${raw_cmd}" return \$? elif [[ -x "/Applications/kitty.app/Contents/MacOS/kitty" ]]; then "/Applications/kitty.app/Contents/MacOS/kitty" --hold /bin/zsh -lc "${raw_cmd}" return \$? fi ;; WezTerm) if has_bin "wezterm"; then wezterm start -- /bin/zsh -lc "${raw_cmd}" return \$? elif [[ -x "/Applications/WezTerm.app/Contents/MacOS/wezterm" ]]; then "/Applications/WezTerm.app/Contents/MacOS/wezterm" start -- /bin/zsh -lc "${raw_cmd}" return \$? fi ;; Ghostty) if has_bin "ghostty"; then ghostty --command "/bin/zsh" -- -lc "${raw_cmd}" return \$? elif [[ -x "/Applications/Ghostty.app/Contents/MacOS/ghostty" ]]; then "/Applications/Ghostty.app/Contents/MacOS/ghostty" --command "/bin/zsh" -- -lc "${raw_cmd}" return \$? fi ;; Hyper) if launcher_available "Hyper" && command -v open >/dev/null 2>&1; then open -na "Hyper" --args /bin/zsh -lc "${raw_cmd}" return \$? fi ;; WindTerm) if launcher_available "WindTerm" && command -v open >/dev/null 2>&1; then open -na "WindTerm" --args /bin/zsh -lc "${raw_cmd}" return \$? fi ;; Warp) if launcher_available "Warp" && command -v open >/dev/null 2>&1; then open -na "Warp" --args /bin/zsh -lc "${raw_cmd}" return \$? fi ;; esac return 1 } if [[ -n "\${TERM:-}" && "\${TERM}" != "dumb" ]]; then "${mo_bin}" ${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 " ${raw_cmd}" exit 1 EOF chmod +x "$target" } create_raycast_commands() { local mo_bin="$1" local default_dir="$HOME/Library/Application Support/Raycast/script-commands" local alt_dir="$HOME/Documents/Raycast/Scripts" local dirs=() if [[ -d "$default_dir" ]]; then dirs+=("$default_dir") fi if [[ -d "$alt_dir" ]]; then dirs+=("$alt_dir") fi if [[ ${#dirs[@]} -eq 0 ]]; then dirs+=("$default_dir") fi log_step "Installing Raycast commands..." for dir in "${dirs[@]}"; do mkdir -p "$dir" write_raycast_script "$dir/mole-clean.sh" "clean" "$mo_bin" "clean" write_raycast_script "$dir/mole-uninstall.sh" "uninstall" "$mo_bin" "uninstall" write_raycast_script "$dir/mole-optimize.sh" "optimize" "$mo_bin" "optimize" write_raycast_script "$dir/mole-analyze.sh" "analyze" "$mo_bin" "analyze" write_raycast_script "$dir/mole-status.sh" "status" "$mo_bin" "status" log_success "Scripts ready in: $dir" done log_header "Raycast Configuration" if open "raycast://extensions/raycast/raycast-settings/extensions" > /dev/null 2>&1; then log_step "Raycast settings opened." else log_warn "Could not auto-open Raycast." fi for dir in "${dirs[@]}"; do if [ ! -d "$dir" ]; then echo "Skipping: $dir (Directory does not exist)" continue fi echo -n "$dir" | pbcopy echo "" echo "--- Action Required for folder: $(basename "$dir") ---" echo "1. In the Raycast Extensions window, click the '+' icon." echo "2. Select 'Add Script Directory'." echo "3. A folder picker will open. Press: Shift + Command + G (โ‡งโŒ˜G)." echo "4. Paste the path (manually) that is copied to your clipboard and press Enter." echo "5. Open / select the folder to confirm." echo "" echo "Path copied to your clipboard: $dir" read -r -p "Press [Enter] once you have added this folder..." done log_header "Finalizing Setup" read -r -p "In Raycast we need to reload the script directories to sync your new commands. Press [Enter] to reload..." open "raycast://extensions/raycast/raycast/reload-script-directories" log_success "Raycast setup complete!" } 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" if [[ ! -d "$workflows_dir" ]]; then log_warn "Alfred preferences not found at $workflows_dir. Skipping Alfred workflow." return fi log_step "Installing Alfred workflows..." local workflows=( "fun.tw93.mole.clean|Mole clean|clean|Run Mole clean|\"${mo_bin}\" clean" "fun.tw93.mole.uninstall|Mole uninstall|uninstall|Uninstall apps via Mole|\"${mo_bin}\" uninstall" "fun.tw93.mole.optimize|Mole optimize|optimize|System health & optimization|\"${mo_bin}\" optimize" "fun.tw93.mole.analyze|Mole analyze|analyze|Disk space analysis|\"${mo_bin}\" analyze" "fun.tw93.mole.status|Mole status|status|Live system dashboard|\"${mo_bin}\" status" ) for entry in "${workflows[@]}"; do IFS="|" read -r bundle name keyword subtitle command <<< "$entry" local workflow_uid="user.workflow.$(uuid | 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 ${name} objects config argumenttype 2 keyword ${keyword} subtext ${subtitle} text ${name} 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: ${name} (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:" echo " โ€ข clean - Deep system cleanup" echo " โ€ข uninstall - Remove applications" echo " โ€ข optimize - System health & tuning" echo " โ€ข analyze - Disk space explorer" echo " โ€ข status - Live system monitor" echo "" } main "$@"