Documentation GitHub CI Version Licence Score Downloads
A simple, intuitive library for building CLI applications in D.
- Nested subcommands — Supports hierarchical command structures, allowing commands to contain other commands.
- Flexible arguments — Required (
<>) and optional ([]) arguments with variadic support (...). - Rich option handling — Supports
--option=value,--option value, and grouped short flags such as-abc(equivalent to-a -b -c). - Automatic help generation — Built-in help and usage text generation.
- Array collection — Repeated options are automatically collected as arrays.
- Type-safe parsing — Clean API for accessing parsed arguments and options.
Here’s a minimal example that splits a string:
import std.stdio; import std.string; import cmd.program; void main(string[] argv) { auto args = new Program("split") .description("Split a string") .versionString("1.0.0") .versionOption("--version", "Show version information") .helpOption("-h, --help", "Show help for command") .argument("<string>", "String to split") .option("-s, --separator <char>", "Separator character") .option("--first", "Return only the first element") .parse(argv); auto parts = args.argument("string").split(args.option("separator")); if (args.hasFlag("first")) writeln(parts[0]); else writeln(parts); }
$ ./split "hello,world,foo" --separator "," ["hello", "world", "foo"] $ ./split "hello,world,foo" --separator "," --first hello
Note: versionOption() enables a version flag for your program (e.g., --version), and helpOption() enables a help flag (e.g., -h or --help). When specified, these flags are handled automatically: the library will print the version or help message and exit.
Use option() to add named parameters to your commands:
ParsedArgs args = new Program("example") // Required option - user must provide a value .option("-t, --target <name>", "Target name") // Optional with default value .option("-p, --port [number]", "Port number", "8080") // Optional without default .option("-c, --config [file]", "Configuration file") // Boolean flag - without a parameter .option("--verbose", "Enable verbose output") .option("-q, --quiet", "Suppress output") .parse(argv);
Please see the API reference page for ParsedArgs.
// Get option value (first occurrence) const string target = args.option("target"); const string port = args.option("port"); // Get all values for repeated options // const string[] targets = args.optionList("target"); // Check if optional option is present if (args.hasOption("--config")) { const string config = args.option("--config"); // ... } // Check boolean flags if (args.hasFlag("verbose")) writeln("Verbose mode enabled"); const bool quiet = args.hasFlag("-q");
You can access options using:
option("target")- access by long name (explicit for long names)option("t")- access by short name (implicit, could be ambiguous)option("-t")- access by short name (explicit)option("--target")- access by long name (explicit, stylistic)
(also applies for hasOption(), hasFlag(), etc.)
Use argument() to add positional arguments that users provide in order:
new Program("file-processor") .argument("<input>", "Input file path") // Required .argument("[output]", "Output file path") // Optional
Variadic (rest) arguments can be added as the last argument in your command to accept multiple values. Optional variadic [files...] accepts ≥ 0 values, required variadic <files...> needs ≥ 1.
new Program("search") // ... .argument("<files...>", "Files to search")
const string inputFile = args.argument("input"); // Check if optional argument was provided if (args.hasArgument("output")) { const string outputFile = args.argument("output"); } // Get all values of variadic const string[] files = args.argumentList("files");
Note: Required arguments after optional ones are technically allowed, but behave counter-intuitively and ambiguously for the user. It is strongly recommended not to use optional positional arguments before required ones—consider re-ordering the arguments, or using named parameters (options).
Build complex CLI applications with nested subcommands using command(). Here’s an example with basic subcommands.
import std.stdio; import std.string; import cmd.program; import cmd.command; void main(string[] args) { new Program("subcommands") .description("A CLI application with subcommands") .versionString("1.0.0") .helpOption("-h, --help", "Show help for command") .command(new Command("split") .description("Split a string") .argument("<string>", "String to split") .option("-s, --separator <char>", "Separator character") .action((args) { auto parts = args.argument("string").split(args.option("separator")); writeln(parts); return 0; }) ) .command(new Command("greet") .description("Greet someone") .argument("<name>", "Name of the person to greet") .option("--excited", "Add excitement to the greeting") .action((args) { auto msg = "Hello, " ~ args.argument("name"); if (args.hasFlag("excited")) msg ~= "!!!"; writeln(msg); return 0; }) ) .run(args); }
$ ./subcommands split "hello,world" --separator "," $ ./subcommands greet Alice --excited $ ./subcommands split --help # Shows help for the split subcommand
Important: A command cannot have both subcommands and arguments. Choose one pattern per command level.
Commands can be nested endlessly.
Create modular command implementations by extending Command (or Program):
module modular.commands.greet; import std.stdio; import cmd.command; import cmd.parsed_args; class GreetCommand : Command { public this() { super("greet") .description("Greet someone") .argument("<name>", "Name of the person to greet") .option("--excited", "Add excitement to the greeting") .action(&execute); } private int execute(ParsedArgs args) { auto msg = "Hello, " ~ args.argument("name"); if (args.hasFlag("excited")) msg ~= "!!!"; writeln(msg); return 0; } }
// ... import modular.commands.greet; import cmd.program; void main(string[] args) { new Program("modular") .command(new GreetCommand()) .run(args); }
Enable help as a subcommand using HelpCommand:
// ... import cmd.help_command; new Program("myapp") .description("My CLI application") .command(new Command("deploy").description("Deploy the application")) .command(new Command("status").description("Check application status")) .command(new HelpCommand()) // Adds the built-in ‘help’ subcommand .run(args);
$ myapp help # Show help for program $ myapp help deploy # Show help for deploy command
The library provides clear error messages for common mistakes during args parsing and exits with status code 2:
error: unknown command 'unknown-command'
error: missing value for option 'target'
error: unknown option 'invalid-option'
error: unexpected argument 'extra'
error: missing required option '-t, --target <name>'
error: missing required argument 'input'
You can also trigger custom errors in your command actions using the error(string) and noreturn error(string, int) methods on Command:
.action((args) {
if (someCondition) {
args.command.error("error message"); // Does not exit (only prints error)
// or
args.command.error("error message", 1); // Exits with status
}
return 0;
});The error methods can be overridden to customise the error format.
Copyright © 2025 Zefir Kirilov.
This project is licenced under the GPL-3.0 licence.