agentsclimarketplace

Python docx

Skill cxcscmu/SkillLearnBench/skills/b1-one-shot-claude-opus-4-6/offer-letter-generator/python-docx

[COLM'26] SkillLearnBench is the first benchmark for evaluating continual learning methods that automatically generate agent skills.

Install
npx -y skills add cxcscmu/SkillLearnBench --skill python-docx

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

Manipulate Word .docx files with python-docx, including handling split placeholders across runs and conditional sections.

SKILL.md

3.0 KB, as published. Nobody here has run it

python-docx: Word Document Manipulation

Installation

pip install python-docx

Core Concepts

Split Placeholders Across Runs

Word often splits text into multiple XML runs due to spell-check, formatting changes, or editing history. A placeholder like {{COMPANY_NAME}} may be stored as separate runs: ['{{COMP', 'ANY_NAME}}'].

Solution: Join all run texts in a paragraph, perform replacements on the joined text, then redistribute back to runs (clearing extra runs).

import re
from docx import Document

def replace_in_paragraph(paragraph, replacements):
    """Replace placeholders in a paragraph, handling split runs."""
    full_text = ''.join(run.text for run in paragraph.runs)
    if not any(key in full_text for key in replacements):
        return
    for key, value in replacements.items():
        full_text = full_text.replace(key, value)
    # Put all text in the first run, clear the rest
    if paragraph.runs:
        paragraph.runs[0].text = full_text
        for run in paragraph.runs[1:]:
            run.text = ''

Replacing in All Document Locations

Documents have paragraphs in the body, tables (including nested tables), headers, and footers.

def replace_all_in_doc(doc, replacements):
    # Body paragraphs
    for p in doc.paragraphs:
        replace_in_paragraph(p, replacements)

    # Tables (including nested)
    def process_table(table):
        for row in table.rows:
            for cell in row.cells:
                for p in cell.paragraphs:
                    replace_in_paragraph(p, replacements)
                for nested in cell.tables:
                    process_table(nested)

    for table in doc.tables:
        process_table(table)

    # Headers and footers
    for section in doc.sections:
        for p in section.header.paragraphs:
            replace_in_paragraph(p, replacements)
        for p in section.footer.paragraphs:
            replace_in_paragraph(p, replacements)

Conditional Sections

For {{IF_X}}...{{END_IF_X}} patterns:

  • If condition is true: remove the markers, keep the content
  • If condition is false: remove markers and content entirely
def handle_conditional(paragraph, tag, keep):
    full_text = ''.join(run.text for run in paragraph.runs)
    start_tag = '{{IF_' + tag + '}}'
    end_tag = '{{END_IF_' + tag + '}}'
    if start_tag in full_text:
        if keep:
            full_text = full_text.replace(start_tag, '').replace(end_tag, '')
        else:
            # Remove everything between and including tags
            pattern = re.escape(start_tag) + '.*?' + re.escape(end_tag)
            full_text = re.sub(pattern, '', full_text)
        if paragraph.runs:
            paragraph.runs[0].text = full_text
            for run in paragraph.runs[1:]:
                run.text = ''

Saving

doc.save('output.docx')

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.