dj-claude 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.
Files changed (87) hide show
  1. package/bin.mjs +24 -0
  2. package/dist/App.d.ts +6 -0
  3. package/dist/App.d.ts.map +1 -0
  4. package/dist/App.js +64 -0
  5. package/dist/App.js.map +1 -0
  6. package/dist/audio/engine.d.ts +15 -0
  7. package/dist/audio/engine.d.ts.map +1 -0
  8. package/dist/audio/engine.js +117 -0
  9. package/dist/audio/engine.js.map +1 -0
  10. package/dist/audio/polyfill.d.ts +2 -0
  11. package/dist/audio/polyfill.d.ts.map +1 -0
  12. package/dist/audio/polyfill.js +122 -0
  13. package/dist/audio/polyfill.js.map +1 -0
  14. package/dist/components/CodeDisplay.d.ts +7 -0
  15. package/dist/components/CodeDisplay.d.ts.map +1 -0
  16. package/dist/components/CodeDisplay.js +12 -0
  17. package/dist/components/CodeDisplay.js.map +1 -0
  18. package/dist/components/DancingClaude.d.ts +6 -0
  19. package/dist/components/DancingClaude.d.ts.map +1 -0
  20. package/dist/components/DancingClaude.js +26 -0
  21. package/dist/components/DancingClaude.js.map +1 -0
  22. package/dist/components/Header.d.ts +8 -0
  23. package/dist/components/Header.d.ts.map +1 -0
  24. package/dist/components/Header.js +28 -0
  25. package/dist/components/Header.js.map +1 -0
  26. package/dist/components/PromptInput.d.ts +9 -0
  27. package/dist/components/PromptInput.d.ts.map +1 -0
  28. package/dist/components/PromptInput.js +7 -0
  29. package/dist/components/PromptInput.js.map +1 -0
  30. package/dist/components/SpeechBubble.d.ts +6 -0
  31. package/dist/components/SpeechBubble.d.ts.map +1 -0
  32. package/dist/components/SpeechBubble.js +25 -0
  33. package/dist/components/SpeechBubble.js.map +1 -0
  34. package/dist/components/StatusBar.d.ts +6 -0
  35. package/dist/components/StatusBar.d.ts.map +1 -0
  36. package/dist/components/StatusBar.js +6 -0
  37. package/dist/components/StatusBar.js.map +1 -0
  38. package/dist/headless.d.ts +2 -0
  39. package/dist/headless.d.ts.map +1 -0
  40. package/dist/headless.js +37 -0
  41. package/dist/headless.js.map +1 -0
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +9 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/lib/ascii-frames.d.ts +3 -0
  47. package/dist/lib/ascii-frames.d.ts.map +1 -0
  48. package/dist/lib/ascii-frames.js +82 -0
  49. package/dist/lib/ascii-frames.js.map +1 -0
  50. package/dist/lib/claude.d.ts +3 -0
  51. package/dist/lib/claude.d.ts.map +1 -0
  52. package/dist/lib/claude.js +44 -0
  53. package/dist/lib/claude.js.map +1 -0
  54. package/dist/lib/config.d.ts +3 -0
  55. package/dist/lib/config.d.ts.map +1 -0
  56. package/dist/lib/config.js +27 -0
  57. package/dist/lib/config.js.map +1 -0
  58. package/dist/lib/parseCode.d.ts +8 -0
  59. package/dist/lib/parseCode.d.ts.map +1 -0
  60. package/dist/lib/parseCode.js +94 -0
  61. package/dist/lib/parseCode.js.map +1 -0
  62. package/dist/lib/prompts.d.ts +2 -0
  63. package/dist/lib/prompts.d.ts.map +1 -0
  64. package/dist/lib/prompts.js +221 -0
  65. package/dist/lib/prompts.js.map +1 -0
  66. package/dist/lib/reducer.d.ts +4 -0
  67. package/dist/lib/reducer.d.ts.map +1 -0
  68. package/dist/lib/reducer.js +79 -0
  69. package/dist/lib/reducer.js.map +1 -0
  70. package/dist/lib/session.d.ts +4 -0
  71. package/dist/lib/session.d.ts.map +1 -0
  72. package/dist/lib/session.js +45 -0
  73. package/dist/lib/session.js.map +1 -0
  74. package/dist/lib/types.d.ts +49 -0
  75. package/dist/lib/types.d.ts.map +1 -0
  76. package/dist/lib/types.js +2 -0
  77. package/dist/lib/types.js.map +1 -0
  78. package/dist/mcp/server.d.ts +2 -0
  79. package/dist/mcp/server.d.ts.map +1 -0
  80. package/dist/mcp/server.js +166 -0
  81. package/dist/mcp/server.js.map +1 -0
  82. package/dist/mcp/state.d.ts +16 -0
  83. package/dist/mcp/state.d.ts.map +1 -0
  84. package/dist/mcp/state.js +32 -0
  85. package/dist/mcp/state.js.map +1 -0
  86. package/mcp.mjs +15 -0
  87. package/package.json +45 -0
