Skip to content

Conversation

shayanhabibi
Copy link
Contributor

@shayanhabibi shayanhabibi commented Aug 29, 2025

This pull uses the community & microsoft driven library System.CommandLine which is currently in v2-beta5.

When compared to Spectre.Console.Cli, this means no requirements for interfaces/classes and attributes to derive the CLI interface.

We miss out on aesthetics. But I am fine to be patient with this, as it's simply a matter of the library becoming polished enough to allow more hooks and customisation with the help message rendering.

We can still use Spectre.Console to render everything outside the help messages.

Completions?

Using dotnet-suggest, System.CommandLine supports completions for zsh, bash and powershell.

Progress

After tinkering and testing some elements of the package, I've implemented a skeleton structure to show how the CLI interface could be logically reasoned and derived.

I have again implemented the CLI options as a interface hierarchy, as this simplifies consuming the args imo.

Nothing has actually been hooked in yet, as some validation has to be added (there should be architecture already existing that supports multiple validation error message reporting like I did with Spectre.Console.Cli).

It's important before this progresses to know whether this API for creating the CLI is acceptable.

Some example option creation and manipulation

    let projPath =
        Argument.create<string voption> "PATH"
        |> Mutate.Argument.defaultValue (System.Environment.CurrentDirectory |> ValueSome)
        |> Mutate.description $"{dim}Path containing project files{dimOff}"
        |> Utils.addArgToCommand root
    // extension argument for clean only
    let extensionArg =
        Argument.create<string voption> "EXT"
        |> Mutate.Argument.defaultValue (".fs.js" |> ValueSome)
        |> Mutate.description $"{dim}Path extension for cleaning{dimOff}"
        |> Utils.addArgToCommand cleanCommand

    let workingDirectory =
        CommandOption.create<string> "--cwd"
        |> Mutate.description $"{dim}Set the working directory{dimOff}"
        |> Mutate.CommandOption.filePathsOnly
        |> Mutate.CommandOption.defaultValue System.Environment.CurrentDirectory
        |> Utils.addToCommands rootAndCommands

    let verbosity =
        CommandOption.create<Verbosity> "--verbosity"
        |> Mutate.description $"{dim}Set the logging volume{dimOff}"
        |> Mutate.CommandOption.addAlias "-v"
        |> Mutate.CommandOption.valueOneOfStrings Utils.Unions.getAllCaseStringOrInitials<Verbosity>
        |> Utils.Unions.addCustomParser (
            function
            | Verbosity.Normal -> [ "n"; "normal" ]
            | Verbosity.Silent -> [ "s"; "silent" ]
            | Verbosity.Verbose -> [ "v"; "verbose" ]
        )
        |> Mutate.CommandOption.defaultValue Verbosity.Normal
        |> Mutate.CommandOption.helpName $"{dim}n|normal|s|silent|v|verbose{dimOff}"
        |> Utils.addToCommands rootAndCommands

Images

As a proof of concept and test, I used ANSI escape sequences to color what is readily available dim. All descriptions can be safely manipulated.

[!NOTE]
There is a way to color everything, except the headers, but it requires a hack.

The method would be to render a option which uses escape sequences, and then the proper option which is hidden.
This is because it is impossible to match the options if they have escape sequences.

image image

@shayanhabibi shayanhabibi mentioned this pull request Aug 29, 2025
@MangelMaxime
Copy link
Member

@shayanhabibi Would using FSharp.SystemCommandLine benefit us?

@shayanhabibi
Copy link
Contributor Author

Personally, I would wait until the dependency itself is stable before we have a dependency on the wrapper

@shayanhabibi
Copy link
Contributor Author

Need to know whether we should continue with this or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants