Skip to content

CLI Commands

The DownDraft CLI is available via bun run packages/cli/src/index.ts <command> or as draft if installed globally.

FlagDescription
--help, -hShow help for a command (draft <cmd> --help or draft help <cmd>)
--version, -VPrint the CLI version and exit

Run draft with no arguments to see the top-level command list.

Scaffolds a new game project with directory structure, a src/native-entry.ts entry point, and downdraft.config.json.

FlagDescription
--template=<name>Project template: minimal / physics / full / gamemodule (default: minimal)
--name=<n>Project name (defaults to directory basename)
--description=<d>Project description
--author=<a>Author name
--version=<v>Initial version (default: 0.1.0)
--ai-companionScaffold .devin/ config + engine-prompt.md
--forceScaffold into a non-empty directory
--list-templatesList available templates and exit
Terminal window
draft new my-game
draft new my-game --template=physics --ai-companion
draft new --list-templates

Starts the game on the native runtime — a JS-runtime process hosting a winit window and the wgpu device, with an embedded Vite dev shell providing tiered HMR. Run from a game directory (the game is inferred by walking up from cwd looking for downdraft.config.json or src/native-entry.ts).

FlagDescription
--entry <path>Game entrypoint file (defaults to src/native-entry.ts)
--port <n>MCP HTTP port (default: auto-assign)
--runtime <r>JS runtime hosting the dev shell: bun / node / deno (default: auto-detect)
--watchBack-compat no-op — the dev shell always watches
--no-hmrDisable HMR — spawn the entry directly (bun run), no dev shell
--verbose, -vVerbose logging

Note: Each game boots from its own src/native-entry.ts. draft dev infers the game from the current directory; there is no root dispatcher or DOWNDRAFT_GAME env var.

Runs the engine in debug mode with profiling, debug draw, and visualization tools.

