mcpField
R
roblox-toolchain

roblox-toolchain

github.com/afrxo开发1259 次安装26 周前更新
代码仓库

关于 roblox-toolchain

roblox-toolchain 是面向 Roblox 游戏开发的 Agent 技能,介绍如何以文件系统为项目源,通过 Rojo、Rokit 和 Wally 或 Pesde 管理构建与依赖。内容涵盖项目配置、文件映射、工具版本固定、Luau LSP 配置及工作流规范。

主要功能

  1. 01Rojo 文件系统开发指南
  2. 02使用 Rokit 固定工具版本
  3. 03Wally 与 Pesde 依赖管理
  4. 04项目文件与 DataModel 映射
  5. 05配置 Luau LSP sourcemap
  6. 06提供代码库集成规范

适用场景

  1. 01从文件系统开发 Roblox 游戏
  2. 02为团队固定 Rojo 等工具版本
  3. 03配置 Wally 或 Pesde 依赖
  4. 04设置 Luau LSP 代码补全

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/afrxo/roblox-agent-skills --skill roblox-toolchain

安装后在 Claude Code 中输入/roblox-toolchain确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
.gitkeep
ci.yml
default.project.json
file-tree.md
pesde.toml
rokit.toml
wally.toml
references/
luau-lsp-setup.md
pesde-manifest.md
project-format.md
sync-mapping.md
wally-manifest.md

详细介绍

Roblox Rojo(文件系统工作流)

如何通过磁盘上的文件而非 Studio 内部来开发 Roblox 游戏。资料来源:rojo.space(Rojo 官方文档)、github.com/rojo-rbx/rojo(Rojo 源码)、github.com/rojo-rbx/rokit(Rokit 工具链管理器——请使用它,而非 Aftman/Foreman)、github.com/UpliftGames/wally(Wally 包管理器)、github.com/pesde-pkg/pesde(Pesde——较新的替代包管理器)。工具:stylua、selene、luau-lsp。

深入了解请参阅:


集成政策

本技能介绍的是推荐模式;不授权重写现有代码。将规则用于新代码,以及用户明确要求修改的内容。**遵循代码库现有的工具链。**如果项目使用 Aftman,不要主动迁移到 Rokit。如果使用 Wally,不要建议使用 Pesde。如果 default.project.json 遵循特定结构,就在此基础上扩展——不要重写。只指出一次冲突,并默认采用现有配置。

完整规则:../../shared/integration-policy.md。


原则

  • **唯一可信来源:文件系统。**仓库是规范来源;Studio 是构建目标。
  • 按项目固定工具链版本。rokit.toml 列出 rojo、wally(或 pesde)、stylua、selene、luau-lsp 的确切版本。可复现构建胜过“在我的机器上能用”。
  • **重新生成 sourcemap,而非将其提交。**它是生成的输出。依赖项变更时重新生成。
  • 在 CI 中构建和检查代码。main 分支状态为绿色,意味着 rojo build 能生成有效的 .rbxl,且检查全部通过。
  • **Packages/ 是生成目录。**由 Wally 写入;不要手动编辑;已加入 .gitignore。

Rojo 基础知识

安装

使用 Rokit(工具链管理器,Foreman/Aftman 的后继者)按项目固定版本:

# rokit.toml — drop-in compatible with foreman.toml / aftman.toml format
[tools]
rojo = "rojo-rbx/[email protected]"
wally = "UpliftGames/[email protected]"
stylua = "JohnnyMorganz/[email protected]"
selene = "Kampfkarren/[email protected]"

然后在项目根目录运行 rokit install,即可安装这些确切版本。不要依赖全局安装的工具——不同机器上的版本可能不一致。

**为什么选 Rokit,而不是 Aftman:**Aftman 已无人维护(第三方作者已不再从事 Roblox 相关工作);Foreman 由 Roblox 维护,但主要面向内部使用。Rokit 由社区维护,可直接兼容这两种清单格式,而且安装速度快得多。迁移时,将 aftman.toml 重命名为 rokit.toml。

常见 Rokit 命令:rokit init、rokit add OWNER/REPO、rokit install、rokit update、rokit list、rokit self-update。

