A Claude Code skill that makes tables in agent replies actually readable.
Ask an agent for a table and you get markdown table syntax. The terminal — not the agent — decides the layout, and it knows nothing about the intended columns. One long cell refolds a row, the alignment collapses, and you get this:
| Library | Auto-fit | Wraps cells | Notes |
|---|---|---|---|
| rich.table | yes | yes | Computes column widths against a Console(width=N) budget and folds long cells itself. |
| tabulate | no | with maxcolwidths | Caller must compute per-column widths; tablefmt picks the box style. |
...which is exactly when a table is least useful, since the whole point was to let your eye run down a column.
scripts/table.py lays the table out itself, against the real width of your
terminal, and emits finished plain text that the agent pastes into a fenced code
block. Code blocks aren't refolded, so what you see is what was computed:
┏━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Library ┃ Auto-fit ┃ Wraps cells ┃ Notes ┃
┡━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ rich.table │ yes │ yes │ Computes column widths against a │
│ │ │ │ Console(width=N) budget and folds long │
│ │ │ │ cells itself. │
├─────────────┼──────────┼───────────────────┼─────────────────────────────────────────────┤
│ tabulate │ no │ with maxcolwidths │ Caller must compute per-column widths. │
└─────────────┴──────────┴───────────────────┴─────────────────────────────────────────────┘
Beyond that:
- Auto-detects input — JSON (object or array of records), an existing markdown table, TSV, or CSV. Piping a broken table back in reflows it.
- Right-aligns numeric columns automatically.
- Degrades honestly. A table only works if every column can hold its longest word unbroken. Past that it transposes (few rows) or falls back to one labelled block per row, rather than shredding columns into three-character stumps.
- Links in cells. Write
[label](url)and the terminal showslabelwhile the HTML page hyperlinks it. Onlyhttp/https/file/mailtobecome link targets; anything else stays literal text. - Escapes to a browser on its own. Past 10 rows or 5 columns — where no terminal does a table justice — it also writes a self-contained sortable page and prints a
file://link on stderr.--htmlforces one for a smaller table,--no-htmlsuppresses it.
git clone git@github.com:dgutson/table-skill.git ~/src/table-skill
ln -s ~/src/table-skill ~/.claude/skills/tableNew Claude Code sessions pick it up automatically; run /reload-plugins in an
existing one. Requires uv — dependencies are
declared inline (PEP 723) and fetched per-script, so nothing is installed into
your system Python.
An agent's shell has no controlling terminal, so tput cols and $COLUMNS
report a fallback (usually 80) rather than your actual window. Trusting it
silently squeezes every table — on the author's 141-column terminal, to 57% of
the screen.
The terminal is still reachable, just not from the shell: an ancestor process
(the CLI itself) holds it open. So the script walks up /proc/<pid>/status,
finds the first pty among an ancestor's stdio descriptors, and asks it directly
with a TIOCGWINSZ ioctl.
A margin is then held back, because the reply area is narrower than the terminal.
That margin is measured, not guessed — --ruler prints test lines to paste into a
reply, and the widest one that doesn't fold gives the true width. Recalibrate it
on your own terminal; the shipped default was measured on the author's.
Rendering is pure script — no model call, so a table costs the conversation only the data itself, and there is no inference to tune.
HTML pages are written to ~/claude-tables/ rather than /tmp on purpose: a
snap-packaged browser gets a private /tmp, so a page written to the host /tmp
exists on disk while the browser insists it doesn't. Set CLAUDE_TABLE_DIR to
move it, but not to /tmp or a dot-directory.
references/options.md documents every flag; dev/ holds the bakeoff scripts
that chose rich over tabulate, prettytable and textual.