Skip to content

generateHelp=true broken when command has no @Option fields #554

Description

@maxandersen

Summary

generateHelp = true on @CommandDefinition only works when the command also declares at least one @Option field. Without any @Option, --help and -h are not recognized by the parser — even though the generated help text lists them as valid options.

Reproducer

///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.aesh:aesh:3.16.5

import org.aesh.AeshRuntimeRunner;
import org.aesh.command.Command;
import org.aesh.command.CommandDefinition;
import org.aesh.command.CommandResult;
import org.aesh.command.invocation.CommandInvocation;
import org.aesh.command.option.Argument;
import org.aesh.command.option.Option;

class aeshhelpbug {

    // Subcommand WITHOUT any @Option — generateHelp is broken
    @CommandDefinition(name = "greet", description = "Greet (no options)", generateHelp = true)
    public static class GreetCmd implements Command<CommandInvocation> {
        @Argument(description = "Name")
        private String name;

        public CommandResult execute(CommandInvocation ci) {
            ci.println("hello " + name);
            return CommandResult.SUCCESS;
        }
    }

    // Subcommand WITH an @Option — generateHelp works
    @CommandDefinition(name = "greet2", description = "Greet (has option)", generateHelp = true)
    public static class Greet2Cmd implements Command<CommandInvocation> {
        @Option(shortName = 'l', name = "loud", hasValue = false, description = "Shout")
        boolean loud;

        @Argument(description = "Name")
        private String name;

        public CommandResult execute(CommandInvocation ci) {
            var msg = "hello " + name;
            ci.println(loud ? msg.toUpperCase() : msg);
            return CommandResult.SUCCESS;
        }
    }

    @CommandDefinition(name = "app", description = "Test app", generateHelp = true,
        groupCommands = {GreetCmd.class, Greet2Cmd.class})
    public static class AppCmd implements Command<CommandInvocation> {
        @Option(shortName = 'V', name = "version", hasValue = false, description = "Show version")
        boolean version;

        public CommandResult execute(CommandInvocation ci) {
            if (version) ci.println("1.0");
            else ci.println("Usage: app <greet|greet2>");
            return CommandResult.SUCCESS;
        }
    }

    public static void main(String[] args) {
        AeshRuntimeRunner.builder()
            .command(AppCmd.class)
            .args(args)
            .execute();
    }
}

Results

Command Has @Option? --help -h
app --help ✅ yes ✅ shows help ✅ shows help
app greet --help ❌ no ❌ "The option --help is unknown." ❌ silently ignored, command executes
app greet2 --help ✅ yes ✅ shows help ✅ shows help
app greet2 -h ✅ yes ✅ shows help ✅ shows help

Expected

All four commands should show help, since generateHelp = true is set on every @CommandDefinition.

Analysis

The generated help option (-h/--help) is listed in the help text output but is never registered in the parser when the command has no other @Option fields. The parser then treats --help as an unknown option (throwing CommandLineParserException) and -h as an argument value.

Environment

  • aesh 3.16.5 (also reproduced on 3.14.3)
  • Java 25
  • macOS aarch64

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions