DotMake.CommandLine
3.7.0
dotnet add package DotMake.CommandLine --version 3.7.0
NuGet\Install-Package DotMake.CommandLine -Version 3.7.0
<PackageReference Include="DotMake.CommandLine" Version="3.7.0" />
<PackageVersion Include="DotMake.CommandLine" Version="3.7.0" />
<PackageReference Include="DotMake.CommandLine" />
paket add DotMake.CommandLine --version 3.7.0
#r "nuget: DotMake.CommandLine, 3.7.0"
#:package DotMake.CommandLine@3.7.0
#addin nuget:?package=DotMake.CommandLine&version=3.7.0
#tool nuget:?package=DotMake.CommandLine&version=3.7.0
DotMake Command-Line
System.CommandLine is a very good parser but you need a lot of boilerplate code to get going and the API is hard to discover.
This becomes complicated to newcomers and also you would have a lot of ugly code in your Program.cs to maintain.
What if you had an easy class-based layer combined with a good parser?
DotMake.CommandLine is a library which provides declarative syntax for System.CommandLine via attributes for easy, fast, strongly-typed (no reflection) usage. The library includes a source generator which automagically converts your classes to CLI commands and properties to CLI options or CLI arguments. Supports trimming, AOT compilation and dependency injection!


Getting started
Install the library to your console app project with NuGet.
In your project directory, via dotnet cli:
dotnet add package DotMake.CommandLine
or in Visual Studio Package Manager Console:
PM> Install-Package DotMake.CommandLine
Prerequisites
- .NET 8.0 and later project or .NET Standard 2.0 and later project.
Note that .NET Framework 4.7.2+ or .NET Core 2.0 to .NET 7.0 projects can reference our netstandard2.0 target (automatic in nuget).
If your target framework is below net5.0, you also need<LangVersion>9.0</LangVersion>tag (minimum) in your .csproj file. - Visual Studio 2022 v17.3+ or .NET SDK 6.0.407+ (when building via
dotnetcli).
Our incremental source generator requires performance features added first in these versions. - Usually a console app project but you can also use a class library project which will be consumed later.
Usage
DotMake.CommandLine offers 2 models: class-based model and delegate-based model. Delegate-based model is useful for simple apps, for more complex apps, you should use the class-based model because you can have sub-commands and command inheritance.
Class-based model
Create a CLI App with DotMake.Commandline in seconds!
In Program.cs, add this simple code:
using System;
using DotMake.CommandLine;
// Add this single line to run you app!
Cli.Run<RootCliCommand>(args);
// Create a simple class like this to define your root command:
[CliCommand(Description = "A root cli command")]
public class RootCliCommand
{
[CliOption(Description = "Description for Option1")]
public string Option1 { get; set; } = "DefaultForOption1";
[CliArgument(Description = "Description for Argument1")]
public string Argument1 { get; set; }
public void Run()
{
Console.WriteLine($"Handler for '{GetType().FullName}' is run:");
Console.WriteLine($"Value for {nameof(Option1)} property is '{Option1}'");
Console.WriteLine($"Value for {nameof(Argument1)} property is '{Argument1}'");
Console.WriteLine();
}
}
And that's it! You now have a fully working command-line app.
You just specify the name of your class which represents your root command to Cli.Run<> method and everything is wired.
argsis the string array typically passed to a program. This is usually the special variableargsavailable inProgram.cs(new style with top-level statements) or the string array passed to the program'sMainmethod (old style). We also have method signatures which does not requireargs, for example you can also callCli.Run<RootCliCommand>()and in that caseargswill be retrieved automatically from the current process viaCli.GetArgs().
If you want to go async, just use this:
await Cli.RunAsync<RootCliCommand>(args);
To handle exceptions, you just use a try-catch block:
try
{
Cli.Run<RootCliCommand>(args);
}
catch (Exception e)
{
Console.WriteLine(@"Exception in main: {0}", e.Message);
}
System.CommandLine, by default overtakes your exceptions that are thrown in command handlers
(even if you don't set an exception handler explicitly) but DotMake.CommandLine, by default allows
the exceptions to pass through. However if you wish, you can easily use the default exception handler
by passing a CliSettings instance like below. Default exception handler prints the exception in red color to console:
Cli.Run<RootCliCommand>(args, new CliSettings { EnableDefaultExceptionHandler = true });
If you need to simply parse the command-line arguments without invocation, use this:
var result = Cli.Parse<RootCliCommand>(args);
var rootCliCommand = result.Bind<RootCliCommand>();
If you need to examine the parse result, such as errors:
var result = Cli.Parse<RootCliCommand>(args);
if (result.ParseResult.Errors.Count > 0)
{
}
Summary
Mark the class with
[CliCommand]attribute to make it a CLI command (see CliCommandAttribute and Commands docs for more info).Mark a property with
[CliOption]attribute to make it a CLI option (see CliOptionAttribute and Options docs for more info).Mark a property with
[CliArgument]attribute to make it a CLI argument (see CliArgumentAttribute and Arguments docs for more info).Add a method with name
RunorRunAsyncto make it the handler for the CLI command. The method can have one of the following signatures:-
void Run() -
int Run() -
async Task RunAsync() -
async Task<int> RunAsync()
Optionally the method signature can have a
CliContextparameter in case you need to access it:-
Run(CliContext context) -
RunAsync(CliContext context)
We also provide interfaces
ICliRun,ICliRunWithReturn,ICliRunWithContext,ICliRunWithContextAndReturnand async versionsICliRunAsync,ICliRunAsyncWithReturn,ICliRunAsyncWithContext,ICliRunAsyncWithContextAndReturnthat you can inherit in your command class. Normally you don't need an interface for a handler method as the source generator can detect it automatically, but the interfaces can be used to prevent your IDE complain about unused method in class.The signatures which return int value, sets the ExitCode of the app. If no handler method is provided, then by default it will show help for the command. This can be also controlled manually by
ShowHelp()method ofCliContext. Other methodsShowValues()andShowHierarchy()are also useful.-
Call
Cli.Run<>orCli.RunAsync<>method with your class name to run your CLI app (see Cli.Run, Cli.RunAsync and Model binding docs for more info).For best practice, create a subfolder named
Commandsin your project and put your command classes there so that they are easy to locate and maintain in the future.
Delegate-based model
Create a CLI App with DotMake.Commandline in seconds!
In Program.cs, add this simple code:
using System;
using DotMake.CommandLine;
Cli.Run(([CliArgument]string arg1, bool opt1) =>
{
Console.WriteLine($"Value for {nameof(arg1)} parameter is '{arg1}'");
Console.WriteLine($"Value for {nameof(opt1)} parameter is '{opt1}'");
});
And that's it! You now have a fully working command-line app.
Summary
- Pass a delegate (a parenthesized lambda expression or a method reference) which has parameters that represent your options and arguments, to
Cli.Run<>orCli.RunAsync<>method (see Cli.Run, Cli.RunAsync and Model binding docs for more info). - A parameter is by default considered as a CLI option but you can;
- Mark a parameter with
[CliArgument]attribute to make it a CLI argument and specify settings (see CliArgumentAttribute and Arguments docs for more info). - Mark a parameter with
[CliOption]attribute to specify CLI option settings (see CliOptionAttribute and Options docs for more info). - Mark the delegate itself with
[CliCommand]attribute to specify CLI command settings (see CliCommandAttribute and Commands docs for more info). - Note that for being able to mark a parameter with an attribute in an anonymous lambda function,
if your target framework is below net6.0, you also need
<LangVersion>10.0</LangVersion>tag (minimum) in your .csproj file.
- Mark a parameter with
- Set a default value for a parameter if you want it to be optional (not required to be specified on the command-line).
- Your delegate can be
async. - Your delegate can have a return type
voidorintand if it's asyncTaskorTask<int>.
Links
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.CommandLine (>= 2.0.11 && < 3.0.0)
-
net8.0
- System.CommandLine (>= 2.0.11 && < 3.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on DotMake.CommandLine:
| Repository | Stars |
|---|---|
|
rr-wfm/MSBuild.Sdk.SqlProj
An MSBuild SDK that provides similar functionality to SQL Server Data Tools (.sqlproj) projects
|
| Version | Downloads | Last Updated |
|---|---|---|
| 3.7.0 | 90 | 9/7/2026 |
| 3.6.0 | 2,622 | 8/22/2026 |
| 3.5.0 | 20,973 | 5/8/2026 |
| 3.2.0 | 9,989 | 4/14/2026 |
| 3.1.0 | 35,537 | 12/9/2025 |
| 3.0.0 | 6,522 | 11/11/2025 |
| 2.8.2 | 6,016 | 10/12/2025 |
| 2.8.1 | 21,159 | 9/27/2025 |
| 2.8.0 | 1,485 | 9/12/2025 |
| 2.7.1 | 1,340 | 9/2/2025 |
| 2.7.0 | 39,510 | 8/16/2025 |
| 2.6.8 | 672 | 8/11/2025 |
| 2.6.7 | 17,024 | 7/25/2025 |
| 2.6.6 | 942 | 7/21/2025 |
| 2.6.4 | 170 | 7/19/2025 |
| 2.6.2 | 1,414 | 7/17/2025 |
| 2.6.0 | 8,880 | 7/15/2025 |
| 2.5.8 | 307 | 7/14/2025 |
| 2.5.6 | 2,224 | 6/28/2025 |
- **Improved:** Redesigned `CliNamer` class. From now on, auto generated short aliases for commands and options
will be single character only to be compatible with POSIX conventions
(to prevent confusion with argument bundling/clustering e.g. `-wp` meaning `-w` and `-p`).
This may be a BREAKING CHANGE in your CLI app so check your short aliases if you depended on auto-generation.
The new rules:
First letter of the name will be used to create short form which is converted according to `[CliCommand].NameCasingConvention` property;
if it conflicts, the case of the letter is changed;
if it conflicts again, first letter of the next word in the name is tried and so on.
(e.g. `Info` -> `i` or `I`, `ServerPort` -> `s` or `S` or `p` or `P`, `Option1` -> `o` or `O`)
No short form alias will be added if a non-conflicting one can not be found, user can manually set a specific alias for these missing ones.
For options, single hyphen/dash prefix is added to the short form.
(e.g. `Info` -> `-i` or `-I`, `ServerPort` -> `-s` or `-S` or `-p` or `-P`, `Option1` -> `-o` or `-O`)
This can be changed via `[CliCommand].ShortFormPrefixConvention` property (default: SingleHyphen).
More robust handling of conflicts in `CliNamer`, i.e. checks all conflicts at once and then throws an exception which
lists all errors.
- **Improved:** Strong name sign the DLLs for NuGet package. Signing will be done only when `DotMake.snk` exists,
it's not committed to this repository.