CtrlK
BlogDocsLog inGet started
Tessl Logo

metis-strategy/metis-pptx

Create or edit PowerPoint presentations. Dual-mode skill: (1) Editing mode preserves existing templates via Open XML unpack/edit/repack when an existing .pptx is provided. (2) Generation mode creates new Metis-branded decks from a design system with 36 composable components and 5 layout grids. Includes brand extraction for client decks and visual QA via PowerPoint COM. Triggers on deck, slides, presentation, PPT, or any .pptx request.

93

Quality

93%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

SKILL.md

name:
metis-pptx
description:
Create or edit PowerPoint presentations. When an existing .pptx file is provided or referenced (by attachment, file path, or description), use the template-preserving editing workflow. When no existing file is involved and a new Metis-branded deck is needed, use the generation workflow. Also trigger on "slides," "deck," "presentation," "PPT," or any .pptx request.

PowerPoint Skill — Metis Strategy

0. Mode Gate

Before doing anything else, determine which workflow to use.

Does an existing .pptx file need to be preserved?

ConditionMode
User provides, attaches, or references an existing .pptx file (by path, filename, or description)Editing → Section 1
No existing file; user wants a new Metis-branded deckGeneration → Section 2

Examples:

  • "Edit this deck" → Editing
  • "Add slides to client_deck.pptx" → Editing
  • "Update the content in proposal.pptx" → Editing
  • "Use the Aflac template" → Editing
  • "Take our proposal from last week and update it" → Editing
  • "Build me a proposal deck" → Generation
  • "Create a Metis pitch for Nationwide" → Generation
  • "Make slides for the offsite" → Generation

The test is simple: does a .pptx file exist that we need to preserve? If yes, use Editing. If no, use Generation.


1. Editing Workflow

Use this workflow when an existing .pptx file is provided or referenced. This workflow preserves the template's layouts, themes, formatting, and brand identity.

1.1 Python Environment

Python is installed but NOT on the git bash PATH. Use the full path:

PYTHON="C:/Users/$USER/AppData/Local/Programs/Python/Python312/python.exe"

Required packages: python-pptx, defusedxml, lxml, Pillow, pywin32, markitdown Do NOT loop trying to install Python or add it to PATH. It is already installed.

All script paths below are relative to this skill's directory. Locate it with:

SKILL_DIRS=(
    "$HOME/.metis/skills/.agents/skills/tessl__metis-pptx"
    "$HOME/.metis/skills/.tessl/tiles/metis-strategy/metis-pptx"
    ".tessl/tiles/metis-strategy/metis-pptx"
)
for d in "${SKILL_DIRS[@]}"; do
    if [ -d "$d/scripts" ]; then SKILL_DIR="$d"; break; fi
done

1.2 Analyze the Existing Deck

Before making any changes, understand the deck:

# Visual overview — exports each slide as a PNG image
"$PYTHON" "$SKILL_DIR/scripts/visual_qa.py" input.pptx slide_images/

# Text content extraction
"$PYTHON" -m markitdown input.pptx

Review the exported slide images and extracted text to understand:

  • How many slides exist and their order
  • What layouts are used
  • Where content lives on each slide
  • The deck's visual style and brand

1.2.1 Slide-by-Slide Triage

After analyzing the deck, classify every slide into one of three categories before making any changes:

CategoryWhen to useAction
Keep & EditThe slide's visual layout matches the new content structure (same number of items, same hierarchy, similar text lengths)Edit text in place via XML
Duplicate & RebuildThe slide's layout/grid is right but the content structure differs significantly (different number of items, different hierarchy, much longer/shorter text)Duplicate the slide, strip content, rebuild using the layout's placeholders and shapes as a skeleton
New SlideNo existing slide has a layout that fits the new content, OR the source slide's visual structure is fundamentally incompatible (e.g., a manufacturing Gantt chart being reused for a strategic capability overview)Create a new slide from a layout using add_slide.py with a slideLayout, then build content using patterns from references/patterns.md with extracted brand tokens

