# benday
Terminal charts from a Vega-Lite-style JSON spec, drawn in braille dots.
Built for AI agents: pipe query results in, get a chart the human can read
in the transcript.

## Install
```sh
cargo install benday
```
Or from source: `git clone https://github.com/fwojciec/benday && cargo install --path benday/crates/benday-cli`.
## Use
Pipe rows in, pass a spec:
```sh
echo '{"columns":[{"name":"day","type":"STRING"},{"name":"n","type":"INT64"}],
"rows":[["mon",32],["tue",78],["wed",51]]}' \
| benday --spec '{"mark":"bar","encoding":{"x":{"field":"day"},"y":{"field":"n"}}}'
```
The spec can instead carry its data inline and arrive on stdin by itself.
`benday --help` documents everything: the spec grammar, both stdin shapes,
the bar rules, and worked examples — an agent needs nothing else.
## The spec
A strict subset of Vega-Lite:
```jsonc
{
"data"?: { // optional — omit to pipe rows in
"values": [ /* one JSON object per row */ ] // tidy rows, OR
// "columns": [ {"name":"day","type":"STRING"} ], "rows": [ ["mon",32] ] // columnar
},
"x": { "field": "...", "type"?: "quantitative" | "nominal" | "ordinal" },
"y": { "field": "...", "aggregate"?: "sum" | "mean" | "median" | "min" | "max" | "count" },
"color"?: { "field": "..." } // series split, or bar grouping
},
"title"?: "...", "width"?: 72, "height"?: 13 // plot area, in cells
}
```
Types are inferred from the data when omitted; a declared column type
(`INT64`, `DATE`, …) beats inference, and an explicit spec `"type"` beats
both. Two bar rules follow from the encoding, with no extra flags:
- **Orientation.** Quantitative `x` + categorical `y` draws *horizontal*
bars — built for rankings: one row per bar, height sized to the category
count, names in a label column. Categorical `x` + quantitative `y` stays
vertical.
- **Grouping.** `color` naming a *third* field splits each category into a
grouped cluster, one bar per value, with a legend. `color` naming the
category field itself just tints the bars.
Rows chart in arrival order, so `ORDER BY` in the producing query is the
sort. Unknown spec fields are errors that name the fix, never silently
ignored — a silently wrong chart is the one failure a caller can't detect.
## Data on stdin
With `--spec`/`--spec-file`, stdin carries the data, auto-detected between
two shapes:
- **Columnar envelope** — `{"columns": [...], "rows": [[...]]}`, the shape
an MCP query tool emits as `structuredContent`. Extra keys are ignored,
so pipe it straight in; `truncated` and `total_rows` flow to `--meta`.
- **Bare array of row objects** — `[{"day":"mon","n":32}, ...]`.
Declared dates chart as ordinal (ISO strings sort chronologically); date
bucketing and label formatting belong to the SQL that produced the rows.
## Flags and output
`--marker braille|octant` · `--bar-style dots|blocks` · `--theme
the display. Errors are JSON on stderr with the fix in the message; exit
`2` = invalid spec, `3` = data doesn't fit the encoding. `--meta` prints
scale domains, resolved series colors, and dropped-row counts to stderr,
so a caller can verify what was drawn without parsing dot art.
## Development
Two-stage pipeline: `compile(spec, data) -> Scene` makes every data- and
layout-dependent decision; `rasterize(scene) -> glyphs` turns the geometry
into cells. A new chart type is compiler work; a new visual style is
rasterizer work. Three test layers: a spec→scene snapshot corpus
(`crates/benday-core/tests/cases/`), rasterizer unit tests, and a rendered
glyph gallery. `make validate` runs exactly what CI runs. Design records
live in `docs/plans/`.
## Status
Works: bars (vertical, horizontal, grouped), line, point, area,
multi-series legends, aggregation, type inference, three themes. Planned:
stacked bars, value labels at bar ends, `benday schema` (JSON Schema
output). Deliberately absent: temporal scales and sort grammar — SQL owns
sorting, bucketing, and date formatting. Negative bars are a hard error,
not a silent miss.
Named for [Ben-Day dots](https://en.wikipedia.org/wiki/Ben_Day_process):
images composed from a raster of small marks, which is what terminal cells
are. MIT. Octant glyph table derived from
[ratatui](https://github.com/ratatui/ratatui) (MIT).