c64bridge 0.8.0 → 0.9.0

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 (79) hide show
  1. package/.github/prompts/steer.prompt.md +55 -0
  2. package/AGENTS.md +9 -7
  3. package/README.md +139 -95
  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 +921 -0
  12. package/dist/.tsbuildinfo +1 -1
  13. package/dist/device.js +86 -10
  14. package/dist/mcp-server.js +1 -1
  15. package/dist/prompts/registry.js +27 -25
  16. package/dist/rag/knowledgeIndex.js +112 -79
  17. package/dist/rag/resourceUris.js +93 -0
  18. package/dist/tools/audio.js +11 -11
  19. package/dist/tools/debug.js +1 -1
  20. package/dist/tools/developer.js +13 -13
  21. package/dist/tools/graphics.js +10 -10
  22. package/dist/tools/machineControl.js +5 -5
  23. package/dist/tools/memory.js +19 -11
  24. package/dist/tools/meta/index.js +1 -1
  25. package/dist/tools/printer.js +11 -12
  26. package/dist/tools/programRunners.js +16 -16
  27. package/dist/tools/rag.js +9 -9
  28. package/dist/tools/registry/config.js +1 -1
  29. package/dist/tools/registry/disk.js +1 -1
  30. package/dist/tools/registry/drive.js +1 -1
  31. package/dist/tools/registry/extract.js +1 -1
  32. package/dist/tools/registry/graphics.js +1 -1
  33. package/dist/tools/registry/memory.js +1 -1
  34. package/dist/tools/registry/platform.js +1 -1
  35. package/dist/tools/registry/printer.js +1 -1
  36. package/dist/tools/registry/program.js +1 -1
  37. package/dist/tools/registry/rag.js +1 -1
  38. package/dist/tools/registry/sound.js +1 -1
  39. package/dist/tools/registry/stream.js +1 -1
  40. package/dist/tools/registry/system.js +1 -1
  41. package/dist/tools/storage.js +15 -15
  42. package/dist/tools/streaming.js +3 -3
  43. package/dist/tools/types.js +41 -4
  44. package/dist/tools/vice.js +2 -5
  45. package/dist/vice/process.js +79 -2
  46. package/dist/vice/readiness.js +35 -0
  47. package/dist/vice/smokeOptions.js +44 -0
  48. package/dist/viceRunner.js +2 -0
  49. package/doc/developer.md +10 -2
  50. package/doc/plans/prompts/README.md +7 -7
  51. package/mcp/prompts.json +42 -59
  52. package/mcp/protocol-examples.json +2 -2
  53. package/mcp/resources.json +179 -166
  54. package/mcp/schemas/c64_config.schema.json +109 -326
  55. package/mcp/schemas/c64_debug.schema.json +158 -337
  56. package/mcp/schemas/c64_disk.schema.json +180 -262
  57. package/mcp/schemas/c64_drive.schema.json +31 -111
  58. package/mcp/schemas/c64_extract.schema.json +193 -291
  59. package/mcp/schemas/c64_graphics.schema.json +208 -308
  60. package/mcp/schemas/c64_memory.schema.json +94 -144
  61. package/mcp/schemas/c64_printer.schema.json +151 -205
  62. package/mcp/schemas/c64_program.schema.json +229 -367
  63. package/mcp/schemas/c64_rag.schema.json +25 -59
  64. package/mcp/schemas/c64_select_backend.schema.json +19 -26
  65. package/mcp/schemas/c64_sound.schema.json +346 -622
  66. package/mcp/schemas/c64_stream.schema.json +25 -54
  67. package/mcp/schemas/c64_system.schema.json +79 -212
  68. package/mcp/schemas/c64_vice.schema.json +24 -55
  69. package/mcp/server.json +1 -1
  70. package/mcp/tools.json +1935 -3443
  71. package/mcp.json +10 -9
  72. package/package.json +20 -15
  73. package/scripts/fetch-external-sources.mjs +1 -2
  74. package/scripts/prepare-release.mjs +59 -2
  75. package/scripts/run-coverage.mjs +26 -1
  76. package/scripts/run-tests.mjs +2 -2
  77. package/scripts/run-tests.ts +273 -52
  78. package/scripts/update-readme.ts +54 -2
  79. package/server.json +94 -0
package/mcp.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c64bridge",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
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",
@@ -9,11 +9,14 @@
9
9
  "mcp-server",
