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.
Files changed (702) hide show
  1. package/LICENSE +50 -0
  2. package/README.md +602 -0
  3. package/README_FIRST.md +27 -0
  4. package/bin/medsci-skills.js +159 -0
  5. package/installers/install-macos.command +19 -0
  6. package/installers/install-windows.cmd +26 -0
  7. package/installers/install-windows.ps1 +17 -0
  8. package/installers/install.py +218 -0
  9. package/metadata/skills_catalog.json +452 -0
  10. package/package.json +48 -0
  11. package/skills/academic-aio/SKILL.md +408 -0
  12. package/skills/academic-aio/references/case_studies/kjr_mllm_2025.md +82 -0
  13. package/skills/academic-aio/references/checklists/AIO_GENERAL.md +354 -0
  14. package/skills/academic-aio/references/journal_summarybox_templates.yaml +126 -0
  15. package/skills/academic-aio/references/oac_funding_checklist.yaml +129 -0
  16. package/skills/academic-aio/references/reporting_guideline_mapping.md +39 -0
  17. package/skills/academic-aio/references/schema_markup_templates/CodeRepository.jsonld +32 -0
  18. package/skills/academic-aio/references/schema_markup_templates/Dataset.jsonld +36 -0
  19. package/skills/academic-aio/references/schema_markup_templates/Person.jsonld +30 -0
  20. package/skills/academic-aio/references/schema_markup_templates/README.md +43 -0
  21. package/skills/academic-aio/references/schema_markup_templates/ScholarlyArticle.jsonld +55 -0
  22. package/skills/academic-aio/scripts/batch_metadata_audit.py +169 -0
  23. package/skills/academic-aio/scripts/validate_schema.py +118 -0
  24. package/skills/academic-aio/skill.yml +36 -0
  25. package/skills/academic-aio/templates/aio_audit_checklist.md.j2 +108 -0
  26. package/skills/add-journal/SKILL.md +482 -0
  27. package/skills/add-journal/skill.yml +33 -0
  28. package/skills/analyze-stats/SKILL.md +598 -0
  29. package/skills/analyze-stats/references/analysis_guides/missing_data.md +109 -0
  30. package/skills/analyze-stats/references/analysis_guides/nhis_icd10_mapping.md +247 -0
  31. package/skills/analyze-stats/references/analysis_guides/propensity_score.md +132 -0
  32. package/skills/analyze-stats/references/analysis_guides/regression.md +115 -0
  33. package/skills/analyze-stats/references/analysis_guides/repeated_measures.md +160 -0
  34. package/skills/analyze-stats/references/analysis_guides/survey_weighted.md +366 -0
  35. package/skills/analyze-stats/references/analysis_guides/test_selection.md +86 -0
  36. package/skills/analyze-stats/references/style/figure_style.mplstyle +69 -0
  37. package/skills/analyze-stats/references/style/theme_publication.R +147 -0
  38. package/skills/analyze-stats/references/table-standards/journal-profiles/ajr.yaml +51 -0
  39. package/skills/analyze-stats/references/table-standards/journal-profiles/european_radiology.yaml +55 -0
  40. package/skills/analyze-stats/references/table-standards/journal-profiles/jama.yaml +66 -0
  41. package/skills/analyze-stats/references/table-standards/journal-profiles/lancet.yaml +57 -0
  42. package/skills/analyze-stats/references/table-standards/journal-profiles/nejm.yaml +51 -0
  43. package/skills/analyze-stats/references/table-standards/journal-profiles/radiology.yaml +66 -0
  44. package/skills/analyze-stats/references/table-standards/table-standards.md +287 -0
  45. package/skills/analyze-stats/references/table-standards/table-types/diagnostic_accuracy.md +36 -0
  46. package/skills/analyze-stats/references/table-standards/table-types/meta_analysis.md +58 -0
  47. package/skills/analyze-stats/references/table-standards/table-types/model_comparison.md +36 -0
  48. package/skills/analyze-stats/references/table-standards/table-types/regression_results.md +50 -0
  49. package/skills/analyze-stats/references/table-standards/table-types/table1_demographics.md +51 -0
  50. package/skills/analyze-stats/references/table-standards/tool-comparison.md +79 -0
  51. package/skills/analyze-stats/references/templates/agreement_analysis.py +436 -0
  52. package/skills/analyze-stats/references/templates/dca_plot.R +237 -0
  53. package/skills/analyze-stats/references/templates/diagnostic_accuracy.py +401 -0
  54. package/skills/analyze-stats/references/templates/dta_meta_analysis.R +384 -0
  55. package/skills/analyze-stats/references/templates/forest_plot.py +412 -0
  56. package/skills/analyze-stats/references/templates/likert_summary.py +356 -0
  57. package/skills/analyze-stats/references/templates/meta_analysis.R +365 -0
  58. package/skills/analyze-stats/references/templates/propensity_score.py +478 -0
  59. package/skills/analyze-stats/references/templates/regression.py +425 -0
  60. package/skills/analyze-stats/references/templates/repeated_measures.py +434 -0
  61. package/skills/analyze-stats/references/templates/sample_size.R +382 -0
  62. package/skills/analyze-stats/references/templates/survey_weighted_analysis.py +411 -0
  63. package/skills/analyze-stats/references/templates/survival_analysis.py +325 -0
  64. package/skills/analyze-stats/references/templates/table1_demographics.py +287 -0
  65. package/skills/analyze-stats/scripts/check_generated_code.py +335 -0
  66. package/skills/analyze-stats/skill.yml +38 -0
  67. package/skills/analyze-stats/tests/fixtures/gen_bad.R +16 -0
  68. package/skills/analyze-stats/tests/fixtures/gen_bad.py +24 -0
  69. package/skills/analyze-stats/tests/fixtures/gen_clean.py +21 -0
  70. package/skills/analyze-stats/tests/test_generated_code.sh +59 -0
  71. package/skills/analyze-stats/tests/test_survival_template.sh +53 -0
  72. package/skills/author-strategy/SKILL.md +117 -0
  73. package/skills/author-strategy/analyze_patterns.py +303 -0
  74. package/skills/author-strategy/fetch_pubmed.py +374 -0
  75. package/skills/author-strategy/skill.yml +34 -0
  76. package/skills/batch-cohort/SKILL.md +223 -0
  77. package/skills/batch-cohort/references/base_template_knhanes.R +210 -0
  78. package/skills/batch-cohort/references/batch_template_generator.R +222 -0
  79. package/skills/batch-cohort/references/variable_coding_registry.md +136 -0
  80. package/skills/batch-cohort/skill.yml +35 -0
  81. package/skills/calc-sample-size/SKILL.md +491 -0
  82. package/skills/calc-sample-size/references/formulas.md +655 -0
  83. package/skills/calc-sample-size/references/observational_cohort.md +49 -0
  84. package/skills/calc-sample-size/skill.yml +51 -0
  85. package/skills/check-reporting/SKILL.md +534 -0
  86. package/skills/check-reporting/references/LICENSES.md +41 -0
  87. package/skills/check-reporting/references/checklists/AMSTAR2.md +54 -0
  88. package/skills/check-reporting/references/checklists/ARRIVE_2.md +234 -0
  89. package/skills/check-reporting/references/checklists/CARE.md +102 -0
  90. package/skills/check-reporting/references/checklists/CLAIM_2024.md +128 -0
  91. package/skills/check-reporting/references/checklists/CLEAR.md +113 -0
  92. package/skills/check-reporting/references/checklists/CONSORT.md +86 -0
  93. package/skills/check-reporting/references/checklists/COSMIN_RoB.md +136 -0
  94. package/skills/check-reporting/references/checklists/GRRAS.md +61 -0
  95. package/skills/check-reporting/references/checklists/MI_CLEAR_LLM.md +167 -0
  96. package/skills/check-reporting/references/checklists/MOOSE.md +85 -0
  97. package/skills/check-reporting/references/checklists/NOS.md +88 -0
  98. package/skills/check-reporting/references/checklists/PRISMA_2020.md +135 -0
  99. package/skills/check-reporting/references/checklists/PRISMA_DTA.md +36 -0
  100. package/skills/check-reporting/references/checklists/PRISMA_P.md +56 -0
  101. package/skills/check-reporting/references/checklists/PROBAST.md +75 -0
  102. package/skills/check-reporting/references/checklists/PROBAST_AI.md +130 -0
  103. package/skills/check-reporting/references/checklists/QUADAS2.md +77 -0
  104. package/skills/check-reporting/references/checklists/QUADAS_C.md +131 -0
  105. package/skills/check-reporting/references/checklists/ROBINS_E.md +179 -0
  106. package/skills/check-reporting/references/checklists/ROBINS_I.md +87 -0
  107. package/skills/check-reporting/references/checklists/ROBIS.md +114 -0
  108. package/skills/check-reporting/references/checklists/ROB_ME.md +126 -0
  109. package/skills/check-reporting/references/checklists/RoB2.md +79 -0
  110. package/skills/check-reporting/references/checklists/RoB_NMA.md +96 -0
  111. package/skills/check-reporting/references/checklists/SPIRIT.md +112 -0
  112. package/skills/check-reporting/references/checklists/SQUIRE_2.md +68 -0
  113. package/skills/check-reporting/references/checklists/STARD.md +129 -0
  114. package/skills/check-reporting/references/checklists/STARD_AI.md +211 -0
  115. package/skills/check-reporting/references/checklists/STROBE.md +80 -0
  116. package/skills/check-reporting/references/checklists/SWiM.md +33 -0
  117. package/skills/check-reporting/references/checklists/TRIPOD.md +157 -0
  118. package/skills/check-reporting/references/checklists/TRIPOD_AI.md +140 -0
  119. package/skills/check-reporting/references/step4c_registration_timing.md +93 -0
  120. package/skills/check-reporting/references/step4d_prisma_figure_audit.md +137 -0
  121. package/skills/check-reporting/scripts/check_checklist_exists.py +183 -0
  122. package/skills/check-reporting/scripts/check_checklist_version.py +168 -0
  123. package/skills/check-reporting/scripts/check_framework_naming.py +206 -0
  124. package/skills/check-reporting/scripts/check_prisma_figure.py +209 -0
  125. package/skills/check-reporting/scripts/prisma_cascade_check.py +274 -0
  126. package/skills/check-reporting/skill.yml +41 -0
  127. package/skills/check-reporting/tests/fixtures/framework_bad.md +8 -0
  128. package/skills/check-reporting/tests/fixtures/framework_clean.md +7 -0
  129. package/skills/check-reporting/tests/test_checklist_fail_fast.sh +77 -0
  130. package/skills/check-reporting/tests/test_checklist_version.sh +72 -0
  131. package/skills/check-reporting/tests/test_framework_naming.sh +45 -0
  132. package/skills/check-reporting/tests/test_prisma_cascade.sh +104 -0
  133. package/skills/clean-data/SKILL.md +180 -0
  134. package/skills/clean-data/references/cleaning_patterns.md +299 -0
  135. package/skills/clean-data/references/profiling_template.py +304 -0
  136. package/skills/clean-data/scripts/check_structural_zero.py +174 -0
  137. package/skills/clean-data/skill.yml +35 -0
  138. package/skills/clean-data/tests/fixtures/smoking.csv +8 -0
  139. package/skills/clean-data/tests/test_structural_zero.sh +49 -0
  140. package/skills/cross-national/SKILL.md +264 -0
  141. package/skills/cross-national/skill.yml +37 -0
  142. package/skills/define-variables/SKILL.md +146 -0
  143. package/skills/define-variables/references/common_definitions.md +190 -0
  144. package/skills/define-variables/skill.yml +34 -0
  145. package/skills/define-variables/templates/variable_operationalization.md +64 -0
  146. package/skills/deidentify/SKILL.md +203 -0
  147. package/skills/deidentify/deidentify.py +1224 -0
  148. package/skills/deidentify/locales/_template.json +45 -0
  149. package/skills/deidentify/locales/au.json +43 -0
  150. package/skills/deidentify/locales/ca.json +44 -0
  151. package/skills/deidentify/locales/cn.json +47 -0
  152. package/skills/deidentify/locales/de.json +48 -0
  153. package/skills/deidentify/locales/fr.json +48 -0
  154. package/skills/deidentify/locales/in.json +48 -0
  155. package/skills/deidentify/locales/jp.json +48 -0
  156. package/skills/deidentify/locales/kr.json +48 -0
  157. package/skills/deidentify/locales/uk.json +45 -0
  158. package/skills/deidentify/locales/us.json +43 -0
  159. package/skills/deidentify/references/date_shift_guide.md +82 -0
  160. package/skills/deidentify/references/hipaa_18_identifiers.md +48 -0
  161. package/skills/deidentify/references/korean_phi_patterns.md +135 -0
  162. package/skills/deidentify/skill.yml +43 -0
  163. package/skills/deidentify/tests/README.md +26 -0
  164. package/skills/deidentify/tests/test_clean.csv +16 -0
  165. package/skills/deidentify/tests/test_edge_cases.csv +11 -0
  166. package/skills/deidentify/tests/test_phi_korean.csv +11 -0
  167. package/skills/design-ai-benchmarking/SKILL.md +214 -0
  168. package/skills/design-ai-benchmarking/references/benchmark_export_schema.json +69 -0
  169. package/skills/design-ai-benchmarking/references/elicitation_rubric_template.md +37 -0
  170. package/skills/design-ai-benchmarking/skill.yml +38 -0
  171. package/skills/design-study/SKILL.md +298 -0
  172. package/skills/design-study/skill.yml +33 -0
  173. package/skills/fill-icmje-coi/SKILL.md +216 -0
  174. package/skills/fill-icmje-coi/scripts/fill_icmje_coi.py +140 -0
  175. package/skills/fill-icmje-coi/skill.yml +35 -0
  176. package/skills/fill-icmje-coi/templates/icmje_coi_seed_synthetic.docx +0 -0
  177. package/skills/fill-protocol/SKILL.md +248 -0
  178. package/skills/fill-protocol/examples/example_irb_template.yaml +53 -0
  179. package/skills/fill-protocol/references/best_practices.md +121 -0
  180. package/skills/fill-protocol/scripts/doc_to_docx.py +111 -0
  181. package/skills/fill-protocol/scripts/fill_form.py +611 -0
  182. package/skills/fill-protocol/scripts/inspect_template.py +61 -0
  183. package/skills/fill-protocol/setup.sh +162 -0
  184. package/skills/fill-protocol/skill.yml +37 -0
  185. package/skills/find-cohort-gap/SKILL.md +309 -0
  186. package/skills/find-cohort-gap/references/cohort_profile_template.md +93 -0
  187. package/skills/find-cohort-gap/references/onepager_template.md +84 -0
  188. package/skills/find-cohort-gap/references/pattern_scoring_rubric.md +169 -0
  189. package/skills/find-cohort-gap/references/saturation_query_templates.md +143 -0
  190. package/skills/find-cohort-gap/skill.yml +35 -0
  191. package/skills/find-journal/POLICY.md +87 -0
  192. package/skills/find-journal/SKILL.md +340 -0
  193. package/skills/find-journal/references/journal_profiles/AJNR.md +29 -0
  194. package/skills/find-journal/references/journal_profiles/AJR.md +30 -0
  195. package/skills/find-journal/references/journal_profiles/Abdominal_Radiology.md +30 -0
  196. package/skills/find-journal/references/journal_profiles/Academic_Radiology.md +30 -0
  197. package/skills/find-journal/references/journal_profiles/Annals_of_Internal_Medicine.md +33 -0
  198. package/skills/find-journal/references/journal_profiles/Artificial_Intelligence_in_Medicine.md +28 -0
  199. package/skills/find-journal/references/journal_profiles/BMC_Medicine.md +31 -0
  200. package/skills/find-journal/references/journal_profiles/British_Journal_of_Radiology.md +39 -0
  201. package/skills/find-journal/references/journal_profiles/CVIR.md +30 -0
  202. package/skills/find-journal/references/journal_profiles/Chest.md +39 -0
  203. package/skills/find-journal/references/journal_profiles/Clinical_Radiology.md +30 -0
  204. package/skills/find-journal/references/journal_profiles/Clinical_and_Molecular_Hepatology.md +32 -0
  205. package/skills/find-journal/references/journal_profiles/Diabetes_Metabolism_Journal.md +36 -0
  206. package/skills/find-journal/references/journal_profiles/Diagnostic_and_Interventional_Radiology.md +32 -0
  207. package/skills/find-journal/references/journal_profiles/Endocrinology_and_Metabolism.md +37 -0
  208. package/skills/find-journal/references/journal_profiles/European_Journal_of_Preventive_Cardiology.md +39 -0
  209. package/skills/find-journal/references/journal_profiles/European_Radiology.md +29 -0
  210. package/skills/find-journal/references/journal_profiles/Hepatology_Communications.md +40 -0
  211. package/skills/find-journal/references/journal_profiles/Hepatology_International.md +37 -0
  212. package/skills/find-journal/references/journal_profiles/IEEE_JBHI.md +28 -0
  213. package/skills/find-journal/references/journal_profiles/IEEE_TMI.md +28 -0
  214. package/skills/find-journal/references/journal_profiles/INSI.md +29 -0
  215. package/skills/find-journal/references/journal_profiles/Investigative_Radiology.md +25 -0
  216. package/skills/find-journal/references/journal_profiles/JACC_Advances.md +41 -0
  217. package/skills/find-journal/references/journal_profiles/JACC_Asia.md +30 -0
  218. package/skills/find-journal/references/journal_profiles/JACR.md +28 -0
  219. package/skills/find-journal/references/journal_profiles/JAMA.md +40 -0
  220. package/skills/find-journal/references/journal_profiles/JAMA_Network_Open.md +30 -0
  221. package/skills/find-journal/references/journal_profiles/JCSM.md +39 -0
  222. package/skills/find-journal/references/journal_profiles/JKMS.md +32 -0
  223. package/skills/find-journal/references/journal_profiles/JMIR.md +29 -0
  224. package/skills/find-journal/references/journal_profiles/JMIR_Medical_Education.md +29 -0
  225. package/skills/find-journal/references/journal_profiles/JNIS.md +35 -0
  226. package/skills/find-journal/references/journal_profiles/JVIR.md +31 -0
  227. package/skills/find-journal/references/journal_profiles/Journal_of_Biomedical_Informatics.md +29 -0
  228. package/skills/find-journal/references/journal_profiles/Journal_of_Clinical_Endocrinology_and_Metabolism.md +40 -0
  229. package/skills/find-journal/references/journal_profiles/Journal_of_Magnetic_Resonance_Imaging.md +30 -0
  230. package/skills/find-journal/references/journal_profiles/Journal_of_Nuclear_Medicine.md +31 -0
  231. package/skills/find-journal/references/journal_profiles/Journal_of_Stroke.md +32 -0
  232. package/skills/find-journal/references/journal_profiles/KJR.md +38 -0
  233. package/skills/find-journal/references/journal_profiles/Korean_Circulation_Journal.md +38 -0
  234. package/skills/find-journal/references/journal_profiles/Korean_Journal_of_Internal_Medicine.md +36 -0
  235. package/skills/find-journal/references/journal_profiles/Lancet_Diabetes_and_Endocrinology.md +40 -0
  236. package/skills/find-journal/references/journal_profiles/Lancet_Gastroenterology_and_Hepatology.md +49 -0
  237. package/skills/find-journal/references/journal_profiles/Lancet_Infectious_Diseases.md +38 -0
  238. package/skills/find-journal/references/journal_profiles/Lancet_Neurology.md +39 -0
  239. package/skills/find-journal/references/journal_profiles/Lancet_Oncology.md +40 -0
  240. package/skills/find-journal/references/journal_profiles/Lancet_Psychiatry.md +38 -0
  241. package/skills/find-journal/references/journal_profiles/Lancet_Public_Health.md +30 -0
  242. package/skills/find-journal/references/journal_profiles/Lancet_Respiratory_Medicine.md +39 -0
  243. package/skills/find-journal/references/journal_profiles/Liver_International.md +33 -0
  244. package/skills/find-journal/references/journal_profiles/Medical_Image_Analysis.md +28 -0
  245. package/skills/find-journal/references/journal_profiles/NEJM.md +33 -0
  246. package/skills/find-journal/references/journal_profiles/Nature_Machine_Intelligence.md +31 -0
  247. package/skills/find-journal/references/journal_profiles/Nature_Medicine.md +39 -0
  248. package/skills/find-journal/references/journal_profiles/Neuroradiology.md +31 -0
  249. package/skills/find-journal/references/journal_profiles/Nutrition_Metabolism_and_Cardiovascular_Diseases.md +39 -0
  250. package/skills/find-journal/references/journal_profiles/PLOS_Medicine.md +32 -0
  251. package/skills/find-journal/references/journal_profiles/RYAI.md +28 -0
  252. package/skills/find-journal/references/journal_profiles/Radiology.md +29 -0
  253. package/skills/find-journal/references/journal_profiles/Skeletal_Radiology.md +31 -0
  254. package/skills/find-journal/references/journal_profiles/Stroke.md +37 -0
  255. package/skills/find-journal/references/journal_profiles/The_BMJ.md +31 -0
  256. package/skills/find-journal/references/journal_profiles/The_Lancet.md +31 -0
  257. package/skills/find-journal/references/journal_profiles/The_Lancet_Digital_Health.md +29 -0
  258. package/skills/find-journal/references/journal_profiles/World_Journal_of_Hepatology.md +53 -0
  259. package/skills/find-journal/references/journal_profiles/npj_Digital_Medicine.md +29 -0
  260. package/skills/find-journal/skill.yml +34 -0
  261. package/skills/fulltext-retrieval/SKILL.md +174 -0
  262. package/skills/fulltext-retrieval/fetch_oa.py +433 -0
  263. package/skills/fulltext-retrieval/pdf_to_md.py +160 -0
  264. package/skills/fulltext-retrieval/skill.yml +41 -0
  265. package/skills/generate-codebook/SKILL.md +155 -0
  266. package/skills/generate-codebook/references/codebook_schema.md +76 -0
  267. package/skills/generate-codebook/scripts/generate_codebook.py +278 -0
  268. package/skills/generate-codebook/skill.yml +35 -0
  269. package/skills/generate-codebook/tests/test_generate_codebook.sh +76 -0
  270. package/skills/grant-builder/SKILL.md +251 -0
  271. package/skills/grant-builder/skill.yml +34 -0
  272. package/skills/humanize/SKILL.md +251 -0
  273. package/skills/humanize/references/ai_patterns.md +571 -0
  274. package/skills/humanize/skill.yml +33 -0
  275. package/skills/intake-project/SKILL.md +264 -0
  276. package/skills/intake-project/skill.yml +34 -0
  277. package/skills/lit-sync/SKILL.md +448 -0
  278. package/skills/lit-sync/references/locale/ko/note_templates.md +110 -0
  279. package/skills/lit-sync/skill.yml +52 -0
  280. package/skills/lit-sync/tests/test_poll_logic.sh +92 -0
  281. package/skills/ma-scout/SKILL.md +640 -0
  282. package/skills/ma-scout/references/project_readme_template.md +95 -0
  283. package/skills/ma-scout/references/project_readme_template_ko.md +82 -0
  284. package/skills/ma-scout/skill.yml +33 -0
  285. package/skills/make-figures/SKILL.md +957 -0
  286. package/skills/make-figures/references/critic_rubrics/data_plot.md +166 -0
  287. package/skills/make-figures/references/critic_rubrics/flow_diagram.md +169 -0
  288. package/skills/make-figures/references/design_principles.md +181 -0
  289. package/skills/make-figures/references/exemplar_diagrams/README.md +65 -0
  290. package/skills/make-figures/references/exemplar_diagrams/consort/README.md +15 -0
  291. package/skills/make-figures/references/exemplar_diagrams/consort/template_input.yaml +37 -0
  292. package/skills/make-figures/references/exemplar_diagrams/consort/template_output.pdf +0 -0
  293. package/skills/make-figures/references/exemplar_diagrams/consort/template_output.png +0 -0
  294. package/skills/make-figures/references/exemplar_diagrams/consort/template_output_600.png +0 -0
  295. package/skills/make-figures/references/exemplar_diagrams/other/other_02.meta.yaml +4 -0
  296. package/skills/make-figures/references/exemplar_diagrams/other/other_02.png +0 -0
  297. package/skills/make-figures/references/exemplar_diagrams/other/other_02_why.md +13 -0
  298. package/skills/make-figures/references/exemplar_diagrams/pipeline/README.md +15 -0
  299. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01.meta.yaml +4 -0
  300. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01.png +0 -0
  301. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01_why.md +13 -0
  302. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03.meta.yaml +4 -0
  303. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03.png +0 -0
  304. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03_why.md +13 -0
  305. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04.meta.yaml +4 -0
  306. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04.png +0 -0
  307. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04_why.md +13 -0
  308. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05.meta.yaml +4 -0
  309. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05.png +0 -0
  310. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05_why.md +13 -0
  311. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06.meta.yaml +4 -0
  312. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06.png +0 -0
  313. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06_why.md +13 -0
  314. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07.meta.yaml +4 -0
  315. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07.png +0 -0
  316. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07_why.md +13 -0
  317. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08.meta.yaml +4 -0
  318. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08.png +0 -0
  319. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08_why.md +13 -0
  320. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09.meta.yaml +4 -0
  321. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09.png +0 -0
  322. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09_why.md +13 -0
  323. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10.meta.yaml +4 -0
  324. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10.png +0 -0
  325. package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10_why.md +13 -0
  326. package/skills/make-figures/references/exemplar_diagrams/prisma/README.md +15 -0
  327. package/skills/make-figures/references/exemplar_diagrams/prisma/template_input.yaml +47 -0
  328. package/skills/make-figures/references/exemplar_diagrams/prisma/template_output.pdf +0 -0
  329. package/skills/make-figures/references/exemplar_diagrams/prisma/template_output.png +0 -0
  330. package/skills/make-figures/references/exemplar_diagrams/prisma/template_output_600.png +0 -0
  331. package/skills/make-figures/references/exemplar_diagrams/stard/README.md +15 -0
  332. package/skills/make-figures/references/exemplar_diagrams/stard/template_input.yaml +40 -0
  333. package/skills/make-figures/references/exemplar_diagrams/stard/template_output.pdf +0 -0
  334. package/skills/make-figures/references/exemplar_diagrams/stard/template_output.png +0 -0
  335. package/skills/make-figures/references/exemplar_diagrams/stard/template_output_600.png +0 -0
  336. package/skills/make-figures/references/exemplar_diagrams/strobe/template_input.yaml +43 -0
  337. package/skills/make-figures/references/exemplar_diagrams/strobe/template_input_pptx.yaml +43 -0
  338. package/skills/make-figures/references/exemplar_diagrams/strobe/template_output.pdf +0 -0
  339. package/skills/make-figures/references/exemplar_diagrams/strobe/template_output.png +0 -0
  340. package/skills/make-figures/references/exemplar_diagrams/strobe/template_output.pptx +0 -0
  341. package/skills/make-figures/references/exemplar_diagrams/strobe/template_output_600.png +0 -0
  342. package/skills/make-figures/references/figure_specs.md +291 -0
  343. package/skills/make-figures/references/flow_diagram_lessons.md +164 -0
  344. package/skills/make-figures/references/jacc_central_illustration_principles.md +91 -0
  345. package/skills/make-figures/references/medical_illustration_sources.md +98 -0
  346. package/skills/make-figures/references/pipeline_concepts_medical_ai.md +240 -0
  347. package/skills/make-figures/references/reporting_guideline_figure_map.md +104 -0
  348. package/skills/make-figures/references/visual_abstract_templates/european_radiology.pptx +0 -0
  349. package/skills/make-figures/references/visual_abstract_templates/jacc_central_illustration.pptx +0 -0
  350. package/skills/make-figures/references/visual_abstract_templates/medsci_default.pptx +0 -0
  351. package/skills/make-figures/references/visual_abstract_templates/template_guide.md +114 -0
  352. package/skills/make-figures/scripts/build_jacc_template.py +77 -0
  353. package/skills/make-figures/scripts/build_prisma2020_template.py +371 -0
  354. package/skills/make-figures/scripts/build_strobe_template.py +351 -0
  355. package/skills/make-figures/scripts/critic_figure.py +264 -0
  356. package/skills/make-figures/scripts/derive_figure_legend_counts.py +138 -0
  357. package/skills/make-figures/scripts/extract_exemplar_from_pdf.py +186 -0
  358. package/skills/make-figures/scripts/fetch_official_templates.sh +88 -0
  359. package/skills/make-figures/scripts/fill_prisma_template.py +142 -0
  360. package/skills/make-figures/scripts/generate_flow_diagram.R +133 -0
  361. package/skills/make-figures/scripts/generate_image.py +99 -0
  362. package/skills/make-figures/scripts/generate_visual_abstract.py +438 -0
  363. package/skills/make-figures/scripts/validate_pptx_mac_compat.py +233 -0
  364. package/skills/make-figures/skill.yml +52 -0
  365. package/skills/make-figures/templates/official/NOTES.md +62 -0
  366. package/skills/make-figures/templates/official/consort2010/CONSORT_2025_editable_checklist.docx +0 -0
  367. package/skills/make-figures/templates/official/consort2010/CONSORT_2025_flow_diagram.docx +0 -0
  368. package/skills/make-figures/templates/official/prisma2020/PRISMA_2020_flow_new_v1.pptx +0 -0
  369. package/skills/make-figures/templates/official/prisma2020/PRISMA_2020_flow_new_v2.pptx +0 -0
  370. package/skills/make-figures/templates/official/prisma2020/PRISMA_2020_flow_updated_v2.pptx +0 -0
  371. package/skills/make-figures/templates/official/spirit2013/SPIRIT_2025_editable_checklist.docx +0 -0
  372. package/skills/make-figures/templates/official/spirit2013/SPIRIT_2025_participant_timeline.docx +0 -0
  373. package/skills/make-figures/templates/official/stard2015/STARD_2015_checklist.docx +0 -0
  374. package/skills/make-figures/templates/official/stard2015/STARD_2015_flow_diagram.pdf +0 -0
  375. package/skills/make-figures/tests/fixtures/figure1_flow.yaml +8 -0
  376. package/skills/make-figures/tests/fixtures/manuscript_ok.md +9 -0
  377. package/skills/make-figures/tests/fixtures/manuscript_stale.md +4 -0
  378. package/skills/make-figures/tests/test_legend_reconcile.sh +36 -0
  379. package/skills/manage-project/SKILL.md +358 -0
  380. package/skills/manage-project/references/pre_submission_checklist.md +53 -0
  381. package/skills/manage-project/references/project_state_template.json +37 -0
  382. package/skills/manage-project/references/scaffold_templates.md +118 -0
  383. package/skills/manage-project/references/status_output_format.md +44 -0
  384. package/skills/manage-project/references/timeline_example.md +20 -0
  385. package/skills/manage-project/skill.yml +36 -0
  386. package/skills/manage-project/templates/SSOT.yaml.template +41 -0
  387. package/skills/manage-refs/LICENSE.zotero-mcp +21 -0
  388. package/skills/manage-refs/NOTICE.md +29 -0
  389. package/skills/manage-refs/SKILL.md +289 -0
  390. package/skills/manage-refs/citation_styles/README.md +40 -0
  391. package/skills/manage-refs/citation_styles/american-journal-of-roentgenology.csl +211 -0
  392. package/skills/manage-refs/citation_styles/cardiovascular-and-interventional-radiology.csl +19 -0
  393. package/skills/manage-refs/citation_styles/european-radiology.csl +19 -0
  394. package/skills/manage-refs/citation_styles/journal-of-cachexia-sarcopenia-and-muscle.csl +150 -0
  395. package/skills/manage-refs/citation_styles/journal-of-korean-medical-science-strict.csl +533 -0
  396. package/skills/manage-refs/citation_styles/journal-of-korean-medical-science.csl +16 -0
  397. package/skills/manage-refs/citation_styles/korean-journal-of-radiology.csl +155 -0
  398. package/skills/manage-refs/citation_styles/nature.csl +189 -0
  399. package/skills/manage-refs/citation_styles/nlm-citation-sequence.csl +535 -0
  400. package/skills/manage-refs/citation_styles/radiology.csl +228 -0
  401. package/skills/manage-refs/citation_styles/springer-basic-brackets.csl +187 -0
  402. package/skills/manage-refs/citation_styles/springer-vancouver-brackets.csl +276 -0
  403. package/skills/manage-refs/citation_styles/vancouver-superscript.csl +536 -0
  404. package/skills/manage-refs/citation_styles/vancouver.csl +535 -0
  405. package/skills/manage-refs/references/REFERENCE_STYLE_SPECS.md +59 -0
  406. package/skills/manage-refs/references/check_xref_symptoms.md +35 -0
  407. package/skills/manage-refs/scripts/_vendor_citation_writer.py +600 -0
  408. package/skills/manage-refs/scripts/check_citation_keys.py +112 -0
  409. package/skills/manage-refs/scripts/check_csl_render.py +102 -0
  410. package/skills/manage-refs/scripts/check_xref.py +633 -0
  411. package/skills/manage-refs/scripts/fill_journal_abbrev.py +104 -0
  412. package/skills/manage-refs/scripts/inject_zotero_cwyw.py +133 -0
  413. package/skills/manage-refs/scripts/md_marker_convert.py +193 -0
  414. package/skills/manage-refs/scripts/pre_submission_gate.sh +238 -0
  415. package/skills/manage-refs/scripts/render_pandoc.sh +88 -0
  416. package/skills/manage-refs/skill.yml +70 -0
  417. package/skills/manage-refs/tests/fixtures/pre_submission_gate/README.md +32 -0
  418. package/skills/manage-refs/tests/fixtures/pre_submission_gate/manuscript.md +10 -0
  419. package/skills/manage-refs/tests/fixtures/pre_submission_gate/refs.bib +34 -0
  420. package/skills/manage-refs/tests/fixtures/pre_submission_gate/run.sh +117 -0
  421. package/skills/manage-refs/tests/test_vN_docx_check.sh +145 -0
  422. package/skills/meta-analysis/SKILL.md +739 -0
  423. package/skills/meta-analysis/references/LICENSES.md +21 -0
  424. package/skills/meta-analysis/references/PROSPERO_template.md +221 -0
  425. package/skills/meta-analysis/references/ai_pre_screening_template.py +245 -0
  426. package/skills/meta-analysis/references/checklists/JBI_Case_Series.md +45 -0
  427. package/skills/meta-analysis/references/checklists/NOS.md +88 -0
  428. package/skills/meta-analysis/references/checklists/PRISMA_DTA.md +36 -0
  429. package/skills/meta-analysis/references/checklists/PROBAST.md +75 -0
  430. package/skills/meta-analysis/references/checklists/QUADAS2.md +77 -0
  431. package/skills/meta-analysis/references/checklists/ROBINS_I.md +87 -0
  432. package/skills/meta-analysis/references/checklists/RoB2.md +79 -0
  433. package/skills/meta-analysis/references/data_integrity_checklist.md +57 -0
  434. package/skills/meta-analysis/references/icmje_coi_guide.md +181 -0
  435. package/skills/meta-analysis/references/phase10_recovery.md +136 -0
  436. package/skills/meta-analysis/references/phase4_km_composite.md +58 -0
  437. package/skills/meta-analysis/references/phase6_statistical_synthesis.md +148 -0
  438. package/skills/meta-analysis/references/phase9_circulation.md +84 -0
  439. package/skills/meta-analysis/references/post_submission_release_ops.md +41 -0
  440. package/skills/meta-analysis/references/r_templates.md +132 -0
  441. package/skills/meta-analysis/references/review_orchestration.md +40 -0
  442. package/skills/meta-analysis/references/submission_package_drift.md +71 -0
  443. package/skills/meta-analysis/scripts/check_pool_consistency.py +201 -0
  444. package/skills/meta-analysis/scripts/cohort_overlap_check.py +242 -0
  445. package/skills/meta-analysis/scripts/dta_extraction_qc.py +137 -0
  446. package/skills/meta-analysis/scripts/screening_reconcile.py +160 -0
  447. package/skills/meta-analysis/skill.yml +47 -0
  448. package/skills/meta-analysis/templates/FINAL_POOL_LOCK.yaml.template +70 -0
  449. package/skills/meta-analysis/templates/extraction_form_v2.md +129 -0
  450. package/skills/meta-analysis/templates/supplementary_8file_checklist.md +94 -0
  451. package/skills/meta-analysis/tests/test_pool_consistency.sh +123 -0
  452. package/skills/orchestrate/SKILL.md +501 -0
  453. package/skills/orchestrate/references/dialogue_nodes.md +196 -0
  454. package/skills/orchestrate/references/report_template.md +109 -0
  455. package/skills/orchestrate/references/report_template_ko.md +88 -0
  456. package/skills/orchestrate/skill.yml +44 -0
  457. package/skills/peer-review/SKILL.md +381 -0
  458. package/skills/peer-review/references/aczel_2021_reviewer2_patterns.md +88 -0
  459. package/skills/peer-review/references/domain-probes/ai_overclaiming.md +47 -0
  460. package/skills/peer-review/references/domain-probes/narrative_review.md +44 -0
  461. package/skills/peer-review/references/domain-probes/observational_confounding.md +48 -0
  462. package/skills/peer-review/references/domain-probes/radiomics.md +38 -0
  463. package/skills/peer-review/references/domain-probes/sr_ma.md +87 -0
  464. package/skills/peer-review/references/domain-probes/survival_prognostic.md +68 -0
  465. package/skills/peer-review/references/exemplar_reviews/README.md +43 -0
  466. package/skills/peer-review/references/exemplar_reviews/ai_overclaiming.md +47 -0
  467. package/skills/peer-review/references/exemplar_reviews/calibration_missing.md +44 -0
  468. package/skills/peer-review/references/exemplar_reviews/data_leakage.md +48 -0
  469. package/skills/peer-review/references/exemplar_reviews/reference_standard_validity.md +45 -0
  470. package/skills/peer-review/references/narrative_review_audit.md +67 -0
  471. package/skills/peer-review/references/reviewer_calibration/README.md +34 -0
  472. package/skills/peer-review/references/reviewer_calibration/compliance_floor.md +52 -0
  473. package/skills/peer-review/references/reviewer_profiles/AJR.md +82 -0
  474. package/skills/peer-review/references/reviewer_profiles/EURE.md +64 -0
  475. package/skills/peer-review/references/reviewer_profiles/INSI.md +57 -0
  476. package/skills/peer-review/references/reviewer_profiles/KJR.md +100 -0
  477. package/skills/peer-review/references/reviewer_profiles/README.md +32 -0
  478. package/skills/peer-review/references/reviewer_profiles/RYAI.md +86 -0
  479. package/skills/peer-review/skill.yml +39 -0
  480. package/skills/present-paper/SKILL.md +675 -0
  481. package/skills/present-paper/references/critic_rubrics/slide.md +155 -0
  482. package/skills/present-paper/references/generate_pptx_templates.py +604 -0
  483. package/skills/present-paper/references/medical_presentation_templates.md +277 -0
  484. package/skills/present-paper/references/slide_design_principles.md +202 -0
  485. package/skills/present-paper/references/slide_visual_styles/nature_lancet.md +168 -0
  486. package/skills/present-paper/references/workflow-checklist.md +109 -0
  487. package/skills/present-paper/scripts/extract_pdf_figures.py +243 -0
  488. package/skills/present-paper/scripts/inject_pronunciation_notes.py +178 -0
  489. package/skills/present-paper/scripts/inject_speaker_notes.py +133 -0
  490. package/skills/present-paper/scripts/strip_notes_for_sharing.py +140 -0
  491. package/skills/present-paper/scripts/trim_caption.py +271 -0
  492. package/skills/present-paper/skill.yml +41 -0
  493. package/skills/present-paper/templates/build_pptx_nature_lancet.py +688 -0
  494. package/skills/publish-skill/SKILL.md +370 -0
  495. package/skills/publish-skill/references/license-compatibility-matrix.md +132 -0
  496. package/skills/publish-skill/references/pii-patterns.md +130 -0
  497. package/skills/publish-skill/scripts/audit_skill.sh +278 -0
  498. package/skills/publish-skill/skill.yml +35 -0
  499. package/skills/render-pdf-doc/SKILL.md +146 -0
  500. package/skills/render-pdf-doc/references/known_pitfalls.md +53 -0
  501. package/skills/render-pdf-doc/references/pandoc_korean_cheatsheet.md +77 -0
  502. package/skills/render-pdf-doc/scripts/check_deps.sh +42 -0
  503. package/skills/render-pdf-doc/scripts/infer_colwidths.py +164 -0
  504. package/skills/render-pdf-doc/scripts/render_pdf.sh +98 -0
  505. package/skills/render-pdf-doc/skill.yml +57 -0
  506. package/skills/render-pdf-doc/templates/anchor-doc.md +27 -0
  507. package/skills/render-pdf-doc/templates/anchor-doc_ko.md +25 -0
  508. package/skills/render-pdf-doc/templates/briefing-handout.md +33 -0
  509. package/skills/render-pdf-doc/templates/briefing-handout_ko.md +31 -0
  510. package/skills/render-pdf-doc/templates/proposal-cover.md +33 -0
  511. package/skills/render-pdf-doc/templates/proposal-cover_ko.md +31 -0
  512. package/skills/render-pdf-doc/templates/reference-table.md +22 -0
  513. package/skills/render-pdf-doc/templates/reference-table_ko.md +20 -0
  514. package/skills/replicate-study/SKILL.md +150 -0
  515. package/skills/replicate-study/references/harmonization_3country.csv +47 -0
  516. package/skills/replicate-study/references/harmonization_knhanes_nhanes.csv +68 -0
  517. package/skills/replicate-study/references/methodology_extraction_template.md +134 -0
  518. package/skills/replicate-study/skill.yml +37 -0
  519. package/skills/review-paper/SKILL.md +104 -0
  520. package/skills/review-paper/references/macro_skeleton.md +6 -0
  521. package/skills/review-paper/skill.yml +25 -0
  522. package/skills/revise/SKILL.md +515 -0
  523. package/skills/revise/references/r2r_voice.md +346 -0
  524. package/skills/revise/skill.yml +43 -0
  525. package/skills/search-lit/SKILL.md +443 -0
  526. package/skills/search-lit/references/parse_pubmed.py +326 -0
  527. package/skills/search-lit/references/pubmed_eutils.sh +111 -0
  528. package/skills/search-lit/skill.yml +46 -0
  529. package/skills/self-review/SKILL.md +1045 -0
  530. package/skills/self-review/references/domain-probes/ai_overclaiming.md +47 -0
  531. package/skills/self-review/references/domain-probes/narrative_review.md +44 -0
  532. package/skills/self-review/references/domain-probes/observational_confounding.md +48 -0
  533. package/skills/self-review/references/domain-probes/radiomics.md +38 -0
  534. package/skills/self-review/references/domain-probes/sr_ma.md +87 -0
  535. package/skills/self-review/references/domain-probes/survival_prognostic.md +68 -0
  536. package/skills/self-review/references/exemplar_findings/README.md +43 -0
  537. package/skills/self-review/references/exemplar_findings/cohort_arithmetic_mismatch.md +35 -0
  538. package/skills/self-review/references/exemplar_findings/estimand_drift_posthoc_primary.md +39 -0
  539. package/skills/self-review/references/exemplar_findings/scope_overreach_cross_sectional.md +35 -0
  540. package/skills/self-review/references/exemplar_findings/unadjusted_confounder.md +36 -0
  541. package/skills/self-review/references/panel_review_template.md +177 -0
  542. package/skills/self-review/scripts/check_artifact_coverage.py +301 -0
  543. package/skills/self-review/scripts/check_claim_artifact.py +248 -0
  544. package/skills/self-review/scripts/check_classical_style.py +185 -0
  545. package/skills/self-review/scripts/check_cohort_arithmetic.py +481 -0
  546. package/skills/self-review/scripts/check_confounding_completeness.py +287 -0
  547. package/skills/self-review/scripts/check_panel_diversity.py +336 -0
  548. package/skills/self-review/scripts/check_reference_adequacy.py +392 -0
  549. package/skills/self-review/scripts/check_reviewer_team_consistency.py +412 -0
  550. package/skills/self-review/scripts/check_scope_coherence.py +177 -0
  551. package/skills/self-review/skill.yml +47 -0
  552. package/skills/self-review/tests/fixtures/claim_manuscript.md +17 -0
  553. package/skills/self-review/tests/fixtures/claim_prereg.md +6 -0
  554. package/skills/self-review/tests/fixtures/cohort_bad.md +21 -0
  555. package/skills/self-review/tests/fixtures/cohort_clean.md +21 -0
  556. package/skills/self-review/tests/fixtures/cohort_partition.csv +5 -0
  557. package/skills/self-review/tests/fixtures/coverage_analysis/31_delong_nested_added_value.csv +3 -0
  558. package/skills/self-review/tests/fixtures/coverage_analysis/table1_demographics.csv +3 -0
  559. package/skills/self-review/tests/fixtures/coverage_clean.md +13 -0
  560. package/skills/self-review/tests/fixtures/coverage_manuscript.md +11 -0
  561. package/skills/self-review/tests/fixtures/panel_collapse.json +27 -0
  562. package/skills/self-review/tests/fixtures/panel_good.json +32 -0
  563. package/skills/self-review/tests/fixtures/panel_monoculture.json +32 -0
  564. package/skills/self-review/tests/fixtures/refadeq_letter.md +13 -0
  565. package/skills/self-review/tests/fixtures/refadeq_original_fixed.md +42 -0
  566. package/skills/self-review/tests/fixtures/refadeq_original_uncited.md +40 -0
  567. package/skills/self-review/tests/fixtures/scope_bad.md +9 -0
  568. package/skills/self-review/tests/fixtures/scope_clean.md +8 -0
  569. package/skills/self-review/tests/fixtures/scope_surrogate.md +8 -0
  570. package/skills/self-review/tests/fixtures/style_bad.md +13 -0
  571. package/skills/self-review/tests/fixtures/style_clean.md +11 -0
  572. package/skills/self-review/tests/fixtures/table1_by_exposure.csv +11 -0
  573. package/skills/self-review/tests/test_artifact_coverage.sh +44 -0
  574. package/skills/self-review/tests/test_claim_artifact.sh +50 -0
  575. package/skills/self-review/tests/test_classical_style.sh +44 -0
  576. package/skills/self-review/tests/test_cohort_arithmetic.sh +49 -0
  577. package/skills/self-review/tests/test_confounding_completeness.sh +66 -0
  578. package/skills/self-review/tests/test_panel_diversity.sh +55 -0
  579. package/skills/self-review/tests/test_panel_mode.sh +69 -0
  580. package/skills/self-review/tests/test_reference_adequacy.sh +68 -0
  581. package/skills/self-review/tests/test_reviewer_team_consistency.sh +138 -0
  582. package/skills/self-review/tests/test_scope_coherence.sh +46 -0
  583. package/skills/setup-medsci/SKILL.md +110 -0
  584. package/skills/setup-medsci/references/setup-checklist.md +51 -0
  585. package/skills/setup-medsci/skill.yml +30 -0
  586. package/skills/sync-submission/SKILL.md +382 -0
  587. package/skills/sync-submission/scripts/author_registry_example.yaml +36 -0
  588. package/skills/sync-submission/scripts/blind_sweep.py +203 -0
  589. package/skills/sync-submission/scripts/check_asset_anonymization.py +300 -0
  590. package/skills/sync-submission/scripts/check_cross_artifact_stale.py +211 -0
  591. package/skills/sync-submission/scripts/cover_letter_drift_check.py +451 -0
  592. package/skills/sync-submission/scripts/cross_document_n_check.py +486 -0
  593. package/skills/sync-submission/scripts/detect_copy_divergence.py +136 -0
  594. package/skills/sync-submission/scripts/preflight_gate.py +458 -0
  595. package/skills/sync-submission/scripts/scope_drift_check.py +362 -0
  596. package/skills/sync-submission/scripts/sync_submission.py +169 -0
  597. package/skills/sync-submission/skill.yml +43 -0
  598. package/skills/sync-submission/tests/fixtures/copy_ok.md +5 -0
  599. package/skills/sync-submission/tests/fixtures/copy_stale.md +5 -0
  600. package/skills/sync-submission/tests/fixtures/ssot.md +5 -0
  601. package/skills/sync-submission/tests/test_asset_anonymization.sh +99 -0
  602. package/skills/sync-submission/tests/test_copy_divergence.sh +44 -0
  603. package/skills/sync-submission/tests/test_cross_artifact_stale.sh +80 -0
  604. package/skills/sync-submission/tests/test_cross_document_n.sh +132 -0
  605. package/skills/sync-submission/tests/test_preflight_gate.sh +112 -0
  606. package/skills/sync-submission/tests/test_scope_drift.sh +122 -0
  607. package/skills/sync-submission/tests/test_vN_docx_assertion.sh +51 -0
  608. package/skills/verify-refs/SKILL.md +177 -0
  609. package/skills/verify-refs/references/manual_checkpoint_guide.md +100 -0
  610. package/skills/verify-refs/scripts/verify_cli.sh +62 -0
  611. package/skills/verify-refs/scripts/verify_refs.py +782 -0
  612. package/skills/verify-refs/skill.yml +44 -0
  613. package/skills/verify-refs/tests/fixtures/pagination_placeholder.bib +17 -0
  614. package/skills/verify-refs/tests/test_pagination_placeholder.sh +42 -0
  615. package/skills/version-dataset/SKILL.md +143 -0
  616. package/skills/version-dataset/references/manifest_schema.md +72 -0
  617. package/skills/version-dataset/scripts/version_dataset.py +242 -0
  618. package/skills/version-dataset/skill.yml +35 -0
  619. package/skills/version-dataset/tests/test_version_dataset.sh +52 -0
  620. package/skills/write-paper/SKILL.md +1148 -0
  621. package/skills/write-paper/references/exemplar_methods/README.md +38 -0
  622. package/skills/write-paper/references/exemplar_methods/ai_validation_tripod_claim.md +47 -0
  623. package/skills/write-paper/references/exemplar_methods/diagnostic_accuracy_stard.md +50 -0
  624. package/skills/write-paper/references/exemplar_methods/observational_cohort_strobe.md +43 -0
  625. package/skills/write-paper/references/journal_profiles/AJNR.md +185 -0
  626. package/skills/write-paper/references/journal_profiles/AJR.md +149 -0
  627. package/skills/write-paper/references/journal_profiles/Abdominal_Radiology.md +139 -0
  628. package/skills/write-paper/references/journal_profiles/Academic_Radiology.md +90 -0
  629. package/skills/write-paper/references/journal_profiles/Annals_of_Internal_Medicine.md +150 -0
  630. package/skills/write-paper/references/journal_profiles/Artificial_Intelligence_in_Medicine.md +82 -0
  631. package/skills/write-paper/references/journal_profiles/British_Journal_of_Radiology.md +161 -0
  632. package/skills/write-paper/references/journal_profiles/CVIR.md +157 -0
  633. package/skills/write-paper/references/journal_profiles/Chest.md +270 -0
  634. package/skills/write-paper/references/journal_profiles/Clinical_Radiology.md +160 -0
  635. package/skills/write-paper/references/journal_profiles/Clinical_and_Molecular_Hepatology.md +147 -0
  636. package/skills/write-paper/references/journal_profiles/Diabetes_Metabolism_Journal.md +163 -0
  637. package/skills/write-paper/references/journal_profiles/Diagnostic_and_Interventional_Radiology.md +216 -0
  638. package/skills/write-paper/references/journal_profiles/Endocrinology_and_Metabolism.md +167 -0
  639. package/skills/write-paper/references/journal_profiles/European_Journal_of_Preventive_Cardiology.md +192 -0
  640. package/skills/write-paper/references/journal_profiles/European_Radiology.md +159 -0
  641. package/skills/write-paper/references/journal_profiles/Hepatology_Communications.md +110 -0
  642. package/skills/write-paper/references/journal_profiles/Hepatology_International.md +106 -0
  643. package/skills/write-paper/references/journal_profiles/IEEE_TMI.md +180 -0
  644. package/skills/write-paper/references/journal_profiles/INSI.md +163 -0
  645. package/skills/write-paper/references/journal_profiles/Investigative_Radiology.md +86 -0
  646. package/skills/write-paper/references/journal_profiles/JACC_Advances.md +197 -0
  647. package/skills/write-paper/references/journal_profiles/JACC_Asia.md +168 -0
  648. package/skills/write-paper/references/journal_profiles/JACR.md +87 -0
  649. package/skills/write-paper/references/journal_profiles/JAMA.md +188 -0
  650. package/skills/write-paper/references/journal_profiles/JAMA_Network_Open.md +170 -0
  651. package/skills/write-paper/references/journal_profiles/JCSM.md +266 -0
  652. package/skills/write-paper/references/journal_profiles/JKMS.md +201 -0
  653. package/skills/write-paper/references/journal_profiles/JMIR.md +88 -0
  654. package/skills/write-paper/references/journal_profiles/JMIR_Medical_Education.md +86 -0
  655. package/skills/write-paper/references/journal_profiles/JNIS.md +227 -0
  656. package/skills/write-paper/references/journal_profiles/JVIR.md +158 -0
  657. package/skills/write-paper/references/journal_profiles/Journal_of_Clinical_Endocrinology_and_Metabolism.md +191 -0
  658. package/skills/write-paper/references/journal_profiles/Journal_of_Stroke.md +176 -0
  659. package/skills/write-paper/references/journal_profiles/KJR.md +185 -0
  660. package/skills/write-paper/references/journal_profiles/Korean_Circulation_Journal.md +184 -0
  661. package/skills/write-paper/references/journal_profiles/Korean_Journal_of_Internal_Medicine.md +178 -0
  662. package/skills/write-paper/references/journal_profiles/Lancet_Gastroenterology_and_Hepatology.md +127 -0
  663. package/skills/write-paper/references/journal_profiles/Liver_International.md +165 -0
  664. package/skills/write-paper/references/journal_profiles/Medical_Image_Analysis.md +147 -0
  665. package/skills/write-paper/references/journal_profiles/NEJM.md +147 -0
  666. package/skills/write-paper/references/journal_profiles/Nature_Medicine.md +181 -0
  667. package/skills/write-paper/references/journal_profiles/Neuroradiology.md +151 -0
  668. package/skills/write-paper/references/journal_profiles/Nutrition_Metabolism_and_Cardiovascular_Diseases.md +184 -0
  669. package/skills/write-paper/references/journal_profiles/PLOS_Medicine.md +166 -0
  670. package/skills/write-paper/references/journal_profiles/RYAI.md +124 -0
  671. package/skills/write-paper/references/journal_profiles/Radiology.md +173 -0
  672. package/skills/write-paper/references/journal_profiles/Skeletal_Radiology.md +135 -0
  673. package/skills/write-paper/references/journal_profiles/Stroke.md +210 -0
  674. package/skills/write-paper/references/journal_profiles/The_BMJ.md +121 -0
  675. package/skills/write-paper/references/journal_profiles/The_Lancet.md +112 -0
  676. package/skills/write-paper/references/journal_profiles/The_Lancet_Digital_Health.md +104 -0
  677. package/skills/write-paper/references/journal_profiles/World_Journal_of_Hepatology.md +106 -0
  678. package/skills/write-paper/references/journal_profiles/npj_Digital_Medicine.md +93 -0
  679. package/skills/write-paper/references/paper_types/ai_validation.md +270 -0
  680. package/skills/write-paper/references/paper_types/animal_study.md +194 -0
  681. package/skills/write-paper/references/paper_types/case_report.md +237 -0
  682. package/skills/write-paper/references/paper_types/cross_national.md +328 -0
  683. package/skills/write-paper/references/paper_types/letter.md +127 -0
  684. package/skills/write-paper/references/paper_types/meta_analysis.md +181 -0
  685. package/skills/write-paper/references/paper_types/nhis_cohort.md +297 -0
  686. package/skills/write-paper/references/paper_types/original_article.md +221 -0
  687. package/skills/write-paper/references/paper_types/technical_note.md +131 -0
  688. package/skills/write-paper/references/section_guides/discussion.md +155 -0
  689. package/skills/write-paper/references/section_guides/introduction.md +108 -0
  690. package/skills/write-paper/references/section_guides/methods.md +144 -0
  691. package/skills/write-paper/references/section_guides/results.md +113 -0
  692. package/skills/write-paper/references/section_guides/step7_1_classical_qc.md +67 -0
  693. package/skills/write-paper/references/section_guides/step7_4a_audit_recovery.md +74 -0
  694. package/skills/write-paper/references/section_guides/title_abstract.md +123 -0
  695. package/skills/write-paper/references/section_templates/methods_statistical.md +147 -0
  696. package/skills/write-paper/scripts/check_placeholders.py +182 -0
  697. package/skills/write-paper/skill.yml +48 -0
  698. package/skills/write-paper/tests/test_placeholders.sh +107 -0
  699. package/skills/write-protocol/SKILL.md +243 -0
  700. package/skills/write-protocol/references/ethics_checklist.md +150 -0
  701. package/skills/write-protocol/references/protocol_template.md +304 -0
  702. 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())