# Claude Code skill: turn a session's findings into a self-contained HTML report (with Markdown source). Drop into ~/.claude/skills/html-report/SKILL.md.

> Source: <https://gist.github.com/laurokirsch/26b70c436238e2c4b739edb3ef4ca7e3>
> Published: 2026-05-29 09:20:21+00:00

| name | html-report |
|---|---|
| description | Take all documentation, discoveries, and structured knowledge gathered in the current conversation and produce a self-contained HTML report (plus its Markdown source). Use when the user says "/html-report", asks to "generate a report", "write this up as a report", "save this session as a report", or wants a shareable artifact summarizing what was figured out in the session. |

Produce a report pair — **Markdown first** (canonical source), then **HTML rendered from it** (self-contained, single file, no external assets). Files are written side-by-side into the user's configured output directory.

Check for `~/.claude/html-report-config.json`

.

-
**If it exists**, read`output_dir`

from it and use that for the rest of this run. Do not prompt. -
**If it does NOT exist**, ask the user ONCE, in one short sentence:"First run — where should reports be written? (default:

`./reports/`

in your current directory)"Accept their answer. Acceptable forms:

- Empty / "default" → use literal string
`./reports`

(resolved against cwd at write time, so reports land next to whatever project you're in) - Absolute path (e.g.
`/Users/x/Documents/reports`

) → use as-is `~`

-prefixed (e.g.`~/reports`

) → expand`~`

to`$HOME`

Save the answer:

```
mkdir -p ~/.claude
printf '{"output_dir": "%s"}\n' "$CHOSEN" > ~/.claude/html-report-config.json
```

Confirm in one sentence: "Saved. Reports will go to

`<path>`

. Re-run to change:`rm ~/.claude/html-report-config.json`

." - Empty / "default" → use literal string

Then proceed to Step 1. Create the resolved output dir with `mkdir -p`

if it doesn't exist.

Base: `<YYYY-MM-DD>-<kebab-topic>`

where:

- Date is today (system date).
`<kebab-topic>`

comes from`$ARGUMENTS`

if provided, else inferred from the dominant topic of the session in 1–4 kebab words (e.g.`auth-migration-research`

,`db-migration-tutorial`

,`vendor-eval`

).

If the resulting `<base>.md`

already exists in the output dir, append `-2`

, `-3`

, etc. until unique.

Final paths:

`MD="$OUTPUT_DIR/<base>.md"`

`HTML="$OUTPUT_DIR/<base>.html"`

Write `$MD`

. Required structure:

**Frontmatter:**

```
---
title: <Human-readable title>
date: <YYYY-MM-DD today>
type: report
tags: [report, <other-relevant-tags>]
---
```

**Body:** all session content in rich Markdown. Use:

- Headings (
`#`

,`##`

,`###`

) —`##`

defines top-level sections used by Step 3. - Tables for schemas, configs, comparisons.
- Fenced code blocks with language tags (
```` go`

,```` python`

,```` bash`

). - Callouts using GitHub/Obsidian syntax:
`> [!note]`

,`> [!warning]`

,`> [!tip]`

,`> [!info]`

,`> [!danger]`

,`> [!success]`

. - Task lists:
`- [ ]`

/`- [x]`

for open questions or follow-ups. - Mermaid diagrams in
```` mermaid`

fences. - Inline emphasis (
`**bold**`

,`*italic*`

,``code``

).

No raw HTML inside the Markdown unless absolutely needed.

Why this is the way it is:the most common failure mode is the agent stalling on a single huge`Write`

. Writing the full HTML at once exceeds the output token cap (~20K tokens) and hangs. Fix: write a small skeleton once, then fill sections incrementally with ONE`Edit`

per turn.If you catch yourself "designing" the layout, picking colors from a menu, or batching Edits "for speed" — STOP. That's the stuck-state path.

State your two choices in one plain sentence and move on. Examples:

- "Using teal palette, single-column layout."
- "Using amber palette, sidebar layout (16 sections)."

**Palette** (pick exactly one keyword): `teal`

(research/data) · `amber`

(RFC/decision) · `red-amber`

(audit) · `green`

(tutorial) · `neutral`

(reference).

**Layout** (pick exactly one keyword):

`single-column`

— default. Max-width 900px, no sidebar, no scroll-spy.`sidebar`

— ONLY if section count`N ≥ 12`

. Static sidebar TOC, no scroll-spy JS.

Do not invent additional palettes, layouts, or "decorations" (back-to-top buttons, scroll-spy, SVG charts, etc.). **Content is the product, not styling.**

```
N=$(grep -c '^## ' "$MD")
echo "N=$N"
```

If `N > 25`

, tell the user once: "Report has $N sections — HTML fill will take ~$N turns. Continuing." Then proceed without waiting.

The skeleton must be a complete valid HTML file with these — and ONLY these — pieces:

`<!DOCTYPE html>`

,`<html>`

,`<head>`

with one inline`<style>`

block. CSS budget: ~2 KB. Include only:- Body font, base typography, max-width container (or sidebar grid if
`layout=sidebar`

). `h1`

/`h2`

/`h3`

styling.`table`

(striped + padding),`pre`

/`code`

styling,`pre.mermaid-source`

styling.- 5 callout classes —
`.callout-note`

(blue),`.callout-tip`

(green),`.callout-warning`

(amber),`.callout-danger`

(red),`.callout-info`

(purple). Each: colored left border + tinted background. `.tasks li::before`

for task-list checkboxes.

- Body font, base typography, max-width container (or sidebar grid if
- Hero header: title (from frontmatter) and date as plain text.
- Main container.
**Exactly N placeholder lines**, in order:

``` php
<!-- SECTION-0 --><!-- /SECTION-0 -->
<!-- SECTION-1 --><!-- /SECTION-1 -->
...
```

- Closing tags.

**FORBIDDEN in the skeleton:** `<script>`

tags of any kind, IntersectionObserver / scroll-spy code, back-to-top buttons, sidebar TOC pre-populated with section titles, fancy responsive grids, `@media`

queries beyond a single max-width fallback, any external CSS/JS link.

If your skeleton draft exceeds 8 KB, delete CSS rules until it fits.

**HARD RULES — do not violate:**

**ONE** Sequential is the whole point. If you're tempted to batch 2-3 Edits "to save time," that IS the stuck state — stop and send one.`Edit`

per response. Never two. Never parallel.**Cap per Edit** If a section's rendered HTML would exceed 3 KB, render only the first H3 subsection and append an inner placeholder`new_string`

: 3 KB.`<!-- SECTION-i.1 --><!-- /SECTION-i.1 -->`

to fill next turn.**After every Edit, run the checkpoint**(3.4.1) BEFORE doing anything else. Do not chain into the next section in the same turn.** Keep text output to ≤ 1 short sentence per turn**("Filled section 3."). No narration, no re-explaining the plan, no progress tables.

```
grep -c '<!-- SECTION-' "$HTML"
```

The number must decrement by exactly 1 after each successful Edit. If it does not, stop and inspect — do NOT keep sending Edits.

For each `i`

from `0`

to `N-1`

, in its OWN turn:

- Locate the
`i`

-th`##`

heading in the markdown; capture everything until the next`##`

or EOF. - Render to HTML following 3.5 (compact reference).
- ONE
`Edit`

:`old_string`

:`<!-- SECTION-i --><!-- /SECTION-i -->`

(literal, substitute`i`

)`new_string`

:`<section id="sec-i"><h2>Heading text</h2>…rendered…</section>`

(≤ 3 KB)

- Run 3.4.1 checkpoint.
- End the turn.

**Tables**→`<table>`

(CSS already handles striping + padding).**Code blocks**→`<pre><code class="lang-X">…</code></pre>`

.**MUST** escape`<`

`>`

`&`

to`<`

`>`

`&`

. NO syntax-highlighting library, NO regex tokenizer — CSS-only styling.**Callouts**`> [!type]`

→`<div class="callout callout-type">…</div>`

. Map: note→note, tip→tip, info→info, warning→warning, danger→danger, success→tip.**Mermaid**```` mermaid`

→`<pre class="mermaid-source">📊 Mermaid — paste into mermaid.live to render\n\n<ESCAPED source>\n</pre>`

. Never try to render visually.**Task lists**`- [ ]`

/`- [x]`

→`<ul class="tasks"><li class="task-open">…</li><li class="task-done">…</li></ul>`

.- No raw Markdown (
`**bold**`

,`###`

, etc.) in the HTML output — render everything.

Don't restart. Run:

``` php
grep -nE '<!-- SECTION-[0-9]+ -->' "$HTML"
```

Resume from the lowest remaining `i`

using 3.4.2. The skeleton is already correct.

```
remaining=$(grep -c '<!-- SECTION-' "$HTML")
size=$(wc -c < "$HTML")
echo "remaining=$remaining size=$size"
```

`remaining`

MUST be `0`

. `size`

should land in 20 000–200 000 bytes. If non-zero, return to 3.4.2 — do NOT rewrite the skeleton.

Output to the user (concise):

- Markdown path
- HTML path
- One-line
`open <html-path>`

command they can paste

Do not include any other commentary unless the user asked questions.
