microsoft/managing-target-frameworks
> Manages target frameworks in .NET project files (.csproj, .vbproj, .fsproj). Handles adding, removing, replacing, and upgrading target frameworks, including converting between single and multi-targeting. Use when user mentions "add TFM", "remove TFM", "retarget", "upgrade target framework", "change framework version", "multi-target", "switch to net8.0/net9.0/net10.0", or modifies TargetFramework/TargetFrameworks properties.
npx skills add https://github.com/microsoft/upgrade-agent-plugins --skill managing-target-frameworks
| Action | Skill |
|--------|-------|
| Add/update package references or resolve NuGet version conflicts | managing-package-references |
| Modify project properties | modifying-project-properties |
Defer to these skills for package versions, CPM, and property edits.
Track progress using this checklist:
Task Progress:
- [ ] Step 1: Locate target framework property
- [ ] Step 2: Modify target frameworks
- [ ] Step 3: Restore and resolve errors
- [ ] Step 4: Apply conditional compilation (if needed)
Before making changes, determine where the TargetFramework or TargetFrameworks property is defined. It may be in the project file, a Directory.Build.props, or another imported MSBuild file. Use the modifying-project-properties skill to locate and understand the property definition.
Determine the intended change from user context and apply the matching scenario:
| User intent | Current state | Action |
|-------------|---------------|--------|
| Replace/upgrade an existing TFM with another | Single or multi-targeted | Replace the specified TFM |
| Add support for an additional TFM | Single TargetFramework | Add multi-targeting (rename property to plural TargetFrameworks, keep existing TFM, add new one) |
| Add support for an additional TFM | Already uses TargetFrameworks | Append new TFM to existing list |
| Remove/drop a TFM | Multi-targeted | Remove from list (revert to singular if one remains) |
Replace a TFM — swap the value in place; preserve the singular/plural property name:
<!-- Single-targeted: stays singular -->
<TargetFramework>OldTFM</TargetFramework>
<!-- After -->
<TargetFramework>NewTFM</TargetFramework>
<!-- Multi-targeted: replace only the specific TFM -->
<TargetFrameworks>OldTFM;OtherTFM</TargetFrameworks>
<!-- After -->
<TargetFrameworks>NewTFM;OtherTFM</TargetFrameworks>
Add multi-targeting — convert singular TargetFramework to plural TargetFrameworks and add the new TFM:
<!-- Before -->
<TargetFramework>ExistingTFM</TargetFramework>
<!-- After -->
<TargetFrameworks>ExistingTFM;NewTFM</TargetFrameworks>
Append to existing multi-targeted project — add the new TFM to the existing list:
<!-- Before -->
<TargetFrameworks>TFM1;TFM2</TargetFrameworks>
<!-- After -->
<TargetFrameworks>TFM1;TFM2;NewTFM</TargetFrameworks>
Remove a TFM — remove the TFM from the list; revert to singular TargetFramework if only one remains:
<!-- Multiple TFMs remain: stays plural -->
<TargetFrameworks>TFM1;TFM2;TFMToRemove</TargetFrameworks>
<!-- After -->
<TargetFrameworks>TFM1;TFM2</TargetFrameworks>
<!-- One TFM remains: revert to singular -->
<TargetFrameworks>TFM1;TFMToRemove</TargetFrameworks>
<!-- After -->
<TargetFramework>TFM1</TargetFramework>
Order TFMs newest to oldest when multiple TFMs are present — MSBuild uses the first TFM as the default for IDE operations and dotnet run, so the newest framework provides the best developer experience.
Restore first and resolve all NUxxxx errors before building. Then build and resolve MSBxxxx/CSxxxx errors. Common multi-targeting errors:
| Error | Cause | Resolution |
|-------|-------|------------|
| NU1202: Package not compatible with <TFM> | Package doesn't support added TFM | Check for a newer package version that supports the TFM, or add a conditional <PackageReference> (see below) |
| NU1202: PackageA -> PackageB -> requires <TFM> | Transitive dependency incompatible | Update the top-level package, or add a conditional reference excluding it for the incompatible TFM |
| MSB3644: Reference assemblies not found | Targeting pack not installed | Advise the user to install the required targeting pack or SDK — the LLM cannot install SDKs |
| CS0246: Type could not be found | API not available in added TFM | Add a #if directive for TFM-specific code, or find a cross-platform alternative API |
| CS1061: Type does not contain member | API differs between TFMs | Find a replacement method or overload available in the new TFM. If no equivalent exists, add a #if directive for the specific member call |
Multi-targeting-specific pattern — use conditional package references when no single package version works for all TFMs:
<ItemGroup Condition="'$(TargetFramework)' == 'SpecificTFM'">
<PackageReference Include="PackageName" />
</ItemGroup>
Use #if directives only when APIs genuinely differ between TFMs. Not every difference needs conditionals — some just need a different overload or parameter. Overusing #if makes code harder to maintain and review.
Preprocessor symbol convention: MSBuild automatically defines preprocessor symbols for each target framework. The symbol name is the TFM with . replaced by _, uppercased (e.g., net10.0 → NET10_0, net48 → NET48, netstandard2.0 → NETSTANDARD2_0). These symbols do not need to be declared manually — they are available in any #if directive.
#if NET10_0_OR_GREATER
// API available only in .NET 10+
#elif NETFRAMEWORK
// .NET Framework fallback
#endif
Use _OR_GREATER suffix symbols (e.g., NET8_0_OR_GREATER) when the code applies to a minimum version rather than an exact version. Use NETFRAMEWORK for any .NET Framework target, NETSTANDARD for any .NET Standard target.
TargetFramework with multiple TFMs — MSBuild silently ignores all but the first value#if when only some APIs differ — creates unmaintainable code with unnecessary divergenceDirectory.Build.props — changes would be overridden by the importTargetFramework to TargetFrameworks when only a TFM replacement was requested — changes build behavior unnecessarilyTargetFrameworks (plural) when only one TFM remains — causes MSBuild to produce a TFM-specific output folder structure instead of a flat bin/ layoutTargetFramework changed to TargetFrameworks (plural) when adding multi-targetingTargetFrameworksTargetFrameworks reverted to singular TargetFramework when only one TFM remains after removal#if directives only where APIs genuinely differTake microsoft/managing-target-frameworks from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.