Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changesets/1791392322-d5d01b4a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
id: 1791392322-d5d01b4a
features:
- core
targets:
- cli
type: fix
bump: patch
description: render nested table values in columns
author: ThomasRooney
date: "2026-10-07"
1 change: 1 addition & 0 deletions templates/perms.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ var fileMode = map[string]fs.FileMode{
"templates/cli/tests/primary/security_ranking_test.go.stmpl": 0o644,
"templates/cli/tests/primary/servers_test.go.stmpl": 0o644,
"templates/cli/tests/primary/stringconstraints_test.go.stmpl": 0o644,
"templates/cli/tests/primary/table_additional_test.go.stmpl": 0o644,
"templates/cli/tests/primary/telemetry_test.go.stmpl": 0o644,
"templates/cli/tests/primary/toon_test.go.stmpl": 0o644,
"templates/cli/tests/primary/unions_additional_test.go.stmpl": 0o644,
Expand Down
4 changes: 3 additions & 1 deletion templates/templates/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,11 +672,13 @@ Three output paths:
- `pretty` (default): Aligned key-value pairs with color, nested indentation
- `json`: Marshaled from the typed Go struct (note: field order and precision may differ from original API JSON)
- `yaml`: Via `gopkg.in/yaml.v3`
- `table`: Aligned columns via `printTable()` — multi-row for slices, vertical key-value for single structs, two-column for maps. A struct with slice-of-struct fields (a list response such as `{items, nextPageToken}`, or a resource holding a list) renders in field order: non-nil plain-value fields as key-value blocks and each slice-of-struct field as a titled, indented sub-table. `time.Time` fields are cells in RFC 3339, like JSON output
- `table`: Aligned columns via `printTable()` — multi-row for slices, `FIELD`/`VALUE` tables for single structs, two-column for maps. Single-object field labels use their JSON names. Nested structs, maps, and complex arrays render as compact JSON cells using custom JSON marshalers; serialized strings such as dates are displayed without JSON quotes. Root custom marshalers define the displayed fields too, including flattened additional properties and omitted fields. Arrays of structs render as titled sub-tables with one row per non-nil entry; columns come from the serialized object's fields in first-seen order across all rows, including fields introduced by later union variants. Nested values occupy compact JSON cells in those columns. Fields absent from a row have blank cells, explicit JSON nulls remain `null`, and fields omitted by the row's JSON serialization do not introduce columns. Custom row marshalers that produce non-object values, or sections with no object keys, use a single `VALUE` column containing each row's complete JSON value. Section scalar arrays use JSON cells; top-level primitive arrays retain comma-separated cells, including on custom-marshaled objects. List wrappers and pagination without a declared results path use these same tables. Direct array tables and `--all` tables that aggregate a declared results array of structs retain their existing columns, with control characters escaped in row cells, column headers, and map keys/values. Single-object tables exclude `json:"-"` fields, escape control characters, omit nil nested values on the reflected object path and preserve explicit nulls from custom root serialization, retain non-nil empty maps/arrays, and use object, field, or row context for serialization errors. `Result()` buffers a single struct's table output and propagates those errors before writing the object; per-item streaming and per-item pagination retain the existing `outputOneItem()` fallback to pretty output when table rendering fails. Declared-results `--all` struct aggregation bypasses `outputOneItem()` and uses the existing `PaginatedResult()` `flushRows` fallback
- `toon`: [Token-Oriented Object Notation](https://github.com/toon-format/spec) via `github.com/alpkeskin/gotoon` — compact, line-oriented, 30–60% fewer tokens than JSON. gotoon omits the final newline, so `encodeTOON()` terminates non-empty output with one, matching JSON and YAML and giving per-item streaming/pagination output its blank-line separator

`pretty`, `yaml` and `toon` never reflect over the typed Go structs directly. `normalizeForOutput()` (`output.go.stmpl`) marshals the value once through the SDK's `utils.MarshalJSON` and decodes it back with `json.Decoder.UseNumber`, and the renderers work from that wire shape (`marshalYAML()`, `encodeTOON()`, `prettyPrint()`, shared by `Result()` and the per-item streaming/pagination path in `outputitems.go.stmpl`). Reflecting encoders would otherwise leak generated union internals — wrapper keys such as `ErrorEvent:`, `null` sibling variants, `Type`/`UnknownRaw` — plus raw struct-tag options as keys (`"error,omitzero":`) and lowercased Go field names in YAML. Numbers stay exact: `json.Number` is converted per renderer (`convertNumbers()` with `yamlNumber()`/`toonNumber()`, `formatJSONNumber()` for pretty) to a native `int64`/`float64` only when that value re-encodes to the identical JSON lexeme (`nativeNumber()`); otherwise the lexeme is kept verbatim — a tagged `!!int`/`!!float` scalar node for yaml, a string for TOON (which has no wider numeric type), the literal for pretty — so bigint/decimal fields never lose digits. TOON's own encoder goes through `float64`, so native integers above 2^53 round there (a gotoon property, unchanged). `--include-headers` output (`outputWithHeaders()`) already worked from the marshalled JSON. Covered by `TestEventStreamUnionEventsRenderWireShape` and `TestEventStreamNumbersKeepPrecision` in `tests/primary/eventstreaming_test.go.stmpl`.

Streaming `--output-format table` applies the same single-object renderer to each event, with a blank line between events. An event wrapper containing only a nested `data` value renders a `data` compact JSON cell beneath a `FIELD`/`VALUE` header; it previously had no displayable table fields and fell back to pretty output. JSON, YAML, TOON, and pretty streaming formats keep their existing rendering paths. Covered by `TestTableStreamingUnionDataCells` in `tests/primary/table_additional_test.go.stmpl`.

3. **jq filtering** (`--jq`): Applies a [gojq](https://github.com/itchyny/gojq) expression to the JSON output. Overrides `--output-format` since the result is always JSON — except under `--raw-output` (jq `-r` semantics: string results are written as raw text plus a newline, unquoted, unescaped, never colorized; non-string results stay JSON), so `--jq '.data' --raw-output | base64 -d > image.png` works. The flag's default is the `cli.jqRawOutput` gen.yaml key (default `false`); it threads through `outputJqResults` (single results, `--include-headers`, artifacts) and `outputOneItem` (streams, `--all` pagination).

**Color default** (`cli.defaultColor` gen.yaml key: `auto` | `always` | `never`, default `auto`): the default value of `--color`. `never` makes uncolored ("raw") output the default so redirected/piped bytes are always clean; `--color`, `NO_COLOR`/`FORCE_COLOR`, and agent mode (never colorized) still apply. JSON and pretty-output keys use bold sky blue (`#38BDF8`), matching the default interactive accent rather than terminal dark blue.
Expand Down
Loading
Loading