c64bridge 0.8.1 → 0.9.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 (92) hide show
  1. package/.github/prompts/steer.prompt.md +55 -0
  2. package/AGENTS.md +9 -7
  3. package/README.md +192 -154
  4. package/data/embeddings_asm.json +1 -1
  5. package/data/embeddings_basic.json +1 -1
  6. package/data/embeddings_hardware.json +1 -1
  7. package/data/embeddings_mixed.json +1 -1
  8. package/data/embeddings_other.json +1 -1
  9. package/data/graphics/petscii-style-guide.md +4 -4
  10. package/data/graphics/sprite-charset-best-practices.md +9 -9
  11. package/data/vice/vice-binary-monitor-spec.md +938 -0
  12. package/dist/.tsbuildinfo +1 -1
  13. package/dist/c64Client.js +76 -2
  14. package/dist/device.js +129 -13
  15. package/dist/mcp-server.js +1 -1
  16. package/dist/prompts/registry.js +27 -25
  17. package/dist/rag/indexer.js +18 -8
  18. package/dist/rag/knowledgeIndex.js +112 -79
  19. package/dist/rag/resourceUris.js +93 -0
  20. package/dist/sidwaveCompiler.js +1 -1
  21. package/dist/{assemblyConverter.js → tools/assember.js} +10 -1
  22. package/dist/tools/audio.js +11 -11
  23. package/dist/{basicConverter.js → tools/basicTokenizer.js} +1 -1
  24. package/dist/tools/debug.js +159 -6
  25. package/dist/tools/developer.js +13 -13
  26. package/dist/tools/disassembler.js +478 -0
  27. package/dist/tools/graphics.js +10 -10
  28. package/dist/tools/input.js +338 -0
  29. package/dist/tools/machineControl.js +5 -5
  30. package/dist/tools/memory.js +336 -12
  31. package/dist/tools/meta/index.js +1 -1
  32. package/dist/tools/printer.js +11 -12
  33. package/dist/tools/programRunners.js +61 -22
  34. package/dist/tools/rag.js +9 -9
  35. package/dist/tools/registry/batch.js +167 -0
  36. package/dist/tools/registry/config.js +1 -1
  37. package/dist/tools/registry/disk.js +1 -1
  38. package/dist/tools/registry/drive.js +1 -1
  39. package/dist/tools/registry/extract.js +1 -1
  40. package/dist/tools/registry/graphics.js +102 -2
  41. package/dist/tools/registry/index.js +4 -0
  42. package/dist/tools/registry/input.js +2 -0
  43. package/dist/tools/registry/memory.js +62 -2
  44. package/dist/tools/registry/platform.js +1 -1
  45. package/dist/tools/registry/printer.js +1 -1
  46. package/dist/tools/registry/program.js +1 -1
  47. package/dist/tools/registry/rag.js +1 -1
  48. package/dist/tools/registry/sound.js +1 -1
  49. package/dist/tools/registry/stream.js +1 -1
  50. package/dist/tools/registry/system.js +1 -1
  51. package/dist/tools/schema.js +4 -5
  52. package/dist/tools/storage.js +15 -15
  53. package/dist/tools/streaming.js +3 -3
  54. package/dist/tools/symbolRegistry.js +49 -0
  55. package/dist/tools/types.js +56 -4
  56. package/dist/tools/vice.js +2 -5
  57. package/dist/vice/process.js +120 -2
  58. package/dist/vice/readiness.js +35 -0
  59. package/dist/vice/smokeOptions.js +44 -0
  60. package/dist/vice/viceClient.js +5 -1
  61. package/dist/viceRunner.js +2 -0
  62. package/doc/plans/prompts/README.md +7 -7
  63. package/mcp/prompts.json +42 -59
  64. package/mcp/protocol-examples.json +2 -2
  65. package/mcp/resources.json +179 -166
  66. package/mcp/schemas/c64_batch.schema.json +53 -0
  67. package/mcp/schemas/c64_config.schema.json +525 -283
  68. package/mcp/schemas/c64_debug.schema.json +763 -304
  69. package/mcp/schemas/c64_disk.schema.json +499 -243
  70. package/mcp/schemas/c64_drive.schema.json +165 -90
  71. package/mcp/schemas/c64_extract.schema.json +533 -274
  72. package/mcp/schemas/c64_graphics.schema.json +607 -295
  73. package/mcp/schemas/c64_input.schema.json +136 -0
  74. package/mcp/schemas/c64_memory.schema.json +637 -128
  75. package/mcp/schemas/c64_printer.schema.json +393 -196
  76. package/mcp/schemas/c64_program.schema.json +699 -340
  77. package/mcp/schemas/c64_rag.schema.json +100 -54
  78. package/mcp/schemas/c64_select_backend.schema.json +49 -23
  79. package/mcp/schemas/c64_sound.schema.json +1092 -577
  80. package/mcp/schemas/c64_stream.schema.json +89 -47
  81. package/mcp/schemas/c64_system.schema.json +351 -185
  82. package/mcp/schemas/c64_vice.schema.json +85 -47
  83. package/mcp/server.json +1 -1
  84. package/mcp/tools.json +7299 -3346
  85. package/mcp.json +3 -3
  86. package/package.json +17 -14
  87. package/scripts/fetch-external-sources.mjs +1 -2
  88. package/scripts/run-coverage.mjs +33 -1
  89. package/scripts/run-tests.mjs +2 -2
  90. package/scripts/run-tests.ts +275 -52
  91. package/scripts/update-readme.ts +132 -3
  92. package/server.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "../types.js";
2
- import { memoryModule, memoryOperationHandlers as groupedMemoryHandlers } from "../memory.js";
2
+ import { memoryModule, memoryOperationHandlers as groupedMemoryHandlers, disassembleArgsSchema, copyMemoryArgsSchema, fillMemoryArgsSchema, searchMemoryArgsSchema, compareMemoryArgsSchema, saveMemoryArgsSchema, } from "../memory.js";
3
3
  import { metaModule } from "../meta/index.js";
4
4
  import { buildDescriptorIndex, ensureDescriptor, extendSchemaWithOp, createOperationHandlers, invokeModuleTool, } from "./utils.js";
5
5
  const memoryDescriptorIndex = buildDescriptorIndex(memoryModule);
@@ -25,13 +25,43 @@ const memoryOperations = [
25
25
  schema: extendSchemaWithOp("wait_for_text", ensureDescriptor(metaDescriptorIndex, "wait_for_screen_text").inputSchema, { description: "Poll the screen until a substring or regex appears, or timeout elapses." }),
26
26
  handler: async (rawArgs, ctx) => invokeModuleTool(metaModule, "wait_for_screen_text", rawArgs, ctx),
27
27
  },
28
+ {
29
+ op: "disassemble",
30
+ schema: extendSchemaWithOp("disassemble", disassembleArgsSchema.jsonSchema, { description: "Disassemble a memory region into annotated 6502/6510 instructions, including undocumented opcodes with canonical names. Symbol annotations from `.vs` files are applied when available. Works on both C64U and VICE." }),
31
+ handler: groupedMemoryHandlers.disassemble,
32
+ },
33
+ {
34
+ op: "copy_memory",
35
+ schema: extendSchemaWithOp("copy_memory", copyMemoryArgsSchema.jsonSchema, { description: "Copy a RAM region to another address." }),
36
+ handler: groupedMemoryHandlers.copy_memory,
37
+ },
38
+ {
39
+ op: "fill_memory",
40
+ schema: extendSchemaWithOp("fill_memory", fillMemoryArgsSchema.jsonSchema, { description: "Fill a memory range with a repeating byte pattern." }),
41
+ handler: groupedMemoryHandlers.fill_memory,
42
+ },
43
+ {
44
+ op: "search_memory",
45
+ schema: extendSchemaWithOp("search_memory", searchMemoryArgsSchema.jsonSchema, { description: "Search for a byte pattern within a memory range and return matching addresses." }),
46
+ handler: groupedMemoryHandlers.search_memory,
47
+ },
48
+ {
49
+ op: "compare_memory",
50
+ schema: extendSchemaWithOp("compare_memory", compareMemoryArgsSchema.jsonSchema, { description: "Compare two memory regions byte-by-byte and report differences." }),
51
+ handler: groupedMemoryHandlers.compare_memory,
52
+ },
53
+ {
54
+ op: "save_memory",
55
+ schema: extendSchemaWithOp("save_memory", saveMemoryArgsSchema.jsonSchema, { description: "Dump a memory range to a local file, with an optional PRG load-address header." }),
56
+ handler: groupedMemoryHandlers.save_memory,
57
+ },
28
58
  ];
