@_vrsen/openswarm 0.1.0

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 (316) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/bin/openswarm.js +38 -0
  4. package/config.py +34 -0
  5. package/data_analyst_agent/.cursor/rules/data_analyst.mdc +43 -0
  6. package/data_analyst_agent/__init__.py +3 -0
  7. package/data_analyst_agent/__pycache__/__init__.cpython-312.pyc +0 -0
  8. package/data_analyst_agent/__pycache__/data_analyst_agent.cpython-312.pyc +0 -0
  9. package/data_analyst_agent/data_analyst_agent.py +45 -0
  10. package/data_analyst_agent/instructions.md +173 -0
  11. package/data_analyst_agent/test_files/test_file.csv +21 -0
  12. package/data_analyst_agent/tools/__init__.py +6 -0
  13. package/deep_research/__init__.py +1 -0
  14. package/deep_research/__pycache__/__init__.cpython-312.pyc +0 -0
  15. package/deep_research/__pycache__/deep_research.cpython-312.pyc +0 -0
  16. package/deep_research/deep_research.py +27 -0
  17. package/deep_research/instructions.md +104 -0
  18. package/deep_research/tools/__init__.py +1 -0
  19. package/docs_agent/__init__.py +3 -0
  20. package/docs_agent/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/docs_agent/__pycache__/docs_agent.cpython-312.pyc +0 -0
  22. package/docs_agent/docs_agent.py +61 -0
  23. package/docs_agent/instructions.md +418 -0
  24. package/docs_agent/tools/ConvertDocument.py +323 -0
  25. package/docs_agent/tools/CreateDocument.py +287 -0
  26. package/docs_agent/tools/ListDocuments.py +134 -0
  27. package/docs_agent/tools/ModifyDocument.py +247 -0
  28. package/docs_agent/tools/RestoreDocument.py +79 -0
  29. package/docs_agent/tools/ViewDocument.py +153 -0
  30. package/docs_agent/tools/__init__.py +1 -0
  31. package/docs_agent/tools/__pycache__/ConvertDocument.cpython-312.pyc +0 -0
  32. package/docs_agent/tools/__pycache__/CreateDocument.cpython-312.pyc +0 -0
  33. package/docs_agent/tools/__pycache__/ListDocuments.cpython-312.pyc +0 -0
  34. package/docs_agent/tools/__pycache__/ModifyDocument.cpython-312.pyc +0 -0
  35. package/docs_agent/tools/__pycache__/RestoreDocument.cpython-312.pyc +0 -0
  36. package/docs_agent/tools/__pycache__/ViewDocument.cpython-312.pyc +0 -0
  37. package/docs_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  38. package/docs_agent/tools/utils/__init__.py +1 -0
  39. package/docs_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  40. package/docs_agent/tools/utils/__pycache__/doc_file_utils.cpython-312.pyc +0 -0
  41. package/docs_agent/tools/utils/__pycache__/html_docx_blocks.cpython-312.pyc +0 -0
  42. package/docs_agent/tools/utils/__pycache__/html_docx_constants.cpython-312.pyc +0 -0
  43. package/docs_agent/tools/utils/__pycache__/html_docx_core.cpython-312.pyc +0 -0
  44. package/docs_agent/tools/utils/__pycache__/html_docx_css.cpython-312.pyc +0 -0
  45. package/docs_agent/tools/utils/__pycache__/html_docx_images.cpython-312.pyc +0 -0
  46. package/docs_agent/tools/utils/__pycache__/html_docx_page.cpython-312.pyc +0 -0
  47. package/docs_agent/tools/utils/__pycache__/html_docx_paragraphs.cpython-312.pyc +0 -0
  48. package/docs_agent/tools/utils/__pycache__/html_docx_playwright.cpython-312.pyc +0 -0
  49. package/docs_agent/tools/utils/__pycache__/html_docx_selectors.cpython-312.pyc +0 -0
  50. package/docs_agent/tools/utils/__pycache__/html_docx_shared.cpython-312.pyc +0 -0
  51. package/docs_agent/tools/utils/__pycache__/html_validation.cpython-312.pyc +0 -0
  52. package/docs_agent/tools/utils/doc_file_utils.py +29 -0
  53. package/docs_agent/tools/utils/html_docx_blocks.py +262 -0
  54. package/docs_agent/tools/utils/html_docx_constants.py +78 -0
  55. package/docs_agent/tools/utils/html_docx_core.py +138 -0
  56. package/docs_agent/tools/utils/html_docx_css.py +262 -0
  57. package/docs_agent/tools/utils/html_docx_images.py +293 -0
  58. package/docs_agent/tools/utils/html_docx_page.py +185 -0
  59. package/docs_agent/tools/utils/html_docx_paragraphs.py +342 -0
  60. package/docs_agent/tools/utils/html_docx_playwright.py +184 -0
  61. package/docs_agent/tools/utils/html_docx_selectors.py +196 -0
  62. package/docs_agent/tools/utils/html_docx_shared.py +23 -0
  63. package/docs_agent/tools/utils/html_docx_tables.py +942 -0
  64. package/docs_agent/tools/utils/html_validation.py +102 -0
  65. package/helpers.py +59 -0
  66. package/image_generation_agent/__init__.py +1 -0
  67. package/image_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
  68. package/image_generation_agent/__pycache__/image_generation_agent.cpython-312.pyc +0 -0
  69. package/image_generation_agent/image_generation_agent.py +31 -0
  70. package/image_generation_agent/instructions.md +80 -0
  71. package/image_generation_agent/tools/CombineImages.py +211 -0
  72. package/image_generation_agent/tools/EditImages.py +200 -0
  73. package/image_generation_agent/tools/GenerateImages.py +184 -0
  74. package/image_generation_agent/tools/RemoveBackground.py +108 -0
  75. package/image_generation_agent/tools/__init__.py +2 -0
  76. package/image_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
  77. package/image_generation_agent/tools/__pycache__/EditImages.cpython-312.pyc +0 -0
  78. package/image_generation_agent/tools/__pycache__/GenerateImages.cpython-312.pyc +0 -0
  79. package/image_generation_agent/tools/__pycache__/RemoveBackground.cpython-312.pyc +0 -0
  80. package/image_generation_agent/tools/utils/__init__.py +2 -0
  81. package/image_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  82. package/image_generation_agent/tools/utils/__pycache__/image_io.cpython-312.pyc +0 -0
  83. package/image_generation_agent/tools/utils/image_io.py +308 -0
  84. package/onboard.py +325 -0
  85. package/orchestrator/__init__.py +3 -0
  86. package/orchestrator/__pycache__/__init__.cpython-312.pyc +0 -0
  87. package/orchestrator/__pycache__/orchestrator.cpython-312.pyc +0 -0
  88. package/orchestrator/instructions.md +90 -0
  89. package/orchestrator/orchestrator.py +33 -0
  90. package/package.json +49 -0
  91. package/patches/__init__.py +1 -0
  92. package/patches/__pycache__/__init__.cpython-312.pyc +0 -0
  93. package/patches/__pycache__/patch_agency_swarm_dual_comms.cpython-312.pyc +0 -0
  94. package/patches/__pycache__/patch_file_attachment_refs.cpython-312.pyc +0 -0
  95. package/patches/__pycache__/patch_ipython_interpreter_composio.cpython-312.pyc +0 -0
  96. package/patches/dom-to-pptx+1.1.5.patch +133440 -0
  97. package/patches/patch_agency_swarm_dual_comms.py +199 -0
  98. package/patches/patch_file_attachment_refs.py +74 -0
  99. package/patches/patch_ipython_interpreter_composio.py +54 -0
  100. package/pyproject.toml +67 -0
  101. package/run.py +343 -0
  102. package/server.py +26 -0
  103. package/shared_instructions.md +119 -0
  104. package/shared_tools/CopyFile.py +68 -0
  105. package/shared_tools/ExecuteTool.py +184 -0
  106. package/shared_tools/FindTools.py +101 -0
  107. package/shared_tools/ManageConnections.py +43 -0
  108. package/shared_tools/SearchTools.py +44 -0
  109. package/shared_tools/__init__.py +7 -0
  110. package/shared_tools/__pycache__/CopyFile.cpython-312.pyc +0 -0
  111. package/shared_tools/__pycache__/ExecuteTool.cpython-312.pyc +0 -0
  112. package/shared_tools/__pycache__/FindTools.cpython-312.pyc +0 -0
  113. package/shared_tools/__pycache__/ManageConnections.cpython-312.pyc +0 -0
  114. package/shared_tools/__pycache__/SearchTools.cpython-312.pyc +0 -0
  115. package/shared_tools/__pycache__/__init__.cpython-312.pyc +0 -0
  116. package/slides_agent/.cursor/rules/slides-agent-workflow.mdc +9 -0
  117. package/slides_agent/__init__.py +1 -0
  118. package/slides_agent/__pycache__/__init__.cpython-312.pyc +0 -0
  119. package/slides_agent/__pycache__/slides_agent.cpython-312.pyc +0 -0
  120. package/slides_agent/instructions.md +298 -0
  121. package/slides_agent/pptx/SKILL.md +528 -0
  122. package/slides_agent/pptx/html2pptx.md +625 -0
  123. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  124. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  125. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  126. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  127. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  128. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  129. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  130. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  131. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  132. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  133. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  134. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  135. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  136. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  137. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  138. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  139. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  140. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  141. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  142. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  143. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  144. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  145. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  146. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  147. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  148. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  149. package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  150. package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  151. package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  152. package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  153. package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  154. package/slides_agent/pptx/ooxml/schemas/mce/mc.xsd +75 -0
  155. package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  156. package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  157. package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  158. package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  159. package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  160. package/slides_agent/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  161. package/slides_agent/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  162. package/slides_agent/pptx/ooxml/scripts/pack.py +169 -0
  163. package/slides_agent/pptx/ooxml/scripts/unpack.py +29 -0
  164. package/slides_agent/pptx/ooxml/scripts/validate.py +69 -0
  165. package/slides_agent/pptx/ooxml/scripts/validation/__init__.py +15 -0
  166. package/slides_agent/pptx/ooxml/scripts/validation/base.py +951 -0
  167. package/slides_agent/pptx/ooxml/scripts/validation/docx.py +274 -0
  168. package/slides_agent/pptx/ooxml/scripts/validation/pptx.py +315 -0
  169. package/slides_agent/pptx/ooxml/scripts/validation/redlining.py +279 -0
  170. package/slides_agent/pptx/ooxml.md +427 -0
  171. package/slides_agent/pptx/scripts/html2pptx.js +1092 -0
  172. package/slides_agent/pptx/scripts/inventory.py +1020 -0
  173. package/slides_agent/pptx/scripts/rearrange.py +231 -0
  174. package/slides_agent/pptx/scripts/replace.py +385 -0
  175. package/slides_agent/pptx/scripts/thumbnail.py +451 -0
  176. package/slides_agent/slides_agent.py +109 -0
  177. package/slides_agent/test_deck/_theme.css +92 -0
  178. package/slides_agent/test_deck/assets/placeholder.svg +11 -0
  179. package/slides_agent/test_deck/slide_01_title.html +10 -0
  180. package/slides_agent/test_deck/slide_02_image_split.html +23 -0
  181. package/slides_agent/test_deck/slide_03_kpi.html +21 -0
  182. package/slides_agent/tools/ApplyPptxTextReplacements.py +91 -0
  183. package/slides_agent/tools/BuildPptxFromHtmlSlides.py +221 -0
  184. package/slides_agent/tools/CheckSlide.py +218 -0
  185. package/slides_agent/tools/CheckSlideCanvasOverflow.py +221 -0
  186. package/slides_agent/tools/CreateImageMontage.py +261 -0
  187. package/slides_agent/tools/CreatePptxThumbnailGrid.py +168 -0
  188. package/slides_agent/tools/DeleteSlide.py +78 -0
  189. package/slides_agent/tools/DownloadImage.py +79 -0
  190. package/slides_agent/tools/EnsureRasterImage.py +157 -0
  191. package/slides_agent/tools/ExtractPptxTextInventory.py +104 -0
  192. package/slides_agent/tools/GenerateImage.py +189 -0
  193. package/slides_agent/tools/ImageSearch.py +127 -0
  194. package/slides_agent/tools/InsertNewSlides.py +393 -0
  195. package/slides_agent/tools/ManageTheme.py +112 -0
  196. package/slides_agent/tools/ModifySlide.py +563 -0
  197. package/slides_agent/tools/ReadSlide.py +26 -0
  198. package/slides_agent/tools/RearrangePptxSlidesFromTemplate.py +114 -0
  199. package/slides_agent/tools/RestoreSnapshot.py +89 -0
  200. package/slides_agent/tools/SlideScreenshot.py +66 -0
  201. package/slides_agent/tools/__init__.py +54 -0
  202. package/slides_agent/tools/__pycache__/ApplyPptxTextReplacements.cpython-312.pyc +0 -0
  203. package/slides_agent/tools/__pycache__/BuildPptxFromHtmlSlides.cpython-312.pyc +0 -0
  204. package/slides_agent/tools/__pycache__/CheckSlide.cpython-312.pyc +0 -0
  205. package/slides_agent/tools/__pycache__/CheckSlideCanvasOverflow.cpython-312.pyc +0 -0
  206. package/slides_agent/tools/__pycache__/CreateImageMontage.cpython-312.pyc +0 -0
  207. package/slides_agent/tools/__pycache__/CreatePptxThumbnailGrid.cpython-312.pyc +0 -0
  208. package/slides_agent/tools/__pycache__/DeleteSlide.cpython-312.pyc +0 -0
  209. package/slides_agent/tools/__pycache__/DownloadImage.cpython-312.pyc +0 -0
  210. package/slides_agent/tools/__pycache__/EnsureRasterImage.cpython-312.pyc +0 -0
  211. package/slides_agent/tools/__pycache__/ExtractPptxTextInventory.cpython-312.pyc +0 -0
  212. package/slides_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
  213. package/slides_agent/tools/__pycache__/ImageSearch.cpython-312.pyc +0 -0
  214. package/slides_agent/tools/__pycache__/InsertNewSlides.cpython-312.pyc +0 -0
  215. package/slides_agent/tools/__pycache__/ManageTheme.cpython-312.pyc +0 -0
  216. package/slides_agent/tools/__pycache__/ModifySlide.cpython-312.pyc +0 -0
  217. package/slides_agent/tools/__pycache__/ReadSlide.cpython-312.pyc +0 -0
  218. package/slides_agent/tools/__pycache__/RearrangePptxSlidesFromTemplate.cpython-312.pyc +0 -0
  219. package/slides_agent/tools/__pycache__/RestoreSnapshot.cpython-312.pyc +0 -0
  220. package/slides_agent/tools/__pycache__/SlideScreenshot.cpython-312.pyc +0 -0
  221. package/slides_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  222. package/slides_agent/tools/__pycache__/slide_file_utils.cpython-312.pyc +0 -0
  223. package/slides_agent/tools/__pycache__/slide_html_utils.cpython-312.pyc +0 -0
  224. package/slides_agent/tools/__pycache__/template_registry.cpython-312.pyc +0 -0
  225. package/slides_agent/tools/deck_utils.py +31 -0
  226. package/slides_agent/tools/html2pptx_runner.js +1183 -0
  227. package/slides_agent/tools/html_writer_instructions.md +149 -0
  228. package/slides_agent/tools/slide_file_utils.py +108 -0
  229. package/slides_agent/tools/slide_html_utils.py +354 -0
  230. package/slides_agent/tools/template_registry.py +55 -0
  231. package/swarm.py +82 -0
  232. package/video_generation_agent/__init__.py +1 -0
  233. package/video_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
  234. package/video_generation_agent/__pycache__/video_generation_agent.cpython-312.pyc +0 -0
  235. package/video_generation_agent/instructions.md +178 -0
  236. package/video_generation_agent/tools/AddSubtitles.py +425 -0
  237. package/video_generation_agent/tools/CombineImages.py +166 -0
  238. package/video_generation_agent/tools/CombineVideos.py +113 -0
  239. package/video_generation_agent/tools/EditAudio.py +297 -0
  240. package/video_generation_agent/tools/EditImage.py +144 -0
  241. package/video_generation_agent/tools/EditVideoContent.py +369 -0
  242. package/video_generation_agent/tools/GenerateImage.py +133 -0
  243. package/video_generation_agent/tools/GenerateVideo.py +556 -0
  244. package/video_generation_agent/tools/TrimVideo.py +233 -0
  245. package/video_generation_agent/tools/__init__.py +1 -0
  246. package/video_generation_agent/tools/__pycache__/AddSubtitles.cpython-312.pyc +0 -0
  247. package/video_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
  248. package/video_generation_agent/tools/__pycache__/CombineVideos.cpython-312.pyc +0 -0
  249. package/video_generation_agent/tools/__pycache__/EditAudio.cpython-312.pyc +0 -0
  250. package/video_generation_agent/tools/__pycache__/EditImage.cpython-312.pyc +0 -0
  251. package/video_generation_agent/tools/__pycache__/EditVideoContent.cpython-312.pyc +0 -0
  252. package/video_generation_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
  253. package/video_generation_agent/tools/__pycache__/GenerateVideo.cpython-312.pyc +0 -0
  254. package/video_generation_agent/tools/__pycache__/TrimVideo.cpython-312.pyc +0 -0
  255. package/video_generation_agent/tools/utils/__init__.py +1 -0
  256. package/video_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  257. package/video_generation_agent/tools/utils/__pycache__/image_utils.cpython-312.pyc +0 -0
  258. package/video_generation_agent/tools/utils/__pycache__/video_utils.cpython-312.pyc +0 -0
  259. package/video_generation_agent/tools/utils/image_utils.py +174 -0
  260. package/video_generation_agent/tools/utils/video_utils.py +522 -0
  261. package/video_generation_agent/video_generation_agent.py +26 -0
  262. package/virtual_assistant/__init__.py +1 -0
  263. package/virtual_assistant/__pycache__/__init__.cpython-312.pyc +0 -0
  264. package/virtual_assistant/__pycache__/virtual_assistant.cpython-312.pyc +0 -0
  265. package/virtual_assistant/instructions.md +206 -0
  266. package/virtual_assistant/tools/AddLabelToEmail.py +154 -0
  267. package/virtual_assistant/tools/CheckEventsForDate.py +218 -0
  268. package/virtual_assistant/tools/CheckUnreadSlackMessages.py +216 -0
  269. package/virtual_assistant/tools/CreateCalendarEvent.py +261 -0
  270. package/virtual_assistant/tools/DeleteCalendarEvent.py +137 -0
  271. package/virtual_assistant/tools/DeleteDraft.py +95 -0
  272. package/virtual_assistant/tools/DraftEmail.py +239 -0
  273. package/virtual_assistant/tools/EditFile.py +113 -0
  274. package/virtual_assistant/tools/FindEmails.py +330 -0
  275. package/virtual_assistant/tools/GetCurrentTime.py +69 -0
  276. package/virtual_assistant/tools/GetSlackUserInfo.py +117 -0
  277. package/virtual_assistant/tools/ListDirectory.py +113 -0
  278. package/virtual_assistant/tools/ListSkills.py +94 -0
  279. package/virtual_assistant/tools/ManageLabels.py +295 -0
  280. package/virtual_assistant/tools/ProductSearch.py +254 -0
  281. package/virtual_assistant/tools/ReadEmail.py +251 -0
  282. package/virtual_assistant/tools/ReadFile.py +108 -0
  283. package/virtual_assistant/tools/ReadSlackMessages.py +191 -0
  284. package/virtual_assistant/tools/RemoveLabelFromEmail.py +137 -0
  285. package/virtual_assistant/tools/RescheduleCalendarEvent.py +227 -0
  286. package/virtual_assistant/tools/ScholarSearch.py +216 -0
  287. package/virtual_assistant/tools/SendDraft.py +101 -0
  288. package/virtual_assistant/tools/SendSlackMessage.py +148 -0
  289. package/virtual_assistant/tools/WriteFile.py +95 -0
  290. package/virtual_assistant/tools/__init__.py +1 -0
  291. package/virtual_assistant/tools/__pycache__/AddLabelToEmail.cpython-312.pyc +0 -0
  292. package/virtual_assistant/tools/__pycache__/CheckEventsForDate.cpython-312.pyc +0 -0
  293. package/virtual_assistant/tools/__pycache__/CheckUnreadSlackMessages.cpython-312.pyc +0 -0
  294. package/virtual_assistant/tools/__pycache__/CreateCalendarEvent.cpython-312.pyc +0 -0
  295. package/virtual_assistant/tools/__pycache__/DeleteCalendarEvent.cpython-312.pyc +0 -0
  296. package/virtual_assistant/tools/__pycache__/DeleteDraft.cpython-312.pyc +0 -0
  297. package/virtual_assistant/tools/__pycache__/DraftEmail.cpython-312.pyc +0 -0
  298. package/virtual_assistant/tools/__pycache__/EditFile.cpython-312.pyc +0 -0
  299. package/virtual_assistant/tools/__pycache__/FindEmails.cpython-312.pyc +0 -0
  300. package/virtual_assistant/tools/__pycache__/GetCurrentTime.cpython-312.pyc +0 -0
  301. package/virtual_assistant/tools/__pycache__/GetSlackUserInfo.cpython-312.pyc +0 -0
  302. package/virtual_assistant/tools/__pycache__/ListDirectory.cpython-312.pyc +0 -0
  303. package/virtual_assistant/tools/__pycache__/ListSkills.cpython-312.pyc +0 -0
  304. package/virtual_assistant/tools/__pycache__/ManageLabels.cpython-312.pyc +0 -0
  305. package/virtual_assistant/tools/__pycache__/ProductSearch.cpython-312.pyc +0 -0
  306. package/virtual_assistant/tools/__pycache__/ReadEmail.cpython-312.pyc +0 -0
  307. package/virtual_assistant/tools/__pycache__/ReadFile.cpython-312.pyc +0 -0
  308. package/virtual_assistant/tools/__pycache__/ReadSlackMessages.cpython-312.pyc +0 -0
  309. package/virtual_assistant/tools/__pycache__/RemoveLabelFromEmail.cpython-312.pyc +0 -0
  310. package/virtual_assistant/tools/__pycache__/RescheduleCalendarEvent.cpython-312.pyc +0 -0
  311. package/virtual_assistant/tools/__pycache__/ScholarSearch.cpython-312.pyc +0 -0
  312. package/virtual_assistant/tools/__pycache__/SendDraft.cpython-312.pyc +0 -0
  313. package/virtual_assistant/tools/__pycache__/SendSlackMessage.cpython-312.pyc +0 -0
  314. package/virtual_assistant/tools/__pycache__/WriteFile.cpython-312.pyc +0 -0
  315. package/virtual_assistant/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  316. package/virtual_assistant/virtual_assistant.py +52 -0
