Generates branded PowerPoint slide decks. Triggered when the user asks to "create a deck", "build a presentation", "make slides", "generate a PPTX", "board deck", "pitch deck", "quarterly review deck", "slide presentation", "executive presentation", or references PPTX/PowerPoint output.
How this skill is triggered — by the user, by Claude, or both
Slash command
/presentation-generation:pptx-generatorThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate branded slide decks using PptxGenJS (Node.js) for creation and python-pptx for reading/editing. Every deck must have visual variety — no text-only slides, no consecutive same-layout slides.
Generate branded slide decks using PptxGenJS (Node.js) for creation and python-pptx for reading/editing. Every deck must have visual variety — no text-only slides, no consecutive same-layout slides.
Before generating output, check that {WORKING_DIR}/.reporting-resolved/brand-config.json exists. If it does not, tell the user: "The branding plugin is required but has not run. Please install the branding plugin and run /reporting-plugins:brand first." Do not produce unbranded output.
If a JSON data file was generated earlier in this session (in output/text/), read it as the canonical data source to ensure cross-format parity. If no prior JSON exists, use data from the conversation context directly.
Read .reporting-resolved/brand-config.json for all brand values. Read logo from .reporting-resolved/logo.png (if it exists).
npm install pptxgenjs # Creation
pip install python-pptx # Reading/editing
output/pptx/{slug}-{YYYY-MM-DD}-{HHmm}-{xxx}.pptxconst pptxgen = require("pptxgenjs");
const pres = new pptxgen();
// Brand colors (NO '#' prefix — PptxGenJS uses bare hex)
const PRIMARY = brand.colors.primary.replace("#", "");
const ACCENT = brand.colors.accent.replace("#", "");
const WHITE = "FFFFFF";
const TEXT = brand.colors.text.replace("#", "");
No # prefix on hex colors. PptxGenJS uses bare hex strings: "1B3A5C" not "#1B3A5C".
Never reuse option objects. PptxGenJS mutates options in-place. Always create fresh objects:
// WRONG
const opts = { x: 0.5, y: 0.5, w: 9, h: 0.5, color: PRIMARY };
slide1.addText("Title 1", opts);
slide2.addText("Title 2", opts); // opts was mutated!
// RIGHT
slide1.addText("Title 1", { x: 0.5, y: 0.5, w: 9, h: 0.5, color: PRIMARY });
slide2.addText("Title 2", { x: 0.5, y: 0.5, w: 9, h: 0.5, color: PRIMARY });
Rounded rectangles: Use pptxgen.shapes.ROUNDED_RECTANGLE, not RECTANGLE with rectRadius.
Every deck must use at least 3 different layouts. Never use the same layout for consecutive slides.
const slide = pres.addSlide();
slide.background = { color: WHITE };
// Logo (if not suppressed)
if (logoPath) {
slide.addImage({ path: logoPath, x: 3.5, y: 1.0, h: 0.6 });
}
// Firm name
slide.addText(brand.firm.name, {
x: 0.5, y: 2.0, w: 9, h: 0.5,
fontSize: 14, color: ACCENT, align: "center",
});
// Title
slide.addText(title, {
x: 0.5, y: 2.8, w: 9, h: 1.0,
fontSize: 36, color: PRIMARY, bold: true, align: "center",
fontFace: brand.typography.font_family_heading.split(",")[0].trim(),
});
// Date
slide.addText(date, {
x: 0.5, y: 4.5, w: 9, h: 0.4,
fontSize: 12, color: TEXT, align: "center",
});
Primary color background, white text, centered:
const slide = pres.addSlide();
slide.background = { color: PRIMARY };
slide.addText(sectionTitle, {
x: 0.5, y: 2.5, w: 9, h: 1.5,
fontSize: 32, color: WHITE, bold: true, align: "center",
});
const slide = pres.addSlide();
// Title bar
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0, w: 10, h: 0.8, fill: { color: PRIMARY },
});
slide.addText(slideTitle, {
x: 0.5, y: 0.1, w: 9, h: 0.6,
fontSize: 20, color: WHITE, bold: true,
});
// Body content below title bar
// ... add text, tables, charts, shapes
const kpis = [
{ label: "Revenue", value: "$12.4M", delta: "+8.2%" },
// ...
];
const cardWidth = 2.1;
const cardHeight = 1.2;
const startX = 0.5;
const startY = 1.2;
kpis.forEach((kpi, i) => {
const x = startX + (i % 4) * (cardWidth + 0.2);
const y = startY + Math.floor(i / 4) * (cardHeight + 0.2);
// Card background
slide.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x, y, w: cardWidth, h: cardHeight,
fill: { color: "F7F8FA" },
rectRadius: 0.1,
});
// Value
slide.addText(kpi.value, {
x, y: y + 0.15, w: cardWidth, h: 0.5,
fontSize: 24, bold: true, color: PRIMARY, align: "center",
});
// Label
slide.addText(kpi.label, {
x, y: y + 0.65, w: cardWidth, h: 0.3,
fontSize: 10, color: "666666", align: "center",
});
});
slide.addTable(rows, {
x: 0.5, y: 1.2, w: 9,
colW: [3, 2, 2, 2],
border: { type: "solid", pt: 0.5, color: "D1D5DB" },
rowH: 0.4,
autoPage: true,
headerRow: true,
headerRowColor: PRIMARY,
headerFontColor: WHITE,
altRowColor: "F7F8FA",
});
Split slide into two equal columns for side-by-side content (text + image, two lists, etc.).
Use PptxGenJS chart capabilities with semantic.chart_series colors.
Add to every slide (except title slide):
// Confidentiality + slide number
slide.addText(brand.content.confidentiality_notice, {
x: 0.5, y: 7.0, w: 7, h: 0.3,
fontSize: 7, color: "999999",
});
slide.addText(`${slideNumber}`, {
x: 8.5, y: 7.0, w: 1, h: 0.3,
fontSize: 7, color: "999999", align: "right",
});
Timeline visuals within a slide deck (e.g., "market expansion timeline" as part of a board deck) are handled by THIS skill. Only route to jsx-generator when the user wants a standalone interactive timeline app.
For template-based editing (unpack XML, modify, repack), see editing.md.
After generating, convert the first few slides to images for visual inspection:
# Read office scripts path from resolved brand config
OFFICE_SCRIPTS=$(python3 -c "import json; print(json.load(open('.reporting-resolved/brand-config.json'))['_office_scripts_path'])")
python "$OFFICE_SCRIPTS/soffice.py" convert output.pptx png
Or use the thumbnail script:
python {PLUGIN_ROOT}/skills/pptx-generator/scripts/thumbnail.py output.pptx
Run at least one QA cycle: generate → thumbnail → inspect → fix any issues.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub equiforte/reporting-services-plugins --plugin presentation-generation