# Keys And Harmony With Pytheory

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-kennethreitz-pytheory-skill-keys-and-harmony-with-pytheory`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [kennethreitz](https://agentstack.voostack.com/s/kennethreitz)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [kennethreitz](https://github.com/kennethreitz)
- **Source:** https://github.com/kennethreitz/pytheory-skill/tree/main/plugins/composing-with-pytheory/skills/keys-and-harmony-with-pytheory

## Install

```sh
agentstack add skill-kennethreitz-pytheory-skill-keys-and-harmony-with-pytheory
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Keys & Harmony

Working with keys and *progressions* — the harmonic level above a single chord.

## Keys and their diatonic content

```python
from pytheory import Key

k = Key("C", "major")
k.chords            # ['C major', 'D minor', 'E minor', 'F major', 'G major', 'A minor', 'B diminished']
k.seventh_chords    # ['C major 7th', 'D minor 7th', …, 'G dominant 7th', …]
k.note_names        # ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C']
k.signature         # {'sharps': 0, 'flats': 0, 'accidentals': []}
k.relative          # 
```

## Detect the key

```python
Key.detect("C", "E", "G", "B", "D")     # -> 
```

## Progressions

```python
k = Key("G", "major")
[c.symbol for c in k.progression("I", "V", "vi", "IV")]   # ['G', 'D', 'Em', 'C']
[c.symbol for c in k.nashville(1, 5, 6, 4)]               # ['G', 'D', 'Em', 'C']
k.random_progression(4)                                    # a diatonic [Chord, …]
```

Roman-numeral **analysis** of an existing progression:

```python
from pytheory import analyze_progression, Chord
chords = [Chord.from_symbol(s) for s in ["C", "G", "Am", "F"]]
analyze_progression(chords, key="C", mode="major")        # ['I', 'V', 'vi', 'IV']
```

**Secondary dominants** — applied dominants that tonicise a non-tonic
degree. `detect_secondary_dominant` *identifies* one chord (the analytical
inverse of `Key.secondary_dominant(degree)`, which *builds* one); pass
`secondary_dominants=True` to `analyze_progression` to label them in
context (instead of the bare degree):

```python
from pytheory import detect_secondary_dominant, analyze_progression, Chord
detect_secondary_dominant(Chord.from_symbol("D7"), "C")   # 'V7/V'  (D7 -> G)
detect_secondary_dominant(Chord.from_symbol("E7"), "C")   # 'V7/vi' (E7 -> Am)
prog = [Chord.from_symbol(s) for s in ("C", "D7", "G7", "C")]
analyze_progression(prog, "C", secondary_dominants=True)  # ['I', 'V7/V', 'V7', 'I']
```

From the terminal, `pytheory analyze C D7 G7 C` prints the whole picture —
detected key, Roman numerals (with secondary dominants), and cadences
(add `--key`/`--mode` to fix the key).

**Cadences** — the harmonic punctuation that ends a phrase. Pass the last
two chords (and the key) to `detect_cadence`, or scan a whole progression
with `find_cadences`:

```python
from pytheory import detect_cadence, find_cadences, Chord
C = Chord.from_name

detect_cadence(C("G"), C("C"), "C")            # 'imperfect authentic' (5th on top)
detect_cadence(C("G"), C("Am"), "C")           # 'deceptive'  (V->vi surprise)
detect_cadence(C("F"), C("C"), "C")            # 'plagal'     (the 'Amen')
detect_cadence(C("Dm"), C("G"), "C")           # 'half'       (ends on V)
detect_cadence(C("E"), C("Am"), "A", "minor")  # 'imperfect authentic'

# Perfect authentic needs the tonic in the soprano (both root position):
pac_I = Chord.from_midi_message(48, 52, 55, 60)   # C3 E3 G3 C4
detect_cadence(C("G"), pac_I, "C")             # 'perfect authentic'

find_cadences([C(n) for n in ("C","F","G","Am")], "C")   # [(2,'half'), (3,'deceptive')]
```

**Non-chord tones** — label the melody notes that *aren't* in the harmony
(passing / neighbor / suspension / anticipation / appoggiatura / escape).
Pass one chord for the whole line, or one chord per note:

```python
from pytheory import analyze_non_chord_tones, Chord, Tone
mel = [Tone.from_string(n) for n in ("C4","D4","E4")]
[r["type"] for r in analyze_non_chord_tones(mel, Chord.from_name("C"))]
# ['chord tone', 'passing', 'chord tone']
```

## Harmonic color & motion

```python
k = Key("C", "major")
k.secondary_dominant(5).symbol     # 'D7'  — the V/V
k.borrowed_chords                  # chords from the parallel minor (modal interchange)
k.suggest_next(Chord.from_symbol("G"))   # ranked next-chord candidates
```

Chords grouped by **harmonic function** (interchangeable within a group), the
key-level **circle of fifths**, and **negative harmony**:

```python
k = Key("C", "major")
k.chords_by_function()   # {'tonic': [C,Em,Am], 'subdominant': [Dm,F], 'dominant': [G,Bdim]}
k.tonic_chords(); k.subdominant_chords(); k.dominant_chords()

cof = k.circle_of_fifths()
cof["position"]                 # 0  (sharps +, flats -)
cof["relative"], cof["parallel"]               # A minor, C minor
cof["dominant"]["key"], cof["dominant"]["shared_chords"]   # G major + the 4 shared chords

neg = k.negative_harmony()       # Levy/Collier reflection across the tonic↔dominant axis
neg["axis"]                      # ('C', 'G')
neg["negative_dominant"]         # Fm — the chord that bridges the two families
neg["scale"], neg["chords"]      # the mirrored scale and diatonic chords
```

## Modulation

```python
src, dst = Key("C", "major"), Key("G", "major")
src.pivot_chords(dst)        # ['A minor', 'C major', 'E minor', 'G major']  — shared chords
src.modulation_path(dst)     # a chord path that carries you from C to G
```

## Hear a progression

```python
from pytheory.play import play_progression
from pytheory import Key
play_progression(Key("G", "major").progression("I", "V", "vi", "IV"), t=700)
```

## Tips

- `progression()` takes Roman numerals (lowercase = minor: `"ii"`, `"vi"`);
  `nashville()` takes degree numbers.
- `analyze_progression(chords, key=, mode=)` returns numerals (or `None` for
  chords outside the key) — handy for "what are these chords doing?".
- To turn a progression into a full track (drums, bass, synths, export), hand off
  to the **composing-with-pytheory** skill.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [kennethreitz](https://github.com/kennethreitz)
- **Source:** [kennethreitz/pytheory-skill](https://github.com/kennethreitz/pytheory-skill)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-kennethreitz-pytheory-skill-keys-and-harmony-with-pytheory
- Seller: https://agentstack.voostack.com/s/kennethreitz
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
