Four-skill presentation system: ingest talks into a rhetoric vault, run interactive clarification, generate a speaker profile, then create new presentations that match your documented patterns. Includes an 88-entry Presentation Patterns taxonomy for scoring, brainstorming, and go-live preparation.
96
93%
Does it follow best practices?
Impact
97%
1.21xAverage score across 30 eval scenarios
Advisory
Suggest reviewing before use
Build the .pptx deck from the finalized outline.
Read the template path from speaker-profile.json → infrastructure.template_pptx_path.
Strip demo slides from template, keep layouts only.
Note: The template stripping, slide deletion, and slide reordering code below uses undocumented python-pptx internals (
slides._sldIdLst,part.drop_rel()). These work reliably as of python-pptx 0.6.x but could break on a major update. If python-pptx adds public APIs for slide deletion/reordering in the future, prefer those instead.
python3 scripts/strip-template.py '{template_pptx_path}' '{output_path}'Then open the clean deck with the MCP: open_presentation(file_path=...).
If the outline includes an Illustration Style Anchor section:
generate-illustrations.py <outline.md> remaining to batch-generate all
missing illustrationsImages are stored in illustrations/ alongside the outline file. See
Image Generation Setup below for prerequisites.
After illustrations are approved, generate progressive-reveal build images for slides
that define a - Builds: section in the outline.
When to use builds:
Backwards-chaining workflow:
illustrations/slide-NN.ext)This backwards approach produces better results than building up from empty, because the model preserves the existing composition and style at each step.
Run:
python3 generate-illustrations.py presentation-outline.md --build 5 # one slide
python3 generate-illustrations.py presentation-outline.md --build all # all buildsOutput: illustrations/builds/slide-NN-build-MM.jpg
Key instructions for build edit prompts:
Review: Check each build step image. For near-perfect results, use --fix for
targeted corrections rather than regenerating the entire chain.
Read the layout map from speaker-profile.json → infrastructure.template_layouts[].
Each layout entry has: index, name, placeholders[], and use_for.
| Outline slide type | Layout to use | Notes |
|---|---|---|
| Title slide | Layout with CENTER_TITLE only | Opening, section dividers |
| Title + subtitle | Layout with TITLE + SUBTITLE | Bio, shownotes, section openers |
| Bullet list | Layout with TITLE + BODY | Most content slides |
| Comparison / two lists | Layout with TITLE + 2 BODY columns | Before/after, pros/cons |
| Image/meme with title | Layout with TITLE only (no body) | Title in placeholder, image via manage_image |
| Full-bleed image/meme | BLANK layout (no placeholders) | Position everything manually |
| Quote / caption | Caption layout if available | Attributed quotes, epigraphs |
Illustration-aware formats (when outline has an Illustration Style Anchor):
| Outline Format | Layout to Use | Image Handling |
|---|---|---|
| FULL | BLANK layout | manage_image full-bleed from illustrations/slide-NN.ext |
| FULL + text overlay | BLANK layout | manage_image full-bleed + manage_text overlay on top |
| IMG+TXT | TITLE only (no body) | manage_image ~60% of slide + manage_text beside/below |
| EXCEPTION | Per content type (see above) | Real asset from [IMAGE NN] placeholder path |
Match these generic types to the speaker's actual layout names/indices from the profile.
Build (progressive reveal) slide insertion:
Build slides are inserted as separate slides in the deck (not PowerPoint animations).
Each build step is a full-bleed image from illustrations/builds/slide-NN-build-MM.jpg.
| Step | Source | Layout | Notes |
|---|---|---|---|
| build-00 | builds/slide-NN-build-00.jpg | BLANK | Empty frame — first slide shown |
| build-01 | builds/slide-NN-build-01.jpg | BLANK | First element revealed |
| ... | ... | BLANK | Progressive reveals |
| build-N | Copy of slide-NN.ext | BLANK | Full image — final reveal |
Insertion rules:
manage_image full-bleed positioning
(left=0, top=0, width=10, height=7.5)# Example: inserting build slides for slide 5 with 3 build steps
builds_dir = "illustrations/builds"
for step in range(0, 4): # build-00 through build-03
img_path = f"{builds_dir}/slide-05-build-{step:02d}.jpg"
# add_slide with BLANK layout
# manage_image full-bleedAlso review the template catalog in slide-design-spec.md Section 7 for rich
patterns (SWOT, timelines, funnels, competitor matrices, etc.) that may be available
in the speaker's template.
For each slide in the outline:
Add slide with the right layout (index from profile template_layouts):
add_slide(layout_index=N, title="Slide Title")Populate placeholders — title first, then body:
populate_placeholder(slide_index=N, placeholder_idx=0, text="Title")
populate_placeholder(slide_index=N, placeholder_idx=1, text="Body text")Or use bullet points for list content:
add_bullet_points(slide_index=N, placeholder_idx=1, bullet_points=[...])For image/meme slides — use title-only or blank layout, add image:
manage_image(slide_index=N, operation="add", image_source="path/to/image.png",
left=1, top=1.5, width=8, height=5)4b. For illustrated slides (when outline has Format: FULL or IMG+TXT):
Resolve the image file: illustrations/slide-{NN}.jpg (or .png), where NN is
the zero-padded slide number from the outline.
FULL format (full-bleed):
# Use BLANK layout
manage_image(slide_index=N, operation="add",
image_source="illustrations/slide-{NN}.jpg",
left=0, top=0, width=10, height=7.5)
# If text overlay specified:
manage_text(slide_index=N, operation="add", text="...",
left=0.5, top=5.5, width=9, height=1.5,
font_size=36, color=[255,255,255])IMG+TXT format (illustration + text):
# Use TITLE-only layout
manage_image(slide_index=N, operation="add",
image_source="illustrations/slide-{NN}.jpg",
left=0.3, top=0.8, width=4, height=6)
# Populate title placeholder with slide title
# manage_text for additional text beside the imageEXCEPTION format:
[IMAGE NN] placeholder, not from illustrations/For non-placeholder text (captions, annotations):
manage_text(slide_index=N, operation="add", text="Caption",
left=1, top=6, width=8, height=0.5,
font_size=14, color=[255,255,255])For shapes (dividers, accent boxes):
add_shape(slide_index=N, shape_type="RECTANGLE",
left=0, top=0, width=10, height=0.3,
fill_color=[0,188,212])Read design_rules.background_color_strategy from the speaker profile. Apply the
strategy when adding slides. Common strategies:
random_non_repeating — pick a random saturated color, never repeat on adjacent slidestheme_sequence — follow the template's built-in color rotationRead design_rules.white_black_reserved_for to know when white/black backgrounds
are appropriate (typically full-bleed image/meme slides only).
Read design_rules.footer from the speaker profile for exact position, font, size,
and color adaptation rules. Add footer to EVERY slide using manage_text. The footer
pattern template is in footer.pattern — substitute conference-specific values.
Template placeholders have fixed sizes. To avoid overflow:
manage_text with auto_fit=True.optimize_slide_text after populating to auto-resize if needed:
optimize_slide_text(slide_index=N, min_font_size=12, max_font_size=28)IMPORTANT: Speaker notes MUST be injected as a separate batch pass AFTER all slides exist — never inline during slide creation. The MCP PPT server does not support notes, so use python-pptx in a dedicated second pass.
Save the notes map as JSON ({"0": "", "1": "Brief intro.", ...}), then run:
python3 scripts/inject-speaker-notes.py path/to/deck.pptx notes.jsonRun this AFTER MCP slide generation is complete, and BEFORE presenting to the author.
Keynote compatibility: The script automatically post-processes the .pptx to add a
<p:notesMasterIdLst>element toppt/presentation.xml. python-pptx writes thenotesMasterrelationship but omits this element, which PowerPoint tolerates but Keynote rejects as "invalid format". The patch is idempotent and only runs when anotesMasterrelationship is present, so speakers who never open Keynote pay zero cost.
Save and present a generation report with slide count, layouts used, and placeholders needing author content.
Free-form conversation. The author gives feedback in whatever format is natural.
Author says: "Slide 12 — make the title shorter"
→ populate_placeholder(slide_index=11, placeholder_idx=0, text="New shorter title")
Author says: "Slide 5 — change to two columns" → Cannot change layout of existing slide via MCP. Instead:
open_presentationRead design_rules.slide_numbers from the speaker profile. If "never", decline
requests to add slide numbers and explain it's a design rule.
Author provides an image for a placeholder slide:
→ manage_image(slide_index=N, operation="add", image_source="/path/to/image.png", ...)
The MCP PPT server cannot delete or reorder slides. Use python-pptx:
Delete slides:
python3 scripts/delete-slides.py path/to/deck.pptx 5 12 15 # 0-based indicesReorder slides:
python3 scripts/reorder-slides.py path/to/deck.pptx --from 5 --to 2After any python-pptx structural operation, re-open the deck with MCP
(open_presentation) to continue editing.
Save the .pptx. Export and publishing happen in Phase 6.
| Operation | Tool | Key params |
|---|---|---|
| Add slide | add_slide | layout_index, title, background_colors=[[r,g,b]] |
| Set title/body | populate_placeholder | slide_index, placeholder_idx, text |
| Add bullets | add_bullet_points | slide_index, placeholder_idx, bullet_points[] |
| Add free text | manage_text | slide_index, operation="add", text, position, font |
| Add image | manage_image | slide_index, operation="add", image_source, position |
| Add shape | add_shape | slide_index, shape_type, position, colors |
| Add table | add_table | slide_index, rows, cols, data, position |
| Add chart | add_chart | slide_index, chart_type, categories, series |
| Fix text overflow | optimize_slide_text | slide_index, min/max_font_size |
| Inspect slide | get_slide_info | slide_index |
| Save | save_presentation | file_path |
After the author declares done, export the .pptx to PDF. The method depends on the
speaker's publishing_process.export_method and platform.
Run the export script — it auto-detects PowerPoint (macOS AppleScript) or LibreOffice:
python3 scripts/export-pdf.py path/to/deck.pptx [path/to/output.pdf]If output.pdf is omitted, uses the same name with .pdf extension.
The script prefers PowerPoint AppleScript on macOS (if installed), falls back to
LibreOffice CLI. Read publishing_process.export_method from the speaker profile
to know which is expected.
Before generating illustrations, ensure:
API Key — add your Gemini key to {vault}/secrets.json (preferred):
{ "gemini": { "api_key": "your-key-here" } }Or set the GEMINI_API_KEY environment variable as a fallback:
export GEMINI_API_KEY="your-key-here"Get a key from https://aistudio.google.com/app/apikey
Model availability — verify the model specified in the outline header
is accessible with your key. The script reads the model name from the
**Model:** \model-name`` line in the Illustration Style Anchor section.
Python 3 — the script uses only stdlib (urllib, json, base64).
No pip install needed.
Run the script:
python3 generate-illustrations.py presentation-outline.md remainingOptions: all, remaining, or specific slide numbers (2 5 9, 2-10)
Model comparison (during Phase 2 model selection):
python3 generate-illustrations.py presentation-outline.md --compare 2Generates the same prompt across multiple Gemini image models for visual
comparison. Results go to illustrations/model-comparison/.
Review & iterate — check generated images in the illustrations/
directory. Delete any that need regeneration and re-run with remaining.
Image editing (for targeted changes to existing images):
python3 generate-illustrations.py presentation-outline.md --edit 5 "Erase the bottom-right label"Sends the existing slide-05 image + edit prompt to the model. Output saves as
a new version (slide-05-v2.jpg). Safety suffixes are auto-appended.
Targeted fix pass (iterate on near-perfect images):
python3 generate-illustrations.py presentation-outline.md --fix 5 "Make the road more prominent. Keep the soldiers."Finds the latest version of the slide image and applies the fix, saving as
the next version number. Use --fix instead of regeneration when 90%+ of the
image is correct.
Build generation (progressive reveals):
python3 generate-illustrations.py presentation-outline.md --build 5
python3 generate-illustrations.py presentation-outline.md --build allGenerates backwards-chained build steps from the full slide image.
Output: illustrations/builds/slide-NN-build-MM.jpg
Versioned generation (generate without overwriting):
python3 generate-illustrations.py presentation-outline.md -v 2 5 9Saves as slide-NN-vM.ext instead of overwriting the base image.
Hard-won lessons from generating large illustration sets (50+ images). These are Gemini-specific behaviors discovered through production use — a general-purpose agent will not know them without this context.
When to regenerate vs. edit — the asymmetry rule:
Never simplify original prompts — the specificity rule: The full style anchor with ALL its specific details ("decorative military document border ornaments, classification stamps, and technical manual header formatting") is what produces the distinctive style. When someone shortens the anchor to save time (e.g., "Military manual style. Pen and ink."), the result looks generic — the model falls back to its default interpretation of "military" rather than the specific aesthetic the anchor describes. Only append to the anchor (e.g., add "large bold font", "WWII uniforms"), never trim or paraphrase it. When auditing prompts, flag any prompt that is significantly shorter than the full style anchor as a simplified-anchor anti-pattern.
Prompt engineering for edits — three mandatory components:
The --edit and --fix commands auto-append suffixes #1 and #2, but you must
always add #3 (preservation list) manually — the script cannot know what to preserve.
Versioning strategy — never overwrite during iteration:
slide-12-v2.jpg, slide-12-v3.jpg, etc.slide-12.jpg during iteration — keep the original as fallback--fix command auto-versions; --edit also auto-versions-v flag with normal generation to version instead of overwriteTargeted fix passes — the 90% rule:
--fix rather than full regeneration--fix automatically finds the latest version and saves the next onePIL/programmatic masking — never use for builds or edits:
Title-overlay compatibility — engineer the safe zone at generation time:
When a slide will have an overlaid title, the illustration must leave a clean negative-space region for it. Relying on a post-hoc placement heuristic to find one fails: the subject occupies the "best" area by default, and a brightness-based picker conflates darkness with cleanness (a bright uniform backdrop is a valid title region).
Instead, assign a SAFE ZONE per slide in the design brief and inject
a directive into every Image prompt:
TITLE SAFE ZONE -- CRITICAL COMPOSITION RULE: Reserve the {zone} of
the 16:9 frame as clean uninterrupted negative space filled only with
{surface}. No subjects, objects, text, props, or focal points may
appear in this region. The scene's subjects must be composed entirely
in the remaining portion of the frame. This negative space will carry
an overlaid title.Five zones are supported:
upper_third — uniform backdrop above the subject (open scenes,
portraits, hero shots)middle_third — reserved center band, subject framing around it
(TV / monitor / window / portrait-frame / vignette compositions)lower_third — uniform region below the subject (full-frame
posters, signs, or top-heavy compositions)left_half — clean left half of the frame, subject composed on the
right (split-panel or "subject pushed to one side" compositions)right_half — mirror of left_half, subject on the leftThe {surface} is chosen from the deck's style anchor — describe
what should fill the reserved region in the style's own vocabulary
(sky, fabric, paper, parchment, gradient, painted backdrop, etc.).
Left/right thirds are intentionally excluded — too narrow for
horizontal title text. left_half / right_half give the title
enough column width to wrap across a few lines.
Apply a zone-sized 45% black scrim between the picture and the title text. Scope matters: a full-slide scrim flattens the whole illustration, while a zone-sized scrim lifts the title locally. For styled decks (warm sepia, cool night, etc.) sample the scrim color from the deck's natural shadow tone instead of pure black.
See rules/title-overlay-rules.md for the full policy.
Read infrastructure.presentation_file_convention from the speaker profile for the
directory structure. Typical convention:
{presentations-dir}/{conference}/{year}/{talk-slug}/
├── {talk-slug}.pptx ← the deck (Phase 5 output)
├── {talk-slug}.pdf ← PDF export (Phase 5 final step)
├── presentation-spec.md ← the spec: slug, mode, duration (Phase 1 output)
├── presentation-outline.md ← the outline (Phase 3/4 output)
├── assets/ ← images, memes, screenshots (author provides)
└── illustrations/ ← generated illustrations (Phase 5 Step 5.1b)
├── slide-01.jpg ← one file per illustrated slide
├── slide-02.png
├── slide-05-v2.jpg ← versioned iterations (--fix / --edit / -v)
├── builds/ ← progressive reveal build steps (Phase 5 Step 5.1c)
│ ├── slide-05-build-00.jpg ← empty frame
│ ├── slide-05-build-01.jpg ← first element revealed
│ └── slide-05-build-02.jpg ← second element (full = copy of slide-05)
└── model-comparison/ ← --compare output (Phase 2 model selection)The speaker's template is read-only — never modify it.
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
rules
skills
presentation-creator
references
patterns
build
deliver
prepare
scripts
vault-clarification
vault-ingress
vault-profile