Install
$ agentstack add skill-assaftzurel-assaf-agent-skills-pptx-to-slide-md ✓ 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 No
- ✓ 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.
About
PPTX → slide-Markdown
Convert an existing .pptx back into the plain-text format the user writes slides in, so a finished deck becomes editable source again.
Target format
One block per slide. --- separates slides; === separates slide content from speaker notes. Presenter instructions (images, animations, demos) live in [brackets] inside the content area. If the deck has sections, each section begins with a [Section N: Section Name] label before the first slide in that section.
[Section 1: Introduction]
[Slide 1]
# Slide Title
Slide content in markdown: bullets, numbered lists, tables, **bold**, *italic*, ~~strikethrough~~, [links](url).
[image: assets/slide3_img1.png — "alt text"]
[animation — 2 click steps:
click 1: entrance of "First point"
click 2: entrance of "Second point"]
===
**Speaker Notes:**
Notes in markdown.
---
[Slide 2]
# Next Slide Title
...
===
**Speaker Notes:**
*(none)*
Each slide is preceded by a [Slide N] label (N is the slide's position in the deck, so a subset extraction keeps the original numbers). --- separates slides and === separates content from notes, as before.
Blank lines between elements ensure Markdown viewers render each block as a separate paragraph rather than collapsing consecutive lines into soft-wrapped text.
The user also uses [^] inside speaker notes to mark a click. See the note on animations below for why extraction puts animation detail in the content area rather than fabricating [^] positions in the notes.
How to run it
The work is done by scripts/extract.py (uses python-pptx, already available).
python scripts/extract.py -o
Options:
-o, --output— output path (default:.mdin the cwd).--slides— subset to extract, e.g.--slides 3-20or--slides 1,4,7-9
(1-based; default: all). Numbers in [Slide N] stay absolute.
--images-dir DIR— where to write extracted images (default:
_images/ next to the output file).
--no-images— don't write image files; still emit[image: filename]inline.
After running, read the output file and make it available to the user. Surface the generated .md (and the images folder) using whatever file-sharing mechanism your runtime offers — a file preview or attachment if one exists, otherwise point the user to the output path. Don't paste a huge deck inline if it's long — write the file and show a representative excerpt.
What gets extracted
- Title →
# Heading(from the slide's title placeholder). - Body text → bullets by indent level (
-, nested with two spaces),
numbered lists for auto-numbered paragraphs, plain paragraphs when a paragraph has no bullet. Run formatting becomes **bold**, *italic*, ***both***, ~~strikethrough~~, and [text](url) for hyperlinks. Adjacent runs with the same formatting are merged; soft line breaks within a paragraph become spaces.
- Tables → Markdown tables (pipes inside cells are escaped).
- Images → extracted to the images folder and referenced as
[image: — ""] (alt text included when the shape has it).
- Charts →
[chart: — ""](not the underlying data). - SmartArt / diagrams →
[SmartArt/diagram — text: ...]when text is
reachable, otherwise a note that the text isn't extractable.
- Speaker notes → Markdown under
**Speaker Notes:**(*(none)*if empty). - Animations → best-effort, see below.
- Hidden slides → flagged with
[hidden slide]. - Sections →
[Section N: Name]emitted before the first slide of each
section (only when the deck actually uses sections).
- Grouped shapes are walked recursively; shapes keep document order, with the
title hoisted to the top.
Animations — what is and isn't recoverable
PowerPoint stores animations as a time-node tree (`) that python-pptx` does not expose, so the script parses the raw slide XML. Be honest with the user about the boundaries:
- Reliable: the number of click-advance steps, and which shape/paragraph
each step reveals (the script maps the animated paragraph range back to its text). Build-by-paragraph reveals (one bullet per click) come through.
- Reliable: the effect category — entrance, exit, emphasis, motion path.
- Not reported: the specific effect name (Fade vs. Wipe vs. Fly-In). The
preset-ID → name mapping is version-dependent and easy to get wrong, so the script deliberately reports only the category rather than guessing.
- By design: animation detail goes in a
[animation — …]block in the
content area (matching the user's convention that presenter instructions live in brackets). The script does not insert [^] click markers into the notes, because where each click belongs in the authored notes prose is not stored in the file and can't be reconstructed faithfully. If the user wants [^] markers in the notes, offer to add them using the click count as a guide.
- If timing XML is present but can't be parsed, the slide gets
[animation present — could not parse (…)] instead of failing.
Build sequences across slides (always surface these)
Decks often split one idea across several consecutive slides: a list that grows one bullet per slide, or a fixed background with different things overlaid. The script flags runs of consecutive slides that share a title and prints them to stderr under BUILD SEQUENCES DETECTED, classifying each as either an additive build or a shared base with changing overlays.
Always extract these as separate slides (one block each, as normal) — do not merge them automatically. But after extraction, tell the user which sequences were detected and offer to compact each into a single slide with the build expressed as progressive-disclosure instructions and [^] click markers in the notes. Only compact if the user agrees. This applies to additive lists, overlay-style sequences, and restyled finales (e.g. a slide that repeats the previous one with text struck through).
Optional follow-ups
- Describe images: if your runtime can view images, open the extracted image
files and, if the user wants, replace [image: file] with a short description.
- Verify a tricky deck: if the output looks off (e.g. a text box that should
be a heading came through as a bullet, or vice-versa), the slide XML can be inspected directly; bullet detection relies on explicit buNone/buChar/ buAutoNum formatting and falls back to "body placeholders bullet, everything else is plain", which is a heuristic, not a guarantee.
Edge cases worth knowing
- Embedded media, OLE objects, and equations are not extracted (only noted if
they carry text).
- Merged table cells repeat their content per the underlying model.
- A slide with no title placeholder is emitted as
# (Slide N — untitled).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: AssafTzurEl
- Source: AssafTzurEl/assaf-agent-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.