> ## Documentation Index
> Fetch the complete documentation index at: https://laminar.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI

The `lmnr-cli` is a command-line tool for onboarding a project, querying Laminar data, managing datasets, Signals, and LLM profiles, and annotating debug sessions from your terminal. It is the fastest way to wire up a new project, pull traces into a shell pipeline, script dataset maintenance, or connect an AI coding agent that can shell out, all without leaving the command line.

<Note>
  The CLI is a standalone npm package (`lmnr-cli`) and ships independently from the `@lmnr-ai/lmnr` SDK. You do not need the SDK installed to onboard a project, query data, or manage datasets.
</Note>

## Install

```bash theme={null}
# Run directly with npx (no install)
npx lmnr-cli@latest <command>

# Or install globally
npm install -g lmnr-cli
```

## Quick start

`lmnr-cli setup` takes you from a fresh install to an instrumented project in one command:

```bash theme={null}
lmnr-cli setup
```

It opens your browser to authorize the CLI, then:

1. Logs you in (if you are not already).
2. Lets you pick or create a project.
3. Mints a project API key for your application's SDK and writes `LMNR_PROJECT_API_KEY=...` to your env file (an existing `.env.local` if you have one, otherwise `.env`). If a valid key for this project is already configured, it keeps that one.
4. Links the directory by writing `.lmnr/project.json`.
5. Installs the Laminar agent skill into your project (`.claude/`, `.cursor/`, `.codex/`, or `.agents/`).

