wormclaude 1.0.119 → 1.0.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/dist/theme.js +1 -1
  2. package/dist/tui.js +6 -1
  3. package/package.json +1 -1
  4. package/skills/build-mcp-app/SKILL.md +0 -393
  5. package/skills/build-mcp-app/references/abuse-protection.md +0 -60
  6. package/skills/build-mcp-app/references/apps-sdk-messages.md +0 -227
  7. package/skills/build-mcp-app/references/directory-checklist.md +0 -18
  8. package/skills/build-mcp-app/references/iframe-sandbox.md +0 -164
  9. package/skills/build-mcp-app/references/payload-budgeting.md +0 -54
  10. package/skills/build-mcp-app/references/widget-templates.md +0 -249
  11. package/skills/build-mcp-server/SKILL.md +0 -222
  12. package/skills/build-mcp-server/references/auth.md +0 -108
  13. package/skills/build-mcp-server/references/deploy-cloudflare-workers.md +0 -106
  14. package/skills/build-mcp-server/references/elicitation.md +0 -129
  15. package/skills/build-mcp-server/references/remote-http-scaffold.md +0 -211
  16. package/skills/build-mcp-server/references/resources-and-prompts.md +0 -122
  17. package/skills/build-mcp-server/references/server-capabilities.md +0 -164
  18. package/skills/build-mcp-server/references/tool-design.md +0 -189
  19. package/skills/build-mcp-server/references/versions.md +0 -25
  20. package/skills/build-mcpb/SKILL.md +0 -200
  21. package/skills/build-mcpb/references/local-security.md +0 -149
  22. package/skills/build-mcpb/references/manifest-schema.md +0 -156
  23. package/skills/docx/script/__init__.py +0 -1
  24. package/skills/docx/script/accept_chages.py +0 -135
  25. package/skills/docx/script/comment.py +0 -318
  26. package/skills/docx/script/office/helpers/__init__.py +0 -0
  27. package/skills/docx/script/office/helpers/merge_runs.py +0 -199
  28. package/skills/docx/script/office/helpers/simplify_redlines.py +0 -197
  29. package/skills/docx/script/office/pack.py +0 -159
  30. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  31. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  32. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  33. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  34. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  35. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  36. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  37. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  38. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  39. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  40. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  41. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  42. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  43. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  44. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  45. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  46. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  47. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  48. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  49. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  50. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  51. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  52. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  53. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  54. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  55. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  56. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  57. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  58. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  59. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  60. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  61. package/skills/docx/script/office/schemas/mce/mc.xsd +0 -75
  62. package/skills/docx/script/office/schemas/microsoft/wml-2010.xsd +0 -560
  63. package/skills/docx/script/office/schemas/microsoft/wml-2012.xsd +0 -67
  64. package/skills/docx/script/office/schemas/microsoft/wml-2018.xsd +0 -14
  65. package/skills/docx/script/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
  66. package/skills/docx/script/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
  67. package/skills/docx/script/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  68. package/skills/docx/script/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
  69. package/skills/docx/script/office/soffice.py +0 -183
  70. package/skills/docx/script/office/unpack.py +0 -132
  71. package/skills/docx/script/office/validate.py +0 -117
  72. package/skills/docx/script/office/validators/__init__.py +0 -15
  73. package/skills/docx/script/office/validators/base.py +0 -851
  74. package/skills/docx/script/office/validators/docx.py +0 -446
  75. package/skills/docx/script/office/validators/pptx.py +0 -275
  76. package/skills/docx/script/office/validators/redlining.py +0 -247
  77. package/skills/docx/script/templates/comments.xml +0 -3
  78. package/skills/docx/script/templates/commentsExtended.xml +0 -3
  79. package/skills/docx/script/templates/commentsExtensible.xml +0 -3
  80. package/skills/docx/script/templates/commentsIds.xml +0 -3
  81. package/skills/docx/script/templates/people.xml +0 -3
  82. package/skills/docx/skill.md +0 -593
  83. package/skills/explain.md +0 -14
  84. package/skills/frontend-design/SKILL.md +0 -42
  85. package/skills/pdf/FORMS.md +0 -294
  86. package/skills/pdf/REFERENCE.md +0 -612
  87. package/skills/pdf/SKILL.md +0 -314
  88. package/skills/pdf/scripts/check_bounding_boxes.py +0 -65
  89. package/skills/pdf/scripts/check_fillable_fields.py +0 -11
  90. package/skills/pdf/scripts/convert_pdf_to_images.py +0 -33
  91. package/skills/pdf/scripts/create_validation_image.py +0 -37
  92. package/skills/pdf/scripts/extract_form_field_info.py +0 -122
  93. package/skills/pdf/scripts/extract_form_structure.py +0 -115
  94. package/skills/pdf/scripts/fill_fillable_fields.py +0 -98
  95. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -107
  96. package/skills/playground/SKILL.md +0 -77
  97. package/skills/playground/templates/code-map.md +0 -158
  98. package/skills/playground/templates/concept-map.md +0 -73
  99. package/skills/playground/templates/data-explorer.md +0 -67
  100. package/skills/playground/templates/design-playground.md +0 -67
  101. package/skills/playground/templates/diff-review.md +0 -179
  102. package/skills/playground/templates/document-critique.md +0 -171
  103. package/skills/pptx/SKILL.md +0 -230
  104. package/skills/pptx/editing.md +0 -205
  105. package/skills/pptx/pptxgenjs.md +0 -437
  106. package/skills/pptx/scripts/__init__.py +0 -0
  107. package/skills/pptx/scripts/add_slide.py +0 -195
  108. package/skills/pptx/scripts/clean.py +0 -286
  109. package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  110. package/skills/pptx/scripts/office/helpers/merge_runs.py +0 -199
  111. package/skills/pptx/scripts/office/helpers/simplify_redlines.py +0 -197
  112. package/skills/pptx/scripts/office/pack.py +0 -159
  113. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  114. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  115. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  116. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  117. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  118. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  119. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  120. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  121. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  122. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  123. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  124. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  125. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  126. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  127. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  128. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  129. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  130. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  131. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  132. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  133. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  134. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  135. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  136. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  137. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  138. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  139. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  140. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  141. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  142. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  143. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  144. package/skills/pptx/scripts/office/schemas/mce/mc.xsd +0 -75
  145. package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
  146. package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
  147. package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
  148. package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
  149. package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
  150. package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  151. package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
  152. package/skills/pptx/scripts/office/soffice.py +0 -183
  153. package/skills/pptx/scripts/office/unpack.py +0 -132
  154. package/skills/pptx/scripts/office/validate.py +0 -117
  155. package/skills/pptx/scripts/office/validators/__init__.py +0 -15
  156. package/skills/pptx/scripts/office/validators/base.py +0 -851
  157. package/skills/pptx/scripts/office/validators/docx.py +0 -446
  158. package/skills/pptx/scripts/office/validators/pptx.py +0 -275
  159. package/skills/pptx/scripts/office/validators/redlining.py +0 -247
  160. package/skills/pptx/scripts/thumbnail.py +0 -289
  161. package/skills/recon.md +0 -16
  162. package/skills/security-audit/SKILL.md +0 -26
  163. package/skills/talent-creator/SKILL.md +0 -486
  164. package/skills/talent-creator/agents/analyzer.md +0 -274
  165. package/skills/talent-creator/agents/comparator.md +0 -202
  166. package/skills/talent-creator/agents/grader.md +0 -223
  167. package/skills/talent-creator/assets/eval_review.html +0 -146
  168. package/skills/talent-creator/eval-viewer/generate_review.py +0 -471
  169. package/skills/talent-creator/eval-viewer/viewer.html +0 -1325
  170. package/skills/talent-creator/references/schemas.md +0 -430
  171. package/skills/talent-creator/scripts/__init__.py +0 -0
  172. package/skills/talent-creator/scripts/aggregate_benchmark.py +0 -401
  173. package/skills/talent-creator/scripts/generate_report.py +0 -326
  174. package/skills/talent-creator/scripts/improve_description.py +0 -247
  175. package/skills/talent-creator/scripts/package_skill.py +0 -136
  176. package/skills/talent-creator/scripts/quick_validate.py +0 -146
  177. package/skills/talent-creator/scripts/run_eval.py +0 -310
  178. package/skills/talent-creator/scripts/run_loop.py +0 -328
  179. package/skills/talent-creator/scripts/utils.py +0 -47
  180. package/skills/xlsx/SKILL.md +0 -300
  181. package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  182. package/skills/xlsx/scripts/office/helpers/merge_runs.py +0 -199
  183. package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +0 -197
  184. package/skills/xlsx/scripts/office/pack.py +0 -159
  185. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  186. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  187. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  188. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  189. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  190. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  191. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  192. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  193. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  194. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  195. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  196. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  197. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  198. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  199. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  200. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  201. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  202. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  203. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  204. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  205. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  206. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  207. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  208. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  209. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  210. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  211. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  212. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  213. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  214. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  215. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  216. package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +0 -75
  217. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
  218. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
  219. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
  220. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
  221. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
  222. package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  223. package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
  224. package/skills/xlsx/scripts/office/soffice.py +0 -183
  225. package/skills/xlsx/scripts/office/unpack.py +0 -132
  226. package/skills/xlsx/scripts/office/validate.py +0 -117
  227. package/skills/xlsx/scripts/office/validators/__init__.py +0 -15
  228. package/skills/xlsx/scripts/office/validators/base.py +0 -851
  229. package/skills/xlsx/scripts/office/validators/docx.py +0 -446
  230. package/skills/xlsx/scripts/office/validators/pptx.py +0 -275
  231. package/skills/xlsx/scripts/office/validators/redlining.py +0 -247
  232. package/skills/xlsx/scripts/recalc.py +0 -184
