When help (or any sort of documentation) is explicitly requested, it should go to stdout. It's the legitimate output of the program.
When help is shown because of an invocation (command-line) error, it should go to stderr, because it isn't the legitimate output of the program and shouldn't be interpreted as such.
Currently:
[x] multicommand --help is correct, it displays on stdout
[ ] command --help doesn't, it throws a parsing error
[x] command error works, it throws a parsing error and displays on stderr
The sw design question is, I think, how best for the flag parser to signal exceptional behavior. Current cases:
Ok: Parsing error: throws an exception with a fair message, but leaves exit code to the exception handler.
Passable: Unknown error: throws an exception and a bad message, and leaves exit code to the exception handler.
Clean abort?
Show help and clean abort?
We could add an extra return param: {flags, args, exitCode?: number / success: boolean}.
We could pass back smarter exceptions (that have status code and can introduce exit or other behavior (like show help and exit).
Exceptions are another, non-sequential channel for handling the semipredicate problem. Go hates them; C# finds them revolutionary. Rust, Go, and Zig go back to multiple returns. JavaScript uses them, with some haters. Let's not make this a language debate.
What sort of exceptional / extra behavior can I imagine?
finish parsing and exit cleanly without running the program
show an error message, and maybe help, and exit dirty
have some meta effect on the program that can't be handle as a flag-validation step: alter i/o, any of the things fancier cli libs use events for. Can't think of any useful things at the moment.
Proposal:
ATM, I think the cleanest design is:
Errors can return exit codes, maybe
centralize exit codes
Parser can return an exit code. Undefined means don't exit. Any other, even 0, means exit the program with that code. Could use a magic number for CONTINUE_AS_NORMAL PROCEED, since I can control the set, but might be a good use of undefined.
I think the best way, honoring the https://en.wikipedia.org/wiki/Semipredicate_problem, is like this:
stdout
. It's the legitimate output of the program.stderr
, because it isn't the legitimate output of the program and shouldn't be interpreted as such.Currently:
The sw design question is, I think, how best for the flag parser to signal exceptional behavior. Current cases:
We could add an extra return param: {flags, args, exitCode?: number / success: boolean}.
We could pass back smarter exceptions (that have status code and can introduce exit or other behavior (like show help and exit).
Exceptions are another, non-sequential channel for handling the semipredicate problem. Go hates them; C# finds them revolutionary. Rust, Go, and Zig go back to multiple returns. JavaScript uses them, with some haters. Let's not make this a language debate.
What sort of exceptional / extra behavior can I imagine?
Proposal:
ATM, I think the cleanest design is: