scientific-writer 2.3.1__py3-none-any.whl
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.
- scientific_writer/.claude/WRITER.md +822 -0
- scientific_writer/.claude/settings.local.json +30 -0
- scientific_writer/.claude/skills/citation-management/SKILL.md +1046 -0
- scientific_writer/.claude/skills/citation-management/assets/bibtex_template.bib +264 -0
- scientific_writer/.claude/skills/citation-management/assets/citation_checklist.md +386 -0
- scientific_writer/.claude/skills/citation-management/references/bibtex_formatting.md +908 -0
- scientific_writer/.claude/skills/citation-management/references/citation_validation.md +794 -0
- scientific_writer/.claude/skills/citation-management/references/google_scholar_search.md +725 -0
- scientific_writer/.claude/skills/citation-management/references/metadata_extraction.md +870 -0
- scientific_writer/.claude/skills/citation-management/references/pubmed_search.md +839 -0
- scientific_writer/.claude/skills/citation-management/scripts/doi_to_bibtex.py +204 -0
- scientific_writer/.claude/skills/citation-management/scripts/extract_metadata.py +569 -0
- scientific_writer/.claude/skills/citation-management/scripts/format_bibtex.py +349 -0
- scientific_writer/.claude/skills/citation-management/scripts/search_google_scholar.py +282 -0
- scientific_writer/.claude/skills/citation-management/scripts/search_pubmed.py +398 -0
- scientific_writer/.claude/skills/citation-management/scripts/validate_citations.py +497 -0
- scientific_writer/.claude/skills/clinical-reports/IMPLEMENTATION_SUMMARY.md +641 -0
- scientific_writer/.claude/skills/clinical-reports/README.md +236 -0
- scientific_writer/.claude/skills/clinical-reports/SKILL.md +1088 -0
- scientific_writer/.claude/skills/clinical-reports/assets/case_report_template.md +352 -0
- scientific_writer/.claude/skills/clinical-reports/assets/clinical_trial_csr_template.md +353 -0
- scientific_writer/.claude/skills/clinical-reports/assets/clinical_trial_sae_template.md +359 -0
- scientific_writer/.claude/skills/clinical-reports/assets/consult_note_template.md +305 -0
- scientific_writer/.claude/skills/clinical-reports/assets/discharge_summary_template.md +453 -0
- scientific_writer/.claude/skills/clinical-reports/assets/hipaa_compliance_checklist.md +395 -0
- scientific_writer/.claude/skills/clinical-reports/assets/history_physical_template.md +305 -0
- scientific_writer/.claude/skills/clinical-reports/assets/lab_report_template.md +309 -0
- scientific_writer/.claude/skills/clinical-reports/assets/pathology_report_template.md +249 -0
- scientific_writer/.claude/skills/clinical-reports/assets/quality_checklist.md +338 -0
- scientific_writer/.claude/skills/clinical-reports/assets/radiology_report_template.md +318 -0
- scientific_writer/.claude/skills/clinical-reports/assets/soap_note_template.md +253 -0
- scientific_writer/.claude/skills/clinical-reports/references/case_report_guidelines.md +570 -0
- scientific_writer/.claude/skills/clinical-reports/references/clinical_trial_reporting.md +693 -0
- scientific_writer/.claude/skills/clinical-reports/references/data_presentation.md +530 -0
- scientific_writer/.claude/skills/clinical-reports/references/diagnostic_reports_standards.md +629 -0
- scientific_writer/.claude/skills/clinical-reports/references/medical_terminology.md +588 -0
- scientific_writer/.claude/skills/clinical-reports/references/patient_documentation.md +744 -0
- scientific_writer/.claude/skills/clinical-reports/references/peer_review_standards.md +585 -0
- scientific_writer/.claude/skills/clinical-reports/references/regulatory_compliance.md +577 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/check_deidentification.py +346 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/compliance_checker.py +78 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/extract_clinical_data.py +102 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/format_adverse_events.py +103 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/generate_report_template.py +163 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/terminology_validator.py +133 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/validate_case_report.py +334 -0
- scientific_writer/.claude/skills/clinical-reports/scripts/validate_trial_report.py +89 -0
- scientific_writer/.claude/skills/document-skills/docx/LICENSE.txt +30 -0
- scientific_writer/.claude/skills/document-skills/docx/SKILL.md +197 -0
- scientific_writer/.claude/skills/document-skills/docx/docx-js.md +350 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/pack.py +159 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/unpack.py +29 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/validate.py +69 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/validation/__init__.py +15 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/validation/base.py +951 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/validation/docx.py +274 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/validation/pptx.py +315 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml/scripts/validation/redlining.py +279 -0
- scientific_writer/.claude/skills/document-skills/docx/ooxml.md +610 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/__init__.py +1 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/document.py +1276 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/templates/comments.xml +3 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/templates/commentsExtended.xml +3 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/templates/commentsIds.xml +3 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/templates/people.xml +3 -0
- scientific_writer/.claude/skills/document-skills/docx/scripts/utilities.py +374 -0
- scientific_writer/.claude/skills/document-skills/pdf/LICENSE.txt +30 -0
- scientific_writer/.claude/skills/document-skills/pdf/SKILL.md +294 -0
- scientific_writer/.claude/skills/document-skills/pdf/forms.md +205 -0
- scientific_writer/.claude/skills/document-skills/pdf/reference.md +612 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/check_bounding_boxes.py +70 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/check_fillable_fields.py +12 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/create_validation_image.py +41 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/extract_form_field_info.py +152 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/fill_fillable_fields.py +114 -0
- scientific_writer/.claude/skills/document-skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- scientific_writer/.claude/skills/document-skills/pptx/LICENSE.txt +30 -0
- scientific_writer/.claude/skills/document-skills/pptx/SKILL.md +484 -0
- scientific_writer/.claude/skills/document-skills/pptx/html2pptx.md +625 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/pack.py +159 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/unpack.py +29 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/validate.py +69 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/validation/base.py +951 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/validation/docx.py +274 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
- scientific_writer/.claude/skills/document-skills/pptx/ooxml.md +427 -0
- scientific_writer/.claude/skills/document-skills/pptx/scripts/html2pptx.js +979 -0
- scientific_writer/.claude/skills/document-skills/pptx/scripts/inventory.py +1020 -0
- scientific_writer/.claude/skills/document-skills/pptx/scripts/rearrange.py +231 -0
- scientific_writer/.claude/skills/document-skills/pptx/scripts/replace.py +385 -0
- scientific_writer/.claude/skills/document-skills/pptx/scripts/thumbnail.py +450 -0
- scientific_writer/.claude/skills/document-skills/xlsx/LICENSE.txt +30 -0
- scientific_writer/.claude/skills/document-skills/xlsx/SKILL.md +289 -0
- scientific_writer/.claude/skills/document-skills/xlsx/recalc.py +178 -0
- scientific_writer/.claude/skills/hypothesis-generation/SKILL.md +155 -0
- scientific_writer/.claude/skills/hypothesis-generation/assets/hypothesis_output_template.md +302 -0
- scientific_writer/.claude/skills/hypothesis-generation/references/experimental_design_patterns.md +327 -0
- scientific_writer/.claude/skills/hypothesis-generation/references/hypothesis_quality_criteria.md +196 -0
- scientific_writer/.claude/skills/hypothesis-generation/references/literature_search_strategies.md +505 -0
- scientific_writer/.claude/skills/latex-posters/README.md +417 -0
- scientific_writer/.claude/skills/latex-posters/SKILL.md +919 -0
- scientific_writer/.claude/skills/latex-posters/assets/baposter_template.tex +257 -0
- scientific_writer/.claude/skills/latex-posters/assets/beamerposter_template.tex +244 -0
- scientific_writer/.claude/skills/latex-posters/assets/poster_quality_checklist.md +358 -0
- scientific_writer/.claude/skills/latex-posters/assets/tikzposter_template.tex +251 -0
- scientific_writer/.claude/skills/latex-posters/references/latex_poster_packages.md +745 -0
- scientific_writer/.claude/skills/latex-posters/references/poster_content_guide.md +748 -0
- scientific_writer/.claude/skills/latex-posters/references/poster_design_principles.md +806 -0
- scientific_writer/.claude/skills/latex-posters/references/poster_layout_design.md +900 -0
- scientific_writer/.claude/skills/latex-posters/scripts/review_poster.sh +214 -0
- scientific_writer/.claude/skills/literature-review/SKILL.md +546 -0
- scientific_writer/.claude/skills/literature-review/assets/review_template.md +412 -0
- scientific_writer/.claude/skills/literature-review/references/citation_styles.md +166 -0
- scientific_writer/.claude/skills/literature-review/references/database_strategies.md +381 -0
- scientific_writer/.claude/skills/literature-review/scripts/generate_pdf.py +176 -0
- scientific_writer/.claude/skills/literature-review/scripts/search_databases.py +303 -0
- scientific_writer/.claude/skills/literature-review/scripts/verify_citations.py +222 -0
- scientific_writer/.claude/skills/markitdown/INSTALLATION_GUIDE.md +318 -0
- scientific_writer/.claude/skills/markitdown/LICENSE.txt +22 -0
- scientific_writer/.claude/skills/markitdown/OPENROUTER_INTEGRATION.md +359 -0
- scientific_writer/.claude/skills/markitdown/QUICK_REFERENCE.md +309 -0
- scientific_writer/.claude/skills/markitdown/README.md +184 -0
- scientific_writer/.claude/skills/markitdown/SKILL.md +450 -0
- scientific_writer/.claude/skills/markitdown/SKILL_SUMMARY.md +307 -0
- scientific_writer/.claude/skills/markitdown/assets/example_usage.md +463 -0
- scientific_writer/.claude/skills/markitdown/references/api_reference.md +399 -0
- scientific_writer/.claude/skills/markitdown/references/file_formats.md +542 -0
- scientific_writer/.claude/skills/markitdown/scripts/batch_convert.py +228 -0
- scientific_writer/.claude/skills/markitdown/scripts/convert_literature.py +283 -0
- scientific_writer/.claude/skills/markitdown/scripts/convert_with_ai.py +243 -0
- scientific_writer/.claude/skills/paper-2-web/SKILL.md +455 -0
- scientific_writer/.claude/skills/paper-2-web/references/installation.md +141 -0
- scientific_writer/.claude/skills/paper-2-web/references/paper2poster.md +346 -0
- scientific_writer/.claude/skills/paper-2-web/references/paper2video.md +305 -0
- scientific_writer/.claude/skills/paper-2-web/references/paper2web.md +187 -0
- scientific_writer/.claude/skills/paper-2-web/references/usage_examples.md +436 -0
- scientific_writer/.claude/skills/peer-review/SKILL.md +375 -0
- scientific_writer/.claude/skills/peer-review/references/common_issues.md +552 -0
- scientific_writer/.claude/skills/peer-review/references/reporting_standards.md +290 -0
- scientific_writer/.claude/skills/research-grants/README.md +285 -0
- scientific_writer/.claude/skills/research-grants/SKILL.md +896 -0
- scientific_writer/.claude/skills/research-grants/assets/budget_justification_template.md +453 -0
- scientific_writer/.claude/skills/research-grants/assets/nih_specific_aims_template.md +166 -0
- scientific_writer/.claude/skills/research-grants/assets/nsf_project_summary_template.md +92 -0
- scientific_writer/.claude/skills/research-grants/references/broader_impacts.md +392 -0
- scientific_writer/.claude/skills/research-grants/references/darpa_guidelines.md +636 -0
- scientific_writer/.claude/skills/research-grants/references/doe_guidelines.md +586 -0
- scientific_writer/.claude/skills/research-grants/references/nih_guidelines.md +851 -0
- scientific_writer/.claude/skills/research-grants/references/nsf_guidelines.md +570 -0
- scientific_writer/.claude/skills/research-grants/references/specific_aims_guide.md +458 -0
- scientific_writer/.claude/skills/research-lookup/README.md +116 -0
- scientific_writer/.claude/skills/research-lookup/SKILL.md +443 -0
- scientific_writer/.claude/skills/research-lookup/examples.py +174 -0
- scientific_writer/.claude/skills/research-lookup/lookup.py +93 -0
- scientific_writer/.claude/skills/research-lookup/research_lookup.py +335 -0
- scientific_writer/.claude/skills/research-lookup/scripts/research_lookup.py +261 -0
- scientific_writer/.claude/skills/scholar-evaluation/SKILL.md +254 -0
- scientific_writer/.claude/skills/scholar-evaluation/references/evaluation_framework.md +663 -0
- scientific_writer/.claude/skills/scholar-evaluation/scripts/calculate_scores.py +378 -0
- scientific_writer/.claude/skills/scientific-critical-thinking/SKILL.md +530 -0
- scientific_writer/.claude/skills/scientific-critical-thinking/references/common_biases.md +364 -0
- scientific_writer/.claude/skills/scientific-critical-thinking/references/evidence_hierarchy.md +484 -0
- scientific_writer/.claude/skills/scientific-critical-thinking/references/experimental_design.md +496 -0
- scientific_writer/.claude/skills/scientific-critical-thinking/references/logical_fallacies.md +478 -0
- scientific_writer/.claude/skills/scientific-critical-thinking/references/scientific_method.md +169 -0
- scientific_writer/.claude/skills/scientific-critical-thinking/references/statistical_pitfalls.md +506 -0
- scientific_writer/.claude/skills/scientific-schematics/SKILL.md +2035 -0
- scientific_writer/.claude/skills/scientific-schematics/assets/block_diagram_template.tex +199 -0
- scientific_writer/.claude/skills/scientific-schematics/assets/circuit_template.tex +159 -0
- scientific_writer/.claude/skills/scientific-schematics/assets/flowchart_template.tex +161 -0
- scientific_writer/.claude/skills/scientific-schematics/assets/pathway_template.tex +162 -0
- scientific_writer/.claude/skills/scientific-schematics/assets/tikz_styles.tex +422 -0
- scientific_writer/.claude/skills/scientific-schematics/references/best_practices.md +562 -0
- scientific_writer/.claude/skills/scientific-schematics/references/diagram_types.md +637 -0
- scientific_writer/.claude/skills/scientific-schematics/references/python_libraries.md +791 -0
- scientific_writer/.claude/skills/scientific-schematics/references/tikz_guide.md +734 -0
- scientific_writer/.claude/skills/scientific-schematics/scripts/circuit_generator.py +307 -0
- scientific_writer/.claude/skills/scientific-schematics/scripts/compile_tikz.py +292 -0
- scientific_writer/.claude/skills/scientific-schematics/scripts/generate_flowchart.py +281 -0
- scientific_writer/.claude/skills/scientific-schematics/scripts/pathway_diagram.py +406 -0
- scientific_writer/.claude/skills/scientific-writing/SKILL.md +443 -0
- scientific_writer/.claude/skills/scientific-writing/references/citation_styles.md +720 -0
- scientific_writer/.claude/skills/scientific-writing/references/figures_tables.md +806 -0
- scientific_writer/.claude/skills/scientific-writing/references/imrad_structure.md +658 -0
- scientific_writer/.claude/skills/scientific-writing/references/reporting_guidelines.md +748 -0
- scientific_writer/.claude/skills/scientific-writing/references/writing_principles.md +824 -0
- scientific_writer/.claude/skills/treatment-plans/README.md +488 -0
- scientific_writer/.claude/skills/treatment-plans/SKILL.md +1536 -0
- scientific_writer/.claude/skills/treatment-plans/assets/STYLING_QUICK_REFERENCE.md +185 -0
- scientific_writer/.claude/skills/treatment-plans/assets/chronic_disease_management_plan.tex +665 -0
- scientific_writer/.claude/skills/treatment-plans/assets/general_medical_treatment_plan.tex +547 -0
- scientific_writer/.claude/skills/treatment-plans/assets/medical_treatment_plan.sty +222 -0
- scientific_writer/.claude/skills/treatment-plans/assets/mental_health_treatment_plan.tex +774 -0
- scientific_writer/.claude/skills/treatment-plans/assets/one_page_treatment_plan.tex +193 -0
- scientific_writer/.claude/skills/treatment-plans/assets/pain_management_plan.tex +799 -0
- scientific_writer/.claude/skills/treatment-plans/assets/perioperative_care_plan.tex +753 -0
- scientific_writer/.claude/skills/treatment-plans/assets/quality_checklist.md +471 -0
- scientific_writer/.claude/skills/treatment-plans/assets/rehabilitation_treatment_plan.tex +756 -0
- scientific_writer/.claude/skills/treatment-plans/references/goal_setting_frameworks.md +411 -0
- scientific_writer/.claude/skills/treatment-plans/references/intervention_guidelines.md +507 -0
- scientific_writer/.claude/skills/treatment-plans/references/regulatory_compliance.md +476 -0
- scientific_writer/.claude/skills/treatment-plans/references/specialty_specific_guidelines.md +655 -0
- scientific_writer/.claude/skills/treatment-plans/references/treatment_plan_standards.md +485 -0
- scientific_writer/.claude/skills/treatment-plans/scripts/check_completeness.py +318 -0
- scientific_writer/.claude/skills/treatment-plans/scripts/generate_template.py +244 -0
- scientific_writer/.claude/skills/treatment-plans/scripts/timeline_generator.py +369 -0
- scientific_writer/.claude/skills/treatment-plans/scripts/validate_treatment_plan.py +367 -0
- scientific_writer/.claude/skills/venue-templates/SKILL.md +590 -0
- scientific_writer/.claude/skills/venue-templates/assets/grants/nih_specific_aims.tex +235 -0
- scientific_writer/.claude/skills/venue-templates/assets/grants/nsf_proposal_template.tex +375 -0
- scientific_writer/.claude/skills/venue-templates/assets/journals/nature_article.tex +171 -0
- scientific_writer/.claude/skills/venue-templates/assets/journals/neurips_article.tex +283 -0
- scientific_writer/.claude/skills/venue-templates/assets/journals/plos_one.tex +317 -0
- scientific_writer/.claude/skills/venue-templates/assets/posters/beamerposter_academic.tex +311 -0
- scientific_writer/.claude/skills/venue-templates/references/conferences_formatting.md +564 -0
- scientific_writer/.claude/skills/venue-templates/references/grants_requirements.md +787 -0
- scientific_writer/.claude/skills/venue-templates/references/journals_formatting.md +486 -0
- scientific_writer/.claude/skills/venue-templates/references/posters_guidelines.md +628 -0
- scientific_writer/.claude/skills/venue-templates/scripts/customize_template.py +206 -0
- scientific_writer/.claude/skills/venue-templates/scripts/query_template.py +260 -0
- scientific_writer/.claude/skills/venue-templates/scripts/validate_format.py +255 -0
- scientific_writer/__init__.py +43 -0
- scientific_writer/api.py +393 -0
- scientific_writer/cli.py +326 -0
- scientific_writer/core.py +275 -0
- scientific_writer/models.py +76 -0
- scientific_writer/utils.py +289 -0
- scientific_writer-2.3.1.dist-info/METADATA +272 -0
- scientific_writer-2.3.1.dist-info/RECORD +315 -0
- scientific_writer-2.3.1.dist-info/WHEEL +4 -0
- scientific_writer-2.3.1.dist-info/entry_points.txt +2 -0
- scientific_writer-2.3.1.dist-info/licenses/LICENSE +22 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validate Format Script
|
|
4
|
+
Check if document meets venue-specific formatting requirements.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python validate_format.py --file my_paper.pdf --venue "Nature" --check-all
|
|
8
|
+
python validate_format.py --file my_paper.pdf --venue "NeurIPS" --check page-count,margins
|
|
9
|
+
python validate_format.py --file my_paper.pdf --venue "PLOS ONE" --report validation_report.txt
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import subprocess
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
# Venue requirements database
|
|
18
|
+
VENUE_REQUIREMENTS = {
|
|
19
|
+
"nature": {
|
|
20
|
+
"page_limit": 5, # Approximate for ~3000 words
|
|
21
|
+
"margins": {"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5}, # cm
|
|
22
|
+
"font_size": 12, # pt
|
|
23
|
+
"font_family": "Times",
|
|
24
|
+
"line_spacing": "double"
|
|
25
|
+
},
|
|
26
|
+
"neurips": {
|
|
27
|
+
"page_limit": 8, # Excluding refs
|
|
28
|
+
"margins": {"top": 2.54, "bottom": 2.54, "left": 2.54, "right": 2.54}, # cm (1 inch)
|
|
29
|
+
"font_size": 10,
|
|
30
|
+
"font_family": "Times",
|
|
31
|
+
"format": "two-column"
|
|
32
|
+
},
|
|
33
|
+
"plos_one": {
|
|
34
|
+
"page_limit": None, # No limit
|
|
35
|
+
"margins": {"top": 2.54, "bottom": 2.54, "left": 2.54, "right": 2.54},
|
|
36
|
+
"font_size": 10,
|
|
37
|
+
"font_family": "Arial",
|
|
38
|
+
"line_spacing": "double"
|
|
39
|
+
},
|
|
40
|
+
"nsf": {
|
|
41
|
+
"page_limit": 15, # Project description
|
|
42
|
+
"margins": {"top": 2.54, "bottom": 2.54, "left": 2.54, "right": 2.54}, # 1 inch required
|
|
43
|
+
"font_size": 11, # Minimum
|
|
44
|
+
"font_family": "Times Roman",
|
|
45
|
+
"line_spacing": "single or double"
|
|
46
|
+
},
|
|
47
|
+
"nih": {
|
|
48
|
+
"page_limit": 12, # Research strategy
|
|
49
|
+
"margins": {"top": 1.27, "bottom": 1.27, "left": 1.27, "right": 1.27}, # 0.5 inch minimum
|
|
50
|
+
"font_size": 11, # Arial 11pt minimum
|
|
51
|
+
"font_family": "Arial",
|
|
52
|
+
"line_spacing": "any"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def get_pdf_info(pdf_path):
|
|
57
|
+
"""Extract information from PDF using pdfinfo."""
|
|
58
|
+
try:
|
|
59
|
+
result = subprocess.run(
|
|
60
|
+
['pdfinfo', str(pdf_path)],
|
|
61
|
+
capture_output=True,
|
|
62
|
+
text=True,
|
|
63
|
+
check=True
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
info = {}
|
|
67
|
+
for line in result.stdout.split('\n'):
|
|
68
|
+
if ':' in line:
|
|
69
|
+
key, value = line.split(':', 1)
|
|
70
|
+
info[key.strip()] = value.strip()
|
|
71
|
+
|
|
72
|
+
return info
|
|
73
|
+
except FileNotFoundError:
|
|
74
|
+
print("⚠️ pdfinfo not found. Install poppler-utils for full PDF analysis.")
|
|
75
|
+
print(" macOS: brew install poppler")
|
|
76
|
+
print(" Linux: sudo apt-get install poppler-utils")
|
|
77
|
+
return None
|
|
78
|
+
except subprocess.CalledProcessError as e:
|
|
79
|
+
print(f"Error running pdfinfo: {e}")
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
def check_page_count(pdf_path, venue_reqs):
|
|
83
|
+
"""Check if page count is within limit."""
|
|
84
|
+
pdf_info = get_pdf_info(pdf_path)
|
|
85
|
+
|
|
86
|
+
if not pdf_info:
|
|
87
|
+
return {"status": "skip", "message": "Could not determine page count"}
|
|
88
|
+
|
|
89
|
+
pages = int(pdf_info.get('Pages', 0))
|
|
90
|
+
limit = venue_reqs.get('page_limit')
|
|
91
|
+
|
|
92
|
+
if limit is None:
|
|
93
|
+
return {"status": "pass", "message": f"No page limit. Document has {pages} pages."}
|
|
94
|
+
|
|
95
|
+
if pages <= limit:
|
|
96
|
+
return {"status": "pass", "message": f"✓ Page count OK: {pages}/{limit} pages"}
|
|
97
|
+
else:
|
|
98
|
+
return {"status": "fail", "message": f"✗ Page count exceeded: {pages}/{limit} pages"}
|
|
99
|
+
|
|
100
|
+
def check_margins(pdf_path, venue_reqs):
|
|
101
|
+
"""Check if margins meet requirements."""
|
|
102
|
+
# Note: This is a simplified check. Full margin analysis requires more sophisticated tools.
|
|
103
|
+
req_margins = venue_reqs.get('margins', {})
|
|
104
|
+
|
|
105
|
+
if not req_margins:
|
|
106
|
+
return {"status": "skip", "message": "No margin requirements specified"}
|
|
107
|
+
|
|
108
|
+
# This is a placeholder - accurate margin checking requires parsing PDF content
|
|
109
|
+
return {
|
|
110
|
+
"status": "info",
|
|
111
|
+
"message": f"ℹ️ Required margins: {req_margins} cm (manual verification recommended)"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
def check_fonts(pdf_path, venue_reqs):
|
|
115
|
+
"""Check fonts in PDF."""
|
|
116
|
+
try:
|
|
117
|
+
result = subprocess.run(
|
|
118
|
+
['pdffonts', str(pdf_path)],
|
|
119
|
+
capture_output=True,
|
|
120
|
+
text=True,
|
|
121
|
+
check=True
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
fonts_found = []
|
|
125
|
+
for line in result.stdout.split('\n')[2:]: # Skip header
|
|
126
|
+
if line.strip():
|
|
127
|
+
parts = line.split()
|
|
128
|
+
if parts:
|
|
129
|
+
fonts_found.append(parts[0])
|
|
130
|
+
|
|
131
|
+
req_font = venue_reqs.get('font_family', '')
|
|
132
|
+
req_size = venue_reqs.get('font_size')
|
|
133
|
+
|
|
134
|
+
message = f"ℹ️ Fonts found: {', '.join(set(fonts_found))}\n"
|
|
135
|
+
message += f" Required: {req_font}"
|
|
136
|
+
if req_size:
|
|
137
|
+
message += f" {req_size}pt minimum"
|
|
138
|
+
|
|
139
|
+
return {"status": "info", "message": message}
|
|
140
|
+
|
|
141
|
+
except FileNotFoundError:
|
|
142
|
+
return {"status": "skip", "message": "pdffonts not available"}
|
|
143
|
+
except subprocess.CalledProcessError:
|
|
144
|
+
return {"status": "skip", "message": "Could not extract font information"}
|
|
145
|
+
|
|
146
|
+
def validate_document(pdf_path, venue, checks):
|
|
147
|
+
"""Validate document against venue requirements."""
|
|
148
|
+
|
|
149
|
+
venue_key = venue.lower().replace(" ", "_")
|
|
150
|
+
|
|
151
|
+
if venue_key not in VENUE_REQUIREMENTS:
|
|
152
|
+
print(f"❌ Unknown venue: {venue}")
|
|
153
|
+
print(f"Available venues: {', '.join(VENUE_REQUIREMENTS.keys())}")
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
venue_reqs = VENUE_REQUIREMENTS[venue_key]
|
|
157
|
+
|
|
158
|
+
print(f"\n{'='*60}")
|
|
159
|
+
print(f"VALIDATING: {pdf_path.name}")
|
|
160
|
+
print(f"VENUE: {venue}")
|
|
161
|
+
print(f"{'='*60}\n")
|
|
162
|
+
|
|
163
|
+
results = {}
|
|
164
|
+
|
|
165
|
+
# Run requested checks
|
|
166
|
+
if 'page-count' in checks or 'all' in checks:
|
|
167
|
+
results['page-count'] = check_page_count(pdf_path, venue_reqs)
|
|
168
|
+
|
|
169
|
+
if 'margins' in checks or 'all' in checks:
|
|
170
|
+
results['margins'] = check_margins(pdf_path, venue_reqs)
|
|
171
|
+
|
|
172
|
+
if 'fonts' in checks or 'all' in checks:
|
|
173
|
+
results['fonts'] = check_fonts(pdf_path, venue_reqs)
|
|
174
|
+
|
|
175
|
+
# Print results
|
|
176
|
+
for check_name, result in results.items():
|
|
177
|
+
print(f"{check_name.upper()}:")
|
|
178
|
+
print(f" {result['message']}\n")
|
|
179
|
+
|
|
180
|
+
# Summary
|
|
181
|
+
failures = sum(1 for r in results.values() if r['status'] == 'fail')
|
|
182
|
+
passes = sum(1 for r in results.values() if r['status'] == 'pass')
|
|
183
|
+
|
|
184
|
+
print(f"{'='*60}")
|
|
185
|
+
if failures == 0:
|
|
186
|
+
print(f"✓ VALIDATION PASSED ({passes} checks)")
|
|
187
|
+
else:
|
|
188
|
+
print(f"✗ VALIDATION FAILED ({failures} issues)")
|
|
189
|
+
print(f"{'='*60}\n")
|
|
190
|
+
|
|
191
|
+
return results
|
|
192
|
+
|
|
193
|
+
def generate_report(pdf_path, venue, results, report_path):
|
|
194
|
+
"""Generate validation report."""
|
|
195
|
+
|
|
196
|
+
with open(report_path, 'w') as f:
|
|
197
|
+
f.write(f"Validation Report\n")
|
|
198
|
+
f.write(f"{'='*60}\n\n")
|
|
199
|
+
f.write(f"File: {pdf_path}\n")
|
|
200
|
+
f.write(f"Venue: {venue}\n")
|
|
201
|
+
f.write(f"Date: {Path.ctime(pdf_path)}\n\n")
|
|
202
|
+
|
|
203
|
+
for check_name, result in results.items():
|
|
204
|
+
f.write(f"{check_name.upper()}:\n")
|
|
205
|
+
f.write(f" Status: {result['status']}\n")
|
|
206
|
+
f.write(f" {result['message']}\n\n")
|
|
207
|
+
|
|
208
|
+
failures = sum(1 for r in results.values() if r['status'] == 'fail')
|
|
209
|
+
f.write(f"\nSummary: {'PASSED' if failures == 0 else 'FAILED'}\n")
|
|
210
|
+
|
|
211
|
+
print(f"Report saved to: {report_path}")
|
|
212
|
+
|
|
213
|
+
def main():
|
|
214
|
+
parser = argparse.ArgumentParser(
|
|
215
|
+
description="Validate document formatting for venue requirements",
|
|
216
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
217
|
+
epilog="""
|
|
218
|
+
Examples:
|
|
219
|
+
%(prog)s --file my_paper.pdf --venue "Nature" --check-all
|
|
220
|
+
%(prog)s --file my_paper.pdf --venue "NeurIPS" --check page-count,fonts
|
|
221
|
+
%(prog)s --file proposal.pdf --venue "NSF" --report validation.txt
|
|
222
|
+
"""
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
parser.add_argument('--file', type=str, required=True, help='PDF file to validate')
|
|
226
|
+
parser.add_argument('--venue', type=str, required=True, help='Target venue')
|
|
227
|
+
parser.add_argument('--check', type=str, default='all',
|
|
228
|
+
help='Checks to perform: page-count, margins, fonts, all (comma-separated)')
|
|
229
|
+
parser.add_argument('--check-all', action='store_true', help='Perform all checks')
|
|
230
|
+
parser.add_argument('--report', type=str, help='Save report to file')
|
|
231
|
+
|
|
232
|
+
args = parser.parse_args()
|
|
233
|
+
|
|
234
|
+
# Check file exists
|
|
235
|
+
pdf_path = Path(args.file)
|
|
236
|
+
if not pdf_path.exists():
|
|
237
|
+
print(f"Error: File not found: {pdf_path}")
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
# Parse checks
|
|
241
|
+
if args.check_all:
|
|
242
|
+
checks = ['all']
|
|
243
|
+
else:
|
|
244
|
+
checks = [c.strip() for c in args.check.split(',')]
|
|
245
|
+
|
|
246
|
+
# Validate
|
|
247
|
+
results = validate_document(pdf_path, args.venue, checks)
|
|
248
|
+
|
|
249
|
+
# Generate report if requested
|
|
250
|
+
if args.report and results:
|
|
251
|
+
generate_report(pdf_path, args.venue, results, Path(args.report))
|
|
252
|
+
|
|
253
|
+
if __name__ == "__main__":
|
|
254
|
+
main()
|
|
255
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Scientific Writer - AI-powered scientific writing assistant.
|
|
3
|
+
|
|
4
|
+
A powerful Python package for generating scientific papers, literature reviews,
|
|
5
|
+
and academic documents using Claude Sonnet 4.5.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
Generate a paper programmatically::
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
from scientific_writer import generate_paper
|
|
12
|
+
|
|
13
|
+
async def main():
|
|
14
|
+
async for update in generate_paper("Create a Nature paper on CRISPR"):
|
|
15
|
+
if update["type"] == "progress":
|
|
16
|
+
print(f"[{update['percentage']}%] {update['message']}")
|
|
17
|
+
else:
|
|
18
|
+
print(f"Paper created: {update['paper_directory']}")
|
|
19
|
+
print(f"PDF: {update['files']['pdf_final']}")
|
|
20
|
+
|
|
21
|
+
asyncio.run(main())
|
|
22
|
+
|
|
23
|
+
Use the CLI::
|
|
24
|
+
|
|
25
|
+
$ scientific-writer
|
|
26
|
+
> Create a NeurIPS paper on transformer attention mechanisms
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from .api import generate_paper
|
|
30
|
+
from .models import ProgressUpdate, PaperResult, PaperMetadata, PaperFiles
|
|
31
|
+
|
|
32
|
+
__version__ = "2.3.1"
|
|
33
|
+
__author__ = "K-Dense"
|
|
34
|
+
__license__ = "MIT"
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"generate_paper",
|
|
38
|
+
"ProgressUpdate",
|
|
39
|
+
"PaperResult",
|
|
40
|
+
"PaperMetadata",
|
|
41
|
+
"PaperFiles",
|
|
42
|
+
]
|
|
43
|
+
|
scientific_writer/api.py
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"""Async API for programmatic scientific paper generation."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional, List, Dict, Any, AsyncGenerator, Union
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
|
|
10
|
+
from claude_agent_sdk import query, ClaudeAgentOptions
|
|
11
|
+
|
|
12
|
+
from .core import (
|
|
13
|
+
get_api_key,
|
|
14
|
+
load_system_instructions,
|
|
15
|
+
ensure_output_folder,
|
|
16
|
+
get_data_files,
|
|
17
|
+
process_data_files,
|
|
18
|
+
create_data_context_message,
|
|
19
|
+
setup_claude_skills,
|
|
20
|
+
)
|
|
21
|
+
from .models import ProgressUpdate, PaperResult, PaperMetadata, PaperFiles
|
|
22
|
+
from .utils import (
|
|
23
|
+
scan_paper_directory,
|
|
24
|
+
count_citations_in_bib,
|
|
25
|
+
extract_citation_style,
|
|
26
|
+
count_words_in_tex,
|
|
27
|
+
extract_title_from_tex,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def generate_paper(
|
|
32
|
+
query: str,
|
|
33
|
+
output_dir: Optional[str] = None,
|
|
34
|
+
api_key: Optional[str] = None,
|
|
35
|
+
model: str = "claude-sonnet-4-20250514",
|
|
36
|
+
data_files: Optional[List[str]] = None,
|
|
37
|
+
cwd: Optional[str] = None,
|
|
38
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
39
|
+
"""
|
|
40
|
+
Generate a scientific paper asynchronously with progress updates.
|
|
41
|
+
|
|
42
|
+
This is a stateless async generator that yields progress updates during
|
|
43
|
+
execution and a final comprehensive result with all paper details.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
query: The paper generation request (e.g., "Create a Nature paper on CRISPR")
|
|
47
|
+
output_dir: Optional custom output directory (defaults to cwd/paper_outputs)
|
|
48
|
+
api_key: Optional Anthropic API key (defaults to ANTHROPIC_API_KEY env var)
|
|
49
|
+
model: Claude model to use (default: claude-sonnet-4-20250514)
|
|
50
|
+
data_files: Optional list of data file paths to include
|
|
51
|
+
cwd: Optional working directory (defaults to package parent directory)
|
|
52
|
+
|
|
53
|
+
Yields:
|
|
54
|
+
Progress updates (dict with type="progress") during execution
|
|
55
|
+
Final result (dict with type="result") containing all paper information
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
```python
|
|
59
|
+
async for update in generate_paper("Create a NeurIPS paper on transformers"):
|
|
60
|
+
if update["type"] == "progress":
|
|
61
|
+
print(f"[{update['percentage']}%] {update['message']}")
|
|
62
|
+
else:
|
|
63
|
+
print(f"Paper created: {update['paper_directory']}")
|
|
64
|
+
print(f"PDF: {update['files']['pdf_final']}")
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
67
|
+
# Initialize
|
|
68
|
+
start_time = time.time()
|
|
69
|
+
|
|
70
|
+
# Explicitly load .env file from working directory
|
|
71
|
+
# Determine working directory first
|
|
72
|
+
if cwd:
|
|
73
|
+
work_dir = Path(cwd).resolve()
|
|
74
|
+
else:
|
|
75
|
+
work_dir = Path.cwd().resolve()
|
|
76
|
+
|
|
77
|
+
# Load .env from working directory
|
|
78
|
+
env_file = work_dir / ".env"
|
|
79
|
+
if env_file.exists():
|
|
80
|
+
load_dotenv(dotenv_path=env_file, override=True)
|
|
81
|
+
|
|
82
|
+
# Get API key
|
|
83
|
+
try:
|
|
84
|
+
api_key_value = get_api_key(api_key)
|
|
85
|
+
except ValueError as e:
|
|
86
|
+
yield _create_error_result(str(e))
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
# Get package directory for copying skills to working directory
|
|
90
|
+
package_dir = Path(__file__).parent.absolute() # scientific_writer/ directory
|
|
91
|
+
|
|
92
|
+
# Set up Claude skills in the working directory (includes WRITER.md)
|
|
93
|
+
setup_claude_skills(package_dir, work_dir)
|
|
94
|
+
|
|
95
|
+
# Ensure output folder exists in user's directory
|
|
96
|
+
output_folder = ensure_output_folder(work_dir, output_dir)
|
|
97
|
+
|
|
98
|
+
# Initial progress update
|
|
99
|
+
yield ProgressUpdate(
|
|
100
|
+
message="Initializing paper generation",
|
|
101
|
+
stage="initialization",
|
|
102
|
+
percentage=0,
|
|
103
|
+
).to_dict()
|
|
104
|
+
|
|
105
|
+
# Load system instructions from .claude/WRITER.md in working directory
|
|
106
|
+
system_instructions = load_system_instructions(work_dir)
|
|
107
|
+
|
|
108
|
+
# Add conversation continuity instruction
|
|
109
|
+
system_instructions += "\n\n" + f"""
|
|
110
|
+
IMPORTANT - WORKING DIRECTORY:
|
|
111
|
+
- Your working directory is: {work_dir}
|
|
112
|
+
- ALWAYS create paper_outputs folder in this directory: {work_dir}/paper_outputs/
|
|
113
|
+
- NEVER write to /tmp/ or any other temporary directory
|
|
114
|
+
- All paper outputs MUST go to: {work_dir}/paper_outputs/<timestamp>_<description>/
|
|
115
|
+
|
|
116
|
+
IMPORTANT - CONVERSATION CONTINUITY:
|
|
117
|
+
- This is a NEW paper request - create a new paper directory
|
|
118
|
+
- Create a unique timestamped directory in the paper_outputs folder
|
|
119
|
+
- Do NOT assume there's an existing paper unless explicitly told in the prompt context
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
# Process data files if provided
|
|
123
|
+
data_context = ""
|
|
124
|
+
temp_paper_path = None
|
|
125
|
+
|
|
126
|
+
if data_files:
|
|
127
|
+
data_file_paths = get_data_files(work_dir, data_files)
|
|
128
|
+
if data_file_paths:
|
|
129
|
+
# We'll need to process these after the paper directory is created
|
|
130
|
+
yield ProgressUpdate(
|
|
131
|
+
message=f"Found {len(data_file_paths)} data file(s) to process",
|
|
132
|
+
stage="initialization",
|
|
133
|
+
percentage=5,
|
|
134
|
+
).to_dict()
|
|
135
|
+
|
|
136
|
+
# Configure Claude agent options
|
|
137
|
+
options = ClaudeAgentOptions(
|
|
138
|
+
system_prompt=system_instructions,
|
|
139
|
+
model=model,
|
|
140
|
+
allowed_tools=["Read", "Write", "Edit", "Bash", "research-lookup"],
|
|
141
|
+
permission_mode="bypassPermissions",
|
|
142
|
+
setting_sources=["project"], # Load skills from project .claude directory
|
|
143
|
+
cwd=str(work_dir), # User's working directory
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Track progress through message analysis
|
|
147
|
+
current_stage = "initialization"
|
|
148
|
+
current_percentage = 10
|
|
149
|
+
paper_directory = None
|
|
150
|
+
|
|
151
|
+
yield ProgressUpdate(
|
|
152
|
+
message="Starting paper generation with Claude",
|
|
153
|
+
stage="initialization",
|
|
154
|
+
percentage=10,
|
|
155
|
+
).to_dict()
|
|
156
|
+
|
|
157
|
+
# Execute query with Claude
|
|
158
|
+
try:
|
|
159
|
+
accumulated_text = ""
|
|
160
|
+
async for message in query(prompt=query, options=options):
|
|
161
|
+
if hasattr(message, "content") and message.content:
|
|
162
|
+
for block in message.content:
|
|
163
|
+
if hasattr(block, "text"):
|
|
164
|
+
text = block.text
|
|
165
|
+
accumulated_text += text
|
|
166
|
+
|
|
167
|
+
# Analyze text for progress indicators
|
|
168
|
+
stage, percentage, msg = _analyze_progress(accumulated_text, current_stage, current_percentage)
|
|
169
|
+
|
|
170
|
+
if stage != current_stage or percentage != current_percentage:
|
|
171
|
+
current_stage = stage
|
|
172
|
+
current_percentage = percentage
|
|
173
|
+
|
|
174
|
+
yield ProgressUpdate(
|
|
175
|
+
message=msg,
|
|
176
|
+
stage=stage,
|
|
177
|
+
percentage=percentage,
|
|
178
|
+
).to_dict()
|
|
179
|
+
|
|
180
|
+
# Paper generation complete - now scan for results
|
|
181
|
+
yield ProgressUpdate(
|
|
182
|
+
message="Scanning paper output directory",
|
|
183
|
+
stage="complete",
|
|
184
|
+
percentage=95,
|
|
185
|
+
).to_dict()
|
|
186
|
+
|
|
187
|
+
# Find the most recently created paper directory
|
|
188
|
+
paper_directory = _find_most_recent_paper(output_folder, start_time)
|
|
189
|
+
|
|
190
|
+
if not paper_directory:
|
|
191
|
+
yield _create_error_result("Paper directory not found after generation")
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
# Process any data files now if we have a paper directory
|
|
195
|
+
if data_files:
|
|
196
|
+
data_file_paths = get_data_files(work_dir, data_files)
|
|
197
|
+
if data_file_paths:
|
|
198
|
+
processed_info = process_data_files(
|
|
199
|
+
work_dir,
|
|
200
|
+
data_file_paths,
|
|
201
|
+
str(paper_directory),
|
|
202
|
+
delete_originals=False # Don't delete when using programmatic API
|
|
203
|
+
)
|
|
204
|
+
if processed_info:
|
|
205
|
+
manuscript_count = len(processed_info.get('manuscript_files', []))
|
|
206
|
+
message = f"Processed {len(processed_info['all_files'])} file(s)"
|
|
207
|
+
if manuscript_count > 0:
|
|
208
|
+
message += f" ({manuscript_count} manuscript(s) copied to drafts/)"
|
|
209
|
+
yield ProgressUpdate(
|
|
210
|
+
message=message,
|
|
211
|
+
stage="complete",
|
|
212
|
+
percentage=97,
|
|
213
|
+
).to_dict()
|
|
214
|
+
|
|
215
|
+
# Scan the paper directory for all files
|
|
216
|
+
file_info = scan_paper_directory(paper_directory)
|
|
217
|
+
|
|
218
|
+
# Build comprehensive result
|
|
219
|
+
result = _build_paper_result(paper_directory, file_info)
|
|
220
|
+
|
|
221
|
+
yield ProgressUpdate(
|
|
222
|
+
message="Paper generation complete",
|
|
223
|
+
stage="complete",
|
|
224
|
+
percentage=100,
|
|
225
|
+
).to_dict()
|
|
226
|
+
|
|
227
|
+
# Final result
|
|
228
|
+
yield result.to_dict()
|
|
229
|
+
|
|
230
|
+
except Exception as e:
|
|
231
|
+
yield _create_error_result(f"Error during paper generation: {str(e)}")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _analyze_progress(text: str, current_stage: str, current_percentage: int) -> tuple:
|
|
235
|
+
"""
|
|
236
|
+
Analyze accumulated text to determine current progress stage.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Tuple of (stage, percentage, message)
|
|
240
|
+
"""
|
|
241
|
+
text_lower = text.lower()
|
|
242
|
+
|
|
243
|
+
# Check for various progress indicators
|
|
244
|
+
if "research" in text_lower or "literature" in text_lower or "searching" in text_lower:
|
|
245
|
+
if current_stage != "research":
|
|
246
|
+
return "research", 30, "Conducting literature research"
|
|
247
|
+
|
|
248
|
+
if "writing" in text_lower or "introduction" in text_lower or "methods" in text_lower:
|
|
249
|
+
if current_stage != "writing":
|
|
250
|
+
return "writing", 50, "Writing paper sections"
|
|
251
|
+
elif current_percentage < 70:
|
|
252
|
+
return "writing", min(current_percentage + 10, 70), "Writing paper sections"
|
|
253
|
+
|
|
254
|
+
if "compil" in text_lower or "latex" in text_lower or "pdf" in text_lower:
|
|
255
|
+
if current_stage != "compilation":
|
|
256
|
+
return "compilation", 80, "Compiling LaTeX to PDF"
|
|
257
|
+
|
|
258
|
+
if "complete" in text_lower or "finished" in text_lower or "done" in text_lower:
|
|
259
|
+
return "complete", 90, "Finalizing paper"
|
|
260
|
+
|
|
261
|
+
# No change detected
|
|
262
|
+
return current_stage, current_percentage, "Processing..."
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _find_most_recent_paper(output_folder: Path, start_time: float) -> Optional[Path]:
|
|
266
|
+
"""
|
|
267
|
+
Find the most recently created/modified paper directory.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
output_folder: Path to paper_outputs folder
|
|
271
|
+
start_time: Start time of generation (to filter relevant directories)
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Path to paper directory or None
|
|
275
|
+
"""
|
|
276
|
+
try:
|
|
277
|
+
paper_dirs = [d for d in output_folder.iterdir() if d.is_dir()]
|
|
278
|
+
if not paper_dirs:
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
# Filter to only directories modified after start_time
|
|
282
|
+
recent_dirs = [
|
|
283
|
+
d for d in paper_dirs
|
|
284
|
+
if d.stat().st_mtime >= start_time - 5 # 5 second buffer
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
if not recent_dirs:
|
|
288
|
+
# Fallback to most recent directory overall
|
|
289
|
+
recent_dirs = paper_dirs
|
|
290
|
+
|
|
291
|
+
# Return the most recent
|
|
292
|
+
most_recent = max(recent_dirs, key=lambda d: d.stat().st_mtime)
|
|
293
|
+
return most_recent
|
|
294
|
+
except Exception:
|
|
295
|
+
return None
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _build_paper_result(paper_dir: Path, file_info: Dict[str, Any]) -> PaperResult:
|
|
299
|
+
"""
|
|
300
|
+
Build a comprehensive PaperResult from scanned files.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
paper_dir: Path to paper directory
|
|
304
|
+
file_info: Dictionary of file information from scan_paper_directory
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
PaperResult object
|
|
308
|
+
"""
|
|
309
|
+
# Extract metadata
|
|
310
|
+
tex_file = file_info['tex_final'] or (file_info['tex_drafts'][0] if file_info['tex_drafts'] else None)
|
|
311
|
+
|
|
312
|
+
title = extract_title_from_tex(tex_file)
|
|
313
|
+
word_count = count_words_in_tex(tex_file)
|
|
314
|
+
|
|
315
|
+
# Extract topic from directory name
|
|
316
|
+
topic = ""
|
|
317
|
+
parts = paper_dir.name.split('_', 2)
|
|
318
|
+
if len(parts) >= 3:
|
|
319
|
+
topic = parts[2].replace('_', ' ')
|
|
320
|
+
|
|
321
|
+
metadata = PaperMetadata(
|
|
322
|
+
title=title,
|
|
323
|
+
created_at=datetime.fromtimestamp(paper_dir.stat().st_ctime).isoformat() + "Z",
|
|
324
|
+
topic=topic,
|
|
325
|
+
word_count=word_count,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Build files object
|
|
329
|
+
files = PaperFiles(
|
|
330
|
+
pdf_final=file_info['pdf_final'],
|
|
331
|
+
tex_final=file_info['tex_final'],
|
|
332
|
+
pdf_drafts=file_info['pdf_drafts'],
|
|
333
|
+
tex_drafts=file_info['tex_drafts'],
|
|
334
|
+
bibliography=file_info['bibliography'],
|
|
335
|
+
figures=file_info['figures'],
|
|
336
|
+
data=file_info['data'],
|
|
337
|
+
progress_log=file_info['progress_log'],
|
|
338
|
+
summary=file_info['summary'],
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Citations info
|
|
342
|
+
citation_count = count_citations_in_bib(file_info['bibliography'])
|
|
343
|
+
citation_style = extract_citation_style(file_info['bibliography'])
|
|
344
|
+
|
|
345
|
+
citations = {
|
|
346
|
+
'count': citation_count,
|
|
347
|
+
'style': citation_style,
|
|
348
|
+
'file': file_info['bibliography'],
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
# Determine status
|
|
352
|
+
status = "success"
|
|
353
|
+
compilation_success = file_info['pdf_final'] is not None
|
|
354
|
+
|
|
355
|
+
if not compilation_success:
|
|
356
|
+
if file_info['tex_final']:
|
|
357
|
+
status = "partial" # TeX created but PDF failed
|
|
358
|
+
else:
|
|
359
|
+
status = "failed"
|
|
360
|
+
|
|
361
|
+
result = PaperResult(
|
|
362
|
+
status=status,
|
|
363
|
+
paper_directory=str(paper_dir),
|
|
364
|
+
paper_name=paper_dir.name,
|
|
365
|
+
metadata=metadata,
|
|
366
|
+
files=files,
|
|
367
|
+
citations=citations,
|
|
368
|
+
figures_count=len(file_info['figures']),
|
|
369
|
+
compilation_success=compilation_success,
|
|
370
|
+
errors=[],
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
return result
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _create_error_result(error_message: str) -> Dict[str, Any]:
|
|
377
|
+
"""
|
|
378
|
+
Create an error result dictionary.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
error_message: Error message string
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Dictionary with error information
|
|
385
|
+
"""
|
|
386
|
+
result = PaperResult(
|
|
387
|
+
status="failed",
|
|
388
|
+
paper_directory="",
|
|
389
|
+
paper_name="",
|
|
390
|
+
errors=[error_message],
|
|
391
|
+
)
|
|
392
|
+
return result.to_dict()
|
|
393
|
+
|