From 228962adea8205e27e5381ff07929623be83ccb2 Mon Sep 17 00:00:00 2001 From: platon-oai Date: Fri, 1 May 2026 09:49:48 -0700 Subject: [PATCH] Remove curated doc skill (#382) Removes the public curated doc skill from skills/.curated. --- skills/.curated/doc/LICENSE.txt | 201 -------------- skills/.curated/doc/SKILL.md | 80 ------ skills/.curated/doc/agents/openai.yaml | 6 - skills/.curated/doc/assets/doc-small.svg | 3 - skills/.curated/doc/assets/doc.png | Bin 1211 -> 0 bytes skills/.curated/doc/scripts/render_docx.py | 296 --------------------- 6 files changed, 586 deletions(-) delete mode 100644 skills/.curated/doc/LICENSE.txt delete mode 100644 skills/.curated/doc/SKILL.md delete mode 100644 skills/.curated/doc/agents/openai.yaml delete mode 100644 skills/.curated/doc/assets/doc-small.svg delete mode 100644 skills/.curated/doc/assets/doc.png delete mode 100755 skills/.curated/doc/scripts/render_docx.py diff --git a/skills/.curated/doc/LICENSE.txt b/skills/.curated/doc/LICENSE.txt deleted file mode 100644 index 13e25df..0000000 --- a/skills/.curated/doc/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf of - any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don\'t include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/skills/.curated/doc/SKILL.md b/skills/.curated/doc/SKILL.md deleted file mode 100644 index c6d9612..0000000 --- a/skills/.curated/doc/SKILL.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: "doc" -description: "Use when the task involves reading, creating, or editing `.docx` documents, especially when formatting or layout fidelity matters; prefer `python-docx` plus the bundled `scripts/render_docx.py` for visual checks." ---- - - -# DOCX Skill - -## When to use -- Read or review DOCX content where layout matters (tables, diagrams, pagination). -- Create or edit DOCX files with professional formatting. -- Validate visual layout before delivery. - -## Workflow -1. Prefer visual review (layout, tables, diagrams). - - If `soffice` and `pdftoppm` are available, convert DOCX -> PDF -> PNGs. - - Or use `scripts/render_docx.py` (requires `pdf2image` and Poppler). - - If these tools are missing, install them or ask the user to review rendered pages locally. -2. Use `python-docx` for edits and structured creation (headings, styles, tables, lists). -3. After each meaningful change, re-render and inspect the pages. -4. If visual review is not possible, extract text with `python-docx` as a fallback and call out layout risk. -5. Keep intermediate outputs organized and clean up after final approval. - -## Temp and output conventions -- Use `tmp/docs/` for intermediate files; delete when done. -- Write final artifacts under `output/doc/` when working in this repo. -- Keep filenames stable and descriptive. - -## Dependencies (install if missing) -Prefer `uv` for dependency management. - -Python packages: -``` -uv pip install python-docx pdf2image -``` -If `uv` is unavailable: -``` -python3 -m pip install python-docx pdf2image -``` -System tools (for rendering): -``` -# macOS (Homebrew) -brew install libreoffice poppler - -# Ubuntu/Debian -sudo apt-get install -y libreoffice poppler-utils -``` - -If installation isn't possible in this environment, tell the user which dependency is missing and how to install it locally. - -## Environment -No required environment variables. - -## Rendering commands -DOCX -> PDF: -``` -soffice -env:UserInstallation=file:///tmp/lo_profile_$$ --headless --convert-to pdf --outdir $OUTDIR $INPUT_DOCX -``` - -PDF -> PNGs: -``` -pdftoppm -png $OUTDIR/$BASENAME.pdf $OUTDIR/$BASENAME -``` - -Bundled helper: -``` -python3 scripts/render_docx.py /path/to/file.docx --output_dir /tmp/docx_pages -``` - -## Quality expectations -- Deliver a client-ready document: consistent typography, spacing, margins, and clear hierarchy. -- Avoid formatting defects: clipped/overlapping text, broken tables, unreadable characters, or default-template styling. -- Charts, tables, and visuals must be legible in rendered pages with correct alignment. -- Use ASCII hyphens only. Avoid U+2011 (non-breaking hyphen) and other Unicode dashes. -- Citations and references must be human-readable; never leave tool tokens or placeholder strings. - -## Final checks -- Re-render and inspect every page at 100% zoom before final delivery. -- Fix any spacing, alignment, or pagination issues and repeat the render loop. -- Confirm there are no leftovers (temp files, duplicate renders) unless the user asks to keep them. diff --git a/skills/.curated/doc/agents/openai.yaml b/skills/.curated/doc/agents/openai.yaml deleted file mode 100644 index 27ce451..0000000 --- a/skills/.curated/doc/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: "Word Docs" - short_description: "Edit and review docx files" - icon_small: "./assets/doc-small.svg" - icon_large: "./assets/doc.png" - default_prompt: "Edit or review this .docx file and return the updated file plus a concise change summary." diff --git a/skills/.curated/doc/assets/doc-small.svg b/skills/.curated/doc/assets/doc-small.svg deleted file mode 100644 index 97289eb..0000000 --- a/skills/.curated/doc/assets/doc-small.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/skills/.curated/doc/assets/doc.png b/skills/.curated/doc/assets/doc.png deleted file mode 100644 index e16517899921ddc1e7691226014b7ef0256c4d54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1211 zcmeAS@N?(olHy`uVBq!ia0vp^DIm``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBZh)tYV@L(#+qpaao;V1!Rqx0bYGGmIRa#{^8?C!Lsu5l}l6jH%$2O;e&&!a+A^BJ9k>npFe-VD@?J1 zNnTz)^j1g6jio8VP24UK8O$6j*92|iILNxq)vYDeOHA=i;wx*3r5&vc?rK;b(B_>j zvT?%p?b|Pu%~;aWvSn|X&mkpOm6o0M^O>4OCJG!oIA0`N^Q6-jHBQ|PQ*u{`!n$jHo(fODecLum-9 z*I&0znldSATFkLW?Wa$l&QkS?6%!Xf{P1C7M0E7*U)v-81y`+M(M(8AR(^8Gg(GFo zym@Am1b6P*#ig#UewAy||3=eD&Hwu~cP^dLvgY&W&rfEze%8~|TlP=3WeQ*P#5oU7 zyKrQAy!@=l9JHlDWX<16NlGbEJ5HAVyL4$%lrG~0rL+7qIV`QLR$Vpt&poI8^XrDT z+R94HjK-29moEoTxZqu@D|OT9q?d5Z+}E#PX9dQ_1{m}4@vXWSCS0&MAg-4)(1zvr z@82i8_nd9XXbx!l)8-b$dpdOE32WAOQ;$m?6z-nQV)$a^Lvi=cC}qk2XL=@dxb0So zJoC0V5;v%j3WW;7%5m1?_VmWbALLYCcv!t$p@S$@) zoQZ2B<2Pz8?wqi9@7@D_p^|5XLQZ6FXxenux9RCI6Zs~#kXiBPd8;B<-aNx$w###F zpxNBI^MxN{53{#n+6qjg2C@?O?%oaTyHZ|O)+DOtWH7m^tSpQ(k;SR6zh8R2;<-l8 z6Hdz}gt9r@oN#f=0T+(VJturrSLir72Cq@G^=*mJRmf-x-FHk!zG=dRFIzKsAFUGh zJQgsCf96c_GLwlM9~%`VPdeSO|DDkJr1VXSV(#yvUbRWplWWxO{V&t3SN?lu(u=;V zea!d7mL%&;oM6WDVT)(2td){bQ`=qX@AnmwJPz;JA((Oc`gxOx*jV16BTg4OJXdFm zH}uHNouhMC#m77&uk{GqI$QU*3^#wRojrTD@lD=irjKuJ{=0tyr$|nAcJ{I*3(}tK zd-mky(f0?^^iE7p{ None: - missing: list[str] = [] - for tool in ("soffice", "pdftoppm"): - if which(tool) is None: - missing.append(tool) - if missing: - tools = ", ".join(missing) - raise RuntimeError( - f"Missing required system tool(s): {tools}. Install LibreOffice and Poppler, then retry." - ) - - -def calc_dpi_via_ooxml_docx(input_path: str, max_w_px: int, max_h_px: int) -> int: - """Calculate DPI from OOXML `word/document.xml` page size (w:pgSz in twips). - - DOCX stores page dimensions in section properties as twips (1/1440 inch). - We read the first encountered section's page size and compute an isotropic DPI - that fits within the target max pixel dimensions. - """ - with ZipFile(input_path, "r") as zf: - xml = zf.read("word/document.xml") - root = ET.fromstring(xml) - ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"} - - # Common placements: w:body/w:sectPr or w:body/w:p/w:pPr/w:sectPr - sect_pr = root.find(".//w:sectPr", ns) - if sect_pr is None: - raise RuntimeError("Section properties not found in document.xml") - pg_sz = sect_pr.find("w:pgSz", ns) - if pg_sz is None: - raise RuntimeError("Page size not found in section properties") - - # Values are in twips - w_twips_str = pg_sz.get( - "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}w" - ) or pg_sz.get("w") - h_twips_str = pg_sz.get( - "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}h" - ) or pg_sz.get("h") - - if not w_twips_str or not h_twips_str: - raise RuntimeError("Page size attributes missing in pgSz") - - width_in = int(w_twips_str) / TWIPS_PER_INCH - height_in = int(h_twips_str) / TWIPS_PER_INCH - if width_in <= 0 or height_in <= 0: - raise RuntimeError("Invalid page size values in document.xml") - return round(min(max_w_px / width_in, max_h_px / height_in)) - - -def calc_dpi_via_pdf(input_path: str, max_w_px: int, max_h_px: int) -> int: - """Convert input to PDF and compute DPI from its page size.""" - with tempfile.TemporaryDirectory(prefix="soffice_profile_") as user_profile: - with tempfile.TemporaryDirectory(prefix="soffice_convert_") as convert_tmp_dir: - stem = splitext(basename(input_path))[0] - pdf_path = convert_to_pdf(input_path, user_profile, convert_tmp_dir, stem) - if not (pdf_path and exists(pdf_path)): - raise RuntimeError("Failed to convert input to PDF for DPI computation.") - - info = pdfinfo_from_path(pdf_path) - size_val = info.get("Page size") - if not size_val: - for k, v in info.items(): - if isinstance(v, str) and "size" in k.lower() and "pts" in v: - size_val = v - break - if not isinstance(size_val, str): - raise RuntimeError("Failed to read PDF page size for DPI computation.") - - m = re.search(r"(\d+)\s*x\s*(\d+)\s*pts", size_val) - if not m: - raise RuntimeError("Unrecognized PDF page size format.") - width_pts = int(m.group(1)) - height_pts = int(m.group(2)) - width_in = width_pts / 72.0 - height_in = height_pts / 72.0 - if width_in <= 0 or height_in <= 0: - raise RuntimeError("Invalid PDF page size values.") - return round(min(max_w_px / width_in, max_h_px / height_in)) - - -def run_cmd_no_check(cmd: list[str]) -> None: - subprocess.run( - cmd, - check=False, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - env=os.environ.copy(), - ) - - -def convert_to_pdf( - doc_path: str, - user_profile: str, - convert_tmp_dir: str, - stem: str, -) -> str: - # Try direct DOC(X) -> PDF - cmd_pdf = [ - "soffice", - "-env:UserInstallation=file://" + user_profile, - "--invisible", - "--headless", - "--norestore", - "--convert-to", - "pdf", - "--outdir", - convert_tmp_dir, - doc_path, - ] - run_cmd_no_check(cmd_pdf) - - pdf_path = join(convert_tmp_dir, f"{stem}.pdf") - if exists(pdf_path): - return pdf_path - - # Fallback: DOCX -> ODT, then ODT -> PDF - cmd_odt = [ - "soffice", - "-env:UserInstallation=file://" + user_profile, - "--invisible", - "--headless", - "--norestore", - "--convert-to", - "odt", - "--outdir", - convert_tmp_dir, - doc_path, - ] - run_cmd_no_check(cmd_odt) - - odt_path = join(convert_tmp_dir, f"{stem}.odt") - - if exists(odt_path): - cmd_odt_pdf = [ - "soffice", - "-env:UserInstallation=file://" + user_profile, - "--invisible", - "--headless", - "--norestore", - "--convert-to", - "pdf", - "--outdir", - convert_tmp_dir, - odt_path, - ] - run_cmd_no_check(cmd_odt_pdf) - if exists(pdf_path): - return pdf_path - - return "" - - -def rasterize( - doc_path: str, - out_dir: str, - dpi: int, -) -> Sequence[str]: - """Rasterise DOCX (or similar) to images placed in out_dir and return their paths. - - Images are named as page-. with pages starting at 1. - """ - makedirs(out_dir, exist_ok=True) - doc_path = abspath(doc_path) - stem = splitext(basename(doc_path))[0] - - # Use a unique user profile to avoid LibreOffice profile lock when running concurrently - with tempfile.TemporaryDirectory(prefix="soffice_profile_") as user_profile: - # Write conversion outputs into a temp directory to avoid any IO oddities - with tempfile.TemporaryDirectory(prefix="soffice_convert_") as convert_tmp_dir: - pdf_path = convert_to_pdf( - doc_path, - user_profile, - convert_tmp_dir, - stem, - ) - - if not pdf_path or not exists(pdf_path): - raise RuntimeError( - "Failed to produce PDF for rasterization (direct and ODT fallback)." - ) - paths_raw = cast( - list[str], - convert_from_path( - pdf_path, - dpi=dpi, - fmt="png", - thread_count=8, - output_folder=out_dir, - paths_only=True, - output_file="page", - ), - ) - - # Rename convert_from_path's output format f'page{thread_id:04d}-{page_num:02d}.' to 'page-.' - pages: list[tuple[int, str]] = [] - for src_path in paths_raw: - base = splitext(basename(src_path))[0] - page_num_str = base.split("-")[-1] - page_num = int(page_num_str) - dst_path = join(out_dir, f"page-{page_num}.png") - replace(src_path, dst_path) - pages.append((page_num, dst_path)) - pages.sort(key=lambda t: t[0]) - final_paths = [path for _, path in pages] - return final_paths - - -def main() -> None: - parser = argparse.ArgumentParser(description="Render DOCX-like file to PNG images.") - parser.add_argument( - "input_path", - type=str, - help="Path to the input DOCX file (or compatible).", - ) - parser.add_argument( - "--output_dir", - type=str, - default=None, - help=( - "Output directory for the rendered images. " - "Defaults to a folder next to the input named after the input file (without extension)." - ), - ) - parser.add_argument( - "--width", - type=int, - default=1600, - help=( - "Approximate maximum width in pixels after isotropic scaling (default 1600). " - "The actual value may exceed slightly." - ), - ) - parser.add_argument( - "--height", - type=int, - default=2000, - help=( - "Approximate maximum height in pixels after isotropic scaling (default 2000). " - "The actual value may exceed slightly." - ), - ) - parser.add_argument( - "--dpi", - type=int, - default=None, - help=("Override computed DPI. If provided, skips DOCX/PDF-based DPI calculation."), - ) - args = parser.parse_args() - - try: - ensure_system_tools() - - input_path = abspath(expanduser(args.input_path)) - out_dir = ( - abspath(expanduser(args.output_dir)) if args.output_dir else splitext(input_path)[0] - ) - - if args.dpi is not None: - dpi = int(args.dpi) - else: - try: - if input_path.lower().endswith((".docx", ".docm", ".dotx", ".dotm")): - dpi = calc_dpi_via_ooxml_docx(input_path, args.width, args.height) - else: - raise RuntimeError("Skip OOXML DPI; not a DOCX container") - except Exception: - dpi = calc_dpi_via_pdf(input_path, args.width, args.height) - - rasterize(input_path, out_dir, dpi) - print("Pages rendered to " + out_dir) - except RuntimeError as exc: - print(f"Error: {exc}", file=sys.stderr) - raise SystemExit(1) - - -if __name__ == "__main__": - main()