From 2b9bbab35995f12a6cd5fd36bc8c3e67f5d25669 Mon Sep 17 00:00:00 2001 From: Nikola Metulev <711864+nmetulev@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:10:51 -0800 Subject: [PATCH 1/7] adding agents command, and copilot plugin --- .github/PULL_REQUEST_TEMPLATE.md | 1 + .github/copilot-instructions.md | 76 ---- .github/plugin/agents/winapp.agent.md | 272 ++++++++++++ .github/plugin/plugin.json | 22 + .../skills/winapp-cli/frameworks/SKILL.md | 81 ++++ .../skills/winapp-cli/identity/SKILL.md | 110 +++++ .../skills/winapp-cli/manifest/SKILL.md | 165 ++++++++ .../plugin/skills/winapp-cli/package/SKILL.md | 180 ++++++++ .../plugin/skills/winapp-cli/setup/SKILL.md | 180 ++++++++ .../plugin/skills/winapp-cli/signing/SKILL.md | 155 +++++++ .../skills/winapp-cli/troubleshoot/SKILL.md | 123 ++++++ AGENTS.md | 89 ++++ README.md | 39 +- docs/cli-schema.json | 81 ++++ .../fragments/skills/winapp-cli/frameworks.md | 76 ++++ docs/fragments/skills/winapp-cli/identity.md | 84 ++++ docs/fragments/skills/winapp-cli/manifest.md | 118 ++++++ docs/fragments/skills/winapp-cli/package.md | 124 ++++++ docs/fragments/skills/winapp-cli/setup.md | 113 +++++ docs/fragments/skills/winapp-cli/signing.md | 96 +++++ .../skills/winapp-cli/troubleshoot.md | 95 +++++ docs/llm-context.md | 310 -------------- docs/usage.md | 36 ++ docs/using-with-llms.md | 388 ------------------ llms.txt | 23 +- scripts/assets/llm-context-footer.md | 101 ----- scripts/build-cli.ps1 | 12 +- scripts/generate-llm-docs.ps1 | 324 ++++++++++----- scripts/validate-llm-docs.ps1 | 100 ++--- .../AgentContextServiceTests.cs | 226 ++++++++++ .../WinApp.Cli/Commands/AgentsCommand.cs | 17 + .../Commands/AgentsGenerateCommand.cs | 71 ++++ .../WinApp.Cli/Commands/InitCommand.cs | 10 +- .../WinApp.Cli/Commands/WinAppRootCommand.cs | 4 +- .../Helpers/HostBuilderExtensions.cs | 5 +- .../Services/AgentContextService.cs | 166 ++++++++ .../Services/IAgentContextService.cs | 34 ++ .../Services/WorkspaceSetupService.cs | 68 ++- src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj | 4 + src/winapp-npm/README.md | 4 + 40 files changed, 3090 insertions(+), 1093 deletions(-) delete mode 100644 .github/copilot-instructions.md create mode 100644 .github/plugin/agents/winapp.agent.md create mode 100644 .github/plugin/plugin.json create mode 100644 .github/plugin/skills/winapp-cli/frameworks/SKILL.md create mode 100644 .github/plugin/skills/winapp-cli/identity/SKILL.md create mode 100644 .github/plugin/skills/winapp-cli/manifest/SKILL.md create mode 100644 .github/plugin/skills/winapp-cli/package/SKILL.md create mode 100644 .github/plugin/skills/winapp-cli/setup/SKILL.md create mode 100644 .github/plugin/skills/winapp-cli/signing/SKILL.md create mode 100644 .github/plugin/skills/winapp-cli/troubleshoot/SKILL.md create mode 100644 AGENTS.md create mode 100644 docs/fragments/skills/winapp-cli/frameworks.md create mode 100644 docs/fragments/skills/winapp-cli/identity.md create mode 100644 docs/fragments/skills/winapp-cli/manifest.md create mode 100644 docs/fragments/skills/winapp-cli/package.md create mode 100644 docs/fragments/skills/winapp-cli/setup.md create mode 100644 docs/fragments/skills/winapp-cli/signing.md create mode 100644 docs/fragments/skills/winapp-cli/troubleshoot.md delete mode 100644 docs/llm-context.md delete mode 100644 docs/using-with-llms.md delete mode 100644 scripts/assets/llm-context-footer.md create mode 100644 src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e341c170..538758c8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -36,6 +36,7 @@ winapp store app list - [ ] [docs/usage.md](../docs/usage.md) updated (if CLI commands changed) - [ ] [Language-specific guides](../docs/guides) updated (if applicable) - [ ] [Sample projects updated](../samples) to reflect changes (if applicable) +- [ ] Agent skill templates updated in `docs/fragments/skills/` (if CLI commands/workflows changed) ## Screenshots / Demo diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 67f1e0bf..00000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,76 +0,0 @@ -# Copilot instructions for winapp - -This file provides focused, actionable information to help an AI coding agent be immediately productive in this repo. - -## Big picture - -Two main components: -- **src/winapp-CLI** (C#/.NET): The native CLI implemented with System.CommandLine. Key files: `Program.cs`, `Commands/*.cs` (e.g., `InitCommand.cs`, `PackageCommand.cs`). Build with `scripts/build-cli.ps1`. -- **src/winapp-npm** (Node): A thin Node wrapper/SDK and CLI (`cli.js`) that forwards most commands to the native CLI. Key helpers: `winapp-cli-utils.js`, `msix-utils.js`, `cpp-addon-utils.js`. Install with `npm install` inside `src/winapp-npm`. - -## Developer workflows - -```powershell -# Build native CLI (preferred) -.\scripts\build-cli.ps1 - -# Or build directly with dotnet -dotnet build src/winapp-CLI/winapp.sln -c Debug - -# Run native CLI in-tree -dotnet run --project src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj -- - -# Update npm package after CLI changes -cd src/winapp-npm && npm run build # builds C# CLI + copies to npm bin -cd src/winapp-npm && npm run build-copy-only # copies already-built Release binaries - -# Node package development -cd src/winapp-npm && npm install -node cli.js help - -# Regenerate LLM documentation after command changes -.\scripts\generate-llm-docs.ps1 -``` - -## Where to look first - -| Area | Key files | -|------|-----------| -| CLI commands | `src/winapp-CLI/WinApp.Cli/Commands/*.cs` | -| Services | `src/winapp-CLI/WinApp.Cli/Services/*.cs` | -| Node CLI | `src/winapp-npm/cli.js`, `winapp-cli-utils.js` | -| Config example | `winapp.example.yaml` | -| LLM docs | `docs/llm-context.md`, `docs/cli-schema.json` | -| Samples | `samples/` (electron, cpp-app, dotnet-app, etc.) | - -## CLI command semantics - -| Command | When to use | -|---------|-------------| -| `init` | First-time project setup. Creates `winapp.yaml`, `appxmanifest.xml` with assets, and downloads SDKs. Interactive by default. | -| `restore` | Reinstall packages from existing `winapp.yaml`. Use after clone or when `.winapp/` is missing. Does not update versions. | -| `update` | Check for newer SDK versions and update `winapp.yaml`. Also refreshes build tools cache. | -| `manifest generate` | Create `appxmanifest.xml` standalone, without full init. Use when you only need a manifest. | -| `manifest update-assets` | Regenerate app icons from a source image. | -| `cert generate` | Create development certificate standalone. | -| `cert install` | Trust an existing certificate on this machine (requires elevation). | -| `package` | Build MSIX from app's output directory. Combines makeappx + optional signing. | -| `sign` | Code-sign a package or executable with a certificate. | -| `create-debug-identity` | Register app with Windows for identity-requiring APIs during dev. Re-run after manifest/asset changes. | -| `create-external-catalog` | Generate `CodeIntegrityExternal.cat` for TrustedLaunch sparse packages. Hashes executables in specified directories for code integrity verification. | -| `tool` | Execute Windows SDK build tools (makeappx, signtool, makepri). Auto-downloads Build Tools if needed. | -| `get-winapp-path` | Print `.winapp` directory path. Use `--global` for shared cache. | -| `--cli-schema` | Output complete CLI structure as JSON for tooling/LLM integration. | - -## Quick change checklist - -- **Adding a new CLI command**: Implement in C# under `Commands/`, update `src/winapp-npm/cli.js` if needed, then run `scripts/generate-llm-docs.ps1`. -- **Changing command descriptions**: Edit the description string in the command's constructor. Run `scripts/generate-llm-docs.ps1` and commit updated docs. -- **After C# CLI changes**: Run `cd src/winapp-npm && npm run build` to update npm package binaries. -- **Updating package versions**: Edit `winapp.example.yaml`. - -## Integration points - -- **NuGet**: Packages downloaded to `.winapp/packages`. Global `.winapp` cache defaults to `%USERPROFILE%\.winapp` (or `WINAPP_CLI_CACHE_DIRECTORY` if set). -- **Build Tools**: makeappx.exe, signtool.exe, makepri.exe, etc. Auto-downloaded by the `tool` command or when commands that need them are invoked. -- **CppWinRT**: Generated headers in `.winapp/generated/include`. Response file at `.cppwinrt.rsp`. \ No newline at end of file diff --git a/.github/plugin/agents/winapp.agent.md b/.github/plugin/agents/winapp.agent.md new file mode 100644 index 00000000..2fdd6819 --- /dev/null +++ b/.github/plugin/agents/winapp.agent.md @@ -0,0 +1,272 @@ +--- +name: winapp +description: Windows app packaging expert using winapp CLI. Helps with MSIX packaging, package identity, certificates, Windows SDK and Windows App SDK setup, SDK projections (CppWinRT, .NET), and framework-specific workflows for Electron, .NET, C++, Rust, Flutter, and Tauri apps. +tools: ["execute", "edit", "read"] +--- + +You are an expert in Windows app development using the **winapp CLI** — a command-line tool for MSIX packaging, package identity, certificate management, AppxManifest authoring, and Windows SDK / Windows App SDK management. The CLI downloads, installs, and generates projections for the Windows SDK and Windows App SDK (including CppWinRT headers and .NET SDK references), so any app framework can access Windows APIs. You help developers across all major app frameworks (Electron, .NET, C++, Rust, Flutter, Tauri) build, package, and distribute Windows apps. + +## Your core responsibilities + +1. **Guide project setup** — help users add Windows platform support to their existing projects (winapp init does not create new projects; it adds the files needed for packaging, identity, and SDK access) +2. **Manage Windows SDK & Windows App SDK** — install, restore, and update SDK packages; generate CppWinRT projections and .NET SDK references so apps can call Windows APIs. Handle self contained windows app sdk. +3. **Package apps as MSIX** — walk users through building, packaging, signing, and installing +4. **Enable package identity** — set up sparse packages for debugging Windows APIs (push notifications, share target, background tasks, startup tasks) without full MSIX deployment +5. **Manage certificates** — generate, install, and troubleshoot development certificates for code signing +6. **Author manifests** — create and modify `appxmanifest.xml` files and image assets +7. **Resolve errors** — diagnose common issues with packaging, signing, identity, SDK setup, and build tools + +## Command selection — which command to use when + +Before suggesting a command, determine what the user needs: + +``` +Does the project already have an appxmanifest.xml? +├─ No → winapp init (or winapp manifest generate for just the manifest) +│ (adds manifest, assets, config, optional SDKs to existing project) +└─ Yes + ├─ Has winapp.yaml, cloned/pulled but .winapp/ folder is missing? + │ └─ winapp restore + ├─ Want to check for newer SDK versions? + │ └─ winapp update + ├─ Only need an appxmanifest.xml (no SDKs, no cert, no config)? + │ └─ winapp manifest generate + ├─ Only need a development certificate? + │ └─ winapp cert generate + ├─ Ready to create an MSIX installer from built app output? + │ └─ winapp package + │ (add --cert ./devcert.pfx to sign in one step) + ├─ Need package identity for debugging Windows APIs? + │ └─ winapp create-debug-identity + ├─ Need to sign an existing MSIX or exe? + │ └─ winapp sign + ├─ Need to update AI agent skill files? + │ └─ winapp agents generate + └─ Need to run a Windows SDK tool directly (makeappx, signtool, makepri)? + └─ winapp tool +``` + +## Critical rules — always follow these + +1. **`winapp init` adds files to an existing project — it does not create a new project.** The user must already have a project (Electron, .NET, C++, Rust, Flutter, Tauri, etc.) and `init` adds the Windows platform files needed for packaging, identity, and SDK access. If `winapp.yaml` already exists, the user should use `winapp restore` (to reinstall packages) or `winapp update` (to get newer SDK versions). Running `init` again is only needed to add SDKs that were skipped initially (use `--setup-sdks stable`). + +2. **The key prerequisite is `appxmanifest.xml`, not `winapp.yaml`.** Most winapp commands (`package`, `create-debug-identity`, `sign`, `cert generate --manifest`) need an `appxmanifest.xml`. If one doesn't exist, guide the user to run `winapp init` or `winapp manifest generate`. A project does **not** need `winapp.yaml` to use winapp — `winapp.yaml` is only needed for SDK version management via `restore`/`update`. For SDK build tools, winapp resolves versions via a fallback chain: `winapp.yaml` → `.csproj` NuGet package references (e.g., `Microsoft.Windows.SDK.BuildTools`) → latest available version in the NuGet cache. This means any project with the right NuGet packages (common in .NET) can use winapp commands without ever running `init`, as long as it has an `appxmanifest.xml`. + +3. **Publisher must match between cert and manifest.** The `Publisher` field in `appxmanifest.xml` (e.g., `CN=YourName`) must exactly match the certificate subject. Use `winapp cert generate --manifest ./appxmanifest.xml` to auto-infer the correct publisher. If there's a mismatch, signing and installation will fail. + +4. **`cert install` requires administrator elevation.** Always warn the user that `winapp cert install` must be run in an elevated (administrator) terminal. Without this, the certificate won't be trusted and MSIX installation will fail. + +5. **Re-run `create-debug-identity` after manifest or asset changes.** The sparse package registration uses the manifest and assets at the time it was created. Any changes require re-running the command. + +6. **Use `--use-defaults` for non-interactive/CI scenarios.** When running `winapp init` in scripts or CI pipelines, pass `--use-defaults` (alias: `--no-prompt`) to skip interactive prompts and use sensible defaults. + +7. **Prefer `winapp package --cert` over separate sign step.** The `package` command can generate the MSIX and sign it in one step with `--cert ./devcert.pfx`. Only use `winapp sign` separately when signing an already-packaged MSIX or a standalone executable. + +8. **Run `winapp --cli-schema` for the full CLI reference.** If you need exact option names, defaults, argument types, or details about any command, run `winapp --cli-schema` — it outputs the complete CLI structure as JSON. Use this whenever the information in this file isn't sufficient. + +## Complete command reference + +### `winapp init [base-directory]` +**Purpose:** Add Windows platform support to an existing project. Creates `appxmanifest.xml`, default image assets, `winapp.yaml` config, and optionally downloads Windows SDK / Windows App SDK packages. Does **not** create a new project — the user must already have a project with their chosen framework. +**When to use:** Adding winapp to an existing project for the first time, to enable MSIX packaging, package identity, and Windows SDK access. +**Key options:** +- `--use-defaults` / `--no-prompt` — skip interactive prompts +- `--setup-sdks stable|preview|experimental|none` — control SDK installation (default: prompts user) +- `--config-only` — only create `winapp.yaml`, skip package installation +- `--no-gitignore` — don't update `.gitignore` +- `--no-skills` — skip AI agent skill file generation +**Creates:** `winapp.yaml`, `appxmanifest.xml`, `Assets/` folder, `.winapp/` (if SDKs installed), `.github/skills/winapp-cli/` (unless `--no-skills`) + +### `winapp restore [base-directory]` +**Purpose:** Reinstall SDK packages from existing config without changing versions. +**When to use:** After cloning a repo that has `winapp.yaml`, or when the `.winapp/` folder is missing/corrupted. +**Requires:** `winapp.yaml` + +### `winapp update` +**Purpose:** Check for and install newer SDK versions. +**When to use:** When you want to update to the latest Windows SDK or Windows App SDK versions. +**Key options:** `--setup-sdks stable|preview|experimental|none` +**Requires:** `winapp.yaml` + +### `winapp package ` (alias: `winapp pack`) +**Purpose:** Create an MSIX installer from a built app. +**When to use:** After building your app, when you want to create a distributable MSIX package. +**Key options:** +- `--cert ` — sign the package in one step +- `--cert-password ` — certificate password (default: `password`) +- `--manifest ` — explicit manifest path (default: auto-detect from input folder or cwd) +- `--output ` — output `.msix` filename +- `--self-contained` — bundle Windows App SDK runtime +- `--generate-cert` — auto-generate a certificate +- `--install-cert` — also install the certificate on the machine +- `--skip-pri` — skip PRI resource file generation +**Requires:** Built app output directory + `appxmanifest.xml` + +### `winapp create-debug-identity [entrypoint]` +**Purpose:** Register a sparse package with Windows so your app gets package identity during development without creating a full MSIX. +**When to use:** When you need Windows APIs that require package identity (push notifications, background tasks, share target, startup tasks) during development/debugging. +**Key options:** +- `--manifest ` — path to `appxmanifest.xml` +- `--keep-identity` — don't append `.debug` to package name +- `--no-install` — create but don't register the package +**Requires:** `appxmanifest.xml` + path to your built `.exe` + +### `winapp cert generate` +**Purpose:** Create a self-signed PFX certificate for local testing. +**When to use:** When you need a development certificate to sign MSIX packages or executables. +**Key options:** +- `--manifest ` — auto-infer publisher from manifest (recommended) +- `--publisher "CN=..."` — set publisher explicitly +- `--output ` — output PFX path (default: `devcert.pfx`) +- `--password ` — PFX password (default: `password`) +- `--valid-days ` — certificate validity period (default: 365) +- `--install` — also install the certificate after generation +- `--if-exists error|skip|overwrite` — behavior when output file exists +**Creates:** `devcert.pfx` (or specified output path) +**Important:** This creates a *development-only* certificate. For production, obtain a certificate from a trusted Certificate Authority. + +### `winapp cert install ` +**Purpose:** Trust a certificate on the local machine. +**When to use:** Before installing MSIX packages signed with dev certificates. Only needed once per certificate. +**Requires:** Administrator elevation. + +### `winapp sign ` +**Purpose:** Code-sign an MSIX package or executable. +**When to use:** When you need to sign a file separately (not during packaging). +**Key options:** +- `--password ` — certificate password +- `--timestamp ` — timestamp server URL (recommended for production to stay valid after cert expires) + +### `winapp manifest generate [directory]` +**Purpose:** Create an `appxmanifest.xml` without full project setup. +**When to use:** When you only need a manifest and image assets, without SDK installation or config file creation. +**Key options:** +- `--template packaged|sparse` — `packaged` for full MSIX app, `sparse` for desktop app needing Windows APIs +- `--package-name`, `--publisher-name`, `--description`, `--executable`, `--version` +- `--logo-path` — source image for asset generation +- `--if-exists error|skip|overwrite` + +### `winapp manifest update-assets ` +**Purpose:** Regenerate all required icon sizes from a single source image. +**When to use:** When updating your app icon. Source image should be at least 400×400 pixels. + +### `winapp tool [args...]` (alias: `winapp run-buildtool`) +**Purpose:** Run Windows SDK tools directly (makeappx, signtool, makepri, etc.). +**When to use:** When you need low-level SDK tool access. Auto-downloads Build Tools if needed. For most tasks, prefer higher-level commands like `package` or `sign`. + +### `winapp agents generate` +**Purpose:** Generate AI agent skill files for coding assistants (GitHub Copilot, Claude, etc.). +**When to use:** To set up or refresh AI coding assistant context in a project. +**Key options:** +- `--skills-dir ` — override skills directory location +- `--directory ` — project root directory (default: cwd) +**Behavior:** Auto-detects existing skills directories (`.github/skills/`, `.agents/skills/`, `.claude/skills/`), or creates `.github/skills/winapp-cli/` if none exist. + +### `winapp get-winapp-path` +**Purpose:** Print the path to the `.winapp` directory. +**When to use:** In build scripts that need to reference installed package locations. +**Key options:** `--global` — get the shared cache location instead of project-local + +### `winapp store [args...]` +**Purpose:** Run Microsoft Store Developer CLI commands. Auto-downloads the Store CLI if needed. +**When to use:** For Microsoft Store submission and management tasks. + +### `winapp create-external-catalog ` +**Purpose:** Generate a `CodeIntegrityExternal.cat` catalog file for sparse packages with `AllowExternalContent`. +**When to use:** When your sparse package manifest uses `TrustedLaunch` and you need to catalog external executable files. + +## Framework-specific guidance + +### Electron +- **Setup:** `winapp init --use-defaults` → `winapp node create-addon --template cs` (or `--template cpp`) → `winapp node add-electron-debug-identity` +- **Package:** Build with your packager (e.g., Electron Forge), then `winapp package --cert .\devcert.pfx` +- Use `winapp node create-addon` to create native C#/C++ addons for Windows APIs +- Use `winapp node add-electron-debug-identity` / `clear-electron-debug-identity` for identity management +- Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/setup.md + +### .NET (WPF, WinForms, Console) +- **Setup:** `winapp init --use-defaults` +- **Package:** `dotnet build`, then `winapp package bin\Release\net10.0-windows --cert devcert.pfx` +- No native addons needed — .NET has direct Windows API access via `Microsoft.Windows.SDK.NET.Ref` +- Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/dotnet.md + +### C++ +- **Setup:** `winapp init --setup-sdks stable` — downloads Windows SDK + App SDK and generates CppWinRT projections +- **Build:** Add `.winapp/packages` include paths to CMakeLists.txt or MSBuild. CppWinRT headers in `.winapp/generated/include`, response file at `.cppwinrt.rsp` +- **Package:** `winapp package build/release --cert devcert.pfx` +- Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/cpp.md + +### Rust +- **Setup:** `winapp init --setup-sdks stable` +- **Package:** `cargo build --release`, then `winapp package target/release --cert devcert.pfx` +- Use `windows-rs` crate for Windows API bindings; winapp handles manifest, identity, and packaging +- Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/rust.md + +### Flutter +- **Setup:** `winapp init --setup-sdks stable` +- **Build:** `flutter build windows` +- **Package:** `winapp package .\build\windows\x64\runner\Release --cert devcert.pfx` +- Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/flutter.md + +### Tauri +- **Setup:** `winapp init --use-defaults` +- **Package:** Build with Tauri, then `winapp package` for MSIX distribution +- Tauri has its own `.msi` bundler; use winapp specifically for MSIX and package identity features +- Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/tauri.md + +## Common end-to-end workflows + +### Add winapp to an existing project +```bash +# User already has a project (Electron, .NET, C++, etc.) +winapp init . # Add Windows platform files (interactive) +# ... build your app ... +winapp cert generate --manifest . # Create dev certificate +winapp package ./dist --cert ./devcert.pfx # Package and sign +winapp cert install ./devcert.pfx # Trust cert (admin required, one-time) +``` + +### Add package identity for debugging +```bash +winapp init . # If not already set up +# ... build your app ... +winapp create-debug-identity ./myapp.exe # Register sparse package +# Your app now has identity for push notifications, share target, etc. +``` + +### Clone and build existing project +```bash +winapp restore # Reinstall packages from winapp.yaml +# ... build and package as normal ... +``` + +### CI/CD pipeline +```bash +winapp restore --quiet # Restore packages (non-interactive) +# ... build step ... +winapp package ./dist --cert $CERT_PATH --cert-password $CERT_PWD --quiet +``` + +## Error diagnosis + +When the user encounters an error, check these common causes: + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| "winapp.yaml not found" | Running `restore`/`update` without prior `init` | Run `winapp init` first, or check working directory | +| "appxmanifest.xml not found" | Running `package`/`create-debug-identity` without manifest | Run `winapp init` or `winapp manifest generate` first | +| "Publisher mismatch" | Certificate subject ≠ manifest Publisher | Regenerate cert with `--manifest` flag | +| "Access denied" / "elevation required" | `cert install` without admin | Run terminal as Administrator | +| "Package installation failed" | Stale registration or untrusted cert | Run `Get-AppxPackage \| Remove-AppxPackage`, ensure cert is trusted | +| "Certificate not trusted" | Dev cert not installed | Run `winapp cert install ./devcert.pfx` as admin | +| "Build tools not found" | First run, tools not downloaded | winapp auto-downloads tools; ensure internet access | + +## Key files and concepts + +- **`winapp.yaml`** — Project config tracking SDK versions and settings. Created by `init`, read by `restore`/`update`. Not required for .NET projects that already have the right NuGet package references in their `.csproj` — winapp auto-detects SDK versions from `.csproj` as a fallback. +- **`appxmanifest.xml`** — MSIX package manifest defining app identity, capabilities, and visual assets. Required for packaging and identity. +- **`Assets/`** — Icon and tile images referenced by the manifest. Generated by `init` or `manifest generate`. +- **`.winapp/`** — Local directory with downloaded SDK packages, generated headers, and libs. Gitignored. +- **`devcert.pfx`** — Self-signed development certificate for local testing. Never use in production. +- **Sparse package** — A package registration that gives a desktop app package identity without full MSIX deployment. Used by `create-debug-identity`. +- **Package identity** — A Windows concept that enables certain APIs (notifications, background tasks, share target). Obtained either via full MSIX packaging or sparse package registration. diff --git a/.github/plugin/plugin.json b/.github/plugin/plugin.json new file mode 100644 index 00000000..fad841a8 --- /dev/null +++ b/.github/plugin/plugin.json @@ -0,0 +1,22 @@ +{ + "name": "winapp-cli", + "description": "Skills for Windows app development with winapp CLI, MSIX packaging, package identity, certificates, SDK projections, and more.", + "version": "0.2.0", + "author": { + "name": "Microsoft", + "url": "https://github.com/microsoft/WinAppCli" + }, + "license": "MIT", + "keywords": [ + "windows", + "msix", + "packaging", + "identity", + "electron", + "winapp", + "windows app sdk", + "appxmanifest.xml" + ], + "agents": "agents/", + "skills": "skills/winapp-cli/" +} diff --git a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md new file mode 100644 index 00000000..f8bc165b --- /dev/null +++ b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md @@ -0,0 +1,81 @@ + +--- +name: winapp-frameworks +description: Framework-specific guidance and links to detailed guides for Electron, .NET, C++, Rust, Flutter, and Tauri. Use when working with a specific app framework. +--- +## When to use + +Use this skill when: +- **Working with a specific app framework** and need to know the right winapp workflow +- **Choosing the correct install method** (npm package vs. standalone CLI) +- **Looking for framework-specific guides** for step-by-step setup, build, and packaging + +Each framework has a detailed guide — refer to the links below rather than trying to guess commands. + +## Framework guides + +| Framework | Install method | Guide | +|-----------|---------------|-------| +| **Electron** | `npm install --save-dev @microsoft/winappcli` | [Electron setup guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/setup.md) | +| **.NET** (WPF, WinForms, Console) | `winget install Microsoft.winappcli` | [.NET guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/dotnet.md) | +| **C++** (CMake, MSBuild) | `winget install Microsoft.winappcli` | [C++ guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/cpp.md) | +| **Rust** | `winget install Microsoft.winappcli` | [Rust guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/rust.md) | +| **Flutter** | `winget install Microsoft.winappcli` | [Flutter guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/flutter.md) | +| **Tauri** | `winget install Microsoft.winappcli` | [Tauri guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/tauri.md) | + +## Key differences by framework + +### Electron (npm package) +Use the **npm package** (`@microsoft/winappcli`), **not** the standalone CLI. The npm package includes: +- The native winapp CLI binary bundled inside `node_modules` +- A Node.js SDK with helpers for creating native C#/C++ addons +- Electron-specific commands under `npx winapp node` + +Quick start: +```powershell +npm install --save-dev @microsoft/winappcli +npx winapp init --use-defaults +npx winapp node create-addon --template cs # create a C# native addon +npx winapp node add-electron-debug-identity # register identity for debugging +``` + +Additional Electron guides: +- [Packaging guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/packaging.md) +- [C++ notification addon guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/cpp-notification-addon.md) +- [WinML addon guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/winml-addon.md) +- [Phi Silica addon guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/phi-silica-addon.md) + +### .NET (WPF, WinForms, Console) +.NET projects have direct access to Windows APIs. Key differences: +- Projects with NuGet references to `Microsoft.Windows.SDK.BuildTools` or `Microsoft.WindowsAppSDK` **don't need `winapp.yaml`** — winapp auto-detects SDK versions from the `.csproj` +- The key prerequisite is `appxmanifest.xml`, not `winapp.yaml` +- No native addon step needed — unlike Electron, .NET can call Windows APIs directly + +Quick start: +```powershell +winapp init --use-defaults +dotnet build +winapp create-debug-identity ./bin/Debug/net10.0-windows/myapp.exe +``` + +### C++ (CMake, MSBuild) +C++ projects use winapp primarily for SDK projections (CppWinRT headers) and packaging: +- `winapp init --setup-sdks stable` downloads Windows SDK + App SDK and generates CppWinRT headers +- Headers generated in `.winapp/generated/include` +- Response file at `.cppwinrt.rsp` for build system integration +- Add `.winapp/packages` to include/lib paths in your build system + +### Rust +- Use the `windows` crate for Windows API bindings +- winapp handles manifest, identity, packaging, and certificate management +- Typical build output: `target/release/myapp.exe` + +### Flutter +- Flutter handles the build (`flutter build windows`) +- winapp handles manifest, identity, packaging +- Build output: `build\windows\x64\runner\Release\` + +### Tauri +- Tauri has its own bundler for `.msi` installers +- Use winapp specifically for **MSIX distribution** and package identity features +- winapp adds capabilities beyond what Tauri's built-in bundler provides (identity, sparse packages, Windows API access) diff --git a/.github/plugin/skills/winapp-cli/identity/SKILL.md b/.github/plugin/skills/winapp-cli/identity/SKILL.md new file mode 100644 index 00000000..32cacbc5 --- /dev/null +++ b/.github/plugin/skills/winapp-cli/identity/SKILL.md @@ -0,0 +1,110 @@ + +--- +name: winapp-identity +description: Add package identity for debugging Windows APIs that require it (push notifications, background tasks, etc.) without creating a full MSIX package. +--- +## When to use + +Use this skill when: +- **Debugging Windows APIs** that require package identity (push notifications, background tasks, share target, startup tasks, etc.) +- **Testing identity-dependent features** without creating and installing a full MSIX package +- **Registering a sparse package** with Windows for development + +## Prerequisites + +1. **`appxmanifest.xml`** in your project — from `winapp init` or `winapp manifest generate` +2. **Built executable** — the `.exe` your app runs from + +## What is package identity? + +Windows package identity enables your app to use restricted APIs and OS integration features: +- **Push notifications** (WNS) +- **Background tasks** +- **Share target** / share source +- **App startup tasks** +- **Taskbar pinning** +- **Windows AI APIs** (Phi Silica, OCR, etc.) +- **File type associations** registered properly in Settings + +A standard `.exe` (from `dotnet build`, `cmake`, etc.) does **not** have identity. `create-debug-identity` registers a *sparse package* with Windows, giving your exe identity without packaging it into an MSIX. + +## Usage + +### Basic usage + +```powershell +# Register sparse package for your exe (manifest auto-detected from current dir) +winapp create-debug-identity ./bin/Release/myapp.exe + +# Specify manifest location +winapp create-debug-identity ./bin/Release/myapp.exe --manifest ./appxmanifest.xml +``` + +### Keep the original package identity + +```powershell +# By default, '.debug' is appended to the package name to avoid conflicts with +# an installed MSIX version. Use --keep-identity to keep the manifest identity as-is. +winapp create-debug-identity ./myapp.exe --keep-identity +``` + +### Generate without installing + +```powershell +# Create the sparse package layout but don't register it with Windows +winapp create-debug-identity ./myapp.exe --no-install +``` + +## What the command does + +1. **Reads `appxmanifest.xml`** — extracts identity, capabilities, and assets +2. **Creates a sparse package layout** in a temp directory +3. **Appends `.debug`** to the package name (unless `--keep-identity`) to avoid conflicts +4. **Registers with Windows** via `Add-AppxPackage -ExternalLocation` — makes your exe "identity-aware" + +After running, launch your exe normally — Windows will recognize it as having package identity. + +## Recommended workflow + +1. **Setup** — `winapp init --use-defaults` (creates `appxmanifest.xml`) +3. **Build** your app +4. **Register identity** — `winapp create-debug-identity ./bin/myapp.exe` +5. **Run** your app — identity-requiring APIs now work +6. **Re-run step 4** whenever you change `appxmanifest.xml` or `Assets/` + +## Tips + +- You must re-run `create-debug-identity` after any changes to `appxmanifest.xml` or image assets +- The debug identity persists across reboots until explicitly removed +- To remove: `Get-AppxPackage *yourapp.debug* | Remove-AppxPackage` +- If you have both a debug identity and an installed MSIX, they may conflict — use `--keep-identity` carefully +- For Electron apps, use `npx winapp node add-electron-debug-identity` instead (handles Electron-specific paths) + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "appxmanifest.xml not found" | No manifest in current directory | Run `winapp init` or `winapp manifest generate`, or pass `--manifest` | +| "Failed to add package identity" | Previous registration stale or cert untrusted | `Get-AppxPackage *yourapp* \| Remove-AppxPackage`, then `winapp cert install ./devcert.pfx` (admin) | +| "Access denied" | Cert not trusted or permission issue | Run `winapp cert install ./devcert.pfx` as admin | +| APIs still fail after registration | App launched before registration completed | Close app, re-run `create-debug-identity`, then relaunch | + + +## Command Reference + +### `winapp create-debug-identity` + +Enable package identity for debugging without creating full MSIX. Required for testing Windows APIs (push notifications, share target, etc.) during development. Example: winapp create-debug-identity ./myapp.exe. Requires appxmanifest.xml in current directory or passed via --manifest. Re-run after changing appxmanifest.xml or Assets/. + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | No | Path to the .exe that will need to run with identity, or entrypoint script. | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--keep-identity` | Keep the package identity from the manifest as-is, without appending '.debug' to the package name and application ID. | (none) | +| `--manifest` | Path to the appxmanifest.xml | (none) | +| `--no-install` | Do not install the package after creation. | (none) | diff --git a/.github/plugin/skills/winapp-cli/manifest/SKILL.md b/.github/plugin/skills/winapp-cli/manifest/SKILL.md new file mode 100644 index 00000000..2af71f1c --- /dev/null +++ b/.github/plugin/skills/winapp-cli/manifest/SKILL.md @@ -0,0 +1,165 @@ + +--- +name: winapp-manifest +description: Generate and modify appxmanifest.xml files for package identity and MSIX packaging with winapp CLI. +--- +## When to use + +Use this skill when: +- **Creating `appxmanifest.xml`** for a project that doesn't have one yet +- **Generating app icon assets** from a single source image +- **Understanding manifest structure** for package identity and capabilities + +## Prerequisites + +- winapp CLI installed +- Optional: a source image (PNG, at least 400x400 pixels) for custom app icons + +## Key concepts + +**`appxmanifest.xml`** is the key prerequisite for most winapp commands — it's more important than `winapp.yaml`. It declares: +- **Package identity** — name, publisher, version +- **App entry point** — which executable to launch +- **Capabilities** — what the app can access (internet, file system, etc.) +- **Visual assets** — icons for Start menu, taskbar, installers +- **Extensions** — share target, startup tasks, file associations, etc. + +**Two manifest templates:** +- **`packaged`** (default) — for full MSIX distribution +- **`sparse`** — for desktop apps that need package identity without full MSIX containment (uses `AllowExternalContent`) + +**`winapp init` also generates a manifest** as part of full project setup. Use `winapp manifest generate` when you only need the manifest without SDK setup or `winapp.yaml`. + +## Usage + +### Generate a new manifest + +```powershell +# Defaults — uses current folder name, current user as publisher +winapp manifest generate + +# Into a specific directory +winapp manifest generate ./my-project + +# Customize identity +winapp manifest generate --package-name "MyApp" --publisher-name "CN=Contoso" --version "2.0.0.0" + +# Set entry point and description +winapp manifest generate --executable myapp.exe --description "My awesome app" + +# Generate a sparse manifest (for desktop apps needing identity without full MSIX) +winapp manifest generate --template sparse + +# Overwrite existing manifest +winapp manifest generate --if-exists overwrite +``` + +Output: +- `appxmanifest.xml` — the manifest file +- `Assets/` — default app icons in required sizes (Square44x44Logo, Square150x150Logo, Wide310x150Logo, etc.) + +### Update app icons from a source image + +```powershell +# Generate all required icon sizes from one source image +winapp manifest update-assets ./my-logo.png + +# Specify manifest location (if not in current directory) +winapp manifest update-assets ./my-logo.png --manifest ./path/to/appxmanifest.xml +``` + +The source image should be at least 400x400 pixels (PNG recommended). The command reads the manifest to determine which asset sizes are needed and generates them all. + +## Manifest structure overview + +A typical `appxmanifest.xml` looks like: + +```xml + + + + + My App + My Publisher + Assets\StoreLogo.png + + + + + + + + + + + + + +``` + +Key fields to edit: +- `Identity.Name` — unique package name (no spaces) +- `Identity.Publisher` — must match your certificate exactly +- `Application.Executable` — your app's exe filename +- `Capabilities` — add capabilities as needed (`internetClient`, `broadFileSystemAccess`, etc.) + +## Tips + +- Always ensure `Identity.Publisher` matches your signing certificate — use `winapp cert generate --manifest` to auto-match +- The `sparse` template adds `uap10:AllowExternalContent="true"` for apps that need identity but run outside the MSIX container +- You can manually edit `appxmanifest.xml` after generation — it's a standard XML file +- Image assets must match the paths referenced in the manifest — `update-assets` handles this automatically +- For logos, transparent PNGs work best. Use a square image for best results across all sizes. + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "Manifest already exists" | `appxmanifest.xml` present | Use `--if-exists overwrite` to replace, or edit existing file directly | +| "Invalid source image" | Image too small or wrong format | Use PNG, at least 400x400 pixels | +| "Publisher mismatch" during packaging | Manifest publisher ≠ cert publisher | Edit `Identity.Publisher` in manifest, or regenerate cert with `--manifest` | + + +## Command Reference + +### `winapp manifest generate` + +Create appxmanifest.xml without full project setup. Use when you only need a manifest and image assets (no SDKs, no certificate). For full setup, use 'init' instead. Templates: 'packaged' (full MSIX), 'sparse' (desktop app needing Windows APIs). + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | No | Directory to generate manifest in | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--description` | Human-readable app description shown during installation and in Windows Settings | `My Application` | +| `--executable` | Path to the application's executable. Default: .exe | (none) | +| `--if-exists` | Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) | `Error` | +| `--logo-path` | Path to logo image file | (none) | +| `--package-name` | Package name (default: folder name) | (none) | +| `--publisher-name` | Publisher CN (default: CN=) | (none) | +| `--template` | Manifest template type: 'packaged' (full MSIX app, default) or 'sparse' (desktop app with package identity for Windows APIs) | `Packaged` | +| `--version` | App version in Major.Minor.Build.Revision format (e.g., 1.0.0.0). | `1.0.0.0` | + +### `winapp manifest update-assets` + +Generate new assets for images referenced in an appxmanifest.xml from a single source image. Source image should be at least 400x400 pixels. + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | Yes | Path to source image file | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--manifest` | Path to AppxManifest.xml file (default: search current directory) | (none) | diff --git a/.github/plugin/skills/winapp-cli/package/SKILL.md b/.github/plugin/skills/winapp-cli/package/SKILL.md new file mode 100644 index 00000000..9c5b7d29 --- /dev/null +++ b/.github/plugin/skills/winapp-cli/package/SKILL.md @@ -0,0 +1,180 @@ + +--- +name: winapp-package +description: Create an MSIX installer package from a built Windows app using winapp CLI. Use when the user needs to package, distribute, or test their Windows app as an MSIX. +--- +## When to use + +Use this skill when: +- **Creating an MSIX installer** from a built app for distribution or testing +- **Signing a package** with a development or production certificate +- **Bundling the Windows App SDK runtime** for self-contained deployment + +## Prerequisites + +Before packaging, you need: +1. **Built app output** in a folder (e.g., `bin/Release/`, `dist/`, `build/`) +2. **`appxmanifest.xml`** — from `winapp init` or `winapp manifest generate` +3. **Certificate** (optional) — `devcert.pfx` from `winapp cert generate` for signing + +## Usage + +### Basic packaging (unsigned) + +```powershell +# Package from build output — manifest auto-detected from current dir or input folder +winapp package ./bin/Release + +# Specify manifest location explicitly +winapp package ./dist --manifest ./appxmanifest.xml +``` + +### Package and sign in one step + +```powershell +# Sign with existing certificate +winapp package ./bin/Release --cert ./devcert.pfx + +# Custom certificate password +winapp package ./bin/Release --cert ./devcert.pfx --cert-password MyP@ssw0rd +``` + +### Generate certificate + package in one step + +```powershell +# Auto-generate cert, sign, and package +winapp package ./bin/Release --generate-cert + +# Also install the cert to trust it on this machine (requires admin) +winapp package ./bin/Release --generate-cert --install-cert +``` + +### Self-contained deployment + +```powershell +# Bundle Windows App SDK runtime so users don't need it installed (must have winappsdk reference in the winapp.yaml or *.csproj) +winapp package ./bin/Release --cert ./devcert.pfx --self-contained +``` + +### Custom output path and name + +```powershell +# Specify output file +winapp package ./dist --output ./releases/myapp-v1.0.msix --cert ./devcert.pfx + +# Custom package name +winapp package ./dist --name "MyApp_1.0.0_x64" --cert ./devcert.pfx +``` + +## What the command does + +1. **Locates `appxmanifest.xml`** — looks in input folder, then current directory (or uses `--manifest`) +2. **Copies manifest + assets** into a staging layout alongside your app files +3. **Generates `resources.pri`** — Package Resource Index for UWP-style resource lookup (skip with `--skip-pri`) +4. **Runs `makeappx pack`** — creates the `.msix` package file +5. **Signs the package** (if `--cert` provided) — calls `signtool` with your certificate + +Output: a `.msix` file that can be installed on Windows via double-click or `Add-AppxPackage`. + +## Installing the MSIX for testing + +```powershell +# Trust the dev certificate first (one-time, requires admin) +winapp cert install ./devcert.pfx + +# Install the MSIX +Add-AppxPackage ./myapp.msix + +# Uninstall if needed +Get-AppxPackage *myapp* | Remove-AppxPackage +``` + +## Recommended workflow + +1. **Build** your app (`dotnet build`, `cmake --build`, `npm run make`, etc.) +2. **Package** — `winapp package --cert ./devcert.pfx` +3. **Trust cert** (first time) — `winapp cert install ./devcert.pfx` (admin) +4. **Install** — double-click the `.msix` or `Add-AppxPackage ./myapp.msix` +5. **Test** the installed app from the Start menu + +### Advanced: External content catalog + +For sparse packages with `AllowExternalContent`, you may need a code integrity catalog: + +```powershell +# Generate CodeIntegrityExternal.cat for external executables +winapp create-external-catalog "./bin/Release" + +# Include subdirectories and specify output path +winapp create-external-catalog "./bin/Release" --recursive --output ./catalog/CodeIntegrityExternal.cat +``` + +This hashes executables in the specified directories so Windows trusts them when running with sparse package identity. + +## Tips + +- The `package` command aliases to `pack` — both work identically +- `appxmanifest.xml` Publisher must match the certificate publisher — use `winapp cert generate --manifest` to ensure they match +- Use `--skip-pri` if your app doesn't use Windows resource loading (e.g., most Electron/Rust/C++ apps without UWP resources) +- For framework-specific packaging paths (Electron, .NET, Rust, etc.), see the `winapp-frameworks` skill +- The `--executable` flag overrides the entry point in the manifest — useful when your exe name differs from what's in `appxmanifest.xml` +- For production distribution, use a certificate from a trusted CA and add `--timestamp` when signing with `winapp sign` + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "appxmanifest.xml not found" | No manifest in input folder or current dir | Run `winapp init` or `winapp manifest generate` first | +| "Publisher mismatch" | Cert publisher ≠ manifest publisher | Regenerate cert with `winapp cert generate --manifest`, or edit manifest | +| "Package installation failed" | Cert not trusted or stale package | Run `winapp cert install ./devcert.pfx` (admin), then `Get-AppxPackage \| Remove-AppxPackage` | +| "makeappx not found" | Build tools not downloaded | Run `winapp update` or `winapp tool makeappx --help` to trigger download | + + +## Command Reference + +### `winapp package` + +Create MSIX installer from your built app. Run after building your app. appxmanifest.xml is required for packaging - it must be in current working directory, passed as --manifest or be in the input folder. Use --cert devcert.pfx to sign for testing. Example: winapp package ./dist --manifest appxmanifest.xml --cert ./devcert.pfx + +**Aliases:** `pack` + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | Yes | Input folder with package layout | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--cert` | Path to signing certificate (will auto-sign if provided) | (none) | +| `--cert-password` | Certificate password (default: password) | `password` | +| `--executable` | Path to the executable relative to the input folder. | (none) | +| `--generate-cert` | Generate a new development certificate | (none) | +| `--install-cert` | Install certificate to machine | (none) | +| `--manifest` | Path to AppX manifest file (default: auto-detect from input folder or current directory) | (none) | +| `--name` | Package name (default: from manifest) | (none) | +| `--output` | Output msix file name for the generated package (defaults to .msix) | (none) | +| `--publisher` | Publisher name for certificate generation | (none) | +| `--self-contained` | Bundle Windows App SDK runtime for self-contained deployment | (none) | +| `--skip-pri` | Skip PRI file generation | (none) | + +### `winapp create-external-catalog` + +Generates a CodeIntegrityExternal.cat catalog file with hashes of executable files from specified directories. Used with the TrustedLaunch flag in MSIX sparse package manifests (AllowExternalContent) to allow execution of external files not included in the package. + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | Yes | List of input folders with executable files to process (separated by semicolons) | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--compute-flat-hashes` | Include flat hashes when generating the catalog | (none) | +| `--if-exists` | Behavior when output file already exists | `Error` | +| `--output` | Output catalog file path. If not specified, the default CodeIntegrityExternal.cat name is used. | (none) | +| `--recursive` | Include files from subdirectories | (none) | +| `--use-page-hashes` | Include page hashes when generating the catalog | (none) | diff --git a/.github/plugin/skills/winapp-cli/setup/SKILL.md b/.github/plugin/skills/winapp-cli/setup/SKILL.md new file mode 100644 index 00000000..3632bb4e --- /dev/null +++ b/.github/plugin/skills/winapp-cli/setup/SKILL.md @@ -0,0 +1,180 @@ + +--- +name: winapp-setup +description: Project setup with winapp CLI: init, restore, update. Use when setting up a new project, restoring after clone, or updating SDK versions. +--- +## When to use + +Use this skill when: +- **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. + +## Key concepts + +**`appxmanifest.xml`** is the most important file winapp creates — it declares your app's identity, capabilities, and visual assets. Most winapp commands require it (`package`, `create-debug-identity`, `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 + +### Initialize a new winapp project + +```powershell +# Interactive — prompts for app name, publisher, SDK channel, etc. +winapp init . + +# Non-interactive — accepts all defaults (stable SDKs, current folder name as app name) +winapp init --use-defaults + +# 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: +- `appxmanifest.xml` — 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` + +### 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. + +### 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. + +## Recommended workflow + +1. **Initialize** — `winapp init --use-defaults` in your existing project +2. **Configure** — edit `appxmanifest.xml` to add capabilities your app needs (e.g., `runFullTrust`, `internetClient`) +3. **Build** — build your app as usual (dotnet build, cmake, npm run build, etc.) +4. **Debug with identity** — `winapp create-debug-identity ./bin/myapp.exe` to test Windows APIs +5. **Package** — `winapp package ./bin/Release --cert ./devcert.pfx` to create MSIX + +### Generate AI agent skills + +```powershell +# Generate SKILL.md files for AI coding assistants (Copilot, Claude, Cursor, etc.) +winapp agents generate + +# Specify a custom skills directory +winapp agents generate --skills-dir ./my-skills +``` + +This is also run automatically during `winapp init` (unless `--no-skills` is passed). + +## Tips + +- Use `--use-defaults` (alias: `--no-prompt`) in CI/CD pipelines and scripts to avoid interactive prompts +- If you only need `appxmanifest.xml` without SDK setup, use `winapp manifest generate` instead of `init` +- The `--no-skills` flag skips AI agent skill file generation if you don't use Copilot/Claude/Cursor +- `winapp init` is idempotent for the config file — re-running it won't overwrite an existing `winapp.yaml` unless you use `--config-only` +- For Electron projects, prefer `npm install --save-dev @microsoft/winappcli` and use `npx winapp init` instead of the standalone CLI + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "winapp.yaml not found" | Running `restore`/`update` without config | Run `winapp init` first, or ensure you're in the right directory | +| "Directory not found" | Target directory doesn't exist | Create the directory first or check the path | +| SDK download fails | Network issue or firewall | Ensure internet access; check proxy settings | +| `init` prompts unexpectedly in CI | Missing `--use-defaults` flag | Add `--use-defaults` to skip all prompts | + + +## Command Reference + +### `winapp init` + +Start here for initializing a Windows app with required setup. Sets up everything needed for Windows app development: creates appxmanifest.xml with default assets, creates winapp.yaml for version management, and downloads Windows SDK and Windows App SDK packages and generates projections. Interactive by default (use --use-defaults to skip prompts). Use 'restore' instead if you cloned a repo that already has winapp.yaml. Use 'manifest generate' if you only need a manifest, or 'cert generate' if you need a development certificate for code signing. + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | No | Base/root directory for the winapp workspace, for consumption or installation. | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--config-dir` | Directory to read/store configuration (default: current directory) | (none) | +| `--config-only` | Only handle configuration file operations (create if missing, validate if exists). Skip package installation and other workspace setup steps. | (none) | +| `--ignore-config` | Don't use configuration file for version management | (none) | +| `--no-gitignore` | Don't update .gitignore file | (none) | +| `--no-skills` | Don't generate AI agent skill files (for Copilot, Claude, etc.) | (none) | +| `--setup-sdks` | SDK installation mode: 'stable' (default), 'preview', 'experimental', or 'none' (skip SDK installation) | (none) | +| `--use-defaults` | Do not prompt, and use default of all prompts | (none) | + +### `winapp restore` + +Use after cloning a repo or when .winapp/ folder is missing. Reinstalls SDK packages from existing winapp.yaml without changing versions. Requires winapp.yaml (created by 'init'). To check for newer SDK versions, use 'update' instead. + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | No | Base/root directory for the winapp workspace | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--config-dir` | Directory to read configuration from (default: current directory) | (none) | + +### `winapp update` + +Check for and install newer SDK versions. Updates winapp.yaml with latest versions and reinstalls packages. Requires existing winapp.yaml (created by 'init'). Use --setup-sdks preview for preview SDKs. To reinstall current versions without updating, use 'restore' instead. + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--setup-sdks` | SDK installation mode: 'stable' (default), 'preview', 'experimental', or 'none' (skip SDK installation) | (none) | + +### `winapp agents generate` + +Generate SKILL.md files that help AI coding agents (GitHub Copilot, Claude Code, etc.) understand your winapp project. Skills are placed in the detected skills directory (.github/skills/, .agents/skills/, or .claude/skills/). Use --skills-dir to override. + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--directory` | Project root directory (default: current directory) | (none) | +| `--skills-dir` | Skills directory override (default: auto-detect from .github/skills, .agents/skills, or .claude/skills) | (none) | diff --git a/.github/plugin/skills/winapp-cli/signing/SKILL.md b/.github/plugin/skills/winapp-cli/signing/SKILL.md new file mode 100644 index 00000000..299f5797 --- /dev/null +++ b/.github/plugin/skills/winapp-cli/signing/SKILL.md @@ -0,0 +1,155 @@ + +--- +name: winapp-signing +description: Generate, install, and manage development certificates for code signing MSIX packages and executables with winapp CLI. +--- +## When to use + +Use this skill when: +- **Generating a development certificate** for local MSIX signing and testing +- **Installing (trusting) a certificate** on a machine so MSIX packages can be installed +- **Signing an MSIX package or executable** for distribution + +## Prerequisites + +- winapp CLI installed +- **Administrator access** required for `cert install` (trusting certificates on the machine) + +## Key concepts + +**Publisher matching:** The publisher in your certificate (e.g., `CN=MyCompany`) must exactly match the `Publisher` attribute in `appxmanifest.xml`. Use `--manifest` when generating to auto-match. + +**Dev vs. production certs:** `winapp cert generate` creates self-signed certificates for **local testing only**. For production distribution (Microsoft Store or enterprise), obtain a certificate from a trusted Certificate Authority. + +**Default password:** Generated certificates use `password` as the default PFX password. Override with `--password`. + +## Usage + +### Generate a development certificate + +```powershell +# Auto-infer publisher from appxmanifest.xml in the current directory +winapp cert generate + +# Explicitly point to a manifest +winapp cert generate --manifest ./path/to/appxmanifest.xml + +# Set publisher manually (when no manifest exists yet) +winapp cert generate --publisher "CN=Contoso, O=Contoso Ltd, C=US" + +# Custom output path and password +winapp cert generate --output ./certs/myapp.pfx --password MySecurePassword + +# Custom validity period +winapp cert generate --valid-days 730 + +# Overwrite existing certificate +winapp cert generate --if-exists overwrite +``` + +Output: `devcert.pfx` (or custom path via `--output`). + +### Install (trust) a certificate + +```powershell +# Trust the certificate on this machine (requires admin/elevated terminal) +winapp cert install ./devcert.pfx + +# Force reinstall even if already trusted +winapp cert install ./devcert.pfx --force +``` + +This adds the certificate to the local machine's **Trusted Root Certification Authorities** store. Required before double-clicking MSIX packages or running `Add-AppxPackage`. + +### Sign a file + +```powershell +# Sign an MSIX package +winapp sign ./myapp.msix ./devcert.pfx + +# Sign with custom password +winapp sign ./myapp.msix ./devcert.pfx --password MySecurePassword + +# Sign with timestamp for production (signature remains valid after cert expires) +winapp sign ./myapp.msix ./production.pfx --timestamp http://timestamp.digicert.com +``` + +Note: The `package` command can sign automatically when you pass `--cert`, so you often don't need `sign` separately. + +## Recommended workflow + +1. **Generate cert** — `winapp cert generate` (auto-infers publisher from manifest) +2. **Trust cert** (one-time) — `winapp cert install ./devcert.pfx` (run as admin) +3. **Package + sign** — `winapp package ./dist --cert ./devcert.pfx` +4. **Distribute** — share the `.msix`; recipients must also trust the cert, or use a trusted CA cert + +## Tips + +- Always use `--manifest` (or have `appxmanifest.xml` in the working directory) when generating certs to ensure the publisher matches automatically +- For CI/CD, store the PFX as a secret and pass the password via `--password` rather than using the default +- `winapp cert install` modifies the machine certificate store — it persists across reboots and user sessions +- Use `--timestamp` when signing production builds so the signature survives certificate expiration +- You can also use the shorthand: `winapp package ./dist --generate-cert --install-cert` to do everything in one command + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "Publisher mismatch" | Cert publisher ≠ manifest publisher | `winapp cert generate --manifest ./appxmanifest.xml` to re-generate with correct publisher | +| "Access denied" / "elevation required" | `cert install` needs admin | Run your terminal as Administrator | +| "Certificate not trusted" | Cert not installed on machine | `winapp cert install ./devcert.pfx` (admin) | +| "Certificate file already exists" | `devcert.pfx` already present | Use `--if-exists overwrite` or `--if-exists skip` | +| Signature invalid after time passes | No timestamp used during signing | Re-sign with `--timestamp http://timestamp.digicert.com` | + + +## Command Reference + +### `winapp cert generate` + +Create a self-signed certificate for local testing only. Publisher must match AppxManifest.xml (auto-inferred if --manifest provided or appxmanifest.xml is in working directory). Output: devcert.pfx (default password: 'password'). For production, obtain a certificate from a trusted CA. Use 'cert install' to trust on this machine. + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--if-exists` | Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) | `Error` | +| `--install` | Install the certificate to the local machine store after generation | (none) | +| `--manifest` | Path to appxmanifest.xml file to extract publisher information from | (none) | +| `--output` | Output path for the generated PFX file | (none) | +| `--password` | Password for the generated PFX file | `password` | +| `--publisher` | Publisher name for the generated certificate. If not specified, will be inferred from manifest. | (none) | +| `--valid-days` | Number of days the certificate is valid | `365` | + +### `winapp cert install` + +Trust a certificate on this machine (requires admin). Run before installing MSIX packages signed with dev certificates. Example: winapp cert install ./devcert.pfx. Only needed once per certificate. + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | Yes | Path to the certificate file (PFX or CER) | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--force` | Force installation even if the certificate already exists | (none) | +| `--password` | Password for the PFX file | `password` | + +### `winapp sign` + +Code-sign an MSIX package or executable. Example: winapp sign ./app.msix ./devcert.pfx. Use --timestamp for production builds to remain valid after cert expires. The 'package' command can sign automatically with --cert. + +#### Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `` | Yes | Path to the file/package to sign | +| `` | Yes | Path to the certificate file (PFX format) | + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--password` | Certificate password | `password` | +| `--timestamp` | Timestamp server URL | (none) | diff --git a/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md new file mode 100644 index 00000000..d420af47 --- /dev/null +++ b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md @@ -0,0 +1,123 @@ + +--- +name: winapp-troubleshoot +description: Diagnose and fix common errors with winapp CLI. Use when the user encounters errors or needs help choosing the right command. +--- +## When to use + +Use this skill when: +- **Diagnosing errors** from winapp CLI commands +- **Choosing the right command** for a task +- **Understanding prerequisites** — what each command needs and what it produces + +## Common errors & solutions + +| Error | Cause | Solution | +|-------|-------|----------| +| "winapp.yaml not found" | Running `restore` or `update` without config | Run `winapp init` first, or `cd` to the directory containing `winapp.yaml` | +| "appxmanifest.xml not found" | Running `package`, `create-debug-identity`, or `cert generate --manifest` | Run `winapp init` or `winapp manifest generate` first, or pass `--manifest ` | +| "Publisher mismatch" | Certificate publisher ≠ manifest publisher | Regenerate cert: `winapp cert generate --manifest`, or edit `appxmanifest.xml` `Identity.Publisher` to match | +| "Access denied" / "elevation required" | `cert install` without admin | Run terminal as Administrator for `winapp cert install` | +| "Package installation failed" | Cert not trusted, or stale package registration | `winapp cert install ./devcert.pfx` (admin), then `Get-AppxPackage \| Remove-AppxPackage` | +| "Certificate not trusted" | Dev cert not installed on machine | `winapp cert install ./devcert.pfx` (admin) | +| "Build tools not found" | First run, tools not yet downloaded | Run `winapp update` to download tools; ensure internet access | +| "Failed to add package identity" | Stale debug identity or untrusted cert | `Get-AppxPackage *yourapp* \| Remove-AppxPackage` to clean up, then `winapp cert install` and retry | +| "Certificate file already exists" | `devcert.pfx` already present | Use `winapp cert generate --if-exists overwrite` or `--if-exists skip` | +| "Manifest already exists" | `appxmanifest.xml` already present | Use `winapp manifest generate --if-exists overwrite` or edit manifest directly | + +## Command selection guide + +``` +Does the project have an appxmanifest.xml? +├─ No → Do you want full setup (manifest + config + optional SDKs)? +│ ├─ Yes → winapp init (adds Windows platform files to existing project) +│ └─ No, just a manifest → winapp manifest generate +└─ Yes + ├─ Has winapp.yaml, cloned/pulled but .winapp/ folder missing? + │ └─ winapp restore + ├─ Want newer SDK versions? + │ └─ winapp update + ├─ Need a dev certificate? + │ └─ winapp cert generate (then winapp cert install for trust) + ├─ Need package identity for debugging? + │ └─ winapp create-debug-identity + ├─ Ready to create MSIX installer? + │ └─ winapp package --cert ./devcert.pfx + ├─ Need to sign an existing file? + │ └─ winapp sign + ├─ Need to update app icons? + │ └─ winapp manifest update-assets ./logo.png + ├─ Need to run SDK tools directly? + │ └─ winapp tool + ├─ Want AI agent skills in your repo? + │ └─ winapp agents generate + ├─ Need to publish to Microsoft Store? + │ └─ winapp store (passthrough to Store Developer CLI) + └─ Need the .winapp directory path for build scripts? + └─ winapp get-winapp-path (or --global for shared cache) +``` + +**Important notes:** +- `winapp init` adds files to an **existing** project — it does not create a new project +- The key prerequisite for most commands is `appxmanifest.xml`, not `winapp.yaml` +- `winapp.yaml` is only needed for SDK version management (`restore`/`update`) +- Projects with NuGet package references (e.g., `.csproj` referencing `Microsoft.Windows.SDK.BuildTools`) can use winapp commands without `winapp.yaml` +- For Electron projects, use the npm package (`npm install --save-dev @microsoft/winappcli`) which includes Node.js-specific commands under `npx winapp node` + +## Prerequisites & state matrix + +| Command | Requires | Creates/Modifies | +|---------|----------|------------------| +| `init` | Existing project (any framework) | `winapp.yaml`, `.winapp/`, `appxmanifest.xml`, `Assets/`, `.gitignore` update | +| `restore` | `winapp.yaml` | `.winapp/packages/`, generated projections | +| `update` | `winapp.yaml` | Updates versions in `winapp.yaml`, reinstalls packages | +| `manifest generate` | Nothing | `appxmanifest.xml`, `Assets/` | +| `manifest update-assets` | `appxmanifest.xml` + source image | Regenerates `Assets/` icons | +| `cert generate` | Nothing (or `appxmanifest.xml` for publisher) | `devcert.pfx` | +| `cert install` | Certificate file + admin | Machine certificate store | +| `create-debug-identity` | `appxmanifest.xml` + exe + trusted cert | Registers sparse package with Windows | +| `package` | Build output + `appxmanifest.xml` | `.msix` file | +| `sign` | File + certificate | Signed file (in-place) | +| `create-external-catalog` | Directory with executables | `CodeIntegrityExternal.cat` | +| `agents generate` | Nothing | `.github/skills/winapp-cli/` (or detected skills dir) | +| `tool ` | Nothing (auto-downloads tools) | Runs SDK tool directly | +| `store` | Nothing (auto-downloads Store CLI) | Passthrough to Microsoft Store Developer CLI | +| `get-winapp-path` | Nothing | Prints `.winapp` directory path | + +## Debugging tips + +- Add `--verbose` (or `-v`) to any command for detailed output +- Add `--quiet` (or `-q`) to suppress progress messages (useful in CI/CD) +- Run `winapp --cli-schema` to get the full JSON schema of all commands and options +- Run any command with `--help` for its specific usage information +- Use `winapp get-winapp-path` to find where packages are stored locally +- Use `winapp get-winapp-path --global` to find the shared cache location + +## Getting more help + +- Full CLI documentation: https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md +- Framework-specific guides: https://github.com/microsoft/WinAppCli/tree/main/docs/guides +- File an issue: https://github.com/microsoft/WinAppCli/issues + + +## Command Reference + +### `winapp get-winapp-path` + +Print the path to the .winapp directory. Use --global for the shared cache location, or omit for the project-local .winapp folder. Useful for build scripts that need to reference installed packages. + +#### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--global` | Get the global .winapp directory instead of local | (none) | + +### `winapp tool` + +Run Windows SDK tools directly (makeappx, signtool, makepri, etc.). Auto-downloads Build Tools if needed. For most tasks, prefer higher-level commands like 'package' or 'sign'. Example: winapp tool makeappx pack /d ./folder /p ./out.msix + +**Aliases:** `run-buildtool` + +### `winapp store` + +Run a Microsoft Store Developer CLI command. This command will download the Microsoft Store Developer CLI if not already downloaded. Learn more about the Microsoft Store Developer CLI here: https://aka.ms/msstoredevcli diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..aa908565 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,89 @@ +# Agent instructions for winapp + +This file provides focused, actionable information to help an AI coding agent be immediately productive in this repo. + +## Big picture + +Two main components: +- **src/winapp-CLI** (C#/.NET): The native CLI implemented with System.CommandLine. Key files: `Program.cs`, `Commands/*.cs` (e.g., `InitCommand.cs`, `PackageCommand.cs`). Build with `scripts/build-cli.ps1`. +- **src/winapp-npm** (Node): A thin Node wrapper/SDK and CLI (`cli.js`) that forwards most commands to the native CLI. Key helpers: `winapp-cli-utils.js`, `msix-utils.js`, `cpp-addon-utils.js`. Install with `npm install` inside `src/winapp-npm`. + +## Developer workflows + +```powershell +# Build everything and generate autogenerated files +# You can pass -SkipTests, -SkipMsix, and/or -SkipNpm to speed up the build when those pieces are not needed +.\scripts\build-cli.ps1 + +# Or build directly with dotnet +dotnet build src/winapp-CLI/winapp.sln -c Debug + +# Run native CLI in-tree +dotnet run --project src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj -- + +# Update npm package after CLI changes +cd src/winapp-npm && npm run build # builds C# CLI + copies to npm bin +cd src/winapp-npm && npm run build-copy-only # copies already-built Release binaries + +# Node package development +cd src/winapp-npm && npm install +node cli.js help + +# Always call the build script at the end to ensure everything builds and all autogenerated docs are generated +.\scripts\build-cli.ps1 +``` + +# Always update documentation and samples +When adding or changing public facing features, ensure all documentation is also updated. Places to update (but not limitted to): + +- docs\usage.md +- docs\guides\ +- samples\ +- docs\fragments\skills\ +- Readme.md +- .github\plugin\agents\ + +(.github\plugin\skills are autogenerated from docs\fragments\skills) + +If a feature is big enough and requires it's own docs page, add it under docs\ + + +## Where to look first + +| Area | Key files | +|------|-----------| +| CLI commands | `src/winapp-CLI/WinApp.Cli/Commands/*.cs` | +| Services | `src/winapp-CLI/WinApp.Cli/Services/*.cs` | +| Node CLI | `src/winapp-npm/cli.js`, `winapp-cli-utils.js` | +| Config example | `winapp.example.yaml` | +| CLI schema | `docs/cli-schema.json` | +| Agent skill templates | `docs/fragments/skills/winapp-cli/` | +| Copilot plugin | `.github/plugin/` | +| Samples | `samples/` (electron, cpp-app, dotnet-app, etc.) | + +## CLI command semantics + +Look at the `docs\cli-schema.json` for the full schema to know what the cli can do + +## Quick change checklist + +- **Adding a new CLI command**: Implement in C# under `Commands/`, register in `HostBuilderExtensions.cs`, inject into `WinAppRootCommand`, update `src/winapp-npm/cli.js` if needed.. +- **Changing command descriptions**: Edit the description string in the command's constructor. +- **Changing CLI commands/workflows**: Update the hand-written skill template in `docs/fragments/skills/` if the workflow, examples, or troubleshooting for that command changed. Run `scripts/build-cli.ps1` at the end to regenerate all skills. +- **After C# CLI changes**: Run `cd src/winapp-npm && npm run build` to update npm package binaries. +- **Updating package versions**: Edit `winapp.example.yaml`. + +## Integration points + +- **NuGet**: Packages downloaded to NuGet global cache. Local `.winapp` folder contains generated headers and libs only. +- **Build Tools**: makeappx.exe, signtool.exe, makepri.exe, etc. Auto-downloaded by the `tool` command or when commands that need them are invoked. +- **CppWinRT**: Generated headers in `.winapp/generated/include`. Response file at `.cppwinrt.rsp`. + +## Auto-generation pipeline + +The following files are auto-generated from `cli-schema.json` via `scripts/generate-llm-docs.ps1`. Do not run this script directly and run via the `build-cli.ps1` script. Do not edit these files directly: + +- `docs/cli-schema.json` — machine-readable schema +- `.github/plugin/skills/winapp-cli/*/SKILL.md` — Copilot CLI plugin skills (also embedded in CLI binary via csproj link) + +The hand-written workflow content lives in `docs/fragments/skills/winapp-cli/`. Running `scripts/build-cli.ps1` triggers regeneration automatically. diff --git a/README.md b/README.md index a15226c2..86ef3688 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,10 @@ npx winapp --help - [`sign`](./docs/usage.md#sign) - Sign MSIX packages and executables - [`create-external-catalog`](./docs/usage.md#create-external-catalog) - Generate CodeIntegrityExternal.cat for TrustedLaunch sparse packages +**AI Agent Integration:** + +- [`agents generate`](./docs/usage.md#agents-generate) - Generate AI agent skill files for coding assistants (Copilot, Claude, Cursor) + **Development Tools:** - [`tool`](./docs/usage.md#tool) - Access Windows SDK tools @@ -208,38 +212,23 @@ This repository includes samples demonstrating how to use the CLI with various f | [Tauri App](/samples/tauri-app/README.md) | Tauri cross-platform app with Rust backend | | [Flutter App](/samples/flutter-app/README.md) | Flutter desktop app with package identity and Windows App SDK | -## 🤖 Using with AI Assistants and Agents - -The winapp CLI is designed to work well with AI coding assistants like GitHub Copilot, Cursor, Claude, and other LLM-powered tools. +## 🤖 Using with AI Coding Agents -### Quick Start Prompt - -Copy and paste this into your AI coding assistant: +AI coding agents (GitHub Copilot, Claude Code, etc) auto-discover skill files in your project. Two ways to set this up: +**Option 1: GitHub Copilot CLI Plugin** (global — works across all projects) +```bash +copilot plugin install microsoft/WinAppCli ``` -I'm working with winapp CLI - a CLI for generating and managing appxmanifest.xml, -image assets, test certificates, Windows (App) SDK projections, package identity, -and packaging for any app framework targeting Windows. - -Please read and reference the official LLM context documentation: -https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -My specific task: [describe what you need help with] +**Option 2: Project-level skills** (checked into your repo) +```bash +winapp agents generate ``` +This creates skill files (`SKILL.md`) in your project that AI agents auto-discover. Skills are also generated automatically during `winapp init`. -### Resources for AI Assistants - -- **[LLM Context Guide](./docs/llm-context.md)** - Comprehensive command reference with workflows and prerequisites -- **[CLI Schema (JSON)](./docs/cli-schema.json)** - Machine-readable command structure for tooling -- **[Using with LLMs Guide](./docs/using-with-llms.md)** - Detailed prompts for GitHub Copilot, Cursor, Claude, and framework-specific templates - -### Programmatic Access +Both options give agents full understanding of winapp commands, workflows, and troubleshooting. -Get the complete CLI structure as JSON for tooling or analysis: - -```bash -winapp --cli-schema -``` ## 🔧 Feedback and Support diff --git a/docs/cli-schema.json b/docs/cli-schema.json index 5fd547e9..d072bd21 100644 --- a/docs/cli-schema.json +++ b/docs/cli-schema.json @@ -50,6 +50,74 @@ } }, "subcommands": { + "agents": { + "description": "Manage AI agent context for coding assistants (GitHub Copilot, Claude Code, etc.). Use 'agents generate' to create SKILL.md files that help AI agents understand your winapp project.", + "hidden": false, + "subcommands": { + "generate": { + "description": "Generate SKILL.md files that help AI coding agents (GitHub Copilot, Claude Code, etc.) understand your winapp project. Skills are placed in the detected skills directory (.github/skills/, .agents/skills/, or .claude/skills/). Use --skills-dir to override.", + "hidden": false, + "options": { + "--directory": { + "description": "Project root directory (default: current directory)", + "hidden": false, + "valueType": "System.IO.DirectoryInfo", + "hasDefaultValue": false, + "arity": { + "minimum": 1, + "maximum": 1 + }, + "required": false, + "recursive": false + }, + "--quiet": { + "description": "Suppress progress messages", + "hidden": false, + "aliases": [ + "-q" + ], + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + }, + "--skills-dir": { + "description": "Skills directory override (default: auto-detect from .github/skills, .agents/skills, or .claude/skills)", + "hidden": false, + "valueType": "System.IO.DirectoryInfo", + "hasDefaultValue": false, + "arity": { + "minimum": 1, + "maximum": 1 + }, + "required": false, + "recursive": false + }, + "--verbose": { + "description": "Enable verbose output", + "hidden": false, + "aliases": [ + "-v" + ], + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + } + } + } + } + }, "cert": { "description": "Manage development certificates for code signing. Use 'cert generate' to create a self-signed certificate for testing, or 'cert install' (requires elevation) to trust an existing certificate on this machine.", "hidden": false, @@ -591,6 +659,19 @@ "required": false, "recursive": false }, + "--no-skills": { + "description": "Don't generate AI agent skill files (for Copilot, Claude, etc.)", + "hidden": false, + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + }, "--quiet": { "description": "Suppress progress messages", "hidden": false, diff --git a/docs/fragments/skills/winapp-cli/frameworks.md b/docs/fragments/skills/winapp-cli/frameworks.md new file mode 100644 index 00000000..8510e452 --- /dev/null +++ b/docs/fragments/skills/winapp-cli/frameworks.md @@ -0,0 +1,76 @@ +## When to use + +Use this skill when: +- **Working with a specific app framework** and need to know the right winapp workflow +- **Choosing the correct install method** (npm package vs. standalone CLI) +- **Looking for framework-specific guides** for step-by-step setup, build, and packaging + +Each framework has a detailed guide — refer to the links below rather than trying to guess commands. + +## Framework guides + +| Framework | Install method | Guide | +|-----------|---------------|-------| +| **Electron** | `npm install --save-dev @microsoft/winappcli` | [Electron setup guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/setup.md) | +| **.NET** (WPF, WinForms, Console) | `winget install Microsoft.winappcli` | [.NET guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/dotnet.md) | +| **C++** (CMake, MSBuild) | `winget install Microsoft.winappcli` | [C++ guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/cpp.md) | +| **Rust** | `winget install Microsoft.winappcli` | [Rust guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/rust.md) | +| **Flutter** | `winget install Microsoft.winappcli` | [Flutter guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/flutter.md) | +| **Tauri** | `winget install Microsoft.winappcli` | [Tauri guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/tauri.md) | + +## Key differences by framework + +### Electron (npm package) +Use the **npm package** (`@microsoft/winappcli`), **not** the standalone CLI. The npm package includes: +- The native winapp CLI binary bundled inside `node_modules` +- A Node.js SDK with helpers for creating native C#/C++ addons +- Electron-specific commands under `npx winapp node` + +Quick start: +```powershell +npm install --save-dev @microsoft/winappcli +npx winapp init --use-defaults +npx winapp node create-addon --template cs # create a C# native addon +npx winapp node add-electron-debug-identity # register identity for debugging +``` + +Additional Electron guides: +- [Packaging guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/packaging.md) +- [C++ notification addon guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/cpp-notification-addon.md) +- [WinML addon guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/winml-addon.md) +- [Phi Silica addon guide](https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/phi-silica-addon.md) + +### .NET (WPF, WinForms, Console) +.NET projects have direct access to Windows APIs. Key differences: +- Projects with NuGet references to `Microsoft.Windows.SDK.BuildTools` or `Microsoft.WindowsAppSDK` **don't need `winapp.yaml`** — winapp auto-detects SDK versions from the `.csproj` +- The key prerequisite is `appxmanifest.xml`, not `winapp.yaml` +- No native addon step needed — unlike Electron, .NET can call Windows APIs directly + +Quick start: +```powershell +winapp init --use-defaults +dotnet build +winapp create-debug-identity ./bin/Debug/net10.0-windows/myapp.exe +``` + +### C++ (CMake, MSBuild) +C++ projects use winapp primarily for SDK projections (CppWinRT headers) and packaging: +- `winapp init --setup-sdks stable` downloads Windows SDK + App SDK and generates CppWinRT headers +- Headers generated in `.winapp/generated/include` +- Response file at `.cppwinrt.rsp` for build system integration +- Add `.winapp/packages` to include/lib paths in your build system + +### Rust +- Use the `windows` crate for Windows API bindings +- winapp handles manifest, identity, packaging, and certificate management +- Typical build output: `target/release/myapp.exe` + +### Flutter +- Flutter handles the build (`flutter build windows`) +- winapp handles manifest, identity, packaging +- Build output: `build\windows\x64\runner\Release\` + +### Tauri +- Tauri has its own bundler for `.msi` installers +- Use winapp specifically for **MSIX distribution** and package identity features +- winapp adds capabilities beyond what Tauri's built-in bundler provides (identity, sparse packages, Windows API access) diff --git a/docs/fragments/skills/winapp-cli/identity.md b/docs/fragments/skills/winapp-cli/identity.md new file mode 100644 index 00000000..ac553c8a --- /dev/null +++ b/docs/fragments/skills/winapp-cli/identity.md @@ -0,0 +1,84 @@ +## When to use + +Use this skill when: +- **Debugging Windows APIs** that require package identity (push notifications, background tasks, share target, startup tasks, etc.) +- **Testing identity-dependent features** without creating and installing a full MSIX package +- **Registering a sparse package** with Windows for development + +## Prerequisites + +1. **`appxmanifest.xml`** in your project — from `winapp init` or `winapp manifest generate` +2. **Built executable** — the `.exe` your app runs from + +## What is package identity? + +Windows package identity enables your app to use restricted APIs and OS integration features: +- **Push notifications** (WNS) +- **Background tasks** +- **Share target** / share source +- **App startup tasks** +- **Taskbar pinning** +- **Windows AI APIs** (Phi Silica, OCR, etc.) +- **File type associations** registered properly in Settings + +A standard `.exe` (from `dotnet build`, `cmake`, etc.) does **not** have identity. `create-debug-identity` registers a *sparse package* with Windows, giving your exe identity without packaging it into an MSIX. + +## Usage + +### Basic usage + +```powershell +# Register sparse package for your exe (manifest auto-detected from current dir) +winapp create-debug-identity ./bin/Release/myapp.exe + +# Specify manifest location +winapp create-debug-identity ./bin/Release/myapp.exe --manifest ./appxmanifest.xml +``` + +### Keep the original package identity + +```powershell +# By default, '.debug' is appended to the package name to avoid conflicts with +# an installed MSIX version. Use --keep-identity to keep the manifest identity as-is. +winapp create-debug-identity ./myapp.exe --keep-identity +``` + +### Generate without installing + +```powershell +# Create the sparse package layout but don't register it with Windows +winapp create-debug-identity ./myapp.exe --no-install +``` + +## What the command does + +1. **Reads `appxmanifest.xml`** — extracts identity, capabilities, and assets +2. **Creates a sparse package layout** in a temp directory +3. **Appends `.debug`** to the package name (unless `--keep-identity`) to avoid conflicts +4. **Registers with Windows** via `Add-AppxPackage -ExternalLocation` — makes your exe "identity-aware" + +After running, launch your exe normally — Windows will recognize it as having package identity. + +## Recommended workflow + +1. **Setup** — `winapp init --use-defaults` (creates `appxmanifest.xml`) +3. **Build** your app +4. **Register identity** — `winapp create-debug-identity ./bin/myapp.exe` +5. **Run** your app — identity-requiring APIs now work +6. **Re-run step 4** whenever you change `appxmanifest.xml` or `Assets/` + +## Tips + +- You must re-run `create-debug-identity` after any changes to `appxmanifest.xml` or image assets +- The debug identity persists across reboots until explicitly removed +- To remove: `Get-AppxPackage *yourapp.debug* | Remove-AppxPackage` +- If you have both a debug identity and an installed MSIX, they may conflict — use `--keep-identity` carefully +- For Electron apps, use `npx winapp node add-electron-debug-identity` instead (handles Electron-specific paths) + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "appxmanifest.xml not found" | No manifest in current directory | Run `winapp init` or `winapp manifest generate`, or pass `--manifest` | +| "Failed to add package identity" | Previous registration stale or cert untrusted | `Get-AppxPackage *yourapp* \| Remove-AppxPackage`, then `winapp cert install ./devcert.pfx` (admin) | +| "Access denied" | Cert not trusted or permission issue | Run `winapp cert install ./devcert.pfx` as admin | +| APIs still fail after registration | App launched before registration completed | Close app, re-run `create-debug-identity`, then relaunch | diff --git a/docs/fragments/skills/winapp-cli/manifest.md b/docs/fragments/skills/winapp-cli/manifest.md new file mode 100644 index 00000000..c86046a9 --- /dev/null +++ b/docs/fragments/skills/winapp-cli/manifest.md @@ -0,0 +1,118 @@ +## When to use + +Use this skill when: +- **Creating `appxmanifest.xml`** for a project that doesn't have one yet +- **Generating app icon assets** from a single source image +- **Understanding manifest structure** for package identity and capabilities + +## Prerequisites + +- winapp CLI installed +- Optional: a source image (PNG, at least 400x400 pixels) for custom app icons + +## Key concepts + +**`appxmanifest.xml`** is the key prerequisite for most winapp commands — it's more important than `winapp.yaml`. It declares: +- **Package identity** — name, publisher, version +- **App entry point** — which executable to launch +- **Capabilities** — what the app can access (internet, file system, etc.) +- **Visual assets** — icons for Start menu, taskbar, installers +- **Extensions** — share target, startup tasks, file associations, etc. + +**Two manifest templates:** +- **`packaged`** (default) — for full MSIX distribution +- **`sparse`** — for desktop apps that need package identity without full MSIX containment (uses `AllowExternalContent`) + +**`winapp init` also generates a manifest** as part of full project setup. Use `winapp manifest generate` when you only need the manifest without SDK setup or `winapp.yaml`. + +## Usage + +### Generate a new manifest + +```powershell +# Defaults — uses current folder name, current user as publisher +winapp manifest generate + +# Into a specific directory +winapp manifest generate ./my-project + +# Customize identity +winapp manifest generate --package-name "MyApp" --publisher-name "CN=Contoso" --version "2.0.0.0" + +# Set entry point and description +winapp manifest generate --executable myapp.exe --description "My awesome app" + +# Generate a sparse manifest (for desktop apps needing identity without full MSIX) +winapp manifest generate --template sparse + +# Overwrite existing manifest +winapp manifest generate --if-exists overwrite +``` + +Output: +- `appxmanifest.xml` — the manifest file +- `Assets/` — default app icons in required sizes (Square44x44Logo, Square150x150Logo, Wide310x150Logo, etc.) + +### Update app icons from a source image + +```powershell +# Generate all required icon sizes from one source image +winapp manifest update-assets ./my-logo.png + +# Specify manifest location (if not in current directory) +winapp manifest update-assets ./my-logo.png --manifest ./path/to/appxmanifest.xml +``` + +The source image should be at least 400x400 pixels (PNG recommended). The command reads the manifest to determine which asset sizes are needed and generates them all. + +## Manifest structure overview + +A typical `appxmanifest.xml` looks like: + +```xml + + + + + My App + My Publisher + Assets\StoreLogo.png + + + + + + + + + + + + + +``` + +Key fields to edit: +- `Identity.Name` — unique package name (no spaces) +- `Identity.Publisher` — must match your certificate exactly +- `Application.Executable` — your app's exe filename +- `Capabilities` — add capabilities as needed (`internetClient`, `broadFileSystemAccess`, etc.) + +## Tips + +- Always ensure `Identity.Publisher` matches your signing certificate — use `winapp cert generate --manifest` to auto-match +- The `sparse` template adds `uap10:AllowExternalContent="true"` for apps that need identity but run outside the MSIX container +- You can manually edit `appxmanifest.xml` after generation — it's a standard XML file +- Image assets must match the paths referenced in the manifest — `update-assets` handles this automatically +- For logos, transparent PNGs work best. Use a square image for best results across all sizes. + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "Manifest already exists" | `appxmanifest.xml` present | Use `--if-exists overwrite` to replace, or edit existing file directly | +| "Invalid source image" | Image too small or wrong format | Use PNG, at least 400x400 pixels | +| "Publisher mismatch" during packaging | Manifest publisher ≠ cert publisher | Edit `Identity.Publisher` in manifest, or regenerate cert with `--manifest` | diff --git a/docs/fragments/skills/winapp-cli/package.md b/docs/fragments/skills/winapp-cli/package.md new file mode 100644 index 00000000..30a82974 --- /dev/null +++ b/docs/fragments/skills/winapp-cli/package.md @@ -0,0 +1,124 @@ +## When to use + +Use this skill when: +- **Creating an MSIX installer** from a built app for distribution or testing +- **Signing a package** with a development or production certificate +- **Bundling the Windows App SDK runtime** for self-contained deployment + +## Prerequisites + +Before packaging, you need: +1. **Built app output** in a folder (e.g., `bin/Release/`, `dist/`, `build/`) +2. **`appxmanifest.xml`** — from `winapp init` or `winapp manifest generate` +3. **Certificate** (optional) — `devcert.pfx` from `winapp cert generate` for signing + +## Usage + +### Basic packaging (unsigned) + +```powershell +# Package from build output — manifest auto-detected from current dir or input folder +winapp package ./bin/Release + +# Specify manifest location explicitly +winapp package ./dist --manifest ./appxmanifest.xml +``` + +### Package and sign in one step + +```powershell +# Sign with existing certificate +winapp package ./bin/Release --cert ./devcert.pfx + +# Custom certificate password +winapp package ./bin/Release --cert ./devcert.pfx --cert-password MyP@ssw0rd +``` + +### Generate certificate + package in one step + +```powershell +# Auto-generate cert, sign, and package +winapp package ./bin/Release --generate-cert + +# Also install the cert to trust it on this machine (requires admin) +winapp package ./bin/Release --generate-cert --install-cert +``` + +### Self-contained deployment + +```powershell +# Bundle Windows App SDK runtime so users don't need it installed (must have winappsdk reference in the winapp.yaml or *.csproj) +winapp package ./bin/Release --cert ./devcert.pfx --self-contained +``` + +### Custom output path and name + +```powershell +# Specify output file +winapp package ./dist --output ./releases/myapp-v1.0.msix --cert ./devcert.pfx + +# Custom package name +winapp package ./dist --name "MyApp_1.0.0_x64" --cert ./devcert.pfx +``` + +## What the command does + +1. **Locates `appxmanifest.xml`** — looks in input folder, then current directory (or uses `--manifest`) +2. **Copies manifest + assets** into a staging layout alongside your app files +3. **Generates `resources.pri`** — Package Resource Index for UWP-style resource lookup (skip with `--skip-pri`) +4. **Runs `makeappx pack`** — creates the `.msix` package file +5. **Signs the package** (if `--cert` provided) — calls `signtool` with your certificate + +Output: a `.msix` file that can be installed on Windows via double-click or `Add-AppxPackage`. + +## Installing the MSIX for testing + +```powershell +# Trust the dev certificate first (one-time, requires admin) +winapp cert install ./devcert.pfx + +# Install the MSIX +Add-AppxPackage ./myapp.msix + +# Uninstall if needed +Get-AppxPackage *myapp* | Remove-AppxPackage +``` + +## Recommended workflow + +1. **Build** your app (`dotnet build`, `cmake --build`, `npm run make`, etc.) +2. **Package** — `winapp package --cert ./devcert.pfx` +3. **Trust cert** (first time) — `winapp cert install ./devcert.pfx` (admin) +4. **Install** — double-click the `.msix` or `Add-AppxPackage ./myapp.msix` +5. **Test** the installed app from the Start menu + +### Advanced: External content catalog + +For sparse packages with `AllowExternalContent`, you may need a code integrity catalog: + +```powershell +# Generate CodeIntegrityExternal.cat for external executables +winapp create-external-catalog "./bin/Release" + +# Include subdirectories and specify output path +winapp create-external-catalog "./bin/Release" --recursive --output ./catalog/CodeIntegrityExternal.cat +``` + +This hashes executables in the specified directories so Windows trusts them when running with sparse package identity. + +## Tips + +- The `package` command aliases to `pack` — both work identically +- `appxmanifest.xml` Publisher must match the certificate publisher — use `winapp cert generate --manifest` to ensure they match +- Use `--skip-pri` if your app doesn't use Windows resource loading (e.g., most Electron/Rust/C++ apps without UWP resources) +- For framework-specific packaging paths (Electron, .NET, Rust, etc.), see the `winapp-frameworks` skill +- The `--executable` flag overrides the entry point in the manifest — useful when your exe name differs from what's in `appxmanifest.xml` +- For production distribution, use a certificate from a trusted CA and add `--timestamp` when signing with `winapp sign` + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "appxmanifest.xml not found" | No manifest in input folder or current dir | Run `winapp init` or `winapp manifest generate` first | +| "Publisher mismatch" | Cert publisher ≠ manifest publisher | Regenerate cert with `winapp cert generate --manifest`, or edit manifest | +| "Package installation failed" | Cert not trusted or stale package | Run `winapp cert install ./devcert.pfx` (admin), then `Get-AppxPackage \| Remove-AppxPackage` | +| "makeappx not found" | Build tools not downloaded | Run `winapp update` or `winapp tool makeappx --help` to trigger download | diff --git a/docs/fragments/skills/winapp-cli/setup.md b/docs/fragments/skills/winapp-cli/setup.md new file mode 100644 index 00000000..7c4f709c --- /dev/null +++ b/docs/fragments/skills/winapp-cli/setup.md @@ -0,0 +1,113 @@ +## When to use + +Use this skill when: +- **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. + +## Key concepts + +**`appxmanifest.xml`** is the most important file winapp creates — it declares your app's identity, capabilities, and visual assets. Most winapp commands require it (`package`, `create-debug-identity`, `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 + +### Initialize a new winapp project + +```powershell +# Interactive — prompts for app name, publisher, SDK channel, etc. +winapp init . + +# Non-interactive — accepts all defaults (stable SDKs, current folder name as app name) +winapp init --use-defaults + +# 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: +- `appxmanifest.xml` — 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` + +### 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. + +### 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. + +## Recommended workflow + +1. **Initialize** — `winapp init --use-defaults` in your existing project +2. **Configure** — edit `appxmanifest.xml` to add capabilities your app needs (e.g., `runFullTrust`, `internetClient`) +3. **Build** — build your app as usual (dotnet build, cmake, npm run build, etc.) +4. **Debug with identity** — `winapp create-debug-identity ./bin/myapp.exe` to test Windows APIs +5. **Package** — `winapp package ./bin/Release --cert ./devcert.pfx` to create MSIX + +### Generate AI agent skills + +```powershell +# Generate SKILL.md files for AI coding assistants (Copilot, Claude, Cursor, etc.) +winapp agents generate + +# Specify a custom skills directory +winapp agents generate --skills-dir ./my-skills +``` + +This is also run automatically during `winapp init` (unless `--no-skills` is passed). + +## Tips + +- Use `--use-defaults` (alias: `--no-prompt`) in CI/CD pipelines and scripts to avoid interactive prompts +- If you only need `appxmanifest.xml` without SDK setup, use `winapp manifest generate` instead of `init` +- The `--no-skills` flag skips AI agent skill file generation if you don't use Copilot/Claude/Cursor +- `winapp init` is idempotent for the config file — re-running it won't overwrite an existing `winapp.yaml` unless you use `--config-only` +- For Electron projects, prefer `npm install --save-dev @microsoft/winappcli` and use `npx winapp init` instead of the standalone CLI + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "winapp.yaml not found" | Running `restore`/`update` without config | Run `winapp init` first, or ensure you're in the right directory | +| "Directory not found" | Target directory doesn't exist | Create the directory first or check the path | +| SDK download fails | Network issue or firewall | Ensure internet access; check proxy settings | +| `init` prompts unexpectedly in CI | Missing `--use-defaults` flag | Add `--use-defaults` to skip all prompts | diff --git a/docs/fragments/skills/winapp-cli/signing.md b/docs/fragments/skills/winapp-cli/signing.md new file mode 100644 index 00000000..166cebd1 --- /dev/null +++ b/docs/fragments/skills/winapp-cli/signing.md @@ -0,0 +1,96 @@ +## When to use + +Use this skill when: +- **Generating a development certificate** for local MSIX signing and testing +- **Installing (trusting) a certificate** on a machine so MSIX packages can be installed +- **Signing an MSIX package or executable** for distribution + +## Prerequisites + +- winapp CLI installed +- **Administrator access** required for `cert install` (trusting certificates on the machine) + +## Key concepts + +**Publisher matching:** The publisher in your certificate (e.g., `CN=MyCompany`) must exactly match the `Publisher` attribute in `appxmanifest.xml`. Use `--manifest` when generating to auto-match. + +**Dev vs. production certs:** `winapp cert generate` creates self-signed certificates for **local testing only**. For production distribution (Microsoft Store or enterprise), obtain a certificate from a trusted Certificate Authority. + +**Default password:** Generated certificates use `password` as the default PFX password. Override with `--password`. + +## Usage + +### Generate a development certificate + +```powershell +# Auto-infer publisher from appxmanifest.xml in the current directory +winapp cert generate + +# Explicitly point to a manifest +winapp cert generate --manifest ./path/to/appxmanifest.xml + +# Set publisher manually (when no manifest exists yet) +winapp cert generate --publisher "CN=Contoso, O=Contoso Ltd, C=US" + +# Custom output path and password +winapp cert generate --output ./certs/myapp.pfx --password MySecurePassword + +# Custom validity period +winapp cert generate --valid-days 730 + +# Overwrite existing certificate +winapp cert generate --if-exists overwrite +``` + +Output: `devcert.pfx` (or custom path via `--output`). + +### Install (trust) a certificate + +```powershell +# Trust the certificate on this machine (requires admin/elevated terminal) +winapp cert install ./devcert.pfx + +# Force reinstall even if already trusted +winapp cert install ./devcert.pfx --force +``` + +This adds the certificate to the local machine's **Trusted Root Certification Authorities** store. Required before double-clicking MSIX packages or running `Add-AppxPackage`. + +### Sign a file + +```powershell +# Sign an MSIX package +winapp sign ./myapp.msix ./devcert.pfx + +# Sign with custom password +winapp sign ./myapp.msix ./devcert.pfx --password MySecurePassword + +# Sign with timestamp for production (signature remains valid after cert expires) +winapp sign ./myapp.msix ./production.pfx --timestamp http://timestamp.digicert.com +``` + +Note: The `package` command can sign automatically when you pass `--cert`, so you often don't need `sign` separately. + +## Recommended workflow + +1. **Generate cert** — `winapp cert generate` (auto-infers publisher from manifest) +2. **Trust cert** (one-time) — `winapp cert install ./devcert.pfx` (run as admin) +3. **Package + sign** — `winapp package ./dist --cert ./devcert.pfx` +4. **Distribute** — share the `.msix`; recipients must also trust the cert, or use a trusted CA cert + +## Tips + +- Always use `--manifest` (or have `appxmanifest.xml` in the working directory) when generating certs to ensure the publisher matches automatically +- For CI/CD, store the PFX as a secret and pass the password via `--password` rather than using the default +- `winapp cert install` modifies the machine certificate store — it persists across reboots and user sessions +- Use `--timestamp` when signing production builds so the signature survives certificate expiration +- You can also use the shorthand: `winapp package ./dist --generate-cert --install-cert` to do everything in one command + +## Troubleshooting +| Error | Cause | Solution | +|-------|-------|----------| +| "Publisher mismatch" | Cert publisher ≠ manifest publisher | `winapp cert generate --manifest ./appxmanifest.xml` to re-generate with correct publisher | +| "Access denied" / "elevation required" | `cert install` needs admin | Run your terminal as Administrator | +| "Certificate not trusted" | Cert not installed on machine | `winapp cert install ./devcert.pfx` (admin) | +| "Certificate file already exists" | `devcert.pfx` already present | Use `--if-exists overwrite` or `--if-exists skip` | +| Signature invalid after time passes | No timestamp used during signing | Re-sign with `--timestamp http://timestamp.digicert.com` | diff --git a/docs/fragments/skills/winapp-cli/troubleshoot.md b/docs/fragments/skills/winapp-cli/troubleshoot.md new file mode 100644 index 00000000..eb5560b1 --- /dev/null +++ b/docs/fragments/skills/winapp-cli/troubleshoot.md @@ -0,0 +1,95 @@ +## When to use + +Use this skill when: +- **Diagnosing errors** from winapp CLI commands +- **Choosing the right command** for a task +- **Understanding prerequisites** — what each command needs and what it produces + +## Common errors & solutions + +| Error | Cause | Solution | +|-------|-------|----------| +| "winapp.yaml not found" | Running `restore` or `update` without config | Run `winapp init` first, or `cd` to the directory containing `winapp.yaml` | +| "appxmanifest.xml not found" | Running `package`, `create-debug-identity`, or `cert generate --manifest` | Run `winapp init` or `winapp manifest generate` first, or pass `--manifest ` | +| "Publisher mismatch" | Certificate publisher ≠ manifest publisher | Regenerate cert: `winapp cert generate --manifest`, or edit `appxmanifest.xml` `Identity.Publisher` to match | +| "Access denied" / "elevation required" | `cert install` without admin | Run terminal as Administrator for `winapp cert install` | +| "Package installation failed" | Cert not trusted, or stale package registration | `winapp cert install ./devcert.pfx` (admin), then `Get-AppxPackage \| Remove-AppxPackage` | +| "Certificate not trusted" | Dev cert not installed on machine | `winapp cert install ./devcert.pfx` (admin) | +| "Build tools not found" | First run, tools not yet downloaded | Run `winapp update` to download tools; ensure internet access | +| "Failed to add package identity" | Stale debug identity or untrusted cert | `Get-AppxPackage *yourapp* \| Remove-AppxPackage` to clean up, then `winapp cert install` and retry | +| "Certificate file already exists" | `devcert.pfx` already present | Use `winapp cert generate --if-exists overwrite` or `--if-exists skip` | +| "Manifest already exists" | `appxmanifest.xml` already present | Use `winapp manifest generate --if-exists overwrite` or edit manifest directly | + +## Command selection guide + +``` +Does the project have an appxmanifest.xml? +├─ No → Do you want full setup (manifest + config + optional SDKs)? +│ ├─ Yes → winapp init (adds Windows platform files to existing project) +│ └─ No, just a manifest → winapp manifest generate +└─ Yes + ├─ Has winapp.yaml, cloned/pulled but .winapp/ folder missing? + │ └─ winapp restore + ├─ Want newer SDK versions? + │ └─ winapp update + ├─ Need a dev certificate? + │ └─ winapp cert generate (then winapp cert install for trust) + ├─ Need package identity for debugging? + │ └─ winapp create-debug-identity + ├─ Ready to create MSIX installer? + │ └─ winapp package --cert ./devcert.pfx + ├─ Need to sign an existing file? + │ └─ winapp sign + ├─ Need to update app icons? + │ └─ winapp manifest update-assets ./logo.png + ├─ Need to run SDK tools directly? + │ └─ winapp tool + ├─ Want AI agent skills in your repo? + │ └─ winapp agents generate + ├─ Need to publish to Microsoft Store? + │ └─ winapp store (passthrough to Store Developer CLI) + └─ Need the .winapp directory path for build scripts? + └─ winapp get-winapp-path (or --global for shared cache) +``` + +**Important notes:** +- `winapp init` adds files to an **existing** project — it does not create a new project +- The key prerequisite for most commands is `appxmanifest.xml`, not `winapp.yaml` +- `winapp.yaml` is only needed for SDK version management (`restore`/`update`) +- Projects with NuGet package references (e.g., `.csproj` referencing `Microsoft.Windows.SDK.BuildTools`) can use winapp commands without `winapp.yaml` +- For Electron projects, use the npm package (`npm install --save-dev @microsoft/winappcli`) which includes Node.js-specific commands under `npx winapp node` + +## Prerequisites & state matrix + +| Command | Requires | Creates/Modifies | +|---------|----------|------------------| +| `init` | Existing project (any framework) | `winapp.yaml`, `.winapp/`, `appxmanifest.xml`, `Assets/`, `.gitignore` update | +| `restore` | `winapp.yaml` | `.winapp/packages/`, generated projections | +| `update` | `winapp.yaml` | Updates versions in `winapp.yaml`, reinstalls packages | +| `manifest generate` | Nothing | `appxmanifest.xml`, `Assets/` | +| `manifest update-assets` | `appxmanifest.xml` + source image | Regenerates `Assets/` icons | +| `cert generate` | Nothing (or `appxmanifest.xml` for publisher) | `devcert.pfx` | +| `cert install` | Certificate file + admin | Machine certificate store | +| `create-debug-identity` | `appxmanifest.xml` + exe + trusted cert | Registers sparse package with Windows | +| `package` | Build output + `appxmanifest.xml` | `.msix` file | +| `sign` | File + certificate | Signed file (in-place) | +| `create-external-catalog` | Directory with executables | `CodeIntegrityExternal.cat` | +| `agents generate` | Nothing | `.github/skills/winapp-cli/` (or detected skills dir) | +| `tool ` | Nothing (auto-downloads tools) | Runs SDK tool directly | +| `store` | Nothing (auto-downloads Store CLI) | Passthrough to Microsoft Store Developer CLI | +| `get-winapp-path` | Nothing | Prints `.winapp` directory path | + +## Debugging tips + +- Add `--verbose` (or `-v`) to any command for detailed output +- Add `--quiet` (or `-q`) to suppress progress messages (useful in CI/CD) +- Run `winapp --cli-schema` to get the full JSON schema of all commands and options +- Run any command with `--help` for its specific usage information +- Use `winapp get-winapp-path` to find where packages are stored locally +- Use `winapp get-winapp-path --global` to find the shared cache location + +## Getting more help + +- Full CLI documentation: https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md +- Framework-specific guides: https://github.com/microsoft/WinAppCli/tree/main/docs/guides +- File an issue: https://github.com/microsoft/WinAppCli/issues diff --git a/docs/llm-context.md b/docs/llm-context.md deleted file mode 100644 index f6ba4226..00000000 --- a/docs/llm-context.md +++ /dev/null @@ -1,310 +0,0 @@ ---- -name: winapp-cli -description: CLI for Windows app development, including package identity, packaging, managing appxmanifest.xml, test certificates, Windows (App) SDK projections, and more. For use with any app framework targeting Windows -version: 0.2.0 -schema_version: 1.0 ---- - -# winapp CLI Context for LLMs - -> Auto-generated from CLI v0.2.0 (schema version 1.0) -> -> This file provides structured context about the winapp CLI for AI assistants and LLMs. -> For the raw JSON schema, see [cli-schema.json](cli-schema.json). - -## Overview - -CLI for Windows app development, including package identity, packaging, managing appxmanifest.xml, test certificates, Windows (App) SDK projections, and more. For use with any app framework targeting Windows - -**Installation:** -- WinGet: `winget install Microsoft.WinAppCli --source winget` -- npm: `npm install -g @microsoft/winappcli` (for electron projects) - -## Command Reference - -### `winapp cert` - -Manage development certificates for code signing. Use 'cert generate' to create a self-signed certificate for testing, or 'cert install' (requires elevation) to trust an existing certificate on this machine. - -#### `winapp cert generate` - -Create a self-signed certificate for local testing only. Publisher must match AppxManifest.xml (auto-inferred if --manifest provided or appxmanifest.xml is in working directory). Output: devcert.pfx (default password: 'password'). For production, obtain a certificate from a trusted CA. Use 'cert install' to trust on this machine. - -**Options:** -- `--if-exists` - Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) (default: `Error`) -- `--install` - Install the certificate to the local machine store after generation -- `--manifest` - Path to appxmanifest.xml file to extract publisher information from -- `--output` - Output path for the generated PFX file -- `--password` - Password for the generated PFX file (default: `password`) -- `--publisher` - Publisher name for the generated certificate. If not specified, will be inferred from manifest. -- `--quiet` / `-q` - Suppress progress messages -- `--valid-days` - Number of days the certificate is valid (default: `365`) -- `--verbose` / `-v` - Enable verbose output - -#### `winapp cert install` - -Trust a certificate on this machine (requires admin). Run before installing MSIX packages signed with dev certificates. Example: winapp cert install ./devcert.pfx. Only needed once per certificate. - -**Arguments:** -- `` *(required)* - Path to the certificate file (PFX or CER) - -**Options:** -- `--force` - Force installation even if the certificate already exists -- `--password` - Password for the PFX file (default: `password`) -- `--quiet` / `-q` - Suppress progress messages -- `--verbose` / `-v` - Enable verbose output -### `winapp create-debug-identity` - -Enable package identity for debugging without creating full MSIX. Required for testing Windows APIs (push notifications, share target, etc.) during development. Example: winapp create-debug-identity ./myapp.exe. Requires appxmanifest.xml in current directory or passed via --manifest. Re-run after changing appxmanifest.xml or Assets/. - -**Arguments:** -- `` - Path to the .exe that will need to run with identity, or entrypoint script. - -**Options:** -- `--keep-identity` - Keep the package identity from the manifest as-is, without appending '.debug' to the package name and application ID. -- `--manifest` - Path to the appxmanifest.xml -- `--no-install` - Do not install the package after creation. -- `--quiet` / `-q` - Suppress progress messages -- `--verbose` / `-v` - Enable verbose output -### `winapp create-external-catalog` - -Generates a CodeIntegrityExternal.cat catalog file with hashes of executable files from specified directories. Used with the TrustedLaunch flag in MSIX sparse package manifests (AllowExternalContent) to allow execution of external files not included in the package. - -**Arguments:** -- `` *(required)* - List of input folders with executable files to process (separated by semicolons) - -**Options:** -- `--compute-flat-hashes` - Include flat hashes when generating the catalog -- `--if-exists` - Behavior when output file already exists (default: `Error`) -- `--output` / `-o` - Output catalog file path. If not specified, the default CodeIntegrityExternal.cat name is used. -- `--quiet` / `-q` - Suppress progress messages -- `--recursive` / `-r` - Include files from subdirectories -- `--use-page-hashes` - Include page hashes when generating the catalog -- `--verbose` / `-v` - Enable verbose output -### `winapp get-winapp-path` - -Print the path to the .winapp directory. Use --global for the shared cache location, or omit for the project-local .winapp folder. Useful for build scripts that need to reference installed packages. - -**Options:** -- `--global` - Get the global .winapp directory instead of local -- `--quiet` / `-q` - Suppress progress messages -- `--verbose` / `-v` - Enable verbose output -### `winapp init` - -Start here for initializing a Windows app with required setup. Sets up everything needed for Windows app development: creates appxmanifest.xml with default assets, creates winapp.yaml for version management, and downloads Windows SDK and Windows App SDK packages and generates projections. Interactive by default (use --use-defaults to skip prompts). Use 'restore' instead if you cloned a repo that already has winapp.yaml. Use 'manifest generate' if you only need a manifest, or 'cert generate' if you need a development certificate for code signing. - -**Arguments:** -- `` - Base/root directory for the winapp workspace, for consumption or installation. - -**Options:** -- `--config-dir` - Directory to read/store configuration (default: current directory) -- `--config-only` - Only handle configuration file operations (create if missing, validate if exists). Skip package installation and other workspace setup steps. -- `--ignore-config` / `--no-config` - Don't use configuration file for version management -- `--no-gitignore` - Don't update .gitignore file -- `--quiet` / `-q` - Suppress progress messages -- `--setup-sdks` - SDK installation mode: 'stable' (default), 'preview', 'experimental', or 'none' (skip SDK installation) -- `--use-defaults` / `--no-prompt` - Do not prompt, and use default of all prompts -- `--verbose` / `-v` - Enable verbose output -### `winapp manifest` - -Create and modify appxmanifest.xml files for package identity and MSIX packaging. Use 'manifest generate' to create a new manifest, or 'manifest update-assets' to regenerate app icons from a source image. - -#### `winapp manifest generate` - -Create appxmanifest.xml without full project setup. Use when you only need a manifest and image assets (no SDKs, no certificate). For full setup, use 'init' instead. Templates: 'packaged' (full MSIX), 'sparse' (desktop app needing Windows APIs). - -**Arguments:** -- `` - Directory to generate manifest in - -**Options:** -- `--description` - Human-readable app description shown during installation and in Windows Settings (default: `My Application`) -- `--executable` / `--entrypoint` - Path to the application's executable. Default: .exe -- `--if-exists` - Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) (default: `Error`) -- `--logo-path` - Path to logo image file -- `--package-name` - Package name (default: folder name) -- `--publisher-name` - Publisher CN (default: CN=) -- `--quiet` / `-q` - Suppress progress messages -- `--template` - Manifest template type: 'packaged' (full MSIX app, default) or 'sparse' (desktop app with package identity for Windows APIs) (default: `Packaged`) -- `--verbose` / `-v` - Enable verbose output -- `--version` - App version in Major.Minor.Build.Revision format (e.g., 1.0.0.0). (default: `1.0.0.0`) - -#### `winapp manifest update-assets` - -Generate new assets for images referenced in an appxmanifest.xml from a single source image. Source image should be at least 400x400 pixels. - -**Arguments:** -- `` *(required)* - Path to source image file - -**Options:** -- `--manifest` - Path to AppxManifest.xml file (default: search current directory) -- `--quiet` / `-q` - Suppress progress messages -- `--verbose` / `-v` - Enable verbose output -### `winapp package` - -Create MSIX installer from your built app. Run after building your app. appxmanifest.xml is required for packaging - it must be in current working directory, passed as --manifest or be in the input folder. Use --cert devcert.pfx to sign for testing. Example: winapp package ./dist --manifest appxmanifest.xml --cert ./devcert.pfx - -**Aliases:** `pack` - -**Arguments:** -- `` *(required)* - Input folder with package layout - -**Options:** -- `--cert` - Path to signing certificate (will auto-sign if provided) -- `--cert-password` - Certificate password (default: password) (default: `password`) -- `--executable` / `--exe` - Path to the executable relative to the input folder. -- `--generate-cert` - Generate a new development certificate -- `--install-cert` - Install certificate to machine -- `--manifest` - Path to AppX manifest file (default: auto-detect from input folder or current directory) -- `--name` - Package name (default: from manifest) -- `--output` - Output msix file name for the generated package (defaults to .msix) -- `--publisher` - Publisher name for certificate generation -- `--quiet` / `-q` - Suppress progress messages -- `--self-contained` - Bundle Windows App SDK runtime for self-contained deployment -- `--skip-pri` - Skip PRI file generation -- `--verbose` / `-v` - Enable verbose output -### `winapp restore` - -Use after cloning a repo or when .winapp/ folder is missing. Reinstalls SDK packages from existing winapp.yaml without changing versions. Requires winapp.yaml (created by 'init'). To check for newer SDK versions, use 'update' instead. - -**Arguments:** -- `` - Base/root directory for the winapp workspace - -**Options:** -- `--config-dir` - Directory to read configuration from (default: current directory) -- `--quiet` / `-q` - Suppress progress messages -- `--verbose` / `-v` - Enable verbose output -### `winapp sign` - -Code-sign an MSIX package or executable. Example: winapp sign ./app.msix ./devcert.pfx. Use --timestamp for production builds to remain valid after cert expires. The 'package' command can sign automatically with --cert. - -**Arguments:** -- `` *(required)* - Path to the file/package to sign -- `` *(required)* - Path to the certificate file (PFX format) - -**Options:** -- `--password` - Certificate password (default: `password`) -- `--quiet` / `-q` - Suppress progress messages -- `--timestamp` - Timestamp server URL -- `--verbose` / `-v` - Enable verbose output -### `winapp store` - -Run a Microsoft Store Developer CLI command. This command will download the Microsoft Store Developer CLI if not already downloaded. Learn more about the Microsoft Store Developer CLI here: https://aka.ms/msstoredevcli -### `winapp tool` - -Run Windows SDK tools directly (makeappx, signtool, makepri, etc.). Auto-downloads Build Tools if needed. For most tasks, prefer higher-level commands like 'package' or 'sign'. Example: winapp tool makeappx pack /d ./folder /p ./out.msix - -**Aliases:** `run-buildtool` - -**Options:** -- `--quiet` / `-q` - Suppress progress messages -- `--verbose` / `-v` - Enable verbose output -### `winapp update` - -Check for and install newer SDK versions. Updates winapp.yaml with latest versions and reinstalls packages. Requires existing winapp.yaml (created by 'init'). Use --setup-sdks preview for preview SDKs. To reinstall current versions without updating, use 'restore' instead. - -**Options:** -- `--quiet` / `-q` - Suppress progress messages -- `--setup-sdks` - SDK installation mode: 'stable' (default), 'preview', 'experimental', or 'none' (skip SDK installation) -- `--verbose` / `-v` - Enable verbose output - -## Common Workflows - -### New Project Setup -1. `winapp init .` - Initialize workspace with appxmanifest.xml, image assets, and optionally SDK projections in the .winapp folder. (run with `--use-defaults` to make it non-interactive) -2. Edit `appxmanifest.xml` if you need to modify properties, set capabilities, or other configurations -3. Build your app -4. `winapp create-debug-identity ` - to generate package identity from generated appxmanifest.xml before running the app so the exe has package identity -5. Run the app -6. `winapp pack --cert .\devcert.pfx` - Create signed MSIX (--cert is optional) - -### Existing Project (Clone/CI) -1. `winapp restore` - Reinstall packages and generate C++ projections from `winapp.yaml` -2. Build and package as normal - -### Update SDK Versions -1. `winapp update` - Check for and install newer SDK versions -2. Rebuild your app - -### Install SDKs After Initial Setup -If you ran `init` with `--setup-sdks none` (or skipped SDK installation) and later need the SDKs: -1. `winapp init --use-defaults --setup-sdks stable` - Re-run init to install SDKs - - `--use-defaults` skips prompts and preserves existing files (manifest, etc.) - - Use `--setup-sdks preview` or `--setup-sdks experimental` for preview/experimental SDK versions -2. Rebuild your app with the new SDK projections in `.winapp/` - -### Debug with Package Identity -For apps that need Windows APIs requiring identity (push notifications, etc.): -1. Ensure an appxmanifest.xml is present, either via `winapp init` or `winapp manifest generate` -2. `winapp create-debug-identity ./myapp.exe` - generate package identity from generated appxmanifest.xml before running the app so the exe has package identity -3. Run your app - it now has package identity - -### Electron Apps -1. `winapp init` - Set up workspace (run with --use-defaults to make it non-interactive) -2. `winapp node create-addon --template cs` - Generate native C# addon for Windows APIs (`--template cpp` for C++ addon) -3. `winapp node add-electron-debug-identity` - Enable identity for debugging -4. `npm start` to launch app normally, but now with identity -5. For production, create production files with the preferred packager and run `winapp pack --cert .\devcert.pfx` - -## Command Selection Guide - -Use this decision tree to pick the right command: - -``` -Using winapp CLI in a new project? -├─ Yes → run winapp init -│ (creates manifest + cert + SDKs + config) -└─ No - ├─ Cloned/pulled a repo with winapp.yaml? - │ └─ Yes → winapp restore - │ (reinstalls SDKs from config) - ├─ Want newer SDK versions? - │ └─ Yes → winapp update - │ (checks NuGet, updates config) - ├─ Only need a manifest (no SDKs/cert)? - │ └─ Yes → winapp manifest generate - ├─ Only need a dev certificate? - │ └─ Yes → winapp cert generate - ├─ Ready to create MSIX installer? - │ └─ Yes → winapp package - │ (add --cert for signing) - ├─ Need to add package identity for debugging Windows APIs that need it? - │ └─ Yes → winapp create-debug-identity - │ (enables push notifications, etc.) - └─ Need to run SDK tools directly? - └─ Yes → winapp tool - (makeappx, signtool, makepri) -``` - -## Prerequisites & State - -| Command | Requires | Creates/Modifies | -|---------|----------|------------------| -| `init` | Nothing | `winapp.yaml`, `.winapp/`, `appxmanifest.xml`, `Assets/` | -| `restore` | `winapp.yaml` | `.winapp/packages/` | -| `update` | `winapp.yaml` | Updates versions in `winapp.yaml` | -| `manifest generate` | Nothing | `appxmanifest.xml`, `Assets/` | -| `cert generate` | Nothing (or `appxmanifest.xml` for publisher inference) | `*.pfx` file | -| `package` | App build output + `appxmanifest.xml` (+ `devcert.pfx` for optional signing) | `*.msix` file | -| `create-debug-identity` | `appxmanifest.xml` + exe | Registers sparse package with Windows | - -## Common Errors & Solutions - -| Error | Cause | Solution | -|-------|-------|----------| -| "winapp.yaml not found" | Running `restore` or `update` without config | Run `winapp init` first, or ensure you're in the right directory | -| "appxmanifest.xml not found" | Running `package` or `create-debug-identity` without manifest | Run `winapp init` or `winapp manifest generate` first | -| "Publisher mismatch" | Certificate publisher doesn't match manifest | Regenerate cert with `--manifest` flag, or edit manifest Publisher to match | -| "Access denied" / "elevation required" | `cert install` without admin | Run terminal as Administrator | -| "Package installation failed" | Signing issue or existing package conflict | Run `Get-AppxPackage | Remove-AppxPackage` first, ensure cert is trusted | -| "Certificate not trusted" | Dev cert not installed on machine | Run `winapp cert install ./devcert.pfx` as admin | -| "Build tools not found" | First run, tools not downloaded yet | winapp auto-downloads; ensure internet access. Or run `winapp tool --help` to trigger download | - -## Machine-Readable Schema - -For programmatic access to the complete CLI structure including all options, types, and defaults: - -```bash -winapp --cli-schema -``` - -This outputs JSON that can be parsed by tools and LLMs. See [cli-schema.json](cli-schema.json). diff --git a/docs/usage.md b/docs/usage.md index 74b76ae7..f774b620 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -20,6 +20,7 @@ winapp init [base-directory] [options] - `--no-gitignore` - Don't update .gitignore file - `--use-defaults`, `--no-prompt` - Do not prompt, and use default of all prompts - `--config-only` - Only handle configuration file operations, skip package installation +- `--no-skills` - Don't generate AI agent skill files (for Copilot, Claude, etc.) **What it does:** @@ -29,6 +30,7 @@ winapp init [base-directory] [options] - Creates AppxManifest.xml - Sets up build tools and enables developer mode - Updates .gitignore to exclude generated files +- Generates AI agent skill files for coding assistants (GitHub Copilot, Claude, Cursor, etc.) — prompts to confirm unless skills already exist or `--no-skills` is passed - Stores sharable files in the global cache directory **Automatic .NET project detection:** @@ -538,6 +540,40 @@ winapp store publish ./myapp.msix --appId --- +### agents generate + +Generate AI agent skill files (SKILL.md) that help coding assistants like GitHub Copilot and Claude Code understand your winapp project. + +```bash +winapp agents generate [options] +``` + +**Options:** + +- `--skills-dir ` - Skills directory override (default: auto-detect from `.github/skills`, `.agents/skills`, or `.claude/skills`) +- `--directory ` - Project root directory (default: current directory) + +**What it does:** + +- Detects existing skills directories (`.github/skills/`, `.agents/skills/`, `.claude/skills/`) or creates `.github/skills/` by default +- Writes 7 skill files covering setup, packaging, identity, signing, manifests, troubleshooting, and framework guides +- Skills are also generated automatically during `winapp init` (unless `--no-skills` is passed) + +**Examples:** + +```bash +# Generate skills in auto-detected directory +winapp agents generate + +# Specify a custom skills directory +winapp agents generate --skills-dir ./my-skills + +# Generate skills for a different project +winapp agents generate --directory ./my-project +``` + +--- + ### get-winapp-path Get paths to installed Windows SDK components. diff --git a/docs/using-with-llms.md b/docs/using-with-llms.md deleted file mode 100644 index 2e697061..00000000 --- a/docs/using-with-llms.md +++ /dev/null @@ -1,388 +0,0 @@ -# Using winapp CLI with AI Coding Assistants - -This guide provides prompt templates and best practices for using AI coding assistants (GitHub Copilot, Cursor, Claude, ChatGPT, etc.) with winapp CLI. - -## Quick Start Prompt - -Copy and paste this into your AI coding assistant: - -``` -I'm working with winapp CLI - a CLI for generating and managing appxmanifest.xml, -image assets, test certificates, Windows (App) SDK projections, package identity, -and packaging for any app framework targeting Windows. - -Please read and reference the official LLM context documentation: -https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - -My specific task: [describe what you need help with] -``` - ---- - -## Detailed Prompt Template - -For more complex tasks, use this extended version: - -```markdown -# Context: winapp CLI Project - -I'm working with **winapp CLI** - a CLI for generating and managing appxmanifest.xml, -image assets, test certificates, Windows (App) SDK projections, package identity, -and packaging. It works with any app framework targeting Windows. - -## Documentation Reference - -**Primary LLM-optimized documentation (please fetch and reference):** -https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - -This contains: -- Complete command reference with all options -- Common workflows (new projects, existing projects, debugging, Electron apps) -- Prerequisites and state requirements -- Machine-readable CLI schema - -Additional resources if needed: -- Main README: https://github.com/microsoft/WinAppCli/blob/main/README.md -- Full usage docs: https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md -- Electron guide: https://github.com/microsoft/WinAppCli/blob/main/docs/electron-get-started.md - -## My Project Details - -**Framework:** [e.g., Electron, .NET, C++, Rust, Tauri, Python, etc.] - -**Current setup:** -- [Describe your current project state] -- [Mention if you have winapp.yaml, appxmanifest.xml, etc.] -- [Current build process] - -**What I need help with:** -[Choose relevant workflow:] -- [ ] New project setup with SDK projections -- [ ] Adding package identity for debugging Windows APIs -- [ ] MSIX packaging and signing -- [ ] Electron integration with native addons -- [ ] CI/CD pipeline integration -- [ ] Updating SDK versions -- [ ] Certificate management -- [ ] Manifest generation or modification - -## Specific Question/Task - -[Your detailed question or task here] -``` - ---- - -## For GitHub Copilot Users - -### Method 1: Workspace Context (Recommended) - -1. Download the LLM-optimized documentation locally: - ```bash - mkdir -p .ai - curl -o .ai/winapp-llm-context.md https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - ``` - -2. Keep the file open in your editor while coding - Copilot will automatically use it as context - -3. Optionally, also download the CLI schema for structured data: - ```bash - curl -o .ai/winapp-cli-schema.json https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/cli-schema.json - ``` - -### Method 2: Project Instructions - -Create `.github/copilot-instructions.md` in your repository: - -```markdown -# winapp CLI Project Instructions - -This project uses winapp CLI for Windows app development, packaging, and identity management. - -## Core Workflows - -**New Project:** -1. `winapp init .` (or with `--use-defaults` for non-interactive) -2. Build your app -3. `winapp create-debug-identity ` for package identity during development -4. `winapp pack --cert .\devcert.pfx` for MSIX packaging - -**Existing Project/CI:** -1. `winapp restore` - reinstall from winapp.yaml -2. Build and package - -**Electron Projects:** -1. `winapp init --use-defaults` -2. `winapp node create-addon --template cs` (or `--template cpp`) -3. `winapp node add-electron-debug-identity` -4. `npm start` (now with package identity) -5. `winapp pack --cert .\devcert.pfx` - -## Key Commands -- `winapp init` - Initialize with manifests, assets, SDK projections -- `winapp restore` - Restore packages from winapp.yaml -- `winapp manifest generate` - Create appxmanifest.xml -- `winapp cert generate` - Create development certificate -- `winapp package` - Build MSIX from output folder -- `winapp create-debug-identity` - Add temporary package identity for debugging -- `winapp tool` - Run Windows SDK build tools - -## Documentation Reference -Primary: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -Schema: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/cli-schema.json - -When helping with winapp CLI tasks: -1. Reference the llm-context.md for command details and workflows -2. Check winapp.yaml for project configuration -3. Verify appxmanifest.xml exists for packaging operations -4. Ensure devcert.pfx is available for signing -``` - ---- - -## For Cursor/Claude/ChatGPT Users - -Simply paste this at the start of your conversation: - -``` -I need help with winapp CLI - a CLI for managing appxmanifest.xml, -image assets, certificates, Windows SDK projections, and MSIX packaging. - -Please fetch and reference the LLM-optimized documentation: -https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - -My task: [describe what you need] - -[Optional: Also mention your framework - Electron, .NET, C++, Rust, Python, etc.] -``` - -These assistants can fetch the documentation directly from the URL. - ---- - -## Framework-Specific Prompts - -### For Electron Projects - -``` -I'm building an Electron app and need help with winapp CLI for: -- [ ] Setting up package identity for debugging -- [ ] Creating native C#/C++ addons for Windows APIs -- [ ] MSIX packaging for distribution -- [ ] Code signing with certificates - -Please reference the LLM-optimized docs: -https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - -Also see Electron-specific guide if needed: -https://github.com/microsoft/WinAppCli/blob/main/docs/electron-get-started.md - -My current setup: [describe your Electron project structure] -My specific need: [e.g., "add push notification support via native addon"] -``` - -**Common Electron tasks:** -- `winapp node create-addon --template cs` - Create C# native addon -- `winapp node create-addon --template cpp` - Create C++ native addon -- `winapp node add-electron-debug-identity` - Enable identity for debugging -- `winapp node clear-electron-debug-identity` - Remove identity - -### For .NET Projects - -``` -I'm building a .NET [WPF/WinForms/Console] app and need winapp CLI help with: -- [ ] Adding package identity for modern Windows APIs -- [ ] MSIX packaging -- [ ] Certificate generation and signing -- [ ] Windows Store preparation - -Reference the LLM-optimized docs: -https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - -Framework guide: -https://github.com/microsoft/WinAppCli/blob/main/docs/guides/dotnet.md - -My project: [describe your .NET project] -``` - -**Key workflow for .NET:** -1. `winapp init` - Sets up manifests and optionally SDK projections -2. Build your app normally -3. `winapp create-debug-identity YourApp.exe` - For testing APIs requiring identity -4. `winapp pack bin\Release\net8.0-windows --cert devcert.pfx` - Package as MSIX - -### For Flutter Projects - -``` -I'm building a Flutter Windows app and need winapp CLI for: -- [ ] Package identity for Windows APIs -- [ ] MSIX packaging -- [ ] Certificate generation and signing - -Reference the LLM-optimized docs: -https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - -Flutter guide: -https://github.com/microsoft/WinAppCli/blob/main/docs/guides/flutter.md - -My project: [describe your Flutter project] -``` - -**Key workflow for Flutter:** -1. `winapp init --setup-sdks stable` - Sets up manifests and SDK headers -2. `flutter build windows` - Build your app normally -3. `winapp create-debug-identity .\build\windows\x64\runner\Release\flutter_app.exe` - Test with identity -4. `winapp pack .\dist --cert .\devcert.pfx` - Package as MSIX - -### For C++ Projects - -``` -I'm building a C++ Win32 app with [CMake/MSBuild] and need winapp CLI for: -- [ ] Windows SDK integration and projections -- [ ] Package identity for modern Windows APIs -- [ ] MSIX packaging -- [ ] Manifest generation - -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -C++ guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/cpp.md - -My build system: [CMake/MSBuild/other] -My specific need: [e.g., "integrate Windows App SDK with CMake build"] -``` - -**C++ workflow:** -1. `winapp init --setup-sdks stable` - Download Windows SDK + App SDK projections -2. Add `.winapp/packages` to include paths -3. Build your app -4. `winapp create-debug-identity YourApp.exe` - Test with package identity -5. `winapp pack build/release --cert devcert.pfx` - Create MSIX - ---- - -## CI/CD Integration Prompt - -``` -I need to integrate winapp CLI into my [GitHub Actions/Azure DevOps] pipeline for: -- [ ] Automated winapp.yaml package restoration -- [ ] MSIX packaging in build pipeline -- [ ] Code signing (with cert stored in secrets) -- [ ] Build artifact generation - -Current pipeline: [describe your CI/CD setup] - -Please reference: -- Setup action: https://github.com/microsoft/setup-WinAppCli -- LLM docs: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - -My specific need: [e.g., "restore packages and build MSIX in GitHub Actions"] -``` - -**Example GitHub Actions workflow:** -```yaml -- uses: microsoft/setup-WinAppCli@v1 -- name: Restore winapp CLI packages - run: winapp restore -- name: Build app - run: [your build command] -- name: Package MSIX - run: winapp pack ./dist --cert ${{ secrets.CERT_PATH }} -``` - ---- - -## Common Tasks - Quick Prompts - -**Initialize new project:** -``` -Help me set up a new [framework] project with winapp CLI. -Run: winapp init . --use-defaults -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` - -**Generate manifest only:** -``` -I need to create an appxmanifest.xml for my [framework] app. -Use: winapp manifest generate -Project details: [app name, publisher, entry point] -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` - -**Add debug identity:** -``` -I need package identity to debug Windows APIs that require it (e.g., push notifications). -Help me use: winapp create-debug-identity -I have: [appxmanifest.xml location] -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` - -**Package as MSIX:** -``` -Help me create a signed MSIX package for distribution. -App output folder: [path] -Use: winapp pack [folder] --cert devcert.pfx -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` - -**Create development certificate:** -``` -Help me generate a test certificate for code signing. -Use: winapp cert generate --publisher "CN=MyCompany" --install -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` - -**Restore from existing project:** -``` -I cloned a project with winapp.yaml and need to restore packages. -Use: winapp restore -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` - -**Update SDK versions:** -``` -Help me update to the latest Windows SDK and App SDK versions. -Use: winapp update --setup-sdks stable -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` -**Install SDKs after initial setup:** -``` -I ran winapp init without SDK installation and now need the SDKs. -Use: winapp init --use-defaults --setup-sdks stable -This re-runs init, skips prompts, preserves existing files, and installs the SDKs. -Reference: https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md -``` ---- - -## Tips for Best Results - -1. **Always reference the LLM-optimized docs** - Include the llm-context.md URL in your prompt: - ``` - https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md - ``` - -2. **Be specific about your framework** - Electron, .NET, C++, Rust, Python, etc. - -3. **Mention existing files** - Does your project already have: - - `winapp.yaml` (version configuration) - - `appxmanifest.xml` (package manifest) - - `devcert.pfx` (development certificate) - - `.winapp/` folder (SDK packages) - -4. **State your goal clearly**: - - Local debugging with package identity - - MSIX packaging for distribution - - Windows Store submission - - CI/CD automation - - Native addon development (Electron) - -5. **Include error messages** if troubleshooting - Full command output helps - -6. **Check prerequisites** - Some commands require: - - `winapp init` → Creates initial setup - - `winapp restore` → Needs existing `winapp.yaml` - - `winapp package` → Needs built app output + `appxmanifest.xml` - - `winapp create-debug-identity` → Needs `appxmanifest.xml` + exe path - -7. **For advanced scenarios** - Mention the CLI schema is available: - ```bash - winapp --cli-schema # Outputs machine-readable JSON - ``` diff --git a/llms.txt b/llms.txt index 619fd234..56bf1f40 100644 --- a/llms.txt +++ b/llms.txt @@ -4,9 +4,27 @@ winapp helps developers add Windows-specific features (MSIX packaging, push notifications, native APIs) to apps built with any framework—Electron, Tauri, .NET, C++, Rust, or others. It handles the complexity of Windows app identity, code signing, and SDK integration. +## AI Coding Agents + +- **Copilot CLI Plugin:** `copilot plugin install microsoft/WinAppCli` +- **Project-level skills:** `winapp agents generate` (also runs during `winapp init`) + +### Copilot Plugin Structure + +The plugin is at `.github/plugin/` and includes: + +- **Agent:** [winapp.agent.md](https://github.com/microsoft/WinAppCli/blob/main/.github/plugin/agents/winapp.agent.md) — full system prompt with command reference, decision trees, framework-specific guidance, error diagnosis, and critical rules +- **Skills** (auto-generated, in `.github/plugin/skills/winapp-cli/`): + - `setup/SKILL.md` — project init, restore, update + - `package/SKILL.md` — MSIX packaging + - `identity/SKILL.md` — debug identity and sparse packages + - `signing/SKILL.md` — certificates and code signing + - `manifest/SKILL.md` — appxmanifest.xml generation + - `troubleshoot/SKILL.md` — error diagnosis and SDK tools + - `frameworks/SKILL.md` — Electron, .NET, C++, Rust, Flutter, Tauri guidance + ## Docs -- [LLM Context](https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/llm-context.md): Complete command reference, workflows, and prerequisites for AI assistants - [CLI Schema](https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/cli-schema.json): Machine-readable JSON schema of all commands, options, and types - [Usage Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md): Full documentation for humans @@ -22,5 +40,4 @@ winapp helps developers add Windows-specific features (MSIX packaging, push noti ## Optional -- [Copilot Instructions](https://github.com/microsoft/WinAppCli/blob/main/.github/copilot-instructions.md): For AI assistants working on the winapp repo itself -- [Using with LLMs](https://github.com/microsoft/WinAppCli/blob/main/docs/using-with-llms.md): Prompt templates and integration patterns +- [AGENTS.md](https://github.com/microsoft/WinAppCli/blob/main/AGENTS.md): For AI assistants working on the winapp repo itself diff --git a/scripts/assets/llm-context-footer.md b/scripts/assets/llm-context-footer.md deleted file mode 100644 index a05602af..00000000 --- a/scripts/assets/llm-context-footer.md +++ /dev/null @@ -1,101 +0,0 @@ -## Common Workflows - -### New Project Setup -1. `winapp init .` - Initialize workspace with appxmanifest.xml, image assets, and optionally SDK projections in the .winapp folder. (run with `--use-defaults` to make it non-interactive) -2. Edit `appxmanifest.xml` if you need to modify properties, set capabilities, or other configurations -3. Build your app -4. `winapp create-debug-identity ` - to generate package identity from generated appxmanifest.xml before running the app so the exe has package identity -5. Run the app -6. `winapp pack --cert .\devcert.pfx` - Create signed MSIX (--cert is optional) - -### Existing Project (Clone/CI) -1. `winapp restore` - Reinstall packages and generate C++ projections from `winapp.yaml` -2. Build and package as normal - -### Update SDK Versions -1. `winapp update` - Check for and install newer SDK versions -2. Rebuild your app - -### Install SDKs After Initial Setup -If you ran `init` with `--setup-sdks none` (or skipped SDK installation) and later need the SDKs: -1. `winapp init --use-defaults --setup-sdks stable` - Re-run init to install SDKs - - `--use-defaults` skips prompts and preserves existing files (manifest, etc.) - - Use `--setup-sdks preview` or `--setup-sdks experimental` for preview/experimental SDK versions -2. Rebuild your app with the new SDK projections in `.winapp/` - -### Debug with Package Identity -For apps that need Windows APIs requiring identity (push notifications, etc.): -1. Ensure an appxmanifest.xml is present, either via `winapp init` or `winapp manifest generate` -2. `winapp create-debug-identity ./myapp.exe` - generate package identity from generated appxmanifest.xml before running the app so the exe has package identity -3. Run your app - it now has package identity - -### Electron Apps -1. `winapp init` - Set up workspace (run with --use-defaults to make it non-interactive) -2. `winapp node create-addon --template cs` - Generate native C# addon for Windows APIs (`--template cpp` for C++ addon) -3. `winapp node add-electron-debug-identity` - Enable identity for debugging -4. `npm start` to launch app normally, but now with identity -5. For production, create production files with the preferred packager and run `winapp pack --cert .\devcert.pfx` - -## Command Selection Guide - -Use this decision tree to pick the right command: - -``` -Using winapp CLI in a new project? -├─ Yes → run winapp init -│ (creates manifest + cert + SDKs + config) -└─ No - ├─ Cloned/pulled a repo with winapp.yaml? - │ └─ Yes → winapp restore - │ (reinstalls SDKs from config) - ├─ Want newer SDK versions? - │ └─ Yes → winapp update - │ (checks NuGet, updates config) - ├─ Only need a manifest (no SDKs/cert)? - │ └─ Yes → winapp manifest generate - ├─ Only need a dev certificate? - │ └─ Yes → winapp cert generate - ├─ Ready to create MSIX installer? - │ └─ Yes → winapp package - │ (add --cert for signing) - ├─ Need to add package identity for debugging Windows APIs that need it? - │ └─ Yes → winapp create-debug-identity - │ (enables push notifications, etc.) - └─ Need to run SDK tools directly? - └─ Yes → winapp tool - (makeappx, signtool, makepri) -``` - -## Prerequisites & State - -| Command | Requires | Creates/Modifies | -|---------|----------|------------------| -| `init` | Nothing | `winapp.yaml`, `.winapp/`, `appxmanifest.xml`, `Assets/` | -| `restore` | `winapp.yaml` | `.winapp/packages/` | -| `update` | `winapp.yaml` | Updates versions in `winapp.yaml` | -| `manifest generate` | Nothing | `appxmanifest.xml`, `Assets/` | -| `cert generate` | Nothing (or `appxmanifest.xml` for publisher inference) | `*.pfx` file | -| `package` | App build output + `appxmanifest.xml` (+ `devcert.pfx` for optional signing) | `*.msix` file | -| `create-debug-identity` | `appxmanifest.xml` + exe | Registers sparse package with Windows | - -## Common Errors & Solutions - -| Error | Cause | Solution | -|-------|-------|----------| -| "winapp.yaml not found" | Running `restore` or `update` without config | Run `winapp init` first, or ensure you're in the right directory | -| "appxmanifest.xml not found" | Running `package` or `create-debug-identity` without manifest | Run `winapp init` or `winapp manifest generate` first | -| "Publisher mismatch" | Certificate publisher doesn't match manifest | Regenerate cert with `--manifest` flag, or edit manifest Publisher to match | -| "Access denied" / "elevation required" | `cert install` without admin | Run terminal as Administrator | -| "Package installation failed" | Signing issue or existing package conflict | Run `Get-AppxPackage | Remove-AppxPackage` first, ensure cert is trusted | -| "Certificate not trusted" | Dev cert not installed on machine | Run `winapp cert install ./devcert.pfx` as admin | -| "Build tools not found" | First run, tools not downloaded yet | winapp auto-downloads; ensure internet access. Or run `winapp tool --help` to trigger download | - -## Machine-Readable Schema - -For programmatic access to the complete CLI structure including all options, types, and defaults: - -```bash -winapp --cli-schema -``` - -This outputs JSON that can be parsed by tools and LLMs. See [cli-schema.json](cli-schema.json). diff --git a/scripts/build-cli.ps1 b/scripts/build-cli.ps1 index 2ce393fb..c023dfc5 100644 --- a/scripts/build-cli.ps1 +++ b/scripts/build-cli.ps1 @@ -15,7 +15,7 @@ .PARAMETER SkipMsix Skip MSIX packages creation .PARAMETER SkipDocs - Skip LLM documentation generation (useful in CI where docs are validated separately) + Skip CLI schema and agent skills generation (useful in CI where docs are validated separately) .PARAMETER Stable Use stable build configuration (default: false, uses prerelease config) .EXAMPLE @@ -196,10 +196,10 @@ try exit 1 } - # Step 6: Generate LLM documentation (optional) + # Step 6: Generate CLI schema and agent skills (optional) if (-not $SkipDocs) { Write-Host "" - Write-Host "[DOCS] Generating LLM documentation..." -ForegroundColor Blue + Write-Host "[DOCS] Generating CLI schema and agent skills..." -ForegroundColor Blue $GenerateLlmDocsScript = Join-Path $PSScriptRoot "generate-llm-docs.ps1" $CliExePath = Join-Path $ProjectRoot "$ArtifactsPath\cli\win-x64\winapp.exe" @@ -207,13 +207,13 @@ try & $GenerateLlmDocsScript -CliPath $CliExePath -CalledFromBuildScript if ($LASTEXITCODE -ne 0) { - Write-Warning "LLM documentation generation failed, but continuing..." + Write-Warning "CLI schema and agent skills generation failed, but continuing..." } else { - Write-Host "[DOCS] LLM documentation generated successfully!" -ForegroundColor Green + Write-Host "[DOCS] CLI schema and agent skills generated successfully!" -ForegroundColor Green } } else { Write-Host "" - Write-Host "[DOCS] Skipping LLM documentation generation (-SkipDocs)" -ForegroundColor Yellow + Write-Host "[DOCS] Skipping CLI schema and agent skills generation (-SkipDocs)" -ForegroundColor Yellow } # Step 7: Create npm package (optional) diff --git a/scripts/generate-llm-docs.ps1 b/scripts/generate-llm-docs.ps1 index 73ba40e4..e149a668 100644 --- a/scripts/generate-llm-docs.ps1 +++ b/scripts/generate-llm-docs.ps1 @@ -1,14 +1,16 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Generate LLM-friendly documentation from CLI schema + Generate CLI schema and agent skills from CLI binary .DESCRIPTION - This script generates docs/cli-schema.json and docs/llm-context.md from the CLI's - --cli-schema output. Run after building the CLI to keep documentation in sync. + This script generates docs/cli-schema.json and SKILL.md files + from the CLI's --cli-schema output. Run after building the CLI to keep documentation in sync. .PARAMETER CliPath Path to the winapp.exe CLI binary (default: artifacts/cli/win-x64/winapp.exe) .PARAMETER DocsPath Path to the docs folder (default: docs) +.PARAMETER SkillsPath + Path to the skills output folder (default: .github/plugin/skills/winapp-cli) .EXAMPLE .\scripts\generate-llm-docs.ps1 .EXAMPLE @@ -18,6 +20,7 @@ param( [string]$CliPath = "", [string]$DocsPath = "", + [string]$SkillsPath = "", [switch]$CalledFromBuildScript = $false ) @@ -34,8 +37,11 @@ if (-not $DocsPath) { $DocsPath = Join-Path $ProjectRoot "docs" } +if (-not $SkillsPath) { + $SkillsPath = Join-Path $ProjectRoot ".github\plugin\skills\winapp-cli" +} + $SchemaOutputPath = Join-Path $DocsPath "cli-schema.json" -$LlmContextPath = Join-Path $DocsPath "llm-context.md" # Verify CLI exists if (-not (Test-Path $CliPath)) { @@ -44,7 +50,7 @@ if (-not (Test-Path $CliPath)) { exit 1 } -Write-Host "[DOCS] Generating LLM documentation..." -ForegroundColor Blue +Write-Host "[DOCS] Generating CLI schema and agent skills..." -ForegroundColor Blue Write-Host "CLI path: $CliPath" -ForegroundColor Gray Write-Host "Docs path: $DocsPath" -ForegroundColor Gray @@ -67,134 +73,240 @@ Write-Host "[DOCS] Saved: $SchemaOutputPath" -ForegroundColor Green # Parse schema for markdown generation $Schema = $SchemaJson | ConvertFrom-Json -# Step 2: Generate llm-context.md -Write-Host "[DOCS] Generating llm-context.md..." -ForegroundColor Blue - -$LlmContext = @" ---- -name: winapp-cli -description: $($Schema.description) -version: $($Schema.version) -schema_version: $($Schema.schemaVersion) ---- - -# winapp CLI Context for LLMs +# ============================================================================== +# Step 2: Generate SKILL.md files for agent context +# ============================================================================== +Write-Host "" +Write-Host "[SKILLS] Generating agent skill files..." -ForegroundColor Blue -> Auto-generated from CLI v$($Schema.version) (schema version $($Schema.schemaVersion)) -> -> This file provides structured context about the winapp CLI for AI assistants and LLMs. -> For the raw JSON schema, see [cli-schema.json](cli-schema.json). +$CliVersion = $Schema.version +$SkillTemplatesDir = Join-Path (Split-Path $PSScriptRoot) "docs\fragments\skills\winapp-cli" -## Overview +# Output directory for generated skills (use parameter or default) +$SkillsDir = $SkillsPath -$($Schema.description) - -**Installation:** -- WinGet: ``winget install Microsoft.WinAppCli --source winget`` -- npm: ``npm install -g @microsoft/winappcli`` (for electron projects) +# Skill → CLI command mapping for auto-generated options/arguments tables +# Each skill maps to one or more CLI commands whose options/arguments should be included +$SkillCommandMap = @{ + "setup" = @("init", "restore", "update", "agents generate") + "package" = @("package", "create-external-catalog") + "identity" = @("create-debug-identity") + "signing" = @("cert generate", "cert install", "sign") + "manifest" = @("manifest generate", "manifest update-assets") + "troubleshoot" = @("get-winapp-path", "tool", "store") + "frameworks" = @() # No auto-generated command sections — links to guides +} -## Command Reference +# Validate that all CLI commands are covered by at least one skill +$allMappedCommands = $SkillCommandMap.Values | ForEach-Object { $_ } | Where-Object { $_ } +$allSchemaCommands = @() +foreach ($cmd in $Schema.subcommands.PSObject.Properties) { + if ($cmd.Value.subcommands) { + foreach ($sub in $cmd.Value.subcommands.PSObject.Properties) { + $allSchemaCommands += "$($cmd.Name) $($sub.Name)" + } + } else { + $allSchemaCommands += $cmd.Name + } +} +$unmappedCommands = $allSchemaCommands | Where-Object { $_ -notin $allMappedCommands } +if ($unmappedCommands) { + Write-Warning "The following CLI commands are not mapped to any skill in `$SkillCommandMap:" + foreach ($cmd in $unmappedCommands) { + Write-Warning " - $cmd" + } + Write-Warning "Add them to `$SkillCommandMap in generate-llm-docs.ps1 so their options appear in SKILL.md files." +} -"@ +# Function to resolve a command path like "cert generate" from the schema +function Get-SchemaCommand { + param([string]$CommandPath, [PSObject]$RootSchema) + + $parts = $CommandPath -split ' ' + $current = $RootSchema + foreach ($part in $parts) { + if ($current.subcommands -and $current.subcommands.PSObject.Properties[$part]) { + $current = $current.subcommands.$part + } else { + return $null + } + } + return $current +} -# Function to format a command and its subcommands -function Format-Command { - param( - [string]$Name, - [PSObject]$Command, - [int]$Depth = 0 - ) +# Function to format a command's arguments as a markdown table +function Format-ArgumentsTable { + param([PSObject]$Command) - $indent = " " * $Depth - $headingLevel = [Math]::Min($Depth + 3, 6) - $heading = "#" * $headingLevel + if (-not $Command.arguments) { return "" } $output = @() - $output += "" - $output += "$heading ``winapp $Name``" - $output += "" - $output += "$($Command.description)" - - # Aliases - if ($Command.aliases -and $Command.aliases.Count -gt 0) { - $aliasStr = ($Command.aliases | ForEach-Object { "``$_``" }) -join ", " - $output += "" - $output += "**Aliases:** $aliasStr" + $output += "| Argument | Required | Description |" + $output += "|----------|----------|-------------|" + + $sortedArgs = $Command.arguments.PSObject.Properties | Sort-Object { $_.Value.order } + foreach ($arg in $sortedArgs) { + $required = if ($arg.Value.arity.minimum -gt 0) { "Yes" } else { "No" } + $output += "| ``<$($arg.Name)>`` | $required | $($arg.Value.description) |" } - # Arguments - if ($Command.arguments) { - $output += "" - $output += "**Arguments:**" - $sortedArgs = $Command.arguments.PSObject.Properties | Sort-Object { $_.Value.order } - foreach ($arg in $sortedArgs) { - $argName = $arg.Name - $argDetails = $arg.Value - $required = if ($argDetails.arity.minimum -gt 0) { " *(required)*" } else { "" } - $default = if ($argDetails.hasDefaultValue -and $null -ne $argDetails.defaultValue) { " (default: ``$($argDetails.defaultValue)``)" } else { "" } - $output += "- ``<$argName>``$required - $($argDetails.description)$default" - } + return ($output -join "`n") +} + +# Function to format a command's options as a markdown table +function Format-OptionsTable { + param([PSObject]$Command) + + if (-not $Command.options) { return "" } + + $visibleOptions = $Command.options.PSObject.Properties | Where-Object { + -not $_.Value.hidden -and $_.Name -notin @('--verbose', '--quiet', '-v', '-q') } - # Options (exclude hidden) - if ($Command.options) { - $visibleOptions = $Command.options.PSObject.Properties | Where-Object { -not $_.Value.hidden } - if ($visibleOptions) { - $output += "" - $output += "**Options:**" - foreach ($opt in $visibleOptions) { - $optName = $opt.Name - $optDetails = $opt.Value - $aliases = if ($optDetails.aliases -and $optDetails.aliases.Count -gt 0) { - $filteredAliases = $optDetails.aliases | Where-Object { $_ -ne $optName -and $_ -ne "--$optName" } - if ($filteredAliases) { " / ``$($filteredAliases -join '``, ``')``" } else { "" } - } else { "" } - $default = if ($optDetails.hasDefaultValue -and $null -ne $optDetails.defaultValue -and $optDetails.defaultValue -ne "") { - " (default: ``$($optDetails.defaultValue)``)" - } else { "" } - $output += "- ``$optName``$aliases - $($optDetails.description)$default" - } + if (-not $visibleOptions) { return "" } + + $output = @() + $output += "| Option | Description | Default |" + $output += "|--------|-------------|---------|" + + foreach ($opt in $visibleOptions) { + $default = if ($opt.Value.hasDefaultValue -and $null -ne $opt.Value.defaultValue -and $opt.Value.defaultValue -ne "" -and $opt.Value.defaultValue -ne "False") { + "``$($opt.Value.defaultValue)``" + } else { + "(none)" } + $output += "| ``$($opt.Name)`` | $($opt.Value.description) | $default |" } - # Subcommands - if ($Command.subcommands) { - foreach ($sub in $Command.subcommands.PSObject.Properties) { - if (-not $sub.Value.hidden) { - $subOutput = Format-Command -Name "$Name $($sub.Name)" -Command $sub.Value -Depth ($Depth + 1) - $output += $subOutput - } + return ($output -join "`n") +} + +# Function to generate auto-generated command sections for a skill +function Format-CommandSections { + param([string[]]$CommandPaths, [PSObject]$RootSchema) + + if (-not $CommandPaths -or $CommandPaths.Count -eq 0) { return "" } + + $output = @() + + foreach ($cmdPath in $CommandPaths) { + $cmd = Get-SchemaCommand -CommandPath $cmdPath -RootSchema $RootSchema + if (-not $cmd) { + Write-Warning "Command '$cmdPath' not found in schema" + continue + } + + $output += "" + $output += "### ``winapp $cmdPath``" + $output += "" + $output += $cmd.description + + # Aliases + if ($cmd.aliases -and $cmd.aliases.Count -gt 0) { + $aliasStr = ($cmd.aliases | ForEach-Object { "``$_``" }) -join ", " + $output += "" + $output += "**Aliases:** $aliasStr" + } + + # Arguments table + $argsTable = Format-ArgumentsTable -Command $cmd + if ($argsTable) { + $output += "" + $output += "#### Arguments" + $output += "" + $output += $argsTable + } + + # Options table + $optsTable = Format-OptionsTable -Command $cmd + if ($optsTable) { + $output += "" + $output += "#### Options" + $output += "" + $output += $optsTable } } - return $output + return ($output -join "`n") +} + +# Generate each skill +$SkillNames = @("setup", "package", "identity", "signing", "manifest", "troubleshoot", "frameworks") +$SkillDescriptions = @{ + "setup" = "Project setup with winapp CLI: init, restore, update. Use when setting up a new project, restoring after clone, or updating SDK versions." + "package" = "Create an MSIX installer package from a built Windows app using winapp CLI. Use when the user needs to package, distribute, or test their Windows app as an MSIX." + "identity" = "Add package identity for debugging Windows APIs that require it (push notifications, background tasks, etc.) without creating a full MSIX package." + "signing" = "Generate, install, and manage development certificates for code signing MSIX packages and executables with winapp CLI." + "manifest" = "Generate and modify appxmanifest.xml files for package identity and MSIX packaging with winapp CLI." + "troubleshoot" = "Diagnose and fix common errors with winapp CLI. Use when the user encounters errors or needs help choosing the right command." + "frameworks" = "Framework-specific guidance and links to detailed guides for Electron, .NET, C++, Rust, Flutter, and Tauri. Use when working with a specific app framework." } -# Generate command documentation -foreach ($cmd in $Schema.subcommands.PSObject.Properties | Sort-Object Name) { - if (-not $cmd.Value.hidden) { - $cmdOutput = Format-Command -Name $cmd.Name -Command $cmd.Value - $LlmContext += ($cmdOutput -join "`n") +foreach ($skillName in $SkillNames) { + $templatePath = Join-Path $SkillTemplatesDir "$skillName.md" + + if (-not (Test-Path $templatePath)) { + Write-Warning "Skill template not found: $templatePath" + continue } + + $templateContent = Get-Content $templatePath -Raw + $commandPaths = $SkillCommandMap[$skillName] + $description = $SkillDescriptions[$skillName] + + # Build the SKILL.md content + $skillContent = @" + +--- +name: winapp-$skillName +description: $description +--- + +"@ + + # Add template content (hand-written workflows, examples, troubleshooting) + $skillContent += $templateContent + + # Add auto-generated command sections + $commandSections = Format-CommandSections -CommandPaths $commandPaths -RootSchema $Schema + if ($commandSections) { + $skillContent += "`n`n## Command Reference`n" + $skillContent += $commandSections + } + + # Normalize line endings and ensure trailing newline + $skillContent = $skillContent -replace "`r`n", "`n" + $skillContent = $skillContent.TrimEnd() + "`n" + + # Write to output directory + $skillDir = Join-Path $SkillsDir $skillName + if (-not (Test-Path $skillDir)) { + New-Item -ItemType Directory -Path $skillDir -Force | Out-Null + } + $skillPath = Join-Path $skillDir "SKILL.md" + [System.IO.File]::WriteAllText($skillPath, $skillContent, [System.Text.UTF8Encoding]::new($false)) + + Write-Host "[SKILLS] $skillName - generated" -ForegroundColor Gray } -# Add footer from separate markdown file (workflows, prerequisites, etc.) -$FooterPath = Join-Path $PSScriptRoot "assets\llm-context-footer.md" -if (Test-Path $FooterPath) { - $Footer = Get-Content $FooterPath -Raw - $LlmContext += "`n`n" + $Footer -} else { - Write-Warning "Footer file not found: $FooterPath" +# Update plugin.json version to match CLI version +$PluginJsonPath = Join-Path $ProjectRoot ".github\plugin\plugin.json" +if (Test-Path $PluginJsonPath) { + $pluginJson = Get-Content $PluginJsonPath -Raw | ConvertFrom-Json + $pluginJson.version = $CliVersion + $pluginJsonContent = $pluginJson | ConvertTo-Json -Depth 10 + # Normalize line endings + $pluginJsonContent = $pluginJsonContent -replace "`r`n", "`n" + $pluginJsonContent = $pluginJsonContent.TrimEnd() + "`n" + [System.IO.File]::WriteAllText($PluginJsonPath, $pluginJsonContent, [System.Text.UTF8Encoding]::new($false)) + Write-Host "[SKILLS] Updated plugin.json version to $CliVersion" -ForegroundColor Gray } -# Save llm-context.md with consistent LF line endings (same as cli-schema.json) -# Normalize CRLF to LF and ensure exactly one trailing newline -$LlmContext = $LlmContext -replace "`r`n", "`n" -$LlmContext = $LlmContext.TrimEnd() + "`n" -[System.IO.File]::WriteAllText($LlmContextPath, $LlmContext, [System.Text.UTF8Encoding]::new($false)) -Write-Host "[DOCS] Saved: $LlmContextPath" -ForegroundColor Green +Write-Host "[SKILLS] Generated $($SkillNames.Count) skills in:" -ForegroundColor Green +Write-Host " .github/plugin/skills/winapp-cli/ (also embedded in CLI binary via csproj link)" -ForegroundColor Gray -Write-Host "[DOCS] LLM documentation generated successfully!" -ForegroundColor Green +Write-Host "" +Write-Host "[DOCS] All documentation and skills generated successfully!" -ForegroundColor Green # Warn if running directly (not from build-cli.ps1) if (-not $CalledFromBuildScript -and $UsingDefaultPaths) { diff --git a/scripts/validate-llm-docs.ps1 b/scripts/validate-llm-docs.ps1 index a346594e..71949ecc 100644 --- a/scripts/validate-llm-docs.ps1 +++ b/scripts/validate-llm-docs.ps1 @@ -1,10 +1,10 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Validate that LLM documentation is up-to-date with CLI schema + Validate that CLI schema and agent skills are up-to-date .DESCRIPTION - This script checks if the generated LLM documentation (docs/cli-schema.json and - docs/llm-context.md) matches what the CLI would generate. Use this locally before + This script checks if the generated CLI schema (docs/cli-schema.json) and agent skills + match what the CLI would generate. Use this locally before committing changes, or in CI to catch drift. .PARAMETER CliPath Path to the winapp.exe CLI binary (default: artifacts/cli/win-x64/winapp.exe) @@ -28,7 +28,6 @@ if (-not $CliPath) { } $SchemaPath = Join-Path $ProjectRoot "docs\cli-schema.json" -$ContextPath = Join-Path $ProjectRoot "docs\llm-context.md" # Verify CLI exists if (-not (Test-Path $CliPath)) { @@ -37,7 +36,7 @@ if (-not (Test-Path $CliPath)) { exit 1 } -Write-Host "[VALIDATE] Checking LLM documentation..." -ForegroundColor Blue +Write-Host "[VALIDATE] Checking CLI schema and agent skills..." -ForegroundColor Blue Write-Host "CLI path: $CliPath" -ForegroundColor Gray # Check if doc files exist @@ -47,12 +46,6 @@ if (-not (Test-Path $SchemaPath)) { exit 0 } -if (-not (Test-Path $ContextPath)) { - Write-Host "::error::docs/llm-context.md not found. Run 'scripts/build-cli.ps1' to build CLI and generate docs." -ForegroundColor Red - if ($FailOnDrift) { exit 1 } - exit 0 -} - # Generate fresh schema and compare Write-Host "[VALIDATE] Generating fresh schema from CLI..." -ForegroundColor Blue $FreshSchemaLines = & $CliPath --cli-schema @@ -142,8 +135,8 @@ if ($SchemaDrift) { Write-Host "[VALIDATE] docs/cli-schema.json is up-to-date" -ForegroundColor Green } -# For llm-context.md, regenerate to a temp location and compare -Write-Host "[VALIDATE] Checking llm-context.md..." -ForegroundColor Blue +# Validate agent skills by regenerating to a temp location and comparing +Write-Host "[VALIDATE] Checking agent skills..." -ForegroundColor Blue $TempDocsPath = Join-Path ([System.IO.Path]::GetTempPath()) "winapp-llm-docs-validate" if (Test-Path $TempDocsPath) { Remove-Item $TempDocsPath -Recurse -Force @@ -151,73 +144,44 @@ if (Test-Path $TempDocsPath) { New-Item -ItemType Directory -Path $TempDocsPath -Force | Out-Null try { - # Generate to temp location + # Generate to temp location (docs + skills) $GenerateScript = Join-Path $PSScriptRoot "generate-llm-docs.ps1" - & $GenerateScript -CliPath $CliPath -DocsPath $TempDocsPath | Out-Null + $TempSkills = Join-Path $TempDocsPath "skills\winapp-cli" + & $GenerateScript -CliPath $CliPath -DocsPath $TempDocsPath -SkillsPath $TempSkills | Out-Null - # Compare llm-context.md with line ending normalization - $FreshContext = Get-Content (Join-Path $TempDocsPath "llm-context.md") -Raw - $CommittedContext = Get-Content $ContextPath -Raw + $SkillNames = @("setup", "package", "identity", "signing", "manifest", "troubleshoot", "frameworks") + $SkillsDrift = $false - $FreshContextNormalized = $FreshContext -replace "`r`n", "`n" - $CommittedContextNormalized = $CommittedContext -replace "`r`n", "`n" - - if ($FreshContextNormalized -ne $CommittedContextNormalized) { - Write-Host "::error::docs/llm-context.md is out of sync with CLI schema!" -ForegroundColor Red - Write-Host "" - Write-Host "Run 'scripts/build-cli.ps1' locally to rebuild CLI and regenerate docs, then commit the changes." -ForegroundColor Yellow - Write-Host "" - - # Show diff details for debugging - Write-Host "[DEBUG] Showing differences:" -ForegroundColor Cyan - Write-Host " Fresh file length: $($FreshContextNormalized.Length) chars" -ForegroundColor Gray - Write-Host " Committed file length: $($CommittedContextNormalized.Length) chars" -ForegroundColor Gray + foreach ($skillName in $SkillNames) { + $FreshSkill = Join-Path $TempDocsPath "skills\winapp-cli\$skillName\SKILL.md" + $CommittedSkill = Join-Path $ProjectRoot ".github\plugin\skills\winapp-cli\$skillName\SKILL.md" - # Split into lines for comparison - $FreshLines = $FreshContextNormalized -split "`n" - $CommittedLines = $CommittedContextNormalized -split "`n" - Write-Host " Fresh file lines: $($FreshLines.Count)" -ForegroundColor Gray - Write-Host " Committed file lines: $($CommittedLines.Count)" -ForegroundColor Gray + if (-not (Test-Path $CommittedSkill)) { + Write-Host "::error::skill '$skillName' not found at $CommittedSkill" -ForegroundColor Red + $SkillsDrift = $true + continue + } - # Find first differing line - $MaxLines = [Math]::Max($FreshLines.Count, $CommittedLines.Count) - $DiffCount = 0 - $MaxDiffsToShow = 10 - for ($i = 0; $i -lt $MaxLines -and $DiffCount -lt $MaxDiffsToShow; $i++) { - $FreshLine = if ($i -lt $FreshLines.Count) { $FreshLines[$i] } else { "" } - $CommittedLine = if ($i -lt $CommittedLines.Count) { $CommittedLines[$i] } else { "" } + if (Test-Path $FreshSkill) { + $freshContent = (Get-Content $FreshSkill -Raw) -replace "`r`n", "`n" + $committedContent = (Get-Content $CommittedSkill -Raw) -replace "`r`n", "`n" - if ($FreshLine -ne $CommittedLine) { - $DiffCount++ - Write-Host "" - Write-Host " [Line $($i + 1)] Difference #$DiffCount" -ForegroundColor Yellow - Write-Host " Expected (fresh): $($FreshLine.Substring(0, [Math]::Min(120, $FreshLine.Length)))$(if ($FreshLine.Length -gt 120) { '...' })" -ForegroundColor Green - Write-Host " Actual (committed): $($CommittedLine.Substring(0, [Math]::Min(120, $CommittedLine.Length)))$(if ($CommittedLine.Length -gt 120) { '...' })" -ForegroundColor Red + if ($freshContent -ne $committedContent) { + Write-Host "::error::skill '$skillName' is out of sync!" -ForegroundColor Red + $SkillsDrift = $true } } - - if ($DiffCount -eq 0) { - Write-Host " [DEBUG] No line-by-line differences found, but content differs (possible whitespace/encoding issue)" -ForegroundColor Yellow - # Check for BOM or other encoding differences - $FreshBytes = [System.Text.Encoding]::UTF8.GetBytes($FreshContextNormalized.Substring(0, [Math]::Min(100, $FreshContextNormalized.Length))) - $CommittedBytes = [System.Text.Encoding]::UTF8.GetBytes($CommittedContextNormalized.Substring(0, [Math]::Min(100, $CommittedContextNormalized.Length))) - Write-Host " Fresh first bytes: $($FreshBytes[0..9] -join ', ')" -ForegroundColor Gray - Write-Host " Committed first bytes: $($CommittedBytes[0..9] -join ', ')" -ForegroundColor Gray - } elseif ($DiffCount -ge $MaxDiffsToShow) { - $TotalDiffs = ($FreshLines | ForEach-Object -Begin { $idx = 0 } -Process { - if ($idx -lt $CommittedLines.Count -and $_ -ne $CommittedLines[$idx]) { 1 } - $idx++ - } | Measure-Object -Sum).Sum - Write-Host "" - Write-Host " ... and more differences (showing first $MaxDiffsToShow of ~$TotalDiffs)" -ForegroundColor Yellow - } + } + + if ($SkillsDrift) { + Write-Host "" + Write-Host "Run 'scripts/build-cli.ps1' locally to rebuild CLI and regenerate all docs/skills, then commit." -ForegroundColor Yellow Write-Host "" - if ($FailOnDrift) { exit 1 } } else { - Write-Host "[VALIDATE] docs/llm-context.md is up-to-date" -ForegroundColor Green + Write-Host "[VALIDATE] Agent skills are up-to-date" -ForegroundColor Green } } finally { @@ -226,7 +190,7 @@ finally { } } -Write-Host "[VALIDATE] LLM documentation is up-to-date!" -ForegroundColor Green +Write-Host "[VALIDATE] CLI schema and agent skills are up-to-date!" -ForegroundColor Green # Warn about potential stale artifacts Write-Host "" diff --git a/src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs new file mode 100644 index 00000000..0da578da --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs @@ -0,0 +1,226 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using WinApp.Cli.Services; + +namespace WinApp.Cli.Tests; + +[TestClass] +public class AgentContextServiceTests : BaseCommandTests +{ + public AgentContextServiceTests() : base(configPaths: false, verboseLogging: true) { } + + private IAgentContextService _agentContextService = null!; + + [TestInitialize] + public void Setup() + { + _agentContextService = GetRequiredService(); + } + + [TestMethod] + public async Task GenerateSkills_CreatesDefaultSkillsDirectory_WhenNoneExist() + { + // Arrange - temp directory with no existing skills dirs + + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); + + // Assert + Assert.IsTrue(result.Success || result.GeneratedSkills.Count == 0, + "Should succeed or have no embedded skills (when running in test context without embedded resources)"); + + // If skills were generated, verify directory was created + if (result.Success) + { + var expectedDir = Path.Combine(_tempDirectory.FullName, ".github", "skills", "winapp-cli"); + Assert.IsTrue(Directory.Exists(expectedDir), "Should create .github/skills/winapp-cli/"); + Assert.IsNotEmpty(result.GeneratedSkills, "Should generate at least one skill"); + } + } + + [TestMethod] + public async Task GenerateSkills_UsesExistingGitHubSkillsDir() + { + // Arrange + var githubSkillsDir = Path.Combine(_tempDirectory.FullName, ".github", "skills"); + Directory.CreateDirectory(githubSkillsDir); + + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); + + // Assert + if (result.Success) + { + StringAssert.Contains(result.SkillsDirectory, ".github/skills/winapp-cli", + "Should use existing .github/skills/ directory"); + } + } + + [TestMethod] + public async Task GenerateSkills_UsesExistingAgentsSkillsDir() + { + // Arrange + var agentsSkillsDir = Path.Combine(_tempDirectory.FullName, ".agents", "skills"); + Directory.CreateDirectory(agentsSkillsDir); + + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); + + // Assert + if (result.Success) + { + StringAssert.Contains(result.SkillsDirectory, ".agents/skills/winapp-cli", + "Should use existing .agents/skills/ directory"); + } + } + + [TestMethod] + public async Task GenerateSkills_UsesExistingClaudeSkillsDir() + { + // Arrange + var claudeSkillsDir = Path.Combine(_tempDirectory.FullName, ".claude", "skills"); + Directory.CreateDirectory(claudeSkillsDir); + + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); + + // Assert + if (result.Success) + { + StringAssert.Contains(result.SkillsDirectory, ".claude/skills/winapp-cli", + "Should use existing .claude/skills/ directory"); + } + } + + [TestMethod] + public async Task GenerateSkills_PrefersGitHubOverClaude() + { + // Arrange — both .github/skills/ and .claude/skills/ exist + Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".github", "skills")); + Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".claude", "skills")); + + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); + + // Assert + if (result.Success) + { + StringAssert.Contains(result.SkillsDirectory, ".github/skills/winapp-cli", + "Should prefer .github/skills/ over .claude/skills/"); + } + } + + [TestMethod] + public async Task GenerateSkills_UsesExplicitSkillsDir() + { + // Arrange + var explicitDir = _tempDirectory.CreateSubdirectory("my-custom-skills"); + + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: explicitDir, CancellationToken.None); + + // Assert + if (result.Success) + { + StringAssert.Contains(result.SkillsDirectory, "my-custom-skills", + "Should use explicit --skills-dir override"); + } + } + + [TestMethod] + public async Task GenerateSkills_OverwritesExistingSkillFiles() + { + // Arrange — create a pre-existing SKILL.md + var skillDir = Path.Combine(_tempDirectory.FullName, ".github", "skills", "winapp-cli", "setup"); + Directory.CreateDirectory(skillDir); + var skillFile = Path.Combine(skillDir, "SKILL.md"); + await File.WriteAllTextAsync(skillFile, "old content"); + + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); + + // Assert + if (result.Success && result.GeneratedSkills.Contains("setup")) + { + var newContent = await File.ReadAllTextAsync(skillFile); + Assert.AreNotEqual("old content", newContent, "Should overwrite existing skill files"); + } + } + + [TestMethod] + public async Task GenerateSkills_ReturnsCorrectSkillCount() + { + // Act + var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); + + // Assert — either we have embedded skills (from a built binary) or we don't (test environment) + if (result.Success) + { + // The spec defines 7 skills: setup, package, identity, signing, manifest, troubleshoot, frameworks + Assert.HasCount(7, result.GeneratedSkills); + } + } + + [TestMethod] + public void SkillsExistInProject_ReturnsFalse_WhenNoSkillsExist() + { + // Act + var exists = _agentContextService.SkillsExistInProject(_tempDirectory); + + // Assert + Assert.IsFalse(exists, "Should return false when no skills directories exist"); + } + + [TestMethod] + public void SkillsExistInProject_ReturnsTrue_WhenGitHubSkillsExist() + { + // Arrange + Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".github", "skills", "winapp-cli")); + + // Act + var exists = _agentContextService.SkillsExistInProject(_tempDirectory); + + // Assert + Assert.IsTrue(exists, "Should return true when .github/skills/winapp-cli/ exists"); + } + + [TestMethod] + public void SkillsExistInProject_ReturnsTrue_WhenAgentsSkillsExist() + { + // Arrange + Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".agents", "skills", "winapp-cli")); + + // Act + var exists = _agentContextService.SkillsExistInProject(_tempDirectory); + + // Assert + Assert.IsTrue(exists, "Should return true when .agents/skills/winapp-cli/ exists"); + } + + [TestMethod] + public void SkillsExistInProject_ReturnsTrue_WhenClaudeSkillsExist() + { + // Arrange + Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".claude", "skills", "winapp-cli")); + + // Act + var exists = _agentContextService.SkillsExistInProject(_tempDirectory); + + // Assert + Assert.IsTrue(exists, "Should return true when .claude/skills/winapp-cli/ exists"); + } + + [TestMethod] + public void SkillsExistInProject_ReturnsFalse_WhenSkillsDirExistsButNoWinappCli() + { + // Arrange — .github/skills/ exists but no winapp-cli subfolder + Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".github", "skills")); + + // Act + var exists = _agentContextService.SkillsExistInProject(_tempDirectory); + + // Assert + Assert.IsFalse(exists, "Should return false when skills dir exists but winapp-cli subfolder doesn't"); + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs new file mode 100644 index 00000000..14078e6c --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.CommandLine; + +namespace WinApp.Cli.Commands; + +internal class AgentsCommand : Command, IShortDescription +{ + public string ShortDescription => "Generate AI agent skill files for coding assistants"; + + public AgentsCommand(AgentsGenerateCommand agentsGenerateCommand) + : base("agents", "Manage AI agent context for coding assistants (GitHub Copilot, Claude Code, etc.). Use 'agents generate' to create SKILL.md files that help AI agents understand your winapp project.") + { + Subcommands.Add(agentsGenerateCommand); + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs new file mode 100644 index 00000000..a3969f2b --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.CommandLine; +using System.CommandLine.Invocation; +using WinApp.Cli.Helpers; +using WinApp.Cli.Services; + +namespace WinApp.Cli.Commands; + +internal class AgentsGenerateCommand : Command, IShortDescription +{ + public string ShortDescription => "Generate SKILL.md files for AI coding agents"; + + public static Option SkillsDirOption { get; } + public static Option DirectoryOption { get; } + + static AgentsGenerateCommand() + { + SkillsDirOption = new Option("--skills-dir") + { + Description = "Skills directory override (default: auto-detect from .github/skills, .agents/skills, or .claude/skills)" + }; + + DirectoryOption = new Option("--directory") + { + Description = "Project root directory (default: current directory)" + }; + DirectoryOption.AcceptExistingOnly(); + } + + public AgentsGenerateCommand() + : base("generate", "Generate SKILL.md files that help AI coding agents (GitHub Copilot, Claude Code, etc.) understand your winapp project. Skills are placed in the detected skills directory (.github/skills/, .agents/skills/, or .claude/skills/). Use --skills-dir to override.") + { + Options.Add(SkillsDirOption); + Options.Add(DirectoryOption); + } + + public class Handler( + IAgentContextService agentContextService, + ICurrentDirectoryProvider currentDirectoryProvider, + IStatusService statusService) : AsynchronousCommandLineAction + { + public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken = default) + { + var skillsDir = parseResult.GetValue(SkillsDirOption); + var directory = parseResult.GetValue(DirectoryOption) ?? currentDirectoryProvider.GetCurrentDirectoryInfo(); + + return await statusService.ExecuteWithStatusAsync( + "Generating winapp agent skills...", + async (context, ct) => + { + var result = await agentContextService.GenerateSkillsAsync(directory, skillsDir, ct); + + if (!result.Success) + { + return (1, "Failed to generate agent skills."); + } + + foreach (var skill in result.GeneratedSkills) + { + context.AddStatusMessage($"{UiSymbols.Check} {skill}"); + } + + var summary = $"{result.GeneratedSkills.Count} skills generated in [underline]{result.SkillsDirectory}[/]"; + return (0, summary); + }, + cancellationToken); + } + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs index 8c9bc09e..f79724ac 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs @@ -18,6 +18,7 @@ internal class InitCommand : Command, IShortDescription public static Option NoGitignoreOption { get; } public static Option UseDefaults { get; } public static Option ConfigOnlyOption { get; } + public static Option NoSkillsOption { get; } static InitCommand() { @@ -53,6 +54,10 @@ static InitCommand() { Description = "Only handle configuration file operations (create if missing, validate if exists). Skip package installation and other workspace setup steps." }; + NoSkillsOption = new Option("--no-skills") + { + Description = "Don't generate AI agent skill files (for Copilot, Claude, etc.)" + }; } public InitCommand() : base("init", "Start here for initializing a Windows app with required setup. Sets up everything needed for Windows app development: creates appxmanifest.xml with default assets, creates winapp.yaml for version management, and downloads Windows SDK and Windows App SDK packages and generates projections. Interactive by default (use --use-defaults to skip prompts). Use 'restore' instead if you cloned a repo that already has winapp.yaml. Use 'manifest generate' if you only need a manifest, or 'cert generate' if you need a development certificate for code signing.") @@ -64,6 +69,7 @@ static InitCommand() Options.Add(NoGitignoreOption); Options.Add(UseDefaults); Options.Add(ConfigOnlyOption); + Options.Add(NoSkillsOption); } public class Handler(IWorkspaceSetupService workspaceSetupService, ICurrentDirectoryProvider currentDirectoryProvider) : AsynchronousCommandLineAction @@ -77,6 +83,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio var noGitignore = parseResult.GetValue(NoGitignoreOption); var useDefaults = parseResult.GetValue(UseDefaults); var configOnly = parseResult.GetValue(ConfigOnlyOption); + var noSkills = parseResult.GetValue(NoSkillsOption); var options = new WorkspaceSetupOptions { @@ -88,7 +95,8 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio UseDefaults = useDefaults, RequireExistingConfig = false, ForceLatestBuildTools = true, - ConfigOnly = configOnly + ConfigOnly = configOnly, + NoSkills = noSkills }; return await workspaceSetupService.SetupWorkspaceAsync(options, cancellationToken); diff --git a/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs index a5e13009..c5e8e155 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs @@ -54,6 +54,7 @@ public WinAppRootCommand( SignCommand signCommand, ToolCommand toolCommand, MSStoreCommand msStoreCommand, + AgentsCommand agentsCommand, IAnsiConsole ansiConsole, CreateExternalCatalogCommand createExternalCatalogCommand) : base("CLI for Windows app development, including package identity, packaging, managing appxmanifest.xml, test certificates, Windows (App) SDK projections, and more. For use with any app framework targeting Windows") { @@ -68,6 +69,7 @@ public WinAppRootCommand( Subcommands.Add(signCommand); Subcommands.Add(toolCommand); Subcommands.Add(msStoreCommand); + Subcommands.Add(agentsCommand); Subcommands.Add(createExternalCatalogCommand); Options.Add(CliSchemaOption); @@ -77,7 +79,7 @@ public WinAppRootCommand( helpOption.Action = new CustomHelpAction(this, ansiConsole, ("Setup", [typeof(InitCommand), typeof(RestoreCommand), typeof(UpdateCommand)]), ("Packaging & Signing", [typeof(PackageCommand), typeof(SignCommand), typeof(CertCommand), typeof(ManifestCommand), typeof(CreateExternalCatalogCommand)]), - ("Development Tools", [typeof(CreateDebugIdentityCommand), typeof(MSStoreCommand), typeof(ToolCommand), typeof(GetWinappPathCommand)]) + ("Development Tools", [typeof(CreateDebugIdentityCommand), typeof(AgentsCommand), typeof(MSStoreCommand), typeof(ToolCommand), typeof(GetWinappPathCommand)]) ); } } diff --git a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs index 7c46df9b..6346c756 100644 --- a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs +++ b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs @@ -36,6 +36,7 @@ public static IServiceCollection ConfigureServices(this IServiceCollection servi .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton(AnsiConsole.Console) .AddSingleton() @@ -61,7 +62,9 @@ public static IServiceCollection ConfigureCommands(this IServiceCollection servi .UseCommandHandler() .UseCommandHandler() .UseCommandHandler(false) - .UseCommandHandler(); + .UseCommandHandler() + .ConfigureCommand() + .UseCommandHandler(); } public static IServiceCollection UseCommandHandler<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommand, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler>(this IServiceCollection services, bool addDefaultOptions = true) diff --git a/src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs b/src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs new file mode 100644 index 00000000..df26914b --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Reflection; +using Microsoft.Extensions.Logging; + +namespace WinApp.Cli.Services; + +/// +/// Service for generating agent context skill files in user projects. +/// Reads SKILL.md files embedded in the CLI binary and writes them to the +/// appropriate skills directory following a silent fallback chain. +/// +internal class AgentContextService(ILogger logger) : IAgentContextService +{ + /// + /// Prefix for embedded skill resource names in the assembly. + /// + private const string EmbeddedSkillPrefix = "WinApp.Cli.Templates.AgentContext.skills.winapp_cli."; + + /// + /// The subdirectory name under the skills directory where winapp skills are placed. + /// + private const string WinAppSkillsDirName = "winapp-cli"; + + /// + /// Ordered list of skills directory candidates for fallback resolution. + /// The first existing directory wins; if none exist, the first entry is created. + /// + private static readonly string[] SkillsDirCandidates = + [ + Path.Combine(".github", "skills"), + Path.Combine(".agents", "skills"), + Path.Combine(".claude", "skills"), + ]; + + /// + public bool SkillsExistInProject(DirectoryInfo projectDirectory) + { + foreach (var candidate in SkillsDirCandidates) + { + var winappSkillsPath = Path.Combine(projectDirectory.FullName, candidate, WinAppSkillsDirName); + if (Directory.Exists(winappSkillsPath)) + { + return true; + } + } + + return false; + } + + /// + public async Task GenerateSkillsAsync( + DirectoryInfo projectDirectory, + DirectoryInfo? skillsDir, + CancellationToken cancellationToken) + { + // Resolve skills directory + var resolvedSkillsDir = ResolveSkillsDirectory(projectDirectory, skillsDir); + var winappSkillsDir = Path.Combine(resolvedSkillsDir, WinAppSkillsDirName); + + logger.LogDebug("Skills directory: {SkillsDir}", resolvedSkillsDir); + + // Find all embedded skill resources + var assembly = Assembly.GetExecutingAssembly(); + var skillResources = assembly.GetManifestResourceNames() + .Where(n => n.StartsWith(EmbeddedSkillPrefix, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (skillResources.Count == 0) + { + logger.LogWarning("No embedded skill files found in CLI binary."); + return new AgentContextResult(resolvedSkillsDir, [], false); + } + + var generatedSkills = new List(); + + foreach (var resourceName in skillResources) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Parse skill name from resource name + // Resource name format: WinApp.Cli.Templates.AgentContext.skills.winapp_cli..SKILL.md + var relativePath = resourceName[EmbeddedSkillPrefix.Length..]; + var skillName = ExtractSkillName(relativePath); + + if (string.IsNullOrEmpty(skillName)) + { + logger.LogDebug("Skipping unrecognized resource: {Resource}", resourceName); + continue; + } + + // Create skill directory and write file + var skillDir = Path.Combine(winappSkillsDir, skillName); + Directory.CreateDirectory(skillDir); + + var outputPath = Path.Combine(skillDir, "SKILL.md"); + + await using var stream = assembly.GetManifestResourceStream(resourceName); + if (stream is null) + { + logger.LogWarning("Could not read embedded resource: {Resource}", resourceName); + continue; + } + + await using var fileStream = File.Create(outputPath); + await stream.CopyToAsync(fileStream, cancellationToken); + + generatedSkills.Add(skillName); + logger.LogDebug(" {SkillName} - created", skillName); + } + + var relativeSkillsDir = Path.GetRelativePath(projectDirectory.FullName, Path.Combine(resolvedSkillsDir, WinAppSkillsDirName)); + + return new AgentContextResult( + relativeSkillsDir.Replace('\\', '/') + "/", + generatedSkills, + generatedSkills.Count > 0); + } + + /// + /// Resolve skills directory using the silent fallback chain: + /// 1. Explicit override (--skills-dir) + /// 2. .github/skills/ (if exists) + /// 3. .agents/skills/ (if exists) + /// 4. .claude/skills/ (if exists) + /// 5. None exist → create .github/skills/ + /// + private static string ResolveSkillsDirectory(DirectoryInfo projectDirectory, DirectoryInfo? explicitSkillsDir) + { + if (explicitSkillsDir is not null) + { + return explicitSkillsDir.FullName; + } + + foreach (var candidate in SkillsDirCandidates) + { + var candidatePath = Path.Combine(projectDirectory.FullName, candidate); + if (Directory.Exists(candidatePath)) + { + return candidatePath; + } + } + + // Default: create .github/skills/ + var defaultPath = Path.Combine(projectDirectory.FullName, SkillsDirCandidates[0]); + Directory.CreateDirectory(defaultPath); + return defaultPath; + } + + /// + /// Extract the skill name from the embedded resource relative path. + /// E.g., "setup.SKILL.md" → "setup", "package.SKILL.md" → "package" + /// + private static string? ExtractSkillName(string relativePath) + { + // Expected format: .SKILL.md + const string suffix = ".SKILL.md"; + if (relativePath.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) + { + return relativePath[..^suffix.Length]; + } + + return null; + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs b/src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs new file mode 100644 index 00000000..a1c94408 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +namespace WinApp.Cli.Services; + +/// +/// Service for generating agent context skill files in user projects. +/// Reads embedded SKILL.md files and writes them to the appropriate skills directory. +/// +internal interface IAgentContextService +{ + /// + /// Generate agent skill files in the target project directory. + /// + /// Root directory of the user's project + /// Optional explicit skills directory override + /// Cancellation token + /// Result containing the skills directory used and skills generated + Task GenerateSkillsAsync(DirectoryInfo projectDirectory, DirectoryInfo? skillsDir, CancellationToken cancellationToken); + + /// + /// Check whether winapp skills already exist in the project. + /// Returns true if any known skills directory contains a winapp-cli subfolder. + /// + bool SkillsExistInProject(DirectoryInfo projectDirectory); +} + +/// +/// Result of agent skills generation +/// +internal record AgentContextResult( + string SkillsDirectory, + IReadOnlyList GeneratedSkills, + bool Success); diff --git a/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs b/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs index ee89a6ff..103e1624 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs @@ -24,6 +24,7 @@ internal class WorkspaceSetupOptions public bool RequireExistingConfig { get; set; } public bool ForceLatestBuildTools { get; set; } public bool ConfigOnly { get; set; } + public bool NoSkills { get; set; } } /// @@ -43,6 +44,7 @@ internal class WorkspaceSetupService( IGitignoreService gitignoreService, IDirectoryPackagesService directoryPackagesService, IDotNetService dotNetService, + IAgentContextService agentContextService, IStatusService statusService, ICurrentDirectoryProvider currentDirectoryProvider, IAnsiConsole ansiConsole, @@ -81,8 +83,9 @@ public async Task SetupWorkspaceAsync(WorkspaceSetupOptions options, Cancel ManifestGenerationInfo? manifestGenerationInfo; bool shouldEnableDeveloperMode; string? recommendedTfm; + bool shouldGenerateSkills; - (var initializationResult, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm) = await InitializeConfigurationAsync(options, isDotNetProject, csprojFile, cancellationToken); + (var initializationResult, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills) = await InitializeConfigurationAsync(options, isDotNetProject, csprojFile, cancellationToken); if (initializationResult != 0) { return initializationResult; @@ -589,6 +592,30 @@ await taskContext.AddSubTaskAsync("Updating Directory.Packages.props", (taskCont }, cancellationToken); } + // Generate agent skill files (for setup only, not restore) + if (shouldGenerateSkills) + { + await taskContext.AddSubTaskAsync("Generating AI agent skills", async (taskContext, cancellationToken) => + { + try + { + var result = await agentContextService.GenerateSkillsAsync(options.BaseDirectory, skillsDir: null, cancellationToken); + if (result.Success) + { + return (0, $"Agent skills generated in [underline]{result.SkillsDirectory}[/]"); + } + taskContext.AddDebugMessage($"{UiSymbols.Note} No embedded skill files found in CLI binary"); + return (0, "Agent skills generation skipped"); + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Failed to generate agent skills: {ex.Message}"); + // Don't fail init if skills generation fails + return (0, "Agent skills generation skipped"); + } + }, cancellationToken); + } + // We're done string successMessage; if (isDotNetProject) @@ -670,7 +697,7 @@ await manifestService.GenerateManifestAsync( }, cancellationToken); } - private async Task<(int ReturnCode, WinappConfig? Config, bool HadExistingConfig, bool ShouldGenerateManifest, ManifestGenerationInfo? ManifestGenerationInfo, bool ShouldEnableDeveloperMode, string? RecommendedTfm)> InitializeConfigurationAsync(WorkspaceSetupOptions options, bool isDotNetProject, FileInfo? csprojFile, CancellationToken cancellationToken) + private async Task<(int ReturnCode, WinappConfig? Config, bool HadExistingConfig, bool ShouldGenerateManifest, ManifestGenerationInfo? ManifestGenerationInfo, bool ShouldEnableDeveloperMode, string? RecommendedTfm, bool ShouldGenerateSkills)> InitializeConfigurationAsync(WorkspaceSetupOptions options, bool isDotNetProject, FileInfo? csprojFile, CancellationToken cancellationToken) { if (!options.RequireExistingConfig && !options.ConfigOnly && options.SdkInstallMode == null && options.UseDefaults) { @@ -681,6 +708,7 @@ await manifestService.GenerateManifestAsync( var hadExistingConfig = configService.Exists(); bool shouldGenerateManifest = true; bool shouldEnableDeveloperMode = false; + bool shouldGenerateSkills = false; string? recommendedTfm = null; ManifestGenerationInfo? manifestGenerationInfo = null; WinappConfig? config = null; @@ -690,7 +718,7 @@ await manifestService.GenerateManifestAsync( { logger.LogInformation("winapp.yaml not found in {ConfigDir}", options.ConfigDir); logger.LogInformation("Run 'winapp init' to initialize a new workspace or navigate to a directory with winapp.yaml"); - return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); + return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); } // Step 2: Load or prepare configuration @@ -702,7 +730,7 @@ await manifestService.GenerateManifestAsync( { logger.LogInformation("{UISymbol} winapp.yaml found but contains no packages. Nothing to restore.", UiSymbols.Note); shouldEnableDeveloperMode = await AskShouldEnableDeveloperModeAsync(options, cancellationToken); - return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); + return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); } var operation = options.RequireExistingConfig ? "Found" : "Found existing"; @@ -761,7 +789,7 @@ await manifestService.GenerateManifestAsync( if (dotNetService.IsMultiTargeted(csprojFile)) { logger.LogError("The project '{CsprojFile}' uses multi-targeting (TargetFrameworks). winapp init does not support multi-targeted projects.", csprojFile.Name); - return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); + return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); } var currentTfm = dotNetService.GetTargetFramework(csprojFile); @@ -788,7 +816,7 @@ await manifestService.GenerateManifestAsync( if (options.SdkInstallMode != SdkInstallMode.None) { logger.LogError("TargetFramework '{Tfm}' is not supported for Windows App SDK. Cannot continue.", currentDisplay); - return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); + return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); } // Not installing SDKs, so TFM update is not required — skip it @@ -812,8 +840,9 @@ await manifestService.GenerateManifestAsync( } shouldEnableDeveloperMode = await AskShouldEnableDeveloperModeAsync(options, cancellationToken); + shouldGenerateSkills = await AskShouldGenerateSkillsAsync(options, cancellationToken); - return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); + return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); } private async Task PromptForManifestInfoAsync(WorkspaceSetupOptions options, CancellationToken cancellationToken) @@ -848,6 +877,31 @@ private async Task AskShouldEnableDeveloperModeAsync(WorkspaceSetupOptions cancellationToken); } + private async Task AskShouldGenerateSkillsAsync(WorkspaceSetupOptions options, CancellationToken cancellationToken) + { + // Only for init (not restore), and only if --no-skills and --config-only are not set + if (options.RequireExistingConfig || options.NoSkills || options.ConfigOnly) + { + return false; + } + + // If skills already exist, silently update them + if (agentContextService.SkillsExistInProject(options.BaseDirectory)) + { + return true; + } + + // No existing skills — ask the user (default to yes with --use-defaults) + if (options.UseDefaults) + { + return true; + } + + return await ansiConsole.PromptAsync( + new ConfirmationPrompt("Generate AI agent skill files (for Copilot, Claude, etc.)?"), + cancellationToken); + } + private async Task AskShouldGenerateManifestAsync(WorkspaceSetupOptions options, CancellationToken cancellationToken) { if (options.RequireExistingConfig) diff --git a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj index bc98d6d3..0b35523b 100644 --- a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj +++ b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj @@ -44,6 +44,10 @@ + + + diff --git a/src/winapp-npm/README.md b/src/winapp-npm/README.md index f9e8ef78..003c9162 100644 --- a/src/winapp-npm/README.md +++ b/src/winapp-npm/README.md @@ -59,6 +59,10 @@ npx winapp --help - [`tool`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#tool) - Access Windows SDK tools - [`get-winapp-path`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#get-winapp-path) - Get paths to installed SDK components +**AI Agent Integration:** + +- [`agents generate`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#agents-generate) - Generate AI agent skill files for coding assistants (Copilot, Claude, Cursor) + **Node.js/Electron Specific:** - [`node create-addon`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#node-create-addon) - Generate native C# or C++ addons From 9426f851d3585fba680e86e6af7538afc7e6d2b5 Mon Sep 17 00:00:00 2001 From: Nikola Metulev Date: Fri, 27 Feb 2026 16:37:27 -0800 Subject: [PATCH 2/7] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/plugin/skills/winapp-cli/identity/SKILL.md | 8 ++++---- AGENTS.md | 2 +- docs/fragments/skills/winapp-cli/identity.md | 1 + scripts/validate-llm-docs.ps1 | 3 +++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/plugin/skills/winapp-cli/identity/SKILL.md b/.github/plugin/skills/winapp-cli/identity/SKILL.md index 32cacbc5..556e3ff0 100644 --- a/.github/plugin/skills/winapp-cli/identity/SKILL.md +++ b/.github/plugin/skills/winapp-cli/identity/SKILL.md @@ -67,10 +67,10 @@ After running, launch your exe normally — Windows will recognize it as having ## Recommended workflow 1. **Setup** — `winapp init --use-defaults` (creates `appxmanifest.xml`) -3. **Build** your app -4. **Register identity** — `winapp create-debug-identity ./bin/myapp.exe` -5. **Run** your app — identity-requiring APIs now work -6. **Re-run step 4** whenever you change `appxmanifest.xml` or `Assets/` +2. **Build** your app +3. **Register identity** — `winapp create-debug-identity ./bin/myapp.exe` +4. **Run** your app — identity-requiring APIs now work +5. **Re-run step 4** whenever you change `appxmanifest.xml` or `Assets/` ## Tips diff --git a/AGENTS.md b/AGENTS.md index aa908565..be2374ac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,7 +45,7 @@ When adding or changing public facing features, ensure all documentation is also (.github\plugin\skills are autogenerated from docs\fragments\skills) -If a feature is big enough and requires it's own docs page, add it under docs\ +If a feature is big enough and requires its own docs page, add it under docs\ ## Where to look first diff --git a/docs/fragments/skills/winapp-cli/identity.md b/docs/fragments/skills/winapp-cli/identity.md index ac553c8a..e22218e4 100644 --- a/docs/fragments/skills/winapp-cli/identity.md +++ b/docs/fragments/skills/winapp-cli/identity.md @@ -62,6 +62,7 @@ After running, launch your exe normally — Windows will recognize it as having ## Recommended workflow 1. **Setup** — `winapp init --use-defaults` (creates `appxmanifest.xml`) +2. **Generate development certificate** — `winapp cert generate` 3. **Build** your app 4. **Register identity** — `winapp create-debug-identity ./bin/myapp.exe` 5. **Run** your app — identity-requiring APIs now work diff --git a/scripts/validate-llm-docs.ps1 b/scripts/validate-llm-docs.ps1 index 71949ecc..ccac8fc9 100644 --- a/scripts/validate-llm-docs.ps1 +++ b/scripts/validate-llm-docs.ps1 @@ -170,6 +170,9 @@ try { Write-Host "::error::skill '$skillName' is out of sync!" -ForegroundColor Red $SkillsDrift = $true } + } else { + Write-Host "::error::freshly generated skill '$skillName' not found at $FreshSkill (generation or template issue?)" -ForegroundColor Red + $SkillsDrift = $true } } From 5779c50148acef4231465ad4b5d6feaa4102ed7a Mon Sep 17 00:00:00 2001 From: Nikola Metulev Date: Mon, 2 Mar 2026 11:03:36 -0800 Subject: [PATCH 3/7] fixing skills --- .github/plugin/agents/winapp.agent.md | 4 +- .github/plugin/plugin.json | 51 +++++++++++++++++-- .../skills/winapp-cli/frameworks/SKILL.md | 4 +- .../skills/winapp-cli/identity/SKILL.md | 13 ++--- .../skills/winapp-cli/manifest/SKILL.md | 4 +- .../plugin/skills/winapp-cli/package/SKILL.md | 4 +- .../plugin/skills/winapp-cli/setup/SKILL.md | 4 +- .../plugin/skills/winapp-cli/signing/SKILL.md | 4 +- .../skills/winapp-cli/troubleshoot/SKILL.md | 4 +- scripts/generate-llm-docs.ps1 | 16 +++--- 10 files changed, 77 insertions(+), 31 deletions(-) diff --git a/.github/plugin/agents/winapp.agent.md b/.github/plugin/agents/winapp.agent.md index 2fdd6819..841da4f1 100644 --- a/.github/plugin/agents/winapp.agent.md +++ b/.github/plugin/agents/winapp.agent.md @@ -1,7 +1,7 @@ --- name: winapp -description: Windows app packaging expert using winapp CLI. Helps with MSIX packaging, package identity, certificates, Windows SDK and Windows App SDK setup, SDK projections (CppWinRT, .NET), and framework-specific workflows for Electron, .NET, C++, Rust, Flutter, and Tauri apps. -tools: ["execute", "edit", "read"] +description: Expert in Windows app development, packaging, and distribution. Activate for ANY task involving packaging apps for Windows, creating Windows installers (MSIX), code signing Windows apps, Windows SDK setup, Windows App SDK, Windows API access (push notifications, background tasks, share target, startup tasks), creating or editing appxmanifest.xml, generating certificates for Windows apps, or distributing apps through the Microsoft Store. Covers all app frameworks including Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Uses the winapp CLI tool. +infer: true --- You are an expert in Windows app development using the **winapp CLI** — a command-line tool for MSIX packaging, package identity, certificate management, AppxManifest authoring, and Windows SDK / Windows App SDK management. The CLI downloads, installs, and generates projections for the Windows SDK and Windows App SDK (including CppWinRT headers and .NET SDK references), so any app framework can access Windows APIs. You help developers across all major app frameworks (Electron, .NET, C++, Rust, Flutter, Tauri) build, package, and distribute Windows apps. diff --git a/.github/plugin/plugin.json b/.github/plugin/plugin.json index fad841a8..9682e329 100644 --- a/.github/plugin/plugin.json +++ b/.github/plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "winapp-cli", - "description": "Skills for Windows app development with winapp CLI, MSIX packaging, package identity, certificates, SDK projections, and more.", - "version": "0.2.0", + "description": "Windows app development, packaging, and distribution. Helps with creating Windows installers (MSIX), code signing, certificates, Windows SDK and Windows App SDK setup, package identity for Windows APIs (push notifications, background tasks, share target), appxmanifest authoring, and Microsoft Store distribution. Supports Electron, .NET, C++, Rust, Flutter, and Tauri apps.", + "version": "0.2.1", "author": { "name": "Microsoft", "url": "https://github.com/microsoft/WinAppCli" @@ -11,11 +11,56 @@ "windows", "msix", "packaging", + "installer", "identity", "electron", "winapp", "windows app sdk", - "appxmanifest.xml" + "windows sdk", + "appxmanifest.xml", + "appxmanifest", + "code signing", + "certificate", + "pfx", + "signtool", + "makeappx", + "windows store", + "microsoft store", + "windows distribution", + "windows packaging", + "windows installer", + "package identity", + "sparse package", + "push notifications", + "background tasks", + "share target", + "startup task", + "tauri", + "flutter", + "rust", + "dotnet", + "wpf", + "winforms", + "cpp", + "cppwinrt", + "windows api", + "winrt", + "uwp", + "desktop app", + "win32" + ], + "category": "windows-development", + "tags": [ + "windows", + "msix", + "packaging", + "installer", + "desktop", + "notifications", + "sdk", + "signing", + "certificate", + "distribution" ], "agents": "agents/", "skills": "skills/winapp-cli/" diff --git a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md index f8bc165b..fab112a4 100644 --- a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md +++ b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md @@ -1,7 +1,7 @@ - --- name: winapp-frameworks -description: Framework-specific guidance and links to detailed guides for Electron, .NET, C++, Rust, Flutter, and Tauri. Use when working with a specific app framework. +description: Framework-specific Windows development guidance for Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Use when packaging or adding Windows features to an Electron app, .NET desktop app, Flutter app, Tauri app, Rust app, or C++ app. +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/identity/SKILL.md b/.github/plugin/skills/winapp-cli/identity/SKILL.md index 556e3ff0..d990fde3 100644 --- a/.github/plugin/skills/winapp-cli/identity/SKILL.md +++ b/.github/plugin/skills/winapp-cli/identity/SKILL.md @@ -1,7 +1,7 @@ - --- name: winapp-identity -description: Add package identity for debugging Windows APIs that require it (push notifications, background tasks, etc.) without creating a full MSIX package. +description: Enable Windows package identity for desktop apps to access Windows APIs like push notifications, background tasks, share target, and startup tasks. Use when adding Windows notifications, background tasks, or other identity-requiring Windows features to a desktop app. +version: 0.2.1 --- ## When to use @@ -67,10 +67,11 @@ After running, launch your exe normally — Windows will recognize it as having ## Recommended workflow 1. **Setup** — `winapp init --use-defaults` (creates `appxmanifest.xml`) -2. **Build** your app -3. **Register identity** — `winapp create-debug-identity ./bin/myapp.exe` -4. **Run** your app — identity-requiring APIs now work -5. **Re-run step 4** whenever you change `appxmanifest.xml` or `Assets/` +2. **Generate development certificate** — `winapp cert generate` +3. **Build** your app +4. **Register identity** — `winapp create-debug-identity ./bin/myapp.exe` +5. **Run** your app — identity-requiring APIs now work +6. **Re-run step 4** whenever you change `appxmanifest.xml` or `Assets/` ## Tips diff --git a/.github/plugin/skills/winapp-cli/manifest/SKILL.md b/.github/plugin/skills/winapp-cli/manifest/SKILL.md index 2af71f1c..dac80cf7 100644 --- a/.github/plugin/skills/winapp-cli/manifest/SKILL.md +++ b/.github/plugin/skills/winapp-cli/manifest/SKILL.md @@ -1,7 +1,7 @@ - --- name: winapp-manifest -description: Generate and modify appxmanifest.xml files for package identity and MSIX packaging with winapp CLI. +description: Create and edit Windows app manifest files (appxmanifest.xml) that define app identity, capabilities, and visual assets. Use when creating a Windows app manifest, adding Windows capabilities, or updating app icons and assets. +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/package/SKILL.md b/.github/plugin/skills/winapp-cli/package/SKILL.md index 9c5b7d29..e8dcba2c 100644 --- a/.github/plugin/skills/winapp-cli/package/SKILL.md +++ b/.github/plugin/skills/winapp-cli/package/SKILL.md @@ -1,7 +1,7 @@ - --- name: winapp-package -description: Create an MSIX installer package from a built Windows app using winapp CLI. Use when the user needs to package, distribute, or test their Windows app as an MSIX. +description: Package a Windows app as an MSIX installer for distribution or testing. Use when creating a Windows installer, packaging an Electron/Flutter/.NET/Rust/C++/Tauri app for Windows, building an MSIX, or distributing a desktop app. +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/setup/SKILL.md b/.github/plugin/skills/winapp-cli/setup/SKILL.md index 3632bb4e..439c8220 100644 --- a/.github/plugin/skills/winapp-cli/setup/SKILL.md +++ b/.github/plugin/skills/winapp-cli/setup/SKILL.md @@ -1,7 +1,7 @@ - --- name: winapp-setup -description: Project setup with winapp CLI: init, restore, update. Use when setting up a new project, restoring after clone, or updating SDK versions. +description: Set up a Windows app project for MSIX packaging, Windows SDK access, or Windows API usage. Use when adding Windows support to an Electron, .NET, C++, Rust, Flutter, or Tauri project, or restoring SDK packages after cloning. +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/signing/SKILL.md b/.github/plugin/skills/winapp-cli/signing/SKILL.md index 299f5797..aa923e61 100644 --- a/.github/plugin/skills/winapp-cli/signing/SKILL.md +++ b/.github/plugin/skills/winapp-cli/signing/SKILL.md @@ -1,7 +1,7 @@ - --- name: winapp-signing -description: Generate, install, and manage development certificates for code signing MSIX packages and executables with winapp CLI. +description: Create and manage code signing certificates for Windows apps and MSIX packages. Use when generating a certificate, signing a Windows app or installer, or fixing certificate trust issues. +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md index d420af47..48881cd0 100644 --- a/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md +++ b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md @@ -1,7 +1,7 @@ - --- name: winapp-troubleshoot -description: Diagnose and fix common errors with winapp CLI. Use when the user encounters errors or needs help choosing the right command. +description: Diagnose and fix common Windows app packaging, signing, identity, and SDK errors. Use when encountering errors with MSIX packaging, certificate signing, Windows SDK setup, or app installation. +version: 0.2.1 --- ## When to use diff --git a/scripts/generate-llm-docs.ps1 b/scripts/generate-llm-docs.ps1 index e149a668..f96b72fe 100644 --- a/scripts/generate-llm-docs.ps1 +++ b/scripts/generate-llm-docs.ps1 @@ -233,13 +233,13 @@ function Format-CommandSections { # Generate each skill $SkillNames = @("setup", "package", "identity", "signing", "manifest", "troubleshoot", "frameworks") $SkillDescriptions = @{ - "setup" = "Project setup with winapp CLI: init, restore, update. Use when setting up a new project, restoring after clone, or updating SDK versions." - "package" = "Create an MSIX installer package from a built Windows app using winapp CLI. Use when the user needs to package, distribute, or test their Windows app as an MSIX." - "identity" = "Add package identity for debugging Windows APIs that require it (push notifications, background tasks, etc.) without creating a full MSIX package." - "signing" = "Generate, install, and manage development certificates for code signing MSIX packages and executables with winapp CLI." - "manifest" = "Generate and modify appxmanifest.xml files for package identity and MSIX packaging with winapp CLI." - "troubleshoot" = "Diagnose and fix common errors with winapp CLI. Use when the user encounters errors or needs help choosing the right command." - "frameworks" = "Framework-specific guidance and links to detailed guides for Electron, .NET, C++, Rust, Flutter, and Tauri. Use when working with a specific app framework." + "setup" = "Set up a Windows app project for MSIX packaging, Windows SDK access, or Windows API usage. Use when adding Windows support to an Electron, .NET, C++, Rust, Flutter, or Tauri project, or restoring SDK packages after cloning." + "package" = "Package a Windows app as an MSIX installer for distribution or testing. Use when creating a Windows installer, packaging an Electron/Flutter/.NET/Rust/C++/Tauri app for Windows, building an MSIX, or distributing a desktop app." + "identity" = "Enable Windows package identity for desktop apps to access Windows APIs like push notifications, background tasks, share target, and startup tasks. Use when adding Windows notifications, background tasks, or other identity-requiring Windows features to a desktop app." + "signing" = "Create and manage code signing certificates for Windows apps and MSIX packages. Use when generating a certificate, signing a Windows app or installer, or fixing certificate trust issues." + "manifest" = "Create and edit Windows app manifest files (appxmanifest.xml) that define app identity, capabilities, and visual assets. Use when creating a Windows app manifest, adding Windows capabilities, or updating app icons and assets." + "troubleshoot" = "Diagnose and fix common Windows app packaging, signing, identity, and SDK errors. Use when encountering errors with MSIX packaging, certificate signing, Windows SDK setup, or app installation." + "frameworks" = "Framework-specific Windows development guidance for Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Use when packaging or adding Windows features to an Electron app, .NET desktop app, Flutter app, Tauri app, Rust app, or C++ app." } foreach ($skillName in $SkillNames) { @@ -256,10 +256,10 @@ foreach ($skillName in $SkillNames) { # Build the SKILL.md content $skillContent = @" - --- name: winapp-$skillName description: $description +version: $CliVersion --- "@ From 894f43e65073653d50182dac2065b13e6ce9a60f Mon Sep 17 00:00:00 2001 From: Nikola Metulev <711864+nmetulev@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:26:10 -0800 Subject: [PATCH 4/7] Extract CLI agents command to separate branch Remove the winapp agents generate CLI command, --no-skills init option, AgentContextService, and related tests from this branch. These will be added in a follow-up PR (nm/agents-cli). This branch now contains only: - Copilot plugin (.github/plugin/) - Documentation updates (AGENTS.md, skill fragments, llms.txt) - Script updates (generate-llm-docs.ps1, validate-llm-docs.ps1) - Cleanup of old LLM docs (llm-context.md, using-with-llms.md, copilot-instructions.md) --- README.md | 16 +- docs/cli-schema.json | 81 ------- docs/usage.md | 36 --- llms.txt | 1 - .../AgentContextServiceTests.cs | 226 ------------------ .../WinApp.Cli/Commands/AgentsCommand.cs | 17 -- .../Commands/AgentsGenerateCommand.cs | 71 ------ .../WinApp.Cli/Commands/InitCommand.cs | 10 +- .../WinApp.Cli/Commands/WinAppRootCommand.cs | 4 +- .../Helpers/HostBuilderExtensions.cs | 5 +- .../Services/AgentContextService.cs | 166 ------------- .../Services/IAgentContextService.cs | 34 --- .../Services/WorkspaceSetupService.cs | 68 +----- src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj | 4 - src/winapp-npm/README.md | 4 - 15 files changed, 13 insertions(+), 730 deletions(-) delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs diff --git a/README.md b/README.md index 86ef3688..fa13364e 100644 --- a/README.md +++ b/README.md @@ -178,10 +178,6 @@ npx winapp --help - [`sign`](./docs/usage.md#sign) - Sign MSIX packages and executables - [`create-external-catalog`](./docs/usage.md#create-external-catalog) - Generate CodeIntegrityExternal.cat for TrustedLaunch sparse packages -**AI Agent Integration:** - -- [`agents generate`](./docs/usage.md#agents-generate) - Generate AI agent skill files for coding assistants (Copilot, Claude, Cursor) - **Development Tools:** - [`tool`](./docs/usage.md#tool) - Access Windows SDK tools @@ -214,20 +210,14 @@ This repository includes samples demonstrating how to use the CLI with various f ## 🤖 Using with AI Coding Agents -AI coding agents (GitHub Copilot, Claude Code, etc) auto-discover skill files in your project. Two ways to set this up: +AI coding agents (GitHub Copilot, Claude Code, etc) auto-discover skill files in your project. -**Option 1: GitHub Copilot CLI Plugin** (global — works across all projects) +**GitHub Copilot CLI Plugin** (global — works across all projects) ```bash copilot plugin install microsoft/WinAppCli ``` -**Option 2: Project-level skills** (checked into your repo) -```bash -winapp agents generate -``` -This creates skill files (`SKILL.md`) in your project that AI agents auto-discover. Skills are also generated automatically during `winapp init`. - -Both options give agents full understanding of winapp commands, workflows, and troubleshooting. +This gives agents full understanding of winapp commands, workflows, and troubleshooting. ## 🔧 Feedback and Support diff --git a/docs/cli-schema.json b/docs/cli-schema.json index cf845a7c..35618784 100644 --- a/docs/cli-schema.json +++ b/docs/cli-schema.json @@ -50,74 +50,6 @@ } }, "subcommands": { - "agents": { - "description": "Manage AI agent context for coding assistants (GitHub Copilot, Claude Code, etc.). Use 'agents generate' to create SKILL.md files that help AI agents understand your winapp project.", - "hidden": false, - "subcommands": { - "generate": { - "description": "Generate SKILL.md files that help AI coding agents (GitHub Copilot, Claude Code, etc.) understand your winapp project. Skills are placed in the detected skills directory (.github/skills/, .agents/skills/, or .claude/skills/). Use --skills-dir to override.", - "hidden": false, - "options": { - "--directory": { - "description": "Project root directory (default: current directory)", - "hidden": false, - "valueType": "System.IO.DirectoryInfo", - "hasDefaultValue": false, - "arity": { - "minimum": 1, - "maximum": 1 - }, - "required": false, - "recursive": false - }, - "--quiet": { - "description": "Suppress progress messages", - "hidden": false, - "aliases": [ - "-q" - ], - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - }, - "--skills-dir": { - "description": "Skills directory override (default: auto-detect from .github/skills, .agents/skills, or .claude/skills)", - "hidden": false, - "valueType": "System.IO.DirectoryInfo", - "hasDefaultValue": false, - "arity": { - "minimum": 1, - "maximum": 1 - }, - "required": false, - "recursive": false - }, - "--verbose": { - "description": "Enable verbose output", - "hidden": false, - "aliases": [ - "-v" - ], - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - } - } - } - } - }, "cert": { "description": "Manage development certificates for code signing. Use 'cert generate' to create a self-signed certificate for testing, or 'cert install' (requires elevation) to trust an existing certificate on this machine.", "hidden": false, @@ -659,19 +591,6 @@ "required": false, "recursive": false }, - "--no-skills": { - "description": "Don't generate AI agent skill files (for Copilot, Claude, etc.)", - "hidden": false, - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - }, "--quiet": { "description": "Suppress progress messages", "hidden": false, diff --git a/docs/usage.md b/docs/usage.md index f774b620..74b76ae7 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -20,7 +20,6 @@ winapp init [base-directory] [options] - `--no-gitignore` - Don't update .gitignore file - `--use-defaults`, `--no-prompt` - Do not prompt, and use default of all prompts - `--config-only` - Only handle configuration file operations, skip package installation -- `--no-skills` - Don't generate AI agent skill files (for Copilot, Claude, etc.) **What it does:** @@ -30,7 +29,6 @@ winapp init [base-directory] [options] - Creates AppxManifest.xml - Sets up build tools and enables developer mode - Updates .gitignore to exclude generated files -- Generates AI agent skill files for coding assistants (GitHub Copilot, Claude, Cursor, etc.) — prompts to confirm unless skills already exist or `--no-skills` is passed - Stores sharable files in the global cache directory **Automatic .NET project detection:** @@ -540,40 +538,6 @@ winapp store publish ./myapp.msix --appId --- -### agents generate - -Generate AI agent skill files (SKILL.md) that help coding assistants like GitHub Copilot and Claude Code understand your winapp project. - -```bash -winapp agents generate [options] -``` - -**Options:** - -- `--skills-dir ` - Skills directory override (default: auto-detect from `.github/skills`, `.agents/skills`, or `.claude/skills`) -- `--directory ` - Project root directory (default: current directory) - -**What it does:** - -- Detects existing skills directories (`.github/skills/`, `.agents/skills/`, `.claude/skills/`) or creates `.github/skills/` by default -- Writes 7 skill files covering setup, packaging, identity, signing, manifests, troubleshooting, and framework guides -- Skills are also generated automatically during `winapp init` (unless `--no-skills` is passed) - -**Examples:** - -```bash -# Generate skills in auto-detected directory -winapp agents generate - -# Specify a custom skills directory -winapp agents generate --skills-dir ./my-skills - -# Generate skills for a different project -winapp agents generate --directory ./my-project -``` - ---- - ### get-winapp-path Get paths to installed Windows SDK components. diff --git a/llms.txt b/llms.txt index 56bf1f40..b08fa03f 100644 --- a/llms.txt +++ b/llms.txt @@ -7,7 +7,6 @@ winapp helps developers add Windows-specific features (MSIX packaging, push noti ## AI Coding Agents - **Copilot CLI Plugin:** `copilot plugin install microsoft/WinAppCli` -- **Project-level skills:** `winapp agents generate` (also runs during `winapp init`) ### Copilot Plugin Structure diff --git a/src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs deleted file mode 100644 index 0da578da..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/AgentContextServiceTests.cs +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class AgentContextServiceTests : BaseCommandTests -{ - public AgentContextServiceTests() : base(configPaths: false, verboseLogging: true) { } - - private IAgentContextService _agentContextService = null!; - - [TestInitialize] - public void Setup() - { - _agentContextService = GetRequiredService(); - } - - [TestMethod] - public async Task GenerateSkills_CreatesDefaultSkillsDirectory_WhenNoneExist() - { - // Arrange - temp directory with no existing skills dirs - - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); - - // Assert - Assert.IsTrue(result.Success || result.GeneratedSkills.Count == 0, - "Should succeed or have no embedded skills (when running in test context without embedded resources)"); - - // If skills were generated, verify directory was created - if (result.Success) - { - var expectedDir = Path.Combine(_tempDirectory.FullName, ".github", "skills", "winapp-cli"); - Assert.IsTrue(Directory.Exists(expectedDir), "Should create .github/skills/winapp-cli/"); - Assert.IsNotEmpty(result.GeneratedSkills, "Should generate at least one skill"); - } - } - - [TestMethod] - public async Task GenerateSkills_UsesExistingGitHubSkillsDir() - { - // Arrange - var githubSkillsDir = Path.Combine(_tempDirectory.FullName, ".github", "skills"); - Directory.CreateDirectory(githubSkillsDir); - - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); - - // Assert - if (result.Success) - { - StringAssert.Contains(result.SkillsDirectory, ".github/skills/winapp-cli", - "Should use existing .github/skills/ directory"); - } - } - - [TestMethod] - public async Task GenerateSkills_UsesExistingAgentsSkillsDir() - { - // Arrange - var agentsSkillsDir = Path.Combine(_tempDirectory.FullName, ".agents", "skills"); - Directory.CreateDirectory(agentsSkillsDir); - - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); - - // Assert - if (result.Success) - { - StringAssert.Contains(result.SkillsDirectory, ".agents/skills/winapp-cli", - "Should use existing .agents/skills/ directory"); - } - } - - [TestMethod] - public async Task GenerateSkills_UsesExistingClaudeSkillsDir() - { - // Arrange - var claudeSkillsDir = Path.Combine(_tempDirectory.FullName, ".claude", "skills"); - Directory.CreateDirectory(claudeSkillsDir); - - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); - - // Assert - if (result.Success) - { - StringAssert.Contains(result.SkillsDirectory, ".claude/skills/winapp-cli", - "Should use existing .claude/skills/ directory"); - } - } - - [TestMethod] - public async Task GenerateSkills_PrefersGitHubOverClaude() - { - // Arrange — both .github/skills/ and .claude/skills/ exist - Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".github", "skills")); - Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".claude", "skills")); - - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); - - // Assert - if (result.Success) - { - StringAssert.Contains(result.SkillsDirectory, ".github/skills/winapp-cli", - "Should prefer .github/skills/ over .claude/skills/"); - } - } - - [TestMethod] - public async Task GenerateSkills_UsesExplicitSkillsDir() - { - // Arrange - var explicitDir = _tempDirectory.CreateSubdirectory("my-custom-skills"); - - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: explicitDir, CancellationToken.None); - - // Assert - if (result.Success) - { - StringAssert.Contains(result.SkillsDirectory, "my-custom-skills", - "Should use explicit --skills-dir override"); - } - } - - [TestMethod] - public async Task GenerateSkills_OverwritesExistingSkillFiles() - { - // Arrange — create a pre-existing SKILL.md - var skillDir = Path.Combine(_tempDirectory.FullName, ".github", "skills", "winapp-cli", "setup"); - Directory.CreateDirectory(skillDir); - var skillFile = Path.Combine(skillDir, "SKILL.md"); - await File.WriteAllTextAsync(skillFile, "old content"); - - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); - - // Assert - if (result.Success && result.GeneratedSkills.Contains("setup")) - { - var newContent = await File.ReadAllTextAsync(skillFile); - Assert.AreNotEqual("old content", newContent, "Should overwrite existing skill files"); - } - } - - [TestMethod] - public async Task GenerateSkills_ReturnsCorrectSkillCount() - { - // Act - var result = await _agentContextService.GenerateSkillsAsync(_tempDirectory, skillsDir: null, CancellationToken.None); - - // Assert — either we have embedded skills (from a built binary) or we don't (test environment) - if (result.Success) - { - // The spec defines 7 skills: setup, package, identity, signing, manifest, troubleshoot, frameworks - Assert.HasCount(7, result.GeneratedSkills); - } - } - - [TestMethod] - public void SkillsExistInProject_ReturnsFalse_WhenNoSkillsExist() - { - // Act - var exists = _agentContextService.SkillsExistInProject(_tempDirectory); - - // Assert - Assert.IsFalse(exists, "Should return false when no skills directories exist"); - } - - [TestMethod] - public void SkillsExistInProject_ReturnsTrue_WhenGitHubSkillsExist() - { - // Arrange - Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".github", "skills", "winapp-cli")); - - // Act - var exists = _agentContextService.SkillsExistInProject(_tempDirectory); - - // Assert - Assert.IsTrue(exists, "Should return true when .github/skills/winapp-cli/ exists"); - } - - [TestMethod] - public void SkillsExistInProject_ReturnsTrue_WhenAgentsSkillsExist() - { - // Arrange - Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".agents", "skills", "winapp-cli")); - - // Act - var exists = _agentContextService.SkillsExistInProject(_tempDirectory); - - // Assert - Assert.IsTrue(exists, "Should return true when .agents/skills/winapp-cli/ exists"); - } - - [TestMethod] - public void SkillsExistInProject_ReturnsTrue_WhenClaudeSkillsExist() - { - // Arrange - Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".claude", "skills", "winapp-cli")); - - // Act - var exists = _agentContextService.SkillsExistInProject(_tempDirectory); - - // Assert - Assert.IsTrue(exists, "Should return true when .claude/skills/winapp-cli/ exists"); - } - - [TestMethod] - public void SkillsExistInProject_ReturnsFalse_WhenSkillsDirExistsButNoWinappCli() - { - // Arrange — .github/skills/ exists but no winapp-cli subfolder - Directory.CreateDirectory(Path.Combine(_tempDirectory.FullName, ".github", "skills")); - - // Act - var exists = _agentContextService.SkillsExistInProject(_tempDirectory); - - // Assert - Assert.IsFalse(exists, "Should return false when skills dir exists but winapp-cli subfolder doesn't"); - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs deleted file mode 100644 index 14078e6c..00000000 --- a/src/winapp-CLI/WinApp.Cli/Commands/AgentsCommand.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.CommandLine; - -namespace WinApp.Cli.Commands; - -internal class AgentsCommand : Command, IShortDescription -{ - public string ShortDescription => "Generate AI agent skill files for coding assistants"; - - public AgentsCommand(AgentsGenerateCommand agentsGenerateCommand) - : base("agents", "Manage AI agent context for coding assistants (GitHub Copilot, Claude Code, etc.). Use 'agents generate' to create SKILL.md files that help AI agents understand your winapp project.") - { - Subcommands.Add(agentsGenerateCommand); - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs deleted file mode 100644 index a3969f2b..00000000 --- a/src/winapp-CLI/WinApp.Cli/Commands/AgentsGenerateCommand.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.CommandLine; -using System.CommandLine.Invocation; -using WinApp.Cli.Helpers; -using WinApp.Cli.Services; - -namespace WinApp.Cli.Commands; - -internal class AgentsGenerateCommand : Command, IShortDescription -{ - public string ShortDescription => "Generate SKILL.md files for AI coding agents"; - - public static Option SkillsDirOption { get; } - public static Option DirectoryOption { get; } - - static AgentsGenerateCommand() - { - SkillsDirOption = new Option("--skills-dir") - { - Description = "Skills directory override (default: auto-detect from .github/skills, .agents/skills, or .claude/skills)" - }; - - DirectoryOption = new Option("--directory") - { - Description = "Project root directory (default: current directory)" - }; - DirectoryOption.AcceptExistingOnly(); - } - - public AgentsGenerateCommand() - : base("generate", "Generate SKILL.md files that help AI coding agents (GitHub Copilot, Claude Code, etc.) understand your winapp project. Skills are placed in the detected skills directory (.github/skills/, .agents/skills/, or .claude/skills/). Use --skills-dir to override.") - { - Options.Add(SkillsDirOption); - Options.Add(DirectoryOption); - } - - public class Handler( - IAgentContextService agentContextService, - ICurrentDirectoryProvider currentDirectoryProvider, - IStatusService statusService) : AsynchronousCommandLineAction - { - public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken = default) - { - var skillsDir = parseResult.GetValue(SkillsDirOption); - var directory = parseResult.GetValue(DirectoryOption) ?? currentDirectoryProvider.GetCurrentDirectoryInfo(); - - return await statusService.ExecuteWithStatusAsync( - "Generating winapp agent skills...", - async (context, ct) => - { - var result = await agentContextService.GenerateSkillsAsync(directory, skillsDir, ct); - - if (!result.Success) - { - return (1, "Failed to generate agent skills."); - } - - foreach (var skill in result.GeneratedSkills) - { - context.AddStatusMessage($"{UiSymbols.Check} {skill}"); - } - - var summary = $"{result.GeneratedSkills.Count} skills generated in [underline]{result.SkillsDirectory}[/]"; - return (0, summary); - }, - cancellationToken); - } - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs index f79724ac..8c9bc09e 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/InitCommand.cs @@ -18,7 +18,6 @@ internal class InitCommand : Command, IShortDescription public static Option NoGitignoreOption { get; } public static Option UseDefaults { get; } public static Option ConfigOnlyOption { get; } - public static Option NoSkillsOption { get; } static InitCommand() { @@ -54,10 +53,6 @@ static InitCommand() { Description = "Only handle configuration file operations (create if missing, validate if exists). Skip package installation and other workspace setup steps." }; - NoSkillsOption = new Option("--no-skills") - { - Description = "Don't generate AI agent skill files (for Copilot, Claude, etc.)" - }; } public InitCommand() : base("init", "Start here for initializing a Windows app with required setup. Sets up everything needed for Windows app development: creates appxmanifest.xml with default assets, creates winapp.yaml for version management, and downloads Windows SDK and Windows App SDK packages and generates projections. Interactive by default (use --use-defaults to skip prompts). Use 'restore' instead if you cloned a repo that already has winapp.yaml. Use 'manifest generate' if you only need a manifest, or 'cert generate' if you need a development certificate for code signing.") @@ -69,7 +64,6 @@ static InitCommand() Options.Add(NoGitignoreOption); Options.Add(UseDefaults); Options.Add(ConfigOnlyOption); - Options.Add(NoSkillsOption); } public class Handler(IWorkspaceSetupService workspaceSetupService, ICurrentDirectoryProvider currentDirectoryProvider) : AsynchronousCommandLineAction @@ -83,7 +77,6 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio var noGitignore = parseResult.GetValue(NoGitignoreOption); var useDefaults = parseResult.GetValue(UseDefaults); var configOnly = parseResult.GetValue(ConfigOnlyOption); - var noSkills = parseResult.GetValue(NoSkillsOption); var options = new WorkspaceSetupOptions { @@ -95,8 +88,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio UseDefaults = useDefaults, RequireExistingConfig = false, ForceLatestBuildTools = true, - ConfigOnly = configOnly, - NoSkills = noSkills + ConfigOnly = configOnly }; return await workspaceSetupService.SetupWorkspaceAsync(options, cancellationToken); diff --git a/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs index c5e8e155..a5e13009 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs @@ -54,7 +54,6 @@ public WinAppRootCommand( SignCommand signCommand, ToolCommand toolCommand, MSStoreCommand msStoreCommand, - AgentsCommand agentsCommand, IAnsiConsole ansiConsole, CreateExternalCatalogCommand createExternalCatalogCommand) : base("CLI for Windows app development, including package identity, packaging, managing appxmanifest.xml, test certificates, Windows (App) SDK projections, and more. For use with any app framework targeting Windows") { @@ -69,7 +68,6 @@ public WinAppRootCommand( Subcommands.Add(signCommand); Subcommands.Add(toolCommand); Subcommands.Add(msStoreCommand); - Subcommands.Add(agentsCommand); Subcommands.Add(createExternalCatalogCommand); Options.Add(CliSchemaOption); @@ -79,7 +77,7 @@ public WinAppRootCommand( helpOption.Action = new CustomHelpAction(this, ansiConsole, ("Setup", [typeof(InitCommand), typeof(RestoreCommand), typeof(UpdateCommand)]), ("Packaging & Signing", [typeof(PackageCommand), typeof(SignCommand), typeof(CertCommand), typeof(ManifestCommand), typeof(CreateExternalCatalogCommand)]), - ("Development Tools", [typeof(CreateDebugIdentityCommand), typeof(AgentsCommand), typeof(MSStoreCommand), typeof(ToolCommand), typeof(GetWinappPathCommand)]) + ("Development Tools", [typeof(CreateDebugIdentityCommand), typeof(MSStoreCommand), typeof(ToolCommand), typeof(GetWinappPathCommand)]) ); } } diff --git a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs index 6346c756..7c46df9b 100644 --- a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs +++ b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs @@ -36,7 +36,6 @@ public static IServiceCollection ConfigureServices(this IServiceCollection servi .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton(AnsiConsole.Console) .AddSingleton() @@ -62,9 +61,7 @@ public static IServiceCollection ConfigureCommands(this IServiceCollection servi .UseCommandHandler() .UseCommandHandler() .UseCommandHandler(false) - .UseCommandHandler() - .ConfigureCommand() - .UseCommandHandler(); + .UseCommandHandler(); } public static IServiceCollection UseCommandHandler<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommand, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler>(this IServiceCollection services, bool addDefaultOptions = true) diff --git a/src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs b/src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs deleted file mode 100644 index df26914b..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/AgentContextService.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Reflection; -using Microsoft.Extensions.Logging; - -namespace WinApp.Cli.Services; - -/// -/// Service for generating agent context skill files in user projects. -/// Reads SKILL.md files embedded in the CLI binary and writes them to the -/// appropriate skills directory following a silent fallback chain. -/// -internal class AgentContextService(ILogger logger) : IAgentContextService -{ - /// - /// Prefix for embedded skill resource names in the assembly. - /// - private const string EmbeddedSkillPrefix = "WinApp.Cli.Templates.AgentContext.skills.winapp_cli."; - - /// - /// The subdirectory name under the skills directory where winapp skills are placed. - /// - private const string WinAppSkillsDirName = "winapp-cli"; - - /// - /// Ordered list of skills directory candidates for fallback resolution. - /// The first existing directory wins; if none exist, the first entry is created. - /// - private static readonly string[] SkillsDirCandidates = - [ - Path.Combine(".github", "skills"), - Path.Combine(".agents", "skills"), - Path.Combine(".claude", "skills"), - ]; - - /// - public bool SkillsExistInProject(DirectoryInfo projectDirectory) - { - foreach (var candidate in SkillsDirCandidates) - { - var winappSkillsPath = Path.Combine(projectDirectory.FullName, candidate, WinAppSkillsDirName); - if (Directory.Exists(winappSkillsPath)) - { - return true; - } - } - - return false; - } - - /// - public async Task GenerateSkillsAsync( - DirectoryInfo projectDirectory, - DirectoryInfo? skillsDir, - CancellationToken cancellationToken) - { - // Resolve skills directory - var resolvedSkillsDir = ResolveSkillsDirectory(projectDirectory, skillsDir); - var winappSkillsDir = Path.Combine(resolvedSkillsDir, WinAppSkillsDirName); - - logger.LogDebug("Skills directory: {SkillsDir}", resolvedSkillsDir); - - // Find all embedded skill resources - var assembly = Assembly.GetExecutingAssembly(); - var skillResources = assembly.GetManifestResourceNames() - .Where(n => n.StartsWith(EmbeddedSkillPrefix, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - if (skillResources.Count == 0) - { - logger.LogWarning("No embedded skill files found in CLI binary."); - return new AgentContextResult(resolvedSkillsDir, [], false); - } - - var generatedSkills = new List(); - - foreach (var resourceName in skillResources) - { - cancellationToken.ThrowIfCancellationRequested(); - - // Parse skill name from resource name - // Resource name format: WinApp.Cli.Templates.AgentContext.skills.winapp_cli..SKILL.md - var relativePath = resourceName[EmbeddedSkillPrefix.Length..]; - var skillName = ExtractSkillName(relativePath); - - if (string.IsNullOrEmpty(skillName)) - { - logger.LogDebug("Skipping unrecognized resource: {Resource}", resourceName); - continue; - } - - // Create skill directory and write file - var skillDir = Path.Combine(winappSkillsDir, skillName); - Directory.CreateDirectory(skillDir); - - var outputPath = Path.Combine(skillDir, "SKILL.md"); - - await using var stream = assembly.GetManifestResourceStream(resourceName); - if (stream is null) - { - logger.LogWarning("Could not read embedded resource: {Resource}", resourceName); - continue; - } - - await using var fileStream = File.Create(outputPath); - await stream.CopyToAsync(fileStream, cancellationToken); - - generatedSkills.Add(skillName); - logger.LogDebug(" {SkillName} - created", skillName); - } - - var relativeSkillsDir = Path.GetRelativePath(projectDirectory.FullName, Path.Combine(resolvedSkillsDir, WinAppSkillsDirName)); - - return new AgentContextResult( - relativeSkillsDir.Replace('\\', '/') + "/", - generatedSkills, - generatedSkills.Count > 0); - } - - /// - /// Resolve skills directory using the silent fallback chain: - /// 1. Explicit override (--skills-dir) - /// 2. .github/skills/ (if exists) - /// 3. .agents/skills/ (if exists) - /// 4. .claude/skills/ (if exists) - /// 5. None exist → create .github/skills/ - /// - private static string ResolveSkillsDirectory(DirectoryInfo projectDirectory, DirectoryInfo? explicitSkillsDir) - { - if (explicitSkillsDir is not null) - { - return explicitSkillsDir.FullName; - } - - foreach (var candidate in SkillsDirCandidates) - { - var candidatePath = Path.Combine(projectDirectory.FullName, candidate); - if (Directory.Exists(candidatePath)) - { - return candidatePath; - } - } - - // Default: create .github/skills/ - var defaultPath = Path.Combine(projectDirectory.FullName, SkillsDirCandidates[0]); - Directory.CreateDirectory(defaultPath); - return defaultPath; - } - - /// - /// Extract the skill name from the embedded resource relative path. - /// E.g., "setup.SKILL.md" → "setup", "package.SKILL.md" → "package" - /// - private static string? ExtractSkillName(string relativePath) - { - // Expected format: .SKILL.md - const string suffix = ".SKILL.md"; - if (relativePath.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) - { - return relativePath[..^suffix.Length]; - } - - return null; - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs b/src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs deleted file mode 100644 index a1c94408..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IAgentContextService.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Services; - -/// -/// Service for generating agent context skill files in user projects. -/// Reads embedded SKILL.md files and writes them to the appropriate skills directory. -/// -internal interface IAgentContextService -{ - /// - /// Generate agent skill files in the target project directory. - /// - /// Root directory of the user's project - /// Optional explicit skills directory override - /// Cancellation token - /// Result containing the skills directory used and skills generated - Task GenerateSkillsAsync(DirectoryInfo projectDirectory, DirectoryInfo? skillsDir, CancellationToken cancellationToken); - - /// - /// Check whether winapp skills already exist in the project. - /// Returns true if any known skills directory contains a winapp-cli subfolder. - /// - bool SkillsExistInProject(DirectoryInfo projectDirectory); -} - -/// -/// Result of agent skills generation -/// -internal record AgentContextResult( - string SkillsDirectory, - IReadOnlyList GeneratedSkills, - bool Success); diff --git a/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs b/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs index 103e1624..ee89a6ff 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs @@ -24,7 +24,6 @@ internal class WorkspaceSetupOptions public bool RequireExistingConfig { get; set; } public bool ForceLatestBuildTools { get; set; } public bool ConfigOnly { get; set; } - public bool NoSkills { get; set; } } /// @@ -44,7 +43,6 @@ internal class WorkspaceSetupService( IGitignoreService gitignoreService, IDirectoryPackagesService directoryPackagesService, IDotNetService dotNetService, - IAgentContextService agentContextService, IStatusService statusService, ICurrentDirectoryProvider currentDirectoryProvider, IAnsiConsole ansiConsole, @@ -83,9 +81,8 @@ public async Task SetupWorkspaceAsync(WorkspaceSetupOptions options, Cancel ManifestGenerationInfo? manifestGenerationInfo; bool shouldEnableDeveloperMode; string? recommendedTfm; - bool shouldGenerateSkills; - (var initializationResult, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills) = await InitializeConfigurationAsync(options, isDotNetProject, csprojFile, cancellationToken); + (var initializationResult, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm) = await InitializeConfigurationAsync(options, isDotNetProject, csprojFile, cancellationToken); if (initializationResult != 0) { return initializationResult; @@ -592,30 +589,6 @@ await taskContext.AddSubTaskAsync("Updating Directory.Packages.props", (taskCont }, cancellationToken); } - // Generate agent skill files (for setup only, not restore) - if (shouldGenerateSkills) - { - await taskContext.AddSubTaskAsync("Generating AI agent skills", async (taskContext, cancellationToken) => - { - try - { - var result = await agentContextService.GenerateSkillsAsync(options.BaseDirectory, skillsDir: null, cancellationToken); - if (result.Success) - { - return (0, $"Agent skills generated in [underline]{result.SkillsDirectory}[/]"); - } - taskContext.AddDebugMessage($"{UiSymbols.Note} No embedded skill files found in CLI binary"); - return (0, "Agent skills generation skipped"); - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Failed to generate agent skills: {ex.Message}"); - // Don't fail init if skills generation fails - return (0, "Agent skills generation skipped"); - } - }, cancellationToken); - } - // We're done string successMessage; if (isDotNetProject) @@ -697,7 +670,7 @@ await manifestService.GenerateManifestAsync( }, cancellationToken); } - private async Task<(int ReturnCode, WinappConfig? Config, bool HadExistingConfig, bool ShouldGenerateManifest, ManifestGenerationInfo? ManifestGenerationInfo, bool ShouldEnableDeveloperMode, string? RecommendedTfm, bool ShouldGenerateSkills)> InitializeConfigurationAsync(WorkspaceSetupOptions options, bool isDotNetProject, FileInfo? csprojFile, CancellationToken cancellationToken) + private async Task<(int ReturnCode, WinappConfig? Config, bool HadExistingConfig, bool ShouldGenerateManifest, ManifestGenerationInfo? ManifestGenerationInfo, bool ShouldEnableDeveloperMode, string? RecommendedTfm)> InitializeConfigurationAsync(WorkspaceSetupOptions options, bool isDotNetProject, FileInfo? csprojFile, CancellationToken cancellationToken) { if (!options.RequireExistingConfig && !options.ConfigOnly && options.SdkInstallMode == null && options.UseDefaults) { @@ -708,7 +681,6 @@ await manifestService.GenerateManifestAsync( var hadExistingConfig = configService.Exists(); bool shouldGenerateManifest = true; bool shouldEnableDeveloperMode = false; - bool shouldGenerateSkills = false; string? recommendedTfm = null; ManifestGenerationInfo? manifestGenerationInfo = null; WinappConfig? config = null; @@ -718,7 +690,7 @@ await manifestService.GenerateManifestAsync( { logger.LogInformation("winapp.yaml not found in {ConfigDir}", options.ConfigDir); logger.LogInformation("Run 'winapp init' to initialize a new workspace or navigate to a directory with winapp.yaml"); - return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); + return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); } // Step 2: Load or prepare configuration @@ -730,7 +702,7 @@ await manifestService.GenerateManifestAsync( { logger.LogInformation("{UISymbol} winapp.yaml found but contains no packages. Nothing to restore.", UiSymbols.Note); shouldEnableDeveloperMode = await AskShouldEnableDeveloperModeAsync(options, cancellationToken); - return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); + return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); } var operation = options.RequireExistingConfig ? "Found" : "Found existing"; @@ -789,7 +761,7 @@ await manifestService.GenerateManifestAsync( if (dotNetService.IsMultiTargeted(csprojFile)) { logger.LogError("The project '{CsprojFile}' uses multi-targeting (TargetFrameworks). winapp init does not support multi-targeted projects.", csprojFile.Name); - return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); + return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); } var currentTfm = dotNetService.GetTargetFramework(csprojFile); @@ -816,7 +788,7 @@ await manifestService.GenerateManifestAsync( if (options.SdkInstallMode != SdkInstallMode.None) { logger.LogError("TargetFramework '{Tfm}' is not supported for Windows App SDK. Cannot continue.", currentDisplay); - return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); + return (1, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); } // Not installing SDKs, so TFM update is not required — skip it @@ -840,9 +812,8 @@ await manifestService.GenerateManifestAsync( } shouldEnableDeveloperMode = await AskShouldEnableDeveloperModeAsync(options, cancellationToken); - shouldGenerateSkills = await AskShouldGenerateSkillsAsync(options, cancellationToken); - return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm, shouldGenerateSkills); + return (0, config, hadExistingConfig, shouldGenerateManifest, manifestGenerationInfo, shouldEnableDeveloperMode, recommendedTfm); } private async Task PromptForManifestInfoAsync(WorkspaceSetupOptions options, CancellationToken cancellationToken) @@ -877,31 +848,6 @@ private async Task AskShouldEnableDeveloperModeAsync(WorkspaceSetupOptions cancellationToken); } - private async Task AskShouldGenerateSkillsAsync(WorkspaceSetupOptions options, CancellationToken cancellationToken) - { - // Only for init (not restore), and only if --no-skills and --config-only are not set - if (options.RequireExistingConfig || options.NoSkills || options.ConfigOnly) - { - return false; - } - - // If skills already exist, silently update them - if (agentContextService.SkillsExistInProject(options.BaseDirectory)) - { - return true; - } - - // No existing skills — ask the user (default to yes with --use-defaults) - if (options.UseDefaults) - { - return true; - } - - return await ansiConsole.PromptAsync( - new ConfirmationPrompt("Generate AI agent skill files (for Copilot, Claude, etc.)?"), - cancellationToken); - } - private async Task AskShouldGenerateManifestAsync(WorkspaceSetupOptions options, CancellationToken cancellationToken) { if (options.RequireExistingConfig) diff --git a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj index 0b35523b..bc98d6d3 100644 --- a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj +++ b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj @@ -44,10 +44,6 @@ - - - diff --git a/src/winapp-npm/README.md b/src/winapp-npm/README.md index 003c9162..f9e8ef78 100644 --- a/src/winapp-npm/README.md +++ b/src/winapp-npm/README.md @@ -59,10 +59,6 @@ npx winapp --help - [`tool`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#tool) - Access Windows SDK tools - [`get-winapp-path`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#get-winapp-path) - Get paths to installed SDK components -**AI Agent Integration:** - -- [`agents generate`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#agents-generate) - Generate AI agent skill files for coding assistants (Copilot, Claude, Cursor) - **Node.js/Electron Specific:** - [`node create-addon`](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md#node-create-addon) - Generate native C# or C++ addons From 6399d3d6f9c346b7b31d95507cbb3270482171e1 Mon Sep 17 00:00:00 2001 From: Nikola Metulev <711864+nmetulev@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:43:48 -0800 Subject: [PATCH 5/7] updating skills --- .github/plugin/skills/winapp-cli/setup/SKILL.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/plugin/skills/winapp-cli/setup/SKILL.md b/.github/plugin/skills/winapp-cli/setup/SKILL.md index 439c8220..d9f1b93d 100644 --- a/.github/plugin/skills/winapp-cli/setup/SKILL.md +++ b/.github/plugin/skills/winapp-cli/setup/SKILL.md @@ -138,7 +138,6 @@ Start here for initializing a Windows app with required setup. Sets up everythin | `--config-only` | Only handle configuration file operations (create if missing, validate if exists). Skip package installation and other workspace setup steps. | (none) | | `--ignore-config` | Don't use configuration file for version management | (none) | | `--no-gitignore` | Don't update .gitignore file | (none) | -| `--no-skills` | Don't generate AI agent skill files (for Copilot, Claude, etc.) | (none) | | `--setup-sdks` | SDK installation mode: 'stable' (default), 'preview', 'experimental', or 'none' (skip SDK installation) | (none) | | `--use-defaults` | Do not prompt, and use default of all prompts | (none) | @@ -167,14 +166,3 @@ Check for and install newer SDK versions. Updates winapp.yaml with latest versio | Option | Description | Default | |--------|-------------|---------| | `--setup-sdks` | SDK installation mode: 'stable' (default), 'preview', 'experimental', or 'none' (skip SDK installation) | (none) | - -### `winapp agents generate` - -Generate SKILL.md files that help AI coding agents (GitHub Copilot, Claude Code, etc.) understand your winapp project. Skills are placed in the detected skills directory (.github/skills/, .agents/skills/, or .claude/skills/). Use --skills-dir to override. - -#### Options - -| Option | Description | Default | -|--------|-------------|---------| -| `--directory` | Project root directory (default: current directory) | (none) | -| `--skills-dir` | Skills directory override (default: auto-detect from .github/skills, .agents/skills, or .claude/skills) | (none) | From 440b7fc2853836ff40efd38692989adde0d3b5cd Mon Sep 17 00:00:00 2001 From: Nikola Metulev <711864+nmetulev@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:48:58 -0800 Subject: [PATCH 6/7] Remove agents generate and --no-skills references from docs and skills Clean up references to the CLI agents command and --no-skills init option from skill fragments, generated SKILL.md files, and the agent prompt. These features will be added back in the nm/agents-cli branch. --- .github/plugin/agents/winapp.agent.md | 13 +------------ .github/plugin/skills/winapp-cli/setup/SKILL.md | 13 ------------- .../plugin/skills/winapp-cli/troubleshoot/SKILL.md | 3 --- docs/fragments/skills/winapp-cli/setup.md | 13 ------------- docs/fragments/skills/winapp-cli/troubleshoot.md | 3 --- 5 files changed, 1 insertion(+), 44 deletions(-) diff --git a/.github/plugin/agents/winapp.agent.md b/.github/plugin/agents/winapp.agent.md index 841da4f1..72a63d81 100644 --- a/.github/plugin/agents/winapp.agent.md +++ b/.github/plugin/agents/winapp.agent.md @@ -40,8 +40,6 @@ Does the project already have an appxmanifest.xml? │ └─ winapp create-debug-identity ├─ Need to sign an existing MSIX or exe? │ └─ winapp sign - ├─ Need to update AI agent skill files? - │ └─ winapp agents generate └─ Need to run a Windows SDK tool directly (makeappx, signtool, makepri)? └─ winapp tool ``` @@ -74,8 +72,7 @@ Does the project already have an appxmanifest.xml? - `--setup-sdks stable|preview|experimental|none` — control SDK installation (default: prompts user) - `--config-only` — only create `winapp.yaml`, skip package installation - `--no-gitignore` — don't update `.gitignore` -- `--no-skills` — skip AI agent skill file generation -**Creates:** `winapp.yaml`, `appxmanifest.xml`, `Assets/` folder, `.winapp/` (if SDKs installed), `.github/skills/winapp-cli/` (unless `--no-skills`) +**Creates:** `winapp.yaml`, `appxmanifest.xml`, `Assets/` folder, `.winapp/` (if SDKs installed) ### `winapp restore [base-directory]` **Purpose:** Reinstall SDK packages from existing config without changing versions. @@ -154,14 +151,6 @@ Does the project already have an appxmanifest.xml? **Purpose:** Run Windows SDK tools directly (makeappx, signtool, makepri, etc.). **When to use:** When you need low-level SDK tool access. Auto-downloads Build Tools if needed. For most tasks, prefer higher-level commands like `package` or `sign`. -### `winapp agents generate` -**Purpose:** Generate AI agent skill files for coding assistants (GitHub Copilot, Claude, etc.). -**When to use:** To set up or refresh AI coding assistant context in a project. -**Key options:** -- `--skills-dir ` — override skills directory location -- `--directory ` — project root directory (default: cwd) -**Behavior:** Auto-detects existing skills directories (`.github/skills/`, `.agents/skills/`, `.claude/skills/`), or creates `.github/skills/winapp-cli/` if none exist. - ### `winapp get-winapp-path` **Purpose:** Print the path to the `.winapp` directory. **When to use:** In build scripts that need to reference installed package locations. diff --git a/.github/plugin/skills/winapp-cli/setup/SKILL.md b/.github/plugin/skills/winapp-cli/setup/SKILL.md index d9f1b93d..30bc3947 100644 --- a/.github/plugin/skills/winapp-cli/setup/SKILL.md +++ b/.github/plugin/skills/winapp-cli/setup/SKILL.md @@ -89,23 +89,10 @@ This updates `winapp.yaml` with the latest versions and reinstalls packages. 4. **Debug with identity** — `winapp create-debug-identity ./bin/myapp.exe` to test Windows APIs 5. **Package** — `winapp package ./bin/Release --cert ./devcert.pfx` to create MSIX -### Generate AI agent skills - -```powershell -# Generate SKILL.md files for AI coding assistants (Copilot, Claude, Cursor, etc.) -winapp agents generate - -# Specify a custom skills directory -winapp agents generate --skills-dir ./my-skills -``` - -This is also run automatically during `winapp init` (unless `--no-skills` is passed). - ## Tips - Use `--use-defaults` (alias: `--no-prompt`) in CI/CD pipelines and scripts to avoid interactive prompts - If you only need `appxmanifest.xml` without SDK setup, use `winapp manifest generate` instead of `init` -- The `--no-skills` flag skips AI agent skill file generation if you don't use Copilot/Claude/Cursor - `winapp init` is idempotent for the config file — re-running it won't overwrite an existing `winapp.yaml` unless you use `--config-only` - For Electron projects, prefer `npm install --save-dev @microsoft/winappcli` and use `npx winapp init` instead of the standalone CLI diff --git a/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md index 48881cd0..c486e2bc 100644 --- a/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md +++ b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md @@ -49,8 +49,6 @@ Does the project have an appxmanifest.xml? │ └─ winapp manifest update-assets ./logo.png ├─ Need to run SDK tools directly? │ └─ winapp tool - ├─ Want AI agent skills in your repo? - │ └─ winapp agents generate ├─ Need to publish to Microsoft Store? │ └─ winapp store (passthrough to Store Developer CLI) └─ Need the .winapp directory path for build scripts? @@ -79,7 +77,6 @@ Does the project have an appxmanifest.xml? | `package` | Build output + `appxmanifest.xml` | `.msix` file | | `sign` | File + certificate | Signed file (in-place) | | `create-external-catalog` | Directory with executables | `CodeIntegrityExternal.cat` | -| `agents generate` | Nothing | `.github/skills/winapp-cli/` (or detected skills dir) | | `tool ` | Nothing (auto-downloads tools) | Runs SDK tool directly | | `store` | Nothing (auto-downloads Store CLI) | Passthrough to Microsoft Store Developer CLI | | `get-winapp-path` | Nothing | Prints `.winapp` directory path | diff --git a/docs/fragments/skills/winapp-cli/setup.md b/docs/fragments/skills/winapp-cli/setup.md index 7c4f709c..a9a6aa68 100644 --- a/docs/fragments/skills/winapp-cli/setup.md +++ b/docs/fragments/skills/winapp-cli/setup.md @@ -84,23 +84,10 @@ This updates `winapp.yaml` with the latest versions and reinstalls packages. 4. **Debug with identity** — `winapp create-debug-identity ./bin/myapp.exe` to test Windows APIs 5. **Package** — `winapp package ./bin/Release --cert ./devcert.pfx` to create MSIX -### Generate AI agent skills - -```powershell -# Generate SKILL.md files for AI coding assistants (Copilot, Claude, Cursor, etc.) -winapp agents generate - -# Specify a custom skills directory -winapp agents generate --skills-dir ./my-skills -``` - -This is also run automatically during `winapp init` (unless `--no-skills` is passed). - ## Tips - Use `--use-defaults` (alias: `--no-prompt`) in CI/CD pipelines and scripts to avoid interactive prompts - If you only need `appxmanifest.xml` without SDK setup, use `winapp manifest generate` instead of `init` -- The `--no-skills` flag skips AI agent skill file generation if you don't use Copilot/Claude/Cursor - `winapp init` is idempotent for the config file — re-running it won't overwrite an existing `winapp.yaml` unless you use `--config-only` - For Electron projects, prefer `npm install --save-dev @microsoft/winappcli` and use `npx winapp init` instead of the standalone CLI diff --git a/docs/fragments/skills/winapp-cli/troubleshoot.md b/docs/fragments/skills/winapp-cli/troubleshoot.md index eb5560b1..c43e0ddf 100644 --- a/docs/fragments/skills/winapp-cli/troubleshoot.md +++ b/docs/fragments/skills/winapp-cli/troubleshoot.md @@ -44,8 +44,6 @@ Does the project have an appxmanifest.xml? │ └─ winapp manifest update-assets ./logo.png ├─ Need to run SDK tools directly? │ └─ winapp tool - ├─ Want AI agent skills in your repo? - │ └─ winapp agents generate ├─ Need to publish to Microsoft Store? │ └─ winapp store (passthrough to Store Developer CLI) └─ Need the .winapp directory path for build scripts? @@ -74,7 +72,6 @@ Does the project have an appxmanifest.xml? | `package` | Build output + `appxmanifest.xml` | `.msix` file | | `sign` | File + certificate | Signed file (in-place) | | `create-external-catalog` | Directory with executables | `CodeIntegrityExternal.cat` | -| `agents generate` | Nothing | `.github/skills/winapp-cli/` (or detected skills dir) | | `tool ` | Nothing (auto-downloads tools) | Runs SDK tool directly | | `store` | Nothing (auto-downloads Store CLI) | Passthrough to Microsoft Store Developer CLI | | `get-winapp-path` | Nothing | Prints `.winapp` directory path | From b9aaf61000ce3aafea52c829238fc52eeb11740f Mon Sep 17 00:00:00 2001 From: Nikola Metulev <711864+nmetulev@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:33:10 -0800 Subject: [PATCH 7/7] pr feedback --- .github/plugin/agents/winapp.agent.md | 2 +- .../skills/winapp-cli/frameworks/SKILL.md | 2 +- AGENTS.md | 10 +++---- README.md | 2 +- .../fragments/skills/winapp-cli/frameworks.md | 2 +- scripts/generate-llm-docs.ps1 | 29 ++++++++++--------- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/.github/plugin/agents/winapp.agent.md b/.github/plugin/agents/winapp.agent.md index 72a63d81..22839350 100644 --- a/.github/plugin/agents/winapp.agent.md +++ b/.github/plugin/agents/winapp.agent.md @@ -9,7 +9,7 @@ You are an expert in Windows app development using the **winapp CLI** — a comm ## Your core responsibilities 1. **Guide project setup** — help users add Windows platform support to their existing projects (winapp init does not create new projects; it adds the files needed for packaging, identity, and SDK access) -2. **Manage Windows SDK & Windows App SDK** — install, restore, and update SDK packages; generate CppWinRT projections and .NET SDK references so apps can call Windows APIs. Handle self contained windows app sdk. +2. **Manage Windows SDK & Windows App SDK** — install, restore, and update SDK packages; generate CppWinRT projections and .NET SDK references so apps can call Windows APIs. Handle self-contained Windows App SDK. 3. **Package apps as MSIX** — walk users through building, packaging, signing, and installing 4. **Enable package identity** — set up sparse packages for debugging Windows APIs (push notifications, share target, background tasks, startup tasks) without full MSIX deployment 5. **Manage certificates** — generate, install, and troubleshoot development certificates for code signing diff --git a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md index fab112a4..2bac7639 100644 --- a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md +++ b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md @@ -26,7 +26,7 @@ Each framework has a detailed guide — refer to the links below rather than try ## Key differences by framework ### Electron (npm package) -Use the **npm package** (`@microsoft/winappcli`), **not** the standalone CLI. The npm package includes: +Use the **npm package** (`@Microsoft/WinAppCli`), **not** the standalone CLI. The npm package includes: - The native winapp CLI binary bundled inside `node_modules` - A Node.js SDK with helpers for creating native C#/C++ addons - Electron-specific commands under `npx winapp node` diff --git a/AGENTS.md b/AGENTS.md index be2374ac..3b2ce9c0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,14 +33,14 @@ node cli.js help .\scripts\build-cli.ps1 ``` -# Always update documentation and samples -When adding or changing public facing features, ensure all documentation is also updated. Places to update (but not limitted to): +## Always update documentation and samples +When adding or changing public facing features, ensure all documentation is also updated. Places to update (but not limited to): - docs\usage.md - docs\guides\ - samples\ - docs\fragments\skills\ -- Readme.md +- README.md - .github\plugin\agents\ (.github\plugin\skills are autogenerated from docs\fragments\skills) @@ -67,7 +67,7 @@ Look at the `docs\cli-schema.json` for the full schema to know what the cli can ## Quick change checklist -- **Adding a new CLI command**: Implement in C# under `Commands/`, register in `HostBuilderExtensions.cs`, inject into `WinAppRootCommand`, update `src/winapp-npm/cli.js` if needed.. +- **Adding a new CLI command**: Implement in C# under `Commands/`, register in `HostBuilderExtensions.cs`, inject into `WinAppRootCommand`, update `src/winapp-npm/cli.js` if needed. - **Changing command descriptions**: Edit the description string in the command's constructor. - **Changing CLI commands/workflows**: Update the hand-written skill template in `docs/fragments/skills/` if the workflow, examples, or troubleshooting for that command changed. Run `scripts/build-cli.ps1` at the end to regenerate all skills. - **After C# CLI changes**: Run `cd src/winapp-npm && npm run build` to update npm package binaries. @@ -84,6 +84,6 @@ Look at the `docs\cli-schema.json` for the full schema to know what the cli can The following files are auto-generated from `cli-schema.json` via `scripts/generate-llm-docs.ps1`. Do not run this script directly and run via the `build-cli.ps1` script. Do not edit these files directly: - `docs/cli-schema.json` — machine-readable schema -- `.github/plugin/skills/winapp-cli/*/SKILL.md` — Copilot CLI plugin skills (also embedded in CLI binary via csproj link) +- `.github/plugin/skills/winapp-cli/*/SKILL.md` — Copilot CLI plugin skills The hand-written workflow content lives in `docs/fragments/skills/winapp-cli/`. Running `scripts/build-cli.ps1` triggers regeneration automatically. diff --git a/README.md b/README.md index fa13364e..b72703b8 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ This repository includes samples demonstrating how to use the CLI with various f ## 🤖 Using with AI Coding Agents -AI coding agents (GitHub Copilot, Claude Code, etc) auto-discover skill files in your project. +AI coding agents (GitHub Copilot, Claude Code, etc.) auto-discover skill files in your project. **GitHub Copilot CLI Plugin** (global — works across all projects) ```bash diff --git a/docs/fragments/skills/winapp-cli/frameworks.md b/docs/fragments/skills/winapp-cli/frameworks.md index 8510e452..37c6f46b 100644 --- a/docs/fragments/skills/winapp-cli/frameworks.md +++ b/docs/fragments/skills/winapp-cli/frameworks.md @@ -21,7 +21,7 @@ Each framework has a detailed guide — refer to the links below rather than try ## Key differences by framework ### Electron (npm package) -Use the **npm package** (`@microsoft/winappcli`), **not** the standalone CLI. The npm package includes: +Use the **npm package** (`@Microsoft/WinAppCli`), **not** the standalone CLI. The npm package includes: - The native winapp CLI binary bundled inside `node_modules` - A Node.js SDK with helpers for creating native C#/C++ addons - Electron-specific commands under `npx winapp node` diff --git a/scripts/generate-llm-docs.ps1 b/scripts/generate-llm-docs.ps1 index f96b72fe..b9c0cea9 100644 --- a/scripts/generate-llm-docs.ps1 +++ b/scripts/generate-llm-docs.ps1 @@ -88,7 +88,7 @@ $SkillsDir = $SkillsPath # Skill → CLI command mapping for auto-generated options/arguments tables # Each skill maps to one or more CLI commands whose options/arguments should be included $SkillCommandMap = @{ - "setup" = @("init", "restore", "update", "agents generate") + "setup" = @("init", "restore", "update") "package" = @("package", "create-external-catalog") "identity" = @("create-debug-identity") "signing" = @("cert generate", "cert install", "sign") @@ -289,21 +289,24 @@ version: $CliVersion Write-Host "[SKILLS] $skillName - generated" -ForegroundColor Gray } -# Update plugin.json version to match CLI version -$PluginJsonPath = Join-Path $ProjectRoot ".github\plugin\plugin.json" -if (Test-Path $PluginJsonPath) { - $pluginJson = Get-Content $PluginJsonPath -Raw | ConvertFrom-Json - $pluginJson.version = $CliVersion - $pluginJsonContent = $pluginJson | ConvertTo-Json -Depth 10 - # Normalize line endings - $pluginJsonContent = $pluginJsonContent -replace "`r`n", "`n" - $pluginJsonContent = $pluginJsonContent.TrimEnd() + "`n" - [System.IO.File]::WriteAllText($PluginJsonPath, $pluginJsonContent, [System.Text.UTF8Encoding]::new($false)) - Write-Host "[SKILLS] Updated plugin.json version to $CliVersion" -ForegroundColor Gray +# Update plugin.json version to match CLI version (only when outputting to the default skills path) +$DefaultSkillsPath = Join-Path $ProjectRoot ".github\plugin\skills\winapp-cli" +if ($SkillsDir -eq $DefaultSkillsPath) { + $PluginJsonPath = Join-Path $ProjectRoot ".github\plugin\plugin.json" + if (Test-Path $PluginJsonPath) { + $pluginJson = Get-Content $PluginJsonPath -Raw | ConvertFrom-Json + $pluginJson.version = $CliVersion + $pluginJsonContent = $pluginJson | ConvertTo-Json -Depth 10 + # Normalize line endings + $pluginJsonContent = $pluginJsonContent -replace "`r`n", "`n" + $pluginJsonContent = $pluginJsonContent.TrimEnd() + "`n" + [System.IO.File]::WriteAllText($PluginJsonPath, $pluginJsonContent, [System.Text.UTF8Encoding]::new($false)) + Write-Host "[SKILLS] Updated plugin.json version to $CliVersion" -ForegroundColor Gray + } } Write-Host "[SKILLS] Generated $($SkillNames.Count) skills in:" -ForegroundColor Green -Write-Host " .github/plugin/skills/winapp-cli/ (also embedded in CLI binary via csproj link)" -ForegroundColor Gray +Write-Host " .github/plugin/skills/winapp-cli/" -ForegroundColor Gray Write-Host "" Write-Host "[DOCS] All documentation and skills generated successfully!" -ForegroundColor Green