c64bridge 0.9.3 → 1.0.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 (65) hide show
  1. package/AGENTS.md +19 -3
  2. package/README.md +190 -148
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/c64Client.js +277 -41
  5. package/dist/config.js +30 -18
  6. package/dist/device.js +191 -125
  7. package/dist/knowledge.js +1 -1
  8. package/dist/loggingHttpClient.js +15 -3
  9. package/dist/mcp-server.js +65 -16
  10. package/dist/platform.js +13 -2
  11. package/dist/rag/indexer.js +11 -8
  12. package/dist/rag/init.js +11 -8
  13. package/dist/sidwaveCompiler.js +2 -2
  14. package/dist/tools/audio.js +27 -6
  15. package/dist/tools/developer.js +6 -6
  16. package/dist/tools/graphics.js +4 -4
  17. package/dist/tools/input.js +136 -24
  18. package/dist/tools/machineControl.js +85 -6
  19. package/dist/tools/memory.js +21 -6
  20. package/dist/tools/meta/artifacts.js +20 -8
  21. package/dist/tools/meta/background.js +11 -1
  22. package/dist/tools/meta/index.js +1 -1
  23. package/dist/tools/pollValidator.js +19 -51
  24. package/dist/tools/printer.js +1 -0
  25. package/dist/tools/programRunners.js +5 -5
  26. package/dist/tools/rag.js +1 -1
  27. package/dist/tools/registry/batch.js +1 -1
  28. package/dist/tools/registry/config.js +5 -5
  29. package/dist/tools/registry/disk.js +2 -2
  30. package/dist/tools/registry/drive.js +2 -2
  31. package/dist/tools/registry/graphics.js +2 -2
  32. package/dist/tools/registry/memory.js +2 -2
  33. package/dist/tools/registry/platform.js +9 -5
  34. package/dist/tools/registry/printer.js +1 -0
  35. package/dist/tools/registry/program.js +5 -5
  36. package/dist/tools/registry/rag.js +1 -1
  37. package/dist/tools/registry/sound.js +4 -4
  38. package/dist/tools/registry/system.js +13 -3
  39. package/dist/tools/storage.js +1 -1
  40. package/dist/vice/mockServer.js +33 -0
  41. package/dist/vice/process.js +11 -1
  42. package/dist/vice/viceClient.js +35 -6
  43. package/doc/c64u/c64-openapi.yaml +1130 -774
  44. package/doc/reviews/hardening/01-fable/prompt-handover.md +125 -0
  45. package/doc/reviews/hardening/01-fable/prompt.md +98 -0
  46. package/doc/reviews/hardening/01-fable/review-prompt.md +468 -0
  47. package/doc/reviews/hardening/01-fable/review.md +1234 -0
  48. package/doc/u2/u2-openapi.yaml +1192 -0
  49. package/generated/c64/index.js +142 -9
  50. package/generated/openapi/c64u/index.js +1117 -0
  51. package/generated/openapi/u2/index.js +929 -0
  52. package/mcp/schemas/c64_graphics.schema.json +3 -3
  53. package/mcp/schemas/c64_input.schema.json +249 -8
  54. package/mcp/schemas/c64_memory.schema.json +3 -3
  55. package/mcp/schemas/c64_program.schema.json +3 -3
  56. package/mcp/schemas/c64_select_backend.schema.json +2 -0
  57. package/mcp/schemas/c64_system.schema.json +41 -1
  58. package/mcp/server.json +1 -1
  59. package/mcp/tools.json +386 -44
  60. package/mcp.json +14 -2
  61. package/package.json +4 -2
  62. package/scripts/mockC64Server.mjs +171 -2
  63. package/scripts/prepare-release.mjs +13 -2
  64. package/scripts/update-readme.ts +18 -30
  65. package/server.json +3 -3
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import "./bootstrap/stdio-logger.js";
3
+ import { createServer } from "node:http";
3
4
  import { dirname, join } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7
9
  import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
8
10
  import { loadConfig } from "./config.js";
9
11
  import { C64Client } from "./c64Client.js";
@@ -32,7 +34,7 @@ function createPromptRegistryGetter() {
32
34
  return registry;
33
35
  };
34
36
  }
35
- function parseCliOptions(argv) {
37
+ export function parseCliOptions(argv) {
36
38
  const httpIndex = argv.indexOf("--http");
37
39
  if (httpIndex === -1) {
38
40
  return { mode: "stdio" };
@@ -165,7 +167,7 @@ async function main() {
165
167
  {
166
168
  uri: PLATFORM_RESOURCE_URI,
167
169
  mimeType: "text/markdown",
168
- text: renderPlatformStatusMarkdown(client),
170
+ text: await renderPlatformStatusMarkdown(client),
169
171
  },
170
172
  ],
171
173
  };
@@ -420,12 +422,34 @@ async function main() {
420
422
  throw error;
421
423
  }
422
424
  });
423
- // Connect via stdio
424
- const transport = new StdioServerTransport();
425
- await server.connect(transport);
426
- writeDiagnosticEvent("mcp_transport_connected", { mode: "stdio" });
425
+ const cli = parseCliOptions(process.argv.slice(2));
426
+ if (cli.mode === "http") {
427
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
428
+ await server.connect(transport);
429
+ const port = cli.port ?? 3000;
430
+ const httpServer = createServer((request, response) => {
431
+ if (request.url?.split("?")[0] !== "/mcp") {
432
+ response.statusCode = 404;
433
+ response.end("Not found");
434
+ return;
435
+ }
436
+ void transport.handleRequest(request, response).catch((error) => {
437
+ if (!response.headersSent)
438
+ response.statusCode = 500;
439
+ response.end(error instanceof Error ? error.message : "MCP transport error");
440
+ });
441
+ });
442
+ await new Promise((resolve, reject) => httpServer.listen(port, "127.0.0.1", resolve).once("error", reject));
443
+ writeDiagnosticEvent("mcp_transport_connected", { mode: "http", port });
444
+ console.error(`c64bridge MCP server running on HTTP at http://127.0.0.1:${port}/mcp`);
445
+ }
446
+ else {
447
+ const transport = new StdioServerTransport();
448
+ await server.connect(transport);
449
+ writeDiagnosticEvent("mcp_transport_connected", { mode: "stdio" });
450
+ console.error("c64bridge MCP server running on stdio");
451
+ }
427
452
  await logConnectivity(client, baseUrl);
428
- console.error("c64bridge MCP server running on stdio");
429
453
  writeDiagnosticEvent("server_ready", {
430
454
  diagnosticsFile: diagnostics.filePath,
431
455
  });
@@ -447,10 +471,18 @@ function createPlatformResourceDescriptor() {
447
471
  },
448
472
  };
449
473
  }
