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.
- package/bin.mjs +24 -0
- package/dist/App.d.ts +6 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +64 -0
- package/dist/App.js.map +1 -0
- package/dist/audio/engine.d.ts +15 -0
- package/dist/audio/engine.d.ts.map +1 -0
- package/dist/audio/engine.js +117 -0
- package/dist/audio/engine.js.map +1 -0
- package/dist/audio/polyfill.d.ts +2 -0
- package/dist/audio/polyfill.d.ts.map +1 -0
- package/dist/audio/polyfill.js +122 -0
- package/dist/audio/polyfill.js.map +1 -0
- package/dist/components/CodeDisplay.d.ts +7 -0
- package/dist/components/CodeDisplay.d.ts.map +1 -0
- package/dist/components/CodeDisplay.js +12 -0
- package/dist/components/CodeDisplay.js.map +1 -0
- package/dist/components/DancingClaude.d.ts +6 -0
- package/dist/components/DancingClaude.d.ts.map +1 -0
- package/dist/components/DancingClaude.js +26 -0
- package/dist/components/DancingClaude.js.map +1 -0
- package/dist/components/Header.d.ts +8 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +28 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/PromptInput.d.ts +9 -0
- package/dist/components/PromptInput.d.ts.map +1 -0
- package/dist/components/PromptInput.js +7 -0
- package/dist/components/PromptInput.js.map +1 -0
- package/dist/components/SpeechBubble.d.ts +6 -0
- package/dist/components/SpeechBubble.d.ts.map +1 -0
- package/dist/components/SpeechBubble.js +25 -0
- package/dist/components/SpeechBubble.js.map +1 -0
- package/dist/components/StatusBar.d.ts +6 -0
- package/dist/components/StatusBar.d.ts.map +1 -0
- package/dist/components/StatusBar.js +6 -0
- package/dist/components/StatusBar.js.map +1 -0
- package/dist/headless.d.ts +2 -0
- package/dist/headless.d.ts.map +1 -0
- package/dist/headless.js +37 -0
- package/dist/headless.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/ascii-frames.d.ts +3 -0
- package/dist/lib/ascii-frames.d.ts.map +1 -0
- package/dist/lib/ascii-frames.js +82 -0
- package/dist/lib/ascii-frames.js.map +1 -0
- package/dist/lib/claude.d.ts +3 -0
- package/dist/lib/claude.d.ts.map +1 -0
- package/dist/lib/claude.js +44 -0
- package/dist/lib/claude.js.map +1 -0
- package/dist/lib/config.d.ts +3 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +27 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/parseCode.d.ts +8 -0
- package/dist/lib/parseCode.d.ts.map +1 -0
- package/dist/lib/parseCode.js +94 -0
- package/dist/lib/parseCode.js.map +1 -0
- package/dist/lib/prompts.d.ts +2 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +221 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/reducer.d.ts +4 -0
- package/dist/lib/reducer.d.ts.map +1 -0
- package/dist/lib/reducer.js +79 -0
- package/dist/lib/reducer.js.map +1 -0
- package/dist/lib/session.d.ts +4 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +45 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/types.d.ts +49 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +166 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/state.d.ts +16 -0
- package/dist/mcp/state.d.ts.map +1 -0
- package/dist/mcp/state.js +32 -0
- package/dist/mcp/state.js.map +1 -0
- package/mcp.mjs +15 -0
- 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 @@
|
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":""}
|
|
@@ -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":"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"}
|
|
@@ -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
|