Skip to content

Latest commit

 

History

History
332 lines (249 loc) · 15.9 KB

File metadata and controls

332 lines (249 loc) · 15.9 KB

Icon go#

Version Downloads EULA License

What is go#?

go# (go sharp) lets you run .cs files directly, like scripts, while still getting the full power of the .NET SDK (dependencies, compilation, AOT, etc.).

It shines for:

  • Quick one-off tools and prototypes
  • File-based apps without a .csproj
  • Fast iteration with smart caching (subsequent runs are near-instant when nothing changed)
  • Easy sharing of small utilities (just a .cs file or a remote ref like owner/repo[@ref][:path])

go# optimizes the underlying dotnet publish and dotnet run commands for file-based apps, with smart up-to-date checks of every C# file used to build the app (including #include and #ref directives, transitively), making it optimal for quick iteration and agentic tools authoring and consumption.

Usage

# Run a file
dnx go -- app.cs

# Pass arguments to your app
dnx go -- app.cs arg1 arg2

# Re-run a previous app from interactive history (no path/ref required)
dnx go

The default mode publishes the app with native AOT and then runs the resulting executable, with smart up-to-date checks of every C# file used to build the app (including #include and #ref directives, transitively).

Run history (MRU)

Every successful go / go dev invocation records the entry point (local full path or remote ref) in a shared history file next to the cache root (dotnet/go/go.toml).

With at least one history entry, running with no arguments in an interactive terminal opens a searchable picker (ordered by use count then recency). After you pick an entry you can optionally type app arguments (quoted groups supported). With an empty history, or when stdin is redirected / non-interactive (CI, pipes), dnx go with no args shows help instead of erroring.

Local paths that no longer exist are dropped from the picker; remote refs stay listed even when their download bundle is gone (the next run re-downloads as usual).

Gists are labeled as owner/shortsha:file (first seven characters of the gist id), for example kzu/0ac826d:run.cs, instead of the full host URL.

# Interactive: pick a previous run, then optional args
dnx go

# Same as always — also bumps that entry in history
dnx go -- app.cs
dnx go -- kzu/sandbox

Open local files and remote refs

Use open to shell-open a local file in the default app, or open the web URL for a remote ref in the browser — without downloading or running the app:

# Open a local file (editor / OS association)
dnx go -- open app.cs

# Open the GitHub/GitLab/gist web page for a remote ref
dnx go -- open kzu/sandbox
dnx go -- open kzu/sandbox@main:src/hello.cs
dnx go -- open gist.github.com/kzu/0ac826dc7de666546aaedd38e5965381

# Interactive: pick from MRU history, then open the selection
dnx go -- open

With no input, open uses the same history list as the default command (searchable picker). Empty history or a non-interactive terminal fails with a clear error instead of hanging. Opening does not record a new history entry.

Native AOT needs a platform C/C++ linker (VC++ build tools on Windows, build-essential on Ubuntu, Xcode Command Line Tools on macOS). Verify with:

dnx go -- check

On failure, the command prints the recommended install command for your OS (for example dnx vs -- install --passive --sku:build on Windows).

Use --r2r when your app needs more dynamic .NET features (reflection, dynamic loading, etc.) that native AOT does not support, while still keeping most publish optimizations:

dnx go -- app.cs --r2r

# Pass arguments to your app
dnx go -- app.cs --r2r arg1 arg2

This publishes with /p:PublishAot=false and /p:PublishReadyToRun=true. An equivalent --aot switch is not needed since native AOT is the default for file-based apps.

A dev mode is also available for faster iteration, which skips the publish step and runs the app directly from the build output without the optimizations applied by dotnet to published executables (i.e. AOT, RID-specific optimizations):

dnx go -- dev app.cs

# Pass arguments to your app
dnx go -- dev app.cs arg1 arg2

Remote references

Instead of a local .cs file, you can pass a remote reference. The tool will download the content (when needed) and treat the resulting local file as the entry point:

# Run from a public repo (defaults to github.com, main + program.cs or first .cs)
dnx go -- kzu/sandbox

# Specific branch/tag and file
dnx go -- kzu/sandbox@v1.2.3:src/hello.cs

# Full host (GitHub, Gist, GitLab, Azure DevOps)
dnx go -- github.com/kzu/sandbox@main:hello.cs
dnx go -- gist.github.com/kzu/0ac826dc7de666546aaedd38e5965381
# Multi-file gist: pick a specific entry file with :path (same as repos)
dnx go -- gist.github.com/kzu/0ac826dc7de666546aaedd38e5965381:run.cs
dnx go -- gitlab.com/kzu/runcs/-/blob/main/program.cs

Without :path, a gist (or repo) defaults to program.cs when present, otherwise the first top-level .cs file. Use :filename.cs when a multi-file gist should run a different entry point.

The first argument is resolved by first checking if it is a local file (File.Exists). If not, it falls back to parsing it as a remote ref (owner/repo[@ref][:path]).

Downloaded content is cached under the dotnet/go directory (same root as local apps) and participates in the normal up-to-date checks. Remote refs are always revalidated by sending a conditional request (using ETag when available) to the source. A 304 Not Modified response means the local copy is used as-is.

To force a fresh download for a remote ref, clean its bundle first:

