Build full-featured .NET CLIs without writing a parser.
Methods become commands, XML docs become help text, records become option sets. A Roslyn source generator emits parsing, routing, dispatch, and help into your assembly at build time — no reflection, no runtime overhead, trimming- and AOT-safe by default.
Write vanilla C# and get a fully functional CLI in return: rich --help output, shell tab-completions for bash, zsh, and fish, and a machine-readable JSON schema ready for agentic use cases — all without writing a single line of plumbing code for any of it.
Heavily Inspired by ConsoleAppFramework (Cysharp) — rewritten from scratch with a different feature set, but ConsoleAppFramework laid out the path for source-generated CLI's in .NET.
- XML docs are your help text — summaries, param descriptions, remarks, and
<example>blocks appear in--helpautomatically - Everything is generated C# — typed dispatch tree, option parsers, and help printers emitted directly into your assembly
MapGroup-style namespaces — nested command groups with their own help pages and scoped option types- DTO binding with
[AsParameters]— records and classes expand into flags without a custom bind loop - Shell completions built-in — bash, zsh, fish; one install command per shell
- Agent-ready schema —
myapp __schemaemits full JSON; conforms to cli-schema v1 - Fuzzy matching — typos produce actionable errors with suggestions
- DataAnnotations validation —
[Range],[StringLength],[AllowedValues],[Existing], and more — constraints in--help, failures exit 2 - CancellationToken injection — add it to a handler, it tracks Ctrl+C (or host shutdown with Hosting)
- Zero-dep or ME. native* —
Nullean.Arghhas noMicrosoft.Extensions.*dependency;Nullean.Argh.Hostingplugs intoIHostand DI
| Package | NuGet | Description |
|---|---|---|
Nullean.Argh |
Metapackage for console apps (Core + Interfaces) | |
Nullean.Argh.Hosting |
Microsoft.Extensions.Hosting integration | |
Nullean.Argh.Core |
Shared runtime + embedded source generator (transitive) | |
Nullean.Argh.Interfaces |
Contracts and attributes (transitive, or for shared libs) |
Which package do I need?
Nullean.Argh— dependency-free console appsNullean.Argh.Hosting— apps usingMicrosoft.Extensions.Hosting/ DI
Everything else is pulled in transitively.
<PackageReference Include="Nullean.Argh" />using Nullean.Argh;
var app = new ArghApp();
app.Map("hello", MyHandlers.SayHello);
return await app.RunAsync(args);<PackageReference Include="Nullean.Argh.Hosting" />using Microsoft.Extensions.Hosting;
using Nullean.Argh.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddArgh(args, b =>
{
b.Map("hello", MyHandlers.SayHello);
});
await builder.Build().RunAsync();// Method group — direct typed dispatch
app.Map("deploy", DeployHandlers.Run);
// Lambda — one-liners
app.Map("greet", (string name) => Console.WriteLine($"Hello, {name}!"));
// Class — every public method becomes a command
app.Map<StorageHandlers>();
// Namespaces — nested command groups
app.MapNamespace<StorageCommands>("storage", ns =>
{
ns.MapNamespace<BlobCommands>("blob");
});// Positional arguments
public static Task<int> Deploy([Argument] string environment) { … }
// Named flags (auto kebab-case)
public static Task<int> Build(string outputDir, bool release = false) { … }
// → --output-dir ./bin --release
// DTO binding
public record DeployOptions(string Environment, bool DryRun = false);
public static Task<int> Deploy([AsParameters] DeployOptions opts) { … }
// Validation
public static void Run(
[Range(1, 65535)] int port,
[AllowedValues("dev", "staging", "prod")] string env) { … }eval "$(myapp __completion bash)" # bash
source <(myapp __completion zsh) # zsh
myapp __completion fish > ~/.config/fish/completions/myapp.fish # fishmyapp __schema > cli-schema.jsonEmits a full JSON description conforming to cli-schema v1 — feed it to an LLM, generate docs, or diff in CI.
See examples/ for complete working projects:
Basic— minimal console appHosted— Microsoft.Extensions.HostingHostedRoot— hosted with root commandXmlDocShowcase— full XML doc tag inventoryArghAotSmoketest— Native AOT validation
