Basic CLI commands: drive FlowDeck from any shell

Basic CLI commands: drive FlowDeck from any shell

Basic CLI commands: drive FlowDeck from any shell

In about 10 minutes you’ll install FlowDeck, launch the interactive TUI (which walks you through trial activation on first run), open or scaffold a project, build and run it, and wire up AI agent skills. The whole loop, driven from the keyboard. No Xcode UI.

In about 10 minutes you’ll install FlowDeck, launch the interactive TUI (which walks you through trial activation on first run), open or scaffold a project, build and run it, and wire up AI agent skills. The whole loop, driven from the keyboard. No Xcode UI.

Before you start

This guide assumes:

  • FlowDeck is installed and your license or trial is active. If not, follow the getting-started guide first.

  • You have an Xcode project to drive. Anything with an .xcodeproj or .xcworkspace works.

  • A terminal you're comfortable with. The CLI is also happy inside tmux, CI runners, or whatever shell you script in.

By the end you'll know how to configure a project, build, run, stream logs, run tests, manage simulators and devices, clean caches, and emit structured JSON from every command.

Discover and configure your project

Switch into your Xcode project's directory. FlowDeck reads the project from disk; no installation step inside Xcode, no fastlane init-style setup.

cd /path/to/your/project
flowdeck context
cd /path/to/your/project
flowdeck context
cd /path/to/your/project
flowdeck context

The context command parses the workspace and prints what it found: workspaces, schemes, build configurations, available simulators, and connected devices. It's the equivalent of xcodebuild -list, but it parses the project natively instead of compiling anything, so it returns instantly.

If you'd rather consume the result as data, ask for JSON:

flowdeck context --json
flowdeck context --json
flowdeck context --json

Pick a scheme to work with and save it so you don't have to repeat it on every command:

flowdeck config set -w MyApp.xcworkspace -s MyApp -S "iPhone 16"
flowdeck config set -w MyApp.xcworkspace -s MyApp -S "iPhone 16"
flowdeck config set -w MyApp.xcworkspace -s MyApp -S "iPhone 16"

To inspect the saved config, or to read individual values:

flowdeck config get
flowdeck config get --json
flowdeck config get
flowdeck config get --json
flowdeck config get
flowdeck config get --json

The config lives in .flowdeck/config.json alongside your project. Safe to check into version control (no secrets) or to .gitignore, whichever your team prefers. If you ever want to start fresh, flowdeck config reset wipes it.

Build, run, and stop

With the config saved, every subsequent command picks up the workspace, scheme, and simulator automatically. Run a build:

flowdeck build
flowdeck build
flowdeck build

FlowDeck shells out to xcodebuild under the hood; the build itself is identical to what Xcode runs, but the output is summarized into the lines that matter: compile errors, warnings, and the success/failure status. Pass --verbose for the raw xcodebuild output, or --json for a structured stream of build events.

To build, install, and launch in one command:

flowdeck run
flowdeck run
flowdeck run

FlowDeck boots the simulator named in your config (override with -S "iPhone 16 Pro" for a one-off, or -D "Daniel's iPhone" for a physical device). The output ends with the app's short ID, which addresses the running app from other commands:

# Example output (yours will differ):
App launched
   ID: a1b2c3d4 | MyApp | iPhone 16
# Example output (yours will differ):
App launched
   ID: a1b2c3d4 | MyApp | iPhone 16
# Example output (yours will differ):
App launched
   ID: a1b2c3d4 | MyApp | iPhone 16

List every app FlowDeck has running, and stop one (or all) cleanly:

flowdeck apps
flowdeck stop a1b2c3d4
flowdeck stop --all
flowdeck apps
flowdeck stop a1b2c3d4
flowdeck stop --all
flowdeck apps
flowdeck stop a1b2c3d4
flowdeck stop --all

FlowDeck refuses to silently replace a running app, so if flowdeck run blocks on "already running," flowdeck stop first.

Stream the app's logs

In a second terminal (so the first one stays free), find your running app and stream its output:

flowdeck apps
flowdeck logs <app-id

flowdeck apps
flowdeck logs <app-id

flowdeck apps
flowdeck logs <app-id

You'll see your print() calls, your OSLog messages, and any runtime errors the app emits, and only those. No SpringBoard, no nsurlsessiond, no other process bleeding into the stream.

