Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/brockmartin/roblox-game-skill/llms.txt

Use this file to discover all available pages before exploring further.

The modern Roblox development stack moves work out of Studio and into the filesystem, enabling version control, code review, automated testing, and all the practices expected in professional software engineering. Instead of editing scripts inside a .rbxlx place file, developers write code in VS Code, manage dependencies through a package registry, enforce quality with static analysis, and keep everything in Git. The tools below form the standard external toolchain that the Roblox Game Skill expects and supports.
If you want Claude to build and iterate on your game fully autonomously — creating instances, wiring scripts, playtesting, and fixing errors — see Full Mode MCP instead of setting up the filesystem toolchain manually.

Tool Overview

Rojo

Syncs your filesystem project into Roblox Studio in real time. The foundation of every external workflow.

Wally

The community-standard package manager for Roblox. Installs third-party Luau libraries from a central registry.

Selene

A static analysis linter for Lua and Luau. Catches bugs, unused variables, and API misuse before runtime.

StyLua

An opinionated formatter for Luau, inspired by Prettier. Eliminates formatting debates and diff noise.

Lune

A standalone Luau runtime for running scripts outside Roblox. Ideal for tests, build scripts, and CI.

Aftman

A toolchain manager that installs and pins exact versions of all the above tools. Start here.
ToolPurposeInstall Via
RojoFilesystem ↔ Studio syncAftman / Cargo
WallyPackage managerAftman / Cargo
SeleneLuau linterAftman / Cargo
StyLuaLuau/Lua formatterAftman / Cargo
LuneLuau runtime outside RobloxAftman / Cargo
AftmanToolchain manager (installs all above)Cargo

Aftman — Toolchain Manager

Aftman pins exact versions of every tool in your project, so every developer and every CI run uses identical binaries. Install it once via Cargo, then manage everything else through aftman.toml.
cargo install aftman          # one-time global install
aftman init                   # creates aftman.toml in project root
aftman add rojo-rbx/rojo      # pins a specific version
aftman install                # installs all tools listed in aftman.toml
A typical aftman.toml for a Roblox project:
[tools]
rojo    = "rojo-rbx/rojo@7.4.4"
wally   = "UpliftGames/wally@0.3.2"
selene  = "Kampfkarren/selene@0.27.1"
stylua  = "JohnnyMorganz/StyLua@0.20.0"
lune    = "lune-org/lune@0.8.9"
Commit aftman.toml to Git so every contributor automatically gets the same tool versions.

Rojo — Filesystem Sync

Rojo syncs a filesystem project into Roblox Studio (and back). It is the foundation of all external tooling workflows, enabling you to write code in any editor and have changes appear in Studio instantly.

Installation

# Option A — Aftman (recommended)
aftman add rojo-rbx/rojo

# Option B — Cargo directly
cargo install rojo

File Naming Conventions

Rojo determines the Roblox class of each file by its suffix:
File Name PatternRoblox ClassExample
*.server.luauScriptmain.server.luau
*.client.luauLocalScriptcontroller.client.luau
*.luau (no prefix)ModuleScriptUtils.luau
init.luauFolder → ModuleScriptMyModule/init.luau
init.server.luauFolder → ScriptGameService/init.server.luau
init.client.luauFolder → LocalScriptHUD/init.client.luau
init.meta.jsonSets properties on the folderMyFolder/init.meta.json
*.model.jsonRojo model filepart.model.json
.lua also works, but .luau is the modern standard and enables better LSP support.

project.json Configuration

The default.project.json file maps filesystem directories to Roblox services. Here is a complete example:
{
  "name": "MyGame",
  "tree": {
    "$className": "DataModel",

    "ServerScriptService": {
      "$className": "ServerScriptService",
      "Server": {
        "$path": "src/server"
      }
    },

    "ServerStorage": {
      "$className": "ServerStorage",
      "Storage": {
        "$path": "src/server-storage"
      }
    },

    "ReplicatedStorage": {
      "$className": "ReplicatedStorage",
      "Shared": {
        "$path": "src/shared"
      },
      "Packages": {
        "$path": "Packages"
      }
    },

    "StarterPlayer": {
      "$className": "StarterPlayer",
      "StarterPlayerScripts": {
        "$className": "StarterPlayerScripts",
        "Client": {
          "$path": "src/client"
        }
      },
      "StarterCharacterScripts": {
        "$className": "StarterCharacterScripts",
        "Character": {
          "$path": "src/character"
        }
      }
    },

    "StarterGui": {
      "$className": "StarterGui",
      "UI": {
        "$path": "src/ui"
      }
    },

    "Workspace": {
      "$className": "Workspace",
      "$properties": {
        "FilteringEnabled": true
      }
    },

    "Lighting": {
      "$className": "Lighting",
      "$properties": {
        "Technology": "Future"
      }
    },

    "SoundService": {
      "$className": "SoundService"
    },

    "HttpService": {
      "$className": "HttpService",
      "$properties": {
        "HttpEnabled": true
      }
    }
  }
}

Common Commands

# Initialize a new project
rojo init my-game

# Start the live sync server (default port 34872)
rojo serve

# Serve a specific project file
rojo serve default.project.json

# Build a .rbxlx place file from the project
rojo build -o game.rbxlx

# Build a binary .rbxl place file
rojo build -o game.rbxl

# Build a model file
rojo build -o model.rbxm

# Build with a base place (for non-code assets authored in Studio)
rojo build default.project.json --output game.rbxlx --base base.rbxlx

Two-Way Sync

Rojo supports two-way sync via the Studio plugin. When connected, filesystem changes are pushed into Studio in real time, and script edits made in Studio are synced back to .luau files.
Two-way sync for non-script instances is limited. For level design, most teams commit a base.rbxlx place file to Git and use Rojo to build on top of it.

Wally — Package Manager

Wally is the community-standard package manager for Roblox, maintained by Uplift Games. It mirrors the experience of npm or Cargo: declare dependencies in a manifest, install them with one command, and import them from a local Packages/ directory.

wally.toml Configuration

[package]
name        = "yourname/my-game"
version     = "0.1.0"
registry    = "https://github.com/UpliftGames/wally-index"
realm       = "shared"
license     = "MIT"
description = "My awesome Roblox game"
authors     = ["Your Name <you@example.com>"]

[dependencies]
Promise        = "evaera/promise@4.0.0"
Knit           = "sleitnick/knit@1.6.0"
Signal         = "sleitnick/signal@2.0.0"
Trove          = "sleitnick/trove@1.1.0"
TableUtil      = "sleitnick/table-util@1.2.0"
Timer          = "sleitnick/timer@1.1.0"
Component      = "sleitnick/component@2.4.0"
ProfileService = "madstudioroblox/profileservice@1.2.0"

[server-dependencies]
DataStoreService = "sleitnick/datastore-service@0.1.0"

[dev-dependencies]
TestEZ = "roblox/testez@0.4.1"

Realms

Wally uses realms to control where packages end up in the Roblox DataModel:
RealmDestinationVisible To
sharedReplicatedStorageServer + Client
serverServerScriptServiceServer only
devReplicatedStorageDevelopment only

Installing Packages

# Install all dependencies listed in wally.toml
wally install

# Search for a package
wally search promise

# Authenticate (required to publish)
wally login

# Publish your own package
wally publish
After wally install, packages appear in the Packages/ directory. Your default.project.json maps this directory into ReplicatedStorage (as shown in the Rojo config above). Generate a sourcemap afterward so the Luau LSP can resolve package types:
rojo sourcemap default.project.json -o sourcemap.json
PackageAuthorPurpose
evaera/promiseevaeraPromise implementation for Luau
sleitnick/knitsleitnickLightweight service/controller framework
sleitnick/signalsleitnickCustom signal (event) implementation
sleitnick/trovesleitnickCleanup/maid utility
sleitnick/componentsleitnickComponent pattern for CollectionService tags
madstudioroblox/profileservicemadstudioRobust DataStore wrapper
roblox/testezRobloxBDD-style testing framework
evaera/cmdrevaeraIn-game admin command framework
stravant/goodsignalstravantOptimized signal implementation
sleitnick/table-utilsleitnickTable utility functions
Add Packages/ to your .gitignore. Committing it bloats the repo and creates noisy diffs. Run wally install in your setup and CI scripts instead. Commit wally.lock for reproducible builds.

Selene — Luau Linter

Selene is a static analysis tool for Lua and Luau. It catches bugs, style issues, and potential errors before runtime, and integrates into VS Code for inline warnings.

Installation

aftman add Kampfkarren/selene
# or
cargo install selene

selene.toml Configuration

std = "roblox"

[rules]
unused_variable               = "warn"
shadowing                     = "warn"
unscoped_variables            = "deny"
incorrect_standard_library_use = "warn"
divide_by_zero                = "warn"
undefined_variable            = "deny"
deprecated                    = "warn"
global_usage                  = "warn"
if_same_then_else             = "warn"
ifs_same_cond                 = "warn"
multiple_statements           = "warn"
parenthese_conditions         = "warn"
type_check_inside_call        = "warn"
mismatched_arg_count          = "warn"
empty_if                      = "warn"
empty_loop                    = "warn"
almost_swapped                = "warn"
bad_string_escape             = "deny"
compare_nan                   = "deny"
constant_table_comparison     = "warn"
high_cyclomatic_complexity    = "warn"
manual_table_clone            = "warn"
mixed_table                   = "warn"
must_use                      = "warn"
roblox_incorrect_color3_new_bounds = "warn"
roblox_incorrect_roact_usage  = "warn"
roblox_suspicious_udim2_new   = "warn"
suspicious_reverse_loop       = "warn"

[config]
high_cyclomatic_complexity = { maximum_complexity = 20 }
The std = "roblox" setting tells Selene about all Roblox globals (game, workspace, Instance, etc.). Without it, every Roblox API call would trigger undefined_variable. Generate an up-to-date standard library from your project’s types:
selene generate-roblox-std

Common Commands

# Lint the entire src directory
selene src/

# Lint with a specific config
selene --config selene.toml src/

# Generate Roblox standard library
selene generate-roblox-std

# Quiet output (only errors)
selene src/ --display-style quiet

Inline Suppression

-- selene: allow(unused_variable)
local temporaryDebugValue = 42

local function example()
    -- selene: allow(shadowing)
    local x = 10
end

StyLua — Luau Formatter

StyLua is an opinionated code formatter for Lua and Luau, inspired by Prettier. It eliminates formatting debates and ensures consistent style across every contributor.

stylua.toml Configuration

column_width              = 120
line_endings              = "Unix"
indent_type               = "Tabs"
indent_width              = 4
quote_style               = "AutoPreferDouble"
call_parentheses          = "Always"
collapse_simple_statement = "Never"

[sort_requires]
enabled = true

Common Commands

# Format all Luau files in src/
stylua src/

# Check formatting without modifying (for CI)
stylua --check src/

# Format a single file
stylua src/server/PlayerService.luau
Add to .vscode/settings.json for format-on-save:
{
  "[luau]": {
    "editor.defaultFormatter": "JohnnyMorganz.stylua",
    "editor.formatOnSave": true
  }
}

VS Code Integration

The Roblox Game Skill is designed around a VS Code workflow. Install these extensions for the best experience:
ExtensionExtension IDPurpose
Luau LSPJohnnyMorganz.luau-lspAutocomplete, type checking, go-to-definition
Rojoevaera.vscode-rojoRojo integration in VS Code
SeleneKampfkarren.selene-vscodeInline lint warnings
StyLuaJohnnyMorganz.styluaFormat-on-save

.vscode/settings.json

{
  "luau-lsp.sourcemap.enabled": true,
  "luau-lsp.sourcemap.rojoProjectFile": "default.project.json",
  "luau-lsp.sourcemap.autogenerate": true,
  "luau-lsp.types.roblox": true,
  "luau-lsp.diagnostics.enabled": true,
  "luau-lsp.completion.enabled": true,
  "luau-lsp.hover.enabled": true,

  "[luau]": {
    "editor.defaultFormatter": "JohnnyMorganz.stylua",
    "editor.formatOnSave": true,
    "editor.tabSize": 4,
    "editor.insertSpaces": false
  },

  "files.associations": {
    "*.luau": "luau",
    "*.lua": "luau"
  },

  "files.exclude": {
    "Packages/": true,
    "sourcemap.json": true
  }
}

.vscode/extensions.json

{
  "recommendations": [
    "JohnnyMorganz.luau-lsp",
    "evaera.vscode-rojo",
    "Kampfkarren.selene-vscode",
    "JohnnyMorganz.stylua"
  ]
}
  • Red squiggles on Roblox APIs: Ensure luau-lsp.types.roblox is true and the sourcemap is being generated. Run rojo sourcemap default.project.json -o sourcemap.json manually if auto-generation fails.
  • Packages not resolving: After wally install, regenerate the sourcemap. The LSP reads sourcemap.json to understand the full tree.
  • Type errors on third-party packages: Some Wally packages lack type annotations. Use -- luau-lsp ignore or add type stubs.
  • Rojo plugin not connecting: Ensure rojo serve is running and Studio has the Rojo plugin installed via the Rojo VS Code extension or Roblox plugin marketplace.

Project Folder Structure

Minimal structure for a single developer:
my-game/
├── aftman.toml
├── default.project.json
├── wally.toml
├── selene.toml
├── stylua.toml
├── .gitignore
├── Packages/                  # git-ignored, populated by wally install
├── src/
│   ├── server/
│   │   ├── Services/
│   │   │   ├── DataService.server.luau
│   │   │   └── GameService.server.luau
│   │   └── init.server.luau   # bootstrap — requires and starts services
│   ├── client/
│   │   ├── Controllers/
│   │   │   ├── InputController.client.luau
│   │   │   └── UIController.client.luau
│   │   └── init.client.luau   # bootstrap — requires and starts controllers
│   └── shared/
│       ├── Constants.luau
│       ├── Types.luau
│       └── Utils.luau
└── tests/
    └── run.luau

.luaurc for Path Aliases

{
  "aliases": {
    "Packages": "Packages",
    "Shared":   "src/shared",
    "Server":   "src/server",
    "Client":   "src/client"
  }
}

CI/CD with GitHub Actions

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  lint:
    name: Lint (Selene)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Aftman
        uses: ok-nick/setup-aftman@v0.4.2
      - name: Generate Roblox standard library
        run: selene generate-roblox-std
      - name: Run Selene
        run: selene src/

  format:
    name: Format Check (StyLua)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Aftman
        uses: ok-nick/setup-aftman@v0.4.2
      - name: Check formatting
        run: stylua --check src/

  analyze:
    name: Type Check (Luau LSP)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Aftman
        uses: ok-nick/setup-aftman@v0.4.2
      - name: Install packages
        run: wally install
      - name: Generate sourcemap
        run: rojo sourcemap default.project.json -o sourcemap.json
      - name: Run Luau analysis
        run: luau-lsp analyze --sourcemap sourcemap.json --defs=globalTypes.d.luau src/

  test:
    name: Tests (Lune)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Aftman
        uses: ok-nick/setup-aftman@v0.4.2
      - name: Run tests
        run: lune run tests/run

  build:
    name: Build Check
    runs-on: ubuntu-latest
    needs: [lint, format, test]
    steps:
      - uses: actions/checkout@v4
      - name: Install Aftman
        uses: ok-nick/setup-aftman@v0.4.2
      - name: Install packages
        run: wally install
      - name: Build place file
        run: rojo build default.project.json -o build.rbxl
      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: game-build
          path: build.rbxl
          retention-days: 7

Common Anti-Patterns

Problem: All code lives exclusively inside a Roblox place file. If the file corrupts or you accidentally delete a script, the work is gone. Roblox auto-saves are not a reliable backup strategy.Fix: Use Rojo to keep code on the filesystem and commit to Git. Even a basic git init + git commit cycle provides infinite undo.
Problem: The Packages/ directory contains third-party code installed by Wally. Committing it bloats the repository, creates noisy diffs on updates, and duplicates what the registry already provides.Fix: Add Packages/ to .gitignore. Run wally install as part of your setup and CI scripts. Commit wally.lock for reproducibility.
Problem: Luau has a powerful type system, but many developers ignore it. Without types, refactoring is risky, autocomplete is limited, and bugs slip through.Fix: Annotate function signatures, especially on public module APIs. Use --!strict at the top of critical modules. Run luau-lsp analyze in CI.
Problem: A single 2,000-line server script handles combat, data, matchmaking, and UI updates. Impossible to test, review, or maintain.Fix: Split into focused services/modules. Each module should do one thing. Use init.luau folders to group related files. Aim for files under 300 lines.

Build docs developers (and LLMs) love