Skip to content

CLI Reference

The graphcal command-line tool provides subcommands for evaluating, formatting, and checking .gcl files, as well as starting the LSP server.

Global Options

graphcal [OPTIONS] <COMMAND>
Option Description
-h, --help Print help
-V, --version Print version and build commit SHA when available

Commands Overview

Command Description
eval Evaluate a .gcl file
format Format .gcl files
check Check .gcl files for errors without evaluation
graph Export the dependency graph of a .gcl file (experimental)
deps lock Resolve exact-rev Git dependencies and write graphcal.lock
plugin new Scaffold a WASM plugin crate using the Rust SDK (experimental)
plugin test Validate a built .wasm plugin module and call its functions (experimental)
lsp Start the LSP server

graphcal deps lock

Resolve a package's exact-rev Git dependencies, materialize them in the local Graphcal cache, verify their source hashes, and write graphcal.lock.

graphcal deps lock [OPTIONS]

Options:

Option Description
--root <ROOT> Project root directory (overrides automatic graphcal.toml detection)

graphcal deps lock is the only public package-management command in the MVP. It reads [dependencies] from graphcal.toml, accepts only Git dependencies with a full commit-hash rev, fetches any missing sources, records a graph-shaped package-instance lockfile, and writes nothing when the deterministic lockfile contents are already up to date.

The command also scans the package's .gcl sources for WASM plugin imports and pins each referenced .wasm file's SHA-256 as a [[plugin]] entry. Loading enforces the pins: an unpinned or hash-mismatched plugin is a hard error, so plugin binaries can only change together with a reviewable lockfile diff.

Dependency-consuming commands (check, eval, graph, and the LSP) are read-only with respect to packages: they read graphcal.lock and cached sources, but they do not fetch, create, or update lockfile entries. If the lockfile is missing, stale, uses a different Graphcal or standard-library version, or references a missing or hash-mismatched cache entry, they fail and ask you to run graphcal deps lock.

Private Git repositories are supported only when the underlying Git fetch can obtain credentials from the current environment. This is intentionally not a portable guarantee: SSH may work with a configured key/agent, while HTTPS may fail unless a compatible credential helper or non-interactive credential provider is available. Do not place credentials directly in git URLs in graphcal.toml.

Examples:

# Resolve dependencies from the discovered project root
$ graphcal deps lock
wrote /path/to/project/graphcal.lock
# Resolve dependencies for an explicit project root
$ graphcal deps lock --root mission
up to date: /path/to/mission/graphcal.lock

See Package Dependencies for manifest syntax and source-resolution rules.


graphcal plugin new

Experimental

The plugin commands are experimental, like the plugin system itself; their surface may change in any release.

Scaffold a new WASM plugin crate that uses the graphcal-plugin Rust SDK: a Cargo.toml (cdylib + rlib), a rust-toolchain.toml with the wasm32-unknown-unknown target, a src/lib.rs with a sample plugin! block, native unit tests, a justfile, and a README describing the build-vendor-pin workflow.

graphcal plugin new <NAME> [OPTIONS]

Arguments:

Argument Description
<NAME> Crate name: a lowercase letter followed by lowercase letters, digits, -, or _

Options:

Option Description
--dir <DIR> Target directory (default: ./<NAME>); must not exist yet

Exit codes:

Code Meaning
0 Scaffold created
2 Invalid name, existing target directory, or I/O error
$ graphcal plugin new fluid-props
created plugin crate `fluid-props` at fluid-props

graphcal plugin test

Validate a built .wasm module against the plugin ABI and print its identity and signatures. Every load-time check runs before any plugin code: the embedded manifest must decode, the module may import nothing beyond graphcal::fail, and each manifest function's wasm parameter/result types must match its declared signature. On success the output includes the SHA-256 (the value graphcal deps lock pins) and a paste-ready import plugin block in .gcl syntax.

graphcal plugin test <MODULE> [--call <FUNCTION> [ARGS]...]

Arguments:

