dotnet-run
Skill2 starsChanged 42 days agoMITView on GitHub
Use whenever you need to run a C#/.NET project from source during development — picking a project when a folder has more than one, passing arguments to the app instead of to `dotnet run`, choosing a launch profile, targeting a framework in a multi-targeted project, or deciding between `dotnet run`, `dotnet <dll>`, and a published apphost. Covers project-based `dotnet run`; for single-file `.cs` apps use `dotnet-file-based-apps` instead.
In this repository56 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- benchmarkingSkill
- commit-messagesSkill
- csharp-15-syntaxSkill
- csharp-aot-trimmingSkill
- csharp-api-designSkill
- csharp-assembly-inspectionSkill
- csharp-asyncSkill
- csharp-collections-modernSkill
- csharp-concurrency-primitivesSkill
- csharp-data-layoutSkill
- csharp-debuggingSkill
- csharp-discardsSkill
- csharp-disposable-patternsSkill
- csharp-docsSkill
- csharp-error-handlingSkill
- csharp-extension-membersSkill
- csharp-generics-modernSkill
- csharp-guard-clausesSkill
- csharp-json-source-generationSkill
- csharp-language-versionsSkill
- csharp-logging-diagnosticsSkill
- csharp-lspSkill
- csharp-modern-typesSkill
- csharp-nullabilitySkill
- csharp-nullable-migrationSkill
- csharp-nunitSkill
- csharp-pattern-matchingSkill
- csharp-performanceSkill
- csharp-polyfillsSkill
- csharp-source-generatorsSkill
- csharp-static-lambdasSkill
- csharp-string-handlingSkill
- csharp-tunitSkill
- csharp-verificationSkill
- dispatching-subagentsSkill
- dotnet-11-librariesSkill
- dotnet-analyzersSkill
- dotnet-central-package-managementSkill
- dotnet-file-based-appsSkill
- dotnet-msbuild-usingsSkill
- dotnet-multi-targetingSkill
- dotnet-project-hygieneSkill
- dotnet-runtime-asyncSkill
- dotnet-slnxSkill
- dotnet-test-platformsSkill
- executing-plansSkill
- performance-bounded-deduplicationSkill
- roslyn-analyzer-performanceSkill
- roslyn-discoverySkill
- roslyn-duplicate-detectionSkill
- roslyn-guard-rewritersSkill
- roslyn-rewritersSkill
- subagent-driven-developmentSkill
- tddSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 36
If the directory *also* has a .csproj, passing a .cs file as the first argument does not switch to file-based mode — dotnet run treats it …
- Very specific: line 45
Use --file <path.cs> explicitly to force file-based mode when a project file is also present.
- Very specific: line 114
Put profiles in Properties/launchSettings.json (My Project/launchSettings.json for VB).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
dotnet run
dotnet run is a development convenience, not a deployment mechanism. It restores, builds, then launches the build output in one command. For anything that isn't interactive iteration — CI, containers, production — use dotnet publish and run the resulting apphost or dotnet app.dll directly. dotnet run resolves dependencies from the NuGet cache on the machine it runs on; a production host may not have that cache.
For single-file .cs apps (dotnet run app.cs, no .csproj), see dotnet-file-based-apps — that skill covers directives, caching, and AOT in depth. This skill is about running a real project.
What actually happens
- Implicit restore — unless
--no-restore. - Build (
dotnet buildunder the hood) — unless--no-build, which also implies--no-restore. - Locate the run target via the project's
RunCommand/RunArguments/RunWorkingDirectoryMSBuild properties (auto-derived fromOutputType/TargetPathfor a normal exe; override them for unusual launch shapes, e.g.RunCommand=mono). - Launch that command, with argv, environment, and working directory assembled from the CLI, any launch profile, and ambient state (see below).
Output goes to bin/<Configuration>/<TargetFramework>/ as usual; nothing about dotnet run changes where dotnet build puts files.
Do: dotnet run # iterate on a project
Do: dotnet publish -c Release ... # then dotnet ./out/app or the apphost — for real deployment
Don't: dotnet run # ...inside a container ENTRYPOINT or a CI "run the app" stepProject selection
- No
--project: uses the current directory. Exactly one.csproj/.vbproj/.fsprojmust be there, ordotnet runfails with "Couldn't find a project to run." - More than one project file in the directory is an error —
dotnet runwill not guess; pass--project. --project <path>accepts a project file or a directory containing exactly one. It does not accept a.sln/.slnx— pointing it at a solution fails with an MSBuild error (<Solution>element unsupported), not a helpful message. Pick the project.- Multi-targeted project (
<TargetFrameworks>plural) and no-f:dotnet runprompts/lists the available frameworks and exits rather than picking one. Always pass-f|--framework <TFM>for multi-targeted projects in scripts.
File-based apps vs. a project in the same folder
If the directory also has a .csproj, passing a .cs file as the first argument does not switch to file-based mode — dotnet run treats it as an application argument to the project and warns:
Warning: 'standalone.cs' appears to be a file-based app but was passed as an
argument to the project '.../proj.csproj'. To run it as a file-based app, use
'dotnet run --file standalone.cs'. To pass it as an application argument, use
'dotnet run -- standalone.cs' to suppress this warning.Use --file <path.cs> explicitly to force file-based mode when a project file is also present. With no project file present, dotnet run app.cs (or bare dotnet app.cs) detects file-based mode on its own — see dotnet-file-based-apps.
Arguments: yours vs. the app's
Everything after a literal -- goes to the application, untouched, in order. This is the only reliable way to pass app arguments — anything before -- that dotnet run doesn't recognize is also forwarded, but a recognized option (like --project) sitting between two of your tokens gets stripped out first, which can silently reassociate the tokens around it.
Do: dotnet run -- --input sample.txt --verbose
Don't: dotnet run --input sample.txt # if --input isn't a dotnet-run option this "works" by luck;
# it breaks the moment a future SDK adds an --input option,
# or the moment another recognized flag sits in between-c, -p/--property, -r, -f, etc. are dotnet-run options unless they appear after --. dotnet run -- --property name=value passes --property name=value to the app; without -- it's parsed as an MSBuild property assignment instead.
-p is overloaded: with an = in its argument it's short for --property (-p:Config=Release or -p Config=Release); without one it's the deprecated short form of --project (still accepted, but write --project in scripts — it's clearer and not slated for removal).
Inline C# from stdin (dotnet run -)
dotnet run - reads the whole program from standard input instead of a file — the npx/python - pattern, useful for CI steps that need a few dozen lines of real C# (typed args, File, Process) instead of fragile bash. Introduced in .NET SDK 10.0.100. It runs through the same file-based-app machinery as dotnet run app.cs — see dotnet-file-based-apps for directives and caching mechanics.
Heredoc (preferred in scripts — keeps the program readable in place) and pipe both work:
dotnet run - -- alpha beta <<'CS'
if (args is not [var a, var b]) { return 2; }
Console.WriteLine($"{a}/{b}");
CS
echo 'Console.WriteLine("hi");' | dotnet run -
cat probe.cs | dotnet run -Quote the heredoc marker — <<'CS', not <<CS. An unquoted marker lets the shell expand $VAR and `cmd` inside the C# before the compiler ever sees it, including inside a C# $"..." interpolated string — the shell eats the $ that C# also uses for interpolation. With <<'CS', $NAME reaches the compiler literally; read real values with Environment.GetEnvironmentVariable("NAME") instead.
cd to a scratch directory first ($RUNNER_TEMP in Actions, mktemp -d locally) before piping in the script. What crosses the cd: global.json SDK pinning is resolved from the caller's current directory, but — unlike a .cs file run in place — Directory.Build.props/Directory.Packages.props/nuget.config from the caller's directory do not apply, because dotnet run - materializes the piped source into its own cache directory under the runfile cache, not the caller's directory, and MSBuild's implicit-import walk starts there. Still cd out of the repo: it keeps the program's own relative file I/O (File.ReadAllText("./x")) predictable and away from a build tree, and it's the one habit that's correct across every environment.
Treat CI inputs as untrusted; never splice them into the C# text. Pass them as environment variables (env: SUITE: ${{ inputs.suite }}) or as dotnet run - -- "$SUITE" "$FILTER" app arguments, then read them with args is [var suite, var filter] or Environment.GetEnvironmentVariable. Interpolating ${{ inputs.x }} directly into the heredoc body is a script-injection hole — the value becomes literal C# text before the compiler runs. Write step outputs with File.AppendAllLines(Environment.GetEnvironmentVariable("GITHUB_OUTPUT")!, [...]); the program's return n; becomes the step's exit code, same propagation as any other dotnet run.
#:package/#:property directives at the top of the piped text work the same as in a file. Caching is the same content-hash mechanism as any file-based app, so a byte-identical rerun is fast — but each CI invocation typically differs (different args baked into env, or you tweak the script), so in practice you rarely get a cache hit, and there's no stable path for a later --no-build step to reuse. For a probe with real package dependencies, or anything you'll run more than a couple of times, save it as a checked-in .cs file instead of stdin — see dotnet-file-based-apps.
using static System.Environment;
if (args is not [var suite, var filter]) { return 2; }
// ... use suite/filter, GetEnvironmentVariable("GITHUB_WORKSPACE"), etc.
return 0;Common options
| Option | Behaviour |
-c|--configuration <CFG> | Build/run configuration (default Debug). |
-f|--framework <TFM> | Required when the project multi-targets. |
-r|--runtime, -a|--arch, --os | Target RID / architecture / OS for restore and run. |
--no-build | Skip building; implies --no-restore. Requires the output to already exist for the requested config/TFM, or the launch fails with a "No such file or directory" process-start error. |
--no-restore | Skip implicit restore only. |
--no-cache | Skip up-to-date checks and always rebuild (mainly meaningful for file-based apps; see dotnet-file-based-apps). |
-lp|--launch-profile <NAME> | Select a named profile from launch settings (case-insensitive; ambiguous case-only differences error). |
--no-launch-profile | Ignore launch settings entirely. |
-e|--environment <K=V> | Set an env var for the launched app only, not for dotnet run itself. Repeatable. Added in .NET SDK 9.0.200; not available on older 9.x SDKs. |
-v|--verbosity <LEVEL> | MSBuild verbosity for the build step. |
-p|--property:<K>=<V> | MSBuild properties, e.g. -p:Configuration=Release. |
--interactive | Allow the build to pause for credential prompts etc. |
--device / --list-devices | Device targeting/listing for mobile-style workloads; generally inert for ordinary console/library projects. |
launchSettings.json
Put profiles in Properties/launchSettings.json (My Project/launchSettings.json for VB). Top-level profiles object, one entry per named profile:
{
"profiles": {
"Local": {
"commandName": "Project",
"commandLineArgs": "--input sample.txt",
"environmentVariables": { "FOO": "bar" },
"applicationUrl": "http://localhost:5005"
}
}
}Two commandName values apply to dotnet run:
commandName | Behaviour |
Project | Build the project, run the command the project produces. commandLineArgs only applies when neither the CLI nor the project itself supplies arguments. |
Executable | Run executablePath directly (still builds first unless --no-build). workingDirectory, if relative, resolves against the launch-settings file's directory, not the project directory. |
Other commandName values (e.g. IDE-specific ones) are silently skipped when dotnet run is auto-selecting a default profile, and error if selected explicitly by name.
Behavior:
- Default profile: the first profile in file order whose
commandNameis onedotnet runsupports (ProjectorExecutable). No name needed to use it. environmentVariablesland in the launched process; explicitcommandLineArgson the CLI override the profile's, but the profile's still apply when you don't pass any.applicationUrlsetsASPNETCORE_URLSin the launched process (an explicitASPNETCORE_URLSinenvironmentVariablesor via-ewins over it).dotnet runsetsDOTNET_LAUNCH_PROFILEto the chosen profile's name in the child process.- Precedence, lowest to highest: ambient OS env → launch-profile
environmentVariables→-e/--environment. dotnet <app>.dllignoreslaunchSettings.jsonentirely — none of the profile's env vars, args, orDOTNET_LAUNCH_PROFILEshow up when launching the built DLL directly. Launch settings are adotnet run/IDE-only concept.- File-based apps use a flat
[AppName].run.jsonnext to the source file instead ofProperties/launchSettings.json— same profile schema. Seedotnet-file-based-apps.
Working directory and exit codes
- The launched app's working directory is the caller's current directory (wherever you invoked
dotnet runfrom), not the project directory — even when running via--project ../other/dir. Override withRunWorkingDirectoryin the project, orworkingDirectoryin anExecutable-type launch profile. dotnet runpropagates the app's exit code (returning42fromMain/top-level code surfaces asdotnet run's exit code).- stdin/stdout/stderr pass through directly — interactive console apps work normally under
dotnet run. - Ctrl+C is forwarded to the child process like any other console app; there's no extra shutdown hook from
dotnet runitself.
Performance
dotnet runbuilds on every invocation (an incremental build if nothing changed, but it still pays MSBuild evaluation overhead). For a tight edit/run loop where you're not changing code between runs — e.g. iterating on CLI args, or a script that runs the same binary many times — build once and add--no-buildon the repeat runs.- Build-server/MSBuild node reuse (the background
dotnet buildserver process) is the default;--disable-build-serversforces a cold in-process build if you suspect stale server state. - File-based apps have their own on-disk build cache (keyed on file content, directives, SDK version, implicit build files);
dotnet clean file-based-appsordotnet clean <file>.cs && dotnet build <file>.csclear/force it. Seedotnet-file-based-appsfor the details — this doesn't apply to ordinary projects, which use the normalobj/binincremental build.
Gotchas
- Directory.Build.props/targets, Directory.Packages.props, nuget.config, global.json are inherited from every parent directory — this applies to project-based runs too, not just file-based apps (see
dotnet-file-based-appsfor the file-based case). A project nested under a repo picks up that repo's analyzers, warning levels, and central package versions. - Microsoft.Testing.Platform (MTP) test projects are executables (
OutputType=Exe), sodotnet runon one works — it builds and runs the test binary directly, bypassing thedotnet test/VSTest orchestration layer (filtering,--results-directory, IDE test-explorer integration, etc.). Preferdotnet testfor anything beyond "does this compile and pass"; reach fordotnet runon a test project only for a quick single-run check. Seecsharp-tunit. DOTNET_ENVIRONMENT/ASPNETCORE_ENVIRONMENTcome from wherever you set them — ambient shell, or a launch profile'senvironmentVariables—dotnet runapplies no default environment name of its own.- A multi-targeted project without
-fdoesn't silently pick one; don't assume CI scripts using baredotnet runwill keep working after adding a second<TargetFrameworks>entry.
Checklist
- Is this for interactive development? If it's CI, a container, or production, use
dotnet publishoutput instead. - More than one project file in the directory, or targeting a solution? Pass
--project <specific .csproj>. - Multi-targeted project? Pass
-f <tfm>. - Passing arguments to the app? Put them after a literal
--. - Need dev-time config (env vars, URL, args)? Use a
launchSettings.jsonprofile, not hardcoded flags, sodotnet runand an IDE agree. - Running the same build repeatedly in a script? Build once, then
--no-buildon subsequent runs. - Single throwaway
.csfile, no project? Usedotnet-file-based-appsinstead of scaffolding a project.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

