Install
$ agentstack add skill-jiplet-transformation-os-documents-docx ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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 Used
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.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
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 →About
DOCX creation, editing, and analysis
CRITICAL: Use python-docx only
Never use docx-js (JavaScript) for creating new documents. docx-js output will not open in Word for Mac. Always use python-docx via the project venv:
source /.venv/bin/activate && python3 - /Template/logo.png`
2. **Report title** — large, navy text (~24pt bold)
3. **Subtitle / date** — Bright Blue text (~12pt)
4. **Blue rule divider** — Bright Blue horizontal line (paragraph bottom border)
5. **Synopsis** — max 2 plain-English sentences stating the hypothesis and approach. No AI-sounding language ("this analysis leverages...", "comprehensive review..."). Write like you're briefing a project director over coffee. Follow with dot-point key findings.
6. **Assumptions** — clearly listed. Every assumption must be verified with the user during planning. If unverified, prefix with "Unverified:". Never assume silently.
7. **How to Read This Document** — list each section with a one-line description of what it contains and what to look for. This is for stakeholders receiving the handover.
```python
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
LOGO_PATH = "/Template/logo.png"
NAVY = RGBColor(0x0B, 0x32, 0x54)
BLUE = RGBColor(0x13, 0xB5, 0xEA)
doc = Document()
# --- Default font ---
style = doc.styles['Normal']
style.font.name = 'Arial'
style.font.size = Pt(10.5)
# --- Page margins ---
for section in doc.sections:
section.top_margin = Cm(2.5)
section.bottom_margin = Cm(2.5)
section.left_margin = Cm(2.5)
section.right_margin = Cm(2.5)
# --- 1. Logo ---
doc.add_picture(LOGO_PATH, width=Cm(3))
# --- 2. Report title (navy, 24pt bold) ---
title_para = doc.add_paragraph()
title_run = title_para.add_run("Report Title Here")
title_run.bold = True
title_run.font.size = Pt(24)
title_run.font.color.rgb = NAVY
title_run.font.name = 'Arial'
# --- 3. Subtitle / date (Bright Blue, 12pt) ---
sub_para = doc.add_paragraph()
sub_run = sub_para.add_run("Subtitle or date here")
sub_run.font.size = Pt(12)
sub_run.font.color.rgb = BLUE
sub_run.font.name = 'Arial'
# --- 4. Blue rule divider (paragraph bottom border) ---
def add_bottom_border(paragraph, color="13B5EA", size="6"):
pPr = paragraph._p.get_or_add_pPr()
pBdr = OxmlElement('w:pBdr')
bottom = OxmlElement('w:bottom')
bottom.set(qn('w:val'), 'single')
bottom.set(qn('w:sz'), size)
bottom.set(qn('w:color'), color)
bottom.set(qn('w:space'), '1')
pBdr.append(bottom)
pPr.append(pBdr)
rule_para = doc.add_paragraph()
add_bottom_border(rule_para)
# --- 5. Synopsis ---
syn_heading = doc.add_paragraph()
syn_run = syn_heading.add_run("Synopsis")
syn_run.bold = True
syn_run.font.size = Pt(12)
syn_run.font.color.rgb = NAVY
doc.add_paragraph("Plain-English synopsis here — 1-2 sentences, then key findings below.")
# Add bullet findings using doc.add_paragraph("Finding text", style='List Bullet')
# --- 6. Assumptions ---
asm_heading = doc.add_paragraph()
asm_run = asm_heading.add_run("Assumptions")
asm_run.bold = True
asm_run.font.size = Pt(12)
asm_run.font.color.rgb = NAVY
# --- 7. How to Read This Document ---
htr_heading = doc.add_paragraph()
htr_run = htr_heading.add_run("How to Read This Document")
htr_run.bold = True
htr_run.font.size = Pt(12)
htr_run.font.color.rgb = NAVY
# --- Page break after cover ---
doc.add_page_break()
# ... body content follows ...
doc.save("output.docx")
Table Formatting Standards
| Element | Style | |---|---| | Header row | Dark Navy (0B3254) fill, White text, 11pt bold | | Body rows | Alternating White / Light Grey (E6EAEE), 10pt regular | | Text columns | Left-aligned | | Number columns | Right-aligned | | Totals / key rows | Bright Blue (13B5EA) fill, White text | | Borders | No vertical borders; thin horizontal Light Grey between rows |
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
from docx.shared import Pt, RGBColor
def set_cell_shading(cell, fill_hex):
"""Set cell background colour. Use hex string without #."""
shading = OxmlElement('w:shd')
shading.set(qn('w:fill'), fill_hex)
shading.set(qn('w:val'), 'clear')
cell._tc.get_or_add_tcPr().append(shading)
def style_header_row(row):
"""Navy background, white bold text for header row."""
for cell in row.cells:
set_cell_shading(cell, '0B3254')
for paragraph in cell.paragraphs:
for run in paragraph.runs:
run.bold = True
run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
run.font.size = Pt(11)
run.font.name = 'Arial'
def style_data_rows(table, start_row=1):
"""Alternating white/light grey rows, 10pt text."""
for i, row in enumerate(table.rows[start_row:], start=start_row):
fill = 'FFFFFF' if (i - start_row) % 2 == 0 else 'E6EAEE'
for cell in row.cells:
set_cell_shading(cell, fill)
for paragraph in cell.paragraphs:
for run in paragraph.runs:
run.font.size = Pt(10)
run.font.name = 'Arial'
# Usage:
table = doc.add_table(rows=4, cols=3)
style_header_row(table.rows[0])
style_data_rows(table)
Tone Guidance
- Write like you're briefing a project director over coffee
- No AI-sounding language: avoid "leverages", "comprehensive", "robust", "cutting-edge"
- Never use em dashes (—). Use a colon, comma, or rewrite the sentence instead.
- Surface the "so what" first, then the detail
- Bullet points and tables over prose for structured information
- Positive variance → Green (
009946). Negative variance → Purple (95358C).
Overview
A .docx file is a ZIP archive containing XML files.
Quick Reference
| Task | Approach | |------|----------| | Read/analyze content | pandoc or unpack for raw XML | | Create new document | Use python-docx (NEVER docx-js) - see Creating New Documents below | | Edit existing document | Unpack → edit XML → repack - see Editing Existing Documents below |
Converting .doc to .docx
Legacy .doc files must be converted before editing:
python scripts/office/soffice.py --headless --convert-to docx document.doc
Reading Content
# Text extraction with tracked changes
pandoc --track-changes=all document.docx -o output.md
# Raw XML access
python scripts/office/unpack.py document.docx unpacked/
Converting to Images
python scripts/office/soffice.py --headless --convert-to pdf document.docx
pdftoppm -jpeg -r 150 document.pdf page
Accepting Tracked Changes
To produce a clean document with all tracked changes accepted (requires LibreOffice):
python scripts/accept_changes.py input.docx output.docx
Creating New Documents
Always use python-docx. Never use docx-js — output will not open in Word for Mac.
Run via the project venv as an inline heredoc:
source /.venv/bin/activate && python3 - Here’s a quote: “Hello”
| Entity | Character | |--------|-----------| | ‘ | ‘ (left single) | | ’ | ’ (right single / apostrophe) | | “ | “ (left double) | | ” | ” (right double) |
Adding comments: Use comment.py to handle boilerplate across multiple XML files (text must be pre-escaped XML):
python scripts/comment.py unpacked/ 0 "Comment text with & and ’"
python scripts/comment.py unpacked/ 1 "Reply text" --parent 0 # reply to comment 0
python scripts/comment.py unpacked/ 0 "Text" --author "Custom Author" # custom author name
Then add markers to document.xml (see Comments in XML Reference).
Step 3: Pack
python scripts/office/pack.py unpacked/ output.docx --original document.docx
Validates with auto-repair, condenses XML, and creates DOCX. Use --validate false to skip.
Auto-repair will fix:
durableId>= 0x7FFFFFFF (regenerates valid ID)- Missing
xml:space="preserve"on `` with whitespace
Auto-repair won't fix:
- Malformed XML, invalid element nesting, missing relationships, schema violations
Common Pitfalls
- **Replace entire `
elements**: When adding tracked changes, replace the whole...block with......` as siblings. Don't inject tracked change tags inside a run. - **Preserve `
formatting**: Copy the original run's` block into your tracked change runs to maintain bold, font size, etc.
XML Reference
Schema Compliance
- **Element order in `
**:,,,,,` last - Whitespace: Add
xml:space="preserve"to `` with leading/trailing spaces - RSIDs: Must be 8-digit hex (e.g.,
00AB1234)
Tracked Changes
Insertion:
inserted text
Deletion:
deleted text
**Inside `**: Use instead of , and instead of `.
Minimal edits - only mark what changes:
The term is
30
60
days.
Deleting entire paragraphs/list items - when removing ALL content from a paragraph, also mark the paragraph mark as deleted so it merges with the next paragraph. Add ` inside `:
...
Entire paragraph content being deleted...
Without the ` in `, accepting changes leaves an empty paragraph/list item.
Rejecting another author's insertion - nest deletion inside their insertion:
their inserted text
Restoring another author's deletion - add insertion after (don't modify their deletion):
deleted text
deleted text
Comments
After running comment.py (see Step 2), add markers to document.xml. For replies, use --parent flag and nest markers inside the parent's.
CRITICAL: ` and are siblings of , never inside `.
deleted
more text
text
Images
- Add image file to
word/media/ - Add relationship to
word/_rels/document.xml.rels:
- Add content type to
[Content_Types].xml:
- Reference in document.xml:
Dependencies
- pandoc: Text extraction
- docx:
npm install -g docx(new documents) - LibreOffice: PDF conversion (auto-configured for sandboxed environments via
scripts/office/soffice.py) - Poppler:
pdftoppmfor images
Wiki Compile (post-delivery)
After delivering the output, compile durable findings to the Knowledge Wiki. Read context/wiki-compile-step.md for the full checklist. Skip if the output is formatting-only or contains no new findings (apply the "so what" test).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Jiplet
- Source: Jiplet/transformation-os
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.