Back to Maui

`maui` CLI Design Document

docs/design/cli.md

10.0.5119.3 KB
Original Source

maui CLI Design Document

Overview

The maui CLI is a command-line tool for .NET MAUI development that provides two main capabilities:

  1. Environment setup — manages Android SDK/JDK, Xcode runtimes, simulators, and emulators
  2. App inspection — captures screenshots, streams logs, and inspects the visual tree of running apps

It is designed for three consumers: AI agents, CI/CD pipelines, and humans.

Full specification: PR #33865 — covers architecture, error contracts, IDE integration, JSON schemas, and vNext roadmap.

Motivation

The "vibe coding" experiment with WPF (vibe-wpf) showed that AI agents can effectively develop applications when given the right tools. .NET MAUI, however, spans multiple platforms with different command-line interfaces: capturing a screenshot on iOS requires xcrun simctl io booted screenshot, while Android uses adb exec-out screencap. Similarly, log access, visual tree inspection, and device management all have platform-specific implementations.

The maui CLI provides a unified interface across Android, iOS, macOS, Windows, and Mac Catalyst, making these operations simple and consistent for both developers and AI agents.

Design Principles

  1. Delegate to native toolchains — wraps sdkmanager, adb, xcrun simctl, etc.
  2. Reuse shared libraries — leverages dotnet/android-tools (Xamarin.Android.Tools.AndroidSdk) for SDK/JDK discovery, and contributes new capabilities (JDK installation, SDK bootstrap, license acceptance) back to it.
  3. Machine-first output — every command supports --json
  4. Stateless — each command reads state, acts, and exits
  5. Complement dotnet run — uses the same device identifiers and framework options as dotnet run for .NET MAUI

Goals

  1. Environment setup: Manage Android SDK/JDK, Xcode runtimes, simulators, and emulators from a single tool

  2. Screenshot capture: Enable AI agents to capture screenshots of running .NET MAUI applications to validate visual changes

  3. Log access: Provide unified access to platform-specific device logs (logcat, Console, etc.)

  4. Visual tree inspection: Allow agents to inspect the runtime visual tree structure and properties (.NET MAUI visual tree)

  5. Developer experience: Integrate seamlessly with existing dotnet CLI workflows, this should fit in with dotnet run, dotnet watch, etc.

Installation and Invocation

The CLI is available through multiple invocation methods:

bash
# Direct tool invocation (after install)
maui screenshot -o screenshot.png

# Via the .NET CLI
dotnet maui screenshot -o screenshot.png

# Inline install and invocation (no prior install needed)
dotnet tool exec -y Microsoft.Maui.Cli screenshot -o screenshot.png

Installation

bash
# As a global tool
dotnet tool install --global Microsoft.Maui.Cli

# As a local tool (recommended for projects)
dotnet tool install Microsoft.Maui.Cli

# Restore local tools
dotnet tool restore

The tool installs as maui on PATH. All commands in this document use the maui form.

The .NET workload specification includes support for automatically installing tools from workloads via the tools-packs feature (see workload manifest specification). However, this feature is not yet implemented. Once available, Microsoft.Maui.Cli could be automatically installed when the maui workload is installed, eliminating the need for manual tool installation.

Until then, manual installation via dotnet tool install will be how we prove out the maui CLI.

Global Options

All commands support:

FlagDescription
--jsonStructured JSON output
--verboseDetailed logging
--interactiveControl interactive prompts (default: true for terminals, false in CI or when output is redirected)
--dry-runPreview actions without executing
--platform <p>Filter by platform: android, ios, maccatalyst, windows

Interactivity detection follows the same pattern as dotnet CLI — auto-detects CI environments (TF_BUILD, GITHUB_ACTIONS, CI, etc.) and checks Console.IsOutputRedirected.

Environment Setup Commands

Android

CommandDescription
maui android installInstall JDK + SDK + recommended packages
maui android install --accept-licensesNon-interactive install
maui android install --packages <list>Install specific packages
maui android jdk checkCheck JDK status
maui android jdk installInstall OpenJDK 21
maui android jdk listList installed JDKs
maui android sdk listList installed packages
maui android sdk list --availableShow available packages
maui android sdk install <packages>Install package(s)
maui android sdk accept-licensesAccept all licenses
maui android sdk uninstall <package>Uninstall a package
maui android emulator listList emulators
maui android emulator create <name>Create emulator (auto-detects system image)
maui android emulator start <name>Start emulator
maui android emulator stop <name>Stop emulator
maui android emulator delete <name>Delete emulator

Install paths and defaults are handled by dotnet/android-tools.

Apple (macOS only)

CommandDescription
maui apple install [--accept-license] [--runtime <version>]Optionally accepts Xcode license and installs simulator runtimes. Could prompt user to install Xcode in the future
maui apple checkCheck Xcode, runtimes, and environment status
maui apple xcode checkCheck Xcode installation and license
maui apple xcode listList Xcode installations
maui apple xcode select <path>Switch active Xcode
maui apple xcode accept-licenseAccept Xcode license
maui apple simulator listList simulators
maui apple simulator create <name> <type> <runtime>Create simulator
maui apple simulator start <id>Start simulator
maui apple simulator stop <id>Stop simulator
maui apple simulator delete <id>Delete simulator
maui apple runtime checkCheck runtime status
maui apple runtime listList installed runtimes
maui apple runtime list --allList all runtimes (installed and downloadable)
maui apple runtime install <version>Install an iOS runtime

License flag naming: Android uses accept-licenses (plural) because sdkmanager requires accepting multiple SDK component licenses. Apple uses accept-license (singular) because xcodebuild -license accept accepts one unified Xcode license agreement.

Implementation References

The maui CLI delegates to shared libraries for platform operations:

Androiddotnet/android-tools (Xamarin.Android.Tools.AndroidSdk):

FeatureImplementation
SDK discovery, bootstrap & license acceptanceSdkManager
JDK discovery & installationJdkInstaller
ADB device managementAdbRunner
AVD / Emulator managementAvdManagerRunner, EmulatorRunner

Apple — wraps native toolchains directly:

FeatureNative tool
Simulator managementxcrun simctl (list, create, boot, shutdown, delete)
Runtime managementxcrun simctl runtime (list, add)
Xcode managementxcode-select, xcodebuild -license
Device detectionxcrun devicectl list devices (physical), xcrun simctl list (simulators)

Apple operations use AppleDev.Tools for simctl and devicectl wrappers.

Exit Codes

All commands use consistent exit codes:

CodeMeaning
0Success
1General error
2Environment/configuration error
3Permission denied (elevation required)
4Network error (download failed)
5Resource not found

App Inspection Commands (Future)

Note: App inspection commands are planned for a future release. The initial release focuses on environment setup and device management.

Device Selection Options

App inspection commands will follow the conventions established by dotnet run for .NET MAUI, using the same device selection and framework options:

-f|--framework <FRAMEWORK>     Target framework (e.g., net10.0-android, net10.0-ios)
-d|--device <DEVICE_ID>        Target device identifier (from --list-devices)
--list-devices                 List available devices/emulators/simulators
-p|--project <PATH>            Path to the .NET MAUI project (default: current directory)
-h|--help                      Show help information
--version                      Show version information

Interactive Prompting Behavior:

When -f|--framework is not specified:

  • If a project file exists and has multiple target frameworks, the CLI prompts to select one

  • If no project file is present, the CLI prompts from known .NET MAUI target frameworks (e.g., net10.0-android, net10.0-ios, net10.0-maccatalyst, net10.0-windows)

When -d|--device is not specified and a framework is selected:

  • The CLI prompts to select from available devices/emulators/simulators for that platform

  • Device list comes from the same ComputeAvailableDevices MSBuild target used by dotnet run

Note: The -d|--device option uses the same device identifiers returned by dotnet run --list-devices.

Commands

screenshot

Captures a screenshot of the currently running .NET MAUI application.

Usage:

bash
maui screenshot [options]

Options:

  • -o|--output <PATH>: Output file path (default: screenshot_{timestamp}.png)
  • -w|--wait <SECONDS>: Wait before capturing (default: 0)

Platform Implementation:

Initial implementation targets Android and iOS/Mac Catalyst, with Windows and macOS support planned as described below.

  • Android: Uses adb exec-out screencap -p
  • iOS/Mac Catalyst: Uses xcrun simctl io booted screenshot <file> for simulator; physical device capture via Xcode tooling (future)
  • Windows (planned): Uses Windows screen capture APIs to capture the active app window or full screen.
  • macOS (planned): Uses macOS screen capture APIs or command-line tooling to capture the active app window or full screen.

Future Commands

  • maui device list for unified device/emulator/simulator listing across platforms
  • maui screenshot for capturing screenshots of running apps
  • maui logs for streaming device logs
  • maui tree for inspecting the visual tree

Integration with dotnet run and dotnet watch

The CLI is designed to work seamlessly with existing .NET workflows:

Example Workflow

bash
# Terminal 1: Run application with hot reload
dotnet watch run

# Terminal 2: Inspect application
maui screenshot --output iteration1.png
maui logs --follow --filter "MyApp"    # future
maui tree --json                       # future

AI Agent Workflow

bash
# 1. Make code changes
# ... agent modifies MainPage.xaml ...

# 2. Wait for hot reload to complete
sleep 2

# 3. Capture screenshot
maui screenshot -o current.png

# 4. Analyze visual tree (future)
maui tree --json

# 5. Check logs for errors (future)
maui logs --level error

# 6. Agent analyzes outputs and decides next steps

Platform-Specific Considerations

