diff --git a/README.md b/README.md
index fa1868b..ff10ed6 100644
--- a/README.md
+++ b/README.md
@@ -4,22 +4,22 @@ Minimal SDL3 + Vulkan spinning cube demo.
 ## Cheat sheet
 
 ### Dependencies
-- `python scripts/dev_commands.py dependencies` installs the Conan graph in `build`.
+- python scripts/dev_commands.py dependencies installs the Conan graph in uild.
 
 ### Configure & build
-- `python scripts/dev_commands.py configure` runs Ninja with the default `build-ninja` directory and `Release` build type; add `--generator vs` for the Visual Studio generator or `--generator ninja-msvc` when you want the MSVC-aware Ninja folder.
-- `python scripts/dev_commands.py build` targets `build-ninja` by default (change `--build-dir` if you configured elsewhere).
-- `python scripts/dev_commands.py msvc-quick` invokes the VC vars + Ninja one-liner (pass `--bat-path` to point at another Visual Studio layout).
-- Combine `--dry-run` with any subcommand to inspect the alias-driven shell invocation without executing it.
- - `python scripts/dev_commands.py run` launches `spinning_cube` from the configured Ninja output; pass `--build-dir` when you configured elsewhere.
+- python scripts/dev_commands.py configure defaults to Ninja+MSVC on Windows or Ninja on other platforms, writing into the matching uild-ninja-msvc/uild-ninja folder with the Release type; override the generator or build dir with --generator / --build-dir if needed.
+- python scripts/dev_commands.py build runs cmake --build in the same folder (change --build-dir to match a different configure directory).
+- python scripts/dev_commands.py msvc-quick (Windows only) runs the VC vars + Ninja build alias; pass --bat-path to target a different Visual Studio installation.
+- python scripts/dev_commands.py run launches spinning_cube from the configured Ninja output.
+- Prefix any subcommand with --dry-run to print the alias-driven shell command instead of executing it.
 
 ### Run
-- `python scripts/dev_commands.py run [--build-dir ...]` to launch `spinning_cube`; source `build/conanrun.sh` / `build\\conanrun.bat` if Conan exports env vars before running the helper.
+- python scripts/dev_commands.py run [--build-dir ...] (source uild/conanrun.sh / uild\\conanrun.bat first if Conan exports env vars).
 
 ## Runtime configuration
 1. sdl3_app --json-file-in <path> loads JSON configs (script path, window size, lua_debug, etc.).
 2. sdl3_app --create-seed-json config/seed_runtime.json writes a starter file assuming scripts/cube_logic.lua sits beside the binary.
-3. sdl3_app --set-default-json [path] stores or overrides the default runtime JSON; Windows writes %APPDATA%/sdl3cpp, other OSes use $XDG_CONFIG_HOME/sdl3cpp/default_runtime.json (fallback ~/.config/sdl3cpp).
+3. sdl3_app --set-default-json [path] stores or overrides the runtime JSON; Windows writes %APPDATA%/sdl3cpp, other OSes use $XDG_CONFIG_HOME/sdl3cpp/default_runtime.json (fallback ~/.config/sdl3cpp).
 
 ### GUI Demo
 scripts/gui_demo.lua paints the Lua GUI framework on top of the Vulkan scene. Launch it as ./build/sdl3_app --json-file-in config/gui_runtime.json or register that config via sdl3_app --set-default-json.
diff --git a/scripts/dev_commands.py b/scripts/dev_commands.py
index 93bdd71..fe9c695 100644
--- a/scripts/dev_commands.py
+++ b/scripts/dev_commands.py
@@ -3,7 +3,8 @@
 from __future__ import annotations
 
 import argparse
-import shlex
+import os
+import platform
 import subprocess
 
 
@@ -15,6 +16,18 @@ def run_commands(commands: list[str], dry_run: bool) -> None:
         subprocess.run(cmd, shell=True, check=True)
 
 
+IS_WINDOWS = platform.system() == "Windows"
+DEFAULT_GENERATOR = "ninja-msvc" if IS_WINDOWS else "ninja"
+GENERATOR_DEFAULT_DIR = {
+    "vs": "build",
+    "ninja": "build-ninja",
+    "ninja-msvc": "build-ninja-msvc",
+}
+DEFAULT_BUILD_DIR = GENERATOR_DEFAULT_DIR[DEFAULT_GENERATOR]
+SPINNING_BINARY = os.path.join(
+    DEFAULT_BUILD_DIR, "spinning_cube.exe" if IS_WINDOWS else "spinning_cube"
+)
+
 ALIASES = {
     "conan_detect": "conan profile detect",
     "conan_install": (
@@ -26,7 +39,6 @@ ALIASES = {
         'cmd /c "call \\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Auxiliary\\Build\\vcvarsall.bat\\" x64 '
         "&& cmake --build build-ninja-msvc --config Release\""
     ),
-    "spinning_cube": "./build-ninja/spinning_cube",
 }
 
 
@@ -36,9 +48,9 @@ def dependencies(args: argparse.Namespace) -> None:
 
 
 def configure(args: argparse.Namespace) -> None:
-    defaults = {"vs": "build", "ninja": "build-ninja", "ninja-msvc": "build-ninja-msvc"}
-    build_dir = args.build_dir or defaults.get(args.generator, "build")
-    if args.generator == "vs":
+    generator = args.generator or DEFAULT_GENERATOR
+    build_dir = args.build_dir or GENERATOR_DEFAULT_DIR.get(generator, "build")
+    if generator == "vs":
         cmd = f"cmake -B {build_dir} -S ."
     else:
         cmd = (
@@ -56,6 +68,8 @@ def build(args: argparse.Namespace) -> None:
 
 
 def msvc_quick(args: argparse.Namespace) -> None:
+    if not IS_WINDOWS:
+        raise SystemExit("msvc-quick is only supported on Windows")
     if args.bat_path:
         alias = ALIASES["msvc_quick"].replace(
             r'"C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat"',
@@ -70,16 +84,14 @@ def msvc_quick(args: argparse.Namespace) -> None:
 
 
 def run_demo(args: argparse.Namespace) -> None:
-    base = ALIASES["spinning_cube"]
-    if args.build_dir:
-        prefix = args.build_dir.rstrip("\\/")
-        alias = base.replace("./build-ninja", prefix)
-    else:
-        alias = base
+    build_dir = args.build_dir or DEFAULT_BUILD_DIR
+    binary = os.path.join(
+        build_dir, "spinning_cube.exe" if IS_WINDOWS else "spinning_cube"
+    )
     if args.dry_run:
-        print("\n> " + alias)
+        print("\n> " + binary)
     else:
-        subprocess.run(alias, shell=True, check=True)
+        subprocess.run(binary, shell=True, check=True)
 
 
 def main() -> int:
@@ -98,8 +110,9 @@ def main() -> int:
     conf.add_argument(
         "--generator",
         choices=["vs", "ninja", "ninja-msvc"],
-        default="ninja",
-        help="which generator to invoke (default: Ninja)",
+        help=(
+            "which generator to invoke (default: Ninja+MSVC on Windows, Ninja elsewhere)"
+        ),
     )
     conf.add_argument(
         "--build-dir",
@@ -114,7 +127,7 @@ def main() -> int:
 
     build_parser = subparsers.add_parser("build", help="run cmake --build")
     build_parser.add_argument(
-        "--build-dir", default="build-ninja", help="which directory to build"
+        "--build-dir", default=DEFAULT_BUILD_DIR, help="which directory to build"
     )
     build_parser.add_argument(
         "--config", default="Release", help="configuration for multi-config builders"
