Login
Free Sign Up
Docs
/

CLI Command Reference

@ligantic/cli provides the ligantic binary. It is a thin layer over @ligantic/sdk: the same result-based operations, rendered as JSON when stdout is not a terminal (scripts, agents, CI) and as tables on a terminal. See Output and Errors.

This page covers the hand-written commands. Every other public API operation is available as a command generated from the OpenAPI spec (for example ligantic space secrets list or ligantic endpoints publish); those are listed in Generated CLI commands. Find either kind with ligantic cli search.

  • Package: @ligantic/cli
  • Binary: ligantic
  • Requires Node.js 22+
npm install -g @ligantic/cli

Global Options

Option

Description

--space <id>

Override the current Space for this command.

--profile <name>

Use this authentication profile for this command only, without changing currentProfile. Also settable with LIGANTIC_PROFILE.

--json

Force JSON output. This is the default when stdout is not a terminal.

--table

Force human-readable tables. This is the default on a terminal. Cannot be combined with --json.

-V, --version

Print the CLI version.

-h, --help

Print help.

Every resource command (spaces, schemas, records, flows, and the generated commands) requires an API key to be available. When no key is resolved, those commands fail with not authenticated and exit code 3; auth and config commands work without a key.

Resource commands are plural-agnostic: the plural form is canonical and the singular is an alias (spaces/space, schemas/schema, records/record, flows/flow) — both do the same thing.

Verb Aliases

Commands accept common kubectl/git-style verb aliases in addition to their canonical name, so ligantic schemas get customer is equivalent to ligantic schemas show customer. The canonical names remain the documented form; aliases exist for convenience and discoverability.

Alias

Canonical

Commands

get

show

schemas, records, flows, flows runs, config

ls

list

space, schemas, records, flows, auth

add / new

create

records

set

update

records

edit

patch

records

rm / remove

delete

records

connect

link

records

disconnect

unlink

records

show / get

current

space

status / current / me

whoami

auth

Config Resolution

The CLI resolves settings with the precedence flag > env var > config file > built-in default, consistent across all settings.

Setting

Flag

Env var

Config file field

Default

Profile

--profile <name>

LIGANTIC_PROFILE

currentProfile

default

Space

--space <id>

LIGANTIC_SPACE_ID

profiles.<profile>.spaceId

none

API key

— (never a flag)

LIGANTIC_API_KEY

profiles.<profile>.apiKey*

OS key store (per profile)

Base URL

—

LIGANTIC_API_BASE_URL

profiles.<profile>.baseUrl, then apiBaseUrl

https://ligantic.cloud/api/v1

* The profiles.<profile>.apiKey config-file field is present only in the key-store-fallback case (when no OS key store is available), for the profile named by currentProfile (default: default). The API key is never a flag.

The selected profile supplies its own key and base URL. A profile chosen with --profile or LIGANTIC_PROFILE is used for that invocation only — the config file is not written, so it works with a read-only ~/.ligantic and does not affect other shells. If that profile does not exist or has no stored credentials, key-requiring commands fail with exit code 3 and an error listing the saved profiles. If the current (or default) profile has no credentials but other profiles are saved, the error names them.

Each profile remembers its own current Space (profiles.<profile>.spaceId), so switching profile — with auth use, --profile, or LIGANTIC_PROFILE — also switches the Space.

The API key resolution chain is: LIGANTIC_API_KEY (env) > the active profile's OS key store entry > profiles.<profile>.apiKey in the config file (key-store fallback only).

Config File

~/.ligantic/config.json, written with 0600 permissions. Set LIGANTIC_CONFIG_DIR to keep it (and the update-check cache) in another directory, for example a separate config for an agent.

{
  "apiBaseUrl": "https://staging.example.com/api/v1",
  "currentProfile": "default",
  "profiles": {
    "default": { "apiKey": "...", "spaceId": "sp_123" },
    "staging": { "baseUrl": "https://staging.example.com/api/v1" }
  }
}

Field

Type

Description

apiBaseUrl

string

Optional override of the default base URL.

currentProfile

string

The active authentication profile (default: default). Set by auth login / auth use.

profiles

object

Named authentication profiles. profiles.<name>.apiKey is present only when no OS key store is available — the only secret in the file. profiles.<name>.baseUrl is an optional per-profile base URL override (set with auth login --base-url). profiles.<name>.spaceId is the profile's remembered current Space (set with space use / space select); profiles.<name>.spaceTitle caches its title for auth whoami.

Update Check

