Appearance
CLI Reference
The lune CLI manages your app's full lifecycle — scaffolding, development, building, and running.
Install the CLI via a pre-built binary or make deploy — see Getting Started.
Global flags
These flags are accepted by all commands:
| Flag | Description |
|---|---|
--debug | Enable verbose debug logging |
lune init
Scaffold a new Lune app.
sh
lune init <APP_NAME> [flags]Arguments:
| Argument | Description |
|---|---|
APP_NAME | Name of the app to create (required). Used as the directory name and Crystal project name. Spaces and slashes are replaced with underscores. |
Flags:
| Flag | Short | Default | Description |
|---|---|---|---|
--template | -t | vanilla | Frontend template to use. Options: vanilla, vue |
--force | -f | false | Delete and reinitialize the app directory from scratch |
--skip-existing | -k | false | Skip files that already exist instead of failing |
--skip-install | -s | false | Skip running shards install and npm install after scaffolding |
Examples:
sh
# Scaffold with default Vanilla JS template
lune init my_app
# Scaffold with Vue 3 template
lune init my_app --template vue
# Re-scaffold over an existing directory
lune init my_app --force
# Scaffold without running install (useful in CI)
lune init my_app --skip-installlune dev
Start the frontend dev server and Crystal backend together with hot reload.
sh
lune devAlias: d
- Starts the Vite dev server (using
frontend.dev.cmdfromlune.yml, defaulting tonpm run dev) - Compiles the Crystal app and opens a native window pointing at the dev server URL
- Watches Crystal source files; recompiles and refreshes on change
- Prevents duplicate windows via single-instance lock
Example:
sh
lune devlune build
Build the frontend and compile the Crystal binary with the frontend embedded.
sh
lune build [flags]Alias: b
Flags:
| Flag | Short | Default | Description |
|---|---|---|---|
--release | -r | false | Compile with Crystal's --release optimizations (slower compile, faster runtime) |
What it does:
- Runs a Crystal pre-pass (
-Dbuild_mode) to generateApp.js/App.d.ts - Runs the frontend build command (default:
npm run build) - Compiles the Crystal binary with the frontend assets embedded
- macOS: if
mac.signis set inlune.yml, runscodesignon the output to enableUNUserNotificationCenter
Output:
- macOS:
build/bin/<app_name>.app(app bundle) - Linux:
build/bin/<app_name>
Examples:
sh
# Development build
lune build
# Optimized release build
lune build --releaselune dist
Package the built app into a platform-native distributable.
sh
lune dist [flags]Requires a prior lune build. The output format is chosen automatically based on the current platform, or set explicitly with a flag.
Flags:
| Flag | Default | Description |
|---|---|---|
--skip-notarize | false | Skip notarization even if mac.notarize: true is set (macOS) |
macOS — DMG:
- Copies the built
.appinto a staging directory alongside an/Applicationssymlink - Runs
hdiutil createto produce a compressedbuild/bin/<name>.dmg - If
mac.notarize: trueandAPPLE_ID/APPLE_PASSWORD/APPLE_TEAM_IDenv vars are set:- Submits to Apple's notary service (
xcrun notarytool submit --wait) - Staples the ticket (
xcrun stapler staple) so Gatekeeper can verify offline
- Submits to Apple's notary service (
Linux — AppImage:
- Assembles an AppDir (
usr/bin/<name>,AppRun,.desktopentry, icon if available) - Runs
appimagetoolto producebuild/bin/<name>.AppImage - Cleans up the AppDir
Requires appimagetool in PATH — download the binary for your architecture from the appimagetool releases.
Output:
| Platform | Output |
|---|---|
| macOS | build/bin/<name>.dmg |
| Linux | build/bin/<name>.AppImage |
Examples:
sh
# Build → package (platform default)
lune build --release
lune dist
# macOS: package without notarizing (local testing)
lune dist --skip-notarizeSee Distribution for the full signing and notarization setup.
lune run
Launch the previously built binary.
sh
lune runAlias: r
Runs the artifact produced by lune build. Respects single-instance locking — a second lune run for the same app exits immediately if one is already running.
lune check
Type-check the Crystal source without building.
sh
lune checkUseful for fast feedback during development without going through a full compile.
lune doctor
Verify your development environment and (optionally) the project's plugin registry.
sh
lune doctor [flags]Flags:
| Flag | Default | Description |
|---|---|---|
--plugins | false | After the environment checks, also list the live plugin registration set (built-ins + Lune.use calls) |
Subcommands:
| Subcommand | Description |
|---|---|
api | Introspect the typed RPC contract the app exposes (see below) |
Environment checks:
| Check | What it verifies |
|---|---|
crystal | Crystal is installed and reports a version |
node | Node.js is installed |
npm | npm is installed |
shards | shards check passes (all deps installed) |
frontend deps | node_modules directory exists in the frontend dir |
app entry | The configured Crystal entry file exists |
Plugin checks (always shown):
The built-in registry is summarised with platform availability and soft-dep gaps. ✓ marks active, ✗ marks platform-filtered. A soft dep <id> not active annotation appears when a dependent's optional dep isn't in the active set.
With --plugins, the doctor compiles your app entry with -Dlune_inspect, short-circuits Lune.run after the registry is populated, and lists the live set — the same shape the running app would see, including any Lune.use(MyPlugin.new) calls your code makes. This catches "I registered it but the app doesn't see it" mismatches early.
lune doctor api
Introspect the typed RPC surface your app exposes — the exact contract the generated client was built against. It compiles your app entry, resolves the full binding set (the same way a build does), and prints every procedure with its argument names/types and return type, followed by the custom types (structs and enums) those signatures reference:
sh
lune doctor api procedures (73):
Demo.greet(name: String) -> String
Lune.Plugins.Window.setSize(width: Int32, height: Int32) -> Nil
Lune.Plugins.System.environment() -> NamedTuple(os: OS, arch: String, devtools: Bool)
…
types (2):
OS = "darwin" | "linux" | "windows"
CounterState { value: Int32; step: Int32; at_default: Bool }Flags:
| Flag | Default | Description |
|---|---|---|
--json | false | Print the raw Vow manifest JSON on stdout instead of the table, with no environment report — pipe it into tooling |
sh
lune doctor api --json | jq '.procedures | length'The same contract is available at runtime via the Introspection plugin (lune.Introspection.manifest()) — see TypeScript & introspection.
Example output:
✓ crystal Crystal 1.20.0 [...]
✓ node v22.0.0
✓ npm 10.0.0
✓ shards ok
✓ frontend deps ok
✓ app entry src/main.cr
Plugins
✓ event Event
✓ stream Stream
✗ file_watch FileWatch (not available on win32)
✓ tray Tray
…lune version
Print the installed Lune version.
sh
lune version