The CLI itself never uses that key: it authenticates as you (see [Authenticate](#authenticate)). The key is purely for the SDK in your app to send traces.

Add the SDK to your app, run it, then confirm traces are landing:

```bash theme={null}
lmnr-cli sql query "SELECT * FROM traces ORDER BY start_time DESC LIMIT 1"
```

`setup` is designed to be driven by coding agents. Pass `--json` for a single machine-readable line on stdout:

```bash theme={null}
lmnr-cli setup --json
```

Useful flags:

```bash theme={null}
lmnr-cli setup --project-id <uuid>   # Disambiguate when you can access multiple projects
lmnr-cli setup --no-write-env        # Skip writing ./.env
lmnr-cli setup --no-browser          # Print the device-flow URL instead of opening it
```

Re-running `setup` in the same directory reuses the linked project and keeps a working key in place: if a valid key for this project is already configured (in your environment or env file), it leaves it untouched. If your API key is invalid or revoked it will mint a new one. If your API key does not match the project configured in `.lmnr/project.json` it will abort with an error. Keys stay visible (and revocable) in **Settings → Project API Keys**.

## Authenticate

Every CLI command authenticates as you, the signed-in user. Log in once with `lmnr-cli login` (or run `lmnr-cli setup`, which logs you in as its first step):

```bash theme={null}
lmnr-cli login
```

This starts an OAuth device flow: the CLI prints a verification URL and code and opens your browser. Approve the request to finish signing in. Credentials are stored at `~/.config/lmnr/credentials.json` (fallback precedence `&XDG_CONFIG_HOME/`, `%APPDATA%\`, `~/.config/`). The short-lived access token is refreshed automatically as it nears expiry; if your session is revoked, run `lmnr-cli login` again.

```bash theme={null}
lmnr-cli logout   # Remove the stored credentials
```

## Self-hosting

For a self-hosted deployment, point the CLI at your instance. `login` talks to your frontend (the auth issuer); the data commands (`sql`, `dataset`, `signal`, `llm-profile`, `project`, `debug`) talk to your API.

The cleanest way is to set the URLs once via environment variables. The CLI reads them from your environment and from a `.env` or `.env.local` file in the current directory.

```bash theme={null}
LMNR_FRONTEND_URL=https://example.com   
LMNR_BASE_URL=https://api.example.com  
LMNR_HTTP_PORT=8000                             
```

With those set, every command works as usual:

```bash theme={null}
lmnr-cli login
lmnr-cli sql query "SELECT 1"
```

You can also pass the URLs per command instead. `login` takes `--frontend-url`; the data commands take `--base-url` (and `--port`):

```bash theme={null}
lmnr-cli login --frontend-url https://laminar.example.com
lmnr-cli sql query "SELECT 1" --base-url https://api.laminar.example.com
```

<Note>
  The CLI does not use a project API key. The `LMNR_PROJECT_API_KEY` that `lmnr-cli setup` writes to your env file is for the Laminar SDK in your application to send traces; the CLI authenticates as you instead.
</Note>

## Directory-scoped projects

Every project command (`sql`, `dataset`, `signal`, `llm-profile`, `project`, `debug`) resolves which project it targets from the directory you run in. `lmnr-cli setup` writes a `.lmnr/project.json` link file at the project root (it holds the project id and display details, never secrets). Commands then resolve the project in this order:

1. `--project-id <id>` flag.
2. The nearest `.lmnr/project.json`, walking up from the current directory.

So once a directory is linked, every command in it (or any subdirectory) targets that project with no extra flags. Override per command with `--project-id`.

List the projects you can access and see which one is linked here:

```bash theme={null}
lmnr-cli project list
lmnr-cli project list --json
```

The linked project is marked with a `●`.

## Query data with SQL

Run ClickHouse SQL against your project's spans, traces, signal events, and more. `sql` runs against the linked project, so `lmnr-cli login` plus a linked directory (or `--project-id`) is all you need:

```bash theme={null}
lmnr-cli sql query "SELECT name, duration FROM spans WHERE start_time > now() - INTERVAL 1 HOUR LIMIT 20"
```

Add `--json` to emit structured JSON on stdout (messages and errors go to stderr), which makes it trivial to pipe into `jq`, a local script, or an AI coding agent:

```bash theme={null}
lmnr-cli sql query "SELECT trace_id, total_cost FROM spans WHERE span_type = 'LLM' LIMIT 10" --json \
  | jq '.[] | select(.total_cost > 0.01)'
```

List the available tables and columns:

```bash theme={null}
lmnr-cli sql schema
```

For the full schema and query guide, see [SQL Editor](/docs/platform/sql-editor).

<Note>
  Only `SELECT` queries are allowed. The query runs scoped to your project automatically; no tenant filter is needed in your WHERE clause.
</Note>

## Ask the agent

When you do not want to write the SQL yourself, ask [Laminar Agent](/docs/platform/laminar-agent) a question in plain language. It runs read-only SQL and reads trace context for you, then answers grounded in your project's data:

```bash theme={null}
lmnr-cli ask "What failed in the most recent run for my dev-claude session?"
```

Like the other commands, `ask` runs against the linked project and authenticates as the signed-in user, so `lmnr-cli login` plus a linked directory (or `--project-id`) is all you need.

```bash theme={null}
lmnr-cli ask "List the 5 slowest traces in the last hour and explain the slowest one"
lmnr-cli ask "How much did we spend on LLM calls today, broken down by model?" --project-id <uuid>
```

### Continue a conversation

Each answer prints a conversation id after it. Pass that id to `--conversation` on your next question to continue the same conversation, so the agent keeps the earlier context and you can ask follow-ups:

```bash theme={null}
lmnr-cli ask "What failed in the most recent run for my dev-claude session?"
# ...answer, then: continue with: lmnr-cli ask "<question>" --conversation <id>
lmnr-cli ask "and which model did it use?" --conversation <id>
```

With `--json`, the conversation id comes back as `conversationId` in the output (`{ answer, conversationId, tools }`), so a script can capture it and thread it through a multi-turn exchange:

```bash theme={null}
lmnr-cli ask "summarize the most expensive trace today" --json
```

See [Laminar Agent](/docs/platform/laminar-agent) for the full list of what it can do and the other surfaces (MCP, Slack) it runs on.

## Manage Signals

Create, inspect, update, and delete [Signals](/docs/signals/introduction) from the terminal.

```bash theme={null}
lmnr-cli signal create "Refund requests" \
  --prompt "Detect when the user asks for a refund. Extract the reason they gave." \
  --schema '{"properties":{"reason":{"type":"string","description":"Refund reason"}}}'

lmnr-cli signal list
lmnr-cli signal get "Refund requests" --json
lmnr-cli signal versions "Refund requests"
lmnr-cli signal update "Refund requests" --mode batch
lmnr-cli signal delete "Refund requests"
```

See [Signals CLI](/docs/signals/cli) for the full reference, including the `--llm-profile-id` and `--model` pair that routes a Signal through an [LLM profile](/docs/platform/llm-profiles) on self-hosted deployments.

## Manage LLM profiles

An [LLM profile](/docs/platform/llm-profiles) bundles a provider, its credentials, and the models you want into one workspace-level entry. `llm-profile` is full CRUD over them, so you can provision providers from a script instead of the settings page.

```bash theme={null}
lmnr-cli llm-profile list
lmnr-cli llm-profile get <profile-id> --json

lmnr-cli llm-profile create "Staging OpenAI" \
  --provider openai_responses --api-key sk-... \
  --model gpt-5-mini --model gpt-4.1-mini

lmnr-cli llm-profile update <profile-id> --model gpt-5
lmnr-cli llm-profile delete <profile-id>
```

Secrets are write-only: reads return a mask, and an update that omits a secret keeps the stored value. See [LLM profiles from the CLI](/docs/platform/llm-profiles#manage-profiles-from-the-cli) for the per-provider flags and the update rules.

## Manage datasets

Create, list, inspect, rename, delete, push, and pull datasets. File operations support `.jsonl`, `.json`, and `.csv`. Like `sql`, these commands run against the linked project.

```bash theme={null}
# List all datasets in the project
lmnr-cli dataset list --json

# Inspect, rename, or delete a dataset by its canonical ID
lmnr-cli dataset get <dataset-id> --json
lmnr-cli dataset update <dataset-id> --name renamed-dataset
lmnr-cli dataset delete <dataset-id>

# Push new datapoints into an existing dataset
lmnr-cli dataset push data.jsonl -n my-dataset

# Pull a dataset down to a local file
lmnr-cli dataset pull output.jsonl -n my-dataset

# Create a new dataset from a file, writing a local copy with dataset IDs
lmnr-cli dataset create my-dataset data.jsonl -o my-dataset.jsonl
```

See [Datasets CLI](/docs/datasets/cli) for the full dataset workflow including versioning semantics.

## Annotate debug sessions

These commands are the operational layer for the [Debugger](/docs/debugger/process): you run the agent with `LMNR_DEBUG=true`, then use these to name the session, write notes, and open the session view. Like the other commands, they run against the linked project.

<Note>
  None of these commands need an id passed in. By default they target the session in `.lmnr/debug-session.json` (the most recent `LMNR_DEBUG=true` run in this directory). Pass `--session-id` to target a different one.
</Note>

### debug session add-note

Attach a markdown note to the debug session. Each call adds a standalone note, slotted into the session timeline by time between the session's runs and evaluations, both in the session view and in `debug session summary`. The note text is the only required argument.

```bash theme={null}
lmnr-cli debug session add-note "## What this run showed
Length cap is working. Next: check citations."

lmnr-cli debug session add-note "Length cap is working." --session-id <session-id>
```

Span references in the note are written as XML tags; the Laminar UI renders them as clickable chips:

```bash theme={null}
lmnr-cli debug session add-note "<span id='<spanId>' name='synthesis call' />"
```

### debug session new

Mint a fresh debug session and reset `.lmnr/debug-session.json` to it. The next `LMNR_DEBUG=true` run in this directory rejoins the new session instead of continuing the previous one. Use this to start a clean investigation.

```bash theme={null}
lmnr-cli debug session new
lmnr-cli debug session new --json
lmnr-cli debug session new --no-browser
```

The bare session id prints to stdout (so an agent can capture it); `--json` emits `{"sessionId","projectId","debuggerUrl"}` instead. The command opens the session in your browser unless you pass `--no-browser`.

### debug session open

Open the debug session's debugger page in your browser. With no flag it opens the session in `.lmnr/debug-session.json`; pass `--session-id` to open another. The URL also prints to stdout (`--json` emits `{"sessionId","debuggerUrl"}`). This command is local-only: no login or network call.

```bash theme={null}
lmnr-cli debug session open
lmnr-cli debug session open --session-id <session-id>
```

### debug session set-name

Set the display name of the current debug session (the one in `.lmnr/debug-session.json`), or pass `--session-id` to name another. The session must already exist (created by a `LMNR_DEBUG=true` run or `debug session new`). You can also rename a session inline by clicking its title in the session view.

```bash theme={null}
lmnr-cli debug session set-name "Fix report length + search tool"
lmnr-cli debug session set-name "Fix report length" --session-id <session-id>
```

### debug session summary

Print the debug session's full timeline, oldest first: every run, evaluation, note, and tracked command. Use this to re-orient in an ongoing session after a context reset. Defaults to the current session; pass `--session-id` for another.

```bash theme={null}
lmnr-cli debug session summary
lmnr-cli debug session summary --session-id <session-id> --json
```

Traces and evaluations print as self-closing `<trace id="..."/>` and `<evaluation id="..."/>` tags you can feed into SQL queries; [tracked commands](#tracking-cli-commands-in-the-debugger) print as `<command exitCode="..." reasoning="...">...</command>` tags; notes print as their raw markdown. With `--json`, the output is an array of `{"id","createdAt","type","content"}` blocks.

See [Debugger](/docs/debugger/process) for the full record/replay process.

## Tracking CLI commands in the debugger

When a debug session is active in the current directory (any `LMNR_DEBUG=true` run has written `.lmnr/debug-session.json`), your investigative commands are recorded into that session automatically. A reviewer watching the session then sees exactly what the agent asked, not just the runs and notes around it.

Two commands are tracked: [`sql query`](#query-data-with-sql) and [`ask`](#ask-the-agent). Each tracked run becomes a `command` block in the session timeline, carrying:

* The command and its arguments (the raw SQL or the raw question).
* The exit code.
* The captured stdout/stderr output.

The block renders as a terminal-style row in the session view (red-accented when the command exited non-zero) and as a `<command>` tag in [`debug session summary`](#debug-session-summary). Tracking is best-effort: it never changes a command's output or exit code, and it stays silent on a normal run.

### Explain the intent with `--reasoning`

Pass `--reasoning "<why you're running this>"` to attach a short explanation to the command block. It shows in the UI on the block (and in the session summary), so a human following the session understands *why* the agent ran a query without reading the raw SQL. Keep it to a short phrase, about 15 words.

```bash theme={null}
lmnr-cli sql query "SELECT status, count(*) FROM traces GROUP BY status" \
  --reasoning "checking how many runs errored in this session"

lmnr-cli ask "why did the most recent run fail?" \
  --reasoning "triaging the failure before editing the prompt"
```

<Note>
  `--reasoning` is recorded only when a debug session is active in the directory. With no active session the flag is ignored and the CLI warns you; start one with `lmnr-cli debug session new`.
</Note>

### Disable tracking

Tracking is on by default whenever a session is active. Turn it off for a single command with `--no-track`, or for the whole shell by setting `LMNR_NO_COMMAND_TRACKING=1`:

```bash theme={null}
lmnr-cli sql query "SELECT 1" --no-track      # this command only
export LMNR_NO_COMMAND_TRACKING=1             # every command in this shell
```

## Piping and agent-friendly output

The CLI is designed to be scriptable and to plug cleanly into AI coding agents:

* **One-shot onboarding.** `lmnr-cli setup --json` emits a single machine-readable line (project id, minted key, dashboard URL) and uses distinct exit codes so an agent can branch on the failure mode: `6` login failed, `7` no project to select, `8` `.env` write failed, `9` key minting failed, `10` project discovery failed.
* **Structured output on stdout, logs on stderr.** Every command that supports it takes `--json` and prints machine-readable output to stdout, while human-friendly progress messages are written to stderr. This keeps pipes clean.

Combined with `sql query --json`, this makes the CLI a drop-in SQL layer for any agent that can run shell commands, with no SDK install required.

## Help

Every command accepts `-h` / `--help`:

```bash theme={null}
lmnr-cli --help
lmnr-cli setup --help
lmnr-cli login --help
lmnr-cli project --help
lmnr-cli sql --help
lmnr-cli sql query --help
lmnr-cli dataset --help
lmnr-cli signal --help
lmnr-cli llm-profile --help
lmnr-cli debug --help
lmnr-cli debug session --help
lmnr-cli debug session new --help
lmnr-cli debug session open --help
```

## What's next

<CardGroup cols={2}>
  <Card title="SQL Editor" href="/docs/platform/sql-editor">
    The UI counterpart for ad-hoc SQL queries, with schema reference and examples.
  </Card>

  <Card title="MCP Server" href="/docs/platform/mcp">
    Connect Claude Code, Cursor, or any MCP client to Laminar directly.
  </Card>

  <Card title="Datasets CLI workflow" href="/docs/datasets/cli">
    End-to-end example of pulling, editing, and pushing a dataset.
  </Card>

  <Card title="Debugger" href="/docs/debugger/introduction">
    Rerun your agent from a checkpoint and prove the fix with an eval.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.