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.
84
84%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Before doing anything else, determine which workflow to use.
Does an existing .pptx file need to be preserved?
| Condition | Mode |
|---|---|
| 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 deck | Generation → Section 2 |
Examples:
The test is simple: does a .pptx file exist that we need to preserve? If yes, use Editing. If no, use Generation.
Every polished Metis content slide uses the same five slots. Before picking a component, the layout must contain all five.
| Slot | Purpose | Treatment |
|---|---|---|
| 1. Title | One-line claim in brand-primary | 28pt bold, Dark Navy #20206E, top of slide |
| 2. Subtitle | The "so-what" in plain language | 13pt italic, neutral gray, directly below title |
| 3. Body | A visual structure that expresses the thought | Pulled from the Timesaver library (see §0.6), or built as a bespoke metaphor — never plain bullets on white |
| 4. Takeaway bar | Single bold mantra that ties body to message | Full-width Dark Navy bar with optional Mint left accent, ~0.65" tall, white/mint text, near bottom (y≈6.05") |
| 5. Brand frame | Logo + page number + green arrow | Inherited automatically from 1_Title Only master layout |
The takeaway bar is required for content slides. If you can't write one bold mantra that summarizes the slide, the slide isn't tight enough yet. Real examples:
The brand frame, title placeholder, and footer all come from master.pptx's
1_Title Only layout — never re-draw them.
Metis Strategy slide design has two co-equal paths, both grounded in the
same brand system (metis-brand.md). Choose per slide based on what the
content needs — neither path is the default or the fallback for the other:
references/patterns.md + references/metis-brand.md)
36 brand-agnostic component patterns with exact python-pptx code recipes.references/timesaver_index.md.Both paths use the same colors, fonts, master, and frame. The decision is structural: does this content live naturally in cards/columns/boxes, or does the shape itself express the thought?
Path A (patterns.md) and Path B (Timesaver) are co-equal candidate libraries —
neither is the default, neither is the fallback. Every content slide is a
candidate for either, and the right choice depends on what the slide's
structural relationships actually require.
The choice is not made by reading the slide title and pattern-matching keywords. It's made by looking at candidates from both libraries against the slide's articulated structural needs, and picking the one that fits best. If you find yourself deciding "this is enumeration, so Path A" without ever loading a Timesaver PNG, you've shortcut the choice.
Operationally, this means every content slide goes through:
Why both paths get equal weight: patterns.md is faster to build with for content that's genuinely card-shaped or column-shaped. Timesaver structures carry meaning the components can't — a hub-and-spoke says something about relationships, a house says something about dependencies. If you only consult patterns.md, you produce slides that look right but miss the structural metaphor. If you only consult Timesaver, you force shapes onto content that doesn't need them. Browse both, every time.
Bespoke metaphor is the third option for content that doesn't fit either
library — see "Universal Closers & Metaphor Primitives" in metis-brand.md.
This is the fallback, not a default; it requires an explicit rationale that
neither library fits.
The catalog ships in this skill:
references/timesaver_index.md — 260 rows
with hand-written "Use when..." triggers.
PNG previews live on the shared drive:
G:\Shared drives\Knowledge Management\New Brand Assets\PPT Assets\timesaver_pngs\slide-{N}.png
Every Metis machine has the G:\ drive mapped, so the path resolves identically for everyone — no per-user setup, no rendering, no caching.
Requirements: Read access to G:\Shared drives\Knowledge Management\New Brand Assets\ (default for all Metis staff).
references/timesaver_index.md. Scan the "Use when..." column. Find rows that match the thought.metis-brand.md (colors, fonts, master, margins).Examples of thought → shape:
| Thought | Shape | Catalog ref |
|---|---|---|
| Three layers, each depending on the one below | House (foundation → body with pillars → roof) | slide 179 |
| Six things that reinforce each other | Honeycomb / hex flywheel | slide 155 |
| Three units forming a unified whole | Triangle concept | slide 160 |
| Now → near → far progression | Linear process / chevron flow | slide 114 |
| One center radiating to many capabilities | Hub-and-spoke (e.g., McKinsey 7-S) | slide 200 |
| Branching decision logic | Decision tree | slide 130 |
| Narrowing stages from broad to specific | Funnel | slide 167 |
| Two states being compared | Comparison chart or Before/After | slide 175 |
If neither path fits the thought, build a bespoke metaphor from
primitives (triangles, hexagons, chevrons, pillars). See metis-brand.md
"Universal Closers & Metaphor Primitives."
The browse described above is a sequence of concrete tool calls in the conversation, not an abstract recommendation. Below is what it looks like end-to-end for one slide. Reproduce this shape for every content slide.
Slide spec (generic):
"Build a content slide titled Three layers of platform capability. The slide describes a foundation layer, a middle services layer, and a top experience layer — each layer depends on the one below."
Step 1 — Articulate structural needs from the content.
Before surfacing any candidates, write the structural requirements. These are content-derived, not library-derived:
These are the locked criteria. The candidate that wins must match them.
Step 2 — Surface candidates from both libraries.
From patterns.md: a stacked-card layout (#9 Cards+Header, three rows) could
plausibly fit. Component is in conversation context already (patterns.md was
read at build init).
From timesaver_index.md (the "Concept slides — metaphors" section):
candidates include:
slide-179 (House chart) — "layered architecture (foundation → body → roof)"slide-159 (Pyramid concept) — "broad base narrowing to a peak"slide-185 (Level diagram) — "discrete graduated levels"Load the candidate PNGs via the multimodal Read tool:
Read("G:\Shared drives\Knowledge Management\New Brand Assets\PPT Assets\timesaver_pngs\slide-179.png")
Read("G:\Shared drives\Knowledge Management\New Brand Assets\PPT Assets\timesaver_pngs\slide-185.png")(House and Level Diagram are the closest matches to the locked needs; Pyramid is a weaker match because the content doesn't imply narrowing.)
Step 3 — Pick against the locked needs.
slide-179 (House) carries all three structural needs: stacked layers,
dependency (the body depends on the foundation, the roof depends on the body),
and the foundation-widest visual logic.
slide-185 (Level Diagram) carries layers and graduated tiers, but the
dependency relationship is implicit and weaker.
patterns.md #9 (Cards) carries layer enumeration but not dependency. The
cards sit side by side or in a grid — they don't structurally show one
depending on another.
Pick: timesaver slide-179 (House). Rationale ties back to locked needs:
"slide-179 is the only candidate that carries layered + dependency + foundation-widest visual semantics. The other candidates require external annotation to communicate dependency, which the House metaphor does intrinsically."
Step 4 — Reproduce the layout.
Reproduce slide-179 in python-pptx using brand tokens from metis-brand.md.
Don't freehand the layout from memory; the candidate PNG is in conversation
context — refer to it for shape positions, hierarchy, label placement.
Anti-patterns — what this workflow done wrong looks like:
timesaver slide-179 in the plan without ever
calling the Read tool on the corresponding PNG. The citation is unverifiable
and the build will be a freehand approximation, not a reproduction.Use this workflow when an existing .pptx file is provided or referenced. This workflow preserves the template's layouts, themes, formatting, and brand identity.
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,markitdownDo 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
doneBefore 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.pptxReview the exported slide images and extracted text to understand:
After analyzing the deck, classify every slide into one of three categories before making any changes:
| Category | When to use | Action |
|---|---|---|
| Keep & Edit | The 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 & Rebuild | The 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 Slide | No 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:
A slide IS reusable when:
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 |If the deck is NOT Metis-branded, extract the client's brand tokens:
"$PYTHON" "$SKILL_DIR/scripts/extract_brand.py" input.pptxThis outputs a JSON dict with:
Use these tokens when creating new slides so they match the client's style.
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.
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.xmlDelete 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>.
Deleting slides requires cleanup beyond just removing the <p:sldId> entry. Follow this sequence:
<p:sldId> element from <p:sldIdLst> in presentation.xmlppt/slides/slideN.xml)ppt/slides/_rels/slideN.xml.rels)ppt/_rels/presentation.xml.rels (the <Relationship> with the matching rId)[Content_Types].xml (the <Override> for /ppt/slides/slideN.xml)clean.py to catch any remaining orphaned media, notes, or referencesNever 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.
For each slide classified as "Keep & Edit" in triage (Section 1.2.1):
unpacked/ppt/slides/slide{N}.xmlUse the Edit tool, not sed or Python scripts. The Edit tool forces specificity.
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:
For every text shape you edit:
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:
<a:buChar> or <a:buAutoNum> on each <a:pPr>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.
When you need to create new slides that match the client's style:
references/patterns.md to pick the right grid pattern and component patternsFor Metis-branded decks being edited, you may reference references/metis-brand.md for the code recipes.
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:
For New Slides in a client deck:
"$PYTHON" "$SKILL_DIR/scripts/add_slide.py" unpacked/ slideLayoutN.xmlreferences/patterns.md<p:sldId> at the correct position in presentation.xmlKey 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:
DARK_NAVY with the client's primary_fill colorCalibri with the client's primary fontcontent_area boundariesUse this workflow when you need to add a small number of new slides to an
existing deck without disturbing any prior slide. It isolates new
construction in a sandbox file, forces visual review, and uses PowerPoint's
own Slides.InsertFromFile for the final commit — which preserves the
source master so brand framing carries over cleanly.
Step 1 — Build the new slides in a separate sandbox file.
master.pptx (so the brand frame is inherited).build_new_slides.py).Step 2 — Render the new slides to PNG via PowerPoint COM.
import win32com.client
ppt = win32com.client.Dispatch("PowerPoint.Application")
ppt.Visible = 1
pres = ppt.Presentations.Open(SANDBOX_PATH, WithWindow=False)
for idx in NEW_SLIDE_INDICES:
out = f"renders/slide-{idx}.png"
pres.Slides(idx).Export(out, "PNG", 1920, 1080)
pres.Close()
ppt.Quit()Step 3 — Inspect each PNG via the multimodal Read tool. Iterate.
Step 4 — Inject into the target deck via Slides.InsertFromFile.
import shutil, win32com.client
# Always back up the target first
shutil.copyfile(TARGET, TARGET + ".bak_preInsert.pptx")
ppt = win32com.client.Dispatch("PowerPoint.Application")
pres = ppt.Presentations.Open(TARGET, WithWindow=False)
# Slides.InsertFromFile(FileName, Index, SlideStart, SlideEnd)
# Index = position AFTER which the new slides should appear
# SlideStart..SlideEnd = source slide numbers (inclusive)
# Example: source slide 2 inserted after target position 5 → becomes new slide 6
pres.Slides.InsertFromFile(SANDBOX, 5, 2, 2)
pres.Save(); pres.Close(); ppt.Quit()Why this works: InsertFromFile preserves the source master on the
inserted slides. As long as the sandbox uses Metis master.pptx branding,
the inserted slides land in the target with full Metis formatting — no XML
rewriting, no risk of corrupting prior slides, no re-doing brand tokens.
Position math (the trap to avoid): after each insert the deck shifts.
If you want final positions 6, 10, 11, the second InsertFromFile's Index
value must be computed against the post-insert-1 deck, not the original.
Always print slide titles before/after each insert to verify positions.
b="1" on <a:rPr><a:buChar> or <a:buAutoNum><a:p> elements for each item — never concatenate into one string“”‘’xml:space="preserve" on <a:t> with leading/trailing spacesBefore committing a text replacement, compare the character count of old vs. new content:
| New vs. Old Length | Risk | Action |
|---|---|---|
| Within ~30% | Low | Replace and verify in QA |
| 30-60% longer | Medium | Replace, but check the shape has wordWrap and adequate height. Consider abbreviating. |
| >60% longer | High | Do NOT force-fit. Either abbreviate the content, or classify this slide as "New Slide" in triage and rebuild with a layout that fits. |
| Significantly shorter | Low | Replace, 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.
"$PYTHON" "$SKILL_DIR/scripts/clean.py" unpacked/
"$PYTHON" "$SKILL_DIR/scripts/pack.py" unpacked/ output.pptx --original input.pptxclean.py removes orphaned slides, media, and relationships.
pack.py validates, condenses XML, and repacks with auto-repair.
"$PYTHON" "$SKILL_DIR/scripts/visual_qa.py" output.pptx qa_images/See Section 3 for the full QA process.
Use this workflow when building a new Metis-branded deck from scratch.
| Asset | Path |
|---|---|
| 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 Library | G:\Shared drives\Knowledge Management\New Brand Assets\PowerPoint Templates\Icon_Lib.pptx |
| Logo — White | G:\Shared drives\Knowledge Management\New Brand Assets\Metis Strategy Logo\Metis Strategy White RGB logo.png |
| Logo — Black-Mint | G:\Shared drives\Knowledge Management\New Brand Assets\Metis Strategy Logo\Metis Strategy Black-Mint RGB Logo.png |
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.pyRequired package:
python-pptx— if missing:"$PYTHON" -m pip install python-pptxDo NOT loop trying to install Python or add it to PATH. It is already installed.
Two co-equal design paths share the same brand system. Per slide, pick the path that fits the content (see §0.6 for routing rules):
references/patterns.md for component selection. Read references/metis-brand.md for python-pptx code recipes.references/timesaver_index.md for the "Use when..." catalog. Load PNG candidates from the shared drive (see §0.6).Both paths share the brand constants below.
Key brand constants:
#20206E, Metis Blue #256BA2, Mint #3BDAC0, Dark Gray #4A4A4A, Light Gray #F2F2F2Read 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.
| Grid | Best for |
|---|---|
| 1. Full Width | Executive summaries, narratives, key findings |
| 2. Two Column | Comparisons, before/after, pros/cons |
| 3. Sidebar + Main | KPI callout + explanation |
| 4. Three Column | Phases, pillars, options |
| 5. Title + Visual | Process flows, org charts, tables |
Before any slide-by-slide planning, prime the conversation with both candidate libraries. This is the precondition that makes the per-slide browse in §0.6 meaningful — the agent can't compare candidates from two libraries it hasn't loaded.
Read references/representatives.json. This file maps each Timesaver
catalog section to 3–4 canonical exemplar slide-Ns. Load each listed PNG
via the multimodal Read tool from
G:\Shared drives\Knowledge Management\New Brand Assets\PPT Assets\timesaver_pngs\slide-{N}.png.
Total: ~50 PNG loads. These provide visual awareness of every catalog
section, not just the one the agent's first instinct points at.
Read references/patterns.md in full. Components are in context
alongside the Timesaver representatives.
After build init, both libraries are in conversation context. The per-slide pick (§0.6) becomes a comparison against locked structural needs, not a reach for one library by default.
Why build init is mandatory: the failure mode it prevents is content-type misclassification. If the agent decides "this is enumeration, only browse the Text-based section," it loads candidates only from one section and can't recognize when a slide actually fits a metaphor it hasn't seen. Loading representatives from every section eliminates that failure point.
Build init applies to single-slide and quick-build tasks too — don't scope it down. A common rationalization is "this is just one slide / a quick draft / an iteration on an existing deck, the full sweep is overkill." That reasoning is wrong, and naming it here so it can be recognized when the temptation appears: misclassification is a per-slide failure mode, not a per-deck one. A single-slide planner that loads only candidates from the section it thinks the slide belongs to has the same blind spot as a single-slide planner that skips the catalog entirely. Scope-down variants of this argument — "single-slide is different," "this is just a draft," "I'm iterating, full load is wasteful" — all share the same shape and should be recognized as the same shortcut. Single-slide planning pays the same build-init cost as multi-slide planning. By design.
After build init, create the 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 content slide, the plan must include:
For each content slide, write the structural needs before surfacing candidates. This is the bias-fighting step: by committing to what the slide structurally requires before looking at what's available, the candidate selection has to defend itself against the locked criteria, not against the agent's preference for one library.
Format:
Slide content: <one-line summary of what the slide says>
Structural requirements (derived from content, not from libraries):
1. <e.g., "show four parties around a center">
2. <e.g., "imply bidirectional contribution">
3. <e.g., "make the center smaller than the parties to signal stewardship">Vague requirements ("clean, on-brand, professional") are not valid. The requirements must derive from the slide's actual content and structural relationships — things a reviewer can match against any candidate.
For every content slide, the plan includes:
patterns.md candidate (component number + brief reason it fits the
locked needs)timesaver candidate (slide-N + brief reason it fits the locked needs,
with the corresponding PNG loaded via Read)Both candidates require corresponding Read tool calls earlier in the conversation. For Timesaver candidates that's the PNG; for patterns.md candidates that's the build-init full read of patterns.md (which already covers the component). A citation without read evidence is not valid.
Then pick the chosen Source and write the rationale — referencing the locked structural needs, not introducing new criteria at decision time.
Apply during planning:
Visual Variety Rule — apply during planning:
Source column naming either a patterns.md component number or a timesaver slide-{N} reference for each content slide:| # | Title | Type | Structural needs | patterns.md cand. | timesaver cand. | Chosen | Why |
|---|------------------------------|---------|---------------------------------------------|-------------------------|-----------------------------|-----------------------------|------------------------------------------------------|
| 1 | Cover | fixed | — | — | — | — | — |
| 2 | Agenda | content | 4 sequential topics, equal weight | #7 Callout | slide-23 (Four categories) | #7 Callout | enumeration; no metaphor adds value |
| 3 | The Opportunity | content | one bold claim + supporting context | #36 Pull Quote | slide-22 (Statements) | #36 Pull Quote | quote primacy fits hero framing better than 3-up |
| 4 | Market Landscape | content | 3 segments, equal weight, comparable | #9 Cards+Header | slide-23 (Four categories) | #9 Cards+Header | enumerative; comparison is implicit |
| 5 | Strategic Pillars | content | foundation → body → roof; layered dependency | #9 Cards+Header (3-row) | slide-179 (House) | timesaver slide-179 (House) | dependency relationship needs metaphor |
| 6 | Transformation Roadmap | content | 5-step linear progression | #22 Process | slide-114 (Linear Process) | #22 Process | both fit; #22 is faster build, picks tied |
| 7 | Capability Ecosystem | content | 6 reinforcing pieces around a center | #9 Cards+Header | slide-155 (Hex flywheel) | timesaver slide-155 (Hex) | reinforcement needs the radial structure |
| 8 | Closing / Call to Action | content | single mantra, hero scale | #36 Pull Quote | slide-32 (Quote + Comm.) | #36 Pull Quote | single sentence beats elaborated quote |The plan must include both candidate columns for every content slide — a patterns.md candidate AND a timesaver candidate — even when the chosen pick is obvious. Filling in both columns forces consideration of both libraries before the pick is made. The "Why" cell ties the choice back to the "Structural needs" cell.
Firm overview is optional. Ask the user: "Should I include the Metis firm overview slides?"
Create a single Python script (build_deck.py). Critical rules:
master.pptx — never open intro-slides.pptx directly as the base.'1_Title Only' layout for content slides — gives green arrow, logo, footer, page number.'Blank' for content slides — it has none of the brand elements.'Section Divider' layout for dividers — navy background is built in.add_textbox().intro-slides.pptx using the copy_slide_from() helper.See references/metis-brand.md for the full boilerplate code including:
get_layout() helperadd_content_slide() helperadd_section_divider() helpercopy_slide_from() helpersave_clean() helperFor each content slide, follow the path picked in Phase 1:
Path A (component recipe):
slide = add_content_slide("Title", "Subtitle")references/patterns.md — every Path-A content slide MUST use at least one component. Plain text on white is never acceptable.references/metis-brand.md and call the component functions.Path B (Timesaver structure):
slide = add_content_slide("Title", "Subtitle")G:\Shared drives\Knowledge Management\New Brand Assets\PPT Assets\timesaver_pngs\slide-{N}.png via the multimodal Read tool.references/metis-brand.md (colors, fonts, margins). Do not freehand colors or fonts.Every text element must have real content. No placeholder text, no "Lorem ipsum."
Before saving, verify:
Proposals / BD Decks:
In-Project Deliverables:
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.45Highlighted 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"Assume there are problems. Your job is to find them.
"$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.
"$PYTHON" "$SKILL_DIR/scripts/thumbnail.py" output.pptxCreates a grid overview of all slides for quick visual inspection.
"$PYTHON" -m markitdown output.pptxCheck 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"When inspecting slide images, look for:
Do not declare success until you've completed at least one fix-and-verify cycle.