@_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,134 @@
1
+ """List all documents in a project."""
2
+
3
+ from agency_swarm.tools import BaseTool
4
+ from pydantic import Field
5
+
6
+ from .utils.doc_file_utils import get_project_dir, get_mnt_dir
7
+
8
+
9
+
10
+ class ListDocuments(BaseTool):
11
+ """
12
+ List all documents in a project folder.
13
+
14
+ Shows all .source.html files (the canonical source) along with their
15
+ associated .docx files and any converted formats (PDF, MD, TXT).
16
+
17
+ Use this tool to:
18
+ - See what documents exist in a project
19
+ - Check file sizes and formats
20
+ - Verify document creation
21
+ - Browse available documents before reading/editing
22
+ """
23
+
24
+ project_name: str = Field(
25
+ ...,
26
+ description="Name of the project folder to list documents from"
27
+ )
28
+
29
+ def run(self):
30
+ """List all documents in the project."""
31
+ try:
32
+ project_dir = get_project_dir(self.project_name)
33
+
34
+ if not project_dir.exists():
35
+ mnt_dir = get_mnt_dir()
36
+ if mnt_dir.exists():
37
+ projects = [d.name for d in mnt_dir.iterdir() if d.is_dir() and (d / "documents").exists()]
38
+ if projects:
39
+ projects_list = "\n - ".join(projects)
40
+ return f"""Error: Project '{self.project_name}' not found.
41
+
42
+ Available projects:
43
+ - {projects_list}"""
44
+ else:
45
+ return f"""Error: Project '{self.project_name}' not found.
46
+
47
+ No projects exist yet. Create a document first using CreateDocument tool."""
48
+ else:
49
+ return f"""Error: Project '{self.project_name}' not found.
50
+
51
+ No projects directory exists yet. Create a document first using CreateDocument tool."""
52
+
53
+ source_files = list(project_dir.glob("*.source.html"))
54
+
55
+ if not source_files:
56
+ return f"""Project: {self.project_name}
57
+ Path: {project_dir}
58
+
59
+ No documents found in this project.
60
+
61
+ Use CreateDocument tool to create a new document."""
62
+
63
+ documents = []
64
+
65
+ for source_file in sorted(source_files):
66
+ doc_name = source_file.name.replace(".source.html", "")
67
+ lines = [f" {len(documents) + 1}. {doc_name}"]
68
+ lines.append(f" Source: {source_file.name} ({source_file.stat().st_size:,} bytes)")
69
+
70
+ # All DOCX exports: base + versioned (_v2, _v3, …), skip Word lock files (~$…)
71
+ docx_exports = sorted(
72
+ f for f in project_dir.glob(f"{doc_name}*.docx")
73
+ if not f.name.startswith("~$")
74
+ )
75
+ if docx_exports:
76
+ lines.append(" Exports:")
77
+ for docx in docx_exports:
78
+ snapshot = project_dir / f"{docx.name}.snapshot.html"
79
+ snapshot_flag = " [snapshot]" if snapshot.exists() else ""
80
+ lines.append(
81
+ f" - {docx.name} ({docx.stat().st_size:,} bytes){snapshot_flag}"
82
+ )
83
+ else:
84
+ lines.append(" Exports: none (use ConvertDocument to generate)")
85
+
86
+ # Other converted formats (PDF, MD, TXT)
87
+ others = []
88
+ for ext, label in [(".pdf", "PDF"), (".md", "Markdown"), (".txt", "TXT")]:
89
+ f = project_dir / f"{doc_name}{ext}"
90
+ if f.exists():
91
+ others.append(f"{label} ({f.stat().st_size:,} bytes)")
92
+ if others:
93
+ lines.append(f" Other formats: {', '.join(others)}")
94
+
95
+ documents.append("\n".join(lines))
96
+
97
+ return (
98
+ f"Project: {self.project_name}\n"
99
+ f"Path: {project_dir}\n\n"
100
+ f"Documents ({len(documents)}):\n\n"
101
+ + "\n\n".join(documents)
102
+ )
103
+
104
+ except Exception as e:
105
+ return f"Error listing documents: {str(e)}"
106
+
107
+
108
+ if __name__ == "__main__":
109
+ print("=" * 70)
110
+ print("TEST: ListDocuments Tool")
111
+ print("=" * 70)
112
+ print()
113
+
114
+ print("Setup: Creating test documents...")
115
+
116
+ # Test 1: List documents in project
117
+ print("Test 1: Listing documents in test_list project...")
118
+ tool = ListDocuments(project_name="test_list")
119
+ print(tool.run())
120
+ print()
121
+
122
+ # Test 2: List documents in non-existent project
123
+ print("Test 2: Attempting to list non-existent project...")
124
+ tool = ListDocuments(project_name="nonexistent_project")
125
+ print(tool.run())
126
+ print()
127
+
128
+ # Test 3: List documents in empty project
129
+ print("Test 3: Listing documents in empty project...")
130
+ empty_dir = get_project_dir("empty_project")
131
+ empty_dir.mkdir(parents=True, exist_ok=True)
132
+
133
+ tool = ListDocuments(project_name="empty_project")
134
+ print(tool.run())
@@ -0,0 +1,247 @@
1
+ """Multi-purpose document editing tool supporting targeted search-and-replace and line operations."""
2
+
3
+ import traceback
4
+ from pathlib import Path
5
+ from typing import Any, Literal, Optional
6
+
7
+ from agency_swarm.tools import BaseTool
8
+ from pydantic import Field
9
+
10
+ from .utils.doc_file_utils import get_project_dir
11
+ from .utils.html_validation import build_unsupported_error, find_unsupported_html
12
+
13
+
14
+ class ModifyDocument(BaseTool):
15
+ """
16
+ Edit a document's HTML source.
17
+
18
+ Supports two modes:
19
+
20
+ **search_and_replace** (preferred for targeted edits):
21
+ Provide a list of {old_content, new_content} pairs. Each old_content is matched
22
+ exactly against the file — like StrReplace. Use a snippet that is unique enough to
23
+ identify the target (can be short or long). Replacements apply sequentially; if one
24
+ fails, the rest are skipped and the file is not modified.
25
+
26
+ **Line operations** (for structural additions/deletions):
27
+ - replace: replace a line range with new content
28
+ - insert: insert content before/after a line
29
+ - delete: remove a line range
30
+
31
+ Always call ViewDocument first to see the current content and line numbers.
32
+ The DOCX is not regenerated on edit — call ConvertDocument when ready to export.
33
+ """
34
+
35
+ project_name: str = Field(
36
+ ...,
37
+ description="Name of the project folder containing the document.",
38
+ )
39
+
40
+ document_name: str = Field(
41
+ ...,
42
+ description="Name of the document to edit (without extension).",
43
+ )
44
+
45
+ operation: Literal["search_and_replace", "replace", "insert", "delete"] = Field(
46
+ ...,
47
+ description=(
48
+ "Edit mode:\n"
49
+ "- 'search_and_replace': batch exact-match replacements (preferred)\n"
50
+ "- 'replace': replace lines start_line–end_line with new_content\n"
51
+ "- 'insert': insert new_content before/after start_line\n"
52
+ "- 'delete': delete lines start_line–end_line"
53
+ ),
54
+ )
55
+
56
+ # --- search_and_replace fields ---
57
+ replacements: Optional[list[dict[str, Any]]] = Field(
58
+ default=None,
59
+ description=(
60
+ "Required for 'search_and_replace'. List of {old_content, new_content} dicts.\n"
61
+ "Each old_content is matched exactly against the file (like StrReplace).\n"
62
+ "Use a snippet that uniquely identifies the target — any length is fine.\n"
63
+ "Replacements apply in order; the first failure stops the batch.\n\n"
64
+ "Example:\n"
65
+ ' [{"old_content": "#C8102E", "new_content": "#DA291C"},\n'
66
+ ' {"old_content": "<h1>Old Title</h1>", "new_content": "<h1>New Title</h1>"}]'
67
+ ),
68
+ )
69
+
70
+ # --- line operation fields ---
71
+ start_line: Optional[int] = Field(
72
+ default=None,
73
+ description="Starting line number (1-based). Required for line operations.",
74
+ )
75
+
76
+ end_line: Optional[int] = Field(
77
+ default=None,
78
+ description="Ending line number (inclusive). Required for 'replace' and 'delete'.",
79
+ )
80
+
81
+ new_content: Optional[str] = Field(
82
+ default=None,
83
+ description="New HTML content. Required for 'replace' and 'insert'.",
84
+ )
85
+
86
+ after: bool = Field(
87
+ default=False,
88
+ description="For 'insert' only: insert AFTER start_line instead of before.",
89
+ )
90
+
91
+ def run(self) -> str:
92
+ try:
93
+ project_dir = get_project_dir(self.project_name)
94
+ if not project_dir.exists():
95
+ return f"Error: Project '{self.project_name}' not found."
96
+
97
+ doc_name = (
98
+ self.document_name.replace(".html", "").replace(".docx", "").replace(".md", "")
99
+ )
100
+ source_path = project_dir / f"{doc_name}.source.html"
101
+ md_path = project_dir / f"{doc_name}.md"
102
+
103
+ if not source_path.exists() and not md_path.exists():
104
+ return f"Error: Document '{doc_name}' not found in project '{self.project_name}'."
105
+
106
+ editing_markdown = not source_path.exists()
107
+ current_content = (
108
+ md_path.read_text(encoding="utf-8")
109
+ if editing_markdown
110
+ else source_path.read_text(encoding="utf-8")
111
+ )
112
+
113
+ if self.operation == "search_and_replace":
114
+ return self._search_and_replace(current_content, source_path, md_path, editing_markdown)
115
+
116
+ lines = current_content.split("\n")
117
+ total_lines = len(lines)
118
+
119
+ if self.operation == "replace":
120
+ return self._replace_lines(lines, total_lines, doc_name, source_path, md_path, editing_markdown)
121
+ if self.operation == "insert":
122
+ return self._insert_lines(lines, total_lines, doc_name, source_path, md_path, editing_markdown)
123
+ if self.operation == "delete":
124
+ return self._delete_lines(lines, total_lines, doc_name, source_path, md_path, editing_markdown)
125
+
126
+ return f"Error: Unknown operation '{self.operation}'."
127
+
128
+ except Exception as e:
129
+ return f"Error modifying document: {type(e).__name__}: {e}\n{traceback.format_exc()}"
130
+
131
+ # ── search_and_replace ────────────────────────────────────────────────────
132
+
133
+ def _search_and_replace(self, content: str, source_path: Path, md_path: Path, editing_markdown: bool) -> str:
134
+ if not self.replacements:
135
+ return "Error: 'replacements' is required for search_and_replace operation."
136
+
137
+ updated = content
138
+ for i, item in enumerate(self.replacements, start=1):
139
+ old = item.get("old_content", "")
140
+ new = item.get("new_content", "")
141
+ if old not in updated:
142
+ snippet = old[:80].replace("\n", "↵")
143
+ return (
144
+ f"Error: replacement #{i} — 'old_content' not found in document.\n"
145
+ f"Snippet: '{snippet}'\n"
146
+ f"Tip: copy a shorter or more unique fragment directly from ViewDocument output and retry."
147
+ )
148
+ updated = updated.replace(old, new, 1)
149
+
150
+ error = self._validate_and_save(updated, source_path, md_path, editing_markdown)
151
+ if error:
152
+ return error
153
+
154
+ count = len(self.replacements)
155
+ return f"Applied {count} replacement{'s' if count != 1 else ''} to '{source_path.name}'."
156
+
157
+ # ── line operations ───────────────────────────────────────────────────────
158
+
159
+ def _replace_lines(self, lines, total_lines, doc_name, source_path, md_path, editing_markdown):
160
+ if not self.new_content:
161
+ return "Error: 'new_content' is required for replace operation."
162
+ if not self.end_line:
163
+ return "Error: 'end_line' is required for replace operation."
164
+ if not self.start_line:
165
+ return "Error: 'start_line' is required for replace operation."
166
+
167
+ if self.start_line < 1 or self.start_line > total_lines:
168
+ return f"Error: Invalid start_line {self.start_line}. Document has {total_lines} lines."
169
+ if self.end_line < self.start_line or self.end_line > total_lines:
170
+ return f"Error: Invalid end_line {self.end_line}. Must be >= start_line and <= {total_lines}."
171
+
172
+ start_idx, end_idx = self.start_line - 1, self.end_line
173
+ lines_removed = end_idx - start_idx
174
+ del lines[start_idx:end_idx]
175
+ lines.insert(start_idx, self.new_content)
176
+
177
+ error = self._validate_and_save("\n".join(lines), source_path, md_path, editing_markdown)
178
+ if error:
179
+ return error
180
+
181
+ net = len(lines) - total_lines
182
+ return (
183
+ f"Replaced lines {self.start_line}–{self.end_line} in '{doc_name}'.\n"
184
+ f"Removed {lines_removed} lines, added {len(self.new_content.splitlines())}. "
185
+ f"Net: {'+' if net >= 0 else ''}{net}. Total: {len(lines)} lines."
186
+ )
187
+
188
+ def _insert_lines(self, lines, total_lines, doc_name, source_path, md_path, editing_markdown):
189
+ if not self.new_content:
190
+ return "Error: 'new_content' is required for insert operation."
191
+ if not self.start_line:
192
+ return "Error: 'start_line' is required for insert operation."
193
+
194
+ if self.start_line < 1 or self.start_line > total_lines + 1:
195
+ return f"Error: Invalid start_line {self.start_line}. Valid range: 1–{total_lines + 1}."
196
+
197
+ insert_idx = self.start_line if self.after else self.start_line - 1
198
+ lines.insert(insert_idx, self.new_content)
199
+
200
+ error = self._validate_and_save("\n".join(lines), source_path, md_path, editing_markdown)
201
+ if error:
202
+ return error
203
+
204
+ position = f"after line {self.start_line}" if self.after else f"before line {self.start_line}"
205
+ return (
206
+ f"Inserted content {position} in '{doc_name}'. "
207
+ f"Added {len(self.new_content.splitlines())} line(s). Total: {len(lines)} lines."
208
+ )
209
+
210
+ def _delete_lines(self, lines, total_lines, doc_name, source_path, md_path, editing_markdown):
211
+ if not self.end_line:
212
+ return "Error: 'end_line' is required for delete operation."
213
+ if not self.start_line:
214
+ return "Error: 'start_line' is required for delete operation."
215
+
216
+ if self.start_line < 1 or self.start_line > total_lines:
217
+ return f"Error: Invalid start_line {self.start_line}. Document has {total_lines} lines."
218
+ if self.end_line < self.start_line or self.end_line > total_lines:
219
+ return f"Error: Invalid end_line {self.end_line}. Must be >= start_line and <= {total_lines}."
220
+
221
+ start_idx, end_idx = self.start_line - 1, self.end_line
222
+ deleted = end_idx - start_idx
223
+ del lines[start_idx:end_idx]
224
+
225
+ error = self._validate_and_save("\n".join(lines), source_path, md_path, editing_markdown)
226
+ if error:
227
+ return error
228
+
229
+ return (
230
+ f"Deleted lines {self.start_line}–{self.end_line} from '{doc_name}'. "
231
+ f"Removed {deleted} line(s). Total: {len(lines)} lines."
232
+ )
233
+
234
+ # ── shared save ──────────────────────────────────────────────────────────
235
+
236
+ def _validate_and_save(self, content: str, source_path: Path, md_path: Path, editing_markdown: bool) -> str | None:
237
+ """Write content to disk. Returns an error string on failure, None on success."""
238
+ if editing_markdown:
239
+ md_path.write_text(content, encoding="utf-8")
240
+ return None
241
+
242
+ issues = find_unsupported_html(content)
243
+ if issues:
244
+ return build_unsupported_error(issues)
245
+
246
+ source_path.write_text(content, encoding="utf-8")
247
+ return None
@@ -0,0 +1,79 @@
1
+ """Restore an HTML document source from a previous DOCX export snapshot."""
2
+
3
+ from pathlib import Path
4
+
5
+ from agency_swarm.tools import BaseTool
6
+ from pydantic import Field
7
+
8
+ from .utils.doc_file_utils import get_project_dir
9
+
10
+
11
+ class RestoreDocument(BaseTool):
12
+ """
13
+ Restore the working HTML source of a document to the state it was in at
14
+ a previous DOCX export.
15
+
16
+ Every time ConvertDocument produces a .docx it automatically saves a
17
+ companion snapshot alongside it:
18
+
19
+ report.docx
20
+ report.docx.snapshot.html ← HTML source at time of that export
21
+ report_v2.docx
22
+ report_v2.docx.snapshot.html
23
+
24
+ This tool reads the snapshot for the requested DOCX version and writes
25
+ it back as the canonical <document>.source.html, ready for further edits
26
+ or re-conversion.
27
+
28
+ To list available versions use ListDocuments — each .docx file in the
29
+ project is one export.
30
+ """
31
+
32
+ project_name: str = Field(
33
+ ...,
34
+ description="Name of the project folder containing the document.",
35
+ )
36
+ docx_filename: str = Field(
37
+ ...,
38
+ description=(
39
+ "Filename of the DOCX export to restore from, e.g. 'report.docx' "
40
+ "or 'report_v2.docx'. The file must exist in the project folder."
41
+ ),
42
+ )
43
+
44
+ def run(self) -> str:
45
+ project_dir = get_project_dir(self.project_name)
46
+ docx_name = (
47
+ self.docx_filename
48
+ if self.docx_filename.endswith(".docx")
49
+ else f"{self.docx_filename}.docx"
50
+ )
51
+ snapshot_path = project_dir / f"{docx_name}.snapshot.html"
52
+
53
+ if not snapshot_path.exists():
54
+ available = sorted(
55
+ p.name for p in project_dir.glob("*.docx.snapshot.html")
56
+ )
57
+ hint = (
58
+ "\nAvailable snapshots:\n" + "\n".join(f" {s}" for s in available)
59
+ if available
60
+ else "\nNo snapshots found in this project."
61
+ )
62
+ return f"Error: No snapshot found for '{docx_name}'.{hint}"
63
+
64
+ doc_name = Path(docx_name).stem
65
+ doc_name = _strip_version(doc_name)
66
+ source_path = project_dir / f"{doc_name}.source.html"
67
+
68
+ source_path.write_text(snapshot_path.read_text(encoding="utf-8"), encoding="utf-8")
69
+
70
+ return (
71
+ f"Restored '{doc_name}' to the version captured in '{docx_name}'.\n"
72
+ f"Working source: {source_path}"
73
+ )
74
+
75
+
76
+ def _strip_version(stem: str) -> str:
77
+ """Remove trailing _vN suffix so report_v2 → report."""
78
+ import re
79
+ return re.sub(r"_v\d+$", "", stem)
@@ -0,0 +1,153 @@
1
+ """View document content from HTML source."""
2
+
3
+ from typing import Optional, List
4
+ from agency_swarm.tools import BaseTool
5
+ from pydantic import Field
6
+
7
+ from .utils.doc_file_utils import get_project_dir
8
+
9
+
10
+ class ViewDocument(BaseTool):
11
+ """
12
+ View the content of an existing document (reads from .source.html file).
13
+
14
+ This tool reads the HTML source file which is the canonical source of truth.
15
+ Optionally specify a line range to view only part of the document.
16
+
17
+ Use this tool to:
18
+ - Read existing document content before editing
19
+ - Check document structure and formatting
20
+ - Verify specific sections of a document
21
+ """
22
+
23
+ project_name: str = Field(
24
+ ...,
25
+ description="Name of the project folder containing the document"
26
+ )
27
+
28
+ document_name: str = Field(
29
+ ...,
30
+ description="Name of the document to view (without extension)"
31
+ )
32
+
33
+ view_range: Optional[List[int]] = Field(
34
+ default=None,
35
+ description="Optional line range [start_line, end_line] (1-based, inclusive). If not provided, shows entire document. Example: [1, 50] shows first 50 lines."
36
+ )
37
+
38
+ def run(self):
39
+ """View document content."""
40
+ try:
41
+ project_dir = get_project_dir(self.project_name)
42
+
43
+ if not project_dir.exists():
44
+ return f"Error: Project '{self.project_name}' not found."
45
+
46
+ doc_name = (
47
+ self.document_name.replace(".html", "")
48
+ .replace(".docx", "")
49
+ .replace(".md", "")
50
+ )
51
+ source_path = project_dir / f"{doc_name}.source.html"
52
+ docx_path = project_dir / f"{doc_name}.docx"
53
+ md_path = project_dir / f"{doc_name}.md"
54
+
55
+ if not source_path.exists() and not md_path.exists():
56
+ return f"Error: Document '{doc_name}' not found in project '{self.project_name}'."
57
+
58
+ if source_path.exists():
59
+ content = source_path.read_text(encoding="utf-8")
60
+ else:
61
+ content = md_path.read_text(encoding="utf-8")
62
+ lines = content.split('\n')
63
+ total_lines = len(lines)
64
+
65
+ if self.view_range:
66
+ if len(self.view_range) != 2:
67
+ return "❌ Error: view_range must be a list of exactly 2 integers [start_line, end_line]"
68
+
69
+ start_line, end_line = self.view_range
70
+
71
+ if start_line < 1 or end_line < start_line or start_line > total_lines:
72
+ return f"Error: Invalid line range [{start_line}, {end_line}]. Document has {total_lines} lines."
73
+
74
+ start_idx = start_line - 1 # 1-based → 0-based
75
+ end_idx = min(end_line, total_lines)
76
+
77
+ selected_lines = lines[start_idx:end_idx]
78
+ content_to_show = '\n'.join(selected_lines)
79
+
80
+ range_info = f"Lines {start_line}-{end_idx} of {total_lines}"
81
+ else:
82
+ content_to_show = content
83
+ range_info = f"All {total_lines} lines"
84
+
85
+ source_size = source_path.stat().st_size if source_path.exists() else md_path.stat().st_size
86
+ docx_exists = docx_path.exists()
87
+ docx_info = (
88
+ f"\n - {docx_path.name} exists"
89
+ if docx_exists
90
+ else f"\n - {docx_path.name} NOT FOUND (needs regeneration)"
91
+ )
92
+
93
+ return f"""# Document: {doc_name}
94
+ Project: {self.project_name}
95
+ Source: {(source_path.name if source_path.exists() else md_path.name)} ({source_size:,} bytes){docx_info}
96
+ Viewing: {range_info}
97
+
98
+ ---
99
+
100
+ {content_to_show}"""
101
+
102
+ except Exception as e:
103
+ return f"Error viewing document: {str(e)}"
104
+
105
+
106
+ if __name__ == "__main__":
107
+ print("=" * 70)
108
+ print("TEST: ViewDocument Tool")
109
+ print("=" * 70)
110
+ print()
111
+
112
+ # First, create a test document to view
113
+ from CreateDocument import CreateDocument
114
+
115
+ print("Setup: Creating test document...")
116
+ html_content = """<!DOCTYPE html>
117
+ <html>
118
+ <head>
119
+ <meta charset="UTF-8">
120
+ <title>Test Document</title>
121
+ </head>
122
+ <body>
123
+ <h1>Test Document Title</h1>
124
+ <p>This is line 1 of content.</p>
125
+ <p>This is line 2 of content.</p>
126
+ <p>This is line 3 of content.</p>
127
+ <p>This is line 4 of content.</p>
128
+ <p>This is line 5 of content.</p>
129
+ <h2>Section 2</h2>
130
+ <p>More content here in section 2.</p>
131
+ <ul>
132
+ <li>Item 1</li>
133
+ <li>Item 2</li>
134
+ <li>Item 3</li>
135
+ </ul>
136
+ </body>
137
+ </html>"""
138
+
139
+ create_tool = CreateDocument(
140
+ project_name="test_view",
141
+ document_name="sample_doc",
142
+ content={"type": "html", "value": html_content},
143
+ overwrite=True
144
+ )
145
+ print(create_tool.run())
146
+ print()
147
+
148
+ tool = ViewDocument(
149
+ project_name="test_view",
150
+ document_name="sample_doc",
151
+ view_range=[1, 10]
152
+ )
153
+ print(tool.run())
@@ -0,0 +1 @@
1
+ """Tools for the agent."""
@@ -0,0 +1 @@
1
+