@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.
Files changed (57) hide show
  1. package/README.md +2 -1
  2. package/THIRD-PARTY-NOTICES.md +1 -24
  3. package/{r2000-acme-ident.ts → anno-acme-ident.ts} +13 -13
  4. package/anno-cli.ts +1465 -0
  5. package/{r2000-confidence.ts → anno-confidence.ts} +22 -22
  6. package/anno-coverage.ts +2465 -0
  7. package/{r2000-d64.ts → anno-d64.ts} +5 -5
  8. package/anno-derive.ts +590 -0
  9. package/anno-details.ts +169 -0
  10. package/anno-enum-gen.ts +533 -0
  11. package/anno-export-asm.ts +1310 -0
  12. package/anno-index.ts +150 -0
  13. package/{r2000-memmap-render.ts → anno-memmap-render.ts} +236 -95
  14. package/{r2000-regbits-gen.ts → anno-regbits-gen.ts} +20 -15
  15. package/{r2000-regbits.json → anno-regbits.json} +2 -2
  16. package/anno-register.ts +240 -0
  17. package/anno-store.ts +3486 -0
  18. package/anno-symbols.ts +266 -0
  19. package/anno-tools.ts +2111 -0
  20. package/anno-types.ts +1636 -0
  21. package/block-class.ts +201 -0
  22. package/build.ts +1 -1
  23. package/capability-registry.ts +3 -1
  24. package/disasm-decoder.ts +14 -14
  25. package/disasm-opcodes.ts +4 -4
  26. package/disasm-renderer.ts +2 -2
  27. package/hostpath.ts +1 -1
  28. package/install-resources.ts +1 -1
  29. package/package.json +23 -17
  30. package/prg-image.ts +119 -0
  31. package/repo-root.ts +20 -5
  32. package/resources/broker-launch.mjs +8 -4
  33. package/resources/vice-launcher.sh +3 -3
  34. package/stock-address.ts +5 -5
  35. package/stock-cia.ts +2 -2
  36. package/stock-condition.ts +7 -7
  37. package/stock-connect.ts +1 -1
  38. package/stock-dispatch.ts +35 -5
  39. package/stock-execution.ts +5 -3
  40. package/stock-input.ts +9 -9
  41. package/stock-machine.ts +17 -6
  42. package/stock-protocol.ts +16 -11
  43. package/stock-registers.ts +54 -29
  44. package/stock-sprites.ts +3 -3
  45. package/stock-symbols.ts +9 -9
  46. package/stock-timing.ts +1 -1
  47. package/stock-vicii.ts +1 -1
  48. package/version.ts +1 -1
  49. package/vice-proxy.ts +68 -46
  50. package/r2000-cli.ts +0 -1103
  51. package/r2000-enum-gen.ts +0 -574
  52. package/r2000-launch.ts +0 -357
  53. package/r2000-mcp-client.ts +0 -596
  54. package/r2000-project.ts +0 -190
  55. package/r2000-symbols.ts +0 -388
  56. package/r2000-tools.ts +0 -914
  57. 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 11-05: the curated r2000_* tool surface's DEFINITIONS, imported
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 no child process and no
191
- // socket: the heavy part (the MCP client, the spawned regenerator2000 child)
192
- // stays behind r2000-tools.ts's own `await import("./r2000-mcp-client.ts")`
193
- // inside runR2000Tool() itself, reached only when a tool is actually called.
194
- import { R2000_TOOL_DEFINITIONS, runR2000Tool } from "./r2000-tools.ts";
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
- // ------------------------------------------------------------ r2000 subcommand
196
+ // ------------------------------------------------------------ anno subcommand
197
197
  //
198
- // D-06 / RESEARCH.md Open Question #1 (plan 10-04): `vice-mcp r2000 <verb>` is
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 `.claude/mcp/vice/*.ts` as plain files inside a consuming project),
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 `r2000` output.
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-`r2000` path.
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 reachable trigger is
232
- // `cmdExportAsm`'s `console.error(result.stderr)` in r2000-cli.ts, which can
233
- // carry a large diagnostic from the spawned regenerator2000 child: the one
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 `R2000_CLI_DRAIN_TIMEOUT_MS`. An
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 R2000_CLI_DRAIN_TIMEOUT_MS = 300;
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, R2000_CLI_DRAIN_TIMEOUT_MS);
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] === "r2000") {
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 a
289
- // real regenerator2000 project (empirically, neither `--help`'s ~5.6 KB
290
- // USAGE text nor a synthesized/garbage `.regen2000proj` fed to
291
- // export-asm's error path scales anywhere near 128 KiB on this host's
292
- // regenerator2000 0.9.20 -- both were measured before this hatch was
293
- // added; see 11.1-05-SUMMARY.md for the measurements). Never reachable
294
- // from a real `r2000 <verb>` invocation: the check is against a specific,
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.VICE_TEST_R2000_CLI_STDOUT_FILL_BYTES;
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 { runR2000Cli } = await import("./r2000-cli.ts");
304
- const code = await runR2000Cli(process.argv.slice(3));
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 11-05): the r2000_* family never
3344
- // touches VICE at all -- regenerator2000 is a separate, static-analysis child
3345
- // process, so there is no fork/stock distinction to make and
3346
- // buildBackendAwareTool() would be flatly wrong here (there is nothing for it
3347
- // to dispatch to on either backend). The family is in NEITHER
3348
- // tools-manifest.json NOR tools-manifest.stock.json: both are regenerated by
3349
- // refresh-manifest.ts from a live HOST VICE server's own tools/list, and an
3350
- // r2000 child process is never that host -- a hand-added entry in either
3351
- // manifest would be silently wiped on the next refresh. Registered here via
3352
- // buildViceTool() directly (the SAME exception RESULT_CONTINUE_TOOL above
3353
- // is), so no r2000_* runner can ever reach forwardToVice(), call(), or
3354
- // ensureViceSession() -- CLAUDE.md's "derived tools must be intercepted
3355
- // before forwardToVice()" constraint is satisfied by construction for this
3356
- // family, not by an interception, because the runner is never wired to
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 (`r2000Def`) keeps the r2000 family's own exemption from
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 r2000Def of R2000_TOOL_DEFINITIONS) {
3367
- tools[r2000Def.name] = buildViceTool(r2000Def, (args) => runR2000Tool(r2000Def.name, args));
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 });