@_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.
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/bin/openswarm.js +38 -0
- package/config.py +34 -0
- package/data_analyst_agent/.cursor/rules/data_analyst.mdc +43 -0
- package/data_analyst_agent/__init__.py +3 -0
- package/data_analyst_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/data_analyst_agent/__pycache__/data_analyst_agent.cpython-312.pyc +0 -0
- package/data_analyst_agent/data_analyst_agent.py +45 -0
- package/data_analyst_agent/instructions.md +173 -0
- package/data_analyst_agent/test_files/test_file.csv +21 -0
- package/data_analyst_agent/tools/__init__.py +6 -0
- package/deep_research/__init__.py +1 -0
- package/deep_research/__pycache__/__init__.cpython-312.pyc +0 -0
- package/deep_research/__pycache__/deep_research.cpython-312.pyc +0 -0
- package/deep_research/deep_research.py +27 -0
- package/deep_research/instructions.md +104 -0
- package/deep_research/tools/__init__.py +1 -0
- package/docs_agent/__init__.py +3 -0
- package/docs_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/__pycache__/docs_agent.cpython-312.pyc +0 -0
- package/docs_agent/docs_agent.py +61 -0
- package/docs_agent/instructions.md +418 -0
- package/docs_agent/tools/ConvertDocument.py +323 -0
- package/docs_agent/tools/CreateDocument.py +287 -0
- package/docs_agent/tools/ListDocuments.py +134 -0
- package/docs_agent/tools/ModifyDocument.py +247 -0
- package/docs_agent/tools/RestoreDocument.py +79 -0
- package/docs_agent/tools/ViewDocument.py +153 -0
- package/docs_agent/tools/__init__.py +1 -0
- package/docs_agent/tools/__pycache__/ConvertDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/CreateDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ListDocuments.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ModifyDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/RestoreDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ViewDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__init__.py +1 -0
- package/docs_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/doc_file_utils.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_blocks.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_constants.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_core.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_css.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_images.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_page.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_paragraphs.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_playwright.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_selectors.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_shared.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_validation.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/doc_file_utils.py +29 -0
- package/docs_agent/tools/utils/html_docx_blocks.py +262 -0
- package/docs_agent/tools/utils/html_docx_constants.py +78 -0
- package/docs_agent/tools/utils/html_docx_core.py +138 -0
- package/docs_agent/tools/utils/html_docx_css.py +262 -0
- package/docs_agent/tools/utils/html_docx_images.py +293 -0
- package/docs_agent/tools/utils/html_docx_page.py +185 -0
- package/docs_agent/tools/utils/html_docx_paragraphs.py +342 -0
- package/docs_agent/tools/utils/html_docx_playwright.py +184 -0
- package/docs_agent/tools/utils/html_docx_selectors.py +196 -0
- package/docs_agent/tools/utils/html_docx_shared.py +23 -0
- package/docs_agent/tools/utils/html_docx_tables.py +942 -0
- package/docs_agent/tools/utils/html_validation.py +102 -0
- package/helpers.py +59 -0
- package/image_generation_agent/__init__.py +1 -0
- package/image_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/image_generation_agent/__pycache__/image_generation_agent.cpython-312.pyc +0 -0
- package/image_generation_agent/image_generation_agent.py +31 -0
- package/image_generation_agent/instructions.md +80 -0
- package/image_generation_agent/tools/CombineImages.py +211 -0
- package/image_generation_agent/tools/EditImages.py +200 -0
- package/image_generation_agent/tools/GenerateImages.py +184 -0
- package/image_generation_agent/tools/RemoveBackground.py +108 -0
- package/image_generation_agent/tools/__init__.py +2 -0
- package/image_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/EditImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/GenerateImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/RemoveBackground.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/__init__.py +2 -0
- package/image_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/__pycache__/image_io.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/image_io.py +308 -0
- package/onboard.py +325 -0
- package/orchestrator/__init__.py +3 -0
- package/orchestrator/__pycache__/__init__.cpython-312.pyc +0 -0
- package/orchestrator/__pycache__/orchestrator.cpython-312.pyc +0 -0
- package/orchestrator/instructions.md +90 -0
- package/orchestrator/orchestrator.py +33 -0
- package/package.json +49 -0
- package/patches/__init__.py +1 -0
- package/patches/__pycache__/__init__.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_agency_swarm_dual_comms.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_file_attachment_refs.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_ipython_interpreter_composio.cpython-312.pyc +0 -0
- package/patches/dom-to-pptx+1.1.5.patch +133440 -0
- package/patches/patch_agency_swarm_dual_comms.py +199 -0
- package/patches/patch_file_attachment_refs.py +74 -0
- package/patches/patch_ipython_interpreter_composio.py +54 -0
- package/pyproject.toml +67 -0
- package/run.py +343 -0
- package/server.py +26 -0
- package/shared_instructions.md +119 -0
- package/shared_tools/CopyFile.py +68 -0
- package/shared_tools/ExecuteTool.py +184 -0
- package/shared_tools/FindTools.py +101 -0
- package/shared_tools/ManageConnections.py +43 -0
- package/shared_tools/SearchTools.py +44 -0
- package/shared_tools/__init__.py +7 -0
- package/shared_tools/__pycache__/CopyFile.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/ExecuteTool.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/FindTools.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/ManageConnections.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/SearchTools.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/.cursor/rules/slides-agent-workflow.mdc +9 -0
- package/slides_agent/__init__.py +1 -0
- package/slides_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/__pycache__/slides_agent.cpython-312.pyc +0 -0
- package/slides_agent/instructions.md +298 -0
- package/slides_agent/pptx/SKILL.md +528 -0
- package/slides_agent/pptx/html2pptx.md +625 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/slides_agent/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/slides_agent/pptx/ooxml/scripts/pack.py +169 -0
- package/slides_agent/pptx/ooxml/scripts/unpack.py +29 -0
- package/slides_agent/pptx/ooxml/scripts/validate.py +69 -0
- package/slides_agent/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/slides_agent/pptx/ooxml/scripts/validation/base.py +951 -0
- package/slides_agent/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/slides_agent/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/slides_agent/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/slides_agent/pptx/ooxml.md +427 -0
- package/slides_agent/pptx/scripts/html2pptx.js +1092 -0
- package/slides_agent/pptx/scripts/inventory.py +1020 -0
- package/slides_agent/pptx/scripts/rearrange.py +231 -0
- package/slides_agent/pptx/scripts/replace.py +385 -0
- package/slides_agent/pptx/scripts/thumbnail.py +451 -0
- package/slides_agent/slides_agent.py +109 -0
- package/slides_agent/test_deck/_theme.css +92 -0
- package/slides_agent/test_deck/assets/placeholder.svg +11 -0
- package/slides_agent/test_deck/slide_01_title.html +10 -0
- package/slides_agent/test_deck/slide_02_image_split.html +23 -0
- package/slides_agent/test_deck/slide_03_kpi.html +21 -0
- package/slides_agent/tools/ApplyPptxTextReplacements.py +91 -0
- package/slides_agent/tools/BuildPptxFromHtmlSlides.py +221 -0
- package/slides_agent/tools/CheckSlide.py +218 -0
- package/slides_agent/tools/CheckSlideCanvasOverflow.py +221 -0
- package/slides_agent/tools/CreateImageMontage.py +261 -0
- package/slides_agent/tools/CreatePptxThumbnailGrid.py +168 -0
- package/slides_agent/tools/DeleteSlide.py +78 -0
- package/slides_agent/tools/DownloadImage.py +79 -0
- package/slides_agent/tools/EnsureRasterImage.py +157 -0
- package/slides_agent/tools/ExtractPptxTextInventory.py +104 -0
- package/slides_agent/tools/GenerateImage.py +189 -0
- package/slides_agent/tools/ImageSearch.py +127 -0
- package/slides_agent/tools/InsertNewSlides.py +393 -0
- package/slides_agent/tools/ManageTheme.py +112 -0
- package/slides_agent/tools/ModifySlide.py +563 -0
- package/slides_agent/tools/ReadSlide.py +26 -0
- package/slides_agent/tools/RearrangePptxSlidesFromTemplate.py +114 -0
- package/slides_agent/tools/RestoreSnapshot.py +89 -0
- package/slides_agent/tools/SlideScreenshot.py +66 -0
- package/slides_agent/tools/__init__.py +54 -0
- package/slides_agent/tools/__pycache__/ApplyPptxTextReplacements.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/BuildPptxFromHtmlSlides.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CheckSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CheckSlideCanvasOverflow.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CreateImageMontage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CreatePptxThumbnailGrid.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/DeleteSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/DownloadImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/EnsureRasterImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ExtractPptxTextInventory.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ImageSearch.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/InsertNewSlides.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ManageTheme.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ModifySlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ReadSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/RearrangePptxSlidesFromTemplate.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/RestoreSnapshot.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/SlideScreenshot.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/slide_file_utils.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/slide_html_utils.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/template_registry.cpython-312.pyc +0 -0
- package/slides_agent/tools/deck_utils.py +31 -0
- package/slides_agent/tools/html2pptx_runner.js +1183 -0
- package/slides_agent/tools/html_writer_instructions.md +149 -0
- package/slides_agent/tools/slide_file_utils.py +108 -0
- package/slides_agent/tools/slide_html_utils.py +354 -0
- package/slides_agent/tools/template_registry.py +55 -0
- package/swarm.py +82 -0
- package/video_generation_agent/__init__.py +1 -0
- package/video_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/video_generation_agent/__pycache__/video_generation_agent.cpython-312.pyc +0 -0
- package/video_generation_agent/instructions.md +178 -0
- package/video_generation_agent/tools/AddSubtitles.py +425 -0
- package/video_generation_agent/tools/CombineImages.py +166 -0
- package/video_generation_agent/tools/CombineVideos.py +113 -0
- package/video_generation_agent/tools/EditAudio.py +297 -0
- package/video_generation_agent/tools/EditImage.py +144 -0
- package/video_generation_agent/tools/EditVideoContent.py +369 -0
- package/video_generation_agent/tools/GenerateImage.py +133 -0
- package/video_generation_agent/tools/GenerateVideo.py +556 -0
- package/video_generation_agent/tools/TrimVideo.py +233 -0
- package/video_generation_agent/tools/__init__.py +1 -0
- package/video_generation_agent/tools/__pycache__/AddSubtitles.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/CombineVideos.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditAudio.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditImage.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditVideoContent.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/GenerateVideo.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/TrimVideo.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__init__.py +1 -0
- package/video_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__pycache__/image_utils.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__pycache__/video_utils.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/image_utils.py +174 -0
- package/video_generation_agent/tools/utils/video_utils.py +522 -0
- package/video_generation_agent/video_generation_agent.py +26 -0
- package/virtual_assistant/__init__.py +1 -0
- package/virtual_assistant/__pycache__/__init__.cpython-312.pyc +0 -0
- package/virtual_assistant/__pycache__/virtual_assistant.cpython-312.pyc +0 -0
- package/virtual_assistant/instructions.md +206 -0
- package/virtual_assistant/tools/AddLabelToEmail.py +154 -0
- package/virtual_assistant/tools/CheckEventsForDate.py +218 -0
- package/virtual_assistant/tools/CheckUnreadSlackMessages.py +216 -0
- package/virtual_assistant/tools/CreateCalendarEvent.py +261 -0
- package/virtual_assistant/tools/DeleteCalendarEvent.py +137 -0
- package/virtual_assistant/tools/DeleteDraft.py +95 -0
- package/virtual_assistant/tools/DraftEmail.py +239 -0
- package/virtual_assistant/tools/EditFile.py +113 -0
- package/virtual_assistant/tools/FindEmails.py +330 -0
- package/virtual_assistant/tools/GetCurrentTime.py +69 -0
- package/virtual_assistant/tools/GetSlackUserInfo.py +117 -0
- package/virtual_assistant/tools/ListDirectory.py +113 -0
- package/virtual_assistant/tools/ListSkills.py +94 -0
- package/virtual_assistant/tools/ManageLabels.py +295 -0
- package/virtual_assistant/tools/ProductSearch.py +254 -0
- package/virtual_assistant/tools/ReadEmail.py +251 -0
- package/virtual_assistant/tools/ReadFile.py +108 -0
- package/virtual_assistant/tools/ReadSlackMessages.py +191 -0
- package/virtual_assistant/tools/RemoveLabelFromEmail.py +137 -0
- package/virtual_assistant/tools/RescheduleCalendarEvent.py +227 -0
- package/virtual_assistant/tools/ScholarSearch.py +216 -0
- package/virtual_assistant/tools/SendDraft.py +101 -0
- package/virtual_assistant/tools/SendSlackMessage.py +148 -0
- package/virtual_assistant/tools/WriteFile.py +95 -0
- package/virtual_assistant/tools/__init__.py +1 -0
- package/virtual_assistant/tools/__pycache__/AddLabelToEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CheckEventsForDate.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CheckUnreadSlackMessages.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CreateCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DeleteCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DeleteDraft.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DraftEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/EditFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/FindEmails.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/GetCurrentTime.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/GetSlackUserInfo.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ListDirectory.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ListSkills.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ManageLabels.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ProductSearch.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadSlackMessages.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/RemoveLabelFromEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/RescheduleCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ScholarSearch.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/SendDraft.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/SendSlackMessage.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/WriteFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- 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."""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
Binary file
|
|
Binary file
|