Install any skill in seconds. Free to start, no credit card required.
Get Started Free →End-to-end outbreak investigation from pathogen isolates to transmission networks. Orchestrates MLST typing, AMR surveillance, phylodynamic dating, and transmission inference with TransPhylo. Use when investigating disease outbreaks or tracking pathogen transmission chains.
.claude/skills/bio-workflows-outbreak-pipeline/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 32% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 93% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 130% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 238% | 0% |
<!--
#
#
-->
Complete workflow for genomic epidemiology: from pathogen isolates to transmission networks and outbreak characterization.
Pathogen Isolate Genomes (FASTA/FASTQ)
|
v
+---------+---------+
| |
v v
[1a. MLST Typing] [1b. AMR Detection] <-- Parallel execution
| |
+--------+----------+
|
v
[2. Core Genome Alignment] --> snippy / ParSNP
|
v
[3. Phylodynamics] --> TreeTime / BEAST2
|
v
[4. Transmission Inference] --> TransPhylo
|
v
Transmission Network + R0 Estimates + Timelinebashconda install -c bioconda mlst abricate snippy iqtree fasttree pip install treetime transphylo biopython pandas matplotlib # R packages for TransPhylo Rscript -e "install.packages('TransPhylo')"
bash#!/bin/bash ISOLATES="isolate1.fasta isolate2.fasta isolate3.fasta" OUTDIR="outbreak_results" mkdir -p ${OUTDIR}/{mlst,amr,alignment,phylo,transmission} # Run MLST on all isolates echo "=== MLST Typing ===" for fasta in $ISOLATES; do sample=$(basename $fasta .fasta) mlst $fasta > ${OUTDIR}/mlst/${sample}.mlst.txt done # Combine results cat ${OUTDIR}/mlst/*.mlst.txt > ${OUTDIR}/mlst/all_mlst.tsv echo "MLST complete: ${OUTDIR}/mlst/all_mlst.tsv"
bashecho "=== AMR Detection ===" for fasta in $ISOLATES; do sample=$(basename $fasta .fasta) abricate --db ncbi $fasta > ${OUTDIR}/amr/${sample}.amr.tsv done # Summary matrix abricate --summary ${OUTDIR}/amr/*.amr.tsv > ${OUTDIR}/amr/amr_summary.tsv echo "AMR summary: ${OUTDIR}/amr/amr_summary.tsv"
bashecho "=== Core Genome Alignment ===" REFERENCE="reference.gbk" # Reference genome in GenBank format # Run snippy for each isolate for fasta in $ISOLATES; do sample=$(basename $fasta .fasta) snippy --outdir ${OUTDIR}/alignment/snippy_${sample} \ --ref $REFERENCE \ --ctgs $fasta \ --cpus 8 done # Core SNP alignment snippy-core --ref $REFERENCE ${OUTDIR}/alignment/snippy_* # Clean alignment (remove recombination, optional) # run_gubbins.py core.full.aln mv core.* ${OUTDIR}/alignment/ echo "Core alignment: ${OUTDIR}/alignment/core.aln"
pythonimport subprocess from Bio import Phylo, AlignIO import pandas as pd import matplotlib.pyplot as plt from pathlib import Path outdir = Path('outbreak_results') # Build ML tree subprocess.run([ 'iqtree2', '-s', str(outdir / 'alignment/core.aln'), '-m', 'GTR+G', '-bb', '1000', '-nt', 'AUTO', '--prefix', str(outdir / 'phylo/outbreak') ], check=True) # Prepare metadata with dates # Format: name\tdate (YYYY-MM-DD or decimal year) metadata = pd.DataFrame({ 'name': ['isolate1', 'isolate2', 'isolate3', 'isolate4', 'isolate5'], 'date': ['2024-01-15', '2024-01-22', '2024-02-01', '2024-02-10', '2024-02-15'] }) metadata.to_csv(outdir / 'phylo/metadata.tsv', sep='\t', index=False) # Run TreeTime subprocess.run([ 'treetime', '--tree', str(outdir / 'phylo/outbreak.treefile'), '--aln', str(outdir / 'alignment/core.aln'), '--dates', str(outdir / 'phylo/metadata.tsv'), '--outdir', str(outdir / 'phylo/treetime_output'), '--coalescent', 'skyline', '--clock-filter', '3' # Remove outliers >3 IQR from clock ], check=True) # Check temporal signal # Good signal: R2 > 0.5, clock rate ~1e-6 to 1e-7 subs/site/year for bacteria print('TreeTime output:', outdir / 'phylo/treetime_output')
rlibrary(TransPhylo) library(ape) # Load dated tree from TreeTime tree <- read.nexus("outbreak_results/phylo/treetime_output/timetree.nexus") # Set parameters # dateT: date when sampling stopped # w.shape, w.scale: generation time distribution (Gamma) # For many bacteria: mean ~14 days, shape=2, scale=7 dateT <- 2024.2 # Decimal year when sampling ended w_shape <- 2 # Generation time shape (Gamma) w_scale <- 7/365 # Generation time scale in years (~7 days mean) # Run TransPhylo res <- inferTTree(tree, dateT = dateT, w.shape = w_shape, w.scale = w_scale, mcmcIterations = 10000, startNeg = 1, startPi = 0.5) # Extract results ttree <- extractTTree(res) # Transmission network medTTree <- medTTree(res) # Plot transmission tree pdf("outbreak_results/transmission/transmission_tree.pdf", width=10, height=8) plotTTree(medTTree) dev.off() # Who infected whom matrix wiw <- computeMatWIW(res) write.csv(wiw, "outbreak_results/transmission/who_infected_whom.csv") # R0 estimate R0 <- getOffspringMulti(res) cat("R0 estimate:", mean(R0), "(95% CI:", quantile(R0, 0.025), "-", quantile(R0, 0.975), ")\n")
pythonimport rpy2.robjects as ro from rpy2.robjects.packages import importr from rpy2.robjects import pandas2ri import pandas as pd from pathlib import Path pandas2ri.activate() transphylo = importr('TransPhylo') ape = importr('ape') outdir = Path('outbreak_results') tree = ape.read_nexus(str(outdir / 'phylo/treetime_output/timetree.nexus')) date_t = 2024.2 w_shape = 2 w_scale = 7/365 res = transphylo.inferTTree(tree, dateT=date_t, w_shape=w_shape, w_scale=w_scale, mcmcIterations=10000, startNeg=1, startPi=0.5) # Extract transmission pairs med_tree = transphylo.medTTree(res) ro.r(f''' pdf("{outdir}/transmission/transmission_tree.pdf", width=10, height=8) plotTTree(medTTree({res})) dev.off() ''') print(f'Transmission tree saved to {outdir}/transmission/')
pythonimport pandas as pd import matplotlib.pyplot as plt import matplotlib.dates as mdates from datetime import datetime metadata = pd.read_csv('outbreak_results/phylo/metadata.tsv', sep='\t') metadata['date'] = pd.to_datetime(metadata['date']) mlst = pd.read_csv('outbreak_results/mlst/all_mlst.tsv', sep='\t', header=None, names=['file', 'scheme', 'ST'] + [f'locus{i}' for i in range(7)]) mlst['sample'] = mlst['file'].apply(lambda x: x.split('/')[-1].replace('.fasta', '')) amr = pd.read_csv('outbreak_results/amr/amr_summary.tsv', sep='\t') # Merge data combined = metadata.merge(mlst[['sample', 'ST']], left_on='name', right_on='sample') fig, ax = plt.subplots(figsize=(12, 6)) colors = {'ST11': 'red', 'ST258': 'blue', 'ST307': 'green'} for st in combined['ST'].unique(): subset = combined[combined['ST'] == st] ax.scatter(subset['date'], [1]*len(subset), label=f'ST{st}', s=100, c=colors.get(f'ST{st}', 'gray'), alpha=0.7) ax.set_xlabel('Date') ax.set_ylabel('') ax.set_title('Outbreak Timeline by Sequence Type') ax.legend() ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d')) plt.xticks(rotation=45) plt.tight_layout() plt.savefig('outbreak_results/outbreak_timeline.pdf')
| Step | Parameter | Value | Rationale | |------|-----------|-------|-----------| | snippy | --mincov | 10 | Minimum coverage for variant call | | IQ-TREE | -m | GTR+G | General time-reversible model | | TreeTime | --clock-filter | 3 | Remove temporal outliers >3 IQR | | TransPhylo | w.shape, w.scale | 2, 7/365 | Generation time ~7 days for many bacteria | | TransPhylo | mcmcIterations | 10000+ | Ensure convergence |
| Issue | Likely Cause | Solution | |-------|--------------|----------| | No MLST match | Novel ST or poor assembly | Check assembly quality, submit novel ST | | Poor temporal signal | Insufficient sampling, recombination | Remove recombination with Gubbins, check dates | | TreeTime clock-filter removes many | Wrong root, contamination | Re-root tree, check sample quality | | TransPhylo non-convergence | Wrong generation time | Adjust w.shape/w.scale, increase iterations | | Missing AMR genes | Database mismatch | Try multiple databases (ncbi, card, resfinder) |
| File | Description | |------|-------------| | mlst/all_mlst.tsv | Sequence types for all isolates | | amr/amr_summary.tsv | AMR gene presence/absence matrix | | alignment/core.aln | Core genome SNP alignment | | phylo/outbreak.treefile | ML phylogenetic tree | | phylo/treetime_output/ | Dated tree and molecular clock | | transmission/transmission_tree.pdf | Inferred transmission network | | transmission/who_infected_whom.csv | Transmission probability matrix |
<!-- AUTHOR_SIGNATURE: 9a7f3c2e-MD-BABU-MIA-2026-MSSM-SECURE -->
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 30,388 | 24,165 | -20% | 1 | 1 | 0% | 6,249 | 8,249 | +32% | 0 | 0 | — |
case-07 | fail→fail | 13,159 | 9,314 | -29% | 1 | 1 | 0% | 2,645 | 4,831 | +83% | 0 | 0 | — |
case-20 | pass→pass | 17,620 | 25,185 | +43% | 1 | 1 | 0% | 3,521 | 6,174 | +75% | 0 | 0 | — |
case-21 | pass→pass | 13,986 | 11,929 | -15% | 1 | 1 | 0% | 3,266 | 5,629 | +72% | 0 | 0 | — |
case-02 | fail→pass | 22,319 | 16,406 | -26% | 1 | 1 | 0% | 4,694 | 6,825 | +45% | 0 | 0 | — |
case-03 | fail→pass | 17,737 | 16,465 | -7% | 1 | 1 | 0% | 3,511 | 6,768 | +93% | 0 | 0 | — |
case-04 | pass→pass | 16,023 | 9,785 | -39% | 1 | 1 | 0% | 3,181 | 5,155 | +62% | 0 | 0 | — |
case-05 | fail→pass | 8,181 | 3,002 | -63% | 1 | 1 | 0% | 1,574 | 3,613 | +130% | 0 | 0 | — |
case-06 | fail→pass | 25,627 | 6,586 | -74% | 1 | 1 | 0% | 1,213 | 4,106 | +238% | 0 | 0 | — |
case-08 | pass→pass | 3,085 | 2,207 | -28% | 1 | 1 | 0% | 543 | 3,345 | +516% | 0 | 0 | — |
case-09 | fail→pass | 3,171 | 2,430 | -23% | 1 | 1 | 0% | 499 | 3,429 | +587% | 0 | 0 | — |
case-10 | fail→pass | 20,654 | 11,098 | -46% | 1 | 1 | 0% | 4,248 | 5,113 | +20% | 0 | 0 | — |
case-11 | pass→pass | 5,514 | 1,490 | -73% | 1 | 1 | 0% | 943 | 3,243 | +244% | 0 | 0 | — |
case-12 | fail→pass | 18,569 | 4,773 | -74% | 1 | 1 | 0% | 1,762 | 3,952 | +124% | 0 | 0 | — |
case-13 | fail→pass | 6,406 | 2,867 | -55% | 1 | 1 | 0% | 1,183 | 3,571 | +202% | 0 | 0 | — |
case-14 | fail→pass | 9,180 | 5,658 | -38% | 1 | 1 | 0% | 1,691 | 4,122 | +144% | 0 | 0 | — |
case-15 | fail→pass | 19,969 | 4,725 | -76% | 1 | 1 | 0% | 1,048 | 3,791 | +262% | 0 | 0 | — |
case-22 | pass→pass | 17,199 | 11,802 | -31% | 1 | 1 | 0% | 3,538 | 5,443 | +54% | 0 | 0 | — |
case-16 | fail→pass | 14,077 | 8,817 | -37% | 1 | 1 | 0% | 2,615 | 4,735 | +81% | 0 | 0 | — |
case-17 | pass→pass | 17,215 | 12,882 | -25% | 1 | 1 | 0% | 3,362 | 5,542 | +65% | 0 | 0 | — |
case-18 | pass→pass | 12,665 | 7,544 | -40% | 1 | 1 | 0% | 2,154 | 4,318 | +100% | 0 | 0 | — |
case-19 | pass→pass | 9,729 | 6,547 | -33% | 1 | 1 | 0% | 1,557 | 4,215 | +171% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 20 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +55 percentage points is the difference between those two pass rates over the 20 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 7/24/2026 | +32% |
Other measured skills in the registry, with their headline benchmark lift.