# CLI Reference

> Parent doc: [LLM_CONTEXT.md](LLM_CONTEXT.md)

## Overview

Three CLI entry points, all in `scripts/`:

| Script | Role | Default output |
|--------|------|---------------|
| `compose.py` | Full composition pipeline | `.rpp` file |
| `generate.py` | Thin wrapper around compose | `.rpp` file + optional validation |
| `run_in_reaper.py` | ReaScript generation & execution | ReaScript `.py`, result JSON, LUFS JSON |

## compose.py

Full pipeline: builds all tracks, applies calibration, generates `.rpp`.

### Usage

```bash
python scripts/compose.py [--bpm BPM] [--key KEY] [--output PATH] [--seed SEED]
                          [--emotion EMOTION] [--inversion INVERSION]
                          [--no-calibrate]
```

### Flags

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--bpm` | `float` | `99` | Tempo. Must be > 0. |
| `--key` | `str` | `"Am"` | Musical key. Format: `[A-G][b#]?m?` (e.g. `"Am"`, `"Dm"`, `"G"`, `"F#m"`) |
| `--output` | `str` | `"output/song.rpp"` | Output `.rpp` file path. Parent directory created if missing. |
| `--seed` | `int` | `None` | Random seed. `None` = non-deterministic (system time). |
| `--emotion` | `str` | `"romantic"` | Chord emotion: `romantic`, `dark`, `club`, `classic` |
| `--inversion` | `str` | `"root"` | Preferred chord inversion: `root`, `first`, `second` |
| `--no-calibrate` | `flag` | `False` | Skip post-processing mix calibration |

### Examples

```bash
# Default reggaetón at 99 BPM in Am
python scripts/compose.py

# Specific BPM and key
python scripts/compose.py --bpm 95 --key Dm

# Deterministic output with seed
python scripts/compose.py --bpm 95 --key Am --seed 42

# Dark emotion with first inversion chords
python scripts/compose.py --emotion dark --inversion first

# Skip calibration (raw volumes from VOLUME_LEVELS in compose.py)
python scripts/compose.py --no-calibrate

# Custom output path
python scripts/compose.py --output my_project/song_v2.rpp
```

### Output Structure

```
output/song.rpp          # REAPER project file
```

Console output includes:
- BPM and key confirmation
- Section count and total duration (bars + beats)
- Kick detection cache summary (per drumloop file)
- Final output path

## generate.py

Thin wrapper around `compose.py`. Delegates by setting `sys.argv` and calling `compose.main()`.

### Usage

```bash
python scripts/generate.py [--bpm BPM] [--key KEY] [--output PATH] [--seed SEED]
                           [--emotion EMOTION] [--inversion INVERSION]
                           [--validate]
```

### Flags

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--bpm` | `float` | `95` | Tempo. Must be > 0. |
| `--key` | `str` | `"Am"` | Musical key |
| `--output` | `str` | `"output/song.rpp"` | Output `.rpp` path |
| `--seed` | `int` | `42` | Random seed |
| `--emotion` | `str` | `"romantic"` | Chord emotion |
| `--inversion` | `str` | `"root"` | Chord inversion |
| `--validate` | `flag` | `False` | Run validator after generation |

### Differences from compose.py

- Default BPM is 95 (vs 99 in compose.py)
- Default seed is 42 (vs None in compose.py)
- Has `--validate` flag (runs `validate_rpp_output()` after generation)
- No `--no-calibrate` flag (calibration always applied)

### Examples

```bash
# Generate with default settings
python scripts/generate.py

# Generate with validation
python scripts/generate.py --bpm 95 --key Dm --seed 123 --validate

# Full custom generation
python scripts/generate.py --bpm 100 --key Fm --output output/custom.rpp --seed 7 --emotion club
```

### Validation Errors

When `--validate` is set, errors are printed to stderr:
```
Validation errors:
  - Expected 416 beats, found 400 beats
  - Missing drumloop in section: chorus
```

Exit code 1 on validation failure.

## run_in_reaper.py

Generates a ReaScript for post-processing and polls for results.

### Usage

```bash
python scripts/run_in_reaper.py <rpp_path> [--output WAV_PATH] [--timeout SECONDS]
                                 [--plugins-config JSON_PATH] [--action ACTIONS]
```

### Arguments and Flags

| Arg/Flag | Type | Default | Description |
|----------|------|---------|-------------|
| `rpp_path` | positional | *(required)* | Path to `.rpp` file |
| `--output`, `-o` | `str` | `<rpp_stem>_rendered.wav` | Rendered WAV output path |
| `--timeout` | `int` | `120` | Seconds to poll for result JSON |
| `--plugins-config` | `str` | `None` | Path to JSON-serialized `SongDefinition` (for deriving `plugins_to_add`) |
| `--action` | `str` | `"calibrate"` | Space-separated action pipeline |

### Actions

Space-separated list of up to 5 actions, executed in order:

```
add_plugins configure_fx_params verify_fx calibrate render
```

Each action name maps to a function in the generated ReaScript. Unknown action names are ignored.

### Examples

```bash
# Basic calibration on an existing .rpp
python scripts/run_in_reaper.py output/song.rpp

# Full pipeline: load plugins, calibrate, render
python scripts/run_in_reaper.py output/song.rpp --action "add_plugins calibrate render"

# With plugin config from JSON SongDefinition
python scripts/run_in_reaper.py output/song.rpp --plugins-config output/song.json --action "add_plugins configure_fx_params"

# Custom render output and extended timeout
python scripts/run_in_reaper.py output/song.rpp -o output/final_mix.wav --timeout 300 --action "calibrate render"
```

### Output Files

```
scripts/fl_control_phase2.py           # Generated ReaScript
scripts/fl_control_command.json        # Command JSON for ReaScript
scripts/fl_control_result.json         # Result JSON from ReaScript (polled)
output/<song>_lufs.json               # LUFS metrics
output/<song>_fx_errors.json          # FX errors (if any)
```

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | Error: .rpp not found, plugin config not found, result read error, or REAPER error |
| 2 | Timeout: result JSON not found within timeout |

## Key Validation

All scripts validate the key format: `^[A-G][b#]?m?$`

Valid examples: `"Am"`, `"C"`, `"D#m"`, `"Gb"`, `"F#"`.

Invalid: `"A minor"`, `"c"` (lowercase), `"H"`, `"Am7"`.

## BPM Validation

BPM must be > 0. Min/max enforced by `SongDefinition.validate()`: 20–999.

## Determinism

- When `--seed` is provided, all random operations use `random.Random(seed)` for reproducible output.
- Same `(--bpm, --key, --seed, --emotion, --inversion)` produces identical `.rpp` files.
- ReaScript seed is derived from the CLI seed. Same command → same script.
