Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sim-cli

Agent-friendly iOS simulator CLI. Thin wrapper around simctl and an idb companion gRPC endpoint, with JSON in / JSON out for easy scripting.

Requirements

  • macOS with Xcode + xcrun simctl
  • Bun
  • An idb_companion running against your simulator (default: localhost:10882)
  • Optional: xcprettyrun pipes xcodebuild output through it when available

Install

bun install
bun run build    # produces ./dist/sim

Or run directly:

bun run src/index.ts --help

Usage

sim [--device <name|udid|booted>] [--companion <host:port>] <command> [args]

Defaults: --device booted, --companion localhost:10882. Overridable via SIM_DEVICE / IDB_COMPANION env vars (--udid / IDB_UDID still accepted). --device takes a simulator name (e.g. mango-qa, set via devices rename), a UDID, or booted; names resolve case-insensitively, preferring a booted match when several runtimes share the name.

Help is progressively disclosed: sim --help prints a grouped command overview; sim help <command> (or sim <command> --help) discloses the flags and details for one command; sim agent-context returns the full command schema as versioned machine-readable JSON (schema_version, per-command args/flags with enum values and aliases) — the layer an agent should consume to learn the surface without parsing help text.

Commands

Device

Command Description
devices list all simulators (alias: list-devices)
devices rename <device> <new_name> rename a simulator; the name then works anywhere --device is accepted
devices clone <source> <new_name> clone a simulator by name or UDID
devices boot <device> boot a simulator and wait until it is ready
devices shutdown <device>... shut down one or more explicit simulators by name or UDID; already-shutdown devices are unchanged
list-apps list installed apps
uninstall <bundle_id> remove app
keyboard status|connect|disconnect toggle the target device's live hardware keyboard via the Simulator I/O menu (enable/disable aliases); disconnect shows the software keyboard like a real device. Needs an open Simulator window + Accessibility permission; state persists (no auto-connect)
file <list|pull|push|delete|mkdir|mv> <bundle_id> ... read/write the app container (list/delete accept ls/rm aliases); --container app|data (default data), --dest <dir> for file pull

App

Command Description
build build once for generic/platform=iOS Simulator and print the .app path, for installing across a pool via run --app. Skips xcodebuild entirely (skipped: true) when the git tree is unchanged since the last build of the same workspace/project + scheme + configuration and that .app still exists; --force rebuilds anyway.
run <bundle_id> [args...] build → install → terminate prior → launch → wait → capture logs. Auto-detects .xcworkspace / .xcodeproj + scheme; override with --workspace, --project, --scheme, --configuration, or pass --app <path> to launch a prebuilt artifact instead of building. Builds skip when the git tree is unchanged, like build (built.skipped: true, --force overrides). Enabled LaunchAction env vars + command-line args from the matching .xcscheme are picked up automatically; pass extra/override env via --env KEY=VAL (repeatable). Detaches a verbose ndjson log stream to ~/.sim-cli/logs/<udid>.log (truncated each run, all subsystems) and reports {logs:{file,pid}}. The next run replaces that streamer; inspect or find it with logs / config.
openurl <url> open a URL / deep link

State — everything sim-cli persists lives under ~/.sim-cli/.

Command Description
config dump ~/.sim-cli/: the dir path, the companion registry (per-UDID idb companions, each with alive), captures (the log files run produced), builds (the build cache behind the unchanged-tree skip), and invocations (the invocation log).
logs [--device <name|udid>] list captured log files from prior runs — udid, file, size, modified, capturing, and live pid. Read a file directly with tail/jq. --device scopes to one sim.
defaults read|write|delete <domain> ... manage simulator defaults; write accepts --type string|bool|int|float
pasteboard get|set [value] read or replace the simulator pasteboard

Every invocation appends one JSON line to ~/.sim-cli/logs/invocations.jsonl: ts, ms, cmd, argv (with --env values redacted), cwd, pid, exit, and then either output — the shape of the result (field names and value types, arrays sampled from their first element, data-keyed dictionaries collapsed to *), never its content — or error on failure. The file is never rotated; delete it whenever.

jq -c 'select(.exit != 0) | {ts, cmd, error}' ~/.sim-cli/logs/invocations.jsonl

Observe

Command Description
screenshot [--out file.png] [--base64] capture screen as PNG
describe [--point x,y] [--screenshot] accessibility tree (+ optional base64 PNG)

Diagnostics — the app's simulator process is a plain host process, so these read it directly (no Xcode). All take the app by bundle id and resolve the pid through the target device's launchd, never by process name.

Command Description
stats <bundle_id> [--watch] resource gauges via proc_pid_rusage: CPU %, memory footprint (current + lifetime peak), disk I/O. One-shot adds net bytes for open sockets and the data container's size on disk; --watch emits one NDJSON line per second until the process exits. App process only — WKWebView helper processes are separate pids and excluded.
hierarchy <bundle_id> [--vc] [--out file.txt] UIKit view hierarchy (recursiveDescription), or the view-controller tree with --vc, via a batch lldb attach. Suspends the app for a few seconds and needs a debuggable (Debug) build; fails cleanly if another debugger is attached. Writes the tree to a file and returns its path.
memory [footprint] <bundle_id> categorized dirty-memory breakdown (footprint tool)
memory leaks <bundle_id> [--out file.txt] leaks scan — count + bytes inline, full report to file; briefly suspends the app
memory warn simulated memory warning, delivered device-wide
sample <bundle_id> [--duration s] [--out file.txt] CPU profile: 1ms call-stack sampling, no attach pause. Top-of-stack summary inline, full call tree to file.

Interact

Command Description
wait --label|--role|--text|--id <s> [--timeout ms] [--stable ms] [--missing] wait for a visible match, stable frame, or disappearance
tap <x> <y> [--duration s] tap at coordinates; or use a selector with --wait, --stable, and --settle to wait for a stable, hit-testable match
swipe <x1> <y1> <x2> <y2> [--duration s] [--delta n] swipe between points, or use --direction up|down|left|right with optional --edge, --distance, and an AX selector
drag <x1> <y1> <x2> <y2> [x y ...] one continuous touch: press (--press s to lift for drag & drop), move through every waypoint (--duration s per segment), optionally pause (--hold s), release at the last pair; start from an AX selector instead of the first pair
type "<string>" send keystrokes to focused field
fill --label|--role|--text|--id <s> "<value>" [--replace] [--wait ms] [--settle ms] tap a field, wait for focus, then append or replace its value
press <home|lock|siri|side_button|apple_pay> [--duration s] press a hardware button

All commands write JSON to stdout on success and {"error": "..."} to stderr on failure with a non-zero exit code.

License

MIT — see LICENSE. src/idb.proto is derived from facebook/idb (also MIT).

About

Let agent test your iOS apps

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.