Skip to content

feat: improve completion command with shell auto-detection, pwsh support, and usage hints - #2552

Merged
maxandersen merged 2 commits into
mainfrom
completionhelp
Jul 1, 2026
Merged

feat: improve completion command with shell auto-detection, pwsh support, and usage hints#2552
maxandersen merged 2 commits into
mainfrom
completionhelp

Conversation

@maxandersen

Copy link
Copy Markdown
Collaborator

When we migrated from picocli to aesh, the completion command lost the helpful usage instructions that told users how to enable tab completions. This PR restores and improves that experience.

Changes

Shell auto-detectionjbang completion now auto-detects the current shell by checking FISH_VERSION, ZSH_VERSION, BASH_VERSION, and PSModulePath environment variables, then falls back to $SHELL. No --shell flag needed in most cases.

Positional argument — The shell can now be passed as a simple argument: jbang completion fish instead of jbang completion --shell fish. The --shell option is still supported for backwards compatibility.

PowerShell support — Added pwsh as a supported shell, enabled by upgrading aesh from 3.14.3 to 3.15.1 which includes PowerShellCompletionGenerator.

Usage hints in --helpjbang completion --help now shows quick-start commands for each shell.

Usage hints in generated scripts — Each generated completion script includes shell-specific comments at the end explaining how to enable completions (both "try it now" and "make it permanent" approaches).

Documentation — Added a "Tab Completion" section to installation.adoc with per-shell setup instructions. Updated CLI reference docs via clidoc.

Dev shelljust shell pwsh now works for interactive completion testing.

Build fixes — Set rootProject.name = 'jbang' in settings.gradle so the shadow jar is named jbang.jar (matching what the launcher script expects).

Example output

$ jbang completion --help
Generate bash/zsh or fish completion script for jbang.
Usage: jbang completion [-hx] [-s=<shell>] [<shell>] ...

Run the following to enable TAB completion in the current shell:

  bash/zsh:  source <(jbang completion)
  fish:      jbang completion | source
  pwsh:      (jbang completion) -join "\`n" | Invoke-Expression

The shell is auto-detected. To make it permanent, see the
comments at the end of the generated script.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • ai-review

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d9673c7-17b8-434a-82f9-8e6f0d8a24f3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@maxandersen

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@stalep i can't get the pwsh completion to work ...any idea what doing wrong?

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR info and diff
gh pr view 2552 --repo jbangdev/jbang --json title,body,files,headRefName,baseRefName 2>/dev/null | jq '{title, headRefName, baseRefName, files: [.files[].path]}'

Length of output: 1926


🏁 Script executed:

#!/bin/bash
# Get the diff of the PR
gh pr diff 2552 --repo jbangdev/jbang 2>/dev/null | head -500

Length of output: 28734


🏁 Script executed:

#!/bin/bash
# Look at the completion command implementation
fd -e java . --exec grep -l -i "completion" {} \; | head -20

Length of output: 1005


🏁 Script executed:

#!/bin/bash
# Look at the aesh version used
cat build.gradle | grep -i "aesh" || true
cat build.gradle.kts | grep -i "aesh" || true
fd -g "*.gradle" --exec grep -l "aesh" {} \;

Length of output: 878


🏁 Script executed:

#!/bin/bash
# Read the full Completion.java file
cat -n src/main/java/dev/jbang/cli/Completion.java

Length of output: 6617


🏁 Script executed:

#!/bin/bash
# Get the rest of the PR diff
gh pr diff 2552 --repo jbangdev/jbang 2>/dev/null | tail -300

Length of output: 12515



@maxandersen: Here's the full review. The likely root cause of pwsh not working is explained in the first finding below.


Review: src/main/java/dev/jbang/cli/Completion.java

🐛 Critical — detectShell() falsely returns BASH when invoked from PowerShell on macOS/Linux

On macOS/Linux, jbang is a bash wrapper script. When pwsh calls it, bash spawns as a subprocess and automatically exports BASH_VERSION into its own environment. Java inherits that variable. So detectShell() hits the BASH_VERSION branch and returns BASHPSModulePath (checked only as a last resort) is never reached.

Workaround right now: run jbang completion pwsh or jbang completion -s pwsh explicitly.

Fix options:

  1. Move the PSModulePath check before the BASH_VERSION / ZSH_VERSION / FISH_VERSION checks — PowerShell sets PSModulePath reliably and unix shells do not.
  2. Or skip the version-variable checks entirely when PSModulePath is set.
static ShellType detectShell() {
    // PSModulePath is set by PowerShell — check it first,
    // before BASH_VERSION etc. which can leak from wrapper scripts.
    if (System.getenv("PSModulePath") != null)
        return ShellType.PWSH;

    if (System.getenv("FISH_VERSION") != null)
        return ShellType.FISH;
    if (System.getenv("ZSH_VERSION") != null)
        return ShellType.ZSH;
    if (System.getenv("BASH_VERSION") != null)
        return ShellType.BASH;

    String shell = System.getenv("SHELL");
    if (shell == null || shell.isEmpty()) return null;
    if (shell.contains("fish"))      return ShellType.FISH;
    if (shell.contains("zsh"))       return ShellType.ZSH;
    if (shell.contains("bash"))      return ShellType.BASH;
    if (shell.contains("pwsh") || shell.contains("powershell")) return ShellType.PWSH;
    return null;
}

⚠️ Inconsistency — pwsh hint in header vs. usage hint

CompletionHelpSectionsProvider.getHeader() shows (no shell argument, relies on auto-detection):

pwsh:  (jbang completion) -join "`n" | Invoke-Expression

But usageHint(PWSH) (appended to the generated script) shows the explicit form:

(jbang completion pwsh) -join "`n" | Invoke-Expression

