rune run
Resolves name against the nearest config, spawns it through the platform shell, waits, and exits
with the child's code.
Arguments for the command
The script name is the boundary. Rune's own options come before it — the rule npm run already
uses — and everything after it belongs to the command.
Each element is quoted for the shell that will receive it, so an argument containing a space
arrives as one argument. This matches npm run test -- --watch, and the parity is covered by a
test suite that compares the two byte for byte.
From a package.json script
This is the layout to write, and it needs no separator:
npm test -- --watch and pnpm test -- --watch both reach the child. The package manager
implements its -- by appending --watch to the command string, so the separator is spent on the
append and never reaches Rune. Nothing needs it to.
Typing the separator yourself
-- still works and still means what it always meant:
The last line is the one it is still needed for: a value that would otherwise read as one of Rune's own options.
An option Rune knows, after the name
The command still receives --root and resolution is unaffected. This is the one case the
positional rule can surprise someone, so it is never silent.
How to call Rune on Windows
pnpm exec rune, a package.json script, and Git Bash all hand your arguments to Rune unchanged.
Running node_modules\.bin\rune.CMD by hand does not. That file is a batch shim the package manager
generated, and cmd.exe re-parses everything through it, so &, ^, |, <, >, (, ), %
and ! inside an argument change meaning before Rune starts. This is true of every tool installed
from npm, not of Rune alone.
PowerShell removes a bare -- from the arguments it forwards. Nothing here depends on the separator
surviving, so the PowerShell path works — but it is why typing -- is not a way to make an
invocation safer there.
Arguments Rune passes to a tool
The same trap sits on the other side, and Rune closes it. On Windows nearly every tool in
node_modules/.bin is a batch file, and a batch file re-reads its own arguments through %* after
cmd.exe has already read the line. An argument escaped once for cmd.exe arrives at the tool with
its & acting as a command separator, which turns a filename into two commands:
When the command resolves to a .cmd or .bat file, Rune escapes each argument for both readers, so
that filename reaches the tool whole. The decision is made from the file PATH resolution chose, not
from the text of the command, because nobody writes the .cmd extension. A command that starts with
an operator or a variable expansion is left exactly as it is.
The limit, stated rather than left to be found: a batch file that calls another batch file adds a third reader and is not covered. No runner in this ecosystem covers it.
The terminal
A single script inherits Rune's standard input, output and error. The child holds the real terminal, so colour detection, progress bars, watch-mode redraws and interactive prompts behave as they would without Rune in front of them. Rune copies no bytes.
Parallel groups are the exception. Their members are piped so their output can be prefixed. See Prefixed output.
Ctrl+C
The interrupt reaches the child directly, because the child shares Rune's process group on POSIX and the console on Windows. Rune records the signal and keeps waiting. It never exits before the child.
The child's own outcome is what Rune reports. A watcher that handles SIGINT and exits 0 makes
Rune exit 0. A process killed by the signal makes Rune exit 130.
Exit codes
The child's code, exactly. Tools speak through exit codes and Rune does not translate them. The full table is on Exit codes.
When the script does not exist
The suggestion appears when a defined name is within a Levenshtein distance of 2. The full list is printed either way, because a suggestion alone hides the config from someone seeing it for the first time.
When there is no config
Running a group
name may be a serial or parallel group. A group has no command of its own, so it takes no
arguments:
See Groups.
The shell
npm_config_script_shell overrides both, and it arrives in the environment automatically when Rune
is called from a package-manager script. A PowerShell value is invoked with -NoProfile -Command.
Going through a shell is what makes &&, pipes and redirection work without Rune parsing any of
it, and it is what npm does, so a command string that worked in package.json keeps working.