A slide is NOT reusable when:

  • It has a specialized visual structure (timeline, Gantt, matrix, process flow) that doesn't map to the new content's logical structure
  • The source has N items but the new content has a significantly different count (e.g., 6 manufacturing stages vs. 3 strategic themes)
  • It contains positioned shapes (arrows, connectors, icons) whose placement depends on the original content's meaning
  • Replacing the text would require most items to be significantly longer or shorter than the originals, causing overflow or empty space

A slide IS reusable when:

  • It's a standard layout (title + bullets, title + 3-column, section divider)
  • The new content has the same number of items in the same visual hierarchy
  • Text lengths are roughly comparable (within ~30% character count)
  • The visual metaphor still applies (e.g., Start/Win/Stay chevrons work for both divisions)

Write out the triage as a table in your plan before starting any edits:

| Slide # | Current Content | New Content | Category | Rationale |
|---------|----------------|-------------|----------|-----------|
| 1 | PSG Title | CTD Title | Keep & Edit | Same layout, just text swap |
| 9 | Data & AI Timeline | Labeling Transformation | New Slide | Timeline axis doesn't apply; empty boxes |
| 15 | PSG Gantt Roadmap | CTD Roadmap | New Slide | Granular Gantt doesn't fit higher-level view |

1.3 Extract Brand Tokens (Non-Metis Decks)

If the deck is NOT Metis-branded, extract the client's brand tokens:

"$PYTHON" "$SKILL_DIR/scripts/extract_brand.py" input.pptx

This outputs a JSON dict with:

  • colors: primary fills, text colors, accents
  • fonts: title font, body font, caption font (with sizes)
  • content_area: left, top, right, bottom boundaries
  • layouts: all available slide layouts and which are used

Use these tokens when creating new slides so they match the client's style.

1.4 Unpack

Extract the .pptx for editing:

"$PYTHON" "$SKILL_DIR/scripts/unpack.py" input.pptx unpacked/

This extracts all XML files, pretty-prints them, and escapes smart quotes.

1.5 Structural Changes

Complete ALL structural changes before editing content.

Duplicate a slide:

"$PYTHON" "$SKILL_DIR/scripts/add_slide.py" unpacked/ slide2.xml
# Prints: Created slide5.xml from slide2.xml
# Prints: Add to presentation.xml <p:sldIdLst>: <p:sldId id="259" r:id="rId8"/>

Create a slide from a layout:

# See available layouts:
ls unpacked/ppt/slideLayouts/
"$PYTHON" "$SKILL_DIR/scripts/add_slide.py" unpacked/ slideLayout2.xml

Delete a slide: Remove its <p:sldId> from <p:sldIdLst> in unpacked/ppt/presentation.xml.

Reorder slides: Rearrange <p:sldId> elements in <p:sldIdLst>.

After adding slides, update presentation.xml by inserting the printed <p:sldId> element at the desired position in <p:sldIdLst>.

1.5.1 Safe Slide Deletion

Deleting slides requires cleanup beyond just removing the <p:sldId> entry. Follow this sequence:

  1. Remove the <p:sldId> element from <p:sldIdLst> in presentation.xml
  2. Delete the slide XML file (ppt/slides/slideN.xml)
  3. Delete the slide's .rels file (ppt/slides/_rels/slideN.xml.rels)
  4. Remove the relationship entry from ppt/_rels/presentation.xml.rels (the <Relationship> with the matching rId)
  5. Remove the Content-Type override from [Content_Types].xml (the <Override> for /ppt/slides/slideN.xml)
  6. Run clean.py to catch any remaining orphaned media, notes, or references

Never delete slides using python-pptx's internal API (drop_rel(), sldIdLst.remove(), etc.) — this leaves orphaned references that corrupt the file and make it unopenable in PowerPoint. Always delete via the unpacked XML approach above, then run clean.py before pack.py.

If you need to remove many slides and the XML approach is impractical, use PowerPoint COM to delete properly:

ppt = win32com.client.Dispatch('PowerPoint.Application')
pres = ppt.Presentations.Open(path, WithWindow=False)
for idx in sorted(indices_to_delete, reverse=True):
    pres.Slides(idx).Delete()
pres.Save()
pres.Close()
ppt.Quit()

COM handles all internal cleanup (relationships, content types, notes) automatically.

1.6 Edit Content