@@ -1,199 +0,0 @@
1
- """Merge adjacent runs with identical formatting in DOCX.
2
-
3
- Merges adjacent <w:r> elements that have identical <w:rPr> properties.
4
- Works on runs in paragraphs and inside tracked changes (<w:ins>, <w:del>).
5
-
6
- Also:
7
- - Removes rsid attributes from runs (revision metadata that doesn't affect rendering)
8
- - Removes proofErr elements (spell/grammar markers that block merging)
9
- """
10
-
11
- from pathlib import Path
12
-
13
- import defusedxml.minidom
14
-
15
-
16
- def merge_runs(input_dir: str) -> tuple[int, str]:
17
- doc_xml = Path(input_dir) / "word" / "document.xml"
18
-
19
- if not doc_xml.exists():
20
- return 0, f"Error: {doc_xml} not found"
21
-
22
- try:
23
- dom = defusedxml.minidom.parseString(doc_xml.read_text(encoding="utf-8"))
24
- root = dom.documentElement
25
-
26
- _remove_elements(root, "proofErr")
27
- _strip_run_rsid_attrs(root)
28
-
29
- containers = {run.parentNode for run in _find_elements(root, "r")}
30
-
31
- merge_count = 0
32
- for container in containers:
33
- merge_count += _merge_runs_in(container)
34
-
35
- doc_xml.write_bytes(dom.toxml(encoding="UTF-8"))
36
- return merge_count, f"Merged {merge_count} runs"
37
-
38
- except Exception as e:
39
- return 0, f"Error: {e}"
40
-
41
-
42
-
43
-
44
- def _find_elements(root, tag: str) -> list:
45
- results = []
46
-
47
- def traverse(node):
48
- if node.nodeType == node.ELEMENT_NODE:
49
- name = node.localName or node.tagName
50
- if name == tag or name.endswith(f":{tag}"):
51
- results.append(node)
52
- for child in node.childNodes:
53
- traverse(child)
54
-
55
- traverse(root)
56
- return results
57
-
58
-
59
- def _get_child(parent, tag: str):
60
- for child in parent.childNodes:
61
- if child.nodeType == child.ELEMENT_NODE:
62
- name = child.localName or child.tagName
63
- if name == tag or name.endswith(f":{tag}"):
64
- return child
65
- return None
66
-
67
-
68
- def _get_children(parent, tag: str) -> list:
69
- results = []
70
- for child in parent.childNodes:
71
- if child.nodeType == child.ELEMENT_NODE:
72
- name = child.localName or child.tagName
73
- if name == tag or name.endswith(f":{tag}"):
74
- results.append(child)
75
- return results
76
-
77
-
78
- def _is_adjacent(elem1, elem2) -> bool:
79
- node = elem1.nextSibling
80
- while node:
81
- if node == elem2:
82
- return True
83
- if node.nodeType == node.ELEMENT_NODE:
84
- return False
85
- if node.nodeType == node.TEXT_NODE and node.data.strip():
86
- return False
87
- node = node.nextSibling
88
- return False
89
-
90
-
91
-
92
-
93
- def _remove_elements(root, tag: str):
94
- for elem in _find_elements(root, tag):
95
- if elem.parentNode:
96
- elem.parentNode.removeChild(elem)
97
-
98
-
99
- def _strip_run_rsid_attrs(root):
100
- for run in _find_elements(root, "r"):
101
- for attr in list(run.attributes.values()):
102
- if "rsid" in attr.name.lower():
103
- run.removeAttribute(attr.name)
104
-
105
-
106
-
107
-
108
- def _merge_runs_in(container) -> int:
109
- merge_count = 0
110
- run = _first_child_run(container)
111
-
112
- while run:
113
- while True:
114
- next_elem = _next_element_sibling(run)
115
- if next_elem and _is_run(next_elem) and _can_merge(run, next_elem):
116
- _merge_run_content(run, next_elem)
117
- container.removeChild(next_elem)
118
- merge_count += 1
119
- else:
120
- break
121
-
122
- _consolidate_text(run)
123
- run = _next_sibling_run(run)
124
-
125
- return merge_count
126
-
127
-
128
- def _first_child_run(container):
129
- for child in container.childNodes:
130
- if child.nodeType == child.ELEMENT_NODE and _is_run(child):
131
- return child
132
- return None
133
-
134
-
135
- def _next_element_sibling(node):
136
- sibling = node.nextSibling
137
- while sibling:
138
- if sibling.nodeType == sibling.ELEMENT_NODE:
139
- return sibling
140
- sibling = sibling.nextSibling
141
- return None
142
-
143
-
144
- def _next_sibling_run(node):
145
- sibling = node.nextSibling
146
- while sibling:
147
- if sibling.nodeType == sibling.ELEMENT_NODE:
148
- if _is_run(sibling):
149
- return sibling
150
- sibling = sibling.nextSibling
151
- return None
152
-
153
-
154
- def _is_run(node) -> bool:
155
- name = node.localName or node.tagName
156
- return name == "r" or name.endswith(":r")
157
-
158
-
159
- def _can_merge(run1, run2) -> bool:
160
- rpr1 = _get_child(run1, "rPr")
161
- rpr2 = _get_child(run2, "rPr")
162
-
163
- if (rpr1 is None) != (rpr2 is None):
164
- return False
165
- if rpr1 is None:
166
- return True
167
- return rpr1.toxml() == rpr2.toxml()
168
-
169
-
170
- def _merge_run_content(target, source):
171
- for child in list(source.childNodes):
172
- if child.nodeType == child.ELEMENT_NODE:
173
- name = child.localName or child.tagName
174
- if name != "rPr" and not name.endswith(":rPr"):
175
- target.appendChild(child)
176
-
177
-
178
- def _consolidate_text(run):
179
- t_elements = _get_children(run, "t")
180
-
181
- for i in range(len(t_elements) - 1, 0, -1):
182
- curr, prev = t_elements[i], t_elements[i - 1]
183
-
184
- if _is_adjacent(prev, curr):
185
- prev_text = prev.firstChild.data if prev.firstChild else ""
186
- curr_text = curr.firstChild.data if curr.firstChild else ""
187
- merged = prev_text + curr_text
188
-
189
- if prev.firstChild:
190
- prev.firstChild.data = merged
191
- else:
192
- prev.appendChild(run.ownerDocument.createTextNode(merged))
193
-
194
- if merged.startswith(" ") or merged.endswith(" "):
195
- prev.setAttribute("xml:space", "preserve")
196
- elif prev.hasAttribute("xml:space"):
197
- prev.removeAttribute("xml:space")
198
-
199
- run.removeChild(curr)
@@ -1,197 +0,0 @@
1
- """Simplify tracked changes by merging adjacent w:ins or w:del elements.
2
-
3
- Merges adjacent <w:ins> elements from the same author into a single element.
4
- Same for <w:del> elements. This makes heavily-redlined documents easier to
5
- work with by reducing the number of tracked change wrappers.
6
-
7
- Rules:
8
- - Only merges w:ins with w:ins, w:del with w:del (same element type)
9
- - Only merges if same author (ignores timestamp differences)
10
- - Only merges if truly adjacent (only whitespace between them)
11
- """
12
-
13
- import xml.etree.ElementTree as ET
14
- import zipfile
15
- from pathlib import Path
16
-
17
- import defusedxml.minidom
18
-
19
- WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
20
-
21
-
22
- def simplify_redlines(input_dir: str) -> tuple[int, str]:
23
- doc_xml = Path(input_dir) / "word" / "document.xml"
24
-
25
- if not doc_xml.exists():
26
- return 0, f"Error: {doc_xml} not found"
27
-
28
- try:
29
- dom = defusedxml.minidom.parseString(doc_xml.read_text(encoding="utf-8"))
30
- root = dom.documentElement
31
-
32
- merge_count = 0
33
-
34
- containers = _find_elements(root, "p") + _find_elements(root, "tc")
35
-
36
- for container in containers:
37
- merge_count += _merge_tracked_changes_in(container, "ins")
38
- merge_count += _merge_tracked_changes_in(container, "del")
39
-
40
- doc_xml.write_bytes(dom.toxml(encoding="UTF-8"))
41
- return merge_count, f"Simplified {merge_count} tracked changes"
42
-
43
- except Exception as e:
44
- return 0, f"Error: {e}"
45
-
46
-
47
- def _merge_tracked_changes_in(container, tag: str) -> int:
48
- merge_count = 0
49
-
50
- tracked = [
51
- child
52
- for child in container.childNodes
53
- if child.nodeType == child.ELEMENT_NODE and _is_element(child, tag)
54
- ]
55
-
56
- if len(tracked) < 2:
57
- return 0
58
-
59
- i = 0
60
- while i < len(tracked) - 1:
61
- curr = tracked[i]
62
- next_elem = tracked[i + 1]
63
-
64
- if _can_merge_tracked(curr, next_elem):
65
- _merge_tracked_content(curr, next_elem)
66
- container.removeChild(next_elem)
67
- tracked.pop(i + 1)
68
- merge_count += 1
69
- else:
70
- i += 1
71
-
72
- return merge_count
73
-
74
-
75
- def _is_element(node, tag: str) -> bool:
76
- name = node.localName or node.tagName
77
- return name == tag or name.endswith(f":{tag}")
78
-
79
-
80
- def _get_author(elem) -> str:
81
- author = elem.getAttribute("w:author")
82
- if not author:
83
- for attr in elem.attributes.values():
84
- if attr.localName == "author" or attr.name.endswith(":author"):
85
- return attr.value
86
- return author
87
-
88
-
89
- def _can_merge_tracked(elem1, elem2) -> bool:
90
- if _get_author(elem1) != _get_author(elem2):
91
- return False
92
-
93
- node = elem1.nextSibling
94
- while node and node != elem2:
95
- if node.nodeType == node.ELEMENT_NODE:
96
- return False
97
- if node.nodeType == node.TEXT_NODE and node.data.strip():
98
- return False
99
- node = node.nextSibling
100
-
101
- return True
102
-
103
-
104
- def _merge_tracked_content(target, source):
105
- while source.firstChild:
106
- child = source.firstChild
107
- source.removeChild(child)
108
- target.appendChild(child)
109
-
110
-
111
- def _find_elements(root, tag: str) -> list:
112
- results = []
113
-
114
- def traverse(node):
115
- if node.nodeType == node.ELEMENT_NODE:
116
- name = node.localName or node.tagName
117
- if name == tag or name.endswith(f":{tag}"):
118
- results.append(node)
119
- for child in node.childNodes:
120
- traverse(child)
121
-
122
- traverse(root)
123
- return results
124
-
125
-
126
- def get_tracked_change_authors(doc_xml_path: Path) -> dict[str, int]:
127
- if not doc_xml_path.exists():
128
- return {}
129
-
130
- try:
131
- tree = ET.parse(doc_xml_path)
132
- root = tree.getroot()
133
- except ET.ParseError:
134
- return {}
135
-
136
- namespaces = {"w": WORD_NS}
137
- author_attr = f"{{{WORD_NS}}}author"
138
-
139
- authors: dict[str, int] = {}
140
- for tag in ["ins", "del"]:
141
- for elem in root.findall(f".//w:{tag}", namespaces):
142
- author = elem.get(author_attr)
143
- if author:
144
- authors[author] = authors.get(author, 0) + 1
145
-
146
- return authors
147
-
148
-
149
- def _get_authors_from_docx(docx_path: Path) -> dict[str, int]:
150
- try:
151
- with zipfile.ZipFile(docx_path, "r") as zf:
152
- if "word/document.xml" not in zf.namelist():
153
- return {}
154
- with zf.open("word/document.xml") as f:
155
- tree = ET.parse(f)
156
- root = tree.getroot()
157
-
158
- namespaces = {"w": WORD_NS}
159
- author_attr = f"{{{WORD_NS}}}author"
160
-
161
- authors: dict[str, int] = {}
162
- for tag in ["ins", "del"]:
163
- for elem in root.findall(f".//w:{tag}", namespaces):
164
- author = elem.get(author_attr)
165
- if author:
166
- authors[author] = authors.get(author, 0) + 1
167
- return authors
168
- except (zipfile.BadZipFile, ET.ParseError):
169
- return {}
170
-
171
-
172
- def infer_author(modified_dir: Path, original_docx: Path, default: str = "WormClaude") -> str:
173
- modified_xml = modified_dir / "word" / "document.xml"
174
- modified_authors = get_tracked_change_authors(modified_xml)
175
-
176
- if not modified_authors:
177
- return default
178
-
179
- original_authors = _get_authors_from_docx(original_docx)
180
-
181
- new_changes: dict[str, int] = {}
182
- for author, count in modified_authors.items():
183
- original_count = original_authors.get(author, 0)
184
- diff = count - original_count
185
- if diff > 0:
186
- new_changes[author] = diff
187
-
188
- if not new_changes:
189
- return default
190
-
191
- if len(new_changes) == 1:
192
- return next(iter(new_changes))
193
-
194
- raise ValueError(
195
- f"Multiple authors added new changes: {new_changes}. "
196
- "Cannot infer which author to validate."
197
- )
@@ -1,159 +0,0 @@
1
- """Pack a directory into a DOCX, PPTX, or XLSX file.
2
-
3
- Validates with auto-repair, condenses XML formatting, and creates the Office file.
4
-
5
- Usage:
6
- python pack.py <input_directory> <output_file> [--original <file>] [--validate true|false]
7
-
8
- Examples:
9
- python pack.py unpacked/ output.docx --original input.docx
10
- python pack.py unpacked/ output.pptx --validate false
11
- """
12
-
13
- import argparse
14
- import sys
15
- import shutil
16
- import tempfile
17
- import zipfile
18
- from pathlib import Path
19
-
20
- import defusedxml.minidom
21
-
22
- from validators import DOCXSchemaValidator, PPTXSchemaValidator, RedliningValidator
23
-
24
- def pack(
25
- input_directory: str,
26
- output_file: str,
27
- original_file: str | None = None,
28
- validate: bool = True,
29
- infer_author_func=None,
30
- ) -> tuple[None, str]:
31
- input_dir = Path(input_directory)
32
- output_path = Path(output_file)
33
- suffix = output_path.suffix.lower()
34
-
35
- if not input_dir.is_dir():
36
- return None, f"Error: {input_dir} is not a directory"
37
-
38
- if suffix not in {".docx", ".pptx", ".xlsx"}:
39
- return None, f"Error: {output_file} must be a .docx, .pptx, or .xlsx file"
40
-
41
- if validate and original_file:
42
- original_path = Path(original_file)
43
- if original_path.exists():
44
- success, output = _run_validation(
45
- input_dir, original_path, suffix, infer_author_func
46
- )
47
- if output:
48
- print(output)
49
- if not success:
50
- return None, f"Error: Validation failed for {input_dir}"
51
-
52
- with tempfile.TemporaryDirectory() as temp_dir:
53
- temp_content_dir = Path(temp_dir) / "content"
54
- shutil.copytree(input_dir, temp_content_dir)
55
-
56
- for pattern in ["*.xml", "*.rels"]:
57
- for xml_file in temp_content_dir.rglob(pattern):
58
- _condense_xml(xml_file)
59
-
60
- output_path.parent.mkdir(parents=True, exist_ok=True)
61
- with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
62
- for f in temp_content_dir.rglob("*"):
63
- if f.is_file():
64
- zf.write(f, f.relative_to(temp_content_dir))
65
-
66
- return None, f"Successfully packed {input_dir} to {output_file}"
67
-
68
-
69
- def _run_validation(
70
- unpacked_dir: Path,
71
- original_file: Path,
72
- suffix: str,
73
- infer_author_func=None,
74
- ) -> tuple[bool, str | None]:
75
- output_lines = []
76
- validators = []
77
-
78
- if suffix == ".docx":
79
- author = "Wormclaude"
80
- if infer_author_func:
81
- try:
82
- author = infer_author_func(unpacked_dir, original_file)
83
- except ValueError as e:
84
- print(f"Warning: {e} Using default author 'WormClaude'.", file=sys.stderr)
85
-
86
- validators = [
87
- DOCXSchemaValidator(unpacked_dir, original_file),
88
- RedliningValidator(unpacked_dir, original_file, author=author),
89
- ]
90
- elif suffix == ".pptx":
91
- validators = [PPTXSchemaValidator(unpacked_dir, original_file)]
92
-
93
- if not validators:
94
- return True, None
95
-
96
- total_repairs = sum(v.repair() for v in validators)
97
- if total_repairs:
98
- output_lines.append(f"Auto-repaired {total_repairs} issue(s)")
99
-
100
- success = all(v.validate() for v in validators)
101
-
102
- if success:
103
- output_lines.append("All validations PASSED!")
104
-
105
- return success, "\n".join(output_lines) if output_lines else None
106
-
107
-
108
- def _condense_xml(xml_file: Path) -> None:
109
- try:
110
- with open(xml_file, encoding="utf-8") as f:
111
- dom = defusedxml.minidom.parse(f)
112
-
113
- for element in dom.getElementsByTagName("*"):
114
- if element.tagName.endswith(":t"):
115
- continue
116
-
117
- for child in list(element.childNodes):
118
- if (
119
- child.nodeType == child.TEXT_NODE
120
- and child.nodeValue
121
- and child.nodeValue.strip() == ""
122
- ) or child.nodeType == child.COMMENT_NODE:
123
- element.removeChild(child)
124
-
125
- xml_file.write_bytes(dom.toxml(encoding="UTF-8"))
126
- except Exception as e:
127
- print(f"ERROR: Failed to parse {xml_file.name}: {e}", file=sys.stderr)
128
- raise
129
-
130
-
131
- if __name__ == "__main__":
132
- parser = argparse.ArgumentParser(
133
- description="Pack a directory into a DOCX, PPTX, or XLSX file"
134
- )
135
- parser.add_argument("input_directory", help="Unpacked Office document directory")
136
- parser.add_argument("output_file", help="Output Office file (.docx/.pptx/.xlsx)")
137
- parser.add_argument(
138
- "--original",
139
- help="Original file for validation comparison",
140
- )
141
- parser.add_argument(
142
- "--validate",
143
- type=lambda x: x.lower() == "true",
144
- default=True,
145
- metavar="true|false",
146
- help="Run validation with auto-repair (default: true)",
147
- )
148
- args = parser.parse_args()
149
-
150
- _, message = pack(
151
- args.input_directory,
152
- args.output_file,
153
- original_file=args.original,
154
- validate=args.validate,
155
- )
156
- print(message)
157
-
158
- if "Error" in message:
159
- sys.exit(1)