c64bridge 0.7.4 → 0.8.1

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 (109) hide show
  1. package/.github/prompts/README.md +14 -4
  2. package/.github/prompts/assembly-routine-session.prompt.md +9 -9
  3. package/.github/prompts/basic-program-session.prompt.md +11 -9
  4. package/.github/prompts/drive-management-session.prompt.md +9 -9
  5. package/.github/prompts/hello-world-session.prompt.md +17 -0
  6. package/.github/prompts/petscii-art-session.prompt.md +9 -9
  7. package/.github/prompts/play-classic-game.prompt.md +9 -9
  8. package/.github/prompts/pr-converge.prompt.md +13 -0
  9. package/.github/prompts/sid-music-session.prompt.md +9 -9
  10. package/AGENTS.md +59 -60
  11. package/README.md +550 -191
  12. package/data/assembly/assembly-spec.md +290 -1
  13. package/data/context/bootstrap.md +26 -2
  14. package/data/context/fast-paths.md +44 -0
  15. package/data/graphics/petscii-style-guide.md +6 -2
  16. package/data/graphics/sprite-charset-best-practices.md +5 -5
  17. package/dist/.tsbuildinfo +1 -1
  18. package/dist/c64Client.js +990 -26
  19. package/dist/config.js +5 -1
  20. package/dist/device.js +680 -57
  21. package/dist/diagnostics.js +371 -0
  22. package/dist/mcp/metadata.js +39 -0
  23. package/dist/mcp-server.js +170 -20
  24. package/dist/platform.js +10 -2
  25. package/dist/prompts/registry.js +166 -309
  26. package/dist/rag/init.js +37 -1
  27. package/dist/rag/knowledgeIndex.js +18 -6
  28. package/dist/rag/rateLimiter.js +2 -2
  29. package/dist/streamCapture.js +135 -0
  30. package/dist/tools/audio.js +3 -1
  31. package/dist/tools/debug.js +470 -0
  32. package/dist/tools/developer.js +10 -4
  33. package/dist/tools/graphics.js +183 -17
  34. package/dist/tools/machineControl.js +11 -5
  35. package/dist/tools/memory.js +19 -10
  36. package/dist/tools/meta/audio.js +240 -1
  37. package/dist/tools/meta/config.js +20 -28
  38. package/dist/tools/meta/configInventory.js +104 -0
  39. package/dist/tools/meta/diagnostics.js +45 -2
  40. package/dist/tools/meta/graphics.js +14 -6
  41. package/dist/tools/meta/index.js +1 -0
  42. package/dist/tools/meta/memory.js +20 -6
  43. package/dist/tools/meta/program.js +473 -0
  44. package/dist/tools/pollValidator.js +4 -2
  45. package/dist/tools/programRunners.js +8 -4
  46. package/dist/tools/registry/config.js +17 -0
  47. package/dist/tools/registry/disk.js +7 -0
  48. package/dist/tools/registry/drive.js +3 -0
  49. package/dist/tools/registry/graphics.js +111 -27
  50. package/dist/tools/registry/index.js +6 -0
  51. package/dist/tools/registry/memory.js +1 -0
  52. package/dist/tools/registry/platform.js +121 -0
  53. package/dist/tools/registry/program.js +26 -8
  54. package/dist/tools/registry/sound.js +96 -0
  55. package/dist/tools/registry/system.js +15 -2
  56. package/dist/tools/storage.js +1 -0
  57. package/dist/tools/streaming.js +1 -0
  58. package/dist/tools/types.js +38 -4
  59. package/dist/tools/vice.js +160 -0
  60. package/dist/vicBitmap.js +317 -0
  61. package/dist/vice/mockServer.js +439 -0
  62. package/dist/vice/process.js +310 -0
  63. package/dist/vice/readiness.js +18 -2
  64. package/dist/vice/viceClient.js +382 -22
  65. package/doc/developer.md +71 -25
  66. package/doc/img/backend-switch/hello-c64u.png +0 -0
  67. package/doc/img/backend-switch/hello-vice-and-c64u.png +0 -0
  68. package/doc/img/backend-switch/hello-vice.png +0 -0
  69. package/doc/img/backend-switch/metadata.json +50 -0
  70. package/doc/plans/vice-backend-switch/plan.md +486 -0
  71. package/doc/plans/vice-backend-switch-2/plan.md +48 -0
  72. package/doc/plans/vice-support/notes.md +1 -0
  73. package/doc/plans/vice-support/plan.md +5 -0
  74. package/doc/plans/vice-support/rollout.md +23 -17
  75. package/doc/reviews/vice-support-review.md +185 -0
  76. package/doc/troubleshooting-mcp.md +35 -6
  77. package/doc/vice/support-matrix.md +54 -0
  78. package/mcp/prompts.json +292 -0
  79. package/mcp/protocol-examples.json +46 -0
  80. package/mcp/resources.json +940 -0
  81. package/mcp/schemas/c64_config.schema.json +350 -0
  82. package/mcp/schemas/c64_debug.schema.json +380 -0
  83. package/mcp/schemas/c64_disk.schema.json +278 -0
  84. package/mcp/schemas/c64_drive.schema.json +120 -0
  85. package/mcp/schemas/c64_extract.schema.json +302 -0
  86. package/mcp/schemas/c64_graphics.schema.json +333 -0
  87. package/mcp/schemas/c64_memory.schema.json +155 -0
  88. package/mcp/schemas/c64_printer.schema.json +218 -0
  89. package/mcp/schemas/c64_program.schema.json +391 -0
  90. package/mcp/schemas/c64_rag.schema.json +67 -0
  91. package/mcp/schemas/c64_select_backend.schema.json +32 -0
  92. package/mcp/schemas/c64_sound.schema.json +644 -0
  93. package/mcp/schemas/c64_stream.schema.json +61 -0
  94. package/mcp/schemas/c64_system.schema.json +227 -0
  95. package/mcp/schemas/c64_vice.schema.json +63 -0
  96. package/mcp/server.json +15 -0
  97. package/mcp/tools.json +4517 -0
  98. package/mcp.json +90 -15
  99. package/package.json +11 -4
  100. package/scripts/capture-backend-switch-screenshots.mjs +276 -0
  101. package/scripts/generate-mcp-interface.ts +289 -0
  102. package/scripts/invoke-bun.mjs +67 -21
  103. package/scripts/mockC64Server.mjs +162 -6
  104. package/scripts/prepare-release.mjs +59 -2
  105. package/scripts/run-coverage.mjs +284 -0
  106. package/scripts/run-tests.mjs +95 -39
  107. package/scripts/run-tests.ts +378 -73
  108. package/scripts/update-readme.ts +147 -13
  109. package/server.json +94 -0