两种模式
# Live sync — Studio plugin connects to this server, applies changes as you save.
rojo serve [path/to/project.json]

# One-shot build — produces a .rbxl (place) or .rbxm (model).
rojo build [path/to/project.json] --output build/place.rbxl

rojo serve 用于开发。rojo build 用于 CI 和最终发布。Studio 插件(通过 Studio 的插件选项卡安装,版本需与 CLI 匹配)负责实际应用实时变更。

项目文件位置

Rojo 会在当前目录中查找 default.project.json。传入显式路径即可使用其他任何 .project.json 文件。包含多个构建目标(游戏、插件、库)的仓库通常会为每个目标配置一个项目。


default.project.json 结构

{
  "name": "MyGame",
  "tree": {
    "$className": "DataModel",

    "ReplicatedStorage": {
      "$className": "ReplicatedStorage",
      "$path": "src/ReplicatedStorage"
    },

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

    "StarterPlayer": {
      "$className": "StarterPlayer",
      "StarterPlayerScripts": {
        "$className": "StarterPlayerScripts",
        "$path": "src/StarterPlayerScripts"
      }
    },

    "Workspace": {
      "$className": "Workspace",
      "$properties": {
        "Gravity": 196.2
      }
    }
  }
}
  • name(必需)— 用作构建产物名称。
  • tree(必需)— 根 Instance 描述。
  • 在 tree 中:
    • $className — Instance 类。如果 $path 能解析出类,或键是服务名称,则可省略。
    • $path — 要挂载到此 Instance 下的磁盘文件夹/文件。
    • $properties — 要设置的属性(例如 Gravity、HttpEnabled)。
    • $ignoreUnknownInstances — 如果为 true,Rojo 会保留未同步的 Instance,而不是将其删除。
    • 其他任意键 = 子 Instance 描述。

其他顶层字段:servePort(默认值为 34872)、servePlaceIds(place ID 允许列表,可防止意外同步到错误的游戏中)、placeId、gameId、serveAddress、globIgnorePaths、emitLegacyScripts。

emitLegacyScripts 用于控制 *.server.luau/*.client.luau 是生成旧式 Script/LocalScript Instance,还是生成设置了 RunContext 的 Script Instance。文档所述的默认值为 true;请先根据已安装的 Rojo 版本(rojo --version + 发布说明)确认,再据此作出假设。新项目应设置为 false 以使用现代 RunContext — 参见 roblox-dev 的脚本类型部分。

完整架构:references/project-format.md。


文件系统 → DataModel 映射

这套规则会经常用到,请记住常见项。

磁盘路径转换为
dir/(任意目录)Folder
foo.luau名为 foo 的 ModuleScript(Rojo 也接受旧式 .lua)
foo.server.luau名为 foo 的 Script(服务器端)
foo.client.luau名为 foo 的 LocalScript(客户端)
dir/init.luau名为 dir 的 ModuleScript(目录会变为脚本)
dir/init.server.luau名为 dir 的 Script(目录会变为服务器脚本)
dir/init.client.luau名为 dir 的 LocalScript(目录会变为本地脚本)
foo.rbxm / foo.rbxmx从二进制/XML 文件生成的模型
foo.csvLocalizationTable
foo.txtStringValue
foo.json(不是 .model.json / .project.json)返回该表的 ModuleScript
foo.toml返回该表的 ModuleScript
foo.model.jsonJSON 描述的模型(手写,适用于 RemoteEvent 等)
foo.project.json嵌套项目(合并到父项目中)
foo.meta.json附加到匹配文件/目录的元数据侧文件
init.luau 语义

目录 + init.luau 会变为一个 ModuleScript,目录中的其他子项则会成为该脚本的子项。这样,脚本无需单独的文件夹也能包含子模块。

src/Inventory/
├── init.luau          # ModuleScript "Inventory"
├── add.luau           #   ModuleScript "add" (child of Inventory)
├── remove.luau        #   ModuleScript "remove"
└── types.luau         #   ModuleScript "types"

每个目录中只能有一个 init.* 文件(init.luau 或 init.server.luau 或 init.client.luau)。

Meta 文件

foo.meta.json 会将 Rojo 专用元数据附加到 foo.*。常见用途:

// init.meta.json — turn the parent folder into a Tool with a Grip
{
  "className": "Tool",
  "properties": {
    "Grip": [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]
  }
}
// foo.meta.json — disable a script
{
  "properties": { "Disabled": true }
}
// hello.meta.json — let Rojo leave Instances under hello.txt alone
{
  "ignoreUnknownInstances": true
}

className 仅适用于 init.meta.json。properties 适用于任何非模型文件。

完整映射表:references/sync-mapping.md。


推荐的项目布局

my-game/
├── rokit.toml                # toolchain pinning
├── default.project.json       # Rojo project file
├── wally.toml                 # package manifest
├── wally.lock                 # lockfile (committed)
├── stylua.toml                # formatter config
├── selene.toml                # linter config
├── sourcemap.json             # generated; .gitignore'd
├── Packages/                  # wally output; .gitignore'd
├── ServerPackages/            # wally output; .gitignore'd
├── DevPackages/               # wally output; .gitignore'd
├── .github/workflows/ci.yml   # CI
├── (editor config)            # .vscode/settings.json, .nvim, etc. — editor-specific
├── .gitignore
├── README.md
└── src/
    ├── ReplicatedStorage/
    │   ├── Shared/
    │   │   ├── init.luau
    │   │   └── ...
    │   └── Client/             # (optional) client-only scripts via RunContext
    ├── ServerScriptService/
    │   ├── init.server.luau
    │   └── Systems/
    └── StarterPlayer/
        └── StarterPlayerScripts/
            └── init.client.luau

让 src/ 目录名称与其 DataModel 挂载点一致。查看路径即可准确了解代码的运行位置。


Wally(包)

通过 Rokit 安装(不要从全局安装中获取)。添加到 rokit.toml:

[tools]
wally = "UpliftGames/[email protected]"

然后运行 rokit install。命令:

wally init               # scaffold wally.toml
wally install            # download deps into Packages/
wally install --locked   # CI; fail if lockfile is stale
wally update [name...]   # bump deps
wally publish            # push your package to the registry
wally search <query>     # find packages
wally.toml
[package]
name = "yourname/coolpkg"        # SCOPE/NAME, lowercase, dashes ok
version = "1.0.0"                # SemVer
license = "MIT"                  # SPDX
authors = ["Your Name <[email protected]>"]
realm = "shared"                 # "shared" or "server"
registry = "https://github.com/UpliftGames/wally-index"

[dependencies]
React = "jsdotlua/[email protected]"
Promise = "evaera/[email protected]"

[server-dependencies]
ProfileService = "loleris/[email protected]"

[dev-dependencies]
Jest = "jsdotlua/[email protected]"
Realm
  • shared — 同步到客户端。挂载到 ReplicatedStorage 下。
  • server — 仅服务器端可用。挂载到 ServerStorage 或 ServerScriptService 下。
  • 不存在 client realm。仅客户端依赖项放在 shared 中,并在客户端通过导入运行。
在 Rojo 中挂载 Wally 输出
"ReplicatedStorage": {
  "$className": "ReplicatedStorage",
  "Packages": {
    "$path": "Packages"
  }
},
"ServerScriptService": {
  "$className": "ServerScriptService",
  "Packages": {
    "$path": "ServerPackages"
  }
}

包会按 realm 安装到不同文件夹中。Shared → Packages/,server → ServerPackages/,dev → DevPackages/。

锁文件

wally.lock 固定了每个依赖项(包括传递依赖项)的确切版本。请提交它。 在 CI 中运行 wally install --locked,可确保所有人构建出相同的内容。

完整的清单字段、锁文件格式和命令列表:references/wally-manifest.md。


Pesde(替代包管理器)

Pesde 是一款较新的 Luau 优先包管理器,旨在避免运行时锁定。它通过一个工具支持多个 Luau 运行时(luau、lune、roblox、roblox_server),还可通过桥接从现有的 Wally 注册表获取包。以下情况值得使用:

  • 希望在纯 Luau、Lune 和 Roblox 项目中使用同一个包管理器。
  • 需要在同一个项目中使用 Wally 包和原生 pesde 包。
  • 相比 Wally 的 Cargo 风格,更偏好 npm/pnpm 风格的使用体验。
pesde.toml
name = "yourname/coolpkg"
version = "1.0.0"
license = "MIT"

[target]
environment = "roblox"     # luau | lune | roblox | roblox_server
lib = "src/init.luau"

[indices]
default = "https://github.com/pesde-pkg/index"

[wally_indices]
default = "https://github.com/UpliftGames/wally-index"

[dependencies]
# Native pesde dep
hello = { name = "pesde/hello", version = "^1.0.0" }
# Wally dep via the bridge
React = { wally = "jsdotlua/react", version = "^17.1.0" }

[dev_dependencies]
Jest = { wally = "jsdotlua/jest", version = "^3.10.0" }
命令
pesde init        # scaffold pesde.toml
pesde add SCOPE/NAME   # add + install a dep
pesde install     # install all deps; writes pesde.lock
pesde run         # run [scripts] entries (uses Lune)
pesde publish
目录结构
  • 原生 pesde 依赖项会安装到以目标命名的文件夹中(roblox_packages/、luau_packages/ 等)。
  • Wally 桥接依赖项仍通过 wally-index 仓库解析。
  • pesde.lock 是锁文件,请提交它。
  • 对于 Roblox 项目,pesde 会通过脚本包生成与 Rojo 兼容的源映射;请配合 rojo sourcemap 使用。
如何选择
需求选择
成熟、仅面向 Roblox、拥有庞大现有生态Wally
多运行时(Lune 命令行工具、Roblox、纯 Luau)Pesde
Cargo 风格的使用体验Wally
npm/pnpm 风格的使用体验,加上 Wally 桥接Pesde
现有 Wally 项目使用顺畅继续使用

Wally 和 Pesde 并非可直接互换的工具,清单格式和解析语义各不相同。没有理由就不要在项目中途切换。


源映射(Luau LSP)

Luau LSP 需要知道文件在 DataModel 中的映射位置,才能正确提供自动补全和 require() 解析。Rojo 会生成该映射。

# Watch mode — regenerates on filesystem change
rojo sourcemap --watch --output sourcemap.json default.project.json

# One-shot
rojo sourcemap --output sourcemap.json default.project.json

然后将其路径提供给 luau-lsp。各编辑器的设置方式如下:

  • VS Code(luau-lsp 扩展): 在 .vscode/settings.json(或工作区级配置)中设置 "luau-lsp.sourcemap.sourcemapFile": "sourcemap.json"。
  • Neovim / Helix / Sublime: 将源映射路径作为 LSP 启动参数(--definitions=... / --sourcemap=...)传入,或通过 LSP 客户端的设置传入;luau-lsp 二进制文件的文档中列出了相关标志。
  • 命令行模式: luau-lsp analyze --sourcemap sourcemap.json ...。请通过已安装的 luau-lsp --help 确认具体标志。

在以下情况后重新生成:

  • 在项目中添加或移除文件。
  • 运行 wally install(新增包路径)。
  • 更改 default.project.json 挂载点。

一种常见工作流:在终端中运行 rojo sourcemap --watch,同时运行 rojo serve。两者都会在整个会话期间持续运行。

sourcemap.json 是生成文件,请将其加入 .gitignore。

LSP 设置详情:references/luau-lsp-setup.md。


代码检查与格式化

StyLua(格式化工具)
# stylua.toml
column_width = 100
line_endings = "Unix"
indent_type = "Tabs"          # or "Spaces" with indent_width = 4
quote_style = "AutoPreferDouble"
call_parentheses = "Always"

[sort_requires]
enabled = true                # alphabetize the require block; pairs with luau-expert guidance

通过 stylua src/ 运行。安装 StyLua 扩展后,大多数编辑器都能在保存时自动格式化。

selene(代码检查工具)
# selene.toml
std = "roblox"                # roblox stdlib definitions

# Optional: per-rule tweaks
[config]
incorrect_standard_library_use = "warn"

std = "roblox" 是关键设置;没有它,selene 会对每个 Instance.new 和 task.wait 报错。通过 selene src/ 运行。

Luau LSP(类型检查器)

luau-lsp 可在编辑器中提供类型检查、悬停信息和自动补全。每个文件结合使用 --!strict(参见 luau-expert 技能),以实现最大覆盖率。

项目级配置(.luaurc):

{
  "languageMode": "strict",
  "lint": {
    "*": true
  }
}

请根据 luau.org/lint 验证架构;.luaurc 字段可能会有变化。


CI

最低可用的 CI 运行流程:

  1. 设置 Rokit(rokit install),安装固定版本的工具。
  2. wally install --locked——如果锁文件不一致则失败。
  3. stylua --check src/——如果格式不一致则失败。
  4. selene src/——执行 lint。
  5. rojo sourcemap --output sourcemap.json default.project.json——生成映射文件。
  6. 使用 sourcemap 运行 luau-lsp analyze——进行类型检查。具体标志取决于已安装的版本(--sourcemap 或 --definitions);请查看 luau-lsp --help。
  7. rojo build default.project.json --output build/place.rbxl——构建产物。
  8. **(可选)**将 place.rbxl 上传为产物/发布。

每一步失败都会中止后续步骤。CI 运行通过意味着:工具版本固定、依赖已锁定、格式正确、lint 无问题、类型检查通过且构建成功。

GitHub Actions 示例见 examples/ci.yml。


验证规则

  • 不要凭记忆引用 .project.json 字段名,以下字段除外:name、tree、$className、$path、$properties。完整架构很简短——提交前请查看 references/project-format.md,确认不常用字段。
  • 不要凭记忆引用 Wally 版本语义。 Wally 遵循 Cargo 风格的 SemVer,默认使用 ^;推荐使用非默认版本说明符前,应根据 Wally 文档确认 ~、=、* 的具体含义。
  • 不同版本中 emitLegacyScripts 的默认值有所变化。 在假定 Script 与 Script(RunContext=Server) 的区别前,请确认项目使用的 Rojo 版本(rojo --version)。
  • 编辑器配置各不相同。 本技能涵盖 Luau LSP 层——参考资料中的 .vscode/settings.json 示例仅供说明;Neovim/Helix/Sublime 用户需配置对应字段。
  • 不要混淆 Rojo 与引擎。 RunContext 的位置/复制/DataModel 语义属于 roblox-dev。本技能只说明“文件最终位于此 DataModel 路径”。

反模式

❌ 不要✅ 应该
全局安装 rojo/wally/stylua通过 rokit.toml 为每个项目固定版本
新项目使用 Aftman 或 Foreman使用 Rokit(可直接替代;由社区维护,速度更快)
手动编辑 Packages/将其视为生成内容;wally install 会重新生成
提交 Packages//ServerPackages//DevPackages/将它们加入 .gitignore
提交 sourcemap.json将其加入 .gitignore;按需重新生成
忘记将 wally.lock 纳入 git提交它;CI 使用 --locked
在同一文件夹中放置多个 init.*每个目录只放一个 init 脚本;改用子模块
将 init.client.luau 放在 ServerScriptService 挂载点让脚本类型与容器匹配
使用未设置 std = "roblox" 的 selene始终设置;否则每个 Roblox API 都会触发 lint
同时编辑 Studio 和文件系统文件系统是唯一标准;不运行 rojo serve 时请关闭 Studio
将 rojo build 生成的 place 文件提交到仓库仅作为 CI 产物;二进制 place 文件无法进行 diff
仅在 ReplicatedStorage 下挂载 Packages,却使用服务器依赖在服务器容器下单独挂载 ServerPackages
在 StarterPlayerScripts 挂载点使用普通 *.luau,并期待客户端行为使用 *.client.luau(旧版方式)或 RunContext=Client 的 Script
每次保存后手动重新运行 rojo sourcemap在长期运行的终端中使用 --watch
从工作树有未提交更改时运行 wally publish为干净的提交打标签;从 CI 发布
不使用 sourcemap 运行 luau-lsp生成 sourcemap;LSP 需要它来解析 require()

示例

评论

支持 Markdown