# Clean the downloaded bundle for a remote ref (forces full download on next run)
dnx go -- clean kzu/sandbox

# Works for refs with @ref or :path too (the bundle for the ref is deleted entirely)
dnx go -- clean kzu/sandbox@main:program.cs

Behavior follows the chosen command:

  • Default command: downloads (if needed) then dotnet publish + execute (AOT by default).
  • dev command: downloads (if needed) then dotnet run for fast iteration.

Trailing arguments are passed to the app, the same as with local files.

Cache and cleaning

go# caches build and publish outputs per entry-point file under the user's temp area, which is what makes unchanged re-runs near-instant.

# Delete the cached artifacts for a single app (next run rebuilds)
dnx go -- clean app.cs

# Delete the downloaded bundle for a remote ref (next run re-downloads; :path ignored)
dnx go -- clean owner/repo[@ref][:path]

# Delete the cached artifacts for all apps
dnx go -- clean --all

clean only removes build/publish/download artifacts. Run history is left intact so the MRU picker still lists those apps (a later run simply rebuilds or re-downloads).

To drop history as well, use remove:

# Clean artifacts and remove this entry from MRU history
dnx go -- remove app.cs
dnx go -- remove owner/repo[@ref][:path]

# Wipe all cached apps and clear the entire MRU history
dnx go -- remove --all

Unused download locations and published binaries are periodically cleaned up in a detached background process. Apps you run regularly are never affected. Automatic cleanup updates lastCleanupUtc in go.toml but does not clear history.

Agent skill

go# ships a bundled agent skill that teaches coding agents how to author and run file-based C# apps with dnx go. Install it for global use or into the current repo:

# Install to ~/.agents/skills/go-sharp/SKILL.md (prompts for confirmation)
dnx go -- skill

# Install for the current project under .agents/skills/go-sharp/SKILL.md
dnx go -- skill .

# Skip the confirmation prompt
dnx go -- skill -y
dnx go -- skill . --yes

# Remove a previously installed skill (same path rules as install)
dnx go -- skill remove
dnx go -- skill remove .
dnx go -- skill remove -y

With no directory, the skill is written under the user home directory. Pass a base directory (commonly .) to install under that location instead. Either form overwrites an existing install.

Performance

The main advantage of go# is fast unchanged re-runs. The two core scenarios for go# file-based apps are:

  • While tweaking 👉 dnx go -- dev app.cs (optimized dotnet run app.cs)
  • When stable 👉 dnx go -- app.cs (optimized dotnet publish app.cs; app[.exe])

The numbers below showcase both scenarios, comparing go# to dotnet run and dotnet publish for a file-based app with different combinations of #include and #ref directives.


BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2)
AMD Ryzen AI 9 HX 370 w/ Radeon 890M 2.00GHz, 1 CPU, 24 logical and 12 physical cores
.NET SDK 11.0.100-preview.5.26302.115
  [Host]     : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v4
  Job-TASYDQ : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v4

InvocationCount=1  IterationCount=3  LaunchCount=1  
UnrollFactor=1  WarmupCount=1  

Method Sample Mean Error StdDev
'dnx go' #include 483.2 ms 85.70 ms 4.70 ms
'dotnet publish' #include 3,302.0 ms 1,003.60 ms 55.01 ms
'dnx go dev' #include 500.1 ms 250.72 ms 13.74 ms
'dotnet run' #include 475.6 ms 100.88 ms 5.53 ms
'dnx go' #include + #ref 481.4 ms 236.79 ms 12.98 ms
'dotnet publish' #include + #ref 3,474.3 ms 391.66 ms 21.47 ms
'dnx go dev' #include + #ref 498.3 ms 290.97 ms 15.95 ms
'dotnet run' #include + #ref 1,426.7 ms 318.56 ms 17.46 ms
'dnx go' #ref 487.1 ms 140.05 ms 7.68 ms
'dotnet publish' #ref 3,509.4 ms 616.31 ms 33.78 ms
'dnx go dev' #ref 505.2 ms 264.59 ms 14.50 ms
'dotnet run' #ref 1,413.9 ms 267.93 ms 14.69 ms
'dnx go' minimal 480.8 ms 486.66 ms 26.68 ms
'dotnet publish' minimal 3,237.4 ms 567.55 ms 31.11 ms
'dnx go dev' minimal 488.4 ms 209.11 ms 11.46 ms
'dotnet run' minimal 482.5 ms 507.46 ms 27.82 ms

Open Source Maintenance Fee

To ensure the long-term sustainability of this project, users of this package who generate revenue must pay an Open Source Maintenance Fee. While the source code is freely available under the terms of the License, this package and other aspects of the project require adherence to the Maintenance Fee.

To pay the Maintenance Fee, become a Sponsor at the proper OSMF tier. A single fee covers all of Devlooped packages.


Sponsors

Clarius Org MFB Technologies, Inc. SandRock DRIVE.NET, Inc. Keith Pickford Thomas Bolon Kori Francis Reuben Swartz Jacob Foshee Eric Johnson Jonathan Ken Bonny Simon Cropp agileworks-eu Zheyu Shen Vezel ChilliCream 4OTC domischell Adrian Alonso torutek Ryan McCaffery Seika Logiciel Andrew Grant eska-gmbh Geodata AS

Sponsor this project

Learn more about GitHub Sponsors