ish command prints a human table on a TTY and switches to JSON when stdout
is piped or redirected. This page is the contract for scripting and CI: how the
JSON is shaped, how to extract one value, how to project fields, and how stdout,
stderr, and exit codes split. For the flag table and the token resolution order,
see global flags. For what a run’s payload means, see
reactions and results.
Mode selection
ish picks an output mode from the flags and the terminal:
| Condition | Mode |
|---|---|
| stdout is a TTY, no flag | Human table |
| stdout is piped or redirected | JSON |
--json | JSON (forced) |
--human | Human (forced, even when piped) |
--get <field> | JSON internally, then a bare value (implies --json) |
ish workspace list | jq and ish study results --get sentiment
work without passing --json. Color is always off in JSON mode and off when stdout
is not a TTY.
--get and --human are mutually exclusive: --get captures a JSON-derived value
and --human forces the table. Passing both exits 2.JSON is lean by default
In JSON modeish strips noise so an agent’s context stays small. From every
response it removes:
- UUID-valued fields (a value matching the UUID shape).
nullandundefinedvalues.- The
created_atandupdated_attimestamps. - Empty arrays on read paths.
alias (always), name, label, status, and other
meaningful fields. Each listed entity also carries a stable alias you can pass
back to any command in place of its UUID, so chaining never needs --verbose.
To get the full untouched payload, pass --verbose:
--fields and --get take precedence over the lean strip: a field you name
explicitly always survives, including a UUID-valued one.
Write-path responses (
create, update) keep their canonical id and *_id
fields without --verbose, so the handle you need for the next call is always
present.List envelope
List commands wrap their items in a fixed envelope. The keys are always present, even for an empty list, so a parser never has to branch on whether results exist.The rows. Each row is lean-stripped unless
--verbose, --fields, or --get is set.Total matching rows. Equals
returned when the backend returns a flat array.Rows in this response. CLI-computed.
Page size.
Rows skipped before this page.
true when total exceeds offset + returned. CLI-computed, so you can detect
truncation without counting items yourself.Extract one value with --get
--get <field> prints only the value at a dotted path, as a bare string with no
JSON quotes. It implies --json, suppresses progress on stderr, and is built for
command substitution.
- Numeric segments index into arrays (
items.0.alias). - Bracket indexing also works (
items[0].alias), the form most reach for first. - A non-numeric segment against an array maps over it, one value per line:
--get items.aliasprints every row’s alias. - On a list response, a path that does not start with
itemsauto-descends through the envelope, so--get aliasbehaves like--get items.alias.
null prints an empty
string; an object prints as compact JSON on one line; an array prints one element
per line.
Project fields with --fields
--fields <a,b,c> narrows JSON output to a comma-separated set. On a list response
it projects each item and leaves the envelope keys intact, so pagination metadata
survives.
ish prints a one-line stderr
warning naming the missing fields and a sample of the keys that are available, and
suggests the backend key when the surface name differs (for example
workspace_id maps to product_id). The stdout JSON stays clean, so a downstream
parser is unaffected.
Force a mode with --human
--human forces the table renderer even when stdout is piped, for reading output
through a pager or capturing a table into a file.
--json for the opposite when you want JSON on a TTY (for example to pipe into
jq while watching the terminal).
Streams: stdout, stderr, exit code
ish keeps the three channels separate so a script can trust each one:
| Channel | Carries |
|---|---|
| stdout | Result data only (the table or the JSON / bare value). |
| stderr | Progress lines, the --fields warning, status notes, and the error envelope on failure. |
| Exit code | The outcome class. |
ish ... > out.json captures only
clean data. --quiet (-q) suppresses the progress lines; it defaults on under
--get and when JSON was auto-selected by piping. Errors and the --fields
warning still print to stderr under --quiet.
On failure in JSON mode, the error is a structured envelope on stderr:
error_code and the exit code, not the prose. Some errors also carry
error_kind, an example invocation that fixes the call, valid_options, or
hint. For the code-to-meaning table, see global flags.