---
name: "Mac App Management"
description: "Launch, quit, switch, and manage macOS applications remotely — window management, Finder operations, app installation. For controlling apps on Mac Minis via SSH."
version: "1.0.0"
author: "skynet"
category: "ops"
agents: ["claude-code", "codex", "gemini"]
tags: ["mac", "apps", "window-management", "finder", "remote"]
tools_required: ["bash", "ssh"]
---

# Mac App Management

# Mac App Management

Launch, quit, manage, and automate macOS applications remotely via SSH.

## IMPORTANT: AppleScript Over SSH

**Never use `tell app "AppName" to <window command>` over SSH** — it hangs. Always use the System Events process wrapper for window operations. Simple commands like `activate` and `quit` are fine.

## Launching Apps

```bash
ssh bots 'open -a Safari'
ssh bots 'open -a "Google Chrome"'
ssh bots 'open -a Terminal'
ssh bots 'open -a "Visual Studio Code"'

# Open a file with default app
ssh bots 'open ~/Documents/report.pdf'

# Open a URL
ssh bots 'open https://example.com'

# Open with a specific app
ssh bots 'open -a "Google Chrome" https://example.com'

# Open Finder at a path
ssh bots 'open ~/Downloads'
```

## Quitting Apps

```bash
# Graceful quit (works over SSH)
ssh bots 'osascript -e "tell app \"Safari\" to quit"'

# Force quit
ssh bots 'pkill -9 Safari'
ssh bots 'killall Safari'
```

## Listing Running Apps

```bash
# All visible apps
ssh bots 'osascript -e "tell app \"System Events\" to get name of every process whose visible is true"'

# Frontmost app
ssh bots 'osascript -e "tell app \"System Events\" to get name of first process whose frontmost is true"'

# Check if an app is running
ssh bots 'pgrep -q Safari && echo "Running" || echo "Not running"'

# Process list
ssh bots 'ps aux | grep -i safari'
```

## Window Management (via System Events)

All window operations MUST go through System Events:

```bash
# List all windows for an app
ssh bots 'osascript -e "tell app \"System Events\" to tell process \"Google Chrome\" to get name of every window"'

# Get window count (JXA — more reliable)
ssh bots 'osascript -l JavaScript -e "Application(\"System Events\").processes.byName(\"Google Chrome\").windows.length"'

# Bring app to front
ssh bots 'osascript -e "tell app \"Google Chrome\" to activate"'

# Move window (via System Events)
ssh bots 'osascript -e "tell app \"System Events\" to tell process \"Google Chrome\" to set position of window 1 to {100, 100}"'

# Resize window (via System Events)
ssh bots 'osascript -e "tell app \"System Events\" to tell process \"Google Chrome\" to set size of window 1 to {1200, 800}"'
```

Always wrap in timeout to catch hangs:
```bash
timeout 5 ssh bots 'osascript -e "..."' || echo "Timed out"
```

## Finder Operations

```bash
# List files
ssh bots 'ls -la ~/Downloads/'

# Move file
ssh bots 'mv ~/Downloads/file.txt ~/Documents/'

# Create folder
ssh bots 'mkdir -p ~/Documents/NewProject'

# Reveal file in Finder
ssh bots 'open -R ~/Documents/file.txt'

# Open Finder window at path
ssh bots 'open ~/Downloads'
```

Note: Finder AppleScript commands (`tell app "Finder" to ...`) hang over SSH. Use `open` command and `ls`/`mv`/`cp` instead.

## App Installation

```bash
# Via Homebrew (preferred) — remember to source brew
ssh bots 'eval "$(/opt/homebrew/bin/brew shellenv)" && brew install --cask google-chrome'
ssh bots 'eval "$(/opt/homebrew/bin/brew shellenv)" && brew install --cask visual-studio-code'
ssh bots 'eval "$(/opt/homebrew/bin/brew shellenv)" && brew install --cask firefox'

# List installed casks
ssh bots 'eval "$(/opt/homebrew/bin/brew shellenv)" && brew list --cask'

# Update all
ssh bots 'eval "$(/opt/homebrew/bin/brew shellenv)" && brew upgrade --cask'

# Uninstall
ssh bots 'eval "$(/opt/homebrew/bin/brew shellenv)" && brew uninstall --cask firefox'
```

## App Settings and Preferences

```bash
ssh bots 'defaults read com.apple.Safari'
ssh bots 'defaults export com.apple.Safari /tmp/safari-prefs.plist'
ssh bots 'defaults import com.apple.Safari /tmp/safari-prefs.plist'
```

## Notification Center

```bash
ssh bots 'osascript -e "display notification \"Build complete\" with title \"Factory\" sound name \"Glass\""'
```

## Login Items (Apps that start on boot)

```bash
# List login items
ssh bots 'osascript -e "tell app \"System Events\" to get name of every login item"'

# Add login item
ssh bots 'osascript -e "tell app \"System Events\" to make login item at end with properties {path:\"/Applications/MyApp.app\", hidden:false}"'

# Remove login item
ssh bots 'osascript -e "tell app \"System Events\" to delete login item \"MyApp\""'
```
