ConsoleAppFramework icon indicating copy to clipboard operation
ConsoleAppFramework copied to clipboard

Unclear how to add description for root command without replacing top-level help text.

Open willnationsdev opened this issue 3 weeks ago • 1 comments

If I have no "root" command per se, and want to take advantage of the generated Commands: list and the usage instructions for help text, but also want to provide a description between the usage and commands list for the (generated) root command's help text, I don't seem to have a way of doing that.

I've tried adding doc comments to a [Command("")]-attributed method registered with Add<T>(), but that (as you would expect) simply makes the default help text output go away, instead executing whatever logic is implemented in that method - which would work if I had a way of explicitly calling the built-in usage/help information from ConsoleAppContext (or at least, getting the string and manipulating myself), but it's all locked away because the generated ConsoleApp.ConsoleAppBuilder.ShowHelp(int helpId) method is private.

Example:

using ConsoleAppFramework;

var app = ConsoleApp.Create();

app.Add<Commands>();

app.Run(args);

internal class Commands
{
    /// <summary>
    /// Displays customized messages for different scenarios.
    /// </summary>
    /// <param name="context">The app context.</param>
    [Command("")]
    public void Run(ConsoleAppContext context)
    {
        // Problem: What can I do here to inject "Displays customized messages for different scenarios."?
    }

    /// <summary>
    /// Displays a greeting message.
    /// </summary>
    /// <param name="message">-m, The message to show.</param>
    public void Greet(string message) => Console.WriteLine($"Hello, {message}");

    /// <summary>
    /// Displays a farewell message.
    /// </summary>
    /// <param name="message">-m, The message to show.</param>
    public void Bye(string message) => Console.WriteLine($"Goodbye, {message}");
}

Expected output of -h argument list:

Usage: [options...] [-h|--help] [--version]

Displays customized messages for different scenarios.

Commands:
  greet           Displays a greeting message.
  bye              Displays a farewell message.

Perhaps just allow people to make the command method with [Command("")] to optionally be made partial, and if it is, then it continues to produce the default behavior but also takes into account your XML doc comments?

willnationsdev avatar Dec 12 '25 17:12 willnationsdev

Hmm, I'm not quite sure what exactly you're looking for. Since ConsoleAppContext is not a command argument, it is registered as a root command with no arguments. So when you call it without arguments, the root command is executed. This is the only possible behavior because otherwise there would be no way to call the root command.

In the case of "--help", it shows the help for the root command (including Run's Summary, and if there are Options, those are also displayed), and as a special case for the root only, the command list is also shown.

Do you want to hide the command list?

neuecc avatar Dec 14 '25 10:12 neuecc

Oh, I believe I mixed things up a bit in my tests. Sorry about that.

If I have an app called myapp and its Program.cs has the originally posted content, then running myapp -h does do what you expect, rendering the help text and such. The problem is this:

Say I have this logic instead:

using ConsoleAppFramework;

var app = ConsoleApp.Create();

app.Add<Commands>();

app.Run(args);

internal class Commands
{
    /// <summary>
    /// Displays a greeting message.
    /// </summary>
    /// <param name="message">-m, The message to show.</param>
    public void Greet(string message) => Console.WriteLine($"Hello, {message}");

    /// <summary>
    /// Displays a farewell message.
    /// </summary>
    /// <param name="message">-m, The message to show.</param>
    public void Bye(string message) => Console.WriteLine($"Goodbye, {message}");
}

This makes it so that myapp and myapp -h both produce the help text message. This is what I want. However, I also want to add a description to that help text. I can't seem to achieve this without resorting to defining a root command. That is, the only way I can customize the description of the default help text for both executions is by adding a root command with [Command("")] and then adding an XML comment for that method. That leads me to the code sample I quoted in the first post.

However, with that code, myapp now produces my overwritten logic rather than doing what myapp -h does. And, as far as I'm concerned, that is what should be expected. I don't necessarily want to change that, but I also don't have any way of artificially reproducing the original behavior.

I want myapp and myapp -h to both produce help text and for that help text to contain a description that I have manually articulated somewhere. I don't have a way of doing that currently. Whether a solution entails me defining an explicit root command or not is immaterial. I then suggested that this could be achieved a number of different ways:

  1. The framework could provide me with a way of manually displaying the help text, and I simply call that from inside my root command implementation.
    • Currently, these are locked behind inaccessible API methods (e.g. I can't manually call ConsoleAppBuilder.ShowHelp(-1)).
    • To me, this seems like the simplest solution.
  2. The framework could detect if I have defined a root command that is partial and without an implementation. If so, then it would merely generate an implementation that does the same thing that the -h argument output would've executed.
    • This seems like the most complex solution, but would give the most streamlined experience for users wanting to do this (I guess?).
  3. The framework could expose an assembly attribute, class attribute, or ConsoleAppBuilder builder method to let people assign custom text for the description that would be used in both cases.
    • This would let people directly set a variable that you could just inject into the template, and then people wouldn't even need to define a root command at all if all they want is to keep the default behavior (like me).

Does that make sense?

willnationsdev avatar Dec 18 '25 19:12 willnationsdev

@willnationsdev If I understand correctly, you are okay with having to specify a global app description by implementing an empty root command, but your issue is that with no arguments it assumes execution, thereby not showing help text? please correct me if I am wrong.

Your suggestion is having to implement a call to ShowHelp manually from your root command?

I believe you can achieve the same thing by putting a

if (args.Length == 0) args = ["-h"];

Before the call to ConsoleApp.Run/RunAsync

dusrdev avatar Dec 21 '25 08:12 dusrdev

Ah! That could work, and is a much simpler solution. Will try that. Thanks!

willnationsdev avatar Dec 21 '25 15:12 willnationsdev