Skip to content

feat: add maui-sherpa CLI with AI-agent discoverability - #91

Merged
Redth merged 7 commits into
Redth:mainfrom
StephaneDelcroix:feature/cli
Mar 9, 2026
Merged

Redth merged 7 commits into
Redth:mainfrom
StephaneDelcroix:feature/cli

Conversation

@StephaneDelcroix

Copy link
Copy Markdown
Contributor

Summary

Adds a new dotnet tool (maui-sherpa) that exposes MAUI Sherpa's capabilities as a CLI designed for discoverability by Copilot CLI and other AI code agents.

Usage

# Install globally
dotnet pack src/MauiSherpa.Cli -c Release -o ./nupkg
dotnet tool install --global MauiSherpa.Cli --add-source ./nupkg

# Or run directly
dotnet run --project src/MauiSherpa.Cli -- --help

Command Tree

maui-sherpa
├── features                    — JSON manifest of all capabilities (AI discovery)
├── doctor [--json]             — environment health check
├── android
│   ├── sdk info|packages|install|uninstall
│   ├── emulators list|start|stop
│   ├── devices
│   └── keystores create|signatures
├── apple
│   ├── simulators list|boot|shutdown|create
│   ├── devices
│   └── xcode
├── workloads list|sets|info
└── version

Key Design

  • maui-sherpa features outputs a structured JSON manifest for AI agent tool discovery
  • --json global flag on every command for machine-readable output
  • Rich --help descriptions on all commands and subcommands
  • Lightweight — depends only on System.CommandLine + MauiSherpa.Workloads (no heavy cloud SDKs)
  • Wraps underlying tools directly (adb, xcrun simctl, keytool, sdkmanager, xcodebuild)
  • PackAsTool configured for dotnet tool install

Example Output

$ maui-sherpa doctor
MAUI Sherpa — Environment Health Check
══════════════════════════════════════════

  ✓ .NET SDK: v10.0.100
  ✓ Android SDK: ~/Library/Android/sdk
  ✓ JDK: openjdk version "21.0.9"
  ✓ Xcode: Xcode 26.1.1
  ✓ iOS Simulators: 66 simulator(s) available

  5/5 checks passed.

StephaneDelcroix and others added 6 commits March 7, 2026 22:38
Add a new dotnet tool (MauiSherpa.Cli) that exposes MAUI Sherpa's
capabilities as a command-line interface designed for discoverability
by Copilot CLI and other AI code agents.

Command tree:
- features       — JSON manifest of all capabilities (AI discovery)
- doctor         — environment health check (.NET SDK, Android SDK, JDK, Xcode)
- android sdk    — SDK info, packages, install/uninstall
- android emulators — AVD list, start, stop
- android devices — connected device listing
- android keystores — create keystores, view signatures
- apple simulators — list, boot, shutdown, create
- apple devices  — physical iOS device listing
- apple xcode    — Xcode installation info
- workloads      — .NET workload list, sets, detailed info

Key design:
- --json global flag on every command for machine-readable output
- Rich --help descriptions for AI agent tool discovery
- Lightweight: depends only on System.CommandLine + MauiSherpa.Workloads
- Wraps underlying tools (adb, xcrun, keytool, sdkmanager, xcodebuild)
- Packaged as dotnet tool (PackAsTool) for global/local install

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 'apple profiles' subcommand with list, show, install, and remove.
Decodes .mobileprovision files via 'security cms' to extract name, UUID,
team, type (Development/Ad Hoc/App Store/Enterprise), entitlements,
bundle ID, expiration, and provisioned device count.

Profiles can be resolved by UUID, file path, or name substring.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When an AI agent (Copilot CLI, etc.) runs 'maui-sherpa doctor --agent',
issues produce structured JSON with:
- diagnostics: full check results
- prompt: the same remediation prompt the GUI would send to its inner Copilot session
- guidance: per-category step-by-step fix instructions
- suggestedCommands: concrete maui-sherpa commands to run
- references: documentation URLs

This lets the outer agent act on the fix prompts directly, instead of
starting a nested Copilot session.

The Remediation helper also supports BuildProcessFailure and
BuildOperationFailure for future use by other commands.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
An AI agent's first contact is either --help or 'features'. Both now
prominently instruct agents to ALWAYS use --agent:

- Root --help: added 'AI AGENTS: Always pass --agent...' block
- features JSON: added 'importantForAgents' top-level field with
  clear instruction, plus 'globalFlags' array describing --agent
  response shape (whenAllOk vs whenIssuesFound)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 'create' and 'asc-list' subcommands to 'maui-sherpa apple profiles':

- create: Creates provisioning profiles on App Store Connect with support
  for all profile types (iOS, macOS, Mac Catalyst). Accepts bundle ID by
  identifier (auto-resolves to resource ID), auto-selects compatible
  certificates, and supports --all-devices for dev/adhoc profiles.
  Optional --install flag downloads and installs the profile locally.

- asc-list: Lists profiles from App Store Connect (vs local 'list').

Authentication via CLI options (--key-id, --issuer-id, --p8-file) or
environment variables (APPLE_KEY_ID, APPLE_ISSUER_ID, APPLE_P8_FILE).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update FeaturesCommand.cs apple.profiles entry to document the new
App Store Connect profile commands (create, asc-list) with usage hints
for API credentials and key options.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@StephaneDelcroix
StephaneDelcroix marked this pull request as ready for review March 8, 2026 18:55
Add real-time log streaming for Android and iOS devices:

