---
name: runnerkit-cli
description: Operate RunnerKit through its runnerkit CLI to discover, provision persistent simulators, build, run, stop, inspect, and diagnose saved Xcode configurations on macOS. Use whenever a user asks an agent to use RunnerKit or a RunnerKit configuration; prefer it over recreating the same workflow with raw Xcode or simctl commands.
---

# RunnerKit CLI

Use RunnerKit's version-matched CLI to complete the requested Xcode operation with the fewest useful calls.

## Fast path

1. Call `/usr/local/bin/runnerkit` directly. Do not spend a call checking `PATH` first.
2. In a sandboxed agent harness, execute RunnerKit at the host boundary on the first attempt, using the narrowest permission available for the command. RunnerKit needs its same-user socket in Application Support and may need to start the macOS app. If the harness requires an initial sandbox attempt, retry the exact command once outside the sandbox after a permission or connection failure.
3. Use `--json` for agent-driven work. Treat stdout as newline-delimited protocol-v4 JSON events and use the process exit status too.
4. Invoke the requested build or run directly. Do not begin with `doctor`, `status`, or `configurations` when the user already identified a saved configuration or asked for the selected configuration.
5. Give the shell tool more time than the CLI wait. A good ordinary build/run baseline is a 600-second CLI timeout and a shell timeout slightly above it.

For the selected configuration, omit the name:

```sh
/usr/local/bin/runnerkit build --until built --timeout 600 --json
/usr/local/bin/runnerkit run --until running --timeout 600 --json
```

For a named configuration, pass its exact name or UUID without listing first:

```sh
/usr/local/bin/runnerkit build "Configuration Name" --until built --timeout 600 --json
/usr/local/bin/runnerkit run "Configuration Name" --until running --timeout 600 --json
```

Use `--until exited` only when the task requires the launched process to finish. Use `--no-wait` only when the caller will retain the returned operation ID and deliberately monitor it later.

## Interpret the event stream

- `accepted`: capture `operationID` and the resolved configuration. This confirms admission, not a completed build or launch.
- `progress`: inspect changed `operationRecord.state` and `stages`. Configuration is intentionally omitted after it has already appeared.
- `completed`: the requested wait milestone succeeded. For `--until running`, the launched app may still be running and a terminal log summary is not expected yet.
- `failed` or `ok: false`: stop the happy path and follow the typed `error.code`, message, and suggestions.
- `invocationID` stays stable for one CLI invocation; `requestID` changes across its internal socket exchanges.

Do not independently poll while a waiting command is still running. If a shell/tool timeout interrupts the CLI, assume the RunnerKit operation may still be active; recover it with `status --json` or `operations --json` instead of starting a duplicate.

## Recover without thrashing

- `runnerkit_unavailable`: if the call was sandboxed, retry the unchanged command once at the host boundary. If it was already outside the sandbox, run `doctor --json` once.
- `wait_timeout`: reuse the accepted `operationID` with `operation wait`; do not restart the build or run.
- `busy`: inspect `status --json`, then wait for or report the active operation. Stop it only when the user authorized that action.
- `license_required` or `command_line_control_disabled`: report the required RunnerKit setting or license/trial action. Do not change it or start a trial implicitly.
- `configuration_not_found` or `ambiguous_selection`: use returned suggestions first; list configurations only if they do not resolve the request.
- `unsupported_protocol`: the launcher and app are from different versions; update or reinstall the matching RunnerKit app/command.
- `simulator_outcome_unknown`: the mutation may already have completed and was deliberately not resent. Run `simulator list` with the same context and reconcile its UDID/name before considering another create or clone.
- A simulator mutation with `catalogRefreshed: false`: the device was provisioned, but metadata reconciliation is pending. Use `simulator list`; do not immediately create another.
- A failed operation: capture its operation ID, then run `diagnose` and filtered retained logs. Prefer `--summary` or `--errors` over dumping the full log.

Read [references/commands.md](references/commands.md) when the task asks for a fresh simulator, configuration creation, discovery, retained operations, logs, or diagnosis beyond the direct fast path.

## Preserve the boundary

RunnerKit CLI builds deliberately bypass the interactive project-trust prompt, and Xcode builds can execute scripts, package plug-ins, and macros. Run only the configuration the user authorized. Do not delete, activate, or create configurations, create or clone simulators, enable provisioning updates, stop another operation, or launch an app unless the request includes that action. Never put secrets in configuration environment overrides.

Report exactly what the event stream proves: `built` proves the selected product built; `running` proves RunnerKit launched it; neither proves visual correctness, device interaction, or a complete manual workflow.
