WowCube Boilerplate
This skill is the infrastructure & packaging component of the
cube_orchestratorpipeline. The orchestrator invokes it (via the Skill tool) at two points: the infra gate before Stage 4 (scaffoldapp_<game>/and prove the simulator builds and launches) and Stage 5 after implementation (build the ARM device target and verify the cube.octembeds the code). It is not a user-facing entry point — the user enters throughcube_orchestrator, which routes here.
Prepare a clean, verified build environment for one WowCube game so that the orchestrator's very first coding prompt lands in a project that already compiles, packs, and runs in the simulator. Catching a broken toolchain here — before any game code exists — is far cheaper than discovering it three prompts into implementation, where it's tangled up with new code.
This component is driven by the master orchestrator, which routes to it at two points in the pipeline:
cube_orchestrator (master controller)
├─ Stage 1: cube_game-designer → GDD
├─ Stage 2: technical_prompter → prompts.md + asset manifest
├─ Stage 3: cube_asset-builder → packed assets
├─ infra gate: wowcube-boilerplate → verified app_<game>/ ← THIS SKILL
├─ Stage 4: coder/verifier/fixer → game code (sim-tested)
└─ Stage 5: wowcube-boilerplate → verified cube .oct ← THIS SKILL
Cross-platform: one pack path, two build toolchains
Asset packing is pure Python on every OS. The art pipeline runs through the
canonical packer scripts/pack.py (PSD→PNG export, BMFont .fnt export,
palette build, packed .raw/.pal emission, kind-aware _ids.h generation,
and — with --beta-app-dir/--app-name — the full beta container: index.bin
plus art/packed/*.raw+*.pal and the launcher icon maps written straight
into the app folder) — it needs only Pillow, numpy, pytoshop,
psd-tools. There is no psd.exe, utils.exe, !pack.bat, or Wine
anywhere in this flow; those legacy Windows binaries are fully replaced.
Sound assets are synthesized WAVs encoded to beta mp3 (22050 Hz mono CBR 32k,
requires ffmpeg) by scripts/gen_sounds.py, copied into
app_<game>/sound/assets/ by the pack step — sounds never ship as WAV.
Only the simulator/device build differs by OS, and this skill ships both:
| Step | Windows | Linux |
|---|---|---|
| Scaffold + pack + sim build | scripts/new_app.ps1 |
scripts/new_app.sh |
| Toolchain check/install | scripts/check_env.ps1 |
scripts/check_env.sh |
Cube .oct device build |
scripts/build_device.ps1 |
scripts/build_device.sh |
| Simulator binary produced | bin/app_<game>.exe (MSVC) |
app_<game>/build-sim/octavios_sim (CMake + SDL3) |
Pick the script matching the host OS. The .ps1 and .sh variants are
behavioural mirrors; the steps below describe both.
When to Use
cube_orchestratorrouted here for the infra gate before Stage 4: prompts and assets exist, butapp_<game>/is missing (no*.target, noart/packed/*.rawANDindex.bin) or the simulator won't build/launchcube_orchestratorrouted here for Stage 5: all prompts are implemented and there is no verified device.octyet (or it was last touched by a sim run)
Do not trigger this skill directly for "scaffold the app" / "prepare the infra" /
"build for the cube" requests — those are owned by cube_orchestrator, which
routes here at the two points above.
When NOT to Use
- No prompts/assets yet — the orchestrator will route to the earlier stages first
- Infra already verified and not at delivery time — the orchestrator proceeds to Stage 4 (implementation)
The infra gate verifies the simulator build (fast iteration). Producing
the cube-loadable .oct (the ARM device build) is the separate Stage 5
step this skill also performs — see "Producing the cube .oct" — run at delivery
time, not as part of the pre-implementation gate.
Expected Workspace Layout
This skill assumes the standard WowCube dev workspace, where the SDK, the skills repo, and each app are siblings under one root:
<workspace>/
├── octavios/ # SDK: engine/, sim/, CMakeLists.txt (Linux), apps/build_sim.cmd (Windows), utils/
├── OCT_wowcube-agent-skills/ # this skills repo (templates/, skills/)
│ ├── scripts/pack.py # the canonical pure-Python packer (all platforms)
│ └── templates/app_ai_template/ # the template app cloned for each game
└── app_<game>/ # created BY this skill
├── app_<game>.target # marker; build scripts derive the app name from it
├── src/ art/ sound/ bin/ build-sim/
If your root differs, pass explicit paths to the script (below).
Core Procedure
The whole flow is bundled as a script — prefer running it over doing the
steps by hand, because the manual path has easy-to-miss gotchas (see Gotchas).
Drive the script (.ps1 on Windows, .sh on Linux); only fall back to manual
steps if the script can't run (e.g. you need to adapt a path it doesn't expose).
Step 0: Verify the toolchain is installed
Before scaffolding, confirm the machine has the tools the build needs. Packing is Python-only on both OSes; the two build targets need:
- Simulator build — Windows: MSVC (Visual Studio C++ tools). Linux:
CMake ≥ 3.13, gcc/g++ ≥ 12, SDL3 (and
bluez/libsystemdfor the BT upload path). - Cube
.octdeliverable additionally needs the ARM device toolchain on both OSes: the ARM GNU embedded compiler (arm-none-eabi-gcc), CMake, and Ninja — because the loadable.octembeds ARM machine code (see "Producing the cube.oct"). - Packer deps (both OSes):
pip install Pillow numpy pytoshop psd-tools.
Run the bundled check, which installs missing tools where it can:
# Windows
powershell -File OCT_wowcube-agent-skills/scripts/check_env.ps1
# Linux
bash OCT_wowcube-agent-skills/scripts/check_env.sh
On Windows the check installs the winget-available tools unattended:
# Simulator build (MSVC C++ build tools + Windows 11 SDK):
winget install --id Microsoft.VisualStudio.2022.BuildTools -e --override `
"--passive --wait --add Microsoft.VisualStudio.Workload.VCTools `
--add Microsoft.VisualStudio.Component.Windows11SDK.22621 --includeRecommended"
# Cube .oct device toolchain:
winget install ARM.GnuArmEmbeddedToolchain # arm-none-eabi-gcc
winget install Kitware.CMake # cmake
winget install Ninja-build.Ninja # ninja
On Linux it checks for cmake, gcc/g++, sdl3, bluez, libsystemd,
arm-none-eabi-gcc, ninja, and the Python packer deps, and prints the distro
install line for whatever's missing (e.g. on Arch:
sudo pacman -S cmake gcc sdl3 bluez bluez-libs systemd-libs arm-none-eabi-gcc ninja).
The Windows MSVC install is a large download (several GB) but runs unattended
(--passive); pass -SkipMsvc if it's being installed separately. Note: a
freshly installed tool may not appear on PATH until a new shell is opened.
new_app.* runs this check automatically as its first step (skip with
-SkipEnvCheck / --skip-env-check).
Step 1: Determine the app name
The orchestrator and prompts use a <game> name (e.g. tetris). The app folder
is always app_<game>. If the name isn't already established by the GDD/prompts
filenames, ask the user — this name is baked into the marker file, headers,
asset paths, and the output binary, so getting it right now avoids a rename later.
Step 2: Run the scaffold-and-verify script
# Windows
powershell -File OCT_wowcube-agent-skills/scripts/new_app.ps1 `
-Name <game> -Workspace <workspace_root> -Run
# Linux
bash OCT_wowcube-agent-skills/scripts/new_app.sh \
--name <game> --workspace <workspace_root> --run
What it does, in order (this is the procedure distilled from real runs):
- Clone
templates/app_ai_template/→<workspace>/app_<game>/ - Rename the
.targetmarker and theapp_ai_template.h/app_ai_template_ids.hheaders toapp_<game>* - Patch every embedded reference to the template name in
src/app.handsrc/app_<game>.h(the#include,APP_DIR) - Guarantee the full beta define set (
APP_VERSION,APP_TITLE,APP_GUID1,APP_CATEGORIES,APP_COLORS) is present insrc/app.handAPP_GUID1is randomized — the template already ships all six defines, so this is a self-healing backstop (re-adds any that go missing, replaces the template's zero placeholder GUID) rather than a required repair; see Gotchas - Pack art assets with the Python packer —
scripts/pack.py --export --build-palette --build-ids --beta-app-dir <AppDir> --app-name app_<game>(plus--icon art/icon.pngwhen present) — which writes the legacyart/packed/*.pngintermediates AND the beta container (app_<game>/index.bin,art/packed/*.raw+*.pal, launcher icon maps) directly into the app folder, and emitsapp_<game>_ids.hstraight intosrc/(--ids-output) so compiled sprite indices match the packed assets - Build the simulator — Windows:
octavios/apps/build_sim.cmd(MSVC/PC target). Linux:cmake -S <octavios> -B app_<game>/build-sim -DAPP_DIR=app_<game>thencmake --build app_<game>/build-sim→build-sim/octavios_sim. Never the ARM build here. - With
-Run/--run: launch the simulator for a few seconds and confirm it stays alive (a crash here usually means assets weren't packed)
Exit code 0 means infrastructure verified. A non-zero exit names the failed
step — fix it (or report it) before handing off to the orchestrator.
Useful switches (both variants): scaffold+pack only (-SkipBuild /
--skip-build), override the template or build-script locations
(-Template/--template, -BuildScript/--build-script).
Step 3: Report readiness and hand off
On success, report to the user what was verified and where:
app_<game>/app_<game>.target(marker)app_<game>/src/app_<game>.h— the file the orchestrator will write game code intoapp_<game>/art/packed/*.raw+*.pal(packed assets) andapp_<game>/index.bin(the beta asset index — the gate that actually matters: both*.rawANDindex.binmust exist, not*.rawalone)- The built simulator that launches without crashing —
app_<game>/bin/app_<game>.exe(Windows) orapp_<game>/build-sim/octavios_sim(Linux)
Then return control to cube_orchestrator, which checkpoints with the user
and runs its first Stage 4 prompt against the known-good project. The orchestrator
overwrites src/app_<game>.h with the game skeleton on its first prompt — that's
expected; this skill's job is to prove the surrounding infra (folder, marker,
assets, toolchain) works, not to preserve the demo game.
Producing the cube .oct (Stage 5)
The simulator binary is for PC testing. The file you actually load onto the
physical WowCube is app_<game>/app_<game>.oct. It is assembled by the
simulator at launch, which packs the art .raw assets + the sounds +
(if present) the ARM device code from app_<game>/out/app_<game>.bin.
The footgun: the engine treats the ARM
.binas optional when packing (seeoctavios/sim/src/sim.h: "it's optional because app running in simulator has own code"). So if you just build and run the simulator, the sim still writes anapp_<game>.oct— but it contains assets only, no ARM code. That package runs fine on the PC (the sim executes its own compiled code) and is completely dead on the cube. Worse, the sim rewrites the.octas asset-only on every launch, so a good package gets clobbered if you run the sim again afterward. This is why "it works in the sim" is never proof the cube build is done.
The cube .oct therefore must be produced by the ARM build, and the device
build must be the last step — after any simulator testing. Both steps are
bundled in build_device.ps1 (Windows) / build_device.sh (Linux):
# Windows
powershell -File OCT_wowcube-agent-skills/scripts/build_device.ps1 -AppDir <workspace>/app_<game>
# Linux
bash OCT_wowcube-agent-skills/scripts/build_device.sh --app-dir <workspace>/app_<game>
- ARM device build →
app_<game>/out/app_<game>.bin(cmake -G Ninja -S octavios/apps -B outthencmake --build out, run from the app folder — this is the ARM target fromoctavios/apps/CMakeLists.txt, needing the Step 0 device toolchain). Fails if the.binis missing or empty. - Pack → launches the sim once, which writes
app_<game>/app_<game>.oct(assets + sounds + ARM code). - Verify → confirms the ARM code is actually embedded in the
.oct(the final.bin-sized bytes of the pack must match the.binbyte-for-byte). It fails loudly on an asset-only pack, so you can never ship a.octthat would silently die on the cube. - Warn → both
build_device.ps1andbuild_device.shflag any stray timestamped<app>_*.octfiles sitting in the app root. The Linux simulator deletes root.octfiles at startup, so a leftoverapp_<game>_20250101.octcan silently vanish the next time anyone runs the sim (Linux or Windows dev machine, doesn't matter where it was created) — this is a warning only, not a failure, but keep deliverables namedapp_<game>.oct(no timestamp suffix) to avoid losing them.
This is the final deliverable the orchestrator runs (mandatorily) as Stage 5
on completion. After it exits 0, return control to cube_orchestrator, which
checkpoints with the user and reports the absolute path to the verified .oct.
Gotchas (why the script exists)
These are the failures observed when doing this by hand. The scripts handle them; if you ever scaffold manually, watch for them.
app.hmust defineAPP_VERSIONitself. The simulator'ssim.honly defines a fallbackAPP_VERSIONwhenSIM_APP_HEADERis not set — but the build always sets it, so a hand-rolledapp.hmissing the define dies witherror C2065: 'APP_VERSION': undeclared identifier. The shipped template already includes#define APP_VERSION 100; this is called out because the scaffolder's step 4 guarantee exists specifically to catch it if a manual edit ever drops it, not because the template ships broken.Pack before you build. The packer (
scripts/pack.py --beta-app-dir <AppDir> --app-name app_<game>) must run first soart/packed/*.rawANDindex.binexist — the simulator loads the beta container at launch and a missing pack shows up as an early crash, not a compile error. Judge pack success by both theart/packed/*.rawfiles produced ANDindex.binexisting (and the_ids.hwritten), not by console noise —*.rawalone is not a complete pack.The generated
_ids.hmust be synced intosrc/. The packer writesapp_<game>_ids.h, but the build compiles againstsrc/app_<game>_ids.h. If the packed asset order changes and the fresh ids header isn't copied intosrc/, sprite indices silently mismatch. The scripts pass--ids-output src/app_<game>_ids.hso it lands in the right place directly; if you pack by hand, always sync it.
Also remember: the infra gate uses the simulator build only. The ARM device
build (octavios/apps/CMakeLists.txt, arm-none-eabi-gcc) is reserved for
producing the final cube .oct (build_device.*) — don't run it as part of the
pre-implementation gate, only at delivery.
Manual pack command (both platforms)
If you must pack by hand, run from the app folder:
python <workspace>/OCT_wowcube-agent-skills/scripts/pack.py \
--export --build-palette --build-ids \
--art-dir art --exported-dir art/exported \
--packed-dir art/packed --output-dir art/packed \
--ids-output src/app_<game>_ids.h --assets assets \
--beta-app-dir . --app-name app_<game> --icon art/icon.png
This is exactly what new_app.ps1/new_app.sh run (from inside the app
folder, so --beta-app-dir .) — drop --icon art/icon.png if the app has no
launcher icon yet. It exports art/assets.psd and the *.fnt fonts to PNGs,
builds the palette, writes the legacy art/packed/*.png intermediates,
generates src/app_<game>_ids.h, and — because --beta-app-dir is set — also
emits the beta container the simulator/.oct actually load: index.bin,
art/packed/*.raw+*.pal, and the launcher icon maps. --emit-raw (with
--raw-dir) is legacy-optional: it writes legacy-format .raw files that only
the pre-beta Linux sim reads — the beta sim never does — and if combined with
--beta-app-dir into the same folder, the beta .raw files overwrite the
legacy ones. Add --manifest plans/<game>_assets.json if any sprite uses color: "full" (so it gets
RAW565-encoded instead of run through the palette codec) or carries flags
like fullsize (so they reach the packed palette-sprite header). Identical on Windows
and Linux — Python only, no .exe/.bat/Wine.
Packing plain-PNG palette sprites by hand (no PSD): drop --export, keep
--build-palette. --export exists solely to regenerate the exported dir
from PSD/FNT sources in --art-dir — as its first act it DELETES every
pre-placed PNG in --exported-dir (it warns loudly and the pack then fails
with exit 1, but the PNGs are already gone). If your sprites are plain PNGs
(the AI-asset workflow — nothing was ever authored in Photoshop), put one
<name>.png per sprite in --exported-dir and run the same command WITHOUT
--export:
python <workspace>/OCT_wowcube-agent-skills/scripts/pack.py \
--build-palette --build-ids \
--art-dir art --exported-dir art/exported \
--packed-dir art/packed --output-dir art/packed \
--ids-output src/app_<game>_ids.h --assets assets \
--beta-app-dir . --app-name app_<game> \
--manifest plans/<game>_assets.json --icon art/icon.png
(That said, the normal PNG route is not this manual command at all — it is
build_pipeline.py pack, which takes the PNGs from <workspace>/art/,
atlases them into a throwaway PSD internally, and runs pack.py itself; see
cube_asset-builder Step 4.)