Screenshots from agents and CI
Let your coding agent capture and compose store screenshots over MCP, keep the recipe in demotake.json, and regenerate every size in CI when the UI changes, skipping screens that didn't.
Screenshots are made for automation. An agent can open the right screen, capture it, write the headline and export every size in one conversation. And the same recipe can live in your repository as code, so CI regenerates the screenshots on every release.
Ask your agent
With DemoTake connected (see Connect your agent), ask in plain words:
Take App Store screenshots of the dashboard and settings screens of the app we just built, with a headline on each, and export iPhone, iPad and Google Play sizes.
- screenshot_formatssizes and ids
- screenshot_captureper screen
- screenshot_composeheadlines · frame · previews
- screenshot_exportexact sizes + manifest
In DemoTake Agent, screenshot_capture can reach the screen first with steps, in the same vocabulary as demo specs:
{
"target": "app:com.example.Tidewell",
"shot": "Settings",
"headline": "Alerts your way",
"steps": [{ "key": "cmd+," }, { "wait": "General" }]
}During a recorded take, take_checkpoint with screenshot: true captures the scene's stable screen into a set named after the take, so one run gives you a demo video and its store screenshots. See the MCP tool reference for every parameter.
Screenshots as code
Add a screenshots section to demotake.json. Each shot names a screen, the steps that reach it, and its words. A spec can have only screenshots, only takes, or both.
{
"version": 1,
"target": { "bundle_id": "com.example.Tidewell" },
"screenshots": {
"set": "Tidewell store screenshots",
"formats": ["iphone", "ipad", "play", "og"],
"style": { "background": "midnight", "device_frame": "auto", "typography": "modern" },
"shots": [
{
"name": "Dashboard",
"headline": "Every metric at a glance",
"steps": [{ "click": "Overview" }, { "wait": "Good morning" }]
},
{
"name": "Settings",
"headline": "Alerts your way",
"steps": [{ "key": "cmd+," }, { "wait": "General" }],
"redact_regions": [{ "x": 0.62, "y": 0.08, "width": 0.3, "height": 0.05 }]
},
{ "name": "Phone", "capture": { "target": "sim:6C1F0B7E-3A2D-4F4B-9C1E-2B7D5A8E9F10" } }
]
}
}| Shot field | What it does |
|---|---|
name |
Identifies the shot across runs and names its files |
headline, subheadline |
The words on the screenshot |
setup, steps |
Steps that reach the screen: click, type, key, scroll, wait and the assert_* checks |
capture |
The target window (default), "display", a target id such as "sim:<UDID>", or { "rect": { "x", "y", "width", "height" } } in screen points |
redact |
Secret Guard on the capture (default true) |
redact_regions |
Regions to hide, 0 to 1 of the capture |
formats |
Formats for this shot only |
The set-level formats, style, headline, subheadline, file_type (png or jpeg) and setup apply to every shot. demotake schema prints the full JSON Schema for editor completion.
Run it
demotake screenshots validate demotake.json
demotake screenshots run demotake.json --out store-screenshots
demotake screenshots run demotake.json --out store-screenshots --only-changed --junit reports/screenshots.xml
demotake screenshots formats$ demotake screenshots run demotake.json --out store-screenshots --only-changed ✓ PASS Tidewell store screenshots 4.2s = Dashboard unchanged, footage kept ✓ Settings 0.9s · UI changed 8 screenshot(s) in store-screenshots (2 written, 7 unchanged)
--outcopies the export tree andmanifest.jsoninto a folder (defaultscreenshots). Files whose bytes didn't change aren't rewritten, so your repository only sees real changes.--only-changedcompares each new capture with the last one of the same shot, with the same perceptual diff demo specs use, ignoring hidden regions. Shots whose screen didn't change keep their capture and their files, byte for byte.--shot Home,Settingsand--formats iphone,ogrun part of the spec;--file-type jpegexports JPEG.--junitwrites a JUnit report with one test case per shot.
Exit codes are the same as the other demotake commands: 0 ok, 1 a shot failed or the spec is invalid, 2 usage error, 3 DemoTake isn't reachable, 4 DemoTake reported an error. If a shot fails, nothing is exported, so a broken build never overwrites good screenshots.
In CI
Run it on the same self-hosted Mac runner as your demo specs (see Demo specs and CI): build and launch the app, then run demotake screenshots run … --only-changed and upload the folder as an artifact or commit it. Specs whose shots have no steps also run in DemoTake Studio; steps need DemoTake Agent, which operates the app.
Keep the screenshots folder in the repository. With --only-changed and byte-stable rendering, a pull request that doesn't touch the UI doesn't change a single screenshot file.