- name
- winapp-setup
- description
- Add Windows SDK and Windows App SDK support to an existing project (winapp init), restore pinned SDKs after cloning (winapp restore), update SDKs, or create a new WinUI app (winapp new). Use for Electron, .NET, C++, Rust, Flutter, or Tauri projects that need Windows setup.
**If you can't run `winapp` yourself** (no shell, or the command is denied), give the user the exact `winapp` command(s) for their project instead of only describing the steps.
## When to use
Use this skill when:
- **Scaffolding a brand-new WinUI 3 app** from an official Windows App SDK template (`winapp new`)
- **Adding Windows platform support** to an existing project (Electron, .NET, C++, Rust, Flutter, Tauri, etc.)
- **Cloning a repo** that already uses winapp and need to restore SDK packages
- **Updating SDK versions** to get the latest Windows SDK or Windows App SDK
## Prerequisites
Install the winapp CLI before running any commands:
```powershell
# Via winget (recommended for non-Node projects)
winget install Microsoft.WinAppCli --source winget
# Via npm (recommended for Electron/Node projects — includes Node.js SDK)
npm install --save-dev @microsoft/winappcli
```
You need an **existing app project** — `winapp init` does **not** create new projects, it adds Windows platform files to your existing codebase.
> **Already have a `Package.appxmanifest`?** .NET projects that already have a packaging manifest (e.g., WinUI 3 apps or projects with an existing MSIX packaging setup) likely **don't need `winapp init`**. Ensure your `.csproj` references the `Microsoft.WindowsAppSDK` NuGet package and has the right properties for packaged builds (e.g., `<WindowsPackageType>MSIX</WindowsPackageType>`). WinUI 3 apps created from Visual Studio templates are typically already fully configured — you can go straight to building and using `winapp run` or `winapp package`.
## Key concepts
**`Package.appxmanifest`** is the most important file winapp creates — it declares your app's identity, capabilities, and visual assets. Most winapp commands require it (`package`, `run`, `cert generate --manifest`).
**`winapp.yaml`** is only needed for SDK version management via `restore`/`update`. Projects that already reference Windows SDK packages (e.g., via NuGet in a `.csproj`) can use winapp commands without it.
**`.winapp/`** is the local folder where SDK packages and generated projections (e.g., CppWinRT headers) are stored. This folder is `.gitignore`d — team members recreate it via `winapp restore`.
## Usage
### Create a new WinUI app
To start a brand-new **WinUI** app (rather than adding Windows support to an existing project), use `winapp new`. It verifies the .NET SDK, installs the official WinUI `dotnet new` template pack on demand (grabbing the latest, or offering to update a stale one), and scaffolds the app against your installed SDK's target framework. Most WinUI templates already include packaging/identity, so **no `winapp init` step is needed** afterward — follow the template-specific next step `winapp new` prints when it finishes. App templates go straight to `winapp run` (which builds and launches the app); the `winui-lib` (class library) and `winui-unittest` templates differ (reference the library from an app project, or `winapp run` the packaged test app to run its tests). The template list is read live from the installed pack — run `winapp new --list` to see the current set.
The pack ships two styles of app. **XAML** templates (`winui`, `winui-navview`, `winui-tabview`, `winui-mvvm`) define the UI in markup with a C# code-behind. **Reactor** templates (`reactor`, `reactor-mvu`, `reactor-navview`, `reactor-tabview`) are pure C# with no XAML, using an MVU pattern.
> **Reactor templates are experimental.** They reference the prerelease `Microsoft.UI.Reactor` packages, whose APIs can change or be removed in a future release — don't pick one unless the user explicitly asks for Reactor. `winapp new` marks them **(Experimental)** in `--list` and in the picker, reports `"Experimental": true` in `--json`, and never selects one as the default. They also require the **.NET 10 SDK or newer**; on an older SDK `winapp new` fails up front naming the version it needs.
> A first run, template-pack update, or newly published Windows App SDK version may take longer while missing NuGet packages download and restore. If scaffolding continues beyond 10 seconds, `winapp new` updates its status message rather than silently waiting.
```powershell
# Interactive — pick a template, then name/output
winapp new
# See the available templates without scaffolding
winapp new --list
# One-shot with a specific template (short names come from `winapp new --list`)
winapp new --name MyApp --template winui-navview
# Experimental Reactor app (pure C#, no XAML) — requires the .NET 10 SDK
winapp new --name MyApp --template reactor-mvu
# Diagnose a failed scaffold: --verbose streams dotnet new's post-creation actions
# (restore, package add, etc.) live so the underlying dotnet error is visible
winapp new --name MyApp --verbose
# Always use the newest template pack without prompting
winapp new --name MyApp --template-version latest --use-defaults
# Non-interactive (agent) with machine-readable output
winapp new --use-defaults --name MyApp --json
```
### Initialize a new winapp project
```powershell
# Interactive — prompts for app name, publisher, SDK channel, etc.
# Automatically searches for compatible projects (Tauri, Electron, .NET, Rust, C++, Flutter)
winapp init
# Non-interactive — accepts all defaults (stable SDKs, current folder name as app name)
winapp init . --use-defaults
# Non-interactive with JS bindings enabled
winapp init . --use-defaults --add-js-bindings
# Skip SDK installation (just manifest + config)
winapp init . --use-defaults --setup-sdks none
# Install preview SDKs instead of stable
winapp init . --use-defaults --setup-sdks preview
```
After `init`, your project will contain:
- `Package.appxmanifest` — package identity and capabilities
- `Assets/` — default app icons (Square44x44Logo, Square150x150Logo, etc.)
- `winapp.yaml` — SDK version pinning for `restore`/`update`
- `.winapp/` — downloaded SDK packages and generated projections
- `.gitignore` update — excludes `.winapp/` and `devcert.pfx`
When JS bindings are enabled (via `--add-js-bindings` or by answering yes in interactive init), npm/Electron projects also get:
- `.winapp/bindings/` — generated JS bindings for Windows App SDK APIs (npm-only, Node / Electron)
- `package.json` update — adds the `winapp.jsBindings` namespace and `@microsoft/dynwinrt` dependency (npm-only)
### Initialize a sparse identity package (existing exe)
Use `--sparse` when you have an **already-built desktop exe** (WPF, WinForms, Win32, Electron, etc.) and only want to give it **package identity** — without repackaging the whole app into the MSIX. The app's files stay where they are and are resolved from an *external content location* at runtime.
```powershell
# Generate an identity-only sparse manifest for an existing exe
winapp init --exe ./bin/Release/MyApp.exe --sparse
# Non-interactive, with explicit identity values
winapp init --exe ./bin/Release/MyApp.exe --sparse --name MyApp --publisher "CN=Contoso" --use-defaults
```
`--sparse` requires `--exe`. It skips all SDK/package installation (sparse identity packages have no SDK dependencies) and, by default, writes to a dedicated `sparse/` folder in the current directory (override with `--output-dir`) so the manifest and its `Assets/` stay out of a build-output folder that a rebuild would wipe:
- `appxmanifest.xml` — identity-only sparse manifest (declares `uap10:AllowExternalContent`)
- `Assets/` — placeholder visual assets (extracted from the exe's icon when possible), resolved from the **external location** at runtime — **not** bundled into the `.msix`
If an `appxmanifest.xml` already exists in the target directory, init fails instead of overwriting it; re-run with `--force` to regenerate.
This is step 1 of the sparse packaging workflow. Continue with:
1. `winapp pack ./sparse/appxmanifest.xml --cert ./devcert.pfx` — build the signed identity `.msix`
2. `winapp embed-identity ./bin/Release/MyApp.exe` — connect the exe to the identity package (re-sign the exe afterward)
3. Register in your installer with `Add-AppxPackage -Path <msix> -ExternalLocation <install-dir>`
For the full walkthrough, see the [Sparse packaging guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/sparse.md).
### Restore after cloning
```powershell
# Reinstall SDK packages from existing winapp.yaml (does not change versions)
winapp restore
# Restore into a specific directory
winapp restore ./my-project
```
Use `restore` when you clone a repo that already has `winapp.yaml` but no `.winapp/` folder. For a .NET project there is no `winapp.yaml`, so `restore` runs `dotnet restore` instead.
### Private or custom NuGet feeds
`init`, `restore`, and `update` download the SDK packages through NuGet, honoring your standard `nuget.config` hierarchy. Private feeds and mirrors, feed credentials (including credential providers), and a custom `globalPackagesFolder` all work as they do for `dotnet restore`. To use only your feed, `<clear />` the inherited sources first:
```xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="contoso" value="https://pkgs.dev.azure.com/contoso/_packaging/winsdk-mirror/nuget/v3/index.json" />
</packageSources>
</configuration>
```
> **Security note:** For native projects winapp resolves `nuget.config` from the directory it operates on: the `init`/`restore` directory argument, `--config-dir` when given, otherwise the current directory. For **.NET projects** the sources come from the project's own `nuget.config` hierarchy instead, because that is what `dotnet add package` and `dotnet restore` use — so put a private feed's config in the project directory or an ancestor, not in a sibling passed via `--config-dir` (that is reported and ignored). Run these commands only against directories you trust, the same as `dotnet restore`. Use `<packageSourceMapping>` to pin packages to specific feeds when more than one source is configured.
### Update SDK versions
```powershell
# Check for and install latest stable SDK versions
winapp update
# Switch to preview channel
winapp update --setup-sdks preview
```
This updates `winapp.yaml` with the latest versions and reinstalls packages. If the newest Windows App SDK release is still being published (some of its packages aren't on the feed yet), `update` uses the previous release instead and prints a note; run `winapp update` again later to pick up the new release.
### Run and debug with identity
```powershell
# Register debug identity and launch app from build output
winapp run ./bin/Debug
# Launch with custom manifest and pass arguments to the app
winapp run ./dist --manifest ./out/Package.appxmanifest --args "--my-flag value"
# Pass arguments after -- to avoid escaping (equivalent to --args)
winapp run ./bin/Debug -- --my-flag value
# Register identity without launching (useful for attaching a debugger manually)
winapp run ./bin/Debug --no-launch
# Launch and capture OutputDebugString messages and crash diagnostics
# Note: prevents other debuggers (VS, VS Code) from attaching — use --no-launch if you need those instead
winapp run ./bin/Debug --debug-output
```
Use `winapp run` during iterative development — it creates a loose layout package, registers a debug identity, and launches the app in one step. For identity-only registration without loose layout, use `winapp create-debug-identity` instead.
#### Project mode: `winapp run` on a `.csproj` (.NET / WinUI)
For .NET SDK projects you can point `winapp run` **at the project instead of the build output** — it builds the `.csproj` and launches it in one step, so there's no separate `dotnet build` and no need to know the output path:
```powershell
# Build and run the project in the current directory (input defaults to ".")
winapp run
# Run a specific project, configuration, and architecture
winapp run ./src/MyApp/MyApp.csproj -c Release --arch arm64
# Force an unpackaged run of a packaged project
winapp run . -p WindowsPackageType=None
# Show winapp's build decision traces (dotnet build stays at minimal verbosity)
winapp run . --verbose
# Test a project configured with <PublishAot>true</PublishAot>
winapp run . --aot
winapp run . --aot -c Release
```
Project mode supports both **packaged** and **unpackaged** WinUI apps, detected from the project's effective `WindowsPackageType` (`MSIX` ⇒ loose-layout register + AUMID launch; `None` ⇒ launch the built `.exe`), and installs the matching-architecture Windows App Runtime before launching. RID-only remains the default; when the effective configuration requires a self-contained profile, winapp selects the architecture-matching profile without forcing that platform onto referenced `AnyCPU` libraries. Requires .NET SDK 8.0.100+.
- **Build inputs:** `-c/--configuration`, `--arch`, `-r/--runtime`, `-f/--framework`, `--no-build`, `--no-restore`, `--aot`, `-p/--property` (repeat for multiple properties; use `%3B` or `%2C` for a literal semicolon or comma in a value). `--aot` supports x64/ARM64 projects, requires .NET SDK 8.0.300+ and effective `PublishAot=true`, and cannot use `--no-build` or `--manifest`; configure the project manifest before publishing.
- **Packaged-only options:** `--manifest`, `--no-launch`, `--with-alias`, `--clean`, `--unregister-on-exit`, `--output-appx-directory`, `--executable`, `--unique-identity` — rejected for unpackaged apps.
- **Output:** winapp restores dependencies, then builds; successful-build warnings stay visible. Interactive terminals show a one-line restore result and dotnet's own build output; other output uses sanitized plain lines that stream live. A failed restore or build prints the exact `dotnet` command; `--verbose` prints every command. `--json` sends restore/build invocations and child output to **stderr** so stdout stays valid JSON. `--quiet` suppresses invocations and sends dotnet's quiet restore/build output to **stderr** so stdout stays clean. With `--aot`, `--verbose` adds the publish command and resolved paths; publish diagnostics go to **stderr** under `--json`/`--quiet`.
For parallel packaged checkouts, see `winapp-identity` and the canonical
[`--unique-identity` workflow](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#unique-identity-for-parallel-checkouts).
Do not add this flag to an unpackaged run or automatically remove another checkout's
registration to make normal mode succeed.
#### Project mode: `winapp run` on a C++ `.vcxproj`
A Visual Studio C++ project (for example the **WinUI Blank App (Packaged)** C++/WinRT template) runs the same way. winapp builds it with Visual Studio's MSBuild (restoring `packages.config` first), then launches it packaged or unpackaged:
```powershell
winapp run .\MyApp.vcxproj
winapp run . -c Release --arch arm64 --detach --json
```
Requires Visual Studio or Build Tools with the **Desktop development with C++** workload (WinUI 3 apps also need **C++ WinUI app development tools**); the .NET SDK is not needed. If the C++ build tools, platform toolset, or Windows SDK are missing, the error says what to install. `--framework` and `--aot` are .NET-only.
#### Single-file mode: `winapp run` on a `.cs` file-based app
A .NET 10 file-based app is a single `.cs` file configured by `#:` directives, with no project file. Point `winapp run` at it and the app builds and launches **with package identity** — no hand-written manifest:
```powershell
# Build and run a file-based app with identity
winapp run counter.cs
# Register identity without launching, or run a Release build detached
winapp run counter.cs --no-launch
winapp run counter.cs -c Release --detach --json
# Remove the package it registered
winapp unregister counter.cs
```
Describe the package with `#:property` directives in the file. All are optional:
| Property | Sets | Default |
|---|---|---|
| `WinAppPackageName` | `Identity/@Name` | file name (sanitized) + a short hash of its path |
| `WinAppDisplayName` | Start/Settings name | file name |
| `WinAppPublisher` | `Identity/@Publisher` | `CN=<current user>`; a bare name is wrapped as `CN=` |
| `WinAppVersion` | `Identity/@Version` | `$(Version)`, normalized to four 0–65535 parts (`1.2.3-preview.4` ⇒ `1.2.3.0`) |
| `WinAppDescription` | Install/Settings description | the display name |
| `WinAppCapabilities` | Capabilities to declare, separated by `;` or `,` | none |
| `WinAppRunUseExecutionAlias` | Launch via execution alias instead of AUMID, so console output stays in the terminal | `true` for `OutputType=Exe`, else `false` |
View on GitHub