CLI

The Norn CLI runs .norn workflows, agent steps, and tests from the terminal. Use it for local execution, CI pipelines, named requests, and report generation.

Installation

Install the published CLI package globally via npm:

shell
npm install -g norn-cli

Updating the CLI

norn update upgrades that global installation to the latest published release. It is equivalent to npm install --global norn-cli@latest, and it runs npm directly in your terminal so npm's progress, authentication prompts, and errors stay visible.

shell
norn update

# Explain the command without running it
norn update --help

# Confirm the result
norn --version

The command only touches the global CLI. It never modifies the current project's dependencies or lockfile, so it is safe to run from inside a repository. On Windows the same npm command runs through cmd.exe. It accepts no arguments other than --help, and it exits 1 with the npm command to run by hand when npm cannot be started or the install does not succeed.

Usage

shell
norn <file.norn|directory> [options]

When you pass a directory, Norn recursively discovers .norn files and runs their test sequence blocks.

Basic Commands

shell
# Run all test sequences in a file
norn api-tests.norn

# Run all test sequences in a directory
norn tests/

# Run one sequence by name
norn api-tests.norn --sequence AuthFlow

# Run one named request by name
norn api-tests.norn --request LoginRequest

# Show the installed CLI version
norn --version

Hosted and CI Sequence Runs

A hosted or CI caller usually runs one named sequence, decides its inputs at run time, and keeps the resulting agent recording as a build artifact. Three flags cover that. Each of them requires a single .norn file together with --sequence.

shell
norn workflows/ticket.norn \
  --sequence HandleTicket \
  --param jiraTicket=NORN-482 \
  --param-json context=./inputs/ticket-context.json \
  --recording ./artifacts/ticket-run.json \
  --json

Sequence Parameters From the Command Line

The sequence declares its parameters as usual, with or without authored defaults.

workflows/ticket.norn
import "./agents.nornai"

test sequence HandleTicket(jiraTicket, context, priority = "normal")
    var verdict = run TicketRouter context

    assert verdict.text exists
end sequence

--param [name=]value is repeatable and keeps the value as a string, so ticket keys, numeric-looking account ids, and values that themselves contain = arrive unchanged. Without a name, values fill the sequence declarations from left to right.

--param-json [name=]file.json is also repeatable. It reads the file, parses it, and assigns the typed JSON value — object, array, string, number, boolean, or null — directly to the named parameter, or to the next undeclared-by-name parameter when the name is omitted. Named and positional forms can be mixed; positional values skip parameters already supplied by name. The explicit flag is deliberate: Norn never guesses that a parameter value ending in .json is a file path. Paths are resolved from the working directory of the CLI process, not from the .norn file, and the file must use a .json extension.

shell
# Strings stay strings
norn workflows/ticket.norn --sequence HandleTicket --param NORN-482

# Named and positional inputs can be mixed
norn workflows/ticket.norn --sequence HandleTicket --param NORN-482 --param-json context=./inputs/ticket-context.json --param priority=urgent

# A JSON file becomes a typed object, array, or scalar
norn workflows/ticket.norn --sequence HandleTicket --param-json context=./inputs/ticket-context.json

Command-line values override authored defaults. Before any step runs and before any model is called, the run fails when a supplied name is not declared by the sequence, when the same name is supplied twice across either flag, when positional values outnumber the remaining declarations, when a required parameter has neither a command-line value nor an authored default, or when a JSON file is missing, unreadable, or malformed. A second bare positional argument after the input path is rejected with --param guidance. Parameter flags apply to a single selected run, so they cannot be combined with @data or @theory cases.

Recording to an Exact Path

--recording <file.json> writes the normal secret-masked agent recording to that exact path instead of the rolling .norn-cache/runs/ cache, which makes it straightforward to publish as a CI artifact.

shell
norn workflows/ticket.norn \
  --sequence HandleTicket \
  --param jiraTicket=NORN-482 \
  --recording ./artifacts/ticket-run.json

Parent directories are created as needed and an existing file is replaced, matching how explicit report paths behave. The output must use a .json extension. An explicit request wins over agents.recording.enabled: false, because that setting governs automatic rolling-cache recordings rather than a recording the caller asked for. The artifact does not also consume one of the 20 cache slots, and a sequence that never reaches an agent finishes normally without inventing an empty recording. The file replays like any other recording with norn replay.

Cancellation Signals

Sequence runs treat SIGINT and SIGTERM as graceful cancellation requests, which matters when a container or CI runner stops a job. The first signal cancels provider, HTTP, wait, script, SQL, nested-sequence, and MCP work through the shared runtime and lets MCP sessions close; a second signal forces an immediate exit. Lifecycle messages go to stderr, so --json stdout stays parseable.

