Run file-based C# apps with the .NET CLI when the user explicitly wants C#/.NET code without creating a project. Use for C# language/API experiments, one-file C# apps, small multi-file C# apps composed with `#:include`/`#:exclude`, or C# file-based apps linked with `#:ref`. Do not use for language-agnostic throwaway scripts, generic computations, Python/PowerShell-style automation, full projects, or existing app integration.
npx skills add https://github.com/dotnet/skills --skill csharp-scripts
.cs files.csproj| Input | Required | Description |
|-------|----------|-------------|
| C# code or intent | Yes | The code to run, or a description of what the file-based app should do |
Run dotnet --version to verify the SDK is installed and note the full version, including the feature band. File-based apps require .NET 10 or later. #:include, #:exclude, and transitive directive processing require SDK 10.0.300 or later; SDK 10.0.100/10.0.200 builds can run single-file apps but do not support those multi-file directives. If the version is below 10, follow the fallback for older SDKs instead.
Create an entry-point .cs file using top-level statements. Place it outside any existing project directory to avoid conflicts with .csproj files.
#!/usr/bin/env dotnet
// hello.cs
Console.WriteLine("Hello from a file-based app!");
var numbers = new[] { 1, 2, 3, 4, 5 };
Console.WriteLine($"Sum: {numbers.Sum()}");
Guidelines:
Main method, class, or namespace boilerplate)using directives at the top of the file (after the #! line and any #: directives if present)dotnet hello.cs
Builds and runs the file automatically. Cached so subsequent runs are fast. Pass arguments after --:
dotnet hello.cs -- arg1 arg2 "multi word arg"
Place directives at the top of the file (immediately after an optional shebang line), before any using directives or other C# code. All directives start with #:.
#:package — NuGet package referencesSpecify a version unless the app intentionally uses central package management. Use @* when the latest available package is acceptable (or @*-* for pre-release):
#:package [email protected]
using Humanizer;
Console.WriteLine("hello world".Titleize());
#:property — MSBuild propertiesSet any MSBuild property inline. Syntax: #:property PropertyName=Value
#:property AllowUnsafeBlocks=true
#:property PublishAot=false
#:property NoWarn=CS0162
MSBuild expressions and property functions are supported:
#:property LogLevel=$([MSBuild]::ValueOrDefault('$(LOG_LEVEL)', 'Information'))
Common properties:
| Property | Purpose |
|----------|---------|
| AllowUnsafeBlocks=true | Enable unsafe code |
| PublishAot=false | Disable native AOT (enabled by default) |
| NoWarn=CS0162;CS0219 | Suppress specific warnings |
| LangVersion=preview | Enable preview language features |
| InvariantGlobalization=false | Enable culture-specific globalization |
#:project — Project referencesReference another project by relative path:
#:project ../MyLibrary/MyLibrary.csproj
#:ref — File-based app referencesReference another .cs file as a separate file-based app project when it should compile into a separate assembly instead of being included in the same compilation. Use #:include for ordinary helper files that should share the same assembly as the entry point; use #:ref when you want project-reference-like boundaries.
#:property ExperimentalFileBasedProgramEnableRefDirective=true
#:ref ../Shared/Formatter.cs
Console.WriteLine(Formatter.Title("hello world"));
Guidelines:
#:property OutputType=Library in that referenced file.#:ref is transitive: a referenced file can contain its own #:ref and other #: directives.#:property ExperimentalFileBasedProgramEnableRefDirective=true; remove that property if the SDK accepts #:ref without it.#:sdk — SDK selectionOverride the default SDK (Microsoft.NET.Sdk):
#:sdk Microsoft.NET.Sdk.Web
#:include and #:exclude — Multi-file appsIn .NET SDK 10.0.300 and later, file-based apps can include additional files in the same virtual project. Check the full dotnet --version output before using these directives; a 10.0.100 or 10.0.200 SDK is still .NET 10 but does not support them. Use #:include for helper source files and supported assets, and #:exclude to remove files from an include pattern or default item set.
#!/usr/bin/env dotnet
#:include Helpers.cs
#:include Models/*.cs
#:exclude Models/Generated/*.cs
Console.WriteLine(Formatter.Title("hello world"));
Guidelines:
dotnet as the entry point; put top-level statements there..cs files.Helpers.cs or Models/*.cs over broad recursive globs.#:package, #:property, #:sdk, #:project, #:ref, #:include, or #:exclude directives.#:package, #:property, #:sdk, #:include, and #:exclude entries can fail.#:include, add a shebang (#!/usr/bin/env dotnet) to the entry-point file on Unix-like systems to make the entry point clear to tools. Use LF line endings and no BOM for shebang files.Example layout:
scratch/
hello.cs
Helpers.cs
Models/
Person.cs
#!/usr/bin/env dotnet
// hello.cs
#:include Helpers.cs
#:include Models/*.cs
var person = new Person("Ada");
Console.WriteLine(Formatter.Title(person.Name));
// Helpers.cs
static class Formatter
{
public static string Title(string value) => value.ToUpperInvariant();
}
// Models/Person.cs
record Person(string Name);
Remove the app files when the user is done. To clear cached build artifacts:
dotnet clean hello.cs
On Unix platforms, make a .cs file directly executable:
#!/usr/bin/env dotnet
Console.WriteLine("I'm executable!");
chmod +x hello.cs
./hello.cs
Use LF line endings (not CRLF) when adding a shebang. This directive is ignored on Windows.
File-based apps enable native AOT by default. Reflection-based APIs like JsonSerializer.Serialize<T>(value) fail at runtime under AOT. Use source-generated serialization instead:
using System.Text.Json;
using System.Text.Json.Serialization;
var person = new Person("Alice", 30);
var json = JsonSerializer.Serialize(person, AppJsonContext.Default.Person);
Console.WriteLine(json);
var deserialized = JsonSerializer.Deserialize(json, AppJsonContext.Default.Person);
Console.WriteLine($"Name: {deserialized!.Name}, Age: {deserialized.Age}");
record Person(string Name, int Age);
[JsonSerializable(typeof(Person))]
partial class AppJsonContext : JsonSerializerContext;
When a file-based app outgrows this workflow, convert it to a full project:
dotnet project convert hello.cs
If the .NET SDK version is below 10, file-based apps are not available. Use a temporary console project instead:
mkdir -p /tmp/csharp-file-based-app && cd /tmp/csharp-file-based-app
dotnet new console -o . --force
Replace the generated Program.cs with the app content and run with dotnet run. Add NuGet packages with dotnet add package <name>. Remove the directory when done.
dotnet --version reports 10.0 or later (or fallback path is used)#:include, #:exclude, or transitive directives from included files, dotnet --version reports SDK 10.0.300 or laterdotnet build <file>.cs)dotnet <file>.cs produces the expected output| Pitfall | Solution |
|---------|----------|
| .cs file is inside a directory with a .csproj | Move the app outside the project directory, or use dotnet run --file file.cs |
| #:package without a version | Specify a version: #:package [email protected] or @* for latest |
| #:property with wrong syntax | Use PropertyName=Value with no spaces around = and no quotes: #:property AllowUnsafeBlocks=true |
| Directives placed after C# code | All #: directives must appear immediately after an optional shebang line (if present) and before any using directives or other C# statements |
| Helper file is not compiled | Add #:include Helper.cs or an appropriate glob to the entry-point file |
| Shared file needs an assembly boundary | Use #:ref Shared.cs instead of #:include Shared.cs, and set #:property OutputType=Library in the referenced file if it has no entry point |
| Broad include pulls in unrelated files | Prefer narrow include patterns and use #:exclude for generated, backup, or experimental files |
| Duplicate directives in included files | Keep package, property, SDK, include, and exclude directives unique across the entry point and included C# files |
| Reflection-based JSON serialization fails | Use source-generated JSON with JsonSerializerContext (see Source-generated JSON) |
| Unexpected build behavior or version errors | File-based apps inherit global.json, Directory.Build.props, Directory.Build.targets, and nuget.config from parent directories. Move the app to an isolated directory if the inherited settings conflict |
See https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps for a full reference on file-based apps.
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
Take dotnet/csharp-scripts 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.