New to Claude Skills? Learn how to install them →

Mdotnet on GitHub

MSBuild Item Management

Free

Streamline your MSBuild item group management.

by dotnet5.1k stars on dotnet/skills
1 views
Updated Aug 10, 2026
Get this skill

Free · Opens the source repo

What MSBuild Item Management does

The MSBuild Item Management skill provides a comprehensive guide to managing item groups within MSBuild projects, specifically focusing on the operations of Include, Remove, and Update. These operations are essential for adding new items, excluding files, and modifying existing item metadata without re-adding them. This skill is particularly useful for developers working with .csproj files who need to ensure their item management practices are efficient and correct.

With this skill, users can learn how to effectively utilize batching with metadata, which allows for optimized execution based on unique metadata values. The skill includes practical examples of item transformations and conditional item inclusion, helping developers to manage their project files more effectively. By understanding these patterns, users can avoid common pitfalls such as cross-product batching and ensure that their build processes are streamlined and error-free.

This skill is designed for developers who are looking to diagnose and fix item group anti-patterns in their MSBuild projects. It will help in addressing issues like duplicate file warnings caused by SDK globbing, ensuring that targets do not run more times than expected, and managing generated files correctly to avoid cluttering the source directory. By following the guidelines provided, developers can enhance their understanding of MSBuild item management and improve their project's build performance.

When to use it

Use this skill when you need to diagnose issues in your MSBuild item groups or when you're reviewing item management practices in .csproj files.

When not to use it

This skill is not suitable for general anti-patterns unrelated to item management or for non-MSBuild build systems.

What you can build with it

Fixing Duplicate File Warnings

Use this skill to address CS2002 warnings caused by duplicate files in your MSBuild project.

Optimizing Build Performance

Implement the provided patterns to streamline your build process and avoid unnecessary target executions.

Reviewing Item Management Practices

Utilize this skill to audit your .csproj files for correct item management and adherence to best practices.

How to install MSBuild Item Management

View source

1. Install with the skills CLI

npx skills add dotnet/skills/item-management --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by dotnet

MSBuild Item Management Patterns

Canonical patterns for working with item groups, from Microsoft.Common.CurrentVersion.targets.

Include / Remove / Update — Three Operations

OperationPurposeWhen to use
IncludeAdd new items to the groupCreating items with identity + metadata
RemoveRemove items matching a patternExcluding files or clearing a group
UpdateModify metadata on existing itemsAdding/changing metadata without re-adding

Include — Add Items

<ItemGroup>
  <Compile Include="Generated\*.cs">
    <AutoGen>true</AutoGen>
  </Compile>
</ItemGroup>

Remove — Subtract Items

<ItemGroup>
  <!-- Remove specific items -->
  <Reference Remove="$(AdditionalExplicitAssemblyReferences)" />

  <!-- Set subtraction: prior minus current -->
  <_CleanOrphanFileWrites Include="@(_CleanPriorFileWrites)"
      Exclude="@(_CleanCurrentFileWrites)" />

  <!-- Clear an entire group -->
  <_Temporary Remove="@(_Temporary)" />
</ItemGroup>

Update — Modify Existing Items

<ItemGroup>
  <EmbeddedResource Update="@(EmbeddedResource)"
      Condition="'%(NuGetPackageId)' == 'Microsoft.CodeAnalysis.Collections'">
    <GenerateSource>true</GenerateSource>
    <ClassName>Microsoft.CodeAnalysis.Collections.SR</ClassName>
  </EmbeddedResource>
</ItemGroup>

Update does not add items — it only modifies items already in the group.

Item Batching — %(Metadata)

When %(Metadata) appears in target attributes or task parameters, MSBuild batches execution per unique metadata value.

Target-level batching (Outputs)

<Target Name="GenerateSatelliteAssemblies"
    Inputs="$(MSBuildAllProjects);@(_SatelliteAssemblyResourceInputs)"
    Outputs="$(IntermediateOutputPath)%(Culture)\$(TargetName).resources.dll">
  <!-- Runs once per unique Culture value -->
</Target>

Task-level batching

<Copy SourceFiles="@(_SourceItems)"
    DestinationFiles="@(_SourceItems->'$(OutDir)%(TargetPath)')">
</Copy>

Per-item filtering with Condition

<ItemGroup>
  <_ResxOutput Include="@(EmbeddedResource->'%(OutputResource)')"
      Condition="'%(EmbeddedResource.WithCulture)' == 'false'" />
</ItemGroup>

Batching rules

  • %(Metadata) in Condition or Outputs → target batches per unique value.
  • %(Metadata) in task parameters → task batches per unique value.
  • Do not mix %() from different item groups in the same expression — this causes a cross-product (see Common Pitfalls).

Item Transforms — @(Item->'expression')

Transforms create new item lists by applying an expression to each item:

<!-- Transform file paths to destinations -->
<Copy SourceFiles="@(IntermediateAssembly)"
    DestinationFiles="@(IntermediateAssembly->'$(OutDir)%(Filename)%(Extension)')"/>

<!-- Transform with separator for display -->
<Message Text="Files: @(Compile->'%(Filename)', ', ')" />

Exclude Pattern — Set Subtraction on Include

<ItemGroup>
  <Compile Include="**\*.cs" Exclude="Generated\**;Tests\**" />
</ItemGroup>

Exclude only works on Include — it cannot be used with Update or Remove.

Conditional Item Inclusion

<!-- Condition on ItemGroup — all or nothing -->
<ItemGroup Condition="'$(NetCoreBuild)' == 'true'">
  <PackageReference Include="System.IO.Pipelines" />
</ItemGroup>

<!-- Condition on individual items -->
<ItemGroup>
  <PackageReference Include="System.IO.Pipelines"
      Condition="'$(NetCoreBuild)' == 'true'" />
</ItemGroup>

PrivateAssets on Tool/Analyzer Packages

<ItemGroup>
  <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="all" />
  <PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" />
</ItemGroup>

Common Pitfalls

Cross-product batching

Referencing %(Metadata) from two different item groups creates O(N×M) executions:

<!-- BAD: Cross-product of @(Source) × @(Config) -->
<Exec Command="process %(Source.Identity) with %(Config.Identity)" />

<!-- GOOD: Reference one group via batching, the other via property -->
<Exec Command="process %(Source.Identity) with $(ConfigFile)" />

Generated files in source tree

Write to $(IntermediateOutputPath) (obj/), not the source directory. Source-tree generation pollutes version control and can cause duplicate compilation via globs.

Missing FileWrites

Every file created during a target must be added to @(FileWrites) for dotnet clean support.

Frequently asked questions about MSBuild Item Management

Similar skills