Toolbox (asset type)
A toolbox is a namespace folder that groups related .tool/ children plus shared helpers. Toolboxes are the addressing layer for tools — every tool's identity is <toolbox>.<name>, so the toolbox name…
When to use one
- You have two or more related tools that belong together (an
animtoolbox withplay/stop/list; anassetstoolbox withcompose/validate). - You need a shared helper module exclusively used by a tool set.
- You want a clean dot-namespaced identity for your tools
(
my_tools.foo,my_tools.bar).
If you only have one tool and no shared helpers, you still need a toolbox — it's the required parent.
Where it lives
- Source:
/zero/source/.../<Name>.toolbox/ - Identity:
<Name>(the.toolboxsuffix strips). - Folder shape (container-style — open child set):
README.md— instance documentation. Required.shared.module/— optional shared helper module (init.luau+README.md).<x>.tool/— any number of.tool/children.
The validator permits unlisted children (allow_unlisted: true) so
tool subfolders don't need to be declared per-name.
How to create one
asset.create("toolbox", "<Name>")
-- Creates: /zero/source/tools/<Name>.toolbox/
-- README.md (instance README template)
-- shared.module/ (ok / fail result-envelope constructors)
Then add tools under it:
local box = asset.resolve("<Name>.toolbox")
asset.create("tool", "<toolName>", { into = box })
How it operates
- Registration. Writing into a
.toolbox/folder indexes the toolbox. It becomes the parent namespace for any.tool/children. - Tool identity composition. Each child
.tool/'s identity is<toolbox>.<tool>. Listing the toolbox's tools enumerates the children. - Shared helpers. Tools inside the toolbox pull
shared.module/in viarequire(".shared")(relative require from the tool'sinit.luau). - Hot reload. Editing
shared.module/init.luaureloads every tool in the toolbox (they allrequireit). Editing the toolbox folder structure (adding / removing tools) reflects intools.listimmediately.
Discovery
asset.list("toolbox")— every registered toolbox.asset.inspect("<name>")— child tools, source, this type README.cat /zero/source/<Name>.toolbox— same summary.tools.list()— every tool across every toolbox.
Authoring conventions
- Per-tool implementation lives in each
<x>.tool/init.luau. The tool's full function body — argument validation, the call into engine APIs, the return value — goes ininit.luauwith--!desc/--!arg/--!return/--!exampleannotations above the function. Usetyped functionsyntax when the signature has clear types. Do NOT makeinit.luaua one-line forwarder onto a function defined inshared.module/. - Keep
shared.module/thin: result envelope constructors (shared.ok(value, formatted),shared.fail(message, exitCode)), argument validators, common formatters — anything genuinely used by more than one tool. Per-tool logic does NOT belong here. - Group by domain, not by surface —
assetstoolbox holds asset operations,entitytoolbox holds entity operations. Tools that feel unrelated probably want different toolboxes. - Document the toolbox's surface in
README.md— what problem space it covers, who calls into it, any shared invariants.
Common pitfalls
- Missing parent toolbox. Tools cannot exist outside a toolbox. Always scaffold the toolbox first.
- Cross-toolbox shared. Each toolbox has its own
shared.module/. Don't try to share helpers across toolboxes; if you need cross-toolbox helpers, factor them into a standalone.module. - Toolbox identity. Strip the
.toolboxsuffix in identity references (my_tools, notmy_tools.toolbox).
Related types
.tool— the required child(ren) of a toolbox..module— for cross-toolbox utilities.