# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

A Snakemake-based CRISPR gRNA design pipeline for SpCas9 (NGG PAM) targeting hg38 and mm10 genomes. It is a modular rewrite of [CRISPRDesigner](https://github.com/EngreitzLab/CRISPRDesigner).

## Environment

The pipeline runs in a conda environment called **`CRISPRDesigner2`**. All pipeline commands should be run with this environment active.

```bash
# First-time setup (creates the env from the yaml)
conda env create -f workflow/envs/CRISPRDesigner2.yaml -n CRISPRDesigner2
conda activate CRISPRDesigner2
```

**Adding dependencies**: Only add packages to `workflow/envs/CRISPRDesigner2.yaml` (and thus the conda env) if they are used at pipeline runtime. For one-time setup tasks or debugging, use the cluster module system instead:

```bash
module spider <tool>   # search for available system modules
module load <tool>     # load without modifying the conda env
```

## Common Commands

```bash
# Run all tests
conda run -n CRISPRDesigner2 python -m pytest tests/ -v

# Run a single test module
conda run -n CRISPRDesigner2 python -m pytest tests/test_guide_extraction.py -v

# Dry-run to validate workflow config
conda run -n CRISPRDesigner2 snakemake -n --configfile config/config.yaml

# Run full workflow
conda run -n CRISPRDesigner2 snakemake --configfile config/config.yaml --cores 4
```

### One-time setup: GuideScan2 BAM index

The GuideScan2 BAM database (`resources/hg38_no_alt.bam.sorted`) requires a `.bai` index for efficient lookup. Without it, pysam performs a full sequential scan of the 154 GB file, which is very slow and may fail. Create it once with:

```bash
module load samtools/1.16.1
samtools index resources/hg38_no_alt.bam.sorted
```

## Architecture

The pipeline has four sequential Snakemake rules, each backed by a dedicated module in `workflow/scripts/`:

1. **`extract_and_filter_guides`** → `snakemake_extract_filter.py`
   - Calls `guide_extraction.py`: scans BED regions in the genome FASTA for NGG PAM sites on both strands, outputs 20nt protospacers + 3nt PAM + 30nt context sequences
   - Calls `sequence_filters.py`: applies quality filters (Pol III termination, T content, homopolymers, low complexity, GC content)
   - Optionally skips regions already covered by `predesigned_guides` (via `guide_cache.py`)
   - Output: `intermediate/filtered_guides.tsv`

2. **`score_ontarget`** → `snakemake_score_ontarget.py`
   - Calls `rs3_scoring.py`: uses the `rs3` package (`rs3.seq.predict_seq`) with 30nt context sequences; scores are raw log-odds (not bounded to [0,1])
   - Output: `intermediate/ontarget_scored.tsv`

3. **`score_offtarget`** (conditional on `guidescan2_index` existing) → `snakemake_score_offtarget.py`
   - Calls `guidescan_scoring.py`: queries a GuideScan2 BAM database via `pysam`; falls back to GuideScan2 Python API, then CLI; if `guidescan2_bwt_zip` is provided, runs `guidescan enumerate` for guides missing BAM scores
   - Specificity scores are 0–1 (1 = no off-targets); if GuideScan2 is unavailable, scores are NaN
   - The GuideScan2 paper ([Perez et al., Genome Biology 2025](https://link.springer.com/article/10.1186/s13059-025-03488-8)) recommends **specificity score > 0.2** as a cutoff for guide selection
   - Output: `intermediate/fully_scored.tsv`

4. **`write_outputs`** → `snakemake_write_outputs.py`
   - Calls `output_writer.py`: writes `designGuides.txt` (TSV) and `designGuides.bed` (BED6)
   - Merges with pre-designed guides via `guide_cache.merge_with_predesigned`; new guides take priority in deduplication on (chr, start, end, strand)
   - Output: `results/GuideDesign/designGuides.txt` and `.bed`

### Key Design Decisions

- **Guide coordinates** are 0-based half-open (BED format). The `start`/`end` in output files refer to the 20nt protospacer only (not the PAM).
- **RS3 context sequences** are 30nt: 4bp upstream + 20bp protospacer + 3bp PAM + 3bp downstream. These are extracted during guide extraction and passed through the pipeline.
- **GuideScan2 BAM coordinate offsets**: for `+` strand, lookup position = `start - 1`; for `-` strand, lookup position = `start - 4`.
- **Guide caching** matches by `guideSet` (region name from BED column 4). Entire regions are skipped if any pre-designed guide shares the guideSet name.
- The `filter.py` at the repo root is a legacy standalone filter script; the active implementation is `workflow/scripts/sequence_filters.py`.

### Config Fields

| Field | Description |
|---|---|
| `genome` | `hg38` or `mm10` |
| `regions` | Input BED file (chr/start/end/name) |
| `genome_fasta` | Genome FASTA (gzipped OK via pyfastx) |
| `guidescan2_index` | GuideScan2 BAM database (optional, enables off-target scoring) |
| `guidescan2_bwt_zip` | GuideScan2 BWT index zip (optional, fallback for guides missing BAM scores) |
| `tracr` | `Hsu2013` or `Chen2013` tracrRNA variant for RS3 |
| `predesigned_guides` | Path to prior `designGuides.txt` to skip already-designed regions |
| `output_dir` | Output directory (default: `results/GuideDesign`) |
