Branch: https://github.com/stalep/jbang/tree/feature/aesh-migration
Replaces picocli with Aesh (Advanced Extensible SHell) as the CLI parsing framework. This is a 2-commit, ~62-file, net -605 lines change across jbang's entire CLI layer.
| Aspect | Picocli (current) | Aesh (branch) |
|---|---|---|
| Annotations | @Command, @Option, @Parameters |
@CommandDefinition, @GroupCommandDefinition, @Option, @Argument, @Arguments |
| Subcommand grouping | Separate top-level classes (e.g. AliasAdd, ExportLocal) |
Inner static classes (e.g. Alias.AliasAdd, Export.ExportLocal) |
| Command base | Callable<Integer> |
Command<CommandInvocation> + CommandLifecycle |
| Entry point | CommandLine.execute() orchestrates everything |
AeshRuntimeRunner.builder().command(JBang.class).args(...).execute() |
| Option features | negatable, arity, fallbackValue, scope=INHERIT, ArgGroup |
hasValue=false + manual negation pairs (--cds/--no-cds), inherited=true, custom OptionParser |
| Mixins | @Mixin with @Spec injection |
@Mixin but no spec injection; fields read directly |
| Default values | IDefaultValueProvider with config lookup |
JBangDefaultValueProvider (simpler, same idea) |
| Help | Rich picocli help with grouped sections, custom CommandGroupRenderer |
generateHelp = true (basic auto-generated help) |
| Completion | Full bash/fish auto-generation via picocli.AutoComplete |
Stubbed out — prints "not yet available" |
| Error handling | IExecutionExceptionHandler, IExitCodeExceptionMapper |
Manual try/catch in Main.main() and JBang.execute() |
- Simpler command hierarchy — Inner static classes for subcommands is cleaner than scattered top-level classes
- Fewer framework abstractions — No
CommandSpec,ParseResult,PrintWriterplumbing; more direct code - Net code reduction — ~600 fewer lines despite some added boilerplate
- Lifecycle hooks —
beforeParse()/afterParse()onCommandLifecycleis a clean separation vs. picocli's setter-driven approach - Deleted complexity — Removes
DeprecatedMessageHandler,KeyValueConsumer,FormatMixin,HelpMixin,ExportMixin,VersionProvider,CommaSeparatedConverter,TemplatePropertyConverter— all replaced by simpler inline code
- Shell completion is gone — Bash and fish completion are stubbed out with TODO comments. This is a major regression — completion is a key CLI UX feature
- Help output degraded — The current picocli help has beautiful grouped sections (Essentials, Editing, Caching, Configuration, Other, External). Aesh's
generateHelp = trueproduces flat, basic help. The entireCommandGroupRenderer+ external command discovery is deleted - No version check — The
VersionChecker.newerVersionAsync()call (checks for newer jbang versions) is completely removed from the execution flow - Negatable options require two fields —
--cds/--no-cdsand--integrations/--no-integrationseach need two boolean fields + a getter, vs picocli's singlenegatable = true Optional<String>→String— TheliteralScriptfield losesOptionalsemantics (distinguishing "not set" from "set to empty"), replaced with null checks- Global flags workaround —
applyParentFlags()manually scans raw args for--verbose,--quiet, etc. as a "workaround" because aesh's inherited option propagation doesn't work reliably in thebuildExecutorpath. This is fragile - Test infrastructure changed —
checkedRun()lost its genericFunction<T, Integer>parameter and now catchesCommandLineParserExceptionto fall back toJBang.execute()— feels like a workaround for parse-vs-execute differences - Default value provider is simpler but less capable — The picocli version checked system properties, then config, with hierarchical key lookup (
app.list.format→format). The aesh version only doescommandName.optionNameconfig lookup --enableassertions/--enablesystemassertionsrenamed to--ea/--esa— a breaking change for existing users/scripts- External plugin command discovery is completely removed (the PATH scanning for
jbang-*commands in help) - Depends on unreleased
aesh 3.6-dev— Not in Maven Central, so nothing builds yet
handleDefaultRunno longer takes aCommandSpec, uses reflection on@CommandDefinition/@GroupCommandDefinitionannotations instead- Custom option parsers (
StrictOptionParser,DebugOptionParser) replace picocli'spreprocessor/fallbackValue/parameterConsumer— roughly equivalent complexity CatalogFileOptionsMixinextracted to replace theBaseAliasCommandabstract class pattern — reasonable refactor- Benchmark test suite added (
TestStartupBenchmark) — useful for comparing startup overhead between frameworks @OptionGroupreplaces@Option(parameterConsumer = KeyValueConsumer.class)for maps@OptionListreplaces multi-value@Optionfor lists
This is a work-in-progress proof of concept that shows aesh can replace picocli structurally, but has significant feature regressions:
- Shell completion (critical for CLI tools) is unimplemented
- Help formatting goes from excellent to basic
- Version update checking is lost
- Global flag handling relies on a fragile workaround
- Several breaking changes to option names
The motivation appears to be startup performance (hence the benchmark tests) and possibly tighter integration with Red Hat's tooling ecosystem (aesh is a Red Hat project, same as the author). Whether the tradeoff is worth it depends on whether aesh can eventually match picocli's rich help/completion features, and whether startup time is actually a pain point for jbang users.