c64bridge 0.5.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 (138) hide show
  1. package/.github/prompts/README.md +6 -0
  2. package/.github/prompts/assembly-routine-session.prompt.md +13 -0
  3. package/.github/prompts/basic-program-session.prompt.md +13 -0
  4. package/.github/prompts/drive-management-session.prompt.md +13 -0
  5. package/.github/prompts/petscii-art-session.prompt.md +13 -0
  6. package/.github/prompts/play-classic-game.prompt.md +13 -0
  7. package/.github/prompts/sid-music-session.prompt.md +13 -0
  8. package/AGENTS.md +173 -0
  9. package/LICENSE +339 -0
  10. package/README.md +582 -0
  11. package/data/README.md +16 -0
  12. package/data/api/basic-api-spec.md +154 -0
  13. package/data/api/kernal-api-spec.md +68 -0
  14. package/data/assembly/assembly-spec.md +196 -0
  15. package/data/assembly/examples/effects/sinewave.asm +5 -0
  16. package/data/assembly/examples/graphics/sprite/mover.asm +15 -0
  17. package/data/assembly/examples/sid-file-structure.md +8 -0
  18. package/data/assembly/examples/sid-overview.md +11 -0
  19. package/data/audio/examples/Biosphere.sid +0 -0
  20. package/data/audio/sid-file-structure.md +91 -0
  21. package/data/audio/sid-programming-best-practices.md +198 -0
  22. package/data/audio/sid-spec.md +166 -0
  23. package/data/audio/sidwave.md +131 -0
  24. package/data/audio/sidwave.schema.json +64 -0
  25. package/data/basic/basic-spec.md +230 -0
  26. package/data/basic/examples/audio/alle-meine-entchen-expressive.bas +41 -0
  27. package/data/basic/examples/audio/c-major-scale-pleasant.bas +29 -0
  28. package/data/basic/examples/io/joystick.bas +5 -0
  29. package/data/basic/examples/video/bounce.bas +8 -0
  30. package/data/basic/examples/video/entchen-petscii.bas +16 -0
  31. package/data/basic/examples/video/hello-world.bas +4 -0
  32. package/data/basic/examples/video/wave.bas +7 -0
  33. package/data/context/bootstrap.md +148 -0
  34. package/data/context/chat.md +36 -0
  35. package/data/drive/drive-spec.md +215 -0
  36. package/data/embeddings_asm.json +1 -0
  37. package/data/embeddings_basic.json +1 -0
  38. package/data/embeddings_hardware.json +1 -0
  39. package/data/embeddings_mixed.json +1 -0
  40. package/data/embeddings_other.json +1 -0
  41. package/data/io/cia-spec.md +115 -0
  42. package/data/io/io-spec.md +124 -0
  43. package/data/memory/kernal-memory-map.md +227 -0
  44. package/data/memory/low-memory-map.md +133 -0
  45. package/data/memory/memory-map.md +186 -0
  46. package/data/printer/printer-commodore-bitmap.md +31 -0
  47. package/data/printer/printer-commodore.md +28 -0
  48. package/data/printer/printer-epson-bitmap.md +29 -0
  49. package/data/printer/printer-epson.md +30 -0
  50. package/data/printer/printer-prompts.md +29 -0
  51. package/data/printer/printer-spec.md +180 -0
  52. package/data/video/character-set.csv +257 -0
  53. package/data/video/vic-spec.md +276 -0
  54. package/dist/assemblyConverter.js +1299 -0
  55. package/dist/audio/record_and_analyze_audio.js +292 -0
  56. package/dist/basicConverter.js +193 -0
  57. package/dist/bootstrap/stdio-logger.js +32 -0
  58. package/dist/c64Client.js +1052 -0
  59. package/dist/chargen.js +197 -0
  60. package/dist/config.js +142 -0
  61. package/dist/context.js +157 -0
  62. package/dist/device.js +413 -0
  63. package/dist/index.js +7 -0
  64. package/dist/knowledge.js +454 -0
  65. package/dist/logger.js +182 -0
  66. package/dist/loggingHttpClient.js +88 -0
  67. package/dist/mcp-server.js +426 -0
  68. package/dist/petscii.js +135 -0
  69. package/dist/petsciiArt.js +356 -0
  70. package/dist/platform.js +82 -0
  71. package/dist/prompts/registry.js +560 -0
  72. package/dist/rag/discover.config.json +41 -0
  73. package/dist/rag/discover.js +401 -0
  74. package/dist/rag/embeddings.js +60 -0
  75. package/dist/rag/externalFetcher.js +934 -0
  76. package/dist/rag/indexer.js +770 -0
  77. package/dist/rag/init.js +139 -0
  78. package/dist/rag/knowledgeIndex.js +361 -0
  79. package/dist/rag/loggingRetriever.js +46 -0
  80. package/dist/rag/rateLimiter.js +127 -0
  81. package/dist/rag/retriever.js +74 -0
  82. package/dist/rag/sources.csv +31 -0
  83. package/dist/rag/types.js +5 -0
  84. package/dist/rag/urlUtils.js +38 -0
  85. package/dist/sidplayRunner.js +240 -0
  86. package/dist/sidwave.js +101 -0
  87. package/dist/sidwaveCompiler.js +329 -0
  88. package/dist/tools/audio.js +944 -0
  89. package/dist/tools/developer.js +515 -0
  90. package/dist/tools/errors.js +59 -0
  91. package/dist/tools/graphics.js +469 -0
  92. package/dist/tools/machineControl.js +267 -0
  93. package/dist/tools/memory.js +224 -0
  94. package/dist/tools/printer.js +482 -0
  95. package/dist/tools/programRunners.js +315 -0
  96. package/dist/tools/rag.js +153 -0
  97. package/dist/tools/registry.js +58 -0
  98. package/dist/tools/responses.js +36 -0
  99. package/dist/tools/schema.js +304 -0
  100. package/dist/tools/storage.js +740 -0
  101. package/dist/tools/streaming.js +132 -0
  102. package/dist/tools/types.js +99 -0
  103. package/dist/viceRunner.js +239 -0
  104. package/doc/MCP_SETUP.md +153 -0
  105. package/doc/README.md +7 -0
  106. package/doc/developer.md +312 -0
  107. package/doc/img/logo.png +0 -0
  108. package/doc/img/prompts/duck_petscii.png +0 -0
  109. package/doc/img/prompts/duck_song.png +0 -0
  110. package/doc/img/vscode/vscode-copilot-c64-chat-mode.png +0 -0
  111. package/doc/img/vscode/vscode-copilot-hello-world.png +0 -0
  112. package/doc/prompts/README.md +96 -0
  113. package/doc/prompts/user-prompt-ideas.md +147 -0
  114. package/doc/rest/c64-openapi.yaml +979 -0
  115. package/doc/rest/c64-rest-api.md +106 -0
  116. package/doc/troubleshooting-mcp.md +177 -0
  117. package/generated/c64/index.js +857 -0
  118. package/mcp.json +106 -0
  119. package/package.json +79 -0
  120. package/scripts/c64-cli.mjs +142 -0
  121. package/scripts/c64-tool.sh +83 -0
  122. package/scripts/check-package.mjs +89 -0
  123. package/scripts/cli.js +61 -0
  124. package/scripts/fetch-external-sources.mjs +67 -0
  125. package/scripts/generate-changelog.mjs +131 -0
  126. package/scripts/postbuild.mjs +101 -0
  127. package/scripts/prepare-release.mjs +58 -0
  128. package/scripts/rag-rebuild.mjs +53 -0
  129. package/scripts/register-ts-node.mjs +6 -0
  130. package/scripts/run-mcp-check.sh +104 -0
  131. package/scripts/run-tests.mjs +195 -0
  132. package/scripts/sid2wav.sh +94 -0
  133. package/scripts/smoke-run.sh +65 -0
  134. package/scripts/start-mock-c64.mjs +51 -0
  135. package/scripts/start-mock.mjs +31 -0
  136. package/scripts/start.mjs +72 -0
  137. package/scripts/update-readme.ts +164 -0
  138. package/scripts/verify-package.mjs +147 -0