@@ -0,0 +1,221 @@
1
+ // System prompt for DJ Claude CLI — adapted from the web version.
2
+ // Removed slider(), ._scope(), and ._pianoroll() references since
3
+ // the terminal cannot render those interactive/visual elements.
4
+ export const SYSTEM_PROMPT = `You are DJ Claude — a virtuoso live-coding musician and MC who creates rich, layered, evolving electronic music using Strudel. You are not a code assistant who happens to make sounds. You are a MUSICIAN who thinks in grooves, textures, tension, and release, and who expresses those ideas through Strudel code.
5
+
6
+ Your music should sound like a REAL producer made it — full frequency spectrum, dynamic movement, intentional arrangement. Never output thin, static, or repetitive code.
7
+
8
+ ## Response Format
9
+ Respond with a JSON object containing two fields:
10
+ {
11
+ "code": "<your Strudel code here>",
12
+ "mcCommentary": "<your MC commentary here>"
13
+ }
14
+
15
+ IMPORTANT:
16
+ - Return ONLY the JSON object, no markdown code fences
17
+ - The code field contains valid Strudel code
18
+ - The mcCommentary field contains your hype + explanation (1-2 short sentences, max 120 characters ideal)
19
+ - Do NOT use slider(), ._scope(), or ._pianoroll() — this is a terminal environment with no visual rendering
20
+
21
+ ## MC Commentary Guidelines
22
+ - Keep it SHORT and punchy (1-2 sentences, max ~120 chars)
23
+ - Mix HYPE energy with musical insight
24
+ - Name specific techniques you're using ("euclidean snare", "filter sweep", "detuned chords")
25
+ - Use DJ/club language: "dropping", "vibes", "groovy", "nasty", "fire", "deep", "heavy" etc.
26
+ - Match your energy to the music — dark tracks get dark commentary, hype tracks get hype commentary
27
+
28
+ MC Commentary Examples:
29
+ - "Deep sawtooth sub with minor sevenths drifting over a halftime groove. Moody."
30
+ - "Euclidean claps over four-on-the-floor. Filter sweep rising. Here it comes!"
31
+ - "Detuned pads wide in stereo. Ghost notes on the hats. Late night vibes."
32
+ - "Syncopated bass hitting those off-beats. Polyrhythmic cowbell. Funk city!"
33
+ - "Cosmic reverb trails and Perlin noise modulation. We're in orbit now."
34
+
35
+ ## Strudel Reference
36
+
37
+ ### Core Functions
38
+ - note("c3 e3 g3") — play notes (use with .s() for synth choice)
39
+ - s("bd sd hh") — play samples
40
+ - stack(p1, p2, p3, ...) — layer patterns simultaneously (your primary tool for building depth)
41
+ - cat(p1, p2) — sequence patterns across cycles (use for A/B sections, intros vs drops)
42
+ - note("[c3,e3,g3]") — play multiple notes simultaneously as a chord
43
+
44
+ ### Effects & Processing
45
+ - .lpf(freq) / .hpf(freq) — low-pass / high-pass filter (essential for frequency separation)
46
+ - .resonance(n) — filter resonance (0-1, adds bite and character to filters)
47
+ - .gain(n) — volume (0-1)
48
+ - .room(n) / .size(n) — reverb amount and decay size
49
+ - .delay(n) / .delaytime(t) / .delayfeedback(n) — delay wet amount, time, and feedback
50
+ - .pan(n) — stereo position (0 = left, 0.5 = center, 1 = right)
51
+ - .vowel("a e i o u") — formant filter (vocal-like textures, great for pads)
52
+ - .vowel("a e i o u") can also add grit when combined with .resonance()
53
+ - .speed(n) — sample playback speed/pitch (0.5 = octave down, 2 = octave up)
54
+ - .cut(n) — cut group (stops overlapping samples, e.g. closed hat cutting open hat)
55
+
56
+ ### Modulation — Creating Movement
57
+ Use continuous patterns as parameter values to make sounds ALIVE and evolving:
58
+ - sine.range(min, max).slow(n) — smooth sine LFO
59
+ - cosine.range(min, max).slow(n) — cosine LFO (offset from sine)
60
+ - saw.range(min, max).slow(n) — ramp LFO (builds then resets)
61
+ - perlin.range(min, max) — smooth Perlin noise (organic, unpredictable movement)
62
+ - rand — random value per event (use for humanization and texture)
63
+
64
+ Usage examples:
65
+ - .lpf(sine.range(300, 2000).slow(8)) — filter sweep over 8 cycles
66
+ - .gain(perlin.range(0.3, 0.6)) — organic volume variation
67
+ - .pan(sine.slow(3)) — auto-panning
68
+ - .speed(sine.range(0.8, 1.2).slow(12)) — subtle pitch drift
69
+
70
+ ### Temporal Variation — Patterns That Evolve Over Time
71
+ - .every(n, fn) — apply transformation every nth cycle
72
+ e.g. .every(4, x => x.fast(2)) — double-time every 4th bar
73
+ e.g. .every(3, x => x.rev()) — reverse every 3rd cycle
74
+ e.g. .every(8, x => x.lpf(400)) — filter dip every 8 bars
75
+ - .sometimes(fn) — apply transformation ~50% of the time
76
+ - .rarely(fn) — apply transformation ~25% of the time
77
+ - .often(fn) — apply transformation ~75% of the time
78
+ - .degradeBy(n) — randomly drop events (0-1, creates organic gaps)
79
+ - .jux(fn) — apply transformation in one stereo channel only
80
+ e.g. .jux(rev) — reverse in one ear (instant stereo width)
81
+ e.g. .jux(x => x.fast(2)) — double-time in one ear
82
+ - .off(t, fn) — superimpose a time-shifted, transformed copy of the pattern
83
+ e.g. .off(1/8, x => x.gain(0.5)) — quieter echo at 1/8 cycle offset
84
+ - .fast(n) / .slow(n) — speed up or slow down a pattern
85
+ - .rev() — reverse the pattern order
86
+
87
+ ### Mini Notation
88
+ - "a b c d" — events spread evenly across one cycle
89
+ - "[a b c]" — group into one step (subdivision)
90
+ - "<a b c>" — alternate per cycle (ESSENTIAL for evolution and harmonic progression!)
91
+ - "a*4" — repeat within the step (hh*8 = fast hi-hats)
92
+ - "a/2" — play every 2nd cycle (sparse elements, slow reveals)
93
+ - "a?" — 50% chance to play (humanization, ghost notes)
94
+ - "a(3,8)" — euclidean rhythm (powerful for polyrhythms and organic grooves!)
95
+ - "~" — rest/silence (critical for syncopation and groove)
96
+ - "a:2" — sample variant (bd:0, bd:1, bd:2 for timbral variety on the same instrument)
97
+ - "a@3" — stretch event over 3 time units
98
+ - "[a [b c]]" — nested grouping for complex subdivisions and swing feel
99
+
100
+ ### Available Sounds
101
+ Percussion: bd, sd, hh, oh, cp, cb, cr
102
+ Synths (use with note().s()): sawtooth, square, sine, triangle
103
+ NOTE: Do NOT use piano, bass, gtr, rhodes, strings, brass — these sample packs are not loaded.
104
+ NOTE: Do NOT use .shape(), .crush(), .coarse(), or .pattern() — AudioWorklet is not available in this environment. For grit/distortion effects, use heavy .lpf() filtering, .vowel(), or aggressive .resonance() instead.
105
+
106
+ ### Note Names
107
+ Use ONLY standard note names: c, d, e, f, g, a, b with optional # or b for sharps/flats.
108
+ Examples: c3, eb3, f#4, bb2. Do NOT use "cm", "fm", "gm" — these are not valid note names in Strudel.
109
+ For minor chords, spell out the notes: [c3,eb3,g3] NOT "cm3".
110
+
111
+ Getting timbral variety from the available sounds:
112
+ - Layer synths at different octaves: saw bass (c1-c2) + triangle pad (c3-c4) + square lead (c4-c5)
113
+ - Shape timbre with .lpf() at different cutoffs — same synth, radically different character
114
+ - Use .vowel() and .resonance() to add texture and grit
115
+ - Use .speed() on percussion to pitch-shift drums into new territory
116
+ - Detune layers: stack the same synth note with one copy slightly .speed(1.01) for thickness
117
+ - Use sample variants (bd:0, bd:1, sd:0, sd:1) for subtle timbral shifts in drum patterns
118
+
119
+ ## Musical Depth Requirements
120
+
121
+ CRITICAL: Every response must produce music that sounds FULL, LAYERED, and ALIVE. Follow these standards:
122
+
123
+ ### 1. Layer Count
124
+ Build every pattern with AT LEAST 5-8 layers in your stack(). A complete mix typically includes:
125
+ - Sub bass or bass line (octave 1-2, saw or sine, filtered low)
126
+ - Chords or pad (octave 3-4, provides harmonic body)
127
+ - Melody, lead, or textural element (octave 4-5, something that catches the ear)
128
+ - Kick drum pattern
129
+ - Snare or clap pattern
130
+ - Hi-hats or cymbal pattern
131
+ - Additional percussion or rhythmic texture (rim, cb, cp with euclidean patterns, etc.)
132
+ - FX layer or atmospheric element (heavily delayed/reverbed hits, filtered noise-like textures)
133
+ Even "minimal" music should have AT LEAST 5 carefully crafted layers.
134
+
135
+ ### 2. Frequency Separation
136
+ Carve out space so each layer is heard clearly:
137
+ - Sub/Bass: notes in octave 1-2, use .lpf(200-500) to keep it low and powerful
138
+ - Chords/Pad: octave 3-4, the harmonic body of the track
139
+ - Lead/Melody: octave 4-5, cuts through the mix
140
+ - Use .hpf(30-60) on the master (end of stack) to remove rumble
141
+ - Use .lpf() on high percussion to tame harshness when needed
142
+ - Use .hpf() on mid/high elements to keep them out of the bass range
143
+
144
+ ### 3. Dynamic Hierarchy
145
+ Not everything should be the same volume. Create a clear mix:
146
+ - Kick: gain 0.7-0.85 (the anchor)
147
+ - Bass: gain 0.5-0.7 (powerful but below the kick)
148
+ - Snare/Clap: gain 0.45-0.65 (punchy but not overpowering)
149
+ - Chords/Pad: gain 0.2-0.4 (supportive, fills space without dominating)
150
+ - Hi-hats: gain 0.15-0.35 (texture, not the focus)
151
+ - Lead/Melody: gain 0.15-0.3 (present but balanced)
152
+ - FX/Texture: gain 0.05-0.2 (subtle ear candy)
153
+
154
+ ### 4. Movement Is Mandatory
155
+ NEVER output static, unchanging loops. Every pattern must include at least 2-3 of these:
156
+ - <> cycling in note patterns so harmonies progress over multiple cycles
157
+ - sine/cosine/perlin modulation on filters, gains, or panning
158
+ - .every() or .sometimes() for periodic rhythmic or timbral variation
159
+ - .degradeBy() or ? probability for organic feel
160
+ - .jux() for stereo movement and width
161
+ - Pattern elements that change over 4-16 cycle arcs
162
+
163
+ ### 5. Rhythmic Depth
164
+ Build grooves with character, not just metronomic hits:
165
+ - Syncopation: put hits on unexpected beats, use ~ rests strategically
166
+ - Ghost notes: low-gain (0.1-0.2) hits on off-beats for groove
167
+ - Vary hi-hat patterns with subdivision: "[~ hh]*4" is a start, "[hh [~ hh]] [hh hh] [hh [hh oh]] [hh ~]" has real groove
168
+ - Euclidean rhythms for organic polyrhythmic feel: sd(3,8), cp(5,8), rim(7,16)
169
+ - Use <> to cycle through different drum pattern variations per bar
170
+
171
+ ## Prompt Interpretation
172
+
173
+ When a user gives you a prompt, THINK MUSICALLY. Translate their words into specific sonic decisions:
174
+
175
+ ### Mood → Musical Decisions
176
+ - "chill / relaxed / mellow / smooth" → .slow(1.2-1.8), triangle and sine waves, heavy room/size reverb, sparse drums, minor 7th and 9th chords, gentle filter modulation
177
+ - "dark / heavy / intense / aggressive" → low frequencies emphasized, sawtooth waves, heavy .lpf() filtering, minor and diminished chords, driving kick patterns, .resonance() on percussion, filtered atmosphere
178
+ - "happy / bright / uplifting / sunny" → major chords, higher octaves for leads, open hi-hats, square wave leads with moderate filtering, less reverb (cleaner), moderate tempo
179
+ - "weird / experimental / glitchy / broken" → euclidean rhythms everywhere, heavy .lpf() and .resonance(), heavy probability with ?, .jux() with unusual transforms, asymmetric time groupings, .off() canons
180
+ - "epic / big / massive / anthemic" → many layers (8+), wide stereo via .jux() and .pan(), long reverb tails, building filter sweeps with slow sine modulation, layered chord voicings
181
+ - "funky / groovy / bouncy" → syncopated bass lines (rests on downbeats, hits on off-beats), staccato chord stabs, claps on the backbeat, higher tempo feel, cowbell or rim patterns
182
+ - "dreamy / ethereal / floating / cosmic" → heavy reverb+delay chains with feedback, sine and triangle waves, very slow filter movement (.slow(16)), sparse percussion, wide panning, .off() for canonic textures
183
+
184
+ ### Genre → Specific Techniques
185
+ - "house" → four-on-the-floor kick (bd*4), off-beat open hats (~ oh ~ oh), chord stabs, filtered bass, claps on 2 and 4
186
+ - "techno" → driving kick (bd*4), minimal melodics, industrial percussion (cb with .resonance()), heavy filter automation, darker tonality
187
+ - "ambient" → no drums or very sparse, long reverb (.room(0.8+).size(0.95+)), slow evolution (.slow(2+)), layered pads, .delay() with high feedback, wide stereo
188
+ - "dnb / drum and bass" → fast breakbeat patterns with syncopated snares, deep sub bass (sine wave, octave 1), .fast() or keep default speed, complex hat patterns
189
+ - "lo-fi / lofi" → heavy .lpf() on everything (muffled feel), jazzy chords (7ths, 9ths, extended voicings), slow tempo, sparse kick and snare
190
+ - "trap" → deep 808 bass (low saw, .lpf(200)), sparse kick, rolling hi-hats with gain ramps (hh*16 with varying gain), snare/clap on beat 3, half-time feel
191
+ - "disco / retro" → syncopated bass lines, open hats on every off-beat, chord stabs, funky clavinet-style square patterns, four-on-the-floor kick
192
+
193
+ ### Abstract / Creative Prompts
194
+ When users give evocative, non-musical prompts like "rainy night in Tokyo", "sunrise on Mars", "underwater cathedral", or "robot love song" — these are invitations to be MAXIMALLY CREATIVE. Translate the FEELING, IMAGERY, and ATMOSPHERE into specific musical choices. These prompts should produce your most unique and interesting output. Think about:
195
+ - What tempo does this scene feel like?
196
+ - What frequency range dominates? (Low and warm? High and crystalline?)
197
+ - Is it sparse or dense? Rhythmic or floating?
198
+ - What effects create this atmosphere? (Rain = delay feedback, Space = huge reverb, Underwater = heavy lpf)
199
+
200
+ ## How to Handle Follow-Up Prompts
201
+ When modifying existing code based on user direction:
202
+ - "make it darker/heavier" → lower frequencies, more filtering, add distortion, minor/diminished harmony, slower
203
+ - "make it brighter/lighter" → raise octaves, open filters, major harmony, remove distortion
204
+ - "add energy/more intense" → add layers, increase tempo feel, add percussion, open filters, increase gain on rhythmic elements
205
+ - "strip it back/minimal" → remove layers selectively (keep the most interesting ones), add space with rests
206
+ - "more variation" → add .every(), .sometimes(), more <> cycling, euclidean patterns, .jux()
207
+ - "completely different / something new" → start fresh with a totally new composition — different key, different tempo, different genre feel
208
+ - Small adjustments: preserve the existing structure and modify parameters
209
+ - Big requests: don't be afraid to restructure significantly — add or replace multiple layers
210
+
211
+ ## Guidelines
212
+ 1. ALWAYS build rich, layered patterns — minimum 5 layers in every stack(), aim for 7-8
213
+ 2. EVERY pattern must evolve over time — use <> cycling, .every(), .sometimes(), modulation, .degradeBy()
214
+ 3. VARY your output significantly between different prompts — don't fall into template patterns. Different user prompts should produce genuinely different music
215
+ 4. Think about frequency balance in every response — bass, mid, and high elements all present
216
+ 5. Use effects chains to create space and depth — filter + reverb + delay + panning creates a full soundscape
217
+ 6. When evolving existing code, make MEANINGFUL changes — don't just tweak one number
218
+ 7. Interpret user prompts CREATIVELY — map emotions, imagery, and genres to specific musical decisions
219
+ 8. Use cat() to create multi-section compositions when appropriate (verse/chorus, buildup/drop)
220
+ 9. Use proper mix hierarchy — not everything at the same gain level`;
221
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/lib/prompts.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,kEAAkE;AAClE,gEAAgE;AAEhE,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oEAwNuC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { DJState, DJAction } from './types.js';
2
+ export declare const initialState: DJState;
3
+ export declare function djReducer(state: DJState, action: DJAction): DJState;
4
+ //# sourceMappingURL=reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/lib/reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEpD,eAAO,MAAM,YAAY,EAAE,OAW1B,CAAC;AAEF,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CA4EnE"}
@@ -0,0 +1,79 @@
1
+ export const initialState = {
2
+ audioInitialized: false,
3
+ isPlaying: false,
4
+ currentCode: '',
5
+ streamingCode: '',
6
+ previousCode: '',
7
+ isStreaming: false,
8
+ streamingError: null,
9
+ executionError: null,
10
+ mcCommentary: '',
11
+ messages: [],
12
+ };
13
+ export function djReducer(state, action) {
14
+ switch (action.type) {
15
+ case 'AUDIO_INITIALIZED':
16
+ return { ...state, audioInitialized: true };
17
+ case 'START_STREAMING':
18
+ return {
19
+ ...state,
20
+ isStreaming: true,
21
+ streamingCode: '',
22
+ streamingError: null,
23
+ executionError: null,
24
+ };
25
+ case 'APPEND_STREAM':
26
+ return { ...state, streamingCode: state.streamingCode + action.text };
27
+ case 'STREAM_COMPLETE':
28
+ return {
29
+ ...state,
30
+ isStreaming: false,
31
+ messages: [...state.messages, { role: 'assistant', content: action.code }],
32
+ };
33
+ case 'STREAM_ERROR':
34
+ return {
35
+ ...state,
36
+ isStreaming: false,
37
+ streamingError: action.error,
38
+ };
39
+ case 'CODE_EXECUTED':
40
+ return {
41
+ ...state,
42
+ previousCode: state.currentCode,
43
+ currentCode: action.code,
44
+ isPlaying: true,
45
+ executionError: null,
46
+ };
47
+ case 'EXECUTION_ERROR':
48
+ return {
49
+ ...state,
50
+ executionError: action.error,
51
+ };
52
+ case 'ADD_USER_MESSAGE':
53
+ return {
54
+ ...state,
55
+ messages: [...state.messages, { role: 'user', content: action.content }],
56
+ };
57
+ case 'SET_MC_COMMENTARY':
58
+ return { ...state, mcCommentary: action.text };
59
+ case 'CLEAR_ERROR':
60
+ return {
61
+ ...state,
62
+ streamingError: null,
63
+ executionError: null,
64
+ };
65
+ case 'HUSH':
66
+ return { ...state, isPlaying: false };
67
+ case 'RESTORE_PREVIOUS':
68
+ if (!state.previousCode)
69
+ return state;
70
+ return {
71
+ ...state,
72
+ currentCode: state.previousCode,
73
+ previousCode: state.currentCode,
74
+ };
75
+ default:
76
+ return state;
77
+ }
78
+ }
79
+ //# sourceMappingURL=reducer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.js","sourceRoot":"","sources":["../../src/lib/reducer.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAY;IACnC,gBAAgB,EAAE,KAAK;IACvB,SAAS,EAAE,KAAK;IAChB,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,EAAE;IACjB,YAAY,EAAE,EAAE;IAChB,WAAW,EAAE,KAAK;IAClB,cAAc,EAAE,IAAI;IACpB,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,EAAE;IAChB,QAAQ,EAAE,EAAE;CACb,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,MAAgB;IACxD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,mBAAmB;YACtB,OAAO,EAAE,GAAG,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;QAE9C,KAAK,iBAAiB;YACpB,OAAO;gBACL,GAAG,KAAK;gBACR,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,EAAE;gBACjB,cAAc,EAAE,IAAI;gBACpB,cAAc,EAAE,IAAI;aACrB,CAAC;QAEJ,KAAK,eAAe;YAClB,OAAO,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAExE,KAAK,iBAAiB;YACpB,OAAO;gBACL,GAAG,KAAK;gBACR,WAAW,EAAE,KAAK;gBAClB,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;aAC3E,CAAC;QAEJ,KAAK,cAAc;YACjB,OAAO;gBACL,GAAG,KAAK;gBACR,WAAW,EAAE,KAAK;gBAClB,cAAc,EAAE,MAAM,CAAC,KAAK;aAC7B,CAAC;QAEJ,KAAK,eAAe;YAClB,OAAO;gBACL,GAAG,KAAK;gBACR,YAAY,EAAE,KAAK,CAAC,WAAW;gBAC/B,WAAW,EAAE,MAAM,CAAC,IAAI;gBACxB,SAAS,EAAE,IAAI;gBACf,cAAc,EAAE,IAAI;aACrB,CAAC;QAEJ,KAAK,iBAAiB;YACpB,OAAO;gBACL,GAAG,KAAK;gBACR,cAAc,EAAE,MAAM,CAAC,KAAK;aAC7B,CAAC;QAEJ,KAAK,kBAAkB;YACrB,OAAO;gBACL,GAAG,KAAK;gBACR,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;aACzE,CAAC;QAEJ,KAAK,mBAAmB;YACtB,OAAO,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QAEjD,KAAK,aAAa;YAChB,OAAO;gBACL,GAAG,KAAK;gBACR,cAAc,EAAE,IAAI;gBACpB,cAAc,EAAE,IAAI;aACrB,CAAC;QAEJ,KAAK,MAAM;YACT,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAExC,KAAK,kBAAkB;YACrB,IAAI,CAAC,KAAK,CAAC,YAAY;gBAAE,OAAO,KAAK,CAAC;YACtC,OAAO;gBACL,GAAG,KAAK;gBACR,WAAW,EAAE,KAAK,CAAC,YAAY;gBAC/B,YAAY,EAAE,KAAK,CAAC,WAAW;aAChC,CAAC;QAEJ;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { DJAction } from './types.js';
2
+ import type { Message } from './types.js';
3
+ export declare function handlePrompt(apiKey: string, prompt: string, currentCode: string, messages: Message[], dispatch: (action: DJAction) => void): Promise<void>;
4
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAkB1C,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,IAAI,GACnC,OAAO,CAAC,IAAI,CAAC,CAiCf"}
@@ -0,0 +1,45 @@
1
+ import { streamChat } from './claude.js';
2
+ import { parseStreamingCode } from './parseCode.js';
3
+ import { safeEvaluate } from '../audio/engine.js';
4
+ async function evalAndDispatch(code, previousCode, dispatch) {
5
+ const result = await safeEvaluate(code, previousCode);
6
+ if (result.success) {
7
+ dispatch({ type: 'CODE_EXECUTED', code });
8
+ }
9
+ else {
10
+ dispatch({ type: 'EXECUTION_ERROR', error: result.error });
11
+ }
12
+ }
13
+ export async function handlePrompt(apiKey, prompt, currentCode, messages, dispatch) {
14
+ dispatch({ type: 'START_STREAMING' });
15
+ try {
16
+ let fullText = '';
17
+ for await (const chunk of streamChat(apiKey, prompt, currentCode, messages)) {
18
+ fullText += chunk;
19
+ dispatch({ type: 'APPEND_STREAM', text: chunk });
20
+ const parsed = parseStreamingCode(fullText);
21
+ if (parsed.isComplete) {
22
+ dispatch({ type: 'STREAM_COMPLETE', code: fullText });
23
+ dispatch({ type: 'SET_MC_COMMENTARY', text: parsed.mcCommentary });
24
+ await evalAndDispatch(parsed.extractedCode, currentCode, dispatch);
25
+ return;
26
+ }
27
+ }
28
+ // Stream ended without complete parse — try anyway
29
+ const finalParse = parseStreamingCode(fullText);
30
+ if (finalParse.displayCode) {
31
+ dispatch({ type: 'STREAM_COMPLETE', code: fullText });
32
+ await evalAndDispatch(finalParse.displayCode, currentCode, dispatch);
33
+ }
34
+ else {
35
+ dispatch({ type: 'STREAM_ERROR', error: 'Could not parse response from Claude' });
36
+ }
37
+ }
38
+ catch (err) {
39
+ dispatch({
40
+ type: 'STREAM_ERROR',
41
+ error: err instanceof Error ? err.message : String(err),
42
+ });
43
+ }
44
+ }
45
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,KAAK,UAAU,eAAe,CAC5B,IAAY,EACZ,YAAoB,EACpB,QAAoC;IAEpC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,CAAC,KAAM,EAAE,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,MAAc,EACd,WAAmB,EACnB,QAAmB,EACnB,QAAoC;IAEpC,QAAQ,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,IAAI,QAAQ,GAAG,EAAE,CAAC;QAElB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC5E,QAAQ,IAAI,KAAK,CAAC;YAClB,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAEjD,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACtB,QAAQ,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACtD,QAAQ,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;gBACnE,MAAM,eAAe,CAAC,MAAM,CAAC,aAAa,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;gBACnE,OAAO;YACT,CAAC;QACH,CAAC;QAED,mDAAmD;QACnD,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;YAC3B,QAAQ,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YACtD,MAAM,eAAe,CAAC,UAAU,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,QAAQ,CAAC;YACP,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -0,0 +1,49 @@
1
+ export interface Message {
2
+ role: 'user' | 'assistant';
3
+ content: string;
4
+ }
5
+ export interface DJState {
6
+ audioInitialized: boolean;
7
+ isPlaying: boolean;
8
+ currentCode: string;
9
+ streamingCode: string;
10
+ previousCode: string;
11
+ isStreaming: boolean;
12
+ streamingError: string | null;
13
+ executionError: string | null;
14
+ mcCommentary: string;
15
+ messages: Message[];
16
+ }
17
+ export type DJAction = {
18
+ type: 'AUDIO_INITIALIZED';
19
+ } | {
20
+ type: 'START_STREAMING';
21
+ } | {
22
+ type: 'APPEND_STREAM';
23
+ text: string;
24
+ } | {
25
+ type: 'STREAM_COMPLETE';
26
+ code: string;
27
+ } | {
28
+ type: 'STREAM_ERROR';
29
+ error: string;
30
+ } | {
31
+ type: 'CODE_EXECUTED';
32
+ code: string;
33
+ } | {
34
+ type: 'EXECUTION_ERROR';
35
+ error: string;
36
+ } | {
37
+ type: 'ADD_USER_MESSAGE';
38
+ content: string;
39
+ } | {
40
+ type: 'SET_MC_COMMENTARY';
41
+ text: string;
42
+ } | {
43
+ type: 'CLEAR_ERROR';
44
+ } | {
45
+ type: 'HUSH';
46
+ } | {
47
+ type: 'RESTORE_PREVIOUS';
48
+ };
49
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function startServer(): Promise<void>;
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAsNA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAOjD"}
@@ -0,0 +1,166 @@
1
+ // MCP server — exposes DJ Claude as 5 tools over stdio transport.
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { z } from 'zod';
5
+ import { initEngine, safeEvaluate, hush as engineHush } from '../audio/engine.js';
6
+ import { findApiKey } from '../lib/config.js';
7
+ import { streamChat } from '../lib/claude.js';
8
+ import { parseStreamingCode } from '../lib/parseCode.js';
9
+ import { getState, setEngineReady, updateAfterPlay, updateAfterHush, addMessage, } from './state.js';
10
+ // ---------------------------------------------------------------------------
11
+ // Engine initialization — kicked off eagerly, tools await the promise.
12
+ // ---------------------------------------------------------------------------
13
+ let enginePromise = null;
14
+ function startEngine() {
15
+ enginePromise = initEngine()
16
+ .then(() => {
17
+ setEngineReady();
18
+ console.error('[dj-claude-mcp] Audio engine ready.');
19
+ })
20
+ .catch((err) => {
21
+ console.error('[dj-claude-mcp] Engine init failed:', err);
22
+ });
23
+ }
24
+ async function ensureEngine() {
25
+ if (!enginePromise)
26
+ startEngine();
27
+ await enginePromise;
28
+ if (!getState().engineReady) {
29
+ throw new Error('Audio engine failed to initialize.');
30
+ }
31
+ }
32
+ // ---------------------------------------------------------------------------
33
+ // Core helper — stream Claude, parse response, evaluate code.
34
+ // ---------------------------------------------------------------------------
35
+ async function generateAndPlay(prompt) {
36
+ const apiKey = findApiKey();
37
+ if (!apiKey) {
38
+ throw new Error('ANTHROPIC_API_KEY not set. Use play_strudel to play Strudel code directly, or set your API key for AI-generated music.');
39
+ }
40
+ const { currentCode, messages } = getState();
41
+ addMessage({ role: 'user', content: prompt });
42
+ let fullText = '';
43
+ for await (const chunk of streamChat(apiKey, prompt, currentCode, messages)) {
44
+ fullText += chunk;
45
+ }
46
+ const parsed = parseStreamingCode(fullText);
47
+ const code = parsed.isComplete ? parsed.extractedCode : parsed.displayCode;
48
+ if (!code) {
49
+ throw new Error('Could not parse Strudel code from Claude response.');
50
+ }
51
+ addMessage({ role: 'assistant', content: fullText });
52
+ await ensureEngine();
53
+ const result = await safeEvaluate(code, currentCode);
54
+ if (!result.success) {
55
+ throw new Error(`Strudel evaluation error: ${result.error}`);
56
+ }
57
+ updateAfterPlay(code, parsed.mcCommentary);
58
+ return { commentary: parsed.mcCommentary, code };
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // Mood → prompt mapping for set_vibe.
62
+ // ---------------------------------------------------------------------------
63
+ const MOOD_PROMPTS = {
64
+ chill: 'Play something chill and relaxing — lo-fi beats, soft pads, gentle rhythms. Keep it mellow.',
65
+ dark: 'Play something dark and moody — minor keys, heavy bass, atmospheric textures. Brooding and intense.',
66
+ hype: 'Play something hype and energetic — driving beats, punchy bass, high energy. Get the adrenaline going!',
67
+ focus: 'Play deep focus music — minimal, repetitive, non-distracting. Ambient textures with a subtle pulse. Perfect for coding.',
68
+ funky: 'Play something funky — groovy basslines, syncopated rhythms, playful melodies. Make it bounce!',
69
+ dreamy: 'Play something dreamy and ethereal — lush reverb, floating pads, gentle arpeggios. Otherworldly and beautiful.',
70
+ weird: 'Play something weird and experimental — unusual sounds, unpredictable rhythms, glitchy textures. Push boundaries!',
71
+ epic: 'Play something epic and cinematic — big builds, soaring melodies, powerful drums. Make it feel legendary.',
72
+ };
73
+ // ---------------------------------------------------------------------------
74
+ // MCP server setup.
75
+ // ---------------------------------------------------------------------------
76
+ const server = new McpServer({
77
+ name: 'dj-claude',
78
+ version: '0.1.0',
79
+ });
80
+ // -- play_music -----------------------------------------------------------
81
+ server.tool('play_music', 'Generate and play live music. Describe what you want to hear — a genre, mood, activity, or anything creative. DJ Claude will compose a Strudel pattern and play it through the speakers.', { prompt: z.string().describe('What kind of music to play, e.g. "jazzy lo-fi beats" or "intense drum and bass"') }, async ({ prompt }) => {
82
+ await ensureEngine();
83
+ const { commentary, code } = await generateAndPlay(prompt);
84
+ return {
85
+ content: [
86
+ {
87
+ type: 'text',
88
+ text: commentary
89
+ ? `${commentary}\n\nNow playing:\n\`\`\`javascript\n${code}\n\`\`\``
90
+ : `Now playing:\n\`\`\`javascript\n${code}\n\`\`\``,
91
+ },
92
+ ],
93
+ };
94
+ });
95
+ // -- play_strudel ---------------------------------------------------------
96
+ server.tool('play_strudel', 'Evaluate raw Strudel/Tidal code directly, bypassing Claude generation. Use this when you already have Strudel code to play.', { code: z.string().describe('Strudel/Tidal code to evaluate') }, async ({ code }) => {
97
+ await ensureEngine();
98
+ const { currentCode } = getState();
99
+ const result = await safeEvaluate(code, currentCode);
100
+ if (!result.success) {
101
+ return {
102
+ content: [{ type: 'text', text: `Evaluation error: ${result.error}` }],
103
+ isError: true,
104
+ };
105
+ }
106
+ updateAfterPlay(code, '');
107
+ return {
108
+ content: [{ type: 'text', text: `Now playing:\n\`\`\`javascript\n${code}\n\`\`\`` }],
109
+ };
110
+ });
111
+ // -- set_vibe -------------------------------------------------------------
112
+ server.tool('set_vibe', 'Instantly set the musical vibe to match a mood. Great for matching music to the current coding task.', {
113
+ mood: z.enum(['chill', 'dark', 'hype', 'focus', 'funky', 'dreamy', 'weird', 'epic'])
114
+ .describe('The mood/vibe to set'),
115
+ }, async ({ mood }) => {
116
+ await ensureEngine();
117
+ const prompt = MOOD_PROMPTS[mood];
118
+ const { commentary, code } = await generateAndPlay(prompt);
119
+ return {
120
+ content: [
121
+ {
122
+ type: 'text',
123
+ text: commentary
124
+ ? `Vibe set to ${mood}! ${commentary}\n\n\`\`\`javascript\n${code}\n\`\`\``
125
+ : `Vibe set to ${mood}!\n\n\`\`\`javascript\n${code}\n\`\`\``,
126
+ },
127
+ ],
128
+ };
129
+ });
130
+ // -- hush -----------------------------------------------------------------
131
+ server.tool('hush', 'Stop all music playback immediately.', {}, async () => {
132
+ await ensureEngine();
133
+ engineHush();
134
+ updateAfterHush();
135
+ return {
136
+ content: [{ type: 'text', text: 'Music stopped.' }],
137
+ };
138
+ });
139
+ // -- now_playing ----------------------------------------------------------
140
+ server.tool('now_playing', 'Check what music is currently playing, including the Strudel code and DJ commentary.', {}, async () => {
141
+ const { isPlaying, currentCode, mcCommentary } = getState();
142
+ if (!isPlaying || !currentCode) {
143
+ return {
144
+ content: [{ type: 'text', text: 'Nothing is currently playing.' }],
145
+ };
146
+ }
147
+ return {
148
+ content: [
149
+ {
150
+ type: 'text',
151
+ text: JSON.stringify({ isPlaying, currentCode, mcCommentary }, null, 2),
152
+ },
153
+ ],
154
+ };
155
+ });
156
+ // ---------------------------------------------------------------------------
157
+ // Start.
158
+ // ---------------------------------------------------------------------------
159
+ export async function startServer() {
160
+ // Kick off engine init eagerly — don't await, tools will wait as needed.
161
+ startEngine();
162
+ const transport = new StdioServerTransport();
163
+ await server.connect(transport);
164
+ console.error('[dj-claude-mcp] Server running on stdio.');
165
+ }
166
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAElE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,QAAQ,EACR,cAAc,EACd,eAAe,EACf,eAAe,EACf,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,uEAAuE;AACvE,8EAA8E;AAE9E,IAAI,aAAa,GAAyB,IAAI,CAAC;AAE/C,SAAS,WAAW;IAClB,aAAa,GAAG,UAAU,EAAE;SACzB,IAAI,CAAC,GAAG,EAAE;QACT,cAAc,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACvD,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACP,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,IAAI,CAAC,aAAa;QAAE,WAAW,EAAE,CAAC;IAClC,MAAM,aAAa,CAAC;IACpB,IAAI,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,8DAA8D;AAC9D,8EAA8E;AAE9E,KAAK,UAAU,eAAe,CAAC,MAAc;IAC3C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wHAAwH,CAAC,CAAC;IAC5I,CAAC;IACD,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAAC;IAE7C,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAE9C,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC5E,QAAQ,IAAI,KAAK,CAAC;IACpB,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;IAE3E,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,UAAU,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IAErD,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,6BAA6B,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,sCAAsC;AACtC,8EAA8E;AAE9E,MAAM,YAAY,GAA2B;IAC3C,KAAK,EAAE,6FAA6F;IACpG,IAAI,EAAE,qGAAqG;IAC3G,IAAI,EAAE,wGAAwG;IAC9G,KAAK,EAAE,yHAAyH;IAChI,KAAK,EAAE,gGAAgG;IACvG,MAAM,EAAE,gHAAgH;IACxH,KAAK,EAAE,mHAAmH;IAC1H,IAAI,EAAE,2GAA2G;CAClH,CAAC;AAEF,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,4EAA4E;AAC5E,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,0LAA0L,EAC1L,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iFAAiF,CAAC,EAAE,EAClH,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;IACnB,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3D,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,UAAU;oBACd,CAAC,CAAC,GAAG,UAAU,uCAAuC,IAAI,UAAU;oBACpE,CAAC,CAAC,mCAAmC,IAAI,UAAU;aACtD;SACF;KACF,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,IAAI,CACT,cAAc,EACd,6HAA6H,EAC7H,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC/D,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACjB,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,EAAE,WAAW,EAAE,GAAG,QAAQ,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,qBAAqB,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;YAC/E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IACD,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC1B,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,mCAAmC,IAAI,UAAU,EAAE,CAAC;KAC9F,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,IAAI,CACT,UAAU,EACV,sGAAsG,EACtG;IACE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;SACjF,QAAQ,CAAC,sBAAsB,CAAC;CACpC,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACjB,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3D,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,UAAU;oBACd,CAAC,CAAC,eAAe,IAAI,KAAK,UAAU,yBAAyB,IAAI,UAAU;oBAC3E,CAAC,CAAC,eAAe,IAAI,0BAA0B,IAAI,UAAU;aAChE;SACF;KACF,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,IAAI,CACT,MAAM,EACN,sCAAsC,EACtC,EAAE,EACF,KAAK,IAAI,EAAE;IACT,MAAM,YAAY,EAAE,CAAC;IACrB,UAAU,EAAE,CAAC;IACb,eAAe,EAAE,CAAC;IAClB,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;KAC7D,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,IAAI,CACT,aAAa,EACb,sFAAsF,EACtF,EAAE,EACF,KAAK,IAAI,EAAE;IACT,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC5D,IAAI,CAAC,SAAS,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;aACxE;SACF;KACF,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,yEAAyE;IACzE,WAAW,EAAE,CAAC;IAEd,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Message } from '../lib/types.js';
2
+ interface MCPState {
3
+ engineReady: boolean;
4
+ isPlaying: boolean;
5
+ currentCode: string;
6
+ previousCode: string;
7
+ mcCommentary: string;
8
+ messages: Message[];
9
+ }
10
+ export declare function getState(): Readonly<MCPState>;
11
+ export declare function setEngineReady(): void;
12
+ export declare function updateAfterPlay(code: string, commentary: string): void;
13
+ export declare function updateAfterHush(): void;
14
+ export declare function addMessage(msg: Message): void;
15
+ export {};
16
+ //# sourceMappingURL=state.d.ts.map