Command Line Tools
Installation
The wheels binary is distributed through four channels: Homebrew for macOS, .deb/.rpm packages for Linux, Scoop for Windows, and a direct JAR download for anyone who wants to wire it up by hand. Each channel installs the same two artifacts — the LuCLI launcher and the Wheels Module that ships the framework commands.
You’ll use this for:
- First-time setup on a new machine.
- Verifying that
wheels --versionruns before you start work. - Upgrading the CLI when a new LuCLI or Wheels Module release lands.
- Troubleshooting PATH and
JAVA_HOMEissues on macOS Apple Silicon or a stripped-down Linux host.
Requirements
Section titled “Requirements”The CLI runs on Java 21. On macOS the Homebrew formula installs openjdk@21 for you and sets JAVA_HOME inside the wheels wrapper. On Linux the .deb/.rpm package declares OpenJDK 21 as a runtime dependency, so apt/dnf pulls it in alongside wheels. The wrapper probes /usr/lib/jvm/ for a Java 21 install and exports JAVA_HOME itself. If you’re installing manually — or on Windows without Scoop — grab a JDK 21 build from Adoptium or Homebrew:
brew install openjdk@21java -versionYou should see openjdk version "21.0.x" in the output. On Apple Silicon macOS, Homebrew’s openjdk@21 isn’t symlinked into /usr/bin/java by default — the wheels formula handles this by exporting JAVA_HOME itself, but if you call java directly you’ll need to set it manually.
macOS (Homebrew)
Section titled “macOS (Homebrew)”Tap the formula repository and install:
brew tap wheels-dev/wheelsbrew install wheelsHomebrew 5.1+ asks you to trust third-party taps on first use — run brew trust wheels-dev/wheels once if prompted.
The formula (wheels-dev/wheels) pins a specific LuCLI release and a matching Wheels Module tarball. At install time, Homebrew downloads both and stages the module under $(brew --prefix)/opt/wheels/share/wheels/module/. It does not copy the module into your home directory yet — that happens on first run.
The first time you invoke wheels, the wrapper:
- Compares the staged module version against
~/.wheels/modules/wheels/.module-version. - If the versions don’t match (or the destination doesn’t exist), it copies the module into
~/.wheels/modules/wheels/. - Exports
LUCLI_HOME=$HOME/.wheelsso all runtime state — installed modules, running servers, resolved deps, stored secrets — lives under that directory and stays isolated from any standalone LuCLI install. - Exports
JAVA_HOMEpointing at the Homebrew-installedopenjdk@21. - Execs the LuCLI launcher with your arguments.
On upgrades (brew upgrade wheels), the staged module version bumps; the next wheels invocation notices the mismatch and refreshes ~/.wheels/modules/wheels/ in place.
Linux (apt / yum)
Section titled “Linux (apt / yum)”Wheels publishes signed native apt and yum repositories at https://apt.wheels.dev and https://yum.wheels.dev. The packages are built by nfpm on the release workflow, signed with the Wheels Distribution GPG key, and served from a Cloudflare R2 bucket per format. Add the source once; apt/dnf watches the repo and upgrades automatically.
curl -fsSL https://apt.wheels.dev/wheels.gpg \ | sudo gpg --dearmor -o /usr/share/keyrings/wheels.gpgecho "deb [signed-by=/usr/share/keyrings/wheels.gpg] https://apt.wheels.dev stable main" \ | sudo tee /etc/apt/sources.list.d/wheels.listsudo apt update && sudo apt install wheelssudo dnf config-manager --add-repo https://yum.wheels.dev/wheels.reposudo dnf install wheelsThe package lays down /usr/bin/wheels (a shell wrapper), /opt/wheels/wheels (the LuCLI launcher, renamed at stage time so basename(argv[0]) is wheels and module dispatch routes correctly), /opt/wheels/module/ (the staged Wheels Module), /opt/wheels/.version and /opt/wheels/.channel (read by the wrapper for wheels --version), and /opt/wheels/sqlite-jdbc.jar (auto-staged into Lucee Express on first run). The wrapper mirrors the Homebrew flow: probes /usr/lib/jvm/ for OpenJDK 21, exports LUCLI_HOME=$HOME/.wheels, syncs the staged module into ~/.wheels/modules/wheels/ on first run or version mismatch, then execs LuCLI.
The bleeding-edge channel publishes a new package on every merge to develop, with a distinct package name (wheels-be) so it can coexist with stable on the same host:
curl -fsSL https://apt.wheels.dev/wheels.gpg \ | sudo gpg --dearmor -o /usr/share/keyrings/wheels.gpgecho "deb [signed-by=/usr/share/keyrings/wheels.gpg] https://apt.wheels.dev bleeding-edge main" \ | sudo tee /etc/apt/sources.list.d/wheels-be.listsudo apt update && sudo apt install wheels-besudo dnf config-manager --add-repo https://yum.wheels.dev/wheels-be.reposudo dnf install wheels-beEach .deb/.rpm carries its full SemVer version internally (e.g. 4.0.1~snapshot.1700 for snapshots), so dpkg --compare-versions and rpmvercmp correctly order pre-releases below the next GA. (GitHub Releases rewrites ~ to . in uploaded asset filenames, but the URLs you actually fetch from the apt/yum repos already use the canonical ~-form.)
Windows (Scoop)
Section titled “Windows (Scoop)”Wheels ships on Windows through Scoop — a portable, sandboxed package manager that fits the project’s release cadence (hourly autoupdate for both stable and bleeding-edge channels). The legacy Chocolatey path is no longer supported; the old v1.x wheels package at community.chocolatey.org/packages/wheels is the CommandBox-based release and won’t drive a v4 tutorial.
# git is required by Scoop to clone any third-party bucket — Scoop's installer# doesn't ship git. The main bucket (where git itself lives) is bundled with# the Scoop installer, which is why this works from a totally fresh shell.scoop install git
scoop bucket add wheels https://github.com/wheels-dev/scoop-wheels
# Pick a channel:scoop install wheels # stable - tracks GA tagsscoop install wheels-be # bleeding-edge - tracks every develop merge
wheels --versionBoth packages bundle OpenJDK 21 inline — no separate java bucket, no JAVA_HOME setup. Both expose the same wheels PATH shim, so Scoop refuses to install both at once. To switch channels:
scoop uninstall wheels && scoop install wheels-bescoop uninstall wheels-be && scoop install wheelsSee Release Channels for the full channel comparison.
The Scoop bucket lives at wheels-dev/scoop-wheels. New versions land in the bucket via autoupdate.yml, a self-hosted workflow that listens for a repository_dispatch event from this repo’s release.yml. End-to-end latency from upstream tag to user-installable manifest: ~5-7 min. A daily cron tick at 08:30 UTC catches any dispatch the workflow missed.
WinGet support (winget install Wheels.Wheels) is planned for post-v4.0.0 GA once a proper installer artifact is built. Until then, use Scoop.
Manual JAR install
Section titled “Manual JAR install”If Homebrew and Scoop don’t fit your environment — a locked-down CI host, a Docker image, or a Windows box where Scoop isn’t an option — you can wire the pieces up directly. The install has two independent artifacts:
-
LuCLI launcher. Download from github.com/cybersonic/LuCLI/releases. Pick the asset matching your OS —
lucli-<version>-macos,lucli-<version>-linux, orlucli-<version>.baton Windows. Rename it towheels(orwheels.bat) so LuCLI’s binary-name detection activates the Wheels branding, and drop it somewhere on yourPATH. -
Wheels Module. Download
wheels-module-<version>.tar.gz(or.zipon Windows) from github.com/wheels-dev/wheels/releases. Extract it into~/.wheels/modules/wheels/:illustrative — manual module extract mkdir -p ~/.wheels/modules/wheelstar -xzf wheels-module-*.tar.gz -C ~/.wheels/modules/wheels/ -
Environment. Export
JAVA_HOMEto your JDK 21 install andLUCLI_HOMEto$HOME/.wheels:illustrative — environment variables export JAVA_HOME=/path/to/jdk-21export LUCLI_HOME=$HOME/.wheelsPersist these in your shell profile (
~/.zshrc,~/.bashrc, or$PROFILEon PowerShell).
Pick LuCLI and Wheels Module versions that are known to be paired — the Homebrew formula’s LUCLI_VERSION and MODULE_VERSION constants in Formula/wheels.rb are the canonical source for “what’s currently shipping together.”
Advanced: existing LuCLI install
Section titled “Advanced: existing LuCLI install”If you already have standalone LuCLI installed and want to layer the Wheels module on top, LuCLI’s built-in installer can pull the same module zip the package managers ship — but only via --url=, not by bare name. The registry entry for wheels currently points at the wheels-cli-lucli mirror, which has been slow to sync from develop, so lucli modules install wheels without --url may land an older version. There is no wheels@be tag in the LuCLI registry; channel selection happens at the URL level.
lucli modules install wheels --force \ --url=https://github.com/wheels-dev/wheels/releases/download/v4.0.0/wheels-module-4.0.0.zipSNAP_TAG=$(curl -fsSL https://api.github.com/repos/wheels-dev/wheels-snapshots/releases \ | jq -r '.[0].tag_name | sub("^v"; "")')if [ -z "$SNAP_TAG" ] || [ "$SNAP_TAG" = "null" ]; then echo "Error: could not resolve snapshot tag. Check your network, GitHub API rate limit, or set SNAP_TAG manually." >&2 exit 1filucli modules install wheels --force \ --url="https://github.com/wheels-dev/wheels-snapshots/releases/download/v${SNAP_TAG}/wheels-module-${SNAP_TAG}.zip"--force overwrites a previously installed version. The module lands in ~/.lucli/modules/wheels/ and is invoked as lucli wheels ….
Verifying installation
Section titled “Verifying installation”After any of the four paths, confirm the binary resolves and reports a version:
wheels --versionYou should see something like:
Wheels Version: 4.0.3followed by the Wheels ASCII-art banner. The richer check is wheels version (no dashes), which reports the release channel and the JVM the wrapper picked up:
Wheels 4.0.3 (stable)Java 21.0.11If either line is missing or reports an unexpected value — say a Java major version other than 21 — jump to troubleshooting below.
Running wheels with no arguments is also a quick sanity check — it prints the same Wheels help banner as wheels --help. (wheels help falls through to LuCLI’s own generic help rather than the Wheels banner, so prefer wheels --help.) If you see a Component [modules.wheels.Module] has no function with name [main] error instead, you are running a build that predates this fix — upgrade to the latest 4.0.x release to restore the expected behavior.
Once the version check passes, wheels info inside a Wheels project gives you a deeper sanity check against a real app — datasource connection, environment, route count. See App Inspection for the full output and how to read it.
Troubleshooting
Section titled “Troubleshooting”JAVA_HOME not set or points at the wrong JDK
Section titled “JAVA_HOME not set or points at the wrong JDK”The Homebrew wrapper sets JAVA_HOME inside its own process, so a missing or wrong JAVA_HOME in your shell doesn’t break wheels. But if you’re calling java directly — or running a manual install — the CLI fails fast with a “Java 21 required” message when JAVA_HOME isn’t pointed at a JDK 21 install. On Apple Silicon macOS, Homebrew stages openjdk@21 at /opt/homebrew/Cellar/openjdk@21/<version>/libexec/openjdk.jdk/Contents/Home; export that path and re-run.
wheels: command not found after install
Section titled “wheels: command not found after install”On macOS, Homebrew’s shim directory has to be on your PATH — /opt/homebrew/bin on Apple Silicon, /usr/local/bin on Intel. Run brew --prefix to confirm, check your PATH, and add the shim directory if it’s missing. A fresh shell (exec $SHELL -l) often fixes this without any config change.
On Linux, the .deb/.rpm package installs /usr/bin/wheels, which should be on every user’s default PATH. If it isn’t, run dpkg -L wheels (Debian/Ubuntu) or rpm -ql wheels (Fedora/RHEL) to confirm the package landed and the binary is at the expected path. A reopened shell (exec $SHELL -l) clears any stale PATH cache from the previous session.
Conflict with a standalone LuCLI install sharing ~/.lucli/
Section titled “Conflict with a standalone LuCLI install sharing ~/.lucli/”The wheels formula deliberately isolates runtime state under ~/.wheels/ (via LUCLI_HOME) so a standalone lucli install — which uses ~/.lucli/ — stays out of the way. If you previously had LuCLI installed directly and see odd module-resolution errors, check that the wrapper set LUCLI_HOME correctly (wheels system paths prints the resolved home directory and where it came from) and that ~/.wheels/modules/wheels/ contains a current Module.cfc and .module-version file.
Windows: there is no Resource provider available with the name [c]
Section titled “Windows: there is no Resource provider available with the name [c]”On Windows, wheels new, wheels start, and most other subcommands crashed before any work could happen with:
lucee.runtime.exp.NativeException: there is no Resource provider availablewith the name [c], available resource providers are [ftp, zip, tar, tgz,http, https, ram, s3]The cause was mixed-slash paths: java.io.File.getCanonicalPath() on Windows returns backslash form (C:\Users\tim\Projects), which — when concatenated with a forward-slash suffix — produced a string like C:\Users\tim\Projects/vendor/wheels. Lucee’s Resource API parsed c: as a URI scheme and bailed because no c provider is registered. This is fixed in the release that includes #2841. Update to the latest version and the error will not recur — scoop update wheels for Scoop installs, or re-fetch the latest Wheels Module (see Manual JAR install above) if you wired it up by hand. wheels --version was unaffected because LuCLI handles that flag before dispatching to the module.