AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Docx Xml

skill-anylegal-ai-anylegal-oss-docx-xml · by anylegal-ai

Advanced — structural OOXML edits via run_code + lxml + zipfile. Use ONLY when the docx-editing tools (edit_document, add_comment, accept/reject, revert_edit) can't express the change. For ordinary text edits, use the docx-editing skill instead.

No reviews yet
0 installs
35 views
0.0% view→install

Install

$ agentstack add skill-anylegal-ai-anylegal-oss-docx-xml

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-anylegal-ai-anylegal-oss-docx-xml)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
4mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Docx Xml? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

DOCX XML — Advanced Skill

You are looking at this skill because a structural edit needs raw OOXML manipulation. Always check first whether edit_document can do it — see the docx-editing skill. Only drop to run_code for:

  • Deleting a clause including its paragraph mark (needs `` pattern)
  • Inserting multi-paragraph content mid-document
  • Editing a part other than word/document.xml (styles.xml, headers/footers, numbering.xml)
  • Bulk structural rewrites spanning dozens of paragraphs

Overview

A .docx file is a ZIP archive containing XML files. The main content is in word/document.xml. The OOXML namespace for Word is http://schemas.openxmlformats.org/wordprocessingml/2006/main (prefix w:).

Use language="python" + lxml + zipfile on the XML parts directly.

Why NOT docx-js: docx-js is write-only — there is no Document.load(...) API. It's for creating new DOCX files from scratch (the draft skill uses it). You cannot use it to load Contract.docx, mutate, and save back.

Why NOT python-docx for run-level mutation: python-docx silently corrupts complex legal templates. It loses non-target glyphs (other placeholders, special characters) when run.text is reassigned, has no reliable API for footnotes / bookmarks / internal hyperlinks / TableOfContents, and collapses ` run properties when rewriting text — losing bold / font / size on surrounding runs. **Never use python-docx for run.text.replace(...)` or any run-level mutation.** python-docx for reading and additive operations (adding images, adding paragraphs) is acceptable.

The right pattern — read XML, mutate with lxml, repack with zipfile

# run_code(language="python", input_files=["Contract.docx"], code=...)
import zipfile, io
from lxml import etree

NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
W = f"{{{NS}}}"

# 1. Read document.xml from the input DOCX.
with zipfile.ZipFile('/sandbox/input/Contract.docx') as zin:
    doc_xml = zin.read('word/document.xml')
    parts = {name: zin.read(name) for name in zin.namelist()}

# 2. Mutate with lxml (example — delete a specific paragraph including its mark).
root = etree.fromstring(doc_xml)
for p in root.iter(f"{W}p"):
    text = "".join(t.text or "" for t in p.iter(f"{W}t"))
    if "text-to-delete" in text:
        p.getparent().remove(p)

# 3. Repack, preserving every other part.
parts['word/document.xml'] = etree.tostring(root, xml_declaration=True,
                                            encoding='UTF-8', standalone=True)
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zout:
    for name, data in parts.items():
        zout.writestr(name, data)

with open('/sandbox/output/Contract_Edited.docx', 'wb') as f:
    f.write(buf.getvalue())

One document = one run_code call. A single call should open the file, make all changes, and save one output. Never split a document edit across multiple scripts.

OOXML XML Reference

Document Structure

word/document.xml
  
                        ← paragraph
                      ← paragraph properties (style, numbering, spacing)
        
        ...
        ...
        ...
        ...
        ...  ← always last child of pPr
      
                        ← run (contiguous text with same formatting)
                      ← run properties (bold, italic, font, size)
                       ← bold
                       ← italic
           ← font size in half-points (24 = 12pt)
          
        
        text content
      
    

Schema-compliance rules

| Rule | What goes wrong if ignored | |---|---| | ` with leading or trailing whitespace must carry xml:space="preserve" | Word strips the spaces during round-trip | | Inside , deleted text content lives in , not | Tracked changes corrupt; Accept All leaves stale text | | is always the last child of | Schema violation; some viewers reject the document | | When wrapping a run in tracked-change markup, copy the original into your / runs | The change preserves bold / font / size of surrounding text | | When creating a tracked change, replace the entire element rather than injecting markup inside it | Avoids invalid run nesting and preserves Word's display logic | | Every w:id in tracked changes is unique | Word silently merges or drops changes with duplicate IDs | | xml:space="preserve"` is the most-missed of these rules — most run-level edits need it | Spaces disappearing is invisible in source diff but obvious to the user |

Smart quotes (typography)

DOCX documents use smart quotes stored as Unicode. When adding text via lxml, use these:

| Code | Char | Description | |------|------|-------------| | | ' | Left single quote | | | ' | Right single quote / apostrophe | | | " | Left double quote | | | " | Right double quote |

