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.
// what you get
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.
Summaries, param descriptions, remarks, and <example> blocks flow
directly into --help. Document the method, get the help page.
Parsing, routing, and dispatch are emitted as plain C# into your assembly. No
MethodInfo.Invoke, no dynamic dispatch — just a switch tree the
compiler can see and optimize.
No reflection means no linker annotations, nothing trimmed away at publish. Ship a sub-10 MB native binary that starts in milliseconds — all features, no runtime dependency.
aot guide →
Nest command groups with their own help pages and scoped option types —
exactly like MapGroup in ASP.NET. Depth unlimited; help renders
per-namespace.
Tab-completion for all three major shells is generated and registered with a single intrinsic command. No extra package, no shell script to maintain.
completions →
myapp __schema emits a machine-readable description of every
command and option. Conforms to the open cli-schema v1 spec — ready for
agentic use cases out of the box.
// more features
Everything you expect from a modern .NET CLI framework — generated at build time so none of it costs startup time or binary size.
[AsParameters] — records and classes expand into flags without a custom bind loop[Range], [StringLength], [AllowedValues] — constraints appear in --help, failures exit 2Nullean.Argh.Hosting plugs into IHost and DI with one callNullean.Argh has no Microsoft.Extensions.* dependency// quick start
Two entry points depending on whether you need DI. Either way, registration is a single method call per command — the generator does the rest.
Console apps use Nullean.Argh. Hosted apps with DI use Nullean.Argh.Hosting. Everything else is pulled in transitively.
Method groups, lambdas, or whole classes. The generator discovers every registration at build time and emits typed dispatch code.
Add <summary> and <param> tags to your methods. That is your help text — no duplication, no string literals.
using Nullean.Argh; var app = new ArghApp(); app.Map("deploy", DeployHandlers.Run); app.Map("status", StatusHandlers.Show); return await app.RunAsync(args);
/// <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; }
using Nullean.Argh.Hosting; var builder = Host.CreateApplicationBuilder(args); builder.Services.AddArgh(args, b => b.Map("deploy", DeployHandlers.Run)); await builder.Build().RunAsync();
// packages
Pick the package that fits your app model. Core, Interfaces, and the embedded source generator are pulled in automatically.
No Microsoft.Extensions.* dependency. The default for CLI tools that
run and exit — lightweight, zero-dep, AOT-ready.
Integrates with Microsoft.Extensions.Hosting. Wires the CLI as an
IHostedService, connects CancellationToken to host lifetime.
Full DI support.
One package. XML docs you already write. A source generator that handles everything else.