
A powerful and flexible command-line parser and command executor framework for .NET applications. Build beautiful CLI tools with minimal boilerplate code.
- 🎯 Type-safe argument binding - Automatically bind command-line arguments to strongly-typed classes
- 🚩 Long and short flags - Support for both
--verboseand-vstyle flags - 📝 Automatic help generation - Beautiful, table-formatted help text generated from attributes
- ⚡ Process command execution - Easily wrap external CLI tools
- 🎨 Attribute-based configuration - Use simple attributes to configure commands and arguments
- ✅ Validation - Automatic validation for required arguments
- 🔄 Flexible architecture - Extend
Command<TArgs>orProcessCommandbase classes
dotnet add package PromtyusingPromty;[Description("greet","Greets a person by name")]publicclassGreetCommand:Command<GreetCommand.Args>{publicclassArgs{[Description("name","The name of the person to greet")]publicstringName{get;set;}=string.Empty;[FlagAlias("uppercase",'u')][Description("Print the greeting in uppercase")]publicboolUppercase{get;set;}[FlagAlias("repeat",'r')][Description("Number of times to repeat the greeting")]publicint?Repeat{get;set;}}publicoverrideTask<int>ExecuteAsync(Argsargs){vargreeting=$"Hello, {args.Name}!";if(args.Uppercase){greeting=greeting.ToUpper();}varrepeat=args.Repeat??1;for(inti=0;i<repeat;i++){Console.WriteLine(greeting);}returnTask.FromResult(0);}}usingSystem.Reflection;usingPromty;varexecutor=newCommandExecutor();executor.RegisterCommandsFromAssembly(Assembly.GetExecutingAssembly());returnawaitexecutor.ExecuteAsync(args);# Show available commands
dotnet run
# Run with arguments
dotnet run -- greet Alice --uppercase -r 3
# Output:# HELLO, ALICE!# HELLO, ALICE!# HELLO, ALICE!Standard commands use typed argument binding with automatic parsing and validation.
[Description("copy","Copies a file from source to destination")]publicclassCopyCommand:Command<CopyCommand.Args>{publicclassArgs{[Description("source","The source file path")]publicstringSource{get;set;}=string.Empty;[Description("destination","The destination file path")]publicstringDestination{get;set;}=string.Empty;[FlagAlias("verbose",'v')][Description("Show detailed output")]publicboolVerbose{get;set;}[FlagAlias("overwrite",'o')][Description("Overwrite existing files")]publicboolOverwrite{get;set;}}publicoverrideTask<int>ExecuteAsync(Argsargs){// Implementation hereFile.Copy(args.Source,args.Destination,args.Overwrite);returnTask.FromResult(0);}}Process commands forward all arguments to an external executable. Perfect for wrapping existing CLI tools.
[Description("git","Execute git commands")]publicclassGitCommand:ProcessCommand{protectedoverridestringExecutablePath=>"git";}Usage:
dotnet run -- git status
dotnet run -- git commit -m "Initial commit"
dotnet run -- git --versionUse [Description] for both commands and arguments:
For Commands:
[Description("command-name","Command description")]publicclassMyCommand:Command<MyCommand.Args>For Positional Arguments:
[Description("arg-name","Argument description")]publicstringMyArgument{get;set;}For Flag Arguments:
[FlagAlias("verbose",'v')][Description("Show detailed output")]publicboolVerbose{get;set;}[FlagAlias(long, short)]- Defines flag aliases[FlagAlias("verbose", 'v')]- Both long and short[FlagAlias("verbose")]- Long only[FlagAlias('v')]- Short only
Promty supports automatic type conversion for:
stringint,long,doublebool- Nullable versions:
int?,bool?, etc. [Flags]enums (see below)
- Positional arguments (without
[FlagAlias]and not[Flags]enums) are required and must come before flags - Flag arguments (with
[FlagAlias]) are optional [Flags]enum properties are automatically treated as optional flags- Boolean flags don't require values:
--verboseis equivalent to--verbose true
Instead of defining multiple boolean properties, you can use a [Flags] enum to group related flags together. Each enum value becomes an individual command-line flag that can be combined.
[Description("build","Build a project with options")]publicclassBuildCommand:Command<BuildCommand.Args>{[Flags]publicenumBuildOptions{None=0,[FlagAlias("verbose",'v')][Description("Enable verbose output")]Verbose=1,[FlagAlias("debug",'d')][Description("Build in debug mode")]Debug=2,[Description("Disable build cache")]NoCache=4,[Description("Skip running tests")]SkipTests=8}publicclassArgs{[Description("project","Project name")]publicstringProject{get;set;}=string.Empty;// No [FlagAlias] needed on the property!publicBuildOptionsOptions{get;set;}}publicoverrideTask<int>ExecuteAsync(Argsargs){Console.WriteLine($"Building {args.Project}");if(args.Options.HasFlag(BuildOptions.Verbose))Console.WriteLine("Verbose mode enabled");if(args.Options.HasFlag(BuildOptions.Debug))Console.WriteLine("Debug mode enabled");returnTask.FromResult(0);}}Usage:
# Combine multiple flags
dotnet run -- build MyProject --verbose --debug --skip-tests
# Use short aliases
dotnet run -- build MyProject -v -d
# Mix aliases with kebab-case names
dotnet run -- build MyProject -v --no-cacheFlags Enum Features:
- Each enum field becomes an individual flag in the help text
- Use
[FlagAlias]on enum fields for custom long/short aliases - Enum fields without
[FlagAlias]auto-convert to kebab-case (e.g.,NoCache→--no-cache) - Use
[Description]on enum fields to provide help text - The
None = 0value is automatically excluded from help output - Multiple flags can be combined and are stored as a bitwise combination
Promty automatically generates beautiful help text:
Usage: greet <name> [options]
Greets a person by name
Arguments:
<name> The name of the person to greet
Options:
-u, --uppercase Print the greeting in uppercase
-r, --repeat <repeat> Number of times to repeat the greeting
For commands with [Flags] enums, each flag is displayed individually:
Usage: build <project> [options]
Build a project with options
Arguments:
<project> Project name
Options:
-v, --verbose Enable verbose output
-d, --debug Build in debug mode
--no-cache Disable build cache
--skip-tests Skip running tests
Command list is formatted as a table:
Available commands:
build Build a project with options
copy Copies a file from source to destination
git Execute git commands
greet Greets a person by name
Return appropriate exit codes from your commands:
publicoverrideTask<int>ExecuteAsync(Argsargs){if(!File.Exists(args.Source)){Console.WriteLine($"Error: Source file '{args.Source}' not found");returnTask.FromResult(1);// Error exit code}// SuccessreturnTask.FromResult(0);}Register commands from multiple assemblies:
usingPromty;varexecutor=newCommandExecutor();executor.RegisterCommandsFromAssembly(Assembly.GetExecutingAssembly());executor.RegisterCommandsFromAssembly(typeof(PluginCommand).Assembly);returnawaitexecutor.ExecuteAsync(args);Implement validation in your command:
publicoverrideTask<int>ExecuteAsync(Argsargs){if(args.Port<1||args.Port>65535){Console.WriteLine("Error: Port must be between 1 and 65535");returnTask.FromResult(1);}// Continue with valid arguments}Check out the example commands in the repository:
- GreetCommand - Demonstrates typed arguments and flags
- CopyCommand - Shows file operations with validation
- GitCommand - Example of wrapping an external CLI tool
- DotNetCommand - Another process command example
MIT License - see LICENSE file for details
Contributions are welcome! Please feel free to submit a Pull Request.