@@ -0,0 +1,50 @@
1
+ {
2
+ "prompts": {
3
+ "c64u": "c64u: write a small BASIC program that clears the screen and prints HELLO C64U",
4
+ "vice": "vice: write a small BASIC program that clears the screen and prints HELLO VICE"
5
+ },
6
+ "ocr": {
7
+ "c64u": {
8
+ "expected": "HELLO C64U",
9
+ "recognized": "HELLO C64U",
10
+ "origin": {
11
+ "x": 32,
12
+ "y": 35
13
+ },
14
+ "score": 0,
15
+ "characterScores": [
16
+ 0,
17
+ 0,
18
+ 0,
19
+ 0,
20
+ 0,
21
+ 0,
22
+ 0,
23
+ 0,
24
+ 0,
25
+ 0
26
+ ]
27
+ },
28
+ "vice": {
29
+ "expected": "HELLO VICE",
30
+ "recognized": "HELLO VICE",
31
+ "origin": {
32
+ "x": 32,
33
+ "y": 35
34
+ },
35
+ "score": 0,
36
+ "characterScores": [
37
+ 0,
38
+ 0,
39
+ 0,
40
+ 0,
41
+ 0,
42
+ 0,
43
+ 0,
44
+ 0,
45
+ 0,
46
+ 0
47
+ ]
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,486 @@
1
+ # C64Bridge: Multi-Backend Runtime Switching — Implementation Brief
2
+
3
+ ## Context
4
+
5
+ You are working in the repository at `/home/chris/dev/c64/c64bridge`.
6
+
7
+ This is a TypeScript MCP (Model Context Protocol) server that bridges Commodore 64
8
+ hardware and emulator backends to AI agents. Two backends exist:
9
+
10
+ - **C64U** (`c64u`): REST API to physical C64 Ultimate hardware at `http://c64u`
11
+ - **VICE** (`vice`): Binary monitor protocol to the VICE C64 emulator
12
+
13
+ The server is started via `node scripts/start.mjs` (stdio MCP transport).
14
+
15
+ ---
16
+
17
+ ## Mandatory Process
18
+
19
+ Before writing any code you **must** create `plans.md` at the repo root with the
20
+ full multi-phase plan described below. Every task in `plans.md` must be a checkbox.
21
+ Tick each checkbox the moment that task is complete — never batch. Alongside
22
+ `plans.md`, maintain `worklog.md` at the repo root. Append a timestamped entry to
23
+ `worklog.md` whenever you start a phase, complete a phase, or make a decision that
24
+ deviates from this brief.
25
+
26
+ Format for `worklog.md` entries:
27
+
28
+ ```
29
+ ## YYYY-MM-DD HH:MM — <one-line summary>
30
+
31
+ <Free prose: what was done, any surprises, decisions made.>
32
+ ```
33
+
34
+ **Do not proceed to the next phase until all checkboxes in the current phase are
35
+ ticked.** If you discover that a later phase must be restructured because of
36
+ something you learned in an earlier phase, update `plans.md` and add a worklog
37
+ entry explaining why.
38
+
39
+ ---
40
+
41
+ ## Test Coverage Mandate
42
+
43
+ **Minimum 91% line + branch coverage across all modified and new source files.**
44
+
45
+ - Run the existing test suite with `./build test` (mock backend) after every phase.
46
+ - After the final phase run `./build coverage` and verify the threshold.
47
+ - If coverage drops below 91% on any modified file, write additional tests before
48
+ marking that phase complete.
49
+ - Tests live in `src/**/*.test.ts` (co-located) or `test/**/*.ts` — follow the
50
+ existing pattern for the file you are modifying.
51
+ - Do not delete or weaken existing tests.
52
+
53
+ ---
54
+
55
+ ## Bug Report: Why Vice Is Never Selected
56
+
57
+ When the server starts, `mcp-server.ts` calls `config.ts::loadConfig()`, which is a
58
+ **C64U-only** legacy loader. It reads `~/.c64bridge.json` or the project root
59
+ `.c64bridge.json` but only ever extracts `c64u.*` fields; the `vice` section is
60
+ silently ignored.
61
+
62
+ Separately, `device.ts::readConfigFile()` — used by `createFacade()` — scans
63
+ candidate paths in order and **returns on the first match**:
64
+
65
+ 1. `C64BRIDGE_CONFIG` env var path
66
+ 2. `.c64bridge.json` in the project root
67
+ 3. `~/.c64bridge.json` in the home directory
68
+
69
+ The project root `.c64bridge.json` currently contains:
70
+
71
+ ```json
72
+ { "c64u": { "host": "c64u", "port": 80 } }
73
+ ```
74
+
75
+ Because this file is found first and contains no `vice` section,
76
+ `createFacade()` always selects c64u — the user's `~/.c64bridge.json` vice config
77
+ is never reached.
78
+
79
+ A second issue: after `createFacade()` resolves, `platform.ts::currentPlatform`
80
+ remains `"c64u"` (its hardcoded default) even when vice was selected, because
81
+ `mcp-server.ts` never calls `setPlatform()` with the result of the facade selection.
82
+
83
+ ---
84
+
85
+ ## Existing Architecture — Key Files
86
+
87
+ Read every file listed here before writing code.
88
+
89
+ | File | Role |
90
+ |---|---|
91
+ | `src/mcp-server.ts` | MCP entry point. Creates `C64Client`, registers MCP handlers. |
92
+ | `src/config.ts` | Legacy C64U-only config loader used by `mcp-server.ts`. |
93
+ | `src/device.ts` | `C64uBackend`, `ViceBackend`, `C64Facade` interface, `createFacade()`, `readConfigFile()`. |
94
+ | `src/c64Client.ts` | Orchestration wrapper around a single `C64Facade`. All tool methods delegate here. |
95
+ | `src/platform.ts` | Global `currentPlatform` state, `setPlatform()`, tool gating helpers. |
96
+ | `src/tools/types.ts` | `ToolExecutionContext` — passed to every tool invocation. |
97
+ | `src/tools/registry/index.ts` | Registers all tool modules; `toolRegistry.invoke()`. |
98
+ | `src/tools/registry/system.ts` | Example of a multi-operation tool module. |
99
+ | `src/tools/vice.ts` | Vice-specific tool (`supportedPlatforms: ["vice"]`). Study this as a pattern. |
100
+ | `mcp.json` | MCP server descriptor with documented env vars. |
101
+ | `.vscode/mcp.json` | VS Code MCP config — stdio invocation. |
102
+ | `data/context/bootstrap.md` | LLM bootstrap prompt injected before every session. |
103
+ | `AGENTS.md` | LLM-facing setup and usage guide. |
104
+
105
+ Also read:
106
+
107
+ - `src/tools/types.ts` fully (especially `ToolExecutionContext` and `ToolModule`).
108
+ - One complete tool module (e.g. `src/tools/registry/system.ts`) to understand the
109
+ pattern before implementing the new tool.
110
+
111
+ ---
112
+
113
+ ## Phase Breakdown for `plans.md`
114
+
115
+ Use exactly the following phase and task names. Add sub-tasks as needed, but do not
116
+ remove or rename the top-level items.
117
+
118
+ ---
119
+
120
+ ### Phase 0 — Bootstrap
121
+
122
+ - [ ] Read every file listed in the architecture table above.
123
+ - [ ] Read `README.md` sections on configuration and VS Code setup.
124
+ - [ ] Read `AGENTS.md` and `data/context/bootstrap.md`.
125
+ - [ ] Run `./build test` and record baseline pass/fail count in `worklog.md`.
126
+ - [ ] Record current `./build coverage` output in `worklog.md`.
127
+ - [ ] Create `plans.md` with all phases and tasks.
128
+ - [ ] Create `worklog.md` with the Phase 0 start entry.
129
+
130
+ ---
131
+
132
+ ### Phase 1 — Merge Config Across Files
133
+
134
+ **Goal**: `readConfigFile()` in `src/device.ts` collects backend sections from
135
+ *all* candidate config files rather than stopping at the first match.
136
+
137
+ **Specification**:
138
+
139
+ - Iterate all candidate paths in order (env var → project root → home dir).
140
+ - Accumulate: take the `c64u` section from the **first** file that contains one;
141
+ take the `vice` section from the **first** file that contains one.
142
+ - Stop iterating early only when both sections have been found.
143
+ - If no file yields a section, that section remains `undefined`.
144
+ - Return `null` only when no candidate file exists at all.
145
+ - The function signature and return type (`C64BridgeConfigFile | null`) must not
146
+ change.
147
+ - Do not change any other behaviour of `createFacade()`.
148
+
149
+ **Tasks**:
150
+
151
+ - [ ] Implement the merge logic in `readConfigFile()`.
152
+ - [ ] Write/update unit tests: config from project root only, home only, both split
153
+ across files, env-var path overrides, missing files.
154
+ - [ ] Run `./build test` — all tests pass.
155
+ - [ ] Coverage ≥ 91% on `src/device.ts`.
156
+ - [ ] Append Phase 1 completion entry to `worklog.md`.
157
+
158
+ ---
159
+
160
+ ### Phase 2 — Env-Var Overrides for C64U Backend
161
+
162
+ **Goal**: The C64U backend respects env-var overrides analogous to the existing
163
+ `VICE_BINARY`, `VICE_HOST`, `VICE_PORT` vars, so a user can fully configure it
164
+ from `.vscode/mcp.json` without a config file.
165
+
166
+ **New env vars** (read inside `C64uBackend` constructor, same pattern as
167
+ `ViceBackend`):
168
+
169
+ | Env var | Overrides |
170
+ |---|---|
171
+ | `C64U_HOST` | `config.host` / `config.hostname` |
172
+ | `C64U_PORT` | `config.port` |
173
+ | `C64U_PASSWORD` | `config.networkPassword` |
174
+
175
+ **Precedence within `C64uBackend`** (highest → lowest):
176
+
177
+ 1. Env var (`C64U_HOST`, etc.)
178
+ 2. Constructor `config` argument (from config file)
179
+ 3. Built-in defaults (`c64u`, port `80`)
180
+
181
+ **Tasks**:
182
+
183
+ - [ ] Read env vars in `C64uBackend` constructor.
184
+ - [ ] Document the three new vars in `mcp.json` `env` section with `description`
185
+ and `default` fields matching the existing format.
186
+ - [ ] Write unit tests: env var alone, config alone, env var beats config,
187
+ neither present → defaults.
188
+ - [ ] Run `./build test` — all tests pass.
189
+ - [ ] Coverage ≥ 91% on the modified constructor path.
190
+ - [ ] Append Phase 2 completion entry to `worklog.md`.
191
+
192
+ ---
193
+
194
+ ### Phase 3 — Dual-Facade Support in `C64Client`
195
+
196
+ **Goal**: When both backends are configured, `C64Client` initialises both facades
197
+ at construction time and exposes a `switchBackend()` method for zero-reconnect
198
+ runtime switching.
199
+
200
+ **Specification**:
201
+
202
+ Add a new exported async function to `src/device.ts`:
203
+
204
+ ```typescript
205
+ export interface AllFacadesResult {
206
+ primary: FacadeSelection; // the initially selected backend
207
+ secondary: C64Facade | null; // the other backend, if configured
208
+ secondaryType: DeviceType | null;
209
+ }
210
+
211
+ export async function createAllFacades(
212
+ logger?: { info: (...a: any[]) => void },
213
+ options?: FacadeOptions,
214
+ ): Promise<AllFacadesResult>
215
+ ```
216
+
217
+ Logic:
218
+
219
+ 1. Call the existing `createFacade()` to determine the primary selection.
220
+ 2. Read the merged config (via the updated `readConfigFile()`).
221
+ 3. If the config contains the OTHER backend (the one not selected as primary),
222
+ construct it immediately (no probing required — just instantiate).
223
+ 4. Return both.
224
+
225
+ Modify `C64Client`:
226
+
227
+ - Change constructor so that when `forceC64uFacade` is `false` it calls
228
+ `createAllFacades()` instead of `createFacade()`.
229
+ - Store results in:
230
+ ```typescript
231
+ private readonly allFacades: Map<DeviceType, Promise<C64Facade>>;
232
+ private activeType: DeviceType;
233
+ private facadePromise: Promise<C64Facade>; // points to allFacades.get(activeType)
234
+ ```
235
+ - Add:
236
+ ```typescript
237
+ switchBackend(type: DeviceType): void
238
+ // Throws if the type is not in allFacades.
239
+ // Updates this.facadePromise = this.allFacades.get(type).
240
+ // Updates this.activeType.
241
+ // Does NOT call setPlatform() — that is the caller's responsibility.
242
+
243
+ getActiveBackendType(): Promise<DeviceType>
244
+ // Returns activeType (already resolved, but keep async for consistency).
245
+
246
+ getAvailableBackends(): DeviceType[]
247
+ // Returns Array.from(allFacades.keys()).
248
+ ```
249
+ - All existing delegate methods in `C64Client` continue to work unchanged because
250
+ they already use `this.facadePromise`.
251
+
252
+ **Tasks**:
253
+
254
+ - [ ] Add `createAllFacades()` to `src/device.ts`.
255
+ - [ ] Modify `C64Client` constructor and add the three new methods.
256
+ - [ ] Write unit tests for `C64Client`: single c64u, single vice, both configured
257
+ (verify both facades initialised), `switchBackend()` swaps the active facade,
258
+ `switchBackend()` to unconfigured type throws.
259
+ - [ ] Run `./build test` — all tests pass.
260
+ - [ ] Coverage ≥ 91% on modified `c64Client.ts` paths.
261
+ - [ ] Append Phase 3 completion entry to `worklog.md`.
262
+
263
+ ---
264
+
265
+ ### Phase 4 — Sync Platform State on Init and Switch
266
+
267
+ **Goal**: `platform.ts::currentPlatform` always reflects the actually active
268
+ backend, from server start through any subsequent switches.
269
+
270
+ **In `src/mcp-server.ts`**:
271
+
272
+ After constructing `C64Client` and before registering MCP handlers, add:
273
+
274
+ ```typescript
275
+ const initialBackendType = await client.getActiveBackendType();
276
+ setPlatform(initialBackendType);
277
+ writeDiagnosticEvent("platform_initialised", { platform: initialBackendType });
278
+ ```
279
+
280
+ **In `C64Client::switchBackend()`** — the platform sync is **not** done here;
281
+ it remains the tool's responsibility (see Phase 5). Document this contract in a
282
+ comment.
283
+
284
+ **Tasks**:
285
+
286
+ - [ ] Add the `setPlatform()` call in `mcp-server.ts` after client construction.
287
+ - [ ] Add the `writeDiagnosticEvent` call.
288
+ - [ ] Write/update integration or unit tests that assert the platform matches the
289
+ backend that was selected from config at startup (both the c64u-only and
290
+ vice-only cases).
291
+ - [ ] Run `./build test` — all tests pass.
292
+ - [ ] Append Phase 4 completion entry to `worklog.md`.
293
+
294
+ ---
295
+
296
+ ### Phase 5 — `c64_select_backend` Tool
297
+
298
+ **Goal**: A new MCP tool lets the LLM (or user) switch the active backend at
299
+ runtime without restarting the server.
300
+
301
+ **Tool name**: `c64_select_backend`
302
+
303
+ **Module placement**: Add a new file `src/tools/registry/platform.ts` following the
304
+ same `ToolModule` / `defineToolModule` / `operationPlatforms` pattern as
305
+ `src/tools/registry/system.ts`. Register it in `src/tools/registry/index.ts`.
306
+
307
+ **Tool specification**:
308
+
309
+ ```
310
+ Input schema
311
+ op: "select" (discriminator, required)
312
+ backend: "c64u" | "vice" (required)
313
+
314
+ Supported platforms: ["c64u", "vice"] — available on both
315
+
316
+ Behaviour
317
+ 1. Call ctx.client.getAvailableBackends().
318
+ 2. If backend is not in the list, return an error result explaining which
319
+ backends are configured (do not throw).
320
+ 3. Call ctx.client.switchBackend(backend).
321
+ 4. Call ctx.setPlatform(backend).
322
+ 5. Return a success result that includes:
323
+ - The newly active backend name
324
+ - The list of tool names now available on that backend
325
+ - The list of tool names now unavailable
326
+ - A one-line usage hint reminding the user they can switch back
327
+ ```
328
+
329
+ **Tasks**:
330
+
331
+ - [ ] Create `src/tools/registry/platform.ts` with the `c64_select_backend` tool.
332
+ - [ ] Register the module in `src/tools/registry/index.ts`.
333
+ - [ ] Write unit tests: select available backend succeeds and updates platform,
334
+ select unavailable backend returns error result (not throw), both backends
335
+ configured round-trip switch.
336
+ - [ ] Run `./build test` — all tests pass.
337
+ - [ ] Coverage ≥ 91% on `src/tools/registry/platform.ts`.
338
+ - [ ] Append Phase 5 completion entry to `worklog.md`.
339
+
340
+ ---
341
+
342
+ ### Phase 6 — LLM Routing Instructions
343
+
344
+ **Goal**: The LLM automatically calls `c64_select_backend` when the user expresses
345
+ a preference for a specific backend in natural language, without requiring any
346
+ server-side prompt parsing.
347
+
348
+ **Update `data/context/bootstrap.md`**:
349
+
350
+ Add a section (before the existing tool listing, or at the top of the "Platform"
351
+ section if one exists) with these exact routing rules:
352
+
353
+ ```markdown
354
+ ## Backend Selection
355
+
356
+ Two backends may be available: `c64u` (physical C64 Ultimate hardware) and `vice`
357
+ (VICE emulator). Use `c64_select_backend` to switch.
358
+
359
+ **Routing rules — call `c64_select_backend` first when the user:**
360
+ - Says "use vice", "on vice", "in the emulator", "via vice", or similar
361
+ - Says "use c64u", "on hardware", "on the Ultimate", "on the real machine", or similar
362
+ - Prefixes a request with a backend name, e.g. "vice: run this program"
363
+
364
+ Do not assume the current backend is correct without checking
365
+ `c64://platform/status` if the user has expressed a preference.
366
+ ```
367
+
368
+ **Update `AGENTS.md`**:
369
+
370
+ Add a short "Runtime Backend Switching" subsection under the configuration section
371
+ that explains:
372
+ - Both backends can be live simultaneously when configured
373
+ - `c64_select_backend` switches without restarting
374
+ - The platform status resource (`c64://platform/status`) always reflects the
375
+ currently active backend
376
+
377
+ **Tasks**:
378
+
379
+ - [ ] Add backend routing rules to `data/context/bootstrap.md`.
380
+ - [ ] Add "Runtime Backend Switching" subsection to `AGENTS.md`.
381
+ - [ ] Verify that the bootstrap content is included in RAG (check
382
+ `src/rag/init.ts` or equivalent to confirm `bootstrap.md` is indexed).
383
+ - [ ] Append Phase 6 completion entry to `worklog.md`.
384
+
385
+ ---
386
+
387
+ ### Phase 7 — Update Platform Status Resource
388
+
389
+ **Goal**: The `c64://platform/status` resource rendered in `mcp-server.ts` now
390
+ also reports which backends are available (not just which is active) and removes
391
+ the stale notice about needing a restart to switch.
392
+
393
+ **In `mcp-server.ts::renderPlatformStatusMarkdown()`**:
394
+
395
+ - Add a "## Available Backends" section listing each backend from
396
+ `client.getAvailableBackends()`, marking the active one.
397
+ - Remove or update the trailing line:
398
+ > Switching platforms currently requires restarting the MCP server with an
399
+ > updated configuration.
400
+
401
+ Replace with a line explaining `c64_select_backend`.
402
+
403
+ **Tasks**:
404
+
405
+ - [ ] Update `renderPlatformStatusMarkdown()`.
406
+ - [ ] Pass `client` into the function (or use a closure — follow the existing
407
+ style in `mcp-server.ts`).
408
+ - [ ] Write/update tests for the rendered markdown output.
409
+ - [ ] Run `./build test` — all tests pass.
410
+ - [ ] Append Phase 7 completion entry to `worklog.md`.
411
+
412
+ ---
413
+
414
+ ### Phase 8 — Final Validation
415
+
416
+ - [ ] Run `./build test` (full mock suite) — zero failures.
417
+ - [ ] Run `./build test:matrix` if available — zero failures.
418
+ - [ ] Run `./build coverage` — overall coverage ≥ 91%.
419
+ - [ ] Manually inspect `plans.md` — every checkbox ticked.
420
+ - [ ] Review `worklog.md` — every phase has a completion entry.
421
+ - [ ] Read `README.md` configuration section and update it to document:
422
+ - The new `C64U_HOST`, `C64U_PORT`, `C64U_PASSWORD` env vars.
423
+ - The config-merging behaviour (both config files are read; first-found per
424
+ section wins).
425
+ - The dual-backend runtime switching capability and `c64_select_backend`.
426
+ - [ ] Append Phase 8 completion entry to `worklog.md`.
427
+
428
+ ---
429
+
430
+ ## Constraints and Non-Goals
431
+
432
+ - **Do not** delete or rename any existing public API on `C64Client` or
433
+ `C64Facade`.
434
+ - **Do not** remove `config.ts` or change its `loadConfig()` signature — it is
435
+ used by `mcp-server.ts` for diagnostics and may be used by external callers.
436
+ - **Do not** change the `createFacade()` function signature or its behaviour when
437
+ called with `preferredC64uBaseUrl` set (that path is used by tests).
438
+ - **Do not** add `npm` dependencies beyond what is already in `package.json`.
439
+ - **Do not** modify generated files under `generated/`.
440
+ - Keep all new source in TypeScript with strict types — no `any` except where the
441
+ existing code already uses it for generated API types.
442
+ - The `switchBackend()` method on `C64Client` must be synchronous (just a pointer
443
+ swap); do not await or re-connect anything inside it.
444
+ - The `createAllFacades()` secondary facade is constructed eagerly but its
445
+ underlying process (VICE) must **not** be started until the first method call
446
+ on it — this matches the existing lazy-start behaviour in `ViceBackend`.
447
+
448
+ ---
449
+
450
+ ## Precedence Order (for reference and tests)
451
+
452
+ After all phases are complete, the effective configuration precedence for backend
453
+ selection is, from highest to lowest:
454
+
455
+ 1. `C64_MODE` env var (`"c64u"` or `"vice"`) → forces a single backend
456
+ 2. `C64U_HOST` / `C64U_PORT` / `C64U_PASSWORD` env vars → override c64u fields
457
+ 3. `VICE_BINARY` / `VICE_HOST` / `VICE_PORT` env vars → override vice fields
458
+ 4. `C64BRIDGE_CONFIG` env var → explicit config file path (first-found wins per
459
+ backend section across this and subsequent candidates)
460
+ 5. `.c64bridge.json` in project root → merged into accumulated config
461
+ 6. `~/.c64bridge.json` in home directory → merged into accumulated config
462
+ 7. Built-in defaults (`c64u:80`, `127.0.0.1:6502`, `x64sc`)
463
+
464
+ ---
465
+
466
+ ## Acceptance Criteria
467
+
468
+ The implementation is complete when:
469
+
470
+ 1. Starting the server with only `{ "vice": { "exe": "/usr/bin/x64sc" } }` in
471
+ `~/.c64bridge.json` and `{ "c64u": { "host": "c64u" } }` in the project root
472
+ results in **both** backends being available and `vice` being selected if
473
+ `C64_MODE=vice` is set (or c64u if not set).
474
+ 2. Starting with only `{ "vice": ... }` in `~/.c64bridge.json` and **no** project
475
+ root config results in the vice backend being selected automatically.
476
+ 3. `C64U_HOST=192.168.1.99` in the environment overrides the host from any config
477
+ file.
478
+ 4. After the server is running with both backends configured, calling the
479
+ `c64_select_backend` tool with `backend: "vice"` returns a success result and
480
+ subsequent tool calls use the vice facade.
481
+ 5. Calling `c64_select_backend` with a backend that is not configured returns an
482
+ error result (not a thrown exception) listing which backends are available.
483
+ 6. `c64://platform/status` correctly lists available backends and the active one.
484
+ 7. `./build coverage` reports ≥ 91% overall.
485
+ 8. `plans.md` has every checkbox ticked and `worklog.md` has a completion entry
486
+ for every phase.
@@ -0,0 +1,48 @@
1
+ # VICE Backend Switch 2
2
+
3
+ ## Goal
4
+
5
+ Make backend switching feel instantaneous in normal LLM-driven use, keep both backends available in one MCP session, and make visible VICE execution match what the MCP server reads programmatically.
6
+
7
+ ## Phase 1: Deterministic VICE Launch
8
+
9
+ - Detect and use a valid VICE resource directory automatically instead of assuming the emulator can locate ROMs by itself.
10
+ - Allow explicit VICE resource-directory overrides for local setups that install binaries and ROM assets in different prefixes.
11
+ - Keep visible desktop launches on the user display by default and avoid silent fallback to hidden sessions unless explicitly requested.
12
+ - Add launcher coverage for resource-directory forwarding and startup argument construction.
13
+
14
+ ## Phase 2: Always-Ready Mixed Backend Runtime
15
+
16
+ - Make the MCP runtime provision both backends inside one `C64Client` when the session can reasonably address both.
17
+ - Ensure a VICE-first session can still switch to C64U without restarting the MCP server or editing config files mid-session.
18
+ - Preserve platform gating so tools still report accurate compatibility after a switch.
19
+ - Add tests for backend availability and switch behavior in split-config and VICE-first scenarios.
20
+
21
+ ## Phase 3: Instant Switch Path
22
+
23
+ - Warm available backends eagerly after MCP startup so the first post-switch tool call does not pay process-start or probe penalties.
24
+ - Serialize VICE monitor access so concurrent tool calls cannot double-start VICE or race the single-client Binary Monitor transport.
25
+ - Keep `c64_select_backend` as a local state change only: no probing, no process launch, no extra round trips.
26
+ - Add tests for eager warmup behavior and race-free VICE access where feasible.
27
+
28
+ ## Phase 4: End-to-End Validation
29
+
30
+ - Validate the MCP flow exactly as an LLM would use it: switch backend, run BASIC, read the screen, switch back, repeat.
31
+ - Measure switch-plus-run behavior and confirm the steady-state switch path stays under two seconds on this machine.
32
+ - Confirm that VICE visibly shows the same BASIC screen state that MCP reports from screen RAM.
33
+ - Update user-facing docs and config metadata for the new VICE launch controls.
34
+
35
+ ## Phase 5: Environment Override Consistency
36
+
37
+ - Ensure every documented runtime environment variable in `mcp.json` is actually honored when the server is launched from a user-provided MCP client config such as `.vscode/mcp.json`.
38
+ - Make the override model consistent: environment variable override first, merged JSON config next when applicable, built-in defaults last.
39
+ - Keep a generated README table of all supported runtime environment variables, their defaults, and any JSON config key they override.
40
+ - Add concrete `.vscode/mcp.json` examples so it is obvious which settings are env-only flags and which map to JSON config fields.
41
+
42
+ ## Success Criteria
43
+
44
+ - `c64_select_backend` returns immediately for already provisioned backends.
45
+ - A VICE-configured session can switch to C64U and back without restarting the server.
46
+ - VICE starts visibly on the desktop with a usable C64 screen instead of an inert grey window caused by missing resources.
47
+ - `vice: run HELLO WORLD` and `c64u: run HELLO WORLD` both work through the grouped MCP tools.
48
+ - Repeated backend switches in one MCP session remain stable and do not spawn duplicate VICE processes.
@@ -32,6 +32,7 @@ Implications for MCP:
32
32
  1) BM RAM injection on a long‑lived process (default): fast, DMA‑like, precise control.
33
33
  2) BM `0xDD` Autostart (fallback): file‑based, minimal pointer logic.
34
34
  3) Per‑run `x64sc -autostart` spawn (last resort): slowest; avoids long‑lived state.
