Skip to main content

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.

warning

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​

OptionDescription
-e, --eval <source>Evaluate Epsil source supplied on the command line.
--jsonWrite 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.
--epsilWrite the result as serialized Epsil source.
--latexWrite 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-symbolsWith --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.
--compileCompile the program to JavaScript and run the generated code instead of interpreting it. See Running a Compiled Program.
--no-colorDisable color in diagnostics. The NO_COLOR environment variable is also honored.
-h, --helpDisplay command help.
-v, --versionDisplay 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/3 is 0.3333333333333333, sqrt(2) is 1.4142135623730951, and 2 + 1 is the float 3.0 (written 3, and {num: "3.0"} with --json). A pole is +oo or NaN, where the interpreter answers an exact value.
  • Every symbol must have a value. The interpreter keeps a symbol with no value symbolic (x + 1 evaluates to x + 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.

ToolPurpose
evaluateRun a complete program; returns the value as display text, Epsil source and MathJSON, plus diagnostics
checkParse and report diagnostics without evaluating
docLook up a library symbol, or search the library by keywords
parseConvert Epsil source to MathJSON
serializeConvert 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​

CommandDescription
.helpList the available REPL commands.
.clearReset to a fresh session.
.load <file>Execute an Epsil source file in the current session.
.astToggle MathJSON result output.
.timeToggle elapsed-time output.
.editorEnter Node's multiline editor mode.
.breakAbandon the current multiline input.
.save <file>Save the entered REPL source to a file.
.exitExit 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:

  • 0 after successful evaluation, including evaluations that emit warnings;
  • 1 for source, runtime, cancellation, or file errors;
  • 2 for 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.