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
@@ -142,6 +142,8 @@ type GroupedOperation = {
142
142
  readonly op: string;
143
143
  readonly description: string;
144
144
  readonly required: readonly string[];
145
+ readonly optional: readonly string[];
146
+ readonly properties: Readonly<Record<string, JsonSchema | undefined>>;
145
147
  readonly notes: readonly string[];
146
148
  };
147
149
 
@@ -165,20 +167,91 @@ function toTableValue(values: readonly string[]): string {
165
167
  return values.join(", ");
166
168
  }
167
169
 
170
+ function formatInputName(name: string, schema?: JsonSchema): string {
171
+ const defaultSuffix = schema && Object.prototype.hasOwnProperty.call(schema, "default")
172
+ ? `=${JSON.stringify(schema.default)}`
173
+ : "";
174
+ return `\`${name}${defaultSuffix}\``;
175
+ }
176
+
177
+ function formatInputs(names: readonly string[], properties: Readonly<Record<string, JsonSchema | undefined>>): string {
178
+ return toTableValue(names.map((name) => formatInputName(name, properties[name])));
179
+ }
180
+
168
181
  function collectGroupedOperations(schema?: JsonSchema): readonly GroupedOperation[] {
169
182
  if (!schema || !isObject(schema)) {
170
183
  return [];
171
184
  }
172
185
 
186
+ const metadataOperations = collectFromOperationMetadata(schema);
187
+ if (metadataOperations.length > 0) {
188
+ return metadataOperations;
189
+ }
190
+
173
191
  const discriminator = (schema as JsonSchema & { discriminator?: { propertyName?: string } }).discriminator;
174
192
  const variants = (schema as JsonSchema & { oneOf?: readonly JsonSchema[] }).oneOf;
175
193
 
176
- if (!discriminator || discriminator.propertyName !== "op" || !Array.isArray(variants)) {
194
+ if (discriminator && discriminator.propertyName === "op" && Array.isArray(variants)) {
195
+ return collectFromVariantList(variants);
196
+ }
197
+
198
+ return collectFromFlattenedSchema(schema);
199
+ }
200
+
201
+ function collectFromOperationMetadata(schema: JsonSchema): readonly GroupedOperation[] {
202
+ const metadata = (schema as JsonSchema & {
203
+ readonly "x-c64bridge-operations"?: readonly unknown[];
204
+ })["x-c64bridge-operations"];
205
+
206
+ if (!Array.isArray(metadata)) {
177
207
  return [];
178
208
  }
179
209
 
180
210
  const operations: GroupedOperation[] = [];
181
211
 
212
+ for (const entry of metadata) {
213
+ if (!isObject(entry)) {
214
+ continue;
215
+ }
216
+
217
+ const opName = getString(entry.op);
218
+ if (!opName) {
219
+ continue;
220
+ }
221
+
222
+ const inputSchema = isObject(entry.inputSchema) ? (entry.inputSchema as JsonSchema) : undefined;
223
+ const properties = (inputSchema?.properties ?? {}) as Record<string, JsonSchema | undefined>;
224
+ const requiredProps = Array.isArray(entry.required)
225
+ ? entry.required.filter((name): name is string => typeof name === "string" && name !== "op")
226
+ : [];
227
+ const optionalProps = Array.isArray(entry.optional)
228
+ ? entry.optional.filter((name): name is string => typeof name === "string" && name !== "op")
229
+ : Object.keys(properties).filter((name) => name !== "op" && !requiredProps.includes(name));
230
+
231
+ const notes: string[] = [];
232
+ const hasVerificationProperty = Object.keys(properties).some((name) =>
233
+ name.toLowerCase().startsWith("verify"),
234
+ );
235
+ if (hasVerificationProperty) {
236
+ notes.push("supports verify");
237
+ }
238
+
239
+ operations.push({
240
+ op: opName,
241
+ description: getString(entry.description, `Operation ${opName}`),
242
+ required: requiredProps,
243
+ optional: optionalProps,
244
+ properties,
245
+ notes,
246
+ });
247
+ }
248
+
249
+ return operations;
250
+ }
251
+
252
+ function collectFromVariantList(variants: readonly JsonSchema[]): readonly GroupedOperation[] {
253
+ const operations: GroupedOperation[] = [];
254
+
182
255
  for (const variant of variants) {
183
256
  if (!variant || !isObject(variant)) {
184
257
  continue;
@@ -220,6 +293,8 @@ function collectGroupedOperations(schema?: JsonSchema): readonly GroupedOperatio
220
293
  op: opName,
221
294
  description,
222
295
  required: requiredProps,
296
+ optional: Object.keys(properties).filter((name) => name !== "op" && !requiredProps.includes(name)),
297
+ properties,
223
298
  notes,
224
299
  });
225
300
  }
@@ -227,6 +302,56 @@ function collectGroupedOperations(schema?: JsonSchema): readonly GroupedOperatio
227
302
  return operations;
228
303
  }