In lxml: elem.text = 'Here’s a quote: “Hello”'

Tracked changes XML patterns

Insertion:


  ...inserted text

Deletion:


  ...deleted text

Minimal edit — only mark what changes (governing law change from Delaware to England and Wales):

This Agreement is governed by the laws of 

  the State of Delaware

  England and Wales

.

Deleting an entire paragraph — mark the paragraph mark as deleted too, otherwise "Accept All" leaves an empty line:


  
    
      
    
  
  
    The deleted clause text...
  

Rejecting a counterparty's insertion — nest your deletion inside their insertion (preserves their authorship in the audit trail):


  
    their inserted text
  

Restoring a counterparty's deletion — add an insertion after their deletion (do not modify their deletion):


  deleted text

  deleted text

Images (additive — python-docx is acceptable here)

Inserting images is an additive operation; python-docx handles relationships and content types correctly:

from docx import Document
from docx.shared import Inches, Cm

doc = Document('/sandbox/input/contract.docx')
doc.add_picture('/sandbox/input/logo.png', width=Inches(2.0))

# Or in a specific paragraph:
para = doc.paragraphs[0]
run = para.add_run()
run.add_picture('/sandbox/input/signature.png', width=Cm(5), height=Cm(2))

doc.save('/sandbox/output/contract_with_images.docx')

This is the only place python-docx is the right tool. For text mutation, always use lxml on the raw XML.

If you need raw XML control (custom positioning, wrapping):


  
      
    
    
      
        
          
            
          
          
            
          
        
      
    
  

To add via raw XML you must also (1) copy the image to word/media/, (2) add a relationship in word/_rels/document.xml.rels, and (3) register the content type in [Content_Types].xml.

Common pitfalls

  • Modifying during iteration: when removing elements with lxml findall(), iterate over a copy: for elem in list(body.findall(...)).
  • Namespace-qualified attribute access: use the full URI — elem.get('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}id'), not elem.get('w:id').
  • Missing paragraph mark deletion: if you delete all text from a paragraph but don't mark the paragraph mark as deleted, "Accept All" leaves blank lines.
  • Stale element references: after removing/moving elements, cached references may be invalid. Re-query after structural changes.
  • ID uniqueness: every w:id in tracked changes must be unique. Use max(existing_ids) + 1.

Validation

When run_code produces a DOCX file, validate it before reporting success. The most common validation failures and their fixes:

| Failure | Fix | |---|---| | ` inside | Use for deleted text, not | | Missing xml:space="preserve" | Add the attribute to elements that have leading/trailing whitespace | | Corrupt ZIP | Ensure zipfile.ZipFile(..., 'w') is properly closed before reading the bytes back | | Schema violation in | Verify element order: , , , , , ` (rPr last) |

If the script produces a corrupt DOCX, fix the Python code and retry rather than retrying the same code.

Data + spreadsheet recipes (when run_code is the right tool for non-DOCX work)

Generate an Excel fee schedule

import openpyxl
from openpyxl.styles import Font, PatternFill

wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Fee Schedule"

headers = ['Service', 'Rate (USD/hr)', 'Estimated Hours', 'Total']
for col, header in enumerate(headers, 1):
    cell = ws.cell(row=1, column=col, value=header)
    cell.font = Font(bold=True, color="FFFFFF")
    cell.fill = PatternFill(start_color="4472C4", fill_type="solid")

services = [
    ('Contract Review', 350, 8),
    ('Negotiation Support', 400, 12),
    ('Due Diligence', 375, 20),
]
for row, (service, rate, hours) in enumerate(services, 2):
    ws.cell(row=row, column=1, value=service)
    ws.cell(row=row, column=2, value=rate)
    ws.cell(row=row, column=3, value=hours)
    ws.cell(row=row, column=4, value=rate * hours)

ws.column_dimensions['A'].width = 25
wb.save('/sandbox/output/fee_schedule.xlsx')

Calculate business day deadlines

from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta

def add_business_days(start, days):
    current = start
    added = 0
    while added < days:
        current += timedelta(days=1)
        if current.weekday() < 5:
            added += 1
    return current

signing_date = datetime(2026, 5, 4)
deadlines = {
    "Signing Date": signing_date.strftime("%Y-%m-%d"),
    "Effective Date (T+5 business days)": add_business_days(signing_date, 5).strftime("%Y-%m-%d"),
    "First Payment Due (30 days)": (signing_date + timedelta(days=30)).strftime("%Y-%m-%d"),
    "Warranty Period Ends (12 months)": (signing_date + relativedelta(months=12)).strftime("%Y-%m-%d"),
}
for name, date in deadlines.items():
    print(f"{name}: {date}")

Source & license

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

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

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.