- to watch your files and allow to
live reloadyour game. - to
exportyour debug and release bundles. - to
generateyour release icons and screenshots.
To use the CLI you'll need NodeJS installed
Install the dev dependencies from the fox folder:
cd path/to/fox
npm installlink the fox executable:
macOS:
ln -s ~/Projects/uralys/gamedev/fox/cli/cli.js /usr/local/bin/foxWSL/Linux:
sudo ln -s /home/user/Projects/uralys/gamedev/fox/cli/cli.js /usr/local/bin/foxWindows (PowerShell):
Add a function to your PowerShell profile ($PROFILE):
function fox { node "C:\Users\chris\Projects\uralys\gamedev\fox\cli\cli.js" @args }You may have to reload your terminal to have fox in your path;
You can now execute fox commands from your terminal:
foxYou can pass parameters to Godot by using them directly from the command line.
See all available parameters on Godot CLI Reference
Example:
fox run:game --headless --debug-collisionsUsage: fox <command> [options]
Commands:
fox run:editor open Godot Editor with your main scene
fox run:game start your game locally (watch + hot reload)
fox import [--force] import assets headless, as the editor does
when opening the project
fox tag [patch|minor|major] bump version in project.godot and create a
git tag
fox export export a bundle for one of your presets
fox publish [demo] [branch] upload exported builds to Steam via steamcmd
fox switch switch from a bundle to another (writes
override.cfg)
fox generate:icons generate icons, using a base 1200x1200 image
fox generate:splashscreens generate splashscreens, extending a background
color from a centered base image
fox generate:screenshots resize store screenshots to the required sizes
fox generate:steam-screenshots resize screenshots for the Steam store
fox update-po-files run msgmerge on the project .po translation
files (experimental)- more details for exporting here
fox import # incremental: only outdated assets
fox import --force # wipes .godot/imported, reimports everythingfox import runs godot --headless --path . --import, which boots the editor
without its window: same EditorFileSystem scan as opening the project
(autoloads, enabled editor plugins, EditorScenePostImport scripts, per-file
.import settings), then quits once the import is done.
It is incremental, exactly like the editor: an asset whose source and import
settings did not change is not reimported. --force deletes the
.godot/imported cache first, so everything is rebuilt — the .import files
are never touched, your import settings are preserved.
Being headless, there is no RenderingDevice: GPU texture compression falls
back to the CPU compressor (slower, same format) and no filesystem thumbnails
are generated. Godot returns 0 even when an importer logs an error, so read
the output rather than trusting the exit code.
Useful before any headless run (tests, screenshots, export) or after a batch of assets landed on disk, to avoid opening the editor just for that.
fox run:game watches your project files (.gd, .tscn, .cfg, .json, .yml) and hot reloads the current scene when a change is detected.
Instead of killing and restarting the Godot process, it writes a .hot-reload trigger file. The HotReload autoload inside Godot detects this file and calls Router.reloadCurrentScene(), which re-instantiates the current scene without closing the window.
Add HotReload to your project's [autoload] section in project.godot:
[autoload]
HotReload="*res://fox/libs/hot-reload.gd"Add .hot-reload and .nav-state to your .gitignore.
.worktrees/ and .godot/ are always ignored. To skip additional folders (tooling, generated data, sub-projects), list them in run:game.ignored of your fox.config.json:
{
"run:game": {
"ignored": ["solutions"]
}
}The Router persists a typed NavState (fox/core/nav-state.gd) to .nav-state (JSON). It stores the current scene path and a path array representing nested sub-view segments — similar to outlets in Ember.js or React Router.
On hot reload or full restart (r), the app restores the last visited scene and navigates to the exact sub-view.
NavState JSON example:
{"scene_path":"res://src/screens/storybook.tscn","path":["Focal Blur"]}Router API:
Router.getNavPath() -> Array— read the current path segmentsRouter.setNavPath(path: Array)— update and persist the pathRouter.restoreOrDefault(defaultAction: Callable)— restore nav state on startup, or call default
Scene pattern — each scene reads/writes its own path segments:
func onOpen(options = {}):
var navPath = Router.getNavPath()
if not navPath.is_empty():
selectedEntry = navPath[0] # restore from navPath
else:
selectedEntry = __.GetOr('default', 'entry', options) # normal nav
Router.setNavPath([selectedEntry])
func _onSubViewSelected(name: String):
Router.setNavPath([selectedEntry, name]) # nested sub-viewStartup (main.gd) — use restoreOrDefault in debug mode:
func run():
Router.restoreOrDefault(func(): Router.openHome())In standalone mode, GDScript files are compiled on load. Hot reload works well for data changes (JSON, resources) and .tscn scenes. For .gd script changes, the scene is re-instantiated but scripts in memory may not update — use r for a full restart in that case.
r— full restart (kills and relaunches Godot, useful when hot reload is not enough)ctrl + c— stop the game
