---
name: stella-cli
description: >-
  Drive the stella command-line client (@stll/cli), a legal-workspace CLI whose
  command surface is generated from the stella MCP tool registry. Covers install,
  OAuth login, the full command tree grouped by domain, JSON output for scripting,
  the --input escape hatch for deep payloads, cursor pagination, destructive-op
  confirmation, and exit codes.
metadata:
  type: reference
  library: "@stll/cli"
---
<!-- GENERATED by `bun run codegen` (packages/cli/src/generate-skill.ts). Do not edit by hand. -->

# stella CLI

`@stll/cli` is the command-line client for stella, an open-source legal
workspace. Curated tools use `stella <domain> <action>`; generated capability
commands use `stella capability <domain> <action>`. Both surfaces are
generated from the stella MCP tool registry, so they mirror exactly the tools
a stella server exposes. Every command works for humans, scripts, and agents alike.

## Install

```sh
npm i -g @stll/cli
```

## Authenticate

```sh
stella auth login --server <url>
```

Login runs an OAuth 2.1 authorization-code flow with PKCE against the stella
server, using a loopback listener (`http://127.0.0.1/callback`, ephemeral port)
to capture the code. Credentials are stored per server origin, so one machine
can hold sessions for several servers at once. The first login needs
`--server <url>` (or `STELLA_SERVER_URL`); it then becomes the default, and
every command accepts `--server <url>` to target another one. A default
login requests the working set of scopes (everything but organization
administration writes and one-off setup); pass `--scopes` to request an
explicit set. The
default scopes are `openid profile email offline_access stella:read stella:search stella:templates stella:documents_write stella:matters_write stella:contacts_write stella:chat stella:knowledge_write stella:billing_write stella:admin_read stella:skills stella:feedback`.
`stella auth whoami` shows the active session; `stella auth logout` clears it.

## Conventions every agent must know

- **Output format**: table is the default only on a TTY; piped/non-TTY output
  defaults to JSON. Force it with `--output json|table` (or `--json` / `--table`).
  Always pass `--output json` when scripting or parsing.
- **Deep payloads**: any command accepts `--input '<json>'` for the whole tool
  argument object, `--input @file` to read JSON from a file, or `--input -` to
  read JSON from stdin. Individual string flags also take gh-style `@file` / `@-`
  sugar (use `@@` to pass a literal leading `@`).
- **Reading a command's contract**: `--schema` prints that command's input JSON
  schema (the same schema the MCP tool validates against) and exits 0, without
  calling the server.
- **Sending a local document**: a command whose tool takes a document also takes
  `--file <path>`; the CLI reads the file and sends it in the tool's own base64
  field, the call an MCP host would make with the file attached. `--help` states
  the size ceiling, which is the one that field's schema declares. A larger file
  is refused: send it from a host that can attach it to the tool's file
  reference, never by re-exporting the document to fit.
- **Array flags** are repeatable: pass the flag once per value.
- **Pagination**: list commands take `--cursor <c>` and `--limit <n>`; `--all`
  follows cursors up to bounded ceilings. The `nextCursor` resume hint is written
  to stderr (`more: --cursor <c>`) so piped JSON on stdout stays clean.
- **Destructive commands** (delete/remove) prompt for confirmation on a TTY and
  require `--yes` when there is no TTY to confirm on. The CLI owns the server's
  `confirm` gate: it injects `confirm: true` only after you confirm (or pass
  `--yes`), so there is no separate `--confirm` flag to pass.
- **Errors** print `error: <message>` (and `hint: <next step>` when the server
  supplies one) to stderr as plain text, never to stdout, so a scripted stdout
  stays clean even with `--output json`. Every tool error carries a stable
  machine `code` that maps to the process exit code (see below): branch on the
  exit code, and read the `error:`/`hint:` lines for the human-readable message.
- **Finding and reading text**: `stella search matters --query '<q>'` returns
  matching documents with their entity ids, and
  `stella document content --entity-id <id>` prints one document's text
  (windowed, so follow `--cursor`).
- **MCP resources**: `stella reference list` enumerates static server resources;
  `stella reference show <name>` prints one.
- **Uploading a file**: `stella upload --file <file> --matter-id <matter-id>` uploads a local file as a new document; add `--entity-id <id>` to upload it as a new version of an existing document instead — a CLI-native path (the CLI reads the file itself), separate from the MCP `upload_document_version`/`open_document_version_upload` tools (which take a host-supplied file reference and are excluded from the CLI).

## Command tree

Generated from the MCP tool registry; `Access` is the OAuth scope the command
requires (request it at `stella auth login --scopes`).

| Domain | Command | Access | Notes |
| --- | --- | --- | --- |
| annotation | `stella annotation create` | knowledge_write |  |
| annotation | `stella annotation delete` | knowledge_write | destructive (needs `--yes` off a TTY) |
| annotation | `stella annotation list` | read | paginated |
| annotation | `stella annotation update` | knowledge_write |  |
| audit-log | `stella audit-log list` | admin_read | paginated |
| capability | `stella capability describe` | read |  |
| capability | `stella capability invoke` | read |  |
| capability | `stella capability list` | read | paginated |
| case-law | `stella case-law citations` | read | paginated |
| case-law | `stella case-law coverage` | read |  |
| case-law | `stella case-law lookup` | read |  |
| case-law | `stella case-law read` | read |  |
| case-law | `stella case-law search` | search | paginated |
| clause | `stella clause delete` | knowledge_write | destructive (needs `--yes` off a TTY) |
| clause | `stella clause list` | read | paginated |
| clause | `stella clause save` | knowledge_write |  |
| contact | `stella contact check-counterparty` | read |  |
| contact | `stella contact delete` | matters_write | destructive (needs `--yes` off a TTY) |
| contact | `stella contact list` | read | paginated |
| contact | `stella contact lookup-registry` | read |  |
| contact | `stella contact read` | read |  |
| contact | `stella contact save` | matters_write |  |
| document | `stella document compare` | documents_write |  |
| document | `stella document comparison prepare` | documents_write |  |
| document | `stella document comparison prepare-from-links` | documents_write |  |
| document | `stella document content` | read | paginated; windowed text |
| document | `stella document delete` | documents_write | destructive (needs `--yes` off a TTY) |
| document | `stella document field set` | documents_write |  |
| document | `stella document list` | read | paginated |
| document | `stella document properties list` | read | paginated |
| document | `stella document read` | read |  |
| document | `stella document save` | documents_write |  |
| feedback | `stella feedback prepare` | feedback |  |
| feedback | `stella feedback submit` | feedback |  |
| invoice | `stella invoice list` | read | paginated |
| legislation | `stella legislation boe-search` | read | paginated |
| legislation | `stella legislation history` | read | paginated |
| legislation | `stella legislation provisions` | read |  |
| legislation | `stella legislation read` | read | paginated; windowed text |
| legislation | `stella legislation search` | search | paginated |
| matter | `stella matter delete` | matters_write | destructive (needs `--yes` off a TTY) |
| matter | `stella matter link-contact` | matters_write |  |
| matter | `stella matter list` | read | paginated |
| matter | `stella matter save` | matters_write |  |
| organization | `stella organization add-member` | admin_write |  |
| organization | `stella organization remove-member` | admin_write | destructive (needs `--yes` off a TTY) |
| organization | `stella organization set-jurisdictions` | onboarding |  |
| organization | `stella organization update-settings` | admin_write |  |
| playbook | `stella playbook list` | read | paginated |
| playbook | `stella playbook run` | knowledge_write |  |
| playbook | `stella playbook save` | knowledge_write |  |
| rate | `stella rate resolve` | read |  |
| search | `stella search matters` | search | paginated |
| task | `stella task delete` | matters_write | destructive (needs `--yes` off a TTY) |
| task | `stella task list` | read | paginated |
| task | `stella task save` | matters_write |  |
| template | `stella template configure-fields` | templates |  |
| template | `stella template create` | templates |  |
| template | `stella template fill` | templates |  |
| template | `stella template list` | templates | paginated |
| template | `stella template preview-conditions` | templates |  |
| template | `stella template save-filled new-document` | documents_write + templates |  |
| template | `stella template save-filled new-version` | documents_write + templates |  |
| time-entry | `stella time-entry delete` | billing_write | destructive (needs `--yes` off a TTY) |
| time-entry | `stella time-entry list` | read | paginated |
| time-entry | `stella time-entry save` | billing_write |  |
| usage | `stella usage get` | read |  |

## Command flags

