Install any skill in seconds. Free to start, no credit card required.
Get Started Free →End-to-end 16S amplicon workflow from FASTQ reads to differential abundance. Orchestrates DADA2 ASV inference, taxonomy assignment, diversity analysis, and compositional testing with ALDEx2. Use when processing 16S/ITS amplicon data.
.claude/skills/bio-workflows-microbiome-pipeline/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 35% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 38% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-19 | ✓→✓ | = Same ✓ | 64% | 0% |
<!--
#
#
-->
Paired-End FASTQ (16S V4)
│
▼
┌──────────────────────────────────────────────────┐
│ microbiome-pipeline │
├──────────────────────────────────────────────────┤
│ 1. Quality Filtering (DADA2 filterAndTrim) │
│ 2. Error Learning & Denoising │
│ 3. Merge Pairs & Remove Chimeras │
│ 4. Taxonomy Assignment (SILVA) │
│ 5. Create phyloseq Object │
│ 6. Alpha/Beta Diversity │
│ 7. Differential Abundance (ALDEx2) │
│ 8. Visualization & Export │
└──────────────────────────────────────────────────┘
│
▼
ASV Table + Taxonomy + Diversity Plots + Differential Taxarlibrary(dada2) library(phyloseq) library(ALDEx2) library(vegan) library(ggplot2) # === CONFIGURATION === path <- 'raw_reads' silva_train <- 'silva_nr99_v138.1_train_set.fa.gz' silva_species <- 'silva_species_assignment_v138.1.fa.gz' metadata_file <- 'sample_metadata.csv' # === 1. READ FILES === fnFs <- sort(list.files(path, pattern = '_R1_001.fastq.gz', full.names = TRUE)) fnRs <- sort(list.files(path, pattern = '_R2_001.fastq.gz', full.names = TRUE)) sample_names <- sapply(strsplit(basename(fnFs), '_'), `[`, 1) # Setup filtered files filtFs <- file.path('filtered', paste0(sample_names, '_F_filt.fastq.gz')) filtRs <- file.path('filtered', paste0(sample_names, '_R_filt.fastq.gz')) # === 2. FILTER & TRIM === out <- filterAndTrim(fnFs, filtFs, fnRs, filtRs, truncLen = c(240, 160), maxN = 0, maxEE = c(2, 2), truncQ = 2, rm.phix = TRUE, compress = TRUE, multithread = TRUE) # === 3. LEARN ERRORS & DENOISE === errF <- learnErrors(filtFs, multithread = TRUE) errR <- learnErrors(filtRs, multithread = TRUE) dadaFs <- dada(filtFs, err = errF, multithread = TRUE) dadaRs <- dada(filtRs, err = errR, multithread = TRUE) # === 4. MERGE & CHIMERAS === mergers <- mergePairs(dadaFs, filtFs, dadaRs, filtRs, verbose = TRUE) seqtab <- makeSequenceTable(mergers) seqtab_nochim <- removeBimeraDenovo(seqtab, method = 'consensus', multithread = TRUE) # === 5. ASSIGN TAXONOMY === taxa <- assignTaxonomy(seqtab_nochim, silva_train, multithread = TRUE) taxa <- addSpecies(taxa, silva_species) # === 6. BUILD PHYLOGENETIC TREE (for UniFrac) === library(DECIPHER) library(phangorn) seqs <- getSequences(seqtab_nochim) names(seqs) <- paste0('ASV', seq_along(seqs)) alignment <- AlignSeqs(DNAStringSet(seqs), anchor = NA, processors = NULL) phang_align <- phyDat(as(alignment, 'matrix'), type = 'DNA') dm <- dist.ml(phang_align) tree <- NJ(dm) tree <- midpoint(ladderize(tree)) # === 7. CREATE PHYLOSEQ === metadata <- read.csv(metadata_file, row.names = 1) ps <- phyloseq(otu_table(seqtab_nochim, taxa_are_rows = FALSE), tax_table(taxa), sample_data(metadata), phy_tree(tree)) taxa_names(ps) <- paste0('ASV', seq(ntaxa(ps))) # === 8. DIVERSITY === # Alpha diversity (including Faith's PD with tree) library(picante) alpha_div <- estimate_richness(ps, measures = c('Observed', 'Shannon', 'Simpson')) faith_pd <- pd(t(otu_table(ps)), phy_tree(ps), include.root = TRUE) alpha_div$PD <- faith_pd$PD alpha_div$Group <- sample_data(ps)$Group # Beta diversity (Bray-Curtis and UniFrac) bray_dist <- phyloseq::distance(ps, method = 'bray') unifrac_dist <- UniFrac(ps, weighted = TRUE) pcoa_bray <- ordinate(ps, method = 'PCoA', distance = bray_dist) pcoa_unifrac <- ordinate(ps, method = 'PCoA', distance = unifrac_dist) # PERMANOVA on both metrics meta_df <- data.frame(sample_data(ps)) permanova_bray <- adonis2(bray_dist ~ Group, data = meta_df, permutations = 999) permanova_unifrac <- adonis2(unifrac_dist ~ Group, data = meta_df, permutations = 999) # === 9. DIFFERENTIAL ABUNDANCE === # Filter low-abundance taxa ps_filt <- filter_taxa(ps, function(x) sum(x > 0) > 0.1 * nsamples(ps), TRUE) # ALDEx2 otu <- as.data.frame(t(otu_table(ps_filt))) groups <- as.character(sample_data(ps_filt)$Group) aldex_results <- aldex(otu, groups, mc.samples = 128, test = 'welch', effect = TRUE) aldex_results$significant <- aldex_results$we.eBH < 0.05 & abs(aldex_results$effect) > 1 # === 10. OUTPUT === cat('Pipeline complete!\n') cat(' ASVs:', ntaxa(ps), '\n') cat(' Samples:', nsamples(ps), '\n') cat(' PERMANOVA R2:', round(permanova$R2[1], 3), 'p =', permanova$`Pr(>F)`[1], '\n') cat(' Differential taxa:', sum(aldex_results$significant), '\n')
| Stage | Check | Expected | Action if Failed | |-------|-------|----------|------------------| | Filter | >70% reads pass | >70% | Adjust truncLen/maxEE | | Merge | >80% pairs merge | >80% | Check amplicon length | | Chimera | <25% chimeras | <25% | Check PCR cycles | | Taxonomy | >80% genus assigned | >80% | Try different database | | Rarefaction | Curves plateau | Plateau | Increase depth | | PERMANOVA | p < 0.05 | p < 0.05 | Check experimental design |
microbiome_results/
├── phyloseq_object.rds # Complete phyloseq
├── asv_table.csv # ASV counts
├── taxonomy.csv # Taxonomic assignments
├── alpha_diversity.csv # Per-sample metrics
├── aldex2_results.csv # Differential taxa
├── read_tracking.csv # Reads per pipeline stage
├── plots/
│ ├── quality_profiles.pdf
│ ├── alpha_diversity.pdf
│ ├── beta_diversity_pcoa.pdf
│ ├── taxonomic_barplot.pdf
│ └── aldex2_effect_plot.pdfr# Key differences for ITS: # 1. No truncLen (variable length amplicons) out <- filterAndTrim(fnFs, filtFs, fnRs, filtRs, maxN = 0, maxEE = c(2, 2), truncQ = 2, minLen = 50, rm.phix = TRUE, multithread = TRUE) # 2. Use UNITE database taxa <- assignTaxonomy(seqtab_nochim, 'sh_general_release_dynamic_25.07.2023.fasta', multithread = TRUE)
r# V3-V4 (~460bp): truncLen = c(280, 200) # V4 (~253bp): truncLen = c(240, 160) # V1-V3 (~500bp): truncLen = c(260, 220)
r# For environmental samples, GTDB may be more accurate taxa <- assignTaxonomy(seqtab_nochim, 'GTDB_bac120_arc53_ssu_r214_fullTaxo.fa.gz', multithread = TRUE)
<!-- 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→fail | 26,414 | 23,848 | -10% | 1 | 1 | 0% | 5,439 | 8,232 | +51% | 0 | 0 | — |
case-02 | fail→pass | 23,757 | 20,396 | -14% | 1 | 1 | 0% | 4,821 | 6,522 | +35% | 0 | 0 | — |
case-03 | fail→fail | 29,456 | 23,749 | -19% | 1 | 1 | 0% | 6,227 | 7,333 | +18% | 0 | 0 | — |
case-12 | fail→pass | 13,195 | 9,034 | -32% | 1 | 1 | 0% | 2,404 | 4,127 | +72% | 0 | 0 | — |
case-13 | fail→fail | 10,732 | 11,287 | +5% | 1 | 1 | 0% | 2,217 | 4,598 | +107% | 0 | 0 | — |
case-14 | fail→fail | 15,672 | 12,782 | -18% | 1 | 1 | 0% | 2,665 | 4,757 | +78% | 0 | 0 | — |
case-20 | fail→pass | 11,424 | 2,990 | -74% | 1 | 1 | 0% | 2,087 | 2,879 | +38% | 0 | 0 | — |
case-19 | pass→pass | 16,894 | 15,153 | -10% | 1 | 1 | 0% | 3,057 | 5,028 | +64% | 0 | 0 | — |
case-10 | pass→pass | 10,707 | 9,415 | -12% | 1 | 1 | 0% | 2,147 | 4,170 | +94% | 0 | 0 | — |
case-11 | pass→pass | 15,829 | 12,553 | -21% | 1 | 1 | 0% | 3,023 | 4,888 | +62% | 0 | 0 | — |
case-04 | pass→pass | 23,028 | 24,951 | +8% | 1 | 1 | 0% | 4,628 | 7,622 | +65% | 0 | 0 | — |
case-05 | pass→pass | 19,161 | 19,081 | -0% | 1 | 1 | 0% | 3,504 | 6,016 | +72% | 0 | 0 | — |
case-06 | pass→pass | 20,056 | 19,170 | -4% | 1 | 1 | 0% | 4,088 | 6,424 | +57% | 0 | 0 | — |
case-07 | pass→pass | 13,479 | 8,932 | -34% | 1 | 1 | 0% | 2,601 | 4,251 | +63% | 0 | 0 | — |
case-08 | pass→pass | 9,903 | 13,951 | +41% | 1 | 1 | 0% | 1,754 | 4,067 | +132% | 0 | 0 | — |
case-09 | fail→pass | 16,761 | 12,189 | -27% | 1 | 1 | 0% | 2,992 | 4,474 | +50% | 0 | 0 | — |
case-15 | fail→fail | 13,687 | 8,899 | -35% | 1 | 1 | 0% | 2,609 | 3,875 | +49% | 0 | 0 | — |
case-16 | pass→pass | 13,263 | 6,178 | -53% | 1 | 1 | 0% | 2,268 | 3,422 | +51% | 0 | 0 | — |
case-17 | pass→pass | 13,771 | 11,586 | -16% | 1 | 1 | 0% | 2,856 | 4,710 | +65% | 0 | 0 | — |
case-18 | pass→pass | 14,646 | 2,912 | -80% | 1 | 1 | 0% | 2,646 | 2,894 | +9% | 0 | 0 | — |
case-21 | pass→pass | 9,864 | 2,862 | -71% | 1 | 1 | 0% | 985 | 2,905 | +195% | 0 | 0 | — |
case-22 | pass→pass | 12,983 | 12,063 | -7% | 1 | 1 | 0% | 2,667 | 4,909 | +84% | 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. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 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/26/2026 | +23% |
Other measured skills in the registry, with their headline benchmark lift.