Skip to content

rib3ye/RoInit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

RoInit

Scaffold a production-grade Roblox project in one command — even on a brand-new machine.

roinit "My Game" --yes

That's it. With --yes, RoInit will:

  1. Lay down the project skeleton (Rojo + Wally + Selene + StyLua + Lune + Order + CI + Cursor config).
  2. git init + initial commit.
  3. Install Rokit for you if it's missing (official installer).
  4. Run rokit install to fetch the pinned toolchain (rojo, wally, selene, stylua, lune).
  5. Run wally install to fetch packages (Promise, Signal, Janitor, ProfileStore, Jest).
  6. Open the new project in Cursor if the cursor CLI is on $PATH.

Drop the --yes and RoInit prompts before each step — same flow, more controlled.

Fresh machine? You only need bash + curl. RoInit will install everything else.


Why RoInit exists

Every new Roblox project starts with the same boring 90 minutes:

  1. Google "Rojo vs Argon vs Lync" and pick one.
  2. Find out about Rokit, install it, pin Rojo to a version that won't bit-rot.
  3. Discover Wally exists. Set up Packages/, ServerPackages/, DevPackages/.
  4. Decide on a folder layout. Get it wrong. Refactor it twice.
  5. Realize you need a module loader. Evaluate Knit vs Flamework vs rolling your own require-all script. Roll your own. Regret it six weeks in.
  6. Add Selene. Add StyLua. Fight selene.toml until shared(...) stops tripping undefined_variable.
  7. Write a default.project.json that mounts everything correctly. Find the one missing comma at 2am.
  8. Set up GitHub Actions. Watch the first three runs fail because Wally wasn't installed before Selene ran.
  9. Configure Luau-LSP, point it at your sourcemap, set up StyLua format-on-save.
  10. Finally write some game code.

RoInit collapses all of that into one command. Every decision has been made for you by people who ship Roblox games, and every choice is the community-canonical one — not a custom snowflake you'll have to maintain.


What you get

my-game/
├── default.project.json            # Rojo mount tree, ready to serve
├── rokit.toml                      # pinned tool versions
├── wally.toml                      # Promise, Signal, Janitor, Jest, ProfileStore
├── selene.toml + my-game.yml       # linter + custom std for `shared(...)`
├── stylua.toml                     # opinionated formatter config
├── .github/workflows/ci.yml        # Selene + StyLua + Lune + rojo build smoke
├── AGENTS.md                       # AI agent context (Cursor / Claude Code)
├── .cursor/
│   ├── rules/                      # auto-applied rules: Order conventions + Luau gotchas
│   └── mcp.json                    # opt-in template for the Roblox Studio MCP
├── .vscode/
│   ├── settings.json               # luau-lsp wired to Rojo, format-on-save, lint-on-save
│   └── extensions.json             # recommends luau-lsp, stylua, selene
├── .gitignore
├── README.md
├── src/
│   ├── first/                      # ReplicatedFirst splash (pre-Order)
│   ├── framework/                  # Order framework, in-tree
│   ├── client/core/{tasks,lib}/    # auto-discovered by Order on the client
│   ├── server/core/{tasks,lib}/    # auto-discovered by Order on the server
│   └── shared/core/{tasks,lib}/    # auto-discovered by Order everywhere
├── tools/
│   ├── build.luau                  # `lune run tools/build`  → .rbxlx
│   └── test.luau                   # `lune run tools/test`   → spec runner
└── tests/
    └── example.spec.luau           # Jest-Lua sample

Optimized for Cursor

RoInit treats Cursor as the default editor and ships configuration so the Cursor agent is productive on day one:

AGENTS.md at the project root

A concise, opinionated summary of the stack, project layout, Order framework, common commands, and hard rules for any code an agent writes. Cursor (and Claude Code, and any other agent that reads AGENTS.md) gets the right context the moment you open the repo.

.cursor/rules/

Three Markdown rules ship pre-installed:

  • order-conventions.mdc (alwaysApply: true) — explains where code goes (tasks/ vs lib/ vs external/), the :Prep()/:Init() lifecycle, the shared(...) global, multi-place routing, and hard security rules. Loaded into every Cursor request automatically.
  • luau-no-chained-assignment.mdc — the myLabel = new("Frame", {...}).Parent = parent parse error that no LLM catches by default.
  • luau-local-register-limit.mdc — the 200-locals-per-function cap that silently kills big UI scripts, with the IIFE and table-collapse escape hatches.

.vscode/settings.json (Cursor reads VS Code config)

  • luau-lsp.sourcemap.enabled + autogenerate → live type info from the Rojo tree, no manual rojo sourcemap runs.
  • luau-lsp.types.roblox: true → full Roblox API typings.
  • luau-lsp.fflags.enableNewSolver: true → the new Luau type solver.
  • editor.formatOnSave for .luau via StyLua.
  • selene.run: onSave for inline lint diagnostics.
  • 100-column rulers, 4-space tabs, Packages/ excluded from search.

