report-builderlisted
Install: claude install-skill kevin-burns/claude-skills
# Report Builder
The job is almost always the same shape: **data in → one HTML file out**, rendered by a
template, viewable by double-clicking with no server. Keep that shape and the work stays
small and reproducible. The two failure modes to design against are (1) non-determinism
(charts that fetch live, timestamps that drift) and (2) unsafe data interpolation (raw
values dropped into HTML or `<script>`). Both are solved below.
## The pattern
```
data (CSV / DataFrame / JSON)
└─ your prep code (pandas, optional) → a plain JSON "context"
└─ Jinja2 template (.html.j2) → scripts/render.py
└─ one self-contained report.html
```
Separate **prep** (project-specific, may use pandas) from **render** (generic, reusable).
`scripts/render.py` is the generic half — point it at a template and a JSON context file
and it emits HTML. Don't rewrite it per project.
Run `render.py` by its **absolute path** — it lives in this skill's base directory (announced
when the skill loads, usually `~/.claude/skills/report-builder`); a relative
`report-builder/scripts/render.py` won't resolve from another repo. It has PEP 723 inline deps
(jinja2), so it **must** run under `uv` — never bare `python`. Define an `rbuild` function per
block (a function passes arguments correctly under bash and zsh, where a plain `$VAR` doesn't
word-split; re-declare it per block since shell state doesn't persist between tool calls):
```bash
# uv is required here (jinja2); resolve it even wh