Argument Description
<MODULE> Path to the .wasm plugin module
[ARGS]... Arguments for --call: numbers in SI base units for quantity parameters, true/false for Bool, integers for Int

Options:

Option Description
--call <FUNCTION> Call one provided function after validation, under the same fuel and memory limits evaluation uses

Exit codes:

Code Meaning
0 Module is a valid plugin (and the call, if any, succeeded)
1 ABI validation failed, or the called function failed or returned a value violating its declared kind
2 Unreadable file, unknown function, or unusable call arguments
$ graphcal plugin test plugins/fluid_props.wasm --call lerp 1.0 3.0 0.5
plugin: plugins/fluid_props.wasm
sha256: 3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855c
abi: version 3, 2 function(s)

import plugin "plugins/fluid_props.wasm" as fluid_props {
    fn air_density(p: Length^-1 * Mass * Time^-2, t: Temperature) -> Length^-3 * Mass;
    fn lerp<D: Dim>(a: D, b: D, t: Dimensionless) -> D;
}

lerp(1.0, 3.0, 0.5) = 2

The rendered fixed dimensions are spelled structurally over the prelude base dimensions (the manifest's alphabet); in your .gcl declaration you may equivalently write derived names such as Pressure — verification is structural. Array arguments to --call are bracketed literals (--call share [1.0,3.0]) and array results render the same way; for struct-returning functions the import block is preceded by a suggested record declaration to paste alongside it.


graphcal eval

Evaluate a .gcl file and print results.

graphcal eval [OPTIONS] <FILE>

Arguments:

Argument Description
<FILE> Path to the .gcl file (required)

Options:

Option Description
--format <FORMAT> Output format: text (default) or json
--output-view <VIEW> Values to display: surface (default) or all
--set <SET> Override or provide a param value: --set 'name=expr' (repeatable)
--input <INPUT> JSON input file for param values
--plot <MODE> Plot output mode: browser (open in browser), json (print only plot JSON to stdout), or a path ending in .html (write a self-contained HTML page)

When both --set and --input are provided, --set takes precedence.

Override names are unqualified parameter names in the entry file (or the local alias of a selectively included/imported parameter). Qualified strings such as module.x=... are rejected at the CLI boundary instead of being interpreted as leaf names; the override key never carries module identity.

Entry-file params are the entry DAG's named input ports. Ports not supplied via --set or --input keep their declared defaults; ports without a default are required and must be supplied.

The default --output-view surface prints every const, param, and node declared by the entry DAG plus the outputs intentionally exposed by each include. A brace include contributes only its selected local aliases. A whole-instance include (include ... as alias) contributes its param ports and public consts/nodes under that alias. Successful private implementation values and producer-side duplicates are omitted.

Use --output-view all to debug an included DAG's complete instantiated state, including bound params, private helper nodes, and producer-side values:

graphcal eval mission.gcl --output-view all

The view changes presentation only; it never makes a private declaration referenceable from Graphcal source. Assertions are always reported, and a failed internal value is shown even in surface view so a non-zero exit is never unexplained.

Output entries (text and JSON) are keyed by the full alias-qualified path for whole-instance includes (e.g. good.out, good.v_positive), so multiple instantiations of the same DAG never collide and JSON output never silently drops an instance. This qualification applies only to output names — the --set override surface is unaffected.

Exit codes:

Code Meaning
0 Success, all assertions pass
1 Assertion failure or evaluation error
2 Compile error (parse or type check), invalid --set/--input argument, or internal I/O error

Examples:

# Basic evaluation
$ graphcal eval rocket.gcl
dry_mass   = 1200 kg
fuel_mass  = 2800 kg
isp        = 320 s
g0         = 9.80665 m/s^2
v_exhaust  = 3138.128 m/s
mass_ratio = 3.333333
delta_v    = 3778.220768 m/s
# Override all parameters
$ graphcal eval rocket.gcl --set 'dry_mass=1200.0 kg' --set 'fuel_mass=3500.0 kg' --set 'isp=320.0 s'
dry_mass   = 1200 kg
fuel_mass  = 3500 kg
isp        = 320 s
g0         = 9.80665 m/s^2
v_exhaust  = 3138.128 m/s
mass_ratio = 3.916667
delta_v    = 4284.300858 m/s
# Provide a required param (param declared without a default value)
graphcal eval engine.gcl --set 'dry_mass=800.0 kg'

The JSON parser preserves module-qualified constructor and index paths inside structured value payloads. The top-level JSON keys are still parameter names, and type/dimension checking reports whether the value is valid for that target:

{
  "phase": { "index": "mission.Phase", "entries": { "Burn": 1, "Coast": 0 } },
  "choice": { "variant": "mission.Pick", "fields": { "distance": "2.0 m" } }
}

Complex parameters use the same expression-string leaf form as other quantities, keeping dimensions and units explicit:

{
  "displacement": "complex(3.0 m, 4.0 m)"
}
# JSON output
$ graphcal eval rocket.gcl --format json
{
  "const": {
    "g0": {
      "display_value": 9.80665,
      "si_value": 9.80665,
      "unit": "m/s^2"
    }
  },
  "node": {
    "delta_v": {
      "si_value": 3778.2207684937407,
      "unit": "m/s"
    },
    ...
  },
  "param": {
    "dry_mass": {
      "display_value": 1200.0,
      "si_value": 1200.0,
      "unit": "kg"
    },
    ...
  }
}

A complex output represents each component explicitly. si_value always uses SI base units; display_value is included when -> selected a display unit:

{
  "si_value": { "re": 3.0, "im": 4.0 },
  "display_value": { "re": 300.0, "im": 400.0 },
  "unit": "cm"
}

Multi-indexed values (2D and higher) are displayed as formatted tables:

# Indexed values displayed as tables
$ graphcal eval mission.gcl
delta_v[Departure]  = 2.46 km/s
delta_v[Correction] = 0.12 km/s
delta_v[Insertion]  = 1.83 km/s

spacecraft_mass (kg):
╭─────────┬───────────┬────────────┬───────────╮
          Departure  Correction  Insertion ├─────────┼───────────┼────────────┼───────────┤
 Launch        5000           0          0  Cruise           0        4500          0  Arrival          0           0       4000 ╰─────────┴───────────┴────────────┴───────────╯

1D indexed values remain as flat lines, while 2D values are shown as table grids. 3D and higher values are displayed as multiple 2D table slices with section headers.

When a file contains assertions, they are checked after evaluation and printed below the values:

$ graphcal eval rocket.gcl
dry_mass   = 1200 kg
fuel_mass  = 2800 kg
...

Assertions:
  fuel_budget    PASS
  fuel_positive  PASS
  pressure_safe  FAIL  (assertion evaluated to false)
                       affected: safety_factor

Plot Output

When a file contains plot, figure, or layer declarations, use the --plot option to render them. Three output modes are available:

Browser mode generates a self-contained HTML file with all figures (rendered via Vega-Embed) and opens it in the default browser. Normal evaluation output is still printed according to --format:

graphcal eval analysis.gcl --plot browser

JSON mode prints only a JSON array of figure objects to stdout, useful for piping to other tools or pasting into the Vega-Lite Editor. Normal evaluation output is suppressed in this mode, and --format does not add eval values to stdout:

graphcal eval analysis.gcl --plot json

HTML file mode writes the same self-contained HTML page that browser mode generates to a path of your choice (the path must end in .html) without opening a browser — useful in headless or CI environments. Normal evaluation output is still printed according to --format:

graphcal eval analysis.gcl --plot report.html

Each figure in the array has a name and a spec (Vega-Lite JSON):

[
  { "name": "curve_a", "spec": { /* Vega-Lite spec */ } },
  { "name": "comparison", "spec": { /* Vega-Lite hconcat spec */ } }
]

Each plot declaration produces a standalone figure. Each figure declaration produces a combined chart using Vega-Lite hconcat. Each layer declaration produces a chart using Vega-Lite layer. Plots marked #[hidden] are suppressed from standalone output but still appear in any figure or layer that references them.

If no plot, figure, or layer declarations are found, JSON mode prints [] to stdout and a warning to stderr. Browser mode prints the warning and opens nothing.

See the Plot Declarations reference for the language syntax.


graphcal format

Format .gcl files. When given a directory, recursively formats regular .gcl files within. Symlinked entries found during directory traversal are skipped; an explicitly named symlinked file path is treated like any other file argument and may write through to its target.

Formatting only ever changes layout, never meaning. After producing the formatted text, the formatter re-parses it and verifies the result is the same syntax tree as the input (ignoring source positions). If they ever diverge — which would be a bug in the formatter, not in your code — formatting fails with an error instead of writing a file whose meaning might differ from the source.

graphcal format [OPTIONS] [PATHS]...

Arguments:

Argument Description
[PATHS]... Files or directories to format (default: current directory)

Options:

Option Description
--check Check formatting without modifying files (exit 1 if unformatted)

Examples:

# Format all .gcl files in the current directory
graphcal format

# Format specific files
graphcal format rocket.gcl hohmann.gcl

# Check formatting in CI (non-destructive)
graphcal format --check

graphcal check

Check .gcl files for type/dimension errors without evaluation. Performs parsing and type/dimension checking.

graphcal check [PATHS]...

Arguments:

Argument Description
[PATHS]... Files or directories to check (default: current directory)

Examples:

# Check all .gcl files in the current directory
graphcal check

# Check a specific file
graphcal check rocket.gcl

# Check a directory
graphcal check my_project/

Exit codes:

Code Meaning
0 No errors found
1 Errors detected

graphcal graph

Experimental

graphcal graph is experimental. The DOT output (node naming, labels, styling) and the CLI surface (flags, formats) may change in any release while the DAG visualizer design evolves. The command prints a warning on stderr to that effect; stdout carries only the exported graph.

Export the dependency graph of a .gcl file as text for external rendering tools. The graph is a one-way projection of the compiled program: param, const node, and node declarations become vertices; every @ reference becomes a directed edge from the value being read to the declaration reading it. Inline dag blocks render as nested clusters. Assertions, plots, figures, and layers are not part of the dataflow and are omitted.

graphcal graph [OPTIONS] <FILE>

Arguments:

Argument Description
<FILE> Path to the .gcl file (required)

Options:

Option Description
--format <FORMAT> Output format: dot (default; currently the only format)
--root <ROOT> Project root directory (overrides automatic graphcal.toml detection)

The output is deterministic (declarations keep source order, edges are sorted), so exported graphs diff cleanly in version control.

Exit codes:

Code Meaning
0 Graph exported successfully
2 Compile error (parse or type check) or I/O error

Examples:

# Print Graphviz DOT text to stdout
$ graphcal graph rocket.gcl
digraph graphcal {
    rankdir=LR;
    node [fontname="Helvetica,Arial,sans-serif"];
    "rocket.dry_mass" [label="dry_mass\nMass", shape=ellipse];
    ...
    "rocket.v_exhaust" -> "rocket.delta_v";
}

# Render to SVG with Graphviz
graphcal graph rocket.gcl | dot -Tsvg -o rocket.svg

Node styling encodes the declaration kind: param declarations are ellipses (the graph's inputs), const node declarations are rounded boxes, node declarations are plain boxes, and values imported from other files are dashed boxes labeled with their fully qualified name. Each vertex's label shows the declaration name and its resolved type.


graphcal lsp

Start the Language Server Protocol (LSP) server. The server communicates over stdin/stdout and is intended to be launched by an editor.

graphcal lsp

This command has no additional options. See Editor Setup for how to configure your editor to use the LSP.

LSP Features

The LSP server provides:

  • Diagnostics -- Real-time parse errors, dimension mismatches, unknown references, assertion failures
  • Inlay hints -- Computed param/node values displayed inline
  • Go to definition -- Navigate from references to declarations
  • Hover -- Show resolved type and dimension information
  • Find references -- Find all usages of a declaration
  • Document symbols -- Outline view of all declarations
  • Formatting -- Format the current document
  • Document links -- Clickable import paths