Summary
The data-driven model behind lib_cli.sh (base_cli_model_init → base_cli_command → base_cli_option/base_cli_positional, all keyed into one associative array by model|path strings, lib_cli.sh:16-31) is a genuinely clever way to fake structured schemas in Bash 4.2 without namerefs, and it gets a real feature set (enums, validators, conflicts, aliases, repeatables, a free bash-completion generator at base_cli_completion_script, lib_cli.sh:927-947) well past raw getopts. But building a CLI with it is verbose relative to what it buys.
Details
Every option is a full imperative call with positional args plus key=value attribute strings (lib_cli.sh:411-500); parent commands must be registered before children (lib_cli.sh:352-355); there's no decorator/struct-literal/heredoc equivalent, so a CLI spec ends up as a scattered sequence of ordered function calls rather than one declarative block. It reads more like "argparse reimplemented in Bash" than "click" — the most verbose part of the framework relative to the ergonomics a "world-class" CLI DSL implies.
Impact
This is the framework's primary selling point for building real CLIs, and it's also its least pleasant surface to actually use. That's a meaningful adoption headwind: prospective users comparing this against bashly/argc (which are more ergonomic but are external code generators, not a sourceable runtime library — see the market research in the "5/5" review) will notice the ceremony.
Suggested fix
Keep the existing low-level engine (it's sound), but add a lighter-weight ergonomic layer on top — for example a single table-driven or heredoc-based "quick declare" helper that expands into the existing base_cli_command/base_cli_option calls, so simple CLIs can be declared in one block while the full imperative API remains available for complex cases.
Summary
The data-driven model behind
lib_cli.sh(base_cli_model_init→base_cli_command→base_cli_option/base_cli_positional, all keyed into one associative array bymodel|pathstrings,lib_cli.sh:16-31) is a genuinely clever way to fake structured schemas in Bash 4.2 without namerefs, and it gets a real feature set (enums, validators, conflicts, aliases, repeatables, a free bash-completion generator atbase_cli_completion_script,lib_cli.sh:927-947) well past rawgetopts. But building a CLI with it is verbose relative to what it buys.Details
Every option is a full imperative call with positional args plus
key=valueattribute strings (lib_cli.sh:411-500); parent commands must be registered before children (lib_cli.sh:352-355); there's no decorator/struct-literal/heredoc equivalent, so a CLI spec ends up as a scattered sequence of ordered function calls rather than one declarative block. It reads more like "argparse reimplemented in Bash" than "click" — the most verbose part of the framework relative to the ergonomics a "world-class" CLI DSL implies.Impact
This is the framework's primary selling point for building real CLIs, and it's also its least pleasant surface to actually use. That's a meaningful adoption headwind: prospective users comparing this against bashly/argc (which are more ergonomic but are external code generators, not a sourceable runtime library — see the market research in the "5/5" review) will notice the ceremony.
Suggested fix
Keep the existing low-level engine (it's sound), but add a lighter-weight ergonomic layer on top — for example a single table-driven or heredoc-based "quick declare" helper that expands into the existing
base_cli_command/base_cli_optioncalls, so simple CLIs can be declared in one block while the full imperative API remains available for complex cases.