medsci-skills 4.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 +50 -0
- package/README.md +602 -0
- package/README_FIRST.md +27 -0
- package/bin/medsci-skills.js +159 -0
- package/installers/install-macos.command +19 -0
- package/installers/install-windows.cmd +26 -0
- package/installers/install-windows.ps1 +17 -0
- package/installers/install.py +218 -0
- package/metadata/skills_catalog.json +452 -0
- package/package.json +48 -0
- package/skills/academic-aio/SKILL.md +408 -0
- package/skills/academic-aio/references/case_studies/kjr_mllm_2025.md +82 -0
- package/skills/academic-aio/references/checklists/AIO_GENERAL.md +354 -0
- package/skills/academic-aio/references/journal_summarybox_templates.yaml +126 -0
- package/skills/academic-aio/references/oac_funding_checklist.yaml +129 -0
- package/skills/academic-aio/references/reporting_guideline_mapping.md +39 -0
- package/skills/academic-aio/references/schema_markup_templates/CodeRepository.jsonld +32 -0
- package/skills/academic-aio/references/schema_markup_templates/Dataset.jsonld +36 -0
- package/skills/academic-aio/references/schema_markup_templates/Person.jsonld +30 -0
- package/skills/academic-aio/references/schema_markup_templates/README.md +43 -0
- package/skills/academic-aio/references/schema_markup_templates/ScholarlyArticle.jsonld +55 -0
- package/skills/academic-aio/scripts/batch_metadata_audit.py +169 -0
- package/skills/academic-aio/scripts/validate_schema.py +118 -0
- package/skills/academic-aio/skill.yml +36 -0
- package/skills/academic-aio/templates/aio_audit_checklist.md.j2 +108 -0
- package/skills/add-journal/SKILL.md +482 -0
- package/skills/add-journal/skill.yml +33 -0
- package/skills/analyze-stats/SKILL.md +598 -0
- package/skills/analyze-stats/references/analysis_guides/missing_data.md +109 -0
- package/skills/analyze-stats/references/analysis_guides/nhis_icd10_mapping.md +247 -0
- package/skills/analyze-stats/references/analysis_guides/propensity_score.md +132 -0
- package/skills/analyze-stats/references/analysis_guides/regression.md +115 -0
- package/skills/analyze-stats/references/analysis_guides/repeated_measures.md +160 -0
- package/skills/analyze-stats/references/analysis_guides/survey_weighted.md +366 -0
- package/skills/analyze-stats/references/analysis_guides/test_selection.md +86 -0
- package/skills/analyze-stats/references/style/figure_style.mplstyle +69 -0
- package/skills/analyze-stats/references/style/theme_publication.R +147 -0
- package/skills/analyze-stats/references/table-standards/journal-profiles/ajr.yaml +51 -0
- package/skills/analyze-stats/references/table-standards/journal-profiles/european_radiology.yaml +55 -0
- package/skills/analyze-stats/references/table-standards/journal-profiles/jama.yaml +66 -0
- package/skills/analyze-stats/references/table-standards/journal-profiles/lancet.yaml +57 -0
- package/skills/analyze-stats/references/table-standards/journal-profiles/nejm.yaml +51 -0
- package/skills/analyze-stats/references/table-standards/journal-profiles/radiology.yaml +66 -0
- package/skills/analyze-stats/references/table-standards/table-standards.md +287 -0
- package/skills/analyze-stats/references/table-standards/table-types/diagnostic_accuracy.md +36 -0
- package/skills/analyze-stats/references/table-standards/table-types/meta_analysis.md +58 -0
- package/skills/analyze-stats/references/table-standards/table-types/model_comparison.md +36 -0
- package/skills/analyze-stats/references/table-standards/table-types/regression_results.md +50 -0
- package/skills/analyze-stats/references/table-standards/table-types/table1_demographics.md +51 -0
- package/skills/analyze-stats/references/table-standards/tool-comparison.md +79 -0
- package/skills/analyze-stats/references/templates/agreement_analysis.py +436 -0
- package/skills/analyze-stats/references/templates/dca_plot.R +237 -0
- package/skills/analyze-stats/references/templates/diagnostic_accuracy.py +401 -0
- package/skills/analyze-stats/references/templates/dta_meta_analysis.R +384 -0
- package/skills/analyze-stats/references/templates/forest_plot.py +412 -0
- package/skills/analyze-stats/references/templates/likert_summary.py +356 -0
- package/skills/analyze-stats/references/templates/meta_analysis.R +365 -0
- package/skills/analyze-stats/references/templates/propensity_score.py +478 -0
- package/skills/analyze-stats/references/templates/regression.py +425 -0
- package/skills/analyze-stats/references/templates/repeated_measures.py +434 -0
- package/skills/analyze-stats/references/templates/sample_size.R +382 -0
- package/skills/analyze-stats/references/templates/survey_weighted_analysis.py +411 -0
- package/skills/analyze-stats/references/templates/survival_analysis.py +325 -0
- package/skills/analyze-stats/references/templates/table1_demographics.py +287 -0
- package/skills/analyze-stats/scripts/check_generated_code.py +335 -0
- package/skills/analyze-stats/skill.yml +38 -0
- package/skills/analyze-stats/tests/fixtures/gen_bad.R +16 -0
- package/skills/analyze-stats/tests/fixtures/gen_bad.py +24 -0
- package/skills/analyze-stats/tests/fixtures/gen_clean.py +21 -0
- package/skills/analyze-stats/tests/test_generated_code.sh +59 -0
- package/skills/analyze-stats/tests/test_survival_template.sh +53 -0
- package/skills/author-strategy/SKILL.md +117 -0
- package/skills/author-strategy/analyze_patterns.py +303 -0
- package/skills/author-strategy/fetch_pubmed.py +374 -0
- package/skills/author-strategy/skill.yml +34 -0
- package/skills/batch-cohort/SKILL.md +223 -0
- package/skills/batch-cohort/references/base_template_knhanes.R +210 -0
- package/skills/batch-cohort/references/batch_template_generator.R +222 -0
- package/skills/batch-cohort/references/variable_coding_registry.md +136 -0
- package/skills/batch-cohort/skill.yml +35 -0
- package/skills/calc-sample-size/SKILL.md +491 -0
- package/skills/calc-sample-size/references/formulas.md +655 -0
- package/skills/calc-sample-size/references/observational_cohort.md +49 -0
- package/skills/calc-sample-size/skill.yml +51 -0
- package/skills/check-reporting/SKILL.md +534 -0
- package/skills/check-reporting/references/LICENSES.md +41 -0
- package/skills/check-reporting/references/checklists/AMSTAR2.md +54 -0
- package/skills/check-reporting/references/checklists/ARRIVE_2.md +234 -0
- package/skills/check-reporting/references/checklists/CARE.md +102 -0
- package/skills/check-reporting/references/checklists/CLAIM_2024.md +128 -0
- package/skills/check-reporting/references/checklists/CLEAR.md +113 -0
- package/skills/check-reporting/references/checklists/CONSORT.md +86 -0
- package/skills/check-reporting/references/checklists/COSMIN_RoB.md +136 -0
- package/skills/check-reporting/references/checklists/GRRAS.md +61 -0
- package/skills/check-reporting/references/checklists/MI_CLEAR_LLM.md +167 -0
- package/skills/check-reporting/references/checklists/MOOSE.md +85 -0
- package/skills/check-reporting/references/checklists/NOS.md +88 -0
- package/skills/check-reporting/references/checklists/PRISMA_2020.md +135 -0
- package/skills/check-reporting/references/checklists/PRISMA_DTA.md +36 -0
- package/skills/check-reporting/references/checklists/PRISMA_P.md +56 -0
- package/skills/check-reporting/references/checklists/PROBAST.md +75 -0
- package/skills/check-reporting/references/checklists/PROBAST_AI.md +130 -0
- package/skills/check-reporting/references/checklists/QUADAS2.md +77 -0
- package/skills/check-reporting/references/checklists/QUADAS_C.md +131 -0
- package/skills/check-reporting/references/checklists/ROBINS_E.md +179 -0
- package/skills/check-reporting/references/checklists/ROBINS_I.md +87 -0
- package/skills/check-reporting/references/checklists/ROBIS.md +114 -0
- package/skills/check-reporting/references/checklists/ROB_ME.md +126 -0
- package/skills/check-reporting/references/checklists/RoB2.md +79 -0
- package/skills/check-reporting/references/checklists/RoB_NMA.md +96 -0
- package/skills/check-reporting/references/checklists/SPIRIT.md +112 -0
- package/skills/check-reporting/references/checklists/SQUIRE_2.md +68 -0
- package/skills/check-reporting/references/checklists/STARD.md +129 -0
- package/skills/check-reporting/references/checklists/STARD_AI.md +211 -0
- package/skills/check-reporting/references/checklists/STROBE.md +80 -0
- package/skills/check-reporting/references/checklists/SWiM.md +33 -0
- package/skills/check-reporting/references/checklists/TRIPOD.md +157 -0
- package/skills/check-reporting/references/checklists/TRIPOD_AI.md +140 -0
- package/skills/check-reporting/references/step4c_registration_timing.md +93 -0
- package/skills/check-reporting/references/step4d_prisma_figure_audit.md +137 -0
- package/skills/check-reporting/scripts/check_checklist_exists.py +183 -0
- package/skills/check-reporting/scripts/check_checklist_version.py +168 -0
- package/skills/check-reporting/scripts/check_framework_naming.py +206 -0
- package/skills/check-reporting/scripts/check_prisma_figure.py +209 -0
- package/skills/check-reporting/scripts/prisma_cascade_check.py +274 -0
- package/skills/check-reporting/skill.yml +41 -0
- package/skills/check-reporting/tests/fixtures/framework_bad.md +8 -0
- package/skills/check-reporting/tests/fixtures/framework_clean.md +7 -0
- package/skills/check-reporting/tests/test_checklist_fail_fast.sh +77 -0
- package/skills/check-reporting/tests/test_checklist_version.sh +72 -0
- package/skills/check-reporting/tests/test_framework_naming.sh +45 -0
- package/skills/check-reporting/tests/test_prisma_cascade.sh +104 -0
- package/skills/clean-data/SKILL.md +180 -0
- package/skills/clean-data/references/cleaning_patterns.md +299 -0
- package/skills/clean-data/references/profiling_template.py +304 -0
- package/skills/clean-data/scripts/check_structural_zero.py +174 -0
- package/skills/clean-data/skill.yml +35 -0
- package/skills/clean-data/tests/fixtures/smoking.csv +8 -0
- package/skills/clean-data/tests/test_structural_zero.sh +49 -0
- package/skills/cross-national/SKILL.md +264 -0
- package/skills/cross-national/skill.yml +37 -0
- package/skills/define-variables/SKILL.md +146 -0
- package/skills/define-variables/references/common_definitions.md +190 -0
- package/skills/define-variables/skill.yml +34 -0
- package/skills/define-variables/templates/variable_operationalization.md +64 -0
- package/skills/deidentify/SKILL.md +203 -0
- package/skills/deidentify/deidentify.py +1224 -0
- package/skills/deidentify/locales/_template.json +45 -0
- package/skills/deidentify/locales/au.json +43 -0
- package/skills/deidentify/locales/ca.json +44 -0
- package/skills/deidentify/locales/cn.json +47 -0
- package/skills/deidentify/locales/de.json +48 -0
- package/skills/deidentify/locales/fr.json +48 -0
- package/skills/deidentify/locales/in.json +48 -0
- package/skills/deidentify/locales/jp.json +48 -0
- package/skills/deidentify/locales/kr.json +48 -0
- package/skills/deidentify/locales/uk.json +45 -0
- package/skills/deidentify/locales/us.json +43 -0
- package/skills/deidentify/references/date_shift_guide.md +82 -0
- package/skills/deidentify/references/hipaa_18_identifiers.md +48 -0
- package/skills/deidentify/references/korean_phi_patterns.md +135 -0
- package/skills/deidentify/skill.yml +43 -0
- package/skills/deidentify/tests/README.md +26 -0
- package/skills/deidentify/tests/test_clean.csv +16 -0
- package/skills/deidentify/tests/test_edge_cases.csv +11 -0
- package/skills/deidentify/tests/test_phi_korean.csv +11 -0
- package/skills/design-ai-benchmarking/SKILL.md +214 -0
- package/skills/design-ai-benchmarking/references/benchmark_export_schema.json +69 -0
- package/skills/design-ai-benchmarking/references/elicitation_rubric_template.md +37 -0
- package/skills/design-ai-benchmarking/skill.yml +38 -0
- package/skills/design-study/SKILL.md +298 -0
- package/skills/design-study/skill.yml +33 -0
- package/skills/fill-icmje-coi/SKILL.md +216 -0
- package/skills/fill-icmje-coi/scripts/fill_icmje_coi.py +140 -0
- package/skills/fill-icmje-coi/skill.yml +35 -0
- package/skills/fill-icmje-coi/templates/icmje_coi_seed_synthetic.docx +0 -0
- package/skills/fill-protocol/SKILL.md +248 -0
- package/skills/fill-protocol/examples/example_irb_template.yaml +53 -0
- package/skills/fill-protocol/references/best_practices.md +121 -0
- package/skills/fill-protocol/scripts/doc_to_docx.py +111 -0
- package/skills/fill-protocol/scripts/fill_form.py +611 -0
- package/skills/fill-protocol/scripts/inspect_template.py +61 -0
- package/skills/fill-protocol/setup.sh +162 -0
- package/skills/fill-protocol/skill.yml +37 -0
- package/skills/find-cohort-gap/SKILL.md +309 -0
- package/skills/find-cohort-gap/references/cohort_profile_template.md +93 -0
- package/skills/find-cohort-gap/references/onepager_template.md +84 -0
- package/skills/find-cohort-gap/references/pattern_scoring_rubric.md +169 -0
- package/skills/find-cohort-gap/references/saturation_query_templates.md +143 -0
- package/skills/find-cohort-gap/skill.yml +35 -0
- package/skills/find-journal/POLICY.md +87 -0
- package/skills/find-journal/SKILL.md +340 -0
- package/skills/find-journal/references/journal_profiles/AJNR.md +29 -0
- package/skills/find-journal/references/journal_profiles/AJR.md +30 -0
- package/skills/find-journal/references/journal_profiles/Abdominal_Radiology.md +30 -0
- package/skills/find-journal/references/journal_profiles/Academic_Radiology.md +30 -0
- package/skills/find-journal/references/journal_profiles/Annals_of_Internal_Medicine.md +33 -0
- package/skills/find-journal/references/journal_profiles/Artificial_Intelligence_in_Medicine.md +28 -0
- package/skills/find-journal/references/journal_profiles/BMC_Medicine.md +31 -0
- package/skills/find-journal/references/journal_profiles/British_Journal_of_Radiology.md +39 -0
- package/skills/find-journal/references/journal_profiles/CVIR.md +30 -0
- package/skills/find-journal/references/journal_profiles/Chest.md +39 -0
- package/skills/find-journal/references/journal_profiles/Clinical_Radiology.md +30 -0
- package/skills/find-journal/references/journal_profiles/Clinical_and_Molecular_Hepatology.md +32 -0
- package/skills/find-journal/references/journal_profiles/Diabetes_Metabolism_Journal.md +36 -0
- package/skills/find-journal/references/journal_profiles/Diagnostic_and_Interventional_Radiology.md +32 -0
- package/skills/find-journal/references/journal_profiles/Endocrinology_and_Metabolism.md +37 -0
- package/skills/find-journal/references/journal_profiles/European_Journal_of_Preventive_Cardiology.md +39 -0
- package/skills/find-journal/references/journal_profiles/European_Radiology.md +29 -0
- package/skills/find-journal/references/journal_profiles/Hepatology_Communications.md +40 -0
- package/skills/find-journal/references/journal_profiles/Hepatology_International.md +37 -0
- package/skills/find-journal/references/journal_profiles/IEEE_JBHI.md +28 -0
- package/skills/find-journal/references/journal_profiles/IEEE_TMI.md +28 -0
- package/skills/find-journal/references/journal_profiles/INSI.md +29 -0
- package/skills/find-journal/references/journal_profiles/Investigative_Radiology.md +25 -0
- package/skills/find-journal/references/journal_profiles/JACC_Advances.md +41 -0
- package/skills/find-journal/references/journal_profiles/JACC_Asia.md +30 -0
- package/skills/find-journal/references/journal_profiles/JACR.md +28 -0
- package/skills/find-journal/references/journal_profiles/JAMA.md +40 -0
- package/skills/find-journal/references/journal_profiles/JAMA_Network_Open.md +30 -0
- package/skills/find-journal/references/journal_profiles/JCSM.md +39 -0
- package/skills/find-journal/references/journal_profiles/JKMS.md +32 -0
- package/skills/find-journal/references/journal_profiles/JMIR.md +29 -0
- package/skills/find-journal/references/journal_profiles/JMIR_Medical_Education.md +29 -0
- package/skills/find-journal/references/journal_profiles/JNIS.md +35 -0
- package/skills/find-journal/references/journal_profiles/JVIR.md +31 -0
- package/skills/find-journal/references/journal_profiles/Journal_of_Biomedical_Informatics.md +29 -0
- package/skills/find-journal/references/journal_profiles/Journal_of_Clinical_Endocrinology_and_Metabolism.md +40 -0
- package/skills/find-journal/references/journal_profiles/Journal_of_Magnetic_Resonance_Imaging.md +30 -0
- package/skills/find-journal/references/journal_profiles/Journal_of_Nuclear_Medicine.md +31 -0
- package/skills/find-journal/references/journal_profiles/Journal_of_Stroke.md +32 -0
- package/skills/find-journal/references/journal_profiles/KJR.md +38 -0
- package/skills/find-journal/references/journal_profiles/Korean_Circulation_Journal.md +38 -0
- package/skills/find-journal/references/journal_profiles/Korean_Journal_of_Internal_Medicine.md +36 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Diabetes_and_Endocrinology.md +40 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Gastroenterology_and_Hepatology.md +49 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Infectious_Diseases.md +38 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Neurology.md +39 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Oncology.md +40 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Psychiatry.md +38 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Public_Health.md +30 -0
- package/skills/find-journal/references/journal_profiles/Lancet_Respiratory_Medicine.md +39 -0
- package/skills/find-journal/references/journal_profiles/Liver_International.md +33 -0
- package/skills/find-journal/references/journal_profiles/Medical_Image_Analysis.md +28 -0
- package/skills/find-journal/references/journal_profiles/NEJM.md +33 -0
- package/skills/find-journal/references/journal_profiles/Nature_Machine_Intelligence.md +31 -0
- package/skills/find-journal/references/journal_profiles/Nature_Medicine.md +39 -0
- package/skills/find-journal/references/journal_profiles/Neuroradiology.md +31 -0
- package/skills/find-journal/references/journal_profiles/Nutrition_Metabolism_and_Cardiovascular_Diseases.md +39 -0
- package/skills/find-journal/references/journal_profiles/PLOS_Medicine.md +32 -0
- package/skills/find-journal/references/journal_profiles/RYAI.md +28 -0
- package/skills/find-journal/references/journal_profiles/Radiology.md +29 -0
- package/skills/find-journal/references/journal_profiles/Skeletal_Radiology.md +31 -0
- package/skills/find-journal/references/journal_profiles/Stroke.md +37 -0
- package/skills/find-journal/references/journal_profiles/The_BMJ.md +31 -0
- package/skills/find-journal/references/journal_profiles/The_Lancet.md +31 -0
- package/skills/find-journal/references/journal_profiles/The_Lancet_Digital_Health.md +29 -0
- package/skills/find-journal/references/journal_profiles/World_Journal_of_Hepatology.md +53 -0
- package/skills/find-journal/references/journal_profiles/npj_Digital_Medicine.md +29 -0
- package/skills/find-journal/skill.yml +34 -0
- package/skills/fulltext-retrieval/SKILL.md +174 -0
- package/skills/fulltext-retrieval/fetch_oa.py +433 -0
- package/skills/fulltext-retrieval/pdf_to_md.py +160 -0
- package/skills/fulltext-retrieval/skill.yml +41 -0
- package/skills/generate-codebook/SKILL.md +155 -0
- package/skills/generate-codebook/references/codebook_schema.md +76 -0
- package/skills/generate-codebook/scripts/generate_codebook.py +278 -0
- package/skills/generate-codebook/skill.yml +35 -0
- package/skills/generate-codebook/tests/test_generate_codebook.sh +76 -0
- package/skills/grant-builder/SKILL.md +251 -0
- package/skills/grant-builder/skill.yml +34 -0
- package/skills/humanize/SKILL.md +251 -0
- package/skills/humanize/references/ai_patterns.md +571 -0
- package/skills/humanize/skill.yml +33 -0
- package/skills/intake-project/SKILL.md +264 -0
- package/skills/intake-project/skill.yml +34 -0
- package/skills/lit-sync/SKILL.md +448 -0
- package/skills/lit-sync/references/locale/ko/note_templates.md +110 -0
- package/skills/lit-sync/skill.yml +52 -0
- package/skills/lit-sync/tests/test_poll_logic.sh +92 -0
- package/skills/ma-scout/SKILL.md +640 -0
- package/skills/ma-scout/references/project_readme_template.md +95 -0
- package/skills/ma-scout/references/project_readme_template_ko.md +82 -0
- package/skills/ma-scout/skill.yml +33 -0
- package/skills/make-figures/SKILL.md +957 -0
- package/skills/make-figures/references/critic_rubrics/data_plot.md +166 -0
- package/skills/make-figures/references/critic_rubrics/flow_diagram.md +169 -0
- package/skills/make-figures/references/design_principles.md +181 -0
- package/skills/make-figures/references/exemplar_diagrams/README.md +65 -0
- package/skills/make-figures/references/exemplar_diagrams/consort/README.md +15 -0
- package/skills/make-figures/references/exemplar_diagrams/consort/template_input.yaml +37 -0
- package/skills/make-figures/references/exemplar_diagrams/consort/template_output.pdf +0 -0
- package/skills/make-figures/references/exemplar_diagrams/consort/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/consort/template_output_600.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/other/other_02.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/other/other_02.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/other/other_02_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/README.md +15 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10.meta.yaml +4 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10_why.md +13 -0
- package/skills/make-figures/references/exemplar_diagrams/prisma/README.md +15 -0
- package/skills/make-figures/references/exemplar_diagrams/prisma/template_input.yaml +47 -0
- package/skills/make-figures/references/exemplar_diagrams/prisma/template_output.pdf +0 -0
- package/skills/make-figures/references/exemplar_diagrams/prisma/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/prisma/template_output_600.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/stard/README.md +15 -0
- package/skills/make-figures/references/exemplar_diagrams/stard/template_input.yaml +40 -0
- package/skills/make-figures/references/exemplar_diagrams/stard/template_output.pdf +0 -0
- package/skills/make-figures/references/exemplar_diagrams/stard/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/stard/template_output_600.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_input.yaml +43 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_input_pptx.yaml +43 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_output.pdf +0 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_output.pptx +0 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_output_600.png +0 -0
- package/skills/make-figures/references/figure_specs.md +291 -0
- package/skills/make-figures/references/flow_diagram_lessons.md +164 -0
- package/skills/make-figures/references/jacc_central_illustration_principles.md +91 -0
- package/skills/make-figures/references/medical_illustration_sources.md +98 -0
- package/skills/make-figures/references/pipeline_concepts_medical_ai.md +240 -0
- package/skills/make-figures/references/reporting_guideline_figure_map.md +104 -0
- package/skills/make-figures/references/visual_abstract_templates/european_radiology.pptx +0 -0
- package/skills/make-figures/references/visual_abstract_templates/jacc_central_illustration.pptx +0 -0
- package/skills/make-figures/references/visual_abstract_templates/medsci_default.pptx +0 -0
- package/skills/make-figures/references/visual_abstract_templates/template_guide.md +114 -0
- package/skills/make-figures/scripts/build_jacc_template.py +77 -0
- package/skills/make-figures/scripts/build_prisma2020_template.py +371 -0
- package/skills/make-figures/scripts/build_strobe_template.py +351 -0
- package/skills/make-figures/scripts/critic_figure.py +264 -0
- package/skills/make-figures/scripts/derive_figure_legend_counts.py +138 -0
- package/skills/make-figures/scripts/extract_exemplar_from_pdf.py +186 -0
- package/skills/make-figures/scripts/fetch_official_templates.sh +88 -0
- package/skills/make-figures/scripts/fill_prisma_template.py +142 -0
- package/skills/make-figures/scripts/generate_flow_diagram.R +133 -0
- package/skills/make-figures/scripts/generate_image.py +99 -0
- package/skills/make-figures/scripts/generate_visual_abstract.py +438 -0
- package/skills/make-figures/scripts/validate_pptx_mac_compat.py +233 -0
- package/skills/make-figures/skill.yml +52 -0
- package/skills/make-figures/templates/official/NOTES.md +62 -0
- package/skills/make-figures/templates/official/consort2010/CONSORT_2025_editable_checklist.docx +0 -0
- package/skills/make-figures/templates/official/consort2010/CONSORT_2025_flow_diagram.docx +0 -0
- package/skills/make-figures/templates/official/prisma2020/PRISMA_2020_flow_new_v1.pptx +0 -0
- package/skills/make-figures/templates/official/prisma2020/PRISMA_2020_flow_new_v2.pptx +0 -0
- package/skills/make-figures/templates/official/prisma2020/PRISMA_2020_flow_updated_v2.pptx +0 -0
- package/skills/make-figures/templates/official/spirit2013/SPIRIT_2025_editable_checklist.docx +0 -0
- package/skills/make-figures/templates/official/spirit2013/SPIRIT_2025_participant_timeline.docx +0 -0
- package/skills/make-figures/templates/official/stard2015/STARD_2015_checklist.docx +0 -0
- package/skills/make-figures/templates/official/stard2015/STARD_2015_flow_diagram.pdf +0 -0
- package/skills/make-figures/tests/fixtures/figure1_flow.yaml +8 -0
- package/skills/make-figures/tests/fixtures/manuscript_ok.md +9 -0
- package/skills/make-figures/tests/fixtures/manuscript_stale.md +4 -0
- package/skills/make-figures/tests/test_legend_reconcile.sh +36 -0
- package/skills/manage-project/SKILL.md +358 -0
- package/skills/manage-project/references/pre_submission_checklist.md +53 -0
- package/skills/manage-project/references/project_state_template.json +37 -0
- package/skills/manage-project/references/scaffold_templates.md +118 -0
- package/skills/manage-project/references/status_output_format.md +44 -0
- package/skills/manage-project/references/timeline_example.md +20 -0
- package/skills/manage-project/skill.yml +36 -0
- package/skills/manage-project/templates/SSOT.yaml.template +41 -0
- package/skills/manage-refs/LICENSE.zotero-mcp +21 -0
- package/skills/manage-refs/NOTICE.md +29 -0
- package/skills/manage-refs/SKILL.md +289 -0
- package/skills/manage-refs/citation_styles/README.md +40 -0
- package/skills/manage-refs/citation_styles/american-journal-of-roentgenology.csl +211 -0
- package/skills/manage-refs/citation_styles/cardiovascular-and-interventional-radiology.csl +19 -0
- package/skills/manage-refs/citation_styles/european-radiology.csl +19 -0
- package/skills/manage-refs/citation_styles/journal-of-cachexia-sarcopenia-and-muscle.csl +150 -0
- package/skills/manage-refs/citation_styles/journal-of-korean-medical-science-strict.csl +533 -0
- package/skills/manage-refs/citation_styles/journal-of-korean-medical-science.csl +16 -0
- package/skills/manage-refs/citation_styles/korean-journal-of-radiology.csl +155 -0
- package/skills/manage-refs/citation_styles/nature.csl +189 -0
- package/skills/manage-refs/citation_styles/nlm-citation-sequence.csl +535 -0
- package/skills/manage-refs/citation_styles/radiology.csl +228 -0
- package/skills/manage-refs/citation_styles/springer-basic-brackets.csl +187 -0
- package/skills/manage-refs/citation_styles/springer-vancouver-brackets.csl +276 -0
- package/skills/manage-refs/citation_styles/vancouver-superscript.csl +536 -0
- package/skills/manage-refs/citation_styles/vancouver.csl +535 -0
- package/skills/manage-refs/references/REFERENCE_STYLE_SPECS.md +59 -0
- package/skills/manage-refs/references/check_xref_symptoms.md +35 -0
- package/skills/manage-refs/scripts/_vendor_citation_writer.py +600 -0
- package/skills/manage-refs/scripts/check_citation_keys.py +112 -0
- package/skills/manage-refs/scripts/check_csl_render.py +102 -0
- package/skills/manage-refs/scripts/check_xref.py +633 -0
- package/skills/manage-refs/scripts/fill_journal_abbrev.py +104 -0
- package/skills/manage-refs/scripts/inject_zotero_cwyw.py +133 -0
- package/skills/manage-refs/scripts/md_marker_convert.py +193 -0
- package/skills/manage-refs/scripts/pre_submission_gate.sh +238 -0
- package/skills/manage-refs/scripts/render_pandoc.sh +88 -0
- package/skills/manage-refs/skill.yml +70 -0
- package/skills/manage-refs/tests/fixtures/pre_submission_gate/README.md +32 -0
- package/skills/manage-refs/tests/fixtures/pre_submission_gate/manuscript.md +10 -0
- package/skills/manage-refs/tests/fixtures/pre_submission_gate/refs.bib +34 -0
- package/skills/manage-refs/tests/fixtures/pre_submission_gate/run.sh +117 -0
- package/skills/manage-refs/tests/test_vN_docx_check.sh +145 -0
- package/skills/meta-analysis/SKILL.md +739 -0
- package/skills/meta-analysis/references/LICENSES.md +21 -0
- package/skills/meta-analysis/references/PROSPERO_template.md +221 -0
- package/skills/meta-analysis/references/ai_pre_screening_template.py +245 -0
- package/skills/meta-analysis/references/checklists/JBI_Case_Series.md +45 -0
- package/skills/meta-analysis/references/checklists/NOS.md +88 -0
- package/skills/meta-analysis/references/checklists/PRISMA_DTA.md +36 -0
- package/skills/meta-analysis/references/checklists/PROBAST.md +75 -0
- package/skills/meta-analysis/references/checklists/QUADAS2.md +77 -0
- package/skills/meta-analysis/references/checklists/ROBINS_I.md +87 -0
- package/skills/meta-analysis/references/checklists/RoB2.md +79 -0
- package/skills/meta-analysis/references/data_integrity_checklist.md +57 -0
- package/skills/meta-analysis/references/icmje_coi_guide.md +181 -0
- package/skills/meta-analysis/references/phase10_recovery.md +136 -0
- package/skills/meta-analysis/references/phase4_km_composite.md +58 -0
- package/skills/meta-analysis/references/phase6_statistical_synthesis.md +148 -0
- package/skills/meta-analysis/references/phase9_circulation.md +84 -0
- package/skills/meta-analysis/references/post_submission_release_ops.md +41 -0
- package/skills/meta-analysis/references/r_templates.md +132 -0
- package/skills/meta-analysis/references/review_orchestration.md +40 -0
- package/skills/meta-analysis/references/submission_package_drift.md +71 -0
- package/skills/meta-analysis/scripts/check_pool_consistency.py +201 -0
- package/skills/meta-analysis/scripts/cohort_overlap_check.py +242 -0
- package/skills/meta-analysis/scripts/dta_extraction_qc.py +137 -0
- package/skills/meta-analysis/scripts/screening_reconcile.py +160 -0
- package/skills/meta-analysis/skill.yml +47 -0
- package/skills/meta-analysis/templates/FINAL_POOL_LOCK.yaml.template +70 -0
- package/skills/meta-analysis/templates/extraction_form_v2.md +129 -0
- package/skills/meta-analysis/templates/supplementary_8file_checklist.md +94 -0
- package/skills/meta-analysis/tests/test_pool_consistency.sh +123 -0
- package/skills/orchestrate/SKILL.md +501 -0
- package/skills/orchestrate/references/dialogue_nodes.md +196 -0
- package/skills/orchestrate/references/report_template.md +109 -0
- package/skills/orchestrate/references/report_template_ko.md +88 -0
- package/skills/orchestrate/skill.yml +44 -0
- package/skills/peer-review/SKILL.md +381 -0
- package/skills/peer-review/references/aczel_2021_reviewer2_patterns.md +88 -0
- package/skills/peer-review/references/domain-probes/ai_overclaiming.md +47 -0
- package/skills/peer-review/references/domain-probes/narrative_review.md +44 -0
- package/skills/peer-review/references/domain-probes/observational_confounding.md +48 -0
- package/skills/peer-review/references/domain-probes/radiomics.md +38 -0
- package/skills/peer-review/references/domain-probes/sr_ma.md +87 -0
- package/skills/peer-review/references/domain-probes/survival_prognostic.md +68 -0
- package/skills/peer-review/references/exemplar_reviews/README.md +43 -0
- package/skills/peer-review/references/exemplar_reviews/ai_overclaiming.md +47 -0
- package/skills/peer-review/references/exemplar_reviews/calibration_missing.md +44 -0
- package/skills/peer-review/references/exemplar_reviews/data_leakage.md +48 -0
- package/skills/peer-review/references/exemplar_reviews/reference_standard_validity.md +45 -0
- package/skills/peer-review/references/narrative_review_audit.md +67 -0
- package/skills/peer-review/references/reviewer_calibration/README.md +34 -0
- package/skills/peer-review/references/reviewer_calibration/compliance_floor.md +52 -0
- package/skills/peer-review/references/reviewer_profiles/AJR.md +82 -0
- package/skills/peer-review/references/reviewer_profiles/EURE.md +64 -0
- package/skills/peer-review/references/reviewer_profiles/INSI.md +57 -0
- package/skills/peer-review/references/reviewer_profiles/KJR.md +100 -0
- package/skills/peer-review/references/reviewer_profiles/README.md +32 -0
- package/skills/peer-review/references/reviewer_profiles/RYAI.md +86 -0
- package/skills/peer-review/skill.yml +39 -0
- package/skills/present-paper/SKILL.md +675 -0
- package/skills/present-paper/references/critic_rubrics/slide.md +155 -0
- package/skills/present-paper/references/generate_pptx_templates.py +604 -0
- package/skills/present-paper/references/medical_presentation_templates.md +277 -0
- package/skills/present-paper/references/slide_design_principles.md +202 -0
- package/skills/present-paper/references/slide_visual_styles/nature_lancet.md +168 -0
- package/skills/present-paper/references/workflow-checklist.md +109 -0
- package/skills/present-paper/scripts/extract_pdf_figures.py +243 -0
- package/skills/present-paper/scripts/inject_pronunciation_notes.py +178 -0
- package/skills/present-paper/scripts/inject_speaker_notes.py +133 -0
- package/skills/present-paper/scripts/strip_notes_for_sharing.py +140 -0
- package/skills/present-paper/scripts/trim_caption.py +271 -0
- package/skills/present-paper/skill.yml +41 -0
- package/skills/present-paper/templates/build_pptx_nature_lancet.py +688 -0
- package/skills/publish-skill/SKILL.md +370 -0
- package/skills/publish-skill/references/license-compatibility-matrix.md +132 -0
- package/skills/publish-skill/references/pii-patterns.md +130 -0
- package/skills/publish-skill/scripts/audit_skill.sh +278 -0
- package/skills/publish-skill/skill.yml +35 -0
- package/skills/render-pdf-doc/SKILL.md +146 -0
- package/skills/render-pdf-doc/references/known_pitfalls.md +53 -0
- package/skills/render-pdf-doc/references/pandoc_korean_cheatsheet.md +77 -0
- package/skills/render-pdf-doc/scripts/check_deps.sh +42 -0
- package/skills/render-pdf-doc/scripts/infer_colwidths.py +164 -0
- package/skills/render-pdf-doc/scripts/render_pdf.sh +98 -0
- package/skills/render-pdf-doc/skill.yml +57 -0
- package/skills/render-pdf-doc/templates/anchor-doc.md +27 -0
- package/skills/render-pdf-doc/templates/anchor-doc_ko.md +25 -0
- package/skills/render-pdf-doc/templates/briefing-handout.md +33 -0
- package/skills/render-pdf-doc/templates/briefing-handout_ko.md +31 -0
- package/skills/render-pdf-doc/templates/proposal-cover.md +33 -0
- package/skills/render-pdf-doc/templates/proposal-cover_ko.md +31 -0
- package/skills/render-pdf-doc/templates/reference-table.md +22 -0
- package/skills/render-pdf-doc/templates/reference-table_ko.md +20 -0
- package/skills/replicate-study/SKILL.md +150 -0
- package/skills/replicate-study/references/harmonization_3country.csv +47 -0
- package/skills/replicate-study/references/harmonization_knhanes_nhanes.csv +68 -0
- package/skills/replicate-study/references/methodology_extraction_template.md +134 -0
- package/skills/replicate-study/skill.yml +37 -0
- package/skills/review-paper/SKILL.md +104 -0
- package/skills/review-paper/references/macro_skeleton.md +6 -0
- package/skills/review-paper/skill.yml +25 -0
- package/skills/revise/SKILL.md +515 -0
- package/skills/revise/references/r2r_voice.md +346 -0
- package/skills/revise/skill.yml +43 -0
- package/skills/search-lit/SKILL.md +443 -0
- package/skills/search-lit/references/parse_pubmed.py +326 -0
- package/skills/search-lit/references/pubmed_eutils.sh +111 -0
- package/skills/search-lit/skill.yml +46 -0
- package/skills/self-review/SKILL.md +1045 -0
- package/skills/self-review/references/domain-probes/ai_overclaiming.md +47 -0
- package/skills/self-review/references/domain-probes/narrative_review.md +44 -0
- package/skills/self-review/references/domain-probes/observational_confounding.md +48 -0
- package/skills/self-review/references/domain-probes/radiomics.md +38 -0
- package/skills/self-review/references/domain-probes/sr_ma.md +87 -0
- package/skills/self-review/references/domain-probes/survival_prognostic.md +68 -0
- package/skills/self-review/references/exemplar_findings/README.md +43 -0
- package/skills/self-review/references/exemplar_findings/cohort_arithmetic_mismatch.md +35 -0
- package/skills/self-review/references/exemplar_findings/estimand_drift_posthoc_primary.md +39 -0
- package/skills/self-review/references/exemplar_findings/scope_overreach_cross_sectional.md +35 -0
- package/skills/self-review/references/exemplar_findings/unadjusted_confounder.md +36 -0
- package/skills/self-review/references/panel_review_template.md +177 -0
- package/skills/self-review/scripts/check_artifact_coverage.py +301 -0
- package/skills/self-review/scripts/check_claim_artifact.py +248 -0
- package/skills/self-review/scripts/check_classical_style.py +185 -0
- package/skills/self-review/scripts/check_cohort_arithmetic.py +481 -0
- package/skills/self-review/scripts/check_confounding_completeness.py +287 -0
- package/skills/self-review/scripts/check_panel_diversity.py +336 -0
- package/skills/self-review/scripts/check_reference_adequacy.py +392 -0
- package/skills/self-review/scripts/check_reviewer_team_consistency.py +412 -0
- package/skills/self-review/scripts/check_scope_coherence.py +177 -0
- package/skills/self-review/skill.yml +47 -0
- package/skills/self-review/tests/fixtures/claim_manuscript.md +17 -0
- package/skills/self-review/tests/fixtures/claim_prereg.md +6 -0
- package/skills/self-review/tests/fixtures/cohort_bad.md +21 -0
- package/skills/self-review/tests/fixtures/cohort_clean.md +21 -0
- package/skills/self-review/tests/fixtures/cohort_partition.csv +5 -0
- package/skills/self-review/tests/fixtures/coverage_analysis/31_delong_nested_added_value.csv +3 -0
- package/skills/self-review/tests/fixtures/coverage_analysis/table1_demographics.csv +3 -0
- package/skills/self-review/tests/fixtures/coverage_clean.md +13 -0
- package/skills/self-review/tests/fixtures/coverage_manuscript.md +11 -0
- package/skills/self-review/tests/fixtures/panel_collapse.json +27 -0
- package/skills/self-review/tests/fixtures/panel_good.json +32 -0
- package/skills/self-review/tests/fixtures/panel_monoculture.json +32 -0
- package/skills/self-review/tests/fixtures/refadeq_letter.md +13 -0
- package/skills/self-review/tests/fixtures/refadeq_original_fixed.md +42 -0
- package/skills/self-review/tests/fixtures/refadeq_original_uncited.md +40 -0
- package/skills/self-review/tests/fixtures/scope_bad.md +9 -0
- package/skills/self-review/tests/fixtures/scope_clean.md +8 -0
- package/skills/self-review/tests/fixtures/scope_surrogate.md +8 -0
- package/skills/self-review/tests/fixtures/style_bad.md +13 -0
- package/skills/self-review/tests/fixtures/style_clean.md +11 -0
- package/skills/self-review/tests/fixtures/table1_by_exposure.csv +11 -0
- package/skills/self-review/tests/test_artifact_coverage.sh +44 -0
- package/skills/self-review/tests/test_claim_artifact.sh +50 -0
- package/skills/self-review/tests/test_classical_style.sh +44 -0
- package/skills/self-review/tests/test_cohort_arithmetic.sh +49 -0
- package/skills/self-review/tests/test_confounding_completeness.sh +66 -0
- package/skills/self-review/tests/test_panel_diversity.sh +55 -0
- package/skills/self-review/tests/test_panel_mode.sh +69 -0
- package/skills/self-review/tests/test_reference_adequacy.sh +68 -0
- package/skills/self-review/tests/test_reviewer_team_consistency.sh +138 -0
- package/skills/self-review/tests/test_scope_coherence.sh +46 -0
- package/skills/setup-medsci/SKILL.md +110 -0
- package/skills/setup-medsci/references/setup-checklist.md +51 -0
- package/skills/setup-medsci/skill.yml +30 -0
- package/skills/sync-submission/SKILL.md +382 -0
- package/skills/sync-submission/scripts/author_registry_example.yaml +36 -0
- package/skills/sync-submission/scripts/blind_sweep.py +203 -0
- package/skills/sync-submission/scripts/check_asset_anonymization.py +300 -0
- package/skills/sync-submission/scripts/check_cross_artifact_stale.py +211 -0
- package/skills/sync-submission/scripts/cover_letter_drift_check.py +451 -0
- package/skills/sync-submission/scripts/cross_document_n_check.py +486 -0
- package/skills/sync-submission/scripts/detect_copy_divergence.py +136 -0
- package/skills/sync-submission/scripts/preflight_gate.py +458 -0
- package/skills/sync-submission/scripts/scope_drift_check.py +362 -0
- package/skills/sync-submission/scripts/sync_submission.py +169 -0
- package/skills/sync-submission/skill.yml +43 -0
- package/skills/sync-submission/tests/fixtures/copy_ok.md +5 -0
- package/skills/sync-submission/tests/fixtures/copy_stale.md +5 -0
- package/skills/sync-submission/tests/fixtures/ssot.md +5 -0
- package/skills/sync-submission/tests/test_asset_anonymization.sh +99 -0
- package/skills/sync-submission/tests/test_copy_divergence.sh +44 -0
- package/skills/sync-submission/tests/test_cross_artifact_stale.sh +80 -0
- package/skills/sync-submission/tests/test_cross_document_n.sh +132 -0
- package/skills/sync-submission/tests/test_preflight_gate.sh +112 -0
- package/skills/sync-submission/tests/test_scope_drift.sh +122 -0
- package/skills/sync-submission/tests/test_vN_docx_assertion.sh +51 -0
- package/skills/verify-refs/SKILL.md +177 -0
- package/skills/verify-refs/references/manual_checkpoint_guide.md +100 -0
- package/skills/verify-refs/scripts/verify_cli.sh +62 -0
- package/skills/verify-refs/scripts/verify_refs.py +782 -0
- package/skills/verify-refs/skill.yml +44 -0
- package/skills/verify-refs/tests/fixtures/pagination_placeholder.bib +17 -0
- package/skills/verify-refs/tests/test_pagination_placeholder.sh +42 -0
- package/skills/version-dataset/SKILL.md +143 -0
- package/skills/version-dataset/references/manifest_schema.md +72 -0
- package/skills/version-dataset/scripts/version_dataset.py +242 -0
- package/skills/version-dataset/skill.yml +35 -0
- package/skills/version-dataset/tests/test_version_dataset.sh +52 -0
- package/skills/write-paper/SKILL.md +1148 -0
- package/skills/write-paper/references/exemplar_methods/README.md +38 -0
- package/skills/write-paper/references/exemplar_methods/ai_validation_tripod_claim.md +47 -0
- package/skills/write-paper/references/exemplar_methods/diagnostic_accuracy_stard.md +50 -0
- package/skills/write-paper/references/exemplar_methods/observational_cohort_strobe.md +43 -0
- package/skills/write-paper/references/journal_profiles/AJNR.md +185 -0
- package/skills/write-paper/references/journal_profiles/AJR.md +149 -0
- package/skills/write-paper/references/journal_profiles/Abdominal_Radiology.md +139 -0
- package/skills/write-paper/references/journal_profiles/Academic_Radiology.md +90 -0
- package/skills/write-paper/references/journal_profiles/Annals_of_Internal_Medicine.md +150 -0
- package/skills/write-paper/references/journal_profiles/Artificial_Intelligence_in_Medicine.md +82 -0
- package/skills/write-paper/references/journal_profiles/British_Journal_of_Radiology.md +161 -0
- package/skills/write-paper/references/journal_profiles/CVIR.md +157 -0
- package/skills/write-paper/references/journal_profiles/Chest.md +270 -0
- package/skills/write-paper/references/journal_profiles/Clinical_Radiology.md +160 -0
- package/skills/write-paper/references/journal_profiles/Clinical_and_Molecular_Hepatology.md +147 -0
- package/skills/write-paper/references/journal_profiles/Diabetes_Metabolism_Journal.md +163 -0
- package/skills/write-paper/references/journal_profiles/Diagnostic_and_Interventional_Radiology.md +216 -0
- package/skills/write-paper/references/journal_profiles/Endocrinology_and_Metabolism.md +167 -0
- package/skills/write-paper/references/journal_profiles/European_Journal_of_Preventive_Cardiology.md +192 -0
- package/skills/write-paper/references/journal_profiles/European_Radiology.md +159 -0
- package/skills/write-paper/references/journal_profiles/Hepatology_Communications.md +110 -0
- package/skills/write-paper/references/journal_profiles/Hepatology_International.md +106 -0
- package/skills/write-paper/references/journal_profiles/IEEE_TMI.md +180 -0
- package/skills/write-paper/references/journal_profiles/INSI.md +163 -0
- package/skills/write-paper/references/journal_profiles/Investigative_Radiology.md +86 -0
- package/skills/write-paper/references/journal_profiles/JACC_Advances.md +197 -0
- package/skills/write-paper/references/journal_profiles/JACC_Asia.md +168 -0
- package/skills/write-paper/references/journal_profiles/JACR.md +87 -0
- package/skills/write-paper/references/journal_profiles/JAMA.md +188 -0
- package/skills/write-paper/references/journal_profiles/JAMA_Network_Open.md +170 -0
- package/skills/write-paper/references/journal_profiles/JCSM.md +266 -0
- package/skills/write-paper/references/journal_profiles/JKMS.md +201 -0
- package/skills/write-paper/references/journal_profiles/JMIR.md +88 -0
- package/skills/write-paper/references/journal_profiles/JMIR_Medical_Education.md +86 -0
- package/skills/write-paper/references/journal_profiles/JNIS.md +227 -0
- package/skills/write-paper/references/journal_profiles/JVIR.md +158 -0
- package/skills/write-paper/references/journal_profiles/Journal_of_Clinical_Endocrinology_and_Metabolism.md +191 -0
- package/skills/write-paper/references/journal_profiles/Journal_of_Stroke.md +176 -0
- package/skills/write-paper/references/journal_profiles/KJR.md +185 -0
- package/skills/write-paper/references/journal_profiles/Korean_Circulation_Journal.md +184 -0
- package/skills/write-paper/references/journal_profiles/Korean_Journal_of_Internal_Medicine.md +178 -0
- package/skills/write-paper/references/journal_profiles/Lancet_Gastroenterology_and_Hepatology.md +127 -0
- package/skills/write-paper/references/journal_profiles/Liver_International.md +165 -0
- package/skills/write-paper/references/journal_profiles/Medical_Image_Analysis.md +147 -0
- package/skills/write-paper/references/journal_profiles/NEJM.md +147 -0
- package/skills/write-paper/references/journal_profiles/Nature_Medicine.md +181 -0
- package/skills/write-paper/references/journal_profiles/Neuroradiology.md +151 -0
- package/skills/write-paper/references/journal_profiles/Nutrition_Metabolism_and_Cardiovascular_Diseases.md +184 -0
- package/skills/write-paper/references/journal_profiles/PLOS_Medicine.md +166 -0
- package/skills/write-paper/references/journal_profiles/RYAI.md +124 -0
- package/skills/write-paper/references/journal_profiles/Radiology.md +173 -0
- package/skills/write-paper/references/journal_profiles/Skeletal_Radiology.md +135 -0
- package/skills/write-paper/references/journal_profiles/Stroke.md +210 -0
- package/skills/write-paper/references/journal_profiles/The_BMJ.md +121 -0
- package/skills/write-paper/references/journal_profiles/The_Lancet.md +112 -0
- package/skills/write-paper/references/journal_profiles/The_Lancet_Digital_Health.md +104 -0
- package/skills/write-paper/references/journal_profiles/World_Journal_of_Hepatology.md +106 -0
- package/skills/write-paper/references/journal_profiles/npj_Digital_Medicine.md +93 -0
- package/skills/write-paper/references/paper_types/ai_validation.md +270 -0
- package/skills/write-paper/references/paper_types/animal_study.md +194 -0
- package/skills/write-paper/references/paper_types/case_report.md +237 -0
- package/skills/write-paper/references/paper_types/cross_national.md +328 -0
- package/skills/write-paper/references/paper_types/letter.md +127 -0
- package/skills/write-paper/references/paper_types/meta_analysis.md +181 -0
- package/skills/write-paper/references/paper_types/nhis_cohort.md +297 -0
- package/skills/write-paper/references/paper_types/original_article.md +221 -0
- package/skills/write-paper/references/paper_types/technical_note.md +131 -0
- package/skills/write-paper/references/section_guides/discussion.md +155 -0
- package/skills/write-paper/references/section_guides/introduction.md +108 -0
- package/skills/write-paper/references/section_guides/methods.md +144 -0
- package/skills/write-paper/references/section_guides/results.md +113 -0
- package/skills/write-paper/references/section_guides/step7_1_classical_qc.md +67 -0
- package/skills/write-paper/references/section_guides/step7_4a_audit_recovery.md +74 -0
- package/skills/write-paper/references/section_guides/title_abstract.md +123 -0
- package/skills/write-paper/references/section_templates/methods_statistical.md +147 -0
- package/skills/write-paper/scripts/check_placeholders.py +182 -0
- package/skills/write-paper/skill.yml +48 -0
- package/skills/write-paper/tests/test_placeholders.sh +107 -0
- package/skills/write-protocol/SKILL.md +243 -0
- package/skills/write-protocol/references/ethics_checklist.md +150 -0
- package/skills/write-protocol/references/protocol_template.md +304 -0
- package/skills/write-protocol/skill.yml +34 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Confounding-completeness gate for observational studies (self-review Phase 2.5e).
|
|
3
|
+
|
|
4
|
+
The highest-yield observational reviewer finding is also the most mechanical: a
|
|
5
|
+
covariate that was *measured*, is *imbalanced across exposure groups* in the
|
|
6
|
+
baseline table, and is *absent from the adjustment set* is residual confounding
|
|
7
|
+
by a measured variable. A single-pass prose review misses it because the
|
|
8
|
+
manuscript text is internally consistent; only a join of the exposure-stratified
|
|
9
|
+
Table 1 against the Methods adjustment set exposes it. This script is that join
|
|
10
|
+
(probe O1 of observational_confounding.md), backported from the panel so the
|
|
11
|
+
deterministic finding lands without a multi-agent pass.
|
|
12
|
+
|
|
13
|
+
INPUTS
|
|
14
|
+
--table1 exposure-stratified baseline table, CSV. One row per covariate.
|
|
15
|
+
Needs a covariate-name column and a p-value (or SMD) column. Column
|
|
16
|
+
names are auto-detected (case-insensitive); override with
|
|
17
|
+
--name-col / --p-col / --smd-col. A file named like
|
|
18
|
+
`table1_by_<exposure>.csv` is the convention.
|
|
19
|
+
--adjusted adjustment-set variables. Either a path to a file (one variable per
|
|
20
|
+
line, or a Methods paragraph the script greps after "adjusted for")
|
|
21
|
+
or a comma-separated list passed inline with --adjusted-list.
|
|
22
|
+
|
|
23
|
+
OUTPUT
|
|
24
|
+
A reconciliation table (stdout) and, with --out, a JSON artifact:
|
|
25
|
+
{covariate, imbalance_p / smd, in_adjustment_set, verdict}
|
|
26
|
+
verdict UNADJUSTED_IMBALANCED is the Major candidate. Exit 1 (with --strict)
|
|
27
|
+
when any UNADJUSTED_IMBALANCED row exists.
|
|
28
|
+
|
|
29
|
+
Matching the adjustment set to Table-1 covariate labels is fuzzy (a table row
|
|
30
|
+
"Smoking, pack-years" vs an adjustment token "smoking"), so the match is a
|
|
31
|
+
normalized-substring test in both directions; review the reconciliation table
|
|
32
|
+
rather than trusting the count blindly.
|
|
33
|
+
|
|
34
|
+
Stdlib-only (csv / json / re / argparse). Exit codes: 0 clean (or report-only),
|
|
35
|
+
1 unadjusted-imbalanced rows found (with --strict), 2 input/usage error.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import argparse
|
|
41
|
+
import csv
|
|
42
|
+
import json
|
|
43
|
+
import re
|
|
44
|
+
import sys
|
|
45
|
+
from pathlib import Path
|
|
46
|
+
|
|
47
|
+
# --- column auto-detection -------------------------------------------------
|
|
48
|
+
|
|
49
|
+
NAME_HINTS = ("covariate", "variable", "characteristic", "feature", "name", "")
|
|
50
|
+
P_HINTS = ("p_value", "pvalue", "p-value", "p val", "p", "pr")
|
|
51
|
+
SMD_HINTS = ("smd", "std_diff", "standardized", "std. mean", "std mean")
|
|
52
|
+
|
|
53
|
+
P_THRESHOLD = 0.05
|
|
54
|
+
SMD_THRESHOLD = 0.10
|
|
55
|
+
|
|
56
|
+
# Header / summary rows that are not covariates (sample-size lines, group totals,
|
|
57
|
+
# trend-p rows). Matched on the whole normalized label, not a substring, so a real
|
|
58
|
+
# covariate like "Total cholesterol" is not swallowed by "total".
|
|
59
|
+
def _is_skip_row(cov: str) -> bool:
|
|
60
|
+
c = _norm(cov)
|
|
61
|
+
if c in ("", "total", "overall", "n", "no", "number"):
|
|
62
|
+
return True
|
|
63
|
+
if re.match(r"^n\s*[=:]", cov.strip().lower()): # "N = ...", "n: ..."
|
|
64
|
+
return True
|
|
65
|
+
if "p for trend" in c or "p trend" in c or "for trend" in c:
|
|
66
|
+
return True
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _norm(s: str) -> str:
|
|
71
|
+
"""Lowercase, drop punctuation/units, collapse whitespace for fuzzy match."""
|
|
72
|
+
s = s.lower()
|
|
73
|
+
s = re.sub(r"\(.*?\)", " ", s) # drop "(mg/dL)", "(%)"
|
|
74
|
+
s = re.sub(r"[^a-z0-9 ]+", " ", s) # punctuation -> space
|
|
75
|
+
s = re.sub(r"\s+", " ", s).strip()
|
|
76
|
+
return s
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _pick_col(header: list[str], hints: tuple[str, ...], override: str | None) -> int | None:
|
|
80
|
+
if override:
|
|
81
|
+
for i, h in enumerate(header):
|
|
82
|
+
if _norm(h) == _norm(override):
|
|
83
|
+
return i
|
|
84
|
+
sys.stderr.write(f"ERROR: column '{override}' not found in header {header}\n")
|
|
85
|
+
return None
|
|
86
|
+
norm = [_norm(h) for h in header]
|
|
87
|
+
# exact-ish first
|
|
88
|
+
for hint in hints:
|
|
89
|
+
h = _norm(hint)
|
|
90
|
+
for i, col in enumerate(norm):
|
|
91
|
+
if col == h and h:
|
|
92
|
+
return i
|
|
93
|
+
# then substring
|
|
94
|
+
for hint in hints:
|
|
95
|
+
h = _norm(hint)
|
|
96
|
+
for i, col in enumerate(norm):
|
|
97
|
+
if h and h in col:
|
|
98
|
+
return i
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _parse_p(raw: str) -> float | None:
|
|
103
|
+
"""Parse a p-value cell: '0.001', '<0.001', 'p<0.01', '0.03*', 'NS'."""
|
|
104
|
+
if raw is None:
|
|
105
|
+
return None
|
|
106
|
+
s = raw.strip().lower()
|
|
107
|
+
if not s or s in ("ns", "na", "n/a", "-", "."):
|
|
108
|
+
return 1.0 if s == "ns" else None
|
|
109
|
+
m = re.search(r"<\s*(0?\.[0-9]+|[0-9]+\.?[0-9]*)", s) # "<0.001", "p<.01"
|
|
110
|
+
if m:
|
|
111
|
+
try: # report just under the stated bound
|
|
112
|
+
return max(float(m.group(1)) - 1e-6, 0.0)
|
|
113
|
+
except ValueError:
|
|
114
|
+
return None
|
|
115
|
+
m = re.search(r"0?\.[0-9]+|[0-9]+\.?[0-9]*", s)
|
|
116
|
+
if m:
|
|
117
|
+
try:
|
|
118
|
+
return float(m.group(0))
|
|
119
|
+
except ValueError:
|
|
120
|
+
return None
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _parse_float(raw: str) -> float | None:
|
|
125
|
+
if raw is None:
|
|
126
|
+
return None
|
|
127
|
+
m = re.search(r"-?[0-9]*\.?[0-9]+", raw.strip())
|
|
128
|
+
return float(m.group(0)) if m else None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# --- adjustment set --------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
def load_adjustment_set(path: str | None, inline: str | None) -> list[str]:
|
|
134
|
+
if inline:
|
|
135
|
+
return [t.strip() for t in inline.split(",") if t.strip()]
|
|
136
|
+
if not path:
|
|
137
|
+
return []
|
|
138
|
+
p = Path(path)
|
|
139
|
+
if not p.is_file():
|
|
140
|
+
sys.stderr.write(f"ERROR: adjustment file not found: {path}\n")
|
|
141
|
+
sys.exit(2)
|
|
142
|
+
text = p.read_text(encoding="utf-8")
|
|
143
|
+
# If the file is a Methods paragraph, grep the "adjusted for ..." clause.
|
|
144
|
+
m = re.search(r"adjust(?:ed|ing)?\s+for\s+(.+?)(?:\.|;|\n\n|$)", text, re.I | re.S)
|
|
145
|
+
if m:
|
|
146
|
+
clause = m.group(1)
|
|
147
|
+
parts = re.split(r",| and | as well as ", clause)
|
|
148
|
+
return [p2.strip() for p2 in parts if p2.strip()]
|
|
149
|
+
# Otherwise treat as one variable per line.
|
|
150
|
+
return [ln.strip() for ln in text.splitlines() if ln.strip() and not ln.startswith("#")]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def in_adjustment_set(cov: str, adj_norm: list[str]) -> bool:
|
|
154
|
+
c = _norm(cov)
|
|
155
|
+
if not c:
|
|
156
|
+
return False
|
|
157
|
+
for a in adj_norm:
|
|
158
|
+
if not a:
|
|
159
|
+
continue
|
|
160
|
+
if a in c or c in a:
|
|
161
|
+
return True
|
|
162
|
+
# token overlap on the leading word (smoking ~ "smoking, pack-years")
|
|
163
|
+
if c.split(" ")[0] == a.split(" ")[0] and len(c.split(" ")[0]) >= 3:
|
|
164
|
+
return True
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# --- core ------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
def analyze(table1: str, adj: list[str], name_col, p_col, smd_col) -> dict:
|
|
171
|
+
p = Path(table1)
|
|
172
|
+
if not p.is_file():
|
|
173
|
+
sys.stderr.write(f"ERROR: table1 not found: {table1}\n")
|
|
174
|
+
sys.exit(2)
|
|
175
|
+
with p.open(encoding="utf-8-sig", newline="") as f:
|
|
176
|
+
reader = csv.reader(f)
|
|
177
|
+
rows = [r for r in reader if any(c.strip() for c in r)]
|
|
178
|
+
if len(rows) < 2:
|
|
179
|
+
sys.stderr.write("ERROR: table1 has no data rows\n")
|
|
180
|
+
sys.exit(2)
|
|
181
|
+
header = rows[0]
|
|
182
|
+
ni = _pick_col(header, NAME_HINTS, name_col)
|
|
183
|
+
pi = _pick_col(header, P_HINTS, p_col)
|
|
184
|
+
si = _pick_col(header, SMD_HINTS, smd_col)
|
|
185
|
+
if ni is None:
|
|
186
|
+
ni = 0
|
|
187
|
+
if pi is None and si is None:
|
|
188
|
+
sys.stderr.write("ERROR: could not locate a p-value or SMD column; pass --p-col/--smd-col\n")
|
|
189
|
+
sys.exit(2)
|
|
190
|
+
|
|
191
|
+
adj_norm = [_norm(a) for a in adj]
|
|
192
|
+
findings = []
|
|
193
|
+
for r in rows[1:]:
|
|
194
|
+
if ni >= len(r):
|
|
195
|
+
continue
|
|
196
|
+
cov = r[ni].strip()
|
|
197
|
+
if _is_skip_row(cov):
|
|
198
|
+
continue
|
|
199
|
+
pval = _parse_p(r[pi]) if (pi is not None and pi < len(r)) else None
|
|
200
|
+
smd = _parse_float(r[si]) if (si is not None and si < len(r)) else None
|
|
201
|
+
imbalanced = (pval is not None and pval < P_THRESHOLD) or \
|
|
202
|
+
(smd is not None and abs(smd) >= SMD_THRESHOLD)
|
|
203
|
+
if not imbalanced:
|
|
204
|
+
continue
|
|
205
|
+
adjusted = in_adjustment_set(cov, adj_norm)
|
|
206
|
+
findings.append({
|
|
207
|
+
"covariate": cov,
|
|
208
|
+
"imbalance_p": pval,
|
|
209
|
+
"smd": smd,
|
|
210
|
+
"in_adjustment_set": adjusted,
|
|
211
|
+
"verdict": "ADJUSTED" if adjusted else "UNADJUSTED_IMBALANCED",
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
unadjusted = [f for f in findings if f["verdict"] == "UNADJUSTED_IMBALANCED"]
|
|
215
|
+
return {
|
|
216
|
+
"table1": str(p),
|
|
217
|
+
"adjustment_set": adj,
|
|
218
|
+
"thresholds": {"p": P_THRESHOLD, "smd": SMD_THRESHOLD},
|
|
219
|
+
"n_imbalanced": len(findings),
|
|
220
|
+
"n_unadjusted_imbalanced": len(unadjusted),
|
|
221
|
+
"findings": findings,
|
|
222
|
+
"verdict": "MAJOR_CANDIDATE" if unadjusted else "OK",
|
|
223
|
+
"suggested_fix": (
|
|
224
|
+
"Report an extended-adjustment sensitivity model adding the "
|
|
225
|
+
"unadjusted imbalanced covariates; keep the original model primary "
|
|
226
|
+
"only if the extended model agrees."
|
|
227
|
+
) if unadjusted else None,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def render_table(result: dict) -> str:
|
|
232
|
+
lines = [
|
|
233
|
+
"| Covariate | Imbalance p | SMD | In adjustment set? | Verdict |",
|
|
234
|
+
"|---|---|---|---|---|",
|
|
235
|
+
]
|
|
236
|
+
for f in result["findings"]:
|
|
237
|
+
p = "—" if f["imbalance_p"] is None else f"{f['imbalance_p']:.4g}"
|
|
238
|
+
s = "—" if f["smd"] is None else f"{f['smd']:.3g}"
|
|
239
|
+
mark = "✗ Major" if f["verdict"] == "UNADJUSTED_IMBALANCED" else "✓"
|
|
240
|
+
lines.append(
|
|
241
|
+
f"| {f['covariate']} | {p} | {s} | "
|
|
242
|
+
f"{'yes' if f['in_adjustment_set'] else 'NO'} | {mark} |"
|
|
243
|
+
)
|
|
244
|
+
return "\n".join(lines)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def main() -> int:
|
|
248
|
+
ap = argparse.ArgumentParser(description="Observational confounding-completeness gate (O1).")
|
|
249
|
+
ap.add_argument("--table1", required=True, help="exposure-stratified Table 1 CSV")
|
|
250
|
+
ap.add_argument("--adjusted", help="adjustment-set file (var-per-line or Methods paragraph)")
|
|
251
|
+
ap.add_argument("--adjusted-list", help="comma-separated adjustment variables (inline)")
|
|
252
|
+
ap.add_argument("--name-col", help="override covariate-name column header")
|
|
253
|
+
ap.add_argument("--p-col", help="override p-value column header")
|
|
254
|
+
ap.add_argument("--smd-col", help="override SMD column header")
|
|
255
|
+
ap.add_argument("--out", help="write JSON artifact to this path")
|
|
256
|
+
ap.add_argument("--strict", action="store_true", help="exit 1 if unadjusted-imbalanced rows exist")
|
|
257
|
+
args = ap.parse_args()
|
|
258
|
+
|
|
259
|
+
adj = load_adjustment_set(args.adjusted, args.adjusted_list)
|
|
260
|
+
if not adj:
|
|
261
|
+
sys.stderr.write("WARN: empty adjustment set — every imbalanced covariate will flag.\n")
|
|
262
|
+
|
|
263
|
+
result = analyze(args.table1, adj, args.name_col, args.p_col, args.smd_col)
|
|
264
|
+
|
|
265
|
+
print("=" * 41)
|
|
266
|
+
print(" Confounding Completeness (Phase 2.5e / O1)")
|
|
267
|
+
print("=" * 41)
|
|
268
|
+
print(f"adjustment set: {', '.join(adj) if adj else '(none)'}")
|
|
269
|
+
print(render_table(result))
|
|
270
|
+
print()
|
|
271
|
+
if result["n_unadjusted_imbalanced"]:
|
|
272
|
+
print(f"MAJOR candidate: {result['n_unadjusted_imbalanced']} imbalanced covariate(s) "
|
|
273
|
+
f"absent from the adjustment set.")
|
|
274
|
+
print(f"Fix: {result['suggested_fix']}")
|
|
275
|
+
else:
|
|
276
|
+
print("OK: no measured-but-unadjusted imbalanced covariate.")
|
|
277
|
+
|
|
278
|
+
if args.out:
|
|
279
|
+
Path(args.out).parent.mkdir(parents=True, exist_ok=True)
|
|
280
|
+
Path(args.out).write_text(json.dumps(result, indent=2), encoding="utf-8")
|
|
281
|
+
print(f"\nwrote {args.out}")
|
|
282
|
+
|
|
283
|
+
return 1 if (args.strict and result["n_unadjusted_imbalanced"]) else 0
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
if __name__ == "__main__":
|
|
287
|
+
sys.exit(main())
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Panel lens-diversity gate (self-review Phase 2.6, --panel).
|
|
3
|
+
|
|
4
|
+
A multi-agent panel is only worth its cost if its reviewers cover *distinct*
|
|
5
|
+
concern axes. Left unchecked, independent reviewers converge on the same easy
|
|
6
|
+
themes (everyone flags "missing calibration") while whole high-risk axes go
|
|
7
|
+
unprobed — the panel collapses to fewer effective lenses than reviewers, and
|
|
8
|
+
the editor synthesis cannot tell monoculture from genuine consensus. This gate
|
|
9
|
+
post-processes the reviewers' structured output (the panel_review_template
|
|
10
|
+
schema the editor already collects) and reports three diversity failures:
|
|
11
|
+
|
|
12
|
+
UNCOVERED_AXIS an expected high-risk axis for this research type produced
|
|
13
|
+
ZERO major findings across the whole panel. Mirrors the
|
|
14
|
+
"completeness critic" pattern: name what nobody probed so
|
|
15
|
+
the editor can re-probe it before finalizing. (Major)
|
|
16
|
+
FAMILY_MONOCULTURE the panel's major findings concentrate in ONE concern
|
|
17
|
+
family beyond a threshold (one family holds the majority),
|
|
18
|
+
a signal the lenses converged rather than spanned the
|
|
19
|
+
manuscript. (Major)
|
|
20
|
+
LENS_COLLAPSE one reviewer contributed only families that another
|
|
21
|
+
reviewer already covered — a fully-redundant lens that
|
|
22
|
+
added no independent signal. Distinct from healthy
|
|
23
|
+
CONSENSUS (a reviewer agreeing on SOME themes but also
|
|
24
|
+
raising at least one family nobody else did). (Flag)
|
|
25
|
+
|
|
26
|
+
Healthy consensus is preserved: a finding family raised by ≥2 reviewers is a
|
|
27
|
+
strength, not a defect. The gate only fires LENS_COLLAPSE when a reviewer's
|
|
28
|
+
ENTIRE contribution is redundant, and UNCOVERED_AXIS/MONOCULTURE on
|
|
29
|
+
panel-level coverage, never on agreement per se.
|
|
30
|
+
|
|
31
|
+
INPUTS
|
|
32
|
+
--panel JSON file. Either a list of reviewer objects, or an object with a
|
|
33
|
+
"reviewers" list (and optional "research_type"). Each reviewer
|
|
34
|
+
object needs reviewer_id, expertise_area, and major[] (with heading
|
|
35
|
+
and/or comment text); minor[] is ignored for axis coverage.
|
|
36
|
+
--research-type one of: survival, sr_ma, radiomics, dta, observational,
|
|
37
|
+
narrative (synonyms accepted). Overrides any value in the JSON.
|
|
38
|
+
When unknown/absent, UNCOVERED_AXIS is skipped (cannot know the
|
|
39
|
+
expected axes) and noted in the summary.
|
|
40
|
+
|
|
41
|
+
OUTPUT
|
|
42
|
+
A diversity table (stdout) and, with --out, a JSON artifact:
|
|
43
|
+
{panel, research_type, claims[{verdict, severity, detail, where}], summary}
|
|
44
|
+
summary carries the family histogram, concentration index, and the
|
|
45
|
+
expected/covered/uncovered axes. Exit 1 (with --strict) when any Major claim
|
|
46
|
+
exists; exit 2 on input error.
|
|
47
|
+
|
|
48
|
+
Stdlib-only (json / re / argparse / pathlib). Exit codes: 0 clean (or
|
|
49
|
+
report-only), 1 Major claim(s) found (with --strict), 2 input/usage error.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
from __future__ import annotations
|
|
53
|
+
|
|
54
|
+
import argparse
|
|
55
|
+
import json
|
|
56
|
+
import re
|
|
57
|
+
import sys
|
|
58
|
+
from pathlib import Path
|
|
59
|
+
|
|
60
|
+
# Concern families, aligned to the panel's per-domain focus checklists and the
|
|
61
|
+
# self-review A–J category system. Each finding is assigned to the FIRST family
|
|
62
|
+
# whose lexicon matches its text; order is most-specific to most-generic so a
|
|
63
|
+
# leakage finding is not swallowed by the generic "statistics" family.
|
|
64
|
+
FAMILY_LEXICON: list[tuple[str, re.Pattern]] = [
|
|
65
|
+
("search_screening", re.compile(
|
|
66
|
+
r"search strateg|screening|eligibilit|inclusion criteri|exclusion criteri|"
|
|
67
|
+
r"database\b|grey literature|gray literature|duplicate (?:removal|record)|"
|
|
68
|
+
r"prisma flow|records identified|study selection", re.IGNORECASE)),
|
|
69
|
+
("design_leakage", re.compile(
|
|
70
|
+
r"leakage|data (?:split|leak)|train(?:ing|/test| test)|test set|"
|
|
71
|
+
r"contaminat|allocation|randomi[sz]|immortal time|time[-\s]?zero|"
|
|
72
|
+
r"selection bias|spectrum bias|case[-\s]?control selection|"
|
|
73
|
+
r"reference standard|verification bias", re.IGNORECASE)),
|
|
74
|
+
("confounding", re.compile(
|
|
75
|
+
r"confound|residual confounding|covariate|adjust(?:ment|ed)|mediator|"
|
|
76
|
+
r"collider|confounding by indication|propensity", re.IGNORECASE)),
|
|
77
|
+
("imaging", re.compile(
|
|
78
|
+
r"acquisition|scanner|sequence|segmentation|voxel|kernel|reconstruction|"
|
|
79
|
+
r"combat|harmoni[sz]|slice thickness|field strength|radiomic feature|"
|
|
80
|
+
r"window(?:ing| level)|protocol heterogeneity", re.IGNORECASE)),
|
|
81
|
+
("reporting", re.compile(
|
|
82
|
+
r"strobe|tripod|prisma\b|consort|claim\b|stard|reporting (?:guideline|standard|"
|
|
83
|
+
r"completeness)|checklist|flow diagram|disclosure|registration|protocol "
|
|
84
|
+
r"deviation|abstract (?:inconsisten|mismatch)", re.IGNORECASE)),
|
|
85
|
+
("reproducibility", re.compile(
|
|
86
|
+
r"reproducib|code availab|data availab|random seed|\bseed\b|script\b|"
|
|
87
|
+
r"open (?:data|code)|version pin", re.IGNORECASE)),
|
|
88
|
+
("statistics", re.compile(
|
|
89
|
+
r"calibrat|discriminat|\bauc\b|c[-\s]?statistic|delong|confidence interval|"
|
|
90
|
+
r"\bci\b|heterogeneit|\bi2\b|i\^?2|pooling|pooled|random[-\s]?effects|"
|
|
91
|
+
r"multiplicit|multiple compar|\bp[-\s]?value|\bpower\b|sample size|"
|
|
92
|
+
r"model specif|proportional hazard|missing data|imputation|competing risk|"
|
|
93
|
+
r"events per variable|overfitting|effect size|subdistribution", re.IGNORECASE)),
|
|
94
|
+
("clinical", re.compile(
|
|
95
|
+
r"clinical|actionab|guideline|management|generali[sz]ab|applicab|"
|
|
96
|
+
r"external validit|patient[-\s]?care|over(?:reach|claim)|"
|
|
97
|
+
r"clinical (?:relevance|utility|significance)", re.IGNORECASE)),
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
# Expected high-risk axes per research type (each SHOULD yield ≥1 major). Mirrors
|
|
101
|
+
# the Phase 2.6 reviewer-set table; optional axes (e.g. imaging when the exposure
|
|
102
|
+
# is non-imaging) are not required and so are omitted here.
|
|
103
|
+
EXPECTED_AXES: dict[str, list[str]] = {
|
|
104
|
+
"survival": ["statistics", "clinical"],
|
|
105
|
+
"sr_ma": ["search_screening", "clinical", "statistics"],
|
|
106
|
+
"radiomics": ["imaging", "statistics", "clinical"],
|
|
107
|
+
"dta": ["design_leakage", "statistics", "clinical"],
|
|
108
|
+
"observational": ["confounding", "clinical", "statistics"],
|
|
109
|
+
"narrative": ["clinical", "reporting"],
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
RESEARCH_TYPE_SYNONYMS: dict[str, str] = {
|
|
113
|
+
"survival": "survival", "prognostic": "survival", "cohort": "survival",
|
|
114
|
+
"sr": "sr_ma", "ma": "sr_ma", "sr_ma": "sr_ma", "sr/ma": "sr_ma",
|
|
115
|
+
"systematic review": "sr_ma", "meta-analysis": "sr_ma", "meta analysis": "sr_ma",
|
|
116
|
+
"radiomics": "radiomics", "feature": "radiomics",
|
|
117
|
+
"dta": "dta", "diagnostic": "dta", "diagnostic-accuracy": "dta", "ai model": "dta",
|
|
118
|
+
"observational": "observational", "strobe": "observational",
|
|
119
|
+
"narrative": "narrative", "review article": "narrative", "sanra": "narrative",
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
MONOCULTURE_MIN_MAJORS = 4 # too few majors to call concentration meaningful
|
|
123
|
+
MONOCULTURE_SHARE = 0.60 # one family holding > this share = monoculture
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def normalize_research_type(raw: str | None) -> str | None:
|
|
127
|
+
if not raw:
|
|
128
|
+
return None
|
|
129
|
+
key = raw.strip().lower()
|
|
130
|
+
if key in RESEARCH_TYPE_SYNONYMS:
|
|
131
|
+
return RESEARCH_TYPE_SYNONYMS[key]
|
|
132
|
+
for syn, canon in RESEARCH_TYPE_SYNONYMS.items():
|
|
133
|
+
if syn in key:
|
|
134
|
+
return canon
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def classify(text: str) -> str:
|
|
139
|
+
for family, pat in FAMILY_LEXICON:
|
|
140
|
+
if pat.search(text):
|
|
141
|
+
return family
|
|
142
|
+
return "other"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def finding_text(major: dict) -> str:
|
|
146
|
+
parts = [str(major.get(k, "")) for k in ("heading", "comment", "location")]
|
|
147
|
+
return " ".join(p for p in parts if p)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def load_reviewers(obj) -> tuple[list[dict], str | None]:
|
|
151
|
+
if isinstance(obj, list):
|
|
152
|
+
return obj, None
|
|
153
|
+
if isinstance(obj, dict):
|
|
154
|
+
revs = obj.get("reviewers")
|
|
155
|
+
if isinstance(revs, list):
|
|
156
|
+
return revs, obj.get("research_type")
|
|
157
|
+
raise ValueError("panel JSON must be a list of reviewers or an object with a 'reviewers' list")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def check(reviewers: list[dict], research_type: str | None) -> tuple[list[dict], dict]:
|
|
161
|
+
claims: list[dict] = []
|
|
162
|
+
|
|
163
|
+
# Per-reviewer families (set of distinct families this reviewer raised as majors)
|
|
164
|
+
rev_families: dict[str, set[str]] = {}
|
|
165
|
+
family_hist: dict[str, int] = {}
|
|
166
|
+
n_majors = 0
|
|
167
|
+
for i, rev in enumerate(reviewers):
|
|
168
|
+
rid = str(rev.get("reviewer_id") or f"R{i + 1}")
|
|
169
|
+
fams: set[str] = set()
|
|
170
|
+
for maj in rev.get("major", []) or []:
|
|
171
|
+
fam = classify(finding_text(maj))
|
|
172
|
+
fams.add(fam)
|
|
173
|
+
family_hist[fam] = family_hist.get(fam, 0) + 1
|
|
174
|
+
n_majors += 1
|
|
175
|
+
rev_families[rid] = fams
|
|
176
|
+
|
|
177
|
+
covered = set(family_hist)
|
|
178
|
+
covered.discard("other")
|
|
179
|
+
|
|
180
|
+
# 1) UNCOVERED_AXIS — only when we know the expected axes
|
|
181
|
+
expected: list[str] = []
|
|
182
|
+
uncovered: list[str] = []
|
|
183
|
+
if research_type and research_type in EXPECTED_AXES:
|
|
184
|
+
expected = EXPECTED_AXES[research_type]
|
|
185
|
+
uncovered = [ax for ax in expected if family_hist.get(ax, 0) == 0]
|
|
186
|
+
for ax in uncovered:
|
|
187
|
+
claims.append({
|
|
188
|
+
"verdict": "UNCOVERED_AXIS",
|
|
189
|
+
"severity": "Major",
|
|
190
|
+
"detail": (f"no major finding addresses the '{ax}' axis, which a "
|
|
191
|
+
f"{research_type} panel is expected to probe; the editor "
|
|
192
|
+
f"should re-probe it before finalizing"),
|
|
193
|
+
"where": f"expected axes for {research_type}: {', '.join(expected)}",
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
# 2) FAMILY_MONOCULTURE — concentration of majors in one family
|
|
197
|
+
hhi = 0.0
|
|
198
|
+
top_family = None
|
|
199
|
+
top_share = 0.0
|
|
200
|
+
if n_majors:
|
|
201
|
+
shares = {f: c / n_majors for f, c in family_hist.items() if f != "other"}
|
|
202
|
+
hhi = sum(s * s for s in shares.values())
|
|
203
|
+
if shares:
|
|
204
|
+
top_family, top_share = max(shares.items(), key=lambda kv: kv[1])
|
|
205
|
+
if n_majors >= MONOCULTURE_MIN_MAJORS and top_share > MONOCULTURE_SHARE:
|
|
206
|
+
claims.append({
|
|
207
|
+
"verdict": "FAMILY_MONOCULTURE",
|
|
208
|
+
"severity": "Major",
|
|
209
|
+
"detail": (f"{top_share:.0%} of major findings fall in the '{top_family}' "
|
|
210
|
+
f"family ({family_hist.get(top_family, 0)}/{n_majors}); the panel "
|
|
211
|
+
f"converged on one axis rather than spanning the manuscript"),
|
|
212
|
+
"where": "family histogram: " + ", ".join(
|
|
213
|
+
f"{f}={c}" for f, c in sorted(family_hist.items(), key=lambda kv: -kv[1])),
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
# 3) LENS_COLLAPSE — a reviewer whose every family is also covered by another
|
|
217
|
+
for rid, fams in rev_families.items():
|
|
218
|
+
own = {f for f in fams if f != "other"}
|
|
219
|
+
if not own:
|
|
220
|
+
continue
|
|
221
|
+
others = set()
|
|
222
|
+
for other_rid, other_fams in rev_families.items():
|
|
223
|
+
if other_rid != rid:
|
|
224
|
+
others |= {f for f in other_fams if f != "other"}
|
|
225
|
+
if own and own <= others: # fully subsumed by other reviewers
|
|
226
|
+
claims.append({
|
|
227
|
+
"verdict": "LENS_COLLAPSE",
|
|
228
|
+
"severity": "Flag",
|
|
229
|
+
"detail": (f"reviewer {rid} raised only families also covered by other "
|
|
230
|
+
f"reviewers ({', '.join(sorted(own))}); this lens added no "
|
|
231
|
+
f"independent axis — confirm it is a genuine consensus, not a "
|
|
232
|
+
f"redundant reviewer assignment"),
|
|
233
|
+
"where": f"reviewer {rid}",
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
# 4) THIN_PANEL
|
|
237
|
+
if len(reviewers) < 2:
|
|
238
|
+
claims.append({
|
|
239
|
+
"verdict": "THIN_PANEL",
|
|
240
|
+
"severity": "Flag",
|
|
241
|
+
"detail": (f"only {len(reviewers)} reviewer(s); a panel needs ≥2 independent "
|
|
242
|
+
f"lenses for diversity to be meaningful"),
|
|
243
|
+
"where": "panel composition",
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
summary = {
|
|
247
|
+
"n_reviewers": len(reviewers),
|
|
248
|
+
"n_majors": n_majors,
|
|
249
|
+
"family_histogram": family_hist,
|
|
250
|
+
"concentration_hhi": round(hhi, 3),
|
|
251
|
+
"top_family": top_family,
|
|
252
|
+
"top_family_share": round(top_share, 3),
|
|
253
|
+
"expected_axes": expected,
|
|
254
|
+
"covered_axes": sorted(covered),
|
|
255
|
+
"uncovered_axes": uncovered,
|
|
256
|
+
"research_type_known": bool(research_type and research_type in EXPECTED_AXES),
|
|
257
|
+
}
|
|
258
|
+
return claims, summary
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def analyze(panel: str, research_type_arg: str | None) -> dict:
|
|
262
|
+
p = Path(panel)
|
|
263
|
+
if not p.is_file():
|
|
264
|
+
sys.stderr.write(f"ERROR: panel JSON not found: {panel}\n")
|
|
265
|
+
sys.exit(2)
|
|
266
|
+
try:
|
|
267
|
+
obj = json.loads(p.read_text(encoding="utf-8"))
|
|
268
|
+
reviewers, rt_in_json = load_reviewers(obj)
|
|
269
|
+
except (ValueError, json.JSONDecodeError) as exc:
|
|
270
|
+
sys.stderr.write(f"ERROR: {exc}\n")
|
|
271
|
+
sys.exit(2)
|
|
272
|
+
|
|
273
|
+
research_type = normalize_research_type(research_type_arg) or normalize_research_type(rt_in_json)
|
|
274
|
+
claims, summary = check(reviewers, research_type)
|
|
275
|
+
n_major = sum(1 for c in claims if c["severity"] == "Major")
|
|
276
|
+
summary["n_claims"] = len(claims)
|
|
277
|
+
summary["n_major"] = n_major
|
|
278
|
+
summary["n_flag"] = len(claims) - n_major
|
|
279
|
+
summary["verdict"] = "MAJOR_CANDIDATE" if n_major else "OK"
|
|
280
|
+
return {
|
|
281
|
+
"panel": str(p),
|
|
282
|
+
"research_type": research_type,
|
|
283
|
+
"claims": claims,
|
|
284
|
+
"summary": summary,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def render(result: dict) -> str:
|
|
289
|
+
lines = ["| Check | Severity | Detail |", "|---|---|---|"]
|
|
290
|
+
for c in result["claims"]:
|
|
291
|
+
lines.append(f"| {c['verdict']} | {c['severity']} | {c['detail']} |")
|
|
292
|
+
if len(lines) == 2:
|
|
293
|
+
lines.append("| (none) | — | panel lenses span distinct axes; no monoculture |")
|
|
294
|
+
return "\n".join(lines)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def main() -> int:
|
|
298
|
+
ap = argparse.ArgumentParser(description="Panel lens-diversity gate (Phase 2.6, --panel).")
|
|
299
|
+
ap.add_argument("--panel", required=True, help="reviewers JSON (list or {reviewers:[...]})")
|
|
300
|
+
ap.add_argument("--research-type", help="survival|sr_ma|radiomics|dta|observational|narrative")
|
|
301
|
+
ap.add_argument("--out", help="write JSON artifact to this path")
|
|
302
|
+
ap.add_argument("--strict", action="store_true", help="exit 1 if any Major claim exists")
|
|
303
|
+
ap.add_argument("--quiet", action="store_true", help="suppress stdout table")
|
|
304
|
+
args = ap.parse_args()
|
|
305
|
+
|
|
306
|
+
result = analyze(args.panel, args.research_type)
|
|
307
|
+
|
|
308
|
+
if not args.quiet:
|
|
309
|
+
print("=" * 41)
|
|
310
|
+
print(" Panel Lens-Diversity (Phase 2.6)")
|
|
311
|
+
print("=" * 41)
|
|
312
|
+
print(render(result))
|
|
313
|
+
print()
|
|
314
|
+
s = result["summary"]
|
|
315
|
+
if not s["research_type_known"]:
|
|
316
|
+
print("NOTE: research type unknown — UNCOVERED_AXIS skipped "
|
|
317
|
+
"(pass --research-type to enable axis-coverage checks).")
|
|
318
|
+
if s["n_major"]:
|
|
319
|
+
print(f"MAJOR candidate: {s['n_major']} diversity failure(s); "
|
|
320
|
+
f"covered axes: {', '.join(s['covered_axes']) or '(none)'}.")
|
|
321
|
+
else:
|
|
322
|
+
print(f"OK: {s['n_reviewers']} reviewers spanning "
|
|
323
|
+
f"{len(s['covered_axes'])} distinct axes "
|
|
324
|
+
f"(HHI={s['concentration_hhi']}).")
|
|
325
|
+
|
|
326
|
+
if args.out:
|
|
327
|
+
Path(args.out).parent.mkdir(parents=True, exist_ok=True)
|
|
328
|
+
Path(args.out).write_text(json.dumps(result, indent=2), encoding="utf-8")
|
|
329
|
+
if not args.quiet:
|
|
330
|
+
print(f"\nwrote {args.out}")
|
|
331
|
+
|
|
332
|
+
return 1 if (args.strict and result["summary"]["n_major"]) else 0
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
if __name__ == "__main__":
|
|
336
|
+
sys.exit(main())
|