Epsil CLI
The @cortex-js/compute-engine package installs an epsil command for
evaluating Epsil source from a terminal. It can run a source file, evaluate an
inline program, read a program from standard input, or start an interactive
REPL.
Epsil and its command-line interface are experimental. Their syntax and behavior may change between releases.
Installation
Install the Compute Engine package in a project:
npm install @cortex-js/compute-engine
The package exposes epsil through npm's local executable directory. Run it
through npx or from a package script:
npx epsil --version
Running Programs
With a source file:
npx epsil program.epsil
With an inline program:
npx epsil --eval 'Simplify(2 + 2x)'
From standard input:
printf '1/2 + 1\n' | npx epsil
Use - as the file name to explicitly read standard input:
npx epsil - < program.epsil
The conventional Epsil file extension is .epsil. A source file
can be made directly executable with a hashbang:
#!/usr/bin/env epsil
let radius = 3
pi * radius^2
Options
| Option | Description |
|---|---|
-e, --eval <source> | Evaluate Epsil source supplied on the command line. |
--json | Write the result as formatted MathJSON, the representation Epsil programs are evaluated in. Finite lazy collections (Range, Map results, …) are materialized into their elements, up to 10,000. |
--epsil | Write the result as serialized Epsil source. |
--latex | Write the result as LaTeX. |
--from <format> | The notation of the source: epsil (the default) or latex, a single LaTeX expression. See LaTeX Input and Output. |
--fancy-symbols | With --epsil, write the Unicode notations instead of the ASCII spellings: √x for sqrt(x), ∛x and ∜x for cube and fourth roots, x² for x ^ 2, and ×, ÷, −, ≠, ⩽, ⩾, ∈, ⇒ for the operators. Every notation reads back to the same expression. |
--diagnostics <fmt> | Write diagnostics as text (the default) or as a json array. |
--time-limit <ms> | Set the evaluation deadline in milliseconds. The default is 10000; 0 disables it. |
--compile | Compile the program to JavaScript and run the generated code instead of interpreting it. See Running a Compiled Program. |
--no-color | Disable color in diagnostics. The NO_COLOR environment variable is also honored. |
-h, --help | Display command help. |
-v, --version | Display the package version. |
--json, --epsil and --latex are mutually exclusive, and
--fancy-symbols requires --epsil. With neither output option, results use the ordinary textual
representation of a value.
$ npx epsil --epsil -e 'Sqrt(2) * x^2'
Sqrt(2) * x ^ 2
$ npx epsil --epsil --fancy-symbols -e 'Sqrt(2) * x^2'
√2 × x²
LaTeX Input and Output
With --from latex, the source is a single LaTeX expression instead of an
Epsil program. It can come from --eval, a file, or standard input, and
combines with every output option:
$ npx epsil --from latex -e '\int_0^1 x^2\,dx'
1/3
$ npx epsil --from latex --latex -e '\frac{1}{2}+\frac{1}{3}'
\frac{5}{6}
$ echo '\frac{d}{dx} \sin(x^2)' | npx epsil --from latex
2x * cos(x^2)
A LaTeX parse error is reported like a runtime error, quoting the LaTeX where the parser stopped:
$ npx epsil --from latex -e '1+'
error: Runtime error: unexpected operator at `+`
In the REPL, --from latex makes each entry a LaTeX expression (.load
still reads an Epsil file). --latex also applies to Epsil programs: it
writes the value of the program as LaTeX.
$ npx epsil --latex -e 'Sqrt(8) / 2'
\sqrt{2}
Running a Compiled Program
With --compile, the program is compiled to JavaScript and the generated
code is run, instead of the program being interpreted:
npx epsil --compile program.epsil
The value of the compiled program is written the same way as an interpreted
result, and every output option (--json, --epsil, --latex) and
--from latex apply. Use this mode to see what a compiled program answers
(a host that compiles Epsil, such as a graphing application, runs the same
generated code) and to compare it with the interpreter.
The compiled route differs from the interpreter in three ways:
- Arithmetic is machine arithmetic. Every number is a float:
1/3is0.3333333333333333,sqrt(2)is1.4142135623730951, and2 + 1is the float3.0(written3, and{num: "3.0"}with--json). A pole is+ooorNaN, where the interpreter answers an exact value. - Every symbol must have a value. The interpreter keeps a symbol with no
value symbolic (
x + 1evaluates tox + 1); compiled code has no symbolic values, so such a program is a runtime error naming the symbol. - A construct the JavaScript target does not compile is an error. The
error names the construct (
Simplify, a pattern the target has no lowering for) instead of falling back to the interpreter.
$ npx epsil --compile -e 'f(x) = x^2 + 1
f(3)'
10
$ npx epsil --compile -e 'x + 1'
error: Runtime error: unbound symbol: `x` has no value; a compiled program cannot keep a symbol symbolic
--> 1:1
|
1 | x + 1
| ^^^^^
A value the compiled code answers that cannot be printed (the program
evaluates to a function) is a runtime error too. A color is answered in the
OKLCh color space, the canonical space of the compiled targets, whichever
constructor the program wrote. Parse errors and static type errors are
reported as they are for an interpreted program. The --time-limit deadline
covers parsing and compiling; the generated code then runs to completion.
Checking a Program Without Evaluating It
epsil check parses a program and reports its diagnostics — syntax errors,
malformed strings, invalid type annotations, match shape problems (a
match over a sum type that leaves a variant uncovered included), and the
trap lints (= inside a call argument, a literal index 0, a // comment
that reads as floor division) — without evaluating anything. It also prepares
the program to run (still without running it) and reports the problems that
surface there — type errors such as "a" + 1, a wrong argument count, or a
call whose argument cannot satisfy a parameter annotation of the function it
names (let k = (n: integer) => n + 1 then k(1.5)) — as
static-type-error diagnostics anchored to the offending statement.
An Error(…) value the program itself builds is not reported: errors are
values. It accepts the same source forms as evaluation: a file,
--eval, or standard input.
npx epsil check program.epsil
npx epsil check --eval 'let x = 5; x +'
The exit status is 0 when there are no error diagnostics (warnings are
allowed) and 1 otherwise. With --json, a machine-readable envelope is
written to standard output instead of formatted text on standard error:
$ npx epsil check --eval 'a+ b' --json
{
"ok": true,
"diagnostics": [
{
"severity": "warning",
"code": "asymmetric-operator-whitespace",
"args": ["+"],
"message": "asymmetric operator whitespace: +",
"start": 1,
"end": 2,
"line": 1,
"column": 3,
"fixits": [{ "start": 1, "end": 2, "value": " + " }]
}
]
}
start/end are 0-based character offsets into the source; line/column
are 1-based. A fixits entry is a replacement (value) for the source range
[start, end). The same structured form is available during evaluation with
--diagnostics json, which writes the array to standard error.
Reporting the effects of each function
With --effects, check also reports what the engine inferred about the
effects of each top-level function the program defines — a function
statement (any of its spellings), or a let/const whose value is written
as a lambda. The report goes to standard output, one line per function:
$ npx epsil check --effects --eval 'function f(x) { Print(x); x + 1 }
function g(x) pure { x * 2 }
let k = x => Random() + x'
f (line 1): console
g (line 2): pure (declared)
k (line 3): random
The labels are the effect labels the body
reaches (console, random, state, …), pure when there are none, and
any when the body calls something the engine does not know, so nothing can
be ruled out. (declared) marks a contract the author wrote on the
definition (pure, random, …); the labels are then what the author
promised, which the check has verified against the body. A multi-clause
function is one entry, the union of its clauses, at the line of its first
clause. Functions defined inside a block are not listed.
With --json, the same report is the effects array of the envelope:
name, effects (a list of labels, or "any", or null when nothing could
be inferred), declared, and the position of the name (start/end
offsets, line/column). The MCP check tool accepts "effects": true for
the same array.
Because check does not evaluate, it does not report runtime problems —
unknown-function suggestions, type mismatches at call sites, or error values.
Those surface when the program runs.
Looking Up Documentation
epsil doc shows the definition of a library symbol — its kind, signature
or type, description, and keywords — or searches the library when the
argument is not an exact name. Search matches identifiers, descriptions,
curated keywords, and LaTeX commands:
$ npx epsil doc Sin
Sin (function) (number) -> number — Sine of an angle.
keywords: sine
$ npx epsil doc greatest common divisor
GCD (function) (any*) -> number — Greatest Common Divisor
...
Use --limit <n> for more search matches (default 10) and --json for a
structured { query, matches } envelope. The exit status is 1 when
nothing matches.
MCP Server
epsil mcp starts a Model Context Protocol
server, giving AI agents structured access to the same operations as the CLI.
The default transport is standard input/output:
npx epsil mcp
Use the native Streamable HTTP transport for clients that connect to a URL:
npx epsil mcp --transport streamable-http
The HTTP endpoint defaults to http://127.0.0.1:8000/mcp. Configure it with
--host <address>, --port <number>, and --path <path>. The server binds
only to loopback by default; using a public bind address does not add HTTPS or
authentication. Repeat --allow-origin <origin> to allow a browser client
from a non-local origin.
| Tool | Purpose |
|---|---|
evaluate | Run a complete program; returns the value as display text, Epsil source and MathJSON, plus diagnostics |
check | Parse and report diagnostics without evaluating |
doc | Look up a library symbol, or search the library by keywords |
parse | Convert Epsil source to MathJSON |
serialize | Convert MathJSON to Epsil source |
The server also exposes the agent-facing language card
(/epsil/for-agents/) as the resource epsil://docs/for-agents.
Each evaluate call runs in a fresh session: definitions do not persist
between calls, so every program must be self-contained. The
--time-limit <ms> option sets the default evaluation deadline for the
evaluate tool (default 10000; each call can override it with its
timeLimit argument).
Interactive REPL
Run epsil with no file or --eval while standard input is a terminal:
$ npx epsil
Epsil 0.92.1
Type .help for more information.
epsil> let x = 5
5
epsil> x^2
25
The REPL keeps one session, so top-level declarations and assignments persist
between inputs. .clear starts a fresh session and clears that state.
Unclosed blocks, collections, strings, and expressions ending with an operator continue at a secondary prompt:
epsil> if x > 0 {
... x + 1
... }
6
REPL Commands
| Command | Description |
|---|---|
.help | List the available REPL commands. |
.clear | Reset to a fresh session. |
.load <file> | Execute an Epsil source file in the current session. |
.ast | Toggle MathJSON result output. |
.time | Toggle elapsed-time output. |
.editor | Enter Node's multiline editor mode. |
.break | Abandon the current multiline input. |
.save <file> | Save the entered REPL source to a file. |
.exit | Exit the REPL. |
Command history is stored in ~/.epsil_history. Set
EPSIL_REPL_HISTORY to use a different path.
Results, Diagnostics, and Exit Status
The value of the last statement is written to standard output. Diagnostics are written to standard error with their source location and an excerpt:
1:4 error: Unexpected symbol "+"
1 | 1 +
^
The process exits with:
0after successful evaluation, including evaluations that emit warnings;1for source, runtime, cancellation, or file errors;2for invalid command-line usage.
Evaluation is symbolic and exact by default. Use N(expr) in the program when
a numeric approximation is required.
Host-state pragmas such as #env and #navigator remain disabled in the CLI.
The command does not provide an option to enable them.
Evaluation Limits
Each input has a 10-second evaluation deadline by default. This prevents a runaway synchronous calculation from leaving an interactive session unresponsive:
npx epsil --time-limit 30000 long-running.epsil
Set --time-limit 0 for no deadline. The iteration and
recursion limits continue to apply independently.