5.0 KiB
CLI Conventions
This document describes the conventions for implementing command-line interfaces (CLI) in PowerToys modules.
Library
Use the System.CommandLine library for CLI argument parsing. This is already defined in Directory.Packages.props:
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
Add the reference to your project:
<PackageReference Include="System.CommandLine" />
Option Naming
Aliases Pattern
Define option aliases as static readonly arrays following this pattern:
private static readonly string[] AliasesSilent = ["--silent", "-s"];
private static readonly string[] AliasesWidth = ["--width", "-w"];
private static readonly string[] AliasesHelp = ["--help"];
When creating dedicated option types (for example, sealed class FooOption : Option<T>),
avoid naming a member Aliases (it hides Option.Aliases). Prefer _aliases.
Naming Rules
- Long form: Use
--kebab-case(e.g.,--shrink-only,--keep-date-modified) - Short form: Use single
-xcharacter (e.g.,-s,-w,-h) - No short form for less common options (e.g.,
--shrink-only,--ignore-orientation)
Option Definition
Create options using Option<T> with descriptive help text:
var silentOption = new Option<bool>(AliasesSilent, "Run in silent mode without UI");
var widthOption = new Option<double?>(AliasesWidth, "Set width in pixels");
var unitOption = new Option<ResizeUnit?>(AliasesUnit, "Set unit (Pixel, Percent, Inch, Centimeter)");
Validation
Add validators for options that require range or format checking:
qualityOption.AddValidator(result =>
{
var value = result.GetValueOrDefault<int?>();
if (value.HasValue && (value.Value < 1 || value.Value > 100))
{
result.ErrorMessage = "JPEG quality must be between 1 and 100.";
}
});
RootCommand Setup
Create a RootCommand with a description and add all options:
public static RootCommand CreateRootCommand()
{
var rootCommand = new RootCommand("PowerToys Module Name - Brief description")
{
silentOption,
widthOption,
heightOption,
// ... other options
filesArgument,
};
return rootCommand;
}
Parsing
Parse arguments and extract values:
public static CliOptions Parse(string[] args)
{
var options = new CliOptions();
var rootCommand = CreateRootCommand();
// Note: with the pinned System.CommandLine version in this repo,
// RootCommand.Parse(args) may not be available. Use Parser instead.
var parseResult = new Parser(rootCommand).Parse(args);
// Extract values
options.Silent = parseResult.GetValueForOption(silentOption);
options.Width = parseResult.GetValueForOption(widthOption);
return options;
}
Parse/Validation Errors
If parsing or validation fails, return a non-zero exit code (and typically print the errors plus usage):
if (parseResult.Errors.Count > 0)
{
foreach (var error in parseResult.Errors)
{
Console.Error.WriteLine(error.Message);
}
PrintUsage();
return 1;
}
Examples
Awake Module
Reference implementation: src/modules/Awake/Awake/Program.cs
private static readonly string[] _aliasesConfigOption = ["--use-pt-config", "-c"];
private static readonly string[] _aliasesDisplayOption = ["--display-on", "-d"];
private static readonly string[] _aliasesTimeOption = ["--time-limit", "-t"];
private static readonly string[] _aliasesPidOption = ["--pid", "-p"];
private static readonly string[] _aliasesExpireAtOption = ["--expire-at", "-e"];
ImageResizer Module
Reference implementation:
src/modules/imageresizer/ui/Cli/Commands/ImageResizerRootCommand.cssrc/modules/imageresizer/ui/Models/CliOptions.cssrc/modules/imageresizer/ui/Cli/ImageResizerCliExecutor.cs
public sealed class DestinationOption : Option<string>
{
private static readonly string[] _aliases = ["--destination", "-d"];
public DestinationOption()
: base(_aliases, "Set destination directory")
{
}
}
Help Output
Provide a PrintUsage() method for custom help formatting if needed:
public static void PrintUsage()
{
Console.WriteLine("ModuleName - PowerToys Module CLI");
Console.WriteLine();
Console.WriteLine("Usage: PowerToys.ModuleName.exe [options] [arguments...]");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine(" --option, -o <value> Description of the option");
// ...
}
Best Practices
- Consistency: Follow existing patterns in the codebase (Awake, ImageResizer)
- Documentation: Always provide help text for each option
- Validation: Validate input values and provide clear error messages
- Nullable types: Use
Option<T?>for optional parameters - Boolean flags: Use
Option<bool>for flags that don't require values - Enum support: System.CommandLine automatically handles enum parsing