Android

  • Device Detection: adb devices
  • Screenshots: adb exec-out screencap -p or UI Automator
  • Logs: adb logcat with package filtering

iOS / Mac Catalyst

  • Device Detection: xcrun simctl list devices (simulators), xcrun devicectl list devices (physical devices) — via AppleDev.Tools

  • Screenshots: xcrun simctl io booted screenshot <file> (simulators), iOS physical devices (future)

  • Logs: xcrun simctl spawn booted log stream or Console.app (simulators), mlaunch --logdev (physical devices)

Security and Privacy

The CLI is designed for development and debugging scenarios only:

  1. Debug builds only: Features should be disabled in Release builds using trimmer feature flags or #if DEBUG conditionals

  2. Reuse existing infrastructure: Leverage existing transport mechanisms (debugger, Hot Reload) rather than creating new communication channels

  3. No production exposure: Except when using standard OS features (like screenshots and logs), the CLI should not be usable against production applications

IDE Integration

The maui CLI and its underlying libraries are designed to be the shared backend for IDE extensions, eliminating duplicate environment detection and setup logic across tools.

Architecture

┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐
│   VS Code ext    │    │  Visual Studio    │    │    AI Agent      │
│   (vscode-maui)  │    │   extension       │    │  (Copilot, etc.) │
└────────┬─────────┘    └────────┬──────────┘    └────────┬─────────┘
         │                       │                        │
    spawns CLI            references NuGet           spawns CLI
         │                  library directly              │
         │                       │                        │
         ▼                       ▼                        ▼
  ┌──────────────┐    ┌────────────────────┐    ┌──────────────┐
  │  maui CLI    │    │  android-tools     │    │  maui CLI    │
  │  (process)   │    │  (in-process)      │    │  (--json)    │
  └──────┬───────┘    └────────┬───────────┘    └──────┬───────┘
         │                     │                       │
         └─────────┬───────────┴───────────────────────┘
                   │ spawns native tools
       ┌───────────┼───────────┐
       ▼           ▼           ▼
 ┌───────────┐ ┌──────────┐ ┌──────────┐
 │ adb       │ │  xcrun   │ │ Windows  │
 │ sdkmanager│ │  simctl  │ │   SDK    │
 └───────────┘ └──────────┘ └──────────┘

Integration Modes

ConsumerIntegrationRationale
Visual Studio extensionReferences android-tools NuGet package directly (in-process).NET extension — no serialization overhead, direct API access
VS Code (vscode-maui)Spawns maui CLI process, parses --json stdoutTypeScript extension — CLI is the natural process boundary
AI agents / CIInvokes maui CLI with --jsonProcess-based, language-agnostic
Terminal (human)Invokes maui CLI directlyHuman-readable output by default, --json when needed

Visual Studio consumes the Xamarin.Android.Tools.AndroidSdk NuGet package from dotnet/android-tools directly — the same library the CLI uses internally. This avoids process overhead and gives the VS extension full API access. Non-.NET consumers (VS Code, AI agents, CI) use the CLI as the canonical interface.

How IDEs Use It

WorkflowCLI commandIDE behavior
Workspace openmaui apple check --json, maui android jdk check --jsonShow environment status in status bar / problems panel
Environment fixmaui android install --jsonDisplay progress bar, stream type: "progress" messages
Device pickermaui device list --json (future)Populate device dropdown / selection UI
Emulator launchmaui android emulator start <name> --jsonShow notification, update device list on completion

Benefits

  • Consistent behavior — VS, VS Code, and CLI all use the same detection and setup logic (via shared libraries)
  • Single maintenance point — bug fixes in android-tools propagate to all consumers
  • AI-ready — agents use the same --json output that VS Code consumes
  • Flexible integration — .NET consumers go in-process, others use the CLI

Current Status

IntegrationStatus
VS Code extension (vscode-maui)✅ In progress
Visual Studio extensionPlanned (vNext)
GitHub Copilot / AI agents✅ Supported via --json output

Future Goals

MCP Server

An MCP (Model Context Protocol) server was considered. However, AI agents can effectively work with CLI commands through examples in copilot-instructions.md without requiring a custom MCP server.

If an MCP server is deemed useful after the CLI is completed, it could be a thin wrapper that exposes the CLI operations through the MCP protocol. Both Visual Studio and VS Code extensions provide options for distributing MCP servers, so we would likely do this through .NET MAUI tooling.

Decision: Build the CLI first. The MCP server can be added later if there's demonstrated need.

More Subcommands

Environment setup commands (Android SDK/JDK, Xcode, emulators, simulators) are now included above. These were inspired by:

Future commands:

  • maui device list for unified device listing
  • maui logs for viewing console output
  • maui tree for displaying the visual tree
  • maui screenshot for capturing screenshots

Decision: Environment setup ships first. Device listing and app inspection commands follow in a future release.

References