This file provides focused, actionable information to help an AI coding agent be immediately productive in this repo.
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 withscripts/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 withnpm installinsidesrc/winapp-npm.
# 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 -- <args>
# 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.ps1When 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
- .github\plugin\agents\
(.github\plugin\skills are autogenerated from docs\fragments\skills)
If a feature is big enough and requires its own docs page, add it under docs\
Each sample under samples/ has a self-contained Pester 5.x test file (test.Tests.ps1) that validates the corresponding guide workflow from scratch (Phase 1) and verifies the existing sample code still builds (Phase 2). Tests share infrastructure via samples/SampleTestHelpers.psm1.
# Run all sample tests
.\scripts\test-samples.ps1
# Run a specific sample
.\scripts\test-samples.ps1 -Samples dotnet-app
# Run with a locally built winapp npm tarball (package-npm.ps1 outputs to .\artifacts\)
.\scripts\test-samples.ps1 -WinappPath .\artifacts -Verbose
# Or pass a specific .tgz / a directory containing one (e.g., a CI artifact download)
.\scripts\test-samples.ps1 -WinappPath .\artifacts\npm -Verbose- Create
test.Tests.ps1in the sample directory (Pester naming convention) - Use
BeforeDiscoveryfor skip logic (prerequisite checks run at discovery time) - Import shared helpers in
BeforeAll:Import-Module "$PSScriptRoot\..\SampleTestHelpers.psm1" -Force - Accept
$WinappPathand$SkipCleanupparameters viaparam()block - Phase 1 (
Context): from-scratch guide workflow in a temp directory (scaffold, winapp init, build, cert, pack) - Phase 2 (
Context): quick build of existing sample code to verify freshness - Add the sample name to the matrix in
.github/workflows/test-samples.yml
BeforeDiscovery: Set$script:skipusing inlineGet-Commandchecks (no module import). Pester evaluates-Skip:$variableduring discovery, beforeBeforeAllruns.BeforeAll: ImportSampleTestHelpers.psm1, install winapp, create temp directories. Guard withif ($script:skip) { return }.AfterAll: Clean up temp directories usingRemove-TempTestDirectory.Itblocks: Use-Skip:$script:skipfor prerequisite gating. Use PesterShouldassertions (Should -Be 0,Should -Exist,Should -Not -BeNullOrEmpty). When all setup happens inBeforeAlland depends on the prerequisite, you may apply-Skip:$script:skipto the enclosingContextinstead.- Shared helpers:
Invoke-WinappCommand(throws on failure),Test-Prerequisite(returns bool),New-TempTestDirectory,Remove-TempTestDirectory,Install-WinappGlobal.
Sample & guide tests run via .github/workflows/test-samples.yml using a GitHub Actions matrix strategy. Each sample runs in its own parallel job after the main build completes. The workflow downloads the npm package artifact from the Build and Package workflow. Test results are uploaded as JUnit XML via Invoke-Pester with TestResult configuration.
| 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.) |
Look at the docs\cli-schema.json for the full schema to know what the cli can do
- Adding a new CLI command: Implement in C# under
Commands/, register inHostBuilderExtensions.cs, inject intoWinAppRootCommand, updatesrc/winapp-npm/cli.jsif 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. Runscripts/build-cli.ps1at the end to regenerate all skills. - After C# CLI changes: Run
cd src/winapp-npm && npm run buildto update npm package binaries. - Updating package versions: Edit
winapp.example.yaml.
- NuGet: Packages downloaded to NuGet global cache. Local
.winappfolder contains generated headers and libs only. - Build Tools: makeappx.exe, signtool.exe, makepri.exe, etc. Auto-downloaded by the
toolcommand or when commands that need them are invoked. - CppWinRT: Generated headers in
.winapp/generated/include. Response file at.cppwinrt.rsp.
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 — your changes will be overwritten:
docs/cli-schema.json— machine-readable schema.github/plugin/skills/winapp-cli/*/SKILL.md— Copilot CLI plugin skills
To edit skill content, modify the hand-written templates in docs/fragments/skills/winapp-cli/. Each template file (e.g., package.md, manifest.md) contains the workflow docs, examples, and troubleshooting content. The auto-generation script appends command reference tables from the CLI schema. Running scripts/build-cli.ps1 triggers regeneration automatically.
Skill descriptions (used for Copilot skill matching) are defined in the $SkillDescriptions hashtable in scripts/generate-llm-docs.ps1.
- Target: ≤500 lines per file
- Soft limit: ~800 lines — if approaching this, look for extraction opportunities
- Hard limit: Do not let any single file exceed ~1,000 lines. Split into partial classes or extract services.
Use the appropriate pattern for new code:
| Pattern | When to use | Example |
|---|---|---|
| Interface + DI service | Stateful logic, needs dependencies | IPriService / PriService |
| Static helper | Pure functions, no DI needed | PeHelper, MrtAssetHelper |
| Data document | Wraps a file/data format with typed access | AppxManifestDocument |
| Partial class | Splitting a large service with tight internal coupling | MsixService.Runtime.cs |
- One responsibility per service/helper file
- Extract shared logic into helpers rather than duplicating across services
- If a method group only uses 1-2 of a service's 10+ dependencies, it's a candidate for extraction
- Use
XDocument/XElement(System.Xml.Linq) for structured XML manipulation — never regex - Regex is acceptable ONLY for: pre-parse placeholder replacement (
$targetnametoken$), raw text scanning before XML is valid - Use
AppxManifestDocumentfor AppxManifest.xml operations — it provides typed access and namespace-aware queries - When adding new manifest manipulation, add methods to
AppxManifestDocumentrather than writing regex in consuming code