Copy To Output Directory logo

Copy To Output Directory

OrganizationPopular
dotnet
copy-to-output-directory

Choosing an MSBuild CopyToOutputDirectory / CopyToPublishDirectory mode: Never, PreserveNewest, Always, and IfDifferent (MSBuild 17.13+), plus $(SkipUnchangedFilesOnCopyAlways). USE FOR: removing the per-build Always copy perf hit; resetting output files mutated between builds. DO NOT USE FOR: general incremental-build diagnosis (use incremental-build); non-MSBuild build systems.

Overview

Publisherdotnet
Repositoryskills
Skill namecopy-to-output-directory
Stars
5.4K
Forks
416
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by dotnet on GitHub. Read the source before you install it.

Installation

Install the Copy To Output Directory AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/dotnet/skills.git /tmp/skills
mkdir -p .claude/skills
cp -r /tmp/skills/plugins/dotnet-msbuild/skills/copy-to-output-directory .claude/skills/copy-to-output-directory
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Copy To Output Directory in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Copy To Output Directory on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Copy To Output Directory is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Choosing a CopyToOutputDirectory Mode

Overview

The CopyToOutputDirectory metadata (and its publish counterpart CopyToPublishDirectory) controls whether an item — Content, None, EmbeddedResource, or Compile — is copied next to your build output, and under what conditions the copy happens. Picking the wrong mode causes either stale files in bin/ or an unnecessary per-build performance hit.

As of MSBuild 17.13 / .NET SDK 9.0.2xx there are four values:

ModeCopies when…Incremental costTypical use
Never (default)NeverNoneFiles not needed at runtime
PreserveNewestSource is newer than destination (or destination missing)Cheap (timestamp check)The common case — source files you edit
AlwaysEvery build, unconditionallyExpensive — copies on every build even in no-op buildsLegacy workaround; avoid (see below)
IfDifferentSource differs from destination in either direction (newer or older, or size differs, or destination missing)Cheap (timestamp + size check)Destination may be mutated between builds
xml
<ItemGroup>
  <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
  <None Include="testdata\seed.db"  CopyToOutputDirectory="IfDifferent" />
</ItemGroup>

You can use either the attribute form shown above or the child-element form:

xml
<None Include="testdata\seed.db">
  <CopyToOutputDirectory>IfDifferent</CopyToOutputDirectory>
</None>

Why Always is usually the wrong choice

Always re-copies the file on every build, including otherwise-clean incremental/no-op builds. On projects with many or large content files this is a measurable, recurring cost and a common cause of "why is my no-op build not instant?" reports.

Historically Always was the only way to handle a specific scenario: the destination file can change between builds — for example an SQLite database, a storage/state file, or a config file that a test run mutates. With PreserveNewest, if the destination is modified (making its timestamp newer than the source) MSBuild will not restore the original source file, because the source is no longer newer. People reached for Always to force the file back into a known-good state — paying the copy cost on every build as a side effect.

IfDifferent: copy when different, in either direction

IfDifferent is the targeted fix for that scenario. It copies the source over the destination whenever MSBuild considers the two different — whether the source is newer or older than the destination, whether the size differs, or the destination is missing — and skips the copy when the destination is unchanged per MSBuild's heuristic.

Under the hood the _CopyDifferingSourceItemsToOutputDirectory target uses the Copy task with SkipUnchangedFiles="true". That "unchanged" check is a heuristic: it compares last-write timestamp and file size only — not a content hash — so a destination that was edited to the same size and timestamp as the source is treated as unchanged and is not re-copied. In practice this restores a mutated destination back to the source version on the next build (the reason people reached for Always) while avoiding the unconditional per-build copy.

Use IfDifferent when:

  • A test run or the app itself writes to the copied file (databases, caches, state/storage files, editable config) and you want each build to reset it to the source version.
  • You were using Always purely as a "keep the output in sync with the source" mechanism, not because you truly need a copy on every single build.
