Schema-validated JSON blocks for AI-generated reports. Markdown stays the source. A ~38 KB safe runtime renders the visuals. The AI never writes HTML, CSS, or JavaScript.
In May 2026, Thariq Shihipar from Anthropic's Claude Code team made a sharp argument: HTML beats Markdown for agent output. Richer reports, polished visuals, real dashboards. He's right about the upside.
But the argument forces a question — who writes the HTML?
If the AI writes it, every report is an attack surface: inline handlers, remote loads, opaque markup, diffs no human can review. If a human writes a fixed template, you lose the dynamism that made LLM output worth shipping in the first place.
AIO is the third path. The AI emits data. A small runtime emits the HTML.
The agent writes CommonMark Markdown plus four schema-validated JSON blocks — table, metric-cards, callout, chart. The runtime renders them safely. There is no surface where the AI controls executable code.
The discussion that sparked this:
- Thariq Shihipar — Unreasonable effectiveness of HTML in Claude Code
- Simon Willison's note on the thread
- r/ClaudeCode discussion
Install as an agent skill (Claude Code, Codex, or any agent that loads skills from GitHub):
npx skills add wxkingstar/ai-output-runtime -g -yThen ask in plain language: "monthly status report", "compare these options", "summarize yesterday's data", "do an audit". AIO blocks appear automatically when the content shape benefits from structure.
Render the agent's Markdown to a polished HTML report:
npx aio render report.md --inline-runtimeThe output is a single self-contained .html you can email, archive, or open from file://. No build step, no server.
| Status | Component | Use it for |
|---|---|---|
| stable | aio:table@1 |
rows × columns, comparisons, finding lists |
| stable | aio:metric-cards@1 |
headline KPIs, status snapshots, deltas |
| stable | aio:callout@1 |
conclusions, recommendations, warnings |
| candidate | aio:chart@1 |
line / bar / area / pie / donut |
Plus:
- ~38 KB runtime, zero dependencies — one
<script>tag, no bundler, no React/Vue lock-in - Light / dark theme — auto via
prefers-color-schemeor force via attribute - i18n —
enandzh-CNshipped; locale viaoptions.localeor<html lang> - Print / PDF friendly —
@media printstrips the chrome, forces light, avoids page breaks - CDN with optional SRI — supply-chain hardened
- MIT licensed, agent-agnostic — works with Claude Code, Codex, or any agent
1. CDN script tag — drop one <script> into any page:
<script src="https://cdn.jsdelivr.net/gh/wxkingstar/ai-output-runtime@v0.4.3/assets/ai-output-runtime.js"></script>
<div id="app"></div>
<script>
AIOutputRuntime.render(markdown, { target: "#app", title: "My Report" });
</script>For supply-chain integrity, pin with Subresource Integrity. Generate the hash:
curl -s https://cdn.jsdelivr.net/gh/wxkingstar/ai-output-runtime@v0.4.3/assets/ai-output-runtime.js \
| openssl dgst -sha384 -binary | openssl base64 -A2. CLI — produce a standalone .html (runtime inlined, no external dependencies):
node scripts/aio.mjs render report.md --inline-runtime --lang en --theme dark# Q1 status
```aio:metric-cards@1
{
"items": [
{ "label": "Revenue", "value": "$1.2M", "note": "+18% YoY", "tone": "good" },
{ "label": "Churn", "value": "3.1%", "note": "−0.4pp", "tone": "good" },
{ "label": "Open incidents", "value": "2", "tone": "warn" }
]
}
```
```aio:chart@1
{
"type": "line",
"title": "Monthly active users",
"x": ["Jan", "Feb", "Mar"],
"series": [{ "name": "MAU", "data": [120, 145, 162] }]
}
```
```aio:callout@1
{
"tone": "success",
"title": "Ready to scale to Q2",
"body": "Three of four KPIs trending positive. One incident class to close out before scope expansion."
}
```The runtime renders this into the polished page at the live demo. The Markdown stays diffable, the JSON stays validated, the page stays safe.
- Emit HTML, CSS, JavaScript, iframes, event handlers, template expressions, or custom components
- Override component colours, load remote schemas, register new components
- Include
<or>in any string field - Emit anything outside the four registered components
Invalid blocks degrade safely to a code block with a validation error. The full security boundary is in specs/01-security.md.
- Spec & schemas:
specs/,schemas/,aio-registry.json - CLI:
node scripts/aio.mjs validate report.md·node scripts/aio.mjs render report.md [--out PATH] [--inline-runtime] [--runtime URL] [--lang TAG] [--theme dark|light] - Cross-field invariants are enforced by the CLI validator (single source of truth). JSON schemas describe shape; see each schema's
descriptionfield for the policy. - CHANGELOG:
CHANGELOG.md - Promo / launch copy:
docs/launch-kit.md - Contributing:
CONTRIBUTING.md. New stable components are intentionally rare.
MIT. Use it, fork it, embed it in commercial products. Attribution appreciated, not required.