@@ -0,0 +1,315 @@
1
+ import { AssemblyError } from "../assemblyConverter.js";
2
+ import { defineToolModule } from "./types.js";
3
+ import { objectSchema, stringSchema } from "./schema.js";
4
+ import { textResult } from "./responses.js";
5
+ import { ToolExecutionError, ToolError, ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
6
+ function extractFailureDetails(details) {
7
+ if (details === undefined || details === null) {
8
+ return undefined;
9
+ }
10
+ if (typeof details === "object") {
11
+ return details;
12
+ }
13
+ return { value: details };
14
+ }
15
+ function toRecord(details) {
16
+ if (details === undefined || details === null) {
17
+ return undefined;
18
+ }
19
+ if (typeof details === "object") {
20
+ return details;
21
+ }
22
+ return { value: details };
23
+ }
24
+ const uploadBasicArgsSchema = objectSchema({
25
+ description: "Arguments for uploading and running a BASIC program.",
26
+ properties: {
27
+ program: stringSchema({
28
+ description: "Commodore BASIC v2 program source to upload and run.",
29
+ minLength: 1,
30
+ }),
31
+ },
32
+ required: ["program"],
33
+ additionalProperties: false,
34
+ });
35
+ const uploadAsmArgsSchema = objectSchema({
36
+ description: "Arguments for uploading and running a 6502/6510 assembly program.",
37
+ properties: {
38
+ program: stringSchema({
39
+ description: "Assembly source that will be assembled to a PRG and executed.",
40
+ minLength: 1,
41
+ }),
42
+ },
43
+ required: ["program"],
44
+ additionalProperties: false,
45
+ });
46
+ const prgFileArgsSchema = objectSchema({
47
+ description: "Arguments for loading or running a PRG that already exists on the Ultimate filesystem.",
48
+ properties: {
49
+ path: stringSchema({
50
+ description: "Absolute or Ultimate filesystem path to the PRG file (e.g. //USB0/demo.prg).",
51
+ minLength: 1,
52
+ }),
53
+ },
54
+ required: ["path"],
55
+ additionalProperties: false,
56
+ });
57
+ const crtFileArgsSchema = objectSchema({
58
+ description: "Arguments for running a CRT image stored on the Ultimate filesystem.",
59
+ properties: {
60
+ path: stringSchema({
61
+ description: "Absolute or Ultimate filesystem path to the CRT file (e.g. //USB0/game.crt).",
62
+ minLength: 1,
63
+ }),
64
+ },
65
+ required: ["path"],
66
+ additionalProperties: false,
67
+ });
68
+ export const programRunnersModule = defineToolModule({
69
+ domain: "programs",
70
+ summary: "Program uploaders, runners, and compilation workflows for BASIC, assembly, and PRG files.",
71
+ resources: [
72
+ "c64://context/bootstrap",
73
+ "c64://specs/basic",
74
+ "c64://specs/assembly",
75
+ ],
76
+ prompts: ["basic-program", "assembly-program"],
77
+ defaultTags: ["programs", "execution"],
78
+ workflowHints: [
79
+ "Choose BASIC or assembly uploaders based on the language you just generated for the user.",
80
+ "Prefer PRG or CRT runners when the user supplies an Ultimate filesystem path instead of source text.",
81
+ ],
82
+ tools: [
83
+ {
84
+ name: "upload_and_run_basic",
85
+ description: "Upload a BASIC program to the C64 and execute it immediately. Refer to c64://specs/basic for syntax and device I/O.",
86
+ summary: "Uploads Commodore BASIC v2 source and runs it via Ultimate 64 firmware.",
87
+ inputSchema: uploadBasicArgsSchema.jsonSchema,
88
+ relatedResources: ["c64://specs/basic", "c64://context/bootstrap"],
89
+ relatedPrompts: ["basic-program"],
90
+ tags: ["basic", "execution"],
91
+ prerequisites: ["read_screen"],
92
+ examples: [
93
+ {
94
+ name: "Hello loop",
95
+ description: "Print HELLO in a loop",
96
+ arguments: { program: "10 PRINT \"HELLO\"\n20 GOTO 10" },
97
+ },
98
+ ],
99
+ workflowHints: [
100
+ "Invoke right after you generate BASIC source so it runs on the C64 without extra user steps.",
101
+ "Ensure the program includes line numbers and uppercase keywords before calling the tool.",
102
+ ],
103
+ supportedPlatforms: ["c64u", "vice"],
104
+ async execute(args, ctx) {
105
+ try {
106
+ const parsed = uploadBasicArgsSchema.parse(args);
107
+ ctx.logger.info("Uploading BASIC program", { sourceLength: parsed.program.length });
108
+ const result = await ctx.client.uploadAndRunBasic(parsed.program);
109
+ if (!result.success) {
110
+ throw new ToolExecutionError("C64 firmware reported failure while running BASIC program", {
111
+ details: extractFailureDetails(result.details),
112
+ });
113
+ }
114
+ const base = textResult("BASIC program uploaded and executed successfully.", {
115
+ success: true,
116
+ details: result.details ?? null,
117
+ });
118
+ return base;
119
+ }
120
+ catch (error) {
121
+ if (error instanceof ToolError) {
122
+ return toolErrorResult(error);
123
+ }
124
+ return unknownErrorResult(error);
125
+ }
126
+ },
127
+ },
128
+ {
129
+ name: "upload_and_run_asm",
130
+ description: "Assemble 6502/6510 source code, upload the PRG, and run it immediately. See c64://specs/assembly.",
131
+ summary: "Compiles assembly to a PRG and executes it on the C64 via Ultimate 64 firmware.",
132
+ inputSchema: uploadAsmArgsSchema.jsonSchema,
133
+ relatedResources: ["c64://specs/assembly", "c64://context/bootstrap"],
134
+ relatedPrompts: ["assembly-program"],
135
+ tags: ["assembly", "execution"],
136
+ prerequisites: ["read_screen"],
137
+ examples: [
138
+ {
139
+ name: "Set screen char",
140
+ description: "Write 1 to $0400 then RTS",
141
+ arguments: { program: ".org $0801\nstart: lda #$01\n sta $0400\n rts" },
142
+ },
143
+ ],
144
+ workflowHints: [
145
+ "Use when the user requests to run new 6502 code; surface any assembler diagnostics in your reply.",
146
+ "Mention the entry routine or important addresses after execution so the user can continue debugging.",
147
+ ],
148
+ supportedPlatforms: ["c64u", "vice"],
149
+ async execute(args, ctx) {
150
+ try {
151
+ const parsed = uploadAsmArgsSchema.parse(args);
152
+ ctx.logger.info("Uploading assembly program", { sourceLength: parsed.program.length });
153
+ const result = await ctx.client.uploadAndRunAsm(parsed.program);
154
+ if (!result.success) {
155
+ throw new ToolExecutionError("C64 firmware reported failure while running assembly program", {
156
+ details: extractFailureDetails(result.details),
157
+ });
158
+ }
159
+ return textResult("Assembly program assembled, uploaded, and executed successfully.", {
160
+ success: true,
161
+ details: result.details ?? null,
162
+ });
163
+ }
164
+ catch (error) {
165
+ if (error instanceof ToolError) {
166
+ return toolErrorResult(error);
167
+ }
168
+ if (error instanceof AssemblyError) {
169
+ const { file, line } = error.location;
170
+ const validationError = new ToolValidationError("Assembly failed", {
171
+ details: {
172
+ file,
173
+ line,
174
+ message: error.message,
175
+ },
176
+ cause: error,
177
+ });
178
+ return toolErrorResult(validationError);
179
+ }
180
+ return unknownErrorResult(error);
181
+ }
182
+ },
183
+ },
184
+ {
185
+ name: "load_prg_file",
186
+ description: "Load a PRG into C64 memory without executing it.",
187
+ summary: "Instructs the Ultimate firmware to transfer a PRG into memory without RUN.",
188
+ inputSchema: prgFileArgsSchema.jsonSchema,
189
+ relatedResources: ["c64://context/bootstrap"],
190
+ tags: ["programs", "file"],
191
+ prerequisites: ["drives_list"],
192
+ examples: [
193
+ {
194
+ name: "Load PRG from USB",
195
+ description: "Load without running",
196
+ arguments: { path: "//USB0/demo.prg" },
197
+ },
198
+ ],
199
+ workflowHints: [
200
+ "Stage PRG files without running when the user wants to inspect memory first.",
201
+ "Confirm the Ultimate filesystem path (e.g. //USB0/demo.prg) is accessible before invoking.",
202
+ ],
203
+ async execute(args, ctx) {
204
+ try {
205
+ const parsed = prgFileArgsSchema.parse(args ?? {});
206
+ ctx.logger.info("Loading PRG file", { path: parsed.path });
207
+ const result = await ctx.client.loadPrgFile(parsed.path);
208
+ if (!result.success) {
209
+ throw new ToolExecutionError("C64 firmware reported failure while loading PRG", {
210
+ details: extractFailureDetails(result.details),
211
+ });
212
+ }
213
+ return textResult(`PRG ${parsed.path} loaded into memory.`, {
214
+ success: true,
215
+ path: parsed.path,
216
+ details: toRecord(result.details) ?? null,
217
+ });
218
+ }
219
+ catch (error) {
220
+ if (error instanceof ToolError) {
221
+ return toolErrorResult(error);
222
+ }
223
+ return unknownErrorResult(error);
224
+ }
225
+ },
226
+ },
227
+ {
228
+ name: "run_prg_file",
229
+ description: "Run a PRG located on the Ultimate filesystem without uploading source.",
230
+ summary: "Loads and executes a PRG file residing on attached storage.",
231
+ inputSchema: prgFileArgsSchema.jsonSchema,
232
+ relatedResources: ["c64://context/bootstrap"],
233
+ tags: ["programs", "execution", "file"],
234
+ workflowHints: [
235
+ "Call when the user provides a PRG path and expects immediate execution without compiling.",
236
+ "Mention that firmware issues a RUN so the user knows the machine state changed.",
237
+ ],
238
+ supportedPlatforms: ["c64u", "vice"],
239
+ prerequisites: ["drives_list"],
240
+ examples: [
241
+ {
242
+ name: "Run PRG from USB",
243
+ description: "Load and RUN",
244
+ arguments: { path: "//USB0/demo.prg" },
245
+ },
246
+ ],
247
+ async execute(args, ctx) {
248
+ try {
249
+ const parsed = prgFileArgsSchema.parse(args ?? {});
250
+ ctx.logger.info("Running PRG file", { path: parsed.path });
251
+ const result = await ctx.client.runPrgFile(parsed.path);
252
+ if (!result.success) {
253
+ throw new ToolExecutionError("C64 firmware reported failure while running PRG", {
254
+ details: extractFailureDetails(result.details),
255
+ });
256
+ }
257
+ return textResult(`PRG ${parsed.path} loaded and executed.`, {
258
+ success: true,
259
+ path: parsed.path,
260
+ details: toRecord(result.details) ?? null,
261
+ });
262
+ }
263
+ catch (error) {
264
+ if (error instanceof ToolError) {
265
+ return toolErrorResult(error);
266
+ }
267
+ return unknownErrorResult(error);
268
+ }
269
+ },
270
+ },
271
+ {
272
+ name: "run_crt_file",
273
+ description: "Run a cartridge image stored on the Ultimate filesystem.",
274
+ summary: "Mounts and autostarts the specified CRT file through the firmware.",
275
+ inputSchema: crtFileArgsSchema.jsonSchema,
276
+ relatedResources: ["c64://context/bootstrap"],
277
+ tags: ["programs", "cartridge"],
278
+ workflowHints: [
279
+ "Use for cartridge images and remind the user that the machine will reset into the CRT.",
280
+ "Suggest capturing the screen afterwards if they need to verify the cartridge booted.",
281
+ ],
282
+ prerequisites: ["drives_list"],
283
+ examples: [
284
+ {
285
+ name: "Start CRT",
286
+ description: "Mount and run game.crt",
287
+ arguments: { path: "//USB0/game.crt" },
288
+ },
289
+ ],
290
+ async execute(args, ctx) {
291
+ try {
292
+ const parsed = crtFileArgsSchema.parse(args ?? {});
293
+ ctx.logger.info("Running CRT file", { path: parsed.path });
294
+ const result = await ctx.client.runCrtFile(parsed.path);
295
+ if (!result.success) {
296
+ throw new ToolExecutionError("C64 firmware reported failure while running CRT", {
297
+ details: extractFailureDetails(result.details),
298
+ });
299
+ }
300
+ return textResult(`CRT ${parsed.path} mounted and started.`, {
301
+ success: true,
302
+ path: parsed.path,
303
+ details: toRecord(result.details) ?? null,
304
+ });
305
+ }
306
+ catch (error) {
307
+ if (error instanceof ToolError) {
308
+ return toolErrorResult(error);
309
+ }
310
+ return unknownErrorResult(error);
311
+ }
312
+ },
313
+ },
314
+ ],
315
+ });
@@ -0,0 +1,153 @@
1
+ import { listKnowledgeResources } from "../rag/knowledgeIndex.js";
2
+ import { defineToolModule } from "./types.js";
3
+ import { objectSchema, optionalSchema, stringSchema, numberSchema } from "./schema.js";
4
+ import { textResult } from "./responses.js";
5
+ import { ToolError, ToolExecutionError, toolErrorResult, unknownErrorResult, } from "./errors.js";
6
+ const ragRetrieveArgsSchema = objectSchema({
7
+ description: "Retrieve domain-specific references from the local C64 knowledge base using embeddings.",
8
+ properties: {
9
+ q: stringSchema({
10
+ description: "Natural-language query describing the desired BASIC or assembly guidance.",
11
+ minLength: 3,
12
+ }),
13
+ k: optionalSchema(numberSchema({
14
+ description: "Maximum number of references to return (1-12).",
15
+ integer: true,
16
+ minimum: 1,
17
+ maximum: 12,
18
+ default: 3,
19
+ })),
20
+ },
21
+ required: ["q"],
22
+ additionalProperties: false,
23
+ });
24
+ function createRagTool(language, options) {
25
+ const primaryResourceUris = language === "basic"
26
+ ? ["c64://specs/basic", "c64://context/bootstrap"]
27
+ : ["c64://specs/assembly", "c64://context/bootstrap"];
28
+ return {
29
+ name: language === "basic" ? "rag_retrieve_basic" : "rag_retrieve_asm",
30
+ description: language === "basic"
31
+ ? "Retrieve BASIC references from local knowledge. See c64://specs/basic before coding."
32
+ : "Retrieve 6502/6510 assembly references from local knowledge. See c64://specs/assembly.",
33
+ summary: options.summary,
34
+ inputSchema: ragRetrieveArgsSchema.jsonSchema,
35
+ relatedResources: language === "basic"
36
+ ? ["c64://specs/basic"]
37
+ : ["c64://specs/assembly"],
38
+ relatedPrompts: language === "basic"
39
+ ? ["basic-program"]
40
+ : ["assembly-program"],
41
+ tags: options.tags,
42
+ prerequisites: [],
43
+ examples: [
44
+ {
45
+ name: language === "basic" ? "BASIC PRINT" : "Raster IRQ",
46
+ description: language === "basic" ? "Find PRINT and device usage" : "Find stable raster IRQ snippet",
47
+ arguments: language === "basic"
48
+ ? { q: "PRINT to device 4 form feed", k: 3 }
49
+ : { q: "stable raster irq acknowledge d019", k: 3 },
50
+ },
51
+ ],
52
+ workflowHints: language === "basic"
53
+ ? [
54
+ "Use when the user needs BASIC examples or syntax reminders before coding.",
55
+ "Summarise key references so the user knows where to look next.",
56
+ ]
57
+ : [
58
+ "Call for assembly patterns, addressing tricks, or hardware explanations before writing code.",
59
+ "Highlight registers or memory addresses found in the returned references.",
60
+ ],
61
+ async execute(args, ctx) {
62
+ try {
63
+ const parsed = ragRetrieveArgsSchema.parse(args ?? {});
64
+ const limit = parsed.k ?? 3;
65
+ ctx.logger.debug("Retrieving RAG references", {
66
+ language,
67
+ queryLength: parsed.q.length,
68
+ limit,
69
+ });
70
+ let refs;
71
+ try {
72
+ refs = await ctx.rag.retrieve(parsed.q, limit, language);
73
+ }
74
+ catch (error) {
75
+ throw new ToolExecutionError("RAG retrieval failed", { cause: error });
76
+ }
77
+ const resources = listKnowledgeResources();
78
+ const primaryResources = primaryResourceUris
79
+ .map((uri) => resources.find((entry) => entry.uri === uri))
80
+ .filter((entry) => Boolean(entry));
81
+ const ragCount = refs.length;
82
+ const primaryLines = primaryResources.length
83
+ ? primaryResources.map((entry) => `- ${entry.name} (${entry.uri}) — ${entry.metadata.summary}`)
84
+ : ["- No curated resources found."];
85
+ const ragLines = ragCount
86
+ ? refs.map((ref, index) => `${index + 1}. ${ref}`)
87
+ : ["No supplemental RAG references were found."];
88
+ const text = [
89
+ "Primary knowledge resources (consult these first):",
90
+ ...primaryLines,
91
+ "",
92
+ "Supplemental RAG references:",
93
+ ...ragLines,
94
+ ].join("\n");
95
+ const base = textResult(text, {
96
+ success: true,
97
+ language,
98
+ limit,
99
+ queryLength: parsed.q.length,
100
+ count: ragCount,
101
+ });
102
+ return {
103
+ ...base,
104
+ structuredContent: {
105
+ type: "json",
106
+ data: {
107
+ primaryResources: primaryResources.map((entry) => ({
108
+ uri: entry.uri,
109
+ name: entry.name,
110
+ summary: entry.metadata.summary,
111
+ })),
112
+ refs,
113
+ },
114
+ },
115
+ };
116
+ }
117
+ catch (error) {
118
+ if (error instanceof ToolError) {
119
+ return toolErrorResult(error);
120
+ }
121
+ return unknownErrorResult(error);
122
+ }
123
+ },
124
+ };
125
+ }
126
+ export const ragModule = defineToolModule({
127
+ domain: "rag",
128
+ summary: "Retrieval-augmented generation helpers for BASIC and assembly examples.",
129
+ resources: [
130
+ "c64://specs/basic",
131
+ "c64://specs/assembly",
132
+ "c64://docs/index",
133
+ ],
134
+ prompts: ["basic-program", "assembly-program"],
135
+ defaultTags: ["rag", "search"],
136
+ supportedPlatforms: ["c64u", "vice"],
137
+ workflowHints: [
138
+ "Call RAG tools when the user needs references or examples before generating new code.",
139
+ "Summarise the number of refs returned and suggest follow-up actions like reading specific docs.",
140
+ ],
141
+ tools: [
142
+ createRagTool("basic", {
143
+ description: "Retrieve BASIC snippets, guidance, and discussion relevant to the provided query.",
144
+ summary: "Returns BASIC-focused references from the local knowledge base.",
145
+ tags: ["basic"],
146
+ }),
147
+ createRagTool("asm", {
148
+ description: "Retrieve 6502/6510 assembly routines and references relevant to the provided query.",
149
+ summary: "Returns assembly-focused references from the local knowledge base.",
150
+ tags: ["asm"],
151
+ }),
152
+ ],
153
+ });
@@ -0,0 +1,58 @@
1
+ import { programRunnersModule } from "./programRunners.js";
2
+ import { memoryModule } from "./memory.js";
3
+ import { audioModule } from "./audio.js";
4
+ import { machineControlModule } from "./machineControl.js";
5
+ import { storageModule } from "./storage.js";
6
+ import { graphicsModule } from "./graphics.js";
7
+ import { printerModule } from "./printer.js";
8
+ import { ragModule } from "./rag.js";
9
+ import { developerModule } from "./developer.js";
10
+ import { streamingModule } from "./streaming.js";
11
+ import { getPlatformStatus, setPlatform } from "../platform.js";
12
+ const toolModules = [
13
+ programRunnersModule,
14
+ memoryModule,
15
+ audioModule,
16
+ machineControlModule,
17
+ storageModule,
18
+ graphicsModule,
19
+ printerModule,
20
+ ragModule,
21
+ developerModule,
22
+ streamingModule,
23
+ ];
24
+ const toolMap = new Map();
25
+ for (const module of toolModules) {
26
+ for (const descriptor of module.describeTools()) {
27
+ if (toolMap.has(descriptor.name)) {
28
+ throw new Error(`Duplicate tool name detected while registering modules: ${descriptor.name}`);
29
+ }
30
+ toolMap.set(descriptor.name, { module, descriptor });
31
+ }
32
+ }
33
+ export const toolRegistry = {
34
+ list() {
35
+ return Array.from(toolMap.values(), (entry) => entry.descriptor);
36
+ },
37
+ async invoke(name, args, ctx) {
38
+ const enrichedCtx = {
39
+ ...ctx,
40
+ platform: ctx.platform ?? getPlatformStatus(),
41
+ setPlatform: ctx.setPlatform ?? setPlatform,
42
+ };
43
+ const entry = toolMap.get(name);
44
+ if (!entry) {
45
+ throw new Error(`Unknown tool: ${name}`);
46
+ }
47
+ return entry.module.invoke(name, args, enrichedCtx);
48
+ },
49
+ };
50
+ export function describeToolModules() {
51
+ return toolModules.map((module) => ({
52
+ domain: module.domain,
53
+ summary: module.summary,
54
+ defaultTags: module.defaultTags,
55
+ workflowHints: module.workflowHints ?? [],
56
+ tools: module.describeTools(),
57
+ }));
58
+ }
@@ -0,0 +1,36 @@
1
+ export function textResult(text, metadata) {
2
+ return {
3
+ content: [
4
+ {
5
+ type: "text",
6
+ text,
7
+ },
8
+ ],
9
+ metadata,
10
+ };
11
+ }
12
+ export function jsonResult(data, metadata) {
13
+ const text = typeof data === "string"
14
+ ? data
15
+ : (() => {
16
+ try {
17
+ return JSON.stringify(data, null, 2);
18
+ }
19
+ catch {
20
+ return String(data);
21
+ }
22
+ })();
23
+ return {
24
+ content: [
25
+ {
26
+ type: "text",
27
+ text,
28
+ },
29
+ ],
30
+ structuredContent: {
31
+ type: "json",
32
+ data,
33
+ },
34
+ metadata,
35
+ };
36
+ }