← All Skills

snappy-desktop

v1.0.0
6 files, 44.3 KB ~3,183 words · 13 min read Updated 2026-09-09

snappy-desktop skill

25 of 34 checks pass
What it can do
run instructionwrite
screenshotwrite-reversible
What does not pass yet
reached bysnappy-ax
$ npx snappy-skills install snappy-desktop
zip ↓
Documents
AGENTS.md

snappy-desktop -- Agent Loader#

You are operating the macOS desktop automation primitive. Wraps npx -y @midscene/computer@1 (Gemini 2.5 Flash via OpenRouter) to give any Snappy skill "hands on the screen" for native apps -- Finder, System Settings, Messages, Notes, Mail, third-party apps. For websites use snappy-browse. For iMessage default to snappy-imessage first.

API module#

typescriptimport { runMidscene, screenshot } from "../snappy-desktop/api.ts";
Function Purpose
runMidscene(instruction) Run a Midscene vision instruction via Terminal routing
screenshot() Take a screenshot and return the file path
whatsappReadChats() Open WhatsApp.app, screenshot chat list, extract chat names + unread badges via Midscene
whatsappReadChat(contactName) Click into a specific chat, screenshot, extract recent messages
whatsappSendMessage(contactName, text) Open chat with contact, type and send message via clipboard + Enter

CLI:

bashnpx tsx ~/.claude/skills/snappy-desktop/api.ts run "click the Settings icon"
npx tsx ~/.claude/skills/snappy-desktop/api.ts screenshot
npx tsx ~/.claude/skills/snappy-desktop/api.ts wa-chats
npx tsx ~/.claude/skills/snappy-desktop/api.ts wa-read "John Smith"
npx tsx ~/.claude/skills/snappy-desktop/api.ts wa-send "John Smith" "Hey, are you free tomorrow?"

Rules#

  1. SSH routing required. Every Midscene command runs from a script file launched via osascript -e 'tell application "Terminal" to do script "..."'. Never invoke Midscene directly over SSH -- Screen Recording permission lives on Terminal.app.
  2. Unique RUN_ID. RUN_ID=$(date +%s) in every script + output filename. No reuse of /tmp/midscene-cmd.sh.
  3. Poll for done marker. while [ ! -f /tmp/midscene-done-${RUN_ID} ]; do sleep 2; done. Never fixed-sleep.
  4. Screenshot first. take_screenshot before clicking blind.
  5. --prompt, not --action. The Midscene flag is --prompt. Wrong flag = silent failure.
  6. AppleScript for app launch. osascript -e 'tell application "X" to activate' is instant. Vision is for seeing, not for things you can do without sight.
  7. act for multi-step. Single act --prompt "step1, step2, step3" handles transient UI (Spotlight, menus) better than chained atomic calls.
  8. Credentials via snappy-settings/.env.cache. Never hardcode. Load via env("KEY") (source load-env.sh). Use clipboard hand-off for password fields, never input --value "password".
  9. Charlotte MCP browser tools are forbidden -- they don't work for desktop OR browsers.
  10. Source env inside scripts. Each script must source ~/.midscene-env -- the osascript Terminal tab is a fresh shell.

Execution pattern#

bashRUN_ID=$(date +%s)
cat > /tmp/midscene-cmd-${RUN_ID}.sh << SCRIPT
#!/bin/bash
source ~/.midscene-env
npx -y @midscene/computer@1 take_screenshot > /tmp/midscene-out-${RUN_ID}.txt 2>&1
echo "EXIT_CODE=\$?" >> /tmp/midscene-out-${RUN_ID}.txt
touch /tmp/midscene-done-${RUN_ID}
SCRIPT
chmod +x /tmp/midscene-cmd-${RUN_ID}.sh
osascript -e "tell application \"Terminal\" to do script \"/tmp/midscene-cmd-${RUN_ID}.sh\""
while [ ! -f /tmp/midscene-done-${RUN_ID} ]; do sleep 2; done
cat /tmp/midscene-out-${RUN_ID}.txt
rm -f /tmp/midscene-cmd-${RUN_ID}.sh /tmp/midscene-out-${RUN_ID}.txt /tmp/midscene-done-${RUN_ID}

