Post-process any completed markdown report (trend-report, research-report, or generic) into a themed, self-contained HTML file — optionally with interactive Chart.js data visualizations and Excalidraw concept diagrams embedded as inline SVG. Analyzes the report structure, identifies data-rich sections (statistics, tables, distributions, value chains, timelines, cost comparisons), generates themed charts and diagrams, and assembles a polished HTML deliverable with navigation sidebar — without changing the original markdown. Also supports exporting to PDF and DOCX formats via the `formats` parameter. This is the single output skill for all report formats — it replaces the deprecated cogni-research:export-report. Use this skill whenever the user has an existing text-only report and wants to make it visual or export it: "enrich report", "add charts to report", "visualize my report", "make the report visual", "turn this into a visual HTML", "export report", "save as PDF", "export to PDF", "export to Word", "export to DOCX", "download report", "convert report", "make it pretty", "publish report", "report mit Diagrammen anreichern", "Bericht visualisieren", "als PDF exportieren", "add data visualizations", "generate HTML version with charts", "I need charts in my trend report", "make this presentable with visuals". Also trigger when the user mentions tips-trend-report.md, output/report.md, or any markdown report that "needs visuals", "looks boring", "is text-only", or should become a "dashboard-style" or "themed HTML" output. This skill works on EXISTING reports (post-processing) — it does NOT create new reports (use research-report/trend-report), does NOT create slide decks (use story-to-slides), does NOT create posters or journey maps (use story-to-big-picture), does NOT create web landing pages (use story-to-web), does NOT create TIPS dashboards (use trends-dashboard), and does NOT polish prose (use cogni-copywriting).
From cogni-visualnpx claudepluginhub cogni-work/insight-wave --plugin cogni-visualThis skill is limited to using the following tools:
evals/evals.jsonreferences/01-report-detection.mdreferences/02-section-analysis.mdreferences/03-enrichment-catalog.mdreferences/04-chart-patterns.mdreferences/05-excalidraw-patterns.mdreferences/06-html-structure.mdreferences/07-citation-normalization.mdschemas/design-variables.schema.jsonschemas/enrichment-plan.schema.jsonscripts/generate-enriched-report.pyProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Read a completed markdown report and produce a self-contained HTML file that presents the same content with interactive data visualizations and conceptual diagrams injected at semantically appropriate positions. The original markdown stays untouched — you are creating a visual rendition, not editing the source.
A great enriched report does not just decorate prose with random charts. Each visualization earns its place by making a data pattern visible that would otherwise require the reader to mentally parse numbers from text, or by making a conceptual relationship (like a T→I→P→S value chain) spatially comprehensible. If a section has no data worth charting and no concept worth diagramming, leave it as styled prose — over-enrichment is worse than no enrichment.
Two-track visualization pipeline:
The HTML assembly uses a Python generator script (scripts/generate-enriched-report.py) that converts markdown to HTML and mounts visualizations at planned injection points — same architectural pattern as cogni-trends/skills/trends-dashboard and cogni-portfolio/skills/portfolio-dashboard.
Theming follows the 3-stage design-variables pattern from cogni-workspace:
cogni-workspace:pick-themedesign-variables.json from theme.md| Parameter | Default | Description |
|---|---|---|
source_path | auto-discovered | Report markdown file path |
output_path | {dir}/output/{stem}-enriched.html | HTML output path |
report_type | auto | Override detection: trend-report, research-report, generic |
language | from frontmatter | en or de — affects chart labels, axis titles, summary card text |
theme | interactive | Theme path, or omit to trigger cogni-workspace:pick-theme |
design_variables | derived from theme | Pre-computed design-variables.json path (skips derivation) |
density | balanced | Enrichment density: none (0 visuals, themed prose only), minimal (5-8), balanced (10-15), rich (15-22) |
formats | ["html"] | Output formats: html, pdf, docx. HTML is always produced first; PDF/DOCX are derived from it. |
interactive | true | Interactive enrichment review checkpoint at Phase 3 |
enrichment_types | all | Whitelist: e.g., ["kpi-dashboard", "tips-flow", "horizon-chart"] |
skip_types | none | Blacklist: e.g., ["summary-card"] |
The source markdown is never modified. Every word, every citation, every heading from the original appears in the HTML output. Visualizations are ADDED between sections — they supplement, never replace.
All colors in Chart.js configs and Excalidraw elements reference the design-variables palette. No hardcoded hex values in visualization code. This means the same enriched report can be re-themed by changing the design-variables.json.
Every inline citation [text](url) from the source becomes a clickable <a href="url">text</a> in the HTML. The validation gate counts links: HTML count must be >= source count.
Phase 3 presents the enrichment plan for user review before any visualization is generated. This prevents wasted computation on unwanted charts. When interactive=false, auto-approve all planned enrichments.
German reports use real Unicode umlauts (ä ö ü ß), dot-separated thousands (2.661), and German chart axis labels. ASCII umlaut substitutions are a credibility failure.
The density parameter gates how many enrichments pass the scoring threshold:
When density=none: skip Phases 1-4 (structural analysis, enrichment planning, interactive review, visualization generation). Go directly from Phase 0 to Phase 5 with an empty enrichment plan.
Each phase: verify the previous phase's output exists (entry gate), load the reference file for that phase, execute, state output summary. Reference files contain phase-specific rules — read them at the start of each phase, not all at once.
Find the report, detect its type, set up theming.
If source_path provided: use directly, set source_dir to parent.
Otherwise, search without asking:
**/tips-trend-report.md (trend-report candidates)**/output/report.md, **/output/draft-v*.md (research-report candidates)generated_by, title, language, total_themes)Report type detection — read references/01-report-detection.md:
| Signal | Type |
|---|---|
generated_by: trend-report in frontmatter | trend-report |
total_themes: + total_claims: in frontmatter | trend-report |
| H2 "Investment Thesis" + "Value Chains" content | trend-report |
project-config.json with cogni-research fields in parent | research-report |
| H2 "Introduction" + "Conclusion" + "References" | research-report |
| Neither | generic |
Theme setup:
design_variables path provided: load and validate against schema.theme path provided: derive design-variables.json from theme.md (read cogni-workspace/references/design-variables-pattern.md for derivation rules, validate against schemas/design-variables.schema.json).cogni-workspace:pick-theme, then derive.Store: report_type, source_path, source_dir, language, design_variables (the JSON object).
Parse the report into a section tree with metadata for each block.
Read references/02-section-analysis.md for report-type-specific analysis rules.
section_id — slugified heading textheading — original heading textdepth — heading level (1-4)line_start / line_end — line range in sourceword_count — body text words (excluding sub-headings)citation_count — count of [text](url) patternshas_tables — booleannumeric_claims — extracted numbers with context: {value, context, source_url}data_structures — detected tables (as arrays), bullet lists with numbers, T→I→P→S chain blocksSection ID generation (slugify): The Python generator converts heading text to IDs by: lowercasing, stripping all non-alphanumeric/non-space/non-hyphen characters (including ×, €, %, –, —, parentheses, quotes), collapsing whitespace and underscores to single hyphens, trimming leading/trailing hyphens. When building the enrichment plan, use this exact logic for target_section IDs — mismatched IDs cause enrichments to silently drop.
executive-summary, headline-evidence, strategic-themes-table, theme-N, value-chain, solution-templates, strategic-actions, bridge, synthesis, emerging-signals, horizon-distribution, mece-validation, evidence-coverage, claims-registryintroduction, body-section, conclusion, references. Check .metadata/diagram-plan.json for pre-planned positions.section with depth.Output: section map (held in memory — not written to disk).
Decide WHAT visualization goes WHERE. Produce a reviewable plan.
Read references/03-enrichment-catalog.md for enrichment types, triggers, and scoring.
Three-layer decision engine:
Layer 1 — Structural rules (report-type-specific, always apply):
For trend-report:
| Section tag | Enrichment | Track |
|---|---|---|
headline-evidence | kpi-dashboard | data |
value-chain (each) | tips-flow | concept |
horizon-distribution | horizon-chart | data |
mece-validation | distribution-doughnut | data |
evidence-coverage | coverage-heatmap | data |
strategic-themes-table | theme-radar | data |
For research-report (content-pattern-driven — fires based on section content, not heading text):
| Content pattern | Enrichment | Track |
|---|---|---|
executive-summary + 3+ numeric claims | kpi-dashboard | data |
has-data-table (numeric table with 4+ rows) | comparison-bar | data |
has-comparison (table or prose comparing entities) | comparison-bar | data |
has-timeline (3+ chronological dates) | timeline-chart | data |
has-distribution (proportional data ~100%) | distribution-doughnut | data |
stat-dense (5+ numeric claims clustered) | stat-chart | data |
has-process (sequential steps / causal chain) | process-flow | concept |
has-synthesis (cross-section aggregation) | relationship-map | concept |
has-thesis + section >800 words | summary-card | html |
methodology + has-process | process-flow | concept |
| Pre-planned (diagram-plan.json) | per-plan type | concept |
Layer 2 — Content pattern detection (generic, scored):
Scan each section's text for patterns:
stat-chart candidate (data)comparison-bar or distribution-doughnut candidate (data)process-flow candidate (concept)comparison-bar candidate (data)timeline-chart candidate (data)summary-card candidate (html)relationship-map candidate (concept)Layer 3 — Scoring and spacing:
Each candidate gets a score (0-100) based on:
Apply filters:
minimal keeps top 5-8, balanced keeps top 10-15, rich keeps top 15-22Theme consistency check (trend-report):
After scoring, verify that every investment theme H2 section has at least 2 enrichments at balanced density:
summary-card (key takeaway at the theme opening)comparison-bar for cost comparisons, timeline-chart for deadline-heavy themes, stat-chart for evidence clusters)If a theme has fewer than 2, force-add from this baseline. Score force-added items at 50. This prevents the visual rhythm from breaking — all themes share the same internal structure, so they must share a consistent visual baseline. See references/03-enrichment-catalog.md "Theme Consistency Rule" for full details.
Section consistency check (research-report):
After scoring, verify that data-rich H2 sections (600+ words AND at least one content-pattern data tag: has-data-table, stat-dense, has-comparison, has-timeline, has-distribution) have at least 1 enrichment at balanced density:
summary-card (if has-thesis detected — section >800 words with thesis sentence)If a qualifying section has fewer enrichments than baseline, force-add. Score force-added summary-card at 40, data charts at 35. Sections without any content-pattern data tags are pure analytical prose — do NOT force enrichments. See references/03-enrichment-catalog.md "Section Consistency Rule" for full details.
Data extraction completeness: Every enrichment needs a non-empty data field containing the extracted values that Phase 4 will use to generate the visualization. An enrichment with "data": {} forces the Python generator to re-parse the markdown during HTML assembly, which is brittle and error-prone — extract the data during Phase 2 while the section content is being analyzed:
kpi-dashboard: stats[] with value, label, source_urlcomparison-bar: items[] with label and value (from table rows or comparison pairs)stat-chart: claims[] with value, label, unittimeline-chart: events[] with date, label, categorydistribution-doughnut: segments[] with label, value, percentageprocess-flow: steps[] with label, sublabel, and connections[]relationship-map: nodes[] and connections[] with labelssummary-card: summary text and word_countIf you cannot extract meaningful data for an enrichment, demote it (lower its score by 20) rather than leaving data empty.
Injection line precision: Every enrichment needs an injection_after_line value pointing to the specific source line after which it should appear. When multiple enrichments target the same section, spread them across different paragraphs — do NOT give them all the same line number. The Python generator uses these lines to interleave enrichments within the section content. If all enrichments share one line, they stack together at one position instead of being distributed through the prose.
Output: enrichment-plan.json (written to {source_dir}/cogni-visual/enrichment-plan.json):
{
"report_type": "trend-report",
"source_path": "tips-trend-report.md",
"density": "balanced",
"total_enrichments": 12,
"enrichments": [
{
"id": "enr-001",
"type": "kpi-dashboard",
"track": "data",
"target_section": "executive-summary",
"injection_after_line": 42,
"description": "5 KPI cards for headline evidence numbers",
"score": 95,
"data": { "stats": [{"value": "€173B", "label": "Utility CAPEX 2026", "source": "ING Think"}] },
"priority": "structural"
}
]
}
Let the user approve, modify, or skip enrichments before generation.
When interactive=true:
When interactive=false: auto-approve all, log plan.
Generate Chart.js configs and Excalidraw SVGs for each approved enrichment.
Read references/04-chart-patterns.md for Chart.js configuration templates.
Read references/05-excalidraw-patterns.md for Excalidraw element recipes.
Data track (Chart.js):
For each data-track enrichment:
04-chart-patterns.md template.chart_id — unique identifier matching enr-XXXtype — Chart.js type (bar, doughnut, radar, line, pie, polarArea)data — labels and datasets with valuesoptions — themed: colors from var(--accent), var(--primary), etc.; font-family from var(--font-body); grid colors from var(--border)Concept track (Excalidraw):
For each concept-track enrichment:
05-excalidraw-patterns.md:
mcp__excalidraw__export_to_image.Parallelization: If there are 4+ concept enrichments, dispatch Excalidraw diagram generation to subagents (one per diagram) for parallel execution. Data-track enrichments are JSON config generation — fast enough to run sequentially.
Merge report content + visualizations into themed HTML.
Read references/06-html-structure.md for HTML layout and CSS architecture.
Run the Python generator script:
python3 {SKILL_PATH}/scripts/generate-enriched-report.py \
--source "{source_path}" \
--enrichment-plan "{source_dir}/cogni-visual/enrichment-plan.json" \
--chart-configs "{source_dir}/cogni-visual/chart-configs.json" \
--svg-dir "{source_dir}/cogni-visual/svgs/" \
--design-variables "{design_variables_path}" \
--output "{output_path}" \
--language "{language}"
Before calling the script:
chart-configs.json — array of all Chart.js configs from Phase 4.{source_dir}/cogni-visual/svgs/enr-XXX.svg — one per Excalidraw diagram.The script:
<a> links).:root {}.<div class="enrichment chart-container" data-type="..."><canvas id="enr-XXX"></canvas></div><div class="enrichment concept-diagram" data-type="..."> + inline SVG + </div><div class="enrichment summary-card"> + styled content + </div>Verify the enriched HTML is complete and correct.
Five validation gates:
<a href= in HTML >= count of [text](url) in source markdown.<canvas id="enr-XXX"> has a corresponding new Chart('enr-XXX', ...) in the script block.<svg block is well-formed (has closing </svg>).#hex values in chart configs or enrichment containers (scan for hex outside :root).If any gate fails: fix the specific issue and re-validate. Do not regenerate from scratch.
Output: Write the HTML file to output_path. Open it in the browser for the user.
Print summary:
Convert the enriched HTML to PDF or DOCX when requested.
This phase only runs when formats includes pdf or docx. The HTML output from Phase 5/6 is always the starting point.
PDF export:
references/07-citation-normalization.md — not for HTML citations (which are already correct), but for understanding the citation landscape in case pre-processing is needed.<pre class="mermaid"> blocks, these render client-side via JavaScript and will appear blank in static PDF conversion. Pre-render them before PDF generation:
mmdc (mermaid-cli): extract Mermaid source → render to SVG → replace in HTMLmcp__excalidraw__create_from_mermaid → export_to_image)<canvas> elements also require JavaScript. For PDF with charts, document-skills:pdf can execute JS during rendering. If using weasyprint (no JS), charts will be blank — inform the user and suggest density=none for chart-free PDF.Skill(document-skills:pdf) from the enriched HTML. Pass design-variables.json for theme token access.python3 -c "import weasyprint; weasyprint.HTML(filename='{html_path}').write_pdf('{pdf_path}')"{output_dir}/{stem}-enriched.pdfDOCX export:
DOCX cannot represent interactive charts or inline SVG. Convert from the original markdown source (not the HTML) to preserve clean document structure.
references/07-citation-normalization.md for citation normalization patterns.## References section, replace inline citation patterns with numbered superscript markers, strip the original references section.Skill(document-skills:docx) from the normalized markdown. Pass theme tokens: heading_font (fonts.headers), body_font (fonts.body), accent_color (colors.accent).pandoc {md_path} -o {docx_path} --from markdown --to docxbrew install pandoc or pip install pandoc{output_dir}/{stem}.docxReport to user:
After all requested formats are generated:
| Reference | Loaded at | Purpose |
|---|---|---|
references/01-report-detection.md | Phase 0 | Report type detection heuristics and frontmatter patterns |
references/02-section-analysis.md | Phase 1 | Section mapping rules per report type, data extraction patterns |
references/03-enrichment-catalog.md | Phase 2 | Enrichment types, trigger conditions, scoring model, density thresholds |
references/04-chart-patterns.md | Phase 4 | Chart.js config templates per chart type, themed with CSS variables |
references/05-excalidraw-patterns.md | Phase 4 | Excalidraw element recipes for concept diagrams |
references/06-html-structure.md | Phase 5 | HTML layout, CSS architecture, responsive breakpoints, script structure |
references/07-citation-normalization.md | Phase 7 | Citation format detection and normalization for DOCX export |
schemas/design-variables.schema.json | Phase 0 | JSON schema for design-variables validation |
schemas/enrichment-plan.schema.json | Phase 2 | JSON schema for enrichment plan validation |
scripts/generate-enriched-report.py | Phase 5 | Python HTML generator (markdown→HTML, theme injection, chart mounting) |