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,362 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ scope_drift_check.py — Phase 6 intra-manuscript scope drift detection.
4
+
5
+ Detects two related failure modes:
6
+
7
+ 1. **Numeric anchor in Limitations only**: an AUC / OR / HR / RR /
8
+ sensitivity / specificity value appears in the Limitations or Discussion
9
+ section but is absent from Methods + Results. This is a strong indicator
10
+ that a late-revision sensitivity analysis was introduced without
11
+ propagating to the primary report, leaving the manuscript's stated scope
12
+ inconsistent with its prose-level claims.
13
+
14
+ 2. **PROSPERO ↔ Methods synthesis-method drift**: the PROSPERO record
15
+ commits to a synthesis method (e.g., Freeman-Tukey transformation,
16
+ random-effects DerSimonian-Laird, bivariate, HSROC, Bayesian) but the
17
+ Methods section silently uses a different one — or vice versa. This
18
+ is a documented "silent protocol deviation" pattern that reviewers
19
+ flag as fabrication-grade if accompanied by a "no amendment lodged"
20
+ PROSPERO note.
21
+
22
+ Why this gate exists
23
+ ====================
24
+ Cross-project precedents (anonymized):
25
+ - Project (DTA-MA example, reporting-quality SR variant): a leave-pair-out
26
+ sensitivity envelope appeared in Limitations with five AUC values and
27
+ four CIs. The primary pool AUC `0.869` was not reported in Methods or
28
+ Results.
29
+ - Project (intervention-MA example): PROSPERO committed to Freeman-Tukey
30
+ pooled-proportion; Methods said descriptive-only Python with no R; the
31
+ manuscript line "no amendment lodged" turned the silent change into a
32
+ documented violation.
33
+
34
+ P0 active-fix pattern when caught.
35
+
36
+ Usage
37
+ =====
38
+
39
+ python scope_drift_check.py \\
40
+ --manuscript manuscript.md \\
41
+ --prospero prospero/prospero_v2.md \\
42
+ --out qc/scope_drift.json
43
+
44
+ python scope_drift_check.py \\
45
+ --manuscript manuscript.md \\
46
+ --out qc/scope_drift.json
47
+
48
+ Output (qc/scope_drift.json):
49
+
50
+ {
51
+ "submission_safe": false,
52
+ "limitations_only_anchors": [
53
+ {"anchor": "0.869", "kind": "AUC",
54
+ "found_in": ["Limitations:31"], "missing_from": ["Methods", "Results"]}
55
+ ],
56
+ "synthesis_method_drift": [
57
+ {"method": "Freeman-Tukey", "prospero": true, "methods": false}
58
+ ]
59
+ }
60
+
61
+ Exit codes:
62
+ 0 — no drift
63
+ 1 — drift detected
64
+ 2 — invocation error
65
+
66
+ Read-only script. No file modification.
67
+ """
68
+
69
+ from __future__ import annotations
70
+
71
+ import argparse
72
+ import json
73
+ import re
74
+ import sys
75
+ from dataclasses import asdict, dataclass, field
76
+ from pathlib import Path
77
+
78
+
79
+ # Section detection — case-insensitive header match. We deliberately accept
80
+ # common variants seen in radiology / MA manuscripts: bolded all-caps
81
+ # (`## **METHODS**`), Title Case (`## Methods`), and Markdown subsections.
82
+ SECTION_HEADERS: dict[str, re.Pattern[str]] = {
83
+ "Methods": re.compile(
84
+ r"^#{1,3}\s*\*{0,2}(?:METHODS?|Method[s]?|Materials and Methods)\*{0,2}\s*$",
85
+ re.IGNORECASE | re.MULTILINE,
86
+ ),
87
+ "Results": re.compile(
88
+ r"^#{1,3}\s*\*{0,2}(?:RESULTS?|Result[s]?|Findings)\*{0,2}\s*$",
89
+ re.IGNORECASE | re.MULTILINE,
90
+ ),
91
+ "Discussion": re.compile(
92
+ r"^#{1,3}\s*\*{0,2}(?:DISCUSSION|Discussion)\*{0,2}\s*$",
93
+ re.IGNORECASE | re.MULTILINE,
94
+ ),
95
+ "Limitations": re.compile(
96
+ r"^#{1,3}\s*\*{0,2}(?:LIMITATIONS?|Limitations?|Study Limitations)\*{0,2}\s*$",
97
+ re.IGNORECASE | re.MULTILINE,
98
+ ),
99
+ "Conclusion": re.compile(
100
+ r"^#{1,3}\s*\*{0,2}(?:CONCLUSIONS?|Conclusion[s]?)\*{0,2}\s*$",
101
+ re.IGNORECASE | re.MULTILINE,
102
+ ),
103
+ }
104
+
105
+
106
+ # Numeric anchor patterns. We bias toward conservative matchers: false
107
+ # positives (e.g. "0.50%" from a table) are easier to triage than false
108
+ # negatives (a missed AUC). Each entry yields a canonical "anchor" string.
109
+ ANCHOR_PATTERNS = [
110
+ # AUC: 0.600-0.999 (typical study range, excludes test-set 0.5 floor)
111
+ ("AUC", re.compile(r"\b(0\.[6-9]\d{2})\b")),
112
+ # OR / HR / RR / aOR / aHR with explicit label
113
+ (
114
+ "RiskRatio",
115
+ re.compile(
116
+ r"\b(?:aOR|aHR|OR|HR|RR)\s*[=:]\s*(\d+\.\d+)\b",
117
+ re.IGNORECASE,
118
+ ),
119
+ ),
120
+ # Sensitivity / specificity with explicit label
121
+ (
122
+ "SnSp",
123
+ re.compile(
124
+ r"\b(?:Sn|Sp|sens(?:itivity)?|spec(?:ificity)?)\s*[=:]\s*"
125
+ r"(\d+(?:\.\d+)?%?)\b",
126
+ re.IGNORECASE,
127
+ ),
128
+ ),
129
+ ]
130
+
131
+
132
+ # Synthesis method keywords (case-insensitive substring search). When the
133
+ # PROSPERO record names a method that does NOT appear in Methods (or
134
+ # vice-versa), emit a drift.
135
+ SYNTHESIS_METHODS = [
136
+ "Freeman-Tukey",
137
+ "Mantel-Haenszel",
138
+ "DerSimonian-Laird",
139
+ "REML",
140
+ "bivariate",
141
+ "HSROC",
142
+ "Bayesian",
143
+ "random-effects",
144
+ "fixed-effect",
145
+ "fixed-effects",
146
+ "Egger",
147
+ "I²",
148
+ "Wilson",
149
+ "Clopper-Pearson",
150
+ ]
151
+
152
+
153
+ @dataclass
154
+ class LimitsOnlyAnchor:
155
+ anchor: str
156
+ kind: str
157
+ found_in: list[str]
158
+ missing_from: list[str]
159
+
160
+
161
+ @dataclass
162
+ class SynthesisDrift:
163
+ method: str
164
+ prospero: bool
165
+ methods: bool
166
+
167
+
168
+ @dataclass
169
+ class Report:
170
+ submission_safe: bool
171
+ limitations_only_anchors: list[LimitsOnlyAnchor] = field(default_factory=list)
172
+ synthesis_method_drift: list[SynthesisDrift] = field(default_factory=list)
173
+
174
+ def as_dict(self) -> dict:
175
+ return {
176
+ "submission_safe": self.submission_safe,
177
+ "limitations_only_anchors": [asdict(a) for a in self.limitations_only_anchors],
178
+ "synthesis_method_drift": [asdict(s) for s in self.synthesis_method_drift],
179
+ }
180
+
181
+
182
+ def split_sections(text: str) -> dict[str, str]:
183
+ """Return mapping of section name → body text. Sections without a header
184
+ return empty string. Body extends from the matched header to the next
185
+ matched header in the document."""
186
+ # Compute (name, start, end_of_header_line) per match across the doc.
187
+ hits: list[tuple[str, int, int]] = []
188
+ for name, pat in SECTION_HEADERS.items():
189
+ for m in pat.finditer(text):
190
+ hits.append((name, m.start(), m.end()))
191
+ hits.sort(key=lambda t: t[1])
192
+
193
+ out: dict[str, str] = {name: "" for name in SECTION_HEADERS}
194
+ for i, (name, start, hdr_end) in enumerate(hits):
195
+ body_start = hdr_end
196
+ body_end = hits[i + 1][1] if i + 1 < len(hits) else len(text)
197
+ out[name] = (out[name] + "\n" + text[body_start:body_end]).strip()
198
+ return out
199
+
200
+
201
+ def find_anchors(text: str) -> list[tuple[str, str]]:
202
+ """Return (anchor_string, kind) tuples found in text."""
203
+ out: list[tuple[str, str]] = []
204
+ for kind, pat in ANCHOR_PATTERNS:
205
+ for m in pat.finditer(text):
206
+ anchor = m.group(1)
207
+ out.append((anchor, kind))
208
+ return out
209
+
210
+
211
+ def line_numbers(text: str, needle: str) -> list[int]:
212
+ """Return 1-based line numbers in text containing the literal needle."""
213
+ out: list[int] = []
214
+ for i, line in enumerate(text.splitlines(), start=1):
215
+ if needle in line:
216
+ out.append(i)
217
+ return out
218
+
219
+
220
+ def detect_limits_only(text: str) -> list[LimitsOnlyAnchor]:
221
+ sections = split_sections(text)
222
+ limits = sections.get("Limitations", "")
223
+ discussion = sections.get("Discussion", "")
224
+ methods = sections.get("Methods", "")
225
+ results = sections.get("Results", "")
226
+
227
+ # Anchors that appear in Limitations OR Discussion but NOT in Methods or
228
+ # Results. We include Discussion because in many manuscripts the
229
+ # Limitations subsection is folded into Discussion without an explicit
230
+ # `## Limitations` header.
231
+ out: list[LimitsOnlyAnchor] = []
232
+ seen: set[tuple[str, str]] = set()
233
+ for region_name, region_text in (("Limitations", limits), ("Discussion", discussion)):
234
+ for anchor, kind in find_anchors(region_text):
235
+ if (anchor, kind) in seen:
236
+ continue
237
+ in_methods = anchor in methods
238
+ in_results = anchor in results
239
+ if not in_methods and not in_results:
240
+ lineno = line_numbers(text, anchor)
241
+ found_label = f"{region_name}:{lineno[0]}" if lineno else region_name
242
+ out.append(
243
+ LimitsOnlyAnchor(
244
+ anchor=anchor,
245
+ kind=kind,
246
+ found_in=[found_label],
247
+ missing_from=["Methods", "Results"],
248
+ )
249
+ )
250
+ seen.add((anchor, kind))
251
+ return out
252
+
253
+
254
+ def detect_synthesis_drift(
255
+ manuscript_text: str,
256
+ prospero_text: str | None,
257
+ ) -> list[SynthesisDrift]:
258
+ if prospero_text is None:
259
+ return []
260
+ sections = split_sections(manuscript_text)
261
+ methods_text = sections.get("Methods", "")
262
+ out: list[SynthesisDrift] = []
263
+ for method in SYNTHESIS_METHODS:
264
+ # case-insensitive presence
265
+ in_prospero = method.lower() in prospero_text.lower()
266
+ in_methods = method.lower() in methods_text.lower()
267
+ if in_prospero != in_methods:
268
+ out.append(
269
+ SynthesisDrift(
270
+ method=method,
271
+ prospero=in_prospero,
272
+ methods=in_methods,
273
+ )
274
+ )
275
+ return out
276
+
277
+
278
+ def build_report(
279
+ manuscript_path: Path,
280
+ prospero_path: Path | None,
281
+ ) -> Report:
282
+ text = manuscript_path.read_text(encoding="utf-8")
283
+ prospero_text: str | None = None
284
+ if prospero_path is not None and prospero_path.is_file():
285
+ prospero_text = prospero_path.read_text(encoding="utf-8")
286
+
287
+ limits_only = detect_limits_only(text)
288
+ synth_drift = detect_synthesis_drift(text, prospero_text)
289
+ submission_safe = not limits_only and not synth_drift
290
+ return Report(
291
+ submission_safe=submission_safe,
292
+ limitations_only_anchors=limits_only,
293
+ synthesis_method_drift=synth_drift,
294
+ )
295
+
296
+
297
+ def main(argv: list[str] | None = None) -> int:
298
+ parser = argparse.ArgumentParser(
299
+ description=(
300
+ "Phase 6 intra-manuscript scope drift detection. Flags numeric "
301
+ "anchors that appear only in Limitations/Discussion (not Methods/"
302
+ "Results) and PROSPERO↔Methods synthesis-method disagreement."
303
+ )
304
+ )
305
+ parser.add_argument(
306
+ "--manuscript",
307
+ type=Path,
308
+ required=True,
309
+ help="Path to manuscript.md (markdown).",
310
+ )
311
+ parser.add_argument(
312
+ "--prospero",
313
+ type=Path,
314
+ default=None,
315
+ help=(
316
+ "Path to PROSPERO record markdown. When supplied, also performs "
317
+ "synthesis-method cross-check vs Methods."
318
+ ),
319
+ )
320
+ parser.add_argument(
321
+ "--out",
322
+ type=Path,
323
+ default=None,
324
+ help="Write JSON report to this path.",
325
+ )
326
+ parser.add_argument(
327
+ "--quiet",
328
+ action="store_true",
329
+ help="Suppress stdout summary.",
330
+ )
331
+ args = parser.parse_args(argv)
332
+
333
+ if not args.manuscript.is_file():
334
+ parser.error(f"--manuscript not a file: {args.manuscript}")
335
+ if args.prospero is not None and not args.prospero.is_file():
336
+ parser.error(f"--prospero not a file: {args.prospero}")
337
+
338
+ report = build_report(args.manuscript, args.prospero)
339
+
340
+ if args.out is not None:
341
+ args.out.parent.mkdir(parents=True, exist_ok=True)
342
+ args.out.write_text(json.dumps(report.as_dict(), indent=2), encoding="utf-8")
343
+
344
+ if not args.quiet:
345
+ if report.submission_safe:
346
+ print("PASS: no scope drift detected.")
347
+ else:
348
+ print(
349
+ f"FAIL: {len(report.limitations_only_anchors)} limits-only anchor(s), "
350
+ f"{len(report.synthesis_method_drift)} synthesis drift(s)."
351
+ )
352
+ for a in report.limitations_only_anchors:
353
+ print(f" - SCOPE_DRIFT {a.kind} {a.anchor!r} found in {a.found_in}, "
354
+ f"missing from {a.missing_from}")
355
+ for s in report.synthesis_method_drift:
356
+ print(f" - PROSPERO_DRIFT {s.method} prospero={s.prospero} methods={s.methods}")
357
+
358
+ return 0 if report.submission_safe else 1
359
+
360
+
361
+ if __name__ == "__main__":
362
+ sys.exit(main())
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env python3
2
+ """Audit and build submission packages from canonical manuscript sources."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import hashlib
8
+ import json
9
+ import shutil
10
+ import sys
11
+ from datetime import date
12
+ from pathlib import Path
13
+
14
+
15
+ def sha256_file(path: Path) -> str:
16
+ h = hashlib.sha256()
17
+ with path.open("rb") as fh:
18
+ for chunk in iter(lambda: fh.read(1024 * 1024), b""):
19
+ h.update(chunk)
20
+ return "sha256:" + h.hexdigest()
21
+
22
+
23
+ def read_project_yaml(path: Path) -> dict[str, str]:
24
+ data: dict[str, str] = {}
25
+ if not path.exists():
26
+ return data
27
+ for raw in path.read_text(encoding="utf-8").splitlines():
28
+ line = raw.strip()
29
+ if not line or line.startswith("#") or ":" not in line:
30
+ continue
31
+ key, value = line.split(":", 1)
32
+ data[key.strip()] = value.strip().strip('"').strip("'")
33
+ return data
34
+
35
+
36
+ def resolve_canonical(project_root: Path, explicit: str | None) -> Path:
37
+ if explicit:
38
+ path = Path(explicit)
39
+ return path if path.is_absolute() else project_root / path
40
+ project = read_project_yaml(project_root / "project.yaml")
41
+ rel = project.get("canonical_manuscript", "manuscript/manuscript.md")
42
+ return project_root / rel
43
+
44
+
45
+ def submission_md_path(project_root: Path, journal: str) -> Path:
46
+ return project_root / "submission" / journal / "manuscript" / "manuscript.md"
47
+
48
+
49
+ def write_json(path: Path, payload: dict) -> None:
50
+ path.parent.mkdir(parents=True, exist_ok=True)
51
+ path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
52
+
53
+
54
+ def load_json(path: Path) -> dict:
55
+ if not path.exists():
56
+ return {}
57
+ try:
58
+ return json.loads(path.read_text(encoding="utf-8"))
59
+ except json.JSONDecodeError:
60
+ return {}
61
+
62
+
63
+ def update_manifest(project_root: Path, journal: str, meta_rel: str, status: str, source_hash: str) -> None:
64
+ manifest_path = project_root / "artifact_manifest.json"
65
+ manifest = load_json(manifest_path) or {"schema_version": 1, "submissions": {}}
66
+ manifest.setdefault("schema_version", 1)
67
+ manifest.setdefault("submissions", {})
68
+ manifest["submissions"][journal] = {
69
+ "path": f"submission/{journal}",
70
+ "metadata": meta_rel,
71
+ "status": status,
72
+ "source_hash": source_hash,
73
+ }
74
+ write_json(manifest_path, manifest)
75
+
76
+
77
+ def audit(project_root: Path, journal: str, canonical: Path) -> int:
78
+ qc_path = project_root / "qc" / f"submission_sync_{journal}.json"
79
+ sub_path = submission_md_path(project_root, journal)
80
+ if not canonical.exists():
81
+ write_json(qc_path, {"schema_version": 1, "journal": journal, "status": "ERROR", "message": "canonical manuscript missing"})
82
+ return 2
83
+ source_hash = sha256_file(canonical)
84
+ meta_path = project_root / "submission" / journal / ".journal_meta.json"
85
+ meta = load_json(meta_path)
86
+ current_hash = sha256_file(sub_path) if sub_path.exists() else None
87
+ recorded_hash = meta.get("source_hash")
88
+ drift = bool(sub_path.exists() and current_hash != source_hash)
89
+ status = "DRIFT" if drift else "CURRENT" if sub_path.exists() else "MISSING_SUBMISSION"
90
+ payload = {
91
+ "schema_version": 1,
92
+ "journal": journal,
93
+ "status": status,
94
+ "canonical": str(canonical.relative_to(project_root)),
95
+ "submission_manuscript": str(sub_path.relative_to(project_root)),
96
+ "source_hash": source_hash,
97
+ "submission_hash": current_hash,
98
+ "recorded_source_hash": recorded_hash,
99
+ "message": "Submission differs from canonical manuscript" if drift else "",
100
+ }
101
+ write_json(qc_path, payload)
102
+ print(json.dumps(payload, indent=2))
103
+ return 1 if drift else 0
104
+
105
+
106
+ def build(project_root: Path, journal: str, canonical: Path) -> int:
107
+ if not canonical.exists():
108
+ print(f"Canonical manuscript missing: {canonical}", file=sys.stderr)
109
+ return 2
110
+ sub_path = submission_md_path(project_root, journal)
111
+ sub_path.parent.mkdir(parents=True, exist_ok=True)
112
+ shutil.copy2(canonical, sub_path)
113
+ source_hash = sha256_file(canonical)
114
+ meta_path = project_root / "submission" / journal / ".journal_meta.json"
115
+ meta = {
116
+ "schema_version": 1,
117
+ "journal": journal,
118
+ "status": "built",
119
+ "canonical": str(canonical.relative_to(project_root)),
120
+ "submission_manuscript": str(sub_path.relative_to(project_root)),
121
+ "source_hash": source_hash,
122
+ "built_date": date.today().isoformat(),
123
+ "frozen": False,
124
+ }
125
+ write_json(meta_path, meta)
126
+ update_manifest(project_root, journal, str(meta_path.relative_to(project_root)), "built", source_hash)
127
+ return audit(project_root, journal, canonical)
128
+
129
+
130
+ def freeze(project_root: Path, journal: str, canonical: Path, status: str) -> int:
131
+ audit_code = audit(project_root, journal, canonical)
132
+ if audit_code != 0:
133
+ print("Cannot freeze a drifted or invalid submission package.", file=sys.stderr)
134
+ return audit_code
135
+ meta_path = project_root / "submission" / journal / ".journal_meta.json"
136
+ meta = load_json(meta_path)
137
+ meta.update({"status": status, "frozen": True, "frozen_date": date.today().isoformat()})
138
+ write_json(meta_path, meta)
139
+ update_manifest(project_root, journal, str(meta_path.relative_to(project_root)), status, meta.get("source_hash", ""))
140
+ print(json.dumps(meta, indent=2))
141
+ return 0
142
+
143
+
144
+ def main() -> int:
145
+ parser = argparse.ArgumentParser(description="Audit or build journal submission package.")
146
+ parser.add_argument("mode", choices=["audit", "build", "freeze"])
147
+ parser.add_argument("--project-root", default=".")
148
+ parser.add_argument("--journal", required=True)
149
+ parser.add_argument("--canonical")
150
+ parser.add_argument("--status", default="submitted")
151
+ args = parser.parse_args()
152
+
153
+ project_root = Path(args.project_root).resolve()
154
+ canonical = resolve_canonical(project_root, args.canonical).resolve()
155
+ try:
156
+ canonical.relative_to(project_root)
157
+ except ValueError:
158
+ print("Canonical manuscript must be inside project root.", file=sys.stderr)
159
+ return 2
160
+
161
+ if args.mode == "audit":
162
+ return audit(project_root, args.journal, canonical)
163
+ if args.mode == "build":
164
+ return build(project_root, args.journal, canonical)
165
+ return freeze(project_root, args.journal, canonical, args.status)
166
+
167
+
168
+ if __name__ == "__main__":
169
+ sys.exit(main())
@@ -0,0 +1,43 @@
1
+ schema_version: 2
2
+ name: sync-submission
3
+ layer: A
4
+ owner_domain: submission_packaging
5
+ when_to_use:
6
+ - Auditing SSOT-to-submission drift before freezing a journal package
7
+ - Building a journal-specific submission manifest from canonical manuscript artifacts
8
+ - Retargeting an existing submission to a new journal (cascade rejection)
9
+ - Refreshing artifact_manifest.json to reflect the current canonical state
10
+ when_NOT_to_use:
11
+ - Drafting or editing the canonical manuscript (use /write-paper or /revise)
12
+ - Choosing the target journal (use /find-journal)
13
+ - Freezing a submission while drift is detected (forbidden — fix drift first)
14
+ inputs:
15
+ - project.yaml
16
+ - manuscript/manuscript.md
17
+ outputs:
18
+ - submission/{journal}/.journal_meta.json
19
+ - qc/submission_sync_{journal}.json
20
+ - artifact_manifest.json
21
+ deterministic_scripts:
22
+ - scripts/sync_submission.py
23
+ side_effects:
24
+ - writes_project_artifacts
25
+ downstream_consumers:
26
+ - orchestrate
27
+ - find-journal
28
+ forbidden_actions:
29
+ - silently_edit_canonical_manuscript
30
+ - freeze_drifted_submission
31
+
32
+ # v2.1 quality card
33
+ purpose: "Audit SSOT-to-submission drift and build journal submission manifests from canonical manuscript artifacts."
34
+ safety_boundaries:
35
+ - "Never silently edits the canonical manuscript; a drifted submission is not frozen until reconciled."
36
+ - "Submission packages are derived from canonical sources, not hand-assembled."
37
+ known_limitations:
38
+ - "Detects drift it is configured to scan (counts, cover-letter fields, scope); portal free-text fields still need a human check."
39
+ - "A clean audit is necessary, not sufficient, for acceptance."
40
+ validation_commands:
41
+ - "python3 scripts/sync_submission.py"
42
+ - "python3 scripts/cross_document_n_check.py"
43
+ evidence_surface: bundled_script
@@ -0,0 +1,5 @@
1
+ ## Methods
2
+ After re-lock the analytic cohort comprised n = 998 participants. Emphysema was
3
+ associated with mortality (HR 1.34), not significant (p = 0.074); prevalence 12.5%.
4
+ ## Results
5
+ The adjusted estimate was OR 2.25 in the exploratory analysis.
@@ -0,0 +1,5 @@
1
+ ## Methods
2
+ The analytic cohort comprised n = 998 participants. Emphysema was associated with
3
+ mortality (HR 1.34) but this was not significant. Prevalence was 12.5%.
4
+ ## Results
5
+ The adjusted estimate was OR 2.25 in the exploratory analysis.
@@ -0,0 +1,5 @@
1
+ ## Methods
2
+ The analytic cohort comprised n = 998 participants. Emphysema was associated with
3
+ mortality (HR 1.34) but this was not significant (p = 0.074). Prevalence was 12.5%.
4
+ ## Results
5
+ The adjusted estimate was OR 2.25 in the exploratory analysis.
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env bash
2
+ # Test scripts/check_asset_anonymization.py — the A2 asset-anonymization gate.
3
+ # Synthetic, PII-free fixtures: a figure script with a generic institution token,
4
+ # a docx with a (synthetic) author metadata, a --names-file name hit, and clean
5
+ # counterparts. Stdlib-only; does not require poppler (PDF paths are exercised
6
+ # only opportunistically).
7
+ set -u
8
+
9
+ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ SCRIPT="$HERE/../scripts/check_asset_anonymization.py"
11
+ PASS=0
12
+ FAIL=0
13
+ ok() { echo " PASS: $1"; PASS=$((PASS+1)); }
14
+ bad() { echo " FAIL: $1"; FAIL=$((FAIL+1)); }
15
+
16
+ WORK="$(mktemp -d)"
17
+ trap 'rm -rf "$WORK"' EXIT
18
+
19
+ # --- fixtures ---
20
+ mkdir -p "$WORK/leaky/figures" "$WORK/clean/figures"
21
+
22
+ # figure script with an institution token (review-severity)
23
+ cat > "$WORK/leaky/figures/flow.R" <<'EOF'
24
+ # CONSORT flow diagram
25
+ label <- "Recruited at General Hospital, 2020-2024"
26
+ plot(1:10)
27
+ EOF
28
+ # clean figure script
29
+ cat > "$WORK/clean/figures/plot.py" <<'EOF'
30
+ import matplotlib.pyplot as plt
31
+ plt.plot([1, 2, 3]); plt.savefig("fig1.png")
32
+ EOF
33
+ # figure script with a name that only --names-file knows (leak-severity)
34
+ cat > "$WORK/leaky/figures/panel.py" <<'EOF'
35
+ caption = "Data from Mercy Riverside Clinic cohort"
36
+ EOF
37
+ cat > "$WORK/names.txt" <<'EOF'
38
+ Mercy Riverside Clinic
39
+ EOF
40
+
41
+ # build a docx with synthetic author metadata (leak) and a clean one (tool author)
42
+ python3 - "$WORK" <<'PY'
43
+ import sys, zipfile, os
44
+ work = sys.argv[1]
45
+ def make_docx(path, creator):
46
+ core = (
47
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
48
+ '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" '
49
+ 'xmlns:dc="http://purl.org/dc/elements/1.1/">'
50
+ f'<dc:creator>{creator}</dc:creator>'
51
+ f'<cp:lastModifiedBy>{creator}</cp:lastModifiedBy>'
52
+ '</cp:coreProperties>'
53
+ )
54
+ with zipfile.ZipFile(path, "w") as z:
55
+ z.writestr("docProps/core.xml", core)
56
+ z.writestr("word/document.xml", "<w:document/>")
57
+ make_docx(os.path.join(work, "leaky", "manuscript.docx"), "Alex P. Investigator")
58
+ make_docx(os.path.join(work, "clean", "manuscript.docx"), "Microsoft Office User")
59
+ PY
60
+
61
+ run() { python3 "$SCRIPT" "$@" 2>/dev/null; }
62
+
63
+ # 1. leaky dir (no names file): docx author leak -> exit 1
64
+ run --dir "$WORK/leaky" --quiet
65
+ [ $? -eq 1 ] && ok "leaky dir fails (docx author leak)" || bad "leaky dir should fail"
66
+
67
+ # 2. JSON reports the docx author + institution token finding types
68
+ out="$(run --dir "$WORK/leaky" --names-file "$WORK/names.txt" --out "$WORK/r.json"; cat "$WORK/r.json")"
69
+ echo "$out" | python3 -c "
70
+ import json,sys
71
+ d=json.load(open('$WORK/r.json'))
72
+ types={f['type'] for f in d['findings']}
73
+ need={'docx_metadata_author','figure_script_institution','figure_script_name'}
74
+ sys.exit(0 if need <= types and d['submission_safe'] is False else 1)
75
+ " && ok "JSON: docx author + institution + name-file findings" || bad "JSON findings incomplete"
76
+
77
+ # 3. name-file hit is a 'leak'
78
+ run --dir "$WORK/leaky" --names-file "$WORK/names.txt" --out "$WORK/r2.json" --quiet
79
+ python3 -c "
80
+ import json,sys
81
+ d=json.load(open('$WORK/r2.json'))
82
+ sys.exit(0 if d['summary']['leak'] >= 2 else 1) # docx author + name hit
83
+ " && ok "name-file hit counts as leak" || bad "name-file hit should be leak"
84
+
85
+ # 4. clean dir -> exit 0
86
+ run --dir "$WORK/clean" --quiet
87
+ [ $? -eq 0 ] && ok "clean dir passes (tool author, no tokens)" || bad "clean dir should pass"
88
+
89
+ # 5. review-only dir under --strict fails; default passes
90
+ mkdir -p "$WORK/reviewonly/figures"
91
+ cp "$WORK/leaky/figures/flow.R" "$WORK/reviewonly/figures/flow.R"
92
+ run --dir "$WORK/reviewonly" --quiet
93
+ [ $? -eq 0 ] && ok "review-only passes by default" || bad "review-only should pass by default"
94
+ run --dir "$WORK/reviewonly" --strict --quiet
95
+ [ $? -eq 1 ] && ok "review-only fails under --strict" || bad "review-only should fail under --strict"
96
+
97
+ echo ""
98
+ echo "test_asset_anonymization: $PASS passed, $FAIL failed"
99
+ [ "$FAIL" -eq 0 ]