lemma 0.9.9

A pure, declarative language for business rules.
Documentation
---
nav_title: Lemma CLI
nav_order: 20
---

# Lemma CLI

See [Installation](../installation.md) for install options.

## Commands

### `lemma run`: evaluate a spec

```bash
lemma run [[repo] spec] [name=value ...] [--prefix PATH] [--rules=RULES] [options]
```

**Syntax:**
- Positionals: optional repository qualifier (e.g. `@iso/countries`), then spec name (see `lemma run --help`)
- `spec --rules=rule`: evaluate one rule
- `spec --rules=rule1,rule2`: evaluate specific rules (comma-separated)
- No arguments with `-i`: interactive mode

**Options:**
- `--prefix <path>`: workspace directory or `.lemma` file (default: current directory)
- `--rules <rules>`: comma-separated rule names (omit to evaluate all)
- `--json`: output results as JSON (default: human-readable table). Each rule result may include `missing_data` (unbound input keys in evaluation / decision-tree order). Types, filled values, and suggestions come from `lemma show`, not from evaluate JSON.
- `-x, --explain`: include explanation trees (human: reasoning tables; JSON: per-rule `explanation` objects matching [`api.v1.json`](../../../engine/schemas/api.v1.json)). Human output prints **Missing data** only for rules still awaiting input (`MissingData` veto), not leftover live keys on a settled value or UserDefined/Computation answer. JSON still carries raw per-rule `missing_data` from the engine.
- `-i, --interactive`: guided spec/rule/data selection
- `--effective <datetime>`: evaluate at effective datetime (e.g. `2025`, `2025-03`, `2025-03-04`)

**Examples:**

```bash
lemma run pricing
lemma run pricing --rules=total,tax
lemma run --prefix ./policies nl/tax/net_salary --rules=net_salary -x
lemma run pricing quantity=10 is_vip=true
lemma run pricing --json
lemma run pricing -x
lemma run pricing --effective 2025-01-01
lemma run -i
lemma run '@iso/countries' alpha2
```

### `lemma show`: declared data catalog and rules

Shows declared data slots with types and constraints (minimum, maximum, units, decimals, text options), filled values, suggestions, and rule result types. Lemma source text is available via `Engine::source` (API) only.

`show` lists every declared promptable data slot (types, constraints, `fill`, suggestions). `needed_by_rules` names local rules that still need the slot after normalize; empty means offered for reuse (`data x: alias.slot`), not an eval intake key for this spec. Run-data-aware pruning for a concrete `run` is per-rule `results.*.missing_data`.

```bash
lemma show [[repo] spec] [--prefix PATH] [--effective <datetime>] [--json]
```

**Options:**
- `[repo]`: optional repository qualifier (e.g. `@iso/countries`)
- `[spec]`: spec name (omit when workspace has a single spec)
- `--prefix <path>`: workspace directory or `.lemma` file (default: current directory)
- `--effective <datetime>`: effective datetime for temporal specs
- `--json`: output as JSON (default: human-readable table)

**Examples:**

```bash
lemma show pricing
lemma show --prefix ./policies net_salary
lemma show --prefix tax.lemma calculator
lemma show '@iso/countries' alpha2
lemma show pricing --json
```

### `lemma list`: list loaded specs by repository

Lists every loaded spec, grouped by repository. Local specs (no repository qualifier) are printed unindented; named repositories (including embedded `lemma`) appear as headers with indented spec names.

```bash
lemma list [--prefix PATH] [--json]
```

**Options:**
- `--prefix <path>`: workspace directory or `.lemma` file (default: current directory)
- `--json`: output `Engine::list()` JSON: array of `{ "repository", "specs" }` where each spec is a `ListedSpec` (`name`, optional `effective_from` / `effective_to`). Human text lists unique spec names only.

**Examples:**

```bash
lemma list
lemma list --prefix ./project
lemma list --json
```

### `lemma install`: install repositories from LemmaBase

Resolves `@...` references and downloads repositories from LemmaBase into `lemma_deps/`.

```bash
lemma install [--prefix PATH] --all
lemma install [--prefix PATH] <repository> -f
```

**Options:**
- `--prefix <path>`: workspace directory or `.lemma` file (default: current directory)
- `-a, --all`: install all `@...` references in the workspace
- `-f, --force`: overwrite existing repositories when content has changed on LemmaBase

### `lemma format`: format .lemma files

```bash
lemma format [paths...] [--check] [--stdout]
```

**Options:**
- `--check`: check formatting without modifying (exit 1 if any file would change)
- `--stdout`: write formatted output to stdout

### `lemma server`: start HTTP server

```bash
lemma server [--prefix PATH] [--host <host>] [-p <port>] [--watch] [--explain] [--eval-timeout SECONDS] [--cors]
```

**Options:**
- `--prefix <path>`: workspace directory or `.lemma` file (default: current directory)
- `--host <host>`: bind address (default: `127.0.0.1`)
- `-p, --port <port>`: port (default: `8012`)
- `--watch`: live-reload on `.lemma` file changes
- `--explain`: enable explanation generation (clients send `x-explain` header; JSON shape [`api.v1.json`](../../../engine/schemas/api.v1.json))
- `--eval-timeout <second>`: wall-clock timeout for a single evaluation request (default: `10`)
- `--cors`: allow cross-origin browser requests from any origin (off by default)

SIGINT/SIGTERM stop accepting new connections, drain in-flight requests up to `--eval-timeout`, then exit 0.

