> For the complete documentation index, see [llms.txt](https://docs.mithrl.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mithrl.com/docs/cli.md).

# Mithrl-1 CLI

Mithrl, an easy to use command line interface.

KG-primitive commands print compact TSV by default and accept `--json`; workflow and management commands print machine-readable JSON. Commands print a trace of their backing operation on stderr. Payloads carrying a `result_id` can be replayed with `mithrl results` or composed with `mithrl filter` and `mithrl export` without recomputing.

For the complete upload → run → monitor → download lifecycle, see [Running and managing Inference](/guides/workflow-cli.md).

Exit codes: `0` success, `1` error (a structured `{"error": {...}}` on stderr), `2` bad usage, `3` incomplete (the command worked but the work it was asked to do is demonstrably still unfinished — e.g. `filter`/`export` cut short by their drain's `--timeout`/page limit; see each command's own constraints). For `mithrl run` in its default blocking mode, `3` means the bounded wait elapsed with the run still active: it still prints its normal payload, carrying the `run_id` and the `mithrl status <run_id> --watch` command to reattach with, so a script must not read it as success and must not re-submit.

`4` remote failure (the command worked, and the run it was reporting on reached a terminal `failed` or `cancelled` state). `mithrl run`, `mithrl status` and `mithrl run-results` all exit `4` in that case, print their normal payload on stdout, and spell the server's `failure` block — code, stage, category, message — out on stderr. `4` is deliberately distinct from `1`: `1` means the CLI could not complete its own call (no credentials, no network, a 5xx) and is often worth retrying verbatim, while `4` means the call succeeded and the answer is that the work failed, which never is.

`mithrl status` exits 0 while a run is still active — including when its own bounded watch expires first — so read `data.run.status` to tell those two apart; the exit code distinguishes only "still going" (0) from "finished and failed" (4).

Argument values are trimmed of surrounding whitespace, so a value pasted with a stray leading or trailing space behaves exactly like a clean one — most visibly for `mithrl api set`, where an untrimmed URL previously persisted verbatim and then failed to match the URL recorded at login. This applies to file-path arguments too (`mithrl upload`, `mithrl validate`): a path whose name deliberately begins or ends with a space no longer resolves, and must be renamed to be passed on the command line.

## Introspection

Emit the machine-readable spec the other CLI surfaces are generated from.

### `mithrl spec`

Emit the machine-readable mithrl command/flag spec — the single source of truth --help and the /mithrl skill are generated from. Scoped by default: an index of every command, with one command's full flags, constraints and caveats a `mithrl spec <command>` away.

**Constraints**

* Depth follows scope: a scoped call (a command, a group, or --section) prints every field of what it selected, while an unscoped call prints the index -- every command's name, one-line help, section, status and param count. `--full` and `--brief` override that in either direction, and are mutually exclusive.
* A selector and --section name different scopes and can't be combined. A selector matches a full command name first (`keys create`), then a group prefix (`keys`); an unknown one fails as `not_found` with the closest command names.
* `--full` prints the pre-scoping payload -- `{"commands": [...]}` with every field -- unchanged. The index adds `brief: true` and a `next` object naming the scoped forms, and carries every command, so a caller reading only `name` sees the same list it always did.

**Arguments**

| Argument  | Type            | Required | Description                                                                                                                                    |
| --------- | --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `command` | str, repeatable | no       | A command or group to scope the output to, typed exactly as you would type the command itself (`mithrl spec keys create`, `mithrl spec keys`). |

**Options**

| Option      | Type | Required | Default | Description                                                                                                                          |
| ----------- | ---- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `--section` | str  | no       | —       | Scope to one documentation section instead of one command, e.g. --section 'KG primitives'. Case-insensitive; the index names them.   |
| `--full`    | bool | no       | `False` | Emit every field of every selected command. Unscoped, this is the whole spec (\~24k tokens for 39 commands) -- prefer a scoped call. |
| `--brief`   | bool | no       | `False` | Emit the index shape (name, help, section, status, param count) even when scoped.                                                    |

**Examples**

```bash
mithrl spec
mithrl spec path
mithrl spec keys create
mithrl spec keys
mithrl spec --section 'KG primitives'
mithrl spec --full
```

## Auth & local infra

Sign in, manage API keys, and install the agent-facing tooling.

### `mithrl login`

Browser-based sign-in (interactive; human-only).

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl login
```

### `mithrl logout`

Clear locally stored credentials.

**Examples**

```bash
mithrl logout
```

### `mithrl keys add`

Register an API key locally from `keys create`'s own JSON output (piped via stdin).

**Constraints**

* Reads a JSON object from stdin -- pipe `mithrl keys create`'s own output straight in.

**Examples**

```bash
mithrl keys create --name ci-agent | mithrl keys add
```

### `mithrl keys use`

Activate a locally-registered API key (from `keys add`) instead of an interactive login session.

**Constraints**

* Exactly one of -p/--prefix or -n/--name is required.

**Options**

| Option           | Type | Required | Default | Description                                                                          |
| ---------------- | ---- | -------- | ------- | ------------------------------------------------------------------------------------ |
| `-p`, `--prefix` | str  | no       | —       | The key's prefix (from `keys add`/`keys list`, e.g. ltk\_live\_ab12cd34ef56).        |
| `-n`, `--name`   | str  | no       | —       | The key's name (from `keys create --name`), if it uniquely identifies one added key. |
| `--debug`        | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).        |

**Examples**

```bash
mithrl keys use -p ltk_live_ab12cd34ef56
mithrl keys use -n ci-agent
```

### `mithrl keys create`

Create a scoped, read-only API key for headless/agent use.

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--name`  | str  | no       | —       | Label for the new key.                                                        |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl keys create --name ci-agent
```

### `mithrl keys list`

List existing API keys.

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl keys list
```

### `mithrl keys revoke`

Revoke an API key.

**Arguments**

| Argument | Type | Required | Description                                                                          |
| -------- | ---- | -------- | ------------------------------------------------------------------------------------ |
| `key_id` | str  | yes      | The key's prefix (from `keys list`, e.g. ltk\_live\_ab12cd34ef56) or its numeric id. |

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl keys revoke ltk_live_ab12cd34ef56
```

### `mithrl keys rotate`

Rotate an API key.

**Arguments**

| Argument | Type | Required | Description                                                                          |
| -------- | ---- | -------- | ------------------------------------------------------------------------------------ |
| `key_id` | str  | yes      | The key's prefix (from `keys list`, e.g. ltk\_live\_ab12cd34ef56) or its numeric id. |

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl keys rotate ltk_live_ab12cd34ef56
```

### `mithrl api set`

Configure the platform admin API's base URL (persisted locally).

**Constraints**

* Optional: the CLI has a built-in default deployment. Run this only to point it at a different one, such as a self-hosted deployment.

**Arguments**

| Argument | Type | Required | Description                                                              |
| -------- | ---- | -------- | ------------------------------------------------------------------------ |
| `url`    | str  | yes      | Base URL of the platform admin API, e.g. <https://platform.example.com>. |

**Examples**

```bash
mithrl api set https://platform.example.com
```

### `mithrl api default`

Point mithrl at the production deployment.

**Examples**

```bash
mithrl api default
```

### `mithrl api show`

Show the platform admin API base URL in effect, and whether it came from local configuration or the built-in default.

**Examples**

```bash
mithrl api show
```

### `mithrl api clear`

Remove the locally configured API base URL, reverting to the built-in default.

**Examples**

```bash
mithrl api clear
```

### `mithrl skill install`

Register the /mithrl skill for coding agents.

**Examples**

```bash
mithrl skill install
```

### `mithrl update`

Signature-verified self-update (binary + snapshot).

**Constraints**

* \--check, --rollback, and --reset-ratchet are mutually exclusive.
* Applying an update requires a packaged install; elsewhere the command reports the available version and points at your package manager instead.
* Applying an update and --rollback are not supported on Windows yet, because a running executable cannot replace itself there.
* \--check works on every platform and install type.

**Options**

| Option            | Type | Required | Default | Description                                                                   |
| ----------------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--check`         | bool | no       | `False` | Check for an available update without applying it.                            |
| `--rollback`      | bool | no       | `False` | Restore the previously installed version.                                     |
| `--reset-ratchet` | bool | no       | `False` | Clear the locally-stored last-verified-update-version downgrade/freeze check. |

**Examples**

```bash
mithrl update
mithrl update --check
mithrl update --rollback
```

## KG primitives

Query the knowledge graph directly: resolve names, read nodes, walk edges, find paths.

### `mithrl search`

Resolve a name/exact/regex/substring query to typed nodes.

**Constraints**

* A resolved result is not proof the query matches the real question. A generic term (a disease name standing in for the actual drug candidate, a bare gene symbol standing in for a specific allele, one gene standing in for a whole gene set) returns real nodes/edges just as cleanly as a fully specified one -- confirm the query names the actual compound/structure, target, organism and strain, allele, gene set, or model before treating its result as the answer.
* Read the matchType column before treating a result as the entity named. Only EXACT\_NAME, EXACT\_CURIE, EXACT\_SYNONYM and EQUIVALENT\_CURIE identify that entity; XREF, NAME\_SUBSTRING and SYNONYM are approximations, and a matched alternative name FRAGMENT can belong to a broader or narrower concept whose own name shares no word with the query. Matching is literal throughout -- there is no fuzzy, phonetic or semantic matching -- so an unexpected result came through one of those arms, not through a similarity score.
* Results are ordered by, in this priority: whether the entity comes from a curated vocabulary rather than from free text lifted out of a corpus (a clinical-trial record, a publication title); then how strongly it matched (an exact name, then an exact alternative name, then a fragment of either); then whether its PRIMARY category is the one --type asked for and whether it is human. Corroboration across sources and name length only break ties. So a trial arm literally named "Glucose" ranks below the CHEBI metabolite that carries `glucose` as an alternative name, and a human gene symbol ranks above its rodent ortholog when the capitalisation differs. Nothing is excluded by this -- every match is still returned, and a query that only a trial or a paper matches still finds it.
* Same symbol, different species: human gene symbols collide with their rodent orthologs (EGFR, TP53, BRCA1, ...), and `--type`/`--type-a`/`--type-b` cannot separate a collision where every candidate is a Gene. `--organism` (aka `--taxon`) is what resolves it -- pass `--organism human` whenever a bare gene symbol is the identifier and the question is about human biology. It narrows the candidates a NAME resolves to; against an already-CURIE identifier it is checked instead, and a CURIE belonging to another species fails as `taxon_mismatch` rather than answering for the wrong organism. A candidate that reports no organism is never excluded (a disease or pathway has none), so one flag is safe on `path`'s two endpoints. On a name whose every candidate belongs to another species the command fails `not_found` naming those species, never an empty result.
* On `search`, --organism narrows the query itself, in the graph store, BEFORE --limit is applied -- so a page holds --limit matches in that organism rather than however few of a mixed-species page survived. The command says on stderr (and under `organismNote` in --json) how many matches the filter removed and which species they belonged to, counted over every match and not just this page.
* `--count` returns the size of one bounded page, not a server-side total -- `search` has no total-count mode and no cursor, so `candidateCount` is capped by `--limit`, or by the API's own default of 20 when `--limit` is omitted. `--limit` is itself clamped to 100 server-side -- a `--limit` above that is not honored, and search reports no signal of its own when the clamp fires. Below that ceiling, a count landing exactly on --limit/the default means "at least this many" (the payload's own `note` says so) -- raise `--limit`, up to the ceiling, until the count comes back under it before reporting the number as a total. A count that lands at the 100 ceiling itself can never be confirmed as a total from `search` -- it means "at least 100", full stop, and a different route (e.g. a narrower --type, or a source with a real total-count mode) is needed for the actual number.
* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.

**Arguments**

| Argument | Type | Required | Description                                  |
| -------- | ---- | -------- | -------------------------------------------- |
| `query`  | str  | yes      | Name, exact/regex/substring query, or CURIE. |

**Options**

| Option                  | Type | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------- | ---- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--type`                | str  | no       | —       | Filter candidates by category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `--organism`, `--taxon` | str  | no       | —       | Restrict to one species, e.g. `--organism human` (also `--taxon`). Accepts human, mouse, rat, zebrafish, fly, worm, yeast, an NCBITaxon CURIE (NCBITaxon:9606), or a bare taxon id (9606). This is the fix for a gene symbol that resolves to several species' orthologs -- `--type` cannot separate those, since every one of them is a Gene. Entities that carry no organism at all (a disease, a pathway, a chemical) are never filtered out by it. Distinct from `--species`, which filters on an edge's `species_context` qualifier -- the organism the experiment was run in, not the organism of the entities the edge connects. |
| `-n`, `--limit`         | int  | no       | —       | Maximum number of candidates to return. Clamped to 100 server-side -- a value above that is not honored, and search reports no signal of its own when the clamp fires.                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `-c`, `--count`         | bool | no       | `False` | Return only the candidate count (capped by --limit/the API's own default -- not a true server-side total).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `--json`                | bool | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--full`                | bool | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--provenance`          | bool | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                           |
| `--debug`               | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

**Examples**

```bash
mithrl search BRCA1
mithrl search melanoma --type Disease
mithrl search EGFR --type Gene --organism human
mithrl search kinase --count --limit 100
```

### `mithrl lookup`

Fetch one node by name or CURIE.

**Constraints**

* A resolved result is not proof the query matches the real question. A generic term (a disease name standing in for the actual drug candidate, a bare gene symbol standing in for a specific allele, one gene standing in for a whole gene set) returns real nodes/edges just as cleanly as a fully specified one -- confirm the query names the actual compound/structure, target, organism and strain, allele, gene set, or model before treating its result as the answer.
* A CURIE is accepted as given -- a well-formed identifier for the wrong entity resolves exactly as cleanly as the right one, and returns a complete, credible result for it. Read the `resolved:` line this prints to stderr (canonical name, CURIE, category, organism) and confirm it is the entity you meant before using the result; pass `--expect <name>` to have that checked for you rather than by eye. `--type` is checked too in this case (it otherwise only narrows candidates while resolving a bare name, which a CURIE skips entirely) -- a CURIE of the wrong category fails as `category_mismatch` rather than silently ignoring `--type`. An identifier that is resolved and its node fetched fails as `not_found` when the active build holds no node for it, whether or not `--expect`/`--type` was passed -- so `no edges` and `no path found` are answers about the graph rather than about a name it does not have. Forms that make no per-identifier node lookup (`--batch`, and `--depth 0` where offered) are outside that guarantee and can still report an empty result for an identifier the build does not hold.
* Same symbol, different species: human gene symbols collide with their rodent orthologs (EGFR, TP53, BRCA1, ...), and `--type`/`--type-a`/`--type-b` cannot separate a collision where every candidate is a Gene. `--organism` (aka `--taxon`) is what resolves it -- pass `--organism human` whenever a bare gene symbol is the identifier and the question is about human biology. It narrows the candidates a NAME resolves to; against an already-CURIE identifier it is checked instead, and a CURIE belonging to another species fails as `taxon_mismatch` rather than answering for the wrong organism. A candidate that reports no organism is never excluded (a disease or pathway has none), so one flag is safe on `path`'s two endpoints. On a name whose every candidate belongs to another species the command fails `not_found` naming those species, never an empty result.
* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.

**Arguments**

| Argument | Type | Required | Description               |
| -------- | ---- | -------- | ------------------------- |
| `name`   | str  | yes      | Name or CURIE to look up. |

**Options**

| Option                  | Type | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------- | ---- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--type`                | str  | no       | —       | Filter candidates by category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `--organism`, `--taxon` | str  | no       | —       | Restrict to one species, e.g. `--organism human` (also `--taxon`). Accepts human, mouse, rat, zebrafish, fly, worm, yeast, an NCBITaxon CURIE (NCBITaxon:9606), or a bare taxon id (9606). This is the fix for a gene symbol that resolves to several species' orthologs -- `--type` cannot separate those, since every one of them is a Gene. Entities that carry no organism at all (a disease, a pathway, a chemical) are never filtered out by it. Distinct from `--species`, which filters on an edge's `species_context` qualifier -- the organism the experiment was run in, not the organism of the entities the edge connects. |
| `--xrefs`               | bool | no       | `False` | Include cross-references to other vocabularies.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `--expect`              | str  | no       | —       | Assert which entity the identifier names, e.g. `--expect RALGDS`. Matched against the resolved node's canonical name and synonyms, case-insensitively; a mismatch fails the command instead of answering for the wrong entity. Use it whenever the identifier came from outside this CLI (memory, a paper, another tool) rather than from `search`/`lookup`.                                                                                                                                                                                                                                                                            |
| `--json`                | bool | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--full`                | bool | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--provenance`          | bool | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                           |
| `--debug`               | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

**Examples**

```bash
mithrl lookup ABCB1
mithrl lookup ABCB1 --xrefs
mithrl lookup EGFR --organism human
mithrl lookup NCBIGene:5900 --expect RALGDS
```

### `mithrl members`

Bulk gene-to-pathway and gene-to-disease set membership.

**Constraints**

* Exactly one of the positional \<term>, --of, or --batch is required.
* \--in is always required (the membership query needs a predicate either way); must be pathways or diseases.
* \--batch reads newline-separated gene names/CURIEs from stdin; blank lines are skipped.
* A --batch entry that fails to resolve (ambiguous or not found) never aborts the others -- membership() runs once over whatever did resolve, and every unresolved entry is reported in a per-entry `errors` list instead, in the same exit-0 response (even when none resolve). The unresolved names, with their reason, are also named on stderr in the default output, so finding out which inputs were lost does not cost a second run of the whole batch.
* \--batch resolves its whole input in one call per 100 names (duplicate lines are asked once), so a 100-gene panel costs one resolution round-trip rather than 100. Progress is reported on stderr for a batch of 25 or more. The result note counts lines, distinct inputs and resolved entities separately -- two names can resolve to one entity, and membership() is keyed by entity.
* A positional argument is read as a term when it is itself a pathway or disease, and as a participant otherwise; --of and --batch are always read as participants.
* Pathway membership spans both stored orientations of the relation, so an entity is reported as belonging to a pathway whether the edge runs entity-to-pathway (participates\_in) or pathway-to-entity (has\_participant).
* A positional argument that is not in the build is reported as not\_found rather than as an empty set.
* A returned list is a window on the real set: memberCount reports the true size, memberOffset where the window starts, truncated whether members lie beyond it, and the output prints a banner naming the next --offset. Walk a large set with --limit and --offset on this command; successive offsets partition the set with no overlap and no gaps, in CURIE order. (Paging with `mithrl results <id> --page N` pages the SETS this command returned, not the members inside them.)
* \--expect applies to the single-entity forms only; it is rejected alongside --batch.
* \--type only filters a bare-name --batch line; rejected alongside --batch if any line is already a CURIE, rather than silently skipping the filter for just that line.
* \--organism behaves exactly as --type does on --batch: it filters every bare-name line, and is rejected if any line is already a CURIE. A batch of bare human gene symbols is the form it is for -- pass --organism human to keep symbols that also name a rodent ortholog from failing as ambiguous.
* Same symbol, different species: human gene symbols collide with their rodent orthologs (EGFR, TP53, BRCA1, ...), and `--type`/`--type-a`/`--type-b` cannot separate a collision where every candidate is a Gene. `--organism` (aka `--taxon`) is what resolves it -- pass `--organism human` whenever a bare gene symbol is the identifier and the question is about human biology. It narrows the candidates a NAME resolves to; against an already-CURIE identifier it is checked instead, and a CURIE belonging to another species fails as `taxon_mismatch` rather than answering for the wrong organism. A candidate that reports no organism is never excluded (a disease or pathway has none), so one flag is safe on `path`'s two endpoints. On a name whose every candidate belongs to another species the command fails `not_found` naming those species, never an empty result.
* A CURIE is accepted as given -- a well-formed identifier for the wrong entity resolves exactly as cleanly as the right one, and returns a complete, credible result for it. Read the `resolved:` line this prints to stderr (canonical name, CURIE, category, organism) and confirm it is the entity you meant before using the result; pass `--expect <name>` to have that checked for you rather than by eye. `--type` is checked too in this case (it otherwise only narrows candidates while resolving a bare name, which a CURIE skips entirely) -- a CURIE of the wrong category fails as `category_mismatch` rather than silently ignoring `--type`. An identifier that is resolved and its node fetched fails as `not_found` when the active build holds no node for it, whether or not `--expect`/`--type` was passed -- so `no edges` and `no path found` are answers about the graph rather than about a name it does not have. Forms that make no per-identifier node lookup (`--batch`, and `--depth 0` where offered) are outside that guarantee and can still report an empty result for an identifier the build does not hold.
* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.

**Arguments**

| Argument | Type | Required | Description                                                                                                                                                                                                                      |
| -------- | ---- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `term`   | str  | no       | Entity or term to query membership for. A pathway or disease is read as a term (its members are listed); anything else is read as a participant (the terms it belongs to are listed). The output states which of the two it did. |

**Options**

| Option                  | Type | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------- | ---- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--of`                  | str  | no       | —       | Query as a participant, listing the terms it belongs to. Skips the inference above.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `--in`                  | str  | no       | —       | Membership kind: pathways or diseases.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--type`                | str  | no       | —       | Filter candidates by category (applies to \<term>/--of/every bare-name --batch line; rejected if any --batch line is already a CURIE).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--organism`, `--taxon` | str  | no       | —       | Restrict to one species, e.g. `--organism human` (also `--taxon`). Accepts human, mouse, rat, zebrafish, fly, worm, yeast, an NCBITaxon CURIE (NCBITaxon:9606), or a bare taxon id (9606). This is the fix for a gene symbol that resolves to several species' orthologs -- `--type` cannot separate those, since every one of them is a Gene. Entities that carry no organism at all (a disease, a pathway, a chemical) are never filtered out by it. Distinct from `--species`, which filters on an edge's `species_context` qualifier -- the organism the experiment was run in, not the organism of the entities the edge connects. |
| `--batch`               | bool | no       | `False` | Read a gene set from stdin (one name/CURIE per line) instead of \<term>/--of, resolving the whole set in a single bulk membership() call.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `--limit`               | int  | no       | —       | Members returned per set. Omitted means the API's own ceiling; a request above it is clamped, not rejected. This bounds one window, not the set -- walk the rest with --offset.                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `--offset`              | int  | no       | `0`     | Skip this many members of each set before returning --limit of them. Page 2 of a `--limit 100` walk is `--offset 100`; the truncation banner prints the next offset to use, and memberCount says when to stop.                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `--expect`              | str  | no       | —       | Assert which entity the identifier names, e.g. `--expect RALGDS`. Matched against the resolved node's canonical name and synonyms, case-insensitively; a mismatch fails the command instead of answering for the wrong entity. Use it whenever the identifier came from outside this CLI (memory, a paper, another tool) rather than from `search`/`lookup`.                                                                                                                                                                                                                                                                            |
| `--json`                | bool | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--full`                | bool | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--provenance`          | bool | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                           |
| `--debug`               | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

**Examples**

```bash
mithrl members ABCB1 --in pathways
mithrl members --of ABCB1 --in pathways
mithrl members REACT:R-HSA-69620 --in pathways
cat genes.txt | mithrl members --batch --in pathways
cat genes.txt | mithrl members --batch --in pathways --type Gene --organism human
mithrl members REACT:R-HSA-69620 --in pathways --limit 100 --offset 100
```

### `mithrl scan`

Stream an unranked graph slice for sweeps and piping.

**Constraints**

* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.

**Options**

| Option              | Type             | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------- | ---------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--predicate`, `-p` | str              | yes      | —       | Predicate to scan for, e.g. regulates or interacts\_with.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `--type`            | str              | no       | —       | Filter by category (the edges' object category).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--source`          | str, at most one | no       | —       | Filter by contributing knowledge source. Case-insensitive. **Limited:** edges(...) takes a single source, not a list — a second --source is rejected.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `--qualifier`       | str, repeatable  | no       | —       | Filter on an edge qualifier as key=value (e.g. --qualifier object\_aspect=phosphorylation), repeatable. The same key repeated ORs its values; distinct keys AND.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--direction`       | str, repeatable  | no       | —       | Filter to a signed direction of effect (increased/decreased/upregulated/downregulated), repeatable. Sugar for --qualifier object\_direction=...; each value also matches its cross-vocabulary spelling (increased also matches upregulated, and vice versa). Object-side only -- sources that write the signed effect on subject\_direction instead (e.g. perturbseq's CRISPRi knockdowns) are not matched; use --qualifier subject\_direction=... for those.                                                                                                                                                                                                                                                                                                                     |
| `--tissue`          | str, repeatable  | no       | —       | Filter to an anatomical context by CURIE (e.g. --tissue UBERON:0002107), repeatable. Sugar for --qualifier anatomical\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. Also matches only the ontology your CURIE's prefix names: anatomical\_context spans MESH (ctd), CL (perturbseq), and BTO/CL/UBERON (signor), so a single --tissue UBERON:... reaches signor's rows for that tissue but not ctd's or perturbseq's -- there is no cross-ontology expansion yet, so a correct CURIE can still return a small fraction of the matching edges with no error.                                                                                                                                                              |
| `--species`         | str, repeatable  | no       | —       | Filter to a species context by CURIE (e.g. --species NCBITaxon:9606), repeatable. Sugar for --qualifier species\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. This is the organism the EXPERIMENT was run in, recorded on the edge -- NOT the organism of the entities at its ends, which is what the separate --organism/--taxon flag filters (offered by `ls` and `tree`, not by `scan`/`match` -- check the command's own --help). An edge between human entities that was measured in mouse matches --organism human and --species NCBITaxon:10090 at once, so neither flag substitutes for the other. Edges whose source records no experimental organism carry no species\_context and are dropped by this filter. |
| `--cell-line`       | str, repeatable  | no       | —       | Filter to a cell-line context by CURIE (e.g. --cell-line cellosaurus:CVCL\_0004), repeatable. Sugar for --qualifier cell\_line\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `--json`            | bool             | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--full`            | bool             | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--provenance`      | bool             | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                                                                                                                                                                     |
| `--debug`           | bool             | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

**Examples**

```bash
mithrl scan --predicate regulates --type Gene --source DoRothEA
mithrl scan --predicate affects --direction increased
```

### `mithrl cat`

Print a node's properties/edges/citations.

**Constraints**

* A CURIE is accepted as given -- a well-formed identifier for the wrong entity resolves exactly as cleanly as the right one, and returns a complete, credible result for it. Read the `resolved:` line this prints to stderr (canonical name, CURIE, category, organism) and confirm it is the entity you meant before using the result; pass `--expect <name>` to have that checked for you rather than by eye. `--type` is checked too in this case (it otherwise only narrows candidates while resolving a bare name, which a CURIE skips entirely) -- a CURIE of the wrong category fails as `category_mismatch` rather than silently ignoring `--type`. An identifier that is resolved and its node fetched fails as `not_found` when the active build holds no node for it, whether or not `--expect`/`--type` was passed -- so `no edges` and `no path found` are answers about the graph rather than about a name it does not have. Forms that make no per-identifier node lookup (`--batch`, and `--depth 0` where offered) are outside that guarantee and can still report an empty result for an identifier the build does not hold.
* Same symbol, different species: human gene symbols collide with their rodent orthologs (EGFR, TP53, BRCA1, ...), and `--type`/`--type-a`/`--type-b` cannot separate a collision where every candidate is a Gene. `--organism` (aka `--taxon`) is what resolves it -- pass `--organism human` whenever a bare gene symbol is the identifier and the question is about human biology. It narrows the candidates a NAME resolves to; against an already-CURIE identifier it is checked instead, and a CURIE belonging to another species fails as `taxon_mismatch` rather than answering for the wrong organism. A candidate that reports no organism is never excluded (a disease or pathway has none), so one flag is safe on `path`'s two endpoints. On a name whose every candidate belongs to another species the command fails `not_found` naming those species, never an empty result.
* Every section validates its node identically -- an ambiguous name, an unknown one, or an identifier absent from the build fails the same way in all three -- and all three accept --expect/--type on the same terms.
* /provenance reports what the node's OWN record cites: the knowledge sources that contributed the entity and the publications that record names. It is not the union of provenance across the node's edges -- for that, page `<node>/edges --provenance`, which reports it per assertion. Many nodes are minted from edge data and carry neither, which the command states rather than leaving as blank cells.
* \--limit bounds one page of the /edges section only; on /meta and /provenance it is rejected rather than ignored. Reach the rest of a large neighborhood with `results <id> --page N` or `export <id>`, exactly as with `ls`.
* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.
* Section shape decides what --full/--provenance do: /edges is edge-shaped and both widen it; the default /meta section is a single node, which --full widens and --provenance has no envelope to widen; /provenance already shows the node's sources and publications, so --provenance is redundant there and --full adds the rest of the record.

**Arguments**

| Argument | Type | Required | Description                                                                                                                                                                                   |
| -------- | ---- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `target` | str  | yes      | Node, optionally suffixed with /meta (its record), /edges (a page of its edges), or /provenance (the sources and publications its own record cites). All three are backed by real operations. |

**Options**

| Option                  | Type | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------- | ---- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--type`                | str  | no       | —       | Filter candidates by category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `--organism`, `--taxon` | str  | no       | —       | Restrict to one species, e.g. `--organism human` (also `--taxon`). Accepts human, mouse, rat, zebrafish, fly, worm, yeast, an NCBITaxon CURIE (NCBITaxon:9606), or a bare taxon id (9606). This is the fix for a gene symbol that resolves to several species' orthologs -- `--type` cannot separate those, since every one of them is a Gene. Entities that carry no organism at all (a disease, a pathway, a chemical) are never filtered out by it. Distinct from `--species`, which filters on an edge's `species_context` qualifier -- the organism the experiment was run in, not the organism of the entities the edge connects. |
| `--limit`               | int  | no       | —       | Edges per page for the /edges section (default 50, the API's own). At most 100 per page: a larger value is clamped, and the header says so. `results <id> --page 1` fetches the rest. Rejected on /meta and /provenance, which return a single record.                                                                                                                                                                                                                                                                                                                                                                                  |
| `--expect`              | str  | no       | —       | Assert which entity the identifier names, e.g. `--expect RALGDS`. Matched against the resolved node's canonical name and synonyms, case-insensitively; a mismatch fails the command instead of answering for the wrong entity. Use it whenever the identifier came from outside this CLI (memory, a paper, another tool) rather than from `search`/`lookup`.                                                                                                                                                                                                                                                                            |
| `--json`                | bool | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--full`                | bool | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--provenance`          | bool | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                           |
| `--debug`               | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

**Examples**

```bash
mithrl cat ABCB1
mithrl cat ABCB1/edges
mithrl cat ABCB1/edges --limit 200
mithrl cat ABCB1/provenance
mithrl cat NCBIGene:5900/edges --expect RALGDS
```

### `mithrl ls`

List a node's neighbors/edges, filtered by relation. Returns one page: a hub node with more edges than --limit reports the total and is continued with `mithrl results <id> --page 2` (or exported whole with `mithrl export <id>`).

**Constraints**

* A resolved result is not proof the query matches the real question. A generic term (a disease name standing in for the actual drug candidate, a bare gene symbol standing in for a specific allele, one gene standing in for a whole gene set) returns real nodes/edges just as cleanly as a fully specified one -- confirm the query names the actual compound/structure, target, organism and strain, allele, gene set, or model before treating its result as the answer.
* A CURIE is accepted as given -- a well-formed identifier for the wrong entity resolves exactly as cleanly as the right one, and returns a complete, credible result for it. Read the `resolved:` line this prints to stderr (canonical name, CURIE, category, organism) and confirm it is the entity you meant before using the result; pass `--expect <name>` to have that checked for you rather than by eye. `--type` is checked too in this case (it otherwise only narrows candidates while resolving a bare name, which a CURIE skips entirely) -- a CURIE of the wrong category fails as `category_mismatch` rather than silently ignoring `--type`. An identifier that is resolved and its node fetched fails as `not_found` when the active build holds no node for it, whether or not `--expect`/`--type` was passed -- so `no edges` and `no path found` are answers about the graph rather than about a name it does not have. Forms that make no per-identifier node lookup (`--batch`, and `--depth 0` where offered) are outside that guarantee and can still report an empty result for an identifier the build does not hold.
* Same symbol, different species: human gene symbols collide with their rodent orthologs (EGFR, TP53, BRCA1, ...), and `--type`/`--type-a`/`--type-b` cannot separate a collision where every candidate is a Gene. `--organism` (aka `--taxon`) is what resolves it -- pass `--organism human` whenever a bare gene symbol is the identifier and the question is about human biology. It narrows the candidates a NAME resolves to; against an already-CURIE identifier it is checked instead, and a CURIE belonging to another species fails as `taxon_mismatch` rather than answering for the wrong organism. A candidate that reports no organism is never excluded (a disease or pathway has none), so one flag is safe on `path`'s two endpoints. On a name whose every candidate belongs to another species the command fails `not_found` naming those species, never an empty result.
* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.

**Arguments**

| Argument | Type | Required | Description                |
| -------- | ---- | -------- | -------------------------- |
| `node`   | str  | yes      | Node to list neighbors of. |

**Options**

| Option                  | Type            | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ----------------------- | --------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--rel`                 | str             | no       | —       | Filter by relation/predicate -- a closed vocabulary, listable with `mithrl schema --predicates`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--type`                | str             | no       | —       | Filter candidates by category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `--organism`, `--taxon` | str             | no       | —       | Restrict to one species, e.g. `--organism human` (also `--taxon`). Accepts human, mouse, rat, zebrafish, fly, worm, yeast, an NCBITaxon CURIE (NCBITaxon:9606), or a bare taxon id (9606). This is the fix for a gene symbol that resolves to several species' orthologs -- `--type` cannot separate those, since every one of them is a Gene. Entities that carry no organism at all (a disease, a pathway, a chemical) are never filtered out by it. Distinct from `--species`, which filters on an edge's `species_context` qualifier -- the organism the experiment was run in, not the organism of the entities the edge connects.                                                                                                                                           |
| `--limit`               | int             | no       | `50`    | Edges per page. At most 100 per page: a larger value is clamped, and the header says so; `results <id> --page 1` fetches the rest. This bounds one page, not the result — use `results --page N` or `export` to reach a node's whole neighborhood.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `--qualifier`           | str, repeatable | no       | —       | Filter on an edge qualifier as key=value (e.g. --qualifier object\_aspect=phosphorylation), repeatable. The same key repeated ORs its values; distinct keys AND.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--direction`           | str, repeatable | no       | —       | Filter to a signed direction of effect (increased/decreased/upregulated/downregulated), repeatable. Sugar for --qualifier object\_direction=...; each value also matches its cross-vocabulary spelling (increased also matches upregulated, and vice versa). Object-side only -- sources that write the signed effect on subject\_direction instead (e.g. perturbseq's CRISPRi knockdowns) are not matched; use --qualifier subject\_direction=... for those.                                                                                                                                                                                                                                                                                                                     |
| `--tissue`              | str, repeatable | no       | —       | Filter to an anatomical context by CURIE (e.g. --tissue UBERON:0002107), repeatable. Sugar for --qualifier anatomical\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. Also matches only the ontology your CURIE's prefix names: anatomical\_context spans MESH (ctd), CL (perturbseq), and BTO/CL/UBERON (signor), so a single --tissue UBERON:... reaches signor's rows for that tissue but not ctd's or perturbseq's -- there is no cross-ontology expansion yet, so a correct CURIE can still return a small fraction of the matching edges with no error.                                                                                                                                                              |
| `--species`             | str, repeatable | no       | —       | Filter to a species context by CURIE (e.g. --species NCBITaxon:9606), repeatable. Sugar for --qualifier species\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. This is the organism the EXPERIMENT was run in, recorded on the edge -- NOT the organism of the entities at its ends, which is what the separate --organism/--taxon flag filters (offered by `ls` and `tree`, not by `scan`/`match` -- check the command's own --help). An edge between human entities that was measured in mouse matches --organism human and --species NCBITaxon:10090 at once, so neither flag substitutes for the other. Edges whose source records no experimental organism carry no species\_context and are dropped by this filter. |
| `--cell-line`           | str, repeatable | no       | —       | Filter to a cell-line context by CURIE (e.g. --cell-line cellosaurus:CVCL\_0004), repeatable. Sugar for --qualifier cell\_line\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `--expect`              | str             | no       | —       | Assert which entity the identifier names, e.g. `--expect RALGDS`. Matched against the resolved node's canonical name and synonyms, case-insensitively; a mismatch fails the command instead of answering for the wrong entity. Use it whenever the identifier came from outside this CLI (memory, a paper, another tool) rather than from `search`/`lookup`.                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--json`                | bool            | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--full`                | bool            | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--provenance`          | bool            | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                                                                                                                                                                     |
| `--debug`               | bool            | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

**Examples**

```bash
mithrl ls ABCB1
mithrl ls ABCB1 --rel physically_interacts_with
mithrl ls ABCB1 --limit 100
mithrl ls EGFR --organism human
mithrl ls NCBIGene:5900 --expect RALGDS
mithrl ls ABCB1 --tissue UBERON:0002107
```

### `mithrl tree`

Expand an N-hop neighborhood (fan-out capped).

**Constraints**

* A CURIE is accepted as given -- a well-formed identifier for the wrong entity resolves exactly as cleanly as the right one, and returns a complete, credible result for it. Read the `resolved:` line this prints to stderr (canonical name, CURIE, category, organism) and confirm it is the entity you meant before using the result; pass `--expect <name>` to have that checked for you rather than by eye. `--type` is checked too in this case (it otherwise only narrows candidates while resolving a bare name, which a CURIE skips entirely) -- a CURIE of the wrong category fails as `category_mismatch` rather than silently ignoring `--type`. An identifier that is resolved and its node fetched fails as `not_found` when the active build holds no node for it, whether or not `--expect`/`--type` was passed -- so `no edges` and `no path found` are answers about the graph rather than about a name it does not have. Forms that make no per-identifier node lookup (`--batch`, and `--depth 0` where offered) are outside that guarantee and can still report an empty result for an identifier the build does not hold.
* Same symbol, different species: human gene symbols collide with their rodent orthologs (EGFR, TP53, BRCA1, ...), and `--type`/`--type-a`/`--type-b` cannot separate a collision where every candidate is a Gene. `--organism` (aka `--taxon`) is what resolves it -- pass `--organism human` whenever a bare gene symbol is the identifier and the question is about human biology. It narrows the candidates a NAME resolves to; against an already-CURIE identifier it is checked instead, and a CURIE belonging to another species fails as `taxon_mismatch` rather than answering for the wrong organism. A candidate that reports no organism is never excluded (a disease or pathway has none), so one flag is safe on `path`'s two endpoints. On a name whose every candidate belongs to another species the command fails `not_found` naming those species, never an empty result.
* \--depth 0 resolves nothing (it makes no API call), so it prints no `resolved:` line and --expect/--type/--organism are all rejected there.
* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.
* \--depth 2 returns a subgraph, which has no compact TSV view: it prints the JSON envelope whether or not --json is passed, so --full/--provenance are inert there too. Depths 0 and 1 return an edge list and widen normally.
* A --depth 2 subgraph is capped: truncated/truncatedNodes/truncatedEdges say so, nodeCount/edgeCount report what was RETURNED rather than the neighbourhood's size, and a truncated result prints a banner on stderr. subgraph() has no cursor or page size, so narrowing with --rel, or walking hop by hop with the cursor-paginated `mithrl ls <node>`, is the only way to see more.
* \--depth 2 calls subgraph(...), which has no qualifier filter at all, so --qualifier/--direction/--tissue/--species/--cell-line are all rejected there.
* \--depth 2 also rejects --organism: subgraph(...) has no organism filter, so the flag would narrow only the seed lookup while the expansion stayed cross-species. Use --depth 1 for an organism-filtered walk.

**Arguments**

| Argument | Type | Required | Description     |
| -------- | ---- | -------- | --------------- |
| `node`   | str  | yes      | Node to expand. |

**Options**

| Option                  | Type            | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ----------------------- | --------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--depth`               | int             | no       | `1`     | Neighborhood depth.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `--rel`                 | str             | no       | —       | Filter by relation/predicate -- a closed vocabulary, listable with `mithrl schema --predicates`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--type`                | str             | no       | —       | Filter candidates by category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `--organism`, `--taxon` | str             | no       | —       | Restrict to one species, e.g. `--organism human` (also `--taxon`). Accepts human, mouse, rat, zebrafish, fly, worm, yeast, an NCBITaxon CURIE (NCBITaxon:9606), or a bare taxon id (9606). This is the fix for a gene symbol that resolves to several species' orthologs -- `--type` cannot separate those, since every one of them is a Gene. Entities that carry no organism at all (a disease, a pathway, a chemical) are never filtered out by it. Distinct from `--species`, which filters on an edge's `species_context` qualifier -- the organism the experiment was run in, not the organism of the entities the edge connects.                                                                                                                                           |
| `--qualifier`           | str, repeatable | no       | —       | Filter on an edge qualifier as key=value (e.g. --qualifier object\_aspect=phosphorylation), repeatable. The same key repeated ORs its values; distinct keys AND.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--direction`           | str, repeatable | no       | —       | Filter to a signed direction of effect (increased/decreased/upregulated/downregulated), repeatable. Sugar for --qualifier object\_direction=...; each value also matches its cross-vocabulary spelling (increased also matches upregulated, and vice versa). Object-side only -- sources that write the signed effect on subject\_direction instead (e.g. perturbseq's CRISPRi knockdowns) are not matched; use --qualifier subject\_direction=... for those.                                                                                                                                                                                                                                                                                                                     |
| `--tissue`              | str, repeatable | no       | —       | Filter to an anatomical context by CURIE (e.g. --tissue UBERON:0002107), repeatable. Sugar for --qualifier anatomical\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. Also matches only the ontology your CURIE's prefix names: anatomical\_context spans MESH (ctd), CL (perturbseq), and BTO/CL/UBERON (signor), so a single --tissue UBERON:... reaches signor's rows for that tissue but not ctd's or perturbseq's -- there is no cross-ontology expansion yet, so a correct CURIE can still return a small fraction of the matching edges with no error.                                                                                                                                                              |
| `--species`             | str, repeatable | no       | —       | Filter to a species context by CURIE (e.g. --species NCBITaxon:9606), repeatable. Sugar for --qualifier species\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. This is the organism the EXPERIMENT was run in, recorded on the edge -- NOT the organism of the entities at its ends, which is what the separate --organism/--taxon flag filters (offered by `ls` and `tree`, not by `scan`/`match` -- check the command's own --help). An edge between human entities that was measured in mouse matches --organism human and --species NCBITaxon:10090 at once, so neither flag substitutes for the other. Edges whose source records no experimental organism carry no species\_context and are dropped by this filter. |
| `--cell-line`           | str, repeatable | no       | —       | Filter to a cell-line context by CURIE (e.g. --cell-line cellosaurus:CVCL\_0004), repeatable. Sugar for --qualifier cell\_line\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `--expect`              | str             | no       | —       | Assert which entity the identifier names, e.g. `--expect RALGDS`. Matched against the resolved node's canonical name and synonyms, case-insensitively; a mismatch fails the command instead of answering for the wrong entity. Use it whenever the identifier came from outside this CLI (memory, a paper, another tool) rather than from `search`/`lookup`.                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--json`                | bool            | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--full`                | bool            | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--provenance`          | bool            | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                                                                                                                                                                     |
| `--debug`               | bool            | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

**Examples**

```bash
mithrl tree ABCB1 --depth 2
mithrl tree NCBIGene:5900 --depth 2 --expect RALGDS
mithrl tree ABCB1 --direction increased
```

### `mithrl match`

Find edges matching a single-edge pattern; for multi-hop, use path instead.

**Constraints**

* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.

**Arguments**

| Argument  | Type | Required | Description                                                                                                                                                                                   |
| --------- | ---- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pattern` | str  | yes      | A single-edge pattern, e.g. '\<Gene> regulates \<Gene>' — category names are case-sensitive and PascalCase (e.g. Gene, Disease, ChemicalEntity); list them with `mithrl schema --categories`. |

**Options**

| Option         | Type             | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------- | ---------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--source`     | str, at most one | no       | —       | Filter by contributing knowledge source. Case-insensitive. **Limited:** edges(...) takes a single source, not a list — a second --source is rejected.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `--qualifier`  | str, repeatable  | no       | —       | Filter on an edge qualifier as key=value (e.g. --qualifier object\_aspect=phosphorylation), repeatable. The same key repeated ORs its values; distinct keys AND.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--direction`  | str, repeatable  | no       | —       | Filter to a signed direction of effect (increased/decreased/upregulated/downregulated), repeatable. Sugar for --qualifier object\_direction=...; each value also matches its cross-vocabulary spelling (increased also matches upregulated, and vice versa). Object-side only -- sources that write the signed effect on subject\_direction instead (e.g. perturbseq's CRISPRi knockdowns) are not matched; use --qualifier subject\_direction=... for those.                                                                                                                                                                                                                                                                                                                     |
| `--tissue`     | str, repeatable  | no       | —       | Filter to an anatomical context by CURIE (e.g. --tissue UBERON:0002107), repeatable. Sugar for --qualifier anatomical\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. Also matches only the ontology your CURIE's prefix names: anatomical\_context spans MESH (ctd), CL (perturbseq), and BTO/CL/UBERON (signor), so a single --tissue UBERON:... reaches signor's rows for that tissue but not ctd's or perturbseq's -- there is no cross-ontology expansion yet, so a correct CURIE can still return a small fraction of the matching edges with no error.                                                                                                                                                              |
| `--species`    | str, repeatable  | no       | —       | Filter to a species context by CURIE (e.g. --species NCBITaxon:9606), repeatable. Sugar for --qualifier species\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet. This is the organism the EXPERIMENT was run in, recorded on the edge -- NOT the organism of the entities at its ends, which is what the separate --organism/--taxon flag filters (offered by `ls` and `tree`, not by `scan`/`match` -- check the command's own --help). An edge between human entities that was measured in mouse matches --organism human and --species NCBITaxon:10090 at once, so neither flag substitutes for the other. Edges whose source records no experimental organism carry no species\_context and are dropped by this filter. |
| `--cell-line`  | str, repeatable  | no       | —       | Filter to a cell-line context by CURIE (e.g. --cell-line cellosaurus:CVCL\_0004), repeatable. Sugar for --qualifier cell\_line\_context=...; takes a CURIE only, not a name -- there is no name index for these qualifier values yet.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `--json`       | bool             | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--full`       | bool             | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--provenance` | bool             | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                                                                                                                                                                     |
| `--debug`      | bool             | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

**Examples**

```bash
mithrl match '<Gene> regulates <Gene>' --source DoRothEA
mithrl match '<Gene> affects <Gene>' --direction increased
```

### `mithrl path`

Shortest path between two entities (up to 5 hops, default 2).

**Constraints**

* A CURIE is accepted as given -- a well-formed identifier for the wrong entity resolves exactly as cleanly as the right one, and returns a complete, credible result for it. Read the `resolved:` line this prints to stderr (canonical name, CURIE, category, organism) and confirm it is the entity you meant before using the result; pass `--expect <name>` to have that checked for you rather than by eye. `--type` is checked too in this case (it otherwise only narrows candidates while resolving a bare name, which a CURIE skips entirely) -- a CURIE of the wrong category fails as `category_mismatch` rather than silently ignoring `--type`. An identifier that is resolved and its node fetched fails as `not_found` when the active build holds no node for it, whether or not `--expect`/`--type` was passed -- so `no edges` and `no path found` are answers about the graph rather than about a name it does not have. Forms that make no per-identifier node lookup (`--batch`, and `--depth 0` where offered) are outside that guarantee and can still report an empty result for an identifier the build does not hold.
* Same symbol, different species: human gene symbols collide with their rodent orthologs (EGFR, TP53, BRCA1, ...), and `--type`/`--type-a`/`--type-b` cannot separate a collision where every candidate is a Gene. `--organism` (aka `--taxon`) is what resolves it -- pass `--organism human` whenever a bare gene symbol is the identifier and the question is about human biology. It narrows the candidates a NAME resolves to; against an already-CURIE identifier it is checked instead, and a CURIE belonging to another species fails as `taxon_mismatch` rather than answering for the wrong organism. A candidate that reports no organism is never excluded (a disease or pathway has none), so one flag is safe on `path`'s two endpoints. On a name whose every candidate belongs to another species the command fails `not_found` naming those species, never an empty result.
* Structural hops are excluded by default: an intermediate node may not be an organism taxon, a clinical trial or a study, and `in_taxon` / `chemically_similar_to` hops are not walked (a direct one between the two endpoints included). The endpoints themselves are never excluded. Pass --include-structural to walk them; with --predicate, the predicate set you name is what is walked, structural or not.
* \--organism is a single flag covering BOTH endpoints, unlike --type-a/--type-b: a cross-species path is not a question anyone asks, and an endpoint that carries no organism (a disease, a pathway) is never excluded by it -- so `--organism human` narrows the gene endpoint and leaves the other alone.
* `path` takes two identifiers, so --expect is spelled --expect-from / --expect-to here; each is optional and checked independently.
* \--hops is a maximum, not an exact length: a 2-hop route is returned by --hops 4. Out-of-range values are rejected, never clamped, so a result is always at the depth asked for. Deeper searches are strongly worth pairing with --predicate: without it, a 5-hop route is merely connected rather than mechanistically meaningful.
* `a` and `b` each run resolve, then the in-build check, then --expect/--type verification, but the two arguments are never staggered against each other: a failure in `a` at ANY of those three stages does not stop `b` from being carried through all three too, and both arguments' failures (whichever stage each hit) come back together in one error, not one at a time across repeated calls.
* Error contract: a failing `a`/`b` reports `error.code == "unresolved_arguments"` at the top level -- even when only one of the two fails -- with each argument's own `ambiguous`/`not_found`/`category_mismatch`/`type_unverified`/`taxon_mismatch`/`expect_unverified`/`entity_mismatch` code nested at `error.errors[i].code` instead, unlike `lookup`/`members`/`cat`/`ls`/`tree`, which report that code as `error.code` itself. A caller parsing `path`'s stderr JSON for one of those bare `error.code` values needs to check `error.errors[].code` instead.
* The three output flags have a precedence, and the CLI names on stderr any flag that did not change the output. `--json` supersedes both widening flags: the JSON envelope carries every field unconditionally, so `--full`/`--provenance` change nothing alongside it. `--full` supersedes `--provenance`: it prints every field, the provenance envelope included. `--provenance` widens edge-shaped results only -- a node- or membership-shaped result has no provenance envelope to widen, and `cat NODE/provenance` already shows the node's own sources and publications without it. `--full` widens any result with a compact view except a membership set, whose compact view is already the complete record. A result with no compact view at all (`tree --depth 2`, `search --count`) prints as the JSON envelope whether or not `--json` is passed, so both widening flags are inert there. None of these combinations is an error, and none of them changes which rows come back or what the record contains -- only which fields are printed.

**Arguments**

| Argument | Type | Required | Description                    |
| -------- | ---- | -------- | ------------------------------ |
| `a`      | str  | yes      | Source entity (name or CURIE). |
| `b`      | str  | yes      | Target entity (name or CURIE). |

**Options**

| Option                  | Type            | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------- | --------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--predicate`           | str, repeatable | no       | —       | Restrict every hop to this predicate (repeatable). A path with a hop outside the set is not returned.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `--hops`                | int             | no       | —       | Maximum path length, 1-5 (default 2). Depth 5 needs a build carrying the integer-id artifacts; a build without them refuses the search rather than quietly searching shallower.                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `--type-a`              | str             | no       | —       | Filter entity a's candidates by category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `--type-b`              | str             | no       | —       | Filter entity b's candidates by category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `--include-structural`  | bool            | no       | `False` | Also walk structural hops. By default a path's intermediate nodes exclude organism taxa, clinical trials and studies, and `in_taxon` / `chemically_similar_to` hops are not walked: they connect almost everything to everything and carry no mechanism. The two endpoints are never excluded.                                                                                                                                                                                                                                                                                                                                          |
| `--organism`, `--taxon` | str             | no       | —       | Restrict to one species, e.g. `--organism human` (also `--taxon`). Accepts human, mouse, rat, zebrafish, fly, worm, yeast, an NCBITaxon CURIE (NCBITaxon:9606), or a bare taxon id (9606). This is the fix for a gene symbol that resolves to several species' orthologs -- `--type` cannot separate those, since every one of them is a Gene. Entities that carry no organism at all (a disease, a pathway, a chemical) are never filtered out by it. Distinct from `--species`, which filters on an edge's `species_context` qualifier -- the organism the experiment was run in, not the organism of the entities the edge connects. |
| `--expect-from`         | str             | no       | —       | Source endpoint. Assert which entity the identifier names, e.g. `--expect RALGDS`. Matched against the resolved node's canonical name and synonyms, case-insensitively; a mismatch fails the command instead of answering for the wrong entity. Use it whenever the identifier came from outside this CLI (memory, a paper, another tool) rather than from `search`/`lookup`.                                                                                                                                                                                                                                                           |
| `--expect-to`           | str             | no       | —       | Target endpoint. Assert which entity the identifier names, e.g. `--expect RALGDS`. Matched against the resolved node's canonical name and synonyms, case-insensitively; a mismatch fails the command instead of answering for the wrong entity. Use it whenever the identifier came from outside this CLI (memory, a paper, another tool) rather than from `search`/`lookup`.                                                                                                                                                                                                                                                           |
| `--json`                | bool            | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--full`                | bool            | no       | `False` | Widen compact TSV output to every field (still TSV, not JSON). No effect alongside --json, which already prints every field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--provenance`          | bool            | no       | `False` | Widen compact TSV output to the full provenance envelope -- knowledgeSources, publications, knowledgeLevel, agentType, publicationsInfo, properties -- instead of the one-line summary shown by default. Edge-shaped results only; ignored by `search`/`lookup`/`members` (`path` is edge-shaped now -- each hop widens too). No effect alongside --json or --full, both of which already print these fields.                                                                                                                                                                                                                           |
| `--debug`               | bool            | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

**Examples**

```bash
mithrl path ABCB1 scurvy
mithrl path ABCB1 scurvy --predicate regulates --predicate affects
mithrl path ABCB1 scurvy --type-a Gene --type-b Disease
mithrl path EGFR "lung cancer" --type-a Gene --type-b Disease --organism human
mithrl path ABCB1 scurvy --hops 4 --predicate regulates
mithrl path imatinib SRC --include-structural
mithrl path NCBIGene:5900 MONDO:0005105 --expect-from RALGDS --expect-to melanoma
```

### `mithrl score`

Rank targets along a curated metapath by calibrated DWPC. z ranks; z is not a p-value.

**Constraints**

* A CURIE is accepted as given: a well-formed identifier for the WRONG entity scores exactly as cleanly as the right one. Read the `resolved:` line this prints to stderr (canonical name, CURIE, category, organism) and confirm it is the entity you meant before using the ranking. An identifier the active build holds no node for fails as `not_found` rather than returning an empty ranking, so `no targets` is an answer about the graph and not about a name it does not have. The source must hold the metapath's starting category -- a compound, for a compound-first metapath -- or nothing is reached.
* z RANKS; z IS NOT A p-VALUE. Do not convert it to a probability, a significance level, or a false-discovery rate, and do not read z = 2 as any particular tail. The permutation null is strongly right-skewed, so normal-tail arithmetic does not apply to it -- an observed score of exactly 0.0 sits at a median z of -0.57, where a normal null would require -inf. Use z to order candidates against each other; nothing more.
* A null z is WITHHELD, never zero and never a low score: it means the pair could not be calibrated at all. Those rows render their reason CODE in the z column (e.g. `withheld:CELL_INSUFFICIENT`) rather than a number, a dash or a blank, and their dwpc must not be compared against another row's z. The graph's own sentence for each code is printed under the table and carried in --json as `zWithheld.detail`.
* Read targetDegree beside z, always. The top of a z ranking is dominated by TINY-DEGREE targets -- a disease associated with a single gene, where the source happens to bind that gene, scores z around 20. Those are real excursions under the null, not artifacts, but whether they are biologically interesting is a judgement calibration cannot make. No minimum-support floor is applied; the column is how you apply your own.
* When no null table applies, the response says so and the ranking falls back to RAW dwpc -- which is degree-confounded (measured rank correlation with target degree +0.567, against the calibrated score's -0.043), so the order is substantially an order on popularity. The command states this outright rather than rendering an uncalibrated table that looks like a calibrated one.
* \--paths requires --target and --path-limit requires --paths: routes are enumerated for a single pair, and both flags are ignored by the API otherwise. Both are refused rather than silently dropped.
* Ranked results have no cursor -- the ranking is computed whole and then trimmed by --limit, so a truncated table is recovered with a bigger --limit, never with `results --page 2`.
* Neither widening flag is offered here, and neither is needed: the compact view already carries every field the API returns for a ranked row, which is the same claim `schema` makes for its own rows. --provenance would be inert regardless -- the provenance envelope belongs to edges, and a ranked row is not one.

**Arguments**

| Argument | Type | Required | Description                           |
| -------- | ---- | -------- | ------------------------------------- |
| `source` | str  | yes      | Entity to score from (name or CURIE). |

**Options**

| Option         | Type | Required | Default                                       | Description                                                                                                                                                                                                                                              |
| -------------- | ---- | -------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--metapath`   | str  | no       | `COMPOUND_BINDS_GENE_ASSOCIATED_WITH_DISEASE` | Which curated mechanism to walk. Metapaths are named, not author-supplied: each has a permutation null table provisioned against the serving build, which is what makes z available at all. Catalogue: COMPOUND\_BINDS\_GENE\_ASSOCIATED\_WITH\_DISEASE. |
| `--target`     | str  | no       | —                                             | Score this one target instead of ranking all of them. A pair reached by no route scores an exact 0.0 -- a measurement, not a failure.                                                                                                                    |
| `--limit`      | int  | no       | —                                             | How many ranked targets to return (server default 100, clamped to 500). The ranking is computed over every reached target and then trimmed.                                                                                                              |
| `--paths`      | bool | no       | `False`                                       | List the concrete routes behind the score. Needs --target.                                                                                                                                                                                               |
| `--path-limit` | int  | no       | —                                             | How many routes --paths lists (server default 20, clamped to 200).                                                                                                                                                                                       |
| `--json`       | bool | no       | `False`                                       | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen.                                                                                             |
| `--debug`      | bool | no       | `False`                                       | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                            |

**Examples**

```bash
mithrl score DRUGBANK:DB00619
mithrl score imatinib --limit 25
mithrl score DRUGBANK:DB00619 --target MONDO:0011996
mithrl score DRUGBANK:DB00619 --target MONDO:0011996 --paths --path-limit 50
mithrl score DRUGBANK:DB00619 --json
```

### `mithrl schema`

List the vocabularies the active build carries — the valid predicates (`--rel`/`--predicate`), categories (`--type`), and knowledge sources (`--source`) — plus the build pin every result is reproducible against.

**Constraints**

* The three narrowing flags are additive, and passing none of them lists all three vocabularies -- so a value whose vocabulary you don't yet know can be found without picking one first.
* Rows are `kind<TAB>value<TAB>version`; only a source row carries a version.
* These are the vocabularies the ACTIVE BUILD carries, which is a subset of what the schema accepts -- a predicate the schema allows but this build has no edges for is absent here rather than listed as something to query.
* Predicates and categories are case-sensitive. A value outside these lists is refused as `invalid_input` before any query is sent, with a suggestion where one is close enough to name; it is never retried and never reported as an upstream outage.
* \--full/--provenance are not offered here: the compact view already carries every field a vocabulary row has, and there is no provenance envelope to widen.

**Options**

| Option         | Type | Required | Default | Description                                                                                                                                                  |
| -------------- | ---- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--predicates` | bool | no       | `False` | List only the predicates (the values --rel/--predicate accept).                                                                                              |
| `--categories` | bool | no       | `False` | List only the categories (the values --type/--type-a/--type-b and a match pattern accept).                                                                   |
| `--sources`    | bool | no       | `False` | List only the contributing knowledge sources, with the version of each.                                                                                      |
| `--json`       | bool | no       | `False` | Print the full machine-readable JSON envelope instead of compact TSV. Supersedes --full/--provenance -- the envelope already carries every field they widen. |
| `--debug`      | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                |

**Examples**

```bash
mithrl schema
mithrl schema --predicates
mithrl schema --predicates | grep interacts
mithrl schema --sources --json
```

## Workflow triggering

List and trigger the analysis workflows that run behind the API.

### `mithrl tox-endpoints`

List the curated toxicity endpoints a Tox run can be scoped to. Each `id` is a value for `run <tox workflow> --param scope=<id>` (comma-separate several; omit scope for all).

**Constraints**

* The list is a committed, version-pinned artifact served whole -- there is nothing to page. The MONDO and GO release pins it was materialized from are reported on stderr (and in --json), because an endpoint's size moves with them.
* `curie_count` counts DISTINCT CURIES, not genes. An endpoint's gene set is whatever has edges to those CURIEs, so this is a proxy for how broad the endpoint is and must not be read as the size the statistics were computed over.
* An unknown scope id fails the run rather than being skipped -- asking for an endpoint that does not exist and silently getting a smaller family back is the shape of a typo that survives into a published q-value. The failure names the valid ids.

**Options**

| Option    | Type | Required | Default | Description                                                                                                                                             |
| --------- | ---- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--json`  | bool | no       | `False` | Print the full JSON envelope -- each endpoint's description and per-source provenance counts, plus the MONDO/GO release pins -- instead of compact TSV. |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                           |

**Examples**

```bash
mithrl tox-endpoints
mithrl tox-endpoints --json
```

### `mithrl workflows`

List available workflows or inspect one workflow's graph, required file inputs, and params.

**Constraints**

* Several presets are near-duplicates within a family, and the shorter name is not always the better analysis. The tox family (`compound-tox-card` / `compound-tox-profile` / `toxicity-endpoint-analysis`) and the pathway family (`pathway-participants` / `pathway-lookup` / `canonical-pathway-analysis`) each have the same answer: `toxicity-endpoint-analysis` and `canonical-pathway-analysis` are the ones that run a statistical background test; the other two names in each family are listings with no test behind them -- pick the tested preset when you want evidence rather than a ranking, and name the id you ran when you report the result. These two families are not the whole catalog: `mithrl workflows` also lists `upstream-regulator-analysis`, `overrepresentation` and `regulator-breakdown` directly, so list it rather than treating this prose as exhaustive.
* Each param reports `effective` -- the value the run uses if you pass nothing -- alongside `default`, which is the part's own fallback and may differ. `effective_source` says where it came from: `workflow` for a value the workflow pinned, `part_default` for one it left alone, and `workflow_placeholder` for a BLANK pin -- a visible prompt, not a choice: `required: true` says you must supply that one or submit rejects the run. Where several nodes declare the same param with different values, `effective` is null and `effective_by_node` carries them. Read `effective`, not `default`, to know what a run will do.

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--id`    | str  | no       | —       | Workflow ID to inspect in detail.                                             |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl workflows
mithrl workflows --id upstream-regulator-analysis
```

### `mithrl run`

Trigger an analysis workflow (URA / Pathways / Tox) through the API to the Workflow Engine.

**Constraints**

* `mithrl workflows` is the only authority on what exists. A workflow family named in a plan, a paper, or this CLI's own prose is not an id: run the catalog first and pick an id out of it, rather than guessing at a name (an unknown one fails `not_found`, listing what is actually available).
* Several presets are near-duplicates within a family, and the shorter name is not always the better analysis. The tox family (`compound-tox-card` / `compound-tox-profile` / `toxicity-endpoint-analysis`) and the pathway family (`pathway-participants` / `pathway-lookup` / `canonical-pathway-analysis`) each have the same answer: `toxicity-endpoint-analysis` and `canonical-pathway-analysis` are the ones that run a statistical background test; the other two names in each family are listings with no test behind them -- pick the tested preset when you want evidence rather than a ranking, and name the id you ran when you report the result. These two families are not the whole catalog: `mithrl workflows` also lists `upstream-regulator-analysis`, `overrepresentation` and `regulator-breakdown` directly, so list it rather than treating this prose as exhaustive.
* Blocking by default: if the bounded wait elapses with the run still active, the payload reports `data.poll_exhausted: true` with the run\_id and `data.next_command`, and the command exits 3 (incomplete) rather than 0. `--no-wait` returns the acceptance envelope and exits 0.
* File inputs are CSV with a header row. The feature table (`omics_csv`, and `background_csv` where a preset takes one) is read by its `entity` column unless you say otherwise: pass `--param entity_column=<your column>` to use a file you already have, or rename the column to `entity`. `--param effect_column=` and `--param significance_column=` bind the optional effect-size and significance columns the same way. A column that does not exist is refused at submit, naming the file, the column asked for, and the columns the file actually has.
* Exits 4 (remote failure), not 0, when the run it waited for reached `failed` or `cancelled`. The payload is still printed in full and the server's `failure` block (code, stage, category, message) is named on stderr. `--no-wait` is unaffected: an acceptance envelope is that invocation's success.

**Arguments**

| Argument   | Type | Required | Description                                                                                                    |
| ---------- | ---- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `workflow` | str  | yes      | Workflow to trigger: an id from `mithrl workflows` (e.g. upstream-regulator-analysis) or a custom `wf_...` id. |

**Options**

| Option      | Type            | Required | Default | Description                                                                                                                                     |
| ----------- | --------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `--input`   | str, repeatable | no       | —       | Bind a workflow FILE input as name=@local-file or name=existing-file\_ref (repeatable). Scalar values, including CURIEs, go on --param instead. |
| `--param`   | str, repeatable | no       | —       | A workflow parameter as key=value (repeatable). `mithrl workflows --id <id>` lists every param and which ones are required.                     |
| `--label`   | str             | no       | —       | Run label. Reuse the same label to retry idempotently; omit it for a unique default.                                                            |
| `--no-wait` | bool            | no       | `False` | Return the acceptance envelope immediately; reattach with `mithrl status <id> --watch`.                                                         |
| `--debug`   | bool            | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                   |

**Examples**

```bash
mithrl run upstream-regulator-analysis --input omics_csv=@data.csv --input background_csv=@background.csv
mithrl run upstream-regulator-analysis --input omics_csv=art_01EXAMPLE --input background_csv=art_01BGEXAMPLE --label cohort-a
```

### `mithrl upload`

Stage a local file for workflow reuse and print its file\_ref.

**Arguments**

| Argument | Type | Required | Description           |
| -------- | ---- | -------- | --------------------- |
| `file`   | str  | yes      | Local file to upload. |

**Options**

| Option     | Type | Required | Default | Description                                                                   |
| ---------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--resume` | str  | no       | —       | Resume an interrupted upload by upload\_id.                                   |
| `--debug`  | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl upload data.csv
mithrl upload data.csv --resume up_01EXAMPLE
```

### `mithrl status`

Inspect an existing workflow run and optionally watch it to a terminal state.

**Constraints**

* Exits 4 (remote failure), not 0, when the run reached `failed` or `cancelled`. The full run record is still printed, and the server's `failure` block (code, stage, category, message) is named on stderr.
* `--watch` is bounded so the command always terminates. If it elapses with the run still active, the payload reports the run's current state with `data.next_command` to resume watching, and the command exits 3 (incomplete) rather than 0. Without `--watch` a run that is merely still running exits 0 — a point-in-time read waited for nothing and so cannot be incomplete.
* A run that is still queued reports how long it has been waiting, and approximately what position it holds, on stderr and in `data.run.queue`. The position is approximate — it is derived from queue timestamps — and the queue is shared across all organizations, so a run can wait behind work that is not yours.

**Arguments**

| Argument | Type | Required | Description      |
| -------- | ---- | -------- | ---------------- |
| `run_id` | str  | yes      | Workflow run ID. |

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--watch` | bool | no       | `False` | Poll with bounded backoff until terminal or reattach.                         |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl status run_01EXAMPLE --watch
```

### `mithrl runs`

List workflow runs with optional status filtering and page-based pagination.

**Options**

| Option     | Type | Required | Default | Description                                                                   |
| ---------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--status` | str  | no       | —       | Filter by lifecycle status.                                                   |
| `--page`   | int  | no       | `1`     | Page number (1-based).                                                        |
| `--per`    | int  | no       | `50`    | Runs per page (1-200).                                                        |
| `--debug`  | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl runs --status running --page 1 --per 25
```

### `mithrl cancel`

Request cooperative cancellation of an active workflow run.

**Arguments**

| Argument | Type | Required | Description      |
| -------- | ---- | -------- | ---------------- |
| `run_id` | str  | yes      | Workflow run ID. |

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl cancel run_01EXAMPLE
```

### `mithrl rerun`

Fork a completed workflow run with the same resolved inputs and configuration.

**Arguments**

| Argument | Type | Required | Description             |
| -------- | ---- | -------- | ----------------------- |
| `run_id` | str  | yes      | Parent workflow run ID. |

**Options**

| Option              | Type | Required | Default | Description                                                                    |
| ------------------- | ---- | -------- | ------- | ------------------------------------------------------------------------------ |
| `--idempotency-key` | str  | no       | —       | Reuse a printed key to retry one fork idempotently; omit it to fork a new run. |
| `--debug`           | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).  |

**Examples**

```bash
mithrl rerun run_01EXAMPLE
```

### `mithrl retry`

Retry a failed retryable workflow run from its recovery boundary.

**Arguments**

| Argument | Type | Required | Description             |
| -------- | ---- | -------- | ----------------------- |
| `run_id` | str  | yes      | Failed workflow run ID. |

**Options**

| Option              | Type | Required | Default | Description                                                                    |
| ------------------- | ---- | -------- | ------- | ------------------------------------------------------------------------------ |
| `--idempotency-key` | str  | no       | —       | Reuse a printed key to retry one fork idempotently; omit it to fork a new run. |
| `--debug`           | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).  |

**Examples**

```bash
mithrl retry run_01EXAMPLE
```

### `mithrl run-results`

Fetch workflow findings and optionally stream durable artifacts to disk.

**Constraints**

* Exits 4 (remote failure), not 0, when the run reached `failed` or `cancelled`. The results payload is still printed, and the server's `failure` block (code, stage, category, message) is named on stderr.
* A run that succeeded with an empty result (`is_empty: true`) still exits 0; the emptiness is reported on stderr, and any `warnings` on the payload are where a cause would be recorded.

**Arguments**

| Argument | Type | Required | Description      |
| -------- | ---- | -------- | ---------------- |
| `run_id` | str  | yes      | Workflow run ID. |

**Options**

| Option       | Type | Required | Default | Description                                                                   |
| ------------ | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--download` | str  | no       | —       | Directory for direct presigned artifact downloads.                            |
| `--debug`    | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl run-results run_01EXAMPLE
mithrl run-results run_01EXAMPLE --download ./artifacts
```

### `mithrl validate`

Dry-run validation of an inline workflow graph without creating a run.

**Arguments**

| Argument     | Type | Required | Description                                               |
| ------------ | ---- | -------- | --------------------------------------------------------- |
| `graph_file` | str  | yes      | Path to a workflow graph document in JSON or YAML format. |

**Options**

| Option    | Type | Required | Default | Description                                                                   |
| --------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--debug` | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl validate graph.yaml
```

## Composability layer

Replay, narrow, and serialize a stored result set without recomputing it.

### `mithrl results`

Replay/page/reformat a stored result set without recompute.

**Constraints**

* `total` counts the rows fetched so far, not the size of the result set. It is a high-water mark: paging further into a continuable result (a hub node's neighborhood) fetches more rows, so the same result id reports a larger `total` on a later page than on the first. `rows_fetched` is the same number under that name, and `truncated` is what says whether rows exist beyond them.
* `columns` echoes the --columns request and is empty when no projection was asked for; `body_columns` is what the served `body` actually carries, in order -- for --format tsv it is the header row.

**Arguments**

| Argument | Type | Required | Description          |
| -------- | ---- | -------- | -------------------- |
| `id`     | str  | yes      | Result ID to replay. |

**Options**

| Option      | Type            | Required | Default | Description                                                                   |
| ----------- | --------------- | -------- | ------- | ----------------------------------------------------------------------------- |
| `--page`    | int             | no       | `1`     | Page number.                                                                  |
| `--per`     | int             | no       | —       | Results per page.                                                             |
| `--format`  | str             | no       | `tsv`   | Output format: tsv, jsonld, graphml, or cypher.                               |
| `--columns` | str, repeatable | no       | —       | Columns to include (repeatable).                                              |
| `--debug`   | bool            | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl results q_abc123 --page 2
```

### `mithrl filter`

Narrow a result set with a structured predicate (no NL). Filters the stored rows; if the source result was left incomplete (a hub node's later pages), the missing pages are fetched first, with progress on stderr and bounded by --timeout.

**Constraints**

* Exits 3 (incomplete), not 0, when the missing-pages fetch above is cut short by --timeout or its own page limit -- the payload on stdout still carries the real matches found so far, but matches may exist in rows not yet retrieved (see the payload's note). A genuine upstream cap (the source itself ran out of pages) still exits 0.

**Arguments**

| Argument | Type | Required | Description          |
| -------- | ---- | -------- | -------------------- |
| `id`     | str  | yes      | Result ID to filter. |

**Options**

| Option      | Type  | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------- | ----- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--where`   | str   | yes      | —       | A single '\<field> \<op> \<value>' predicate: ==, !=, >, >=, <, <= (dot-path fields like properties.combined\_score work). \<value> is coerced to int, then float, then left as a string — quote it (e.g. '007') to force a literal string comparison against a numeric-looking value.                                                                                                                                                                  |
| `--timeout` | float | no       | —       | Seconds to spend fetching the source result's missing pages before answering with what was fetched. Default is sized from the result's row count (about 1.5s per 100-row page, up to a 300s ceiling), falling back to 30s when the size is unknown. Progress is printed to stderr while fetching, and the payload's `note` says so when this budget is what stopped the fetch. Fetched pages are cached, so a re-run resumes rather than starting over. |
| `--debug`   | bool  | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                           |

**Examples**

```bash
mithrl filter q_abc123 --where "category == Gene"
```

### `mithrl export`

Serialize a result set/subgraph, escaped per format.

**Constraints**

* Writes the serialized document to stdout, so `mithrl export <id> --format tsv > rows.tsv` produces a file a TSV reader can open. --json prints the JSON envelope instead, with the document inside `body` as a JSON string.
* Exits 3 (incomplete), not 0, when the missing-pages fetch above is cut short by --timeout or its own page limit -- the document on stdout is still a valid prefix of the result (--json's `note` says how to resume). A genuine upstream cap (the source itself ran out of pages) still exits 0.

**Arguments**

| Argument | Type | Required | Description          |
| -------- | ---- | -------- | -------------------- |
| `id`     | str  | yes      | Result ID to export. |

**Options**

| Option      | Type  | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------- | ----- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--format`  | str   | yes      | —       | tsv, jsonld, graphml, or cypher.                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `--timeout` | float | no       | —       | Seconds to spend fetching the source result's missing pages before answering with what was fetched. Default is sized from the result's row count (about 1.5s per 100-row page, up to a 300s ceiling), falling back to 30s when the size is unknown. Progress is printed to stderr while fetching, and the payload's `note` says so when this budget is what stopped the fetch. Fetched pages are cached, so a re-run resumes rather than starting over. |
| `--json`    | bool  | no       | `False` | Print the JSON envelope (result\_id, format, truncated, note) with the serialized document inside `body`, instead of writing the document itself to stdout.                                                                                                                                                                                                                                                                                             |
| `--debug`   | bool  | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted).                                                                                                                                                                                                                                                                                                                                                                           |

**Examples**

```bash
mithrl export q_abc123 --format tsv > rows.tsv
```

## Feedback

Tell us how mithrl is working for you.

### `mithrl feedback`

Send us your feedback on mithrl, optionally with a 0-3 rating.

**Constraints**

* Run with no arguments at all to be prompted for both fields; give any argument and the message becomes required.
* Message is capped at 4000 characters.

**Arguments**

| Argument  | Type | Required | Description                                                                                                                                         |
| --------- | ---- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message` | str  | no       | Your feedback, as a quoted string. Required unless you run the command with no arguments at all, which prompts for it instead. Max 4000 characters. |

**Options**

| Option           | Type | Required | Default | Description                                                                   |
| ---------------- | ---- | -------- | ------- | ----------------------------------------------------------------------------- |
| `-r`, `--rating` | int  | no       | —       | How you'd rate mithrl: 0-Poor, 1-Fine, 2-Good, 3-Great. Optional everywhere.  |
| `--debug`        | bool | no       | `False` | Print raw request/response headers and payloads to stderr (secrets redacted). |

**Examples**

```bash
mithrl feedback
mithrl feedback "The graph is missing the pathway I needed."
mithrl feedback -r 2 "Good, but the local graph is too small."
```