On each non-informational command, the CLI checks whether a newer version of @ligantic/cli is published and, if so, prints a notice on stderr (never on stdout, so --json output stays clean):

A new version of ligantic is available: 0.1.0 → 1.2.3
  Upgrade with: npm install -g @ligantic/cli@latest

The check is cached in ~/.ligantic/update-check.json (in LIGANTIC_CONFIG_DIR when set) — at most one network call to the npm registry per day, so it adds no per-command latency after the first run. It is non-fatal: if the registry is unreachable (offline), the check is skipped and the last-seen version is reused for the day.

Disable it with the LIGANTIC_NO_UPDATE_CHECK env var (it is also skipped automatically in CI, detected via the CI env var, and for --help/--version).

auth

Manage authentication.

ligantic auth login [--profile <name>] [--base-url <url>] [--no-switch] [--no-store] [--store-plaintext]

Authenticate with an API key. The key is read from piped stdin (echo $KEY | ligantic auth login) or a hidden prompt — never a flag. On success the key is stored in the OS key store for the named profile (default: default).

  • --profile <name> stores the key for a named profile and makes it the current profile.
  • --base-url <url> remembers a base URL on the profile (staging / self-hosted). It is flag-only — the login flow never prompts for it — and takes precedence over the file-level apiBaseUrl for that profile. The URL must be a plain http(s) URL with no query or fragment. LIGANTIC_API_BASE_URL still wins over the flag for the current session; the flag value is persisted for future invocations.
  • --no-switch stores the key without changing the current profile.
  • When no OS key store is available, the CLI warns that the key would be stored in plain text in the 0600 config file and asks for explicit consent before writing it. In an interactive session it prompts; when non-interactive it fails safe (does not store) unless --store-plaintext is passed. Under --json, prompts are disabled: pipe the key and pass --store-plaintext when needed. Declining leaves the key unstored (use LIGANTIC_API_KEY for future commands).
  • --no-store skips persistence entirely — the key is used for this session only.

In an interactive session the CLI then offers to open the Space picker.

ligantic auth list

List saved authentication profiles. The current profile is marked with *. With --json, emits [{ name, active }].

ligantic auth use <name>

Switch the active authentication profile. The profile must have stored credentials — in the OS key store, or in the config file when no key store is available.

ligantic auth select

Interactively select the current profile (arrow-key + Enter picker). Only profiles with stored credentials are offered; selecting one writes currentProfile to the config file (the same path auth use takes). Requires an interactive terminal — in a script or CI, use auth use <name> instead. Rejected with a usage error (exit 2) under --json, since the picker draws on stdout.

ligantic auth whoami

Alias: auth status. Show the authenticated user and the current Space (ID and title). With --json, prints the full user object plus space: { id, title } | null.

ligantic auth logout [--profile <name>]

Remove the stored API key (from the key store and, if present, the config file). Defaults to the current profile.

spaces

Manage the current Space (kubectl-style). The singular space is an alias for spaces.

ligantic spaces list

List Spaces the current user can access. The current Space is marked with *.

ligantic spaces overview

List the current Space's Schemas with their Record counts (id, name, records) — a quick way to see what is in a Space and where a topic lives. Counts are read without listing the Records. With --json, emits [{ id, name, records }].

ligantic spaces use <id>

Set the remembered current Space. Verifies the Space exists, then remembers it as the current profile's Space (profiles.<profile>.spaceId) in the config file. Other profiles keep their own Space.

ligantic spaces select

Interactively select the current Space (arrow-key + Enter picker). Requires an interactive terminal and is rejected with a usage error (exit 2) under --json — in a script or CI, use space use <id> instead.

ligantic spaces secrets set --path <segments...> [--comment <text>]

Create or update a Space secret at the given path (for example --path db password). Like an API key, the value is never a flag: it is read from piped stdin (printf %s "$DB_PASSWORD" | ligantic space secrets set --path db password; one trailing newline is dropped) or from a hidden prompt on a terminal. Under --json, the prompt is disabled, so pipe the value. With no value, fails with a usage error (exit 2). The value is never printed; --json emits { path }. space secrets list and space secrets delete are listed in Generated CLI commands.

ligantic spaces current

Show the current Space (ID and title), or (no current space).

schemas

Manage Schemas. The singular schema is an alias for schemas.

ligantic schemas list [--name <name>]

List Schemas in the current Space. --name filters by (case-insensitive) Schema name.

ligantic schemas show <idOrName>