FlagDescription
--verbose, -vVerbose logging
--no-devtoolsDisable devtools overlay (sets DOWNDRAFT_DISABLE_DEVTOOLS=1)
--inspectorEnable Node inspector (chrome://inspect)

Unified build + package pipeline. Replaces the separate build, dist, export, mobile, and build-games commands (which remain as deprecated backward-compat aliases).

FlagDescription
--game <name>, -gGame to release (name or directory; resolves games/<name> when run from the engine root). For multiple games, use --games.
--games=<csv>Comma-separated game names (e.g. sandjongg,to-the-ocean)
--target <t>, -tTarget: win / linux / mac / android / all (default: all). android can’t mix with desktop targets in one invocation.
--runtime=<r>JS runtime embedded in the package: bun (compiled binary) / node / deno / all (default: build.runtime in package.json, else bun). node/deno are linux-only today — win/mac targets still embed bun.
--format=<csv>Linux package formats: dir / deb / appimage / flatpak (default: build.linux.target in package.json, else dir).
--stage=<s>Stage: build / package / release (all compile the same native binary; default: release)
--mode=<m>Build mode: dev / debug / prod (default: prod)
--abi=<csv>Android ABI(s): arm64-v8a / x86_64 / all (android target only)
--min-sdk=<n>Android minimum SDK (default: 30; android target only)
--node-flavor=<f>Embedded Node build flavor: full (default) / lite — lite drops intl/inspector/sqlite/amaro (android target only)
--libs=<csv>Force-include optional engine cdylibs, comma-separated (android target only)
--exclude-libs=<csv>Exclude optional engine cdylibs, comma-separated (android target only)
--no-stripKeep debug info/symtab in packaged .sos (android target only)
--out=<dir>Artifact output directory (default: release)
--skip-buildAlias for --stage=package
--build-onlyAlias for --stage=build
--mcpRetain the MCP automation endpoint in the packaged binary (stripped by default)
--verbose, -vVerbose logging
Terminal window
# Full release for all desktop platforms
draft release --game=my-game
# Build stage only (compile the native binary)
draft release --game=my-game --stage=build
# Specific targets
draft release --game=my-game --target=win,linux
# Linux .deb + AppImage + Flatpak for every supported JS runtime
draft release --game=my-game --target=linux --runtime=all --format=deb,appimage,flatpak
# Android APK (native winit+wgpu shell with embedded libnode)
draft release --game=my-game --target=android
# Multiple games at once
draft release --games=sandjongg,to-the-ocean --target=all
  • win / mac — a standalone Bun binary (<out>/<game>-<target>) via packages/cli/scripts/package-native.mjs, plus a sibling native/ cdylib and dd-assets/ staging tree. Windows binaries get version-info/icon resource stamping via resedit.
  • linux — packages/cli/scripts/package-desktop.mjs, an electron-builder-style packager. The appdir layout is identical across runtimes (<exe> + bundle/ + native/ + dd-assets/, all anchored on dirname(process.execPath)); each --format then wraps it:
    • dir — the staged appdir verbatim
    • deb — dpkg-deb package (/usr/lib/<exe> + /usr/bin symlink + desktop file + hicolor icons)
    • appimage — AppDir + appimagetool (auto-downloaded to ~/.cache/downdraft/tools/; works without FUSE via APPIMAGE_EXTRACT_AND_RUN)
    • flatpak — generated manifest + flatpak-builder + build-bundle
  • android — packages/cli/scripts/package-mobile.mjs assembles the APK directly (no Gradle): bundle + workers emitted as .mjs, embedded libnode, lib/<abi>/ engine + game .sos, .node addons staged under the bundle’s native/ dir.

See the Packaging & Distribution guide for the build block in package.json, the runtime matrix, and native-module staging.

Deprecated commands (backward-compat aliases)

Section titled “Deprecated commands (backward-compat aliases)”

The old commands still work but emit a deprecation warning and delegate to release:

Old commandEquivalent
draft builddraft release --stage=build
draft distdraft release --stage=package
draft exportdraft release --stage=package
draft build-gamesdraft release --games=<csv> --target=<csv>

draft assets <command> [project] [options]

Section titled “draft assets <command> [project] [options]”

Manages remote asset packs (pull, push, list, init, add, add-store).

SubcommandDescription
init [project]Create an empty downdraft.assets.json manifest
add-store <name> [project]Add a blob store backend to the manifest
add <pack> [project]Add an asset pack to the manifest
pull [project]Download all manifest packs to local cache
push [project]Upload local assets/ dir to configured store
list [project]Show manifest packs and local cache status

add-store flags:

FlagDescription
--bucket=<name>S3 bucket name (required)
--endpoint=<url>S3-compatible endpoint URL
--region=<r>AWS region (default: us-east-1)
--path-styleUse path-style addressing

add flags:

FlagDescription
--version=<v>Pack version (default: 1.0.0)
--store=<name>Store name — must exist in manifest (required)
--path=<p>Remote path prefix (defaults to pack name)

push flags:

FlagDescription
--pack=<name>Pack name to push (defaults to first pack)
--store=<name>Store name to push to
--path=<p>Remote path prefix override

Global flags: --verbose, -v — verbose logging.

Runs e2e tests via bun:test. Sets DOWNDRAFT_DETERMINISTIC=1 (fixed seed, paused render loop, no autosave) and DOWNDRAFT_GPU=swiftshader by default, then spawns bun test <spec>. The default smoke specs (tests/e2e/<game>-smoke.spec.ts) use the in-game MCP RPC harness to drive the game, but --spec can point at any bun:test file — the MCP harness is not required.

FlagDescription
--game <name>, -gGame to test (required; resolves games/<name> from the engine root, or the game dir from cwd)
--spec <path>, -sSpec file to run (default: tests/e2e/<game>-smoke.spec.ts)
--port <n>, -pMCP port (0 = auto-assign a free port; default: 0)
--renderer <r>, -rWebGPU backend: cpu (SwiftShader) / gpu (hardware) (default: cpu)
--no-deterministicDisable fixed seed / render loop pause
--headedShow the window instead of running headless
--verbose, -vVerbose logging
Terminal window
# Default: SwiftShader + deterministic
draft test --game=my-game
# Hardware GPU
draft test --game=my-game --renderer=gpu
# Headed (show window)
draft test --game=my-game --headed
VariableValuePurpose
MCP_PORT<port>MCP HTTP transport port
MCP_TIMEOUT_MS120000MCP proxy IPC round-trip timeout (ms)
DOWNDRAFT_GPUswiftshader | hardwareWebGPU backend selection
DOWNDRAFT_DETERMINISTIC1Fixed seed, paused render loop, no autosave
DOWNDRAFT_HEADED1Show the window in deterministic mode
VariableUsed byPurpose
AWS_ACCESS_KEY_IDassetsS3 credentials fallback (manifest config wins)
AWS_SECRET_ACCESS_KEYassetsS3 credentials fallback
DISPLAYtestWhen absent, wraps in xvfb-run
DOWNDRAFT_STRICTall commands1 → hard-error on unknown flags (warns otherwise)
VariableUsed byPurpose
DOWNDRAFT_STRICTpackages/engine/core/src/module/diagnostics.ts0/1 force-disable/enable module DI validation (else = Vite dev mode)
DOWNDRAFT_MCPpackages/engine/core/src/util/logger.ts1 routes logs to stderr (keeps stdout clean for MCP JSON-RPC)
DOWNDRAFT_DISABLE_DEVTOOLSpackages/cli/src/debug.ts1 suppresses devtools auto-open (set by draft debug --no-devtools)
Terminal window
# Scaffold a new game
draft new my-game
draft new my-game --template=physics --ai-companion
# Run in dev mode (from the game directory)
cd my-game && draft dev
# Build for production
draft release --game=my-game --target=win
draft release --game=my-game --target=all --out=release
# Run e2e tests
draft test --game=my-game
draft test --game=my-game --renderer=gpu --headed
# Manage assets
draft assets init
draft assets add-store s3 --bucket=my-bucket --region=us-east-1
draft assets add textures --store=s3 --version=1.0.0
draft assets push
draft assets list
# Manage game plugins/mods (runtime extensions under <game>/plugins/)
draft plugin list # list discovered plugin.json/mod.json
draft plugin new my-plugin --game=my-game # scaffold plugin.json (worker-js)
draft plugin new my-plugin --game=my-game --format=wasm
draft mod new my-mod --game=my-game # alias — scaffolds mod.json (asset)