c64bridge 0.9.1 → 0.9.2

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/mcp/tools.json CHANGED
@@ -8125,147 +8125,266 @@
8125
8125
  "description": "Keyboard buffer injection (cross-platform) and joystick simulation (VICE only).",
8126
8126
  "inputSchema": {
8127
8127
  "type": "object",
8128
- "description": "Input operations: write_text, key, joystick.",
8129
- "oneOf": [
8128
+ "properties": {
8129
+ "op": {
8130
+ "type": "string",
8131
+ "enum": [
8132
+ "write_text",
8133
+ "key",
8134
+ "joystick"
8135
+ ],
8136
+ "description": "Selects the operation."
8137
+ },
8138
+ "text": {
8139
+ "type": "string",
8140
+ "description": "Text to type. Use {RETURN}, {CLR}, {F1}…{F8}, {UP/DOWN/LEFT/RIGHT}, colour names, etc.",
8141
+ "minLength": 1,
8142
+ "maxLength": 512
8143
+ },
8144
+ "delayMs": {
8145
+ "type": [
8146
+ "integer",
8147
+ "null"
8148
+ ],
8149
+ "description": "Delay in ms after sending (default 0).",
8150
+ "minimum": 0,
8151
+ "maximum": 10000,
8152
+ "default": 0
8153
+ },
8154
+ "key": {
8155
+ "type": "string",
8156
+ "description": "Key to press: a printable character, or a PETSCII token name (RETURN, F1…F8, UP, etc.).",
8157
+ "minLength": 1,
8158
+ "maxLength": 32
8159
+ },
8160
+ "durationMs": {
8161
+ "type": [
8162
+ "integer",
8163
+ "null"
8164
+ ],
8165
+ "description": "Delay in ms after each queued key press before the next press (default 0 = no delay).",
8166
+ "minimum": 0,
8167
+ "maximum": 5000,
8168
+ "default": 0
8169
+ },
8170
+ "count": {
8171
+ "type": [
8172
+ "integer",
8173
+ "null"
8174
+ ],
8175
+ "description": "Number of times to press the key (default 1).",
8176
+ "minimum": 1,
8177
+ "maximum": 100,
8178
+ "default": 1
8179
+ },
8180
+ "port": {
8181
+ "type": "integer",
8182
+ "description": "Joystick port (1 = $DC01, 2 = $DC00).",
8183
+ "minimum": 1,
8184
+ "maximum": 2
8185
+ },
8186
+ "controls": {
8187
+ "type": "array",
8188
+ "description": "List of controls to activate simultaneously.",
8189
+ "items": {
8190
+ "type": "string",
8191
+ "description": "Control to activate.",
8192
+ "enum": [
8193
+ "up",
8194
+ "down",
8195
+ "left",
8196
+ "right",
8197
+ "fire"
8198
+ ]
8199
+ },
8200
+ "minItems": 0
8201
+ },
8202
+ "action": {
8203
+ "type": "string",
8204
+ "description": "press = hold until a release call; release = restore all bits; tap = press then release after durationMs.",
8205
+ "enum": [
8206
+ "press",
8207
+ "release",
8208
+ "tap"
8209
+ ]
8210
+ }
8211
+ },
8212
+ "required": [
8213
+ "op"
8214
+ ],
8215
+ "description": "Input operations: write_text, key, joystick.\n\nOperations:\n- write_text: Send a text string to the keyboard buffer, with PETSCII token expansion. Required inputs: text. Optional inputs: delayMs.\n- key: Tap a single key or hold it for a duration. Required inputs: key. Optional inputs: durationMs, count.\n- joystick: Simulate joystick input by writing directly to CIA1 Port A/B registers. Required inputs: port, controls, action. Optional inputs: durationMs.",
8216
+ "additionalProperties": false,
8217
+ "x-c64bridge-operations": [
8130
8218
  {
8131
- "type": "object",
8219
+ "op": "write_text",
8132
8220
  "description": "Send a text string to the keyboard buffer, with PETSCII token expansion.",
8133
- "properties": {
8134
- "op": {
8135
- "const": "write_text"
8136
- },
8137
- "text": {
8138
- "type": "string",
8139
- "description": "Text to type. Use {RETURN}, {CLR}, {F1}…{F8}, {UP/DOWN/LEFT/RIGHT}, colour names, etc.",
8140
- "minLength": 1,
8141
- "maxLength": 512
8142
- },
8143
- "delayMs": {
8144
- "type": [
8145
- "integer",
8146
- "null"
8147
- ],
8148
- "description": "Delay in ms after sending (default 0).",
8149
- "minimum": 0,
8150
- "maximum": 10000,
8151
- "default": 0
8152
- }
8153
- },
8154
8221
  "required": [
8155
- "op",
8156
8222
  "text"
8157
8223
  ],
8158
- "additionalProperties": false
8224
+ "optional": [
8225
+ "delayMs"
8226
+ ],
8227
+ "inputSchema": {
8228
+ "type": "object",
8229
+ "description": "Send a text string to the keyboard buffer, with PETSCII token expansion.",
8230
+ "properties": {
8231
+ "op": {
8232
+ "const": "write_text"
8233
+ },
8234
+ "text": {
8235
+ "type": "string",
8236
+ "description": "Text to type. Use {RETURN}, {CLR}, {F1}…{F8}, {UP/DOWN/LEFT/RIGHT}, colour names, etc.",
8237
+ "minLength": 1,
8238
+ "maxLength": 512
8239
+ },
8240
+ "delayMs": {
8241
+ "type": [
8242
+ "integer",
8243
+ "null"
8244
+ ],
8245
+ "description": "Delay in ms after sending (default 0).",
8246
+ "minimum": 0,
8247
+ "maximum": 10000,
8248
+ "default": 0
8249
+ }
8250
+ },
8251
+ "required": [
8252
+ "op",
8253
+ "text"
8254
+ ],
8255
+ "additionalProperties": false
8256
+ }
8159
8257
  },
8160
8258
  {
8161
- "type": "object",
8259
+ "op": "key",
8162
8260
  "description": "Tap a single key or hold it for a duration.",
8163
- "properties": {
8164
- "op": {
8165
- "const": "key"
8166
- },
8167
- "key": {
8168
- "type": "string",
8169
- "description": "Key to press: a printable character, or a PETSCII token name (RETURN, F1…F8, UP, etc.).",
8170
- "minLength": 1,
8171
- "maxLength": 32
8172
- },
8173
- "durationMs": {
8174
- "type": [
8175
- "integer",
8176
- "null"
8177
- ],
8178
- "description": "Delay in ms after each queued key press before the next press (default 0 = no delay).",
8179
- "minimum": 0,
8180
- "maximum": 5000,
8181
- "default": 0
8182
- },
8183
- "count": {
8184
- "type": [
8185
- "integer",
8186
- "null"
8187
- ],
8188
- "description": "Number of times to press the key (default 1).",
8189
- "minimum": 1,
8190
- "maximum": 100,
8191
- "default": 1
8192
- }
8193
- },
8194
8261
  "required": [
8195
- "op",
8196
8262
  "key"
8197
8263
  ],
8198
- "additionalProperties": false
8199
- },
8200
- {
8201
- "type": "object",
8202
- "description": "Simulate joystick input by writing directly to CIA1 Port A/B registers.",
8203
- "properties": {
8204
- "op": {
8205
- "const": "joystick"
8206
- },
8207
- "port": {
8208
- "type": "integer",
8209
- "description": "Joystick port (1 = $DC01, 2 = $DC00).",
8210
- "minimum": 1,
8211
- "maximum": 2
8212
- },
8213
- "controls": {
8214
- "type": "array",
8215
- "description": "List of controls to activate simultaneously.",
8216
- "items": {
8264
+ "optional": [
8265
+ "durationMs",
8266
+ "count"
8267
+ ],
8268
+ "inputSchema": {
8269
+ "type": "object",
8270
+ "description": "Tap a single key or hold it for a duration.",
8271
+ "properties": {
8272
+ "op": {
8273
+ "const": "key"
8274
+ },
8275
+ "key": {
8217
8276
  "type": "string",
8218
- "description": "Control to activate.",
8219
- "enum": [
8220
- "up",
8221
- "down",
8222
- "left",
8223
- "right",
8224
- "fire"
8225
- ]
8277
+ "description": "Key to press: a printable character, or a PETSCII token name (RETURN, F1…F8, UP, etc.).",
8278
+ "minLength": 1,
8279
+ "maxLength": 32
8226
8280
  },
8227
- "minItems": 0
8228
- },
8229
- "action": {
8230
- "type": "string",
8231
- "description": "press = hold until a release call; release = restore all bits; tap = press then release after durationMs.",
8232
- "enum": [
8233
- "press",
8234
- "release",
8235
- "tap"
8236
- ]
8281
+ "durationMs": {
8282
+ "type": [
8283
+ "integer",
8284
+ "null"
8285
+ ],
8286
+ "description": "Delay in ms after each queued key press before the next press (default 0 = no delay).",
8287
+ "minimum": 0,
8288
+ "maximum": 5000,
8289
+ "default": 0
8290
+ },
8291
+ "count": {
8292
+ "type": [
8293
+ "integer",
8294
+ "null"
8295
+ ],
8296
+ "description": "Number of times to press the key (default 1).",
8297
+ "minimum": 1,
8298
+ "maximum": 100,
8299
+ "default": 1
8300
+ }
8237
8301
  },
8238
- "durationMs": {
8239
- "type": [
8240
- "integer",
8241
- "null"
8242
- ],
8243
- "description": "Duration to hold for tap action in ms (default 80).",
8244
- "minimum": 10,
8245
- "maximum": 5000,
8246
- "default": 80
8247
- }
8248
- },
8302
+ "required": [
8303
+ "op",
8304
+ "key"
8305
+ ],
8306
+ "additionalProperties": false
8307
+ }
8308
+ },
8309
+ {
8310
+ "op": "joystick",
8311
+ "description": "Simulate joystick input by writing directly to CIA1 Port A/B registers.",
8249
8312
  "required": [
8250
- "op",
8251
8313
  "port",
8252
8314
  "controls",
8253
8315
  "action"
8254
8316
  ],
8255
- "additionalProperties": false
8317
+ "optional": [
8318
+ "durationMs"
8319
+ ],
8320
+ "inputSchema": {
8321
+ "type": "object",
8322
+ "description": "Simulate joystick input by writing directly to CIA1 Port A/B registers.",
8323
+ "properties": {
8324
+ "op": {
8325
+ "const": "joystick"
8326
+ },
8327
+ "port": {
8328
+ "type": "integer",
8329
+ "description": "Joystick port (1 = $DC01, 2 = $DC00).",
8330
+ "minimum": 1,
8331
+ "maximum": 2
8332
+ },
8333
+ "controls": {
8334
+ "type": "array",
8335
+ "description": "List of controls to activate simultaneously.",
8336
+ "items": {
8337
+ "type": "string",
8338
+ "description": "Control to activate.",
8339
+ "enum": [
8340
+ "up",
8341
+ "down",
8342
+ "left",
8343
+ "right",
8344
+ "fire"
8345
+ ]
8346
+ },
8347
+ "minItems": 0
8348
+ },
8349
+ "action": {
8350
+ "type": "string",
8351
+ "description": "press = hold until a release call; release = restore all bits; tap = press then release after durationMs.",
8352
+ "enum": [
8353
+ "press",
8354
+ "release",
8355
+ "tap"
8356
+ ]
8357
+ },
8358
+ "durationMs": {
8359
+ "type": [
8360
+ "integer",
8361
+ "null"
8362
+ ],
8363
+ "description": "Duration to hold for tap action in ms (default 80).",
8364
+ "minimum": 10,
8365
+ "maximum": 5000,
8366
+ "default": 80
8367
+ }
8368
+ },
8369
+ "required": [
8370
+ "op",
8371
+ "port",
8372
+ "controls",
8373
+ "action"
8374
+ ],
8375
+ "additionalProperties": false
8376
+ }
8256
8377
  }
8257
- ],
8258
- "discriminator": {
8259
- "propertyName": "op"
8260
- }
8378
+ ]
8261
8379
  },
8262
8380
  "_meta": {
8263
8381
  "domain": "input",
8264
8382
  "summary": "Types text, taps keys, and simulates joystick movements.",
8265
8383
  "lifecycle": "request-response",
8266
8384
  "resources": [
8267
- "c64://specs/assembly",
8268
- "c64://specs/memory-map"
8385
+ "c64://guide/bootstrap",
8386
+ "c64://memory/map",
8387
+ "c64://io/cia/spec"
8269
8388
  ],
8270
8389
  "prompts": [],
8271
8390
  "examples": [
package/mcp.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c64bridge",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Stdio-based Model Context Protocol (MCP) server that bridges Commodore 64 Ultimate hardware with AI agents.",
5
5
  "entry": "dist/index.js",
6
6
  "type": "stdio",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c64bridge",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "mcpName": "io.github.chrisgleissner/c64bridge",
5
5
  "description": "C64 Bridge MCP server for Commodore 64 Ultimate hardware and VICE emulator control",
6
6
  "type": "module",
@@ -18,7 +18,7 @@
18
18
  "mcp": "node scripts/invoke-bun.mjs src/mcp-server.ts",
19
19
  "mcp:node": "node dist/mcp-server.js",
20
20
  "mcp:build": "npm run build && node dist/mcp-server.js",
21
- "build": "node scripts/prebuild.mjs && tsc -p tsconfig.json && node scripts/postbuild.mjs && node scripts/invoke-bun.mjs scripts/update-readme.ts && node scripts/invoke-bun.mjs scripts/generate-mcp-interface.ts",
21
+ "build": "VICE_VISIBLE=false node scripts/prebuild.mjs && VICE_VISIBLE=false tsc -p tsconfig.json && VICE_VISIBLE=false node scripts/postbuild.mjs && VICE_VISIBLE=false node scripts/invoke-bun.mjs scripts/update-readme.ts && VICE_VISIBLE=false node scripts/invoke-bun.mjs scripts/generate-mcp-interface.ts",
22
22
  "lint": "npm run check",
23
23
  "test": "node scripts/invoke-bun.mjs scripts/run-tests.ts",
24
24
  "test:node": "node scripts/run-tests.mjs",
@@ -18,7 +18,7 @@ async function loadCoreModules() {
18
18
  }
19
19
 
20
20
  if (typeof globalThis.Bun !== "undefined") {
21
- ({ basicToPrg } = await import("../src/basicConverter.ts"));
21
+ ({ basicToPrg } = await import("../src/tools/translation/basicTokenizer.ts"));
22
22
  ({ loadConfig } = await import("../src/config.ts"));
23
23
  ({ C64Client } = await import("../src/c64Client.ts"));
24
24
  return;
@@ -26,7 +26,7 @@ async function loadCoreModules() {
26
26
 
27
27
  const distPath = resolve(projectRoot, "dist");
28
28
  try {
29
- ({ basicToPrg } = await import(pathToFileURL(resolve(distPath, "basicConverter.js")).href));
29
+ ({ basicToPrg } = await import(pathToFileURL(resolve(distPath, "tools/translation/basicTokenizer.js")).href));
30
30
  ({ loadConfig } = await import(pathToFileURL(resolve(distPath, "config.js")).href));
31
31
  ({ C64Client } = await import(pathToFileURL(resolve(distPath, "c64Client.js")).href));
32
32
  } catch (error) {
@@ -162,7 +162,8 @@ async function listRepoTestFiles(root) {
162
162
  return files;
163
163
  }
164
164
 
165
- function runCoverageLeg(leg, legDir, label, files) {
165
+ async function runCoverageLeg(leg, legDir, label, files) {
166
+ const knownTempFiles = new Set(await listCoverageTempFiles(coverageDir));
166
167
  return new Promise((resolve, reject) => {
167
168
  const outputPath = path.join(legDir, `${label}.lcov.info`);
168
169
  const args = [runner, "scripts/run-tests.ts", "--coverage", ...leg.args, ...files];
@@ -178,7 +179,7 @@ function runCoverageLeg(leg, legDir, label, files) {
178
179
  return;
179
180
  }
180
181
  try {
181
- await fs.copyFile(path.join(coverageDir, "lcov.info"), outputPath);
182
+ await copyCoverageOutput(coverageDir, outputPath, knownTempFiles);
182
183
  resolve(outputPath);
183
184
  } catch (error) {
184
185
  reject(error);
@@ -187,6 +188,56 @@ function runCoverageLeg(leg, legDir, label, files) {
187
188
  });
188
189
  }
189
190
 
191
+ export async function copyCoverageOutput(sourceDir, outputPath, knownTempFiles = new Set()) {
192
+ const stablePath = path.join(sourceDir, "lcov.info");
193
+ try {
194
+ await fs.copyFile(stablePath, outputPath);
195
+ return outputPath;
196
+ } catch (error) {
197
+ if (!isNodeErrorCode(error, "ENOENT")) {
198
+ throw error;
199
+ }
200
+ }
201
+
202
+ const tempFiles = await listCoverageTempFiles(sourceDir);
203
+ const newTempFiles = tempFiles.filter((filePath) => !knownTempFiles.has(filePath));
204
+ if (newTempFiles.length === 0) {
205
+ throw new Error(`Coverage run completed but no LCOV report was produced in ${sourceDir}`);
206
+ }
207
+
208
+ const newest = await newestFile(newTempFiles);
209
+ await fs.copyFile(newest, outputPath);
210
+ return outputPath;
211
+ }
212
+
213
+ async function listCoverageTempFiles(dir) {
214
+ let entries;
215
+ try {
216
+ entries = await fs.readdir(dir);
217
+ } catch (error) {
218
+ if (isNodeErrorCode(error, "ENOENT")) {
219
+ return [];
220
+ }
221
+ throw error;
222
+ }
223
+ return entries
224
+ .filter((entry) => /^\.lcov\.info\.[^.]+\.tmp$/.test(entry))
225
+ .map((entry) => path.join(dir, entry));
226
+ }
227
+
228
+ async function newestFile(filePaths) {
229
+ const stats = await Promise.all(filePaths.map(async (filePath) => ({
230
+ filePath,
231
+ mtimeMs: (await fs.stat(filePath)).mtimeMs,
232
+ })));
233
+ stats.sort((a, b) => b.mtimeMs - a.mtimeMs || a.filePath.localeCompare(b.filePath));
234
+ return stats[0].filePath;
235
+ }
236
+
237
+ function isNodeErrorCode(error, code) {
238
+ return Boolean(error && typeof error === "object" && error.code === code);
239
+ }
240
+
190
241
  async function readReport(filePath) {
191
242
  const text = await fs.readFile(filePath, "utf8");
192
243
  return parseReport(text);
@@ -313,4 +364,4 @@ function globToRegExp(glob) {
313
364
  }
314
365
  regex += "$";
315
366
  return new RegExp(regex);
316
- }
367
+ }
@@ -18,6 +18,7 @@ if (!fs.existsSync(defaultEmbeddingsDir)) {
18
18
  const env = {
19
19
  ...process.env,
20
20
  C64_TEST_TARGET: target,
21
+ VICE_VISIBLE: "false",
21
22
  };
22
23
 
23
24
  if (!env.RAG_EMBEDDINGS_DIR) {
@@ -142,6 +142,7 @@ export function buildMatrixEnv(
142
142
  const env: Record<string, string> = { ...envSource } as Record<string, string>;
143
143
  env.C64_MODE = platform;
144
144
  env.C64_TEST_TARGET = target === "device" ? "real" : "mock";
145
+ env.VICE_VISIBLE = "false";
145
146
 
146
147
  if (platform === "vice") {
147
148
  const useViceMock = target !== "device";
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.9.1",
11
+ "version": "0.9.2",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "c64bridge",
16
- "version": "0.9.1",
16
+ "version": "0.9.2",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  },