# Narrative Pptx Composer

> >

- **Type:** Skill
- **Install:** `agentstack add skill-sanpingli-skills-narrative-pptx-composer`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [sanpingli](https://agentstack.voostack.com/s/sanpingli)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [sanpingli](https://github.com/sanpingli)
- **Source:** https://github.com/sanpingli/skills/tree/main/packs/pptx-generation/narrative-pptx-composer

## Install

```sh
agentstack add skill-sanpingli-skills-narrative-pptx-composer
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Narrative PPTX Composer

## Purpose

Turn content into a finished `.pptx` that tells a compelling,
audience-aware story. Content first, design second.

## Global Rules

These apply across all steps. Do not repeat-check per step.

**Priority order:** Content → Narrative → Design; higher pillar wins. See [design-principles.md](references/design-principles.md).

**Workflow invariants:**
- All nine steps run in order for every input modality. **No skip-paths, no merged-step shortcuts.**
- **Completion gate:** the workflow is **incomplete** until `s09-session-retrospective.json` exists. Step 7's `output.pptx` is a **draft** — it is not reviewed, not delivered, and not announced to the user. Stopping after Step 7 is a workflow violation.
- Large decks: when a step's total output would exceed what fits in a single coherent pass, split by acts per [references/batching-strategy.md](references/batching-strategy.md). Each step self-assesses whether to batch.
- Agenda page numbers use the em-dash placeholder `"—"` (never `"TBD"`) in Steps 4/5/6; patched in Step 7c.
- Cross-slide consistency: numbers match `s04a-terminology-registry.json`; only `canonical` forms; low-confidence data points (`confidence `) |
| `$PROFILER_SKILL/scripts/render_samples.py` | Renders `.pptx` slides to JPEG. **Mandatory for Step 8a.** |
| `$IMAGE_SKILL/scripts/generate_images.py` | Batch image generation from JSON request. See `$IMAGE_SKILL/SKILL.md`. |

Skill-internal scripts live in `$COMPOSER_SKILL/scripts/`. Each step section below invokes the relevant ones with full commands.

---

## Workflow

```
Step 1: Input Registry
Step 2: Communication Brief
Step 3: Narrative Blueprint
Step 4: Content Draft & Form Specification
Step 5: Slide Visual Design
Step 6: Content–Zone Fitting & Speaker Notes
Step 7: Build PPTX              ← output.pptx is DRAFT, not deliverable
Step 8: QA, Fix & Deliver       ← first deliverable copy to outputs/
Step 9: Session Retrospective   ← workflow complete only after this step
```

---

### Step 1 — Input Registry

> Full procedure: [references/step1-input-registry.md](references/step1-input-registry.md)

**Session initialization (1a, before any other command):**

```bash
export SKILLS_ROOT=".claude/skills"
export COMPOSER_SKILL="$SKILLS_ROOT/narrative-pptx-composer"
export PROFILER_SKILL="$SKILLS_ROOT/pptx-profiler"
export IMAGE_SKILL="$SKILLS_ROOT/image-generator"
```

If unset, validators and rendering fail silently.

Four sub-tasks: (1a) session directory; (1b) classification → `s01b-query-intent.json`; (1c) content extraction → `s01c-content-digest.json`; (1d) design registration → `s01d-design-config.json`. **1c and 1d co-execute in one pass.** Optional: `1c-pptx` for `.pptx` docs, `1c-image` for image inputs (three-track VLM).

**Guard rules:**

- **Registry only, not analysis.** No audience/purpose/formality inference (→ Step 2), no `imageryDemand` (→ Step 3a), no palette/typography/style (→ Step 5b). Only infer `language` and `inputMode`.
- **`inputMode`** set in 1b, locked. One of `generate`/`beautify`/`reproduce`/`expand`.
- **`s01b.inferences[]`** whitelisted to `language` + `inputMode` only — validator hard-fails others.
- **`s01d` resolution:** user-explicit → reference-image (VLM) → deferred. Non-supplied design fields → `null` + `deferred-step5b`. Only `dimensions` (16:9) and `monoFont` (`JetBrains Mono`) get `system-default`.
- **`sourceProvenance`** (origin tag) mandatory per leaf: `user-explicit` / `reference-image` / `system-default` / `deferred-step5b`. Null↔deferred coupling enforced.
- **Media:** extract images/diagrams into `mediaAssets`; log conflicts in `mergedSections.conflicts[]` — never block.

**Outputs:** `s01b-query-intent.json`, `s01c-content-digest.json`, `s01d-design-config.json`; optionally `s01c-image-style-extraction.json`.

```bash
python $COMPOSER_SKILL/scripts/s01_validate_inputs.py "$SESSION"
```

---

### Step 2 — Communication Brief

> Full procedure: [references/step2-comm-brief.md](references/step2-comm-brief.md)

Pure LLM reasoning — **no design knowledge**. Always runs (mandatory for every modality). Sparse signals → thin brief with `inferences` + `confidence`.

**Guard rules:**

- Consume `s01b.explicitSignals` verbatim. **Step 2 owns all audience/purpose inference.**
- Single-pass: `presentationPurpose` (∈ inform/persuade/report/propose/inspire) + `purposeStatement`; `audience`; `communicationObjectives` (think/feel/do); `keyMessages` (1–5); `constraints` (language, formalityLevel, optional timeMinutes).
- **Out of scope:** `slideCountConstraint` (→ Step 3c), `imageryDemand` (→ Step 3a).
- `audience.decisionPower` required when purpose ∈ {persuade, propose, report}.
- Brief-only mode: infer all unsupported fields; record in `inferences` with `confidence`. **Never block to ask.**

**Outputs:** `s02-communication-brief.json`

```bash
python $COMPOSER_SKILL/scripts/s02_validate_brief.py "$SESSION"
```

---

### Step 3 — Narrative Blueprint

> Full procedure: [references/step3-narrative-blueprint.md](references/step3-narrative-blueprint.md) | Patterns: [references/storytelling-patterns.md](references/storytelling-patterns.md)

Pure LLM reasoning — **no design knowledge**. Three sub-phases: (3a) narrative architecture, (3b) structural scaffolding, (3c) per-slide blueprint.

**Guard rules — 3a:**

- Five sub-tasks: core argument, pattern & acts, act transitions, opening hook & closing takeaway, information hierarchy.
- Pattern paths: reuse / compose / nest / design new. Record `origin`. Justify from content+audience, not the pattern table.
- **Core argument:** falsifiable claim (not vague topic). Derive from `s02.keyMessages[rank=1]`; log any departure in `inferences[]`.
- Every `cut` item needs `reason`.

**Guard rules — 3b:**

- Decide structural slides (executive summary, agenda, dividers, recap, appendix). Executive summary ↔ opening hook mutually exclusive. Appendix excluded from `totalSlides`.
- **Section dividers:** include only when (a) each act has enough body slides to justify a structural marker (minimum depth ≥ 2 — a single-slide act makes its divider feel hollow), AND (b) the total divider count stays well below ~25 % of body slides (avoids over-scaffolding). Never asymmetric — all acts or none.

**Guard rules — 3c:**

- Per-slide fields: `slideId`, `headlineMessage`, `narrativeRole` (story function), `slideType`, `narrativeIntent` (delivery mode), `transitionIn`/`Out`, `informationDensity`, `contentSource`, `sourceRef`.
- **Slide count is bottom-up.** `slideCountConstraint` clamped here (single decision point). Exactly one `cover` + one `closing`. Step 5 may not insert/remove slides.
- **Pacing rule:** every 6–8 body slides ≥ 1 with `informationDensity: "minimal"`.
- Every body slide MUST have `headlineMessage`. Structural slides may leave null.
- Adjacent `transitionOut[N]` / `transitionIn[N+1]` must be logically coherent. If incoherent: revise N+1's `transitionIn` to align.
- **Media:** assign `mediaAttachments` from `s01c.mediaAssets`; each on ≤ 1 slide.
- **Illustration intent:** single decision point for the illustration roster. Run minimum imagery check. Step 5 may not back-fill or demote. Field shape: [step3-narrative-blueprint.md § illustrationIntent](references/step3-narrative-blueprint.md).
- Document mode: `supportingPoints` need `sourceRef`; numbers match `preGlossary` (initial term list from content extraction).

**Outputs:** `s03-presentation-blueprint.json` (embeds `architecture`: core argument, pattern, acts, hooks, hierarchy, scaffold, `contentDomain`, `imageryDemand` (deck-level imagery budget), `imageryDemandRationale`).

```bash
python $COMPOSER_SKILL/scripts/s03_validate_blueprint.py "$SESSION"
```

---

### Step 4 — Content Draft & Form Specification

> Full procedure: [references/step4-content-draft.md](references/step4-content-draft.md)

Write per-slide content (4b) and choose `contentForm` per slide (4c). Form is a **content decision**, not visual design.

**Guard rules:**

- **4a — Terminology registry.** Seed from `s01c.preGlossary` (initial term list) before 4b; finalize after. Every `dataPoints` entry: `verificationTier` (verified/common-knowledge/unverified) + `confidence` (0.0–1.0). Low-confidence precise numbers also get `qualitativeForm`.
- **Source rules:** `document` → no fabrication; `generated`/`hybrid` → numbers from registry.
- **Headlines** faithfully render blueprint's `headlineMessage`.
- **4c — `contentForm`** (`{type, ...counts}`). No spatial structure → `text-narrative` / `bullet-list`. Do not pre-name a layout.
- **`blocks[]`** for multi-zone types (`card-grid`, `comparison-matrix`, `timeline`, `step-flow`, `stat-callout`, `architecture-layers`, `before-after`, `diagram-callouts`, `icon-list`): one entry per zone with `role`, `headline`, `body`. Single-zone → empty.
- **Content metrics** auto-derived by validator. Do not hand-fill.
- **4c-illust:** refine `illustrationIntent` → `illustrationSpec` with `source: "to-generate"`, `subject`, `perImageSubject`, `compositionNote`, `contentInteraction`.
- **4d — Speaker-note cues only.** Brief transition cue per slide (tone from `formalityLevel` + `audience`). Full talk track → Step 6c. Required: non-empty `speakerNotes` for every slide.

**Outputs:** `s04-content-draft.json`, `s04a-terminology-registry.json`.

```bash
python $COMPOSER_SKILL/scripts/s04_validate_content.py "$SESSION"
```

---

### Step 5 — Slide Visual Design

> Full procedure: [references/step5-visual-design.md](references/step5-visual-design.md) | Layout: [references/layout-principles.md](references/layout-principles.md) | Tone: [references/visual-tone-mapping.md](references/visual-tone-mapping.md)

Read before this step: [design-principles.md](references/design-principles.md), [design-guardrails.md](references/design-guardrails.md).

**Execution flow:**

```
5a  Fill deferred design fields (palette, fonts, contrast)
     ↓
5b  Derive visual tone from content strategy:
     - resolve register (authoritative/analytical/conversational/inspirational/instructional-rich)
     - set color temperature, whitespace rhythm, imagery guidance per act
     - write designThesis (one-sentence design concept + 2–4 mechanisms)
     - plan compositionPalette (layout vocabulary) and titleChoreography (title treatments)
     ↓
5c  Freeze s05b-style-policy.json — read-only from here on
     ↓
5d  Design each slide's layoutSpec (three phases per slide):
     Phase 1 — Concept: read context + review prior slides' moves →
               commit aestheticMove, palette family, and focal idea
               BEFORE drawing zones (→ designRationale)
     Phase 2 — Execute: choose grid → size zones → add decorations → set background
     Phase 3 — Verify: check geometry + confirm layout delivers the Phase 1 concept
     ↓
5f  Generate images (if needed):
     - collect all source:"generated" zones → image request file
     - run generate_images.py (backgrounds: 1 variant; illustrations: 3 variants)
     - VLM selects best illustration variant (best-of-3)
     - patch file paths back into the plan
```

**Guard rules:**

- **`designThesis` (mandatory, frozen at 5c).** Single sentence: what the design is *about* + 2–4 visible `mechanisms`. Verified in Step 8, compared in Step 9.
- **`aestheticSignals` (style preferences):** `designAmbition` (style intensity) overrides default (default: `"expressive"`; `restrained` only for explicit minimalism); `moodKeywords` bias 5d; `avoidKeywords` are hard exclusions; `diversityPreference: "high"` tightens quotas.
- **5d — Layout from `contentForm`.** Design from first principles per layout-principles.md. Preserve content's spatial structure. Generic layout for structured content → justify in `designRationale.aesthetic`.
- **Zone types:** `text`, `shape`, `chart`, `image`, `icon`, `formula`. Unknown type → validator hard-fails. Formula zones: `notation: "mathtext"`, `source: "authored"`, `fallback` block required.
- **Formula-zone mandate:** when content contains math equations or symbolic expressions, create `type: "formula"` zones — **never** embed formulas as Unicode text in `type: "text"` zones. Short inline symbols (σ, θ) are acceptable in prose; standalone equations must be formula zones.
- **Coordinate units:** inches by default (bare numbers). `"%"` for percent. No mixing.
- **Design minimums** (4 quotas: layout variety, hero cadence, color-area, typographic punch): [visual-tone-design-floor.md](references/visual-tone-design-floor.md). Auto-relax with `quotaWaiver`; never block.
- **Minimum imagery requirements:** apply row for resolved `imageryDemand`. Step 3 owns the illustration roster; Step 5 may not add or remove illustration slots. Waiver needs reason ≥ 20 chars.
- **Cover–closing consistency (recommended):** cover ↔ closing share ≥ 1 decorative element in common. [step5d-slide-layout.md § Bookend](references/step5d-slide-layout.md).
- **Deck-wide decorative motifs** (`motifPalette`, recommended): 2–5 shape/pattern variations; slides classified as motif-bearing / decoration-free / non-motif. [step5d-slide-layout.md § motifPalette](references/step5d-slide-layout.md).
- **Illustration zones:** match `illustrationSpec` for `imageCount`, `placement`. Step 5 decides `imageShape` based on layout and available space. Every generated zone MUST have `fallback`.

**Outputs:** `s05-slide-visual-design.json` ([schema](schemas/s05-slide-visual-design.schema.json)), `s05b-style-policy.json`. If 5f: `s05f-image-requests.json`, `images/`, `s05f-image_generation_output.json`, `s05f-image-selection.json`.

```bash
python $COMPOSER_SKILL/scripts/s05_validate_plan.py "$SESSION"
```

---

### Step 6 — Content–Zone Fitting & Speaker Notes

> Full procedure: [references/step6-content-fitting.md](references/step6-content-fitting.md)

The **only step holding both content draft and real zone geometry** — owns fit end-to-end (no escape hatch to Step 4).

**Guard rules:**

- **6a — Map content to zones** by zone `role` names (not placeholders). `blocks[]` present → map by matching `role` (no splitting). `blocks[]` absent on multi-zone → heuristic split (informational log, not error).
- **Title compression (within Step 6 only):** Stage 1 syntactic (drop modifiers), then Stage 2 semantic (core claim stays; dropped framing → `speakerNotes`). Must always be complete statement. Validator warns at  Full procedure: [references/step7-build-pptx.md](references/step7-build-pptx.md) | Template: [references/build-script-template.md](references/build-script-template.md)

Write and execute `s07-build.py` using python-pptx. Import helpers from `$COMPOSER_SKILL/scripts/s07_slide_helpers.py`.

**Guard rules:**

- **7a — Per-`taskId` builder functions.** One `build_` per slide (promote to shared `build_` only when ≥ 2 slides share identical pattern). **Disallowed:** single generic `build_slide(tid)` branching internally. Helpers: `add_textbox`, `add_card`, `add_accent_bar`, `add_rounded_rect`, `add_outline_capsule`, `add_outline_circle`, `add_image_safe`, `set_slide_bg`, `set_slide_bg_image`, `add_image_with_overlay`, `resolve_image_path`, `add_formula`, `render_formulas`.
- **Images:** insert ONLY via `add_image_safe()` / `add_image_with_overlay()` / `set_slide_bg_image()` (aspect-ratio preserving). Never use `slide.shapes.add_picture()` directly.
- **Formulas:** call `render_formulas()` once at top of `main()` (renders to `/formulas/-.png`). Builders call `add_formula()` with cached path. No `$…$` in `formulaSource`; no SVG via `add_picture()`.
- **Illustration zones in every builder:** every builder function whose slide has an `illustration` zone in the plan MUST resolve and insert it via `resolve_image_path(plan_zone, slot)` + `add_image_safe()`. Omitting the illustration call silently drops the image — invisible until Step 8 visual QA. Audit: grep shared builders for illustration handling before dispatch.- **7c — `patch_agenda_pages()`** inline at end of `s07-build.py` before `prs.save()`. Not a separate fil

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [sanpingli](https://github.com/sanpingli)
- **Source:** [sanpingli/skills](https://github.com/sanpingli/skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-sanpingli-skills-narrative-pptx-composer
- Seller: https://agentstack.voostack.com/s/sanpingli
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
