Skip to content

CLI workflow

Surf files use .ch; Deep files use .dp. The commands below accept source files for formatting, checking, evaluation, and native builds. For a complete example, start with the first program.

CommandWhat it does
chelis fmt FILEPrints canonical Surf or Deep source. Use --inplace to write it or --check to test formatting without writing.
chelis lint --check FILEReports style violations and exits nonzero for blocking ones.
chelis check FILEChecks types and effects and prints a JSON report.
chelis deep FILE.chPrints canonical Deep for Surf input.
chelis surf FILE.dpPrints canonical Surf for Deep input.
chelis validate --surf FILE.chChecks Surf syntax against the conformance grammar. Use --deep for Deep or --desugar to validate the Deep form of Surf input.
chelis eval --file FILEEvaluates a .ch or .dp file. chelis eval 'EXPR' evaluates an inline expression.
chelis build FILE.ch --target c --output out/Builds a native executable or static library. C is the default target.
chelis testRuns tests in the current Reef package; see Testing.
chelis proveChecks properties; see Checking Properties.
chelis reefManages packages; see Reef and Packages.
chelis lane-check PATHRuns each program through the evaluator and a compiled C build and compares their complete stdout.
chelis cost FILEReports the copy cost of the lowered IR.
chelis runtime export DIRWrites the compiler's bundled runtime archive and public headers, plus a JSON record of the exported files.
chelis migrate surf --from 0.18 PATH...Prints Surf written in the 0.18 grammar in the current grammar; migrate deep does the same for Deep. --check verifies the files are already migrated, and --inplace rewrites them.
chelis tideOpens the interactive REPL. chelis tide serve and chelis tide lsp start the HTTP and language servers.

validate requires exactly one of --surf, --deep, or --desugar. It checks syntax; use check for type and effect errors. Run chelis COMMAND --help for the full options of any command.

For agent integration, use the agent workflow. It explains how to use the CLI and how to test an MCP connection to chelis tide mcp before an agent uses it.

From a directory containing app.ch:

Terminal window
chelis fmt --inplace app.ch
chelis lint --check app.ch
chelis check app.ch
chelis eval --file app.ch

check prints a JSON report even when checking fails. For one file, an empty errors array means exit 0; a nonempty one means exit 2. chelis check DIRECTORY checks discovered .ch and .dp files and prints one JSON envelope with files and errors. Directory failures appear either in the affected file's report or in the envelope. A failing directory check exits 2.

File reports carry fitness components, checked-node counters, unresolved names, and diagnostics. --show-inferred requests callable signatures. Annotated Deep is available through the compiler checked-program API; the JSON report has no typed_ast member. A failure report still carries diagnostics even when no checked program can be produced.

For a short calculation without a file, use chelis eval 'EXPR'. A file containing definitions but no expression has nothing to display; with --json, a successful evaluation of such a file prints a result whose roots array is empty. chelis eval --file app.ch --timeout 30 bounds an evaluation in seconds. A timeout fails with a diagnostic.

For scripts, chelis eval --json --file app.ch writes one JSON result to stdout on success. On failure, it writes no result JSON; diagnostics go to stderr. Effects already completed before the failure may still have produced output.

check, validate, build, and eval --file check the input file's canonical formatting and blocking lint rules before their main work. If one fails, run chelis fmt --inplace FILE, then chelis lint --check FILE and address any remaining blocking diagnostics. Advisory lint warnings do not block these commands.

Each of those commands accepts --allow-style-violations for an exceptional local run. It prints a warning and skips the style check for that command. Parse, type, evaluation, and build errors still fail.

Terminal window
chelis build app.ch --target c --output out/

c is the default target. build invokes its native toolchain and produces an executable for observable programs, or a static library for definitions-only modules. It retains generated sources and runtime support. Run ./out/app after the example above.

Add --emit-c to stop after source emission and runtime staging, without requiring a native compiler. A .dp input takes the Deep path automatically. See Build programs for artifact names, compiler overrides, library linking, and platform requirements.

Surf retains |>, and fmt preserves it. Deep 0.20 represents a pipe as ordinary applications. chelis deep (with or without --annotate) and chelis surf therefore emit calls. Stored Deep and compiled caches containing the previous pipe node must be regenerated. Compiler caches and shell packages reject previous format versions.

Folding before literal typing makes 0.1 |> cast(f64) equal to cast(0.1, f64). To retain an existing program's previous f32-rounded value, migrate using a compiler from before this change:

Terminal window
chelis migrate pipes --baseline-compiler /path/to/previous/chelis --inplace file.ch other.ch
chelis migrate pipes --baseline-compiler /path/to/previous/chelis --check file.ch other.ch

With neither flag, the command prints one migrated file. It uses the previous compiler's expanded Deep to suffix unsuffixed numeric literals in pipe seeds and adds grouping with the previous grammar's reading, such as (2.0f32 * x) |> f. Every file must preserve normalized expanded Deep and reach a formatter fixed point. The whole batch is checked before any file is replaced; missing dtype evidence, changed Deep, or comments the formatter cannot preserve reject the migration. Use the command on library and application sources before switching compiler versions. A file rejected by the previous compiler needs separate diagnosis, rather than an assumed default dtype.

Keep the baseline executable used to check the original files. The migration compares whole-file graphs, so a compiler with unrelated typing changes is not a compatible substitute. If the comparison fails, leave the files unchanged and diagnose the reported difference before retrying. Regenerate stored Deep from the migrated Surf source.