35
+ - For tests, provide a BM stub controlled via `VICE_TEST_TARGET=mock`; default behavior is to use real VICE when available.
35
36
  2) Manage a long‑lived VICE process with BM and implement Autostart (`0xDD`) or direct program injection + `Keyboard Feed` for `RUN` (faster iteration, enables pause/step/breakpoints).
36
37
 
37
38
  ## Design Constraints & Decisions
@@ -67,6 +67,11 @@ Objective: Provide first‑class VICE emulator support in the MCP server using t
67
67
  - New grouped tool `c64_debug` exposing BM features: set/list/delete/toggle breakpoints, conditionals, step/next/return, register get/set, banks list, display get.
68
68
  - Separate from generic memory/system tools to avoid confusing capabilities across platforms.
69
69
 
70
+ 7) Test Harness & Stub (`src/vice/mockServer.ts` + helpers)
71
+ - Provide a lightweight BM stub that responds to the limited command surface the smoke test and unit tests rely on (`info`, `reset`, `memGet`, `memSet`, `keyboardFeed`, screen snapshot).
72
+ - Controlled via an opt-in flag (e.g., `VICE_TEST_TARGET=mock`), mirroring the C64U test flags. Default behavior: use real VICE when available.
73
+ - Document detection order: if `VICE_TEST_TARGET=mock`, start the stub; otherwise attempt to start VICE (skip tests gracefully when neither is available).
74
+
70
75
  ## Acceptance Guidelines
71
76
 
72
77
  - Under `C64_MODE=vice`, the following must succeed without REST using the long‑lived process: