PowerPoint speaker notes editor for AI agents. Use when the user needs to: (1) modify PPT speaker notes, (2) export notes to Markdown, (3) rewrite notes in narrative/concise/verbatim style, (4) unpack/pack PPTX files. Works with Claude Code, OpenClaw, and any agent that supports SKILL.md.
User Request
├── Modify Notes → 1,2,3,4,5,6
├── Export to MD Only → 1,2 → 7
├── Pack Existing Content Only → 5 (optional verify → 6)
└── Unpack and View Only → 1
# Unpack to specified directory
mkdir -p pptx-unpacked && unzip your-presentation.pptx -d pptx-unpacked
File Structure:
pptx-unpacked/
├── ppt/
│ ├── slides/ # Slides
│ │ ├── slide1.xml
│ │ ├── slide2.xml
│ │ └── _rels/ # Slide relationships
│ │ ├── slide1.xml.rels
│ │ └── slide2.xml.rels
│ ├── notesSlides/ # Notes files
│ │ ├── notesSlide1.xml
│ │ └── notesSlide2.xml
│ └── _rels/
│ └── presentation.xml.rels
└── [Content_Types].xml
Important: The numbering of notesSlideN.xml does NOT necessarily correspond to PPT page numbers! You MUST confirm via the relationship files.
# Check which notes file corresponds to slide N
cat pptx-unpacked/ppt/slides/_rels/slideN.xml.rels | grep -i "notesSlide"
# Example output:
# <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide" Target="../notesSlides/notesSlide2.xml"/>
# This means slideN.xml corresponds to notesSlide2.xml
# List all slide-to-notes correspondences
for f in pptx-unpacked/ppt/slides/_rels/slide*.xml.rels; do
slide=$(basename "$f" .xml.rels)
notes=$(grep -o 'notesSlide[0-9]*\.xml' "$f" | head -1)
if [ -n "$notes" ]; then
echo "$slide -> $notes"
fi
done
PPTX XML uses these namespace prefixes:
a: - DrawingML namespace (text, shapes)p: - PresentationML namespace (slide structure)r: - Relationships namespaceNotes text is inside <a:t> tags.
# List all notes files
ls pptx-unpacked/ppt/notesSlides/
# Use file reading tool to read individual notes file
# Notes text is in <a:t> tags
# Extract text from all notes files (macOS compatible)
grep -oh '<a:t>[^<]*</a:t>' pptx-unpacked/ppt/notesSlides/*.xml | sed 's/<a:t>\([^<]*\)<\/a:t>/\1/g' | head -50
# Extract all text from notesSlide2.xml (macOS compatible)
grep -oh '<a:t>[^<]*</a:t>' pptx-unpacked/ppt/notesSlides/notesSlide2.xml | sed 's/<a:t>\([^<]*\)<\/a:t>/\1/g'
Notes text may contain these escape sequences:
< → <> → >& → &" → "Decode with sed:
sed 's/</</g; s/>/>/g; s/&/\&/g; s/"/"/g'
Present the user with the following choices before starting:
Page Range Selection:
Notes Style Selection:
Language Selection (optional):
Before modifying page by page, you MUST read through all slides to build global context:
# Batch extract text summary from all slides (macOS compatible)
for f in pptx-unpacked/ppt/slides/slide*.xml; do
slide=$(basename "$f" .xml)
echo "=== $slide ==="
grep -oh '<a:t>[^<]*</a:t>' "$f" | sed 's/<a:t>\([^<]*\)<\/a:t>/\1/g'
echo ""
done
After reading, present a global overview to the user:
[PPT Global Overview]
Total: X pages, Theme: xxx
Page 1: Opening - xxx
Page 2: Background - xxx
Page 3: Core argument - xxx
...
Page X: Summary - xxx
Narrative arc: xxx → xxx → xxx
Purpose of reading through:
--- Page N ---
[Slide Content]
Title: xxx
Points: aaa, bbb, ccc
Charts: [brief description if any]
[Current Notes]
(show full existing notes, or "None" if empty)
<a:t> text in XML. Do NOT execute any modification without user confirmation\n<!-- Replace entire <a:t> tag content -->
<!-- Wrong: replacing text directly will break XML structure -->
<!-- Correct: replace the entire <a:t> tag content -->
<a:t>Old text</a:t> → <a:t>New text</a:t>
Example:
<!-- Before -->
<a:t>This is old notes content</a:t>
<!-- After -->
<a:t>This is new notes content</a:t>
If notes contain multiple paragraphs, there will be multiple <a:t> tags:
<a:p>
<a:r><a:t>First paragraph</a:t></a:r>
</a:p>
<a:p>
<a:r><a:t>Second paragraph</a:t></a:r>
</a:p>
Modify each <a:t> tag separately.
If you need to uniformly modify similar content in multiple places:
cd pptx-unpacked && zip -r ../pptx-packed.pptx . -x "*.py" -x "*.txt" -x "*.json" -x ".DS_Store"
# Unpack newly packed file and verify XML structure is correct
mkdir -p /tmp/verify && unzip -o pptx-packed.pptx -d /tmp/verify
# Check if modified content exists
grep "new text" /tmp/verify/ppt/notesSlides/*.xml
# Verify XML format is correct (no parse errors)
# If xmllint is installed:
# xmllint --noout /tmp/verify/ppt/notesSlides/*.xml 2>&1 && echo "XML format correct"
# Extract title text from slides (macOS compatible)
# Titles are usually in the first <a:t> tag
grep -oh '<a:t>[^<]*</a:t>' pptx-unpacked/ppt/slides/slideN.xml | sed 's/<a:t>\([^<]*\)<\/a:t>/\1/g' | head -1
Organize all page notes into a single MD file:
# Presentation Name - Speaker Notes
## Page 1: Title
Notes content...
## Page 2: Title
Notes content...
#!/bin/bash
# Export notes in slide order using actual mapping (macOS compatible)
output="notes-export.md"
echo "# PPT Speaker Notes Export" > "$output"
echo "" >> "$output"
slide_num=0
for slide_rels in pptx-unpacked/ppt/slides/_rels/slide*.xml.rels; do
slide_num=$((slide_num + 1))
# Get the notes file for this slide (macOS compatible)
notes_file=$(grep -o 'notesSlide[^"]*\.xml' "$slide_rels" | head -1)
if [ -n "$notes_file" ] && [ -f "pptx-unpacked/ppt/notesSlides/$notes_file" ]; then
# Get slide title (first <a:t>)
slide_file="pptx-unpacked/ppt/slides/slide${slide_num}.xml"
title=""
if [ -f "$slide_file" ]; then
title=$(grep -oh '<a:t>[^<]*</a:t>' "$slide_file" | sed 's/<a:t>\([^<]*\)<\/a:t>/\1/g' | head -1)
fi
echo "## Page ${slide_num}${title:+: $title}" >> "$output"
echo "" >> "$output"
# Extract notes text
grep -oh '<a:t>[^<]*</a:t>' "pptx-unpacked/ppt/notesSlides/$notes_file" | \
sed 's/<a:t>\([^<]*\)<\/a:t>/\1/g' | \
sed 's/</</g; s/>/>/g; s/&/\&/g; s/"/"/g' >> "$output"
echo "" >> "$output"
echo "---" >> "$output"
echo "" >> "$output"
fi
done
echo "Export complete: $output"
<a:t>...</a:t> structure is preserved& < > " need escaping& < > "# 1. Unpack
mkdir -p pptx-unpacked && unzip demo.pptx -d pptx-unpacked
# 2. Confirm mapping (find which notes correspond to page 5)
cat pptx-unpacked/ppt/slides/_rels/slide5.xml.rels | grep -i "notesSlide"
# Output: Target="../notesSlides/notesSlide3.xml"
# 3. Read page 5 notes (corresponds to notesSlide3.xml, macOS compatible)
grep -oh '<a:t>[^<]*</a:t>' pptx-unpacked/ppt/notesSlides/notesSlide3.xml | sed 's/<a:t>\([^<]*\)<\/a:t>/\1/g'
# 4. Modify notes (edit XML file)
# Replace <a:t>Old content</a:t> with <a:t>New content</a:t> in notesSlide3.xml
# 5. Pack
cd pptx-unpacked && zip -r ../demo-new.pptx . -x "*.py" -x ".DS_Store"
cd ..
# 6. Verify
mkdir -p /tmp/verify && unzip -o demo-new.pptx -d /tmp/verify
grep "new text" /tmp/verify/ppt/notesSlides/notesSlide3.xml
# 7. Export MD (using script above)