Skip to content
Back to skills

template-filler

ASecurity

Use this skill to reuse an EXISTING, already-branded PowerPoint (.pptx) or Word (.docx) file for a new client/project — swap a client name, figures, dates, or other content everywhere it appears, while preserving 100% of the original styling, layout and masters. Different from pptx-builder-skill/docx-builder, which build a NEW document from an HTML design: this skill never touches formatting, it only rewrites the text of an existing file. Trigger on "fill in this template with...", "reuse las...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
developmentpythongoshellbashapi

Works with

  • claude desktop
  • cli
  • api
  • mcp

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 20 files and shows the line behind each finding

Scanned September 27, 2026

npx -y skills add Julien339/template-filler-skill --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of template-filler?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for template-filler
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/julien339-template-filler/badge)](https://www.skillsdirectory.com/skills/julien339-template-filler)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: template-filler
description: Use this skill to reuse an EXISTING, already-branded PowerPoint (.pptx) or Word (.docx) file for a new client/project — swap a client name, figures, dates, or other content everywhere it appears, while preserving 100% of the original styling, layout and masters. Different from pptx-builder-skill/docx-builder, which build a NEW document from an HTML design: this skill never touches formatting, it only rewrites the text of an existing file. Trigger on "fill in this template with...", "reuse last year's deck/report for...", "update this pptx/docx with the new client's info", "swap the numbers in this existing presentation/document".
---

# Template Filler

Fills an **existing** `.pptx`/`.docx` template with new content — no HTML,
no Playwright, no design stage. The file already has its branding; this
skill only changes what the file *says*, never how it looks.

**Zero template prep required.** There's no `{{placeholder}}` syntax to
insert first — the agent reads the template's current content and decides
what to change based on what it actually says (e.g. spotting last year's
client name, or last year's figures, wherever they appear).

## Stage 1 — Extract the current content

From this skill's directory, run the extractor matching the template's
format:

```bash
python scripts/extract_pptx.py <template.pptx> work/content_map.json
# or
python scripts/extract_docx.py <template.docx> work/content_map.json
```

This walks the **existing** file (via python-pptx/python-docx, reading the
real object model — not scraping HTML) and produces a flat JSON list of
every text run's current text, each with a stable ID:

- pptx: `{slide}/{shape_id}/{paragraph}/{run}` for text, `{slide}/{shape_id}
  /r{row}c{col}/{paragraph}/{run}` for table cells.
- docx: `{block}/{run}` for body paragraphs, `{block}/r{row}c{col}/{paragraph}
  /{run}` for table cells, `h{section}/{paragraph}/{run}` /
  `f{section}/{paragraph}/{run}` for each section's default header/footer.

Read `work/content_map.json` (it can be large — grep/filter it rather than
dumping the whole thing into context if the template has many slides/pages).
Each entry also carries a `paragraph_text` field: the full text of the
paragraph that run belongs to, because PowerPoint/Word routinely split what
looks like one phrase into several runs (autocorrect, a prior manual edit) —
read `paragraph_text` to understand what a run is really part of, don't
judge a fragment in isolation.

## Stage 2 — Decide what changes, and how

This is where "matching by content" actually happens, and it's the agent's
job, not a script's: read the content map, identify which runs/paragraphs
hold the values that need to change for the new client/project, and note
that a repeated value (a client name, a date) usually appears in **more
than one place** — check the whole map, don't stop at the first match.

Write `work/changes.json` as a list of `{"id": ..., "new_text": ...}`:

- Address a **single run** by its exact ID (`"2/5/0/0"`) when you want
  surgical precision.
- Address a **whole paragraph** by leaving off the trailing run index
  (`"2/5/0"`) when its `paragraph_text` shows it was split into more runs
  than are meaningful — the first run gets the new text, every other run in
  that paragraph is blanked. This is the normal way to replace a phrase that
  autocorrect fragmented, and is usually simpler than reconstructing exact
  run boundaries.
- Never invent an ID that isn't in the content map, and never rely on
  search-and-replace across the raw file — a value like "12" can appear
  as a real figure in one place and something unrelated (a page number, an
  unrelated count) elsewhere; only IDs seen in the content map are safe to
  target.

**Present an old → new diff table to the human and get explicit approval
before running Stage 3.** This mirrors the "approve before compile" rule
the sibling skills already use for HTML designs — nothing gets written
until the human has seen exactly what's about to change.

## Stage 3 — Write the new file

```bash
python scripts/apply_pptx.py <template.pptx> work/changes.json <output.pptx>
# or
python scripts/apply_docx.py <template.docx> work/changes.json <output.docx>
```

Opens the **original** file and writes the new text into only the runs
named in `changes.json`, in place — every other run, every shape,
paragraph, table, and all formatting is left completely untouched. Prints a
warning (not a silent failure) for any change ID it couldn't resolve; read
stderr and fix `changes.json` rather than ignoring it.

## Stage 4 — Verify (never skip this)

Three checks, all required before calling the fill done:

```bash
python scripts/verify_pptx.py <output.pptx>          # or verify_docx.py
python scripts/verify_parity.py <template.pptx> <output.pptx> work/changes.json
python scripts/render_preview.py <output.pptx> work/preview --pages <touched slide/page numbers>
```

- `verify_pptx.py`/`verify_docx.py` catch the same class of structural/XML
  corruption the sibling skills guard against (PowerPoint/Word silently
  "repairing" a file by dropping content, with no Python-side error).
- `verify_parity.py` is specific to this skill: it re-extracts both the
  original and the output and asserts that **every run not in
  `changes.json` is byte-identical text before/after**, every run that *is*
  in `changes.json` shows the expected new text, and slide/paragraph/table
  counts match. This is the direct check against "did the fill silently
  break something else" — treat any FAIL here as a bug, not a warning.
- `render_preview.py` renders the touched slides/pages to PNG — use
  `--pages` with just the numbers that changed, no need to re-render an
  entire 80-slide deck for a 2-slide edit. **Actually look at the rendered
  PNGs with the Read tool** before reporting success — this is the only way
  to catch a pptx text box whose autofit didn't reflow after much longer
  text was written in (a visual issue verify_parity.py cannot see; it only
  checks text, not layout).

## Known limits (design around these, don't fight them)

- **No image/logo swapping** — needs relationship-level (blip) XML surgery
  not implemented in this version. Swap images by hand in PowerPoint/Word
  after the text fill.
- **Only per-slide (pptx) / per-document-body+headers+footers (docx)
  content is addressable** — text inherited from a slide master/layout, or
  from non-default (first-page/even-page) headers/footers, is out of scope.
- **Field-coded runs are read-only** — dates, page numbers, and TOC entries
  built from Word/PowerPoint auto-fields are either invisible to the
  extractor (pptx) or flagged `"field": true` in the content map (docx);
  writing to a flagged run looks successful but Word regenerates it on next
  open, so don't target these IDs.
- **docx table cells** assume simple paragraph/run content — a table
  nested inside another table's cell isn't addressed.
- **Autofit isn't reflowed.** If new text is much longer than the original
  in a pptx text box with shrink-to-fit, the box may look visually off even
  though the write itself is correct — this is exactly why the
  render-preview review step in Stage 4 isn't optional.

## MCP Server Usage

This skill is also available as a standalone MCP server (`template-filler-mcp`)
that exposes the same pipeline as 8 MCP tools — usable by any MCP-compatible
agent (Claude Desktop, VS Code Copilot, etc.) without running shell commands.

### Installation

```bash
pip install -e /path/to/template-filler-skill
```

### MCP Configuration

Add to your MCP client's configuration:

```json
{
  "mcpServers": {
    "template-filler": {
      "command": "template-filler-mcp",
      "args": []
    }
  }
}
```

Or run the server directly:

```bash
python -m template_filler_mcp.server
```

### Available Tools

| Tool | Stage | Description |
|------|-------|-------------|
| `extract_pptx` | Extract | Read all text runs from a .pptx as structured JSON |
| `extract_docx` | Extract | Read all text runs from a .docx as structured JSON |
| `apply_pptx` | Apply | Write changes into specific runs, preserving formatting |
| `apply_docx` | Apply | Same for .docx |
| `verify_pptx` | Verify | Structural/XML integrity check |
| `verify_docx` | Verify | Same for .docx |
| `verify_parity_tool` | Verify | Assert output matches original except changes |
| `render_preview` | Verify | Render slides/pages to PNG (requires LibreOffice) |

### CLI

A standalone CLI is also available with the same tools:

```bash
template-filler extract pptx template.pptx
template-filler apply pptx template.pptx --changes changes.json output.pptx
template-filler verify pptx output.pptx
template-filler parity original.pptx output.pptx --changes changes.json
template-filler render output.pptx preview/ --pages 1,3,5
```

### MCP vs Script Workflow Mapping

| Script (`scripts/`) | MCP Tool | Key Difference |
|---------------------|----------|----------------|
| `extract_pptx.py <in> <out>` | `extract_pptx(template_path)` | Returns dict directly, no file I/O |
| `apply_pptx.py <in> <changes> <out>` | `apply_pptx(template, changes, output)` | `changes` is a list param, not a file |
| `verify_pptx.py <file>` | `verify_pptx(filepath)` | Same |
| `verify_parity.py <orig> <out> <changes>` | `verify_parity_tool(orig, out, changes)` | `changes` is a list param |
| `render_preview.py <file> <dir> --pages` | `render_preview(file, dir, pages)` | Same |

The content_map format, ID scheme, and output semantics are identical between
scripts and MCP tools. An agent can switch between them transparently.

Files in this skill

  • SKILL.md9.4 KB
  • docs/demo-after.png55.6 KB
  • docs/demo-before.png57 KB
  • pyproject.toml860 B
  • requirements.txt63 B
  • scripts/apply_docx.py3.2 KB
  • scripts/apply_pptx.py3.7 KB
  • scripts/extract_docx.py5.3 KB
  • scripts/extract_pptx.py5.1 KB
  • scripts/render_preview.py2.8 KB
  • scripts/verify_docx.py4.7 KB
  • scripts/verify_parity.py5.5 KB
  • scripts/verify_pptx.py4.4 KB
  • template_filler_mcp/__init__.py111 B
  • template_filler_mcp/_apply.py7 KB
  • template_filler_mcp/_extract.py8 KB
  • template_filler_mcp/_parity.py4.7 KB
  • template_filler_mcp/_path_validation.py3.4 KB
  • template_filler_mcp/_render.py3.2 KB
  • template_filler_mcp/_verify.py7.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…