@brainpilot/skills 0.0.6 → 0.0.8

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 (285) hide show
  1. package/package.json +2 -2
  2. package/skills/01_Meta-Skills/academic-research-hub/SKILL.md +108 -0
  3. package/skills/01_Meta-Skills/academic-research-hub/scripts/requirements.txt +17 -0
  4. package/skills/01_Meta-Skills/academic-research-hub/scripts/research.py +781 -0
  5. package/skills/01_Meta-Skills/beautiful-log/SKILL.md +64 -0
  6. package/skills/01_Meta-Skills/beautiful-log/scripts/beautiful_log.py +274 -0
  7. package/skills/01_Meta-Skills/ethoclaw-daily-paper/SKILL.md +130 -0
  8. package/skills/01_Meta-Skills/ethoclaw-daily-paper/assets/config.template.yaml +54 -0
  9. package/skills/01_Meta-Skills/ethoclaw-daily-paper/assets/top5_digest_template.md +5 -0
  10. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/build_top5_digest.py +300 -0
  11. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/common.py +137 -0
  12. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/merge_results.py +106 -0
  13. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/run_pipeline.py +177 -0
  14. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/search_arxiv.py +162 -0
  15. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/search_pubmed.py +202 -0
  16. package/skills/01_Meta-Skills/ethoclaw-normalize-tabular/SKILL.md +173 -0
  17. package/skills/01_Meta-Skills/ethoclaw-normalize-tabular/scripts/normalize_data.py +874 -0
  18. package/skills/01_Meta-Skills/ethoclaw-pdf-research/SKILL.md +134 -0
  19. package/skills/01_Meta-Skills/ethoclaw-pdf-research/references/confirmation-prompts.md +31 -0
  20. package/skills/01_Meta-Skills/ethoclaw-pdf-research/references/output-patterns.md +45 -0
  21. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_markdown_deliverables.py +41 -0
  22. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_research_log.py +84 -0
  23. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_summary_md.py +63 -0
  24. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/extract_pdf_bundle.py +140 -0
  25. package/skills/01_Meta-Skills/experiment-controller/SKILL.md +140 -0
  26. package/skills/01_Meta-Skills/knowledge-graph-builder/SKILL.md +366 -0
  27. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/entity_resolution.py +120 -0
  28. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/extraction_prompt_template.txt +19 -0
  29. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/graph_query.py +106 -0
  30. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/hypothesis_cli_reference.py +42 -0
  31. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/new_data_source_template.py +116 -0
  32. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/requirements.txt +15 -0
  33. package/skills/01_Meta-Skills/method-design/SKILL.md +61 -0
  34. package/skills/01_Meta-Skills/multi-search-engine/SKILL.md +119 -0
  35. package/skills/01_Meta-Skills/research-idea/SKILL.md +65 -0
  36. package/skills/05_EEG_ERP/eeg-skill/SKILL.md +197 -0
  37. package/skills/05_EEG_ERP/meg-skill/SKILL.md +188 -0
  38. package/skills/05_EEG_ERP/meg-skill/scripts/time_frequency.py +223 -0
  39. package/skills/05_EEG_ERP/mne-eeg-tool/SKILL.md +165 -0
  40. package/skills/05_EEG_ERP/mne-eeg-tool/scripts/eeg_pipeline_reference.py +231 -0
  41. package/skills/05_EEG_ERP/seed-iv-skill/SKILL.md +184 -0
  42. package/skills/05_EEG_ERP/seed-iv-skill/scripts/classify_seed_iv.py +154 -0
  43. package/skills/05_EEG_ERP/seed-iv-skill/scripts/extract_seed_iv_features.py +190 -0
  44. package/skills/05_EEG_ERP/seed-iv-skill/scripts/validate_seed_iv.py +102 -0
  45. package/skills/05_EEG_ERP/seed-vig-skill/SKILL.md +182 -0
  46. package/skills/05_EEG_ERP/seed-vig-skill/scripts/classify_seed_vig.py +165 -0
  47. package/skills/05_EEG_ERP/seed-vig-skill/scripts/extract_seed_vig_features.py +185 -0
  48. package/skills/05_EEG_ERP/seed-vig-skill/scripts/validate_seed_vig.py +88 -0
  49. package/skills/06_fMRI_Neuroimaging/abcd-skill/SKILL.md +308 -0
  50. package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/abcd_qc_summary.py +449 -0
  51. package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/extract_abcd_phenotype.py +292 -0
  52. package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/reorganize_abcd.py +387 -0
  53. package/skills/06_fMRI_Neuroimaging/abide-skill/SKILL.md +302 -0
  54. package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/abide_qc_summary.py +317 -0
  55. package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/extract_abide_phenotype.py +267 -0
  56. package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/reorganize_abide.py +387 -0
  57. package/skills/06_fMRI_Neuroimaging/adhd200-skill/SKILL.md +244 -0
  58. package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/adhd200_qc_summary.py +98 -0
  59. package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/extract_adhd200_phenotype.py +134 -0
  60. package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/reorganize_adhd200.py +206 -0
  61. package/skills/06_fMRI_Neuroimaging/adni-skill/SKILL.md +358 -0
  62. package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/generate_adni_task_files.py +1305 -0
  63. package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/generate_vqa_from_tasks.py +766 -0
  64. package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/reorganize_adni.py +491 -0
  65. package/skills/06_fMRI_Neuroimaging/aibl-skill/SKILL.md +295 -0
  66. package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/aibl_qc_summary.py +260 -0
  67. package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/extract_aibl_phenotype.py +365 -0
  68. package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/reorganize_aibl.py +394 -0
  69. package/skills/06_fMRI_Neuroimaging/aomic-skill/SKILL.md +292 -0
  70. package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/aomic_qc_summary.py +258 -0
  71. package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/extract_aomic_phenotype.py +284 -0
  72. package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/reorganize_aomic.py +322 -0
  73. package/skills/06_fMRI_Neuroimaging/asl-skill/SKILL.md +168 -0
  74. package/skills/06_fMRI_Neuroimaging/asl-skill/scripts/compute_cbf.py +224 -0
  75. package/skills/06_fMRI_Neuroimaging/bids-organizer/SKILL.md +241 -0
  76. package/skills/06_fMRI_Neuroimaging/bold5000-skill/SKILL.md +186 -0
  77. package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/bold5000_qc_summary.py +96 -0
  78. package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/extract_bold5000_stimulus.py +125 -0
  79. package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/reorganize_bold5000.py +102 -0
  80. package/skills/06_fMRI_Neuroimaging/camcan-skill/SKILL.md +213 -0
  81. package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/camcan_qc_summary.py +131 -0
  82. package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/extract_camcan_phenotype.py +145 -0
  83. package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/validate_camcan.py +141 -0
  84. package/skills/06_fMRI_Neuroimaging/cobre-skill/SKILL.md +201 -0
  85. package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/cobre_qc_summary.py +95 -0
  86. package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/extract_cobre_phenotype.py +104 -0
  87. package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/reorganize_cobre.py +140 -0
  88. package/skills/06_fMRI_Neuroimaging/conn-tool/SKILL.md +180 -0
  89. package/skills/06_fMRI_Neuroimaging/dcm2nii/SKILL.md +189 -0
  90. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/SKILL.md +183 -0
  91. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/dmt_har_med_qc_summary.py +96 -0
  92. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/extract_dmt_har_med_phenotype.py +121 -0
  93. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/reorganize_dmt_har_med.py +125 -0
  94. package/skills/06_fMRI_Neuroimaging/dwi-skill/SKILL.md +359 -0
  95. package/skills/06_fMRI_Neuroimaging/fmri-skill/SKILL.md +371 -0
  96. package/skills/06_fMRI_Neuroimaging/fmriprep-tool/SKILL.md +228 -0
  97. package/skills/06_fMRI_Neuroimaging/freesurfer-tool/SKILL.md +286 -0
  98. package/skills/06_fMRI_Neuroimaging/freesurfer-tool/scripts/freesurfer_processor.py +145 -0
  99. package/skills/06_fMRI_Neuroimaging/fsl-tool/SKILL.md +208 -0
  100. package/skills/06_fMRI_Neuroimaging/hbn-skill/SKILL.md +271 -0
  101. package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/extract_hbn_phenotype.py +107 -0
  102. package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/hbn_qc_summary.py +96 -0
  103. package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/reorganize_hbn.py +150 -0
  104. package/skills/06_fMRI_Neuroimaging/hcpa-skill/SKILL.md +210 -0
  105. package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/extract_hcpa_phenotype.py +146 -0
  106. package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/hcpa_qc_summary.py +120 -0
  107. package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/reorganize_hcpa.py +155 -0
  108. package/skills/06_fMRI_Neuroimaging/hcpd-skill/SKILL.md +210 -0
  109. package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/extract_hcpd_phenotype.py +148 -0
  110. package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/hcpd_qc_summary.py +125 -0
  111. package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/reorganize_hcpd.py +146 -0
  112. package/skills/06_fMRI_Neuroimaging/hcpep-skill/SKILL.md +215 -0
  113. package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/extract_hcpep_phenotype.py +157 -0
  114. package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/hcpep_qc_summary.py +143 -0
  115. package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/reorganize_hcpep.py +146 -0
  116. package/skills/06_fMRI_Neuroimaging/hcppipeline-tool/SKILL.md +217 -0
  117. package/skills/06_fMRI_Neuroimaging/hcpya-skill/SKILL.md +214 -0
  118. package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/extract_hcpya_phenotype.py +190 -0
  119. package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/hcpya_qc_summary.py +152 -0
  120. package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/reorganize_hcpya.py +203 -0
  121. package/skills/06_fMRI_Neuroimaging/ixi-skill/SKILL.md +198 -0
  122. package/skills/06_fMRI_Neuroimaging/ixi-skill/scripts/ixi_qc_summary.py +137 -0
  123. package/skills/06_fMRI_Neuroimaging/ixi-skill/scripts/reorganize_ixi.py +190 -0
  124. package/skills/06_fMRI_Neuroimaging/mnd-skill/SKILL.md +191 -0
  125. package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/extract_mnd_phenotype.py +143 -0
  126. package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/mnd_qc_summary.py +120 -0
  127. package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/validate_mnd.py +107 -0
  128. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/SKILL.md +203 -0
  129. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/analyze_lesions.py +119 -0
  130. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/longitudinal_lesion.py +148 -0
  131. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/mschallenge_qc_summary.py +132 -0
  132. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/validate_mschallenge.py +116 -0
  133. package/skills/06_fMRI_Neuroimaging/nibabel-skill/SKILL.md +184 -0
  134. package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/atlas_coordinate_reference.py +61 -0
  135. package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/freesurfer_io_reference.py +34 -0
  136. package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/nifti_inspection_reference.py +35 -0
  137. package/skills/06_fMRI_Neuroimaging/nifd-skill/SKILL.md +205 -0
  138. package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/extract_nifd_phenotype.py +132 -0
  139. package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/nifd_qc_summary.py +111 -0
  140. package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/validate_nifd.py +111 -0
  141. package/skills/06_fMRI_Neuroimaging/nii2dcm/SKILL.md +143 -0
  142. package/skills/06_fMRI_Neuroimaging/nilearn-tool/SKILL.md +266 -0
  143. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/connectome_reference.py +65 -0
  144. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/denoise_timeseries_reference.py +58 -0
  145. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/hierarchical_parcellation_reference.py +53 -0
  146. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/kmeans_parcellation_reference.py +53 -0
  147. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/preprocess_bold_reference.py +76 -0
  148. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/rest_dictlearning_reference.py +56 -0
  149. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/rest_ica_reference.py +59 -0
  150. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/second_level_glm_reference.py +58 -0
  151. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/spacenet_classifier_reference.py +59 -0
  152. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/svm_classifier_reference.py +60 -0
  153. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/task_glm_reference.py +63 -0
  154. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/zalff_summary_reference.py +109 -0
  155. package/skills/06_fMRI_Neuroimaging/nsd-skill/SKILL.md +210 -0
  156. package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/extract_nsd_stimulus.py +171 -0
  157. package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/nsd_qc_summary.py +142 -0
  158. package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/validate_nsd.py +142 -0
  159. package/skills/06_fMRI_Neuroimaging/oasis-skill/SKILL.md +205 -0
  160. package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/extract_oasis_phenotype.py +126 -0
  161. package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/oasis_qc_summary.py +115 -0
  162. package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/validate_oasis.py +119 -0
  163. package/skills/06_fMRI_Neuroimaging/pet-skill/SKILL.md +173 -0
  164. package/skills/06_fMRI_Neuroimaging/pet-skill/scripts/compute_suvr.py +202 -0
  165. package/skills/06_fMRI_Neuroimaging/pnc-skill/SKILL.md +206 -0
  166. package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/extract_pnc_phenotype.py +136 -0
  167. package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/pnc_qc_summary.py +116 -0
  168. package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/validate_pnc.py +120 -0
  169. package/skills/06_fMRI_Neuroimaging/ppmi-skill/SKILL.md +209 -0
  170. package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/extract_ppmi_phenotype.py +138 -0
  171. package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/ppmi_qc_summary.py +111 -0
  172. package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/validate_ppmi.py +117 -0
  173. package/skills/06_fMRI_Neuroimaging/qsiprep-tool/SKILL.md +320 -0
  174. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/SKILL.md +215 -0
  175. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/extract_rest_mdd_phenotype.py +132 -0
  176. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/harmonize_sites.py +152 -0
  177. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/rest_mdd_qc_summary.py +124 -0
  178. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/validate_rest_mdd.py +103 -0
  179. package/skills/06_fMRI_Neuroimaging/smri-skill/SKILL.md +302 -0
  180. package/skills/06_fMRI_Neuroimaging/tcp-skill/SKILL.md +204 -0
  181. package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/extract_tcp_phenotype.py +139 -0
  182. package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/tcp_qc_summary.py +111 -0
  183. package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/validate_tcp.py +99 -0
  184. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/SKILL.md +217 -0
  185. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/extract_ucla_cnp_phenotype.py +145 -0
  186. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/ucla_cnp_qc_summary.py +111 -0
  187. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/validate_ucla_cnp.py +113 -0
  188. package/skills/06_fMRI_Neuroimaging/ukb-skill/SKILL.md +310 -0
  189. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/build_ukb_survival.py +210 -0
  190. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/extract_ukb_cases.py +308 -0
  191. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/extract_ukb_phenotype.py +232 -0
  192. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/ukb_qc_summary.py +158 -0
  193. package/skills/06_fMRI_Neuroimaging/wmh-segmentation/SKILL.md +133 -0
  194. package/skills/07_Computational_Modeling/detrending/SKILL.md +118 -0
  195. package/skills/07_Computational_Modeling/dictlearning/SKILL.md +122 -0
  196. package/skills/07_Computational_Modeling/filtering/SKILL.md +121 -0
  197. package/skills/07_Computational_Modeling/glm/SKILL.md +153 -0
  198. package/skills/07_Computational_Modeling/hierarchical/SKILL.md +121 -0
  199. package/skills/07_Computational_Modeling/ica/SKILL.md +122 -0
  200. package/skills/07_Computational_Modeling/kmeans/SKILL.md +119 -0
  201. package/skills/07_Computational_Modeling/run_models/SKILL.md +427 -0
  202. package/skills/07_Computational_Modeling/spacenet/SKILL.md +122 -0
  203. package/skills/07_Computational_Modeling/svm/SKILL.md +120 -0
  204. package/skills/08_Computational_Neuroscience/brain_gnn/SKILL.md +183 -0
  205. package/skills/08_Computational_Neuroscience/dipy-tool/SKILL.md +239 -0
  206. package/skills/08_Computational_Neuroscience/dipy-tool/scripts/dti_metrics_reference.py +70 -0
  207. package/skills/08_Computational_Neuroscience/dipy-tool/scripts/load_and_mask_reference.py +76 -0
  208. package/skills/08_Computational_Neuroscience/dipy-tool/scripts/roi_stats_reference.py +59 -0
  209. package/skills/08_Computational_Neuroscience/fm_app/SKILL.md +195 -0
  210. package/skills/08_Computational_Neuroscience/neurostorm/SKILL.md +151 -0
  211. package/skills/13_Visualization/brain-visualization/SKILL.md +191 -0
  212. package/skills/13_Visualization/brain-visualization/scripts/connectome_reference.py +108 -0
  213. package/skills/13_Visualization/brain-visualization/scripts/freesurfer_ply_reference.py +54 -0
  214. package/skills/13_Visualization/brain-visualization/scripts/zalff_summary_reference.py +116 -0
  215. package/skills/13_Visualization/ethoclaw-paper-figure-layout/SKILL.md +78 -0
  216. package/skills/13_Visualization/ethoclaw-paper-figure-layout/assets/naturecomm_figures.tex +74 -0
  217. package/skills/13_Visualization/ethoclaw-paper-figure-layout/scripts/layout_results_foldered.py +579 -0
  218. package/skills/14_Writing/overleaf-skill/SKILL.md +184 -0
  219. package/skills/14_Writing/overleaf-skill/scripts/install.sh +30 -0
  220. package/skills/14_Writing/paper-writing/SKILL.md +146 -0
  221. package/skills/14_Writing/paper-writing/scripts/data_statement_templates.py +164 -0
  222. package/skills/14_Writing/paper-writing/scripts/figure_templates.py +315 -0
  223. package/skills/14_Writing/paper-writing/scripts/nature_figure_style.py +214 -0
  224. package/skills/14_Writing/paper-writing/scripts/section_phrasebank.py +246 -0
  225. package/skills/16_Animal_Behavior/deeplabcut/SKILL.md +154 -0
  226. package/skills/16_Animal_Behavior/deeplabcut/references/3d-pose.md +89 -0
  227. package/skills/16_Animal_Behavior/deeplabcut/references/maDLC.md +123 -0
  228. package/skills/16_Animal_Behavior/deeplabcut/references/modelzoo.md +98 -0
  229. package/skills/16_Animal_Behavior/deeplabcut/references/standard-pipeline.md +165 -0
  230. package/skills/16_Animal_Behavior/deeplabcut/references/utilities.md +146 -0
  231. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/SKILL.md +274 -0
  232. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/report_template_en.html +112 -0
  233. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/report_template_en.md +21 -0
  234. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/cluster-section.md +5 -0
  235. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/heatmap-section.md +5 -0
  236. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/integrated-interpretation.md +3 -0
  237. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/overview.md +3 -0
  238. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/project-summary.md +3 -0
  239. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/radar-section.md +5 -0
  240. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/raw-trajectory.md +3 -0
  241. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/sample-check.md +3 -0
  242. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/single-subject-section.md +3 -0
  243. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/stats-section.md +5 -0
  244. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/epm.md +52 -0
  245. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/fst.md +37 -0
  246. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/nor.md +39 -0
  247. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/oft.md +43 -0
  248. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/tcst.md +45 -0
  249. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/tst.md +36 -0
  250. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/input-types.md +59 -0
  251. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/interpretation-guardrails.md +45 -0
  252. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/metadata-schema.md +57 -0
  253. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/report-sections.md +86 -0
  254. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/section-selection-rules.md +169 -0
  255. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/build_report_manifest.py +27 -0
  256. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/render_report.py +34 -0
  257. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/report_utils.py +1121 -0
  258. package/skills/16_Animal_Behavior/ethoclaw-animal-grounding/SKILL.md +390 -0
  259. package/skills/16_Animal_Behavior/ethoclaw-animal-grounding/reference_code.py +98 -0
  260. package/skills/16_Animal_Behavior/ethoclaw-animal-pose-estimation/SKILL.md +336 -0
  261. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/README.md +21 -0
  262. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/SKILL.md +41 -0
  263. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/batch_kinematic_generator.py +663 -0
  264. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/config.json +19 -0
  265. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/generate_kinematic_parameter.py +401 -0
  266. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/kinematic_generator.py +265 -0
  267. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/SKILL.md +72 -0
  268. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/references/config.example.toml +56 -0
  269. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/scripts/cluster_all_params.py +232 -0
  270. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/scripts/cluster_all_params_from_config.py +236 -0
  271. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/SKILL.md +68 -0
  272. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/references/notes.md +5 -0
  273. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/scripts/plot_h5_radar.py +513 -0
  274. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/SKILL.md +52 -0
  275. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/config.toml +81 -0
  276. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/references/stats-rule.md +18 -0
  277. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_inspect.py +79 -0
  278. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_violin_batch.py +624 -0
  279. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_violin_stats.py +438 -0
  280. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/SKILL.md +280 -0
  281. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/core_scripts/heatmap_trajectory.py +790 -0
  282. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/core_scripts/heatmap_velocity.py +855 -0
  283. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_2d.csv +101 -0
  284. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_2d.h5 +0 -0
  285. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_data_readme.md +126 -0
@@ -0,0 +1,292 @@
1
+ #!/usr/bin/env python3
2
+ """Extract and merge ABCD Study phenotype tables for downstream analysis.
3
+
4
+ Reads ABCD tab-delimited phenotype files, selects columns, aligns visits,
5
+ and optionally cross-references with imaging subject lists.
6
+ """
7
+ import argparse
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Dict, List, Optional, Set
11
+
12
+ try:
13
+ import pandas as pd
14
+ except ImportError:
15
+ print("Error: pandas is required. Install with: pip install pandas", file=sys.stderr)
16
+ sys.exit(1)
17
+
18
+ # Standard ABCD event names in temporal order
19
+ ABCD_EVENTS = [
20
+ "baselineYear1Arm1",
21
+ "1YearFollowUpYArm1",
22
+ "2YearFollowUpYArm1",
23
+ "3YearFollowUpYArm1",
24
+ "4YearFollowUpYArm1",
25
+ ]
26
+
27
+ # Default phenotype files to look for
28
+ DEFAULT_PHENOTYPE_FILES = [
29
+ "abcd_p_tab.csv",
30
+ "abcd_p_tab.tsv",
31
+ "mental_health.csv",
32
+ "mental_health.tsv",
33
+ "cbcl.csv",
34
+ "cbcl.tsv",
35
+ "ksads.csv",
36
+ "ksads.tsv",
37
+ "nihtb.csv",
38
+ "nihtb.tsv",
39
+ "abcd_imgincl01.csv",
40
+ "abcd_imgincl01.tsv",
41
+ ]
42
+
43
+
44
+ def detect_delimiter(file_path: Path) -> str:
45
+ """Detect whether file uses tab or comma delimiter."""
46
+ with open(file_path, "r", encoding="utf-8") as f:
47
+ first_line = f.readline()
48
+ if "\t" in first_line:
49
+ return "\t"
50
+ return ","
51
+
52
+
53
+ def read_phenotype_file(file_path: Path) -> Optional["pd.DataFrame"]:
54
+ """Read a single ABCD phenotype file."""
55
+ delimiter = detect_delimiter(file_path)
56
+ try:
57
+ df = pd.read_csv(file_path, sep=delimiter, low_memory=False)
58
+ return df
59
+ except Exception as e:
60
+ print(f"[WARN] Failed to read {file_path}: {e}", file=sys.stderr)
61
+ return None
62
+
63
+
64
+ def find_common_columns(dataframes: List["pd.DataFrame"]) -> List[str]:
65
+ """Find columns present in all dataframes."""
66
+ if not dataframes:
67
+ return []
68
+ common = set(dataframes[0].columns)
69
+ for df in dataframes[1:]:
70
+ common &= set(df.columns)
71
+ return sorted(common)
72
+
73
+
74
+ def merge_phenotype_tables(
75
+ phenotype_dir: Path,
76
+ columns: Optional[List[str]] = None,
77
+ events: Optional[List[str]] = None,
78
+ imaging_ids: Optional[Set[str]] = None,
79
+ drop_missing_threshold: float = 0.5,
80
+ ) -> "pd.DataFrame":
81
+ """Merge multiple ABCD phenotype tables.
82
+
83
+ Args:
84
+ phenotype_dir: Directory containing phenotype CSV/TSV files.
85
+ columns: Specific columns to select (None = all).
86
+ events: ABCD event names to include (None = all).
87
+ imaging_ids: Set of subject IDs from imaging data to filter by.
88
+ drop_missing_threshold: Drop columns with > this fraction of missing values.
89
+
90
+ Returns:
91
+ Merged DataFrame.
92
+ """
93
+ phenotype_files = []
94
+ for f in sorted(phenotype_dir.iterdir()):
95
+ if f.is_file() and f.suffix in (".csv", ".tsv"):
96
+ phenotype_files.append(f)
97
+
98
+ if not phenotype_files:
99
+ # Try default filenames
100
+ for name in DEFAULT_PHENOTYPE_FILES:
101
+ candidate = phenotype_dir / name
102
+ if candidate.exists():
103
+ phenotype_files.append(candidate)
104
+
105
+ if not phenotype_files:
106
+ print(f"[ERROR] No phenotype files found in {phenotype_dir}", file=sys.stderr)
107
+ return pd.DataFrame()
108
+
109
+ print(f"Found {len(phenotype_files)} phenotype files:")
110
+ for f in phenotype_files:
111
+ print(f" - {f.name}")
112
+
113
+ # Read all files
114
+ dataframes = []
115
+ for f in phenotype_files:
116
+ df = read_phenotype_file(f)
117
+ if df is not None and len(df) > 0:
118
+ # Standardize ID column name
119
+ if "src_subject_id" in df.columns:
120
+ df = df.rename(columns={"src_subject_id": "subject_id"})
121
+ elif "SUBJECTKEY" in df.columns:
122
+ df = df.rename(columns={"SUBJECTKEY": "subject_id"})
123
+ dataframes.append(df)
124
+
125
+ if not dataframes:
126
+ return pd.DataFrame()
127
+
128
+ # Find common columns across all tables
129
+ common_cols = find_common_columns(dataframes)
130
+ print(f"Common columns across all tables: {len(common_cols)}")
131
+
132
+ # Merge on subject_id + eventname
133
+ merge_keys = []
134
+ if "subject_id" in common_cols:
135
+ merge_keys.append("subject_id")
136
+ if "eventname" in common_cols:
137
+ merge_keys.append("eventname")
138
+
139
+ if not merge_keys:
140
+ print("[WARN] No merge keys found (subject_id/eventname). Concatenating instead.")
141
+ merged = pd.concat(dataframes, ignore_index=True)
142
+ else:
143
+ merged = dataframes[0]
144
+ for df in dataframes[1:]:
145
+ # Columns to merge (excluding merge keys and duplicates)
146
+ new_cols = [c for c in df.columns if c not in merged.columns or c in merge_keys]
147
+ df_subset = df[new_cols]
148
+ merged = pd.merge(merged, df_subset, on=merge_keys, how="outer", suffixes=("", "_dup"))
149
+
150
+ # Drop duplicate columns
151
+ dup_cols = [c for c in merged.columns if c.endswith("_dup")]
152
+ if dup_cols:
153
+ merged = merged.drop(columns=dup_cols)
154
+
155
+ # Filter by events
156
+ if events and "eventname" in merged.columns:
157
+ merged = merged[merged["eventname"].isin(events)]
158
+ print(f"Filtered to events: {events} -> {len(merged)} rows")
159
+
160
+ # Filter by imaging IDs
161
+ if imaging_ids and "subject_id" in merged.columns:
162
+ # Normalize imaging IDs for matching
163
+ normalized_imaging = set()
164
+ for sid in imaging_ids:
165
+ clean = sid.replace("sub-", "")
166
+ normalized_imaging.add(clean)
167
+ normalized_imaging.add(sid)
168
+
169
+ before_count = len(merged)
170
+ mask = merged["subject_id"].apply(
171
+ lambda x: str(x).strip() in normalized_imaging
172
+ or f"sub-{str(x).strip()}" in normalized_imaging
173
+ )
174
+ merged = merged[mask]
175
+ print(f"Filtered to imaging subjects: {before_count} -> {len(merged)} rows")
176
+
177
+ # Select specific columns
178
+ if columns:
179
+ available = [c for c in columns if c in merged.columns]
180
+ missing = [c for c in columns if c not in merged.columns]
181
+ if missing:
182
+ print(f"[WARN] Columns not found (skipped): {missing}")
183
+ if available:
184
+ merged = merged[available]
185
+
186
+ # Drop columns with too many missing values
187
+ if drop_missing_threshold < 1.0:
188
+ missing_frac = merged.isnull().mean()
189
+ cols_to_drop = missing_frac[missing_frac > drop_missing_threshold].index.tolist()
190
+ if cols_to_drop:
191
+ print(f"Dropping {len(cols_to_drop)} columns with >{drop_missing_threshold*100}% missing")
192
+ merged = merged.drop(columns=cols_to_drop)
193
+
194
+ return merged
195
+
196
+
197
+ def load_imaging_ids(imaging_ids_file: Path) -> Set[str]:
198
+ """Load subject IDs from a BIDS participants.tsv or similar file."""
199
+ ids = set()
200
+ delimiter = detect_delimiter(imaging_ids_file)
201
+ try:
202
+ df = pd.read_csv(imaging_ids_file, sep=delimiter)
203
+ id_col = None
204
+ for col_name in ["participant_id", "subject_id", "SUBJECTKEY", "src_subject_id"]:
205
+ if col_name in df.columns:
206
+ id_col = col_name
207
+ break
208
+ if id_col:
209
+ ids = set(df[id_col].astype(str).str.strip())
210
+ except Exception as e:
211
+ print(f"[WARN] Failed to read imaging IDs: {e}", file=sys.stderr)
212
+ return ids
213
+
214
+
215
+ def main() -> int:
216
+ parser = argparse.ArgumentParser(
217
+ description="Extract and merge ABCD Study phenotype tables."
218
+ )
219
+ parser.add_argument(
220
+ "--phenotype-dir",
221
+ required=True,
222
+ help="Directory containing ABCD phenotype CSV/TSV files",
223
+ )
224
+ parser.add_argument(
225
+ "--output",
226
+ required=True,
227
+ help="Output path for merged phenotype CSV",
228
+ )
229
+ parser.add_argument(
230
+ "--columns",
231
+ help="Comma-separated list of columns to select (default: all)",
232
+ )
233
+ parser.add_argument(
234
+ "--events",
235
+ help=f"Comma-separated ABCD event names to include. Options: {', '.join(ABCD_EVENTS)}",
236
+ )
237
+ parser.add_argument(
238
+ "--imaging-ids",
239
+ help="Path to BIDS participants.tsv or file with imaging subject IDs",
240
+ )
241
+ parser.add_argument(
242
+ "--missing-threshold",
243
+ type=float,
244
+ default=0.5,
245
+ help="Drop columns with more than this fraction of missing values (default: 0.5)",
246
+ )
247
+ args = parser.parse_args()
248
+
249
+ phenotype_dir = Path(args.phenotype_dir).resolve()
250
+ if not phenotype_dir.exists() or not phenotype_dir.is_dir():
251
+ print(f"Phenotype directory does not exist: {phenotype_dir}", file=sys.stderr)
252
+ return 1
253
+
254
+ # Parse options
255
+ columns = None
256
+ if args.columns:
257
+ columns = [c.strip() for c in args.columns.split(",")]
258
+
259
+ events = None
260
+ if args.events:
261
+ events = [e.strip() for e in args.events.split(",")]
262
+
263
+ imaging_ids = None
264
+ if args.imaging_ids:
265
+ imaging_ids_path = Path(args.imaging_ids).resolve()
266
+ if imaging_ids_path.exists():
267
+ imaging_ids = load_imaging_ids(imaging_ids_path)
268
+ print(f"Loaded {len(imaging_ids)} imaging subject IDs")
269
+
270
+ # Merge phenotype tables
271
+ merged = merge_phenotype_tables(
272
+ phenotype_dir=phenotype_dir,
273
+ columns=columns,
274
+ events=events,
275
+ imaging_ids=imaging_ids,
276
+ drop_missing_threshold=args.missing_threshold,
277
+ )
278
+
279
+ if merged.empty:
280
+ print("[ERROR] No data after merging. Check input files.", file=sys.stderr)
281
+ return 1
282
+
283
+ # Write output
284
+ output_path = Path(args.output).resolve()
285
+ output_path.parent.mkdir(parents=True, exist_ok=True)
286
+ merged.to_csv(output_path, index=False)
287
+ print(f"\nWrote {len(merged)} rows x {len(merged.columns)} columns to {output_path}")
288
+ return 0
289
+
290
+
291
+ if __name__ == "__main__":
292
+ sys.exit(main())
@@ -0,0 +1,387 @@
1
+ #!/usr/bin/env python3
2
+ """Reorganize ABCD Study raw data into BIDS-compliant directory structure.
3
+
4
+ Handles NDAR-style subject IDs, ABCD event names, and multimodal routing
5
+ for T1w, T2w, dMRI, rs-fMRI, and task-fMRI.
6
+ """
7
+ import argparse
8
+ import json
9
+ import os
10
+ import re
11
+ import shutil
12
+ import sys
13
+ from collections import defaultdict
14
+ from pathlib import Path
15
+ from typing import Dict, List, Optional, Set, Tuple
16
+
17
+ # ABCD event name -> BIDS session label mapping
18
+ ABCD_SESSION_MAP = {
19
+ "baselineYear1Arm1": "baselineYear1Arm1",
20
+ "1YearFollowUpYArm1": "1YearFollowUpYArm1",
21
+ "2YearFollowUpYArm1": "2YearFollowUpYArm1",
22
+ "3YearFollowUpYArm1": "3YearFollowUpYArm1",
23
+ "4YearFollowUpYArm1": "4YearFollowUpYArm1",
24
+ }
25
+
26
+ # Modality keywords -> BIDS modality mapping
27
+ MODALITY_PATTERNS = [
28
+ (re.compile(r"T1w|t1w|T1|mprage", re.IGNORECASE), "T1w", "anat"),
29
+ (re.compile(r"T2w|t2w|T2", re.IGNORECASE), "T2w", "anat"),
30
+ (re.compile(r"dMRI|dmri|DWI|dwi|DTI|dti", re.IGNORECASE), "dwi", "dwi"),
31
+ (re.compile(r"rsfMRI|rs-fMRI|rest_bold|task-rest", re.IGNORECASE), "task-rest_bold", "func"),
32
+ (re.compile(r"task-fMRI|tfMRI|task-[a-zA-Z]+_bold", re.IGNORECASE), None, "func"), # dynamic task name
33
+ ]
34
+
35
+ SIDECAR_EXTENSIONS = [".json", ".bval", ".bvec", ".tsv"]
36
+
37
+
38
+ def normalize_ndar_id(raw_id: str) -> str:
39
+ """Convert NDAR subject ID to BIDS-compatible label.
40
+
41
+ NDAR_INV000ABC0 -> sub-NDARINV000ABC0
42
+ """
43
+ clean = raw_id.strip()
44
+ if clean.startswith("sub-"):
45
+ clean = clean[4:]
46
+ # Remove any non-alphanumeric characters except underscore
47
+ clean = re.sub(r"[^a-zA-Z0-9_]", "", clean)
48
+ return f"sub-{clean}"
49
+
50
+
51
+ def normalize_session(event_name: str) -> str:
52
+ """Convert ABCD event name to BIDS session label."""
53
+ event_name = event_name.strip()
54
+ if event_name.startswith("ses-"):
55
+ event_name = event_name[4:]
56
+ if event_name in ABCD_SESSION_MAP:
57
+ return f"ses-{ABCD_SESSION_MAP[event_name]}"
58
+ # Fallback: sanitize
59
+ clean = re.sub(r"[^a-zA-Z0-9]", "", event_name)
60
+ return f"ses-{clean}"
61
+
62
+
63
+ def detect_modality(filename: str) -> Optional[Tuple[str, str, str]]:
64
+ """Detect BIDS modality suffix and folder from filename.
65
+
66
+ Returns (bids_suffix, bids_folder, task_name) or None.
67
+ """
68
+ for pattern, suffix, folder in MODALITY_PATTERNS:
69
+ if pattern.search(filename):
70
+ # For task-fMRI, extract task name if present
71
+ if folder == "func" and suffix is None:
72
+ task_match = re.search(r"task-([a-zA-Z]+)", filename)
73
+ if task_match:
74
+ task_name = task_match.group(1)
75
+ return (f"task-{task_name}_bold", "func", task_name)
76
+ return ("task-unknown_bold", "func", "unknown")
77
+ return (suffix, folder, "")
78
+ return None
79
+
80
+
81
+ def find_nifti_files(directory: Path) -> List[Path]:
82
+ """Find all NIfTI files in a directory."""
83
+ results = []
84
+ for f in directory.rglob("*"):
85
+ if f.is_file() and (f.name.endswith(".nii") or f.name.endswith(".nii.gz")):
86
+ results.append(f)
87
+ return results
88
+
89
+
90
+ def copy_with_sidecars(src_nifti: Path, dst_dir: Path, dst_stem: str) -> List[Path]:
91
+ """Copy NIfTI file and its sidecar files to destination."""
92
+ dst_dir.mkdir(parents=True, exist_ok=True)
93
+ copied = []
94
+
95
+ # Copy main NIfTI
96
+ ext = ".nii.gz" if src_nifti.name.endswith(".nii.gz") else ".nii"
97
+ dst_nifti = dst_dir / f"{dst_stem}{ext}"
98
+ if not dst_nifti.exists():
99
+ shutil.copy2(str(src_nifti), str(dst_nifti))
100
+ copied.append(dst_nifti)
101
+
102
+ # Copy sidecars
103
+ src_stem = src_nifti.name
104
+ if src_stem.endswith(".nii.gz"):
105
+ src_stem = src_stem[:-7]
106
+ elif src_stem.endswith(".nii"):
107
+ src_stem = src_stem[:-4]
108
+
109
+ for sidecar_ext in SIDECAR_EXTENSIONS:
110
+ src_sidecar = src_nifti.parent / f"{src_stem}{sidecar_ext}"
111
+ if src_sidecar.exists():
112
+ dst_sidecar = dst_dir / f"{dst_stem}{sidecar_ext}"
113
+ if not dst_sidecar.exists():
114
+ shutil.copy2(str(src_sidecar), str(dst_sidecar))
115
+ copied.append(dst_sidecar)
116
+
117
+ return copied
118
+
119
+
120
+ def write_dataset_description(bids_root: Path, release_version: str = "5.1") -> None:
121
+ """Write BIDS dataset_description.json."""
122
+ desc = {
123
+ "Name": f"ABCD Study {release_version}",
124
+ "BIDSVersion": "1.8.0",
125
+ "DatasetType": "raw",
126
+ "GeneratedBy": [
127
+ {
128
+ "Name": "NeuroClaw abcd-skill",
129
+ "Description": "ABCD raw data reorganized to BIDS structure",
130
+ "Version": "1.0.0",
131
+ }
132
+ ],
133
+ }
134
+ desc_path = bids_root / "dataset_description.json"
135
+ with open(desc_path, "w", encoding="utf-8") as f:
136
+ json.dump(desc, f, indent=2, ensure_ascii=False)
137
+ print(f"[OK] Wrote {desc_path}")
138
+
139
+
140
+ def write_participants_tsv(bids_root: Path, subject_ids: List[str]) -> None:
141
+ """Write BIDS participants.tsv header."""
142
+ tsv_path = bids_root / "participants.tsv"
143
+ with open(tsv_path, "w", encoding="utf-8") as f:
144
+ f.write("participant_id\n")
145
+ for sid in sorted(set(subject_ids)):
146
+ f.write(f"{sid}\n")
147
+ print(f"[OK] Wrote {tsv_path} ({len(set(subject_ids))} participants)")
148
+
149
+
150
+ def detect_subject_layout(input_dir: Path) -> str:
151
+ """Detect whether input follows flat or nested ABCD layout.
152
+
153
+ Returns 'flat' if subject dirs are direct children,
154
+ 'nested' if organized as subject/session/modality,
155
+ 'ndar_package' if following NDA download package structure.
156
+ """
157
+ children = list(input_dir.iterdir())
158
+ if not children:
159
+ return "flat"
160
+
161
+ # Check for NDA package structure (collection directories)
162
+ for child in children:
163
+ if child.is_dir() and child.name.startswith("collection_"):
164
+ return "ndar_package"
165
+
166
+ # Check for subject/session/modality nesting
167
+ first_child = children[0]
168
+ if first_child.is_dir():
169
+ sub_children = list(first_child.iterdir())
170
+ if sub_children:
171
+ # If children have modality-like names, it's flat
172
+ for sc in sub_children:
173
+ if sc.is_dir() and detect_modality(sc.name):
174
+ return "flat"
175
+ # If children look like session names, it's nested
176
+ for sc in sub_children:
177
+ if sc.is_dir() and any(
178
+ k in sc.name for k in ["baseline", "Year", "FollowUp"]
179
+ ):
180
+ return "nested"
181
+
182
+ return "flat"
183
+
184
+
185
+ def process_flat_layout(
186
+ input_dir: Path,
187
+ bids_root: Path,
188
+ participants: List[str],
189
+ dry_run: bool = False,
190
+ ) -> Tuple[int, int, int]:
191
+ """Process flat ABCD layout: subject/modality/files."""
192
+ converted = 0
193
+ skipped = 0
194
+ failed = 0
195
+
196
+ subject_dirs = sorted(
197
+ [p for p in input_dir.iterdir() if p.is_dir() and not p.name.startswith(".")]
198
+ )
199
+
200
+ for subject_dir in subject_dirs:
201
+ sub_label = normalize_ndar_id(subject_dir.name)
202
+ participants.append(sub_label)
203
+
204
+ for modality_dir in sorted(subject_dir.iterdir()):
205
+ if not modality_dir.is_dir():
206
+ continue
207
+
208
+ nifti_files = find_nifti_files(modality_dir)
209
+ for nifti in nifti_files:
210
+ result = detect_modality(nifti.name)
211
+ if result is None:
212
+ # Try parent folder name
213
+ result = detect_modality(modality_dir.name)
214
+ if result is None:
215
+ print(f"[WARN] {sub_label} | cannot detect modality: {nifti.name}")
216
+ failed += 1
217
+ continue
218
+
219
+ bids_suffix, bids_folder, task_name = result
220
+
221
+ # Default session
222
+ ses_label = "ses-baselineYear1Arm1"
223
+
224
+ bids_sub_dir = bids_root / sub_label / ses_label / bids_folder
225
+ dst_stem = f"{sub_label}_{ses_label}_{bids_suffix}"
226
+
227
+ if (bids_sub_dir / f"{dst_stem}.nii").exists() or (
228
+ bids_sub_dir / f"{dst_stem}.nii.gz"
229
+ ).exists():
230
+ skipped += 1
231
+ continue
232
+
233
+ if dry_run:
234
+ print(f"[DRY] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}")
235
+ converted += 1
236
+ else:
237
+ copy_with_sidecars(nifti, bids_sub_dir, dst_stem)
238
+ print(f"[OK] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}")
239
+ converted += 1
240
+
241
+ return converted, skipped, failed
242
+
243
+
244
+ def process_nested_layout(
245
+ input_dir: Path,
246
+ bids_root: Path,
247
+ participants: List[str],
248
+ dry_run: bool = False,
249
+ ) -> Tuple[int, int, int]:
250
+ """Process nested ABCD layout: subject/session/modality/files."""
251
+ converted = 0
252
+ skipped = 0
253
+ failed = 0
254
+
255
+ subject_dirs = sorted(
256
+ [p for p in input_dir.iterdir() if p.is_dir() and not p.name.startswith(".")]
257
+ )
258
+
259
+ for subject_dir in subject_dirs:
260
+ sub_label = normalize_ndar_id(subject_dir.name)
261
+ participants.append(sub_label)
262
+
263
+ for session_dir in sorted(subject_dir.iterdir()):
264
+ if not session_dir.is_dir():
265
+ continue
266
+
267
+ ses_label = normalize_session(session_dir.name)
268
+
269
+ for modality_dir in sorted(session_dir.iterdir()):
270
+ if not modality_dir.is_dir():
271
+ continue
272
+
273
+ nifti_files = find_nifti_files(modality_dir)
274
+ if not nifti_files:
275
+ # Try detecting modality from folder name
276
+ result = detect_modality(modality_dir.name)
277
+ if result:
278
+ nifti_files = find_nifti_files(session_dir)
279
+ nifti_files = [
280
+ f
281
+ for f in nifti_files
282
+ if f.parent == modality_dir or f.parent == session_dir
283
+ ]
284
+
285
+ for nifti in nifti_files:
286
+ result = detect_modality(nifti.name)
287
+ if result is None:
288
+ result = detect_modality(modality_dir.name)
289
+ if result is None:
290
+ print(
291
+ f"[WARN] {sub_label}/{ses_label} | cannot detect modality: {nifti.name}"
292
+ )
293
+ failed += 1
294
+ continue
295
+
296
+ bids_suffix, bids_folder, task_name = result
297
+ bids_sub_dir = bids_root / sub_label / ses_label / bids_folder
298
+ dst_stem = f"{sub_label}_{ses_label}_{bids_suffix}"
299
+
300
+ if (bids_sub_dir / f"{dst_stem}.nii").exists() or (
301
+ bids_sub_dir / f"{dst_stem}.nii.gz"
302
+ ).exists():
303
+ skipped += 1
304
+ continue
305
+
306
+ if dry_run:
307
+ print(
308
+ f"[DRY] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}"
309
+ )
310
+ converted += 1
311
+ else:
312
+ copy_with_sidecars(nifti, bids_sub_dir, dst_stem)
313
+ print(
314
+ f"[OK] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}"
315
+ )
316
+ converted += 1
317
+
318
+ return converted, skipped, failed
319
+
320
+
321
+ def main() -> int:
322
+ parser = argparse.ArgumentParser(
323
+ description="Reorganize ABCD Study raw data into BIDS-compliant structure."
324
+ )
325
+ parser.add_argument(
326
+ "--input",
327
+ required=True,
328
+ help="Path to ABCD raw data directory",
329
+ )
330
+ parser.add_argument(
331
+ "--output",
332
+ required=True,
333
+ help="Path to output BIDS directory",
334
+ )
335
+ parser.add_argument(
336
+ "--participants-file",
337
+ help="Optional: path to ABCD phenotype file for participant metadata",
338
+ )
339
+ parser.add_argument(
340
+ "--release-version",
341
+ default="5.1",
342
+ help="ABCD release version (default: 5.1)",
343
+ )
344
+ parser.add_argument(
345
+ "--dry-run",
346
+ action="store_true",
347
+ help="Preview without copying files",
348
+ )
349
+ args = parser.parse_args()
350
+
351
+ input_dir = Path(args.input).resolve()
352
+ output_dir = Path(args.output).resolve()
353
+
354
+ if not input_dir.exists() or not input_dir.is_dir():
355
+ print(f"Input directory does not exist: {input_dir}", file=sys.stderr)
356
+ return 1
357
+
358
+ output_dir.mkdir(parents=True, exist_ok=True)
359
+ participants: List[str] = []
360
+
361
+ # Detect layout
362
+ layout = detect_subject_layout(input_dir)
363
+ print(f"Detected layout: {layout}")
364
+ print(f"Input: {input_dir}")
365
+ print(f"Output: {output_dir}")
366
+ print(f"{'[DRY RUN] ' if args.dry_run else ''}Starting reorganization...\n")
367
+
368
+ if layout == "nested":
369
+ converted, skipped, failed = process_nested_layout(
370
+ input_dir, output_dir, participants, args.dry_run
371
+ )
372
+ else:
373
+ converted, skipped, failed = process_flat_layout(
374
+ input_dir, output_dir, participants, args.dry_run
375
+ )
376
+
377
+ # Write BIDS metadata
378
+ if not args.dry_run and converted > 0:
379
+ write_dataset_description(output_dir, args.release_version)
380
+ write_participants_tsv(output_dir, participants)
381
+
382
+ print(f"\nDone. Converted={converted}, Skipped={skipped}, Failed={failed}")
383
+ return 0 if failed == 0 else 2
384
+
385
+
386
+ if __name__ == "__main__":
387
+ sys.exit(main())