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,8 +1,13 @@
1
1
  import { Buffer } from "node:buffer";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
2
4
  import { defineToolModule, OPERATION_DISCRIMINATOR, } from "./types.js";
3
5
  import { booleanSchema, numberSchema, objectSchema, optionalSchema, stringSchema } from "./schema.js";
4
- import { textResult } from "./responses.js";
6
+ import { jsonResult, textResult } from "./responses.js";
5
7
  import { ToolError, ToolExecutionError, ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
8
+ import { disassemble, formatDisassembly } from "./disassembler.js";
9
+ import { getViceSymbols } from "./symbolRegistry.js";
10
+ import { resolveAddressSymbol } from "../knowledge.js";
6
11
  function toRecord(details) {
7
12
  if (details && typeof details === "object") {
8
13
  return details;
@@ -79,6 +84,18 @@ function supportsMachinePause(ctx) {
79
84
  const platformId = ctx.platform?.id;
80
85
  return platformId !== "vice";
81
86
  }
87
+ export const __memoryHelpersForTests = {
88
+ toRecord,
89
+ normaliseFailure,
90
+ cleanHex,
91
+ parseUserHex,
92
+ parseFirmwareHex,
93
+ formatByte,
94
+ resolveAddressLabel,
95
+ resolveLength,
96
+ supportsMachinePause,
97
+ stripOperationDiscriminator,
98
+ };
82
99
  const readScreenArgsSchema = objectSchema({
83
100
  description: "No arguments are required for reading the current screen contents.",
84
101
  properties: {},
@@ -171,13 +188,21 @@ async function executeReadMemory(rawArgs, ctx) {
171
188
  const detailRecord = toRecord(result.details) ?? {};
172
189
  const resolvedAddress = resolveAddressLabel(detailRecord, parsed.address);
173
190
  const resolvedLength = resolveLength(detailRecord) ?? parsed.length;
174
- return textResult(`Read ${resolvedLength} bytes starting at ${resolvedAddress}.`, {
191
+ const data = {
175
192
  success: true,
176
193
  address: resolvedAddress,
177
194
  length: resolvedLength,
178
195
  hexData: result.data ?? null,
179
196
  details: detailRecord,
180
- });
197
+ };
198
+ const base = textResult(`Read ${resolvedLength} bytes starting at ${resolvedAddress}.`, data);
199
+ return {
200
+ ...base,
201
+ structuredContent: {
202
+ type: "json",
203
+ data,
204
+ },
205
+ };
181
206
  }
182
207
  catch (error) {
183
208
  if (error instanceof ToolError) {
@@ -344,19 +369,318 @@ async function executeWriteMemory(rawArgs, ctx) {
344
369
  return unknownErrorResult(error);
345
370
  }
346
371
  }
372
+ const disassembleArgsSchema = objectSchema({
373
+ description: "Parameters for disassembling a block of VICE memory into 6502/6510 instructions.",
374
+ properties: {
375
+ address: stringSchema({
376
+ description: "Start address as $HHHH, decimal, or symbol name.",
377
+ minLength: 1,
378
+ }),
379
+ length: numberSchema({
380
+ description: "Number of bytes to read and disassemble.",
381
+ integer: true,
382
+ minimum: 1,
383
+ maximum: 4096,
384
+ default: 64,
385
+ }),
386
+ instructionCount: optionalSchema(numberSchema({
387
+ description: "Stop after this many instructions (overrides length as a termination condition).",
388
+ integer: true,
389
+ minimum: 1,
390
+ maximum: 512,
391
+ })),
392
+ },
393
+ required: ["address"],
394
+ additionalProperties: false,
395
+ });
396
+ async function executeDisassemble(rawArgs, ctx) {
397
+ try {
398
+ const parsed = disassembleArgsSchema.parse(rawArgs ?? {});
399
+ const length = parsed.length ?? 64;
400
+ ctx.logger.info("Disassembling VICE memory", { address: parsed.address, length });
401
+ const address = parseAddressArg(parsed.address, "$.address");
402
+ const bytes = await ctx.client.readMemoryRaw(address, length);
403
+ const symbols = ctx.platform?.id === "vice" ? getViceSymbols() : undefined;
404
+ const lines = disassemble(bytes, address, parsed.instructionCount, symbols);
405
+ const text = formatDisassembly(lines);
406
+ const addrHex = `$${address.toString(16).toUpperCase().padStart(4, "0")}`;
407
+ return textResult(`Disassembly of ${lines.length} instructions starting at ${addrHex}:\n\n${text}`, {
408
+ success: true,
409
+ address: addrHex,
410
+ bytesRead: bytes.length,
411
+ instructionCount: lines.length,
412
+ });
413
+ }
414
+ catch (error) {
415
+ if (error instanceof ToolError) {
416
+ return toolErrorResult(error);
417
+ }
418
+ return unknownErrorResult(error);
419
+ }
420
+ }
421
+ // ---------------------------------------------------------------------------
422
+ // Shared address-parsing helper
423
+ // ---------------------------------------------------------------------------
424
+ function parseAddressArg(raw, fieldPath) {
425
+ const trimmed = raw.trim();
426
+ let addr;
427
+ if (/^\$[0-9A-Fa-f]+$/.test(trimmed)) {
428
+ addr = parseInt(trimmed.slice(1), 16);
429
+ }
430
+ else if (/^0[xX][0-9A-Fa-f]+$/.test(trimmed)) {
431
+ addr = parseInt(trimmed.slice(2), 16);
432
+ }
433
+ else if (/^[0-9]+$/.test(trimmed)) {
434
+ addr = parseInt(trimmed, 10);
435
+ }
436
+ else {
437
+ const resolved = resolveAddressSymbol(trimmed);
438
+ if (resolved === undefined) {
439
+ throw new ToolValidationError(`Unknown address or symbol: ${trimmed}`, { path: fieldPath });
440
+ }
441
+ addr = resolved;
442
+ }
443
+ if (!Number.isInteger(addr) || addr < 0 || addr > 0xffff) {
444
+ throw new ToolValidationError("Address must be in range $0000-$FFFF", { path: fieldPath });
445
+ }
446
+ return addr;
447
+ }
448
+ function fmtAddr(addr) {
449
+ return `$${addr.toString(16).toUpperCase().padStart(4, "0")}`;
450
+ }
451
+ function validateRangeWithinAddressSpace(start, length, fieldPath) {
452
+ const end = start + length - 1;
453
+ if (end > 0xffff) {
454
+ throw new ToolValidationError(`${fieldPath} range exceeds address space; ${fmtAddr(start)} + ${length} bytes would pass $FFFF.`, { path: fieldPath });
455
+ }
456
+ }
457
+ // ---------------------------------------------------------------------------
458
+ // copy_memory
459
+ // ---------------------------------------------------------------------------
460
+ const copyMemoryArgsSchema = objectSchema({
461
+ description: "Copy a region of RAM from one address to another.",
462
+ properties: {
463
+ source: stringSchema({ description: "Source start address ($HHHH, decimal, or symbol).", minLength: 1 }),
464
+ dest: stringSchema({ description: "Destination start address.", minLength: 1 }),
465
+ length: numberSchema({ description: "Number of bytes to copy (1–16384).", integer: true, minimum: 1, maximum: 16384 }),
466
+ },
467
+ required: ["source", "dest", "length"],
468
+ additionalProperties: false,
469
+ });
470
+ async function executeCopyMemory(rawArgs, ctx) {
471
+ try {
472
+ const parsed = copyMemoryArgsSchema.parse(rawArgs ?? {});
473
+ const src = parseAddressArg(parsed.source, "$.source");
474
+ const dst = parseAddressArg(parsed.dest, "$.dest");
475
+ const len = parsed.length;
476
+ validateRangeWithinAddressSpace(src, len, "$.source");
477
+ validateRangeWithinAddressSpace(dst, len, "$.dest");
478
+ ctx.logger.info("Copying memory", { src: fmtAddr(src), dst: fmtAddr(dst), len });
479
+ const bytes = await ctx.client.readMemoryRaw(src, len);
480
+ await ctx.client.writeMemoryRaw(dst, bytes);
481
+ return textResult(`Copied ${bytes.length} bytes from ${fmtAddr(src)} to ${fmtAddr(dst)}.`, {
482
+ success: true, source: fmtAddr(src), dest: fmtAddr(dst), bytesCopied: bytes.length,
483
+ });
484
+ }
485
+ catch (error) {
486
+ if (error instanceof ToolError)
487
+ return toolErrorResult(error);
488
+ return unknownErrorResult(error);
489
+ }
490
+ }
491
+ // ---------------------------------------------------------------------------
492
+ // fill_memory
493
+ // ---------------------------------------------------------------------------
494
+ const fillMemoryArgsSchema = objectSchema({
495
+ description: "Fill a memory region with a repeating byte pattern.",
496
+ properties: {
497
+ address: stringSchema({ description: "Start address ($HHHH, decimal, or symbol).", minLength: 1 }),
498
+ length: numberSchema({ description: "Number of bytes to fill (1–16384).", integer: true, minimum: 1, maximum: 16384 }),
499
+ pattern: stringSchema({
500
+ description: "Hex bytes to repeat, e.g. 'FF' or 'AA 55'. Space-separated or run together.",
501
+ minLength: 2,
502
+ pattern: /^[\s0-9A-Fa-f$]+$/,
503
+ }),
504
+ },
505
+ required: ["address", "length", "pattern"],
506
+ additionalProperties: false,
507
+ });
508
+ async function executeFillMemory(rawArgs, ctx) {
509
+ try {
510
+ const parsed = fillMemoryArgsSchema.parse(rawArgs ?? {});
511
+ const addr = parseAddressArg(parsed.address, "$.address");
512
+ validateRangeWithinAddressSpace(addr, parsed.length, "$.address");
513
+ const { bytes: patternBytes } = parseUserHex(parsed.pattern, "$.pattern");
514
+ if (patternBytes.length === 0)
515
+ throw new ToolValidationError("Pattern must not be empty", { path: "$.pattern" });
516
+ const buf = new Uint8Array(parsed.length);
517
+ for (let i = 0; i < parsed.length; i++)
518
+ buf[i] = patternBytes[i % patternBytes.length];
519
+ await ctx.client.writeMemoryRaw(addr, buf);
520
+ ctx.logger.info("Filled memory", { address: fmtAddr(addr), length: parsed.length, pattern: parsed.pattern });
521
+ return textResult(`Filled ${parsed.length} bytes at ${fmtAddr(addr)} with pattern ${parsed.pattern.trim()}.`, {
522
+ success: true, address: fmtAddr(addr), length: parsed.length, pattern: parsed.pattern.trim(),
523
+ });
524
+ }
525
+ catch (error) {
526
+ if (error instanceof ToolError)
527
+ return toolErrorResult(error);
528
+ return unknownErrorResult(error);
529
+ }
530
+ }
531
+ // ---------------------------------------------------------------------------
532
+ // search_memory
533
+ // ---------------------------------------------------------------------------
534
+ const searchMemoryArgsSchema = objectSchema({
535
+ description: "Search for a byte pattern within a memory range.",
536
+ properties: {
537
+ startAddress: stringSchema({ description: "Start of search range ($HHHH, decimal, or symbol).", minLength: 1 }),
538
+ endAddress: stringSchema({ description: "End of search range (inclusive).", minLength: 1 }),
539
+ pattern: stringSchema({
540
+ description: "Hex bytes to find, e.g. 'A9 00' for LDA #$00.",
541
+ minLength: 2,
542
+ pattern: /^[\s0-9A-Fa-f$]+$/,
543
+ }),
544
+ maxResults: optionalSchema(numberSchema({ description: "Maximum matches to return (default 10).", integer: true, minimum: 1, maximum: 100, default: 10 })),
545
+ },
546
+ required: ["startAddress", "endAddress", "pattern"],
547
+ additionalProperties: false,
548
+ });
549
+ async function executeSearchMemory(rawArgs, ctx) {
550
+ try {
551
+ const parsed = searchMemoryArgsSchema.parse(rawArgs ?? {});
552
+ const start = parseAddressArg(parsed.startAddress, "$.startAddress");
553
+ const end = parseAddressArg(parsed.endAddress, "$.endAddress");
554
+ if (end < start)
555
+ throw new ToolValidationError("endAddress must be ≥ startAddress", { path: "$.endAddress" });
556
+ const { bytes: needle } = parseUserHex(parsed.pattern, "$.pattern");
557
+ if (needle.length === 0)
558
+ throw new ToolValidationError("Pattern must not be empty", { path: "$.pattern" });
559
+ const maxResults = parsed.maxResults ?? 10;
560
+ const len = end - start + 1;
561
+ ctx.logger.info("Searching memory", { start: fmtAddr(start), end: fmtAddr(end), pattern: parsed.pattern });
562
+ const haystack = await ctx.client.readMemoryRaw(start, len);
563
+ const matches = [];
564
+ outer: for (let i = 0; i <= haystack.length - needle.length && matches.length < maxResults; i++) {
565
+ for (let j = 0; j < needle.length; j++) {
566
+ if (haystack[i + j] !== needle[j])
567
+ continue outer;
568
+ }
569
+ matches.push(fmtAddr(start + i));
570
+ }
571
+ return jsonResult({ found: matches.length, matches, pattern: parsed.pattern.trim(), range: { start: fmtAddr(start), end: fmtAddr(end) } }, { success: true, found: matches.length });
572
+ }
573
+ catch (error) {
574
+ if (error instanceof ToolError)
575
+ return toolErrorResult(error);
576
+ return unknownErrorResult(error);
577
+ }
578
+ }
579
+ // ---------------------------------------------------------------------------
580
+ // compare_memory
581
+ // ---------------------------------------------------------------------------
582
+ const compareMemoryArgsSchema = objectSchema({
583
+ description: "Compare two memory regions and report differing bytes.",
584
+ properties: {
585
+ address1: stringSchema({ description: "First region start address.", minLength: 1 }),
586
+ address2: stringSchema({ description: "Second region start address.", minLength: 1 }),
587
+ length: numberSchema({ description: "Number of bytes to compare (1–16384).", integer: true, minimum: 1, maximum: 16384 }),
588
+ maxDiffs: optionalSchema(numberSchema({ description: "Max differences to report (default 10).", integer: true, minimum: 1, maximum: 200, default: 10 })),
589
+ },
590
+ required: ["address1", "address2", "length"],
591
+ additionalProperties: false,
592
+ });
593
+ async function executeCompareMemory(rawArgs, ctx) {
594
+ try {
595
+ const parsed = compareMemoryArgsSchema.parse(rawArgs ?? {});
596
+ const a1 = parseAddressArg(parsed.address1, "$.address1");
597
+ const a2 = parseAddressArg(parsed.address2, "$.address2");
598
+ const len = parsed.length;
599
+ const maxDiffs = parsed.maxDiffs ?? 10;
600
+ ctx.logger.info("Comparing memory regions", { a1: fmtAddr(a1), a2: fmtAddr(a2), len });
601
+ const [r1, r2] = await Promise.all([
602
+ ctx.client.readMemoryRaw(a1, len),
603
+ ctx.client.readMemoryRaw(a2, len),
604
+ ]);
605
+ const diffs = [];
606
+ for (let i = 0; i < len && diffs.length < maxDiffs; i++) {
607
+ if (r1[i] !== r2[i]) {
608
+ diffs.push({
609
+ offset: i,
610
+ address1: fmtAddr(a1 + i),
611
+ address2: fmtAddr(a2 + i),
612
+ value1: formatByte(r1[i] ?? 0),
613
+ value2: formatByte(r2[i] ?? 0),
614
+ });
615
+ }
616
+ }
617
+ const identical = diffs.length === 0;
618
+ return jsonResult({ identical, diffCount: diffs.length, diffs, region1: fmtAddr(a1), region2: fmtAddr(a2), length: len }, { success: true, identical, diffCount: diffs.length });
619
+ }
620
+ catch (error) {
621
+ if (error instanceof ToolError)
622
+ return toolErrorResult(error);
623
+ return unknownErrorResult(error);
624
+ }
625
+ }
626
+ // ---------------------------------------------------------------------------
627
+ // save_memory
628
+ // ---------------------------------------------------------------------------
629
+ const saveMemoryArgsSchema = objectSchema({
630
+ description: "Dump a memory region to a local file, optionally with a PRG load-address header.",
631
+ properties: {
632
+ startAddress: stringSchema({ description: "Start address of region to save.", minLength: 1 }),
633
+ endAddress: stringSchema({ description: "End address (inclusive).", minLength: 1 }),
634
+ filePath: stringSchema({ description: "Absolute or relative path for the output file.", minLength: 1 }),
635
+ asPrg: optionalSchema(booleanSchema({ description: "Prepend a 2-byte little-endian load-address header (PRG format). Default: true.", default: true })),
636
+ },
637
+ required: ["startAddress", "endAddress", "filePath"],
638
+ additionalProperties: false,
639
+ });
640
+ async function executeSaveMemory(rawArgs, ctx) {
641
+ try {
642
+ const parsed = saveMemoryArgsSchema.parse(rawArgs ?? {});
643
+ const start = parseAddressArg(parsed.startAddress, "$.startAddress");
644
+ const end = parseAddressArg(parsed.endAddress, "$.endAddress");
645
+ if (end < start)
646
+ throw new ToolValidationError("endAddress must be ≥ startAddress", { path: "$.endAddress" });
647
+ const asPrg = parsed.asPrg !== false;
648
+ const len = end - start + 1;
649
+ const resolvedPath = path.resolve(parsed.filePath);
650
+ ctx.logger.info("Saving memory to file", { start: fmtAddr(start), end: fmtAddr(end), filePath: resolvedPath, asPrg });
651
+ const bytes = await ctx.client.readMemoryRaw(start, len);
652
+ const header = asPrg ? Buffer.from([start & 0xff, (start >> 8) & 0xff]) : Buffer.alloc(0);
653
+ await fs.promises.mkdir(path.dirname(resolvedPath), { recursive: true });
654
+ await fs.promises.writeFile(resolvedPath, Buffer.concat([header, Buffer.from(bytes)]));
655
+ const fileSize = bytes.length + header.length;
656
+ return textResult(`Saved ${bytes.length} bytes (${fmtAddr(start)}–${fmtAddr(end)}) to ${resolvedPath}${asPrg ? " (PRG, with load address header)" : ""}.`, { success: true, filePath: resolvedPath, bytesRead: bytes.length, fileSize, asPrg, startAddress: fmtAddr(start), endAddress: fmtAddr(end) });
657
+ }
658
+ catch (error) {
659
+ if (error instanceof ToolError)
660
+ return toolErrorResult(error);
661
+ return unknownErrorResult(error);
662
+ }
663
+ }
347
664
  export const memoryOperationHandlers = {
348
665
  read: async (args, ctx) => executeReadMemory(stripOperationDiscriminator(args), ctx),
349
666
  write: async (args, ctx) => executeWriteMemory(stripOperationDiscriminator(args), ctx),
350
667
  read_screen: async (args, ctx) => executeReadScreen(stripOperationDiscriminator(args), ctx),
668
+ disassemble: async (args, ctx) => executeDisassemble(stripOperationDiscriminator(args), ctx),
669
+ copy_memory: async (args, ctx) => executeCopyMemory(stripOperationDiscriminator(args), ctx),
670
+ fill_memory: async (args, ctx) => executeFillMemory(stripOperationDiscriminator(args), ctx),
671
+ search_memory: async (args, ctx) => executeSearchMemory(stripOperationDiscriminator(args), ctx),
672
+ compare_memory: async (args, ctx) => executeCompareMemory(stripOperationDiscriminator(args), ctx),
673
+ save_memory: async (args, ctx) => executeSaveMemory(stripOperationDiscriminator(args), ctx),
351
674
  };
675
+ export { disassembleArgsSchema, copyMemoryArgsSchema, fillMemoryArgsSchema, searchMemoryArgsSchema, compareMemoryArgsSchema, saveMemoryArgsSchema, };
352
676
  export const memoryModule = defineToolModule({
353
677
  domain: "memory",
354
678
  summary: "Screen, main memory, and low-level inspection utilities.",
355
679
  supportedPlatforms: ["c64u", "vice"],
356
680
  resources: [
357
- "c64://context/bootstrap",
358
- "c64://specs/basic",
359
- "c64://specs/assembly",
681
+ "c64://guide/bootstrap",
682
+ "c64://basic/spec",
683
+ "c64://assembly/6510-spec",
360
684
  ],
361
685
  prompts: ["memory-debug", "basic-program", "assembly-program"],
362
686
  defaultTags: ["memory", "debug"],
@@ -367,10 +691,10 @@ export const memoryModule = defineToolModule({
367
691
  tools: [
368
692
  {
369
693
  name: "read_screen",
370
- description: "Read the current text screen (40x25) and return its ASCII representation. For PETSCII details, see c64://specs/basic.",
694
+ description: "Read the current text screen (40x25) and return its ASCII representation. For PETSCII details, see c64://basic/spec.",
371
695
  summary: "Fetches screen RAM, converts from PETSCII, and returns the printable output.",
372
696
  inputSchema: readScreenArgsSchema.jsonSchema,
373
- relatedResources: ["c64://context/bootstrap", "c64://specs/basic"],
697
+ relatedResources: ["c64://guide/bootstrap", "c64://basic/spec"],
374
698
  relatedPrompts: ["memory-debug", "basic-program", "assembly-program"],
375
699
  tags: ["screen", "memory"],
376
700
  prerequisites: [],
@@ -390,10 +714,10 @@ export const memoryModule = defineToolModule({
390
714
  },
391
715
  {
392
716
  name: "read",
393
- description: "Read a range of bytes from main memory and return the data as hexadecimal. Consult c64://specs/assembly and docs index.",
717
+ description: "Read a range of bytes from main memory and return the data as hexadecimal. Consult c64://assembly/6510-spec and docs index.",
394
718
  summary: "Resolves symbols, reads memory, and returns a hex dump with addressing metadata.",
395
719
  inputSchema: readMemoryArgsSchema.jsonSchema,
396
- relatedResources: ["c64://context/bootstrap", "c64://specs/assembly", "c64://docs/index"],
720
+ relatedResources: ["c64://guide/bootstrap", "c64://assembly/6510-spec", "c64://guide/index"],
397
721
  relatedPrompts: ["memory-debug", "assembly-program"],
398
722
  tags: ["memory", "hex"],
399
723
  prerequisites: [],
@@ -414,10 +738,10 @@ export const memoryModule = defineToolModule({
414
738
  },
415
739
  {
416
740
  name: "write",
417
- description: "Write a hexadecimal byte sequence into main memory at the specified address. See c64://context/bootstrap for safety rules.",
741
+ description: "Write a hexadecimal byte sequence into main memory at the specified address. See c64://guide/bootstrap for safety rules.",
418
742
  summary: "Resolves symbols, validates hex data, and writes bytes to RAM.",
419
743
  inputSchema: writeMemoryArgsSchema.jsonSchema,
420
- relatedResources: ["c64://context/bootstrap", "c64://specs/assembly", "c64://docs/index"],
744
+ relatedResources: ["c64://guide/bootstrap", "c64://assembly/6510-spec", "c64://guide/index"],
421
745
  relatedPrompts: ["memory-debug", "assembly-program"],
422
746
  tags: ["memory", "hex", "write"],
423
747
  prerequisites: ["read"],
@@ -28,7 +28,7 @@ const allTools = [
28
28
  export const metaModule = defineToolModule({
29
29
  domain: "meta",
30
30
  summary: "High-level meta tools that orchestrate multiple MCP actions.",
31
- resources: ["c64://context/bootstrap", "c64://specs/assembly"],
31
+ resources: ["c64://guide/bootstrap", "c64://assembly/6510-spec"],
32
32
  defaultTags: ["meta", "orchestration", "experimental"],
33
33
  supportedPlatforms: ["c64u", "vice"],
34
34
  workflowHints: [
@@ -227,13 +227,12 @@ export const printerModule = defineToolModule({
227
227
  domain: "printer",
228
228
  summary: "Printer workflow helpers for Commodore MPS and Epson FX devices, including prompt templates.",
229
229
  resources: [
230
- "c64://specs/printer",
231
- "c64://docs/printer/guide",
232
- "c64://docs/printer/commodore-text",
233
- "c64://docs/printer/commodore-bitmap",
234
- "c64://docs/printer/epson-text",
235
- "c64://docs/printer/epson-bitmap",
236
- "c64://docs/printer/prompts",
230
+ "c64://printer/spec",
231
+ "c64://printer/commodore/text",
232
+ "c64://printer/commodore/bitmap",
233
+ "c64://printer/epson/text",
234
+ "c64://printer/epson/bitmap",
235
+ "c64://printer/prompt-guide",
237
236
  ],
238
237
  prompts: ["printer-job"],
239
238
  defaultTags: ["printer"],
@@ -244,10 +243,10 @@ export const printerModule = defineToolModule({
244
243
  tools: [
245
244
  {
246
245
  name: "print_text",
247
- description: "Print text on device 4 using Commodore or Epson workflows. See c64://docs/printer/guide.",
246
+ description: "Print text on device 4 using Commodore or Epson workflows. See c64://printer/spec.",
248
247
  summary: "Generates printer BASIC, opens device 4, prints text, and optionally emits a form feed.",
249
248
  inputSchema: printTextArgsSchema.jsonSchema,
250
- relatedResources: ["c64://docs/printer/guide", "c64://specs/printer"],
249
+ relatedResources: ["c64://printer/spec"],
251
250
  relatedPrompts: ["printer-job"],
252
251
  tags: ["text"],
253
252
  prerequisites: ["upload_run_basic"],
@@ -304,7 +303,7 @@ export const printerModule = defineToolModule({
304
303
  description: "Print a Commodore MPS bit-image row using BIM BASIC helpers.",
305
304
  summary: "Generates BASIC that emits Commodore BIM commands for the provided bitmap columns.",
306
305
  inputSchema: commodoreBitmapArgsSchema.jsonSchema,
307
- relatedResources: ["c64://docs/printer/commodore-bitmap"],
306
+ relatedResources: ["c64://printer/commodore/bitmap"],
308
307
  relatedPrompts: ["printer-job"],
309
308
  tags: ["bitmap", "commodore"],
310
309
  prerequisites: ["upload_run_basic"],
@@ -364,7 +363,7 @@ export const printerModule = defineToolModule({
364
363
  description: "Print an Epson FX bit-image row using ESC/P commands.",
365
364
  summary: "Generates BASIC that emits ESC/P graphics commands for Epson printers.",
366
365
  inputSchema: epsonBitmapArgsSchema.jsonSchema,
367
- relatedResources: ["c64://docs/printer/epson-bitmap"],
366
+ relatedResources: ["c64://printer/epson/bitmap"],
368
367
  relatedPrompts: ["printer-job"],
369
368
  tags: ["bitmap", "epson"],
370
369
  prerequisites: ["upload_run_basic"],
@@ -425,7 +424,7 @@ export const printerModule = defineToolModule({
425
424
  description: "Define custom characters on Commodore MPS printers using DLL mode.",
426
425
  summary: "Uploads custom glyph data (11 columns each) for device 4 DLL mode.",
427
426
  inputSchema: commodoreDllArgsSchema.jsonSchema,
428
- relatedResources: ["c64://docs/printer/commodore-bitmap"],
427
+ relatedResources: ["c64://printer/commodore/bitmap"],
429
428
  relatedPrompts: ["printer-job"],
430
429
  tags: ["dll", "commodore"],
431
430
  prerequisites: ["upload_run_basic"],