medsci-skills 5.20.1 → 5.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +55 -16
- package/README.md +74 -6
- package/installers/install-macos.command +84 -13
- package/installers/install-windows.cmd +99 -17
- package/installers/install.py +72 -0
- package/installers/medsci_txn.py +64 -0
- package/installers/update-macos.command +46 -11
- package/installers/update-windows.cmd +41 -6
- package/installers/update.py +13 -0
- package/metadata/distribution_files.json +533 -323
- package/metadata/distribution_manifest.json +2 -1
- package/metadata/skills_catalog.json +11 -1
- package/package.json +1 -1
- package/skills/MAINTENANCE.md +45 -1
- package/skills/academic-aio/scripts/check_summary_box.py +1 -1
- package/skills/add-journal/SKILL.md +31 -116
- package/skills/add-journal/references/write_paper_profile_template.md +124 -0
- package/skills/analyze-stats/SKILL.md +20 -0
- package/skills/analyze-stats/scripts/check_generated_code.py +1 -1
- package/skills/analyze-stats/scripts/check_separation.py +262 -0
- package/skills/analyze-stats/tests/test_separation.sh +154 -0
- package/skills/check-reporting/SKILL.md +34 -112
- package/skills/check-reporting/references/checklists/CLEAR.md +176 -107
- package/skills/check-reporting/references/checklists/MI_CLEAR_LLM.md +94 -136
- package/skills/check-reporting/references/checklists/TRIPOD_AI.md +127 -117
- package/skills/check-reporting/references/report_templates.md +140 -0
- package/skills/check-reporting/scripts/check_checklist_version.py +1 -1
- package/skills/check-reporting/scripts/check_framework_naming.py +1 -1
- package/skills/check-reporting/scripts/check_prisma_figure.py +1 -1
- package/skills/check-reporting/scripts/verify_checklist_fidelity.py +162 -0
- package/skills/check-reporting/tests/test_checklist_fidelity.sh +148 -0
- package/skills/clean-data/scripts/check_reverse_coding.py +1 -1
- package/skills/clean-data/scripts/check_structural_zero.py +1 -1
- package/skills/contribute/SKILL.md +226 -0
- package/skills/contribute/scripts/check_contribution_safety.py +241 -0
- package/skills/contribute/scripts/contribution_prefs.py +159 -0
- package/skills/contribute/scripts/find_local_changes.py +198 -0
- package/skills/contribute/scripts/star_repo.py +165 -0
- package/skills/contribute/scripts/submit_contribution.py +203 -0
- package/skills/contribute/skill.yml +50 -0
- package/skills/contribute/tests/test_contribution_safety.sh +264 -0
- package/skills/design-study/SKILL.md +16 -88
- package/skills/design-study/references/reader_elicitation_design.md +97 -0
- package/skills/explainability/scripts/check_explainability_report.py +1 -1
- package/skills/find-cohort-gap/SKILL.md +57 -16
- package/skills/find-cohort-gap/references/cohort_profile_template.md +18 -4
- package/skills/find-cohort-gap/scripts/build_cohort_profile.py +528 -0
- package/skills/find-cohort-gap/skill.yml +11 -2
- package/skills/find-cohort-gap/tests/test_cohort_profile.sh +206 -0
- package/skills/find-journal/SKILL.md +2 -72
- package/skills/humanize/SKILL.md +2 -2
- package/skills/lit-sync/SKILL.md +4 -10
- package/skills/make-figures/SKILL.md +39 -93
- package/skills/make-figures/references/exemplar_diagrams/README.md +65 -50
- package/skills/make-figures/references/exemplar_diagrams/consort/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/consort/template_output_600.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/prisma/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/prisma/template_output_600.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/stard/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/stard/template_output_600.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_output.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/strobe/template_output_600.png +0 -0
- package/skills/make-figures/references/figure_manifest.md +38 -0
- package/skills/make-figures/references/flow_diagram_recipe.md +68 -0
- package/skills/make-figures/references/pipeline_concepts_medical_ai.md +1 -1
- package/skills/make-figures/references/visual_abstract_templates/template_guide.md +20 -4
- package/skills/make-figures/scripts/derive_figure_legend_counts.py +1 -1
- package/skills/make-figures/scripts/extract_exemplar_from_pdf.py +14 -2
- package/skills/manage-project/SKILL.md +20 -88
- package/skills/manage-project/references/init_scaffold.md +118 -0
- package/skills/manage-refs/SKILL.md +2 -1
- package/skills/manage-refs/scripts/check_bib_title_markup.py +167 -0
- package/skills/manage-refs/scripts/check_csl_render.py +1 -1
- package/skills/manage-refs/scripts/check_reference_duplication.py +1 -1
- package/skills/manage-refs/scripts/check_xref.py +1 -1
- package/skills/manage-refs/scripts/pre_submission_gate.sh +29 -8
- package/skills/manage-refs/skill.yml +1 -0
- package/skills/manage-refs/tests/test_bib_title_markup.sh +125 -0
- package/skills/meta-analysis/SKILL.md +115 -273
- package/skills/meta-analysis/references/phase3_screening_detail.md +148 -0
- package/skills/meta-analysis/references/phase4_extraction_detail.md +189 -0
- package/skills/meta-analysis/scripts/check_pool_consistency.py +1 -1
- package/skills/mllm-eval/scripts/check_mllm_eval_completeness.py +1 -1
- package/skills/model-card/scripts/check_model_card_complete.py +1 -1
- package/skills/model-evaluation/scripts/check_metric_reporting.py +1 -1
- package/skills/model-scaffold/scripts/check_training_hygiene.py +1 -1
- package/skills/model-validation/scripts/check_split_leakage.py +1 -1
- package/skills/orchestrate/SKILL.md +1 -0
- package/skills/peer-review/SKILL.md +45 -67
- package/skills/peer-review/references/domain-probes/self_improving_system.md +113 -0
- package/skills/peer-review/references/review_draft_template.md +71 -0
- package/skills/peer-review/scripts/check_pdf_injection.py +1 -1
- package/skills/peer-review/scripts/check_review_request_types.py +293 -0
- package/skills/peer-review/scripts/check_review_request_types_challenge/expected/disciplined.txt +3 -0
- package/skills/peer-review/scripts/check_review_request_types_challenge/expected/undisciplined.txt +8 -0
- package/skills/peer-review/scripts/check_review_request_types_challenge/fixture/disciplined.md +19 -0
- package/skills/peer-review/scripts/check_review_request_types_challenge/fixture/undisciplined.md +16 -0
- package/skills/peer-review/scripts/check_review_request_types_challenge/problem.md +87 -0
- package/skills/peer-review/scripts/check_review_request_types_challenge/verify.sh +24 -0
- package/skills/peer-review/scripts/check_self_improvement_claims.py +250 -0
- package/skills/peer-review/tests/test_self_improvement_claims.sh +142 -0
- package/skills/polish-language/SKILL.md +1 -1
- package/skills/preprocess-imaging/scripts/check_preprocessing_leakage.py +1 -1
- package/skills/present-paper/SKILL.md +115 -25
- package/skills/present-paper/references/ai_slide_tells.md +167 -0
- package/skills/present-paper/references/critic_rubrics/slide.md +30 -0
- package/skills/present-paper/references/presentation_archetypes.md +257 -0
- package/skills/present-paper/references/slide_visual_styles/nature_lancet.md +23 -5
- package/skills/present-paper/scripts/check_deck_budget.py +263 -0
- package/skills/present-paper/scripts/check_deck_budget_challenge/make_fixtures.py +96 -0
- package/skills/present-paper/scripts/check_deck_budget_challenge/verify.sh +86 -0
- package/skills/present-paper/scripts/check_slide_tells.py +540 -0
- package/skills/present-paper/scripts/check_slide_tells_challenge/make_fixtures.py +143 -0
- package/skills/present-paper/scripts/check_slide_tells_challenge/verify.sh +86 -0
- package/skills/present-paper/scripts/inject_speaker_notes.py +15 -3
- package/skills/present-paper/skill.yml +6 -0
- package/skills/present-paper/templates/build_pptx_nature_lancet.py +74 -48
- package/skills/present-paper/tests/test_backup_off_the_clock.py +141 -0
- package/skills/present-paper/tests/test_builder_no_chrome.py +183 -0
- package/skills/present-paper/tests/test_md_parity.py +129 -0
- package/skills/radiomics-ml/scripts/check_radiomics_ml.py +1 -1
- package/skills/revise/SKILL.md +38 -5
- package/skills/revise/scripts/check_density_complaint.py +190 -0
- package/skills/revise/scripts/check_response_claims.py +1 -1
- package/skills/revise/scripts/density_complaint_challenge/fixture/decision_letter.md +9 -0
- package/skills/revise/scripts/density_complaint_challenge/fixture/v20_longer.md +20 -0
- package/skills/revise/scripts/density_complaint_challenge/fixture/v21_shorter.md +20 -0
- package/skills/revise/scripts/density_complaint_challenge/fixture/v_prev.md +20 -0
- package/skills/revise/scripts/density_complaint_challenge/verify.sh +65 -0
- package/skills/revise/skill.yml +2 -0
- package/skills/self-review/SKILL.md +246 -614
- package/skills/self-review/references/domain-probes/self_improving_system.md +113 -0
- package/skills/self-review/references/phases/phase2_5a_source_fidelity.md +98 -0
- package/skills/self-review/references/phases/phase2_5b_screening_counts.md +127 -0
- package/skills/self-review/references/phases/phase2_5d_xref_qc.md +94 -0
- package/skills/self-review/references/phases/phase2_5f_claim_artifact.md +183 -0
- package/skills/self-review/references/phases/phase2_systematic_check.md +214 -0
- package/skills/self-review/scripts/check_analysis_definitions.py +279 -0
- package/skills/self-review/scripts/check_analysis_definitions_challenge/expected/defined.txt +4 -0
- package/skills/self-review/scripts/check_analysis_definitions_challenge/expected/undefined.txt +6 -0
- package/skills/self-review/scripts/check_analysis_definitions_challenge/fixture/defined.md +25 -0
- package/skills/self-review/scripts/check_analysis_definitions_challenge/fixture/undefined.md +20 -0
- package/skills/self-review/scripts/check_analysis_definitions_challenge/problem.md +57 -0
- package/skills/self-review/scripts/check_analysis_definitions_challenge/verify.sh +21 -0
- package/skills/self-review/scripts/check_artifact_coverage.py +1 -1
- package/skills/self-review/scripts/check_binning_consistency.py +1 -1
- package/skills/self-review/scripts/check_citation_order.py +1 -1
- package/skills/self-review/scripts/check_claim_artifact.py +1 -1
- package/skills/self-review/scripts/check_classical_style.py +1 -1
- package/skills/self-review/scripts/check_cohort_arithmetic.py +1 -1
- package/skills/self-review/scripts/check_confounding_completeness.py +1 -1
- package/skills/self-review/scripts/check_cv_leakage.py +1 -1
- package/skills/self-review/scripts/check_dta_denominators.py +1 -1
- package/skills/self-review/scripts/check_editorial_impression.py +1 -1
- package/skills/self-review/scripts/check_emphasis_density.py +1 -1
- package/skills/self-review/scripts/check_figure_citation.py +1 -1
- package/skills/self-review/scripts/check_nested_group_comparison.py +1 -1
- package/skills/self-review/scripts/check_null_calibration.py +1 -1
- package/skills/self-review/scripts/check_paired_difference_estimator.py +1 -1
- package/skills/self-review/scripts/check_panel_diversity.py +1 -1
- package/skills/self-review/scripts/check_paren_spans.py +1 -1
- package/skills/self-review/scripts/check_reference_adequacy.py +1 -1
- package/skills/self-review/scripts/check_reported_p_from_counts.py +1 -1
- package/skills/self-review/scripts/check_reviewer_team_consistency.py +1 -3
- package/skills/self-review/scripts/check_rounded_delta.py +1 -1
- package/skills/self-review/scripts/check_scope_coherence.py +1 -1
- package/skills/self-review/scripts/check_supplement_hygiene.py +1 -1
- package/skills/self-review/scripts/check_table_percentages.py +2 -2
- package/skills/sync-submission/SKILL.md +49 -0
- package/skills/sync-submission/scripts/build_marked_manuscript.py +162 -0
- package/skills/sync-submission/scripts/check_asset_anonymization.py +1 -1
- package/skills/sync-submission/scripts/check_checklist_dump_leak.py +1 -1
- package/skills/sync-submission/scripts/check_cross_artifact_stale.py +1 -1
- package/skills/sync-submission/scripts/check_disclosure_availability.py +1 -1
- package/skills/sync-submission/scripts/check_marked_manuscript.py +333 -0
- package/skills/sync-submission/scripts/check_wordcount_cap.py +1 -1
- package/skills/sync-submission/scripts/detect_copy_divergence.py +1 -1
- package/skills/sync-submission/skill.yml +2 -0
- package/skills/sync-submission/tests/test_marked_manuscript.sh +208 -0
- package/skills/uncertainty-imaging/scripts/check_uncertainty_reporting.py +1 -1
- package/skills/verify-refs/scripts/verify_refs.py +38 -8
- package/skills/verify-refs/tests/test_author_normalization.sh +88 -0
- package/skills/write-paper/SKILL.md +116 -363
- package/skills/write-paper/references/phase0_init_detail.md +139 -0
- package/skills/write-paper/references/phase7_polish_detail.md +283 -0
- package/skills/write-paper/scripts/check_placeholders.py +1 -1
- package/skills/make-figures/references/exemplar_diagrams/other/other_02.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/other/other_02.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_01.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_03.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_04.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_05.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_06.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_07.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_08.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_09.png +0 -0
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10.meta.yaml +0 -4
- package/skills/make-figures/references/exemplar_diagrams/pipeline/pipeline_10.png +0 -0
- package/skills/make-figures/references/visual_abstract_templates/european_radiology.pptx +0 -0
package/LICENSE
CHANGED
|
@@ -24,22 +24,61 @@ SOFTWARE.
|
|
|
24
24
|
|
|
25
25
|
## Third-Party Content Licenses
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
27
|
+
Parts of this package are not ours, and are bundled under their own licenses (NOT under MIT).
|
|
28
|
+
This section is the index of them. It is checked against the tree on every build by
|
|
29
|
+
`scripts/check_third_party_index.py` — a license file that describes a package other than the one
|
|
30
|
+
you downloaded is worse than no license file at all.
|
|
31
|
+
|
|
32
|
+
### Reporting guideline checklists — our summaries (`skills/*/references/checklists/*.md`)
|
|
33
|
+
|
|
34
|
+
These are educational summaries written in our own words, each citing its source. They do not
|
|
35
|
+
relicense the underlying guideline.
|
|
36
|
+
|
|
37
|
+
- STROBE: CC BY (https://www.strobe-statement.org)
|
|
38
|
+
- STARD 2015 / STARD-AI: CC BY 4.0 (https://www.equator-network.org/reporting-guidelines/stard/)
|
|
39
|
+
- TRIPOD / TRIPOD+AI: CC BY 4.0 (https://www.tripod-statement.org)
|
|
40
|
+
- PRISMA 2020 / PRISMA-DTA: CC BY (https://www.prisma-statement.org)
|
|
41
|
+
- ARRIVE 2.0: CC0 (https://arriveguidelines.org)
|
|
42
|
+
- CONSORT 2025, SPIRIT 2025: CC BY 4.0 (https://www.consort-spirit.org)
|
|
43
|
+
|
|
44
|
+
**Non-commercial restrictions.** The following summaries derive from instruments whose materials are
|
|
45
|
+
CC BY-NC, and are therefore free to use and redistribute **for non-commercial purposes with
|
|
46
|
+
attribution**; commercial use requires permission from the rights holders:
|
|
47
|
+
|
|
48
|
+
- CARE, MI-CLEAR-LLM, DECIDE-AI: CC BY-NC 4.0
|
|
49
|
+
- CLAIM 2024: educational summary of an RSNA open-access checklist (© RSNA)
|
|
50
|
+
|
|
51
|
+
See `skills/check-reporting/references/LICENSES.md` for the per-file detail.
|
|
52
|
+
|
|
53
|
+
### Official guideline templates, verbatim (`skills/make-figures/templates/official/`)
|
|
54
|
+
|
|
55
|
+
These are the guideline authors' own documents, redistributed unmodified:
|
|
56
|
+
|
|
57
|
+
- CONSORT 2025 checklist + flow diagram (`.docx`): CC BY 4.0.
|
|
58
|
+
© 2025 Hopewell S et al. *BMJ* 2025;388:e081123. doi:10.1136/bmj-2024-081123
|
|
59
|
+
- SPIRIT 2025 checklist + participant timeline (`.docx`): CC BY 4.0.
|
|
60
|
+
© 2025 Chan A-W et al. *BMJ* 2025;389:e081477. doi:10.1136/bmj-2024-081477
|
|
61
|
+
- STARD 2015 checklist (`.docx`) and flow diagram (`.pdf`): CC BY 4.0.
|
|
62
|
+
Bossuyt PM et al. *BMJ* 2015;351:h5527
|
|
63
|
+
- PRISMA 2020 flow diagrams (`.pptx`): built by us with
|
|
64
|
+
`skills/make-figures/scripts/build_prisma2020_template.py`, reproducing the published layout
|
|
65
|
+
(Page MJ et al. *BMJ* 2021;372:n71, Fig 1, CC BY 4.0)
|
|
66
|
+
|
|
67
|
+
The CONSORT and SPIRIT **2025** updates are CC BY 4.0 — "unrestricted use, distribution, and
|
|
68
|
+
reproduction in any medium, provided the original work is properly cited". Their 2010 / 2013
|
|
69
|
+
predecessors were CC BY-NC / CC BY-NC-ND and could not have been bundled; this file said so for a
|
|
70
|
+
long time after the 2025 versions had replaced them.
|
|
71
|
+
|
|
72
|
+
### Citation styles (`skills/manage-refs/citation_styles/*.csl`)
|
|
73
|
+
|
|
74
|
+
15 CSL files from the Citation Style Language project: **CC BY-SA 3.0**, unmodified, each retaining
|
|
75
|
+
its own `<rights>` element. See `skills/manage-refs/NOTICE.md`.
|
|
76
|
+
|
|
77
|
+
### Not bundled — download these yourself
|
|
78
|
+
|
|
79
|
+
- **European Radiology graphical-abstract template** (`EURA-GA-Jan2025.pptx`):
|
|
80
|
+
https://www.springer.com/journal/330 — the journal's own file, with no license permitting
|
|
81
|
+
redistribution. `/make-figures` accepts it via `--template /absolute/path.pptx`.
|
|
43
82
|
|
|
44
83
|
## Optional Dependencies
|
|
45
84
|
|
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# MedSci Skills
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**56 skills that actually work.** Built by a physician-researcher, tested on real publications.
|
|
6
6
|
|
|
7
7
|
*MedSci Skills is an end-to-end research tool for physician and medical-engineering researchers — design → scaffold → validate → publish — for the clinical manuscript and the medical-AI model behind it. Its moat is the compliance layer — 46 reporting guidelines and risk-of-bias tools, reference/citation verification, and deterministic integrity gates before peer review — now extended by a model-engineering lane that scaffolds reproducible, leakage-safe training repos and audits model validation. Clinical AI model research engineering is in scope; a general AI-scientist platform is not. It competes on clinical submission reliability, not skill count.*
|
|
8
8
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[](https://github.com/Aperivue/medsci-skills/releases/latest)
|
|
11
11
|
[](https://github.com/Aperivue/medsci-skills/actions/workflows/validate.yml)
|
|
12
|
-

|
|
13
13
|
[](https://www.npmjs.com/package/medsci-skills)
|
|
14
14
|
[](https://www.npmjs.com/package/medsci-skills)
|
|
15
15
|
[](https://youtu.be/MclQ_RIofpE)
|
|
@@ -288,6 +288,8 @@ The E2E pipeline (`orchestrate --e2e`) produces everything up to `qc/`. The `sub
|
|
|
288
288
|
|
|
289
289
|
## What's New
|
|
290
290
|
|
|
291
|
+
**v5.21** — verification-layer batch, mostly promoted from real submission failures. A **marked (tracked-changes) manuscript** is now built by driving Word's Compare from the command line and proved by a **round trip** — accepting every revision must reproduce the revised paper exactly, rejecting every revision must reproduce the original — and the gate is **move-aware**, because Word encodes a relocated paragraph as `w:moveFrom`/`w:moveTo` and an insert-and-delete-only verifier calls a good file corrupt. Every detector's `qc/*.json` now **names the detector that wrote it**, so an artifact can be traced back to the check that produced it (a CI-enforced contract). Two `/verify-refs` precision defects: a Unicode hyphen in a surname fired `MISMATCH` — the verdict that means *fabricated author* — on a correct reference, and a Better BibTeX brace-protected surname was read as a corporate author, **silently skipping** the author check the tool exists to perform. New gates: publisher markup in a `.bib` title (`<scp>WHO</scp>` renders as garbage and no gate looked at the printed title), **complete/quasi separation** before a logistic model is fitted (a pathognomonic sign has an empty cell by construction; `glm` returns OR = 0.00, *p* = 0.99, and an AUC that gets reported), and a probe + gate for manuscripts claiming a system **improved itself** (which rung of the verification hierarchy said so?). `/find-cohort-gap` now accepts a **local codebook** — an institutional registry or EMR export, not only a named public cohort — enumerating variables verbatim with provenance rather than letting a model summarise them. Additive and backward-compatible; **55 skills / 46 guidelines / 61 integrity detectors / 23 domain-probe modules**. (See the [CHANGELOG](CHANGELOG.md) for v5.0–v5.20.)
|
|
292
|
+
|
|
291
293
|
**v5.20.1** — audit-driven coherence fixes. A real `/orchestrate --e2e` state-transition bug (the pipeline halted at step 3 because it required a DOCX that is only rendered at step 7), all 55 skills made routable from the single entry point with a CI reachability gate, and a README plugin-count that had drifted from the marketplace SSOT (now gated). No skill/detector change.
|
|
292
294
|
|
|
293
295
|
**v5.20** — reviewer-arithmetic gates. Five deterministic `self-review` detectors promoted from a peer-review cycle, each recomputing what a manuscript already prints: `check_table_percentages` (an `n (%)` cell vs its column denominator), `check_nested_group_comparison` (a P value comparing an analysed subset against the parent cohort that **contains** it — nested, invalid), `check_reported_p_from_counts` (rebuilds each 2×2 row and recomputes Fisher / Pearson χ² ± Yates in **pure stdlib**, calibrating the family on rows that reproduce), `check_dta_denominators` (sensitivity/specificity denominators vs the reference-standard category counts, behind a matching grand total), and `check_paired_difference_estimator` (an odd-n integer-scale median cannot be non-integer; a zero-width CI; an unnamed estimator). Plus `/peer-review` request-type discipline (disclosure vs computation) and impossibility-claim verification. Additive and backward-compatible; **55 skills / 46 guidelines / 57 integrity detectors / 22 domain-probe modules**. (See the [CHANGELOG](CHANGELOG.md) for v5.0–v5.19.)
|
|
@@ -714,9 +716,33 @@ Prints a checklist showing which components are present, which are missing, and
|
|
|
714
716
|
|
|
715
717
|
## Requirements
|
|
716
718
|
|
|
719
|
+
**Python 3.9+ and an agent host. That is the whole hard requirement.** Every integrity detector is
|
|
720
|
+
stdlib-only, and so is drafting, reviewing, and auditing a manuscript. If you have no Python, the
|
|
721
|
+
double-click installer will offer to install it for you (`winget` on Windows, or the official
|
|
722
|
+
download page) rather than leaving you at a dead end.
|
|
723
|
+
|
|
717
724
|
- An [Agent Skills](https://agentskills.io)-compatible host — [Claude Code](https://claude.ai/code) (primary), or Codex / Cursor / GitHub Copilot (see [`docs/host_compatibility.md`](docs/host_compatibility.md); some live-data workflows rely on Claude MCP servers)
|
|
718
|
-
- Python 3.9+ (
|
|
719
|
-
|
|
725
|
+
- Python 3.9+ — the floor is CI-enforced (`scripts/check_python_floor.py`). Newer is better; if you are installing Python today, take the latest.
|
|
726
|
+
|
|
727
|
+
Everything else is needed by *some* skills and not others. Rather than a shopping list of packages
|
|
728
|
+
you have never heard of, ask the toolkit what **this** computer can do:
|
|
729
|
+
|
|
730
|
+
```bash
|
|
731
|
+
python3 installers/doctor.py # what works, what does not, and the exact fix for each
|
|
732
|
+
python3 installers/doctor.py --fix # offers to install what is missing — asking before each one
|
|
733
|
+
```
|
|
734
|
+
(Or double-click `installers/check-setup-macos.command` / `installers/check-setup-windows.cmd`.)
|
|
735
|
+
|
|
736
|
+
It reports in terms of what you were trying to *do* — "turn your manuscript into a journal-formatted
|
|
737
|
+
Word file" needs **pandoc**; "read and QC submission PDFs" needs **poppler**; "open a .docx at all"
|
|
738
|
+
needs **python-docx** — and installs the small things on request. Large things (a TeX distribution,
|
|
739
|
+
R, PyTorch) are never installed for you: it prints the size and the command and leaves the choice
|
|
740
|
+
alone.
|
|
741
|
+
|
|
742
|
+
**R is not required.** `/analyze-stats` writes Python by default and only emits R if you ask it to;
|
|
743
|
+
the toolkit itself never executes R. Install R (with `meta`, `metafor`, `mada` for meta-analysis)
|
|
744
|
+
only if you want to run the R code it writes for you. The same is true of PyTorch and
|
|
745
|
+
`/model-scaffold`: writing the training code needs nothing; running it needs torch.
|
|
720
746
|
|
|
721
747
|
## Use Cases
|
|
722
748
|
|
|
@@ -786,8 +812,32 @@ Or equivalently: `/write-paper --autonomous` if analysis and figures already exi
|
|
|
786
812
|
|
|
787
813
|
## Contributing
|
|
788
814
|
|
|
789
|
-
|
|
790
|
-
|
|
815
|
+
**If you have already changed something on your own machine, `/contribute` will send it for you.**
|
|
816
|
+
Most people who use this are clinicians, not git users: they add the journal they publish in, fix a
|
|
817
|
+
checklist item that was wrong for their specialty, adapt a skill to their department — and the
|
|
818
|
+
change dies on one laptop.
|
|
819
|
+
|
|
820
|
+
```
|
|
821
|
+
/contribute
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
It compares your installed skills against what was shipped, tells you exactly what you changed,
|
|
825
|
+
**scans it for patient data, hospital names, IRB numbers and manuscript IDs** (that scan blocks, and
|
|
826
|
+
it is the reason this is a skill and not a button), shows you every line that would leave your
|
|
827
|
+
machine, and only then opens the pull request — you never type a git command. No GitHub CLI? It
|
|
828
|
+
reaches the project as an issue instead.
|
|
829
|
+
|
|
830
|
+
The same skill files a **false positive** ("this flagged my paper and it was wrong") or a failed
|
|
831
|
+
step. That is not a lesser contribution: it is the only evidence anyone has of how a detector
|
|
832
|
+
behaves on a real manuscript rather than a synthetic fixture.
|
|
833
|
+
|
|
834
|
+
**You will not be nagged.** Reminders are off by default; the installer mentions the option once
|
|
835
|
+
and then never again. `/contribute` only ever runs when you run it.
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
Contributions are also welcome the ordinary way — and most are **one small, self-contained file**
|
|
840
|
+
that a template walks you through. You do not need to understand the whole pipeline to add value.
|
|
791
841
|
Pick a [**good first issue**](https://github.com/Aperivue/medsci-skills/contribute), or start
|
|
792
842
|
from one of these:
|
|
793
843
|
|
|
@@ -823,6 +873,24 @@ Community members who have reported, verified, or shaped the project are credite
|
|
|
823
873
|
|
|
824
874
|
## In the Wild
|
|
825
875
|
|
|
876
|
+
### Cited in the literature
|
|
877
|
+
|
|
878
|
+
**Chen, Wang & Qu (2026), *Recursive Self-Improvement in AI*** — [arXiv:2607.07663](https://arxiv.org/abs/2607.07663), a
|
|
879
|
+
survey of **1,250 papers** — cites this project's methods paper
|
|
880
|
+
([arXiv:2606.09500](https://arxiv.org/abs/2606.09500)) twice, as the reference for what fails when an AI
|
|
881
|
+
audits its own scientific output, and names the approach taken here as the response:
|
|
882
|
+
|
|
883
|
+
> "In regulated domains, **deterministic integrity gates are interposed** because 'self-critique
|
|
884
|
+
> inherits the blind spots that produce confident fabrication'."
|
|
885
|
+
> — §6.3 (quoting the methods paper; also cited in §5.3, on the self-confirming loop)
|
|
886
|
+
|
|
887
|
+
That is the argument this repository is built on: a model asked to check its own work inherits the
|
|
888
|
+
blind spots that produced the error, and a script that **recomputes** the number does not.
|
|
889
|
+
|
|
890
|
+
Every citation we know of is logged in [`docs/citations.md`](docs/citations.md).
|
|
891
|
+
|
|
892
|
+
### Adoption
|
|
893
|
+
|
|
826
894
|
Adoption is tracked openly in [`IMPACT.md`](IMPACT.md) (stars, forks, traffic,
|
|
827
895
|
release downloads — snapshotted weekly into [`metrics/traffic_log.csv`](metrics/traffic_log.csv))
|
|
828
896
|
and academic use is logged in [`docs/citations.md`](docs/citations.md).
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# MedSci Skills — double-click installer for macOS.
|
|
3
|
+
#
|
|
4
|
+
# The person running this is a clinician who double-clicked a file. Every failure must end in a
|
|
5
|
+
# sentence they can act on — never a Python traceback, and never a silent "done" that installed
|
|
6
|
+
# nothing. Two things had to change for that to be true:
|
|
7
|
+
#
|
|
8
|
+
# * `python` is not necessarily Python 3. Handing the installer to whatever `command -v python`
|
|
9
|
+
# finds could hand it to a Python 2 that dies on the first f-string.
|
|
10
|
+
# * Python 3.8 is worse than Python 2, not better: install.py *parses* there, so instead of a
|
|
11
|
+
# clean "wrong version" it dies halfway through with a wall of traceback.
|
|
12
|
+
#
|
|
13
|
+
# So the interpreter is checked for a real version BEFORE anything runs.
|
|
2
14
|
set -u
|
|
3
15
|
|
|
4
16
|
cd "$(dirname "$0")/.."
|
|
@@ -6,23 +18,82 @@ cd "$(dirname "$0")/.."
|
|
|
6
18
|
echo "MedSci Skills Installer for macOS"
|
|
7
19
|
echo
|
|
8
20
|
|
|
21
|
+
MIN_MAJOR=3
|
|
22
|
+
MIN_MINOR=9
|
|
23
|
+
|
|
24
|
+
usable() { # true only if $1 is really Python >= 3.9
|
|
25
|
+
command -v "$1" >/dev/null 2>&1 || return 1
|
|
26
|
+
"$1" -c "import sys; raise SystemExit(0 if sys.version_info >= ($MIN_MAJOR, $MIN_MINOR) else 1)" \
|
|
27
|
+
>/dev/null 2>&1
|
|
28
|
+
}
|
|
29
|
+
|
|
9
30
|
PY=""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
31
|
+
for candidate in python3 python; do
|
|
32
|
+
if usable "$candidate"; then
|
|
33
|
+
PY="$candidate"
|
|
34
|
+
break
|
|
35
|
+
fi
|
|
36
|
+
done
|
|
37
|
+
|
|
38
|
+
if [ -z "$PY" ]; then
|
|
39
|
+
# "No Python" and "too old a Python" need different actions, and a clinician cannot be expected
|
|
40
|
+
# to work out which one they have from a version string.
|
|
41
|
+
if command -v python3 >/dev/null 2>&1 || command -v python >/dev/null 2>&1; then
|
|
42
|
+
# No pipe to `head` — an installer must not depend on tools that may not be on PATH in a
|
|
43
|
+
# stripped-down environment. `python --version` prints one line anyway.
|
|
44
|
+
FOUND="$( python3 --version 2>/dev/null || python --version 2>/dev/null )"
|
|
45
|
+
echo "The Python on this Mac is too old for MedSci Skills."
|
|
46
|
+
echo " Found: ${FOUND:-an older version}"
|
|
47
|
+
echo " Needed: Python ${MIN_MAJOR}.${MIN_MINOR} or newer"
|
|
48
|
+
else
|
|
49
|
+
echo "Python was not found on this Mac."
|
|
50
|
+
fi
|
|
51
|
+
echo
|
|
52
|
+
echo "MedSci Skills is written in Python, so Python has to be on the computer first."
|
|
53
|
+
echo "It is free, official, and takes about two minutes."
|
|
54
|
+
echo
|
|
55
|
+
echo " 1. Download the latest Python for macOS (the page opens by itself in a moment)"
|
|
56
|
+
echo " 2. Open the file you downloaded and click through the installer"
|
|
57
|
+
echo " 3. Double-click THIS installer again"
|
|
58
|
+
echo
|
|
59
|
+
echo "Nothing has been changed on your computer."
|
|
60
|
+
echo
|
|
61
|
+
|
|
62
|
+
# Open the page for them. Telling a clinician to "go to python.org" is a step they have to
|
|
63
|
+
# perform; opening it is a step they do not. `open` is standard on macOS, but never let a
|
|
64
|
+
# convenience break the message they still need to read.
|
|
65
|
+
if command -v open >/dev/null 2>&1; then
|
|
66
|
+
read -r -p "Press Enter to open the Python download page..."
|
|
67
|
+
open "https://www.python.org/downloads/" >/dev/null 2>&1 || \
|
|
68
|
+
echo "Could not open the page. It is at: https://www.python.org/downloads/"
|
|
69
|
+
else
|
|
70
|
+
echo "The download page is at: https://www.python.org/downloads/"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo
|
|
74
|
+
read -r -p "Press Enter to close..."
|
|
75
|
+
exit 1
|
|
14
76
|
fi
|
|
15
77
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
echo
|
|
24
|
-
echo "
|
|
78
|
+
"$PY" installers/install.py --target all --desktop-launcher
|
|
79
|
+
STATUS=$?
|
|
80
|
+
|
|
81
|
+
if [ "$STATUS" -ne 0 ]; then
|
|
82
|
+
echo
|
|
83
|
+
echo "The installation did not finish (it stopped with error $STATUS)."
|
|
84
|
+
echo "Nothing was left half-installed — the installer undoes its own work if it cannot complete."
|
|
85
|
+
echo
|
|
86
|
+
echo "If you tell us what the message above said, we can fix it:"
|
|
87
|
+
echo " https://github.com/Aperivue/medsci-skills/issues/new"
|
|
88
|
+
echo
|
|
89
|
+
read -r -p "Press Enter to close..."
|
|
90
|
+
exit "$STATUS"
|
|
25
91
|
fi
|
|
26
92
|
|
|
93
|
+
# Turn on the in-app "update available" reminder for this turnkey install, so you are told when a
|
|
94
|
+
# new version ships instead of staying on this one forever. Best-effort: a failure here is not a
|
|
95
|
+
# failed install. Turn it off later with `install.py --disable-update-notify`.
|
|
96
|
+
"$PY" installers/install.py --enable-update-notify >/dev/null 2>&1 || true
|
|
97
|
+
|
|
27
98
|
echo
|
|
28
99
|
read -r -p "Press Enter to close..."
|
|
@@ -1,30 +1,112 @@
|
|
|
1
1
|
@echo off
|
|
2
|
-
|
|
2
|
+
rem MedSci Skills - double-click installer for Windows.
|
|
3
|
+
rem
|
|
4
|
+
rem Windows is where most of these downloads go, and it has a trap the old script fell into.
|
|
5
|
+
rem
|
|
6
|
+
rem THE MICROSOFT STORE STUB. On a Windows machine with no Python, `python` still EXISTS: it is an
|
|
7
|
+
rem App Execution Alias that opens the Microsoft Store. So `where python` succeeds, errorlevel is
|
|
8
|
+
rem 0, the script cheerfully runs it -- and a Store page opens, nothing is installed, and the
|
|
9
|
+
rem installer reports it is done. The clinician is told everything worked and has nothing.
|
|
10
|
+
rem
|
|
11
|
+
rem A TOO-OLD PYTHON. install.py PARSES on 3.8, so it does not fail cleanly: it dies partway
|
|
12
|
+
rem through with a traceback instead of saying "wrong version".
|
|
13
|
+
rem
|
|
14
|
+
rem So an interpreter is only accepted after it proves, BY RUNNING, that it is Python 3.9 or newer.
|
|
15
|
+
rem Asking `where` only proves a name exists.
|
|
16
|
+
setlocal EnableDelayedExpansion
|
|
3
17
|
cd /d "%~dp0\.."
|
|
4
18
|
|
|
5
19
|
echo MedSci Skills Installer for Windows
|
|
6
20
|
echo.
|
|
7
21
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
set "PY="
|
|
23
|
+
|
|
24
|
+
rem The py launcher is the reliable path when it exists.
|
|
25
|
+
py -3 -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 9) else 1)" >nul 2>nul
|
|
26
|
+
if !errorlevel!==0 set "PY=py -3"
|
|
27
|
+
|
|
28
|
+
rem Fall back to `python` -- but only if it actually runs. The Store stub fails this test, which is
|
|
29
|
+
rem the entire reason for testing instead of asking `where`.
|
|
30
|
+
if not defined PY (
|
|
31
|
+
python -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 9) else 1)" >nul 2>nul
|
|
32
|
+
if !errorlevel!==0 set "PY=python"
|
|
14
33
|
)
|
|
15
34
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
if not defined PY (
|
|
36
|
+
echo Python 3.9 or newer was not found on this computer.
|
|
37
|
+
echo.
|
|
38
|
+
echo MedSci Skills is written in Python, so Python has to be installed first.
|
|
39
|
+
echo It is free, official, and takes a couple of minutes.
|
|
40
|
+
echo.
|
|
41
|
+
|
|
42
|
+
rem winget ships with Windows 10 ^(1809+^) and Windows 11, so on most hospital machines Python can
|
|
43
|
+
rem simply be installed for the user -- no admin rights, no download page, no PATH checkbox to
|
|
44
|
+
rem miss. We ASK first: installing software on someone's computer without asking is not ours to do.
|
|
45
|
+
where winget >nul 2>nul
|
|
46
|
+
if !errorlevel!==0 (
|
|
47
|
+
echo This installer can install Python for you now, from the official Python
|
|
48
|
+
echo repository, into your own user account ^(no administrator password needed^).
|
|
49
|
+
echo.
|
|
50
|
+
set /p "DOIT=Install Python now? [Y/N] "
|
|
51
|
+
if /i "!DOIT!"=="Y" (
|
|
52
|
+
echo.
|
|
53
|
+
echo Installing Python. This takes a couple of minutes...
|
|
54
|
+
echo.
|
|
55
|
+
winget install --exact --id Python.Python.3.13 --scope user ^
|
|
56
|
+
--accept-source-agreements --accept-package-agreements
|
|
57
|
+
echo.
|
|
58
|
+
if !errorlevel!==0 (
|
|
59
|
+
echo Python is installed.
|
|
60
|
+
echo.
|
|
61
|
+
echo Windows only notices a new program in NEW windows, so this one cannot use it yet:
|
|
62
|
+
echo.
|
|
63
|
+
echo ^>^> Close this window and double-click the installer again. ^<^<
|
|
64
|
+
echo.
|
|
65
|
+
echo Nothing else has been changed on your computer.
|
|
66
|
+
echo.
|
|
67
|
+
pause
|
|
68
|
+
exit /b 1
|
|
69
|
+
)
|
|
70
|
+
echo Python could not be installed automatically ^(error !errorlevel!^).
|
|
71
|
+
echo Use the download page instead - it is opening now.
|
|
72
|
+
echo.
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
echo 1. Download the latest Python for Windows ^(the page opens by itself in a moment^)
|
|
77
|
+
echo 2. Run the file you downloaded.
|
|
78
|
+
echo IMPORTANT: on the FIRST screen, tick the box "Add python.exe to PATH"
|
|
79
|
+
echo before pressing Install. It is easy to miss, and nothing works without it.
|
|
80
|
+
echo 3. Double-click THIS installer again.
|
|
81
|
+
echo.
|
|
82
|
+
echo If a Microsoft Store page opened when you tried Python before, that page is a
|
|
83
|
+
echo placeholder, not Python. Use python.org instead.
|
|
84
|
+
echo.
|
|
85
|
+
echo Nothing has been changed on your computer.
|
|
86
|
+
echo.
|
|
87
|
+
start "" "https://www.python.org/downloads/"
|
|
88
|
+
pause
|
|
89
|
+
exit /b 1
|
|
22
90
|
)
|
|
23
91
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
92
|
+
%PY% installers\install.py --target all --desktop-launcher
|
|
93
|
+
if not !errorlevel!==0 (
|
|
94
|
+
set "RC=!errorlevel!"
|
|
95
|
+
echo.
|
|
96
|
+
echo The installation did not finish ^(it stopped with error !RC!^).
|
|
97
|
+
echo Nothing was left half-installed - the installer undoes its own work if it cannot complete.
|
|
98
|
+
echo.
|
|
99
|
+
echo If you tell us what the message above said, we can fix it:
|
|
100
|
+
echo https://github.com/Aperivue/medsci-skills/issues/new
|
|
101
|
+
echo.
|
|
102
|
+
pause
|
|
103
|
+
exit /b !RC!
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
rem Turn on the in-app "update available" reminder for this turnkey install, so you are told when a
|
|
107
|
+
rem new version ships instead of staying on this one forever. Best-effort: a failure here is not a
|
|
108
|
+
rem failed install.
|
|
109
|
+
%PY% installers\install.py --enable-update-notify >nul 2>nul
|
|
27
110
|
|
|
28
|
-
:done
|
|
29
111
|
echo.
|
|
30
112
|
pause
|
package/installers/install.py
CHANGED
|
@@ -15,6 +15,20 @@ import os
|
|
|
15
15
|
import sys
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
|
+
# The floor the README promises. Checked here as well as in the double-click installers, because
|
|
19
|
+
# the failure it prevents is a clinician staring at a Python traceback: on 3.8 this file *parses*
|
|
20
|
+
# (so there is no clean syntax error to explain itself) and then dies somewhere in the middle,
|
|
21
|
+
# leaving a half-explained wall of text and no idea what to do next.
|
|
22
|
+
MIN_PYTHON = (3, 9)
|
|
23
|
+
if sys.version_info < MIN_PYTHON:
|
|
24
|
+
have = ".".join(str(n) for n in sys.version_info[:3])
|
|
25
|
+
sys.exit(
|
|
26
|
+
f"\nMedSci Skills needs Python {MIN_PYTHON[0]}.{MIN_PYTHON[1]} or newer. This computer has {have}.\n\n"
|
|
27
|
+
" Install the latest Python from https://www.python.org/downloads/\n"
|
|
28
|
+
" then run this installer again.\n\n"
|
|
29
|
+
"Nothing has been changed on your computer.\n"
|
|
30
|
+
)
|
|
31
|
+
|
|
18
32
|
sys.path.insert(0, str(Path(__file__).resolve().parent)) # allow `import medsci_txn` when run as a script
|
|
19
33
|
import medsci_txn # noqa: E402
|
|
20
34
|
|
|
@@ -231,6 +245,47 @@ def parse_args() -> argparse.Namespace:
|
|
|
231
245
|
return parser.parse_args()
|
|
232
246
|
|
|
233
247
|
|
|
248
|
+
|
|
249
|
+
def _offer_contribution_reminders_once(log_lines) -> None:
|
|
250
|
+
"""Mention — once, ever — that the option exists. Then never again, whatever they do.
|
|
251
|
+
|
|
252
|
+
A person who ignores the question has answered it. Asking a second time would be the exact
|
|
253
|
+
nagging this setting exists to prevent, so `asked_once` is recorded whether or not they act.
|
|
254
|
+
"""
|
|
255
|
+
import json as _json
|
|
256
|
+
home = medsci_txn.state_home()
|
|
257
|
+
cfg_path = home / "config.json"
|
|
258
|
+
try:
|
|
259
|
+
cfg = _json.loads(cfg_path.read_text(encoding="utf-8")) if cfg_path.is_file() else {}
|
|
260
|
+
except (ValueError, OSError):
|
|
261
|
+
cfg = {}
|
|
262
|
+
if cfg.get("asked_once"):
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
log(
|
|
266
|
+
"\nTwo things, once — then we will not bring either up again:\n"
|
|
267
|
+
"\n"
|
|
268
|
+
" 1. If you ever adapt a skill (add your journal, fix something wrong for your specialty),\n"
|
|
269
|
+
" that change can be offered back with /contribute. Reminders are OFF by default, and\n"
|
|
270
|
+
" nothing is ever sent without a patient-data scan and your confirmation on every line.\n"
|
|
271
|
+
" python3 ~/.claude/skills/contribute/scripts/contribution_prefs.py --on\n"
|
|
272
|
+
"\n"
|
|
273
|
+
" 2. If this ends up saving you time, the way to say so is a star on the repository.\n"
|
|
274
|
+
" Not applause — it is how the next researcher with your problem finds it, and for\n"
|
|
275
|
+
" software with no DOI in anyone's reference list it is the closest thing to a citation.\n"
|
|
276
|
+
" Most people who write to say thanks have never done it, because nobody told them.\n"
|
|
277
|
+
" One command, no browser:\n"
|
|
278
|
+
" python3 ~/.claude/skills/contribute/scripts/star_repo.py --now\n"
|
|
279
|
+
" (or one click: https://github.com/Aperivue/medsci-skills)",
|
|
280
|
+
log_lines,
|
|
281
|
+
)
|
|
282
|
+
cfg["asked_once"] = True
|
|
283
|
+
try:
|
|
284
|
+
cfg_path.parent.mkdir(parents=True, exist_ok=True)
|
|
285
|
+
medsci_txn.atomic_write_json(cfg_path, cfg)
|
|
286
|
+
except OSError:
|
|
287
|
+
pass # never fail an install over a preference file
|
|
288
|
+
|
|
234
289
|
def main() -> int:
|
|
235
290
|
args = parse_args()
|
|
236
291
|
if args.self_test:
|
|
@@ -319,6 +374,23 @@ def main() -> int:
|
|
|
319
374
|
print(f"\nInstall log: {log_path}")
|
|
320
375
|
return 1
|
|
321
376
|
|
|
377
|
+
# Say what ELSE this computer needs, while they are still looking at the screen.
|
|
378
|
+
#
|
|
379
|
+
# Every integrity detector is stdlib-only, so the install above is enough to use most of the
|
|
380
|
+
# toolkit. But a few skills need a program we do not ship — pandoc to render a manuscript into
|
|
381
|
+
# a journal-formatted Word file, poppler to read a submission PDF. Those skills already fail
|
|
382
|
+
# politely; the problem is that they fail *later*, in the middle of the work, to someone who
|
|
383
|
+
# will not go and install a package manager at that moment. Tell them now, when the answer is
|
|
384
|
+
# one command. Read-only, asks nothing, installs nothing, and can never fail the install.
|
|
385
|
+
if not args.dry_run:
|
|
386
|
+
try:
|
|
387
|
+
import doctor # noqa: PLC0415
|
|
388
|
+
|
|
389
|
+
doctor.brief_summary(lambda m: log(m, log_lines))
|
|
390
|
+
except Exception: # noqa: BLE001 - a setup *check* must never break an install that worked
|
|
391
|
+
pass
|
|
392
|
+
|
|
393
|
+
_offer_contribution_reminders_once(log_lines)
|
|
322
394
|
log("\nDone. Restart Claude Code, Codex, or Cursor before testing the skills.", log_lines)
|
|
323
395
|
log("First test prompt:", log_lines)
|
|
324
396
|
log("MedSci Skills가 설치됐는지 확인하고, 오늘 실습에 쓸 대표 스킬 5개만 보여줘.", log_lines)
|
package/installers/medsci_txn.py
CHANGED
|
@@ -38,6 +38,7 @@ so staging + holding dirs are kept on the destination's filesystem.
|
|
|
38
38
|
|
|
39
39
|
from __future__ import annotations
|
|
40
40
|
|
|
41
|
+
import datetime
|
|
41
42
|
import hashlib
|
|
42
43
|
import json
|
|
43
44
|
import os
|
|
@@ -144,6 +145,50 @@ def permanent_backup(skill_path: Path, target: str, home: Path, reason: str, log
|
|
|
144
145
|
return dest
|
|
145
146
|
|
|
146
147
|
|
|
148
|
+
|
|
149
|
+
# ------------------------------------------------------- contribution reminders (opt-in, off)
|
|
150
|
+
|
|
151
|
+
def _contrib_config(home: Path) -> Path:
|
|
152
|
+
return home / "config.json"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _contribution_reminder_wanted(home: Path) -> bool:
|
|
156
|
+
"""Off unless the user explicitly asked to be reminded, and then at most monthly.
|
|
157
|
+
|
|
158
|
+
An installer that nags is an installer people stop running, and this audience already
|
|
159
|
+
under-updates. Defaulting to silence costs a few contributions; defaulting to noise costs
|
|
160
|
+
the update path itself.
|
|
161
|
+
"""
|
|
162
|
+
p = _contrib_config(home)
|
|
163
|
+
if not p.is_file():
|
|
164
|
+
return False
|
|
165
|
+
try:
|
|
166
|
+
cfg = json.loads(p.read_text(encoding="utf-8"))
|
|
167
|
+
except (ValueError, OSError):
|
|
168
|
+
return False
|
|
169
|
+
if cfg.get("contribution_reminders") != "on":
|
|
170
|
+
return False
|
|
171
|
+
last = cfg.get("last_reminded")
|
|
172
|
+
if not last:
|
|
173
|
+
return True
|
|
174
|
+
try:
|
|
175
|
+
y, m, d = (int(x) for x in str(last).split("-"))
|
|
176
|
+
return (datetime.date.today() - datetime.date(y, m, d)).days >= 30
|
|
177
|
+
except (ValueError, TypeError):
|
|
178
|
+
return True
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _mark_reminded(home: Path) -> None:
|
|
182
|
+
p = _contrib_config(home)
|
|
183
|
+
try:
|
|
184
|
+
cfg = json.loads(p.read_text(encoding="utf-8")) if p.is_file() else {}
|
|
185
|
+
cfg["last_reminded"] = datetime.date.today().isoformat()
|
|
186
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
187
|
+
atomic_write_json(p, cfg)
|
|
188
|
+
except OSError:
|
|
189
|
+
pass # a reminder is not worth failing an install over
|
|
190
|
+
|
|
191
|
+
|
|
147
192
|
# ---------------------------------------------------------------- recovery
|
|
148
193
|
|
|
149
194
|
def recover_target(target: str, home: Path, log) -> bool:
|
|
@@ -256,6 +301,25 @@ def install_target(
|
|
|
256
301
|
else:
|
|
257
302
|
permanent_backup(d, target, home, "legacy-collision", log)
|
|
258
303
|
|
|
304
|
+
# A user-modified skill is usually a clinician adapting the toolkit to their journal, their
|
|
305
|
+
# specialty, their department. The backup preserves it; nothing ever read the backup again.
|
|
306
|
+
#
|
|
307
|
+
# But the person who installed a research tool did not sign up to be asked for things, and a
|
|
308
|
+
# physician finishing a paper does not need a lecture on open-source etiquette from their
|
|
309
|
+
# installer. So this is OPT-IN and OFF BY DEFAULT: we say nothing unless they asked to be
|
|
310
|
+
# told, and even then not more than once a month. Silence is the default, and it is correct.
|
|
311
|
+
modified = [
|
|
312
|
+
name for name in owned_skills
|
|
313
|
+
if (dest / name).exists()
|
|
314
|
+
and name in prior_skills
|
|
315
|
+
and skill_inventory(dest / name) != prior_skills[name].get("inventory", {})
|
|
316
|
+
]
|
|
317
|
+
if modified and _contribution_reminder_wanted(home):
|
|
318
|
+
log(f" You had changed {len(modified)} skill(s): {', '.join(sorted(modified))}. Your version")
|
|
319
|
+
log(" is backed up (above); if the change is worth keeping, /contribute will offer it back")
|
|
320
|
+
log(" to the project — patient-data scan first, nothing sent without your confirmation.")
|
|
321
|
+
_mark_reminded(home)
|
|
322
|
+
|
|
259
323
|
prune = [n for n in prior_skills if n not in set(owned_skills)]
|
|
260
324
|
for name in prune: # a removed/renamed owned skill: back up before pruning, then remove in txn
|
|
261
325
|
d = dest / name
|