From a02733125759604ae28ac64262bdc5302bdb6e58 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 15:05:45 +0200 Subject: [PATCH 1/7] Add setup-local-sdk skill for global.json paths feature Adds a skill that guides users through installing a .NET SDK into a project-local directory using the global.json paths feature (.NET 10+). Includes: - 12-step workflow: verify host, install SDK, configure global.json, gitignore, workloads, team scripts, verification - MAUI and wasm-tools workload support - Cross-platform install scripts (bash + PowerShell) - 7 eval scenarios covering basic setup, exact version, team scripts, troubleshooting, incompatible host, existing .dotnet/, and MAUI workload - CODEOWNERS entry for @jfversluis and @Redth Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/CODEOWNERS | 3 + .../dotnet/skills/setup-local-sdk/SKILL.md | 487 ++++++++++++++++++ tests/dotnet/setup-local-sdk/eval.yaml | 99 ++++ 3 files changed, 589 insertions(+) create mode 100644 plugins/dotnet/skills/setup-local-sdk/SKILL.md create mode 100644 tests/dotnet/setup-local-sdk/eval.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 994b94b39..0c2f14637 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,6 +22,9 @@ /plugins/dotnet/skills/nuget-trusted-publishing/ @lewing @kartheekp-ms /tests/dotnet/nuget-trusted-publishing/ @lewing @kartheekp-ms +/plugins/dotnet/skills/setup-local-sdk/ @jfversluis @Redth +/tests/dotnet/setup-local-sdk/ @jfversluis @Redth + /plugins/dotnet/agents/optimizing-dotnet-performance.agent.md @dotnet/appmodel /plugins/dotnet-ai/skills/mcp-csharp-create/ @leslierichardson95 @mikekistler diff --git a/plugins/dotnet/skills/setup-local-sdk/SKILL.md b/plugins/dotnet/skills/setup-local-sdk/SKILL.md new file mode 100644 index 000000000..119258dd6 --- /dev/null +++ b/plugins/dotnet/skills/setup-local-sdk/SKILL.md @@ -0,0 +1,487 @@ +--- +name: setup-local-sdk +description: > + Set up a local .NET SDK installation using the global.json `paths` feature + (.NET 10+), including workload installation (MAUI, wasm-tools, etc.). Downloads a + prerelease or specific SDK version into a project-local `.dotnet/` directory + and configures global.json so `dotnet` resolves it automatically, without + touching the system-wide installation. + Use when a user wants to try a .NET preview, test against a specific SDK + version, or create reproducible team/CI install scripts. + Do NOT use when the user wants a system-wide install, is on .NET < 10, needs a + runtime-only install, or is not using SDK-style commands. +--- + +# setup-local-sdk + +## Purpose + +Guide the user through installing a .NET SDK into a project-local directory +(`.dotnet/`) and wiring it up via the `global.json` `paths` feature introduced +in .NET 10. The examples below use .NET 11, but this workflow works with any SDK +version — prerelease or stable, current or future. + +The result is a fully isolated SDK that: + +- Does **not** modify the system-wide .NET installation. +- Is picked up automatically by `dotnet` commands run from the project root. +- Can be deleted with a single `rm -rf .dotnet/` to revert. + +### Target personas + +| Persona | What they need | Emphasis | +|---|---|---| +| **Cautious hobbyist** | Try a preview without risk | Reversibility, safety | +| **Team lead** | Evaluate a preview for the team | Install scripts, reproducibility | +| **OSS maintainer** | Test against previews in CI | CI integration, automation | +| **Curious developer** | Try a new feature NOW | Speed, minimal steps | + +## When to use + +- User wants to install a prerelease or specific .NET SDK version locally. +- User wants to test a new .NET feature without affecting other projects. +- User needs reproducible SDK setup for a team or CI pipeline. +- User asks about `global.json` `paths`, local SDK installs, or safe preview testing. +- User wants to test MAUI or other workloads on a preview SDK. + +## When NOT to use + +- User wants to install the SDK **system-wide** — direct them to the official + installer or `dotnet-install` without `--install-dir`. +- User is on a .NET version **older than 10** — the `paths` feature does not + exist; explain the requirement and stop. +- User needs a **runtime-only** installation (e.g., to run `dotnet app.dll`) — + `paths` applies to SDK resolution only, not apphost or `dotnet exec`. +- The project does **not use SDK-style commands** — `paths` has no effect + outside of the SDK resolver. + +## Inputs / Prerequisites + +| Input | Required | Default | Notes | +|---|---|---|---| +| Channel or version | No | `11.0` | e.g. `11.0`, `STS`, `LTS`, or an exact version like `11.0.100-preview.2.26159.112` | +| Quality | No | `preview` | One of: `daily`, `preview`, `GA` | +| Install directory | No | `.dotnet` | Relative to the project root | + +### Prerequisites the agent must verify before proceeding + +1. **A .NET 10+ SDK is installed globally** — run `dotnet --version` and confirm + the major version is ≥ 10. If not, stop and explain that the host `dotnet` + must be v10+ to understand the `paths` key. +2. **curl** is available (macOS/Linux) or **PowerShell** is available (Windows). + +## Workflow + +Follow these steps in order. Each step includes a checkpoint the agent must +verify before continuing. + +### Step 1 — Clarify what to install + +If the user did not specify a channel, quality, or exact version, ask: + +> What .NET SDK would you like to install locally? +> For example: "latest .NET 11 preview", ".NET 11 daily build", or a specific +> version like "11.0.100-preview.2.26159.112". + +Map the answer to `--channel` and `--quality` flags for the install script. + +**Checkpoint:** Agent has concrete values for `--channel` and `--quality` +(or `--version`). + +### Step 2 — Verify .NET 10+ host + +```bash +dotnet --version +``` + +Parse the major version. If < 10, stop and tell the user: + +> The global.json `paths` feature requires .NET 10 or later as the host SDK. +> Your current version is {version}. Please install .NET 10+ system-wide first. + +**Checkpoint:** Major version ≥ 10 confirmed. + +### Step 3 — Detect operating system + +Check the OS by running `uname -s 2>/dev/null`. If the command succeeds, use +the bash script path. If it fails (Windows), use PowerShell commands. + +```bash +# macOS / Linux — uname succeeds and returns "Darwin" or "Linux" +uname -s 2>/dev/null +``` + +> **Git Bash / Cygwin edge case:** If `uname -s` returns a value starting with +> `MINGW` or `CYGWIN`, the agent is running in Git Bash or Cygwin on Windows. +> Use the PowerShell install path instead. + +If `uname` fails or is unavailable, assume Windows and use PowerShell: + +```powershell +# Windows (PowerShell) +$IsWindows # $true +``` + +Determine which install script variant to use: +- macOS/Linux (uname succeeds) → `dotnet-install.sh` +- Windows (uname fails) → `dotnet-install.ps1` + +**Checkpoint:** OS detected; correct script variant selected. + +### Step 4 — Check for existing local SDK + +Before installing, check whether a `.dotnet/` directory already exists: + +```bash +test -d .dotnet && echo "exists" || echo "not found" +``` + +If `.dotnet/` already exists, ask the user: + +> A local SDK installation already exists at `.dotnet/`. Would you like to +> **update** it with the new version, or **skip** installation and keep the +> current one? + +- If the user wants to **update**, proceed to Step 5. +- If the user wants to **skip**, jump to Step 6 (identify the installed version). + +**Checkpoint:** Decision made — update or skip installation. + +### Step 5 — Download and run the install script + +> **Security note:** The install script is downloaded from Microsoft's official +> URL. Inform the user: "The install script is sourced from +> https://dot.net/v1/dotnet-install.sh (Microsoft official). You can inspect +> the script before running it if you'd like." + +**macOS / Linux:** + +```bash +# Step 1: Download the script +curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh + +# Step 2: Execute the script +bash /tmp/dotnet-install.sh \ + --channel \ + --quality \ + --install-dir .dotnet +``` + +**Windows (PowerShell):** + +```powershell +# Step 1: Download the script +Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TEMP\dotnet-install.ps1" + +# Step 2: Execute the script +& "$env:TEMP\dotnet-install.ps1" ` + -Channel ` + -Quality ` + -InstallDir .dotnet +``` + +If the user provided an exact `--version`, replace `--channel`/`--quality` with +`--version `. + +> **Safety note to communicate:** This downloads the SDK into a `.dotnet/` +> folder inside your project. Nothing outside this folder is modified. You can +> delete it at any time to revert. + +**Checkpoint:** Install script exits with code 0; `.dotnet/` directory exists +and contains a `dotnet` binary. + +### Step 6 — Identify the installed version + +```bash +.dotnet/dotnet --version +``` + +Record the exact version string (e.g., `11.0.100-preview.2.26159.112`). This is +needed for `global.json`. + +**Checkpoint:** Exact version string captured. + +### Step 7 — Install workloads (if requested) + +**Trigger keywords:** "MAUI", "mobile", "workload", "Blazor WASM", +"cross-platform". + +If the user mentioned any of the above, or if appropriate for their scenario, +ask whether they need workloads installed: + +> Do you need any .NET workloads installed on this local SDK? +> Common workloads: `maui`, `wasm-tools`, `maui-android`, `maui-ios`. + +If yes, install using the **local** `dotnet` binary: + +**macOS / Linux:** + +```bash +./.dotnet/dotnet workload install +``` + +**Windows (PowerShell):** + +```powershell +.\.dotnet\dotnet.exe workload install +``` + +> **No sudo needed:** Because the SDK lives in a user-owned `.dotnet/` folder, +> workload installs do not require elevated permissions. + +Verify the workload was installed: + +```bash +./.dotnet/dotnet workload list +``` + +> **Always use `./.dotnet/dotnet` for workload commands.** Workload metadata is +> stored relative to the dotnet root of the host process. Running `dotnet workload +> install` or `dotnet workload list` through the system host puts metadata in the +> wrong location — the system host's dotnet root, not `.dotnet/`. The `paths` +> feature routes SDK resolution but does **not** redirect workload storage. +> (See [dotnet/sdk#49825](https://github.com/dotnet/sdk/issues/49825).) + +**Checkpoint:** Requested workloads appear in `./.dotnet/dotnet workload list` +output. + +### Step 8 — Create or update global.json + +Write (or merge into an existing) `global.json` in the project root: + +```json +{ + "sdk": { + "version": "", + "allowPrerelease": true, + "rollForward": "latestFeature", + "paths": [".dotnet", "$host$"], + "errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally." + } +} +``` + +Key details: +- `paths` lists `.dotnet` first so the local SDK takes priority. +- `$host$` is a sentinel that tells the resolver to also search the system-wide + location as a fallback. +- `rollForward: "latestFeature"` allows patch-level flexibility. +- `errorMessage` tells other developers how to get the SDK if `.dotnet/` is + missing. + +**Merging into an existing global.json:** If `global.json` already exists, read +it first and preserve any existing properties (such as `msbuild-sdks`, `tools`, +or `test`). Add or update **only** the `sdk` section. When updating the `sdk` +section, preserve any existing `sdk` properties not being set (e.g., +user-defined `rollForward` or `workloadVersion`). Only add or overwrite: +`version`, `allowPrerelease`, `paths`, and `errorMessage`. If the `sdk` section +already exists, warn the user about any keys being overwritten. If `global.json` +does not exist, create it from scratch using the template above. + +> **Laziest path:** When the user just wants a quick setup and doesn't need +> version pinning or team reproducibility, use this minimal `global.json`: +> +> ```json +> { +> "sdk": { +> "paths": [".dotnet", "$host$"] +> } +> } +> ``` +> +> This is the shortest config that activates local SDK resolution. Use the full +> version-pinned template above when the user wants reproducibility, team setups, +> or CI integration. + +**Checkpoint:** `global.json` exists and contains the correct `sdk` section. + +### Step 9 — Update .gitignore + +Append `.dotnet/` to `.gitignore` if not already present: + +```bash +grep -qxF '.dotnet/' .gitignore 2>/dev/null || echo '.dotnet/' >> .gitignore +``` + +> **Safety note:** The `.dotnet/` folder can be hundreds of MB. It must not be +> committed to source control. + +**Checkpoint:** `.gitignore` contains `.dotnet/`. + +### Step 10 — Create team install scripts + +**Trigger:** Execute this step if the user mentioned "team", "share", "CI", +"pipeline", "reproducible", "colleagues", or "scripts" in their request. +Otherwise, offer: "Would you also like install scripts so your team can set up +with one command?" + +**install-dotnet.sh:** + +```bash +#!/usr/bin/env bash +set -euo pipefail +INSTALL_DIR=".dotnet" +CHANNEL="11.0" +QUALITY="preview" +SCRIPT_URL="https://dot.net/v1/dotnet-install.sh" +WORKLOADS=("${@}") # pass workload names as arguments, e.g. ./install-dotnet.sh maui wasm-tools + +echo "Installing .NET SDK ($CHANNEL, $QUALITY) into $INSTALL_DIR..." +echo "Downloading install script from $SCRIPT_URL (Microsoft official)..." +curl -sSL "$SCRIPT_URL" -o /tmp/dotnet-install.sh +bash /tmp/dotnet-install.sh \ + --channel "$CHANNEL" \ + --quality "$QUALITY" \ + --install-dir "$INSTALL_DIR" + +# Auto-detect installed version and create global.json +SDK_VERSION=$("$INSTALL_DIR/dotnet" --version) +echo "Installed SDK version: $SDK_VERSION" + +cat > global.json </dev/null || echo '.dotnet/' >> .gitignore +echo "Ensured .dotnet/ is in .gitignore" + +# Install workloads if any were requested +if [ ${#WORKLOADS[@]} -gt 0 ]; then + echo "Installing workloads: ${WORKLOADS[*]}..." + "$INSTALL_DIR/dotnet" workload install "${WORKLOADS[@]}" +fi + +echo "Done. SDK version: $SDK_VERSION" +``` + +```bash +chmod +x install-dotnet.sh +``` + +**install-dotnet.ps1:** + +```powershell +param( + [string[]]$Workloads = @() # e.g. .\install-dotnet.ps1 -Workloads maui,wasm-tools +) +$ErrorActionPreference = 'Stop' +$installDir = '.dotnet' +$channel = '11.0' +$quality = 'preview' +$scriptUrl = 'https://dot.net/v1/dotnet-install.ps1' + +Write-Host "Installing .NET SDK ($channel, $quality) into $installDir..." +Write-Host "Downloading install script from $scriptUrl (Microsoft official)..." +Invoke-WebRequest -Uri $scriptUrl -OutFile "$env:TEMP\dotnet-install.ps1" +& "$env:TEMP\dotnet-install.ps1" ` + -Channel $channel ` + -Quality $quality ` + -InstallDir $installDir + +# Auto-detect installed version and create global.json +$sdkVersion = & "$installDir\dotnet.exe" --version +Write-Host "Installed SDK version: $sdkVersion" + +@" +{ + "sdk": { + "version": "$sdkVersion", + "allowPrerelease": true, + "rollForward": "latestFeature", + "paths": [".dotnet", "`$host`$"], + "errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally." + } +} +"@ | Set-Content -Path 'global.json' -Encoding UTF8 +Write-Host "Created global.json pinned to $sdkVersion" + +# Update .gitignore +if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\\.dotnet/$' -Quiet)) { + Add-Content -Path .gitignore -Value '.dotnet/' +} +Write-Host "Ensured .dotnet/ is in .gitignore" + +# Install workloads if any were requested +if ($Workloads.Count -gt 0) { + Write-Host "Installing workloads: $($Workloads -join ', ')..." + & "$installDir\dotnet.exe" workload install @Workloads +} + +Write-Host "Done. SDK version: $sdkVersion" +``` + +These scripts should be committed to the repo so teammates can run them. + +**Checkpoint:** Scripts are executable and match the channel/quality used in +Step 5. + +### Step 11 — Verify SDK resolution + +```bash +dotnet --version +``` + +Run this from the project root (where `global.json` lives). The output should +match the version installed in Step 6. + +If it does not match, troubleshoot: +- Is `global.json` in the current directory or a parent? +- Does `paths` contain `.dotnet`? +- Is the host `dotnet` v10+? + +**Checkpoint:** `dotnet --version` output matches the locally installed version. + +### Step 12 — Summarize and explain cleanup + +Tell the user what was done: + +> ✅ **Local SDK setup complete.** +> +> - Installed .NET SDK {version} into `.dotnet/`. +> - Configured `global.json` to resolve the local SDK first. +> - Added `.dotnet/` to `.gitignore`. +> {if scripts created: - Created `install-dotnet.sh` and `install-dotnet.ps1` +> for your team.} +> +> **To clean up later:** +> 1. Delete the `.dotnet/` directory: `rm -rf .dotnet/` +> (This also removes any installed workloads automatically.) +> 2. Remove the `paths` and `errorMessage` keys from `global.json`. +> 3. (Optional) Delete `install-dotnet.sh` / `install-dotnet.ps1`. +> 4. Run `dotnet build` to confirm your project works with the system-wide SDK +> again. +> +> Your system-wide .NET installation was never modified. + +## Validation + +After completing the workflow, verify: + +1. `dotnet --version` (from project root) shows the locally installed version. +2. `dotnet --info` shows the SDK path pointing to `.dotnet/`. +3. `dotnet build` (if a project exists) succeeds with the local SDK. +4. Running `dotnet --version` from **outside** the project still shows the + system-wide SDK (confirming no global side effects). + +## Common pitfalls + +| Pitfall | Cause | Fix | +|---|---|---| +| `paths` key is ignored | Host `dotnet` is < v10 | Install .NET 10+ system-wide | +| Wrong SDK resolves | `global.json` is in a parent directory | Check for `global.json` files up the directory tree | +| `dotnet app.dll` uses wrong runtime | `paths` applies to SDK resolution only, not apphost or `dotnet exec` | Use `dotnet run` instead, or set `DOTNET_ROOT` | +| `.dotnet/` is huge | SDKs include targeting packs, templates, etc. | Expected; always gitignore | +| Install script fails on proxy/firewall | Corporate network blocks `dot.net` | Download the script and SDK manually; use `--install-dir` | +| Teammates get "SDK not found" | `.dotnet/` is gitignored and they haven't run the install script | Ensure `errorMessage` in `global.json` directs them to the script | +| CI build fails | CI image doesn't have .NET 10+ host | Add a step to install .NET 10+ globally first, then run the local install script | +| MAUI templates not available | Forgot to install workloads on the local SDK | Run `./.dotnet/dotnet workload install maui` using the local binary | diff --git a/tests/dotnet/setup-local-sdk/eval.yaml b/tests/dotnet/setup-local-sdk/eval.yaml new file mode 100644 index 000000000..f03a96b7f --- /dev/null +++ b/tests/dotnet/setup-local-sdk/eval.yaml @@ -0,0 +1,99 @@ +scenarios: + - name: "Basic local SDK setup with .NET 11 preview" + prompt: "I want to try the latest .NET 11 preview in my project without messing up my global install. Can you set it up locally?" + assertions: + - type: "output_contains" + value: "dotnet-install" + - type: "output_contains" + value: "global.json" + - type: "output_contains" + value: ".dotnet" + - type: "output_contains" + value: ".gitignore" + rubric: + - "The agent verified the host dotnet version is 10 or later before proceeding" + - "The agent ran the dotnet-install script with --install-dir .dotnet" + - "The agent created or updated global.json with a paths array containing .dotnet" + - "The agent added .dotnet/ to .gitignore" + - "The agent verified the local SDK resolves correctly with dotnet --version" + - "The agent explained how to clean up (delete .dotnet/ and revert global.json)" + timeout: 120 + + - name: "Install a specific SDK version locally" + prompt: "Install .NET SDK version 11.0.100-preview.2.26159.112 locally in this project using global.json paths." + assertions: + - type: "output_contains" + value: "11.0.100-preview.2.26159.112" + - type: "output_contains" + value: "--version" + - type: "output_contains" + value: "global.json" + rubric: + - "The agent used --version instead of --channel/--quality to install the exact version requested" + - "The agent set the exact version in global.json sdk.version" + - "The agent configured the paths array in global.json" + - "The agent did not prompt for channel or quality since an exact version was given" + - "The agent verified the installed version matches the requested version" + timeout: 120 + + - name: "Create team install scripts for reproducible setup" + prompt: "Set up a local .NET 11 preview SDK and create install scripts so my teammates can reproduce the setup on any OS." + assertions: + - type: "output_contains" + value: "install-dotnet.sh" + - type: "output_contains" + value: "install-dotnet.ps1" + - type: "output_contains" + value: "global.json" + rubric: + - "The agent created install-dotnet.sh with the correct channel and quality flags" + - "The agent created install-dotnet.ps1 with the correct channel and quality flags" + - "The agent made install-dotnet.sh executable (chmod +x)" + - "The agent configured global.json with an errorMessage directing users to the install scripts" + - "The agent explained that teammates should run the install script after cloning" + timeout: 120 + + - name: "Verify SDK resolution after local setup" + prompt: "I already have a .dotnet/ folder and global.json with paths configured, but dotnet --version still shows my global SDK. Help me fix it." + assertions: + - type: "output_contains" + value: "dotnet --version" + - type: "output_contains" + value: "global.json" + rubric: + - "The agent checked the host dotnet version is 10 or later" + - "The agent inspected the existing global.json to verify the paths array is correct" + - "The agent verified that .dotnet/ contains a valid SDK installation" + - "The agent checked for global.json files in parent directories that might interfere" + - "The agent mentioned the common pitfall about global.json in parent directories potentially overriding settings" + - "The agent suggested concrete fixes based on the diagnosis" + timeout: 120 + + - name: "Detect incompatible .NET host version" + prompt: "Set up a local .NET 11 preview SDK for my project." + rubric: + - "The agent checked the host dotnet version" + - "The agent detected or mentioned that .NET 10+ host is required" + - "The agent instructed the user to install .NET 10+ system-wide before proceeding" + timeout: 120 + + - name: "Handle existing local SDK installation" + prompt: "Set up a local .NET 11 preview. I already have a .dotnet folder from a previous setup." + rubric: + - "The agent detected the existing .dotnet/ directory" + - "The agent asked the user whether to update or skip installation" + - "The agent proceeded based on user preference" + timeout: 120 + + - name: "Set up local SDK with MAUI workload" + prompt: "I want to test my .NET MAUI app against the latest .NET 11 preview. Set up a local SDK with the MAUI workload." + assertions: + - type: "output_contains" + value: "workload install maui" + rubric: + - "The agent installed the .NET 11 preview SDK locally" + - "The agent installed the MAUI workload on the local SDK" + - "The agent used ./.dotnet/dotnet (not plain dotnet) for workload install and list commands" + - "The agent configured global.json with paths" + - "The agent mentioned that no sudo is needed for local workload installs" + timeout: 180 From a3f3e7019b1c3590db9f450f1dca38c92a37fab0 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 15:09:24 +0200 Subject: [PATCH 2/7] Address PR review feedback - Add Windows PowerShell equivalents for version check (Step 6) and workload list commands - Fix rollForward description: latestFeature rolls across feature bands, not just patches - Add global.json backup in team install scripts before overwriting - Fix eval scenario: provide explicit host version (9.0.306) in prompt so the incompatible-host test is deterministic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dotnet/skills/setup-local-sdk/SKILL.md | 35 ++++++++++++++++--- tests/dotnet/setup-local-sdk/eval.yaml | 6 ++-- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/plugins/dotnet/skills/setup-local-sdk/SKILL.md b/plugins/dotnet/skills/setup-local-sdk/SKILL.md index 119258dd6..c52cf07d7 100644 --- a/plugins/dotnet/skills/setup-local-sdk/SKILL.md +++ b/plugins/dotnet/skills/setup-local-sdk/SKILL.md @@ -192,8 +192,16 @@ and contains a `dotnet` binary. ### Step 6 — Identify the installed version +**macOS / Linux:** + ```bash -.dotnet/dotnet --version +./.dotnet/dotnet --version +``` + +**Windows (PowerShell):** + +```powershell +.\.dotnet\dotnet.exe --version ``` Record the exact version string (e.g., `11.0.100-preview.2.26159.112`). This is @@ -231,10 +239,18 @@ If yes, install using the **local** `dotnet` binary: Verify the workload was installed: +**macOS / Linux:** + ```bash ./.dotnet/dotnet workload list ``` +**Windows (PowerShell):** + +```powershell +.\.dotnet\dotnet.exe workload list +``` + > **Always use `./.dotnet/dotnet` for workload commands.** Workload metadata is > stored relative to the dotnet root of the host process. Running `dotnet workload > install` or `dotnet workload list` through the system host puts metadata in the @@ -265,7 +281,8 @@ Key details: - `paths` lists `.dotnet` first so the local SDK takes priority. - `$host$` is a sentinel that tells the resolver to also search the system-wide location as a fallback. -- `rollForward: "latestFeature"` allows patch-level flexibility. +- `rollForward: "latestFeature"` allows roll-forward to later feature bands + (including patches), not just patch-level updates. - `errorMessage` tells other developers how to get the SDK if `.dotnet/` is missing. @@ -334,10 +351,15 @@ bash /tmp/dotnet-install.sh \ --quality "$QUALITY" \ --install-dir "$INSTALL_DIR" -# Auto-detect installed version and create global.json +# Auto-detect installed version and create/update global.json SDK_VERSION=$("$INSTALL_DIR/dotnet" --version) echo "Installed SDK version: $SDK_VERSION" +if [ -f global.json ]; then + echo "WARNING: global.json already exists. Backing up to global.json.bak" + cp global.json global.json.bak +fi + cat > global.json < Date: Wed, 8 Apr 2026 15:11:17 +0200 Subject: [PATCH 3/7] Trim SKILL.md to 500 lines, condense notes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dotnet/skills/setup-local-sdk/SKILL.md | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/plugins/dotnet/skills/setup-local-sdk/SKILL.md b/plugins/dotnet/skills/setup-local-sdk/SKILL.md index c52cf07d7..a6ea8a234 100644 --- a/plugins/dotnet/skills/setup-local-sdk/SKILL.md +++ b/plugins/dotnet/skills/setup-local-sdk/SKILL.md @@ -107,24 +107,19 @@ Check the OS by running `uname -s 2>/dev/null`. If the command succeeds, use the bash script path. If it fails (Windows), use PowerShell commands. ```bash -# macOS / Linux — uname succeeds and returns "Darwin" or "Linux" uname -s 2>/dev/null ``` > **Git Bash / Cygwin edge case:** If `uname -s` returns a value starting with -> `MINGW` or `CYGWIN`, the agent is running in Git Bash or Cygwin on Windows. -> Use the PowerShell install path instead. +> `MINGW` or `CYGWIN`, use the PowerShell install path instead. If `uname` fails or is unavailable, assume Windows and use PowerShell: ```powershell -# Windows (PowerShell) $IsWindows # $true ``` -Determine which install script variant to use: -- macOS/Linux (uname succeeds) → `dotnet-install.sh` -- Windows (uname fails) → `dotnet-install.ps1` +Determine which script to use: macOS/Linux → `dotnet-install.sh`, Windows → `dotnet-install.ps1`. **Checkpoint:** OS detected; correct script variant selected. @@ -149,18 +144,13 @@ If `.dotnet/` already exists, ask the user: ### Step 5 — Download and run the install script -> **Security note:** The install script is downloaded from Microsoft's official -> URL. Inform the user: "The install script is sourced from -> https://dot.net/v1/dotnet-install.sh (Microsoft official). You can inspect -> the script before running it if you'd like." +> **Security note:** The install script is from Microsoft's official URL +> (https://dot.net/v1/dotnet-install.sh). The user can inspect it before running. **macOS / Linux:** ```bash -# Step 1: Download the script curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh - -# Step 2: Execute the script bash /tmp/dotnet-install.sh \ --channel \ --quality \ @@ -170,10 +160,7 @@ bash /tmp/dotnet-install.sh \ **Windows (PowerShell):** ```powershell -# Step 1: Download the script Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TEMP\dotnet-install.ps1" - -# Step 2: Execute the script & "$env:TEMP\dotnet-install.ps1" ` -Channel ` -Quality ` @@ -183,9 +170,8 @@ Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TE If the user provided an exact `--version`, replace `--channel`/`--quality` with `--version `. -> **Safety note to communicate:** This downloads the SDK into a `.dotnet/` -> folder inside your project. Nothing outside this folder is modified. You can -> delete it at any time to revert. +> **Safety note:** This downloads the SDK into `.dotnet/` inside the project. +> Nothing outside this folder is modified. Delete it at any time to revert. **Checkpoint:** Install script exits with code 0; `.dotnet/` directory exists and contains a `dotnet` binary. From 528b5a218b6eada5dca5f21883a990d93eb8d447 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 15:27:19 +0200 Subject: [PATCH 4/7] Address second review round and improve eval SKILL.md: - Windows -Version flag for exact installs (vs --version on bash) - Workload note includes both ./.dotnet/dotnet and .\.dotnet\dotnet.exe - Cleanup includes Windows Remove-Item equivalent - Revert/delete instructions include both OS forms eval.yaml: - Remove brittle --version assertion; use rubric for version flag check - Increase all scenario timeouts from 120s to 180s (many were timing out) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dotnet/skills/setup-local-sdk/SKILL.md | 33 +++++++++---------- tests/dotnet/setup-local-sdk/eval.yaml | 16 ++++----- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/plugins/dotnet/skills/setup-local-sdk/SKILL.md b/plugins/dotnet/skills/setup-local-sdk/SKILL.md index a6ea8a234..33df75851 100644 --- a/plugins/dotnet/skills/setup-local-sdk/SKILL.md +++ b/plugins/dotnet/skills/setup-local-sdk/SKILL.md @@ -25,7 +25,8 @@ The result is a fully isolated SDK that: - Does **not** modify the system-wide .NET installation. - Is picked up automatically by `dotnet` commands run from the project root. -- Can be deleted with a single `rm -rf .dotnet/` to revert. +- Can be deleted with `rm -rf .dotnet/` (macOS/Linux) or + `Remove-Item -Recurse -Force .\.dotnet` (Windows) to revert. ### Target personas @@ -99,8 +100,6 @@ Parse the major version. If < 10, stop and tell the user: > The global.json `paths` feature requires .NET 10 or later as the host SDK. > Your current version is {version}. Please install .NET 10+ system-wide first. -**Checkpoint:** Major version ≥ 10 confirmed. - ### Step 3 — Detect operating system Check the OS by running `uname -s 2>/dev/null`. If the command succeeds, use @@ -121,8 +120,6 @@ $IsWindows # $true Determine which script to use: macOS/Linux → `dotnet-install.sh`, Windows → `dotnet-install.ps1`. -**Checkpoint:** OS detected; correct script variant selected. - ### Step 4 — Check for existing local SDK Before installing, check whether a `.dotnet/` directory already exists: @@ -140,8 +137,6 @@ If `.dotnet/` already exists, ask the user: - If the user wants to **update**, proceed to Step 5. - If the user wants to **skip**, jump to Step 6 (identify the installed version). -**Checkpoint:** Decision made — update or skip installation. - ### Step 5 — Download and run the install script > **Security note:** The install script is from Microsoft's official URL @@ -167,8 +162,9 @@ Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TE -InstallDir .dotnet ``` -If the user provided an exact `--version`, replace `--channel`/`--quality` with -`--version `. +If the user provided an exact version, replace `--channel`/`--quality` with +`--version ` on macOS/Linux, or replace `-Channel`/`-Quality` with +`-Version ` on Windows PowerShell. > **Safety note:** This downloads the SDK into `.dotnet/` inside the project. > Nothing outside this folder is modified. Delete it at any time to revert. @@ -237,15 +233,17 @@ Verify the workload was installed: .\.dotnet\dotnet.exe workload list ``` -> **Always use `./.dotnet/dotnet` for workload commands.** Workload metadata is +> **Always use the local dotnet binary for workload commands** (`./.dotnet/dotnet` +> on macOS/Linux or `.\.dotnet\dotnet.exe` on Windows). Workload metadata is > stored relative to the dotnet root of the host process. Running `dotnet workload > install` or `dotnet workload list` through the system host puts metadata in the > wrong location — the system host's dotnet root, not `.dotnet/`. The `paths` > feature routes SDK resolution but does **not** redirect workload storage. > (See [dotnet/sdk#49825](https://github.com/dotnet/sdk/issues/49825).) -**Checkpoint:** Requested workloads appear in `./.dotnet/dotnet workload list` -output. +**Checkpoint:** Requested workloads appear in the local workload list output +(`./.dotnet/dotnet workload list` on macOS/Linux or +`.\.dotnet\dotnet.exe workload list` on Windows). ### Step 8 — Create or update global.json @@ -467,8 +465,9 @@ Tell the user what was done: > for your team.} > > **To clean up later:** -> 1. Delete the `.dotnet/` directory: `rm -rf .dotnet/` -> (This also removes any installed workloads automatically.) +> 1. Delete the `.dotnet/` directory: `rm -rf .dotnet/` (macOS/Linux) or +> `Remove-Item -Recurse -Force .\.dotnet` (Windows). +> This also removes any installed workloads automatically. > 2. Remove the `paths` and `errorMessage` keys from `global.json`. > 3. (Optional) Delete `install-dotnet.sh` / `install-dotnet.ps1`. > 4. Run `dotnet build` to confirm your project works with the system-wide SDK @@ -495,6 +494,6 @@ After completing the workflow, verify: | `dotnet app.dll` uses wrong runtime | `paths` applies to SDK resolution only, not apphost or `dotnet exec` | Use `dotnet run` instead, or set `DOTNET_ROOT` | | `.dotnet/` is huge | SDKs include targeting packs, templates, etc. | Expected; always gitignore | | Install script fails on proxy/firewall | Corporate network blocks `dot.net` | Download the script and SDK manually; use `--install-dir` | -| Teammates get "SDK not found" | `.dotnet/` is gitignored and they haven't run the install script | Ensure `errorMessage` in `global.json` directs them to the script | -| CI build fails | CI image doesn't have .NET 10+ host | Add a step to install .NET 10+ globally first, then run the local install script | -| MAUI templates not available | Forgot to install workloads on the local SDK | Run `./.dotnet/dotnet workload install maui` using the local binary | +| Teammates get "SDK not found" | `.dotnet/` is gitignored, they haven't run install script | Ensure `errorMessage` in `global.json` directs them to the script | +| CI build fails | CI image lacks .NET 10+ host | Install .NET 10+ globally first, then run local install script | +| MAUI templates not available | Workloads not on local SDK | Run `./.dotnet/dotnet workload install maui` (local binary) | diff --git a/tests/dotnet/setup-local-sdk/eval.yaml b/tests/dotnet/setup-local-sdk/eval.yaml index 29bf492a4..7a57ba289 100644 --- a/tests/dotnet/setup-local-sdk/eval.yaml +++ b/tests/dotnet/setup-local-sdk/eval.yaml @@ -17,24 +17,22 @@ scenarios: - "The agent added .dotnet/ to .gitignore" - "The agent verified the local SDK resolves correctly with dotnet --version" - "The agent explained how to clean up (delete .dotnet/ and revert global.json)" - timeout: 120 + timeout: 180 - name: "Install a specific SDK version locally" prompt: "Install .NET SDK version 11.0.100-preview.2.26159.112 locally in this project using global.json paths." assertions: - type: "output_contains" value: "11.0.100-preview.2.26159.112" - - type: "output_contains" - value: "--version" - type: "output_contains" value: "global.json" rubric: - - "The agent used --version instead of --channel/--quality to install the exact version requested" + - "The agent used a version flag (--version or -Version) instead of channel/quality to install the exact version requested" - "The agent set the exact version in global.json sdk.version" - "The agent configured the paths array in global.json" - "The agent did not prompt for channel or quality since an exact version was given" - "The agent verified the installed version matches the requested version" - timeout: 120 + timeout: 180 - name: "Create team install scripts for reproducible setup" prompt: "Set up a local .NET 11 preview SDK and create install scripts so my teammates can reproduce the setup on any OS." @@ -51,7 +49,7 @@ scenarios: - "The agent made install-dotnet.sh executable (chmod +x)" - "The agent configured global.json with an errorMessage directing users to the install scripts" - "The agent explained that teammates should run the install script after cloning" - timeout: 120 + timeout: 180 - name: "Verify SDK resolution after local setup" prompt: "I already have a .dotnet/ folder and global.json with paths configured, but dotnet --version still shows my global SDK. Help me fix it." @@ -67,7 +65,7 @@ scenarios: - "The agent checked for global.json files in parent directories that might interfere" - "The agent mentioned the common pitfall about global.json in parent directories potentially overriding settings" - "The agent suggested concrete fixes based on the diagnosis" - timeout: 120 + timeout: 180 - name: "Detect incompatible .NET host version" prompt: "Set up a local .NET 11 preview SDK for my project. My current dotnet --version shows 9.0.306." @@ -75,7 +73,7 @@ scenarios: - "The agent detected that the host version (9.0.306) is below the .NET 10 requirement" - "The agent mentioned that .NET 10+ host is required for the paths feature" - "The agent instructed the user to install .NET 10+ system-wide before proceeding" - timeout: 120 + timeout: 180 - name: "Handle existing local SDK installation" prompt: "Set up a local .NET 11 preview. I already have a .dotnet folder from a previous setup." @@ -83,7 +81,7 @@ scenarios: - "The agent detected the existing .dotnet/ directory" - "The agent asked the user whether to update or skip installation" - "The agent proceeded based on user preference" - timeout: 120 + timeout: 180 - name: "Set up local SDK with MAUI workload" prompt: "I want to test my .NET MAUI app against the latest .NET 11 preview. Set up a local SDK with the MAUI workload." From 31252f0e524d9c3182e432b20ff4efca7ff2eb43 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 15:59:13 +0200 Subject: [PATCH 5/7] Major rewrite: halve skill size, improve activation and eval 239 lines, -52%): - Rewrite description: intent-focused USE FOR/DO NOT USE FOR pattern with activation keywords (MAUI, existing, testing, team) - Remove personas table, checkpoint markers, verbose notes - Condense all sections while preserving complete workflow 5 entries) - Remove redundant Validation section 5 scenarios): - Drop 'Handle existing' (handled naturally by Step 4) - Drop 'Verify SDK resolution' (covered by basic setup rubric) - Add expect_tools: ['bash'] to actionable scenarios - Reduce rubric items to 3-4 per scenario - Incompatible host scenario: 60s timeout (quick response) Expected improvements: - Token usage: -50% (skill is half the size) - Activation: USE FOR keywords match all prompts - Variance: fewer scenarios = less variability - Eval time: -40% (fewer scenarios, shorter timeouts) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dotnet/skills/setup-local-sdk/SKILL.md | 410 ++++-------------- tests/dotnet/setup-local-sdk/eval.yaml | 90 ++-- 2 files changed, 105 insertions(+), 395 deletions(-) diff --git a/plugins/dotnet/skills/setup-local-sdk/SKILL.md b/plugins/dotnet/skills/setup-local-sdk/SKILL.md index 33df75851..54f8b3673 100644 --- a/plugins/dotnet/skills/setup-local-sdk/SKILL.md +++ b/plugins/dotnet/skills/setup-local-sdk/SKILL.md @@ -1,60 +1,33 @@ --- name: setup-local-sdk description: > - Set up a local .NET SDK installation using the global.json `paths` feature - (.NET 10+), including workload installation (MAUI, wasm-tools, etc.). Downloads a - prerelease or specific SDK version into a project-local `.dotnet/` directory - and configures global.json so `dotnet` resolves it automatically, without - touching the system-wide installation. - Use when a user wants to try a .NET preview, test against a specific SDK - version, or create reproducible team/CI install scripts. - Do NOT use when the user wants a system-wide install, is on .NET < 10, needs a - runtime-only install, or is not using SDK-style commands. + Install a .NET SDK locally for safe preview testing, specific-version pinning, or + reproducible team setups — without modifying the system-wide installation. + USE FOR: trying .NET previews safely, testing specific SDK versions, installing MAUI + or other workloads on a preview, updating or replacing an existing local SDK, + creating reproducible team/CI install scripts, configuring global.json paths. + DO NOT USE FOR: system-wide SDK installs, .NET hosts older than 10, runtime-only + installs, or projects not using SDK-style commands. --- # setup-local-sdk ## Purpose -Guide the user through installing a .NET SDK into a project-local directory -(`.dotnet/`) and wiring it up via the `global.json` `paths` feature introduced -in .NET 10. The examples below use .NET 11, but this workflow works with any SDK -version — prerelease or stable, current or future. +Guide the user through installing a .NET SDK into a project-local `.dotnet/` +directory and wiring it up via the `global.json` `paths` feature (.NET 10+). +The examples use .NET 11, but this works with any version — prerelease or stable. The result is a fully isolated SDK that: - - Does **not** modify the system-wide .NET installation. -- Is picked up automatically by `dotnet` commands run from the project root. -- Can be deleted with `rm -rf .dotnet/` (macOS/Linux) or - `Remove-Item -Recurse -Force .\.dotnet` (Windows) to revert. - -### Target personas - -| Persona | What they need | Emphasis | -|---|---|---| -| **Cautious hobbyist** | Try a preview without risk | Reversibility, safety | -| **Team lead** | Evaluate a preview for the team | Install scripts, reproducibility | -| **OSS maintainer** | Test against previews in CI | CI integration, automation | -| **Curious developer** | Try a new feature NOW | Speed, minimal steps | - -## When to use - -- User wants to install a prerelease or specific .NET SDK version locally. -- User wants to test a new .NET feature without affecting other projects. -- User needs reproducible SDK setup for a team or CI pipeline. -- User asks about `global.json` `paths`, local SDK installs, or safe preview testing. -- User wants to test MAUI or other workloads on a preview SDK. +- Is picked up automatically by `dotnet` commands from the project root. +- Can be deleted to revert (`rm -rf .dotnet/` or `Remove-Item -Recurse -Force .\.dotnet`). ## When NOT to use -- User wants to install the SDK **system-wide** — direct them to the official - installer or `dotnet-install` without `--install-dir`. -- User is on a .NET version **older than 10** — the `paths` feature does not - exist; explain the requirement and stop. -- User needs a **runtime-only** installation (e.g., to run `dotnet app.dll`) — - `paths` applies to SDK resolution only, not apphost or `dotnet exec`. -- The project does **not use SDK-style commands** — `paths` has no effect - outside of the SDK resolver. +- User wants a **system-wide** install — direct to the official installer. +- Host `dotnet` is **older than v10** — `paths` doesn't exist; explain and stop. +- User needs a **runtime-only** install — `paths` applies to SDK resolution only. ## Inputs / Prerequisites @@ -64,30 +37,18 @@ The result is a fully isolated SDK that: | Quality | No | `preview` | One of: `daily`, `preview`, `GA` | | Install directory | No | `.dotnet` | Relative to the project root | -### Prerequisites the agent must verify before proceeding +### Prerequisites -1. **A .NET 10+ SDK is installed globally** — run `dotnet --version` and confirm - the major version is ≥ 10. If not, stop and explain that the host `dotnet` - must be v10+ to understand the `paths` key. -2. **curl** is available (macOS/Linux) or **PowerShell** is available (Windows). +1. **A .NET 10+ SDK is installed globally** — run `dotnet --version`; major ≥ 10. +2. **curl** (macOS/Linux) or **PowerShell** (Windows) is available. ## Workflow -Follow these steps in order. Each step includes a checkpoint the agent must -verify before continuing. - ### Step 1 — Clarify what to install -If the user did not specify a channel, quality, or exact version, ask: - -> What .NET SDK would you like to install locally? -> For example: "latest .NET 11 preview", ".NET 11 daily build", or a specific -> version like "11.0.100-preview.2.26159.112". - -Map the answer to `--channel` and `--quality` flags for the install script. - -**Checkpoint:** Agent has concrete values for `--channel` and `--quality` -(or `--version`). +If the user didn't specify, ask what .NET SDK version they want (e.g., "latest +.NET 11 preview" or an exact version like `11.0.100-preview.2.26159.112`). +Map the answer to `--channel`/`--quality` or `--version` flags. ### Step 2 — Verify .NET 10+ host @@ -95,160 +56,68 @@ Map the answer to `--channel` and `--quality` flags for the install script. dotnet --version ``` -Parse the major version. If < 10, stop and tell the user: - -> The global.json `paths` feature requires .NET 10 or later as the host SDK. -> Your current version is {version}. Please install .NET 10+ system-wide first. +If major version < 10, stop: the `paths` feature requires .NET 10+. ### Step 3 — Detect operating system -Check the OS by running `uname -s 2>/dev/null`. If the command succeeds, use -the bash script path. If it fails (Windows), use PowerShell commands. - -```bash -uname -s 2>/dev/null -``` - -> **Git Bash / Cygwin edge case:** If `uname -s` returns a value starting with -> `MINGW` or `CYGWIN`, use the PowerShell install path instead. - -If `uname` fails or is unavailable, assume Windows and use PowerShell: - -```powershell -$IsWindows # $true -``` - -Determine which script to use: macOS/Linux → `dotnet-install.sh`, Windows → `dotnet-install.ps1`. +Run `uname -s 2>/dev/null`. If it succeeds → use bash/`dotnet-install.sh`. +If it fails or returns `MINGW`/`CYGWIN` → use PowerShell/`dotnet-install.ps1`. ### Step 4 — Check for existing local SDK -Before installing, check whether a `.dotnet/` directory already exists: - ```bash test -d .dotnet && echo "exists" || echo "not found" ``` -If `.dotnet/` already exists, ask the user: - -> A local SDK installation already exists at `.dotnet/`. Would you like to -> **update** it with the new version, or **skip** installation and keep the -> current one? - -- If the user wants to **update**, proceed to Step 5. -- If the user wants to **skip**, jump to Step 6 (identify the installed version). +If `.dotnet/` exists, ask: update with the new version, or skip and keep it? ### Step 5 — Download and run the install script -> **Security note:** The install script is from Microsoft's official URL -> (https://dot.net/v1/dotnet-install.sh). The user can inspect it before running. - **macOS / Linux:** ```bash curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh -bash /tmp/dotnet-install.sh \ - --channel \ - --quality \ - --install-dir .dotnet +bash /tmp/dotnet-install.sh --channel --quality --install-dir .dotnet ``` **Windows (PowerShell):** ```powershell Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TEMP\dotnet-install.ps1" -& "$env:TEMP\dotnet-install.ps1" ` - -Channel ` - -Quality ` - -InstallDir .dotnet +& "$env:TEMP\dotnet-install.ps1" -Channel -Quality -InstallDir .dotnet ``` -If the user provided an exact version, replace `--channel`/`--quality` with -`--version ` on macOS/Linux, or replace `-Channel`/`-Quality` with -`-Version ` on Windows PowerShell. - -> **Safety note:** This downloads the SDK into `.dotnet/` inside the project. -> Nothing outside this folder is modified. Delete it at any time to revert. - -**Checkpoint:** Install script exits with code 0; `.dotnet/` directory exists -and contains a `dotnet` binary. +For exact versions: use `--version ` (bash) or `-Version ` (PowerShell) +instead of channel/quality flags. The install script is from Microsoft's official +URL (https://dot.net/v1/dotnet-install.sh). ### Step 6 — Identify the installed version -**macOS / Linux:** - ```bash -./.dotnet/dotnet --version -``` - -**Windows (PowerShell):** - -```powershell -.\.dotnet\dotnet.exe --version +./.dotnet/dotnet --version # macOS/Linux +.\.dotnet\dotnet.exe --version # Windows ``` -Record the exact version string (e.g., `11.0.100-preview.2.26159.112`). This is -needed for `global.json`. - -**Checkpoint:** Exact version string captured. +Record the exact version string (e.g., `11.0.100-preview.2.26159.112`) for `global.json`. ### Step 7 — Install workloads (if requested) -**Trigger keywords:** "MAUI", "mobile", "workload", "Blazor WASM", -"cross-platform". - -If the user mentioned any of the above, or if appropriate for their scenario, -ask whether they need workloads installed: - -> Do you need any .NET workloads installed on this local SDK? -> Common workloads: `maui`, `wasm-tools`, `maui-android`, `maui-ios`. - -If yes, install using the **local** `dotnet` binary: - -**macOS / Linux:** +If the user mentioned MAUI, mobile, workload, Blazor WASM, or cross-platform, +install using the **local** binary (no sudo needed): ```bash -./.dotnet/dotnet workload install +./.dotnet/dotnet workload install # macOS/Linux +.\.dotnet\dotnet.exe workload install # Windows ``` -**Windows (PowerShell):** +Verify: `./.dotnet/dotnet workload list` (or `.\.dotnet\dotnet.exe workload list`). -```powershell -.\.dotnet\dotnet.exe workload install -``` - -> **No sudo needed:** Because the SDK lives in a user-owned `.dotnet/` folder, -> workload installs do not require elevated permissions. - -Verify the workload was installed: - -**macOS / Linux:** - -```bash -./.dotnet/dotnet workload list -``` - -**Windows (PowerShell):** - -```powershell -.\.dotnet\dotnet.exe workload list -``` - -> **Always use the local dotnet binary for workload commands** (`./.dotnet/dotnet` -> on macOS/Linux or `.\.dotnet\dotnet.exe` on Windows). Workload metadata is -> stored relative to the dotnet root of the host process. Running `dotnet workload -> install` or `dotnet workload list` through the system host puts metadata in the -> wrong location — the system host's dotnet root, not `.dotnet/`. The `paths` -> feature routes SDK resolution but does **not** redirect workload storage. -> (See [dotnet/sdk#49825](https://github.com/dotnet/sdk/issues/49825).) - -**Checkpoint:** Requested workloads appear in the local workload list output -(`./.dotnet/dotnet workload list` on macOS/Linux or -`.\.dotnet\dotnet.exe workload list` on Windows). +> **Always use the local dotnet binary for workload commands.** Workload metadata +> is stored relative to the host process's dotnet root. The system `dotnet` puts +> metadata in the wrong location. (See [dotnet/sdk#49825](https://github.com/dotnet/sdk/issues/49825).) ### Step 8 — Create or update global.json -Write (or merge into an existing) `global.json` in the project root: - ```json { "sdk": { @@ -261,60 +130,25 @@ Write (or merge into an existing) `global.json` in the project root: } ``` -Key details: -- `paths` lists `.dotnet` first so the local SDK takes priority. -- `$host$` is a sentinel that tells the resolver to also search the system-wide - location as a fallback. -- `rollForward: "latestFeature"` allows roll-forward to later feature bands - (including patches), not just patch-level updates. -- `errorMessage` tells other developers how to get the SDK if `.dotnet/` is - missing. - -**Merging into an existing global.json:** If `global.json` already exists, read -it first and preserve any existing properties (such as `msbuild-sdks`, `tools`, -or `test`). Add or update **only** the `sdk` section. When updating the `sdk` -section, preserve any existing `sdk` properties not being set (e.g., -user-defined `rollForward` or `workloadVersion`). Only add or overwrite: -`version`, `allowPrerelease`, `paths`, and `errorMessage`. If the `sdk` section -already exists, warn the user about any keys being overwritten. If `global.json` -does not exist, create it from scratch using the template above. - -> **Laziest path:** When the user just wants a quick setup and doesn't need -> version pinning or team reproducibility, use this minimal `global.json`: -> -> ```json -> { -> "sdk": { -> "paths": [".dotnet", "$host$"] -> } -> } -> ``` -> -> This is the shortest config that activates local SDK resolution. Use the full -> version-pinned template above when the user wants reproducibility, team setups, -> or CI integration. - -**Checkpoint:** `global.json` exists and contains the correct `sdk` section. +- `paths`: `.dotnet` first (local priority), `$host$` = system-wide fallback. +- `rollForward: "latestFeature"`: rolls forward across feature bands, not just patches. +- `errorMessage`: tells teammates how to get the SDK. -### Step 9 — Update .gitignore +If `global.json` already exists, preserve existing properties (`msbuild-sdks`, +`tools`, etc.) and only add/update the `sdk` section. Warn about overwrites. -Append `.dotnet/` to `.gitignore` if not already present: +**Minimal config** (when version pinning isn't needed): +`{"sdk":{"paths":[".dotnet","$host$"]}}` + +### Step 9 — Update .gitignore ```bash grep -qxF '.dotnet/' .gitignore 2>/dev/null || echo '.dotnet/' >> .gitignore ``` -> **Safety note:** The `.dotnet/` folder can be hundreds of MB. It must not be -> committed to source control. - -**Checkpoint:** `.gitignore` contains `.dotnet/`. - ### Step 10 — Create team install scripts -**Trigger:** Execute this step if the user mentioned "team", "share", "CI", -"pipeline", "reproducible", "colleagues", or "scripts" in their request. -Otherwise, offer: "Would you also like install scripts so your team can set up -with one command?" +Create if user mentioned "team", "share", "CI", "scripts", etc. Otherwise offer. **install-dotnet.sh:** @@ -324,26 +158,11 @@ set -euo pipefail INSTALL_DIR=".dotnet" CHANNEL="11.0" QUALITY="preview" -SCRIPT_URL="https://dot.net/v1/dotnet-install.sh" -WORKLOADS=("${@}") # pass workload names as arguments, e.g. ./install-dotnet.sh maui wasm-tools - -echo "Installing .NET SDK ($CHANNEL, $QUALITY) into $INSTALL_DIR..." -echo "Downloading install script from $SCRIPT_URL (Microsoft official)..." -curl -sSL "$SCRIPT_URL" -o /tmp/dotnet-install.sh -bash /tmp/dotnet-install.sh \ - --channel "$CHANNEL" \ - --quality "$QUALITY" \ - --install-dir "$INSTALL_DIR" - -# Auto-detect installed version and create/update global.json +WORKLOADS=("${@}") +curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh +bash /tmp/dotnet-install.sh --channel "$CHANNEL" --quality "$QUALITY" --install-dir "$INSTALL_DIR" SDK_VERSION=$("$INSTALL_DIR/dotnet" --version) -echo "Installed SDK version: $SDK_VERSION" - -if [ -f global.json ]; then - echo "WARNING: global.json already exists. Backing up to global.json.bak" - cp global.json global.json.bak -fi - +[ -f global.json ] && cp global.json global.json.bak cat > global.json < global.json </dev/null || echo '.dotnet/' >> .gitignore -echo "Ensured .dotnet/ is in .gitignore" - -# Install workloads if any were requested -if [ ${#WORKLOADS[@]} -gt 0 ]; then - echo "Installing workloads: ${WORKLOADS[*]}..." - "$INSTALL_DIR/dotnet" workload install "${WORKLOADS[@]}" -fi - -echo "Done. SDK version: $SDK_VERSION" +[ ${#WORKLOADS[@]} -gt 0 ] && "$INSTALL_DIR/dotnet" workload install "${WORKLOADS[@]}" +echo "Done. SDK: $SDK_VERSION" ``` ```bash @@ -377,32 +186,13 @@ chmod +x install-dotnet.sh **install-dotnet.ps1:** ```powershell -param( - [string[]]$Workloads = @() # e.g. .\install-dotnet.ps1 -Workloads maui,wasm-tools -) +param([string[]]$Workloads = @()) $ErrorActionPreference = 'Stop' -$installDir = '.dotnet' -$channel = '11.0' -$quality = 'preview' -$scriptUrl = 'https://dot.net/v1/dotnet-install.ps1' - -Write-Host "Installing .NET SDK ($channel, $quality) into $installDir..." -Write-Host "Downloading install script from $scriptUrl (Microsoft official)..." -Invoke-WebRequest -Uri $scriptUrl -OutFile "$env:TEMP\dotnet-install.ps1" -& "$env:TEMP\dotnet-install.ps1" ` - -Channel $channel ` - -Quality $quality ` - -InstallDir $installDir - -# Auto-detect installed version and create/update global.json +$installDir = '.dotnet'; $channel = '11.0'; $quality = 'preview' +Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TEMP\dotnet-install.ps1" +& "$env:TEMP\dotnet-install.ps1" -Channel $channel -Quality $quality -InstallDir $installDir $sdkVersion = & "$installDir\dotnet.exe" --version -Write-Host "Installed SDK version: $sdkVersion" - -if (Test-Path 'global.json') { - Write-Host "WARNING: global.json already exists. Backing up to global.json.bak" - Copy-Item 'global.json' 'global.json.bak' -} - +if (Test-Path 'global.json') { Copy-Item 'global.json' 'global.json.bak' } @" { "sdk": { @@ -414,27 +204,14 @@ if (Test-Path 'global.json') { } } "@ | Set-Content -Path 'global.json' -Encoding UTF8 -Write-Host "Created global.json pinned to $sdkVersion" - -# Update .gitignore if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\\.dotnet/$' -Quiet)) { Add-Content -Path .gitignore -Value '.dotnet/' } -Write-Host "Ensured .dotnet/ is in .gitignore" - -# Install workloads if any were requested -if ($Workloads.Count -gt 0) { - Write-Host "Installing workloads: $($Workloads -join ', ')..." - & "$installDir\dotnet.exe" workload install @Workloads -} - -Write-Host "Done. SDK version: $sdkVersion" +if ($Workloads.Count -gt 0) { & "$installDir\dotnet.exe" workload install @Workloads } +Write-Host "Done. SDK: $sdkVersion" ``` -These scripts should be committed to the repo so teammates can run them. - -**Checkpoint:** Scripts are executable and match the channel/quality used in -Step 5. +Commit these scripts to the repo so teammates can run them. ### Step 11 — Verify SDK resolution @@ -442,58 +219,21 @@ Step 5. dotnet --version ``` -Run this from the project root (where `global.json` lives). The output should -match the version installed in Step 6. - -If it does not match, troubleshoot: -- Is `global.json` in the current directory or a parent? -- Does `paths` contain `.dotnet`? -- Is the host `dotnet` v10+? - -**Checkpoint:** `dotnet --version` output matches the locally installed version. +Output should match the locally installed version. If not, check: global.json +location, `paths` array contents, host dotnet version ≥ 10. ### Step 12 — Summarize and explain cleanup -Tell the user what was done: - -> ✅ **Local SDK setup complete.** -> -> - Installed .NET SDK {version} into `.dotnet/`. -> - Configured `global.json` to resolve the local SDK first. -> - Added `.dotnet/` to `.gitignore`. -> {if scripts created: - Created `install-dotnet.sh` and `install-dotnet.ps1` -> for your team.} -> -> **To clean up later:** -> 1. Delete the `.dotnet/` directory: `rm -rf .dotnet/` (macOS/Linux) or -> `Remove-Item -Recurse -Force .\.dotnet` (Windows). -> This also removes any installed workloads automatically. -> 2. Remove the `paths` and `errorMessage` keys from `global.json`. -> 3. (Optional) Delete `install-dotnet.sh` / `install-dotnet.ps1`. -> 4. Run `dotnet build` to confirm your project works with the system-wide SDK -> again. -> -> Your system-wide .NET installation was never modified. - -## Validation - -After completing the workflow, verify: - -1. `dotnet --version` (from project root) shows the locally installed version. -2. `dotnet --info` shows the SDK path pointing to `.dotnet/`. -3. `dotnet build` (if a project exists) succeeds with the local SDK. -4. Running `dotnet --version` from **outside** the project still shows the - system-wide SDK (confirming no global side effects). +Tell the user: SDK installed, global.json configured, .dotnet/ gitignored, system +install untouched. Cleanup: delete `.dotnet/`, remove `paths`/`errorMessage` from +global.json, optionally delete install scripts. ## Common pitfalls | Pitfall | Cause | Fix | |---|---|---| -| `paths` key is ignored | Host `dotnet` is < v10 | Install .NET 10+ system-wide | -| Wrong SDK resolves | `global.json` is in a parent directory | Check for `global.json` files up the directory tree | -| `dotnet app.dll` uses wrong runtime | `paths` applies to SDK resolution only, not apphost or `dotnet exec` | Use `dotnet run` instead, or set `DOTNET_ROOT` | -| `.dotnet/` is huge | SDKs include targeting packs, templates, etc. | Expected; always gitignore | -| Install script fails on proxy/firewall | Corporate network blocks `dot.net` | Download the script and SDK manually; use `--install-dir` | -| Teammates get "SDK not found" | `.dotnet/` is gitignored, they haven't run install script | Ensure `errorMessage` in `global.json` directs them to the script | -| CI build fails | CI image lacks .NET 10+ host | Install .NET 10+ globally first, then run local install script | -| MAUI templates not available | Workloads not on local SDK | Run `./.dotnet/dotnet workload install maui` (local binary) | +| `paths` ignored | Host `dotnet` < v10 | Install .NET 10+ system-wide | +| Wrong SDK resolves | `global.json` in parent directory | Check for global.json up the tree | +| Teammates get "SDK not found" | `.dotnet/` gitignored, no install script run | Use `errorMessage` in global.json | +| Workloads missing | Used system `dotnet` instead of local | Use `./.dotnet/dotnet workload install` | +| `dotnet app.dll` wrong runtime | `paths` is SDK-only, not apphost | Use `dotnet run` or set `DOTNET_ROOT` | diff --git a/tests/dotnet/setup-local-sdk/eval.yaml b/tests/dotnet/setup-local-sdk/eval.yaml index 7a57ba289..02ee867a3 100644 --- a/tests/dotnet/setup-local-sdk/eval.yaml +++ b/tests/dotnet/setup-local-sdk/eval.yaml @@ -1,6 +1,6 @@ scenarios: - name: "Basic local SDK setup with .NET 11 preview" - prompt: "I want to try the latest .NET 11 preview in my project without messing up my global install. Can you set it up locally?" + prompt: "I want to try the latest .NET 11 preview in my project without affecting my global install. Set it up locally for me." assertions: - type: "output_contains" value: "dotnet-install" @@ -8,90 +8,60 @@ scenarios: value: "global.json" - type: "output_contains" value: ".dotnet" - - type: "output_contains" - value: ".gitignore" rubric: - - "The agent verified the host dotnet version is 10 or later before proceeding" - - "The agent ran the dotnet-install script with --install-dir .dotnet" - - "The agent created or updated global.json with a paths array containing .dotnet" + - "The agent installed the SDK into a .dotnet/ directory using the dotnet-install script" + - "The agent created or updated global.json with a paths array" - "The agent added .dotnet/ to .gitignore" - - "The agent verified the local SDK resolves correctly with dotnet --version" - - "The agent explained how to clean up (delete .dotnet/ and revert global.json)" + - "The agent explained how to clean up" + expect_tools: ["bash"] timeout: 180 - name: "Install a specific SDK version locally" - prompt: "Install .NET SDK version 11.0.100-preview.2.26159.112 locally in this project using global.json paths." + prompt: "Install .NET SDK version 11.0.100-preview.2.26159.112 locally using global.json paths." assertions: - type: "output_contains" value: "11.0.100-preview.2.26159.112" - type: "output_contains" value: "global.json" rubric: - - "The agent used a version flag (--version or -Version) instead of channel/quality to install the exact version requested" - - "The agent set the exact version in global.json sdk.version" - - "The agent configured the paths array in global.json" - - "The agent did not prompt for channel or quality since an exact version was given" - - "The agent verified the installed version matches the requested version" + - "The agent used a version flag instead of channel/quality to install the exact version" + - "The agent configured global.json with the paths array and exact version" + - "The agent verified the installed version matches the request" + expect_tools: ["bash"] timeout: 180 - - name: "Create team install scripts for reproducible setup" - prompt: "Set up a local .NET 11 preview SDK and create install scripts so my teammates can reproduce the setup on any OS." + - name: "Set up local SDK with MAUI workload" + prompt: "I need to test my .NET MAUI app with the latest .NET 11 preview. Set up a local SDK with the MAUI workload installed." assertions: - type: "output_contains" - value: "install-dotnet.sh" - - type: "output_contains" - value: "install-dotnet.ps1" + value: "workload" - type: "output_contains" - value: "global.json" + value: ".dotnet" rubric: - - "The agent created install-dotnet.sh with the correct channel and quality flags" - - "The agent created install-dotnet.ps1 with the correct channel and quality flags" - - "The agent made install-dotnet.sh executable (chmod +x)" - - "The agent configured global.json with an errorMessage directing users to the install scripts" - - "The agent explained that teammates should run the install script after cloning" + - "The agent installed the SDK locally and the MAUI workload" + - "The agent used ./.dotnet/dotnet (not system dotnet) for workload commands" + - "The agent configured global.json with paths" + expect_tools: ["bash"] timeout: 180 - - name: "Verify SDK resolution after local setup" - prompt: "I already have a .dotnet/ folder and global.json with paths configured, but dotnet --version still shows my global SDK. Help me fix it." + - name: "Create team install scripts" + prompt: "Set up a local .NET 11 preview SDK and create install scripts so teammates can reproduce the setup on any OS." assertions: - type: "output_contains" - value: "dotnet --version" + value: "install-dotnet" - type: "output_contains" value: "global.json" rubric: - - "The agent checked the host dotnet version is 10 or later" - - "The agent inspected the existing global.json to verify the paths array is correct" - - "The agent verified that .dotnet/ contains a valid SDK installation" - - "The agent checked for global.json files in parent directories that might interfere" - - "The agent mentioned the common pitfall about global.json in parent directories potentially overriding settings" - - "The agent suggested concrete fixes based on the diagnosis" + - "The agent created install-dotnet.sh and install-dotnet.ps1 scripts" + - "The agent configured global.json with an errorMessage directing to the scripts" + - "The agent explained that teammates run the script after cloning" + expect_tools: ["bash"] timeout: 180 - name: "Detect incompatible .NET host version" - prompt: "Set up a local .NET 11 preview SDK for my project. My current dotnet --version shows 9.0.306." - rubric: - - "The agent detected that the host version (9.0.306) is below the .NET 10 requirement" - - "The agent mentioned that .NET 10+ host is required for the paths feature" - - "The agent instructed the user to install .NET 10+ system-wide before proceeding" - timeout: 180 - - - name: "Handle existing local SDK installation" - prompt: "Set up a local .NET 11 preview. I already have a .dotnet folder from a previous setup." + prompt: "Set up a local .NET 11 preview SDK for my project. My dotnet --version shows 9.0.306." rubric: - - "The agent detected the existing .dotnet/ directory" - - "The agent asked the user whether to update or skip installation" - - "The agent proceeded based on user preference" - timeout: 180 - - - name: "Set up local SDK with MAUI workload" - prompt: "I want to test my .NET MAUI app against the latest .NET 11 preview. Set up a local SDK with the MAUI workload." - assertions: - - type: "output_contains" - value: "workload install maui" - rubric: - - "The agent installed the .NET 11 preview SDK locally" - - "The agent installed the MAUI workload on the local SDK" - - "The agent used ./.dotnet/dotnet (not plain dotnet) for workload install and list commands" - - "The agent configured global.json with paths" - - "The agent mentioned that no sudo is needed for local workload installs" - timeout: 180 + - "The agent detected that version 9.0.306 is below the .NET 10 requirement" + - "The agent explained that .NET 10+ is required for the paths feature" + - "The agent told the user to install .NET 10+ system-wide first" + timeout: 60 From 64f77869f21cbf80d80da687545db650b75dc019 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 16:13:43 +0200 Subject: [PATCH 6/7] Address review round 4: cross-platform fixes and conditional configs - MINGW/MSYS/CYGWIN treated as bash-capable (Git Bash), not PowerShell - Remove hardcoded 'Install directory' input row - Make allowPrerelease conditional on preview installs - Make errorMessage conditional on team scripts being created - Add PowerShell equivalent for .gitignore update - Add assertion to incompatible host eval scenario Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dotnet/skills/setup-local-sdk/SKILL.md | 19 +++++++++++++++---- tests/dotnet/setup-local-sdk/eval.yaml | 3 +++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/dotnet/skills/setup-local-sdk/SKILL.md b/plugins/dotnet/skills/setup-local-sdk/SKILL.md index 54f8b3673..843e5ea22 100644 --- a/plugins/dotnet/skills/setup-local-sdk/SKILL.md +++ b/plugins/dotnet/skills/setup-local-sdk/SKILL.md @@ -35,7 +35,6 @@ The result is a fully isolated SDK that: |---|---|---|---| | Channel or version | No | `11.0` | e.g. `11.0`, `STS`, `LTS`, or an exact version like `11.0.100-preview.2.26159.112` | | Quality | No | `preview` | One of: `daily`, `preview`, `GA` | -| Install directory | No | `.dotnet` | Relative to the project root | ### Prerequisites @@ -60,8 +59,9 @@ If major version < 10, stop: the `paths` feature requires .NET 10+. ### Step 3 — Detect operating system -Run `uname -s 2>/dev/null`. If it succeeds → use bash/`dotnet-install.sh`. -If it fails or returns `MINGW`/`CYGWIN` → use PowerShell/`dotnet-install.ps1`. +Run `uname -s 2>/dev/null`. If it succeeds (including `MINGW*`, `MSYS*`, `CYGWIN*` — +these are bash-capable environments like Git Bash) → use bash/`dotnet-install.sh`. +If it fails (native Windows without Git Bash) → use PowerShell/`dotnet-install.ps1`. ### Step 4 — Check for existing local SDK @@ -132,7 +132,8 @@ Verify: `./.dotnet/dotnet workload list` (or `.\.dotnet\dotnet.exe workload list - `paths`: `.dotnet` first (local priority), `$host$` = system-wide fallback. - `rollForward: "latestFeature"`: rolls forward across feature bands, not just patches. -- `errorMessage`: tells teammates how to get the SDK. +- `allowPrerelease`: set to `true` only when installing a prerelease SDK. Omit for stable versions. +- `errorMessage`: include only when team install scripts are created (Step 10). Otherwise omit. If `global.json` already exists, preserve existing properties (`msbuild-sdks`, `tools`, etc.) and only add/update the `sdk` section. Warn about overwrites. @@ -142,10 +143,20 @@ If `global.json` already exists, preserve existing properties (`msbuild-sdks`, ### Step 9 — Update .gitignore +**macOS / Linux (or Git Bash):** + ```bash grep -qxF '.dotnet/' .gitignore 2>/dev/null || echo '.dotnet/' >> .gitignore ``` +**Windows (PowerShell):** + +```powershell +if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\\.dotnet/$' -Quiet)) { + Add-Content -Path .gitignore -Value '.dotnet/' +} +``` + ### Step 10 — Create team install scripts Create if user mentioned "team", "share", "CI", "scripts", etc. Otherwise offer. diff --git a/tests/dotnet/setup-local-sdk/eval.yaml b/tests/dotnet/setup-local-sdk/eval.yaml index 02ee867a3..8b45266d7 100644 --- a/tests/dotnet/setup-local-sdk/eval.yaml +++ b/tests/dotnet/setup-local-sdk/eval.yaml @@ -60,6 +60,9 @@ scenarios: - name: "Detect incompatible .NET host version" prompt: "Set up a local .NET 11 preview SDK for my project. My dotnet --version shows 9.0.306." + assertions: + - type: "output_contains" + value: "10" rubric: - "The agent detected that version 9.0.306 is below the .NET 10 requirement" - "The agent explained that .NET 10+ is required for the paths feature" From ebbec974f548c32e8f9861e746c4fcf081624404 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 16:58:31 +0200 Subject: [PATCH 7/7] Fix timeouts, review comments, and eval issues Immediate fixes based on eval results analysis: eval.yaml: 120s) Rationale: All 5 scenarios timed out; runs need more time to produce output - Remove expect_tools constraints: redundant with timeout fix and brittle - Change incompatible-host assertion from '.10.' to '.NET 10.' for specificity SKILL.md: -fsSL (fail fast on HTTP errors) - Improve global.json merge guidance: explicitly document backing up and preserving existing msbuild-sdks/tools properties - Rationale: Addresses 5 unresolved PR review comments Key findings from eval artifact analysis: - Plugin mode produces correct output (skill is good) - Isolated mode times out + no output (timeout is blocker) - Judge JSON-RPC failures are infrastructure issue (not our problem) - Skill activates correctly in all scenarios Next: Wait for CI to run with longer timeouts, then address judge infrastructure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- plugins/dotnet/skills/setup-local-sdk/SKILL.md | 10 ++++++---- tests/dotnet/setup-local-sdk/eval.yaml | 16 ++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/plugins/dotnet/skills/setup-local-sdk/SKILL.md b/plugins/dotnet/skills/setup-local-sdk/SKILL.md index 843e5ea22..e54c4e2b2 100644 --- a/plugins/dotnet/skills/setup-local-sdk/SKILL.md +++ b/plugins/dotnet/skills/setup-local-sdk/SKILL.md @@ -76,7 +76,7 @@ If `.dotnet/` exists, ask: update with the new version, or skip and keep it? **macOS / Linux:** ```bash -curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh +curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh bash /tmp/dotnet-install.sh --channel --quality --install-dir .dotnet ``` @@ -135,8 +135,10 @@ Verify: `./.dotnet/dotnet workload list` (or `.\.dotnet\dotnet.exe workload list - `allowPrerelease`: set to `true` only when installing a prerelease SDK. Omit for stable versions. - `errorMessage`: include only when team install scripts are created (Step 10). Otherwise omit. -If `global.json` already exists, preserve existing properties (`msbuild-sdks`, -`tools`, etc.) and only add/update the `sdk` section. Warn about overwrites. +If `global.json` already exists, **merge** carefully: preserve existing properties (`msbuild-sdks`, +`tools`, etc.) and only add/update the `sdk` section. Read the existing file first, update/add +the `sdk` object, then write it back. This ensures cross-project config (e.g., MSBuild settings) +isn't lost. Always back up the original file (e.g., `global.json.bak`) before modifying. **Minimal config** (when version pinning isn't needed): `{"sdk":{"paths":[".dotnet","$host$"]}}` @@ -170,7 +172,7 @@ INSTALL_DIR=".dotnet" CHANNEL="11.0" QUALITY="preview" WORKLOADS=("${@}") -curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh +curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh bash /tmp/dotnet-install.sh --channel "$CHANNEL" --quality "$QUALITY" --install-dir "$INSTALL_DIR" SDK_VERSION=$("$INSTALL_DIR/dotnet" --version) [ -f global.json ] && cp global.json global.json.bak diff --git a/tests/dotnet/setup-local-sdk/eval.yaml b/tests/dotnet/setup-local-sdk/eval.yaml index 8b45266d7..c6cf6751c 100644 --- a/tests/dotnet/setup-local-sdk/eval.yaml +++ b/tests/dotnet/setup-local-sdk/eval.yaml @@ -13,8 +13,7 @@ scenarios: - "The agent created or updated global.json with a paths array" - "The agent added .dotnet/ to .gitignore" - "The agent explained how to clean up" - expect_tools: ["bash"] - timeout: 180 + timeout: 300 - name: "Install a specific SDK version locally" prompt: "Install .NET SDK version 11.0.100-preview.2.26159.112 locally using global.json paths." @@ -27,8 +26,7 @@ scenarios: - "The agent used a version flag instead of channel/quality to install the exact version" - "The agent configured global.json with the paths array and exact version" - "The agent verified the installed version matches the request" - expect_tools: ["bash"] - timeout: 180 + timeout: 300 - name: "Set up local SDK with MAUI workload" prompt: "I need to test my .NET MAUI app with the latest .NET 11 preview. Set up a local SDK with the MAUI workload installed." @@ -41,8 +39,7 @@ scenarios: - "The agent installed the SDK locally and the MAUI workload" - "The agent used ./.dotnet/dotnet (not system dotnet) for workload commands" - "The agent configured global.json with paths" - expect_tools: ["bash"] - timeout: 180 + timeout: 300 - name: "Create team install scripts" prompt: "Set up a local .NET 11 preview SDK and create install scripts so teammates can reproduce the setup on any OS." @@ -55,16 +52,15 @@ scenarios: - "The agent created install-dotnet.sh and install-dotnet.ps1 scripts" - "The agent configured global.json with an errorMessage directing to the scripts" - "The agent explained that teammates run the script after cloning" - expect_tools: ["bash"] - timeout: 180 + timeout: 300 - name: "Detect incompatible .NET host version" prompt: "Set up a local .NET 11 preview SDK for my project. My dotnet --version shows 9.0.306." assertions: - type: "output_contains" - value: "10" + value: ".NET 10" rubric: - "The agent detected that version 9.0.306 is below the .NET 10 requirement" - "The agent explained that .NET 10+ is required for the paths feature" - "The agent told the user to install .NET 10+ system-wide first" - timeout: 60 + timeout: 120