iusztinpaul/ai-research-os-workshop/research-render

Claude Code and agent skill by iusztinpaul/ai-research-os-workshop

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Generate a multi-form answer (Marp slide deck, matplotlib chart, Obsidian Canvas, or social content brief) from one or more wiki pages in a research directory and file the output back into wiki/renders/. Outputs compound — they appear in index.yaml/index.md and can be re-rendered idempotently. One run can render several forms at once. Use when the user wants to "make a slide deck on X", "chart the comparison of A vs B", "build a canvas of how these concepts connect", "render this as Marp", or "extract a post idea / content brief from my research". Trigger on phrasings like "render", "slide deck", "chart", "canvas", "marp", "brief", "post idea", "extract ideas for social".

Install

npx vskill@latest install iusztinpaul/ai-research-os-workshop/research-render

SKILL.md

Research Render

Wiki pages are the substrate. This skill turns them into multi-form answers — slide decks, charts, canvases, social briefs — and files those outputs back into wiki/renders/<format>/<slug>.{md,png,canvas} so they compound just like any other wiki artifact.

Four formats are supported:

  • marp — slide deck (Marp markdown)
  • chart — matplotlib chart (PNG + companion .py script for reproducibility)
  • canvas — Obsidian Canvas (.canvas JSON)
  • brief — social content brief (copy-ready Markdown for a LinkedIn post / Substack note / X thread / Reddit), a reusable idea seed you can paste into a draft

A run can produce several formats at once — the user picks one or many up front (Step 1), and each chosen format is rendered independently.

A fifth notional format ("table") was considered but rejected: tables are best embedded inside comparison wiki pages, which /research already produces via its comparison_writer. If you want a table, ask for a comparison.

Step 1 — Gather inputs and pick the format(s)

You need:

  • format(s) — one or more of marp | chart | canvas | brief. Resolve as follows:
  • If the user's verb pins exactly one format unambiguously ("make a slide deck" → marp, "chart this" → chart, "build a canvas" → canvas, "extract a post idea / write a brief" → brief), use it without asking.
  • Otherwise, present a single AskUserQuestion with `multiSelect: true` offering the four formats, so the user can pick one or render several at once. Capture the result as selected_formats (a list).
  • source wiki pages — one or more paths under <research_dir>/wiki/. Most renders draw from a single page; canvases, decks, and briefs often draw from multiple (overview + synthesis + a few entity/concept pages).
  • prompt — the user's framing (e.g., "compare BM25 and hybrid retrieval as a 5-slide deck for an internal tech-talk audience"). Verbatim into each render's frontmatter so future agents can reproduce. For a brief, the prompt also carries the body structure the user wants (the sections to cover) — the brief writer follows it for the body (see Format details → Brief).
  • platform (brief only, optional) — LinkedIn / Substack note / X thread / Reddit / generic. Infer from the prompt; default to a platform-agnostic brief. Do not interrupt with a separate question — keep the brief lightly platform-aware and leave true platform-final copy to the user.

Locate research_dir the same way /research (query mode) and /research-lint do.

Steps 2–4 run once per selected format. When several formats are chosen, run the idempotency checks per format and spawn the format writers in parallel (Step 4).

Step 2 — Compute the slug + output paths

Slug:

  • If the user gave a slug (rare): use it.
  • If sources are a single page: derive from that page's slug.
  • If sources are multiple: derive from the prompt (≤ 6 words, kebab-case).

Output paths by format:

FormatPath
marp<research_dir>/wiki/renders/marp/<slug>.md
chart<research_dir>/wiki/renders/charts/<slug>.png (+ <slug>.py next to it)
canvas<research_dir>/wiki/renders/canvases/<slug>.canvas
brief<research_dir>/wiki/renders/briefs/<slug>.md

Create the format directory if it doesn't exist:

bash
mkdir -p "<research_dir>/wiki/renders/<format>"

Step 3 — Idempotency check

Run this check for each selected format (each has its own output path). If the output already exists:

  • Read the existing file's frontmatter (for marp / brief, and the .canvas.meta.yaml sidecar for canvas) or companion .py (for chart).
  • Compare prompt and sources to the new run.
  • If identical, return noop without writing.
  • If different, ask the user via AskUserQuestion whether to overwrite, suffix-with-timestamp, or skip.

Step 4 — Spawn the format-specific subagent

Each format has a dedicated subagent that knows its output shape. Pass:

  • format
  • source_pages — absolute paths
  • research_topic, input_summary (from index.yaml)
  • prompt
  • output_path
  • research_dir
  • platform — (brief only) the inferred platform, or generic
FormatAgent file
marpagents/marp_writer.md
chartagents/chart_writer.md
canvasagents/canvas_writer.md
briefagents/brief_writer.md

Each subagent reads its source pages (these are short — entity/concept/comparison/source wiki pages, not raw files), produces the render, and returns a JSON summary on stdout. The orchestrator never reads the source pages itself.

When selected_formats has more than one entry, spawn all the chosen format writers in parallel — one Agent call per format in a single message. They're independent and write to different paths.

For chart only: after the writer subagent saves the .py script, the orchestrator runs it via uv run --script to produce the .png (the script carries a PEP 723 header declaring matplotlib, so it self-bootstraps). The script is the source of truth; the PNG is regenerable. If execution fails, the script stays on disk and the failure is surfaced — the user can fix it and re-run.

For brief only — handle the `needs_guidance` return. The brief writer is capped at ~1000 words. If covering the prompt's sections faithfully would exceed that, it writes nothing and returns {"action": "needs_guidance", "estimated_words": N, "reason": "...", "options": [...]}. When you get this, do not force the brief — surface it to the user via AskUserQuestion, using the writer's reason as context and its options as the choices (e.g., drop a section, split into two briefs, headline-level only, or raise the cap). Then re-spawn the brief writer with the tightened prompt (or the agreed higher cap). Don't run Steps 5–6 for a brief that returned needs_guidance and wasn't re-rendered.

Step 5 — Update the index

Renders compound. After all selected formats are written, regenerate index.md once so the new renders appear in the navigation:

bash
uv run --script ${CLAUDE_PLUGIN_ROOT:-.claude}/skills/research/scripts/build_index_md.py --research-dir "<research_dir>"

index.yaml does not need to be rebuilt — renders aren't sources, they don't have entries in the sources: array. The total_wiki_pages count is computed live by build_index_md.py from the wiki tree.

Step 6 — Append log entry

One entry per format rendered (share the date when several ran together):

markdown
## [YYYY-MM-DD] render | <format> | <slug>
-format: <marp|chart|canvas|brief>
-output: wiki/renders/<format>/<slug>.<ext>
-sources: <count> wiki page(s) — <comma-separated relpaths>
-prompt: "<verbatim prompt, truncated to 200 chars>"

Step 7 — Present results

Tell the user:

  • The output path (absolute) and how to view it, for each format rendered
  • marp: open in Obsidian with the Marp plugin, or marp --watch <file> from the CLI
  • chart: open the PNG in any viewer; the companion .py is alongside for editing
  • canvas: open in Obsidian (Canvas is a native plugin)
  • brief: open the .md in Obsidian; copy the body into a post draft, or use it as the idea seed for whatever content workflow you use
  • For multi-page renders, the list of source pages used
  • The prompt (so they can compare against future renders)

Format details

Marp

Marp is a markdown-based slide format. The output file has a YAML frontmatter block configuring the deck, then --- separators between slides:

yaml
---
marp: true
theme: default
paginate: true
backgroundColor: white
sources: [<wiki page paths>]
prompt: "<verbatim>"
created: <ISO-8601>
---
# Title slide
Content
---
## Second slide
- bullet
- bullet
---
...

The Marp Obsidian plugin renders this in-vault. Length: 5–15 slides typical; cap at 25 unless explicitly asked. Each slide ≤ 40 words of body text.

Chart

Charts are matplotlib outputs. The writer subagent produces a .py script that:

  • Imports matplotlib (matplotlib.use("Agg") for headless, then import matplotlib.pyplot as plt)
  • Builds the figure
  • Saves to a path passed in via sys.argv[1] (so the orchestrator can wire output paths cleanly)

Companion files:

wiki/renders/charts/<slug>.py # the script (source of truth, editable)
wiki/renders/charts/<slug>.png # the rendered chart (regenerable)

The script must include a frontmatter-equivalent comment block at the top:

python
# -- render metadata --
# format: chart
# sources: <comma-separated wiki page paths>
# prompt: "<verbatim>"
# created: <ISO-8601>
# --

Run it via:

bash
uv run --script "<research_dir>/wiki/renders/charts/<slug>.py" "<research_dir>/wiki/renders/charts/<slug>.png"
Canvas

Obsidian Canvas files are JSON with a fixed schema (see Obsidian docs). The writer subagent produces a .canvas file with:

  • A frontmatter-style metadata block as a separate sidecar (Canvas files don't carry YAML frontmatter natively) — store at wiki/renders/canvases/<slug>.canvas.meta.yaml for the same sources / prompt / created info
  • Nodes: text nodes (one per source wiki page or one per claim) AND embed nodes (for any image assets the wiki references)
  • Edges: directed connections representing relationships (cites, depends-on, contradicts)

Use Canvas for visual argument maps, entity-relationship views, and "how these concepts connect" overviews — anything where spatial layout adds meaning.

Brief

A brief is a copy-ready social content seed composed from wiki pages — the kind of "executive summary for a post" that a human can paste into a draft. It is prose, never code, and it follows a fixed three-part spine. The body is the only flexible part — the user's prompt dictates its sections.

markdown
---
type: brief
format: brief
platform: <linkedin | substack-note | x-thread | reddit | generic>
sources:
-<source_page_relpath_1>
-<source_page_relpath_2>
prompt: "<verbatim prompt>"
created: <ISO-8601 now>
---
# <Working title / hook line>
## Opening — problem → solution
<Problem told as a short story, then the solution and the transformation it brings.
Weaves in the 6 W's: why, what, how, who, where, when.>
## <Body — sections driven by the prompt>
<Whatever the user asked the body to cover.>
## Open questions
1.<highest-signal open question>
2.<second>
3.<third>
---
> Grounding: [[wiki/...]] citations + a one-line `> Synthesis:` note.

The spine, in order:

  1. Opening (problem → solution). Always start with the problem, told as a story (a relatable scenario or pain), then continue with the solution and the transformation it unlocks (before → after). Across this opening, surface the 6 W's — why (why it matters / why now), what (what the thing is), how (how it works at a high level), who (who it's for / who's involved), where (where it fits), when (when it applies). Weave them into the narrative; do not render them as a labeled checklist.
  2. Body — follow the user's request. The prompt names the sections to cover; the body follows it beat-for-beat (e.g., "the 3 memory types, their dynamics, the pipeline, the triggers"). This is the flexible middle.
  3. Conclusion — 3 open questions. Generate exactly three open questions with the highest signal relative to the brief — the ones a thoughtful reader (or the author) would most want answered next, and that genuinely extend or stress-test the idea. They double as the post's engagement closer and as research seeds. Generate them from the brief's own content; you may align them with the research dir's wiki/open-questions.md if it sharpens them, but never just copy that file wholesale.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
178
Repository forks
34
7-day trend score
26

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/iusztinpaul/ai-research-os-workshop/research-render/badge)](https://verified-skill.com/skills/iusztinpaul/ai-research-os-workshop/research-render)

Something wrong with this skill or its page? Tell us.