.NET · source-generated · AOT-safe

Write methods.
Get a CLI.

Methods become commands, XML docs become help text, records become option sets. A Roslyn source generator emits parsing, routing, and dispatch directly into your assembly — no reflection, no runtime overhead, trimming- and AOT-safe from day one.

nuget ↗
nuget ↗
mytool --help
$ mytool --help
 
Usage: mytool <command> [options]
 
Commands:
  deploy Deploy a release to an environment
  rollback Roll back to the previous version
  status Show current deployment status
 
$ mytool deploy --help
 
Usage: mytool deploy [options]
  Deploy a release to an environment.
 
  --env <string> Target environment [required]
  --version <string> Release version tag
  --dry-run <bool> Simulate without applying
 
$

Everything a CLI needs.
None of the plumbing.

The generator emits a typed dispatch tree, option parsers, help printers, and completion tables directly into your assembly at build time. Nothing to configure at runtime, nothing that can go wrong at startup.


The full picture.

Everything you expect from a modern .NET CLI framework — generated at build time so none of it costs startup time or binary size.

  • DTO binding with [AsParameters] — records and classes expand into flags without a custom bind loop
  • DataAnnotations validation[Range], [StringLength], [AllowedValues] — constraints appear in --help, failures exit 2
  • Fuzzy matching — typos produce actionable errors with ranked suggestions
  • CancellationToken injection — add it to a handler and it tracks Ctrl+C or host shutdown
  • Middleware pipeline — cross-cutting concerns wired once, applied to every command
  • Global and namespace options — flags that apply to a whole tree, parsed once, injected everywhere
  • Microsoft.Extensions.HostingNullean.Argh.Hosting plugs into IHost and DI with one call
  • Zero dep or ME.* nativeNullean.Argh has no Microsoft.Extensions.* dependency

One package.
Then write methods.

Two entry points depending on whether you need DI. Either way, registration is a single method call per command — the generator does the rest.

01
Add the package

Console apps use Nullean.Argh. Hosted apps with DI use Nullean.Argh.Hosting. Everything else is pulled in transitively.

02
Map your handlers

Method groups, lambdas, or whole classes. The generator discovers every registration at build time and emits typed dispatch code.

03
Document with XML docs

Add <summary> and <param> tags to your methods. That is your help text — no duplication, no string literals.

console app
using Nullean.Argh;

var app = new ArghApp();
app.Map("deploy", DeployHandlers.Run);
app.Map("status", StatusHandlers.Show);

return await app.RunAsync(args);
handler with xml docs
/// <summary>Deploy a release to an environment.</summary>
/// <param name="env">Target environment.</param>
/// <param name="dryRun">Simulate without applying.</param>
public static async Task<int> Run(
    string env,
    bool dryRun = false)
{
    // --env and --dry-run are generated
    return 0;
}
hosted app (DI)
using Nullean.Argh.Hosting;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddArgh(args, b =>
    b.Map("deploy", DeployHandlers.Run));
await builder.Build().RunAsync();

Write the method.
Ship the CLI.

One package. XML docs you already write. A source generator that handles everything else.