.vscode/extensions.json

Recommends luau-lsp, stylua, and selene — Cursor pops a one-click "Install All" notification on first open.

.cursor/mcp.json (opt-in)

Template for wiring the Roblox Studio MCP so Cursor's agent can talk directly to a running Studio session — read selections, run commands, edit instances. Uncomment and point command at your StudioMCP binary.

cursor . auto-open

If the cursor CLI is on $PATH, RoInit offers to open the new project in Cursor when scaffolding finishes. With --yes it just does it.


The toolchain, and why each piece is in here

RoInit isn't a custom framework — it's a curated bundle of the best-in-class community tools, glued together so they work the moment you clone.

Rokit — toolchain pin

rokit.toml declares exact versions of every CLI tool. rokit install makes your laptop and CI use the same Rojo, Selene, StyLua, Wally, and Lune binaries forever. No more "works on my machine because I'm on Rojo 7.4 and you're on 7.6". This is the single biggest time-saver on the list. RoInit installs Rokit for you on a fresh machine via the official installer script.

Rojo — source ↔ Studio sync

The de-facto standard for treating .luau files on disk as the source of truth, with live two-way sync into Roblox Studio. Means your code lives in git, your editor of choice (Cursor / VS Code / Zed), and your usual diff tools.

Wally — package manager

Cargo for Roblox. Pull in Promise, Signal, Janitor, ProfileStore, Jest-Lua — and version-pin them in wally.toml. RoInit ships with a sensible default set; add or remove freely.

Selene — linter

Roblox-aware Luau linter. Catches undefined variables, shadowing, and dozens of footguns before you rojo serve. Pre-configured to understand Roblox APIs and the shared(...) global that Order installs.

StyLua — formatter

gofmt for Lua. End style debates forever. CI runs stylua --check src/ on every push so nothing un-formatted hits main.

Lune — standalone Luau runtime

Run Luau scripts outside Roblox — no Node.js, no Python, no Bash gymnastics. RoInit uses Lune for tools/build.luau (build a .rbxlx) and tools/test.luau (discover and run Jest specs). Once you have Lune, you'll write everything else in it too.

Order — module loader + lifecycle

The piece every Roblox project ends up needing and most reinvent. Order:

  • Auto-discovers every module under tasks/ folders and runs them on boot. No more 80-line init.server.luau with require(...) after require(...).
  • Gives every task an optional :Prep() (sync) and :Init() (async) lifecycle hook with Priority ordering.
  • Installs a global shared(...) callable so any module can grab any other by short name — shared("Profile") finds src/server/core/lib/Profile.luau no matter where it's required from.
  • Multi-place support built in. Map PlaceIdPlaceType in framework/Settings.luau, then add code-group folders next to core/ (lobby/, arena/, tutorial/). Each place loads only the groups it needs. Ship a lobby + game from one repo without if game.PlaceId == ... branches scattered across your codebase.

RoInit ships a minimal in-tree implementation of Order so you have zero external dependencies on day one. When the upstream Wally package lands, swap the mount in default.project.json and delete src/framework/.

Jest-Lua — tests

