Render Axiom metrics query results (application/vnd.metrics.v3+json) as line charts. Zero-dependency Unicode/ASCII by default; upgrades to inline PNG/SVG/sixel via gnuplot when present. Use when you have a metrics v3 query response and want to see the series as a chart in the terminal or transcript.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add nuroctane/nur-cli --skill metrics-chart --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Metrics Chart?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/nuroctane-metrics-chart)More formats (shields.io, HTML) on the badges page.
---
name: metrics-chart
description: Render Axiom metrics query results (application/vnd.metrics.v3+json) as line charts. Zero-dependency Unicode/ASCII by default; upgrades to inline PNG/SVG/sixel via gnuplot when present. Use when you have a metrics v3 query response and want to see the series as a chart in the terminal or transcript.
---
# metrics-chart
Turns a metrics query response into a **multi-series line chart**.
Input is exactly the body the Axiom metrics query service returns for
`application/vnd.metrics.v3+json` — defined in the metrics query service
(`service/query`, `JsonV3QueryResponse` in `src/server.rs`). It is a
regularly-sampled, multi-series time series; this skill draws it.
## TL;DR
```bash
# From a file, stdout picks the best renderer for your terminal:
python3 scripts/metrics_chart.py response.json
# From a pipe (e.g. straight off the query API):
curl ... -H 'Accept: application/vnd.metrics.v3+json' | python3 scripts/metrics_chart.py
# Force a zero-dependency ASCII chart (always works, anywhere):
python3 scripts/metrics_chart.py --format ascii response.json
```
> **Aggregate first.** Charts are only readable with a *handful* of series.
> The cleanest result comes from a query that already groups/aggregates to a few
> lines (e.g. `group by status` rather than per-`pod`). This skill's series
> reduction (below) is a safety net for high-cardinality results, **not** a
> substitute for aggregating in the query.
## Input format (vnd.metrics.v3+json)
```json
{
"metadata": {
"group_keys": ["code", "method"],
"warnings": [],
"unit": "ms",
"custom_unit": "millisec"
},
"series": [
{
"metric": "http_requests",
"tags": { "code": 200, "method": "GET", "path": "/" },
"start": 1750753164,
"resolution": 60,
"data": [420.0, 42.0, 30.0, 9.0, 15.0, 18.0]
}
]
}
```
| Field | Meaning |
| --------------------- | ----------------------------------------------------------------------- |
| `metadata.group_keys` | Tags the query grouped on → used to build legend labels. |
| `metadata.warnings` | Shown as `⚠` captions under the chart. |
| `metadata.unit` | Canonical y-axis unit (`ms`, `bytes`, …). |
| `metadata.custom_unit`| Human y-axis label; preferred over `unit` when present. |
| `series[].start` | Unix **seconds** (UTC) of `data[0]`. |
| `series[].resolution` | **Seconds** per point; `x[i] = start + resolution*i` (uniform spacing). |
| `series[].data` | Y values; **`null` = gap** (the line breaks, it is not drawn as 0). |
The parser also accepts v2/v2a (same shape with an extra per-series `summary`,
ignored) and the v0/v1 bare-array form, so you can feed older responses too.
## Behaviour
- **Labels** come from `group_keys` values joined with ` | ` → `200 | GET`
(not `code=200,method=GET`). Without `group_keys`, falls back to `k=v` tags.
- **Local time** axis by default; `--tz UTC` or `--tz Europe/Berlin` to override.
- **Gaps**: `null` points break the line.
- **Series reduction** (unless `--all`), in order:
1. **Collapse overlapping** series whose lines essentially coincide (within
`--eps`, default 2% of the y-range); the most prominent is kept.
2. **Top-N** by peak value (`--top`, default 8).
3. The legend prints **how many were dropped**, e.g.
`showing 8 of 30 series (5 overlapping, 17 low/hidden)`, plus the
aggregate-your-query tip.
## Output formats
`--format auto` (default) picks the best renderer for the context:
- an **inline image** when `gnuplot` is installed and the terminal supports
images (Kitty/Ghostty, iTerm2);
- a **PNG file plus a "display this file" instruction** when output is
piped/captured (e.g. an AI agent harness) and `gnuplot` is installed — the
agent then displays the file;
- **ASCII** in a plain terminal, or whenever `gnuplot` is unavailable.
Force a specific backend with `--format`:
| `--format` | Needs | Output |
| ---------- | --------- | ------------------------------------------------------------- |
| `auto` | — | inline image, else a PNG file + display instruction when piped, else ASCII |
| `ascii` | nothing | Unicode braille line chart; renders in any terminal/transcript/CI log. |
| `png` | gnuplot | PNG; displayed inline (Kitty/iTerm2) or written to `--output`/temp. |
| `svg` | gnuplot | SVG written to `--output` or a temp file (path printed). |
| `sixel` | gnuplot | sixel stream to stdout (terminals with sixel support). |
If an image format is requested but `gnuplot` is missing, it prints a note and
falls back to ASCII — it never hard-fails.
## Using from an AI agent / harness
Terminal harnesses (e.g. pi) collapse tool output and don't repaint inline
ANSI, so ASCII charts come out monochrome and folded. **Prefer images:** render
a file and let the agent display it. With `--format auto` (the default) this is
automatic whenever output is piped and `gnuplot` is installed — the skill
writes a PNG and prints:
```
metrics-chart: wrote image to /tmp/metrics-chart-XXXX.png
→ Display this file to the user with your image tool (e.g. read it); do not paste its contents.
```
The agent should then **display that file** (its image/`read` tool renders it
inline, in colour, un-folded). To control the path, pass `--output chart.png`.
Do not paste the PNG bytes or the ASCII into chat.
## Options
| Flag | Default | Meaning |
| ------------------- | ------- | -------------------------------------------------- |
| `--format` | `auto` | `auto`/`ascii`/`png`/`svg`/`sixel`. |
| `--tz NAME` | local | IANA tz for the x-axis; `UTC` for UTC. |
| `--top N` | `8` | Max series after collapse; must be >= 1. |
| `--eps F` | `0.02` | Overlap threshold as a fraction of the y-range. |
| `--all` | off | Draw every series (disable collapse + top-N). |
| `--width` / `--height` | auto | Chart size (cells for ASCII, pixels for images). |
| `--title TEXT` | — | Chart title. |
| `--output PATH` | — | Write png/svg to PATH instead of a temp file. |
| `--color` / `--no-color` | auto | Force ANSI colour on/off (default: on when a TTY). |
## Dependencies
- **Default path: none.** Pure Python 3.9+ standard library. The ASCII renderer
always works.
- **Optional: `gnuplot`** (one system binary) for PNG/SVG/sixel. Install only if
you want high-fidelity images.
## Why not graphviz?
`graphviz` (`dot`/`neato`) lays out node-edge graphs; it has no numeric/time
axis and cannot plot `data[i]` over time. This payload is a numeric time series
with no graph structure, so graphviz does not apply. `gnuplot` is the correct
image backend. (matplotlib is avoided on purpose: it is a heavy multi-package
pip install.)
## Tests
```bash
cd scripts && python3 -m unittest test_metrics_chart -v
```
The parser tests are pinned to the golden bytes from the metrics query
service's `service/query/src/server/tests.rs`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!