Key gotchas#

  1. Terminal steals focus. osascript -e 'tell app "Terminal" to do script ...' brings Terminal to front. Always re-activate the target app INSIDE the script before Midscene runs: osascript -e 'tell application "AppName" to activate' && sleep 2.
  2. Prefer AppleScript keystrokes over Midscene for typing. Midscene act --prompt "Type ..." often targets the wrong field (e.g. search bar instead of note body). Use osascript -e 'tell application "System Events" to keystroke "text"' after focusing the right field via Cmd+N or clicking.
  3. Midscene act with long/complex prompts can fail. XML parse errors occur with long prompts. Keep act --prompt short and single-purpose. Break multi-step flows into separate calls or use AppleScript for what you can.
  4. Keyboard shortcuts > vision for standard actions. Cmd+N (new), Cmd+S (save), Escape (dismiss), etc. are faster and more reliable than vision-based clicking.

Fast paths (use these when possible -- no vision cost)#

bashosascript -e 'tell application "AppName" to activate'           # Launch app
osascript -e 'the clipboard as text'                            # Read clipboard
open "x-apple.systempreferences:com.apple.preference.security"  # System Settings
open -a "TextEdit" file.txt                                     # Open in app
osascript -e 'tell application "System Events" to keystroke "n" using command down'  # New doc/note
osascript -e 'tell application "System Events" to keystroke "text here"'             # Type text
osascript -e 'tell application "System Events" to key code 53'                       # Escape key

Routing#

Target Skill
Any website / web SPA snappy-browse
Native macOS app / Electron app snappy-desktop (this skill)
iMessage send/list snappy-imessage first, this as fallback
WhatsApp read/send snappy-desktop wa-chats, wa-read, wa-send (native WhatsApp.app via vision)
File operations plain shell (cp, mv, mkdir, rm)

Skill files#

File Contents
SKILL.md Full reference (principles, commands, setup, permissions, timing)
patterns.md Multi-app workflows, copy+paste patterns, system operations
troubleshooting.md Permission errors, hangs, locator failures

Consumers#

snappy-imessage (Messages.app fallback), snappy-image (screen captures), snappy-video (screen recording controls), snappy-browse (CAPTCHA/native dialog fallback), snappy-maintenance (visual dev tool checks).


If this loader doesn't cover your case:

bashecho "[$(date -u +%FT%TZ)] snappy-desktop: <what was missing>" >> ~/.claude/logs/agents-md-feedback.log

Show produced work with snappy-faces: call draw for image channels or lang for MCP Apps.

<!-- SKILL-INDEX-START -->

[snappy-desktop Index]|root: ~/.claude/skills/snappy-desktop|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,patterns.md,troubleshooting.md}

<!-- SKILL-INDEX-END -->

Used by#

  • snappy-ax

<!-- SNAPPY-CONTRACT-VERBS-START -->

Contract verbs#

Generated from api.ts HAND_CONTRACT. Do not hand-edit this block.

Verb Contract arguments Effect First call
run instruction write npx tsx ~/.claude/skills/snappy-desktop/api.ts run <instruction>
screenshot write-reversible npx tsx ~/.claude/skills/snappy-desktop/api.ts screenshot

Show the result#

When an answer carries face_hint, show it with one snappy_present(<answer>) call.

See /snappy-faces for face selection. Human-facing images must crop to the

element, render at 2x on Retina, and fill the destination channel instead of

placing a small card in a full-page screenshot.

<!-- SNAPPY-CONTRACT-VERBS-END -->

---
name: snappy-desktop
role: macOS desktop automation primitive via Midscene vision AI. Controls mouse and keyboard from screen vision for any native app.
loaded-by: PreToolUse hook (auto-injected when "snappy-desktop" is mentioned)
Triggers on: macOS automation, native app, Midscene, mouse keyboard, screenshot, Finder
---

