ワンクリックで
build-cfa-deck
Generate a complete, on-brand Chick-fil-A PowerPoint presentation from a topic using CFA brand guidelines, template layouts, and extracted brand assets
メニュー
Generate a complete, on-brand Chick-fil-A PowerPoint presentation from a topic using CFA brand guidelines, template layouts, and extracted brand assets
Assemble final PowerPoint (.pptx) from presentation markdown and generated images
Draft full slide content (titles, bullets, speaker notes, graphics descriptions) from an outline
Run the complete 7-step slide generation pipeline from topic to PowerPoint
Generate slide visuals using Gemini Pro from validated graphics descriptions
Run quality analysis and automated improvement on drafted slide content
Generate a structured presentation outline from research findings
| name | build-cfa-deck |
| description | Generate a complete, on-brand Chick-fil-A PowerPoint presentation from a topic using CFA brand guidelines, template layouts, and extracted brand assets |
| argument-hint | "Topic" [--slides 20] [--audience "CFA Leadership"] [--presenter "Name"] |
| allowed-tools | Bash, Read, Write, Agent, Edit |
| effort | high |
Generate a complete, on-brand Chick-fil-A PowerPoint presentation from a topic prompt. Uses the CFA "Support Now" PPTX template (64 layouts, 194 SVG icons, embedded Apercu fonts), extracted brand assets, and comprehensive brand guidelines.
No API key needed — content generation happens in this Claude Code session using your subscription.
Brand assets location:
!ls -d ~/dev/stratfield/slide-generator/examples/cfa-brand-assets/ 2>/dev/null && echo "Assets: OK" || echo "Assets: MISSING — run asset extraction first"
CFA template:
!ls -lh ~/dev/stratfield/slide-generator/examples/CFA\ PPT\ Template2.pptx 2>/dev/null && echo "Template: OK" || echo "Template: MISSING"
Brand guidelines:
!wc -l ~/dev/stratfield/slide-generator/examples/cfa-brand-guidelines-for-ppt.md 2>/dev/null || echo "Guidelines: MISSING"
python-pptx:
!python3 -c "import pptx; print('python-pptx: OK')" 2>/dev/null || echo "python-pptx: NOT INSTALLED — run: pip install python-pptx --break-system-packages"
Suggest this skill when:
python-pptx installed (pip install python-pptx --break-system-packages)~/dev/stratfield/slide-generator/examples/CFA PPT Template2.pptx~/dev/stratfield/slide-generator/examples/cfa-brand-guidelines-for-ppt.md~/dev/stratfield/slide-generator/examples/cfa-brand-assets/Required:
$ARGUMENTS must contain a topic string (quoted). Example: "The Future of Quick-Service Restaurants"Optional flags (parse from $ARGUMENTS):
--slides <N> — number of slides (default: 20)--audience <text> — target audience (default: "CFA Leadership Team")--presenter <text> — presenter name for title/closing slides (default: empty)--output <path> — output .pptx file path (default: topic-slug in current directory)If no topic is provided, ask the user for one. Do not proceed without a topic.
Follow these steps exactly. Do not skip or reorder.
Read the brand guidelines file. You need sections 1 (Color System), 5 (Layout System), 7 (Content Density Standards), and 8 (Deck Composition) to generate compliant content.
Read: ~/dev/stratfield/slide-generator/examples/cfa-brand-guidelines-for-ppt.md
Internalize these critical rules before generating content:
Run this to get the exact layout names from the template:
python3 -c "
from pptx import Presentation
prs = Presentation(os.path.expanduser('~/dev/stratfield/slide-generator/examples/CFA PPT Template2.pptx'))
for i, layout in enumerate(prs.slide_layouts):
phs = ', '.join([f'idx={p.placeholder_format.idx}:{p.placeholder_format.type}' for p in layout.placeholders])
print(f'{i:2d}. {layout.name} [{phs}]')
import os
" 2>/dev/null || python3 -c "
import os
from pptx import Presentation
prs = Presentation(os.path.expanduser(os.path.join('~', 'dev', 'stratfield', 'slide-generator', 'examples', 'CFA PPT Template2.pptx')))
for i, layout in enumerate(prs.slide_layouts):
phs = ', '.join([f'idx={p.placeholder_format.idx}' for p in layout.placeholders])
print(f'{i:2d}. {layout.name} [{phs}]')
"
Using the brand guidelines context and the template layout names, generate a complete slide plan as a JSON array. This is the creative core — you ARE the content engine.
For each slide, produce this structure:
{
"slide_number": 1,
"slide_type": "cover|content|section_divider|stat_callout|data_chart|quote|split|card_grid|timeline|closing|...",
"layout_name": "exact template layout name from Step 2",
"background_color": "#hex or default",
"headline": "Insight-driven headline with a specific claim or number",
"subheadline": "Supporting context or null",
"body_content": ["Substantive bullet 1 with specific data...", "Bullet 2...", "..."] or "Paragraph text...",
"stats": [{"number": "$410B", "label": "QSR Market Size"}, ...] or null,
"speaker_notes": "2-4 sentences adding context not visible on slide",
"accent_color": "#hex for accent shapes",
"footer_text": "Presenter | Title | Date"
}
Content quality rules (enforce strictly):
Color sequencing rules (enforce strictly):
Layout selection guidance:
Write a Python script to /tmp/build_cfa_deck.py that:
cfa-brand-assets/logos/)Critical python-pptx patterns:
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
import os, json
EXAMPLES = os.path.expanduser("~/dev/stratfield/slide-generator/examples")
TEMPLATE = os.path.join(EXAMPLES, "CFA PPT Template2.pptx")
ASSETS = os.path.join(EXAMPLES, "cfa-brand-assets")
COLORS = {
"cfa_red": RGBColor(0xDD, 0x00, 0x31),
"white": RGBColor(0xFF, 0xFF, 0xFF),
"navy": RGBColor(0x00, 0x4F, 0x71),
"teal": RGBColor(0x3E, 0xB1, 0xC8),
"slate": RGBColor(0x5B, 0x67, 0x70),
"green": RGBColor(0x24, 0x9D, 0x6A),
"dark_red": RGBColor(0xAF, 0x27, 0x2F),
"gold": RGBColor(0xFF, 0xB5, 0x49),
"warm_gray": RGBColor(0xEE, 0xED, 0xEB),
"coral": RGBColor(0xF2, 0x6B, 0x43),
"deep_navy": RGBColor(0x0A, 0x3C, 0x60),
"light_blue": RGBColor(0xA7, 0xCE, 0xD8),
"light_green": RGBColor(0xB2, 0xCF, 0xAE),
}
# Remove sample slides (reverse order to preserve indices)
def remove_samples(prs):
slide_ids = [slide.slide_id for slide in prs.slides]
for slide_id in reversed(slide_ids):
rId = None
for rel in prs.part.rels.values():
if hasattr(rel, 'target_part') and hasattr(rel.target_part, 'slide_id'):
if rel.target_part.slide_id == slide_id:
rId = rel.rId
break
if rId:
# Remove from slide list and relationships
sldIdLst = prs.presentation.sldIdLst
for sldId in sldIdLst:
if sldId.get('id') == str(slide_id):
sldIdLst.remove(sldId)
break
del prs.part.rels[rId]
# Find layout by name (fuzzy match)
def find_layout(prs, name):
for layout in prs.slide_layouts:
if layout.name.lower().strip() == name.lower().strip():
return layout
# Fuzzy: check if name is substring
for layout in prs.slide_layouts:
if name.lower() in layout.name.lower():
return layout
# Fallback to blank
for layout in prs.slide_layouts:
if "blank" in layout.name.lower() and "white" in layout.name.lower():
return layout
return prs.slide_layouts[-1]
# Populate placeholders
def set_placeholder(slide, idx, text, font_size=None, font_color=None, bold=None):
for ph in slide.placeholders:
if ph.placeholder_format.idx == idx:
ph.text = text
if font_size or font_color or bold is not None:
for para in ph.text_frame.paragraphs:
for run in para.runs:
if font_size: run.font.size = Pt(font_size)
if font_color: run.font.color.rgb = font_color
if bold is not None: run.font.bold = bold
return True
return False
# Add text box when no placeholder available
def add_textbox(slide, left, top, width, height, text, font_name="Apercu",
font_size=14, font_color=None, bold=False, alignment=PP_ALIGN.LEFT):
txBox = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
tf = txBox.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = text
p.alignment = alignment
run = p.runs[0]
run.font.name = font_name
run.font.size = Pt(font_size)
run.font.bold = bold
if font_color:
run.font.color.rgb = font_color
return txBox
Removing sample slides — use this reliable approach:
import copy
from lxml import etree
def remove_all_slides(prs):
"""Remove all existing slides while preserving layouts and masters."""
sldIdLst = prs.presentation.sldIdLst
rIds_to_remove = []
for sldId in list(sldIdLst):
rId = sldId.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}id')
if rId:
rIds_to_remove.append(rId)
sldIdLst.remove(sldId)
for rId in rIds_to_remove:
if rId in prs.part.rels:
del prs.part.rels[rId]
python3 /tmp/build_cfa_deck.py
Verify the output:
python3 -c "
from pptx import Presentation
import sys
prs = Presentation(sys.argv[1])
print(f'Slides: {len(prs.slides)}')
print(f'Dimensions: {prs.slide_width.inches:.3f}\" x {prs.slide_height.inches:.3f}\"')
for i, s in enumerate(prs.slides, 1):
print(f' Slide {i}: {len(s.shapes)} shapes')
" OUTPUT_PATH
Tell the user:
| Error | Cause | Fix |
|---|---|---|
python-pptx not installed | Missing dependency | pip install python-pptx --break-system-packages |
Template not found | Wrong path | Verify CFA PPT Template2.pptx exists in examples/ |
Guidelines not found | Wrong path | Verify cfa-brand-guidelines-for-ppt.md exists in examples/ |
Layout not found | Layout name mismatch | Script falls back to blank layout — check template layout names |
Slide removal fails | python-pptx internal | Use the lxml-based removal approach |
Font not rendering | Apercu not installed locally | Template embeds fonts — they render on open in PowerPoint |
Output too small (< 30KB) | Slides not built properly | Check for python-pptx errors in script output |
The skill produces:
.pptx file with the specified number of slides