pi-spinner-lyrics 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jamieeeeeeeee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # pi-spinner-lyrics
2
+
3
+ A [pi](https://pi.dev) extension that retires the agent's beige, focus-grouped spinner verbs and replaces them with rotating phrases drawn from the catalog of Phish — cycled every ~2.5s, shimmered Claude-style, and crowned with a waxing-and-waning moon in place of the default braille spinner.
4
+
5
+ ## Tired of boooooring corpo spinner verbs?
6
+
7
+ Let's be honest: "Thinking…", "Working…", "Processing…" — the stock spinner verbs of every LLM tool are not Fun. They do not let you show off your discerning personality from within your LLM configuration.
8
+
9
+ Lucky for you there is always Phish: a polarizing four-piece from Vermont who, across forty years and a catalog north of three hundred originals, have built a body of work that polite music critics have variously described as *eccentric*, *labyrinthine*, and — in one infamous 1997 review of their August 10 show at Deer Creek Music Center — a band that "could urinate in its fans' ears and tell them it's music," with said fans dutifully "there with tape recorders to capture the moment." Detractors call the lyrics nonsense. Phans are there for something that feels slightly akin to summer camp songs — the lyrics are always a little weird, but at this point it's tradition and part of the lore. Either way, the material is uncommonly rich in vivid, oddly-specific gerunds — the exact grammatical shape a spinner verb wants to be — and there is, mercifully, a *lot* of it.
10
+
11
+ This extension mines that repertoire for inline-verb gold. Whether you are a hater who thinks it all sounds like bee-bee-beep-boop-beee-bee-boop-doop-a-loo, or someone with 300 shows under your belt, you will find these prompts measurably more entertaining than the standard corpo verbs. The catalog is deep enough that you will not see the same phrase twice in a working session, and weird enough that you may briefly forget you were waiting on a model at all.
12
+
13
+ Is it useful? Define "useful". Is it better than the OEM spinner? Unambiguously yes.
14
+
15
+ ## What you actually get
16
+
17
+ - A rotating list of short verb-phrases inspired by the Phish songbook, refreshed every ~2.5 seconds while the agent is working.
18
+ - A Claude-style shimmer that sweeps a bright highlight left-to-right across each phrase using your active pi theme's color ramp.
19
+ - A moon-phase emoji indicator (🌑🌒🌓🌔🌕🌖🌗🌘) cycling every 120ms in place of the default braille spinner.
20
+ - Phrases grouped in source comments by song so you can blame, credit, or argue with the curator.
21
+ - Zero runtime dependencies beyond pi itself.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pi install git:github.com/jamieeeeeeeee/pi-spinner-lyrics
27
+ ```
28
+
29
+ Or try it without installing:
30
+
31
+ ```bash
32
+ pi -e git:github.com/jamieeeeeeeee/pi-spinner-lyrics
33
+ ```
34
+
35
+ ## What it does
36
+
37
+ Listens for `agent_start` / `turn_start` and cycles `ui.setWorkingMessage(...)` with a random verb from a hand-curated list of Phish lyrics. Restores the default message on `agent_end` / `session_shutdown`.
38
+
39
+ ## License
40
+
41
+ MIT
@@ -0,0 +1,284 @@
1
+ // Tunables and Phish-lyric verbs used by the spinner-lyrics extension.
2
+ // Adjust the constants below to tune timing, spinner glyphs, and the
3
+ // shimmer highlight without touching the extension logic.
4
+
5
+ // How often to swap to a new random verb, in milliseconds.
6
+ export const VERB_INTERVAL_MS = 2500;
7
+
8
+ // How often to repaint the shimmer highlight, in milliseconds.
9
+ export const SHIMMER_INTERVAL_MS = 80;
10
+
11
+ // Moon-phase frames replace pi's default braille spinner.
12
+ export const MOON_FRAMES = ["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"];
13
+ export const MOON_INTERVAL_MS = 120;
14
+
15
+ // How far the bright "head" travels per shimmer tick, in characters.
16
+ export const SHIMMER_STEP = 1;
17
+
18
+ // Width of the falloff on each side of the head (in characters).
19
+ // Beyond this distance the character renders as "dim".
20
+ export const FALLOFF = 3;
21
+
22
+ export type ThemeColor = "accent" | "text" | "muted" | "dim";
23
+
24
+ // Map distance-from-head → theme color name. Index 0 = head.
25
+ export const RAMP: ThemeColor[] = ["accent", "text", "muted", "dim"];
26
+
27
+ // Phish-lyric verbs used by the spinner-lyrics extension.
28
+ // Grouped by song where known; some are unattributed.
29
+ export const VERBS: string[] = [
30
+ // "Roses Are Free" (Ween cover, frequently played by Phish)
31
+ "Taking a piece of tinsel",
32
+ "Putting it on the tree",
33
+ "Cutting a slice of melon",
34
+ "Carving out a pumpkin",
35
+ "Relying on your destiny",
36
+ "Getting in your car and cruising the land of the brave and free",
37
+ "Understanding exactly what you put on the tree",
38
+ "Disbelieving the florist when he tells you that the roses are free",
39
+ "Taking a wrinkled raisin",
40
+ "Pushing it into third",
41
+ "Eating plenty of lasagna",
42
+ "Resisting all the urges that make you wanna go out and kill",
43
+ "Throwing that pumpkin at the tree",
44
+ "Believing that pumpkin holds your destiny",
45
+ "Casting it off into the sea",
46
+ "Baking that pie and eating it with me",
47
+
48
+ // "Run Like an Antelope"
49
+ "Setting the gearshift",
50
+ "Running like an antelope out of control",
51
+
52
+ // "Reba"
53
+ "Sinking a boulder in the water",
54
+ "Tying a cable to a tree",
55
+ "Stirring it with the ladle that Grandmother bought her",
56
+ "Bagging it, tagging it, selling it to the butcher in the store",
57
+ "Dipping the ladle for a taste of her creation",
58
+ "Hearing the never-ending coral maze",
59
+ "Wiping primal soup from my shoes",
60
+ "Stuffing a newt into my sack",
61
+ "Scraping musty grime off with Rick's fork",
62
+
63
+ // "Bouncing Around the Room"
64
+ "Bouncing around the room",
65
+
66
+ // "Harry Hood"
67
+ "Taking care of my shoes",
68
+ "Feeling good about Hood",
69
+ "Thanking Mr. Miner",
70
+ "Wandering soaking, secretly afraid",
71
+
72
+ // "Sample in a Jar"
73
+ "Wheeling around because I didn't hear",
74
+ "Helping you to your car",
75
+ "Letting the binding belt enclose me",
76
+ "Dancing with Elihu up on Leemor's bed",
77
+ "Letting the market stands unfold",
78
+ "Unheeding all the times I raise my cup",
79
+
80
+ // "Free"
81
+ "Floating in the blimp a lot",
82
+ "Swimming weightless in the womb",
83
+ "Splashing in the sea",
84
+
85
+ // "Golgi Apparatus"
86
+ "Looking into the finance box",
87
+ "Looking into the microscope",
88
+ "Seeing the Golgi Apparatus",
89
+ "Running like a junkyard dog with a brain of brass",
90
+ "Seeing you with a ticket stub in your hand",
91
+
92
+ // "Mike's Song" / "Weekapaug Groove"
93
+ "Sharing in a Weekapaug groove",
94
+ "Trying to make a woman that you move",
95
+ "Walking through the hallways inside my mind",
96
+ "Chasing the backbeat falling behind",
97
+
98
+ // "Wading in the Velvet Sea"
99
+ "Taking a moment from my day",
100
+ "Wrapping it up in things you say",
101
+ "Mailing it off to your address",
102
+ "Wading in the velvet sea",
103
+
104
+ // "Chalk Dust Torture"
105
+ "Climbing up my waterfalls and swinging on my vines",
106
+ "Stealing all my lines",
107
+ "Collecting chalk dust on my tongue",
108
+
109
+ // "Slave to the Traffic Light"
110
+ "Plugging the distress tube up tight",
111
+ "Slaving to the traffic light",
112
+
113
+ // "Limb by Limb"
114
+ "Leaning on a shoulder carved out of stone",
115
+ "Coming unglued while in mid-air",
116
+ "Never wanting my hand cut off",
117
+ "Never wanting a hacking cough",
118
+ "Taking the best parts back",
119
+ "Landing to reform limb by limb",
120
+ "Peeling my fingers off the rim",
121
+ "Turning left",
122
+ "Pooling like water that drips from above",
123
+ "Being trampled by lambs",
124
+ "Tossing with the salad",
125
+
126
+ // "Maze"
127
+ "Stumbling as I fall from Grace",
128
+ "Seeing what I'm hunting a few steps away",
129
+ "Taking a wrong turn on the wrong path",
130
+ "Sweeping into the cavern of shame and the hall of dismay",
131
+ "Never getting out of this maze",
132
+
133
+ // "Esther"
134
+ "Making soup for the ambassadors",
135
+ "Singing to the troubadours",
136
+
137
+ // "Bathtub Gin"
138
+ "Making bathtub gin",
139
+ "Taking a bath, because we're all in this together",
140
+
141
+ // "Stash"
142
+ "Pulling the pavement from under my nails",
143
+ "Brushing past a garden, dependent on whales",
144
+ "Yanking on my tunic",
145
+ "Dangling my stash",
146
+ "Zipping through the forest with the curdling fleas",
147
+ "Seizing the mutant",
148
+ "Crying to my cohorts",
149
+ "Bed-heating the apple toast",
150
+ "Watching the solar garlic start to rot",
151
+ "Poking a double decker on a llama, taboot",
152
+
153
+ // "Ghost"
154
+ "Telling you the story of the ghost",
155
+ "Feeling forsaken",
156
+ "Leaving the latch left unhooked",
157
+ "Waiting in the wind and rain",
158
+ "Calling the ghost instead",
159
+
160
+ // "Birds of a Feather"
161
+ "Flocking outside like birds of a feather",
162
+ "Pecking at the ground and strutting out of stride",
163
+
164
+ // "Tweezer"
165
+ "Stepping into the freezer",
166
+ "Teasing her with a tweezer",
167
+
168
+ // "Fluffhead"
169
+ "Asking the banker for some powerful pills",
170
+
171
+ // "Possum"
172
+ "Coming from atop the mountain where the people come to pray",
173
+ "Engraving words on the walls of the cave",
174
+
175
+ // "Punch You in the Eye"
176
+ "Paddling 'til I could take no more",
177
+ "Punching you in the eye",
178
+ "Tying you to a chair with a giant clip",
179
+ "Laying this hate on you",
180
+
181
+ // "I Didn't Know"
182
+ "Disliking cheeseburger alleycats",
183
+ "Inquiring about the picture of Otis Redding taken just before he died",
184
+ "Breaking my sunny shoe shine",
185
+ "Greeting mustard pies and carrot eyes",
186
+
187
+ // "The Lizards"
188
+ "Meeting an aging knight in the corridor",
189
+ "Surrendering to the flow",
190
+ "Remembering all the thoughts that he had thunk",
191
+ "Realizing the lizards, they have died",
192
+
193
+ // (Phish — source unconfirmed)
194
+ "Bouncing like a newborn elf",
195
+ "Sleeping the sleep of death",
196
+
197
+ // "Esther" (the doll's flight) (?)
198
+ "Floating higher over hills and valleys and treetops",
199
+ "Tumbling and diving through the clouds",
200
+ "Plummeting earthward",
201
+
202
+ // "The Squirming Coil"
203
+ "Watching Satan catch a ray on the beach",
204
+ "Licking the coil some day",
205
+ "Stunning the puppy",
206
+ "Burning the whale",
207
+
208
+ // "Cavern"
209
+ "Forging the coin and licking the stamp",
210
+ "Putting your money where your mouth is",
211
+
212
+ // (Phish — source unconfirmed; possibly "Foam")
213
+ "Running so fast my feet don't touch the ground",
214
+ "Falling into a deep well through the foam",
215
+
216
+ // "Backwards Down the Number Line"
217
+ "Blowing out candles once again",
218
+ "Whispering it into his ear",
219
+
220
+ // "46 Days"
221
+ "Waiting 46 days for the coal to run out",
222
+ "Tasting the fear, for the devil's drawing near",
223
+
224
+ // "The Lizards"
225
+ "Wondering if tigers sleep in lily patches",
226
+
227
+ // "Tube"
228
+ "Living in this tube, so stupendous",
229
+ "Biding my time and taking it slow",
230
+
231
+ // "The Divided Sky"
232
+ "Watching the sky divide",
233
+
234
+ // "You Enjoy Myself"
235
+ "Washing Uffizi",
236
+ "Driving me to Firenze",
237
+
238
+ // "Split Open and Melt"
239
+ "Tossing it in my carryall",
240
+ "Running the wide load to the lip",
241
+ "Watching the big core crack and glow",
242
+ "Splitting open and melting",
243
+ "Bumping my head and raising a welt",
244
+ "Coming apart at the seams",
245
+ "Breathing in a steam dream",
246
+ "Plunging below the water line",
247
+
248
+ // "Fee"
249
+ "Using a bamboo cane to help keep the pace",
250
+ "Racing with the wind",
251
+ "Flirting with death",
252
+ "Hopping and jumping in wooden shoes",
253
+ "Finding Fee and Millie on a lover's trek",
254
+ "Trying to stay with Millie",
255
+ "Getting bottled upside the head",
256
+ "Slamming him in a face with a nectarine",
257
+ "Slicing him on the nipple",
258
+
259
+ // "Harpua"
260
+ "Coming to your town",
261
+ "Helping you party down",
262
+ "Mourning Poster Nutbag",
263
+
264
+ // "Cities" (Talking Heads cover, frequently played by Phish)
265
+ "Finding myself a city to live in",
266
+ "Looking over at the dry ice factory",
267
+
268
+ // "It's Ice"
269
+ "Mimicking the image",
270
+ "Pressing on the elastic sheet",
271
+ "Breathing through a slice",
272
+
273
+ // Dog-Faced Boy
274
+ "Sparing a moment for the dog-faced boy",
275
+ "Lending another hand to the worm girl",
276
+ "Depleting your oxygen",
277
+
278
+ // Piper
279
+ "Waking up Piper, the red, red worm",
280
+
281
+ // Dirt
282
+ "Living beneath the dirt",
283
+ "Being free from push and shove",
284
+ ];
@@ -0,0 +1,131 @@
1
+ // Spinner Lyrics: replaces pi's spinner verb with rotating Phish-lyric verbs,
2
+ // rendered with a Claude-style shimmer (a moving bright highlight sweeping
3
+ // left→right across the text).
4
+ //
5
+ // Behavior:
6
+ // - While the agent is streaming/working, cycles a random verb every ~2.5s.
7
+ // - Between verb changes, repaints the working message every ~80ms with a
8
+ // shimmer highlight that travels across the text, then wraps and repeats.
9
+
10
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
+ import {
12
+ VERBS,
13
+ VERB_INTERVAL_MS,
14
+ SHIMMER_INTERVAL_MS,
15
+ MOON_FRAMES,
16
+ MOON_INTERVAL_MS,
17
+ SHIMMER_STEP,
18
+ FALLOFF,
19
+ RAMP,
20
+ type ThemeColor,
21
+ } from "./constants";
22
+
23
+ type Theme = { fg(color: string, text: string): string };
24
+ type WorkingIndicator = { frames: string[]; intervalMs?: number };
25
+ type WorkingUi = {
26
+ setWorkingMessage(msg?: string): void;
27
+ setWorkingIndicator?(spec?: WorkingIndicator): void;
28
+ theme?: Theme;
29
+ };
30
+
31
+ function colorAt(distance: number): ThemeColor {
32
+ if (distance < 0) distance = -distance;
33
+ if (distance >= RAMP.length) return "dim";
34
+ return RAMP[distance]!;
35
+ }
36
+
37
+ function shimmer(text: string, head: number, theme: Theme | undefined): string {
38
+ if (!theme) return text;
39
+ let out = "";
40
+ for (let i = 0; i < text.length; i++) {
41
+ const ch = text[i]!;
42
+ if (ch === " ") {
43
+ // Spaces have no visible glyph; skip styling to avoid pointless escapes.
44
+ out += ch;
45
+ continue;
46
+ }
47
+ const d = Math.abs(i - head);
48
+ out += theme.fg(colorAt(d), ch);
49
+ }
50
+ return out;
51
+ }
52
+
53
+ export default function (pi: ExtensionAPI) {
54
+ let verbTimer: NodeJS.Timeout | undefined;
55
+ let shimmerTimer: NodeJS.Timeout | undefined;
56
+ let lastUi: WorkingUi | undefined;
57
+ let lastIdx = -1;
58
+ let currentVerb = "";
59
+ let head = -FALLOFF; // start off-screen left so the highlight glides in
60
+
61
+ function pickVerb(): string {
62
+ if (VERBS.length === 0) return "Working";
63
+ if (VERBS.length === 1) return VERBS[0]!;
64
+ let i = Math.floor(Math.random() * VERBS.length);
65
+ if (i === lastIdx) i = (i + 1) % VERBS.length;
66
+ lastIdx = i;
67
+ return VERBS[i]!;
68
+ }
69
+
70
+ function paint() {
71
+ if (!lastUi) return;
72
+ const text = `${currentVerb}…`;
73
+ lastUi.setWorkingMessage(shimmer(text, head, lastUi.theme));
74
+ head += SHIMMER_STEP;
75
+ // Reset once the head has fully traversed the text plus its trailing falloff.
76
+ if (head > text.length + FALLOFF) head = -FALLOFF;
77
+ }
78
+
79
+ function rotate() {
80
+ currentVerb = pickVerb();
81
+ head = -FALLOFF;
82
+ paint();
83
+ }
84
+
85
+ function start(ui: WorkingUi) {
86
+ lastUi = ui;
87
+ ui.setWorkingIndicator?.({ frames: MOON_FRAMES, intervalMs: MOON_INTERVAL_MS });
88
+ if (!verbTimer) {
89
+ rotate();
90
+ verbTimer = setInterval(rotate, VERB_INTERVAL_MS);
91
+ }
92
+ if (!shimmerTimer) {
93
+ shimmerTimer = setInterval(paint, SHIMMER_INTERVAL_MS);
94
+ }
95
+ }
96
+
97
+ function stop() {
98
+ if (verbTimer) {
99
+ clearInterval(verbTimer);
100
+ verbTimer = undefined;
101
+ }
102
+ if (shimmerTimer) {
103
+ clearInterval(shimmerTimer);
104
+ shimmerTimer = undefined;
105
+ }
106
+ if (lastUi) {
107
+ lastUi.setWorkingMessage(); // restore default
108
+ lastUi.setWorkingIndicator?.(); // restore default spinner
109
+ lastUi = undefined;
110
+ }
111
+ }
112
+
113
+ pi.on("agent_start", async (_event, ctx) => {
114
+ if (!ctx.hasUI) return;
115
+ start(ctx.ui as unknown as WorkingUi);
116
+ });
117
+
118
+ pi.on("turn_start", async (_event, ctx) => {
119
+ if (!ctx.hasUI) return;
120
+ // Re-arm in case the previous turn's tool execution cleared it.
121
+ start(ctx.ui as unknown as WorkingUi);
122
+ });
123
+
124
+ pi.on("agent_end", async () => {
125
+ stop();
126
+ });
127
+
128
+ pi.on("session_shutdown", async () => {
129
+ stop();
130
+ });
131
+ }
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "pi-spinner-lyrics",
3
+ "version": "0.1.0",
4
+ "description": "Replaces pi's spinner verb with rotating Phish-lyric verbs.",
5
+ "keywords": ["pi-package", "pi-extension", "spinner", "phish"],
6
+ "author": "jamieeeeeeeee",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/jamieeeeeeeee/pi-spinner-lyrics.git"
11
+ },
12
+ "pi": {
13
+ "extensions": ["./extensions/spinner-lyrics.ts"],
14
+ "image": "https://i.ytimg.com/vi/o0F-NmSkPBM/hqdefault.jpg"
15
+ },
16
+ "peerDependencies": {
17
+ "@earendil-works/pi-coding-agent": "*"
18
+ }
19
+ }