Project contract for WinUtil — what the project is, how it's built, and how it runs. Written for anyone, human or AI, who needs to understand the project itself.
AGENTS.md in the repository root points here for these facts, and separately covers how an agent should behave while working in this repo. This file does not change based on who's reading it.
WinUtil is a Windows PowerShell utility with a WPF interface. The repository is maintained as modular source, but the distributed artifact is one compiled PowerShell script.
- Language: Windows PowerShell / PowerShell.
- UI: WPF via
xaml/inputXML.xaml. - Configuration: JSON files under
config/. - Tests: Pester tests under
pester/. - Lint: PowerShell Script Analyzer with settings in
lint/PSScriptAnalyser.ps1. - Docs: Astro + Starlight site under
docs/, built independently ofCompile.ps1(its ownpackage.json/node_modules). - Release artifact: generated root
winutil.ps1.
Compile.ps1: build script that createswinutil.ps1.scripts/start.ps1: startup/bootstrap segment used at the beginning of the compiled script.scripts/main.ps1: main entrypoint appended at the end of the compiled script.functions/public/: public/UI-facing PowerShell functions.functions/private/: internal helper PowerShell functions.config/: JSON configuration consumed at compile time and embedded into$sync.configs.xaml/inputXML.xaml: WPF UI markup embedded into the compiled script.tools/autounattend.xml: unattended setup XML embedded for Windows ISO workflows.pester/: Pester tests for config and function checks.lint/PSScriptAnalyser.ps1: PowerShell Script Analyzer settings.tools/title-screen/: uv project that captures WinUtil's Light and Dark themes and generates the title-screen composite.docs/: Astro + Starlight documentation site, with its ownpackage.jsonand build independent ofCompile.ps1.winutil.ps1: ignored generated build artifact.
- Provide a single-script Windows utility that can be launched from PowerShell.
- Keep development modular enough for contributors to work on functions, config, UI, docs, and tooling independently.
- Make install, tweak, feature, repair, update, and ISO workflows discoverable from the WPF UI.
- Keep common lists and options declarative in JSON config where possible.
- Preserve a repeatable compile process so local builds and GitHub Actions builds produce the distributable script from the same inputs.
winutil.ps1is not hand-maintained.- The project is not structured as a PowerShell module at runtime.
- The GUI is not a separate packaged desktop application in this repository's normal release path.
- Generated files should not be reviewed as source changes.
Compile.ps1 combines the repository sources into winutil.ps1 in this order:
- Read
scripts/start.ps1and replace#{replaceme}with the currentyy.MM.ddbuild date. - Append every file under
functions/recursively. - Convert each
config/*.jsonfile into embedded$sync.configsobjects. - Special-case
config/applications.jsonso keys receive theWPFInstallprefix in compiled config. - Embed
xaml/inputXML.xamlinto$inputXML. - Embed
tools/autounattend.xmlinto$WinUtilAutounattendXml. - Append
scripts/main.ps1. - Write the result to root
winutil.ps1.
Because the final script is concatenated, code cannot rely on runtime module imports or source-relative dot-sourcing unless the compiled script will also contain the required code/data.
- WinUtil runs in PowerShell on Windows and uses WPF for the UI.
- Shared mutable state is stored in
$sync, including configs, UI element references, runspace state, selections, and progress. - Long-running operations use runspaces or existing async patterns so the UI remains responsive.
- UI updates from background work are dispatched back to the WPF UI thread.
- Declarative features such as apps, tweaks, presets, DNS providers, and navigation stay in
config/*.jsonunless code is required.
- UI layout lives in
xaml/inputXML.xaml. - Named WPF controls are discovered and stored in
$sync. - Button/action wiring follows a naming convention: an element named like
WPFThingButtonmaps to a function named likeInvoke-WPFThingButton.
- Config files must remain valid JSON and compile cleanly through
ConvertFrom-Json. config/dns.jsonopts unfiltered providers into Fastest selection withBenchmarkEligible: true; missing or false values exclude a provider from the TCP latency benchmark.config/applications.jsondefines installable applications; each entry includes the fields expected by tests and UI code, such as package manager IDs, category, display content, description, and link.config/tweaks.jsondefines Windows tweaks; registry and service changes include original values or original states when applicable so undo workflows can restore user systems.- Preset and navigation files reference valid config keys. Renaming a config key requires updating all presets, UI references, docs, and code paths together.
- Adding or renaming a preset, adding a selection key prefix, or adding a new kind of tweak change also requires updating the docs Tools pages (see Docs Site).
- Registry, service, package manager, Windows Update, AppX removal, and ISO operations affect the host system and are treated as high-risk.
- Tweak changes include undo metadata when the schema supports it, so changes stay reversible.
- ISO workflows never modify the user's original ISO file; they work on copied/mounted content.
docs/is an Astro + Starlight site, independent ofCompile.ps1's build (its ownpackage.json/node_modules, deployed via thedocs.yamlGitHub Actions workflow to GitHub Pages).- Pages live under
docs/src/content/docs/(.mdx), organized intoguides/,code-reference/, plus top-level pages likefaq.mdx,knownissues.mdx,contributing.mdx,index.mdx. docs/src/content/docs/code-reference/tweaks/and.../features/are auto-generated bytools/devdocs-generator.ps1fromconfig/tweaks.json/config/feature.jsonand the relevant PowerShell function files. Other pages undercode-reference/(e.g.architecture.mdx) are hand-written and untouched by the generator. The docs workflow regenerates reference pages before building Astro and runs for docs, generator, config, and function changes; generated output is not committed by deployment.- Sidebar entries in
docs/astro.config.mjsmust match actual page slugs underdocs/src/content/docs/. - The site's UI is plugged into Starlight through component overrides in
docs/src/components/overrides/(Header, Sidebar, MobileMenuToggle, TableOfContents, MobileTableOfContents, PageTitle, Pagination, ThemeSelect), registered indocs/astro.config.mjs. Static parts are Astro components; interactive parts (search, menus, table of contents, theme toggle) are React islands indocs/src/components/docs/. Starlight still owns routing, Markdown rendering, the Pagefind search index, and the generated reference pages. The landing page (index.mdx) adds React Bits components (docs/src/components/react-bits/, MIT + Commons Clause; see itsLICENSE.md) and GSAP + Lenis motion (docs/src/scripts/landing-motion.js, with landing-only components indocs/src/components/landing/). That motion is skipped entirely for visitors who prefer reduced motion. - The Tools pages, Config Builder (
config-builder.mdx,docs/src/components/config-builder/) and Tweak Explorer (tweak-explorer.mdx,docs/src/components/tweak-explorer/), fetchconfig/*.jsonin the browser from the latest stable release tag throughdocs/src/lib/winutil-release.jsrather than bundling the repo's copy, because-Configruns the stable release and rejects the whole import on any key it does not know.- New or removed apps, tweaks, toggles, features, AppX packages, and categories show up in the next browser session after a release is marked Latest, with no docs rebuild. Pre-releases are not picked up. Tweak Explorer shows a reference link only when the latest stable release's config contains that link; its target page becomes available after the corresponding source entry is on main and the docs are redeployed.
- Three things are hard-coded and must be updated by hand when WinUtil changes them: preset names (
Standard,Minimal,Advanced,AppxDefault) inConfigBuilder.jsxandTweakExplorer.jsx; the importable key prefixes (WPFInstall,WPFTweaks,WPFToggle,WPFFeature,WPFAppx) inConfigBuilder.jsx, which mirrorUpdate-WinUtilSelections; and the tweak change fields (registry,service,InvokeScript,UndoScript,appx, and registryValuesmappings forComboboxentries) read byTweakExplorer.jsx, which mirrorInvoke-WinUtilTweaksandSet-WinUtilRegistryComboState. - Config Builder share links, including the Tweak Explorer's hand-off, put the selected keys in the URL hash as a comma-separated list, built and read by
docs/src/lib/config-link.js. Links are posted publicly, so the builder must keep reading that format.
docs/public/is tracked source for static assets (favicons, etc.), not generated output. Generated/ignored paths are listed indocs/.gitignore(dist/,.astro/,node_modules/, local env files).docs/src/assets/branding/title-screen.pngis a tracked generated image used by the repository README and docs homepage. Its raw Light and Dark captures are temporary.docs/Dockerfileanddocs/docker-compose.yml(servicewinutil-astro) containerize the site's npm tooling, including Playwright and its browsers (version pinned indocs/package.json); see AGENTS.md's Dependency Installs, Builds, And Dev Servers for why and how agents must use them instead of running npm on the host.
.\Compile.ps1verifies the compiler can generatewinutil.ps1..\Compile.ps1 -Runcompiles and launches the generated utility for manual GUI verification.- Pester 5.8.0 runs the suite under
pester/*.Tests.ps1. GitHub Actions (unittests.yaml) installs Pester 5.8.0 fresh and runs with-CI, which producestestResults.xmland exits non-zero on failure. - GitHub Actions also runs PowerShell Script Analyzer with
lint/PSScriptAnalyser.ps1on every push. - The generated
winutil.ps1may appear locally after compile. It remains ignored build output (see root.gitignore) and must not be committed. - The manually triggered title-screen workflow compiles WinUtil from
mainand opens an image-only pull request when the generated composite changes. These pull requests require manual review. Failed runs retain diagnostics for 14 days.
GitHub Actions is responsible for producing the release winutil.ps1 from repository sources. A release is considered valid only if the generated script came from the compile process, not from direct manual edits to winutil.ps1.