Pass --json if you want each log line as a structured event (useful for CI artifacts or piping into jq):

flowdeck logs <app-id> --json | jq 'select(.level == "error")'
flowdeck logs <app-id> --json | jq 'select(.level == "error")'
flowdeck logs <app-id> --json | jq 'select(.level == "error")'

A shorthand for launching and streaming in one command:

flowdeck run --log
flowdeck run --log
flowdeck run --log

Discover and run tests

FlowDeck discovers tests by parsing your Swift source files directly. No build required. List what's available:

flowdeck test discover
flowdeck test discover
flowdeck test discover

Want it as data? Same flag pattern:

flowdeck test discover --json
flowdeck test discover --json
flowdeck test discover --json

Run a specific test, class, or whole target:

# A single test method
flowdeck test --only LoginTests/testValidLogin

# A whole test class
flowdeck test --only LoginTests

# A whole test target
flowdeck test --only MyAppTests
# A single test method
flowdeck test --only LoginTests/testValidLogin

# A whole test class
flowdeck test --only LoginTests

# A whole test target
flowdeck test --only MyAppTests
# A single test method
flowdeck test --only LoginTests/testValidLogin

# A whole test class
flowdeck test --only LoginTests

# A whole test target
flowdeck test --only MyAppTests

Skip slow tests instead of selecting fast ones:

flowdeck test --skip SlowIntegrationTests
flowdeck test --skip SlowIntegrationTests
flowdeck test --skip SlowIntegrationTests

Output is the same shape every time: a stream of pass/fail/skip events, plus a final summary. Add --json for a structured stream you can pipe into jq or a CI reporter.

Manage simulators and devices

List every simulator on the machine, in any state, with their runtime:

flowdeck simulator list
flowdeck simulator list --json
flowdeck simulator list
flowdeck simulator list --json
flowdeck simulator list
flowdeck simulator list --json

Boot a specific simulator by name (FlowDeck handles UDID lookup for you):

flowdeck simulator boot "iPhone 16 Pro"
flowdeck simulator shutdown "iPhone 16 Pro"
flowdeck simulator erase "iPhone 16 Pro"
flowdeck simulator boot "iPhone 16 Pro"
flowdeck simulator shutdown "iPhone 16 Pro"
flowdeck simulator erase "iPhone 16 Pro"
flowdeck simulator boot "iPhone 16 Pro"
flowdeck simulator shutdown "iPhone 16 Pro"
flowdeck simulator erase "iPhone 16 Pro"

Create a new simulator from a device type and runtime:

flowdeck simulator create --name "TestSim" --device-type "iPhone 16" --runtime "iOS 18.4"
flowdeck simulator create --name "TestSim" --device-type "iPhone 16" --runtime "iOS 18.4"
flowdeck simulator create --name "TestSim" --device-type "iPhone 16" --runtime "iOS 18.4"

Install a missing iOS runtime from Apple's catalog (handy in CI when a fresh image is missing a runtime your project needs):

flowdeck simulator runtime list
flowdeck simulator runtime install iOS 18.4
flowdeck simulator runtime list
flowdeck simulator runtime install iOS 18.4
flowdeck simulator runtime list
flowdeck simulator runtime install iOS 18.4

For physical devices, list what FlowDeck can see:

flowdeck device list
flowdeck device list --json
flowdeck device list
flowdeck device list --json
flowdeck device list
flowdeck device list --json

Pairing a device for the first time still happens through Xcode (plug in over USB, trust the prompt on the device). After that, FlowDeck sees the device for the rest of its life. The physical devices guide covers the one-time setup.

To target a device for a single build or run, swap the saved simulator for a device with the -D flag:

flowdeck run -D "Daniel's iPhone"
flowdeck run -D "My Mac"   # macOS apps
flowdeck run -D "Daniel's iPhone"
flowdeck run -D "My Mac"   # macOS apps
flowdeck run -D "Daniel's iPhone"
flowdeck run -D "My Mac"   # macOS apps

Clean, reset, and recover

Two levels of clean, depending on how stuck things are:

# Just this project's derived data
flowdeck clean

# Global DerivedData + Xcode caches + FlowDeck's own caches
flowdeck clean --all
# Just this project's derived data
flowdeck clean

# Global DerivedData + Xcode caches + FlowDeck's own caches
flowdeck clean --all
# Just this project's derived data
flowdeck clean