For each slide classified as "Keep & Edit" in triage (Section 1.2.1):

  1. Read the slide XML: unpacked/ppt/slides/slide{N}.xml
  2. Identify ALL content elements — text runs, images, charts
  3. Replace each element with final content

Use the Edit tool, not sed or Python scripts. The Edit tool forces specificity.

1.6.1 Content Authoring vs. Text Replacement

This is NOT a find-and-replace exercise. You are authoring new content that happens to live in an existing visual container. The distinction matters:

  • Text replacement (wrong): "Find 'PSG' and replace with 'CTD' everywhere" — this misses context, leaves orphaned metrics, and produces hybrid content where some values are from the source deck and others are new
  • Content authoring (right): Read each shape, understand what it communicates in the source deck, then write the equivalent new content from scratch using the target source materials

For every text shape you edit:

  1. Read the source text and understand its purpose (is it a metric? a capability description? a label?)
  2. Write the new content from the target source materials, not by modifying the source deck's text
  3. Verify the new text fits the container (similar character count, similar line count)
  4. Verify all quantitative values come from the target data, not leftover source numbers

1.6.2 Formatting Preservation

When replacing text in XML, preserve the original formatting by keeping the <a:rPr> (run properties) element intact and only changing the <a:t> (text) element. If a shape has multiple runs with different formatting (e.g., a bold red label followed by regular black description), maintain that run structure.

Common formatting failures to avoid:

  • Replacing a multi-run paragraph with a single run (loses inline formatting like bold labels)
  • Changing text in a bulleted list without preserving <a:buChar> or <a:buAutoNum> on each <a:pPr>
  • Writing longer text into a fixed-size shape without checking for overflow
  • Clearing a text frame and rebuilding it (loses paragraph-level formatting like spacing, alignment, indentation, bullet style)

When the new content has different structure than the old (e.g., 3 bullets to 5 bullets), you must add or remove <a:p> elements. Copy the <a:pPr> (paragraph properties) from an existing paragraph to maintain consistent bullet style, indentation, and spacing.

1.7 Adding New Slides to Client Decks

When you need to create new slides that match the client's style:

  1. Read references/patterns.md to pick the right grid pattern and component patterns
  2. Use the extracted brand tokens (from step 1.3) for colors, fonts, and spacing
  3. Build the slide using the client's coordinates and palette — NOT the Metis hardcoded values

For Metis-branded decks being edited, you may reference references/metis-brand.md for the code recipes.

1.7.1 Hybrid Workflow: Editing + Generation

Most real-world editing tasks are hybrid: some slides are edited in place, others need to be built from scratch. This is expected and correct — do not force every slide through the same workflow.

After completing triage (Section 1.2.1), you will typically have:

  • Keep & Edit slides — process via the XML editing workflow (Sections 1.4-1.6)
  • New Slides — process via a generation-like workflow but using the client's brand tokens instead of Metis brand constants

For New Slides in a client deck:

  1. Create the slide from a layout: "$PYTHON" "$SKILL_DIR/scripts/add_slide.py" unpacked/ slideLayoutN.xml
  2. Choose the right component pattern from references/patterns.md
  3. Build the slide content using extracted brand tokens (colors, fonts, spacing from Section 1.3)
  4. Write the content shapes into the new slide's XML using the Edit tool
  5. Insert the <p:sldId> at the correct position in presentation.xml

Key principle: A deck that mixes edited originals with well-crafted new slides (using the same brand tokens) will look more cohesive than a deck where every slide is force-fit via text replacement into layouts that don't match the content.

When building new slides for a client deck, adapt the python-pptx component recipes from references/metis-brand.md by substituting the client's brand tokens for the Metis constants:

  • Replace DARK_NAVY with the client's primary_fill color
  • Replace Calibri with the client's primary font
  • Replace Metis margins with the client's content_area boundaries

1.8 Formatting Rules for XML Editing

  • Bold all headers, subheadings, and inline labels: Use b="1" on <a:rPr>
  • Never use unicode bullets (•): Use proper list formatting with <a:buChar> or <a:buAutoNum>
  • Multi-item content: Create separate <a:p> elements for each item — never concatenate into one string
  • Smart quotes: When adding new text with quotes, use XML entities:
    • Left double quote: &#x201C;
    • Right double quote: &#x201D;
    • Left single quote: &#x2018;
    • Right single quote: &#x2019;
  • Whitespace: Use xml:space="preserve" on <a:t> with leading/trailing spaces

