Terminal commands
Prefab runs in the macOS sandbox, which means it cannot run a shell command by itself. Commands go through a small script that lives outside the sandbox, in your own Application Scripts folder, and nothing runs until you have switched the feature on and installed that script.
Nothing runs until you have switched the feature on. There is no second step: Windows apps are not sandboxed, so Prefab starts a command itself rather than handing it to a helper the way it has to on macOS. The switch is in Settings ▸ Permissions and is off until you turn it on.
Switching it on
Terminal commands are off unless you turn them on in Settings ▸ Permissions ▸ Terminal Access. The switch is there for every kind of user. The experience you chose when you set Prefab up only decides whether it starts on or off.

Shown on macOS. This screen has no Windows equivalent.


Installing the shell runner
Turning the switch on offers the install straight away. It happens once.
Prefab checks whether the runner is already there and offers to install it if not. The button beside the status starts the same thing at any time.
A save panel appears, already pointed at your Application Scripts folder
(~/Library/Application Scripts/) with the name prefab-runner.sh filled in. Saving it there
is what gives Prefab permission to run it. A sandboxed app cannot put a file in that folder on
its own, which is why you are asked rather than told.
The Shell runner row reads Ready, with the version underneath. Test runs a command through the script and reports what came back below the buttons.
With the setting off, or the script not installed, a terminal action does not run and the deploy reports why. The folder and files are still created, but the command is not attempted. If a template you shared with someone relies on a command, they have to do this on their own Mac too.
What the command runs in
The runner is a zsh script. Before it runs anything it sources ~/.zprofile and ~/.zshrc, and
loads nvm from ~/.nvm if it is there, so commands see the same environment you get in Terminal:
Homebrew on the PATH, the Node version you actually use, your own aliases and functions.
The working directory is the folder the action is attached to, so a command can be written as
though you had already cd-ed into it. Placeholders are replaced before the command runs, so
git commit -m "Start of {{project_name}}" uses the value you gave for that deploy.
For a command attached to a watched folder rather than run at deploy, the file that arrived is
available as {{file}} - quote it, as paths contain spaces - with {{file_name}} for the name on
its own. That case is covered in the Rule reference.
When a command fails
The exit code comes back to Prefab, and a non-zero one is reported at the end of the deploy along with anything the command printed to standard error.
Continue on error decides what happens next. With it off, a failed command stops the actions after it on the same item. With it on, the rest run regardless - reasonable for something optional, less so for a command the next one depends on.
Timeouts
Each command carries a Timeout in seconds, 300 by default. The runner starts a watchdog alongside the command, and when the time is up it ends the command and everything the command started. Prefab reports it as having timed out rather than as a failure with an odd exit code, so one command that hangs cannot hold up the ones behind it.
Set the timeout to 0 to wait indefinitely. Do that only for something you are sure will finish, as nothing else on that item runs until it does.
Updating the runner
The installed script records which version of itself it is, and Settings reads that version back without running anything. The Shell runner row shows what you have against what this build of Prefab expects, and the button beside it is always offered: Install Shell Runner when there is none, Update Shell Runner when Prefab has moved on, and Reinstall Shell Runner otherwise, for a script that has been edited or removed by hand. Each of them walks through the same save panel.
An older script is worth replacing when the update is offered. It still runs commands, but it cannot tell Prefab whether one succeeded, so every command looks as though it worked, including the ones that did not. Timeouts are not enforced on an older script either - Prefab does not send one, because a script that predates them would treat the extra argument as part of the command.