Since auto-detection won't work on macOS/Linux (see above), the header should also show the explicit form, or at least add a note.


⚠️ Potential breaking change — default shell removed

Previously shellType defaulted to ShellType.BASH. Now jbang completion (with no args) throws if detection fails. Any CI scripts running jbang completion without a shell argument on a non-POSIX or ambiguous system will start failing.


Review: docs/modules/cli/nav.adoc

🐛 Duplicate navigation entries

The diff introduces duplicate entries in the nav:

*** xref:jbang:cli:jbang-jdk-install.adoc[install]   ← duplicate
*** xref:jbang:cli:jbang-jdk-list.adoc[list]         ← duplicate
*** xref:jbang:cli:jbang-jdk-uninstall.adoc[uninstall] ← duplicate
*** xref:jbang:cli:jbang-jdk-java-env.adoc[env]      ← duplicate
*** xref:jbang:cli:jbang-jdk-exec.adoc[exec]         ← duplicate

Each of these exists twice. The duplicates should be removed before merging.


Review: docs/modules/cli/pages/jbang-completion.adoc and jbang.adoc

⚠️ Machine-specific / clidoc artifact committed

The generated completion page ends with:

== EXTERNAL

*mac-aarch64*

---

   JBang ❤️  Commonhaus Foundation, MIT License
🏡 https://www.commonhaus.org/community
🚀 https://jbang.dev

*mac-aarch64* looks like a platform tag captured from the machine where clidoc was run, not intended for the public docs. The same == EXTERNAL \n*mac-aarch64* section appears in jbang.adoc. These should be scrubbed or the clidoc generation should be made platform-agnostic before committing generated files.


Review: misc/dev-shell.sh (pwsh block)

The profile is placed at $dir/powershell/Microsoft.PowerShell_profile.ps1 and pwsh is started with XDG_CONFIG_HOME=$dir. This is the correct location on Linux — looks good. One small note: the heredoc uses <<PSEOF (unquoted), so bash expands $dir inside it — which is intentional. The \$env:PATH and \$(Get-Location) escapes are correct.


Summary

# Severity File Issue
1 🐛 Bug Completion.java detectShell() returns BASH from PowerShell on macOS/Linux due to BASH_VERSION leaking from the wrapper; PSModulePath should be checked first
2 ⚠️ Completion.java Inconsistent pwsh hint (header vs. usageHint)
3 ⚠️ Completion.java Bare jbang completion now throws instead of defaulting to bash
4 🐛 Bug nav.adoc Duplicate navigation entries
5 ⚠️ jbang-completion.adoc, jbang.adoc Machine-specific clidoc artifact (mac-aarch64) in committed docs

The PSModulePath-first detection order fix (#1) should resolve the pwsh completion issue you're seeing @maxandersen.

ʕ •ᴥ•ʔ (hope that helps track it down!)

stalep added a commit to aeshell/aesh that referenced this pull request Jun 24, 2026
…537)

Move the PSModulePath check before BASH_VERSION/ZSH_VERSION/FISH_VERSION
in detectShell(). On macOS/Linux, CLI tools are typically launched via a
bash wrapper script which sets BASH_VERSION in the environment. When
PowerShell calls such a tool, Java inherits BASH_VERSION from the wrapper
and detectShell() incorrectly returns BASH instead of PWSH.

PSModulePath is reliably set by PowerShell on all platforms (Windows,
macOS, Linux) and is never set by bash/zsh/fish, so checking it first
correctly identifies the user's actual shell.

Fixes: #537
Related: jbangdev/jbang#2552
stalep added a commit to aeshell/aesh that referenced this pull request Jun 24, 2026
…539)

Remove the cursorAtPositional heuristic from performDynamicCompletion()
that aggressively removed all option candidates (anything starting with
"-") when the cursor was at a positional argument position. This caused
"jbang run <tab>" to show only files instead of the expected options
and file arguments.

The completion engine (AeshCommandLineCompletionParser) already
determines the correct candidate set based on parser state. The
cursorAtPositional filter was second-guessing the engine and removing
valid candidates for the very common case of pressing tab after a
subcommand name.

Changes:
- Remove the option filtering heuristic (was lines 234-239)
- Emit __aesh_file__/__aesh_dir__ sentinel alongside other candidates
  (not instead of them) when the current positional supports file
  completion. Shell scripts (bash/fish/pwsh) merge file completion
  with option/subcommand suggestions.
- Update test: options and file sentinel now coexist at positional
  positions, matching how bash/fish/zsh handle completion

Fixes: #539
Related: jbangdev/jbang#2552
…ort, and usage hints

- Auto-detect shell from FISH_VERSION, ZSH_VERSION, BASH_VERSION,
  PSModulePath env vars, falling back to $SHELL
- Accept shell as positional argument: jbang completion fish
- Add PowerShell (pwsh) support via aesh 3.15.1 upgrade
- Show quick-start commands in --help output
- Append shell-specific setup instructions to generated scripts
- Add Tab Completion section to installation.adoc
- Add pwsh support to dev-shell.sh and .justfile
- Regenerate CLI reference docs
- Set rootProject.name = 'jbang' in settings.gradle
…experimental, nav duplicates

- Check PSModulePath before BASH_VERSION in detectShell() to fix pwsh
  detection on macOS/Linux where bash wrapper leaks BASH_VERSION
- Add testable detectShell(Function) overload with 11 unit tests
- Mark pwsh completion support as experimental in help, docs, and clidoc
- Remove duplicate jdk sub-command entries in cli nav.adoc
@maxandersen
maxandersen merged commit d465bb1 into main Jul 1, 2026
30 checks passed
@maxandersen
maxandersen deleted the completionhelp branch July 1, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant