Skip to content

wxkingstar/ai-output-runtime

Repository files navigation

AI Output Runtime (AIO)

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.

CI Version License: MIT CDN skills.sh

🇬🇧 English · 🇨🇳 中文 · 🇯🇵 日本語

Live demo →

AIO landing page in light theme


Why this exists

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:

Try it in 30 seconds

Install as an agent skill (Claude Code, Codex, or any agent that loads skills from GitHub):

npx skills add wxkingstar/ai-output-runtime -g -y

Then 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-runtime

The output is a single self-contained .html you can email, archive, or open from file://. No build step, no server.

See it live →

What you get

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

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-scheme or force via attribute
  • i18nen and zh-CN shipped; locale via options.locale or <html lang>
  • Print / PDF friendly@media print strips 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

Two ways to render

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 -A

2. CLI — produce a standalone .html (runtime inlined, no external dependencies):

node scripts/aio.mjs render report.md --inline-runtime --lang en --theme dark

What an AIO report looks like

# 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.

What the AI cannot do (by design)

  • 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.

For developers

  • 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 description field for the policy.
  • CHANGELOG: CHANGELOG.md
  • Promo / launch copy: docs/launch-kit.md
  • Contributing: CONTRIBUTING.md. New stable components are intentionally rare.

License

MIT. Use it, fork it, embed it in commercial products. Attribution appreciated, not required.