alloy init
UpdatedWrites alloy.toml in the working folder, and one Luau configuration.
neither file writes .config.luau: strict mode and alloy = "./build/alloy" .config.luau adds the mode and the alias to it, only where they are absent .luaurc adds them to it, and writes no .config.luau
The edit keeps every other key and every comment, so a file you wrote stays as you wrote it. With both files present, .config.luau is the one edited, as Luau reads it first. alloy init fails when alloy.toml is already there.
The @alloy alias names the runtime the build writes to the output root. Emitted code requires it by that alias only when [emit] std_require says so; the default is a relative path, or the runtime's instance path when the DataModel tree holds the file (alloy doc mount).
A new project needs no project file to build. Add a default.project.json, or a [mount] table, when the sources have to land somewhere in the DataModel.
The wizard
alloy init in a terminal opens the wizard. A script, a pipe and a CI job have no terminal to answer with, so they read the files above and never wait for a key. --non-interactive forces the plain path and wins over every other flag, so a script that passes it reads one thing. -i or --interactive asks for the wizard where one is not opened for you. The wizard needs a terminal and color: with NO_COLOR set, or with stdin on a pipe, it refuses and names the plain path, since a prompt that no one can answer is a hang.
The first question is one confirm:
Use the recommended setup? (Y/n)
Yes asks nothing else. It writes a game, ember, lest, the temper preset, strict mode, and an AGENTS.md, and the report names every one of those, so nothing is guessed at. It assumes a game because that is the common case on Roblox, and the last line of the report says how to make a package instead. alloy init --yes, or -y, writes the same setup with no prompt at all, and needs no terminal, for a bootstrap script.
No opens the seven questions below, each one already on the recommended answer, so no means "let me change some of it" and enter alone walks the same setup:
What is this project? game: src/client, src/server, src/shared, the matching [mount] entries, sourcemap on, a .gitignore package: src alone, no mounts, sourcemap and source_of_truth off Project name? [project] name; the folder's name is the default Package manager? ember writes ember.toml; pesde and wally write their own, so alloy writes none; none writes nothing Test runner? [test] lest. With none, `alloy test` still writes a spec per @test and no runner runs them Style preset? the [fmt] and [lint] tables Strict mode for every file? languagemode in .config.luau Agent guide? AGENTS.md, CLAUDE.md, both, or none: a file that explains Alloy to a coding agent
Nothing is written until the last question is answered, so ctrl-c or esc at any prompt leaves the folder exactly as it was. Alloy runs no package manager and depends on none: the closing line names the one command to run next.
The agent guide
The guide tells a coding agent what Alloy is, the layout alloy.toml sets, and the commands to run. It holds the rules of alloy doc style word for word, so the guide and the article cannot drift. It ends with the two ways to look a topic up: alloy doc <topic>, and alloy mcp with the line that registers it, claude mcp add alloy -- alloy mcp. A guide that is already in the folder stays as it is, and the report says so.
The style presets
Three ship with Alloy, named for what heat does to metal. temper is the one preselected.
anneal soft. The formatter's own layout, [lint] recommended and strict both off, and correctness denied. For a port or a spike temper balanced. What alloy init writes: the [fmt] defaults written out, and [lint] recommended and strict both on forge hard. column_width 80, force-double quotes, sorted requires by kind, expanded imports, aligned struct fields, method call chains; strict on, naming on, and both documentation lints on
Your own presets
A preset is a TOML file holding a [fmt] table, a [lint] table, or both, and nothing else. The file name is the name the wizard shows. Two folders are read:
~/.alloy/presets/*.toml yours, beside the toolchain alloy self install writes .alloy/presets/*.toml the project's own, for a team to commit
A file that takes a built-in name replaces that preset, so a team can redefine temper and still see one entry in the list. A file that does not parse, or that writes another table such as [build], is not listed: the wizard splices the text into alloy.toml, and a second [build] there would not read.
# ~/.alloy/presets/house.toml [fmt] recommended = true column_width = 72 indent_type = "tabs" [lint] recommended = true strict = false [lint.rules] naming = "warn"
alloy doc fmt lists every [fmt] key and alloy doc lint every [lint] key, so a preset can only hold what those two tables hold.
