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.
- package/.github/prompts/steer.prompt.md +55 -0
- package/AGENTS.md +9 -7
- package/README.md +192 -154
- package/data/embeddings_asm.json +1 -1
- package/data/embeddings_basic.json +1 -1
- package/data/embeddings_hardware.json +1 -1
- package/data/embeddings_mixed.json +1 -1
- package/data/embeddings_other.json +1 -1
- package/data/graphics/petscii-style-guide.md +4 -4
- package/data/graphics/sprite-charset-best-practices.md +9 -9
- package/data/vice/vice-binary-monitor-spec.md +938 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/c64Client.js +76 -2
- package/dist/device.js +129 -13
- package/dist/mcp-server.js +1 -1
- package/dist/prompts/registry.js +27 -25
- package/dist/rag/indexer.js +18 -8
- package/dist/rag/knowledgeIndex.js +112 -79
- package/dist/rag/resourceUris.js +93 -0
- package/dist/sidwaveCompiler.js +1 -1
- package/dist/{assemblyConverter.js → tools/assember.js} +10 -1
- package/dist/tools/audio.js +11 -11
- package/dist/{basicConverter.js → tools/basicTokenizer.js} +1 -1
- package/dist/tools/debug.js +159 -6
- package/dist/tools/developer.js +13 -13
- package/dist/tools/disassembler.js +478 -0
- package/dist/tools/graphics.js +10 -10
- package/dist/tools/input.js +338 -0
- package/dist/tools/machineControl.js +5 -5
- package/dist/tools/memory.js +336 -12
- package/dist/tools/meta/index.js +1 -1
- package/dist/tools/printer.js +11 -12
- package/dist/tools/programRunners.js +61 -22
- package/dist/tools/rag.js +9 -9
- package/dist/tools/registry/batch.js +167 -0
- package/dist/tools/registry/config.js +1 -1
- package/dist/tools/registry/disk.js +1 -1
- package/dist/tools/registry/drive.js +1 -1
- package/dist/tools/registry/extract.js +1 -1
- package/dist/tools/registry/graphics.js +102 -2
- package/dist/tools/registry/index.js +4 -0
- package/dist/tools/registry/input.js +2 -0
- package/dist/tools/registry/memory.js +62 -2
- package/dist/tools/registry/platform.js +1 -1
- package/dist/tools/registry/printer.js +1 -1
- package/dist/tools/registry/program.js +1 -1
- package/dist/tools/registry/rag.js +1 -1
- package/dist/tools/registry/sound.js +1 -1
- package/dist/tools/registry/stream.js +1 -1
- package/dist/tools/registry/system.js +1 -1
- package/dist/tools/schema.js +4 -5
- package/dist/tools/storage.js +15 -15
- package/dist/tools/streaming.js +3 -3
- package/dist/tools/symbolRegistry.js +49 -0
- package/dist/tools/types.js +56 -4
- package/dist/tools/vice.js +2 -5
- package/dist/vice/process.js +120 -2
- package/dist/vice/readiness.js +35 -0
- package/dist/vice/smokeOptions.js +44 -0
- package/dist/vice/viceClient.js +5 -1
- package/dist/viceRunner.js +2 -0
- package/doc/plans/prompts/README.md +7 -7
- package/mcp/prompts.json +42 -59
- package/mcp/protocol-examples.json +2 -2
- package/mcp/resources.json +179 -166
- package/mcp/schemas/c64_batch.schema.json +53 -0
- package/mcp/schemas/c64_config.schema.json +525 -283
- package/mcp/schemas/c64_debug.schema.json +763 -304
- package/mcp/schemas/c64_disk.schema.json +499 -243
- package/mcp/schemas/c64_drive.schema.json +165 -90
- package/mcp/schemas/c64_extract.schema.json +533 -274
- package/mcp/schemas/c64_graphics.schema.json +607 -295
- package/mcp/schemas/c64_input.schema.json +136 -0
- package/mcp/schemas/c64_memory.schema.json +637 -128
- package/mcp/schemas/c64_printer.schema.json +393 -196
- package/mcp/schemas/c64_program.schema.json +699 -340
- package/mcp/schemas/c64_rag.schema.json +100 -54
- package/mcp/schemas/c64_select_backend.schema.json +49 -23
- package/mcp/schemas/c64_sound.schema.json +1092 -577
- package/mcp/schemas/c64_stream.schema.json +89 -47
- package/mcp/schemas/c64_system.schema.json +351 -185
- package/mcp/schemas/c64_vice.schema.json +85 -47
- package/mcp/server.json +1 -1
- package/mcp/tools.json +7299 -3346
- package/mcp.json +3 -3
- package/package.json +17 -14
- package/scripts/fetch-external-sources.mjs +1 -2
- package/scripts/run-coverage.mjs +33 -1
- package/scripts/run-tests.mjs +2 -2
- package/scripts/run-tests.ts +275 -52
- package/scripts/update-readme.ts +132 -3
- package/server.json +2 -2
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { assemblyToPrgDetailed, AssemblyError } from "./assember.js";
|
|
3
|
+
import { basicToPrg } from "./basicTokenizer.js";
|
|
3
4
|
import { defineToolModule, OPERATION_DISCRIMINATOR, } from "./types.js";
|
|
4
|
-
import { booleanSchema, objectSchema, stringSchema } from "./schema.js";
|
|
5
|
+
import { booleanSchema, objectSchema, optionalSchema, stringSchema } from "./schema.js";
|
|
5
6
|
import { textResult } from "./responses.js";
|
|
6
7
|
import { ToolExecutionError, ToolError, ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
|
|
7
8
|
import { pollForProgramOutcome } from "./pollValidator.js";
|
|
9
|
+
import { clearViceSymbols, setViceSymbols, parseViceSymbolFile } from "./symbolRegistry.js";
|
|
8
10
|
function extractFailureDetails(details) {
|
|
9
11
|
if (details === undefined || details === null) {
|
|
10
12
|
return undefined;
|
|
@@ -163,6 +165,14 @@ function normalizeRuntimeErrors(errors) {
|
|
|
163
165
|
text: error.raw,
|
|
164
166
|
}));
|
|
165
167
|
}
|
|
168
|
+
export const __programRunnerHelpersForTests = {
|
|
169
|
+
extractFailureDetails,
|
|
170
|
+
toRecord,
|
|
171
|
+
stripOperationDiscriminator,
|
|
172
|
+
parseBasicRuntimeErrors,
|
|
173
|
+
attemptAutoFixBasicProgram,
|
|
174
|
+
normalizeRuntimeErrors,
|
|
175
|
+
};
|
|
166
176
|
function structuredExecutionError(message, data) {
|
|
167
177
|
return {
|
|
168
178
|
content: [
|
|
@@ -221,6 +231,10 @@ const prgFileArgsSchema = objectSchema({
|
|
|
221
231
|
description: prgPathDescription,
|
|
222
232
|
minLength: 1,
|
|
223
233
|
}),
|
|
234
|
+
symbolsFile: optionalSchema(stringSchema({
|
|
235
|
+
description: "Path to a VICE symbol file (.vs) to load alongside the PRG. Symbols are used by the disassemble tool when annotating addresses.",
|
|
236
|
+
minLength: 1,
|
|
237
|
+
})),
|
|
224
238
|
},
|
|
225
239
|
required: ["path"],
|
|
226
240
|
additionalProperties: false,
|
|
@@ -394,7 +408,7 @@ async function executeUploadRunBasic(rawArgs, ctx) {
|
|
|
394
408
|
format: "prg",
|
|
395
409
|
entryAddress,
|
|
396
410
|
prgSize: prg.length,
|
|
397
|
-
resources: ["c64://
|
|
411
|
+
resources: ["c64://basic/spec", "c64://guide/bootstrap"],
|
|
398
412
|
...(screenOutput ? { screen: screenOutput } : {}),
|
|
399
413
|
...(autoFixInfo
|
|
400
414
|
? {
|
|
@@ -424,9 +438,13 @@ async function executeUploadRunAsm(rawArgs, ctx) {
|
|
|
424
438
|
sourceLength: parsed.program.length,
|
|
425
439
|
...(shouldVerify ? { verify: true } : {}),
|
|
426
440
|
});
|
|
427
|
-
// Assemble locally to expose structured metadata
|
|
428
|
-
const
|
|
441
|
+
// Assemble locally to expose structured metadata and capture symbols
|
|
442
|
+
const asmResult = assemblyToPrgDetailed(parsed.program);
|
|
443
|
+
const prg = asmResult.prg;
|
|
429
444
|
const entryAddress = prg.readUInt16LE(0);
|
|
445
|
+
if (ctx.platform.id === "vice") {
|
|
446
|
+
setViceSymbols(asmResult.symbols.entries());
|
|
447
|
+
}
|
|
430
448
|
const result = await ctx.client.uploadAndRunAsm(parsed.program);
|
|
431
449
|
if (!result.success) {
|
|
432
450
|
return toolErrorResult(new ToolExecutionError("C64 firmware reported failure while running assembly program", {
|
|
@@ -460,7 +478,7 @@ async function executeUploadRunAsm(rawArgs, ctx) {
|
|
|
460
478
|
format: "prg",
|
|
461
479
|
entryAddress,
|
|
462
480
|
prgSize: prg.length,
|
|
463
|
-
resources: ["c64://
|
|
481
|
+
resources: ["c64://assembly/6510-spec", "c64://guide/bootstrap"],
|
|
464
482
|
...(shouldVerify && verified ? { verified: true } : {}),
|
|
465
483
|
};
|
|
466
484
|
const base = textResult("Assembly program assembled, uploaded, and executed successfully.", {
|
|
@@ -506,7 +524,7 @@ async function executeLoadPrg(rawArgs, ctx) {
|
|
|
506
524
|
format: "prg",
|
|
507
525
|
path: parsed.path,
|
|
508
526
|
entryAddress: null,
|
|
509
|
-
resources: ["c64://
|
|
527
|
+
resources: ["c64://guide/bootstrap"],
|
|
510
528
|
};
|
|
511
529
|
const base = textResult(`PRG ${parsed.path} loaded into memory.`, {
|
|
512
530
|
success: true,
|
|
@@ -527,6 +545,24 @@ async function executeRunPrg(rawArgs, ctx) {
|
|
|
527
545
|
try {
|
|
528
546
|
const parsed = prgFileArgsSchema.parse(rawArgs ?? {});
|
|
529
547
|
ctx.logger.info("Running PRG file", { path: parsed.path });
|
|
548
|
+
let symbolsLoaded = 0;
|
|
549
|
+
if (ctx.platform.id === "vice") {
|
|
550
|
+
clearViceSymbols();
|
|
551
|
+
if (parsed.symbolsFile) {
|
|
552
|
+
try {
|
|
553
|
+
const content = await fs.promises.readFile(parsed.symbolsFile, "utf8");
|
|
554
|
+
const syms = parseViceSymbolFile(content);
|
|
555
|
+
setViceSymbols(syms.entries());
|
|
556
|
+
symbolsLoaded = syms.size;
|
|
557
|
+
ctx.logger.info("Loaded VICE symbols from file", { path: parsed.symbolsFile, count: symbolsLoaded });
|
|
558
|
+
}
|
|
559
|
+
catch (fileError) {
|
|
560
|
+
throw new ToolExecutionError(`Failed to read symbols file: ${parsed.symbolsFile}`, {
|
|
561
|
+
details: { message: fileError instanceof Error ? fileError.message : String(fileError) },
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
530
566
|
const result = await ctx.client.runPrgFile(parsed.path);
|
|
531
567
|
if (!result.success) {
|
|
532
568
|
throw new ToolExecutionError("C64 firmware reported failure while running PRG", {
|
|
@@ -538,13 +574,16 @@ async function executeRunPrg(rawArgs, ctx) {
|
|
|
538
574
|
format: "prg",
|
|
539
575
|
path: parsed.path,
|
|
540
576
|
entryAddress: null,
|
|
541
|
-
resources: ["c64://
|
|
577
|
+
resources: ["c64://guide/bootstrap"],
|
|
578
|
+
...(symbolsLoaded > 0 ? { symbolsLoaded } : {}),
|
|
542
579
|
};
|
|
543
|
-
const
|
|
580
|
+
const symbolNote = symbolsLoaded > 0 ? ` Loaded ${symbolsLoaded} debug symbol(s).` : "";
|
|
581
|
+
const base = textResult(`PRG ${parsed.path} loaded and executed.${symbolNote}`, {
|
|
544
582
|
success: true,
|
|
545
583
|
path: parsed.path,
|
|
546
584
|
entryAddress: null,
|
|
547
585
|
details: toRecord(result.details) ?? null,
|
|
586
|
+
...(symbolsLoaded > 0 ? { symbolsLoaded } : {}),
|
|
548
587
|
});
|
|
549
588
|
return { ...base, structuredContent: { type: "json", data } };
|
|
550
589
|
}
|
|
@@ -570,7 +609,7 @@ async function executeRunCrt(rawArgs, ctx) {
|
|
|
570
609
|
format: "crt",
|
|
571
610
|
path: parsed.path,
|
|
572
611
|
entryAddress: null,
|
|
573
|
-
resources: ["c64://
|
|
612
|
+
resources: ["c64://guide/bootstrap"],
|
|
574
613
|
};
|
|
575
614
|
const base = textResult(`PRG ${parsed.path} loaded and executed.`, {
|
|
576
615
|
success: true,
|
|
@@ -598,9 +637,9 @@ export const programRunnersModule = defineToolModule({
|
|
|
598
637
|
domain: "programs",
|
|
599
638
|
summary: "Program uploaders, runners, and compilation workflows for BASIC, assembly, and PRG files.",
|
|
600
639
|
resources: [
|
|
601
|
-
"c64://
|
|
602
|
-
"c64://
|
|
603
|
-
"c64://
|
|
640
|
+
"c64://guide/bootstrap",
|
|
641
|
+
"c64://basic/spec",
|
|
642
|
+
"c64://assembly/6510-spec",
|
|
604
643
|
],
|
|
605
644
|
prompts: ["basic-program", "assembly-program"],
|
|
606
645
|
defaultTags: ["programs", "execution"],
|
|
@@ -611,10 +650,10 @@ export const programRunnersModule = defineToolModule({
|
|
|
611
650
|
tools: [
|
|
612
651
|
{
|
|
613
652
|
name: "upload_run_basic",
|
|
614
|
-
description: "Upload a BASIC program to the C64 and execute it immediately. Refer to c64://
|
|
653
|
+
description: "Upload a BASIC program to the C64 and execute it immediately. Refer to c64://basic/spec for syntax and device I/O.",
|
|
615
654
|
summary: "Uploads Commodore BASIC v2 source and runs it via Ultimate 64 firmware.",
|
|
616
655
|
inputSchema: uploadBasicArgsSchema.jsonSchema,
|
|
617
|
-
relatedResources: ["c64://
|
|
656
|
+
relatedResources: ["c64://basic/spec", "c64://basic/pitfalls", "c64://guide/bootstrap"],
|
|
618
657
|
relatedPrompts: ["basic-program"],
|
|
619
658
|
tags: ["basic", "execution"],
|
|
620
659
|
prerequisites: ["read_screen"],
|
|
@@ -636,10 +675,10 @@ export const programRunnersModule = defineToolModule({
|
|
|
636
675
|
},
|
|
637
676
|
{
|
|
638
677
|
name: "upload_run_asm",
|
|
639
|
-
description: "Assemble 6502/6510 source code, upload the PRG, and run it immediately. See c64://
|
|
678
|
+
description: "Assemble 6502/6510 source code, upload the PRG, and run it immediately. See c64://assembly/6510-spec.",
|
|
640
679
|
summary: "Compiles assembly to a PRG and executes it on the C64 via Ultimate 64 firmware.",
|
|
641
680
|
inputSchema: uploadAsmArgsSchema.jsonSchema,
|
|
642
|
-
relatedResources: ["c64://
|
|
681
|
+
relatedResources: ["c64://assembly/6510-spec", "c64://guide/bootstrap"],
|
|
643
682
|
relatedPrompts: ["assembly-program"],
|
|
644
683
|
tags: ["assembly", "execution"],
|
|
645
684
|
prerequisites: ["read_screen"],
|
|
@@ -664,7 +703,7 @@ export const programRunnersModule = defineToolModule({
|
|
|
664
703
|
description: "Load a PRG into C64 memory without executing it.",
|
|
665
704
|
summary: "Instructs the Ultimate firmware to transfer a PRG into memory without RUN.",
|
|
666
705
|
inputSchema: prgFileArgsSchema.jsonSchema,
|
|
667
|
-
relatedResources: ["c64://
|
|
706
|
+
relatedResources: ["c64://guide/bootstrap"],
|
|
668
707
|
tags: ["programs", "file"],
|
|
669
708
|
prerequisites: ["drives_list"],
|
|
670
709
|
examples: [
|
|
@@ -688,7 +727,7 @@ export const programRunnersModule = defineToolModule({
|
|
|
688
727
|
description: "Run a PRG located on the Ultimate filesystem without uploading source.",
|
|
689
728
|
summary: "Loads and executes a PRG file residing on attached storage.",
|
|
690
729
|
inputSchema: prgFileArgsSchema.jsonSchema,
|
|
691
|
-
relatedResources: ["c64://
|
|
730
|
+
relatedResources: ["c64://guide/bootstrap"],
|
|
692
731
|
tags: ["programs", "execution", "file"],
|
|
693
732
|
workflowHints: [
|
|
694
733
|
"Call when the user provides a PRG path and expects immediate execution without compiling.",
|
|
@@ -712,7 +751,7 @@ export const programRunnersModule = defineToolModule({
|
|
|
712
751
|
description: "Run a cartridge image stored on the Ultimate filesystem.",
|
|
713
752
|
summary: "Mounts and autostarts the specified CRT file through the firmware.",
|
|
714
753
|
inputSchema: crtFileArgsSchema.jsonSchema,
|
|
715
|
-
relatedResources: ["c64://
|
|
754
|
+
relatedResources: ["c64://guide/bootstrap"],
|
|
716
755
|
tags: ["programs", "cartridge"],
|
|
717
756
|
workflowHints: [
|
|
718
757
|
"Use for cartridge images and remind the user that the machine will reset into the CRT.",
|
|
@@ -742,7 +781,7 @@ export const programRunnersModule = defineToolModule({
|
|
|
742
781
|
format: "crt",
|
|
743
782
|
path: parsed.path,
|
|
744
783
|
entryAddress: null,
|
|
745
|
-
resources: ["c64://
|
|
784
|
+
resources: ["c64://guide/bootstrap"],
|
|
746
785
|
};
|
|
747
786
|
const base = textResult(`CRT ${parsed.path} mounted and started.`, {
|
|
748
787
|
success: true,
|
package/dist/tools/rag.js
CHANGED
|
@@ -23,18 +23,18 @@ const ragRetrieveArgsSchema = objectSchema({
|
|
|
23
23
|
});
|
|
24
24
|
function createRagTool(language, options) {
|
|
25
25
|
const primaryResourceUris = language === "basic"
|
|
26
|
-
? ["c64://
|
|
27
|
-
: ["c64://
|
|
26
|
+
? ["c64://basic/spec", "c64://guide/bootstrap"]
|
|
27
|
+
: ["c64://assembly/6510-spec", "c64://guide/bootstrap"];
|
|
28
28
|
return {
|
|
29
29
|
name: language === "basic" ? "rag_retrieve_basic" : "rag_retrieve_asm",
|
|
30
30
|
description: language === "basic"
|
|
31
|
-
? "Retrieve BASIC references from local knowledge. See c64://
|
|
32
|
-
: "Retrieve 6502/6510 assembly references from local knowledge. See c64://
|
|
31
|
+
? "Retrieve BASIC references from local knowledge. See c64://basic/spec before coding."
|
|
32
|
+
: "Retrieve 6502/6510 assembly references from local knowledge. See c64://assembly/6510-spec.",
|
|
33
33
|
summary: options.summary,
|
|
34
34
|
inputSchema: ragRetrieveArgsSchema.jsonSchema,
|
|
35
35
|
relatedResources: language === "basic"
|
|
36
|
-
? ["c64://
|
|
37
|
-
: ["c64://
|
|
36
|
+
? ["c64://basic/spec"]
|
|
37
|
+
: ["c64://assembly/6510-spec"],
|
|
38
38
|
relatedPrompts: language === "basic"
|
|
39
39
|
? ["basic-program"]
|
|
40
40
|
: ["assembly-program"],
|
|
@@ -175,9 +175,9 @@ export const ragModule = defineToolModule({
|
|
|
175
175
|
domain: "rag",
|
|
176
176
|
summary: "Retrieval-augmented generation helpers for BASIC and assembly examples.",
|
|
177
177
|
resources: [
|
|
178
|
-
"c64://
|
|
179
|
-
"c64://
|
|
180
|
-
"c64://
|
|
178
|
+
"c64://basic/spec",
|
|
179
|
+
"c64://assembly/6510-spec",
|
|
180
|
+
"c64://guide/index",
|
|
181
181
|
],
|
|
182
182
|
prompts: ["basic-program", "assembly-program"],
|
|
183
183
|
defaultTags: ["rag", "search"],
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { defineToolModule, } from "../types.js";
|
|
2
|
+
import { getPlatformStatus } from "../../platform.js";
|
|
3
|
+
import { arraySchema, booleanSchema, objectSchema, optionalSchema, stringSchema, } from "../schema.js";
|
|
4
|
+
import { jsonResult } from "../responses.js";
|
|
5
|
+
import { ToolValidationError, unknownErrorResult } from "../errors.js";
|
|
6
|
+
const commandSchema = objectSchema({
|
|
7
|
+
description: "A single batch command.",
|
|
8
|
+
properties: {
|
|
9
|
+
tool: stringSchema({ description: "MCP tool name (e.g. c64_memory, c64_debug).", minLength: 1 }),
|
|
10
|
+
args: objectSchema({
|
|
11
|
+
description: "Arguments to pass to the tool (must include 'op' for grouped tools).",
|
|
12
|
+
properties: {},
|
|
13
|
+
additionalProperties: true,
|
|
14
|
+
}),
|
|
15
|
+
description: optionalSchema(stringSchema({ description: "Optional label for this command in the result.", maxLength: 128 })),
|
|
16
|
+
},
|
|
17
|
+
required: ["tool", "args"],
|
|
18
|
+
additionalProperties: false,
|
|
19
|
+
});
|
|
20
|
+
const executeBatchArgsSchema = objectSchema({
|
|
21
|
+
description: "Execute multiple tool calls in one request.",
|
|
22
|
+
properties: {
|
|
23
|
+
commands: arraySchema(commandSchema, {
|
|
24
|
+
description: "Ordered list of commands to execute.",
|
|
25
|
+
minItems: 1,
|
|
26
|
+
}),
|
|
27
|
+
stopOnError: optionalSchema(booleanSchema({
|
|
28
|
+
description: "Halt the batch after the first failure. Default: true.",
|
|
29
|
+
default: true,
|
|
30
|
+
})),
|
|
31
|
+
},
|
|
32
|
+
required: ["commands"],
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
});
|
|
35
|
+
export const batchModuleGroup = defineToolModule({
|
|
36
|
+
domain: "batch",
|
|
37
|
+
summary: "Execute multiple tool calls in a single MCP request for reduced round-trip overhead.",
|
|
38
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
39
|
+
resources: [],
|
|
40
|
+
prompts: [],
|
|
41
|
+
defaultTags: ["batch", "orchestration"],
|
|
42
|
+
workflowHints: [
|
|
43
|
+
"Use execute_batch when you need to run 3 or more sequential operations — it eliminates per-call network overhead.",
|
|
44
|
+
"Each command must include 'tool' and 'args'; grouped tools require 'op' inside args.",
|
|
45
|
+
],
|
|
46
|
+
tools: [
|
|
47
|
+
{
|
|
48
|
+
name: "c64_batch",
|
|
49
|
+
description: "Execute multiple c64bridge tool calls in a single request. Reduces latency for multi-step workflows.",
|
|
50
|
+
summary: "Runs an ordered list of tool commands and returns all results with per-command timing.",
|
|
51
|
+
inputSchema: executeBatchArgsSchema.jsonSchema,
|
|
52
|
+
tags: ["batch", "orchestration", "grouped"],
|
|
53
|
+
examples: [
|
|
54
|
+
{
|
|
55
|
+
name: "Read registers then step",
|
|
56
|
+
description: "Get registers, step one instruction, get registers again",
|
|
57
|
+
arguments: {
|
|
58
|
+
commands: [
|
|
59
|
+
{ tool: "c64_debug", args: { op: "get_registers" }, description: "before" },
|
|
60
|
+
{ tool: "c64_debug", args: { op: "step", count: 1 }, description: "step" },
|
|
61
|
+
{ tool: "c64_debug", args: { op: "get_registers" }, description: "after" },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "Clear screen and type text",
|
|
67
|
+
description: "Fill screen RAM then feed keyboard",
|
|
68
|
+
arguments: {
|
|
69
|
+
commands: [
|
|
70
|
+
{ tool: "c64_memory", args: { op: "fill_memory", address: "$0400", length: 1000, pattern: "20" } },
|
|
71
|
+
{ tool: "c64_input", args: { op: "write_text", text: "HELLO{RETURN}" } },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
execute: async (rawArgs, ctx) => {
|
|
77
|
+
try {
|
|
78
|
+
const parsed = executeBatchArgsSchema.parse(rawArgs);
|
|
79
|
+
const stopOnError = parsed.stopOnError !== false;
|
|
80
|
+
// Lazy import to avoid circular dependency at module-init time.
|
|
81
|
+
const { toolRegistry } = await import("./index.js");
|
|
82
|
+
const results = [];
|
|
83
|
+
let succeeded = 0;
|
|
84
|
+
let failed = 0;
|
|
85
|
+
const batchStart = Date.now();
|
|
86
|
+
for (let i = 0; i < parsed.commands.length; i++) {
|
|
87
|
+
const cmd = parsed.commands[i];
|
|
88
|
+
const toolName = typeof cmd.tool === "string" ? cmd.tool : "";
|
|
89
|
+
const args = (typeof cmd.args === "object" && cmd.args !== null) ? cmd.args : {};
|
|
90
|
+
const label = typeof cmd.description === "string" ? cmd.description : undefined;
|
|
91
|
+
if (!toolName) {
|
|
92
|
+
const err = { index: i, tool: String(cmd.tool), description: label, success: false, error: "tool name must be a non-empty string", elapsedMs: 0 };
|
|
93
|
+
results.push(err);
|
|
94
|
+
failed++;
|
|
95
|
+
if (stopOnError)
|
|
96
|
+
break;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (toolName === "c64_batch") {
|
|
100
|
+
results.push({
|
|
101
|
+
index: i,
|
|
102
|
+
tool: toolName,
|
|
103
|
+
description: label,
|
|
104
|
+
success: false,
|
|
105
|
+
error: "Nested c64_batch execution is not supported.",
|
|
106
|
+
elapsedMs: 0,
|
|
107
|
+
});
|
|
108
|
+
failed++;
|
|
109
|
+
if (stopOnError)
|
|
110
|
+
break;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const cmdStart = Date.now();
|
|
114
|
+
try {
|
|
115
|
+
const toolResult = await toolRegistry.invoke(toolName, args, {
|
|
116
|
+
...ctx,
|
|
117
|
+
platform: getPlatformStatus(),
|
|
118
|
+
});
|
|
119
|
+
const elapsedMs = Date.now() - cmdStart;
|
|
120
|
+
const isError = toolResult.isError === true;
|
|
121
|
+
const text = toolResult.content.map((c) => c.text).join("\n");
|
|
122
|
+
const resultPayload = toolResult.structuredContent?.data ?? text;
|
|
123
|
+
if (isError) {
|
|
124
|
+
results.push({ index: i, tool: toolName, description: label, success: false, error: text, elapsedMs });
|
|
125
|
+
failed++;
|
|
126
|
+
if (stopOnError)
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
results.push({ index: i, tool: toolName, description: label, success: true, result: resultPayload, elapsedMs });
|
|
131
|
+
succeeded++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
const elapsedMs = Date.now() - cmdStart;
|
|
136
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
137
|
+
results.push({ index: i, tool: toolName, description: label, success: false, error: message, elapsedMs });
|
|
138
|
+
failed++;
|
|
139
|
+
if (stopOnError)
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const totalElapsedMs = Date.now() - batchStart;
|
|
144
|
+
const summary = {
|
|
145
|
+
total: parsed.commands.length,
|
|
146
|
+
executed: results.length,
|
|
147
|
+
succeeded,
|
|
148
|
+
failed,
|
|
149
|
+
elapsedMs: totalElapsedMs,
|
|
150
|
+
results,
|
|
151
|
+
};
|
|
152
|
+
ctx.logger.info("Batch complete", { total: parsed.commands.length, succeeded, failed, elapsedMs: totalElapsedMs });
|
|
153
|
+
return jsonResult(summary, { success: failed === 0, succeeded, failed, total: parsed.commands.length });
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (error instanceof ToolValidationError) {
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: error.message }],
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return unknownErrorResult(error);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
});
|
|
@@ -151,7 +151,7 @@ const configOperationHandlers = createOperationHandlers(configOperations);
|
|
|
151
151
|
export const configModuleGroup = defineToolModule({
|
|
152
152
|
domain: "config",
|
|
153
153
|
summary: "Grouped configuration management, diagnostics, and snapshot workflows.",
|
|
154
|
-
resources: ["c64://
|
|
154
|
+
resources: ["c64://guide/bootstrap", "c64://guide/index"],
|
|
155
155
|
prompts: ["memory-debug"],
|
|
156
156
|
defaultTags: ["config", "diagnostics"],
|
|
157
157
|
supportedPlatforms: ["c64u", "vice"],
|
|
@@ -252,7 +252,7 @@ export const diskModuleGroup = defineToolModule({
|
|
|
252
252
|
domain: "storage",
|
|
253
253
|
summary: "Grouped disk image management, mounting, and discovery tools.",
|
|
254
254
|
supportedPlatforms: ["c64u", "vice"],
|
|
255
|
-
resources: ["c64://
|
|
255
|
+
resources: ["c64://guide/bootstrap"],
|
|
256
256
|
prompts: ["drive-management"],
|
|
257
257
|
defaultTags: ["storage", "drive"],
|
|
258
258
|
workflowHints: [
|
|
@@ -34,7 +34,7 @@ export const driveModuleGroup = defineToolModule({
|
|
|
34
34
|
domain: "drive",
|
|
35
35
|
summary: "Grouped drive power, reset, ROM, and mode helpers.",
|
|
36
36
|
supportedPlatforms: ["c64u", "vice"],
|
|
37
|
-
resources: ["c64://
|
|
37
|
+
resources: ["c64://guide/bootstrap"],
|
|
38
38
|
defaultTags: ["drive", "hardware"],
|
|
39
39
|
workflowHints: [
|
|
40
40
|
"State the resulting power/mode/ROM so the user can reconcile IEC behaviour.",
|
|
@@ -33,7 +33,7 @@ const extractOperationHandlers = createOperationHandlers(extractOperations);
|
|
|
33
33
|
export const extractModule = defineToolModule({
|
|
34
34
|
domain: "extract",
|
|
35
35
|
summary: "Grouped extraction helpers for sprites, charsets, memory dumps, and diagnostics.",
|
|
36
|
-
resources: ["c64://
|
|
36
|
+
resources: ["c64://guide/bootstrap", "c64://basic/spec", "c64://assembly/6510-spec"],
|
|
37
37
|
defaultTags: ["extract", "diagnostics"],
|
|
38
38
|
workflowHints: [
|
|
39
39
|
"Pause the machine when advised so dumps and sprite scans remain stable.",
|
|
@@ -3,8 +3,8 @@ import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema,
|
|
|
3
3
|
import { graphicsModule, graphicsOperationHandlers as groupedGraphicsHandlers } from "../graphics.js";
|
|
4
4
|
import { booleanSchema, literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "../schema.js";
|
|
5
5
|
import { buildDescriptorIndex, ensureDescriptor, extendSchemaWithOp, createOperationHandlers, } from "./utils.js";
|
|
6
|
-
import { ToolError, toolErrorResult, unknownErrorResult, } from "../errors.js";
|
|
7
6
|
import { jsonResult } from "../responses.js";
|
|
7
|
+
import { ToolError, toolErrorResult, unknownErrorResult, } from "../errors.js";
|
|
8
8
|
const graphicsDescriptorIndex = buildDescriptorIndex(graphicsModule);
|
|
9
9
|
const captureFrameArgsSchema = objectSchema({
|
|
10
10
|
description: "Capture one or more complete video frames from the active backend.",
|
|
@@ -30,6 +30,72 @@ const captureFrameArgsSchema = objectSchema({
|
|
|
30
30
|
required: ["op"],
|
|
31
31
|
additionalProperties: false,
|
|
32
32
|
});
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// get_display_state helpers — VIC-II register decoding
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
function parseVicState(d011, d016, d018, dd00, d020, d021) {
|
|
37
|
+
const bank = (~dd00) & 0x03; // CIA2 Port A bits 0-1 select VIC bank (inverted)
|
|
38
|
+
const bankBase = bank * 0x4000;
|
|
39
|
+
const bitmapMode = (d011 & 0x20) !== 0;
|
|
40
|
+
const extendedColor = (d011 & 0x40) !== 0;
|
|
41
|
+
const multicolor = (d016 & 0x10) !== 0;
|
|
42
|
+
const screenVisible = (d011 & 0x10) !== 0;
|
|
43
|
+
const raster8 = (d011 & 0x80) !== 0;
|
|
44
|
+
const vertScroll = d011 & 0x07;
|
|
45
|
+
const horizScroll = d016 & 0x07;
|
|
46
|
+
const columns40 = (d016 & 0x08) !== 0;
|
|
47
|
+
const rows25 = (d011 & 0x08) !== 0;
|
|
48
|
+
// D018 bits 4-7 = screen RAM offset (× $0400); bits 1-3 = char/bitmap pointer (× $0800)
|
|
49
|
+
const screenRamOffset = ((d018 >> 4) & 0x0f) * 0x0400;
|
|
50
|
+
const screenRamAddress = bankBase + screenRamOffset;
|
|
51
|
+
const charOrBitmapPointer = ((d018 >> 1) & 0x07) * 0x0800;
|
|
52
|
+
const charOrBitmapAddress = bankBase + charOrBitmapPointer;
|
|
53
|
+
let mode;
|
|
54
|
+
if (bitmapMode && multicolor)
|
|
55
|
+
mode = "multicolor_bitmap";
|
|
56
|
+
else if (bitmapMode)
|
|
57
|
+
mode = "hires_bitmap";
|
|
58
|
+
else if (extendedColor)
|
|
59
|
+
mode = "extended_color_text";
|
|
60
|
+
else if (multicolor)
|
|
61
|
+
mode = "multicolor_text";
|
|
62
|
+
else
|
|
63
|
+
mode = "standard_text";
|
|
64
|
+
return {
|
|
65
|
+
mode,
|
|
66
|
+
bank,
|
|
67
|
+
bankBase: `$${bankBase.toString(16).toUpperCase().padStart(4, "0")}`,
|
|
68
|
+
screenRamAddress: `$${screenRamAddress.toString(16).toUpperCase().padStart(4, "0")}`,
|
|
69
|
+
charOrBitmapAddress: `$${charOrBitmapAddress.toString(16).toUpperCase().padStart(4, "0")}`,
|
|
70
|
+
screenVisible,
|
|
71
|
+
bitmapMode,
|
|
72
|
+
extendedColor,
|
|
73
|
+
multicolor,
|
|
74
|
+
rows25,
|
|
75
|
+
columns40,
|
|
76
|
+
vertScroll,
|
|
77
|
+
horizScroll,
|
|
78
|
+
raster8,
|
|
79
|
+
borderColor: d020 & 0x0f,
|
|
80
|
+
backgroundColor: d021 & 0x0f,
|
|
81
|
+
registers: {
|
|
82
|
+
d011: `$${d011.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
83
|
+
d016: `$${d016.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
84
|
+
d018: `$${d018.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
85
|
+
d020: `$${d020.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
86
|
+
d021: `$${d021.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
87
|
+
dd00: `$${dd00.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const getDisplayStateArgsSchema = objectSchema({
|
|
92
|
+
description: "Read VIC-II and CIA2 registers to determine the current graphics mode and memory layout. Works on C64U and VICE; both backends are read through shared memory primitives so the response shape is identical.",
|
|
93
|
+
properties: {
|
|
94
|
+
op: literalSchema("get_display_state"),
|
|
95
|
+
},
|
|
96
|
+
required: ["op"],
|
|
97
|
+
additionalProperties: false,
|
|
98
|
+
});
|
|
33
99
|
const graphicsOperations = [
|
|
34
100
|
{
|
|
35
101
|
op: "capture_frame",
|
|
@@ -105,12 +171,41 @@ const graphicsOperations = [
|
|
|
105
171
|
schema: extendSchemaWithOp("render_bitmap", ensureDescriptor(graphicsDescriptorIndex, "render_bitmap").inputSchema, { description: "Import an image file, convert it to VIC-II bitmap memory, write it into RAM, and display it." }),
|
|
106
172
|
handler: groupedGraphicsHandlers.render_bitmap,
|
|
107
173
|
},
|
|
174
|
+
{
|
|
175
|
+
op: "get_display_state",
|
|
176
|
+
schema: getDisplayStateArgsSchema.jsonSchema,
|
|
177
|
+
handler: async (_rawArgs, ctx) => {
|
|
178
|
+
try {
|
|
179
|
+
// Use cross-platform memory reads so the same handler works on both
|
|
180
|
+
// C64U and VICE. The VIC-II and CIA2 registers live in machine memory
|
|
181
|
+
// either way; the underlying facade hides the transport difference.
|
|
182
|
+
const vicRegs = await ctx.client.readMemoryRaw(0xD011, 8);
|
|
183
|
+
const colorRegs = await ctx.client.readMemoryRaw(0xD020, 2);
|
|
184
|
+
const cia2 = await ctx.client.readMemoryRaw(0xDD00, 1);
|
|
185
|
+
const d011 = vicRegs[0] ?? 0;
|
|
186
|
+
const d016 = vicRegs[5] ?? 0; // $D016 = $D011 + 5
|
|
187
|
+
const d018 = vicRegs[7] ?? 0; // $D018 = $D011 + 7
|
|
188
|
+
const d020 = colorRegs[0] ?? 0;
|
|
189
|
+
const d021 = colorRegs[1] ?? 0;
|
|
190
|
+
const dd00 = cia2[0] ?? 0x03;
|
|
191
|
+
const backend = await ctx.client.getActiveBackendType();
|
|
192
|
+
const state = { ...parseVicState(d011, d016, d018, dd00, d020, d021), backend };
|
|
193
|
+
ctx.logger.info("Read VIC-II display state", { mode: state.mode, bank: state.bank, backend });
|
|
194
|
+
return jsonResult(state, { success: true, mode: state.mode, backend });
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (error instanceof ToolError)
|
|
198
|
+
return toolErrorResult(error);
|
|
199
|
+
return unknownErrorResult(error);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
},
|
|
108
203
|
];
|
|
109
204
|
const graphicsOperationHandlers = createOperationHandlers(graphicsOperations);
|
|
110
205
|
export const graphicsModuleGroup = defineToolModule({
|
|
111
206
|
domain: "graphics",
|
|
112
207
|
summary: "Grouped frame capture and graphics rendering helpers.",
|
|
113
|
-
resources: ["c64://
|
|
208
|
+
resources: ["c64://graphics/vic/spec", "c64://basic/spec", "c64://assembly/6510-spec"],
|
|
114
209
|
prompts: ["graphics-demo", "basic-program", "assembly-program"],
|
|
115
210
|
defaultTags: ["graphics", "vic"],
|
|
116
211
|
workflowHints: [
|
|
@@ -154,6 +249,11 @@ export const graphicsModuleGroup = defineToolModule({
|
|
|
154
249
|
description: "Convert a PNG into VIC bitmap memory and display it",
|
|
155
250
|
arguments: { op: "render_bitmap", imagePath: "./artifacts/sample.png", format: "hires" },
|
|
156
251
|
},
|
|
252
|
+
{
|
|
253
|
+
name: "Read VIC-II state",
|
|
254
|
+
description: "Decode graphics mode, memory layout, and colours from VIC-II registers (cross-platform)",
|
|
255
|
+
arguments: { op: "get_display_state" },
|
|
256
|
+
},
|
|
157
257
|
],
|
|
158
258
|
execute: createOperationDispatcher("c64_graphics", graphicsOperationHandlers),
|
|
159
259
|
},
|
|
@@ -14,6 +14,8 @@ import { extractModule } from "./extract.js";
|
|
|
14
14
|
import { streamModule } from "./stream.js";
|
|
15
15
|
import { debugModuleGroup as debugModule } from "../debug.js";
|
|
16
16
|
import { viceModuleGroup as viceModule } from "../vice.js";
|
|
17
|
+
import { inputModuleGroup as inputModule } from "./input.js";
|
|
18
|
+
import { batchModuleGroup as batchModule } from "./batch.js";
|
|
17
19
|
const modules = [
|
|
18
20
|
programModule,
|
|
19
21
|
memoryModule,
|
|
@@ -30,6 +32,8 @@ const modules = [
|
|
|
30
32
|
streamModule,
|
|
31
33
|
debugModule,
|
|
32
34
|
viceModule,
|
|
35
|
+
inputModule,
|
|
36
|
+
batchModule,
|
|
33
37
|
];
|
|
34
38
|
const toolMap = new Map();
|
|
35
39
|
for (const module of modules) {
|