Desktop automation via native OS accessibility trees using the agent-desktop CLI. Use when an AI agent needs to observe, interact with, or automate desktop a...
---
name: agent-desktop
version: 0.1.8
tags: desktop-automation, accessibility, ai-agent, gui-automation, cli
requirements:
- agent-desktop
description: >
Desktop automation via native OS accessibility trees using the agent-desktop CLI.
Use when an AI agent needs to observe, interact with, or automate desktop applications
(click buttons, fill forms, navigate menus, read UI state, toggle checkboxes, scroll,
drag, type text, take screenshots, manage windows, use clipboard). Covers 54 commands
across observation, interaction, keyboard/mouse, app lifecycle, clipboard, and wait.
Triggers on: "click button", "fill form", "open app", "read UI", "automate desktop",
"accessibility tree", "snapshot app", "type into field", "navigate menu", "toggle checkbox",
"take screenshot", "desktop automation", "agent-desktop", or any desktop GUI interaction task.
Supports macOS (Phase 1), with Windows and Linux planned.
---
# agent-desktop
CLI tool enabling AI agents to observe and control desktop applications via native OS accessibility trees.
**Core principle:** agent-desktop is NOT an AI agent. It is a tool that AI agents invoke. It outputs structured JSON with ref-based element identifiers. The observation-action loop lives in the calling agent.
## Installation
```bash
npm install -g agent-desktop
# or
bun install -g --trust agent-desktop
```
Requires macOS 12+ with Accessibility permission granted to your terminal.
## Reference Files
Detailed documentation is split into focused reference files. Read them as needed:
| Reference | Contents |
|-----------|----------|
| `references/commands-observation.md` | snapshot, find, get, is, screenshot, list-surfaces — all flags, output examples |
| `references/commands-interaction.md` | click, type, set-value, select, toggle, scroll, drag, keyboard, mouse — choosing the right command |
| `references/commands-system.md` | launch, close, windows, clipboard, wait, batch, status, permissions, version |
| `references/workflows.md` | 12 common patterns: forms, menus, dialogs, scroll-find, drag-drop, async wait, anti-patterns |
| `references/macos.md` | macOS permissions/TCC, AX API internals, smart activation chain, surfaces, Notification Center, troubleshooting |
## The Observe-Act Loop
Every automation follows this pattern:
```
1. OBSERVE → agent-desktop snapshot --app "App Name" -i
2. REASON → Parse JSON, find target element by ref (@e1, @e2...)
3. ACT → agent-desktop click @e5 (or type, select, toggle...)
4. VERIFY → agent-desktop snapshot again to confirm state change
5. REPEAT → Continue until task is complete
```
Always snapshot before acting. Refs are snapshot-scoped and become stale after UI changes.
## Ref System
- Refs assigned depth-first: `@e1`, `@e2`, `@e3`...
- Only interactive elements get refs: button, textfield, checkbox, link, menuitem, tab, slider, combobox, treeitem, cell
- Static text, groups, containers remain in tree for context but have no ref
- Refs are deterministic within a snapshot but NOT stable across snapshots if UI changed
- After any action that changes UI, run `snapshot` again for fresh refs
## JSON Output Contract
Every command returns a JSON envelope on stdout:
**Success:** `{ "version": "1.0", "ok": true, "command": "snapshot", "data": { ... } }`
**Error:** `{ "version": "1.0", "ok": false, "command": "click", "error": { "code": "STALE_REF", "message": "...", "suggestion": "..." } }`
Exit codes: `0` success, `1` structured error, `2` argument error.
### Error Codes
| Code | Meaning | Recovery |
|------|---------|----------|
| `PERM_DENIED` | Accessibility permission not granted | Grant in System Settings > Privacy > Accessibility |
| `ELEMENT_NOT_FOUND` | Ref not in current refmap | Re-run snapshot, use fresh ref |
| `APP_NOT_FOUND` | App not running | Launch it first |
| `ACTION_FAILED` | AX action rejected | Try alternative approach or coordinate-based click |
| `ACTION_NOT_SUPPORTED` | Element can't do this | Use different command |
| `STALE_REF` | Ref from old snapshot | Re-run snapshot |
| `WINDOW_NOT_FOUND` | No matching window | Check app name, use list-windows |
| `TIMEOUT` | Wait condition not met | Increase --timeout |
| `INVALID_ARGS` | Bad arguments | Check command syntax |
## Command Quick Reference (54 commands)
### Observation
```
agent-desktop snapshot --app "App" -i # Accessibility tree with refs
agent-desktop screenshot --app "App" out.png # PNG screenshot
agent-desktop find --app "App" --role button # Search elements
agent-desktop get @e1 --property text # Read element property
agent-desktop is @e1 --property enabled # Check element state
agent-desktop list-surfaces --app "App" # Available surfaces
```
### Interaction
```
agent-desktop click @e5 # Click element
agent-desktop double-click @e3 # Double-click
agent-desktop triple-click @e2 # Triple-click (select line)
agent-desktop right-click @e5 # Right-click (context menu)
agent-desktop type @e2 "hello" # Type text into element
agent-desktop set-value @e2 "new value" # Set value directly
agent-desktop clear @e2 # Clear element value
agent-desktop focus @e2 # Set keyboard focus
agent-desktop select @e4 "Option B" # Select dropdown option
agent-desktop toggle @e6 # Toggle checkbox/switch
agent-desktop check @e6 # Idempotent check
agent-desktop uncheck @e6 # Idempotent uncheck
agent-desktop expand @e7 # Expand disclosure
agent-desktop collapse @e7 # Collapse disclosure
agent-desktop scroll @e1 --direction down # Scroll element
agent-desktop scroll-to @e8 # Scroll into view
```
### Keyboard & Mouse
```
agent-desktop press cmd+c # Key combo
agent-desktop press return --app "App" # Targeted key press
agent-desktop key-down shift # Hold key
agent-desktop key-up shift # Release key
agent-desktop hover @e5 # Cursor to element
agent-desktop hover --xy 500,300 # Cursor to coordinates
agent-desktop drag --from @e1 --to @e5 # Drag between elements
agent-desktop mouse-click --xy 500,300 # Click at coordinates
agent-desktop mouse-move --xy 100,200 # Move cursor
agent-desktop mouse-down --xy 100,200 # Press mouse button
agent-desktop mouse-up --xy 300,400 # Release mouse button
```
### App & Window
```
agent-desktop launch "System Settings" # Launch and wait
agent-desktop close-app "TextEdit" # Quit gracefully
agent-desktop close-app "TextEdit" --force # Force kill
agent-desktop list-windows --app "Finder" # List windows
agent-desktop list-apps # List running GUI apps
agent-desktop focus-window --app "Finder" # Bring to front
agent-desktop resize-window --app "App" --width 800 --height 600
agent-desktop move-window --app "App" --x 0 --y 0
agent-desktop minimize --app "App"
agent-desktop maximize --app "App"
agent-desktop restore --app "App"
```
### Notifications
```
agent-desktop list-notifications # List all notifications
agent-desktop list-notifications --app "Slack" # Filter by app
agent-desktop list-notifications --text "deploy" --limit 5 # Filter by text
agent-desktop dismiss-notification 1 # Dismiss by index
agent-desktop dismiss-all-notifications # Dismiss all
agent-desktop dismiss-all-notifications --app "Slack" # Dismiss all from app
agent-desktop notification-action 1 --action "Reply" # Click action button
```
### Clipboard
```
agent-desktop clipboard-get # Read clipboard
agent-desktop clipboard-set "text" # Write to clipboard
agent-desktop clipboard-clear # Clear clipboard
```
### Wait
```
agent-desktop wait 1000 # Pause 1 second
agent-desktop wait --element @e5 --timeout 5000 # Wait for element
agent-desktop wait --window "Title" # Wait for window
agent-desktop wait --text "Done" --app "App" # Wait for text
agent-desktop wait --menu --app "App" # Wait for context menu
agent-desktop wait --menu-closed --app "App" # Wait for menu dismissal
agent-desktop wait --notification --app "App" # Wait for new notification
```
### System
```
agent-desktop status # Health check
agent-desktop permissions # Check permission
agent-desktop permissions --request # Trigger permission dialog
agent-desktop version --json # Version info
agent-desktop batch '[...]' --stop-on-error # Batch commands
```
## Key Principles for Agents
1. **Always snapshot first.** Never assume UI state.
2. **Use `-i` flag.** Filters to interactive elements only, reducing tokens.
3. **Refs are ephemeral.** Snapshot again after any UI-changing action.
4. **Prefer refs over coordinates.** `click @e5` > `mouse-click --xy 500,300`.
5. **Use `wait` for async UI.** After launch/dialog triggers, wait for expected state.
6. **Check permissions first.** Run `permissions` on first use.
7. **Handle errors.** Parse `error.code` and follow `error.suggestion`.
8. **Use `find` for targeted searches.** Faster than full snapshot when you know role/name.
9. **Use surfaces for menus.** `snapshot --surface menu` captures open menus.
10. **Batch for performance.** Multiple commands in one invocation.
don't have the plugin yet? install it then click "run inline in claude" again.
CLI tool enabling AI agents to observe and control desktop applications via native OS accessibility trees.
core principle: agent-desktop is NOT an AI agent. it is a tool that AI agents invoke. it outputs structured JSON with ref-based element identifiers. the observation-action loop lives in the calling agent.
use agent-desktop when you need to automate desktop GUI interactions without writing platform-specific code. the skill lets you click buttons, fill forms, navigate menus, read UI state, toggle checkboxes, scroll, drag, type text, take screenshots, manage windows, and access clipboard across 54 commands. triggers on: "click button", "fill form", "open app", "read UI", "automate desktop", "accessibility tree", "snapshot app", "type into field", "navigate menu", "toggle checkbox", "take screenshot", or any desktop GUI interaction task. currently supports macOS 12+ with Windows and Linux planned.
installation:
npm install -g agent-desktop
# or
bun install -g --trust agent-desktop
system requirements:
agent-desktop launch if needed)environment setup:
agent-desktop permissionsagent-desktop permissions --requestreference documentation (external):
references/commands-observation.md: snapshot, find, get, is, screenshot, list-surfaces with all flags and examplesreferences/commands-interaction.md: click, type, set-value, select, toggle, scroll, drag, keyboard, mouse command selectionreferences/commands-system.md: launch, close, windows, clipboard, wait, batch, status, permissions, versionreferences/workflows.md: 12 common patterns (forms, menus, dialogs, scroll-find, drag-drop, async wait, anti-patterns)references/macos.md: macOS permissions/TCC, AX API internals, smart activation chain, surfaces, Notification Center, troubleshootingevery automation follows the observe-act loop:
observe: run agent-desktop snapshot --app "App Name" -i to fetch the current accessibility tree with refs for interactive elements only. parse the JSON output to locate target elements by ref (@e1, @e2, etc.). store the refmap in memory; refs are scoped to this snapshot and become stale after UI changes.
reason: examine the JSON structure, identify the interactive element (button, textfield, checkbox, link, menuitem, tab, slider, combobox, treeitem, cell) matching your intent. note its ref identifier.
act: invoke the appropriate interaction command (click, type, select, toggle, scroll, drag, etc.) with the target ref. example: agent-desktop click @e5 or agent-desktop type @e2 "hello". if the action requires coordinates instead, use mouse-click --xy X,Y.
verify: run agent-desktop snapshot --app "App Name" -i again to confirm the UI state changed as expected. parse the new refmap and continue if more actions are needed.
repeat: loop through steps 1-4 until the task is complete (form submitted, menu closed, file saved, etc.).
ref system:
command groups (54 total):
observation (6 commands):
agent-desktop snapshot --app "App" -i # accessibility tree with refs
agent-desktop screenshot --app "App" out.png # PNG screenshot
agent-desktop find --app "App" --role button # search by role/name
agent-desktop get @e1 --property text # read element property
agent-desktop is @e1 --property enabled # check element state
agent-desktop list-surfaces --app "App" # available surfaces (menu, tooltip, etc.)
interaction (17 commands):
agent-desktop click @e5 # click element
agent-desktop double-click @e3 # double-click
agent-desktop triple-click @e2 # triple-click (select line)
agent-desktop right-click @e5 # right-click (context menu)
agent-desktop type @e2 "hello" # type text into focused element
agent-desktop set-value @e2 "new value" # set value directly (bypass focus)
agent-desktop clear @e2 # clear element value
agent-desktop focus @e2 # set keyboard focus
agent-desktop select @e4 "Option B" # select dropdown option by label
agent-desktop toggle @e6 # toggle checkbox or switch
agent-desktop check @e6 # idempotent check (enable)
agent-desktop uncheck @e6 # idempotent uncheck (disable)
agent-desktop expand @e7 # expand disclosure/collapse group
agent-desktop collapse @e7 # collapse disclosure/collapse group
agent-desktop scroll @e1 --direction down # scroll element (up/down/left/right)
agent-desktop scroll-to @e8 # scroll element into view
agent-desktop press return --app "App" # send key or combo to app
keyboard & mouse (12 commands):
agent-desktop press cmd+c # key combo (cmd, ctrl, shift, option)
agent-desktop key-down shift # press and hold key
agent-desktop key-up shift # release held key
agent-desktop hover @e5 # move cursor to element
agent-desktop hover --xy 500,300 # move cursor to coordinates
agent-desktop drag --from @e1 --to @e5 # drag between elements
agent-desktop mouse-click --xy 500,300 # click at coordinates
agent-desktop mouse-move --xy 100,200 # move cursor to coordinates
agent-desktop mouse-down --xy 100,200 # press mouse button
agent-desktop mouse-up --xy 300,400 # release mouse button
agent-desktop mouse-scroll --xy 500,300 --direction down # scroll at coordinates
agent-desktop mouse-scroll-momentum --xy 500,300 --direction down --amount 5 # momentum scroll
app & window (12 commands):
agent-desktop launch "System Settings" # launch app and wait for startup
agent-desktop close-app "TextEdit" # quit gracefully
agent-desktop close-app "TextEdit" --force # force kill (SIGKILL)
agent-desktop list-windows --app "Finder" # list open windows
agent-desktop list-apps # list all running GUI apps
agent-desktop focus-window --app "Finder" # bring window to front
agent-desktop resize-window --app "App" --width 800 --height 600 # resize
agent-desktop move-window --app "App" --x 0 --y 0 # reposition
agent-desktop minimize --app "App" # minimize window
agent-desktop maximize --app "App" # maximize window
agent-desktop restore --app "App" # restore from minimized/maximized
agent-desktop activate-window --app "App" --title "Partial Title" # activate by app and optional title
notifications (6 commands):
agent-desktop list-notifications # list all system notifications
agent-desktop list-notifications --app "Slack" # filter by app
agent-desktop list-notifications --text "deploy" --limit 5 # filter by text content
agent-desktop dismiss-notification 1 # dismiss by index
agent-desktop dismiss-all-notifications # dismiss all
agent-desktop notification-action 1 --action "Reply" # click action button on notification
clipboard (3 commands):
agent-desktop clipboard-get # read clipboard contents
agent-desktop clipboard-set "text" # write to clipboard
agent-desktop clipboard-clear # clear clipboard
wait (6 commands):
agent-desktop wait 1000 # pause (ms)
agent-desktop wait --element @e5 --timeout 5000 # wait for element to exist
agent-desktop wait --window "Title" # wait for window to open
agent-desktop wait --text "Done" --app "App" # wait for text in accessibility tree
agent-desktop wait --menu --app "App" # wait for context menu to open
agent-desktop wait --menu-closed --app "App" # wait for menu to dismiss
system (5 commands):
agent-desktop status # health check
agent-desktop permissions # check accessibility permission
agent-desktop permissions --request # trigger permission dialog
agent-desktop version --json # version info
agent-desktop batch '[...]' --stop-on-error # batch multiple commands
permission flow:
agent-desktop status returns error code PERM_DENIED.agent-desktop permissions --request to trigger the system dialog, or direct user to System Settings > Privacy > Accessibility > add terminal app.PERM_DENIED until permission is granted.app not running:
snapshot --app "App Name" returns error code APP_NOT_FOUND.agent-desktop launch "App Name" first, then retry snapshot.element not found (stale ref or wrong ref):
ELEMENT_NOT_FOUND.snapshot --app "App" to fetch fresh refs, then re-issue the action with the new ref.action not supported by element:
toggle on a read-only checkbox), the command returns error code ACTION_NOT_SUPPORTED.click instead of toggle, or use coordinate-based mouse-click).timeout during wait:
wait command does not meet its condition within the --timeout window (default 5000ms), it returns error code TIMEOUT.--timeout for slow apps or network delays, or verify the expected state with snapshot to see if the condition is actually met.async/delayed UI changes:
agent-desktop wait --element @e5 --timeout 5000 or wait --text "Label" to block until the new state exists, then snapshot again.ref stability across snapshots:
snapshot after any action that modifies the DOM (click, type, toggle, scroll, etc.) before using old refs.STALE_REF errors on subsequent actions.rate limits and performance:
agent-desktop batch to reduce per-command overhead; add deliberate waits between rapid actions; profile with time to detect bottlenecks.network and file dependencies:
agent-desktop screenshot --app "App" /tmp/out.png).terminal accessibility permission expiry:
agent-desktop permissions --request to re-grant access.PERM_DENIED until permission is re-granted.coordinate-based fallback:
agent-desktop screenshot --app "App" to get coordinates, then use mouse-click --xy X,Y, hover --xy X,Y, or drag --from --to with coordinates.every command returns JSON on stdout following this envelope:
success:
{
"version": "1.0",
"ok": true,
"command": "snapshot",
"data": { ... }
}
error:
{
"version": "1.0",
"ok": false,
"command": "click",
"error": {
"code": "STALE_REF",
"message": "ref @e5 not found in current refmap",
"suggestion": "re-run snapshot to fetch fresh refs"
}
}
exit codes:
0: success (ok=true)1: structured error (ok=false with error.code)2: argument/syntax error (invalid command or flags)snapshot data format (observation commands):
data.tree: accessibility tree as nested JSON. each node has role, label (optional), children (array), ref (string, only for interactive elements).data.refmap: flat dict mapping ref to element info, e.g. {"@e1": {"role": "button", "label": "OK", "enabled": true}, "@e2": {"role": "textfield", "label": "Username", "value": ""}}.data.app: app bundle name (e.g., "com.apple.finder").data.window: window title if applicable.screenshot data format:
data.path: file path where PNG was written (absolute path).data.size: { "width": number, "height": number } in pixels.find data format:
data.matches: array of matching elements, each with ref, role, label, enabled, position (x, y, width, height).get/is data format:
data.value: the property value (string, boolean, number, or null).data.property: the property name that was retrieved.list-windows data format:
data.windows: array of window objects, each with title (string), position (x, y), size (width, height), focused (boolean).list-apps data format:
data.apps: array of app names (bundle identifiers or display names).list-notifications data format:
data.notifications: array of notification objects, each with index (int), app (string), title (string), body (string), actions (array of action strings).clipboard data format:
data.text: clipboard contents as string (or null if empty).wait data format:
data.elapsed: milliseconds waited before condition was met.data.condition: the wait condition that was satisfied (e.g., "element @e5 exists", "text 'Done' found").error codes and recovery:
| code | meaning | recovery |
|---|---|---|
PERM_DENIED |
accessibility permission not granted | run agent-desktop permissions --request or add terminal to System Settings > Privacy > Accessibility |
ELEMENT_NOT_FOUND |
ref not in current refmap | re-run snapshot, use fresh ref |
APP_NOT_FOUND |
app not running | launch app with agent-desktop launch "App Name" |
ACTION_FAILED |
AX action rejected by OS | try alternative approach or coordinate-based command (mouse-click --xy) |
ACTION_NOT_SUPPORTED |
element cannot perform this action | use different command (e.g., click instead of toggle) |
STALE_REF |
ref from old snapshot, UI has changed | re-run snapshot immediately |
WINDOW_NOT_FOUND |
no window matching app name or title | check app name with list-windows or list-apps |
TIMEOUT |
wait condition not met within timeout window | increase --timeout value or verify condition with snapshot |
INVALID_ARGS |
bad command arguments or syntax | check command flags and argument order in reference docs |
RATE_LIMITED |
AX API overloaded | add delay with wait or batch related commands |
FILE_IO_ERROR |
screenshot or file write failed | ensure output directory is writable and has disk space |
you know the skill worked when:
snapshot returns a non-empty refmap: parse data.refmap and confirm refs exist for target elements. if refmap is empty, the accessibility tree is broken or the app does not expose UI.