feat: replace picocli with aesh for CLI parsing#2453
Conversation
ad9ecbe to
ff9018e
Compare
There was a problem hiding this comment.
Pull request overview
This PR migrates JBang’s CLI layer from picocli to aesh, replacing command annotations, parsing helpers, default-value handling, and related test infrastructure so the CLI can run without picocli’s runtime reflection model.
Changes:
- Reworks core CLI plumbing to use aesh commands, mixins, parsers, and default-value resolution.
- Replaces picocli-specific helpers/converters with aesh-specific implementations and updates command classes accordingly.
- Updates test coverage and build wiring for the new parser, including new parsing tests and a benchmark task.
Reviewed changes
Copilot reviewed 67 out of 69 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/test/java/dev/jbang/cli/TestTemplate.java |
Swaps tests to JBang.execute. |
src/test/java/dev/jbang/cli/TestStartupBenchmark.java |
Adds tagged CLI startup benchmark tests. |
src/test/java/dev/jbang/cli/TestPlugins.java |
Adapts help-output assertion for aesh output. |
src/test/java/dev/jbang/cli/TestJdk.java |
Rewrites JDK command tests around parsed CLI invocations. |
src/test/java/dev/jbang/cli/TestInit.java |
Updates init tests to new execute helper. |
src/test/java/dev/jbang/cli/TestInfo.java |
Switches info tests to typed parseCommand. |
src/test/java/dev/jbang/cli/TestExternalDeps.java |
Updates run parsing tests to aesh path. |
src/test/java/dev/jbang/cli/TestExport.java |
Reworks export tests for new command API. |
src/test/java/dev/jbang/cli/TestEdit.java |
Updates edit tests to aesh parsing/execution. |
src/test/java/dev/jbang/cli/TestDeps.java |
Moves deps tests to JBang.execute. |
src/test/java/dev/jbang/cli/TestConfig.java |
Adjusts config tests for aesh syntax/behavior. |
src/test/java/dev/jbang/cli/TestCatalog.java |
Updates catalog tests to new execute helper. |
src/test/java/dev/jbang/cli/TestArguments.java |
Rewrites parser-behavior tests around aesh. |
src/test/java/dev/jbang/cli/TestApp.java |
Updates app command tests and typed parsing. |
src/test/java/dev/jbang/cli/TestAlias.java |
Reworks alias tests for new parser and option syntax. |
src/test/java/dev/jbang/cli/TestAeshParsing.java |
Adds focused aesh parsing regression tests. |
src/test/java/dev/jbang/cli/TemplatePropertyConverterTest.java |
Replaces removed converter tests with property parser tests. |
src/test/java/dev/jbang/TestConfiguration.java |
Updates config-default tests for new parser flow. |
src/test/java/dev/jbang/BaseTest.java |
Replaces picocli-based test runner helper. |
src/native-image/config/reflect-config.json |
Removes old picocli/native-image reflection config. |
src/main/java/dev/jbang/util/ConsoleOutput.java |
Replaces picocli ANSI rendering with local ANSI helper. |
src/main/java/dev/jbang/cli/Wrapper.java |
Converts wrapper command to aesh group/subcommand structure. |
src/main/java/dev/jbang/cli/VersionProvider.java |
Deletes obsolete picocli version provider. |
src/main/java/dev/jbang/cli/Version.java |
Converts version command to aesh options/output. |
src/main/java/dev/jbang/cli/Trust.java |
Converts trust commands to aesh group/subcommands. |
src/main/java/dev/jbang/cli/TemplatePropertyConverter.java |
Removes picocli-specific template property converter. |
src/main/java/dev/jbang/cli/StrictParameterPreprocessor.java |
Removes picocli preprocessor in favor of aesh parser. |
src/main/java/dev/jbang/cli/StrictOptionParser.java |
Adds aesh parser for --opt=value-only behavior. |
src/main/java/dev/jbang/cli/ScriptMixin.java |
Ports script-related options to aesh annotations. |
src/main/java/dev/jbang/cli/RunMixin.java |
Reimplements runtime-option parsing/default handling. |
src/main/java/dev/jbang/cli/Run.java |
Converts run command to aesh argument model. |
src/main/java/dev/jbang/cli/NativeMixin.java |
Ports native-image flags to aesh. |
src/main/java/dev/jbang/cli/KeyValueConsumer.java |
Removes obsolete picocli parameter consumer. |
src/main/java/dev/jbang/cli/JdkProvidersMixin.java |
Ports hidden JDK-provider option to aesh. |
src/main/java/dev/jbang/cli/JBangDefaultValueProvider.java |
Adds aesh-backed default value provider. |
src/main/java/dev/jbang/cli/Init.java |
Converts init command and rewires property/template handling. |
src/main/java/dev/jbang/cli/HelpMixin.java |
Removes obsolete picocli help mixin. |
src/main/java/dev/jbang/cli/FormatMixin.java |
Removes obsolete picocli format mixin. |
src/main/java/dev/jbang/cli/ExternalCommandsProvider.java |
Adds external plugin help-section provider for aesh help. |
src/main/java/dev/jbang/cli/ExportMixin.java |
Removes old export picocli mixin. |
src/main/java/dev/jbang/cli/Edit.java |
Converts edit command to aesh and new defaults flow. |
src/main/java/dev/jbang/cli/Deps.java |
Converts deps commands to aesh group/subcommands. |
src/main/java/dev/jbang/cli/DeprecatedMessageHandler.java |
Removes picocli-specific deprecated-flag handler. |
src/main/java/dev/jbang/cli/DependencyInfoMixin.java |
Ports dependency/repository/property options to aesh. |
src/main/java/dev/jbang/cli/DebugOptionParser.java |
Adds aesh parser for debug option fallback semantics. |
src/main/java/dev/jbang/cli/Config.java |
Converts config commands to aesh and new argument parsing. |
src/main/java/dev/jbang/cli/Completion.java |
Replaces picocli completion generation with aesh generator. |
src/main/java/dev/jbang/cli/CommaSeparatedConverter.java |
Removes obsolete picocli list converter. |
src/main/java/dev/jbang/cli/CatalogFileOptionsMixin.java |
Adds shared aesh catalog-file option mixin. |
src/main/java/dev/jbang/cli/Cache.java |
Converts cache commands to aesh group/subcommands. |
src/main/java/dev/jbang/cli/BuildMixin.java |
Ports build-related options to aesh. |
src/main/java/dev/jbang/cli/Build.java |
Converts build command to aesh argument model. |
src/main/java/dev/jbang/cli/BaseCommand.java |
Replaces picocli base command lifecycle with aesh lifecycle. |
src/main/java/dev/jbang/cli/BaseBuildCommand.java |
Rewires shared build command setup for aesh mixins. |
src/main/java/dev/jbang/cli/AIOptions.java |
Removes old picocli AI option holder. |
src/main/java/dev/jbang/Main.java |
Replaces picocli entrypoint execution with aesh runtime. |
src/it/java/dev/jbang/it/AbstractHelpBaseIT.java |
Updates integration help assertion for new help output. |
build.gradle |
Swaps picocli deps for aesh and adds benchmark task. |
.gitignore |
Ignores new local planning/cache files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ff9018e to
2039fea
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 67 out of 69 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
09449a7 to
674b80e
Compare
| private static volatile Set<String> subcommandNames; | ||
|
|
||
| static Set<String> getSubcommandNames() { | ||
| if (subcommandNames == null) { |
There was a problem hiding this comment.
there is no methods in aesh to do this outside looking up annotations manually?
There was a problem hiding this comment.
Correct, aesh doesn't expose the registered command names outside of a running CommandRegistry. The annotation lookup is the most lightweight approach — avoids building the full runtime just to get names.
|
difference in behaviour on help info. before: now: |
674b80e to
febaf10
Compare
| @Option(shortName = 'i', name = "interactive", hasValue = false, description = "Activate interactive mode") | ||
| public Boolean interactive; | ||
|
|
||
| public void resolveAfterParse() { |
There was a problem hiding this comment.
why is this + its 3(?) calls needed ? some difference in how aesh and picocli handles mixins or ?
There was a problem hiding this comment.
picocli handled --debug and --jfr config fallbacks via IParameterConsumer/IParameterPreprocessor during parsing. aesh doesn't have those hooks, so resolveAfterParse() applies the Configuration-based defaults after parsing. Called from the 3 commands that use RunMixin (Run, AliasAdd, AppInstall).
There was a problem hiding this comment.
Would it not make sense to have such api in aesh or is there some downside ?
|
Hi, maybe it is a bit late, I see the conversation and an effort in this PR, but why is the motivation to change the dependency in jbang?
Maybe it is a strong change in the solution. Juan Antonio |
|
As you can notice it's been around as long as picocli itself - so it's not a random project :) With 1.5-2x on jvm and 5 time faster startup in native is very noticeable. It's the main thing that makes Jbang feel "sluggish" so definitely high in the list. You'll notice that thus far not a single integration test changed so it gives fairly high confidence it will work. I'll keeep testing on it and would love others to give a try with this build to see if they find issues. |
|
Oki, so performance is the main argument. I can help on testing if required, happy to help. |
ef9bc5c to
7eacd2d
Compare
| abstract class BaseExportCommand extends BaseCommand { | ||
|
|
||
| @CommandLine.Mixin | ||
| ExportMixin exportMixin; |
There was a problem hiding this comment.
any reason why not keep using mixin ?
There was a problem hiding this comment.
aesh does not support mixin inheritance atm.
|
about jbang config system - it seem to work..but its alot of extra code. ...and do we have to remember on all commands to set JBangDefaultValueProvider ? |
- Rename Cache.CacheClear fields to match option names (e.g. urls->url, jars->jar, groovys->groovyc) for consistency as suggested by Max - Add comment explaining FALLBACK_OPTIONS in JBangDefaultValueProvider to clarify why debug and jfr are excluded from default value lookup - Move null-element filtering inside the args!=null guard in Main.handleDefaultRun() to avoid unnecessary work on null input
This has been fixed by an improvement in aesh. |
f6cca41 to
1ba028e
Compare
|
@jabrena @wfouche @quintesse would be great to hear if you spot any issues with this variant of jbang. would like to merge this sooner rather than later :) |
Migrate jbang's CLI framework from picocli to aesh, leveraging aesh's compile-time annotation processor to eliminate runtime reflection and improve startup performance. Key changes: - Replace all picocli annotations with aesh equivalents - Convert Callable<Integer> commands to Command<CommandInvocation> - Use aesh's fallbackValue for three-state options (--debug, --jfr, --module, --code, --open) replacing custom StrictOptionParser - Set DefaultValueProvider at registry level instead of per-command - Implement external plugin discovery via ExternalCommandsProvider - Port DebugOptionParser for --debug peek-ahead pattern matching - Add subcommand name consistency test - Use terminal-api ANSI constants in ConsoleOutput - Rewrite docs generator (genadoc.java) for aesh command model with alias, negatable, paramLabel, and boolean flag support - Regenerate all CLI reference documentation Error handling: - Unwrap aesh RuntimeException to find root IllegalArgumentException for clean error messages and EXIT_INVALID_INPUT (2) exit codes - Add catch-all in BaseCommand.execute() for unexpected exceptions - Replace System.exit() in handleDefaultRun() with ExitException - Add 'Missing required subcommand' message for group commands Performance (vs picocli, median): - Native image: 6ms vs 33ms (5.5x faster) for single commands - JDK 25: 109ms vs 228ms (2.1x faster) - JDK 11: 149ms vs 269ms (1.8x faster) Dependencies: - org.aesh:aesh:3.8, org.aesh:readline-api:3.8 - Removed: info.picocli:picocli
ad5bf78 to
885cde1
Compare
The native build (PR jbangdev#2473) now produces platform-suffixed binaries like jbang-linux-x64.bin / jbang-windows-x64.bin.exe. Match by suffix (.bin / .bin.exe) instead of exact file name so 'jbang wrapper install' works again when run from the native image.
885cde1 to
4d740b3
Compare
Migrate jbang's CLI framework from picocli to aesh, leveraging aesh's compile-time annotation processor to eliminate runtime reflection and improve startup performance.
Key changes:
Performance (vs picocli, median):
Summary by CodeRabbit
New Features
Bug Fixes
Build & Configuration
Documentation