29
59
  const memoryOperationHandlers = createOperationHandlers(memoryOperations);
30
60
  export const memoryModuleGroup = defineToolModule({
31
61
  domain: "memory",
32
62
  summary: "Grouped memory, screen, and polling operations.",
33
63
  supportedPlatforms: ["c64u", "vice"],
34
- resources: ["c64://context/bootstrap", "c64://specs/basic", "c64://specs/assembly"],
64
+ resources: ["c64://guide/bootstrap", "c64://basic/spec", "c64://assembly/6510-spec"],
35
65
  prompts: ["memory-debug", "basic-program", "assembly-program"],
36
66
  defaultTags: ["memory", "debug"],
37
67
  workflowHints: [
@@ -59,6 +89,36 @@ export const memoryModuleGroup = defineToolModule({
59
89
  description: "Poll until the READY. prompt appears",
60
90
  arguments: { op: "wait_for_text", pattern: "READY." },
61
91
  },
92
+ {
93
+ name: "Disassemble entry point",
94
+ description: "Disassemble 20 instructions starting at $0810",
95
+ arguments: { op: "disassemble", address: "$0810", instructionCount: 20 },
96
+ },
97
+ {
98
+ name: "Copy screen RAM",
99
+ description: "Copy 1000 bytes of screen RAM to a scratch area",
100
+ arguments: { op: "copy_memory", source: "$0400", dest: "$C000", length: 1000 },
101
+ },
102
+ {
103
+ name: "Clear screen RAM",
104
+ description: "Fill screen RAM with space character ($20)",
105
+ arguments: { op: "fill_memory", address: "$0400", length: 1000, pattern: "20" },
106
+ },
107
+ {
108
+ name: "Search for RTS",
109
+ description: "Find RTS ($60) instructions in BASIC ROM",
110
+ arguments: { op: "search_memory", startAddress: "$A000", endAddress: "$BFFF", pattern: "60" },
111
+ },
112
+ {
113
+ name: "Compare two buffers",
114
+ description: "Check whether two 256-byte regions differ",
115
+ arguments: { op: "compare_memory", address1: "$C000", address2: "$C100", length: 256 },
116
+ },
117
+ {
118
+ name: "Save PRG dump",
119
+ description: "Save $0801-$CFFF as a PRG file",
120
+ arguments: { op: "save_memory", startAddress: "$0801", endAddress: "$CFFF", filePath: "/tmp/dump.prg" },
121
+ },
62
122
  ],
63
123
  execute: createOperationDispatcher("c64_memory", memoryOperationHandlers),
64
124
  },
@@ -92,7 +92,7 @@ export const platformModuleGroup = defineToolModule({
92
92
  domain: "platform",
93
93
  summary: "Runtime backend selection and platform-status coordination.",
94
94
  supportedPlatforms: ["c64u", "vice"],
95
- resources: ["c64://platform/status", "c64://context/bootstrap"],
95
+ resources: ["c64://platform/status", "c64://guide/bootstrap"],
96
96
  prompts: [],
97
97
  defaultTags: ["platform", "backend", "grouped"],
98
98
  workflowHints: [
@@ -117,7 +117,7 @@ function invokePrintTool(toolName, rawArgs, ctx) {
117
117
  export const printerModuleGroup = defineToolModule({
118
118
  domain: "printer",
119
119
  summary: "Grouped printer text, bitmap, and character definition helpers.",
120
- resources: ["c64://context/bootstrap"],
120
+ resources: ["c64://guide/bootstrap"],
121
121
  defaultTags: ["printer", "device"],
122
122
  workflowHints: [
123
123
  "Mention device/secondary addresses so the user knows which printer workflow ran.",
@@ -50,7 +50,7 @@ const programOperationHandlers = createOperationHandlers(programOperations);
50
50
  export const programModule = defineToolModule({
51
51
  domain: "programs",
52
52
  summary: "Grouped program upload, run, and orchestration workflows.",
53
- resources: ["c64://context/bootstrap", "c64://specs/basic", "c64://specs/assembly"],
53
+ resources: ["c64://guide/bootstrap", "c64://basic/spec", "c64://assembly/6510-spec"],
54
54
  prompts: ["basic-program", "assembly-program"],
55
55
  defaultTags: ["programs", "execution"],
56
56
  workflowHints: [
@@ -18,7 +18,7 @@ const ragOperationHandlers = createOperationHandlers(ragOperations);
18
18
  export const ragModuleGroup = defineToolModule({
19
19
  domain: "rag",
20
20
  summary: "Grouped retrieval helpers for BASIC and assembly references.",
21
- resources: ["c64://specs/basic", "c64://specs/assembly", "c64://context/bootstrap"],
21
+ resources: ["c64://basic/spec", "c64://assembly/6510-spec", "c64://guide/bootstrap"],
22
22
  prompts: ["basic-program", "assembly-program"],
23
23
  defaultTags: ["rag", "search"],
24
24
  workflowHints: [
@@ -140,7 +140,7 @@ export const soundModuleGroup = defineToolModule({
140
140
  domain: "audio",
141
141
  summary: "Grouped SID control, playback, composition, and analysis operations.",
142
142
  supportedPlatforms: ["c64u", "vice"],
143
- resources: ["c64://specs/sid", "c64://specs/sidwave", "c64://docs/sid/file-structure"],
143
+ resources: ["c64://sound/sid/spec", "c64://sound/sidwave/spec", "c64://sound/sid/file-format"],
144
144
  prompts: ["sid-music"],
145
145
  defaultTags: ["sid", "audio"],
146
146
  workflowHints: [
@@ -18,7 +18,7 @@ const streamOperationHandlers = createOperationHandlers(streamOperations);
18
18
  export const streamModule = defineToolModule({
19
19
  domain: "stream",
20
20
  summary: "Grouped streaming helpers for starting and stopping Ultimate capture sessions.",
21
- resources: ["c64://docs/index", "c64://specs/sid"],
21
+ resources: ["c64://guide/index", "c64://sound/sid/spec"],
22
22
  prompts: ["sid-music"],
23
23
  defaultTags: ["stream", "monitor"],
24
24
  workflowHints: [
@@ -66,7 +66,7 @@ export const systemModuleGroup = defineToolModule({
66
66
  domain: "system",
67
67
  summary: "Grouped machine control and background task orchestration.",
68
68
  supportedPlatforms: ["c64u", "vice"],
69
- resources: ["c64://context/bootstrap"],
69
+ resources: ["c64://guide/bootstrap"],
70
70
  prompts: ["memory-debug"],
71
71
  defaultTags: ["system", "control"],
72
72
  workflowHints: [
@@ -195,15 +195,14 @@ function isPlainObject(value) {
195
195
  }
196
196
  export function objectSchema(options) {
197
197
  const required = options.required ?? [];
198
+ const additionalProperties = options.additionalProperties ?? false;
198
199
  const propertyEntries = Object.entries(options.properties).map(([key, schema]) => [key, schema.jsonSchema]);
199
200
  const jsonSchema = {
200
201
  type: "object",
201
202
  ...(options.description ? { description: options.description } : {}),
202
203
  properties: Object.fromEntries(propertyEntries),
203
204
  ...(required.length > 0 ? { required: required.map((key) => key) } : {}),
204
- ...(options.additionalProperties !== undefined
205
- ? { additionalProperties: options.additionalProperties }
206
- : { additionalProperties: false }),
205
+ additionalProperties,
207
206
  };
208
207
  return createSchema(jsonSchema, (value, path) => {
209
208
  ensureType(isPlainObject(value), "Expected an object", path, {
@@ -234,7 +233,7 @@ export function objectSchema(options) {
234
233
  }
235
234
  }
236
235
  }
237
- if (options.additionalProperties === false) {
236
+ if (!additionalProperties) {
238
237
  for (const key of Object.keys(input)) {
239
238
  if (!hasOwn.call(options.properties, key)) {
240
239
  throw new ToolValidationError("Unexpected property", {
@@ -243,7 +242,7 @@ export function objectSchema(options) {
243
242
  }
244
243
  }
245
244
  }
246
- else if (options.additionalProperties === true) {
245
+ else {
247
246
  for (const key of Object.keys(input)) {
248
247
  if (!hasOwn.call(options.properties, key)) {
249
248
  result[key] = input[key];
@@ -144,7 +144,7 @@ export const storageModule = defineToolModule({
144
144
  domain: "storage",
145
145
  summary: "Drive management, disk image creation, and file inspection utilities.",
146
146
  supportedPlatforms: ["c64u", "vice"],
147
- resources: ["c64://context/bootstrap"],
147
+ resources: ["c64://guide/bootstrap"],
148
148
  prompts: ["memory-debug"],
149
149
  defaultTags: ["drive", "storage"],
150
150
  workflowHints: [
@@ -154,10 +154,10 @@ export const storageModule = defineToolModule({
154
154
  tools: [
155
155
  {
156
156
  name: "drives_list",
157
- description: "List Ultimate drive slots and their currently mounted images. Read c64://context/bootstrap for drive safety.",
157
+ description: "List Ultimate drive slots and their currently mounted images. Read c64://guide/bootstrap for drive safety.",
158
158
  summary: "Fetches firmware drive status including mounted images, power state, and modes.",
159
159
  inputSchema: drivesListArgsSchema.jsonSchema,
160
- relatedResources: ["c64://context/bootstrap"],
160
+ relatedResources: ["c64://guide/bootstrap"],
161
161
  tags: ["drive", "status"],
162
162
  prerequisites: [],
163
163
  examples: [
@@ -193,7 +193,7 @@ export const storageModule = defineToolModule({
193
193
  description: "Mount a disk image onto a specific Ultimate drive slot.",
194
194
  summary: "Validates parameters and instructs the firmware to mount an image with optional type/mode overrides.",
195
195
  inputSchema: driveMountArgsSchema.jsonSchema,
196
- relatedResources: ["c64://context/bootstrap"],
196
+ relatedResources: ["c64://guide/bootstrap"],
197
197
  tags: ["drive", "mount"],
198
198
  prerequisites: ["drives_list"],
199
199
  examples: [
@@ -250,7 +250,7 @@ export const storageModule = defineToolModule({
250
250
  description: "Remove the currently mounted disk image from an Ultimate drive slot.",
251
251
  summary: "Ejects the image and leaves the drive empty without powering it off.",
252
252
  inputSchema: driveOnlyArgsSchema.jsonSchema,
253
- relatedResources: ["c64://context/bootstrap"],
253
+ relatedResources: ["c64://guide/bootstrap"],
254
254
  tags: ["drive", "unmount"],
255
255
  prerequisites: ["drives_list"],
256
256
  examples: [
@@ -292,7 +292,7 @@ export const storageModule = defineToolModule({
292
292
  description: "Reset the selected Ultimate drive slot.",
293
293
  summary: "Issues a IEC reset for the chosen drive without cycling the entire machine.",
294
294
  inputSchema: driveOnlyArgsSchema.jsonSchema,
295
- relatedResources: ["c64://context/bootstrap"],
295
+ relatedResources: ["c64://guide/bootstrap"],
296
296
  tags: ["drive", "reset"],
297
297
  prerequisites: ["drives_list"],
298
298
  examples: [
@@ -334,7 +334,7 @@ export const storageModule = defineToolModule({
334
334
  description: "Power on a specific Ultimate drive slot.",
335
335
  summary: "Ensures the selected drive is reported as powered and ready on the IEC bus.",
336
336
  inputSchema: driveOnlyArgsSchema.jsonSchema,
337
- relatedResources: ["c64://context/bootstrap"],
337
+ relatedResources: ["c64://guide/bootstrap"],
338
338
  tags: ["drive", "power"],
339
339
  prerequisites: ["drives_list"],
340
340
  examples: [
@@ -377,7 +377,7 @@ export const storageModule = defineToolModule({
377
377
  description: "Power off a specific Ultimate drive slot.",
378
378
  summary: "Disables the drive without altering mounted media so it can be re-enabled later.",
379
379
  inputSchema: driveOnlyArgsSchema.jsonSchema,
380
- relatedResources: ["c64://context/bootstrap"],
380
+ relatedResources: ["c64://guide/bootstrap"],
381
381
  tags: ["drive", "power"],
382
382
  prerequisites: ["drives_list"],
383
383
  examples: [
@@ -420,7 +420,7 @@ export const storageModule = defineToolModule({
420
420
  description: "Temporarily load a custom ROM into an Ultimate drive slot.",
421
421
  summary: "Points the drive at an alternative ROM file without flashing permanent storage.",
422
422
  inputSchema: driveLoadRomArgsSchema.jsonSchema,
423
- relatedResources: ["c64://context/bootstrap"],
423
+ relatedResources: ["c64://guide/bootstrap"],
424
424
  tags: ["drive", "rom"],
425
425
  prerequisites: ["drives_list"],
426
426
  examples: [
@@ -463,7 +463,7 @@ export const storageModule = defineToolModule({
463
463
  description: "Set the emulation mode for an Ultimate drive slot (1541/1571/1581).",
464
464
  summary: "Switches the drive hardware profile to match the mounted media and desired behaviour.",
465
465
  inputSchema: driveModeArgsSchema.jsonSchema,
466
- relatedResources: ["c64://context/bootstrap"],
466
+ relatedResources: ["c64://guide/bootstrap"],
467
467
  tags: ["drive", "mode"],
468
468
  prerequisites: ["drives_list"],
469
469
  examples: [
@@ -506,7 +506,7 @@ export const storageModule = defineToolModule({
506
506
  description: "Inspect metadata for a file on the Ultimate filesystem.",
507
507
  summary: "Queries the REST API for file size, type, and other details useful when managing disk images.",
508
508
  inputSchema: fileInfoArgsSchema.jsonSchema,
509
- relatedResources: ["c64://context/bootstrap"],
509
+ relatedResources: ["c64://guide/bootstrap"],
510
510
  tags: ["storage", "info"],
511
511
  prerequisites: [],
512
512
  examples: [
@@ -543,7 +543,7 @@ export const storageModule = defineToolModule({
543
543
  description: "Create a blank D64 disk image on the Ultimate filesystem.",
544
544
  summary: "Allocates a 35 or 40 track D64 image with an optional disk name.",
545
545
  inputSchema: createD64ArgsSchema.jsonSchema,
546
- relatedResources: ["c64://context/bootstrap"],
546
+ relatedResources: ["c64://guide/bootstrap"],
547
547
  tags: ["disk", "create"],
548
548
  prerequisites: ["drives_list"],
549
549
  examples: [
@@ -594,7 +594,7 @@ export const storageModule = defineToolModule({
594
594
  description: "Create a blank D71 disk image on the Ultimate filesystem.",
595
595
  summary: "Allocates a 1571-compatible disk image with optional labelling.",
596
596
  inputSchema: createD71ArgsSchema.jsonSchema,
597
- relatedResources: ["c64://context/bootstrap"],
597
+ relatedResources: ["c64://guide/bootstrap"],
598
598
  tags: ["disk", "create"],
599
599
  prerequisites: ["drives_list"],
600
600
  examples: [
@@ -642,7 +642,7 @@ export const storageModule = defineToolModule({
642
642
  description: "Create a blank D81 disk image on the Ultimate filesystem.",
643
643
  summary: "Allocates a 1581-compatible disk image with optional labelling.",
644
644
  inputSchema: createD81ArgsSchema.jsonSchema,
645
- relatedResources: ["c64://context/bootstrap"],
645
+ relatedResources: ["c64://guide/bootstrap"],
646
646
  tags: ["disk", "create"],
647
647
  prerequisites: ["drives_list"],
648
648
  examples: [
@@ -690,7 +690,7 @@ export const storageModule = defineToolModule({
690
690
  description: "Create a blank DNP disk image on the Ultimate filesystem.",
691
691
  summary: "Allocates a CMD-native DNP image with custom track count and optional label.",
692
692
  inputSchema: createDnpArgsSchema.jsonSchema,
693
- relatedResources: ["c64://context/bootstrap"],
693
+ relatedResources: ["c64://guide/bootstrap"],
694
694
  tags: ["disk", "create"],
695
695
  prerequisites: [
696
696
  "drives_list"
@@ -36,8 +36,8 @@ export const streamingModule = defineToolModule({
36
36
  domain: "streaming",
37
37
  summary: "Long-running or streaming workflows such as audio capture or SID playback monitoring.",
38
38
  resources: [
39
- "c64://specs/sid",
40
- "c64://docs/index",
39
+ "c64://sound/sid/spec",
40
+ "c64://guide/index",
41
41
  ],
42
42
  prompts: ["sid-music"],
43
43
  defaultLifecycle: "stream",
@@ -50,7 +50,7 @@ export const streamingModule = defineToolModule({
50
50
  tools: [
51
51
  {
52
52
  name: "stream_start",
53
- description: "Start an Ultimate streaming session (video/audio/debug) targeting a host:port destination. See c64://docs/index for usage notes.",
53
+ description: "Start an Ultimate streaming session (video/audio/debug) targeting a host:port destination. See c64://guide/index for usage notes.",
54
54
  summary: "Validates stream arguments and forwards the request to firmware, returning status metadata.",
55
55
  inputSchema: streamStartArgsSchema.jsonSchema,
56
56
  tags: ["stream", "start"],
@@ -0,0 +1,49 @@
1
+ /**
2
+ * In-process symbol registry for VICE debug sessions.
3
+ * Maps addresses to label names so the disassembler can annotate output.
4
+ * Cleared and repopulated each time a new program is loaded.
5
+ */
6
+ const viceAddressToLabel = new Map();
7
+ export function setViceSymbols(nameToAddress) {
8
+ viceAddressToLabel.clear();
9
+ for (const [name, address] of nameToAddress) {
10
+ if (address >= 0 && address <= 0xffff) {
11
+ viceAddressToLabel.set(address, name);
12
+ }
13
+ }
14
+ }
15
+ export function getViceSymbol(address) {
16
+ return viceAddressToLabel.get(address);
17
+ }
18
+ export function clearViceSymbols() {
19
+ viceAddressToLabel.clear();
20
+ }
21
+ export function getViceSymbols() {
22
+ return viceAddressToLabel;
23
+ }
24
+ /**
25
+ * Parse a VICE symbol file (.vs) into a name->address map.
26
+ *
27
+ * Supported line formats:
28
+ * add_label 0x0810 .main
29
+ * add_label C:0810 .main
30
+ * al C:0810 .main
31
+ * al 0810 .main
32
+ */
33
+ export function parseViceSymbolFile(content) {
34
+ const symbols = new Map();
35
+ for (const rawLine of content.split("\n")) {
36
+ const line = rawLine.trim();
37
+ if (!line || line.startsWith(";") || line.startsWith("#"))
38
+ continue;
39
+ const match = /^(?:add_label|al)\s+(?:[A-Za-z]+:)?(?:0x|\$)?([0-9A-Fa-f]+)\s+\.?(\S+)/i.exec(line);
40
+ if (match) {
41
+ const addr = parseInt(match[1], 16);
42
+ const name = match[2];
43
+ if (!isNaN(addr) && addr >= 0 && addr <= 0xffff && name) {
44
+ symbols.set(name, addr);
45
+ }
46
+ }
47
+ }
48
+ return symbols;
49
+ }
@@ -30,13 +30,65 @@ export function discriminatedUnionSchema(options) {
30
30
  if (!variants || variants.length === 0) {
31
31
  throw new Error("Discriminated union schemas require at least one variant.");
32
32
  }
33
+ // Flatten the discriminated union into a single object schema.
34
+ // Claude's API does not support oneOf/allOf/anyOf at the top level,
35
+ // so we merge all variant properties and use an enum for the discriminator.
36
+ const opValues = [];
37
+ const mergedProperties = {};
38
+ const opDescriptions = [];
39
+ const operationMetadata = [];
40
+ for (const variant of variants) {
41
+ const variantProps = variant.properties ?? {};
42
+ const discProp = variantProps[discriminator];
43
+ const opValue = discProp?.const;
44
+ const variantDesc = variant.description ?? discProp?.description ?? "";
45
+ const requiredInputs = (variant.required ?? []).filter((name) => name !== discriminator);
46
+ const optionalInputs = Object.keys(variantProps).filter((name) => name !== discriminator && !requiredInputs.includes(name));
47
+ if (opValue) {
48
+ opValues.push(opValue);
49
+ const requiredSummary = requiredInputs.length > 0
50
+ ? ` Required inputs: ${requiredInputs.join(", ")}.`
51
+ : " Required inputs: none.";
52
+ const optionalSummary = optionalInputs.length > 0
53
+ ? ` Optional inputs: ${optionalInputs.join(", ")}.`
54
+ : "";
55
+ opDescriptions.push(`${opValue}: ${variantDesc || `Operation ${opValue}`}${requiredSummary}${optionalSummary}`);
56
+ operationMetadata.push({
57
+ op: opValue,
58
+ ...(variantDesc ? { description: variantDesc } : {}),
59
+ required: requiredInputs,
60
+ optional: optionalInputs,
61
+ inputSchema: variant,
62
+ });
63
+ }
64
+ for (const [key, propSchema] of Object.entries(variantProps)) {
65
+ if (key === discriminator)
66
+ continue;
67
+ if (!mergedProperties[key]) {
68
+ mergedProperties[key] = propSchema;
69
+ }
70
+ }
71
+ }
72
+ const opPropertySchema = opValues.length > 0
73
+ ? { type: "string", enum: opValues, description: `Selects the operation.` }
74
+ : { type: "string", description: `Selects the operation.` };
75
+ const fullDescription = description
76
+ ? (opDescriptions.length > 0
77
+ ? `${description}\n\nOperations:\n${opDescriptions.map((d) => `- ${d}`).join("\n")}`
78
+ : description)
79
+ : (opDescriptions.length > 0
80
+ ? `Operations:\n${opDescriptions.map((d) => `- ${d}`).join("\n")}`
81
+ : undefined);
33
82
  const schema = {
34
83
  type: "object",
35
- ...(description ? { description } : {}),
36
- oneOf: [...variants],
37
- discriminator: {
38
- propertyName: discriminator,
84
+ ...(fullDescription ? { description: fullDescription } : {}),
85
+ properties: {
86
+ [discriminator]: opPropertySchema,
87
+ ...mergedProperties,
39
88
  },
89
+ required: [discriminator],
90
+ additionalProperties: false,
91
+ ...(operationMetadata.length > 0 ? { "x-c64bridge-operations": operationMetadata } : {}),
40
92
  };
41
93
  return schema;
42
94
  }
@@ -15,11 +15,8 @@ const SAFE_RESOURCE_PREFIXES = [
15
15
  ];
16
16
  const resourceValueSchema = {
17
17
  jsonSchema: {
18
+ type: ["string", "integer"],
18
19
  description: "VICE resource value (string or integer).",
19
- oneOf: [
20
- { type: "string" },
21
- { type: "integer" },
22
- ],
23
20
  },
24
21
  parse(value, path) {
25
22
  const resolvedPath = path ?? "$.value";
@@ -110,7 +107,7 @@ const viceOperationDispatcher = createOperationDispatcher("c64_vice", viceOperat
110
107
  export const viceModuleGroup = defineToolModule({
111
108
  domain: "vice",
112
109
  summary: "VICE emulator helpers for safe runtime resource access.",
113
- resources: ["c64://specs/memory-map"],
110
+ resources: ["c64://memory/map"],
114
111
  prompts: ["assembly-program", "memory-debug"],
115
112
  defaultTags: ["vice", "emulator"],
116
113
  workflowHints: [