{
  "schema_version": 1,
  "registry": "aiscientist-skills",
  "registry_version": "0.2.1",
  "generator": "aiscientist-skills 0.2.1",
  "categories": [
    {
      "name": "crispr-screens",
      "description": "Pooled CRISPR screens with a single-cell readout (Perturb-seq): escaping cells, per-perturbation differential expression and effect sizes."
    },
    {
      "name": "knowledge-graph",
      "description": "Biomedical knowledge graphs: resolving entities, reading typed relationships with their evidence and provenance, and finding datasets to analyze."
    },
    {
      "name": "literature",
      "description": "Scientific literature: recovering, verifying and assembling full-text paper corpora for review and retrieval."
    },
    {
      "name": "single-cell",
      "description": "Single-cell and single-nucleus RNA-seq: QC, integration, annotation and downstream analysis."
    },
    {
      "name": "variants",
      "description": "DNA variant analysis from VCF files: normalization, callset QC, result tables, research triage and variant databases."
    }
  ],
  "skills": [
    {
      "name": "compare-perturbations-to-control",
      "category": "crispr-screens",
      "path": "skills/crispr-screens/compare-perturbations-to-control",
      "version": "1.0.0",
      "status": "community",
      "description": "Per-perturbation differential expression against one shared non-targeting control in a pooled CRISPR screen with single-cell readout (Perturb-seq, CROP-seq, CRISPRi or CRISPRa) with scanpy rank_genes_groups (Wilcoxon, explicit reference), from an AnnData with a guide or perturbation column to one DE table per perturbation, a summary table with a target self-knockdown positive control, and the genes shared across perturbations. Use when every guide or target gene must be compared with the same control cells (the AiScientist run_de tool does per-cluster one-vs-rest and does not do this), when collapsing guides to target genes, or when checking which guides lowered their own target.",
      "tags": [
        "crispr",
        "perturb-seq",
        "crop-seq",
        "scrna-seq",
        "differential-expression",
        "scanpy",
        "wilcoxon"
      ],
      "license": "MIT",
      "tested_with": "scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, scipy 1.17.1, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 0.5 GB RAM, seconds for 270 cells x 49 genes (4 perturbations)",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "filter-escapers-with-mixscape",
      "category": "crispr-screens",
      "path": "skills/crispr-screens/filter-escapers-with-mixscape",
      "version": "1.0.0",
      "status": "community",
      "description": "Label and remove escaping cells in a pooled CRISPR screen with single-cell readout (Perturb-seq, CROP-seq) with Mixscape from pertpy, from a log-normalized AnnData with a guide or perturbation column and a non-targeting control to adata_mixscape.h5ad without the non-perturbed (NP) cells, plus the NP fraction of every perturbation. Use before per-perturbation differential expression when guide-assigned cells may have escaped knockout or knockdown and dilute the comparison, or when asked which cells of a screen were actually perturbed. Optional step with a heavy dependency; without pertpy the script exits cleanly and the DE runs on all guide-assigned cells.",
      "tags": [
        "crispr",
        "perturb-seq",
        "crop-seq",
        "scrna-seq",
        "mixscape",
        "pertpy",
        "escaping-cells"
      ],
      "license": "MIT",
      "tested_with": "pertpy 1.4.0, scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, scipy 1.17.1, scikit-learn 1.9.1, pynndescent 0.6.0, numba 0.68.0, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, 0.85 GB peak RAM, about 12 s for 240 cells x 47 genes (mostly pertpy import and JIT compilation)",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "rank-perturbations-by-edistance",
      "category": "crispr-screens",
      "path": "skills/crispr-screens/rank-perturbations-by-edistance",
      "version": "1.0.0",
      "status": "community",
      "description": "Rank the perturbations of a pooled CRISPR screen with single-cell readout (Perturb-seq, CROP-seq, CRISPRi or CRISPRa) by E-distance (energy distance) to the non-targeting control in PCA space, with a label-permutation test and Benjamini-Hochberg correction, from an AnnData with a guide or perturbation column to a ranked CSV, the list of hits to carry into differential expression, and an optional perturbation-by-perturbation distance matrix. Use when asking which guides actually changed the transcriptome, when separating real hits from silent guides before per-perturbation DE, or when grouping perturbations with similar effects.",
      "tags": [
        "crispr",
        "perturb-seq",
        "crop-seq",
        "scrna-seq",
        "e-distance",
        "energy-distance",
        "permutation-test",
        "scperturb"
      ],
      "license": "MIT",
      "tested_with": "scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, scikit-learn 1.9.0, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 0.5 GB RAM, seconds for 270 cells x 60 genes with 1000 permutations; about 0.1 s per perturbation per 1000 permutations with 2000 control cells and 30 PCs",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "answer-eye-questions-with-visionkb-kg",
      "category": "knowledge-graph",
      "path": "skills/knowledge-graph/answer-eye-questions-with-visionkb-kg",
      "version": "0.0.1",
      "status": "community",
      "description": "Answer chat-style knowledge questions about eye genes, cell types, diseases and datasets from the VisionKB knowledge graph through its read-only HTTP API or the equivalent visionkb-kg MCP tools, citing the KG release and dataset versions. Resolves a gene or cell type to a CURIE, reads per-class and per-cell-type expression from the HECA atlas summaries, reads typed Biolink relationships with knowledge_level, sources and PMIDs, opens the evidence behind an edge, and finds atlases with ready bind records, keeping atlas observations apart from curated claims. Use for questions such as which retinal cell types express LRAT, which class has the highest RHO, which diseases and PMIDs are recorded for PRPF31, or which atlases contain RPE cells. Not for new analysis of cell-level data.",
      "tags": [
        "knowledge-graph",
        "biolink",
        "gene-expression",
        "retina",
        "eye",
        "cell-atlas",
        "disease-genes",
        "provenance",
        "rest-api",
        "mcp"
      ],
      "license": "MIT",
      "tested_with": "visionkb-api 0.1.1 (image digest sha256:5d956d03...), visionkb-deploy 0.1.1, KG release visionkb-2026.11.0, Neo4j 2026.10.1 Community, PostgreSQL 17.11, httpx 0.28.1, Python 3.13.18, curl 8.23.0, jq 1.8.2",
      "author": "Jin Li",
      "requirements": {
        "compute": "none on the client: the four worked questions (roughly 100 HTTP requests, most of them evidence lookups) took 8-18 s wall (four runs) against a stack on the same node; the server stack used 4 GB Neo4j heap and 8 GB page cache",
        "gpu": false,
        "scheduler": "none",
        "network": "runtime",
        "secrets": [
          "VISIONKB_API_TOKEN_FILE"
        ],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "read-whole-corpus-with-claude",
      "category": "literature",
      "path": "skills/literature/read-whole-corpus-with-claude",
      "version": "0.0.1",
      "status": "community",
      "description": "Answer a question from a chunked full-text literature corpus completely, by having Claude Haiku read every passage (not the top-k of a search) and return the numbers of the sentences that bear on it, so every quote is verbatim. Writes evidence.csv (each bearing passage with its sentences, PMID, page and citation), a read log, measured tokens and cost, and for a gene-list question a table of every HGNC gene those sentences name. Tested on the RetiGene corpus of inherited retinal disease papers (39,563 passages). Use when a question needs every paper that reports something (every gene with a property, every study of a variant or phenotype), when a RAG or PaperQA answer built from the best 10-30 chunks may be incomplete, to check how complete such an answer is, or to estimate the cost and time of reading a corpus before running it. Not for a quick lookup of one fact.",
      "tags": [
        "literature",
        "rag",
        "corpus",
        "full-text",
        "evidence",
        "claude",
        "haploinsufficiency",
        "retina"
      ],
      "license": "MIT",
      "tested_with": "anthropic 1.13.0 (Python SDK), claude-haiku-5-5 at effort medium, Python 3.13.3",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "whole RetiGene corpus (39,563 passages, one question): 4,012 calls, 408 s with 48 parallel calls, $5.31 (Claude Haiku 5.5 at $0.10 / $0.50 per million input / output tokens); a 3,270-passage sample: 327 calls, 42 s, $0.47; estimate and gene table: seconds on one CPU",
        "gpu": false,
        "scheduler": "none",
        "network": "runtime",
        "secrets": [
          "ANTHROPIC_API_KEY"
        ],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "recover-paper-pdfs",
      "category": "literature",
      "path": "skills/literature/recover-paper-pdfs",
      "version": "1.0.0",
      "status": "community",
      "description": "Recover and assemble a corpus of scientific paper PDFs from a list of PMIDs or DOIs through a ladder of legal sources (open-access APIs, PMC and Europe PMC, publisher open access, then the user's institutional access through the campus network, VPN or library discovery search), verifying every PDF and tracking each paper in a manifest. Use when someone needs to download, recover, back-fill or assemble full-text PDFs from PMIDs or DOIs, especially to build a literature corpus for RAG or PaperQA, when papers are paywalled and need institutional access, or when they say \"download these papers\", \"get the full-text PDFs\" or \"the missing papers\". Use it even for a few papers; the value is trying the cheap open sources first and escalating only when needed.",
      "tags": [
        "literature",
        "pdf",
        "pubmed",
        "open-access",
        "corpus",
        "rag",
        "paperqa"
      ],
      "license": "MIT",
      "tested_with": "a 1,749-PMID corpus run (2026): NCBI E-utilities, Unpaywall, OpenAlex, Semantic Scholar, poppler, ocrmypdf, pypdf, Chrome (versions not recorded)",
      "author": "Ziyao Ma",
      "requirements": {
        "compute": "",
        "gpu": false,
        "scheduler": "none",
        "network": "runtime",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": false,
      "has_tests": false
    },
    {
      "name": "annotate-clusters-by-markers",
      "category": "single-cell",
      "path": "skills/single-cell/annotate-clusters-by-markers",
      "version": "1.0.0",
      "status": "community",
      "description": "Reference-free cell-type annotation of clusters from a curated marker panel with scanpy. Signature scores (score_genes) give a z-scored first pass, and the final label is decided on raw log-normalised expression of a few lineage-specific discriminator genes, so lineages that share markers are not mislabelled and clusters with no coherent signal stay Unassigned. Takes a clustered AnnData and writes per-cluster labels with the first-pass call, any correction and its evidence (JSON), a score matrix, a composition table and an annotated h5ad. Use when labelling clusters by markers instead of transferring labels from a reference atlas, when building a marker panel and discriminator table for a tissue, or when a score argmax gives suspicious labels. In AiScientist the built-in run_marker_annotation tool runs this procedure; use the script to change the procedure itself.",
      "tags": [
        "scrna-seq",
        "snrna-seq",
        "cell-type-annotation",
        "marker-genes",
        "scanpy",
        "retina"
      ],
      "license": "MIT",
      "tested_with": "scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 1 GB RAM (0.3 GB peak), about 2 s for 240 cells x 181 genes",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "compare-conditions-by-celltype",
      "category": "single-cell",
      "path": "skills/single-cell/compare-conditions-by-celltype",
      "version": "1.0.0",
      "status": "community",
      "description": "Condition-vs-control differential expression within each cell type (for example KO vs WT, or disease vs control, per cell type) with scanpy rank_genes_groups (Wilcoxon, explicit reference group). Takes an AnnData with a condition column and an existing cell-type column and writes per-cell-type DE tables, a volcano plot per cell type, a summary table, the genes changed in the same direction in two or more cell types, and the de_KEY_all.csv and universe hand-off files that enrichment reads. Use for a stratified condition comparison outside AiScientist, or inside it when the design is one the built-in tool cannot express (a paired or covariate-adjusted comparison, a custom shared-signature rule, a non-standard figure). In AiScientist, run_de with groupby, reference and stratify_by covers the common case.",
      "tags": [
        "scrna-seq",
        "snrna-seq",
        "differential-expression",
        "scanpy",
        "wilcoxon",
        "cell-type"
      ],
      "license": "MIT",
      "tested_with": "scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, matplotlib 3.10.9, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 1 GB RAM (0.3 GB peak), about 2 s for 260 cells x 50 genes",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "compare-two-groups-with-scanpy",
      "category": "single-cell",
      "path": "skills/single-cell/compare-two-groups-with-scanpy",
      "version": "1.0.0",
      "status": "community",
      "description": "Differential expression between two named groups of cells (for example disease vs control, or cluster 3 vs cluster 5) with scanpy rank_genes_groups (Wilcoxon) and an explicit reference group, from an AnnData file to a ranked CSV table and a JSON summary. Use when a question compares exactly two groups that a per-cluster one-vs-rest test does not answer, or when choosing the group column, the two groups and the significance thresholds for such a comparison.",
      "tags": [
        "scrna-seq",
        "snrna-seq",
        "differential-expression",
        "scanpy",
        "wilcoxon"
      ],
      "license": "MIT",
      "tested_with": "scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 1 GB RAM, seconds for 120 cells x 40 genes",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "crossvalidate-scgpt-vs-leiden",
      "category": "single-cell",
      "path": "skills/single-cell/crossvalidate-scgpt-vs-leiden",
      "version": "1.0.0",
      "status": "community",
      "description": "Cross-validate scGPT per-cell cell-type predictions against the data's own structure. Joins the scGPT predictions CSV to a clustered AnnData by barcode, never by row position (scGPT labels every raw cell, QC keeps a subset), and for the Leiden clusters and any existing majorclass or celltype labels writes a reference-by-scGPT confusion table, each group's top scGPT label and purity, an exact-string agreement and the scGPT confidence distribution, as CSV and JSON. Use after scGPT label transfer (for example AiScientist's scgpt_annotate) to judge which predicted labels the data supports, to compare scGPT labels with labels the dataset already carries, or when merging predictions onto an AnnData fails with a length or alignment error.",
      "tags": [
        "scrna-seq",
        "snrna-seq",
        "cell-type-annotation",
        "scgpt",
        "clustering",
        "validation"
      ],
      "license": "MIT",
      "tested_with": "scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 1 GB RAM (0.3 GB peak), seconds for 100 predicted and 85 clustered cells",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "explore-atlas-expression-with-aiscientist-mcp",
      "category": "single-cell",
      "path": "skills/single-cell/explore-atlas-expression-with-aiscientist-mcp",
      "version": "0.0.1",
      "status": "community",
      "description": "Answer a gene expression query such as \"show LRAT across retinal cell types\" on a large single-cell or single-nucleus atlas .h5ad with raw counts, by calling the atlas-expression-exploration MCP server from the aiscientist-mcp repository. Its tools resolve gene names (case, HGNC alias, previous symbol, ID), run the full-atlas scanpy step on a big-memory Slurm node, and return donor-aware summary tables at major-class, cell-type and region-by-class resolution, scanpy dot, violin and UMAP feature plots, and a short Markdown report. Use when asked where a gene is expressed in an atlas, to compare a few genes across annotated cell types, to produce standard expression figures for a gene list, or to install or register this MCP server. Not a differential expression test.",
      "tags": [
        "scrna-seq",
        "snrna-seq",
        "gene-expression",
        "mcp",
        "scanpy",
        "dot-plot",
        "violin-plot",
        "umap",
        "cell-atlas",
        "retina"
      ],
      "license": "MIT",
      "tested_with": "aiscientist-mcp common 0.0.2, atlas-expression-exploration 0.0.2 (conda) and 0.0.3 (image), mcp 2.3.0, scanpy 1.12.5, anndata 0.13.4, Python 3.13.18, Claude Code 2.1.309",
      "author": "Jin Li",
      "requirements": {
        "compute": "full-atlas step: Slurm job, 4 CPUs, 300 GB requested (218 GiB peak), 10.5-12.5 min for 3.18M nuclei; plot and report calls 5-9 min each from a conda env on a network filesystem (mostly server start and imports), 9-19 s each in the image on a 40,000-nucleus subset",
        "gpu": false,
        "scheduler": "optional",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": false,
      "has_tests": false
    },
    {
      "name": "prioritize-disease-genes-with-atlas",
      "category": "single-cell",
      "path": "skills/single-cell/prioritize-disease-genes-with-atlas",
      "version": "1.0.0",
      "status": "community",
      "description": "Prioritize disease-associated genes by combining an existing condition-vs-control differential expression result (per cell type) with a precalculated cell-type atlas, offline. The atlas (long-form CSV, TSV or Parquet profiles, or an h5ad of profiles or of cells) adds cell-type expression specificity and, when present, an independent disease direction; these and the DE effect (plus FDR when the p-values are inferential) form a ranking score, written to tables/atlas/candidates.csv with a figure and a JSON summary. Use after a pseudobulk or reference-group DE, when an atlas can add independent cell-type evidence, when mapping mouse or Ensembl IDs onto a human atlas, or when checking candidates against a known-gene panel. It refuses one-vs-rest marker tables and computes no DE itself.",
      "tags": [
        "scrna-seq",
        "snrna-seq",
        "differential-expression",
        "disease-genes",
        "cell-atlas",
        "gene-prioritization"
      ],
      "license": "MIT",
      "tested_with": "pandas 2.3.3, numpy 2.4.6, matplotlib 3.10.9, anndata 0.12.16, scipy 1.17.1, Python 3.13.9",
      "author": "Karry1123",
      "requirements": {
        "compute": "1 CPU, under 1 GB RAM (0.3 GB peak), seconds for 3-gene tables and a 123-cell atlas",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "qc-snrna-with-cellqc-standalone",
      "category": "single-cell",
      "path": "skills/single-cell/qc-snrna-with-cellqc-standalone",
      "version": "1.2.0",
      "status": "core",
      "description": "Run the CellQC pipeline (ambient correction, count/feature/mitochondrial filtering, doublet calling, nuclear fraction) over a cohort of 10x Cell Ranger libraries by hand, with bash plus conda or a container, from installing cellqc to validating result/metrics.csv. Use when setting up a cellqc environment or container outside an AiScientist run, writing a cellqc config.yaml (including mitochondrial/ribosomal/hemoglobin gene sets for mouse or non-human references), or diagnosing a failed or inert QC run. Inside an AiScientist analysis, the run_cellqc tool runs this skill's pipeline; follow these steps there only for what run_cellqc does not expose.",
      "tags": [
        "snrna-seq",
        "scrna-seq",
        "qc",
        "10x-genomics",
        "cell-ranger",
        "doublets",
        "ambient-rna",
        "snakemake"
      ],
      "license": "MIT",
      "tested_with": "cellqc 0.3.6, Python 3.12.14, R 4.5.3, DoubletFinder 2.0.6, scDblFinder 1.24.10, SoupX 1.6.2, Seurat 5.5.1, Snakemake 9.22.0",
      "author": "Jin Li",
      "requirements": {
        "compute": "16 CPUs, 80 GB requested (25.6 GB peak), 20 min for 4 libraries",
        "gpu": false,
        "scheduler": "optional",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": false,
      "has_tests": false
    },
    {
      "name": "score-gene-signatures-with-scanpy",
      "category": "single-cell",
      "path": "skills/single-cell/score-gene-signatures-with-scanpy",
      "version": "1.0.0",
      "status": "community",
      "description": "Score every cell for one or more gene signatures with scanpy score_genes (the signature's mean expression minus that of a matched random control gene set) and summarise each score per group, for example cell type by condition, as n, mean, median and IQR, with per-cell scores, box plots and a JSON coverage report. It looks genes up in the matrix actually scored (.raw when present), matches symbols case-insensitively and reports every remap, does not score a signature below a coverage threshold, and refuses integer counts. Use when a per-cell programme score is the deliverable, when the programme is in no gene-set library, or when choosing and checking signature genes for a scoring. To test whether a curated pathway shifts between conditions within a cell type, preranked GSEA on the full contrast ranking (AiScientist run_gsea_prerank) is the better tool.",
      "tags": [
        "scrna-seq",
        "snrna-seq",
        "gene-signature",
        "module-score",
        "scanpy"
      ],
      "license": "MIT",
      "tested_with": "scanpy 1.12.1, anndata 0.12.16, pandas 2.3.3, numpy 2.4.6, matplotlib 3.10.9, Python 3.13.9",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 1 GB RAM (0.3 GB peak), about 2 s for 120 cells x 200 genes",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "build-variant-db-with-tiledbvcf",
      "category": "variants",
      "path": "skills/variants/build-variant-db-with-tiledbvcf",
      "version": "1.0.0",
      "status": "community",
      "description": "Build and query a TileDB-VCF variant database for a cohort with tiledbvcf-py - ingest many indexed single-sample VCFs into one compressed sparse-array dataset (a re-run adds only new samples, with no re-merging), query genomic regions across all or selected samples, and export the matching records (sample, contig, position, alleles, genotype) to a TSV with per-sample counts. Use when a study has many single-sample VCFs and needs population-scale storage, incremental sample addition or fast region and sample queries instead of repeated bcftools merges. Skip it for a single VCF.",
      "tags": [
        "vcf",
        "tiledb-vcf",
        "variant-database",
        "cohort",
        "population-genomics",
        "region-query"
      ],
      "license": "MIT",
      "tested_with": "tiledbvcf-py 0.40.3, libtiledbvcf 0.40.3, tiledb 2.30.1, bcftools 1.24, htslib 1.23.1, pandas 2.3.3, numpy 2.5.4, Python 3.12.15",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 1 GB RAM, about 1 s for 3 single-sample VCFs of 1-3 records",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "normalize-vcf-with-bcftools",
      "category": "variants",
      "path": "skills/variants/normalize-vcf-with-bcftools",
      "version": "1.0.0",
      "status": "community",
      "description": "Normalize a VCF with bcftools norm before annotation or ClinVar/gnomAD matching - split multiallelic sites into biallelic records, atomize MNPs into SNPs, then left-align and trim indels against the reference FASTA of the same build - writing an indexed normalized.vcf.gz and a JSON of record counts before and after. Use when a VCF not known to be normalized (especially an indel-heavy one) will be matched against ClinVar, gnomAD or dbSNP, compared between callers or loaded into PLINK, or when a known pathogenic indel comes back as not in ClinVar. In AiScientist, the offline annotate_variants path already normalizes, so do not plan it as a separate step there.",
      "tags": [
        "vcf",
        "variant-normalization",
        "bcftools",
        "left-alignment",
        "multiallelic",
        "clinvar",
        "gnomad"
      ],
      "license": "MIT",
      "tested_with": "bcftools 1.24, htslib 1.23.1, Python 3.12.15",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 100 MB RAM, about 1 s for a 4-record VCF on a 60-base reference",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "prioritize-clinical-variants",
      "category": "variants",
      "path": "skills/variants/prioritize-clinical-variants",
      "version": "1.0.0",
      "status": "community",
      "description": "Rank annotated variants into research-triage tiers (ClinVar pathogenic, likely pathogenic, VUS favouring pathogenic, VUS, likely benign, benign) from ClinVar significance used verbatim, gnomAD allele frequency against a dominant or recessive disease-model cutoff and the ACMG BA1 5 percent rule, and in-silico predictors (SIFT, PolyPhen, REVEL, CADD, AlphaMissense) as supporting evidence only. Pure Python; reads a per-variant annotation TSV and writes a tier-sorted CSV with the evidence for each tier plus a JSON of tier counts. Use when a rare-variant list from VEP, ClinVar and gnomAD annotation must be ordered for review (which variants to look at first) or when choosing the disease model and cutoffs for that ordering. Research triage only, NOT a clinical ACMG/AMP classification.",
      "tags": [
        "variant-prioritization",
        "clinvar",
        "gnomad",
        "acmg",
        "rare-disease",
        "vus",
        "in-silico-predictors"
      ],
      "license": "MIT",
      "tested_with": "Python 3.12.15 and 3.13.9 (standard library only)",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 100 MB RAM, well under 1 s for 10 variants",
        "gpu": false,
        "scheduler": "none",
        "network": "none",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "qc-vcf-callset",
      "category": "variants",
      "path": "skills/variants/qc-vcf-callset",
      "version": "1.0.0",
      "status": "community",
      "description": "Judge whether a germline VCF callset is trustworthy before interpreting any variant - Ti/Tv, Het/Hom, SNP, indel and multiallelic counts, sample count and genotype call rate from bcftools stats (falling back to cyvcf2, then to a plain-Python Ti/Tv scan) - and flag values outside the expected human WGS or WES ranges. Writes tables/vcf_qc_summary.csv and data/vcf_qc.json with a qc_flags list. Use when a VCF (one sample or a cohort) arrives for annotation or prioritization, when a Ti/Tv or Het/Hom value needs interpreting, or when deciding whether downstream variant tables must carry a low-confidence caveat.",
      "tags": [
        "vcf",
        "qc",
        "callset-qc",
        "ti-tv",
        "het-hom",
        "call-rate",
        "bcftools",
        "cyvcf2",
        "wgs",
        "wes"
      ],
      "license": "MIT",
      "tested_with": "bcftools 1.24, htslib 1.23.1, cyvcf2 0.34.0, numpy 2.5.4, Python 3.12.15 (plain-Python path also on 3.13.9)",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 100 MB RAM, about 1 s for a 5-record, 2-sample VCF",
        "gpu": false,
        "scheduler": "none",
        "network": "install",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    },
    {
      "name": "write-variant-tables",
      "category": "variants",
      "path": "skills/variants/write-variant-tables",
      "version": "1.0.0",
      "status": "community",
      "description": "Write the five standard variant-annotation result tables - consequence, impact and ClinVar-significance distributions, a ClinVar pathogenic and likely-pathogenic list, and a shortlist of rare, damaging variants not in ClinVar - plus a JSON summary, from an annotated per-variant TSV (VEP consequence and impact, SIFT, PolyPhen, max population AF, rsID, ClinVar significance), in pure Python without re-parsing the VCF or re-running VEP. Use when a study needs these tables with a different rarity cutoff, impact set or damaging-predictor rule, or when rebuilding them from an existing annotation table. In AiScientist, annotate_variants already writes the default tables, so use this only for a custom cut.",
      "tags": [
        "variant-annotation",
        "vep",
        "clinvar",
        "gnomad",
        "result-tables",
        "rare-variants"
      ],
      "license": "MIT",
      "tested_with": "Python 3.12.15 and 3.13.9 (standard library only)",
      "author": "Yijun Sun",
      "requirements": {
        "compute": "1 CPU, under 100 MB RAM, well under 1 s for 5 variants",
        "gpu": false,
        "scheduler": "none",
        "network": "none",
        "secrets": [],
        "allowed_tools": ""
      },
      "has_code": true,
      "has_tests": true
    }
  ]
}