- maui-sherpa android logs <serial>: Stream adb logcat with level/tag
  filtering, colored output, --clear, and --json NDJSON mode
- maui-sherpa apple logs <identifier>: Auto-detect simulator vs physical
  device, stream via xcrun simctl log stream or pymobiledevice3/idevicesyslog
  with level/process/subsystem filtering

New helpers:
- StreamingProcess: line-by-line async stdout streaming with cancellation
- LogParsers: lightweight logcat threadtime, simulator NDJSON, and physical
  device syslog parsers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Redth
Redth merged commit ea60775 into Redth:main Mar 9, 2026
7 checks passed
dalexsoto added a commit that referenced this pull request Mar 31, 2026
## Problem

The Doctor's 'Fix: Android SDK' action failed with:
  'Android SDK Directory was not specified.'

Even after fixing the upstream AndroidSdk.Tools library (PR #91), a
second issue remained: after successfully acquiring the SDK to
~/android-sdk, restarting the app caused the Doctor to pick up
~/.android instead — a user config directory, not an SDK installation.

## Root Cause

Two bugs contributed to the broken experience:

### 1. Upstream: SdkLocator discarded non-existent paths (fixed in AndroidSdk 0.35.1)

AndroidSdkManager and SdkTool constructors passed the user-specified
target directory through SdkLocator.Locate(), which only returns paths
where Directory.Exists() is true. When acquiring a fresh SDK, the
target directory doesn't exist yet, so:
  - Locate() discarded the user's path
  - It returned either null or a different directory (e.g. ~/.android)
  - DownloadSdk() then threw because it had no valid target

This was fixed upstream in Redth/AndroidSdk.Tools#91 and released
in AndroidSdk 0.35.1.

### 2. Local: Acquired SDK path was never persisted

After AcquireSdkAsync successfully installed the SDK to ~/android-sdk:
  - The in-memory _sdkManager was updated correctly
  - SdkPathChanged event was NOT fired (missing invoke)
  - The path was NOT saved to secure storage

On app restart, AndroidSdkSettingsService.InitializeAsync() found no
saved custom path and fell back to DetectSdkAsync(), which auto-
discovered ~/.android (a config directory created during the fix
process) instead of ~/android-sdk (the actual SDK installation).

## Changes

### AndroidSdkService.cs
- Fire SdkPathChanged event after successful SDK acquisition so that
  listeners (device watchers, UI pages) are notified immediately

### DoctorService.cs
- Accept optional IAndroidSdkSettingsService via constructor injection
- After a successful 'install-android-sdk' fix action, persist the
  acquired SDK path via SetCustomSdkPathAsync() so it survives app
  restarts and is not overridden by auto-detection

### MauiSherpa.Core.csproj
- Update AndroidSdk from 0.33.0 to 0.35.1 (includes upstream fix)
- Update AndroidSdk.Adbd from 0.33.0 to 0.35.1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dalexsoto added a commit that referenced this pull request Mar 31, 2026
## Problem

The Doctor's 'Fix: Android SDK' action failed with:
  'Android SDK Directory was not specified.'

Even after fixing the upstream AndroidSdk.Tools library (PR #91), a
second issue remained: after successfully acquiring the SDK to
~/android-sdk, restarting the app caused the Doctor to pick up
~/.android instead — a user config directory, not an SDK installation.

## Root Cause

Two bugs contributed to the broken experience:

### 1. Upstream: SdkLocator discarded non-existent paths (fixed in AndroidSdk 0.35.1)

AndroidSdkManager and SdkTool constructors passed the user-specified
target directory through SdkLocator.Locate(), which only returns paths
where Directory.Exists() is true. When acquiring a fresh SDK, the
target directory doesn't exist yet, so:
  - Locate() discarded the user's path
  - It returned either null or a different directory (e.g. ~/.android)
  - DownloadSdk() then threw because it had no valid target

This was fixed upstream in Redth/AndroidSdk.Tools#91 and released
in AndroidSdk 0.35.1.

### 2. Local: Acquired SDK path was never persisted

After AcquireSdkAsync successfully installed the SDK to ~/android-sdk:
  - The in-memory _sdkManager was updated correctly
  - SdkPathChanged event was NOT fired (missing invoke)
  - The path was NOT saved to secure storage

On app restart, AndroidSdkSettingsService.InitializeAsync() found no
saved custom path and fell back to DetectSdkAsync(), which auto-
discovered ~/.android (a config directory created during the fix
process) instead of ~/android-sdk (the actual SDK installation).

## Changes

### AndroidSdkService.cs
- Fire SdkPathChanged event after successful SDK acquisition so that
  listeners (device watchers, UI pages) are notified immediately

### DoctorService.cs
- Accept optional IAndroidSdkSettingsService via constructor injection
- After a successful 'install-android-sdk' fix action, persist the
  acquired SDK path via SetCustomSdkPathAsync() so it survives app
  restarts and is not overridden by auto-detection

### MauiSherpa.Core.csproj
- Update AndroidSdk from 0.33.0 to 0.35.1 (includes upstream fix)
- Update AndroidSdk.Adbd from 0.33.0 to 0.35.1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants