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.
- package/.github/prompts/steer.prompt.md +55 -0
- package/AGENTS.md +9 -7
- package/README.md +192 -154
- package/data/embeddings_asm.json +1 -1
- package/data/embeddings_basic.json +1 -1
- package/data/embeddings_hardware.json +1 -1
- package/data/embeddings_mixed.json +1 -1
- package/data/embeddings_other.json +1 -1
- package/data/graphics/petscii-style-guide.md +4 -4
- package/data/graphics/sprite-charset-best-practices.md +9 -9
- package/data/vice/vice-binary-monitor-spec.md +938 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/c64Client.js +76 -2
- package/dist/device.js +129 -13
- package/dist/mcp-server.js +1 -1
- package/dist/prompts/registry.js +27 -25
- package/dist/rag/indexer.js +18 -8
- package/dist/rag/knowledgeIndex.js +112 -79
- package/dist/rag/resourceUris.js +93 -0
- package/dist/sidwaveCompiler.js +1 -1
- package/dist/{assemblyConverter.js → tools/assember.js} +10 -1
- package/dist/tools/audio.js +11 -11
- package/dist/{basicConverter.js → tools/basicTokenizer.js} +1 -1
- package/dist/tools/debug.js +159 -6
- package/dist/tools/developer.js +13 -13
- package/dist/tools/disassembler.js +478 -0
- package/dist/tools/graphics.js +10 -10
- package/dist/tools/input.js +338 -0
- package/dist/tools/machineControl.js +5 -5
- package/dist/tools/memory.js +336 -12
- package/dist/tools/meta/index.js +1 -1
- package/dist/tools/printer.js +11 -12
- package/dist/tools/programRunners.js +61 -22
- package/dist/tools/rag.js +9 -9
- package/dist/tools/registry/batch.js +167 -0
- package/dist/tools/registry/config.js +1 -1
- package/dist/tools/registry/disk.js +1 -1
- package/dist/tools/registry/drive.js +1 -1
- package/dist/tools/registry/extract.js +1 -1
- package/dist/tools/registry/graphics.js +102 -2
- package/dist/tools/registry/index.js +4 -0
- package/dist/tools/registry/input.js +2 -0
- package/dist/tools/registry/memory.js +62 -2
- package/dist/tools/registry/platform.js +1 -1
- package/dist/tools/registry/printer.js +1 -1
- package/dist/tools/registry/program.js +1 -1
- package/dist/tools/registry/rag.js +1 -1
- package/dist/tools/registry/sound.js +1 -1
- package/dist/tools/registry/stream.js +1 -1
- package/dist/tools/registry/system.js +1 -1
- package/dist/tools/schema.js +4 -5
- package/dist/tools/storage.js +15 -15
- package/dist/tools/streaming.js +3 -3
- package/dist/tools/symbolRegistry.js +49 -0
- package/dist/tools/types.js +56 -4
- package/dist/tools/vice.js +2 -5
- package/dist/vice/process.js +120 -2
- package/dist/vice/readiness.js +35 -0
- package/dist/vice/smokeOptions.js +44 -0
- package/dist/vice/viceClient.js +5 -1
- package/dist/viceRunner.js +2 -0
- package/doc/plans/prompts/README.md +7 -7
- package/mcp/prompts.json +42 -59
- package/mcp/protocol-examples.json +2 -2
- package/mcp/resources.json +179 -166
- package/mcp/schemas/c64_batch.schema.json +53 -0
- package/mcp/schemas/c64_config.schema.json +525 -283
- package/mcp/schemas/c64_debug.schema.json +763 -304
- package/mcp/schemas/c64_disk.schema.json +499 -243
- package/mcp/schemas/c64_drive.schema.json +165 -90
- package/mcp/schemas/c64_extract.schema.json +533 -274
- package/mcp/schemas/c64_graphics.schema.json +607 -295
- package/mcp/schemas/c64_input.schema.json +136 -0
- package/mcp/schemas/c64_memory.schema.json +637 -128
- package/mcp/schemas/c64_printer.schema.json +393 -196
- package/mcp/schemas/c64_program.schema.json +699 -340
- package/mcp/schemas/c64_rag.schema.json +100 -54
- package/mcp/schemas/c64_select_backend.schema.json +49 -23
- package/mcp/schemas/c64_sound.schema.json +1092 -577
- package/mcp/schemas/c64_stream.schema.json +89 -47
- package/mcp/schemas/c64_system.schema.json +351 -185
- package/mcp/schemas/c64_vice.schema.json +85 -47
- package/mcp/server.json +1 -1
- package/mcp/tools.json +7299 -3346
- package/mcp.json +3 -3
- package/package.json +17 -14
- package/scripts/fetch-external-sources.mjs +1 -2
- package/scripts/run-coverage.mjs +33 -1
- package/scripts/run-tests.mjs +2 -2
- package/scripts/run-tests.ts +275 -52
- package/scripts/update-readme.ts +132 -3
- package/server.json +2 -2
package/dist/c64Client.js
CHANGED
|
@@ -8,8 +8,8 @@ See <https://www.gnu.org/licenses/> for details.
|
|
|
8
8
|
import { Buffer } from "node:buffer";
|
|
9
9
|
import { createSocket } from "node:dgram";
|
|
10
10
|
import axios from "axios";
|
|
11
|
-
import { basicToPrg } from "./
|
|
12
|
-
import { assemblyToPrg } from "./
|
|
11
|
+
import { basicToPrg } from "./tools/basicTokenizer.js";
|
|
12
|
+
import { assemblyToPrg } from "./tools/assember.js";
|
|
13
13
|
import { screenCodesToAscii } from "./petscii.js";
|
|
14
14
|
import { resolveAddressSymbol } from "./knowledge.js";
|
|
15
15
|
import { createAllFacades, createFacade } from "./device.js";
|
|
@@ -1492,6 +1492,80 @@ export class C64Client {
|
|
|
1492
1492
|
}
|
|
1493
1493
|
return body instanceof ArrayBuffer ? new Uint8Array(body) : this.extractBytes(body);
|
|
1494
1494
|
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Low-level memory write that delegates to the active backend's facade.
|
|
1497
|
+
* Public so tools that need to drive cross-platform machine state (e.g.
|
|
1498
|
+
* keyboard-buffer injection) can avoid backend-specific paths.
|
|
1499
|
+
*/
|
|
1500
|
+
async writeMemoryRaw(address, bytes) {
|
|
1501
|
+
const facade = await this.facadePromise;
|
|
1502
|
+
const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
|
|
1503
|
+
await facade.writeMemory(address, buf);
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Inject a sequence of PETSCII byte values into the KERNAL keyboard buffer
|
|
1507
|
+
* using shared memory writes. Works on both C64U and VICE because both
|
|
1508
|
+
* backends expose `writeMemory`/`readMemory` against the C64 zero-page and
|
|
1509
|
+
* the keyboard queue at $0277.
|
|
1510
|
+
*
|
|
1511
|
+
* The KERNAL buffer ($0277-$0280) holds 10 bytes; $00C6 (NDX) tracks the
|
|
1512
|
+
* number of pending characters. We chunk the input, then poll NDX until
|
|
1513
|
+
* the kernel has consumed each chunk before queueing the next one.
|
|
1514
|
+
*
|
|
1515
|
+
* @param bytes Raw PETSCII bytes (already token-expanded by caller)
|
|
1516
|
+
* @param options Optional knobs for chunk size, drain timing, and total
|
|
1517
|
+
* timeout. The defaults mirror KERNAL behavior.
|
|
1518
|
+
*/
|
|
1519
|
+
async injectKeyboardQueue(bytes, options) {
|
|
1520
|
+
const data = Buffer.isBuffer(bytes)
|
|
1521
|
+
? bytes
|
|
1522
|
+
: Buffer.from(bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes));
|
|
1523
|
+
if (data.length === 0) {
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
const chunkSize = Math.max(1, Math.min(10, options?.chunkSize ?? 10));
|
|
1527
|
+
const pollMs = Math.max(1, options?.drainPollMs ?? 8);
|
|
1528
|
+
const totalTimeoutMs = Math.max(50, options?.drainTimeoutMs ?? 2000);
|
|
1529
|
+
const facade = await this.facadePromise;
|
|
1530
|
+
const KEYD = 0x0277; // KERNAL keyboard buffer base
|
|
1531
|
+
const NDX = 0x00C6; // pending byte count
|
|
1532
|
+
for (let offset = 0; offset < data.length; offset += chunkSize) {
|
|
1533
|
+
const slice = data.subarray(offset, Math.min(data.length, offset + chunkSize));
|
|
1534
|
+
// Write the bytes into the queue first so KERNAL never reads a stale
|
|
1535
|
+
// length+old-byte combination.
|
|
1536
|
+
await facade.writeMemory(KEYD, Buffer.from(slice));
|
|
1537
|
+
await facade.writeMemory(NDX, Buffer.from([slice.length]));
|
|
1538
|
+
// Drain: wait for KERNAL to consume the queue. If the machine is paused
|
|
1539
|
+
// or otherwise not running, give up after the timeout and continue —
|
|
1540
|
+
// higher layers can decide whether that is a failure for their context.
|
|
1541
|
+
const drainStart = Date.now();
|
|
1542
|
+
while (Date.now() - drainStart < totalTimeoutMs) {
|
|
1543
|
+
const ndx = await facade.readMemory(NDX, 1);
|
|
1544
|
+
if ((ndx[0] ?? 0) === 0) {
|
|
1545
|
+
break;
|
|
1546
|
+
}
|
|
1547
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
async viceExitMonitor() {
|
|
1552
|
+
await this.withViceMonitor((client) => client.exitMonitor());
|
|
1553
|
+
}
|
|
1554
|
+
async viceKeyboardFeed(text) {
|
|
1555
|
+
await this.withViceMonitor((client) => client.keyboardFeed(text));
|
|
1556
|
+
}
|
|
1557
|
+
async viceMemSet(address, bytes) {
|
|
1558
|
+
const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
|
|
1559
|
+
await this.withViceMonitor((client) => client.memSet(address, buf));
|
|
1560
|
+
}
|
|
1561
|
+
async viceMemGet(address, length) {
|
|
1562
|
+
const end = (address + length - 1) & 0xffff;
|
|
1563
|
+
return this.withViceMonitor((client) => client.memGet(address, end));
|
|
1564
|
+
}
|
|
1565
|
+
async viceNuclearReset() {
|
|
1566
|
+
const backend = await this.requireViceBackend();
|
|
1567
|
+
await backend.nuclearReset();
|
|
1568
|
+
}
|
|
1495
1569
|
async viceCheckpointList() {
|
|
1496
1570
|
return this.withViceMonitor((client) => client.checkpointList());
|
|
1497
1571
|
}
|
package/dist/device.js
CHANGED
|
@@ -18,6 +18,18 @@ const DEFAULT_VICE_PORT = 6502;
|
|
|
18
18
|
function delay(ms) {
|
|
19
19
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
20
20
|
}
|
|
21
|
+
async function resumeViceMonitor(client, timeoutMs = 250) {
|
|
22
|
+
try {
|
|
23
|
+
const exitMonitor = client.exitMonitor().catch(() => { });
|
|
24
|
+
await Promise.race([
|
|
25
|
+
exitMonitor,
|
|
26
|
+
delay(timeoutMs),
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Ignore resume failures while tearing down a monitor session.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
21
33
|
function coalesceMemoryWriteBlocks(blocks) {
|
|
22
34
|
const merged = [];
|
|
23
35
|
for (const block of blocks) {
|
|
@@ -215,12 +227,18 @@ export class ViceBackend {
|
|
|
215
227
|
const configBinary = config.exe !== undefined
|
|
216
228
|
? configuredString(config.exe) ?? (typeof config.exe === "string" ? config.exe : String(config.exe))
|
|
217
229
|
: undefined;
|
|
218
|
-
|
|
230
|
+
const resolvedLaunch = resolveViceLaunch({
|
|
231
|
+
envBinary,
|
|
232
|
+
configBinary,
|
|
233
|
+
envDirectory: configuredString(process.env.VICE_DIRECTORY),
|
|
234
|
+
configDirectory: configuredString(config.directory),
|
|
235
|
+
});
|
|
236
|
+
this.exe = resolvedLaunch.binary;
|
|
219
237
|
const envHost = normaliseViceHost(process.env.VICE_HOST);
|
|
220
238
|
const envPort = normaliseVicePort(process.env.VICE_PORT);
|
|
221
239
|
this.host = firstDefined(envHost, normaliseViceHost(config.host)) ?? DEFAULT_VICE_HOST;
|
|
222
240
|
this.port = firstDefined(envPort, normaliseVicePort(config.port)) ?? DEFAULT_VICE_PORT;
|
|
223
|
-
this.directory =
|
|
241
|
+
this.directory = resolvedLaunch.directory;
|
|
224
242
|
this.mockMode = (process.env.VICE_TEST_TARGET || "").toLowerCase() === "mock";
|
|
225
243
|
const hostLower = this.host.toLowerCase();
|
|
226
244
|
const isLocal = hostLower === "127.0.0.1" || hostLower === "localhost";
|
|
@@ -238,6 +256,10 @@ export class ViceBackend {
|
|
|
238
256
|
console.error("[vice-backend] probing existing VICE", this.host, this.port);
|
|
239
257
|
await client.connect(this.port, this.host);
|
|
240
258
|
await client.info();
|
|
259
|
+
try {
|
|
260
|
+
await client.exitMonitor();
|
|
261
|
+
}
|
|
262
|
+
catch { }
|
|
241
263
|
if (this.debugEnabled)
|
|
242
264
|
console.error("[vice-backend] existing VICE is reachable");
|
|
243
265
|
return true;
|
|
@@ -257,6 +279,10 @@ export class ViceBackend {
|
|
|
257
279
|
try {
|
|
258
280
|
await client.connect(this.port, this.host);
|
|
259
281
|
await client.info();
|
|
282
|
+
try {
|
|
283
|
+
await client.exitMonitor();
|
|
284
|
+
}
|
|
285
|
+
catch { }
|
|
260
286
|
return;
|
|
261
287
|
}
|
|
262
288
|
catch (error) {
|
|
@@ -275,6 +301,7 @@ export class ViceBackend {
|
|
|
275
301
|
const client = new ViceClient();
|
|
276
302
|
try {
|
|
277
303
|
await client.connect(this.port, this.host);
|
|
304
|
+
await client.reset();
|
|
278
305
|
const readiness = await waitForBasicReady(client, { timeoutMs, ensurePrompt: true });
|
|
279
306
|
if (!readiness.pointersOk || !readiness.promptOk) {
|
|
280
307
|
throw new Error(`VICE machine at ${this.host}:${this.port} did not reach the BASIC prompt within ${timeoutMs}ms`);
|
|
@@ -312,15 +339,15 @@ export class ViceBackend {
|
|
|
312
339
|
extraArgs: this.extraArgs,
|
|
313
340
|
});
|
|
314
341
|
}
|
|
315
|
-
const handle = await startViceProcess({
|
|
342
|
+
const handle = await startViceProcess(buildManagedViceProcessOptions({
|
|
316
343
|
binary: this.exe,
|
|
317
344
|
directory: this.directory,
|
|
318
345
|
host: this.host,
|
|
319
346
|
port: this.port,
|
|
320
347
|
warp: this.warp,
|
|
321
348
|
visible: this.visible,
|
|
322
|
-
extraArgs: this.extraArgs
|
|
323
|
-
});
|
|
349
|
+
extraArgs: this.extraArgs,
|
|
350
|
+
}));
|
|
324
351
|
this.lastProcessStart = Date.now();
|
|
325
352
|
if (this.debugEnabled) {
|
|
326
353
|
console.error("[vice-backend] VICE process started", { pid: handle.process.pid });
|
|
@@ -351,6 +378,7 @@ export class ViceBackend {
|
|
|
351
378
|
return;
|
|
352
379
|
}
|
|
353
380
|
ViceBackend.cleanupRegistered = true;
|
|
381
|
+
/* c8 ignore next 5 -- global process exit hooks are not practical to exercise in the unit test process */
|
|
354
382
|
process.once("exit", () => {
|
|
355
383
|
for (const [, handle] of ViceBackend.supervisors) {
|
|
356
384
|
handle.stop().catch(() => { });
|
|
@@ -358,7 +386,7 @@ export class ViceBackend {
|
|
|
358
386
|
ViceBackend.supervisors.clear();
|
|
359
387
|
});
|
|
360
388
|
}
|
|
361
|
-
async withClient(fn) {
|
|
389
|
+
async withClient(fn, options) {
|
|
362
390
|
const previous = this.monitorQueue;
|
|
363
391
|
let releaseQueue = () => { };
|
|
364
392
|
this.monitorQueue = new Promise((resolve) => {
|
|
@@ -385,6 +413,9 @@ export class ViceBackend {
|
|
|
385
413
|
return await fn(client);
|
|
386
414
|
}
|
|
387
415
|
finally {
|
|
416
|
+
if (options?.resumeOnClose !== false) {
|
|
417
|
+
await resumeViceMonitor(client);
|
|
418
|
+
}
|
|
388
419
|
if (this.debugEnabled)
|
|
389
420
|
console.error("[vice-backend] closing VICE monitor connection");
|
|
390
421
|
client.close();
|
|
@@ -532,7 +563,7 @@ export class ViceBackend {
|
|
|
532
563
|
catch {
|
|
533
564
|
// Ignore transport errors during quit; the emulator will terminate regardless.
|
|
534
565
|
}
|
|
535
|
-
});
|
|
566
|
+
}, { resumeOnClose: false });
|
|
536
567
|
if (managedHandle) {
|
|
537
568
|
try {
|
|
538
569
|
await managedHandle.stop();
|
|
@@ -549,6 +580,31 @@ export class ViceBackend {
|
|
|
549
580
|
return { success: false, details };
|
|
550
581
|
}
|
|
551
582
|
}
|
|
583
|
+
async nuclearReset() {
|
|
584
|
+
const poweroffResult = await this.poweroff();
|
|
585
|
+
if (!poweroffResult.success) {
|
|
586
|
+
return poweroffResult;
|
|
587
|
+
}
|
|
588
|
+
if (!this.manageProcess) {
|
|
589
|
+
return {
|
|
590
|
+
success: false,
|
|
591
|
+
details: {
|
|
592
|
+
code: "UNSUPPORTED",
|
|
593
|
+
message: "VICE nuclear reset requires a managed process; unmanaged instances can only be powered off.",
|
|
594
|
+
},
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
try {
|
|
598
|
+
await this.ensureProcess();
|
|
599
|
+
return { success: true };
|
|
600
|
+
}
|
|
601
|
+
catch (error) {
|
|
602
|
+
const details = error instanceof Error
|
|
603
|
+
? { message: error.message }
|
|
604
|
+
: error;
|
|
605
|
+
return { success: false, details };
|
|
606
|
+
}
|
|
607
|
+
}
|
|
552
608
|
async menuButton() { throw unsupported("menuButton"); }
|
|
553
609
|
async debugregRead() { throw unsupported("debugregRead"); }
|
|
554
610
|
async debugregWrite(_v) { throw unsupported("debugregWrite"); }
|
|
@@ -775,9 +831,59 @@ function resolveViceBinary(options, findBinary = which) {
|
|
|
775
831
|
}
|
|
776
832
|
return findBinary("x64sc") ?? findBinary("x64") ?? "x64sc";
|
|
777
833
|
}
|
|
834
|
+
function resolveViceLaunch(options, dependencies = {}) {
|
|
835
|
+
const findBinary = dependencies.findBinary ?? which;
|
|
836
|
+
const isResourceDirectory = dependencies.isResourceDirectory ?? isViceResourceDirectory;
|
|
837
|
+
const explicitBinary = firstDefined(options.envBinary, options.configBinary);
|
|
838
|
+
const explicitDirectory = firstDefined(resolveViceDirectoryWithValidator(options.envDirectory, isResourceDirectory), resolveViceDirectoryWithValidator(options.configDirectory, isResourceDirectory));
|
|
839
|
+
let binary = resolveViceBinary({ envBinary: options.envBinary, configBinary: options.configBinary }, findBinary);
|
|
840
|
+
if (explicitDirectory) {
|
|
841
|
+
return { binary, directory: explicitDirectory };
|
|
842
|
+
}
|
|
843
|
+
const adjacentDirectory = findViceResourceDirectory(binary, {
|
|
844
|
+
allowGlobalFallback: false,
|
|
845
|
+
isResourceDirectory,
|
|
846
|
+
});
|
|
847
|
+
if (adjacentDirectory) {
|
|
848
|
+
return { binary, directory: adjacentDirectory };
|
|
849
|
+
}
|
|
850
|
+
if (explicitBinary) {
|
|
851
|
+
return {
|
|
852
|
+
binary,
|
|
853
|
+
directory: findViceResourceDirectory(binary, {
|
|
854
|
+
allowGlobalFallback: true,
|
|
855
|
+
isResourceDirectory,
|
|
856
|
+
}),
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
return {
|
|
860
|
+
binary,
|
|
861
|
+
directory: findViceResourceDirectory(binary, {
|
|
862
|
+
allowGlobalFallback: true,
|
|
863
|
+
isResourceDirectory,
|
|
864
|
+
}),
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
function buildManagedViceProcessOptions(input) {
|
|
868
|
+
return {
|
|
869
|
+
binary: input.binary,
|
|
870
|
+
directory: input.directory,
|
|
871
|
+
host: input.host,
|
|
872
|
+
port: input.port,
|
|
873
|
+
warp: input.warp,
|
|
874
|
+
visible: input.visible,
|
|
875
|
+
extraArgs: input.extraArgs.length > 0 ? input.extraArgs : undefined,
|
|
876
|
+
};
|
|
877
|
+
}
|
|
778
878
|
export function __resolveViceBinaryForTests(options, findBinary) {
|
|
779
879
|
return resolveViceBinary(options, findBinary ?? which);
|
|
780
880
|
}
|
|
881
|
+
export function __resolveViceLaunchForTests(options, dependencies) {
|
|
882
|
+
return resolveViceLaunch(options, dependencies);
|
|
883
|
+
}
|
|
884
|
+
export function __buildViceProcessOptionsForTests(input) {
|
|
885
|
+
return buildManagedViceProcessOptions(input);
|
|
886
|
+
}
|
|
781
887
|
export async function createFacade(logger, options) {
|
|
782
888
|
// Caller-forced preference: use c64u with provided base URL (used by tests and server wiring)
|
|
783
889
|
if (options?.preferredC64uBaseUrl) {
|
|
@@ -935,7 +1041,15 @@ function resolveViceDirectory(value) {
|
|
|
935
1041
|
}
|
|
936
1042
|
return isViceResourceDirectory(input) ? input : undefined;
|
|
937
1043
|
}
|
|
938
|
-
function
|
|
1044
|
+
function resolveViceDirectoryWithValidator(value, isResourceDirectory) {
|
|
1045
|
+
const input = configuredString(value);
|
|
1046
|
+
if (!input) {
|
|
1047
|
+
return undefined;
|
|
1048
|
+
}
|
|
1049
|
+
return isResourceDirectory(input) ? input : undefined;
|
|
1050
|
+
}
|
|
1051
|
+
function findViceResourceDirectory(binaryPath, options = {}) {
|
|
1052
|
+
const isResourceDirectory = options.isResourceDirectory ?? isViceResourceDirectory;
|
|
939
1053
|
const candidates = new Set();
|
|
940
1054
|
const resolvedBinary = configuredString(binaryPath);
|
|
941
1055
|
if (resolvedBinary) {
|
|
@@ -943,12 +1057,14 @@ function findViceResourceDirectory(binaryPath) {
|
|
|
943
1057
|
candidates.add(path.resolve(binaryDir, "..", "share", "vice"));
|
|
944
1058
|
candidates.add(path.resolve(binaryDir, "..", "..", "share", "vice"));
|
|
945
1059
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1060
|
+
if (options.allowGlobalFallback !== false) {
|
|
1061
|
+
candidates.add("/usr/local/share/vice");
|
|
1062
|
+
candidates.add("/usr/share/vice");
|
|
1063
|
+
candidates.add("/opt/homebrew/share/vice");
|
|
1064
|
+
candidates.add("/opt/local/share/vice");
|
|
1065
|
+
}
|
|
950
1066
|
for (const candidate of candidates) {
|
|
951
|
-
if (
|
|
1067
|
+
if (isResourceDirectory(candidate)) {
|
|
952
1068
|
return candidate;
|
|
953
1069
|
}
|
|
954
1070
|
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -8,6 +8,7 @@ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSche
|
|
|
8
8
|
import { loadConfig } from "./config.js";
|
|
9
9
|
import { C64Client } from "./c64Client.js";
|
|
10
10
|
import { listKnowledgeResources, readKnowledgeResource, } from "./rag/knowledgeIndex.js";
|
|
11
|
+
import { PLATFORM_RESOURCE_URI } from "./rag/resourceUris.js";
|
|
11
12
|
import { createLazyRagRetriever, initRag } from "./rag/init.js";
|
|
12
13
|
import { getMcpServerImplementationInfo } from "./mcp/metadata.js";
|
|
13
14
|
import { toolRegistry } from "./tools/registry/index.js";
|
|
@@ -417,7 +418,6 @@ async function main() {
|
|
|
417
418
|
diagnosticsFile: diagnostics.filePath,
|
|
418
419
|
});
|
|
419
420
|
}
|
|
420
|
-
const PLATFORM_RESOURCE_URI = "c64://platform/status";
|
|
421
421
|
function createPlatformResourceDescriptor() {
|
|
422
422
|
return {
|
|
423
423
|
uri: PLATFORM_RESOURCE_URI,
|
package/dist/prompts/registry.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { listKnowledgeResources } from "../rag/knowledgeIndex.js";
|
|
2
|
+
import { RESOURCE_URIS } from "../rag/resourceUris.js";
|
|
2
3
|
import { toolRegistry } from "../tools/registry/index.js";
|
|
3
4
|
const ASSEMBLY_HARDWARE_NOTES = {
|
|
4
5
|
sid: [
|
|
@@ -34,6 +35,7 @@ const ROUTING_CORE_SEGMENT = {
|
|
|
34
35
|
content: [
|
|
35
36
|
"Prompts in this repository define intent and routing only.",
|
|
36
37
|
"Execution logic, validation steps, and safety rules live exclusively in `.github/skills/*/SKILL.md`.",
|
|
38
|
+
`When the request targets the \`vice\` backend, read \`${RESOURCE_URIS.vice.binaryMonitorSpec}\` before executing the skill so Binary Monitor single-client behaviour, trap/resume semantics, and monitor-side side effects stay in scope.`,
|
|
37
39
|
"Use the referenced skill, extract missing inputs from the user request, execute the skill, and summarize the outcome.",
|
|
38
40
|
].join("\n"),
|
|
39
41
|
};
|
|
@@ -115,7 +117,7 @@ export function createPromptRegistry() {
|
|
|
115
117
|
name: "hello-world",
|
|
116
118
|
title: "Hello World Workflow",
|
|
117
119
|
description: "Route ultra-fast hello-world and smoke-test requests to the canonical greeting skill.",
|
|
118
|
-
requiredResources: [
|
|
120
|
+
requiredResources: [RESOURCE_URIS.guide.bootstrap, RESOURCE_URIS.guide.fastPaths, RESOURCE_URIS.guide.index],
|
|
119
121
|
optionalResources: [],
|
|
120
122
|
tools: ["c64_program"],
|
|
121
123
|
tags: ["hello", "demo", "greeting", "smoke-test"],
|
|
@@ -134,8 +136,8 @@ export function createPromptRegistry() {
|
|
|
134
136
|
name: "basic-program",
|
|
135
137
|
title: "BASIC Program Workflow",
|
|
136
138
|
description: "Route bespoke Commodore BASIC v2 requests to the canonical BASIC skill.",
|
|
137
|
-
requiredResources: [
|
|
138
|
-
optionalResources: [
|
|
139
|
+
requiredResources: [RESOURCE_URIS.basic.spec, RESOURCE_URIS.guide.bootstrap, RESOURCE_URIS.guide.index],
|
|
140
|
+
optionalResources: [RESOURCE_URIS.guide.fastPaths],
|
|
139
141
|
tools: ["c64_program", "c64_memory"],
|
|
140
142
|
tags: ["basic", "program"],
|
|
141
143
|
},
|
|
@@ -150,7 +152,7 @@ export function createPromptRegistry() {
|
|
|
150
152
|
name: "cross-platform-demo",
|
|
151
153
|
title: "Cross-Platform Demo Workflow",
|
|
152
154
|
description: "Route quick visible demo requests to the cross-platform demo skill.",
|
|
153
|
-
requiredResources: [
|
|
155
|
+
requiredResources: [RESOURCE_URIS.guide.bootstrap, RESOURCE_URIS.guide.fastPaths, RESOURCE_URIS.guide.index],
|
|
154
156
|
optionalResources: [],
|
|
155
157
|
tools: ["c64_program"],
|
|
156
158
|
tags: ["demo", "platform", "greeting"],
|
|
@@ -166,8 +168,8 @@ export function createPromptRegistry() {
|
|
|
166
168
|
name: "preset-music-demo",
|
|
167
169
|
title: "Preset Music Demo Workflow",
|
|
168
170
|
description: "Route quick recognizable tune requests to the SID music skill.",
|
|
169
|
-
requiredResources: [
|
|
170
|
-
optionalResources: [
|
|
171
|
+
requiredResources: [RESOURCE_URIS.sound.sid.spec, RESOURCE_URIS.sound.sidwave.spec, RESOURCE_URIS.guide.fastPaths],
|
|
172
|
+
optionalResources: [RESOURCE_URIS.sound.sid.bestPractices],
|
|
171
173
|
tools: ["c64_sound"],
|
|
172
174
|
tags: ["sid", "music", "demo"],
|
|
173
175
|
},
|
|
@@ -181,8 +183,8 @@ export function createPromptRegistry() {
|
|
|
181
183
|
name: "assembly-program",
|
|
182
184
|
title: "Assembly Program Workflow",
|
|
183
185
|
description: "Route 6502/6510 routine requests to the canonical assembly skill.",
|
|
184
|
-
requiredResources: [
|
|
185
|
-
optionalResources: [
|
|
186
|
+
requiredResources: [RESOURCE_URIS.assembly.spec6510, RESOURCE_URIS.graphics.vic.spec, RESOURCE_URIS.sound.sid.spec, RESOURCE_URIS.guide.bootstrap],
|
|
187
|
+
optionalResources: [RESOURCE_URIS.sound.sid.bestPractices],
|
|
186
188
|
tools: ["c64_program", "c64_memory"],
|
|
187
189
|
tags: ["assembly", "program"],
|
|
188
190
|
},
|
|
@@ -201,7 +203,7 @@ export function createPromptRegistry() {
|
|
|
201
203
|
selectOptionalResources: (args) => {
|
|
202
204
|
const hardware = args.hardware;
|
|
203
205
|
return hardware === "sid" || hardware === "multi"
|
|
204
|
-
? [
|
|
206
|
+
? [RESOURCE_URIS.sound.sid.bestPractices]
|
|
205
207
|
: [];
|
|
206
208
|
},
|
|
207
209
|
renderRoutingNotes: (args) => {
|
|
@@ -220,12 +222,12 @@ export function createPromptRegistry() {
|
|
|
220
222
|
title: "SID Composition Workflow",
|
|
221
223
|
description: "Route SID playback and composition work to the canonical SID skill.",
|
|
222
224
|
requiredResources: [
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
RESOURCE_URIS.sound.sid.spec,
|
|
226
|
+
RESOURCE_URIS.sound.sidwave.spec,
|
|
227
|
+
RESOURCE_URIS.sound.sid.fileFormat,
|
|
228
|
+
RESOURCE_URIS.sound.sid.bestPractices,
|
|
227
229
|
],
|
|
228
|
-
optionalResources: [
|
|
230
|
+
optionalResources: [RESOURCE_URIS.guide.fastPaths],
|
|
229
231
|
tools: ["c64_sound"],
|
|
230
232
|
tags: ["sid", "music"],
|
|
231
233
|
},
|
|
@@ -239,8 +241,8 @@ export function createPromptRegistry() {
|
|
|
239
241
|
name: "graphics-demo",
|
|
240
242
|
title: "Graphics Demo Workflow",
|
|
241
243
|
description: "Route graphics requests to the canonical graphics skill.",
|
|
242
|
-
requiredResources: [
|
|
243
|
-
optionalResources: [
|
|
244
|
+
requiredResources: [RESOURCE_URIS.graphics.vic.spec, RESOURCE_URIS.guide.bootstrap],
|
|
245
|
+
optionalResources: [RESOURCE_URIS.assembly.spec6510, RESOURCE_URIS.graphics.characterSet, RESOURCE_URIS.graphics.petscii.styleGuide],
|
|
244
246
|
tools: ["c64_program", "c64_memory", "c64_graphics"],
|
|
245
247
|
tags: ["graphics", "vic"],
|
|
246
248
|
},
|
|
@@ -271,12 +273,12 @@ export function createPromptRegistry() {
|
|
|
271
273
|
name: "printer-job",
|
|
272
274
|
title: "Printer Job Workflow",
|
|
273
275
|
description: "Route printer work to the canonical printer skill.",
|
|
274
|
-
requiredResources: [
|
|
276
|
+
requiredResources: [RESOURCE_URIS.printer.spec, RESOURCE_URIS.printer.promptGuide],
|
|
275
277
|
optionalResources: [
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
RESOURCE_URIS.printer.commodore.text,
|
|
279
|
+
RESOURCE_URIS.printer.commodore.bitmap,
|
|
280
|
+
RESOURCE_URIS.printer.epson.text,
|
|
281
|
+
RESOURCE_URIS.printer.epson.bitmap,
|
|
280
282
|
],
|
|
281
283
|
tools: ["c64_printer"],
|
|
282
284
|
tags: ["printer"],
|
|
@@ -296,10 +298,10 @@ export function createPromptRegistry() {
|
|
|
296
298
|
selectOptionalResources: (args) => {
|
|
297
299
|
const printerType = args.printerType;
|
|
298
300
|
if (printerType === "commodore") {
|
|
299
|
-
return [
|
|
301
|
+
return [RESOURCE_URIS.printer.commodore.text, RESOURCE_URIS.printer.commodore.bitmap];
|
|
300
302
|
}
|
|
301
303
|
if (printerType === "epson") {
|
|
302
|
-
return [
|
|
304
|
+
return [RESOURCE_URIS.printer.epson.text, RESOURCE_URIS.printer.epson.bitmap];
|
|
303
305
|
}
|
|
304
306
|
return [];
|
|
305
307
|
},
|
|
@@ -315,7 +317,7 @@ export function createPromptRegistry() {
|
|
|
315
317
|
name: "memory-debug",
|
|
316
318
|
title: "Memory Debug Workflow",
|
|
317
319
|
description: "Route reversible memory inspection or patching work to the canonical memory skill.",
|
|
318
|
-
requiredResources: [
|
|
320
|
+
requiredResources: [RESOURCE_URIS.guide.bootstrap, RESOURCE_URIS.assembly.spec6510, RESOURCE_URIS.guide.index],
|
|
319
321
|
optionalResources: [],
|
|
320
322
|
tools: ["c64_memory", "c64_system"],
|
|
321
323
|
tags: ["memory", "debug"],
|
|
@@ -330,7 +332,7 @@ export function createPromptRegistry() {
|
|
|
330
332
|
name: "drive-manager",
|
|
331
333
|
title: "Drive Manager Workflow",
|
|
332
334
|
description: "Route disk-image and drive-state requests to the canonical drive skill.",
|
|
333
|
-
requiredResources: [
|
|
335
|
+
requiredResources: [RESOURCE_URIS.guide.bootstrap],
|
|
334
336
|
optionalResources: [],
|
|
335
337
|
tools: ["c64_disk", "c64_drive"],
|
|
336
338
|
tags: ["drive", "storage"],
|
package/dist/rag/indexer.js
CHANGED
|
@@ -474,20 +474,30 @@ export async function buildAllIndexes({ model, embeddingsDir: overrideDir, basic
|
|
|
474
474
|
repoMetadataCache.clear();
|
|
475
475
|
const embeddingsDir = resolveEmbeddingsDir(overrideDir);
|
|
476
476
|
const paths = embeddingIndexPaths(embeddingsDir);
|
|
477
|
-
//
|
|
477
|
+
// Production indexing defaults to the repo docs tree. Tests and other callers can
|
|
478
|
+
// override that set explicitly, including passing an empty array to opt out.
|
|
478
479
|
const docCandidates = [];
|
|
479
|
-
|
|
480
|
-
const
|
|
481
|
-
for (const f of mdFiles) {
|
|
480
|
+
if (docFiles !== undefined) {
|
|
481
|
+
for (const f of docFiles) {
|
|
482
482
|
if (!shouldExcludeDocFile(f)) {
|
|
483
483
|
docCandidates.push(f);
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
487
|
+
else {
|
|
488
|
+
try {
|
|
489
|
+
const mdFiles = await collectFiles(DOC_ROOT, [".md"]);
|
|
490
|
+
for (const f of mdFiles) {
|
|
491
|
+
if (!shouldExcludeDocFile(f)) {
|
|
492
|
+
docCandidates.push(f);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch { }
|
|
497
|
+
for (const f of ENV_DOC_FILES) {
|
|
498
|
+
if (!shouldExcludeDocFile(f)) {
|
|
499
|
+
docCandidates.push(f);
|
|
500
|
+
}
|
|
491
501
|
}
|
|
492
502
|
}
|
|
493
503
|
// De-duplicate
|