SkillAgentSearch skills...

CliFx

Class-first framework for building command-line interfaces

Install / Use

/learn @Tyrrrz/CliFx
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

CliFx

Status Made in Ukraine Build Coverage Version Downloads Discord Fuck Russia

<table> <tr> <td width="99999" align="center">Development of this project is entirely funded by the community. <b><a href="https://tyrrrz.me/donate">Consider donating to support!</a></b></td> </tr> </table> <p align="center"> <img src="favicon.png" alt="Icon" /> </p>

CliFx is a simple to use, yet powerful framework for building command-line applications. Its primary goal is to completely take over the user input layer, allowing you to forget about infrastructural concerns and instead focus on writing your application.

Terms of use<sup>[?]</sup>

By using this project or its source code, for any purpose and in any shape or form, you grant your implicit agreement to all the following statements:

  • You condemn Russia and its military aggression against Ukraine
  • You recognize that Russia is an occupant that unlawfully invaded a sovereign state
  • You support Ukraine's territorial integrity, including its claims over temporarily occupied territories of Crimea and Donbas
  • You reject false narratives perpetuated by Russian state propaganda

To learn more about the war and how you can help, click here. Glory to Ukraine! 🇺🇦

Install

  • 📦 NuGet: dotnet add package CliFx

Features

  • Complete application framework, not just an argument parser
  • Minimum boilerplate and easy to get started
  • Class-first configuration via attributes
  • Comprehensive auto-generated help text
  • Support for deeply nested command hierarchies
  • Graceful cancellation via interrupt signals
  • Support for reading and writing binary data
  • Testable console interaction layer
  • Built-in analyzers to catch configuration issues
  • Targets .NET Standard 2.0+
  • No external dependencies

Screenshots

help screen

Usage

Quick overview

To turn your program into a command-line interface, modify the Main() method so that it delegates the execution to an instance of CliApplication. You can use CliApplicationBuilder to simplify the process of creating and configuring an application:

using CliFx;

public static class Program
{
    public static async Task<int> Main() =>
        await new CliApplicationBuilder()
            .AddCommandsFromThisAssembly()
            .Build()
            .RunAsync();
}

[!WARNING] Ensure that your Main() method returns the integer exit code provided by CliApplication.RunAsync(), as shown in the above example. Exit code is used to communicate execution result to the parent process, so it's important that your program propagates it.

[!NOTE] When calling CliApplication.RunAsync(), CliFx resolves command-line arguments and environment variables from Environment.GetCommandLineArgs() and Environment.GetEnvironmentVariables() respectively. You can also provide them manually using one of the alternative overloads.

The code above uses AddCommandsFromThisAssembly() to detect command types defined within the current project and register them on the application. Commands are independent entry points, through which the user can interact with your program.

To define a command, create a class that implements the ICommand interface and annotate it with the [Command] attribute:

using CliFx;
using CliFx.Attributes;

[Command(Description = "Calculates the logarithm of a value.")]
public class LogCommand : ICommand
{
    // Order: 0
    [CommandParameter(0, Description = "Value whose logarithm is to be found.")]
    public required double Value { get; init; }

    // Name: --base
    // Short name: -b
    [CommandOption("base", 'b', Description = "Logarithm base.")]
    public double Base { get; init; } = 10;

    public ValueTask ExecuteAsync(IConsole console)
    {
        var result = Math.Log(Value, Base);
        console.Output.WriteLine(result);

        // If the execution is not meant to be asynchronous,
        // return an empty task at the end of the method.
        return default;
    }
}

In order to implement ICommand, the class needs to define an ExecuteAsync(...) method. This is the method that gets called by the framework when the user decides to execute the command.

As the only parameter, this method takes an instance of IConsole, which is an abstraction around the system console. Use this abstraction in place of System.Console whenever you need to write output, read input, or otherwise interact with the console.

In most cases, you will also want to define input bindings, which are properties annotated by the [CommandParameter] and [CommandOption] attributes. These bindings provide a way to map command-line arguments into structured input data that can be used by the command.

The command in the above example serves as a simple logarithm calculator and defines two inputs: a positional parameter for the input value and a named option for the logarithm base. In order to execute this command, at minimum, the user needs to provide the input value:

$ dotnet myapp.dll 10000

4

They can also pass the -b|--base option to override the default logarithm base of 10:

$ dotnet myapp.dll 729 -b 3

6

In case the user forgets to specify the required value parameter, the application will instead exit with an error:

$ dotnet myapp.dll -b 10

Missing required parameter(s):
<value>

Out of the box, CliFx also provides a built-in --help option, which generates a help screen that lists all parameters and options available for the command:

$ dotnet myapp.dll --help

MyApp v1.0

USAGE
  dotnet myapp.dll <value> [options]

DESCRIPTION
  Calculates the logarithm of a value.

PARAMETERS
* value             Value whose logarithm is to be found.

OPTIONS
  -b|--base         Logarithm base. Default: "10".
  -h|--help         Shows help text.
  --version         Shows version information.

Argument syntax

This library employs a variation of the POSIX argument syntax, which is used in most modern command-line tools. Here are some examples of how it works:

  • myapp --foo bar sets option "foo" to value "bar"
  • myapp -f bar sets option 'f' to value "bar"
  • myapp --switch sets option "switch" without value
  • myapp -s sets option 's' without value
  • myapp -abc sets options 'a', 'b' and 'c' without value
  • myapp -xqf bar sets options 'x' and 'q' without value, and option 'f' to value "bar"
  • myapp -i file1.txt file2.txt sets option 'i' to a sequence of values "file1.txt" and "file2.txt"
  • myapp -i file1.txt -i file2.txt sets option 'i' to a sequence of values "file1.txt" and "file2.txt"
  • myapp cmd abc -o routes to command cmd (assuming it's a command) with parameter abc and sets option 'o' without value

Additionally, argument parsing in CliFx aims to be as deterministic as possible, ideally yielding the same result regardless of the application configuration. In fact, the only context-sensitive part in the parser is the command name resolution, which needs to know the list of available commands in order to discern them from parameters.

The parser's context-free nature has several implications on how it consumes arguments. For example, myapp -i file1.txt file2.txt will always be parsed as an option with multiple values, regardless of the arity of the underlying property it's bound to. Similarly, unseparated arguments in the form of myapp -ofile will be treated as five distinct options 'o', 'f', 'i', 'l', 'e', instead of 'o' being set to value "file".

These rules also make the order of arguments important — command-line string is expected to follow this pattern:

$ myapp [...directives] [command] [...parameters] [...options]

Parameters and options

CliFx supports two types of argument bindings: parameters and options. Parameters are bound from the command-line arguments based on the order they appear in, while options are bound by their name.

Besides that, they also differ in the following ways:

  • Parameters are required by default, while options are not.

    • You can make an option required by adding the required keyword to the property declaration (introduced in C# 11):

      // Any option can be required or optional without restrictions
      [CommandOption("foo")]
      public required string RequiredOption { get; init; }
      
    • To make a parameter optional, omit the required keyword, but only the last parameter (by order) can be configured in such way:

      // Only the last parameter can be optional
      [CommandParameter(0)]
      public string? OptionalParameter { get; init; }
      
  • Parameters are primarily used for scalar (non-enumerable) properties, while options can be used for both scalar and non-scalar properties.

    • You can bind an option to a property of a non-scala

Related Skills

View on GitHub
GitHub Stars1.6k
CategoryDevelopment
Updated6h ago
Forks65

Languages

C#

Security Score

100/100

Audited on Mar 25, 2026

No findings