CommandLineUtils: attribute and builder APIs for .NET console apps
Command line parsing and utilities for .NET
At a glance
- What is it?
- A community fork of an abandoned Microsoft library, offering two ways to declare options in a .NET console app. Its own README says it is in maintenance mode while its releases include a breaking major.
- Who is it for?
- CommandLineUtils is a reasonable pick for a .NET console tool that wants help text and validation without hand-writing a parser, and the two API styles mean you can adopt it in an existing project without restructuring. Its central risk is stated on its own README: the project says it entered maintenance mode in 2022 with only critical fixes planned, while its release history includes a 5.0 major that dropped older targets.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 102 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the library actually saves you from writing
The README describes the project in three clauses: it helps you create command line applications using .NET, and it simplifies parsing arguments provided on the command line, validating user input, and generating help text.
That third clause is the one that usually decides it. Help text is the part every hand-rolled parser gets wrong, because it needs to stay in sync with the options and a developer has to remember to update it. Here the help is generated from the same declaration that defines the option, so a single attribute or one builder call is the source for both.
Validation is the second saving. Required options and constraints are expressed in the declaration, and the README's samples list a dedicated folder for required options and arguments, alongside samples for hello world, async console apps, subcommands, attributes and interactive prompts. The existence of that sample list is a reasonable proxy for what the library considers its core use cases.
It is worth being clear about the category. This is argument parsing and console interaction, not a command framework in the sense of a plugin system or a job runner. There is an `IConsole` and an `IReporter` in the wider API for abstracting the terminal, but the scope stays on parsing and interaction.
Installing from NuGet and the two ways to declare options
Installation is a single package reference:
$ dotnet add package McMaster.Extensions.CommandLineUtils`CommandLineApplication` is the main entry point, and the README states there are two primary ways to use the API: the builder pattern and attributes. Both produce the same help output and the same validation, so the choice is stylistic rather than a trade-off in capability.
The builder form constructs an application object and attaches options to it:
var app = new CommandLineApplication();
app.HelpOption();
var subject = app.Option("-s|--subject <SUBJECT>", "The subject", CommandOptionType.SingleValue);
subject.DefaultValue = "world";The option template string carries both the short and long name and declares that a value is required, using angle brackets around the value placeholder. `CommandOptionType.SingleValue` states the arity explicitly, which matters for options that are flags rather than values. The separate `DefaultValue` assignment is why the hello world sample prints a subject even when nothing was passed.
The attribute form declares the same information on a class, with `Execute<T>` wiring the arguments in:
public class Program
{
public static int Main(string[] args)
=> CommandLineApplication.Execute<Program>(args);
[Option(Description = "The subject")]
public string Subject { get; } = "world";
}Note that the properties are get-only with initialisers, and that the conventional entry point returns the exit code as an int. That shape matters for a console tool, since the process exit code is often the only thing a script or CI job reads. The attributes live on the class rather than on local variables, so validation happens during binding, and the naming convention is what maps a property to an option name.
Console utilities beyond parsing: escaping, prompts and finding dotnet
The README lists three utilities that are useful precisely because they solve problems that look trivial and are not. Each is small, and each has a documented code sample.
`ArgumentEscaper` exists for when you start a new process, because arguments with spaces and quotes have to be escaped before they are handed to a shell-like parser:
var args = new [] { "Arg1", "arg with space", "args ' with \" quotes" };
Process.Start("echo", ArgumentEscaper.EscapeAndConcatenate(args));That sample doubles as a warning. Writing this by hand is a recurring source of command injection bugs, and the sample string deliberately includes both single and double quotes to show the awkward cases.
`Prompt` covers interactive input, including a yes-or-no question with a default answer and a password prompt that masks what the user types with asterisks. Both are relevant when your console app is a setup or configuration tool rather than a filter in a pipeline. The masking behaviour is the part that should stop you writing your own.
`DotNetExe` locates the path to the dotnet executable used to start a .NET process, with a `FullPathOrDefault` method for the case where you simply want the current runtime. The README's sample is a single line that starts `dotnet run` from a known path. Both utilities point at the same underlying problem: a console app that shells out to other tools needs to know where its own runtime lives, and the answer varies by how the app was published.
Maintenance mode in the README, a breaking major in the releases
The README contains a paragraph that deserves close reading. The project is a fork of Microsoft.Extensions.CommandLineUtils, which it says was completely abandoned by Microsoft. The fork happened in 2017 and continued to take improvements, with over 30 contributors adding features and fixing bugs from 2017 to 2021. Then it says that as of 2022 the project has entered maintenance mode, so no major changes are planned, and that only the most critical of bugs will be fixed, such as security issues.
The release history tells a different story. Version 5.0.0 was published on 2026-01-21 and its notes carry a breaking-change marker on the change that bumps the version to 5.0 and drops support for .NET Standard 2 and anything below .NET 6. That is a major version with a target framework removal in it, published four years after the README said no major changes were planned.
Both statements are in front of you, and neither one makes the other false. A maintainer can genuinely believe no new features are planned while still accepting that a framework support policy forces a version bump, and the 5.0.0 notes are otherwise mostly dependency and tooling work rather than redesign. What it does mean is that the README paragraph is not a reliable guide to what will happen across a major version.
The check that resolves it is named in the README itself: the roadmap is pinned to the top of the issue list, and the paragraph links an issue for the latest project status. Read that issue and the release notes for the major you would be skipping, rather than relying on the maintenance-mode sentence. The activity record supports taking releases seriously, since the last push was on 2026-07-01 and 5.1.0 arrived on 2026-04-05.
The supported versions table and the frameworks 5.0.0 dropped
The README ends with a support table. It lists `dotnet` 8.0, described as the lowest Microsoft LTS version at the time of release, and .NET Framework 4.7.2, described as the lowest .NET Framework version fully compatible with .NET Standard 2.0. A note below it quotes Microsoft guidance that .NET 5 and later adopt a different approach to uniformity that removes the need for .NET Standard in most scenarios, and says compiling for two frameworks appears sufficient to avoid added complexity.
The 5.0.0 release notes say that .NET Standard 2 and everything below .NET 6 were dropped. Read alongside the table, those two documents disagree about the same subject. The table's rationale is expressed entirely in terms of .NET Standard 2.0 compatibility, which is the framework the release notes removed.
This is the kind of drift you get when a table written for one major survives into the next. The resolvable version is in the repository itself rather than in either document. The tree carries Directory.Build.props, which is where target frameworks are declared for the solution, and the 5.1.0 notes record a change described as adding support for keyed dependency injection and updating target frameworks. That update is itself evidence that the framework list was being changed at 5.1, after 5.0 had already moved it.
So if your application still targets .NET Framework, check Directory.Build.props before assuming the table describes the package you would install. If it targets .NET 8 or later, the 5.x line is the one to be on and the older rows are history.
Where to look when the README runs out
The README is an index more than a manual. It points to hosted documentation for the API reference, samples and tutorials, and it names a samples folder in the repository with runnable projects for hello world, async console apps, subcommands, attributes, interactive prompts and validation.
It is equally clear about where to take questions. Read the documentation first, then look at the samples, then review existing issues, including the ones already labelled as questions, and only then consider a general programming forum. That ordering is a reasonable signal about how much of the library's knowledge lives in its issue tracker.
The tree reinforces the picture of a small, well-run project. There is a solution file, a Directory.Build.props for shared build settings, a build.ps1 for the build script, a test directory, a CHANGELOG.md, a CODE_OF_CONDUCT.md, a CONTRIBUTING.md, and both a LICENSE.txt and a NOTICE.txt, which is the Apache 2.0 convention of carrying attribution separately from the licence text.
The deeper API is referenced rather than described: `IConsole` and `IReporter` are named as further utilities to look up, alongside the argument escaper, prompt and dotnet locator. If your program needs to be testable without a terminal, those two interfaces are where the design pays off, and they are the first thing to read after the parsing basics.
Editorial conclusion
CommandLineUtils is a reasonable pick for a .NET console tool that wants help text and validation without hand-writing a parser, and the two API styles mean you can adopt it in an existing project without restructuring. Its central risk is stated on its own README: the project says it entered maintenance mode in 2022 with only critical fixes planned, while its release history includes a 5.0 major that dropped older targets. Before you depend on it, read the roadmap issue pinned to the top of the issue list, check the target frameworks declared in Directory.Build.props against your runtime, and confirm the 5.x release notes line up with what you upgraded from.
Frequently asked questions
How do I add CommandLineUtils to a .NET project?
Add the NuGet package with dotnet add package McMaster.Extensions.CommandLineUtils. The main entry point is CommandLineApplication, and options are declared either with the builder pattern or with attributes on a class whose properties are bound by CommandLineApplication.Execute.
Should I use the attribute API or the builder API?
The README describes both as primary ways to use the library, with no stated difference in capability. The attribute form puts each option next to the property it fills and binds through Execute, while the builder form attaches options to a CommandLineApplication instance explicitly, which is easier to follow when options are assembled conditionally.
Is Microsoft.Extensions.CommandLineUtils still maintained?
The README says the original was completely abandoned by Microsoft, and that this project forked it in 2017 and has continued since. Microsoft.Extensions.CommandLineUtils is therefore not a route to new work, while the fork states that as of 2022 it is in maintenance mode with only critical fixes planned, though it has since shipped 5.x releases.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/natemcmaster-commandlineutils)