Invocation & Envelope

How a call is built, and what comes back. One grammar covers every platform action; one output contract covers every result. Everything on this page also rides every discovery response as the invocation, envelope, and pagination scaffolding.

The grammar

omni-cli <platform> <resource> <verb> [--flag value ...] [--json]

Every action parameter binds as a long flag --<name>. There are no positional arguments and no short flags, and all values are passed as strings — a param's type in discovery is a semantic and validation hint, not a shell type. The action's argv field gives the exact <platform> <resource> <verb> triple; never guess flag spelling — read the action's tier-3 discovery entry.

Global flags

Flag Type What it does
--context string work, personal, or both. Omit to auto-resolve when exactly one context is logged in — agents should pass it explicitly.
--json bool Machine-readable JSON output. Agents should always set it.
--verbose bool Diagnostic logging to stderr (never secrets).

Hosts

Platforms with host_required (a self-hosted server URL, or a Jira site including /rest/api/3) need a host. The host is saved at login and applied automatically to every action — omit --host on action commands; an explicit value overrides the saved host for that single call (exceptional). Omit host entirely for hosted platforms.

The output contract

Success — exit 0, and stdout carries the bare platform payload, projected to the action's output_schema. It is not wrapped in an {ok, data} envelope; if the platform returns an array, stdout is an array.

Failure — non-zero exit, and the error goes to stderr: a structured JSON object when --json is set, human-readable text otherwise.

{
  "ok": false,
  "error": "rate_limited",
  "platform": "github",
  "context": "work",
  "message": "GitHub rejected the request: rate limit exceeded",
  "remedy": "wait and re-issue; the request was rejected before applying",
  "upstream": {
    "http_status": 429,
    "action": "github:issue.list",
    "mutating": false,
    "idempotent": true,
    "retry_after_seconds": 30
  }
}

upstream is present only when a platform request actually failed (never on local, usage, or auth errors): http_status (0 for a transport failure with no response), action (<platform>:<action-id>), mutating, idempotent, and retry_after_seconds (0 unless the server sent a Retry-After). It exists so the caller can decide whether re-issuing is safe.

Control flow

Branch on the process exit code — never parse stdout's shape to detect failure. Exit 0: parse stdout against output_schema. Non-zero: read the error object from stderr; the code-to-meaning table is in Exit Codes (and ships in the CLI as omni-cli errors).

Execute-once: retries are yours

The CLI executes each action exactly once and never retries — not even reads. There is no backoff, no Retry-After sleep, and deliberately no retryable field: retry-safety is a judgment about side effects, and the caller owns it. The rule:

  • A rate_limited (12) or upstream_error (9) on an idempotent action (upstream.idempotent true) is safe to re-issue, optionally after upstream.retry_after_seconds.
  • Never blindly re-issue a non-idempotent action (upstream.idempotent false) after an indeterminate failure — a create or append may already have applied.

Pagination

The CLI returns a single page and never auto-paginates — paging is the caller's job. An action with a pagination block in its discovery entry pages; one without returns a complete set. To advance, re-call the action passing the prior response's next-page value via the param named by pagination.cursor_param; stop when that value is empty or absent.

Where the next-page value lives depends on pagination.next.from:

  • header — the cursor is lifted from a response header into a top-level next_cursor: an array body is wrapped as {items, next_cursor}, an object body gets a next_cursor sibling. An empty next_cursor means last page.
  • body — the cursor is already in the named response field.
  • body_total — page by number until the named total field is reached.
  • body_empty — page by number until the named array field comes back empty.
omni-cli github issue list --repo octo/hello --json
# → { "items": [ ... ], "next_cursor": "2" }
omni-cli github issue list --repo octo/hello --page 2 --json
# → { "items": [ ... ], "next_cursor": "" }   # empty: last page

Pick your platforms, ship your agents

Sign up, choose the actions your workflow needs, and get a copy-paste install one-liner.

Get Started