Scal.Interpreting.Commands 1.0.0

Prefix Reserved
dotnet add package Scal.Interpreting.Commands --version 1.0.0
                    
NuGet\Install-Package Scal.Interpreting.Commands -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Scal.Interpreting.Commands" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Scal.Interpreting.Commands" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Scal.Interpreting.Commands" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Scal.Interpreting.Commands --version 1.0.0
                    
#r "nuget: Scal.Interpreting.Commands, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Scal.Interpreting.Commands@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Scal.Interpreting.Commands&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Scal.Interpreting.Commands&version=1.0.0
                    
Install as a Cake Tool

Scal.Interpreting.Commands

A lightweight deterministic command-line interpreter for .NET.

Unlike reflection-heavy or attribute-mandatory CLI frameworks, Scal.Interpreting.Commands prioritizes deterministic resolution and minimal dependencies.

Motivation

Philosophy

  • simple deterministic grammar: verb (noun) (arguments)
  • case-insensitive
  • dash-tolerant but dash-agnostic
  • accept abbreviations but detect collisions
  • bulletproof and predictable behavior

Technical features

  • verb/noun definition by attributes or Pascal-case naming convention
  • strongly-typed command instantiation
  • validation via DataAnnotations
  • TypeConverter support
  • contextual help generation
  • dependency-free
  • DI-agnostic construction
  • .Net 8.0 LTS compatible (console or ASP.NET)

Usage example

dotnet add package Scal.Interpreting.Commands

<details open="true"> <summary> Example of a program accepting as <b>List Image Name=abc</b> command </summary>

[DataContract(Name = "CliArgs")]
[Description("Cli arguments interpreter example")]
public abstract class Program
{
    private static async Task<int> Main(string[] args)
    {
        var interpretation = new CommandLineInterpreter().Interpret<Program>(args);
        if (interpretation.Command is null) {
            interpretation.Feedback(Console.WriteLine);
            return 1;
        }
        interpretation.Feedback(Console.WriteLine, showHelp: false);
        return await interpretation.Command.ExecuteAsync();
    }

    public abstract Task<int> ExecuteAsync();

    [Description("List the images")]
    [DataContract(Namespace = "List", Name = "Image")]
    public class ListImage : Program
    {
        [Description("The image name pattern")]
        [Required]
        [MinLength(1)]
        public string Name { get; set; } = string.Empty;

        [Description("The image type Id")]
        [Range(1, 9)]
        public int TypeId { get; set; } = 1;

        public override Task<int> ExecuteAsync()
        {
            Console.WriteLine("Simulate {0} {1}", nameof(ListImage), this.Name);
            return Task.FromResult(0);
        }
    }
}

</details>

Mention that:

  • the Program itself is an abstract with just an entrypoint and the ExecuteAsync contract
  • it is derived in a ListImage class that is instantiated
  • I choose to output the feedback without help in case of success which shows the program title

Executing it with <b>List Image Name=abc</b> or <b>L I N=abc</b> gives:

CliArgs Cli arguments interpreter example
Simulate ListImage abc

Help

Executing the program without parameter will output this with the accepted abbreviations in parentheses:

CliArgs Cli arguments interpreter example
*** : Usage: verb (noun) (parameters)
  List     Image            List the images (L I)
    TypeId                  The image type Id (T)
    Name                    The image name pattern (N)

Validation using System.ComponentModel.DataAnnotations attributes or your custom attributes

Executing it with <b>List Image Name= Type=10</b> or <b>L I N= T=10</b> gives:

CliArgs Cli arguments interpreter example
*** TypeId: The field TypeId must be between 1 and 9.
*** Name: The Name field is required.
  List     Image            List the images (L I)
    TypeId                  The image type Id (T)
    Name                    The image name pattern (N)

New ListImport command without attribute

<details open="true"> <summary> When adding a new command <b>List Import</b> to the same program: </summary>

    public class ListImportWithoutParameter : Program
    {
        public override Task<int> ExecuteAsync()
        {
            Console.WriteLine("Simulate {0}", nameof(ListImportWithoutParameter));
            return Task.FromResult(0);
        }
    }

</details>

Mention that:

  • The class does not require any attribute and VerbNoun is extracted using the first two words of the class name Pascal-casing
  • Acronyms such as XML or HTTP will be split into individual letters unless explicitly configured using attributes, e.g. a ListXMLFile will be interpreted as List X by convention. In such a case, use a DataContract attribute to clarify your intent.
  • The abbreviations now become l ima for List Image and l imp for List Import as those are the minimum required to prevent ambiguity.

⚠️ Please note that abbreviations should never be used is scripts or documentation for many reasons (clarity, newbie-friendly), including the fact that they may change by adding new commands.

Verb-only command

<details open="true"> <summary> You may a define a verb-only command by creating a one word class like <b>Cleanup</b>, or by specifying a <b>Namespace</b> in a <b>DataContract</b> without <b>Name</b>: </summary>

    public class Cleanup : Program
    {
        public override Task<int> ExecuteAsync()
        {
            Console.WriteLine("Simulate {0}", nameof(Cleanup));
            return Task.FromResult(0);
        }
    }

</details>

Type converter and custom validation

<details open="true"> <summary> Custom type converter and custom validation may be used: </summary>

    public class SomeCommand : Program
    {
        [TypeConverter(typeof(YourTypeConverter))]
        [YourCustomValidation]
        [Required]
        public YourType Reference { get; set; }

        public override Task<int> ExecuteAsync()
        {
            Console.WriteLine("Simulate {0} {1}", nameof(SomeCommand), this.Reference);
            return Task.FromResult(0);
        }
    }

</details>

Factory constructor

You may provide a factory delegate to integrate with your preferred DI framework:

var interpreter = new CommandLineInterpreter(
    factory: type => MyContainer.Resolve(type));

If no factory delegate is provided, Activator is used:

... = Activator.CreateInstance(type);

Examples

To view examples, see the tests models: by convention, by annotation and with type converter.

Thanks

Thanks to Dan (aka ChatGPT) for they advices and making doubts disappear.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 81 2/18/2026

ChangeLog.md