Real describe / it / expect for Luau. Specs live in tests/*.spec.luau and are discovered by lune run tools/test.

ProfileStore — saves

Pre-wired in wally.toml (server realm). The community-canonical wrapper around DataStoreService — handles session locking, the auto-save loop, and graceful shutdowns. You should not be writing to DataStoreService directly in 2026.


Install

One-liner (recommended)

curl -fsSL https://raw.githubusercontent.com/rib3ye/RoInit/main/roinit \
  -o /usr/local/bin/roinit && chmod +x /usr/local/bin/roinit

Then anywhere:

roinit "My Game" --yes      # totally fresh machine? this is all you need.

Or clone

git clone https://github.com/rib3ye/RoInit ~/code/RoInit
ln -s ~/code/RoInit/roinit /usr/local/bin/roinit

Or just download the script

roinit is a single self-contained Bash script. Drop it anywhere on your $PATH. The script itself only needs bash, curl, and standard Unix tools (sed, awk, tr); git is optional. Everything else (Rokit, Rojo, Wally, Selene, StyLua, Lune) is installed for you.


Usage

roinit <project-name> [--author <name>] [--dir <path>]
                      [--yes] [--skip-install] [--no-git] [--no-cursor]
Flag Behavior
--yes, -y Assume yes to every prompt. Required for non-interactive use (CI, piped scripts).
--skip-install Scaffold files only. Don't install Rokit, don't run rokit/wally install.
--no-git Skip git init + initial commit.
--no-cursor Don't auto-open in Cursor even if the CLI is on $PATH.
--author <name> Author baked into wally.toml and the README (default "You").
--dir <path> Target directory (default ./<slug> in CWD). Refuses to overwrite a non-empty directory.

Examples

# Fresh machine, fully automated:
roinit "My Game" --yes

# Interactive — RoInit prompts before installing tools / opening Cursor:
roinit "My Game"

# Just lay down files, decide what to install yourself:
roinit "My Game" --skip-install --no-cursor

# Customize for a studio:
roinit "Lobby" --author "Acme Studios" --dir ~/code/acme-lobby

What happens on a fresh machine

$ roinit "My Game" --yes
Scaffolding My Game
  slug:    my-game
  pascal:  MyGame
  author:  You (you)
  target:  /Users/.../my-game

Initialized git repo with initial commit.

Environment check:
  ✓ git   git version 2.50.1
  ✗ rokit (not installed — needed for the pinned toolchain)
  · wally (not installed — `rokit install` will install it)
  · rojo  (not installed — `rokit install` will install it)
  · lune  (not installed — `rokit install` will install it)
  ✓ cursor (CLI on PATH)

Rokit isn't installed. It's the toolchain manager that pins your
rojo / wally / selene / stylua / lune versions to what's in rokit.toml.

  ? Install Rokit now via the official installer? [Y/n] (auto-yes)

Installing Rokit (the toolchain manager)…
  ✓ Rokit installed: rokit 1.2.0

  ? Run `rokit install` to fetch the pinned toolchain? [Y/n] (auto-yes)
Running: rokit install
  ? Run `wally install` to fetch packages? [Y/n] (auto-yes)
Running: wally install
[INFO ] Downloaded 52 packages!
  ? Open my-game in Cursor now? [Y/n] (auto-yes)
Opening Cursor…

✓ Done. Project is at: /Users/.../my-game

You're now in Cursor with Luau-LSP wired up, ready to rojo serve.


After scaffolding

cd my-game
rojo serve default.project.json     # then Connect from the Rojo Studio plugin

# in other terminals as you work:
selene src/                         # lint
stylua src/                         # format
lune run tools/test                 # discover + run Jest specs
lune run tools/build                # build a .rbxlx without serving

Adding code

  • Tasks (auto-loaded on boot): drop a module under src/<context>/core/tasks/. Optional :Prep() (sync) and :Init() (async) hooks fire in Priority order. See src/framework/Settings.luau for the lifecycle schema.
  • Libraries (loaded on demand): drop a module under src/<context>/core/lib/. Pull it from anywhere with local M = shared("ModuleName").
  • Multi-place: map PlaceId → place type in Settings.PlaceTypes, then add code-group folders (game/, lobby/, ...) next to core/.

What's intentionally NOT in here

  • A UI framework. Fusion, Vide, and React-Lua all have strong cases. Pick one and add it: wally add elttob/fusion@^0.3 (etc.).
  • A networking layer. Zap, ByteNet, and BridgeNet2 all want different trade-offs. Add the one you want — RoInit doesn't pre-commit you.
  • An ECS. Matter is great but highly opinionated; it belongs in projects that need it, not in every project.
  • Game code. RoInit is scaffolding only. Zero gameplay assumptions.

FAQ

I already have Rokit / Rojo / Wally installed. Will RoInit re-install them?

No. roinit detects existing tools and skips install steps for anything already on $PATH. With --yes it runs rokit install (idempotent — pulls only what's missing) and wally install.

Will this work on Windows?

The script is Bash. WSL works fine. Native Windows + Git Bash should work but isn't tested — PRs welcome. On Windows-native, you can also install Rokit manually via the PowerShell installer and run roinit ... --skip-install.

Can I use RoInit without Cursor?

Yes. .cursor/rules/ and AGENTS.md are harmless to other tools. .vscode/ settings work in plain VS Code. Pass --no-cursor to skip the auto-open prompt.

Can I customize what gets scaffolded?

roinit is a single ~900-line Bash script. Fork it, edit the heredocs, done. There's no plugin system because Bash heredocs are the plugin system.

Why Order instead of Knit / Flamework / Sapphire?

Order is the smallest framework that covers the 90% case (auto-discovery, lifecycle, place-type routing) without imposing a service/controller metaphor. The in-tree implementation is ~340 lines of Luau — small enough to read in one sitting and patch when you need to.

Why bundle ProfileStore but not, say, Trove or DataLink?

ProfileStore solves the universal problem (player saves) and has no real competitor in 2026. Trove/Janitor are duplicates — RoInit ships Janitor because it's better-typed; swap to Trove in wally.toml if you prefer.

Can I publish to Wally with this layout?

Yes — the wally.toml [package] block is filled in with your slug and author. Edit version and description, then wally publish.


License

MIT. See LICENSE.

Contributing

Issues and PRs welcome. The bar is: does this make a brand-new Roblox project better, faster, or more reproducible without locking the user into RoInit?

If yes, ship it.

About

Scaffold a production-grade Roblox project in one command — Rojo + Rokit + Wally + Selene + StyLua + Lune + Order, pinned and ready to go.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages