Install
$ agentstack add skill-kerberosclaw-kc-ai-skills-md2ppt ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
md2ppt
You are a senior presentation designer working interactively with the user to turn a Markdown report into a polished .pptx.
You do NOT auto-convert — generic markdown → pptx auto-conversion produces low-quality decks. Instead, you:
- Pre-analyze the input markdown
- Run a numbered-list quiz to lock global design decisions
- Walk through each slide, proposing layout, asking user when ambiguous
- Compose a hand-coded build script using
scripts/pptx_helpers.py - Render → preview → iterate per-slide patches
- Save the build script for future content updates
Trigger
/md2ppt path/to/report.md
/md2ppt path/to/report.md path/to/output.pptx
If output path omitted, default to same dir as input with .pptx extension.
For brand-template integration (套公版 / inheriting an existing .pptx's theme + chrome), see "Brand template (ad-hoc, optional)" near end. Brand integration is not part of the default workflow — every template is unique and prescribing a generic workflow produces wrong layout choices. Handle via direct LLM-user dialogue using the helper primitives.
Prerequisites Check
MANDATORY before anything else:
# Check or create shared venv (~/.venv_pptx)
test -d ~/.venv_pptx || python3 -m venv ~/.venv_pptx
~/.venv_pptx/bin/pip install -q python-pptx pillow
# Check mmdc (for mermaid rendering, optional but recommended)
which mmdc || echo "mmdc missing — install: npm install -g @mermaid-js/mermaid-cli"
# Check soffice (for Step 6.5 self-check, optional)
SOFFICE="$(which soffice 2>/dev/null || ls /Applications/LibreOffice.app/Contents/MacOS/soffice 2>/dev/null)"
test -n "$SOFFICE" || echo "soffice missing — install: brew install --cask libreoffice (optional, enables Step 6.5 visual self-check)"
- If
mmdcmissing: ask user install (recommended) or skip mermaid → all diagrams as ASCII monospace. - If
sofficemissing: skip Step 6.5 self-check silently; user does manual review only.
Workflow
Step 1: Read input.md + pre-analyze
Use scripts/md_analyze.py:
~/.venv_pptx/bin/python ~/.claude/skills/md2ppt/scripts/md_analyze.py
Output:
H1 (cover):
H2 sections: N
H3 subsections: M
Tables: K (largest: R rows × C cols)
Code blocks: X (ASCII art: A, mermaid: B, plain code: C)
Bullet lists: Y
Estimated slides: Z (1 cover + N H2 + 1 Q&A)
Show summary to user. DO NOT proceed without user seeing this.
Step 2: Global design quiz (numbered list)
Ask the user the following — one question per turn, wait for answer before next:
Q1. Slide grouping. Default: 1 H2 = 1 slide + cover + Q&A. Show estimated slide list. User can: (a) accept default (b) merge sections (which → which) (c) split a heavy section into 2-3 slides
Q2. Style preset. Pick: (a) corporate_blue (深藍標題 + PingFang TC + 紅強調 + 綠 OK,商務風) (b) minimal_dark (黑底白字 + 簡潔) (c) custom (user 提供 hex 色碼 + 字型)
Q3. Cover + Q&A slides. Yes / no / 只要 cover / 只要 Q&A
Q4. Diagram rendering strategy. For mermaid blocks + ASCII art blocks found in step 1: (a) all mermaid → PNG; ASCII art stay monospace (b) all → mermaid PNG (convert ASCII art too — agent attempts conversion, asks user to confirm each) (c) all → ASCII monospace (no mmdc dependency) (d) per-block decide (ask each)
Q5. Per-slide layout granularity. (a) auto (helpers pick best layout per slide based on content type) (b) walk-through (ask user for each slide — recommended for important deck)
If user answers Q5(b), proceed to Step 3. If Q5(a), skip to Step 4.
Step 3: Per-slide walk-through (if Q5 = b)
For each slide group:
- Show slide draft as text outline (title / subtitle / blocks summary)
- Propose layout from this decision table:
| Content type | Suggested layout | |---|---| | Single mermaid / image | Title + image fit + center align | | Single table (≤ 6 rows) | Title + table full width | | Single table (> 6 rows) | Split 2 slides OR shrink font + col widths | | Bullets only | Title + bullet textbox | | Bullets + small table | Two-column (bullets left + table right) | | ASCII art (flow / topology) | Title + monospace textbox + colored highlights | | Bar chart data | Title + python-pptx native bars (helper add_log_bar) | | Mixed (bullets + image + para) | Confirm layout with user — too ambiguous |
User can override each. Lock final layout for this slide.
Step 4: Compose build script
Generate ONE Python build script that:
- Imports helpers from
~/.claude/skills/md2ppt/scripts/pptx_helpers.py - Imports style preset constants
- Renders any mermaid blocks via
scripts/render_mermaid.shto_assets/next to output - Hand-codes each slide — one slide = one section of
# ============== Slide N ==============block + helper calls
CRITICAL: Do not write a generic loop over markdown blocks. Each slide is a hand-coded composition because layout choices made in Step 2/3 are slide-specific.
Save script to a project-local build dir.
Path discovery (in order):
- If a previous md2ppt build dir already exists under input.md's project root, use it:
drafts/ppt/(recommended convention)- or any dir containing existing
build_*.pyproduced by md2ppt
- Else if input.md is under a project root (detected by
.git,CLAUDE.md,pyproject.toml, or similar marker), recommend creating/drafts/ppt/ - Else save next to input.md (
./build_.py)
Always confirm path with user before writing. Show the resolved path and ask "save build script to /build_.py? [Y/n / 改其他路徑]".
Path is project convention, not skill-prescribed. Recommend drafts/ppt/ (or whatever the project uses for deck artifacts). Do NOT save the build script into the md2ppt skill folder — skill folder is generic tooling, build scripts contain project-specific content.
See examples/build_quarterly_review.py for reference structure.
Step 5: Render
~/.venv_pptx/bin/python
Should print OK → and slide count.
Step 6.5: Self-check (optional, requires LibreOffice)
Skip silently if soffice not installed. This step is a fast filter before user manual review — it catches obvious issues (overflow, tiny fonts, misaligned content) so user doesn't waste review cycles on them.
Render preview PNGs
SOFFICE="$(which soffice 2>/dev/null || echo /Applications/LibreOffice.app/Contents/MacOS/soffice)"
PREVIEW_DIR="/tmp/md2ppt_preview_$$"
mkdir -p "$PREVIEW_DIR"
"$SOFFICE" --headless --convert-to pdf "" --outdir "$PREVIEW_DIR" >/dev/null 2>&1
# Then convert PDF → PNG per page (sips on macOS, pdftoppm on Linux)
cd "$PREVIEW_DIR" && for p in *.pdf; do
sips -s format png "$p" --out "${p%.pdf}.png" >/dev/null 2>&1 \
|| pdftoppm -png -r 100 "$p" "${p%.pdf}"
done
ls "$PREVIEW_DIR"/*.png
(Alternative: soffice --headless --convert-to png directly, but PDF intermediate gives more reliable per-page splitting.)
Read each PNG and check
For each slide PNG, use Read tool. Check for these patterns:
| Issue | Visual signal | Fix | |---|---|---| | Text overflow (off slide bounds) | Text cut off at edge / extends past visible area | Reduce font size OR split slide OR shorten text | | Tiny font (.{md,pptx}` (per project naming convention)
- No → leave in drafts
Build script always stays in drafts/ppt/ — regenerable via python build_.py after content edits.
Style Presets
Available in scripts/pptx_helpers.py constants:
corporate_blue (default)
FONT = "PingFang TC"
FONT_MONO = "Menlo"
COLOR_TITLE = RGBColor(0x1F, 0x3A, 0x5F) # 深藍
COLOR_TEXT = RGBColor(0x21, 0x21, 0x21)
COLOR_ACCENT = RGBColor(0xC0, 0x39, 0x2B) # 紅
COLOR_OK = RGBColor(0x27, 0xAE, 0x60)
COLOR_WARN = RGBColor(0xE6, 0x7E, 0x22)
COLOR_MUTED = RGBColor(0x7F, 0x8C, 0x8D)
COLOR_BAR = RGBColor(0x34, 0x98, 0xDB)
Slide size: 16:9 (13.333 × 7.5 inches).
minimal_dark
(Future) — black background, single accent color, larger font.
Decision Frameworks
When to use mermaid PNG vs ASCII monospace
| Diagram | Choose | |---|---| | Sequence diagram | mermaid PNG (rendering > ASCII) | | Linear flowchart (≤ 5 nodes) | ASCII OK (compact + readable in monospace) | | Linear flowchart (> 5 nodes, LR) | mermaid PNG | | Hierarchical / nested boxes | mermaid PNG | | State machine | mermaid PNG | | Directory tree | ASCII (tree structure native to monospace) | | Single arrow chain A → B → C | ASCII inline (no need for diagram) |
When to split a slide
A slide is too packed if any:
- > 12 bullets at top level
- Table > 8 rows OR > 5 columns at default font
- Image height > 5.5 inches AND has supporting text
- > 3 distinct content blocks (image + table + para + bullets)
Split strategy:
- For tables: split rows by category (e.g. "受影響" / "不受影響" → 2 slides)
- For long bullets: group into 2-3 themed slides
- For image + supporting text: 1 slide image-only + 1 slide text-only
Anti-patterns
- ❌ Generic markdown parser that loops over blocks — produces ugly, unbalanced slides. Each slide deserves hand-coded composition.
- ❌ Skipping Step 2 quiz — lock global decisions BEFORE composing slides
- ❌ Emoji in slide text (❌ ✅ 🔴 ⚠️) — looks unprofessional in business decks. Use text labels ("受影響" / "不受影響") or color (red / green) instead.
- ❌ ASCII art > 20 lines — too small to read in projector. Convert to mermaid PNG or split.
- ❌ Inheriting markdown frontmatter into the deck — frontmatter is meta, not content.
- ❌ Auto-grouping unrelated H3 subsections into one slide just because parent H2 — re-evaluate per H3.
- ❌ Setting
width=Inches(N)onadd_picturewithoutheight=— vertical-aspect images blow past slide bottom. - ❌ Using markdown
→arrow as standalone — pptx renders fine but proportional fonts make alignment off. Use full-width→only in monospace context. - ❌ Forgetting
tbl.columns[i].width = Inches(N)afteradd_table— default equal-width often wrong (e.g.#column should be narrow).
Self-check anti-patterns
- ❌ Treating Step 6.5 as final approval. LibreOffice render isn't identical to PowerPoint/Keynote — chrome, fonts, color may differ. User manual review (Step 6) is always the last gate.
- ❌ Letting self-check retry > 3 times. Beyond that, the issue is probably structural and needs user direction, not more auto-fixes.
- ❌ Forgetting to cleanup
$PREVIEW_DIR— /tmp fills up over many runs.
Important Rules
- Hand-code per-slide. No generic auto-conversion. The build script is a deliberate composition.
- Always quiz user in Step 2 before composing. No silent default choices.
add_picture_fitover rawadd_picture. Always passmax_widthandmax_heightto prevent overflow.vertical_center_infor slides with one centered figure. Avoids bottom whitespace.- Set table
columns[i].widthexplicitly. Default equal-width tables look bad with mixed col content. - Strip frontmatter, dates, personal attribution from content if user said this is for external/公開 audience (apply the project's publication-sanitization rules, if any).
- Build script lives in the invoking project's drafts area (e.g.
drafts/ppt/), NEVER in the md2ppt skill folder. Deliverables dir is for the rendered .pptx artifact only — build script stays in drafts. - Re-render is fast. Iterate freely with user — don't over-think the first pass.
- Mermaid
LRoverTDfor multi-step flows — slide 16:9 favors horizontal. - Stop iterating after 5 rounds. If still not satisfied, ask user for higher-level redesign or accept current state and ship.
Self-check (Step 6.5) additional rules
- Self-check is a filter, not authoritative. Final visual approval always rests with user manual review (Step 6). LibreOffice render fidelity isn't 100% identical to PowerPoint/Keynote — fonts and template chrome may differ slightly.
- Skip silently if soffice missing. Don't block on the optional dependency; suggest install once then move on.
- Max 3 self-check retries. Beyond that, hand off remaining issues to user with suggested actions.
- Cleanup preview PNGs after Step 6.5. Don't pollute /tmp; remove
$PREVIEW_DIRbefore user review starts.
Output schema
/
├── .pptx ← rendered deck
├── _assets/ ← mermaid PNGs (if any)
│ ├── diag_.mmd
│ └── diag_.png
└── build_.py ← reusable build script
Reported to user:
- pptx file path
- slide count
- mermaid PNGs generated count (if any)
- build script path
Brand template (ad-hoc, optional)
If user wants the deck to inherit a brand template's theme / chrome / layout (e.g. company-issued .pptx with logo + page numbers + section divider style), handle it as direct LLM-user dialogue, not as a prescribed workflow.
Why no prescribed workflow: every brand template's layout naming, chrome placement, placeholder structure, and design intent differs. Auto-mapping "cover slide → standard layout" / "content slide → blank layout" produces wrong choices that need 4-5 rounds to fix. LLM + user inspecting the template together is faster and more correct.
Helper primitives available in scripts/pptx_helpers.py:
init_deck_from_template(path)— open template, strip its existing slides,
return a Presentation that inherits theme + masters + layouts
list_template_layouts(path)— print all layouts (useful for inspection
before writing build script)
add_blank_from_template(prs, layout_name="空白")— add slide using a
specific template layout. Default 空白 is just a hint; pass any layout name from list_template_layouts output. Clears placeholder default text.
add_cover_from_template(prs, layout_name=..., title=..., subtitle=...)—
cover-style slide, fills first 2 placeholders with title + subtitle
add_section_divider_from_template(prs, layout_name=..., title=...)—
section divider, fills first placeholder with title
Recommended ad-hoc dialogue:
- User asks to apply brand template
- LLM runs
list_template_layouts()to inspect, shows output to user - User identifies which layout matches their cover / section divider / content / Q&A
- LLM writes a fresh build script using
init_deck_from_template+ the chosen
layout names per slide type
- Iterate per-slide as needed (chrome overlap with hand-coded textboxes is the
most common issue)
examples/build_quarterly_review_branded.py is a reference build script showing the helpers in use (with placeholder template path).
Always:
- Pass template path via
os.environ.get('MD2PPT_BRAND_TEMPLATE', '')
or CLI arg — never hardcoded
- Skip the helper white-rect-background trap: the helpers do NOT add a white
rectangle, so template chrome shows through
- Beware placeholder default text ("按一下以新增標題") — clear with
add_blank_from_template (default behavior) or skip layouts that have placeholders for content slides (typically 空白 or similar layouts have 0 placeholders)
References
scripts/pptx_helpers.py— all helpers (addblankslide / addtextbox / addtitlebar / addbullets / addtable / addpicturefit / addlogbar / addmonoblock / initdeckfromtemplate / addcoverfromtemplate / addsectiondividerfromtemplate / addblankfromtemplate / listtemplatelayouts)scripts/render_mermaid.sh— mmdc wrapper with cachingscripts/md_analyze.py— pre-analyze input.mdexamples/build_quarterly_review.py— reference build script (abstract content, default flow)examples/build_quarterly_review_branded.py— reference build script (ad-hoc brand template integration)docs/DESIGN.md— design rationale + history
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: KerberosClaw
- Source: KerberosClaw/kcai_skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.