Required: `--flag — description (type)`. Optional: one `optional: --a,
--b (enum1|enum2)` line, names only (`--help` has full descriptions).
Global flags (output/cursor/limit/all/yes/input; see Conventions above)
are omitted here. Input union keys are required unless marked `?`.

- `stella annotation create`
  - `--target-type` — decision (case law) or statute (legislation). (enum: decision, statute)
  - `--target-id` — The document: for a decision, its decisionId (read_case_law_decision, search_case_law); for a statute, the documentId of the consolidated version (read_statute). A statute's marks belong to that one version. (string)
  - optional: --visibility (private|shared)
  - via `--input` only: mark, passages
  - mark: kind="highlight": none; kind="comment": body:string. Example: `--input '{"mark":{"kind":"highlight"}}'`
- `stella annotation delete`
  - `--annotation-id` — The mark to delete: annotationId from list_reader_annotations, or the mark id the chat lists beside the user's marks. (string)
- `stella annotation list`
  - `--target-type` — decision (case law) or statute (legislation). (enum: decision, statute)
  - `--target-id` — The document: for a decision, its decisionId (read_case_law_decision, search_case_law); for a statute, the documentId of the consolidated version (read_statute). A statute's marks belong to that one version. (string)
- `stella annotation update`
  - `--annotation-id` — The mark to change: annotationId from list_reader_annotations, or the mark id the chat lists beside the user's marks. (string)
  - via `--input` only: change
  - change: type="body": body:string; type="color": color:"yellow" | "green" | "sky" | "violet" | "red"; type="style": style:string; type="visibility": visibility:"private" | "shared". Example: `--input '{"change":{"type":"body","body":"x"}}'`
- `stella audit-log list`
  - optional: --matter-id, --action, --resource-type, --resource-id, --user-id, --from, --to
- `stella capability describe`
  - `--capability` — Capability id to describe, as returned by list_capabilities (e.g. "time-entries.create"). (string)
- `stella capability invoke`
  - `--capability` — Capability id to invoke. Use an id list_capabilities returned. (string)
  - optional: --validate-only
  - via `--input` only: input
- `stella capability list`
  - optional: --domain, --access (all|read|write)
- `stella case-law citations`
  - `--decision-id` — Case-law decision ID (string)
  - `--direction` — Which side of the citation graph to read: 'cites' for the decisions this decision cites, 'cited_by' for the decisions that cite it. Citing is not agreeing: both sides carry negative treatments. (enum: cites, cited_by)
- `stella case-law coverage`
  - optional: --country
- `stella case-law lookup`
  - `--identifiers` — The references to resolve, at most 50 per call: a docket number as the court writes it (the sheet number after it is ignored) or an ECLI. Each is answered on its own. (string-array, repeatable)
  - `--country` — Required corpus country. Admitted: CZE. An ISO 3166-1 alpha-3 or alpha-2 code, or the country's name, is read. (string)
- `stella case-law read`
  - `--decision-ids` — The decisions to read, at most 20 per call. Each id is answered on its own, so one unknown id does not sink the rest. (string-array, repeatable)
  - optional: --max-chars, --page, --full, --text-version, --query, --include (details|metadata|textFields|source|citations|outline)
- `stella case-law search`
  - `--queries` — Several phrasings of ONE question, at most 5. Their pages are merged and deduplicated within the page, so a reformulation costs no extra round trip; one phrasing is a valid call. (string-array, repeatable)
  - `--country` — Required corpus country. Admitted: CZE. An ISO 3166-1 alpha-3 or alpha-2 code, or the country's name, is read. (string)
  - optional: --court, --courts, --category, --has-legal-sentence, --language, --decision-type, --source-id, --date-from, --date-to, --sort (relevance|newest), --strict
- `stella clause delete`
  - `--clause-id` — Clause id to delete (string)
- `stella clause list`
  - optional: --clause-id, --version-id, --category-id, --query, --include-categories
- `stella clause save`
  - optional: --clause-id, --title, --category-id, --language, --description, --usage-notes, --snapshot-version
  - via `--input` only: body, expected_body, metadata
- `stella contact check-counterparty`
  - `--check` — cz-insolvency: ISIR proceedings, company ID or person with full birth date. cz-vat-reliability: unreliable payer and bank accounts, tax ID or derived CZ+IČO. sanctions: all EU, UN and national lists; company ID, organization name, or person with any known birth date and nationalities. Use an advertised value; case and surrounding whitespace are normalized. (enum: cz-insolvency, cz-vat-reliability, sanctions)
  - via `--input` only: subject
  - subject: type="company-id": company_id:string; type="tax-id": tax_id:string; type="person": first_name:string, last_name:string, date_of_birth?:{precision="year"|"month"|"day"}, nationality_codes?:string[]; type="organization": name:string. Example: `--input '{"subject":{"type":"company-id","company_id":"x"}}'`