**Routes:**

| Method | Route | Description |
|--------|-------|-------------|
| GET | `/` | List all specs |
| GET | `/{spec}` | Show spec interface (data, rules, versions) |
| POST | `/{spec}` | Evaluate (data as JSON or form body) |
| GET/POST | `/{spec}/{rules}` | Evaluate specific rules (comma-separated) |
| GET | `/openapi.json` | OpenAPI 3.1 specification |
| GET | `/docs` | Interactive API documentation (Scalar) |
| GET | `/health` | Health check |

**Example:**

```bash
lemma server --prefix ./policies --watch

curl "http://localhost:8012/pricing?quantity=10&is_member=true"

curl -X POST http://localhost:8012/pricing \
  -H "Content-Type: application/json" \
  -d '{"quantity": 10, "is_member": true}'
```

### `lemma lsp`: start language server

Starts the Language Server Protocol server over stdio for editor integration (diagnostics, formatting, semantic tokens). The VS Code/Cursor extension invokes this automatically; a globally installed `lemma` CLI is the only requirement.

Workspace file discovery uses the same policy as `lemma list` / `lemma run`: project `.gitignore` rules, plus always loading `<workdir>/lemma_deps/**/*.lemma`. The CLI injects that discovery and a filesystem watch into the LSP. The watch covers discovered workspace `.lemma` files and `lemma_deps/` only (not a recursive whole-tree watch), so disk changes such as `lemma install` update diagnostics without hanging on large build directories. Editor open/change/save/close remain the primary sync path for buffers.

```bash
lemma lsp
```

### `lemma mcp`: start MCP server

Start the MCP server over stdio so AI assistants can work with workspace specs. Full setup guide: [MCP](../tools/mcp.md).

```bash
lemma mcp [--prefix PATH] [--write] [--request-timeout SECONDS]
```

**Options:**
- `--prefix <path>`: workspace directory or `.lemma` file (default: current directory)
- `--write`: enable write tools (read-only by default)
- `--request-timeout <seconds>`: wall-clock timeout for a single request (default: `10`)

## Workspace

A workspace is a directory containing `.lemma` files. Commands that load specs use `--prefix` to select the workspace (default: current directory). Every `.lemma` file is loaded recursively from that directory, plus any repositories in `lemma_deps/`.

```
policies/
  pricing.lemma
  shipping.lemma
  tax.lemma
```

## Resource Limits

Resource limits control parse-time and planning-time budgets. These are security boundaries that prevent unbounded resource consumption from untrusted input.

| Limit | Default | Purpose |
|-------|---------|---------|
| `max_sources` | 4096 | Maximum source files in one engine |
| `max_loaded_bytes` | 50 MB | Total source text across all files |
| `max_source_size_bytes` | 5 MB | Single source file size |
| `max_expression_depth` | 7 | AST nesting depth |
| `max_expression_count` | 65,536 | Expression nodes per source (parser) |
| `max_normalized_expression_nodes` | 30,000 | Unique normal-form cells reachable from one rule root after normalize (rule embeds count as one cell) |
| `max_normal_form_depth` | 4096 | Nesting depth of a rule's normalized NormalForm DAG (rule embeds count as one level) |
| `max_data_value_bytes` | 1 KB | Single data value size |
| `max_spec_dependency_depth` | 32 | `uses` chain depth |
| `max_dag_specs` | 4096 | Total specs in dependency DAG |

`max_expression_depth` and `max_spec_dependency_depth` bound recursion during parsing and planning. Raising these beyond the defaults requires stack analysis for your deployment.

## API Defaults

**Effective datetime**: when no `--effective` flag or `Accept-Datetime` header is provided, the engine uses the current instant to select the temporal version of the root spec.

**Accept-Datetime (HTTP)**: clients send `Accept-Datetime` with the same formats as `--effective`: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`, or an ISO 8601 datetime. Empty or omitted → now. Invalid values are a bad request. Responses include `Vary: Accept-Datetime`. When the resolved spec row has an `effective_from`, the server also sets `Memento-Datetime` to that instant.

**Explanations**: disabled by default in CLI (`lemma run`), HTTP, and SDKs. Use `--explain` (CLI and server) + `x-explain` (HTTP client), or `explain: true` (SDK) to opt in. MCP `run`/`evaluate` always return formatted ASCII explanation trees (no `explain` arg, no opt-out) plus a `Missing data` block when inputs are unbound. Evaluate/run JSON (CLI `--json`, HTTP, SDKs) has no top-level `data` array: unbound inputs are per-rule `missing_data`; types and suggestions come from `lemma show` / MCP `show`. When explanations are enabled on JSON surfaces, each `results.<rule>.explanation` is a rule node (`"type":"rule"`, `"name"`, `"result"`, `"body"`, optional `"causes"` / `"children"`) per [`api.v1.json`](../../../engine/schemas/api.v1.json). Bound data uses `"type":"data"`, unused cause paths `"type":"data_unused"`.

## See Also

- [Learn guide](../learn/readme.md)
- [Installation](../installation.md)
- [Language reference](readme.md)
- [API schema](../../../engine/schemas/api.v1.json) (Show, Response, list, errors, `RuleResult.explanation` / `ExplanationNode`)
- [LemmaBase](registry.md)
- [Engine test coverage](coverage/engine.md)
- [CLI test coverage](coverage/cli.md)
- [CLI benchmarks](benchmarks/cli.md)
- [Engine benchmarks](benchmarks/engine.md)