229
304
 
305
+ function collectFromFlattenedSchema(schema: JsonSchema): readonly GroupedOperation[] {
306
+ const properties = (schema.properties ?? {}) as Record<string, JsonSchema | undefined>;
307
+ const opSchema = properties.op;
308
+ if (!opSchema || !isObject(opSchema)) {
309
+ return [];
310
+ }
311
+
312
+ const enumValues = (opSchema as JsonSchema).enum;
313
+ if (!Array.isArray(enumValues) || enumValues.length === 0) {
314
+ return [];
315
+ }
316
+
317
+ const opNames = enumValues.filter((value): value is string => typeof value === "string" && value.length > 0);
318
+ if (opNames.length === 0) {
319
+ return [];
320
+ }
321
+
322
+ const descriptionMap = parseOperationDescriptions(getString(schema.description));
323
+ const hasVerificationProperty = Object.keys(properties).some((name) =>
324
+ name !== "op" && name.toLowerCase().startsWith("verify"),
325
+ );
326
+ const notes = hasVerificationProperty ? ["supports verify"] : [];
327
+
328
+ return opNames.map((opName) => ({
329
+ op: opName,
330
+ description: descriptionMap.get(opName) ?? `Operation ${opName}`,
331
+ required: [] as readonly string[],
332
+ optional: Object.keys(properties).filter((name) => name !== "op"),
333
+ properties,
334
+ notes,
335
+ }));
336
+ }
337
+
338
+ function parseOperationDescriptions(description: string): Map<string, string> {
339
+ const result = new Map<string, string>();
340
+ if (!description) {
341
+ return result;
342
+ }
343
+
344
+ const lines = description.split(/\r?\n/);
345
+ for (const line of lines) {
346
+ const match = line.match(/^\s*-\s+([A-Za-z0-9_]+):\s*(.+)$/);
347
+ if (match) {
348
+ result.set(match[1], match[2].trim());
349
+ }
350
+ }
351
+
352
+ return result;
353
+ }
354
+
230
355
  export function renderToolsSection(modules: readonly ToolModuleDescriptor[] = describeToolModules()): string[] {
231
356
  const lines: string[] = ["### Tools", ""];
232
357
  const groupedTools: GroupedToolInfo[] = [];
@@ -250,6 +375,9 @@ export function renderToolsSection(modules: readonly ToolModuleDescriptor[] = de
250
375
  if (operations.length === 0) {
251
376
  lines.push("_No operations defined._");
252
377
  } else {
378
+ if (!lines.includes("_Address range convention: `address` + `length` means start address plus byte count; `startAddress` + `endAddress` means inclusive bounds._")) {
379
+ lines.splice(2, 0, "_Address range convention: `address` + `length` means start address plus byte count; `startAddress` + `endAddress` means inclusive bounds._", "");
380
+ }
253
381
  const toolPlatforms: readonly string[] = tool.metadata.platforms ?? ["c64u"];
254
382
  const opPlatformOverrides: Record<string, readonly string[]> = tool.metadata.operationPlatforms ?? {};
255
383
  const operationRows = operations
@@ -260,14 +388,15 @@ export function renderToolsSection(modules: readonly ToolModuleDescriptor[] = de
260
388
  return [
261
389
  `\`${operation.op}\``,
262
390
  escapeCell(operation.description),
263
- escapeCell(toTableValue(operation.required.map((name) => `\`${name}\``))),
391
+ escapeCell(formatInputs(operation.required, operation.properties)),
392
+ escapeCell(formatInputs(operation.optional, operation.properties)),
264
393
  operation.notes.length ? escapeCell(operation.notes.join(", ")) : "—",
265
394
  effectivePlatforms.includes("c64u") ? "✅" : "",
266
395
  effectivePlatforms.includes("vice") ? "✅" : "",
267
396
  ];
268
397
  });
269
398
 
270
- lines.push(renderTable(["Operation", "Description", "Required Inputs", "Notes", "C64U", "VICE"], operationRows));
399
+ lines.push(renderTable(["Operation", "Description", "Required Inputs", "Optional Inputs", "Notes", "C64U", "VICE"], operationRows));
271
400
  }
272
401
 
273
402
  lines.push("");
package/server.json CHANGED
@@ -8,12 +8,12 @@
8
8
  "source": "github"
9
9
  },
10
10
  "websiteUrl": "https://github.com/chrisgleissner/c64bridge",
11
- "version": "0.8.1",
11
+ "version": "0.9.1",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "c64bridge",
16
- "version": "0.8.1",
16
+ "version": "0.9.1",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  },