| name | minuet |
| description | Turn meeting notes (or any notes/agenda/transcript) into a short, funny, on-the-nose AI theme song, and optionally play it into a video call as warm-up music. Use whenever the user wants a theme song, jingle, anthem, or hype track made from notes, an agenda, a sprint/standup/retro, or a project — or wants warm-up/hold music for a meeting — or mentions "minuet". Acts as a collaborative producer: brainstorm the song's concept (genre, mood, vocals, length, comedic angle) with the user, then drive the bundled `minuet` CLI to compose lyrics and generate audio via the Gemini API (Lyria) and pipe it through a virtual microphone. Trigger even if the user never says "minuet" — e.g. "make a song from our standup notes", "I want hype music for the team call", "turn these minutes into a jingle". |
minuet — meeting notes → theme song
You are the producer. Shape a song concept in conversation with the user, then drive the
bundled minuet CLI (a Bun + TypeScript tool) to do the deterministic work: compose the lyrics
and a Lyria prompt, generate the audio, and play it into a meeting. The CLI is the engine; you
are the creative director and orchestrator.
The CLI has exactly one input: a SessionConfig JSON blob. Run it from this skill's directory:
minuet '<session-json>' # or: minuet @file.json / minuet --json '<blob>'
# if not linked onto PATH: bun run src/cli.ts '<session-json>'
Everything is controllable from JSON — a single task or a sequence. A SessionConfig is
{ "steps": [...], "generate": <RunConfig|null>, "browser": {...}, "play": {...} } where each entry
in steps is a task (generate, browser, play). Pass one task (e.g. just ["play"]) or chain
several (e.g. ["generate","browser","play"]) — there are no other subcommands. See
references/run-config.md for the exact shape and worked examples. Build
the blob from the conversation and pass it in.
1. First run — configure the key
minuet needs a Gemini API key (the same key grants Lyria), read from .env in this directory.
- If
node_modules/ is missing, run bun install.
- Check for the key:
test -f .env && grep -q '^GEMINI_API_KEY=.' .env && echo ok.
- If it's not set, ask the user for a key (they create one at https://aistudio.google.com/apikey).
Then
cp .env.example .env if needed and put GEMINI_API_KEY=<their key> in .env. Do not echo,
print, or commit the key — .env is gitignored; prefer having the user paste it into .env
(or export GEMINI_API_KEY=…) rather than passing it through a shell command that logs it.
2. Shape the song with the user
The creative input is a SongDescriptor with two expandable fields — style and lyrics — plus
duration and hints. Each field is { sketch, expanded, locked }. The rule that matters:
- if a field's
expanded is set, minuet uses it as-is;
- if only a
sketch is set, minuet's text model expands it at generate time.
You are a capable model, so prefer to do the expansion yourself. Collaborate on the concept,
then write the finished style (a vivid Lyria-tuned production paragraph) and lyrics (with
[Verse]/[Chorus] tags) directly into expanded and set locked: true. minuet then makes no
text-model call and goes straight to Lyria. Use sketches (leave expanded: null) only when you'd
rather minuet's model fill them in.
Either way: roast the work and the situation (recurring agenda items, open questions, the looming
deadline), never individuals, keep lyrics original, and scale the lyrics to any duration
(~30s = one verse + chorus). See references/run-config.md for the shape.
3. Generate
Build a one-step blob and run it:
minuet '{"steps":["generate"],"generate":{"descriptor":{ ...SongDescriptor... }}}'
Each run writes a self-contained folder under runs/<timestamp>_<slug>/: descriptor.json (the
sketches + the expansions that ran), prompt.txt (exactly what Lyria received), lyrics.txt,
session.json (replayable), and song.<ext> (Lyria returns mp3). Tell the user the folder path.
4. Listen, then iterate
- Preview on the user's speakers (the CLI plays into the mic, not speakers, so use a player
directly):
ffplay -nodisp -autoexit runs/<…>/song.mp3 (or mpv), or just tell the user the
path to open.
- Then decide together: ship it; re-roll (run the same blob again — a fresh take, new
folder); or tweak (adjust the concept and regenerate). Iterate until they're happy; every
take is preserved in its own folder.
5. Duration (read before promising "30 seconds")
Length is driven mostly by the lyrics, not a hard knob on the default model. Set
descriptor.duration (e.g. "about 30 seconds") and keep the lyrics short to match (~30s = one
short verse + one chorus — whether you write them or let minuet expand them). For a hard ~30s,
also set models.musicModel to "lyria-3-clip-preview" (always ~30s); the default
"lyria-3-pro-preview" makes full-length (~2 min) songs.
6. Play it into a meeting (optional)
The virtual mic is created with the browser and removed when the session ends — never a separate
step. Do it all in one blob:
minuet '{"steps":["browser","play"],
"browser":{"url":"https://meet.google.com/abc-defg-hij"},
"play":{"file":"runs/<…>/song.mp3","loop":true}}'
This opens a dedicated Chrome with a fresh virtual mic (the user joins and picks "MeetingMic" on
Linux / "BlackHole 2ch" on macOS) and loops the song into it. You can also chain it after a fresh
generate: "steps":["generate","browser","play"] with play.file:null (it uses the song just made).
Notes
- The whole pipeline is the blob, so runs are reproducible —
session.json in each run folder is
a runnable blob. Prefer building blobs over the interactive TUI (bare minuet), which is the
human-at-a-keyboard path.
- If a run fails complaining about the key, fix
.env per §1.