Agent Runs and Replay

Sequences that call agents from a .nornai or .nornagent sidecar run from the CLI like any other file, and produce the same nested contract and retry trace you see in VS Code. The CLI accepts both extensions everywhere — imports, parsing, validation, recording, and replay — so a project can use either, or mix them. A failed judged expectation fails the sequence and exits non-zero.

While an agent run is in flight, an interactive terminal shows what it is doing right now on one line that updates in place with an elapsed timer: preparing tools and context, waiting for a model response, calling an MCP tool, loading knowledge, or invoking a child agent. The line follows the deepest active invocation and falls back to its parent as children finish. It is written to stderr, never names prompts, arguments, or results, and is erased before the completed agent trace prints. Non-interactive runs — CI, piped output, and --json — produce exactly the same output as before, and nothing about this live line reaches recordings, replay, or reports.

Agent recording is enabled by default, with recordings stored under .norn-cache/runs/. Replay one without model calls, MCP calls, or other side effects:

shell
norn replay .norn-cache/runs/<recording>.json
norn replay .norn-cache/runs/<recording>.json --json
norn replay .norn-cache/runs/<recording>.json --from CriteriaComparer

Pure replay reproduces the stored trace and exits non-zero for failed hops or contracts. A recording can be used as an offline CI fixture when any masked environment-secret placeholders it references are available. --from replays the prefix and runs that hop onward live. See AI Agents for the full model.

Shared Runtime Features

The CLI runs the same shared runtime as the VS Code extension. Files that use run sql or run mcp behave the same way in terminal runs as they do in editor runs.

shell
# Run a file that includes MCP steps
norn smoke-mcp.norn --sequence TimeTools --env prelive

# Run a suite that includes SQL, scripts, HTTP, and MCP steps
norn tests/ --env prelive

Request Timeouts

HTTP requests use a 30 second timeout by default. Teams can set a shared timeout in norn.config.json, and CI can override it for a single run with --timeout.

norn.config.json
{
  "version": 1,
  "http": {
    "timeoutMs": 180000
  }
}
shell
# Override the project timeout for this run
norn tests/ --timeout 180s
norn tests/ --timeout 3m
norn tests/ --timeout 300000ms

The CLI accepts ms, s, and m suffixes. Bare numbers are treated as seconds for command-line compatibility. Precedence is --timeout, then norn.config.json, then the built-in 30 second default.

Environment Selection

shell
norn tests/ --env staging
norn tests/ -e production

Environment File Refactors

The CLI can print or apply the same stage/region .nornenv refactor that appears in VS Code. Use it when a file has flat environment names such as dev_us, dev_uk, prod_us, and prod_uk.

shell
# Preview the refactored .nornenv content
norn .nornenv --refactor-region-pattern

# Rewrite the file in place
norn .nornenv --refactor-region-pattern --write

Tag Filtering

--tag uses AND logic when repeated. --tags uses OR logic for a comma-separated list.

shell
# Single tag
norn tests/ --tag smoke

# All listed tags must match (AND)
norn tests/ --tag smoke --tag critical

# Any listed tag may match (OR)
norn tests/ --tags smoke,critical

# Key-value tag
norn tests/ --tag "team(CustomerExp)"

Reports

Use explicit file paths when you want one report, or --output-dir to auto-generate timestamped report names.

shell
# Explicit report files
norn tests/ --junit results/junit.xml
norn tests/ --html results/report.html

# Auto-generated report filenames
norn tests/ --output-dir results

Output and Diagnostics

Contract assertions are summarized by schema name in terminal output, including validation counts and the first issues when a schema check fails.

text
✓ GET-todos-1.schema.json: 4/4 validations passed
shell
norn tests/ --json
norn tests/ --verbose
norn tests/ --timeout 180s
norn tests/ --no-fail
norn tests/ --no-redact
norn api-tests.norn --insecure

CI/CD Example

GitHub Actions

workflow.yml
name: Norn Workflows

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '24'
      
      - name: Install Norn CLI
        run: npm install -g norn-cli
      
      - name: Prepare report folder
        run: mkdir -p results
      
      - name: Run Tests
        run: norn tests/ --env ci --junit results/junit.xml
      
      - name: Upload Results
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: results/

Exit Codes

  • 0 - Run completed successfully
  • 1 - A test failed, a request failed, or Norn hit a configuration/runtime error
  • 130 - The run was cancelled by SIGINT
  • 143 - The run was cancelled by SIGTERM