# snappy-desktop -- Agent Loader

You are operating the macOS desktop automation primitive. Wraps `npx -y @midscene/computer@1` (Gemini 2.5 Flash via OpenRouter) to give any Snappy skill "hands on the screen" for native apps -- Finder, System Settings, Messages, Notes, Mail, third-party apps. For websites use snappy-browse. For iMessage default to snappy-imessage first.

## API module

```typescript
import { runMidscene, screenshot } from "../snappy-desktop/api.ts";
```

| Function | Purpose |
|----------|---------|
| `runMidscene(instruction)` | Run a Midscene vision instruction via Terminal routing |
| `screenshot()` | Take a screenshot and return the file path |
| `whatsappReadChats()` | Open WhatsApp.app, screenshot chat list, extract chat names + unread badges via Midscene |
| `whatsappReadChat(contactName)` | Click into a specific chat, screenshot, extract recent messages |
| `whatsappSendMessage(contactName, text)` | Open chat with contact, type and send message via clipboard + Enter |

CLI:
```bash
npx tsx ~/.claude/skills/snappy-desktop/api.ts run "click the Settings icon"
npx tsx ~/.claude/skills/snappy-desktop/api.ts screenshot
npx tsx ~/.claude/skills/snappy-desktop/api.ts wa-chats
npx tsx ~/.claude/skills/snappy-desktop/api.ts wa-read "John Smith"
npx tsx ~/.claude/skills/snappy-desktop/api.ts wa-send "John Smith" "Hey, are you free tomorrow?"
```

## Rules

1. **SSH routing required.** Every Midscene command runs from a script file launched via `osascript -e 'tell application "Terminal" to do script "..."'`. Never invoke Midscene directly over SSH -- Screen Recording permission lives on Terminal.app.
2. **Unique RUN_ID.** `RUN_ID=$(date +%s)` in every script + output filename. No reuse of `/tmp/midscene-cmd.sh`.
3. **Poll for done marker.** `while [ ! -f /tmp/midscene-done-${RUN_ID} ]; do sleep 2; done`. Never fixed-sleep.
4. **Screenshot first.** `take_screenshot` before clicking blind.
5. **`--prompt`, not `--action`.** The Midscene flag is `--prompt`. Wrong flag = silent failure.
6. **AppleScript for app launch.** `osascript -e 'tell application "X" to activate'` is instant. Vision is for seeing, not for things you can do without sight.
7. **`act` for multi-step.** Single `act --prompt "step1, step2, step3"` handles transient UI (Spotlight, menus) better than chained atomic calls.
8. **Credentials via `snappy-settings/.env.cache`.** Never hardcode. Load via `env("KEY")` (source `load-env.sh`). Use clipboard hand-off for password fields, never `input --value "password"`.
9. **Charlotte MCP browser tools are forbidden** -- they don't work for desktop OR browsers.
10. **Source env inside scripts.** Each script must `source ~/.midscene-env` -- the osascript Terminal tab is a fresh shell.

## Execution pattern

```bash
RUN_ID=$(date +%s)
cat > /tmp/midscene-cmd-${RUN_ID}.sh << SCRIPT
#!/bin/bash
source ~/.midscene-env
npx -y @midscene/computer@1 take_screenshot > /tmp/midscene-out-${RUN_ID}.txt 2>&1
echo "EXIT_CODE=\$?" >> /tmp/midscene-out-${RUN_ID}.txt
touch /tmp/midscene-done-${RUN_ID}
SCRIPT
chmod +x /tmp/midscene-cmd-${RUN_ID}.sh
osascript -e "tell application \"Terminal\" to do script \"/tmp/midscene-cmd-${RUN_ID}.sh\""
while [ ! -f /tmp/midscene-done-${RUN_ID} ]; do sleep 2; done
cat /tmp/midscene-out-${RUN_ID}.txt
rm -f /tmp/midscene-cmd-${RUN_ID}.sh /tmp/midscene-out-${RUN_ID}.txt /tmp/midscene-done-${RUN_ID}
```

