@henols/vice-mcp 0.2.1 → 0.2.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/README.md +2 -1
- package/THIRD-PARTY-NOTICES.md +1 -24
- package/{r2000-acme-ident.ts → anno-acme-ident.ts} +13 -13
- package/anno-cli.ts +1465 -0
- package/{r2000-confidence.ts → anno-confidence.ts} +22 -22
- package/anno-coverage.ts +2465 -0
- package/{r2000-d64.ts → anno-d64.ts} +5 -5
- package/anno-derive.ts +590 -0
- package/anno-details.ts +169 -0
- package/anno-enum-gen.ts +533 -0
- package/anno-export-asm.ts +1310 -0
- package/anno-index.ts +150 -0
- package/{r2000-memmap-render.ts → anno-memmap-render.ts} +236 -95
- package/{r2000-regbits-gen.ts → anno-regbits-gen.ts} +20 -15
- package/{r2000-regbits.json → anno-regbits.json} +2 -2
- package/anno-register.ts +240 -0
- package/anno-store.ts +3486 -0
- package/anno-symbols.ts +266 -0
- package/anno-tools.ts +2111 -0
- package/anno-types.ts +1636 -0
- package/block-class.ts +201 -0
- package/build.ts +1 -1
- package/capability-registry.ts +3 -1
- package/disasm-decoder.ts +14 -14
- package/disasm-opcodes.ts +4 -4
- package/disasm-renderer.ts +2 -2
- package/hostpath.ts +1 -1
- package/install-resources.ts +1 -1
- package/package.json +23 -17
- package/prg-image.ts +119 -0
- package/repo-root.ts +20 -5
- package/resources/broker-launch.mjs +8 -4
- package/resources/vice-launcher.sh +3 -3
- package/stock-address.ts +5 -5
- package/stock-cia.ts +2 -2
- package/stock-condition.ts +7 -7
- package/stock-connect.ts +1 -1
- package/stock-dispatch.ts +35 -5
- package/stock-execution.ts +5 -3
- package/stock-input.ts +9 -9
- package/stock-machine.ts +17 -6
- package/stock-protocol.ts +16 -11
- package/stock-registers.ts +54 -29
- package/stock-sprites.ts +3 -3
- package/stock-symbols.ts +9 -9
- package/stock-timing.ts +1 -1
- package/stock-vicii.ts +1 -1
- package/version.ts +1 -1
- package/vice-proxy.ts +68 -46
- package/r2000-cli.ts +0 -1103
- package/r2000-enum-gen.ts +0 -574
- package/r2000-launch.ts +0 -357
- package/r2000-mcp-client.ts +0 -596
- package/r2000-project.ts +0 -190
- package/r2000-symbols.ts +0 -388
- package/r2000-tools.ts +0 -914
- package/r2000-verify.ts +0 -184
package/vice-proxy.ts
CHANGED
|
@@ -185,22 +185,22 @@ import * as stockDispatch from "./stock-dispatch.ts";
|
|
|
185
185
|
// below, strictly after the DENY_LIST check -- see the comment at that call
|
|
186
186
|
// site for why the ordering is load-bearing.
|
|
187
187
|
import { capabilityRefusalMessage } from "./capability-registry.ts";
|
|
188
|
-
// Plan
|
|
188
|
+
// Plan 29-01: the curated anno_* tool surface's DEFINITIONS, imported
|
|
189
189
|
// STATICALLY -- registration below happens synchronously at module scope, so
|
|
190
|
-
// a dynamic import cannot serve it. This costs
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
import {
|
|
190
|
+
// a dynamic import cannot serve it. This costs nothing at module load: no
|
|
191
|
+
// store file is opened here. The owned SQLite annotation store stays behind
|
|
192
|
+
// anno-tools.ts's own runAnnoTool(), which opens it, answers exactly one call
|
|
193
|
+
// against it and closes it again (D-06), reached only when a tool is called.
|
|
194
|
+
import { ANNO_TOOL_DEFINITIONS, runAnnoTool } from "./anno-tools.ts";
|
|
195
195
|
|
|
196
|
-
// ------------------------------------------------------------
|
|
196
|
+
// ------------------------------------------------------------ anno subcommand
|
|
197
197
|
//
|
|
198
|
-
// D-06 / RESEARCH.md Open Question #1 (plan 10-04): `vice-mcp
|
|
198
|
+
// D-06 / RESEARCH.md Open Question #1 (plan 10-04): `vice-mcp anno <verb>` is
|
|
199
199
|
// the ONLY surface that resolves identically across the Claude Code plugin
|
|
200
200
|
// route and both npm-installer routes -- `installer/bin/cli.mjs`'s
|
|
201
201
|
// `viceServerEntry()` always launches this server via `npx` in BOTH
|
|
202
202
|
// npm-installer modes (`--vendor` only pre-resolves the package; it never
|
|
203
|
-
// places
|
|
203
|
+
// places `src/mcp/vice/*.ts` as plain files inside a consuming project),
|
|
204
204
|
// so any design resolving a filesystem path to the seam would silently fail
|
|
205
205
|
// to resolve for npm-installed users. This bin is the one surface proven to
|
|
206
206
|
// work in all three routes.
|
|
@@ -212,7 +212,7 @@ import { R2000_TOOL_DEFINITIONS, runR2000Tool } from "./r2000-tools.ts";
|
|
|
212
212
|
// must never open a socket, never probe a binary, and never write a byte of
|
|
213
213
|
// JSON-RPC to stdout. WHAT NOT TO DO: never let this branch fall through
|
|
214
214
|
// into the server path, and never print anything on stdout on the server
|
|
215
|
-
// path that a CLI caller could confuse for `
|
|
215
|
+
// path that a CLI caller could confuse for `anno` output.
|
|
216
216
|
//
|
|
217
217
|
// Ending the process here is deliberate and is NOT a violation of this
|
|
218
218
|
// file's standing "never end the process from a teardown handler" rule (see
|
|
@@ -220,7 +220,7 @@ import { R2000_TOOL_DEFINITIONS, runR2000Tool } from "./r2000-tools.ts";
|
|
|
220
220
|
// long-lived server's lease-release path, and this branch ends the process
|
|
221
221
|
// before any lease, socket or handler exists. A dynamic import is used
|
|
222
222
|
// (not a static one) so the CLI module is not part of the server's startup
|
|
223
|
-
// cost on the normal, non-`
|
|
223
|
+
// cost on the normal, non-`anno` path.
|
|
224
224
|
//
|
|
225
225
|
// IN-01 (10-REVIEW.md; 11.1-CONTEXT.md AUDIT-01, D-11.1-04): `console.log`/
|
|
226
226
|
// `console.error` writes to `process.stdout`/`process.stderr` are
|
|
@@ -228,16 +228,18 @@ import { R2000_TOOL_DEFINITIONS, runR2000Tool } from "./r2000-tools.ts";
|
|
|
228
228
|
// non-blocking, unlike a TTY or a regular file), so a bare `process.exit()`
|
|
229
229
|
// immediately after can discard whatever write has not yet drained --
|
|
230
230
|
// measured at a 128 KiB truncation point on this host's Node for a single
|
|
231
|
-
// write exceeding the OS pipe's capacity. The
|
|
232
|
-
//
|
|
233
|
-
//
|
|
231
|
+
// write exceeding the OS pipe's capacity. The trigger this was FOUND
|
|
232
|
+
// through -- a CLI verb that echoed a spawned child's whole stderr -- was
|
|
233
|
+
// withdrawn with the analyser it spawned (plan 29-07, D-14), but the hazard
|
|
234
|
+
// is a property of the exit path rather than of that verb: any verb that
|
|
235
|
+
// prints a diagnostic larger than the pipe's capacity hits it, and the one
|
|
234
236
|
// case where the user most needs the diagnostic is exactly the case a piped
|
|
235
237
|
// invocation could silently lose it in. `drainStdio()` below explicitly
|
|
236
238
|
// awaits both streams' own pending writes (a `write("", cb)`-style
|
|
237
239
|
// zero-length write's callback fires only once every prior queued write has
|
|
238
240
|
// actually flushed) before the terminating `process.exit(code)`.
|
|
239
241
|
//
|
|
240
|
-
// T-11.1-EXITHANG: the drain is BOUNDED to `
|
|
242
|
+
// T-11.1-EXITHANG: the drain is BOUNDED to `ANNO_CLI_DRAIN_TIMEOUT_MS`. An
|
|
241
243
|
// exit that hangs forever waiting on a pipe nobody reads is worse than a
|
|
242
244
|
// truncated diagnostic -- this project's standing rule is that a teardown
|
|
243
245
|
// path never becomes a hang (see the "never end the process from a teardown
|
|
@@ -245,7 +247,7 @@ import { R2000_TOOL_DEFINITIONS, runR2000Tool } from "./r2000-tools.ts";
|
|
|
245
247
|
// for the same reason the original unbounded `process.exit()` did not: this
|
|
246
248
|
// still runs before any lease, socket or handler exists, and now also can
|
|
247
249
|
// never block indefinitely).
|
|
248
|
-
const
|
|
250
|
+
const ANNO_CLI_DRAIN_TIMEOUT_MS = 300;
|
|
249
251
|
|
|
250
252
|
/** Resolves once `stream`'s own pending writes have flushed, or after
|
|
251
253
|
* `timeoutMs`, whichever comes first. A zero-length `write("", cb)`'s
|
|
@@ -260,7 +262,7 @@ function drainStdio(stream: NodeJS.WriteStream): Promise<void> {
|
|
|
260
262
|
resolve();
|
|
261
263
|
return;
|
|
262
264
|
}
|
|
263
|
-
const timer = setTimeout(resolve,
|
|
265
|
+
const timer = setTimeout(resolve, ANNO_CLI_DRAIN_TIMEOUT_MS);
|
|
264
266
|
stream.write("", () => {
|
|
265
267
|
clearTimeout(timer);
|
|
266
268
|
resolve();
|
|
@@ -268,7 +270,7 @@ function drainStdio(stream: NodeJS.WriteStream): Promise<void> {
|
|
|
268
270
|
});
|
|
269
271
|
}
|
|
270
272
|
|
|
271
|
-
if (process.argv[2] === "
|
|
273
|
+
if (process.argv[2] === "anno") {
|
|
272
274
|
// A broken pipe (the reader closing early, e.g. `| head`) makes
|
|
273
275
|
// `stream.write()` fail with EPIPE. Awaiting `drainStdio()` below gives
|
|
274
276
|
// that failure the chance to actually surface as Node's stream 'error'
|
|
@@ -285,23 +287,25 @@ if (process.argv[2] === "r2000") {
|
|
|
285
287
|
// Test-only escape hatch, never documented to end users and inert unless
|
|
286
288
|
// this exact env var is set: writes a deterministic filler payload
|
|
287
289
|
// through this SAME drained-exit path, so vice-proxy.test.ts can measure
|
|
288
|
-
// an exact byte count well above any OS pipe capacity without needing
|
|
289
|
-
// real
|
|
290
|
-
// USAGE text nor a synthesized
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
290
|
+
// an exact byte count well above any OS pipe capacity without needing any
|
|
291
|
+
// real project file at all. That independence is the point: measured at
|
|
292
|
+
// the time, neither `--help`'s ~5.6 KB USAGE text nor a synthesized or
|
|
293
|
+
// garbage project file fed to a verb's error path scaled anywhere near
|
|
294
|
+
// 128 KiB on this host, so no real invocation could reach the truncation
|
|
295
|
+
// point on demand (see 11.1-05-SUMMARY.md for those measurements). The
|
|
296
|
+
// hatch outlives the routes it was measured against, because it depends on
|
|
297
|
+
// none of them. Never reachable
|
|
298
|
+
// from a real `anno <verb>` invocation: the check is against a specific,
|
|
295
299
|
// unambiguous env var name no real caller would ever set.
|
|
296
|
-
const testFillBytes = process.env.
|
|
300
|
+
const testFillBytes = process.env.VICE_TEST_ANNO_CLI_STDOUT_FILL_BYTES;
|
|
297
301
|
if (testFillBytes) {
|
|
298
302
|
process.stdout.write("x".repeat(Number(testFillBytes)));
|
|
299
303
|
await Promise.all([drainStdio(process.stdout), drainStdio(process.stderr)]);
|
|
300
304
|
process.exit(0);
|
|
301
305
|
}
|
|
302
306
|
|
|
303
|
-
const {
|
|
304
|
-
const code = await
|
|
307
|
+
const { runAnnoCli } = await import("./anno-cli.ts");
|
|
308
|
+
const code = await runAnnoCli(process.argv.slice(3));
|
|
305
309
|
await Promise.all([drainStdio(process.stdout), drainStdio(process.stderr)]);
|
|
306
310
|
process.exit(code);
|
|
307
311
|
}
|
|
@@ -313,6 +317,23 @@ const HERE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
|
313
317
|
// selection, the tools construction loop's dispatch choice, and the final
|
|
314
318
|
// ready log line) reads THIS constant, never the raw environment variable
|
|
315
319
|
// and never a second detection call.
|
|
320
|
+
//
|
|
321
|
+
// `ACTIVE_BACKEND.binPath` is what `vice_ping`'s `resolvedBinaryPath` field
|
|
322
|
+
// reports (see stock-dispatch.ts's `handlePing()`). It is resolved exactly
|
|
323
|
+
// ONCE here, at MCP-server process startup, by a bare `x64sc` `$PATH` probe
|
|
324
|
+
// run in THIS process's own environment -- it is NOT re-probed per request
|
|
325
|
+
// and has no connection to the broker, a separate, already-running process
|
|
326
|
+
// that leases whichever instance it chose to whatever request comes in. On a
|
|
327
|
+
// host where bare `x64sc` resolves to one build, `resolvedBinaryPath` reports
|
|
328
|
+
// that build's path on every ping, even when the broker actually launched
|
|
329
|
+
// (or later recycled to) a different one. The authoritative per-instance
|
|
330
|
+
// answer lives in the broker's own launch record (`epoch.json`'s `vice_bin`
|
|
331
|
+
// field, written by `broker-epoch.mts`) -- Phase 8.2 plan 04's own
|
|
332
|
+
// walkthrough had to route around this field entirely and prove backend
|
|
333
|
+
// identity from that launch record plus a live `ps -o args=` read instead
|
|
334
|
+
// (2026-08-19 finding, closed as a documentation fix by Phase 15 plan 15-09
|
|
335
|
+
// rather than a per-request requery, which would be a behavioural change out
|
|
336
|
+
// of a disposition phase's remit).
|
|
316
337
|
const ACTIVE_BACKEND = backendDetect.resolvedBackend();
|
|
317
338
|
|
|
318
339
|
// -------------------------------------------------------------- JSON-RPC
|
|
@@ -3136,6 +3157,7 @@ async function forwardToVice(name: string, args: Record<string, unknown>): Promi
|
|
|
3136
3157
|
// and asserts that slice contains no promise-awaiting construct and calls
|
|
3137
3158
|
// the control session's release function exactly once. Do not move either
|
|
3138
3159
|
// marker away from the code each one bounds.
|
|
3160
|
+
//
|
|
3139
3161
|
let teardownRan = false;
|
|
3140
3162
|
|
|
3141
3163
|
function releaseLeaseNow(trigger: string): void {
|
|
@@ -3340,31 +3362,31 @@ tools[RESULT_CONTINUE_TOOL.name] = buildViceTool(RESULT_CONTINUE_TOOL, (args) =>
|
|
|
3340
3362
|
// entry in `tools/list`.
|
|
3341
3363
|
tools[RECYCLE_TOOL.name] = buildBackendAwareTool(stockDispatch.resolveAdvertisedToolDefinition(RECYCLE_TOOL, ACTIVE_BACKEND.backend, manifestTools), (args) => handleRecycle(args));
|
|
3342
3364
|
tools[DIAGNOSE_TOOL.name] = buildBackendAwareTool(stockDispatch.resolveAdvertisedToolDefinition(DIAGNOSE_TOOL, ACTIVE_BACKEND.backend, manifestTools), (args) => handleDiagnose(args));
|
|
3343
|
-
// Backend-INDEPENDENT by construction (plan
|
|
3344
|
-
// touches VICE at all --
|
|
3345
|
-
//
|
|
3346
|
-
// buildBackendAwareTool() would be flatly
|
|
3347
|
-
// to dispatch to on either backend). The
|
|
3348
|
-
// tools-manifest.json NOR tools-manifest.stock.json:
|
|
3349
|
-
// refresh-manifest.ts from a live HOST VICE server's
|
|
3350
|
-
//
|
|
3351
|
-
// manifest would be silently wiped on the next refresh.
|
|
3352
|
-
// buildViceTool() directly (the SAME exception
|
|
3353
|
-
// is), so no
|
|
3354
|
-
// ensureViceSession() -- CLAUDE.md's "derived
|
|
3355
|
-
// before forwardToVice()" constraint is satisfied
|
|
3356
|
-
// family, not by an interception, because the
|
|
3357
|
-
// forwardToVice() in the first place.
|
|
3365
|
+
// Backend-INDEPENDENT by construction (plan 29-01): the anno_* family never
|
|
3366
|
+
// touches VICE at all -- it reaches a PROXY-LOCAL SQLite annotation store
|
|
3367
|
+
// this repo owns, opened and closed inside the runner itself, so there is no
|
|
3368
|
+
// fork/stock distinction to make and buildBackendAwareTool() would be flatly
|
|
3369
|
+
// wrong here (there is nothing for it to dispatch to on either backend). The
|
|
3370
|
+
// family is in NEITHER tools-manifest.json NOR tools-manifest.stock.json:
|
|
3371
|
+
// both are regenerated by refresh-manifest.ts from a live HOST VICE server's
|
|
3372
|
+
// own tools/list, and a local store file is never that host -- a hand-added
|
|
3373
|
+
// entry in either manifest would be silently wiped on the next refresh.
|
|
3374
|
+
// Registered here via buildViceTool() directly (the SAME exception
|
|
3375
|
+
// RESULT_CONTINUE_TOOL above is), so no anno_* runner can ever reach
|
|
3376
|
+
// forwardToVice(), call(), or ensureViceSession() -- CLAUDE.md's "derived
|
|
3377
|
+
// tools must be intercepted before forwardToVice()" constraint is satisfied
|
|
3378
|
+
// by construction for this family, not by an interception, because the
|
|
3379
|
+
// runner is never wired to forwardToVice() in the first place.
|
|
3358
3380
|
// Deliberately NOT named `def` (the manifest loop's own loop variable,
|
|
3359
3381
|
// above): `stock-dispatch.test.ts`'s `proxyToolRegistrations()` regex-scans
|
|
3360
3382
|
// this file's own `tools[...] = ...;` lines and keys each one by its raw
|
|
3361
3383
|
// captured text, so an identically-named loop variable here would make this
|
|
3362
3384
|
// registration textually indistinguishable from the manifest loop's -- a
|
|
3363
|
-
// distinct name (`
|
|
3385
|
+
// distinct name (`annoDef`) keeps the anno_* family's own exemption from
|
|
3364
3386
|
// `buildBackendAwareTool()` from ever being confused with, or accidentally
|
|
3365
3387
|
// widened to cover, the manifest loop's registration.
|
|
3366
|
-
for (const
|
|
3367
|
-
tools[
|
|
3388
|
+
for (const annoDef of ANNO_TOOL_DEFINITIONS) {
|
|
3389
|
+
tools[annoDef.name] = buildViceTool(annoDef, (args) => runAnnoTool(annoDef.name, args));
|
|
3368
3390
|
}
|
|
3369
3391
|
|
|
3370
3392
|
const server = new MCPServer({ name: "vice", version: PROXY_VERSION, tools });
|