- `stella contact delete`
  - `--contact-id` — Contact ID to delete (string)
- `stella contact list`
  - optional: --query, --type (person|organization)
- `stella contact lookup-registry`
  - `--registry` — Business register to query (enum: ares, brreg, companies-house, denue, edgar, gcis, krs, orsr, prh, recherche-entreprises, rpo, vies)
  - `--query` — Canonical identifier (e.g. company number, VAT number) or company name (string)
  - optional: --detail (standard|full)
- `stella contact read`
  - `--contact-id` — Contact ID (string)
- `stella contact save`
  - optional: --contact-id, --type (person|organization), --display-name, --first-name, --last-name, --organization-name, --notes, --nationality-codes
  - via `--input` only: date_of_birth
  - date_of_birth: precision="year": year:integer; precision="month": year:integer, month:integer; precision="day": year:integer, month:integer, day:integer. Example: `--input '{"date_of_birth":{"precision":"year","year":1000}}'`
- `stella document compare`
  - `--base-tracked-changes` — Tracked changes the base version already carries: accept compares its final text, keep leaves them in place, reject compares its original text. (enum: keep, accept, reject)
  - `--target-tracked-changes` — Tracked changes the target version already carries: accept compares its final text, keep leaves them in place, reject compares its original text. (enum: keep, accept, reject)
  - `--output-mode` — preview compares without writing. download returns each redline as a temporary link and saves nothing to the document. version saves each redline as a derived version and needs a stored-version source. (enum: preview, download, version)
  - optional: --mode (strict|best-effort), --granularity (word|character)
  - via `--input` only: source
  - source: type="versions": document_id:string, base_version_id:string, target_version_ids:string[]; type="previous": document_id:string, target_version_id:string; type="uploads": base_upload_id:string, target_upload_id:string. Example: `--input '{"source":{"type":"versions","document_id":"00000000-0000-4000-8000-000000000000","base_version_id":"00000000-0000-4000-8000-000000000000","target_version_ids":["00000000-0000-4000-8000-000000000000"]}}'`
- `stella document comparison prepare`
  - `--base.name` — File name to show the user, including the .docx suffix. (string)
  - `--base.size` — Exact byte length of the file, at most 50 MB. (int 1..52428800)
  - `--base.sha256-hex` — SHA-256 of the exact bytes you will PUT, as 64 hexadecimal characters. (string)
  - `--target.name` — File name to show the user, including the .docx suffix. (string)
  - `--target.size` — Exact byte length of the file, at most 50 MB. (int 1..52428800)
  - `--target.sha256-hex` — SHA-256 of the exact bytes you will PUT, as 64 hexadecimal characters. (string)
- `stella document comparison prepare-from-links`
  - `--base.url` — HTTPS link the server downloads the .docx from. A share link that opens a web page is not the file; use the direct-download form. (string)
  - `--target.url` — HTTPS link the server downloads the .docx from. A share link that opens a web page is not the file; use the direct-download form. (string)
  - optional: --base.name, --target.name
- `stella document content`
  - `--entity-id` — Entity ID (string)
- `stella document delete`
  - `--entity-id` — Document entity ID to delete (string)
  - optional: --version-id
- `stella document field set`
  - `--entity-id` — Document entity ID whose cell to set (string)
  - `--property-id` — Property ID, as returned by list_properties (string)
  - via `--input` only: content
  - content: type="text": value:string; type="single-select": value:string|null; type="multi-select": value:string[]; type="date": value:string|null; type="int": value:integer, currency?:string|null. Example: `--input '{"content":{"type":"text","value":"xxxxx"}}'`
- `stella document list`
  - `--matter-id` — Matter ID to list documents in. (string)
  - optional: --mode (flat|children), --parent-id
- `stella document properties list`
  - `--matter-id` — Matter ID to list properties for. (string)
- `stella document read`
  - `--entity-id` — Document entity ID (string)
  - optional: --version-id, --compare-with-version-id, --include-versions, --versions-cursor