# Global DerivedData + Xcode caches + FlowDeck's own caches
flowdeck clean --all

When Swift Package Manager state is corrupt, force a re-resolve:

flowdeck project packages resolve
flowdeck project packages resolve
flowdeck project packages resolve

When code-signing fails (expired provisioning profile, new device, fresh CI runner), sync provisioning profiles from your Apple Developer account:

flowdeck project sync-profiles
flowdeck project sync-profiles
flowdeck project sync-profiles

To start a brand-new project from the CLI (the same wizard the TUI uses, but scriptable, so it works in CI or a non-interactive shell):

flowdeck project create --name MyApp --bundle-id com.example.MyApp \
  --platforms ios,macos --deployment-target 17.0
flowdeck project create --name MyApp --bundle-id com.example.MyApp \
  --platforms ios,macos --deployment-target 17.0
flowdeck project create --name MyApp --bundle-id com.example.MyApp \
  --platforms ios,macos --deployment-target 17.0

If you'd rather drive the same flow interactively, run flowdeck -i from an empty folder and the TUI's new-project wizard runs automatically. See the getting-started guide for the wizard walkthrough.

Speak JSON to scripts, CI, and agents

Every command accepts --json and emits versioned NDJSON: one event per line, schema documented and stable across minor releases. The same shape applies to build events, test events, log lines, simulator state, and UI automation results.

For a quick taste, watch a build's events as they happen:

flowdeck build --json | jq -r 'select(.type == "error") | "\(.file):\(.line)  \(.message)"'
flowdeck build --json | jq -r 'select(.type == "error") | "\(.file):\(.line)  \(.message)"'
flowdeck build --json | jq -r 'select(.type == "error") | "\(.file):\(.line)  \(.message)"'

The output is one event per line: build_started, compile, warning, error, build_completed. Filter, reformat, or hand it to an agent.

The same flag works on every command that produces output, useful when you want one consistent contract across your CI scripts:

flowdeck context --json
flowdeck test --only LoginTests --json
flowdeck logs <app-id> --json
flowdeck simulator list --json
flowdeck config get --json
flowdeck context --json
flowdeck test --only LoginTests --json
flowdeck logs <app-id> --json
flowdeck simulator list --json
flowdeck config get --json
flowdeck context --json
flowdeck test --only LoginTests --json
flowdeck logs <app-id> --json
flowdeck simulator list --json
flowdeck config get --json

Common patterns from here

Once the basic build-run-test loop feels natural, the patterns that come up most often:

Switch simulators or devices on the fly.
Any command accepts -S "iPhone 16 Pro" (simulator) or -D "Daniel's iPhone" (device) to override the saved config for a single call. To change the default, use flowdeck config set -S "iPhone 16 Pro".

Run on a physical device.
Pair and trust once in Xcode, then flowdeck run -D "Daniel's iPhone". After that, every command works the same as on a simulator. Logs, tests, UI automation, all of it.

Mix CLI and TUI in the same project.
The TUI (flowdeck -i) and the CLI share .flowdeck/config.json. Drive a build in the TUI, run a one-off scripted test from the CLI, then go back to the TUI. State stays consistent.

Wire FlowDeck into CI.
The same commands work in GitHub Actions, Bitrise, or any runner with macOS and Xcode. The CI automation guide walks through a working pipeline.

Use it from your editor.
The VS Code / Cursor extension surfaces the same commands as menu items and status-bar buttons. Install free from the marketplace.

How this compares to Apple's tools

For reference, the same five operations done with Apple's CLIs and with FlowDeck side by side.

What you're doing

Apple's tools

FlowDeck

Discover schemes

xcodebuild -list (2-5 seconds)

flowdeck context (native parse, instant)

Build and run on a simulator

xcodebuild + simctl install + simctl launch

flowdeck run -S "iPhone 16"

Stream this app's logs

simctl spawn booted log stream --predicate '...'

flowdeck logs <app-id>

Run one test

-only-testing:Target/Class/method

--only Class/method

Parse output

Regex over hundreds of lines of stdout

--json on any command

Full side-by-side at FlowDeck vs xcodebuild.

When things go wrong

flowdeck context says “No workspace found”

“Simulator not found” when running

Build succeeds but launch is blocked

No logs streaming

Code signing fails on a fresh machine or runner

Swift Package state is wedged