Command Line
Ruzit ships as a Cargo workspace with two binaries:
ruzit (the dev tool for scaffolding, testing,
building, and packaging) and ruzitrun (the runtime /
launcher template — runs games, serves as the EXE shipped games
are cut from). Drop both on your PATH; the CLI calls
the runtime as a library for test and copies the
ruzitrun binary as a template for build.
ruzit init [path] scaffold a new project
ruzit initpackage [path] scaffold a Managed package folder
ruzit scaffold [path] regenerate .luaurc aliases from every manifest in the tree
ruzit test [path] run a Luau file
ruzit build [path] [-o] produce Generated/<exe> + Generated/Managed/*.managed
ruzit package [folder] [-o] produce <id>.scripts.managed + <id>.assets.managed
ruzit fetch-deps [path] download steam_api into the given dir (default: cwd)
ruzit refresh-types [path] re-download types.d.luau into the given dir (default: cwd)
ruzit update [path] refresh ruzitrun from the configured release base (CLI itself is left alone)
--help print this help
ruzitrun embedded launcher; runs the bundled .managed
game when called with the appended trailer
ruzitrun --run <path> run a project from disk directly
ruzitrun --console attach/allocate a console for windowed launchers
ruzit init / Test / Build
on the same binary. Those subcommands now live exclusively on
the ruzit CLI. The ruzitrun binary
handles only the running side: launcher mode (with appended
trailer) or a direct --run <path> invocation.
ruzit init
Creates a new project at path (current directory if omitted).
Writes:
| File | What's in it |
|---|---|
build.toml | Project metadata and build settings. |
Main.luau | The entry script the engine runs. |
types.d.luau | API type declarations for the Luau LSP. Always rewritten. |
.vscode/settings.json | Wires luau-lsp up to types.d.luau, sets luau-lsp.require.mode to relativeToFile, and pins luau-lsp.platform.type to standard so the LSP doesn't try to apply Roblox conventions. |
.luaurc | Strict-mode flag plus an aliases map. Seeded with Game = "./" so require("@Game/scenes/level1") resolves from the project root regardless of which file is requiring it. Run ruzit scaffold to extend the aliases with one entry per package folder anywhere in the tree. |
assets/ | Empty folder. Anything you drop in is auto-bundled. |
Packages/ | Empty folder for third-party .managed drop-ins. |
build.toml, Main.luau) are skipped
if they already exist. types.d.luau is always rewritten so
your autocomplete tracks whichever Ruzit version you're on.
ruzit initpackage
Scaffolds a Managed package folder, the unit of distribution for DLC, mods, and shareable libraries. Writes:
ManagedInfo.toml, package id, name, entry script, file mode.init.luau, the entry returned torequire("@PkgId/init").
Drop the folder next to your project's build.toml and
ruzit build will pick it up as a DLC. See
Packages & DLC for the full lifecycle.
ruzit scaffold
Regenerates .luaurc aliases by walking the entire
project tree. Any folder containing both a
ManagedInfo.toml and its declared entry file is
treated as a package: the manifest's ID becomes an
alias pointing at the folder. require("@PkgId")
then resolves to the entry file inside via Luau's directory
init lookup, and submodules are reachable as
require("@PkgId/sub/module"). The entry file's
existence is still checked — packages whose declared
Entry is missing are skipped.
The walk is recursive but skips .git,
Generated/, target/,
node_modules/, and any dotfile directory. When a
folder with a manifest is found the walk stops descending into it
(packages don't nest aliases inside themselves).
Re-run any time you add, remove, or rename a package. The file is
overwritten in full, so any aliases you added by hand get blown
away — keep custom aliases in a separate file or commit them back
into .luaurc after each scaffold.
The Game = "./" alias is always re-emitted. Example:
-- Packages/cool-math/ManagedInfo.toml has ID = "CoolMath"
-- Packages/cool-math/init.luau exists
-- libs/utils/ManagedInfo.toml has ID = "Utils" (a hand-placed package)
$ ruzit scaffold
[Ruzit] scaffold -> /work/MyGame (scanning recursively)
alias CoolMath -> ./Packages/cool-math
alias Utils -> ./libs/utils
[Ruzit] scaffold done: wrote .luaurc (3 aliases)
-- in your code:
local math = require("@CoolMath") -- -> Packages/cool-math/init.luau
local vec = require("@CoolMath/vector") -- submodule inside the package
local menu = require("@Game/scenes/menu")
luau-lsp autocomplete and Luau's own
require resolution — they don't change what the runtime loads.
At runtime, require("@PkgId") still goes through the
Managed package loader.
ruzit test
Runs a project from source. No packaging, no encryption, the
engine reads your .luau files directly off disk and the next
edit hits the next run.
With no path, the engine searches for Main.luau next to the
Ruzit executable, then in the working directory, then walks parent
folders. Pass an explicit path to override:
ruzit test # auto-find Main.luau
ruzit test ./MyGame # project dir -> MyGame/Main.luau
ruzit test ./MyGame/scenes/level1.luau
Any DLC folders adjacent to the project (folders containing
ManagedInfo.toml) are loaded too, same as in a
packaged build.
ruzit build
Packages a project for shipping. Output layout:
Generated/
MyGame.exe # launcher (ruzitrun binary + JSON trailer)
steam_api64.dll # Steam redistributable (Windows)
Managed/
MyGame.scripts.managed # encrypted .luau bundle
MyGame.assets.managed # encrypted asset bundle
MyGame.assets.shard0001.managed # sharded asset bundles (if enabled)
MyDlc.scripts.managed # discovered DLC, packaged in
MyDlc.assets.managed
Ship the entire Generated/ folder. The
launcher reads its own JSON trailer at startup, finds Managed/
next to itself, and boots the bundled scripts.
Flags
| Flag | Effect |
|---|---|
-o <dir> |
Write output to <dir> instead of ./Generated. |
--console |
Attach (or allocate) a console for print() output. Useful
when build.toml sets exe-windowed = true
and you still need stdout for debugging.
|
ruzit build always produces a launcher for the OS Ruzit
is running on, it's a copy of the running binary plus a JSON
trailer. To ship Windows + Linux + macOS, build Ruzit on each OS (or
use the GitHub Actions matrix), then run ruzit build on
each.
ruzit package
Packages a single Managed folder into .scripts.managed +
.assets.managed files, the format used by DLC and
Workshop drops. The folder must contain a ManagedInfo.toml.
cd MyMod
ruzit package # -> MyMod/Generated/Managed/<id>.scripts.managed
ruzit package . -o ../release # custom output dir
Players install your package by copying the .managed files
into a host game's Packages/ folder. The host game's
runtime mounts them as require("@your-id/...").
ruzit fetch-deps
Download the Steam SDK redistributable
(steam_api64.dll on Windows,
libsteam_api.so on Linux,
libsteam_api.dylib on macOS) into the given directory
(current directory if omitted). The Steamworks SDK is not bundled
with Ruzit, you obtain it from
partner.steamgames.com,
host the redist binary somewhere reachable, and point the CLI at
it via:
RUZIT_STEAMSDK_URL=https://your.host/path/steam_api64.dll
ruzit fetch-deps .
Without the env var set, the command exits with an explanation
rather than guessing a URL. Drop the resulting file alongside
your ruzit binary (or your shipped game's
launcher EXE), Ruzit only loads Steam when it sees the redist
file next to the executable AND either runs through Steam or
runs in test mode. See Steam for the
runtime gating logic.
ruzit refresh-types
Re-download types.d.luau into the given directory
(current directory if omitted). The CLI no longer ships a
bundled copy of the type declarations, every fresh
ruzit init fetches them from
https://raw.githubusercontent.com/thekingofspace/Ruzit/master/types.d.luau
so a newly-cloned project always picks up the latest API. This
command is the on-demand version, useful when you've updated
the runtime and want hover-docs / autocomplete to reflect the
new APIs without re-running init.
# default: pull latest from master
ruzit refresh-types
# pin to a tagged version of the types
RUZIT_TYPES_URL=https://raw.githubusercontent.com/thekingofspace/Ruzit/v4.0.0/types.d.luau \
ruzit refresh-types
# or point at your own fork / mirror
RUZIT_TYPES_URL=https://example.com/types/v4.0.0.luau \
ruzit refresh-types
ruzit init,
the CLI writes a small stub explaining how to retry instead of
failing the whole init. Run ruzit refresh-types
once you're online to drop in the real declarations.
ruzit update
Refresh the local ruzitrun runtime binary from the
configured release host. The ruzit CLI itself is
deliberately not touched, so the running CLI doesn't have
to swap itself out and there's no
ruzit.update.exe staging dance on Windows. To upgrade
the CLI, reinstall it manually (download a new zip and replace
ruzit.exe) or run a fresh install once.
# Default: pull the latest stable runtime straight from GitHub.
# No env var needed, the CLI ships pointing at the official repo.
ruzit update
# Or pin to a specific tagged version:
RUZIT_RELEASE_BASE=https://github.com/thekingofspace/Ruzit/releases/download/v4.0.0
ruzit update
# Or point at your own fork / mirror:
RUZIT_RELEASE_BASE=https://example.com/ruzit/v4.0.0
ruzit update
The CLI fetches a single platform-specific zip and extracts only
the runtime binary inside it. The zip's contents are deliberately
plain-named so a manual download -> extract leaves you with files
you can run as ruzit init /
ruzitrun --run … straight away:
{base}/ruzit-windows.zip -> ruzit.exe + ruzitrun.exe (update extracts ruzitrun.exe)
{base}/ruzit-linux.zip -> ruzit + ruzitrun (update extracts ruzitrun)
{base}/ruzit-macos.zip -> ruzit + ruzitrun (when built)
The default base URL is
https://github.com/thekingofspace/Ruzit/releases/latest/download
— GitHub's own redirect that always points at the most recent
stable release. So a fresh install can run ruzit
update with no setup. The bundled GitHub Actions
workflow auto-publishes versioned releases on every push, see
Release flow below.
ruzit update while a game is open.
On Windows the runtime exe is locked while it's running, so close
any Ruzit Test session or shipped game built on the
same ruzitrun.exe before updating.
self-update is kept as an alias of update
for back-compat.
Release flow
The repo ships
.github/workflows/release.yml
which builds Windows + Linux binaries, combines them into a
single artifact, and creates a GitHub Release with the four
binaries attached. Two trigger modes:
| Trigger | What happens |
|---|---|
Push to main / master |
Reads the version from
runtime/Cargo.toml (e.g.
4.0.0), builds the binaries, force-tags
the current commit as v4.0.0, and
publishes a normal release marked
--latest on GitHub. Both
ruzit-windows.zip and
ruzit-linux.zip are attached.
|
Push an explicit v* tag |
Builds at that exact tag and publishes the same way.
Useful for backfill releases or when you've already
bumped Cargo.toml and want to release
from a non-tip commit.
|
Releases are never marked as prereleases. The
workflow uses gh release create --latest, so
GitHub's /releases/latest/ redirect resolves
automatically to whatever the most recent push produced — which
is what the CLI's default RUZIT_RELEASE_BASE
targets.
To cut a new version: bump version = "4.0.0" ->
"1.1.0" in
Ruzit/runtime/Cargo.toml (and the matching
core + cli Cargo.tomls), commit, push.
The workflow tags + releases v1.1.0 automatically.
No secrets to configure, the workflow uses the built-in
GITHUB_TOKEN. Permissions are scoped via
permissions: contents: write at the workflow
level, so it can create / delete releases on its own
repository.
Global flags
| Flag | Effect |
|---|---|
--console |
Attach the parent console (or allocate a new one). Works on packaged launchers too, useful when a windowed game needs an emergency stdout. |
--help / -h |
Print the usage banner. |