450
- function renderPlatformStatusMarkdown(client) {
474
+ export async function renderPlatformStatusMarkdown(client) {
451
475
  const status = getPlatformStatus();
452
476
  const availableBackends = client.getAvailableBackends();
453
477
  const capabilities = describePlatformCapabilities(toolRegistry.list());
478
+ // Resource reads must remain responsive while an Ultimate boots or is
479
+ // unreachable. The probe itself remains retryable (unknown is not cached).
480
+ const nativeEndpoints = await Promise.race([
481
+ client.getNativeEndpointCapabilities(),
482
+ new Promise((resolve) => {
483
+ setTimeout(() => resolve({ machineInput: "unknown", machineMenuScreen: "unknown" }), 750).unref();
484
+ }),
485
+ ]);
454
486
  const lines = [
455
487
  "# MCP Platform Status",
456
488
  "",
@@ -466,6 +498,13 @@ function renderPlatformStatusMarkdown(client) {
466
498
  status.limitedFeatures.length > 0
467
499
  ? ["## Limited or Unavailable Features", "", ...status.limitedFeatures.map((feature) => `- ${feature}`)].join("\n")
468
500
  : "",
501
+ "## Native REST Endpoint Availability",
502
+ "",
503
+ `- \`machine:input\`: **${nativeEndpoints.machineInput}**`,
504
+ `- \`machine:menu_screen\`: **${nativeEndpoints.machineMenuScreen}** (it can only be confirmed while an Ultimate menu is visible)`,
505
+ "",
506
+ "Use physical-input, menu-screen, and C64U/U64 Tool Menu power-cycle operations only when the required endpoint is available. Prefer `key` or `write_text` for ordinary C64 input when `machine:input` is unavailable.",
507
+ "",
469
508
  "## Tool Compatibility",
470
509
  "",
471
510
  ].filter(Boolean);
@@ -487,7 +526,7 @@ function renderPlatformStatusMarkdown(client) {
487
526
  }
488
527
  async function logConnectivity(client, baseUrl) {
489
528
  const backendType = await client.getBackendType();
490
- if (backendType !== "c64u") {
529
+ if (backendType === "vice") {
491
530
  writeDiagnosticEvent("connectivity_probe_skipped", { baseUrl, reason: "vice_backend" });
492
531
  console.error("Skipping direct REST connectivity probe because active backend is VICE");
493
532
  try {
@@ -582,11 +621,21 @@ function toPromptMessage(segment) {
582
621
  },
583
622
  };
584
623
  }
585
- main().catch((error) => {
586
- writeDiagnosticEvent("server_fatal", {
587
- diagnosticsFile: getDiagnosticsSessionInfo()?.filePath,
588
- error,
624
+ // The documented entrypoint (src/index.ts / dist/index.js, per package.json
625
+ // "main"/"bin") always imports this module as a side effect rather than
626
+ // invoking it as the process's own script, so process.argv[1] never points
627
+ // here — an entrypoint-identity check would wrongly skip startup in every
628
+ // real deployment. Test code that only needs a pure export (e.g.
629
+ // parseCliOptions, renderPlatformStatusMarkdown) sets this opt-out instead,
630
+ // mirroring the existing C64BRIDGE_START_SKIP_AUTO_LAUNCH convention in
631
+ // scripts/start.mjs.
632
+ if (process.env.C64BRIDGE_SKIP_AUTO_START !== "1") {
633
+ main().catch((error) => {
634
+ writeDiagnosticEvent("server_fatal", {
635
+ diagnosticsFile: getDiagnosticsSessionInfo()?.filePath,
636
+ error,
637
+ });
638
+ console.error("Fatal error in MCP server:", error);
639
+ process.exit(1);
589
640
  });
590
- console.error("Fatal error in MCP server:", error);
591
- process.exit(1);
592
- });
641
+ }
package/dist/platform.js CHANGED
@@ -10,6 +10,10 @@ const PLATFORM_FEATURES = {
10
10
  ],
11
11
  limited: [],
12
12
  },
13
+ u2: {
14
+ features: ["ultimate-rest-api", "cartridge-execution", "sid-control", "drive-management", "printer-integration"],
15
+ limited: ["no-machine-input", "no-debugreg", "no-poweroff", "no-streaming"],
16
+ },
13
17
  vice: {
14
18
  features: [
15
19
  "software-emulation",
@@ -23,7 +27,7 @@ const PLATFORM_FEATURES = {
23
27
  limited: ["no-rest-api", "no-firmware-filesystem", "no-flash-config", "limited-sid"],
24
28
  },
25
29
  };
26
- let currentPlatform = process.env.C64_MODE === "vice" ? "vice" : "c64u";
30
+ let currentPlatform = process.env.C64_MODE === "vice" ? "vice" : process.env.C64_MODE === "u2" ? "u2" : "c64u";
27
31
  export function getPlatformStatus() {
28
32
  const spec = PLATFORM_FEATURES[currentPlatform];
29
33
  return {
@@ -48,11 +52,12 @@ export function isPlatformSupported(platform, supported) {
48
52
  export function describePlatformCapabilities(tools) {
49
53
  const map = {
50
54
  c64u: { available: [], unsupported: [] },
55
+ u2: { available: [], unsupported: [] },
51
56
  vice: { available: [], unsupported: [] },
52
57
  };
53
58
  for (const tool of tools) {
54
59
  const supported = tool.metadata.platforms ?? ["c64u"];
55
- for (const id of ["c64u", "vice"]) {
60
+ for (const id of ["c64u", "u2", "vice"]) {
56
61
  if (supported.includes(id)) {
57
62
  map[id].available.push(tool.name);
58
63
  }
@@ -69,6 +74,12 @@ export function describePlatformCapabilities(tools) {
69
74
  tools: map.c64u.available.sort(),
70
75
  unsupported_tools: map.c64u.unsupported.sort(),
71
76
  },
77
+ u2: {
78
+ features: PLATFORM_FEATURES.u2.features,
79
+ limited_features: PLATFORM_FEATURES.u2.limited,
80
+ tools: map.u2.available.sort(),
81
+ unsupported_tools: map.u2.unsupported.sort(),
82
+ },
72
83
  vice: {
73
84
  features: PLATFORM_FEATURES.vice.features,
74
85
  limited_features: PLATFORM_FEATURES.vice.limited,
@@ -4,16 +4,19 @@ GPL-2.0-only
4
4
  */
5
5
  import fs from "node:fs/promises";
6
6
  import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
7
8
  import fsSync from "node:fs";
8
- const BASIC_DIR = path.resolve("data/basic/examples");
9
- const ASM_DIR = path.resolve("data/assembly/examples");
10
- const EXTERNAL_DIR = path.resolve("external");
11
- const DOC_ROOT = path.resolve("doc");
9
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
10
+ const ASSET_ROOT = process.env.C64BRIDGE_ASSET_ROOT ? path.resolve(process.env.C64BRIDGE_ASSET_ROOT) : PACKAGE_ROOT;
11
+ const BASIC_DIR = path.join(ASSET_ROOT, "data/basic/examples");
12
+ const ASM_DIR = path.join(ASSET_ROOT, "data/assembly/examples");
13
+ const EXTERNAL_DIR = path.join(ASSET_ROOT, "external");
14
+ const DOC_ROOT = path.join(ASSET_ROOT, "doc");
12
15
  const DOC_EXCLUDE_DIRS = new Set(["plans"]);
13
- const CONTEXT_DIR = path.resolve("data/context");
16
+ const CONTEXT_DIR = path.join(ASSET_ROOT, "data/context");
14
17
  const BOOTSTRAP_PATH = path.join(CONTEXT_DIR, "bootstrap.md");
15
- const AGENTS_PATH = path.resolve("AGENTS.md");
16
- const PROMPTS_DIR = path.resolve(".github/prompts");
18
+ const AGENTS_PATH = path.join(ASSET_ROOT, "AGENTS.md");
19
+ const PROMPTS_DIR = path.join(ASSET_ROOT, ".github/prompts");
17
20
  const CHAT_PATH = path.join(CONTEXT_DIR, "chat.md");
18
21
  // RAG_DOC_FILES env var can add extra specific files, comma-separated
19
22
  const ENV_DOC_FILES = (process.env.RAG_DOC_FILES ?? "")
@@ -34,7 +37,7 @@ function shouldExcludeDocFile(file) {
34
37
  return firstSegment !== undefined && DOC_EXCLUDE_DIRS.has(firstSegment);
35
38
  }
36
39
  function resolveEmbeddingsDir(override) {
37
- return path.resolve(override ?? process.env.RAG_EMBEDDINGS_DIR ?? "data");
40
+ return path.resolve(override ?? process.env.RAG_EMBEDDINGS_DIR ?? path.join(ASSET_ROOT, "data"));
38
41
  }
39
42
  function embeddingIndexPaths(dir) {
40
43
  return {
package/dist/rag/init.js CHANGED
@@ -4,22 +4,25 @@ GPL-2.0-only
4
4
  */
5
5
  import fs from "node:fs/promises";
6
6
  import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
7
8
  import { setTimeout as delay } from "node:timers/promises";
8
9
  import { LocalMiniHashEmbedding } from "./embeddings.js";
9
10
  import { buildAllIndexes, loadIndexes } from "./indexer.js";
10
11
  import { LocalRagRetriever } from "./retriever.js";
11
12
  import { LoggingRagRetriever } from "./loggingRetriever.js";
12
- const EXTERNAL_DIR = path.resolve("external");
13
- const BASIC_DATA_DIR = path.resolve("data/basic/examples");
14
- const ASM_DATA_DIR = path.resolve("data/assembly/examples");
15
- const DOC_DIR = path.resolve("doc");
16
- const CONTEXT_DIR = path.resolve("data/context");
13
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
14
+ const assetRoot = process.env.C64BRIDGE_ASSET_ROOT ? path.resolve(process.env.C64BRIDGE_ASSET_ROOT) : PACKAGE_ROOT;
15
+ const EXTERNAL_DIR = path.join(assetRoot, "external");
16
+ const BASIC_DATA_DIR = path.join(assetRoot, "data/basic/examples");
17
+ const ASM_DATA_DIR = path.join(assetRoot, "data/assembly/examples");
18
+ const DOC_DIR = path.join(assetRoot, "doc");
19
+ const CONTEXT_DIR = path.join(assetRoot, "data/context");
17
20
  const BOOTSTRAP_PATH = path.join(CONTEXT_DIR, "bootstrap.md");
18
- const AGENTS_PATH = path.resolve("AGENTS.md");
19
- const PROMPTS_DIR = path.resolve(".github/prompts");
21
+ const AGENTS_PATH = path.join(assetRoot, "AGENTS.md");
22
+ const PROMPTS_DIR = path.join(assetRoot, ".github/prompts");
20
23
  const CHAT_PATH = path.join(CONTEXT_DIR, "chat.md");
21
24
  function resolveEmbeddingsDir() {
22
- return path.resolve(process.env.RAG_EMBEDDINGS_DIR ?? "data");
25
+ return path.resolve(process.env.RAG_EMBEDDINGS_DIR ?? path.join(assetRoot, "data"));
23
26
  }
24
27
  function embeddingIndexPaths() {
25
28
  const dir = resolveEmbeddingsDir();
@@ -169,9 +169,9 @@ export function compileSidwaveToSid(doc, prg, options) {
169
169
  const body = Buffer.isBuffer(prg) ? prg : Buffer.from(prg);
170
170
  return { sid: Buffer.concat([header, body]) };
171
171
  }
172
- function hzToSidFrequency(hz, system) {
172
+ export function hzToSidFrequency(hz, system) {
173
173
  const phi2 = system === "PAL" ? 985248 : 1022727;
174
- const value = Math.round((hz * 65536) / phi2);
174
+ const value = Math.round((hz * 16777216) / phi2);
175
175
  return Math.max(0, Math.min(0xffff, value));
176
176
  }
177
177
  function waveformToCtrl(w) {
@@ -9,6 +9,12 @@ const NOTE_PATTERN = /^([A-Ga-g])([#b]?)(-?\d+)$/;
9
9
  const DEFAULT_SILENCE_VERIFY_DURATION_SECONDS = 1.5;
10
10
  const DEFAULT_SILENCE_VERIFY_RMS_THRESHOLD = 0.02;
11
11
  const DEFAULT_SILENCE_VERIFY_WAIT_MS = 150;
12
+ const generatedPlaybacks = new Set();
13
+ export function cancelGeneratedSidPlayback() {
14
+ for (const controller of generatedPlaybacks)
15
+ controller.abort();
16
+ generatedPlaybacks.clear();
17
+ }
12
18
  function toRecord(details) {
13
19
  if (details && typeof details === "object") {
14
20
  return details;
@@ -403,7 +409,7 @@ export const audioModule = defineToolModule({
403
409
  ],
404
410
  prompts: ["sid-music"],
405
411
  defaultTags: ["sid", "audio"],
406
- supportedPlatforms: ["c64u", "vice"],
412
+ supportedPlatforms: ["c64u", "u2", "vice"],
407
413
  workflowHints: [
408
414
  "Reach for SID helpers when the user talks about sound design, playback quality, or stuck notes.",
409
415
  "After changing playback state, suggest verify-by-ear steps such as analyze_audio so the user gets concrete feedback.",
@@ -473,6 +479,7 @@ export const audioModule = defineToolModule({
473
479
  try {
474
480
  const parsed = sidResetArgsSchema.parse(args ?? {});
475
481
  ctx.logger.info("Resetting SID", { mode: parsed.hard ? "hard" : "soft" });
482
+ cancelGeneratedSidPlayback(); // Reset is the documented way to stop a generated arpeggio.
476
483
  const result = await ctx.client.sidReset(parsed.hard);
477
484
  if (!result.success) {
478
485
  throw new ToolExecutionError("C64 firmware reported failure while resetting SID", {
@@ -622,6 +629,7 @@ export const audioModule = defineToolModule({
622
629
  const parsed = sidSilenceArgsSchema.parse(args ?? {});
623
630
  const verify = Boolean(parsed.verify);
624
631
  ctx.logger.info("Silencing all SID voices", { verify });
632
+ cancelGeneratedSidPlayback(); // Otherwise its remaining notes would resume writing right after this silences the chip.
625
633
  const result = await ctx.client.sidSilenceAll();
626
634
  if (!result.success) {
627
635
  throw new ToolExecutionError("C64 firmware reported failure while silencing SID", {
@@ -812,10 +820,17 @@ export const audioModule = defineToolModule({
812
820
  tempoMs: parsed.tempoMs,
813
821
  waveform: parsed.waveform,
814
822
  });
823
+ cancelGeneratedSidPlayback(); // Replace any prior generated arpeggio rather than letting two run at once.
824
+ const controller = new AbortController();
825
+ generatedPlaybacks.add(controller);
826
+ const pinnedFacadePromise = ctx.client.pinFacade ? ctx.client.pinFacade() : Promise.resolve(undefined); // Pin once so a later select_backend can't retarget notes mid-arpeggio (HARD01-030).
815
827
  void (async () => {
816
828
  try {
817
- await ctx.client.sidSetVolume(8);
829
+ const pinnedFacade = await pinnedFacadePromise;
830
+ await ctx.client.sidSetVolume(8, pinnedFacade);
818
831
  for (let i = 0; i < parsed.steps; i += 1) {
832
+ if (controller.signal.aborted)
833
+ break;
819
834
  const iv = intervals[i % intervals.length];
820
835
  const note = transposeNote(parsed.root, iv);
821
836
  await ctx.client.sidNoteOn({
@@ -827,18 +842,24 @@ export const audioModule = defineToolModule({
827
842
  decay: 7,
828
843
  sustain: 15,
829
844
  release: 0,
830
- });
845
+ }, pinnedFacade);
831
846
  const durationMs = parsed.preset === "expression" ? expressiveDurations[i % expressiveDurations.length] : parsed.tempoMs;
832
- await sleep(durationMs);
847
+ await sleep(Math.max(1, Math.min(20, Math.floor(durationMs / 8)))); // Clear GATE between notes so every note retriggers ADSR.
848
+ await ctx.client.sidNoteOff(1, pinnedFacade);
849
+ if (!controller.signal.aborted)
850
+ await sleep(Math.max(0, durationMs - Math.max(1, Math.min(20, Math.floor(durationMs / 8)))));
833
851
  }
834
- await ctx.client.sidNoteOff(1);
852
+ await ctx.client.sidNoteOff(1, pinnedFacade);
835
853
  }
836
854
  catch (playbackError) {
837
855
  const message = playbackError instanceof Error ? playbackError.message : String(playbackError);
838
856
  ctx.logger.warn("music_generate playback failed", { error: message });
839
857
  }
858
+ finally {
859
+ generatedPlaybacks.delete(controller);
860
+ }
840
861
  })();
841
- return textResult(`Scheduled ${parsed.steps} note arpeggio starting on ${parsed.root}.`, {
862
+ return textResult(`Scheduled ${parsed.steps} note arpeggio starting on ${parsed.root}. Playback is running and will be replaced by the next generated sequence.`, {
842
863
  success: true,
843
864
  root: parsed.root,
844
865
  intervals,
@@ -146,7 +146,7 @@ export const developerModule = defineToolModule({
146
146
  workflowHints: [
147
147
  "Run first to enumerate categories before reading or writing specific settings; summarise the top-level sections for the user.",
148
148
  ],
149
- supportedPlatforms: ["c64u", "vice"],
149
+ supportedPlatforms: ["c64u", "u2", "vice"],
150
150
  async execute(args, ctx) {
151
151
  try {
152
152
  noArgsSchema.parse(args ?? {});
@@ -182,7 +182,7 @@ export const developerModule = defineToolModule({
182
182
  workflowHints: [
183
183
  "Use after config_list to fetch details; restate category/item so the user can cross-check values before changes.",
184
184
  ],
185
- supportedPlatforms: ["c64u", "vice"],
185
+ supportedPlatforms: ["c64u", "u2", "vice"],
186
186
  async execute(args, ctx) {
187
187
  try {
188
188
  const parsed = configGetArgsSchema.parse(args ?? {});
@@ -218,7 +218,7 @@ export const developerModule = defineToolModule({
218
218
  workflowHints: [
219
219
  "Confirm the user-supplied value before writing; mention whether a reboot is required for the change to take effect.",
220
220
  ],
221
- supportedPlatforms: ["c64u", "vice"],
221
+ supportedPlatforms: ["c64u", "u2", "vice"],
222
222
  async execute(args, ctx) {
223
223
  try {
224
224
  const parsed = configSetArgsSchema.parse(args ?? {});
@@ -262,7 +262,7 @@ export const developerModule = defineToolModule({
262
262
  workflowHints: [
263
263
  "Use when multiple categories must be updated together; remind the user to review validation errors closely.",
264
264
  ],
265
- supportedPlatforms: ["c64u", "vice"],
265
+ supportedPlatforms: ["c64u", "u2", "vice"],
266
266
  async execute(args, ctx) {
267
267
  try {
268
268
  const payload = configBatchSchema.parse(args ?? {});
@@ -405,7 +405,7 @@ export const developerModule = defineToolModule({
405
405
  name: "version",
406
406
  description: "Retrieve firmware or emulator version information.",
407
407
  summary: "Calls the firmware version endpoint (Ultimate) or returns emulator info (VICE).",
408
- supportedPlatforms: ["c64u", "vice"],
408
+ supportedPlatforms: ["c64u", "u2", "vice"],
409
409
  inputSchema: noArgsSchema.jsonSchema,
410
410
  relatedResources: ["c64://guide/bootstrap"],
411
411
  tags: ["diagnostics", "version"],
@@ -437,7 +437,7 @@ export const developerModule = defineToolModule({
437
437
  name: "info",
438
438
  description: "Retrieve hardware or emulator information and status.",
439
439
  summary: "Returns the diagnostics payload reported by the firmware (Ultimate) or emulator (VICE).",
440
- supportedPlatforms: ["c64u", "vice"],
440
+ supportedPlatforms: ["c64u", "u2", "vice"],
441
441
  inputSchema: noArgsSchema.jsonSchema,
442
442
  relatedResources: ["c64://guide/bootstrap"],
443
443
  tags: ["diagnostics", "info"],
@@ -577,7 +577,7 @@ export const graphicsModule = defineToolModule({
577
577
  "Use when the user supplies sprite bytes or asks to preview graphics quickly; describe resulting coordinates and colours.",
578
578
  "Remind the user that sprites live in banked memory so further tweaks may require write_memory calls.",
579
579
  ],
580
- supportedPlatforms: ["c64u", "vice"],
580
+ supportedPlatforms: ["c64u", "u2", "vice"],
581
581
  async execute(args, ctx) {
582
582
  return executeGenerateSprite(args, ctx);
583
583
  },
@@ -594,7 +594,7 @@ export const graphicsModule = defineToolModule({
594
594
  "Use when the user wants to display an external image on screen rather than generate PETSCII art.",
595
595
  "Call out the selected bitmap and screen RAM addresses so follow-up memory inspection or raster work stays grounded.",
596
596
  ],
597
- supportedPlatforms: ["c64u", "vice"],
597
+ supportedPlatforms: ["c64u", "u2", "vice"],
598
598
  async execute(args, ctx) {
599
599
  return executeGenerateBitmap(args, ctx);
600
600
  },
@@ -619,7 +619,7 @@ export const graphicsModule = defineToolModule({
619
619
  "Call after generating PETSCII text or when the user wants border/background colour changes applied.",
620
620
  "Echo the colour indices and mention CLEAR + PRINT so the user knows what ran.",
621
621
  ],
622
- supportedPlatforms: ["c64u", "vice"],
622
+ supportedPlatforms: ["c64u", "u2", "vice"],
623
623
  async execute(args, ctx) {
624
624
  return executeRenderPetscii(args, ctx);
625
625
  },
@@ -645,7 +645,7 @@ export const graphicsModule = defineToolModule({
645
645
  "Response includes petsciiCodes array and glyphs for character-level inspection.",
646
646
  "Provide follow-up suggestions like saving the PRG or capturing the screen after rendering.",
647
647
  ],
648
- supportedPlatforms: ["c64u", "vice"],
648
+ supportedPlatforms: ["c64u", "u2", "vice"],
649
649
  async execute(args, ctx) {
650
650
  return executeCreatePetscii(args, ctx);
651
651
  },