xml
<ItemGroup>
  <!-- Reset the fixture DB to the source copy whenever it has drifted,
       but don't pay a copy on every no-op build. -->
  <None Include="fixtures\catalog.db" CopyToOutputDirectory="IfDifferent" />
</ItemGroup>

Globally softening Always with $(SkipUnchangedFilesOnCopyAlways)

If you have an existing codebase full of CopyToOutputDirectory="Always" items and want the performance benefit without editing every item, set the property:

xml
<PropertyGroup>
  <SkipUnchangedFilesOnCopyAlways>true</SkipUnchangedFilesOnCopyAlways>
</PropertyGroup>

This makes the _CopyOutOfDateSourceItemsToOutputDirectoryAlways target pass SkipUnchangedFiles="true" to its Copy task, so Always items are only copied when they actually differ — effectively giving Always the same skip-unchanged behavior as IfDifferent.

  • Default is false for backwards compatibility (classic Always = copy every build).
  • Set it in Directory.Build.props to opt an entire repo in at once.
  • Prefer converting individual items to IfDifferent when you can; use this property when a bulk, non-invasive opt-in is more practical.

How the modes flow through the build

GetCopyToOutputDirectoryItems buckets each item by its CopyToOutputDirectory value. Three copy targets then do the work as dependencies of _CopySourceItemsToOutputDirectory (which is itself invoked by CopyFilesToOutputDirectory):

  • _CopyOutOfDateSourceItemsToOutputDirectoryPreserveNewest items (incremental via Inputs/Outputs timestamp comparison).
  • _CopyOutOfDateSourceItemsToOutputDirectoryAlwaysAlways items (unconditional copy unless $(SkipUnchangedFilesOnCopyAlways) is true).
  • _CopyDifferingSourceItemsToOutputDirectoryIfDifferent items (SkipUnchangedFiles="true").

All copied files are registered in FileWrites, so dotnet clean removes them.

Transitive copy: items marked Always, PreserveNewest, or IfDifferent also flow to referencing projects through ProjectReference (via _CopyToOutputDirectoryTransitiveItems). Never items do not. IfDifferent participates in ClickOnce publish item collection alongside Always/PreserveNewest.

Version requirement

IfDifferent and $(SkipUnchangedFilesOnCopyAlways) require MSBuild 17.13 or later (.NET SDK 9.0.2xx+ / Visual Studio 2022 17.13+). On older toolsets the value is not recognized: it will not match the Always/PreserveNewest/IfDifferent conditions in the common targets, so the item is silently not copied. Gate usage on the toolset if you must support older SDKs, or require the minimum SDK via global.json.

Quick decision guide

  • Don't need the file at runtime → Never (or omit — it's the default).
  • Normal source file you edit → PreserveNewest.
  • Destination gets mutated between builds and must be reset to the source → IfDifferent.
  • You truly need a fresh copy on literally every build → Always (rare).
  • Stuck with lots of legacy Always and want the perf win without edits → keep Always but set $(SkipUnchangedFilesOnCopyAlways)=true.

Frequently asked questions

What does the Copy To Output Directory AI skill do?

Choosing an MSBuild CopyToOutputDirectory / CopyToPublishDirectory mode: Never, PreserveNewest, Always, and IfDifferent (MSBuild 17.13+), plus $(SkipUnchangedFilesOnCopyAlways). USE FOR: removing the per-build Always copy perf hit; resetting output files mutated between builds. DO NOT USE FOR: general incremental-build diagnosis (use incremental-build); non-MSBuild build systems.

Why use Copy To Output Directory on TypingMind?

Because you install it once and use it with any model. Copy To Output Directory is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Copy To Output Directory in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/dotnet/skills/tree/main/plugins/dotnet-msbuild/skills/copy-to-output-directory. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Copy To Output Directory?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Copy To Output Directory?

As many as you like. As long as a model supports skills, you can use Copy To Output Directory with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Copy To Output Directory AI skill free?

Yes. It is published on GitHub by dotnet under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