10
10
  "model context protocol",
11
11
  "openai",
12
- "ultimate64",
13
- "commodore64",
14
- "commodore 64",
12
+ "commodore",
15
13
  "c64",
16
14
  "c64u",
15
+ "commodore64",
16
+ "commodore 64",
17
+ "commodore 64 ultimate",
18
+ "ultimate64",
19
+ "ultimate 64",
17
20
  "sid",
18
21
  "basic",
19
22
  "assembly",
@@ -22,9 +25,7 @@
22
25
  "retrogaming",
23
26
  "vintage computing",
24
27
  "hardware integration",
25
- "c64 development",
26
- "commodore 64 ultimate",
27
- "ultimate 64"
28
+ "c64 development"
28
29
  ],
29
30
  "env": {
30
31
  "LOG_LEVEL": {
@@ -112,11 +113,11 @@
112
113
  "default": "120000000"
113
114
  },
114
115
  "VICE_BINARY": {
115
- "description": "VICE binary to launch for managed emulator sessions and audio capture",
116
+ "description": "VICE binary to launch for managed emulator sessions and audio capture; automatic search is used only when this override is missing or invalid",
116
117
  "default": "x64sc"
117
118
  },
118
119
  "VICE_DIRECTORY": {
119
- "description": "Override the VICE resource directory used for ROM and UI asset discovery",
120
+ "description": "Override the VICE resource directory used for ROM and UI asset discovery; automatic search is used only when this override is missing or invalid",
120
121
  "default": "auto-detect"
121
122
  },
122
123
  "VICE_HOST": {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "c64bridge",
3
- "version": "0.8.0",
4
- "description": "Local MCP server for controlling a Commodore 64 via Ultimate 64 REST API",
3
+ "version": "0.9.0",
4
+ "mcpName": "io.github.chrisgleissner/c64bridge",
5
+ "description": "C64 Bridge MCP server for Commodore 64 Ultimate hardware and VICE emulator control",
5
6
  "type": "module",
6
7
  "main": "dist/index.js",
7
8
  "packageManager": "bun@1.3.1",
@@ -21,9 +22,11 @@
21
22
  "lint": "npm run check",
22
23
  "test": "node scripts/invoke-bun.mjs scripts/run-tests.ts",
23
24
  "test:node": "node scripts/run-tests.mjs",
25
+ "test:vice:mock": "npm run test -- --platform=vice --target=mock",
26
+ "test:vice:device": "FORCE_XVFB=1 VICE_VISIBLE=false VICE_WARP=true npm run test -- --platform=vice --target=device",
24
27
  "coverage": "node scripts/run-coverage.mjs",
25
28
  "coverage:single": "node scripts/invoke-bun.mjs scripts/run-tests.ts --coverage",
26
- "test:matrix": "npm run test -- --platform=c64u --target=mock && npm run test -- --platform=vice --target=mock && npm run test -- --platform=vice --target=device",
29
+ "test:matrix": "npm run test -- --platform=c64u --target=mock && npm run test:vice:mock && npm run test:vice:device",
27
30
  "mcp:generate": "node scripts/invoke-bun.mjs scripts/generate-mcp-interface.ts",
28
31
  "check:package": "node scripts/check-package.mjs",
29
32
  "check:run-local": "bash scripts/run-mcp-check.sh local 5",
@@ -39,7 +42,8 @@
39
42
  "changelog:generate": "node scripts/generate-changelog.mjs",
40
43
  "release:prepare": "node scripts/prepare-release.mjs",
41
44
  "compare:rest": "node scripts/compare-rest-apis.mjs",
42
- "vice:smoke": "node scripts/invoke-bun.mjs test/vice/viceSmokeTest.ts"
45
+ "vice:smoke": "node scripts/invoke-bun.mjs test/vice/viceSmokeTest.ts",
46
+ "vice:smoke:visible": "node scripts/invoke-bun.mjs test/vice/viceSmokeTest.ts --visible-demo"
43
47
  },
44
48
  "keywords": [
45
49
  "mcp",
@@ -58,27 +62,28 @@
58
62
  "node": ">=24"
59
63
  },
60
64
  "dependencies": {
61
- "@modelcontextprotocol/sdk": "^1.20.2",
62
- "axios": "^1.7.7",
65
+ "@modelcontextprotocol/sdk": "^1.29.0",
66
+ "axios": "^1.16.0",
63
67
  "date-fns": "^4.1.0",
64
- "fastify": "^5.6.1",
65
- "jimp": "^1.6.0",
66
- "meyda": "^5.6.2",
67
- "octokit": "^5.0.4",
68
- "pitchfinder": "^2.2.0",
68
+ "fastify": "^5.8.5",
69
+ "jimp": "^1.6.1",
70
+ "meyda": "^5.6.3",
71
+ "octokit": "^5.0.5",
72
+ "pitchfinder": "^2.3.4",
69
73
  "reflect-metadata": "^0.2.2",
70
- "yaml": "^2.6.0"
74
+ "yaml": "^2.8.4"
71
75
  },
72
76
  "optionalDependencies": {
73
77
  "naudiodon": "^2.3.6"
74
78
  },
75
79
  "devDependencies": {
76
- "@types/node": "^24.12.0",
77
- "ts-node": "^10.9.2",
78
- "typescript": "^5.9.3"
80
+ "@types/node": "^25.6.0",
81
+ "tsx": "^4.21.0",
82
+ "typescript": "^6.0.3"
79
83
  },
80
84
  "files": [
81
85
  "mcp.json",
86
+ "server.json",
82
87
  "AGENTS.md",
83
88
  "LICENSE",
84
89
  "README.md",
@@ -7,8 +7,7 @@ import { register } from 'node:module';
7
7
  async function main() {
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  const projectRoot = path.resolve(__dirname, '..');
10
- const tsNodeLoader = pathToFileURL(path.join(projectRoot, 'node_modules', 'ts-node', 'esm.mjs')).href;
11
- register(tsNodeLoader, pathToFileURL(projectRoot));
10
+ register("tsx/esm", pathToFileURL(projectRoot));
12
11
  const { fetchFromCsv } = await import('../src/rag/externalFetcher.ts');
13
12
  // Resolve sources.csv from env, dist/rag, or src/rag (in that order)
14
13
  let csvPath = process.env.RAG_SOURCES_CSV;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync } from 'node:child_process';
3
- import { readFile, writeFile } from 'node:fs/promises';
3
+ import { access, readFile, writeFile } from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import process from 'node:process';
6
6
 
@@ -16,6 +16,27 @@ const updateJsonFile = async (relativePath, updater) => {
16
16
  await writeFile(filePath, JSON.stringify(updated, null, 2) + '\n');
17
17
  };
18
18
 
19
+ const fileExists = async (relativePath) => {
20
+ try {
21
+ await access(path.resolve(relativePath));
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ };
27
+
28
+ const getRepositoryMetadata = (repository) => {
29
+ const url = typeof repository === 'string' ? repository : repository?.url;
30
+ if (!url) {
31
+ return undefined;
32
+ }
33
+
34
+ return {
35
+ url,
36
+ source: url.includes('github.com') ? 'github' : undefined,
37
+ };
38
+ };
39
+
19
40
  const normaliseVersion = (value) => value.replace(/^v/, '');
20
41
 
21
42
  const isExactVersion = (value) => /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(value);
@@ -72,6 +93,42 @@ await updateJsonFile('mcp.json', async (data) => ({
72
93
  version: newVersion,
73
94
  }));
74
95
 
96
+ if (await fileExists('server.json')) {
97
+ await updateJsonFile('server.json', async (data) => {
98
+ const packages = Array.isArray(data.packages) ? [...data.packages] : [];
99
+ const primaryPackage = packages[0] ?? {
100
+ registryType: 'npm',
101
+ transport: {
102
+ type: 'stdio',
103
+ },
104
+ };
105
+
106
+ packages[0] = {
107
+ ...primaryPackage,
108
+ identifier: pkg.name,
109
+ version: newVersion,
110
+ transport: primaryPackage.transport ?? {
111
+ type: 'stdio',
112
+ },
113
+ };
114
+
115
+ const repository = getRepositoryMetadata(pkg.repository);
116
+
117
+ return {
118
+ ...data,
119
+ name: pkg.mcpName ?? data.name,
120
+ version: newVersion,
121
+ repository: repository
122
+ ? {
123
+ ...(data.repository ?? {}),
124
+ ...repository,
125
+ }
126
+ : data.repository,
127
+ packages,
128
+ };
129
+ });
130
+ }
131
+
75
132
  // Manifest generation removed (runtime discovery via MCP stdio)
76
133
 
77
134
  // Update CHANGELOG.md from commits since last tag using Conventional Commits subjects.
@@ -88,4 +145,4 @@ if (skippedVersionBump && hasExistingChangelogEntry) {
88
145
  }
89
146
 
90
147
  console.log(`Release metadata updated to ${newVersion}.`);
91
- console.log('Next steps: commit the changes, open a PR, and tag the merged commit.');
148
+ console.log('Next steps: commit and push the changes, then create the release tag from GitHub once main is ready.');
@@ -13,6 +13,22 @@ const runner = path.join(repoRoot, "scripts", "invoke-bun.mjs");
13
13
  const configPath = path.join(repoRoot, ".c8rc.json");
14
14
  const testRoot = path.join(repoRoot, "test");
15
15
  const DEFAULT_COVERAGE_SHARD_SIZE = 12;
16
+ const DEFAULT_VICE_MOCK_TEST_FILES = [
17
+ "test/device.test.mjs",
18
+ "test/viceIntegration.test.mjs",
19
+ "test/viceModule.test.mjs",
20
+ "test/groupedToolsShims.test.mjs",
21
+ "test/toolsTypes.test.mjs",
22
+ "test/platformRegistry.test.mjs",
23
+ "test/meta/program.test.mjs",
24
+ "test/mcpServerIntegration.test.mjs",
25
+ "test/c64Client.test.mjs",
26
+ "test/vice/viceSmokeTest.ts",
27
+ ];
28
+ const DEFAULT_VICE_DEVICE_TEST_FILES = [
29
+ "test/device.test.mjs",
30
+ "test/vice/viceSmokeTest.ts",
31
+ ];
16
32
 
17
33
  const legs = [
18
34
  { name: "c64u-mock", args: ["--platform=c64u", "--target=mock"] },
@@ -50,11 +66,20 @@ export async function main() {
50
66
  const allTestFiles = await listRepoTestFiles(testRoot);
51
67
  const isolatedTestSet = new Set(isolatedTests);
52
68
  const defaultTestFiles = allTestFiles.filter((f) => !isolatedTestSet.has(f));
53
- const coverageBatches = buildCoverageBatches(defaultTestFiles, [...supplementalTests, ...isolatedTests], process.env);
54
69
  for (const leg of legs) {
55
70
  const legDir = path.join(coverageDir, "matrix", leg.name);
56
71
  await fs.mkdir(legDir, { recursive: true });
57
72
 
73
+ const legDefaultFiles = leg.name === "vice-mock"
74
+ ? DEFAULT_VICE_MOCK_TEST_FILES
75
+ : leg.name === "vice-device"
76
+ ? DEFAULT_VICE_DEVICE_TEST_FILES
77
+ : defaultTestFiles;
78
+ const legSupplementalTests = leg.name.startsWith("vice")
79
+ ? []
80
+ : [...supplementalTests, ...isolatedTests];
81
+ const coverageBatches = buildCoverageBatches(legDefaultFiles, legSupplementalTests, process.env);
82
+
58
83
  const reports = [];
59
84
  for (const batch of coverageBatches) {
60
85
  reports.push(await runCoverageLeg(leg, legDir, batch.label, batch.files));
@@ -8,7 +8,7 @@ import { fileURLToPath } from "node:url";
8
8
  export const DEFAULT_TARGET = "mock";
9
9
 
10
10
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- const registerDataUri = "data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));";
11
+ const tsLoader = "tsx/esm";
12
12
  const { nodeArgs, target, explicitBaseUrl } = buildNodeTestArgs(process.argv.slice(2));
13
13
  const defaultEmbeddingsDir = path.join(repoRoot, "artifacts", "test-embeddings");
14
14
  if (!fs.existsSync(defaultEmbeddingsDir)) {
@@ -180,7 +180,7 @@ function isMainModule(moduleUrl) {
180
180
 
181
181
  export function buildNodeTestArgs(args, root = repoRoot) {
182
182
  const defaultTestFiles = listRepoTestFiles(path.join(root, "test"), root);
183
- const nodeArgs = ["--import", registerDataUri, "--test"];
183
+ const nodeArgs = ["--import", tsLoader, "--test"];
184
184
  let target = DEFAULT_TARGET;
185
185
  let explicitBaseUrl = null;
186
186
  let hasExplicitTestFiles = false;