{"slug": "show-hn-offscreen-give-your-agents-a-mac-screen-of-their-own", "title": "Show HN: Offscreen - Give your agents a Mac screen of their own", "summary": "Mark Phelps released Offscreen, a beta tool that runs coding agents' macOS UI tests, iOS Simulator sessions and GUI app launches inside throwaway macOS virtual machines instead of on the user's own desktop. Offscreen requires an Apple silicon Mac running macOS 14 or later, full Xcode and Homebrew, and installs via `brew install --cask markphelps/tap/offscreen`; it consists of the `offscreen` CLI (aliased `ofs`), the `offscreend` daemon and a menu bar app, with jobs returning logs, screenshots and `.xcresult` bundles. The tool matters because agents running XCUITest or booting the simulator otherwise seize windows and focus on the developer's Mac, and Offscreen queues jobs from any number of agents, terminals and scripts into a fresh VM.", "body_md": "**Give your agents a Mac screen of their own. Offscreen runs UI tests, the iOS Simulator and GUI apps in throwaway VMs, so your desktop stays yours.**\n\nWhen a coding agent runs XCUITest, boots the iOS Simulator, or launches the app it just built, it takes over your Mac: windows pop up, focus jumps, and you can't type until it finishes. Offscreen runs that work in a fresh macOS VM instead. Any number of agents, terminals and scripts share one queue, and each job gives you back logs, screenshots and `.xcresult` bundles.\n\nNote\n\nOffscreen is beta software. Commands, config and on-disk state may change between versions. Please [open an issue](https://github.com/markphelps/offscreen/issues) if something breaks.\n\nYou need an Apple silicon Mac with macOS 14 or later, full Xcode, and [Homebrew](https://brew.sh/).\n\nInstall Offscreen and build its VM image:\n\n```\nbrew install --cask markphelps/tap/offscreen\n                           # installs the CLI, the daemon, the Offscreen\n                           # menu bar app and Tart\nopen -a Offscreen          # if macOS asks, allow \"Offscreen Daemon\" on the Local Network\noffscreen image build      # one-time: pulls a macOS + Xcode image, takes a while, go grab lunch\n```\n\nThe install also adds `ofs` as a shorter name for `offscreen`, so `ofs run hello test-ui` works too.\n\nThen try it on the example app. The example lives in this repository, so clone it to get the project; you don't build Offscreen itself:\n\n```\ngit clone https://github.com/markphelps/offscreen.git\ncd offscreen/examples/hello\noffscreen run hello test-ui\n```\n\nThe last command builds a small SwiftUI app on your Mac, runs its UI test inside a fresh VM, streams the output, and prints the job's artifact directory. Nothing opens on your desktop. The same example has two more tasks: `offscreen run hello test-ios` runs the test in an iOS Simulator inside the VM, and `offscreen run hello launch` opens the app and saves a screenshot. For problems, see the [install guide](https://github.com/markphelps/offscreen/blob/main/docs/install.md). It also covers [building from source](https://github.com/markphelps/offscreen/blob/main/docs/install.md#build-from-source) if you'd rather not use Homebrew.\n\nOffscreen has three parts: the `offscreen` CLI, the `offscreend` daemon that runs jobs, and the **Offscreen** menu bar app. The app owns the daemon: opening Offscreen starts it, and quitting Offscreen stops it. Its menu lists queued, running and recently finished jobs from both lanes, with Cancel, Reveal Artifacts and Copy Job ID for each, plus Restart Daemon and Quit Offscreen. If Offscreen isn't running, CLI commands say so: `Offscreen isn't running. Open it with: open -a Offscreen`.\n\nAnything that needs a logged-in Mac desktop:\n\n- **macOS UI tests.** XCUITest for Mac apps, with`.xcresult` bundles collected after each run.\n- **iOS Simulator tests.** XCUITest against an iPhone simulator inside the VM; the hello example runs on the default image with no extra setup. A fresh VM boots the simulator from scratch, so expect a few minutes per run, or warm it once in the golden image ([hello example](https://github.com/markphelps/offscreen/blob/main/examples/hello/README.md#offscreen-tasks) ).\n- **Launch and look.** Open the app your agent just built, drive it with AppleScript or System Events, and save screenshots for the agent (or you) to check.\n- **One-off commands.**`offscreen run myapp -- <cmd>` runs any command in a fresh VM, no task definition needed.\n\nAt the repo root, write a starter `.offscreen.toml`, fill it in, and check it:\n\n```\nofs init     # writes .offscreen.toml with every section commented out\nofs check    # validates it and lists its tasks, without starting a VM\n```\n\nA filled-in contract looks like this:\n\n```\nproject = \"myapp\"\n\n[prepare]   # runs on your Mac before each job\nrun = \"xcodebuild -scheme MyApp -derivedDataPath .build build\"\nshare = [\".build/Build/Products/Debug/MyApp.app\"]\n\n[tasks.ui]  # runs inside the VM\nlane = \"vm\"\nrun = \"sh scripts/ui-test.sh\"\n```\n\nThen tell your agent to run this instead of `xcodebuild test` or launching the app itself:\n\n```\noffscreen run myapp ui\noffscreen run myapp -- swift test   # or any one-off command\n```\n\nTo teach your agent the rest (reading exit codes and artifacts, queuing long jobs, and what not to touch), install the [offscreen skill](https://github.com/markphelps/offscreen/blob/main/skills/offscreen/SKILL.md):\n\n```\nnpx skills add markphelps/offscreen      # add -g to install for all projects\n```\n\nOr, without the skill, add a line like this to your `AGENTS.md` or `CLAUDE.md`:\n\n```\nRun UI tests with `offscreen run myapp ui`. Never run `xcodebuild test` directly; it takes over the user's screen.\n```\n\n`offscreen run` exits with the wrapped command's status, so agents and scripts read pass/fail the usual way. Offscreen's own failures use separate codes: 75 for a queue timeout, 70 for VM or daemon errors. Tasks write screenshots and results to `$OFFSCREEN_OUT`, and `offscreen artifacts <job>` prints where those ended up. The [config reference](https://github.com/markphelps/offscreen/blob/main/docs/config.md) covers guest setup, caches and environment variables.\n\n- **Running tests on your own desktop.** This works fine until a test or the Simulator opens a window while you're typing. Offscreen runs the same`xcodebuild` commands in a VM, so you keep working.\n- **Running Tart yourself.**[Tart](https://github.com/cirruslabs/tart) is the VM layer offscreen uses. Offscreen adds the parts you'd otherwise script by hand: a per-job clone of a golden image, copying the worktree and build products in, collecting artifacts, cleaning up afterwards, and a single queue for every caller, capped at Apple's limit of two macOS VMs.\n- **Hosted macOS CI** (GitHub Actions, Cirrus, and others). CI runs after you push. Offscreen runs locally whenever you or your agent call it, before you commit. It doesn't replace CI.\n\n| Command | What it does | \n|---|---|\n| `offscreen run <project> <task>` | Queue a named task, stream output, and wait for its result. | \n| `offscreen run <project> -- <cmd>` | Run a one-off command in a VM. | \n| `offscreen init` | Write a starter `.offscreen.toml` in the current directory. | \n| `offscreen check` | Validate `.offscreen.toml` and list its tasks, without starting a VM. | \n| `offscreen status` | List queued and active jobs; `-a` adds completed ones. | \n| `offscreen logs <job>` | Print a job's output so far; `-f` follows it. | \n| `offscreen artifacts <job>` | Print a job's artifact directory. | \n| `offscreen cancel <job>` | Cancel a job and wait for cleanup. | \n\nUseful `run` flags: `--detach` returns right away, `--keep` keeps a failed VM for inspection, `--show` opens the VM in a window so you can watch it, and `--timeout` limits how long a job can wait and run. See the [command reference](https://github.com/markphelps/offscreen/blob/main/docs/cli.md) for all commands, flags, and exit codes.\n\nSome checks can't run in a VM. A task with `lane = \"host\"` runs on your actual desktop, one job at a time, and only when you're idle: the screen must be unlocked and awake, the camera and microphone off, and you away for long enough. A countdown lets you say **Not now** first. If you come back mid-job, a notification lets you take the desktop back. The [Offscreen menu bar app](https://github.com/markphelps/offscreen/blob/main/app/README.md) watches the desktop for this lane; it asks for Input Monitoring only once you queue a host job. Automatic return detection stays off until you check it on your Mac. Read [lanes and safety](https://github.com/markphelps/offscreen/blob/main/docs/lanes.md) before turning it on.\n\nI use Offscreen to develop and test my three macOS apps:\n\n- [Cove](https://covereader.com) : a local-first RSS reader that keeps the full articles you subscribe to.\n- [Vigil](https://vigilmac.com) : a menu bar app that keeps your Mac awake while coding agents work.\n- [Ramble](https://ramblemac.com) : point at something on screen and talk; your coding agent gets screenshots and a transcript.\n\n- [Install guide](https://github.com/markphelps/offscreen/blob/main/docs/install.md) : Homebrew, building from source, upgrades, and permissions\n- [Configuration](https://github.com/markphelps/offscreen/blob/main/docs/config.md) : project contract and user config\n- [Command reference](https://github.com/markphelps/offscreen/blob/main/docs/cli.md) : every command, flag, and exit code\n- [Lanes and safety](https://github.com/markphelps/offscreen/blob/main/docs/lanes.md) : how the VM and host lanes isolate work\n- [Daemon operation](https://github.com/markphelps/offscreen/blob/main/DAEMON.md) : state, restarts, and cleanup\n- [Architecture](https://github.com/markphelps/offscreen/blob/main/ARCHITECTURE.md) : components, job lifecycle, and invariants\n- [Hello example](https://github.com/markphelps/offscreen/blob/main/examples/hello/README.md) : the sample app and its tasks\n- [Contributing](https://github.com/markphelps/offscreen/blob/main/CONTRIBUTING.md) : developer checks and local end-to-end testing", "url": "https://wpnews.pro/news/show-hn-offscreen-give-your-agents-a-mac-screen-of-their-own", "canonical_source": "https://github.com/markphelps/offscreen", "published_at": "2026-10-17 12:13:15+00:00", "updated_at": "2026-10-17 12:32:53.101338+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Offscreen", "Mark Phelps", "Apple", "Xcode", "Homebrew", "Tart", "iOS Simulator", "XCUITest"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-offscreen-give-your-agents-a-mac-screen-of-their-own", "markdown": "https://wpnews.pro/news/show-hn-offscreen-give-your-agents-a-mac-screen-of-their-own.md", "text": "https://wpnews.pro/news/show-hn-offscreen-give-your-agents-a-mac-screen-of-their-own.txt", "jsonld": "https://wpnews.pro/news/show-hn-offscreen-give-your-agents-a-mac-screen-of-their-own.jsonld"}}