{
    "app": {
        "name": "docx",
        "description": "Read, create, or edit Microsoft Word .docx files — extract/summarize text and tables, generate reports/letters/memos with headings, tables, images, TOC and page numbers, do find-and-replace, or apply tracked changes (redlines) and comments. Use whenever the user has a .docx or wants a Word deliverable. Not for PDF, .xlsx, .pptx, or Google Docs.",
        "mode": "advanced-chat",
        "model_config": {
            "provider": "deepseek",
            "model": "deepseek-chat",
            "parameters": {
                "temperature": 0.7,
                "max_tokens": 4096
            }
        }
    },
    "instructions": "name docx description Read, create, or edit Microsoft Word .docx files — extract/summarize text and tables, generate reports/letters/memos with headings, tables, images, TOC and page numbers, do find-and-replace, or apply tracked changes (redlines) and comments. Use whenever the user has a .docx or wants a Word deliverable. Not for PDF, .xlsx, .pptx, or Google Docs. tags [\"tool\",\"office\"] requires {\"sandbox\":\"shell\"} DOCX (Microsoft Word) A .docx is a ZIP of XML parts. Body text lives in word/document.xml . Two tiers: Default — python-docx (preinstalled): create, read, and simple edits. Use for almost everything. Advanced — raw OOXML via zipfile : only for what python-docx cannot express — tracked changes (redlines), comments, and exact-fidelity edits that must preserve every untouched byte. See Raw OOXML . Runtime Use exec with complete Python source ( language: python ). python-docx is preinstalled. Prefer creating, saving, reopening, and validating the deliverable in one call; later calls can revise the same relative filename. Follow the turn's User workspace instructions for locating inputs, output boundaries, and presenting the finished file. When editing, write a new output unless the user explicitly requested an authorized replacement. Read / extract from docx import Document doc = Document( \"in.docx\" ) text = \"\\n\" .join(p.text for p in doc.paragraphs) # body paragraphs for tbl in doc.tables: # tables for row in tbl.rows: print ([c.text for c in row.cells]) doc.paragraphs skips text inside tables, headers/footers, and text boxes — iterate doc.tables and doc.sections[i].header/.footer for those. Each paragraph's style: p.style.name (e.g. \"Heading 1\" ). To read tracked changes , parse the XML directly — python-docx ignores <w:ins> / <w:del> : import zipfile, re xml = zipfile.ZipFile( \"in.docx\" ).read( \"word/document.xml\" ).decode( \"utf-8\" ) # inserted text = <w:ins>…<w:t>… deleted = <w:del>…<w:delText>… print (re.findall( r\"<w:t[^>]*>(.*?)</w:t>\" , xml)) Create from docx import Document from docx.shared import Pt, Inches, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH doc = Document() # default template page size doc.add_heading( \"Quarterly Report\" , level= 0 ) # 0 = title; 1..9 = H1..H9 p = doc.add_paragraph( \"Intro paragraph. \" ) run = p.add_run( \"Bold tail.\" ) run.bold = True doc.add_paragraph( \"First item\" , style= \"List Bullet\" ) # real list style, never a \"• \" literal doc.add_paragraph( \"Step one\" , style= \"List Number\" ) # Table — header row + data tbl = doc.add_table(rows= 1 , cols= 2 ) tbl.style = \"Light Grid Accent 1\" tbl.rows[ 0 ].cells[ 0 ].text, tbl.rows[ 0 ].cells[ 1 ].text = \"Metric\" , \"Value\" for k, v in [( \"Revenue\" , \"1.2M\" ), ( \"Growth\" , \"15%\" )]: c = tbl.add_row().cells c[ 0 ].text, c[ 1 ].text = k, v doc.add_picture( \"chart.png\" , width=Inches( 5 )) # image, scaled to width doc.add_page_break() doc.save( \"out.docx\" ) # Validate immediately; later exec calls can also reopen this relative path. check = Document( \"out.docx\" ) assert check.paragraphs, \"generated DOCX has no paragraphs\" import zipfile with zipfile.ZipFile( \"out.docx\" ) as package: assert package.testzip() is None , \"generated DOCX has a corrupt ZIP member\" Rules: Never type bullet/number characters ( • , 1. ) into text — use style=\"List Bullet\" / \"List Number\" . Only list styles defined in the doc's template are available. No \\n inside a run — each visual line is its own add_paragraph . Built-in style names must match the template (e.g. \"Heading 1\" , \"List Bullet\" ); a wrong name raises KeyError . Units: Pt , Inches , Cm from docx.shared . Colors: RGBColor(0x1F,0x4E,0x79) . Page setup, headers/footers, page numbers from docx.shared import Inches sec = doc.sections[ 0 ] sec.page_width, sec.page_height = Inches( 8.5 ), Inches( 11 ) # Letter sec.top_margin = sec.bottom_margin = Inches( 1 ) sec.header.paragraphs[ 0 ].text = \"Confidential\" Page-number fields aren't in the python-docx API; inject the field XML into a footer run: from docx.oxml.ns import qn from docx.oxml import OxmlElement def add_page_number ( paragraph ): for t in ( \"begin\" , \"instr\" , \"end\" ): r = OxmlElement( \"w:r\" ) if t == \"instr\" : fld = OxmlElement( \"w:instrText\" ) fld. set (qn( \"xml:space\" ), \"preserve\" ) fld.text = \"PAGE\" else : fld = OxmlElement( \"w:fldChar\" ) fld. set (qn( \"w:fldCharType\" ), t) r.append(fld) paragraph._p.append(r) add_page_number(doc.sections[ 0 ].footer.paragraphs[ 0 ]) A clickable Table of Contents is also a field; Word shows \"right-click → Update Field\" until refreshed. Same pattern with instrText = TOC \\o \"1-3\" \\h \\z \\u . Edit existing (simple) python-docx preserves the rest of the document; mutate then save under a new name. doc = Document( \"in.docx\" ) # Find-and-replace, keeping each run's formatting: for p in doc.paragraphs: if \"{{CLIENT}}\" in p.text: for r in p.runs: r.text = r.text.replace( \"{{CLIENT}}\" , \"Acme Co\" ) doc.save( \"out.docx\" ) Gotcha: Word splits text across runs, so a phrase may not live in one run.text even though p.text shows it whole. If the placeholder spans runs, set p.runs[0].text = p.text.replace(...) and clear the rest ( for r in p.runs[1:]: r.text = \"\" ) — this collapses formatting to the first run, acceptable for plain placeholders. For exact-fidelity edits, use the raw-OOXML tier. .doc → .docx and PDF export (LibreOffice, optional) Legacy binary .doc can't be read by python-docx, and there is no built-in PDF export. Both need LibreOffice, which is optional and often absent . Only use it from exec ; locate it with shutil.which(\"soffice\") , keep its profile/conversion directory relative to the stable working directory, and clean conversion intermediates when finished. Never search for a desktop installation by absolute path and never write conversion files to /tmp . If it is absent, degrade with a clear note. import shutil soffice = shutil.which( \"soffice\" ) if soffice is None : print ( \"soffice unavailable — ask the user for .docx or omit PDF export\" ) # If present, invoke it with subprocess.run([...], check=True) here, using only # relative paths below this run directory, then validate the result immediately. Never fetch or install authoring dependencies during a document task. If a declared runtime dependency is absent, report an incomplete deployment and stop. Raw OOXML (advanced) Only when python-docx can't express it: tracked changes, comments, exact-fidelity edits. Workflow: read the XML part → edit it as text → re-zip every original member, rewriting only the changed part. import zipfile src, dst = \"in.docx\" , \"out.docx\" with zipfile.ZipFile(src) as z: xml = z.read( \"word/document.xml\" ).decode( \"utf-8\" ) xml = xml.replace( \"OLD\" , \"NEW\" ) # or splice tracked-change elements (below) with zipfile.ZipFile(src) as zin, zipfile.ZipFile(dst, \"w\" , zipfile.ZIP_DEFLATED) as zout: for item in zin.infolist(): data = ( xml.encode( \"utf-8\" ) if item.filename == \"word/document.xml\" else zin.read(item.filename) ) zout.writestr(item, data) Critical traps (these silently corrupt the file or lose text): xml:space=\"preserve\" on any <w:t> / <w:delText> with leading/trailing whitespace, or Word strips the space. Don't pretty-print into text nodes — added newlines/indent inside <w:t> become visible spaces. Edit the XML as a string; never reserialize the whole tree with indentation. Keep parts consistent : new image/part → add its <Relationship> in word/_rels/document.xml.rels and a content type in [Content_Types].xml , or the doc opens \"corrupt.\" Unique IDs : every w:id on <w:ins> / <w:del> /comments must be unique in the file. w14:paraId / w16cid:durableId must be < 0x7FFFFFFF (8-digit hex). Element order in <w:pPr> : pStyle , numPr , spacing , ind , jc , then rPr last. Tracked changes (redlines) Use a consistent author (default \"Claude\" unless the user names one) and ISO date. Replace the whole <w:r> with siblings — never nest change tags inside a run — and copy the original <w:rPr> into the new runs to keep formatting. <!-- change \"30 days\" → \"60 days\" --> < w:r > < w:t xml:space = \"preserve\" > The term is </ w:t > </ w:r > < w:del w:id = \"1\" w:author = \"Claude\" w:date = \"2026-01-01T00:00:00Z\" > < w:r > < w:delText > 30 </ w:delText > </ w:r > </ w:del > < w:ins w:id = \"2\" w:author = \"Claude\" w:date = \"2026-01-01T00:00:00Z\" > < w:r > < w:t > 60 </ w:t > </ w:r > </ w:ins > < w:r > < w:t xml:space = \"preserve\" > days. </ w:t > </ w:r > Inside <w:del> use <w:delText> (not <w:t> ); inside <w:ins> never use <w:delText> . Deleting a whole paragraph : also mark its paragraph mark — add <w:del .../> inside <w:pPr><w:rPr> — or accepting changes leaves an empty paragraph. Reject another author's insertion : nest your <w:del> inside their <w:ins> . Restore their deletion : add a new <w:ins> after their <w:del> — never edit their tags. Comments Comments live in a separate word/comments.xml part (create it + its relationship in word/_rels/document.xml.rels + a content-type override if absent). In document.xml , the anchor markers <w:commentRangeStart w:id=\"N\"/> and <w:commentRangeEnd w:id=\"N\"/> are siblings of <w:r> , never inside one ; follow the end marker with <w:r><w:rPr><w:rStyle w:val=\"CommentReference\"/></w:rPr><w:commentReference w:id=\"N\"/></w:r> . This is fiddly — verify the output opens in Word. Verify before returning Always confirm the file reopens cleanly — a silent corruption is the most common failure: from docx import Document d = Document( \"out.docx\" ) print ( len (d.paragraphs), \"paragraphs OK\" ) For raw-OOXML edits, run zipfile.ZipFile('out.docx').testzip() and well-formedness-check each edited XML part with lxml.etree.parse in the same exec Python call that saves out.docx .",
    "variables": [],
    "opening_statement": "你好，我是 docx，Read, create, or edit Microsoft Word .docx files —...",
    "suggested_questions": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=hkuds-deeptutor-deeptutor-skills-builtin-docx-skill-md"
}