c64bridge 0.7.4 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/prompts/README.md +14 -4
- package/.github/prompts/assembly-routine-session.prompt.md +9 -9
- package/.github/prompts/basic-program-session.prompt.md +11 -9
- package/.github/prompts/drive-management-session.prompt.md +9 -9
- package/.github/prompts/hello-world-session.prompt.md +17 -0
- package/.github/prompts/petscii-art-session.prompt.md +9 -9
- package/.github/prompts/play-classic-game.prompt.md +9 -9
- package/.github/prompts/pr-converge.prompt.md +13 -0
- package/.github/prompts/sid-music-session.prompt.md +9 -9
- package/AGENTS.md +59 -60
- package/README.md +550 -191
- package/data/assembly/assembly-spec.md +290 -1
- package/data/context/bootstrap.md +26 -2
- package/data/context/fast-paths.md +44 -0
- package/data/graphics/petscii-style-guide.md +6 -2
- package/data/graphics/sprite-charset-best-practices.md +5 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/c64Client.js +990 -26
- package/dist/config.js +5 -1
- package/dist/device.js +680 -57
- package/dist/diagnostics.js +371 -0
- package/dist/mcp/metadata.js +39 -0
- package/dist/mcp-server.js +170 -20
- package/dist/platform.js +10 -2
- package/dist/prompts/registry.js +166 -309
- package/dist/rag/init.js +37 -1
- package/dist/rag/knowledgeIndex.js +18 -6
- package/dist/rag/rateLimiter.js +2 -2
- package/dist/streamCapture.js +135 -0
- package/dist/tools/audio.js +3 -1
- package/dist/tools/debug.js +470 -0
- package/dist/tools/developer.js +10 -4
- package/dist/tools/graphics.js +183 -17
- package/dist/tools/machineControl.js +11 -5
- package/dist/tools/memory.js +19 -10
- package/dist/tools/meta/audio.js +240 -1
- package/dist/tools/meta/config.js +20 -28
- package/dist/tools/meta/configInventory.js +104 -0
- package/dist/tools/meta/diagnostics.js +45 -2
- package/dist/tools/meta/graphics.js +14 -6
- package/dist/tools/meta/index.js +1 -0
- package/dist/tools/meta/memory.js +20 -6
- package/dist/tools/meta/program.js +473 -0
- package/dist/tools/pollValidator.js +4 -2
- package/dist/tools/programRunners.js +8 -4
- package/dist/tools/registry/config.js +17 -0
- package/dist/tools/registry/disk.js +7 -0
- package/dist/tools/registry/drive.js +3 -0
- package/dist/tools/registry/graphics.js +111 -27
- package/dist/tools/registry/index.js +6 -0
- package/dist/tools/registry/memory.js +1 -0
- package/dist/tools/registry/platform.js +121 -0
- package/dist/tools/registry/program.js +26 -8
- package/dist/tools/registry/sound.js +96 -0
- package/dist/tools/registry/system.js +15 -2
- package/dist/tools/storage.js +1 -0
- package/dist/tools/streaming.js +1 -0
- package/dist/tools/types.js +38 -4
- package/dist/tools/vice.js +160 -0
- package/dist/vicBitmap.js +317 -0
- package/dist/vice/mockServer.js +439 -0
- package/dist/vice/process.js +310 -0
- package/dist/vice/readiness.js +18 -2
- package/dist/vice/viceClient.js +382 -22
- package/doc/developer.md +71 -25
- package/doc/img/backend-switch/hello-c64u.png +0 -0
- package/doc/img/backend-switch/hello-vice-and-c64u.png +0 -0
- package/doc/img/backend-switch/hello-vice.png +0 -0
- package/doc/img/backend-switch/metadata.json +50 -0
- package/doc/plans/vice-backend-switch/plan.md +486 -0
- package/doc/plans/vice-backend-switch-2/plan.md +48 -0
- package/doc/plans/vice-support/notes.md +1 -0
- package/doc/plans/vice-support/plan.md +5 -0
- package/doc/plans/vice-support/rollout.md +23 -17
- package/doc/reviews/vice-support-review.md +185 -0
- package/doc/troubleshooting-mcp.md +35 -6
- package/doc/vice/support-matrix.md +54 -0
- package/mcp/prompts.json +292 -0
- package/mcp/protocol-examples.json +46 -0
- package/mcp/resources.json +940 -0
- package/mcp/schemas/c64_config.schema.json +350 -0
- package/mcp/schemas/c64_debug.schema.json +380 -0
- package/mcp/schemas/c64_disk.schema.json +278 -0
- package/mcp/schemas/c64_drive.schema.json +120 -0
- package/mcp/schemas/c64_extract.schema.json +302 -0
- package/mcp/schemas/c64_graphics.schema.json +333 -0
- package/mcp/schemas/c64_memory.schema.json +155 -0
- package/mcp/schemas/c64_printer.schema.json +218 -0
- package/mcp/schemas/c64_program.schema.json +391 -0
- package/mcp/schemas/c64_rag.schema.json +67 -0
- package/mcp/schemas/c64_select_backend.schema.json +32 -0
- package/mcp/schemas/c64_sound.schema.json +644 -0
- package/mcp/schemas/c64_stream.schema.json +61 -0
- package/mcp/schemas/c64_system.schema.json +227 -0
- package/mcp/schemas/c64_vice.schema.json +63 -0
- package/mcp/server.json +15 -0
- package/mcp/tools.json +4517 -0
- package/mcp.json +90 -15
- package/package.json +11 -4
- package/scripts/capture-backend-switch-screenshots.mjs +276 -0
- package/scripts/generate-mcp-interface.ts +289 -0
- package/scripts/invoke-bun.mjs +67 -21
- package/scripts/mockC64Server.mjs +162 -6
- package/scripts/prepare-release.mjs +59 -2
- package/scripts/run-coverage.mjs +284 -0
- package/scripts/run-tests.mjs +95 -39
- package/scripts/run-tests.ts +378 -73
- package/scripts/update-readme.ts +147 -13
- package/server.json +94 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "./types.js";
|
|
2
|
+
import { arraySchema, booleanSchema, literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "./schema.js";
|
|
3
|
+
import { jsonResult, textResult } from "./responses.js";
|
|
4
|
+
import { ToolError, ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
|
|
5
|
+
const checkpointIdSchema = numberSchema({
|
|
6
|
+
description: "Debugger checkpoint identifier.",
|
|
7
|
+
integer: true,
|
|
8
|
+
minimum: 1,
|
|
9
|
+
});
|
|
10
|
+
const memspaceSchema = optionalSchema(numberSchema({
|
|
11
|
+
description: "VICE memory space (0=CPU, 1=Drive8, etc.).",
|
|
12
|
+
integer: true,
|
|
13
|
+
minimum: 0,
|
|
14
|
+
maximum: 4,
|
|
15
|
+
}));
|
|
16
|
+
const registerSelectorSchema = objectSchema({
|
|
17
|
+
description: "Selects a register by id or name.",
|
|
18
|
+
properties: {
|
|
19
|
+
name: optionalSchema(stringSchema({ description: "Register name (case-insensitive)." })),
|
|
20
|
+
id: optionalSchema(numberSchema({ description: "Register id.", integer: true, minimum: 0 })),
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
});
|
|
24
|
+
const registerWriteSchema = objectSchema({
|
|
25
|
+
description: "Register write descriptor.",
|
|
26
|
+
properties: {
|
|
27
|
+
name: optionalSchema(stringSchema({ description: "Register name (case-insensitive)." })),
|
|
28
|
+
id: optionalSchema(numberSchema({ description: "Register id.", integer: true, minimum: 0 })),
|
|
29
|
+
value: numberSchema({ description: "Value to write.", integer: true, minimum: 0 }),
|
|
30
|
+
},
|
|
31
|
+
required: ["value"],
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
});
|
|
34
|
+
const listCheckpointsArgsSchema = objectSchema({
|
|
35
|
+
description: "List all active VICE checkpoints (breakpoints).",
|
|
36
|
+
properties: {
|
|
37
|
+
op: literalSchema("list_checkpoints"),
|
|
38
|
+
},
|
|
39
|
+
required: ["op"],
|
|
40
|
+
additionalProperties: false,
|
|
41
|
+
});
|
|
42
|
+
const getCheckpointArgsSchema = objectSchema({
|
|
43
|
+
description: "Fetch a single checkpoint by id.",
|
|
44
|
+
properties: {
|
|
45
|
+
op: literalSchema("get_checkpoint"),
|
|
46
|
+
id: checkpointIdSchema,
|
|
47
|
+
},
|
|
48
|
+
required: ["op", "id"],
|
|
49
|
+
additionalProperties: false,
|
|
50
|
+
});
|
|
51
|
+
const createCheckpointArgsSchema = objectSchema({
|
|
52
|
+
description: "Create a new checkpoint (breakpoint) in VICE.",
|
|
53
|
+
properties: {
|
|
54
|
+
op: literalSchema("create_checkpoint"),
|
|
55
|
+
address: stringSchema({ description: "Start address (e.g. $0801).", minLength: 1 }),
|
|
56
|
+
endAddress: optionalSchema(stringSchema({ description: "Optional end address for range.", minLength: 1 })),
|
|
57
|
+
stopOnHit: optionalSchema(booleanSchema({ description: "Pause execution when hit (default true).", default: true })),
|
|
58
|
+
enabled: optionalSchema(booleanSchema({ description: "Whether the checkpoint is enabled (default true).", default: true })),
|
|
59
|
+
temporary: optionalSchema(booleanSchema({ description: "Automatically remove after first hit.", default: false })),
|
|
60
|
+
operations: optionalSchema(objectSchema({
|
|
61
|
+
description: "Checkpoint operation filters.",
|
|
62
|
+
properties: {
|
|
63
|
+
execute: optionalSchema(booleanSchema({ description: "Trigger on execute (default true).", default: true })),
|
|
64
|
+
load: optionalSchema(booleanSchema({ description: "Trigger on memory load.", default: false })),
|
|
65
|
+
store: optionalSchema(booleanSchema({ description: "Trigger on memory store.", default: false })),
|
|
66
|
+
},
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
})),
|
|
69
|
+
memspace: memspaceSchema,
|
|
70
|
+
},
|
|
71
|
+
required: ["op", "address"],
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
});
|
|
74
|
+
const deleteCheckpointArgsSchema = objectSchema({
|
|
75
|
+
description: "Remove a checkpoint by id.",
|
|
76
|
+
properties: {
|
|
77
|
+
op: literalSchema("delete_checkpoint"),
|
|
78
|
+
id: checkpointIdSchema,
|
|
79
|
+
},
|
|
80
|
+
required: ["op", "id"],
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
});
|
|
83
|
+
const toggleCheckpointArgsSchema = objectSchema({
|
|
84
|
+
description: "Enable or disable a checkpoint by id.",
|
|
85
|
+
properties: {
|
|
86
|
+
op: literalSchema("toggle_checkpoint"),
|
|
87
|
+
id: checkpointIdSchema,
|
|
88
|
+
enabled: booleanSchema({ description: "Set to true to enable, false to disable." }),
|
|
89
|
+
},
|
|
90
|
+
required: ["op", "id", "enabled"],
|
|
91
|
+
additionalProperties: false,
|
|
92
|
+
});
|
|
93
|
+
const setConditionArgsSchema = objectSchema({
|
|
94
|
+
description: "Attach a conditional expression to a checkpoint.",
|
|
95
|
+
properties: {
|
|
96
|
+
op: literalSchema("set_condition"),
|
|
97
|
+
id: checkpointIdSchema,
|
|
98
|
+
expression: stringSchema({ description: "VICE monitor conditional expression.", minLength: 1 }),
|
|
99
|
+
},
|
|
100
|
+
required: ["op", "id", "expression"],
|
|
101
|
+
additionalProperties: false,
|
|
102
|
+
});
|
|
103
|
+
const listRegistersArgsSchema = objectSchema({
|
|
104
|
+
description: "List available registers (metadata).",
|
|
105
|
+
properties: {
|
|
106
|
+
op: literalSchema("list_registers"),
|
|
107
|
+
memspace: memspaceSchema,
|
|
108
|
+
},
|
|
109
|
+
required: ["op"],
|
|
110
|
+
additionalProperties: false,
|
|
111
|
+
});
|
|
112
|
+
const getRegistersArgsSchema = objectSchema({
|
|
113
|
+
description: "Read register values, optionally filtered by name or id.",
|
|
114
|
+
properties: {
|
|
115
|
+
op: literalSchema("get_registers"),
|
|
116
|
+
memspace: memspaceSchema,
|
|
117
|
+
registers: optionalSchema(arraySchema(registerSelectorSchema, { description: "Registers to include." })),
|
|
118
|
+
},
|
|
119
|
+
required: ["op"],
|
|
120
|
+
additionalProperties: false,
|
|
121
|
+
});
|
|
122
|
+
const setRegistersArgsSchema = objectSchema({
|
|
123
|
+
description: "Write register values.",
|
|
124
|
+
properties: {
|
|
125
|
+
op: literalSchema("set_registers"),
|
|
126
|
+
memspace: memspaceSchema,
|
|
127
|
+
writes: arraySchema(registerWriteSchema, { description: "Register writes to apply.", minItems: 1 }),
|
|
128
|
+
},
|
|
129
|
+
required: ["op", "writes"],
|
|
130
|
+
additionalProperties: false,
|
|
131
|
+
});
|
|
132
|
+
const stepArgsSchema = objectSchema({
|
|
133
|
+
description: "Single-step CPU execution.",
|
|
134
|
+
properties: {
|
|
135
|
+
op: literalSchema("step"),
|
|
136
|
+
count: optionalSchema(numberSchema({ description: "Number of instructions to step.", integer: true, minimum: 1, default: 1 })),
|
|
137
|
+
mode: optionalSchema(stringSchema({ description: "Step mode (into or over).", enum: ["into", "over"] })),
|
|
138
|
+
},
|
|
139
|
+
required: ["op"],
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
});
|
|
142
|
+
const stepReturnArgsSchema = objectSchema({
|
|
143
|
+
description: "Continue execution until the current routine returns.",
|
|
144
|
+
properties: {
|
|
145
|
+
op: literalSchema("step_return"),
|
|
146
|
+
},
|
|
147
|
+
required: ["op"],
|
|
148
|
+
additionalProperties: false,
|
|
149
|
+
});
|
|
150
|
+
const debugOperationSchemas = [
|
|
151
|
+
listCheckpointsArgsSchema,
|
|
152
|
+
getCheckpointArgsSchema,
|
|
153
|
+
createCheckpointArgsSchema,
|
|
154
|
+
deleteCheckpointArgsSchema,
|
|
155
|
+
toggleCheckpointArgsSchema,
|
|
156
|
+
setConditionArgsSchema,
|
|
157
|
+
listRegistersArgsSchema,
|
|
158
|
+
getRegistersArgsSchema,
|
|
159
|
+
setRegistersArgsSchema,
|
|
160
|
+
stepArgsSchema,
|
|
161
|
+
stepReturnArgsSchema,
|
|
162
|
+
];
|
|
163
|
+
const debugOperationHandlers = {
|
|
164
|
+
list_checkpoints: async (_args, ctx) => {
|
|
165
|
+
try {
|
|
166
|
+
listCheckpointsArgsSchema.parse(_args);
|
|
167
|
+
ctx.logger.info("Listing VICE checkpoints");
|
|
168
|
+
const checkpoints = await ctx.client.viceCheckpointList();
|
|
169
|
+
return jsonResult({ checkpoints: checkpoints.map(formatCheckpoint) }, { success: true, count: checkpoints.length });
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return handleToolError(error);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
get_checkpoint: async (args, ctx) => {
|
|
176
|
+
try {
|
|
177
|
+
const parsed = getCheckpointArgsSchema.parse(args);
|
|
178
|
+
const checkpoint = await ctx.client.viceCheckpointGet(parsed.id);
|
|
179
|
+
return jsonResult({ checkpoint: formatCheckpoint(checkpoint) }, { success: true });
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return handleToolError(error);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
create_checkpoint: async (args, ctx) => {
|
|
186
|
+
try {
|
|
187
|
+
const parsed = createCheckpointArgsSchema.parse(args);
|
|
188
|
+
const start = parseAddress(parsed.address, "address");
|
|
189
|
+
const end = parsed.endAddress ? parseAddress(parsed.endAddress, "endAddress") : start;
|
|
190
|
+
const operations = parsed.operations ?? {};
|
|
191
|
+
const created = await ctx.client.viceCheckpointCreate({
|
|
192
|
+
start,
|
|
193
|
+
end,
|
|
194
|
+
stopOnHit: parsed.stopOnHit ?? true,
|
|
195
|
+
enabled: parsed.enabled ?? true,
|
|
196
|
+
temporary: parsed.temporary ?? false,
|
|
197
|
+
operations: {
|
|
198
|
+
execute: operations.execute !== false,
|
|
199
|
+
load: operations.load === true,
|
|
200
|
+
store: operations.store === true,
|
|
201
|
+
},
|
|
202
|
+
memspace: normaliseMemspace(parsed.memspace),
|
|
203
|
+
});
|
|
204
|
+
ctx.logger.info("Created VICE checkpoint", { id: created.id, start: formatAddress(created.start) });
|
|
205
|
+
return jsonResult({ checkpoint: formatCheckpoint(created) }, { success: true, id: created.id });
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
return handleToolError(error);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
delete_checkpoint: async (args, ctx) => {
|
|
212
|
+
try {
|
|
213
|
+
const parsed = deleteCheckpointArgsSchema.parse(args);
|
|
214
|
+
await ctx.client.viceCheckpointDelete(parsed.id);
|
|
215
|
+
ctx.logger.info("Deleted VICE checkpoint", { id: parsed.id });
|
|
216
|
+
return textResult(`Deleted checkpoint ${parsed.id}.`, { success: true, id: parsed.id });
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
return handleToolError(error);
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
toggle_checkpoint: async (args, ctx) => {
|
|
223
|
+
try {
|
|
224
|
+
const parsed = toggleCheckpointArgsSchema.parse(args);
|
|
225
|
+
await ctx.client.viceCheckpointToggle(parsed.id, parsed.enabled);
|
|
226
|
+
ctx.logger.info(parsed.enabled ? "Enabled VICE checkpoint" : "Disabled VICE checkpoint", { id: parsed.id });
|
|
227
|
+
return textResult(`Checkpoint ${parsed.id} ${parsed.enabled ? "enabled" : "disabled"}.`, { success: true, id: parsed.id, enabled: parsed.enabled });
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
return handleToolError(error);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
set_condition: async (args, ctx) => {
|
|
234
|
+
try {
|
|
235
|
+
const parsed = setConditionArgsSchema.parse(args);
|
|
236
|
+
await ctx.client.viceCheckpointSetCondition(parsed.id, parsed.expression);
|
|
237
|
+
ctx.logger.info("Updated checkpoint condition", { id: parsed.id });
|
|
238
|
+
return textResult(`Updated condition for checkpoint ${parsed.id}.`, { success: true, id: parsed.id });
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
return handleToolError(error);
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
list_registers: async (args, ctx) => {
|
|
245
|
+
try {
|
|
246
|
+
const parsed = listRegistersArgsSchema.parse(args);
|
|
247
|
+
const memspace = normaliseMemspace(parsed.memspace);
|
|
248
|
+
const metadata = await ctx.client.viceRegistersAvailable(memspace);
|
|
249
|
+
return jsonResult({
|
|
250
|
+
memspace,
|
|
251
|
+
registers: metadata.map((entry) => ({
|
|
252
|
+
id: entry.id,
|
|
253
|
+
name: entry.name,
|
|
254
|
+
bits: entry.bits,
|
|
255
|
+
size: entry.size,
|
|
256
|
+
})),
|
|
257
|
+
}, { success: true, count: metadata.length, memspace });
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
return handleToolError(error);
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
get_registers: async (args, ctx) => {
|
|
264
|
+
try {
|
|
265
|
+
const parsed = getRegistersArgsSchema.parse(args);
|
|
266
|
+
const memspace = normaliseMemspace(parsed.memspace);
|
|
267
|
+
const metadata = await ctx.client.viceRegistersAvailable(memspace);
|
|
268
|
+
const values = await ctx.client.viceRegistersGet(memspace);
|
|
269
|
+
const metadataById = new Map(metadata.map((entry) => [entry.id, entry]));
|
|
270
|
+
let filtered = values;
|
|
271
|
+
if (parsed.registers && parsed.registers.length > 0) {
|
|
272
|
+
const requested = buildRegisterPredicate(parsed.registers, metadataById);
|
|
273
|
+
filtered = values.filter((value) => requested(value.id));
|
|
274
|
+
}
|
|
275
|
+
return jsonResult({
|
|
276
|
+
memspace,
|
|
277
|
+
registers: filtered.map((value) => {
|
|
278
|
+
const meta = metadataById.get(value.id);
|
|
279
|
+
return {
|
|
280
|
+
id: value.id,
|
|
281
|
+
name: meta?.name,
|
|
282
|
+
bits: meta?.bits,
|
|
283
|
+
size: value.size,
|
|
284
|
+
value: value.value,
|
|
285
|
+
};
|
|
286
|
+
}),
|
|
287
|
+
}, { success: true, count: filtered.length, memspace });
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
return handleToolError(error);
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
set_registers: async (args, ctx) => {
|
|
294
|
+
try {
|
|
295
|
+
const parsed = setRegistersArgsSchema.parse(args);
|
|
296
|
+
const memspace = normaliseMemspace(parsed.memspace);
|
|
297
|
+
const metadata = await ctx.client.viceRegistersAvailable(memspace);
|
|
298
|
+
const writes = parsed.writes.map((entry) => {
|
|
299
|
+
if (!entry.name && entry.id === undefined) {
|
|
300
|
+
throw new ToolValidationError("Each write must specify a name or id.", { path: "$.writes" });
|
|
301
|
+
}
|
|
302
|
+
return entry;
|
|
303
|
+
});
|
|
304
|
+
const updated = await ctx.client.viceRegistersSet(writes, { memspace, metadata });
|
|
305
|
+
const metadataById = new Map(metadata.map((entry) => [entry.id, entry]));
|
|
306
|
+
return jsonResult({
|
|
307
|
+
memspace,
|
|
308
|
+
registers: updated.map((value) => {
|
|
309
|
+
const meta = metadataById.get(value.id);
|
|
310
|
+
return {
|
|
311
|
+
id: value.id,
|
|
312
|
+
name: meta?.name,
|
|
313
|
+
bits: meta?.bits,
|
|
314
|
+
size: value.size,
|
|
315
|
+
value: value.value,
|
|
316
|
+
};
|
|
317
|
+
}),
|
|
318
|
+
}, { success: true, count: updated.length, memspace });
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
return handleToolError(error);
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
step: async (args, ctx) => {
|
|
325
|
+
try {
|
|
326
|
+
const parsed = stepArgsSchema.parse(args);
|
|
327
|
+
const count = parsed.count ?? 1;
|
|
328
|
+
const stepOver = parsed.mode === "over";
|
|
329
|
+
await ctx.client.viceStepInstructions(count, { stepOver });
|
|
330
|
+
ctx.logger.info("Stepped instructions", { count, stepOver });
|
|
331
|
+
return textResult(`Stepped ${count} instruction${count === 1 ? "" : "s"}${stepOver ? " (over)" : ""}.`, { success: true, count, mode: stepOver ? "over" : "into" });
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
return handleToolError(error);
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
step_return: async (args, ctx) => {
|
|
338
|
+
try {
|
|
339
|
+
stepReturnArgsSchema.parse(args);
|
|
340
|
+
await ctx.client.viceStepReturn();
|
|
341
|
+
ctx.logger.info("Stepped until return");
|
|
342
|
+
return textResult("Continued execution until current routine return.", { success: true });
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
return handleToolError(error);
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
const debugOperationDispatcher = createOperationDispatcher("c64_debug", debugOperationHandlers);
|
|
350
|
+
export const debugModuleGroup = defineToolModule({
|
|
351
|
+
domain: "debug",
|
|
352
|
+
summary: "VICE Binary Monitor debugger operations (breakpoints, registers, stepping).",
|
|
353
|
+
resources: ["c64://specs/assembly", "c64://specs/memory-map"],
|
|
354
|
+
prompts: ["assembly-program", "memory-debug"],
|
|
355
|
+
defaultTags: ["debug", "vice"],
|
|
356
|
+
workflowHints: [
|
|
357
|
+
"Use debugger tools after pausing execution so memory and screen reads remain stable.",
|
|
358
|
+
"Communicate breakpoint effects clearly and restore original state when toggling them.",
|
|
359
|
+
],
|
|
360
|
+
supportedPlatforms: ["vice"],
|
|
361
|
+
tools: [
|
|
362
|
+
{
|
|
363
|
+
name: "c64_debug",
|
|
364
|
+
description: "Grouped entry point for VICE debugger operations (breakpoints, registers, stepping).",
|
|
365
|
+
summary: "Manage breakpoints, inspect registers, and step through code on the VICE backend.",
|
|
366
|
+
inputSchema: discriminatedUnionSchema({
|
|
367
|
+
description: "Available debugger operations.",
|
|
368
|
+
variants: debugOperationSchemas.map((schema) => schema.jsonSchema),
|
|
369
|
+
}),
|
|
370
|
+
tags: ["debug", "breakpoint", "registers", "grouped"],
|
|
371
|
+
examples: [
|
|
372
|
+
{
|
|
373
|
+
name: "List checkpoints",
|
|
374
|
+
description: "Show all breakpoints defined in the emulator.",
|
|
375
|
+
arguments: { op: "list_checkpoints" },
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: "Step over instruction",
|
|
379
|
+
description: "Advance one instruction without entering subroutines.",
|
|
380
|
+
arguments: { op: "step", mode: "over" },
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: "Inspect registers",
|
|
384
|
+
description: "Read CPU registers from memspace 0 (default).",
|
|
385
|
+
arguments: { op: "get_registers" },
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
execute: debugOperationDispatcher,
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
});
|
|
392
|
+
function handleToolError(error) {
|
|
393
|
+
if (error instanceof ToolError) {
|
|
394
|
+
return toolErrorResult(error);
|
|
395
|
+
}
|
|
396
|
+
return unknownErrorResult(error);
|
|
397
|
+
}
|
|
398
|
+
function formatCheckpoint(checkpoint) {
|
|
399
|
+
return {
|
|
400
|
+
id: checkpoint.id,
|
|
401
|
+
start: formatAddress(checkpoint.start),
|
|
402
|
+
end: formatAddress(checkpoint.end),
|
|
403
|
+
enabled: checkpoint.enabled,
|
|
404
|
+
stopOnHit: checkpoint.stopOnHit,
|
|
405
|
+
temporary: checkpoint.temporary,
|
|
406
|
+
hitCount: checkpoint.hitCount,
|
|
407
|
+
ignoreCount: checkpoint.ignoreCount,
|
|
408
|
+
operations: {
|
|
409
|
+
execute: checkpoint.operations.execute,
|
|
410
|
+
load: checkpoint.operations.load,
|
|
411
|
+
store: checkpoint.operations.store,
|
|
412
|
+
},
|
|
413
|
+
memspace: checkpoint.memspace,
|
|
414
|
+
hasCondition: checkpoint.hasCondition,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function formatAddress(value) {
|
|
418
|
+
return `$${value.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
419
|
+
}
|
|
420
|
+
function parseAddress(value, label) {
|
|
421
|
+
const trimmed = value.trim();
|
|
422
|
+
let radix = 10;
|
|
423
|
+
let literal = trimmed;
|
|
424
|
+
if (trimmed.startsWith("$")) {
|
|
425
|
+
radix = 16;
|
|
426
|
+
literal = trimmed.slice(1);
|
|
427
|
+
}
|
|
428
|
+
else if (trimmed.toLowerCase().startsWith("0x")) {
|
|
429
|
+
radix = 16;
|
|
430
|
+
literal = trimmed.slice(2);
|
|
431
|
+
}
|
|
432
|
+
const parsed = Number.parseInt(literal, radix);
|
|
433
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 0xffff) {
|
|
434
|
+
throw new ToolValidationError(`Invalid ${label}`, { path: `$.${label}` });
|
|
435
|
+
}
|
|
436
|
+
return parsed;
|
|
437
|
+
}
|
|
438
|
+
function normaliseMemspace(value) {
|
|
439
|
+
const allowed = [0, 1, 2, 3, 4];
|
|
440
|
+
if (value === undefined) {
|
|
441
|
+
return 0;
|
|
442
|
+
}
|
|
443
|
+
const numeric = Number(value);
|
|
444
|
+
if (allowed.includes(numeric)) {
|
|
445
|
+
return numeric;
|
|
446
|
+
}
|
|
447
|
+
throw new ToolValidationError("memspace must be between 0 and 4", { path: "$.memspace" });
|
|
448
|
+
}
|
|
449
|
+
function buildRegisterPredicate(selectors, metadataById) {
|
|
450
|
+
const ids = new Set();
|
|
451
|
+
const names = new Set();
|
|
452
|
+
for (const selector of selectors) {
|
|
453
|
+
if (selector.id !== undefined) {
|
|
454
|
+
ids.add(selector.id);
|
|
455
|
+
}
|
|
456
|
+
if (selector.name) {
|
|
457
|
+
names.add(selector.name.toLowerCase());
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return (id) => {
|
|
461
|
+
if (ids.has(id)) {
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
const meta = metadataById.get(id);
|
|
465
|
+
if (!meta) {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
return names.has(meta.name.toLowerCase());
|
|
469
|
+
};
|
|
470
|
+
}
|
package/dist/tools/developer.js
CHANGED
|
@@ -146,6 +146,7 @@ export const developerModule = defineToolModule({
|
|
|
146
146
|
workflowHints: [
|
|
147
147
|
"Run first to enumerate categories before reading or writing specific settings; summarise the top-level sections for the user.",
|
|
148
148
|
],
|
|
149
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
149
150
|
async execute(args, ctx) {
|
|
150
151
|
try {
|
|
151
152
|
noArgsSchema.parse(args ?? {});
|
|
@@ -181,6 +182,7 @@ export const developerModule = defineToolModule({
|
|
|
181
182
|
workflowHints: [
|
|
182
183
|
"Use after config_list to fetch details; restate category/item so the user can cross-check values before changes.",
|
|
183
184
|
],
|
|
185
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
184
186
|
async execute(args, ctx) {
|
|
185
187
|
try {
|
|
186
188
|
const parsed = configGetArgsSchema.parse(args ?? {});
|
|
@@ -216,6 +218,7 @@ export const developerModule = defineToolModule({
|
|
|
216
218
|
workflowHints: [
|
|
217
219
|
"Confirm the user-supplied value before writing; mention whether a reboot is required for the change to take effect.",
|
|
218
220
|
],
|
|
221
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
219
222
|
async execute(args, ctx) {
|
|
220
223
|
try {
|
|
221
224
|
const parsed = configSetArgsSchema.parse(args ?? {});
|
|
@@ -259,6 +262,7 @@ export const developerModule = defineToolModule({
|
|
|
259
262
|
workflowHints: [
|
|
260
263
|
"Use when multiple categories must be updated together; remind the user to review validation errors closely.",
|
|
261
264
|
],
|
|
265
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
262
266
|
async execute(args, ctx) {
|
|
263
267
|
try {
|
|
264
268
|
const payload = configBatchSchema.parse(args ?? {});
|
|
@@ -399,8 +403,9 @@ export const developerModule = defineToolModule({
|
|
|
399
403
|
},
|
|
400
404
|
{
|
|
401
405
|
name: "version",
|
|
402
|
-
description: "Retrieve
|
|
403
|
-
summary: "Calls the firmware version endpoint
|
|
406
|
+
description: "Retrieve firmware or emulator version information.",
|
|
407
|
+
summary: "Calls the firmware version endpoint (Ultimate) or returns emulator info (VICE).",
|
|
408
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
404
409
|
inputSchema: noArgsSchema.jsonSchema,
|
|
405
410
|
relatedResources: ["c64://context/bootstrap"],
|
|
406
411
|
tags: ["diagnostics", "version"],
|
|
@@ -430,8 +435,9 @@ export const developerModule = defineToolModule({
|
|
|
430
435
|
},
|
|
431
436
|
{
|
|
432
437
|
name: "info",
|
|
433
|
-
description: "Retrieve
|
|
434
|
-
summary: "Returns the
|
|
438
|
+
description: "Retrieve hardware or emulator information and status.",
|
|
439
|
+
summary: "Returns the diagnostics payload reported by the firmware (Ultimate) or emulator (VICE).",
|
|
440
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
435
441
|
inputSchema: noArgsSchema.jsonSchema,
|
|
436
442
|
relatedResources: ["c64://context/bootstrap"],
|
|
437
443
|
tags: ["diagnostics", "info"],
|