1.9 Template Adaptation Rules

  • When source content has fewer items than the template: remove excess elements entirely, don't just clear text
  • When replacing text with longer content: may overflow — test with visual QA
  • When replacing text with shorter content: usually safe
  • Template slots ≠ source items: If template has 4 items but you only need 3, delete the 4th group entirely

Overflow Decision Tree

Before committing a text replacement, compare the character count of old vs. new content:

New vs. Old LengthRiskAction
Within ~30%LowReplace and verify in QA
30-60% longerMediumReplace, but check the shape has wordWrap and adequate height. Consider abbreviating.
>60% longerHighDo NOT force-fit. Either abbreviate the content, or classify this slide as "New Slide" in triage and rebuild with a layout that fits.
Significantly shorterLowReplace, but remove any excess visual elements (extra bullet markers, connector lines) that now point at empty space

Never rely on PowerPoint's auto-shrink (<a:bodyPr fontScale="...">) to handle overflow. If the text doesn't fit at the original font size, the slide needs redesign, not font shrinking.

1.10 Clean + Pack

"$PYTHON" "$SKILL_DIR/scripts/clean.py" unpacked/
"$PYTHON" "$SKILL_DIR/scripts/pack.py" unpacked/ output.pptx --original input.pptx

clean.py removes orphaned slides, media, and relationships. pack.py validates, condenses XML, and repacks with auto-repair.

1.11 Visual QA

"$PYTHON" "$SKILL_DIR/scripts/visual_qa.py" output.pptx qa_images/

See Section 3 for the full QA process.


2. Generation Workflow

Use this workflow when building a new Metis-branded deck from scratch.

2.1 Asset Paths

AssetPath
Master template (blank deck with slide masters)G:\Shared drives\Knowledge Management\New Brand Assets\PowerPoint Templates\master.pptx
Firm overview slides (8 slides)G:\Shared drives\Knowledge Management\New Brand Assets\PowerPoint Templates\intro-slides.pptx
Icon LibraryG:\Shared drives\Knowledge Management\New Brand Assets\PowerPoint Templates\Icon_Lib.pptx
Logo — WhiteG:\Shared drives\Knowledge Management\New Brand Assets\Metis Strategy Logo\Metis Strategy White RGB logo.png
Logo — Black-MintG:\Shared drives\Knowledge Management\New Brand Assets\Metis Strategy Logo\Metis Strategy Black-Mint RGB Logo.png

2.2 Python Environment

Python is installed but NOT on the git bash PATH. Use the full path:

PYTHON="C:/Users/$USER/AppData/Local/Programs/Python/Python312/python.exe"
"$PYTHON" build_deck.py

Required package: python-pptx — if missing: "$PYTHON" -m pip install python-pptx Do NOT loop trying to install Python or add it to PATH. It is already installed.

2.3 Design System

Read references/patterns.md for which component patterns to use (brand-agnostic guidance). Read references/metis-brand.md for the exact Metis python-pptx code recipes.

Key brand constants:

  • Colors: Dark Navy #20206E, Metis Blue #256BA2, Mint #3BDAC0, Dark Gray #4A4A4A, Light Gray #F2F2F2
  • Font: Calibri only. Title 28pt bold navy, body 14pt dark gray, caption 11pt blue.
  • Margins: Content area starts at (0.50", 1.10") and ends at (12.83", 6.80"). Slide is 13.33" x 7.50".

2.4 Layout Grids

Read references/patterns.md for the grid selection guide — which grid suits your content. Read references/metis-brand.md for exact Metis coordinates and code for each grid.

GridBest for
1. Full WidthExecutive summaries, narratives, key findings
2. Two ColumnComparisons, before/after, pros/cons
3. Sidebar + MainKPI callout + explanation
4. Three ColumnPhases, pillars, options
5. Title + VisualProcess flows, org charts, tables

2.5 Phased Build Workflow

Phase 1: Plan the Deck