- `stella document save`
  - optional: --entity-id, --matter-id, --name, --parent-id, --kind (document|folder), --move-to-root, --version-id, --label, --description
- `stella feedback prepare`
  - `--kind` — bug (it behaved wrongly), idea (it could be better), missing_capability (there is no way to do this), docs (the reference or description is wrong or missing). (enum: bug, idea, missing_capability, docs)
  - `--area` — Which part of stella the report is about. (enum: matters, documents, templates, case_law, legislation, contacts, tasks, billing, chat, mcp_cli, web_app, desktop, other)
  - `--title` — One line naming the problem, not the symptom's location. (string)
  - `--what-happened` — What stella actually did. (string)
  - optional: --expected, --steps, --evidence, --context.client (mcp|cli|web|desktop|other), --context.client-version, --context.request-id, --context.route, --context.error-reference
- `stella feedback submit`
  - `--kind` — bug (it behaved wrongly), idea (it could be better), missing_capability (there is no way to do this), docs (the reference or description is wrong or missing). (enum: bug, idea, missing_capability, docs)
  - `--area` — Which part of stella the report is about. (enum: matters, documents, templates, case_law, legislation, contacts, tasks, billing, chat, mcp_cli, web_app, desktop, other)
  - `--title` — One line naming the problem, not the symptom's location. (string)
  - `--what-happened` — What stella actually did. (string)
  - `--approval-token` — From prepare_feedback; covers only that report. (string)
  - optional: --expected, --steps, --evidence, --context.client (mcp|cli|web|desktop|other), --context.client-version, --context.request-id, --context.route, --context.error-reference
- `stella invoice list`
  - optional: --matter-id, --invoice-id
- `stella legislation boe-search`
  - optional: --query, --title, --department-code, --legal-range-code, --matter-code, --date-from, --date-to, --law-id, --block-id, --relation-type (modifies|modifiedBy|derogates|derogatedBy|all), --full-text
- `stella legislation history`
  - `--eli` — European Legislation Identifier of the work, as search_legislation returns it (for example https://www.e-sbirka.cz/eli/cz/sb/2012/89). It addresses the act, not one consolidation of it. A short, prefix-less or reordered ELI is read as the canonical one. (string)
  - `--anchor` — Publisher provision anchor; confirm it in read_statute's outline for the chosen consolidation. Czech e-Sbírka commonly uses par_<section>, -odst_<paragraph>, and -pism_<letter> (par_1729, par_1729-odst_1, par_1729-odst_2-pism_a). Subdivision anchors narrow the answer to that subdivision. Other publishers may use different schemes. (string)
  - optional: --language
- `stella legislation provisions` — no flags; pass `--input` with items
- `stella legislation read`
  - `--eli` — European Legislation Identifier of the work, as search_legislation returns it (for example https://www.e-sbirka.cz/eli/cz/sb/2012/89). It addresses the act, not one consolidation of it. A short, prefix-less or reordered ELI is read as the canonical one. (string)
  - optional: --language, --as-of
- `stella legislation search`
  - `--query` — Search query (string)
  - `--country` — Required corpus country. Admitted: CZE. An ISO 3166-1 alpha-3 or alpha-2 code, or the country's name, is read. (string)
  - optional: --document-type, --status, --language, --date-from, --date-to
- `stella matter delete`
  - `--matter-id` — Matter ID to delete (string)
- `stella matter link-contact`
  - `--matter-id` — Matter ID (string)
  - optional: --contact-id, --role (opposing_party|opposing_counsel|co_counsel|witness|expert_witness|third_party|judge|mediator|other), --matter-contact-id
- `stella matter list`
  - optional: --matter-id, --status (active|all)
- `stella matter save`
  - optional: --matter-id, --name, --client-id, --reference, --billing-reference, --status (active|archived)
- `stella organization add-member`
  - `--matter-id` — Matter ID for add_member and remove_member. (string)
  - `--user-id` — User id to add or remove for the member actions (string)
- `stella organization remove-member`
  - `--matter-id` — Matter ID for add_member and remove_member. (string)
  - `--user-id` — User id to add or remove for the member actions (string)
  - optional: --reassign-to
- `stella organization set-jurisdictions` — no flags; pass `--input` with jurisdictions
- `stella organization update-settings`
  - optional: --matter-number-pattern, --matter-number-padding, --prompt-caching-enabled, --document-processing-mode (off|searchable-text), --time-minimum-unit-minutes, --time-edit-window-days, --time-locked-through-month, --time-narrative-required, --time-zone
- `stella playbook list`
  - optional: --playbook-id
- `stella playbook run`
  - `--matter-id` — Matter ID to run the playbook over. (string)
  - `--playbook-id` — Playbook id to run (string)
- `stella playbook save`
  - optional: --playbook-id, --expected-updated-at, --name, --description, --scope.document-type-key, --scope.perspective (buyer|seller|neutral), --remove-source-ids
  - via `--input` only: positions
  - positions[]: mode="extract": issue:string, sources?:string[], ask:{question}; mode="graded": issue:string, sources?:string[], severity:"blocker" | "high" | "medium" | "low", tiers:object, negotiation?:object. Example: `--input '{"positions":[{"mode":"extract","issue":"x","ask":{"question":"x"}}]}'`
- `stella rate resolve`
  - `--matter-id` — Matter ID to resolve the rate in. (string)
  - `--user-id` — User ID to resolve the rate for (string)
  - `--date` — Date to resolve the rate on (ISO YYYY-MM-DD) (string)
- `stella search matters`
  - `--query` — Search query (string)
- `stella task delete`
  - `--task-id` — Task entity ID to delete (string)
- `stella task list`
  - optional: --matter-id, --task-id, --assignee (me|any|unassigned), --date-from, --date-to, --status
- `stella task save`
  - optional: --task-id, --matter-id, --name, --status (open|in_progress|in_review|done|cancelled), --priority (none|urgent|high|medium|low), --item-type (task|fact|issue|requirement|event), --list-id, --list-section-id, --list-description, --due-date, --workflow-reason, --add-assignee-user-id, --remove-assignee-user-id, --link-entity-id, --unlink-link-id
- `stella template configure-fields`
  - `--template-id` — Template to configure, as returned by create_template or list_templates (string)
  - via `--input` only: fields
- `stella template create`
  - optional: --template-id, --name, --docx-base64, --file.download-url, --file.file-id, --file.mime-type, --file.file-name, --file <path>
- `stella template fill`
  - `--template-id` — Template id, as returned by list_templates (string)
  - optional: --allow-unused-values, --completion-mode (require_complete|allow_partial), --output-mode (text|docx)
  - via `--input` only: values
- `stella template list`
  - optional: --template-id
- `stella template preview-conditions`
  - `--template-id` — Template whose AI-decided conditions to ask about, as returned by list_templates (string)
  - via `--input` only: values
- `stella template save-filled new-document`
  - `--template-id` — Template id, as returned by list_templates (string)
  - `--matter-id` — Matter receiving the filled DOCX. (string)
  - `--idempotency-key` — Unique retry key for this save operation; reuse it only to recover the same timed-out request (string)
  - optional: --parent-id, --name, --completion-mode (require_complete|allow_partial)
  - via `--input` only: values
- `stella template save-filled new-version`
  - `--template-id` — Template id, as returned by list_templates (string)
  - `--matter-id` — Matter receiving the filled DOCX. (string)
  - `--idempotency-key` — Unique retry key for this save operation; reuse it only to recover the same timed-out request (string)
  - `--entity-id` — Existing document entity id; required only for create_version (string)
  - optional: --name, --completion-mode (require_complete|allow_partial)
  - via `--input` only: values
- `stella time-entry delete`
  - `--time-entry-id` — Time entry ID to delete or write off (string)
- `stella time-entry list`
  - optional: --matter-id, --time-entry-id, --entity-id, --user-id, --date-from, --date-to, --status (draft|approved|billed|written_off)
- `stella time-entry save`
  - optional: --time-entry-id, --matter-id, --entity-id, --date-worked, --timezone-id, --duration-minutes, --narrative, --narrative-language, --invoice-narrative, --billable, --no-charge, --task-code, --activity-code
- `stella usage get` — no arguments

## Exit codes

| Code | Meaning |
| --- | --- |
| 0 | success |
| 1 | unexpected internal error |
| 2 | usage or input validation error |
| 3 | authentication required or failed (run `stella auth login`) |
| 4 | server or tool error |
| 5 | feature disabled for this organization |
| 6 | resource not found |
| 7 | confirmation aborted (a destructive op was declined) |
| 8 | permission denied (member role lacks the required permission) |
| 9 | usage entitlement exceeded |
| 10 | conflict with current state (duplicate or concurrent change) |

The exit code lines up with the tool-error `code`: `validation_error` -> 2,
`missing_scope` -> 3, `feature_disabled` -> 5, `not_found` -> 6,
`confirmation_required` -> 7, and `rate_limited` / `upstream_unavailable` /
`search_index_unavailable` / `unknown_tool` / `internal_error` -> 4. A legacy server that tags only a bare `feature_disabled`
code (no envelope) still maps to 5; anything else falls to 4.

## Capability commands (full surface)

Beyond the curated commands above, the CLI generates 409
capability commands from the server's capability catalog: every safe handler
that is not a curated tool, reached through the generic `invoke_capability`
path. Every generated command lives at `stella capability <domain> <action>`;
multi-segment capability actions are flattened with hyphens into `<action>`.

- **Discover**: `stella capability list [--domain <d>] [--access read|write]`
  enumerates them (paginated); `stella capability describe <id>` prints one
  capability's full input schema, scope, and flags.
- **Invoke by id** (forward-compatible with any server): `stella capability
  invoke <id> --input '<json>'`, where the JSON is `{ body?, params?, query? }`.
- **Flags**: each capability command derives flags from its input schema;
  matter-scoped capabilities take a required `--matter-id <id>`. Deep or
  ambiguous payloads use `--input` (the whole `{ body?, params?, query? }`).
- **Dry run**: write capabilities accept `--dry-run`, which validates the input
  server-side and returns without executing (maps to `validate_only`).
- **Destructive** capabilities prompt on a TTY and need `--yes` off a TTY; the
  server's per-capability confirm gate is satisfied automatically once confirmed.
- Exit codes are identical to the curated commands (see above).

### When no curated command fits

The curated commands above cover common tasks; anything else goes through the
generic capability path. Current domains: `audit-logs`, `billing-codes`, `case-law`, `catalogue`, `chat`, `clauses`, `contacts`, `document-translations`, `document-types`, `documents`, `entities`, `entity-views`, `expenses`, `fields`, `flows`, `invoices`, `legal-reader`, `legislation`, `lists`, `matters`, `number-series`, `organization-settings`, `playbooks`, `properties`, `rates`, `reports`, `saved-time-narratives`, `seller-profiles`, `signals`, `skills`, `style-sets`, `tasks`, `template-packs`, `template-recipes`, `templates`, `time-entries`, `time-timers`, `uploads`, `usage`, `vat-rates`, `view-templates`, `views`, `work-obligations`.

- Start a document translation run: `stella capability document-translations runs-create --matter-id <matter-id> --input '{"body":{"entityId":"00000000-0000-4000-8000-000000000000","fieldId":"00000000-0000-4000-8000-000000000000","targetLang":"value","engine":"deepl","output":"translated"}}'`.
- Start workflow extraction: `stella capability matters workflow-start --matter-id <matter-id> --input '{"body":{"serviceTier":"standard"}}'`.
- **`--input` casing is not uniform; never guess it.** A curated command's
  `--input` JSON (the table and flags above) uses the MCP tool schema's own
  keys, snake_case (`matter_id`, `contact_id`). A capability command's
  `--input` JSON uses the handler schema's own keys, camelCase (`fieldId`,
  `matterId`). Run `stella <command> --help` or `stella capability describe
  <id>` and copy the field paths it prints.

## Sending feedback

Two steps, so a human reads the report before it leaves the workspace.
`stella feedback prepare` sanitizes a draft and sends nothing: emails, UUIDs
and ULIDs, secret-looking tokens, non-allowlisted URLs, and IP addresses are
redacted server-side, and the sanitized report comes back in the shape the
next step accepts. Show it to the human. After they approve,
`stella feedback submit` stores the report, delivers it to the maintainers,
and returns a receipt (`FB-XXXX-XXXX`) to pass on. `submit` takes the
`approval_token` that `prepare` returned and refuses any other report;
it asks for confirmation, and `--yes` skips the prompt once the human
has approved.

Describe the problem, the steps, and expected versus actual behaviour. Put
the request id of a failed call in the context rather than in free text,
where it would be redacted. Refer to people by role, never by name. Never
include document text, client or matter names, ids, or secrets. The reporter
identity is stored privately and never published. Resubmitting identical
content within one day returns the same receipt.
