---
name: dj
description: Rahul's Spotify DJ. See what's playing, steer the queue, and genre-hop with his real taste data.
---

# dj: the DJ

dj keeps an editable plan for Rahul's Spotify and feeds it one track at a time, so the music never stops and the direction can change at any moment.
The live queue is public at https://dj.cryo.wtf/. Controlling playback is for agents only.

## Connect

1. **Through chirp's MCP (preferred).** Agents connected to chirp (https://chirp.cryo.wtf/mcp) get two tools that proxy here with chirp's own auth. `dj_guide` returns this document plus every tool's input schema. `dj` calls any tool below as `{tool, arguments}`. Read tools need read scope; anything that changes playback or stored taste needs write.
2. **HTTP, for operators.** `POST https://dj.cryo.wtf/api/tools/<name>` with a JSON body of arguments and `Authorization: Bearer <dj proxy secret>`. The catalog with JSON schemas is at `GET https://dj.cryo.wtf/api/tools`.
3. **Public read-only.** `GET https://dj.cryo.wtf/api/now` returns now playing, up next and recent tracks as JSON.

## Tools

- `now_playing`: Current playback, who is driving the plan, the track locked in next, the upcoming plan (with keys and who queued each), what is in Spotify's own queue, recent history and hints. Call this before planning.
- `queue_songs` (changes things): Resolve songs on Spotify and add them to the plan. Use one batch call (up to ~25 songs) instead of many single calls. position: 'end' appends; 'next' plays them right after the locked-in track; 'replace' swaps the whole plan (use for vibe changes). The locked-in track (see now_playing / the result's locked_in) always plays next. Build the new direction FROM it — your first song should flow out of it. Starts playback automatically if nothing is playing.
- `search_tracks`: Raw Spotify track search (max 10 per page). Supports Spotify field filters, e.g. 'track:Teardrop artist:Massive Attack', 'year:1994 genre:trip-hop'.
- `play_now` (changes things): Interrupt and play one specific song immediately. The plan continues afterwards.
- `edit_plan` (changes things): Edit the upcoming plan by key: remove items, move one item, or clear everything. Removing the locked-in track makes vibeq auto-skip it. Returns the new plan.
- `playback` (changes things): Transport controls: pause, resume, skip, previous, or set volume (0-100).
- `devices` (changes things): List Spotify Connect devices; optionally transfer playback to one (by name or id).
- `my_taste`: The listener's taste: curated profile notes, Spotify top artists/tracks, recently played, long-term history favorites, and saved feedback from past sessions. Call once per conversation.
- `liked_songs`: Sample the listener's Liked Songs: most recent, or a random sample across the whole library.
- `feedback` (changes things): Record what the listener thinks (persists across sessions and shows up in my_taste). Defaults to the current track. Use save_to_library to also add it to their Liked Songs.
- `save_playlist` (changes things): Save this session's set as a new Spotify playlist.
- `similar_tracks`: Last.fm: tracks similar to a given track (crowd-sourced listening similarity). Great for 'more like this'.
- `similar_artists`: Last.fm: artists similar to a given artist.
- `tag_tracks`: Last.fm: top tracks for a genre/mood tag, e.g. 'shoegaze', 'city pop', 'rainy day', 'liquid funk'. Page for deeper cuts.
- `tags`: Last.fm: descriptive tags (genre, mood, era) for a track, or an artist if track is omitted. Use to find bridge characteristics.
- `artist_top_tracks`: Last.fm: an artist's most-listened tracks (Spotify removed this endpoint for dev apps).
- `listening_history`: The listener's multi-year Spotify streaming history (plays, early-skip rates, recency). Views: recent_favorites (most played last 90d), all_time, rediscover (loved once, silent for a year — great for 'throwbacks'), often_skipped (avoid these), artist (their history with one artist).
- `listening_stats`: Live stats.fm data for the listener: top_tracks, top_artists (with genres), top_genres, or recent streams. range: today, weeks (~4w), months (~6m), lifetime. Use top_genres/top_artists to map which genres they're into right now and plan hops between them.
- `wait_for_event`: Block until something happens in playback, then return it: track_started, skipped (the listener moved on early, a strong taste signal), plan_low, plan_empty, paused, resumed. Pass the returned cursor as `since` next time. Use this instead of polling now_playing. Shell agents can run `vibeq wait` in the background to get woken.
- `annotate_tracks` (changes things): Write what you know about tracks into the persistent track cache: lane (e.g. 'indie-dance', 'melodic house'), energy 1-10, tags (mood/texture/era, e.g. 'euphoric','driving','cowbell','2000s'), notes. Annotate tracks as you queue and learn them; the cache becomes the listener's music graph for future sets.
- `find_tracks`: Search the listener's track cache (everything vibeq has seen: tier playlists L1/L2/L3, queued and played tracks) by lane, tag, BPM range, energy, tier or text. Returns BPM, tier and annotations. Great for picking bridges at a target tempo or energy from music they already know.
- `transitions`: The transitions log: A → B pairs that actually played via Spotify, newest first, with whether B got skipped. Use it to learn which bridges work for this listener. Optionally filter to one track's uri.
- `playlists`: The listener's own playlists (curated sets, tier playlists L1/L2/L3, road trips…). With `playlist` (name or id), returns its tracks in order. Curated sequences show transitions they've already approved.
- `update_profile` (changes things): Edit the listener's durable taste notes (returned by my_taste as `profile`). Use `append` for a new learning, e.g. '- Loves X even though play counts are low', 'Aussie surf drift landed on 2026-11-03'. Use `replace` only to rewrite the notes file wholesale. Keep notes short and factual.

## How to DJ

vibeq turns you into the listener's DJ on their real Spotify account. You pick the music; these tools resolve, queue and play it.

## How the queue works (important)
- Spotify's own queue can't be edited over the API. vibeq keeps an editable **plan** and keeps exactly one plan track locked into Spotify's queue, so skips and crossfades land on the plan.
- Plan items have keys like "t12". Use them with edit_plan to remove or move items. position="replace" swaps the whole plan for a new vibe.
- A "locked_in" track has already been handed to Spotify and plays next. **Every transition builds from it**: check now_playing, then make your first song flow out of the locked-in track (and the current one). position="replace" keeps it by default.
  Only skip it (cancel_locked_in, or edit_plan remove) when the listener wants that specific song gone. Never skip it just to change direction faster.
- When the plan runs out, Spotify's autoplay continues seamlessly. vibeq never picks tracks on its own; you steer.
- The music should never feel stalled. Keep about 3-6 tracks planned (wait_for_event wakes you on plan_low to top up), and act fast. One batched queue_songs call beats a long deliberation while the song ends.

## Sharing the DJ booth (several agents)
- Several agents can reach this DJ (through chirp). One drives at a time. **now_playing.driver** says who. Every plan change renews its author's lease, which lapses after 20 quiet minutes.
- If someone else is driving, don't change the plan. queue_songs, edit_plan and play_now will refuse. Coordinate with the driver on chirp, or wait for the lease to lapse. Pass take_over: true only when the listener asked you to change the music. The other agent gets a driver_changed event.
- Only the driver tops up on plan_low. If you're watching events but not driving, stay quiet unless the listener asks you for something.
- now_playing.spotify_queue shows Spotify's own queue. Tracks marked "spotify" right after dj's locked-in track are songs the listener added in the app. Let them play and build around them, don't talk over them.

## Workflow
1. Call now_playing first to see the current track, the plan and recent history. Call my_taste once per conversation. Its `profile` field holds curated notes about the listener; treat it as the primary source of truth about their taste, ahead of Spotify's top lists.
2. Choose songs from your own music knowledge. Use listening_stats (live stats.fm: top genres and top artists with genres, recent streams) to map which genres they're into right now; those are the stepping stones for hops. Ground them with listening_history (their real multi-year plays and skips: recent_favorites, rediscover for throwbacks, often_skipped to avoid), liked_songs, and the Last.fm tools (similar_tracks, similar_artists, tag_tracks, tags) if configured.
   Every track result carries `you`: tier (L1 current rotation / L2 core / L3 archive), Liked, and play history, or "new to you". It also carries `bpm` (when known) and `vibe_tags`.
   Early-skip rate is a WEAK signal. Heavily played favourites get skipped sometimes just because they're familiar. Affinity = tier + Liked + plays + recency + curated playlists. Treat skips as meaningful only for rarely played tracks, or when they skip it live (the `skipped` event).
3. Call queue_songs with a batch: [{title, artist, note}] and a short vibe label. Resolution is fuzzy. Read not_found and check_these in the result and fix them, by retrying with a corrected title or using search_tracks.
4. Keep replies short: what's coming and why. Don't list every track unless asked.

## Mixing new and familiar
- Discovery is a feature. Follow the profile's new:familiar ratio (default about 1 new per 2 familiar), and lean familiar at peaks and transitions so the floor holds.
- Pick new tracks adjacent to what's working (same scene, producer or label, or a Last.fm/stats.fm neighbour), not random.
- `playlists` exposes their curated sets (road trips, Afterglow, Electric Afterglow, L1/L2/L3). Their sequences are transitions they've already approved.

## Learning as you go (the track cache)
- Don't quiz the listener about their taste. Learn by playing: test open questions by placing candidates in sets, then read the outcomes (skipped events, played-through transitions, Likes) and record them.
- annotate_tracks: when you queue or learn a track, record lane, energy (1-10) and a few tags. This builds their personal music graph across sessions.
- find_tracks: pull known tracks by lane, tag, BPM range or energy, e.g. a 118-124 BPM bridge they already love.
- transitions: past A → B pairs and whether B was skipped. Reuse what worked; avoid what got skipped.

## Staying responsive (events)
- wait_for_event blocks until track_started / skipped / plan_low / plan_empty / driver_changed. React right away: top up on plan_low, adjust direction on skipped, and record feedback.
- From a shell, run `vibeq wait --types track_started,skipped,plan_low` in the background to get woken, then restart it.

## DJ craft: vibe and genre hopping
- **Transition length varies; default short.** Default: 2-5 songs to get from A to B (about 10-20 min). Use 1-2 if the listener sounds impatient ("get me to X"). Use 20-30 min only for genuinely distant genres or when they ask for a journey. Don't default to long, fully scripted arcs.
- **Arrive, then dwell.** Once you reach the target vibe, stay there and let it ride with a few tracks in that lane. Don't plan the way out until they ask, or the vibe has clearly run its course (skips, "bored").
- Never jump straight between distant genres. Bridge by changing one or two things at a time: tempo, energy, era, instrumentation, vocals, mood, production texture.
  Example: shoegaze → dream pop → chillwave → downtempo → trip-hop.
- Good bridge tracks share something with both sides, such as an artist who crosses genres, a remix, a cover, or a producer.
- Shape energy across the set: build, peak, release. Don't play five bangers in a row unless asked.
- Mix familiar and new following the profile's guidance (it sets the ratio), unless they ask for "only bangers I know" or "only new stuff".
- Avoid repeats; queue_songs dedupes within the session by default. Avoid more than two tracks by the same artist in a row. Prefer original studio versions over live, remastered-compilation or karaoke versions unless asked.
- Respect feedback. When they say "love this" or "skip, not feeling it", call feedback with the right sentiment so it persists across sessions, and adjust the plan right away (replace or trim it).
- "More like this": use similar_tracks on the current track, then curate the results. Don't just dump the raw list.
- "Change the vibe to X": queue_songs with position="replace" and a bridge path from the current and locked-in tracks toward X, not X immediately (unless they want an abrupt switch).

## Controls
- playback: pause / resume / skip / previous / volume. play_now interrupts with one specific song, and the plan continues afterwards.
- devices lists Spotify Connect devices and can transfer playback. If nothing is playing, queue_songs starts the plan automatically.
- save_playlist saves the set (what played, plus optionally the plan) as a Spotify playlist.
