gapit 0.3.0__tar.gz → 0.4.0__tar.gz

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 (296) hide show
  1. {gapit-0.3.0 → gapit-0.4.0}/AGENTS.md +24 -6
  2. {gapit-0.3.0 → gapit-0.4.0}/CHANGELOG.md +52 -0
  3. {gapit-0.3.0 → gapit-0.4.0}/PKG-INFO +14 -7
  4. {gapit-0.3.0 → gapit-0.4.0}/PLAN.md +26 -0
  5. {gapit-0.3.0 → gapit-0.4.0}/README.md +11 -4
  6. {gapit-0.3.0 → gapit-0.4.0}/docs/agents.md +1 -1
  7. {gapit-0.3.0 → gapit-0.4.0}/docs/databases.md +105 -3
  8. {gapit-0.3.0 → gapit-0.4.0}/docs/index.md +8 -8
  9. {gapit-0.3.0 → gapit-0.4.0}/docs/installation.md +2 -2
  10. {gapit-0.3.0 → gapit-0.4.0}/docs/mcp.md +7 -7
  11. {gapit-0.3.0 → gapit-0.4.0}/docs/outputs.md +77 -6
  12. {gapit-0.3.0 → gapit-0.4.0}/docs/quickstart.md +1 -1
  13. {gapit-0.3.0 → gapit-0.4.0}/docs/reads.md +6 -6
  14. {gapit-0.3.0 → gapit-0.4.0}/docs/screen.md +92 -2
  15. {gapit-0.3.0 → gapit-0.4.0}/docs/summary.md +2 -2
  16. {gapit-0.3.0 → gapit-0.4.0}/mkdocs.yml +2 -2
  17. {gapit-0.3.0 → gapit-0.4.0}/pixi.lock +2 -2
  18. {gapit-0.3.0 → gapit-0.4.0}/pixi.toml +1 -1
  19. {gapit-0.3.0 → gapit-0.4.0}/pyproject.toml +3 -3
  20. {gapit-0.3.0 → gapit-0.4.0}/recipe/meta.yaml +12 -16
  21. gapit-0.4.0/scripts/cluster_calibration.py +180 -0
  22. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/__init__.py +1 -1
  23. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/blast.py +14 -3
  24. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cli.py +8 -3
  25. gapit-0.4.0/src/gapit/cluster.py +250 -0
  26. gapit-0.4.0/src/gapit/cluster_math.py +125 -0
  27. gapit-0.4.0/src/gapit/clusterbuild.py +185 -0
  28. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cmd_db_build.py +34 -8
  29. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cmd_screen.py +45 -2
  30. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/db.py +27 -3
  31. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/db_build_ops.py +47 -8
  32. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/db_ops.py +27 -10
  33. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/db_query_ops.py +4 -3
  34. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/dbbuild.py +5 -5
  35. gapit-0.4.0/src/gapit/engines.py +25 -0
  36. gapit-0.4.0/src/gapit/formats/cluster.py +224 -0
  37. gapit-0.4.0/src/gapit/formats/cluster_md.py +149 -0
  38. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/formats/json.py +32 -1
  39. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/formats/md.py +11 -3
  40. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/formats/schemas.py +6 -0
  41. gapit-0.4.0/src/gapit/fragments.py +131 -0
  42. gapit-0.4.0/src/gapit/gbfeatures.py +308 -0
  43. gapit-0.4.0/src/gapit/gffparse.py +204 -0
  44. gapit-0.4.0/src/gapit/hits.py +155 -0
  45. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/mcp_schemas.py +11 -2
  46. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/mcp_tools.py +19 -2
  47. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/minimap2_run.py +8 -7
  48. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/paf.py +16 -3
  49. gapit-0.4.0/src/gapit/providers/__init__.py +53 -0
  50. gapit-0.4.0/src/gapit/providers/cluster_common.py +80 -0
  51. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/common.py +7 -6
  52. gapit-0.4.0/src/gapit/providers/kaptive_ak.py +27 -0
  53. gapit-0.4.0/src/gapit/providers/kaptive_k.py +34 -0
  54. gapit-0.4.0/src/gapit/providers/kaptive_o.py +26 -0
  55. gapit-0.4.0/src/gapit/providers/kaptive_oc.py +27 -0
  56. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/records.py +12 -3
  57. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/screening.py +44 -22
  58. gapit-0.4.0/src/gapit/screening_cluster.py +138 -0
  59. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/screening_reads.py +5 -0
  60. gapit-0.4.0/src/gapit/typing_engine.py +250 -0
  61. gapit-0.4.0/src/gapit/typing_models.py +213 -0
  62. gapit-0.4.0/tests/data/cluster/bakta_style.gbk +73 -0
  63. gapit-0.4.0/tests/data/cluster/clusters.gff3 +16 -0
  64. gapit-0.4.0/tests/data/cluster/clusters_gz.gff3.gz +0 -0
  65. gapit-0.4.0/tests/data/cluster/clusters_sidecar.fa +7 -0
  66. gapit-0.4.0/tests/data/cluster/clusters_sidecar.gff3 +8 -0
  67. gapit-0.4.0/tests/data/cluster/compound.gbk +19 -0
  68. gapit-0.4.0/tests/data/cluster/gz_input.gbk.gz +0 -0
  69. gapit-0.4.0/tests/data/cluster/kaptive_style.gbk +48 -0
  70. gapit-0.4.0/tests/data/cluster/nofasta.gff3 +8 -0
  71. gapit-0.4.0/tests/data/cluster/screening.gbk +82 -0
  72. gapit-0.4.0/tests/data/cluster/typing_invalid.json +9 -0
  73. gapit-0.4.0/tests/data/cluster/typing_screen.json +22 -0
  74. gapit-0.4.0/tests/data/cluster/typing_screen_cm.json +22 -0
  75. gapit-0.4.0/tests/data/cluster/typing_valid.json +31 -0
  76. gapit-0.4.0/tests/data/cluster/unnamed_cds.gbk +29 -0
  77. gapit-0.4.0/tests/data/fragments/fragdb/sequences +11 -0
  78. gapit-0.4.0/tests/data/fragments/split3040.fa +9 -0
  79. gapit-0.4.0/tests/data/fragments/split4060.fa +12 -0
  80. gapit-0.4.0/tests/data/fragments/split5050.fa +12 -0
  81. gapit-0.4.0/tests/golden/cluster_exact.json +72 -0
  82. gapit-0.4.0/tests/golden/cluster_exact.md +29 -0
  83. gapit-0.4.0/tests/golden/cluster_exact.tsv +2 -0
  84. gapit-0.4.0/tests/golden/cluster_fragmented.json +72 -0
  85. gapit-0.4.0/tests/golden/cluster_fragmented.md +29 -0
  86. gapit-0.4.0/tests/golden/cluster_fragmented.tsv +2 -0
  87. gapit-0.4.0/tests/golden/cluster_typing_ambiguous.json +133 -0
  88. gapit-0.4.0/tests/golden/cluster_typing_ambiguous.md +31 -0
  89. gapit-0.4.0/tests/golden/cluster_typing_ambiguous.tsv +2 -0
  90. gapit-0.4.0/tests/golden/cluster_typing_cm_exact.json +95 -0
  91. gapit-0.4.0/tests/golden/cluster_typing_cm_exact.md +31 -0
  92. gapit-0.4.0/tests/golden/cluster_typing_cm_exact.tsv +2 -0
  93. gapit-0.4.0/tests/golden/cluster_typing_exact.json +95 -0
  94. gapit-0.4.0/tests/golden/cluster_typing_exact.md +31 -0
  95. gapit-0.4.0/tests/golden/cluster_typing_exact.tsv +2 -0
  96. gapit-0.4.0/tests/golden/fragments_merge.json +100 -0
  97. gapit-0.4.0/tests/golden/fragments_merge.md +32 -0
  98. gapit-0.4.0/tests/golden/fragments_merge.tsv +3 -0
  99. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/reads_tinyamr.json +1 -1
  100. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/reads_tinyamr.md +1 -1
  101. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/screen_multi.json +1 -1
  102. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/screen_multi.md +1 -1
  103. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/summary_multi.json +1 -1
  104. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/summary_multi.md +1 -1
  105. gapit-0.4.0/tests/parity/corpus/04_reverse.fa +42 -0
  106. gapit-0.4.0/tests/parity/corpus/05_threshold.fa +32 -0
  107. {gapit-0.3.0 → gapit-0.4.0}/tests/parity/make_corpus.py +52 -0
  108. {gapit-0.3.0 → gapit-0.4.0}/tests/parity/run_parity.py +5 -2
  109. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_completion.py +9 -0
  110. gapit-0.4.0/tests/test_cli_db_build_cluster.py +242 -0
  111. gapit-0.4.0/tests/test_cli_fragments.py +190 -0
  112. gapit-0.4.0/tests/test_cluster_calibration.py +152 -0
  113. gapit-0.4.0/tests/test_cluster_db.py +118 -0
  114. gapit-0.4.0/tests/test_cluster_math.py +141 -0
  115. gapit-0.4.0/tests/test_cluster_screen.py +574 -0
  116. gapit-0.4.0/tests/test_cluster_typing.py +372 -0
  117. gapit-0.4.0/tests/test_fragments.py +293 -0
  118. gapit-0.4.0/tests/test_gbfeatures.py +133 -0
  119. gapit-0.4.0/tests/test_gffparse.py +113 -0
  120. {gapit-0.3.0 → gapit-0.4.0}/tests/test_json_format.py +7 -1
  121. {gapit-0.3.0 → gapit-0.4.0}/tests/test_mcp.py +32 -0
  122. {gapit-0.3.0 → gapit-0.4.0}/tests/test_md_format.py +1 -0
  123. gapit-0.4.0/tests/test_provider_kaptive.py +195 -0
  124. gapit-0.4.0/tests/test_typing_engine.py +670 -0
  125. gapit-0.4.0/tests/test_typing_models.py +140 -0
  126. gapit-0.3.0/src/gapit/hits.py +0 -107
  127. gapit-0.3.0/src/gapit/providers/__init__.py +0 -39
  128. {gapit-0.3.0 → gapit-0.4.0}/.gitattributes +0 -0
  129. {gapit-0.3.0 → gapit-0.4.0}/.github/workflows/ci.yml +0 -0
  130. {gapit-0.3.0 → gapit-0.4.0}/.github/workflows/docs.yml +0 -0
  131. {gapit-0.3.0 → gapit-0.4.0}/.github/workflows/release.yml +0 -0
  132. {gapit-0.3.0 → gapit-0.4.0}/.github/workflows/snapshot-refresh.yml +0 -0
  133. {gapit-0.3.0 → gapit-0.4.0}/.gitignore +0 -0
  134. {gapit-0.3.0 → gapit-0.4.0}/LICENSE +0 -0
  135. {gapit-0.3.0 → gapit-0.4.0}/SPEC.md +0 -0
  136. {gapit-0.3.0 → gapit-0.4.0}/docs/custom-db.md +0 -0
  137. {gapit-0.3.0 → gapit-0.4.0}/docs/faq.md +0 -0
  138. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cmd_db.py +0 -0
  139. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cmd_db_install.py +0 -0
  140. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cmd_db_outdated.py +0 -0
  141. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cmd_db_search.py +0 -0
  142. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/cmd_summary.py +0 -0
  143. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/config.py +0 -0
  144. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/data/snapshots/card.tar.gz +0 -0
  145. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/data/snapshots/vfdb.tar.gz +0 -0
  146. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/dbcodec.py +0 -0
  147. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/dispatch.py +0 -0
  148. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/errors.py +0 -0
  149. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/fasta.py +0 -0
  150. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/formats/__init__.py +0 -0
  151. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/formats/summary.py +0 -0
  152. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/formats/tsv.py +0 -0
  153. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/mcp.py +0 -0
  154. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/minimap.py +0 -0
  155. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/proctools.py +0 -0
  156. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/argannot.py +0 -0
  157. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/bacmet2.py +0 -0
  158. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/card.py +0 -0
  159. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/ecoh.py +0 -0
  160. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/ecoli_vf.py +0 -0
  161. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/megares.py +0 -0
  162. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/ncbi.py +0 -0
  163. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/plasmidfinder.py +0 -0
  164. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/resfinder.py +0 -0
  165. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/snapshots.py +0 -0
  166. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/upec_expec_vf.py +0 -0
  167. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/vfdb.py +0 -0
  168. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/providers/victors.py +0 -0
  169. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/py.typed +0 -0
  170. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/reads.py +0 -0
  171. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/report.py +0 -0
  172. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/seqconvert.py +0 -0
  173. {gapit-0.3.0 → gapit-0.4.0}/src/gapit/summary.py +0 -0
  174. {gapit-0.3.0 → gapit-0.4.0}/tests/data/contigs/full.fa +0 -0
  175. {gapit-0.3.0 → gapit-0.4.0}/tests/data/contigs/gap.fa +0 -0
  176. {gapit-0.3.0 → gapit-0.4.0}/tests/data/contigs/none.fa +0 -0
  177. {gapit-0.3.0 → gapit-0.4.0}/tests/data/contigs/partial.fa +0 -0
  178. {gapit-0.3.0 → gapit-0.4.0}/tests/data/contigs/sort.fa +0 -0
  179. {gapit-0.3.0 → gapit-0.4.0}/tests/data/convert/sample.embl +0 -0
  180. {gapit-0.3.0 → gapit-0.4.0}/tests/data/convert/sample.embl.bz2 +0 -0
  181. {gapit-0.3.0 → gapit-0.4.0}/tests/data/convert/sample.fa +0 -0
  182. {gapit-0.3.0 → gapit-0.4.0}/tests/data/convert/sample.fa.gz +0 -0
  183. {gapit-0.3.0 → gapit-0.4.0}/tests/data/convert/sample.fq +0 -0
  184. {gapit-0.3.0 → gapit-0.4.0}/tests/data/convert/sample.gbk +0 -0
  185. {gapit-0.3.0 → gapit-0.4.0}/tests/data/convert/sample.gbk.gz +0 -0
  186. {gapit-0.3.0 → gapit-0.4.0}/tests/data/db/tinyamr/sequences +0 -0
  187. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/argannot/ARG-ANNOT_NT_V6_July2019.txt +0 -0
  188. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/bacmet2/BacMet2_EXP_database.fasta +0 -0
  189. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/card/card.json +0 -0
  190. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/ecoh/EcOH.fasta +0 -0
  191. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/ecoli_vf/repaired_ecoli_vfs_shortnames.ffn +0 -0
  192. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/megares/megares_drugs_demo.fasta +0 -0
  193. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/ncbi/AMR_CDS.fa +0 -0
  194. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/ncbi/ReferenceGeneCatalog.txt +0 -0
  195. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/plasmidfinder/plasmids_a.fsa +0 -0
  196. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/plasmidfinder/plasmids_b.fsa +0 -0
  197. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/resfinder/aminoglycoside.fsa +0 -0
  198. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/resfinder/beta-lactam.fsa +0 -0
  199. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/resfinder/phenotypes.txt +0 -0
  200. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/upec_expec_vf/UPEC_ExPEC_VF.tsv +0 -0
  201. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/vfdb/VFDB_setA_nt.fas +0 -0
  202. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/victors/gen_downloads.php +0 -0
  203. {gapit-0.3.0 → gapit-0.4.0}/tests/data/providers/victors/gen_downloads_protein.php +0 -0
  204. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/bla_partial.fq +0 -0
  205. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/junk.fq +0 -0
  206. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/suly_partial.fq +0 -0
  207. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_R1.fq +0 -0
  208. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_R2.fq +0 -0
  209. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_full.fq +0 -0
  210. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_full.fq.gz +0 -0
  211. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_lane1.fq +0 -0
  212. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_lane2.fq +0 -0
  213. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_pe1_R1.fq +0 -0
  214. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_pe1_R2.fq +0 -0
  215. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_pe2_R1.fq +0 -0
  216. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads/tetx_pe2_R2.fq +0 -0
  217. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads2/ont_homologs.fq +0 -0
  218. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads2/sr_homologs.fq +0 -0
  219. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads2_db/homologs/sequences +0 -0
  220. {gapit-0.3.0 → gapit-0.4.0}/tests/data/reads_db/tinyreads/sequences +0 -0
  221. {gapit-0.3.0 → gapit-0.4.0}/tests/data/summary/empty.tsv +0 -0
  222. {gapit-0.3.0 → gapit-0.4.0}/tests/data/summary/multi_sample.tsv +0 -0
  223. {gapit-0.3.0 → gapit-0.4.0}/tests/data/summary/sample_a.csv +0 -0
  224. {gapit-0.3.0 → gapit-0.4.0}/tests/data/summary/sample_a.tsv +0 -0
  225. {gapit-0.3.0 → gapit-0.4.0}/tests/data/summary/sample_b.csv +0 -0
  226. {gapit-0.3.0 → gapit-0.4.0}/tests/data/summary/sample_b.tsv +0 -0
  227. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/summary_dutch.tsv +0 -0
  228. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/summary_multi.csv +0 -0
  229. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/summary_multi.tsv +0 -0
  230. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/tinyamr_multi_nopath.csv +0 -0
  231. {gapit-0.3.0 → gapit-0.4.0}/tests/golden/tinyamr_multi_nopath.tsv +0 -0
  232. {gapit-0.3.0 → gapit-0.4.0}/tests/parity/corpus/01_exact_and_junk.fa +0 -0
  233. {gapit-0.3.0 → gapit-0.4.0}/tests/parity/corpus/02_mutated.fa +0 -0
  234. {gapit-0.3.0 → gapit-0.4.0}/tests/parity/corpus/03_truncated.fa +0 -0
  235. {gapit-0.3.0 → gapit-0.4.0}/tests/parity/run_summary_parity.py +0 -0
  236. {gapit-0.3.0 → gapit-0.4.0}/tests/test_blast_parse.py +0 -0
  237. {gapit-0.3.0 → gapit-0.4.0}/tests/test_blast_pipeline.py +0 -0
  238. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_aligner.py +0 -0
  239. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_db_build.py +0 -0
  240. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_db_fetch.py +0 -0
  241. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_db_query.py +0 -0
  242. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_reads.py +0 -0
  243. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_reads2.py +0 -0
  244. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_schema.py +0 -0
  245. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_screen.py +0 -0
  246. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_screen_jobs.py +0 -0
  247. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_setupdb.py +0 -0
  248. {gapit-0.3.0 → gapit-0.4.0}/tests/test_cli_summary.py +0 -0
  249. {gapit-0.3.0 → gapit-0.4.0}/tests/test_config.py +0 -0
  250. {gapit-0.3.0 → gapit-0.4.0}/tests/test_db.py +0 -0
  251. {gapit-0.3.0 → gapit-0.4.0}/tests/test_db_fetch.py +0 -0
  252. {gapit-0.3.0 → gapit-0.4.0}/tests/test_db_headers.py +0 -0
  253. {gapit-0.3.0 → gapit-0.4.0}/tests/test_dbbuild.py +0 -0
  254. {gapit-0.3.0 → gapit-0.4.0}/tests/test_dbcodec.py +0 -0
  255. {gapit-0.3.0 → gapit-0.4.0}/tests/test_error_envelope.py +0 -0
  256. {gapit-0.3.0 → gapit-0.4.0}/tests/test_fasta.py +0 -0
  257. {gapit-0.3.0 → gapit-0.4.0}/tests/test_gapit_db_e2e.py +0 -0
  258. {gapit-0.3.0 → gapit-0.4.0}/tests/test_hits.py +0 -0
  259. {gapit-0.3.0 → gapit-0.4.0}/tests/test_mcp_db_tools.py +0 -0
  260. {gapit-0.3.0 → gapit-0.4.0}/tests/test_mcp_protocol.py +0 -0
  261. {gapit-0.3.0 → gapit-0.4.0}/tests/test_mcp_reads.py +0 -0
  262. {gapit-0.3.0 → gapit-0.4.0}/tests/test_minimap.py +0 -0
  263. {gapit-0.3.0 → gapit-0.4.0}/tests/test_mol_type.py +0 -0
  264. {gapit-0.3.0 → gapit-0.4.0}/tests/test_oversubscription.py +0 -0
  265. {gapit-0.3.0 → gapit-0.4.0}/tests/test_paf.py +0 -0
  266. {gapit-0.3.0 → gapit-0.4.0}/tests/test_paf_identity.py +0 -0
  267. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_argannot.py +0 -0
  268. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_bacmet2.py +0 -0
  269. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_card.py +0 -0
  270. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_ecoh.py +0 -0
  271. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_ecoli_vf.py +0 -0
  272. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_megares.py +0 -0
  273. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_ncbi.py +0 -0
  274. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_plasmidfinder.py +0 -0
  275. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_resfinder.py +0 -0
  276. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_upec_expec_vf.py +0 -0
  277. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_vfdb.py +0 -0
  278. {gapit-0.3.0 → gapit-0.4.0}/tests/test_provider_victors.py +0 -0
  279. {gapit-0.3.0 → gapit-0.4.0}/tests/test_providers_common.py +0 -0
  280. {gapit-0.3.0 → gapit-0.4.0}/tests/test_reads2_homologs.py +0 -0
  281. {gapit-0.3.0 → gapit-0.4.0}/tests/test_reads2_model.py +0 -0
  282. {gapit-0.3.0 → gapit-0.4.0}/tests/test_reads_aggregate.py +0 -0
  283. {gapit-0.3.0 → gapit-0.4.0}/tests/test_reads_integration.py +0 -0
  284. {gapit-0.3.0 → gapit-0.4.0}/tests/test_reads_interval_union.py +0 -0
  285. {gapit-0.3.0 → gapit-0.4.0}/tests/test_reads_streaming.py +0 -0
  286. {gapit-0.3.0 → gapit-0.4.0}/tests/test_records.py +0 -0
  287. {gapit-0.3.0 → gapit-0.4.0}/tests/test_report.py +0 -0
  288. {gapit-0.3.0 → gapit-0.4.0}/tests/test_screen_integration.py +0 -0
  289. {gapit-0.3.0 → gapit-0.4.0}/tests/test_screening_reads_unit.py +0 -0
  290. {gapit-0.3.0 → gapit-0.4.0}/tests/test_seqconvert.py +0 -0
  291. {gapit-0.3.0 → gapit-0.4.0}/tests/test_seqconvert_differential.py +0 -0
  292. {gapit-0.3.0 → gapit-0.4.0}/tests/test_snapshots.py +0 -0
  293. {gapit-0.3.0 → gapit-0.4.0}/tests/test_summary.py +0 -0
  294. {gapit-0.3.0 → gapit-0.4.0}/tests/test_summary_formats.py +0 -0
  295. {gapit-0.3.0 → gapit-0.4.0}/tests/test_tsv.py +0 -0
  296. {gapit-0.3.0 → gapit-0.4.0}/tests/test_version.py +0 -0
@@ -1,7 +1,7 @@
1
1
  # AGENTS.md — gapit
2
2
 
3
3
  > Python reimplementation of [abricate](https://github.com/tseemann/abricate): mass screening of
4
- > contigs for antimicrobial resistance and virulence genes. **Agent-first**: every output is
4
+ > contigs and reads for known genes. **Agent-first**: every output is
5
5
  > machine-readable (JSON / Markdown) by design, not as an afterthought.
6
6
 
7
7
  ## 1. Mission
@@ -70,16 +70,26 @@ gapit/
70
70
  │ ├── dbcodec.py # gapit/v1 tagged-header codec (percent-encoded ids)
71
71
  │ ├── records.py # records.jsonl truth store + gapit.manifest/1 provenance
72
72
  │ ├── dbbuild.py # deterministic native-db build pipeline with self-check
73
+ │ ├── clusterbuild.py # cluster-db build pipeline: GBK/GFF → locus FASTA + features.json + typing copy
74
+ │ ├── cluster.py # cluster-screening engine core: minimap2 asm20 → per-locus/per-gene calls
75
+ │ ├── cluster_math.py # cs-walk → union coverage/identity math + verdicts (pure functions)
73
76
  │ ├── db_ops.py # db use-cases: provider fetch + list (shared CLI + MCP; no typer)
74
77
  │ ├── db_query_ops.py # db use-cases: search + outdated over installed DBs (shared CLI + MCP)
75
- │ ├── db_build_ops.py # db use-case: custom FASTA+TSV → native db build (shared CLI + MCP)
78
+ │ ├── db_build_ops.py # db use-case: custom FASTA+TSV → gene db, GBK/GFF → cluster db (shared CLI + MCP)
79
+ │ ├── gbfeatures.py # GenBank FEATURES/ORIGIN parser → LocusFeatures/GeneFeature + gapit.features/1
80
+ │ ├── gffparse.py # GFF3 parser (embedded ##FASTA or sidecar) → the same locus/gene models
81
+ │ ├── typing_models.py # gapit.typing/1 scoring schema + the evaluation-result models
82
+ │ ├── typing_engine.py # gapit.typing/1 evaluation: rule scoring → phenotype call + breakdown
76
83
  │ ├── blast.py # blastn invocation + tabular output parsing
77
84
  │ ├── hits.py # Hit model, identity/coverage computation, filtering, dedup
85
+ │ ├── fragments.py # opt-in --merge-fragments cross-contig gene-fragment merging
78
86
  │ ├── minimap.py # COVERAGE_MAP construction (exact abricate arithmetic)
79
87
  │ ├── minimap2_run.py # minimap2 invocation layer for reads mode (streaming PAF, --cs/NM tags)
80
88
  │ ├── paf.py # PAF row parsing + interval arithmetic (minimap2 output boundary)
81
89
  │ ├── report.py # Report model: the canonical in-memory result
82
- │ ├── screening.py # blastn screen use-case + shared engine helpers (OutputFormat, AlignerEnum)
90
+ │ ├── engines.py # shared OutputFormat / AlignerEnum (leaf module for the three engines)
91
+ │ ├── screening.py # blastn screen use-case + the shared run_screen dispatcher
92
+ │ ├── screening_cluster.py # cluster screen use-case: kind guards, typing wiring, rendering
83
93
  │ ├── screening_reads.py # minimap2 use-cases: --r1/--r2 reads + --aligner minimap2 assemblies
84
94
  │ ├── reads.py # FASTQ mode: minimap2 PAF parsing, coverage breadth/depth, presence
85
95
  │ ├── summary.py # summary core: parse report tables into a gene matrix
@@ -98,11 +108,15 @@ gapit/
98
108
  │ │ ├── tsv.py # abricate-compatible TSV/CSV
99
109
  │ │ ├── json.py # versioned JSON (gapit.report/1 et al.)
100
110
  │ │ ├── md.py # Markdown (human + agent readable, YAML frontmatter)
111
+ │ │ ├── cluster.py # gapit.cluster/1 models + JSON/TSV renderers (typed variant)
112
+ │ │ ├── cluster_md.py # gapit.cluster/1 Markdown renderer (Phenotype column when typed)
101
113
  │ │ ├── schemas.py # registered output models behind `gapit schema`
102
114
  │ │ └── summary.py # summary matrix renderers (TSV/CSV/JSON/MD)
103
- │ ├── providers/ # 12 DB providers + common.py helpers + snapshots.py loader
115
+ │ ├── providers/ # 16 DB providers (12 gene + 4 kaptive cluster) + common.py + snapshots.py
104
116
  │ ├── data/snapshots/ # bundled card + vfdb snapshot archives (.tar.gz)
105
117
  │ └── py.typed
118
+ ├── scripts/
119
+ │ └── cluster_calibration.py # developer calibration harness for typed cluster dbs
106
120
  └── tests/
107
121
  ├── data/ # tiny synthetic db + contigs + reads (fast, offline)
108
122
  ├── golden/ # expected outputs incl. abricate reference TSVs
@@ -142,7 +156,9 @@ This is what distinguishes gapit from abricate. Treat it as a public API.
142
156
  - **DB acquisition** `[gapit-extension]`: `gapit db fetch|list|search|outdated|build|install` —
143
157
  provider fetch (bundled card/vfdb snapshots install offline; `--from-source` forces upstream),
144
158
  provider listing, records.jsonl gene search, staleness report, custom FASTA→native-db build,
145
- and SHA256-verified local-file install.
159
+ GBK/GFF→cluster-db build (`--typing` installs a validated `gapit.typing/1` spec), the four
160
+ download-on-fetch kaptive cluster providers (GPL content, never bundled), and SHA256-verified
161
+ local-file install.
146
162
  - **MCP** `[gapit-extension]`: `gapit mcp` / `gapit-mcp` stdio server exposing nine tools:
147
163
  read-only `screen` (incl. `aligner minimap2` assembly survey), `screen_reads` (FASTQ via
148
164
  minimap2), `summary`, `schema`, `db_list`, `db_search`, `db_outdated`, plus the datadir-mutating
@@ -175,7 +191,9 @@ This is what distinguishes gapit from abricate. Treat it as a public API.
175
191
  query contigs against one db → 15-field
176
192
  tabular hits → filter by identity / coverage thresholds → **dedup hits sharing identical
177
193
  `(contig, qstart, qend)`** (first/best BLAST row wins) → one TSV row per surviving hit.
178
- abricate does **not** merge overlapping intervals — do not "improve" this on the default path.
194
+ abricate does **not** merge overlapping intervals — do not "improve" this on the default path (the
195
+ opt-in `--merge-fragments` cross-contig extension is the one sanctioned exception:
196
+ `fragments.py`).
179
197
  - Key computed fields: `%COVERAGE = 100*(length-gaps)/slen` (filtered unrounded, displayed
180
198
  `%.2f`), `%IDENTITY` (BLAST pident, never post-filtered), `COVERAGE_MAP` (15-char minimap),
181
199
  `GAPS`. Exact formulas, the dedup rule, and the minimap arithmetic live in `SPEC.md` — when
@@ -5,6 +5,58 @@ All notable changes to gapit are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
6
6
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-09-30
9
+
10
+ ### Added
11
+
12
+ - **Gene-cluster databases and cluster screening** (stages 1–3; the blastn gene path and
13
+ all existing outputs are untouched — parity stays byte-identical):
14
+ - `gapit db build NAME input.gbk|input.gff3` detects the input kind by suffix and builds
15
+ a `kind: cluster` database — locus FASTA `sequences`, `gapit.features/1` feature table
16
+ (`features.json`), makeblastdb index, manifest `kind: cluster` — with `--kind
17
+ gene|cluster` to override (usage error on contradiction). New parsers (no Biopython):
18
+ GenBank FEATURES/ORIGIN (Bakta/modern and kaptive-style source-note labels) and GFF3
19
+ (embedded `##FASTA` or sidecar); compound `join()` CDS locations are rejected with a
20
+ typed error.
21
+ - `gapit screen --db <cluster db>` dispatches to the minimap2 `asm20` cluster engine:
22
+ per-gene present/partial/absent verdicts (`--min-gene-cov`/`--min-gene-id`, default
23
+ 90/90), cs-based union coverage/identity, cross-contig fragmentation resilience, and a
24
+ best-locus call above `--min-cluster-cov` (default 96). Output is the new
25
+ `gapit.cluster/1` document (json), the cluster TSV/CSV (one row per file), or
26
+ Markdown; kind-mismatched flags are usage errors; reads mode rejects cluster
27
+ databases. The MCP `screen` tool gains `minGeneCov`/`minGeneId`/`minClusterCov`.
28
+ - `--typing FILE` installs a validated `gapit.typing/1` declarative scoring spec into a
29
+ cluster db (`typing.json`). Screening a typed database annotates every best call with
30
+ a `phenotype` and an additive `phenotype_detail` breakdown (score, confidence,
31
+ per-rule components, runner-up, ambiguity pair); the TSV gains a PHENOTYPE column
32
+ (typed runs only — untyped output stays byte-identical). Three rule kinds —
33
+ `weighted_genes`, `cluster_match`, `learned_linear` (schema only; no training code) —
34
+ behind a `cutoff` + `ambiguity_margin` + `fallback` decision layer. Unknown gene/locus
35
+ references fail with `TYPING_UNKNOWN_GENE` at build AND screen time.
36
+ - Four kaptive cluster providers, `kaptive_k`, `kaptive_o`, `kaptive_ak`, `kaptive_oc`
37
+ (Klebsiella K/O and A. baumannii K/OC antigen loci; Wyres et al. 2020 — cite Kaptive).
38
+ The database content is GPL-3.0, so nothing is bundled: fetch downloads the pinned
39
+ Kaptive v2.0.9 GenBank files and builds cluster databases; manifests record the
40
+ source URL, `license`, and citation `note`. No typing model ships with them yet
41
+ (locus calls only, phenotype null).
42
+ - `scripts/cluster_calibration.py`: developer harness that screens a typed cluster db
43
+ against a labels TSV and prints the per-expected-phenotype score distribution, the
44
+ called×expected agreement matrix, and the divergence list (the v1 loop for future
45
+ learned_linear training).
46
+ - `gapit screen --merge-fragments` (blastn contig mode only; off by default): merge gene
47
+ fragments split across contig boundaries into one reported hit when their union subject
48
+ coverage reaches `--mincov`. Merged rows carry additive optional `merged` and `fragments`
49
+ fields in `gapit.report/1` JSON plus a Markdown detail line; the default path (and all
50
+ existing output) is untouched. The MCP `screen` tool gains the matching `mergeFragments`
51
+ boolean parameter.
52
+
53
+ ## [0.3.1] - 2026-09-28
54
+
55
+ ### Added
56
+
57
+ - `-h` now works as an alias for `--help` on every command (root, subcommands,
58
+ and nested `db` sub-app).
59
+
8
60
  ## [0.3.0] - 2026-09-28
9
61
 
10
62
  ### Removed
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gapit
3
- Version: 0.3.0
4
- Summary: Mass screening of contigs for antimicrobial resistance and virulence genes — agent-first Python reimplementation of abricate
3
+ Version: 0.4.0
4
+ Summary: Mass screening of contigs and reads for known genes — agent-first Python reimplementation of abricate
5
5
  Project-URL: Homepage, https://github.com/indexofire/gapit
6
6
  Project-URL: Documentation, https://indexofire.github.io/gapit/
7
7
  Project-URL: Changelog, https://github.com/indexofire/gapit/blob/main/CHANGELOG.md
@@ -10,7 +10,7 @@ Project-URL: Issues, https://github.com/indexofire/gapit/issues
10
10
  Author-email: indexofire <indexofire@gmail.com>
11
11
  License-Expression: MIT
12
12
  License-File: LICENSE
13
- Keywords: AMR,bioinformatics,blast,genomics,virulence
13
+ Keywords: AMR,abricate,bioinformatics,blast,gene-screening,genomics,virulence
14
14
  Classifier: Development Status :: 4 - Beta
15
15
  Classifier: Intended Audience :: Science/Research
16
16
  Classifier: License :: OSI Approved :: MIT License
@@ -30,9 +30,10 @@ Description-Content-Type: text/markdown
30
30
 
31
31
  # gapit
32
32
 
33
- Mass screening of contigs and reads for antimicrobial resistance and virulence genes. An
34
- agent-first Python reimplementation of [abricate](https://github.com/tseemann/abricate):
35
- byte-compatible TSV plus first-class JSON and Markdown.
33
+ Mass screening of contigs and reads for known genes — AMR, virulence, serotype, plasmid
34
+ replicons, species targets, or any custom database. An agent-first Python reimplementation
35
+ of [abricate](https://github.com/tseemann/abricate): byte-compatible TSV plus first-class
36
+ JSON and Markdown.
36
37
 
37
38
  ## Why gapit
38
39
 
@@ -126,6 +127,12 @@ gapit also reads datadirs built by abricate itself (legacy `~~~` headers). The r
126
127
  does not hold: gapit-native databases use the `gapit/v1` header format (see SPEC.md §11),
127
128
  which abricate cannot read. Protein databases such as `bacmet2` screen through blastx.
128
129
 
130
+ Gene **cluster** databases are a second kind: `gapit db build NAME loci.gbk|gff3` builds a
131
+ `kind: cluster` db (locus calls via minimap2, optional `--typing FILE` phenotype scoring),
132
+ and the four `kaptive_k`/`kaptive_o`/`kaptive_ak`/`kaptive_oc` providers fetch the Kaptive
133
+ Klebsiella and A. baumannii antigen-locus references at install time (GPL-3.0 content, so
134
+ nothing is bundled — cite Kaptive/Wyres et al. 2020 for results).
135
+
129
136
  ## Output contract
130
137
 
131
138
  - **stdout purity.** Data on stdout, diagnostics on stderr, always. `--quiet` silences
@@ -172,7 +179,7 @@ Register it with an MCP client:
172
179
  | `pixi run lint` | ruff check |
173
180
  | `pixi run fmt` | ruff format |
174
181
  | `pixi run typecheck` | basedpyright (strict) |
175
- | `pixi run test` | pytest, 374 offline tests |
182
+ | `pixi run test` | pytest, 778 offline tests |
176
183
  | `pixi run -e parity parity` | byte-diff screening vs real abricate |
177
184
  | `pixi run -e parity summary-parity` | byte-diff summary vs real abricate |
178
185
 
@@ -146,11 +146,37 @@ consumes abricate-built datadirs.
146
146
  releases via `.github/workflows/release.yml` (OIDC trusted publishing, `workflow_dispatch`
147
147
  or `v*` tag). **Deferred by decision (2026-09-18, still open):** bioconda submission —
148
148
  recipe remains in-repo; sha256 placeholder to be replaced at submission time.
149
+ - **Bioconda preparation (2026-09-29, submission HELD pending rightsholder go):** recipe
150
+ finalized (real PyPI sdist sha256, license_file, noarch python) and parity corpus
151
+ strengthened 6 → 15 byte-identical cases (minus-strand hits, coverage/identity threshold
152
+ boundaries, blastx protein-db leg via bacmet2) — quality gate for the abricate-replacement
153
+ claim before submitting to bioconda/bioconda-recipes.
149
154
  - **Contract change (2026-09-27, rightsholder decision):** `gapit list` and its
150
155
  `gapit.list/1` document removed; `gapit db list` (`gapit.dblist/1`) is the single
151
156
  listing surface and abricate `--list` byte-parity is intentionally dropped for it.
152
157
  Schema registry is now six documents (report, reads, reads2, summary, error, version).
153
158
  Breaking; recorded under \[Unreleased\] in CHANGELOG.md for the next minor bump.
159
+ - **Gene-cluster feature, stage 2 (2026-09-30, additive):** `gapit screen` dispatches
160
+ cluster-kind databases to the minimap2 `asm20` cluster engine (`gapit.cluster/1`
161
+ document: best-locus call above `--min-cluster-cov` 96, per-gene
162
+ present/partial/absent verdicts at `--min-gene-cov`/`--min-gene-id` 90/90, cs-based
163
+ union coverage/identity, cross-contig fragmentation resilience; `phenotype` reserved
164
+ null for stage 3). Gene-kind screening is byte-identical (parity 15/15 + summary 6/6
165
+ unchanged); kind-mismatched flags are usage errors; reads mode rejects cluster dbs.
166
+ Schema registry gains `cluster` (stage 1 added `features`/`typing`). No version bump.
167
+ - **Gene-cluster feature, stage 3 (2026-09-30, additive; feature complete):** the
168
+ `gapit.typing/1` evaluation engine (`typing_engine.py`: weighted_genes / cluster_match /
169
+ learned_linear scoring, cutoff + ambiguity-margin decision with an explainable
170
+ components breakdown) fills `best.phenotype` + additive `best.phenotype_detail` in
171
+ gapit.cluster/1 on typed databases; TSV gains a PHENOTYPE column as a typed-only
172
+ renderer variant (untyped output byte-identical; goldens for both rule kinds + the
173
+ ambiguous two-locus case). Unknown gene/locus references fail TYPING_UNKNOWN_GENE at
174
+ build and screen time. Four kaptive providers (`kaptive_k`/`_o`/`_ak`/`_oc`,
175
+ klebgenomics/Kaptive v2.0.9 pinned raw URLs — v3 decentralized its dbs; GPL-3.0
176
+ content, download-on-fetch, never bundled; manifest gains optional `license`/`note`).
177
+ `scripts/cluster_calibration.py` developer harness (score distribution, agreement
178
+ matrix, divergences) seeds the future learned_linear training loop (training itself
179
+ out of scope). Parity 15/15 + summary 6/6 unchanged; no version bump.
154
180
 
155
181
  ## Milestones
156
182
 
@@ -1,8 +1,9 @@
1
1
  # gapit
2
2
 
3
- Mass screening of contigs and reads for antimicrobial resistance and virulence genes. An
4
- agent-first Python reimplementation of [abricate](https://github.com/tseemann/abricate):
5
- byte-compatible TSV plus first-class JSON and Markdown.
3
+ Mass screening of contigs and reads for known genes — AMR, virulence, serotype, plasmid
4
+ replicons, species targets, or any custom database. An agent-first Python reimplementation
5
+ of [abricate](https://github.com/tseemann/abricate): byte-compatible TSV plus first-class
6
+ JSON and Markdown.
6
7
 
7
8
  ## Why gapit
8
9
 
@@ -96,6 +97,12 @@ gapit also reads datadirs built by abricate itself (legacy `~~~` headers). The r
96
97
  does not hold: gapit-native databases use the `gapit/v1` header format (see SPEC.md §11),
97
98
  which abricate cannot read. Protein databases such as `bacmet2` screen through blastx.
98
99
 
100
+ Gene **cluster** databases are a second kind: `gapit db build NAME loci.gbk|gff3` builds a
101
+ `kind: cluster` db (locus calls via minimap2, optional `--typing FILE` phenotype scoring),
102
+ and the four `kaptive_k`/`kaptive_o`/`kaptive_ak`/`kaptive_oc` providers fetch the Kaptive
103
+ Klebsiella and A. baumannii antigen-locus references at install time (GPL-3.0 content, so
104
+ nothing is bundled — cite Kaptive/Wyres et al. 2020 for results).
105
+
99
106
  ## Output contract
100
107
 
101
108
  - **stdout purity.** Data on stdout, diagnostics on stderr, always. `--quiet` silences
@@ -142,7 +149,7 @@ Register it with an MCP client:
142
149
  | `pixi run lint` | ruff check |
143
150
  | `pixi run fmt` | ruff format |
144
151
  | `pixi run typecheck` | basedpyright (strict) |
145
- | `pixi run test` | pytest, 374 offline tests |
152
+ | `pixi run test` | pytest, 778 offline tests |
146
153
  | `pixi run -e parity parity` | byte-diff screening vs real abricate |
147
154
  | `pixi run -e parity summary-parity` | byte-diff summary vs real abricate |
148
155
 
@@ -13,7 +13,7 @@ Version, as one JSON line:
13
13
 
14
14
  ```console
15
15
  $ gapit --version --json
16
- {"schema":"gapit.version/1","name":"gapit","version":"0.3.0"}
16
+ {"schema":"gapit.version/1","name":"gapit","version":"0.4.0"}
17
17
  ```
18
18
 
19
19
  Output schemas. Six documents are introspectable: `report`, `reads`,
@@ -184,8 +184,10 @@ unknown `--db NAME` is a usage error (exit 2) listing what is installed.
184
184
 
185
185
  ## Providers
186
186
 
187
- Twelve providers ship with gapit. `card` and `vfdb` also ship as bundled snapshots inside
188
- the package, so they install with zero network access.
187
+ Sixteen providers ship with gapit. `card` and `vfdb` also ship as bundled snapshots inside
188
+ the package, so they install with zero network access. The four kaptive providers are
189
+ **cluster** databases (downloaded and built at fetch time; GPL content is never bundled —
190
+ see [Kaptive providers](#kaptive-providers-gpl-downloaded-on-fetch)).
189
191
 
190
192
  | Name | Content | dbtype |
191
193
  |---|---|---|
@@ -201,8 +203,30 @@ the package, so they install with zero network access.
201
203
  | `bacmet2` | BacMet2 experimentally confirmed biocide/resistance genes (protein) | prot |
202
204
  | `victors` | Victors virulence factors | nucl |
203
205
  | `upec_expec_vf` | UPEC/ExPEC virulence genes (FordeGenomics) | nucl |
206
+ | `kaptive_k` | Kaptive Klebsiella K antigen loci (cluster; GPL-3.0, downloaded on fetch) | nucl |
207
+ | `kaptive_o` | Kaptive Klebsiella O antigen loci (cluster; GPL-3.0, downloaded on fetch) | nucl |
208
+ | `kaptive_ak` | Kaptive A. baumannii K antigen loci (cluster; GPL-3.0, downloaded on fetch) | nucl |
209
+ | `kaptive_oc` | Kaptive A. baumannii OC antigen loci (cluster; GPL-3.0, downloaded on fetch) | nucl |
204
210
 
205
211
  Protein databases (`bacmet2`) screen through `blastx`; nucleotide ones through `blastn`.
212
+ Cluster databases screen through the minimap2 cluster engine
213
+ ([screen.md](./screen.md#cluster-databases-kind-cluster)).
214
+
215
+ ### Kaptive providers (GPL, downloaded on fetch)
216
+
217
+ The four `kaptive_*` providers wrap the reference databases of
218
+ [Kaptive](https://github.com/klebgenomics/Kaptive) (Wyres et al., J Clin Microbiol 2020 —
219
+ please cite Kaptive when you use results from these databases). Kaptive v3 moved the
220
+ databases out of its git repository, so gapit pins the archived **v2.0.9 tag** raw URLs
221
+ (stable direct downloads, verified 2026-09-30).
222
+
223
+ The database content is **GPL-3.0**, while gapit is MIT — so nothing kaptive is ever
224
+ bundled: `gapit db fetch kaptive_k` (and `_o`, `_ak`, `_oc`) downloads the GenBank file at
225
+ fetch time and builds a `kind: cluster` database through the cluster pipeline. The manifest
226
+ records the upstream URL, the `GPL-3.0 (database content)` license, and the citation note.
227
+ No typing model ships with them (phenotype calls stay null); you get kaptive-style best
228
+ locus calls, and you can later install your own `typing.json` semantics by rebuilding with
229
+ `gapit db build --typing`.
206
230
 
207
231
  ## Fetching databases
208
232
 
@@ -345,7 +369,7 @@ versions. A real one, from the plasmidfinder database:
345
369
  "upstream_version": "",
346
370
  "tool": {
347
371
  "name": "gapit",
348
- "version": "0.3.0"
372
+ "version": "0.4.0"
349
373
  },
350
374
  "makeblastdb_version": "blastn: 2.17.0+",
351
375
  "minimap2_version": "2.31-r1302"
@@ -363,6 +387,8 @@ versions. A real one, from the plasmidfinder database:
363
387
  | `dbtype` | string | `nucl` or `prot`, chosen explicitly at build time |
364
388
  | `header_format` | string | Always `gapit/v1` for gapit-built databases |
365
389
  | `upstream_version` | string | Upstream release label when the source has one |
390
+ | `license` | string | Database-content license, present only when the provider declares one (kaptive: `GPL-3.0 (database content)`) |
391
+ | `note` | string | Free-text provenance note (kaptive: source file + citation), present only when set |
366
392
  | `tool` | object | `{name, version}` of the gapit that built the database |
367
393
  | `makeblastdb_version` | string | BLAST+ version that built the index |
368
394
  | `minimap2_version` | string | minimap2 version in the build environment (reads mode indexes in memory; no `.mmi` is built) |
@@ -527,3 +553,79 @@ abricate datadirs take the same in-memory path.
527
553
  A complete `db build` walkthrough with worked examples for every header format, protein
528
554
  databases, metadata TSVs, and a troubleshooting table lives in
529
555
  [Custom databases](./custom-db.md).
556
+
557
+ ## Cluster databases (GBK/GFF)
558
+
559
+ `gapit db build NAME input.gbk` (or `.gbff`, `.gb`, `.gff`, `.gff3`, each plain or
560
+ `.gz`/`.bz2`) builds a different kind of database: one record = one gene locus. The input
561
+ kind is detected by suffix (`--kind gene|cluster` must agree or the build is a usage
562
+ error); the FASTA gene pipeline of the previous section is untouched.
563
+
564
+ ```console
565
+ $ gapit db build mycps cps_loci.gbk --datadir ./db
566
+ gapit: generated ./db/mycps/sequences
567
+ gapit: self-check passed for mycps
568
+ gapit: BLAST index built (nucl)
569
+ {"db":"mycps","records":2,"dbtype":"nucl","destination":"./db/mycps"}
570
+ ```
571
+
572
+ Parsing (no Biopython): GenBank records become loci (`LOCUS` name = locus id) with their
573
+ `CDS` features as genes; the source-feature notes carry the label and type (kaptive
574
+ `K locus:`/`K type:` style, with Bakta-style fallbacks to the locus id). GFF3 inputs take
575
+ the sequences from an embedded `##FASTA` block or a `<stem>.fa/.fna/.fasta` sidecar.
576
+ Compound `join()` CDS locations are rejected with a typed error — split the record or use
577
+ simple CDS annotations.
578
+
579
+ The artifacts differ from a gene database: `sequences` holds one bare-locus-id record per
580
+ locus (plain headers — abricate cannot read these), `features.json` is the
581
+ `gapit.features/1` feature table (introspect with `gapit schema features`), the manifest
582
+ declares `kind: cluster`, and there is no `records.jsonl`. Screening dispatches to the
583
+ minimap2 cluster engine ([screen.md](./screen.md#cluster-databases-kind-cluster)).
584
+
585
+ ### typing.json — declarative phenotype scoring
586
+
587
+ `--typing FILE` installs a validated `gapit.typing/1` document into the database as
588
+ `typing.json`; screening then annotates every best call with a phenotype and an explainable
589
+ score breakdown (the PHENOTYPE TSV column and `phenotype_detail` in gapit.cluster/1 —
590
+ [outputs.md](./outputs.md#gapitcluster1-cluster-database-screening)). A minimal
591
+ `weighted_genes` example:
592
+
593
+ ```json
594
+ {
595
+ "schema": "gapit.typing/1",
596
+ "rules": [
597
+ {
598
+ "model": "weighted_genes",
599
+ "phenotype": "K1",
600
+ "weights": {"wzx": 1.0, "wzy": 3.0},
601
+ "negative": {"rfaD": -1.0},
602
+ "identity_floor": 95.0,
603
+ "require_any": ["wzx", "wzy"]
604
+ }
605
+ ],
606
+ "cutoff": 0.9,
607
+ "ambiguity_margin": 0.05,
608
+ "fallback": "unknown"
609
+ }
610
+ ```
611
+
612
+ Three rule kinds exist — `weighted_genes` (gene presence with an identity floor, negative
613
+ markers, and an any-of gate), `cluster_match` (weighted locus coverage/identity/key-genes
614
+ components with per-component floors), and `learned_linear` (a trained sigmoid over named
615
+ features `gene:<id>:<cov|ident|present>` / `cluster:<locus>:<coverage|identity>`) — plus
616
+ the document-level `cutoff`, `ambiguity_margin`, and `fallback` the decision layer applies.
617
+ The full schema prints with `gapit schema typing`. Validation is strict at build time: a
618
+ malformed document fails with `TYPING_MALFORMED`, and a rule referencing a gene or locus
619
+ the input does not carry fails with `TYPING_UNKNOWN_GENE` before any artifact is written
620
+ (the same check reruns at screen time, so hand-edited databases cannot smuggle dead
621
+ references).
622
+
623
+ Tuning a typing document against labeled assemblies is what
624
+ `scripts/cluster_calibration.py` is for (developer tool): it screens every labeled sample,
625
+ prints the per-expected-phenotype score distribution, the called×expected agreement
626
+ matrix, and the divergence list — the loop a future `learned_linear` training flow will
627
+ consume:
628
+
629
+ ```console
630
+ $ python scripts/cluster_calibration.py mycps --datadir ./db labels.tsv
631
+ ```
@@ -1,9 +1,9 @@
1
1
  # gapit documentation
2
2
 
3
- gapit screens contig files and FASTQ reads for antimicrobial resistance (AMR) and virulence
4
- genes. It is an agent-first Python reimplementation of
5
- [abricate](https://github.com/tseemann/abricate): abricate-compatible TSV on stdout, plus
6
- first-class JSON and Markdown outputs with versioned schemas.
3
+ gapit screens contig files and FASTQ reads for known genes — AMR, virulence, serotype,
4
+ plasmid replicons, species targets, or any custom database. It is an agent-first Python
5
+ reimplementation of [abricate](https://github.com/tseemann/abricate): abricate-compatible
6
+ TSV on stdout, plus first-class JSON and Markdown outputs with versioned schemas.
7
7
 
8
8
  ## Pages
9
9
 
@@ -14,7 +14,7 @@ first-class JSON and Markdown outputs with versioned schemas.
14
14
  | [Screening contigs](./screen.md) | `gapit screen` on FASTA/GBK/EMBL inputs: thresholds, filters, formats |
15
15
  | [Screening reads](./reads.md) | FASTQ and assembly FASTA through minimap2: `--r1`/`--r2`, presets, breadth-based presence, two-stage survey |
16
16
  | [Summary](./summary.md) | `gapit summary`: gene presence/absence matrix across report tables |
17
- | [Databases](./databases.md) | Providers, `gapit db fetch/list/install`, datadirs, native `gapit/v1` format |
17
+ | [Databases](./databases.md) | Providers, `gapit db fetch/list/install`, datadirs, native `gapit/v1` format, cluster databases (GBK/GFF, typing.json, kaptive) |
18
18
  | [Custom databases](./custom-db.md) | `gapit db build` walkthrough: any FASTA to a screenable database, worked examples |
19
19
  | [Outputs](./outputs.md) | TSV/JSON/Markdown formats, schemas, error envelopes, exit codes |
20
20
  | [MCP server](./mcp.md) | `gapit mcp`: analysis + database tools for agent runtimes over stdio JSON-RPC |
@@ -25,13 +25,13 @@ first-class JSON and Markdown outputs with versioned schemas.
25
25
 
26
26
  | Command | What it does | Docs |
27
27
  |---|---|---|
28
- | `gapit screen` | Screen contig files or FASTQ reads for AMR and virulence genes | [Screening](./screen.md) |
28
+ | `gapit screen` | Screen contig files or FASTQ reads for known genes | [Screening](./screen.md) |
29
29
  | `gapit summary` | Summarize report table(s) into a gene presence/absence matrix | [Summary](./summary.md) |
30
- | `gapit db fetch` | Fetch and build provider database(s) into the datadir | [Databases](./databases.md) |
30
+ | `gapit db fetch` | Fetch and build provider database(s) into the datadir (kaptive cluster dbs download on fetch) | [Databases](./databases.md) |
31
31
  | `gapit db list` | List database providers and their installed state | [Databases](./databases.md) |
32
32
  | `gapit db install` | Install a local file after verifying its SHA256 | [Databases](./databases.md) |
33
33
  | `gapit setupdb` | Build BLAST indices for all databases under the datadir | here |
34
- | `gapit schema` | Print the JSON Schema of a gapit output document (`report`, `reads`, `reads2`, `summary`, `error`, `version`) | [Outputs](./outputs.md) |
34
+ | `gapit schema` | Print the JSON Schema of a gapit output document (`report`, `reads`, `reads2`, `cluster`, `summary`, `error`, `version`, `features`, `typing`) | [Outputs](./outputs.md) |
35
35
  | `gapit mcp` | Run the MCP stdio server (also installed as the `gapit-mcp` console script) | [MCP server](./mcp.md) |
36
36
 
37
37
  ## Project documents
@@ -44,9 +44,9 @@ gapit --version
44
44
 
45
45
  ```console
46
46
  $ gapit --version
47
- gapit 0.3.0
47
+ gapit 0.4.0
48
48
  $ gapit --version --json
49
- {"schema":"gapit.version/1","name":"gapit","version":"0.3.0"}
49
+ {"schema":"gapit.version/1","name":"gapit","version":"0.4.0"}
50
50
  ```
51
51
 
52
52
  ## External binaries
@@ -37,10 +37,10 @@ transcribed from the live `tools/list` `inputSchema` objects.
37
37
 
38
38
  | Tool | Arguments | Returns |
39
39
  |---|---|---|
40
- | `screen` | `files` (array of strings, required), `db` (string, default `ncbi`), `minid` (number), `mincov` (number), `format` (string: `json` \| `tsv` \| `md`, default `json`), `aligner` (string: `blastn` \| `minimap2`, default `blastn`), `min_breadth` (number 0–100, default `90`; minimap2 only), `min_identity` (number 0–100, default `0`; minimap2 only), `min_mapq` (integer ≥ 0, default `0`; minimap2 only), `datadir` (string) | Report text: `gapit.report/1` JSON by default, TSV or Markdown per `format`; `aligner minimap2` runs the reads engine and emits `gapit.reads/1` |
40
+ | `screen` | `files` (array of strings, required), `db` (string, default `ncbi`), `minid` (number), `mincov` (number), `format` (string: `json` \| `tsv` \| `md`, default `json`), `aligner` (string: `blastn` \| `minimap2`, default `blastn`), `mergeFragments` (boolean, default `false`; blastn only — cross-contig fragment merging), `min_breadth` (number 0–100, default `90`; minimap2 only), `min_identity` (number 0–100, default `0`; minimap2 only), `min_mapq` (integer ≥ 0, default `0`; minimap2 only), `minGeneCov` (number 0–100, default `90`; cluster dbs only), `minGeneId` (number 0–100, default `90`; cluster dbs only), `minClusterCov` (number 0–100, default `96`; cluster dbs only), `datadir` (string) | Report text: `gapit.report/1` JSON by default, TSV or Markdown per `format`; `aligner minimap2` runs the reads engine and emits `gapit.reads/1`. A cluster-kind `db` emits `gapit.cluster/1`: best-locus call, per-gene verdicts, and — when the database carries a `typing.json` — the phenotype call with its `phenotype_detail` score breakdown |
41
41
  | `screen_reads` | `r1` (array of strings, required — one path per lane), `r2` (array of strings, same count as `r1`), `read_type` (string: `sr` \| `map-ont` \| `map-hifi`, default `sr`), `min_breadth` (number 0–100, default `90`), `min_identity` (number 0–100, default `0`), `min_mapq` (integer ≥ 0, default `0`), `format` (string: `json` \| `md`, default `json`), `db` (string, default `ncbi`), `datadir` (string) | `gapit.reads/1` JSON by default or Markdown per `format`; `min_identity`/`min_mapq` > 0 switches to `gapit.reads/2` |
42
42
  | `summary` | `files` (array of report table paths, required), `identity` (boolean), `nopath` (boolean) | `gapit.summary/1` JSON |
43
- | `schema` | `name` (string, required, one of `error`, `reads`, `reads2`, `report`, `summary`, `version`) | The JSON Schema of that output document |
43
+ | `schema` | `name` (string, required, one of `cluster`, `error`, `features`, `reads`, `reads2`, `report`, `summary`, `typing`, `version`) | The JSON Schema of that output document |
44
44
  | `db_list` | none | `gapit.dblist/1`: provider names, install state, record counts |
45
45
  | `db_fetch` | `name` (string), `datadir` (string), `force` (boolean, default `false`) | One JSON receipt line per database (`db`, `records`, `dbtype`, `destination`). Name omitted: the card+vfdb default set from bundled snapshots. Network installs can take minutes |
46
46
  | `db_build` | `name` (string, required), `fasta` (string, required — a LOCAL filesystem path), `tsv` (string), `dbtype` (string: `nucl` \| `prot`), `description` (string), `datadir` (string), `force` (boolean, default `false`) | One JSON receipt line (`db`, `records`, `dbtype`, `destination`) |
@@ -94,14 +94,14 @@ EOF
94
94
  Response line 1, verbatim:
95
95
 
96
96
  ```text
97
- {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"gapit","version":"0.3.0"}}}
97
+ {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"gapit","version":"0.4.0"}}}
98
98
  ```
99
99
 
100
100
  Response line 2 (real output, elided in the middle; each tool carries its full
101
101
  `inputSchema`):
102
102
 
103
103
  ```text
104
- {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"screen","description":"Screen contig files for AMR/virulence genes (json = gapit.report/1; aligner minimap2 = fast assembly survey emitting gapit.reads/1).","inputSchema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string"}},"db":{"type":"string","default":"ncbi"}, ... },"required":["files"]}}, {"name":"screen_reads","description":"Screen FASTQ reads for genes via minimap2 (json = gapit.reads/1; min_identity/min_mapq > 0 emits gapit.reads/2). Returns the rendered document.","inputSchema":{"type":"object","properties":{"r1":{"type":"array","items":{"type":"string"}},"r2":{"type":"array","items":{"type":"string"}},"read_type":{"type":"string","enum":["sr","map-ont","map-hifi"],"default":"sr"}, ... },"required":["r1"]}}, {"name":"summary", ...}, {"name":"schema", ...}, {"name":"db_list", ...}, {"name":"db_fetch", ...}, {"name":"db_build", ...}, {"name":"db_search", ...}, {"name":"db_outdated", ...}]}}
104
+ {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"screen","description":"Screen contig files for known genes (json = gapit.report/1; aligner minimap2 = fast assembly survey emitting gapit.reads/1).","inputSchema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string"}},"db":{"type":"string","default":"ncbi"}, ... },"required":["files"]}}, {"name":"screen_reads","description":"Screen FASTQ reads for genes via minimap2 (json = gapit.reads/1; min_identity/min_mapq > 0 emits gapit.reads/2). Returns the rendered document.","inputSchema":{"type":"object","properties":{"r1":{"type":"array","items":{"type":"string"}},"r2":{"type":"array","items":{"type":"string"}},"read_type":{"type":"string","enum":["sr","map-ont","map-hifi"],"default":"sr"}, ... },"required":["r1"]}}, {"name":"summary", ...}, {"name":"schema", ...}, {"name":"db_list", ...}, {"name":"db_fetch", ...}, {"name":"db_build", ...}, {"name":"db_search", ...}, {"name":"db_outdated", ...}]}}
105
105
  ```
106
106
 
107
107
  Response line 3 (real output, text content elided). The screen result rides in
@@ -117,7 +117,7 @@ the same run):
117
117
  ```json
118
118
  {
119
119
  "schema": "gapit.report/1",
120
- "tool": {"name": "gapit", "version": "0.3.0"},
120
+ "tool": {"name": "gapit", "version": "0.4.0"},
121
121
  "created_at": "2026-09-19T01:15:44Z",
122
122
  "params": {"db": "tinyamr", "minid": 80.0, "mincov": 80.0, "threads": 1},
123
123
  "files": [
@@ -173,7 +173,7 @@ the same run):
173
173
  ```json
174
174
  {
175
175
  "schema": "gapit.reads/1",
176
- "tool": {"name": "gapit", "version": "0.3.0"},
176
+ "tool": {"name": "gapit", "version": "0.4.0"},
177
177
  "created_at": "2026-09-22T00:07:35Z",
178
178
  "params": {
179
179
  "db": "tinyreads",
@@ -248,7 +248,7 @@ hit is the gene the agent just built the database from (real output):
248
248
  ```json
249
249
  {
250
250
  "schema": "gapit.report/1",
251
- "tool": {"name": "gapit", "version": "0.3.0"},
251
+ "tool": {"name": "gapit", "version": "0.4.0"},
252
252
  "created_at": "2026-09-20T13:38:39Z",
253
253
  "params": {"db": "myamr", "minid": 80.0, "mincov": 80.0, "threads": 1},
254
254
  "files": [