Show a Schema by ID or (case-insensitive) name. Ambiguous names error with the candidate IDs. Below the detail block, the latest version's fields are listed as a table (id, name, type, display, required, options, order, rest, sorted by order) — the vocabulary needed to interpret Record data, which is keyed by field name by default (--id-based on records list or records show keys it by field ID instead). required marks server-side required fields (including relationship fields); options is only shown when at least one field has options (fields without options render -); rest is a trailing column dumping any remaining property attributes (for example, description, default, relationship targetSchemaId/cardinality) so nothing is hidden. With --json, the schema is emitted exactly as the server sent it (no added fields array).

records

Work with Records. The singular record is an alias for records. Every command takes the Schema as a positional <schema> argument (ID or name — names resolve case-insensitively, with an explicit error on ambiguity).

ligantic records list <schema> [--limit <n>] [--offset <n>] [--id-based] [--filter <json>] [--where <clause>…] [--search <text>] [--sort <field:order>…] [--select <fields>] [--expand <rels>…] [--relationships] [--content <markdown|text|json>]

List Records in a Schema. By default the table shows the Record id plus one column per top-level key of the Record's data object (keys are the union across the returned Records, in first-seen order; a missing value renders as -). When no Record has object data, a single data column is shown instead. When the page does not cover the full result set, the table ends with a pagination footer, for example 25 of 307 (use --limit/--offset for more).

With --json, prints the full API result — the page plus the pagination metadata: { "records": […], "count": 307, "offset": 0, "limit": 25 }.

Non-scalar cells are summarized to keep the table compact: content fields render as a one-line excerpt ([content: …]), arrays as [N items], and other objects as [object]. Scalars render as themselves. Full content is available via records show (see --content) or --json.

--content <markdown|text|json> (default markdown, json under --json) controls the content-cell excerpt: markdown keeps inline marks in the one-line excerpt, text strips them, and json shows only the block count ([content: N blocks]). Under --json the whole Record list is emitted as JSON regardless.

Record data is keyed by field name by default — columns read Name, Status, … (the field vocabulary is listed by schemas show). --id-based keys data by opaque field ID instead (9a83, 3910, …). Under name-based output, data keys that are not in the current Schema are omitted.

Query options (all server-side):

  • --where '<field> <op> <value>' — human shorthand for a single property filter. Ops: = != > >= < <= ~ (contains), =:ic (ignore-case equals). Fields may be names or IDs; names resolve against the Schema (unknown fields error with the available list). Repeatable — multiple clauses are ANDed.
  • --filter <json> — a raw filter object (the full DSL: and/or/not, property, relationship). Combined with --where by AND.
  • --search <text> — full-text search.
  • --sort '<field>:asc|desc' — repeatable; asc is the default when the order is omitted. Fields may be names or IDs.
  • --select <fields> — comma-separated fields to include. Names work by default; pass field IDs with --id-based. Standard properties (id, createdAt, updatedAt, createdById, updatedById) are always accepted.

--filter paths must be field IDs: a path that uses a field name silently matches nothing.