Before writing any code, create a slide-by-slide outline:

Slide 1: Cover (fixed) — "Client Name — Project Title"
Slide 2: Agenda (content, Grid 1) — 4 agenda items
Slide 3-12: Firm Overview (fixed, optional) — ask user if needed
Slide 13: Section Divider (fixed) — "Our Approach"
Slide 14: Three-Phase Process (content, Grid 4) — Discovery, Design, Deliver
...

For each slide, decide:

  • Fixed or content? Fixed slides come from templates. Content slides are generated.
  • Which layout grid? Pick from the 5 grids based on content type.
  • Which components? Pick from references/patterns.md component selection guide.
  • What text goes on it? Write the actual content now, not placeholders.
  • Which component pattern(s)? Assign specific component numbers from references/patterns.md. Use the Component Rotation Guide (in patterns.md) to ensure variety — never assign the same primary component to consecutive slides.

Visual Variety Rule — apply during planning:

  1. No-repeat rule: The primary component on slide N must differ from slide N-1 AND slide N-2. If three consecutive slides have the same content type (e.g., "key points"), use three different components from the rotation alternatives for that type (see Component Rotation Guide in patterns.md).
  2. Grid diversity: Across any 5 consecutive content slides, use at least 2 different grid layouts. A deck where every content slide uses the same grid is monotonous even if the components vary.
  3. Rhythm breakers: Every 3-4 content slides, insert one slide that uses a fundamentally different visual form — a pull quote (#36), a before/after (#20), a framework diagram (#31 or #32), or a process visual (#22 or #24). These prevent the "wall of cards" effect.
  4. Write it in the outline: The slide plan MUST include a Component column showing the assigned component number(s) for each content slide:
| # | Title                        | Type    | Grid | Component(s)     |
|---|------------------------------|---------|------|------------------|
| 1 | Cover                        | fixed   | —    | —                |
| 2 | Agenda                       | content | 1    | #7 Callout       |
| 3 | The Opportunity              | content | 1    | #36 Pull Quote   |
| 4 | Market Landscape             | content | 4    | #9 Cards+Header  |
| 5 | Our Point of View            | content | 2    | #10 Benefits     |
| 6 | Transformation Roadmap       | content | 5    | #22 Process      |
| 7 | Key Capabilities             | content | 4    | #11 Category Grid|
| 8 | Closing / Call to Action     | content | 1    | #36 Pull Quote   |

Firm overview is optional. Ask the user: "Should I include the Metis firm overview slides?"

Phase 2: Write the Build Script

Create a single Python script (build_deck.py). Critical rules:

  • Always start from master.pptx — never open intro-slides.pptx directly as the base.
  • Use '1_Title Only' layout for content slides — gives green arrow, logo, footer, page number.
  • Never use 'Blank' for content slides — it has none of the brand elements.
  • Use 'Section Divider' layout for dividers — navy background is built in.
  • Set titles via placeholder (idx=0), not add_textbox().
  • Copy firm overview slides from intro-slides.pptx using the copy_slide_from() helper.
  • Deduplicate the zip before saving.

See references/metis-brand.md for the full boilerplate code including:

  • get_layout() helper
  • add_content_slide() helper
  • add_section_divider() helper
  • copy_slide_from() helper
  • save_clean() helper

Phase 3: Build Content Slides

For each content slide:

  1. Add a content slide: slide = add_content_slide("Title", "Subtitle")
  2. Choose components from references/patterns.md — every content slide MUST use at least one component. Plain text on white is never acceptable.
  3. Get the code recipe from references/metis-brand.md and call the component functions.
  4. Add content shapes below the title area (starting at y=1.10").
  5. Verify visual variety: Before moving to the next slide, check:
    • Is the primary component different from the previous slide's primary component?
    • Has this exact grid+component combination appeared on any of the last 3 slides?
    • If either check fails, swap in an alternative component from the Component Rotation Guide in patterns.md.

Every text element must have real content. No placeholder text, no "Lorem ipsum."

Phase 4: Polish and Save

Before saving, verify:

  • Every slide has a title
  • No shapes contain placeholder text
  • All fonts are Calibri
  • All colors match the brand palette
  • Content is within margin bounds (0.50" to 12.83" horizontal, 1.10" to 6.80" vertical)

2.6 Deck Structure Guidelines

Proposals / BD Decks:

  1. Cover
  2. Firm Overview (optional — ask user)
  3. Our Understanding of Your Challenge (Grid 1)
  4. Our Approach (Grid 4 or Grid 5)
  5. Our Team (Grid 4)
  6. Investment (Grid 5 with table)
  7. Next Steps / Closing

In-Project Deliverables:

  1. Cover
  2. Agenda (Grid 1)
  3. Section Divider + Content slides (repeat per section)
  4. Key Findings / Recommendations (Grid 1 or Grid 2)
  5. Next Steps
  6. Appendix (Firm Overview here if included)

2.7 Common Patterns

Bullet list with accent markers:

bullets = ["Finding one", "Finding two", "Finding three"]
y = 1.10
for b in bullets:
    marker = slide.shapes.add_shape(MSO_SHAPE.OVAL, Inches(0.50), Inches(y + 0.05), Inches(0.12), Inches(0.12))
    marker.fill.solid()
    marker.fill.fore_color.rgb = MINT
    marker.line.fill.background()
    add_body(slide, b, 0.75, y, 12.08, 0.35)
    y += 0.45

Highlighted callout box:

box = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(0.50), Inches(y), Inches(12.33), Inches(1.50))
box.fill.solid()
box.fill.fore_color.rgb = LIGHT_GRAY
box.line.fill.background()
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, Inches(0.50), Inches(y), Inches(0.06), Inches(1.50))
bar.fill.solid()
bar.fill.fore_color.rgb = MINT
bar.line.fill.background()

