/setup-local-sdk
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,
Claude Code
$ npx -y skills add dotnet/skills --skill setup-local-sdk --agent claude-code
Codex
$ npx skills add dotnet/skills --skill setup-local-sdk -a codex -y
Cursor
$ npx skills add dotnet/skills --skill setup-local-sdk -a cursor -y
Gemini CLI
$ npx skills add dotnet/skills --skill setup-local-sdk -a gemini-cli -y
GitHub Copilot
$ npx skills add dotnet/skills --skill setup-local-sdk -a github-copilot -y
OpenCode
$ npx skills add dotnet/skills --skill setup-local-sdk -a opencode -y
Windsurf
$ npx skills add dotnet/skills --skill setup-local-sdk -a windsurf -y
SKILL.md
setup-local-sdk.SKILL.mdname: setup-local-sdk
license: MIT
description: >
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 `.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 from the project root.
- Can be deleted to revert (`rm -rf .dotnet/` or `Remove-Item -Recurse -Force .\.dotnet`).
When NOT to use
- 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
| 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` | | jq | No | — | Optional for bash team scripts when patching an existing `global.json`; without it, do not overwrite the file |
Prerequisites
1. **A .NET 10+ SDK is installed globally** — run `dotnet --version`; major ≥ 10. 2. **curl** (macOS/Linux) or **PowerShell** (Windows) is available.
Workflow
Step 1 — Clarify what to install
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
If the user already provided `dotnet --version` output, treat that as the authoritative version for their machine. Do not override it with the agent workspace's version; if the two differ, explain that the workspace differs and continue advising for the user's machine.
dotnet --version
If major version < 10, stop before downloading anything: the `paths` feature requires a .NET 10+ host SDK. Tell the user to install .NET 10 or later system-wide first, then return to the local SDK setup.
Step 3 — Detect operating system
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
**macOS / Linux:**
test -d .dotnet && echo "exists" || echo "not found"
**Windows (PowerShell):**
if (Test-Path -LiteralPath .\.dotnet) { "exists" } else { "not found" }If `.dotnet/` exists, ask: update with the new version, or skip and keep it?
Step 5 — Download and run the install script
**macOS / Linux:**
INSTALL_SCRIPT="$(mktemp "${TMPDIR:-/tmp}/dotnet-install.XXXXXX")"
trap 'rm -f "$INSTALL_SCRIPT"' EXIT
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$INSTALL_SCRIPT"
bash "$INSTALL_SCRIPT" --channel <CHANNEL> --quality <QUALITY> --install-dir .dotnet**Windows (PowerShell):**
$installScript = Join-Path $env:TEMP "dotnet-install-$([guid]::NewGuid()).ps1"
try {
Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript
& $installScript -Channel <CHANNEL> -Quality <QUALITY> -InstallDir .dotnet
}
finally {
if (Test-Path -LiteralPath $installScript) {
Remove-Item -LiteralPath $installScript -Force
}
}For exact versions: use `--version <VERSION>` (bash) or `-Version <VERSION>` (PowerShell) instead of channel/quality flags. The install scripts are from Microsoft's official URLs: `https://dot.net/v1/dotnet-install.sh` and `https://dot.net/v1/dotnet-install.ps1`.
Step 6 — Identify the installed 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`) for `global.json`.
Step 7 — Create or update global.json
{
"sdk": {
"version": "<INSTALLED_VERSION>",
"allowPrerelease": true,
"rollForward": "latestFeature",
"paths": [".dotnet", "$host$"],
"errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally."
}
}- `paths`: `.dotnet` first (local priority), `$host$` = system-wide fallback.
- `rollForward: "latestFeature"`: use for latest-preview or floating feature-band installs.
- Exact version requests: use `rollForward: "disable"` so SDK resolution doesn't move to a different feature band.
- `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, **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$"]}}`
Step 8 — Update .gitignore
**macOS / Linux (o
Read more
name: setup-local-sdk license: MIT description: > 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 `.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 from the project root.
- Can be deleted to revert (`rm -rf .dotnet/` or `Remove-Item -Recurse -Force .\.dotnet`).
When NOT to use
- 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
| 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` | | jq | No | — | Optional for bash team scripts when patching an existing `global.json`; without it, do not overwrite the file |
Prerequisites
1. **A .NET 10+ SDK is installed globally** — run `dotnet --version`; major ≥ 10. 2. **curl** (macOS/Linux) or **PowerShell** (Windows) is available.
Workflow
Step 1 — Clarify what to install
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
If the user already provided `dotnet --version` output, treat that as the authoritative version for their machine. Do not override it with the agent workspace's version; if the two differ, explain that the workspace differs and continue advising for the user's machine.
dotnet --version
If major version < 10, stop before downloading anything: the `paths` feature requires a .NET 10+ host SDK. Tell the user to install .NET 10 or later system-wide first, then return to the local SDK setup.
Step 3 — Detect operating system
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
**macOS / Linux:**
test -d .dotnet && echo "exists" || echo "not found"
**Windows (PowerShell):**
if (Test-Path -LiteralPath .\.dotnet) { "exists" } else { "not found" }If `.dotnet/` exists, ask: update with the new version, or skip and keep it?
Step 5 — Download and run the install script
**macOS / Linux:**
INSTALL_SCRIPT="$(mktemp "${TMPDIR:-/tmp}/dotnet-install.XXXXXX")"
trap 'rm -f "$INSTALL_SCRIPT"' EXIT
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$INSTALL_SCRIPT"
bash "$INSTALL_SCRIPT" --channel <CHANNEL> --quality <QUALITY> --install-dir .dotnet**Windows (PowerShell):**
$installScript = Join-Path $env:TEMP "dotnet-install-$([guid]::NewGuid()).ps1"
try {
Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript
& $installScript -Channel <CHANNEL> -Quality <QUALITY> -InstallDir .dotnet
}
finally {
if (Test-Path -LiteralPath $installScript) {
Remove-Item -LiteralPath $installScript -Force
}
}For exact versions: use `--version <VERSION>` (bash) or `-Version <VERSION>` (PowerShell) instead of channel/quality flags. The install scripts are from Microsoft's official URLs: `https://dot.net/v1/dotnet-install.sh` and `https://dot.net/v1/dotnet-install.ps1`.
Step 6 — Identify the installed 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`) for `global.json`.
Step 7 — Create or update global.json
{
"sdk": {
"version": "<INSTALLED_VERSION>",
"allowPrerelease": true,
"rollForward": "latestFeature",
"paths": [".dotnet", "$host$"],
"errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally."
}
}- `paths`: `.dotnet` first (local priority), `$host$` = system-wide fallback.
- `rollForward: "latestFeature"`: use for latest-preview or floating feature-band installs.
- Exact version requests: use `rollForward: "disable"` so SDK resolution doesn't move to a different feature band.
- `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, **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$"]}}`
Step 8 — Update .gitignore
**macOS / Linux (o
This repository contains the .NET team's curated set of portable skills and host-specific custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io.
Other skills on skills.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about setup-local-sdk.
- Which coding agents can use setup-local-sdk?
- setup-local-sdk installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

