@brainpilot/skills 0.0.6 → 0.0.7

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,781 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Academic Research Hub - Multi-Source Academic Paper Search
4
+
5
+ Search and retrieve academic papers from arXiv, PubMed, Semantic Scholar, and more.
6
+ Download PDFs, extract citations, and generate bibliographies.
7
+
8
+ Requires: pip install arxiv scholarly pubmed-parser semanticscholar requests
9
+ """
10
+
11
+ import argparse
12
+ import json
13
+ import sys
14
+ import os
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+ from typing import List, Dict, Any, Optional
18
+ from enum import Enum
19
+
20
+ # Import handlers
21
+ try:
22
+ import arxiv
23
+ except ImportError:
24
+ arxiv = None
25
+
26
+ try:
27
+ from semanticscholar import SemanticScholar
28
+ except ImportError:
29
+ SemanticScholar = None
30
+
31
+ try:
32
+ from Bio import Entrez
33
+ except ImportError:
34
+ Entrez = None
35
+
36
+ import requests
37
+
38
+
39
+ class Source(Enum):
40
+ """Available research sources"""
41
+ ARXIV = "arxiv"
42
+ PUBMED = "pubmed"
43
+ SEMANTIC = "semantic"
44
+
45
+
46
+ class OutputFormat(Enum):
47
+ """Available output formats"""
48
+ TEXT = "text"
49
+ JSON = "json"
50
+ BIBTEX = "bibtex"
51
+ RIS = "ris"
52
+ MARKDOWN = "markdown"
53
+
54
+
55
+ def check_dependencies(source: Source):
56
+ """Check if required dependencies are installed"""
57
+ if source == Source.ARXIV and arxiv is None:
58
+ print("Error: arxiv library not installed", file=sys.stderr)
59
+ print("Install with: pip install arxiv", file=sys.stderr)
60
+ sys.exit(1)
61
+
62
+ if source == Source.SEMANTIC and SemanticScholar is None:
63
+ print("Error: semanticscholar library not installed", file=sys.stderr)
64
+ print("Install with: pip install semanticscholar", file=sys.stderr)
65
+ sys.exit(1)
66
+
67
+ if source == Source.PUBMED and Entrez is None:
68
+ print("Error: biopython library not installed", file=sys.stderr)
69
+ print("Install with: pip install biopython", file=sys.stderr)
70
+ sys.exit(1)
71
+
72
+
73
+ # ============================================================================
74
+ # arXiv Search Functions
75
+ # ============================================================================
76
+
77
+ def search_arxiv(
78
+ query: str,
79
+ max_results: int = 10,
80
+ category: Optional[str] = None,
81
+ author: Optional[str] = None,
82
+ year: Optional[int] = None,
83
+ start_date: Optional[str] = None,
84
+ end_date: Optional[str] = None,
85
+ sort_by: str = "relevance"
86
+ ) -> List[Dict[str, Any]]:
87
+ """Search arXiv repository"""
88
+
89
+ # Build query
90
+ search_query = query
91
+
92
+ if category:
93
+ search_query = f"cat:{category} AND {query}"
94
+
95
+ if author:
96
+ search_query = f"{search_query} AND au:{author}"
97
+
98
+ # Determine sort order
99
+ sort_order = arxiv.SortCriterion.Relevance
100
+ if sort_by == "date":
101
+ sort_order = arxiv.SortCriterion.SubmittedDate
102
+
103
+ try:
104
+ search = arxiv.Search(
105
+ query=search_query,
106
+ max_results=max_results,
107
+ sort_by=sort_order
108
+ )
109
+
110
+ results = []
111
+ for paper in search.results():
112
+ # Filter by date if specified
113
+ pub_date = paper.published.date()
114
+
115
+ if year and pub_date.year != year:
116
+ continue
117
+
118
+ if start_date:
119
+ start = datetime.strptime(start_date, "%Y-%m-%d").date()
120
+ if pub_date < start:
121
+ continue
122
+
123
+ if end_date:
124
+ end = datetime.strptime(end_date, "%Y-%m-%d").date()
125
+ if pub_date > end:
126
+ continue
127
+
128
+ results.append({
129
+ "title": paper.title,
130
+ "authors": [author.name for author in paper.authors],
131
+ "published": paper.published.strftime("%Y-%m-%d"),
132
+ "updated": paper.updated.strftime("%Y-%m-%d"),
133
+ "arxiv_id": paper.entry_id.split("/")[-1],
134
+ "categories": paper.categories,
135
+ "abstract": paper.summary,
136
+ "pdf_url": paper.pdf_url,
137
+ "doi": paper.doi,
138
+ "primary_category": paper.primary_category,
139
+ "comment": paper.comment,
140
+ "journal_ref": paper.journal_ref
141
+ })
142
+
143
+ return results
144
+
145
+ except Exception as e:
146
+ print(f"Error searching arXiv: {e}", file=sys.stderr)
147
+ return []
148
+
149
+
150
+ def download_arxiv_papers(papers: List[Dict[str, Any]], output_dir: str):
151
+ """Download arXiv papers as PDFs"""
152
+ output_path = Path(output_dir)
153
+ output_path.mkdir(parents=True, exist_ok=True)
154
+
155
+ downloaded = 0
156
+ for paper in papers:
157
+ arxiv_id = paper["arxiv_id"]
158
+ title = paper["title"][:100] # Truncate long titles
159
+ # Clean filename
160
+ filename = "".join(c if c.isalnum() or c in " -_" else "_" for c in title)
161
+ filepath = output_path / f"{arxiv_id}_{filename}.pdf"
162
+
163
+ try:
164
+ # Download PDF
165
+ pdf_url = paper["pdf_url"]
166
+ response = requests.get(pdf_url, timeout=30)
167
+ response.raise_for_status()
168
+
169
+ with open(filepath, "wb") as f:
170
+ f.write(response.content)
171
+
172
+ print(f"Downloaded: {filepath.name}", file=sys.stderr)
173
+ downloaded += 1
174
+
175
+ except Exception as e:
176
+ print(f"Failed to download {arxiv_id}: {e}", file=sys.stderr)
177
+
178
+ print(f"\nDownloaded {downloaded}/{len(papers)} papers to {output_dir}", file=sys.stderr)
179
+
180
+
181
+ # ============================================================================
182
+ # PubMed Search Functions
183
+ # ============================================================================
184
+
185
+ def search_pubmed(
186
+ query: str,
187
+ max_results: int = 10,
188
+ start_date: Optional[str] = None,
189
+ end_date: Optional[str] = None,
190
+ publication_type: Optional[str] = None,
191
+ author: Optional[str] = None,
192
+ email: str = "user@example.com" # Required by NCBI
193
+ ) -> List[Dict[str, Any]]:
194
+ """Search PubMed database"""
195
+
196
+ # Set email for Entrez (required by NCBI)
197
+ Entrez.email = email
198
+
199
+ # Build query
200
+ search_query = query
201
+
202
+ if publication_type:
203
+ search_query = f"{search_query} AND {publication_type}[Publication Type]"
204
+
205
+ if author:
206
+ search_query = f"{search_query} AND {author}[Author]"
207
+
208
+ # Add date range
209
+ date_filter = ""
210
+ if start_date and end_date:
211
+ date_filter = f"{start_date}:{end_date}[Date - Publication]"
212
+ elif start_date:
213
+ date_filter = f"{start_date}:3000[Date - Publication]"
214
+ elif end_date:
215
+ date_filter = f"1900:{end_date}[Date - Publication]"
216
+
217
+ if date_filter:
218
+ search_query = f"{search_query} AND {date_filter}"
219
+
220
+ try:
221
+ # Search for PMIDs
222
+ handle = Entrez.esearch(db="pubmed", term=search_query, retmax=max_results)
223
+ record = Entrez.read(handle)
224
+ handle.close()
225
+
226
+ pmids = record["IdList"]
227
+
228
+ if not pmids:
229
+ return []
230
+
231
+ # Fetch details
232
+ handle = Entrez.efetch(db="pubmed", id=pmids, rettype="medline", retmode="text")
233
+ records = handle.read()
234
+ handle.close()
235
+
236
+ # Parse results (simplified - would need proper MEDLINE parser)
237
+ results = []
238
+ for pmid in pmids:
239
+ # Fetch individual record in XML for easier parsing
240
+ handle = Entrez.efetch(db="pubmed", id=pmid, rettype="abstract", retmode="xml")
241
+ record = Entrez.read(handle)
242
+ handle.close()
243
+
244
+ article = record["PubmedArticle"][0]["MedlineCitation"]["Article"]
245
+
246
+ # Extract authors
247
+ authors = []
248
+ if "AuthorList" in article:
249
+ for author in article["AuthorList"]:
250
+ if "LastName" in author and "Initials" in author:
251
+ authors.append(f"{author['LastName']} {author['Initials']}")
252
+
253
+ # Extract abstract
254
+ abstract = ""
255
+ if "Abstract" in article and "AbstractText" in article["Abstract"]:
256
+ abstract = " ".join(str(text) for text in article["Abstract"]["AbstractText"])
257
+
258
+ # Extract publication date
259
+ pub_date = ""
260
+ if "Journal" in article and "JournalIssue" in article["Journal"]:
261
+ issue = article["Journal"]["JournalIssue"]
262
+ if "PubDate" in issue:
263
+ date = issue["PubDate"]
264
+ year = date.get("Year", "")
265
+ month = date.get("Month", "")
266
+ day = date.get("Day", "")
267
+ pub_date = f"{year}-{month}-{day}".strip("-")
268
+
269
+ # Extract DOI
270
+ doi = ""
271
+ if "ELocationID" in article:
272
+ for eid in article["ELocationID"]:
273
+ if eid.attributes.get("EIdType") == "doi":
274
+ doi = str(eid)
275
+
276
+ results.append({
277
+ "title": str(article.get("ArticleTitle", "No title")),
278
+ "authors": authors,
279
+ "journal": str(article.get("Journal", {}).get("Title", "")),
280
+ "published": pub_date,
281
+ "pmid": pmid,
282
+ "doi": doi,
283
+ "abstract": abstract,
284
+ "url": f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/"
285
+ })
286
+
287
+ return results
288
+
289
+ except Exception as e:
290
+ print(f"Error searching PubMed: {e}", file=sys.stderr)
291
+ return []
292
+
293
+
294
+ # ============================================================================
295
+ # Semantic Scholar Search Functions
296
+ # ============================================================================
297
+
298
+ def search_semantic(
299
+ query: str,
300
+ max_results: int = 10,
301
+ year: Optional[int] = None,
302
+ min_citations: Optional[int] = None,
303
+ author: Optional[str] = None,
304
+ sort_by: str = "relevance"
305
+ ) -> List[Dict[str, Any]]:
306
+ """Search Semantic Scholar"""
307
+
308
+ try:
309
+ sch = SemanticScholar()
310
+
311
+ # Search papers
312
+ results = sch.search_paper(query, limit=max_results)
313
+
314
+ papers = []
315
+ for paper in results:
316
+ # The semanticscholar client returns Paper objects (attrs), not dicts.
317
+ # We should use attribute access and not assume dict-like .get().
318
+ paper_id = getattr(paper, "paperId", None)
319
+ if not paper_id:
320
+ continue
321
+ details = sch.get_paper(paper_id)
322
+
323
+ # Filter by year
324
+ if year and details.year != year:
325
+ continue
326
+
327
+ # Filter by citations
328
+ if min_citations and (details.citationCount or 0) < min_citations:
329
+ continue
330
+
331
+ # Filter by author
332
+ if author and details.authors:
333
+ author_match = any(
334
+ author.lower() in a.name.lower()
335
+ for a in details.authors
336
+ )
337
+ if not author_match:
338
+ continue
339
+
340
+ # Extract data
341
+ authors = [getattr(a, "name", str(a)) for a in (details.authors or [])]
342
+
343
+ # fieldsOfStudy is typically a list[str] in current semanticscholar versions.
344
+ fos = []
345
+ if details.fieldsOfStudy:
346
+ try:
347
+ fos = [str(f) for f in details.fieldsOfStudy]
348
+ except Exception:
349
+ fos = []
350
+
351
+ external_ids = getattr(details, "externalIds", None) or {}
352
+ oa_pdf = getattr(details, "openAccessPdf", None)
353
+ oa_url = getattr(oa_pdf, "url", None) if oa_pdf is not None else None
354
+
355
+ papers.append({
356
+ "title": getattr(details, "title", ""),
357
+ "authors": authors,
358
+ "published": str(getattr(details, "year", None)) if getattr(details, "year", None) else "Unknown",
359
+ "paper_id": getattr(details, "paperId", paper_id),
360
+ "citations": getattr(details, "citationCount", 0) or 0,
361
+ "influential_citations": getattr(details, "influentialCitationCount", 0) or 0,
362
+ "fields": fos,
363
+ "abstract": getattr(details, "abstract", "") or "",
364
+ "doi": getattr(details, "doi", "") or "",
365
+ "arxiv_id": external_ids.get("ArXiv") if isinstance(external_ids, dict) else None,
366
+ "url": getattr(details, "url", None) or f"https://www.semanticscholar.org/paper/{paper_id}",
367
+ "pdf_url": oa_url
368
+ })
369
+
370
+ # Sort results
371
+ if sort_by == "citations":
372
+ papers.sort(key=lambda p: p["citations"], reverse=True)
373
+ elif sort_by == "date":
374
+ papers.sort(key=lambda p: p["published"], reverse=True)
375
+
376
+ return papers[:max_results]
377
+
378
+ except Exception as e:
379
+ print(f"Error searching Semantic Scholar: {e}", file=sys.stderr)
380
+ return []
381
+
382
+
383
+ # ============================================================================
384
+ # Output Formatting Functions
385
+ # ============================================================================
386
+
387
+ def format_text(papers: List[Dict[str, Any]], source: Source) -> str:
388
+ """Format results as plain text"""
389
+ if not papers:
390
+ return "No results found."
391
+
392
+ lines = [f"Search Results: {len(papers)} papers found\n"]
393
+
394
+ for i, paper in enumerate(papers, 1):
395
+ lines.append(f"\n{i}. {paper['title']}")
396
+
397
+ if "authors" in paper:
398
+ authors = ", ".join(paper["authors"][:5])
399
+ if len(paper["authors"]) > 5:
400
+ authors += " et al."
401
+ lines.append(f" Authors: {authors}")
402
+
403
+ if "published" in paper:
404
+ lines.append(f" Published: {paper['published']}")
405
+
406
+ if source == Source.ARXIV:
407
+ lines.append(f" arXiv ID: {paper.get('arxiv_id', 'N/A')}")
408
+ lines.append(f" Categories: {', '.join(paper.get('categories', []))}")
409
+
410
+ elif source == Source.PUBMED:
411
+ lines.append(f" Journal: {paper.get('journal', 'N/A')}")
412
+ lines.append(f" PMID: {paper.get('pmid', 'N/A')}")
413
+ if paper.get('doi'):
414
+ lines.append(f" DOI: {paper['doi']}")
415
+
416
+ elif source == Source.SEMANTIC:
417
+ lines.append(f" Paper ID: {paper.get('paper_id', 'N/A')}")
418
+ lines.append(f" Citations: {paper.get('citations', 0)}")
419
+ if paper.get('fields'):
420
+ lines.append(f" Fields: {', '.join(paper['fields'])}")
421
+
422
+ if "abstract" in paper and paper["abstract"]:
423
+ abstract = paper["abstract"][:300]
424
+ if len(paper["abstract"]) > 300:
425
+ abstract += "..."
426
+ lines.append(f" Abstract: {abstract}")
427
+
428
+ # Add URLs
429
+ if "pdf_url" in paper and paper["pdf_url"]:
430
+ lines.append(f" PDF: {paper['pdf_url']}")
431
+ if "url" in paper:
432
+ lines.append(f" URL: {paper['url']}")
433
+
434
+ return "\n".join(lines)
435
+
436
+
437
+ def format_json_output(papers: List[Dict[str, Any]]) -> str:
438
+ """Format results as JSON"""
439
+ return json.dumps(papers, indent=2, ensure_ascii=False)
440
+
441
+
442
+ def format_bibtex(papers: List[Dict[str, Any]], source: Source) -> str:
443
+ """Format results as BibTeX"""
444
+ entries = []
445
+
446
+ for paper in papers:
447
+ # Generate citation key
448
+ first_author = paper.get("authors", ["Unknown"])[0].split()[-1].lower()
449
+ year = paper.get("published", "0000")[:4]
450
+ title_word = paper.get("title", "").split()[0].lower()
451
+ key = f"{first_author}{year}{title_word}"
452
+
453
+ # Build entry
454
+ entry = f"@article{{{key},\n"
455
+ entry += f" title={{{paper.get('title', 'No title')}}},\n"
456
+
457
+ if paper.get("authors"):
458
+ authors = " and ".join(paper["authors"])
459
+ entry += f" author={{{authors}}},\n"
460
+
461
+ entry += f" year={{{year}}},\n"
462
+
463
+ if source == Source.ARXIV:
464
+ entry += f" journal={{arXiv preprint}},\n"
465
+ if paper.get("arxiv_id"):
466
+ entry += f" volume={{arXiv:{paper['arxiv_id']}}},\n"
467
+
468
+ elif source == Source.PUBMED:
469
+ if paper.get("journal"):
470
+ entry += f" journal={{{paper['journal']}}},\n"
471
+ if paper.get("pmid"):
472
+ entry += f" note={{PMID: {paper['pmid']}}},\n"
473
+
474
+ if paper.get("doi"):
475
+ entry += f" doi={{{paper['doi']}}},\n"
476
+
477
+ if paper.get("url"):
478
+ entry += f" url={{{paper['url']}}},\n"
479
+
480
+ entry = entry.rstrip(",\n") + "\n}\n"
481
+ entries.append(entry)
482
+
483
+ return "\n".join(entries)
484
+
485
+
486
+ def format_ris(papers: List[Dict[str, Any]], source: Source) -> str:
487
+ """Format results as RIS"""
488
+ entries = []
489
+
490
+ for paper in papers:
491
+ entry = "TY - JOUR\n"
492
+ entry += f"TI - {paper.get('title', 'No title')}\n"
493
+
494
+ for author in paper.get("authors", []):
495
+ entry += f"AU - {author}\n"
496
+
497
+ year = paper.get("published", "0000")[:4]
498
+ entry += f"PY - {year}\n"
499
+
500
+ if paper.get("published"):
501
+ entry += f"DA - {paper['published']}\n"
502
+
503
+ if source == Source.ARXIV:
504
+ entry += "JO - arXiv preprint\n"
505
+ if paper.get("arxiv_id"):
506
+ entry += f"VL - arXiv:{paper['arxiv_id']}\n"
507
+
508
+ elif source == Source.PUBMED:
509
+ if paper.get("journal"):
510
+ entry += f"JO - {paper['journal']}\n"
511
+
512
+ if paper.get("doi"):
513
+ entry += f"DO - {paper['doi']}\n"
514
+
515
+ if paper.get("abstract"):
516
+ entry += f"AB - {paper['abstract']}\n"
517
+
518
+ if paper.get("url"):
519
+ entry += f"UR - {paper['url']}\n"
520
+
521
+ entry += "ER -\n\n"
522
+ entries.append(entry)
523
+
524
+ return "".join(entries)
525
+
526
+
527
+ def format_markdown(papers: List[Dict[str, Any]], source: Source) -> str:
528
+ """Format results as Markdown"""
529
+ if not papers:
530
+ return "# Search Results\n\nNo results found."
531
+
532
+ lines = [f"# Search Results: {len(papers)} papers found\n"]
533
+
534
+ for i, paper in enumerate(papers, 1):
535
+ lines.append(f"\n## {i}. {paper['title']}\n")
536
+
537
+ if paper.get("authors"):
538
+ authors = ", ".join(paper["authors"][:5])
539
+ if len(paper["authors"]) > 5:
540
+ authors += " et al."
541
+ lines.append(f"**Authors:** {authors}\n")
542
+
543
+ if paper.get("published"):
544
+ lines.append(f"**Published:** {paper['published']}\n")
545
+
546
+ if source == Source.ARXIV:
547
+ lines.append(f"**arXiv ID:** {paper.get('arxiv_id', 'N/A')}\n")
548
+ lines.append(f"**Categories:** {', '.join(paper.get('categories', []))}\n")
549
+
550
+ elif source == Source.PUBMED:
551
+ lines.append(f"**Journal:** {paper.get('journal', 'N/A')}\n")
552
+ lines.append(f"**PMID:** {paper.get('pmid', 'N/A')}\n")
553
+ if paper.get("doi"):
554
+ lines.append(f"**DOI:** {paper['doi']}\n")
555
+
556
+ elif source == Source.SEMANTIC:
557
+ lines.append(f"**Citations:** {paper.get('citations', 0)}\n")
558
+ if paper.get("fields"):
559
+ lines.append(f"**Fields:** {', '.join(paper['fields'])}\n")
560
+
561
+ if paper.get("abstract"):
562
+ lines.append(f"**Abstract:** {paper['abstract']}\n")
563
+
564
+ if paper.get("pdf_url"):
565
+ lines.append(f"**PDF:** [Download]({paper['pdf_url']})\n")
566
+ if paper.get("url"):
567
+ lines.append(f"**URL:** {paper['url']}\n")
568
+
569
+ return "\n".join(lines)
570
+
571
+
572
+ def format_output(papers: List[Dict[str, Any]], format_type: OutputFormat, source: Source) -> str:
573
+ """Format results according to specified format"""
574
+ if format_type == OutputFormat.JSON:
575
+ return format_json_output(papers)
576
+ elif format_type == OutputFormat.BIBTEX:
577
+ return format_bibtex(papers, source)
578
+ elif format_type == OutputFormat.RIS:
579
+ return format_ris(papers, source)
580
+ elif format_type == OutputFormat.MARKDOWN:
581
+ return format_markdown(papers, source)
582
+ else: # TEXT
583
+ return format_text(papers, source)
584
+
585
+
586
+ # ============================================================================
587
+ # Main Function
588
+ # ============================================================================
589
+
590
+ def main():
591
+ parser = argparse.ArgumentParser(
592
+ description="Search academic papers from multiple sources",
593
+ formatter_class=argparse.RawDescriptionHelpFormatter,
594
+ epilog="""
595
+ Examples:
596
+ # Search arXiv
597
+ %(prog)s arxiv "quantum computing" --max-results 10
598
+
599
+ # Search PubMed with date filter
600
+ %(prog)s pubmed "covid vaccine" --start-date 2023-01-01 --end-date 2023-12-31
601
+
602
+ # Search Semantic Scholar, highly cited papers
603
+ %(prog)s semantic "machine learning" --min-citations 100
604
+
605
+ # Download arXiv papers
606
+ %(prog)s arxiv "deep learning" --download --max-results 5
607
+
608
+ # Generate BibTeX citations
609
+ %(prog)s arxiv "transformers" --format bibtex --output refs.bib
610
+ """
611
+ )
612
+
613
+ # Source selection
614
+ parser.add_argument(
615
+ "source",
616
+ choices=["arxiv", "pubmed", "semantic"],
617
+ help="Research source to search"
618
+ )
619
+
620
+ # Required arguments
621
+ parser.add_argument(
622
+ "query",
623
+ type=str,
624
+ help="Search query"
625
+ )
626
+
627
+ # General options
628
+ parser.add_argument(
629
+ "-n", "--max-results",
630
+ type=int,
631
+ default=10,
632
+ help="Maximum number of results (default: 10)"
633
+ )
634
+
635
+ parser.add_argument(
636
+ "-f", "--format",
637
+ type=str,
638
+ choices=["text", "json", "bibtex", "ris", "markdown"],
639
+ default="text",
640
+ help="Output format (default: text)"
641
+ )
642
+
643
+ parser.add_argument(
644
+ "-o", "--output",
645
+ type=str,
646
+ help="Save results to file"
647
+ )
648
+
649
+ parser.add_argument(
650
+ "--sort-by",
651
+ type=str,
652
+ choices=["relevance", "date", "citations"],
653
+ default="relevance",
654
+ help="Sort results by (default: relevance)"
655
+ )
656
+
657
+ # Filtering options
658
+ parser.add_argument(
659
+ "--year",
660
+ type=int,
661
+ help="Filter by specific year"
662
+ )
663
+
664
+ parser.add_argument(
665
+ "--start-date",
666
+ type=str,
667
+ help="Start date (YYYY-MM-DD)"
668
+ )
669
+
670
+ parser.add_argument(
671
+ "--end-date",
672
+ type=str,
673
+ help="End date (YYYY-MM-DD)"
674
+ )
675
+
676
+ parser.add_argument(
677
+ "--author",
678
+ type=str,
679
+ help="Filter by author name"
680
+ )
681
+
682
+ # arXiv-specific options
683
+ parser.add_argument(
684
+ "--category",
685
+ type=str,
686
+ help="arXiv category (e.g., cs.AI, cs.LG)"
687
+ )
688
+
689
+ # PubMed-specific options
690
+ parser.add_argument(
691
+ "--publication-type",
692
+ type=str,
693
+ help="PubMed publication type filter"
694
+ )
695
+
696
+ # Semantic Scholar-specific options
697
+ parser.add_argument(
698
+ "--min-citations",
699
+ type=int,
700
+ help="Minimum citation count"
701
+ )
702
+
703
+ # Download options
704
+ parser.add_argument(
705
+ "--download",
706
+ action="store_true",
707
+ help="Download paper PDFs (arXiv only)"
708
+ )
709
+
710
+ parser.add_argument(
711
+ "--output-dir",
712
+ type=str,
713
+ default="downloads",
714
+ help="Directory for downloaded PDFs (default: downloads/)"
715
+ )
716
+
717
+ args = parser.parse_args()
718
+
719
+ # Determine source
720
+ source = Source(args.source)
721
+
722
+ # Check dependencies
723
+ check_dependencies(source)
724
+
725
+ # Perform search
726
+ papers = []
727
+
728
+ if source == Source.ARXIV:
729
+ papers = search_arxiv(
730
+ query=args.query,
731
+ max_results=args.max_results,
732
+ category=args.category,
733
+ author=args.author,
734
+ year=args.year,
735
+ start_date=args.start_date,
736
+ end_date=args.end_date,
737
+ sort_by=args.sort_by
738
+ )
739
+
740
+ if args.download and papers:
741
+ download_arxiv_papers(papers, args.output_dir)
742
+
743
+ elif source == Source.PUBMED:
744
+ papers = search_pubmed(
745
+ query=args.query,
746
+ max_results=args.max_results,
747
+ start_date=args.start_date,
748
+ end_date=args.end_date,
749
+ publication_type=args.publication_type,
750
+ author=args.author
751
+ )
752
+
753
+ elif source == Source.SEMANTIC:
754
+ papers = search_semantic(
755
+ query=args.query,
756
+ max_results=args.max_results,
757
+ year=args.year,
758
+ min_citations=args.min_citations,
759
+ author=args.author,
760
+ sort_by=args.sort_by
761
+ )
762
+
763
+ # Format output
764
+ output_format = OutputFormat(args.format)
765
+ formatted_output = format_output(papers, output_format, source)
766
+
767
+ # Save or print results
768
+ if args.output:
769
+ try:
770
+ with open(args.output, 'w', encoding='utf-8') as f:
771
+ f.write(formatted_output)
772
+ print(f"Results saved to: {args.output}", file=sys.stderr)
773
+ except Exception as e:
774
+ print(f"Error saving to file: {e}", file=sys.stderr)
775
+ sys.exit(1)
776
+ else:
777
+ print(formatted_output)
778
+
779
+
780
+ if __name__ == "__main__":
781
+ main()