## Key gotchas

1. **Terminal steals focus.** `osascript -e 'tell app "Terminal" to do script ...'` brings Terminal to front. Always re-activate the target app INSIDE the script before Midscene runs: `osascript -e 'tell application "AppName" to activate' && sleep 2`.
2. **Prefer AppleScript keystrokes over Midscene for typing.** Midscene `act --prompt "Type ..."` often targets the wrong field (e.g. search bar instead of note body). Use `osascript -e 'tell application "System Events" to keystroke "text"'` after focusing the right field via Cmd+N or clicking.
3. **Midscene act with long/complex prompts can fail.** XML parse errors occur with long prompts. Keep `act --prompt` short and single-purpose. Break multi-step flows into separate calls or use AppleScript for what you can.
4. **Keyboard shortcuts > vision for standard actions.** Cmd+N (new), Cmd+S (save), Escape (dismiss), etc. are faster and more reliable than vision-based clicking.

## Fast paths (use these when possible -- no vision cost)

```bash
osascript -e 'tell application "AppName" to activate'           # Launch app
osascript -e 'the clipboard as text'                            # Read clipboard
open "x-apple.systempreferences:com.apple.preference.security"  # System Settings
open -a "TextEdit" file.txt                                     # Open in app
osascript -e 'tell application "System Events" to keystroke "n" using command down'  # New doc/note
osascript -e 'tell application "System Events" to keystroke "text here"'             # Type text
osascript -e 'tell application "System Events" to key code 53'                       # Escape key
```

## Routing

| Target | Skill |
|---|---|
| Any website / web SPA | snappy-browse |
| Native macOS app / Electron app | snappy-desktop (this skill) |
| iMessage send/list | snappy-imessage first, this as fallback |
| WhatsApp read/send | snappy-desktop `wa-chats`, `wa-read`, `wa-send` (native WhatsApp.app via vision) |
| File operations | plain shell (cp, mv, mkdir, rm) |

## Skill files

| File | Contents |
|---|---|
| SKILL.md | Full reference (principles, commands, setup, permissions, timing) |
| patterns.md | Multi-app workflows, copy+paste patterns, system operations |
| troubleshooting.md | Permission errors, hangs, locator failures |

## Consumers

snappy-imessage (Messages.app fallback), snappy-image (screen captures), snappy-video (screen recording controls), snappy-browse (CAPTCHA/native dialog fallback), snappy-maintenance (visual dev tool checks).

---

If this loader doesn't cover your case:
```bash
echo "[$(date -u +%FT%TZ)] snappy-desktop: <what was missing>" >> ~/.claude/logs/agents-md-feedback.log
```


Show produced work with `snappy-faces`: call `draw` for image channels or `lang` for MCP Apps.

<!-- SKILL-INDEX-START -->
[snappy-desktop Index]|root: ~/.claude/skills/snappy-desktop|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,patterns.md,troubleshooting.md}
<!-- SKILL-INDEX-END -->

## Used by

- `snappy-ax`

<!-- SNAPPY-CONTRACT-VERBS-START -->
## Contract verbs

Generated from `api.ts` `HAND_CONTRACT`. Do not hand-edit this block.

| Verb | Contract arguments | Effect | First call |
|---|---|---|---|
| `run` | `instruction` | `write` | `npx tsx ~/.claude/skills/snappy-desktop/api.ts run <instruction>` |
| `screenshot` | — | `write-reversible` | `npx tsx ~/.claude/skills/snappy-desktop/api.ts screenshot` |

## Show the result

When an answer carries `face_hint`, show it with one `snappy_present(<answer>)` call.
See `/snappy-faces` for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->

Keyboard Shortcuts

Search in document⌘K
Focus search/
Previous file tab
Next file tab
Close overlayEsc
Show shortcuts?