--expand <rels> (repeatable, comma-separated) expands relationship fields: each name is a relationship field on the Schema (matched via the Schema's relationship versions), a standalone relationship's side label (for example Depends on / Dependents on a self-referential Schema — no field in the Schema config), or a relationship version id. Only rows where the Record sits on the label's side are counted, so the column renders what the UI shows for that label on that Record. Each Record's related Records are resolved and rendered inline as id (primary text) in a column per expanded field (- when the Record has no such relationship). Under --json, the raw relationships array is included in the Record objects.

--relationships includes each Record's raw relationship rows (the fromEntityId/toEntityId + schemaRelationshipVersionId rows, not resolved to text). It includes every relationship version on the Schema, adds a relationships column to the table (one versionId: fromEntityId -> toEntityId per row, ; -joined, - when none), and includes the relationships array in the Record objects under --json — always present when the flag is set, an empty array ([]) when the Schema has no relationship versions or the Record has no links. Use it to inspect which Records a Record is linked to, or to feed records link.

ligantic records show <schema> <id> [--id-based] [--expand <rels>…] [--content <markdown|text|json>]

Show a Record by ID. data is keyed by field name by default; --id-based keys it by opaque field ID. Data is rendered field-by-field (not as a single JSON blob): scalars on one line, and content fields rendered in full per --content (multi-line values are indented under their label).

--content <markdown|text|json> controls how content fields render: markdown (default for human output) renders headings, (nested) lists, bold/italic/code, links, tables and images as markdown; text is the same structure with inline marks stripped; json emits the raw content array. Under --json the whole record is emitted as JSON regardless.

--expand <rels> (repeatable, comma-separated) renders each expanded relationship field as a row of id (primary text); names may be relationship fields, a standalone relationship's side label (for example, Depends on / Dependents), or a relationship version id, and only rows where the record sits on the label's side are rendered. Under --json, --expand adds resolved target rows on top of the always-present raw relationships array.

The Record's raw relationships rows are always included — a relationships section in the human view (one versionId: fromEntityId -> toEntityId line per row, - when none) and the relationships array under --json. Unlike --expand, the rows are not resolved to text; when --expand is also set, --expand renders its resolved rows alongside the raw array.

ligantic records link <schema> <recordId> <field> <targetId…>

Link a Record to one or more target Records via a relationship field. <field> is a relationship field name on the Schema (matched via the Schema's relationship versions) or a relationship version id (an unambiguous handle when two labels collide); the command works out which side of the relationship the source Record sits on, so you never reason about direction. Each <targetId> is a Record ID in the relationship's other Schema (repeated IDs are deduplicated). Prints linked <recordId> -> <targetId> via <field> per target (or { field, recordId, links: [{ targetId, entityRelationshipId }] } under --json).

Cardinality is checked before each link is created: on a single-valued source side at most one target is allowed and an existing link is rejected; on a single-valued target side a target that is already linked is rejected. The source Record is checked against the source Schema, and a target that is not a Record in the target Schema is rejected as not a valid record. Each such check fetches the affected Record's relationship rows; if that fetch fails for a non-404 reason the command prints a warning: … line and proceeds. Links are created one at a time and each linked … line is printed as it happens, so if a later target fails the earlier links are already persisted and the error says how many were created; under --json the { field, recordId, links } document (with the links created so far) is also emitted before the command exits, so automation can recover the persisted entityRelationshipIds.

ligantic records link Customer r1 Owner person_1 person_2

ligantic records unlink <schema> <recordId> <field> <targetId> [--yes]

Remove a single link between a Record and a target Record via a relationship field. Without --yes, it asks for confirmation on a terminal in table mode; anywhere else (scripts, agents, JSON mode) it fails with exit code 2 and removes nothing. The link is looked up first, so a missing link is reported before any confirmation. <field> is a relationship field name on the Schema or a relationship version id; the command fetches the Record's relationship rows, finds the one linking <recordId> to <targetId> (either endpoint), and deletes it. Prints unlinked <recordId> -> <targetId> via <field> (or { field, recordId, targetId, removed } under --json). Errors with no link between … — nothing to unlink when the two records are not linked via that field. Each invocation removes one link row. If records show still lists a link afterwards, run unlink again.

ligantic records create <schema> [--data <json>]

Create a Record. --data is the Record data as a JSON object string, keyed by field name. Prints the new Record ID (or the full result with --json).

Without --data:

  • On a terminal in table mode, the CLI shows a form built from the Schema's fields, in field order. Required fields are marked * and asked again until answered; press Enter to skip an optional field. Fields with options, and boolean fields, use an arrow-key picker; Esc cancels the whole form and nothing is created. Number and date answers are checked before moving on, and content fields take a line of markdown. Relationship, file, duration, geojson, array, and object fields are skipped and listed afterwards; set them with records patch or records link.
  • Otherwise (stdout or stdin not a terminal, or JSON mode) the CLI never prompts. When the Schema has required fields (relationship fields excepted, since they are linked after creation), it fails with exit code 2 and names them, for example missing required field(s): Title — pass them with --data, for example, --data '{"Title":"..."}'. A Schema with no required fields gets a Record with empty data.

Content fields accept markdown. For any field typed content on the Schema, a --data value that is a plain string is converted to content blocks before the request. This includes content fields nested inside object and array fields (matched by field name at each level). Non-string values (already-block arrays) and non-content fields pass through unchanged. Image destinations in the markdown must be absolute URLs (http/https); a relative path (or empty destination) is rejected with an error.

ligantic records create Customer --data '{"Name":"Ada","Notes":"# Heading\n\nSome **bold** text."}'

ligantic records put <schema> <id> [--data <json>]

Replace a Record's data (full replace of the data object; fields not provided are reset). --data is the new Record data as a JSON object string, keyed by field name. Content fields accept markdown exactly as in records create. Prints the updated Record ID (or the full result with --json). update and set are aliases; for a partial update use records patch.

ligantic records patch <schema> <id> (--patch <json> | --ops <json>) [--id-based]

Partially update a Record's data. Provide exactly one of:

  • --patch <json> — a plain JSON object merged into the Record's data; only the listed fields change.
  • --ops <json> — an RFC 6902 JSON Patch array, for example, [{"op":"replace","path":"/Status","value":"Triaged"}].

Keys and patch paths are field names by default; --id-based switches them to field IDs. Content fields accept markdown as in records create, with both --patch and --ops, and with name or id keys. For --ops, the value of an add/replace operation is converted when its path targets a content field, or an object or array that contains one. A path inside a content value (for example, /Description/- to append a block) is sent unchanged. Prints patched <id> (or the full result with --json).

ligantic records patch Issue r1 --patch '{"Status":"Fix In Progress"}'

ligantic records delete <schema> <id> [--yes]

Delete a Record. Prints the deleted Record ID. Without --yes, it asks for confirmation on a terminal in table mode; anywhere else (scripts, agents, JSON mode) it fails with exit code 2 and changes nothing.

flows

Work with Flows. The singular flow is an alias for flows.

ligantic flows list

List Flows in the current Space.

ligantic flows show <id>

Show a Flow by ID.

ligantic flows run <id> [--input <json>] [--wait]

Run the current published Flow. --input is the Flow inputs as a JSON string. Without --wait, prints the run ID and returns immediately. With --wait, polls to completion (terminal statuses: succeeded, failed, cancelled) and prints the final run.

ligantic flows runs show <id>

Show a run by ID.

ligantic flows runs outputs <id>

Show the outputs of a run.

config

Inspect the CLI configuration. These commands make no network call and work without an API key.

ligantic config show

Show the resolved configuration for the current invocation — profile (and where it came from: flag, env, config, or default), space, API key, and base URL — applying the precedence in Config Resolution. The API key is always masked (first and last 4 secret characters, middle elided) so the output is safe to share; keys too short to mask are hidden entirely. When no key is resolved the field is shown as -.

$ ligantic config show
profile      work (flag)
space        sp_123
api key      lak_…_eak
base url     https://ligantic.cloud/api/v1

With --json, prints { "profile", "profileSource", "spaceId", "apiKey", "baseUrl" } (with the masked key, or null when no key is resolved).

cli

Discover the CLI's own commands. These commands make no network call and work without an API key, so an agent that has never used the CLI can find its way from ligantic --help.

ligantic cli search <query...> [--limit <n>]

Find the command for a task described in plain words, for example ligantic cli search change the status of a record. Commands are ranked by how many query words match their name, description, arguments, and options. Common verbs are treated as the CLI's own verbs (get → show, remove → delete, add → create, edit → update, execute → run), and entity matches record. --limit caps the results (default 10).

With --json, emits [{ command, description, usage, score }], best match first, or [] when nothing matches.

ligantic cli commands [path...]

List commands with their arguments and options. Without a path it covers every command; with a path (records, or records list) it covers that group or command only. The table shows one row per runnable command (usage and description).

With --json, emits the command tree: { command, description, usage, arguments: [{ name, required, variadic, description }], options: [{ flags, description, mandatory, default? }], commands? }, where commands is present on groups.

ligantic <command> --help --json prints the same JSON description for a single command. Plain --help is always text, even when stdout is not a terminal.

Output and Errors

The output format follows where stdout goes:

stdout

Default format

Override

A terminal

Tables and key/value blocks; JSON is pretty-printed when forced with --json.

--json

Not a terminal (pipe, file, agent, CI)

JSON, compact on one line.

--table

  • In JSON mode every command writes exactly one JSON document to stdout, including commands whose table output is a status line (for example records delete emits { "id", "deleted": true } and space use emits { "spaceId", "title" }).
  • Errors always go to stderr. In table mode an error is error: <message> text, followed by status, code, request, and any validation issues when present. In JSON mode it is a single-line document:

    { "error": { "message": "no schema 'Isue' in sp_123", "code": "NOT_FOUND", "status": null } }

    code and status come from the API when the API rejected the request (requestId and issues are added when present). For failures detected by the CLI itself, status is null and code is one of USAGE, UNAUTHORIZED, NOT_FOUND, or FAILED, matching the exit code.

  • Warnings (for example the plaintext key-store warning) are warning: … text lines on stderr in both modes. No stack trace is printed by default.

Exit Codes

Code

Meaning

0

Success.

1

The command failed: an API, server, or network error, or a failed operation.

2

Usage error: an unknown command or option, a missing argument, malformed input such as invalid --data JSON, an ambiguous name, or no Space selected.

3

Not authenticated (no API key resolved), the API rejected the key (HTTP 401), or the key lacks permission (HTTP 403).

4

Not found: the API returned 404, or a Schema, Record, run, or link could not be resolved.

Related pages