There are programs you ship, and there are programs you write to get something done — a rename over a directory, a quick calculation, a check you will run twice and delete. Scripting languages own that second kind, and they own it for one reason: a single command runs the file.

mica --run gives Mica that command, without giving up what a compiler knows. It ships with compiler release 6.12.5; every command below is exactly what the released compiler does.

One command

cat > hello.mica << 'EOF'
program Hello;

imp
    WriteLn : std;

begin
    WriteLn("hello from a script");
end.
EOF

mica --run hello.mica
hello from a script

That is the whole ceremony. The compiler builds the file into a temporary directory at the debug tier, runs it, forwards its exit code, and removes every trace. Nothing appears beside your file, nothing is cached anywhere — a one-file debug compile is milliseconds, so the honest cache is no cache. The whole round trip above, compile and run, takes about a hundredth of a second.

Notice what did not appear: no banner, no progress lines. On a clean compile the compiler says nothing at all, so the program owns standard output and pipes stay clean. Warnings still surface — on standard error, where diagnostics belong.

Arguments and exit codes

A script earns its keep through its arguments and its exit code. Everything after the file on the command line belongs to the program, readable through the process unit:

cat > args.mica << 'EOF'
program Args;

imp
    WriteLn : std;
    *       : process;

begin
    WriteLn("count=%d", ArgCount());
    WriteLn("first=%ls", Arg(1));
    WriteLn("second=%ls", Arg(2));
end.
EOF

mica --run args.mica alpha beta
count=2
first=alpha
second=beta

When a program’s own arguments begin with a dash, put -- between file and arguments; everything after it is passed through untouched.

The exit code works the way $? demands: the program’s verdict is the command’s verdict. Even a runtime failure reports honestly — here a program that prints one line and then indexes past an array’s bound, which trips Mica’s always-on bounds guard:

cat > trap.mica << 'EOF'
program Trap;

imp
    WriteLn : std;

type
    Numbers = array[0..2] of int64;

var
    values : Numbers;
    index  : int64;

begin
    WriteLn("before the trap");
    index := 5;
    values[index] := 1;
end.
EOF

mica --run trap.mica; echo "exit=$?"
before the trap
Mica runtime failure: reason=index_out_of_range (12), token_stream_index=40
Mica runtime context: file=.../trap.mica, line=16, column=11
Mica runtime source:     values[index] := 1;
exit=1

The file becomes a command

This is the part that surprises people meeting it for the first time: a Mica source file can be a command — indistinguishable from any installed program — and the mechanism behind it is worth two minutes of understanding, because it is the same mechanism behind every script on every Unix system.

What a shebang is. When you execute a file, the kernel reads its first two bytes. If they are #!, it treats the rest of that first line as the path of the program that should run this file, and starts that program with the file’s path appended. That is the whole mechanism. It is a kernel feature, not a language feature — it is how every bash and python3 script becomes runnable — and the line is called the shebang line.

#!/usr/bin/env mica adds one useful indirection: env looks up mica on your PATH and runs it. That is the portable idiom scripting ecosystems settled on, because the script then never hardcodes where the compiler is installed. Most languages need the GNU-only env -S trick here, to smuggle a flag like --run into the line; Mica does not, because the bare spelling mica tool.mica already means “run this” — and the compiler’s source preparation removes the #! line on every road, line numbers preserved, so the same file compiles through --compile, runs through --run, and opens in the editor without a diagnostic on line one.

A tool, step by step. Here is a complete command-line tool — sum, which adds up whatever numbers follow it. Step one, write the file:

#!/usr/bin/env mica
{
    sum — add up whatever numbers follow the command.
}
program Sum;

imp
    WriteLn : std;
    *       : process;
    Val     : strings;

var
    total, number : int64;
    index         : int32;

begin
    total := 0;

    { every command-line argument is either a number that joins the total or a complaint on the way out }
    for index := 1 to ArgCount() do
        if Val(Arg(index), address number) then
            total := total + number
        else
            WriteLn("not a number: %ls", Arg(index));

    WriteLn("%lld", total);
end.

Step two, make it executable — once:

chmod +x sum.mica

Step three, it runs:

$ ./sum.mica 12 30
42

Step four — the full command experience. Commands do not carry file extensions, and the bare spelling accepts that too: any existing file counts as the script. So drop the extension and move it onto your PATH:

$ cp sum.mica ~/bin/sum
$ sum 12 30 -5
37

sum is now a command. Whoever runs it sees arguments in, answer out, exit code honest — and a civil reply instead of a stack trace when an argument is not a number:

$ sum 12 oops 30
not a number: oops
42

What they never see is that there is no interpreter behind it. Each invocation compiles the file into a temporary directory and runs the result — about twenty milliseconds for this tool, compile and run together, beneath the threshold anyone notices — and every compile-time check ran before the first statement did. Your ten-line tool was type-checked, format-string checked, and bounds-guarded on every single invocation. No scripting language gives you that; no compiled language made it this easy before.

And a here-document is a program — --run - reads the source from standard input:

mica --run - << 'EOF'
program Hi; imp WriteLn : std; begin WriteLn("hi from a here-document"); end.
EOF
hi from a here-document

Your libraries come along

If your script lives inside a tree that carries a mica.project, the run mode discovers it exactly the way the editor does — the nearest project file up the directory tree governs — and its encoding and contracts apply. A script beside your project imports your libraries with no flags at all:

$ cat mica.project
{
  "contracts": ["build-lib"]
}
$ mica --run twice.mica       # twice.mica says: imp Twice : Numbers;
twice(21) = 42

No import paths to configure, no environment to activate. The file your build and your editor already read is the whole configuration — and a here-document typed inside the tree gets the same facts.

The check happens before the run

Here is the part no scripting language gives you. Change args.mica to print a string with %s — the utf-8 specifier — while the file compiles under the utf-32 default:

     9: WriteLn("first=%s", Arg(1));
                            ^ analyzer error 5184 [9,25]: format string validation error: string format specifier at position 1 must use '%ls' for UTF-32 encoding (found '%s')

The script never ran. Elsewhere the matching mistake waits until run time, for the input that happens to reach the broken line; a Mica script fails on your terminal, before a single statement executes, with the line and the fix in the message. Every compile-time check — types, format strings, unused variables, definite assignment — runs at full strength under --run, because the default is the debug tier, where checks are maximal and latency is already unmeasurable.

The mode is deliberately narrow about everything else. It targets the host architecture, and it combines with exactly four flags — --optimize, --stdlib, --memory-class, --tasking. Ask it for anything the build owns and it refuses in one line:

mica --build somewhere --run hello.mica
the flag '-build' cannot be combined with '--run': the run mode owns compilation, linking, and the build directory, and combines only with --optimize, --stdlib, --memory-class, and --tasking
run 'mica --help' for the compiler's full flag surface

And when a script outgrows scripting — it happens — nothing needs rewriting: the same file goes through --compile --link --optimize O2 and becomes the shipped program it was slowly becoming anyway.

Where it came from

The run mode shipped with compiler release 6.12.5, together with the project model it composes with. The announcement article tells the story in release terms; the command-line reference states every rule.

Next