@mthines/reaper-mcp 0.19.0 → 0.70.2
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/README.md +19 -570
- package/bridge-ui/mcp_bridge.html +129 -0
- package/bridge-ui/mcp_bridge.webmanifest +16 -0
- package/bridge-ui/mcp_bridge_icon_192.png +0 -0
- package/bridge-ui/mcp_bridge_icon_512.png +0 -0
- package/bridge-ui/mcp_bridge_sw.js +35 -0
- package/claude/agents/plugin-researcher.md +188 -0
- package/claude/knowledge/reference/aesthetics-scoring.md +48 -0
- package/{knowledge → claude/knowledge}/reference/common-mistakes.md +1 -1
- package/claude/knowledge/reference/lorekit-setup.md +36 -0
- package/claude/knowledge/reference/memory-protocol.md +377 -0
- package/{knowledge → claude/knowledge}/reference/metering.md +5 -5
- package/claude/knowledge/reference/perceived-loudness.md +182 -0
- package/{knowledge → claude/knowledge}/workflows/editing.md +4 -0
- package/{knowledge → claude/knowledge}/workflows/gain-staging.md +21 -23
- package/{knowledge → claude/knowledge}/workflows/learn-plugin.md +15 -48
- package/{knowledge → claude/knowledge}/workflows/master-bus.md +13 -1
- package/claude/knowledge/workflows/section-levels.md +138 -0
- package/{knowledge → claude/knowledge}/workflows/session-prep.md +4 -0
- package/claude/knowledge/workflows/transient-lighting.md +52 -0
- package/claude/skills/critique/SKILL.md +131 -0
- package/claude/skills/editing/SKILL.md +281 -0
- package/claude/skills/learn-plugin/SKILL.md +210 -0
- package/claude/skills/mastering/SKILL.md +151 -0
- package/claude/skills/mix-memory/SKILL.md +140 -0
- package/claude/skills/mixer/SKILL.md +253 -0
- package/claude/skills/new-extension/SKILL.md +324 -0
- package/main.js +7843 -1940
- package/package.json +17 -15
- package/reaper/CLAUDE.md +22 -4
- package/reaper/mcp_actions_lib.lua +375 -0
- package/reaper/mcp_analyzer.jsfx +27 -12
- package/reaper/mcp_arrange_lib.lua +949 -0
- package/reaper/mcp_bridge.lua +2321 -758
- package/reaper/mcp_edit_views_lib.lua +1378 -0
- package/reaper/mcp_envelopes_lib.lua +748 -0
- package/reaper/mcp_extensions_lib.lua +55 -0
- package/reaper/mcp_fxstate_lib.lua +144 -0
- package/reaper/mcp_gain_trace.jsfx +126 -0
- package/reaper/mcp_items_lib.lua +877 -0
- package/reaper/mcp_lufs_meter.jsfx +16 -13
- package/reaper/mcp_midi_lib.lua +555 -0
- package/reaper/mcp_mixing_lib.lua +1021 -0
- package/reaper/mcp_plan_lib.lua +801 -0
- package/reaper/mcp_prefs_lib.lua +127 -0
- package/reaper/mcp_project_lib.lua +1088 -0
- package/reaper/mcp_recording_lib.lua +595 -0
- package/reaper/mcp_render_lib.lua +492 -0
- package/reaper/mcp_snapshot_lib.lua +547 -120
- package/reaper/mcp_snapshot_next.lua +10 -4
- package/reaper/mcp_snapshot_prev.lua +11 -4
- package/reaper/mcp_snapshot_quick_save.lua +7 -32
- package/reaper/mcp_snapshots_ui_lib.lua +258 -0
- package/reaper/mcp_tracks_lib.lua +409 -0
- package/sidecar/analysis.py +661 -0
- package/sidecar/chords.py +96 -0
- package/sidecar/mixcheck.py +295 -0
- package/sidecar/requirements-chords.txt +6 -0
- package/sidecar/requirements-separation.txt +3 -0
- package/sidecar/requirements.txt +17 -0
- package/sidecar/separation.py +142 -0
- package/sidecar/server.py +622 -0
- package/sidecar/tests/test_analysis.py +249 -0
- package/sidecar/tests/test_chords.py +117 -0
- package/sidecar/tests/test_mixcheck.py +163 -0
- package/sidecar/tests/test_separation.py +73 -0
- package/sidecar/tests/test_server.py +464 -0
- package/sidecar/tests/test_verify.py +243 -0
- package/sidecar/tests/test_voice_render.py +268 -0
- package/sidecar/tests/test_voice_sketch.py +140 -0
- package/sidecar/verify.py +224 -0
- package/sidecar/voice_render.py +295 -0
- package/sidecar/voice_sketch.py +280 -0
- package/claude-agents/critique.md +0 -147
- package/claude-agents/editor.md +0 -184
- package/claude-agents/levels.md +0 -107
- package/claude-agents/mastering.md +0 -129
- package/claude-agents/mixer.md +0 -248
- package/claude-agents/preflight.md +0 -149
- package/claude-agents/producer.md +0 -159
- package/claude-agents/setup.md +0 -150
- package/claude-agents/stems.md +0 -135
- package/claude-rules/architecture.md +0 -39
- package/claude-rules/development.md +0 -54
- package/claude-rules/lua-bridge.md +0 -50
- package/claude-rules/testing.md +0 -42
- package/claude-skills/learn-plugin.md +0 -123
- package/knowledge/CLAUDE.md +0 -71
- package/knowledge/plugins/_template.md +0 -82
- package/knowledge/plugins/fabfilter/pro-c-2.md +0 -117
- package/knowledge/plugins/fabfilter/pro-l-2.md +0 -95
- package/knowledge/plugins/fabfilter/pro-q-3.md +0 -112
- package/knowledge/plugins/neural-dsp/helix-native.md +0 -104
- package/knowledge/plugins/stock-reaper/js-1175-compressor.md +0 -94
- package/knowledge/plugins/stock-reaper/rea-comp.md +0 -100
- package/knowledge/plugins/stock-reaper/rea-delay.md +0 -95
- package/knowledge/plugins/stock-reaper/rea-eq.md +0 -103
- package/knowledge/plugins/stock-reaper/rea-gate.md +0 -99
- package/knowledge/plugins/stock-reaper/rea-limit.md +0 -75
- package/knowledge/plugins/stock-reaper/rea-verb.md +0 -76
- package/knowledge/reference/perceived-loudness.md +0 -122
- package/reaper/mcp_snapshot_manager.lua +0 -1001
- /package/{knowledge → claude/knowledge}/genres/_template.md +0 -0
- /package/{knowledge → claude/knowledge}/genres/electronic.md +0 -0
- /package/{knowledge → claude/knowledge}/genres/hip-hop.md +0 -0
- /package/{knowledge → claude/knowledge}/genres/metal.md +0 -0
- /package/{knowledge → claude/knowledge}/genres/orchestral.md +0 -0
- /package/{knowledge → claude/knowledge}/genres/pop.md +0 -0
- /package/{knowledge → claude/knowledge}/genres/rock.md +0 -0
- /package/{knowledge → claude/knowledge}/reference/compression.md +0 -0
- /package/{knowledge → claude/knowledge}/reference/frequencies.md +0 -0
- /package/{knowledge → claude/knowledge}/workflows/delivery.md +0 -0
- /package/{knowledge → claude/knowledge}/workflows/drum-bus.md +0 -0
- /package/{knowledge → claude/knowledge}/workflows/low-end.md +0 -0
- /package/{knowledge → claude/knowledge}/workflows/stem-prep.md +0 -0
- /package/{knowledge → claude/knowledge}/workflows/vocal-chain.md +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: new-extension
|
|
3
|
+
description: >
|
|
4
|
+
Builds or updates a reaper-mcp extension for one plugin or workflow, end to end:
|
|
5
|
+
checks it is feasible, picks the right kind (VST3 state editor, preset-file builder,
|
|
6
|
+
or bridge workflow), finds how the plugin stores its state, writes tools with
|
|
7
|
+
behaviour tests, proves them live on a scratch track (including that the sound
|
|
8
|
+
changes), documents them and teaches the mixing skills about it. Use when the user
|
|
9
|
+
wants Claude to control a plugin's internals that host parameters cannot reach
|
|
10
|
+
(wavetables, mod matrix, patch graphs, preset files), asks to "make an extension for
|
|
11
|
+
X", "add support for X", "build a plugin for reaper-mcp", or to change, extend or
|
|
12
|
+
fix an existing extension ("update the vital extension", "add macros to vital").
|
|
13
|
+
Triggers on "/new-extension".
|
|
14
|
+
argument-hint: '[plugin or workflow to build an extension for | update <extension>: <what to change>]'
|
|
15
|
+
license: MIT
|
|
16
|
+
metadata:
|
|
17
|
+
author: mthines
|
|
18
|
+
version: '2.0.0'
|
|
19
|
+
workflow_type: applied
|
|
20
|
+
tags:
|
|
21
|
+
- extensions
|
|
22
|
+
- plugins
|
|
23
|
+
- reaper
|
|
24
|
+
- vst3
|
|
25
|
+
- plugin-state
|
|
26
|
+
- preset-files
|
|
27
|
+
- synth
|
|
28
|
+
- tdd
|
|
29
|
+
- live-verification
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
# /new-extension
|
|
33
|
+
|
|
34
|
+
This skill builds or updates an **opt-in extension** (`docs/EXTENSIONS.md`) for one plugin
|
|
35
|
+
or workflow: tools that reach what `set_fx_parameter` cannot, such as a synth's
|
|
36
|
+
wavetables, mod matrix and LFO shapes, or a guitar rig's preset files. Read
|
|
37
|
+
`docs/EXTENSIONS.md` first; it holds the manifest, the API, the three kinds of
|
|
38
|
+
extension and the trust model. Two worked examples ship in the repo, copy their shape,
|
|
39
|
+
not their details:
|
|
40
|
+
|
|
41
|
+
- `extensions/vital/`: a **state editor** (VST3 saved state, `api.fxState`).
|
|
42
|
+
- `extensions/helix/`: a **file-format builder** (the plugin's state is encrypted, so it
|
|
43
|
+
edits the documented preset file and imports it with a checked driver).
|
|
44
|
+
|
|
45
|
+
It works the way `/learn-plugin` does: do the research and the probing yourself, ask
|
|
46
|
+
the user only what you cannot find out, and prove the result on a scratch track
|
|
47
|
+
before calling it done. `/learn-plugin` teaches the mixing skills *about* a plugin;
|
|
48
|
+
this skill teaches reaper-mcp to *edit* one. They meet at the end (Step 8).
|
|
49
|
+
|
|
50
|
+
## Trigger
|
|
51
|
+
|
|
52
|
+
The user types `/new-extension [plugin or workflow]`, `/new-extension update <extension>: <change>`,
|
|
53
|
+
or asks for control of a plugin's internals that the existing tools cannot give.
|
|
54
|
+
|
|
55
|
+
**Mode.** `ls extensions/` and `reaper-mcp extension list`. If the argument names an
|
|
56
|
+
existing extension, or the work is "add X to / fix Y in" one, you are in **update mode**
|
|
57
|
+
(see [Updating an extension](#updating-an-extension)); otherwise **new mode**: run Steps
|
|
58
|
+
1-9.
|
|
59
|
+
|
|
60
|
+
## Rules that hold the whole way
|
|
61
|
+
|
|
62
|
+
- **Scratch track only.** Never add the plugin to the user's tracks, never change their
|
|
63
|
+
tracks, FX, selection or transport, and never start playback of their material. The
|
|
64
|
+
user edits the session live, so track indices shift: look a track up by name before
|
|
65
|
+
each write batch. Name it `[MCP] extension-research <TAG>`, add it at the end of the
|
|
66
|
+
track list and delete it when you are done, **even when a step failed**.
|
|
67
|
+
- **Never write a user's file in place.** Anything that touches a preset or library file
|
|
68
|
+
writes a copy, or backs up first (`.bak`) and verifies afterwards. Do not run an import
|
|
69
|
+
or write against the user's real library slots; use a scratch slot.
|
|
70
|
+
- **Extensions never import core.** Only `@reaper-mcp/extension-api` (eslint enforces it).
|
|
71
|
+
- **Test behaviour, never implementation** (`CLAUDE.md`): drive the tools through a fake
|
|
72
|
+
`ExtensionApi` and real state fixtures; assert what the agent gets and what state
|
|
73
|
+
REAPER ends up with. Mock only REAPER.
|
|
74
|
+
- **Fixtures are small and impersonal.** No personal presets, no license keys, no file
|
|
75
|
+
paths from the user's machine.
|
|
76
|
+
- **Do not commit** unless the user asks. The tree may hold other sessions' edits: stage
|
|
77
|
+
explicit paths (or hunks) of your own files only.
|
|
78
|
+
- **Do not guess a format and write it.** A wrong write can make a plugin silently ignore
|
|
79
|
+
the change or load garbage. Stay on read-only probes until the parse is exact.
|
|
80
|
+
|
|
81
|
+
## Instructions
|
|
82
|
+
|
|
83
|
+
### Step 0: Load what earlier runs learned
|
|
84
|
+
|
|
85
|
+
Read `docs/EXTENSIONS.md`. Then read the LoreKit lessons for extensions (capped at 5:
|
|
86
|
+
`memory.list` in `project::reaper-mcp` with tag `loop::extension-lessons`, plus one
|
|
87
|
+
`memory.search` on the plugin's name) and the plugin's own record (`user::plugins::…`, or
|
|
88
|
+
`get_builtin_knowledge`). Treat them as considerations, not rules; verify against what
|
|
89
|
+
you see. If LoreKit is not connected, continue without it and say so in the report.
|
|
90
|
+
|
|
91
|
+
### Step 1: Scope it, pick the kind, check it is worth building
|
|
92
|
+
|
|
93
|
+
From the arguments (or one question: "Which plugin or workflow, and what should Claude be
|
|
94
|
+
able to change in it?"), settle:
|
|
95
|
+
|
|
96
|
+
1. **What for.** Read, edit, build, or all three. Smallest useful set wins: an extension
|
|
97
|
+
that reads and edits the thing people actually tweak beats one that models all of it.
|
|
98
|
+
2. **Does it already exist?** `reaper-mcp extension list`, `ls extensions/`, and
|
|
99
|
+
`list_tool_categories`. If one covers it, switch to update mode.
|
|
100
|
+
3. **Can the host parameters do it?** If `get_fx_parameters` already exposes everything
|
|
101
|
+
needed, stop: `/learn-plugin` is the right tool. An extension earns its place only
|
|
102
|
+
for state that lives outside the parameters.
|
|
103
|
+
4. **Pick the kind** (the table in `docs/EXTENSIONS.md`). `search_fx` for the plugin first.
|
|
104
|
+
|
|
105
|
+
| Kind | Choose it when | Core of the work |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| **State editor** | A **VST3** build is installed and its saved state holds a recoverable format | `api.fxState` + `state.ts` parse/build (Steps 3-6 as written) |
|
|
108
|
+
| **File-format builder** | The state is closed or encrypted (probe shows no readable document) but the plugin imports a documented preset file | Parse/build/validate the file in Node; a checked import (`extensions/helix/src/import.ts`) |
|
|
109
|
+
| **Bridge composer** | No plugin internals needed, only a new multi-step REAPER workflow | `api.bridge` calls, `handlers.lua` only if a ReaScript call is missing from core |
|
|
110
|
+
|
|
111
|
+
`api.fxState` supports **VST3** only (VST2, AU, CLAP and FX inside containers are
|
|
112
|
+
refused). If only other formats are installed, say so and offer the file-format or
|
|
113
|
+
bridge route, or `/learn-plugin`.
|
|
114
|
+
5. **Name it.** Lowercase, digits, dashes, 2-32 characters. It prefixes every tool
|
|
115
|
+
(`<name>_<tool>`).
|
|
116
|
+
|
|
117
|
+
### Step 2: Research how the plugin stores its state
|
|
118
|
+
|
|
119
|
+
`WebSearch` / `WebFetch`, in this order: the vendor's docs on the **preset file format**,
|
|
120
|
+
open-source parsers or format write-ups, then forum threads. You are looking for:
|
|
121
|
+
|
|
122
|
+
- the preset format (JSON, XML, a binary chunk), its version field, and which parts are
|
|
123
|
+
safe to edit
|
|
124
|
+
- how the VST3 state wraps it (a JUCE bank, a raw preset, a custom blob)
|
|
125
|
+
- limits that make an edit invalid (counts, ranges, name rules)
|
|
126
|
+
- the license, if you will port any parser logic (credit it, or write your own)
|
|
127
|
+
|
|
128
|
+
Keep the URLs. Do not present guesses as facts. If the format is closed and
|
|
129
|
+
undocumented, say so now: you will have to work it out from probes in Step 3, and
|
|
130
|
+
should tell the user it may not be feasible.
|
|
131
|
+
|
|
132
|
+
### Step 3: Scaffold, then probe the real state
|
|
133
|
+
|
|
134
|
+
1. `reaper-mcp extension new <name> --dir extensions`. It scaffolds plain JavaScript
|
|
135
|
+
(`index.mjs`, a manifest). In this repo make it a TypeScript project like
|
|
136
|
+
`extensions/vital/`: copy its `project.json` (tag `type:extension`, esbuild to
|
|
137
|
+
`dist/index.mjs`), `tsconfig.json`, `package.json` and `vitest.config.ts`, set `main`
|
|
138
|
+
to `dist/index.mjs`, delete the scaffold's `index.mjs`, and run `pnpm install` once so
|
|
139
|
+
the new package is linked (the `extensions/*` workspace glob covers it).
|
|
140
|
+
2. Add the plugin to the scratch track (`add_fx`, exact FX name from `search_fx`).
|
|
141
|
+
Check `get_fx_parameters`: a plugin showing only 3 parameters is not loaded (offline),
|
|
142
|
+
toggle it online first.
|
|
143
|
+
3. **State editor:** write a throwaway **probe tool** that uses `api.fxState.get` and
|
|
144
|
+
returns, as JSON: state length, the 44-byte header's two length fields, the first and
|
|
145
|
+
last 64 bytes (hex and ASCII), every printable-string run over 8 characters, and
|
|
146
|
+
whether a JSON or XML document starts inside it and where it ends.
|
|
147
|
+
**File-format builder:** if the probe shows an encrypted or opaque blob, record that
|
|
148
|
+
(it is the reason for this kind) and work from the preset files instead: collect two or
|
|
149
|
+
three real files from the user's library or the vendor's factory content, read them,
|
|
150
|
+
and write the layout down.
|
|
151
|
+
4. Build it (`pnpm nx build <name>`), link it (`reaper-mcp extension add <name>`, a
|
|
152
|
+
symlink to this clone's `extensions/<name>`), and run it with the harness. A running
|
|
153
|
+
Claude Code session only sees new tools after a restart, so use:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
node scripts/ext-call.mjs <name>_probe '{"trackIndex": N}'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
(Needs `pnpm nx build reaper-mcp-server` and a running bridge.)
|
|
160
|
+
5. Learn the encoding by **changing one thing and diffing**: set one parameter with
|
|
161
|
+
`set_fx_parameter`, run the probe again, compare the bytes. Then change one thing
|
|
162
|
+
the host cannot reach (load a preset, switch a wavetable) and diff again. Save two or
|
|
163
|
+
three real states as **test fixtures**.
|
|
164
|
+
6. Write down the layout: where the editable document starts and ends, which length
|
|
165
|
+
fields wrap it (and what they count), and anything after it that must be preserved.
|
|
166
|
+
|
|
167
|
+
Stay on probes until the parse is exact on every fixture. Delete the probe tool before
|
|
168
|
+
Step 7.
|
|
169
|
+
|
|
170
|
+
### Step 4: Design the tools
|
|
171
|
+
|
|
172
|
+
Propose the tool list to the user in one short message (name, one line each, what is
|
|
173
|
+
read and what is written) and fold in their answer. Defaults that have worked:
|
|
174
|
+
|
|
175
|
+
- `read_*`: a **summary first** (structure, names, counts), raw values only on request
|
|
176
|
+
(`settingsMatching`-style filter, pagination). Never return the whole state.
|
|
177
|
+
- `write_*`: validate everything first and write nothing on one bad edit; `dryRun: true`
|
|
178
|
+
returns what would change with before → after; one undo point; say how to undo
|
|
179
|
+
(`undo_project`).
|
|
180
|
+
- One **domain tool** for the thing people want to build (Vital: `set_wavetable`), not a
|
|
181
|
+
generic "set any byte".
|
|
182
|
+
- `z.coerce.number()` for numbers, descriptions that tell an agent *when* to use the tool
|
|
183
|
+
and what the units are. Locate the plugin automatically when exactly one is on the track.
|
|
184
|
+
- Keep the user's selection, transport and track list as they were.
|
|
185
|
+
|
|
186
|
+
### Step 5: Implement, with behaviour tests first
|
|
187
|
+
|
|
188
|
+
Split the extension like `extensions/vital/src/`:
|
|
189
|
+
|
|
190
|
+
- `state.ts` (or `hlx.ts`-style for a file format): `parse` and `build`, pure and
|
|
191
|
+
byte-exact. `build` must keep every byte it does not edit, fix every inner length field
|
|
192
|
+
the edit changes, and (state editor) leave the VST3 wrapper's two outer lengths to
|
|
193
|
+
`api.fxState.set`.
|
|
194
|
+
- `patch.ts` (or similar): the edits as pure functions over the parsed document, each
|
|
195
|
+
returning an error list instead of throwing.
|
|
196
|
+
- `index.ts`: tool registration and the read → mutate → write helper.
|
|
197
|
+
- `__fixtures__/fake-api.ts`: a fake `ExtensionApi` (copy Vital's).
|
|
198
|
+
|
|
199
|
+
Tests (`*.test.ts`), each named after a behaviour:
|
|
200
|
+
|
|
201
|
+
- **round trip**: `build(parse(x), {})` returns `x` byte for byte, on every real fixture
|
|
202
|
+
- an edit changes exactly what was asked and nothing else
|
|
203
|
+
- a bad edit is refused and `set` is never called
|
|
204
|
+
- a wrong FX on the track gives a clear message, not a write
|
|
205
|
+
- a state the plugin changed after load (extra trailing bytes) still edits correctly
|
|
206
|
+
- file builders: an invalid file is refused before anything is written; an overwrite keeps
|
|
207
|
+
a `.bak`
|
|
208
|
+
|
|
209
|
+
Run `pnpm nx run-many --target=test,lint,typecheck,build --projects=<name>` until green.
|
|
210
|
+
|
|
211
|
+
### Step 6: Prove it live
|
|
212
|
+
|
|
213
|
+
On the scratch track, with the harness (a fresh process, so no restart):
|
|
214
|
+
|
|
215
|
+
1. `read_*` and compare with what you know about the loaded patch.
|
|
216
|
+
2. `write_*` with `dryRun: true`, then for real. Read it back: the change must be there.
|
|
217
|
+
3. **Prove it sounds different.** Add a MIDI item with a note or chord on the scratch
|
|
218
|
+
track (`create_midi_item`, `insert_midi_notes`), then `analyze_track_offline` before
|
|
219
|
+
and after the edit. Spectral centroid, band balance or loudness must move the way the
|
|
220
|
+
edit should move them. A read-back that matches does not prove the plugin loaded it.
|
|
221
|
+
4. Edit **twice in a row** (the state the plugin saves after loading your first write can
|
|
222
|
+
differ from the one you wrote; the second write is where a stale length shows up).
|
|
223
|
+
5. `undo_project` once and read back: the edit is gone.
|
|
224
|
+
6. Call a tool with a deliberately bad input: the refusal must be readable and change nothing.
|
|
225
|
+
|
|
226
|
+
If anything fails, keep the failing state's bytes (`api.fxState.get`) as a new fixture and
|
|
227
|
+
fix it with a test first. If it cannot be made reliable, say so in the report and do not
|
|
228
|
+
ship a tool that only sometimes works.
|
|
229
|
+
|
|
230
|
+
### Step 7: Gates
|
|
231
|
+
|
|
232
|
+
`reaper-mcp doctor` must list the extension as ok (manifest valid, `main` built, `lua`
|
|
233
|
+
present). Then everything: `pnpm nx run-many --target=build,lint,typecheck,test`, run with
|
|
234
|
+
`REAPER_RESOURCE_PATH` pointed at a temp folder so live-REAPER suites don't touch the
|
|
235
|
+
user's bridge. Delete the scratch track.
|
|
236
|
+
|
|
237
|
+
### Step 8: Teach the rest of the system
|
|
238
|
+
|
|
239
|
+
1. **Docs.** All four, in the same change:
|
|
240
|
+
- `docs/EXTENSIONS.md`: a section for the extension (what it does, a tool table, what is
|
|
241
|
+
not covered, env vars) next to Vital and Helix, plus a row in the table at the top.
|
|
242
|
+
- **Root `README.md`**: a row in the "Extensions" table (`## 🧩 Extensions`) with the emoji,
|
|
243
|
+
name, and what Claude can do with it in plain words for a non-coder, and the plugin in
|
|
244
|
+
the FAQ answer if it names them. Plain words, no jargon.
|
|
245
|
+
- `docs/assets/extensions.svg`: if it is a headline extension, add or swap a card so the
|
|
246
|
+
picture stays true (keep the file valid: `xmllint --noout`).
|
|
247
|
+
- `CLAUDE.md` (the structure block) and `docs/REFERENCE.md` (Project Structure).
|
|
248
|
+
|
|
249
|
+
Rebuild with `--skip-nx-cache` after editing knowledge or `.claude` files (Nx ignores
|
|
250
|
+
asset-dir changes).
|
|
251
|
+
2. **Plugin record.** Write or update the plugin's `user::plugins::<vendor>/<plugin>`
|
|
252
|
+
record in LoreKit (`project::reaper-mcp`) following
|
|
253
|
+
`knowledge/reference/memory-protocol.md`: copy the built-in record's tags, add
|
|
254
|
+
`extension::<name>`, and put the workflow, the raw-name vs host-name difference, what
|
|
255
|
+
must be enabled before it sounds, and what you verified live in `## Learned notes`. A
|
|
256
|
+
write replaces the record: read, merge, write.
|
|
257
|
+
3. **Lessons.** Save each non-obvious gotcha you hit as a LoreKit lesson in
|
|
258
|
+
`project::reaper-mcp`, tag `loop::extension-lessons`, phrased as an observation with
|
|
259
|
+
what you saw. Do not save anything that is already in this file or in the code.
|
|
260
|
+
|
|
261
|
+
### Step 9: Report
|
|
262
|
+
|
|
263
|
+
One short message: what the extension does and its tools, what you verified live (with
|
|
264
|
+
the numbers from Step 6), what is **not** covered or not verified, and the install line
|
|
265
|
+
(`reaper-mcp extension add <name>`, then restart the MCP server). Offer to commit and
|
|
266
|
+
open a PR; do not do either unprompted.
|
|
267
|
+
|
|
268
|
+
## Updating an extension
|
|
269
|
+
|
|
270
|
+
For "add X to", "fix Y in" or "support Z in" an existing extension. Same rules, shorter loop:
|
|
271
|
+
|
|
272
|
+
1. **Read it first.** Its manifest, `README` section in `docs/EXTENSIONS.md`, tool list,
|
|
273
|
+
tests and fixtures; then `git log --oneline -- extensions/<name>` for how it grew. Read
|
|
274
|
+
the Step 0 lessons.
|
|
275
|
+
2. **Reproduce or probe.** A bug: capture the failing state (`api.fxState.get` on a scratch
|
|
276
|
+
track) as a fixture and write the failing test first. A new capability: probe the one
|
|
277
|
+
thing it needs (Step 3, steps 2-6) on the scratch track, diffing as before.
|
|
278
|
+
3. **Keep tools compatible.** Existing tool names and parameters keep working; add
|
|
279
|
+
optional parameters rather than renaming. A breaking change needs the user's yes and a
|
|
280
|
+
`version` major bump.
|
|
281
|
+
4. **Behaviour tests, then code,** then Steps 6 and 7 for the changed tools (live proof of
|
|
282
|
+
the new behaviour **and** one regression check on an old tool).
|
|
283
|
+
5. **Bump `version`** in the manifest (patch for fixes, minor for new tools or parameters;
|
|
284
|
+
`apiVersion` only changes when `libs/extension-api` does).
|
|
285
|
+
6. **Docs.** Update the extension's section in `docs/EXTENSIONS.md` (move the item out of
|
|
286
|
+
its "Not yet" list), the root README row if what Claude can do changed, and the plugin's
|
|
287
|
+
LoreKit record. Lessons only for new gotchas.
|
|
288
|
+
7. Report as in Step 9, adding what changed for existing users (usually: rebuild, restart).
|
|
289
|
+
|
|
290
|
+
## Definition of done
|
|
291
|
+
|
|
292
|
+
- [ ] Parse round-trips every fixture byte for byte (or the file builder validates every fixture)
|
|
293
|
+
- [ ] Tools validate before writing; `dryRun` on every write; one undo point
|
|
294
|
+
- [ ] Live proof on a scratch track: read, write, read back, audible change, two writes, undo
|
|
295
|
+
- [ ] `reaper-mcp doctor` shows the extension ok; build, lint, typecheck, test green
|
|
296
|
+
- [ ] `docs/EXTENSIONS.md`, root `README.md`, `CLAUDE.md`, `docs/REFERENCE.md` updated
|
|
297
|
+
- [ ] LoreKit plugin record and lessons written (or the report says why not)
|
|
298
|
+
- [ ] Scratch track deleted; the user's tracks, selection and transport untouched
|
|
299
|
+
- [ ] Nothing committed unless asked
|
|
300
|
+
|
|
301
|
+
## Known gotchas (seed lessons, from building `vital` and `helix`)
|
|
302
|
+
|
|
303
|
+
- REAPER ignores changed VST3 state unless the FX gets a **new `FXID`**, and it checks two
|
|
304
|
+
outer length fields (header bytes 32..35 = state length; state bytes 0..3 = state length
|
|
305
|
+
minus 16). `api.fxState.set` handles both; never write the chunk by hand.
|
|
306
|
+
- After loading an edited state, a plugin can save itself with a few **extra trailing
|
|
307
|
+
bytes** while REAPER keeps the lengths it was given. Parse tolerantly, and keep trailing
|
|
308
|
+
bytes on `build`.
|
|
309
|
+
- A plugin whose host exposes only 3 parameters is **offline** (not loaded). Toggle it
|
|
310
|
+
online (`set_fx_offline`) before probing or writing, and refuse to write while offline.
|
|
311
|
+
- Some plugins (Helix Native) keep an **encrypted** state blob: do not try to edit it. Use
|
|
312
|
+
the plugin's preset file and verify the result on disk, rolling back if more than the
|
|
313
|
+
intended slot changed.
|
|
314
|
+
- The bridge's JSON decode is flat (SWS has no `CF_Json_Parse`): extension params travel as
|
|
315
|
+
a JSON string (`paramsJson`, decoded for you before your Lua handler runs), and large
|
|
316
|
+
state travels as files in the bridge folder, never inside a command.
|
|
317
|
+
- `mcp_bridge.lua` is near Lua's 200-locals limit. New Lua goes in a lib file or on the
|
|
318
|
+
`ext` table, not as new top-level locals.
|
|
319
|
+
- A write reloads the plugin, so held notes cut off. Do not write mid-playback.
|
|
320
|
+
- `register(api)` must finish within 10 s: register tools, do slow work inside handlers.
|
|
321
|
+
- Extension tools are not in the Claude Code allow-list, so each new one asks for
|
|
322
|
+
permission the first time.
|
|
323
|
+
- A running Claude Code session only sees a new or changed extension after a restart. Use
|
|
324
|
+
`node scripts/ext-call.mjs` to test in the meantime.
|