Working with Multiple Command Hierarchies

How to create hierarchical (nested) commands using branching

When building a CLI with grouped commands like git remote add and git remote remove, use AddBranch to create command hierarchies. The branch groups related subcommands together and can share common options through settings inheritance.

What We're Building

A git-style CLI with nested remote subcommands—add, remove, and list—all sharing a --verbose flag inherited from the branch:

Command hierarchies demonstration

Create a Command Branch

Use AddBranch<TSettings>("name", ...) to define a parent command with nested subcommands:

var app = new CommandApp();
  
app.Configure(config =>
{
    config.SetApplicationName("myapp");
  
    // Create a "remote" branch with nested commands
    config.AddBranch<RemoteSettings>("remote", remote =>
    {
        remote.SetDescription("Manage remote repositories");
  
        remote.AddCommand<RemoteAddCommand>("add")
            .WithDescription("Add a new remote");
  
        remote.AddCommand<RemoteRemoveCommand>("remove")
            .WithDescription("Remove a remote")
            .WithAlias("rm");
  
        remote.AddCommand<RemoteListCommand>("list")
            .WithDescription("List all remotes")
            .WithAlias("ls");
    });
});
  
return await app.RunAsync(args);

This creates commands like myapp remote add, myapp remote remove, and myapp remote list. Running myapp remote --help shows all subcommands.

Share Options with Settings Inheritance

Define a base settings class for the branch, then inherit from it in subcommand settings:

public class RemoteSettings : CommandSettings
{
    [CommandOption("-v|--verbose")]
    [Description("Show detailed output")]
    public bool Verbose { get; init; }
}
  
public class RemoteAddSettings : RemoteSettings
{
    [CommandArgument(0, "<name>")]
    [Description("Name for the remote")]
    public string Name { get; init; } = string.Empty;
  
    [CommandArgument(1, "<url>")]
    [Description("URL of the remote repository")]
    public string Url { get; init; } = string.Empty;
}

The --verbose flag is now available on all remote subcommands: myapp remote add origin https://... --verbose.

Nest Multiple Levels

For complex CLIs, branches can contain other branches:

config.AddBranch("cloud", cloud =>
{
    cloud.AddBranch("storage", storage =>
    {
        storage.AddCommand<UploadCommand>("upload");
        storage.AddCommand<DownloadCommand>("download");
    });
});

This creates deeply nested commands like myapp cloud storage upload.

See Also