@@ -0,0 +1,21 @@
1
+ <div class="canvas">
2
+ <div class="title-block">
3
+ <p class="kicker">METRICS</p>
4
+ <h2>KPI layout with three cards</h2>
5
+ <p class="small">Use big numbers and short labels for fast scanning.</p>
6
+ </div>
7
+ <div class="kpi-row">
8
+ <div class="kpi-card">
9
+ <p class="kpi-value">3.2x</p>
10
+ <p class="kpi-label">Faster turnaround</p>
11
+ </div>
12
+ <div class="kpi-card">
13
+ <p class="kpi-value">18%</p>
14
+ <p class="kpi-label">Lower review time</p>
15
+ </div>
16
+ <div class="kpi-card">
17
+ <p class="kpi-value">92</p>
18
+ <p class="kpi-label">Quality score</p>
19
+ </div>
20
+ </div>
21
+ </div>
@@ -0,0 +1,91 @@
1
+ """Apply text replacements to a PowerPoint presentation."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from agency_swarm.tools import BaseTool
7
+ from pydantic import Field
8
+
9
+ # Add pptx/scripts to path for replace module
10
+ PPTX_SCRIPTS_DIR = Path(__file__).parent.parent / "pptx" / "scripts"
11
+ sys.path.insert(0, str(PPTX_SCRIPTS_DIR))
12
+
13
+
14
+ class ApplyPptxTextReplacements(BaseTool):
15
+ """
16
+ Apply text replacements to shapes in a PowerPoint presentation.
17
+
18
+ Takes a PPTX file and a JSON file containing replacement paragraphs,
19
+ then applies the replacements while preserving formatting. All text
20
+ shapes identified in the inventory will have their text cleared unless
21
+ "paragraphs" is specified for that shape in the replacements JSON.
22
+
23
+ The replacements JSON should follow the structure from ExtractPptxTextInventory:
24
+ {
25
+ "slide-0": {
26
+ "shape-0": {
27
+ "paragraphs": [
28
+ {"text": "New title", "bold": true, "alignment": "CENTER"},
29
+ {"text": "Bullet point", "bullet": true, "level": 0}
30
+ ]
31
+ }
32
+ }
33
+ }
34
+
35
+ The tool validates that:
36
+ - All referenced shapes exist in the presentation
37
+ - Text overflow does not worsen after replacements
38
+ - No bullet formatting warnings are triggered
39
+ """
40
+
41
+ input_pptx: str = Field(
42
+ ...,
43
+ description="Path to the input PowerPoint file (.pptx)",
44
+ )
45
+ replacements_json: str = Field(
46
+ ...,
47
+ description="Path to the JSON file containing replacement paragraphs",
48
+ )
49
+ output_pptx: str = Field(
50
+ ...,
51
+ description="Path where the updated presentation will be saved",
52
+ )
53
+
54
+ def run(self) -> str:
55
+ """Apply replacements and save the updated presentation."""
56
+ import sys
57
+ from pathlib import Path as PathLib
58
+
59
+ # Add pptx/scripts to path for replace import
60
+ scripts_dir = PathLib(__file__).parent.parent / "pptx" / "scripts"
61
+ if str(scripts_dir) not in sys.path:
62
+ sys.path.insert(0, str(scripts_dir))
63
+
64
+ from replace import apply_replacements # type: ignore
65
+
66
+ input_path = Path(self.input_pptx)
67
+ json_path = Path(self.replacements_json)
68
+ output_path = Path(self.output_pptx)
69
+
70
+ # Validate inputs
71
+ if not input_path.exists():
72
+ return f"Error: Input file not found: {self.input_pptx}"
73
+ if input_path.suffix.lower() != ".pptx":
74
+ return f"Error: Input must be a PowerPoint file (.pptx), got: {input_path.suffix}"
75
+ if not json_path.exists():
76
+ return f"Error: Replacements JSON not found: {self.replacements_json}"
77
+
78
+ # Ensure output directory exists
79
+ output_path.parent.mkdir(parents=True, exist_ok=True)
80
+
81
+ # Apply replacements
82
+ try:
83
+ apply_replacements(str(input_path), str(json_path), str(output_path))
84
+ return f"Presentation saved to: {output_path}\nReplacements applied successfully"
85
+ except ValueError as e:
86
+ return f"Error: {e}"
87
+
88
+
89
+ if __name__ == "__main__":
90
+ print("ApplyPptxTextReplacements tool definition is valid.")
91
+ print("Requires input PPTX and replacements JSON to test.")
@@ -0,0 +1,221 @@
1
+ """Convert HTML slides to an editable PowerPoint presentation via dom-to-pptx."""
2
+
3
+ import os
4
+ import subprocess
5
+ import tempfile
6
+ from pathlib import Path
7
+ from typing import List, Optional
8
+
9
+ from agency_swarm.tools import BaseTool
10
+ from pydantic import Field
11
+
12
+ from .slide_file_utils import get_project_dir, next_pptx_version
13
+
14
+ RUNNER_JS = Path(__file__).parent / "html2pptx_runner.js"
15
+
16
+
17
+ class BuildPptxFromHtmlSlides(BaseTool):
18
+ """
19
+ Convert HTML slides to a fully editable PowerPoint presentation.
20
+
21
+ Uses dom-to-pptx (via Playwright) to measure every element's exact computed
22
+ position and style, then maps them to native PPTX shapes and text boxes.
23
+ CSS gradients are converted to vector SVGs, inline SVGs are kept as editable
24
+ vectors, custom fonts are auto-embedded, and text remains fully editable.
25
+
26
+ The output file is auto-versioned: if my_deck.pptx already exists the tool
27
+ saves my_deck_v2.pptx, then my_deck_v3.pptx, etc. Previous exports and their
28
+ snapshots are never overwritten.
29
+
30
+ Requires: Node.js with local node_modules (dom-to-pptx, playwright)
31
+ """
32
+
33
+ project_name: str = Field(
34
+ ...,
35
+ description="Presentation project folder name (e.g. 'my_pitch')",
36
+ )
37
+ slide_names: List[str] = Field(
38
+ ...,
39
+ description=(
40
+ "Ordered list of slide names to include, e.g. ['slide_01', 'slide_02']. "
41
+ "Use bare names (without .html) or include the extension — both work."
42
+ ),
43
+ )
44
+ output_filename: str = Field(
45
+ ...,
46
+ description=(
47
+ "Output filename stem, e.g. 'my_deck' (saved as my_deck.pptx inside "
48
+ "the project folder). Including the .pptx extension is also accepted."
49
+ ),
50
+ )
51
+ layout: str = Field(
52
+ default="LAYOUT_16x9_1280",
53
+ description=(
54
+ "Presentation layout: LAYOUT_16x9_1280 (1280x720 HTML), "
55
+ "LAYOUT_16x9_1920 (1920x1080 HTML), LAYOUT_16x9, LAYOUT_4x3, or LAYOUT_16x10"
56
+ ),
57
+ )
58
+ tmp_dir: Optional[str] = Field(
59
+ default=None,
60
+ description="Optional temporary directory for intermediate files",
61
+ )
62
+
63
+ def run(self) -> str:
64
+ """Convert HTML slides to PPTX."""
65
+ project_dir = get_project_dir(self.project_name)
66
+
67
+ html_paths = self._resolve_slide_paths(self.slide_names, project_dir)
68
+ if isinstance(html_paths, str):
69
+ return html_paths # error message
70
+
71
+ if not html_paths:
72
+ return "Error: No slide names provided"
73
+
74
+ valid_layouts = ["LAYOUT_16x9_1280", "LAYOUT_16x9_1920", "LAYOUT_16x9", "LAYOUT_4x3", "LAYOUT_16x10"]
75
+ if self.layout not in valid_layouts:
76
+ return f"Error: Invalid layout '{self.layout}'. Must be one of: {', '.join(valid_layouts)}"
77
+
78
+ output_path = next_pptx_version(self._resolve_output_path(self.output_filename, project_dir))
79
+ output_path.parent.mkdir(parents=True, exist_ok=True)
80
+
81
+ if not self._check_node():
82
+ return "Error: Node.js not found. Please install Node.js."
83
+
84
+ node_modules = Path(__file__).parent.parent.parent / "node_modules"
85
+ if not node_modules.exists():
86
+ return (
87
+ "Error: node_modules not found. Please run 'npm install' in the project root.\n"
88
+ f"Expected location: {node_modules}"
89
+ )
90
+
91
+ if not RUNNER_JS.exists():
92
+ return f"Error: Runner script not found at {RUNNER_JS}"
93
+
94
+ tmp_dir = self.tmp_dir or tempfile.mkdtemp(prefix="html2pptx_")
95
+
96
+ cmd = [
97
+ "node",
98
+ str(RUNNER_JS),
99
+ "--output", str(output_path),
100
+ "--layout", self.layout,
101
+ "--tmp-dir", tmp_dir,
102
+ "--",
103
+ ] + html_paths
104
+
105
+ try:
106
+ kwargs = {
107
+ "capture_output": True,
108
+ "text": True,
109
+ "timeout": 300,
110
+ "cwd": str(Path(__file__).parent.parent.parent),
111
+ }
112
+ if os.name == "nt":
113
+ kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
114
+
115
+ result = subprocess.run(cmd, **kwargs)
116
+
117
+ if result.returncode != 0:
118
+ error_msg = result.stderr.strip() or result.stdout.strip()
119
+ return f"Error converting HTML to PPTX:\n{error_msg}"
120
+
121
+ self._write_snapshots(html_paths, output_path)
122
+ snapshot_dir = output_path.parent / f"{output_path.name}.slides"
123
+ return (
124
+ f"Presentation saved to: {output_path}\n"
125
+ f"Snapshot saved to: {snapshot_dir}\n"
126
+ f"Converted {len(html_paths)} slide(s)"
127
+ )
128
+
129
+ except subprocess.TimeoutExpired:
130
+ return "Error: Conversion timed out after 5 minutes"
131
+ except Exception as e:
132
+ return f"Error running html2pptx: {e}"
133
+
134
+ # ------------------------------------------------------------------
135
+ # Internal helpers
136
+ # ------------------------------------------------------------------
137
+
138
+ def _resolve_slide_paths(self, slide_names: List[str], project_dir: Path) -> list[str] | str:
139
+ """Resolve slide name strings to absolute .html paths."""
140
+ paths = []
141
+ for name in slide_names:
142
+ path = project_dir / (name if name.endswith(".html") else f"{name}.html")
143
+ if not path.exists():
144
+ return f"Error: Slide not found: {name} (looked for {path})"
145
+ paths.append(str(path.absolute()))
146
+ return paths
147
+
148
+ def _resolve_output_path(self, output_filename: str, project_dir: Path) -> Path:
149
+ """Return the full output .pptx path from a stem or filename."""
150
+ stem = Path(output_filename).stem
151
+ return project_dir / f"{stem}.pptx"
152
+
153
+ def _write_snapshots(self, html_paths: list[str], output_pptx: Path) -> None:
154
+ """Write self-contained HTML snapshots to <output>.pptx.slides/1.html, 2.html, …"""
155
+ slides_dir = output_pptx.parent / f"{output_pptx.name}.slides"
156
+ slides_dir.mkdir(parents=True, exist_ok=True)
157
+
158
+ for i, html_path in enumerate(html_paths, start=1):
159
+ src = Path(html_path)
160
+ html = src.read_text(encoding="utf-8")
161
+ html = self._inline_theme_css(html, src.parent)
162
+ (slides_dir / f"{i}.html").write_text(html, encoding="utf-8")
163
+
164
+ def _inline_theme_css(self, html: str, slide_dir: Path) -> str:
165
+ """Inline local <link rel="stylesheet"> tags into sentinel-marked <style> blocks.
166
+
167
+ External URLs (http/https/protocol-relative) are left as-is.
168
+ Local files are inlined as:
169
+ <!-- css-snapshot:<filename>:start -->
170
+ <style>…</style>
171
+ <!-- css-snapshot:<filename>:end -->
172
+ so that RestoreSnapshot can reverse the operation exactly.
173
+ """
174
+ import re
175
+
176
+ def replace_link(match: re.Match) -> str:
177
+ href = match.group(1)
178
+ if href.startswith(("http://", "https://", "//")):
179
+ return match.group(0)
180
+ css_path = (slide_dir / href).resolve()
181
+ if not css_path.exists():
182
+ return match.group(0)
183
+ filename = css_path.name
184
+ css = css_path.read_text(encoding="utf-8")
185
+ return (
186
+ f"<!-- css-snapshot:{filename}:start -->\n"
187
+ f"<style>\n{css}\n</style>\n"
188
+ f"<!-- css-snapshot:{filename}:end -->"
189
+ )
190
+
191
+ return re.sub(
192
+ r'<link\b[^>]*\brel=["\']stylesheet["\'][^>]*\bhref=["\']([^"\']+)["\'][^>]*>',
193
+ replace_link,
194
+ html,
195
+ flags=re.IGNORECASE,
196
+ )
197
+
198
+ def _check_node(self) -> bool:
199
+ """Check if Node.js is available."""
200
+ try:
201
+ kwargs = {
202
+ "capture_output": True,
203
+ "timeout": 5,
204
+ "text": True,
205
+ }
206
+ if os.name == "nt":
207
+ kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
208
+ subprocess.run(["node", "--version"], **kwargs)
209
+ return True
210
+ except (FileNotFoundError, subprocess.TimeoutExpired):
211
+ return False
212
+
213
+
214
+ if __name__ == "__main__":
215
+ tool = BuildPptxFromHtmlSlides(
216
+ project_name="dinosaur_presentation_v2",
217
+ slide_names=["slide_01", "slide_02", "slide_03", "slide_04", "slide_05"],
218
+ output_filename="dinosaur_presentation_v2",
219
+ layout="LAYOUT_16x9_1280",
220
+ )
221
+ print(tool.run())
@@ -0,0 +1,218 @@
1
+ """Render a single slide to image and load it as attachment."""
2
+
3
+ import asyncio
4
+ import inspect
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ import tempfile
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ from agency_swarm.tools import BaseTool, LoadFileAttachment
13
+ from pydantic import Field
14
+
15
+ PPTX_SCRIPTS_DIR = Path(__file__).parent.parent / "pptx" / "scripts"
16
+ sys.path.insert(0, str(PPTX_SCRIPTS_DIR))
17
+
18
+ _SLIDE_VIEWPORT = {"width": 1280, "height": 720}
19
+
20
+
21
+ class CheckSlide(BaseTool):
22
+ """
23
+ Render a single slide (HTML or PPTX) to an image and return a file attachment.
24
+
25
+ Allows you to see the slide so you can inspect it for spacing, alignment, and other issues.
26
+ """
27
+
28
+ slide_path: str = Field(
29
+ ...,
30
+ description="Path to a single HTML slide or a PPTX file",
31
+ )
32
+ output_image_path: Optional[str] = Field(
33
+ default=None,
34
+ description="Optional output image path (.jpg). Defaults next to slide.",
35
+ )
36
+ layout: str = Field(
37
+ default="LAYOUT_16x9_1280",
38
+ description="Layout for HTML slides: LAYOUT_16x9_1280 or LAYOUT_16x9_1920",
39
+ )
40
+ slide_index: int = Field(
41
+ default=1,
42
+ description="1-based slide index when rendering a PPTX with multiple slides",
43
+ )
44
+
45
+ def run(self):
46
+ input_path = Path(self.slide_path)
47
+ if not input_path.exists():
48
+ return f"Error: Slide not found: {self.slide_path}"
49
+
50
+ if input_path.suffix.lower() == ".pptx":
51
+ if not self._check_soffice():
52
+ return "Error: LibreOffice (soffice) not found. Please install LibreOffice."
53
+ if not self._check_pdftoppm():
54
+ return "Error: pdftoppm not found. Please install Poppler (poppler-utils)."
55
+
56
+ try:
57
+ if input_path.suffix.lower() == ".html":
58
+ image_path = self._screenshot_html(input_path)
59
+ elif input_path.suffix.lower() == ".pptx":
60
+ image_path = self._render_pptx_slide(input_path, input_path)
61
+ else:
62
+ return "Error: slide_path must be a .html or .pptx file."
63
+
64
+ attachment = LoadFileAttachment(path=str(image_path))
65
+ return self._run_attachment(attachment)
66
+ except Exception as exc:
67
+ return f"Error checking slide: {exc}"
68
+
69
+ def _screenshot_html(self, html_path: Path) -> Path:
70
+ """Direct Playwright screenshot — fast, no PPTX/PDF round-trip."""
71
+ from playwright.sync_api import sync_playwright
72
+ from PIL import Image
73
+
74
+ output_path = self._resolve_output_path(html_path)
75
+ output_path.parent.mkdir(parents=True, exist_ok=True)
76
+
77
+ with sync_playwright() as pw:
78
+ browser = pw.chromium.launch(headless=True)
79
+ page = browser.new_page(viewport=_SLIDE_VIEWPORT)
80
+ page.goto(html_path.resolve().as_uri(), wait_until="load", timeout=20_000)
81
+ page.wait_for_timeout(800)
82
+ tmp = Path(tempfile.mktemp(suffix=".jpg"))
83
+ page.screenshot(
84
+ path=str(tmp),
85
+ clip={"x": 0, "y": 0, **_SLIDE_VIEWPORT},
86
+ type="jpeg",
87
+ quality=80,
88
+ )
89
+ browser.close()
90
+
91
+ img = Image.open(tmp)
92
+ new_size = (int(img.width * 0.75), int(img.height * 0.75))
93
+ img = img.resize(new_size, Image.Resampling.LANCZOS)
94
+ img.save(output_path, "JPEG", quality=75, optimize=True)
95
+ return output_path
96
+
97
+ def _build_temp_pptx(self, html_path: Path) -> Path:
98
+ if __package__ is None:
99
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
100
+ from tools.BuildPptxFromHtmlSlides import BuildPptxFromHtmlSlides
101
+ else:
102
+ from .BuildPptxFromHtmlSlides import BuildPptxFromHtmlSlides
103
+
104
+ temp_dir = Path(tempfile.mkdtemp(prefix="slide_check_"))
105
+ output_pptx = temp_dir / "slide_check.pptx"
106
+ tool = BuildPptxFromHtmlSlides(
107
+ html_files=[str(html_path)],
108
+ output_pptx=str(output_pptx),
109
+ layout=self.layout,
110
+ )
111
+ result = tool.run()
112
+ if isinstance(result, str) and result.lower().startswith("error"):
113
+ raise RuntimeError(result)
114
+ return output_pptx
115
+
116
+ def _render_pptx_slide(self, pptx_path: Path, source_path: Path) -> Path:
117
+ import sys
118
+ from pathlib import Path as PathLib
119
+
120
+ # Add pptx/scripts to path for thumbnail import
121
+ scripts_dir = PathLib(__file__).parent.parent / "pptx" / "scripts"
122
+ if str(scripts_dir) not in sys.path:
123
+ sys.path.insert(0, str(scripts_dir))
124
+
125
+ from thumbnail import convert_to_images # type: ignore
126
+ from PIL import Image
127
+
128
+ output_path = self._resolve_output_path(source_path)
129
+ with tempfile.TemporaryDirectory() as temp_dir:
130
+ slide_images = convert_to_images(pptx_path, Path(temp_dir), 120)
131
+ if not slide_images:
132
+ raise RuntimeError("No slides found in presentation")
133
+
134
+ if self.slide_index < 1 or self.slide_index > len(slide_images):
135
+ raise RuntimeError(
136
+ f"slide_index out of range (1-{len(slide_images)}): {self.slide_index}"
137
+ )
138
+
139
+ selected = Path(slide_images[self.slide_index - 1])
140
+ output_path.parent.mkdir(parents=True, exist_ok=True)
141
+
142
+ # Compress image to reduce token usage
143
+ img = Image.open(selected)
144
+ # Resize to 75% of original (reduces token usage significantly)
145
+ new_size = (int(img.width * 0.75), int(img.height * 0.75))
146
+ img = img.resize(new_size, Image.Resampling.LANCZOS)
147
+ # Save with moderate JPEG quality
148
+ img.save(output_path, 'JPEG', quality=75, optimize=True)
149
+
150
+ return output_path
151
+
152
+ def _resolve_output_path(self, pptx_path: Path) -> Path:
153
+ if self.output_image_path:
154
+ return Path(self.output_image_path)
155
+
156
+ stem = pptx_path.stem
157
+ if pptx_path.suffix.lower() == ".pptx":
158
+ return pptx_path.with_name(f"{stem}_check.jpg")
159
+ return pptx_path.with_name(f"{stem}_check.jpg")
160
+
161
+ def _check_soffice(self) -> bool:
162
+ try:
163
+ kwargs = {
164
+ "capture_output": True,
165
+ "timeout": 15,
166
+ "text": True,
167
+ "input": "\n",
168
+ }
169
+ if os.name == "nt":
170
+ kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
171
+
172
+ soffice_bin = "soffice.com" if os.name == "nt" else "soffice"
173
+ subprocess.run([soffice_bin, "--version"], **kwargs)
174
+ return True
175
+ except (FileNotFoundError, subprocess.TimeoutExpired):
176
+ return False
177
+
178
+ def _check_pdftoppm(self) -> bool:
179
+ try:
180
+ kwargs = {
181
+ "capture_output": True,
182
+ "timeout": 5,
183
+ "stdin": subprocess.DEVNULL,
184
+ }
185
+ if os.name == "nt":
186
+ kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
187
+
188
+ subprocess.run(["pdftoppm", "-v"], **kwargs)
189
+ return True
190
+ except (FileNotFoundError, subprocess.TimeoutExpired):
191
+ return False
192
+
193
+ def _run_attachment(self, attachment):
194
+ result = attachment.run()
195
+ if not inspect.isawaitable(result):
196
+ return result
197
+
198
+ try:
199
+ asyncio.get_running_loop()
200
+ except RuntimeError:
201
+ return asyncio.run(result)
202
+
203
+ new_loop = asyncio.new_event_loop()
204
+ try:
205
+ return new_loop.run_until_complete(result)
206
+ finally:
207
+ new_loop.close()
208
+
209
+
210
+ if __name__ == "__main__":
211
+ repo_root = Path(__file__).resolve().parents[2]
212
+ test_pptx = repo_root / "mnt/claude_cowork_deck/presentations/claude_cowork_deck_v5_rendered.pptx"
213
+ if test_pptx.exists():
214
+ tool = CheckSlide(slide_path="mnt/claude_cowork_deck/presentations/claude_cowork_deck_v5_rendered.pptx", slide_index=3, output_image_path="mnt/claude_cowork_deck/presentations/_v5_prev3.jpg")
215
+ print(tool.run())
216
+ else:
217
+ print(f"Test file not found: {test_pptx}")
218
+ print("Tool definition is valid.")