Skip to content
Merged
2 changes: 1 addition & 1 deletion plugins/dotnet-template-engine/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"version": "0.1.0",
"description": ".NET Template Engine skills for dotnet new: create projects (console app, class library, web API, Blazor, MAUI), discover and search templates, inspect template parameters and frameworks (net8.0, net9.0, net10.0), scaffold solutions, author and validate custom templates, install template packages from NuGet.",
"skills": ["./skills/"],
"agents": ["./agents/template-engine.agent.md"]
"agents": ["./agents/template-engine.agent.md"]
}
17 changes: 14 additions & 3 deletions plugins/dotnet-template-engine/agents/template-engine.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,24 @@ Classify the user's request and invoke the appropriate skill:
| "Create a new project/app/service" | `template-instantiation` skill |
| "What templates are available for X?" | `template-discovery` skill |
| "Show me template details/parameters" | `template-discovery` skill (inspect via `dotnet new <template> --help`) |
| "Compare templates X vs Y" / "which template should I use" | `template-comparison` skill |
| "Apply smart defaults" / cross-parameter questions during creation | `template-smart-defaults` skill |
| "Create a template from my project" | `template-authoring` skill |
| "Validate my custom template" | `template-authoring` skill |
| "Validate my custom template" / "check my template.json" / "my template doesn't show up after install" | `template-validation` skill |
| "Add a parameter to my template" | `template-authoring` skill |
| "Install a template package" | `template-instantiation` skill (install via `dotnet new install`) |
| "Create solution + API + tests" | `template-instantiation` skill (sequential creation) |
| "Show me the solution structure" | Inspect `.sln` and `.csproj` files directly |

## Skills Inventory

- `template-discovery` — find, inspect, and select templates from natural-language intent
- `template-comparison` — compare 2+ templates side by side to help users choose
- `template-instantiation` — create projects/solutions, manage template packages, adapt to CPM
- `template-smart-defaults` — apply cross-parameter default rules during creation
- `template-authoring` — create custom templates from existing projects
- `template-validation` — validate `template.json` for correctness before publishing

## Workflow: Creating a Project

When a user asks to create a new project, follow this workflow:
Expand All @@ -59,7 +70,7 @@ Ask clarifying questions if needed:
Map the user's description to a template short name (see template-discovery skill for keyword mappings), or use `dotnet new search` for keyword-based search. Present options if multiple matches exist.

