Skip to content


Commands are defined by methods and classes.

Using our calculator example...

public class Program
    // this is the entry point of your application
    static int Main(string[] args)
        // AppRunner<T> where T is the class defining your commands
        // You can use Program or create commands in another class
        return new AppRunner<Program>().Run(args);

    // Add command with two positional arguments
    public void Add(int x, int y) => Console.WriteLine(x + y);

    // Subtract command with two positional arguments
    public void Subtract(int x, int y) => Console.WriteLine(x - y);
snippet source | anchor

Program is the root command and is not directly referenced in the terminal. The root command is the type specified in AppRunner<TRootCommand>

Add and Subtract are the commands.

Command methods must:

  • be public
  • return void, int, Task or Task<int>
    • when the return type is int or Task<int> the value is used as the exit code.

Command methods may be async.

Only public methods defined within the class will be commands. Methods from base classes are not included. Set AppSettings.Commands.InheritCommandsFromBaseClasses = true to include public methods from base classes. Methods from System.Object and IDisposable are not included.

Command Attribute#

Every public method will be interpreted as a command and the command name will be the method name.

Use the [Command] attribute to change the command name, enhance help output and provide parser hints.

    Usage = "sum <int> [<int> ...]",
    Description = "sums all the numbers provided",
    ExtendedHelpText = "more details and examples could be provided here")]
public void Add(IConsole console, IEnumerable<int> numbers) =>
snippet source | anchor

$ dotnet calculator.dll Sum --help
sums all the numbers provided

Usage: sum <int> [<int> ...]


  numbers (Multiple)  <NUMBER>

more details and examples could be provided here
snippet source | anchor


Use of [Command] attribute is optional and only required when you need to add customizations.

Use IgnoreUnexpectedOperands & ArgumentSeparatorStrategy to override argument parsing behavior for the command. See Argument Separator for more details.

Use Usage to override the auto-generated usage section.

Use Description & ExtendedHelpText to include additional information in help output.

ExtendedHelpText on the root command is a good place to mention additional features, such as any directives the user could take advantage of

ExtendedHelpText = "Directives:\n" +
                   "  [debug] to attach a debugger to the app\n" +
                   "  [parse] to output how the inputs were tokenized\n" +
                   "  [log] to output framework logs to the console\n" +
                   "  [log:debug|info|warn|error|fatal] to output framework logs for the given level or above\n" +
                   "\n" +
                   "directives must be specified before any commands and arguments.\n" +
                   "\n" +
                   "Example: %AppName% [debug] [parse] [log:info] math")]
snippet source | anchor


If you're looking to change the app name, set AppSettings.Help.UsageAppName and use the %AppName% template variable mentioned below. Learn more in Help

Template variables#

Two template variables are available for use in Usage, Description and ExtendedHelpText: %AppName% and %CmdPath%

public class Program
    static int Main(string[] args) => AppRunner.Run(args);
    public static AppRunner AppRunner => new AppRunner<Program>().UseNameCasing(Case.KebabCase);

    public class Stash
        public void StashImpl(IConsole console) => console.WriteLine("stash");

        [Command(Usage = "%AppName% %CmdPath%")]
        public void Pop(IConsole console) => console.WriteLine("pop");
snippet source | anchor

help for Pop may have Usage: git Stash Pop

  • %AppName% = git
  • %CmdPath% = Git Stash Pop

This is useful when you want to override the Usage example and still want to include how the command would be called.


Use %AppName% to include the name as calculated by CommandDotNet. This will use AppSettings.Help.UsageAppName if it's set.


Use %CmdPath% to include the full path of commands. This is helpful when working with subcommands.

Default Command#

Let's assume we have a program with a single command defined, called Process.

public class Program
    static int Main(string[] args) => AppRunner.Run(args);
    public static AppRunner AppRunner => new AppRunner<Program>();

    public void Process()
        // do very important stuff
snippet source | anchor

Because the Program is considered the RootCommand, you'll need to explicitely call the Process command. eg.

dotnet myapp.dll Process

If the root command has only one command, you may want to exectute it by default without specifying any commands names. We can do this with the [DefaultCommand] attribute.

Now executed as

dotnet myapp.dll

Default commands are also useful in cases like Git Stash above where a command can have subcommands, but can execute an action when no subcommand is specified.

Back to top