Section divider: Use the 'Section Divider' layout — never draw navy rectangles manually.

divider = prs.slides.add_slide(get_layout('Section Divider'))
for ph in divider.placeholders:
    if ph.placeholder_format.idx == 0:
        ph.text = "Section Title"

3. Visual QA (Both Modes)

Assume there are problems. Your job is to find them.

3.1 Export Slides as Images

"$PYTHON" "$SKILL_DIR/scripts/visual_qa.py" output.pptx qa_images/

This uses PowerPoint COM automation to render each slide as a high-fidelity PNG.

3.2 Thumbnail Grid

"$PYTHON" "$SKILL_DIR/scripts/thumbnail.py" output.pptx

Creates a grid overview of all slides for quick visual inspection.

3.3 Content QA

"$PYTHON" -m markitdown output.pptx

Check for missing content, typos, wrong order.

Check for leftover placeholder text:

"$PYTHON" -m markitdown output.pptx | grep -iE "\bx{3,}\b|lorem|ipsum|\bTODO|\[insert"

3.4 Visual QA Checklist

When inspecting slide images, look for:

  • Overlapping elements (text through shapes, lines through words)
  • Text overflow or cut off at edges/box boundaries
  • Elements too close (< 0.3" gaps) or sections nearly touching
  • Uneven gaps (large empty area in one place, cramped in another)
  • Insufficient margin from slide edges (< 0.5")
  • Columns or similar elements not aligned consistently
  • Low-contrast text (light gray on cream, dark on dark)
  • Text boxes too narrow causing excessive wrapping
  • Leftover placeholder content
  • Leftover source content: Any text, labels, metrics, or brand names from the original deck that weren't updated
  • Hybrid metrics: Numbers or KPIs that mix source and target data (e.g., a new revenue figure next to a growth multiplier from the original deck)
  • Empty containers: Shapes, boxes, or pill elements that had content in the source but are now empty or contain generic labels
  • Mismatched structure: Visual elements (timelines, arrows, connectors) whose spatial arrangement implies a relationship that doesn't exist in the new content
  • Formatting inconsistency: Slides where bullet style, text color, or font size varies from the rest of the deck (common when some slides are edited and others rebuilt)

3.5 Verification Loop

  1. Generate/edit the deck
  2. Export slides as images
  3. Inspect every slide — list issues found
  4. Fix issues
  5. Re-export affected slides — re-inspect
  6. Repeat until a full pass reveals no new issues

Do not declare success until you've completed at least one fix-and-verify cycle.

SKILL.md

tile.json