### 3. Inspect Parameters
Use `dotnet new <template> --help` to show available parameters and their defaults, types, and choices.
Use `dotnet new <template> --help` to show available parameters and their defaults, types, and choices. Apply cross-parameter defaults via the `template-smart-defaults` skill (e.g., AOT → latest compatible framework; auth set → don't disable HTTPS) without overriding values the user set explicitly.

### 4. Analyze Workspace
Inspect the existing project structure: check for `Directory.Packages.props` (CPM), `global.json`, and existing `.csproj` files to determine framework conventions.
Expand All @@ -83,7 +94,7 @@ When a user asks to create a custom template:
Read the `.csproj` and create a `.template.config/template.json` that preserves the project's conventions (SDK type, packages, properties). Review the generated template.json.

### 2. Validate
Review the generated `template.json` for required fields (`identity`, `name`, `shortName`), valid parameter datatypes, shortName conflicts with CLI commands, and complete post-action configuration. Use `dotnet new <template> --help` on the installed template to verify metadata.
Validate the generated `template.json` with the `template-validation` skill — it owns the full rule set (required fields, identity format, reserved shortName conflicts, parameter datatypes, post-actions, constraints, tags). Then use `dotnet new <template> --help` on the installed template to verify metadata.

### 3. Refine
Help the user add parameters, conditional content, post-actions, and constraints.
Expand Down
23 changes: 13 additions & 10 deletions plugins/dotnet-template-engine/skills/template-authoring/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ Analyze the source `.csproj` and create a `.template.config/template.json`:

1. Create `.template.config` directory next to the project
2. Generate `template.json` with `identity` (reverse-DNS), `name`, `shortName`, `sourceName` (project name for replacement), `classifications`, and `tags`
3. Preserve from source: SDK type, package references with metadata (PrivateAssets, IncludeAssets), properties (OutputType, TreatWarningsAsErrors), CPM patterns
3. Preserve from source — generic `dotnet new` templates frequently get these wrong, so verify each is carried over from the original `.csproj`:
1. **SDK type** — `Microsoft.NET.Sdk`, `Microsoft.NET.Sdk.Web`, `Microsoft.NET.Sdk.Worker`, etc.
2. **Analyzer/package reference metadata** — `PrivateAssets`, `IncludeAssets`, `ExcludeAssets`
3. **`OutputType` and other key properties** — `TreatWarningsAsErrors`, `Nullable`, `LangVersion`
4. **CPM participation** — no inline `Version` attributes when a `Directory.Packages.props` is present
5. **Custom build props/targets** and `Directory.Build.props` conventions
6. **Repo conventions** — folder layout, naming, `global.json` SDK pin

Minimal example:
```json
Expand All @@ -64,15 +70,12 @@ Minimal example:

### Step 2: Validate template.json

Read and review the `template.json` for common authoring issues:
Validate the generated `template.json` using the **template-validation** skill (it owns the full rule set — required fields, identity format, reserved shortName conflicts, parameter datatypes, post-actions, constraints, and tags).

Validation checks to perform:
- **Required fields** — verify `identity`, `name`, and `shortName` are present
- **Identity format** — use reverse-DNS format (e.g., `MyOrg.Templates.WebApi`)
- **Parameter issues** — check datatypes are valid (`string`, `bool`, `choice`, `int`, `float`), choices have defaults, descriptions are present
- **ShortName conflicts** — avoid names that collide with built-in CLI commands (`build`, `run`, `test`, `publish`). Check with `dotnet new list` to see if the name is already taken
- **Post-action completeness** — verify post-actions have all required configuration
- **Tags** — ensure language, type, and classification tags are set for discoverability
Quick summary of what gets checked:
- **Required fields** — `identity`, `name`, and `shortName` must be present.
- **ShortName conflicts** — avoid names that collide with `dotnet new` subcommands. Read the authoritative set from the `Commands:` section of `dotnet new --help` for the installed SDK and do not hardcode it (it can change between versions); illustrative examples from current SDKs are `install`, `uninstall`, `update`, `list`, `search`, `details`, `create`. A conflict happens because `dotnet new <name>` would be parsed as the subcommand of the same name. Top-level `dotnet` verbs like `build`, `run`, `test`, and `publish` do NOT conflict. Run `dotnet new list` to confirm the name is not already taken.
- **Parameters, post-actions, tags** — see template-validation for the complete rules, including the valid datatype list.

### Step 3: Refine the template

Expand Down Expand Up @@ -107,7 +110,7 @@ dotnet build ./test-output/TestProject
| Pitfall | Solution |
|---------|----------|
| Identity format issues | Use reverse-DNS format (e.g., `MyOrg.Templates.WebApi`). Avoid spaces or special characters. |
| ShortName conflicts with CLI commands | Avoid names like `build`, `run`, `test`, `publish`. Check by running `dotnet new list` to see if the name is already taken. |
| ShortName conflicts with CLI commands | Avoid names that match a `dotnet new` subcommand; read the live set from `dotnet new --help` and don't hardcode it (illustrative examples: `install`, `uninstall`, `update`, `list`, `search`, `details`, `create`). Top-level verbs like `build`/`run`/`test`/`publish` are fine. Run `dotnet new list` to see if the name is already taken. |
| Missing parameter descriptions | Every parameter should have a `description` and `displayName` for discoverability. |
| Not testing all parameter combinations | Use `dotnet new <template> --dry-run` with different parameter values to verify conditional content works correctly. |
| Hardcoded versions in template | Use `sourceName` replacement for project names and consider parameterizing framework versions. |
Expand Down
104 changes: 104 additions & 0 deletions plugins/dotnet-template-engine/skills/template-comparison/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
name: template-comparison
description: >
Compares two or more dotnet new templates side by side to help users choose between
them based on parameters, feature support, frameworks, and classifications.
USE FOR: deciding between similar templates (webapi vs webapp, blazor vs
blazorwasm, console vs worker), producing a side-by-side comparison of parameters and
feature support, understanding how templates differ before creating a project.
DO NOT USE FOR: creating a project from a template (use template-instantiation),
authoring or validating custom templates (use template-authoring and template-validation),
general single-template discovery (use template-discovery).
license: MIT
---

# Template Comparison

This skill helps an agent compare 2+ `dotnet new` templates side by side so the user can
pick the right one. It inspects each template's parameters and feature support and renders
a comparison table.

## When to Use

- User is deciding between similar templates (e.g., `webapi` vs `webapp`, `blazor` vs `blazorwasm`)
- User asks "which template should I use for X?"
- User wants to understand how two or more templates differ before creating a project

## When Not to Use

- User wants to create a project — route to `template-instantiation`
- User wants to author or validate a custom template — route to `template-authoring` or `template-validation`
- User just needs to find or inspect a single template — route to `template-discovery`

## Inputs

| Input | Required | Description |
|-------|----------|-------------|
| Template short names | Yes | Two or more template short names to compare (e.g., `webapi`, `webapp`) |
| Comparison focus | No | Optional aspect to emphasize (auth, AOT, frameworks, interactivity) |

## Workflow

### Step 1: Inspect each template

Run `dotnet new <template> --help` for each template being compared to collect its
parameters (names, types, defaults, choices) and supported frameworks:

```bash
dotnet new webapi --help
dotnet new webapp --help
```

If a template is not installed, find and install it first (`dotnet new search <keyword>`,
then `dotnet new install <package>`).

> **Run `--help` calls sequentially.** The template engine uses a global mutex, so running
> several `dotnet new <template> --help` commands concurrently can fail with a transient
> "mutex"/"persistence" error and empty output. Inspect templates one at a time; if a call
> fails, retry it once before moving on, and still produce the comparison from whatever
> parameter knowledge you have rather than ending with no answer.

### Step 2: Build the comparison table

Produce a side-by-side table covering:

- **Parameters** — name, type, default, choices
- **Feature support** — auth, AOT, Docker, controllers, interactivity
- **Available frameworks** — e.g., net8.0, net9.0, net10.0
- **Classifications** — categories the template advertises (Web, API, Blazor, etc.)

Example shape:

| Aspect | `webapi` | `webapp` |
|--------|----------|----------|
| Auth (`--auth`) | None, Individual, SingleOrg, Windows | None, Individual, SingleOrg, ... |
| AOT (`--aot` flag) | n/a — native AOT via publish-time `PublishAot` | n/a |
| Controllers (`--use-controllers`) | Yes | n/a |
| Interactivity | n/a | n/a |
Comment thread
YuliiaKovalova marked this conversation as resolved.
| Frameworks | net8.0 / net9.0 / net10.0 | net8.0 / net9.0 / net10.0 |
| Classifications | Web, WebAPI | Web, Razor Pages |

### Step 3: Recommend

Summarize the key differences and recommend a template for the user's stated scenario,
linking to `template-instantiation` to create it.

## Validation

- [ ] Every template requested was inspected via `dotnet new <template> --help`
- [ ] The comparison covers parameters, feature support, frameworks, and classifications
- [ ] Differences relevant to the user's scenario are called out explicitly
- [ ] A recommendation (or clear trade-off) is provided

## Common Pitfalls

| Pitfall | Solution |
|---------|----------|
| Comparing uninstalled templates from memory | Install and inspect each template so the comparison reflects the real parameters and choices. |
| Assuming feature parity | Parameter names and feature support vary by template — confirm each with `--help`. |
| Comparing fundamentally different template types | Only compare templates that solve overlapping problems; note when they target different scenarios. |

## More Info

- [dotnet new templates](https://learn.microsoft.com/dotnet/core/tools/dotnet-new-sdk-templates) — built-in template reference
- [dotnet new](https://learn.microsoft.com/dotnet/core/tools/dotnet-new) — CLI reference
71 changes: 57 additions & 14 deletions plugins/dotnet-template-engine/skills/template-discovery/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ description: >
produces before creating a project, resolving intent like "web API with auth" to
concrete template + parameters.
DO NOT USE FOR: actually creating projects (use template-instantiation), authoring
custom templates (use template-authoring), MSBuild or build issues (use dotnet-msbuild
custom templates (use template-authoring), comparing templates side by side in detail
(use template-comparison), MSBuild or build issues (use dotnet-msbuild
plugin), NuGet package management unrelated to template packages.
license: MIT
---
Expand All @@ -29,6 +30,7 @@ This skill helps an agent find, inspect, and select the right `dotnet new` templ

- User wants to create a project — route to `template-instantiation` skill
- User wants to author or validate a custom template — route to `template-authoring` skill
- User wants a detailed side-by-side comparison of templates — route to `template-comparison` skill
- User is troubleshooting build issues — route to `dotnet-msbuild` plugin

## Inputs
Expand All @@ -43,19 +45,59 @@ This skill helps an agent find, inspect, and select the right `dotnet new` templ

### Step 1: Resolve intent to template candidates

Map the user's natural-language description to template short names using these common keyword mappings:

| User Intent | Template | Suggested Parameters |
|-------------|----------|---------------------|
| web API, REST API | `webapi` | `--auth Individual --use-controllers` if auth requested |
| web app, website | `webapp` | |
| Blazor, interactive web | `blazor` | |
| console app, CLI tool | `console` | |
| class library, shared code | `classlib` | |
| worker service, background job | `worker` | |
| gRPC service | `grpc` | |
| MAUI app, mobile app | `maui` | |
| test project, unit tests | `xunit`, `mstest`, or `nunit` | |
Map the user's natural-language description to template short names and parameters using these mappings.

**Intent → template short name(s):**

| Intent / phrase | Template short name(s) |
Comment thread
YuliiaKovalova marked this conversation as resolved.
|---|---|
| web api, web service, rest api, restful, api, minimal api | `webapi` |
| web app, web application | `webapp`, `blazorserver` |
| mvc | `mvc` |
| razor, razor pages | `webapp` |
| blazor, blazor web app | `blazor` |
| blazor server | `blazorserver` |
| blazor wasm, blazor webassembly | `blazorwasm` |
| grpc | `grpc` |
| signalr | `webapi`, `webapp` |
| console, console app, command line, cli | `console` |
| worker, background service, daemon, windows service | `worker` |
| class library, library, lib, nuget package | `classlib` |
| maui, mobile, cross-platform app, ios, android | `maui` |
Comment thread
YuliiaKovalova marked this conversation as resolved.
| desktop | `maui`, `wpf`, `winforms` |
| wpf | `wpf` |
| winforms, windows forms | `winforms` |
| winui, winui3 | `winui3` |
Comment thread
YuliiaKovalova marked this conversation as resolved.
| test, unit test | `xunit`, `nunit`, `mstest` |
| xunit / nunit / mstest | `xunit` / `nunit` / `mstest` |
| solution | `sln` |
| aspire, .net aspire | `aspire-starter`, `aspire` |
| azure functions, function app, serverless | `func` |
| orleans | `orleans` |
Comment thread
YuliiaKovalova marked this conversation as resolved.
| razor component, web component | `razorcomponent` |
| razor class library | `razorclasslib` |
| gitignore / editorconfig / nuget config / global json | `gitignore` / `editorconfig` / `nugetconfig` / `globaljson` |

**Keyword → parameter:**

| Keyword / phrase | Parameter | Value |
|---|---|---|
| authentication, auth, individual auth, individual accounts | `--auth` | `Individual` |
| windows auth, azure ad, entra | `--auth` | `SingleOrg` |
| no auth, no authentication | `--auth` | `None` |
| controllers, with controllers | `--use-controllers` | (flag) |
| minimal api | (default) | — |
| aot, native aot | `--aot` | (flag) |
| docker, container | the template's Docker/container option | varies by template — confirm with `--help` (not all templates expose one) |
| net8 / .net 8 / dotnet 8 | `--framework` | `net8.0` |
| net9 / .net 9 / dotnet 9 | `--framework` | `net9.0` |
| net10 / .net 10 / dotnet 10 | `--framework` | `net10.0` |

These are starting guesses. Always confirm the real parameter names/choices with `dotnet new <template> --help`, because parameter names vary by template (e.g., `--auth` vs `--Authentication`).
Comment thread
YuliiaKovalova marked this conversation as resolved.
Comment thread
YuliiaKovalova marked this conversation as resolved.

Some mapped short names are not present in a default SDK install — templates like `maui`, `winui3`, `aspire-starter`/`aspire`, `func`, and `orleans` typically require a workload (`dotnet workload install <id>`) and/or an additional template package (`dotnet new install <package>`). If a mapped short name does not appear in `dotnet new list`, fall back to `dotnet new list`/`dotnet new search` to find the right template and the package/workload that provides it before recommending it.

> **Resilience — always answer, even if the CLI fails.** The intent mapping above is a usable answer on its own. Run `dotnet new` commands **sequentially, one at a time** — the template engine uses a global mutex, so firing several `dotnet new <template> --help`/`--dry-run` calls concurrently can produce a transient "mutex"/"persistence" error and empty output. If a command fails, retry it once; if it still fails, **fall back to this intent/parameter mapping and give the user a concrete recommendation**, noting that the exact parameter names/choices could not be CLI-confirmed. Never end the turn with no answer because a CLI call errored.

### Step 2: Search for templates

Expand Down Expand Up @@ -109,6 +151,7 @@ Summarize the best template match with:
| Not searching NuGet for templates | If `dotnet new list` shows no matches, use `dotnet new search <keyword>` to find installable templates on NuGet.org. |
| Not checking template constraints | Some templates require specific SDKs or workloads. Use `dotnet new <template> --help` to surface constraints before recommending. |
| Recommending a template without previewing output | Always use `dotnet new <template> --dry-run` to confirm the template produces what the user expects. |
| A `dotnet new` call fails with a "mutex"/"persistence" error and you return nothing | These are transient (often from concurrent invocations). Run `dotnet new` calls sequentially, retry once, then fall back to the Step 1 intent mapping and still give the user a concrete answer. |

## More Info

Expand Down
Loading