configs/agents/skills/codex-primary-runtime/slides/SKILL.md
Create, edit, render, verify, and export PowerPoint slide decks. Use when Codex needs to build or modify a deck, presentation deck, slide deck, slides, PowerPoint, PPT, or visually ambitious editable .pptx file.
npx skillsauth add Ehrax/dotfiles PowerPointInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill whenever the user asks Codex to create, generate, edit, revise, improve, render, verify, or export a deck, presentation deck, slide deck, slides, PowerPoint, PPT, or PPTX file, unless the user explicitly requests a different slide skill or tool.
This is the internal PPTX workflow for visually ambitious editable decks.
@oai/artifact-tool JS package which exists in the default Codex workspace dependencies node_modules for final deck construction, scratch preview rendering, internal editable-text verification, and .pptx export. Use the bundled workspace Node.js and Python runtimes for local builders and helper scripts.NODE_PATH; ensure bundled packages resolve through normal Node package lookup from the builder file.node_modules directory link or Windows junction to the bundled node_modules; do not copy bundled dependency directories or import internal package files directly..pptx artifact(s). Use a Markdown link whose label is <deck or slide title> - <filename> and whose target is the platform-appropriate absolute filesystem path to the final .pptx. If there are multiple requested final decks, put each final .pptx Markdown link on its own line. Do not wrap final artifact links in backticks or code fences, and do not put them in bullets, headings, or prose sentences.artifact_tool, artifact-tool, @oai/artifact-tool, the Node/JS builder, copied builder scripts, package manifests, export workflow, verification workflow, rendered previews, or internal tooling unless explicitly requested..pptx artifact link in the final response.pypdf since we don't have the javascript version installed.fit: "cover".PptxGenJS, pptxgenjs_helpers, python-pptx, LibreOffice rendering, or screenshot-only slides.slide.charts.add(...). Do not hand-draw these with shapes, connectors, dots, or text boxes when the chart API can represent them.montage, or magick part of the required authoring path. Contact sheets are optional and must never block .pptx export, rendered previews, or lightweight verification.Start deck authoring in a local builder file with:
const {
Presentation,
PresentationFile,
} = await import("@oai/artifact-tool");
For existing decks, also import FileBlob:
const { FileBlob, PresentationFile } = await import("@oai/artifact-tool");
const pptx = await FileBlob.load("input.pptx");
const presentation = await PresentationFile.importPptx(pptx);
Default new deck size is 16:9 at 1280x720:
const presentation = Presentation.create({
slideSize: { width: 1280, height: 720 },
});
Write final deck deliverables in the chosen output directory, using an outputs subdirectory named for the unique thread id unless the user gives an explicit output folder. The final deck artifact is output.pptx; any agent-authored narrative_plan.md created during planning is a support file. Keep previews, inspect records, reference images, and verification scratch in the deck-specific scratch directory under tmp/slides. Use the filesystem paths resolved in the current environment.
Use this quick surface first when editing the JS builder. It is a curated subset of the full TypeScript presentation API, not the full reference.
@oai/artifact-tool from Node for final deck construction, rendering, editable-text verification, and .pptx export.slide.shapes, shape.text, slide.tables, slide.charts, and slide.speakerNotes.slide.charts.add(...) for data-backed charts and graphs that the API can express; do not construct bar, line, scatter, pie, treemap, map, axis, series, or data-label systems out of raw shapes/text boxes."rect", "roundRect", "ellipse", "rightArrow", and "connector"; use geometry: "custom" with customPaths when a preset cannot express the vector shape.presentation.export({ slide, format: "png", scale: 1 }) before final export, and use scratch inspect records to confirm important copy is editable.Presentation.create({ slideSize: { width: 1280, height: 720 } }).Presentation is the in-memory deck object; PresentationFile is for .pptx import/export.PresentationFile.importPptx(await FileBlob.load("input.pptx")).const pptx = await PresentationFile.exportPptx(presentation); await pptx.save("output.pptx").await presentation.export({ slide, format: "png", scale: 1 })."accent1", "background1", "text1", "#FF6600", "#11223380".{ style: "solid" | "dashed" | "dotted" | "dash-dot" | "dash-dot-dot", fill, width }.0..100000; rotation is in degrees.typeface.shape.text.get("word").style = "heading1" styles the whole paragraph and clears inline overrides. Apply inline overrides after assigning styles.shape.text.insets and fontSize are pixels, but spacingBefore and spacingAfter are 1/100 point.fit: "cover" to fill a frame with possible cropping; use fit: "contain" to preserve the entire image.readImageBlob(...) and pass an exact ArrayBuffer as { blob: await readImageBlob(path) }.chart.lineOptions.grouping = "standard" before export. Leaving grouping unset can produce repair-prone PowerPoint chart XML.chart.chartFill in PPTX exports for now; prefer a panel/background shape behind the chart plus chart.plotAreaFill. If whole chart-space styling is required, verify the file opens in PowerPoint without repair.chart.titleTextStyle.typeface, chart.legend.textStyle.typeface, chart.xAxis.textStyle.typeface, chart.yAxis.textStyle.typeface, and chart.dataLabels.textStyle.typeface to the same body/title font family used elsewhere.Local image helper:
const fs = await import("node:fs/promises");
async function readImageBlob(imagePath) {
const bytes = await fs.readFile(imagePath);
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
}
const { FileBlob, Presentation, PresentationFile } = await import("@oai/artifact-tool")const presentation = Presentation.create({ slideSize: { width: 1280, height: 720 } })const imported = await PresentationFile.importPptx(await FileBlob.load("input.pptx"))const pptx = await PresentationFile.exportPptx(presentation); await pptx.save("output.pptx")const slide = presentation.slides.add()const { slide, index } = presentation.slides.insert({ after: presentation.slides.getItem(0) })presentation.slides.getItem(0), presentation.slides.items, presentation.slides.countslide.duplicate(), slide.moveTo(index), slide.delete()slide.background.fill = "background1" or a solid/gradient fill objectslide.shapes.add({ geometry, position, fill, line })geometry values are non-exhaustive; use known preset names first and verify each shape by rendering the deck.shape.position = { left?: number, top?: number, width?: number, height?: number, rotation?: number, horizontalFlip?: boolean, verticalFlip?: boolean }shape.fill = "accent1"; shape.line.width = 1.5; shape.rotation = 15shape.text = "Quarterly Results" or shape.text = ["Line 1", "Line 2"]adjustmentList: [{ name: "adj", formula: "val 16667" }]; 50000 is pill-like max rounding.shape.text.fontSize = 28, shape.text.bold = true, shape.text.color = "text1"shape.text.typeface = "Aptos", shape.text.alignment = "center", shape.text.verticalAlignment = "middle"shape.text.insets = { left: 12, right: 12, top: 8, bottom: 8 }shape.text.autoFit = "shrinkText" or "resizeShapeToFitText" only when needed.shape.text.get("literal text").bold = true, shape.text.get("literal text").style = "heading1"shape.text.add("New paragraph"), shape.text.replace("Old", "New")Detached rich text:
const { Text } = await import("@oai/artifact-tool");
const block = Text.create(["Quarterly Business Review", "Q2 Execution Plan"]);
block.get("Quarterly Business Review").style = "title";
block.get("Quarterly Business Review").fontSize = 38;
block.get("Q2 Execution Plan").color = "accent1";
shape.text = block;
Connector example:
slide.shapes.add({
geometry: "connector",
kind: "elbow",
from: sourceShape,
fromIdx: 3,
to: targetShape,
toIdx: 1,
line: { style: "solid", fill: "accent1", width: 2 },
head: { type: "arrow", width: "med", length: "med" },
});
slide.images.add({ blob, fit: "cover", alt }), slide.images.add({ dataUrl, fit: "contain", alt }), slide.images.add({ uri, alt })image.position = { left, top, width, height }image.replace({ blob: replacementBlob, alt: "Updated hero" })image.crop = { left: 0.05, top: 0.05, right: 0.05, bottom: 0.05 }image.geometry = "roundRect" or pass geometry: "roundRect" at creation for masksconst plate = slide.images.add({
blob: await readImageBlob("tmp/slides/pro-reference-images/slide-01.png"),
fit: "cover",
alt: "Text-free visual plate",
});
plate.position = { left: 0, top: 0, width: 1280, height: 720 };
const table = slide.tables.add([["Metric", "North", "EMEA"], ["Bookings", 120, 94]])const table = slide.tables.add({ rows, columns, left, top, width, height, values })table.getCell(row, col).value = "APAC", table.setValues(matrix)table.merge({ startRow, endRow, startColumn, endColumn })table.style = "TableStyleMedium9"; table.columns.get(0).width = 220table.cells.block({ row, column, rowCount, columnCount }).fill = "#0F172A"slide.charts.add(...) for every data-backed chart or graph; do not implement bar/line/scatter/pie/treemap/map charts with slide.shapes.add(...).const chart = slide.charts.add("line" | "bar" | "scatter" | "pie" | "treemap" | "map" | "bar3D")chart.position = { left, top, width, height }chart.title = "Quarterly Revenue", chart.categories = ["Q1", "Q2", "Q3", "Q4"]const series = chart.series.add("Revenue"); series.values = [120, 140, 180, 210]; series.categories = chart.categoriesseries.fill = "accent1"; series.stroke = { width: 2, style: "solid", fill: "accent1" }chart.hasLegend = true; chart.legend.position = "bottom"chart.barOptions.direction = "column"; chart.barOptions.grouping = "stacked"chart.dataLabels.showValue = true; chart.dataLabels.position = "outEnd"chart.lineOptions.grouping = "standard"; chart.lineOptions.smooth = falsechart.titleTextStyle.typeface = FONT.title; chart.legend.textStyle.typeface = FONT.body; chart.xAxis.textStyle.typeface = FONT.body; chart.yAxis.textStyle.typeface = FONT.body; chart.dataLabels.textStyle.typeface = FONT.bodychart.titleTextStyle.fill, chart.titleTextStyle.fontSize, chart.legend.textStyle.fontSize, chart.xAxis.textStyle.fontSize, chart.yAxis.textStyle.fontSize, chart.yAxis.majorGridlines, chart.xAxis.line, chart.yAxis.line, chart.plotAreaFill, series.fill, and series.strokeslide.shapes.add({ geometry: "roundRect", ... }) panel behind the chart instead of chart.chartFill until chart-space fill export is verified.slide.speakerNotes.setText("Presenter notes"), slide.speakerNotes.append(["Next point", "Final point"])presentation.theme.colorScheme = { name, themeColors: { accent1, accent2, bg1, bg2, tx1, tx2, ... } }presentation.theme.hexColorMapconst style = presentation.styles.add("metricLabel"); set style.fontSize, style.bold, style.color, style.typefacepresentation.styles.describe() or presentation.styles.describe("title")AutoLayout, AutoLayoutAlign, and AutoLayoutDirection only when using auto-layout.slide.autoLayout(shapes, { direction: AutoLayoutDirection.horizontal, frame: "slide", align: AutoLayoutAlign.topCenter, horizontalGap: 32, verticalPadding: 48 })AutoLayout.apply(slide, shapes, options) is the static entrypoint.const layout = presentation.layouts.add("Title Slide"); layout.placeholders.add({ name: "Title", type: "title", index: 1, text: "Title" }); slide.setLayout(layout); slide.placeholders.getItem("title").text = "Kickoff".narrative_plan.md in the final output directory during this step unless the user explicitly asks for PPTX-only output. Include audience, objective, narrative arc, slide list, source plan, visual system, imagegen plan, asset needs, and editability plan. Do not rely on the JS builder to create or overwrite this file.pypdf. Use web/source search only if local extraction fails, source data is incomplete, or the task requires current external context.Poppins for titles and Lato for body/captions. Use formal serif only when appropriate: Caladea headings and Liberation Serif body/captions.scripts/prepare_reference_prompts.js to create one prompt per slide and a manifest with expected filenames.tmp/slides/pro-reference-images/ with exact names slide-01.png, slide-02.png, and so on.tmp/slides/pro-reference-images/.fit: "cover" to place it on the slide canvas..pptx is exported and verified, delete imagegen scratch files created for this run, including raw outputs in the default generated-images location and deck-specific reference plates, unless the user explicitly requested those image files as deliverables.Run this skill's scripts/prepare_reference_prompts.js helper with the outline file, reference-image directory, slide count, deck size, and style guidance. Use platform-appropriate paths resolved from the current environment.
slide-XX.png in the reference directory from the manifest.scripts/init_pro_deck_builder_js.js or write a bespoke Node builder with the same architecture: theme constants, slide data, source notes, addText, addCard, addTitleBlock, addHeader, addPlate, native chart helpers wrapping slide.charts.add(...), table/card helpers, icon helpers, render/export helpers, and inspect-record helpers.fs.readFile, pass an exact ArrayBuffer via { blob }, and retain the source path only in scratch inspect records; do not rely on { path } for final exportPptx media embedding.scripts/init_pro_deck_builder_js.js, keep the generated sibling build/node_modules/@oai/artifact-tool package link when the script creates one. Node resolves @oai/artifact-tool from the builder file's directory, so this makes shell-run eval builders import the default Codex runtime package while preserving await import("@oai/artifact-tool") in the builder.To scaffold a builder, run this skill's scripts/init_pro_deck_builder_js.js helper with the deck id, output builder file, slide count, reference-image directory, and output deck directory. Use the deck-specific scratch directory for the builder file and selected reference images unless the task requires a different location.
Then edit SLIDES/SOURCES and slide-specific layout functions, render previews, verify, and export by running the generated builder in the existing Node environment.
Run local builder files from a workspace where @oai/artifact-tool is resolvable. Generate builders with scripts/init_pro_deck_builder_js.js so the builder directory has a local build/node_modules/@oai/artifact-tool package link to the default Codex runtime package and a package.json with type: "module". Run the generated builder with the Node executable from Codex workspace dependencies, or with the platform-appropriate command printed by the init script.
Do not use a different node binary for @oai/artifact-tool builders unless you first prove the package resolves. Do not run pnpm exec from the repo root, and do not run a builder without a sibling or otherwise resolvable node_modules/@oai/artifact-tool; Node resolves the package from the builder path.
presentation.export({ slide, format: "png", scale: 1 }); save previews under scratch tmp/slides/<deck-id>/preview/, not in the final output folder.kind, slide, role, text, textChars, textLines, and bbox.slide.charts.add(...) for every chart-like visual, and the exported PPTX should contain native chart XML parts such as ppt/slides/charts/chart*.xml when the deck includes charts.titleTextStyle, legend.textStyle, xAxis.textStyle, yAxis.textStyle, dataLabels.textStyle, axis/gridline strokes, series fill/stroke, legend position, and plotAreaFill as appropriate. Do not accept mismatched default chart fonts or generic Office chart styling when the rest of the slide has a designed visual system.output.pptx are exported. Stop after 3 total render/verify/fix loops, including the initial render.scripts/pro_deck_quality_check.js for normal deck creation. It is reserved for explicit debug, eval, or manual investigation requests.Complete only when:
output.pptx; any agent-authored narrative_plan.md remains a support file that is not surfaced unless requested.slide.charts.add(...); any shape-drawn sparkline/microchart exception is recorded in scratch verification notes..pptx artifact(s), using <deck or slide title> - <filename> as the link label and an absolute filesystem path as the target.artifact_tool, artifact-tool, @oai/artifact-tool, the Node/JS builder, scripts, package manifests, export workflow, verification workflow, or internal tooling unless explicitly requested.narrative_plan.md, generated image plates, rendered previews, screenshots, contact sheets, verification records, scripts, package manifests, scratch files, or other support artifacts unless explicitly requested.Use the installed @oai/artifact-tool TypeScript presentation docs when they are available in the current environment. Do not assume local checkout-specific absolute paths exist in packaged installs.
documentation
Compact the current conversation into a handoff document for another agent to pick up.
development
Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.
data-ai
Triage issues through a state machine driven by triage roles. Use when user wants to create an issue, triage issues, review incoming bugs or feature requests, prepare issues for an AFK agent, or manage issue workflow.
tools
Turn the current conversation context into a PRD and publish it to the project issue tracker. Use when user wants to create a PRD from the current context.